@tarcisiopgs/lisa 1.7.4 → 1.7.6

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
@@ -14,8 +14,6 @@
14
14
 
15
15
  ---
16
16
 
17
- <!-- Add a GIF or screen recording of the full pipeline here (fetch → implement → PR) -->
18
-
19
17
  ## Quickstart
20
18
 
21
19
  ```bash
@@ -57,9 +55,9 @@ Lisa follows a deterministic pipeline:
57
55
 
58
56
  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.
59
57
  2. **Activate** — Moves the issue to `in_progress` so your team knows it's being worked on.
60
- 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, and commits.
61
- 4. **Validate** — Runs the project's test suite. If tests fail, the session is aborted and the issue reverts.
62
- 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.
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
+ 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. The PR body includes a footer crediting the provider that resolved it.
63
61
  6. **Update** — Moves the issue to the `done` status and removes the pickup label.
64
62
  7. **Next** — Picks the next issue. When there are no more matching issues, Lisa stops.
65
63
 
@@ -75,15 +73,15 @@ Lisa follows a deterministic pipeline:
75
73
 
76
74
  ## Providers
77
75
 
78
- | Provider | CLI | Auto-approve Flag |
79
- |----------|-----|-------------------|
80
- | Claude Code | `claude` | `--dangerously-skip-permissions` |
81
- | Gemini CLI | `gemini` | `--yolo` |
82
- | OpenCode | `opencode` | implicit in `run` |
83
- | GitHub Copilot CLI | `copilot` | `--allow-all` |
84
- | Cursor Agent | `agent` | `--force` |
85
- | Goose | `goose` | implicit in `run` |
86
- | Aider | `aider` | `--yes-always` |
76
+ | Provider | Key | Command |
77
+ |----------|-----|---------|
78
+ | Claude Code | `claude` | `claude` |
79
+ | Gemini CLI | `gemini` | `gemini` |
80
+ | OpenCode | `opencode` | `opencode` |
81
+ | GitHub Copilot CLI | `copilot` | `copilot` |
82
+ | Cursor Agent | `cursor` | `agent` / `cursor-agent` |
83
+ | Goose | `goose` | `goose` |
84
+ | Aider | `aider` | `aider` |
87
85
 
88
86
  At least one provider must be installed and available in your PATH.
89
87
 
@@ -108,7 +106,7 @@ npm install -g @tarcisiopgs/lisa
108
106
  ## Environment Variables
109
107
 
110
108
  ```bash
111
- # Required (at least one)
109
+ # Required for PR creation (at least one)
112
110
  export GITHUB_TOKEN="" # or have `gh` CLI authenticated
113
111
 
114
112
  # Required when source = linear
@@ -150,16 +148,47 @@ export JIRA_API_TOKEN="" # Atlassian API token
150
148
  | `lisa run --limit N` | Process up to N issues |
151
149
  | `lisa run --dry-run` | Preview without executing |
152
150
  | `lisa run --provider NAME` | Override AI provider |
153
- | `lisa run --source NAME` | Override issue source (linear, trello, plane, shortcut, gitlab-issues, github-issues, jira) |
151
+ | `lisa run --source NAME` | Override issue source |
154
152
  | `lisa run --label NAME` | Override label filter |
155
- | `lisa run --github METHOD` | Override GitHub method (cli, token) |
153
+ | `lisa run --github METHOD` | Override GitHub method (`cli` or `token`) |
156
154
  | `lisa run --json` | Output as JSON lines |
157
155
  | `lisa run --quiet` | Suppress non-essential output |
158
- | `lisa config` | Interactive config wizard |
159
- | `lisa config --show` | Show current config |
160
- | `lisa config --set key=value` | Set a config value |
161
- | `lisa init` | Create `.lisa/config.yaml` |
156
+ | `lisa init` | Create `.lisa/config.yaml` interactively |
157
+ | `lisa config` | Edit config interactively |
158
+ | `lisa config --show` | Print current config as JSON |
159
+ | `lisa config --set key=value` | Set a single config value |
162
160
  | `lisa status` | Show session stats |
