@tarcisiopgs/lisa 1.7.4 → 1.7.5

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.
@@ -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";
@@ -346,52 +351,6 @@ function banner() {
346
351
  `));
347
352
  }
348
353
 
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
354
  // src/prompt.ts
396
355
  import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
397
356
  import { join as join2, resolve as resolve3 } from "path";
@@ -4092,7 +4051,7 @@ Add them to your ${shell} and run: source ${shell}`));
4092
4051
  if (isTUI) {
4093
4052
  const { render } = await import("ink");
4094
4053
  const { createElement } = await import("react");
4095
- const { KanbanApp } = await import("./kanban-KPIDX2SU.js");
4054
+ const { KanbanApp } = await import("./kanban-YP3TJJUT.js");
4096
4055
  render(createElement(KanbanApp, { config: merged }), { exitOnCtrlC: false });
4097
4056
  }
4098
4057
  await runLoop(merged, {
@@ -4141,15 +4100,14 @@ var init = defineCommand({
4141
4100
  process.exit(1);
4142
4101
  }
4143
4102
  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
- }
4103
+ const existing = loadConfig();
4104
+ clack.log.info(
4105
+ `Existing config found \u2014 current values will be pre-filled. Edit what you need, keep the rest.`
4106
+ );
4107
+ await runConfigWizard(existing);
4108
+ } else {
4109
+ await runConfigWizard();
4151
4110
  }
4152
- await runConfigWizard();
4153
4111
  }
4154
4112
  });
