@codyswann/lisa 4.54.13 → 4.55.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 (578) hide show
  1. package/all/copy-contents/.gitattributes +8 -0
  2. package/all/copy-overwrite/scripts/check-orphaned-branches.mjs +78 -9
  3. package/all/copy-overwrite/scripts/check-third-party-action-pins.mjs +17 -1
  4. package/all/copy-overwrite/scripts/lib/process-tree-runner.mjs +194 -7
  5. package/all/copy-overwrite/scripts/lisa-enforcement-fallback.sh +31 -0
  6. package/all/copy-overwrite/scripts/lisa-hooks/block-blind-automerge.sh +81 -2
  7. package/all/copy-overwrite/scripts/lisa-hooks/block-direct-issue-create.sh +293 -8
  8. package/all/copy-overwrite/scripts/lisa-hooks/block-instruction-file-edits.sh +21 -0
  9. package/all/copy-overwrite/scripts/lisa-hooks/block-managed-file-edits.sh +78 -15
  10. package/all/copy-overwrite/scripts/lisa-hooks/block-no-verify.sh +21 -0
  11. package/all/copy-overwrite/scripts/lisa-hooks/block-shell-json-parsing.sh +21 -0
  12. package/all/copy-overwrite/scripts/lisa-hooks/guard-dedupe.bash +340 -0
  13. package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +29 -1
  14. package/all/copy-overwrite/scripts/lisa-hooks/worktree-binding-guard.mjs +183 -1
  15. package/all/copy-overwrite/scripts/lisa-hooks/worktree-binding-guard.sh +21 -0
  16. package/all/copy-overwrite/scripts/lisa-postinstall.mjs +21 -1
  17. package/all/copy-overwrite/scripts/lisa-run-gates.mjs +100 -5
  18. package/all/copy-overwrite/scripts/lisa-work-item.mjs +1085 -80
  19. package/all/create-only/.github/workflows/continuous-gates.yml +7 -0
  20. package/cdk/create-only/.github/workflows/ci.yml +7 -0
  21. package/cdk/create-only/.github/workflows/deploy.yml +21 -0
  22. package/dist/cli/apply.d.ts.map +1 -1
  23. package/dist/cli/apply.js +9 -1
  24. package/dist/cli/apply.js.map +1 -1
  25. package/dist/cli/doctor-apply-deletions.d.ts +61 -0
  26. package/dist/cli/doctor-apply-deletions.d.ts.map +1 -0
  27. package/dist/cli/doctor-apply-deletions.js +137 -0
  28. package/dist/cli/doctor-apply-deletions.js.map +1 -0
  29. package/dist/cli/doctor-cdk-preset-adoption.d.ts +15 -0
  30. package/dist/cli/doctor-cdk-preset-adoption.d.ts.map +1 -0
  31. package/dist/cli/doctor-cdk-preset-adoption.js +316 -0
  32. package/dist/cli/doctor-cdk-preset-adoption.js.map +1 -0
  33. package/dist/cli/doctor-config-shadowing.d.ts +22 -0
  34. package/dist/cli/doctor-config-shadowing.d.ts.map +1 -0
  35. package/dist/cli/doctor-config-shadowing.js +123 -0
  36. package/dist/cli/doctor-config-shadowing.js.map +1 -0
  37. package/dist/cli/doctor-rails-deploy-intent.d.ts +15 -0
  38. package/dist/cli/doctor-rails-deploy-intent.d.ts.map +1 -0
  39. package/dist/cli/doctor-rails-deploy-intent.js +197 -0
  40. package/dist/cli/doctor-rails-deploy-intent.js.map +1 -0
  41. package/dist/cli/doctor-seeded-artifacts.d.ts +12 -0
  42. package/dist/cli/doctor-seeded-artifacts.d.ts.map +1 -0
  43. package/dist/cli/doctor-seeded-artifacts.js +54 -0
  44. package/dist/cli/doctor-seeded-artifacts.js.map +1 -0
  45. package/dist/cli/doctor-stale-banner-scan.d.ts +44 -0
  46. package/dist/cli/doctor-stale-banner-scan.d.ts.map +1 -0
  47. package/dist/cli/doctor-stale-banner-scan.js +152 -0
  48. package/dist/cli/doctor-stale-banner-scan.js.map +1 -0
  49. package/dist/cli/doctor-stale-managed-banner.d.ts +19 -0
  50. package/dist/cli/doctor-stale-managed-banner.d.ts.map +1 -0
  51. package/dist/cli/doctor-stale-managed-banner.js +198 -0
  52. package/dist/cli/doctor-stale-managed-banner.js.map +1 -0
  53. package/dist/cli/doctor.d.ts.map +1 -1
  54. package/dist/cli/doctor.js +16 -0
  55. package/dist/cli/doctor.js.map +1 -1
  56. package/dist/cli/ui-detected-stacks.d.ts.map +1 -1
  57. package/dist/cli/ui-detected-stacks.js +3 -2
  58. package/dist/cli/ui-detected-stacks.js.map +1 -1
  59. package/dist/cli/update-check.d.ts.map +1 -1
  60. package/dist/cli/update-check.js +14 -0
  61. package/dist/cli/update-check.js.map +1 -1
  62. package/dist/cli/worktree-liveness.d.ts +5 -0
  63. package/dist/cli/worktree-liveness.d.ts.map +1 -1
  64. package/dist/cli/worktree-liveness.js +7 -6
  65. package/dist/cli/worktree-liveness.js.map +1 -1
  66. package/dist/cli/worktree-ownership.d.ts +13 -0
  67. package/dist/cli/worktree-ownership.d.ts.map +1 -1
  68. package/dist/cli/worktree-ownership.js +14 -0
  69. package/dist/cli/worktree-ownership.js.map +1 -1
  70. package/dist/configs/eslint/base.d.ts +34 -0
  71. package/dist/configs/eslint/base.d.ts.map +1 -1
  72. package/dist/configs/eslint/base.js +34 -0
  73. package/dist/configs/eslint/base.js.map +1 -1
  74. package/dist/core/apply-receipt.d.ts +65 -0
  75. package/dist/core/apply-receipt.d.ts.map +1 -1
  76. package/dist/core/apply-receipt.js +62 -2
  77. package/dist/core/apply-receipt.js.map +1 -1
  78. package/dist/core/cdk-preset-adoption.d.ts +136 -0
  79. package/dist/core/cdk-preset-adoption.d.ts.map +1 -0
  80. package/dist/core/cdk-preset-adoption.js +135 -0
  81. package/dist/core/cdk-preset-adoption.js.map +1 -0
  82. package/dist/core/config-shadowing.d.ts +36 -0
  83. package/dist/core/config-shadowing.d.ts.map +1 -1
  84. package/dist/core/config-shadowing.js +38 -0
  85. package/dist/core/config-shadowing.js.map +1 -1
  86. package/dist/core/config.d.ts +11 -0
  87. package/dist/core/config.d.ts.map +1 -1
  88. package/dist/core/config.js.map +1 -1
  89. package/dist/core/learnings-merge-driver.d.ts +10 -0
  90. package/dist/core/learnings-merge-driver.d.ts.map +1 -1
  91. package/dist/core/learnings-merge-driver.js +18 -0
  92. package/dist/core/learnings-merge-driver.js.map +1 -1
  93. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  94. package/dist/core/lisa-owned-hash-ledger.js +136 -0
  95. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  96. package/dist/core/lisa.d.ts.map +1 -1
  97. package/dist/core/lisa.js +9 -1
  98. package/dist/core/lisa.js.map +1 -1
  99. package/dist/core/nightly-e2e-guard-behavior-certificate.d.ts +5 -5
  100. package/dist/core/nightly-e2e-guard-behavior-certificate.js +7 -7
  101. package/dist/core/nightly-e2e-guard-behavior-certificate.js.map +1 -1
  102. package/dist/core/ownership-header.d.ts +74 -0
  103. package/dist/core/ownership-header.d.ts.map +1 -0
  104. package/dist/core/ownership-header.js +76 -0
  105. package/dist/core/ownership-header.js.map +1 -0
  106. package/dist/core/rails-deploy-production-intent.d.ts +36 -0
  107. package/dist/core/rails-deploy-production-intent.d.ts.map +1 -0
  108. package/dist/core/rails-deploy-production-intent.js +79 -0
  109. package/dist/core/rails-deploy-production-intent.js.map +1 -0
  110. package/dist/core/stale-managed-banner.d.ts +78 -0
  111. package/dist/core/stale-managed-banner.d.ts.map +1 -0
  112. package/dist/core/stale-managed-banner.js +118 -0
  113. package/dist/core/stale-managed-banner.js.map +1 -0
  114. package/dist/core/two-channel-delivery-scan.d.ts +33 -1
  115. package/dist/core/two-channel-delivery-scan.d.ts.map +1 -1
  116. package/dist/core/two-channel-delivery-scan.js +133 -6
  117. package/dist/core/two-channel-delivery-scan.js.map +1 -1
  118. package/dist/core/two-channel-delivery.d.ts +101 -4
  119. package/dist/core/two-channel-delivery.d.ts.map +1 -1
  120. package/dist/core/two-channel-delivery.js +121 -13
  121. package/dist/core/two-channel-delivery.js.map +1 -1
  122. package/dist/core/two-channel-staleness.d.ts +137 -0
  123. package/dist/core/two-channel-staleness.d.ts.map +1 -0
  124. package/dist/core/two-channel-staleness.js +268 -0
  125. package/dist/core/two-channel-staleness.js.map +1 -0
  126. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  127. package/dist/core/upstream-evidence-manifest.js +265 -121
  128. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  129. package/dist/core/workflow-deletion-ownership.d.ts +6 -24
  130. package/dist/core/workflow-deletion-ownership.d.ts.map +1 -1
  131. package/dist/core/workflow-deletion-ownership.js +22 -42
  132. package/dist/core/workflow-deletion-ownership.js.map +1 -1
  133. package/dist/detection/detectors/cdk.d.ts.map +1 -1
  134. package/dist/detection/detectors/cdk.js +2 -1
  135. package/dist/detection/detectors/cdk.js.map +1 -1
  136. package/dist/health/template-inspection.d.ts.map +1 -1
  137. package/dist/health/template-inspection.js +2 -1
  138. package/dist/health/template-inspection.js.map +1 -1
  139. package/dist/migrations/ensure-lisa-postinstall.d.ts +16 -0
  140. package/dist/migrations/ensure-lisa-postinstall.d.ts.map +1 -1
  141. package/dist/migrations/ensure-lisa-postinstall.js +16 -0
  142. package/dist/migrations/ensure-lisa-postinstall.js.map +1 -1
  143. package/dist/migrations/ensure-pinned-reusable-workflow-refs.d.ts +2 -2
  144. package/dist/migrations/ensure-pinned-reusable-workflow-refs.d.ts.map +1 -1
  145. package/dist/migrations/ensure-pinned-reusable-workflow-refs.js +98 -34
  146. package/dist/migrations/ensure-pinned-reusable-workflow-refs.js.map +1 -1
  147. package/dist/migrations/ensure-playwright-dedicated-caller.d.ts.map +1 -1
  148. package/dist/migrations/ensure-playwright-dedicated-caller.js +1 -0
  149. package/dist/migrations/ensure-playwright-dedicated-caller.js.map +1 -1
  150. package/dist/opencode/hooks-installer.d.ts.map +1 -1
  151. package/dist/opencode/hooks-installer.js +1 -0
  152. package/dist/opencode/hooks-installer.js.map +1 -1
  153. package/dist/opencode/plugin-templates/block-managed-file-edits.sh +78 -15
  154. package/dist/opencode/plugin-templates/block-no-verify.sh +21 -0
  155. package/dist/opencode/plugin-templates/guard-dedupe.bash +337 -0
  156. package/dist/opencode/plugin-templates/lisa-block-direct-issue-create.ts +242 -55
  157. package/dist/opencode/plugin-templates/parity-safety-net.sh +29 -1
  158. package/dist/strategies/package-lisa.d.ts.map +1 -1
  159. package/dist/strategies/package-lisa.js +2 -1
  160. package/dist/strategies/package-lisa.js.map +1 -1
  161. package/dist/utils/path-utils.d.ts +29 -0
  162. package/dist/utils/path-utils.d.ts.map +1 -1
  163. package/dist/utils/path-utils.js +41 -0
  164. package/dist/utils/path-utils.js.map +1 -1
  165. package/expo/create-only/.github/workflows/ci.yml +21 -0
  166. package/expo/create-only/.github/workflows/deploy.yml +28 -0
  167. package/expo/create-only/.github/workflows/maestro-e2e.yml +7 -0
  168. package/expo/create-only/.github/workflows/nightly-e2e-bypass-reaper.yml +124 -13
  169. package/expo/create-only/.github/workflows/nightly-e2e-health.yml +7 -0
  170. package/expo/create-only/.github/workflows/nightly-e2e-report.yml +7 -0
  171. package/expo/create-only/.github/workflows/nightly-e2e-tracking.yml +8 -0
  172. package/expo/create-only/.github/workflows/playwright-e2e.yml +7 -0
  173. package/harper-fabric/copy-overwrite/.github/workflows/ci.yml +7 -0
  174. package/harper-fabric/create-only/.github/workflows/deploy.yml +14 -0
  175. package/nestjs/create-only/.github/workflows/ci.yml +14 -0
  176. package/nestjs/create-only/.github/workflows/deploy.yml +28 -0
  177. package/package.json +10 -5
  178. package/phaser/copy-overwrite/.github/workflows/ci.yml +7 -0
  179. package/plugins/lisa/.claude-plugin/plugin.json +28 -1
  180. package/plugins/lisa/.codex-plugin/hooks.json +27 -0
  181. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  182. package/plugins/lisa/.codex-plugin/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
  183. package/plugins/lisa/.codex-plugin/skills/lisa-github-build-intake/SKILL.md +27 -4
  184. package/plugins/lisa/.codex-plugin/skills/lisa-github-prd-intake/SKILL.md +1 -1
  185. package/plugins/lisa/.codex-plugin/skills/lisa-github-validate-issue/SKILL.md +22 -0
  186. package/plugins/lisa/.codex-plugin/skills/lisa-github-verify/SKILL.md +14 -0
  187. package/plugins/lisa/.codex-plugin/skills/lisa-github-write-issue/SKILL.md +51 -0
  188. package/plugins/lisa/.codex-plugin/skills/lisa-intake/SKILL.md +1 -1
  189. package/plugins/lisa/.codex-plugin/skills/lisa-jira-build-intake/SKILL.md +27 -4
  190. package/plugins/lisa/.codex-plugin/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
  191. package/plugins/lisa/.codex-plugin/skills/lisa-jira-verify/SKILL.md +14 -0
  192. package/plugins/lisa/.codex-plugin/skills/lisa-jira-write-ticket/SKILL.md +51 -0
  193. package/plugins/lisa/.codex-plugin/skills/lisa-linear-access/SKILL.md +55 -2
  194. package/plugins/lisa/.codex-plugin/skills/lisa-linear-build-intake/SKILL.md +27 -4
  195. package/plugins/lisa/.codex-plugin/skills/lisa-linear-prd-intake/SKILL.md +47 -30
  196. package/plugins/lisa/.codex-plugin/skills/lisa-linear-to-tracker/SKILL.md +3 -3
  197. package/plugins/lisa/.codex-plugin/skills/lisa-linear-validate-issue/SKILL.md +22 -0
  198. package/plugins/lisa/.codex-plugin/skills/lisa-linear-verify/SKILL.md +14 -0
  199. package/plugins/lisa/.codex-plugin/skills/lisa-linear-write-issue/SKILL.md +54 -3
  200. package/plugins/lisa/.codex-plugin/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  201. package/plugins/lisa/.codex-plugin/skills/lisa-qa-clear/SKILL.md +19 -7
  202. package/plugins/lisa/.codex-plugin/skills/lisa-qa-fail/SKILL.md +28 -3
  203. package/plugins/lisa/.codex-plugin/skills/lisa-qa-queue/SKILL.md +28 -3
  204. package/plugins/lisa/.codex-plugin/skills/lisa-repair-intake/SKILL.md +94 -24
  205. package/plugins/lisa/.codex-plugin/skills/lisa-rework-triage/SKILL.md +32 -2
  206. package/plugins/lisa/.codex-plugin/skills/lisa-setup-linear/SKILL.md +4 -4
  207. package/plugins/lisa/.codex-plugin/skills/lisa-track/SKILL.md +1 -1
  208. package/plugins/lisa/.codex-plugin/skills/lisa-tracker-build-intake/SKILL.md +9 -0
  209. package/plugins/lisa/agents/linear-prd-intake.md +8 -8
  210. package/plugins/lisa/hooks/block-blind-automerge.sh +81 -2
  211. package/plugins/lisa/hooks/block-direct-issue-create.agy.sh +64 -16
  212. package/plugins/lisa/hooks/block-direct-issue-create.sh +293 -8
  213. package/plugins/lisa/hooks/block-instruction-file-edits.sh +21 -0
  214. package/plugins/lisa/hooks/block-managed-file-edits.sh +78 -15
  215. package/plugins/lisa/hooks/block-no-verify.sh +21 -0
  216. package/plugins/lisa/hooks/block-shell-json-parsing.sh +21 -0
  217. package/plugins/lisa/hooks/failure-signature-index.mjs +240 -5
  218. package/plugins/lisa/hooks/guard-dedupe.bash +337 -0
  219. package/plugins/lisa/hooks/inject-rules.sh +13 -5
  220. package/plugins/lisa/hooks/operational-hazards.mjs +736 -0
  221. package/plugins/lisa/hooks/operational-hazards.sh +28 -0
  222. package/plugins/lisa/hooks/parity-safety-net.sh +29 -1
  223. package/plugins/lisa/hooks/threshold-ratchet-compare.mjs +102 -21
  224. package/plugins/lisa/hooks/threshold-ratchet-families.mjs +130 -2
  225. package/plugins/lisa/hooks/threshold-ratchet.mjs +63 -13
  226. package/plugins/lisa/hooks/worktree-binding-guard.mjs +183 -1
  227. package/plugins/lisa/hooks/worktree-binding-guard.sh +21 -0
  228. package/plugins/lisa/rules/eager/00-rule-index.md +1 -0
  229. package/plugins/lisa/rules/eager/operational-hazards.md +50 -0
  230. package/plugins/lisa/rules/eager/tracked-work.md +1 -1
  231. package/plugins/lisa/rules/reference/config-resolution.md +1 -1
  232. package/plugins/lisa/rules/reference/leaf-only-lifecycle.md +1 -1
  233. package/plugins/lisa/rules/reference/operational-hazards.md +115 -0
  234. package/plugins/lisa/rules/reference/work-item-trailer-definition.md +129 -0
  235. package/plugins/lisa/scripts/intake-blocker-reprobe.mjs +407 -26
  236. package/plugins/lisa/scripts/qa-signal-lifecycle.mjs +507 -0
  237. package/plugins/lisa/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
  238. package/plugins/lisa/skills/lisa-github-build-intake/SKILL.md +27 -4
  239. package/plugins/lisa/skills/lisa-github-prd-intake/SKILL.md +1 -1
  240. package/plugins/lisa/skills/lisa-github-validate-issue/SKILL.md +22 -0
  241. package/plugins/lisa/skills/lisa-github-verify/SKILL.md +14 -0
  242. package/plugins/lisa/skills/lisa-github-write-issue/SKILL.md +51 -0
  243. package/plugins/lisa/skills/lisa-intake/SKILL.md +1 -1
  244. package/plugins/lisa/skills/lisa-jira-build-intake/SKILL.md +27 -4
  245. package/plugins/lisa/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
  246. package/plugins/lisa/skills/lisa-jira-verify/SKILL.md +14 -0
  247. package/plugins/lisa/skills/lisa-jira-write-ticket/SKILL.md +51 -0
  248. package/plugins/lisa/skills/lisa-linear-access/SKILL.md +55 -2
  249. package/plugins/lisa/skills/lisa-linear-build-intake/SKILL.md +27 -4
  250. package/plugins/lisa/skills/lisa-linear-prd-intake/SKILL.md +48 -31
  251. package/plugins/lisa/skills/lisa-linear-to-tracker/SKILL.md +3 -3
  252. package/plugins/lisa/skills/lisa-linear-validate-issue/SKILL.md +22 -0
  253. package/plugins/lisa/skills/lisa-linear-verify/SKILL.md +14 -0
  254. package/plugins/lisa/skills/lisa-linear-write-issue/SKILL.md +54 -3
  255. package/plugins/lisa/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  256. package/plugins/lisa/skills/lisa-qa-clear/SKILL.md +19 -7
  257. package/plugins/lisa/skills/lisa-qa-fail/SKILL.md +28 -3
  258. package/plugins/lisa/skills/lisa-qa-queue/SKILL.md +28 -3
  259. package/plugins/lisa/skills/lisa-repair-intake/SKILL.md +94 -24
  260. package/plugins/lisa/skills/lisa-rework-triage/SKILL.md +32 -2
  261. package/plugins/lisa/skills/lisa-setup-linear/SKILL.md +5 -5
  262. package/plugins/lisa/skills/lisa-track/SKILL.md +1 -1
  263. package/plugins/lisa/skills/lisa-tracker-build-intake/SKILL.md +9 -0
  264. package/plugins/lisa-agy/agents/linear-prd-intake.md +8 -8
  265. package/plugins/lisa-agy/hooks/block-blind-automerge.sh +81 -2
  266. package/plugins/lisa-agy/hooks/block-direct-issue-create.agy.sh +64 -16
  267. package/plugins/lisa-agy/hooks/block-direct-issue-create.sh +293 -8
  268. package/plugins/lisa-agy/hooks/block-instruction-file-edits.sh +21 -0
  269. package/plugins/lisa-agy/hooks/block-managed-file-edits.sh +78 -15
  270. package/plugins/lisa-agy/hooks/block-shell-json-parsing.sh +21 -0
  271. package/plugins/lisa-agy/hooks/guard-dedupe.bash +337 -0
  272. package/plugins/lisa-agy/hooks/parity-safety-net.sh +29 -1
  273. package/plugins/lisa-agy/hooks.json +1 -1
  274. package/plugins/lisa-agy/plugin.json +1 -1
  275. package/plugins/lisa-agy/scripts/intake-blocker-reprobe.mjs +407 -26
  276. package/plugins/lisa-agy/scripts/qa-signal-lifecycle.mjs +507 -0
  277. package/plugins/lisa-agy/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
  278. package/plugins/lisa-agy/skills/lisa-github-build-intake/SKILL.md +27 -4
  279. package/plugins/lisa-agy/skills/lisa-github-prd-intake/SKILL.md +1 -1
  280. package/plugins/lisa-agy/skills/lisa-github-validate-issue/SKILL.md +22 -0
  281. package/plugins/lisa-agy/skills/lisa-github-verify/SKILL.md +14 -0
  282. package/plugins/lisa-agy/skills/lisa-github-write-issue/SKILL.md +51 -0
  283. package/plugins/lisa-agy/skills/lisa-intake/SKILL.md +1 -1
  284. package/plugins/lisa-agy/skills/lisa-jira-build-intake/SKILL.md +27 -4
  285. package/plugins/lisa-agy/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
  286. package/plugins/lisa-agy/skills/lisa-jira-verify/SKILL.md +14 -0
  287. package/plugins/lisa-agy/skills/lisa-jira-write-ticket/SKILL.md +51 -0
  288. package/plugins/lisa-agy/skills/lisa-linear-access/SKILL.md +55 -2
  289. package/plugins/lisa-agy/skills/lisa-linear-build-intake/SKILL.md +27 -4
  290. package/plugins/lisa-agy/skills/lisa-linear-prd-intake/SKILL.md +48 -31
  291. package/plugins/lisa-agy/skills/lisa-linear-to-tracker/SKILL.md +3 -3
  292. package/plugins/lisa-agy/skills/lisa-linear-validate-issue/SKILL.md +22 -0
  293. package/plugins/lisa-agy/skills/lisa-linear-verify/SKILL.md +14 -0
  294. package/plugins/lisa-agy/skills/lisa-linear-write-issue/SKILL.md +54 -3
  295. package/plugins/lisa-agy/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  296. package/plugins/lisa-agy/skills/lisa-qa-clear/SKILL.md +19 -7
  297. package/plugins/lisa-agy/skills/lisa-qa-fail/SKILL.md +28 -3
  298. package/plugins/lisa-agy/skills/lisa-qa-queue/SKILL.md +28 -3
  299. package/plugins/lisa-agy/skills/lisa-repair-intake/SKILL.md +94 -24
  300. package/plugins/lisa-agy/skills/lisa-rework-triage/SKILL.md +32 -2
  301. package/plugins/lisa-agy/skills/lisa-setup-linear/SKILL.md +5 -5
  302. package/plugins/lisa-agy/skills/lisa-track/SKILL.md +1 -1
  303. package/plugins/lisa-agy/skills/lisa-tracker-build-intake/SKILL.md +9 -0
  304. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  305. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  306. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  307. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  308. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  309. package/plugins/lisa-copilot/.claude-plugin/plugin.json +19 -1
  310. package/plugins/lisa-copilot/agents/linear-prd-intake.agent.md +8 -8
  311. package/plugins/lisa-copilot/hooks/block-blind-automerge.sh +81 -2
  312. package/plugins/lisa-copilot/hooks/block-direct-issue-create.sh +293 -8
  313. package/plugins/lisa-copilot/hooks/block-instruction-file-edits.sh +21 -0
  314. package/plugins/lisa-copilot/hooks/block-managed-file-edits.sh +78 -15
  315. package/plugins/lisa-copilot/hooks/block-no-verify.sh +21 -0
  316. package/plugins/lisa-copilot/hooks/block-shell-json-parsing.sh +21 -0
  317. package/plugins/lisa-copilot/hooks/failure-signature-index.mjs +240 -5
  318. package/plugins/lisa-copilot/hooks/guard-dedupe.bash +337 -0
  319. package/plugins/lisa-copilot/hooks/inject-rules.sh +13 -5
  320. package/plugins/lisa-copilot/hooks/operational-hazards.mjs +736 -0
  321. package/plugins/lisa-copilot/hooks/operational-hazards.sh +28 -0
  322. package/plugins/lisa-copilot/hooks/parity-safety-net.sh +29 -1
  323. package/plugins/lisa-copilot/hooks/threshold-ratchet-compare.mjs +102 -21
  324. package/plugins/lisa-copilot/hooks/threshold-ratchet-families.mjs +130 -2
  325. package/plugins/lisa-copilot/hooks/threshold-ratchet.mjs +63 -13
  326. package/plugins/lisa-copilot/hooks/worktree-binding-guard.mjs +183 -1
  327. package/plugins/lisa-copilot/hooks/worktree-binding-guard.sh +21 -0
  328. package/plugins/lisa-copilot/rules/eager/00-rule-index.md +1 -0
  329. package/plugins/lisa-copilot/rules/eager/operational-hazards.md +50 -0
  330. package/plugins/lisa-copilot/rules/eager/tracked-work.md +1 -1
  331. package/plugins/lisa-copilot/rules/reference/config-resolution.md +1 -1
  332. package/plugins/lisa-copilot/rules/reference/leaf-only-lifecycle.md +1 -1
  333. package/plugins/lisa-copilot/rules/reference/operational-hazards.md +115 -0
  334. package/plugins/lisa-copilot/rules/reference/work-item-trailer-definition.md +129 -0
  335. package/plugins/lisa-copilot/scripts/intake-blocker-reprobe.mjs +407 -26
  336. package/plugins/lisa-copilot/scripts/qa-signal-lifecycle.mjs +507 -0
  337. package/plugins/lisa-copilot/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
  338. package/plugins/lisa-copilot/skills/lisa-github-build-intake/SKILL.md +27 -4
  339. package/plugins/lisa-copilot/skills/lisa-github-prd-intake/SKILL.md +1 -1
  340. package/plugins/lisa-copilot/skills/lisa-github-validate-issue/SKILL.md +22 -0
  341. package/plugins/lisa-copilot/skills/lisa-github-verify/SKILL.md +14 -0
  342. package/plugins/lisa-copilot/skills/lisa-github-write-issue/SKILL.md +51 -0
  343. package/plugins/lisa-copilot/skills/lisa-intake/SKILL.md +1 -1
  344. package/plugins/lisa-copilot/skills/lisa-jira-build-intake/SKILL.md +27 -4
  345. package/plugins/lisa-copilot/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
  346. package/plugins/lisa-copilot/skills/lisa-jira-verify/SKILL.md +14 -0
  347. package/plugins/lisa-copilot/skills/lisa-jira-write-ticket/SKILL.md +51 -0
  348. package/plugins/lisa-copilot/skills/lisa-linear-access/SKILL.md +55 -2
  349. package/plugins/lisa-copilot/skills/lisa-linear-build-intake/SKILL.md +27 -4
  350. package/plugins/lisa-copilot/skills/lisa-linear-prd-intake/SKILL.md +48 -31
  351. package/plugins/lisa-copilot/skills/lisa-linear-to-tracker/SKILL.md +3 -3
  352. package/plugins/lisa-copilot/skills/lisa-linear-validate-issue/SKILL.md +22 -0
  353. package/plugins/lisa-copilot/skills/lisa-linear-verify/SKILL.md +14 -0
  354. package/plugins/lisa-copilot/skills/lisa-linear-write-issue/SKILL.md +54 -3
  355. package/plugins/lisa-copilot/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  356. package/plugins/lisa-copilot/skills/lisa-qa-clear/SKILL.md +19 -7
  357. package/plugins/lisa-copilot/skills/lisa-qa-fail/SKILL.md +28 -3
  358. package/plugins/lisa-copilot/skills/lisa-qa-queue/SKILL.md +28 -3
  359. package/plugins/lisa-copilot/skills/lisa-repair-intake/SKILL.md +94 -24
  360. package/plugins/lisa-copilot/skills/lisa-rework-triage/SKILL.md +32 -2
  361. package/plugins/lisa-copilot/skills/lisa-setup-linear/SKILL.md +5 -5
  362. package/plugins/lisa-copilot/skills/lisa-track/SKILL.md +1 -1
  363. package/plugins/lisa-copilot/skills/lisa-tracker-build-intake/SKILL.md +9 -0
  364. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  365. package/plugins/lisa-cursor/agents/linear-prd-intake.md +8 -8
  366. package/plugins/lisa-cursor/hooks/block-blind-automerge.sh +81 -2
  367. package/plugins/lisa-cursor/hooks/block-direct-issue-create.sh +293 -8
  368. package/plugins/lisa-cursor/hooks/block-instruction-file-edits.sh +21 -0
  369. package/plugins/lisa-cursor/hooks/block-managed-file-edits.sh +78 -15
  370. package/plugins/lisa-cursor/hooks/block-no-verify.sh +21 -0
  371. package/plugins/lisa-cursor/hooks/block-shell-json-parsing.sh +21 -0
  372. package/plugins/lisa-cursor/hooks/failure-signature-index.mjs +240 -5
  373. package/plugins/lisa-cursor/hooks/guard-dedupe.bash +337 -0
  374. package/plugins/lisa-cursor/hooks/hooks.json +10 -0
  375. package/plugins/lisa-cursor/hooks/operational-hazards.mjs +736 -0
  376. package/plugins/lisa-cursor/hooks/operational-hazards.sh +28 -0
  377. package/plugins/lisa-cursor/hooks/parity-safety-net.sh +29 -1
  378. package/plugins/lisa-cursor/hooks/threshold-ratchet-compare.mjs +102 -21
  379. package/plugins/lisa-cursor/hooks/threshold-ratchet-families.mjs +130 -2
  380. package/plugins/lisa-cursor/hooks/threshold-ratchet.mjs +63 -13
  381. package/plugins/lisa-cursor/hooks/worktree-binding-guard.mjs +183 -1
  382. package/plugins/lisa-cursor/hooks/worktree-binding-guard.sh +21 -0
  383. package/plugins/lisa-cursor/rules/00-rule-index.mdc +1 -0
  384. package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +1 -1
  385. package/plugins/lisa-cursor/rules/leaf-only-lifecycle-reference.mdc +1 -1
  386. package/plugins/lisa-cursor/rules/operational-hazards-reference.mdc +120 -0
  387. package/plugins/lisa-cursor/rules/operational-hazards.mdc +55 -0
  388. package/plugins/lisa-cursor/rules/tracked-work.mdc +1 -1
  389. package/plugins/lisa-cursor/rules/work-item-trailer-definition-reference.mdc +134 -0
  390. package/plugins/lisa-cursor/scripts/intake-blocker-reprobe.mjs +407 -26
  391. package/plugins/lisa-cursor/scripts/qa-signal-lifecycle.mjs +507 -0
  392. package/plugins/lisa-cursor/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
  393. package/plugins/lisa-cursor/skills/lisa-github-build-intake/SKILL.md +27 -4
  394. package/plugins/lisa-cursor/skills/lisa-github-prd-intake/SKILL.md +1 -1
  395. package/plugins/lisa-cursor/skills/lisa-github-validate-issue/SKILL.md +22 -0
  396. package/plugins/lisa-cursor/skills/lisa-github-verify/SKILL.md +14 -0
  397. package/plugins/lisa-cursor/skills/lisa-github-write-issue/SKILL.md +51 -0
  398. package/plugins/lisa-cursor/skills/lisa-intake/SKILL.md +1 -1
  399. package/plugins/lisa-cursor/skills/lisa-jira-build-intake/SKILL.md +27 -4
  400. package/plugins/lisa-cursor/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
  401. package/plugins/lisa-cursor/skills/lisa-jira-verify/SKILL.md +14 -0
  402. package/plugins/lisa-cursor/skills/lisa-jira-write-ticket/SKILL.md +51 -0
  403. package/plugins/lisa-cursor/skills/lisa-linear-access/SKILL.md +55 -2
  404. package/plugins/lisa-cursor/skills/lisa-linear-build-intake/SKILL.md +27 -4
  405. package/plugins/lisa-cursor/skills/lisa-linear-prd-intake/SKILL.md +48 -31
  406. package/plugins/lisa-cursor/skills/lisa-linear-to-tracker/SKILL.md +3 -3
  407. package/plugins/lisa-cursor/skills/lisa-linear-validate-issue/SKILL.md +22 -0
  408. package/plugins/lisa-cursor/skills/lisa-linear-verify/SKILL.md +14 -0
  409. package/plugins/lisa-cursor/skills/lisa-linear-write-issue/SKILL.md +54 -3
  410. package/plugins/lisa-cursor/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  411. package/plugins/lisa-cursor/skills/lisa-qa-clear/SKILL.md +19 -7
  412. package/plugins/lisa-cursor/skills/lisa-qa-fail/SKILL.md +28 -3
  413. package/plugins/lisa-cursor/skills/lisa-qa-queue/SKILL.md +28 -3
  414. package/plugins/lisa-cursor/skills/lisa-repair-intake/SKILL.md +94 -24
  415. package/plugins/lisa-cursor/skills/lisa-rework-triage/SKILL.md +32 -2
  416. package/plugins/lisa-cursor/skills/lisa-setup-linear/SKILL.md +5 -5
  417. package/plugins/lisa-cursor/skills/lisa-track/SKILL.md +1 -1
  418. package/plugins/lisa-cursor/skills/lisa-tracker-build-intake/SKILL.md +9 -0
  419. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  420. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  421. package/plugins/lisa-expo-agy/plugin.json +1 -1
  422. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  423. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  424. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  425. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  426. package/plugins/lisa-harper-fabric/hooks/inject-rules.sh +14 -3
  427. package/plugins/lisa-harper-fabric/rules/eager/harper-fabric.md +31 -0
  428. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  429. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  430. package/plugins/lisa-harper-fabric-copilot/hooks/inject-rules.sh +14 -3
  431. package/plugins/lisa-harper-fabric-copilot/rules/eager/harper-fabric.md +31 -0
  432. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  433. package/plugins/lisa-harper-fabric-cursor/rules/harper-fabric-reference.mdc +57 -0
  434. package/plugins/lisa-harper-fabric-cursor/rules/harper-fabric.mdc +32 -53
  435. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  436. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  437. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  438. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  439. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  440. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  441. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  442. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  443. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  444. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  445. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  446. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  447. package/plugins/lisa-phaser/hooks/inject-rules.sh +14 -3
  448. package/plugins/lisa-phaser/rules/eager/phaser.md +44 -0
  449. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  450. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  451. package/plugins/lisa-phaser-copilot/hooks/inject-rules.sh +14 -3
  452. package/plugins/lisa-phaser-copilot/rules/eager/phaser.md +44 -0
  453. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  454. package/plugins/lisa-phaser-cursor/rules/phaser-reference.mdc +189 -0
  455. package/plugins/lisa-phaser-cursor/rules/phaser.mdc +45 -185
  456. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  457. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  458. package/plugins/lisa-rails/hooks/inject-rules.sh +14 -4
  459. package/plugins/lisa-rails/rules/eager/rails-conventions.md +23 -0
  460. package/plugins/lisa-rails-agy/plugin.json +1 -1
  461. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  462. package/plugins/lisa-rails-copilot/hooks/inject-rules.sh +14 -4
  463. package/plugins/lisa-rails-copilot/rules/eager/rails-conventions.md +23 -0
  464. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  465. package/plugins/lisa-rails-cursor/rules/rails-conventions-reference.mdc +181 -0
  466. package/plugins/lisa-rails-cursor/rules/rails-conventions.mdc +24 -177
  467. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  468. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  469. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  470. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  471. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  472. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  473. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  474. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  475. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  476. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  477. package/plugins/materialized-artifacts.json +1 -0
  478. package/plugins/src/base/.claude-plugin/plugin.json +27 -0
  479. package/plugins/src/base/agents/linear-prd-intake.md +8 -8
  480. package/plugins/src/base/hooks/block-blind-automerge.sh +81 -2
  481. package/plugins/src/base/hooks/block-direct-issue-create.agy.sh +64 -16
  482. package/plugins/src/base/hooks/block-direct-issue-create.sh +293 -8
  483. package/plugins/src/base/hooks/block-instruction-file-edits.sh +21 -0
  484. package/plugins/src/base/hooks/block-managed-file-edits.sh +78 -15
  485. package/plugins/src/base/hooks/block-no-verify.sh +21 -0
  486. package/plugins/src/base/hooks/block-shell-json-parsing.sh +21 -0
  487. package/plugins/src/base/hooks/failure-signature-index.mjs +240 -5
  488. package/plugins/src/base/hooks/guard-dedupe.bash +337 -0
  489. package/plugins/src/base/hooks/inject-rules.sh +13 -5
  490. package/plugins/src/base/hooks/operational-hazards.mjs +736 -0
  491. package/plugins/src/base/hooks/operational-hazards.sh +28 -0
  492. package/plugins/src/base/hooks/parity-safety-net.sh +29 -1
  493. package/plugins/src/base/hooks/threshold-ratchet-compare.mjs +102 -21
  494. package/plugins/src/base/hooks/threshold-ratchet-families.mjs +130 -2
  495. package/plugins/src/base/hooks/threshold-ratchet.mjs +63 -13
  496. package/plugins/src/base/hooks/worktree-binding-guard.mjs +183 -1
  497. package/plugins/src/base/hooks/worktree-binding-guard.sh +21 -0
  498. package/plugins/src/base/rules/eager/00-rule-index.md +1 -0
  499. package/plugins/src/base/rules/eager/operational-hazards.md +50 -0
  500. package/plugins/src/base/rules/eager/tracked-work.md +1 -1
  501. package/plugins/src/base/rules/reference/config-resolution.md +1 -1
  502. package/plugins/src/base/rules/reference/leaf-only-lifecycle.md +1 -1
  503. package/plugins/src/base/rules/reference/operational-hazards.md +115 -0
  504. package/plugins/src/base/rules/reference/work-item-trailer-definition.md +129 -0
  505. package/plugins/src/base/scripts/intake-blocker-reprobe.mjs +407 -26
  506. package/plugins/src/base/scripts/qa-signal-lifecycle.mjs +507 -0
  507. package/plugins/src/base/skills/lisa-drive-pr-to-merge/SKILL.md +130 -19
  508. package/plugins/src/base/skills/lisa-github-build-intake/SKILL.md +27 -4
  509. package/plugins/src/base/skills/lisa-github-prd-intake/SKILL.md +1 -1
  510. package/plugins/src/base/skills/lisa-github-validate-issue/SKILL.md +22 -0
  511. package/plugins/src/base/skills/lisa-github-verify/SKILL.md +14 -0
  512. package/plugins/src/base/skills/lisa-github-write-issue/SKILL.md +51 -0
  513. package/plugins/src/base/skills/lisa-intake/SKILL.md +1 -1
  514. package/plugins/src/base/skills/lisa-jira-build-intake/SKILL.md +27 -4
  515. package/plugins/src/base/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
  516. package/plugins/src/base/skills/lisa-jira-verify/SKILL.md +14 -0
  517. package/plugins/src/base/skills/lisa-jira-write-ticket/SKILL.md +51 -0
  518. package/plugins/src/base/skills/lisa-linear-access/SKILL.md +55 -2
  519. package/plugins/src/base/skills/lisa-linear-build-intake/SKILL.md +27 -4
  520. package/plugins/src/base/skills/lisa-linear-prd-intake/SKILL.md +48 -31
  521. package/plugins/src/base/skills/lisa-linear-to-tracker/SKILL.md +3 -3
  522. package/plugins/src/base/skills/lisa-linear-validate-issue/SKILL.md +22 -0
  523. package/plugins/src/base/skills/lisa-linear-verify/SKILL.md +14 -0
  524. package/plugins/src/base/skills/lisa-linear-write-issue/SKILL.md +54 -3
  525. package/plugins/src/base/skills/lisa-prd-ticket-coverage/SKILL.md +2 -2
  526. package/plugins/src/base/skills/lisa-qa-clear/SKILL.md +19 -7
  527. package/plugins/src/base/skills/lisa-qa-fail/SKILL.md +28 -3
  528. package/plugins/src/base/skills/lisa-qa-queue/SKILL.md +28 -3
  529. package/plugins/src/base/skills/lisa-repair-intake/SKILL.md +94 -24
  530. package/plugins/src/base/skills/lisa-rework-triage/SKILL.md +32 -2
  531. package/plugins/src/base/skills/lisa-setup-linear/SKILL.md +5 -5
  532. package/plugins/src/base/skills/lisa-track/SKILL.md +1 -1
  533. package/plugins/src/base/skills/lisa-tracker-build-intake/SKILL.md +9 -0
  534. package/plugins/src/harper-fabric/hooks/inject-rules.sh +14 -3
  535. package/plugins/src/harper-fabric/rules/eager/harper-fabric.md +31 -0
  536. package/plugins/src/phaser/hooks/inject-rules.sh +14 -3
  537. package/plugins/src/phaser/rules/eager/phaser.md +44 -0
  538. package/plugins/src/rails/hooks/inject-rules.sh +14 -4
  539. package/plugins/src/rails/rules/eager/rails-conventions.md +23 -0
  540. package/rails/copy-overwrite/scripts/check-threshold-ratchet.mjs +63 -13
  541. package/rails/copy-overwrite/scripts/threshold-ratchet-compare.mjs +102 -21
  542. package/rails/copy-overwrite/scripts/threshold-ratchet-families.mjs +130 -2
  543. package/rails/create-only/.github/workflows/ci.yml +8 -0
  544. package/rails/create-only/.github/workflows/deploy.yml +22 -0
  545. package/scripts/build-plugins.sh +2 -1
  546. package/scripts/check-engine-floor.mjs +361 -0
  547. package/scripts/check-guard-parity-notes.mjs +709 -0
  548. package/scripts/check-template-workflow-refs.mjs +9 -0
  549. package/scripts/check-third-party-action-pins.mjs +17 -1
  550. package/scripts/check-workflow-contract-assertions.mjs +480 -0
  551. package/scripts/copy-opencode-plugin-templates.mjs +1 -0
  552. package/scripts/deployed-guard-advice.mjs +636 -0
  553. package/scripts/generate-agy-plugin-artifacts.mjs +35 -10
  554. package/scripts/generate-export-surface.mjs +105 -19
  555. package/scripts/generate-two-channel-couplings.ts +143 -212
  556. package/scripts/lisa-enforcement-fallback.sh +31 -0
  557. package/scripts/measure-scripts-profile-gap.mts +361 -0
  558. package/scripts/mutation-performance-measure.mjs +21 -5
  559. package/scripts/reconcile-release-tags.mjs +65 -84
  560. package/scripts/two-channel-couplings.json +302 -70
  561. package/scripts/workflow-contract-assertion.sh +47 -0
  562. package/typescript/copy-overwrite/.lintstagedrc.json +1 -0
  563. package/typescript/copy-overwrite/eslint.ignore.config.json +1 -0
  564. package/typescript/copy-overwrite/scripts/check-nightly-e2e-health.mjs +527 -53
  565. package/typescript/copy-overwrite/scripts/check-skipped-required-checks.mjs +301 -13
  566. package/typescript/copy-overwrite/scripts/check-threshold-ratchet.mjs +63 -13
  567. package/typescript/copy-overwrite/scripts/threshold-ratchet-compare.mjs +102 -21
  568. package/typescript/copy-overwrite/scripts/threshold-ratchet-families.mjs +130 -2
  569. package/typescript/create-only/.github/workflows/ci.yml +7 -0
  570. /package/plugins/lisa-harper-fabric/rules/{harper-fabric.md → reference/harper-fabric.md} +0 -0
  571. /package/plugins/lisa-harper-fabric-copilot/rules/{harper-fabric.md → reference/harper-fabric.md} +0 -0
  572. /package/plugins/lisa-phaser/rules/{phaser.md → reference/phaser.md} +0 -0
  573. /package/plugins/lisa-phaser-copilot/rules/{phaser.md → reference/phaser.md} +0 -0
  574. /package/plugins/lisa-rails/rules/{rails-conventions.md → reference/rails-conventions.md} +0 -0
  575. /package/plugins/lisa-rails-copilot/rules/{rails-conventions.md → reference/rails-conventions.md} +0 -0
  576. /package/plugins/src/harper-fabric/rules/{harper-fabric.md → reference/harper-fabric.md} +0 -0
  577. /package/plugins/src/phaser/rules/{phaser.md → reference/phaser.md} +0 -0
  578. /package/plugins/src/rails/rules/{rails-conventions.md → reference/rails-conventions.md} +0 -0
