claude-dev-env 4.3.0 → 7.0.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 (293) hide show
  1. package/.agents/agents/pr-description-writer.md +2 -2
  2. package/.agents/agents/test_agent_frontmatter.py +2 -2
  3. package/.agents/agents/test_pr_description_writer_style.py +1 -3
  4. package/.agents/skills/e-code-review/SKILL.md +12 -12
  5. package/.agents/skills/e-code-review/preflight-proposal.contract.test.mjs +20 -0
  6. package/.agents/skills/e-code-review/reference/fix.md +15 -17
  7. package/.agents/skills/e-code-review/reference/loop.md +35 -39
  8. package/.agents/skills/e-code-review/reference/mode-contract.test.mjs +2 -1
  9. package/.agents/skills/e-code-review/reference/preflight-proposal.md +44 -9
  10. package/.agents/skills/e-code-review/reference/runner-selection.md +13 -14
  11. package/.agents/skills/issue-tracker/SKILL.md +5 -4
  12. package/.agents/skills/pull-request/SKILL.md +11 -13
  13. package/.agents/skills/pull-request/reference/publication-tasks.md +3 -3
  14. package/.agents/skills/pull-request/scripts/github_pr_command_constants/config/constants.py +9 -1
  15. package/.agents/skills/pull-request/scripts/pull_request.py +33 -44
  16. package/.agents/skills/pull-request/scripts/test_github_pr_command_constants.py +9 -0
  17. package/.agents/skills/pull-request/scripts/test_pull_request.py +1 -3
  18. package/.agents/skills/skill-builder/SKILL.md +3 -196
  19. package/_shared/pr-loop/scripts/pr_loop_shared_constants/terminology_sweep_constants.py +6 -0
  20. package/_shared/pr-loop/scripts/terminology_sweep.py +64 -19
  21. package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +59 -0
  22. package/bin/install.agents-home.test.mjs +15 -13
  23. package/bin/install.mjs +28 -0
  24. package/bin/install.policy-lint.test.mjs +78 -0
  25. package/bin/install.prune.test.mjs +59 -20
  26. package/bin/install.pstack-plugin.test.mjs +114 -0
  27. package/bin/install.test.mjs +37 -0
  28. package/bin/refresh-pstack-plugin-skills.test.mjs +211 -0
  29. package/hooks/advisory/pr_done_reminder.py +342 -0
  30. package/hooks/advisory/test_pr_done_reminder.py +249 -0
  31. package/hooks/blocking/bash_post_call_dispatcher.py +53 -6
  32. package/hooks/blocking/code_rules_paired_test.py +22 -15
  33. package/hooks/blocking/test_bash_post_call_dispatcher.py +65 -4
  34. package/hooks/blocking/test_code_rules_enforcer_paired_test.py +36 -29
  35. package/hooks/blocking/test_code_rules_js_resume_task_enumeration.py +4 -4
  36. package/hooks/blocking/test_code_rules_js_returns_object_schemaless.py +5 -5
  37. package/hooks/blocking/test_pre_tool_use_dispatcher.py +68 -133
  38. package/hooks/blocking/test_pre_tool_use_dispatcher_native.py +20 -51
  39. package/hooks/blocking/test_precommit_code_rules_gate_native_owner.py +18 -18
  40. package/hooks/git-hooks/git_hooks_constants/staged_policy_lint.py +12 -0
  41. package/hooks/git-hooks/pre_commit.py +52 -11
  42. package/hooks/git-hooks/test_installed_policy_lint.py +159 -0
  43. package/hooks/git-hooks/test_native_policy_commit.py +87 -0
  44. package/hooks/git-hooks/test_pre_commit.py +246 -0
  45. package/hooks/hooks.json +1 -1
  46. package/hooks/hooks_constants/bash_post_call_dispatcher_constants.py +14 -1
  47. package/hooks/hooks_constants/pr_done_reminder_constants.py +98 -0
  48. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +10 -43
  49. package/hooks/hooks_constants/test_bash_post_call_dispatcher_constants.py +19 -2
  50. package/hooks/hooks_constants/test_pr_done_reminder_constants.py +40 -0
  51. package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +20 -32
  52. package/package.json +2 -2
  53. package/rules/durable-post-artifacts.md +2 -1
  54. package/rules/git-workflow.md +6 -6
  55. package/scripts/check.ps1 +0 -1
  56. package/{.agents/skills/run-claude-dev-env/driver.mjs → scripts/ci/installer-lifecycle.mjs} +3 -28
  57. package/scripts/ci/installer-lifecycle.test.mjs +26 -0
  58. package/scripts/ci/windows-installer-lifecycle.ps1 +1 -11
  59. package/scripts/dev_env_scripts_constants/shared_tree_constants.py +4 -5
  60. package/{.agents/skills/_shared/pr-loop/scripts → scripts}/durable_post_lint.py +5 -41
  61. package/{.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/durable_post_lint_constants.py → scripts/durable_post_lint_config/config/constants.py} +0 -10
  62. package/{.agents/skills/_shared/pr-loop/scripts/test_durable_post_lint_constants.py → scripts/durable_post_lint_config/config/test_constants.py} +1 -1
  63. package/scripts/policy_lint/adapter_detectors.py +13 -6
  64. package/scripts/policy_lint/adapters.py +7 -4
  65. package/scripts/policy_lint/config/constants.py +5 -0
  66. package/scripts/policy_lint/engine.py +48 -4
  67. package/scripts/policy_lint/model.py +1 -0
  68. package/scripts/policy_lint/registry.py +1 -1
  69. package/scripts/policy_lint/selection.py +2 -1
  70. package/scripts/policy_lint/selection_git.py +2 -0
  71. package/scripts/refresh_pstack_plugin_skills.mjs +200 -0
  72. package/scripts/shared_tree_paths.py +32 -3
  73. package/scripts/test_shared_tree_paths.py +33 -0
  74. package/scripts/tests/test_policy_lint_rules_archive.py +120 -0
  75. package/scripts/tests/test_policy_lint_rules_engine.py +25 -4
  76. package/scripts/tests/test_policy_lint_rules_installed_loader.py +29 -0
  77. package/scripts/tests/test_policy_lint_rules_registry.py +227 -13
  78. package/scripts/tests/test_policy_lint_selection.py +25 -2
  79. package/scripts/tests/test_policy_lint_selection_native_index.py +30 -0
  80. package/scripts/tests/test_rules.py +3 -7
  81. package/.agents/skills/_shared/pr-loop/.claude/CLAUDE.md +0 -1
  82. package/.agents/skills/_shared/pr-loop/AGENTS.md +0 -1
  83. package/.agents/skills/_shared/pr-loop/audit-contract.md +0 -5
  84. package/.agents/skills/_shared/pr-loop/audit-reply-template.md +0 -5
  85. package/.agents/skills/_shared/pr-loop/code-rules-gate.md +0 -5
  86. package/.agents/skills/_shared/pr-loop/fix-protocol.md +0 -5
  87. package/.agents/skills/_shared/pr-loop/gh-payloads.md +0 -5
  88. package/.agents/skills/_shared/pr-loop/portable-driver.md +0 -149
  89. package/.agents/skills/_shared/pr-loop/post-audit-thread-contract.md +0 -5
  90. package/.agents/skills/_shared/pr-loop/precatch-rubric.md +0 -5
  91. package/.agents/skills/_shared/pr-loop/preflight-proposal.contract.test.mjs +0 -119
  92. package/.agents/skills/_shared/pr-loop/preflight-proposal.md +0 -5
  93. package/.agents/skills/_shared/pr-loop/prompts/.claude/CLAUDE.md +0 -1
  94. package/.agents/skills/_shared/pr-loop/prompts/AGENTS.md +0 -1
  95. package/.agents/skills/_shared/pr-loop/prompts/pr-consistency-audit.xml +0 -324
  96. package/.agents/skills/_shared/pr-loop/scripts/.claude/CLAUDE.md +0 -1
  97. package/.agents/skills/_shared/pr-loop/scripts/AGENTS.md +0 -1
  98. package/.agents/skills/_shared/pr-loop/scripts/RUNTIME_SCRIPTS.md +0 -29
  99. package/.agents/skills/_shared/pr-loop/scripts/_cli_utils.py +0 -22
  100. package/.agents/skills/_shared/pr-loop/scripts/_path_resolver.py +0 -186
  101. package/.agents/skills/_shared/pr-loop/scripts/_xml_utils.py +0 -20
  102. package/.agents/skills/_shared/pr-loop/scripts/audit_category_schema.py +0 -355
  103. package/.agents/skills/_shared/pr-loop/scripts/build_audit_prompt.py +0 -289
  104. package/.agents/skills/_shared/pr-loop/scripts/build_converge_task_list.py +0 -310
  105. package/.agents/skills/_shared/pr-loop/scripts/build_fix_prompt.py +0 -292
  106. package/.agents/skills/_shared/pr-loop/scripts/init_loop_state.py +0 -134
  107. package/.agents/skills/_shared/pr-loop/scripts/portable_converge_driver.py +0 -1646
  108. package/.agents/skills/_shared/pr-loop/scripts/preflight_worktree.py +0 -392
  109. package/.agents/skills/_shared/pr-loop/scripts/select_converge_pacer.py +0 -215
  110. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/.claude/CLAUDE.md +0 -1
  111. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/AGENTS.md +0 -1
  112. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/__init__.py +0 -0
  113. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/audit_category_schema_constants.py +0 -37
  114. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/converge_task_list_constants.py +0 -56
  115. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/handoff_constants.py +0 -45
  116. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/pacer_constants.py +0 -47
  117. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +0 -157
  118. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/portable_driver_constants.py +0 -177
  119. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/preflight_constants.py +0 -70
  120. package/.agents/skills/_shared/pr-loop/scripts/teardown_worktrees.py +0 -172
  121. package/.agents/skills/_shared/pr-loop/scripts/test__path_resolver.py +0 -106
  122. package/.agents/skills/_shared/pr-loop/scripts/test_audit_category_schema.py +0 -94
  123. package/.agents/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +0 -499
  124. package/.agents/skills/_shared/pr-loop/scripts/test_build_converge_task_list.py +0 -140
  125. package/.agents/skills/_shared/pr-loop/scripts/test_build_fix_prompt.py +0 -262
  126. package/.agents/skills/_shared/pr-loop/scripts/test_init_loop_state.py +0 -71
  127. package/.agents/skills/_shared/pr-loop/scripts/test_portable_converge_driver.py +0 -1119
  128. package/.agents/skills/_shared/pr-loop/scripts/test_preflight_worktree.py +0 -287
  129. package/.agents/skills/_shared/pr-loop/scripts/test_select_converge_pacer.py +0 -207
  130. package/.agents/skills/_shared/pr-loop/scripts/test_teardown_worktrees.py +0 -59
  131. package/.agents/skills/_shared/pr-loop/scripts/test_write_handoff.py +0 -201
  132. package/.agents/skills/_shared/pr-loop/scripts/write_audit_outcomes.py +0 -182
  133. package/.agents/skills/_shared/pr-loop/scripts/write_fix_outcomes.py +0 -206
  134. package/.agents/skills/_shared/pr-loop/scripts/write_handoff.py +0 -309
  135. package/.agents/skills/_shared/pr-loop/state-schema.md +0 -5
  136. package/.agents/skills/_shared/pr-loop/worker-spawn.md +0 -5
  137. package/.agents/skills/autoconverge/.claude/CLAUDE.md +0 -1
  138. package/.agents/skills/autoconverge/AGENTS.md +0 -1
  139. package/.agents/skills/autoconverge/SKILL.md +0 -431
  140. package/.agents/skills/autoconverge/reference/.claude/CLAUDE.md +0 -1
  141. package/.agents/skills/autoconverge/reference/AGENTS.md +0 -1
  142. package/.agents/skills/autoconverge/reference/closing-report.md +0 -93
  143. package/.agents/skills/autoconverge/reference/convergence-gates.md +0 -31
  144. package/.agents/skills/autoconverge/reference/convergence.md +0 -219
  145. package/.agents/skills/autoconverge/reference/copilot-findings.md +0 -51
  146. package/.agents/skills/autoconverge/reference/gotchas.md +0 -66
  147. package/.agents/skills/autoconverge/reference/headless-safety.md +0 -44
  148. package/.agents/skills/autoconverge/reference/multi-pr.md +0 -128
  149. package/.agents/skills/autoconverge/reference/self-closing-loop.md +0 -84
  150. package/.agents/skills/autoconverge/reference/stop-conditions.md +0 -127
  151. package/.agents/skills/autoconverge/test_portable_pacer_gate.py +0 -54
  152. package/.agents/skills/autoconverge/workflow/.claude/CLAUDE.md +0 -1
  153. package/.agents/skills/autoconverge/workflow/AGENTS.md +0 -1
  154. package/.agents/skills/autoconverge/workflow/aggregate_runs.py +0 -371
  155. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/.claude/CLAUDE.md +0 -1
  156. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/AGENTS.md +0 -1
  157. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/__init__.py +0 -0
  158. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/render_report_constants.py +0 -250
  159. package/.agents/skills/autoconverge/workflow/converge.clean-audit.test.mjs +0 -665
  160. package/.agents/skills/autoconverge/workflow/converge.codex-gate.test.mjs +0 -472
  161. package/.agents/skills/autoconverge/workflow/converge.contract.test.mjs +0 -64
  162. package/.agents/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +0 -1009
  163. package/.agents/skills/autoconverge/workflow/converge.fix-progress.test.mjs +0 -107
  164. package/.agents/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +0 -33
  165. package/.agents/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +0 -15
  166. package/.agents/skills/autoconverge/workflow/converge.mjs +0 -3132
  167. package/.agents/skills/autoconverge/workflow/converge.p2-advance.test.mjs +0 -202
  168. package/.agents/skills/autoconverge/workflow/converge.path-aware.test.mjs +0 -47
  169. package/.agents/skills/autoconverge/workflow/converge.precatch.test.mjs +0 -152
  170. package/.agents/skills/autoconverge/workflow/converge.run-input.test.mjs +0 -116
  171. package/.agents/skills/autoconverge/workflow/converge_multi.mjs +0 -169
  172. package/.agents/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +0 -173
  173. package/.agents/skills/autoconverge/workflow/convergence_summary.py +0 -110
  174. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a11d903476b803493.jsonl +0 -2
  175. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a26213978adeef6fb.jsonl +0 -2
  176. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a3def0d15ed9d9110.jsonl +0 -2
  177. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a41f41b1b708ee3b7.jsonl +0 -2
  178. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a758b880abecc3ff7.jsonl +0 -2
  179. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a8897b89656b1bd16.jsonl +0 -2
  180. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ab1c2d3e4f5a6b7c8.jsonl +0 -2
  181. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-abd463d744a1437bc.jsonl +0 -2
  182. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ad19d027ae8ee1816.jsonl +0 -2
  183. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/workflows/wf_881252e6-700.json +0 -266
  184. package/.agents/skills/autoconverge/workflow/render_report.py +0 -1024
  185. package/.agents/skills/autoconverge/workflow/test_aggregate_runs.py +0 -134
  186. package/.agents/skills/autoconverge/workflow/test_convergence_summary.py +0 -149
  187. package/.agents/skills/autoconverge/workflow/test_render_report.py +0 -812
  188. package/.agents/skills/hitl/SKILL.md +0 -45
  189. package/.agents/skills/pr-cleanup/SKILL.md +0 -140
  190. package/.agents/skills/pr-cleanup/reference/process-inventory.md +0 -17
  191. package/.agents/skills/pr-cleanup/reference/task-seeds.md +0 -22
  192. package/.agents/skills/pr-name-by-capability/SKILL.md +0 -73
  193. package/.agents/skills/pr-name-by-capability/reference/fetch-commands.md +0 -29
  194. package/.agents/skills/pr-name-by-capability/reference/offense-examples.md +0 -32
  195. package/.agents/skills/pr-name-by-capability/reference/report-template.md +0 -29
  196. package/.agents/skills/pr-name-by-capability/reference/rule-checklist.md +0 -38
  197. package/.agents/skills/pr-name-by-capability/reference/task-seeds.md +0 -10
  198. package/.agents/skills/pr-plain-language-cleanup/SKILL.md +0 -93
  199. package/.agents/skills/pr-refinement/SKILL.md +0 -31
  200. package/.agents/skills/pr-shared-extraction/SKILL.md +0 -177
  201. package/.agents/skills/pr-shared-extraction/reference/examples.md +0 -46
  202. package/.agents/skills/pr-shared-extraction/reference/offense-taxonomy.md +0 -100
  203. package/.agents/skills/pr-small-cl/SKILL.md +0 -52
  204. package/.agents/skills/pr-title-description/SKILL.md +0 -96
  205. package/.agents/skills/prototype/SKILL.md +0 -85
  206. package/.agents/skills/prototype/reference/honest-limitations.md +0 -23
  207. package/.agents/skills/prototype/reference/promotion-tasks.md +0 -23
  208. package/.agents/skills/prototype/scripts/build_sandbox_settings.py +0 -249
  209. package/.agents/skills/prototype/scripts/conftest.py +0 -15
  210. package/.agents/skills/prototype/scripts/launch_sandbox.py +0 -205
  211. package/.agents/skills/prototype/scripts/probe_sandbox_safety.py +0 -311
  212. package/.agents/skills/prototype/scripts/prototype_scripts_constants/__init__.py +0 -1
  213. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/__init__.py +0 -0
  214. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/build_sandbox_settings_constants.py +0 -41
  215. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/launch_sandbox_constants.py +0 -23
  216. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/probe_sandbox_safety_constants.py +0 -45
  217. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/prototype_common_constants.py +0 -10
  218. package/.agents/skills/prototype/scripts/test_build_sandbox_settings.py +0 -275
  219. package/.agents/skills/prototype/scripts/test_launch_sandbox.py +0 -303
  220. package/.agents/skills/prototype/scripts/test_probe_sandbox_safety.py +0 -312
  221. package/.agents/skills/prototype/workflows/promotion.md +0 -25
  222. package/.agents/skills/prototype/workflows/sandbox.md +0 -35
  223. package/.agents/skills/rebase/.claude/CLAUDE.md +0 -1
  224. package/.agents/skills/rebase/AGENTS.md +0 -1
  225. package/.agents/skills/rebase/SKILL.md +0 -178
  226. package/.agents/skills/review-router/SKILL.md +0 -51
  227. package/.agents/skills/review-router/reference/route-policy.json +0 -7
  228. package/.agents/skills/review-router/reference/task-seeds.md +0 -1
  229. package/.agents/skills/review-router/scripts/conftest.py +0 -4
  230. package/.agents/skills/review-router/scripts/review_router.py +0 -50
  231. package/.agents/skills/review-router/scripts/review_router_cli.py +0 -211
  232. package/.agents/skills/review-router/scripts/review_router_constants/__init__.py +0 -1
  233. package/.agents/skills/review-router/scripts/review_router_constants/config/__init__.py +0 -0
  234. package/.agents/skills/review-router/scripts/review_router_constants/config/constants.py +0 -13
  235. package/.agents/skills/review-router/scripts/test_review_router.py +0 -23
  236. package/.agents/skills/review-router/scripts/test_review_router_cli.py +0 -47
  237. package/.agents/skills/review-tier/SKILL.md +0 -56
  238. package/.agents/skills/review-tier/reference/task-seeds.md +0 -1
  239. package/.agents/skills/review-tier/reference/tier-policy.json +0 -9
  240. package/.agents/skills/review-tier/scripts/conftest.py +0 -4
  241. package/.agents/skills/review-tier/scripts/review_tier.py +0 -359
  242. package/.agents/skills/review-tier/scripts/review_tier_constants/__init__.py +0 -1
  243. package/.agents/skills/review-tier/scripts/review_tier_constants/config/__init__.py +0 -0
  244. package/.agents/skills/review-tier/scripts/review_tier_constants/config/constants.py +0 -29
  245. package/.agents/skills/review-tier/scripts/test_generation.py +0 -159
  246. package/.agents/skills/review-tier/scripts/test_review_tier.py +0 -33
  247. package/.agents/skills/run-claude-dev-env/SKILL.md +0 -84
  248. package/.agents/skills/session-log/.claude/CLAUDE.md +0 -1
  249. package/.agents/skills/session-log/AGENTS.md +0 -1
  250. package/.agents/skills/session-log/SKILL.md +0 -170
  251. package/.agents/skills/session-log/templates/frontmatter.md +0 -40
  252. package/.agents/skills/session-tidy/.claude/CLAUDE.md +0 -1
  253. package/.agents/skills/session-tidy/AGENTS.md +0 -1
  254. package/.agents/skills/session-tidy/SKILL.md +0 -191
  255. package/.agents/skills/skill-builder/references/.claude/CLAUDE.md +0 -1
  256. package/.agents/skills/skill-builder/references/AGENTS.md +0 -1
  257. package/.agents/skills/skill-builder/references/delegation-map.md +0 -130
  258. package/.agents/skills/skill-builder/references/description-field.md +0 -111
  259. package/.agents/skills/skill-builder/references/deterministic-elements.md +0 -218
  260. package/.agents/skills/skill-builder/references/progressive-disclosure.md +0 -122
  261. package/.agents/skills/skill-builder/references/self-audit-checklist.md +0 -126
  262. package/.agents/skills/skill-builder/references/skill-modularity.md +0 -111
  263. package/.agents/skills/skill-builder/references/skill-types.md +0 -232
  264. package/.agents/skills/skill-builder/references/thariq-x-post-skills.json +0 -33
  265. package/.agents/skills/skill-builder/templates/.claude/CLAUDE.md +0 -1
  266. package/.agents/skills/skill-builder/templates/AGENTS.md +0 -1
  267. package/.agents/skills/skill-builder/templates/gap-analysis.md +0 -87
  268. package/.agents/skills/skill-builder/workflows/.claude/CLAUDE.md +0 -1
  269. package/.agents/skills/skill-builder/workflows/AGENTS.md +0 -1
  270. package/.agents/skills/skill-builder/workflows/improve-skill.md +0 -136
  271. package/.agents/skills/skill-builder/workflows/new-skill.md +0 -179
  272. package/.agents/skills/skill-builder/workflows/polish-skill.md +0 -139
  273. package/.agents/skills/source-command-sr-loop/SKILL.md +0 -82
  274. package/.agents/skills/update/.claude/CLAUDE.md +0 -1
  275. package/.agents/skills/update/AGENTS.md +0 -1
  276. package/.agents/skills/update/SKILL.md +0 -190
  277. package/_shared/pr-loop/AGENTS.md +0 -1
  278. package/_shared/pr-loop/CLAUDE.md +0 -1
  279. package/_shared/pr-loop/audit-contract.md +0 -183
  280. package/_shared/pr-loop/audit-reply-template.md +0 -151
  281. package/_shared/pr-loop/code-rules-gate.md +0 -65
  282. package/_shared/pr-loop/fix-protocol.md +0 -60
  283. package/_shared/pr-loop/gh-payloads.md +0 -75
  284. package/_shared/pr-loop/post-audit-thread-contract.md +0 -51
  285. package/_shared/pr-loop/precatch-rubric.md +0 -66
  286. package/_shared/pr-loop/preflight-proposal.md +0 -56
  287. package/_shared/pr-loop/scripts/post_audit_thread.py +0 -1242
  288. package/_shared/pr-loop/scripts/pr_loop_shared_constants/post_audit_thread_constants.py +0 -199
  289. package/_shared/pr-loop/scripts/tests/test_post_audit_thread.py +0 -1115
  290. package/_shared/pr-loop/scripts/tests/test_post_audit_thread_constants.py +0 -129
  291. package/_shared/pr-loop/state-schema.md +0 -45
  292. package/_shared/pr-loop/worker-spawn.md +0 -189
  293. /package/{.agents/skills/_shared/pr-loop/scripts → scripts}/test_durable_post_lint.py +0 -0
