@tarcisiopgs/lisa 1.15.0 → 1.17.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
@@ -112,7 +112,7 @@ Set the tokens for your chosen source and PR platform:
112
112
  # PR platform
113
113
  GITHUB_TOKEN # GitHub (platform: cli or token)
114
114
  GITLAB_TOKEN # GitLab (platform: gitlab)
115
- BITBUCKET_TOKEN # Bitbucket (platform: bitbucket)
115
+ BITBUCKET_TOKEN # Bitbucket app password (platform: bitbucket)
116
116
  BITBUCKET_USERNAME # Bitbucket username
117
117
 
118
118
  # Issue sources
@@ -125,9 +125,14 @@ PLANE_BASE_URL # optional, defaults to https://api.plane.so
125
125
  GITLAB_TOKEN # source: gitlab-issues
126
126
  GITLAB_BASE_URL # optional, defaults to https://gitlab.com
127
127
  GITHUB_TOKEN # source: github-issues
128
- JIRA_BASE_URL # source: jira
128
+ JIRA_BASE_URL # source: jira (e.g. https://yourorg.atlassian.net)
129
129
  JIRA_EMAIL
130
- JIRA_API_TOKEN
130
+ JIRA_API_TOKEN # generate at id.atlassian.com — expires, regenerate if 401
131
+
132
+ # Aider provider (one of)
133
+ GEMINI_API_KEY
134
+ OPENAI_API_KEY
135
+ ANTHROPIC_API_KEY
131
136
  ```
132
137
 
133
138
  ---
@@ -184,6 +189,7 @@ repos:
184
189
  loop:
185
190
  cooldown: 10 # seconds between issues
186
191
  max_sessions: 0 # 0 = unlimited
192
+ session_timeout: 0 # seconds per provider run (0 = disabled)
187
193
 
188
194
  # Optional — kill stuck providers
189
195
  overseer:
@@ -196,6 +202,22 @@ validation:
196
202
  require_acceptance_criteria: true
197
203
  ```
198
204
 