161
+ | `lisa issue get <id>` | Fetch full issue details as JSON (for use inside worktrees) |
162
+ | `lisa issue done <id> --pr-url <url>` | Complete issue, attach PR, update status, remove label |
163
+
164
+ ## TUI
165
+
166
+ When running in an interactive terminal, `lisa run` renders a real-time Kanban board:
167
+
168
+ ```
169
+ ┌──────────────────────────┐ ┌───────────────────────────┐ ┌───────────────────────────┐
170
+ │ ▶ BACKLOG [3] │ │ ▶ IN PROGRESS [1] │ │ ▶ IN REVIEW [2] │
171
+ │ │ │ │ │ │
172
+ │ ┌────────────────────┐ │ │ ┌─────────────────────┐ │ │ ┌─────────────────────┐ │
173
+ │ │ ENG-42 │ │ │ │ ● ENG-38 │ │ │ │ ✓ ENG-35 │ │
174
+ │ │ Add dark mode │ │ │ │ Fix login redirect │ │ │ │ Update dependencies │ │
175
+ │ │ ready │ │ │ │ ~1 running │ │ │ │ PR created │ │
176
+ │ └────────────────────┘ │ │ └─────────────────────┘ │ │ └─────────────────────┘ │
177
+ └──────────────────────────┘ └───────────────────────────┘ └───────────────────────────┘
178
+ ```
179
+
180
+ ### Keyboard shortcuts
181
+
182
+ | Key | Action |
183
+ |-----|--------|
184
+ | `Tab` | Move to next column |
185
+ | `Shift+Tab` | Move to previous column |
186
+ | `↑` / `↓` | Navigate cards within a column |
187
+ | `Enter` | Open issue detail view (streams provider output) |
188
+ | `Esc` | Close detail view, return to board |
189
+ | `q` | Quit |
190
+
191
+ The terminal tab title also updates in real time: it shows a spinner with the active issue ID while work is in progress, and a checkmark when done.
163
192
 
164
193
  ## Configuration
165
194
 
@@ -1,5 +1,49 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ // src/output/terminal.ts
4
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
5
+ var SPINNER_INTERVAL_MS = 80;
6
+ var spinnerTimer = null;
7
+ var spinnerFrame = 0;
8
+ function isTTY() {
9
+ return process.stdout.isTTY === true;
10
+ }
11
+ function writeOSC(title) {
12
+ process.stdout.write(`\x1B]0;${title}\x07`);
13
+ }
14
+ function setTitle(title) {
15
+ if (!isTTY()) return;
16
+ writeOSC(title);
17
+ }
18
+ function startSpinner(message) {
19
+ if (!isTTY()) return;
20
+ stopSpinner();
21
+ spinnerFrame = 0;
22
+ writeOSC(`${SPINNER_FRAMES[0]} Lisa \u2014 ${message}`);
23
+ spinnerTimer = setInterval(() => {
24
+ spinnerFrame = (spinnerFrame + 1) % SPINNER_FRAMES.length;
25
+ writeOSC(`${SPINNER_FRAMES[spinnerFrame]} Lisa \u2014 ${message}`);
26
+ }, SPINNER_INTERVAL_MS);
27
+ }
28
+ function stopSpinner(message) {
29
+ if (spinnerTimer) {
30
+ clearInterval(spinnerTimer);
31
+ spinnerTimer = null;
32
+ }
33
+ if (!isTTY()) return;
34
+ if (message) {
35
+ writeOSC(message);
36
+ }
37
+ }
38
+ function notify() {
39
+ if (!isTTY()) return;
40
+ process.stdout.write("\x07");
41
+ }
42
+ function resetTitle() {
43
+ if (!isTTY()) return;
44
+ writeOSC("");
45
+ }
46
+
3
47
  // src/ui/state.ts
4
48
  import { EventEmitter } from "events";
5
49
  import { useEffect, useState } from "react";
@@ -68,6 +112,11 @@ function useKanbanState() {
68
112
  }
69
113
 
70
114
  export {
115
+ setTitle,
116
+ startSpinner,
117
+ stopSpinner,
118
+ notify,
119
+ resetTitle,
71
120
  kanbanEmitter,
72
121
  useKanbanState
73
122
  };