@@ -32,6 +32,8 @@ const INSTALLER_PATH = join(THIS_DIRECTORY, 'install.mjs');
32
32
  const PACKAGE_DIRECTORY = dirname(THIS_DIRECTORY);
33
33
  const SHIPPED_SKILL_NAME = 'privacy-hygiene';
34
34
  const ELI5_SKILL_NAME = 'eli5';
35
+ const E_CODE_REVIEW_SKILL_NAME = 'e-code-review';
36
+ const REFERENCE_DIRECTORY_NAME = 'reference';
35
37
  const SHIPPED_AGENT_FILE_NAME = 'clean-coder.md';
36
38
  const SHIPPED_AGENT_FILE_NAMES = [
37
39
  SHIPPED_AGENT_FILE_NAME,
@@ -69,10 +71,7 @@ const CLEAN_CODER_POLICY_REFERENCES = [
69
71
  ],
70
72
  ];
71
73
  const PERSONAL_SKILL_NAME = 'my-notes';
72
- const SHARED_DIRECTORY_NAME = '_shared';
73
- const PR_LOOP_DIRECTORY_NAME = 'pr-loop';
74
74
  const PREFLIGHT_PROPOSAL_FILE_NAME = 'preflight-proposal.md';
75
- const PREFLIGHT_PROPOSAL_POINTER = '@~/.claude/_shared/pr-loop/preflight-proposal.md';
76
75
 
