xtrm-tools 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 (333) hide show
  1. package/CHANGELOG.md +504 -0
  2. package/README.md +201 -0
  3. package/cli/dist/index.cjs +57378 -0
  4. package/cli/dist/index.cjs.map +1 -0
  5. package/cli/dist/index.d.cts +2 -0
  6. package/cli/package.json +47 -0
  7. package/config/.env.example +40 -0
  8. package/config/hooks.json +72 -0
  9. package/config/instructions/agents-top.md +30 -0
  10. package/config/instructions/claude-top.md +30 -0
  11. package/config/mcp_servers.json +57 -0
  12. package/config/mcp_servers_optional.json +53 -0
  13. package/config/pi/auth.json.template +14 -0
  14. package/config/pi/extensions/auto-session-name/index.ts +29 -0
  15. package/config/pi/extensions/auto-session-name/package.json +16 -0
  16. package/config/pi/extensions/auto-update/index.ts +71 -0
  17. package/config/pi/extensions/auto-update/package.json +16 -0
  18. package/config/pi/extensions/beads/index.ts +166 -0
  19. package/config/pi/extensions/beads/package.json +16 -0
  20. package/config/pi/extensions/bg-process/index.ts +230 -0
  21. package/config/pi/extensions/bg-process/package.json +16 -0
  22. package/config/pi/extensions/compact-header/index.ts +69 -0
  23. package/config/pi/extensions/compact-header/package.json +16 -0
  24. package/config/pi/extensions/core/adapter.ts +52 -0
  25. package/config/pi/extensions/core/guard-rules.ts +102 -0
  26. package/config/pi/extensions/core/lib.ts +3 -0
  27. package/config/pi/extensions/core/logger.ts +45 -0
  28. package/config/pi/extensions/core/runner.ts +71 -0
  29. package/config/pi/extensions/core/session-state.ts +59 -0
  30. package/config/pi/extensions/custom-footer/index.ts +160 -0
  31. package/config/pi/extensions/custom-footer/package.json +16 -0
  32. package/config/pi/extensions/custom-provider-qwen-cli/index.ts +363 -0
  33. package/config/pi/extensions/custom-provider-qwen-cli/package.json +1 -0
  34. package/config/pi/extensions/git-checkpoint/index.ts +53 -0
  35. package/config/pi/extensions/git-checkpoint/package.json +16 -0
  36. package/config/pi/extensions/minimal-mode/index.ts +201 -0
  37. package/config/pi/extensions/minimal-mode/package.json +16 -0
  38. package/config/pi/extensions/plan-mode/README.md +65 -0
  39. package/config/pi/extensions/plan-mode/index.ts +417 -0
  40. package/config/pi/extensions/plan-mode/package.json +12 -0
  41. package/config/pi/extensions/plan-mode/utils.ts +324 -0
  42. package/config/pi/extensions/quality-gates/index.ts +67 -0
  43. package/config/pi/extensions/quality-gates/package.json +16 -0
  44. package/config/pi/extensions/service-skills/index.ts +108 -0
  45. package/config/pi/extensions/service-skills/package.json +16 -0
  46. package/config/pi/extensions/session-flow/index.ts +131 -0
  47. package/config/pi/extensions/session-flow/package.json +16 -0
  48. package/config/pi/extensions/todo/index.ts +299 -0
  49. package/config/pi/extensions/todo/package.json +16 -0
  50. package/config/pi/extensions/xtrm-loader/index.ts +89 -0
  51. package/config/pi/extensions/xtrm-loader/package.json +16 -0
  52. package/config/pi/install-schema.json +44 -0
  53. package/config/pi/models.json.template +76 -0
  54. package/config/pi/pi-worktrees-settings.json +6 -0
  55. package/config/pi/settings.json.template +16 -0
  56. package/config/settings.json +70 -0
  57. package/hooks/README.md +75 -0
  58. package/hooks/agent_context.py +105 -0
  59. package/hooks/beads-claim-sync.mjs +166 -0
  60. package/hooks/beads-commit-gate.mjs +55 -0
  61. package/hooks/beads-compact-restore.mjs +69 -0
  62. package/hooks/beads-compact-save.mjs +51 -0
  63. package/hooks/beads-edit-gate.mjs +45 -0
  64. package/hooks/beads-gate-core.mjs +215 -0
  65. package/hooks/beads-gate-messages.mjs +87 -0
  66. package/hooks/beads-gate-utils.mjs +185 -0
  67. package/hooks/beads-memory-gate.mjs +61 -0
  68. package/hooks/beads-stop-gate.mjs +32 -0
  69. package/hooks/branch-state.mjs +39 -0
  70. package/hooks/gitnexus/gitnexus-hook.cjs +222 -0
  71. package/hooks/guard-rules.mjs +118 -0
  72. package/hooks/hooks.json +116 -0
  73. package/hooks/main-guard-post-push.mjs +71 -0
  74. package/hooks/main-guard.mjs +119 -0
  75. package/hooks/quality-check.cjs +1286 -0
  76. package/hooks/quality-check.py +345 -0
  77. package/hooks/serena-workflow-reminder.py +74 -0
  78. package/package.json +77 -0
  79. package/project-skills/quality-gates/.claude/hooks/hook-config.json +66 -0
  80. package/project-skills/quality-gates/.claude/hooks/quality-check.cjs +1286 -0
  81. package/project-skills/quality-gates/.claude/hooks/quality-check.py +334 -0
  82. package/project-skills/quality-gates/.claude/settings.json +3 -0
  83. package/project-skills/quality-gates/.claude/skills/using-quality-gates/SKILL.md +254 -0
  84. package/project-skills/quality-gates/README.md +109 -0
  85. package/project-skills/quality-gates/evals/evals.json +181 -0
  86. package/project-skills/quality-gates/workspace/iteration-1/FINAL-EVAL-SUMMARY.md +75 -0
  87. package/project-skills/quality-gates/workspace/iteration-1/edge-case-auto-fix-verification/with_skill/outputs/response.md +59 -0
  88. package/project-skills/quality-gates/workspace/iteration-1/edge-case-mixed-language-project/with_skill/outputs/response.md +60 -0
  89. package/project-skills/quality-gates/workspace/iteration-1/eval-summary.md +105 -0
  90. package/project-skills/quality-gates/workspace/iteration-1/partial-install-python-only/with_skill/outputs/response.md +93 -0
  91. package/project-skills/quality-gates/workspace/iteration-1/python-refactor-request/with_skill/outputs/response.md +104 -0
  92. package/project-skills/quality-gates/workspace/iteration-1/quality-gate-error-fix/with_skill/outputs/response.md +74 -0
  93. package/project-skills/quality-gates/workspace/iteration-1/should-not-trigger-general-chat/with_skill/outputs/response.md +18 -0
  94. package/project-skills/quality-gates/workspace/iteration-1/should-not-trigger-math-question/with_skill/outputs/response.md +18 -0
  95. package/project-skills/quality-gates/workspace/iteration-1/should-not-trigger-unrelated-coding/with_skill/outputs/response.md +56 -0
  96. package/project-skills/quality-gates/workspace/iteration-1/tdd-guard-blocking-confusion/with_skill/outputs/response.md +67 -0
  97. package/project-skills/quality-gates/workspace/iteration-1/typescript-feature-with-tests/with_skill/outputs/response.md +97 -0
  98. package/project-skills/service-skills-set/.claude/git-hooks/doc_reminder.py +67 -0
  99. package/project-skills/service-skills-set/.claude/git-hooks/skill_staleness.py +194 -0
  100. package/project-skills/service-skills-set/.claude/service-registry.json +4 -0
  101. package/project-skills/service-skills-set/.claude/settings.json +37 -0
  102. package/project-skills/service-skills-set/.claude/skills/creating-service-skills/SKILL.md +433 -0
  103. package/project-skills/service-skills-set/.claude/skills/creating-service-skills/references/script_quality_standards.md +425 -0
  104. package/project-skills/service-skills-set/.claude/skills/creating-service-skills/references/service_skill_system_guide.md +278 -0
  105. package/project-skills/service-skills-set/.claude/skills/creating-service-skills/scripts/bootstrap.py +308 -0
  106. package/project-skills/service-skills-set/.claude/skills/creating-service-skills/scripts/deep_dive.py +304 -0
  107. package/project-skills/service-skills-set/.claude/skills/creating-service-skills/scripts/scaffolder.py +482 -0
  108. package/project-skills/service-skills-set/.claude/skills/scoping-service-skills/SKILL.md +231 -0
  109. package/project-skills/service-skills-set/.claude/skills/scoping-service-skills/scripts/scope.py +74 -0
  110. package/project-skills/service-skills-set/.claude/skills/updating-service-skills/SKILL.md +136 -0
  111. package/project-skills/service-skills-set/.claude/skills/updating-service-skills/scripts/drift_detector.py +222 -0
  112. package/project-skills/service-skills-set/.claude/skills/using-service-skills/SKILL.md +108 -0
  113. package/project-skills/service-skills-set/.claude/skills/using-service-skills/scripts/cataloger.py +74 -0
  114. package/project-skills/service-skills-set/.claude/skills/using-service-skills/scripts/skill_activator.py +152 -0
  115. package/project-skills/service-skills-set/README.md +93 -0
  116. package/project-skills/service-skills-set/install-service-skills.py +193 -0
  117. package/project-skills/service-skills-set/service-skills-readme.md +236 -0
  118. package/skills/README.txt +31 -0
  119. package/skills/clean-code/SKILL.md +201 -0
  120. package/skills/creating-service-skills/SKILL.md +433 -0
  121. package/skills/creating-service-skills/references/script_quality_standards.md +425 -0
  122. package/skills/creating-service-skills/references/service_skill_system_guide.md +278 -0
  123. package/skills/creating-service-skills/scripts/bootstrap.py +326 -0
  124. package/skills/creating-service-skills/scripts/deep_dive.py +304 -0
  125. package/skills/creating-service-skills/scripts/scaffolder.py +482 -0
  126. package/skills/delegating/SKILL.md +196 -0
  127. package/skills/delegating/config.yaml +210 -0
  128. package/skills/delegating/references/orchestration-protocols.md +41 -0
  129. package/skills/docker-expert/SKILL.md +409 -0
  130. package/skills/documenting/CHANGELOG.md +23 -0
  131. package/skills/documenting/README.md +148 -0
  132. package/skills/documenting/SKILL.md +113 -0
  133. package/skills/documenting/examples/example_pattern.md +70 -0
  134. package/skills/documenting/examples/example_reference.md +70 -0
  135. package/skills/documenting/examples/example_ssot_analytics.md +64 -0
  136. package/skills/documenting/examples/example_workflow.md +141 -0
  137. package/skills/documenting/references/changelog-format.md +97 -0
  138. package/skills/documenting/references/metadata-schema.md +136 -0
  139. package/skills/documenting/references/taxonomy.md +81 -0
  140. package/skills/documenting/references/versioning-rules.md +78 -0
  141. package/skills/documenting/scripts/bump_version.sh +60 -0
  142. package/skills/documenting/scripts/changelog/__init__.py +0 -0
  143. package/skills/documenting/scripts/changelog/add_entry.py +216 -0
  144. package/skills/documenting/scripts/changelog/bump_release.py +117 -0
  145. package/skills/documenting/scripts/changelog/init_changelog.py +54 -0
  146. package/skills/documenting/scripts/changelog/validate_changelog.py +128 -0
  147. package/skills/documenting/scripts/drift_detector.py +266 -0
  148. package/skills/documenting/scripts/generate_template.py +311 -0
  149. package/skills/documenting/scripts/list_by_category.sh +84 -0
  150. package/skills/documenting/scripts/orchestrator.py +255 -0
  151. package/skills/documenting/scripts/validate_metadata.py +242 -0
  152. package/skills/documenting/templates/CHANGELOG.md.template +13 -0
  153. package/skills/find-skills/SKILL.md +133 -0
  154. package/skills/gitnexus-debugging/SKILL.md +85 -0
  155. package/skills/gitnexus-exploring/SKILL.md +75 -0
  156. package/skills/gitnexus-impact-analysis/SKILL.md +94 -0
  157. package/skills/gitnexus-refactoring/SKILL.md +113 -0
  158. package/skills/hook-development/SKILL.md +797 -0
  159. package/skills/hook-development/examples/load-context.sh +55 -0
  160. package/skills/hook-development/examples/quality-check.js +1168 -0
  161. package/skills/hook-development/examples/validate-bash.sh +43 -0
  162. package/skills/hook-development/examples/validate-write.sh +38 -0
  163. package/skills/hook-development/references/advanced.md +527 -0
  164. package/skills/hook-development/references/migration.md +369 -0
  165. package/skills/hook-development/references/patterns.md +412 -0
  166. package/skills/hook-development/scripts/README.md +164 -0
  167. package/skills/hook-development/scripts/hook-linter.sh +153 -0
  168. package/skills/hook-development/scripts/test-hook.sh +252 -0
  169. package/skills/hook-development/scripts/validate-hook-schema.sh +159 -0
  170. package/skills/obsidian-cli/SKILL.md +106 -0
  171. package/skills/orchestrating-agents/SKILL.md +135 -0
  172. package/skills/orchestrating-agents/config.yaml +45 -0
  173. package/skills/orchestrating-agents/references/agent-context-integration.md +37 -0
  174. package/skills/orchestrating-agents/references/examples.md +45 -0
  175. package/skills/orchestrating-agents/references/handover-protocol.md +31 -0
  176. package/skills/orchestrating-agents/references/workflows.md +42 -0
  177. package/skills/orchestrating-agents/scripts/detect_neighbors.py +23 -0
  178. package/skills/prompt-improving/README.md +162 -0
  179. package/skills/prompt-improving/SKILL.md +74 -0
  180. package/skills/prompt-improving/references/analysis_commands.md +24 -0
  181. package/skills/prompt-improving/references/chain_of_thought.md +24 -0
  182. package/skills/prompt-improving/references/mcp_definitions.md +20 -0
  183. package/skills/prompt-improving/references/multishot.md +23 -0
  184. package/skills/prompt-improving/references/xml_core.md +60 -0
  185. package/skills/python-testing/SKILL.md +815 -0
  186. package/skills/scoping-service-skills/SKILL.md +231 -0
  187. package/skills/scoping-service-skills/scripts/scope.py +74 -0
  188. package/skills/senior-backend/SKILL.md +209 -0
  189. package/skills/senior-backend/references/api_design_patterns.md +103 -0
  190. package/skills/senior-backend/references/backend_security_practices.md +103 -0
  191. package/skills/senior-backend/references/database_optimization_guide.md +103 -0
  192. package/skills/senior-backend/scripts/api_load_tester.py +114 -0
  193. package/skills/senior-backend/scripts/api_scaffolder.py +114 -0
  194. package/skills/senior-backend/scripts/database_migration_tool.py +114 -0
  195. package/skills/senior-data-scientist/SKILL.md +226 -0
  196. package/skills/senior-data-scientist/references/experiment_design_frameworks.md +80 -0
  197. package/skills/senior-data-scientist/references/feature_engineering_patterns.md +80 -0
  198. package/skills/senior-data-scientist/references/statistical_methods_advanced.md +80 -0
  199. package/skills/senior-data-scientist/scripts/experiment_designer.py +100 -0
  200. package/skills/senior-data-scientist/scripts/feature_engineering_pipeline.py +100 -0
  201. package/skills/senior-data-scientist/scripts/model_evaluation_suite.py +100 -0
  202. package/skills/senior-devops/SKILL.md +209 -0
  203. package/skills/senior-devops/references/cicd_pipeline_guide.md +103 -0
  204. package/skills/senior-devops/references/deployment_strategies.md +103 -0
  205. package/skills/senior-devops/references/infrastructure_as_code.md +103 -0
  206. package/skills/senior-devops/scripts/deployment_manager.py +114 -0
  207. package/skills/senior-devops/scripts/pipeline_generator.py +114 -0
  208. package/skills/senior-devops/scripts/terraform_scaffolder.py +114 -0
  209. package/skills/senior-security/SKILL.md +209 -0
  210. package/skills/senior-security/references/cryptography_implementation.md +103 -0
  211. package/skills/senior-security/references/penetration_testing_guide.md +103 -0
  212. package/skills/senior-security/references/security_architecture_patterns.md +103 -0
  213. package/skills/senior-security/scripts/pentest_automator.py +114 -0
  214. package/skills/senior-security/scripts/security_auditor.py +114 -0
  215. package/skills/senior-security/scripts/threat_modeler.py +114 -0
  216. package/skills/skill-creator/LICENSE.txt +202 -0
  217. package/skills/skill-creator/SKILL.md +479 -0
  218. package/skills/skill-creator/agents/analyzer.md +274 -0
  219. package/skills/skill-creator/agents/comparator.md +202 -0
  220. package/skills/skill-creator/agents/grader.md +223 -0
  221. package/skills/skill-creator/assets/eval_review.html +146 -0
  222. package/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  223. package/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  224. package/skills/skill-creator/references/schemas.md +430 -0
  225. package/skills/skill-creator/scripts/__init__.py +0 -0
  226. package/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  227. package/skills/skill-creator/scripts/generate_report.py +326 -0
  228. package/skills/skill-creator/scripts/improve_description.py +248 -0
  229. package/skills/skill-creator/scripts/package_skill.py +136 -0
  230. package/skills/skill-creator/scripts/quick_validate.py +103 -0
  231. package/skills/skill-creator/scripts/run_eval.py +310 -0
  232. package/skills/skill-creator/scripts/run_loop.py +332 -0
  233. package/skills/skill-creator/scripts/utils.py +47 -0
  234. package/skills/sync-docs/SKILL.md +132 -0
  235. package/skills/sync-docs/evals/evals.json +89 -0
  236. package/skills/sync-docs/references/doc-structure.md +99 -0
  237. package/skills/sync-docs/references/schema.md +103 -0
  238. package/skills/sync-docs/scripts/changelog/add_entry.py +216 -0
  239. package/skills/sync-docs/scripts/context_gatherer.py +240 -0
  240. package/skills/sync-docs/scripts/doc_structure_analyzer.py +495 -0
  241. package/skills/sync-docs/scripts/drift_detector.py +327 -0
  242. package/skills/sync-docs/scripts/validate_doc.py +365 -0
  243. package/skills/sync-docs/scripts/validate_metadata.py +185 -0
  244. package/skills/sync-docs-workspace/iteration-1/benchmark.json +293 -0
  245. package/skills/sync-docs-workspace/iteration-1/benchmark.md +13 -0
  246. package/skills/sync-docs-workspace/iteration-1/eval-doc-audit/eval_metadata.json +27 -0
  247. package/skills/sync-docs-workspace/iteration-1/eval-doc-audit/with_skill/outputs/result.md +210 -0
  248. package/skills/sync-docs-workspace/iteration-1/eval-doc-audit/with_skill/run-1/grading.json +28 -0
  249. package/skills/sync-docs-workspace/iteration-1/eval-doc-audit/with_skill/run-1/timing.json +1 -0
  250. package/skills/sync-docs-workspace/iteration-1/eval-doc-audit/without_skill/outputs/result.md +101 -0
  251. package/skills/sync-docs-workspace/iteration-1/eval-doc-audit/without_skill/run-1/grading.json +28 -0
  252. package/skills/sync-docs-workspace/iteration-1/eval-doc-audit/without_skill/run-1/timing.json +5 -0
  253. package/skills/sync-docs-workspace/iteration-1/eval-doc-audit/without_skill/timing.json +5 -0
  254. package/skills/sync-docs-workspace/iteration-1/eval-fix-mode/eval_metadata.json +27 -0
  255. package/skills/sync-docs-workspace/iteration-1/eval-fix-mode/with_skill/outputs/result.md +198 -0
  256. package/skills/sync-docs-workspace/iteration-1/eval-fix-mode/with_skill/run-1/grading.json +28 -0
  257. package/skills/sync-docs-workspace/iteration-1/eval-fix-mode/with_skill/run-1/timing.json +1 -0
  258. package/skills/sync-docs-workspace/iteration-1/eval-fix-mode/without_skill/outputs/result.md +94 -0
  259. package/skills/sync-docs-workspace/iteration-1/eval-fix-mode/without_skill/run-1/grading.json +28 -0
  260. package/skills/sync-docs-workspace/iteration-1/eval-fix-mode/without_skill/run-1/timing.json +1 -0
  261. package/skills/sync-docs-workspace/iteration-1/eval-sprint-closeout/eval_metadata.json +27 -0
  262. package/skills/sync-docs-workspace/iteration-1/eval-sprint-closeout/with_skill/outputs/result.md +237 -0
  263. package/skills/sync-docs-workspace/iteration-1/eval-sprint-closeout/with_skill/run-1/grading.json +28 -0
  264. package/skills/sync-docs-workspace/iteration-1/eval-sprint-closeout/with_skill/run-1/timing.json +1 -0
  265. package/skills/sync-docs-workspace/iteration-1/eval-sprint-closeout/without_skill/outputs/result.md +134 -0
  266. package/skills/sync-docs-workspace/iteration-1/eval-sprint-closeout/without_skill/run-1/grading.json +28 -0
  267. package/skills/sync-docs-workspace/iteration-1/eval-sprint-closeout/without_skill/run-1/timing.json +1 -0
  268. package/skills/sync-docs-workspace/iteration-2/benchmark.json +297 -0
  269. package/skills/sync-docs-workspace/iteration-2/benchmark.md +13 -0
  270. package/skills/sync-docs-workspace/iteration-2/eval-doc-audit/eval_metadata.json +27 -0
  271. package/skills/sync-docs-workspace/iteration-2/eval-doc-audit/with_skill/outputs/result.md +137 -0
  272. package/skills/sync-docs-workspace/iteration-2/eval-doc-audit/with_skill/run-1/grading.json +92 -0
  273. package/skills/sync-docs-workspace/iteration-2/eval-doc-audit/with_skill/run-1/timing.json +1 -0
  274. package/skills/sync-docs-workspace/iteration-2/eval-doc-audit/without_skill/outputs/result.md +134 -0
  275. package/skills/sync-docs-workspace/iteration-2/eval-doc-audit/without_skill/run-1/grading.json +86 -0
  276. package/skills/sync-docs-workspace/iteration-2/eval-doc-audit/without_skill/run-1/timing.json +1 -0
  277. package/skills/sync-docs-workspace/iteration-2/eval-fix-mode/eval_metadata.json +27 -0
  278. package/skills/sync-docs-workspace/iteration-2/eval-fix-mode/with_skill/outputs/result.md +193 -0
  279. package/skills/sync-docs-workspace/iteration-2/eval-fix-mode/with_skill/run-1/grading.json +72 -0
  280. package/skills/sync-docs-workspace/iteration-2/eval-fix-mode/with_skill/run-1/timing.json +1 -0
  281. package/skills/sync-docs-workspace/iteration-2/eval-fix-mode/without_skill/outputs/result.md +211 -0
  282. package/skills/sync-docs-workspace/iteration-2/eval-fix-mode/without_skill/run-1/grading.json +91 -0
  283. package/skills/sync-docs-workspace/iteration-2/eval-fix-mode/without_skill/run-1/timing.json +5 -0
  284. package/skills/sync-docs-workspace/iteration-2/eval-sprint-closeout/eval_metadata.json +27 -0
  285. package/skills/sync-docs-workspace/iteration-2/eval-sprint-closeout/with_skill/outputs/result.md +182 -0
  286. package/skills/sync-docs-workspace/iteration-2/eval-sprint-closeout/with_skill/run-1/grading.json +95 -0
  287. package/skills/sync-docs-workspace/iteration-2/eval-sprint-closeout/with_skill/run-1/timing.json +1 -0
  288. package/skills/sync-docs-workspace/iteration-2/eval-sprint-closeout/without_skill/outputs/result.md +222 -0
  289. package/skills/sync-docs-workspace/iteration-2/eval-sprint-closeout/without_skill/run-1/grading.json +88 -0
  290. package/skills/sync-docs-workspace/iteration-2/eval-sprint-closeout/without_skill/run-1/timing.json +5 -0
  291. package/skills/sync-docs-workspace/iteration-3/benchmark.json +298 -0
  292. package/skills/sync-docs-workspace/iteration-3/benchmark.md +13 -0
  293. package/skills/sync-docs-workspace/iteration-3/eval-doc-audit/eval_metadata.json +27 -0
  294. package/skills/sync-docs-workspace/iteration-3/eval-doc-audit/with_skill/outputs/result.md +125 -0
  295. package/skills/sync-docs-workspace/iteration-3/eval-doc-audit/with_skill/run-1/grading.json +97 -0
  296. package/skills/sync-docs-workspace/iteration-3/eval-doc-audit/with_skill/run-1/timing.json +5 -0
  297. package/skills/sync-docs-workspace/iteration-3/eval-doc-audit/without_skill/outputs/result.md +144 -0
  298. package/skills/sync-docs-workspace/iteration-3/eval-doc-audit/without_skill/run-1/grading.json +78 -0
  299. package/skills/sync-docs-workspace/iteration-3/eval-doc-audit/without_skill/run-1/timing.json +5 -0
  300. package/skills/sync-docs-workspace/iteration-3/eval-fix-mode/eval_metadata.json +27 -0
  301. package/skills/sync-docs-workspace/iteration-3/eval-fix-mode/with_skill/outputs/result.md +104 -0
  302. package/skills/sync-docs-workspace/iteration-3/eval-fix-mode/with_skill/run-1/grading.json +91 -0
  303. package/skills/sync-docs-workspace/iteration-3/eval-fix-mode/with_skill/run-1/timing.json +5 -0
  304. package/skills/sync-docs-workspace/iteration-3/eval-fix-mode/without_skill/outputs/result.md +79 -0
  305. package/skills/sync-docs-workspace/iteration-3/eval-fix-mode/without_skill/run-1/grading.json +82 -0
  306. package/skills/sync-docs-workspace/iteration-3/eval-fix-mode/without_skill/run-1/timing.json +5 -0
  307. package/skills/sync-docs-workspace/iteration-3/eval-sprint-closeout/eval_metadata.json +27 -0
  308. package/skills/sync-docs-workspace/iteration-3/eval-sprint-closeout/with_skill/outputs/phase1_context.json +302 -0
  309. package/skills/sync-docs-workspace/iteration-3/eval-sprint-closeout/with_skill/outputs/phase2_drift.txt +33 -0
  310. package/skills/sync-docs-workspace/iteration-3/eval-sprint-closeout/with_skill/outputs/phase3_analysis.json +114 -0
  311. package/skills/sync-docs-workspace/iteration-3/eval-sprint-closeout/with_skill/outputs/phase4_fix.txt +118 -0
  312. package/skills/sync-docs-workspace/iteration-3/eval-sprint-closeout/with_skill/outputs/phase5_validate.txt +38 -0
  313. package/skills/sync-docs-workspace/iteration-3/eval-sprint-closeout/with_skill/outputs/result.md +158 -0
  314. package/skills/sync-docs-workspace/iteration-3/eval-sprint-closeout/with_skill/run-1/grading.json +95 -0
  315. package/skills/sync-docs-workspace/iteration-3/eval-sprint-closeout/with_skill/run-1/timing.json +5 -0
  316. package/skills/sync-docs-workspace/iteration-3/eval-sprint-closeout/without_skill/outputs/result.md +71 -0
  317. package/skills/sync-docs-workspace/iteration-3/eval-sprint-closeout/without_skill/run-1/grading.json +90 -0
  318. package/skills/sync-docs-workspace/iteration-3/eval-sprint-closeout/without_skill/run-1/timing.json +5 -0
  319. package/skills/test-planning/SKILL.md +208 -0
  320. package/skills/test-planning/evals/evals.json +23 -0
  321. package/skills/updating-service-skills/SKILL.md +136 -0
  322. package/skills/updating-service-skills/scripts/drift_detector.py +222 -0
  323. package/skills/using-TDD/SKILL.md +410 -0
  324. package/skills/using-quality-gates/SKILL.md +254 -0
  325. package/skills/using-serena-lsp/README.md +8 -0
  326. package/skills/using-serena-lsp/REFERENCE.md +194 -0
  327. package/skills/using-serena-lsp/SKILL.md +82 -0
  328. package/skills/using-service-skills/SKILL.md +108 -0
  329. package/skills/using-service-skills/scripts/cataloger.py +74 -0
  330. package/skills/using-service-skills/scripts/skill_activator.py +152 -0
  331. package/skills/using-service-skills/scripts/test_skill_activator.py +58 -0
  332. package/skills/using-xtrm/SKILL.md +245 -0
  333. package/skills/xt-end/SKILL.md +128 -0
