@codyswann/lisa 4.85.0 → 4.86.1

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 (333) hide show
  1. package/all/copy-overwrite/scripts/lib/completion-proof-content.mjs +63 -22
  2. package/all/copy-overwrite/scripts/lib/npm-update-hosted-hook.mjs +6 -1
  3. package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +19 -3
  4. package/all/copy-overwrite/scripts/npm-updater-helper-graph.json +2 -2
  5. package/dist/cli/doctor-session-manager-pending.d.ts +17 -0
  6. package/dist/cli/doctor-session-manager-pending.d.ts.map +1 -0
  7. package/dist/cli/doctor-session-manager-pending.js +130 -0
  8. package/dist/cli/doctor-session-manager-pending.js.map +1 -0
  9. package/dist/cli/doctor-session-manager-token.d.ts +69 -0
  10. package/dist/cli/doctor-session-manager-token.d.ts.map +1 -0
  11. package/dist/cli/doctor-session-manager-token.js +264 -0
  12. package/dist/cli/doctor-session-manager-token.js.map +1 -0
  13. package/dist/cli/doctor-session-manager.d.ts +3 -1
  14. package/dist/cli/doctor-session-manager.d.ts.map +1 -1
  15. package/dist/cli/doctor-session-manager.js +168 -146
  16. package/dist/cli/doctor-session-manager.js.map +1 -1
  17. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  18. package/dist/core/lisa-owned-hash-ledger.js +16 -0
  19. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  20. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  21. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  22. package/dist/core/upstream-evidence-manifest.js +168 -26
  23. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  24. package/dist/opencode/plugin-templates/parity-safety-net.sh +19 -3
  25. package/package.json +4 -4
  26. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  27. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  28. package/plugins/lisa/.codex-plugin/skills/lisa-parity-sentry-sdk-setup/SKILL.md +24 -17
  29. package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/SKILL.md +5 -3
  30. package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
  31. package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/validate-config.mjs +86 -31
  32. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/SKILL.md +197 -54
  33. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/references/runbook-template.md +23 -10
  34. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
  35. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
  36. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
  37. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
  38. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
  39. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
  40. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
  41. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
  42. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
  43. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/config.mjs +39 -11
  44. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
  45. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
  46. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
  47. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
  48. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/launch.mjs +69 -60
  49. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
  50. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
  51. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
  52. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
  53. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
  54. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
  55. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
  56. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
  57. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/token.mjs +39 -120
  58. package/plugins/lisa/.codex-plugin/skills/lisa-setup-session-manager/SKILL.md +82 -19
  59. package/plugins/lisa/commands/session-manager.md +1 -1
  60. package/plugins/lisa/commands/setup/session-manager.md +1 -1
  61. package/plugins/lisa/hooks/parity-safety-net.sh +19 -3
  62. package/plugins/lisa/rules/reference/config-resolution.md +39 -7
  63. package/plugins/lisa/skills/lisa-parity-sentry-sdk-setup/SKILL.md +24 -17
  64. package/plugins/lisa/skills/lisa-secrets-access/SKILL.md +5 -3
  65. package/plugins/lisa/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
  66. package/plugins/lisa/skills/lisa-secrets-access/scripts/validate-config.mjs +86 -31
  67. package/plugins/lisa/skills/lisa-session-manager/SKILL.md +197 -54
  68. package/plugins/lisa/skills/lisa-session-manager/references/runbook-template.md +23 -10
  69. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
  70. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
  71. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
  72. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
  73. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
  74. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
  75. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
  76. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
  77. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
  78. package/plugins/lisa/skills/lisa-session-manager/scripts/config.mjs +39 -11
  79. package/plugins/lisa/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
  80. package/plugins/lisa/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
  81. package/plugins/lisa/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
  82. package/plugins/lisa/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
  83. package/plugins/lisa/skills/lisa-session-manager/scripts/launch.mjs +69 -60
  84. package/plugins/lisa/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
  85. package/plugins/lisa/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
  86. package/plugins/lisa/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
  87. package/plugins/lisa/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
  88. package/plugins/lisa/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
  89. package/plugins/lisa/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
  90. package/plugins/lisa/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
  91. package/plugins/lisa/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
  92. package/plugins/lisa/skills/lisa-session-manager/scripts/token.mjs +39 -120
  93. package/plugins/lisa/skills/lisa-setup-session-manager/SKILL.md +82 -19
  94. package/plugins/lisa-agy/commands/lisa/session-manager.md +1 -1
  95. package/plugins/lisa-agy/commands/lisa/setup/session-manager.md +1 -1
  96. package/plugins/lisa-agy/hooks/parity-safety-net.sh +19 -3
  97. package/plugins/lisa-agy/plugin.json +1 -1
  98. package/plugins/lisa-agy/skills/lisa-parity-sentry-sdk-setup/SKILL.md +24 -17
  99. package/plugins/lisa-agy/skills/lisa-secrets-access/SKILL.md +5 -3
  100. package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
  101. package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/validate-config.mjs +86 -31
  102. package/plugins/lisa-agy/skills/lisa-session-manager/SKILL.md +197 -54
  103. package/plugins/lisa-agy/skills/lisa-session-manager/references/runbook-template.md +23 -10
  104. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
  105. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
  106. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
  107. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
  108. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
  109. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
  110. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
  111. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
  112. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
  113. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/config.mjs +39 -11
  114. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
  115. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
  116. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
  117. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
  118. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/launch.mjs +69 -60
  119. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
  120. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
  121. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
  122. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
  123. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
  124. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
  125. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
  126. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
  127. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/token.mjs +39 -120
  128. package/plugins/lisa-agy/skills/lisa-setup-session-manager/SKILL.md +82 -19
  129. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  130. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  131. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  132. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  133. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  134. package/plugins/lisa-cdk-devin/.claude-plugin/plugin.json +1 -1
  135. package/plugins/lisa-cdk-devin/.devin-plugin/plugin.json +1 -1
  136. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  137. package/plugins/lisa-copilot/commands/lisa/session-manager.md +1 -1
  138. package/plugins/lisa-copilot/commands/lisa/setup/session-manager.md +1 -1
  139. package/plugins/lisa-copilot/hooks/parity-safety-net.sh +19 -3
  140. package/plugins/lisa-copilot/rules/reference/config-resolution.md +39 -7
  141. package/plugins/lisa-copilot/skills/lisa-parity-sentry-sdk-setup/SKILL.md +24 -17
  142. package/plugins/lisa-copilot/skills/lisa-secrets-access/SKILL.md +5 -3
  143. package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
  144. package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/validate-config.mjs +86 -31
  145. package/plugins/lisa-copilot/skills/lisa-session-manager/SKILL.md +197 -54
  146. package/plugins/lisa-copilot/skills/lisa-session-manager/references/runbook-template.md +23 -10
  147. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
  148. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
  149. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
  150. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
  151. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
  152. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
  153. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
  154. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
  155. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
  156. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/config.mjs +39 -11
  157. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
  158. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
  159. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
  160. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
  161. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/launch.mjs +69 -60
  162. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
  163. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
  164. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
  165. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
  166. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
  167. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
  168. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
  169. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
  170. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/token.mjs +39 -120
  171. package/plugins/lisa-copilot/skills/lisa-setup-session-manager/SKILL.md +82 -19
  172. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  173. package/plugins/lisa-cursor/commands/lisa/session-manager.md +1 -1
  174. package/plugins/lisa-cursor/commands/lisa/setup/session-manager.md +1 -1
  175. package/plugins/lisa-cursor/hooks/parity-safety-net.sh +19 -3
  176. package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +39 -7
  177. package/plugins/lisa-cursor/skills/lisa-parity-sentry-sdk-setup/SKILL.md +24 -17
  178. package/plugins/lisa-cursor/skills/lisa-secrets-access/SKILL.md +5 -3
  179. package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
  180. package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/validate-config.mjs +86 -31
  181. package/plugins/lisa-cursor/skills/lisa-session-manager/SKILL.md +197 -54
  182. package/plugins/lisa-cursor/skills/lisa-session-manager/references/runbook-template.md +23 -10
  183. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
  184. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
  185. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
  186. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
  187. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
  188. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
  189. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
  190. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
  191. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
  192. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/config.mjs +39 -11
  193. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
  194. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
  195. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
  196. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
  197. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/launch.mjs +69 -60
  198. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
  199. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
  200. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
  201. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
  202. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
  203. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
  204. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
  205. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
  206. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/token.mjs +39 -120
  207. package/plugins/lisa-cursor/skills/lisa-setup-session-manager/SKILL.md +82 -19
  208. package/plugins/lisa-devin/.claude-plugin/plugin.json +1 -1
  209. package/plugins/lisa-devin/.devin-plugin/plugin.json +1 -1
  210. package/plugins/lisa-devin/hooks/parity-safety-net.sh +19 -3
  211. package/plugins/lisa-devin/rules/reference/config-resolution.md +39 -7
  212. package/plugins/lisa-devin/skills/lisa-parity-sentry-sdk-setup/SKILL.md +24 -17
  213. package/plugins/lisa-devin/skills/lisa-secrets-access/SKILL.md +5 -3
  214. package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
  215. package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/validate-config.mjs +86 -31
  216. package/plugins/lisa-devin/skills/lisa-session-manager/SKILL.md +197 -54
  217. package/plugins/lisa-devin/skills/lisa-session-manager/references/runbook-template.md +23 -10
  218. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
  219. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
  220. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
  221. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
  222. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
  223. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
  224. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
  225. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
  226. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
  227. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/config.mjs +39 -11
  228. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
  229. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
  230. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
  231. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
  232. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/launch.mjs +69 -60
  233. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
  234. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
  235. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
  236. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
  237. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
  238. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
  239. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
  240. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
  241. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/token.mjs +39 -120
  242. package/plugins/lisa-devin/skills/lisa-setup-session-manager/SKILL.md +82 -19
  243. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  244. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  245. package/plugins/lisa-expo-agy/plugin.json +1 -1
  246. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  247. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  248. package/plugins/lisa-expo-devin/.claude-plugin/plugin.json +1 -1
  249. package/plugins/lisa-expo-devin/.devin-plugin/plugin.json +1 -1
  250. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  251. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  252. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  253. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  254. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  255. package/plugins/lisa-harper-fabric-devin/.claude-plugin/plugin.json +1 -1
  256. package/plugins/lisa-harper-fabric-devin/.devin-plugin/plugin.json +1 -1
  257. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  258. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  259. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  260. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  261. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  262. package/plugins/lisa-nestjs-devin/.claude-plugin/plugin.json +1 -1
  263. package/plugins/lisa-nestjs-devin/.devin-plugin/plugin.json +1 -1
  264. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  265. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  266. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  267. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  268. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  269. package/plugins/lisa-openclaw-devin/.claude-plugin/plugin.json +1 -1
  270. package/plugins/lisa-openclaw-devin/.devin-plugin/plugin.json +1 -1
  271. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  272. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  273. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  274. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  275. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  276. package/plugins/lisa-phaser-devin/.claude-plugin/plugin.json +1 -1
  277. package/plugins/lisa-phaser-devin/.devin-plugin/plugin.json +1 -1
  278. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  279. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  280. package/plugins/lisa-rails-agy/plugin.json +1 -1
  281. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  282. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  283. package/plugins/lisa-rails-devin/.claude-plugin/plugin.json +1 -1
  284. package/plugins/lisa-rails-devin/.devin-plugin/plugin.json +1 -1
  285. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  286. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  287. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  288. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  289. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  290. package/plugins/lisa-typescript-devin/.claude-plugin/plugin.json +1 -1
  291. package/plugins/lisa-typescript-devin/.devin-plugin/plugin.json +1 -1
  292. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  293. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  294. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  295. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  296. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  297. package/plugins/lisa-wiki-devin/.claude-plugin/plugin.json +1 -1
  298. package/plugins/lisa-wiki-devin/.devin-plugin/plugin.json +1 -1
  299. package/plugins/src/base/commands/session-manager.md +1 -1
  300. package/plugins/src/base/commands/setup/session-manager.md +1 -1
  301. package/plugins/src/base/hooks/parity-safety-net.sh +19 -3
  302. package/plugins/src/base/rules/reference/config-resolution.md +39 -7
  303. package/plugins/src/base/skills/lisa-parity-sentry-sdk-setup/SKILL.md +24 -17
  304. package/plugins/src/base/skills/lisa-secrets-access/SKILL.md +5 -3
  305. package/plugins/src/base/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
  306. package/plugins/src/base/skills/lisa-secrets-access/scripts/validate-config.mjs +86 -31
  307. package/plugins/src/base/skills/lisa-session-manager/SKILL.md +197 -54
  308. package/plugins/src/base/skills/lisa-session-manager/references/runbook-template.md +23 -10
  309. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
  310. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
  311. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
  312. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
  313. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
  314. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
  315. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
  316. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
  317. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
  318. package/plugins/src/base/skills/lisa-session-manager/scripts/config.mjs +39 -11
  319. package/plugins/src/base/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
  320. package/plugins/src/base/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
  321. package/plugins/src/base/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
  322. package/plugins/src/base/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
  323. package/plugins/src/base/skills/lisa-session-manager/scripts/launch.mjs +69 -60
  324. package/plugins/src/base/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
  325. package/plugins/src/base/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
  326. package/plugins/src/base/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
  327. package/plugins/src/base/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
  328. package/plugins/src/base/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
  329. package/plugins/src/base/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
  330. package/plugins/src/base/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
  331. package/plugins/src/base/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
  332. package/plugins/src/base/skills/lisa-session-manager/scripts/token.mjs +39 -120
  333. package/plugins/src/base/skills/lisa-setup-session-manager/SKILL.md +82 -19
