@tarcisiopgs/lisa 1.20.1 → 1.21.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/README.md CHANGED
@@ -15,258 +15,72 @@
15
15
  <img src="assets/demo.gif" alt="Lisa demo" />
16
16
  </p>
17
17
 
18
- <p align="center">
19
- Lisa is an autonomous issue resolver that turns your backlog into pull requests — no babysitting required.
20
- </p>
21
-
22
- ---
18
+ Lisa connects your issue tracker to an AI coding agent and delivers pull requests — autonomously. Tag an issue with a label, Lisa picks it up, implements it, opens a PR, and updates your board. No babysitting.
23
19
 
24
20
  ## Quickstart
25
21
 
26
22
  ```bash
27
23
  npm install -g @tarcisiopgs/lisa
28
- lisa init
24
+ lisa init # interactive setup wizard
29
25
  lisa run
30
26
  ```
31
27
 
32
- That's it. Lisa picks up the next labeled issue, implements it, pushes a branch, opens a pull request, and moves the ticket to "In Review" — all without you touching it.
33
-
34
- ## Try it safely first
35
-
36
- Before letting Lisa touch real issues, verify your configuration with `--dry-run`. No issues will be fetched, no code will be written, no PRs will be created.
37
-
38
- ```bash
39
- lisa run --once --dry-run
40
- ```
41
-
42
- Example output:
28
+ ## How It Works
43
29
 
44
30
  ```
45
- [dry-run] Would fetch issue from linear (Engineering/Web App)
46
- [dry-run] Workflow mode: worktree
47
- [dry-run] Models priority: claude/claude-sonnet-4-6
48
- [dry-run] Then implement, push, create PR, and update issue status
31
+ Fetch issue Activate Build context → Implement → Push → Open PR → Update board → Next
49
32
  ```
50
33
 
51
- If the output looks correct, you're ready to run Lisa for real.
52
-
53
- ## What Lisa Does
34
+ Lisa picks the highest-priority labeled issue, moves it to "In Progress", sends a structured prompt to the AI agent, and monitors execution. The agent works in an isolated git worktree, implements the change, runs tests, and commits. Lisa pushes, opens a PR, moves the ticket to "In Review", and picks up the next one.
54
35
 
55
- Lisa follows a deterministic pipeline:
36
+ If something fails — pre-push hooks, quota limits, stuck processes — Lisa handles it: retries with error context, falls back to the next model, or kills and moves on.
56
37
 
57
- ```
58
- ┌─────────┐ ┌──────────┐ ┌─────────┐ ┌───────────┐ ┌──────────┐ ┌────┐ ┌────────┐
59
- │ Fetch │───▶│ Activate │───▶│ Context │───▶│ Implement │───▶│ Validate │───▶│ PR │───▶│ Update │
60
- └─────────┘ └──────────┘ └─────────┘ └───────────┘ └──────────┘ └────┘ └────────┘
61
- ```
38
+ ## Features
62
39
 
63
- 1. **Fetch** — Pulls the next issue from Linear, Trello, Plane, Shortcut, GitLab Issues, GitHub Issues, or Jira matching the configured label, team, and project. Issues are sorted by priority. Blocked issues are skipped.
64
- 2. **Activate** — Moves the issue to `in_progress` so your team knows it's being worked on.
65
- 3. **Context** — On first run, Lisa analyzes your codebase and generates `.lisa/context.md` — a concise project guide (stack, conventions, constraints) injected into every agent prompt. Skipped automatically if the file already exists.
66
- 4. **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.
67
- 5. **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.
68
- 6. **PR** — Pushes the branch and creates a pull request referencing the original issue.
69
- 7. **Update** — Moves the issue to the `done` status and removes the pickup label.
70
- 8. **Next** — Picks the next issue. When there are no more matching issues, Lisa stops.
71
-
72
- ### What makes it different
73
-
74
- - **Deterministic, not hopeful** — Each issue follows a structured pipeline with clear stages. No blind retries, no speculative loops.
75
- - **Token efficiency** — Each issue gets one focused prompt. No wasted retries, no idle polling.
76
- - **Multi-repo awareness** — Plans across multiple repos, executes in the correct order, creates one PR per repo.
77
- - **Model fallback** — Configure a chain of models. Transient errors (429, quota, timeout) trigger the next model automatically.
78
- - **Workflow integration** — Issues move through your board in real time. Your team always knows what's being worked on.
79
- - **Self-healing** — Orphan issues stuck in "In Progress" are recovered on startup. Pre-push failures trigger the agent to fix and retry.
80
- - **Guardrails** — Past failures are logged and injected into future prompts so the agent avoids repeating mistakes.
40
+ - **7 issue trackers** — Linear, GitHub Issues, GitLab Issues, Jira, Trello, Plane, Shortcut
41
+ - **8 AI agents** — Claude Code, Gemini CLI, GitHub Copilot CLI, Cursor Agent, Aider, Goose, OpenCode, Codex
42
+ - **Concurrent execution** — process multiple issues in parallel, each in its own worktree
43
+ - **Multi-repo** — plans across repos, creates one PR per repo in the correct order
44
+ - **Model fallback** — chain models; transient errors (429, quota, timeout) auto-switch to the next
45
+ - **Real-time TUI** — Kanban board with live provider output, keyboard controls, PR merge detection
46
+ - **Self-healing** — orphan recovery on startup, push failure retry, stuck process detection
47
+ - **Guardrails** — past failures are injected into future prompts to avoid repeating mistakes
48
+ - **Project context** — auto-generates `.lisa/context.md` with your stack, conventions, and constraints
81
49
 