4155
4113
  var status = defineCommand({
@@ -4321,14 +4279,26 @@ function fetchCursorModels() {
4321
4279
  function fetchOpenCodeModels() {
4322
4280
  try {
4323
4281
  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
- )
4282
+ const hasAnthropic = Boolean(process.env.ANTHROPIC_API_KEY);
4283
+ const hasGoogle = Boolean(
4284
+ process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY || process.env.GOOGLE_GENERATIVE_AI_API_KEY
4331
4285
  );
4286
+ const hasOpenAI = Boolean(process.env.OPENAI_API_KEY);
4287
+ const hasCopilot = Boolean(process.env.GITHUB_COPILOT_API_KEY || process.env.GITHUB_TOKEN);
4288
+ const hasGroq = Boolean(process.env.GROQ_API_KEY);
4289
+ const hasMistral = Boolean(process.env.MISTRAL_API_KEY);
4290
+ const hasDeepSeek = Boolean(process.env.DEEPSEEK_API_KEY);
4291
+ return raw.split("\n").map((l) => l.trim()).filter((m) => {
4292
+ if (/^opencode\//.test(m)) return true;
4293
+ if (/^anthropic\/claude-(opus|sonnet|haiku)-4-\d+$/.test(m)) return hasAnthropic;
4294
+ if (/^google\/gemini-2\.5-(pro|flash|flash-lite)$/.test(m)) return hasGoogle;
4295
+ if (/^openai\//.test(m)) return hasOpenAI;
4296
+ if (/^github-copilot\//.test(m)) return hasCopilot;
4297
+ if (/^groq\//.test(m)) return hasGroq;
4298
+ if (/^mistral\//.test(m)) return hasMistral;
4299
+ if (/^deepseek\//.test(m)) return hasDeepSeek;
4300
+ return false;
4301
+ });
4332
4302
  } catch {
4333
4303
  return [];
4334
4304
  }
@@ -4341,8 +4311,10 @@ var main = defineCommand({
4341
4311
  },
4342
4312
  subCommands: { run, config, init, status, issue }
4343
4313
  });
4344
- async function runConfigWizard() {
4345
- clack.intro(pc2.cyan(" lisa \u266A autonomous issue resolver "));
4314
+ async function runConfigWizard(existing) {
4315
+ clack.intro(
4316
+ pc2.cyan(existing ? " lisa \u266A editing config " : " lisa \u266A autonomous issue resolver ")
4317
+ );
4346
4318
  const providerLabels = {
4347
4319
  claude: "Claude Code",
4348
4320
  gemini: "Gemini CLI",
@@ -4378,12 +4350,13 @@ async function runConfigWizard() {
4378
4350
  return process.exit(1);
4379
4351
  }
4380
4352
  let providerName;
4381
- if (available.length === 1 && available[0]) {
4353
+ if (available.length === 1 && available[0] && !existing) {
4382
4354
  providerName = available[0].name;
4383
4355
  clack.log.info(`Auto-detected ${pc2.bold(providerLabels[providerName])} as your AI provider.`);
4384
4356
  } else {
4385
4357
  const selected = await clack.select({
4386
4358
  message: "Which AI provider should resolve your issues?",
4359
+ initialValue: existing?.provider,
4387
4360
  options: allProviders.map(({ provider, available: isAvailable }) => ({
4388
4361
  value: provider.name,
4389
4362
  label: providerLabels[provider.name],
@@ -4417,6 +4390,7 @@ async function runConfigWizard() {
4417
4390
  if (availableModels && availableModels.length > 0) {
4418
4391
  const modelSelection = await clack.multiselect({
4419
4392
  message: "Which models should Lisa use? Select in order \u2014 first = primary, rest = fallbacks",
4393
+ initialValues: existing?.models?.filter((m) => availableModels.includes(m)) ?? [],
4420
4394
  options: availableModels.map((m) => ({
4421
4395
  value: m,
4422
4396
  label: m
@@ -4428,6 +4402,7 @@ async function runConfigWizard() {
4428
4402
  }
4429
4403
  const source = await clack.select({
4430
4404
  message: "Where do your issues come from?",
4405
+ initialValue: existing?.source,
4431
4406
  options: [
4432
4407
  { value: "linear", label: "Linear", apiHint: "GraphQL API", envVars: ["LINEAR_API_KEY"] },
4433
4408
  {
@@ -4489,13 +4464,14 @@ Then reload: ${pc2.cyan(`source ${shell}`)}`
4489
4464
  const githubMethod = await detectGitHubMethod();
4490
4465
  const teamAnswer = await clack.text({
4491
4466
  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?",
4467
+ initialValue: existing?.source_config.team ?? "",
4492
4468
  placeholder: source === "linear" ? "e.g. Engineering" : void 0
4493
4469
  });
4494
4470
  if (clack.isCancel(teamAnswer)) return process.exit(0);
4495
4471
  const team = teamAnswer;
4496
4472
  const labelAnswer = await clack.text({
4497
4473
  message: "Which label marks issues as ready for the agent to pick up?",
4498
- initialValue: "ready",
4474
+ initialValue: existing?.source_config.label ?? "ready",
4499
4475
  placeholder: "e.g. ready, ai, lisa"
4500
4476
  });
4501
4477
  if (clack.isCancel(labelAnswer)) return process.exit(0);
@@ -4507,52 +4483,54 @@ Then reload: ${pc2.cyan(`source ${shell}`)}`
4507
4483
  if (source === "trello") {
4508
4484
  const pickFromAnswer = await clack.text({
4509
4485
  message: "Pick up cards from which list?",
4510
- initialValue: "Backlog"
4486
+ initialValue: existing?.source_config.pick_from ?? "Backlog"
4511
4487
  });
4512
4488
  if (clack.isCancel(pickFromAnswer)) return process.exit(0);
4513
4489
  pickFrom = pickFromAnswer;
4514
4490
  project = pickFrom;
4515
4491
  const inProgressAnswer = await clack.text({
4516
4492
  message: "Move the card to which list while the agent is working?",
4517
- initialValue: "In Progress"
4493
+ initialValue: existing?.source_config.in_progress ?? "In Progress"
4518
4494
  });
4519
4495
  if (clack.isCancel(inProgressAnswer)) return process.exit(0);
4520
4496
  inProgress = inProgressAnswer;
4521
4497
  const doneAnswer = await clack.text({
4522
4498
  message: "Move the card to which list after the PR is created?",
4523
- initialValue: "Code Review"
4499
+ initialValue: existing?.source_config.done ?? "Code Review"
4524
4500
  });
4525
4501
  if (clack.isCancel(doneAnswer)) return process.exit(0);
4526
4502
  done = doneAnswer;
4527
4503
  } else {
4528
4504
  const projectAnswer = await clack.text({
4529
4505
  message: source === "linear" ? "Which Linear project should Lisa work on? (leave empty for all team issues)" : "Which project should Lisa work on?",
4506
+ initialValue: existing?.source_config.project ?? "",
4530
4507
  placeholder: source === "linear" ? "e.g. Q1 Roadmap (optional)" : void 0
4531
4508
  });
4532
4509
  if (clack.isCancel(projectAnswer)) return process.exit(0);
4533
4510
  project = projectAnswer;
4534
4511
  const pickFromAnswer = await clack.text({
4535
4512
  message: "Pick up issues in which status?",
4536
- initialValue: "Backlog",
4513
+ initialValue: existing?.source_config.pick_from ?? "Backlog",
4537
4514
  placeholder: "e.g. Backlog, Todo"
4538
4515
  });
4539
4516
  if (clack.isCancel(pickFromAnswer)) return process.exit(0);
4540
4517
  pickFrom = pickFromAnswer;
4541
4518
  const inProgressAnswer = await clack.text({
4542
4519
  message: "Move to which status while the agent is working?",
4543
- initialValue: "In Progress"
4520
+ initialValue: existing?.source_config.in_progress ?? "In Progress"
4544
4521
  });
4545
4522
  if (clack.isCancel(inProgressAnswer)) return process.exit(0);
4546
4523
  inProgress = inProgressAnswer;
4547
4524
  const doneAnswer = await clack.text({
4548
4525
  message: "Move to which status after the PR is created?",
4549
- initialValue: "In Review"
4526
+ initialValue: existing?.source_config.done ?? "In Review"
4550
4527
  });
4551
4528
  if (clack.isCancel(doneAnswer)) return process.exit(0);
4552
4529
  done = doneAnswer;
4553
4530
  }
4554
4531
  const workflowAnswer = await clack.select({
4555
4532
  message: "How should Lisa check out code for each issue?",
4533
+ initialValue: existing?.workflow,
4556
4534
  options: [
4557
4535
  {
4558
4536
  value: "worktree",
@@ -4572,7 +4550,7 @@ Then reload: ${pc2.cyan(`source ${shell}`)}`
4572
4550
  let baseBranch = "main";
4573
4551
  const cwd = process.cwd();
4574
4552
  if (repos.length === 0) {
4575
- const detected = detectDefaultBranch(cwd);
4553
+ const detected = existing?.base_branch ?? detectDefaultBranch(cwd);
4576
4554
  const branchAnswer = await clack.text({
4577
4555
  message: "What is the base branch to branch off from?",
4578
4556
  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.5",
4
+ "description": "Autonomous issue resolver",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "bin": {