@@ -0,0 +1,117 @@
1
+ /**
2
+ * The GitHub fetches behind the session-manager `queue` subcommand, split out
3
+ * of cli-queue.mjs to keep it under the line budget (#4555): the Ready tier
4
+ * ({@link fetchGithubQueue}) and the backlog tier ({@link fetchGithubBacklog}).
5
+ * Both read with `gh api --paginate --slurp`, so every page is read or the
6
+ * command fails, and both run gh with the scrubbed worker env.
7
+ * @module session-manager/queue-github
8
+ */
9
+ import { normalizeGithubIssue } from "../../../scripts/ready-queue-order.mjs";
10
+ import { resolveRole } from "../../../scripts/resolve-lifecycle-role.mjs";
11
+ import { CliError, mustRun } from "./cli-runtime.mjs";
12
+ import { parseJson } from "./json-input.mjs";
13
+ import { scrubWorkerEnv } from "./workers.mjs";
14
+
15
+ const OWNER_REPO = /^[\w.-]+\/[\w.-]+$/;
16
+
17
+ /**
18
+ * The GitHub repo to read: `--github-repo`, else github.org/github.repo.
19
+ * @param {object} ctx - CLI context
20
+ * @param {{global?: object, local?: object}} project - Loaded project
21
+ * @returns {string} `owner/repo`
22
+ */
23
+ function githubRepo(ctx, project) {
24
+ const merged = { ...project.global?.github, ...project.local?.github };
25
+ const repo =
26
+ ctx.values["github-repo"] ??
27
+ (merged.org && merged.repo ? `${merged.org}/${merged.repo}` : "");
28
+ if (!OWNER_REPO.test(repo)) {
29
+ throw new CliError(
30
+ "No GitHub repo: set github.org and github.repo, or pass --github-repo <owner/repo>.",
31
+ 2
32
+ );
33
+ }
34
+ return repo;
35
+ }
36
+
37
+ /**
38
+ * Every string leaf of an object (label names, including env-keyed `done`).
39
+ * @param {unknown} value - Config subtree
40
+ * @returns {string[]} Leaves
41
+ */
42
+ function stringLeaves(value) {
43
+ if (typeof value === "string") return value ? [value] : [];
44
+ if (!value || typeof value !== "object") return [];
45
+ return Object.values(value).flatMap(stringLeaves);
46
+ }
47
+
48
+ /**
49
+ * Fetch every open GitHub issue carrying no lifecycle label (build or PRD),
50
+ * all pages: the GitHub backlog tier.
51
+ * @param {object} ctx - CLI context
52
+ * @param {{global?: object, local?: object}} project - Loaded project
53
+ * @returns {{ items: object[], pages: number }} Normalized items
54
+ */
55
+ export function fetchGithubBacklog(ctx, project) {
56
+ const repo = githubRepo(ctx, project);
57
+ const lifecycle = new Set(
58
+ [project.global?.github?.labels, project.local?.github?.labels]
59
+ .flatMap(labels => [
60
+ ...stringLeaves(labels?.build),
61
+ ...stringLeaves(labels?.prd),
62
+ ])
63
+ .map(label => label.toLowerCase())
64
+ );
65
+ const stdout = mustRun(
66
+ "gh",
67
+ [
68
+ "api",
69
+ "--paginate",
70
+ "--slurp",
71
+ `repos/${repo}/issues?state=open&per_page=100`,
72
+ ],
73
+ { env: scrubWorkerEnv(ctx.env) }
74
+ );
75
+ const pages = parseJson(stdout, "gh api output");
76
+ const rows = Array.isArray(pages) ? pages.flat() : [];
77
+ return {
78
+ items: rows
79
+ .map(row => normalizeGithubIssue(row, { repo }))
80
+ .filter(Boolean)
81
+ .filter(
82
+ item => !item.labels.some(label => lifecycle.has(label.toLowerCase()))
83
+ ),
84
+ pages: Array.isArray(pages) ? pages.length : 0,
85
+ };
86
+ }
87
+
88
+ /**
89
+ * Fetch every open GitHub issue carrying the ready label, all pages.
90
+ * @param {object} ctx - CLI context
91
+ * @param {{global?: object, local?: object}} project - Loaded project
92
+ * @returns {{ items: object[], pages: number }} Normalized items
93
+ */
94
+ export function fetchGithubQueue(ctx, project) {
95
+ const repo = githubRepo(ctx, project);
96
+ const role = resolveRole({
97
+ role: "ready",
98
+ vendor: "github",
99
+ intent: "read",
100
+ local: project.local,
101
+ global: project.global,
102
+ });
103
+ const label = ctx.values.label ?? role.value;
104
+ if (!label) throw new CliError(role.message, 2);
105
+ const endpoint =
106
+ `repos/${repo}/issues?state=open&labels=${encodeURIComponent(label)}` +
107
+ `&per_page=100`;
108
+ const stdout = mustRun("gh", ["api", "--paginate", "--slurp", endpoint], {
109
+ env: scrubWorkerEnv(ctx.env),
110
+ });
111
+ const pages = parseJson(stdout, "gh api output");
112
+ const rows = Array.isArray(pages) ? pages.flat() : [];
113
+ return {
114
+ items: rows.map(row => normalizeGithubIssue(row, { repo })).filter(Boolean),
115
+ pages: Array.isArray(pages) ? pages.length : 0,
116
+ };
117
+ }
@@ -18,7 +18,11 @@
18
18
  * background sessions (`claude --bg`, `claude agents`), cross-session