82
50
  ## Providers
83
51
 
84
- | Provider | Key | Command |
85
- |----------|-----|---------|
86
- | Claude Code | `claude` | `claude` |
87
- | Gemini CLI | `gemini` | `gemini` |
88
- | OpenCode | `opencode` | `opencode` |
89
- | GitHub Copilot CLI | `copilot` | `copilot` |
90
- | Cursor Agent | `cursor` | `agent` / `cursor-agent` |
91
- | Goose | `goose` | `goose` |
92
- | Aider | `aider` | `aider` |
93
- | OpenAI Codex | `codex` | `codex` |
52
+ | Provider | Key | Provider | Key |
53
+ |----------|-----|----------|-----|
54
+ | Claude Code | `claude` | Cursor Agent | `cursor` |
55
+ | Gemini CLI | `gemini` | Goose | `goose` |
56
+ | GitHub Copilot CLI | `copilot` | Aider | `aider` |
57
+ | OpenCode | `opencode` | OpenAI Codex | `codex` |
94
58
 
95
- At least one provider must be installed and available in your PATH.
96
-
97
- ### Fallback Chain
98
-
99
- Configure multiple models — Lisa tries each in order. Transient errors (429, quota, timeout, network) trigger the next model; non-transient errors stop the chain.
59
+ Configure a fallback chain:
100
60
 
101
61
  ```yaml
102
62
  provider: claude
103
63
  models:
104
64
  - claude-sonnet-4-6 # primary
105
- - claude-opus-4-6 # fallback 1
106
- - claude-haiku-4-5 # fallback 2
107
- ```
108
-
109
- When the TUI is active, the sidebar shows a **Model Queue** — the currently active model is highlighted with a bullet (●).
110
-
111
- ## Install
112
-
113
- ```bash
114
- npm install -g @tarcisiopgs/lisa
115
- ```
116
-
117
- ## Environment Variables
118
-
119
- ```bash
120
- # Required for PR creation (at least one)
121
- export GITHUB_TOKEN="" # or have `gh` CLI authenticated
122
- export GITLAB_TOKEN="" # platform: gitlab
123
- export BITBUCKET_TOKEN="" # platform: bitbucket
124
- export BITBUCKET_USERNAME=""
125
-
126
- # Required when source = linear
127
- export LINEAR_API_KEY=""
128
-
129
- # Required when source = trello
130
- export TRELLO_API_KEY=""
131
- export TRELLO_TOKEN=""
132
-
133
- # Required when source = plane
134
- export PLANE_API_TOKEN=""
135
- export PLANE_BASE_URL="" # optional; defaults to https://api.plane.so
136
-
137
- # Required when source = shortcut
138
- export SHORTCUT_API_TOKEN=""
139
-
140
- # Required when source = gitlab-issues
141
- export GITLAB_TOKEN=""
142
- export GITLAB_BASE_URL="" # optional; defaults to https://gitlab.com
143
-
144
- # Required when source = github-issues
145
- export GITHUB_TOKEN="" # same token used for PR creation
146
-
147
- # Required when source = jira
148
- export JIRA_BASE_URL="" # e.g. https://yourcompany.atlassian.net
149
- export JIRA_EMAIL="" # Atlassian account email
150
- export JIRA_API_TOKEN="" # Atlassian API token
151
-
152
- # Required when provider = aider (one of)
153
- export GEMINI_API_KEY=""
154
- export OPENAI_API_KEY=""
155
- export ANTHROPIC_API_KEY=""
65
+ - claude-opus-4-6 # fallback
156
66
  ```
