@tarcisiopgs/lisa 1.8.0 → 1.8.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 +8 -4
- package/dist/chunk-YZKNBQN6.js +0 -0
- package/dist/index.js +51 -2
- package/dist/{kanban-5C3WZIKC.js → kanban-PD2F4KWT.js} +118 -29
- package/package.json +59 -61
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ Lisa follows a deterministic pipeline:
|
|
|
57
57
|
2. **Activate** — Moves the issue to `in_progress` so your team knows it's being worked on.
|
|
58
58
|
3. **Implement** — Builds a structured prompt with full issue context and sends it to the AI agent. The agent works in a worktree or branch, implements the change, runs tests, and commits.
|
|
59
59
|
4. **Validate** — If the agent's tests pass and pre-push hooks succeed, the branch is pushed. If hooks fail, Lisa re-invokes the agent with the error output and retries.
|
|
60
|
-
5. **PR** — Pushes the branch and creates a pull request referencing the original issue.
|
|
60
|
+
5. **PR** — Pushes the branch and creates a pull request referencing the original issue.
|
|
61
61
|
6. **Update** — Moves the issue to the `done` status and removes the pickup label.
|
|
62
62
|
7. **Next** — Picks the next issue. When there are no more matching issues, Lisa stops.
|
|
63
63
|
|
|
@@ -86,7 +86,9 @@ Lisa follows a deterministic pipeline:
|
|
|
86
86
|
|
|
87
87
|
At least one provider must be installed and available in your PATH.
|
|
88
88
|
|
|
89
|
-
> **Cursor Free plan** — `lisa init` automatically detects Free accounts and restricts model selection to `auto` only. On paid plans,
|
|
89
|
+
> **Cursor Free plan** — `lisa init` automatically detects Free accounts and restricts model selection to `auto` only. On paid plans, available models are fetched live from `cursor --list-models` and filtered to a curated top-tier list (`composer-1.5`, `opus-4.6`, `sonnet-4.6`, `gpt-5.3-codex`, etc.).
|
|
90
|
+
|
|
91
|
+
> **OpenCode** — `lisa init` fetches available models from `opencode models` and filters them based on which API keys are present in your environment (`ANTHROPIC_API_KEY`, `GEMINI_API_KEY`, `OPENAI_API_KEY`, `GITHUB_TOKEN`, `GROQ_API_KEY`, `MISTRAL_API_KEY`, `DEEPSEEK_API_KEY`). Only models from providers you have credentials for are shown.
|
|
90
92
|
|
|
91
93
|
### Fallback Chain
|
|
92
94
|
|
|
@@ -180,12 +182,14 @@ When running in an interactive terminal, `lisa run` renders a real-time Kanban b
|
|
|
180
182
|
└──────────────────────────┘ └───────────────────────────┘ └───────────────────────────┘
|
|
181
183
|
```
|
|
182
184
|
|
|
185
|
+
In-progress cards show a live elapsed timer. When the loop is paused, the active card displays a visual pause indicator. The detail view includes a scroll bar when output overflows.
|
|
186
|
+
|
|
183
187
|
### Keyboard shortcuts
|
|
184
188
|
|
|
185
189
|
| Key | Action |
|
|
186
190
|
|-----|--------|
|
|
187
|
-
|
|
|
188
|
-
| `Shift+Tab` | Move
|
|
191
|
+
| `←` / `→` | Move between columns |
|
|
192
|
+
| `Tab` / `Shift+Tab` | Move between columns (alternative) |
|
|
189
193
|
| `↑` / `↓` | Navigate cards / scroll output |
|
|
190
194
|
| `Enter` | Open issue detail view (streams provider output) |
|
|
191
195
|
| `Esc` | Close detail view, return to board |
|
package/dist/chunk-YZKNBQN6.js
CHANGED
|
File without changes
|
package/dist/index.js
CHANGED
|
@@ -171,6 +171,30 @@ function mergeWithFlags(config2, flags) {
|
|
|
171
171
|
|
|
172
172
|
// src/git/github.ts
|
|
173
173
|
import { execa } from "execa";
|
|
174
|
+
|
|
175
|
+
// src/git/pr-body.ts
|
|
176
|
+
var PROVIDER_ATTRIBUTION_RE = /claude\.ai|claude\s+code|gemini\s+cli|openai\s+codex|\bgoose\b|\baider\b|github\s+copilot|cursor\s+agent|\bopencode\b/i;
|
|
177
|
+
var AI_COAUTHOR_RE = /co-authored-by:[^\n]*(anthropic|claude|gemini|openai|codex|goose|aider|copilot|cursor|google)/i;
|
|
178
|
+
function stripProviderAttribution(body) {
|
|
179
|
+
let result = body;
|
|
180
|
+
while (true) {
|
|
181
|
+
const sepIndex = result.lastIndexOf("\n---");
|
|
182
|
+
if (sepIndex === -1) break;
|
|
183
|
+
const section = result.slice(sepIndex);
|
|
184
|
+
if (PROVIDER_ATTRIBUTION_RE.test(section) || AI_COAUTHOR_RE.test(section)) {
|
|
185
|
+
result = result.slice(0, sepIndex).trimEnd();
|
|
186
|
+
} else {
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
result = result.replace(
|
|
191
|
+
/\n+Co-Authored-By:[^\n]*(anthropic|claude|gemini|openai|codex|goose|aider|copilot|cursor|google)[^\n]*/gi,
|
|
192
|
+
""
|
|
193
|
+
);
|
|
194
|
+
return result.trimEnd();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// src/git/github.ts
|
|
174
198
|
async function isGhCliAvailable() {
|
|
175
199
|
try {
|
|
176
200
|
await execa("gh", ["auth", "status"]);
|
|
@@ -202,7 +226,7 @@ async function appendPrAttribution(prUrl, providerUsed) {
|
|
|
202
226
|
|
|
203
227
|
---
|
|
204
228
|
\u{1F916} Resolved by [lisa](https://github.com/tarcisiopgs/lisa) using **${providerName}**`;
|
|
205
|
-
const newBody = (body ?? "") + attribution;
|
|
229
|
+
const newBody = stripProviderAttribution(body ?? "") + attribution;
|
|
206
230
|
await execa("gh", ["pr", "edit", prUrl, "--body", newBody]);
|
|
207
231
|
} catch {
|
|
208
232
|
}
|
|
@@ -266,6 +290,28 @@ function ensureWorktreeGitignore(repoRoot) {
|
|
|
266
290
|
`);
|
|
267
291
|
}
|
|
268
292
|
}
|
|
293
|
+
var LOGS_GITIGNORE_ENTRY = ".lisa/logs/*";
|
|
294
|
+
function ensureLogsGitignore(repoRoot) {
|
|
295
|
+
if (!existsSync2(join(repoRoot, ".git"))) {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
const gitignorePath = join(repoRoot, ".gitignore");
|
|
299
|
+
if (!existsSync2(gitignorePath)) {
|
|
300
|
+
appendFileSync(gitignorePath, `# Lisa
|
|
301
|
+
${LOGS_GITIGNORE_ENTRY}
|
|
302
|
+
`);
|
|
303
|
+
return true;
|
|
304
|
+
}
|
|
305
|
+
const content = readFileSync2(gitignorePath, "utf-8");
|
|
306
|
+
if (content.split("\n").some((line) => line.trim() === LOGS_GITIGNORE_ENTRY)) {
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
const separator = content.endsWith("\n") ? "" : "\n";
|
|
310
|
+
appendFileSync(gitignorePath, `${separator}# Lisa
|
|
311
|
+
${LOGS_GITIGNORE_ENTRY}
|
|
312
|
+
`);
|
|
313
|
+
return true;
|
|
314
|
+
}
|
|
269
315
|
function determineRepoPath(repos, issue2, workspace) {
|
|
270
316
|
if (repos.length === 0) return void 0;
|
|
271
317
|
if (issue2.repo) {
|
|
@@ -4186,7 +4232,7 @@ Add them to your ${shell} and run: source ${shell}`));
|
|
|
4186
4232
|
if (isTUI) {
|
|
4187
4233
|
const { render } = await import("ink");
|
|
4188
4234
|
const { createElement } = await import("react");
|
|
4189
|
-
const { KanbanApp } = await import("./kanban-
|
|
4235
|
+
const { KanbanApp } = await import("./kanban-PD2F4KWT.js");
|
|
4190
4236
|
render(createElement(KanbanApp, { config: merged }), { exitOnCtrlC: false });
|
|
4191
4237
|
}
|
|
4192
4238
|
await runLoop(merged, {
|
|
@@ -4745,6 +4791,9 @@ Then reload: ${pc2.cyan(`source ${shell}`)}`
|
|
|
4745
4791
|
logs: { dir: ".lisa/logs", format: "text" }
|
|
4746
4792
|
};
|
|
4747
4793
|
saveConfig(cfg);
|
|
4794
|
+
if (ensureLogsGitignore(process.cwd())) {
|
|
4795
|
+
clack.log.info("Added .lisa/logs/* to .gitignore");
|
|
4796
|
+
}
|
|
4748
4797
|
clack.outro(
|
|
4749
4798
|
`${pc2.green("All set!")} Config saved to ${pc2.cyan(".lisa/config.yaml")}
|
|
4750
4799
|
Run ${pc2.bold(pc2.cyan("lisa run"))} to start resolving issues.`
|
|
@@ -29,7 +29,32 @@ function formatElapsed(ms) {
|
|
|
29
29
|
if (minutes > 0) return `${minutes}m ${remainingSeconds}s`;
|
|
30
30
|
return `${seconds}s`;
|
|
31
31
|
}
|
|
32
|
-
function
|
|
32
|
+
function wrapTitle(title, maxWidth) {
|
|
33
|
+
if (title.length <= maxWidth) return [title, ""];
|
|
34
|
+
const words = title.split(" ");
|
|
35
|
+
let line1 = "";
|
|
36
|
+
let i = 0;
|
|
37
|
+
for (; i < words.length; i++) {
|
|
38
|
+
const word = words[i] ?? "";
|
|
39
|
+
const candidate = line1 ? `${line1} ${word}` : word;
|
|
40
|
+
if (candidate.length > maxWidth) break;
|
|
41
|
+
line1 = candidate;
|
|
42
|
+
}
|
|
43
|
+
if (!line1) {
|
|
44
|
+
line1 = title.slice(0, maxWidth);
|
|
45
|
+
const rest = title.slice(maxWidth);
|
|
46
|
+
const line22 = rest.length > maxWidth ? `${rest.slice(0, maxWidth - 1)}\u2026` : rest;
|
|
47
|
+
return [line1, line22];
|
|
48
|
+
}
|
|
49
|
+
const remaining = words.slice(i).join(" ");
|
|
50
|
+
const line2 = remaining.length > maxWidth ? `${remaining.slice(0, maxWidth - 1)}\u2026` : remaining;
|
|
51
|
+
return [line1, line2];
|
|
52
|
+
}
|
|
53
|
+
function Card({
|
|
54
|
+
card,
|
|
55
|
+
isSelected = false,
|
|
56
|
+
paused = false
|
|
57
|
+
}) {
|
|
33
58
|
const [now, setNow] = useState(Date.now());
|
|
34
59
|
useEffect(() => {
|
|
35
60
|
if (card.column !== "in_progress") return;
|
|
@@ -53,7 +78,9 @@ function Card({ card, isSelected = false }) {
|
|
|
53
78
|
}
|
|
54
79
|
const selectionBar = isSelected ? "\u2590" : " ";
|
|
55
80
|
const selectionColor = isSelected ? "yellow" : "white";
|
|
56
|
-
const
|
|
81
|
+
const CARD_TITLE_WIDTH = 28;
|
|
82
|
+
const [titleLine1, titleLine2] = wrapTitle(card.title, CARD_TITLE_WIDTH);
|
|
83
|
+
const isPausedInProgress = paused && card.column === "in_progress";
|
|
57
84
|
return /* @__PURE__ */ jsxs(
|
|
58
85
|
Box,
|
|
59
86
|
{
|
|
@@ -61,7 +88,7 @@ function Card({ card, isSelected = false }) {
|
|
|
61
88
|
paddingX: 0,
|
|
62
89
|
marginBottom: 0,
|
|
63
90
|
borderStyle: "single",
|
|
64
|
-
borderColor: card.hasError ? "red" : isSelected ? "yellow" : "gray",
|
|
91
|
+
borderColor: card.hasError ? "red" : isPausedInProgress ? "gray" : isSelected ? "yellow" : "gray",
|
|
65
92
|
children: [
|
|
66
93
|
/* @__PURE__ */ jsx(Text, { color: selectionColor, children: selectionBar }),
|
|
67
94
|
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
|
|
@@ -69,10 +96,11 @@ function Card({ card, isSelected = false }) {
|
|
|
69
96
|
/* @__PURE__ */ jsx(Text, { color: "yellow", bold: isSelected, children: card.id }),
|
|
70
97
|
/* @__PURE__ */ jsx(Text, { color: statusColor, children: statusGlyph })
|
|
71
98
|
] }),
|
|
72
|
-
/* @__PURE__ */ jsx(Text, { bold: isSelected, dimColor: !isSelected, children:
|
|
99
|
+
/* @__PURE__ */ jsx(Text, { bold: isSelected, dimColor: !isSelected, children: titleLine1 }),
|
|
100
|
+
/* @__PURE__ */ jsx(Text, { bold: isSelected, dimColor: !isSelected, children: titleLine2 }),
|
|
73
101
|
card.column === "in_progress" && card.startedAt !== void 0 && /* @__PURE__ */ jsxs(Box, { flexDirection: "row", marginTop: 0, children: [
|
|
74
|
-
/* @__PURE__ */ jsx(Text, { color: "yellow", children: /* @__PURE__ */ jsx(Spinner, { type: "dots" }) }),
|
|
75
|
-
/* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
|
|
102
|
+
isPausedInProgress ? /* @__PURE__ */ jsx(Text, { color: "gray", children: "\u23F8" }) : /* @__PURE__ */ jsx(Text, { color: "yellow", children: /* @__PURE__ */ jsx(Spinner, { type: "dots" }) }),
|
|
103
|
+
/* @__PURE__ */ jsxs(Text, { color: isPausedInProgress ? "gray" : "yellow", dimColor: isPausedInProgress, children: [
|
|
76
104
|
" ",
|
|
77
105
|
formatElapsed(now - card.startedAt)
|
|
78
106
|
] })
|
|
@@ -90,9 +118,15 @@ function Card({ card, isSelected = false }) {
|
|
|
90
118
|
|
|
91
119
|
// src/ui/column.tsx
|
|
92
120
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
93
|
-
var CARD_HEIGHT =
|
|
121
|
+
var CARD_HEIGHT = 6;
|
|
94
122
|
var HEADER_ROWS = 4;
|
|
95
|
-
function Column({
|
|
123
|
+
function Column({
|
|
124
|
+
label,
|
|
125
|
+
cards,
|
|
126
|
+
isFocused = false,
|
|
127
|
+
activeCardIndex = 0,
|
|
128
|
+
paused = false
|
|
129
|
+
}) {
|
|
96
130
|
const terminalRows = process.stdout.rows ?? 24;
|
|
97
131
|
const visibleCount = Math.max(1, Math.floor((terminalRows - HEADER_ROWS) / CARD_HEIGHT));
|
|
98
132
|
let scrollOffset = 0;
|
|
@@ -134,7 +168,7 @@ function Column({ label, cards, isFocused = false, activeCardIndex = 0 }) {
|
|
|
134
168
|
visibleCards.map((card, idx) => {
|
|
135
169
|
const absoluteIdx = scrollOffset + idx;
|
|
136
170
|
const isSelected = isFocused && absoluteIdx === activeCardIndex;
|
|
137
|
-
return /* @__PURE__ */ jsx2(Card, { card, isSelected }, card.id);
|
|
171
|
+
return /* @__PURE__ */ jsx2(Card, { card, isSelected, paused }, card.id);
|
|
138
172
|
}),
|
|
139
173
|
cards.length === 0 && /* @__PURE__ */ jsx2(Box2, { justifyContent: "center", paddingY: 1, children: /* @__PURE__ */ jsx2(Text2, { color: "gray", dimColor: true, children: "\u2014 empty \u2014" }) }),
|
|
140
174
|
hiddenBelow > 0 && /* @__PURE__ */ jsx2(Box2, { justifyContent: "center", children: /* @__PURE__ */ jsx2(Text2, { color: "yellow", dimColor: true, children: `\u2193 ${hiddenBelow} more` }) })
|
|
@@ -158,7 +192,8 @@ function Board({
|
|
|
158
192
|
isEmpty,
|
|
159
193
|
workComplete,
|
|
160
194
|
activeColIndex = 0,
|
|
161
|
-
activeCardIndex = 0
|
|
195
|
+
activeCardIndex = 0,
|
|
196
|
+
paused = false
|
|
162
197
|
}) {
|
|
163
198
|
const backlog = cards.filter((c) => c.column === "backlog");
|
|
164
199
|
const inProgress = cards.filter((c) => c.column === "in_progress");
|
|
@@ -197,7 +232,8 @@ function Board({
|
|
|
197
232
|
label: labels.backlog,
|
|
198
233
|
cards: backlog,
|
|
199
234
|
isFocused: activeColIndex === 0,
|
|
200
|
-
activeCardIndex: activeColIndex === 0 ? activeCardIndex : 0
|
|
235
|
+
activeCardIndex: activeColIndex === 0 ? activeCardIndex : 0,
|
|
236
|
+
paused
|
|
201
237
|
}
|
|
202
238
|
),
|
|
203
239
|
/* @__PURE__ */ jsx3(
|
|
@@ -206,7 +242,8 @@ function Board({
|
|
|
206
242
|
label: labels.inProgress,
|
|
207
243
|
cards: inProgress,
|
|
208
244
|
isFocused: activeColIndex === 1,
|
|
209
|
-
activeCardIndex: activeColIndex === 1 ? activeCardIndex : 0
|
|
245
|
+
activeCardIndex: activeColIndex === 1 ? activeCardIndex : 0,
|
|
246
|
+
paused
|
|
210
247
|
}
|
|
211
248
|
),
|
|
212
249
|
/* @__PURE__ */ jsx3(
|
|
@@ -215,7 +252,8 @@ function Board({
|
|
|
215
252
|
label: labels.done,
|
|
216
253
|
cards: done,
|
|
217
254
|
isFocused: activeColIndex === 2,
|
|
218
|
-
activeCardIndex: activeColIndex === 2 ? activeCardIndex : 0
|
|
255
|
+
activeCardIndex: activeColIndex === 2 ? activeCardIndex : 0,
|
|
256
|
+
paused
|
|
219
257
|
}
|
|
220
258
|
)
|
|
221
259
|
] })
|
|
@@ -234,6 +272,19 @@ function formatElapsed2(ms) {
|
|
|
234
272
|
if (minutes > 0) return `${minutes}m ${remainingSeconds}s`;
|
|
235
273
|
return `${seconds}s`;
|
|
236
274
|
}
|
|
275
|
+
function hyperlink(url, text) {
|
|
276
|
+
return `\x1B]8;;${url}\x07${text}\x1B]8;;\x07`;
|
|
277
|
+
}
|
|
278
|
+
function logLineColor(line) {
|
|
279
|
+
if (/\berror\b|✖/i.test(line)) return "red";
|
|
280
|
+
if (/\bwarn(ing)?\b/i.test(line)) return "yellow";
|
|
281
|
+
if (/✔|\bsuccess\b/i.test(line)) return "green";
|
|
282
|
+
return "white";
|
|
283
|
+
}
|
|
284
|
+
function scrollBar(pct, width = 8) {
|
|
285
|
+
const filled = Math.round(pct / 100 * width);
|
|
286
|
+
return "\u2593".repeat(filled) + "\u2591".repeat(width - filled);
|
|
287
|
+
}
|
|
237
288
|
function statusLabel(column, hasError) {
|
|
238
289
|
if (hasError) return { text: "FAILED", color: "red" };
|
|
239
290
|
if (column === "in_progress") return { text: "IN PROGRESS", color: "yellow" };
|
|
@@ -292,7 +343,7 @@ function IssueDetail({ card, onBack }) {
|
|
|
292
343
|
const separatorInner = Math.max(0, terminalCols - SIDEBAR_TOTAL_WIDTH - 4);
|
|
293
344
|
const separator = `\u2560${"\u2550".repeat(Math.max(0, separatorInner - 2))}\u2563`;
|
|
294
345
|
const totalLines = lines.length;
|
|
295
|
-
const
|
|
346
|
+
const scrollPctNum = totalLines <= bodyRows ? 100 : Math.round((startLine + bodyRows) / totalLines * 100);
|
|
296
347
|
return /* @__PURE__ */ jsxs4(
|
|
297
348
|
Box4,
|
|
298
349
|
{
|
|
@@ -323,21 +374,24 @@ function IssueDetail({ card, onBack }) {
|
|
|
323
374
|
/* @__PURE__ */ jsx4(Box4, { marginTop: 0, children: /* @__PURE__ */ jsx4(Text4, { color: "white", bold: true, children: card.title }) }),
|
|
324
375
|
card.prUrl !== void 0 && card.prUrl.length > 0 && /* @__PURE__ */ jsxs4(Box4, { marginTop: 0, children: [
|
|
325
376
|
/* @__PURE__ */ jsx4(Text4, { color: "yellow", dimColor: true, children: "PR: " }),
|
|
326
|
-
/* @__PURE__ */ jsx4(Text4, { color: "yellow", children: card.prUrl })
|
|
377
|
+
/* @__PURE__ */ jsx4(Text4, { color: "yellow", children: hyperlink(card.prUrl, card.prUrl) })
|
|
327
378
|
] }),
|
|
328
379
|
/* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsx4(Text4, { color: "yellow", dimColor: true, children: separator }) }),
|
|
329
380
|
/* @__PURE__ */ jsxs4(Box4, { flexDirection: "row", justifyContent: "space-between", children: [
|
|
330
381
|
/* @__PURE__ */ jsx4(Text4, { color: "gray", dimColor: true, children: "PROVIDER OUTPUT" }),
|
|
331
|
-
userScrolled && /* @__PURE__ */ jsx4(Text4, { color: "yellow", dimColor: true, children:
|
|
382
|
+
userScrolled && /* @__PURE__ */ jsx4(Text4, { color: "yellow", dimColor: true, children: scrollBar(scrollPctNum) }),
|
|
332
383
|
!userScrolled && totalLines > bodyRows && /* @__PURE__ */ jsx4(Text4, { color: "gray", dimColor: true, children: "auto-scroll" })
|
|
333
384
|
] }),
|
|
334
385
|
/* @__PURE__ */ jsx4(Box4, { flexGrow: 1, flexDirection: "column", overflow: "hidden", children: card.outputLog.length === 0 ? /* @__PURE__ */ jsxs4(Box4, { flexDirection: "row", marginTop: 1, children: [
|
|
335
386
|
/* @__PURE__ */ jsx4(Text4, { color: "yellow", children: /* @__PURE__ */ jsx4(Spinner2, { type: "dots" }) }),
|
|
336
387
|
/* @__PURE__ */ jsx4(Text4, { color: "gray", dimColor: true, children: " Waiting for provider output..." })
|
|
337
|
-
] }) : visibleLines.map((line, i) =>
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
388
|
+
] }) : visibleLines.map((line, i) => {
|
|
389
|
+
const color = logLineColor(line);
|
|
390
|
+
return (
|
|
391
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: log lines have no stable key
|
|
392
|
+
/* @__PURE__ */ jsx4(Text4, { color, dimColor: color === "white", children: line }, i)
|
|
393
|
+
);
|
|
394
|
+
}) })
|
|
341
395
|
]
|
|
342
396
|
}
|
|
343
397
|
);
|
|
@@ -402,7 +456,7 @@ function Sidebar({ provider, source, cwd, activeView, paused = false }) {
|
|
|
402
456
|
/* @__PURE__ */ jsx5(Box5, { flexGrow: 1 }),
|
|
403
457
|
/* @__PURE__ */ jsx5(Text5, { color: "yellow", children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
|
|
404
458
|
activeView === "board" ? /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, flexDirection: "column", children: [
|
|
405
|
-
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "[
|
|
459
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "[\u2190\u2192] columns " }),
|
|
406
460
|
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "[\u2191\u2193] navigate " }),
|
|
407
461
|
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "[\u21B5] view detail " }),
|
|
408
462
|
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: paused ? "[p] resume " : "[p] pause " }),
|
|
@@ -425,6 +479,7 @@ function KanbanApp({ config }) {
|
|
|
425
479
|
const [activeColIndex, setActiveColIndex] = useState3(0);
|
|
426
480
|
const [activeCardIndex, setActiveCardIndex] = useState3(0);
|
|
427
481
|
const [paused, setPaused] = useState3(false);
|
|
482
|
+
const [selectedCardId, setSelectedCardId] = useState3(null);
|
|
428
483
|
useEffect3(() => {
|
|
429
484
|
const onExit = () => exit();
|
|
430
485
|
kanbanEmitter.on("tui:exit", onExit);
|
|
@@ -447,13 +502,34 @@ function KanbanApp({ config }) {
|
|
|
447
502
|
}, [inProgress, workComplete]);
|
|
448
503
|
const done = cards.filter((c) => c.column === "done");
|
|
449
504
|
const columnCards = [backlog, inProgress, done];
|
|
505
|
+
useEffect3(() => {
|
|
506
|
+
if (!selectedCardId || activeView !== "detail") return;
|
|
507
|
+
const card = cards.find((c) => c.id === selectedCardId);
|
|
508
|
+
if (!card) return;
|
|
509
|
+
const colIndexMap = {
|
|
510
|
+
backlog: 0,
|
|
511
|
+
in_progress: 1,
|
|
512
|
+
done: 2
|
|
513
|
+
};
|
|
514
|
+
const newColIndex = colIndexMap[card.column];
|
|
515
|
+
const colCards = cards.filter((c) => c.column === card.column);
|
|
516
|
+
const newCardIndex = Math.max(
|
|
517
|
+
0,
|
|
518
|
+
colCards.findIndex((c) => c.id === selectedCardId)
|
|
519
|
+
);
|
|
520
|
+
setActiveColIndex(newColIndex);
|
|
521
|
+
setActiveCardIndex(newCardIndex);
|
|
522
|
+
}, [cards, selectedCardId, activeView]);
|
|
450
523
|
useInput2((input, key) => {
|
|
451
524
|
if (input === "q") {
|
|
452
525
|
process.emit("SIGINT");
|
|
453
526
|
return;
|
|
454
527
|
}
|
|
455
528
|
if (activeView === "detail") {
|
|
456
|
-
if (key.escape)
|
|
529
|
+
if (key.escape) {
|
|
530
|
+
setActiveView("board");
|
|
531
|
+
setSelectedCardId(null);
|
|
532
|
+
}
|
|
457
533
|
return;
|
|
458
534
|
}
|
|
459
535
|
if (input === "p") {
|
|
@@ -462,14 +538,14 @@ function KanbanApp({ config }) {
|
|
|
462
538
|
kanbanEmitter.emit(next ? "loop:pause" : "loop:resume");
|
|
463
539
|
return;
|
|
464
540
|
}
|
|
465
|
-
if (key.
|
|
541
|
+
if (key.rightArrow) {
|
|
466
542
|
const nextCol = (activeColIndex + 1) % 3;
|
|
467
543
|
setActiveColIndex(nextCol);
|
|
468
544
|
const colLen = columnCards[nextCol]?.length ?? 0;
|
|
469
545
|
setActiveCardIndex(Math.min(activeCardIndex, Math.max(0, colLen - 1)));
|
|
470
546
|
return;
|
|
471
547
|
}
|
|
472
|
-
if (key.
|
|
548
|
+
if (key.leftArrow) {
|
|
473
549
|
const prevCol = (activeColIndex + 2) % 3;
|
|
474
550
|
setActiveColIndex(prevCol);
|
|
475
551
|
const colLen = columnCards[prevCol]?.length ?? 0;
|
|
@@ -486,8 +562,11 @@ function KanbanApp({ config }) {
|
|
|
486
562
|
return;
|
|
487
563
|
}
|
|
488
564
|
if (key.return) {
|
|
489
|
-
const
|
|
490
|
-
if (
|
|
565
|
+
const card = columnCards[activeColIndex]?.[activeCardIndex];
|
|
566
|
+
if (card) {
|
|
567
|
+
setSelectedCardId(card.id);
|
|
568
|
+
setActiveView("detail");
|
|
569
|
+
}
|
|
491
570
|
}
|
|
492
571
|
});
|
|
493
572
|
const labels = {
|
|
@@ -495,7 +574,7 @@ function KanbanApp({ config }) {
|
|
|
495
574
|
inProgress: config.source_config.in_progress,
|
|
496
575
|
done: config.source_config.done
|
|
497
576
|
};
|
|
498
|
-
const selectedCard = activeView === "detail" ?
|
|
577
|
+
const selectedCard = activeView === "detail" && selectedCardId ? cards.find((c) => c.id === selectedCardId) ?? null : null;
|
|
499
578
|
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "row", height: process.stdout.rows, children: [
|
|
500
579
|
/* @__PURE__ */ jsx6(
|
|
501
580
|
Sidebar,
|
|
@@ -515,9 +594,19 @@ function KanbanApp({ config }) {
|
|
|
515
594
|
isEmpty,
|
|
516
595
|
workComplete,
|
|
517
596
|
activeColIndex,
|
|
518
|
-
activeCardIndex
|
|
597
|
+
activeCardIndex,
|
|
598
|
+
paused
|
|
599
|
+
}
|
|
600
|
+
) : /* @__PURE__ */ jsx6(
|
|
601
|
+
IssueDetail,
|
|
602
|
+
{
|
|
603
|
+
card: selectedCard,
|
|
604
|
+
onBack: () => {
|
|
605
|
+
setActiveView("board");
|
|
606
|
+
setSelectedCardId(null);
|
|
607
|
+
}
|
|
519
608
|
}
|
|
520
|
-
)
|
|
609
|
+
)
|
|
521
610
|
] });
|
|
522
611
|
}
|
|
523
612
|
export {
|
package/package.json
CHANGED
|
@@ -1,62 +1,60 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
}
|
|
2
|
+
"name": "@tarcisiopgs/lisa",
|
|
3
|
+
"version": "1.8.2",
|
|
4
|
+
"description": "Autonomous issue resolver",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"lisa": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@clack/prompts": "^1.0.1",
|
|
12
|
+
"citty": "^0.2.1",
|
|
13
|
+
"execa": "^9.6.1",
|
|
14
|
+
"ink": "^6.8.0",
|
|
15
|
+
"ink-spinner": "^5.0.0",
|
|
16
|
+
"picocolors": "^1.1.1",
|
|
17
|
+
"react": "^19.2.4",
|
|
18
|
+
"yaml": "^2.8.2"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@biomejs/biome": "^2.4.3",
|
|
22
|
+
"@types/node": "^22.13.4",
|
|
23
|
+
"@types/react": "^19.2.14",
|
|
24
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
25
|
+
"concurrently": "^9.2.1",
|
|
26
|
+
"husky": "^9.1.7",
|
|
27
|
+
"lint-staged": "^16.2.7",
|
|
28
|
+
"tsup": "^8.4.0",
|
|
29
|
+
"tsx": "^4.19.3",
|
|
30
|
+
"typescript": "^5.9.3",
|
|
31
|
+
"vitest": "^4.0.18"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/tarcisiopgs/lisa.git"
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"dist"
|
|
42
|
+
],
|
|
43
|
+
"lint-staged": {
|
|
44
|
+
"*.{ts,tsx}": [
|
|
45
|
+
"biome check --write"
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsup",
|
|
50
|
+
"dev": "tsx src/index.ts",
|
|
51
|
+
"check": "biome check src/",
|
|
52
|
+
"format": "biome format --write src/",
|
|
53
|
+
"lint": "biome lint src/",
|
|
54
|
+
"typecheck": "tsc --noEmit",
|
|
55
|
+
"test": "vitest run",
|
|
56
|
+
"test:watch": "vitest",
|
|
57
|
+
"test:coverage": "vitest run --coverage",
|
|
58
|
+
"ci": "concurrently -n lint,typecheck,test \"pnpm lint\" \"pnpm typecheck\" \"pnpm test\""
|
|
59
|
+
}
|
|
60
|
+
}
|