@tarcisiopgs/lisa 0.9.6 → 1.0.1

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.
Files changed (3) hide show
  1. package/README.md +106 -66
  2. package/dist/index.js +62 -12
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  While the Ralphs of the world flooded GitHub with mindless agent loops — brute-forcing their way through issues with no context, no workflow awareness, and no regard for the mess they leave behind — Lisa takes a different approach. She reads the issue, understands the workspace, picks the right repo, creates the branch, validates her work, and opens the PR. Then she moves on to the next one. When there's nothing left to do, she stops.
8
8
 
9
- Named after the smartest Simpson, Lisa is an autonomous issue resolver that connects your project tracker (Linear or Trello) to an AI coding agent (Claude Code, Gemini CLI, or OpenCode) and delivers pull requests via the GitHub API. No MCP servers. No prompt chains. No blind retries. Just structured, end-to-end execution.
9
+ Named after the smartest Simpson, Lisa is an autonomous issue resolver that connects your project tracker (Linear or Trello) to an AI coding agent (Claude Code, Gemini CLI, or OpenCode) and delivers pull requests via GitHub. No MCP servers. No prompt chains. No blind retries. Just structured, end-to-end execution.
10
10
 
11
11
  ## Why Lisa?
12
12
 
@@ -14,10 +14,12 @@ Most AI agent loops work like Ralph — they grab an issue, throw it at a model,
14
14
 
15
15
  Lisa is deterministic. She follows a structured pipeline with clear stages (fetch, activate, implement, validate, PR, update) and stops when the work is done. This means:
16
16
 
17
- - **Token efficiency** — Each issue gets one focused prompt with full context (description, acceptance criteria, repo conventions). No wasted retries, no speculative exploration, no idle polling burning API calls.
18
- - **Multi-repo awareness** — Lisa detects which repos the agent actually touched and creates a PR for each. No guessing, no hardcoded paths.
19
- - **Workflow integration** — Issues move through your board in real time (Todo, In Progress, In Review). Your team always knows what's being worked on.
20
- - **Predictable cost** — One issue = one agent session = one set of PRs. You can estimate cost per issue instead of hoping the loop eventually converges.
17
+ - **Token efficiency** — Each issue gets one focused prompt with full context. No wasted retries, no speculative exploration, no idle polling.
18
+ - **Multi-repo awareness** — Lisa detects which repos the agent touched and creates one PR per repo. Monorepos with multiple packages just work.
19
+ - **Model fallback** — Configure a chain of models (`claude gemini → opencode`). Transient errors (429, quota, timeout) trigger the next model; non-transient errors stop the chain.
20
+ - **Workflow integration** — Issues move through your board in real time (Backlog In Progress In Review). Your team always knows what's being worked on.
21
+ - **Self-healing** — Orphan issues (stuck in "In Progress" from interrupted runs) are automatically recovered on startup. Pre-push hook failures trigger the agent to fix and retry.
22
+ - **Guardrails** — Past failures are logged and injected into future prompts so the agent avoids repeating mistakes.
21
23
 
22
24
  ## Install
23
25
 
@@ -27,11 +29,9 @@ npm install -g @tarcisiopgs/lisa
27
29
 
28
30
  ## Environment Variables
29
31
 
30
- Lisa calls external APIs directly. Set these in your shell profile (`~/.zshrc` or `~/.bashrc`):
31
-
32
32
  ```bash
33
- # Required (always)
34
- export GITHUB_TOKEN=""
33
+ # Required (at least one)
34
+ export GITHUB_TOKEN="" # or have `gh` CLI authenticated
35
35
 
36
36
  # Required when source = linear
37
37
  export LINEAR_API_KEY=""
@@ -41,24 +41,28 @@ export TRELLO_API_KEY=""
41
41
  export TRELLO_TOKEN=""
42
42
  ```
43
43
 
44
- The CLI will warn you if any required variable is missing.
45
-
46
44
  ## Quick Start
