@codyswann/lisa 4.22.9 → 4.22.10

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 (272) hide show
  1. package/all/copy-overwrite/scripts/lib/process-tree-runner.mjs +127 -0
  2. package/all/copy-overwrite/scripts/lisa-floor-collisions.mjs +63 -8
  3. package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +36 -6
  4. package/all/copy-overwrite/scripts/lisa-postinstall.mjs +35 -13
  5. package/all/copy-overwrite/scripts/lisa-reconcile-policy.mjs +11 -3
  6. package/all/copy-overwrite/scripts/lisa-run-gates.mjs +41 -8
  7. package/all/copy-overwrite/scripts/lisa-schema-validate.mjs +2 -2
  8. package/all/copy-overwrite/scripts/lisa-work-item.mjs +2 -1
  9. package/cdk/package-lisa/package.lisa.json +1 -1
  10. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  11. package/dist/core/lisa-owned-hash-ledger.js +24 -0
  12. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  13. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  14. package/dist/core/nightly-e2e-guard-behavior-certificate.js.map +1 -1
  15. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  16. package/dist/core/upstream-evidence-manifest.js +59 -50
  17. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  18. package/dist/opencode/plugin-templates/parity-safety-net.sh +36 -6
  19. package/nestjs/package-lisa/package.lisa.json +1 -1
  20. package/package.json +3 -3
  21. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  22. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  23. package/plugins/lisa/.codex-plugin/skills/lisa-atlassian-access/SKILL.md +10 -13
  24. package/plugins/lisa/.codex-plugin/skills/lisa-doctor/SKILL.md +4 -2
  25. package/plugins/lisa/.codex-plugin/skills/lisa-drive-pr-to-merge/SKILL.md +29 -4
  26. package/plugins/lisa/.codex-plugin/skills/lisa-github-sync/SKILL.md +8 -3
  27. package/plugins/lisa/.codex-plugin/skills/lisa-jam-access/SKILL.md +7 -10
  28. package/plugins/lisa/.codex-plugin/skills/lisa-jira-journey/SKILL.md +5 -0
  29. package/plugins/lisa/.codex-plugin/skills/lisa-jira-journey/scripts/parse-plan.py +36 -2
  30. package/plugins/lisa/.codex-plugin/skills/lisa-jira-sync/SKILL.md +1 -1
  31. package/plugins/lisa/.codex-plugin/skills/lisa-linear-access/SKILL.md +8 -12
  32. package/plugins/lisa/.codex-plugin/skills/lisa-linear-sync/SKILL.md +1 -1
  33. package/plugins/lisa/.codex-plugin/skills/lisa-nightly-lower-code-complexity/SKILL.md +7 -1
  34. package/plugins/lisa/.codex-plugin/skills/lisa-notion-access/SKILL.md +7 -10
  35. package/plugins/lisa/.codex-plugin/skills/lisa-posthog-access/SKILL.md +7 -10
  36. package/plugins/lisa/.codex-plugin/skills/lisa-repair-intake/SKILL.md +19 -3
  37. package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +10 -6
  38. package/plugins/lisa/.codex-plugin/skills/lisa-sentry-access/SKILL.md +7 -10
  39. package/plugins/lisa/.codex-plugin/skills/lisa-setup-atlassian/SKILL.md +7 -10
  40. package/plugins/lisa/.codex-plugin/skills/lisa-setup-linear/SKILL.md +9 -12
  41. package/plugins/lisa/.codex-plugin/skills/lisa-setup-notion/SKILL.md +7 -10
  42. package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-aws/SKILL.md +7 -2
  43. package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/SKILL.md +6 -1
  44. package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/assets/session-start.sh +19 -0
  45. package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/assets/setup.sh +1 -0
  46. package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +25 -4
  47. package/plugins/lisa/.codex-plugin/skills/lisa-setup-workstation/scripts/bounded-child.mjs +9 -0
  48. package/plugins/lisa/.codex-plugin/skills/lisa-setup-workstation/scripts/workstation.mjs +29 -21
  49. package/plugins/lisa/.codex-plugin/skills/lisa-sonarcloud-access/SKILL.md +7 -10
  50. package/plugins/lisa/.codex-plugin/skills/lisa-tracker-sync/SKILL.md +1 -1
  51. package/plugins/lisa/hooks/parity-safety-net.sh +36 -6
  52. package/plugins/lisa/hooks/threshold-ratchet-families.mjs +31 -7
  53. package/plugins/lisa/rules/reference/base-rules.md +1 -1
  54. package/plugins/lisa/rules/reference/config-resolution.md +5 -14
  55. package/plugins/lisa/scripts/install-remote-agent-aws.mjs +5 -1
  56. package/plugins/lisa/scripts/rollup-blocker-classification.mjs +18 -3
  57. package/plugins/lisa/skills/lisa-atlassian-access/SKILL.md +10 -13
  58. package/plugins/lisa/skills/lisa-doctor/SKILL.md +4 -2
  59. package/plugins/lisa/skills/lisa-drive-pr-to-merge/SKILL.md +29 -4
  60. package/plugins/lisa/skills/lisa-github-sync/SKILL.md +8 -3
  61. package/plugins/lisa/skills/lisa-jam-access/SKILL.md +7 -10
  62. package/plugins/lisa/skills/lisa-jira-journey/SKILL.md +5 -0
  63. package/plugins/lisa/skills/lisa-jira-journey/scripts/parse-plan.py +36 -2
  64. package/plugins/lisa/skills/lisa-jira-sync/SKILL.md +1 -1
  65. package/plugins/lisa/skills/lisa-linear-access/SKILL.md +8 -12
  66. package/plugins/lisa/skills/lisa-linear-sync/SKILL.md +1 -1
  67. package/plugins/lisa/skills/lisa-nightly-lower-code-complexity/SKILL.md +7 -1
  68. package/plugins/lisa/skills/lisa-notion-access/SKILL.md +7 -10
  69. package/plugins/lisa/skills/lisa-posthog-access/SKILL.md +7 -10
  70. package/plugins/lisa/skills/lisa-repair-intake/SKILL.md +19 -3
  71. package/plugins/lisa/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +10 -6
  72. package/plugins/lisa/skills/lisa-sentry-access/SKILL.md +7 -10
  73. package/plugins/lisa/skills/lisa-setup-atlassian/SKILL.md +7 -10
  74. package/plugins/lisa/skills/lisa-setup-linear/SKILL.md +9 -12
  75. package/plugins/lisa/skills/lisa-setup-notion/SKILL.md +7 -10
  76. package/plugins/lisa/skills/lisa-setup-remote-aws/SKILL.md +7 -2
  77. package/plugins/lisa/skills/lisa-setup-remote-env/SKILL.md +6 -1
  78. package/plugins/lisa/skills/lisa-setup-remote-env/assets/session-start.sh +19 -0
  79. package/plugins/lisa/skills/lisa-setup-remote-env/assets/setup.sh +1 -0
  80. package/plugins/lisa/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +25 -4
  81. package/plugins/lisa/skills/lisa-setup-workstation/scripts/bounded-child.mjs +9 -0
  82. package/plugins/lisa/skills/lisa-setup-workstation/scripts/workstation.mjs +29 -21
  83. package/plugins/lisa/skills/lisa-sonarcloud-access/SKILL.md +7 -10
  84. package/plugins/lisa/skills/lisa-tracker-sync/SKILL.md +1 -1
  85. package/plugins/lisa-agy/hooks/parity-safety-net.sh +36 -6
  86. package/plugins/lisa-agy/plugin.json +1 -1
  87. package/plugins/lisa-agy/scripts/install-remote-agent-aws.mjs +5 -1
  88. package/plugins/lisa-agy/scripts/rollup-blocker-classification.mjs +18 -3
  89. package/plugins/lisa-agy/skills/lisa-atlassian-access/SKILL.md +10 -13
  90. package/plugins/lisa-agy/skills/lisa-doctor/SKILL.md +4 -2
  91. package/plugins/lisa-agy/skills/lisa-drive-pr-to-merge/SKILL.md +29 -4
  92. package/plugins/lisa-agy/skills/lisa-github-sync/SKILL.md +8 -3
  93. package/plugins/lisa-agy/skills/lisa-jam-access/SKILL.md +7 -10
  94. package/plugins/lisa-agy/skills/lisa-jira-journey/SKILL.md +5 -0
  95. package/plugins/lisa-agy/skills/lisa-jira-journey/scripts/parse-plan.py +36 -2
  96. package/plugins/lisa-agy/skills/lisa-jira-sync/SKILL.md +1 -1
  97. package/plugins/lisa-agy/skills/lisa-linear-access/SKILL.md +8 -12
  98. package/plugins/lisa-agy/skills/lisa-linear-sync/SKILL.md +1 -1
  99. package/plugins/lisa-agy/skills/lisa-nightly-lower-code-complexity/SKILL.md +7 -1
  100. package/plugins/lisa-agy/skills/lisa-notion-access/SKILL.md +7 -10
  101. package/plugins/lisa-agy/skills/lisa-posthog-access/SKILL.md +7 -10
  102. package/plugins/lisa-agy/skills/lisa-repair-intake/SKILL.md +19 -3
  103. package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +10 -6
  104. package/plugins/lisa-agy/skills/lisa-sentry-access/SKILL.md +7 -10
  105. package/plugins/lisa-agy/skills/lisa-setup-atlassian/SKILL.md +7 -10
  106. package/plugins/lisa-agy/skills/lisa-setup-linear/SKILL.md +9 -12
  107. package/plugins/lisa-agy/skills/lisa-setup-notion/SKILL.md +7 -10
  108. package/plugins/lisa-agy/skills/lisa-setup-remote-aws/SKILL.md +7 -2
  109. package/plugins/lisa-agy/skills/lisa-setup-remote-env/SKILL.md +6 -1
  110. package/plugins/lisa-agy/skills/lisa-setup-remote-env/assets/session-start.sh +19 -0
  111. package/plugins/lisa-agy/skills/lisa-setup-remote-env/assets/setup.sh +1 -0
  112. package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +25 -4
  113. package/plugins/lisa-agy/skills/lisa-setup-workstation/scripts/bounded-child.mjs +9 -0
  114. package/plugins/lisa-agy/skills/lisa-setup-workstation/scripts/workstation.mjs +29 -21
  115. package/plugins/lisa-agy/skills/lisa-sonarcloud-access/SKILL.md +7 -10
  116. package/plugins/lisa-agy/skills/lisa-tracker-sync/SKILL.md +1 -1
  117. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  118. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  119. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  120. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  121. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  122. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  123. package/plugins/lisa-copilot/hooks/parity-safety-net.sh +36 -6
  124. package/plugins/lisa-copilot/hooks/threshold-ratchet-families.mjs +31 -7
  125. package/plugins/lisa-copilot/rules/reference/base-rules.md +1 -1
  126. package/plugins/lisa-copilot/rules/reference/config-resolution.md +5 -14
  127. package/plugins/lisa-copilot/scripts/install-remote-agent-aws.mjs +5 -1
  128. package/plugins/lisa-copilot/scripts/rollup-blocker-classification.mjs +18 -3
  129. package/plugins/lisa-copilot/skills/lisa-atlassian-access/SKILL.md +10 -13
  130. package/plugins/lisa-copilot/skills/lisa-doctor/SKILL.md +4 -2
  131. package/plugins/lisa-copilot/skills/lisa-drive-pr-to-merge/SKILL.md +29 -4
  132. package/plugins/lisa-copilot/skills/lisa-github-sync/SKILL.md +8 -3
  133. package/plugins/lisa-copilot/skills/lisa-jam-access/SKILL.md +7 -10
  134. package/plugins/lisa-copilot/skills/lisa-jira-journey/SKILL.md +5 -0
  135. package/plugins/lisa-copilot/skills/lisa-jira-journey/scripts/parse-plan.py +36 -2
  136. package/plugins/lisa-copilot/skills/lisa-jira-sync/SKILL.md +1 -1
  137. package/plugins/lisa-copilot/skills/lisa-linear-access/SKILL.md +8 -12
  138. package/plugins/lisa-copilot/skills/lisa-linear-sync/SKILL.md +1 -1
  139. package/plugins/lisa-copilot/skills/lisa-nightly-lower-code-complexity/SKILL.md +7 -1
  140. package/plugins/lisa-copilot/skills/lisa-notion-access/SKILL.md +7 -10
  141. package/plugins/lisa-copilot/skills/lisa-posthog-access/SKILL.md +7 -10
  142. package/plugins/lisa-copilot/skills/lisa-repair-intake/SKILL.md +19 -3
  143. package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +10 -6
  144. package/plugins/lisa-copilot/skills/lisa-sentry-access/SKILL.md +7 -10
  145. package/plugins/lisa-copilot/skills/lisa-setup-atlassian/SKILL.md +7 -10
  146. package/plugins/lisa-copilot/skills/lisa-setup-linear/SKILL.md +9 -12
  147. package/plugins/lisa-copilot/skills/lisa-setup-notion/SKILL.md +7 -10
  148. package/plugins/lisa-copilot/skills/lisa-setup-remote-aws/SKILL.md +7 -2
  149. package/plugins/lisa-copilot/skills/lisa-setup-remote-env/SKILL.md +6 -1
  150. package/plugins/lisa-copilot/skills/lisa-setup-remote-env/assets/session-start.sh +19 -0
  151. package/plugins/lisa-copilot/skills/lisa-setup-remote-env/assets/setup.sh +1 -0
  152. package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +25 -4
  153. package/plugins/lisa-copilot/skills/lisa-setup-workstation/scripts/bounded-child.mjs +9 -0
  154. package/plugins/lisa-copilot/skills/lisa-setup-workstation/scripts/workstation.mjs +29 -21
  155. package/plugins/lisa-copilot/skills/lisa-sonarcloud-access/SKILL.md +7 -10
  156. package/plugins/lisa-copilot/skills/lisa-tracker-sync/SKILL.md +1 -1
  157. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  158. package/plugins/lisa-cursor/hooks/parity-safety-net.sh +36 -6
  159. package/plugins/lisa-cursor/hooks/threshold-ratchet-families.mjs +31 -7
  160. package/plugins/lisa-cursor/rules/base-rules-reference.mdc +1 -1
  161. package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +5 -14
  162. package/plugins/lisa-cursor/scripts/install-remote-agent-aws.mjs +5 -1
  163. package/plugins/lisa-cursor/scripts/rollup-blocker-classification.mjs +18 -3
  164. package/plugins/lisa-cursor/skills/lisa-atlassian-access/SKILL.md +10 -13
  165. package/plugins/lisa-cursor/skills/lisa-doctor/SKILL.md +4 -2
  166. package/plugins/lisa-cursor/skills/lisa-drive-pr-to-merge/SKILL.md +29 -4
  167. package/plugins/lisa-cursor/skills/lisa-github-sync/SKILL.md +8 -3
  168. package/plugins/lisa-cursor/skills/lisa-jam-access/SKILL.md +7 -10
  169. package/plugins/lisa-cursor/skills/lisa-jira-journey/SKILL.md +5 -0
  170. package/plugins/lisa-cursor/skills/lisa-jira-journey/scripts/parse-plan.py +36 -2
  171. package/plugins/lisa-cursor/skills/lisa-jira-sync/SKILL.md +1 -1
  172. package/plugins/lisa-cursor/skills/lisa-linear-access/SKILL.md +8 -12
  173. package/plugins/lisa-cursor/skills/lisa-linear-sync/SKILL.md +1 -1
  174. package/plugins/lisa-cursor/skills/lisa-nightly-lower-code-complexity/SKILL.md +7 -1
  175. package/plugins/lisa-cursor/skills/lisa-notion-access/SKILL.md +7 -10
  176. package/plugins/lisa-cursor/skills/lisa-posthog-access/SKILL.md +7 -10
  177. package/plugins/lisa-cursor/skills/lisa-repair-intake/SKILL.md +19 -3
  178. package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +10 -6
  179. package/plugins/lisa-cursor/skills/lisa-sentry-access/SKILL.md +7 -10
  180. package/plugins/lisa-cursor/skills/lisa-setup-atlassian/SKILL.md +7 -10
  181. package/plugins/lisa-cursor/skills/lisa-setup-linear/SKILL.md +9 -12
  182. package/plugins/lisa-cursor/skills/lisa-setup-notion/SKILL.md +7 -10
  183. package/plugins/lisa-cursor/skills/lisa-setup-remote-aws/SKILL.md +7 -2
  184. package/plugins/lisa-cursor/skills/lisa-setup-remote-env/SKILL.md +6 -1
  185. package/plugins/lisa-cursor/skills/lisa-setup-remote-env/assets/session-start.sh +19 -0
  186. package/plugins/lisa-cursor/skills/lisa-setup-remote-env/assets/setup.sh +1 -0
  187. package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +25 -4
  188. package/plugins/lisa-cursor/skills/lisa-setup-workstation/scripts/bounded-child.mjs +9 -0
  189. package/plugins/lisa-cursor/skills/lisa-setup-workstation/scripts/workstation.mjs +29 -21
  190. package/plugins/lisa-cursor/skills/lisa-sonarcloud-access/SKILL.md +7 -10
  191. package/plugins/lisa-cursor/skills/lisa-tracker-sync/SKILL.md +1 -1
  192. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  193. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  194. package/plugins/lisa-expo-agy/plugin.json +1 -1
  195. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  196. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  197. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  198. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  199. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  200. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  201. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  202. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  203. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  204. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  205. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  206. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  207. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  208. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  209. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  210. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  211. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  212. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  213. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  214. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  215. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  216. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  217. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  218. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  219. package/plugins/lisa-rails-agy/plugin.json +1 -1
  220. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  221. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  222. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  223. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  224. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  225. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  226. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  227. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  228. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  229. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  230. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  231. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  232. package/plugins/src/base/hooks/parity-safety-net.sh +36 -6
  233. package/plugins/src/base/hooks/threshold-ratchet-families.mjs +31 -7
  234. package/plugins/src/base/rules/reference/base-rules.md +1 -1
  235. package/plugins/src/base/rules/reference/config-resolution.md +5 -14
  236. package/plugins/src/base/scripts/install-remote-agent-aws.mjs +5 -1
  237. package/plugins/src/base/scripts/rollup-blocker-classification.mjs +18 -3
  238. package/plugins/src/base/skills/lisa-atlassian-access/SKILL.md +10 -13
  239. package/plugins/src/base/skills/lisa-doctor/SKILL.md +4 -2
  240. package/plugins/src/base/skills/lisa-drive-pr-to-merge/SKILL.md +29 -4
  241. package/plugins/src/base/skills/lisa-github-sync/SKILL.md +8 -3
  242. package/plugins/src/base/skills/lisa-jam-access/SKILL.md +7 -10
  243. package/plugins/src/base/skills/lisa-jira-journey/SKILL.md +5 -0
  244. package/plugins/src/base/skills/lisa-jira-journey/scripts/parse-plan.py +36 -2
  245. package/plugins/src/base/skills/lisa-jira-sync/SKILL.md +1 -1
  246. package/plugins/src/base/skills/lisa-linear-access/SKILL.md +8 -12
  247. package/plugins/src/base/skills/lisa-linear-sync/SKILL.md +1 -1
  248. package/plugins/src/base/skills/lisa-nightly-lower-code-complexity/SKILL.md +7 -1
  249. package/plugins/src/base/skills/lisa-notion-access/SKILL.md +7 -10
  250. package/plugins/src/base/skills/lisa-posthog-access/SKILL.md +7 -10
  251. package/plugins/src/base/skills/lisa-repair-intake/SKILL.md +19 -3
  252. package/plugins/src/base/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +10 -6
  253. package/plugins/src/base/skills/lisa-sentry-access/SKILL.md +7 -10
  254. package/plugins/src/base/skills/lisa-setup-atlassian/SKILL.md +7 -10
  255. package/plugins/src/base/skills/lisa-setup-linear/SKILL.md +9 -12
  256. package/plugins/src/base/skills/lisa-setup-notion/SKILL.md +7 -10
  257. package/plugins/src/base/skills/lisa-setup-remote-aws/SKILL.md +7 -2
  258. package/plugins/src/base/skills/lisa-setup-remote-env/SKILL.md +6 -1
  259. package/plugins/src/base/skills/lisa-setup-remote-env/assets/session-start.sh +19 -0
  260. package/plugins/src/base/skills/lisa-setup-remote-env/assets/setup.sh +1 -0
  261. package/plugins/src/base/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +25 -4
  262. package/plugins/src/base/skills/lisa-setup-workstation/scripts/bounded-child.mjs +9 -0
  263. package/plugins/src/base/skills/lisa-setup-workstation/scripts/workstation.mjs +29 -21
  264. package/plugins/src/base/skills/lisa-sonarcloud-access/SKILL.md +7 -10
  265. package/plugins/src/base/skills/lisa-tracker-sync/SKILL.md +1 -1
  266. package/rails/copy-overwrite/scripts/threshold-ratchet-families.mjs +31 -7
  267. package/scripts/lisa-remote-env/session-start.sh +19 -0
  268. package/scripts/lisa-remote-env/setup.sh +1 -0
  269. package/scripts/two-channel-couplings.json +1 -1
  270. package/typescript/copy-contents/.husky/pre-push +1 -1
  271. package/typescript/copy-overwrite/scripts/threshold-ratchet-families.mjs +31 -7
  272. package/typescript/package-lisa/package.lisa.json +1 -1