19
19
  * messaging and channels, which no other supported agent has. Codex, Cursor,
20
20
  * OpenCode, Antigravity, Copilot and Devin can run the setup-time subcommands
21
- * (init-config, store-token, trust-status, coexistence, validate, start), and
21
+ * (init-config, store-token into the configured secrets provider,
22
+ * trust-status, coexistence, validate, start), `status` (pending questions
23
+ * and their answerCommand) and `answer` (an operator answering a pending
24
+ * question at the terminal), from the Claude Code plugin install or the npm
25
+ * package only (never the Codex overlay copy), and
22
26
  * `start` hosts a Claude Code manager in tmux whatever agent ran it. Note that
23
27
  * trust-status reads Claude Code's own trust record, which is the one that
24
28
  * matters: workers are always Claude Code. They cannot run a manager cycle
@@ -41,6 +45,7 @@ import {
41
45
  recordCycle,
42
46
  route,
43
47
  } from "./cli-cycle.mjs";
48
+ import { answer, itemAnswer, markPlan } from "./cli-human.mjs";
44
49
  import { repoDir, start, status, stop, validate } from "./cli-manager.mjs";
45
50
  import { queue } from "./cli-queue.mjs";
46
51
  import { CliError, EXIT } from "./cli-runtime.mjs";