package/dist/index.js CHANGED
@@ -1,7 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- kanbanEmitter
4
- } from "./chunk-MUBWKMRZ.js";
3
+ kanbanEmitter,
4
+ notify,
5
+ resetTitle,
6
+ setTitle,
7
+ startSpinner,
8
+ stopSpinner
9
+ } from "./chunk-YZKNBQN6.js";
5
10
 
6
11
  // src/cli.ts
7
12
  import { execSync as execSync8 } from "child_process";
@@ -174,6 +179,33 @@ async function isGhCliAvailable() {
174
179
  return false;
175
180
  }
176
181
  }
182
+ var PROVIDER_DISPLAY_NAMES = {
183
+ claude: "Claude Code",
184
+ gemini: "Gemini CLI",
185
+ opencode: "OpenCode",
186
+ copilot: "GitHub Copilot CLI",
187
+ cursor: "Cursor Agent",
188
+ goose: "Goose",
189
+ aider: "Aider"
190
+ };
191
+ function formatProviderName(providerUsed) {
192
+ const providerKey = providerUsed.split("/")[0] ?? providerUsed;
193
+ return PROVIDER_DISPLAY_NAMES[providerKey] ?? providerKey;
194
+ }
195
+ async function appendPrAttribution(prUrl, providerUsed) {
196
+ try {
197
+ const { stdout: bodyJson } = await execa("gh", ["pr", "view", prUrl, "--json", "body"]);
198
+ const { body } = JSON.parse(bodyJson);
199
+ const providerName = formatProviderName(providerUsed);
200
+ const attribution = `
201
+
202
+ ---
203
+ \u{1F916} Resolved by [lisa](https://github.com/tarcisiopgs/lisa) using **${providerName}**`;
204
+ const newBody = (body ?? "") + attribution;
205
+ await execa("gh", ["pr", "edit", prUrl, "--body", newBody]);
206
+ } catch {
207
+ }
208
+ }
177
209
 
178
210
  // src/git/worktree.ts
179
211
  import { appendFileSync, existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
@@ -346,52 +378,6 @@ function banner() {
346
378
  `));
347
379
  }
348
380
 
349
- // src/output/terminal.ts
350
- var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
351
- var SPINNER_INTERVAL_MS = 80;
352
- var spinnerTimer = null;
353
- var spinnerFrame = 0;
354
- function isTTY() {
355
- return process.stdout.isTTY === true;
356
- }
357
- function writeOSC(title) {
358
- process.stdout.write(`\x1B]0;${title}\x07`);
359
- }
360
- function setTitle(title) {
361
- if (!isTTY()) return;
362
- writeOSC(title);
363
- }
364
- function startSpinner(message) {
365
- if (!isTTY()) return;
366
- if (getOutputMode() === "tui") return;
367
- stopSpinner();
368
- spinnerFrame = 0;
369
- writeOSC(`${SPINNER_FRAMES[0]} Lisa \u2014 ${message}`);
370
- spinnerTimer = setInterval(() => {
371
- spinnerFrame = (spinnerFrame + 1) % SPINNER_FRAMES.length;
372
- writeOSC(`${SPINNER_FRAMES[spinnerFrame]} Lisa \u2014 ${message}`);
373
- }, SPINNER_INTERVAL_MS);
374
- }
375
- function stopSpinner(message) {
376
- if (spinnerTimer) {
377
- clearInterval(spinnerTimer);
378
- spinnerTimer = null;
379
- }
380
- if (!isTTY()) return;
381
- if (getOutputMode() === "tui") return;
382
- if (message) {
383
- writeOSC(message);
384
- }
385
- }
386
- function notify() {
387
- if (!isTTY()) return;
388
- process.stdout.write("\x07");
389
- }
390
- function resetTitle() {
391
- if (!isTTY()) return;
392
- writeOSC("");
393
- }
394
-
395
381
  // src/prompt.ts
396
382
  import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
397
383
  import { join as join2, resolve as resolve3 } from "path";
@@ -3643,6 +3629,7 @@ ${result.output}
3643
3629
  }
3644
3630
  const worktreePath = await findWorktreeForBranch(repoPath, manifest.branch ?? "");
3645
3631
  ok(`PR created by provider: ${manifest.prUrl}`);
3632
+ await appendPrAttribution(manifest.prUrl, result.providerUsed);
3646
3633
  if (worktreePath) await cleanupWorktree(repoPath, worktreePath);
3647
3634
  ok(`Session ${session} complete for ${issue2.id}`);
3648
3635
  return {
@@ -3749,6 +3736,7 @@ ${result.output}
3749
3736
  return { success: false, providerUsed: result.providerUsed, prUrls: [], fallback: result };
3750
3737
  }
3751
3738
  ok(`PR created by provider: ${manifest.prUrl}`);
3739
+ await appendPrAttribution(manifest.prUrl, result.providerUsed);
3752
3740
  await cleanupWorktree(repoPath, worktreePath);
3753
3741
  ok(`Session ${session} complete for ${issue2.id}`);
3754
3742
  return {
@@ -3937,6 +3925,7 @@ ${result.output}
3937
3925
  return { ...failResult(result.providerUsed, result), branch: branchName };
3938
3926
  }
3939
3927
  await cleanupWorktree(repoPath, worktreePath);
3928
+ await appendPrAttribution(manifest.prUrl, result.providerUsed);
3940
3929
  ok(`Step ${stepNum} complete: ${repoPath} \u2014 PR: ${manifest.prUrl}`);
3941
3930
  return {
3942
3931
  success: true,
@@ -4017,6 +4006,7 @@ ${result.output}
4017
4006
  return { success: false, providerUsed: result.providerUsed, prUrls: [], fallback: result };
4018
4007
  }
4019
4008
  ok(`PR created by provider: ${manifest.prUrl}`);
4009
+ await appendPrAttribution(manifest.prUrl, result.providerUsed);
4020
4010
  ok(`Session ${session} complete for ${issue2.id}`);
4021
4011
  return {
4022
4012
  success: true,
@@ -4092,7 +4082,7 @@ Add them to your ${shell} and run: source ${shell}`));
4092
4082
  if (isTUI) {
4093
4083
  const { render } = await import("ink");
4094
4084
  const { createElement } = await import("react");
4095
- const { KanbanApp } = await import("./kanban-KPIDX2SU.js");
4085
+ const { KanbanApp } = await import("./kanban-YP3TJJUT.js");
4096
4086
  render(createElement(KanbanApp, { config: merged }), { exitOnCtrlC: false });
4097
4087
  }
