@codyswann/lisa 4.84.11 → 4.86.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (322) hide show
  1. package/dist/cli/doctor-session-manager-pending.d.ts +17 -0
  2. package/dist/cli/doctor-session-manager-pending.d.ts.map +1 -0
  3. package/dist/cli/doctor-session-manager-pending.js +130 -0
  4. package/dist/cli/doctor-session-manager-pending.js.map +1 -0
  5. package/dist/cli/doctor-session-manager-token.d.ts +69 -0
  6. package/dist/cli/doctor-session-manager-token.d.ts.map +1 -0
  7. package/dist/cli/doctor-session-manager-token.js +264 -0
  8. package/dist/cli/doctor-session-manager-token.js.map +1 -0
  9. package/dist/cli/doctor-session-manager.d.ts +3 -1
  10. package/dist/cli/doctor-session-manager.d.ts.map +1 -1
  11. package/dist/cli/doctor-session-manager.js +168 -146
  12. package/dist/cli/doctor-session-manager.js.map +1 -1
  13. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  14. package/dist/core/lisa-owned-hash-ledger.js +34 -0
  15. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  16. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  17. package/dist/core/nightly-e2e-guard-behavior-certificate.js.map +1 -1
  18. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  19. package/dist/core/upstream-evidence-manifest.js +172 -23
  20. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  21. package/package.json +4 -4
  22. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  23. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  24. package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/SKILL.md +5 -3
  25. package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
  26. package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/validate-config.mjs +47 -26
  27. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/SKILL.md +197 -54
  28. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/references/runbook-template.md +23 -10
  29. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
  30. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
  31. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
  32. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
  33. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
  34. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
  35. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
  36. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
  37. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
  38. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/config.mjs +39 -11
  39. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
  40. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
  41. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
  42. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
  43. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/launch.mjs +69 -60
  44. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
  45. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
  46. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
  47. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
  48. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
  49. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
  50. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
  51. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
  52. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/token.mjs +39 -120
  53. package/plugins/lisa/.codex-plugin/skills/lisa-setup-session-manager/SKILL.md +82 -19
  54. package/plugins/lisa/commands/session-manager.md +1 -1
  55. package/plugins/lisa/commands/setup/session-manager.md +1 -1
  56. package/plugins/lisa/rules/reference/config-resolution.md +39 -7
  57. package/plugins/lisa/skills/lisa-secrets-access/SKILL.md +5 -3
  58. package/plugins/lisa/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
  59. package/plugins/lisa/skills/lisa-secrets-access/scripts/validate-config.mjs +47 -26
  60. package/plugins/lisa/skills/lisa-session-manager/SKILL.md +197 -54
  61. package/plugins/lisa/skills/lisa-session-manager/references/runbook-template.md +23 -10
  62. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
  63. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
  64. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
  65. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
  66. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
  67. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
  68. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
  69. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
  70. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
  71. package/plugins/lisa/skills/lisa-session-manager/scripts/config.mjs +39 -11
  72. package/plugins/lisa/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
  73. package/plugins/lisa/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
  74. package/plugins/lisa/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
  75. package/plugins/lisa/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
  76. package/plugins/lisa/skills/lisa-session-manager/scripts/launch.mjs +69 -60
  77. package/plugins/lisa/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
  78. package/plugins/lisa/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
  79. package/plugins/lisa/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
  80. package/plugins/lisa/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
  81. package/plugins/lisa/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
  82. package/plugins/lisa/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
  83. package/plugins/lisa/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
  84. package/plugins/lisa/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
  85. package/plugins/lisa/skills/lisa-session-manager/scripts/token.mjs +39 -120
  86. package/plugins/lisa/skills/lisa-setup-session-manager/SKILL.md +82 -19
  87. package/plugins/lisa-agy/commands/lisa/session-manager.md +1 -1
  88. package/plugins/lisa-agy/commands/lisa/setup/session-manager.md +1 -1
  89. package/plugins/lisa-agy/plugin.json +1 -1
  90. package/plugins/lisa-agy/skills/lisa-secrets-access/SKILL.md +5 -3
  91. package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
  92. package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/validate-config.mjs +47 -26
  93. package/plugins/lisa-agy/skills/lisa-session-manager/SKILL.md +197 -54
  94. package/plugins/lisa-agy/skills/lisa-session-manager/references/runbook-template.md +23 -10
  95. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
  96. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
  97. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
  98. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
  99. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
  100. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
  101. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
  102. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
  103. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
  104. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/config.mjs +39 -11
  105. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
  106. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
  107. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
  108. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
  109. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/launch.mjs +69 -60
  110. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
  111. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
  112. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
  113. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
  114. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
  115. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
  116. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
  117. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
  118. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/token.mjs +39 -120
  119. package/plugins/lisa-agy/skills/lisa-setup-session-manager/SKILL.md +82 -19
  120. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  121. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  122. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  123. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  124. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  125. package/plugins/lisa-cdk-devin/.claude-plugin/plugin.json +1 -1
  126. package/plugins/lisa-cdk-devin/.devin-plugin/plugin.json +1 -1
  127. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  128. package/plugins/lisa-copilot/commands/lisa/session-manager.md +1 -1
  129. package/plugins/lisa-copilot/commands/lisa/setup/session-manager.md +1 -1
  130. package/plugins/lisa-copilot/rules/reference/config-resolution.md +39 -7
  131. package/plugins/lisa-copilot/skills/lisa-secrets-access/SKILL.md +5 -3
  132. package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
  133. package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/validate-config.mjs +47 -26
  134. package/plugins/lisa-copilot/skills/lisa-session-manager/SKILL.md +197 -54
  135. package/plugins/lisa-copilot/skills/lisa-session-manager/references/runbook-template.md +23 -10
  136. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
  137. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
  138. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
  139. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
  140. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
  141. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
  142. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
  143. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
  144. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
  145. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/config.mjs +39 -11
  146. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
  147. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
  148. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
  149. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
  150. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/launch.mjs +69 -60
  151. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
  152. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
  153. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
  154. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
  155. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
  156. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
  157. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
  158. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
  159. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/token.mjs +39 -120
  160. package/plugins/lisa-copilot/skills/lisa-setup-session-manager/SKILL.md +82 -19
  161. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  162. package/plugins/lisa-cursor/commands/lisa/session-manager.md +1 -1
  163. package/plugins/lisa-cursor/commands/lisa/setup/session-manager.md +1 -1
  164. package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +39 -7
  165. package/plugins/lisa-cursor/skills/lisa-secrets-access/SKILL.md +5 -3
  166. package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
  167. package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/validate-config.mjs +47 -26
  168. package/plugins/lisa-cursor/skills/lisa-session-manager/SKILL.md +197 -54
  169. package/plugins/lisa-cursor/skills/lisa-session-manager/references/runbook-template.md +23 -10
  170. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
  171. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
  172. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
  173. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
  174. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
  175. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
  176. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
  177. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
  178. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
  179. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/config.mjs +39 -11
  180. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
  181. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
  182. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
  183. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
  184. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/launch.mjs +69 -60
  185. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
  186. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
  187. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
  188. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
  189. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
  190. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
  191. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
  192. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
  193. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/token.mjs +39 -120
  194. package/plugins/lisa-cursor/skills/lisa-setup-session-manager/SKILL.md +82 -19
  195. package/plugins/lisa-devin/.claude-plugin/plugin.json +1 -1
  196. package/plugins/lisa-devin/.devin-plugin/plugin.json +1 -1
  197. package/plugins/lisa-devin/rules/reference/config-resolution.md +39 -7
  198. package/plugins/lisa-devin/skills/lisa-secrets-access/SKILL.md +5 -3
  199. package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
  200. package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/validate-config.mjs +47 -26
  201. package/plugins/lisa-devin/skills/lisa-session-manager/SKILL.md +197 -54
  202. package/plugins/lisa-devin/skills/lisa-session-manager/references/runbook-template.md +23 -10
  203. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
  204. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
  205. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
  206. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
  207. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
  208. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
  209. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
  210. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
  211. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
  212. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/config.mjs +39 -11
  213. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
  214. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
  215. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
  216. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
  217. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/launch.mjs +69 -60
  218. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
  219. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
  220. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
  221. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
  222. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
  223. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
  224. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
  225. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
  226. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/token.mjs +39 -120
  227. package/plugins/lisa-devin/skills/lisa-setup-session-manager/SKILL.md +82 -19
  228. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  229. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  230. package/plugins/lisa-expo-agy/plugin.json +1 -1
  231. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  232. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  233. package/plugins/lisa-expo-devin/.claude-plugin/plugin.json +1 -1
  234. package/plugins/lisa-expo-devin/.devin-plugin/plugin.json +1 -1
  235. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  236. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  237. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  238. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  239. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  240. package/plugins/lisa-harper-fabric-devin/.claude-plugin/plugin.json +1 -1
  241. package/plugins/lisa-harper-fabric-devin/.devin-plugin/plugin.json +1 -1
  242. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  243. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  244. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  245. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  246. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  247. package/plugins/lisa-nestjs-devin/.claude-plugin/plugin.json +1 -1
  248. package/plugins/lisa-nestjs-devin/.devin-plugin/plugin.json +1 -1
  249. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  250. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  251. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  252. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  253. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  254. package/plugins/lisa-openclaw-devin/.claude-plugin/plugin.json +1 -1
  255. package/plugins/lisa-openclaw-devin/.devin-plugin/plugin.json +1 -1
  256. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  257. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  258. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  259. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  260. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  261. package/plugins/lisa-phaser-devin/.claude-plugin/plugin.json +1 -1
  262. package/plugins/lisa-phaser-devin/.devin-plugin/plugin.json +1 -1
  263. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  264. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  265. package/plugins/lisa-rails-agy/plugin.json +1 -1
  266. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  267. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  268. package/plugins/lisa-rails-devin/.claude-plugin/plugin.json +1 -1
  269. package/plugins/lisa-rails-devin/.devin-plugin/plugin.json +1 -1
  270. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  271. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  272. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  273. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  274. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  275. package/plugins/lisa-typescript-devin/.claude-plugin/plugin.json +1 -1
  276. package/plugins/lisa-typescript-devin/.devin-plugin/plugin.json +1 -1
  277. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  278. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  279. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  280. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  281. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  282. package/plugins/lisa-wiki-devin/.claude-plugin/plugin.json +1 -1
  283. package/plugins/lisa-wiki-devin/.devin-plugin/plugin.json +1 -1
  284. package/plugins/src/base/commands/session-manager.md +1 -1
  285. package/plugins/src/base/commands/setup/session-manager.md +1 -1
  286. package/plugins/src/base/rules/reference/config-resolution.md +39 -7
  287. package/plugins/src/base/skills/lisa-secrets-access/SKILL.md +5 -3
  288. package/plugins/src/base/skills/lisa-secrets-access/scripts/providers.mjs +121 -3
  289. package/plugins/src/base/skills/lisa-secrets-access/scripts/validate-config.mjs +47 -26
  290. package/plugins/src/base/skills/lisa-session-manager/SKILL.md +197 -54
  291. package/plugins/src/base/skills/lisa-session-manager/references/runbook-template.md +23 -10
  292. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-cycle.mjs +21 -3
  293. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-human.mjs +199 -0
  294. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-manager-token.mjs +99 -0
  295. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-manager.mjs +52 -52
  296. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-queue.mjs +51 -139
  297. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-runtime.mjs +5 -3
  298. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-setup.mjs +94 -14
  299. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-status.mjs +73 -0
  300. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-usage.mjs +105 -15
  301. package/plugins/src/base/skills/lisa-session-manager/scripts/config.mjs +39 -11
  302. package/plugins/src/base/skills/lisa-session-manager/scripts/human-answer.mjs +162 -0
  303. package/plugins/src/base/skills/lisa-session-manager/scripts/human-needed.mjs +198 -0
  304. package/plugins/src/base/skills/lisa-session-manager/scripts/json-input.mjs +34 -0
  305. package/plugins/src/base/skills/lisa-session-manager/scripts/launch-shell.mjs +115 -0
  306. package/plugins/src/base/skills/lisa-session-manager/scripts/launch.mjs +69 -60
  307. package/plugins/src/base/skills/lisa-session-manager/scripts/ledger-questions.mjs +252 -0
  308. package/plugins/src/base/skills/lisa-session-manager/scripts/ledger.mjs +22 -56
  309. package/plugins/src/base/skills/lisa-session-manager/scripts/queue-github.mjs +117 -0
  310. package/plugins/src/base/skills/lisa-session-manager/scripts/session-manager.mjs +17 -5
  311. package/plugins/src/base/skills/lisa-session-manager/scripts/token-migrate.mjs +180 -0
  312. package/plugins/src/base/skills/lisa-session-manager/scripts/token-provider.mjs +150 -0
  313. package/plugins/src/base/skills/lisa-session-manager/scripts/token-read.mjs +166 -0
  314. package/plugins/src/base/skills/lisa-session-manager/scripts/token-store.mjs +248 -0
  315. package/plugins/src/base/skills/lisa-session-manager/scripts/token.mjs +39 -120
  316. package/plugins/src/base/skills/lisa-setup-session-manager/SKILL.md +82 -19
  317. package/rails/copy-overwrite/scripts/lisa-rspec-shards.mjs +747 -0
  318. package/rails/copy-overwrite/scripts/lisa_rspec_shard_recorder.rb +143 -0
  319. package/rails/copy-overwrite/scripts/lisa_simplecov_collate.rb +51 -0
  320. package/rails/create-only/.github/workflows/ci.yml +4 -0
  321. package/scripts/two-channel-couplings.json +89 -7
  322. package/typescript/copy-overwrite/.prettierignore +1 -0