47
45
 
48
46
  ```bash
49
47
  # Interactive setup
50
48
  lisa init
51
49
 
52
- # Run continuously
50
+ # Run continuously until all labeled issues are done
53
51
  lisa run
54
52
 
55
53
  # Single issue
56
54
  lisa run --once
57
55
 
56
+ # Specific issue by identifier or URL
57
+ lisa run --issue INT-150
58
+
59
+ # Process up to N issues
60
+ lisa run --limit 5
61
+
58
62
  # Preview without executing
59
63
  lisa run --dry-run
60
64
 
61
- # Override provider
65
+ # Override provider for a single run
62
66
  lisa run --provider gemini --once
63
67
  ```
64
68
 
@@ -68,8 +72,13 @@ lisa run --provider gemini --once
68
72
  |---------|-------------|
69
73
  | `lisa run` | Run the agent loop |
70
74
  | `lisa run --once` | Process a single issue |
75
+ | `lisa run --issue ID` | Process a specific issue by identifier or URL |
71
76
  | `lisa run --limit N` | Process up to N issues |
72
77
  | `lisa run --dry-run` | Preview without executing |
78
+ | `lisa run --provider NAME` | Override AI provider |
79
+ | `lisa run --label NAME` | Override label filter |
80
+ | `lisa run --json` | Output as JSON lines |
81
+ | `lisa run --quiet` | Suppress non-essential output |
73
82
  | `lisa config` | Interactive config wizard |
74
83
  | `lisa config --show` | Show current config |
75
84
  | `lisa config --set key=value` | Set a config value |
@@ -86,106 +95,137 @@ lisa run --provider gemini --once
86
95
 
87
96
  At least one provider must be installed and available in your PATH.
88
97
 
89
- All providers stream output to stdout and to the session log file in real time. Prompts are written to a temp file and passed via shell expansion (`$(cat file)`) to avoid argument length limits.
98
+ All providers use `child_process.spawn` with `sh -c`. Prompts are written to a temp file and passed via `$(cat file)` to avoid argument length limits. Output streams to both stdout and the session log file in real time.
90
99
 
91
- ## Workflow Modes
100
+ ### Fallback Chain
101
+
102
+ Configure multiple models in the `models` array. Lisa tries them in order — transient errors (429, quota, timeout, network) trigger the next model. Non-transient errors stop the chain immediately.
103
+
104
+ ```yaml
105
+ models:
106
+ - claude
107
+ - gemini
108
+ ```
109
+
110
+ If `models` is not set, Lisa uses the single `provider` field.
92
111
 
93
- Lisa supports two workflow modes, configured during `lisa init`:
112
+ ## Workflow Modes
94
113
 
95
- ### Branch (default)
114
+ ### Branch
96
115
 
97
116
  The AI agent creates a branch directly in your current checkout, implements the changes, and pushes. Simple setup, works everywhere.
98
117
 
99
118
  ### Worktree
100
119
 
