autohand-cli 0.6.12 → 0.7.2

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.
Files changed (101) hide show
  1. package/README.md +60 -48
  2. package/dist/AutomodeManager-FQQPBHAD.js +1422 -0
  3. package/dist/AutomodeManager-PUNJS7IZ.cjs +1422 -0
  4. package/dist/{CommunitySkillsCache-XPDVYU3K.js → CommunitySkillsCache-AY3ZYDTN.js} +1 -0
  5. package/dist/{CommunitySkillsCache-X3X237QQ.cjs → CommunitySkillsCache-KO4DSKMA.cjs} +1 -0
  6. package/dist/{GitHubRegistryFetcher-K744NNAJ.cjs → GitHubRegistryFetcher-S4JREWUQ.cjs} +1 -0
  7. package/dist/{GitHubRegistryFetcher-US2JJID4.js → GitHubRegistryFetcher-V23KTTLM.js} +1 -0
  8. package/dist/HookManager-OGINWAJN.cjs +7 -0
  9. package/dist/HookManager-VG46FXSZ.js +7 -0
  10. package/dist/MemoryManager-AFS5EZJ2.js +8 -0
  11. package/dist/MemoryManager-KE6EKW7Y.cjs +8 -0
  12. package/dist/{PermissionManager-PMTQN263.cjs → PermissionManager-MAPKIJMD.cjs} +1 -0
  13. package/dist/{PermissionManager-YFZI4ZZ6.js → PermissionManager-V2Q2OAS2.js} +1 -0
  14. package/dist/SessionManager-MKLGLZWC.cjs +10 -0
  15. package/dist/SessionManager-V25OJDDY.js +10 -0
  16. package/dist/{SkillsRegistry-OINIPILA.cjs → SkillsRegistry-4RU2OAEQ.cjs} +1 -0
  17. package/dist/{SkillsRegistry-7NICF6FY.js → SkillsRegistry-FTLVJZMA.js} +1 -0
  18. package/dist/{agents-GRAFXZY3.cjs → agents-CHNQ7LQ4.cjs} +1 -0
  19. package/dist/{agents-B33IAATH.js → agents-E4NEH2PN.js} +1 -0
  20. package/dist/{agents-new-67NJJSDA.cjs → agents-new-GHVWXW7T.cjs} +1 -0
  21. package/dist/{agents-new-KTXJFC5E.js → agents-new-W6HMQ7V5.js} +1 -0
  22. package/dist/automode-BJYGRMEQ.cjs +9 -0
  23. package/dist/automode-XCNP6HP4.js +9 -0
  24. package/dist/chunk-2FLBGPE3.js +199 -0
  25. package/dist/{chunk-5RX6NVQO.cjs → chunk-3L76MLO5.cjs} +20 -2
  26. package/dist/chunk-3RG5ZIWI.js +10 -0
  27. package/dist/chunk-4L5WYXHN.js +246 -0
  28. package/dist/{chunk-SFT6BEYU.js → chunk-BAFJQUWR.js} +20 -2
  29. package/dist/chunk-CXZEPTRI.js +172 -0
  30. package/dist/{chunk-CT2VTDPQ.cjs → chunk-E6U4UE3B.cjs} +1 -1
  31. package/dist/{chunk-TG34DNLU.cjs → chunk-EBM5QDJ4.cjs} +2 -2
  32. package/dist/{chunk-MFLRXVKU.js → chunk-HUE3GT5M.js} +1 -1
  33. package/dist/{chunk-A2TFZJD4.js → chunk-LCCJ26QR.js} +2 -2
  34. package/dist/chunk-OBGZSXTJ.cjs +10 -0
  35. package/dist/chunk-OTS4YFSZ.cjs +172 -0
  36. package/dist/chunk-SXYYH3VD.cjs +497 -0
  37. package/dist/chunk-UOLJZFPJ.js +497 -0
  38. package/dist/chunk-UPR5PKX4.cjs +199 -0
  39. package/dist/chunk-URY4AS4L.cjs +246 -0
  40. package/dist/{completion-Y42FKDT3.js → completion-2XTEWNMC.js} +1 -0
  41. package/dist/{completion-WVFWX7XQ.cjs → completion-BTDPVWVB.cjs} +1 -0
  42. package/dist/{constants-QYBEF3DB.js → constants-RVCOJL3L.js} +1 -0
  43. package/dist/{constants-PE5DLI7Q.cjs → constants-V4V43DZQ.cjs} +1 -0
  44. package/dist/{defaultHooks-3G3DVF6I.js → defaultHooks-3GRSZNKA.js} +2 -0
  45. package/dist/{defaultHooks-Z4KA6U5C.cjs → defaultHooks-7EAUU6MN.cjs} +3 -1
  46. package/dist/{export-BKBJ7PB2.cjs → export-CB34FSEH.cjs} +1 -0
  47. package/dist/{export-WJ5P6E5Z.js → export-LNPP3XXH.js} +1 -0
  48. package/dist/{feedback-PZ2PINDU.js → feedback-IZKDNGHP.js} +1 -0
  49. package/dist/{feedback-R66B3B3C.cjs → feedback-OKGBXZZ4.cjs} +1 -0
  50. package/dist/{formatters-UG6VZJJ5.js → formatters-JJK6RS55.js} +1 -0
  51. package/dist/{formatters-N5IJKYZY.cjs → formatters-OIGPANHJ.cjs} +1 -0
  52. package/dist/{help-PKC6QCNG.js → help-MU553D6W.js} +1 -0
  53. package/dist/{help-UEELQRHP.cjs → help-QAAUDLFS.cjs} +1 -0
  54. package/dist/{hooks-5NY5K4L3.js → hooks-4N5VRVOF.js} +2 -1
  55. package/dist/hooks-7DZGBMXP.cjs +10 -0
  56. package/dist/index.cjs +933 -1395
  57. package/dist/index.js +863 -1325
  58. package/dist/{init-SLLSDDJN.cjs → init-2QEB7GL5.cjs} +1 -0
  59. package/dist/{init-DML7AOII.js → init-WW4RITLV.js} +1 -0
  60. package/dist/{lint-TA2ZHVLM.js → lint-L2TD6NY6.js} +1 -0
  61. package/dist/{lint-44UQJ673.cjs → lint-ZLRBEAFF.cjs} +1 -0
  62. package/dist/{localProjectPermissions-75X3ZGKH.cjs → localProjectPermissions-2DP6X55S.cjs} +1 -0
  63. package/dist/{localProjectPermissions-DURCNDZG.js → localProjectPermissions-5CXHD4DO.js} +1 -0
  64. package/dist/{login-BZ7J4Z4M.js → login-LH62FYMH.js} +1 -0
  65. package/dist/{login-HUH3CEWL.cjs → login-TWWYJKX6.cjs} +1 -0
  66. package/dist/{logout-3V3SH7OL.cjs → logout-35XNU6Q2.cjs} +1 -0
  67. package/dist/{logout-BZKEMNHB.js → logout-KPHUXO23.js} +1 -0
  68. package/dist/{memory-4GSP7NKV.js → memory-2SGSO4MW.js} +1 -0
  69. package/dist/{memory-CFNC7RJH.cjs → memory-ALXCFFQY.cjs} +1 -0
  70. package/dist/{model-TKVEJ5BC.cjs → model-JC43B3KM.cjs} +1 -0
  71. package/dist/{model-HKEFSH5E.js → model-U3BWIWVH.js} +1 -0
  72. package/dist/{new-EB2MBQXA.cjs → new-24YT5KVI.cjs} +1 -0
  73. package/dist/{new-EEZC4XXV.js → new-PCOF6OLV.js} +1 -0
  74. package/dist/{patch-J32X2QQP.cjs → patch-ETANEGRW.cjs} +3 -1
  75. package/dist/{patch-BAAQIYSW.js → patch-RPK3BZQR.js} +2 -0
  76. package/dist/{permissions-5MTH22EF.js → permissions-5W5JOVM7.js} +1 -0
  77. package/dist/{permissions-IP5SITPI.cjs → permissions-NHPJPHDV.cjs} +1 -0
  78. package/dist/{quit-RSYIERO5.js → quit-NIDVPHNL.js} +1 -0
  79. package/dist/{quit-2RYFGIJP.cjs → quit-TMKMX2YF.cjs} +1 -0
  80. package/dist/{resume-OYZMJRNO.cjs → resume-5C44HAAH.cjs} +1 -0
  81. package/dist/{resume-2NERFSTD.js → resume-ZZ2D2NMB.js} +1 -0
  82. package/dist/{session-T3TAZ5ZU.cjs → session-6TMBGN7N.cjs} +1 -0
  83. package/dist/{session-H5QWKE5E.js → session-76F55XKA.js} +1 -0
  84. package/dist/{sessions-4KXIT76T.js → sessions-ERKBJ7MC.js} +1 -0
  85. package/dist/{sessions-7RTCPVNE.cjs → sessions-NUPCJVCF.cjs} +1 -0
  86. package/dist/{skills-CRFOVWEQ.js → skills-R25OBHE5.js} +2 -1
  87. package/dist/skills-UCWKIHHG.cjs +13 -0
  88. package/dist/{skills-install-RMPXN6RK.js → skills-install-GNTBBL46.js} +1 -0
  89. package/dist/{skills-install-Z27KPEGF.cjs → skills-install-VUSVGSON.cjs} +1 -0
  90. package/dist/{skills-new-3QJUST7P.cjs → skills-new-AGXQNBRA.cjs} +1 -0
  91. package/dist/{skills-new-S2YPO635.js → skills-new-L36LQXIE.js} +1 -0
  92. package/dist/status-2QV7C3JJ.cjs +9 -0
  93. package/dist/{status-KC56ITDB.js → status-VJ6FOSRI.js} +2 -1
  94. package/dist/{theme-LIF3RD3A.cjs → theme-MI3BM56M.cjs} +1 -0
  95. package/dist/{theme-JAMJSCKR.js → theme-ZXGSJBZI.js} +1 -0
  96. package/dist/{undo-7QJBXARS.js → undo-3UU5LWQS.js} +1 -0
  97. package/dist/{undo-2WR2ZIEC.cjs → undo-W4VN2Y37.cjs} +1 -0
  98. package/package.json +2 -2
  99. package/dist/hooks-PIYYJZMI.cjs +0 -9
  100. package/dist/skills-6PIGHOWS.cjs +0 -12
  101. package/dist/status-VDVKZCQR.cjs +0 -8
package/dist/index.js CHANGED
@@ -1,11 +1,23 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ PermissionManager
4
+ } from "./chunk-YMP7AGNT.js";
5
+ import {
6
+ SessionManager
7
+ } from "./chunk-2FLBGPE3.js";
8
+ import {
9
+ MemoryManager
10
+ } from "./chunk-4L5WYXHN.js";
2
11
  import {
3
12
  SkillsRegistry
4
13
  } from "./chunk-SKU4M27Z.js";
14
+ import {
15
+ HookManager
16
+ } from "./chunk-UOLJZFPJ.js";
5
17
  import {
6
18
  installMetadata,
7
19
  metadata as metadata23
8
- } from "./chunk-MFLRXVKU.js";
20
+ } from "./chunk-HUE3GT5M.js";
9
21
  import {
10
22
  metadata as metadata24
11
23
  } from "./chunk-NGSLABLS.js";
@@ -14,8 +26,8 @@ import {
14
26
  metadata as metadata25
15
27
  } from "./chunk-ZYHQ652D.js";
16
28
  import {
17
- PermissionManager
18
- } from "./chunk-YMP7AGNT.js";
29
+ metadata as metadata26
30
+ } from "./chunk-CXZEPTRI.js";
19
31
  import "./chunk-27ISZOFA.js";
20
32
  import {
21
33
  metadata as metadata15
@@ -29,7 +41,7 @@ import {
29
41
  import {
30
42
  metadata as metadata18,
31
43
  package_default
32
- } from "./chunk-A2TFZJD4.js";
44
+ } from "./chunk-LCCJ26QR.js";
33
45
  import {
34
46
  metadata as metadata19
35
47
  } from "./chunk-6XITU2RU.js";
@@ -53,7 +65,7 @@ import {
53
65
  } from "./chunk-YDH2BMEN.js";
54
66
  import {
55
67
  metadata as metadata22
56
- } from "./chunk-SFT6BEYU.js";
68
+ } from "./chunk-BAFJQUWR.js";
57
69
  import {
58
70
  metadata as metadata7
59
71
  } from "./chunk-SVLBJMYO.js";
@@ -106,6 +118,9 @@ import {
106
118
  import {
107
119
  metadata as metadata6
108
120
  } from "./chunk-QJ53OSGF.js";
121
+ import {
122
+ __require
123
+ } from "./chunk-3RG5ZIWI.js";
109
124
 
110
125
  // src/index.ts
111
126
  import "dotenv/config";
@@ -326,12 +341,251 @@ function printStartupCheckResults(results, verbose = false) {
326
341
  }
327
342
  }
328
343
 
329
- // src/utils/versionCheck.ts
330
- import fs2 from "fs-extra";
331
- import path from "path";
344
+ // src/startup/workspaceSafety.ts
332
345
  import os2 from "os";
346
+ import path from "path";
347
+ import fs2 from "fs-extra";
348
+ var DANGEROUS_PATHS = {
349
+ // Filesystem roots (Unix and Windows)
350
+ roots: ["/", "C:\\", "D:\\", "E:\\", "F:\\", "G:\\"],
351
+ // System directories (Unix/Linux)
352
+ unix: [
353
+ "/etc",
354
+ "/var",
355
+ "/usr",
356
+ "/opt",
357
+ "/bin",
358
+ "/sbin",
359
+ "/lib",
360
+ "/lib64",
361
+ "/root",
362
+ "/sys",
363
+ "/proc",
364
+ "/dev",
365
+ "/boot",
366
+ "/run",
367
+ "/snap"
368
+ ],
369
+ // System directories (macOS)
370
+ macos: [
371
+ "/System",
372
+ "/Library",
373
+ "/Applications",
374
+ "/private",
375
+ "/cores",
376
+ "/Volumes"
377
+ ],
378
+ // System directories (Windows)
379
+ windows: [
380
+ "C:\\Windows",
381
+ "C:\\Program Files",
382
+ "C:\\Program Files (x86)",
383
+ "C:\\ProgramData",
384
+ "C:\\Recovery",
385
+ "C:\\$Recycle.Bin"
386
+ ],
387
+ // WSL mount points (Windows drives mounted in WSL)
388
+ wsl: ["/mnt/c", "/mnt/d", "/mnt/e", "/mnt/f"]
389
+ };
390
+ function normalizePath(inputPath) {
391
+ let normalized = path.resolve(inputPath);
392
+ try {
393
+ normalized = fs2.realpathSync(normalized);
394
+ } catch {
395
+ }
396
+ if (normalized.length > 1 && normalized.endsWith(path.sep)) {
397
+ normalized = normalized.slice(0, -1);
398
+ }
399
+ return normalized;
400
+ }
401
+ function pathsEqual(path1, path22) {
402
+ const normalized1 = normalizePath(path1);
403
+ const normalized2 = normalizePath(path22);
404
+ if (process.platform === "darwin" || process.platform === "win32") {
405
+ return normalized1.toLowerCase() === normalized2.toLowerCase();
406
+ }
407
+ return normalized1 === normalized2;
408
+ }
409
+ function isFilesystemRoot(workspacePath) {
410
+ const normalized = normalizePath(workspacePath);
411
+ if (normalized === "/") {
412
+ return true;
413
+ }
414
+ if (process.platform === "win32") {
415
+ if (/^[A-Za-z]:[\\/]?$/.test(normalized)) {
416
+ return true;
417
+ }
418
+ }
419
+ return false;
420
+ }
421
+ function isHomeDirectory(workspacePath) {
422
+ const homeDir = os2.homedir();
423
+ return pathsEqual(workspacePath, homeDir);
424
+ }
425
+ function isParentOfHome(workspacePath) {
426
+ const homeDir = os2.homedir();
427
+ const normalized = normalizePath(workspacePath);
428
+ const normalizedHome = normalizePath(homeDir);
429
+ if (process.platform === "darwin" || process.platform === "win32") {
430
+ return normalizedHome.toLowerCase().startsWith(normalized.toLowerCase() + path.sep);
431
+ }
432
+ return normalizedHome.startsWith(normalized + path.sep);
433
+ }
434
+ function isSystemDirectory(workspacePath) {
435
+ const normalized = normalizePath(workspacePath);
436
+ let dangerousPaths = [...DANGEROUS_PATHS.roots];
437
+ if (process.platform === "darwin") {
438
+ dangerousPaths = [...dangerousPaths, ...DANGEROUS_PATHS.unix, ...DANGEROUS_PATHS.macos];
439
+ } else if (process.platform === "win32") {
440
+ dangerousPaths = [...dangerousPaths, ...DANGEROUS_PATHS.windows];
441
+ } else {
442
+ dangerousPaths = [...dangerousPaths, ...DANGEROUS_PATHS.unix, ...DANGEROUS_PATHS.wsl];
443
+ }
444
+ for (const dangerous of dangerousPaths) {
445
+ if (pathsEqual(normalized, dangerous)) {
446
+ return true;
447
+ }
448
+ }
449
+ return false;
450
+ }
451
+ function isWslWindowsHome(workspacePath) {
452
+ if (process.platform !== "linux") {
453
+ return false;
454
+ }
455
+ const normalized = normalizePath(workspacePath);
456
+ for (const wslMount of DANGEROUS_PATHS.wsl) {
457
+ if (pathsEqual(normalized, wslMount)) {
458
+ return true;
459
+ }
460
+ }
461
+ const wslUsersPattern = /^\/mnt\/[a-z]\/users(\/[^/]+)?$/i;
462
+ if (wslUsersPattern.test(normalized)) {
463
+ return true;
464
+ }
465
+ return false;
466
+ }
467
+ function getDangerReason(workspacePath) {
468
+ const normalized = normalizePath(workspacePath);
469
+ if (isFilesystemRoot(workspacePath)) {
470
+ return "This is the filesystem root directory.";
471
+ }
472
+ if (isHomeDirectory(workspacePath)) {
473
+ return "This is your home directory.";
474
+ }
475
+ if (isParentOfHome(workspacePath)) {
476
+ return "This directory contains user home directories.";
477
+ }
478
+ if (isWslWindowsHome(workspacePath)) {
479
+ return "This is a Windows system directory accessed via WSL.";
480
+ }
481
+ if (normalized.startsWith("/etc") || normalized.match(/^[A-Za-z]:\\Windows/i)) {
482
+ return "This is a system configuration directory.";
483
+ }
484
+ if (normalized.startsWith("/var") || normalized.match(/^[A-Za-z]:\\ProgramData/i)) {
485
+ return "This is a system data directory.";
486
+ }
487
+ if (normalized.startsWith("/usr") || normalized.match(/^[A-Za-z]:\\Program Files/i)) {
488
+ return "This is a system programs directory.";
489
+ }
490
+ if (normalized.startsWith("/System") || normalized.startsWith("/Library")) {
491
+ return "This is a macOS system directory.";
492
+ }
493
+ return "This directory is too broad for safe AI agent operation.";
494
+ }
495
+ function checkWorkspaceSafety(workspacePath) {
496
+ try {
497
+ const normalized = normalizePath(workspacePath);
498
+ if (isFilesystemRoot(normalized)) {
499
+ return {
500
+ safe: false,
501
+ reason: "This is the filesystem root directory. Running an AI agent here could modify critical system files.",
502
+ suggestion: "Navigate to a specific project folder and try again."
503
+ };
504
+ }
505
+ if (isHomeDirectory(normalized)) {
506
+ return {
507
+ safe: false,
508
+ reason: "This is your home directory. Running an AI agent here could modify files across your entire user account.",
509
+ suggestion: "Navigate to a specific project folder (e.g., ~/projects/my-app) and try again."
510
+ };
511
+ }
512
+ if (isParentOfHome(normalized)) {
513
+ return {
514
+ safe: false,
515
+ reason: "This directory contains user home directories. Running an AI agent here is too broad.",
516
+ suggestion: "Navigate to a specific project folder and try again."
517
+ };
518
+ }
519
+ if (isSystemDirectory(normalized)) {
520
+ return {
521
+ safe: false,
522
+ reason: getDangerReason(normalized),
523
+ suggestion: "Navigate to a specific project folder and try again."
524
+ };
525
+ }
526
+ if (isWslWindowsHome(normalized)) {
527
+ return {
528
+ safe: false,
529
+ reason: getDangerReason(normalized),
530
+ suggestion: "Navigate to a specific project folder and try again."
531
+ };
532
+ }
533
+ return { safe: true };
534
+ } catch (error) {
535
+ console.warn(`Warning: Could not verify workspace safety for "${workspacePath}": ${error}`);
536
+ return { safe: true };
537
+ }
538
+ }
539
+ function printDangerousWorkspaceWarning(workspacePath, result) {
540
+ const chalk16 = __require("chalk");
541
+ const boxWidth = 65;
542
+ const horizontalLine = "\u2500".repeat(boxWidth - 2);
543
+ console.log();
544
+ console.log(chalk16.red(`\u250C${horizontalLine}\u2510`));
545
+ console.log(chalk16.red(`\u2502`) + chalk16.yellow.bold(" \u26A0\uFE0F Unsafe Workspace Directory") + " ".repeat(boxWidth - 35) + chalk16.red("\u2502"));
546
+ console.log(chalk16.red(`\u251C${horizontalLine}\u2524`));
547
+ console.log(chalk16.red(`\u2502`) + " ".repeat(boxWidth - 2) + chalk16.red("\u2502"));
548
+ console.log(chalk16.red(`\u2502`) + chalk16.white(" You're trying to run autohand in:") + " ".repeat(boxWidth - 38) + chalk16.red("\u2502"));
549
+ const pathLine = ` ${workspacePath}`;
550
+ const truncatedPath = pathLine.length > boxWidth - 4 ? pathLine.slice(0, boxWidth - 7) + "..." : pathLine;
551
+ console.log(chalk16.red(`\u2502`) + chalk16.cyan(truncatedPath) + " ".repeat(Math.max(0, boxWidth - 2 - truncatedPath.length)) + chalk16.red("\u2502"));
552
+ console.log(chalk16.red(`\u2502`) + " ".repeat(boxWidth - 2) + chalk16.red("\u2502"));
553
+ if (result.reason) {
554
+ const reasonWords = result.reason.split(" ");
555
+ let currentLine = " ";
556
+ for (const word of reasonWords) {
557
+ if (currentLine.length + word.length + 1 > boxWidth - 4) {
558
+ const paddedLine = currentLine + " ".repeat(Math.max(0, boxWidth - 2 - currentLine.length));
559
+ console.log(chalk16.red(`\u2502`) + chalk16.white(paddedLine) + chalk16.red("\u2502"));
560
+ currentLine = " ";
561
+ }
562
+ currentLine += (currentLine === " " ? "" : " ") + word;
563
+ }
564
+ if (currentLine.length > 2) {
565
+ const paddedLine = currentLine + " ".repeat(Math.max(0, boxWidth - 2 - currentLine.length));
566
+ console.log(chalk16.red(`\u2502`) + chalk16.white(paddedLine) + chalk16.red("\u2502"));
567
+ }
568
+ }
569
+ console.log(chalk16.red(`\u2502`) + " ".repeat(boxWidth - 2) + chalk16.red("\u2502"));
570
+ console.log(chalk16.red(`\u2502`) + chalk16.white(" Please navigate to a specific project folder:") + " ".repeat(boxWidth - 50) + chalk16.red("\u2502"));
571
+ console.log(chalk16.red(`\u2502`) + " ".repeat(boxWidth - 2) + chalk16.red("\u2502"));
572
+ console.log(chalk16.red(`\u2502`) + chalk16.cyan(" cd ~/projects/my-app") + " ".repeat(boxWidth - 27) + chalk16.red("\u2502"));
573
+ console.log(chalk16.red(`\u2502`) + chalk16.cyan(" autohand") + " ".repeat(boxWidth - 15) + chalk16.red("\u2502"));
574
+ console.log(chalk16.red(`\u2502`) + " ".repeat(boxWidth - 2) + chalk16.red("\u2502"));
575
+ console.log(chalk16.red(`\u2502`) + chalk16.white(" Or specify a path directly:") + " ".repeat(boxWidth - 32) + chalk16.red("\u2502"));
576
+ console.log(chalk16.red(`\u2502`) + " ".repeat(boxWidth - 2) + chalk16.red("\u2502"));
577
+ console.log(chalk16.red(`\u2502`) + chalk16.cyan(" autohand --path ~/projects/my-app") + " ".repeat(boxWidth - 40) + chalk16.red("\u2502"));
578
+ console.log(chalk16.red(`\u2502`) + " ".repeat(boxWidth - 2) + chalk16.red("\u2502"));
579
+ console.log(chalk16.red(`\u2514${horizontalLine}\u2518`));
580
+ console.log();
581
+ }
582
+
583
+ // src/utils/versionCheck.ts
584
+ import fs3 from "fs-extra";
585
+ import path2 from "path";
586
+ import os3 from "os";
333
587
  var GITHUB_API_URL = "https://api.github.com/repos/autohandai/code-cli/releases/latest";
