@tarcisiopgs/lisa 1.33.3 → 1.35.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
@@ -28,7 +28,7 @@ lisa # start the agent loop
28
28
  ## How It Works
29
29
 
30
30
  ```
31
- Plan → Create issues → Fetch → Implement → Push → Open PR → Update board → Next
31
+ Plan → Create issues → Fetch → Implement → Push → Open PR → CI Monitor → Review Monitor → Update board → Next
32
32
  ```
33
33
 
34
34
  Lisa starts and shows a Kanban board. If the queue is empty, press `n` to plan — describe a goal and the AI brainstorms with you (asking clarifying questions), presents its understanding for your confirmation, then decomposes the goal into atomic issues created directly in your tracker. You can review, edit, reorder, delete, or regenerate the plan with feedback before approving. 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.
@@ -47,11 +47,16 @@ If something fails — pre-push hooks, quota limits, stuck processes — Lisa ha
47
47
  - **Model fallback** — chain models; transient errors (429, quota, timeout) auto-switch to the next
48
48
  - **Real-time TUI** — Kanban board with live provider output, plan mode, merge PRs with `m`
49
49
  - **CI monitoring** — polls CI after PR creation, re-invokes the agent to fix failures automatically
50
+ - **Review monitoring** — polls PR reviews after CI, auto-addresses reviewer feedback (GitHub)
51
+ - **Reaction engine** — configurable actions for CI failures, review changes, stuck agents
52
+ - **Session state tracking** — real-time visibility into agent pipeline phase (implementing → validating → CI → review)
53
+ - **Smart activity detection** — reads agent session logs to prevent false stuck kills during analysis phases
50
54
  - **Progress comments** — posts real-time status updates on issues as Lisa works through stages
51
55
  - **Context enrichment** — greps for issue-related files and surfaces them in the agent prompt
52
56
  - **PR reviewers & assignees** — auto-request reviews and assign PRs via config; `self` keyword resolves to the authenticated user
53
57
  - **Self-healing** — orphan recovery on startup, push failure retry, stuck process detection
54
58
  - **Guardrails** — past failures are injected into future prompts to avoid repeating mistakes
59
+ - **Lineage context** — plan-decomposed issues get sibling task awareness, preventing duplicate work in concurrent mode
55
60
  - **Project context** — auto-generates `.lisa/context.md` with your stack, conventions, and constraints
56
61
 
57
62
  ## Providers
@@ -109,6 +114,7 @@ lisa config --set loop.cooldown=5 # set nested config values
109
114
  lisa doctor # diagnose setup issues (config, provider, env, git)
110
115
  lisa context refresh # regenerate project context
111
116
  lisa feedback --pr URL # inject PR review feedback into guardrails
117
+ lisa sessions # list active session states (supports --json)
112
118
  ```
113
119
 
114
120
  Append `--json` to any command for machine-readable output. Use `--verbose` / `--quiet` to control log verbosity.
@@ -252,6 +258,31 @@ ci_monitor:
252
258
  poll_timeout: 600 # max seconds to wait for CI
253
259
  block_on_failure: false # revert issue if CI never passes
254
260
 
261
+ # Post-PR review monitoring (GitHub only)
262
+ review_monitor:
263
+ enabled: true
264
+ max_retries: 2 # fix attempts on review changes (default: 2)
265
+ poll_interval: 60 # seconds between review checks (default: 60)
266
+ poll_timeout: 3600 # max seconds to wait for review (default: 3600)
267
+
268
+ # Configurable reactions (override defaults)
269
+ reactions:
270
+ ci_failed:
271
+ action: reinvoke # reinvoke | notify | skip
272
+ max_retries: 3
273
+ escalate_after: 30m
274
+ changes_requested:
275
+ action: reinvoke
276
+ max_retries: 2
277
+ escalate_after: 1h
278
+ approved:
279
+ action: notify
280
+ agent_stuck:
281
+ action: notify
282
+ validation_failed:
283
+ action: reinvoke
284
+ max_retries: 2
285
+
255
286
  progress_comments:
256
287
  enabled: true # post real-time status on issues
257
288
 
@@ -275,7 +306,7 @@ hooks:
275
306
  After the agent implements an issue, Lisa runs a multi-stage validation pipeline before creating a PR:
276
307
 
277
308
  ```
