@tarcisiopgs/lisa 0.9.4 → 0.9.6

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 +126 -20
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -531,6 +531,16 @@ This project uses **${testRunner}** as its test runner.
531
531
  - Do NOT skip writing tests \u2014 the PR will be blocked if tests are missing or failing.
532
532
  `;
533
533
  }
534
+ function buildPreCommitHookInstructions() {
535
+ return `
536
+ **Pre-commit hooks:**
537
+ If \`git commit\` fails due to a pre-commit hook (e.g. husky), read the error output carefully and fix the underlying issue:
538
+ - Linter/formatter failures \u2192 run the project's lint/format commands, then re-stage and retry the commit.
539
+ - Code generation errors (e.g. stale Prisma client) \u2192 run the required generation command (e.g. \`npx prisma generate\`), then re-stage and retry.
540
+ - Type errors \u2192 fix the type issues in the source files, then re-stage and retry.
541
+ Do NOT skip or bypass hooks (no \`--no-verify\`). Fix the root cause and retry.
542
+ `;
543
+ }
534
544
  function buildReadmeInstructions() {
535
545
  return `
536
546
  **README.md Evaluation:**
@@ -565,6 +575,7 @@ function buildWorktreeMultiRepoPrompt(issue, config2) {
565
575
  ].join("\n");
566
576
  }).join("\n\n");
567
577
  const readmeBlock = buildReadmeInstructions();
578
+ const hookBlock = buildPreCommitHookInstructions();
568
579
  const manifestPath = join(workspace, ".lisa-manifest.json");
569
580
  return `You are an autonomous implementation agent working in a multi-repository workspace.
570
581
  Your job is to determine the correct repository, create an English-named branch, implement the issue, commit, and write a manifest file.
@@ -610,7 +621,7 @@ ${repoBlock}
610
621
  - Follow the implementation instructions exactly
611
622
  - Verify each acceptance criteria (if present)
612
623
  - Respect any stack or technical constraints (if present)
613
- ${readmeBlock}
624
+ ${readmeBlock}${hookBlock}
614
625
  5. **Validate**: Run the project's linter/typecheck/tests if available:
615
626
  - Check \`package.json\` for lint, typecheck, check, or test scripts.
616
627
  - Run whichever validation scripts exist (e.g., \`npm run lint\`, \`npm run typecheck\`, \`npm run test\`).
@@ -647,6 +658,7 @@ ${readmeBlock}
647
658
  function buildWorktreePrompt(issue, testRunner) {
648
659
  const testBlock = buildTestInstructions(testRunner ?? null);
649
660
  const readmeBlock = buildReadmeInstructions();
661
+ const hookBlock = buildPreCommitHookInstructions();
650
662
  return `You are an autonomous implementation agent. Your job is to implement a single
651
663
  issue, validate it, commit, and push the branch.
652
664
 
@@ -670,7 +682,7 @@ ${issue.description}
670
682
  - Follow the implementation instructions exactly
671
683
  - Verify each acceptance criteria (if present)
672
684
  - Respect any stack or technical constraints (if present)
673
- ${testBlock}${readmeBlock}
685
+ ${testBlock}${readmeBlock}${hookBlock}
674
686
  2. **Validate**: Run the project's linter/typecheck/tests if available:
675
687
  - Check \`package.json\` (or equivalent) for lint, typecheck, check, or test scripts.
676
688
  - Run whichever validation scripts exist (e.g., \`npm run lint\`, \`npm run typecheck\`).
@@ -711,6 +723,7 @@ function buildBranchPrompt(issue, config2, testRunner) {
711
723
  const baseBranchInstruction = config2.repos.length > 0 ? "From the repo's base branch (listed above)" : `From \`${config2.base_branch}\``;
712
724
  const testBlock = buildTestInstructions(testRunner ?? null);
713
725
  const readmeBlock = buildReadmeInstructions();
726
+ const hookBlock = buildPreCommitHookInstructions();
714
727
  const manifestPath = join(workspace, ".lisa-manifest.json");
715
728
  return `You are an autonomous implementation agent. Your job is to implement a single
716
729
  issue, validate it, commit, and push the branch.
@@ -741,7 +754,7 @@ ${repoEntries}
741
754
  - Follow the implementation instructions exactly
742
755
  - Verify each acceptance criteria (if present)
743
756
  - Respect any stack or technical constraints (if present)
744
- ${testBlock}${readmeBlock}
757
+ ${testBlock}${readmeBlock}${hookBlock}
745
758
  4. **Validate**: Run the project's linter/typecheck/tests if available:
746
759
  - Check \`package.json\` (or equivalent) for lint, typecheck, check, or test scripts.
747
760
  - Run whichever validation scripts exist (e.g., \`npm run lint\`, \`npm run typecheck\`).
@@ -771,6 +784,32 @@ ${testBlock}${readmeBlock}
771
784
  - Do NOT create pull requests \u2014 the caller handles that.
772
785
  - Do NOT update the issue tracker \u2014 the caller handles that.`;
773
786
  }