77
76
  /**
78
77
  * @param {string} homeDirectory
@@ -100,20 +99,23 @@ function runInstaller(homeDirectory, extraArguments, environmentOverrides = {})
100
99
  }
101
100
 
102
101
  /**
103
- * @param {{ homeDirectory: string, skillsInstallDirectory: string }} installationPaths
102
+ * @param {{ skillsInstallDirectory: string }} installationPaths
104
103
  */
105
104
  function assertProposalContractInstallation(installationPaths) {
106
- const { homeDirectory, skillsInstallDirectory } = installationPaths;
105
+ const { skillsInstallDirectory } = installationPaths;
107
106
  const allContractPathSegments = [
108
- SHARED_DIRECTORY_NAME, PR_LOOP_DIRECTORY_NAME, PREFLIGHT_PROPOSAL_FILE_NAME,
107
+ E_CODE_REVIEW_SKILL_NAME,
108
+ REFERENCE_DIRECTORY_NAME,
109
+ PREFLIGHT_PROPOSAL_FILE_NAME,
109
110
  ];
110
- const installedPointerPath = join(skillsInstallDirectory, ...allContractPathSegments);
111
- const pointerLine = readFileSync(installedPointerPath, 'utf8').trimEnd().split('\n').at(-1);
112
- const allTargetSegments = pointerLine.slice('@~/'.length).split('/');
113
- const installedContractPath = join(homeDirectory, ...allTargetSegments);
114
- const sourceContractPath = join(PACKAGE_DIRECTORY, ...allContractPathSegments);
111
+ const installedContractPath = join(skillsInstallDirectory, ...allContractPathSegments);
112
+ const sourceSkillsDirectory = resolvePackageManagedDirectory(
113
+ PACKAGE_DIRECTORY,
114
+ MANAGED_SKILLS_DIRECTORY_NAME,
115
+ );
116
+ const sourceContractPath = join(sourceSkillsDirectory, ...allContractPathSegments);
115
117
 
116
- assert.equal(pointerLine, PREFLIGHT_PROPOSAL_POINTER);
118
+ assert.equal(existsSync(installedContractPath), true);
117
119
  assert.equal(readFileSync(installedContractPath, 'utf8'), readFileSync(sourceContractPath, 'utf8'));
118
120
  }
