@codyswann/lisa 4.74.8 → 4.75.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 (809) hide show
  1. package/.claude-plugin/marketplace.json +60 -0
  2. package/all/copy-overwrite/scripts/lib/npm-update-cancel-origin.mjs +4 -2
  3. package/all/copy-overwrite/scripts/lib/npm-update-checkpoint-codec.mjs +141 -0
  4. package/all/copy-overwrite/scripts/lib/npm-update-checkpoint.mjs +162 -116
  5. package/all/copy-overwrite/scripts/lib/npm-update-helper-inventory.mjs +1 -0
  6. package/all/copy-overwrite/scripts/lib/npm-update-recovery.mjs +9 -60
  7. package/all/copy-overwrite/scripts/npm-updater-helper-graph.json +18 -6
  8. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  9. package/dist/core/lisa-owned-hash-ledger.js +20 -0
  10. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  11. package/dist/core/lisa-skill-sources.d.ts +3 -3
  12. package/dist/core/lisa-skill-sources.d.ts.map +1 -1
  13. package/dist/core/lisa-skill-sources.js +3 -2
  14. package/dist/core/lisa-skill-sources.js.map +1 -1
  15. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  16. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  17. package/dist/core/upstream-evidence-manifest.js +739 -11
  18. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  19. package/package.json +4 -4
  20. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  21. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  22. package/plugins/lisa-agy/plugin.json +1 -1
  23. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  24. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  25. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  26. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  27. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  28. package/plugins/lisa-cdk-devin/.claude-plugin/plugin.json +11 -0
  29. package/plugins/lisa-cdk-devin/.devin-plugin/plugin.json +8 -0
  30. package/plugins/lisa-cdk-devin/skills/setup-aws-accounts/SKILL.md +292 -0
  31. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  32. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  33. package/plugins/lisa-devin/.claude-plugin/plugin.json +8 -0
  34. package/plugins/lisa-devin/.devin-plugin/plugin.json +8 -0
  35. package/plugins/lisa-devin/.mcp.json +8 -0
  36. package/plugins/lisa-devin/agents/architecture-specialist.md +32 -0
  37. package/plugins/lisa-devin/agents/bug-fixer.md +43 -0
  38. package/plugins/lisa-devin/agents/builder.md +44 -0
  39. package/plugins/lisa-devin/agents/confluence-prd-intake.md +65 -0
  40. package/plugins/lisa-devin/agents/debug-specialist.md +34 -0
  41. package/plugins/lisa-devin/agents/eval-specialist.md +33 -0
  42. package/plugins/lisa-devin/agents/git-history-analyzer.md +183 -0
  43. package/plugins/lisa-devin/agents/github-agent.md +155 -0
  44. package/plugins/lisa-devin/agents/github-build-intake.md +64 -0
  45. package/plugins/lisa-devin/agents/github-prd-intake.md +66 -0
  46. package/plugins/lisa-devin/agents/jira-agent.md +135 -0
  47. package/plugins/lisa-devin/agents/jira-build-intake.md +64 -0
  48. package/plugins/lisa-devin/agents/learner.md +85 -0
  49. package/plugins/lisa-devin/agents/learning-judge.md +119 -0
  50. package/plugins/lisa-devin/agents/learnings-synthesizer.md +155 -0
  51. package/plugins/lisa-devin/agents/linear-agent.md +138 -0
  52. package/plugins/lisa-devin/agents/linear-build-intake.md +64 -0
  53. package/plugins/lisa-devin/agents/linear-prd-intake.md +66 -0
  54. package/plugins/lisa-devin/agents/notion-prd-intake.md +63 -0
  55. package/plugins/lisa-devin/agents/performance-specialist.md +26 -0
  56. package/plugins/lisa-devin/agents/pr-mining-specialist.md +85 -0
  57. package/plugins/lisa-devin/agents/product-specialist.md +30 -0
  58. package/plugins/lisa-devin/agents/quality-specialist.md +30 -0
  59. package/plugins/lisa-devin/agents/security-specialist.md +43 -0
  60. package/plugins/lisa-devin/agents/skill-evaluator.md +205 -0
  61. package/plugins/lisa-devin/agents/spec-conformance-specialist.md +56 -0
  62. package/plugins/lisa-devin/agents/test-specialist.md +32 -0
  63. package/plugins/lisa-devin/agents/tracker-mining-specialist.md +85 -0
  64. package/plugins/lisa-devin/agents/verification-specialist.md +145 -0
  65. package/plugins/lisa-devin/hooks/auto-update.mjs +824 -0
  66. package/plugins/lisa-devin/hooks/auto-update.sh +31 -0
  67. package/plugins/lisa-devin/hooks/block-blind-automerge.sh +1242 -0
  68. package/plugins/lisa-devin/hooks/block-direct-issue-create.sh +2603 -0
  69. package/plugins/lisa-devin/hooks/block-host-name-leak.mjs +412 -0
  70. package/plugins/lisa-devin/hooks/block-host-name-leak.sh +61 -0
  71. package/plugins/lisa-devin/hooks/block-instruction-file-edits.sh +207 -0
  72. package/plugins/lisa-devin/hooks/block-managed-file-edits.sh +1175 -0
  73. package/plugins/lisa-devin/hooks/block-no-verify.sh +2288 -0
  74. package/plugins/lisa-devin/hooks/block-shell-json-parsing.sh +263 -0
  75. package/plugins/lisa-devin/hooks/cleanup-stale-worktrees.sh +122 -0
  76. package/plugins/lisa-devin/hooks/devin-tool-name-shim.sh +30 -0
  77. package/plugins/lisa-devin/hooks/discharge-work-item-gates.sh +90 -0
  78. package/plugins/lisa-devin/hooks/enforce-verification-gate.sh +506 -0
  79. package/plugins/lisa-devin/hooks/enforcement-vintage-npm.mjs +320 -0
  80. package/plugins/lisa-devin/hooks/enforcement-vintage.mjs +472 -0
  81. package/plugins/lisa-devin/hooks/enforcement-vintage.sh +48 -0
  82. package/plugins/lisa-devin/hooks/failure-signature-index.mjs +717 -0
  83. package/plugins/lisa-devin/hooks/failure-signature-index.sh +26 -0
  84. package/plugins/lisa-devin/hooks/guard-dedupe.bash +374 -0
  85. package/plugins/lisa-devin/hooks/inject-resolved-config.mjs +903 -0
  86. package/plugins/lisa-devin/hooks/inject-resolved-config.sh +107 -0
  87. package/plugins/lisa-devin/hooks/inject-rules.sh +49 -0
  88. package/plugins/lisa-devin/hooks/install-pkgs.sh +84 -0
  89. package/plugins/lisa-devin/hooks/operational-hazards.mjs +736 -0
  90. package/plugins/lisa-devin/hooks/operational-hazards.sh +28 -0
  91. package/plugins/lisa-devin/hooks/parity-safety-net-heredoc.py +1004 -0
  92. package/plugins/lisa-devin/hooks/parity-safety-net.sh +3275 -0
  93. package/plugins/lisa-devin/hooks/secrets-preflight.sh +72 -0
  94. package/plugins/lisa-devin/hooks/setup-jira-cli.sh +205 -0
  95. package/plugins/lisa-devin/hooks/shell-write-nudge.sh +86 -0
  96. package/plugins/lisa-devin/hooks/sonar-secrets.sh +205 -0
  97. package/plugins/lisa-devin/hooks/threshold-ratchet-compare.mjs +518 -0
  98. package/plugins/lisa-devin/hooks/threshold-ratchet-families.mjs +543 -0
  99. package/plugins/lisa-devin/hooks/threshold-ratchet.mjs +568 -0
  100. package/plugins/lisa-devin/hooks/threshold-ratchet.sh +22 -0
  101. package/plugins/lisa-devin/hooks/withdrawn-rulings.mjs +600 -0
  102. package/plugins/lisa-devin/hooks/withdrawn-rulings.sh +24 -0
  103. package/plugins/lisa-devin/hooks/worktree-binding-guard.mjs +1383 -0
  104. package/plugins/lisa-devin/hooks/worktree-binding-guard.sh +79 -0
  105. package/plugins/lisa-devin/hooks.json +247 -0
  106. package/plugins/lisa-devin/rules/eager/00-rule-index.md +82 -0
  107. package/plugins/lisa-devin/rules/eager/base-rules.md +85 -0
  108. package/plugins/lisa-devin/rules/eager/coding-philosophy.md +27 -0
  109. package/plugins/lisa-devin/rules/eager/config-resolution.md +37 -0
  110. package/plugins/lisa-devin/rules/eager/do-it-now.md +11 -0
  111. package/plugins/lisa-devin/rules/eager/empirical-inquiry.md +24 -0
  112. package/plugins/lisa-devin/rules/eager/falsifiable-checks.md +48 -0
  113. package/plugins/lisa-devin/rules/eager/history-audit.md +27 -0
  114. package/plugins/lisa-devin/rules/eager/integration-access-layer.md +19 -0
  115. package/plugins/lisa-devin/rules/eager/intent-routing.md +18 -0
  116. package/plugins/lisa-devin/rules/eager/learnings-ladder.md +18 -0
  117. package/plugins/lisa-devin/rules/eager/local-ci-first.md +15 -0
  118. package/plugins/lisa-devin/rules/eager/measurement-integrity.md +18 -0
  119. package/plugins/lisa-devin/rules/eager/never-name-a-downstream-project.md +9 -0
  120. package/plugins/lisa-devin/rules/eager/not-blocked-just-waiting.md +13 -0
  121. package/plugins/lisa-devin/rules/eager/operational-hazards.md +50 -0
  122. package/plugins/lisa-devin/rules/eager/report-actionability.md +26 -0
  123. package/plugins/lisa-devin/rules/eager/security-audit-handling.md +40 -0
  124. package/plugins/lisa-devin/rules/eager/session-status-updates.md +20 -0
  125. package/plugins/lisa-devin/rules/eager/settled-decisions.md +75 -0
  126. package/plugins/lisa-devin/rules/eager/stale-state-claims.md +26 -0
  127. package/plugins/lisa-devin/rules/eager/tool-access-gate.md +23 -0
  128. package/plugins/lisa-devin/rules/eager/tracked-work.md +9 -0
  129. package/plugins/lisa-devin/rules/eager/upstream-to-lisa.md +14 -0
  130. package/plugins/lisa-devin/rules/eager/verification.md +33 -0
  131. package/plugins/lisa-devin/rules/eager/wiki-knowledge-source.md +13 -0
  132. package/plugins/lisa-devin/rules/eager/withdrawn-rulings.md +102 -0
  133. package/plugins/lisa-devin/rules/reference/automation-runbook-contract.md +355 -0
  134. package/plugins/lisa-devin/rules/reference/base-rules.md +136 -0
  135. package/plugins/lisa-devin/rules/reference/bdd-e2e-coverage.md +301 -0
  136. package/plugins/lisa-devin/rules/reference/blocker-containment.md +165 -0
  137. package/plugins/lisa-devin/rules/reference/claim-archaeology.md +184 -0
  138. package/plugins/lisa-devin/rules/reference/claim-evidence-mapping.md +362 -0
  139. package/plugins/lisa-devin/rules/reference/claim-time-guards.md +135 -0
  140. package/plugins/lisa-devin/rules/reference/coding-philosophy.md +428 -0
  141. package/plugins/lisa-devin/rules/reference/config-resolution.md +1225 -0
  142. package/plugins/lisa-devin/rules/reference/control-reachability.md +166 -0
  143. package/plugins/lisa-devin/rules/reference/convergent-review.md +145 -0
  144. package/plugins/lisa-devin/rules/reference/credential-substrate-precedence.md +249 -0
  145. package/plugins/lisa-devin/rules/reference/dependency-decision-records.md +177 -0
  146. package/plugins/lisa-devin/rules/reference/dependency-internalization-kit.md +244 -0
  147. package/plugins/lisa-devin/rules/reference/dependency-trust-classes.md +291 -0
  148. package/plugins/lisa-devin/rules/reference/deployed-state-readback.md +142 -0
  149. package/plugins/lisa-devin/rules/reference/derived-branch-plan.md +214 -0
  150. package/plugins/lisa-devin/rules/reference/design-source-of-truth.md +260 -0
  151. package/plugins/lisa-devin/rules/reference/design-value-binding.md +311 -0
  152. package/plugins/lisa-devin/rules/reference/do-it-now.md +19 -0
  153. package/plugins/lisa-devin/rules/reference/documentation-source-paths.md +31 -0
  154. package/plugins/lisa-devin/rules/reference/empirical-inquiry.md +28 -0
  155. package/plugins/lisa-devin/rules/reference/factory-model.md +111 -0
  156. package/plugins/lisa-devin/rules/reference/falsifiable-checks.md +275 -0
  157. package/plugins/lisa-devin/rules/reference/history-audit.md +42 -0
  158. package/plugins/lisa-devin/rules/reference/integration-access-layer.md +89 -0
  159. package/plugins/lisa-devin/rules/reference/intent-routing.md +428 -0
  160. package/plugins/lisa-devin/rules/reference/leaf-only-lifecycle.md +227 -0
  161. package/plugins/lisa-devin/rules/reference/learnings-ladder.md +41 -0
  162. package/plugins/lisa-devin/rules/reference/local-ci-first.md +34 -0
  163. package/plugins/lisa-devin/rules/reference/measurement-integrity.md +119 -0
  164. package/plugins/lisa-devin/rules/reference/never-name-a-downstream-project.md +37 -0
  165. package/plugins/lisa-devin/rules/reference/not-blocked-just-waiting.md +41 -0
  166. package/plugins/lisa-devin/rules/reference/observability-audit.md +146 -0
  167. package/plugins/lisa-devin/rules/reference/operational-hazards.md +115 -0
  168. package/plugins/lisa-devin/rules/reference/prd-definition-of-ready.md +118 -0
  169. package/plugins/lisa-devin/rules/reference/prd-lifecycle-rollup.md +192 -0
  170. package/plugins/lisa-devin/rules/reference/pre-flight-autofill.md +250 -0
  171. package/plugins/lisa-devin/rules/reference/project-learnings.md +159 -0
  172. package/plugins/lisa-devin/rules/reference/promotion-contract.md +148 -0
  173. package/plugins/lisa-devin/rules/reference/readiness-rubric.md +242 -0
  174. package/plugins/lisa-devin/rules/reference/ready-role-filing.md +212 -0
  175. package/plugins/lisa-devin/rules/reference/rejection-detection.md +308 -0
  176. package/plugins/lisa-devin/rules/reference/repo-scope-split.md +119 -0
  177. package/plugins/lisa-devin/rules/reference/report-actionability.md +61 -0
  178. package/plugins/lisa-devin/rules/reference/reset-seed-coverage.md +286 -0
  179. package/plugins/lisa-devin/rules/reference/security-audit-handling.md +55 -0
  180. package/plugins/lisa-devin/rules/reference/session-status-updates.md +111 -0
  181. package/plugins/lisa-devin/rules/reference/settled-decisions.md +39 -0
  182. package/plugins/lisa-devin/rules/reference/stale-state-claims.md +88 -0
  183. package/plugins/lisa-devin/rules/reference/tool-access-gate.md +114 -0
  184. package/plugins/lisa-devin/rules/reference/tracked-work.md +49 -0
  185. package/plugins/lisa-devin/rules/reference/upstream-to-lisa.md +26 -0
  186. package/plugins/lisa-devin/rules/reference/usage-accounting.md +261 -0
  187. package/plugins/lisa-devin/rules/reference/verification.md +278 -0
  188. package/plugins/lisa-devin/rules/reference/wiki-knowledge-source.md +37 -0
  189. package/plugins/lisa-devin/rules/reference/withdrawn-rulings.md +148 -0
  190. package/plugins/lisa-devin/rules/reference/work-item-definition-of-ready.md +177 -0
  191. package/plugins/lisa-devin/rules/reference/work-item-trailer-definition.md +129 -0
  192. package/plugins/lisa-devin/scripts/automation-run-record.mjs +678 -0
  193. package/plugins/lisa-devin/scripts/automation-status-claude-adapter.mjs +804 -0
  194. package/plugins/lisa-devin/scripts/automation-status-codex-adapter.mjs +1081 -0
  195. package/plugins/lisa-devin/scripts/automation-status-contract-drift.mjs +392 -0
  196. package/plugins/lisa-devin/scripts/automation-status-expected-fleet.mjs +508 -0
  197. package/plugins/lisa-devin/scripts/automation-status-report.mjs +200 -0
  198. package/plugins/lisa-devin/scripts/automation-status-run-history.mjs +216 -0
  199. package/plugins/lisa-devin/scripts/automation-status-unrecorded-runs.mjs +127 -0
  200. package/plugins/lisa-devin/scripts/blocker-edge-resolution.mjs +150 -0
  201. package/plugins/lisa-devin/scripts/cross-pollinate.mjs +763 -0
  202. package/plugins/lisa-devin/scripts/design-bindings-probe.mjs +713 -0
  203. package/plugins/lisa-devin/scripts/design-intake-gate.mjs +470 -0
  204. package/plugins/lisa-devin/scripts/design-source-gate.mjs +610 -0
  205. package/plugins/lisa-devin/scripts/design-variable-ids.mjs +567 -0
  206. package/plugins/lisa-devin/scripts/doctor-report.mjs +635 -0
  207. package/plugins/lisa-devin/scripts/install-remote-agent-aws.mjs +443 -0
  208. package/plugins/lisa-devin/scripts/intake-blocker-reprobe.mjs +1065 -0
  209. package/plugins/lisa-devin/scripts/intake-prework-denominator.mjs +249 -0
  210. package/plugins/lisa-devin/scripts/lifecycle-label-trust.mjs +593 -0
  211. package/plugins/lisa-devin/scripts/linear-state-write-target.mjs +362 -0
  212. package/plugins/lisa-devin/scripts/plugin-sync-explain.mjs +654 -0
  213. package/plugins/lisa-devin/scripts/pr-arming-sweep.mjs +561 -0
  214. package/plugins/lisa-devin/scripts/project-ideation-idempotency-harness.mjs +338 -0
  215. package/plugins/lisa-devin/scripts/qa-signal-lifecycle.mjs +523 -0
  216. package/plugins/lisa-devin/scripts/queue-contract-resolution.mjs +583 -0
  217. package/plugins/lisa-devin/scripts/queue-health-classification.mjs +157 -0
  218. package/plugins/lisa-devin/scripts/queue-status-build-readers.mjs +561 -0
  219. package/plugins/lisa-devin/scripts/queue-status-prd-readers.mjs +452 -0
  220. package/plugins/lisa-devin/scripts/remote-agent-aws-setup.sh +677 -0
  221. package/plugins/lisa-devin/scripts/repo-scope-vocabulary-audit.mjs +458 -0
  222. package/plugins/lisa-devin/scripts/resolve-lifecycle-role.mjs +347 -0
  223. package/plugins/lisa-devin/scripts/rollup-blocker-classification.mjs +616 -0
  224. package/plugins/lisa-devin/scripts/scoping-label-audit.mjs +474 -0
  225. package/plugins/lisa-devin/skills/lisa-acceptance-criteria/SKILL.md +73 -0
  226. package/plugins/lisa-devin/skills/lisa-agent-design-best-practices/SKILL.md +242 -0
  227. package/plugins/lisa-devin/skills/lisa-agent-ready/SKILL.md +271 -0
  228. package/plugins/lisa-devin/skills/lisa-analyze-claude-remote/SKILL.md +434 -0
  229. package/plugins/lisa-devin/skills/lisa-atlassian-access/SKILL.md +469 -0
  230. package/plugins/lisa-devin/skills/lisa-atlassian-access/scripts/markdown-to-adf.mjs +236 -0
  231. package/plugins/lisa-devin/skills/lisa-attribute-failure/SKILL.md +142 -0
  232. package/plugins/lisa-devin/skills/lisa-automation-status/SKILL.md +149 -0
  233. package/plugins/lisa-devin/skills/lisa-bug-triage/SKILL.md +23 -0
  234. package/plugins/lisa-devin/skills/lisa-capability-drift/SKILL.md +71 -0
  235. package/plugins/lisa-devin/skills/lisa-codebase-research/SKILL.md +87 -0
  236. package/plugins/lisa-devin/skills/lisa-codify-verification/SKILL.md +213 -0
  237. package/plugins/lisa-devin/skills/lisa-confluence-prd-intake/SKILL.md +417 -0
  238. package/plugins/lisa-devin/skills/lisa-confluence-to-tracker/SKILL.md +469 -0
  239. package/plugins/lisa-devin/skills/lisa-confluence-write-prd/SKILL.md +111 -0
  240. package/plugins/lisa-devin/skills/lisa-cross-pollinate/SKILL.md +175 -0
  241. package/plugins/lisa-devin/skills/lisa-debrief/SKILL.md +96 -0
  242. package/plugins/lisa-devin/skills/lisa-debrief-apply/SKILL.md +106 -0
  243. package/plugins/lisa-devin/skills/lisa-delivery-effectiveness/SKILL.md +62 -0
  244. package/plugins/lisa-devin/skills/lisa-design-intake/SKILL.md +198 -0
  245. package/plugins/lisa-devin/skills/lisa-detect-tooling/SKILL.md +110 -0
  246. package/plugins/lisa-devin/skills/lisa-detect-tooling/scripts/commands.mjs +479 -0
  247. package/plugins/lisa-devin/skills/lisa-detect-tooling/scripts/detect-tooling.mjs +658 -0
  248. package/plugins/lisa-devin/skills/lisa-doctor/SKILL.md +743 -0
  249. package/plugins/lisa-devin/skills/lisa-drive-pr-to-merge/SKILL.md +1587 -0
  250. package/plugins/lisa-devin/skills/lisa-epic-triage/SKILL.md +28 -0
  251. package/plugins/lisa-devin/skills/lisa-evaluation-suite/SKILL.md +63 -0
  252. package/plugins/lisa-devin/skills/lisa-exploratory-qa/SKILL.md +185 -0
  253. package/plugins/lisa-devin/skills/lisa-fix-linter-error/SKILL.md +45 -0
  254. package/plugins/lisa-devin/skills/lisa-generate-claude-remote-build-script/SKILL.md +235 -0
  255. package/plugins/lisa-devin/skills/lisa-git-commit/SKILL.md +53 -0
  256. package/plugins/lisa-devin/skills/lisa-git-prune/SKILL.md +35 -0
  257. package/plugins/lisa-devin/skills/lisa-git-submit-pr/SKILL.md +170 -0
  258. package/plugins/lisa-devin/skills/lisa-github-add-journey/SKILL.md +118 -0
  259. package/plugins/lisa-devin/skills/lisa-github-build-intake/SKILL.md +745 -0
  260. package/plugins/lisa-devin/skills/lisa-github-claim/SKILL.md +27 -0
  261. package/plugins/lisa-devin/skills/lisa-github-create/SKILL.md +105 -0
  262. package/plugins/lisa-devin/skills/lisa-github-evidence/SKILL.md +120 -0
  263. package/plugins/lisa-devin/skills/lisa-github-journey/SKILL.md +134 -0
  264. package/plugins/lisa-devin/skills/lisa-github-prd-intake/SKILL.md +424 -0
  265. package/plugins/lisa-devin/skills/lisa-github-project-v2/SKILL.md +227 -0
  266. package/plugins/lisa-devin/skills/lisa-github-read-issue/SKILL.md +305 -0
  267. package/plugins/lisa-devin/skills/lisa-github-sync/SKILL.md +201 -0
  268. package/plugins/lisa-devin/skills/lisa-github-to-tracker/SKILL.md +456 -0
  269. package/plugins/lisa-devin/skills/lisa-github-validate-issue/SKILL.md +598 -0
  270. package/plugins/lisa-devin/skills/lisa-github-verify/SKILL.md +60 -0
  271. package/plugins/lisa-devin/skills/lisa-github-write-issue/SKILL.md +531 -0
  272. package/plugins/lisa-devin/skills/lisa-github-write-prd/SKILL.md +156 -0
  273. package/plugins/lisa-devin/skills/lisa-health/SKILL.md +142 -0
  274. package/plugins/lisa-devin/skills/lisa-health-drift-cron/SKILL.md +80 -0
  275. package/plugins/lisa-devin/skills/lisa-implement/SKILL.md +372 -0
  276. package/plugins/lisa-devin/skills/lisa-improve-code-complexity/SKILL.md +44 -0
  277. package/plugins/lisa-devin/skills/lisa-improve-harness/SKILL.md +487 -0
  278. package/plugins/lisa-devin/skills/lisa-improve-max-lines/SKILL.md +45 -0
  279. package/plugins/lisa-devin/skills/lisa-improve-max-lines-per-function/SKILL.md +46 -0
  280. package/plugins/lisa-devin/skills/lisa-improve-test-coverage/SKILL.md +47 -0
  281. package/plugins/lisa-devin/skills/lisa-improve-tests/SKILL.md +50 -0
  282. package/plugins/lisa-devin/skills/lisa-intake/SKILL.md +208 -0
  283. package/plugins/lisa-devin/skills/lisa-intake-explain/SKILL.md +279 -0
  284. package/plugins/lisa-devin/skills/lisa-jam-access/SKILL.md +128 -0
  285. package/plugins/lisa-devin/skills/lisa-jira-add-journey/SKILL.md +139 -0
  286. package/plugins/lisa-devin/skills/lisa-jira-build-intake/SKILL.md +556 -0
  287. package/plugins/lisa-devin/skills/lisa-jira-claim/SKILL.md +27 -0
  288. package/plugins/lisa-devin/skills/lisa-jira-create/SKILL.md +157 -0
  289. package/plugins/lisa-devin/skills/lisa-jira-evidence/SKILL.md +106 -0
  290. package/plugins/lisa-devin/skills/lisa-jira-evidence/scripts/post-evidence.sh +262 -0
  291. package/plugins/lisa-devin/skills/lisa-jira-journey/SKILL.md +168 -0
  292. package/plugins/lisa-devin/skills/lisa-jira-journey/scripts/generate-templates.py +239 -0
  293. package/plugins/lisa-devin/skills/lisa-jira-journey/scripts/parse-plan.py +570 -0
  294. package/plugins/lisa-devin/skills/lisa-jira-read-ticket/SKILL.md +199 -0
  295. package/plugins/lisa-devin/skills/lisa-jira-read-ticket/scripts/download-attachment.sh +211 -0
  296. package/plugins/lisa-devin/skills/lisa-jira-sync/SKILL.md +117 -0
  297. package/plugins/lisa-devin/skills/lisa-jira-validate-ticket/SKILL.md +525 -0
  298. package/plugins/lisa-devin/skills/lisa-jira-verify/SKILL.md +61 -0
  299. package/plugins/lisa-devin/skills/lisa-jira-write-ticket/SKILL.md +440 -0
  300. package/plugins/lisa-devin/skills/lisa-jsdoc-best-practices/SKILL.md +491 -0
  301. package/plugins/lisa-devin/skills/lisa-kane-browser/SKILL.md +80 -0
  302. package/plugins/lisa-devin/skills/lisa-learnings-audit/SKILL.md +490 -0
  303. package/plugins/lisa-devin/skills/lisa-linear-access/SKILL.md +483 -0
  304. package/plugins/lisa-devin/skills/lisa-linear-add-journey/SKILL.md +109 -0
  305. package/plugins/lisa-devin/skills/lisa-linear-build-intake/SKILL.md +548 -0
  306. package/plugins/lisa-devin/skills/lisa-linear-claim/SKILL.md +28 -0
  307. package/plugins/lisa-devin/skills/lisa-linear-create/SKILL.md +150 -0
  308. package/plugins/lisa-devin/skills/lisa-linear-evidence/SKILL.md +124 -0
  309. package/plugins/lisa-devin/skills/lisa-linear-journey/SKILL.md +147 -0
  310. package/plugins/lisa-devin/skills/lisa-linear-prd-intake/SKILL.md +411 -0
  311. package/plugins/lisa-devin/skills/lisa-linear-read-issue/SKILL.md +201 -0
  312. package/plugins/lisa-devin/skills/lisa-linear-sync/SKILL.md +164 -0
  313. package/plugins/lisa-devin/skills/lisa-linear-to-tracker/SKILL.md +454 -0
  314. package/plugins/lisa-devin/skills/lisa-linear-validate-issue/SKILL.md +504 -0
  315. package/plugins/lisa-devin/skills/lisa-linear-verify/SKILL.md +65 -0
  316. package/plugins/lisa-devin/skills/lisa-linear-write-issue/SKILL.md +448 -0
  317. package/plugins/lisa-devin/skills/lisa-linear-write-prd/SKILL.md +98 -0
  318. package/plugins/lisa-devin/skills/lisa-monitor/SKILL.md +224 -0
  319. package/plugins/lisa-devin/skills/lisa-nightly-add-test-coverage/SKILL.md +40 -0
  320. package/plugins/lisa-devin/skills/lisa-nightly-improve-tests/SKILL.md +29 -0
  321. package/plugins/lisa-devin/skills/lisa-nightly-lower-code-complexity/SKILL.md +42 -0
  322. package/plugins/lisa-devin/skills/lisa-notion-access/SKILL.md +301 -0
  323. package/plugins/lisa-devin/skills/lisa-notion-prd-intake/SKILL.md +360 -0
  324. package/plugins/lisa-devin/skills/lisa-notion-to-tracker/SKILL.md +467 -0
  325. package/plugins/lisa-devin/skills/lisa-notion-write-prd/SKILL.md +111 -0
  326. package/plugins/lisa-devin/skills/lisa-parity-code-review/SKILL.md +83 -0
  327. package/plugins/lisa-devin/skills/lisa-parity-code-simplifier/SKILL.md +76 -0
  328. package/plugins/lisa-devin/skills/lisa-parity-coderabbit/SKILL.md +87 -0
  329. package/plugins/lisa-devin/skills/lisa-parity-sentry-sdk-setup/SKILL.md +375 -0
  330. package/plugins/lisa-devin/skills/lisa-parity-sentry-seer/SKILL.md +168 -0
  331. package/plugins/lisa-devin/skills/lisa-parity-skill-creator/SKILL.md +155 -0
  332. package/plugins/lisa-devin/skills/lisa-performance-review/SKILL.md +94 -0
  333. package/plugins/lisa-devin/skills/lisa-persist-learning/SKILL.md +247 -0
  334. package/plugins/lisa-devin/skills/lisa-plan/SKILL.md +64 -0
  335. package/plugins/lisa-devin/skills/lisa-plugin-sync-explain/SKILL.md +53 -0
  336. package/plugins/lisa-devin/skills/lisa-posthog-access/SKILL.md +140 -0
  337. package/plugins/lisa-devin/skills/lisa-prd-backlink/SKILL.md +274 -0
  338. package/plugins/lisa-devin/skills/lisa-prd-source-write/SKILL.md +104 -0
  339. package/plugins/lisa-devin/skills/lisa-prd-ticket-coverage/SKILL.md +187 -0
  340. package/plugins/lisa-devin/skills/lisa-product-walkthrough/SKILL.md +106 -0
  341. package/plugins/lisa-devin/skills/lisa-project-ideation/SKILL.md +417 -0
  342. package/plugins/lisa-devin/skills/lisa-project-ideation/examples/evidence-card-format.md +21 -0
  343. package/plugins/lisa-devin/skills/lisa-project-ideation/examples/host-project-only.md +22 -0
  344. package/plugins/lisa-devin/skills/lisa-project-ideation/examples/idempotency-verification-harness.md +57 -0
  345. package/plugins/lisa-devin/skills/lisa-project-ideation/examples/public-external-inspiration.md +22 -0
  346. package/plugins/lisa-devin/skills/lisa-project-ideation/examples/unavailable-data-rejection.md +22 -0
  347. package/plugins/lisa-devin/skills/lisa-pull-request-review/SKILL.md +132 -0
  348. package/plugins/lisa-devin/skills/lisa-quality-review/SKILL.md +63 -0
  349. package/plugins/lisa-devin/skills/lisa-queue-status/SKILL.md +215 -0
  350. package/plugins/lisa-devin/skills/lisa-remote-dispatch/SKILL.md +109 -0
  351. package/plugins/lisa-devin/skills/lisa-remote-dispatch/scripts/dispatch.mjs +535 -0
  352. package/plugins/lisa-devin/skills/lisa-repair-intake/SKILL.md +1621 -0
  353. package/plugins/lisa-devin/skills/lisa-reproduce-bug/SKILL.md +75 -0
  354. package/plugins/lisa-devin/skills/lisa-research/SKILL.md +92 -0
  355. package/plugins/lisa-devin/skills/lisa-review-implementation/SKILL.md +209 -0
  356. package/plugins/lisa-devin/skills/lisa-review-local/SKILL.md +141 -0
  357. package/plugins/lisa-devin/skills/lisa-rework-triage/SKILL.md +206 -0
  358. package/plugins/lisa-devin/skills/lisa-root-cause-analysis/SKILL.md +135 -0
  359. package/plugins/lisa-devin/skills/lisa-secrets-access/SKILL.md +430 -0
  360. package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/aws-bootstrap.mjs +433 -0
  361. package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/bootstrap-store.mjs +288 -0
  362. package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/doctor-secrets.mjs +273 -0
  363. package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/envfile.mjs +80 -0
  364. package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/inspect-vault.mjs +142 -0
  365. package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +1085 -0
  366. package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/note-format.mjs +179 -0
  367. package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/owner.mjs +96 -0
  368. package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/preflight-secrets.mjs +342 -0
  369. package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/prompt-secret.mjs +79 -0
  370. package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/providers.mjs +805 -0
  371. package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/publish-aws-bootstrap.mjs +680 -0
  372. package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/read-secret-note.mjs +105 -0
  373. package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/resolve-expo-token.mjs +90 -0
  374. package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/resolve-secret.mjs +285 -0
  375. package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/rotate-secret.mjs +297 -0
  376. package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/routing-floor.mjs +153 -0
  377. package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/surfaces.mjs +480 -0
  378. package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/sync-secret-to-ci.mjs +510 -0
  379. package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/tools-from-notes.mjs +63 -0
  380. package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/validate-config.mjs +550 -0
  381. package/plugins/lisa-devin/skills/lisa-security-review/SKILL.md +124 -0
  382. package/plugins/lisa-devin/skills/lisa-security-zap-scan/SKILL.md +53 -0
  383. package/plugins/lisa-devin/skills/lisa-sentry-access/SKILL.md +140 -0
  384. package/plugins/lisa-devin/skills/lisa-setup-atlassian/SKILL.md +407 -0
  385. package/plugins/lisa-devin/skills/lisa-setup-automations/SKILL.md +430 -0
  386. package/plugins/lisa-devin/skills/lisa-setup-automations/scripts/generate-workflow.mjs +288 -0
  387. package/plugins/lisa-devin/skills/lisa-setup-confluence/SKILL.md +254 -0
  388. package/plugins/lisa-devin/skills/lisa-setup-github/SKILL.md +319 -0
  389. package/plugins/lisa-devin/skills/lisa-setup-github-repo/SKILL.md +128 -0
  390. package/plugins/lisa-devin/skills/lisa-setup-jira/SKILL.md +198 -0
  391. package/plugins/lisa-devin/skills/lisa-setup-kane/SKILL.md +96 -0
  392. package/plugins/lisa-devin/skills/lisa-setup-linear/SKILL.md +375 -0
  393. package/plugins/lisa-devin/skills/lisa-setup-local-env/SKILL.md +95 -0
  394. package/plugins/lisa-devin/skills/lisa-setup-local-env/scripts/local-env.mjs +196 -0
  395. package/plugins/lisa-devin/skills/lisa-setup-notion/SKILL.md +376 -0
  396. package/plugins/lisa-devin/skills/lisa-setup-remote-aws/SKILL.md +149 -0
  397. package/plugins/lisa-devin/skills/lisa-setup-remote-env/SKILL.md +382 -0
  398. package/plugins/lisa-devin/skills/lisa-setup-remote-env/assets/materialized-env-authority.mjs +262 -0
  399. package/plugins/lisa-devin/skills/lisa-setup-remote-env/assets/session-start.sh +87 -0
  400. package/plugins/lisa-devin/skills/lisa-setup-remote-env/assets/setup.sh +216 -0
  401. package/plugins/lisa-devin/skills/lisa-setup-remote-env/scripts/environment.mjs +227 -0
  402. package/plugins/lisa-devin/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +378 -0
  403. package/plugins/lisa-devin/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +1632 -0
  404. package/plugins/lisa-devin/skills/lisa-setup-remote-env/scripts/tool-floor.mjs +94 -0
  405. package/plugins/lisa-devin/skills/lisa-setup-remote-env/scripts/toolchain.mjs +448 -0
  406. package/plugins/lisa-devin/skills/lisa-setup-remote-env/scripts/verify-remote-env.mjs +250 -0
  407. package/plugins/lisa-devin/skills/lisa-setup-sonar/SKILL.md +153 -0
  408. package/plugins/lisa-devin/skills/lisa-setup-workstation/SKILL.md +143 -0
  409. package/plugins/lisa-devin/skills/lisa-setup-workstation/scripts/bounded-child.mjs +168 -0
  410. package/plugins/lisa-devin/skills/lisa-setup-workstation/scripts/catalogue.mjs +293 -0
  411. package/plugins/lisa-devin/skills/lisa-setup-workstation/scripts/cli.mjs +333 -0
  412. package/plugins/lisa-devin/skills/lisa-setup-workstation/scripts/workstation.mjs +511 -0
  413. package/plugins/lisa-devin/skills/lisa-sonarcloud-access/SKILL.md +202 -0
  414. package/plugins/lisa-devin/skills/lisa-spec-conformance/SKILL.md +205 -0
  415. package/plugins/lisa-devin/skills/lisa-starter-sync/SKILL.md +35 -0
  416. package/plugins/lisa-devin/skills/lisa-sync-down/SKILL.md +209 -0
  417. package/plugins/lisa-devin/skills/lisa-task-decomposition/SKILL.md +195 -0
  418. package/plugins/lisa-devin/skills/lisa-task-triage/SKILL.md +23 -0
  419. package/plugins/lisa-devin/skills/lisa-tdd-implementation/SKILL.md +91 -0
  420. package/plugins/lisa-devin/skills/lisa-tear-down-automations/SKILL.md +85 -0
  421. package/plugins/lisa-devin/skills/lisa-test-strategy/SKILL.md +66 -0
  422. package/plugins/lisa-devin/skills/lisa-ticket-triage/SKILL.md +214 -0
  423. package/plugins/lisa-devin/skills/lisa-track/SKILL.md +129 -0
  424. package/plugins/lisa-devin/skills/lisa-tracker-add-journey/SKILL.md +28 -0
  425. package/plugins/lisa-devin/skills/lisa-tracker-build-intake/SKILL.md +134 -0
  426. package/plugins/lisa-devin/skills/lisa-tracker-claim/SKILL.md +35 -0
  427. package/plugins/lisa-devin/skills/lisa-tracker-create/SKILL.md +28 -0
  428. package/plugins/lisa-devin/skills/lisa-tracker-evidence/SKILL.md +63 -0
  429. package/plugins/lisa-devin/skills/lisa-tracker-journey/SKILL.md +27 -0
  430. package/plugins/lisa-devin/skills/lisa-tracker-read/SKILL.md +28 -0
  431. package/plugins/lisa-devin/skills/lisa-tracker-source-artifacts/SKILL.md +111 -0
  432. package/plugins/lisa-devin/skills/lisa-tracker-sync/SKILL.md +150 -0
  433. package/plugins/lisa-devin/skills/lisa-tracker-validate/SKILL.md +37 -0
  434. package/plugins/lisa-devin/skills/lisa-tracker-verify/SKILL.md +29 -0
  435. package/plugins/lisa-devin/skills/lisa-tracker-write/SKILL.md +55 -0
  436. package/plugins/lisa-devin/skills/lisa-usage-accounting/SKILL.md +205 -0
  437. package/plugins/lisa-devin/skills/lisa-usage-accounting/references/effectiveness.md +63 -0
  438. package/plugins/lisa-devin/skills/lisa-use-the-product/SKILL.md +86 -0
  439. package/plugins/lisa-devin/skills/lisa-validate-tracker-mapping/SKILL.md +467 -0
  440. package/plugins/lisa-devin/skills/lisa-verification-lifecycle/SKILL.md +384 -0
  441. package/plugins/lisa-devin/skills/lisa-verify/SKILL.md +55 -0
  442. package/plugins/lisa-devin/skills/lisa-verify-prd/SKILL.md +394 -0
  443. package/plugins/lisa-devin/skills/lisa-verify-workflow-change/SKILL.md +154 -0
  444. package/plugins/lisa-devin/skills/lisa-wiki-install/SKILL.md +101 -0
  445. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  446. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  447. package/plugins/lisa-expo-agy/plugin.json +1 -1
  448. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  449. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  450. package/plugins/lisa-expo-devin/.claude-plugin/plugin.json +11 -0
  451. package/plugins/lisa-expo-devin/.devin-plugin/plugin.json +8 -0
  452. package/plugins/lisa-expo-devin/.mcp.json +8 -0
  453. package/plugins/lisa-expo-devin/THIRD-PARTY-NOTICES.md +84 -0
  454. package/plugins/lisa-expo-devin/agents/ops-specialist.md +124 -0
  455. package/plugins/lisa-expo-devin/hooks/devin-tool-name-shim.sh +30 -0
  456. package/plugins/lisa-expo-devin/hooks/register-maestro-mcp.sh +84 -0
  457. package/plugins/lisa-expo-devin/hooks.json +14 -0
  458. package/plugins/lisa-expo-devin/skills/add-app-clip/SKILL.md +280 -0
  459. package/plugins/lisa-expo-devin/skills/add-app-clip/references/native-module.md +96 -0
  460. package/plugins/lisa-expo-devin/skills/apollo-client/SKILL.md +238 -0
  461. package/plugins/lisa-expo-devin/skills/apollo-client/references/mutation-patterns.md +360 -0
  462. package/plugins/lisa-expo-devin/skills/atomic-design-gluestack/SKILL.md +369 -0
  463. package/plugins/lisa-expo-devin/skills/atomic-design-gluestack/references/atomic-levels.md +417 -0
  464. package/plugins/lisa-expo-devin/skills/atomic-design-gluestack/references/folder-structure.md +257 -0
  465. package/plugins/lisa-expo-devin/skills/atomic-design-gluestack/references/gluestack-mapping.md +233 -0
  466. package/plugins/lisa-expo-devin/skills/atomic-design-gluestack/scripts/test_validate_atomic_structure.py +240 -0
  467. package/plugins/lisa-expo-devin/skills/atomic-design-gluestack/scripts/validate_atomic_structure.py +438 -0
  468. package/plugins/lisa-expo-devin/skills/building-native-ui/SKILL.md +321 -0
  469. package/plugins/lisa-expo-devin/skills/building-native-ui/references/animations.md +220 -0
  470. package/plugins/lisa-expo-devin/skills/building-native-ui/references/controls.md +272 -0
  471. package/plugins/lisa-expo-devin/skills/building-native-ui/references/form-sheet.md +253 -0
  472. package/plugins/lisa-expo-devin/skills/building-native-ui/references/gradients.md +106 -0
  473. package/plugins/lisa-expo-devin/skills/building-native-ui/references/icons.md +213 -0
  474. package/plugins/lisa-expo-devin/skills/building-native-ui/references/media.md +198 -0
  475. package/plugins/lisa-expo-devin/skills/building-native-ui/references/route-structure.md +229 -0
  476. package/plugins/lisa-expo-devin/skills/building-native-ui/references/search.md +248 -0
  477. package/plugins/lisa-expo-devin/skills/building-native-ui/references/storage.md +121 -0
  478. package/plugins/lisa-expo-devin/skills/building-native-ui/references/tabs.md +433 -0
  479. package/plugins/lisa-expo-devin/skills/building-native-ui/references/toolbar-and-headers.md +284 -0
  480. package/plugins/lisa-expo-devin/skills/building-native-ui/references/visual-effects.md +197 -0
  481. package/plugins/lisa-expo-devin/skills/building-native-ui/references/webgpu-three.md +605 -0
  482. package/plugins/lisa-expo-devin/skills/building-native-ui/references/zoom-transitions.md +158 -0
  483. package/plugins/lisa-expo-devin/skills/container-view-pattern/SKILL.md +355 -0
  484. package/plugins/lisa-expo-devin/skills/container-view-pattern/references/examples.md +749 -0
  485. package/plugins/lisa-expo-devin/skills/container-view-pattern/references/patterns.md +318 -0
  486. package/plugins/lisa-expo-devin/skills/container-view-pattern/scripts/create_component.py +200 -0
  487. package/plugins/lisa-expo-devin/skills/container-view-pattern/scripts/validate_component.py +332 -0
  488. package/plugins/lisa-expo-devin/skills/cross-platform-compatibility/SKILL.md +268 -0
  489. package/plugins/lisa-expo-devin/skills/cross-platform-compatibility/references/common-issues.md +619 -0
  490. package/plugins/lisa-expo-devin/skills/cross-platform-compatibility/references/file-extensions.md +340 -0
  491. package/plugins/lisa-expo-devin/skills/cross-platform-compatibility/references/platform-api.md +276 -0
  492. package/plugins/lisa-expo-devin/skills/cross-platform-compatibility/scripts/validate_cross_platform.py +416 -0
  493. package/plugins/lisa-expo-devin/skills/directory-structure/SKILL.md +214 -0
  494. package/plugins/lisa-expo-devin/skills/directory-structure/scripts/validate_structure.py +454 -0
  495. package/plugins/lisa-expo-devin/skills/e2e-coverage-gaps/SKILL.md +117 -0
  496. package/plugins/lisa-expo-devin/skills/eas-update-insights/SKILL.md +228 -0
  497. package/plugins/lisa-expo-devin/skills/eas-update-insights/references/channel-insights-schema.md +47 -0
  498. package/plugins/lisa-expo-devin/skills/eas-update-insights/references/update-insights-schema.md +69 -0
  499. package/plugins/lisa-expo-devin/skills/expo-api-routes/SKILL.md +369 -0
  500. package/plugins/lisa-expo-devin/skills/expo-brownfield/SKILL.md +54 -0
  501. package/plugins/lisa-expo-devin/skills/expo-brownfield/references/brownfield-integrated.md +526 -0
  502. package/plugins/lisa-expo-devin/skills/expo-brownfield/references/brownfield-isolated.md +402 -0
  503. package/plugins/lisa-expo-devin/skills/expo-brownfield/references/comparison.md +63 -0
  504. package/plugins/lisa-expo-devin/skills/expo-brownfield/references/troubleshooting.md +88 -0
  505. package/plugins/lisa-expo-devin/skills/expo-cicd-workflows/SKILL.md +92 -0
  506. package/plugins/lisa-expo-devin/skills/expo-cicd-workflows/scripts/fetch.js +113 -0
  507. package/plugins/lisa-expo-devin/skills/expo-cicd-workflows/scripts/package.json +11 -0
  508. package/plugins/lisa-expo-devin/skills/expo-cicd-workflows/scripts/validate.js +85 -0
  509. package/plugins/lisa-expo-devin/skills/expo-deployment/SKILL.md +193 -0
  510. package/plugins/lisa-expo-devin/skills/expo-deployment/references/app-store-metadata.md +479 -0
  511. package/plugins/lisa-expo-devin/skills/expo-deployment/references/ios-app-store.md +355 -0
  512. package/plugins/lisa-expo-devin/skills/expo-deployment/references/play-store.md +264 -0
  513. package/plugins/lisa-expo-devin/skills/expo-deployment/references/testflight.md +58 -0
  514. package/plugins/lisa-expo-devin/skills/expo-deployment/references/workflows.md +200 -0
  515. package/plugins/lisa-expo-devin/skills/expo-dev-client/SKILL.md +164 -0
  516. package/plugins/lisa-expo-devin/skills/expo-env-config/SKILL.md +309 -0
  517. package/plugins/lisa-expo-devin/skills/expo-env-config/references/validation-patterns.md +417 -0
  518. package/plugins/lisa-expo-devin/skills/expo-module/SKILL.md +141 -0
  519. package/plugins/lisa-expo-devin/skills/expo-module/references/config-plugin.md +90 -0
  520. package/plugins/lisa-expo-devin/skills/expo-module/references/create-expo-module.md +206 -0
  521. package/plugins/lisa-expo-devin/skills/expo-module/references/lifecycle.md +127 -0
  522. package/plugins/lisa-expo-devin/skills/expo-module/references/module-config.md +48 -0
  523. package/plugins/lisa-expo-devin/skills/expo-module/references/native-module.md +286 -0
  524. package/plugins/lisa-expo-devin/skills/expo-module/references/native-view.md +171 -0
  525. package/plugins/lisa-expo-devin/skills/expo-router-best-practices/SKILL.md +432 -0
  526. package/plugins/lisa-expo-devin/skills/expo-router-best-practices/references/official-docs.md +290 -0
  527. package/plugins/lisa-expo-devin/skills/expo-router-best-practices/references/static-export-verification.md +54 -0
  528. package/plugins/lisa-expo-devin/skills/expo-router-best-practices/scripts/generate-route.py +171 -0
  529. package/plugins/lisa-expo-devin/skills/expo-tailwind-setup/SKILL.md +487 -0
  530. package/plugins/lisa-expo-devin/skills/expo-ui-jetpack-compose/SKILL.md +40 -0
  531. package/plugins/lisa-expo-devin/skills/expo-ui-swift-ui/SKILL.md +39 -0
  532. package/plugins/lisa-expo-devin/skills/gluestack-nativewind/SKILL.md +419 -0
  533. package/plugins/lisa-expo-devin/skills/gluestack-nativewind/references/color-tokens.md +343 -0
  534. package/plugins/lisa-expo-devin/skills/gluestack-nativewind/references/component-mapping.md +307 -0
  535. package/plugins/lisa-expo-devin/skills/gluestack-nativewind/references/spacing-scale.md +300 -0
  536. package/plugins/lisa-expo-devin/skills/gluestack-nativewind/scripts/test_validate_styling.py +240 -0
  537. package/plugins/lisa-expo-devin/skills/gluestack-nativewind/scripts/validate_styling.py +424 -0
  538. package/plugins/lisa-expo-devin/skills/jira-add-journey/SKILL.md +140 -0
  539. package/plugins/lisa-expo-devin/skills/jira-create/SKILL.md +161 -0
  540. package/plugins/lisa-expo-devin/skills/jira-evidence/SKILL.md +84 -0
  541. package/plugins/lisa-expo-devin/skills/jira-evidence/scripts/post-evidence.sh +262 -0
  542. package/plugins/lisa-expo-devin/skills/jira-journey/SKILL.md +196 -0
  543. package/plugins/lisa-expo-devin/skills/jira-journey/scripts/generate-templates.py +281 -0
  544. package/plugins/lisa-expo-devin/skills/jira-journey/scripts/parse-plan.py +400 -0
  545. package/plugins/lisa-expo-devin/skills/jira-verify/SKILL.md +27 -0
  546. package/plugins/lisa-expo-devin/skills/local-state/SKILL.md +362 -0
  547. package/plugins/lisa-expo-devin/skills/local-state/references/async-storage.md +505 -0
  548. package/plugins/lisa-expo-devin/skills/local-state/references/persistence-patterns.md +711 -0
  549. package/plugins/lisa-expo-devin/skills/local-state/references/reactive-variables.md +446 -0
  550. package/plugins/lisa-expo-devin/skills/maestro-mcp-setup/SKILL.md +144 -0
  551. package/plugins/lisa-expo-devin/skills/native-data-fetching/SKILL.md +507 -0
  552. package/plugins/lisa-expo-devin/skills/native-data-fetching/references/expo-router-loaders.md +344 -0
  553. package/plugins/lisa-expo-devin/skills/ops-browser-uat/SKILL.md +124 -0
  554. package/plugins/lisa-expo-devin/skills/ops-check-logs/SKILL.md +217 -0
  555. package/plugins/lisa-expo-devin/skills/ops-db-ops/SKILL.md +214 -0
  556. package/plugins/lisa-expo-devin/skills/ops-deploy/SKILL.md +123 -0
  557. package/plugins/lisa-expo-devin/skills/ops-monitor-errors/SKILL.md +99 -0
  558. package/plugins/lisa-expo-devin/skills/ops-performance/SKILL.md +165 -0
  559. package/plugins/lisa-expo-devin/skills/ops-run-local/SKILL.md +169 -0
  560. package/plugins/lisa-expo-devin/skills/ops-verify-health/SKILL.md +101 -0
  561. package/plugins/lisa-expo-devin/skills/owasp-zap/SKILL.md +61 -0
  562. package/plugins/lisa-expo-devin/skills/play-store-access/SKILL.md +226 -0
  563. package/plugins/lisa-expo-devin/skills/playwright-ci-debugging/SKILL.md +140 -0
  564. package/plugins/lisa-expo-devin/skills/playwright-selectors/SKILL.md +496 -0
  565. package/plugins/lisa-expo-devin/skills/reduce-complexity/SKILL.md +251 -0
  566. package/plugins/lisa-expo-devin/skills/reduce-complexity/references/extraction-strategies.md +456 -0
  567. package/plugins/lisa-expo-devin/skills/reduce-complexity/references/refactoring-patterns.md +557 -0
  568. package/plugins/lisa-expo-devin/skills/testing-library/SKILL.md +314 -0
  569. package/plugins/lisa-expo-devin/skills/testing-library/references/async-patterns.md +420 -0
  570. package/plugins/lisa-expo-devin/skills/testing-library/references/expo-router-testing.md +556 -0
  571. package/plugins/lisa-expo-devin/skills/testing-library/references/mocking-patterns.md +612 -0
  572. package/plugins/lisa-expo-devin/skills/testing-library/references/query-priority.md +291 -0
  573. package/plugins/lisa-expo-devin/skills/upgrading-expo/SKILL.md +134 -0
  574. package/plugins/lisa-expo-devin/skills/upgrading-expo/references/expo-av-to-audio.md +132 -0
  575. package/plugins/lisa-expo-devin/skills/upgrading-expo/references/expo-av-to-video.md +160 -0
  576. package/plugins/lisa-expo-devin/skills/upgrading-expo/references/native-tabs.md +124 -0
  577. package/plugins/lisa-expo-devin/skills/upgrading-expo/references/new-architecture.md +79 -0
  578. package/plugins/lisa-expo-devin/skills/upgrading-expo/references/react-19.md +79 -0
  579. package/plugins/lisa-expo-devin/skills/upgrading-expo/references/react-compiler.md +59 -0
  580. package/plugins/lisa-expo-devin/skills/upgrading-expo/references/react-navigation-to-expo-router.md +61 -0
  581. package/plugins/lisa-expo-devin/skills/use-dom/SKILL.md +417 -0
  582. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  583. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  584. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  585. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  586. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  587. package/plugins/lisa-harper-fabric-devin/.claude-plugin/plugin.json +11 -0
  588. package/plugins/lisa-harper-fabric-devin/.devin-plugin/plugin.json +8 -0
  589. package/plugins/lisa-harper-fabric-devin/generated-artifact-globs.txt +5 -0
  590. package/plugins/lisa-harper-fabric-devin/hooks/block-generated-artifact-edits.sh +141 -0
  591. package/plugins/lisa-harper-fabric-devin/hooks/devin-tool-name-shim.sh +30 -0
  592. package/plugins/lisa-harper-fabric-devin/hooks/enforce-config-extensions.mjs +196 -0
  593. package/plugins/lisa-harper-fabric-devin/hooks/enforce-config-extensions.sh +46 -0
  594. package/plugins/lisa-harper-fabric-devin/hooks/inject-rules.sh +27 -0
  595. package/plugins/lisa-harper-fabric-devin/hooks.json +36 -0
  596. package/plugins/lisa-harper-fabric-devin/rules/eager/harper-fabric.md +31 -0
  597. package/plugins/lisa-harper-fabric-devin/rules/reference/harper-fabric.md +52 -0
  598. package/plugins/lisa-harper-fabric-devin/skills/e2e-coverage-gaps/SKILL.md +117 -0
  599. package/plugins/lisa-harper-fabric-devin/skills/harper-auth/SKILL.md +368 -0
  600. package/plugins/lisa-harper-fabric-devin/skills/harper-build-and-deploy/SKILL.md +198 -0
  601. package/plugins/lisa-harper-fabric-devin/skills/harper-caching/SKILL.md +265 -0
  602. package/plugins/lisa-harper-fabric-devin/skills/harper-component-model/SKILL.md +87 -0
  603. package/plugins/lisa-harper-fabric-devin/skills/harper-config-yaml/SKILL.md +283 -0
  604. package/plugins/lisa-harper-fabric-devin/skills/harper-operations/SKILL.md +239 -0
  605. package/plugins/lisa-harper-fabric-devin/skills/harper-realtime/SKILL.md +187 -0
  606. package/plugins/lisa-harper-fabric-devin/skills/harper-resources/SKILL.md +142 -0
  607. package/plugins/lisa-harper-fabric-devin/skills/harper-rest-queries/SKILL.md +403 -0
  608. package/plugins/lisa-harper-fabric-devin/skills/harper-schema-graphql/SKILL.md +123 -0
  609. package/plugins/lisa-harper-fabric-devin/skills/harper-testing/SKILL.md +254 -0
  610. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  611. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  612. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  613. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  614. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  615. package/plugins/lisa-nestjs-devin/.claude-plugin/plugin.json +11 -0
  616. package/plugins/lisa-nestjs-devin/.devin-plugin/plugin.json +8 -0
  617. package/plugins/lisa-nestjs-devin/hooks/block-migration-edits.sh +125 -0
  618. package/plugins/lisa-nestjs-devin/hooks/devin-tool-name-shim.sh +30 -0
  619. package/plugins/lisa-nestjs-devin/hooks/lisa-edit-gate.sh +152 -0
  620. package/plugins/lisa-nestjs-devin/hooks.json +15 -0
  621. package/plugins/lisa-nestjs-devin/skills/nestjs-graphql/SKILL.md +176 -0
  622. package/plugins/lisa-nestjs-devin/skills/nestjs-graphql/references/advanced-features.md +527 -0
  623. package/plugins/lisa-nestjs-devin/skills/nestjs-graphql/references/project-patterns.md +531 -0
  624. package/plugins/lisa-nestjs-devin/skills/nestjs-graphql/references/quick-start.md +257 -0
  625. package/plugins/lisa-nestjs-devin/skills/nestjs-graphql/references/resolvers-mutations.md +413 -0
  626. package/plugins/lisa-nestjs-devin/skills/nestjs-graphql/references/types-scalars.md +513 -0
  627. package/plugins/lisa-nestjs-devin/skills/nestjs-rules/SKILL.md +560 -0
  628. package/plugins/lisa-nestjs-devin/skills/typeorm-patterns/SKILL.md +275 -0
  629. package/plugins/lisa-nestjs-devin/skills/typeorm-patterns/references/configuration-patterns.md +487 -0
  630. package/plugins/lisa-nestjs-devin/skills/typeorm-patterns/references/entity-patterns.md +450 -0
  631. package/plugins/lisa-nestjs-devin/skills/typeorm-patterns/references/observability-patterns.md +536 -0
  632. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  633. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  634. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  635. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  636. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  637. package/plugins/lisa-openclaw-devin/.claude-plugin/plugin.json +8 -0
  638. package/plugins/lisa-openclaw-devin/.devin-plugin/plugin.json +8 -0
  639. package/plugins/lisa-openclaw-devin/skills/lisa-openclaw-connect-repo-topic/SKILL.md +163 -0
  640. package/plugins/lisa-openclaw-devin/skills/lisa-openclaw-connect-repo-topic/references/repo-topic-config.md +185 -0
  641. package/plugins/lisa-openclaw-devin/skills/lisa-openclaw-connect-staff/SKILL.md +175 -0
  642. package/plugins/lisa-openclaw-devin/skills/lisa-openclaw-connect-staff/references/platform-routing.md +123 -0
  643. package/plugins/lisa-openclaw-devin/skills/lisa-openclaw-connect-staff/references/prompts.md +78 -0
  644. package/plugins/lisa-openclaw-devin/skills/lisa-openclaw-setup/SKILL.md +141 -0
  645. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  646. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  647. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  648. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  649. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  650. package/plugins/lisa-phaser-devin/.claude-plugin/plugin.json +11 -0
  651. package/plugins/lisa-phaser-devin/.devin-plugin/plugin.json +8 -0
  652. package/plugins/lisa-phaser-devin/agents/accessibility-advocate.md +55 -0
  653. package/plugins/lisa-phaser-devin/agents/art-director.md +90 -0
  654. package/plugins/lisa-phaser-devin/agents/audio-director.md +65 -0
  655. package/plugins/lisa-phaser-devin/agents/combat-designer.md +61 -0
  656. package/plugins/lisa-phaser-devin/agents/economy-designer.md +59 -0
  657. package/plugins/lisa-phaser-devin/agents/game-designer.md +62 -0
  658. package/plugins/lisa-phaser-devin/agents/game-feel-specialist.md +56 -0
  659. package/plugins/lisa-phaser-devin/agents/level-designer.md +59 -0
  660. package/plugins/lisa-phaser-devin/agents/localization-manager.md +61 -0
  661. package/plugins/lisa-phaser-devin/agents/marketing-strategist.md +59 -0
  662. package/plugins/lisa-phaser-devin/agents/monetization-designer.md +59 -0
  663. package/plugins/lisa-phaser-devin/agents/narrative-designer.md +56 -0
  664. package/plugins/lisa-phaser-devin/agents/onboarding-advocate.md +59 -0
  665. package/plugins/lisa-phaser-devin/agents/player-advocate.md +57 -0
  666. package/plugins/lisa-phaser-devin/agents/producer.md +61 -0
  667. package/plugins/lisa-phaser-devin/agents/product-analyst.md +62 -0
  668. package/plugins/lisa-phaser-devin/agents/publisher.md +59 -0
  669. package/plugins/lisa-phaser-devin/agents/qa-playtester.md +57 -0
  670. package/plugins/lisa-phaser-devin/agents/target-player.md +52 -0
  671. package/plugins/lisa-phaser-devin/agents/ux-ui-designer.md +60 -0
  672. package/plugins/lisa-phaser-devin/hooks/devin-tool-name-shim.sh +30 -0
  673. package/plugins/lisa-phaser-devin/hooks/inject-rules.sh +27 -0
  674. package/plugins/lisa-phaser-devin/hooks.json +14 -0
  675. package/plugins/lisa-phaser-devin/rules/eager/phaser.md +44 -0
  676. package/plugins/lisa-phaser-devin/rules/reference/phaser.md +184 -0
  677. package/plugins/lisa-phaser-devin/skills/phaser-accessibility/SKILL.md +135 -0
  678. package/plugins/lisa-phaser-devin/skills/phaser-asset-pipeline/SKILL.md +222 -0
  679. package/plugins/lisa-phaser-devin/skills/phaser-asset-sourcing/SKILL.md +151 -0
  680. package/plugins/lisa-phaser-devin/skills/phaser-build-deploy/SKILL.md +153 -0
  681. package/plugins/lisa-phaser-devin/skills/phaser-i18n/SKILL.md +122 -0
  682. package/plugins/lisa-phaser-devin/skills/phaser-project-structure/SKILL.md +89 -0
  683. package/plugins/lisa-phaser-devin/skills/phaser-services/SKILL.md +208 -0
  684. package/plugins/lisa-phaser-devin/skills/phaser-testing/SKILL.md +192 -0
  685. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  686. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  687. package/plugins/lisa-rails-agy/plugin.json +1 -1
  688. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  689. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  690. package/plugins/lisa-rails-devin/.claude-plugin/plugin.json +11 -0
  691. package/plugins/lisa-rails-devin/.devin-plugin/plugin.json +8 -0
  692. package/plugins/lisa-rails-devin/agents/ops-specialist.md +226 -0
  693. package/plugins/lisa-rails-devin/hooks/devin-tool-name-shim.sh +30 -0
  694. package/plugins/lisa-rails-devin/hooks/inject-rules.sh +40 -0
  695. package/plugins/lisa-rails-devin/hooks/lisa-edit-gate.sh +152 -0
  696. package/plugins/lisa-rails-devin/hooks/rubocop-on-edit.sh +101 -0
  697. package/plugins/lisa-rails-devin/hooks/sg-scan-on-edit.sh +97 -0
  698. package/plugins/lisa-rails-devin/hooks.json +29 -0
  699. package/plugins/lisa-rails-devin/rules/eager/rails-conventions.md +23 -0
  700. package/plugins/lisa-rails-devin/rules/reference/rails-conventions.md +176 -0
  701. package/plugins/lisa-rails-devin/skills/action-controller-best-practices/SKILL.md +374 -0
  702. package/plugins/lisa-rails-devin/skills/action-view-best-practices/SKILL.md +335 -0
  703. package/plugins/lisa-rails-devin/skills/active-record-model-best-practices/SKILL.md +166 -0
  704. package/plugins/lisa-rails-devin/skills/e2e-coverage-gaps/SKILL.md +117 -0
  705. package/plugins/lisa-rails-devin/skills/fix-linter-error/SKILL.md +45 -0
  706. package/plugins/lisa-rails-devin/skills/improve-code-complexity/SKILL.md +51 -0
  707. package/plugins/lisa-rails-devin/skills/improve-max-lines/SKILL.md +46 -0
  708. package/plugins/lisa-rails-devin/skills/improve-max-lines-per-function/SKILL.md +46 -0
  709. package/plugins/lisa-rails-devin/skills/improve-test-coverage/SKILL.md +48 -0
  710. package/plugins/lisa-rails-devin/skills/jira-add-journey/SKILL.md +79 -0
  711. package/plugins/lisa-rails-devin/skills/jira-create/SKILL.md +100 -0
  712. package/plugins/lisa-rails-devin/skills/jira-evidence/SKILL.md +76 -0
  713. package/plugins/lisa-rails-devin/skills/jira-evidence/scripts/post-evidence.sh +262 -0
  714. package/plugins/lisa-rails-devin/skills/jira-journey/SKILL.md +65 -0
  715. package/plugins/lisa-rails-devin/skills/jira-verify/SKILL.md +26 -0
  716. package/plugins/lisa-rails-devin/skills/ops-check-logs/SKILL.md +191 -0
  717. package/plugins/lisa-rails-devin/skills/ops-deploy/SKILL.md +153 -0
  718. package/plugins/lisa-rails-devin/skills/ops-run-local/SKILL.md +169 -0
  719. package/plugins/lisa-rails-devin/skills/ops-verify-jobs/SKILL.md +157 -0
  720. package/plugins/lisa-rails-devin/skills/ops-verify-telemetry/SKILL.md +226 -0
  721. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  722. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  723. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  724. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  725. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  726. package/plugins/lisa-typescript-devin/.claude-plugin/plugin.json +11 -0
  727. package/plugins/lisa-typescript-devin/.devin-plugin/plugin.json +8 -0
  728. package/plugins/lisa-typescript-devin/hooks/block-suppress-directives.sh +141 -0
  729. package/plugins/lisa-typescript-devin/hooks/devin-tool-name-shim.sh +30 -0
  730. package/plugins/lisa-typescript-devin/hooks/format-on-edit.sh +102 -0
  731. package/plugins/lisa-typescript-devin/hooks/lint-on-edit.sh +117 -0
  732. package/plugins/lisa-typescript-devin/hooks/lisa-edit-gate.sh +152 -0
  733. package/plugins/lisa-typescript-devin/hooks/sg-scan-on-edit.sh +94 -0
  734. package/plugins/lisa-typescript-devin/hooks.json +34 -0
  735. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  736. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  737. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  738. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  739. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  740. package/plugins/lisa-wiki-devin/.claude-plugin/plugin.json +8 -0
  741. package/plugins/lisa-wiki-devin/.devin-plugin/plugin.json +8 -0
  742. package/plugins/lisa-wiki-devin/ci/lisa-wiki-validate.yml +33 -0
  743. package/plugins/lisa-wiki-devin/schema/lisa-wiki-config.schema.json +162 -0
  744. package/plugins/lisa-wiki-devin/schema/wiki-structure.schema.json +51 -0
  745. package/plugins/lisa-wiki-devin/scripts/_wiki-lib.mjs +204 -0
  746. package/plugins/lisa-wiki-devin/scripts/diff-guard.mjs +117 -0
  747. package/plugins/lisa-wiki-devin/scripts/ensure-gitignore.mjs +131 -0
  748. package/plugins/lisa-wiki-devin/scripts/ensure-wiki.mjs +268 -0
  749. package/plugins/lisa-wiki-devin/scripts/ingest-git.mjs +198 -0
  750. package/plugins/lisa-wiki-devin/scripts/ingest-memory.mjs +135 -0
  751. package/plugins/lisa-wiki-devin/scripts/ingest-roles.mjs +95 -0
  752. package/plugins/lisa-wiki-devin/scripts/ingest_slack_channel.py +361 -0
  753. package/plugins/lisa-wiki-devin/scripts/lib/bounded-child.mjs +159 -0
  754. package/plugins/lisa-wiki-devin/scripts/lint-wiki.mjs +513 -0
  755. package/plugins/lisa-wiki-devin/scripts/mcp-doctor.mjs +72 -0
  756. package/plugins/lisa-wiki-devin/scripts/render-contract.mjs +107 -0
  757. package/plugins/lisa-wiki-devin/scripts/rewrite-refs.mjs +144 -0
  758. package/plugins/lisa-wiki-devin/scripts/slack_oauth_user.py +179 -0
  759. package/plugins/lisa-wiki-devin/scripts/validate-config.mjs +321 -0
  760. package/plugins/lisa-wiki-devin/scripts/verify-migration.mjs +203 -0
  761. package/plugins/lisa-wiki-devin/scripts/verify-wiki-safety.mjs +126 -0
  762. package/plugins/lisa-wiki-devin/scripts/wiki-safety.mjs +378 -0
  763. package/plugins/lisa-wiki-devin/scripts/wiki-status.mjs +345 -0
  764. package/plugins/lisa-wiki-devin/skills/lisa-wiki-add-ingest/SKILL.md +34 -0
  765. package/plugins/lisa-wiki-devin/skills/lisa-wiki-add-role/SKILL.md +35 -0
  766. package/plugins/lisa-wiki-devin/skills/lisa-wiki-connector-confluence/SKILL.md +25 -0
  767. package/plugins/lisa-wiki-devin/skills/lisa-wiki-connector-docs/SKILL.md +30 -0
  768. package/plugins/lisa-wiki-devin/skills/lisa-wiki-connector-git/SKILL.md +25 -0
  769. package/plugins/lisa-wiki-devin/skills/lisa-wiki-connector-jira/SKILL.md +28 -0
  770. package/plugins/lisa-wiki-devin/skills/lisa-wiki-connector-memory/SKILL.md +28 -0
  771. package/plugins/lisa-wiki-devin/skills/lisa-wiki-connector-notion/SKILL.md +25 -0
  772. package/plugins/lisa-wiki-devin/skills/lisa-wiki-connector-roles/SKILL.md +22 -0
  773. package/plugins/lisa-wiki-devin/skills/lisa-wiki-connector-slack/SKILL.md +30 -0
  774. package/plugins/lisa-wiki-devin/skills/lisa-wiki-connector-web/SKILL.md +23 -0
  775. package/plugins/lisa-wiki-devin/skills/lisa-wiki-doctor/SKILL.md +47 -0
  776. package/plugins/lisa-wiki-devin/skills/lisa-wiki-ingest/SKILL.md +106 -0
  777. package/plugins/lisa-wiki-devin/skills/lisa-wiki-lint/SKILL.md +32 -0
  778. package/plugins/lisa-wiki-devin/skills/lisa-wiki-migrate/SKILL.md +43 -0
  779. package/plugins/lisa-wiki-devin/skills/lisa-wiki-onboard-me/SKILL.md +33 -0
  780. package/plugins/lisa-wiki-devin/skills/lisa-wiki-query/SKILL.md +40 -0
  781. package/plugins/lisa-wiki-devin/skills/lisa-wiki-setup/SKILL.md +75 -0
  782. package/plugins/lisa-wiki-devin/skills/lisa-wiki-setup-automations/SKILL.md +83 -0
  783. package/plugins/lisa-wiki-devin/skills/lisa-wiki-status/SKILL.md +47 -0
  784. package/plugins/lisa-wiki-devin/skills/lisa-wiki-tear-down-automations/SKILL.md +34 -0
  785. package/plugins/lisa-wiki-devin/skills/lisa-wiki-usage/SKILL.md +50 -0
  786. package/plugins/lisa-wiki-devin/templates/agents/role-agent.claude.md +16 -0
  787. package/plugins/lisa-wiki-devin/templates/agents/role-agent.codex.toml +15 -0
  788. package/plugins/lisa-wiki-devin/templates/index.md +17 -0
  789. package/plugins/lisa-wiki-devin/templates/llm-wiki-contract.md +72 -0
  790. package/plugins/lisa-wiki-devin/templates/log.md +8 -0
  791. package/plugins/lisa-wiki-devin/templates/page-types/architecture.md +18 -0
  792. package/plugins/lisa-wiki-devin/templates/page-types/concept.md +18 -0
  793. package/plugins/lisa-wiki-devin/templates/page-types/decision.md +18 -0
  794. package/plugins/lisa-wiki-devin/templates/page-types/entity.md +19 -0
  795. package/plugins/lisa-wiki-devin/templates/page-types/open-question.md +18 -0
  796. package/plugins/lisa-wiki-devin/templates/page-types/playbook.md +18 -0
  797. package/plugins/lisa-wiki-devin/templates/page-types/project.md +19 -0
  798. package/plugins/lisa-wiki-devin/templates/page-types/requirement.md +19 -0
  799. package/plugins/lisa-wiki-devin/templates/page-types/staff.md +26 -0
  800. package/plugins/lisa-wiki-devin/templates/start-here.md +24 -0
  801. package/plugins/lisa-wiki-devin/templates/state-readme.md +20 -0
  802. package/plugins/lisa-wiki-devin/templates/wrapper-gitignore.txt +19 -0
  803. package/scripts/build-plugins.sh +2 -2
  804. package/scripts/deployed-guard-advice.mjs +1 -0
  805. package/scripts/generate-devin-plugin-artifacts.mjs +263 -0
  806. package/scripts/lib/hook-registration-audit.mjs +3 -1
  807. package/scripts/lib/per-agent-hook-filter.mjs +174 -5
  808. package/scripts/lib/plugin-rule-citations.mjs +2 -2
  809. package/scripts/two-channel-couplings.json +1 -1
