@tarcisiopgs/lisa 1.36.0 → 1.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-PGIXWLQT.js → chunk-555PDPCW.js} +89 -49
- package/dist/chunk-CTMZ666K.js +30 -0
- package/dist/{chunk-R6D5VH65.js → chunk-FUIWWBDX.js} +22 -10
- package/dist/chunk-HPWL5JRW.js +113 -0
- package/dist/{chunk-AFKXWCAM.js → chunk-MOQR4OY5.js} +9 -4
- package/dist/{chunk-V44FTYWZ.js → chunk-VS6R5KBO.js} +133 -146
- package/dist/{chunk-6VIN5PMW.js → chunk-XXVTKBC5.js} +1093 -542
- package/dist/{detection-H5QJR5XI.js → detection-MHQPM7O6.js} +3 -2
- package/dist/index.js +31 -24
- package/dist/{kanban-CRHTDRBU.js → kanban-ZQ67DJHP.js} +5 -3
- package/dist/{loop-732CLNLZ.js → loop-UN2MO6JN.js} +7 -4
- package/dist/{merge-CFQO7VU4.js → merge-Q3P65FEA.js} +1 -1
- package/dist/{tui-bridge-TSGCSJV4.js → tui-bridge-A5XQUJQ7.js} +11 -9
- package/package.json +1 -1
- package/dist/chunk-7JT7DTSS.js +0 -10
|
@@ -32,16 +32,10 @@ import {
|
|
|
32
32
|
resolveModels,
|
|
33
33
|
runValidationCommands,
|
|
34
34
|
runWithFallback
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-XXVTKBC5.js";
|
|
36
36
|
import {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
initLogFile,
|
|
40
|
-
kanbanEmitter,
|
|
41
|
-
log,
|
|
42
|
-
ok,
|
|
43
|
-
warn
|
|
44
|
-
} from "./chunk-V44FTYWZ.js";
|
|
37
|
+
kanbanEmitter
|
|
38
|
+
} from "./chunk-VS6R5KBO.js";
|
|
45
39
|
import {
|
|
46
40
|
appendRawEntry,
|
|
47
41
|
migrateGuardrails
|
|
@@ -62,8 +56,17 @@ import {
|
|
|
62
56
|
stopSpinner
|
|
63
57
|
} from "./chunk-72CYGBT4.js";
|
|
64
58
|
import {
|
|
59
|
+
divider,
|
|
60
|
+
error,
|
|
61
|
+
initLogFile,
|
|
62
|
+
log,
|
|
63
|
+
ok,
|
|
64
|
+
warn
|
|
65
|
+
} from "./chunk-HPWL5JRW.js";
|
|
66
|
+
import {
|
|
67
|
+
LisaError,
|
|
65
68
|
formatError
|
|
66
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-CTMZ666K.js";
|
|
67
70
|
import {
|
|
68
71
|
fetchPrFeedback,
|
|
69
72
|
formatPrFeedbackEntry
|
|
@@ -111,7 +114,7 @@ var configSchema = object({
|
|
|
111
114
|
base_branch: string().optional(),
|
|
112
115
|
workspace: string().optional()
|
|
113
116
|
}).passthrough();
|
|
114
|
-
var ConfigValidationError = class extends
|
|
117
|
+
var ConfigValidationError = class extends LisaError {
|
|
115
118
|
constructor(message) {
|
|
116
119
|
super(message);
|
|
117
120
|
this.name = "ConfigValidationError";
|
|
@@ -857,13 +860,13 @@ function killProviderForIssue(issueId) {
|
|
|
857
860
|
}, 5e3);
|
|
858
861
|
}
|
|
859
862
|
function setupEventListeners() {
|
|
860
|
-
|
|
863
|
+
const onPause = () => {
|
|
861
864
|
_loopPaused = true;
|
|
862
|
-
}
|
|
863
|
-
|
|
865
|
+
};
|
|
866
|
+
const onResume = () => {
|
|
864
867
|
_loopPaused = false;
|
|
865
|
-
}
|
|
866
|
-
|
|
868
|
+
};
|
|
869
|
+
const onPauseProvider = (issueId) => {
|
|
867
870
|
if (issueId) {
|
|
868
871
|
const pid = activeProviderPids.get(issueId);
|
|
869
872
|
if (pid) {
|
|
@@ -883,8 +886,8 @@ function setupEventListeners() {
|
|
|
883
886
|
}
|
|
884
887
|
}
|
|
885
888
|
kanbanEmitter.emit("provider:paused", issueId);
|
|
886
|
-
}
|
|
887
|
-
|
|
889
|
+
};
|
|
890
|
+
const onResumeProvider = (issueId) => {
|
|
888
891
|
if (issueId) {
|
|
889
892
|
const pid = activeProviderPids.get(issueId);
|
|
890
893
|
if (pid && providerPausedSet.has(issueId)) {
|
|
@@ -907,8 +910,8 @@ function setupEventListeners() {
|
|
|
907
910
|
providerPausedSet.clear();
|
|
908
911
|
}
|
|
909
912
|
kanbanEmitter.emit("provider:resumed", issueId);
|
|
910
|
-
}
|
|
911
|
-
|
|
913
|
+
};
|
|
914
|
+
const onKill = (issueId) => {
|
|
912
915
|
if (issueId) {
|
|
913
916
|
userKilledSet.add(issueId);
|
|
914
917
|
killProviderForIssue(issueId);
|
|
@@ -919,8 +922,8 @@ function setupEventListeners() {
|
|
|
919
922
|
killProviderForIssue(firstId);
|
|
920
923
|
}
|
|
921
924
|
}
|
|
922
|
-
}
|
|
923
|
-
|
|
925
|
+
};
|
|
926
|
+
const onSkip = (issueId) => {
|
|
924
927
|
if (issueId) {
|
|
925
928
|
userSkippedSet.add(issueId);
|
|
926
929
|
killProviderForIssue(issueId);
|
|
@@ -931,15 +934,33 @@ function setupEventListeners() {
|
|
|
931
934
|
killProviderForIssue(firstId);
|
|
932
935
|
}
|
|
933
936
|
}
|
|
934
|
-
}
|
|
935
|
-
|
|
937
|
+
};
|
|
938
|
+
const onRun = () => {
|
|
936
939
|
resolveIdle();
|
|
937
|
-
}
|
|
938
|
-
|
|
940
|
+
};
|
|
941
|
+
const onQuit = () => {
|
|
939
942
|
_userQuitFromWatchPrompt = true;
|
|
940
943
|
setShuttingDown(true);
|
|
941
944
|
resolveIdle();
|
|
942
|
-
}
|
|
945
|
+
};
|
|
946
|
+
kanbanEmitter.on("loop:pause", onPause);
|
|
947
|
+
kanbanEmitter.on("loop:resume", onResume);
|
|
948
|
+
kanbanEmitter.on("loop:pause-provider", onPauseProvider);
|
|
949
|
+
kanbanEmitter.on("loop:resume-provider", onResumeProvider);
|
|
950
|
+
kanbanEmitter.on("loop:kill", onKill);
|
|
951
|
+
kanbanEmitter.on("loop:skip", onSkip);
|
|
952
|
+
kanbanEmitter.on("loop:run", onRun);
|
|
953
|
+
kanbanEmitter.on("loop:quit", onQuit);
|
|
954
|
+
return () => {
|
|
955
|
+
kanbanEmitter.off("loop:pause", onPause);
|
|
956
|
+
kanbanEmitter.off("loop:resume", onResume);
|
|
957
|
+
kanbanEmitter.off("loop:pause-provider", onPauseProvider);
|
|
958
|
+
kanbanEmitter.off("loop:resume-provider", onResumeProvider);
|
|
959
|
+
kanbanEmitter.off("loop:kill", onKill);
|
|
960
|
+
kanbanEmitter.off("loop:skip", onSkip);
|
|
961
|
+
kanbanEmitter.off("loop:run", onRun);
|
|
962
|
+
kanbanEmitter.off("loop:quit", onQuit);
|
|
963
|
+
};
|
|
943
964
|
}
|
|
944
965
|
|
|
945
966
|
// src/session/reconciliation.ts
|
|
@@ -1803,7 +1824,14 @@ var WORKTREES_DIR = ".worktrees";
|
|
|
1803
1824
|
function generateBranchName(issueId, title) {
|
|
1804
1825
|
const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").substring(0, 40).replace(/^-|-$/g, "");
|
|
1805
1826
|
const safeId = issueId.toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
|
1806
|
-
|
|
1827
|
+
let branch = `feat/${safeId}-${slug}`;
|
|
1828
|
+
branch = branch.replace(/\.\./g, "");
|
|
1829
|
+
branch = branch.replace(/@\{/g, "");
|
|
1830
|
+
branch = branch.replace(/^[./]+/, "").replace(/[./]+$/, "");
|
|
1831
|
+
if (!branch || branch === "feat/" || branch === "feat") {
|
|
1832
|
+
branch = `feat/${safeId}-${Date.now()}`;
|
|
1833
|
+
}
|
|
1834
|
+
return branch;
|
|
1807
1835
|
}
|
|
1808
1836
|
async function cleanupOrphanedWorktree(repoRoot, branchName) {
|
|
1809
1837
|
const { stdout: branchList } = await execa2("git", ["branch", "--list", branchName], {
|
|
@@ -2083,28 +2111,11 @@ async function monitorCi(branch, config, issue, models, cwd, logFile, workspace,
|
|
|
2083
2111
|
// src/session/hooks.ts
|
|
2084
2112
|
import { spawn as spawn2 } from "child_process";
|
|
2085
2113
|
var DEFAULT_HOOK_TIMEOUT = 6e4;
|
|
2086
|
-
var
|
|
2087
|
-
/^GITHUB_TOKEN$/,
|
|
2088
|
-
/^GH_TOKEN$/,
|
|
2089
|
-
/^GITLAB_TOKEN$/,
|
|
2090
|
-
/^BITBUCKET_.*(TOKEN|PASSWORD|SECRET)/i,
|
|
2091
|
-
/^LINEAR_API_KEY$/,
|
|
2092
|
-
/^TRELLO_(API_KEY|TOKEN)$/,
|
|
2093
|
-
/^PLANE_API_TOKEN$/,
|
|
2094
|
-
/^SHORTCUT_API_TOKEN$/,
|
|
2095
|
-
/^JIRA_(API_TOKEN|TOKEN)$/,
|
|
2096
|
-
/^AWS_(SECRET|SESSION).*KEY/i,
|
|
2097
|
-
/^ANTHROPIC_API_KEY$/,
|
|
2098
|
-
/^OPENAI_API_KEY$/,
|
|
2099
|
-
/^GOOGLE_API_KEY$/,
|
|
2100
|
-
/^GEMINI_API_KEY$/,
|
|
2101
|
-
/^NPM_TOKEN$/,
|
|
2102
|
-
/^PYPI_TOKEN$/
|
|
2103
|
-
];
|
|
2114
|
+
var SAFE_ENV_PATTERN = /^(PATH|HOME|USER|LANG|LANGUAGE|LC_.+|SHELL|TERM|TERM_PROGRAM|PWD|OLDPWD|TMPDIR|TMP|TEMP|EDITOR|VISUAL|CI|LISA_.+|NODE_ENV|NODE_PATH|NPM_.+|PNPM_.+|YARN_.+|XDG_.+|COLORTERM|FORCE_COLOR|NO_COLOR|COLUMNS|LINES|HOSTNAME|LOGNAME|SHLVL)$/;
|
|
2104
2115
|
function sanitizeEnv(env) {
|
|
2105
2116
|
const result = {};
|
|
2106
2117
|
for (const [key, value] of Object.entries(env)) {
|
|
2107
|
-
if (value !== void 0 &&
|
|
2118
|
+
if (value !== void 0 && SAFE_ENV_PATTERN.test(key)) {
|
|
2108
2119
|
result[key] = value;
|
|
2109
2120
|
}
|
|
2110
2121
|
}
|
|
@@ -3433,6 +3444,8 @@ async function runConcurrentLoop(config, source, models, workspace, opts) {
|
|
|
3433
3444
|
let consecutiveExhaustions = 0;
|
|
3434
3445
|
const MAX_CONSECUTIVE_EXHAUSTIONS = 3;
|
|
3435
3446
|
const slotPool = Array.from({ length: concurrency }, (_, i) => i);
|
|
3447
|
+
let watchStartTime = null;
|
|
3448
|
+
const watchTimeout = config.loop.watch_timeout ?? 0;
|
|
3436
3449
|
const processIssue = async (issue, session, slotIndex) => {
|
|
3437
3450
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").substring(0, 19);
|
|
3438
3451
|
const logFile = resolve7(getLogsDir(workspace), `session_${session}_${timestamp}.log`);
|
|
@@ -3461,8 +3474,12 @@ async function runConcurrentLoop(config, source, models, workspace, opts) {
|
|
|
3461
3474
|
} catch (err) {
|
|
3462
3475
|
error(`Unhandled error in session for ${issue.id}: ${formatError(err)}`);
|
|
3463
3476
|
await revertIssueStatus(issue, source, config);
|
|
3464
|
-
|
|
3477
|
+
userKilledSet.delete(issue.id);
|
|
3478
|
+
userSkippedSet.delete(issue.id);
|
|
3479
|
+
providerPausedSet.delete(issue.id);
|
|
3465
3480
|
activeProviderPids.delete(issue.id);
|
|
3481
|
+
activeCleanups.delete(issue.id);
|
|
3482
|
+
claimedIssueIds.delete(issue.id);
|
|
3466
3483
|
if (config.bell !== false) notify(2);
|
|
3467
3484
|
return;
|
|
3468
3485
|
}
|
|
@@ -3525,10 +3542,20 @@ async function runConcurrentLoop(config, source, models, workspace, opts) {
|
|
|
3525
3542
|
}
|
|
3526
3543
|
if (issue && claimedIssueIds.has(issue.id)) {
|
|
3527
3544
|
log(`Issue ${issue.id} already claimed by another worker. Skipping.`);
|
|
3545
|
+
await sleep(Math.max(config.loop.cooldown * 1e3, 2e3));
|
|
3528
3546
|
break;
|
|
3529
3547
|
}
|
|
3530
3548
|
if (!issue) {
|
|
3531
3549
|
if (opts.watch) {
|
|
3550
|
+
if (watchStartTime === null) watchStartTime = Date.now();
|
|
3551
|
+
if (watchTimeout > 0) {
|
|
3552
|
+
const elapsed = (Date.now() - watchStartTime) / 1e3;
|
|
3553
|
+
if (elapsed >= watchTimeout) {
|
|
3554
|
+
ok(`Watch mode timeout reached (${watchTimeout}s). Stopping.`);
|
|
3555
|
+
noMoreIssues = true;
|
|
3556
|
+
break;
|
|
3557
|
+
}
|
|
3558
|
+
}
|
|
3532
3559
|
if (activeWorkers.size === 0) {
|
|
3533
3560
|
if (completedCount > 0) {
|
|
3534
3561
|
ok(`All issues resolved. Prompting user to continue watching...`);
|
|
@@ -3566,6 +3593,7 @@ async function runConcurrentLoop(config, source, models, workspace, opts) {
|
|
|
3566
3593
|
break;
|
|
3567
3594
|
}
|
|
3568
3595
|
kanbanEmitter.emit("work:resumed");
|
|
3596
|
+
watchStartTime = null;
|
|
3569
3597
|
sessionCounter = tentativeSession;
|
|
3570
3598
|
const session = sessionCounter;
|
|
3571
3599
|
claimedIssueIds.add(issue.id);
|
|
@@ -3929,6 +3957,8 @@ async function runSequentialLoop(config, source, models, workspace, opts) {
|
|
|
3929
3957
|
const MAX_CONSECUTIVE_FETCH_ERRORS = 3;
|
|
3930
3958
|
let consecutiveExhaustions = 0;
|
|
3931
3959
|
const MAX_CONSECUTIVE_EXHAUSTIONS = 3;
|
|
3960
|
+
let watchStartTime = null;
|
|
3961
|
+
const watchTimeout = config.loop.watch_timeout ?? 0;
|
|
3932
3962
|
while (true) {
|
|
3933
3963
|
session++;
|
|
3934
3964
|
if (opts.limit > 0 && session > opts.limit) {
|
|
@@ -3990,6 +4020,14 @@ async function runSequentialLoop(config, source, models, workspace, opts) {
|
|
|
3990
4020
|
break;
|
|
3991
4021
|
}
|
|
3992
4022
|
if (opts.watch) {
|
|
4023
|
+
if (watchStartTime === null) watchStartTime = Date.now();
|
|
4024
|
+
if (watchTimeout > 0) {
|
|
4025
|
+
const elapsed = (Date.now() - watchStartTime) / 1e3;
|
|
4026
|
+
if (elapsed >= watchTimeout) {
|
|
4027
|
+
ok(`Watch mode timeout reached (${watchTimeout}s). Stopping.`);
|
|
4028
|
+
break;
|
|
4029
|
+
}
|
|
4030
|
+
}
|
|
3993
4031
|
if (completedCount > 0) {
|
|
3994
4032
|
ok(`All issues resolved. Prompting user to continue watching...`);
|
|
3995
4033
|
kanbanEmitter.emit("work:watch-prompt");
|
|
@@ -4029,6 +4067,7 @@ async function runSequentialLoop(config, source, models, workspace, opts) {
|
|
|
4029
4067
|
continue;
|
|
4030
4068
|
}
|
|
4031
4069
|
kanbanEmitter.emit("work:resumed");
|
|
4070
|
+
watchStartTime = null;
|
|
4032
4071
|
ok(`Picked up: ${issue.id} \u2014 ${issue.title}`);
|
|
4033
4072
|
setTitle(`Lisa \u2014 ${issue.id}`);
|
|
4034
4073
|
const cachedPrUrls = loadPrUrls(workspace, issue.id);
|
|
@@ -4251,7 +4290,7 @@ async function runDemoLoop() {
|
|
|
4251
4290
|
}
|
|
4252
4291
|
|
|
4253
4292
|
// src/loop/index.ts
|
|
4254
|
-
setupEventListeners();
|
|
4293
|
+
var cleanupEventListeners = setupEventListeners();
|
|
4255
4294
|
async function runLoop(config, opts) {
|
|
4256
4295
|
const source = createSource(config.source);
|
|
4257
4296
|
const models = resolveModels(config);
|
|
@@ -4323,5 +4362,6 @@ export {
|
|
|
4323
4362
|
checkoutBaseBranches,
|
|
4324
4363
|
listSessionRecords,
|
|
4325
4364
|
runDemoLoop,
|
|
4365
|
+
cleanupEventListeners,
|
|
4326
4366
|
runLoop
|
|
4327
4367
|
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/errors.ts
|
|
4
|
+
function formatError(err) {
|
|
5
|
+
if (err instanceof Error) {
|
|
6
|
+
const cause = err.cause ? ` (caused by: ${formatError(err.cause)})` : "";
|
|
7
|
+
return `${err.message}${cause}`;
|
|
8
|
+
}
|
|
9
|
+
return String(err);
|
|
10
|
+
}
|
|
11
|
+
var LisaError = class extends Error {
|
|
12
|
+
constructor(message, options) {
|
|
13
|
+
super(message, options);
|
|
14
|
+
this.name = "LisaError";
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
var SourceError = class extends LisaError {
|
|
18
|
+
constructor(message, source, statusCode, options) {
|
|
19
|
+
super(message, options);
|
|
20
|
+
this.source = source;
|
|
21
|
+
this.statusCode = statusCode;
|
|
22
|
+
this.name = "SourceError";
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export {
|
|
27
|
+
formatError,
|
|
28
|
+
LisaError,
|
|
29
|
+
SourceError
|
|
30
|
+
};
|
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
import {
|
|
3
3
|
isGhCliAvailable
|
|
4
4
|
} from "./chunk-YMV4CBQE.js";
|
|
5
|
+
import {
|
|
6
|
+
verbose
|
|
7
|
+
} from "./chunk-HPWL5JRW.js";
|
|
5
8
|
import {
|
|
6
9
|
formatError
|
|
7
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-CTMZ666K.js";
|
|
8
11
|
|
|
9
12
|
// src/cli/detection.ts
|
|
10
13
|
import { execSync } from "child_process";
|
|
@@ -17,7 +20,8 @@ function getVersion() {
|
|
|
17
20
|
const pkgPath = resolvePath(new URL(".", import.meta.url).pathname, "../package.json");
|
|
18
21
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
19
22
|
return pkg.version;
|
|
20
|
-
} catch {
|
|
23
|
+
} catch (err) {
|
|
24
|
+
verbose(`Failed to read package.json version: ${formatError(err)}`);
|
|
21
25
|
return "0.0.0";
|
|
22
26
|
}
|
|
23
27
|
}
|
|
@@ -32,7 +36,8 @@ async function isCursorFreePlan() {
|
|
|
32
36
|
try {
|
|
33
37
|
execSync(`${b} --version`, { stdio: "ignore" });
|
|
34
38
|
return true;
|
|
35
|
-
} catch {
|
|
39
|
+
} catch (err) {
|
|
40
|
+
verbose(`Cursor binary "${b}" not found: ${formatError(err)}`);
|
|
36
41
|
return false;
|
|
37
42
|
}
|
|
38
43
|
});
|
|
@@ -49,7 +54,8 @@ async function isCursorFreePlan() {
|
|
|
49
54
|
} finally {
|
|
50
55
|
try {
|
|
51
56
|
rmSync(tmpDir, { recursive: true, force: true });
|
|
52
|
-
} catch {
|
|
57
|
+
} catch (err) {
|
|
58
|
+
verbose(`Failed to clean up temp dir ${tmpDir}: ${formatError(err)}`);
|
|
53
59
|
}
|
|
54
60
|
}
|
|
55
61
|
}
|
|
@@ -75,7 +81,8 @@ function fetchCursorModels() {
|
|
|
75
81
|
try {
|
|
76
82
|
execSync(`${b} --version`, { stdio: "ignore" });
|
|
77
83
|
return true;
|
|
78
|
-
} catch {
|
|
84
|
+
} catch (err) {
|
|
85
|
+
verbose(`Cursor binary "${b}" not available: ${formatError(err)}`);
|
|
79
86
|
return false;
|
|
80
87
|
}
|
|
81
88
|
});
|
|
@@ -85,7 +92,8 @@ function fetchCursorModels() {
|
|
|
85
92
|
const all = clean.split("\n").map((l) => l.trim()).filter((l) => l.includes(" - ")).map((l) => (l.split(" - ")[0] ?? "").trim()).filter(Boolean);
|
|
86
93
|
const filtered = CURSOR_PREFERRED_MODELS.filter((m) => all.includes(m));
|
|
87
94
|
return filtered.length > 0 ? filtered : CURSOR_PREFERRED_MODELS;
|
|
88
|
-
} catch {
|
|
95
|
+
} catch (err) {
|
|
96
|
+
verbose(`Failed to fetch Cursor models: ${formatError(err)}`);
|
|
89
97
|
return CURSOR_PREFERRED_MODELS;
|
|
90
98
|
}
|
|
91
99
|
}
|
|
@@ -98,7 +106,8 @@ function fetchOpenCodeModels() {
|
|
|
98
106
|
const raw = execSync("opencode models", { encoding: "utf-8", timeout: 1e4 });
|
|
99
107
|
const clean = raw.replace(/\x1b\[[0-9;]*[mGKHFA-Z]/g, "");
|
|
100
108
|
return clean.split("\n").map((l) => l.trim()).filter((m) => /^[a-z0-9][\w.-]*\/.+/i.test(m));
|
|
101
|
-
} catch {
|
|
109
|
+
} catch (err) {
|
|
110
|
+
verbose(`Failed to fetch OpenCode models: ${formatError(err)}`);
|
|
102
111
|
return [];
|
|
103
112
|
}
|
|
104
113
|
}
|
|
@@ -120,7 +129,8 @@ async function detectPlatform() {
|
|
|
120
129
|
const platformLabel = detectedPlatform === "cli" || detectedPlatform === "token" ? "GitHub" : detectedPlatform === "gitlab" ? "GitLab" : "Bitbucket";
|
|
121
130
|
clack.log.info(`Detected ${platformLabel} remote`);
|
|
122
131
|
}
|
|
123
|
-
} catch {
|
|
132
|
+
} catch (err) {
|
|
133
|
+
verbose(`Platform detection from git remote failed: ${formatError(err)}`);
|
|
124
134
|
}
|
|
125
135
|
const initialValue = detectedPlatform ?? "cli";
|
|
126
136
|
const selected = await clack.select({
|
|
@@ -210,7 +220,8 @@ function detectDefaultBranch(repoPath) {
|
|
|
210
220
|
encoding: "utf-8"
|
|
211
221
|
}).trim();
|
|
212
222
|
return ref.replace("origin/", "");
|
|
213
|
-
} catch {
|
|
223
|
+
} catch (err) {
|
|
224
|
+
verbose(`Failed to detect default branch, falling back to "main": ${formatError(err)}`);
|
|
214
225
|
return "main";
|
|
215
226
|
}
|
|
216
227
|
}
|
|
@@ -219,7 +230,8 @@ function getGitRepoName(repoPath) {
|
|
|
219
230
|
const url = execSync("git remote get-url origin", { cwd: repoPath, encoding: "utf-8" }).trim();
|
|
220
231
|
const match = url.match(/\/([^/]+?)(?:\.git)?$/) ?? url.match(/:([^/]+?)(?:\.git)?$/);
|
|
221
232
|
return match?.[1] ?? null;
|
|
222
|
-
} catch {
|
|
233
|
+
} catch (err) {
|
|
234
|
+
verbose(`Failed to get git repo name for ${repoPath}: ${formatError(err)}`);
|
|
223
235
|
return null;
|
|
224
236
|
}
|
|
225
237
|
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/output/logger.ts
|
|
4
|
+
import { appendFileSync, existsSync, mkdirSync, writeFileSync } from "fs";
|
|
5
|
+
import { dirname } from "path";
|
|
6
|
+
import pc from "picocolors";
|
|
7
|
+
var logFilePath = null;
|
|
8
|
+
var outputMode = "default";
|
|
9
|
+
var logLevel = "default";
|
|
10
|
+
function setOutputMode(mode) {
|
|
11
|
+
outputMode = mode;
|
|
12
|
+
}
|
|
13
|
+
function getOutputMode() {
|
|
14
|
+
return outputMode;
|
|
15
|
+
}
|
|
16
|
+
function setLogLevel(level) {
|
|
17
|
+
logLevel = level;
|
|
18
|
+
}
|
|
19
|
+
function shouldPrintToConsole() {
|
|
20
|
+
return outputMode !== "tui" && logLevel !== "quiet";
|
|
21
|
+
}
|
|
22
|
+
function initLogFile(path) {
|
|
23
|
+
const dir = dirname(path);
|
|
24
|
+
if (!existsSync(dir)) {
|
|
25
|
+
mkdirSync(dir, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
writeFileSync(path, `[${timestamp()}] Log started
|
|
28
|
+
`);
|
|
29
|
+
logFilePath = path;
|
|
30
|
+
}
|
|
31
|
+
function timestamp() {
|
|
32
|
+
return (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
|
|
33
|
+
}
|
|
34
|
+
function writeToFile(level, message) {
|
|
35
|
+
if (logFilePath) {
|
|
36
|
+
appendFileSync(logFilePath, `[${timestamp()}] [${level}] ${message}
|
|
37
|
+
`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function log(message) {
|
|
41
|
+
if (shouldPrintToConsole()) {
|
|
42
|
+
console.error(`${pc.cyan("[lisa]")} ${pc.dim(timestamp())} ${message}`);
|
|
43
|
+
}
|
|
44
|
+
writeToFile("info", message);
|
|
45
|
+
}
|
|
46
|
+
function warn(message) {
|
|
47
|
+
if (shouldPrintToConsole()) {
|
|
48
|
+
console.error(`${pc.yellow("[lisa]")} ${pc.dim(timestamp())} ${message}`);
|
|
49
|
+
}
|
|
50
|
+
writeToFile("warn", message);
|
|
51
|
+
}
|
|
52
|
+
function error(message) {
|
|
53
|
+
if (shouldPrintToConsole()) {
|
|
54
|
+
console.error(`${pc.red("[lisa]")} ${pc.dim(timestamp())} ${message}`);
|
|
55
|
+
}
|
|
56
|
+
writeToFile("error", message);
|
|
57
|
+
}
|
|
58
|
+
function ok(message) {
|
|
59
|
+
if (shouldPrintToConsole()) {
|
|
60
|
+
console.error(`${pc.green("[lisa]")} ${pc.dim(timestamp())} ${message}`);
|
|
61
|
+
}
|
|
62
|
+
writeToFile("ok", message);
|
|
63
|
+
}
|
|
64
|
+
function verbose(message) {
|
|
65
|
+
if (logLevel !== "verbose") return;
|
|
66
|
+
if (shouldPrintToConsole()) {
|
|
67
|
+
console.error(`${pc.dim("[lisa]")} ${pc.dim(timestamp())} ${pc.dim(message)}`);
|
|
68
|
+
}
|
|
69
|
+
writeToFile("verbose", message);
|
|
70
|
+
}
|
|
71
|
+
function divider(session) {
|
|
72
|
+
log(`${"\u2501".repeat(3)} Session ${session} ${"\u2501".repeat(3)}`);
|
|
73
|
+
}
|
|
74
|
+
function banner() {
|
|
75
|
+
if (outputMode !== "default" || logLevel === "quiet") return;
|
|
76
|
+
const title = " lisa \u266A autonomous issue resolver ";
|
|
77
|
+
const border = "\u2500".repeat(title.length);
|
|
78
|
+
console.error(pc.yellow(`
|
|
79
|
+
\u250C${border}\u2510`));
|
|
80
|
+
console.error(pc.yellow(` \u2502`) + pc.bold(pc.white(title)) + pc.yellow("\u2502"));
|
|
81
|
+
console.error(pc.yellow(` \u2514${border}\u2518
|
|
82
|
+
`));
|
|
83
|
+
}
|
|
84
|
+
function updateNotice(update) {
|
|
85
|
+
if (outputMode !== "default" || logLevel === "quiet") return;
|
|
86
|
+
const msg = `Update available ${pc.dim(update.currentVersion)} \u2192 ${pc.green(pc.bold(update.latestVersion))}`;
|
|
87
|
+
const cmd = `Run ${pc.cyan("npm i -g @tarcisiopgs/lisa")} to update`;
|
|
88
|
+
const lines = [msg, cmd];
|
|
89
|
+
const strip = (s) => s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
90
|
+
const maxLen = Math.max(...lines.map((l) => strip(l).length));
|
|
91
|
+
const pad = (s) => s + " ".repeat(maxLen - strip(s).length);
|
|
92
|
+
console.error(pc.yellow(` \u250C${"\u2500".repeat(maxLen + 2)}\u2510`));
|
|
93
|
+
for (const line of lines) {
|
|
94
|
+
console.error(pc.yellow(" \u2502 ") + pad(line) + pc.yellow(" \u2502"));
|
|
95
|
+
}
|
|
96
|
+
console.error(pc.yellow(` \u2514${"\u2500".repeat(maxLen + 2)}\u2518
|
|
97
|
+
`));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export {
|
|
101
|
+
setOutputMode,
|
|
102
|
+
getOutputMode,
|
|
103
|
+
setLogLevel,
|
|
104
|
+
initLogFile,
|
|
105
|
+
log,
|
|
106
|
+
warn,
|
|
107
|
+
error,
|
|
108
|
+
ok,
|
|
109
|
+
verbose,
|
|
110
|
+
divider,
|
|
111
|
+
banner,
|
|
112
|
+
updateNotice
|
|
113
|
+
};
|
|
@@ -5,17 +5,22 @@ import {
|
|
|
5
5
|
resolveModels,
|
|
6
6
|
runWithFallback,
|
|
7
7
|
saveLineage
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-XXVTKBC5.js";
|
|
9
|
+
import {
|
|
10
|
+
normalizeLabels
|
|
11
|
+
} from "./chunk-VS6R5KBO.js";
|
|
9
12
|
import {
|
|
10
13
|
error,
|
|
11
14
|
log,
|
|
12
|
-
normalizeLabels,
|
|
13
15
|
ok,
|
|
14
16
|
warn
|
|
15
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-HPWL5JRW.js";
|
|
18
|
+
import {
|
|
19
|
+
LisaError
|
|
20
|
+
} from "./chunk-CTMZ666K.js";
|
|
16
21
|
|
|
17
22
|
// src/cli/error.ts
|
|
18
|
-
var CliError = class extends
|
|
23
|
+
var CliError = class extends LisaError {
|
|
19
24
|
exitCode;
|
|
20
25
|
constructor(message, exitCode = 1) {
|
|
21
26
|
super(message);
|