@tarcisiopgs/lisa 1.0.2 → 1.1.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.
Files changed (2) hide show
  1. package/dist/index.js +93 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1638,7 +1638,7 @@ var TrelloSource = class {
1638
1638
  name = "trello";
1639
1639
  async fetchNextIssue(config2) {
1640
1640
  const board = await findBoardByName(config2.team);
1641
- const list = await findListByName(board.id, config2.project);
1641
+ const list = await findListByName(board.id, config2.pick_from);
1642
1642
  const label = await findLabelByName(board.id, config2.label);
1643
1643
  const cards = await trelloGet(
1644
1644
  `/lists/${list.id}/cards`,
@@ -1715,6 +1715,50 @@ function createSource(name) {
1715
1715
  return factory();
1716
1716
  }
1717
1717
 
1718
+ // src/terminal.ts
1719
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
1720
+ var SPINNER_INTERVAL_MS = 80;
1721
+ var spinnerTimer = null;
1722
+ var spinnerFrame = 0;
1723
+ function isTTY() {
1724
+ return process.stdout.isTTY === true;
1725
+ }
1726
+ function writeOSC(title) {
1727
+ process.stdout.write(`\x1B]0;${title}\x07`);
1728
+ }
1729
+ function setTitle(title) {
1730
+ if (!isTTY()) return;
1731
+ writeOSC(title);
1732
+ }
1733
+ function startSpinner(message) {
1734
+ if (!isTTY()) return;
1735
+ stopSpinner();
1736
+ spinnerFrame = 0;
1737
+ writeOSC(`${SPINNER_FRAMES[0]} Lisa \u2014 ${message}`);
1738
+ spinnerTimer = setInterval(() => {
1739
+ spinnerFrame = (spinnerFrame + 1) % SPINNER_FRAMES.length;
1740
+ writeOSC(`${SPINNER_FRAMES[spinnerFrame]} Lisa \u2014 ${message}`);
1741
+ }, SPINNER_INTERVAL_MS);
1742
+ }
1743
+ function stopSpinner(message) {
1744
+ if (spinnerTimer) {
1745
+ clearInterval(spinnerTimer);
1746
+ spinnerTimer = null;
1747
+ }
1748
+ if (!isTTY()) return;
1749
+ if (message) {
1750
+ writeOSC(message);
1751
+ }
1752
+ }
1753
+ function notify() {
1754
+ if (!isTTY()) return;
1755
+ process.stdout.write("\x07");
1756
+ }
1757
+ function resetTitle() {
1758
+ if (!isTTY()) return;
1759
+ writeOSC("");
1760
+ }
1761
+
1718
1762
  // src/worktree.ts
1719
1763
  import { appendFileSync as appendFileSync5, existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
1720
1764
  import { join as join6, resolve as resolve4 } from "path";
@@ -1946,6 +1990,8 @@ function installSignalHandlers() {
1946
1990
  process.exit(1);
1947
1991
  }
1948
1992
  shuttingDown = true;
1993
+ stopSpinner();
1994
+ resetTitle();
1949
1995
  warn(`Received ${signal}. Reverting active issue...`);
1950
1996
  if (activeCleanup) {
1951
1997
  const { issueId, previousStatus, source } = activeCleanup;
@@ -2022,12 +2068,14 @@ async function runLoop(config2, opts) {
2022
2068
  const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").substring(0, 19);
2023
2069
  const logFile = resolve5(config2.logs.dir, `session_${session}_${timestamp2}.log`);
2024
2070
  divider(session);
2071
+ startSpinner("fetching issue...");
2025
2072
  if (opts.issueId) {
2026
2073
  log(`Fetching issue '${opts.issueId}' from ${config2.source}...`);
2027
2074
  } else {
2028
2075
  log(`Fetching next '${config2.source_config.label}' issue from ${config2.source}...`);
2029
2076
  }
2030
2077
  if (opts.dryRun) {
2078
+ stopSpinner();
2031
2079
  if (opts.issueId) {
2032
2080
  log(`[dry-run] Would fetch issue '${opts.issueId}' from ${config2.source}`);
2033
2081
  } else {
@@ -2044,11 +2092,14 @@ async function runLoop(config2, opts) {
2044
2092
  try {
2045
2093
  issue = opts.issueId ? await source.fetchIssueById(opts.issueId) : await source.fetchNextIssue(config2.source_config);
2046
2094
  } catch (err) {
2095
+ stopSpinner();
2047
2096
  error(`Failed to fetch issues: ${err instanceof Error ? err.message : String(err)}`);
2048
2097
  if (opts.once) break;
2098
+ setTitle("Lisa \u2014 cooling down...");
2049
2099
  await sleep(config2.loop.cooldown * 1e3);
2050
2100
  continue;
2051
2101
  }
2102
+ stopSpinner();
2052
2103
  if (!issue) {
2053
2104
  if (opts.issueId) {
2054
2105
  error(`Issue '${opts.issueId}' not found.`);
@@ -2058,6 +2109,7 @@ async function runLoop(config2, opts) {
2058
2109
  break;
2059
2110
  }
2060
2111
  ok(`Picked up: ${issue.id} \u2014 ${issue.title}`);
2112
+ setTitle(`Lisa \u2014 ${issue.id}`);
2061
2113
  const previousStatus = config2.source_config.pick_from;
2062
2114
  try {
2063
2115
  const inProgress = config2.source_config.in_progress;
@@ -2071,6 +2123,7 @@ async function runLoop(config2, opts) {
2071
2123
  try {
2072
2124
  sessionResult = config2.workflow === "worktree" ? await runWorktreeSession(config2, issue, logFile, session, models) : await runBranchSession(config2, issue, logFile, session, models);
2073
2125
  } catch (err) {
2126
+ stopSpinner();
2074
2127
  error(
2075
2128
  `Unhandled error in session for ${issue.id}: ${err instanceof Error ? err.message : String(err)}`
2076
2129
  );
@@ -2083,8 +2136,10 @@ async function runLoop(config2, opts) {
2083
2136
  );
2084
2137
  }
2085
2138
  activeCleanup = null;
2139
+ notify();
2086
2140
  if (opts.once) break;
2087
2141
  log(`Cooling down ${config2.loop.cooldown}s before next issue...`);
2142
+ setTitle("Lisa \u2014 cooling down...");
2088
2143
  await sleep(config2.loop.cooldown * 1e3);
2089
2144
  continue;
2090
2145
  }
@@ -2100,11 +2155,13 @@ async function runLoop(config2, opts) {
2100
2155
  );
2101
2156
  }
2102
2157
  activeCleanup = null;
2158
+ notify();
2103
2159
  if (opts.once) {
2104
2160
  log("Single iteration mode. Exiting.");
2105
2161
  break;
2106
2162
  }
2107
2163
  log(`Cooling down ${config2.loop.cooldown}s before next issue...`);
2164
+ setTitle("Lisa \u2014 cooling down...");
2108
2165
  await sleep(config2.loop.cooldown * 1e3);
2109
2166
  continue;
2110
2167
  }
@@ -2122,11 +2179,13 @@ async function runLoop(config2, opts) {
2122
2179
  );
2123
2180
  }
2124
2181
  activeCleanup = null;
2182
+ notify();
2125
2183
  if (opts.once) {
2126
2184
  log("Single iteration mode. Exiting.");
2127
2185
  break;
2128
2186
  }
2129
2187
  log(`Cooling down ${config2.loop.cooldown}s before next issue...`);
2188
+ setTitle("Lisa \u2014 cooling down...");
2130
2189
  await sleep(config2.loop.cooldown * 1e3);
2131
2190
  continue;
2132
2191
  }
@@ -2150,13 +2209,17 @@ async function runLoop(config2, opts) {
2150
2209
  error(`Failed to complete issue: ${err instanceof Error ? err.message : String(err)}`);
2151
2210
  }
2152
2211
  activeCleanup = null;
2212
+ stopSpinner(`\u2713 Lisa \u2014 ${issue.id} \u2014 PR created`);
2213
+ notify();
2153
2214
  if (opts.once) {
2154
2215
  log("Single iteration mode. Exiting.");
2155
2216
  break;
2156
2217
  }
2157
2218
  log(`Cooling down ${config2.loop.cooldown}s before next issue...`);
2219
+ setTitle("Lisa \u2014 cooling down...");
2158
2220
  await sleep(config2.loop.cooldown * 1e3);
2159
2221
  }
2222
+ resetTitle();
2160
2223
  ok(`lisa finished. ${session} session(s) run.`);
2161
2224
  }
2162
2225
  function logAttemptHistory(result) {
@@ -2205,11 +2268,13 @@ async function runWorktreeSession(config2, issue, logFile, session, models) {
2205
2268
  const repoPath = determineRepoPath(config2.repos, issue, workspace) ?? workspace;
2206
2269
  const defaultBranch = resolveBaseBranch(config2, repoPath);
2207
2270
  const branchName = generateBranchName(issue.id, issue.title);
2271
+ startSpinner(`${issue.id} \u2014 creating worktree...`);
2208
2272
  log(`Creating worktree for ${branchName} (base: ${defaultBranch})...`);
2209
2273
  let worktreePath;
2210
2274
  try {
2211
2275
  worktreePath = await createWorktree(repoPath, branchName, defaultBranch);
2212
2276
  } catch (err) {
2277
+ stopSpinner();
2213
2278
  error(`Failed to create worktree: ${err instanceof Error ? err.message : String(err)}`);
2214
2279
  return {
2215
2280
  success: false,
@@ -2224,10 +2289,13 @@ async function runWorktreeSession(config2, issue, logFile, session, models) {
2224
2289
  }
2225
2290
  };
2226
2291
  }
2292
+ stopSpinner();
2227
2293
  ok(`Worktree created at ${worktreePath}`);
2228
2294
  const repo = findRepoConfig(config2, issue);
2229
2295
  if (repo?.lifecycle) {
2296
+ startSpinner(`${issue.id} \u2014 starting resources...`);
2230
2297
  const started = await startResources(repo, worktreePath);
2298
+ stopSpinner();
2231
2299
  if (!started) {
2232
2300
  error(`Lifecycle startup failed for ${issue.id}. Aborting session.`);
2233
2301
  await cleanupWorktree(repoPath, worktreePath);
@@ -2250,6 +2318,7 @@ async function runWorktreeSession(config2, issue, logFile, session, models) {
2250
2318
  log(`Detected test runner: ${testRunner}`);
2251
2319
  }
2252
2320
  const prompt = buildImplementPrompt(issue, config2, testRunner);
2321
+ startSpinner(`${issue.id} \u2014 implementing...`);
2253
2322
  log(`Implementing in worktree... (log: ${logFile})`);
2254
2323
  initLogFile(logFile);
2255
2324
  const result = await runWithFallback(models, prompt, {
@@ -2259,6 +2328,7 @@ async function runWorktreeSession(config2, issue, logFile, session, models) {
2259
2328
  issueId: issue.id,
2260
2329
  overseer: config2.overseer
2261
2330
  });
2331
+ stopSpinner();
2262
2332
  try {
2263
2333
  appendFileSync6(
2264
2334
  logFile,
@@ -2279,7 +2349,9 @@ ${result.output}
2279
2349
  await cleanupWorktree(repoPath, worktreePath);
2280
2350
  return { success: false, providerUsed: result.providerUsed, prUrls: [], fallback: result };
2281
2351
  }
2352
+ startSpinner(`${issue.id} \u2014 validating tests...`);
2282
2353
  const testsPassed = await runTestValidation(worktreePath);
2354
+ stopSpinner();
2283
2355
  if (!testsPassed) {
2284
2356
  error(`Tests failed for ${issue.id}. Blocking PR creation.`);
2285
2357
  await cleanupWorktree(repoPath, worktreePath);
@@ -2299,6 +2371,7 @@ ${result.output}
2299
2371
  );
2300
2372
  }
2301
2373
  }
2374
+ startSpinner(`${issue.id} \u2014 pushing...`);
2302
2375
  const pushResult = await pushWithRecovery({
2303
2376
  branch: effectiveBranch,
2304
2377
  cwd: worktreePath,
@@ -2308,12 +2381,14 @@ ${result.output}
2308
2381
  issueId: issue.id,
2309
2382
  overseer: config2.overseer
2310
2383
  });
2384
+ stopSpinner();
2311
2385
  if (!pushResult.success) {
2312
2386
  error(`Failed to push branch to remote: ${pushResult.error}`);
2313
2387
  cleanupManifest(worktreePath);
2314
2388
  await cleanupWorktree(repoPath, worktreePath);
2315
2389
  return { success: false, providerUsed: result.providerUsed, prUrls: [], fallback: result };
2316
2390
  }
2391
+ startSpinner(`${issue.id} \u2014 creating PR...`);
2317
2392
  const prTitle = manifest?.prTitle ?? readPrTitle(worktreePath) ?? issue.title;
2318
2393
  cleanupPrTitle(worktreePath);
2319
2394
  cleanupManifest(worktreePath);
@@ -2336,6 +2411,7 @@ ${result.output}
2336
2411
  } catch (err) {
2337
2412
  error(`Failed to create PR: ${err instanceof Error ? err.message : String(err)}`);
2338
2413
  }
2414
+ stopSpinner();
2339
2415
  await cleanupWorktree(repoPath, worktreePath);
2340
2416
  ok(`Session ${session} complete for ${issue.id}`);
2341
2417
  return { success: true, providerUsed: result.providerUsed, prUrls, fallback: result };
@@ -2344,6 +2420,7 @@ async function runWorktreeMultiRepoSession(config2, issue, logFile, session, mod
2344
2420
  const workspace = resolve5(config2.workspace);
2345
2421
  cleanupManifest(workspace);
2346
2422
  const prompt = buildWorktreeMultiRepoPrompt(issue, config2);
2423
+ startSpinner(`${issue.id} \u2014 implementing...`);
2347
2424
  log(`Multi-repo worktree session for ${issue.id} (agent selects repo and branch name)`);
2348
2425
  log(`Implementing (agent selects repo)... (log: ${logFile})`);
2349
2426
  initLogFile(logFile);
@@ -2354,6 +2431,7 @@ async function runWorktreeMultiRepoSession(config2, issue, logFile, session, mod
2354
2431
  issueId: issue.id,
2355
2432
  overseer: config2.overseer
2356
2433
  });
2434
+ stopSpinner();
2357
2435
  try {
2358
2436
  appendFileSync6(
2359
2437
  logFile,
@@ -2387,13 +2465,16 @@ ${result.output}
2387
2465
  if (!hasWorktree) {
2388
2466
  warn(`Worktree not found at ${worktreePath} \u2014 using repo root for git operations`);
2389
2467
  }
2468
+ startSpinner(`${issue.id} \u2014 validating tests...`);
2390
2469
  const testsPassed = await runTestValidation(effectiveCwd);
2470
+ stopSpinner();
2391
2471
  if (!testsPassed) {
2392
2472
  error(`Tests failed for ${issue.id}. Blocking PR creation.`);
2393
2473
  if (hasWorktree) await cleanupWorktree(manifest.repoPath, worktreePath);
2394
2474
  cleanupManifest(workspace);
2395
2475
  return { success: false, providerUsed: result.providerUsed, prUrls: [], fallback: result };
2396
2476
  }
2477
+ startSpinner(`${issue.id} \u2014 pushing...`);
2397
2478
  const pushResult = await pushWithRecovery({
2398
2479
  branch: manifest.branch,
2399
2480
  cwd: effectiveCwd,
@@ -2403,12 +2484,14 @@ ${result.output}
2403
2484
  issueId: issue.id,
2404
2485
  overseer: config2.overseer
2405
2486
  });
2487
+ stopSpinner();
2406
2488
  if (!pushResult.success) {
2407
2489
  error(`Failed to push branch to remote: ${pushResult.error}`);
2408
2490
  if (hasWorktree) await cleanupWorktree(manifest.repoPath, worktreePath);
2409
2491
  cleanupManifest(workspace);
2410
2492
  return { success: false, providerUsed: result.providerUsed, prUrls: [], fallback: result };
2411
2493
  }
2494
+ startSpinner(`${issue.id} \u2014 creating PR...`);
2412
2495
  const prTitle = manifest.prTitle ?? issue.title;
2413
2496
  const prUrls = [];
2414
2497
  try {
@@ -2429,6 +2512,7 @@ ${result.output}
2429
2512
  } catch (err) {
2430
2513
  error(`Failed to create PR: ${err instanceof Error ? err.message : String(err)}`);
2431
2514
  }
2515
+ stopSpinner();
2432
2516
  cleanupManifest(workspace);
2433
2517
  if (hasWorktree) await cleanupWorktree(manifest.repoPath, worktreePath);
2434
2518
  ok(`Session ${session} complete for ${issue.id}`);
@@ -2444,8 +2528,10 @@ async function runBranchSession(config2, issue, logFile, session, models) {
2444
2528
  const prompt = buildImplementPrompt(issue, config2, testRunner);
2445
2529
  const repo = findRepoConfig(config2, issue);
2446
2530
  if (repo?.lifecycle) {
2531
+ startSpinner(`${issue.id} \u2014 starting resources...`);
2447
2532
  const cwd = resolve5(workspace, repo.path);
2448
2533
  const started = await startResources(repo, cwd);
2534
+ stopSpinner();
2449
2535
  if (!started) {
2450
2536
  error(`Lifecycle startup failed for ${issue.id}. Aborting session.`);
2451
2537
  return {
@@ -2462,6 +2548,7 @@ async function runBranchSession(config2, issue, logFile, session, models) {
2462
2548
  };
2463
2549
  }
2464
2550
  }
2551
+ startSpinner(`${issue.id} \u2014 implementing...`);
2465
2552
  log(`Implementing... (log: ${logFile})`);
2466
2553
  initLogFile(logFile);
2467
2554
  const result = await runWithFallback(models, prompt, {
@@ -2471,6 +2558,7 @@ async function runBranchSession(config2, issue, logFile, session, models) {
2471
2558
  issueId: issue.id,
2472
2559
  overseer: config2.overseer
2473
2560
  });
2561
+ stopSpinner();
2474
2562
  try {
2475
2563
  appendFileSync6(
2476
2564
  logFile,
@@ -2490,7 +2578,9 @@ ${result.output}
2490
2578
  error(`Session ${session} failed for ${issue.id}. Check ${logFile}`);
2491
2579
  return { success: false, providerUsed: result.providerUsed, prUrls: [], fallback: result };
2492
2580
  }
2581
+ startSpinner(`${issue.id} \u2014 validating tests...`);
2493
2582
  const testsPassed = await runTestValidation(workspace);
2583
+ stopSpinner();
2494
2584
  if (!testsPassed) {
2495
2585
  error(`Tests failed for ${issue.id}. Blocking PR creation.`);
2496
2586
  cleanupManifest(workspace);
@@ -2513,6 +2603,7 @@ ${result.output}
2513
2603
  ok(`Session ${session} complete for ${issue.id}`);
2514
2604
  return { success: true, providerUsed: result.providerUsed, prUrls: [], fallback: result };
2515
2605
  }
2606
+ startSpinner(`${issue.id} \u2014 creating PR...`);
2516
2607
  const prTitle = manifest?.prTitle ?? readPrTitle(workspace) ?? issue.title;
2517
2608
  cleanupPrTitle(workspace);
2518
2609
  const prUrls = [];
@@ -2538,6 +2629,7 @@ ${result.output}
2538
2629
  error(`Failed to create PR: ${err instanceof Error ? err.message : String(err)}`);
2539
2630
  }
2540
2631
  }
2632
+ stopSpinner();
2541
2633
  ok(`Session ${session} complete for ${issue.id}`);
2542
2634
  return { success: true, providerUsed: result.providerUsed, prUrls, fallback: result };
2543
2635
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tarcisiopgs/lisa",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "Deterministic autonomous issue resolver — structured AI agent loop for Linear/Trello",
5
5
  "license": "MIT",
6
6
  "type": "module",