@tarcisiopgs/lisa 1.8.1 → 1.9.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
@@ -57,7 +57,7 @@ Lisa follows a deterministic pipeline:
57
57
  2. **Activate** — Moves the issue to `in_progress` so your team knows it's being worked on.
58
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
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.
60
+ 5. **PR** — Pushes the branch and creates a pull request referencing the original issue.
61
61
  6. **Update** — Moves the issue to the `done` status and removes the pickup label.
62
62
  7. **Next** — Picks the next issue. When there are no more matching issues, Lisa stops.
63
63
 
@@ -86,7 +86,9 @@ Lisa follows a deterministic pipeline:
86
86
 
87
87
  At least one provider must be installed and available in your PATH.
88
88
 
89
- > **Cursor Free plan** — `lisa init` automatically detects Free accounts and restricts model selection to `auto` only. On paid plans, a curated list of top-tier models is shown (`composer-1.5`, `opus-4.6`, `sonnet-4.6`, `gpt-5.3-codex`, etc.).
89
+ > **Cursor Free plan** — `lisa init` automatically detects Free accounts and restricts model selection to `auto` only. On paid plans, available models are fetched live from `cursor --list-models` and filtered to a curated top-tier list (`composer-1.5`, `opus-4.6`, `sonnet-4.6`, `gpt-5.3-codex`, etc.).
90
+
91
+ > **OpenCode** — `lisa init` fetches available models from `opencode models` and filters them based on which API keys are present in your environment (`ANTHROPIC_API_KEY`, `GEMINI_API_KEY`, `OPENAI_API_KEY`, `GITHUB_TOKEN`, `GROQ_API_KEY`, `MISTRAL_API_KEY`, `DEEPSEEK_API_KEY`). Only models from providers you have credentials for are shown.
90
92
 
91
93
  ### Fallback Chain
92
94
 
@@ -180,12 +182,14 @@ When running in an interactive terminal, `lisa run` renders a real-time Kanban b
180
182
  └──────────────────────────┘ └───────────────────────────┘ └───────────────────────────┘
181
183
  ```
182
184
 
185
+ In-progress cards show a live elapsed timer. When the loop is paused, the active card displays a visual pause indicator. The detail view includes a scroll bar when output overflows.
186
+
183
187
  ### Keyboard shortcuts
184
188
 
185
189
  | Key | Action |
186
190
  |-----|--------|
187
- | `Tab` | Move to next column |
188
- | `Shift+Tab` | Move to previous column |
191
+ | `←` / `→` | Move between columns |
192
+ | `Tab` / `Shift+Tab` | Move between columns (alternative) |
189
193
  | `↑` / `↓` | Navigate cards / scroll output |
190
194
  | `Enter` | Open issue detail view (streams provider output) |
191
195
  | `Esc` | Close detail view, return to board |
@@ -66,7 +66,16 @@ function useKanbanState() {
66
66
  const onStarted = (issueId) => {
67
67
  setCards(
68
68
  (prev) => prev.map(
69
- (c) => c.id === issueId ? { ...c, column: "in_progress", startedAt: Date.now(), hasError: false } : c
69
+ (c) => c.id === issueId ? {
70
+ ...c,
71
+ column: "in_progress",
72
+ startedAt: Date.now(),
73
+ hasError: false,
74
+ skipped: false,
75
+ killed: false,
76
+ pausedAt: void 0,
77
+ pauseAccumulated: 0
78
+ } : c
70
79
  )
71
80
  );
72
81
  };
@@ -84,6 +93,52 @@ function useKanbanState() {
84
93
  )
85
94
  );
86
95
  };
96
+ const onSkipped = (issueId) => {
97
+ setCards(
98
+ (prev) => prev.map(
99
+ (c) => c.id === issueId ? {
100
+ ...c,
101
+ column: "backlog",
102
+ startedAt: void 0,
103
+ skipped: true,
104
+ hasError: false,
105
+ pausedAt: void 0
106
+ } : c
107
+ )
108
+ );
109
+ };
110
+ const onKilled = (issueId) => {
111
+ setCards(
112
+ (prev) => prev.map(
113
+ (c) => c.id === issueId ? {
114
+ ...c,
115
+ column: "backlog",
116
+ startedAt: void 0,
117
+ killed: true,
118
+ hasError: false,
119
+ pausedAt: void 0
120
+ } : c
121
+ )
122
+ );
123
+ };
124
+ const onProviderPaused = () => {
125
+ setCards(
126
+ (prev) => prev.map((c) => c.column === "in_progress" ? { ...c, pausedAt: Date.now() } : c)
127
+ );
128
+ };
129
+ const onProviderResumed = () => {
130
+ setCards(
131
+ (prev) => prev.map((c) => {
132
+ if (c.column !== "in_progress" || !c.pausedAt) return c;
133
+ const pauseDuration = Date.now() - c.pausedAt;
134
+ return {
135
+ ...c,
136
+ pausedAt: void 0,
137
+ pauseAccumulated: (c.pauseAccumulated ?? 0) + pauseDuration
138
+ };
139
+ })
140
+ );
141
+ };
87
142
  const onOutput = (issueId, text) => {
88
143
  setCards(
89
144
  (prev) => prev.map((c) => c.id === issueId ? { ...c, outputLog: c.outputLog + text } : c)
@@ -93,6 +148,10 @@ function useKanbanState() {
93
148
  kanbanEmitter.on("issue:started", onStarted);
94
149
  kanbanEmitter.on("issue:done", onDone);
95
150
  kanbanEmitter.on("issue:reverted", onReverted);
151
+ kanbanEmitter.on("issue:skipped", onSkipped);
152
+ kanbanEmitter.on("issue:killed", onKilled);
153
+ kanbanEmitter.on("provider:paused", onProviderPaused);
154
+ kanbanEmitter.on("provider:resumed", onProviderResumed);
96
155
  kanbanEmitter.on("issue:output", onOutput);
97
156
  const onEmpty = () => setIsEmpty(true);
98
157
  const onComplete = (data) => setWorkComplete(data);
@@ -103,6 +162,10 @@ function useKanbanState() {
103
162
  kanbanEmitter.off("issue:started", onStarted);
104
163
  kanbanEmitter.off("issue:done", onDone);
105
164
  kanbanEmitter.off("issue:reverted", onReverted);
165
+ kanbanEmitter.off("issue:skipped", onSkipped);
166
+ kanbanEmitter.off("issue:killed", onKilled);
167
+ kanbanEmitter.off("provider:paused", onProviderPaused);
168
+ kanbanEmitter.off("provider:resumed", onProviderResumed);
106
169
  kanbanEmitter.off("issue:output", onOutput);
107
170
  kanbanEmitter.off("work:empty", onEmpty);
108
171
  kanbanEmitter.off("work:complete", onComplete);