switchroom 0.5.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 (718) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +447 -0
  3. package/bin/autoaccept.exp +81 -0
  4. package/bin/boot-self-test.sh +149 -0
  5. package/bin/bridge-watchdog.sh +967 -0
  6. package/bin/handoff-briefing.sh +206 -0
  7. package/bin/run-hook.sh +228 -0
  8. package/bin/switchroom.ts +4 -0
  9. package/bin/timezone-hook.sh +67 -0
  10. package/bin/user-profile-refresh-hook.sh +38 -0
  11. package/bin/workspace-dynamic-hook.sh +142 -0
  12. package/bin/workspace-stable-hook.sh +57 -0
  13. package/dist/cli/autoaccept-poll.js +118 -0
  14. package/dist/cli/switchroom.js +48557 -0
  15. package/package.json +95 -0
  16. package/profiles/_base/settings.json.hbs +15 -0
  17. package/profiles/_base/start.sh.hbs +383 -0
  18. package/profiles/_shared/telegram-style.md.hbs +140 -0
  19. package/profiles/coding/CLAUDE.md.hbs +57 -0
  20. package/profiles/coding/skills/architecture/SKILL.md +70 -0
  21. package/profiles/coding/skills/code-review/SKILL.md +58 -0
  22. package/profiles/coding/workspace/SOUL.md.hbs +25 -0
  23. package/profiles/default/CLAUDE.md +238 -0
  24. package/profiles/default/CLAUDE.md.hbs +113 -0
  25. package/profiles/default/workspace/CLAUDE.md.hbs +126 -0
  26. package/profiles/default/workspace/HEARTBEAT.md.hbs +40 -0
  27. package/profiles/default/workspace/IDENTITY.md.hbs +32 -0
  28. package/profiles/default/workspace/MEMORY.md.hbs +29 -0
  29. package/profiles/default/workspace/SOUL.md.hbs +61 -0
  30. package/profiles/default/workspace/TOOLS.md.hbs +29 -0
  31. package/profiles/default/workspace/USER.md.hbs +52 -0
  32. package/profiles/default/workspace/memory/.gitkeep +0 -0
  33. package/profiles/executive-assistant/CLAUDE.md.hbs +51 -0
  34. package/profiles/executive-assistant/skills/daily-briefing/SKILL.md +55 -0
  35. package/profiles/executive-assistant/skills/meeting-prep/SKILL.md +58 -0
  36. package/profiles/executive-assistant/workspace/SOUL.md.hbs +25 -0
  37. package/profiles/health-coach/CLAUDE.md.hbs +45 -0
  38. package/profiles/health-coach/skills/check-in/SKILL.md +41 -0
  39. package/profiles/health-coach/skills/weekly-review/SKILL.md +53 -0
  40. package/profiles/health-coach/workspace/SOUL.md.hbs +25 -0
  41. package/skills/buildkite-agent-infrastructure/SKILL.md +302 -0
  42. package/skills/buildkite-agent-infrastructure/agents/openai.yaml +6 -0
  43. package/skills/buildkite-agent-infrastructure/assets/buildkite-icon-large.png +0 -0
  44. package/skills/buildkite-agent-infrastructure/assets/buildkite-icon-small.png +0 -0
  45. package/skills/buildkite-agent-infrastructure/references/audit-logging.md +87 -0
  46. package/skills/buildkite-agent-infrastructure/references/graphql-mutations.md +690 -0
  47. package/skills/buildkite-agent-infrastructure/references/instance-shapes.md +38 -0
  48. package/skills/buildkite-agent-infrastructure/references/pipeline-templates.md +73 -0
  49. package/skills/buildkite-agent-infrastructure/references/self-hosted-agents.md +137 -0
  50. package/skills/buildkite-agent-infrastructure/references/sso-saml.md +92 -0
  51. package/skills/buildkite-agent-runtime/SKILL.md +476 -0
  52. package/skills/buildkite-agent-runtime/agents/openai.yaml +6 -0
  53. package/skills/buildkite-agent-runtime/assets/buildkite-icon-large.png +0 -0
  54. package/skills/buildkite-agent-runtime/assets/buildkite-icon-small.png +0 -0
  55. package/skills/buildkite-agent-runtime/references/flag-reference.md +417 -0
  56. package/skills/buildkite-agent-runtime/references/patterns-and-recipes.md +555 -0
  57. package/skills/buildkite-api/SKILL.md +285 -0
  58. package/skills/buildkite-api/agents/openai.yaml +6 -0
  59. package/skills/buildkite-api/assets/buildkite-icon-large.png +0 -0
  60. package/skills/buildkite-api/assets/buildkite-icon-small.png +0 -0
  61. package/skills/buildkite-api/references/graphql-reference.md +195 -0
  62. package/skills/buildkite-api/references/patterns.md +44 -0
  63. package/skills/buildkite-api/references/webhooks.md +161 -0
  64. package/skills/buildkite-cli/SKILL.md +379 -0
  65. package/skills/buildkite-cli/agents/openai.yaml +6 -0
  66. package/skills/buildkite-cli/assets/buildkite-icon-large.png +0 -0
  67. package/skills/buildkite-cli/assets/buildkite-icon-small.png +0 -0
  68. package/skills/buildkite-cli/references/command-reference.md +181 -0
  69. package/skills/buildkite-migration/SKILL.md +182 -0
  70. package/skills/buildkite-pipelines/SKILL.md +464 -0
  71. package/skills/buildkite-pipelines/agents/openai.yaml +6 -0
  72. package/skills/buildkite-pipelines/assets/buildkite-icon-large.png +0 -0
  73. package/skills/buildkite-pipelines/assets/buildkite-icon-small.png +0 -0
  74. package/skills/buildkite-pipelines/examples/basic-pipeline.yml +24 -0
  75. package/skills/buildkite-pipelines/examples/optimized-pipeline.yml +100 -0
  76. package/skills/buildkite-pipelines/references/advanced-patterns.md +286 -0
  77. package/skills/buildkite-pipelines/references/retry-and-error-codes.md +131 -0
  78. package/skills/buildkite-pipelines/references/step-types-reference.md +225 -0
  79. package/skills/buildkite-secure-delivery/SKILL.md +168 -0
  80. package/skills/buildkite-secure-delivery/agents/openai.yaml +6 -0
  81. package/skills/buildkite-secure-delivery/assets/buildkite-icon-large.png +0 -0
  82. package/skills/buildkite-secure-delivery/assets/buildkite-icon-small.png +0 -0
  83. package/skills/buildkite-secure-delivery/references/oidc-cloud-providers.md +83 -0
  84. package/skills/buildkite-secure-delivery/references/package-publishing.md +100 -0
  85. package/skills/buildkite-test-engine/SKILL.md +239 -0
  86. package/skills/buildkite-test-engine/agents/openai.yaml +6 -0
  87. package/skills/buildkite-test-engine/assets/buildkite-icon-large.png +0 -0
  88. package/skills/buildkite-test-engine/assets/buildkite-icon-small.png +0 -0
  89. package/skills/buildkite-test-engine/examples/bktec-splitting.yml +16 -0
  90. package/skills/buildkite-test-engine/examples/collector-pipeline.yml +11 -0
  91. package/skills/buildkite-test-engine/references/collectors.md +198 -0
  92. package/skills/buildkite-test-engine/references/splitting-examples.md +93 -0
  93. package/skills/docx/LICENSE.txt +30 -0
  94. package/skills/docx/SKILL.md +590 -0
  95. package/skills/docx/VENDORED.md +32 -0
  96. package/skills/docx/scripts/__init__.py +1 -0
  97. package/skills/docx/scripts/accept_changes.py +135 -0
  98. package/skills/docx/scripts/comment.py +318 -0
  99. package/skills/docx/scripts/office/helpers/__init__.py +0 -0
  100. package/skills/docx/scripts/office/helpers/merge_runs.py +199 -0
  101. package/skills/docx/scripts/office/helpers/simplify_redlines.py +197 -0
  102. package/skills/docx/scripts/office/pack.py +159 -0
  103. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  104. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  105. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  106. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  107. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  108. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  109. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  110. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  111. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  112. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  113. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  114. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  115. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  116. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  117. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  118. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  119. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  120. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  121. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  122. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  123. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  124. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  125. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  126. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  127. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  128. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  129. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  130. package/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  131. package/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  132. package/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  133. package/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  134. package/skills/docx/scripts/office/schemas/mce/mc.xsd +75 -0
  135. package/skills/docx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
  136. package/skills/docx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
  137. package/skills/docx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
  138. package/skills/docx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  139. package/skills/docx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  140. package/skills/docx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  141. package/skills/docx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  142. package/skills/docx/scripts/office/soffice.py +183 -0
  143. package/skills/docx/scripts/office/unpack.py +132 -0
  144. package/skills/docx/scripts/office/validate.py +111 -0
  145. package/skills/docx/scripts/office/validators/__init__.py +15 -0
  146. package/skills/docx/scripts/office/validators/__pycache__/__init__.cpython-313.pyc +0 -0
  147. package/skills/docx/scripts/office/validators/__pycache__/base.cpython-313.pyc +0 -0
  148. package/skills/docx/scripts/office/validators/base.py +847 -0
  149. package/skills/docx/scripts/office/validators/docx.py +446 -0
  150. package/skills/docx/scripts/office/validators/pptx.py +275 -0
  151. package/skills/docx/scripts/office/validators/redlining.py +247 -0
  152. package/skills/docx/scripts/templates/comments.xml +3 -0
  153. package/skills/docx/scripts/templates/commentsExtended.xml +3 -0
  154. package/skills/docx/scripts/templates/commentsExtensible.xml +3 -0
  155. package/skills/docx/scripts/templates/commentsIds.xml +3 -0
  156. package/skills/docx/scripts/templates/people.xml +3 -0
  157. package/skills/file-bug/SKILL.md +129 -0
  158. package/skills/humanizer/LICENSE +21 -0
  159. package/skills/humanizer/SKILL.md +559 -0
  160. package/skills/humanizer/VENDORED.md +38 -0
  161. package/skills/humanizer-calibrate/SKILL.md +144 -0
  162. package/skills/mcp-builder/LICENSE.txt +202 -0
  163. package/skills/mcp-builder/SKILL.md +236 -0
  164. package/skills/mcp-builder/VENDORED.md +32 -0
  165. package/skills/mcp-builder/reference/evaluation.md +602 -0
  166. package/skills/mcp-builder/reference/mcp_best_practices.md +249 -0
  167. package/skills/mcp-builder/reference/node_mcp_server.md +970 -0
  168. package/skills/mcp-builder/reference/python_mcp_server.md +719 -0
  169. package/skills/mcp-builder/scripts/connections.py +151 -0
  170. package/skills/mcp-builder/scripts/evaluation.py +373 -0
  171. package/skills/mcp-builder/scripts/example_evaluation.xml +22 -0
  172. package/skills/mcp-builder/scripts/requirements.txt +2 -0
  173. package/skills/pdf/LICENSE.txt +30 -0
  174. package/skills/pdf/SKILL.md +314 -0
  175. package/skills/pdf/VENDORED.md +32 -0
  176. package/skills/pdf/forms.md +294 -0
  177. package/skills/pdf/reference.md +612 -0
  178. package/skills/pdf/scripts/check_bounding_boxes.py +65 -0
  179. package/skills/pdf/scripts/check_fillable_fields.py +11 -0
  180. package/skills/pdf/scripts/convert_pdf_to_images.py +33 -0
  181. package/skills/pdf/scripts/create_validation_image.py +37 -0
  182. package/skills/pdf/scripts/extract_form_field_info.py +122 -0
  183. package/skills/pdf/scripts/extract_form_structure.py +115 -0
  184. package/skills/pdf/scripts/fill_fillable_fields.py +98 -0
  185. package/skills/pdf/scripts/fill_pdf_form_with_annotations.py +107 -0
  186. package/skills/pptx/LICENSE.txt +30 -0
  187. package/skills/pptx/SKILL.md +232 -0
  188. package/skills/pptx/VENDORED.md +32 -0
  189. package/skills/pptx/editing.md +205 -0
  190. package/skills/pptx/pptxgenjs.md +420 -0
  191. package/skills/pptx/scripts/__init__.py +0 -0
  192. package/skills/pptx/scripts/add_slide.py +195 -0
  193. package/skills/pptx/scripts/clean.py +286 -0
  194. package/skills/pptx/scripts/office/helpers/__init__.py +0 -0
  195. package/skills/pptx/scripts/office/helpers/merge_runs.py +199 -0
  196. package/skills/pptx/scripts/office/helpers/simplify_redlines.py +197 -0
  197. package/skills/pptx/scripts/office/pack.py +159 -0
  198. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  199. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  200. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  201. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  202. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  203. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  204. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  205. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  206. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  207. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  208. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  209. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  210. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  211. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  212. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  213. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  214. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  215. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  216. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  217. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  218. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  219. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  220. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  221. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  222. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  223. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  224. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  225. package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  226. package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  227. package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  228. package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  229. package/skills/pptx/scripts/office/schemas/mce/mc.xsd +75 -0
  230. package/skills/pptx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
  231. package/skills/pptx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
  232. package/skills/pptx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
  233. package/skills/pptx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  234. package/skills/pptx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  235. package/skills/pptx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  236. package/skills/pptx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  237. package/skills/pptx/scripts/office/soffice.py +183 -0
  238. package/skills/pptx/scripts/office/unpack.py +132 -0
  239. package/skills/pptx/scripts/office/validate.py +111 -0
  240. package/skills/pptx/scripts/office/validators/__init__.py +15 -0
  241. package/skills/pptx/scripts/office/validators/base.py +847 -0
  242. package/skills/pptx/scripts/office/validators/docx.py +446 -0
  243. package/skills/pptx/scripts/office/validators/pptx.py +275 -0
  244. package/skills/pptx/scripts/office/validators/redlining.py +247 -0
  245. package/skills/pptx/scripts/thumbnail.py +289 -0
  246. package/skills/skill-creator/LICENSE.txt +202 -0
  247. package/skills/skill-creator/SKILL.md +485 -0
  248. package/skills/skill-creator/VENDORED.md +32 -0
  249. package/skills/skill-creator/agents/analyzer.md +274 -0
  250. package/skills/skill-creator/agents/comparator.md +202 -0
  251. package/skills/skill-creator/agents/grader.md +223 -0
  252. package/skills/skill-creator/assets/eval_review.html +146 -0
  253. package/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  254. package/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  255. package/skills/skill-creator/references/schemas.md +430 -0
  256. package/skills/skill-creator/scripts/__init__.py +0 -0
  257. package/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  258. package/skills/skill-creator/scripts/generate_report.py +326 -0
  259. package/skills/skill-creator/scripts/improve_description.py +247 -0
  260. package/skills/skill-creator/scripts/package_skill.py +136 -0
  261. package/skills/skill-creator/scripts/quick_validate.py +103 -0
  262. package/skills/skill-creator/scripts/run_eval.py +310 -0
  263. package/skills/skill-creator/scripts/run_loop.py +328 -0
  264. package/skills/skill-creator/scripts/utils.py +47 -0
  265. package/skills/switchroom-architecture/SKILL.md +60 -0
  266. package/skills/switchroom-architecture/cascade.md +112 -0
  267. package/skills/switchroom-architecture/sub-agents.md +87 -0
  268. package/skills/switchroom-architecture/telegram.md +94 -0
  269. package/skills/switchroom-cli/SKILL.md +274 -0
  270. package/skills/switchroom-health/SKILL.md +101 -0
  271. package/skills/switchroom-install/SKILL.md +116 -0
  272. package/skills/switchroom-manage/SKILL.md +90 -0
  273. package/skills/switchroom-status/SKILL.md +69 -0
  274. package/skills/switchroom-status/scripts/status.sh +69 -0
  275. package/skills/telegram-test-harness/SKILL.md +191 -0
  276. package/skills/token-helpers/SKILL.md +73 -0
  277. package/skills/token-helpers/scripts/google-cal-token.sh +62 -0
  278. package/skills/token-helpers/scripts/ms-graph-token.sh +70 -0
  279. package/skills/webapp-testing/LICENSE.txt +202 -0
  280. package/skills/webapp-testing/SKILL.md +96 -0
  281. package/skills/webapp-testing/VENDORED.md +32 -0
  282. package/skills/webapp-testing/examples/console_logging.py +35 -0
  283. package/skills/webapp-testing/examples/element_discovery.py +40 -0
  284. package/skills/webapp-testing/examples/static_html_automation.py +33 -0
  285. package/skills/webapp-testing/scripts/with_server.py +106 -0
  286. package/skills/xlsx/LICENSE.txt +30 -0
  287. package/skills/xlsx/SKILL.md +292 -0
  288. package/skills/xlsx/VENDORED.md +32 -0
  289. package/skills/xlsx/scripts/office/helpers/__init__.py +0 -0
  290. package/skills/xlsx/scripts/office/helpers/merge_runs.py +199 -0
  291. package/skills/xlsx/scripts/office/helpers/simplify_redlines.py +197 -0
  292. package/skills/xlsx/scripts/office/pack.py +159 -0
  293. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  294. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  295. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  296. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  297. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  298. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  299. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  300. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  301. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  302. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  303. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  304. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  305. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  306. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  307. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  308. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  309. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  310. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  311. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  312. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  313. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  314. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  315. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  316. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  317. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  318. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  319. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  320. package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  321. package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  322. package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  323. package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  324. package/skills/xlsx/scripts/office/schemas/mce/mc.xsd +75 -0
  325. package/skills/xlsx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
  326. package/skills/xlsx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
  327. package/skills/xlsx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
  328. package/skills/xlsx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  329. package/skills/xlsx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  330. package/skills/xlsx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  331. package/skills/xlsx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  332. package/skills/xlsx/scripts/office/soffice.py +183 -0
  333. package/skills/xlsx/scripts/office/unpack.py +132 -0
  334. package/skills/xlsx/scripts/office/validate.py +111 -0
  335. package/skills/xlsx/scripts/office/validators/__init__.py +15 -0
  336. package/skills/xlsx/scripts/office/validators/base.py +847 -0
  337. package/skills/xlsx/scripts/office/validators/docx.py +446 -0
  338. package/skills/xlsx/scripts/office/validators/pptx.py +275 -0
  339. package/skills/xlsx/scripts/office/validators/redlining.py +247 -0
  340. package/skills/xlsx/scripts/recalc.py +184 -0
  341. package/telegram-plugin/.claude-plugin/plugin.json +20 -0
  342. package/telegram-plugin/.mcp.json +14 -0
  343. package/telegram-plugin/LICENSE +21 -0
  344. package/telegram-plugin/README.md +352 -0
  345. package/telegram-plugin/active-pins-sweep.ts +204 -0
  346. package/telegram-plugin/active-pins.ts +146 -0
  347. package/telegram-plugin/active-reactions-sweep.ts +79 -0
  348. package/telegram-plugin/active-reactions.ts +134 -0
  349. package/telegram-plugin/admin-commands/dispatch.test.ts +149 -0
  350. package/telegram-plugin/admin-commands/index.ts +106 -0
  351. package/telegram-plugin/answer-stream.ts +565 -0
  352. package/telegram-plugin/ask-user.ts +179 -0
  353. package/telegram-plugin/attachment-path.ts +80 -0
  354. package/telegram-plugin/auth-code-redact.ts +83 -0
  355. package/telegram-plugin/auth-dashboard.ts +1104 -0
  356. package/telegram-plugin/auth-slot-parser.ts +497 -0
  357. package/telegram-plugin/auto-fallback-dispatcher.ts +68 -0
  358. package/telegram-plugin/auto-fallback.ts +348 -0
  359. package/telegram-plugin/bridge/bridge.ts +687 -0
  360. package/telegram-plugin/bridge/ipc-client.ts +326 -0
  361. package/telegram-plugin/bun.lock +218 -0
  362. package/telegram-plugin/card-format.ts +62 -0
  363. package/telegram-plugin/channel-envelope-safety.test.ts +56 -0
  364. package/telegram-plugin/channel-envelope-safety.ts +56 -0
  365. package/telegram-plugin/chat-lock.ts +65 -0
  366. package/telegram-plugin/context-exhaustion.ts +38 -0
  367. package/telegram-plugin/credits-watch.ts +220 -0
  368. package/telegram-plugin/dist/bridge/bridge.js +24758 -0
  369. package/telegram-plugin/dist/foreman/foreman.js +30723 -0
  370. package/telegram-plugin/dist/gateway/gateway.js +46497 -0
  371. package/telegram-plugin/dist/server.js +24551 -0
  372. package/telegram-plugin/dm-command-gate.ts +56 -0
  373. package/telegram-plugin/docs/gateway-server-split.md +133 -0
  374. package/telegram-plugin/docs/multi-agent-card-design.md +847 -0
  375. package/telegram-plugin/docs/pinned-progress-card-reliability.md +144 -0
  376. package/telegram-plugin/docs/stream-json-daemon-mode.md +477 -0
  377. package/telegram-plugin/docs/waiting-ux-spec.md +233 -0
  378. package/telegram-plugin/draft-stream.ts +442 -0
  379. package/telegram-plugin/draft-transport.ts +72 -0
  380. package/telegram-plugin/first-paint.ts +246 -0
  381. package/telegram-plugin/fleet-state.ts +246 -0
  382. package/telegram-plugin/foreman/foreman-create-flow.ts +202 -0
  383. package/telegram-plugin/foreman/foreman-handlers.ts +493 -0
  384. package/telegram-plugin/foreman/foreman.ts +1130 -0
  385. package/telegram-plugin/foreman/setup-flow.ts +345 -0
  386. package/telegram-plugin/foreman/setup-state.ts +239 -0
  387. package/telegram-plugin/foreman/state.ts +203 -0
  388. package/telegram-plugin/format.ts +685 -0
  389. package/telegram-plugin/gateway/access-validator.test.ts +95 -0
  390. package/telegram-plugin/gateway/access-validator.ts +37 -0
  391. package/telegram-plugin/gateway/boot-card.ts +582 -0
  392. package/telegram-plugin/gateway/boot-probes.ts +863 -0
  393. package/telegram-plugin/gateway/boot-reason.ts +51 -0
  394. package/telegram-plugin/gateway/boot-sweep-filter.test.ts +54 -0
  395. package/telegram-plugin/gateway/boot-sweep-filter.ts +32 -0
  396. package/telegram-plugin/gateway/clean-shutdown-marker.ts +183 -0
  397. package/telegram-plugin/gateway/disconnect-flush.ts +109 -0
  398. package/telegram-plugin/gateway/gateway.ts +10202 -0
  399. package/telegram-plugin/gateway/inbound-coalesce.ts +147 -0
  400. package/telegram-plugin/gateway/inject-handler.test.ts +221 -0
  401. package/telegram-plugin/gateway/inject-handler.ts +190 -0
  402. package/telegram-plugin/gateway/ipc-protocol.ts +151 -0
  403. package/telegram-plugin/gateway/ipc-server.ts +494 -0
  404. package/telegram-plugin/gateway/pid-file.ts +103 -0
  405. package/telegram-plugin/gateway/poll-health.ts +156 -0
  406. package/telegram-plugin/gateway/preamble-suppressor.ts +154 -0
  407. package/telegram-plugin/gateway/quota-cache.ts +125 -0
  408. package/telegram-plugin/gateway/resolve-calling-subagent.ts +78 -0
  409. package/telegram-plugin/gateway/restart-watchdog.ts +200 -0
  410. package/telegram-plugin/gateway/session-marker.ts +83 -0
  411. package/telegram-plugin/gateway/shutdown-drain.ts +162 -0
  412. package/telegram-plugin/gateway/startup-mutex.ts +285 -0
  413. package/telegram-plugin/gateway/startup-network-retry.ts +142 -0
  414. package/telegram-plugin/gateway/turn-active-marker.ts +176 -0
  415. package/telegram-plugin/gateway/unhandled-rejection-policy.ts +78 -0
  416. package/telegram-plugin/handoff-continuity.ts +200 -0
  417. package/telegram-plugin/history.ts +468 -0
  418. package/telegram-plugin/hooks/hooks.json +58 -0
  419. package/telegram-plugin/hooks/secret-guard-pretool.mjs +208 -0
  420. package/telegram-plugin/hooks/secret-scrub-stop.mjs +98 -0
  421. package/telegram-plugin/hooks/silent-end-interrupt-stop.mjs +111 -0
  422. package/telegram-plugin/hooks/subagent-tracker-posttool.mjs +296 -0
  423. package/telegram-plugin/hooks/subagent-tracker-pretool.mjs +261 -0
  424. package/telegram-plugin/html-sanitize.ts +244 -0
  425. package/telegram-plugin/idle-footer.ts +65 -0
  426. package/telegram-plugin/inline-keyboard-callbacks.ts +166 -0
  427. package/telegram-plugin/interrupt-marker.ts +66 -0
  428. package/telegram-plugin/issues-card.ts +371 -0
  429. package/telegram-plugin/issues-watcher.ts +125 -0
  430. package/telegram-plugin/model-unavailable.ts +325 -0
  431. package/telegram-plugin/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  432. package/telegram-plugin/operator-events-history.ts +94 -0
  433. package/telegram-plugin/operator-events.fixtures.json +161 -0
  434. package/telegram-plugin/operator-events.ts +421 -0
  435. package/telegram-plugin/package.json +55 -0
  436. package/telegram-plugin/permission-rule.ts +133 -0
  437. package/telegram-plugin/permission-title.ts +117 -0
  438. package/telegram-plugin/pin-event-log.ts +76 -0
  439. package/telegram-plugin/plugin-logger.ts +136 -0
  440. package/telegram-plugin/progress-card-driver.ts +2697 -0
  441. package/telegram-plugin/progress-card-pin-manager.ts +589 -0
  442. package/telegram-plugin/progress-card-pin-watchdog.ts +98 -0
  443. package/telegram-plugin/progress-card.ts +1409 -0
  444. package/telegram-plugin/pty-partial-handler.ts +247 -0
  445. package/telegram-plugin/pty-tail.ts +730 -0
  446. package/telegram-plugin/quota-check.ts +474 -0
  447. package/telegram-plugin/recent-outbound-dedup.ts +169 -0
  448. package/telegram-plugin/registry/api-registry.test.ts +201 -0
  449. package/telegram-plugin/registry/subagents-bugs.test.ts +454 -0
  450. package/telegram-plugin/registry/subagents-schema.ts +509 -0
  451. package/telegram-plugin/registry/subagents.test.ts +476 -0
  452. package/telegram-plugin/registry/turns-schema.test.ts +101 -0
  453. package/telegram-plugin/registry/turns-schema.ts +417 -0
  454. package/telegram-plugin/retry-api-call.ts +172 -0
  455. package/telegram-plugin/scripts/build.mjs +78 -0
  456. package/telegram-plugin/secret-detect/audit.ts +66 -0
  457. package/telegram-plugin/secret-detect/chunker.ts +37 -0
  458. package/telegram-plugin/secret-detect/entropy.ts +20 -0
  459. package/telegram-plugin/secret-detect/gitleaks-loader.ts +74 -0
  460. package/telegram-plugin/secret-detect/gitleaks.toml +27 -0
  461. package/telegram-plugin/secret-detect/index.ts +218 -0
  462. package/telegram-plugin/secret-detect/kv-scanner.ts +60 -0
  463. package/telegram-plugin/secret-detect/mask.ts +13 -0
  464. package/telegram-plugin/secret-detect/patterns.ts +115 -0
  465. package/telegram-plugin/secret-detect/pipeline.ts +144 -0
  466. package/telegram-plugin/secret-detect/rewrite.ts +26 -0
  467. package/telegram-plugin/secret-detect/secretlint-source.ts +95 -0
  468. package/telegram-plugin/secret-detect/slug.ts +44 -0
  469. package/telegram-plugin/secret-detect/staging.ts +85 -0
  470. package/telegram-plugin/secret-detect/suppressor.ts +34 -0
  471. package/telegram-plugin/secret-detect/url-redact.ts +60 -0
  472. package/telegram-plugin/secret-detect/vault-write.ts +56 -0
  473. package/telegram-plugin/server.js +41795 -0
  474. package/telegram-plugin/server.ts +171 -0
  475. package/telegram-plugin/session-tail.ts +884 -0
  476. package/telegram-plugin/shared/bot-runtime.ts +324 -0
  477. package/telegram-plugin/silent-reply.ts +58 -0
  478. package/telegram-plugin/slot-banner-driver.ts +147 -0
  479. package/telegram-plugin/slot-banner.ts +86 -0
  480. package/telegram-plugin/start.js +26 -0
  481. package/telegram-plugin/startup-reset.ts +45 -0
  482. package/telegram-plugin/status-reactions.ts +332 -0
  483. package/telegram-plugin/steering.ts +155 -0
  484. package/telegram-plugin/sticker-aliases.ts +249 -0
  485. package/telegram-plugin/stream-controller.ts +311 -0
  486. package/telegram-plugin/stream-reply-handler.ts +664 -0
  487. package/telegram-plugin/streaming-metrics.ts +134 -0
  488. package/telegram-plugin/streaming-report.ts +204 -0
  489. package/telegram-plugin/subagent-watcher.ts +880 -0
  490. package/telegram-plugin/telegram-button-constraints.ts +191 -0
  491. package/telegram-plugin/telegraph.ts +381 -0
  492. package/telegram-plugin/tests/HARNESS.md +340 -0
  493. package/telegram-plugin/tests/_progress-card-harness.ts +105 -0
  494. package/telegram-plugin/tests/active-pins-boot-reaper.test.ts +211 -0
  495. package/telegram-plugin/tests/active-pins-sweep.test.ts +309 -0
  496. package/telegram-plugin/tests/active-pins.test.ts +187 -0
  497. package/telegram-plugin/tests/active-reactions-sweep.test.ts +116 -0
  498. package/telegram-plugin/tests/active-reactions.test.ts +198 -0
  499. package/telegram-plugin/tests/answer-stream-dedup.test.ts +352 -0
  500. package/telegram-plugin/tests/answer-stream-silent-markers.test.ts +236 -0
  501. package/telegram-plugin/tests/answer-stream.test.ts +878 -0
  502. package/telegram-plugin/tests/ask-user.test.ts +203 -0
  503. package/telegram-plugin/tests/attachment-path.test.ts +199 -0
  504. package/telegram-plugin/tests/auth-account-identity-surface.test.ts +118 -0
  505. package/telegram-plugin/tests/auth-code-auto-capture.test.ts +144 -0
  506. package/telegram-plugin/tests/auth-code-redact.test.ts +248 -0
  507. package/telegram-plugin/tests/auth-dashboard-edge-cases.test.ts +260 -0
  508. package/telegram-plugin/tests/auth-dashboard-restart-flow.test.ts +140 -0
  509. package/telegram-plugin/tests/auth-dashboard-v3b.test.ts +559 -0
  510. package/telegram-plugin/tests/auth-dashboard.test.ts +1045 -0
  511. package/telegram-plugin/tests/auth-login-url-button.test.ts +122 -0
  512. package/telegram-plugin/tests/auth-slot-commands.test.ts +640 -0
  513. package/telegram-plugin/tests/auto-fallback-dispatcher.e2e.test.ts +183 -0
  514. package/telegram-plugin/tests/auto-fallback.test.ts +381 -0
  515. package/telegram-plugin/tests/boot-card-account-quota.test.ts +137 -0
  516. package/telegram-plugin/tests/boot-card-dedupe.test.ts +154 -0
  517. package/telegram-plugin/tests/boot-card-probe-target.test.ts +194 -0
  518. package/telegram-plugin/tests/boot-card-reason.test.ts +103 -0
  519. package/telegram-plugin/tests/boot-card-render.test.ts +219 -0
  520. package/telegram-plugin/tests/boot-probes.test.ts +451 -0
  521. package/telegram-plugin/tests/bot-api.harness.ts +116 -0
  522. package/telegram-plugin/tests/bot-runtime.test.ts +190 -0
  523. package/telegram-plugin/tests/bridge-anonymous-refuse.test.ts +60 -0
  524. package/telegram-plugin/tests/context-exhaustion.test.ts +114 -0
  525. package/telegram-plugin/tests/credits-watch.test.ts +221 -0
  526. package/telegram-plugin/tests/dm-command-gate.test.ts +176 -0
  527. package/telegram-plugin/tests/draft-stream.test.ts +752 -0
  528. package/telegram-plugin/tests/draft-transport.test.ts +141 -0
  529. package/telegram-plugin/tests/e2e.test.ts +436 -0
  530. package/telegram-plugin/tests/fake-bot-api.test.ts +213 -0
  531. package/telegram-plugin/tests/fake-bot-api.ts +617 -0
  532. package/telegram-plugin/tests/false-restart-banner.test.ts +253 -0
  533. package/telegram-plugin/tests/first-paint.test.ts +257 -0
  534. package/telegram-plugin/tests/fixtures/pty-tail-tmux-fragment.bin +6 -0
  535. package/telegram-plugin/tests/fixtures/service-log-current-claude-code.bin +3624 -0
  536. package/telegram-plugin/tests/fleet-state-watcher.test.ts +101 -0
  537. package/telegram-plugin/tests/fleet-state.test.ts +185 -0
  538. package/telegram-plugin/tests/foreman-create-flow.test.ts +359 -0
  539. package/telegram-plugin/tests/foreman-handlers.test.ts +347 -0
  540. package/telegram-plugin/tests/foreman-state.test.ts +164 -0
  541. package/telegram-plugin/tests/foreman-write-ops.test.ts +214 -0
  542. package/telegram-plugin/tests/gateway-409-retry-banner.test.ts +173 -0
  543. package/telegram-plugin/tests/gateway-boot-marker-clear.test.ts +72 -0
  544. package/telegram-plugin/tests/gateway-bridge.test.ts +811 -0
  545. package/telegram-plugin/tests/gateway-clean-shutdown-marker.test.ts +414 -0
  546. package/telegram-plugin/tests/gateway-disconnect-flush.test.ts +144 -0
  547. package/telegram-plugin/tests/gateway-message-validator.test.ts +133 -0
  548. package/telegram-plugin/tests/gateway-no-reply-single-emit.test.ts +103 -0
  549. package/telegram-plugin/tests/gateway-secret-detect.test.ts +127 -0
  550. package/telegram-plugin/tests/gateway-startup-mutex.test.ts +284 -0
  551. package/telegram-plugin/tests/gateway-startup-network-retry.test.ts +185 -0
  552. package/telegram-plugin/tests/gateway-startup-reset.test.ts +72 -0
  553. package/telegram-plugin/tests/gateway-update-placeholder-dispatch.test.ts +125 -0
  554. package/telegram-plugin/tests/handoff-continuity.test.ts +249 -0
  555. package/telegram-plugin/tests/harness-ordering-invariants.test.ts +243 -0
  556. package/telegram-plugin/tests/harness-parse-mode-validation.test.ts +114 -0
  557. package/telegram-plugin/tests/history.test.ts +364 -0
  558. package/telegram-plugin/tests/html-balanced.ts +63 -0
  559. package/telegram-plugin/tests/html-sanitize.test.ts +146 -0
  560. package/telegram-plugin/tests/idle-footer-wiring.test.ts +88 -0
  561. package/telegram-plugin/tests/idle-footer.test.ts +66 -0
  562. package/telegram-plugin/tests/inbound-coalesce.test.ts +127 -0
  563. package/telegram-plugin/tests/inline-keyboard-callbacks.test.ts +150 -0
  564. package/telegram-plugin/tests/interrupt-marker.test.ts +126 -0
  565. package/telegram-plugin/tests/ipc-protocol.test.ts +218 -0
  566. package/telegram-plugin/tests/ipc-server-anonymous-refuse.test.ts +82 -0
  567. package/telegram-plugin/tests/ipc-server-client.test.ts +323 -0
  568. package/telegram-plugin/tests/ipc-server-race.test.ts +183 -0
  569. package/telegram-plugin/tests/ipc-server-validate-operator.test.ts +91 -0
  570. package/telegram-plugin/tests/ipc-server-validate-pty-partial.test.ts +64 -0
  571. package/telegram-plugin/tests/ipc-server-validate-update-placeholder.test.ts +77 -0
  572. package/telegram-plugin/tests/ipc-validator.test.ts +274 -0
  573. package/telegram-plugin/tests/issues-card.test.ts +495 -0
  574. package/telegram-plugin/tests/issues-watcher.test.ts +165 -0
  575. package/telegram-plugin/tests/model-unavailable.test.ts +303 -0
  576. package/telegram-plugin/tests/multi-turn-continuity.test.ts +159 -0
  577. package/telegram-plugin/tests/operator-events-history.test.ts +125 -0
  578. package/telegram-plugin/tests/operator-events-session-tail.test.ts +192 -0
  579. package/telegram-plugin/tests/operator-events.test.ts +331 -0
  580. package/telegram-plugin/tests/outbound-ordering.test.ts +293 -0
  581. package/telegram-plugin/tests/parse-mode-rotation.test.ts +164 -0
  582. package/telegram-plugin/tests/permission-rule.test.ts +121 -0
  583. package/telegram-plugin/tests/permission-title.test.ts +106 -0
  584. package/telegram-plugin/tests/pin-event-log.test.ts +124 -0
  585. package/telegram-plugin/tests/plugin-logger.test.ts +97 -0
  586. package/telegram-plugin/tests/poll-health.test.ts +86 -0
  587. package/telegram-plugin/tests/preamble-suppressor.test.ts +285 -0
  588. package/telegram-plugin/tests/progress-card-api-failure-during-deferred.test.ts +73 -0
  589. package/telegram-plugin/tests/progress-card-close-paths-converge.test.ts +272 -0
  590. package/telegram-plugin/tests/progress-card-cross-turn.test.ts +258 -0
  591. package/telegram-plugin/tests/progress-card-dispose-preservepending.test.ts +81 -0
  592. package/telegram-plugin/tests/progress-card-draft-flag.test.ts +80 -0
  593. package/telegram-plugin/tests/progress-card-driver-eviction.test.ts +215 -0
  594. package/telegram-plugin/tests/progress-card-driver-fleet-shadow.test.ts +123 -0
  595. package/telegram-plugin/tests/progress-card-driver-force-complete-parent-done.test.ts +76 -0
  596. package/telegram-plugin/tests/progress-card-edit-timestamps-budget.test.ts +62 -0
  597. package/telegram-plugin/tests/progress-card-memory-bounds.test.ts +84 -0
  598. package/telegram-plugin/tests/progress-card-pin-failure-paths.test.ts +139 -0
  599. package/telegram-plugin/tests/progress-card-pin-manager.test.ts +773 -0
  600. package/telegram-plugin/tests/progress-card-pin-race-fast-turn.test.ts +66 -0
  601. package/telegram-plugin/tests/progress-card-pin-sidecar-partial-write.test.ts +64 -0
  602. package/telegram-plugin/tests/progress-card-pin-watchdog.test.ts +190 -0
  603. package/telegram-plugin/tests/progress-card-sigterm-pin-flush.test.ts +146 -0
  604. package/telegram-plugin/tests/progress-update.test.ts +236 -0
  605. package/telegram-plugin/tests/protocol-fixtures.test.ts +59 -0
  606. package/telegram-plugin/tests/protocol-fixtures.ts +198 -0
  607. package/telegram-plugin/tests/pty-partial-handler.test.ts +326 -0
  608. package/telegram-plugin/tests/pty-tail-real-fixture.test.ts +114 -0
  609. package/telegram-plugin/tests/pty-tail-tmux-fragment.test.ts +71 -0
  610. package/telegram-plugin/tests/pty-tail.test.ts +525 -0
  611. package/telegram-plugin/tests/quota-cache.test.ts +187 -0
  612. package/telegram-plugin/tests/quota-check.test.ts +622 -0
  613. package/telegram-plugin/tests/races.test.ts +842 -0
  614. package/telegram-plugin/tests/real-gateway-f1-ladder-integrity.test.ts +123 -0
  615. package/telegram-plugin/tests/real-gateway-f2-instant-draft.test.ts +82 -0
  616. package/telegram-plugin/tests/real-gateway-f3-late-card.test.ts +114 -0
  617. package/telegram-plugin/tests/real-gateway-harness.ts +699 -0
  618. package/telegram-plugin/tests/real-gateway-i6-turn-flush-replay-dedup.test.ts +313 -0
  619. package/telegram-plugin/tests/real-gateway-ipc-lifecycle.test.ts +299 -0
  620. package/telegram-plugin/tests/real-gateway-spec.test.ts +487 -0
  621. package/telegram-plugin/tests/real-gateway.smoke.test.ts +101 -0
  622. package/telegram-plugin/tests/recent-outbound-dedup.test.ts +192 -0
  623. package/telegram-plugin/tests/registry-turns.test.ts +432 -0
  624. package/telegram-plugin/tests/reply-terminal-reaction.test.ts +149 -0
  625. package/telegram-plugin/tests/resolve-calling-subagent.test.ts +269 -0
  626. package/telegram-plugin/tests/restart-watchdog.test.ts +224 -0
  627. package/telegram-plugin/tests/retry-api-call.test.ts +287 -0
  628. package/telegram-plugin/tests/secret-detect-audit.test.ts +58 -0
  629. package/telegram-plugin/tests/secret-detect-fail-closed.test.ts +83 -0
  630. package/telegram-plugin/tests/secret-detect-gitleaks.test.ts +32 -0
  631. package/telegram-plugin/tests/secret-detect-oauth-code.test.ts +308 -0
  632. package/telegram-plugin/tests/secret-detect-pipeline.test.ts +123 -0
  633. package/telegram-plugin/tests/secret-detect-secretlint.test.ts +101 -0
  634. package/telegram-plugin/tests/secret-detect-staging.test.ts +45 -0
  635. package/telegram-plugin/tests/secret-detect-suppressor-no-silent-allow.test.ts +67 -0
  636. package/telegram-plugin/tests/secret-detect.test.ts +223 -0
  637. package/telegram-plugin/tests/secret-guard-pretool.test.ts +194 -0
  638. package/telegram-plugin/tests/send-typing-action-validation.test.ts +61 -0
  639. package/telegram-plugin/tests/session-tail-capped.test.ts +285 -0
  640. package/telegram-plugin/tests/session-tail.test.ts +524 -0
  641. package/telegram-plugin/tests/setup-flow.test.ts +510 -0
  642. package/telegram-plugin/tests/setup-state.test.ts +146 -0
  643. package/telegram-plugin/tests/silent-reply-guard.test.ts +122 -0
  644. package/telegram-plugin/tests/slot-banner-driver.e2e.test.ts +350 -0
  645. package/telegram-plugin/tests/slot-banner.test.ts +74 -0
  646. package/telegram-plugin/tests/snapshot-serializer.ts +79 -0
  647. package/telegram-plugin/tests/spawn-detached-cgroup-escape.test.ts +51 -0
  648. package/telegram-plugin/tests/status-accent.test.ts +186 -0
  649. package/telegram-plugin/tests/status-reactions-allowed-filter.test.ts +132 -0
  650. package/telegram-plugin/tests/status-reactions.test.ts +314 -0
  651. package/telegram-plugin/tests/steering.test.ts +282 -0
  652. package/telegram-plugin/tests/sticker-aliases.test.ts +232 -0
  653. package/telegram-plugin/tests/stream-controller-html-fallback.test.ts +127 -0
  654. package/telegram-plugin/tests/stream-controller.test.ts +262 -0
  655. package/telegram-plugin/tests/stream-reply-error-paths.test.ts +208 -0
  656. package/telegram-plugin/tests/stream-reply-handler.test.ts +1292 -0
  657. package/telegram-plugin/tests/streaming-e2e.test.ts +389 -0
  658. package/telegram-plugin/tests/streaming-metrics.test.ts +201 -0
  659. package/telegram-plugin/tests/streaming-orchestration.test.ts +756 -0
  660. package/telegram-plugin/tests/subagent-registry-bugs.test.ts +725 -0
  661. package/telegram-plugin/tests/subagent-tracker-hooks.test.ts +213 -0
  662. package/telegram-plugin/tests/subagent-watcher-parent-marker.test.ts +274 -0
  663. package/telegram-plugin/tests/subagent-watcher-stall-notification.test.ts +243 -0
  664. package/telegram-plugin/tests/subagent-watcher.test.ts +877 -0
  665. package/telegram-plugin/tests/subagents-schema-init-order.test.ts +109 -0
  666. package/telegram-plugin/tests/sync-chat-running-subagents.test.ts +116 -0
  667. package/telegram-plugin/tests/telegram-button-constraints.test.ts +194 -0
  668. package/telegram-plugin/tests/telegram-format.test.ts +1093 -0
  669. package/telegram-plugin/tests/telegraph.test.ts +246 -0
  670. package/telegram-plugin/tests/tool-labels.test.ts +383 -0
  671. package/telegram-plugin/tests/turn-active-marker.test.ts +195 -0
  672. package/telegram-plugin/tests/turn-end-regressions.test.ts +489 -0
  673. package/telegram-plugin/tests/turn-flush-card-takeover.test.ts +218 -0
  674. package/telegram-plugin/tests/turn-flush-dedup-controller.test.ts +144 -0
  675. package/telegram-plugin/tests/turn-flush-prose-recovery.test.ts +78 -0
  676. package/telegram-plugin/tests/turn-flush-safety.test.ts +189 -0
  677. package/telegram-plugin/tests/turn-signal-tracker.test.ts +107 -0
  678. package/telegram-plugin/tests/turns-writer.test.ts +323 -0
  679. package/telegram-plugin/tests/two-zone-bg-carry-full-lifecycle.test.ts +131 -0
  680. package/telegram-plugin/tests/two-zone-bg-detection.test.ts +120 -0
  681. package/telegram-plugin/tests/two-zone-bg-done-when-all-terminal.test.ts +114 -0
  682. package/telegram-plugin/tests/two-zone-bg-early-turn-end.test.ts +87 -0
  683. package/telegram-plugin/tests/two-zone-bg-survives-next-turn.test.ts +211 -0
  684. package/telegram-plugin/tests/two-zone-card-cap.test.ts +62 -0
  685. package/telegram-plugin/tests/two-zone-card-fleet-row.test.ts +101 -0
  686. package/telegram-plugin/tests/two-zone-card-header-phases.test.ts +68 -0
  687. package/telegram-plugin/tests/two-zone-card-html-balance.test.ts +110 -0
  688. package/telegram-plugin/tests/two-zone-card-lifecycle.test.ts +128 -0
  689. package/telegram-plugin/tests/two-zone-card-sanitise.test.ts +58 -0
  690. package/telegram-plugin/tests/two-zone-card-snapshot.test.ts +133 -0
  691. package/telegram-plugin/tests/two-zone-concurrent-turns-isolation.test.ts +155 -0
  692. package/telegram-plugin/tests/two-zone-phasefor-precedence.test.ts +117 -0
  693. package/telegram-plugin/tests/two-zone-snapshot-extras.test.ts +143 -0
  694. package/telegram-plugin/tests/two-zone-stuck-edit-throttle.test.ts +149 -0
  695. package/telegram-plugin/tests/two-zone-stuck-header-escalation.test.ts +101 -0
  696. package/telegram-plugin/tests/two-zone-stuck-per-member.test.ts +114 -0
  697. package/telegram-plugin/tests/two-zone-stuck-recovery.test.ts +105 -0
  698. package/telegram-plugin/tests/typing-wrap.test.ts +141 -0
  699. package/telegram-plugin/tests/unhandled-rejection-policy.test.ts +147 -0
  700. package/telegram-plugin/tests/update-factory-edited-and-reactions.test.ts +108 -0
  701. package/telegram-plugin/tests/update-factory.ts +305 -0
  702. package/telegram-plugin/tests/vault-grant-wizard.test.ts +84 -0
  703. package/telegram-plugin/tests/vault-grants-revoke.test.ts +265 -0
  704. package/telegram-plugin/tests/vault-subcommands.test.ts +234 -0
  705. package/telegram-plugin/tests/voice-transcribe.test.ts +196 -0
  706. package/telegram-plugin/tests/waiting-ux-harness.ts +381 -0
  707. package/telegram-plugin/tests/waiting-ux.e2e.test.ts +233 -0
  708. package/telegram-plugin/tests/welcome-text.test.ts +407 -0
  709. package/telegram-plugin/tool-error-filter.ts +89 -0
  710. package/telegram-plugin/tool-labels.ts +330 -0
  711. package/telegram-plugin/tool-names.ts +53 -0
  712. package/telegram-plugin/turn-flush-prose-recovery.ts +40 -0
  713. package/telegram-plugin/turn-flush-safety.ts +109 -0
  714. package/telegram-plugin/turn-signal-tracker.ts +79 -0
  715. package/telegram-plugin/two-zone-card.ts +249 -0
  716. package/telegram-plugin/typing-wrap.ts +92 -0
  717. package/telegram-plugin/voice-transcribe.ts +166 -0
  718. package/telegram-plugin/welcome-text.ts +359 -0
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Unit tests for `shouldSkipDuplicateBootCard` — the helper that prevents
3
+ * the boot path AND the bridge-reconnect path from BOTH posting a boot
4
+ * card on a single gateway lifetime.
5
+ *
6
+ * Regression for the duplicate-post observed in klanker's journal at
7
+ * 2026-04-26 11:19:47, where msgId 2245 was posted by the boot path and
8
+ * msgId 2248 by the bridge-reconnect path within 5 seconds.
9
+ */
10
+
11
+ import { describe, it, expect } from 'bun:test'
12
+ import { shouldSkipDuplicateBootCard } from '../gateway/boot-card.js'
13
+
14
+ describe('shouldSkipDuplicateBootCard — boot path', () => {
15
+ it('skips on the boot path when bridge-reconnect already posted a card', () => {
16
+ // First-write-wins: when the agent's IPC client connects faster
17
+ // than the gateway IIFE reaches its emit, bridge-reconnect runs
18
+ // first and sets activeBootCard. The boot path must defer.
19
+ // Regression for finn 2026-05-02 duplicate (msgId 673 + 674)
20
+ // — both posted within ~100ms because the boot path was
21
+ // unconditionally allowed past the gate.
22
+ const decision = shouldSkipDuplicateBootCard(
23
+ { activeBootCard: { messageId: 42 } },
24
+ 'boot',
25
+ )
26
+ expect(decision.skip).toBe(true)
27
+ expect(decision.reason).toMatch(/msgId.*42/)
28
+ expect(decision.reason).toMatch(/site=boot/)
29
+ })
30
+
31
+ it('skips on the boot path when bridge-reconnect emit is in-flight', () => {
32
+ const decision = shouldSkipDuplicateBootCard(
33
+ { activeBootCard: null, bootCardPending: true },
34
+ 'boot',
35
+ )
36
+ expect(decision.skip).toBe(true)
37
+ expect(decision.reason).toMatch(/in-flight/i)
38
+ })
39
+
40
+ it('does not skip on the boot path with no active card and no pending emit', () => {
41
+ const decision = shouldSkipDuplicateBootCard({ activeBootCard: null }, 'boot')
42
+ expect(decision.skip).toBe(false)
43
+ })
44
+ })
45
+
46
+ describe('shouldSkipDuplicateBootCard — bridge-reconnect path', () => {
47
+ it('skips when the boot path already posted (active card present)', () => {
48
+ const decision = shouldSkipDuplicateBootCard(
49
+ { activeBootCard: { messageId: 2245 } },
50
+ 'bridge-reconnect',
51
+ )
52
+ expect(decision.skip).toBe(true)
53
+ expect(decision.reason).toBeDefined()
54
+ expect(decision.reason).toContain('2245')
55
+ })
56
+
57
+ it('does not skip when the boot path produced no card', () => {
58
+ // Plausible scenario: boot path skipped due to no chat_id known yet,
59
+ // and bridge-reconnect arrives later with a chat_id from the IPC client.
60
+ const decision = shouldSkipDuplicateBootCard(
61
+ { activeBootCard: null },
62
+ 'bridge-reconnect',
63
+ )
64
+ expect(decision.skip).toBe(false)
65
+ })
66
+ })
67
+
68
+ describe('shouldSkipDuplicateBootCard — reason format', () => {
69
+ it('includes the active messageId in the reason for observability', () => {
70
+ const decision = shouldSkipDuplicateBootCard(
71
+ { activeBootCard: { messageId: 9999 } },
72
+ 'bridge-reconnect',
73
+ )
74
+ expect(decision.skip).toBe(true)
75
+ expect(decision.reason).toMatch(/msgId.*9999/)
76
+ })
77
+
78
+ it('omits reason when not skipping', () => {
79
+ const decision = shouldSkipDuplicateBootCard({ activeBootCard: null }, 'boot')
80
+ expect(decision.skip).toBe(false)
81
+ expect(decision.reason).toBeUndefined()
82
+ })
83
+
84
+ it('reason carries the site label so dedupe-source is greppable in logs', () => {
85
+ const fromBoot = shouldSkipDuplicateBootCard(
86
+ { activeBootCard: { messageId: 1 } },
87
+ 'boot',
88
+ )
89
+ expect(fromBoot.reason).toMatch(/site=boot/)
90
+ const fromReconnect = shouldSkipDuplicateBootCard(
91
+ { activeBootCard: { messageId: 1 } },
92
+ 'bridge-reconnect',
93
+ )
94
+ expect(fromReconnect.reason).toMatch(/site=bridge-reconnect/)
95
+ })
96
+ })
97
+
98
+ // ---------------------------------------------------------------------------
99
+ // In-flight race window (issue #489)
100
+ //
101
+ // Before #489, the gate only saw activeBootCard, which is only assigned
102
+ // AFTER the boot path's `await startBootCard(...)` resolved. If the agent's
103
+ // IPC client connected during that 1–2s sendMessage round-trip,
104
+ // onClientRegistered would dedupe-check, see activeBootCard = null, and
105
+ // fire its own boot card. Klanker on 2026-05-01 10:13:15 produced msgId
106
+ // 4715 + 4716 from the same gateway PID via this race. The bootCardPending
107
+ // flag is set synchronously before the await so the dedupe sees in-flight.
108
+ // ---------------------------------------------------------------------------
109
+
110
+ describe('shouldSkipDuplicateBootCard — in-flight (race window, #489)', () => {
111
+ it('skips bridge-reconnect when boot path is still awaiting sendMessage', () => {
112
+ const decision = shouldSkipDuplicateBootCard(
113
+ { activeBootCard: null, bootCardPending: true },
114
+ 'bridge-reconnect',
115
+ )
116
+ expect(decision.skip).toBe(true)
117
+ expect(decision.reason).toMatch(/in-flight/i)
118
+ })
119
+
120
+ it('skips bridge-reconnect when both pending and active are set (post-resolution overlap)', () => {
121
+ // A bridge-reconnect can fire after activeBootCard was assigned but
122
+ // before the finally-clears bootCardPending — both true is legal.
123
+ const decision = shouldSkipDuplicateBootCard(
124
+ { activeBootCard: { messageId: 4715 }, bootCardPending: true },
125
+ 'bridge-reconnect',
126
+ )
127
+ expect(decision.skip).toBe(true)
128
+ // In-flight wins because it's checked first; either reason is fine
129
+ // for observability — the card is correctly skipped either way.
130
+ expect(decision.reason).toBeDefined()
131
+ })
132
+
133
+ it('skips boot path when bridge-reconnect emit is in-flight (first-write-wins)', () => {
134
+ // The boot path is no longer "primary" — empirical evidence
135
+ // (finn 2026-05-02) shows bridge-reconnect can fire first when
136
+ // the agent IPC-connects before the gateway IIFE reaches its
137
+ // emit. Both sites consult the gate symmetrically.
138
+ const decision = shouldSkipDuplicateBootCard(
139
+ { activeBootCard: null, bootCardPending: true },
140
+ 'boot',
141
+ )
142
+ expect(decision.skip).toBe(true)
143
+ })
144
+
145
+ it('treats undefined bootCardPending as "not pending" for backward compat', () => {
146
+ // Callers that pre-date the flag still pass { activeBootCard } only.
147
+ // Their behaviour must not change.
148
+ const decision = shouldSkipDuplicateBootCard(
149
+ { activeBootCard: null },
150
+ 'bridge-reconnect',
151
+ )
152
+ expect(decision.skip).toBe(false)
153
+ })
154
+ })
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Tests for #309: boot card uses the agent slug (not display name) for
3
+ * systemd unit probes.
4
+ *
5
+ * Root cause: probeAgentProcess and probeCronTimers were called with
6
+ * opts.agentName (the persona display name, e.g. "Klanker") instead of
7
+ * the lowercase slug ("klanker"). systemctl returns LoadState=not-found
8
+ * for the capitalised name because unit files are always lowercase.
9
+ *
10
+ * Fix: RunProbesOpts.agentSlug carries the slug separately; runAllProbes
11
+ * passes opts.agentSlug (falling back to opts.agentName for compat) to
12
+ * both probeAgentProcess and probeCronTimers.
13
+ */
14
+
15
+ import { describe, it, expect } from 'vitest'
16
+ import { join } from 'path'
17
+ import { mkdtempSync, rmSync } from 'fs'
18
+ import { tmpdir } from 'os'
19
+
20
+ import { runAllProbes } from '../gateway/boot-card.js'
21
+
22
+ // ── Helpers ───────────────────────────────────────────────────────────────────
23
+
24
+ type ExecFileResult = { stdout: string; stderr: string }
25
+ type RecordedCall = { cmd: string; args: string[] }
26
+
27
+ /**
28
+ * Build an execFile mock that records every (cmd, args) call and dispatches
29
+ * the response based on which systemctl sub-command is being called.
30
+ *
31
+ * `show` → responds with a valid "active" systemctl kv blob so that
32
+ * probeAgentProcess resolves immediately without retrying.
33
+ * `list-timers`→ responds with empty JSON array (0 timers).
34
+ * anything else→ responds with empty stdout.
35
+ */
36
+ function makeDispatchingExecFile(slug: string): {
37
+ fn: (cmd: string, args: string[]) => Promise<ExecFileResult>
38
+ calls: RecordedCall[]
39
+ } {
40
+ const calls: RecordedCall[] = []
41
+ return {
42
+ fn: async (cmd: string, args: string[]) => {
43
+ calls.push({ cmd, args })
44
+ if (cmd === 'systemctl') {
45
+ if (args.includes('show')) {
46
+ // Return a valid active systemd kv blob so probeAgentProcess exits
47
+ // immediately on the first call (no retry needed).
48
+ return {
49
+ stdout: [
50
+ 'MainPID=9999',
51
+ 'ActiveState=active',
52
+ 'MemoryCurrent=52428800',
53
+ 'ActiveEnterTimestamp=1700000000000000',
54
+ ].join('\n') + '\n',
55
+ stderr: '',
56
+ }
57
+ }
58
+ if (args.includes('list-timers')) {
59
+ return { stdout: '[]', stderr: '' }
60
+ }
61
+ }
62
+ return { stdout: '', stderr: '' }
63
+ },
64
+ calls,
65
+ }
66
+ }
67
+
68
+ // ── Core probe-target tests ───────────────────────────────────────────────────
69
+
70
+ describe('#309: runAllProbes — slug vs display name for systemd calls', () => {
71
+ function makeTmpAgentDir(): string {
72
+ return mkdtempSync(join(tmpdir(), 'switchroom-test-'))
73
+ }
74
+
75
+ it('probeAgentProcess target is switchroom-<slug>.service, not switchroom-<displayName>.service', async () => {
76
+ const tmpDir = makeTmpAgentDir()
77
+ try {
78
+ const { fn: execFileMock, calls } = makeDispatchingExecFile('klanker')
79
+
80
+ await runAllProbes({
81
+ agentName: 'Klanker', // persona display name — capitalised
82
+ agentSlug: 'klanker', // systemd slug — lowercase
83
+ version: 'v0.3.0',
84
+ agentDir: tmpDir,
85
+ gatewayInfo: { pid: 12345, startedAtMs: Date.now() },
86
+ fetchImpl: async () => new Response('', { status: 200 }),
87
+ settleWindowMs: 0,
88
+ agentLiveWindowMs: 0, // disable live loop
89
+ probeExecFileImpl: execFileMock,
90
+ })
91
+
92
+ // probeAgentProcess calls: systemctl --user show switchroom-<name>.service -p ...
93
+ const agentProbeCall = calls.find(c =>
94
+ c.cmd === 'systemctl' && c.args.includes('show'),
95
+ )
96
+ expect(agentProbeCall, 'probeAgentProcess must call systemctl show').toBeDefined()
97
+ const unitArg = agentProbeCall!.args.find(a => a.startsWith('switchroom-'))
98
+ // Must use the slug, not the capitalised display name.
99
+ expect(unitArg).toBe('switchroom-klanker.service')
100
+ } finally {
101
+ rmSync(tmpDir, { recursive: true })
102
+ }
103
+ })
104
+
105
+ it('probeCronTimers target is switchroom-<slug>-cron-*, not switchroom-<displayName>-cron-*', async () => {
106
+ const tmpDir = makeTmpAgentDir()
107
+ try {
108
+ const { fn: execFileMock, calls } = makeDispatchingExecFile('klanker')
109
+
110
+ await runAllProbes({
111
+ agentName: 'Klanker',
112
+ agentSlug: 'klanker',
113
+ version: 'v0.3.0',
114
+ agentDir: tmpDir,
115
+ gatewayInfo: { pid: 12345, startedAtMs: Date.now() },
116
+ fetchImpl: async () => new Response('', { status: 200 }),
117
+ settleWindowMs: 0,
118
+ agentLiveWindowMs: 0,
119
+ probeExecFileImpl: execFileMock,
120
+ })
121
+
122
+ // probeCronTimers calls: systemctl --user list-timers switchroom-<name>-cron-*
123
+ const cronProbeCall = calls.find(c =>
124
+ c.cmd === 'systemctl' && c.args.includes('list-timers'),
125
+ )
126
+ expect(cronProbeCall, 'probeCronTimers must call systemctl list-timers').toBeDefined()
127
+ const cronGlob = cronProbeCall!.args.find(a => a.includes('cron'))
128
+ expect(cronGlob).toBe('switchroom-klanker-cron-*')
129
+ } finally {
130
+ rmSync(tmpDir, { recursive: true })
131
+ }
132
+ })
133
+
134
+ it('no systemctl call uses the capitalised display name as the unit target', async () => {
135
+ const tmpDir = makeTmpAgentDir()
136
+ try {
137
+ const { fn: execFileMock, calls } = makeDispatchingExecFile('klanker')
138
+
139
+ await runAllProbes({
140
+ agentName: 'Klanker',
141
+ agentSlug: 'klanker',
142
+ version: 'v0.3.0',
143
+ agentDir: tmpDir,
144
+ gatewayInfo: { pid: 12345, startedAtMs: Date.now() },
145
+ fetchImpl: async () => new Response('', { status: 200 }),
146
+ settleWindowMs: 0,
147
+ agentLiveWindowMs: 0,
148
+ probeExecFileImpl: execFileMock,
149
+ })
150
+
151
+ const systemctlCalls = calls.filter(c => c.cmd === 'systemctl')
152
+ expect(systemctlCalls.length, 'execFileMock must be called at least once').toBeGreaterThan(0)
153
+
154
+ for (const call of systemctlCalls) {
155
+ for (const arg of call.args) {
156
+ expect(
157
+ arg,
158
+ `systemctl was invoked with arg "${arg}" which contains the display name "Klanker" — expected slug "klanker"`,
159
+ ).not.toContain('Klanker')
160
+ }
161
+ }
162
+ } finally {
163
+ rmSync(tmpDir, { recursive: true })
164
+ }
165
+ })
166
+
167
+ it('falls back to agentName when agentSlug is not provided (backwards compat)', async () => {
168
+ const tmpDir = makeTmpAgentDir()
169
+ try {
170
+ const { fn: execFileMock, calls } = makeDispatchingExecFile('myagent')
171
+
172
+ await runAllProbes({
173
+ agentName: 'myagent',
174
+ // no agentSlug — should fall back to agentName
175
+ version: 'v0.3.0',
176
+ agentDir: tmpDir,
177
+ gatewayInfo: { pid: 12345, startedAtMs: Date.now() },
178
+ fetchImpl: async () => new Response('', { status: 200 }),
179
+ settleWindowMs: 0,
180
+ agentLiveWindowMs: 0,
181
+ probeExecFileImpl: execFileMock,
182
+ })
183
+
184
+ const agentProbeCall = calls.find(c =>
185
+ c.cmd === 'systemctl' && c.args.includes('show'),
186
+ )
187
+ expect(agentProbeCall, 'probeAgentProcess must call systemctl show').toBeDefined()
188
+ const unitArg = agentProbeCall!.args.find(a => a.startsWith('switchroom-'))
189
+ expect(unitArg).toBe('switchroom-myagent.service')
190
+ } finally {
191
+ rmSync(tmpDir, { recursive: true })
192
+ }
193
+ })
194
+ })
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Unit tests for determineRestartReason() — the pure helper that decides
3
+ * which restart reason to show in the boot card.
4
+ *
5
+ * Run with:
6
+ * bun test telegram-plugin/tests/boot-card-reason.test.ts
7
+ */
8
+ import { describe, it, expect } from 'bun:test'
9
+ import { determineRestartReason } from '../gateway/boot-reason.js'
10
+
11
+ const NOW = 1_700_000_000_000 // arbitrary fixed timestamp
12
+
13
+ // ── Marker fixtures ────────────────────────────────────────────────────────
14
+
15
+ function recentMarker(offsetMs = 0) {
16
+ return { ts: NOW - offsetMs }
17
+ }
18
+
19
+ function recentCleanMarker(offsetMs = 0) {
20
+ return { ts: NOW - offsetMs }
21
+ }
22
+
23
+ function sessionMarker() {
24
+ return { pid: 1234 }
25
+ }
26
+
27
+ // ── Tests ──────────────────────────────────────────────────────────────────
28
+
29
+ describe('determineRestartReason', () => {
30
+ it('returns "planned" when a restart marker is present and fresh (<5 min)', () => {
31
+ const result = determineRestartReason({
32
+ marker: recentMarker(10_000), // 10s ago
33
+ cleanMarker: null,
34
+ sessionMarker: sessionMarker(),
35
+ now: NOW,
36
+ })
37
+ expect(result).toBe('planned')
38
+ })
39
+
40
+ it('returns "graceful" when clean-shutdown marker is present and fresh, no restart marker', () => {
41
+ const result = determineRestartReason({
42
+ marker: null,
43
+ cleanMarker: recentCleanMarker(5_000), // 5s ago, within 60s default
44
+ sessionMarker: sessionMarker(),
45
+ now: NOW,
46
+ })
47
+ expect(result).toBe('graceful')
48
+ })
49
+
50
+ it('returns "crash" when session marker exists but no other markers', () => {
51
+ const result = determineRestartReason({
52
+ marker: null,
53
+ cleanMarker: null,
54
+ sessionMarker: sessionMarker(),
55
+ now: NOW,
56
+ })
57
+ expect(result).toBe('crash')
58
+ })
59
+
60
+ it('returns "fresh" when no markers exist at all (first ever start)', () => {
61
+ const result = determineRestartReason({
62
+ marker: null,
63
+ cleanMarker: null,
64
+ sessionMarker: null,
65
+ now: NOW,
66
+ })
67
+ expect(result).toBe('fresh')
68
+ })
69
+
70
+ it('returns "crash" (not "graceful") when clean-shutdown marker is stale (>60s)', () => {
71
+ const result = determineRestartReason({
72
+ marker: null,
73
+ cleanMarker: recentCleanMarker(90_000), // 90s ago, stale
74
+ sessionMarker: sessionMarker(),
75
+ now: NOW,
76
+ })
77
+ // stale clean-shutdown = marker too old to suppress crash detection
78
+ expect(result).toBe('crash')
79
+ })
80
+
81
+ it('returns "planned" even when clean-shutdown marker is also present (planned wins)', () => {
82
+ // Both present: planned restart marker takes priority
83
+ const result = determineRestartReason({
84
+ marker: recentMarker(3_000),
85
+ cleanMarker: recentCleanMarker(3_000),
86
+ sessionMarker: sessionMarker(),
87
+ now: NOW,
88
+ })
89
+ expect(result).toBe('planned')
90
+ })
91
+
92
+ it('respects custom markerMaxAgeMs — stale marker does not count as planned', () => {
93
+ const result = determineRestartReason({
94
+ marker: recentMarker(10 * 60_000), // 10 min ago
95
+ cleanMarker: null,
96
+ sessionMarker: sessionMarker(),
97
+ now: NOW,
98
+ markerMaxAgeMs: 5 * 60_000, // 5 min window
99
+ })
100
+ // Marker is too old to be "planned", session marker present → crash
101
+ expect(result).toBe('crash')
102
+ })
103
+ })
@@ -0,0 +1,219 @@
1
+ /**
2
+ * Tests for the post-#142 boot-card renderer.
3
+ *
4
+ * The contract this PR locks in:
5
+ * - Healthy boot → one line ack ("✅ <agent> back up · <version>"). No probe
6
+ * rows ever, regardless of `probes` shape (all-ok or absent).
7
+ * - Crash recovery → ⚠️ on the ack line + a single "Crash recovery" row.
8
+ * - Degraded probes (status='degraded' or 'fail') → one row per probe,
9
+ * in PROBE_KEYS order, with the matching dot emoji.
10
+ * - All-green probes after the settle window → no edit (text equals
11
+ * the initial post).
12
+ * - HTML escaping on agentName, version, and probe `detail`.
13
+ */
14
+
15
+ import { describe, it, expect } from 'vitest'
16
+ import { renderBootCard, resolvePersonaName } from '../gateway/boot-card.js'
17
+
18
+ describe('renderBootCard — quiet by default', () => {
19
+ it('returns one-line ack with default ✅ when no probes and no restart reason', () => {
20
+ const out = renderBootCard({ agentName: 'klanker', version: 'v0.3.0+44' })
21
+ expect(out).toBe('✅ <b>klanker</b> back up · v0.3.0+44')
22
+ expect(out).not.toContain('\n')
23
+ })
24
+
25
+ it('uses ✅ for planned and graceful restart reasons (return-to-service)', () => {
26
+ for (const reason of ['planned', 'graceful'] as const) {
27
+ const out = renderBootCard({ agentName: 'agent', version: 'v1', restartReason: reason })
28
+ expect(out).toBe('✅ <b>agent</b> back up · v1')
29
+ }
30
+ })
31
+
32
+ it('uses 🆕 for fresh starts (no prior session marker — first boot)', () => {
33
+ const out = renderBootCard({ agentName: 'a', version: 'v', restartReason: 'fresh' })
34
+ expect(out.startsWith('🆕')).toBe(true)
35
+ expect(out).toBe('🆕 <b>a</b> back up · v')
36
+ })
37
+
38
+ it('all-ok probes produce no extra rows — same output as no probes at all', () => {
39
+ const ackOnly = renderBootCard({ agentName: 'k', version: 'v0.3.0' })
40
+ const ackWithGreenProbes = renderBootCard({
41
+ agentName: 'k',
42
+ version: 'v0.3.0',
43
+ probes: {
44
+ account: { status: 'ok', label: 'Account', detail: 'a@b' },
45
+ agent: { status: 'ok', label: 'Agent', detail: 'PID 1' },
46
+ gateway: { status: 'ok', label: 'Gateway', detail: 'up 5s' },
47
+ quota: { status: 'ok', label: 'Quota', detail: '50% used' },
48
+ hindsight: { status: 'ok', label: 'Hindsight', detail: 'reachable' },
49
+ crons: { status: 'ok', label: 'Crons', detail: '0 timers' },
50
+ },
51
+ })
52
+ expect(ackWithGreenProbes).toBe(ackOnly)
53
+ })
54
+ })
55
+
56
+ describe('renderBootCard — degraded conditions', () => {
57
+ it('crash reason flips ack emoji to ⚠️ and appends a Crash recovery row', () => {
58
+ const out = renderBootCard({
59
+ agentName: 'lawgpt',
60
+ version: 'v0.3.0+44',
61
+ restartReason: 'crash',
62
+ restartAgeMs: 2_100,
63
+ })
64
+ const lines = out.split('\n')
65
+ expect(lines[0]).toBe('⚠️ <b>lawgpt</b> back up · v0.3.0+44')
66
+ expect(lines[1]).toBe('') // separator
67
+ expect(lines[2]).toContain('⚠️ <b>Restart</b>')
68
+ expect(lines[2]).toContain('crash recovery')
69
+ expect(lines[2]).toContain('2.1s ago')
70
+ })
71
+
72
+ it('crash reason without restartAgeMs omits the "ago" suffix', () => {
73
+ const out = renderBootCard({ agentName: 'a', version: 'v1', restartReason: 'crash' })
74
+ expect(out).toContain('⚠️ <b>Restart</b> crash recovery')
75
+ expect(out).not.toMatch(/\d+\.\d+s ago/)
76
+ })
77
+
78
+ it('appends one row per non-ok probe, healthy probes never render', () => {
79
+ const out = renderBootCard({
80
+ agentName: 'klanker',
81
+ version: 'v0.3.0',
82
+ probes: {
83
+ account: { status: 'degraded', label: 'Account', detail: 'token expiring · 3d' },
84
+ agent: { status: 'ok', label: 'Agent', detail: 'PID 1' },
85
+ gateway: { status: 'ok', label: 'Gateway', detail: 'up 30s' },
86
+ quota: { status: 'fail', label: 'Quota', detail: 'rate limited' },
87
+ hindsight: { status: 'ok', label: 'Hindsight', detail: 'reachable' },
88
+ crons: { status: 'ok', label: 'Crons', detail: '4 timers' },
89
+ },
90
+ })
91
+ expect(out).toContain('🟡 <b>Account</b> token expiring · 3d')
92
+ expect(out).toContain('🔴 <b>Quota</b> rate limited')
93
+ // Healthy probes must not render.
94
+ expect(out).not.toContain('Agent</b>')
95
+ expect(out).not.toContain('Gateway</b>')
96
+ expect(out).not.toContain('Hindsight</b>')
97
+ expect(out).not.toContain('Crons</b>')
98
+ })
99
+
100
+ it('orders probe rows in PROBE_KEYS canonical order regardless of object iteration', () => {
101
+ // Insert in a non-canonical order; renderer must still output Account first,
102
+ // then Hindsight, then Crons (matching PROBE_KEYS).
103
+ const out = renderBootCard({
104
+ agentName: 'a',
105
+ version: 'v',
106
+ probes: {
107
+ crons: { status: 'fail', label: 'Crons', detail: 'bad' },
108
+ hindsight: { status: 'fail', label: 'Hindsight', detail: 'unreachable' },
109
+ account: { status: 'degraded', label: 'Account', detail: 'expiring' },
110
+ },
111
+ })
112
+ const accountIdx = out.indexOf('Account</b>')
113
+ const hindsightIdx = out.indexOf('Hindsight</b>')
114
+ const cronsIdx = out.indexOf('Crons</b>')
115
+ expect(accountIdx).toBeGreaterThan(-1)
116
+ expect(hindsightIdx).toBeGreaterThan(accountIdx)
117
+ expect(cronsIdx).toBeGreaterThan(hindsightIdx)
118
+ })
119
+
120
+ it('crash + degraded probe = both rows render', () => {
121
+ const out = renderBootCard({
122
+ agentName: 'a',
123
+ version: 'v',
124
+ restartReason: 'crash',
125
+ restartAgeMs: 1_500,
126
+ probes: {
127
+ account: { status: 'degraded', label: 'Account', detail: 'expiring' },
128
+ },
129
+ })
130
+ const lines = out.split('\n')
131
+ expect(lines[0].startsWith('⚠️')).toBe(true)
132
+ expect(lines).toContain('') // separator after ack
133
+ expect(out).toContain('⚠️ <b>Restart</b>')
134
+ expect(out).toContain('🟡 <b>Account</b> expiring')
135
+ })
136
+
137
+ it('null probe entries are skipped (defensive against partial probe maps)', () => {
138
+ const out = renderBootCard({
139
+ agentName: 'a',
140
+ version: 'v',
141
+ probes: {
142
+ account: null,
143
+ agent: { status: 'fail', label: 'Agent', detail: 'service deactivating' },
144
+ },
145
+ })
146
+ expect(out).not.toContain('Account')
147
+ expect(out).toContain('🔴 <b>Agent</b> service deactivating')
148
+ })
149
+ })
150
+
151
+ describe('renderBootCard — HTML escaping', () => {
152
+ it('escapes special chars in agent name', () => {
153
+ const out = renderBootCard({ agentName: 'foo<bar>', version: 'v1' })
154
+ expect(out).toContain('foo&lt;bar&gt;')
155
+ expect(out).not.toContain('foo<bar>')
156
+ })
157
+
158
+ it('escapes special chars in version', () => {
159
+ const out = renderBootCard({ agentName: 'a', version: 'v<1>' })
160
+ expect(out).toContain('v&lt;1&gt;')
161
+ })
162
+
163
+ it('escapes special chars in probe detail', () => {
164
+ const out = renderBootCard({
165
+ agentName: 'a',
166
+ version: 'v',
167
+ probes: {
168
+ quota: { status: 'fail', label: 'Quota', detail: 'rate <limited>' },
169
+ },
170
+ })
171
+ expect(out).toContain('rate &lt;limited&gt;')
172
+ expect(out).not.toContain('rate <limited>')
173
+ })
174
+ })
175
+
176
+ describe('resolvePersonaName — persona name over slug (#169)', () => {
177
+ it('returns soul.name when set in config', () => {
178
+ const fakeCfg = () => ({
179
+ agents: { finn: { soul: { name: 'Finn', style: 'warm' } } },
180
+ })
181
+ expect(resolvePersonaName('finn', fakeCfg)).toBe('Finn')
182
+ })
183
+
184
+ it('falls back to slug when soul.name is absent', () => {
185
+ const fakeCfg = () => ({
186
+ agents: { finn: { soul: null } },
187
+ })
188
+ expect(resolvePersonaName('finn', fakeCfg)).toBe('finn')
189
+ })
190
+
191
+ it('falls back to slug when soul.name is empty string', () => {
192
+ const fakeCfg = () => ({
193
+ agents: { finn: { soul: { name: '', style: 'warm' } } },
194
+ })
195
+ expect(resolvePersonaName('finn', fakeCfg)).toBe('finn')
196
+ })
197
+
198
+ it('falls back to slug when agent key is not in config', () => {
199
+ const fakeCfg = () => ({ agents: {} })
200
+ expect(resolvePersonaName('finn', fakeCfg)).toBe('finn')
201
+ })
202
+
203
+ it('falls back to slug when config loader throws', () => {
204
+ const throwing = () => { throw new Error('no config file') }
205
+ expect(resolvePersonaName('finn', throwing)).toBe('finn')
206
+ })
207
+
208
+ it('boot card rendered with soul.name not slug — acceptance criterion for #169', () => {
209
+ // Simulate: slug is "finn", soul.name is "Finn"
210
+ const fakeCfg = () => ({
211
+ agents: { finn: { soul: { name: 'Finn', style: 'warm' } } },
212
+ })
213
+ const displayName = resolvePersonaName('finn', fakeCfg)
214
+ const out = renderBootCard({ agentName: displayName, version: 'v0.3.0+50' })
215
+ // User sees "Finn", not the slug "finn"
216
+ expect(out).toBe('✅ <b>Finn</b> back up · v0.3.0+50')
217
+ expect(out).not.toContain('<b>finn</b>')
218
+ })
219
+ })