205
+ ### Source-specific notes
206
+
207
+ **GitHub Issues / GitLab Issues** — `pick_from`, `in_progress`, and `done` are **labels**, not statuses. Make sure `in_progress` differs from `pick_from`; using the same value causes Lisa to re-pick issues that are already being worked on.
208
+
209
+ **Trello** — `team`, `pick_from`, `in_progress`, and `done` are list **names** (not IDs).
210
+
211
+ **Jira** — `team` is your project **key** (e.g. `ENG`). `JIRA_API_TOKEN` is generated at [id.atlassian.com](https://id.atlassian.com) and expires — regenerate if you get 401 errors.
212
+
213
+ **Goose** — `lisa init` asks which backend to use (gemini-cli, anthropic, openai, etc.) and saves it to config. No env vars needed. You can also set `GOOSE_PROVIDER` manually — it takes precedence over the config value.
214
+
215
+ **Aider** — requires a direct LLM API key (`GEMINI_API_KEY`, `OPENAI_API_KEY`, or `ANTHROPIC_API_KEY`). Does not support OAuth or cached credentials.
216
+
217
+ **OpenCode** — if `~/.config/opencode/config.json` contains MCP entries, remove them or set the file to `{}` to prevent OpenCode from hanging on startup.
218
+
219
+ ---
220
+
199
221
  ### Workflow Modes
200
222
 
201
223
  **Branch** — The agent creates a branch in your current checkout. Simple setup, works everywhere.
@@ -206,6 +228,19 @@ When `--concurrency` is greater than 1, worktree mode is enforced automatically.
206
228
 
207
229
  ---
208
230
 
231
+ ### Session Timeout
232
+
233
+ If a provider hangs (e.g. misconfigured model, network issue), Lisa can kill it after a configurable duration:
234
+
235
+ ```yaml
236
+ loop:
237
+ session_timeout: 300 # kill provider after 5 minutes (0 = disabled, default)
238
+ ```
239
+
240
+ When the timeout fires, the provider process is killed and the error is eligible for fallback — Lisa will try the next model in your chain. This is disabled by default so long-running sessions work uninterrupted.
241
+
242
+ ---
243
+
209
244
  ## Writing Issues
210
245
 
211
246
  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`.
@@ -3,7 +3,7 @@
3
3
  // src/paths.ts
4
4
  import { createHash } from "crypto";
5
5
  import { existsSync, mkdirSync, readdirSync, statSync, unlinkSync } from "fs";
6
- import { homedir } from "os";
6
+ import { homedir, platform } from "os";
7
7
  import { join, resolve } from "path";
8
8
  var MAX_LOG_FILES = 20;
9
9
  function projectHash(cwd) {
@@ -11,7 +11,14 @@ function projectHash(cwd) {
11
11
  return createHash("sha256").update(absolute).digest("hex").slice(0, 12);
12
12
  }
13
13
  function getCacheDir(cwd) {
14
- const base = process.env.XDG_CACHE_HOME || join(homedir(), ".cache");
14
+ let base;
15
+ if (process.env.XDG_CACHE_HOME) {
16
+ base = process.env.XDG_CACHE_HOME;
17
+ } else if (platform() === "darwin") {
18
+ base = join(homedir(), "Library", "Caches");
19
+ } else {
20
+ base = join(homedir(), ".cache");
21
+ }
15
22
  return join(base, "lisa", projectHash(cwd));
16
23
  }
17
24
  function getLogsDir(cwd) {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getGuardrailsPath
4
- } from "./chunk-OYQ6TOAG.js";
4
+ } from "./chunk-GZ2ZAQO4.js";
5
5
 
6
6
  // src/session/guardrails.ts
7
7
  import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
@@ -193,7 +193,7 @@ var GitHubIssuesSource = class {
193
193
  const filterLabels = isOrphanDetection ? [config.pick_from] : Array.isArray(config.label) ? config.label : [config.label];
194
194
  const label = filterLabels.map((l) => encodeURIComponent(l)).join(",");
195
195
  const path = `/repos/${owner}/${repo}/issues?labels=${label}&state=open&sort=created&direction=asc&per_page=100`;
196
- const issues = await githubGet(path);
196
+ const issues = (await githubGet(path)).filter((i) => !i.pull_request);
197
197
  if (issues.length === 0) return null;
198
198
  const unblocked = [];
199
199
  const blocked = [];
@@ -333,7 +333,7 @@ var GitHubIssuesSource = class {
333
333
  const labels = Array.isArray(config.label) ? config.label : [config.label];
334
334
  const label = labels.map((l) => encodeURIComponent(l)).join(",");
335
335
  const path = `/repos/${owner}/${repo}/issues?labels=${label}&state=open&sort=created&direction=asc&per_page=100`;
336
- const issues = await githubGet(path);
336
+ const issues = (await githubGet(path)).filter((i) => !i.pull_request);
337
337
  return issues.map((issue) => ({
338
338
  id: makeIssueId(owner, repo, issue.number),
339
339
  title: issue.title,
@@ -448,10 +448,10 @@ var GitLabIssuesSource = class {
448
448
  );
449
449
  const activeBlockers = links.filter((link) => {
450
450
  if (link.link_type === "is_blocked_by") {
451
- return link.source.state !== "closed";
451
+ return link.state !== "closed";
452
452
  }
453
453
  return false;
454
- }).map((link) => link.source.iid);
454
+ }).map((link) => link.iid);
455
455
  if (activeBlockers.length === 0) {
456
456
  unblocked.push(issue2);
457
457
  } else {
@@ -743,6 +743,9 @@ function useKanbanState(bellEnabled) {
743
743
  })
744
744
  );
745
745
  };
746
+ const onLogFile = (issueId, logFile) => {
747
+ setCards((prev) => prev.map((c) => c.id === issueId ? { ...c, logFile } : c));
748
+ };
746
749
  const onOutput = (issueId, text) => {
747
750
  setCards(
748
751
  (prev) => prev.map((c) => c.id === issueId ? { ...c, outputLog: c.outputLog + text } : c)
@@ -757,6 +760,7 @@ function useKanbanState(bellEnabled) {
757
760
  kanbanEmitter.on("issue:killed", onKilled);
758
761
  kanbanEmitter.on("provider:paused", onProviderPaused);
759
762
  kanbanEmitter.on("provider:resumed", onProviderResumed);
763
+ kanbanEmitter.on("issue:log-file", onLogFile);
760
764
  kanbanEmitter.on("issue:output", onOutput);
761
765
  const onModelChanged = (model) => setModelInUse(model);
762
766
  kanbanEmitter.on("provider:model-changed", onModelChanged);
@@ -786,6 +790,7 @@ function useKanbanState(bellEnabled) {
786
790
  kanbanEmitter.off("issue:killed", onKilled);
787
791
  kanbanEmitter.off("provider:paused", onProviderPaused);
788
792
  kanbanEmitter.off("provider:resumed", onProviderResumed);
793
+ kanbanEmitter.off("issue:log-file", onLogFile);
789
794
  kanbanEmitter.off("issue:output", onOutput);
790
795
  kanbanEmitter.off("provider:model-changed", onModelChanged);
791
796
  kanbanEmitter.off("work:empty", onEmpty);
@@ -10,8 +10,8 @@ import {
10
10
  guardrailsPath,
11
11
  migrateGuardrails,
12
12
  readGuardrails
13
- } from "./chunk-NXGXGHS3.js";
14
- import "./chunk-OYQ6TOAG.js";
13
+ } from "./chunk-UQPR5OXK.js";
14
+ import "./chunk-GZ2ZAQO4.js";
15
15
  export {
16
16
  appendEntry,
17
17
  appendEntrySync,