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.
- package/README.md +60 -48
- package/dist/AutomodeManager-FQQPBHAD.js +1422 -0
- package/dist/AutomodeManager-PUNJS7IZ.cjs +1422 -0
- package/dist/{CommunitySkillsCache-XPDVYU3K.js → CommunitySkillsCache-AY3ZYDTN.js} +1 -0
- package/dist/{CommunitySkillsCache-X3X237QQ.cjs → CommunitySkillsCache-KO4DSKMA.cjs} +1 -0
- package/dist/{GitHubRegistryFetcher-K744NNAJ.cjs → GitHubRegistryFetcher-S4JREWUQ.cjs} +1 -0
- package/dist/{GitHubRegistryFetcher-US2JJID4.js → GitHubRegistryFetcher-V23KTTLM.js} +1 -0
- package/dist/HookManager-OGINWAJN.cjs +7 -0
- package/dist/HookManager-VG46FXSZ.js +7 -0
- package/dist/MemoryManager-AFS5EZJ2.js +8 -0
- package/dist/MemoryManager-KE6EKW7Y.cjs +8 -0
- package/dist/{PermissionManager-PMTQN263.cjs → PermissionManager-MAPKIJMD.cjs} +1 -0
- package/dist/{PermissionManager-YFZI4ZZ6.js → PermissionManager-V2Q2OAS2.js} +1 -0
- package/dist/SessionManager-MKLGLZWC.cjs +10 -0
- package/dist/SessionManager-V25OJDDY.js +10 -0
- package/dist/{SkillsRegistry-OINIPILA.cjs → SkillsRegistry-4RU2OAEQ.cjs} +1 -0
- package/dist/{SkillsRegistry-7NICF6FY.js → SkillsRegistry-FTLVJZMA.js} +1 -0
- package/dist/{agents-GRAFXZY3.cjs → agents-CHNQ7LQ4.cjs} +1 -0
- package/dist/{agents-B33IAATH.js → agents-E4NEH2PN.js} +1 -0
- package/dist/{agents-new-67NJJSDA.cjs → agents-new-GHVWXW7T.cjs} +1 -0
- package/dist/{agents-new-KTXJFC5E.js → agents-new-W6HMQ7V5.js} +1 -0
- package/dist/automode-BJYGRMEQ.cjs +9 -0
- package/dist/automode-XCNP6HP4.js +9 -0
- package/dist/chunk-2FLBGPE3.js +199 -0
- package/dist/{chunk-5RX6NVQO.cjs → chunk-3L76MLO5.cjs} +20 -2
- package/dist/chunk-3RG5ZIWI.js +10 -0
- package/dist/chunk-4L5WYXHN.js +246 -0
- package/dist/{chunk-SFT6BEYU.js → chunk-BAFJQUWR.js} +20 -2
- package/dist/chunk-CXZEPTRI.js +172 -0
- package/dist/{chunk-CT2VTDPQ.cjs → chunk-E6U4UE3B.cjs} +1 -1
- package/dist/{chunk-TG34DNLU.cjs → chunk-EBM5QDJ4.cjs} +2 -2
- package/dist/{chunk-MFLRXVKU.js → chunk-HUE3GT5M.js} +1 -1
- package/dist/{chunk-A2TFZJD4.js → chunk-LCCJ26QR.js} +2 -2
- package/dist/chunk-OBGZSXTJ.cjs +10 -0
- package/dist/chunk-OTS4YFSZ.cjs +172 -0
- package/dist/chunk-SXYYH3VD.cjs +497 -0
- package/dist/chunk-UOLJZFPJ.js +497 -0
- package/dist/chunk-UPR5PKX4.cjs +199 -0
- package/dist/chunk-URY4AS4L.cjs +246 -0
- package/dist/{completion-Y42FKDT3.js → completion-2XTEWNMC.js} +1 -0
- package/dist/{completion-WVFWX7XQ.cjs → completion-BTDPVWVB.cjs} +1 -0
- package/dist/{constants-QYBEF3DB.js → constants-RVCOJL3L.js} +1 -0
- package/dist/{constants-PE5DLI7Q.cjs → constants-V4V43DZQ.cjs} +1 -0
- package/dist/{defaultHooks-3G3DVF6I.js → defaultHooks-3GRSZNKA.js} +2 -0
- package/dist/{defaultHooks-Z4KA6U5C.cjs → defaultHooks-7EAUU6MN.cjs} +3 -1
- package/dist/{export-BKBJ7PB2.cjs → export-CB34FSEH.cjs} +1 -0
- package/dist/{export-WJ5P6E5Z.js → export-LNPP3XXH.js} +1 -0
- package/dist/{feedback-PZ2PINDU.js → feedback-IZKDNGHP.js} +1 -0
- package/dist/{feedback-R66B3B3C.cjs → feedback-OKGBXZZ4.cjs} +1 -0
- package/dist/{formatters-UG6VZJJ5.js → formatters-JJK6RS55.js} +1 -0
- package/dist/{formatters-N5IJKYZY.cjs → formatters-OIGPANHJ.cjs} +1 -0
- package/dist/{help-PKC6QCNG.js → help-MU553D6W.js} +1 -0
- package/dist/{help-UEELQRHP.cjs → help-QAAUDLFS.cjs} +1 -0
- package/dist/{hooks-5NY5K4L3.js → hooks-4N5VRVOF.js} +2 -1
- package/dist/hooks-7DZGBMXP.cjs +10 -0
- package/dist/index.cjs +933 -1395
- package/dist/index.js +863 -1325
- package/dist/{init-SLLSDDJN.cjs → init-2QEB7GL5.cjs} +1 -0
- package/dist/{init-DML7AOII.js → init-WW4RITLV.js} +1 -0
- package/dist/{lint-TA2ZHVLM.js → lint-L2TD6NY6.js} +1 -0
- package/dist/{lint-44UQJ673.cjs → lint-ZLRBEAFF.cjs} +1 -0
- package/dist/{localProjectPermissions-75X3ZGKH.cjs → localProjectPermissions-2DP6X55S.cjs} +1 -0
- package/dist/{localProjectPermissions-DURCNDZG.js → localProjectPermissions-5CXHD4DO.js} +1 -0
- package/dist/{login-BZ7J4Z4M.js → login-LH62FYMH.js} +1 -0
- package/dist/{login-HUH3CEWL.cjs → login-TWWYJKX6.cjs} +1 -0
- package/dist/{logout-3V3SH7OL.cjs → logout-35XNU6Q2.cjs} +1 -0
- package/dist/{logout-BZKEMNHB.js → logout-KPHUXO23.js} +1 -0
- package/dist/{memory-4GSP7NKV.js → memory-2SGSO4MW.js} +1 -0
- package/dist/{memory-CFNC7RJH.cjs → memory-ALXCFFQY.cjs} +1 -0
- package/dist/{model-TKVEJ5BC.cjs → model-JC43B3KM.cjs} +1 -0
- package/dist/{model-HKEFSH5E.js → model-U3BWIWVH.js} +1 -0
- package/dist/{new-EB2MBQXA.cjs → new-24YT5KVI.cjs} +1 -0
- package/dist/{new-EEZC4XXV.js → new-PCOF6OLV.js} +1 -0
- package/dist/{patch-J32X2QQP.cjs → patch-ETANEGRW.cjs} +3 -1
- package/dist/{patch-BAAQIYSW.js → patch-RPK3BZQR.js} +2 -0
- package/dist/{permissions-5MTH22EF.js → permissions-5W5JOVM7.js} +1 -0
- package/dist/{permissions-IP5SITPI.cjs → permissions-NHPJPHDV.cjs} +1 -0
- package/dist/{quit-RSYIERO5.js → quit-NIDVPHNL.js} +1 -0
- package/dist/{quit-2RYFGIJP.cjs → quit-TMKMX2YF.cjs} +1 -0
- package/dist/{resume-OYZMJRNO.cjs → resume-5C44HAAH.cjs} +1 -0
- package/dist/{resume-2NERFSTD.js → resume-ZZ2D2NMB.js} +1 -0
- package/dist/{session-T3TAZ5ZU.cjs → session-6TMBGN7N.cjs} +1 -0
- package/dist/{session-H5QWKE5E.js → session-76F55XKA.js} +1 -0
- package/dist/{sessions-4KXIT76T.js → sessions-ERKBJ7MC.js} +1 -0
- package/dist/{sessions-7RTCPVNE.cjs → sessions-NUPCJVCF.cjs} +1 -0
- package/dist/{skills-CRFOVWEQ.js → skills-R25OBHE5.js} +2 -1
- package/dist/skills-UCWKIHHG.cjs +13 -0
- package/dist/{skills-install-RMPXN6RK.js → skills-install-GNTBBL46.js} +1 -0
- package/dist/{skills-install-Z27KPEGF.cjs → skills-install-VUSVGSON.cjs} +1 -0
- package/dist/{skills-new-3QJUST7P.cjs → skills-new-AGXQNBRA.cjs} +1 -0
- package/dist/{skills-new-S2YPO635.js → skills-new-L36LQXIE.js} +1 -0
- package/dist/status-2QV7C3JJ.cjs +9 -0
- package/dist/{status-KC56ITDB.js → status-VJ6FOSRI.js} +2 -1
- package/dist/{theme-LIF3RD3A.cjs → theme-MI3BM56M.cjs} +1 -0
- package/dist/{theme-JAMJSCKR.js → theme-ZXGSJBZI.js} +1 -0
- package/dist/{undo-7QJBXARS.js → undo-3UU5LWQS.js} +1 -0
- package/dist/{undo-2WR2ZIEC.cjs → undo-W4VN2Y37.cjs} +1 -0
- package/package.json +2 -2
- package/dist/hooks-PIYYJZMI.cjs +0 -9
- package/dist/skills-6PIGHOWS.cjs +0 -12
- 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-
|
|
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
|
-
|
|
18
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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/
|
|
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 =
|
|
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
|
|
360
|
-
const data = await
|
|
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
|
|
370
|
-
await
|
|
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
|
|
457
|
-
import
|
|
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
|
|
463
|
-
import
|
|
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 =
|
|
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 =
|
|
733
|
+
const absoluteFilePath = path3.resolve(this.projectRoot, filePath);
|
|
480
734
|
if (!absoluteFilePath.startsWith(this.projectRoot)) {
|
|
481
735
|
return false;
|
|
482
736
|
}
|
|
483
|
-
const relativePath =
|
|
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 =
|
|
492
|
-
this.globalPatterns =
|
|
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(
|
|
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 =
|
|
754
|
+
current = path3.join(current, pathParts[i]);
|
|
501
755
|
dirsToVisit.push(current);
|
|
502
756
|
}
|
|
503
757
|
for (const dir of dirsToVisit) {
|
|
504
|
-
const relativeDir =
|
|
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 =
|
|
516
|
-
if (
|
|
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 =
|
|
785
|
+
content = fs4.readFileSync(patternsFilePath, "utf8");
|
|
532
786
|
} catch {
|
|
533
787
|
return [];
|
|
534
788
|
}
|
|
535
|
-
const isExcludeFile = patternsFilePath.endsWith(
|
|
536
|
-
const relativeBaseDir = isExcludeFile ? "." :
|
|
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 =
|
|
813
|
+
newPattern = path3.posix.join("**", p);
|
|
560
814
|
}
|
|
561
|
-
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 =
|
|
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
|
|
919
|
+
await fs5.remove(fullPath);
|
|
666
920
|
} else {
|
|
667
|
-
await
|
|
668
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
728
|
-
const previous = exists ? await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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 =
|
|
1101
|
+
const relative = path4.relative(this.workspaceRoot, current);
|
|
848
1102
|
const normalizedRel = relative.replace(/\\/g, "/");
|
|
849
|
-
if (
|
|
1103
|
+
if (path4.basename(current).startsWith(".") || ignoreFilter.isIgnored(normalizedRel)) {
|
|
850
1104
|
continue;
|
|
851
1105
|
}
|
|
852
1106
|
try {
|
|
853
|
-
const stats =
|
|
1107
|
+
const stats = fs5.statSync(current);
|
|
854
1108
|
if (stats.isDirectory()) {
|
|
855
|
-
const entries =
|
|
1109
|
+
const entries = fs5.readdirSync(current);
|
|
856
1110
|
for (const entry of entries) {
|
|
857
1111
|
if (!entry.startsWith(".")) {
|
|
858
|
-
stack.push(
|
|
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 =
|
|
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 =
|
|
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 ||
|
|
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
|
|
1202
|
+
if (!await fs5.pathExists(filePath)) {
|
|
949
1203
|
return "";
|
|
950
1204
|
}
|
|
951
|
-
const stats = await
|
|
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
|
|
1211
|
+
return fs5.readFile(filePath, "utf8");
|
|
958
1212
|
}
|
|
959
1213
|
resolvePath(target) {
|
|
960
|
-
const normalized =
|
|
961
|
-
const resolved =
|
|
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 =
|
|
1218
|
+
realPath = fs5.realpathSync(resolved);
|
|
965
1219
|
} catch {
|
|
966
|
-
const parentDir =
|
|
1220
|
+
const parentDir = path4.dirname(resolved);
|
|
967
1221
|
try {
|
|
968
|
-
const realParent =
|
|
969
|
-
realPath =
|
|
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 =
|
|
1230
|
+
realWorkspaceRoot = fs5.realpathSync(this.workspaceRoot);
|
|
977
1231
|
} catch {
|
|
978
1232
|
realWorkspaceRoot = this.workspaceRoot;
|
|
979
1233
|
}
|
|
980
|
-
const rootWithSep = realWorkspaceRoot.endsWith(
|
|
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 =
|
|
995
|
-
const relative =
|
|
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 =
|
|
1254
|
+
const stats = fs5.statSync(current);
|
|
1001
1255
|
if (stats.isDirectory()) {
|
|
1002
|
-
const entries =
|
|
1256
|
+
const entries = fs5.readdirSync(current);
|
|
1003
1257
|
for (const entry of entries) {
|
|
1004
1258
|
if (!entry.startsWith(".")) {
|
|
1005
|
-
stack.push(
|
|
1259
|
+
stack.push(path4.join(current, entry));
|
|
1006
1260
|
}
|
|
1007
1261
|
}
|
|
1008
1262
|
} else if (stats.isFile()) {
|
|
1009
|
-
const contents =
|
|
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:
|
|
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
|
|
1284
|
+
await fs5.ensureDir(dirPath);
|
|
1031
1285
|
}
|
|
1032
1286
|
async deletePath(relativePath, description) {
|
|
1033
1287
|
const fullPath = this.resolvePath(relativePath);
|
|
1034
|
-
const exists = await
|
|
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
|
|
1039
|
-
const previousContents = stats.isFile() ? await
|
|
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
|
|
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
|
|
1060
|
-
await
|
|
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
|
|
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 (!
|
|
1333
|
+
if (!fs5.existsSync(filePath)) {
|
|
1080
1334
|
return `${hit.file}:${hit.line}`;
|
|
1081
1335
|
}
|
|
1082
|
-
const contents =
|
|
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(
|
|
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/
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
1801
|
-
import
|
|
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
|
|
2022
|
-
return `${pointer} ${
|
|
2290
|
+
const path21 = dir ? chalk2.gray(dir) : "";
|
|
2291
|
+
return `${pointer} ${path21}${highlighted}`;
|
|
2023
2292
|
}
|
|
2024
2293
|
const dimmedFilename = chalk2.white(filename);
|
|
2025
|
-
const
|
|
2026
|
-
return `${pointer} ${
|
|
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
|
|
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 =
|
|
5348
|
+
const ext = path5.extname(filePath).toLowerCase();
|
|
5079
5349
|
if (EXTENSION_MAP[ext]) {
|
|
5080
5350
|
return EXTENSION_MAP[ext];
|
|
5081
5351
|
}
|
|
5082
|
-
const basename3 =
|
|
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
|
|
5209
|
-
import
|
|
5478
|
+
import fs6 from "fs-extra";
|
|
5479
|
+
import path6 from "path";
|
|
5210
5480
|
async function readPackageManifest(cwd) {
|
|
5211
|
-
const manifestPath =
|
|
5212
|
-
if (!await
|
|
5481
|
+
const manifestPath = path6.join(cwd, "package.json");
|
|
5482
|
+
if (!await fs6.pathExists(manifestPath)) {
|
|
5213
5483
|
return null;
|
|
5214
5484
|
}
|
|
5215
|
-
return
|
|
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 =
|
|
5227
|
-
await
|
|
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 =
|
|
5235
|
-
await
|
|
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
|
|
5305
|
-
import
|
|
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 =
|
|
5313
|
-
const resolvedWorkspace =
|
|
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
|
|
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 =
|
|
5334
|
-
const rel =
|
|
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
|
|
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 =
|
|
5357
|
-
if (!await
|
|
5626
|
+
const fullPath = path7.join(root, relativePath);
|
|
5627
|
+
if (!await fs7.pathExists(fullPath)) {
|
|
5358
5628
|
return null;
|
|
5359
5629
|
}
|
|
5360
|
-
const stats = await
|
|
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 =
|
|
5369
|
-
const exists = await
|
|
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 =
|
|
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
|
|
5385
|
-
import
|
|
5386
|
-
import
|
|
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 =
|
|
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 =
|
|
5741
|
+
worktreePath = path8.join(path8.dirname(this.repoRoot), `${this.repoName}-${branch}`);
|
|
5472
5742
|
}
|
|
5473
|
-
await
|
|
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 =
|
|
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 ||
|
|
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
|
|
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
|
|
5884
|
-
import
|
|
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 =
|
|
5888
|
-
if (!await
|
|
6157
|
+
const filePath = path9.join(COMMANDS_DIR, `${sanitizeName(name)}.json`);
|
|
6158
|
+
if (!await fs9.pathExists(filePath)) {
|
|
5889
6159
|
return null;
|
|
5890
6160
|
}
|
|
5891
|
-
return
|
|
6161
|
+
return fs9.readJson(filePath);
|
|
5892
6162
|
}
|
|
5893
6163
|
async function saveCustomCommand(definition) {
|
|
5894
|
-
await
|
|
5895
|
-
const filePath =
|
|
5896
|
-
await
|
|
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
|
|
6202
|
-
import
|
|
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
|
|
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 =
|
|
6247
|
-
await
|
|
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
|
|
6546
|
+
const exists = await fs10.pathExists(this.toolsDir);
|
|
6277
6547
|
if (!exists) {
|
|
6278
6548
|
return;
|
|
6279
6549
|
}
|
|
6280
|
-
const files = await
|
|
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 =
|
|
6555
|
+
const fullPath = path10.join(this.toolsDir, file);
|
|
6286
6556
|
try {
|
|
6287
|
-
const data = await
|
|
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
|
|
6728
|
-
const exists = this.files.root && await
|
|
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
|
|
7132
|
-
lines.push(` \u2713 ${
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
8384
|
+
const { help } = await import("./help-MU553D6W.js");
|
|
8115
8385
|
return help();
|
|
8116
8386
|
}
|
|
8117
8387
|
case "/agents": {
|
|
8118
|
-
const { handler } = await import("./agents-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
9059
|
-
const totalMem =
|
|
9060
|
-
const freeMem =
|
|
9149
|
+
const cpus = os5.cpus();
|
|
9150
|
+
const totalMem = os5.totalmem();
|
|
9151
|
+
const freeMem = os5.freemem();
|
|
9061
9152
|
return {
|
|
9062
|
-
platform:
|
|
9063
|
-
arch:
|
|
9064
|
-
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:
|
|
9077
|
-
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/
|
|
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
|
|
9107
|
-
var
|
|
9108
|
-
|
|
9109
|
-
|
|
9110
|
-
|
|
9111
|
-
|
|
9112
|
-
|
|
9113
|
-
|
|
9114
|
-
|
|
9115
|
-
|
|
9116
|
-
|
|
9117
|
-
this.
|
|
9118
|
-
|
|
9119
|
-
|
|
9120
|
-
|
|
9121
|
-
|
|
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
|
-
|
|
9126
|
-
|
|
9127
|
-
|
|
9128
|
-
|
|
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
|
-
|
|
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
|
-
|
|
9142
|
-
|
|
9143
|
-
|
|
9144
|
-
|
|
9145
|
-
|
|
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
|
|
9240
|
+
return this.deviceId;
|
|
9180
9241
|
}
|
|
9181
|
-
|
|
9182
|
-
|
|
9183
|
-
|
|
9184
|
-
|
|
9185
|
-
|
|
9186
|
-
|
|
9187
|
-
const
|
|
9188
|
-
|
|
9189
|
-
|
|
9190
|
-
|
|
9191
|
-
|
|
9192
|
-
|
|
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
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
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:
|
|
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
|
|
9467
|
-
queue = await
|
|
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
|
|
9479
|
-
await
|
|
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
|
|
9348
|
+
if (!await fs13.pathExists(this.queuePath)) {
|
|
9491
9349
|
return { sent, failed };
|
|
9492
9350
|
}
|
|
9493
|
-
const queue = await
|
|
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
|
|
9376
|
+
await fs13.writeJson(this.queuePath, remaining, { spaces: 2 });
|
|
9519
9377
|
} else {
|
|
9520
|
-
await
|
|
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
|
|
9389
|
+
if (!await fs13.pathExists(this.queuePath)) {
|
|
9532
9390
|
return { pending: 0, oldestItem: null };
|
|
9533
9391
|
}
|
|
9534
|
-
const queue = await
|
|
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 =
|
|
9601
|
-
this.responsesPath =
|
|
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 (
|
|
9614
|
-
return
|
|
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
|
-
|
|
9632
|
-
|
|
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
|
-
|
|
9496
|
+
fs14.ensureDirSync(this.stateDir);
|
|
9639
9497
|
let responses = [];
|
|
9640
|
-
if (
|
|
9641
|
-
responses =
|
|
9498
|
+
if (fs14.existsSync(this.responsesPath)) {
|
|
9499
|
+
responses = fs14.readJsonSync(this.responsesPath);
|
|
9642
9500
|
}
|
|
9643
9501
|
responses.push(response);
|
|
9644
|
-
|
|
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 (
|
|
9911
|
-
return
|
|
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
|
|
9778
|
+
import os7 from "os";
|
|
9921
9779
|
|
|
9922
9780
|
// src/telemetry/TelemetryClient.ts
|
|
9923
|
-
import
|
|
9924
|
-
import
|
|
9925
|
-
import
|
|
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
|
-
|
|
9958
|
-
if (
|
|
9959
|
-
return
|
|
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 =
|
|
9962
|
-
|
|
9819
|
+
const id = crypto4.randomUUID();
|
|
9820
|
+
fs15.writeFileSync(DEVICE_ID_FILE, id);
|
|
9963
9821
|
return id;
|
|
9964
9822
|
} catch {
|
|
9965
|
-
return
|
|
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
|
-
|
|
9974
|
-
if (
|
|
9975
|
-
const data =
|
|
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
|
-
|
|
9988
|
-
|
|
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:
|
|
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 =
|
|
9996
|
+
const syncQueueFile = path15.join(TELEMETRY_DIR, "session-sync-queue.json");
|
|
10139
9997
|
let syncQueue = [];
|
|
10140
|
-
if (
|
|
10141
|
-
syncQueue = JSON.parse(
|
|
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
|
-
|
|
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 =
|
|
10183
|
-
if (!
|
|
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(
|
|
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
|
-
|
|
10060
|
+
fs15.writeFileSync(syncQueueFile, JSON.stringify(remaining, null, 2));
|
|
10203
10061
|
} else {
|
|
10204
|
-
|
|
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:
|
|
10113
|
+
osVersion: os7.release(),
|
|
10256
10114
|
nodeVersion: process.version,
|
|
10257
10115
|
cpuArch: process.arch,
|
|
10258
|
-
cpuCores:
|
|
10259
|
-
memoryTotal: Math.round(
|
|
10116
|
+
cpuCores: os7.cpus().length,
|
|
10117
|
+
memoryTotal: Math.round(os7.totalmem() / 1024 / 1024),
|
|
10260
10118
|
// MB
|
|
10261
|
-
memoryFree: Math.round(
|
|
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
|
|
10482
|
-
import
|
|
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 ||
|
|
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 =
|
|
10356
|
+
const deviceIdPath = path16.join(AUTOHAND_HOME, "device-id");
|
|
10499
10357
|
try {
|
|
10500
|
-
if (
|
|
10501
|
-
return
|
|
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 =
|
|
10371
|
+
const queuePath = path16.join(this.queueDir, "queue.json");
|
|
10514
10372
|
try {
|
|
10515
|
-
if (
|
|
10516
|
-
this.queue =
|
|
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
|
-
|
|
10528
|
-
|
|
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
|
|
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 =
|
|
11246
|
-
const dirName =
|
|
11247
|
-
const displayPath = 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
|
|
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
|
|
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
|
|
11618
|
+
if (await fs17.pathExists(pkgPath)) {
|
|
12251
11619
|
try {
|
|
12252
|
-
const pkg = await
|
|
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
|
|
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
|
|
11732
|
+
const nvmrcExists = await fs17.pathExists(nvmrcPath);
|
|
12365
11733
|
const nodeVersionPath = join2(root, ".node-version");
|
|
12366
|
-
const nodeVersionExists = await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
11890
|
+
if (!await fs18.pathExists(pkgPath)) {
|
|
12523
11891
|
return {};
|
|
12524
11892
|
}
|
|
12525
11893
|
try {
|
|
12526
|
-
const pkg = await
|
|
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
|
|
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
|
|
12735
|
+
entries = await fs19.readdir(current);
|
|
13368
12736
|
} catch {
|
|
13369
12737
|
return;
|
|
13370
12738
|
}
|
|
13371
12739
|
for (const entry of entries) {
|
|
13372
|
-
const full =
|
|
13373
|
-
const rel =
|
|
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
|
|
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 =
|
|
13749
|
-
if (await
|
|
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
|
|
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.
|
|
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 (
|
|
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 =
|
|
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
|
|
14261
|
+
const exists = await fs19.pathExists(fullPath);
|
|
14893
14262
|
if (!exists) {
|
|
14894
14263
|
return false;
|
|
14895
14264
|
}
|
|
14896
14265
|
try {
|
|
14897
|
-
const stats = await
|
|
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
|
|
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 =
|
|
15217
|
-
if (await
|
|
15218
|
-
const content = await
|
|
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 =
|
|
15225
|
-
if (await
|
|
15226
|
-
const content = await
|
|
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 =
|
|
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
|
|
15860
|
-
import
|
|
15861
|
-
import
|
|
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:
|
|
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
|
|
15961
|
-
analysis.hasCI = await
|
|
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 =
|
|
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 =
|
|
15984
|
-
if (!await
|
|
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
|
|
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
|
|
15365
|
+
if (await fs20.pathExists(path19.join(this.projectRoot, "bun.lockb"))) {
|
|
15997
15366
|
analysis.packageManager = "bun";
|
|
15998
|
-
} else if (await
|
|
15367
|
+
} else if (await fs20.pathExists(path19.join(this.projectRoot, "pnpm-lock.yaml"))) {
|
|
15999
15368
|
analysis.packageManager = "pnpm";
|
|
16000
|
-
} else if (await
|
|
15369
|
+
} else if (await fs20.pathExists(path19.join(this.projectRoot, "yarn.lock"))) {
|
|
16001
15370
|
analysis.packageManager = "yarn";
|
|
16002
|
-
} else if (await
|
|
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 =
|
|
16044
|
-
const pyprojectPath =
|
|
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
|
|
15415
|
+
if (await fs20.pathExists(requirementsPath)) {
|
|
16047
15416
|
hasPython = true;
|
|
16048
15417
|
analysis.packageManager = "pip";
|
|
16049
15418
|
try {
|
|
16050
|
-
const content = await
|
|
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
|
|
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 =
|
|
16076
|
-
if (await
|
|
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 =
|
|
16086
|
-
if (await
|
|
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
|
|
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
|
|
15483
|
+
const entries = await fs20.readdir(dir, { withFileTypes: true });
|
|
16115
15484
|
for (const entry of entries) {
|
|
16116
|
-
const fullPath =
|
|
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 =
|
|
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 =
|
|
16266
|
-
const skillPath =
|
|
15634
|
+
const skillDir = path19.join(skillsDir, skill.name);
|
|
15635
|
+
const skillPath = path19.join(skillDir, "SKILL.md");
|
|
16267
15636
|
try {
|
|
16268
|
-
await
|
|
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
|
|
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 =
|
|
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,
|
|
16708
|
+
emitHookPermissionRequest(tool, path20, command, args) {
|
|
17340
16709
|
writeNotification(RPC_NOTIFICATIONS.HOOK_PERMISSION_REQUEST, {
|
|
17341
16710
|
tool,
|
|
17342
|
-
path:
|
|
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-
|
|
17411
|
-
const { GitHubRegistryFetcher } = await import("./GitHubRegistryFetcher-
|
|
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-
|
|
17471
|
-
const { GitHubRegistryFetcher } = await import("./GitHubRegistryFetcher-
|
|
17472
|
-
const { AUTOHAND_PATHS: AUTOHAND_PATHS2, PROJECT_DIR_NAME: PROJECT_DIR_NAME2 } = await import("./constants-
|
|
17473
|
-
const
|
|
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" ?
|
|
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 "
|
|
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
|
|
18190
|
-
|
|
18191
|
-
|
|
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
|
|
18205
|
-
|
|
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
|
|
18268
|
-
const { generateUnifiedPatch, formatChangeSummary } = await import("./patch-
|
|
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
|
|
18307
|
-
await
|
|
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
|