278
- Agent implements → Proof of Work (lint/test/typecheck) → Spec Compliance → PR
309
+ Agent implements → Proof of Work (lint/test/typecheck) → Spec Compliance → PR → CI Monitor → Review Monitor
279
310
  ```
280
311
 
281
312
  **Proof of Work** runs configured shell commands (lint, typecheck, test). If any fail, the agent is re-invoked with the error output to fix the issue.
@@ -318,9 +349,10 @@ The real-time Kanban board shows issue progress, streams provider output, and de
318
349
  | Key | Action | Key | Action |
319
350
  |-----|--------|-----|--------|
320
351
  | `←` `→` | Switch columns | `k` | Kill current issue |
321
- | `↑` `↓` | Navigate cards | `n` | Open plan mode |
322
- | `↵` | Open detail view | `r` | Run (from idle) |
323
- | `p` | Pause / resume | `q` | Quit |
352
+ | `↑` `↓` | Navigate cards | `s` | Skip current issue |
353
+ | `↵` | Open detail view | `n` | Open plan mode |
354
+ | `p` | Pause / resume | `r` | Run (from idle) |
355
+ | `m` | Merge PR (opens detail + triggers merge) | `q` | Quit |
324
356
 
325
357
  **Detail view**
326
358
 
@@ -3,15 +3,16 @@ import {
3
3
  buildContextMdBlock,
4
4
  readContext,
5
5
  resolveModels,
6
- runWithFallback
7
- } from "./chunk-5724FKM6.js";
6
+ runWithFallback,
7
+ saveLineage
8
+ } from "./chunk-6VIN5PMW.js";
8
9
  import {
9
10
  error,
10
11
  log,
11
12
  normalizeLabels,
12
13
  ok,
13
14
  warn
14
- } from "./chunk-PPRXJHPW.js";
15
+ } from "./chunk-V44FTYWZ.js";
15
16
 
16
17
  // src/cli/error.ts
17
18
  var CliError = class extends Error {
@@ -163,7 +164,7 @@ function ensureAcceptanceCriteria(description, criteria) {
163
164
 
164
165
  ${checklist}`;
165
166
  }
