@tarcisiopgs/lisa 1.26.0 → 1.26.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.
Files changed (3) hide show
  1. package/README.md +39 -5
  2. package/dist/index.js +37 -26
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -56,13 +56,27 @@ If something fails — pre-push hooks, quota limits, stuck processes — Lisa ha
56
56
  | GitHub Copilot CLI | `copilot` | Aider | `aider` |
57
57
  | OpenCode | `opencode` | OpenAI Codex | `codex` |
58
58
 
59
- Configure a fallback chain:
59
+ Configure models and provider-specific options:
60
60
 
61
61
  ```yaml
62
62
  provider: claude
63
- models:
64
- - claude-sonnet-4-6 # primary
65
- - claude-opus-4-6 # fallback
63
+ provider_options:
64
+ claude:
65
+ models:
66
+ - claude-sonnet-4-6 # primary
67
+ - claude-opus-4-6 # fallback
68
+ effort: high # optional: low, medium, high, max
69
+ ```
70
+
71
+ Goose requires a backend selection:
72
+
73
+ ```yaml
74
+ provider: goose
75
+ provider_options:
76
+ goose:
77
+ goose_provider: gemini-cli # gemini-cli, anthropic, openai, google, ollama
78
+ models:
79
+ - gemini-2.5-pro
66
80
  ```
67
81
 
68
82
  ## Commands
@@ -93,11 +107,14 @@ workflow: worktree # "worktree" (isolated) or "branch" (in-place)
93
107
  source_config:
94
108
  scope: Engineering
95
109
  project: Web App
96
- label: ready
110
+ label: ready # or array: [ready, urgent]
111
+ remove_label: ready # label to remove on completion (defaults to label)
97
112
  pick_from: Backlog
98
113
  in_progress: In Progress
99
114
  done: In Review
100
115
 
116
+ bell: true # terminal bell on issue completion
117
+
101
118
  platform: cli # "cli" (gh), "token" (GITHUB_TOKEN), "gitlab", "bitbucket"
102
119
  base_branch: main
103
120
  ```
@@ -120,6 +137,21 @@ SHORTCUT_API_TOKEN=""
120
137
  GITLAB_TOKEN=""
121
138
  GITHUB_TOKEN=""
122
139
  JIRA_BASE_URL="" && JIRA_EMAIL="" && JIRA_API_TOKEN=""
140
+
141
+ # Self-hosted instances (optional)
142
+ PLANE_BASE_URL="" # default: https://api.plane.so
143
+ GITLAB_BASE_URL="" # default: https://gitlab.com
144
+ PLANE_WORKSPACE="" # fallback for source_config.scope
145
+
146
+ # Goose backend (required when provider: goose)
147
+ GOOSE_PROVIDER="" # gemini-cli, anthropic, openai, google, ollama
148
+ GOOSE_MODEL="" # model name for the selected backend
149
+
150
+ # AI provider API keys (used by Aider / Goose / wizard auto-detection)
151
+ ANTHROPIC_API_KEY=""
152
+ OPENAI_API_KEY=""
153
+ GEMINI_API_KEY=""
154
+ GOOGLE_API_KEY="" # for Goose with goose_provider: google
123
155
  ```
124
156
 
125
157
  </details>
@@ -133,6 +165,7 @@ JIRA_BASE_URL="" && JIRA_EMAIL="" && JIRA_API_TOKEN=""
133
165
  | `project` | Project name | — | Project ID | — | — | — | — |
134
166
  | `pick_from` | Status name | List name | State name | Workflow state | — | — | Status name |
135
167
  | `label` | Label | Label | Label | Label | Label | Label | Label |
168
+ | `remove_label` | Label | Label | Label | Label | — | — | — |
136
169
  | `in_progress` | Status | Column | State | Workflow state | Label | Label | Status |
137
170
  | `done` | Status | Column | State | Workflow state | Closes issue | Closes issue | Status |
138
171
 
@@ -163,6 +196,7 @@ Lisa runs a planning phase, then executes steps sequentially — one worktree an
163
196
  loop:
164
197
  cooldown: 10 # seconds between issues
165
198
  session_timeout: 0 # max seconds per provider run (0 = disabled)
199
+ output_stall_timeout: 120 # seconds without stdout before killing provider (0 = disabled)
166
200
 