4098
4088
  await runLoop(merged, {
@@ -4141,15 +4131,14 @@ var init = defineCommand({
4141
4131
  process.exit(1);
4142
4132
  }
4143
4133
  if (configExists()) {
4144
- const overwrite = await clack.confirm({
4145
- message: "A config already exists at .lisa/config.yaml. Overwrite it?"
4146
- });
4147
- if (clack.isCancel(overwrite) || !overwrite) {
4148
- log("Cancelled.");
4149
- return;
4150
- }
4134
+ const existing = loadConfig();
4135
+ clack.log.info(
4136
+ `Existing config found \u2014 current values will be pre-filled. Edit what you need, keep the rest.`
4137
+ );
4138
+ await runConfigWizard(existing);
4139
+ } else {
4140
+ await runConfigWizard();
4151
4141
  }
4152
- await runConfigWizard();
4153
4142
  }
4154
4143
  });
4155
4144
  var status = defineCommand({
@@ -4321,14 +4310,26 @@ function fetchCursorModels() {
4321
4310
  function fetchOpenCodeModels() {
4322
4311
  try {
4323
4312
  const raw = execSync8("opencode models", { encoding: "utf-8", timeout: 1e4 });
4324
- return raw.split("\n").map((l) => l.trim()).filter(
4325
- (m) => (
4326
- // Latest Anthropic Claude 4.x — exclude dated snapshots (contain 8-digit date suffix)
4327
- /^anthropic\/claude-(opus|sonnet|haiku)-4-\d+$/.test(m) || // Google Gemini 2.5 stable (no preview/tts suffixes)
4328
- /^google\/gemini-2\.5-(pro|flash|flash-lite)$/.test(m) || // OpenCode proprietary models
4329
- /^opencode\//.test(m)
4330
- )
4313
+ const hasAnthropic = Boolean(process.env.ANTHROPIC_API_KEY);
4314
+ const hasGoogle = Boolean(
4315
+ process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY || process.env.GOOGLE_GENERATIVE_AI_API_KEY
4331
4316
  );
4317
+ const hasOpenAI = Boolean(process.env.OPENAI_API_KEY);
4318
+ const hasCopilot = Boolean(process.env.GITHUB_COPILOT_API_KEY || process.env.GITHUB_TOKEN);
4319
+ const hasGroq = Boolean(process.env.GROQ_API_KEY);
4320
+ const hasMistral = Boolean(process.env.MISTRAL_API_KEY);
4321
+ const hasDeepSeek = Boolean(process.env.DEEPSEEK_API_KEY);
4322
+ return raw.split("\n").map((l) => l.trim()).filter((m) => {
4323
+ if (/^opencode\//.test(m)) return true;
4324
+ if (/^anthropic\/claude-(opus|sonnet|haiku)-4-\d+$/.test(m)) return hasAnthropic;
4325
+ if (/^google\/gemini-2\.5-(pro|flash|flash-lite)$/.test(m)) return hasGoogle;
4326
+ if (/^openai\//.test(m)) return hasOpenAI;
4327
+ if (/^github-copilot\//.test(m)) return hasCopilot;
4328
+ if (/^groq\//.test(m)) return hasGroq;
4329
+ if (/^mistral\//.test(m)) return hasMistral;
4330
+ if (/^deepseek\//.test(m)) return hasDeepSeek;
4331
+ return false;
4332
+ });
4332
4333
  } catch {
4333
4334
  return [];
4334
4335
  }
@@ -4341,8 +4342,10 @@ var main = defineCommand({
4341
4342
  },
4342
4343
  subCommands: { run, config, init, status, issue }
4343
4344
  });
4344
- async function runConfigWizard() {
4345
- clack.intro(pc2.cyan(" lisa \u266A autonomous issue resolver "));
4345
+ async function runConfigWizard(existing) {
4346
+ clack.intro(
4347
+ pc2.cyan(existing ? " lisa \u266A editing config " : " lisa \u266A autonomous issue resolver ")
4348
+ );
4346
4349
  const providerLabels = {
4347
4350
  claude: "Claude Code",
4348
4351
  gemini: "Gemini CLI",
@@ -4378,12 +4381,13 @@ async function runConfigWizard() {
4378
4381
  return process.exit(1);
4379
4382
  }
4380
4383
  let providerName;
4381
- if (available.length === 1 && available[0]) {
4384
+ if (available.length === 1 && available[0] && !existing) {
4382
4385
  providerName = available[0].name;
4383
4386
  clack.log.info(`Auto-detected ${pc2.bold(providerLabels[providerName])} as your AI provider.`);
4384
4387
  } else {
4385
4388
  const selected = await clack.select({
4386
4389
  message: "Which AI provider should resolve your issues?",
4390
+ initialValue: existing?.provider,
4387
4391
  options: allProviders.map(({ provider, available: isAvailable }) => ({
4388
4392
  value: provider.name,
4389
4393
  label: providerLabels[provider.name],
@@ -4417,6 +4421,7 @@ async function runConfigWizard() {
4417
4421
  if (availableModels && availableModels.length > 0) {
4418
4422
  const modelSelection = await clack.multiselect({
4419
4423
  message: "Which models should Lisa use? Select in order \u2014 first = primary, rest = fallbacks",
4424
+ initialValues: existing?.models?.filter((m) => availableModels.includes(m)) ?? [],
4420
4425
  options: availableModels.map((m) => ({
4421
4426
  value: m,
4422
4427
  label: m
@@ -4428,6 +4433,7 @@ async function runConfigWizard() {
4428
4433
  }
4429
4434
  const source = await clack.select({
4430
4435
  message: "Where do your issues come from?",
4436
+ initialValue: existing?.source,
4431
4437
  options: [
4432
4438
  { value: "linear", label: "Linear", apiHint: "GraphQL API", envVars: ["LINEAR_API_KEY"] },
4433
4439
  {
@@ -4489,13 +4495,14 @@ Then reload: ${pc2.cyan(`source ${shell}`)}`
4489
4495
  const githubMethod = await detectGitHubMethod();
4490
4496
  const teamAnswer = await clack.text({
4491
4497
  message: source === "linear" ? "What is your Linear team name?" : source === "trello" ? "What is your Trello board name?" : source === "jira" ? "What is your Jira project key?" : "What is your team or project name?",
4498
+ initialValue: existing?.source_config.team ?? "",
4492
4499
  placeholder: source === "linear" ? "e.g. Engineering" : void 0
4493
4500
  });
4494
4501
  if (clack.isCancel(teamAnswer)) return process.exit(0);
4495
4502
  const team = teamAnswer;
4496
4503
  const labelAnswer = await clack.text({
4497
4504
  message: "Which label marks issues as ready for the agent to pick up?",
4498
- initialValue: "ready",
4505
+ initialValue: existing?.source_config.label ?? "ready",
4499
4506
  placeholder: "e.g. ready, ai, lisa"
4500
4507
  });
4501
4508
  if (clack.isCancel(labelAnswer)) return process.exit(0);
@@ -4507,52 +4514,54 @@ Then reload: ${pc2.cyan(`source ${shell}`)}`
4507
4514
  if (source === "trello") {
4508
4515
  const pickFromAnswer = await clack.text({
4509
4516
  message: "Pick up cards from which list?",
4510
- initialValue: "Backlog"
4517
+ initialValue: existing?.source_config.pick_from ?? "Backlog"
4511
4518
  });
4512
4519
  if (clack.isCancel(pickFromAnswer)) return process.exit(0);
4513
4520
  pickFrom = pickFromAnswer;
4514
4521
  project = pickFrom;
4515
4522
  const inProgressAnswer = await clack.text({
4516
4523
  message: "Move the card to which list while the agent is working?",
4517
- initialValue: "In Progress"
4524
+ initialValue: existing?.source_config.in_progress ?? "In Progress"
4518
4525
  });
4519
4526
  if (clack.isCancel(inProgressAnswer)) return process.exit(0);
4520
4527
  inProgress = inProgressAnswer;
4521
4528
  const doneAnswer = await clack.text({
4522
4529
  message: "Move the card to which list after the PR is created?",
4523
- initialValue: "Code Review"
4530
+ initialValue: existing?.source_config.done ?? "Code Review"
4524
4531
  });
4525
4532
  if (clack.isCancel(doneAnswer)) return process.exit(0);
4526
4533
  done = doneAnswer;
4527
4534
  } else {
4528
4535
  const projectAnswer = await clack.text({
4529
4536
  message: source === "linear" ? "Which Linear project should Lisa work on? (leave empty for all team issues)" : "Which project should Lisa work on?",
4537
+ initialValue: existing?.source_config.project ?? "",
4530
4538
  placeholder: source === "linear" ? "e.g. Q1 Roadmap (optional)" : void 0
4531
4539
  });
4532
4540
  if (clack.isCancel(projectAnswer)) return process.exit(0);
4533
4541
  project = projectAnswer;
4534
4542
  const pickFromAnswer = await clack.text({
4535
4543
  message: "Pick up issues in which status?",
4536
- initialValue: "Backlog",
4544
+ initialValue: existing?.source_config.pick_from ?? "Backlog",
4537
4545
  placeholder: "e.g. Backlog, Todo"
4538
4546
  });
4539
4547
  if (clack.isCancel(pickFromAnswer)) return process.exit(0);
4540
4548
  pickFrom = pickFromAnswer;
4541
4549
  const inProgressAnswer = await clack.text({
4542
4550
  message: "Move to which status while the agent is working?",
4543
- initialValue: "In Progress"
4551
+ initialValue: existing?.source_config.in_progress ?? "In Progress"
4544
4552
  });
4545
4553
  if (clack.isCancel(inProgressAnswer)) return process.exit(0);
4546
4554
  inProgress = inProgressAnswer;
4547
4555
  const doneAnswer = await clack.text({
4548
4556
  message: "Move to which status after the PR is created?",
4549
- initialValue: "In Review"
4557
+ initialValue: existing?.source_config.done ?? "In Review"
4550
4558
  });
4551
4559
  if (clack.isCancel(doneAnswer)) return process.exit(0);
4552
4560
  done = doneAnswer;
4553
4561
  }
4554
4562
  const workflowAnswer = await clack.select({
4555
4563
  message: "How should Lisa check out code for each issue?",
4564
+ initialValue: existing?.workflow,
4556
4565
  options: [
4557
4566
  {
4558
4567
  value: "worktree",
@@ -4572,7 +4581,7 @@ Then reload: ${pc2.cyan(`source ${shell}`)}`
4572
4581
  let baseBranch = "main";
4573
4582
  const cwd = process.cwd();
4574
4583
  if (repos.length === 0) {
4575
- const detected = detectDefaultBranch(cwd);
4584
+ const detected = existing?.base_branch ?? detectDefaultBranch(cwd);
4576
4585
  const branchAnswer = await clack.text({
4577
4586
  message: "What is the base branch to branch off from?",
4578
4587
  initialValue: detected
@@ -1,8 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  kanbanEmitter,
4
+ resetTitle,
5
+ startSpinner,
6
+ stopSpinner,
4
7
  useKanbanState
5
- } from "./chunk-MUBWKMRZ.js";
8
+ } from "./chunk-YZKNBQN6.js";
6
9
 
7
10
  // src/ui/kanban.tsx
8
11
  import { Box as Box6, useApp, useInput as useInput2 } from "ink";
@@ -109,6 +112,7 @@ function Column({ label, cards, isFocused = false, activeCardIndex = 0 }) {
109
112
  {
110
113
  flexDirection: "column",
111
114
  flexGrow: 1,
115
+ flexBasis: 0,
112
116
  borderStyle: "single",
113
117
  borderColor,
114
118
  paddingX: 1,
@@ -344,11 +348,13 @@ function IssueDetail({ card, onBack }) {
344
348
  }
345
349
 
346
350
  // src/ui/sidebar.tsx
347
- import { basename } from "path";
351
+ import { existsSync } from "fs";
352
+ import { basename, join } from "path";
348
353
  import { Box as Box5, Text as Text5 } from "ink";
349
354
  import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
350
355
  function Sidebar({ provider, source, cwd }) {
351
356
  const dir = basename(cwd);
357
+ const cwdLabel = existsSync(join(cwd, ".git")) ? "REPOSITORY" : "WORKSPACE";
352
358
  return /* @__PURE__ */ jsxs5(
353
359
  Box5,
354
360
  {
@@ -390,7 +396,7 @@ function Sidebar({ provider, source, cwd }) {
390
396
  ] })
391
397
  ] }),
392
398
  /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, children: [
393
- /* @__PURE__ */ jsx5(Text5, { color: "white", dimColor: true, children: "WORKSPACE" }),
399
+ /* @__PURE__ */ jsx5(Text5, { color: "white", dimColor: true, children: cwdLabel }),
394
400
  /* @__PURE__ */ jsxs5(Box5, { children: [
395
401
  /* @__PURE__ */ jsx5(Text5, { color: "yellow", children: "\u25B8 " }),
396
402
  /* @__PURE__ */ jsx5(Text5, { color: "white", bold: true, children: dir.length > 18 ? `${dir.slice(0, 15)}\u2026` : dir })
@@ -426,6 +432,17 @@ function KanbanApp({ config }) {
426
432
  }, [exit]);
427
433
  const backlog = cards.filter((c) => c.column === "backlog");
428
434
  const inProgress = cards.filter((c) => c.column === "in_progress");
435
+ useEffect3(() => {
436
+ if (workComplete) {
437
+ stopSpinner("Lisa \u2014 done \u2713");
438
+ } else if (inProgress.length > 0) {
439
+ const ids = inProgress.map((c) => c.id).join(", ");
440
+ startSpinner(ids);
441
+ } else {
442
+ stopSpinner("Lisa \u266A");
443
+ }
444
+ return () => resetTitle();
445
+ }, [inProgress, workComplete]);
429
446
  const done = cards.filter((c) => c.column === "done");
430
447
  const columnCards = [backlog, inProgress, done];
431
448
  useInput2((input, key) => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tarcisiopgs/lisa",
3
- "version": "1.7.4",
4
- "description": "Deterministic autonomous issue resolver — structured AI agent loop for Linear/Trello",
3
+ "version": "1.7.6",
4
+ "description": "Autonomous issue resolver",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "bin": {