157
67
 
158
68
  ## Commands
159
69
 
160
- | Command | Description |
161
- |---------|-------------|
162
- | `lisa run` | Run the agent loop |
163
- | `lisa run --once` | Process a single issue |
164
- | `lisa run --once --dry-run` | **Recommended first step** — preview config without executing |
165
- | `lisa run --watch` | Keep running after queue empties — poll for new issues every 60s |
166
- | `lisa run --issue ID` | Process a specific issue by identifier or URL |
167
- | `lisa run --concurrency N` | Process N issues in parallel (each in its own worktree) |
168
- | `lisa run --limit N` | Stop after processing N issues |
169
- | `lisa run --provider NAME` | Override AI provider |
170
- | `lisa run --source NAME` | Override issue source |
171
- | `lisa run --label NAME` | Override label filter |
172
- | `lisa run --platform NAME` | Override PR platform (`cli`, `token`, `gitlab`, `bitbucket`) |
173
- | `lisa run --no-bell` | Disable terminal bell on completion/failure |
174
- | `lisa run --demo` | Run an animated demo of the Kanban UI with fake issues |
175
- | `lisa init` | Create `.lisa/config.yaml` interactively |
176
- | `lisa config --show` | Print current config |
177
- | `lisa status` | Show session stats and log file location |
178
- | `lisa context` | Show the current `.lisa/context.md` for all repos |
179
- | `lisa context refresh` | Regenerate `.lisa/context.md` |
180
- | `lisa context refresh --repo NAME` | Regenerate context for a specific repo (multi-repo only) |
181
- | `lisa feedback --pr URL` | Inject PR review feedback from a closed PR into guardrails |
182
-
183
- ## TUI
184
-
185
- When running in an interactive terminal, `lisa run` renders a real-time Kanban board:
186
-
187
- ```
188
- ┌──────────────────────────┐ ┌───────────────────────────┐ ┌───────────────────────────┐
189
- │ ▶ BACKLOG [3] │ │ ▶ IN PROGRESS [1] │ │ ▶ IN REVIEW [2] │
190
- │ │ │ │ │ │
191
- │ ┌────────────────────┐ │ │ ┌─────────────────────┐ │ │ ┌─────────────────────┐ │
192
- │ │ ENG-42 │ │ │ │ ● ENG-38 │ │ │ │ ✓ ENG-35 │ │
193
- │ │ Add dark mode │ │ │ │ Fix login redirect │ │ │ │ Update dependencies │ │
194
- │ │ ready │ │ │ │ ~1m running │ │ │ │ PR created │ │
195
- │ └────────────────────┘ │ │ └─────────────────────┘ │ │ └─────────────────────┘ │
196
- └──────────────────────────┘ └───────────────────────────┘ └───────────────────────────┘
197
- ```
198
-
199
- ### Keyboard shortcuts
200
-
201
- | Key | Action |
202
- |-----|--------|
203
- | `←` `→` | Switch columns |
204
- | `↑` `↓` | Navigate cards within a column |
205
- | `↵` | Open issue detail view (streams provider output) |
206
- | `p` | Pause / resume (suspends the active provider process) |
207
- | `k` | Kill the selected in-progress issue (marks it for retry) |
208
- | `s` | Skip the selected in-progress issue (moves back to Backlog) |
209
- | `q` | Quit |
210
- | `Esc` | Return to board from detail view |
211
- | `o` | Open PR URL in browser (detail view, when PR exists) |
212
-
213
- The terminal tab title updates in real time: it shows a spinner with the active issue ID while work is in progress, and a checkmark when done.
214
-
215
- ### State Persistence
216
-
217
- The Kanban board state is saved automatically to the OS cache directory and restored when you restart `lisa run`. Cards survive process restarts — issues that completed remain visible in "Done", and issues that were mid-flight are correctly re-queued as Backlog.
218
-
219
- ### PR Merge Detection
220
-
221
- For GitHub and GitLab PRs, Lisa polls every 60 seconds and marks cards in the Done column as **merged** once the PR is merged — live, without restarting.
222
-
223
- ## Project Context
224
-
225
- On first run, Lisa automatically analyzes your codebase and writes `.lisa/context.md` — a concise document covering your stack, exact tool commands, file conventions, and constraints. This file is injected into every agent prompt so the agent works correctly from the start without rediscovering project-specific patterns.
226
-
227
- Context is generated once and reused on subsequent runs. To regenerate it (e.g. after switching stacks or adding tooling):
228
-
229
- ```bash
230
- lisa context refresh
231
- ```
232
-
233
- For multi-repo workspaces, Lisa generates a per-repo `context.md` for each repository plus a global workspace context describing cross-repo relationships and execution ordering rules.
234
-
235
70
  ```bash
236
- lisa context refresh --repo my-api # regenerate a single repo's context
71
+ lisa run # start the agent loop
72
+ lisa run --once # process a single issue
73
+ lisa run --once --dry-run # preview config without executing
74
+ lisa run --watch # poll for new issues after queue empties
75
+ lisa run --concurrency 3 # process 3 issues in parallel
76
+ lisa run --issue INT-42 # process a specific issue
77
+ lisa run --limit 5 # stop after 5 issues
78
+ lisa init # create .lisa/config.yaml interactively
79
+ lisa status # show session stats
80
+ lisa context refresh # regenerate project context
81
+ lisa feedback --pr URL # inject PR review feedback into guardrails
237
82
  ```
