@tarcisiopgs/lisa 1.7.0 → 1.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/dist/index.js
CHANGED
|
@@ -317,7 +317,9 @@ function error(message) {
|
|
|
317
317
|
emitJson("error", message);
|
|
318
318
|
return;
|
|
319
319
|
}
|
|
320
|
-
|
|
320
|
+
if (shouldPrintToConsole()) {
|
|
321
|
+
console.error(`${pc.red("[lisa]")} ${pc.dim(timestamp())} ${message}`);
|
|
322
|
+
}
|
|
321
323
|
writeToFile("error", message);
|
|
322
324
|
}
|
|
323
325
|
function ok(message) {
|
|
@@ -3285,8 +3287,14 @@ function installSignalHandlers() {
|
|
|
3285
3287
|
`Failed to revert ${issueId}: ${err instanceof Error ? err.message : String(err)}`
|
|
3286
3288
|
);
|
|
3287
3289
|
}
|
|
3290
|
+
kanbanEmitter.emit("issue:reverted", issueId);
|
|
3291
|
+
}
|
|
3292
|
+
const hasTUI = kanbanEmitter.listenerCount("tui:exit") > 0;
|
|
3293
|
+
kanbanEmitter.emit("tui:exit");
|
|
3294
|
+
if (hasTUI) {
|
|
3295
|
+
await new Promise((resolve6) => setTimeout(resolve6, 250));
|
|
3288
3296
|
}
|
|
3289
|
-
process.exit(
|
|
3297
|
+
process.exit(0);
|
|
3290
3298
|
};
|
|
3291
3299
|
process.on("SIGINT", () => {
|
|
3292
3300
|
cleanup("SIGINT");
|
|
@@ -4114,7 +4122,7 @@ Add them to your ${shell} and run: source ${shell}`));
|
|
|
4114
4122
|
if (isTUI) {
|
|
4115
4123
|
const { render } = await import("ink");
|
|
4116
4124
|
const { createElement } = await import("react");
|
|
4117
|
-
const { KanbanApp } = await import("./kanban-
|
|
4125
|
+
const { KanbanApp } = await import("./kanban-BY7QNVEE.js");
|
|
4118
4126
|
render(createElement(KanbanApp, { config: merged }), { exitOnCtrlC: false });
|
|
4119
4127
|
}
|
|
4120
4128
|
await runLoop(merged, {
|
|
@@ -4164,7 +4172,7 @@ var init = defineCommand({
|
|
|
4164
4172
|
}
|
|
4165
4173
|
if (configExists()) {
|
|
4166
4174
|
const overwrite = await clack.confirm({
|
|
4167
|
-
message: "
|
|
4175
|
+
message: "A config already exists at .lisa/config.yaml. Overwrite it?"
|
|
4168
4176
|
});
|
|
4169
4177
|
if (clack.isCancel(overwrite) || !overwrite) {
|
|
4170
4178
|
log("Cancelled.");
|
|
@@ -4335,6 +4343,7 @@ var main = defineCommand({
|
|
|
4335
4343
|
});
|
|
4336
4344
|
async function runConfigWizard() {
|
|
4337
4345
|
banner();
|
|
4346
|
+
clack.intro(pc2.cyan(" lisa \u2014 autonomous issue resolver "));
|
|
4338
4347
|
const providerLabels = {
|
|
4339
4348
|
claude: "Claude Code",
|
|
4340
4349
|
gemini: "Gemini CLI",
|
|
@@ -4350,25 +4359,26 @@ async function runConfigWizard() {
|
|
|
4350
4359
|
};
|
|
4351
4360
|
const available = await getAvailableProviders();
|
|
4352
4361
|
if (available.length === 0) {
|
|
4353
|
-
clack.log.error("No
|
|
4362
|
+
clack.log.error("No AI provider found on your system.");
|
|
4354
4363
|
clack.log.info(
|
|
4355
|
-
`Install at least one of the following
|
|
4364
|
+
`Install at least one of the following and re-run ${pc2.cyan("lisa init")}:
|
|
4356
4365
|
|
|
4357
|
-
${pc2.bold("Claude Code")}
|
|
4358
|
-
${pc2.bold("Gemini CLI")}
|
|
4359
|
-
${pc2.bold("OpenCode")}
|
|
4360
|
-
|
|
4361
|
-
|
|
4366
|
+
${pc2.bold("Claude Code")} ${pc2.dim("npm i -g @anthropic-ai/claude-code")}
|
|
4367
|
+
${pc2.bold("Gemini CLI")} ${pc2.dim("npm i -g @google/gemini-cli")}
|
|
4368
|
+
${pc2.bold("OpenCode")} ${pc2.dim("npm i -g opencode")}
|
|
4369
|
+
${pc2.bold("GitHub Copilot CLI")} ${pc2.dim("npm i -g @github/copilot-cli")}
|
|
4370
|
+
${pc2.bold("Goose")} ${pc2.dim("https://block.github.io/goose")}
|
|
4371
|
+
${pc2.bold("Aider")} ${pc2.dim("pip install aider-chat")}`
|
|
4362
4372
|
);
|
|
4363
4373
|
return process.exit(1);
|
|
4364
4374
|
}
|
|
4365
4375
|
let providerName;
|
|
4366
4376
|
if (available.length === 1 && available[0]) {
|
|
4367
4377
|
providerName = available[0].name;
|
|
4368
|
-
clack.log.info(`
|
|
4378
|
+
clack.log.info(`Auto-detected ${pc2.bold(providerLabels[providerName])} as your AI provider.`);
|
|
4369
4379
|
} else {
|
|
4370
4380
|
const selected = await clack.select({
|
|
4371
|
-
message: "Which AI provider
|
|
4381
|
+
message: "Which AI provider should resolve your issues?",
|
|
4372
4382
|
options: available.map((p) => ({
|
|
4373
4383
|
value: p.name,
|
|
4374
4384
|
label: providerLabels[p.name]
|
|
@@ -4383,14 +4393,14 @@ After installing, run ${pc2.cyan("lisa init")} again.`
|
|
|
4383
4393
|
const isFree = await isCursorFreePlan();
|
|
4384
4394
|
if (isFree) {
|
|
4385
4395
|
availableModels = ["auto"];
|
|
4386
|
-
clack.log.info("Cursor Free plan detected
|
|
4396
|
+
clack.log.info("Cursor Free plan detected \u2014 only the 'auto' model is available.");
|
|
4387
4397
|
} else {
|
|
4388
4398
|
availableModels = CURSOR_MODELS;
|
|
4389
4399
|
}
|
|
4390
4400
|
}
|
|
4391
4401
|
if (availableModels && availableModels.length > 0) {
|
|
4392
4402
|
const modelSelection = await clack.multiselect({
|
|
4393
|
-
message: "Which models
|
|
4403
|
+
message: "Which models should Lisa use? Select in order \u2014 first = primary, rest = fallbacks",
|
|
4394
4404
|
options: availableModels.map((m) => ({
|
|
4395
4405
|
value: m,
|
|
4396
4406
|
label: m
|
|
@@ -4401,10 +4411,15 @@ After installing, run ${pc2.cyan("lisa init")} again.`
|
|
|
4401
4411
|
selectedModels = modelSelection ?? [];
|
|
4402
4412
|
}
|
|
4403
4413
|
const source = await clack.select({
|
|
4404
|
-
message: "Where do your issues
|
|
4414
|
+
message: "Where do your issues come from?",
|
|
4405
4415
|
options: [
|
|
4406
|
-
{ value: "linear", label: "Linear" },
|
|
4407
|
-
{ value: "trello", label: "Trello" }
|
|
4416
|
+
{ value: "linear", label: "Linear", hint: "GraphQL API" },
|
|
4417
|
+
{ value: "trello", label: "Trello", hint: "REST API" },
|
|
4418
|
+
{ value: "github", label: "GitHub Issues", hint: "REST API" },
|
|
4419
|
+
{ value: "gitlab", label: "GitLab Issues", hint: "REST API" },
|
|
4420
|
+
{ value: "plane", label: "Plane", hint: "REST API" },
|
|
4421
|
+
{ value: "shortcut", label: "Shortcut", hint: "REST API" },
|
|
4422
|
+
{ value: "jira", label: "Jira", hint: "REST API" }
|
|
4408
4423
|
]
|
|
4409
4424
|
});
|
|
4410
4425
|
if (clack.isCancel(source)) return process.exit(0);
|
|
@@ -4412,24 +4427,27 @@ After installing, run ${pc2.cyan("lisa init")} again.`
|
|
|
4412
4427
|
if (missing.length > 0) {
|
|
4413
4428
|
const shell = process.env.SHELL?.includes("zsh") ? "~/.zshrc" : "~/.bashrc";
|
|
4414
4429
|
clack.log.warning(
|
|
4415
|
-
`
|
|
4430
|
+
`The following environment variables are missing:
|
|
4431
|
+
|
|
4416
4432
|
${missing.map((v) => ` ${pc2.bold(v)}`).join("\n")}
|
|
4417
4433
|
|
|
4418
|
-
Add them to
|
|
4419
|
-
${missing.map((v) => ` export ${v}="your-
|
|
4434
|
+
Add them to ${pc2.cyan(shell)}:
|
|
4435
|
+
${missing.map((v) => ` export ${v}="your-value-here"`).join("\n")}
|
|
4420
4436
|
|
|
4421
|
-
Then
|
|
4437
|
+
Then reload: ${pc2.cyan(`source ${shell}`)}`
|
|
4422
4438
|
);
|
|
4423
4439
|
}
|
|
4424
4440
|
const githubMethod = await detectGitHubMethod();
|
|
4425
4441
|
const teamAnswer = await clack.text({
|
|
4426
|
-
message: source === "linear" ? "
|
|
4442
|
+
message: source === "linear" ? "What is your Linear team name?" : source === "trello" ? "What is your Trello board name?" : source === "jira" ? "What is your Jira project key?" : "What is your team or project name?",
|
|
4443
|
+
placeholder: source === "linear" ? "e.g. Engineering" : void 0
|
|
4427
4444
|
});
|
|
4428
4445
|
if (clack.isCancel(teamAnswer)) return process.exit(0);
|
|
4429
4446
|
const team = teamAnswer;
|
|
4430
4447
|
const labelAnswer = await clack.text({
|
|
4431
|
-
message: "
|
|
4432
|
-
initialValue: "ready"
|
|
4448
|
+
message: "Which label marks issues as ready for the agent to pick up?",
|
|
4449
|
+
initialValue: "ready",
|
|
4450
|
+
placeholder: "e.g. ready, ai, lisa"
|
|
4433
4451
|
});
|
|
4434
4452
|
if (clack.isCancel(labelAnswer)) return process.exit(0);
|
|
4435
4453
|
const label = labelAnswer;
|
|
@@ -4446,47 +4464,57 @@ Then run: ${pc2.cyan(`source ${shell}`)}`
|
|
|
4446
4464
|
pickFrom = pickFromAnswer;
|
|
4447
4465
|
project = pickFrom;
|
|
4448
4466
|
const inProgressAnswer = await clack.text({
|
|
4449
|
-
message: "Move to which
|
|
4467
|
+
message: "Move the card to which list while the agent is working?",
|
|
4450
4468
|
initialValue: "In Progress"
|
|
4451
4469
|
});
|
|
4452
4470
|
if (clack.isCancel(inProgressAnswer)) return process.exit(0);
|
|
4453
4471
|
inProgress = inProgressAnswer;
|
|
4454
4472
|
const doneAnswer = await clack.text({
|
|
4455
|
-
message: "Move to which
|
|
4473
|
+
message: "Move the card to which list after the PR is created?",
|
|
4456
4474
|
initialValue: "Code Review"
|
|
4457
4475
|
});
|
|
4458
4476
|
if (clack.isCancel(doneAnswer)) return process.exit(0);
|
|
4459
4477
|
done = doneAnswer;
|
|
4460
4478
|
} else {
|
|
4461
4479
|
const projectAnswer = await clack.text({
|
|
4462
|
-
message: "
|
|
4480
|
+
message: source === "linear" ? "Which Linear project should Lisa work on? (leave empty for all team issues)" : "Which project should Lisa work on?",
|
|
4481
|
+
placeholder: source === "linear" ? "e.g. Q1 Roadmap (optional)" : void 0
|
|
4463
4482
|
});
|
|
4464
4483
|
if (clack.isCancel(projectAnswer)) return process.exit(0);
|
|
4465
4484
|
project = projectAnswer;
|
|
4466
4485
|
const pickFromAnswer = await clack.text({
|
|
4467
|
-
message: "Pick up issues
|
|
4468
|
-
initialValue: "Backlog"
|
|
4486
|
+
message: "Pick up issues in which status?",
|
|
4487
|
+
initialValue: "Backlog",
|
|
4488
|
+
placeholder: "e.g. Backlog, Todo"
|
|
4469
4489
|
});
|
|
4470
4490
|
if (clack.isCancel(pickFromAnswer)) return process.exit(0);
|
|
4471
4491
|
pickFrom = pickFromAnswer;
|
|
4472
4492
|
const inProgressAnswer = await clack.text({
|
|
4473
|
-
message: "Move to which status while working?",
|
|
4493
|
+
message: "Move to which status while the agent is working?",
|
|
4474
4494
|
initialValue: "In Progress"
|
|
4475
4495
|
});
|
|
4476
4496
|
if (clack.isCancel(inProgressAnswer)) return process.exit(0);
|
|
4477
4497
|
inProgress = inProgressAnswer;
|
|
4478
4498
|
const doneAnswer = await clack.text({
|
|
4479
|
-
message: "Move to which status after PR?",
|
|
4499
|
+
message: "Move to which status after the PR is created?",
|
|
4480
4500
|
initialValue: "In Review"
|
|
4481
4501
|
});
|
|
4482
4502
|
if (clack.isCancel(doneAnswer)) return process.exit(0);
|
|
4483
4503
|
done = doneAnswer;
|
|
4484
4504
|
}
|
|
4485
4505
|
const workflowAnswer = await clack.select({
|
|
4486
|
-
message: "How should Lisa
|
|
4506
|
+
message: "How should Lisa check out code for each issue?",
|
|
4487
4507
|
options: [
|
|
4488
|
-
{
|
|
4489
|
-
|
|
4508
|
+
{
|
|
4509
|
+
value: "worktree",
|
|
4510
|
+
label: "Worktree",
|
|
4511
|
+
hint: "isolated git worktree per issue \u2014 recommended"
|
|
4512
|
+
},
|
|
4513
|
+
{
|
|
4514
|
+
value: "branch",
|
|
4515
|
+
label: "Branch",
|
|
4516
|
+
hint: "new branch in the current checkout"
|
|
4517
|
+
}
|
|
4490
4518
|
]
|
|
4491
4519
|
});
|
|
4492
4520
|
if (clack.isCancel(workflowAnswer)) return process.exit(0);
|
|
@@ -4497,7 +4525,7 @@ Then run: ${pc2.cyan(`source ${shell}`)}`
|
|
|
4497
4525
|
if (repos.length === 0) {
|
|
4498
4526
|
const detected = detectDefaultBranch(cwd);
|
|
4499
4527
|
const branchAnswer = await clack.text({
|
|
4500
|
-
message: "
|
|
4528
|
+
message: "What is the base branch to branch off from?",
|
|
4501
4529
|
initialValue: detected
|
|
4502
4530
|
});
|
|
4503
4531
|
if (clack.isCancel(branchAnswer)) return process.exit(0);
|
|
@@ -4507,7 +4535,7 @@ Then run: ${pc2.cyan(`source ${shell}`)}`
|
|
|
4507
4535
|
const repoPath = resolvePath(cwd, repo.path);
|
|
4508
4536
|
const detected = detectDefaultBranch(repoPath);
|
|
4509
4537
|
const branchAnswer = await clack.text({
|
|
4510
|
-
message: `Base branch for ${repo.name}?`,
|
|
4538
|
+
message: `Base branch for ${pc2.bold(repo.name)}?`,
|
|
4511
4539
|
initialValue: detected
|
|
4512
4540
|
});
|
|
4513
4541
|
if (clack.isCancel(branchAnswer)) return process.exit(0);
|
|
@@ -4522,7 +4550,7 @@ Then run: ${pc2.cyan(`source ${shell}`)}`
|
|
|
4522
4550
|
ensureWorktreeGitignore(resolvePath(cwd, repo.path));
|
|
4523
4551
|
}
|
|
4524
4552
|
}
|
|
4525
|
-
clack.log.info("Added .worktrees to .gitignore");
|
|
4553
|
+
clack.log.info("Added .worktrees/ to .gitignore");
|
|
4526
4554
|
}
|
|
4527
4555
|
const cfg = {
|
|
4528
4556
|
provider: providerName,
|
|
@@ -4545,28 +4573,31 @@ Then run: ${pc2.cyan(`source ${shell}`)}`
|
|
|
4545
4573
|
logs: { dir: ".lisa/logs", format: "text" }
|
|
4546
4574
|
};
|
|
4547
4575
|
saveConfig(cfg);
|
|
4548
|
-
clack.outro(
|
|
4576
|
+
clack.outro(
|
|
4577
|
+
`${pc2.green("All set!")} Config saved to ${pc2.cyan(".lisa/config.yaml")}
|
|
4578
|
+
Run ${pc2.bold(pc2.cyan("lisa run"))} to start resolving issues.`
|
|
4579
|
+
);
|
|
4549
4580
|
}
|
|
4550
4581
|
async function detectGitHubMethod() {
|
|
4551
4582
|
const hasToken = !!process.env.GITHUB_TOKEN;
|
|
4552
4583
|
const hasCli = await isGhCliAvailable();
|
|
4553
4584
|
if (hasToken && hasCli) {
|
|
4554
4585
|
const selected = await clack.select({
|
|
4555
|
-
message: "
|
|
4586
|
+
message: "How should Lisa create pull requests?",
|
|
4556
4587
|
options: [
|
|
4557
|
-
{ value: "cli", label: "GitHub CLI", hint: "gh" },
|
|
4558
|
-
{ value: "token", label: "GitHub API", hint: "GITHUB_TOKEN" }
|
|
4588
|
+
{ value: "cli", label: "GitHub CLI", hint: "uses `gh pr create` \u2014 recommended" },
|
|
4589
|
+
{ value: "token", label: "GitHub API", hint: "uses GITHUB_TOKEN directly" }
|
|
4559
4590
|
]
|
|
4560
4591
|
});
|
|
4561
4592
|
if (clack.isCancel(selected)) return process.exit(0);
|
|
4562
4593
|
return selected;
|
|
4563
4594
|
}
|
|
4564
4595
|
if (hasCli) {
|
|
4565
|
-
clack.log.info("
|
|
4596
|
+
clack.log.info("Pull requests will be created using the GitHub CLI.");
|
|
4566
4597
|
return "cli";
|
|
4567
4598
|
}
|
|
4568
4599
|
if (hasToken) {
|
|
4569
|
-
clack.log.info("
|
|
4600
|
+
clack.log.info("Pull requests will be created using GITHUB_TOKEN.");
|
|
4570
4601
|
return "token";
|
|
4571
4602
|
}
|
|
4572
4603
|
return "token";
|
|
@@ -4574,7 +4605,7 @@ async function detectGitHubMethod() {
|
|
|
4574
4605
|
async function detectGitRepos() {
|
|
4575
4606
|
const cwd = process.cwd();
|
|
4576
4607
|
if (existsSync7(join12(cwd, ".git"))) {
|
|
4577
|
-
clack.log.info(
|
|
4608
|
+
clack.log.info("Found a git repository in the current directory.");
|
|
4578
4609
|
return [];
|
|
4579
4610
|
}
|
|
4580
4611
|
const entries = readdirSync(cwd, { withFileTypes: true });
|
|
@@ -4583,7 +4614,7 @@ async function detectGitRepos() {
|
|
|
4583
4614
|
return [];
|
|
4584
4615
|
}
|
|
4585
4616
|
const selected = await clack.multiselect({
|
|
4586
|
-
message: "
|
|
4617
|
+
message: "Multiple git repositories found \u2014 which ones should Lisa work on?",
|
|
4587
4618
|
options: gitDirs.map((dir) => ({ value: dir, label: dir }))
|
|
4588
4619
|
});
|
|
4589
4620
|
if (clack.isCancel(selected)) return process.exit(0);
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
kanbanEmitter,
|
|
3
4
|
useKanbanState
|
|
4
5
|
} from "./chunk-MUBWKMRZ.js";
|
|
5
6
|
|
|
6
7
|
// src/ui/kanban.tsx
|
|
7
8
|
import { Box as Box6, useApp, useInput as useInput2 } from "ink";
|
|
8
|
-
import { useState as useState3 } from "react";
|
|
9
|
+
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
9
10
|
|
|
10
11
|
// src/ui/board.tsx
|
|
11
12
|
import { Box as Box3, Text as Text3 } from "ink";
|
|
@@ -283,8 +284,9 @@ function IssueDetail({ card, onBack }) {
|
|
|
283
284
|
} else if (card.column === "done" && card.startedAt !== void 0 && card.finishedAt !== void 0) {
|
|
284
285
|
elapsedDisplay = formatElapsed2(card.finishedAt - card.startedAt);
|
|
285
286
|
}
|
|
286
|
-
const
|
|
287
|
-
const
|
|
287
|
+
const SIDEBAR_TOTAL_WIDTH = 28;
|
|
288
|
+
const separatorInner = Math.max(0, terminalCols - SIDEBAR_TOTAL_WIDTH - 4);
|
|
289
|
+
const separator = `\u2560${"\u2550".repeat(Math.max(0, separatorInner - 2))}\u2563`;
|
|
288
290
|
const totalLines = lines.length;
|
|
289
291
|
const scrollPct = totalLines <= bodyRows ? "100%" : `${Math.round((startLine + bodyRows) / totalLines * 100)}%`;
|
|
290
292
|
return /* @__PURE__ */ jsxs4(
|
|
@@ -413,6 +415,13 @@ function KanbanApp({ config }) {
|
|
|
413
415
|
const [activeView, setActiveView] = useState3("board");
|
|
414
416
|
const [activeColIndex, setActiveColIndex] = useState3(0);
|
|
415
417
|
const [activeCardIndex, setActiveCardIndex] = useState3(0);
|
|
418
|
+
useEffect3(() => {
|
|
419
|
+
const onExit = () => exit();
|
|
420
|
+
kanbanEmitter.on("tui:exit", onExit);
|
|
421
|
+
return () => {
|
|
422
|
+
kanbanEmitter.off("tui:exit", onExit);
|
|
423
|
+
};
|
|
424
|
+
}, [exit]);
|
|
416
425
|
const backlog = cards.filter((c) => c.column === "backlog");
|
|
417
426
|
const inProgress = cards.filter((c) => c.column === "in_progress");
|
|
418
427
|
const done = cards.filter((c) => c.column === "done");
|
|
@@ -420,7 +429,6 @@ function KanbanApp({ config }) {
|
|
|
420
429
|
useInput2((input, key) => {
|
|
421
430
|
if (input === "q") {
|
|
422
431
|
process.emit("SIGINT");
|
|
423
|
-
exit();
|
|
424
432
|
return;
|
|
425
433
|
}
|
|
426
434
|
if (activeView === "detail") {
|