166
- async function createPlanIssues(source, config, plan) {
167
+ async function createPlanIssues(source, config, plan, workspace) {
167
168
  if (!source.createIssue) {
168
169
  throw new Error(`Source "${source.name}" does not support createIssue`);
169
170
  }
@@ -219,6 +220,21 @@ _Depends on: ${depRefs}_`;
219
220
  );
220
221
  }
221
222
  }
223
+ if (createdIds.length > 1 && workspace) {
224
+ const lineage = {
225
+ planId: plan.createdAt,
226
+ goal: plan.goal,
227
+ issues: sorted.map((issue, idx) => ({
228
+ id: createdIds[idx] ?? `unknown-${idx}`,
229
+ title: issue.title,
230
+ order: issue.order
231
+ }))
232
+ };
233
+ try {
234
+ saveLineage(workspace, lineage);
235
+ } catch {
236
+ }
237
+ }
222
238
  return createdIds;
223
239
  }
224
240
 
@@ -11,7 +11,7 @@ import {
11
11
  normalizeLabels,
12
12
  ok,
13
13
  warn
14
- } from "./chunk-PPRXJHPW.js";
14
+ } from "./chunk-V44FTYWZ.js";
15
15
  import {
16
16
  appendEntry,
17
17
  buildGuardrailsSection,
@@ -1180,7 +1180,8 @@ function buildPrompt(opts) {
1180
1180
  step,
1181
1181
  previousResults = [],
1182
1182
  isLastStep = false,
1183
- relevantFiles
1183
+ relevantFiles,
1184
+ lineageBlock
1184
1185
  } = opts;
1185
1186
  let manifestPath = opts.manifestPath;
1186
1187
  if (!manifestPath && variant === "branch" && config) {
@@ -1194,6 +1195,7 @@ function buildPrompt(opts) {
1194
1195
  const depBlock = issue.dependency ? buildDependencyContext(issue.dependency) : "";
1195
1196
  const specWarningBlock = buildSpecWarningBlock(issue.specWarning);
1196
1197
  const contextMdBlock = buildContextMdBlock(repoContextMd ?? null);
1198
+ const dodBlock = buildDefinitionOfDone(issue.description ?? "");
1197
1199
  const relevantFilesBlock = relevantFiles ?? "";
1198
1200
  const prBase = issue.dependency ? issue.dependency.branch : baseBranch;
1199
1201
  const prCreateBlock = buildPrCreateInstruction(platform2, prBase);
@@ -1266,10 +1268,7 @@ ${repoEntries}
1266
1268
  - Verify each acceptance criteria (if present)
1267
1269
  - Respect any stack or technical constraints (if present)
1268
1270
  ${testBlock}${hookBlock}
1269
- 4. **Validate**: Run the project's linter/typecheck/tests if available:
1270
- - Check \`package.json\` (or equivalent) for lint, typecheck, check, or test scripts.
1271
- - Run whichever validation scripts exist (e.g., \`npm run lint\`, \`npm run typecheck\`).
1272
- - Fix any errors before proceeding.
1271
+ 4. ${buildValidateStep(testRunner ?? null, pm)}
1273
1272
  ${readmeBlock}
1274
1273
  **CRITICAL \u2014 Do NOT stop here. The following steps (commit, push, PR, manifest) are MANDATORY. Skipping them means the task has FAILED.**
1275
1274
 
@@ -1305,10 +1304,7 @@ ${readmeBlock}
1305
1304
  - Follow the implementation instructions exactly
1306
1305
  - Verify each acceptance criteria relevant to your scope
1307
1306
  ${testBlock}${hookBlock}
1308
- 2. **Validate**: Run the project's linter/typecheck/tests if available:
1309
- - Check \`package.json\` (or equivalent) for lint, typecheck, check, or test scripts.
1310
- - Run whichever validation scripts exist (e.g., \`npm run lint\`, \`npm run typecheck\`).
1311
- - Fix any errors before proceeding.
1307
+ 2. ${buildValidateStep(testRunner ?? null, pm)}
1312
1308
  ${readmeBlock}
1313
1309
  **CRITICAL \u2014 Do NOT stop here. The following steps (commit, push, PR, manifest) are MANDATORY. Skipping them means the task has FAILED.**
1314
1310
 
@@ -1341,10 +1337,7 @@ ${trackerStep}
1341
1337
  - Verify each acceptance criteria (if present)
1342
1338
  - Respect any stack or technical constraints (if present)
1343
1339
  ${testBlock}${hookBlock}
1344
- 2. **Validate**: Run the project's linter/typecheck/tests if available:
1345
- - Check \`package.json\` (or equivalent) for lint, typecheck, check, or test scripts.
1346
- - Run whichever validation scripts exist (e.g., \`npm run lint\`, \`npm run typecheck\`).
1347
- - Fix any errors before proceeding.
1340
+ 2. ${buildValidateStep(testRunner ?? null, pm)}
1348
1341
  ${readmeBlock}
1349
1342
  **CRITICAL \u2014 Do NOT stop here. The following steps (commit, push, PR, manifest) are MANDATORY. Skipping them means the task has FAILED.**
1350
1343
 
@@ -1384,6 +1377,7 @@ ${branchRenameInstruction}
1384
1377
  rulesSection = buildRulesSection(projectContext?.environment);
1385
1378
  }
1386
1379
  const taskHint = buildTaskTypeHint(issue.title);
1380
+ const lineageSection = lineageBlock ?? "";
1387
1381
  return `${preamble}${taskHint}
1388
1382
  ${workContext}${contextBlock ? `
1389
1383
  ${contextBlock}
@@ -1391,6 +1385,8 @@ ${contextBlock}
1391
1385
  ${relevantFilesBlock}
1392
1386
  ` : ""}${depBlock ? `
1393
1387
  ${depBlock}
1388
+ ` : ""}${lineageSection ? `
1389
+ ${lineageSection}
1394
1390
  ` : ""}
1395
1391
  ## Issue
1396
1392
 
@@ -1401,7 +1397,7 @@ ${depBlock}
1401
1397
  ### Description
1402
1398
 
1403
1399
  ${issue.description}
1404
- ${specWarningBlock}${scopeSection}${GUARDRAILS_PLACEHOLDER}
1400
+ ${specWarningBlock}${dodBlock}${scopeSection}${GUARDRAILS_PLACEHOLDER}
1405
1401
  ${instructions}
1406
1402
 
1407
1403
  ${rulesSection}
@@ -1415,7 +1411,7 @@ Before finishing, verify ALL of the following are true:
1415
1411
  - [ ] Manifest file is written with \`prUrl\` field
1416
1412
  If ANY item is unchecked, go back and complete it. Do NOT finish with incomplete steps.`;
1417
1413
  }
1418
- function buildImplementPrompt(issue, config, testRunner, pm, projectContext, cwd, manifestPath, repoContextMd, relevantFiles) {
1414
+ function buildImplementPrompt(issue, config, testRunner, pm, projectContext, cwd, manifestPath, repoContextMd, relevantFiles, lineageBlock) {
1419
1415
  const workspace = resolve(config.workspace);
1420
1416
  const resolvedManifestPath = manifestPath ?? getManifestPath(workspace);
1421
1417
  if (config.workflow === "worktree") {
@@ -1430,7 +1426,8 @@ function buildImplementPrompt(issue, config, testRunner, pm, projectContext, cwd
1430
1426
  cwd,
1431
1427
  platform: config.platform,
1432
1428
  repoContextMd,
1433
- relevantFiles
1429
+ relevantFiles,
1430
+ lineageBlock
1434
1431
  });
1435
1432
  }
1436
1433
  return buildPrompt({
@@ -1445,7 +1442,8 @@ function buildImplementPrompt(issue, config, testRunner, pm, projectContext, cwd
1445
1442
  platform: config.platform,
1446
1443
  repoContextMd,
1447
1444
  config,
1448
- relevantFiles
1445
+ relevantFiles,
1446
+ lineageBlock
1449
1447
  });
1450
1448
  }
1451
1449
  function buildNativeWorktreePrompt(issue, repoPath, testRunner, pm, baseBranch, projectContext, manifestPath, platform2 = "cli", repoContextMd, relevantFiles) {
@@ -1484,12 +1482,26 @@ function buildTestInstructions(testRunner, pm = "npm") {
1484
1482
  if (!testRunner) return "";
1485
1483
  const testCmd = pm === "bun" ? "bun run test" : `${pm} run test`;
1486
1484
  return `
1487
- **MANDATORY \u2014 Unit Tests:**
1488
- This project uses **${testRunner}** as its test runner.
1489
- - You MUST write unit tests (\`*.test.ts\`) for every new file or module you create.
1490
- - Tests should cover the main functionality, edge cases, and error scenarios.
1491
- - Run \`${testCmd}\` and ensure ALL tests pass before committing.
1492
- - Do NOT skip writing tests \u2014 the PR will be blocked if tests are missing or failing.
1485
+ **MANDATORY \u2014 Test-Driven Development (TDD):**
1486
+ This project uses **${testRunner}**. Follow the RED \u2192 GREEN \u2192 REFACTOR cycle strictly:
1487
+ 1. **RED**: Write the failing tests first \u2014 before writing any implementation code.
1488
+ Run \`${testCmd}\` and confirm the new tests fail. If they pass immediately, the tests are wrong.
1489
+ 2. **GREEN**: Write the minimum implementation to make the tests pass.
1490
+ Run \`${testCmd}\` \u2014 all tests must pass before continuing.
1491
+ 3. **REFACTOR**: Clean up the code without breaking tests. Run \`${testCmd}\` one final time to confirm.
1492
+ - Cover the main functionality, edge cases, and error scenarios.
1493
+ - Do NOT write implementation before tests. The PR will be blocked if tests are missing or written after the fact.
1494
+ `;
1495
+ }
1496
+ function buildDefinitionOfDone(description) {
1497
+ const criteria = description.split("\n").map((l) => l.trim()).filter((l) => /^- \[ \]/.test(l));
1498
+ if (criteria.length === 0) return "";
1499
+ return `
1500
+ ## Definition of Done
1501
+
1502
+ Verify each item before finishing:
1503
+
1504
+ ${criteria.join("\n")}
1493
1505
  `;
1494
1506
  }
1495
1507
  function buildSpecWarningBlock(warning) {
@@ -1525,6 +1537,14 @@ function buildEnvironmentDependencyRule(env) {
1525
1537
  }
1526
1538
  return "";
1527
1539
  }
1540
+ function buildValidateStep(testRunner, pm = "npm") {
1541
+ const testCmd = pm === "bun" ? "bun run test" : `${pm} run test`;
1542
+ const testLine = testRunner ? ` - Run \`${testCmd}\` \u2014 ALL tests must pass (final gate after the TDD cycle).
1543
+ ` : "";
1544
+ return `**Validate**: Confirm all quality gates before committing:
1545
+ ${testLine} - Run lint/typecheck scripts if available (e.g., \`npm run lint\`, \`npm run typecheck\`).
1546
+ - Fix every error. Do NOT commit with failing tests or lint errors.`;
1547
+ }
1528
1548
  function buildPreCommitHookInstructions() {
1529
1549
  return `
1530
1550
  **Pre-commit hooks:**
@@ -1731,7 +1751,7 @@ If ANY item is unchecked, go back and complete it. Do NOT finish with incomplete
1731
1751
  // src/providers/aider.ts
1732
1752
  import { mkdtempSync as mkdtempSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
1733
1753
  import { tmpdir as tmpdir2 } from "os";
1734
- import { join as join4 } from "path";
1754
+ import { join as join5 } from "path";
1735
1755
 
1736
1756
  // src/providers/output-buffer.ts
1737
1757
  import { appendFileSync } from "fs";
@@ -1791,7 +1811,7 @@ var OutputBuffer = class {
1791
1811
  import { execFile as execFile2, spawn as spawn3 } from "child_process";
1792
1812
  import { mkdtempSync, rmSync, writeFileSync } from "fs";
1793
1813
  import { tmpdir } from "os";
1794
- import { join as join3 } from "path";
1814
+ import { join as join4 } from "path";
1795
1815
  import { promisify as promisify2 } from "util";
1796
1816
 
1797
1817
  // src/session/overseer.ts
@@ -1833,6 +1853,13 @@ function createSessionTimeout(proc, timeoutSeconds) {
1833
1853
  };
1834
1854
  }
1835
1855
 
1856
+ // src/session/activity.ts
1857
+ import { closeSync, openSync, readdirSync as readdirSync2, readSync, statSync as statSync2 } from "fs";
1858
+ import { homedir } from "os";
1859
+ import { join as join3 } from "path";
1860
+ var TAIL_BYTES = 128 * 1024;
1861
+ var DEFAULT_IDLE_THRESHOLD_MS = 5 * 60 * 1e3;
1862
+
1836
1863
  // src/session/overseer.ts
1837
1864
  var execFileAsync = promisify(execFile);
1838
1865
  var STUCK_MESSAGE = "\n[lisa-overseer] Provider killed: no git changes detected within the stuck threshold. Eligible for fallback.\n";
@@ -2052,8 +2079,8 @@ async function isCommandAvailable(command, args = ["--version"]) {
2052
2079
  }
2053
2080
  async function runProviderProcess(config, prompt, opts) {
2054
2081
  const start = Date.now();
2055
- const tmpDir = mkdtempSync(join3(tmpdir(), "lisa-"));
2056
- const promptFile = join3(tmpDir, "prompt.md");
2082
+ const tmpDir = mkdtempSync(join4(tmpdir(), "lisa-"));
2083
+ const promptFile = join4(tmpDir, "prompt.md");
2057
2084
  writeFileSync(promptFile, prompt, { encoding: "utf-8", mode: 384 });
2058
2085
  try {
2059
2086
  const promptCatExpr = `"$(cat '${escapeShellPath(promptFile)}')"`;
@@ -2180,8 +2207,8 @@ var AiderProvider = class {
2180
2207
  try {
2181
2208
  if (opts.model) validateShellArg(opts.model, "model");
2182
2209
  const modelFlag = opts.model ? `--model ${opts.model}` : "";
2183
- const aiderTmpDir = mkdtempSync2(join4(tmpdir2(), "lisa-aider-"));
2184
- const aiderPromptFile = join4(aiderTmpDir, "prompt.md");
2210
+ const aiderTmpDir = mkdtempSync2(join5(tmpdir2(), "lisa-aider-"));
2211
+ const aiderPromptFile = join5(aiderTmpDir, "prompt.md");
2185
2212
  writeFileSync2(aiderPromptFile, prompt, { encoding: "utf-8", mode: 384 });
2186
2213
  const config = {
2187
2214
  name: "aider",
@@ -4310,9 +4337,9 @@ function resolveModels(config) {
4310
4337
 
4311
4338
  // src/session/context-manager.ts
4312
4339
  import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
4313
- import { join as join5 } from "path";
4340
+ import { join as join6 } from "path";
4314
4341
  function getContextPath(dir) {
4315
- return join5(dir, ".lisa", "context.md");
4342
+ return join6(dir, ".lisa", "context.md");
4316
4343
  }
4317
4344
  function contextExists(dir) {
4318
4345
  return existsSync3(getContextPath(dir));
@@ -4327,6 +4354,70 @@ function readContext(dir) {
4327
4354
  }
4328
4355
  }
4329
4356
 
4357
+ // src/plan/lineage.ts
4358
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2, readdirSync as readdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
4359
+ import { join as join7 } from "path";
4360
+ function lineageDir(workspace) {
4361
+ return join7(workspace, ".lisa", "lineage");
4362
+ }
4363
+ function sanitizePlanId(planId) {
4364
+ return planId.replace(/[^a-zA-Z0-9_\-.]/g, "_");
4365
+ }
4366
+ function lineagePath(workspace, planId) {
4367
+ return join7(lineageDir(workspace), `${sanitizePlanId(planId)}.json`);
4368
+ }
4369
+ function saveLineage(workspace, lineage) {
4370
+ const dir = lineageDir(workspace);
4371
+ if (!existsSync4(dir)) {
4372
+ mkdirSync2(dir, { recursive: true });
4373
+ }
4374
+ const path = lineagePath(workspace, lineage.planId);
4375
+ writeFileSync4(path, JSON.stringify(lineage, null, " "), "utf-8");
4376
+ }
4377
+ function loadLineageForIssue(workspace, issueId) {
4378
+ const dir = lineageDir(workspace);
4379
+ if (!existsSync4(dir)) return null;
4380
+ let files;
4381
+ try {
4382
+ files = readdirSync3(dir).filter((f) => f.endsWith(".json"));
4383
+ } catch {
4384
+ return null;
4385
+ }
4386
+ for (const file of files) {
4387
+ try {
4388
+ const raw = readFileSync4(join7(dir, file), "utf-8");
4389
+ const lineage = JSON.parse(raw);
4390
+ if (lineage.issues.some((issue) => issue.id === issueId)) {
4391
+ return lineage;
4392
+ }
4393
+ } catch {
4394
+ }
4395
+ }
4396
+ return null;
4397
+ }
4398
+ function buildLineagePromptBlock(lineage, currentIssueId) {
4399
+ if (lineage.issues.length <= 1) return "";
4400
+ const sorted = [...lineage.issues].sort((a, b) => a.order - b.order);
4401
+ const taskList = sorted.map((issue, idx) => {
4402
+ const marker = issue.id === currentIssueId ? " <-- (this task)" : "";
4403
+ return ` ${idx + 1}. [${issue.id}] ${issue.title}${marker}`;
4404
+ }).join("\n");
4405
+ const siblings = sorted.filter((issue) => issue.id !== currentIssueId).map((issue) => `- [${issue.id}] ${issue.title}`).join("\n");
4406
+ return `## Task Hierarchy
4407
+
4408
+ **Goal:** ${lineage.goal}
4409
+
4410
+ This task is part of a decomposed plan with ${lineage.issues.length} subtasks:
4411
+
4412
+ ${taskList}
4413
+
4414
+ ## Parallel Work
4415
+
4416
+ The following sibling tasks may be running concurrently. Do NOT duplicate their work:
4417
+
4418
+ ${siblings}`;
4419
+ }
4420
+
4330
4421
  export {
4331
4422
  analyzeProject,
4332
4423
  runValidationCommands,
@@ -4359,5 +4450,8 @@ export {
4359
4450
  contextExists,
4360
4451
  readContext,
4361
4452
  WATCH_POLL_INTERVAL_MS,
4362
- resolveModels
4453
+ resolveModels,
4454
+ saveLineage,
4455
+ loadLineageForIssue,
4456
+ buildLineagePromptBlock
4363
4457
  };