238
83
 
239
- If context generation fails, Lisa logs a warning and continues — it is never a blocker.
240
-
241
- ## Writing Issues
242
-
243
- Issue quality is the single biggest factor in PR quality. Lisa validates issues before accepting them — vague tickets without clear criteria are skipped and labelled `needs-spec`.
244
-
245
- Issues must contain acceptance criteria: markdown checklists (`- [ ]`) or keywords like `acceptance criteria`, `expected`, `should`.
246
-
247
- ### Example
248
-
249
- ```markdown
250
- Title: Add rate limiting to /api/users endpoint
251
-
252
- Description:
253
- Implement rate limiting on the `/api/users` endpoint to prevent abuse.
254
-
255
- Relevant files:
256
- - src/routes/users.ts
257
- - src/middleware/auth.ts
258
-
259
- Acceptance criteria:
260
- - [ ] Requests exceeding 100/min per IP return HTTP 429
261
- - [ ] Rate limit headers (X-RateLimit-Limit, X-RateLimit-Remaining) included in all responses
262
- - [ ] Rate limit state stored in Redis (use existing connection from src/lib/redis.ts)
263
- - [ ] Existing tests still pass
264
-
265
- Stack: Express, Redis
266
- ```
267
-
268
- Including **relevant files**, **technical constraints**, and **stack information** in the description leads to better results.
269
-
270
84
  ## Configuration
271
85
 
272
86
  Config lives in `.lisa/config.yaml`. Run `lisa init` to create it interactively.
@@ -274,7 +88,7 @@ Config lives in `.lisa/config.yaml`. Run `lisa init` to create it interactively.
274
88
  ```yaml
275
89
  provider: claude
276
90
  source: linear
277
- workflow: worktree
91
+ workflow: worktree # "worktree" (isolated) or "branch" (in-place)
278
92
 
279
93
  source_config:
280
94
  team: Engineering
@@ -284,156 +98,118 @@ source_config:
284
98
  in_progress: In Progress
285
99
  done: In Review
286
100
 
287
- platform: cli # "cli" (gh), "token" (GITHUB_TOKEN), "gitlab", or "bitbucket"
288
- workspace: .
101
+ platform: cli # "cli" (gh), "token" (GITHUB_TOKEN), "gitlab", "bitbucket"
289
102
  base_branch: main
103
+ ```
290
104
 
291
- repos:
292
- - name: my-api
293
- path: ./api
294
- base_branch: main
295
- match: "[API]" # route issues whose title starts with "[API]" to this repo
296
- - name: my-app
297
- path: ./app
298
- base_branch: main
299
-
300
- loop:
301
- cooldown: 10 # seconds between issues
302
- max_sessions: 0 # 0 = unlimited
303
- session_timeout: 0 # seconds per provider run (0 = disabled)
304
-
305
- # Optional — kill stuck providers
306
- overseer:
307
- enabled: true
308
- check_interval: 30 # seconds between git status checks
309
- stuck_threshold: 300 # seconds without git changes before killing
105
+ <details>
106
+ <summary><strong>Environment variables</strong></summary>
310
107
 
