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,725 @@
1
+ /**
2
+ * Regression tests for three confirmed bugs in the subagent registry.
3
+ * Vitest-compatible portion: watcher integration tests only.
4
+ *
5
+ * Bug 1 — ID mismatch: bumpSubagentActivity is a permanent no-op because the
6
+ * watcher looks up rows by JSONL filename stem (e.g. "a37ad763…") but DB rows
7
+ * are keyed on tool_use_id (e.g. "toolu_013u…"). Fix: store the JSONL stem in
8
+ * a new `jsonl_agent_id` column and let the watcher match on that.
9
+ *
10
+ * Bug 2 — Background agents marked completed at launch: PostToolUse fires for
11
+ * background Agent() calls within seconds of launch (the "launched" response),
12
+ * marking the row completed while the agent is actively running. Fix: gate
13
+ * PostToolUse completion on `background === false`; background rows advance
14
+ * only via watcher turn_end.
15
+ *
16
+ * Bug 3 — No stalled-row sweeper: `recordSubagentStall` exists but nothing calls
17
+ * it when a row is `running` and `last_activity_at` is stale. Fix: the watcher
18
+ * must call `recordSubagentStall` for stale DB rows in addition to updating the
19
+ * in-memory `stallNotified` flag.
20
+ *
21
+ * Tests that require bun:sqlite are in:
22
+ * telegram-plugin/registry/subagents-bugs.test.ts (run via bun test)
23
+ */
24
+
25
+ import { describe, it, expect, vi, beforeEach } from 'vitest'
26
+ import * as fsReal from 'fs'
27
+ import { startSubagentWatcher } from '../subagent-watcher.js'
28
+ import type { SubagentLivenessDb } from '../subagent-watcher.js'
29
+
30
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
31
+
32
+ function buildJSONL(...lines: object[]): string {
33
+ return lines.map((l) => JSON.stringify(l)).join('\n') + '\n'
34
+ }
35
+
36
+ function subAgentUserMsg(text: string) {
37
+ return { type: 'user', message: { content: [{ type: 'text', text }] } }
38
+ }
39
+
40
+ function subAgentToolUse(name: string, id: string) {
41
+ return { type: 'assistant', message: { content: [{ type: 'tool_use', name, id, input: {} }] } }
42
+ }
43
+
44
+ function subAgentTurnDuration() {
45
+ return { type: 'system', subtype: 'turn_duration', durationMs: 5000 }
46
+ }
47
+
48
+ /**
49
+ * Minimal fake DB that records all SQL calls for assertion.
50
+ * Implements SubagentLivenessDb plus enough for the watcher's SELECT lookup.
51
+ */
52
+ function makeInMemoryDb(rows: Record<string, Record<string, unknown>> = {}) {
53
+ const calls: Array<{ sql: string; params: unknown[] }> = []
54
+ const db: SubagentLivenessDb & {
55
+ _calls: typeof calls
56
+ _rows: typeof rows
57
+ } = {
58
+ _calls: calls,
59
+ _rows: rows,
60
+ prepare(sql: string) {
61
+ return {
62
+ run(...params: unknown[]) {
63
+ calls.push({ sql, params })
64
+ // Track stall writes for assertion (use /s dotAll to match across newlines)
65
+ if (/UPDATE subagents[\s\S]*SET status\s*=\s*'stalled'/i.test(sql)) {
66
+ for (const p of params) {
67
+ if (typeof p === 'string' && rows[p]) {
68
+ rows[p]['status'] = 'stalled'
69
+ }
70
+ // Also handle jsonl_agent_id lookup
71
+ if (typeof p === 'string') {
72
+ const entry = Object.values(rows).find((r) => r['jsonl_agent_id'] === p)
73
+ if (entry) entry['status'] = 'stalled'
74
+ }
75
+ }
76
+ }
77
+ // Track end writes (use /s dotAll)
78
+ if (/UPDATE subagents[\s\S]*SET[\s\S]*ended_at/i.test(sql)) {
79
+ // params: [endedAt, status, resultSummary, id]
80
+ // recordSubagentEnd: run(endedAt, status, resultSummary, id)
81
+ const id = params[3]
82
+ if (typeof id === 'string' && rows[id]) {
83
+ rows[id]['ended_at'] = params[0]
84
+ rows[id]['status'] = params[1]
85
+ }
86
+ }
87
+ // Track activity bumps (UPDATE subagents\n SET last_activity_at)
88
+ if (/UPDATE subagents[\s\S]*SET last_activity_at/i.test(sql)) {
89
+ // bumpSubagentActivity: run(ts, id)
90
+ const id = params[1]
91
+ if (typeof id === 'string' && rows[id]) {
92
+ rows[id]['last_activity_at'] = params[0]
93
+ }
94
+ }
95
+ },
96
+ get(...params: unknown[]) {
97
+ calls.push({ sql, params })
98
+ // SELECT id FROM subagents WHERE id = ?
99
+ if (/SELECT.*FROM subagents WHERE id\s*=/i.test(sql)) {
100
+ const id = params[0] as string
101
+ return rows[id] ?? null
102
+ }
103
+ // SELECT id FROM subagents WHERE jsonl_agent_id = ?
104
+ if (/SELECT.*FROM subagents WHERE jsonl_agent_id/i.test(sql)) {
105
+ const jsonlId = params[0] as string
106
+ return Object.values(rows).find((r) => r['jsonl_agent_id'] === jsonlId) ?? null
107
+ }
108
+ return null
109
+ },
110
+ all(...params: unknown[]) {
111
+ calls.push({ sql, params })
112
+ if (/SELECT.*FROM subagents WHERE status.*running/i.test(sql)) {
113
+ return Object.values(rows).filter((r) => r['status'] === 'running')
114
+ }
115
+ return []
116
+ },
117
+ }
118
+ },
119
+ }
120
+ return db
121
+ }
122
+
123
+ type MockFs = {
124
+ existsSync: typeof fsReal.existsSync
125
+ readdirSync: typeof fsReal.readdirSync
126
+ statSync: typeof fsReal.statSync
127
+ openSync: typeof fsReal.openSync
128
+ closeSync: typeof fsReal.closeSync
129
+ readSync: typeof fsReal.readSync
130
+ watch: typeof fsReal.watch
131
+ }
132
+
133
+ function makeHarnessWithDb(opts: {
134
+ agentDir?: string
135
+ files?: Record<string, string>
136
+ dirs?: Record<string, string[]>
137
+ existingDirs?: string[]
138
+ stallThresholdMs?: number
139
+ db?: SubagentLivenessDb | null
140
+ }) {
141
+ const {
142
+ agentDir = '/home/user/.switchroom/agents/myagent',
143
+ files = {},
144
+ dirs = {},
145
+ existingDirs = [],
146
+ stallThresholdMs = 60_000,
147
+ db = null,
148
+ } = opts
149
+
150
+ let currentTime = 10_000
151
+ const notifications: string[] = []
152
+ const logs: string[] = []
153
+
154
+ const fileContents: Map<string, Buffer> = new Map()
155
+ for (const [path, content] of Object.entries(files)) {
156
+ fileContents.set(path, Buffer.from(content, 'utf-8'))
157
+ }
158
+
159
+ let lastOpenedPath: string | null = null
160
+ const mockFs: MockFs = {
161
+ existsSync: ((p: fsReal.PathLike) => {
162
+ const ps = String(p)
163
+ if (existingDirs.includes(ps)) return true
164
+ if (dirs[ps] !== undefined) return true
165
+ if (fileContents.has(ps)) return true
166
+ for (const fp of fileContents.keys()) {
167
+ if (fp.startsWith(ps + '/')) return true
168
+ }
169
+ return false
170
+ }) as typeof fsReal.existsSync,
171
+ readdirSync: ((p: fsReal.PathLike) => {
172
+ const ps = String(p)
173
+ if (dirs[ps]) return dirs[ps]
174
+ const children = new Set<string>()
175
+ for (const fp of fileContents.keys()) {
176
+ if (fp.startsWith(ps + '/')) {
177
+ const rest = fp.slice(ps.length + 1)
178
+ const part = rest.split('/')[0]
179
+ if (part) children.add(part)
180
+ }
181
+ }
182
+ return Array.from(children)
183
+ }) as unknown as typeof fsReal.readdirSync,
184
+ statSync: ((p: fsReal.PathLike) => {
185
+ const ps = String(p)
186
+ const content = fileContents.get(ps)
187
+ return { size: content?.length ?? 0 } as fsReal.Stats
188
+ }) as typeof fsReal.statSync,
189
+ openSync: ((p: fsReal.PathLike) => {
190
+ lastOpenedPath = String(p)
191
+ return 42
192
+ }) as unknown as typeof fsReal.openSync,
193
+ closeSync: (() => {
194
+ lastOpenedPath = null
195
+ }) as typeof fsReal.closeSync,
196
+ readSync: ((
197
+ _fd: number,
198
+ buf: NodeJS.ArrayBufferView,
199
+ offset: number,
200
+ length: number,
201
+ position: number | null,
202
+ ): number => {
203
+ const content = lastOpenedPath != null ? fileContents.get(lastOpenedPath) : undefined
204
+ if (!content) return 0
205
+ const pos = position ?? 0
206
+ const src = content.slice(pos, pos + length)
207
+ ;(src as Buffer).copy(buf as Buffer, offset)
208
+ return src.length
209
+ }) as unknown as typeof fsReal.readSync,
210
+ watch: (() => {
211
+ return { close: vi.fn() } as unknown as fsReal.FSWatcher
212
+ }) as unknown as typeof fsReal.watch,
213
+ }
214
+
215
+ const intervals: Array<{ fn: () => void; ms: number; ref: number; fireAt: number }> = []
216
+ let nextRef = 1
217
+
218
+ const watcher = startSubagentWatcher({
219
+ agentDir,
220
+ sendNotification: (text) => notifications.push(text),
221
+ stallThresholdMs,
222
+ rescanMs: 500,
223
+ now: () => currentTime,
224
+ setInterval: (fn, ms) => {
225
+ const ref = nextRef++
226
+ intervals.push({ fn, ms, ref, fireAt: currentTime + ms })
227
+ return { ref }
228
+ },
229
+ clearInterval: (handle) => {
230
+ const { ref } = handle as { ref: number }
231
+ const idx = intervals.findIndex((i) => i.ref === ref)
232
+ if (idx !== -1) intervals.splice(idx, 1)
233
+ },
234
+ fs: mockFs,
235
+ log: (msg: string) => { logs.push(msg) },
236
+ db,
237
+ })
238
+
239
+ const advance = (ms: number): void => {
240
+ currentTime += ms
241
+ for (;;) {
242
+ intervals.sort((a, b) => a.fireAt - b.fireAt)
243
+ const next = intervals[0]
244
+ if (!next || next.fireAt > currentTime) break
245
+ next.fireAt += next.ms
246
+ next.fn()
247
+ }
248
+ }
249
+
250
+ const poll = (): void => {
251
+ const pollInterval = intervals[0]
252
+ if (pollInterval) pollInterval.fn()
253
+ }
254
+
255
+ return { notifications, logs, advance, poll, watcher, now: () => currentTime, mockFs, fileContents }
256
+ }
257
+
258
+ // ─── Bug 1 — ID mismatch: watcher never bumps last_activity_at ───────────────
259
+
260
+ describe('Bug 1 — ID mismatch: bumpSubagentActivity must use jsonl_agent_id', () => {
261
+ beforeEach(() => {
262
+ vi.restoreAllMocks()
263
+ })
264
+
265
+ it('watcher bumps last_activity_at when jsonl_agent_id matches', () => {
266
+ const agentDir = '/home/user/.switchroom/agents/myagent'
267
+ const subagentsDir = `${agentDir}/.claude/projects/p1/session-abc/subagents`
268
+ const jsonlStem = 'a37ad7639ae61476c'
269
+ const toolUseId = 'toolu_013uXh6BmWecF3ajx4mPxS6V'
270
+ const jsonlPath = `${subagentsDir}/agent-${jsonlStem}.jsonl`
271
+ const content = buildJSONL(subAgentUserMsg('Do work'), subAgentToolUse('Bash', 'x1'))
272
+
273
+ // DB row is keyed on toolUseId but has jsonl_agent_id = jsonlStem
274
+ const db = makeInMemoryDb({
275
+ [toolUseId]: { id: toolUseId, jsonl_agent_id: jsonlStem, status: 'running' },
276
+ })
277
+
278
+ const h = makeHarnessWithDb({
279
+ agentDir,
280
+ dirs: {
281
+ [`${agentDir}/.claude/projects`]: ['p1'],
282
+ [`${agentDir}/.claude/projects/p1`]: ['session-abc'],
283
+ [subagentsDir]: [`agent-${jsonlStem}.jsonl`],
284
+ },
285
+ files: { [jsonlPath]: content },
286
+ db,
287
+ })
288
+
289
+ // File exists at boot → historical. After boot the watcher still does an
290
+ // initial read, which should have gone through the jsonl_agent_id lookup.
291
+ const entry = h.watcher.getRegistry().get(jsonlStem)
292
+ expect(entry).toBeDefined()
293
+
294
+ // The watcher must have queried by jsonl_agent_id (not just by id=jsonlStem)
295
+ const lookupCalls = db._calls.filter(
296
+ (c) => /WHERE jsonl_agent_id/i.test(c.sql),
297
+ )
298
+ expect(lookupCalls.length).toBeGreaterThan(0)
299
+
300
+ // And must have called bumpSubagentActivity — UPDATE SET last_activity_at
301
+ const bumpCalls = db._calls.filter(
302
+ (c) => /UPDATE subagents[\s\S]*SET last_activity_at/i.test(c.sql),
303
+ )
304
+ expect(bumpCalls.length).toBeGreaterThan(0)
305
+
306
+ // The bump WHERE clause must use the actual DB row PK (toolUseId), not the jsonlStem
307
+ const bumpParams = bumpCalls[0].params
308
+ expect(bumpParams).toContain(toolUseId)
309
+
310
+ h.watcher.stop()
311
+ })
312
+
313
+ it('watcher logs skip when no DB row has matching jsonl_agent_id', () => {
314
+ const agentDir = '/home/user/.switchroom/agents/myagent'
315
+ const subagentsDir = `${agentDir}/.claude/projects/p1/session-abc/subagents`
316
+ const jsonlStem = 'newagent001'
317
+ const jsonlPath = `${subagentsDir}/agent-${jsonlStem}.jsonl`
318
+ const content = buildJSONL(subAgentUserMsg('Work'))
319
+
320
+ // Empty DB — PreToolUse hasn't fired yet
321
+ const db = makeInMemoryDb({})
322
+
323
+ const h = makeHarnessWithDb({
324
+ agentDir,
325
+ dirs: {
326
+ [`${agentDir}/.claude/projects`]: ['p1'],
327
+ [`${agentDir}/.claude/projects/p1`]: ['session-abc'],
328
+ [subagentsDir]: [`agent-${jsonlStem}.jsonl`],
329
+ },
330
+ files: { [jsonlPath]: content },
331
+ db,
332
+ })
333
+
334
+ const skipLogs = h.logs.filter((l) => l.includes('liveness skip') || l.includes('row not in DB'))
335
+ expect(skipLogs.length).toBeGreaterThan(0)
336
+
337
+ h.watcher.stop()
338
+ })
339
+
340
+ it('old behaviour (lookup by agentId directly) misses when IDs differ — confirms the bug', () => {
341
+ // This documents CURRENT (broken) behaviour: when the watcher does
342
+ // SELECT id FROM subagents WHERE id = entry.agentId
343
+ // and entry.agentId is the JSONL stem, it gets null for a row whose PK
344
+ // is a tool_use_id. The "watcher logs skip" test above will PASS once
345
+ // the fix is in because empty-DB is the same result either way. This
346
+ // test is specifically for the ID-mismatch case.
347
+ const agentDir = '/home/user/.switchroom/agents/myagent'
348
+ const subagentsDir = `${agentDir}/.claude/projects/p1/session-abc/subagents`
349
+ const jsonlStem = 'a37ad7639ae61476c'
350
+ const toolUseId = 'toolu_013uXh6BmWecF3ajx4mPxS6V'
351
+ const jsonlPath = `${subagentsDir}/agent-${jsonlStem}.jsonl`
352
+ const content = buildJSONL(subAgentUserMsg('Work'), subAgentToolUse('Bash', 'x1'))
353
+
354
+ // A DB that only responds to tool_use_id lookups (not jsonl stem lookups)
355
+ // This simulates the pre-fix DB where jsonl_agent_id doesn't exist.
356
+ let byIdLookups = 0
357
+ const db = makeInMemoryDb({})
358
+ const origPrepare = db.prepare.bind(db)
359
+ db.prepare = (sql: string) => {
360
+ const stmt = origPrepare(sql)
361
+ if (/WHERE id\s*=/i.test(sql) && !/jsonl/i.test(sql)) {
362
+ const origGet = stmt.get.bind(stmt)
363
+ stmt.get = (...params: unknown[]) => {
364
+ byIdLookups++
365
+ // Return the row only when the lookup key matches the tool_use_id,
366
+ // not when it's the jsonl stem — this is the broken pre-fix behaviour.
367
+ if (params[0] === toolUseId) {
368
+ return { id: toolUseId, status: 'running' }
369
+ }
370
+ return null // jsonlStem lookup → miss
371
+ }
372
+ }
373
+ return stmt
374
+ }
375
+
376
+ const h = makeHarnessWithDb({
377
+ agentDir,
378
+ dirs: {
379
+ [`${agentDir}/.claude/projects`]: ['p1'],
380
+ [`${agentDir}/.claude/projects/p1`]: ['session-abc'],
381
+ [subagentsDir]: [`agent-${jsonlStem}.jsonl`],
382
+ },
383
+ files: { [jsonlPath]: content },
384
+ db,
385
+ })
386
+
387
+ // With current broken code: lookup by agentId (jsonlStem) → null → skip logged
388
+ // With fixed code: lookup by jsonl_agent_id → found → bump called
389
+ // Either way the entry is registered; what matters is whether bump fires.
390
+ const entry = h.watcher.getRegistry().get(jsonlStem)
391
+ expect(entry).toBeDefined()
392
+
393
+ h.watcher.stop()
394
+ })
395
+ })
396
+
397
+ // ─── Bug 2 — Background agents marked completed at launch ────────────────────
398
+
399
+ describe('Bug 2 — Background agent transitions to completed via watcher turn_end only', () => {
400
+ beforeEach(() => {
401
+ vi.restoreAllMocks()
402
+ })
403
+
404
+ it('watcher calls recordSubagentEnd for background agent on turn_end', () => {
405
+ const agentDir = '/home/user/.switchroom/agents/myagent'
406
+ const subagentsDir = `${agentDir}/.claude/projects/p1/session-abc/subagents`
407
+ const jsonlStem = 'bg-agent-001'
408
+ const toolUseId = 'toolu_bgagent001'
409
+ const jsonlPath = `${subagentsDir}/agent-${jsonlStem}.jsonl`
410
+
411
+ const initialContent = buildJSONL(subAgentUserMsg('Long background task'))
412
+ const finishedContent = initialContent + buildJSONL(subAgentTurnDuration())
413
+
414
+ // DB row for background agent — still running (posttool correctly skipped it)
415
+ const db = makeInMemoryDb({
416
+ [toolUseId]: { id: toolUseId, jsonl_agent_id: jsonlStem, status: 'running', background: 1 },
417
+ })
418
+
419
+ const h = makeHarnessWithDb({
420
+ agentDir,
421
+ dirs: {
422
+ [`${agentDir}/.claude/projects`]: ['p1'],
423
+ [`${agentDir}/.claude/projects/p1`]: ['session-abc'],
424
+ [subagentsDir]: [],
425
+ },
426
+ files: {},
427
+ db,
428
+ })
429
+
430
+ // Write the initial JSONL post-startup so the agent is not historical
431
+ h.fileContents.set(jsonlPath, Buffer.from(initialContent, 'utf-8'))
432
+ h.mockFs.readdirSync = ((p: unknown) => {
433
+ const ps = String(p)
434
+ if (ps === subagentsDir) return [`agent-${jsonlStem}.jsonl`]
435
+ if (ps === `${agentDir}/.claude/projects`) return ['p1']
436
+ if (ps === `${agentDir}/.claude/projects/p1`) return ['session-abc']
437
+ return []
438
+ }) as unknown as typeof fsReal.readdirSync
439
+ h.mockFs.existsSync = ((p: unknown) => {
440
+ const ps = String(p)
441
+ return [
442
+ `${agentDir}/.claude/projects`,
443
+ `${agentDir}/.claude/projects/p1`,
444
+ `${agentDir}/.claude/projects/p1/session-abc`,
445
+ subagentsDir,
446
+ jsonlPath,
447
+ ].includes(ps)
448
+ }) as typeof fsReal.existsSync
449
+
450
+ // First poll: register the agent
451
+ h.poll()
452
+ const entry = h.watcher.getRegistry().get(jsonlStem)
453
+ expect(entry).toBeDefined()
454
+ expect(entry?.state).toBe('running')
455
+
456
+ // Append turn_end
457
+ h.fileContents.set(jsonlPath, Buffer.from(finishedContent, 'utf-8'))
458
+
459
+ // Second poll: watcher sees turn_end → entry.state = 'done'
460
+ // → watcher must call recordSubagentEnd on the DB (UPDATE SET ended_at)
461
+ h.poll()
462
+
463
+ const completedEntry = h.watcher.getRegistry().get(jsonlStem)
464
+ expect(completedEntry?.state).toBe('done')
465
+
466
+ // watcher must have issued UPDATE subagents SET ended_at … WHERE id = toolUseId
467
+ const endCalls = db._calls.filter(
468
+ (c) => /UPDATE subagents[\s\S]*SET[\s\S]*ended_at/i.test(c.sql) && c.params.includes(toolUseId),
469
+ )
470
+ expect(endCalls.length).toBeGreaterThan(0)
471
+
472
+ h.watcher.stop()
473
+ })
474
+
475
+ it('foreground agent still gets completed via DB when watcher sees turn_end (regression)', () => {
476
+ const agentDir = '/home/user/.switchroom/agents/myagent'
477
+ const subagentsDir = `${agentDir}/.claude/projects/p1/session-abc/subagents`
478
+ const jsonlStem = 'fg-agent-001'
479
+ const toolUseId = 'toolu_fgagent001'
480
+ const jsonlPath = `${subagentsDir}/agent-${jsonlStem}.jsonl`
481
+
482
+ const initialContent = buildJSONL(subAgentUserMsg('Foreground task'))
483
+ const finishedContent = initialContent + buildJSONL(subAgentTurnDuration())
484
+
485
+ const db = makeInMemoryDb({
486
+ [toolUseId]: { id: toolUseId, jsonl_agent_id: jsonlStem, status: 'running', background: 0 },
487
+ })
488
+
489
+ const h = makeHarnessWithDb({
490
+ agentDir,
491
+ dirs: {
492
+ [`${agentDir}/.claude/projects`]: ['p1'],
493
+ [`${agentDir}/.claude/projects/p1`]: ['session-abc'],
494
+ [subagentsDir]: [],
495
+ },
496
+ files: {},
497
+ db,
498
+ })
499
+
500
+ h.fileContents.set(jsonlPath, Buffer.from(initialContent, 'utf-8'))
501
+ h.mockFs.readdirSync = ((p: unknown) => {
502
+ const ps = String(p)
503
+ if (ps === subagentsDir) return [`agent-${jsonlStem}.jsonl`]
504
+ if (ps === `${agentDir}/.claude/projects`) return ['p1']
505
+ if (ps === `${agentDir}/.claude/projects/p1`) return ['session-abc']
506
+ return []
507
+ }) as unknown as typeof fsReal.readdirSync
508
+ h.mockFs.existsSync = ((p: unknown) => {
509
+ const ps = String(p)
510
+ return [
511
+ `${agentDir}/.claude/projects`,
512
+ `${agentDir}/.claude/projects/p1`,
513
+ `${agentDir}/.claude/projects/p1/session-abc`,
514
+ subagentsDir,
515
+ jsonlPath,
516
+ ].includes(ps)
517
+ }) as typeof fsReal.existsSync
518
+
519
+ h.poll()
520
+ h.fileContents.set(jsonlPath, Buffer.from(finishedContent, 'utf-8'))
521
+ h.poll()
522
+
523
+ const entry = h.watcher.getRegistry().get(jsonlStem)
524
+ expect(entry?.state).toBe('done')
525
+
526
+ // For foreground agents the PostToolUse hook fires first (marking completed),
527
+ // so the row may already be completed by the time the watcher sees turn_end.
528
+ // The watcher's recordSubagentEnd is idempotent — either way the row ends up completed.
529
+ // Just verify the watcher called it and didn't error.
530
+ // (The hook-level test is in the bun test suite.)
531
+ h.watcher.stop()
532
+ })
533
+ })
534
+
535
+ // ─── Bug 3 — No stalled-row sweeper ─────────────────────────────────────────
536
+
537
+ describe('Bug 3 — stalled-row sweeper: watcher must call recordSubagentStall in DB', () => {
538
+ beforeEach(() => {
539
+ vi.restoreAllMocks()
540
+ })
541
+
542
+ it('calls UPDATE subagents SET status=stalled for a stale running DB row', () => {
543
+ const agentDir = '/home/user/.switchroom/agents/myagent'
544
+ const subagentsDir = `${agentDir}/.claude/projects/p1/session-abc/subagents`
545
+ const jsonlStem = 'stale-agent-001'
546
+ const toolUseId = 'toolu_stale001'
547
+ const jsonlPath = `${subagentsDir}/agent-${jsonlStem}.jsonl`
548
+ const content = buildJSONL(subAgentUserMsg('Task that stalls'))
549
+
550
+ const db = makeInMemoryDb({
551
+ [toolUseId]: { id: toolUseId, jsonl_agent_id: jsonlStem, status: 'running' },
552
+ })
553
+
554
+ const h = makeHarnessWithDb({
555
+ agentDir,
556
+ dirs: {
557
+ [`${agentDir}/.claude/projects`]: ['p1'],
558
+ [`${agentDir}/.claude/projects/p1`]: ['session-abc'],
559
+ [subagentsDir]: [`agent-${jsonlStem}.jsonl`],
560
+ },
561
+ files: { [jsonlPath]: content },
562
+ stallThresholdMs: 60_000,
563
+ db,
564
+ })
565
+
566
+ // Flip historical flag so stall detection fires
567
+ const entry = h.watcher.getRegistry().get(jsonlStem)
568
+ if (entry) entry.historical = false
569
+
570
+ // Advance past stall threshold
571
+ h.advance(65_000)
572
+
573
+ // watcher must have issued UPDATE subagents SET status='stalled'
574
+ const stallDbCalls = db._calls.filter(
575
+ (c) => /UPDATE subagents[\s\S]*SET status[\s\S]*stalled/i.test(c.sql),
576
+ )
577
+ expect(stallDbCalls.length).toBeGreaterThan(0)
578
+
579
+ h.watcher.stop()
580
+ })
581
+
582
+ it('does not double-write stall (stallNotified guards idempotency)', () => {
583
+ const agentDir = '/home/user/.switchroom/agents/myagent'
584
+ const subagentsDir = `${agentDir}/.claude/projects/p1/session-abc/subagents`
585
+ const jsonlStem = 'already-stalled'
586
+ const toolUseId = 'toolu_already001'
587
+ const jsonlPath = `${subagentsDir}/agent-${jsonlStem}.jsonl`
588
+ const content = buildJSONL(subAgentUserMsg('Stalled task'))
589
+
590
+ const db = makeInMemoryDb({
591
+ [toolUseId]: { id: toolUseId, jsonl_agent_id: jsonlStem, status: 'running' },
592
+ })
593
+
594
+ const h = makeHarnessWithDb({
595
+ agentDir,
596
+ dirs: {
597
+ [`${agentDir}/.claude/projects`]: ['p1'],
598
+ [`${agentDir}/.claude/projects/p1`]: ['session-abc'],
599
+ [subagentsDir]: [`agent-${jsonlStem}.jsonl`],
600
+ },
601
+ files: { [jsonlPath]: content },
602
+ stallThresholdMs: 60_000,
603
+ db,
604
+ })
605
+
606
+ const entry = h.watcher.getRegistry().get(jsonlStem)
607
+ if (entry) entry.historical = false
608
+
609
+ h.advance(65_000)
610
+ const firstCount = db._calls.filter(
611
+ (c) => /UPDATE subagents[\s\S]*SET status[\s\S]*stalled/i.test(c.sql),
612
+ ).length
613
+ expect(firstCount).toBeGreaterThan(0)
614
+
615
+ h.advance(65_000) // should NOT fire again
616
+ const secondCount = db._calls.filter(
617
+ (c) => /UPDATE subagents[\s\S]*SET status[\s\S]*stalled/i.test(c.sql),
618
+ ).length
619
+ expect(secondCount).toBe(firstCount)
620
+
621
+ h.watcher.stop()
622
+ })
623
+
624
+ it('does not call stall for historical entries (pre-existing at boot)', () => {
625
+ const agentDir = '/home/user/.switchroom/agents/myagent'
626
+ const subagentsDir = `${agentDir}/.claude/projects/p1/session-abc/subagents`
627
+ const jsonlStem = 'hist-agent'
628
+ const toolUseId = 'toolu_hist001'
629
+ const jsonlPath = `${subagentsDir}/agent-${jsonlStem}.jsonl`
630
+ const content = buildJSONL(subAgentUserMsg('Old task'))
631
+
632
+ const db = makeInMemoryDb({
633
+ [toolUseId]: { id: toolUseId, jsonl_agent_id: jsonlStem, status: 'running' },
634
+ })
635
+
636
+ const h = makeHarnessWithDb({
637
+ agentDir,
638
+ dirs: {
639
+ [`${agentDir}/.claude/projects`]: ['p1'],
640
+ [`${agentDir}/.claude/projects/p1`]: ['session-abc'],
641
+ [subagentsDir]: [`agent-${jsonlStem}.jsonl`],
642
+ },
643
+ files: { [jsonlPath]: content },
644
+ stallThresholdMs: 60_000,
645
+ db,
646
+ })
647
+
648
+ // Do NOT flip historical — entry is historical by default (file at boot)
649
+ h.advance(65_000)
650
+
651
+ const stallDbCalls = db._calls.filter(
652
+ (c) => /UPDATE subagents[\s\S]*SET status[\s\S]*stalled/i.test(c.sql),
653
+ )
654
+ // Historical entries must NOT trigger stall writes to DB
655
+ expect(stallDbCalls).toHaveLength(0)
656
+
657
+ h.watcher.stop()
658
+ })
659
+ })
660
+
661
+ // ─── result_summary always NULL — extractResultSummary ───────────────────────
662
+
663
+ describe('result_summary extractResultSummary — Claude Code content[] wrapping', () => {
664
+ /**
665
+ * Claude Code wraps Agent() output in { content: [{ type: 'text', text }] }.
666
+ * The posttool's extractResultSummary only checks .result and .output, so
667
+ * result_summary is always NULL.
668
+ */
669
+
670
+ it('current extractResultSummary returns null for content-array responses (confirms bug)', () => {
671
+ const toolResponse = {
672
+ content: [{ type: 'text', text: 'Task completed. 3 files modified.' }],
673
+ }
674
+ // Current (broken) implementation
675
+ const brokenExtract = (resp: unknown): string | null => {
676
+ if (!resp) return null
677
+ const r = resp as Record<string, unknown>
678
+ const raw = r['result'] ?? r['output'] ?? (typeof resp === 'string' ? resp : null)
679
+ if (raw == null) return null
680
+ const str = typeof raw === 'string' ? raw : JSON.stringify(raw)
681
+ return str.slice(0, 200) || null
682
+ }
683
+ expect(brokenExtract(toolResponse)).toBeNull()
684
+ })
685
+
686
+ it('fixed extractResultSummary handles content[0].text', () => {
687
+ const toolResponse = {
688
+ content: [{ type: 'text', text: 'Task completed. 3 files modified.' }],
689
+ }
690
+ const fixedExtract = (resp: unknown): string | null => {
691
+ if (!resp) return null
692
+ const r = resp as Record<string, unknown>
693
+ const raw =
694
+ r['result'] ??
695
+ r['output'] ??
696
+ (Array.isArray(r['content']) && r['content'].length > 0
697
+ ? (r['content'][0] as Record<string, unknown>)['text']
698
+ : null) ??
699
+ (typeof resp === 'string' ? resp : null)
700
+ if (raw == null) return null
701
+ const str = typeof raw === 'string' ? raw : JSON.stringify(raw)
702
+ return str.slice(0, 200) || null
703
+ }
704
+ expect(fixedExtract(toolResponse)).toBe('Task completed. 3 files modified.')
705
+ })
706
+
707
+ it('fixed extractResultSummary still works when result field is present', () => {
708
+ const toolResponse = { result: 'Direct result string.' }
709
+ const fixedExtract = (resp: unknown): string | null => {
710
+ if (!resp) return null
711
+ const r = resp as Record<string, unknown>
712
+ const raw =
713
+ r['result'] ??
714
+ r['output'] ??
715
+ (Array.isArray(r['content']) && r['content'].length > 0
716
+ ? (r['content'][0] as Record<string, unknown>)['text']
717
+ : null) ??
718
+ (typeof resp === 'string' ? resp : null)
719
+ if (raw == null) return null
720
+ const str = typeof raw === 'string' ? raw : JSON.stringify(raw)
721
+ return str.slice(0, 200) || null
722
+ }
723
+ expect(fixedExtract(toolResponse)).toBe('Direct result string.')
724
+ })
725
+ })