@@ -161,12 +161,16 @@ function stripFamilyBlocks(text, family) {
161
161
  const opened = findFamily(out, start);
162
162
  if (opened.index === -1) return out;
163
163
  const closed = findFamily(out, end, opened.index + opened.length);
164
- // A truncated block (opened, never closed) would otherwise swallow the rest
165
- // of the file on every subsequent write.
166
- const after =
167
- closed.index === -1
168
- ? ""
169
- : out.slice(closed.index + closed.length).replace(/^\n/, "");
164
+ // A truncated block (opened, never closed) is not safe to interpret. Any
165
+ // guessed boundary could delete operator-authored content after the marker,
166
+ // so refuse before the caller writes and preserve the original byte-for-byte.
167
+ if (closed.index === -1) {
168
+ throw new Error(
169
+ `Refusing to rewrite a managed ${family} block with no closing marker. ` +
170
+ `Repair the marker pair first; no file was changed.`
171
+ );
172
+ }
173
+ const after = out.slice(closed.index + closed.length).replace(/^\n/, "");
170
174
  out = `${out.slice(0, opened.index)}${after}`;
171
175
  }
172
176
  }
@@ -48,9 +48,9 @@ interactively and headlessly — which is why it leads. The REST tier uses:
48
48
  read_sentry_token() {
49
49
  [ -n "${SENTRY_AUTH_TOKEN:-}" ] && { echo "$SENTRY_AUTH_TOKEN"; return; }
50
50
  #
51
- # Ordered repo-first, ending at the plugin's own copy. Repo-relative rungs
52
- # lead because a project that vendors the resolver has declared which copy it
53
- # wants used, and that decision must survive this change untouched. The plugin
51
+ # Ordered across trusted machine-managed substrates, ending at the installed
52
+ # package. Checkout-local paths are deliberately absent: a familiar generated
53
+ # destination is still repository-controlled executable code. The plugin
54
54
  # rungs are the floor: `resolve-secret.mjs` ships beside this skill, so a rung
55
55
  # pointing at it is reachable from anywhere the plugin itself is installed.
56
56
  # Without one, a consumer repository that vendors none of the leading paths
@@ -59,12 +59,9 @@ read_sentry_token() {
59
59
  # lookups. This LADDER is identical in every skill that resolves a credential
60
60
  # and `credential-resolver-ladder` fails if any copy diverges. Only what
61
61
  # happens AFTER the ladder may differ between them.
62
- local candidates=(
63
- .claude/skills/lisa-secrets-access/scripts/resolve-secret.mjs
64
- .agents/skills/lisa-secrets-access/scripts/resolve-secret.mjs
65
- .opencode/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
66
- .codex/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
67
- )
62
+ # Execute only machine-managed plugin/package resolvers. Checkout-local
63
+ # candidates are repository-controlled code, not trusted merely by path.
64
+ local candidates=()
68
65
  if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then
69
66
  candidates+=("$CLAUDE_PLUGIN_ROOT/skills/lisa-secrets-access/scripts/resolve-secret.mjs")
70
67
  fi
@@ -83,7 +80,7 @@ read_sentry_token() {
83
80
  local via_lisa
84
81
  via_lisa=$(node "$resolver" get SENTRY_AUTH_TOKEN 2>/dev/null) \
85
82
  && [ -n "$via_lisa" ] && { echo "$via_lisa"; return; }
86
- break
83
+ # Empty/error means this substrate had no answer; try the next trusted one.
87
84
  fi
88
85
  done
89
86
  # Name every path. A bare `return 1` sends the next reader hunting for a
@@ -86,9 +86,9 @@ read_token() {
86
86
  # than "this function" is deliberate — the old wording asked for something
87
87
  # unachievable, and an unachievable rule is one that gets quietly dropped.
88
88
  #
89
- # Ordered repo-first, ending at the plugin's own copy. Repo-relative rungs
90
- # lead because a project that vendors the resolver has declared which copy it
91
- # wants used, and that decision must survive this change untouched. The plugin
89
+ # Ordered across trusted machine-managed substrates, ending at the installed
90
+ # package. Checkout-local paths are deliberately absent: a familiar generated
91
+ # destination is still repository-controlled executable code. The plugin
92
92
  # rungs are the floor: `resolve-secret.mjs` ships beside this skill, so a rung
93
93
  # pointing at it is reachable from anywhere the plugin itself is installed.
94
94
  # Without one, a consumer repository that vendors none of the leading paths
@@ -97,12 +97,9 @@ read_token() {
97
97
  # lookups. This LADDER is identical in every skill that resolves a credential
98
98
  # and `credential-resolver-ladder` fails if any copy diverges. Only what
99
99
  # happens AFTER the ladder may differ between them.
100
- local candidates=(
101
- .claude/skills/lisa-secrets-access/scripts/resolve-secret.mjs
102
- .agents/skills/lisa-secrets-access/scripts/resolve-secret.mjs
103
- .opencode/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
104
- .codex/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
105
- )
100
+ # Execute only machine-managed plugin/package resolvers. Checkout-local
101
+ # candidates are repository-controlled code, not trusted merely by path.
102
+ local candidates=()
106
103
  if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then
107
104
  candidates+=("$CLAUDE_PLUGIN_ROOT/skills/lisa-secrets-access/scripts/resolve-secret.mjs")
108
105
  fi
@@ -121,7 +118,7 @@ read_token() {
121
118
  local via_lisa
122
119
  via_lisa=$(node "$resolver" get ATLASSIAN_API_TOKEN 2>/dev/null) \
123
120
  && [ -n "$via_lisa" ] && { echo "$via_lisa"; return; }
124
- break
121
+ # Empty/error means this substrate had no answer; try the next trusted one.
125
122
  fi
126
123
  done
127
124
  # Legacy fallback: the OS keychain written by the guided flow below, for
@@ -104,17 +104,14 @@ read_linear_key() { # $1=workspace slug
104
104
  # `linear-access` grew to seven, leaving `/lisa:setup:linear` unable to reach
105
105
  # a key that `lisa-linear-access` could read from the same repository.
106
106
  #
107
- # Ordered repo-first, ending at the plugin's own copy. Repo-relative rungs
108
- # lead because a project that vendors the resolver has declared which copy it
109
- # wants used. The plugin rungs are the floor: `resolve-secret.mjs` ships
110
- # beside this skill, so a rung pointing at it is reachable from anywhere the
111
- # plugin itself is installed.
112
- local candidates=(
113
- .claude/skills/lisa-secrets-access/scripts/resolve-secret.mjs
114
- .agents/skills/lisa-secrets-access/scripts/resolve-secret.mjs
115
- .opencode/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
116
- .codex/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
117
- )
107
+ # Ordered across trusted machine-managed substrates, ending at the installed
108
+ # package. Checkout-local paths are deliberately absent: a familiar generated
109
+ # destination is still repository-controlled executable code. The plugin
110
+ # rungs are the floor: `resolve-secret.mjs` ships beside this skill, so a rung
111
+ # pointing at it is reachable from anywhere the plugin itself is installed.
112
+ # Execute only machine-managed plugin/package resolvers. Checkout-local
113
+ # candidates are repository-controlled code, not trusted merely by path.
114
+ local candidates=()
118
115
  if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then
119
116
  candidates+=("$CLAUDE_PLUGIN_ROOT/skills/lisa-secrets-access/scripts/resolve-secret.mjs")
120
117
  fi
@@ -133,7 +130,7 @@ read_linear_key() { # $1=workspace slug
133
130
  local via_lisa
134
131
  via_lisa=$(node "$resolver" get LINEAR_API_KEY 2>/dev/null) \
135
132
  && [ -n "$via_lisa" ] && { echo "$via_lisa"; return; }
136
- break
133
+ # Empty/error means this substrate had no answer; try the next trusted one.
137
134
  fi
138
135
  done
139
136
  # Legacy fallback: the OS keychain written by the guided flow below, for
@@ -173,9 +173,9 @@ read_notion_token() {
173
173
  # than "this function" is deliberate — the old wording asked for something
174
174
  # unachievable, and an unachievable rule is one that gets quietly dropped.
175
175
  #
176
- # Ordered repo-first, ending at the plugin's own copy. Repo-relative rungs
177
- # lead because a project that vendors the resolver has declared which copy it
178
- # wants used, and that decision must survive this change untouched. The plugin
176
+ # Ordered across trusted machine-managed substrates, ending at the installed
177
+ # package. Checkout-local paths are deliberately absent: a familiar generated
178
+ # destination is still repository-controlled executable code. The plugin
179
179
  # rungs are the floor: `resolve-secret.mjs` ships beside this skill, so a rung
180
180
  # pointing at it is reachable from anywhere the plugin itself is installed.
181
181
  # Without one, a consumer repository that vendors none of the leading paths
@@ -184,12 +184,9 @@ read_notion_token() {
184
184
  # lookups. This LADDER is identical in every skill that resolves a credential
185
185
  # and `credential-resolver-ladder` fails if any copy diverges. Only what
186
186
  # happens AFTER the ladder may differ between them.
187
- local candidates=(
188
- .claude/skills/lisa-secrets-access/scripts/resolve-secret.mjs
189
- .agents/skills/lisa-secrets-access/scripts/resolve-secret.mjs
190
- .opencode/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
191
- .codex/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
192
- )
187
+ # Execute only machine-managed plugin/package resolvers. Checkout-local
188
+ # candidates are repository-controlled code, not trusted merely by path.
189
+ local candidates=()
193
190
  if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then
194
191
  candidates+=("$CLAUDE_PLUGIN_ROOT/skills/lisa-secrets-access/scripts/resolve-secret.mjs")
195
192
  fi
@@ -208,7 +205,7 @@ read_notion_token() {
208
205
  local via_lisa
209
206
  via_lisa=$(node "$resolver" get NOTION_API_TOKEN 2>/dev/null) \
210
207
  && [ -n "$via_lisa" ] && { echo "$via_lisa"; return; }
211
- break
208
+ # Empty/error means this substrate had no answer; try the next trusted one.
212
209
  fi
213
210
  done
214
211
  # Legacy fallback: the OS keychain written by the guided flow below, for
@@ -64,13 +64,18 @@ The lock remains held through rollback and its deletion is verified before the
64
64
  command reports success:
65
65
 
66
66
  ```bash
67
- aws --profile shared secretsmanager get-secret-value \
68
- --secret-id remote-agent-credentials \
67
+ aws secretsmanager get-secret-value \
68
+ --secret-id <selected-secret-name> \
69
69
  --query SecretString \
70
70
  --output text \
71
71
  | node .claude/skills/lisa-secrets-access/scripts/publish-aws-bootstrap.mjs publish
72
72
  ```
73
73
 
74
+ Use the same secret name passed to `--secret-name`; the default is
75
+ `remote-agent-credentials`. Add `--profile <source-profile>` only when the
76
+ operator's ambient AWS identity cannot read the emission and the project names
77
+ a source profile. A named `shared` profile is not otherwise required.
78
+
74
79
  Resolve the script from the installed runtime or `node_modules` when the
75
80
  `.claude/skills` copy is not present. Never print or place the candidate in an
76
81
  argument or temporary file. A CDK deployment followed later by a human copy is
@@ -97,9 +97,14 @@ A surface that **re-runs this script when a container resumes** — Codex Cloud
97
97
  A surface that **skips this script whenever a filesystem cache exists** — Claude Code web — must not. Materializing here would write the values once and then never refresh them, so a credential rotated on Tuesday would still be serving Monday's value until the cache expired days later. Those surfaces materialize from a session-start hook instead, which runs every session including a resumed one:
98
98
 
99
99
  ```sh
100
- bash scripts/lisa-remote-env/session-start.sh # guard; reruns toolchain, secrets, then hook
100
+ bash "$CLAUDE_PROJECT_DIR/scripts/lisa-remote-env/session-start.sh" # project-scoped absolute hook
101
101
  ```
102
102
 
103
+ The installer also registers a user-scoped hook using the checkout's resolved
104
+ absolute path. If invoking this asset by hand, either use that absolute path or
105
+ change to the repository root first; a bare checkout-relative command is not a
106
+ portable session-start configuration.
107
+
103
108
  The selection comes from the surface's `materializeAt` capability in `lisa-secrets-access`, not from its name, so adding a surface does not mean editing a branch. The hook is committed to the repository, so it also fires on a developer's machine — it exits `0` immediately there rather than failing, because a correct local session must not look broken.
104
109
 
105
110
  ## Detect before you provision
@@ -46,8 +46,27 @@ here="$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd)"
46
46
  # SessionStart hosts are fail-open, so they surface that result and still allow
47
47
  # the agent session to continue.
48
48
  bash "${here}/setup.sh" --phase=toolchain "$@"
49
+
50
+ # The toolchain child cannot export into this parent shell. Its durable output
51
+ # is linked into ~/.local/bin, so make that directory visible before the next
52
+ # child tries to execute the provider CLI it just installed.
53
+ case ":$PATH:" in
54
+ *":$HOME/.local/bin:"*) ;;
55
+ *) PATH="$HOME/.local/bin:$PATH"; export PATH ;;
56
+ esac
57
+
49
58
  bash "${here}/setup.sh" --phase=secrets "$@"
50
59
 
60
+ # Materialization writes a Lisa-owned block into .profile that sources the
61
+ # generated environment and removes ambient AWS credential poison. The project
62
+ # hook is another non-login child, so load that block here and let the child
63
+ # inherit the exact environment materialization just produced.
64
+ if [ -r "$HOME/.profile" ]; then
65
+ # The materializer owns this runtime path.
66
+ # shellcheck disable=SC1090
67
+ . "$HOME/.profile"
68
+ fi
69
+
51
70
  # A project hook is part of the remote toolchain contract too. It covers tools
52
71
  # that cannot be expressed by the pinned manifest, so skipping it on a cached
53
72
  # session can leave fresh credentials with no client capable of using them.
@@ -132,6 +132,7 @@ for candidate in \
132
132
  ".claude/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs" \
133
133
  ".agents/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs" \
134
134
  ".codex/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs" \
135
+ ".opencode/skills/lisa/lisa-setup-remote-env/scripts/setup-remote-env.mjs" \
135
136
  "plugins/lisa/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs" \
136
137
  "node_modules/@codyswann/lisa/plugins/lisa/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs"; do
137
138
  if [ -f "$candidate" ]; then
@@ -51,7 +51,10 @@ import { pathDirs } from "../../lisa-setup-workstation/scripts/catalogue.mjs";
51
51
 
52
52
  import { assertPinned, extractVersion, planToolchain } from "./toolchain.mjs";
53
53
 
54
- import { boundedChildOutput } from "../../lisa-setup-workstation/scripts/bounded-child.mjs";
54
+ import {
55
+ boundedChildOutput,
56
+ SETUP_OPERATION_BUDGET_MS,
57
+ } from "../../lisa-setup-workstation/scripts/bounded-child.mjs";
55
58
 
56
59
  const HERE = dirname(fileURLToPath(import.meta.url));
57
60
 
@@ -173,12 +176,14 @@ function installReleaseZip(tool, binDir) {
173
176
  const archive = join(temporary, "download.zip");
174
177
  boundedChildOutput("curl", ["-fsSL", tool.url, "-o", archive], {
175
178
  stdio: "inherit",
179
+ timeout: SETUP_OPERATION_BUDGET_MS,
176
180
  });
177
181
  // Verify before unpacking, not after. An unexpected archive must fail
178
182
  // before any of its contents reach a directory that is on PATH.
179
183
  verifyChecksum(archive, tool.sha256, tool.name);
180
184
  boundedChildOutput("unzip", ["-q", "-o", archive, "-d", temporary], {
181
185
  stdio: "inherit",
186
+ timeout: SETUP_OPERATION_BUDGET_MS,
182
187
  });
183
188
  const binary = join(temporary, tool.binary ?? tool.name);
184
189
  boundedChildOutput(
@@ -186,6 +191,7 @@ function installReleaseZip(tool, binDir) {
186
191
  ["-m", "0755", binary, join(binDir, tool.name)],
187
192
  {
188
193
  stdio: "inherit",
194
+ timeout: SETUP_OPERATION_BUDGET_MS,
189
195
  }
190
196
  );
191
197
  } finally {
@@ -210,10 +216,12 @@ function installReleaseTar(tool, binDir) {
210
216
  const archive = join(temporary, "download.tar.gz");
211
217
  boundedChildOutput("curl", ["-fsSL", tool.url, "-o", archive], {
212
218
  stdio: "inherit",
219
+ timeout: SETUP_OPERATION_BUDGET_MS,
213
220
  });
214
221
  verifyChecksum(archive, tool.sha256, tool.name);
215
222
  boundedChildOutput("tar", ["-xzf", archive, "-C", temporary], {
216
223
  stdio: "inherit",
224
+ timeout: SETUP_OPERATION_BUDGET_MS,
217
225
  });
218
226
  // Release tarballs usually nest under a versioned directory, so `binary` is
219
227
  // a path within the archive rather than a bare name.
@@ -223,6 +231,7 @@ function installReleaseTar(tool, binDir) {
223
231
  ["-m", "0755", binary, join(binDir, tool.name)],
224
232
  {
225
233
  stdio: "inherit",
234
+ timeout: SETUP_OPERATION_BUDGET_MS,
226
235
  }
227
236
  );
228
237
  } finally {
@@ -272,6 +281,7 @@ function installReleaseTree(tool, binDir) {
272
281
  const archive = join(temporary, "download.archive");
273
282
  boundedChildOutput("curl", ["-fsSL", tool.url, "-o", archive], {
274
283
  stdio: "inherit",
284
+ timeout: SETUP_OPERATION_BUDGET_MS,
275
285
  });
276
286
  // Verify before unpacking, as everywhere else here: an unexpected archive
277
287
  // must fail before any of its contents reach the filesystem.
@@ -285,10 +295,12 @@ function installReleaseTree(tool, binDir) {
285
295
  if (tool.url.endsWith(".zip")) {
286
296
  boundedChildOutput("unzip", ["-q", "-o", archive, "-d", prefix], {
287
297
  stdio: "inherit",
298
+ timeout: SETUP_OPERATION_BUDGET_MS,
288
299
  });
289
300
  } else {
290
301
  boundedChildOutput("tar", ["-xzf", archive, "-C", prefix], {
291
302
  stdio: "inherit",
303
+ timeout: SETUP_OPERATION_BUDGET_MS,
292
304
  });
293
305
  }
294
306
 
@@ -342,6 +354,7 @@ function installReleaseBinary(tool, binDir) {
342
354
  const artifact = join(temporary, tool.name);
343
355
  boundedChildOutput("curl", ["-fsSL", tool.url, "-o", artifact], {
344
356
  stdio: "inherit",
357
+ timeout: SETUP_OPERATION_BUDGET_MS,
345
358
  });
346
359
  // Verify before it reaches a directory on PATH. For this kind that ordering
347
360
  // matters more than for the archives: the downloaded file is directly
@@ -353,6 +366,7 @@ function installReleaseBinary(tool, binDir) {
353
366
  ["-m", "0755", artifact, join(binDir, tool.name)],
354
367
  {
355
368
  stdio: "inherit",
369
+ timeout: SETUP_OPERATION_BUDGET_MS,
356
370
  }
357
371
  );
358
372
  } finally {
@@ -370,6 +384,7 @@ function installNpmGlobal(tool) {
370
384
  ["install", "--global", `${tool.package}@${tool.version}`],
371
385
  {
372
386
  stdio: "inherit",
387
+ timeout: SETUP_OPERATION_BUDGET_MS,
373
388
  }
374
389
  );
375
390
  }
@@ -509,7 +524,10 @@ function runHook(hook, dryRun) {
509
524
  console.log(`\nProject hook: ${hook}`);
510
525
  if (dryRun) return;
511
526
  chmodSync(path, 0o755);
512
- boundedChildOutput("bash", [path], { stdio: "inherit" });
527
+ boundedChildOutput("bash", [path], {
528
+ stdio: "inherit",
529
+ timeout: SETUP_OPERATION_BUDGET_MS,
530
+ });
513
531
  }
514
532
 
515
533
  /** Phases this runner can execute, in the order they must happen. */
@@ -789,7 +807,10 @@ export function installUserSessionHook(repoRoot, options = {}) {
789
807
 
790
808
  const dir = join(home, ".claude");
791
809
  const settingsPath = join(dir, "settings.json");
792
- const command = `bash ${script}`;
810
+ // Settings stores shell source, so the absolute path still needs quoting.
811
+ // JSON string syntax is accepted by the shell for ordinary path text and
812
+ // safely escapes spaces, quotes, dollars and backticks.
813
+ const command = `bash ${JSON.stringify(script)}`;
793
814
 
794
815
  let settings = {};
795
816
  if (exists(settingsPath)) {
@@ -1506,7 +1527,7 @@ async function main() {
1506
1527
  boundedChildOutput(
1507
1528
  "node",
1508
1529
  dryRun ? [materialize, "--dry-run"] : [materialize],
1509
- { stdio: "inherit" }
1530
+ { stdio: "inherit", timeout: SETUP_OPERATION_BUDGET_MS }
1510
1531
  );
1511
1532
  } catch (err) {
1512
1533
  if (!retried) throw err;
@@ -83,6 +83,15 @@ import { execFileSync, spawnSync } from "node:child_process";
83
83
  */
84
84
  export const CHILD_BUDGET_MS = 30_000;
85
85
 
86
+ /**
87
+ * Deadline for setup operations that legitimately include downloads, package
88
+ * installation, secret-provider calls, or a project-declared hook.
89
+ *
90
+ * Kept separate from {@link CHILD_BUDGET_MS}: probes should still fail quickly,
91
+ * while a cold network install must not be killed by the probe budget.
92
+ */
93
+ export const SETUP_OPERATION_BUDGET_MS = 10 * 60_000;
94
+
86
95
  /**
87
96
  * Whether this value is a child that was killed at its deadline.
88
97
  *
@@ -36,7 +36,10 @@
36
36
  import { existsSync, lstatSync, mkdirSync, rmSync, symlinkSync } from "node:fs";
37
37
  import { join } from "node:path";
38
38
 
39
- import { boundedChildOutput } from "./bounded-child.mjs";
39
+ import {
40
+ boundedChildOutput,
41
+ SETUP_OPERATION_BUDGET_MS,
42
+ } from "./bounded-child.mjs";
40
43
 
41
44
  import {
42
45
  AGENTS,
@@ -387,7 +390,9 @@ export function installEntry(entry, row, deps = {}) {
387
390
 
388
391
  try {
389
392
  if (entry.kind === "npm-global") {
390
- run("npm", ["install", "-g", entry.package]);
393
+ run("npm", ["install", "-g", entry.package], {
394
+ timeout: SETUP_OPERATION_BUDGET_MS,
395
+ });
391
396
  } else if (entry.kind === "vendor-script") {
392
397
  if (!entry.script) {
393
398
  return {
@@ -410,12 +415,11 @@ export function installEntry(entry, row, deps = {}) {
410
415
  // exit status is the SHELL's, not curl's, so a 404 feeds an empty script
411
416
  // to bash, bash exits 0, and a tool that was never installed is reported
412
417
  // as installed. That is exactly what `sonarsource.com/install` did.
413
- run("bash", [
414
- "-o",
415
- "pipefail",
416
- "-c",
417
- `curl -fsSL ${entry.script} | bash`,
418
- ]);
418
+ run(
419
+ "bash",
420
+ ["-o", "pipefail", "-c", `curl -fsSL ${entry.script} | bash`],
421
+ { timeout: SETUP_OPERATION_BUDGET_MS }
422
+ );
419
423
  } else if (entry.kind === "aws-cli") {
420
424
  // macOS ships a .pkg that wants an administrator, which a headless run
421
425
  // has no business prompting for. Reported as manual with the one-line
@@ -436,20 +440,24 @@ export function installEntry(entry, row, deps = {}) {
436
440
  const url = `https://awscli.amazonaws.com/awscli-exe-linux-${
437
441
  process.arch === "arm64" ? "aarch64" : "x86_64"
438
442
  }.zip`;
439
- run("bash", [
440
- "-o",
441
- "pipefail",
442
- "-c",
443
+ run(
444
+ "bash",
443
445
  [
444
- "set -e",
445
- 'tmp="$(mktemp -d)"',
446
- `curl -fsSL "${url}" -o "$tmp/awscli.zip"`,
447
- 'unzip -q "$tmp/awscli.zip" -d "$tmp"',
448
- `"$tmp/aws/install" --install-dir "${home}/.local/aws-cli" ` +
449
- `--bin-dir "${home}/.local/bin" --update`,
450
- 'rm -rf "$tmp"',
451
- ].join("\n"),
452
- ]);
446
+ "-o",
447
+ "pipefail",
448
+ "-c",
449
+ [
450
+ "set -e",
451
+ 'tmp="$(mktemp -d)"',
452
+ `curl -fsSL "${url}" -o "$tmp/awscli.zip"`,
453
+ 'unzip -q "$tmp/awscli.zip" -d "$tmp"',
454
+ `"$tmp/aws/install" --install-dir "${home}/.local/aws-cli" ` +
455
+ `--bin-dir "${home}/.local/bin" --update`,
456
+ 'rm -rf "$tmp"',
457
+ ].join("\n"),
458
+ ],
459
+ { timeout: SETUP_OPERATION_BUDGET_MS }
460
+ );
453
461
  } else if (CHECKSUMMED.has(entry.kind)) {
454
462
  // The pinned, checksummed path belongs to the remote-env installer. It is
455
463
  // borrowed rather than reimplemented so the pins and checksums cannot
@@ -63,9 +63,9 @@ read_sonar_secret() {
63
63
  local name="$1"
64
64
  [ -n "${!name:-}" ] && { echo "${!name}"; return; }
65
65
  #
66
- # Ordered repo-first, ending at the plugin's own copy. Repo-relative rungs
67
- # lead because a project that vendors the resolver has declared which copy it
68
- # wants used, and that decision must survive this change untouched. The plugin
66
+ # Ordered across trusted machine-managed substrates, ending at the installed
67
+ # package. Checkout-local paths are deliberately absent: a familiar generated
68
+ # destination is still repository-controlled executable code. The plugin
69
69
  # rungs are the floor: `resolve-secret.mjs` ships beside this skill, so a rung
70
70
  # pointing at it is reachable from anywhere the plugin itself is installed.
71
71
  # Without one, a consumer repository that vendors none of the leading paths
@@ -74,12 +74,9 @@ read_sonar_secret() {
74
74
  # lookups. This LADDER is identical in every skill that resolves a credential
75
75
  # and `credential-resolver-ladder` fails if any copy diverges. Only what
76
76
  # happens AFTER the ladder may differ between them.
77
- local candidates=(
78
- .claude/skills/lisa-secrets-access/scripts/resolve-secret.mjs
79
- .agents/skills/lisa-secrets-access/scripts/resolve-secret.mjs
80
- .opencode/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
81
- .codex/skills/lisa/lisa-secrets-access/scripts/resolve-secret.mjs
82
- )
77
+ # Execute only machine-managed plugin/package resolvers. Checkout-local
78
+ # candidates are repository-controlled code, not trusted merely by path.
79
+ local candidates=()
83
80
  if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then
84
81
  candidates+=("$CLAUDE_PLUGIN_ROOT/skills/lisa-secrets-access/scripts/resolve-secret.mjs")
85
82
  fi
@@ -98,7 +95,7 @@ read_sonar_secret() {
98
95
  local via_lisa
99
96
  via_lisa=$(node "$resolver" get "$name" 2>/dev/null) \
100
97
  && [ -n "$via_lisa" ] && { echo "$via_lisa"; return; }
101
- break
98
+ # Empty/error means this substrate had no answer; try the next trusted one.
102
99
  fi
103
100
  done
104
101
  # Name every path. A bare `return 1` sends the next reader hunting for a
@@ -58,7 +58,7 @@ The state machine (first match wins, evaluated over the **required** leaves only
58
58
  | else any leaf has **started** (claimed or in review, or shipped while a sibling has not) | active / in-progress | `claimed` (or `review` where supported) |
59
59
  | else (leaves exist, none started) | unchanged | — |
60
60
 
61
- - **Blocked dominates** — one blocked leaf surfaces blocked on the parent even while others progress. It never says *which* child or *which kind* of hold; run `scripts/rollup-blocker-classification.mjs` over the resolved child graph and carry its per-class report — blocking leaf, path, and who must act — into the rollup note. A non-zero exit means it classified nothing; that is a failure to report, never an all-clear. See `leaf-only-lifecycle` → **Classifying a hold**.
61
+ - **Blocked dominates** — one blocked leaf surfaces blocked on the parent even while others progress. It never says *which* child or *which kind* of hold. Resolve the shared classifier from `${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT:-plugins/lisa}}/scripts/rollup-blocker-classification.mjs` (the current-directory fallback supports a Lisa checkout), run it over the resolved child graph, and carry its per-class report — blocking leaf, path, and who must act — into the rollup note. A missing classifier or non-zero exit is a strict **no-write** result for every vendor: no lifecycle mutation and no rollup-comment mutation. Report the failure, never an all-clear. See `leaf-only-lifecycle` → **Classifying a hold**.
62
62
  - **Least-advanced env wins** — a parent reaches an env only once all required leaves have reached at least that env (all `On Stg` → `On Stg`; mixed dev/staging → the dev value). Native terminal closure fires only at the production `done`, never at an intermediate env.
63
63
  - **The parent never carries `ready`** — `ready` is a human "claim this leaf" signal; rollup only moves a parent between non-ready container states.
64
64
  - **Rollup is recursive** — an Epic rolls up from its Stories, each of which rolls up from its own leaves. Evaluate bottom-up.
@@ -1064,8 +1064,38 @@ readonly AGENT_CREDENTIAL='(\.(claude|codex|cursor|copilot|gemini|antigravity)/[
1064
1064
  # 15d. Dotenv files. `.env.example`, `.sample`, `.template`, `.dist` and
1065
1065
  # `.schema` are checked-in documentation of which keys exist, never the values,
1066
1066
  # so they are excluded — that collision is the one this rule most had to avoid.
1067
- readonly DOTENV_SECRET='(^|[[:space:]='"$qc"'./])\.env(\.[[:alnum:]_-]+)?([[:space:]'"$qc"']|$)'
1068
- readonly DOTENV_PUBLIC='\.env\.(example|sample|template|dist|schema|defaults)([[:space:]'"$qc"']|$)'
1067
+ # Decide dotenv privacy per PATH, never by subtracting one command-wide match
1068
+ # from another. A command that names `.env.example` and `.env` contains both a
1069
+ # public path and a private one; seeing the former cannot authorize the latter.
1070
+ #
1071
+ # The one write-only shape this guard has always allowed remains explicit:
1072
+ # seeding a private dotenv destination from one public template. This is safe
1073
+ # because the private path is the destination, not a value being read back.
1074
+ has_private_dotenv_path() {
1075
+ dotenv_scan_status=0
1076
+ dotenv_candidates="$(printf '%s' "$normalized_command_str" \
1077
+ | grep -Eio -- '(^|[[:space:]='"$qc"'./])\.env(\.[[:alnum:]_-]+)?')" \
1078
+ || dotenv_scan_status=$?
1079
+ if [ "$dotenv_scan_status" -gt 1 ]; then
1080
+ block "dotenv path classification failed; denying the credential-store read fail-closed."
1081
+ fi
1082
+
1083
+ while IFS= read -r candidate; do
1084
+ [ -n "$candidate" ] || continue
1085
+ candidate="$(printf '%s' "$candidate" \
1086
+ | tr '[:upper:]' '[:lower:]' \
1087
+ | grep -Eo -- '\.env(\.[[:alnum:]_-]+)?')"
1088
+ case "$candidate" in
1089
+ .env.example | .env.sample | .env.template | .env.dist | .env.schema | .env.defaults) ;;
1090
+ *) return 0 ;;
1091
+ esac
1092
+ done <<EOF
1093
+ $dotenv_candidates
1094
+ EOF
1095
+ return 1
1096
+ }
1097
+
1098
+ readonly SAFE_DOTENV_SEED='^[[:space:]]*(cp|mv)[[:space:]]+\.env\.(example|sample|template|dist|schema|defaults)[[:space:]]+\.env(\.[[:alnum:]_-]+)?[[:space:]]*$'
1069
1099
 
1070
1100
  if matches "$SECRET_READ_VERB"; then
1071
1101
  if matches "$SSH_PRIVATE_KEY"; then
@@ -1077,10 +1107,10 @@ if matches "$SECRET_READ_VERB"; then
1077
1107
  if matches "$AGENT_CREDENTIAL"; then
1078
1108
  block "reading, copying or transmitting a coding-agent or package-registry credential store. These tokens impersonate the operator to every service the agent can reach."
1079
1109
  fi
1080
- # `.env` only when the path is NOT one of the public example forms. Ordered as
1081
- # an exclusion rather than a narrower pattern so a new example suffix is one
1082
- # word to add, and so the reason a file is exempt stays readable.
1083
- if matches "$DOTENV_SECRET" && ! matches "$DOTENV_PUBLIC"; then
1110
+ # A public path elsewhere in the command never exempts a private path. The
1111
+ # narrow public-template -> private-destination seed is write-only and remains
1112
+ # allowed; every other command containing a private dotenv path is refused.
1113
+ if has_private_dotenv_path && ! matches "$SAFE_DOTENV_SEED"; then
1084
1114
  block "reading, copying or transmitting a dotenv file holding real values. \`.env.example\`, \`.env.sample\`, \`.env.template\` and \`.env.dist\` are readable, and WRITING a .env is unaffected — only reading one back is refused."
1085
1115
  fi
1086
1116
  # Fail-closed, narrowly. A read whose DIRECTORY is a known credential store
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.22.9",
3
+ "version": "4.22.10",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -329,12 +329,16 @@ the complete bundle from the shared account. Do not extract or distribute its
329
329
  individual access-key fields:
330
330
 
331
331
  \`\`\`bash
332
- aws --profile shared secretsmanager get-secret-value \\
332
+ aws secretsmanager get-secret-value \\
333
333
  --secret-id ${secretName} \\
334
334
  --query SecretString \\
335
335
  --output text
336
336
  \`\`\`
337
337
 
338
+ This uses the ambient AWS identity. Add \`--profile <source-profile>\` only when
339
+ this project requires a named source profile to read the emission; \`shared\` is
340
+ not an implicit requirement.
341
+
338
342
  Store that exact output as the masked secret \`LISA_AWS_BOOTSTRAP_JSON\` on each
339
343
  remote-agent platform. Run the setup command and require its live
340
344
  \`sts:GetCallerIdentity\` check to pass before considering the platform ready.