311
- # Optional — Docker infrastructure management (disabled by default)
312
- lifecycle:
313
- mode: auto # "auto" = start/stop services, "skip" = disabled (default), "validate-only" = fail if not running
314
- timeout: 30 # seconds per service to wait on startup
108
+ ```bash
109
+ # PR creation (at least one)
110
+ GITHUB_TOKEN="" # or use `gh` CLI
111
+ GITLAB_TOKEN="" # for platform: gitlab
112
+ BITBUCKET_TOKEN="" # for platform: bitbucket
113
+ BITBUCKET_USERNAME=""
315
114
 
316
- # Optional skip issues without acceptance criteria
317
- validation:
318
- require_acceptance_criteria: true
115
+ # Issue tracker (set the one you use)
116
+ LINEAR_API_KEY=""
117
+ TRELLO_API_KEY="" && TRELLO_TOKEN=""
118
+ PLANE_API_TOKEN=""
119
+ SHORTCUT_API_TOKEN=""
120
+ GITLAB_TOKEN=""
121
+ GITHUB_TOKEN=""
122
+ JIRA_BASE_URL="" && JIRA_EMAIL="" && JIRA_API_TOKEN=""
319
123
  ```
320
124
 
321
- ### Source-Specific Fields
125
+ </details>
126
+
127
+ <details>
128
+ <summary><strong>Source-specific configuration</strong></summary>
322
129
 
323
130
  | Field | Linear | Trello | Plane | Shortcut | GitLab Issues | GitHub Issues | Jira |
324
131
  |-------|--------|--------|-------|----------|---------------|---------------|------|
325
- | `team` | Team name | Board name | Workspace slug | Group name (optional) | Project path (`namespace/project`) or numeric ID | `owner/repo` | Project key (e.g. `ENG`) |
326
- | `project` | Project name | — | Project identifier or UUID | — | — | — | — |
327
- | `pick_from` | Status to pick issues from | List to pick cards from | State name to pick issues from | Workflow state to pick stories from | — | — | Status to pick issues from |
328
- | `label` | Label to filter issues | Label to filter cards | Label to filter issues | Label to filter stories | Label to filter issues | Label to filter issues | Label to filter issues |
329
- | `in_progress` | In-progress status | In-progress column | In-progress state name | In-progress workflow state | Label to apply on activate | Label to apply on activate | In-progress status name |
330
- | `done` | Destination status after PR | Destination column after PR | Done state name | Done workflow state | Closes the issue | Closes the issue | Destination status after PR |
331
-
332
- Plane example:
132
+ | `team` | Team name | Board name | Workspace slug | Group name | Project path | `owner/repo` | Project key |
133
+ | `project` | Project name | — | Project ID | — | — | — | — |
134
+ | `pick_from` | Status name | List name | State name | Workflow state | — | — | Status name |
135
+ | `label` | Label | Label | Label | Label | Label | Label | Label |
136
+ | `in_progress` | Status | Column | State | Workflow state | Label | Label | Status |
137
+ | `done` | Status | Column | State | Workflow state | Closes issue | Closes issue | Status |
333
138
 
334
- ```yaml
335
- source: plane
336
- source_config:
337
- team: my-workspace # workspace slug (or set PLANE_WORKSPACE env var)
338
- project: DEV # project identifier or UUID
339
- label: ready # issues with this label are picked up
340
- pick_from: Todo # state to fetch issues from
341
- in_progress: In Progress # state set when Lisa starts working
342
- done: Done # state set after PR is created
343
- ```
139
+ </details>
344
140
 
345
- Shortcut example:
141
+ <details>
142
+ <summary><strong>Multi-repo setup</strong></summary>
346
143
 
347
144
  ```yaml
348
- source: shortcut
349
- source_config:
350
- label: ready # stories with this label are picked up
351
- pick_from: Ready for Development # workflow state to fetch stories from
352
- in_progress: In Progress # state set when Lisa starts working
353
- done: Done # state set after PR is created
145
+ repos:
146
+ - name: my-api
147
+ path: ./api
148
+ base_branch: main
149
+ match: "[API]" # route issues by title prefix
150
+ - name: my-app
151
+ path: ./app
152
+ base_branch: main
354
153
  ```
355
154
 
356
- GitLab Issues example:
155
+ Lisa runs a planning phase, then executes steps sequentially — one worktree and one PR per repo.
357
156
 
358
- ```yaml
359
- source: gitlab-issues
360
- source_config:
361
- team: my-org/my-repo # namespace/project path or numeric project ID
362
- label: ready # issues with this label are picked up
363
- in_progress: in-progress # label applied when Lisa starts working
364
- done: "" # issue is closed after PR (value unused)
365
- ```
157
+ </details>
366
158
 
