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,12 @@
1
+ {
2
+ "name": "@xtrm/pi-plan-mode",
3
+ "version": "1.0.0",
4
+ "description": "Plan mode extension for Pi - read-only exploration with plan extraction and bd integration",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./index.ts"
8
+ },
9
+ "keywords": ["pi", "extension", "plan-mode", "xtrm"],
10
+ "author": "xtrm",
11
+ "license": "MIT"
12
+ }
@@ -0,0 +1,324 @@
1
+ /**
2
+ * Pure utility functions for plan mode.
3
+ * Extracted for testability.
4
+ */
5
+
6
+ import { existsSync } from "node:fs";
7
+ import { join } from "node:path";
8
+ import { spawnSync } from "node:child_process";
9
+
10
+ // Destructive commands blocked in plan mode
11
+ const DESTRUCTIVE_PATTERNS = [
12
+ /\brm\b/i,
13
+ /\brmdir\b/i,
14
+ /\bmv\b/i,
15
+ /\bcp\b/i,
16
+ /\bmkdir\b/i,
17
+ /\btouch\b/i,
18
+ /\bchmod\b/i,
19
+ /\bchown\b/i,
20
+ /\bchgrp\b/i,
21
+ /\bln\b/i,
22
+ /\btee\b/i,
23
+ /\btruncate\b/i,
24
+ /\bdd\b/i,
25
+ /\bshred\b/i,
26
+ /(^|[^<])>(?!>)/,
27
+ />>/,
28
+ /\bnpm\s+(install|uninstall|update|ci|link|publish)/i,
29
+ /\byarn\s+(add|remove|install|publish)/i,
30
+ /\bpnpm\s+(add|remove|install|publish)/i,
31
+ /\bpip\s+(install|uninstall)/i,
32
+ /\bapt(-get)?\s+(install|remove|purge|update|upgrade)/i,
33
+ /\bbrew\s+(install|uninstall|upgrade)/i,
34
+ /\bgit\s+(add|commit|push|pull|merge|rebase|reset|checkout|branch\s+-[dD]|stash|cherry-pick|revert|tag|init|clone)/i,
35
+ /\bsudo\b/i,
36
+ /\bsu\b/i,
37
+ /\bkill\b/i,
38
+ /\bpkill\b/i,
39
+ /\bkillall\b/i,
40
+ /\breboot\b/i,
41
+ /\bshutdown\b/i,
42
+ /\bsystemctl\s+(start|stop|restart|enable|disable)/i,
43
+ /\bservice\s+\S+\s+(start|stop|restart)/i,
44
+ /\b(vim?|nano|emacs|code|subl)\b/i,
45
+ ];
46
+
47
+ // Safe read-only commands allowed in plan mode
48
+ const SAFE_PATTERNS = [
49
+ /^\s*cat\b/,
50
+ /^\s*head\b/,
51
+ /^\s*tail\b/,
52
+ /^\s*less\b/,
53
+ /^\s*more\b/,
54
+ /^\s*grep\b/,
55
+ /^\s*find\b/,
56
+ /^\s*ls\b/,
57
+ /^\s*pwd\b/,
58
+ /^\s*echo\b/,
59
+ /^\s*printf\b/,
60
+ /^\s*wc\b/,
61
+ /^\s*sort\b/,
62
+ /^\s*uniq\b/,
63
+ /^\s*diff\b/,
64
+ /^\s*file\b/,
65
+ /^\s*stat\b/,
66
+ /^\s*du\b/,
67
+ /^\s*df\b/,
68
+ /^\s*tree\b/,
69
+ /^\s*which\b/,
70
+ /^\s*whereis\b/,
71
+ /^\s*type\b/,
72
+ /^\s*env\b/,
73
+ /^\s*printenv\b/,
74
+ /^\s*uname\b/,
75
+ /^\s*whoami\b/,
76
+ /^\s*id\b/,
77
+ /^\s*date\b/,
78
+ /^\s*cal\b/,
79
+ /^\s*uptime\b/,
80
+ /^\s*ps\b/,
81
+ /^\s*top\b/,
82
+ /^\s*htop\b/,
83
+ /^\s*free\b/,
84
+ /^\s*git\s+(status|log|diff|show|branch|remote|config\s+--get)/i,
85
+ /^\s*git\s+ls-/i,
86
+ /^\s*npm\s+(list|ls|view|info|search|outdated|audit)/i,
87
+ /^\s*yarn\s+(list|info|why|audit)/i,
88
+ /^\s*node\s+--version/i,
89
+ /^\s*python\s+--version/i,
90
+ /^\s*curl\s/i,
91
+ /^\s*wget\s+-O\s*-/i,
92
+ /^\s*jq\b/,
93
+ /^\s*sed\s+-n/i,
94
+ /^\s*awk\b/,
95
+ /^\s*rg\b/,
96
+ /^\s*fd\b/,
97
+ /^\s*bat\b/,
98
+ /^\s*exa\b/,
99
+ ];
100
+
101
+ export function isSafeCommand(command: string): boolean {
102
+ const isDestructive = DESTRUCTIVE_PATTERNS.some((p) => p.test(command));
103
+ const isSafe = SAFE_PATTERNS.some((p) => p.test(command));
104
+ return !isDestructive && isSafe;
105
+ }
106
+
107
+ export interface TodoItem {
108
+ step: number;
109
+ text: string;
110
+ completed: boolean;
111
+ }
112
+
113
+ export function cleanStepText(text: string): string {
114
+ let cleaned = text
115
+ .replace(/\*{1,2}([^*]+)\*{1,2}/g, "$1") // Remove bold/italic
116
+ .replace(/`([^`]+)`/g, "$1") // Remove code
117
+ .replace(
118
+ /^(Use|Run|Execute|Create|Write|Read|Check|Verify|Update|Modify|Add|Remove|Delete|Install)\s+(the\s+)?/i,
119
+ "",
120
+ )
121
+ .replace(/\s+/g, " ")
122
+ .trim();
123
+
124
+ if (cleaned.length > 0) {
125
+ cleaned = cleaned.charAt(0).toUpperCase() + cleaned.slice(1);
126
+ }
127
+ if (cleaned.length > 50) {
128
+ cleaned = `${cleaned.slice(0, 47)}...`;
129
+ }
130
+ return cleaned;
131
+ }
132
+
133
+ export function extractTodoItems(message: string): TodoItem[] {
134
+ const items: TodoItem[] = [];
135
+ const headerMatch = message.match(/\*{0,2}Plan:\*{0,2}\s*\n/i);
136
+ if (!headerMatch) return items;
137
+
138
+ const planSection = message.slice(message.indexOf(headerMatch[0]) + headerMatch[0].length);
139
+ const numberedPattern = /^\s*(\d+)[.)]\s+\*{0,2}([^*\n]+)/gm;
140
+
141
+ const matches = Array.from(planSection.matchAll(numberedPattern));
142
+ for (const match of matches) {
143
+ const text = match[2]
144
+ .trim()
145
+ .replace(/\*{1,2}$/, "")
146
+ .trim();
147
+ if (text.length > 5 && !text.startsWith("`") && !text.startsWith("/") && !text.startsWith("-")) {
148
+ const cleaned = cleanStepText(text);
149
+ if (cleaned.length > 3) {
150
+ items.push({ step: items.length + 1, text: cleaned, completed: false });
151
+ }
152
+ }
153
+ }
154
+ return items;
155
+ }
156
+
157
+ export function extractDoneSteps(message: string): number[] {
158
+ const steps: number[] = [];
159
+ const matches = Array.from(message.matchAll(/\[DONE:(\d+)\]/gi));
160
+ for (const match of matches) {
161
+ const step = Number(match[1]);
162
+ if (Number.isFinite(step)) steps.push(step);
163
+ }
164
+ return steps;
165
+ }
166
+
167
+ export function markCompletedSteps(text: string, items: TodoItem[]): number {
168
+ const doneSteps = extractDoneSteps(text);
169
+ for (const step of doneSteps) {
170
+ const item = items.find((t) => t.step === step);
171
+ if (item) item.completed = true;
172
+ }
173
+ return doneSteps.length;
174
+ }
175
+
176
+ // =============================================================================
177
+ // bd (beads) Integration Functions
178
+ // =============================================================================
179
+
180
+ /**
181
+ * Extract short ID from full bd issue ID.
182
+ * Example: "jaggers-agent-tools-xr9b.1" → "xr9b.1"
183
+ */
184
+ export function getShortId(fullId: string): string {
185
+ const parts = fullId.split("-");
186
+ // Last part is the ID (e.g., "xr9b.1")
187
+ return parts[parts.length - 1];
188
+ }
189
+
190
+ /**
191
+ * Check if a directory is a beads project (has .beads directory).
192
+ */
193
+ export function isBeadsProject(cwd: string): boolean {
194
+ return existsSync(join(cwd, ".beads"));
195
+ }
196
+
197
+ /**
198
+ * Derive epic title from user prompt or conversation messages.
199
+ */
200
+ export function deriveEpicTitle(messages: Array<{ role: string; content?: unknown }>): string {
201
+ // Find the last user message
202
+ for (let i = messages.length - 1; i >= 0; i--) {
203
+ const msg = messages[i];
204
+ if (msg.role === "user") {
205
+ const content = msg.content;
206
+ if (typeof content === "string") {
207
+ // Extract first sentence or first 50 chars
208
+ const firstSentence = content.split(/[.!?\n]/)[0].trim();
209
+ if (firstSentence.length > 10 && firstSentence.length < 80) {
210
+ return firstSentence;
211
+ }
212
+ if (firstSentence.length >= 80) {
213
+ return `${firstSentence.slice(0, 77)}...`;
214
+ }
215
+ }
216
+ }
217
+ }
218
+ return "Plan execution";
219
+ }
220
+
221
+ /**
222
+ * Run a bd command and return the result.
223
+ */
224
+ function runBd(args: string[], cwd: string): { stdout: string; stderr: string; status: number } {
225
+ const result = spawnSync("bd", args, {
226
+ cwd,
227
+ encoding: "utf8",
228
+ timeout: 30000,
229
+ });
230
+ return {
231
+ stdout: result.stdout || "",
232
+ stderr: result.stderr || "",
233
+ status: result.status ?? 1,
234
+ };
235
+ }
236
+
237
+ /**
238
+ * Create an epic in bd.
239
+ */
240
+ export function bdCreateEpic(title: string, cwd: string): { id: string; title: string } | null {
241
+ const result = runBd(["create", title, "-t", "epic", "-p", "1", "--json"], cwd);
242
+ if (result.status === 0) {
243
+ try {
244
+ const data = JSON.parse(result.stdout);
245
+ if (Array.isArray(data) && data[0]) {
246
+ return { id: data[0].id, title: data[0].title };
247
+ }
248
+ } catch {
249
+ // Parse the ID from stdout if JSON parse fails
250
+ const match = result.stdout.match(/Created issue:\s*(\S+)/);
251
+ if (match) {
252
+ return { id: match[1], title };
253
+ }
254
+ }
255
+ }
256
+ return null;
257
+ }
258
+
259
+ /**
260
+ * Create a task issue in bd under an epic.
261
+ */
262
+ export function bdCreateIssue(
263
+ title: string,
264
+ description: string,
265
+ parentId: string,
266
+ cwd: string,
267
+ ): { id: string; title: string } | null {
268
+ const result = runBd(
269
+ ["create", title, "-t", "task", "-p", "1", "--parent", parentId, "-d", description, "--json"],
270
+ cwd,
271
+ );
272
+ if (result.status === 0) {
273
+ try {
274
+ const data = JSON.parse(result.stdout);
275
+ if (Array.isArray(data) && data[0]) {
276
+ return { id: data[0].id, title: data[0].title };
277
+ }
278
+ } catch {
279
+ const match = result.stdout.match(/Created issue:\s*(\S+)/);
280
+ if (match) {
281
+ return { id: match[1], title };
282
+ }
283
+ }
284
+ }
285
+ return null;
286
+ }
287
+
288
+ /**
289
+ * Claim an issue in bd.
290
+ */
291
+ export function bdClaim(issueId: string, cwd: string): boolean {
292
+ const result = runBd(["update", issueId, "--claim"], cwd);
293
+ return result.status === 0;
294
+ }
295
+
296
+ /**
297
+ * Result of creating plan issues.
298
+ */
299
+ export interface PlanIssuesResult {
300
+ epic: { id: string; title: string };
301
+ issues: Array<{ id: string; title: string }>;
302
+ }
303
+
304
+ /**
305
+ * Create an epic and issues from todo items.
306
+ */
307
+ export function createPlanIssues(
308
+ epicTitle: string,
309
+ todos: TodoItem[],
310
+ cwd: string,
311
+ ): PlanIssuesResult | null {
312
+ const epic = bdCreateEpic(epicTitle, cwd);
313
+ if (!epic) return null;
314
+
315
+ const issues: Array<{ id: string; title: string }> = [];
316
+ for (const todo of todos) {
317
+ const issue = bdCreateIssue(todo.text, `Step ${todo.step} of plan: ${epicTitle}`, epic.id, cwd);
318
+ if (issue) {
319
+ issues.push(issue);
320
+ }
321
+ }
322
+
323
+ return { epic, issues };
324
+ }
@@ -0,0 +1,67 @@
1
+ import type { ExtensionAPI, ToolResultEvent } from "@mariozechner/pi-coding-agent";
2
+ import { SubprocessRunner, EventAdapter, Logger } from "../core/lib";
3
+ import * as path from "node:path";
4
+ import * as fs from "node:fs";
5
+
6
+ const logger = new Logger({ namespace: "quality-gates" });
7
+
8
+ export default function (pi: ExtensionAPI) {
9
+ pi.on("tool_result", async (event, ctx) => {
10
+ if (!EventAdapter.isMutatingFileTool(event)) return undefined;
11
+
12
+ const cwd = ctx.cwd || process.cwd();
13
+ const filePath = EventAdapter.extractPathFromToolInput(event, cwd);
14
+ if (!filePath) return undefined;
15
+
16
+ const fullPath = path.isAbsolute(filePath) ? filePath : path.join(cwd, filePath);
17
+ const ext = path.extname(fullPath);
18
+
19
+ let scriptPath: string | null = null;
20
+ let runner: string = "node";
21
+
22
+ if ([".ts", ".tsx", ".js", ".jsx"].includes(ext)) {
23
+ scriptPath = path.join(cwd, ".claude", "hooks", "quality-check.cjs");
24
+ runner = "node";
25
+ } else if (ext === ".py") {
26
+ scriptPath = path.join(cwd, ".claude", "hooks", "quality-check.py");
27
+ runner = "python3";
28
+ }
29
+
30
+ if (!scriptPath || !fs.existsSync(scriptPath)) return undefined;
31
+
32
+ const hookInput = JSON.stringify({
33
+ tool_name: event.toolName,
34
+ tool_input: event.input,
35
+ cwd: cwd,
36
+ });
37
+
38
+ const result = await SubprocessRunner.run(runner, [scriptPath], {
39
+ cwd,
40
+ input: hookInput,
41
+ env: { ...process.env, CLAUDE_PROJECT_DIR: cwd },
42
+ timeoutMs: 30000,
43
+ });
44
+
45
+ if (result.code === 0) {
46
+ if (result.stderr && result.stderr.trim()) {
47
+ const newContent = [...event.content];
48
+ newContent.push({ type: "text", text: `\n\n**Quality Gate**: ${result.stderr.trim()}` });
49
+ return { content: newContent };
50
+ }
51
+ return undefined;
52
+ }
53
+
54
+ if (result.code === 2) {
55
+ const newContent = [...event.content];
56
+ newContent.push({ type: "text", text: `\n\n**Quality Gate FAILED**:\n${result.stderr || result.stdout || "Unknown error"}` });
57
+
58
+ if (ctx.hasUI) {
59
+ ctx.ui.notify(`Quality Gate failed for ${path.basename(fullPath)}`, "error");
60
+ }
61
+
62
+ return { isError: true, content: newContent };
63
+ }
64
+
65
+ return undefined;
66
+ });
67
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "@xtrm/pi-quality-gates",
3
+ "version": "1.0.0",
4
+ "description": "xtrm Pi extension: quality-gates",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./index.ts"
8
+ },
9
+ "keywords": [
10
+ "pi",
11
+ "extension",
12
+ "xtrm"
13
+ ],
14
+ "author": "xtrm",
15
+ "license": "MIT"
16
+ }
@@ -0,0 +1,108 @@
1
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
+ import { SubprocessRunner } from "../core/lib";
3
+ import * as path from "node:path";
4
+ import * as fs from "node:fs";
5
+
6
+ const SERVICE_REGISTRY_FILES = [
7
+ "service-registry.json",
8
+ path.join(".claude", "skills", "service-registry.json"),
9
+ ];
10
+
11
+ const GLOBAL_SKILL_ROOTS = [
12
+ path.join(process.env.HOME || "", ".agents", "skills"),
13
+ path.join(process.env.HOME || "", ".claude", "skills"),
14
+ ];
15
+
16
+ export default function (pi: ExtensionAPI) {
17
+ const getCwd = (ctx: any) => ctx.cwd || process.cwd();
18
+
19
+ const resolveRegistryPath = (cwd: string): string | null => {
20
+ for (const rel of SERVICE_REGISTRY_FILES) {
21
+ const candidate = path.join(cwd, rel);
22
+ if (fs.existsSync(candidate)) return candidate;
23
+ }
24
+ return null;
25
+ };
26
+
27
+ const resolveSkillScript = (cwd: string, skillName: string, scriptName: string): string | null => {
28
+ const localPath = path.join(cwd, ".claude", "skills", skillName, "scripts", scriptName);
29
+ if (fs.existsSync(localPath)) return localPath;
30
+
31
+ for (const root of GLOBAL_SKILL_ROOTS) {
32
+ if (!root) continue;
33
+ const candidate = path.join(root, skillName, "scripts", scriptName);
34
+ if (fs.existsSync(candidate)) return candidate;
35
+ }
36
+
37
+ return null;
38
+ };
39
+
40
+ // 1. Catalog Injection
41
+ pi.on("before_agent_start", async (event, ctx) => {
42
+ const cwd = getCwd(ctx);
43
+ const registryPath = resolveRegistryPath(cwd);
44
+ if (!registryPath) return undefined;
45
+
46
+ const catalogerPath = resolveSkillScript(cwd, "using-service-skills", "cataloger.py");
47
+ if (!catalogerPath) return undefined;
48
+
49
+ const result = await SubprocessRunner.run("python3", [catalogerPath], {
50
+ cwd,
51
+ env: {
52
+ ...process.env,
53
+ CLAUDE_PROJECT_DIR: cwd,
54
+ SERVICE_REGISTRY_PATH: registryPath,
55
+ },
56
+ });
57
+
58
+ if (result.code === 0 && result.stdout.trim()) {
59
+ return { systemPrompt: event.systemPrompt + "\n\n" + result.stdout.trim() };
60
+ }
61
+ return undefined;
62
+ });
63
+
64
+ const toClaudeToolName = (toolName: string): string => {
65
+ if (toolName === "bash") return "Bash";
66
+ if (toolName === "read_file") return "Read";
67
+ if (toolName === "write" || toolName === "create_text_file") return "Write";
68
+ if (toolName === "edit" || toolName === "replace_content" || toolName === "replace_lines" || toolName === "insert_at_line" || toolName === "delete_lines") return "Edit";
69
+ if (toolName === "search_for_pattern") return "Grep";
70
+ if (toolName === "find_file" || toolName === "list_dir") return "Glob";
71
+ return toolName;
72
+ };
73
+
74
+ // 2. Drift Detection (skill activation is before_agent_start only — not per-tool)
75
+ pi.on("tool_result", async (event, ctx) => {
76
+ const cwd = getCwd(ctx);
77
+ const registryPath = resolveRegistryPath(cwd);
78
+ if (!registryPath) return undefined;
79
+
80
+ const driftDetectorPath = resolveSkillScript(cwd, "updating-service-skills", "drift_detector.py");
81
+ if (!driftDetectorPath) return undefined;
82
+
83
+ const hookInput = JSON.stringify({
84
+ tool_name: toClaudeToolName(event.toolName),
85
+ tool_input: event.input,
86
+ cwd,
87
+ });
88
+
89
+ const result = await SubprocessRunner.run("python3", [driftDetectorPath], {
90
+ cwd,
91
+ input: hookInput,
92
+ env: {
93
+ ...process.env,
94
+ CLAUDE_PROJECT_DIR: cwd,
95
+ SERVICE_REGISTRY_PATH: registryPath,
96
+ },
97
+ timeoutMs: 10000,
98
+ });
99
+
100
+ if (result.code === 0 && result.stdout.trim()) {
101
+ const newContent = [...event.content];
102
+ newContent.push({ type: "text", text: "\n\n" + result.stdout.trim() });
103
+ return { content: newContent };
104
+ }
105
+
106
+ return undefined;
107
+ });
108
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "@xtrm/pi-service-skills",
3
+ "version": "1.0.0",
4
+ "description": "xtrm Pi extension: service-skills",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./index.ts"
8
+ },
9
+ "keywords": [
10
+ "pi",
11
+ "extension",
12
+ "xtrm"
13
+ ],
14
+ "author": "xtrm",
15
+ "license": "MIT"
16
+ }
@@ -0,0 +1,131 @@
1
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
+ import { isBashToolResult } from "@mariozechner/pi-coding-agent";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { SubprocessRunner, EventAdapter } from "../core/lib";
6
+ import { readSessionState } from "../core/session-state";
7
+
8
+ function isClaimCommand(command: string): { isClaim: boolean; issueId: string | null } {
9
+ if (!/\bbd\s+update\b/.test(command) || !/--claim\b/.test(command)) {
10
+ return { isClaim: false, issueId: null };
11
+ }
12
+ const match = command.match(/\bbd\s+update\s+(\S+)/);
13
+ return { isClaim: true, issueId: match?.[1] ?? null };
14
+ }
15
+
16
+ function statePathFrom(startCwd: string): string {
17
+ let current = path.resolve(startCwd || process.cwd());
18
+ for (;;) {
19
+ const candidate = path.join(current, ".xtrm-session-state.json");
20
+ if (fs.existsSync(candidate)) return candidate;
21
+ const parent = path.dirname(current);
22
+ if (parent === current) return path.join(startCwd, ".xtrm-session-state.json");
23
+ current = parent;
24
+ }
25
+ }
26
+
27
+ async function ensureWorktreeSessionState(cwd: string, issueId: string): Promise<{ ok: boolean; message?: string }> {
28
+ const repoRootResult = await SubprocessRunner.run("git", ["rev-parse", "--show-toplevel"], { cwd });
29
+ if (repoRootResult.code !== 0 || !repoRootResult.stdout) return { ok: false, message: "not a git repo" };
30
+ const repoRoot = repoRootResult.stdout.trim();
31
+
32
+ const gitDir = await SubprocessRunner.run("git", ["rev-parse", "--git-dir"], { cwd });
33
+ const commonDir = await SubprocessRunner.run("git", ["rev-parse", "--git-common-dir"], { cwd });
34
+ if (gitDir.code === 0 && commonDir.code === 0 && gitDir.stdout.trim() !== commonDir.stdout.trim()) {
35
+ return { ok: false, message: "already in linked worktree" };
36
+ }
37
+
38
+ const overstoryDir = path.join(repoRoot, ".overstory");
39
+ const worktreesBase = fs.existsSync(overstoryDir)
40
+ ? path.join(overstoryDir, "worktrees")
41
+ : path.join(repoRoot, ".worktrees");
42
+ fs.mkdirSync(worktreesBase, { recursive: true });
43
+
44
+ const branch = `feature/${issueId}`;
45
+ const worktreePath = path.join(worktreesBase, issueId);
46
+ if (!fs.existsSync(worktreePath)) {
47
+ const branchExists = (await SubprocessRunner.run("git", ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`], { cwd: repoRoot })).code === 0;
48
+ const addArgs = branchExists
49
+ ? ["worktree", "add", worktreePath, branch]
50
+ : ["worktree", "add", worktreePath, "-b", branch];
51
+ const add = await SubprocessRunner.run("git", addArgs, { cwd: repoRoot, timeoutMs: 20000 });
52
+ if (add.code !== 0) {
53
+ return { ok: false, message: add.stderr || add.stdout || "worktree creation failed" };
54
+ }
55
+ }
56
+
57
+ const statePath = statePathFrom(repoRoot);
58
+ const payload = {
59
+ issueId,
60
+ branch,
61
+ worktreePath,
62
+ prNumber: null,
63
+ prUrl: null,
64
+ phase: "claimed",
65
+ conflictFiles: [],
66
+ startedAt: new Date().toISOString(),
67
+ lastChecked: new Date().toISOString(),
68
+ };
69
+ fs.writeFileSync(statePath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
70
+ return { ok: true, message: `Worktree created: ${worktreePath} Branch: ${branch}` };
71
+ }
72
+
73
+ export default function (pi: ExtensionAPI) {
74
+ const getCwd = (ctx: any) => ctx.cwd || process.cwd();
75
+
76
+ pi.on("tool_result", async (event, ctx) => {
77
+ if (!isBashToolResult(event)) return undefined;
78
+ const cwd = getCwd(ctx);
79
+ if (!EventAdapter.isBeadsProject(cwd)) return undefined;
80
+
81
+ const command = event.input.command || "";
82
+ const { isClaim, issueId } = isClaimCommand(command);
83
+ if (!isClaim || !issueId) return undefined;
84
+
85
+ const ensured = await ensureWorktreeSessionState(cwd, issueId);
86
+ if (ensured.ok) {
87
+ const state = readSessionState(cwd);
88
+ const worktreePath = state?.worktreePath;
89
+ const nextStep = worktreePath
90
+ ? `\nNext: cd ${worktreePath} && pi (sandboxed session)`
91
+ : "";
92
+ const text = `\n\n🧭 Session Flow: ${ensured.message}${nextStep}`;
93
+ return { content: [...event.content, { type: "text", text }] };
94
+ }
95
+ return undefined;
96
+ });
97
+
98
+ pi.on("agent_end", async (_event, ctx) => {
99
+ const cwd = getCwd(ctx);
100
+ if (!EventAdapter.isBeadsProject(cwd)) return undefined;
101
+ const state = readSessionState(cwd);
102
+ if (!state) return undefined;
103
+
104
+ if (state.phase === "waiting-merge" || state.phase === "pending-cleanup") {
105
+ const pr = state.prNumber != null ? `#${state.prNumber}` : "(pending PR)";
106
+ const url = state.prUrl ? ` ${state.prUrl}` : "";
107
+ pi.sendUserMessage(
108
+ `âš  PR ${pr}${url} is still pending. xtrm finish is deprecated for Pi workflow. ` +
109
+ "Use xtpi publish (when available) and external merge/cleanup steps.",
110
+ );
111
+ return undefined;
112
+ }
113
+
114
+ if (state.phase === "conflicting") {
115
+ const files = state.conflictFiles?.length ? state.conflictFiles.join(", ") : "unknown files";
116
+ pi.sendUserMessage(
117
+ `âš  Conflicts in: ${files}. xtrm finish is deprecated for Pi workflow. ` +
118
+ "Resolve conflicts, then continue with publish-only flow.",
119
+ );
120
+ return undefined;
121
+ }
122
+
123
+ if (state.phase === "claimed" || state.phase === "phase1-done") {
124
+ pi.sendUserMessage(
125
+ `âš  Session has an active worktree at ${state.worktreePath}. ` +
126
+ "Use publish-only workflow (no automatic push/PR/merge).",
127
+ );
128
+ }
129
+ return undefined;
130
+ });
131
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "@xtrm/pi-session-flow",
3
+ "version": "1.0.0",
4
+ "description": "xtrm Pi extension: session-flow",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./index.ts"
8
+ },
9
+ "keywords": [
10
+ "pi",
11
+ "extension",
12
+ "xtrm"
13
+ ],
14
+ "author": "xtrm",
15
+ "license": "MIT"
16
+ }