334
- var CACHE_FILE = path.join(os2.homedir(), ".autohand", "version-check.json");
588
+ var CACHE_FILE = path2.join(os3.homedir(), ".autohand", "version-check.json");
335
589
  var DEFAULT_CHECK_INTERVAL_HOURS = 24;
336
590
  var REQUEST_TIMEOUT_MS = 3e3;
337
591
  function compareVersions(a, b) {
@@ -356,8 +610,8 @@ function compareVersions(a, b) {
356
610
  }
357
611
  async function readCache() {
358
612
  try {
359
- if (await fs2.pathExists(CACHE_FILE)) {
360
- const data = await fs2.readJson(CACHE_FILE);
613
+ if (await fs3.pathExists(CACHE_FILE)) {
614
+ const data = await fs3.readJson(CACHE_FILE);
361
615
  return data;
362
616
  }
363
617
  } catch {
@@ -366,8 +620,8 @@ async function readCache() {
366
620
  }
367
621
  async function writeCache(cache) {
368
622
  try {
369
- await fs2.ensureDir(path.dirname(CACHE_FILE));
370
- await fs2.writeJson(CACHE_FILE, cache, { spaces: 2 });
623
+ await fs3.ensureDir(path2.dirname(CACHE_FILE));
624
+ await fs3.writeJson(CACHE_FILE, cache, { spaces: 2 });
371
625
  } catch {
372
626
  }
373
627
  }
@@ -453,21 +707,21 @@ async function checkForUpdates(currentVersion, options = {}) {
453
707
  }
454
708
 
455
709
  // src/actions/filesystem.ts
456
- import fs4 from "fs-extra";
457
- import path3 from "path";
710
+ import fs5 from "fs-extra";
711
+ import path4 from "path";
458
712
  import { spawnSync as spawnSync2 } from "child_process";
459
713
  import { applyPatch as applyUnifiedPatch } from "diff";
460
714
 
461
715
  // src/utils/gitIgnore.ts
462
- import fs3 from "fs-extra";
463
- import path2 from "path";
716
+ import fs4 from "fs-extra";
717
+ import path3 from "path";
464
718
  import ignore from "ignore";
465
719
  var GitIgnoreParser = class {
466
720
  constructor(projectRoot, extraPatterns) {
467
721
  this.extraPatterns = extraPatterns;
468
722
  this.cache = /* @__PURE__ */ new Map();
469
723
  this.processedExtraPatterns = [];
470
- this.projectRoot = path2.resolve(projectRoot);
724
+ this.projectRoot = path3.resolve(projectRoot);
471
725
  if (this.extraPatterns?.length) {
472
726
  this.processedExtraPatterns = this.processPatterns(this.extraPatterns, ".");
473
727
  }
@@ -476,11 +730,11 @@ var GitIgnoreParser = class {
476
730
  if (!filePath) {
477
731
  return false;
478
732
  }
479
- const absoluteFilePath = path2.resolve(this.projectRoot, filePath);
733
+ const absoluteFilePath = path3.resolve(this.projectRoot, filePath);
480
734
  if (!absoluteFilePath.startsWith(this.projectRoot)) {
481
735
  return false;
482
736
  }
483
- const relativePath = path2.relative(this.projectRoot, absoluteFilePath);
737
+ const relativePath = path3.relative(this.projectRoot, absoluteFilePath);
484
738
  if (!relativePath || relativePath.startsWith("..")) {
485
739
  return false;
486
740
  }
@@ -488,20 +742,20 @@ var GitIgnoreParser = class {
488
742
  const ig = ignore();
489
743
  ig.add(".git");
490
744
  if (this.globalPatterns === void 0) {
491
- const excludeFile = path2.join(this.projectRoot, ".git", "info", "exclude");
492
- this.globalPatterns = fs3.existsSync(excludeFile) ? this.loadPatternsForFile(excludeFile) : [];
745
+ const excludeFile = path3.join(this.projectRoot, ".git", "info", "exclude");
746
+ this.globalPatterns = fs4.existsSync(excludeFile) ? this.loadPatternsForFile(excludeFile) : [];
493
747
  }
494
748
  ig.add(this.globalPatterns);
495
- const pathParts = relativePath.split(path2.sep);
749
+ const pathParts = relativePath.split(path3.sep);
496
750
  const dirsToVisit = [];
497
751
  let current = this.projectRoot;
498
752
  dirsToVisit.push(current);
499
753
  for (let i = 0; i < pathParts.length - 1; i++) {
500
- current = path2.join(current, pathParts[i]);
754
+ current = path3.join(current, pathParts[i]);
501
755
  dirsToVisit.push(current);
502
756
  }
503
757
  for (const dir of dirsToVisit) {
504
- const relativeDir = path2.relative(this.projectRoot, dir);
758
+ const relativeDir = path3.relative(this.projectRoot, dir);
505
759
  if (relativeDir) {
506
760
  const normalizedDir = relativeDir.replace(/\\/g, "/");
507
761
  const igPlusExtras = ignore().add(ig).add(this.processedExtraPatterns);
@@ -512,8 +766,8 @@ var GitIgnoreParser = class {
512
766
  if (this.cache.has(dir)) {
513
767
  ig.add(this.cache.get(dir) ?? []);
514
768
  } else {
515
- const gitignorePath = path2.join(dir, ".gitignore");
516
- if (fs3.existsSync(gitignorePath)) {
769
+ const gitignorePath = path3.join(dir, ".gitignore");
770
+ if (fs4.existsSync(gitignorePath)) {
517
771
  const patterns = this.loadPatternsForFile(gitignorePath);
518
772
  this.cache.set(dir, patterns);
519
773
  ig.add(patterns);
@@ -528,12 +782,12 @@ var GitIgnoreParser = class {
528
782
  loadPatternsForFile(patternsFilePath) {
529
783
  let content = "";
530
784
  try {
531
- content = fs3.readFileSync(patternsFilePath, "utf8");
785
+ content = fs4.readFileSync(patternsFilePath, "utf8");
532
786
  } catch {
533
787
  return [];
534
788
  }
535
- const isExcludeFile = patternsFilePath.endsWith(path2.join(".git", "info", "exclude"));
536
- const relativeBaseDir = isExcludeFile ? "." : path2.dirname(path2.relative(this.projectRoot, patternsFilePath)).split(path2.sep).join(path2.posix.sep);
789
+ const isExcludeFile = patternsFilePath.endsWith(path3.join(".git", "info", "exclude"));
790
+ const relativeBaseDir = isExcludeFile ? "." : path3.dirname(path3.relative(this.projectRoot, patternsFilePath)).split(path3.sep).join(path3.posix.sep);
537
791
  const rawPatterns = content.split("\n");
538
792
  return this.processPatterns(rawPatterns, relativeBaseDir);
539
793
  }
@@ -556,9 +810,9 @@ var GitIgnoreParser = class {
556
810
  let newPattern = p;
557
811
  if (relativeBaseDir && relativeBaseDir !== ".") {
558
812
  if (!isAnchored && !p.includes("/")) {
559
- newPattern = path2.posix.join("**", p);
813
+ newPattern = path3.posix.join("**", p);
560
814
  }
561
- newPattern = path2.posix.join(relativeBaseDir, newPattern);
815
+ newPattern = path3.posix.join(relativeBaseDir, newPattern);
562
816
  if (!newPattern.startsWith("/")) {
563
817
  newPattern = "/" + newPattern;
564
818
  }
@@ -597,7 +851,7 @@ var FileActionManager = class {
597
851
  this.onBatchChange = null;
598
852
  this.currentToolId = "";
599
853
  this.currentToolName = "";
600
- this.workspaceRoot = path3.resolve(workspaceRoot);
854
+ this.workspaceRoot = path4.resolve(workspaceRoot);
601
855
  }
602
856
  /**
603
857
  * Enter preview mode - changes will be batched instead of written
@@ -662,10 +916,10 @@ var FileActionManager = class {
662
916
  try {
663
917
  const fullPath = this.resolvePath(change.filePath);
664
918
  if (change.changeType === "delete") {
665
- await fs4.remove(fullPath);
919
+ await fs5.remove(fullPath);
666
920
  } else {
667
- await fs4.ensureDir(path3.dirname(fullPath));
668
- await fs4.writeFile(fullPath, change.proposedContent, "utf8");
921
+ await fs5.ensureDir(path4.dirname(fullPath));
922
+ await fs5.writeFile(fullPath, change.proposedContent, "utf8");
669
923
  }
670
924
  applied.push(change.id);
671
925
  } catch (err) {
@@ -704,17 +958,17 @@ var FileActionManager = class {
704
958
  }
705
959
  async readFile(target) {
706
960
  const filePath = this.resolvePath(target);
707
- const exists = await fs4.pathExists(filePath);
961
+ const exists = await fs5.pathExists(filePath);
708
962
  if (!exists) {
709
963
  throw new Error(`File ${target} not found in workspace.`);
710
964
  }
711
- const stats = await fs4.stat(filePath);
965
+ const stats = await fs5.stat(filePath);
712
966
  if (stats.size > FILE_LIMITS.MAX_READ_SIZE) {
713
967
  const sizeMB = (stats.size / 1024 / 1024).toFixed(2);
714
968
  const limitMB = (FILE_LIMITS.MAX_READ_SIZE / 1024 / 1024).toFixed(0);
715
969
  throw new Error(`File ${target} is too large (${sizeMB}MB). Maximum allowed: ${limitMB}MB`);
716
970
  }
717
- return fs4.readFile(filePath, "utf8");
971
+ return fs5.readFile(filePath, "utf8");
718
972
  }
719
973
  async writeFile(target, contents, description) {
720
974
  const contentSize = Buffer.byteLength(contents, "utf8");
@@ -724,8 +978,8 @@ var FileActionManager = class {
724
978
  throw new Error(`Content too large to write (${sizeMB}MB). Maximum allowed: ${limitMB}MB`);
725
979
  }
726
980
  const filePath = this.resolvePath(target);
727
- const exists = await fs4.pathExists(filePath);
728
- const previous = exists ? await fs4.readFile(filePath, "utf8") : "";
981
+ const exists = await fs5.pathExists(filePath);
982
+ const previous = exists ? await fs5.readFile(filePath, "utf8") : "";
729
983
  const changeType = exists ? "modify" : "create";
730
984
  if (this.previewMode) {
731
985
  this.addBatchedChange(
@@ -737,12 +991,12 @@ var FileActionManager = class {
737
991
  );
738
992
  return;
739
993
  }
740
- await fs4.ensureDir(path3.dirname(filePath));
994
+ await fs5.ensureDir(path4.dirname(filePath));
741
995
  if (this.undoStack.length >= FILE_LIMITS.MAX_UNDO_STACK) {
742
996
  this.undoStack.shift();
743
997
  }
744
998
  this.undoStack.push({ absolutePath: filePath, previousContents: previous });
745
- await fs4.writeFile(filePath, contents, "utf8");
999
+ await fs5.writeFile(filePath, contents, "utf8");
746
1000
  }
747
1001
  async appendFile(target, contents) {
748
1002
  const current = await this.readFileSafe(target);
@@ -766,14 +1020,14 @@ var FileActionManager = class {
766
1020
  return;
767
1021
  }
768
1022
  this.undoStack.push({ absolutePath: filePath, previousContents: current });
769
- await fs4.writeFile(filePath, updated, "utf8");
1023
+ await fs5.writeFile(filePath, updated, "utf8");
770
1024
  }
771
1025
  async undoLast() {
772
1026
  const entry = this.undoStack.pop();
773
1027
  if (!entry) {
774
1028
  throw new Error("Undo stack is empty");
775
1029
  }
776
- await fs4.writeFile(entry.absolutePath, entry.previousContents, "utf8");
1030
+ await fs5.writeFile(entry.absolutePath, entry.previousContents, "utf8");
777
1031
  }
778
1032
  search(query, relativePath) {
779
1033
  const searchDir = this.resolvePath(relativePath ?? ".");
@@ -818,7 +1072,7 @@ var FileActionManager = class {
818
1072
  const results = rgResult.stdout.trim().split("\n").filter(Boolean).slice(0, FILE_LIMITS.MAX_SEARCH_RESULTS).map((line) => {
819
1073
  const [file, lineNo, ...rest] = line.split(":");
820
1074
  return {
821
- file: path3.relative(this.workspaceRoot, path3.join(searchDir, file)),
1075
+ file: path4.relative(this.workspaceRoot, path4.join(searchDir, file)),
822
1076
  line: Number(lineNo),
823
1077
  text: rest.join(":")
824
1078
  };
@@ -844,18 +1098,18 @@ var FileActionManager = class {
844
1098
  while (stack.length && results.length < limit) {
845
1099
  const current = stack.pop();
846
1100
  if (!current) continue;
847
- const relative = path3.relative(this.workspaceRoot, current);
1101
+ const relative = path4.relative(this.workspaceRoot, current);
848
1102
  const normalizedRel = relative.replace(/\\/g, "/");
849
- if (path3.basename(current).startsWith(".") || ignoreFilter.isIgnored(normalizedRel)) {
1103
+ if (path4.basename(current).startsWith(".") || ignoreFilter.isIgnored(normalizedRel)) {
850
1104
  continue;
851
1105
  }
852
1106
  try {
853
- const stats = fs4.statSync(current);
1107
+ const stats = fs5.statSync(current);
854
1108
  if (stats.isDirectory()) {
855
- const entries = fs4.readdirSync(current);
1109
+ const entries = fs5.readdirSync(current);
856
1110
  for (const entry of entries) {
857
1111
  if (!entry.startsWith(".")) {
858
- stack.push(path3.join(current, entry));
1112
+ stack.push(path4.join(current, entry));
859
1113
  }
860
1114
  }
861
1115
  continue;
@@ -863,7 +1117,7 @@ var FileActionManager = class {
863
1117
  if (!stats.isFile()) {
864
1118
  continue;
865
1119
  }
866
- const ext = path3.extname(current).toLowerCase();
1120
+ const ext = path4.extname(current).toLowerCase();
867
1121
  const binaryExtensions = /* @__PURE__ */ new Set([
868
1122
  ".exe",
869
1123
  ".dll",
@@ -924,7 +1178,7 @@ var FileActionManager = class {
924
1178
  if (normalizedRel.includes("node_modules/") || normalizedRel.includes("/dist/") || normalizedRel.includes("/build/") || normalizedRel.includes("/binaries/")) {
925
1179
  continue;
926
1180
  }
927
- const contents = fs4.readFileSync(current, "utf8");
1181
+ const contents = fs5.readFileSync(current, "utf8");
928
1182
  const haystack = contents.toLowerCase();
929
1183
  const idx = haystack.indexOf(lowerQuery);
930
1184
  if (idx === -1) continue;
@@ -934,7 +1188,7 @@ var FileActionManager = class {
934
1188
  const suffixEllipsis = end < contents.length ? "\u2026" : "";
935
1189
  const snippet = `${prefixEllipsis}${contents.slice(start, end)}${suffixEllipsis}`;
936
1190
  results.push({
937
- file: normalizedRel || path3.basename(current),
1191
+ file: normalizedRel || path4.basename(current),
938
1192
  snippet
939
1193
  });
940
1194
  } catch {
@@ -945,39 +1199,39 @@ var FileActionManager = class {
945
1199
  }
946
1200
  async readFileSafe(target) {
947
1201
  const filePath = this.resolvePath(target);
948
- if (!await fs4.pathExists(filePath)) {
1202
+ if (!await fs5.pathExists(filePath)) {
949
1203
  return "";
950
1204
  }
951
- const stats = await fs4.stat(filePath);
1205
+ const stats = await fs5.stat(filePath);
952
1206
  if (stats.size > FILE_LIMITS.MAX_READ_SIZE) {
953
1207
  const sizeMB = (stats.size / 1024 / 1024).toFixed(2);
954
1208
  const limitMB = (FILE_LIMITS.MAX_READ_SIZE / 1024 / 1024).toFixed(0);
955
1209
  throw new Error(`File ${target} is too large (${sizeMB}MB). Maximum allowed: ${limitMB}MB`);
956
1210
  }
957
- return fs4.readFile(filePath, "utf8");
1211
+ return fs5.readFile(filePath, "utf8");
958
1212
  }
959
1213
  resolvePath(target) {
960
- const normalized = path3.isAbsolute(target) ? target : path3.join(this.workspaceRoot, target);
961
- const resolved = path3.resolve(normalized);
1214
+ const normalized = path4.isAbsolute(target) ? target : path4.join(this.workspaceRoot, target);
1215
+ const resolved = path4.resolve(normalized);
962
1216
  let realPath;
963
1217
  try {
964
- realPath = fs4.realpathSync(resolved);
1218
+ realPath = fs5.realpathSync(resolved);
965
1219
  } catch {
966
- const parentDir = path3.dirname(resolved);
1220
+ const parentDir = path4.dirname(resolved);
967
1221
  try {
968
- const realParent = fs4.realpathSync(parentDir);
969
- realPath = path3.join(realParent, path3.basename(resolved));
1222
+ const realParent = fs5.realpathSync(parentDir);
1223
+ realPath = path4.join(realParent, path4.basename(resolved));
970
1224
  } catch {
971
1225
  realPath = resolved;
972
1226
  }
973
1227
  }
974
1228
  let realWorkspaceRoot;
975
1229
  try {
976
- realWorkspaceRoot = fs4.realpathSync(this.workspaceRoot);
1230
+ realWorkspaceRoot = fs5.realpathSync(this.workspaceRoot);
977
1231
  } catch {
978
1232
  realWorkspaceRoot = this.workspaceRoot;
979
1233
  }
980
- const rootWithSep = realWorkspaceRoot.endsWith(path3.sep) ? realWorkspaceRoot : `${realWorkspaceRoot}${path3.sep}`;
1234
+ const rootWithSep = realWorkspaceRoot.endsWith(path4.sep) ? realWorkspaceRoot : `${realWorkspaceRoot}${path4.sep}`;
981
1235
  if (realPath !== realWorkspaceRoot && !realPath.startsWith(rootWithSep)) {
982
1236
  throw new Error(`Path ${target} escapes the workspace root ${this.workspaceRoot}`);
983
1237
  }
@@ -991,28 +1245,28 @@ var FileActionManager = class {
991
1245
  if (!current) {
992
1246
  continue;
993
1247
  }
994
- const basename3 = path3.basename(current);
995
- const relative = path3.relative(this.workspaceRoot, current);
1248
+ const basename3 = path4.basename(current);
1249
+ const relative = path4.relative(this.workspaceRoot, current);
996
1250
  if (basename3.startsWith(".") || relative.includes("node_modules") || relative.startsWith("dist")) {
997
1251
  continue;
998
1252
  }
999
1253
  try {
1000
- const stats = fs4.statSync(current);
1254
+ const stats = fs5.statSync(current);
1001
1255
  if (stats.isDirectory()) {
1002
- const entries = fs4.readdirSync(current);
1256
+ const entries = fs5.readdirSync(current);
1003
1257
  for (const entry of entries) {
1004
1258
  if (!entry.startsWith(".")) {
1005
- stack.push(path3.join(current, entry));
1259
+ stack.push(path4.join(current, entry));
1006
1260
  }
1007
1261
  }
1008
1262
  } else if (stats.isFile()) {
1009
- const contents = fs4.readFileSync(current, "utf8");
1263
+ const contents = fs5.readFileSync(current, "utf8");
1010
1264
  const lines = contents.split(/\r?\n/);
1011
1265
  for (let idx = 0; idx < lines.length && hits.length < FILE_LIMITS.MAX_SEARCH_RESULTS; idx++) {
1012
1266
  const line = lines[idx];
1013
1267
  if (line.includes(query)) {
1014
1268
  hits.push({
1015
- file: path3.relative(this.workspaceRoot, current),
1269
+ file: path4.relative(this.workspaceRoot, current),
1016
1270
  line: idx + 1,
1017
1271
  text: line.trim()
1018
1272
  });
@@ -1027,16 +1281,16 @@ var FileActionManager = class {
1027
1281
  }
1028
1282
  async createDirectory(relativePath) {
1029
1283
  const dirPath = this.resolvePath(relativePath);
1030
- await fs4.ensureDir(dirPath);
1284
+ await fs5.ensureDir(dirPath);
1031
1285
  }
1032
1286
  async deletePath(relativePath, description) {
1033
1287
  const fullPath = this.resolvePath(relativePath);
1034
- const exists = await fs4.pathExists(fullPath);
1288
+ const exists = await fs5.pathExists(fullPath);
1035
1289
  if (!exists) {
1036
1290
  throw new Error(`${relativePath} does not exist.`);
1037
1291
  }
1038
- const stats = await fs4.stat(fullPath);
1039
- const previousContents = stats.isFile() ? await fs4.readFile(fullPath, "utf8") : "";
1292
+ const stats = await fs5.stat(fullPath);
1293
+ const previousContents = stats.isFile() ? await fs5.readFile(fullPath, "utf8") : "";
1040
1294
  if (this.previewMode) {
1041
1295
  this.addBatchedChange(
1042
1296
  relativePath,
@@ -1051,18 +1305,18 @@ var FileActionManager = class {
1051
1305
  absolutePath: fullPath,
1052
1306
  previousContents
1053
1307
  });
1054
- await fs4.remove(fullPath);
1308
+ await fs5.remove(fullPath);
1055
1309
  }
1056
1310
  async renamePath(from, to) {
1057
1311
  const fromPath = this.resolvePath(from);
1058
1312
  const toPath = this.resolvePath(to);
1059
- await fs4.ensureDir(path3.dirname(toPath));
1060
- await fs4.move(fromPath, toPath, { overwrite: true });
1313
+ await fs5.ensureDir(path4.dirname(toPath));
1314
+ await fs5.move(fromPath, toPath, { overwrite: true });
1061
1315
  }
1062
1316
  async copyPath(from, to) {
1063
1317
  const fromPath = this.resolvePath(from);
1064
1318
  const toPath = this.resolvePath(to);
1065
- await fs4.copy(fromPath, toPath, { overwrite: true });
1319
+ await fs5.copy(fromPath, toPath, { overwrite: true });
1066
1320
  }
1067
1321
  async replaceInFile(relativePath, searchValue, replaceValue) {
1068
1322
  const current = await this.readFile(relativePath);
@@ -1076,10 +1330,10 @@ var FileActionManager = class {
1076
1330
  }
1077
1331
  renderContext(hit, contextLines) {
1078
1332
  const filePath = this.resolvePath(hit.file);
1079
- if (!fs4.existsSync(filePath)) {
1333
+ if (!fs5.existsSync(filePath)) {
1080
1334
  return `${hit.file}:${hit.line}`;
1081
1335
  }
1082
- const contents = fs4.readFileSync(filePath, "utf8");
1336
+ const contents = fs5.readFileSync(filePath, "utf8");
1083
1337
  const lines = contents.split(/\r?\n/);
1084
1338
  const start = Math.max(0, hit.line - 1 - contextLines);
1085
1339
  const end = Math.min(lines.length, hit.line - 1 + contextLines + 1);
@@ -1495,7 +1749,10 @@ var OpenRouterClient = class {
1495
1749
  this.baseUrl = settings.baseUrl ?? DEFAULT_BASE_URL;
1496
1750
  this.defaultModel = settings.model;
1497
1751
  const configuredRetries = networkSettings?.maxRetries ?? DEFAULT_MAX_RETRIES;
1498
- this.maxRetries = Math.min(Math.max(0, configuredRetries), MAX_ALLOWED_RETRIES);
1752
+ this.maxRetries = Math.min(
1753
+ Math.max(0, configuredRetries),
1754
+ MAX_ALLOWED_RETRIES
1755
+ );
1499
1756
  this.retryDelay = networkSettings?.retryDelay ?? DEFAULT_RETRY_DELAY;
1500
1757
  this.timeout = networkSettings?.timeout ?? DEFAULT_TIMEOUT;
1501
1758
  }
@@ -1526,8 +1783,8 @@ var OpenRouterClient = class {
1526
1783
  }
1527
1784
  const headers = {
1528
1785
  "Content-Type": "application/json",
1529
- "HTTP-Referer": "https://github.com/autohand/cli",
1530
- "X-Title": "autohand-cli"
1786
+ "HTTP-Referer": "https://github.com/autohandai/code-cli",
1787
+ "X-Title": "autohand-code-cli"
1531
1788
  };
1532
1789
  if (this.apiKey) {
1533
1790
  headers.Authorization = `Bearer ${this.apiKey}`;
@@ -1544,7 +1801,12 @@ var OpenRouterClient = class {
1544
1801
  let lastError = null;
1545
1802
  for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
1546
1803
  try {
1547
- const response = await this.makeRequest(payload, headers, request.signal, payloadJson);
1804
+ const response = await this.makeRequest(
1805
+ payload,
1806
+ headers,
1807
+ request.signal,
1808
+ payloadJson
1809
+ );
1548
1810
  return response;
1549
1811
  } catch (error) {
1550
1812
  lastError = error;
@@ -1563,7 +1825,10 @@ var OpenRouterClient = class {
1563
1825
  let response;
1564
1826
  try {
1565
1827
  const timeoutController = new AbortController();
1566
- const timeoutId = setTimeout(() => timeoutController.abort(), this.timeout);
1828
+ const timeoutId = setTimeout(
1829
+ () => timeoutController.abort(),
1830
+ this.timeout
1831
+ );
1567
1832
  const combinedSignal = signal ? this.combineSignals(signal, timeoutController.signal) : timeoutController.signal;
1568
1833
  try {
1569
1834
  response = await fetch(`${this.baseUrl}/chat/completions`, {
@@ -1581,7 +1846,9 @@ var OpenRouterClient = class {
1581
1846
  throw new Error("Request cancelled.");
1582
1847
  }
1583
1848
  if (err.name === "AbortError") {
1584
- throw new Error("Request timed out. The AI service may be experiencing high load.");
1849
+ throw new Error(
1850
+ "Request timed out. The AI service may be experiencing high load."
1851
+ );
1585
1852
  }
1586
1853
  throw new Error(
1587
1854
  "Unable to connect to the AI service. Please check your internet connection."
@@ -1599,7 +1866,9 @@ var OpenRouterClient = class {
1599
1866
  toolCalls = message.tool_calls.map((tc) => {
1600
1867
  const rawArgs = tc.function?.arguments;
1601
1868
  if (!rawArgs || rawArgs === "{}" || rawArgs === "") {
1602
- console.error(`[DEBUG] Tool "${tc.function?.name}" raw arguments from API: "${rawArgs}" (type: ${typeof rawArgs})`);
1869
+ console.error(
1870
+ `[DEBUG] Tool "${tc.function?.name}" raw arguments from API: "${rawArgs}" (type: ${typeof rawArgs})`
1871
+ );
1603
1872
  }
1604
1873
  return {
1605
1874
  id: tc.id,
@@ -1797,8 +2066,8 @@ var ProviderFactory = class {
1797
2066
 
1798
2067
  // src/core/agent.ts
1799
2068
  import chalk13 from "chalk";
1800
- import fs20 from "fs-extra";
1801
- import path19 from "path";
2069
+ import fs19 from "fs-extra";
2070
+ import path18 from "path";
1802
2071
  import { randomUUID } from "crypto";
1803
2072
  import { spawnSync as spawnSync5 } from "child_process";
1804
2073
  import ora from "ora";
@@ -2018,12 +2287,12 @@ var MentionPreview = class {
2018
2287
  const dir = parts.length ? parts.join("/") + "/" : "";
2019
2288
  if (isSelected) {
2020
2289
  const highlighted = chalk2.cyan(filename);
2021
- const path22 = dir ? chalk2.gray(dir) : "";
2022
- return `${pointer} ${path22}${highlighted}`;
2290
+ const path21 = dir ? chalk2.gray(dir) : "";
2291
+ return `${pointer} ${path21}${highlighted}`;
2023
2292
  }
2024
2293
  const dimmedFilename = chalk2.white(filename);
2025
- const path21 = dir ? chalk2.gray(dir) : "";
2026
- return `${pointer} ${path21}${dimmedFilename}`;
2294
+ const path20 = dir ? chalk2.gray(dir) : "";
2295
+ return `${pointer} ${path20}${dimmedFilename}`;
2027
2296
  }
2028
2297
  const text = isSelected ? chalk2.cyan(entry) : entry;
2029
2298
  return `${pointer} ${text}`;
@@ -3562,7 +3831,8 @@ var SLASH_COMMANDS = [
3562
3831
  metadata23,
3563
3832
  installMetadata,
3564
3833
  metadata24,
3565
- metadata25
3834
+ metadata25,
3835
+ metadata26
3566
3836
  ];
3567
3837
 
3568
3838
  // src/core/conversationManager.ts
@@ -4676,7 +4946,7 @@ import { diffLines } from "diff";
4676
4946
 
4677
4947
  // src/ui/syntaxHighlight.ts
4678
4948
  import chalk5 from "chalk";
4679
- import path4 from "path";
4949
+ import path5 from "path";
4680
4950
  var EXTENSION_MAP = {
4681
4951
  ".ts": "typescript",
4682
4952
  ".tsx": "typescript",
@@ -5075,11 +5345,11 @@ var BUILTIN_TYPES = {
5075
5345
  };
5076
5346
  function detectLanguage(filePath, content) {
5077
5347
  if (filePath) {
5078
- const ext = path4.extname(filePath).toLowerCase();
5348
+ const ext = path5.extname(filePath).toLowerCase();
5079
5349
  if (EXTENSION_MAP[ext]) {
5080
5350
  return EXTENSION_MAP[ext];
5081
5351
  }
5082
- const basename3 = path4.basename(filePath).toLowerCase();
5352
+ const basename3 = path5.basename(filePath).toLowerCase();
5083
5353
  if (basename3 === "dockerfile") return "dockerfile";
5084
5354
  if (basename3 === "makefile") return "makefile";
5085
5355
  if (basename3.endsWith(".d.ts")) return "typescript";
@@ -5205,14 +5475,14 @@ function highlightLine(line, language) {
5205
5475
  }
5206
5476
 
5207
5477
  // src/actions/dependencies.ts
5208
- import fs5 from "fs-extra";
5209
- import path5 from "path";
5478
+ import fs6 from "fs-extra";
5479
+ import path6 from "path";
5210
5480
  async function readPackageManifest(cwd) {
5211
- const manifestPath = path5.join(cwd, "package.json");
5212
- if (!await fs5.pathExists(manifestPath)) {
5481
+ const manifestPath = path6.join(cwd, "package.json");
5482
+ if (!await fs6.pathExists(manifestPath)) {
5213
5483
  return null;
5214
5484
  }
5215
- return fs5.readJson(manifestPath);
5485
+ return fs6.readJson(manifestPath);
5216
5486
  }
5217
5487
  async function addDependency(cwd, name, version, options = {}) {
5218
5488
  const manifest = await readPackageManifest(cwd) ?? {};
@@ -5223,16 +5493,16 @@ async function addDependency(cwd, name, version, options = {}) {
5223
5493
  manifest.dependencies = manifest.dependencies ?? {};
5224
5494
  manifest.dependencies[name] = version;
5225
5495
  }
5226
- const manifestPath = path5.join(cwd, "package.json");
5227
- await fs5.writeJson(manifestPath, manifest, { spaces: 2 });
5496
+ const manifestPath = path6.join(cwd, "package.json");
5497
+ await fs6.writeJson(manifestPath, manifest, { spaces: 2 });
5228
5498
  }
5229
5499
  async function removeDependency(cwd, name, options = {}) {
5230
5500
  const manifest = await readPackageManifest(cwd) ?? {};
5231
5501
  const targetKey = options.dev ? "devDependencies" : "dependencies";
5232
5502
  if (manifest[targetKey] && manifest[targetKey][name]) {
5233
5503
  delete manifest[targetKey][name];
5234
- const manifestPath = path5.join(cwd, "package.json");
5235
- await fs5.writeJson(manifestPath, manifest, { spaces: 2 });
5504
+ const manifestPath = path6.join(cwd, "package.json");
5505
+ await fs6.writeJson(manifestPath, manifest, { spaces: 2 });
5236
5506
  }
5237
5507
  }
5238
5508
 
@@ -5301,16 +5571,16 @@ function runCommand(cmd, args, cwd, options = {}) {
5301
5571
 
5302
5572
  // src/actions/metadata.ts
5303
5573
  import crypto2 from "crypto";
5304
- import fs6 from "fs-extra";
5305
- import path6 from "path";
5574
+ import fs7 from "fs-extra";
5575
+ import path7 from "path";
5306
5576
  var ALWAYS_SKIP = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".next", "__pycache__", ".cache"]);
5307
5577
  async function listDirectoryTree(root, options = {}) {
5308
5578
  const depth = options.depth ?? 2;
5309
5579
  const maxEntries = options.maxEntries ?? 200;
5310
5580
  const workspaceRoot = options.workspaceRoot ?? root;
5311
5581
  const result = [];
5312
- const resolvedRoot = path6.resolve(root);
5313
- const resolvedWorkspace = path6.resolve(workspaceRoot);
5582
+ const resolvedRoot = path7.resolve(root);
5583
+ const resolvedWorkspace = path7.resolve(workspaceRoot);
5314
5584
  if (!resolvedRoot.startsWith(resolvedWorkspace)) {
5315
5585
  throw new Error(`Path ${root} is outside the workspace root.`);
5316
5586
  }
@@ -5321,7 +5591,7 @@ async function listDirectoryTree(root, options = {}) {
5321
5591
  }
5322
5592
  let entries;
5323
5593
  try {
5324
- entries = await fs6.readdir(current);
5594
+ entries = await fs7.readdir(current);
5325
5595
  } catch (err) {
5326
5596
  return;
5327
5597
  }
@@ -5330,13 +5600,13 @@ async function listDirectoryTree(root, options = {}) {
5330
5600
  if (entry.startsWith(".")) {
5331
5601
  continue;
5332
5602
  }
5333
- const full = path6.join(current, entry);
5334
- const rel = path6.relative(workspaceRoot, full);
5603
+ const full = path7.join(current, entry);
5604
+ const rel = path7.relative(workspaceRoot, full);
5335
5605
  if (ALWAYS_SKIP.has(entry) || ignoreFilter.isIgnored(rel)) {
5336
5606
  continue;
5337
5607
  }
5338
5608
  try {
5339
- const stats = await fs6.stat(full);
5609
+ const stats = await fs7.stat(full);
5340
5610
  result.push(`${prefix}${entry}${stats.isDirectory() ? "/" : ""}`);
5341
5611
  if (stats.isDirectory() && currentDepth < depth) {
5342
5612
  await walk(full, `${prefix} `, currentDepth + 1);
@@ -5353,11 +5623,11 @@ async function listDirectoryTree(root, options = {}) {
5353
5623
  return result;
5354
5624
  }
5355
5625
  async function fileStats(root, relativePath) {
5356
- const fullPath = path6.join(root, relativePath);
5357
- if (!await fs6.pathExists(fullPath)) {
5626
+ const fullPath = path7.join(root, relativePath);
5627
+ if (!await fs7.pathExists(fullPath)) {
5358
5628
  return null;
5359
5629
  }
5360
- const stats = await fs6.stat(fullPath);
5630
+ const stats = await fs7.stat(fullPath);
5361
5631
  return {
5362
5632
  size: stats.size,
5363
5633
  mtime: stats.mtime.toISOString(),
@@ -5365,13 +5635,13 @@ async function fileStats(root, relativePath) {
5365
5635
  };
5366
5636
  }
5367
5637
  async function checksumFile(root, relativePath, algorithm = "sha256") {
5368
- const fullPath = path6.join(root, relativePath);
5369
- const exists = await fs6.pathExists(fullPath);
5638
+ const fullPath = path7.join(root, relativePath);
5639
+ const exists = await fs7.pathExists(fullPath);
5370
5640
  if (!exists) {
5371
5641
  throw new Error(`${relativePath} does not exist.`);
5372
5642
  }
5373
5643
  const hash = crypto2.createHash(algorithm);
5374
- const stream = fs6.createReadStream(fullPath);
5644
+ const stream = fs7.createReadStream(fullPath);
5375
5645
  return await new Promise((resolve, reject) => {
5376
5646
  stream.on("data", (chunk) => hash.update(chunk));
5377
5647
  stream.on("error", reject);
@@ -5381,9 +5651,9 @@ async function checksumFile(root, relativePath, algorithm = "sha256") {
5381
5651
 
5382
5652
  // src/actions/worktree.ts
5383
5653
  import { spawnSync as spawnSync4, spawn as spawn2 } from "child_process";
5384
- import path7 from "path";
5385
- import fs7 from "fs-extra";
5386
- import os3 from "os";
5654
+ import path8 from "path";
5655
+ import fs8 from "fs-extra";
5656
+ import os4 from "os";
5387
5657
  var DEFAULT_TEMPLATES = [
5388
5658
  {
5389
5659
  name: "feature",
@@ -5419,7 +5689,7 @@ var DEFAULT_TEMPLATES = [
5419
5689
  var WorktreeManager = class {
5420
5690
  constructor(cwd) {
5421
5691
  this.repoRoot = this.findGitRoot(cwd);
5422
- this.repoName = path7.basename(this.repoRoot);
5692
+ this.repoName = path8.basename(this.repoRoot);
5423
5693
  this.templates = [...DEFAULT_TEMPLATES];
5424
5694
  }
5425
5695
  // ============ Core Operations ============
@@ -5468,9 +5738,9 @@ var WorktreeManager = class {
5468
5738
  if (template) {
5469
5739
  worktreePath = this.resolvePath(template.pathPattern, branch);
5470
5740
  } else {
5471
- worktreePath = path7.join(path7.dirname(this.repoRoot), `${this.repoName}-${branch}`);
5741
+ worktreePath = path8.join(path8.dirname(this.repoRoot), `${this.repoName}-${branch}`);
5472
5742
  }
5473
- await fs7.ensureDir(path7.dirname(worktreePath));
5743
+ await fs8.ensureDir(path8.dirname(worktreePath));
5474
5744
  const args = ["worktree", "add"];
5475
5745
  if (options.newBranch) {
5476
5746
  args.push("-b", branch);
@@ -5511,7 +5781,7 @@ var WorktreeManager = class {
5511
5781
  * Remove a worktree with cleanup
5512
5782
  */
5513
5783
  async remove(worktreePath, options = {}) {
5514
- const absolutePath = path7.isAbsolute(worktreePath) ? worktreePath : path7.resolve(this.repoRoot, worktreePath);
5784
+ const absolutePath = path8.isAbsolute(worktreePath) ? worktreePath : path8.resolve(this.repoRoot, worktreePath);
5515
5785
  const worktrees = this.list();
5516
5786
  const wt = worktrees.find((w) => w.path === absolutePath);
5517
5787
  const branchToDelete = wt?.branch;
@@ -5579,7 +5849,7 @@ var WorktreeManager = class {
5579
5849
  async runParallel(command, options = {}) {
5580
5850
  const worktrees = this.list().filter((wt) => !wt.bare);
5581
5851
  const filtered = options.filter ? worktrees.filter(options.filter) : worktrees;
5582
- const maxConcurrent = options.maxConcurrent || os3.cpus().length;
5852
+ const maxConcurrent = options.maxConcurrent || os4.cpus().length;
5583
5853
  const timeout = options.timeout || 3e5;
5584
5854
  const results = [];
5585
5855
  const running = [];
@@ -5814,7 +6084,7 @@ var WorktreeManager = class {
5814
6084
  resolvePath(pattern, branch) {
5815
6085
  const timestamp = Date.now().toString();
5816
6086
  const sanitizedBranch = branch.replace(/[^a-zA-Z0-9-_]/g, "-");
5817
- return path7.resolve(
6087
+ return path8.resolve(
5818
6088
  this.repoRoot,
5819
6089
  pattern.replace("{repo}", this.repoName).replace("{branch}", sanitizedBranch).replace("{timestamp}", timestamp)
5820
6090
  );
@@ -5880,20 +6150,20 @@ var WorktreeManager = class {
5880
6150
  };
5881
6151
 
5882
6152
  // src/core/customCommands.ts
5883
- import fs8 from "fs-extra";
5884
- import path8 from "path";
6153
+ import fs9 from "fs-extra";
6154
+ import path9 from "path";
5885
6155
  var COMMANDS_DIR = AUTOHAND_PATHS.commands;
5886
6156
  async function loadCustomCommand(name) {
5887
- const filePath = path8.join(COMMANDS_DIR, `${sanitizeName(name)}.json`);
5888
- if (!await fs8.pathExists(filePath)) {
6157
+ const filePath = path9.join(COMMANDS_DIR, `${sanitizeName(name)}.json`);
6158
+ if (!await fs9.pathExists(filePath)) {
5889
6159
  return null;
5890
6160
  }
5891
- return fs8.readJson(filePath);
6161
+ return fs9.readJson(filePath);
5892
6162
  }
5893
6163
  async function saveCustomCommand(definition) {
5894
- await fs8.ensureDir(COMMANDS_DIR);
5895
- const filePath = path8.join(COMMANDS_DIR, `${sanitizeName(definition.name)}.json`);
5896
- await fs8.writeJson(filePath, definition, { spaces: 2 });
6164
+ await fs9.ensureDir(COMMANDS_DIR);
6165
+ const filePath = path9.join(COMMANDS_DIR, `${sanitizeName(definition.name)}.json`);
6166
+ await fs9.writeJson(filePath, definition, { spaces: 2 });
5897
6167
  }
5898
6168
  function sanitizeName(name) {
5899
6169
  return name.replace(/[^a-z0-9-_]/gi, "_");
@@ -6198,15 +6468,15 @@ function formatPackageInfo(info) {
6198
6468
  }
6199
6469
 
6200
6470
  // src/core/toolsRegistry.ts
6201
- import fs9 from "fs-extra";
6202
- import path9 from "path";
6471
+ import fs10 from "fs-extra";
6472
+ import path10 from "path";
6203
6473
  var ToolsRegistry = class {
6204
6474
  constructor(toolsDir = AUTOHAND_PATHS.tools) {
6205
6475
  this.toolsDir = toolsDir;
6206
6476
  this.metaToolCache = /* @__PURE__ */ new Map();
6207
6477
  }
6208
6478
  async initialize() {
6209
- await fs9.ensureDir(this.toolsDir);
6479
+ await fs10.ensureDir(this.toolsDir);
6210
6480
  await this.loadMetaToolDefinitions();
6211
6481
  }
6212
6482
  async listTools(builtIns) {
@@ -6243,8 +6513,8 @@ var ToolsRegistry = class {
6243
6513
  ...definition,
6244
6514
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
6245
6515
  };
6246
- const filePath = path9.join(this.toolsDir, `${definition.name}.json`);
6247
- await fs9.writeJson(filePath, fullDef, { spaces: 2 });
6516
+ const filePath = path10.join(this.toolsDir, `${definition.name}.json`);
6517
+ await fs10.writeJson(filePath, fullDef, { spaces: 2 });
6248
6518
  this.metaToolCache.set(definition.name, fullDef);
6249
6519
  return fullDef;
6250
6520
  }
@@ -6273,18 +6543,18 @@ var ToolsRegistry = class {
6273
6543
  }
6274
6544
  async loadMetaToolDefinitions() {
6275
6545
  try {
6276
- const exists = await fs9.pathExists(this.toolsDir);
6546
+ const exists = await fs10.pathExists(this.toolsDir);
6277
6547
  if (!exists) {
6278
6548
  return;
6279
6549
  }
6280
- const files = await fs9.readdir(this.toolsDir);
6550
+ const files = await fs10.readdir(this.toolsDir);
6281
6551
  for (const file of files) {
6282
6552
  if (!file.endsWith(".json")) {
6283
6553
  continue;
6284
6554
  }
6285
- const fullPath = path9.join(this.toolsDir, file);
6555
+ const fullPath = path10.join(this.toolsDir, file);
6286
6556
  try {
6287
- const data = await fs9.readJson(fullPath);
6557
+ const data = await fs10.readJson(fullPath);
6288
6558
  if (this.isValidMetaTool(data)) {
6289
6559
  this.metaToolCache.set(data.name, data);
6290
6560
  }
@@ -6724,8 +6994,8 @@ var ActionExecutor = class _ActionExecutor {
6724
6994
  throw new Error(`write_file requires a "path" argument. Received arguments: [${receivedKeys}]`);
6725
6995
  }
6726
6996
  const filePath = this.resolveWorkspacePath(action.path);
6727
- const fs22 = await import("fs-extra");
6728
- const exists = this.files.root && await fs22.pathExists(filePath);
6997
+ const fs21 = await import("fs-extra");
6998
+ const exists = this.files.root && await fs21.pathExists(filePath);
6729
6999
  const oldContent = exists ? await this.files.readFile(action.path) : "";
6730
7000
  const newContent = this.pickText(action.contents, action.content) ?? "";
6731
7001
  if (!exists) {
@@ -7128,8 +7398,8 @@ ${result.removed.map((p) => ` - ${p}`).join("\n")}`;
7128
7398
  const lines = [chalk6.cyan("\u{1F504} Worktree Sync Results:"), ""];
7129
7399
  if (result.synced.length > 0) {
7130
7400
  lines.push(chalk6.green(`Synced (${result.synced.length}):`));
7131
- for (const path21 of result.synced) {
7132
- lines.push(` \u2713 ${path21}`);
7401
+ for (const path20 of result.synced) {
7402
+ lines.push(` \u2713 ${path20}`);
7133
7403
  }
7134
7404
  lines.push("");
7135
7405
  }
@@ -8098,24 +8368,24 @@ var SlashCommandHandler = class {
8098
8368
  try {
8099
8369
  switch (command) {
8100
8370
  case "/model": {
8101
- const { model } = await import("./model-HKEFSH5E.js");
8371
+ const { model } = await import("./model-U3BWIWVH.js");
8102
8372
  return model(this.ctx);
8103
8373
  }
8104
8374
  case "/init": {
8105
- const { init } = await import("./init-DML7AOII.js");
8375
+ const { init } = await import("./init-WW4RITLV.js");
8106
8376
  return init(this.ctx);
8107
8377
  }
8108
8378
  case "/quit": {
8109
- const { quit } = await import("./quit-RSYIERO5.js");
8379
+ const { quit } = await import("./quit-NIDVPHNL.js");
8110
8380
  return quit();
8111
8381
  }
8112
8382
  case "/help":
8113
8383
  case "/?": {
8114
- const { help } = await import("./help-PKC6QCNG.js");
8384
+ const { help } = await import("./help-MU553D6W.js");
8115
8385
  return help();
8116
8386
  }
8117
8387
  case "/agents": {
8118
- const { handler } = await import("./agents-B33IAATH.js");
8388
+ const { handler } = await import("./agents-E4NEH2PN.js");
8119
8389
  const output = await handler();
8120
8390
  if (output) {
8121
8391
  console.log(output);
@@ -8124,27 +8394,27 @@ var SlashCommandHandler = class {
8124
8394
  }
8125
8395
  case "/agents new":
8126
8396
  case "/agents-new": {
8127
- const { createAgent } = await import("./agents-new-KTXJFC5E.js");
8397
+ const { createAgent } = await import("./agents-new-W6HMQ7V5.js");
8128
8398
  return createAgent(this.ctx);
8129
8399
  }
8130
8400
  case "/feedback": {
8131
- const { feedback } = await import("./feedback-PZ2PINDU.js");
8401
+ const { feedback } = await import("./feedback-IZKDNGHP.js");
8132
8402
  return feedback(this.ctx);
8133
8403
  }
8134
8404
  case "/resume": {
8135
- const { resume } = await import("./resume-2NERFSTD.js");
8405
+ const { resume } = await import("./resume-ZZ2D2NMB.js");
8136
8406
  return resume({ sessionManager: this.ctx.sessionManager, args });
8137
8407
  }
8138
8408
  case "/sessions": {
8139
- const { sessions } = await import("./sessions-4KXIT76T.js");
8409
+ const { sessions } = await import("./sessions-ERKBJ7MC.js");
8140
8410
  return sessions({ sessionManager: this.ctx.sessionManager, args });
8141
8411
  }
8142
8412
  case "/session": {
8143
- const { session } = await import("./session-H5QWKE5E.js");
8413
+ const { session } = await import("./session-76F55XKA.js");
8144
8414
  return session({ sessionManager: this.ctx.sessionManager });
8145
8415
  }
8146
8416
  case "/undo": {
8147
- const { undo } = await import("./undo-7QJBXARS.js");
8417
+ const { undo } = await import("./undo-3UU5LWQS.js");
8148
8418
  return undo({
8149
8419
  workspaceRoot: this.ctx.workspaceRoot,
8150
8420
  undoFileMutation: this.ctx.undoFileMutation ?? (async () => {
@@ -8154,7 +8424,7 @@ var SlashCommandHandler = class {
8154
8424
  });
8155
8425
  }
8156
8426
  case "/new": {
8157
- const { newConversation } = await import("./new-EEZC4XXV.js");
8427
+ const { newConversation } = await import("./new-PCOF6OLV.js");
8158
8428
  return newConversation({
8159
8429
  resetConversation: this.ctx.resetConversation,
8160
8430
  sessionManager: this.ctx.sessionManager,
@@ -8163,26 +8433,26 @@ var SlashCommandHandler = class {
8163
8433
  });
8164
8434
  }
8165
8435
  case "/memory": {
8166
- const { memory } = await import("./memory-4GSP7NKV.js");
8436
+ const { memory } = await import("./memory-2SGSO4MW.js");
8167
8437
  return memory({ memoryManager: this.ctx.memoryManager });
8168
8438
  }
8169
8439
  case "/formatters": {
8170
- const { execute } = await import("./formatters-UG6VZJJ5.js");
8440
+ const { execute } = await import("./formatters-JJK6RS55.js");
8171
8441
  await execute();
8172
8442
  return null;
8173
8443
  }
8174
8444
  case "/lint": {
8175
- const { execute } = await import("./lint-TA2ZHVLM.js");
8445
+ const { execute } = await import("./lint-L2TD6NY6.js");
8176
8446
  await execute();
8177
8447
  return null;
8178
8448
  }
8179
8449
  case "/completion": {
8180
- const { execute } = await import("./completion-Y42FKDT3.js");
8450
+ const { execute } = await import("./completion-2XTEWNMC.js");
8181
8451
  await execute(args.join(" "));
8182
8452
  return null;
8183
8453
  }
8184
8454
  case "/export": {
8185
- const { execute } = await import("./export-WJ5P6E5Z.js");
8455
+ const { execute } = await import("./export-LNPP3XXH.js");
8186
8456
  await execute(args.join(" "), {
8187
8457
  sessionManager: this.ctx.sessionManager,
8188
8458
  currentSession: this.ctx.currentSession,
@@ -8191,30 +8461,30 @@ var SlashCommandHandler = class {
8191
8461
  return null;
8192
8462
  }
8193
8463
  case "/status": {
8194
- const { status } = await import("./status-KC56ITDB.js");
8464
+ const { status } = await import("./status-VJ6FOSRI.js");
8195
8465
  return status(this.ctx);
8196
8466
  }
8197
8467
  case "/login": {
8198
- const { login } = await import("./login-BZ7J4Z4M.js");
8468
+ const { login } = await import("./login-LH62FYMH.js");
8199
8469
  return login({ config: this.ctx.config });
8200
8470
  }
8201
8471
  case "/logout": {
8202
- const { logout } = await import("./logout-BZKEMNHB.js");
8472
+ const { logout } = await import("./logout-KPHUXO23.js");
8203
8473
  return logout({ config: this.ctx.config });
8204
8474
  }
8205
8475
  case "/permissions": {
8206
- const { permissions } = await import("./permissions-5MTH22EF.js");
8476
+ const { permissions } = await import("./permissions-5W5JOVM7.js");
8207
8477
  return permissions({ permissionManager: this.ctx.permissionManager });
8208
8478
  }
8209
8479
  case "/hooks": {
8210
- const { hooks } = await import("./hooks-5NY5K4L3.js");
8480
+ const { hooks } = await import("./hooks-4N5VRVOF.js");
8211
8481
  if (!this.ctx.hookManager) {
8212
8482
  return "Hook manager not available.";
8213
8483
  }
8214
8484
  return hooks({ hookManager: this.ctx.hookManager });
8215
8485
  }
8216
8486
  case "/skills": {
8217
- const { skills } = await import("./skills-CRFOVWEQ.js");
8487
+ const { skills } = await import("./skills-R25OBHE5.js");
8218
8488
  if (!this.ctx.skillsRegistry) {
8219
8489
  return "Skills registry not available.";
8220
8490
  }
@@ -8224,7 +8494,7 @@ var SlashCommandHandler = class {
8224
8494
  }, args);
8225
8495
  }
8226
8496
  case "/skills install": {
8227
- const { skillsInstall } = await import("./skills-install-RMPXN6RK.js");
8497
+ const { skillsInstall } = await import("./skills-install-GNTBBL46.js");
8228
8498
  if (!this.ctx.skillsRegistry) {
8229
8499
  return "Skills registry not available.";
8230
8500
  }
@@ -8236,7 +8506,7 @@ var SlashCommandHandler = class {
8236
8506
  }
8237
8507
  case "/skills new":
8238
8508
  case "/skills-new": {
8239
- const { createSkill } = await import("./skills-new-S2YPO635.js");
8509
+ const { createSkill } = await import("./skills-new-L36LQXIE.js");
8240
8510
  if (!this.ctx.skillsRegistry) {
8241
8511
  return "Skills registry not available.";
8242
8512
  }
@@ -8247,13 +8517,20 @@ var SlashCommandHandler = class {
8247
8517
  });
8248
8518
  }
8249
8519
  case "/theme": {
8250
- const { theme } = await import("./theme-JAMJSCKR.js");
8520
+ const { theme } = await import("./theme-ZXGSJBZI.js");
8251
8521
  if (!this.ctx.config) {
8252
8522
  console.log(chalk7.yellow("Config not available for theme selection."));
8253
8523
  return null;
8254
8524
  }
8255
8525
  return theme({ config: this.ctx.config });
8256
8526
  }
8527
+ case "/automode": {
8528
+ const { automode } = await import("./automode-XCNP6HP4.js");
8529
+ return automode({
8530
+ automodeManager: this.ctx.automodeManager,
8531
+ workspaceRoot: this.ctx.workspaceRoot
8532
+ }, args);
8533
+ }
8257
8534
  default:
8258
8535
  this.printUnsupported(command);
8259
8536
  return null;
@@ -8277,196 +8554,10 @@ var SlashCommandHandler = class {
8277
8554
  }
8278
8555
  };
8279
8556
 
8280
- // src/session/SessionManager.ts
8281
- import fs10 from "fs-extra";
8282
- import path10 from "path";
8283
- import crypto3 from "crypto";
8284
- var SessionManager = class {
8285
- constructor(baseDir) {
8286
- this.currentSession = null;
8287
- this.index = null;
8288
- this.sessionsDir = baseDir ?? AUTOHAND_PATHS.sessions;
8289
- }
8290
- async initialize() {
8291
- await fs10.ensureDir(this.sessionsDir);
8292
- await this.loadIndex();
8293
- }
8294
- async createSession(projectPath, model) {
8295
- const sessionId = this.generateSessionId();
8296
- const sessionDir = path10.join(this.sessionsDir, sessionId);
8297
- await fs10.ensureDir(sessionDir);
8298
- const metadata26 = {
8299
- sessionId,
8300
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
8301
- lastActiveAt: (/* @__PURE__ */ new Date()).toISOString(),
8302
- projectPath: path10.resolve(projectPath),
8303
- projectName: path10.basename(projectPath),
8304
- model,
8305
- messageCount: 0,
8306
- status: "active"
8307
- };
8308
- const session = new Session(sessionDir, metadata26);
8309
- await session.save();
8310
- this.currentSession = session;
8311
- await this.addToIndex(session.metadata);
8312
- return session;
8313
- }
8314
- async loadSession(sessionId) {
8315
- const sessionDir = path10.join(this.sessionsDir, sessionId);
8316
- if (!await fs10.pathExists(sessionDir)) {
8317
- throw new Error(`Session not found: ${sessionId}`);
8318
- }
8319
- const metadataPath = path10.join(sessionDir, "metadata.json");
8320
- const metadata26 = await fs10.readJson(metadataPath);
8321
- const session = new Session(sessionDir, metadata26);
8322
- await session.load();
8323
- this.currentSession = session;
8324
- return session;
8325
- }
8326
- async listSessions(filter) {
8327
- await this.loadIndex();
8328
- if (!this.index) return [];
8329
- let sessions = this.index.sessions;
8330
- if (filter?.project) {
8331
- const projectPath = path10.resolve(filter.project);
8332
- const sessionIds = this.index.byProject[projectPath] || [];
8333
- sessions = sessions.filter((s) => sessionIds.includes(s.id));
8334
- }
8335
- if (filter?.since) {
8336
- sessions = sessions.filter((s) => new Date(s.createdAt) >= filter.since);
8337
- }
8338
- const fullMetadata = [];
8339
- for (const s of sessions) {
8340
- const sessionDir = path10.join(this.sessionsDir, s.id);
8341
- const metadataPath = path10.join(sessionDir, "metadata.json");
8342
- if (await fs10.pathExists(metadataPath)) {
8343
- const metadata26 = await fs10.readJson(metadataPath);
8344
- fullMetadata.push(metadata26);
8345
- }
8346
- }
8347
- return fullMetadata.sort(
8348
- (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
8349
- );
8350
- }
8351
- async getLastSession(projectPath) {
8352
- const sessions = await this.listSessions(projectPath ? { project: projectPath } : void 0);
8353
- return sessions[0] || null;
8354
- }
8355
- async closeSession(summary) {
8356
- if (!this.currentSession) return;
8357
- this.currentSession.metadata.closedAt = (/* @__PURE__ */ new Date()).toISOString();
8358
- this.currentSession.metadata.lastActiveAt = (/* @__PURE__ */ new Date()).toISOString();
8359
- this.currentSession.metadata.status = "completed";
8360
- if (summary) {
8361
- this.currentSession.metadata.summary = summary;
8362
- }
8363
- await this.currentSession.save();
8364
- await this.updateIndex(this.currentSession.metadata);
8365
- this.currentSession = null;
8366
- }
8367
- getCurrentSession() {
8368
- return this.currentSession;
8369
- }
8370
- generateSessionId() {
8371
- const timestamp = Date.now();
8372
- const uuid = crypto3.randomUUID();
8373
- return `${uuid}-${timestamp}`;
8374
- }
8375
- async loadIndex() {
8376
- const indexPath = path10.join(this.sessionsDir, "index.json");
8377
- if (await fs10.pathExists(indexPath)) {
8378
- this.index = await fs10.readJson(indexPath);
8379
- } else {
8380
- this.index = { sessions: [], byProject: {} };
8381
- }
8382
- }
8383
- async saveIndex() {
8384
- const indexPath = path10.join(this.sessionsDir, "index.json");
8385
- await fs10.writeJson(indexPath, this.index, { spaces: 2 });
8386
- }
8387
- async addToIndex(metadata26) {
8388
- if (!this.index) await this.loadIndex();
8389
- if (!this.index) return;
8390
- this.index.sessions.push({
8391
- id: metadata26.sessionId,
8392
- projectPath: metadata26.projectPath,
8393
- createdAt: metadata26.createdAt,
8394
- summary: metadata26.summary
8395
- });
8396
- if (!this.index.byProject[metadata26.projectPath]) {
8397
- this.index.byProject[metadata26.projectPath] = [];
8398
- }
8399
- this.index.byProject[metadata26.projectPath].push(metadata26.sessionId);
8400
- await this.saveIndex();
8401
- }
8402
- async updateIndex(metadata26) {
8403
- if (!this.index) return;
8404
- const session = this.index.sessions.find((s) => s.id === metadata26.sessionId);
8405
- if (session) {
8406
- session.summary = metadata26.summary;
8407
- }
8408
- await this.saveIndex();
8409
- }
8410
- };
8411
- var Session = class {
8412
- constructor(sessionDir, metadata26) {
8413
- this.messages = [];
8414
- this.state = null;
8415
- this.sessionDir = sessionDir;
8416
- this.metadata = metadata26;
8417
- }
8418
- async append(message) {
8419
- this.messages.push(message);
8420
- this.metadata.messageCount = this.messages.length;
8421
- this.metadata.lastActiveAt = (/* @__PURE__ */ new Date()).toISOString();
8422
- const conversationPath = path10.join(this.sessionDir, "conversation.jsonl");
8423
- await fs10.appendFile(conversationPath, JSON.stringify(message) + "\n");
8424
- await this.save();
8425
- }
8426
- async appendTransient(message) {
8427
- const conversationPath = path10.join(this.sessionDir, "conversation.jsonl");
8428
- await fs10.appendFile(conversationPath, JSON.stringify(message) + "\n");
8429
- }
8430
- async updateState(state) {
8431
- this.state = state;
8432
- const statePath = path10.join(this.sessionDir, "state.json");
8433
- await fs10.writeJson(statePath, state, { spaces: 2 });
8434
- }
8435
- async save() {
8436
- const metadataPath = path10.join(this.sessionDir, "metadata.json");
8437
- await fs10.writeJson(metadataPath, this.metadata, { spaces: 2 });
8438
- }
8439
- async load() {
8440
- const conversationPath = path10.join(this.sessionDir, "conversation.jsonl");
8441
- if (await fs10.pathExists(conversationPath)) {
8442
- const content = await fs10.readFile(conversationPath, "utf-8");
8443
- this.messages = content.trim().split("\n").filter((line) => line).map((line) => JSON.parse(line));
8444
- }
8445
- const statePath = path10.join(this.sessionDir, "state.json");
8446
- if (await fs10.pathExists(statePath)) {
8447
- this.state = await fs10.readJson(statePath);
8448
- }
8449
- }
8450
- getMessages() {
8451
- return this.messages;
8452
- }
8453
- getState() {
8454
- return this.state;
8455
- }
8456
- async close(summary) {
8457
- this.metadata.closedAt = (/* @__PURE__ */ new Date()).toISOString();
8458
- this.metadata.status = "completed";
8459
- if (summary) {
8460
- this.metadata.summary = summary;
8461
- }
8462
- await this.save();
8463
- }
8464
- };
8465
-
8466
8557
  // src/session/ProjectManager.ts
8467
8558
  import fs11 from "fs-extra";
8468
8559
  import path11 from "path";
8469
- import crypto4 from "crypto";
8560
+ import crypto3 from "crypto";
8470
8561
  var ProjectManager = class {
8471
8562
  constructor(baseDir) {
8472
8563
  this.projectCache = /* @__PURE__ */ new Map();
@@ -8658,7 +8749,7 @@ var ProjectManager = class {
8658
8749
  return candidates.filter((c) => tags.some((t) => t.toLowerCase().includes(c)));
8659
8750
  }
8660
8751
  hashPath(projectPath) {
8661
- return crypto4.createHash("sha256").update(path11.resolve(projectPath)).digest("hex").slice(0, 8);
8752
+ return crypto3.createHash("sha256").update(path11.resolve(projectPath)).digest("hex").slice(0, 8);
8662
8753
  }
8663
8754
  };
8664
8755
 
@@ -9022,7 +9113,7 @@ ${result}`;
9022
9113
 
9023
9114
  // src/core/errorLogger.ts
9024
9115
  import fs12 from "fs-extra";
9025
- import os4 from "os";
9116
+ import os5 from "os";
9026
9117
  import path12 from "path";
9027
9118
  var ErrorLogger = class {
9028
9119
  constructor(cliVersion, logDir) {
@@ -9055,13 +9146,13 @@ var ErrorLogger = class {
9055
9146
  }
9056
9147
  }
9057
9148
  getSystemInfo() {
9058
- const cpus = os4.cpus();
9059
- const totalMem = os4.totalmem();
9060
- const freeMem = os4.freemem();
9149
+ const cpus = os5.cpus();
9150
+ const totalMem = os5.totalmem();
9151
+ const freeMem = os5.freemem();
9061
9152
  return {
9062
- platform: os4.platform(),
9063
- arch: os4.arch(),
9064
- release: os4.release(),
9153
+ platform: os5.platform(),
9154
+ arch: os5.arch(),
9155
+ release: os5.release(),
9065
9156
  nodeVersion: process.version,
9066
9157
  bunVersion: process.versions.bun,
9067
9158
  memory: {
@@ -9073,8 +9164,8 @@ var ErrorLogger = class {
9073
9164
  model: cpus[0]?.model || "Unknown",
9074
9165
  cores: cpus.length
9075
9166
  },
9076
- hostname: os4.hostname(),
9077
- username: os4.userInfo().username
9167
+ hostname: os5.hostname(),
9168
+ username: os5.userInfo().username
9078
9169
  };
9079
9170
  }
9080
9171
  async getRecentErrors(count = 10) {
@@ -9100,309 +9191,76 @@ var ErrorLogger = class {
9100
9191
  }
9101
9192
  };
9102
9193
 
9103
- // src/memory/MemoryManager.ts
9194
+ // src/feedback/FeedbackManager.ts
9195
+ import fs14 from "fs-extra";
9196
+ import path14 from "path";
9197
+ import chalk10 from "chalk";
9198
+ import Enquirer from "enquirer";
9199
+
9200
+ // src/feedback/FeedbackApiClient.ts
9104
9201
  import fs13 from "fs-extra";
9105
9202
  import path13 from "path";
9106
- import crypto5 from "crypto";
9107
- var SIMILARITY_THRESHOLD = 0.6;
9108
- var MemoryManager = class {
9109
- constructor(workspaceRoot) {
9110
- this.projectMemoryDir = null;
9111
- this.userMemoryDir = AUTOHAND_PATHS.memory;
9112
- if (workspaceRoot) {
9113
- this.projectMemoryDir = path13.join(workspaceRoot, PROJECT_DIR_NAME, "memory");
9114
- }
9115
- }
9116
- setWorkspace(workspaceRoot) {
9117
- this.projectMemoryDir = path13.join(workspaceRoot, PROJECT_DIR_NAME, "memory");
9118
- }
9119
- async initialize() {
9120
- await fs13.ensureDir(this.userMemoryDir);
9121
- if (this.projectMemoryDir) {
9122
- await fs13.ensureDir(this.projectMemoryDir);
9123
- }
9203
+ import os6 from "os";
9204
+ var DEFAULT_CONFIG = {
9205
+ baseUrl: "https://api.autohand.ai",
9206
+ timeout: 5e3,
9207
+ maxRetries: 3,
9208
+ offlineQueue: true,
9209
+ cliVersion: "0.1.0",
9210
+ companySecret: ""
9211
+ };
9212
+ var FeedbackApiClient = class {
9213
+ constructor(configOverrides) {
9214
+ this.deviceId = null;
9215
+ this.config = { ...DEFAULT_CONFIG, ...configOverrides };
9216
+ const dataDir = AUTOHAND_PATHS.feedback;
9217
+ this.queuePath = path13.join(dataDir, "queue.json");
9218
+ this.deviceIdPath = path13.join(dataDir, ".device-id");
9124
9219
  }
9125
- getMemoryDir(level) {
9126
- if (level === "project") {
9127
- if (!this.projectMemoryDir) {
9128
- throw new Error("Project memory directory not set. Use setWorkspace() first.");
9220
+ // ============ Device ID ============
9221
+ /**
9222
+ * Get or create anonymous device identifier
9223
+ * Used for deduplication and analytics, not tracking
9224
+ */
9225
+ async getDeviceId() {
9226
+ if (this.deviceId) return this.deviceId;
9227
+ try {
9228
+ if (await fs13.pathExists(this.deviceIdPath)) {
9229
+ this.deviceId = (await fs13.readFile(this.deviceIdPath, "utf8")).trim();
9230
+ return this.deviceId;
9129
9231
  }
9130
- return this.projectMemoryDir;
9131
- }
9132
- return this.userMemoryDir;
9133
- }
9134
- async store(content, level, tags, source) {
9135
- const dir = this.getMemoryDir(level);
9136
- await fs13.ensureDir(dir);
9137
- const similar = await this.findSimilar(content, level);
9138
- if (similar && similar.score >= SIMILARITY_THRESHOLD) {
9139
- return this.updateMemory(similar.entry.id, content, level, tags);
9232
+ } catch {
9140
9233
  }
9141
- const id = this.generateId();
9142
- const now = (/* @__PURE__ */ new Date()).toISOString();
9143
- const entry = {
9144
- id,
9145
- content,
9146
- createdAt: now,
9147
- updatedAt: now,
9148
- tags,
9149
- source
9150
- };
9151
- const entryPath = path13.join(dir, `${id}.json`);
9152
- await fs13.writeJson(entryPath, entry, { spaces: 2 });
9153
- await this.updateIndex(level, entry);
9154
- return entry;
9155
- }
9156
- async updateMemory(id, content, level, tags) {
9157
- const dir = this.getMemoryDir(level);
9158
- const entryPath = path13.join(dir, `${id}.json`);
9159
- if (!await fs13.pathExists(entryPath)) {
9160
- throw new Error(`Memory entry not found: ${id}`);
9161
- }
9162
- const existing = await fs13.readJson(entryPath);
9163
- const updated = {
9164
- ...existing,
9165
- content,
9166
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
9167
- tags: tags ?? existing.tags
9168
- };
9169
- await fs13.writeJson(entryPath, updated, { spaces: 2 });
9170
- await this.updateIndex(level, updated);
9171
- return updated;
9172
- }
9173
- async get(id, level) {
9174
- const dir = this.getMemoryDir(level);
9175
- const entryPath = path13.join(dir, `${id}.json`);
9176
- if (!await fs13.pathExists(entryPath)) {
9177
- return null;
9234
+ this.deviceId = `anon_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
9235
+ try {
9236
+ await fs13.ensureDir(path13.dirname(this.deviceIdPath));
9237
+ await fs13.writeFile(this.deviceIdPath, this.deviceId);
9238
+ } catch {
9178
9239
  }
9179
- return fs13.readJson(entryPath);
9240
+ return this.deviceId;
9180
9241
  }
9181
- async list(level) {
9182
- const dir = this.getMemoryDir(level);
9183
- if (!await fs13.pathExists(dir)) {
9184
- return [];
9185
- }
9186
- const files = await fs13.readdir(dir);
9187
- const entries = [];
9188
- for (const file of files) {
9189
- if (file.endsWith(".json") && file !== "index.json") {
9190
- const entryPath = path13.join(dir, file);
9191
- const entry = await fs13.readJson(entryPath);
9192
- entries.push(entry);
9242
+ // ============ Submission ============
9243
+ /**
9244
+ * Submit feedback to the API
9245
+ * Automatically queues for retry if offline
9246
+ */
9247
+ async submit(feedback) {
9248
+ const submission = await this.enrichFeedback(feedback);
9249
+ try {
9250
+ const response = await this.sendToApi(submission);
9251
+ if (response.success) {
9252
+ this.flushQueue().catch(() => {
9253
+ });
9193
9254
  }
9194
- }
9195
- return entries.sort(
9196
- (a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
9197
- );
9198
- }
9199
- async listAll() {
9200
- const user = await this.list("user");
9201
- let project = [];
9202
- if (this.projectMemoryDir) {
9203
- try {
9204
- project = await this.list("project");
9205
- } catch {
9206
- }
9207
- }
9208
- return { project, user };
9209
- }
9210
- async delete(id, level) {
9211
- const dir = this.getMemoryDir(level);
9212
- const entryPath = path13.join(dir, `${id}.json`);
9213
- if (await fs13.pathExists(entryPath)) {
9214
- await fs13.remove(entryPath);
9215
- await this.removeFromIndex(level, id);
9216
- }
9217
- }
9218
- async findSimilar(content, level) {
9219
- const entries = await this.list(level);
9220
- let bestMatch = null;
9221
- for (const entry of entries) {
9222
- const score = this.calculateSimilarity(content, entry.content);
9223
- if (!bestMatch || score > bestMatch.score) {
9224
- bestMatch = { entry, score };
9225
- }
9226
- }
9227
- return bestMatch;
9228
- }
9229
- async search(query, level) {
9230
- const levels = level ? [level] : ["project", "user"];
9231
- const results = [];
9232
- const queryLower = query.toLowerCase();
9233
- for (const lvl of levels) {
9234
- try {
9235
- const entries = await this.list(lvl);
9236
- for (const entry of entries) {
9237
- if (entry.content.toLowerCase().includes(queryLower) || entry.tags?.some((t) => t.toLowerCase().includes(queryLower))) {
9238
- results.push(entry);
9239
- }
9240
- }
9241
- } catch {
9242
- }
9243
- }
9244
- return results;
9245
- }
9246
- async recall(query, level) {
9247
- const levels = level ? [level] : ["user", "project"];
9248
- const results = [];
9249
- for (const lvl of levels) {
9250
- try {
9251
- const entries = await this.list(lvl);
9252
- for (const entry of entries) {
9253
- if (!query || entry.content.toLowerCase().includes(query.toLowerCase())) {
9254
- results.push({ content: entry.content, level: lvl });
9255
- }
9256
- }
9257
- } catch {
9258
- }
9259
- }
9260
- return results;
9261
- }
9262
- /**
9263
- * Get memories formatted for LLM context injection
9264
- */
9265
- async getContextMemories() {
9266
- const { project, user } = await this.listAll();
9267
- const parts = [];
9268
- if (project.length > 0) {
9269
- parts.push("## Project Memories");
9270
- for (const entry of project.slice(0, 10)) {
9271
- parts.push(`- ${entry.content}`);
9272
- }
9273
- }
9274
- if (user.length > 0) {
9275
- parts.push("## User Preferences");
9276
- for (const entry of user.slice(0, 10)) {
9277
- parts.push(`- ${entry.content}`);
9278
- }
9279
- }
9280
- return parts.join("\n");
9281
- }
9282
- calculateSimilarity(a, b) {
9283
- const wordsA = this.tokenize(a);
9284
- const wordsB = this.tokenize(b);
9285
- if (wordsA.size === 0 || wordsB.size === 0) {
9286
- return 0;
9287
- }
9288
- const intersection = new Set([...wordsA].filter((x) => wordsB.has(x)));
9289
- const union = /* @__PURE__ */ new Set([...wordsA, ...wordsB]);
9290
- return intersection.size / union.size;
9291
- }
9292
- tokenize(text) {
9293
- return new Set(
9294
- text.toLowerCase().replace(/[^\w\s]/g, " ").split(/\s+/).filter((w) => w.length > 2)
9295
- );
9296
- }
9297
- generateId() {
9298
- return crypto5.randomUUID().split("-")[0];
9299
- }
9300
- async updateIndex(level, entry) {
9301
- const dir = this.getMemoryDir(level);
9302
- const indexPath = path13.join(dir, "index.json");
9303
- let index;
9304
- if (await fs13.pathExists(indexPath)) {
9305
- index = await fs13.readJson(indexPath);
9306
- } else {
9307
- index = { version: 1, entries: [] };
9308
- }
9309
- const existingIdx = index.entries.findIndex((e) => e.id === entry.id);
9310
- const indexEntry = {
9311
- id: entry.id,
9312
- preview: entry.content.slice(0, 100),
9313
- createdAt: entry.createdAt,
9314
- updatedAt: entry.updatedAt,
9315
- tags: entry.tags
9316
- };
9317
- if (existingIdx >= 0) {
9318
- index.entries[existingIdx] = indexEntry;
9319
- } else {
9320
- index.entries.push(indexEntry);
9321
- }
9322
- await fs13.writeJson(indexPath, index, { spaces: 2 });
9323
- }
9324
- async removeFromIndex(level, id) {
9325
- const dir = this.getMemoryDir(level);
9326
- const indexPath = path13.join(dir, "index.json");
9327
- if (!await fs13.pathExists(indexPath)) {
9328
- return;
9329
- }
9330
- const index = await fs13.readJson(indexPath);
9331
- index.entries = index.entries.filter((e) => e.id !== id);
9332
- await fs13.writeJson(indexPath, index, { spaces: 2 });
9333
- }
9334
- };
9335
-
9336
- // src/feedback/FeedbackManager.ts
9337
- import fs15 from "fs-extra";
9338
- import path15 from "path";
9339
- import chalk10 from "chalk";
9340
- import Enquirer from "enquirer";
9341
-
9342
- // src/feedback/FeedbackApiClient.ts
9343
- import fs14 from "fs-extra";
9344
- import path14 from "path";
9345
- import os5 from "os";
9346
- var DEFAULT_CONFIG = {
9347
- baseUrl: "https://api.autohand.ai",
9348
- timeout: 5e3,
9349
- maxRetries: 3,
9350
- offlineQueue: true,
9351
- cliVersion: "0.1.0",
9352
- companySecret: ""
9353
- };
9354
- var FeedbackApiClient = class {
9355
- constructor(configOverrides) {
9356
- this.deviceId = null;
9357
- this.config = { ...DEFAULT_CONFIG, ...configOverrides };
9358
- const dataDir = AUTOHAND_PATHS.feedback;
9359
- this.queuePath = path14.join(dataDir, "queue.json");
9360
- this.deviceIdPath = path14.join(dataDir, ".device-id");
9361
- }
9362
- // ============ Device ID ============
9363
- /**
9364
- * Get or create anonymous device identifier
9365
- * Used for deduplication and analytics, not tracking
9366
- */
9367
- async getDeviceId() {
9368
- if (this.deviceId) return this.deviceId;
9369
- try {
9370
- if (await fs14.pathExists(this.deviceIdPath)) {
9371
- this.deviceId = (await fs14.readFile(this.deviceIdPath, "utf8")).trim();
9372
- return this.deviceId;
9373
- }
9374
- } catch {
9375
- }
9376
- this.deviceId = `anon_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
9377
- try {
9378
- await fs14.ensureDir(path14.dirname(this.deviceIdPath));
9379
- await fs14.writeFile(this.deviceIdPath, this.deviceId);
9380
- } catch {
9381
- }
9382
- return this.deviceId;
9383
- }
9384
- // ============ Submission ============
9385
- /**
9386
- * Submit feedback to the API
9387
- * Automatically queues for retry if offline
9388
- */
9389
- async submit(feedback) {
9390
- const submission = await this.enrichFeedback(feedback);
9391
- try {
9392
- const response = await this.sendToApi(submission);
9393
- if (response.success) {
9394
- this.flushQueue().catch(() => {
9395
- });
9396
- }
9397
- return response;
9398
- } catch (error) {
9399
- if (this.config.offlineQueue) {
9400
- await this.addToQueue(submission);
9401
- return {
9402
- success: true,
9403
- // Queued successfully
9404
- id: `queued_${Date.now()}`
9405
- };
9255
+ return response;
9256
+ } catch (error) {
9257
+ if (this.config.offlineQueue) {
9258
+ await this.addToQueue(submission);
9259
+ return {
9260
+ success: true,
9261
+ // Queued successfully
9262
+ id: `queued_${Date.now()}`
9263
+ };
9406
9264
  }
9407
9265
  return {
9408
9266
  success: false,
@@ -9419,7 +9277,7 @@ var FeedbackApiClient = class {
9419
9277
  deviceId: await this.getDeviceId(),
9420
9278
  cliVersion: this.config.cliVersion,
9421
9279
  platform: process.platform,
9422
- osVersion: os5.release(),
9280
+ osVersion: os6.release(),
9423
9281
  nodeVersion: process.version
9424
9282
  };
9425
9283
  }
@@ -9463,8 +9321,8 @@ var FeedbackApiClient = class {
9463
9321
  async addToQueue(submission) {
9464
9322
  try {
9465
9323
  let queue = [];
9466
- if (await fs14.pathExists(this.queuePath)) {
9467
- queue = await fs14.readJson(this.queuePath);
9324
+ if (await fs13.pathExists(this.queuePath)) {
9325
+ queue = await fs13.readJson(this.queuePath);
9468
9326
  }
9469
9327
  queue.push({
9470
9328
  feedback: submission,
@@ -9475,8 +9333,8 @@ var FeedbackApiClient = class {
9475
9333
  if (queue.length > 50) {
9476
9334
  queue = queue.slice(-50);
9477
9335
  }
9478
- await fs14.ensureDir(path14.dirname(this.queuePath));
9479
- await fs14.writeJson(this.queuePath, queue, { spaces: 2 });
9336
+ await fs13.ensureDir(path13.dirname(this.queuePath));
9337
+ await fs13.writeJson(this.queuePath, queue, { spaces: 2 });
9480
9338
  } catch {
9481
9339
  }
9482
9340
  }
@@ -9487,10 +9345,10 @@ var FeedbackApiClient = class {
9487
9345
  let sent = 0;
9488
9346
  let failed = 0;
9489
9347
  try {
9490
- if (!await fs14.pathExists(this.queuePath)) {
9348
+ if (!await fs13.pathExists(this.queuePath)) {
9491
9349
  return { sent, failed };
9492
9350
  }
9493
- const queue = await fs14.readJson(this.queuePath);
9351
+ const queue = await fs13.readJson(this.queuePath);
9494
9352
  const remaining = [];
9495
9353
  for (const item of queue) {
9496
9354
  if (item.attempts >= this.config.maxRetries) {
@@ -9515,9 +9373,9 @@ var FeedbackApiClient = class {
9515
9373
  }
9516
9374
  }
9517
9375
  if (remaining.length > 0) {
9518
- await fs14.writeJson(this.queuePath, remaining, { spaces: 2 });
9376
+ await fs13.writeJson(this.queuePath, remaining, { spaces: 2 });
9519
9377
  } else {
9520
- await fs14.remove(this.queuePath);
9378
+ await fs13.remove(this.queuePath);
9521
9379
  }
9522
9380
  } catch {
9523
9381
  }
@@ -9528,10 +9386,10 @@ var FeedbackApiClient = class {
9528
9386
  */
9529
9387
  async getQueueStatus() {
9530
9388
  try {
9531
- if (!await fs14.pathExists(this.queuePath)) {
9389
+ if (!await fs13.pathExists(this.queuePath)) {
9532
9390
  return { pending: 0, oldestItem: null };
9533
9391
  }
9534
- const queue = await fs14.readJson(this.queuePath);
9392
+ const queue = await fs13.readJson(this.queuePath);
9535
9393
  return {
9536
9394
  pending: queue.length,
9537
9395
  oldestItem: queue[0]?.lastAttempt || null
@@ -9597,8 +9455,8 @@ var FeedbackManager = class {
9597
9455
  this.sessionInteractions = 0;
9598
9456
  this.hasPromptedThisSession = false;
9599
9457
  this.stateDir = AUTOHAND_PATHS.feedback;
9600
- this.statePath = path15.join(this.stateDir, "state.json");
9601
- this.responsesPath = path15.join(this.stateDir, "responses.json");
9458
+ this.statePath = path14.join(this.stateDir, "state.json");
9459
+ this.responsesPath = path14.join(this.stateDir, "responses.json");
9602
9460
  this.config = { ...DEFAULT_CONFIG2, ...configOverrides };
9603
9461
  this.state = this.loadState();
9604
9462
  this.sessionStartTime = Date.now();
@@ -9610,8 +9468,8 @@ var FeedbackManager = class {
9610
9468
  // ============ State Management ============
9611
9469
  loadState() {
9612
9470
  try {
9613
- if (fs15.existsSync(this.statePath)) {
9614
- return fs15.readJsonSync(this.statePath);
9471
+ if (fs14.existsSync(this.statePath)) {
9472
+ return fs14.readJsonSync(this.statePath);
9615
9473
  }
9616
9474
  } catch {
9617
9475
  }
@@ -9628,20 +9486,20 @@ var FeedbackManager = class {
9628
9486
  }
9629
9487
  saveState() {
9630
9488
  try {
9631
- fs15.ensureDirSync(this.stateDir);
9632
- fs15.writeJsonSync(this.statePath, this.state, { spaces: 2 });
9489
+ fs14.ensureDirSync(this.stateDir);
9490
+ fs14.writeJsonSync(this.statePath, this.state, { spaces: 2 });
9633
9491
  } catch {
9634
9492
  }
9635
9493
  }
9636
9494
  async saveFeedbackResponse(response) {
9637
9495
  try {
9638
- fs15.ensureDirSync(this.stateDir);
9496
+ fs14.ensureDirSync(this.stateDir);
9639
9497
  let responses = [];
9640
- if (fs15.existsSync(this.responsesPath)) {
9641
- responses = fs15.readJsonSync(this.responsesPath);
9498
+ if (fs14.existsSync(this.responsesPath)) {
9499
+ responses = fs14.readJsonSync(this.responsesPath);
9642
9500
  }
9643
9501
  responses.push(response);
9644
- fs15.writeJsonSync(this.responsesPath, responses, { spaces: 2 });
9502
+ fs14.writeJsonSync(this.responsesPath, responses, { spaces: 2 });
9645
9503
  } catch {
9646
9504
  }
9647
9505
  if (this.config.sendToApi) {
@@ -9907,8 +9765,8 @@ var FeedbackManager = class {
9907
9765
  /** Export all feedback responses */
9908
9766
  async exportResponses() {
9909
9767
  try {
9910
- if (fs15.existsSync(this.responsesPath)) {
9911
- return fs15.readJsonSync(this.responsesPath);
9768
+ if (fs14.existsSync(this.responsesPath)) {
9769
+ return fs14.readJsonSync(this.responsesPath);
9912
9770
  }
9913
9771
  } catch {
9914
9772
  }
@@ -9917,12 +9775,12 @@ var FeedbackManager = class {
9917
9775
  };
9918
9776
 
9919
9777
  // src/telemetry/TelemetryManager.ts
9920
- import os6 from "os";
9778
+ import os7 from "os";
9921
9779
 
9922
9780
  // src/telemetry/TelemetryClient.ts
9923
- import fs16 from "fs-extra";
9924
- import path16 from "path";
9925
- import crypto6 from "crypto";
9781
+ import fs15 from "fs-extra";
9782
+ import path15 from "path";
9783
+ import crypto4 from "crypto";
9926
9784
  var TELEMETRY_DIR = AUTOHAND_PATHS.telemetry;
9927
9785
  var QUEUE_FILE = AUTOHAND_FILES.telemetryQueue;
9928
9786
  var DEVICE_ID_FILE = AUTOHAND_FILES.deviceId;
@@ -9954,15 +9812,15 @@ var TelemetryClient = class {
9954
9812
  */
9955
9813
  getOrCreateDeviceId() {
9956
9814
  try {
9957
- fs16.ensureDirSync(path16.dirname(DEVICE_ID_FILE));
9958
- if (fs16.existsSync(DEVICE_ID_FILE)) {
9959
- return fs16.readFileSync(DEVICE_ID_FILE, "utf8").trim();
9815
+ fs15.ensureDirSync(path15.dirname(DEVICE_ID_FILE));
9816
+ if (fs15.existsSync(DEVICE_ID_FILE)) {
9817
+ return fs15.readFileSync(DEVICE_ID_FILE, "utf8").trim();
9960
9818
  }
9961
- const id = crypto6.randomUUID();
9962
- fs16.writeFileSync(DEVICE_ID_FILE, id);
9819
+ const id = crypto4.randomUUID();
9820
+ fs15.writeFileSync(DEVICE_ID_FILE, id);
9963
9821
  return id;
9964
9822
  } catch {
9965
- return crypto6.randomUUID();
9823
+ return crypto4.randomUUID();
9966
9824
  }
9967
9825
  }
9968
9826
  /**
@@ -9970,9 +9828,9 @@ var TelemetryClient = class {
9970
9828
  */
9971
9829
  loadQueue() {
9972
9830
  try {
9973
- fs16.ensureDirSync(TELEMETRY_DIR);
9974
- if (fs16.existsSync(QUEUE_FILE)) {
9975
- const data = fs16.readFileSync(QUEUE_FILE, "utf8");
9831
+ fs15.ensureDirSync(TELEMETRY_DIR);
9832
+ if (fs15.existsSync(QUEUE_FILE)) {
9833
+ const data = fs15.readFileSync(QUEUE_FILE, "utf8");
9976
9834
  this.queue = JSON.parse(data);
9977
9835
  }
9978
9836
  } catch {
@@ -9984,8 +9842,8 @@ var TelemetryClient = class {
9984
9842
  */
9985
9843
  saveQueue() {
9986
9844
  try {
9987
- fs16.ensureDirSync(TELEMETRY_DIR);
9988
- fs16.writeFileSync(QUEUE_FILE, JSON.stringify(this.queue, null, 2));
9845
+ fs15.ensureDirSync(TELEMETRY_DIR);
9846
+ fs15.writeFileSync(QUEUE_FILE, JSON.stringify(this.queue, null, 2));
9989
9847
  } catch {
9990
9848
  }
9991
9849
  }
@@ -10040,7 +9898,7 @@ var TelemetryClient = class {
10040
9898
  if (!this.config.enabled) return;
10041
9899
  const fullEvent = {
10042
9900
  ...event,
10043
- id: crypto6.randomUUID(),
9901
+ id: crypto4.randomUUID(),
10044
9902
  deviceId: this.deviceId,
10045
9903
  clientType: this.config.clientType,
10046
9904
  clientVersion: this.config.clientVersion,
@@ -10135,16 +9993,16 @@ var TelemetryClient = class {
10135
9993
  const online = await this.isOnline();
10136
9994
  if (!online) {
10137
9995
  try {
10138
- const syncQueueFile = path16.join(TELEMETRY_DIR, "session-sync-queue.json");
9996
+ const syncQueueFile = path15.join(TELEMETRY_DIR, "session-sync-queue.json");
10139
9997
  let syncQueue = [];
10140
- if (fs16.existsSync(syncQueueFile)) {
10141
- syncQueue = JSON.parse(fs16.readFileSync(syncQueueFile, "utf8"));
9998
+ if (fs15.existsSync(syncQueueFile)) {
9999
+ syncQueue = JSON.parse(fs15.readFileSync(syncQueueFile, "utf8"));
10142
10000
  }
10143
10001
  syncQueue.push(sessionData);
10144
10002
  if (syncQueue.length > 10) {
10145
10003
  syncQueue = syncQueue.slice(-10);
10146
10004
  }
10147
- fs16.writeFileSync(syncQueueFile, JSON.stringify(syncQueue, null, 2));
10005
+ fs15.writeFileSync(syncQueueFile, JSON.stringify(syncQueue, null, 2));
10148
10006
  return { success: false, error: "Offline - queued for sync" };
10149
10007
  } catch {
10150
10008
  return { success: false, error: "Failed to queue session" };
@@ -10179,12 +10037,12 @@ var TelemetryClient = class {
10179
10037
  * Sync queued sessions (call when back online)
10180
10038
  */
10181
10039
  async syncQueuedSessions() {
10182
- const syncQueueFile = path16.join(TELEMETRY_DIR, "session-sync-queue.json");
10183
- if (!fs16.existsSync(syncQueueFile)) {
10040
+ const syncQueueFile = path15.join(TELEMETRY_DIR, "session-sync-queue.json");
10041
+ if (!fs15.existsSync(syncQueueFile)) {
10184
10042
  return { synced: 0, failed: 0 };
10185
10043
  }
10186
10044
  try {
10187
- const syncQueue = JSON.parse(fs16.readFileSync(syncQueueFile, "utf8"));
10045
+ const syncQueue = JSON.parse(fs15.readFileSync(syncQueueFile, "utf8"));
10188
10046
  let synced = 0;
10189
10047
  let failed = 0;
10190
10048
  const remaining = [];
@@ -10199,9 +10057,9 @@ var TelemetryClient = class {
10199
10057
  }
10200
10058
  }
10201
10059
  if (remaining.length > 0) {
10202
- fs16.writeFileSync(syncQueueFile, JSON.stringify(remaining, null, 2));
10060
+ fs15.writeFileSync(syncQueueFile, JSON.stringify(remaining, null, 2));
10203
10061
  } else {
10204
- fs16.removeSync(syncQueueFile);
10062
+ fs15.removeSync(syncQueueFile);
10205
10063
  }
10206
10064
  return { synced, failed };
10207
10065
  } catch {
@@ -10252,13 +10110,13 @@ var TelemetryManager = class {
10252
10110
  return {
10253
10111
  cliVersion: package_default.version,
10254
10112
  platform: process.platform,
10255
- osVersion: os6.release(),
10113
+ osVersion: os7.release(),
10256
10114
  nodeVersion: process.version,
10257
10115
  cpuArch: process.arch,
10258
- cpuCores: os6.cpus().length,
10259
- memoryTotal: Math.round(os6.totalmem() / 1024 / 1024),
10116
+ cpuCores: os7.cpus().length,
10117
+ memoryTotal: Math.round(os7.totalmem() / 1024 / 1024),
10260
10118
  // MB
10261
- memoryFree: Math.round(os6.freemem() / 1024 / 1024)
10119
+ memoryFree: Math.round(os7.freemem() / 1024 / 1024)
10262
10120
  // MB
10263
10121
  };
10264
10122
  }
@@ -10478,8 +10336,8 @@ var TelemetryManager = class {
10478
10336
  };
10479
10337
 
10480
10338
  // src/skills/CommunitySkillsClient.ts
10481
- import fs17 from "fs-extra";
10482
- import path17 from "path";
10339
+ import fs16 from "fs-extra";
10340
+ import path16 from "path";
10483
10341
  var CommunitySkillsClient = class {
10484
10342
  constructor(config) {
10485
10343
  this.queue = [];
@@ -10487,7 +10345,7 @@ var CommunitySkillsClient = class {
10487
10345
  this.apiBaseUrl = config.apiBaseUrl.replace(/\/$/, "");
10488
10346
  this.enabled = config.enabled;
10489
10347
  this.deviceId = config.deviceId || this.loadDeviceId();
10490
- this.queueDir = config.queueDir || path17.join(AUTOHAND_HOME, "community-skills");
10348
+ this.queueDir = config.queueDir || path16.join(AUTOHAND_HOME, "community-skills");
10491
10349
  this.timeout = config.timeout || 1e4;
10492
10350
  this.maxRetries = config.maxRetries || 3;
10493
10351
  }
@@ -10495,10 +10353,10 @@ var CommunitySkillsClient = class {
10495
10353
  * Load device ID from filesystem
10496
10354
  */
10497
10355
  loadDeviceId() {
10498
- const deviceIdPath = path17.join(AUTOHAND_HOME, "device-id");
10356
+ const deviceIdPath = path16.join(AUTOHAND_HOME, "device-id");
10499
10357
  try {
10500
- if (fs17.existsSync(deviceIdPath)) {
10501
- return fs17.readFileSync(deviceIdPath, "utf-8").trim();
10358
+ if (fs16.existsSync(deviceIdPath)) {
10359
+ return fs16.readFileSync(deviceIdPath, "utf-8").trim();
10502
10360
  }
10503
10361
  } catch {
10504
10362
  }
@@ -10510,10 +10368,10 @@ var CommunitySkillsClient = class {
10510
10368
  loadQueue() {
10511
10369
  if (this.queueLoaded) return;
10512
10370
  this.queueLoaded = true;
10513
- const queuePath = path17.join(this.queueDir, "queue.json");
10371
+ const queuePath = path16.join(this.queueDir, "queue.json");
10514
10372
  try {
10515
- if (fs17.existsSync(queuePath)) {
10516
- this.queue = fs17.readJsonSync(queuePath);
10373
+ if (fs16.existsSync(queuePath)) {
10374
+ this.queue = fs16.readJsonSync(queuePath);
10517
10375
  }
10518
10376
  } catch {
10519
10377
  this.queue = [];
@@ -10524,8 +10382,8 @@ var CommunitySkillsClient = class {
10524
10382
  */
10525
10383
  saveQueue() {
10526
10384
  try {
10527
- fs17.ensureDirSync(this.queueDir);
10528
- fs17.writeJsonSync(path17.join(this.queueDir, "queue.json"), this.queue);
10385
+ fs16.ensureDirSync(this.queueDir);
10386
+ fs16.writeJsonSync(path16.join(this.queueDir, "queue.json"), this.queue);
10529
10387
  } catch {
10530
10388
  }
10531
10389
  }
@@ -11202,7 +11060,7 @@ function createPersistentInput(options) {
11202
11060
  }
11203
11061
 
11204
11062
  // src/ui/toolOutput.ts
11205
- import * as path18 from "path";
11063
+ import * as path17 from "path";
11206
11064
  var FILE_SUMMARY_TOOLS = /* @__PURE__ */ new Set([
11207
11065
  "read_file",
11208
11066
  "write_file",
@@ -11242,9 +11100,9 @@ ${truncatedContent}` : outputLines > 0 ? `
11242
11100
  };
11243
11101
  }
11244
11102
  if (FILE_SUMMARY_TOOLS.has(tool) && filePath) {
11245
- const fileName = path18.basename(filePath);
11246
- const dirName = path18.dirname(filePath);
11247
- const displayPath = dirName === "." ? fileName : `${path18.basename(dirName)}/${fileName}`;
11103
+ const fileName = path17.basename(filePath);
11104
+ const dirName = path17.dirname(filePath);
11105
+ const displayPath = dirName === "." ? fileName : `${path17.basename(dirName)}/${fileName}`;
11248
11106
  const lines = countLines(content);
11249
11107
  const size = formatFileSize(Buffer.byteLength(content, "utf8"));
11250
11108
  return {
@@ -11265,496 +11123,6 @@ ${truncatedContent}` : outputLines > 0 ? `
11265
11123
  return { output: content, truncated: false, totalChars };
11266
11124
  }
11267
11125
 
11268
- // src/core/HookManager.ts
11269
- import { spawn as spawn3 } from "child_process";
11270
- import { minimatch } from "minimatch";
11271
- var DEFAULT_HOOK_TIMEOUT = 5e3;
11272
- var HookManager = class {
11273
- constructor(options) {
11274
- this.initialized = false;
11275
- this.settings = options.settings ?? { enabled: true, hooks: [] };
11276
- this.workspaceRoot = options.workspaceRoot;
11277
- this.onPersist = options.onPersist;
11278
- this.onHookOutput = options.onHookOutput;
11279
- }
11280
- /**
11281
- * Initialize hooks - set up default hooks if none exist
11282
- */
11283
- async initialize() {
11284
- if (this.initialized) return;
11285
- this.initialized = true;
11286
- if (!this.settings.hooks || this.settings.hooks.length === 0) {
11287
- await this.installDefaultHooks();
11288
- } else {
11289
- await this.mergeDefaultHooks();
11290
- }
11291
- await this.ensureHookScripts();
11292
- }
11293
- /**
11294
- * Install default hooks (disabled by default)
11295
- */
11296
- async installDefaultHooks() {
11297
- try {
11298
- const { DEFAULT_HOOKS, SMART_COMMIT_HOOK } = await import("./defaultHooks-3G3DVF6I.js");
11299
- this.settings.hooks = [...DEFAULT_HOOKS, SMART_COMMIT_HOOK];
11300
- if (this.onPersist) {
11301
- await this.onPersist();
11302
- }
11303
- } catch {
11304
- }
11305
- }
11306
- /**
11307
- * Get a unique identifier for a hook (used for deduplication)
11308
- * Uses script filename for script-based hooks, or event+description for inline commands
11309
- */
11310
- getHookIdentifier(hook) {
11311
- const scriptMatch = hook.command.match(/([^/]+\.sh)$/);
11312
- if (scriptMatch) {
11313
- return `script:${scriptMatch[1]}`;
11314
- }
11315
- if (hook.description) {
11316
- return `${hook.event}:${hook.description}`;
11317
- }
11318
- return `${hook.event}:${hook.command}`;
11319
- }
11320
- /**
11321
- * Merge any missing default hooks with existing user hooks
11322
- * This ensures new built-in hooks are added when upgrading
11323
- */
11324
- async mergeDefaultHooks() {
11325
- try {
11326
- const { DEFAULT_HOOKS, SMART_COMMIT_HOOK } = await import("./defaultHooks-3G3DVF6I.js");
11327
- const allDefaults = [...DEFAULT_HOOKS, SMART_COMMIT_HOOK];
11328
- const existingHooks = this.settings.hooks ?? [];
11329
- const existingIds = new Set(existingHooks.map((h) => this.getHookIdentifier(h)));
11330
- const missingHooks = allDefaults.filter((h) => !existingIds.has(this.getHookIdentifier(h)));
11331
- if (missingHooks.length > 0) {
11332
- this.settings.hooks = [...existingHooks, ...missingHooks];
11333
- if (this.onPersist) {
11334
- await this.onPersist();
11335
- }
11336
- }
11337
- } catch (error) {
11338
- console.error("[hooks] Failed to merge default hooks:", error);
11339
- }
11340
- }
11341
- /**
11342
- * Ensure all hook scripts exist in ~/.autohand/hooks/
11343
- * Installs bundled scripts for built-in hooks
11344
- */
11345
- async ensureHookScripts() {
11346
- try {
11347
- const fs22 = await import("fs-extra");
11348
- const path21 = await import("path");
11349
- const os8 = await import("os");
11350
- const hooksDir = path21.join(os8.homedir(), ".autohand", "hooks");
11351
- await fs22.ensureDir(hooksDir);
11352
- const { HOOK_SCRIPTS } = await import("./defaultHooks-3G3DVF6I.js");
11353
- for (const [scriptName, scriptContent] of Object.entries(HOOK_SCRIPTS)) {
11354
- const scriptPath = path21.join(hooksDir, scriptName);
11355
- if (!await fs22.pathExists(scriptPath)) {
11356
- await fs22.writeFile(scriptPath, scriptContent, { mode: 493 });
11357
- }
11358
- }
11359
- } catch {
11360
- }
11361
- }
11362
- /**
11363
- * Check if hooks are globally enabled
11364
- */
11365
- isEnabled() {
11366
- return this.settings.enabled !== false;
11367
- }
11368
- /**
11369
- * Get all registered hooks
11370
- */
11371
- getHooks() {
11372
- return this.settings.hooks ?? [];
11373
- }
11374
- /**
11375
- * Get current settings
11376
- */
11377
- getSettings() {
11378
- return { ...this.settings };
11379
- }
11380
- /**
11381
- * Update settings
11382
- */
11383
- async updateSettings(settings) {
11384
- this.settings = { ...this.settings, ...settings };
11385
- if (this.onPersist) {
11386
- await this.onPersist();
11387
- }
11388
- }
11389
- /**
11390
- * Add a new hook
11391
- */
11392
- async addHook(hook) {
11393
- const hooks = this.settings.hooks ?? [];
11394
- hooks.push({ ...hook, enabled: hook.enabled !== false });
11395
- this.settings.hooks = hooks;
11396
- if (this.onPersist) {
11397
- await this.onPersist();
11398
- }
11399
- }
11400
- /**
11401
- * Remove a hook by index within its event type
11402
- */
11403
- async removeHook(event, index) {
11404
- const hooks = this.settings.hooks ?? [];
11405
- const eventHooks = hooks.filter((h) => h.event === event);
11406
- if (index < 0 || index >= eventHooks.length) {
11407
- return false;
11408
- }
11409
- const hookToRemove = eventHooks[index];
11410
- const globalIndex = hooks.indexOf(hookToRemove);
11411
- if (globalIndex !== -1) {
11412
- hooks.splice(globalIndex, 1);
11413
- this.settings.hooks = hooks;
11414
- if (this.onPersist) {
11415
- await this.onPersist();
11416
- }
11417
- return true;
11418
- }
11419
- return false;
11420
- }
11421
- /**
11422
- * Toggle a hook's enabled status
11423
- */
11424
- async toggleHook(event, index) {
11425
- const hooks = this.settings.hooks ?? [];
11426
- const eventHooks = hooks.filter((h) => h.event === event);
11427
- if (index < 0 || index >= eventHooks.length) {
11428
- return false;
11429
- }
11430
- const hook = eventHooks[index];
11431
- hook.enabled = hook.enabled === false;
11432
- if (this.onPersist) {
11433
- await this.onPersist();
11434
- }
11435
- return true;
11436
- }
11437
- /**
11438
- * Check if a hook's filter matches the context
11439
- */
11440
- matchesFilter(filter, context) {
11441
- if (!filter) return true;
11442
- if (filter.tool && filter.tool.length > 0) {
11443
- if (!context.tool || !filter.tool.includes(context.tool)) {
11444
- return false;
11445
- }
11446
- }
11447
- if (filter.path && filter.path.length > 0) {
11448
- if (!context.path) return false;
11449
- const matches = filter.path.some((pattern) => minimatch(context.path, pattern));
11450
- if (!matches) return false;
11451
- }
11452
- return true;
11453
- }
11454
- /**
11455
- * Check if a hook's regex matcher matches the context
11456
- * Matchers apply to tool names, notification types, session types, etc.
11457
- */
11458
- matchesMatcher(hook, context) {
11459
- if (!hook.matcher) return true;
11460
- let value = "";
11461
- switch (hook.event) {
11462
- case "pre-tool":
11463
- case "post-tool":
11464
- case "permission-request":
11465
- value = context.tool ?? "";
11466
- break;
11467
- case "notification":
11468
- value = context.notificationType ?? "";
11469
- break;
11470
- case "session-start":
11471
- value = context.sessionType ?? "";
11472
- break;
11473
- case "session-end":
11474
- value = context.sessionEndReason ?? "";
11475
- break;
11476
- case "subagent-stop":
11477
- value = context.subagentType ?? "";
11478
- break;
11479
- default:
11480
- return true;
11481
- }
11482
- try {
11483
- return new RegExp(hook.matcher).test(value);
11484
- } catch {
11485
- return false;
11486
- }
11487
- }
11488
- /**
11489
- * Build environment variables from hook context
11490
- */
11491
- buildEnvironment(context) {
11492
- const env = {
11493
- ...process.env,
11494
- HOOK_EVENT: context.event,
11495
- HOOK_WORKSPACE: context.workspace
11496
- };
11497
- if (context.sessionId) env.HOOK_SESSION_ID = context.sessionId;
11498
- if (context.tool) env.HOOK_TOOL = context.tool;
11499
- if (context.toolCallId) env.HOOK_TOOL_CALL_ID = context.toolCallId;
11500
- if (context.args) env.HOOK_ARGS = JSON.stringify(context.args);
11501
- if (context.success !== void 0) env.HOOK_SUCCESS = String(context.success);
11502
- if (context.output) env.HOOK_OUTPUT = context.output;
11503
- if (context.duration !== void 0) env.HOOK_DURATION = String(context.duration);
11504
- if (context.path) env.HOOK_PATH = context.path;
11505
- if (context.changeType) env.HOOK_CHANGE_TYPE = context.changeType;
11506
- if (context.instruction) env.HOOK_INSTRUCTION = context.instruction;
11507
- if (context.mentionedFiles) env.HOOK_MENTIONED_FILES = JSON.stringify(context.mentionedFiles);
11508
- if (context.tokensUsed !== void 0) env.HOOK_TOKENS = String(context.tokensUsed);
11509
- if (context.toolCallsCount !== void 0) env.HOOK_TOOL_CALLS_COUNT = String(context.toolCallsCount);
11510
- if (context.toolCallsInTurn !== void 0) env.HOOK_TURN_TOOL_CALLS = String(context.toolCallsInTurn);
11511
- if (context.turnDuration !== void 0) env.HOOK_TURN_DURATION = String(context.turnDuration);
11512
- if (context.error) env.HOOK_ERROR = context.error;
11513
- if (context.errorCode) env.HOOK_ERROR_CODE = context.errorCode;
11514
- if (context.sessionType) env.HOOK_SESSION_TYPE = context.sessionType;
11515
- if (context.sessionEndReason) env.HOOK_SESSION_END_REASON = context.sessionEndReason;
11516
- if (context.subagentId) env.HOOK_SUBAGENT_ID = context.subagentId;
11517
- if (context.subagentType) env.HOOK_SUBAGENT_TYPE = context.subagentType;
11518
- if (context.permissionType) env.HOOK_PERMISSION_TYPE = context.permissionType;
11519
- if (context.notificationType) env.HOOK_NOTIFICATION_TYPE = context.notificationType;
11520
- if (context.notificationMessage) env.HOOK_NOTIFICATION_MSG = context.notificationMessage;
11521
- return env;
11522
- }
11523
- /**
11524
- * Build JSON input to pass via stdin to hook
11525
- */
11526
- buildJsonInput(context) {
11527
- return JSON.stringify({
11528
- session_id: context.sessionId,
11529
- cwd: context.workspace,
11530
- hook_event_name: context.event,
11531
- // Tool context
11532
- tool_name: context.tool,
11533
- tool_input: context.args,
11534
- tool_use_id: context.toolCallId,
11535
- tool_response: context.output,
11536
- tool_success: context.success,
11537
- // File context
11538
- file_path: context.path,
11539
- change_type: context.changeType,
11540
- // Prompt context
11541
- instruction: context.instruction,
11542
- mentioned_files: context.mentionedFiles,
11543
- // Stop/response context
11544
- tokens_used: context.tokensUsed,
11545
- tool_calls_count: context.toolCallsCount,
11546
- turn_tool_calls: context.toolCallsInTurn,
11547
- turn_duration: context.turnDuration,
11548
- duration: context.duration,
11549
- // Error context
11550
- error: context.error,
11551
- error_code: context.errorCode,
11552
- // Session context
11553
- session_type: context.sessionType,
11554
- session_end_reason: context.sessionEndReason,
11555
- // Subagent context
11556
- subagent_id: context.subagentId,
11557
- subagent_name: context.subagentName,
11558
- subagent_type: context.subagentType,
11559
- subagent_success: context.subagentSuccess,
11560
- subagent_error: context.subagentError,
11561
- subagent_duration: context.subagentDuration,
11562
- // Permission context
11563
- permission_type: context.permissionType,
11564
- // Notification context
11565
- notification_type: context.notificationType,
11566
- notification_message: context.notificationMessage
11567
- });
11568
- }
11569
- /**
11570
- * Parse JSON response from hook stdout
11571
- */
11572
- parseHookResponse(stdout) {
11573
- const trimmed = stdout.trim();
11574
- if (!trimmed.startsWith("{")) {
11575
- return void 0;
11576
- }
11577
- try {
11578
- return JSON.parse(trimmed);
11579
- } catch {
11580
- return void 0;
11581
- }
11582
- }
11583
- /**
11584
- * Execute a single hook
11585
- */
11586
- async executeHook(hook, context) {
11587
- const startTime = Date.now();
11588
- const timeout = hook.timeout ?? DEFAULT_HOOK_TIMEOUT;
11589
- const env = this.buildEnvironment(context);
11590
- const jsonInput = this.buildJsonInput(context);
11591
- return new Promise((resolve) => {
11592
- const child = spawn3(hook.command, [], {
11593
- shell: true,
11594
- cwd: this.workspaceRoot,
11595
- env,
11596
- stdio: ["pipe", "pipe", "pipe"]
11597
- // stdin enabled for JSON input
11598
- });
11599
- let stdout = "";
11600
- let stderr = "";
11601
- let killed = false;
11602
- const timeoutId = setTimeout(() => {
11603
- killed = true;
11604
- child.kill("SIGTERM");
11605
- setTimeout(() => child.kill("SIGKILL"), 1e3);
11606
- }, timeout);
11607
- child.stdin?.write(jsonInput);
11608
- child.stdin?.end();
11609
- child.stdout?.on("data", (data) => {
11610
- stdout += data.toString();
11611
- });
11612
- child.stderr?.on("data", (data) => {
11613
- stderr += data.toString();
11614
- });
11615
- child.on("close", (code) => {
11616
- clearTimeout(timeoutId);
11617
- const duration = Date.now() - startTime;
11618
- const exitCode = code ?? 0;
11619
- const isBlockingError = exitCode === 2;
11620
- let response;
11621
- if (exitCode === 0) {
11622
- response = this.parseHookResponse(stdout);
11623
- }
11624
- const result = {
11625
- hook,
11626
- success: !killed && exitCode === 0,
11627
- stdout: stdout.trim() || void 0,
11628
- stderr: stderr.trim() || void 0,
11629
- error: killed ? `Hook timed out after ${timeout}ms` : isBlockingError ? stderr.trim() || "Hook blocked execution" : void 0,
11630
- duration,
11631
- exitCode,
11632
- blockingError: isBlockingError,
11633
- response
11634
- };
11635
- if (this.onHookOutput) {
11636
- this.onHookOutput(result);
11637
- }
11638
- resolve(result);
11639
- });
11640
- child.on("error", (err) => {
11641
- clearTimeout(timeoutId);
11642
- const duration = Date.now() - startTime;
11643
- const result = {
11644
- hook,
11645
- success: false,
11646
- error: err.message,
11647
- duration,
11648
- exitCode: -1
11649
- };
11650
- if (this.onHookOutput) {
11651
- this.onHookOutput(result);
11652
- }
11653
- resolve(result);
11654
- });
11655
- });
11656
- }
11657
- /**
11658
- * Get hooks for an event, including alias handling
11659
- */
11660
- getHooksForEvent(event) {
11661
- const hooks = this.getHooks().filter((h) => h.enabled !== false);
11662
- if (event === "stop") {
11663
- return hooks.filter((h) => h.event === "stop" || h.event === "post-response");
11664
- }
11665
- if (event === "post-response") {
11666
- return hooks.filter((h) => h.event === "stop" || h.event === "post-response");
11667
- }
11668
- return hooks.filter((h) => h.event === event);
11669
- }
11670
- /**
11671
- * Execute all hooks for an event
11672
- *
11673
- * Sync hooks are executed sequentially and block until complete.
11674
- * Async hooks are executed in parallel and don't block.
11675
- */
11676
- async executeHooks(event, context) {
11677
- if (!this.isEnabled()) {
11678
- return [];
11679
- }
11680
- const fullContext = {
11681
- ...context,
11682
- event,
11683
- workspace: this.workspaceRoot
11684
- };
11685
- const hooks = this.getHooksForEvent(event).filter(
11686
- (h) => this.matchesFilter(h.filter, fullContext) && this.matchesMatcher(h, fullContext)
11687
- );
11688
- if (hooks.length === 0) {
11689
- return [];
11690
- }
11691
- const syncHooks = hooks.filter((h) => !h.async);
11692
- const asyncHooks = hooks.filter((h) => h.async);
11693
- const results = [];
11694
- for (const hook of syncHooks) {
11695
- const result = await this.executeHook(hook, fullContext);
11696
- results.push(result);
11697
- if (result.response?.continue === false) {
11698
- break;
11699
- }
11700
- }
11701
- if (asyncHooks.length > 0) {
11702
- const asyncResults = await Promise.all(
11703
- asyncHooks.map((hook) => this.executeHook(hook, fullContext))
11704
- );
11705
- results.push(...asyncResults);
11706
- }
11707
- return results;
11708
- }
11709
- /**
11710
- * Test a hook by executing it with a sample context
11711
- */
11712
- async testHook(hook) {
11713
- const context = {
11714
- event: hook.event,
11715
- workspace: this.workspaceRoot,
11716
- tool: "test_tool",
11717
- toolCallId: "test_123",
11718
- args: { test: true },
11719
- success: true,
11720
- path: "test/file.ts",
11721
- changeType: "modify",
11722
- instruction: "Test instruction",
11723
- tokensUsed: 100
11724
- };
11725
- return this.executeHook(hook, context);
11726
- }
11727
- /**
11728
- * Get a summary of hooks by event
11729
- */
11730
- getSummary() {
11731
- const events = [
11732
- "pre-tool",
11733
- "post-tool",
11734
- "file-modified",
11735
- "pre-prompt",
11736
- "stop",
11737
- "post-response",
11738
- // Alias for 'stop'
11739
- "session-error",
11740
- "subagent-stop",
11741
- "session-start",
11742
- "session-end",
11743
- "permission-request",
11744
- "notification"
11745
- ];
11746
- const summary = {};
11747
- for (const event of events) {
11748
- const eventHooks = this.getHooks().filter((h) => h.event === event);
11749
- summary[event] = {
11750
- total: eventHooks.length,
11751
- enabled: eventHooks.filter((h) => h.enabled !== false).length
11752
- };
11753
- }
11754
- return summary;
11755
- }
11756
- };
11757
-
11758
11126
  // src/ui/promptCallback.ts
11759
11127
  import enquirer2 from "enquirer";
11760
11128
  function isExternalCallbackEnabled() {
@@ -12146,7 +11514,7 @@ var IntentDetector = class {
12146
11514
  };
12147
11515
 
12148
11516
  // src/core/EnvironmentBootstrap.ts
12149
- import fs18 from "fs-extra";
11517
+ import fs17 from "fs-extra";
12150
11518
  import { join as join2 } from "path";
12151
11519
  import { exec } from "child_process";
12152
11520
  import { promisify } from "util";
@@ -12242,14 +11610,14 @@ var EnvironmentBootstrap = class {
12242
11610
  */
12243
11611
  async detectPackageManager(root) {
12244
11612
  for (const { file, pm } of this.lockfileChecks) {
12245
- if (await fs18.pathExists(join2(root, file))) {
11613
+ if (await fs17.pathExists(join2(root, file))) {
12246
11614
  return pm;
12247
11615
  }
12248
11616
  }
12249
11617
  const pkgPath = join2(root, "package.json");
12250
- if (await fs18.pathExists(pkgPath)) {
11618
+ if (await fs17.pathExists(pkgPath)) {
12251
11619
  try {
12252
- const pkg = await fs18.readJson(pkgPath);
11620
+ const pkg = await fs17.readJson(pkgPath);
12253
11621
  if (pkg.packageManager) {
12254
11622
  const match = pkg.packageManager.match(/^(bun|pnpm|yarn|npm)@/);
12255
11623
  if (match) {
@@ -12280,7 +11648,7 @@ var EnvironmentBootstrap = class {
12280
11648
  const step = { name: "Git Sync", status: "running" };
12281
11649
  const start = Date.now();
12282
11650
  try {
12283
- const isGit = await fs18.pathExists(join2(root, ".git"));
11651
+ const isGit = await fs17.pathExists(join2(root, ".git"));
12284
11652
  if (!isGit) {
12285
11653
  return {
12286
11654
  ...step,
@@ -12361,18 +11729,18 @@ var EnvironmentBootstrap = class {
12361
11729
  const details = [];
12362
11730
  try {
12363
11731
  const nvmrcPath = join2(root, ".nvmrc");
12364
- const nvmrcExists = await fs18.pathExists(nvmrcPath);
11732
+ const nvmrcExists = await fs17.pathExists(nvmrcPath);
12365
11733
  const nodeVersionPath = join2(root, ".node-version");
12366
- const nodeVersionExists = await fs18.pathExists(nodeVersionPath);
11734
+ const nodeVersionExists = await fs17.pathExists(nodeVersionPath);
12367
11735
  if (nvmrcExists || nodeVersionExists) {
12368
11736
  details.push("Node version file found");
12369
11737
  }
12370
11738
  const toolVersionsPath = join2(root, ".tool-versions");
12371
- if (await fs18.pathExists(toolVersionsPath)) {
11739
+ if (await fs17.pathExists(toolVersionsPath)) {
12372
11740
  details.push(".tool-versions found");
12373
11741
  }
12374
11742
  const rustToolchainPath = join2(root, "rust-toolchain.toml");
12375
- if (await fs18.pathExists(rustToolchainPath)) {
11743
+ if (await fs17.pathExists(rustToolchainPath)) {
12376
11744
  details.push("Rust toolchain found");
12377
11745
  }
12378
11746
  step.status = "success";
@@ -12449,7 +11817,7 @@ var EnvironmentBootstrap = class {
12449
11817
  };
12450
11818
 
12451
11819
  // src/core/CodeQualityPipeline.ts
12452
- import fs19 from "fs-extra";
11820
+ import fs18 from "fs-extra";
12453
11821
  import { join as join3 } from "path";
12454
11822
  import { exec as exec2 } from "child_process";
12455
11823
  import { promisify as promisify2 } from "util";
@@ -12519,11 +11887,11 @@ var CodeQualityPipeline = class {
12519
11887
  */
12520
11888
  async detectScripts(root) {
12521
11889
  const pkgPath = join3(root, "package.json");
12522
- if (!await fs19.pathExists(pkgPath)) {
11890
+ if (!await fs18.pathExists(pkgPath)) {
12523
11891
  return {};
12524
11892
  }
12525
11893
  try {
12526
- const pkg = await fs19.readJson(pkgPath);
11894
+ const pkg = await fs18.readJson(pkgPath);
12527
11895
  const scripts = pkg.scripts || {};
12528
11896
  return {
12529
11897
  lint: this.findScript(scripts, this.scriptPatterns.lint),
@@ -13337,7 +12705,7 @@ If lint or tests fail, report the issues but do NOT commit.`;
13337
12705
  }
13338
12706
  }
13339
12707
  async listWorkspaceFiles() {
13340
- const entries = await fs20.readdir(this.runtime.workspaceRoot);
12708
+ const entries = await fs19.readdir(this.runtime.workspaceRoot);
13341
12709
  const sorted = entries.sort((a, b) => a.localeCompare(b));
13342
12710
  console.log("\n" + chalk13.cyan("Workspace files:"));
13343
12711
  console.log(sorted.map((entry) => ` - ${entry}`).join("\n"));
@@ -13364,18 +12732,18 @@ If lint or tests fail, report the issues but do NOT commit.`;
13364
12732
  async walkWorkspace(current, acc) {
13365
12733
  let entries;
13366
12734
  try {
13367
- entries = await fs20.readdir(current);
12735
+ entries = await fs19.readdir(current);
13368
12736
  } catch {
13369
12737
  return;
13370
12738
  }
13371
12739
  for (const entry of entries) {
13372
- const full = path19.join(current, entry);
13373
- const rel = path19.relative(this.runtime.workspaceRoot, full);
12740
+ const full = path18.join(current, entry);
12741
+ const rel = path18.relative(this.runtime.workspaceRoot, full);
13374
12742
  if (rel === "" || this.shouldSkipPath(rel) || this.ignoreFilter.isIgnored(rel)) {
13375
12743
  continue;
13376
12744
  }
13377
12745
  try {
13378
- const stats = await fs20.stat(full);
12746
+ const stats = await fs19.stat(full);
13379
12747
  if (stats.isDirectory()) {
13380
12748
  await this.walkWorkspace(full, acc);
13381
12749
  } else if (stats.isFile()) {
@@ -13745,8 +13113,8 @@ ${selectedProvider} is not configured yet. Let's set it up!
13745
13113
  );
13746
13114
  }
13747
13115
  async createAgentsFile() {
13748
- const target = path19.join(this.runtime.workspaceRoot, "AGENTS.md");
13749
- if (await fs20.pathExists(target)) {
13116
+ const target = path18.join(this.runtime.workspaceRoot, "AGENTS.md");
13117
+ if (await fs19.pathExists(target)) {
13750
13118
  console.log(chalk13.gray("AGENTS.md already exists in this workspace."));
13751
13119
  return;
13752
13120
  }
@@ -13754,7 +13122,7 @@ ${selectedProvider} is not configured yet. Let's set it up!
13754
13122
 
13755
13123
  Describe how Autohand should work in this repo. Include framework commands, testing requirements, and any constraints.
13756
13124
  `;
13757
- await fs20.writeFile(target, template, "utf8");
13125
+ await fs19.writeFile(target, template, "utf8");
13758
13126
  console.log(chalk13.green("Created AGENTS.md template. Customize it to guide the agent."));
13759
13127
  }
13760
13128
  /**
@@ -14016,17 +13384,18 @@ Completed in ${elapsed} \xB7 ${tokens} used${queueStatus}`));
14016
13384
  `));
14017
13385
  }
14018
13386
  async runReactLoop(abortController) {
14019
- process.stderr.write(`[AGENT DEBUG] runReactLoop started
13387
+ const debugMode = this.runtime.config.agent?.debug === true || process.env.AUTOHAND_DEBUG === "1";
13388
+ if (debugMode) process.stderr.write(`[AGENT DEBUG] runReactLoop started
14020
13389
  `);
14021
13390
  const maxIterations = this.runtime.config.agent?.maxIterations ?? 100;
14022
13391
  const allTools = this.toolManager.toFunctionDefinitions();
14023
- process.stderr.write(`[AGENT DEBUG] Loaded ${allTools.length} tools, maxIterations=${maxIterations}
13392
+ if (debugMode) process.stderr.write(`[AGENT DEBUG] Loaded ${allTools.length} tools, maxIterations=${maxIterations}
14024
13393
  `);
14025
13394
  this.startStatusUpdates();
14026
13395
  const showThinking = this.runtime.config.ui?.showThinking !== false;
14027
13396
  for (let iteration = 0; iteration < maxIterations; iteration += 1) {
14028
13397
  if (abortController.signal.aborted) {
14029
- process.stderr.write("[AGENT DEBUG] Abort detected at loop start, breaking\n");
13398
+ if (debugMode) process.stderr.write("[AGENT DEBUG] Abort detected at loop start, breaking\n");
14030
13399
  break;
14031
13400
  }
14032
13401
  const messages = this.conversation.history();
@@ -14066,7 +13435,7 @@ ${summary}`
14066
13435
  this.runtime.spinner?.start("Working ...");
14067
13436
  }
14068
13437
  const messagesWithImages = this.getMessagesWithImages();
14069
- process.stderr.write(`[AGENT DEBUG] Calling LLM with ${messagesWithImages.length} messages, ${tools.length} tools
13438
+ if (debugMode) process.stderr.write(`[AGENT DEBUG] Calling LLM with ${messagesWithImages.length} messages, ${tools.length} tools
14070
13439
  `);
14071
13440
  let completion;
14072
13441
  try {
@@ -14080,14 +13449,14 @@ ${summary}`
14080
13449
  maxTokens: 16e3
14081
13450
  // Allow large outputs for file generation
14082
13451
  });
14083
- process.stderr.write(`[AGENT DEBUG] LLM returned: content length=${completion.content?.length ?? 0}, toolCalls=${completion.toolCalls?.length ?? 0}
13452
+ if (debugMode) process.stderr.write(`[AGENT DEBUG] LLM returned: content length=${completion.content?.length ?? 0}, toolCalls=${completion.toolCalls?.length ?? 0}
14084
13453
  `);
14085
13454
  } catch (llmError) {
14086
13455
  const errMsg = llmError instanceof Error ? llmError.message : String(llmError);
14087
13456
  const errStack = llmError instanceof Error ? llmError.stack : "";
14088
- process.stderr.write(`[AGENT DEBUG] LLM ERROR: ${errMsg}
13457
+ if (debugMode) process.stderr.write(`[AGENT DEBUG] LLM ERROR: ${errMsg}
14089
13458
  `);
14090
- process.stderr.write(`[AGENT DEBUG] LLM STACK: ${errStack}
13459
+ if (debugMode) process.stderr.write(`[AGENT DEBUG] LLM STACK: ${errStack}
14091
13460
  `);
14092
13461
  throw llmError;
14093
13462
  }
@@ -14096,7 +13465,7 @@ ${summary}`
14096
13465
  this.forceRenderSpinner();
14097
13466
  }
14098
13467
  const payload = this.parseAssistantResponse(completion);
14099
- process.stderr.write(`[AGENT DEBUG] Parsed payload: finalResponse=${!!payload.finalResponse}, thought=${!!payload.thought}, toolCalls=${payload.toolCalls?.length ?? 0}
13468
+ if (debugMode) process.stderr.write(`[AGENT DEBUG] Parsed payload: finalResponse=${!!payload.finalResponse}, thought=${!!payload.thought}, toolCalls=${payload.toolCalls?.length ?? 0}
14100
13469
  `);
14101
13470
  const assistantMessage = { role: "assistant", content: completion.content };
14102
13471
  if (completion.toolCalls?.length) {
@@ -14105,7 +13474,7 @@ ${summary}`
14105
13474
  this.conversation.addMessage(assistantMessage);
14106
13475
  await this.saveAssistantMessage(completion.content, payload.toolCalls);
14107
13476
  this.updateContextUsage(this.conversation.history(), tools);
14108
- if (process.env.AUTOHAND_DEBUG) {
13477
+ if (debugMode) {
14109
13478
  console.log(chalk13.yellow(`
14110
13479
  [DEBUG] Iteration ${iteration}:`));
14111
13480
  console.log(chalk13.yellow(` - toolCalls: ${payload.toolCalls?.length ?? 0}`));
@@ -14244,7 +13613,7 @@ ${summary}`
14244
13613
  );
14245
13614
  }
14246
13615
  if (abortController.signal.aborted) {
14247
- process.stderr.write("[AGENT DEBUG] Abort detected after tools, breaking\n");
13616
+ if (debugMode) process.stderr.write("[AGENT DEBUG] Abort detected after tools, breaking\n");
14248
13617
  break;
14249
13618
  }
14250
13619
  continue;
@@ -14282,7 +13651,7 @@ ${summary}`
14282
13651
  const consecutiveEmpty = (this[consecutiveEmptyKey] ?? 0) + 1;
14283
13652
  this[consecutiveEmptyKey] = consecutiveEmpty;
14284
13653
  if (consecutiveEmpty >= 3) {
14285
- process.stderr.write(`[AGENT DEBUG] Exiting after 3 consecutive empty responses
13654
+ if (debugMode) process.stderr.write(`[AGENT DEBUG] Exiting after 3 consecutive empty responses
14286
13655
  `);
14287
13656
  console.log(chalk13.yellow("\n\u26A0 Model not providing response after multiple attempts. Showing available context."));
14288
13657
  const fallback = payload.thought || "The model did not provide a clear response. Please try rephrasing your question.";
@@ -14885,16 +14254,16 @@ ${toolSignatures}
14885
14254
  return normalizedSeed || null;
14886
14255
  }
14887
14256
  async fileExists(relativePath) {
14888
- const fullPath = path19.resolve(this.runtime.workspaceRoot, relativePath);
14257
+ const fullPath = path18.resolve(this.runtime.workspaceRoot, relativePath);
14889
14258
  if (!fullPath.startsWith(this.runtime.workspaceRoot)) {
14890
14259
  return false;
14891
14260
  }
14892
- const exists = await fs20.pathExists(fullPath);
14261
+ const exists = await fs19.pathExists(fullPath);
14893
14262
  if (!exists) {
14894
14263
  return false;
14895
14264
  }
14896
14265
  try {
14897
- const stats = await fs20.stat(fullPath);
14266
+ const stats = await fs19.stat(fullPath);
14898
14267
  return stats.isFile();
14899
14268
  } catch {
14900
14269
  return false;
@@ -15202,7 +14571,7 @@ ${ctx.contents}`).join("\n\n");
15202
14571
  encoding: "utf8"
15203
14572
  });
15204
14573
  const gitStatus2 = git.status === 0 ? git.stdout.trim() : void 0;
15205
- const entries = await fs20.readdir(this.runtime.workspaceRoot);
14574
+ const entries = await fs19.readdir(this.runtime.workspaceRoot);
15206
14575
  const recentFiles = entries.filter((entry) => !this.ignoreFilter.isIgnored(entry)).slice(0, 20);
15207
14576
  return {
15208
14577
  workspaceRoot: this.runtime.workspaceRoot,
@@ -15213,17 +14582,17 @@ ${ctx.contents}`).join("\n\n");
15213
14582
  async loadInstructionFiles() {
15214
14583
  const instructions = [];
15215
14584
  const workspace = this.runtime.workspaceRoot;
15216
- const agentsPath = path19.join(workspace, "AGENTS.md");
15217
- if (await fs20.pathExists(agentsPath)) {
15218
- const content = await fs20.readFile(agentsPath, "utf-8");
14585
+ const agentsPath = path18.join(workspace, "AGENTS.md");
14586
+ if (await fs19.pathExists(agentsPath)) {
14587
+ const content = await fs19.readFile(agentsPath, "utf-8");
15219
14588
  instructions.push(`## Project Instructions (AGENTS.md)
15220
14589
  ${content}`);
15221
14590
  }
15222
14591
  const providerFile = this.activeProvider.includes("anthropic") || this.activeProvider === "openrouter" ? "CLAUDE.md" : this.activeProvider.includes("google") ? "GEMINI.md" : null;
15223
14592
  if (providerFile) {
15224
- const providerPath = path19.join(workspace, providerFile);
15225
- if (await fs20.pathExists(providerPath)) {
15226
- const content = await fs20.readFile(providerPath, "utf-8");
14593
+ const providerPath = path18.join(workspace, providerFile);
14594
+ if (await fs19.pathExists(providerPath)) {
14595
+ const content = await fs19.readFile(providerPath, "utf-8");
15227
14596
  instructions.push(`## Provider Instructions (${providerFile})
15228
14597
  ${content}`);
15229
14598
  }
@@ -15810,7 +15179,7 @@ ${chalk13.gray("\u203A")} ${inputPreview}${chalk13.gray("\u258B")}`;
15810
15179
  }
15811
15180
  }
15812
15181
  resolveWorkspacePath(relativePath) {
15813
- const resolved = path19.resolve(this.runtime.workspaceRoot, relativePath);
15182
+ const resolved = path18.resolve(this.runtime.workspaceRoot, relativePath);
15814
15183
  if (!resolved.startsWith(this.runtime.workspaceRoot)) {
15815
15184
  throw new Error(`Path ${relativePath} escapes workspace root.`);
15816
15185
  }
@@ -15856,9 +15225,9 @@ ${chalk13.gray("\u203A")} ${inputPreview}${chalk13.gray("\u258B")}`;
15856
15225
  };
15857
15226
 
15858
15227
  // src/skills/autoSkill.ts
15859
- import fs21 from "fs-extra";
15860
- import os7 from "os";
15861
- import path20 from "path";
15228
+ import fs20 from "fs-extra";
15229
+ import os8 from "os";
15230
+ import path19 from "path";
15862
15231
  import chalk14 from "chalk";
15863
15232
  var AVAILABLE_TOOLS = {
15864
15233
  file: [
@@ -15945,7 +15314,7 @@ var ProjectAnalyzer = class {
15945
15314
  */
15946
15315
  async analyze() {
15947
15316
  const analysis = {
15948
- projectName: path20.basename(this.projectRoot),
15317
+ projectName: path19.basename(this.projectRoot),
15949
15318
  languages: [],
15950
15319
  frameworks: [],
15951
15320
  patterns: [],
@@ -15957,8 +15326,8 @@ var ProjectAnalyzer = class {
15957
15326
  hasCI: false,
15958
15327
  packageManager: null
15959
15328
  };
15960
- analysis.hasGit = await fs21.pathExists(path20.join(this.projectRoot, ".git"));
15961
- analysis.hasCI = await fs21.pathExists(path20.join(this.projectRoot, ".github", "workflows")) || await fs21.pathExists(path20.join(this.projectRoot, ".gitlab-ci.yml")) || await fs21.pathExists(path20.join(this.projectRoot, ".circleci"));
15329
+ analysis.hasGit = await fs20.pathExists(path19.join(this.projectRoot, ".git"));
15330
+ analysis.hasCI = await fs20.pathExists(path19.join(this.projectRoot, ".github", "workflows")) || await fs20.pathExists(path19.join(this.projectRoot, ".gitlab-ci.yml")) || await fs20.pathExists(path19.join(this.projectRoot, ".circleci"));
15962
15331
  await this.analyzePackageJson(analysis);
15963
15332
  await this.analyzePython(analysis);
15964
15333
  await this.analyzeRust(analysis);
@@ -15970,7 +15339,7 @@ var ProjectAnalyzer = class {
15970
15339
  * Detect the current platform
15971
15340
  */
15972
15341
  detectPlatform() {
15973
- const platform = os7.platform();
15342
+ const platform = os8.platform();
15974
15343
  if (platform === "darwin" || platform === "linux" || platform === "win32") {
15975
15344
  return platform;
15976
15345
  }
@@ -15980,12 +15349,12 @@ var ProjectAnalyzer = class {
15980
15349
  * Analyze package.json for Node.js projects
15981
15350
  */
15982
15351
  async analyzePackageJson(analysis) {
15983
- const packageJsonPath = path20.join(this.projectRoot, "package.json");
15984
- if (!await fs21.pathExists(packageJsonPath)) {
15352
+ const packageJsonPath = path19.join(this.projectRoot, "package.json");
15353
+ if (!await fs20.pathExists(packageJsonPath)) {
15985
15354
  return;
15986
15355
  }
15987
15356
  try {
15988
- const pkg = await fs21.readJson(packageJsonPath);
15357
+ const pkg = await fs20.readJson(packageJsonPath);
15989
15358
  analysis.projectName = pkg.name || analysis.projectName;
15990
15359
  const allDeps = {
15991
15360
  ...pkg.dependencies,
@@ -15993,13 +15362,13 @@ var ProjectAnalyzer = class {
15993
15362
  };
15994
15363
  const depNames = Object.keys(allDeps);
15995
15364
  analysis.dependencies.push(...depNames);
15996
- if (await fs21.pathExists(path20.join(this.projectRoot, "bun.lockb"))) {
15365
+ if (await fs20.pathExists(path19.join(this.projectRoot, "bun.lockb"))) {
15997
15366
  analysis.packageManager = "bun";
15998
- } else if (await fs21.pathExists(path20.join(this.projectRoot, "pnpm-lock.yaml"))) {
15367
+ } else if (await fs20.pathExists(path19.join(this.projectRoot, "pnpm-lock.yaml"))) {
15999
15368
  analysis.packageManager = "pnpm";
16000
- } else if (await fs21.pathExists(path20.join(this.projectRoot, "yarn.lock"))) {
15369
+ } else if (await fs20.pathExists(path19.join(this.projectRoot, "yarn.lock"))) {
16001
15370
  analysis.packageManager = "yarn";
16002
- } else if (await fs21.pathExists(path20.join(this.projectRoot, "package-lock.json"))) {
15371
+ } else if (await fs20.pathExists(path19.join(this.projectRoot, "package-lock.json"))) {
16003
15372
  analysis.packageManager = "npm";
16004
15373
  }
16005
15374
  if (allDeps.typescript) {
@@ -16040,14 +15409,14 @@ var ProjectAnalyzer = class {
16040
15409
  * Analyze Python project files
16041
15410
  */
16042
15411
  async analyzePython(analysis) {
16043
- const requirementsPath = path20.join(this.projectRoot, "requirements.txt");
16044
- const pyprojectPath = path20.join(this.projectRoot, "pyproject.toml");
15412
+ const requirementsPath = path19.join(this.projectRoot, "requirements.txt");
15413
+ const pyprojectPath = path19.join(this.projectRoot, "pyproject.toml");
16045
15414
  let hasPython = false;
16046
- if (await fs21.pathExists(requirementsPath)) {
15415
+ if (await fs20.pathExists(requirementsPath)) {
16047
15416
  hasPython = true;
16048
15417
  analysis.packageManager = "pip";
16049
15418
  try {
16050
- const content = await fs21.readFile(requirementsPath, "utf-8");
15419
+ const content = await fs20.readFile(requirementsPath, "utf-8");
16051
15420
  const lines = content.toLowerCase();
16052
15421
  for (const [framework, deps] of Object.entries(PYTHON_FRAMEWORKS)) {
16053
15422
  if (deps.some((d) => lines.includes(d))) {
@@ -16061,7 +15430,7 @@ var ProjectAnalyzer = class {
16061
15430
  } catch {
16062
15431
  }
16063
15432
  }
16064
- if (await fs21.pathExists(pyprojectPath)) {
15433
+ if (await fs20.pathExists(pyprojectPath)) {
16065
15434
  hasPython = true;
16066
15435
  }
16067
15436
  if (hasPython) {
@@ -16072,8 +15441,8 @@ var ProjectAnalyzer = class {
16072
15441
  * Analyze Rust project
16073
15442
  */
16074
15443
  async analyzeRust(analysis) {
16075
- const cargoPath = path20.join(this.projectRoot, "Cargo.toml");
16076
- if (await fs21.pathExists(cargoPath)) {
15444
+ const cargoPath = path19.join(this.projectRoot, "Cargo.toml");
15445
+ if (await fs20.pathExists(cargoPath)) {
16077
15446
  analysis.languages.push("rust");
16078
15447
  analysis.packageManager = "cargo";
16079
15448
  }
@@ -16082,8 +15451,8 @@ var ProjectAnalyzer = class {
16082
15451
  * Analyze Go project
16083
15452
  */
16084
15453
  async analyzeGo(analysis) {
16085
- const goModPath = path20.join(this.projectRoot, "go.mod");
16086
- if (await fs21.pathExists(goModPath)) {
15454
+ const goModPath = path19.join(this.projectRoot, "go.mod");
15455
+ if (await fs20.pathExists(goModPath)) {
16087
15456
  analysis.languages.push("go");
16088
15457
  analysis.packageManager = "go";
16089
15458
  }
@@ -16092,7 +15461,7 @@ var ProjectAnalyzer = class {
16092
15461
  * Detect additional project patterns
16093
15462
  */
16094
15463
  async detectPatterns(analysis) {
16095
- if (await fs21.pathExists(path20.join(this.projectRoot, "Dockerfile"))) {
15464
+ if (await fs20.pathExists(path19.join(this.projectRoot, "Dockerfile"))) {
16096
15465
  analysis.patterns.push("docker");
16097
15466
  }
16098
15467
  const dbFiles = await this.findFiles("**/migrations/**", 2);
@@ -16111,13 +15480,13 @@ var ProjectAnalyzer = class {
16111
15480
  async function walk(dir, depth) {
16112
15481
  if (depth > maxDepth) return;
16113
15482
  try {
16114
- const entries = await fs21.readdir(dir, { withFileTypes: true });
15483
+ const entries = await fs20.readdir(dir, { withFileTypes: true });
16115
15484
  for (const entry of entries) {
16116
- const fullPath = path20.join(dir, entry.name);
15485
+ const fullPath = path19.join(dir, entry.name);
16117
15486
  if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
16118
15487
  await walk(fullPath, depth + 1);
16119
15488
  } else if (entry.isFile()) {
16120
- const ext = path20.extname(entry.name);
15489
+ const ext = path19.extname(entry.name);
16121
15490
  if (pattern === "*.js" && ext === ".js") {
16122
15491
  results.push(fullPath);
16123
15492
  } else if (pattern === "*.ts" && ext === ".ts") {
@@ -16262,10 +15631,10 @@ async function generateAutoSkills(analysis, llm) {
16262
15631
  }
16263
15632
  }
16264
15633
  async function saveSkill(skill, skillsDir) {
16265
- const skillDir = path20.join(skillsDir, skill.name);
16266
- const skillPath = path20.join(skillDir, "SKILL.md");
15634
+ const skillDir = path19.join(skillsDir, skill.name);
15635
+ const skillPath = path19.join(skillDir, "SKILL.md");
16267
15636
  try {
16268
- await fs21.ensureDir(skillDir);
15637
+ await fs20.ensureDir(skillDir);
16269
15638
  let frontmatter = `---
16270
15639
  name: ${skill.name}
16271
15640
  description: ${skill.description}`;
@@ -16278,7 +15647,7 @@ allowed-tools: ${skill.allowedTools.join(" ")}`;
16278
15647
 
16279
15648
  `;
16280
15649
  const content = frontmatter + skill.body + "\n";
16281
- await fs21.writeFile(skillPath, content, "utf-8");
15650
+ await fs20.writeFile(skillPath, content, "utf-8");
16282
15651
  return true;
16283
15652
  } catch {
16284
15653
  return false;
@@ -16314,7 +15683,7 @@ async function runAutoSkillGeneration(workspaceRoot, llm) {
16314
15683
  result.error = "No skills generated";
16315
15684
  return result;
16316
15685
  }
16317
- const skillsDir = path20.join(workspaceRoot, PROJECT_DIR_NAME, "skills");
15686
+ const skillsDir = path19.join(workspaceRoot, PROJECT_DIR_NAME, "skills");
16318
15687
  for (const skill of skills) {
16319
15688
  const saved = await saveSkill(skill, skillsDir);
16320
15689
  if (saved) {
@@ -17336,10 +16705,10 @@ ${sel.text}
17336
16705
  * Emit hook permission-request notification
17337
16706
  * Called when a permission dialog is about to be shown
17338
16707
  */
17339
- emitHookPermissionRequest(tool, path21, command, args) {
16708
+ emitHookPermissionRequest(tool, path20, command, args) {
17340
16709
  writeNotification(RPC_NOTIFICATIONS.HOOK_PERMISSION_REQUEST, {
17341
16710
  tool,
17342
- path: path21,
16711
+ path: path20,
17343
16712
  command,
17344
16713
  args,
17345
16714
  timestamp: createTimestamp()
@@ -17407,8 +16776,8 @@ ${sel.text}
17407
16776
  */
17408
16777
  async handleGetSkillsRegistry(requestId, params) {
17409
16778
  try {
17410
- const { CommunitySkillsCache } = await import("./CommunitySkillsCache-XPDVYU3K.js");
17411
- const { GitHubRegistryFetcher } = await import("./GitHubRegistryFetcher-US2JJID4.js");
16779
+ const { CommunitySkillsCache } = await import("./CommunitySkillsCache-AY3ZYDTN.js");
16780
+ const { GitHubRegistryFetcher } = await import("./GitHubRegistryFetcher-V23KTTLM.js");
17412
16781
  const cache = new CommunitySkillsCache();
17413
16782
  const fetcher = new GitHubRegistryFetcher();
17414
16783
  let registry;
@@ -17467,10 +16836,10 @@ ${sel.text}
17467
16836
  };
17468
16837
  }
17469
16838
  const workspaceRoot = this.workspace;
17470
- const { CommunitySkillsCache } = await import("./CommunitySkillsCache-XPDVYU3K.js");
17471
- const { GitHubRegistryFetcher } = await import("./GitHubRegistryFetcher-US2JJID4.js");
17472
- const { AUTOHAND_PATHS: AUTOHAND_PATHS2, PROJECT_DIR_NAME: PROJECT_DIR_NAME2 } = await import("./constants-QYBEF3DB.js");
17473
- const path21 = await import("path");
16839
+ const { CommunitySkillsCache } = await import("./CommunitySkillsCache-AY3ZYDTN.js");
16840
+ const { GitHubRegistryFetcher } = await import("./GitHubRegistryFetcher-V23KTTLM.js");
16841
+ const { AUTOHAND_PATHS: AUTOHAND_PATHS2, PROJECT_DIR_NAME: PROJECT_DIR_NAME2 } = await import("./constants-RVCOJL3L.js");
16842
+ const path20 = await import("path");
17474
16843
  const cache = new CommunitySkillsCache();
17475
16844
  const fetcher = new GitHubRegistryFetcher();
17476
16845
  let registry = await cache.getRegistry();
@@ -17488,7 +16857,7 @@ ${sel.text}
17488
16857
  error: `Skill not found: ${params.skillName}${suggestions ? `. Did you mean: ${suggestions}?` : ""}`
17489
16858
  };
17490
16859
  }
17491
- const targetDir = params.scope === "project" ? path21.join(workspaceRoot, PROJECT_DIR_NAME2, "skills") : AUTOHAND_PATHS2.skills;
16860
+ const targetDir = params.scope === "project" ? path20.join(workspaceRoot, PROJECT_DIR_NAME2, "skills") : AUTOHAND_PATHS2.skills;
17492
16861
  const isInstalled = await skillsRegistry.isSkillInstalled(skill.name, targetDir);
17493
16862
  if (isInstalled && !params.force) {
17494
16863
  return {
@@ -17942,7 +17311,7 @@ async function handleSingleRequest(request, adapter) {
17942
17311
  process.title = "autohand";
17943
17312
  function getGitCommit() {
17944
17313
  if (true) {
17945
- return "f429388";
17314
+ return "1243faa";
17946
17315
  }
17947
17316
  try {
17948
17317
  return execSync3("git rev-parse --short HEAD", { encoding: "utf8", stdio: ["pipe", "pipe", "ignore"] }).trim();
@@ -18011,7 +17380,7 @@ var ASCII_FRIEND = [
18011
17380
  "\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"
18012
17381
  ].join("\n");
18013
17382
  var program = new Command();
18014
- 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("--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").action(async (opts) => {
17383
+ 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) => {
18015
17384
  if (opts.skillInstall !== void 0) {
18016
17385
  await runSkillInstall(opts);
18017
17386
  return;
@@ -18024,6 +17393,11 @@ program.name("autohand").description("Autonomous LLM-powered coding agent CLI").
18024
17393
  await runPatchMode(opts);
18025
17394
  return;
18026
17395
  }
17396
+ if (opts.autoMode) {
17397
+ opts.noWorktree = opts.worktree === false;
17398
+ await runAutoMode(opts);
17399
+ return;
17400
+ }
18027
17401
  if (opts.mode === "rpc") {
18028
17402
  await runRpcMode(opts);
18029
17403
  } else {
@@ -18086,6 +17460,11 @@ async function runCLI(options) {
18086
17460
  console.log(chalk15.green("\u2713 API key saved to config\n"));
18087
17461
  }
18088
17462
  const workspaceRoot = resolveWorkspaceRoot(config, options.path);
17463
+ const safetyCheck = checkWorkspaceSafety(workspaceRoot);
17464
+ if (!safetyCheck.safe) {
17465
+ printDangerousWorkspaceWarning(workspaceRoot, safetyCheck);
17466
+ process.exit(1);
17467
+ }
18089
17468
  const runtime = {
18090
17469
  config,
18091
17470
  workspaceRoot,
@@ -18108,6 +17487,10 @@ async function runCLI(options) {
18108
17487
  config[providerName2].model = options.model;
18109
17488
  }
18110
17489
  }
17490
+ if (options.debug) {
17491
+ config.agent = config.agent ?? {};
17492
+ config.agent.debug = true;
17493
+ }
18111
17494
  const llmProvider = ProviderFactory.create(config);
18112
17495
  const files = new FileActionManager(workspaceRoot);
18113
17496
  if (options.autoSkill) {
@@ -18186,9 +17569,14 @@ function printWelcome(runtime, authUser, versionCheck) {
18186
17569
  async function runSkillInstall(opts) {
18187
17570
  const config = await loadConfig(opts.config);
18188
17571
  const workspaceRoot = resolveWorkspaceRoot(config, opts.path);
18189
- const { SkillsRegistry: SkillsRegistry2 } = await import("./SkillsRegistry-7NICF6FY.js");
18190
- const { AUTOHAND_PATHS: AUTOHAND_PATHS2 } = await import("./constants-QYBEF3DB.js");
18191
- const { skillsInstall } = await import("./skills-install-RMPXN6RK.js");
17572
+ const safetyCheck = checkWorkspaceSafety(workspaceRoot);
17573
+ if (!safetyCheck.safe) {
17574
+ printDangerousWorkspaceWarning(workspaceRoot, safetyCheck);
17575
+ process.exit(1);
17576
+ }
17577
+ const { SkillsRegistry: SkillsRegistry2 } = await import("./SkillsRegistry-FTLVJZMA.js");
17578
+ const { AUTOHAND_PATHS: AUTOHAND_PATHS2 } = await import("./constants-RVCOJL3L.js");
17579
+ const { skillsInstall } = await import("./skills-install-GNTBBL46.js");
18192
17580
  const skillsRegistry = new SkillsRegistry2(AUTOHAND_PATHS2.skills);
18193
17581
  await skillsRegistry.initialize();
18194
17582
  await skillsRegistry.setWorkspace(workspaceRoot);
@@ -18201,8 +17589,13 @@ async function runSkillInstall(opts) {
18201
17589
  async function displayPermissions(opts) {
18202
17590
  const config = await loadConfig(opts.config);
18203
17591
  const workspaceRoot = resolveWorkspaceRoot(config, opts.path);
18204
- const { PermissionManager: PermissionManager2 } = await import("./PermissionManager-YFZI4ZZ6.js");
18205
- const { loadLocalProjectSettings } = await import("./localProjectPermissions-DURCNDZG.js");
17592
+ const safetyCheck = checkWorkspaceSafety(workspaceRoot);
17593
+ if (!safetyCheck.safe) {
17594
+ printDangerousWorkspaceWarning(workspaceRoot, safetyCheck);
17595
+ process.exit(1);
17596
+ }
17597
+ const { PermissionManager: PermissionManager2 } = await import("./PermissionManager-V2Q2OAS2.js");
17598
+ const { loadLocalProjectSettings } = await import("./localProjectPermissions-5CXHD4DO.js");
18206
17599
  const localSettings = await loadLocalProjectSettings(workspaceRoot);
18207
17600
  const mergedSettings = {
18208
17601
  mode: localSettings?.permissions?.mode ?? config.permissions?.mode ?? "interactive",
@@ -18264,10 +17657,15 @@ async function runPatchMode(opts) {
18264
17657
  console.error(chalk15.gray('Usage: autohand --prompt "your instruction" --patch'));
18265
17658
  process.exit(1);
18266
17659
  }
18267
- const fs22 = await import("fs-extra");
18268
- const { generateUnifiedPatch, formatChangeSummary } = await import("./patch-BAAQIYSW.js");
17660
+ const fs21 = await import("fs-extra");
17661
+ const { generateUnifiedPatch, formatChangeSummary } = await import("./patch-RPK3BZQR.js");
18269
17662
  const config = await loadConfig(opts.config);
18270
17663
  const workspaceRoot = resolveWorkspaceRoot(config, opts.path);
17664
+ const safetyCheck = checkWorkspaceSafety(workspaceRoot);
17665
+ if (!safetyCheck.safe) {
17666
+ printDangerousWorkspaceWarning(workspaceRoot, safetyCheck);
17667
+ process.exit(1);
17668
+ }
18271
17669
  if (opts.model) {
18272
17670
  const providerName = config.provider ?? "openrouter";
18273
17671
  if (config[providerName]) {
@@ -18303,8 +17701,8 @@ async function runPatchMode(opts) {
18303
17701
  console.error(chalk15.green(`
18304
17702
  \u2713 ${formatChangeSummary(changes)}`));
18305
17703
  if (opts.output) {
18306
- await fs22.default.ensureDir((await import("path")).dirname(opts.output));
18307
- await fs22.default.writeFile(opts.output, patch);
17704
+ await fs21.default.ensureDir((await import("path")).dirname(opts.output));
17705
+ await fs21.default.writeFile(opts.output, patch);
18308
17706
  console.error(chalk15.green(`\u2713 Patch written to ${opts.output}`));
18309
17707
  console.error(chalk15.gray("\nTo apply: git apply " + opts.output));
18310
17708
  } else {
@@ -18319,6 +17717,150 @@ Error: ${error.message}`));
18319
17717
  process.exit(1);
18320
17718
  }
18321
17719
  }
17720
+ async function runAutoMode(opts) {
17721
+ if (!opts.autoMode) {
17722
+ console.error(chalk15.red("Error: --auto-mode requires a task prompt"));
17723
+ process.exit(1);
17724
+ }
17725
+ const config = await loadConfig(opts.config);
17726
+ const workspaceRoot = resolveWorkspaceRoot(config, opts.path);
17727
+ const safetyCheck = checkWorkspaceSafety(workspaceRoot);
17728
+ if (!safetyCheck.safe) {
17729
+ printDangerousWorkspaceWarning(workspaceRoot, safetyCheck);
17730
+ process.exit(1);
17731
+ }
17732
+ if (opts.model) {
17733
+ const providerName2 = config.provider ?? "openrouter";
17734
+ if (config[providerName2]) {
17735
+ config[providerName2].model = opts.model;
17736
+ }
17737
+ }
17738
+ if (opts.debug) {
17739
+ config.agent = config.agent ?? {};
17740
+ config.agent.debug = true;
17741
+ }
17742
+ const { AutomodeManager, getAutomodeOptions } = await import("./AutomodeManager-FQQPBHAD.js");
17743
+ const { HookManager: HookManager2 } = await import("./HookManager-VG46FXSZ.js");
17744
+ const { SessionManager: SessionManager2 } = await import("./SessionManager-V25OJDDY.js");
17745
+ const { MemoryManager: MemoryManager2 } = await import("./MemoryManager-AFS5EZJ2.js");
17746
+ const readline5 = await import("readline");
17747
+ const llmProvider = ProviderFactory.create(config);
17748
+ const files = new FileActionManager(workspaceRoot);
17749
+ const providerName = config.provider ?? "openrouter";
17750
+ const modelName = opts.model ?? config[providerName]?.model ?? "unknown";
17751
+ const sessionManager = new SessionManager2();
17752
+ await sessionManager.initialize();
17753
+ const session = await sessionManager.createSession(workspaceRoot, modelName);
17754
+ session.metadata.type = "automode";
17755
+ session.metadata.automodePrompt = opts.autoMode;
17756
+ const memoryManager = new MemoryManager2(workspaceRoot);
17757
+ const hookManager = new HookManager2({
17758
+ settings: config.hooks ?? { enabled: true, hooks: [] },
17759
+ workspaceRoot
17760
+ });
17761
+ const automodeManager = new AutomodeManager(config, workspaceRoot, hookManager, session, memoryManager);
17762
+ const automodeOptions = getAutomodeOptions(opts, config);
17763
+ if (!automodeOptions) {
17764
+ console.error(chalk15.red("Error: Failed to parse auto-mode options"));
17765
+ process.exit(1);
17766
+ }
17767
+ printBanner();
17768
+ console.log(chalk15.bold.cyan("\n\u{1F504} Auto-Mode: Autonomous Development Loop\n"));
17769
+ console.log(chalk15.gray("Task:"), chalk15.white(opts.autoMode));
17770
+ console.log(chalk15.gray("Max Iterations:"), chalk15.cyan(automodeOptions.maxIterations ?? 50));
17771
+ console.log(chalk15.gray("Completion Marker:"), chalk15.cyan(automodeOptions.completionPromise ?? "DONE"));
17772
+ console.log(chalk15.gray("Worktree Isolation:"), chalk15.cyan(automodeOptions.useWorktree !== false ? "enabled" : "disabled"));
17773
+ console.log();
17774
+ if (process.stdin.isTTY) {
17775
+ readline5.emitKeypressEvents(process.stdin);
17776
+ process.stdin.setRawMode(true);
17777
+ process.stdin.on("keypress", (_str, key) => {
17778
+ if (key && key.name === "escape") {
17779
+ console.log(chalk15.yellow("\n\u26A0\uFE0F Cancelling auto-mode..."));
17780
+ automodeManager.cancel("user_escape");
17781
+ }
17782
+ if (key && key.ctrl && key.name === "c") {
17783
+ console.log(chalk15.yellow("\n\u26A0\uFE0F Cancelling auto-mode..."));
17784
+ automodeManager.cancel("user_escape");
17785
+ if (process.stdin.isTTY) {
17786
+ process.stdin.setRawMode(false);
17787
+ }
17788
+ process.exit(0);
17789
+ }
17790
+ });
17791
+ }
17792
+ try {
17793
+ const runtime = {
17794
+ config,
17795
+ workspaceRoot,
17796
+ options: {
17797
+ ...opts,
17798
+ yes: true
17799
+ // Auto-confirm in auto-mode
17800
+ }
17801
+ };
17802
+ const agent = new AutohandAgent(llmProvider, files, runtime);
17803
+ const runIteration = async (iteration, prompt, abortSignal) => {
17804
+ const iterationPrompt = buildIterationPrompt(prompt, iteration);
17805
+ let output = "";
17806
+ let actions = [];
17807
+ let success = true;
17808
+ let error;
17809
+ try {
17810
+ await agent.runCommandMode(iterationPrompt);
17811
+ actions = ["Executed agent iteration"];
17812
+ } catch (err) {
17813
+ success = false;
17814
+ error = err.message;
17815
+ console.error(chalk15.red(`Iteration error: ${error}`));
17816
+ }
17817
+ return {
17818
+ success,
17819
+ actions,
17820
+ output,
17821
+ error
17822
+ };
17823
+ };
17824
+ await automodeManager.start(automodeOptions, runIteration);
17825
+ if (process.stdin.isTTY) {
17826
+ process.stdin.setRawMode(false);
17827
+ }
17828
+ const finalState = automodeManager.getState();
17829
+ if (finalState) {
17830
+ session.metadata.automodeIterations = finalState.currentIteration;
17831
+ const statusText = finalState.status === "completed" ? "completed" : `ended (${finalState.status})`;
17832
+ await sessionManager.closeSession(`Auto-mode ${statusText} after ${finalState.currentIteration} iterations: ${opts.autoMode?.slice(0, 50)}...`);
17833
+ console.log(chalk15.gray(`
17834
+ \u{1F4C1} Session saved: ${session.metadata.sessionId}`));
17835
+ }
17836
+ process.exit(finalState?.status === "completed" ? 0 : 1);
17837
+ } catch (error) {
17838
+ if (process.stdin.isTTY) {
17839
+ process.stdin.setRawMode(false);
17840
+ }
17841
+ await sessionManager.closeSession(`Auto-mode failed: ${error.message}`);
17842
+ console.error(chalk15.red(`
17843
+ Auto-mode error: ${error.message}`));
17844
+ process.exit(1);
17845
+ }
17846
+ }
17847
+ function buildIterationPrompt(taskPrompt, iteration) {
17848
+ return `# Auto-Mode Task (Iteration ${iteration})
17849
+
17850
+ ## Original Task
17851
+ ${taskPrompt}
17852
+
17853
+ ## Instructions
17854
+ You are running in auto-mode, an autonomous development loop. Continue working on the task above.
17855
+
17856
+ 1. Review your previous work (check git log, file changes, test results)
17857
+ 2. Identify what remains to be done
17858
+ 3. Make progress on the task
17859
+ 4. If the task is complete, output: <promise>DONE</promise>
17860
+
17861
+ IMPORTANT: Only output <promise>DONE</promise> when ALL requirements are fully met.
17862
+ Do not stop early - keep improving until the task is truly complete.`;
17863
+ }
18322
17864
  program.parseAsync();
18323
17865
  /**
18324
17866
  * @license
@@ -18412,10 +17954,6 @@ program.parseAsync();
18412
17954
  * Persistent Input - Always-visible input field at the bottom of the terminal
18413
17955
  * Uses terminal scroll regions to separate spinner output from input area
18414
17956
  */
18415
- /**
18416
- * Hook Manager - Executes lifecycle hooks based on config
18417
- * @license Apache-2.0
18418
- */
18419
17957
  /**
18420
17958
  * Unified Prompt Utility
18421
17959
  * Supports both interactive (enquirer) and external (HTTP callback) modes