367
- GitHub Issues example:
159
+ <details>
160
+ <summary><strong>Advanced options</strong></summary>
368
161
 
369
162
  ```yaml
370
- source: github-issues
371
- source_config:
372
- team: my-org/my-repo # owner/repo
373
- label: ready # issues with this label are picked up
374
- in_progress: in-progress # label applied when Lisa starts working
375
- done: "" # issue is closed after PR (value unused)
376
- ```
163
+ loop:
164
+ cooldown: 10 # seconds between issues
165
+ session_timeout: 0 # max seconds per provider run (0 = disabled)
166
+
167
+ overseer:
168
+ enabled: true
169
+ check_interval: 30 # seconds between git status checks
170
+ stuck_threshold: 300 # kill provider after this many seconds without changes
377
171
 
378
- Jira example:
172
+ lifecycle:
173
+ mode: auto # "auto", "skip" (default), "validate-only"
174
+ timeout: 30
379
175
 
380
- ```yaml
381
- source: jira
382
- source_config:
383
- team: ENG # Jira project key
384
- label: lisa # label to filter issues
385
- pick_from: Backlog # status to pick issues from
386
- in_progress: In Progress # status applied when Lisa starts working
387
- done: In Review # status applied after PR is created
176
+ validation:
177
+ require_acceptance_criteria: true
388
178
  ```
389
179
 
390
- ### Workflow Modes
180
+ </details>
391
181
 
392
- **Branch** The AI agent creates a branch directly in your current checkout, implements the changes, and pushes. Simple setup, works everywhere.
182
+ ## Writing Good Issues
393
183
 