@@ -0,0 +1,1383 @@
1
+ #!/usr/bin/env node
2
+ // This file is managed by Lisa and IS replaced on each `lisa` run.
3
+ // Do not edit directly — durable changes belong upstream in Lisa.
4
+
5
+ /**
6
+ * Reconcile the worktree a session was TOLD it is in against the one it is
7
+ * ACTUALLY operating in, and refuse to act while the two disagree.
8
+ *
9
+ * WHAT WENT WRONG. `EnterWorktree` reported "the session is now working in the
10
+ * worktree" for a tree that Bash never moved to and that Edit then refused by
11
+ * name (CodySwannGT/lisa#3864). Three subsystems, three answers, and the one
12
+ * that answered affirmatively was the wrong one. A refusal teaches you
13
+ * something; a success message that is not true is a confirmation — it removes
14
+ * the reason to check and sends the agent forward confidently in the wrong
15
+ * place. Acting on that confirmation, one session ran an install that landed in
16
+ * a different agent's active worktree. The next such command is a `git commit`
17
+ * or a file write, and that tree held eighteen modified tracked files.
18
+ *
19
+ * The displacement is also mutual: whoever calls `EnterWorktree` last wins for
20
+ * every session whose own binding never took, so each agent repairing itself
21
+ * displaces the others. That is why "just re-enter your worktree" is not a
22
+ * remedy — it is the mechanism.
23
+ *
24
+ * WHAT THIS FIXES AND WHAT IT CANNOT. Lisa does not own `EnterWorktree`, so it
25
+ * cannot make that call itself return failure. What it can do is make the
26
+ * false confirmation cost nothing: the FIRST action taken on the strength of it
27
+ * is refused, and the refusal names the worktree the session is really bound
28
+ * to. The gap is stated rather than hidden — the acceptance criterion asking
29
+ * `EnterWorktree` to fail is met one tool call later, by the guard, not by the
30
+ * tool.
31
+ *
32
+ * WHY BLOCKING AND NOT A WARNING. The failure mode is that nothing feels wrong.
33
+ * There is no symptom to notice, so an advisory line scrolls past exactly when
34
+ * it matters. A refusal is the only form of this check the agent cannot read
35
+ * past.
36
+ *
37
+ * WHY AN EXPLICIT ACKNOWLEDGEMENT AND NOT "BLOCK ONCE". A guard that blocks the
38
+ * first attempt and lets the retry through is defeated by a blind retry, which
39
+ * is the single most likely next action. So the block stands until the session
40
+ * states, in the acknowledgement, the absolute path it now intends to work in.
41
+ * An acknowledgement can choose the observed root, return to the initial root,
42
+ * or accept a linked target this session was explicitly offered. A selected
43
+ * target does not prove a move: the next guarded call must actually be there.
44
+ *
45
+ * FAILING OPEN, LOUDLY. Missing input or unavailable I/O — no session id, no
46
+ * cwd, not a git repository, an unreadable state file — retains the measured
47
+ * fail-open behavior. A readable state file with missing, mismatched or damaged
48
+ * ownership is a different fact: it cannot authorize a baseline or be silently
49
+ * overwritten. Legacy ambiguity requires explicit intent; a conflicting reserved
50
+ * slot requires a new session identity.
51
+ */
52
+
53
+ import { execFileSync } from "node:child_process";
54
+ import { createHash } from "node:crypto";
55
+ import {
56
+ mkdirSync,
57
+ readFileSync,
58
+ realpathSync,
59
+ statSync,
60
+ writeFileSync,
61
+ } from "node:fs";
62
+ import { homedir } from "node:os";
63
+ import { dirname, isAbsolute, join, resolve } from "node:path";
64
+
65
+ /** Tools whose effect on the wrong worktree is not undoable by re-reading. */
66
+ const GUARDED_TOOLS = new Set(["Bash", "Write", "Edit", "MultiEdit"]);
67
+
68
+ /**
69
+ * The literal an agent echoes to rebind this session deliberately.
70
+ *
71
+ * A shell line rather than a flag on this script, because the only surface the
72
+ * agent has for saying so is a Bash command, and it must be one this guard sees
73
+ * on its own PreToolUse pass — which is exactly where a command string arrives.
74
+ */
75
+ const ACCEPT_PREFIX = "lisa-worktree-binding: accept";
76
+ const ACK_AND_SEPARATOR = Symbol("acknowledgement AND separator");
77
+ const BINDING_VERSION = 4;
78
+ const CURRENT_BINDING = Symbol("current binding state");
79
+ const UNAVAILABLE_BINDING = Symbol("unavailable binding read");
80
+ const BINDING_WRITE_CONFLICT = Symbol("binding write conflict");
81
+
82
+ /**
83
+ * Resolve a path the way git reports one.
84
+ *
85
+ * `git rev-parse --show-toplevel` answers with symlinks resolved, and on macOS
86
+ * every temporary directory is a symlink — `/var/…` is `/private/var/…`. A
87
+ * comparison between git's answer and a path handed in by a tool would then
88
+ * differ by that prefix alone and read as a displacement, which is a false
89
+ * accusation in exactly the state the guard is supposed to be trusted in. A
90
+ * path that does not exist yet cannot be resolved and is returned as given.
91
+ * @param value - Absolute path to normalize
92
+ * @returns The path with symlinks resolved where possible
93
+ */
94
+ function realpath(value) {
95
+ try {
96
+ return realpathSync(value);
97
+ } catch {
98
+ return value;
99
+ }
100
+ }
101
+
102
+ function say(message) {
103
+ process.stderr.write(`worktree-binding-guard: ${message}\n`);
104
+ }
105
+
106
+ /** Run git, returning trimmed stdout, or null when it exits non-zero. */
107
+ function git(args, cwd) {
108
+ try {
109
+ return execFileSync("git", args, {
110
+ cwd,
111
+ encoding: "utf8",
112
+ stdio: ["ignore", "pipe", "ignore"],
113
+ }).trim();
114
+ } catch {
115
+ // probe-direction: neutral — this helper REPORTS, it does not decide. Both
116
+ // callers below read null and each one says out loud what it could not do
117
+ // before returning, so no gate outcome follows from this null silently
118
+ // (#3848). Keep that true: a caller added here that spends null as an
119
+ // answer has to say so at its own line.
120
+ return null;
121
+ }
122
+ }
123
+
124
+ /**
125
+ * The worktree root containing `cwd`, or null when `cwd` is not in a repo.
126
+ *
127
+ * `--show-toplevel` exits non-zero for two OPPOSITE reasons and hands back the
128
+ * same nothing for both: `cwd` is outside any repository — a real absence, and
129
+ * a call there is genuinely none of this guard's business — or git could not
130
+ * answer at all, where the binding is unproven rather than inapplicable. Only
131
+ * the second is a withdrawal of enforcement, and it is the one that must not
132
+ * pass for the first. `git --version` succeeds wherever git can run, so its
133
+ * failure is what separates them.
134
+ * @param {string} cwd Directory the tool call runs in.
135
+ * @returns {string|null} Absolute worktree root, or null.
136
+ */
137
+ function worktreeRoot(cwd) {
138
+ const top = git(["rev-parse", "--show-toplevel"], cwd);
139
+ if (top) return resolve(top);
140
+ if (git(["--version"], cwd) === null) {
141
+ say("git could not be run here, so the worktree binding is NOT enforced");
142
+ }
143
+ return null;
144
+ }
145
+
146
+ /**
147
+ * The MAIN checkout's root, which is where `.claude/worktrees/` lives.
148
+ *
149
+ * `--git-common-dir` is shared by every linked worktree, so its parent is the
150
+ * main checkout no matter which worktree asks.
151
+ */
152
+ function mainCheckout(cwd) {
153
+ const common = git(
154
+ ["rev-parse", "--path-format=absolute", "--git-common-dir"],
155
+ cwd
156
+ );
157
+ if (common) return dirname(resolve(common));
158
+ // Reached only for an `EnterWorktree` naming a worktree, which means a
159
+ // repository was already in hand — so failing here is "could not ask", and
160
+ // the consequence is that the claim goes unrecorded and the NEXT call's
161
+ // displacement check has nothing to compare against. Said out loud rather
162
+ // than returned as a quiet null (#3848).
163
+ say("git could not resolve the main checkout, so this claim is NOT recorded");
164
+ return null;
165
+ }
166
+
167
+ /** Registered roots of the bound repository, with Git path quoting disabled. */
168
+ function linkedWorktreeRoots(bound) {
169
+ const listing = git(["worktree", "list", "--porcelain", "-z"], bound);
170
+ if (listing === null) {
171
+ say(
172
+ "git could not identify linked worktrees; repository identity is NOT enforced"
173
+ );
174
+ return null;
175
+ }
176
+ return new Set(
177
+ listing
178
+ .split("\0")
179
+ .filter(record => record.startsWith("worktree "))
180
+ .map(record => realpath(record.slice("worktree ".length)))
181
+ );
182
+ }
183
+
184
+ /** Canonical common Git directory: stable identity across linked checkouts. */
185
+ function repositoryIdentity(root) {
186
+ if (typeof root !== "string") return null;
187
+ const common = git(["rev-parse", "--git-common-dir"], root);
188
+ if (common === null) {
189
+ say("git could not compare repository identity; identity is NOT enforced");
190
+ return null;
191
+ }
192
+ return realpath(resolve(root, common));
193
+ }
194
+
195
+ /** A registration is only authoritative while its live repository agrees. */
196
+ function isLinkedWorktree(
197
+ bound,
198
+ candidate,
199
+ roots = linkedWorktreeRoots(bound)
200
+ ) {
201
+ if (!roots?.has(candidate) || worktreeRoot(candidate) !== candidate)
202
+ return false;
203
+ const common = repositoryIdentity(bound);
204
+ return common !== null && common === repositoryIdentity(candidate);
205
+ }
206
+
207
+ /**
208
+ * How much of an executed script this guard will read, in bytes.
209
+ *
210
+ * A generated fixture can be megabytes, and a guard that reads all of it on
211
+ * every Bash call is a guard someone switches off.
212
+ */
213
+ const SCRIPT_READ_LIMIT = 256 * 1024;
214
+
215
+ /**
216
+ * Programs that take their operands as DATA and never execute them.
217
+ *
218
+ * The same split `block-direct-issue-create.sh` makes, and for the same reason
219
+ * it had to: a guard that opens every file a command names, then judges the
220
+ * COMMAND by that FILE's contents, refuses ordinary inspection. Reading a
221
+ * script that visits another worktree is not visiting it.
222
+ *
223
+ * The list is an exemption, not an allowlist. Anything unrecognised is
224
+ * FOLLOWED, because an interpreter roster fails open on the runner nobody
225
+ * enumerated.
226
+ */
227
+ const READ_ONLY_PROGRAMS = new Set([
228
+ "awk",
229
+ "basename",
230
+ "cat",
231
+ "cksum",
232
+ "cmp",
233
+ "comm",
234
+ "cut",
235
+ "diff",
236
+ "dirname",
237
+ "du",
238
+ "file",
239
+ "find",
240
+ "grep",
241
+ "egrep",
242
+ "fgrep",
243
+ "head",
244
+ "less",
245
+ "ls",
246
+ "md5",
247
+ "md5sum",
248
+ "more",
249
+ "nl",
250
+ "od",
251
+ "realpath",
252
+ "rg",
253
+ "sed",
254
+ "sha1sum",
255
+ "sha256sum",
256
+ "sort",
257
+ "stat",
258
+ "strings",
259
+ "tail",
260
+ "tee",
261
+ "tr",
262
+ "uniq",
263
+ "wc",
264
+ "xxd",
265
+ ]);
266
+
267
+ /** Interpreters whose first non-option operand is the file they run. */
268
+ const EXECUTING_INTERPRETERS = new Set([
269
+ "bash",
270
+ "dash",
271
+ "ksh",
272
+ "node",
273
+ "perl",
274
+ "python",
275
+ "python3",
276
+ "ruby",
277
+ "sh",
278
+ "zsh",
279
+ "bun",
280
+ "deno",
281
+ "tsx",
282
+ ]);
283
+
284
+ /** Shell builtins that run a file in the CURRENT shell. */
285
+ const SOURCE_BUILTINS = new Set([".", "source"]);
286
+
287
+ /** Where one command ends and the next begins. */
288
+ const COMMAND_SEPARATORS = /\|\||&&|[;|&\n]/u;
289
+
290
+ /**
291
+ * Split a command into tokens, honouring simple quoting.
292
+ *
293
+ * Deliberately simple, and the guard fails OPEN when it is not enough — see
294
+ * {@link scriptReachingForeignTree}. A tokeniser that guessed would be worse
295
+ * than one that says it does not know.
296
+ */
297
+ function tokenise(segment) {
298
+ const tokens = segment.match(/"[^"]*"|'[^']*'|[^\s]+/gu) ?? [];
299
+ return tokens.map(token => token.replace(/^["']|["']$/gu, ""));
300
+ }
301
+
302
+ /**
303
+ * The path this command EXECUTES, or null.
304
+ *
305
+ * The command-position question. A path anywhere else is an argument, and an
306
+ * argument is data.
307
+ */
308
+ function executedPath(segment) {
309
+ const tokens = tokenise(segment);
310
+ if (tokens.length === 0) return null;
311
+ const [head, ...rest] = tokens;
312
+ const program = head.split("/").pop() ?? "";
313
+ if (READ_ONLY_PROGRAMS.has(program)) return null;
314
+ if (SOURCE_BUILTINS.has(program)) return rest[0] ?? null;
315
+ if (!EXECUTING_INTERPRETERS.has(program)) {
316
+ // A command word that is itself a file runs by its shebang.
317
+ return head.includes("/") ? head : null;
318
+ }
319
+ // A known interpreter: the first operand that is not an option.
320
+ const operand = rest.find(token => !token.startsWith("-"));
321
+ return operand ?? null;
322
+ }
323
+
324
+ /**
325
+ * A worktree root other than `bound` that this text reaches into, or null.
326
+ *
327
+ * ## Why this does not enumerate `cd`
328
+ *
329
+ * The redirect spellings — `cd`, `git -C`, `--work-tree`, `--git-dir`, a
330
+ * subshell, `env -C`, a variable holding the path — are an open set, and
331
+ * enumerating them is the failure this arm exists to end
332
+ * (CodySwannGT/lisa#3927 states it for the sibling guard). So the question is
333
+ * inverted the same way the filing guard inverted its own: not *which syntax
334
+ * moves*, which is unbounded, but *which tokens name a directory in another
335
+ * worktree*, which is bounded by the file.
336
+ *
337
+ * A path only has to RESOLVE. How it arrived does not matter, which is what
338
+ * makes an unenumerated spelling reach the same verdict as `cd`.
339
+ *
340
+ * ## Why comments are stripped first
341
+ *
342
+ * A script that merely MENTIONS a sibling worktree — a usage line, a comment,
343
+ * a log message — would otherwise be refused for describing the thing rather
344
+ * than doing it. Prose about a subject is the likeliest text to contain the
345
+ * subject's shapes, and the population that writes it is the people
346
+ * documenting the guard. A parity classifier elsewhere in this repository was
347
+ * fooled by exactly that and was caught only by a known-answer control.
348
+ */
349
+ function foreignTreeIn(text, bound, cwd) {
350
+ let linkedRoots;
351
+ const code = text
352
+ .split("\n")
353
+ .map(line => line.replace(/^\s*#.*$/u, ""))
354
+ .join("\n");
355
+ for (const raw of code.match(/[^\s'";|&()<>]+/gu) ?? []) {
356
+ const token = raw.replace(/^["']|["']$/gu, "");
357
+ if (!token.includes("/")) continue;
358
+ // A null base means an unliteral `cd` left the directory this script runs
359
+ // from unknown. An ABSOLUTE token is unambiguous either way; a relative one
360
+ // is exactly the thing that cannot be placed, so it is skipped rather than
361
+ // resolved against a directory picked for having been handy.
362
+ if (!isAbsolute(token) && cwd === null) continue;
363
+ const candidate = isAbsolute(token) ? token : join(cwd, token);
364
+ let root;
365
+ try {
366
+ if (!statSync(candidate).isDirectory()) continue;
367
+ root = worktreeRoot(realpathSync(candidate));
368
+ } catch {
369
+ continue;
370
+ }
371
+ if (root && root !== bound) {
372
+ if (linkedRoots === undefined) linkedRoots = linkedWorktreeRoots(bound);
373
+ if (isLinkedWorktree(bound, root, linkedRoots)) return root;
374
+ }
375
+ }
376
+ return null;
377
+ }
378
+
379
+ /**
380
+ * A `cd` target this guard cannot turn into a literal directory.
381
+ *
382
+ * Mirrors `parity-safety-net.sh`'s `follow_cd` test exactly, and the mirror is
383
+ * the point: two spellings of one rule are how the guards drift.
384
+ */
385
+ const UNLITERAL_CD_TARGET = /[$`*?[{]/u;
386
+
387
+ /**
388
+ * The literal target of a leading `cd`, `""` for a bare `cd`, or null when the
389
+ * segment does not change directory.
390
+ *
391
+ * A bare `cd` is `cd "$HOME"`. Leaving the previous directory in place for it
392
+ * would resolve later tokens from a directory the shell has already left.
393
+ * @param segment - One statement from the command
394
+ * @returns The target text, or null when this segment is not a `cd`
395
+ */
396
+ function cdTarget(segment) {
397
+ const tokens = tokenise(segment);
398
+ if (tokens.length === 0 || tokens[0] !== "cd") return null;
399
+ return tokens[1] ?? "";
400
+ }
401
+
402
+ /**
403
+ * Apply one `cd` to the directory currently in force.
404
+ *
405
+ * Contract points 1-4 of `cwd-resolution-corpus.json`: a literal target
406
+ * REPLACES the directory rather than joining a list of candidates — the
407
+ * ordering was #3933's entire defect; a relative target composes against the
408
+ * directory in force; a target that is not literal text yields unknown; and
409
+ * unknown is sticky only until the next resolvable `cd`, because this returns
410
+ * a fresh answer for each one.
411
+ * @param current - Directory in force, or null when it is already unknown
412
+ * @param target - Literal `cd` target, `""` for a bare `cd`
413
+ * @returns The new directory, or null when it cannot be known
414
+ */
415
+ function applyCd(current, target) {
416
+ const home = homedir();
417
+ let value = target === "" ? home : target;
418
+ if (value === "~") value = home;
419
+ else if (value.startsWith("~/")) value = join(home, value.slice(2));
420
+ if (
421
+ value === "" ||
422
+ value.startsWith("-") ||
423
+ UNLITERAL_CD_TARGET.test(value)
424
+ ) {
425
+ return null;
426
+ }
427
+ if (!isAbsolute(value) && current === null) return null;
428
+ const next = isAbsolute(value) ? resolve(value) : resolve(current, value);
429
+ try {
430
+ return statSync(next).isDirectory() ? next : null;
431
+ } catch {
432
+ return null;
433
+ }
434
+ }
435
+
436
+ /**
437
+ * The foreign worktree an executed script reaches, or null.
438
+ *
439
+ * ## Why this arm exists at all
440
+ *
441
+ * The check above compares `payload.cwd` against the bound root, and that is
442
+ * the right comparison — but **`payload.cwd` is sampled before the child
443
+ * process runs**. A script that changes directory internally moves AFTER the
444
+ * guard has measured. The guard is not wrong about the target; it is right
445
+ * about a target that stops being the target one instruction later.
446
+ *
447
+ * A measurement taken before execution cannot bind what execution does
448
+ * (CodySwannGT/lisa#3924). So the only thing left is to read what is about to
449
+ * run.
450
+ *
451
+ * ## What this arm does NOT cover, stated rather than implied
452
+ *
453
+ * An INLINE redirect — `cd B && git status`, `git -C B status` — is not
454
+ * refused here. Those are already refused by the runtime's own worktree
455
+ * isolation, and duplicating a control is how two controls drift into
456
+ * disagreeing. The scripted form is the one neither covers, and it is the only
457
+ * cell this arm fills.
458
+ *
459
+ * That reason is a dependency on a runtime Lisa does not ship, and it can
460
+ * expire without anything here failing. It is pinned at
461
+ * {@link VERIFIED_RUNTIME_VERSION} and reported when the runtime moves off it
462
+ * — read that constant before widening this arm.
463
+ *
464
+ * ## A relative script path is resolved against the cwd the command will have
465
+ *
466
+ * A leading `cd` establishes the directory this guard resolves relative tokens
467
+ * from, because that is the directory the shell will be in once the command
468
+ * runs. Resolving against `payload.cwd` instead was CodySwannGT/lisa#3933's
469
+ * defect, inherited here knowingly and fixed in CodySwannGT/lisa#3952: on a
470
+ * machine running many worktrees of one repository, every tree holds its own
471
+ * copy of every script at the same relative path, and they differ by whatever
472
+ * each lane is doing.
473
+ *
474
+ * ## One contract, two implementations, and why that is the honest answer
475
+ *
476
+ * `parity-safety-net.sh` answers this same question — "which file will this
477
+ * command actually execute?" — in POSIX shell, where `follow_cd` mutates
478
+ * file-scope state that `follow_locate` reads. This guard is Node. **There is
479
+ * no call that reaches from here into a shell function's file-scope state**, so
480
+ * the duplication is not a factoring somebody skipped; it is one idea that
481
+ * cannot cross a language boundary as a function call.
482
+ *
483
+ * What binds the two is a written contract and a corpus of rows that BOTH
484
+ * guards are driven over in CI, so a divergence fails a test on the commit that
485
+ * causes it rather than surfacing later as a wrong verdict. Contract and corpus
486
+ * live in `tests/unit/hooks/support/cwd-resolution-corpus.json`.
487
+ *
488
+ * This is explicitly NOT unification, and CodySwannGT/lisa#3952 records it as
489
+ * such: two implementations remain. **A behaviour with no row in the corpus is
490
+ * free to diverge — add a row when you add a branch.**
491
+ *
492
+ * ## The two guards agree on the RESOLUTION, not on the verdict
493
+ *
494
+ * When the effective directory cannot be known, both guards decline to resolve
495
+ * the token. What they then do differs, and that difference is deliberate:
496
+ * `parity-safety-net.sh` refuses, because a destructive command it cannot see
497
+ * is the thing it exists to stop. This guard fails open, which is the doctrine
498
+ * every other undecidable case in this file follows. The corpus asserts the
499
+ * resolved path, not the exit code, precisely so that one contract can bind two
500
+ * guards whose fail directions are correctly different.
501
+ *
502
+ * Fails OPEN and says so, like every other undecidable case in this file.
503
+ */
504
+ function scriptReachingForeignTree(payload, bound) {
505
+ const command = payload.tool_input?.command;
506
+ if (typeof command !== "string" || !command) return null;
507
+ let cwd = payload.cwd;
508
+ for (const segment of command.split(COMMAND_SEPARATORS)) {
509
+ const target = cdTarget(segment);
510
+ if (target !== null) {
511
+ cwd = applyCd(cwd, target);
512
+ continue;
513
+ }
514
+ const named = executedPath(segment);
515
+ if (!named) continue;
516
+ if (!isAbsolute(named) && cwd === null) {
517
+ // Contract point 3: the effective directory is unknown, so WHICH copy of
518
+ // a same-named script would run is unknown too. Declining to resolve is
519
+ // the half shared with parity-safety-net.sh; failing open rather than
520
+ // refusing is this guard's own doctrine, and the corpus binds the two
521
+ // guards on the resolution precisely so it does not have to bind them
522
+ // on a verdict one of them would be wrong to reach.
523
+ say(
524
+ `cannot tell which directory ${named} would run from; NOT enforcing on it`
525
+ );
526
+ continue;
527
+ }
528
+ const path = isAbsolute(named) ? resolve(named) : resolve(cwd, named);
529
+ let text;
530
+ try {
531
+ if (statSync(path).size > SCRIPT_READ_LIMIT) {
532
+ say(`${path} is too large to inspect; NOT enforcing on it`);
533
+ continue;
534
+ }
535
+ text = readFileSync(path, "utf8");
536
+ } catch {
537
+ // Not a readable file: nothing was executed that this can read.
538
+ continue;
539
+ }
540
+ const foreign = foreignTreeIn(text, bound, cwd);
541
+ if (foreign) return { script: path, foreign };
542
+ }
543
+ return null;
544
+ }
545
+
546
+ function reachesOut(script, foreign, bound) {
547
+ return refuse([
548
+ "worktree-binding-guard: this command runs a script that reaches into",
549
+ "another worktree.",
550
+ ` script: ${script}`,
551
+ ` reaches: ${foreign}`,
552
+ ` bound to: ${bound}`,
553
+ "The access lives inside the file, after this guard measures the session.",
554
+ "Run the work from the tree it belongs to, or acknowledge that target:",
555
+ acceptanceLine(foreign),
556
+ "Then move to the acknowledged tree before further guarded calls.",
557
+ ]);
558
+ }
559
+
560
+ function stateFile(bindingKey) {
561
+ const home = process.env.LISA_STATE_HOME || join(homedir(), ".lisa");
562
+ return join(home, "worktree-binding", `${bindingKey}.json`);
563
+ }
564
+
565
+ /**
566
+ * Percent-escape every byte that is not filename-safe, so the output cannot
567
+ * contain `-` at all and two escaped parts can be joined on `--` without the
568
+ * joiner ever being smuggled in from either side.
569
+ *
570
+ * `:` and friends are folded too — the key lands in a filename and `:` is
571
+ * illegal on Windows/NTFS (CodySwannGT/lisa#4277). `encodeURIComponent`
572
+ * leaves `! ' ( ) *` alone, and `*` is equally illegal on Windows, so they
573
+ * are escaped by hand.
574
+ * @param {string} value - One component of the binding key
575
+ * @returns {string} A component containing only `[A-Za-z0-9._~]` and `%XX`
576
+ */
577
+ function keyPart(value) {
578
+ return encodeURIComponent(value)
579
+ .replaceAll(
580
+ /[!'()*]/g,
581
+ c => `%${c.charCodeAt(0).toString(16).toUpperCase()}`
582
+ )
583
+ .replaceAll("-", "%2D");
584
+ }
585
+
586
+ /**
587
+ * The identity a binding belongs to.
588
+ *
589
+ * Parallel subagents share the session's `session_id`, so keying the binding
590
+ * on it alone made each subagent's guarded call read the binding a sibling
591
+ * recorded — the sibling's worktree then read as "changed underneath it" and
592
+ * its writes were refused, or an acknowledgement rebound the file out from
593
+ * under the sibling (CodySwannGT/lisa#4277). The payload's `agent_id` is what
594
+ * distinguishes a subagent from the main agent, so a caller carrying one gets
595
+ * its own state file. The main agent carries no `agent_id` and uses a null
596
+ * agent component, also the fallback for runtimes that never send the field.
597
+ *
598
+ * v4 hashes the JSON tuple into a lowercase, bounded filename in its own
599
+ * directory. The digest fixes filename size and casing; the persisted exact
600
+ * tuple remains the authority. A conflicting owner in a reserved slot is
601
+ * refused, never overwritten. Earlier flat filenames were ambiguous across
602
+ * formats (CodySwannGT/lisa#4311).
603
+ * @param {object} payload - Native hook identity
604
+ * @returns {[string, string|null]} Session and normalized optional agent
605
+ */
606
+ function bindingIdentity(payload) {
607
+ const agent = payload?.agent_id;
608
+ return [
609
+ payload.session_id,
610
+ typeof agent === "string" && agent ? agent : null,
611
+ ];
612
+ }
613
+
614
+ /**
615
+ * Resolve the reserved slot without exposing filename-unsafe identity bytes.
616
+ * @param {object} payload - Native hook identity
617
+ * @returns {string} Versioned, bounded relative state-file stem
618
+ */
619
+ function bindingKey(payload) {
620
+ const digest = createHash("sha256")
621
+ .update(JSON.stringify(bindingIdentity(payload)))
622
+ .digest("hex");
623
+ return join(`v${BINDING_VERSION}`, digest);
624
+ }
625
+
626
+ /**
627
+ * The keys earlier versions wrote for this payload, newest first.
628
+ *
629
+ * The v1 key was the raw `session_id` (single shared file per session). v2
630
+ * joined sanitized session and agent on `--`; v3 escaped both components.
631
+ * All are inspected on upgrade, but only a matching v4 owner record grants
632
+ * authority. An unstamped file cannot distinguish its possible owners, so the
633
+ * guard requires explicit intent rather than guessing or silently re-baselining.
634
+ * Legacy files stay untouched and only safe filename stems are inspected.
635
+ * @param {object} payload - The hook payload
636
+ * @returns {string[]} Zero or more legacy keys this payload may have written
637
+ */
638
+ function legacyKeys(payload) {
639
+ const agent = payload?.agent_id;
640
+ const session = payload.session_id;
641
+ const perAgent = typeof agent === "string" && agent;
642
+ const san = value => value.replaceAll(/[^A-Za-z0-9._-]/g, "_");
643
+ const older = perAgent ? `${san(session)}--${san(agent)}` : session;
644
+ let escaped = null;
645
+ try {
646
+ escaped = perAgent
647
+ ? `${keyPart(session)}--${keyPart(agent)}`
648
+ : keyPart(session);
649
+ } catch {
650
+ // A lone surrogate could not have produced an escaped v3 filename.
651
+ }
652
+ return [...new Set([escaped, older])].filter(
653
+ key => typeof key === "string" && key && !/[\\/]/u.test(key)
654
+ );
655
+ }
656
+
657
+ /**
658
+ * Require the producer's version, exact tuple and an absolute binding root.
659
+ * A matching identity with a damaged root cannot authorize re-baselining.
660
+ * @param {object|null} state - Parsed local state
661
+ * @param {object} payload - Caller claiming the slot
662
+ * @returns {boolean} Whether the record identifies this caller
663
+ */
664
+ function ownsState(state, payload) {
665
+ const expected = bindingIdentity(payload);
666
+ return (
667
+ typeof state?.boundRoot === "string" &&
668
+ isAbsolute(state.boundRoot) &&
669
+ !state.boundRoot.includes("\0") &&
670
+ state?.bindingVersion === BINDING_VERSION &&
671
+ Array.isArray(state.bindingIdentity) &&
672
+ state.bindingIdentity.length === expected.length &&
673
+ state.bindingIdentity.every((part, index) => part === expected[index])
674
+ );
675
+ }
676
+
677
+ function readState(bindingKeyValue, payload) {
678
+ try {
679
+ const state = JSON.parse(readFileSync(stateFile(bindingKeyValue), "utf8"));
680
+ return ownsState(state, payload)
681
+ ? { ...state, [CURRENT_BINDING]: true }
682
+ : { unverifiedOwner: true, canonicalConflict: true };
683
+ } catch (error) {
684
+ if (error instanceof SyntaxError)
685
+ return { unverifiedOwner: true, canonicalConflict: true };
686
+ // Unavailable I/O proves neither absence nor conflicting ownership. Keep
687
+ // it separate so an exclusive write cannot mistake EACCES for a race.
688
+ if (error.code !== "ENOENT")
689
+ return { [UNAVAILABLE_BINDING]: error.message };
690
+ }
691
+ let ambiguous = false;
692
+ for (const key of legacyKeys(payload)) {
693
+ try {
694
+ const state = JSON.parse(readFileSync(stateFile(key), "utf8"));
695
+ if (ownsState(state, payload)) return state;
696
+ ambiguous = true;
697
+ } catch (error) {
698
+ if (error instanceof SyntaxError) ambiguous = true;
699
+ // Not written under this key — try the next older one.
700
+ }
701
+ }
702
+ return ambiguous ? { unverifiedOwner: true } : null;
703
+ }
704
+
705
+ /**
706
+ * Write the session's binding state, MERGING over whatever is already there.
707
+ *
708
+ * Explicit `claimedRoot: null` still clears a claim. Merging means that a
709
+ * field one writer owns is not silently dropped by
710
+ * another writer that had no opinion about it: `runtimeNoticed` is written by
711
+ * {@link noticeRuntimeDrift} and read by nobody else, and a clobbering write
712
+ * would turn its "reported once" into "reported again after the next
713
+ * acknowledgement", which is the noise the notice is designed not to be.
714
+ * @param bindingKeyValue - The key whose state file this is
715
+ * @param state - Fields to write over the recorded state
716
+ * @param payload - The hook payload, for legacy-key reads while a binding is
717
+ * migrating to the current key format
718
+ * @param options - Validated acknowledgement or create-only baseline intent
719
+ * @returns True on success, false for unavailable I/O, or a conflict sentinel
720
+ */
721
+ function writeState(bindingKeyValue, state, payload, options = {}) {
722
+ const file = stateFile(bindingKeyValue);
723
+ const previous = readState(bindingKeyValue, payload);
724
+ if (previous?.[UNAVAILABLE_BINDING]) {
725
+ say(
726
+ `could not read the binding (${previous[UNAVAILABLE_BINDING]}); NOT enforcing`
727
+ );
728
+ return false;
729
+ }
730
+ if (
731
+ (options.createOnly && previous) ||
732
+ previous?.canonicalConflict ||
733
+ (previous?.unverifiedOwner && !options.acceptUnowned)
734
+ )
735
+ return BINDING_WRITE_CONFLICT;
736
+ const proven = previous?.unverifiedOwner ? null : previous;
737
+ // A runtime notice alone cannot establish a binding after a failed baseline.
738
+ // Every record we produce must satisfy the same authority check as a read.
739
+ const next = {
740
+ ...proven,
741
+ ...state,
742
+ bindingVersion: BINDING_VERSION,
743
+ bindingIdentity: bindingIdentity(payload),
744
+ };
745
+ if (!ownsState(next, payload)) return false;
746
+ const originalRoot =
747
+ proven?.originalRoot ?? proven?.boundRoot ?? state.boundRoot;
748
+ const originalCommonDir =
749
+ proven?.originalCommonDir ?? repositoryIdentity(originalRoot);
750
+ try {
751
+ mkdirSync(dirname(file), { recursive: true });
752
+ writeFileSync(
753
+ file,
754
+ `${JSON.stringify(
755
+ {
756
+ ...next,
757
+ // Preserve the initial root after intentional rebinds. A legacy
758
+ // state's established root is its only proven recovery anchor.
759
+ originalRoot,
760
+ originalCommonDir,
761
+ },
762
+ null,
763
+ 2
764
+ )}\n`,
765
+ { flag: previous?.[CURRENT_BINDING] ? "w" : "wx" }
766
+ );
767
+ return true;
768
+ } catch (error) {
769
+ if (error.code === "EEXIST") return BINDING_WRITE_CONFLICT;
770
+ say(`could not record the binding (${error.message}); NOT enforcing`);
771
+ return false;
772
+ }
773
+ }
774
+
775
+ /**
776
+ * The worktree `EnterWorktree` claims the session moved to.
777
+ *
778
+ * `path` is given verbatim; `name` resolves under the main checkout's
779
+ * `.claude/worktrees/`, which is where the tool creates one. A call carrying
780
+ * neither is a generated name this guard cannot predict, so it returns null and
781
+ * the claim is simply not recorded — an unrecorded claim degrades to the plain
782
+ * displacement check rather than to a false accusation.
783
+ */
784
+ function claimedRoot(toolInput, cwd) {
785
+ const path = toolInput?.path;
786
+ if (typeof path === "string" && path.trim()) {
787
+ return realpath(resolve(isAbsolute(path) ? path : join(cwd, path)));
788
+ }
789
+ const name = toolInput?.name;
790
+ if (typeof name === "string" && name.trim()) {
791
+ const main = mainCheckout(cwd);
792
+ return main
793
+ ? realpath(join(main, ".claude", "worktrees", name.trim()))
794
+ : null;
795
+ }
796
+ return null;
797
+ }
798
+
799
+ /**
800
+ * Record where the session STARTED, so its first guarded call is checked
801
+ * against a baseline rather than establishing one.
802
+ *
803
+ * ## Why the first call could not be checked before
804
+ *
805
+ * It established the baseline instead of testing one, which is
806
+ * trust-on-first-use — and TOFU is only as safe as its first use. Here the
807
+ * first use is assigned by the harness, unasked (CodySwannGT/lisa#3864), so the
808
+ * one moment the guard trusted was the moment the session had least control
809
+ * over. A session already sitting in a foreign worktree when it first acted
810
+ * bound to that tree and was never refused, and every later call was then
811
+ * checked faithfully against the wrong baseline — defending the session INTO
812
+ * the wrong worktree rather than out of it (CodySwannGT/lisa#3955).
813
+ *
814
+ * ## Why an earlier observation separates what a cleverer check cannot
815
+ *
816
+ * At the first guarded call, "I was displaced before I acted" and "I
817
+ * legitimately started here" present identically: one session id, one observed
818
+ * root, no prior state. No comparison can tell them apart, because the
819
+ * information is not in the hook's inputs at that instant.
820
+ *
821
+ * At session start it is. The displacement mechanism actually observed is a
822
+ * PEER's `EnterWorktree` moving this session (CodySwannGT/lisa#3712), which
823
+ * happens during the session — after it has started. A baseline taken at start
824
+ * is therefore recorded BEFORE the event that moves it, and the first guarded
825
+ * call becomes an ordinary comparison against a baseline that already exists.
826
+ * It reaches the same refusal a later displacement produces, because by then it
827
+ * is one.
828
+ *
829
+ * ## What this does NOT achieve
830
+ *
831
+ * #3955 asks for a baseline from the ASSIGNER rather than from observation.
832
+ * That is not what this is and Lisa cannot build it: Lisa does not own
833
+ * `EnterWorktree`, and nothing the harness writes names a session's assigned
834
+ * worktree in a form a hook can read. This is still an observation, just an
835
+ * earlier one. It closes the window between session start and the first guarded
836
+ * call; it leaves open a session LAUNCHED already displaced, where start
837
+ * observes the foreign tree and trust-on-first-use still applies.
838
+ *
839
+ * Returns nothing on purpose, like its sibling below: SessionStart has no call
840
+ * to refuse, so every path through it allows.
841
+ * @param bindingKeyValue - The key this baseline belongs to
842
+ * @param observed - The worktree root the session started in
843
+ * @param payload - The hook payload, for legacy-key reads
844
+ */
845
+ function recordBaseline(bindingKeyValue, observed, payload) {
846
+ // Never overwrite. A resumed session, a reconnect, or anything else that
847
+ // fires the event twice must not be able to launder a displaced binding into
848
+ // a fresh baseline — which is the whole failure this exists to close.
849
+ const previous = readState(bindingKeyValue, payload);
850
+ if (previous?.boundRoot || previous?.unverifiedOwner) return;
851
+ writeState(
852
+ bindingKeyValue,
853
+ {
854
+ boundRoot: observed,
855
+ claimedRoot: null,
856
+ updatedAt: new Date().toISOString(),
857
+ },
858
+ payload,
859
+ { createOnly: true }
860
+ );
861
+ }
862
+
863
+ /**
864
+ * Record what `EnterWorktree` just claimed, for the next tool call to check.
865
+ *
866
+ * Returns nothing on purpose. This runs on PostToolUse, where the tool has
867
+ * already happened and there is no call left to refuse, so every path through
868
+ * it is an allow — and a function whose only answer is "allow" should not be
869
+ * spelled as one that computes an exit code.
870
+ */
871
+ function recordClaim(payload, observed) {
872
+ const claimed = claimedRoot(payload.tool_input, payload.cwd);
873
+ if (!claimed || claimed === observed) return;
874
+ const previous = readState(bindingKey(payload), payload);
875
+ if (previous?.unverifiedOwner) return;
876
+ writeState(
877
+ bindingKey(payload),
878
+ {
879
+ boundRoot: previous?.boundRoot ?? observed,
880
+ claimedRoot: claimed,
881
+ updatedAt: new Date().toISOString(),
882
+ },
883
+ payload,
884
+ { createOnly: !previous }
885
+ );
886
+ }
887
+
888
+ function refuse(lines) {
889
+ process.stderr.write(`${lines.join("\n")}\n`);
890
+ return 2;
891
+ }
892
+
893
+ /** Refuse an action whose binding changed while its creation was in flight. */
894
+ function bindingWriteConflict() {
895
+ return refuse([
896
+ "worktree-binding-guard: another hook recorded this session's binding during this call.",
897
+ "The existing file has been preserved. Retry the call to check its established binding.",
898
+ ]);
899
+ }
900
+
901
+ function acceptanceLine(observed) {
902
+ const literal = `${ACCEPT_PREFIX} ${observed}`.replaceAll("'", "'\\''");
903
+ return ` echo '${literal}'`;
904
+ }
905
+
906
+ /**
907
+ * Explain a real ownership ambiguity without treating a stranger's root as fact.
908
+ * @param {object} state - Unverified-state sentinel, containing no imported fields
909
+ * @param {string|null} observed - Measured checkout
910
+ * @returns {number} Blocking native-hook status
911
+ */
912
+ function unownedBinding(state, observed) {
913
+ return refuse([
914
+ "worktree-binding-guard: cannot prove this binding belongs to this session.",
915
+ "The existing file has no matching owner record. It has not been imported or changed.",
916
+ ...(state.canonicalConflict
917
+ ? [
918
+ "This session's reserved state file identifies another owner or an unknown format.",
919
+ "Start a new session with its own identity; acknowledging cannot overwrite that file.",
920
+ ]
921
+ : [
922
+ "Choose the worktree you intend to use by acknowledging its measured path:",
923
+ acceptanceLine(observed),
924
+ ]),
925
+ ]);
926
+ }
927
+
928
+ /** Read literal shell words and AND separators without evaluating expansions. */
929
+ function acknowledgementWords(text) {
930
+ const words = [];
931
+ let word = "",
932
+ active = false,
933
+ quote = null;
934
+ const emit = () => {
935
+ if (active) words.push(word);
936
+ word = "";
937
+ active = false;
938
+ };
939
+ for (let index = 0; index < text.length; index += 1) {
940
+ const char = text[index];
941
+ if (quote === "'") {
942
+ if (char === "'") quote = null;
943
+ else word += char;
944
+ continue;
945
+ }
946
+ if (char === "\\") {
947
+ const next = text[index + 1];
948
+ if (next === undefined) return null;
949
+ if (quote === '"' && !['"', "\\", "$", "`", "\n"].includes(next)) {
950
+ word += char;
951
+ continue;
952
+ }
953
+ index += 1;
954
+ if (next !== "\n") {
955
+ word += next;
956
+ active = true;
957
+ }
958
+ continue;
959
+ }
960
+ if (quote === '"') {
961
+ if (char === '"') quote = null;
962
+ else if (char === "$" || char === "`") return null;
963
+ else word += char;
964
+ continue;
965
+ }
966
+ if (char === "'" || char === '"') {
967
+ quote = char;
968
+ active = true;
969
+ } else if (char === " " || char === "\t") emit();
970
+ else if (char === "\n") return null;
971
+ else if (char === "&" && text[index + 1] === "&") {
972
+ emit();
973
+ words.push(ACK_AND_SEPARATOR);
974
+ index += 1;
975
+ } else if (/[&;|<>$`*?[\]{}()]/u.test(char)) return null;
976
+ else {
977
+ word += char;
978
+ active = true;
979
+ }
980
+ }
981
+ if (quote !== null) return null;
982
+ emit();
983
+ return words;
984
+ }
985
+
986
+ /** Strip only actual shell edge whitespace in linear time. */
987
+ function acknowledgementText(command) {
988
+ let start = 0,
989
+ end = command.length;
990
+ while (start < end && " \t\n".includes(command[start])) start += 1;
991
+ while (end > start && " \t\n".includes(command[end - 1])) end -= 1;
992
+ return command.slice(start, end);
993
+ }
994
+
995
+ /** A literal echo, optionally preceded by a literal cd to the stated root. */
996
+ function acceptanceRequest(command) {
997
+ const text = acknowledgementText(command);
998
+ const words = acknowledgementWords(text.replace(/;$/u, ""));
999
+ if (!words) return null;
1000
+ const direct = words.length === 2 && words[0] === "echo";
1001
+ const moving =
1002
+ words.length === 5 &&
1003
+ words[0] === "cd" &&
1004
+ typeof words[1] === "string" &&
1005
+ words[2] === ACK_AND_SEPARATOR &&
1006
+ words[3] === "echo";
1007
+ if (!direct && !moving) return null;
1008
+ const message = words.at(-1);
1009
+ if (typeof message !== "string" || !message.startsWith(`${ACCEPT_PREFIX} `))
1010
+ return null;
1011
+ const stated = message.slice(ACCEPT_PREFIX.length + 1);
1012
+ // The printed protocol names absolute roots. Empty/relative operands and
1013
+ // cd's special ~, -, or -- forms cannot authorize a move.
1014
+ if (!isAbsolute(stated)) return null;
1015
+ const path = realpath(stated);
1016
+ const destination =
1017
+ moving && isAbsolute(words[1]) ? realpath(words[1]) : null;
1018
+ return {
1019
+ path,
1020
+ validMove: !moving || (destination !== null && destination === path),
1021
+ };
1022
+ }
1023
+
1024
+ /**
1025
+ * Validate the initial recovery root against the identity captured at baseline.
1026
+ * The current checkout may have been removed or deliberately rebound elsewhere.
1027
+ */
1028
+ function isOriginalRoot(state, path) {
1029
+ const original = state?.originalRoot ?? state?.boundRoot;
1030
+ if (path !== original || worktreeRoot(path) !== path) return false;
1031
+ // Older state can only prove the repository of its established binding.
1032
+ const common = state.originalCommonDir ?? repositoryIdentity(state.boundRoot);
1033
+ return common !== null && common === repositoryIdentity(path);
1034
+ }
1035
+
1036
+ /** Acknowledgements select intent; the next call must prove actual movement. */
1037
+ function handleAcceptance(payload, observed) {
1038
+ const command = payload.tool_input?.command;
1039
+ if (typeof command !== "string" || !command.includes(ACCEPT_PREFIX))
1040
+ return null;
1041
+ const request = acceptanceRequest(command);
1042
+ if (!request) return null;
1043
+ const { path, validMove } = request;
1044
+ const previous = readState(bindingKey(payload), payload);
1045
+ if (previous?.canonicalConflict) return unownedBinding(previous, observed);
1046
+ const original = previous?.originalRoot ?? previous?.boundRoot;
1047
+ const recovering = isOriginalRoot(previous, path);
1048
+ const offered =
1049
+ path === previous?.offeredRoot &&
1050
+ isLinkedWorktree(previous.boundRoot, path);
1051
+ if (!validMove || (path !== observed && !recovering && !offered)) {
1052
+ const suggestion = observed ?? original;
1053
+ return refuse([
1054
+ `worktree-binding-guard: this acknowledgement names ${path ?? "no path"},`,
1055
+ "but it is not a valid move to the observed, original, or offered worktree. Not rebinding.",
1056
+ ...(suggestion
1057
+ ? [
1058
+ "Acknowledge the measured or original worktree:",
1059
+ acceptanceLine(suggestion),
1060
+ ]
1061
+ : []),
1062
+ ]);
1063
+ }
1064
+ const written = writeState(
1065
+ bindingKey(payload),
1066
+ {
1067
+ boundRoot: path,
1068
+ claimedRoot: null,
1069
+ offeredRoot: null,
1070
+ updatedAt: new Date().toISOString(),
1071
+ },
1072
+ payload,
1073
+ { acceptUnowned: true }
1074
+ );
1075
+ if (written === BINDING_WRITE_CONFLICT) return bindingWriteConflict();
1076
+ if (written)
1077
+ say(
1078
+ path === observed
1079
+ ? `bound to ${path}`
1080
+ : `binding set to ${path}; move there before further guarded calls`
1081
+ );
1082
+ return 0;
1083
+ }
1084
+
1085
+ /** Refusal text for a switch that reported success without taking effect. */
1086
+ function unconfirmedSwitch(claimed, observed) {
1087
+ return refuse([
1088
+ `worktree-binding-guard: EnterWorktree reported success for`,
1089
+ ` ${claimed}`,
1090
+ `but this session is still operating in`,
1091
+ ` ${observed}`,
1092
+ "The switch did not take effect. Anything run now lands in the second",
1093
+ "path, not the first — including installs, commits, and file writes, and",
1094
+ "that tree may hold another agent's uncommitted work.",
1095
+ "",
1096
+ "Use absolute paths under the tree you actually want, or acknowledge that",
1097
+ "you intend to keep working where you are:",
1098
+ acceptanceLine(observed),
1099
+ ]);
1100
+ }
1101
+
1102
+ /** Refusal text for a binding that moved without this session asking. */
1103
+ function displaced(bound, observed) {
1104
+ return refuse([
1105
+ "worktree-binding-guard: this session's worktree changed underneath it.",
1106
+ ` bound to: ${bound}`,
1107
+ ` now in: ${observed}`,
1108
+ "Concurrent sessions share one worktree binding, so another agent's",
1109
+ "EnterWorktree can move this one. Commands run now land in the second",
1110
+ "path. If you moved deliberately, say so:",
1111
+ acceptanceLine(observed),
1112
+ ]);
1113
+ }
1114
+
1115
+ /**
1116
+ * The runtime version the inline-redirect gap was last verified at.
1117
+ *
1118
+ * ## What this constant is a pin ON
1119
+ *
1120
+ * This guard deliberately does NOT refuse an INLINE redirect into another
1121
+ * worktree — `cd <other> && git …`, `git -C <other> …`. The reason is good and
1122
+ * is written out at {@link scriptReachingForeignTree}: the runtime's own
1123
+ * worktree isolation already refuses them, and duplicating a control is how two
1124
+ * controls drift into disagreeing about the same command.
1125
+ *
1126
+ * That reason is a DEPENDENCY on behaviour Lisa does not ship. It was verified
1127
+ * refusing at this version, with the refusal wording "this command names git in
1128
+ * a form too complex to verify that it stays inside the worktree" and
1129
+ * "redirects git to the shared checkout via -C".
1130
+ *
1131
+ * ## Why a pin rather than a test
1132
+ *
1133
+ * The runtime's isolation is not reachable from a unit test: it adjudicates
1134
+ * inside a live worktree-isolated session, and there is no entry point that
1135
+ * takes one hook envelope and returns a verdict the way this guard does. So the
1136
+ * dependency cannot be turned red by writing a test, and a claim that cannot go
1137
+ * red goes stale silently instead (CodySwannGT/lisa#3944).
1138
+ *
1139
+ * What CAN be measured is whether the runtime is still the one the claim was
1140
+ * checked against. That does not prove the behaviour changed — only that nobody
1141
+ * has checked since, which is the honest thing to report and the whole of what
1142
+ * this pin does.
1143
+ *
1144
+ * ## Moving it
1145
+ *
1146
+ * Re-run both inline forms in a worktree-isolated session. Still refused: move
1147
+ * this pin, and say where. Permitted: those two cells are UNCOVERED, and #3944
1148
+ * owns the decision about which of them Lisa takes on.
1149
+ */
1150
+ const VERIFIED_RUNTIME_VERSION = "2.1.261";
1151
+
1152
+ /** The ticket that owns the inline-redirect dependency and its expiry. */
1153
+ const RUNTIME_ASSUMPTION_TICKET = "CodySwannGT/lisa#3944";
1154
+
1155
+ /**
1156
+ * This session's runtime version, or null when this is not that runtime.
1157
+ *
1158
+ * Three answers, and the difference between the last two is the point:
1159
+ * - `null` — not Claude Code. Codex, Cursor and Copilot ship this guard too,
1160
+ * and the assumption above is about a control none of them has. A notice that
1161
+ * fires on every session of every harness is noise, and noise gets switched
1162
+ * off.
1163
+ * - `""` — Claude Code, which will not say which version. Read as a DIVERGENCE
1164
+ * rather than as a match: the assumption is unconfirmed either way, and
1165
+ * treating an unreadable version as "still 2.1.261" is exactly the silent
1166
+ * expiry #3944 was filed about.
1167
+ * - a version — take it at its word.
1168
+ *
1169
+ * Both env vars are read because either can be the one a future release keeps.
1170
+ * `AI_AGENT` carries `claude-code_2-1-261_agent`; `CLAUDE_CODE_EXECPATH` ends
1171
+ * in the version directory the running build was launched from.
1172
+ * @param env - Environment to read
1173
+ * @returns The version, `""` when unreadable, or null for another runtime
1174
+ */
1175
+ const isDigit = ch => ch >= "0" && ch <= "9";
1176
+
1177
+ /** @param value - Segment to test @returns Whether it is all digits */
1178
+ const allDigits = value => value.length > 0 && [...value].every(isDigit);
1179
+
1180
+ /** @param value - Segment to read @returns Its trailing digit run, else `""` */
1181
+ const trailingDigits = value => {
1182
+ let start = value.length;
1183
+ while (start > 0 && isDigit(value[start - 1])) start -= 1;
1184
+ return value.slice(start);
1185
+ };
1186
+
1187
+ function runtimeVersion(env) {
1188
+ if (!env.CLAUDECODE) return null;
1189
+ const tagged = /claude-code[_-](\d+)[-.](\d+)[-.](\d+)/u.exec(
1190
+ env.AI_AGENT ?? ""
1191
+ );
1192
+ if (tagged) return `${tagged[1]}.${tagged[2]}.${tagged[3]}`;
1193
+ // Read the version off the final path segment by scanning characters rather
1194
+ // than by regex: an unanchored `\d+\.\d+\.\d+` before `$` backtracks
1195
+ // super-linearly on a long execpath, and the version Claude Code launches
1196
+ // from is always the trailing directory name.
1197
+ const segments = (env.CLAUDE_CODE_EXECPATH ?? "").split("/").filter(Boolean);
1198
+ const trailing = segments.length ? segments[segments.length - 1] : "";
1199
+ const parts = trailing.split(".");
1200
+ if (parts.length < 3) return "";
1201
+ const [major, minor, patch] = parts.slice(-3);
1202
+ const majorDigits = trailingDigits(major);
1203
+ if (!majorDigits || !allDigits(minor) || !allDigits(patch)) return "";
1204
+ return `${majorDigits}.${minor}.${patch}`;
1205
+ }
1206
+
1207
+ /**
1208
+ * The notice text for a runtime the inline-redirect claim was not checked at.
1209
+ * @param version - The observed runtime version, `""` when unreadable
1210
+ * @returns The lines to report
1211
+ */
1212
+ function driftNotice(version) {
1213
+ return [
1214
+ "worktree-binding-guard: the inline-redirect gap is UNVERIFIED here.",
1215
+ "",
1216
+ "This guard does not refuse `cd <other-worktree> && git …` or",
1217
+ "`git -C <other-worktree> …`. Those two cells are covered by the runtime's",
1218
+ `own worktree isolation, verified refusing at Claude Code ${VERIFIED_RUNTIME_VERSION}.`,
1219
+ version
1220
+ ? `This session is running ${version}, so that check does not cover it.`
1221
+ : "This session's runtime version could not be read, so nothing covers it.",
1222
+ "",
1223
+ "This does NOT say the runtime stopped refusing them — only that nobody has",
1224
+ "checked since. Run both forms in a worktree-isolated session: still",
1225
+ "refused, move VERIFIED_RUNTIME_VERSION in worktree-binding-guard.mjs;",
1226
+ `permitted, the two cells are uncovered and ${RUNTIME_ASSUMPTION_TICKET}`,
1227
+ "owns what Lisa does about it.",
1228
+ ];
1229
+ }
1230
+
1231
+ /**
1232
+ * Report, once per session, that the runtime moved off the verified version.
1233
+ *
1234
+ * ## Why SessionStart and not the guarded call
1235
+ *
1236
+ * SessionStart is the guard's first evaluation of a session, it fires exactly
1237
+ * once per start, and its `additionalContext` is the one channel here that the
1238
+ * agent actually reads — the `say()` lines elsewhere in this file are for a
1239
+ * human reading a transcript. A notice attached to a guarded tool call would
1240
+ * either repeat on every Bash command or need a suppression rule of its own.
1241
+ *
1242
+ * ## Why it is recorded rather than counted
1243
+ *
1244
+ * A resume fires SessionStart again for the same session id. The recorded
1245
+ * version is what makes the second one silent, and recording the VERSION rather
1246
+ * than a flag means a runtime that moves twice inside one resumed session is
1247
+ * still reported the second time.
1248
+ *
1249
+ * Never refuses. The dependency is a reporting gap, not a live displacement,
1250
+ * and blocking a session over a version number would be a wall where an
1251
+ * observation belongs.
1252
+ * @param bindingKeyValue - The key being started
1253
+ * @param env - Environment to read the runtime version from
1254
+ * @param payload - The hook payload, for legacy-key reads
1255
+ * @returns Nothing; every path through this allows
1256
+ */
1257
+ function noticeRuntimeDrift(bindingKeyValue, env, payload) {
1258
+ const version = runtimeVersion(env);
1259
+ if (version === null || version === VERIFIED_RUNTIME_VERSION) return;
1260
+ if (readState(bindingKeyValue, payload)?.runtimeNoticed === version) return;
1261
+ writeState(bindingKeyValue, { runtimeNoticed: version }, payload);
1262
+ process.stdout.write(
1263
+ `${JSON.stringify({
1264
+ hookSpecificOutput: {
1265
+ hookEventName: "SessionStart",
1266
+ additionalContext: driftNotice(version).join("\n"),
1267
+ },
1268
+ })}\n`
1269
+ );
1270
+ }
1271
+
1272
+ function evaluate(payload) {
1273
+ const sessionId = payload.session_id;
1274
+ const cwd = payload.cwd;
1275
+ if (typeof sessionId !== "string" || !sessionId || typeof cwd !== "string") {
1276
+ say("payload carries no session id or cwd; binding is NOT enforced");
1277
+ return 0;
1278
+ }
1279
+ const binding = bindingKey(payload);
1280
+ const observed = worktreeRoot(cwd);
1281
+ // The initial-root recovery acknowledgement also works outside any repo.
1282
+ if (GUARDED_TOOLS.has(payload.tool_name)) {
1283
+ const accepted = handleAcceptance(payload, observed);
1284
+ if (accepted !== null) return accepted;
1285
+ }
1286
+ if (!observed) return 0;
1287
+
1288
+ if (payload.hook_event_name === "SessionStart") {
1289
+ recordBaseline(binding, observed, payload);
1290
+ // After the baseline, never before: `recordBaseline` writes a fresh state
1291
+ // for a session it has not seen, and the notice's record has to go on top
1292
+ // of that write rather than under it.
1293
+ noticeRuntimeDrift(binding, process.env, payload);
1294
+ return 0;
1295
+ }
1296
+ if (payload.tool_name === "EnterWorktree") {
1297
+ const previous = readState(binding, payload);
1298
+ if (
1299
+ previous?.boundRoot &&
1300
+ previous.boundRoot !== observed &&
1301
+ !isLinkedWorktree(previous.boundRoot, observed)
1302
+ )
1303
+ return 0;
1304
+ recordClaim(payload, observed);
1305
+ return 0;
1306
+ }
1307
+ if (!GUARDED_TOOLS.has(payload.tool_name)) return 0;
1308
+
1309
+ const state = readState(binding, payload);
1310
+ if (state?.unverifiedOwner) return unownedBinding(state, observed);
1311
+ if (!state?.boundRoot) {
1312
+ // No baseline means SessionStart did not run for this session — an older
1313
+ // install, a runtime that fires no such event, a harness that skips it.
1314
+ // Absence of a baseline is absence of evidence, not evidence of
1315
+ // displacement, so this records one exactly as it always did rather than
1316
+ // refusing. Treating it as a refusal would turn a floor into a wall on
1317
+ // every surface where the event does not fire, and a wall gets switched
1318
+ // off, which costs the whole guard.
1319
+ const written = writeState(
1320
+ binding,
1321
+ {
1322
+ boundRoot: observed,
1323
+ claimedRoot: null,
1324
+ updatedAt: new Date().toISOString(),
1325
+ },
1326
+ payload,
1327
+ { createOnly: true }
1328
+ );
1329
+ if (written === BINDING_WRITE_CONFLICT) return bindingWriteConflict();
1330
+ return 0;
1331
+ }
1332
+ if (
1333
+ state.boundRoot !== observed &&
1334
+ !isLinkedWorktree(state.boundRoot, observed)
1335
+ )
1336
+ return 0;
1337
+ if (state.claimedRoot && state.claimedRoot !== observed) {
1338
+ return unconfirmedSwitch(state.claimedRoot, observed);
1339
+ }
1340
+ if (state.claimedRoot === observed) {
1341
+ const written = writeState(
1342
+ binding,
1343
+ {
1344
+ boundRoot: observed,
1345
+ claimedRoot: null,
1346
+ offeredRoot: null,
1347
+ updatedAt: new Date().toISOString(),
1348
+ },
1349
+ payload
1350
+ );
1351
+ if (written === BINDING_WRITE_CONFLICT) return bindingWriteConflict();
1352
+ return 0;
1353
+ }
1354
+ if (state.boundRoot !== observed) return displaced(state.boundRoot, observed);
1355
+ // Last, and only once the session is provably where it should be: a script
1356
+ // that moves somewhere else after this check has already run.
1357
+ const reach = scriptReachingForeignTree(payload, observed);
1358
+ if (reach) {
1359
+ writeState(binding, { offeredRoot: reach.foreign }, payload);
1360
+ return reachesOut(reach.script, reach.foreign, observed);
1361
+ }
1362
+ return 0;
1363
+ }
1364
+
1365
+ function main() {
1366
+ let raw = "";
1367
+ try {
1368
+ raw = readFileSync(0, "utf8");
1369
+ } catch {
1370
+ say("could not read the hook payload; binding is NOT enforced");
1371
+ return 0;
1372
+ }
1373
+ let payload;
1374
+ try {
1375
+ payload = JSON.parse(raw);
1376
+ } catch {
1377
+ say("hook payload was not JSON; binding is NOT enforced");
1378
+ return 0;
1379
+ }
1380
+ return evaluate(payload);
1381
+ }
1382
+
1383
+ process.exit(main());