@@ -30,8 +30,10 @@ export { detectRepoLayout, detectRepos, draftHints } from "./repo-detect.mjs";
30
30
  /**
31
31
  * Documented defaults for every optional key.
32
32
  *
33
- * `name` and `notify.stateDir` have none: both are per-project, and a shared
34
- * default for either is how two projects end up fighting over one bot.
33
+ * `notify.provider` defaults to `"none"`: a notification channel is optional
34
+ * (#4555), and an absent `notify` block means the manager runs without one.
35
+ * `name` and `notify.stateDir` have no default: both are per-project, and a
36
+ * shared default for either is how two projects end up fighting over one bot.
35
37
  * `buildDefaultBlock` derives them from the project slug at setup time.
36
38
  */
37
39
  export const SESSION_MANAGER_DEFAULTS = Object.freeze({
@@ -48,9 +50,24 @@ export const SESSION_MANAGER_DEFAULTS = Object.freeze({
48
50
  maxAttempts: 3,
49
51
  requestionHours: 4,
50
52
  }),
51
- notify: Object.freeze({ provider: "telegram" }),
53
+ notify: Object.freeze({ provider: "none" }),
52
54
  });
53
55
 
56
+ /** The `notify` choices `buildDefaultBlock` (and `init-config --notify`) take. */
57
+ export const NOTIFY_CHOICES = Object.freeze(["none", "telegram"]);
58
+
59
+ /**
60
+ * The notification provider a resolved (or raw) block runs with.
61
+ * @param {{ notify?: { provider?: unknown } } | null | undefined} config
62
+ * Resolved or raw `sessionManager` block.
63
+ * @returns {string} `"telegram"`, `"none"`, or whatever unsupported value
64
+ * was declared (validation refuses it).
65
+ */
66
+ export function notifyProvider(config) {
67
+ const provider = config?.notify?.provider;
68
+ return provider === undefined || provider === null ? "none" : provider;
69
+ }
70
+
54
71
  /**
55
72
  * @typedef {{ root: string, entries: Record<string, { hints: string[] }> }} ReposBlock
56
73
  * @typedef {{ maxWorkers: number, gbPerWorker: number, reserveGb: number,
@@ -75,7 +92,7 @@ export const SESSION_MANAGER_DEFAULTS = Object.freeze({
75
92
  * repos?: ReposBlock,
76
93
  * capacity: CapacityBlock,
77
94
  * stall: StallBlock,
78
- * notify: { provider: string, stateDir: string },
95
+ * notify: { provider: string, stateDir?: string },
79
96
  * }} SessionManagerBlock
80
97
  */
