@tarcisiopgs/lisa 1.28.0 → 1.28.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.
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Lisa
2
2
 
3
3
  <p align="center">
4
- <strong>Label an issue. Walk away. Come back to a PR.</strong>
4
+ <strong>Plan issues. Run agents. Get PRs.</strong>
5
5
  </p>
6
6
 
7
7
  <p align="center">
@@ -15,23 +15,23 @@
15
15
  <img src="assets/demo.gif" alt="Lisa demo" />
16
16
  </p>
17
17
 
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.
18
+ Lisa connects your issue tracker to an AI coding agent and delivers pull requests — autonomously. Describe a goal, Lisa decomposes it into issues, picks them up, implements each one, opens PRs, and updates your board. No babysitting.
19
19
 
20
20
  ## Quickstart
21
21
 
22
22
  ```bash
23
23
  npm install -g @tarcisiopgs/lisa
24
24
  lisa init # interactive setup wizard
25
- lisa run
25
+ lisa # start the agent loop
26
26
  ```
27
27
 
28
28
  ## How It Works
29
29
 
30
30
  ```
31
- Fetch issue ActivateBuild context → Implement → Push → Open PR → Update board → Next
31
+ PlanCreate issues Fetch → Implement → Push → Open PR → Update board → Next
32
32
  ```
33
33
 
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.
34
+ Lisa starts and shows a Kanban board. If the queue is empty, press `n` to plan — describe a goal and the AI decomposes it into atomic issues, created directly in your tracker. Press `r` to start processing. 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.
35
35
 
36
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.
37
37
 
@@ -39,13 +39,13 @@ If something fails — pre-push hooks, quota limits, stuck processes — Lisa ha
39
39
 
40
40
  - **7 issue trackers** — Linear, GitHub Issues, GitLab Issues, Jira, Trello, Plane, Shortcut
41
41
  - **8 AI agents** — Claude Code, Gemini CLI, GitHub Copilot CLI, Cursor Agent, Aider, Goose, OpenCode, Codex
42
+ - **AI planning** — describe a goal, the AI decomposes it into issues with dependencies, created in your tracker
42
43
  - **Concurrent execution** — process multiple issues in parallel, each in its own worktree
43
44
  - **Multi-repo** — plans across repos, creates one PR per repo in the correct order
44
45
  - **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
+ - **Real-time TUI** — Kanban board with live provider output, plan mode, PR merge detection
46
47
  - **Self-healing** — orphan recovery on startup, push failure retry, stuck process detection
47
48
  - **Guardrails** — past failures are injected into future prompts to avoid repeating mistakes
48
- - **AI planning** — `lisa plan` decomposes goals into atomic issues with dependencies, creates them in your tracker
49
49
  - **Project context** — auto-generates `.lisa/context.md` with your stack, conventions, and constraints
50
50
 
51
51
  ## Providers
@@ -83,21 +83,21 @@ provider_options:
83
83
  ## Commands
84
84
 
85
85
  ```bash
86
- lisa run # start the agent loop
87
- lisa run --once # process a single issue
88
- lisa run --once --dry-run # preview config without executing
89
- lisa run --watch # poll for new issues after queue empties
90
- lisa run --concurrency 3 # process 3 issues in parallel
91
- lisa run --issue INT-42 # process a specific issue
92
- lisa run --limit 5 # stop after 5 issues
93
- lisa plan "Add rate limiting" # decompose goal into issues via AI
94
- lisa plan --issue EPIC-123 # decompose existing issue into sub-issues
95
- lisa plan --continue # resume interrupted plan
96
- lisa init # create .lisa/config.yaml interactively
97
- lisa status # show session stats
98
- lisa doctor # diagnose setup issues (config, provider, env, git)
99
- lisa context refresh # regenerate project context
100
- lisa feedback --pr URL # inject PR review feedback into guardrails
86
+ lisa # start the agent loop (Kanban TUI)
87
+ lisa --once # process a single issue
88
+ lisa --once --dry-run # preview config without executing
89
+ lisa --watch # poll for new issues after queue empties
90
+ lisa -c 3 # process 3 issues in parallel
91
+ lisa --issue INT-42 # process a specific issue
92
+ lisa --limit 5 # stop after 5 issues
93
+ lisa plan "Add rate limiting" # decompose goal into issues via AI (CLI mode)
94
+ lisa plan --issue EPIC-123 # decompose existing issue into sub-issues
95
+ lisa plan --continue # resume interrupted plan
96
+ lisa init # create .lisa/config.yaml interactively
97
+ lisa status # show session stats
98
+ lisa doctor # diagnose setup issues (config, provider, env, git)
99
+ lisa context refresh # regenerate project context
100
+ lisa feedback --pr URL # inject PR review feedback into guardrails
101
101
  ```