@@ -0,0 +1,99 @@
1
+ # docs/ Structure Guide
2
+
3
+ This reference defines what belongs in each focused docs/ file vs README.md.
4
+
5
+ ## The Rule
6
+
7
+ **README.md** is an entry point — a quick-start and orientation map. It should be < 200 lines. Anything that requires more than a few bullet points belongs in a focused docs/ file.
8
+
9
+ **docs/** holds focused, public-facing SSOT files for each subsystem. Each file:
10
+ - Has YAML frontmatter (see `schema.md`)
11
+ - Covers exactly one subsystem or concern
12
+ - Is linked from README.md with a single line
13
+
14
+ ---
15
+
16
+ ## What Goes Where
17
+
18
+ | Content Type | Location | When to Create |
19
+ |---|---|---|
20
+ | Quick start, one-liner install | `README.md` | Always |
21
+ | Feature overview table | `README.md` | Always, < 20 lines |
22
+ | Hook events, scripts, behavior | `docs/hooks.md` | When hooks/ dir exists |
23
+ | Pi/Copilot extension catalog | `docs/pi-extensions.md` | When config/pi/extensions/ exists |
24
+ | System architecture, components | `docs/architecture.md` | When > 2 major subsystems |
25
+ | Policy rules and enforcement | `docs/policies.md` | When policies/ dir exists |
26
+ | MCP server config and usage | `docs/mcp-servers.md` | When .mcp.json or mcp servers exist |
27
+ | Skills catalog | `docs/skills.md` | When skills/ dir has > 5 skills |
28
+ | CLI commands reference | `docs/cli-reference.md` | When CLI has > 10 commands |
29
+ | Troubleshooting and FAQs | `docs/troubleshooting.md` | When recurring issues exist |
30
+ | In-progress work plans | `docs/plans/` | Always for planning docs |
31
+
32
+ ---
33
+
34
+ ## Standard File Shapes
35
+
36
+ ### docs/hooks.md
37
+ Covers: hook events, what triggers each hook, which scripts run, output format.
38
+ Section outline:
39
+ ```
40
+ ## Overview
41
+ ## Hook Events
42
+ ## Scripts Reference
43
+ ## Adding a New Hook
44
+ ```
45
+
46
+ ### docs/pi-extensions.md
47
+ Covers: what Pi extensions are installed, their events, behavior, and configuration.
48
+ Section outline:
49
+ ```
50
+ ## Overview
51
+ ## Installed Extensions
52
+ ## Extension Events
53
+ ## Configuration
54
+ ```
55
+
56
+ ### docs/architecture.md
57
+ Covers: system overview, key components, data flow, dependency diagram.
58
+ Section outline:
59
+ ```
60
+ ## System Overview
61
+ ## Key Components
62
+ ## Data Flow
63
+ ## Directory Structure
64
+ ```
65
+
66
+ ### docs/policies.md
67
+ Covers: what policies exist, what each enforces, runtime targets.
68
+ Section outline:
69
+ ```
70
+ ## Overview
71
+ ## Policy Files
72
+ ## Policy Compiler
73
+ ## Adding a Policy
74
+ ```
75
+
76
+ ---
77
+
78
+ ## Detecting When README is Too Big
79
+
80
+ The `doc_structure_analyzer.py` script flags README as `BLOATED` when:
81
+ 1. Line count > 200, AND
82
+ 2. At least one section has a matching `docs/` file that doesn't exist yet
83
+
84
+ The `EXTRACTABLE` status means sections are present but README isn't over threshold yet — worth noting but not urgent.
85
+
86
+ ---
87
+
88
+ ## Extraction Process
89
+
90
+ When extracting a section from README to docs/:
91
+
92
+ 1. Identify the target section heading and its body in `README.md`
93
+ 2. Create `docs/X.md` with `validate_doc.py --generate`
94
+ 3. Move the section content into the new file
95
+ 4. Replace the original README section with:
96
+ ```markdown
97
+ See [docs/X.md](docs/X.md) for the full reference.
98
+ ```
99
+ 5. Run `validate_doc.py docs/X.md` to confirm schema
@@ -0,0 +1,103 @@
1
+ # docs/ File Schema
2
+
3
+ Every file in `docs/` must have YAML frontmatter. This is enforced by `validate_doc.py`.
4
+
5
+ ## Frontmatter Fields
6
+
7
+ ```yaml
8
+ ---
9
+ title: Human Readable Title # required — clear, descriptive
10
+ scope: kebab-case-identifier # required — unique slug for this doc
11
+ category: reference # required — see valid values below
12
+ version: 1.0.0 # required — semver (x.y.z)
13
+ updated: 2026-03-18 # required — ISO date of last update
14
+ description: "One-line summary" # optional
15
+ source_of_truth_for: # optional — glob patterns this doc describes
16
+ - "hooks/**/*.mjs"
17
+ - "policies/*.json"
18
+ domain: [hooks, claude, policy] # optional — cross-cutting tags
19
+ ---
20
+ ```
21
+
22
+ ## Required Fields
23
+
24
+ | Field | Type | Description | Example |
25
+ |-------|------|-------------|---------|
26
+ | `title` | string | Clear, descriptive title | "Hooks Reference" |
27
+ | `scope` | string | Unique kebab-case slug | "hooks" |
28
+ | `category` | enum | Document type | "reference" |
29
+ | `version` | semver | x.y.z — bump on every update | "1.2.0" |
30
+ | `updated` | date | YYYY-MM-DD of last change | "2026-03-18" |
31
+
32
+ ## Category Values
33
+
34
+ | Value | Purpose |
35
+ |---|---|
36
+ | `reference` | Look-up table, cheat sheet, technical spec |
37
+ | `guide` | Step-by-step how-to documentation |
38
+ | `architecture` | System design and component overview |
39
+ | `api` | API contracts, interfaces, schemas |
40
+ | `plan` | Implementation plan or roadmap |
41
+ | `overview` | Summary introduction to a subsystem |
42
+
43
+ ## Optional Fields
44
+
45
+ ### source_of_truth_for
46
+ A list of glob patterns (fnmatch syntax) pointing to the source files this doc describes. Used by `drift_detector.py` and `doc_structure_analyzer.py` to detect when the source changed but the doc wasn't updated.
47
+
48
+ ```yaml
49
+ source_of_truth_for:
50
+ - "hooks/**/*.mjs"
51
+ - "policies/*.json"
52
+ - "config/pi/extensions/**/*.ts"
53
+ ```
54
+
55
+ ### domain
56
+ Tags for cross-cutting concerns. Useful for searching related docs.
57
+
58
+ ```yaml
59
+ domain: [hooks, claude, pi, enforcement]
60
+ ```
61
+
62
+ ## Version Bumping Rules
63
+
64
+ | Change | Bump |
65
+ |---|---|
66
+ | Fix a typo or outdated detail | `patch` (1.0.0 → 1.0.1) |
67
+ | Add a new section | `minor` (1.0.0 → 1.1.0) |
68
+ | Full rewrite or restructure | `major` (1.0.0 → 2.0.0) |
69
+
70
+ Always update `updated:` to today's date alongside the version bump.
71
+
72
+ ## INDEX Block
73
+
74
+ `validate_doc.py` auto-generates a navigation table from `##` headings:
75
+
76
+ ```markdown
77
+ <!-- INDEX: auto-generated by validate_doc.py — do not edit manually -->
78
+ | Section | Summary |
79
+ |---|---|
80
+ | [Overview](#overview) | ... |
81
+ | [Hook Events](#hook-events) | ... |
82
+ <!-- END INDEX -->
83
+ ```
84
+
85
+ This block is regenerated every time `validate_doc.py` runs on the file. Do not edit it manually.
86
+
87
+ ## Complete Example
88
+
89
+ ```yaml
90
+ ---
91
+ title: Hooks Reference
92
+ scope: hooks
93
+ category: reference
94
+ version: 1.1.0
95
+ updated: 2026-03-18
96
+ description: "All hook events, scripts, and behavior for the xtrm plugin"
97
+ source_of_truth_for:
98
+ - "hooks/**/*.mjs"
99
+ - "policies/beads.json"
100
+ - "policies/main-guard.json"
101
+ domain: [hooks, claude, enforcement]
102
+ ---
103
+ ```
@@ -0,0 +1,216 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Add entry to CHANGELOG.md [Unreleased] section.
4
+
5
+ Automatically:
6
+ - Places entry in correct category
7
+ - Creates category if missing
8
+ - Maintains category ordering per Keep a Changelog
9
+ - Preserves existing entries
10
+ """
11
+
12
+ import re
13
+ import sys
14
+ from enum import Enum
15
+ from pathlib import Path
16
+ from typing import Optional
17
+
18
+
19
+ class ChangeCategory(Enum):
20
+ """Keep a Changelog categories in proper order."""
21
+ ADDED = "Added"
22
+ CHANGED = "Changed"
23
+ DEPRECATED = "Deprecated"
24
+ REMOVED = "Removed"
25
+ FIXED = "Fixed"
26
+ SECURITY = "Security"
27
+
28
+
29
+ CATEGORY_ORDER = [cat.value for cat in ChangeCategory]
30
+
31
+
32
+ def add_entry(changelog_content: str, category: ChangeCategory, description: str) -> str:
33
+ """
34
+ Add entry to [Unreleased] section under specified category.
35
+
36
+ Args:
37
+ changelog_content: Full CHANGELOG.md content
38
+ category: ChangeCategory enum value
39
+ description: Entry text (without leading "- ")
40
+
41
+ Returns:
42
+ Updated changelog content
43
+ """
44
+ lines = changelog_content.splitlines()
45
+
46
+ # Find [Unreleased] section
47
+ unreleased_idx = None
48
+ for i, line in enumerate(lines):
49
+ if re.match(r"^## \[Unreleased\]", line):
50
+ unreleased_idx = i
51
+ break
52
+
53
+ if unreleased_idx is None:
54
+ raise ValueError("CHANGELOG missing [Unreleased] section")
55
+
56
+ # Find next version section (end of Unreleased)
57
+ next_version_idx = len(lines)
58
+ for i in range(unreleased_idx + 1, len(lines)):
59
+ if re.match(r"^## \[.+\]", lines[i]):
60
+ next_version_idx = i
61
+ break
62
+
63
+ # Find or create category section
64
+ category_name = category.value
65
+ category_idx = None
66
+
67
+ for i in range(unreleased_idx + 1, next_version_idx):
68
+ if lines[i].strip() == f"### {category_name}":
69
+ category_idx = i
70
+ break
71
+
72
+ if category_idx is None:
73
+ # Category doesn't exist, create it in proper order
74
+ category_idx = _insert_category_in_order(
75
+ lines,
76
+ unreleased_idx,
77
+ next_version_idx,
78
+ category_name
79
+ )
80
+ # Update next_version_idx as lines shifted
81
+ # Wait, if I insert, I need to know how many lines.
82
+ # _insert_category_in_order returns the index of the new category header.
83
+ # But if it inserted blank lines, the indices after it shifted.
84
+ # But next_version_idx is an index, so if I insert BEFORE it, it shifts.
85
+ # Let's check _insert_category_in_order implementation logic.
86
+ # It mutates 'lines' list.
87
+ # If it inserts 2 lines, next_version_idx should increase by 2.
88
+ # But wait, next_version_idx was calculated based on initial list.
89
+ # I should probably re-calculate or just use the return value.
90
+ # But wait, the function mutates the list.
91
+ # Let's look at the implementation below.
92
+
93
+ # Recalculate next_version_idx to be safe?
94
+ # Or rely on finding the category index.
95
+ pass
96
+
97
+ # Re-find next version idx just to be safe if lines shifted
98
+ for i in range(unreleased_idx + 1, len(lines)):
99
+ if re.match(r"^## \[.+\]", lines[i]):
100
+ next_version_idx = i
101
+ break
102
+ else:
103
+ next_version_idx = len(lines)
104
+
105
+ # Re-find category index
106
+ for i in range(unreleased_idx + 1, next_version_idx):
107
+ if lines[i].strip() == f"### {category_name}":
108
+ category_idx = i
109
+ break
110
+
111
+ # Find where to insert the entry (after category header)
112
+ insert_idx = category_idx + 1
113
+
114
+ # Skip existing entries to add at end of category
115
+ while insert_idx < len(lines) and insert_idx < next_version_idx and lines[insert_idx].strip().startswith("- "):
116
+ insert_idx += 1
117
+
118
+ # Insert the new entry
119
+ entry_line = f"- {description}"
120
+ lines.insert(insert_idx, entry_line)
121
+
122
+ return '\n'.join(lines)
123
+
124
+
125
+ def _insert_category_in_order(
126
+ lines: list,
127
+ unreleased_idx: int,
128
+ next_version_idx: int,
129
+ category_name: str
130
+ ) -> int:
131
+ """
132
+ Insert category header in proper Keep a Changelog order.
133
+
134
+ Returns:
135
+ Index where category header was inserted
136
+ """
137
+ category_order_idx = CATEGORY_ORDER.index(category_name)
138
+
139
+ # Find existing categories in Unreleased section
140
+ existing_categories = []
141
+ # Note: next_version_idx is based on current lines state
142
+ for i in range(unreleased_idx + 1, next_version_idx):
143
+ match = re.match(r"^### (.+)$", lines[i])
144
+ if match:
145
+ cat = match.group(1)
146
+ if cat in CATEGORY_ORDER:
147
+ existing_categories.append((i, cat))
148
+
149
+ # Find insertion point
150
+ insert_idx = next_version_idx
151
+ for idx, cat in existing_categories:
152
+ cat_order_idx = CATEGORY_ORDER.index(cat)
153
+ if category_order_idx < cat_order_idx:
154
+ # Insert before this category
155
+ insert_idx = idx
156
+ break
157
+
158
+ # Insert category header with blank line before if not first
159
+ if existing_categories:
160
+ lines.insert(insert_idx, "")
161
+ insert_idx += 1
162
+ else:
163
+ # First category, add blank line after [Unreleased]
164
+ lines.insert(unreleased_idx + 1, "")
165
+ insert_idx = unreleased_idx + 2
166
+
167
+ lines.insert(insert_idx, f"### {category_name}")
168
+
169
+ return insert_idx
170
+
171
+
172
+ def add_entry_to_file(
173
+ filepath: Path,
174
+ category: ChangeCategory,
175
+ description: str
176
+ ) -> None:
177
+ """Add entry to CHANGELOG file."""
178
+ if not filepath.exists():
179
+ raise FileNotFoundError(f"CHANGELOG not found: {filepath}")
180
+
181
+ content = filepath.read_text(encoding='utf-8')
182
+ updated = add_entry(content, category, description)
183
+ filepath.write_text(updated, encoding='utf-8')
184
+
185
+ print(f"✅ Added to {filepath.name}:")
186
+ print(f" [{category.value}] {description}")
187
+
188
+
189
+ def main():
190
+ """CLI entry point."""
191
+ if len(sys.argv) != 4:
192
+ print("Usage: add_entry.py <changelog_file> <category> <description>")
193
+ print("")
194
+ print("Categories: Added, Changed, Deprecated, Removed, Fixed, Security")
195
+ print("")
196
+ print("Example:")
197
+ print(' add_entry.py CHANGELOG.md Added "New semantic search feature"')
198
+ sys.exit(1)
199
+
200
+ filepath = Path(sys.argv[1])
201
+ category_str = sys.argv[2]
202
+ description = sys.argv[3]
203
+
204
+ # Validate category
205
+ try:
206
+ category = ChangeCategory[category_str.upper()]
207
+ except KeyError:
208
+ print(f"ERROR: Invalid category '{category_str}'")
209
+ print(f"Valid: {', '.join(CATEGORY_ORDER)}")
210
+ sys.exit(1)
211
+
212
+ add_entry_to_file(filepath, category, description)
213
+
214
+
215
+ if __name__ == "__main__":
216
+ main()
@@ -0,0 +1,240 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Gather project context for documentation sync.
4
+
5
+ Collects:
6
+ - Recently closed bd issues (if .beads/ exists)
7
+ - Recently merged PRs (via git log)
8
+ - bd memories persisted this cycle (bd kv list)
9
+ - Stale docs/ files (via sync-docs drift_detector.py)
10
+
11
+ Outputs JSON to stdout. Safe to run in any project — degrades gracefully
12
+ when bd or drift detection tools are unavailable.
13
+
14
+ Usage:
15
+ context_gatherer.py [--since=30]
16
+
17
+ --since=N Look back N commits for git context (default: 30)
18
+ """
19
+
20
+ import sys
21
+ import json
22
+ import subprocess
23
+ import time
24
+ from pathlib import Path
25
+ from datetime import datetime, timezone
26
+
27
+
28
+ def run(cmd: list, cwd: str | None = None, timeout: int = 8) -> str | None:
29
+ """Run a command, return stdout or None on failure."""
30
+ try:
31
+ result = subprocess.run(
32
+ cmd, cwd=cwd, capture_output=True, text=True, timeout=timeout
33
+ )
34
+ if result.returncode == 0:
35
+ return result.stdout.strip()
36
+ return None
37
+ except Exception:
38
+ return None
39
+
40
+
41
+ def find_project_root() -> Path:
42
+ """Walk up from cwd looking for .git."""
43
+ p = Path.cwd()
44
+ for parent in [p, *p.parents]:
45
+ if (parent / ".git").exists():
46
+ return parent
47
+ return p
48
+
49
+
50
+ def find_main_repo_root(root: Path) -> Path:
51
+ """For git worktrees, resolve the main repo root from the .git file.
52
+
53
+ In a worktree, .git is a file: "gitdir: /path/to/main/.git/worktrees/<name>"
54
+ Navigate up two levels to reach the main .git, then one more for the repo root.
55
+ """
56
+ git_path = root / ".git"
57
+ if git_path.is_file():
58
+ content = git_path.read_text(encoding="utf-8").strip()
59
+ if content.startswith("gitdir:"):
60
+ worktree_git = Path(content[len("gitdir:"):].strip())
61
+ main_git = worktree_git.parent.parent
62
+ return main_git.parent
63
+ return root
64
+
65
+
66
+ def ensure_dolt_server(cwd: str) -> bool:
67
+ """Ensure the Dolt server is running. Start it if not. Returns True if ready."""
68
+ test = run(["bd", "dolt", "test"], cwd=cwd, timeout=5)
69
+ if test is not None:
70
+ return True
71
+
72
+ try:
73
+ subprocess.run(
74
+ ["bd", "dolt", "start"],
75
+ cwd=cwd,
76
+ capture_output=True,
77
+ text=True,
78
+ timeout=15,
79
+ )
80
+ except Exception:
81
+ return False
82
+
83
+ for _ in range(6):
84
+ time.sleep(1)
85
+ if run(["bd", "dolt", "test"], cwd=cwd, timeout=3) is not None:
86
+ return True
87
+
88
+ return False
89
+
90
+
91
+ def has_beads(root: Path) -> bool:
92
+ return (root / ".beads").exists()
93
+
94
+
95
+ def gather_bd_closed(cwd: str) -> list[dict]:
96
+ """Get recently closed bd issues."""
97
+ out = run(["bd", "list", "--status=closed"], cwd=cwd)
98
+ if not out:
99
+ return []
100
+
101
+ issues = []
102
+ for line in out.splitlines():
103
+ line = line.strip()
104
+ if line.startswith("✓") or "closed" in line.lower():
105
+ parts = line.lstrip("✓ ").split()
106
+ if len(parts) >= 2:
107
+ issue_id = parts[0]
108
+ title_start = 2 if len(parts) > 2 and parts[1].startswith("P") else 1
109
+ title = " ".join(parts[title_start:])
110
+ issues.append({"id": issue_id, "title": title})
111
+
112
+ return issues[:20]
113
+
114
+
115
+ def gather_bd_memories(cwd: str) -> list[dict]:
116
+ """Read bd memories via bd kv list, filtering memory.* keys."""
117
+ out = run(["bd", "kv", "list"], cwd=cwd)
118
+ if not out:
119
+ return []
120
+
121
+ memories = []
122
+ for line in out.splitlines():
123
+ stripped = line.strip()
124
+ if not stripped.startswith("memory."):
125
+ continue
126
+ if " = " in stripped:
127
+ key, _, value = stripped.partition(" = ")
128
+ memories.append({"key": key.strip(), "value": value.strip()})
129
+ else:
130
+ memories.append({"key": stripped, "value": ""})
131
+
132
+ return memories[:20]
133
+
134
+
135
+ def gather_merged_prs(root: Path, since_n: int) -> list[dict]:
136
+ """Get merged PRs from git log."""
137
+ out = run(
138
+ ["git", "log", f"-{since_n}", "--merges", "--oneline", "--format=%H|%s|%ci"],
139
+ cwd=str(root),
140
+ )
141
+ if not out:
142
+ return []
143
+
144
+ prs = []
145
+ for line in out.splitlines():
146
+ parts = line.split("|", 2)
147
+ if len(parts) == 3:
148
+ sha, subject, date = parts
149
+ prs.append({"sha": sha[:8], "subject": subject.strip(), "date": date.strip()})
150
+ return prs[:10]
151
+
152
+
153
+ def gather_recent_commits(root: Path, since_n: int) -> list[dict]:
154
+ """Get recent non-merge commits for context."""
155
+ out = run(
156
+ ["git", "log", f"-{since_n}", "--no-merges", "--oneline", "--format=%H|%s|%ci"],
157
+ cwd=str(root),
158
+ )
159
+ if not out:
160
+ return []
161
+
162
+ commits = []
163
+ for line in out.splitlines():
164
+ parts = line.split("|", 2)
165
+ if len(parts) == 3:
166
+ sha, subject, date = parts
167
+ commits.append({"sha": sha[:8], "subject": subject.strip(), "date": date.strip()})
168
+ return commits[:15]
169
+
170
+
171
+ def gather_docs_drift(root: Path, since_n: int) -> dict:
172
+ """Run sync-docs drift_detector.py and capture stale docs report."""
173
+ candidates = [
174
+ Path.home() / ".claude/skills/sync-docs/scripts/drift_detector.py",
175
+ root / "skills/sync-docs/scripts/drift_detector.py",
176
+ Path(__file__).parent / "drift_detector.py",
177
+ ]
178
+ detector = next((p for p in candidates if p.exists()), None)
179
+ if not detector:
180
+ return {"available": False, "stale": []}
181
+
182
+ out = run([sys.executable, str(detector), "scan", "--since", str(since_n), "--json"], cwd=str(root))
183
+ if out is None:
184
+ return {"available": False, "stale": []}
185
+
186
+ try:
187
+ data = json.loads(out)
188
+ return {
189
+ "available": True,
190
+ "stale": data.get("stale", []),
191
+ "count": data.get("count", 0),
192
+ "raw": data,
193
+ }
194
+ except json.JSONDecodeError:
195
+ return {"available": True, "stale": [], "raw": out}
196
+
197
+
198
+ def main() -> None:
199
+ since_n = 30
200
+ for arg in sys.argv[1:]:
201
+ if arg.startswith("--since="):
202
+ try:
203
+ since_n = int(arg.split("=", 1)[1])
204
+ except ValueError:
205
+ pass
206
+
207
+ root = find_project_root()
208
+ main_root = find_main_repo_root(root)
209
+ bd_cwd = str(main_root)
210
+ bd_available = has_beads(main_root)
211
+
212
+ dolt_ready = False
213
+ dolt_warning: str | None = None
214
+ if bd_available:
215
+ dolt_ready = ensure_dolt_server(bd_cwd)
216
+ if not dolt_ready:
217
+ dolt_warning = (
218
+ "Dolt server could not be started — bd data unavailable. "
219
+ "Run 'bd dolt start' manually from the project root and retry."
220
+ )
221
+
222
+ report = {
223
+ "generated_at": datetime.now(timezone.utc).isoformat(),
224
+ "project_root": str(root),
225
+ "bd_available": bd_available,
226
+ "bd_closed_issues": gather_bd_closed(bd_cwd) if dolt_ready else [],
227
+ "bd_memories": gather_bd_memories(bd_cwd) if dolt_ready else [],
228
+ "merged_prs": gather_merged_prs(root, since_n),
229
+ "recent_commits": gather_recent_commits(root, since_n),
230
+ "docs_drift": gather_docs_drift(root, since_n),
231
+ }
232
+
233
+ if dolt_warning:
234
+ report["warnings"] = [dolt_warning]
235
+
236
+ print(json.dumps(report, indent=2))
237
+
238
+
239
+ if __name__ == "__main__":
240
+ main()