394
- **Worktree** Lisa creates an isolated [git worktree](https://git-scm.com/docs/git-worktree) for each issue under `.worktrees/`. The agent works in the worktree without touching your main checkout. After the PR is created, the worktree is cleaned up automatically. Ideal when you want to keep working in the repo while Lisa resolves issues in the background.
184
+ Issue quality = PR quality. Lisa validates issues and skips vague ones (labeling them `needs-spec`).
395
185
 
396
- **Multi-repo worktree** When multiple repos are configured, Lisa runs a two-phase flow: a planning agent produces a `.lisa-plan.json` with ordered steps, then Lisa executes each step sequentially — one worktree and one PR per repo. Cross-repo context (branch names, PR URLs) is passed to each subsequent step.
186
+ **Include:** acceptance criteria (`- [ ]` checklists), relevant file paths, technical constraints, stack info.
397
187
 
398
- When `--concurrency` is greater than 1, worktree mode is enforced automatically.
188
+ ```markdown
189
+ Title: Add rate limiting to /api/users endpoint
399
190
 
400
- ### Lifecycle Resources
191
+ Implement rate limiting on `/api/users` to prevent abuse.
401
192
 
402
- For repos that need services running during implementation (databases, dev servers):
193
+ Relevant files: src/routes/users.ts, src/middleware/auth.ts
403
194
 
404
- ```yaml
405
- repos:
406
- - name: my-api
407
- path: ./api
408
- base_branch: main
409
- lifecycle:
410
- resources:
411
- - name: postgres
412
- check_port: 5432
413
- up: "docker compose up -d postgres"
414
- down: "docker compose down"
415
- startup_timeout: 30
416
- setup:
417
- - "npx prisma generate"
418
- - "npx prisma db push"
195
+ Acceptance criteria:
196
+ - [ ] Requests exceeding 100/min per IP return HTTP 429
197
+ - [ ] Rate limit headers included in responses
198
+ - [ ] Rate limit state stored in Redis (use src/lib/redis.ts)
199
+ - [ ] Existing tests still pass
419
200
  ```
420
201
 
421
- Lisa starts resources before the agent runs, waits for the port to be ready, runs setup commands, then stops everything after the session.
422
-
423
- ### Recovery Mechanisms
424
-
425
- - **Orphan recovery** — On startup, Lisa scans for issues stuck in `in_progress` from interrupted runs and reverts them to `pick_from`.
426
- - **Push recovery** — If `git push` fails due to pre-push hooks (linter, typecheck, tests), Lisa re-invokes the agent with the error output and retries the push.
427
- - **Signal handling** — SIGINT/SIGTERM gracefully revert the active issue to its previous status before exiting.
428
- - **Guardrails** — Failed sessions are logged and injected into future prompts so the agent avoids repeating the same mistakes. Use `lisa feedback --pr URL` to manually inject review feedback from a closed PR.
429
-
430
- ### Overseer
431
-
432
- When enabled, the overseer periodically checks `git status` in the working directory. If no changes are detected within `stuck_threshold` seconds, the provider process is killed and the error is eligible for fallback to the next model.
202
+ ## TUI
433
203
 
434
- ### Auto-Detection
204
+ The real-time Kanban board shows issue progress, streams provider output, and detects PR merges.
435
205
 
436
- Lisa auto-detects `vitest` or `jest` from `package.json` dependencies and injects the correct test command into the agent prompt. It also detects the package manager from lockfiles (`bun.lockb`/`bun.lock` → `bun`, `pnpm-lock.yaml` → `pnpm`, `yarn.lock` → `yarn`, otherwise `npm`).
206
+ | Key | Action | Key | Action |
207
+ |-----|--------|-----|--------|
208
+ | `←` `→` | Switch columns | `p` | Pause / resume provider |
209
+ | `↑` `↓` | Navigate cards | `k` | Kill current issue |
210
+ | `↵` | Open detail view | `s` | Skip current issue |
211
+ | `Esc` | Back to board | `o` | Open PR in browser |
212
+ | `q` | Quit | | |
437
213
 
438
214
  ## License
439
215
 
@@ -777,6 +777,9 @@ function useKanbanState(bellEnabled, initialCards = []) {
777
777
  (prev) => prev.map((c) => c.id === issueId ? { ...c, outputLog: c.outputLog + text } : c)
778
778
  );
779
779
  };
780
+ const onReconcileRemove = (issueId) => {
781
+ setCards((prev) => prev.filter((c) => c.id !== issueId));
782
+ };
780
783
  kanbanEmitter.on("issue:queued", onQueued);
781
784
  kanbanEmitter.on("issue:started", onStarted);
782
785
  kanbanEmitter.on("issue:done", onDone);
@@ -784,6 +787,7 @@ function useKanbanState(bellEnabled, initialCards = []) {
784
787
  kanbanEmitter.on("issue:reverted", onReverted);
785
788
  kanbanEmitter.on("issue:skipped", onSkipped);
786
789
  kanbanEmitter.on("issue:killed", onKilled);
790
+ kanbanEmitter.on("issue:reconcile-remove", onReconcileRemove);
787
791
  kanbanEmitter.on("provider:paused", onProviderPaused);
788
792
  kanbanEmitter.on("provider:resumed", onProviderResumed);
789
793
  kanbanEmitter.on("issue:log-file", onLogFile);
@@ -814,6 +818,7 @@ function useKanbanState(bellEnabled, initialCards = []) {
814
818
  kanbanEmitter.off("issue:reverted", onReverted);
815
819
  kanbanEmitter.off("issue:skipped", onSkipped);
816
820
  kanbanEmitter.off("issue:killed", onKilled);
821
+ kanbanEmitter.off("issue:reconcile-remove", onReconcileRemove);
817
822
  kanbanEmitter.off("provider:paused", onProviderPaused);
818
823
  kanbanEmitter.off("provider:resumed", onProviderResumed);
819
824
  kanbanEmitter.off("issue:log-file", onLogFile);
package/dist/index.js CHANGED
@@ -47,7 +47,7 @@ import {
47
47
  setOutputMode,
48
48
  updateNotice,
49
49
  warn
50
- } from "./chunk-HDOVPYNL.js";
50
+ } from "./chunk-XE35VI6S.js";
51
51
  import {
52
52
  notify,
53
53
  resetTitle,
@@ -406,7 +406,7 @@ import { promisify } from "util";
406
406
  var execFileAsync = promisify(execFile);
407
407
  var STUCK_MESSAGE = "\n[lisa-overseer] Provider killed: no git changes detected within the stuck threshold. Eligible for fallback.\n";
408
408
  var STALL_MESSAGE = "\n[lisa-stall] Provider killed: no output received within the stall timeout. Eligible for fallback.\n";
409
- var DEFAULT_OUTPUT_STALL_TIMEOUT = 120;
409
+ var DEFAULT_OUTPUT_STALL_TIMEOUT = 300;
410
410
  function createOutputStallDetector(proc, timeoutSeconds) {
411
411
  const timeout = timeoutSeconds ?? DEFAULT_OUTPUT_STALL_TIMEOUT;
412
412
  if (timeout <= 0) {
@@ -7045,6 +7045,17 @@ async function runLoop(config2, opts) {
7045
7045
  if (kanbanEmitter.listenerCount("issue:queued") > 0) {
7046
7046
  try {
7047
7047
  const allIssues = await source.listIssues(config2.source_config);
7048
+ const activeIssueIds = new Set(allIssues.map((i) => i.id));
7049
+ if (opts.initialCards) {
7050
+ for (const card of opts.initialCards) {
7051
+ if (card.column === "backlog" && (card.hasError || card.skipped || card.killed)) {
7052
+ if (!activeIssueIds.has(card.id)) {
7053
+ kanbanEmitter.emit("issue:reconcile-remove", card.id);
7054
+ log(`Reconciled ${card.id}: no longer in source queue \u2014 removed from kanban`);
7055
+ }
7056
+ }
7057
+ }
7058
+ }
7048
7059
  for (const issue2 of allIssues) {
7049
7060
  kanbanEmitter.emit("issue:queued", issue2);
7050
7061
  }
@@ -7180,6 +7191,10 @@ var KanbanPersistence = class {
7180
7191
  this.updateCard(issueId, { column: "backlog", startedAt: void 0, killed: true });
7181
7192
  this.scheduleFlush();
7182
7193
  });
7194
+ on("issue:reconcile-remove", (issueId) => {
7195
+ this.removeCard(issueId);
7196
+ this.scheduleFlush();
7197
+ });
7183
7198
  on("issue:log-file", (issueId, logFile) => {
7184
7199
  this.updateCard(issueId, { logFile });
7185
7200
  this.scheduleFlush();
@@ -7213,6 +7228,9 @@ var KanbanPersistence = class {
7213
7228
  if (!existsSync9(dir)) mkdirSync4(dir, { recursive: true });
7214
7229
  writeFileSync12(this.statePath, JSON.stringify(this.state));
7215
7230
  }
7231
+ removeCard(id) {
7232
+ this.state.cards = this.state.cards.filter((c) => c.id !== id);
7233
+ }
7216
7234
  upsertCard(id, title) {
7217
7235
  if (!this.state.cards.some((c) => c.id === id)) {
7218
7236
  this.state.cards.push({ id, title, column: "backlog", prUrls: [], outputLogTail: [] });
@@ -7297,7 +7315,7 @@ var run = defineCommand6({
7297
7315
  if (isTTY) {
7298
7316
  const { render } = await import("ink");
7299
7317
  const { createElement } = await import("react");
7300
- const { KanbanApp } = await import("./kanban-TOKVY5AS.js");
7318
+ const { KanbanApp } = await import("./kanban-VOOYRIWF.js");
7301
7319
  const demoConfig = {
7302
7320
  provider: "claude",
7303
7321
  source: "linear",
@@ -7370,15 +7388,17 @@ Add them to your ${shell} and run: source ${shell}`));
7370
7388
  merged.workflow = "worktree";