102
102
 
103
103
  Append `--json` to any command for machine-readable output. Use `--verbose` / `--quiet` to control log verbosity.
@@ -242,16 +242,16 @@ Acceptance criteria:
242
242
 
243
243
  ## TUI
244
244
 
245
- The real-time Kanban board shows issue progress, streams provider output, and detects PR merges. The sidebar legend updates contextuallyonly the shortcuts active in the current view are shown.
245
+ The real-time Kanban board shows issue progress, streams provider output, and detects PR merges. When the queue is empty, Lisa enters idle mode plan new issues with `n`, then start processing with `r`.
246
246
 
247
247
  **Board view**
248
248
 
249
249
  | Key | Action | Key | Action |
250
250
  |-----|--------|-----|--------|
251
- | `←` `→` | Switch columns | `p` | Pause / resume provider |
252
- | `1` `2` `3` | Jump to column | `k` | Kill current issue |
253
- | `↑` `↓` | Navigate cards | `s` | Skip current issue |
254
- | `↵` | Open detail view | `q` | Quit |
251
+ | `←` `→` | Switch columns | `k` | Kill current issue |
252
+ | `↑` `↓` | Navigate cards | `n` | Open plan mode |
253
+ | `↵` | Open detail view | `r` | Run (from idle) |
254
+ | `p` | Pause / resume | `q` | Quit |
255
255
 
256
256
  **Detail view**
257
257
 
@@ -261,6 +261,16 @@ The real-time Kanban board shows issue progress, streams provider output, and de
261
261
  | `o` | Open PR in browser |
262
262
  | `Esc` | Back to board |
263
263
 
264
+ **Plan mode**
265
+
266
+ | Key | Action |
267
+ |-----|--------|
268
+ | `↵` | Send message / view detail |
269
+ | `e` | Edit issue in $EDITOR |
270
+ | `d` | Delete issue |
271
+ | `a` | Approve and create issues |
272
+ | `Esc` | Cancel / back |
273
+
264
274
  ## License
265
275
 
266
276
  [MIT](LICENSE)