@@ -68,7 +73,7 @@ const COMMANDS = {
68
73
  stop: { run: stop, options: {}, positionals: 0 },
69
74
  "store-token": {
70
75
  run: storeToken,
71
- options: { replace: flag, "from-clipboard": flag },
76
+ options: { replace: flag, "from-clipboard": flag, "vault-project": text },
72
77
  positionals: 0,
73
78
  },
74
79
  "trust-status": { run: trustStatus, options: {}, positionals: 0 },
@@ -81,7 +86,7 @@ const COMMANDS = {
81
86
  validate: { run: validate, options: {}, positionals: 0 },
82
87
  "init-config": {
83
88
  run: initConfig,
84
- options: { slug: text, write: flag },
89
+ options: { slug: text, write: flag, notify: text },
85
90
  positionals: 0,
86
91
  },
87
92
  queue: {
@@ -106,6 +111,9 @@ const COMMANDS = {
106
111
  options: { input: text, ref: text },
107
112
  positionals: 0,
108
113
  },
114
+ "mark-plan": { run: markPlan, options: { kind: text }, positionals: 1 },
115
+ answer: { run: answer, options: {}, positionals: 1 },
116
+ "item-answer": { run: itemAnswer, options: {}, positionals: 1 },
109
117
  "record-cycle": {
110
118
  run: recordCycle,
111
119
  options: {
@@ -125,10 +133,14 @@ const LEDGER_SPECS = {
125
133
  positionals: 1,
126
134
  },
127
135
  "set-question": {
128
- options: { "telegram-message-id": text, sender: text },
136
+ options: { "telegram-message-id": text, sender: text, kind: text },
129
137
  positionals: 1,
130
138
  },
131
- "clear-question": { options: {}, positionals: 1 },
139
+ marked: {
140
+ options: { "marker-type": text, marker: text, "comment-id": text },
141
+ positionals: 1,
142
+ },
143
+ "clear-question": { options: { "reset-attempts": flag }, positionals: 1 },
132
144
  finish: { options: { status: text, sender: text }, positionals: 1 },
133
145
  "validation-failed": { options: { "updated-at": text }, positionals: 1 },
134
146
  "validation-cleared": { options: {}, positionals: 1 },
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Migrating the old plaintext Telegram bot token file into the secrets
3
+ * provider (#4555): reading `<notify.stateDir>/.env`, deciding whether it
4
+ * may be deleted, and the refusals `store-token` and `start` share.
5
+ *
6
+ * Split from token-store.mjs, which keeps the provider calls. The two sides
7
+ * of the migration must agree on what counts as a leftover token, or an
8
+ * operator is sent round a loop: `start` refuses only a file that really
9
+ * holds a `TELEGRAM_BOT_TOKEN` line ({@link holdsToken}), and `store-token`
10
+ * deletes a file with no token and nothing else in it (empty, or comments
11
+ * only) once the provider holds a verified secret ({@link removable}).
12
+ *
13
+ * Values are never kept beyond the comparison, returned in a message or
14
+ * logged; a file's other keys are reported by name only.
15
+ * @module session-manager/token-migrate
16
+ */
17
+ import { createHash } from "node:crypto";
18
+ import path from "node:path";
19
+
20
+ import { validateBotToken } from "./token.mjs";
21
+
22
+ /** The plugin's plaintext token file in the state dir (the old storage). */
23
+ export const TOKEN_FILE = ".env";
24
+
25
+ /** The variable the Telegram plugin reads its token from. */
26
+ export const TOKEN_VAR = "TELEGRAM_BOT_TOKEN";
27
+
28
+ /** One `KEY=value` line of a dotenv file, optionally `export`ed. */
29
+ const ENV_LINE = /^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)=(.*)$/;
30
+
31
+ /** A refusal or failure carrying the exit code the CLI should use. */
32
+ export class TokenStoreError extends Error {
33
+ /**
34
+ * @param {string} message - Operator-readable reason; never a value
35
+ * @param {number} exitCode - 1 (failure, retry) or 2 (setup)
36
+ */
37
+ constructor(message, exitCode) {
38
+ super(message);
39
+ this.name = "TokenStoreError";
40
+ this.exitCode = exitCode;
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Exit 2: something the operator has to change.
46
+ * @param {string} message - Reason
47
+ * @returns {TokenStoreError} Error
48
+ */
49
+ export const setupError = message => new TokenStoreError(message, 2);
50
+
51
+ /**
52
+ * sha256 of a value, for comparing a written secret with its read-back.
53
+ * @param {string} value - Value
54
+ * @returns {string} Hex digest
55
+ */
56
+ export function digest(value) {
57
+ return createHash("sha256").update(String(value)).digest("hex");
58
+ }
59
+
60
+ /**
61
+ * Read the plaintext token file, if there is one: its token, and the names
62
+ * of anything else in it (values are never kept or reported).
63
+ * @param {string} stateDir - Resolved state dir
64
+ * @param {Pick<import("node:fs"), "existsSync" | "readFileSync">} fs - Seam
65
+ * @returns {{ path: string, token: string | undefined, others: string[] } |
66
+ * null} The file, or null when absent
67
+ */
68
+ export function readEnvFile(stateDir, fs) {
69
+ const file = path.join(stateDir, TOKEN_FILE);
70
+ if (!fs.existsSync(file)) return null;
71
+ const entries = fs
72
+ .readFileSync(file, "utf8")
73
+ .split(/\r?\n/)
74
+ .map((line, at) => ({ line: line.trim(), at }))
75
+ .filter(({ line }) => line !== "" && !line.startsWith("#"))
76
+ .map(({ line, at }) => ({ match: ENV_LINE.exec(line), at }));
77
+ const token = entries.find(({ match }) => match?.[1] === TOKEN_VAR)?.match;
78
+ return {
79
+ path: file,
80
+ token: token ? token[2].replace(/^(['"])(.*)\1$/, "$2") : undefined,
81
+ others: entries
82
+ .filter(({ match }) => match?.[1] !== TOKEN_VAR)
83
+ .map(({ match, at }) => match?.[1] ?? `an unrecognised line ${at + 1}`),
84
+ };
85
+ }
86
+
87
+ /**
88
+ * Whether a token file really holds a token: what `start` refuses.
89
+ * @param {ReturnType<typeof readEnvFile>} file - The file, if any
90
+ * @returns {boolean} True for a `TELEGRAM_BOT_TOKEN` line
91
+ */
92
+ export function holdsToken(file) {
93
+ return file !== null && file.token !== undefined;
94
+ }
95
+
96
+ /**
97
+ * Whether a file holds nothing at all (empty, blank or comments only), so
98
+ * `store-token` may delete it once the provider holds a verified secret.
99
+ * @param {ReturnType<typeof readEnvFile>} file - The file, if any
100
+ * @returns {boolean} True for a removable empty file
101
+ */
102
+ export function removable(file) {
103
+ return file !== null && file.token === undefined && file.others.length === 0;
104
+ }
105
+
106
+ /**
107
+ * Refuse a plaintext file that cannot be migrated as it is.
108
+ * @param {ReturnType<typeof readEnvFile>} file - The file, if any
109
+ */
110
+ export function assertMigratable(file) {
111
+ if (file === null) return;
112
+ if (file.others.length > 0) {
113
+ throw setupError(
114
+ `${file.path} holds ${file.others.join(", ")} besides ${TOKEN_VAR}, so ` +
115
+ `it cannot be deleted after the token moves. Move the rest out of it, ` +
116
+ `then run store-token again. Nothing was changed.`
117
+ );
118
+ }
119
+ if (file.token !== undefined && !validateBotToken(file.token)) {
120
+ throw setupError(
121
+ `${file.path} holds a ${TOKEN_VAR} that does not look like a Telegram ` +
122
+ `bot token. Fix or delete the file, then run store-token again. ` +
123
+ `Nothing was changed.`
124
+ );
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Delete the plaintext file after the provider's copy is verified, and say
130
+ * so: `migratedFrom` when its token moved, `deleted` whenever it went.
131
+ * @param {ReturnType<typeof readEnvFile>} file - The file, if any
132
+ * @param {Pick<import("node:fs"), "rmSync">} fs - Seam
133
+ * @returns {{ migratedFrom?: string, deleted?: true }} What was removed
134
+ */
135
+ export function removeAfterVerify(file, fs) {
136
+ if (holdsToken(file)) {
137
+ fs.rmSync(file.path);
138
+ return { migratedFrom: file.path, deleted: true };
139
+ }
140
+ if (removable(file)) {
141
+ fs.rmSync(file.path);
142
+ return { deleted: true };
143
+ }
144
+ return {};
145
+ }
146
+
147
+ /**
148
+ * The result for a secret the provider already holds (no write): a matching
149
+ * or empty plaintext file is deleted; one holding a different token is kept
150
+ * and refused.
151
+ * @param {{ value: string }} existing - The stored secret
152
+ * @param {ReturnType<typeof readEnvFile>} file - The plaintext file, if any
153
+ * @param {{ cfg: object, name: string, fs: object }} input - Context
154
+ * @returns {object} Outcome
155
+ */
156
+ export function keepExisting(existing, file, { cfg, name, fs }) {
157
+ const result = {
158
+ stored: true,
159
+ storage: "provider",
160
+ provider: cfg.provider,
161
+ secret: name,
162
+ verified: true,
163
+ alreadyStored: true,
164
+ };
165
+ if (!validateBotToken(existing.value)) {
166
+ throw setupError(
167
+ `${name} in ${cfg.provider} does not look like a Telegram bot token. ` +
168
+ `Run store-token --replace to store the right one.`
169
+ );
170
+ }
171
+ if (holdsToken(file) && digest(file.token) !== digest(existing.value)) {
172
+ throw setupError(
173
+ `${file.path} holds a different token from the one ${cfg.provider} ` +
174
+ `already stores as ${name}. Nothing was changed. To make the file's ` +
175
+ `token the stored one, run store-token --replace; to keep the stored ` +
176
+ `one, delete ${file.path}.`
177
+ );
178
+ }
179
+ return { ...result, ...removeAfterVerify(file, fs) };
180
+ }
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Talking to the secrets provider for the Telegram bot token (#4555): the
3
+ * adapter over `lisa-secrets-access` (`surfaces.mjs` for the config,
4
+ * `providers.mjs` for reads and writes), whether a provider is configured at
5
+ * all, and turning a provider error into fixed operator text.
6
+ *
7
+ * Split from token-store.mjs, which keeps the store and resolve flows. The
8
+ * adapter is injectable so tests run against a fake vault. A provider's raw
9
+ * error is matched, never repeated: a failed write's error can quote its
10
+ * argv, which holds the value.
11
+ * @module session-manager/token-provider
12
+ */
13
+ import { TokenStoreError, setupError } from "./token-migrate.mjs";
14
+
15
+ /** A provider error that names a rate limit (HTTP 429). */
16
+ const RATE_LIMITED = /\b429\b|too many requests|rate.?limit/i;
17
+
18
+ /** The bootstrap prefix whose CLI each provider runs, for the preflight. */
19
+ const CLI_PREFIX = { bitwarden: "BWS_ACCESS_TOKEN", doppler: "DOPPLER_TOKEN" };
20
+
21
+ /** Operator-facing reasons, by {@link classifyProviderFailure} kind. */
22
+ const REASONS = {
23
+ "rate-limited": "it rate-limited the request (429 Too Many Requests)",
24
+ "timed-out": "it did not answer in time",
25
+ unavailable: "it could not be reached or refused the request",
26
+ };
27
+
28
+ /**
29
+ * What went wrong talking to the provider, read from the error's code and
30
+ * text only (the text is matched, never repeated).
31
+ * @param {unknown} error - Caught value
32
+ * @returns {"setup" | "missing-cli" | "timed-out" | "rate-limited" |
33
+ * "unsupported" | "duplicate" | "unavailable"} Kind
34
+ */
35
+ export function classifyProviderFailure(error) {
36
+ const text = `${error?.message ?? ""} ${error?.stderr ?? ""}`;
37
+ if (error?.kind === "setup") return "setup";
38
+ if (error?.code === "ENOENT") return "missing-cli";
39
+ if (error?.code === "ETIMEDOUT" || /at its own deadline/.test(text)) {
40
+ return "timed-out";
41
+ }
42
+ if (RATE_LIMITED.test(text)) return "rate-limited";
43
+ if (/has no (bulk read|create|write) implemented/.test(text)) {
44
+ return "unsupported";
45
+ }
46
+ if (/duplicate secret key/.test(text)) return "duplicate";
47
+ return "unavailable";
48
+ }
49
+
50
+ /**
51
+ * Turn a provider error into a {@link TokenStoreError} with fixed text.
52
+ * @param {unknown} error - Caught value
53
+ * @param {{ provider: string, name: string, retry: string }} where - The
54
+ * provider, the secret, and the retry advice for a transient failure
55
+ * @returns {TokenStoreError} Error
56
+ */
57
+ export function providerError(error, { provider, name, retry }) {
58
+ const kind = classifyProviderFailure(error);
59
+ if (kind === "setup") return setupError(String(error.message));
60
+ if (kind === "missing-cli") {
61
+ return setupError(
62
+ `The ${provider} command-line tool is not on PATH. Install it with ` +
63
+ `/lisa:setup:workstation, then try again.`
64
+ );
65
+ }
66
+ if (kind === "unsupported") {
67
+ return setupError(
68
+ `Lisa cannot read or write ${name} in provider "${provider}" yet. ` +
69
+ `Create ${name} in ${provider} yourself, then run store-token to ` +
70
+ `verify it and remove any plaintext copy.`
71
+ );
72
+ }
73
+ if (kind === "duplicate") {
74
+ return setupError(
75
+ `${provider} shows more than one secret with the same name, so Lisa ` +
76
+ `will not guess which is ${name}. Remove the duplicate in ${provider}.`
77
+ );
78
+ }
79
+ return new TokenStoreError(`${retry.replace("<reason>", REASONS[kind])}`, 1);
80
+ }
81
+
82
+ /**
83
+ * The lazily-loaded secrets adapter over `lisa-secrets-access`, by path
84
+ * relative to this file (the way token.mjs loads prompt-secret.mjs).
85
+ * @returns {Promise<{ readConfig: Function, preflight: Function,
86
+ * list: Function, create: Function, replace: Function }>} Adapter
87
+ */
88
+ export async function defaultSecretsAdapter() {
89
+ const base = new URL("../../lisa-secrets-access/scripts/", import.meta.url);
90
+ const [surfaces, providers] = await Promise.all([
91
+ import(new URL("surfaces.mjs", base).href),
92
+ import(new URL("providers.mjs", base).href),
93
+ ]);
94
+ return {
95
+ readConfig: (root, env) => surfaces.readConfig(root, env),
96
+ preflight: cfg => {
97
+ const prefix = CLI_PREFIX[cfg.provider];
98
+ if (prefix && providers.providerCliPresent(prefix) === false) {
99
+ throw Object.assign(new Error("missing"), { code: "ENOENT" });
100
+ }
101
+ try {
102
+ providers.bootstrapToken(cfg.bootstrap);
103
+ } catch (error) {
104
+ throw Object.assign(new Error(error.message), { kind: "setup" });
105
+ }
106
+ },
107
+ list: cfg =>
108
+ [...providers.normalizeRows(providers.fetchRaw(cfg), cfg.narrow)].map(
109
+ ([key, row]) => ({ key, ...row })
110
+ ),
111
+ create: (cfg, secret) => providers.createSecret(cfg, secret),
112
+ replace: (cfg, { id, value }) => providers.writeSecret(cfg, id, value),
113
+ };
114
+ }
115
+
116
+ /**
117
+ * The project's secrets config and adapter, or `null` when no provider is
118
+ * configured (no `secrets` block, or `provider: "env"`): then the token stays
119
+ * in the state dir's `.env` file.
120
+ * @param {string} root - Project root
121
+ * @param {Record<string, string | undefined>} env - Environment
122
+ * @param {object} [adapter] - Injected adapter (tests)
123
+ * @returns {Promise<{ cfg: object, adapter: object } | null>} Provider mode
124
+ */
125
+ export async function loadTokenSecrets(root, env, adapter) {
126
+ const secrets = adapter ?? (await defaultSecretsAdapter());
127
+ let cfg;
128
+ try {
129
+ cfg = secrets.readConfig(root, env);
130
+ } catch (error) {
131
+ throw setupError(`Could not read the secrets config: ${error.message}`);
132
+ }
133
+ const configured =
134
+ typeof cfg?.provider === "string" && cfg.provider !== "env";
135
+ return configured ? { cfg, adapter: secrets } : null;
136
+ }
137
+
138
+ /**
139
+ * Call the adapter, turning a provider error into a {@link TokenStoreError}.
140
+ * @param {() => unknown} call - Adapter call
141
+ * @param {{ provider: string, name: string, retry: string }} where - Context
142
+ * @returns {Promise<unknown>} The call's result
143
+ */
144
+ export async function guarded(call, where) {
145
+ try {
146
+ return await call();
147
+ } catch (error) {
148
+ throw providerError(error, where);
149
+ }
150
+ }
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Reading the Telegram bot token from the operator, and the shell lines that
3
+ * name the `store-token` command: token shape validation, the echo-off
4
+ * prompt, the clipboard reader, and the command printed when there is no TTY.
5
+ * Nothing here writes a file or prints the token. Split out of token.mjs to
6
+ * keep it under the line budget (#4555); token.mjs re-exports every export,
7
+ * and token-store.mjs reuses the readers for the secrets-provider path.
8
+ * @module token-read
9
+ */
10
+ import path from "node:path";
11
+
12
+ import { readClipboard } from "./clipboard.mjs";
13
+
14
+ /** The shape of a BotFather token: `<bot id>:<secret>`. */
15
+ const BOT_TOKEN_PATTERN = /^\d+:[A-Za-z0-9_-]{30,}$/;
16
+
17
+ /**
18
+ * Whether `token` looks like a BotFather bot token.
19
+ * @param {unknown} token - Candidate
20
+ * @returns {boolean} True for `<digits>:<30+ of [A-Za-z0-9_-]>`
21
+ */
22
+ export function validateBotToken(token) {
23
+ return typeof token === "string" && BOT_TOKEN_PATTERN.test(token);
24
+ }
25
+
26
+ /**
27
+ * Load `promptSecret`/`canPrompt` from the secrets-access skill, by path
28
+ * relative to this file, the same way other skills reach it. Lazy so the
29
+ * argv builders never pay for it.
30
+ * @returns {Promise<{ promptSecret: Function, canPrompt: Function }>} Module
31
+ */
32
+ export function loadPromptSecret() {
33
+ return import(
34
+ new URL(
35
+ "../../lisa-secrets-access/scripts/prompt-secret.mjs",
36
+ import.meta.url
37
+ ).href
38
+ );
39
+ }
40
+
41
+ /**
42
+ * Quote one word for a POSIX shell.
43
+ * @param {string} word - Word to quote
44
+ * @returns {string} Single-quoted word
45
+ */
46
+ export function shellQuote(word) {
47
+ const escaped = String(word).replaceAll("'", String.raw`'\''`);
48
+ return `'${escaped}'`;
49
+ }
50
+
51
+ /**
52
+ * The command an operator pastes into their own terminal to store the token,
53
+ * for when the caller (an agent's Bash) has no TTY to prompt on. `start`
54
+ * also names it when a plaintext token file is left over, since the same
55
+ * command migrates that file into the secrets provider (#4555).
56
+ * @param {{ scriptsDir: string, projectRoot: string, replace?: boolean,
57
+ * fromClipboard?: boolean, vaultProject?: string }} input - Script and
58
+ * project locations, and the flags to carry
59
+ * @returns {string} One shell line
60
+ */
61
+ export function buildStoreTokenCommand({
62
+ scriptsDir,
63
+ projectRoot,
64
+ replace,
65
+ fromClipboard = false,
66
+ vaultProject,
67
+ }) {
68
+ const script = shellQuote(path.join(scriptsDir, "session-manager.mjs"));
69
+ const flags =
70
+ (replace ? " --replace" : "") +
71
+ (fromClipboard ? " --from-clipboard" : "") +
72
+ (vaultProject ? ` --vault-project ${shellQuote(vaultProject)}` : "");
73
+ return `node ${script} store-token${flags} --project-root ${shellQuote(projectRoot)}`;
74
+ }
75
+
76
+ /** Why `store-token` cannot prompt here, and the two ways forward. */
77
+ const NO_TTY_HINT =
78
+ "No terminal to prompt on, and Claude Code's ! prefix has no TTY either. " +
79
+ "Run `command` in a separate terminal app, or copy the token from " +
80
+ "BotFather and run `clipboardCommand`.";
81
+
82
+ /** The `invalid` outcome; names the expected shape, never the input. */
83
+ const INVALID = Object.freeze({
84
+ stored: false,
85
+ reason: "invalid",
86
+ message:
87
+ "That does not look like a Telegram bot token " +
88
+ "(expected <digits>:<30 or more letters, digits, _ or ->).",
89
+ });
90
+
91
+ /**
92
+ * Read a well-formed bot token from the system clipboard, with no TTY.
93
+ *
94
+ * The token is returned only to the caller that stores it, with a `clear`
95
+ * function to call after a successful store, so a failed attempt leaves the
96
+ * operator's copy in place to retry with. An invalid value is reported by
97
+ * shape, never echoed.
98
+ * @param {{ readClipboard?: typeof readClipboard }} [io] - Seams
99
+ * @returns {{ token: string, clear: () => boolean } |
100
+ * { stored: false, reason: "empty" | "invalid" | "unsupported" |
101
+ * "unavailable" | "failed", message: string }} Token, or why not
102
+ */
103
+ export function readTokenFromClipboard(io = {}) {
104
+ const clip = (io.readClipboard ?? readClipboard)();
105
+ if (!clip.ok) {
106
+ return { stored: false, reason: clip.reason, message: clip.message };
107
+ }
108
+ const token = clip.text.trim();
109
+ if (!token) {
110
+ return {
111
+ stored: false,
112
+ reason: "empty",
113
+ message: "The clipboard is empty.",
114
+ };
115
+ }
116
+ if (!validateBotToken(token)) return { ...INVALID };
117
+ return { token, clear: clip.clear };
118
+ }
119
+
120
+ /**
121
+ * Prompt for a well-formed bot token on the operator's terminal (echo off).
122
+ *
123
+ * With no TTY this does not prompt and does not fail: it returns the exact
124
+ * command for the operator to run in a separate terminal app, and the
125
+ * `--from-clipboard` form of it, because an agent's Bash (and Claude Code's
126
+ * `!` prefix) can never answer a prompt and a hung setup looks like a slow
127
+ * one.
128
+ * @param {{ label: string, scriptsDir: string, projectRoot: string,
129
+ * replace?: boolean, vaultProject?: string, io?: {
130
+ * canPrompt?: () => boolean, promptSecret?: (message: string) => string } }}
131
+ * input - What the token is for (shown in the prompt), locations, the
132
+ * flags the printed commands carry, and seams
133
+ * @returns {Promise<{ token: string } |
134
+ * { stored: false, reason: "no-tty", message: string, command: string,
135
+ * clipboardCommand: string } |
136
+ * { stored: false, reason: "empty" | "invalid", message: string }>} Token,
137
+ * or why not
138
+ */
139
+ export async function readTokenFromPrompt(input) {
140
+ const { label, io = {} } = input;
141
+ const loaded =
142
+ io.canPrompt && io.promptSecret ? null : await loadPromptSecret();
143
+ const canPrompt = io.canPrompt ?? (() => loaded.canPrompt());
144
+ const promptSecret = io.promptSecret ?? loaded.promptSecret;
145
+
146
+ if (!canPrompt()) {
147
+ return {
148
+ stored: false,
149
+ reason: "no-tty",
150
+ message: NO_TTY_HINT,
151
+ command: buildStoreTokenCommand(input),
152
+ clipboardCommand: buildStoreTokenCommand({
153
+ ...input,
154
+ fromClipboard: true,
155
+ }),
156
+ };
157
+ }
158
+ const typed = promptSecret(
159
+ `Paste the Telegram bot token for ${label} (input is hidden): `
160
+ );
161
+ if (!typed) {
162
+ return { stored: false, reason: "empty", message: "Nothing entered." };
163
+ }
164
+ if (!validateBotToken(typed)) return { ...INVALID };
165
+ return { token: typed };
166
+ }