787
+ function buildPushRecoveryPrompt(hookErrors) {
788
+ return `The previous \`git push\` failed because a pre-push hook rejected the push.
789
+ Here is the full error output:
790
+
791
+ \`\`\`
792
+ ${hookErrors}
793
+ \`\`\`
794
+
795
+ ## Instructions
796
+
797
+ 1. **Read the errors** above carefully and identify the root cause.
798
+ 2. **Fix the issue** \u2014 common fixes include:
799
+ - Run linters/formatters (e.g. \`npm run lint -- --fix\`, \`npm run format\`)
800
+ - Run code generation (e.g. \`npx prisma generate\`, \`npm run codegen\`)
801
+ - Fix type errors in the source files
802
+ - Fix failing tests
803
+ 3. **Amend the commit** so the fix is included:
804
+ \`\`\`
805
+ git add -A && git commit --amend --no-edit
806
+ \`\`\`
807
+ 4. **Do NOT push** \u2014 the caller handles pushing after you finish.
808
+ 5. **Do NOT create pull requests** \u2014 the caller handles that.
809
+ 6. **Do NOT update the issue tracker** \u2014 the caller handles that.
810
+
811
+ Focus only on fixing the hook errors. Do not make unrelated changes.`;
812
+ }
774
813
 
775
814
  // src/guardrails.ts
776
815
  import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
@@ -1792,6 +1831,58 @@ function cleanupManifest(dir) {
1792
1831
  } catch {
1793
1832
  }
1794
1833
  }
1834
+ var MAX_PUSH_RETRIES = 2;
1835
+ var HOOK_ERROR_PATTERNS = [
1836
+ /husky - pre-push/i,
1837
+ /husky - pre-commit/i,
1838
+ /pre-push hook/i,
1839
+ /pre-commit hook/i,
1840
+ /hook declined/i,
1841
+ /hook.*failed/i,
1842
+ /hook.*exited with/i,
1843
+ /hook.*returned.*exit code/i
1844
+ ];
1845
+ function isHookError(errorMessage) {
1846
+ return HOOK_ERROR_PATTERNS.some((pattern) => pattern.test(errorMessage));
1847
+ }
1848
+ async function pushWithRecovery(opts) {
1849
+ for (let attempt = 0; attempt <= MAX_PUSH_RETRIES; attempt++) {
1850
+ try {
1851
+ await execa3("git", ["push", "-u", "origin", opts.branch], { cwd: opts.cwd });
1852
+ return { success: true };
1853
+ } catch (err) {
1854
+ const errorMessage = err instanceof Error ? err.message : String(err);
1855
+ if (!isHookError(errorMessage)) {
1856
+ return { success: false, error: errorMessage };
1857
+ }
1858
+ if (attempt >= MAX_PUSH_RETRIES) {
1859
+ return {
1860
+ success: false,
1861
+ error: `Push hook failed after ${MAX_PUSH_RETRIES} recovery attempts: ${errorMessage}`
1862
+ };
1863
+ }
1864
+ warn(
1865
+ `Push hook failed (attempt ${attempt + 1}/${MAX_PUSH_RETRIES}). Re-invoking provider to fix...`
1866
+ );
1867
+ const recoveryPrompt = buildPushRecoveryPrompt(errorMessage);
1868
+ const result = await runWithFallback(opts.models, recoveryPrompt, {
1869
+ logFile: opts.logFile,
1870
+ cwd: opts.cwd,
1871
+ guardrailsDir: opts.guardrailsDir,
1872
+ issueId: opts.issueId,
1873
+ overseer: opts.overseer
1874
+ });
1875
+ if (!result.success) {
1876
+ return {
1877
+ success: false,
1878
+ error: `Provider failed to fix push hook errors: ${result.output}`
1879
+ };
1880
+ }
1881
+ ok("Provider finished recovery. Retrying push...");
1882
+ }
1883
+ }
1884
+ return { success: false, error: "Push recovery exhausted retries" };
1885
+ }
1795
1886
  function installSignalHandlers() {
1796
1887
  const cleanup = async (signal) => {
1797
1888
  if (shuttingDown) {
@@ -2158,12 +2249,17 @@ ${result.output}
2158
2249
  );
2159
2250
  }
2160
2251
  }