81
98
 
@@ -160,10 +177,14 @@ function normalizeSlug(projectSlug) {
160
177
  * name: valid either way, and setup asks the operator to review them.
161
178
  * `reposRoot` is where the repos were found (`detectRepoLayout`): `projects`
162
179
  * by default, or `.` when they sit directly under the project root.
180
+ * `notify` is the operator's channel choice: `"none"` (the default) writes
181
+ * `{provider:"none"}` and no state dir; `"telegram"` adds the per-project
182
+ * state dir derived from the slug.
163
183
  * @param {{ projectSlug: string, detectedRepos?: string[],
164
- * hints?: Record<string, string[]>, reposRoot?: string }} input Project
165
- * slug, the output of `detectRepos`, drafted hints per repo, and the repos
166
- * root they were found under.
184
+ * hints?: Record<string, string[]>, reposRoot?: string,
185
+ * notify?: string }} input Project slug, the output of `detectRepos`,
186
+ * drafted hints per repo, the repos root they were found under, and the
187
+ * notification channel.
167
188
  * @returns {SessionManagerBlock} A block `validateSessionManager` accepts.
168
189
  */
169
190
  export function buildDefaultBlock({
@@ -171,7 +192,14 @@ export function buildDefaultBlock({
171
192
  detectedRepos = [],
172
193
  hints = {},
173
194
  reposRoot = DEFAULT_REPOS_ROOT,
195
+ notify = SESSION_MANAGER_DEFAULTS.notify.provider,
174
196
  }) {
197
+ if (!NOTIFY_CHOICES.includes(notify)) {
198
+ throw new Error(
199
+ `notify ${JSON.stringify(notify)} is not supported. Use one of: ` +
200
+ `${NOTIFY_CHOICES.join(", ")}.`
201
+ );
202
+ }
175
203
  const slug = normalizeSlug(projectSlug);
176
204
  const repos = [...detectedRepos].sort();
177
205
  return {
@@ -192,10 +220,10 @@ export function buildDefaultBlock({
192
220
  : {}),
193
221
  capacity: { ...SESSION_MANAGER_DEFAULTS.capacity },
194
222
  stall: { ...SESSION_MANAGER_DEFAULTS.stall },
195
- notify: {
196
- provider: SESSION_MANAGER_DEFAULTS.notify.provider,
197
- stateDir: `~/.claude/channels/telegram-${slug}`,
198
- },
223
+ notify:
224
+ notify === "telegram"
225
+ ? { provider: notify, stateDir: `~/.claude/channels/telegram-${slug}` }
226
+ : { provider: notify },
199
227
  };
200
228
  }
201
229
 
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Reading a human's answer back off a work item (CodySwannGT/lisa#4555,
3
+ * part 3). Pure: no I/O, no clock, no config reads.
4
+ *
5
+ * Tracker text is untrusted, and in a single-user setup the operator's
6
+ * tracker identity is also the one the workers and the manager post as. So
7
+ * an operator-id match alone proves nothing: {@link isMachineAuthoredComment}
8
+ * runs first and is the ONE place that decides a comment is a machine's.
9
+ * It excludes a comment that opens with a Lisa or Claude machine tag
10
+ * (`[claude-sync] …`, `[lisa-…] …`), holds a `[lisa-` marker or a
11
+ * `<!-- lisa:` marker, says "Written by Claude", or carries the Claude Code
12
+ * attribution footer; and any comment whose id the manager recorded as one it
13
+ * posted itself (`ledger marked --comment-id`), whatever it says. Markdown
14
+ * blockquote lines are ignored before the markers are tested, so a human's
15
+ * "Quote reply" of the question (or of a machine comment) is still a human's
16
+ * answer, and an ordinary reply such as `[x] option 1` is not a machine tag
17
+ * (#4561). Only then is the author matched, on stable ids only: a GitHub
18
+ * login or numeric id, a Jira accountId, a Linear user id, never a display
19
+ * name or email, which anyone can set. The text is carried as data.
20
+ *
21
+ * The question the answer must follow is the comment whose id the manager
22
+ * recorded last; with no recorded id, the latest comment that STARTS with
23
+ * the question marker and is by an allowed answerer. A marker merely quoted,
24
+ * or posted by anyone else, never moves that anchor, so nobody can hide an
25
+ * answer by posting the marker after it.
26
+ * @module session-manager/human-answer
27
+ */
28
+ import { QUESTION_MARKER } from "./human-needed.mjs";
29
+
30
+ /** Body markers that make a comment a machine's, never a human's answer. */
31
+ const MACHINE_MARKERS = [
32
+ /^\s*\[(lisa|claude)-[a-z0-9-]*\]/i,
33
+ /\[lisa-/i,
34
+ /<!--\s*lisa:/i,
35
+ /Written by Claude/i,
36
+ /Generated with Claude Code/i,
37
+ /🤖 Generated with/,
38
+ ];
39
+
40
+ /** A Markdown blockquote line: quoted text, never the commenter's own. */
41
+ const QUOTE_LINE = /^\s*>/;
42
+
43
+ /** The comment fields that carry its tracker id. */
44
+ const COMMENT_ID_KEYS = ["id", "node_id"];
45
+
46
+ /** The author fields that are stable ids, never settable display text. */
47
+ const STABLE_ID_KEYS = ["login", "id", "accountId"];
48
+
49
+ /**
50
+ * The string or number values of `keys` on `value`, as strings.
51
+ * @param {unknown} value - Object to read
52
+ * @param {readonly string[]} keys - Keys to read
53
+ * @returns {string[]} Values
54
+ */
55
+ function idsOf(value, keys) {
56
+ if (value === null || typeof value !== "object") return [];
57
+ return keys
58
+ .map(key => value[key])
59
+ .filter(id => typeof id === "string" || typeof id === "number")
60
+ .map(String);
61
+ }
62
+
63
+ /**
64
+ * The comment body, whatever the tracker calls it.
65
+ * @param {Record<string, unknown>} comment - One comment
66
+ * @returns {string} Body text
67
+ */
68
+ function bodyOf(comment) {
69
+ const body = comment?.body ?? comment?.text ?? "";
70
+ return typeof body === "string" ? body : "";
71
+ }
72
+
73
+ /**
74
+ * The comment body without its Markdown blockquote lines.
75
+ * @param {string} text - Body text
76
+ * @returns {string} The commenter's own text
77
+ */
78
+ function unquoted(text) {
79
+ return text
80
+ .split("\n")
81
+ .filter(line => !QUOTE_LINE.test(line))
82
+ .join("\n");
83
+ }
84
+
85
+ /**
86
+ * Whether a comment was written by a machine (an agent, a Lisa flow, or the
87
+ * manager itself) and so can never be a human's answer.
88
+ * @param {Record<string, unknown>} comment - One tracker comment
89
+ * @param {{managerCommentIds?: readonly (string|number)[]}} [options] - Ids
90
+ * of comments the manager posted (from the ledger)
91
+ * @returns {boolean} True for a machine's comment
92
+ */
93
+ export function isMachineAuthoredComment(comment, options = {}) {
94
+ const mine = new Set((options.managerCommentIds ?? []).map(String));
95
+ if (idsOf(comment, COMMENT_ID_KEYS).some(id => mine.has(id))) return true;
96
+ const text = unquoted(bodyOf(comment));
97
+ return MACHINE_MARKERS.some(pattern => pattern.test(text));
98
+ }
99
+
100
+ /**
101
+ * Index of the question comment an answer must follow: the comment whose id
102
+ * the manager recorded last, when it recorded any; otherwise the latest
103
+ * comment that starts with the question marker and is by an allowed author.
104
+ * @param {Record<string, unknown>[]} rows - Comments, chronological
105
+ * @param {readonly (string|number)[]} managerCommentIds - Recorded ids
106
+ * @param {(row: Record<string, unknown>) => string|undefined} authorOf -
107
+ * The row's allowed stable author id, if any
108
+ * @returns {number} Index, or -1 when there is no question comment
109
+ */
110
+ function questionIndex(rows, managerCommentIds, authorOf) {
111
+ if (managerCommentIds.length > 0) {
112
+ const latest = String(managerCommentIds.at(-1));
113
+ return rows.findLastIndex(row =>
114
+ idsOf(row, COMMENT_ID_KEYS).includes(latest)
115
+ );
116
+ }
117
+ return rows.findLastIndex(
118
+ row =>
119
+ bodyOf(row).trimStart().startsWith(QUESTION_MARKER) &&
120
+ authorOf(row) !== undefined
121
+ );
122
+ }
123
+
124
+ /**
125
+ * Pick the human's answer from an item's comments: the first comment after
126
+ * the manager's latest question comment ({@link questionIndex}) that is not
127
+ * a machine's ({@link isMachineAuthoredComment}), has text of its own
128
+ * outside quoted lines (a comment made only of `>` lines quotes someone
129
+ * else and answers nothing), and whose author's stable id is allowed. Comments are taken in the order given (chronological).
130
+ * @param {{comments: unknown, allowed: readonly string[],
131
+ * managerCommentIds?: readonly (string|number)[]}} input - The item's
132
+ * comments, the answerer ids, and the ids the manager posted
133
+ * @returns {{answer: {text: string, author: string, at: string|null} | null,
134
+ * reason: string}} The answer, or null with why there is none
135
+ */
136
+ export function pickItemAnswer({ comments, allowed, managerCommentIds = [] }) {
137
+ if (!Array.isArray(comments)) {
138
+ throw new Error('stdin must be JSON {"comments":[...]}');
139
+ }
140
+ const rows = comments.filter(row => row && typeof row === "object");
141
+ const allow = new Set(allowed.map(String));
142
+ const authorOf = row =>
143
+ idsOf(row.author ?? row.user, STABLE_ID_KEYS).find(id => allow.has(id));
144
+ const asked = questionIndex(rows, managerCommentIds, authorOf);
145
+ if (asked === -1) return { answer: null, reason: "no-question-comment" };
146
+ const reply = rows
147
+ .slice(asked + 1)
148
+ .filter(row => !isMachineAuthoredComment(row, { managerCommentIds }))
149
+ .find(
150
+ row => authorOf(row) !== undefined && unquoted(bodyOf(row)).trim() !== ""
151
+ );
152
+ if (!reply) return { answer: null, reason: "no-answer-yet" };
153
+ const at = reply.createdAt ?? reply.created ?? reply.created_at ?? null;
154
+ return {
155
+ answer: {
156
+ text: bodyOf(reply).trim(),
157
+ author: authorOf(reply),
158
+ at: typeof at === "string" ? at : null,
159
+ },
160
+ reason: "answered",
161
+ };
162
+ }
@@ -0,0 +1,198 @@
1
+ /**
2
+ * "Mark and move on" (CodySwannGT/lisa#4555, part 3): what the manager writes
3
+ * on a work item that cannot progress without a human, and how it reads the
4
+ * human's answer back off the item. Pure: no I/O, no clock, no config reads.
5
+ *
6
+ * Four cases need a human: a worker's question, an ambiguous repo route, an
7
+ * item `exhausted` after `stall.maxAttempts`, and a design or product
8
+ * decision. In each the manager marks the item with the configured
9
+ * human-needed marker, posts the question on the item with enough context to
10
+ * answer it there, records it pending in the ledger, and keeps dispatching
11
+ * other work. One item waiting on a person never stops the queue.
12
+ *
13
+ * The marker is the tracker's configured human-needed label; with no label
14
+ * for the tracker it is the configured `blocked` role. An `exhausted` item is
15
+ * also moved to the `blocked` role, because no worker is building it any
16
+ * more. Names always come from config, never from this file.
17
+ *
18
+ * Reading the answer back off the item lives in human-answer.mjs.
19
+ * @module session-manager/human-needed
20
+ */
21
+
22
+ /** The four reasons an item can need a human. */
23
+ export const HUMAN_NEEDED_KINDS = Object.freeze([
24
+ "worker-question",
25
+ "repo-route",
26
+ "exhausted",
27
+ "decision",
28
+ ]);
29
+
30
+ /** Marks the manager's question comment, so its answer can be found after it. */
31
+ export const QUESTION_MARKER = "[lisa-session-manager-question]";
32
+
33
+ /** What each kind is called in the comment's first line. */
34
+ const KIND_TITLES = {
35
+ "worker-question": "the worker building it has a question",
36
+ "repo-route": "it is not clear which repository it belongs to",
37
+ exhausted: "every automatic attempt to build it stopped without finishing",
38
+ decision: "it needs a design or product decision",
39
+ };
40
+
41
+ /**
42
+ * Credential shapes refused in question and answer text. Best effort: a match
43
+ * is refused, a miss is not proof the text is safe. A secret belongs in the
44
+ * project's secrets provider, never in a tracker comment or the ledger.
45
+ */
46
+ const CREDENTIAL_SHAPES = [
47
+ ["Telegram bot token", /\b\d{8,10}:[A-Za-z0-9_-]{30,}\b/],
48
+ ["GitHub token", /\b(?:gh[pousr]_[A-Za-z0-9]{30,}|github_pat_\w{30,})/],
49
+ ["AWS access key", /\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/],
50
+ ["Slack token", /\bxox[abprs]-[A-Za-z0-9-]{10,}/],
51
+ ["API key", /\bsk-(?:ant-)?[A-Za-z0-9_-]{20,}/],
52
+ ["private key", /-----BEGIN [A-Z ]*PRIVATE KEY-----/],
53
+ ["bearer token", /\bbearer\s+[A-Za-z0-9._~+/=-]{20,}/i],
54
+ ];
55
+
56
+ /**
57
+ * The name of the first credential shape found in `text`, or null.
58
+ * @param {string} text - Question or answer text
59
+ * @returns {string | null} Shape name
60
+ */
61
+ export function findCredentialShape(text) {
62
+ const found = CREDENTIAL_SHAPES.find(([, pattern]) => pattern.test(text));
63
+ return found ? found[0] : null;
64
+ }
65
+
66
+ /**
67
+ * The short label an operator starts a Telegram answer with: `#12` for a
68
+ * GitHub ref (`owner/repo#12`), the key itself for Jira and Linear.
69
+ * @param {string} ref - Item ref
70
+ * @returns {string} Answer label
71
+ */
72
+ export function answerLabel(ref) {
73
+ const hash = ref.lastIndexOf("#");
74
+ return hash > 0 ? ref.slice(hash) : ref;
75
+ }
76
+
77
+ /**
78
+ * The comment the manager posts on the item. It names the ref, the reason,
79
+ * the question verbatim, and every way to answer, in plain English with
80
+ * nothing a reader has to paste and no local path. A reply on the item is
81
+ * offered only when someone may answer there (`answerers`): with no
82
+ * operator or trusted author configured, replies are never picked up.
83
+ * @param {{ref: string, kind: string, text: string, label: string,
84
+ * markerText: string, channel: boolean, answerers: boolean}} input - What
85
+ * to say
86
+ * @returns {string} Comment body
87
+ */
88
+ function questionComment({
89
+ ref,
90
+ kind,
91
+ text,
92
+ label,
93
+ markerText,
94
+ channel,
95
+ answerers,
96
+ }) {
97
+ const ways = [
98
+ answerers
99
+ ? `- Reply in a comment on this item, in plain words (do not start it with a bracketed tag). The session manager reads the first reply from an operator or trusted author on its next cycle.`
100
+ : `- Replies on this item are not picked up: no operator or trusted author is configured for the session manager.`,
101
+ `- On the machine running the session manager, run \`session-manager.mjs status\` and use the \`answerCommand\` it lists for this item.`,
102
+ ...(channel
103
+ ? [`- Or on Telegram: \`Answer with: ${label} <your answer>\`.`]
104
+ : []),
105
+ ];
106
+ return [
107
+ `${QUESTION_MARKER} ${ref} needs a human: ${KIND_TITLES[kind]}.`,
108
+ "",
109
+ text,
110
+ "",
111
+ "How to answer:",
112
+ ...ways,
113
+ "",
114
+ `The session manager keeps working on other items meanwhile. Once the answer is delivered it removes ${markerText} and says so here.`,
115
+ ].join("\n");
116
+ }
117
+
118
+ /**
119
+ * The marker for an item: the human-needed label, else the `blocked` role.
120
+ * @param {string | undefined} label - Configured human-needed label
121
+ * @param {string | undefined} blocked - Configured `blocked` role value
122
+ * @returns {{type: "label"|"role", value: string}} Marker
123
+ */
124
+ function chooseMarker(label, blocked) {
125
+ if (label) return { type: "label", value: label };
126
+ if (blocked) return { type: "role", value: blocked };
127
+ throw new Error(
128
+ "no human-needed label and no blocked role is configured for this tracker; configure one (see the config-resolution rule) so the manager can mark the item"
129
+ );
130
+ }
131
+
132
+ /**
133
+ * Plan how to mark one item human-needed. Throws (a setup refusal) on an
134
+ * unknown kind, empty or credential-shaped text, no marker, or an `exhausted`
135
+ * item with no `blocked` role to move it to.
136
+ * @param {{ref: string, kind: string, text: string, label?: string,
137
+ * blocked?: string, channel?: boolean, answerers?: boolean}} input - The
138
+ * item, why, the question, the configured human-needed label and `blocked`
139
+ * role value, whether a notification channel exists, and whether anyone
140
+ * (operatorIds / trustedAuthorIds) may answer on the item
141
+ * @returns {{ref: string, kind: string, marker: {type: string, value: string},
142
+ * move?: {role: "blocked", value: string}, comment: string,
143
+ * answerWith: {item: string|null, cli: string, channel: string|null}}} Plan
144
+ */
145
+ export function planMark({
146
+ ref,
147
+ kind,
148
+ text,
149
+ label,
150
+ blocked,
151
+ channel = false,
152
+ answerers = true,
153
+ }) {
154
+ if (!HUMAN_NEEDED_KINDS.includes(kind)) {
155
+ throw new Error(`--kind must be one of: ${HUMAN_NEEDED_KINDS.join(", ")}.`);
156
+ }
157
+ const question = String(text ?? "").trim();
158
+ if (question === "") throw new Error("the question or reason is empty");
159
+ const shape = findCredentialShape(question);
160
+ if (shape) {
161
+ throw new Error(
162
+ `the text looks like it holds a credential (${shape}); it would be posted on the item. Remove it and keep secrets in the secrets provider.`
163
+ );
164
+ }
165
+ const marker = chooseMarker(label, blocked);
166
+ if (kind === "exhausted" && !blocked) {
167
+ throw new Error(
168
+ "an exhausted item moves to the blocked role, and none is configured for this tracker"
169
+ );
170
+ }
171
+ const move =
172
+ kind === "exhausted" ? { role: "blocked", value: blocked } : undefined;
173
+ const short = answerLabel(ref);
174
+ const markerText =
175
+ marker.type === "label"
176
+ ? `the \`${marker.value}\` label`
177
+ : `the hold (it moves the item out of \`${marker.value}\`)`;
178
+ return {
179
+ ref,
180
+ kind,
181
+ marker,
182
+ ...(move ? { move } : {}),
183
+ comment: questionComment({
184
+ ref,
185
+ kind,
186
+ text: question,
187
+ label: short,
188
+ markerText,
189
+ channel,
190
+ answerers,
191
+ }),
192
+ answerWith: {
193
+ item: answerers ? `a comment on ${ref}` : null,
194
+ cli: `the answerCommand that session-manager.mjs status lists for ${ref} (answer on stdin)`,
195
+ channel: channel ? `Answer with: ${short} <your answer>` : null,
196
+ },
197
+ };
198
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * JSON input for the session-manager CLI: parse text or read a file named by
3
+ * a flag, exiting 2 with the source named when it is not valid JSON. Split
4
+ * out of cli-queue.mjs to keep it under the line budget (#4555); cli-queue.mjs
5
+ * re-exports both, so callers keep their import.
6
+ * @module session-manager/json-input
7
+ */
8
+ import { readFileSync } from "node:fs";
9
+
10
+ import { CliError, asSetupError } from "./cli-runtime.mjs";
11
+
12
+ /**
13
+ * Parse JSON, or exit 2 naming the source.
14
+ * @param {string} text - JSON text
15
+ * @param {string} source - Where it came from
16
+ * @returns {any} Parsed value
17
+ */
18
+ export function parseJson(text, source) {
19
+ try {
20
+ return JSON.parse(text);
21
+ } catch (error) {
22
+ throw new CliError(`${source} is not valid JSON: ${error.message}`, 2);
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Read a JSON file named by a flag, or exit 2.
28
+ * @param {string} file - Path
29
+ * @returns {any} Parsed value
30
+ */
31
+ export function readJsonFile(file) {
32
+ const text = asSetupError(() => readFileSync(file, "utf8"));
33
+ return parseJson(text, file);
34
+ }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * The two shell-facing renderings of the manager's tmux launch, split out of
3
+ * launch.mjs to keep it under the line budget (#4555):
4
+ * - {@link buildForegroundCommand}: the display-only line an operator runs to
5
+ * start the manager in the foreground and see claude's own error;
6
+ * - {@link buildSourcedLaunch}: the `tmux start-server ; source-file -` call
7
+ * that carries secret session variables (the Telegram bot token) on stdin,
8
+ * never in argv.
9
+ *
10
+ * Both take the argv `buildManagerLaunch` returned. Everything here is pure:
11
+ * nothing reads the environment or runs a process. launch.mjs re-exports
12
+ * every export, so callers keep one import.
13
+ * @module launch-shell
14
+ */
15
+ import { LaunchError } from "./state-dir.mjs";
16
+
17
+ /** A `'` inside a single-quoted shell word: close, escaped quote, reopen. */
18
+ const ESCAPED_QUOTE = "'\\''";
19
+
20
+ /** An argument a POSIX shell reads back unchanged without quotes. */
21
+ const SHELL_SAFE = /^[A-Za-z0-9_@%+=:,./-]+$/;
22
+
23
+ /**
24
+ * Quote one argument for a POSIX shell, for display only.
25
+ * @param {string} value - Argument
26
+ * @returns {string} Single-quoted argument
27
+ */
28
+ function shellQuote(value) {
29
+ return `'${value.replaceAll("'", ESCAPED_QUOTE)}'`;
30
+ }
31
+
32
+ /**
33
+ * The shell line an operator runs to start the manager in the foreground,
34
+ * from the tmux argv `buildManagerLaunch` (launch.mjs) returned: same cwd, the same
35
+ * two session variables, the same claude argv. `start` prints it when the
36
+ * tmux session dies at once, because tmux closes the window and claude's own
37
+ * error goes with it. It is display text only and is never executed here.
38
+ * The token is not in the argv, so it is not in this line either. When the
39
+ * token comes from a secrets provider, `shellVars` adds it as a command the
40
+ * operator's own shell runs (`$(node …/resolve-secret.mjs get <NAME>)`), so
41
+ * the line names the secret and never holds its value (#4555).
42
+ * @param {readonly string[]} argv - Result of `buildManagerLaunch` (launch.mjs)
43
+ * @param {Record<string, string>} [shellVars] - Extra variables, each value
44
+ * double-quoted so the operator's shell expands it
45
+ * @returns {string} One shell line
46
+ */
47
+ export function buildForegroundCommand(argv, shellVars = {}) {
48
+ const claudeAt = argv.indexOf("claude");
49
+ const head = argv.slice(0, claudeAt);
50
+ const cwd = head[head.indexOf("-c") + 1];
51
+ const vars = head
52
+ .filter((_, index) => head[index - 1] === "-e")
53
+ .map(entry => {
54
+ const at = entry.indexOf("=");
55
+ return `${entry.slice(0, at)}=${shellQuote(entry.slice(at + 1))}`;
56
+ });
57
+ const expanded = Object.entries(shellVars).map(
58
+ ([name, value]) => `${name}="${value}"`
59
+ );
60
+ const command = argv
61
+ .slice(claudeAt)
62
+ .map(arg => (SHELL_SAFE.test(arg) ? arg : shellQuote(arg)));
63
+ return `cd ${shellQuote(cwd)} && ${[...vars, ...expanded, ...command].join(" ")}`;
64
+ }
65
+
66
+ /**
67
+ * Quote one word for tmux's command parser: single quotes, inside which
68
+ * nothing is special, and `'"'"'` for a quote (close, a double-quoted quote,
69
+ * reopen; tmux joins adjacent quoted runs into one word).
70
+ * @param {string} word - Word
71
+ * @returns {string} Quoted word
72
+ */
73
+ export function tmuxQuote(word) {
74
+ return `'${String(word).replaceAll("'", `'"'"'`)}'`;
75
+ }
76
+
77
+ /**
78
+ * The tmux call that starts the manager with secret session variables
79
+ * (the Telegram bot token from the secrets provider, #4555) that must never
80
+ * reach argv, where `ps` shows them to every user on the host.
81
+ *
82
+ * `tmux start-server ; source-file -` reads the `new-session` command from
83
+ * stdin, so the argv holds no value. The `-e` variables join the session's
84
+ * own environment only, the same as the ones `buildManagerLaunch` (launch.mjs)
85
+ * already passes. A newline in any word would end the command early and run
86
+ * the rest as a second tmux command, so it is refused.
87
+ * @param {readonly string[]} argv - Result of `buildManagerLaunch` (launch.mjs)
88
+ * @param {Record<string, string>} sessionVars - Extra session variables
89
+ * @returns {{ argv: string[], input: string }} argv for `spawnSync("tmux")`
90
+ * and the line for its stdin
91
+ */
92
+ export function buildSourcedLaunch(argv, sessionVars) {
93
+ if (argv[0] !== "new-session") {
94
+ throw new LaunchError("buildSourcedLaunch needs a new-session argv.");
95
+ }
96
+ const claudeAt = argv.indexOf("claude");
97
+ const words = [
98
+ ...argv.slice(0, claudeAt),
99
+ ...Object.entries(sessionVars).flatMap(([name, value]) => [
100
+ "-e",
101
+ `${name}=${value}`,
102
+ ]),
103
+ ...argv.slice(claudeAt),
104
+ ];
105
+ if (words.some(word => /[\r\n]/.test(word))) {
106
+ throw new LaunchError(
107
+ "A launch argument contains a newline, which would end the tmux " +
108
+ "command early. Nothing was started."
109
+ );
110
+ }
111
+ return {
112
+ argv: ["start-server", ";", "source-file", "-"],
113
+ input: `${words.map(tmuxQuote).join(" ")}\n`,
114
+ };
115
+ }