@@ -2917,18 +2917,56 @@ function installSignalHandlers(onBeforeExit) {
2917
2917
 
2918
2918
  // src/loop/demo.ts
2919
2919
  async function runDemoLoop() {
2920
- const demoIssues = [
2921
- { id: "INT-514", title: "Dark mode UI" },
2922
- { id: "INT-513", title: "WebSocket leak fix" },
2923
- { id: "INT-512", title: "Rate limiter middleware" },
2924
- { id: "INT-511", title: "Sidebar navigation icons" },
2925
- { id: "INT-510", title: "Blog post CRUD" },
2926
- { id: "INT-509", title: "Admin FAQ management" },
2927
- { id: "INT-508", title: "Changelog CRUD" }
2928
- ];
2929
2920
  await sleep(3e3);
2930
2921
  kanbanEmitter.emit("provider:model-changed", "claude-sonnet-4-6");
2931
- await sleep(400);
2922
+ kanbanEmitter.emit("work:empty");
2923
+ await sleep(2e3);
2924
+ kanbanEmitter.emit("demo:open-plan", "Add a FAQ section to the web app");
2925
+ await sleep(2500);
2926
+ const plannedIssues = [
2927
+ {
2928
+ title: "Add FAQ shared types to @playground/shared",
2929
+ description: "Create TypeScript interfaces for FAQ data models",
2930
+ order: 1,
2931
+ dependsOn: [],
2932
+ relevantFiles: ["packages/shared/src/types/faq.ts"],
2933
+ acceptanceCriteria: ["FAQ type exported", "Tests pass"]
2934
+ },
2935
+ {
2936
+ title: "Create GET /faq API route returning FAQ data",
2937
+ description: "Add Fastify route that returns FAQ entries as JSON",
2938
+ order: 2,
2939
+ dependsOn: [1],
2940
+ relevantFiles: ["apps/api/src/routes/faq.ts"],
2941
+ acceptanceCriteria: ["GET /faq returns 200", "Response matches schema"]
2942
+ },
2943
+ {
2944
+ title: "Create FAQ page with accordion UI",
2945
+ description: "Build the FAQ page component with expandable sections",
2946
+ order: 3,
2947
+ dependsOn: [1, 2],
2948
+ relevantFiles: ["apps/web/src/pages/faq.tsx"],
2949
+ acceptanceCriteria: ["Page renders FAQ items", "Accordion expands/collapses"]
2950
+ },
2951
+ {
2952
+ title: "Add FAQ link to the navigation menu",
2953
+ description: "Add a navigation entry pointing to the FAQ page",
2954
+ order: 4,
2955
+ dependsOn: [3],
2956
+ relevantFiles: ["apps/web/src/components/nav.tsx"],
2957
+ acceptanceCriteria: ["Link visible in nav", "Navigates to /faq"]
2958
+ }
2959
+ ];
2960
+ kanbanEmitter.emit("plan:issues-ready", plannedIssues);
2961
+ await sleep(3e3);
2962
+ kanbanEmitter.emit("demo:approve-plan");
2963
+ await sleep(500);
2964
+ const demoIssues = [
2965
+ { id: "INT-601", title: "Add FAQ shared types to @playground/shared" },
2966
+ { id: "INT-602", title: "Create GET /faq API route returning FAQ data" },
2967
+ { id: "INT-603", title: "Create FAQ page with accordion UI" },
2968
+ { id: "INT-604", title: "Add FAQ link to the navigation menu" }
2969
+ ];
2932
2970
  for (const issue of demoIssues) {
2933
2971
  kanbanEmitter.emit("issue:queued", {
2934
2972
  id: issue.id,
@@ -2938,57 +2976,27 @@ async function runDemoLoop() {
2938
2976
  });
2939
2977
  await sleep(200);
2940
2978
  }
2941
- await sleep(1e3);
2942
- const issue1 = demoIssues[0];
2943
- kanbanEmitter.emit("issue:started", issue1.id);
2944
- const outputs1 = [
2945
- "Reading issue description...\n",
2946
- "Analyzing codebase structure...\n",
2947
- "Creating src/theme/dark-mode.ts...\n",
2948
- "Updating CSS variables for dark palette...\n",
2949
- "Adding toggle component...\n",
2950
- "Running tests... all passing \u2713\n",
2951
- "Pushing branch int-514-dark-mode-ui...\n"
2952
- ];
2953
- for (const line of outputs1) {
2954
- kanbanEmitter.emit("issue:output", issue1.id, line);
2955
- await sleep(500);
2956
- }
2957
- kanbanEmitter.emit("issue:done", issue1.id, ["https://github.com/acme/webapp/pull/91"]);
2958
- await sleep(1e3);
2959
- const issue2 = demoIssues[1];
2960
- kanbanEmitter.emit("issue:started", issue2.id);
2961
- const outputs2 = [
2962
- "Reading issue description...\n",
2963
- "Locating WebSocket connection handler...\n",
2964
- "Patching connection lifecycle in src/ws/handler.ts...\n",
2965
- "Adding cleanup in disconnect callback...\n",
2966
- "Running tests... all passing \u2713\n",
2967
- "Pushing branch int-513-fix-ws-leak...\n"
2968
- ];
2969
- for (const line of outputs2) {
2970
- kanbanEmitter.emit("issue:output", issue2.id, line);
2971
- await sleep(500);
2972
- }
2973
- kanbanEmitter.emit("issue:done", issue2.id, ["https://github.com/acme/webapp/pull/92"]);
2974
- await sleep(1e3);
2975
- const issue3 = demoIssues[2];
2976
- kanbanEmitter.emit("issue:started", issue3.id);
2977
- const outputs3 = [
2978
- "Reading issue description...\n",
2979
- "Creating src/middleware/rateLimiter.ts...\n",
2980
- "Writing sliding window rate limiter...\n",
2981
- "Adding tests in rateLimiter.test.ts...\n",
2982
- "Running tests... all passing \u2713\n",
2983
- "Pushing branch int-512-rate-limiting...\n"
2984
- ];
2985
- for (const line of outputs3) {
2986
- kanbanEmitter.emit("issue:output", issue3.id, line);
2987
- await sleep(500);
2979
+ kanbanEmitter.emit("work:resumed");
2980
+ await sleep(1500);
2981
+ for (let i = 0; i < demoIssues.length; i++) {
2982
+ const issue = demoIssues[i];
2983
+ kanbanEmitter.emit("issue:started", issue.id);
2984
+ const steps = [
2985
+ "Reading issue description...\n",
2986
+ "Analyzing codebase...\n",
2987
+ "Implementing changes...\n",
2988
+ "Running tests... all passing \u2713\n",
2989
+ `Pushing branch feat/${issue.id.toLowerCase()}...
2990
+ `
2991
+ ];
2992
+ for (const step of steps) {
2993
+ kanbanEmitter.emit("issue:output", issue.id, step);
2994
+ await sleep(400);
2995
+ }
2996
+ kanbanEmitter.emit("issue:done", issue.id, [`https://github.com/acme/webapp/pull/${90 + i}`]);
2997
+ await sleep(800);
2988
2998
  }
2989
- kanbanEmitter.emit("issue:done", issue3.id, ["https://github.com/acme/webapp/pull/93"]);
2990
- await sleep(1e3);
2991
- kanbanEmitter.emit("work:complete", { total: 3, duration: 127e3 });
2999
+ kanbanEmitter.emit("work:complete", { total: 4, duration: 185e3 });
2992
3000
  await sleep(4e3);
2993
3001
  kanbanEmitter.emit("tui:exit");
2994
3002
  }
package/dist/index.js CHANGED
@@ -36,7 +36,7 @@ import {
36
36
  runLoop,
37
37
  saveConfig,
38
38
  validateConfig
39
- } from "./chunk-W73XGHD4.js";
39
+ } from "./chunk-KDAXGOFF.js";
40
40
  import {
41
41
  createProvider,
42
42
  createSource,
@@ -1286,7 +1286,7 @@ async function reviewAndCreate(plan2, planPath, opts) {
1286
1286
  log("Run `lisa run` when ready.");
1287
1287
  return;
1288
1288
  }
1289
- const { runLoop: runLoop2 } = await import("./loop-KNJIRK7T.js");
1289
+ const { runLoop: runLoop2 } = await import("./loop-SXI4PQOI.js");
1290
1290
  await runLoop2(config2, {
1291
1291
  once: false,
1292
1292
  watch: false,
@@ -1637,7 +1637,7 @@ async function executeRun(args) {
1637
1637
  if (isTTY) {
1638
1638
  const { render } = await import("ink");
1639
1639
  const { createElement } = await import("react");
1640
- const { KanbanApp } = await import("./kanban-2WMA5VPQ.js");
1640
+ const { KanbanApp } = await import("./kanban-HFUBTOF4.js");
1641
1641
  const demoConfig = {
1642
1642
  provider: "claude",
1643
1643
  source: "linear",
@@ -1745,7 +1745,7 @@ Add them to your ${shell} and run: source ${shell}`));
1745
1745
  };
1746
1746
  const { render } = await import("ink");
1747
1747
  const { createElement } = await import("react");
1748
- const { KanbanApp } = await import("./kanban-2WMA5VPQ.js");
1748
+ const { KanbanApp } = await import("./kanban-HFUBTOF4.js");
1749
1749
  render(createElement(KanbanApp, { config: merged, initialCards }), { exitOnCtrlC: false });
1750
1750
  }
1751
1751
  await runLoop(merged, {
@@ -1016,15 +1016,28 @@ function KanbanApp({ config, initialCards = [] }) {
1016
1016
  return next;
1017
1017
  });
1018
1018
  };
1019
+ const onDemoOpenPlan = (userMessage) => {
1020
+ setActiveView("plan-chat");
1021
+ setPlanMessages([{ role: "user", content: userMessage }]);
1022
+ setPlanGoal(userMessage);
1023
+ setPlanThinking(true);
1024
+ };
1025
+ const onDemoApprovePlan = () => {
1026
+ setActiveView("board");
1027
+ };
1019
1028
  kanbanEmitter.on("plan:ai-message", onAiMessage);
1020
1029
  kanbanEmitter.on("plan:thinking", onThinking);
1021
1030
  kanbanEmitter.on("plan:issues-ready", onIssuesReady);
1022
1031
  kanbanEmitter.on("plan:edit-result", onEditResult);
1032
+ kanbanEmitter.on("demo:open-plan", onDemoOpenPlan);
1033
+ kanbanEmitter.on("demo:approve-plan", onDemoApprovePlan);
1023
1034
  return () => {
1024
1035
  kanbanEmitter.off("plan:ai-message", onAiMessage);
1025
1036
  kanbanEmitter.off("plan:thinking", onThinking);
1026
1037
  kanbanEmitter.off("plan:issues-ready", onIssuesReady);
1027
1038
  kanbanEmitter.off("plan:edit-result", onEditResult);
1039
+ kanbanEmitter.off("demo:open-plan", onDemoOpenPlan);
1040
+ kanbanEmitter.off("demo:approve-plan", onDemoApprovePlan);
1028
1041
  };
1029
1042
  }, []);
1030
1043
  const backlog = [...cards.filter((c) => c.column === "backlog")].sort((a, b) => {
@@ -3,7 +3,7 @@ import {
3
3
  checkoutBaseBranches,
4
4
  runDemoLoop,
5
5
  runLoop
6
- } from "./chunk-W73XGHD4.js";
6
+ } from "./chunk-KDAXGOFF.js";
7
7
  import {
8
8
  WATCH_POLL_INTERVAL_MS
9
9
  } from "./chunk-UXVSQQID.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tarcisiopgs/lisa",
3
- "version": "1.28.0",
3
+ "version": "1.28.1",
4
4
  "description": "Autonomous issue resolver",
5
5
  "keywords": [
6
6
  "loop",