@@ -0,0 +1,636 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * What the DEPLOYED guard copies say, not what the source says (#3998).
4
+ *
5
+ * ## The defect
6
+ *
7
+ * The repository forbids a piece of advice, pins a test against it, and
8
+ * deployed guards keep dispensing it anyway. Those are two different claims —
9
+ * *"the repo forbids this advice"* and *"no agent is receiving this advice"* —
10
+ * and until this file only the first was testable. Every conformance control
11
+ * here reads `plugins/**` and `all/**`. None of them can see what a copy that
12
+ * is actually serving an agent says.
13
+ *
14
+ * `parity-safety-net-no-stash-advice.test.ts` forbids four advisory spellings
15
+ * of "use the shared stash to preserve your work". It is green. Three sessions
16
+ * were nonetheless told to use the shared stash in one day, by guards resolving
17
+ * from an older copy — and on a machine running many agents against one `.git`,
18
+ * `stash@{0}` belongs to whoever pushed last, so a `pop` taken on that advice
19
+ * consumes a sibling lane's uncommitted work.
20
+ *
21
+ * ## Why a fix does not retire the advice
22
+ *
23
+ * A guard reaches an agent by more than one channel and the agent sees the
24
+ * UNION of their verdicts, so a TIGHTENING on either channel takes effect at
25
+ * once while a RELAXATION is inert until the slowest channel catches up.
26
+ * Deleting the advice from source is a relaxation. It propagates at the speed
27
+ * of the slowest copy on the disk, and a session resolves its copy ONCE at
28
+ * start and executes that copy for its whole life — which is why every cache
29
+ * entry below is a channel and not an archive.
30
+ *
31
+ * ## One list, cited by both
32
+ *
33
+ * `FORBIDDEN_ADVISORY_PATTERNS` is the single definition of what the advice IS.
34
+ * The in-repo source suite imports it from here rather than keeping its own
35
+ * table, because two lists drift and the drift is invisible: the source suite
36
+ * would stay green against patterns this sweep no longer looks for.
37
+ *
38
+ * ## Fail direction
39
+ *
40
+ * **Report-only on findings, fail-closed on its own blindness.** It gates no
41
+ * commit and no push. It must not: a developer machine legitimately holds
42
+ * hundreds of superseded cache entries, and a blocker that fires on every one
43
+ * of them is a permanent stop-work whose first consequence is being switched
44
+ * off. But the healthy state of this check is an EMPTY report, so a wrong path,
45
+ * an unreadable cache directory, or a channel the resolver does not know about
46
+ * all produce empty and read as clean. Any channel that could not be read makes
47
+ * the sweep `NOT_MEASURED`, never `CLEAN`. Exit codes carry the same split:
48
+ * 0 clean, 1 findings, 2 not measured.
49
+ */
50
+
51
+ import { readdirSync, readFileSync, realpathSync, statSync } from "node:fs";
52
+ import path from "node:path";
53
+ import process from "node:process";
54
+ import { fileURLToPath } from "node:url";
55
+
56
+ /** Directory segment holding plugin trees under a harness config directory. */
57
+ const PLUGINS = "plugins";
58
+
59
+ /** The plugin and marketplace both named `lisa`. */
60
+ const LISA = "lisa";
61
+
62
+ /** Name of the channel the plugin manifest serves. */
63
+ const PLUGIN_MANIFEST = "plugin-manifest";
64
+
65
+ /** Directory a plugin's own manifest lives in. */
66
+ const PLUGIN_MANIFEST_DIR = ".claude-plugin";
67
+
68
+ /** Filename of a plugin's own manifest. */
69
+ const PLUGIN_MANIFEST_FILE = "plugin.json";
70
+
71
+ /** The guard every caller means unless it says otherwise. */
72
+ const DEFAULT_GUARD = "parity-safety-net";
73
+
74
+ /** Whole-sweep verdicts. */
75
+ export const ADVICE_SWEEP_VERDICTS = Object.freeze([
76
+ "CLEAN",
77
+ "OUTLAWED_ADVICE",
78
+ "NOT_MEASURED",
79
+ ]);
80
+
81
+ /**
82
+ * Spellings that RECOMMEND the shared stash rather than prohibiting it.
83
+ *
84
+ * **Every pattern here was verified against the real historical bytes, in both
85
+ * directions** — it matches the text that shipped before the fix and does not
86
+ * match the text that shipped after. That check is not ceremony: the first
87
+ * draft of this table required "stash" to appear AFTER "safe alternatives",
88
+ * and in guard 7's actual comment it appears before, so the pattern matched
89
+ * nothing and the suite passed against the very file it was written to catch.
90
+ * A prose pattern that has not been run against the prose it forbids is not
91
+ * evidence of anything. If you add one, take the bytes from the commit that
92
+ * carried the bad text and watch the case go red first.
93
+ *
94
+ * ## Why these key on ADVISORY forms, not the bare word
95
+ *
96
+ * Guard 7 legitimately BLOCKS `git stash drop` / `git stash clear`, and its
97
+ * message necessarily contains the word. That is a prohibition, not a
98
+ * recommendation, and it must stay. Keying on the bare token would report the
99
+ * one message doing the right thing. Do not "tighten" these to /stash/.
100
+ *
101
+ * The first three were the shipped refusal text of guards 4 and 3 until the
102
+ * fix; the fourth is guard 7's comment, which that fix left behind.
103
+ */
104
+ export const FORBIDDEN_ADVISORY_PATTERNS = Object.freeze([
105
+ Object.freeze({
106
+ id: "guard-4-old-refusal",
107
+ label: "guard 4's old refusal",
108
+ pattern: /use\s+git\s+stash/i,
109
+ why: "offers the shared stash as the way to preserve work before a discard",
110
+ }),
111
+ Object.freeze({
112
+ id: "guard-3-old-refusal",
113
+ label: "guard 3's old refusal",
114
+ pattern: /\(\s*stash\s+or\s+commit/i,
115
+ why: "offers the shared stash as an alternative to committing",
116
+ }),
117
+ Object.freeze({
118
+ id: "stash-as-preservation",
119
+ label: "stash offered as the preservation step",
120
+ pattern: /stash\s+to\s+preserve/i,
121
+ why: "names the shared stash as the preservation mechanism",
122
+ }),
123
+ Object.freeze({
124
+ id: "push-pop-safe-alternative",
125
+ label: "push/pop called a safe alternative",
126
+ pattern: /push\/pop[^.]*safe\s+alternatives?/i,
127
+ why: "asserts the shared stash is safe, which it is not across worktrees",
128
+ }),
129
+ ]);
130
+
131
+ /**
132
+ * Every in-repo directory a guard is SOURCED from.
133
+ *
134
+ * These are the copies the existing source-only suite reads. They are listed
135
+ * here so the source scan and the deployed scan share one scanner and one
136
+ * pattern table — the whole point being that the two answers differ.
137
+ */
138
+ export const SOURCE_GUARD_ROOTS = Object.freeze([
139
+ "plugins/src/base/hooks",
140
+ "plugins/lisa/hooks",
141
+ "plugins/lisa-agy/hooks",
142
+ "plugins/lisa-cursor/hooks",
143
+ "plugins/lisa-copilot/hooks",
144
+ "all/copy-overwrite/scripts/lisa-hooks",
145
+ ]);
146
+
147
+ /**
148
+ * The version a JSON manifest states at its top level.
149
+ * @param {string} file - Absolute path to a JSON file.
150
+ * @param {string} key - Top-level key holding the version.
151
+ * @returns {string} The version, or "" when the file or key is unreadable.
152
+ */
153
+ function readJsonVersion(file, key) {
154
+ try {
155
+ const value = JSON.parse(readFileSync(file, "utf8"))?.[key];
156
+ return typeof value === "string" ? value : "";
157
+ } catch {
158
+ return "";
159
+ }
160
+ }
161
+
162
+ /**
163
+ * A channel that resolved to a copy on disk.
164
+ * @param {string} channel - Channel name.
165
+ * @param {string} file - Absolute path to the guard copy.
166
+ * @param {string} version - The copy's vintage, or "" when undateable.
167
+ * @returns {{channel: string, path: string, version: string, reason: string}}
168
+ * The resolved channel.
169
+ */
170
+ function resolved(channel, file, version) {
171
+ return { channel, path: file, version: version || "unknown", reason: "" };
172
+ }
173
+
174
+ /**
175
+ * A channel that exists in the topology but could not be resolved to a copy.
176
+ * @param {string} channel - Channel name.
177
+ * @param {string} reason - Why it could not be resolved.
178
+ * @returns {{channel: string, path: string, version: string, reason: string}}
179
+ * The unresolved channel.
180
+ */
181
+ function unresolved(channel, reason) {
182
+ return { channel, path: "", version: "", reason };
183
+ }
184
+
185
+ /**
186
+ * The guard tree the repository hook dispatcher resolves, and its vintage.
187
+ *
188
+ * Resolution is first-wins: `scripts/lisa-hooks/` shadows `plugins/lisa/hooks/`
189
+ * outright and the shadowed copy never runs, so the tree reported is whichever
190
+ * is FIRST rather than whichever is newest.
191
+ * @param {string} projectDir - Repository root.
192
+ * @param {string} guard - Guard basename without extension.
193
+ * @returns {{channel: string, path: string, version: string, reason: string}}
194
+ * The dispatcher channel.
195
+ */
196
+ export function repositoryChannel(projectDir, guard) {
197
+ const host = path.join(projectDir, "scripts", "lisa-hooks", `${guard}.sh`);
198
+ if (existsFile(host)) {
199
+ const receipt = path.join(projectDir, ".lisa", "apply-receipt.json");
200
+ return resolved(
201
+ "repository",
202
+ host,
203
+ readJsonVersion(receipt, "lisa_version")
204
+ );
205
+ }
206
+ const plugin = path.join(projectDir, PLUGINS, LISA, "hooks", `${guard}.sh`);
207
+ if (existsFile(plugin)) {
208
+ const manifest = path.join(
209
+ projectDir,
210
+ PLUGINS,
211
+ LISA,
212
+ PLUGIN_MANIFEST_DIR,
213
+ PLUGIN_MANIFEST_FILE
214
+ );
215
+ return resolved("repository", plugin, readJsonVersion(manifest, "version"));
216
+ }
217
+ return unresolved("repository", "no-dispatcher-tree-in-project");
218
+ }
219
+
220
+ /**
221
+ * Whether a path names a readable regular file.
222
+ * @param {string} file - Candidate path.
223
+ * @returns {boolean} True when it is a file this process can stat.
224
+ */
225
+ function existsFile(file) {
226
+ try {
227
+ return statSync(file).isFile();
228
+ } catch {
229
+ return false;
230
+ }
231
+ }
232
+
233
+ /**
234
+ * The channel the plugin manifest runs, per PROJECT DIRECTORY.
235
+ *
236
+ * The runtime keys installs on the project path, which is exactly why two
237
+ * checkouts of one repository on one disk really do run different vintages.
238
+ * A record whose install path does not sit under a `lisa/lisa/` cache is left
239
+ * unresolved on purpose: reporting a clean result about a copy that was never
240
+ * read is the failure this whole file exists to end.
241
+ * @param {string} configDir - Harness config directory.
242
+ * @param {string} projectDir - Repository root.
243
+ * @param {string} guard - Guard basename without extension.
244
+ * @returns {{channel: string, path: string, version: string, reason: string}}
245
+ * The plugin-manifest channel.
246
+ */
247
+ export function pluginManifestChannel(configDir, projectDir, guard) {
248
+ const record = path.join(configDir, PLUGINS, "installed_plugins.json");
249
+ let parsed = null;
250
+ try {
251
+ parsed = JSON.parse(readFileSync(record, "utf8"));
252
+ } catch {
253
+ return unresolved(PLUGIN_MANIFEST, "install-record-unreadable");
254
+ }
255
+ const installs = Object.values(parsed?.plugins ?? {}).flat();
256
+ const match = installs.find(
257
+ entry =>
258
+ entry?.projectPath === projectDir &&
259
+ typeof entry?.installPath === "string" &&
260
+ entry.installPath.includes(`${path.sep}lisa${path.sep}lisa${path.sep}`)
261
+ );
262
+ if (!match) {
263
+ return unresolved(PLUGIN_MANIFEST, "no-install-record-for-this-project");
264
+ }
265
+ return resolved(
266
+ PLUGIN_MANIFEST,
267
+ path.join(match.installPath, "hooks", `${guard}.sh`),
268
+ String(match.version ?? "")
269
+ );
270
+ }
271
+
272
+ /**
273
+ * The copy this session itself resolved, as the harness reports it.
274
+ *
275
+ * `CLAUDE_PLUGIN_ROOT` is a claim the harness makes rather than something read
276
+ * off disk, so an unset variable is an unanswered question and not an absence.
277
+ * @param {string | undefined} pluginRoot - The harness-declared plugin root.
278
+ * @param {string} guard - Guard basename without extension.
279
+ * @returns {{channel: string, path: string, version: string, reason: string}}
280
+ * The session channel.
281
+ */
282
+ export function sessionChannel(pluginRoot, guard) {
283
+ if (!pluginRoot) return unresolved("session", "plugin-root-not-declared");
284
+ const manifest = path.join(
285
+ pluginRoot,
286
+ PLUGIN_MANIFEST_DIR,
287
+ PLUGIN_MANIFEST_FILE
288
+ );
289
+ return resolved(
290
+ "session",
291
+ path.join(pluginRoot, "hooks", `${guard}.sh`),
292
+ readJsonVersion(manifest, "version")
293
+ );
294
+ }
295
+
296
+ /**
297
+ * The marketplace clone, which is what the next install will copy from.
298
+ * @param {string} configDir - Harness config directory.
299
+ * @param {string} guard - Guard basename without extension.
300
+ * @returns {{channel: string, path: string, version: string, reason: string}}
301
+ * The marketplace channel.
302
+ */
303
+ export function marketplaceChannel(configDir, guard) {
304
+ const root = path.join(
305
+ configDir,
306
+ PLUGINS,
307
+ "marketplaces",
308
+ LISA,
309
+ PLUGINS,
310
+ LISA
311
+ );
312
+ const manifest = path.join(root, PLUGIN_MANIFEST_DIR, PLUGIN_MANIFEST_FILE);
313
+ return resolved(
314
+ "marketplace",
315
+ path.join(root, "hooks", `${guard}.sh`),
316
+ readJsonVersion(manifest, "version")
317
+ );
318
+ }
319
+
320
+ /**
321
+ * Every cache entry a live session may still be pinned to.
322
+ *
323
+ * These are channels, not an archive. A session resolves its copy once at start
324
+ * and executes it for its whole life, so an entry six months old is serving
325
+ * whichever lane started while it was in force. Enumerating them is the only
326
+ * way the sweep can answer "is any agent being told this right now".
327
+ * @param {string} configDir - Harness config directory.
328
+ * @param {string} guard - Guard basename without extension.
329
+ * @returns {readonly {channel: string, path: string, version: string, reason: string}[]}
330
+ * One channel per cached vintage, or one unresolved channel.
331
+ */
332
+ export function cacheChannels(configDir, guard) {
333
+ const root = path.join(configDir, PLUGINS, "cache", LISA, LISA);
334
+ let entries = [];
335
+ try {
336
+ entries = readdirSync(root, { withFileTypes: true });
337
+ } catch {
338
+ return [unresolved("cache", "cache-directory-unreadable")];
339
+ }
340
+ return entries
341
+ .filter(entry => entry.isDirectory())
342
+ .map(entry =>
343
+ resolved(
344
+ `cache:${entry.name}`,
345
+ path.join(root, entry.name, "hooks", `${guard}.sh`),
346
+ entry.name
347
+ )
348
+ );
349
+ }
350
+
351
+ /**
352
+ * Every channel a guard can be SERVED from on this machine.
353
+ * @param {{
354
+ * readonly projectDir: string
355
+ * readonly configDir: string
356
+ * readonly pluginRoot?: string | undefined
357
+ * readonly guard?: string | undefined
358
+ * }} where - Where to look.
359
+ * @returns {readonly {channel: string, path: string, version: string, reason: string}[]}
360
+ * The channel roster.
361
+ */
362
+ export function resolveGuardChannels(where) {
363
+ const guard = where.guard ?? DEFAULT_GUARD;
364
+ return [
365
+ repositoryChannel(where.projectDir, guard),
366
+ pluginManifestChannel(where.configDir, where.projectDir, guard),
367
+ sessionChannel(where.pluginRoot, guard),
368
+ marketplaceChannel(where.configDir, guard),
369
+ ...cacheChannels(where.configDir, guard),
370
+ ];
371
+ }
372
+
373
+ /**
374
+ * Every channel a guard is SOURCED from in the checkout.
375
+ *
376
+ * The deliberate counterpart to `resolveGuardChannels`: same scanner, same
377
+ * patterns, different copies. A tree whose source scan is empty and whose
378
+ * deployed scan is not is the gap this file closes, stated as two numbers.
379
+ * @param {string} projectDir - Repository root.
380
+ * @param {string} [guard] - Guard basename without extension.
381
+ * @returns {readonly {channel: string, path: string, version: string, reason: string}[]}
382
+ * The source roster.
383
+ */
384
+ export function resolveSourceChannels(projectDir, guard = DEFAULT_GUARD) {
385
+ return SOURCE_GUARD_ROOTS.map(root =>
386
+ resolved(
387
+ `source:${root}`,
388
+ path.join(projectDir, root, `${guard}.sh`),
389
+ "source"
390
+ )
391
+ );
392
+ }
393
+
394
+ /**
395
+ * Every forbidden advisory pattern a piece of guard text carries.
396
+ * @param {string} text - Guard file contents.
397
+ * @returns {readonly {id: string, label: string, why: string}[]} Matches.
398
+ */
399
+ export function matchAdvisory(text) {
400
+ return FORBIDDEN_ADVISORY_PATTERNS.filter(entry =>
401
+ entry.pattern.test(text)
402
+ ).map(({ id, label, why }) => ({ id, label, why }));
403
+ }
404
+
405
+ /**
406
+ * Scan a channel roster and report every copy dispensing outlawed advice.
407
+ * @param {{
408
+ * readonly channels?: readonly {
409
+ * channel: string, path: string, version: string, reason: string
410
+ * }[]
411
+ * readonly read?: (file: string) => string
412
+ * }} input - The roster, and how to read a copy.
413
+ * @returns {{
414
+ * readonly verdict: string
415
+ * readonly examinedCount: number
416
+ * readonly findings: readonly {
417
+ * readonly channel: string
418
+ * readonly path: string
419
+ * readonly version: string
420
+ * readonly id: string
421
+ * readonly label: string
422
+ * readonly why: string
423
+ * }[]
424
+ * readonly notMeasured: readonly {
425
+ * readonly channel: string
426
+ * readonly reason: string
427
+ * }[]
428
+ * readonly reportOnly: true
429
+ * }} The sweep result.
430
+ */
431
+ export function scanDeployedGuardAdvice(input = {}) {
432
+ const channels = Array.isArray(input.channels) ? input.channels : null;
433
+ const read = input.read ?? (file => readFileSync(file, "utf8"));
434
+ if (channels === null) {
435
+ return sweep(
436
+ [],
437
+ [{ channel: "<roster>", reason: "channel-list-unavailable" }],
438
+ 0
439
+ );
440
+ }
441
+
442
+ const findings = [];
443
+ const notMeasured = [];
444
+ let examinedCount = 0;
445
+ for (const channel of channels) {
446
+ if (!channel?.path) {
447
+ notMeasured.push({
448
+ channel: channel?.channel ?? "<unnamed>",
449
+ reason: channel?.reason || "unresolved",
450
+ });
451
+ continue;
452
+ }
453
+ let text = null;
454
+ try {
455
+ text = read(channel.path);
456
+ } catch {
457
+ notMeasured.push({ channel: channel.channel, reason: "copy-unreadable" });
458
+ continue;
459
+ }
460
+ examinedCount += 1;
461
+ for (const match of matchAdvisory(text)) {
462
+ findings.push({
463
+ channel: channel.channel,
464
+ path: channel.path,
465
+ version: channel.version,
466
+ ...match,
467
+ });
468
+ }
469
+ }
470
+ return sweep(findings, notMeasured, examinedCount);
471
+ }
472
+
473
+ /**
474
+ * Assemble a sweep result, with blindness outranking findings.
475
+ *
476
+ * A sweep that could not read part of its roster cannot call the rest clean —
477
+ * but it still reports what it DID find, because a real finding does not stop
478
+ * mattering because a different channel was unreadable.
479
+ * @param {readonly object[]} findings - Copies dispensing outlawed advice.
480
+ * @param {readonly object[]} notMeasured - Channels that could not be read.
481
+ * @param {number} examinedCount - Copies actually read.
482
+ * @returns {ReturnType<typeof scanDeployedGuardAdvice>} The sweep.
483
+ */
484
+ function sweep(findings, notMeasured, examinedCount) {
485
+ const verdict =
486
+ notMeasured.length > 0
487
+ ? "NOT_MEASURED"
488
+ : findings.length > 0
489
+ ? "OUTLAWED_ADVICE"
490
+ : "CLEAN";
491
+ return { verdict, examinedCount, findings, notMeasured, reportOnly: true };
492
+ }
493
+
494
+ /**
495
+ * The CLI exit status for a sweep.
496
+ *
497
+ * `2` is load-bearing: a sweep that examined nothing must not exit 0 beside an
498
+ * empty finding list, which is indistinguishable from a clean fleet.
499
+ * @param {{ readonly verdict?: string }} result - A sweep result.
500
+ * @returns {0 | 1 | 2} The exit status.
501
+ */
502
+ export function deployedGuardAdviceExitCode(result) {
503
+ if (result?.verdict === "CLEAN") return 0;
504
+ if (result?.verdict === "OUTLAWED_ADVICE") return 1;
505
+ return 2;
506
+ }
507
+
508
+ /** Channel-name prefix marking a superseded copy a session may still be pinned to. */
509
+ const CACHE_PREFIX = "cache:";
510
+
511
+ /**
512
+ * Numeric release fields of a version string, ignoring prerelease suffixes.
513
+ *
514
+ * A field that is not a plain number becomes 0 rather than throwing, so a
515
+ * version string this does not understand can never sort ABOVE one it does.
516
+ * @param {string} version - A dotted version string.
517
+ * @returns {readonly number[]} Three release fields.
518
+ */
519
+ function releaseFields(version) {
520
+ const parts = String(version).split("-")[0].split("+")[0].split(".");
521
+ return [0, 1, 2].map(index =>
522
+ /^\d+$/u.test(parts[index] ?? "") ? Number(parts[index]) : 0
523
+ );
524
+ }
525
+
526
+ /**
527
+ * Order two versions newest-first. String order is not usable here: `2.9.0`
528
+ * sorts above `2.128.1` lexically and would name the wrong copy.
529
+ * @param {string} a - Left version.
530
+ * @param {string} b - Right version.
531
+ * @returns {number} Negative when `a` is newer.
532
+ */
533
+ function newestFirst(a, b) {
534
+ const left = releaseFields(a);
535
+ const right = releaseFields(b);
536
+ for (let index = 0; index < 3; index += 1) {
537
+ if (left[index] !== right[index]) return right[index] - left[index];
538
+ }
539
+ return 0;
540
+ }
541
+
542
+ /**
543
+ * One rendered finding: the copy, its vintage, and the pattern it matched.
544
+ * @param {{channel: string, path: string, version: string, id: string, label: string, why: string}} finding
545
+ * A finding.
546
+ * @returns {readonly string[]} Report lines.
547
+ */
548
+ function renderFinding(finding) {
549
+ return [
550
+ ` ! ${finding.channel} (lisa ${finding.version})`,
551
+ ` ${finding.path}`,
552
+ ` ${finding.id}: ${finding.label} — ${finding.why}`,
553
+ ];
554
+ }
555
+
556
+ /**
557
+ * Render the sweep for an operator.
558
+ *
559
+ * IN-FORCE findings are printed in full and CACHED ones are summarised, with
560
+ * the newest cached vintage per pattern shown. That split is the whole
561
+ * operator judgement: a developer machine legitimately holds hundreds of
562
+ * superseded copies, so printing all of them buries the one that is serving
563
+ * somebody. The newest cached copy carrying a pattern is the one most likely to
564
+ * still be pinned by a live session, so it is the one named.
565
+ * @param {ReturnType<typeof scanDeployedGuardAdvice>} result - A sweep result.
566
+ * @returns {string} A terminal-first report.
567
+ */
568
+ export function formatDeployedGuardAdviceReport(result) {
569
+ const inForce = result.findings.filter(
570
+ f => !f.channel.startsWith(CACHE_PREFIX)
571
+ );
572
+ const cached = result.findings.filter(f =>
573
+ f.channel.startsWith(CACHE_PREFIX)
574
+ );
575
+ const lines = [
576
+ `Deployed guard advice: ${result.verdict}`,
577
+ `Read ${result.examinedCount} deployed copy(s); ${result.findings.length} carry advice this repository has outlawed.`,
578
+ ];
579
+
580
+ lines.push(`In force now: ${inForce.length} finding(s).`);
581
+ for (const finding of inForce) lines.push(...renderFinding(finding));
582
+
583
+ lines.push(
584
+ `Superseded copies a live session may still be pinned to: ${cached.length} finding(s) across ${new Set(cached.map(f => f.channel)).size} vintage(s).`
585
+ );
586
+ for (const id of new Set(cached.map(f => f.id))) {
587
+ const newest = cached
588
+ .filter(f => f.id === id)
589
+ .sort((a, b) => newestFirst(a.version, b.version))[0];
590
+ lines.push(
591
+ ...renderFinding(newest),
592
+ " (newest cached copy carrying it)"
593
+ );
594
+ }
595
+
596
+ if (result.notMeasured.length > 0) {
597
+ lines.push(
598
+ `${result.notMeasured.length} channel(s) could not be read. This is not a clean fleet — it is an unanswered question.`,
599
+ ...result.notMeasured.map(
600
+ entry => ` ? ${entry.channel}: ${entry.reason}`
601
+ )
602
+ );
603
+ }
604
+ return `${lines.join("\n")}\n`;
605
+ }
606
+
607
+ /**
608
+ * True when `moduleUrl` names the module node was asked to run.
609
+ * @param {string} moduleUrl - The caller's own `import.meta.url`.
610
+ * @param {string | undefined} [argv1] - Entry path.
611
+ * @returns {boolean} Whether to run the CLI body.
612
+ */
613
+ export function invokedAsScript(moduleUrl, argv1 = process.argv[1]) {
614
+ if (!argv1) return false;
615
+ try {
616
+ return realpathSync(argv1) === realpathSync(fileURLToPath(moduleUrl));
617
+ } catch {
618
+ return false;
619
+ }
620
+ }
621
+
622
+ if (invokedAsScript(import.meta.url)) {
623
+ const configDir =
624
+ process.env.CLAUDE_CONFIG_DIR ??
625
+ path.join(process.env.HOME ?? "", ".claude");
626
+ const result = scanDeployedGuardAdvice({
627
+ channels: resolveGuardChannels({
628
+ projectDir: process.cwd(),
629
+ configDir,
630
+ pluginRoot: process.env.CLAUDE_PLUGIN_ROOT,
631
+ guard: process.argv[2] ?? DEFAULT_GUARD,
632
+ }),
633
+ });
634
+ process.stdout.write(formatDeployedGuardAdviceReport(result));
635
+ process.exitCode = deployedGuardAdviceExitCode(result);
636
+ }