2161
- try {
2162
- await execa3("git", ["push", "-u", "origin", effectiveBranch], { cwd: worktreePath });
2163
- } catch (err) {
2164
- error(
2165
- `Failed to push branch to remote: ${err instanceof Error ? err.message : String(err)}`
2166
- );
2252
+ const pushResult = await pushWithRecovery({
2253
+ branch: effectiveBranch,
2254
+ cwd: worktreePath,
2255
+ models,
2256
+ logFile,
2257
+ guardrailsDir: repoPath,
2258
+ issueId: issue.id,
2259
+ overseer: config2.overseer
2260
+ });
2261
+ if (!pushResult.success) {
2262
+ error(`Failed to push branch to remote: ${pushResult.error}`);
2167
2263
  cleanupManifest(worktreePath);
2168
2264
  await cleanupWorktree(repoPath, worktreePath);
2169
2265
  return { success: false, providerUsed: result.providerUsed, prUrls: [], fallback: result };
@@ -2236,27 +2332,37 @@ ${result.output}
2236
2332
  ok(`Provider chose repo: ${manifest.repoPath}, branch: ${manifest.branch}`);
2237
2333
  const worktreePath = join7(manifest.repoPath, ".worktrees", manifest.branch);
2238
2334
  const baseBranch = resolveBaseBranch(config2, manifest.repoPath);
2239
- const testsPassed = await runTestValidation(worktreePath);
2335
+ const hasWorktree = existsSync6(worktreePath);
2336
+ const effectiveCwd = hasWorktree ? worktreePath : manifest.repoPath;
2337
+ if (!hasWorktree) {
2338
+ warn(`Worktree not found at ${worktreePath} \u2014 using repo root for git operations`);
2339
+ }
2340
+ const testsPassed = await runTestValidation(effectiveCwd);
2240
2341
  if (!testsPassed) {
2241
2342
  error(`Tests failed for ${issue.id}. Blocking PR creation.`);
2242
- await cleanupWorktree(manifest.repoPath, worktreePath);
2343
+ if (hasWorktree) await cleanupWorktree(manifest.repoPath, worktreePath);
2243
2344
  cleanupManifest(workspace);
2244
2345
  return { success: false, providerUsed: result.providerUsed, prUrls: [], fallback: result };
2245
2346
  }
2246
- try {
2247
- await execa3("git", ["push", "-u", "origin", manifest.branch], { cwd: worktreePath });
2248
- } catch (err) {
2249
- error(
2250
- `Failed to push branch to remote: ${err instanceof Error ? err.message : String(err)}`
2251
- );
2252
- await cleanupWorktree(manifest.repoPath, worktreePath);
2347
+ const pushResult = await pushWithRecovery({
2348
+ branch: manifest.branch,
2349
+ cwd: effectiveCwd,
2350
+ models,
2351
+ logFile,
2352
+ guardrailsDir: manifest.repoPath,
2353
+ issueId: issue.id,
2354
+ overseer: config2.overseer
2355
+ });
2356
+ if (!pushResult.success) {
2357
+ error(`Failed to push branch to remote: ${pushResult.error}`);
2358
+ if (hasWorktree) await cleanupWorktree(manifest.repoPath, worktreePath);
2253
2359
  cleanupManifest(workspace);
2254
2360
  return { success: false, providerUsed: result.providerUsed, prUrls: [], fallback: result };
2255
2361
  }
2256
2362
  const prTitle = manifest.prTitle ?? issue.title;
2257
2363
  const prUrls = [];
2258
2364
  try {
2259
- const repoInfo = await getRepoInfo(worktreePath);
2365
+ const repoInfo = await getRepoInfo(effectiveCwd);
2260
2366
  const pr = await createPullRequest(
2261
2367
  {
2262
2368
  owner: repoInfo.owner,
@@ -2274,7 +2380,7 @@ ${result.output}
2274
2380
  error(`Failed to create PR: ${err instanceof Error ? err.message : String(err)}`);
2275
2381
  }
2276
2382
  cleanupManifest(workspace);
2277
- await cleanupWorktree(manifest.repoPath, worktreePath);
2383
+ if (hasWorktree) await cleanupWorktree(manifest.repoPath, worktreePath);
2278
2384
  ok(`Session ${session} complete for ${issue.id}`);
2279
2385
  return { success: true, providerUsed: result.providerUsed, prUrls, fallback: result };
2280
2386
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tarcisiopgs/lisa",
3
- "version": "0.9.4",
3
+ "version": "0.9.6",
4
4
  "description": "Deterministic autonomous issue resolver — structured AI agent loop for Linear/Trello",
5
5
  "license": "MIT",
6
6
  "type": "module",