7371
7389
  }
7372
7390
  let onBeforeExit;
7391
+ let persistedCards;
7373
7392
  if (isTTY) {
7374
7393
  const workspace = resolve15(merged.workspace);
7375
7394
  const persistence = createKanbanPersistence(workspace);
7376
7395
  const initialCards = persistence.load();
7396
+ persistedCards = initialCards;
7377
7397
  persistence.start();
7378
7398
  onBeforeExit = () => persistence.stop();
7379
7399
  const { render } = await import("ink");
7380
7400
  const { createElement } = await import("react");
7381
- const { KanbanApp } = await import("./kanban-TOKVY5AS.js");
7401
+ const { KanbanApp } = await import("./kanban-VOOYRIWF.js");
7382
7402
  render(createElement(KanbanApp, { config: merged, initialCards }), { exitOnCtrlC: false });
7383
7403
  }
7384
7404
  await runLoop(merged, {
@@ -7388,7 +7408,8 @@ Add them to your ${shell} and run: source ${shell}`));
7388
7408
  dryRun: args["dry-run"],
7389
7409
  issueId: args.issue,
7390
7410
  concurrency,
7391
- onBeforeExit
7411
+ onBeforeExit,
7412
+ initialCards: persistedCards
7392
7413
  });
7393
7414
  }
7394
7415
  });
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  kanbanEmitter,
4
4
  useKanbanState
5
- } from "./chunk-HDOVPYNL.js";
5
+ } from "./chunk-XE35VI6S.js";
6
6
  import {
7
7
  resetTitle,
8
8
  startSpinner,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tarcisiopgs/lisa",
3
- "version": "1.20.1",
3
+ "version": "1.21.0",
4
4
  "description": "Autonomous issue resolver",
5
5
  "keywords": [
6
6
  "loop",