119
121
 
@@ -197,7 +199,7 @@ test('a full install writes skills and agents under .agents and points .claude a
197
199
  );
198
200
  assert.equal(realpathSync(lookupSkillFile), realpathSync(canonicalSkillFile));
199
201
  assert.equal(realpathSync(lookupEli5SkillFile), realpathSync(canonicalEli5SkillFile));
200
- assertProposalContractInstallation({ homeDirectory, skillsInstallDirectory });
202
+ assertProposalContractInstallation({ skillsInstallDirectory });
201
203
  assert.equal(
202
204
  readFileSync(lookupAgentFile, 'utf8'),
203
205
  readFileSync(canonicalAgentFile, 'utf8'),
package/bin/install.mjs CHANGED
@@ -35,6 +35,11 @@ import {
35
35
  WINDOWS_PYTHON_LAUNCHER_COMMAND,
36
36
  PYTHON_PROBE_TIMEOUT_MILLISECONDS,
37
37
  } from './install-constants.mjs';
38
+ import {
39
+ PSTACK_PLUGIN_DIRECTORY_NAME,
40
+ PSTACK_PLUGIN_MANIFEST_RELATIVE_PATH,
41
+ refreshPstackPluginManifest,
42
+ } from '../scripts/refresh_pstack_plugin_skills.mjs';
38
43
  import {
39
44
  resolveInstallRoot,
40
45
  parseExplicitTargetFromArgv,
@@ -429,6 +434,26 @@ export function runCursorRuleSync(pythonCommand, scriptPath, claudeRoot, cursorR
429
434
  );
430
435
  }
431
436
 
437
+ /**
438
+ * Refresh the pstack plugin manifest Claude Code reads, when this home has one.
439
+ *
440
+ * Claude Code loads a skills-directory folder as a plugin when it holds
441
+ * `.claude-plugin/plugin.json`, and namespaces its skills as `pstack:how`.
442
+ * Without the manifest, a folder of skill folders loads nothing.
443
+ *
444
+ * @param {string} [skillsRoot] The skills root to read. Defaults to the root
445
+ * this run installs into.
446
+ * @returns {string|null} The manifest path when this run wrote it, else null.
447
+ */
448
+ export function refreshInstalledPstackPluginManifest(
449
+ skillsRoot = join(CLAUDE_HOME, MANAGED_SKILLS_DIRECTORY_NAME),
450
+ ) {
451
+ const pluginRoot = join(skillsRoot, PSTACK_PLUGIN_DIRECTORY_NAME);
452
+ const outcome = refreshPstackPluginManifest(pluginRoot);
453
+ if (!outcome.didWrite) return null;
454
+ return join(pluginRoot, PSTACK_PLUGIN_MANIFEST_RELATIVE_PATH);
455
+ }
456
+
432
457
  function seedCodexPstackPreferences() {
433
458
  const preferenceDirectory = join(AGENTS_HOME, CURSOR_RULES_DIRECTORY_NAME);
434
459
  const preferencePath = join(
@@ -2320,6 +2345,9 @@ function executeInstallPlanMutations(plan, transactionHelpers) {
2320
2345
  }
2321
2346
  summary.skills = { created: skillsCreated, updated: skillsUpdated, pruned: 0, paths: skillPaths };
2322
2347
  allInstalledFiles.push(...skillPaths);
2348
+ const pstackManifestPath = refreshInstalledPstackPluginManifest();
2349
+ if (pstackManifestPath) allInstalledFiles.push(pstackManifestPath);
2350
+ summary.pstackPlugin = { manifestPath: pstackManifestPath };
2323
2351
  syncWrittenPaths([...allInstalledFiles, ...publishedPointerPaths]);
2324
2352
  throwIfFault(FAULT_PHASES.AFTER_FILE_STAGING);
2325
2353
  throwIfFault(FAULT_PHASES.BEFORE_DURABLE_PROMOTION);
@@ -0,0 +1,78 @@
1
+ import { test } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { readFileSync } from 'node:fs';
4
+ import {
5
+ FOLDED_HOOK_RELATIVE_PATHS,
6
+ mergeHooksIntoSettings,
7
+ } from './install.mjs';
8
+
9
+ const allRetiredWriteHooks = [
10
+ 'blocking/code_rules_enforcer.py',
11
+ 'blocking/tdd_enforcer.py',
12
+ 'blocking/windows_rmtree_blocker.py',
13
+ 'blocking/state_description_blocker.py',
14
+ 'blocking/subprocess_budget_completeness.py',
15
+ 'blocking/hook_prose_detector_consistency.py',
16
+ 'blocking/workflow_substitution_slot_blocker.py',
17
+ ];
18
+ const sourceHooks = JSON.parse(readFileSync(new URL('../hooks/hooks.json', import.meta.url), 'utf8'));
19
+ const installedRoot = '/fixture-home/.claude';
20
+ const foreignCommand = 'python /unmanaged/keep_this_hook.py';
21
+ const foreignSessionCommand = 'node /unmanaged/session.js';
22
+
23
+ function legacySettings() {
24
+ return {
25
+ permissions: { allow: ['Read'] },
26
+ hooks: {
27
+ PreToolUse: [{
28
+ matcher: 'Write|Edit|MultiEdit|apply_patch',
29
+ hooks: [
30
+ { type: 'command', command: foreignCommand, timeout: 17 },
31
+ ...allRetiredWriteHooks.map(relativePath => ({
32
+ type: 'command',
33
+ command: `python ${installedRoot}/hooks/${relativePath}`,
34
+ timeout: 10,
35
+ })),
36
+ ],
37
+ }],
38
+ SessionStart: [{
39
+ matcher: '',
40
+ hooks: [{ type: 'command', command: foreignSessionCommand, timeout: 9 }],
41
+ }],
42
+ },
43
+ };
44
+ }
45
+
46
+ function allCommands(settings) {
47
+ return Object.values(settings.hooks).flatMap(groups =>
48
+ groups.flatMap(group => group.hooks.map(hook => hook.command)));
49
+ }
50
+
51
+ test('every retired write registration already has an installer-owned prune path', () => {
52
+ for (const relativePath of allRetiredWriteHooks) {
53
+ assert.ok(FOLDED_HOOK_RELATIVE_PATHS.has(relativePath), relativePath);
54
+ }
55
+ });
56
+
57
+ test('upgrade prunes legacy direct file checks while retaining foreign hooks', () => {
58
+ const settings = legacySettings();
59
+ mergeHooksIntoSettings(settings, sourceHooks, installedRoot, 'python3');
60
+ const commands = allCommands(settings);
61
+ for (const relativePath of allRetiredWriteHooks) {
62
+ assert.ok(!commands.some(command => command.includes(relativePath)), relativePath);
63
+ }
64
+ assert.ok(commands.includes(foreignCommand));
65
+ assert.ok(commands.includes(foreignSessionCommand));
66
+ assert.deepEqual(settings.permissions, { allow: ['Read'] });
67
+ assert.ok(commands.some(command => command.includes('/blocking/pre_tool_use_dispatcher.py')));
68
+ assert.ok(commands.some(command => command.includes('/blocking/bash_pre_tool_use_dispatcher.py')));
69
+ });
70
+
71
+ test('reinstall does not restore retired direct checks or duplicate foreign hooks', () => {
72
+ const settings = legacySettings();
73
+ mergeHooksIntoSettings(settings, sourceHooks, installedRoot, 'python3');
74
+ const firstInstall = structuredClone(settings);
75
+ mergeHooksIntoSettings(settings, sourceHooks, installedRoot, 'python3');
76
+ assert.deepEqual(settings, firstInstall);
77
+ assert.equal(allCommands(settings).filter(command => command === foreignCommand).length, 1);
78
+ });
@@ -25,16 +25,43 @@ const RETIRED_SKILL_DIRECTORIES = [
25
25
  'post-audit-findings',
26
26
  'pr-consistency-audit',
27
27
  'bdd-protocol',
28
+ 'hitl',
29
+ 'autoconverge',
30
+ 'pr-cleanup',
31
+ 'pr-name-by-capability',
32
+ 'pr-plain-language-cleanup',
33
+ 'pr-refinement',
34
+ 'pr-shared-extraction',
35
+ 'pr-small-cl',
36
+ 'pr-title-description',
37
+ 'prototype',
38
+ 'rebase',
39
+ 'review-router',
40
+ 'review-tier',
41
+ 'run-claude-dev-env',
42
+ 'session-log',
43
+ 'session-tidy',
44
+ 'source-command-sr-loop',
45
+ 'update',
28
46
  ];
29
47
  const PERSONAL_SKILL_DIRECTORIES = ['credit-card-picker', 'midjourney-sref'];
30
- const SHIPPED_SKILL_DIRECTORY = 'autoconverge';
48
+ const SHIPPED_SKILL_DIRECTORY = 'e-code-review';
31
49
  const PRUNED_BACKUP_DIRECTORY_NAME = '.claude-dev-env-pruned';
32
50
  const SKIP_PRUNE_NOTICE_MARKER = 'Skipping retired-skill and stale-file prune';
33
51
  const STALE_SKILL_FILE_RELATIVE_SEGMENTS = ['scripts', 'retired_module.py'];
34
52
  const RUNTIME_ARTIFACT_RELATIVE_SEGMENTS = ['scripts', '__pycache__', 'helper.cpython-312.pyc'];
35
53
  const SCOPED_GROUP_SKILL_DIRECTORY = 'orchestrator';
36
- const CORE_REVIEW_GUIDE_SKILL_DIRECTORIES = [
37
- 'pr-small-cl',
54
+ const CORE_SKILL_DIRECTORIES = [
55
+ 'orchestrator',
56
+ 'orchestrator-refresh',
57
+ 'team-advisor',
58
+ 'grok-spawn',
59
+ 'everything-search',
60
+ 'test-runner',
61
+ 'privacy-hygiene',
62
+ 'issue-tracker',
63
+ 'task-build',
64
+ 'eli5',
38
65
  ];
39
66
  const PRIOR_RUN_BACKUP_DIRECTORY_NAMES = [
40
67
  '2020-01-01T00-00-00-000Z',
@@ -693,25 +720,33 @@ test('a scoped --only install leaves retired skills in place because prune runs
693
720
  }
694
721
  });
695
722
 
696
- test('a full reinstall keeps autoconverge because the package ships it again', () => {
723
+ test('a full reinstall keeps the skill-builder stub and archives its obsolete support files', () => {
697
724
  const sandbox = createSandbox();
698
725
  try {
699
- plantSkillDirectory(sandbox.skillsDirectory, 'autoconverge', true);
700
- writeFileSync(join(sandbox.skillsDirectory, 'autoconverge', 'SKILL.md'), 'stale seeded copy\n');
726
+ const skillName = 'skill-builder';
727
+ plantSkillDirectory(sandbox.skillsDirectory, skillName, true);
728
+ const stubPath = join(sandbox.skillsDirectory, skillName, 'SKILL.md');
729
+ const obsoleteReferencePath = join(sandbox.skillsDirectory, skillName, 'references', 'retired.md');
730
+ writeFileWithParents(obsoleteReferencePath, 'obsolete skill-builder guidance\n');
731
+ writeFileSync(sandbox.manifestPath, JSON.stringify({
732
+ package: 'claude-dev-env',
733
+ version: '0.0.0',
734
+ installedAt: new Date().toISOString(),
735
+ files: [stubPath, obsoleteReferencePath],
736
+ skills: [skillName],
737
+ }, null, 2) + '\n');
701
738
 
702
739
  const installerOutput = runInstaller(sandbox.homeDirectory, []);
703
740
 
741
+ assert.equal(installerOutput.includes(SKIP_PRUNE_NOTICE_MARKER), false);
742
+ assert.equal(existsSync(stubPath), true, 'the requested stub stays installed');
743
+ assert.match(readFileSync(stubPath, 'utf8'), /TODO: Rework to follow pstack philosophy\./);
744
+ assert.ok(readManifest(sandbox.manifestPath).skills.includes(skillName));
745
+ assert.equal(existsSync(obsoleteReferencePath), false);
704
746
  assert.equal(
705
- installerOutput.includes(SKIP_PRUNE_NOTICE_MARKER),
706
- false,
707
- 'the full install runs the prune rather than skipping it',
708
- );
709
- const restoredSkillPath = join(sandbox.skillsDirectory, 'autoconverge', 'SKILL.md');
710
- assert.equal(existsSync(restoredSkillPath), true, 'autoconverge survives and is reinstalled');
711
- assert.notEqual(
712
- readFileSync(restoredSkillPath, 'utf8'),
713
- 'stale seeded copy\n',
714
- 'the shipped autoconverge overwrites the stale seeded copy',
747
+ prunedSkillBackupContains(sandbox.claudeDirectory, join(skillName, 'references', 'retired.md')),
748
+ true,
749
+ 'old implementation files move to the recovery backup while the stub stays',
715
750
  );
716
751
  } finally {
717
752
  rmSync(sandbox.homeDirectory, { recursive: true, force: true });
@@ -739,20 +774,24 @@ test('a scoped --only install records the skill names it installed', () => {
739
774
  }
740
775
  });
741
776
 
742
- test('a scoped core install ships the canonical review guide skills', () => {
777
+ test('a fresh scoped core install ships active skills and omits retired skills', () => {
743
778
  const sandbox = createSandbox();
744
779
  try {
745
780
  runInstaller(sandbox.homeDirectory, ['--only', 'core']);
746
781
 
747
- const missingGuideSkillNames = CORE_REVIEW_GUIDE_SKILL_DIRECTORIES.filter(
782
+ const missingSkillNames = CORE_SKILL_DIRECTORIES.filter(
748
783
  eachSkillName => !existsSync(join(sandbox.skillsDirectory, eachSkillName, 'SKILL.md')),
749
784
  );
750
- assert.deepEqual(missingGuideSkillNames, []);
785
+ assert.deepEqual(missingSkillNames, []);
751
786
 
752
787
  const recordedSkillNames = new Set(readManifest(sandbox.manifestPath).skills);
753
- for (const eachSkillName of CORE_REVIEW_GUIDE_SKILL_DIRECTORIES) {
788
+ for (const eachSkillName of CORE_SKILL_DIRECTORIES) {
754
789
  assert.equal(recordedSkillNames.has(eachSkillName), true);
755
790
  }
791
+ for (const eachSkillName of RETIRED_SKILL_DIRECTORIES) {
792
+ assert.equal(existsSync(join(sandbox.skillsDirectory, eachSkillName)), false);
793
+ assert.equal(recordedSkillNames.has(eachSkillName), false);
794
+ }
756
795
  } finally {
757
796
  rmSync(sandbox.homeDirectory, { recursive: true, force: true });
758
797
  }
@@ -0,0 +1,114 @@
1
+ import { test } from 'node:test';
2
+ import { strict as assert } from 'node:assert';
3
+ import {
4
+ mkdtempSync,
5
+ mkdirSync,
6
+ writeFileSync,
7
+ readFileSync,
8
+ existsSync,
9
+ rmSync,
10
+ } from 'node:fs';
11
+ import { tmpdir } from 'node:os';
12
+ import { dirname, join } from 'node:path';
13
+ import { fileURLToPath } from 'node:url';
14
+ import { execFileSync } from 'node:child_process';
15
+ import { resolveInstallRoot } from './resolve-install-root.mjs';
16
+ import { PSTACK_PLUGIN_MANIFEST_RELATIVE_PATH } from '../scripts/refresh_pstack_plugin_skills.mjs';
17
+
18
+ const THIS_DIRECTORY = dirname(fileURLToPath(import.meta.url));
19
+ const INSTALLER_PATH = join(THIS_DIRECTORY, 'install.mjs');
20
+ const PACKAGE_DIRECTORY = dirname(THIS_DIRECTORY);
21
+
22
+ function runInstaller(homeDirectory, extraArguments) {
23
+ return execFileSync('node', [INSTALLER_PATH, ...extraArguments], {
24
+ cwd: PACKAGE_DIRECTORY,
25
+ encoding: 'utf8',
26
+ env: {
27
+ ...process.env,
28
+ HOME: homeDirectory,
29
+ USERPROFILE: homeDirectory,
30
+ CODEX_HOME: join(homeDirectory, '.codex'),
31
+ CLAUDE_CONFIG_DIR: join(homeDirectory, '.claude'),
32
+ GIT_CONFIG_GLOBAL: join(homeDirectory, '.gitconfig'),
33
+ },
34
+ });
35
+ }
36
+
37
+ function seedPstackSkills(agentsHome, skillNames) {
38
+ const pluginRoot = join(agentsHome, 'skills', 'pstack');
39
+ for (const eachSkillName of skillNames) {
40
+ const skillDirectory = join(pluginRoot, eachSkillName);
41
+ mkdirSync(skillDirectory, { recursive: true });
42
+ writeFileSync(join(skillDirectory, 'SKILL.md'), '---\nname: ' + eachSkillName + '\n---\n');
43
+ }
44
+ return pluginRoot;
45
+ }
46
+
47
+ function withTemporaryHome(runAssertions) {
48
+ const homeDirectory = mkdtempSync(join(tmpdir(), 'cdev-pstack-plugin-'));
49
+ try {
50
+ const resolution = resolveInstallRoot({
51
+ homeDirectory,
52
+ environment: {},
53
+ explicitTarget: null,
54
+ });
55
+ runAssertions(homeDirectory, resolution);
56
+ } finally {
57
+ rmSync(homeDirectory, { recursive: true, force: true });
58
+ }
59
+ }
60
+
61
+ test('a pstack folder the user installed gains a plugin manifest', () => {
62
+ withTemporaryHome((homeDirectory, resolution) => {
63
+ const pluginRoot = seedPstackSkills(resolution.agentsHome, ['how', 'why']);
64
+
65
+ runInstaller(homeDirectory, ['--only', 'core']);
66
+
67
+ const manifestPath = join(pluginRoot, PSTACK_PLUGIN_MANIFEST_RELATIVE_PATH);
68
+ assert.equal(existsSync(manifestPath), true);
69
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
70
+ assert.equal(manifest.name, 'pstack');
71
+ assert.deepEqual(manifest.skills, ['./how', './why']);
72
+ });
73
+ });
74
+
75
+ test('a second install leaves an unchanged manifest byte for byte', () => {
76
+ withTemporaryHome((homeDirectory, resolution) => {
77
+ const pluginRoot = seedPstackSkills(resolution.agentsHome, ['how']);
78
+
79
+ runInstaller(homeDirectory, ['--only', 'core']);
80
+ const manifestPath = join(pluginRoot, PSTACK_PLUGIN_MANIFEST_RELATIVE_PATH);
81
+ const afterFirstInstall = readFileSync(manifestPath, 'utf8');
82
+ runInstaller(homeDirectory, ['--only', 'core']);
83
+
84
+ assert.equal(readFileSync(manifestPath, 'utf8'), afterFirstInstall);
85
+ });
86
+ });
87
+
88
+ test('a later install lists a skill a pstack update added', () => {
89
+ withTemporaryHome((homeDirectory, resolution) => {
90
+ const pluginRoot = seedPstackSkills(resolution.agentsHome, ['how']);
91
+ runInstaller(homeDirectory, ['--only', 'core']);
92
+
93
+ seedPstackSkills(resolution.agentsHome, ['why']);
94
+ runInstaller(homeDirectory, ['--only', 'core']);
95
+
96
+ const manifestPath = join(pluginRoot, PSTACK_PLUGIN_MANIFEST_RELATIVE_PATH);
97
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
98
+ assert.deepEqual(manifest.skills, ['./how', './why']);
99
+ });
100
+ });
101
+
102
+ test('an install with no pstack folder writes no manifest and does not fail', () => {
103
+ withTemporaryHome((homeDirectory, resolution) => {
104
+ runInstaller(homeDirectory, ['--only', 'core']);
105
+
106
+ const manifestPath = join(
107
+ resolution.agentsHome,
108
+ 'skills',
109
+ 'pstack',
110
+ PSTACK_PLUGIN_MANIFEST_RELATIVE_PATH,
111
+ );
112
+ assert.equal(existsSync(manifestPath), false);
113
+ });
114
+ });
@@ -43,6 +43,7 @@ import {
43
43
  retiredManagedHookRelativePaths,
44
44
  pruneRetiredHookEntriesFromSettings,
45
45
  retainNewestRunBackupOnly,
46
+ refreshInstalledPstackPluginManifest,
46
47
  } from './install.mjs';
47
48
  import { EVER_SHIPPED_SKILL_NAMES } from './ever-shipped-skills.mjs';
48
49
  import {
@@ -2541,3 +2542,39 @@ test('pruneStaleInstalledFiles leaves the run backup root standing when the rena
2541
2542
  rmSync(sandbox.root, { recursive: true, force: true });
2542
2543
  }
2543
2544
  });
2545
+
2546
+ test('the pstack step writes a manifest for a skills root that carries pstack', () => {
2547
+ const skillsRoot = mkdtempSync(join(tmpdir(), 'cdev-pstack-step-'));
2548
+ try {
2549
+ const skillDirectory = join(skillsRoot, 'pstack', 'how');
2550
+ mkdirSync(skillDirectory, { recursive: true });
2551
+ writeFileSync(join(skillDirectory, 'SKILL.md'), '---\nname: how\n---\n');
2552
+
2553
+ const manifestPath = refreshInstalledPstackPluginManifest(skillsRoot);
2554
+
2555
+ assert.equal(
2556
+ manifestPath,
2557
+ join(skillsRoot, 'pstack', '.claude-plugin', 'plugin.json'),
2558
+ 'the step reports the manifest it wrote so the run records the path',
2559
+ );
2560
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
2561
+ assert.deepEqual(manifest.skills, ['./how']);
2562
+ assert.equal(
2563
+ refreshInstalledPstackPluginManifest(skillsRoot),
2564
+ null,
2565
+ 'a second run writes nothing and reports no path',
2566
+ );
2567
+ } finally {
2568
+ rmSync(skillsRoot, { recursive: true, force: true });
2569
+ }
2570
+ });
2571
+
2572
+ test('the pstack step reports no path for a skills root without pstack', () => {
2573
+ const skillsRoot = mkdtempSync(join(tmpdir(), 'cdev-pstack-absent-'));
2574
+ try {
2575
+ assert.equal(refreshInstalledPstackPluginManifest(skillsRoot), null);
2576
+ assert.equal(existsSync(join(skillsRoot, 'pstack')), false);
2577
+ } finally {
2578
+ rmSync(skillsRoot, { recursive: true, force: true });
2579
+ }
2580
+ });