167
201
  overseer:
168
202
  enabled: true
package/dist/index.js CHANGED
@@ -1406,6 +1406,9 @@ async function jiraPost(path, body) {
1406
1406
  async function jiraPut(path, body) {
1407
1407
  return jiraFetch("PUT", path, body);
1408
1408
  }
1409
+ async function jiraSearchJql(jql, fields, maxResults) {
1410
+ return jiraPost("/search/jql", { jql, fields: fields.split(","), maxResults });
1411
+ }
1409
1412
  function priorityRank(issue2) {
1410
1413
  const name = issue2.fields.priority?.name?.toLowerCase() ?? "";
1411
1414
  return PRIORITY_RANK[name] ?? Number.MAX_SAFE_INTEGER;
@@ -1432,6 +1435,21 @@ function extractAdfText(node) {
1432
1435
  function escapeJql(value) {
1433
1436
  return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
1434
1437
  }
1438
+ async function resolveStatusId(scope, statusName) {
1439
+ try {
1440
+ const data = await jiraGet(
1441
+ `/project/${encodeURIComponent(scope)}/statuses`
1442
+ );
1443
+ for (const issueType of data) {
1444
+ const match = issueType.statuses.find(
1445
+ (s) => s.name.toLowerCase() === statusName.toLowerCase()
1446
+ );
1447
+ if (match) return match.id;
1448
+ }
1449
+ } catch {
1450
+ }
1451
+ return null;
1452
+ }
1435
1453
  function issueUrl(baseUrl, key) {
1436
1454
  return `${baseUrl}/browse/${key}`;
1437
1455
  }
@@ -1440,13 +1458,11 @@ var JiraSource = class {
1440
1458
  async fetchNextIssue(config2) {
1441
1459
  const labels = Array.isArray(config2.label) ? config2.label : [config2.label];
1442
1460
  const labelClause = labels.map((l) => `labels = "${escapeJql(l)}"`).join(" AND ");
1443
- const jql = encodeURIComponent(
1444
- `project = "${escapeJql(config2.scope)}" AND ${labelClause} AND status = "${escapeJql(config2.pick_from)}" ORDER BY priority ASC, created ASC`
1445
- );
1461
+ const statusId = await resolveStatusId(config2.scope, config2.pick_from);
1462
+ const statusClause = statusId ? `status = ${statusId}` : `status = "${escapeJql(config2.pick_from)}"`;
1463
+ const jql = `project = "${escapeJql(config2.scope)}" AND ${labelClause} AND ${statusClause} ORDER BY priority ASC, created ASC`;
1446
1464
  const fields = "summary,description,priority,status,labels,issuelinks";
1447
- const data = await jiraGet(
1448
- `/search/jql?jql=${jql}&fields=${fields}&maxResults=50`
1449
- );
1465
+ const data = await jiraSearchJql(jql, fields, 50);
1450
1466
  const issues = data.issues ?? [];
1451
1467
  if (issues.length === 0) return null;
1452
1468
  const unblocked = [];
@@ -1504,11 +1520,10 @@ var JiraSource = class {
1504
1520
  async updateStatus(issueId, statusName) {
1505
1521
  const key = parseJiraIdentifier(issueId);
1506
1522
  const data = await jiraGet(`/issue/${key}/transitions`);
1507
- const transition = data.transitions.find(
1508
- (t) => t.name.toLowerCase() === statusName.toLowerCase()
1509
- );
1523
+ const lowerName = statusName.toLowerCase();
1524
+ const transition = data.transitions.find((t) => t.to.name.toLowerCase() === lowerName) ?? data.transitions.find((t) => t.name.toLowerCase() === lowerName);
1510
1525
  if (!transition) {
1511
- const available = data.transitions.map((t) => t.name).join(", ");
1526
+ const available = data.transitions.map((t) => `${t.name} \u2192 ${t.to.name}`).join(", ");
1512
1527
  throw new Error(`Jira transition "${statusName}" not found. Available: ${available}`);
1513
1528
  }
1514
1529
  await jiraPost(`/issue/${key}/transitions`, { transition: { id: transition.id } });
@@ -1535,13 +1550,11 @@ var JiraSource = class {
1535
1550
  async listIssues(config2) {
1536
1551
  const labels = Array.isArray(config2.label) ? config2.label : [config2.label];
1537
1552
  const labelClause = labels.map((l) => `labels = "${escapeJql(l)}"`).join(" AND ");
1538
- const jql = encodeURIComponent(
1539
- `project = "${escapeJql(config2.scope)}" AND ${labelClause} AND status = "${escapeJql(config2.pick_from)}" ORDER BY priority ASC, created ASC`
1540
- );
1553
+ const statusId = await resolveStatusId(config2.scope, config2.pick_from);
1554
+ const statusClause = statusId ? `status = ${statusId}` : `status = "${escapeJql(config2.pick_from)}"`;
1555
+ const jql = `project = "${escapeJql(config2.scope)}" AND ${labelClause} AND ${statusClause} ORDER BY priority ASC, created ASC`;
1541
1556
  const fields = "summary,description,priority,status,labels";
1542
- const data = await jiraGet(
1543
- `/search/jql?jql=${jql}&fields=${fields}&maxResults=100`
1544
- );
1557
+ const data = await jiraSearchJql(jql, fields, 100);
1545
1558
  const baseUrl = getBaseUrl();
1546
1559
  return (data.issues ?? []).map((issue2) => ({
1547
1560
  id: issue2.key,
@@ -2552,14 +2565,15 @@ var ShortcutSource = class {
2552
2565
  }
2553
2566
  async removeLabel(storyId, labelName) {
2554
2567
  const story = await shortcutGet(`/api/v3/stories/${storyId}`);
2555
- const labels = await shortcutGet("/api/v3/labels");
2556
- const label = labels.find(
2568
+ const allLabels = await shortcutGet("/api/v3/labels");
2569
+ const labelToRemove = allLabels.find(
2557
2570
  (l) => l.name.toLowerCase() === labelName.toLowerCase() && !l.archived
2558
2571
  );
2559
- if (!label || !story.label_ids.includes(label.id)) return;
2560
- const updatedLabelIds = story.label_ids.filter((lid) => lid !== label.id);
2572
+ if (!labelToRemove || !story.label_ids.includes(labelToRemove.id)) return;
2573
+ const remainingIds = story.label_ids.filter((lid) => lid !== labelToRemove.id);
2574
+ const labelNames = remainingIds.map((lid) => allLabels.find((l) => l.id === lid)).filter((l) => l !== void 0).map((l) => ({ name: l.name }));
2561
2575
  await shortcutPut(`/api/v3/stories/${storyId}`, {
2562
- label_ids: updatedLabelIds
2576
+ labels: labelNames
2563
2577
  });
2564
2578
  }
2565
2579
  };
@@ -4663,17 +4677,14 @@ function formatProviderName(providerUsed) {
4663
4677
  function getAuthHeader2() {
4664
4678
  const token = process.env.BITBUCKET_TOKEN;
4665
4679
  if (!token) throw new Error("BITBUCKET_TOKEN is not set");
4666
- const username = process.env.BITBUCKET_USERNAME;
4667
- if (!username) throw new Error("BITBUCKET_USERNAME is not set");
4668
- const credentials = Buffer.from(`${username}:${token}`).toString("base64");
4669
- return `Basic ${credentials}`;
4680
+ return `Bearer ${token}`;
4670
4681
  }
4671
4682
  async function appendPrAttribution2(prUrl, providerUsed) {
4672
4683
  try {
4673
4684
  const match = prUrl.match(/bitbucket\.org\/([^/]+)\/([^/]+)\/pull-requests\/(\d+)/);
4674
4685
  if (!match) return;
4675
4686
  const [, workspace, repoSlug, prId] = match;
4676
- if (!process.env.BITBUCKET_TOKEN || !process.env.BITBUCKET_USERNAME) return;
4687
+ if (!process.env.BITBUCKET_TOKEN) return;
4677
4688
  const authHeader = getAuthHeader2();
4678
4689
  const getRes = await fetch(
4679
4690
  `${API_URL3}/repositories/${workspace}/${repoSlug}/pullrequests/${prId}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tarcisiopgs/lisa",
3
- "version": "1.26.0",
3
+ "version": "1.26.2",
4
4
  "description": "Autonomous issue resolver",
5
5
  "keywords": [
6
6
  "loop",