101
- Lisa creates an isolated [git worktree](https://git-scm.com/docs/git-worktree) for each issue under `.worktrees/`. The AI agent works inside the worktree without touching your main checkout. After the PR is created, the worktree is cleaned up automatically.
120
+ Lisa creates an isolated [git worktree](https://git-scm.com/docs/git-worktree) for each issue under `.worktrees/`. The agent works inside the worktree without touching your main checkout. After the PR is created, the worktree is cleaned up automatically.
121
+
122
+ In multi-repo workspaces, the agent selects the correct repository, creates the worktree, implements, and writes a `.lisa-manifest.json` with the repo path, branch name, and PR title. Lisa reads the manifest to push and create the PR.
102
123
 
103
124
  Worktree mode is ideal when you want to keep working in the repo while Lisa resolves issues in the background.
104
125
 
105
126
  ## Configuration
106
127
 
107
- Config lives in `.lisa/config.yaml`:
128
+ Config lives in `.lisa/config.yaml`. Run `lisa init` to create it interactively.
108
129
 
109
- **Linear:**
110
130
  ```yaml
111
131
  provider: claude
132
+ models:
133
+ - claude
134
+ - gemini
112
135
  source: linear
113
- workflow: branch
136
+ workflow: worktree
114
137
 
115
138
  source_config:
116
139
  team: Engineering
117
140
  project: Web App
118
141
  label: ready
119
- pick_from: Todo
142
+ pick_from: Backlog
120
143
  in_progress: In Progress
121
144
  done: In Review
122
145
 
123
- github: cli
146
+ github: cli # "cli" (gh) or "token" (GITHUB_TOKEN)
124
147
  workspace: .
148
+ base_branch: main
149
+
125
150
  repos:
126
- - name: app
151
+ - name: my-api
152
+ path: ./api
153
+ base_branch: main
154
+ - name: my-app
127
155
  path: ./app
128
- match: "App:"
156
+ base_branch: main
129
157
 
130
158
  loop:
131
- cooldown: 10
132
- max_sessions: 0
159
+ cooldown: 10 # seconds between issues
160
+ max_sessions: 0 # 0 = unlimited
133
161
 
134
162
  logs:
135
163
  dir: .lisa/logs
136
- format: text
137
- ```
164
+ format: text # "text" or "json"
138
165
 
139
- **Trello:**
140
- ```yaml
141
- provider: claude
142
- source: trello
143
- workflow: branch
144
-
145
- source_config:
146
- board: Product
147
- pick_from: Backlog
148
- label: ready
149
- in_progress: In Progress
150
- done: Code Review
151
-
152
- github: cli
153
- workspace: .
154
-
155
- loop:
156
- cooldown: 10
157
- max_sessions: 0
158
-
159
- logs:
160
- dir: .lisa/logs
161
- format: text
166
+ # Optional — kill stuck providers
167
+ overseer:
168
+ enabled: true
169
+ check_interval: 30 # seconds between git status checks
170
+ stuck_threshold: 300 # seconds without git changes before killing
162
171
  ```
163
172
 
164
- ### Source-specific fields
173
+ ### Source-Specific Fields
165
174
 
166
175
  | Field | Linear | Trello |
167
176
  |-------|--------|--------|
168
- | `team` / `board` | Team name | Board name |
177
+ | `team` | Team name | Board name |
169
178
  | `project` | Project name | — |
170
- | `pick_from` | Status to pick issues from (e.g. Todo) | List to pick cards from (e.g. Backlog) |
179
+ | `pick_from` | Status to pick issues from | List to pick cards from |
171
180
  | `label` | Label to filter issues | Label to filter cards |
172
- | `in_progress` | In-progress status (e.g. In Progress) | In-progress column |
173
- | `done` | Destination status (e.g. In Review) | Destination column (e.g. Code Review) |
181
+ | `in_progress` | In-progress status | In-progress column |
182
+ | `done` | Destination status after PR | Destination column after PR |
174
183
 
175
- CLI flags override config values:
184
+ ### Lifecycle Resources
176
185
 
177
- ```bash
178
- lisa run --provider gemini --label "urgent"
186
+ For repos that need services running during implementation (databases, dev servers):
187
+
188
+ ```yaml
189
+ repos:
190
+ - name: my-api
191
+ path: ./api
192
+ base_branch: main
193
+ lifecycle:
194
+ resources:
195
+ - name: postgres
196
+ check_port: 5432
197
+ up: "docker compose up -d postgres"
198
+ down: "docker compose down"
199
+ startup_timeout: 30
200
+ setup:
201
+ - "npx prisma generate"
202
+ - "npx prisma db push"
179
203
  ```
180
204
 
205
+ Lisa starts resources before the agent runs, waits for the port to be ready, runs setup commands, then stops everything after the session.
206
+
181
207
  ## How It Works
182
208
 
183
- 1. **Fetch** — Pulls the next issue from Linear or Trello matching the configured label, team, and project. Issues are sorted by priority.
184
- 2. **Activate** — Moves the issue to the `in_progress` status so your team knows it's being worked on.
185
- 3. **Implement** Builds a structured prompt with full issue context and sends it to the AI agent. The agent creates a branch, implements, validates (lint, typecheck, tests), commits, and pushes.
186
- 4. **PR** — Detects every repo the agent touched and creates a pull request for each, referencing the original issue. Multi-repo workspaces are handled automatically.
187
- 5. **Update** — Moves the issue to the `done` status and removes the pickup label.
188
- 6. **Next** — Picks the next issue. When there are no more issues, Lisa stops. No idle polling, no wasted cycles.
209
+ ```
210
+ ┌─────────┐ ┌──────────┐ ┌───────────┐ ┌──────────┐ ┌────┐ ┌────────┐
211
+ │ Fetch │───▶│ Activate │───▶│ Implement │───▶│ Validate │───▶│ PR │───▶│ Update
212
+ └─────────┘ └──────────┘ └───────────┘ └──────────┘ └────┘ └────────┘
213
+ ```
214
+
215
+ 1. **Fetch** — Pulls the next issue from Linear or Trello matching the configured label, team, and project. Issues are sorted by priority. Blocked issues (with unresolved dependencies) are skipped.
216
+ 2. **Activate** — Moves the issue to `in_progress` so your team knows it's being worked on.
217
+ 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 validation, and commits.
218
+ 4. **Validate** — Runs the project's test suite. If tests fail, the session is aborted and the issue reverts.
219
+ 5. **PR** — Pushes the branch and creates a pull request referencing the original issue. If pre-push hooks fail, Lisa re-invokes the agent to fix the errors and retries (up to 2 recovery attempts).
220
+ 6. **Update** — Moves the issue to the `done` status and removes the pickup label in a single atomic operation.
221
+ 7. **Next** — Picks the next issue. When there are no more matching issues, Lisa stops.
222
+
223
+ ### Recovery Mechanisms
224
+
225
+ - **Orphan recovery** — On startup, Lisa scans for issues stuck in `in_progress` from previous interrupted runs and reverts them to `pick_from`.
226
+ - **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.
227
+ - **Signal handling** — SIGINT/SIGTERM gracefully revert the active issue to its previous status before exiting.
228
+ - **Guardrails** — Failed sessions are logged to `.lisa/guardrails.md` and injected into future prompts so the agent avoids repeating the same mistakes.
189
229
 
190
230
  ## License
191
231
 
package/dist/index.js CHANGED
@@ -1488,6 +1488,56 @@ var LinearSource = class {
1488
1488
  }
1489
1489
  async attachPullRequest(_issueId, _prUrl) {
1490
1490
  }
1491
+ async completeIssue(issueId, statusName, labelToRemove) {
1492
+ const issueData = await gql(
1493
+ `query($identifier: String!) {
1494
+ issue(id: $identifier) {
1495
+ id
1496
+ team { id }
1497
+ labels { nodes { id name } }
1498
+ }
1499
+ }`,
1500
+ { identifier: issueId }
1501
+ );
1502
+ const statesData = await gql(
1503
+ `query($teamId: ID!) {
1504
+ workflowStates(filter: { team: { id: { eq: $teamId } } }) {
1505
+ nodes { id name }
1506
+ }
1507
+ }`,
1508
+ { teamId: issueData.issue.team.id }
1509
+ );
1510
+ const state = statesData.workflowStates.nodes.find(
1511
+ (s) => s.name.toLowerCase() === statusName.toLowerCase()
1512
+ );
1513
+ if (!state) {
1514
+ const available = statesData.workflowStates.nodes.map((s) => s.name).join(", ");
1515
+ throw new Error(`Status "${statusName}" not found. Available: ${available}`);
1516
+ }
1517
+ const input = { stateId: state.id };
1518
+ if (labelToRemove) {
1519
+ const currentLabels = issueData.issue.labels.nodes;
1520
+ const filtered = currentLabels.filter(
1521
+ (l) => l.name.toLowerCase() !== labelToRemove.toLowerCase()
1522
+ );
1523
+ if (filtered.length !== currentLabels.length) {
1524
+ input.labelIds = filtered.map((l) => l.id);
1525
+ }
1526
+ }
1527
+ const mutationResult = await gql(
1528
+ `mutation($issueId: String!, $input: IssueUpdateInput!) {
1529
+ issueUpdate(id: $issueId, input: $input) {
1530
+ success
1531
+ }
1532
+ }`,
1533
+ { issueId: issueData.issue.id, input }
1534
+ );
1535
+ if (!mutationResult.issueUpdate.success) {
1536
+ throw new Error(
1537
+ `issueUpdate returned success=false for ${issueId} (stateId: ${state.id}, stateName: ${state.name})`
1538
+ );
1539
+ }
1540
+ }
1491
1541
  async removeLabel(issueId, labelName) {
1492
1542
  const issueData = await gql(
1493
1543
  `query($identifier: String!) {
@@ -1630,6 +1680,12 @@ var TrelloSource = class {
1630
1680
  async attachPullRequest(cardId, prUrl) {
1631
1681
  await trelloPost(`/cards/${cardId}/attachments`, `url=${encodeURIComponent(prUrl)}`);
1632
1682
  }
1683
+ async completeIssue(cardId, listName, labelToRemove) {
1684
+ await this.updateStatus(cardId, listName);
1685
+ if (labelToRemove) {
1686
+ await this.removeLabel(cardId, labelToRemove);
1687
+ }
1688
+ }
1633
1689
  async removeLabel(cardId, labelName) {
1634
1690
  const card = await trelloGet(
1635
1691
  `/cards/${cardId}`,
@@ -2082,22 +2138,16 @@ async function runLoop(config2, opts) {
2082
2138
  warn(`Failed to attach PR: ${err instanceof Error ? err.message : String(err)}`);
2083
2139
  }
2084
2140
  }
2085
- let statusUpdated = false;
2086
2141
  try {
2087
2142
  const doneStatus = config2.source_config.done;
2088
- await source.updateStatus(issue.id, doneStatus);
2143
+ const labelToRemove = opts.issueId ? void 0 : config2.source_config.label;
2144
+ await source.completeIssue(issue.id, doneStatus, labelToRemove);
2089
2145
  ok(`Updated ${issue.id} status to "${doneStatus}"`);
2090
- statusUpdated = true;
2091
- } catch (err) {
2092
- error(`Failed to update status: ${err instanceof Error ? err.message : String(err)}`);
2093
- }
2094
- if (statusUpdated && !opts.issueId) {
2095
- try {
2096
- await source.removeLabel(issue.id, config2.source_config.label);
2097
- ok(`Removed label "${config2.source_config.label}" from ${issue.id}`);
2098
- } catch (err) {
2099
- error(`Failed to remove label: ${err instanceof Error ? err.message : String(err)}`);
2146
+ if (labelToRemove) {
2147
+ ok(`Removed label "${labelToRemove}" from ${issue.id}`);
2100
2148
  }
2149
+ } catch (err) {
2150
+ error(`Failed to complete issue: ${err instanceof Error ? err.message : String(err)}`);
2101
2151
  }
2102
2152
  activeCleanup = null;
2103
2153
  if (opts.once) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tarcisiopgs/lisa",
3
- "version": "0.9.6",
3
+ "version": "1.0.1",
4
4
  "description": "Deterministic autonomous issue resolver — structured AI agent loop for Linear/Trello",
5
5
  "license": "MIT",
6
6
  "type": "module",