claude-dev-env 1.93.1 → 1.95.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 (262) hide show
  1. package/_shared/advisor/CLAUDE.md +2 -2
  2. package/_shared/advisor/advisor-protocol.md +35 -27
  3. package/_shared/advisor/scripts/config/advisor_scripts_constants/model_tier_run_validator_constants.py +3 -2
  4. package/_shared/advisor/scripts/model_tier_run_validator.py +23 -15
  5. package/_shared/advisor/scripts/tests/test_model_tier_run_validator.py +81 -17
  6. package/_shared/pr-loop/CLAUDE.md +1 -0
  7. package/_shared/pr-loop/audit-contract.md +1 -1
  8. package/_shared/pr-loop/gh-payloads.md +3 -3
  9. package/_shared/pr-loop/post-audit-thread-contract.md +51 -0
  10. package/_shared/pr-loop/scripts/README.md +2 -2
  11. package/_shared/pr-loop/scripts/fix_hookspath.py +47 -12
  12. package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +0 -69
  13. package/_shared/pr-loop/scripts/tests/test_fix_hookspath.py +497 -374
  14. package/_shared/pr-loop/state-schema.md +3 -22
  15. package/bin/CLAUDE.md +11 -2
  16. package/bin/ever-shipped-skills.mjs +70 -0
  17. package/bin/install.mjs +138 -13
  18. package/bin/install.prune.test.mjs +457 -0
  19. package/bin/install.test.mjs +10 -4
  20. package/docs/CODE_RULES.md +1 -1
  21. package/hooks/blocking/CLAUDE.md +2 -0
  22. package/hooks/blocking/code_rules_enforcer.py +4 -0
  23. package/hooks/blocking/code_rules_shared.py +82 -0
  24. package/hooks/blocking/code_rules_test_layout.py +9 -3
  25. package/hooks/blocking/pii_commit_command.py +398 -0
  26. package/hooks/blocking/pii_payload_scan.py +133 -0
  27. package/hooks/blocking/pii_prevention_blocker.py +151 -449
  28. package/hooks/blocking/plain_language_blocker.py +138 -4
  29. package/hooks/blocking/sensitive_file_protector.py +114 -48
  30. package/hooks/blocking/tdd_enforcer.py +9 -2
  31. package/hooks/blocking/test_code_rules_enforcer_scratchpad.py +105 -0
  32. package/hooks/blocking/test_code_rules_shared.py +181 -0
  33. package/hooks/blocking/test_pii_prevention_blocker.py +299 -0
  34. package/hooks/blocking/test_plain_language_blocker_allowlist.py +184 -0
  35. package/hooks/blocking/test_sensitive_file_protector.py +185 -0
  36. package/hooks/blocking/test_tdd_enforcer_scratchpad.py +105 -0
  37. package/hooks/blocking/test_verdict_directory_write_blocker.py +42 -7
  38. package/hooks/hooks_constants/CLAUDE.md +4 -2
  39. package/hooks/hooks_constants/code_rules_path_utils_constants.py +0 -2
  40. package/hooks/hooks_constants/harness_scratchpad_constants.py +17 -0
  41. package/hooks/hooks_constants/local_identity.py +65 -15
  42. package/hooks/hooks_constants/pii_prevention_constants.py +16 -0
  43. package/hooks/hooks_constants/plain_language_blocker_constants.py +5 -0
  44. package/hooks/hooks_constants/pr_description_enforcer_constants.py +1 -1
  45. package/hooks/hooks_constants/sensitive_file_protector_constants.py +42 -0
  46. package/hooks/hooks_constants/test_local_identity.py +68 -0
  47. package/hooks/pyproject.toml +75 -4
  48. package/hooks/validators/CLAUDE.md +1 -1
  49. package/hooks/validators/README.md +2 -0
  50. package/hooks/validators/python_style_checks.py +114 -136
  51. package/hooks/validators/python_style_helpers.py +95 -0
  52. package/hooks/validators/test_python_style_checks.py +0 -164
  53. package/hooks/validators/test_python_style_checks_decorator_gap.py +119 -0
  54. package/hooks/validators/test_python_style_fixes.py +251 -0
  55. package/hooks/validators/test_python_style_helpers.py +125 -0
  56. package/package.json +1 -1
  57. package/rules/CLAUDE.md +1 -0
  58. package/rules/anti-corollary-tests.md +69 -0
  59. package/rules/bdd.md +1 -3
  60. package/rules/code-reviews.md +1 -1
  61. package/rules/gh-paginate.md +1 -1
  62. package/rules/plain-language.md +2 -0
  63. package/scripts/CLAUDE.md +10 -1
  64. package/scripts/Get-SessionAccount.ps1 +210 -0
  65. package/scripts/tests/CLAUDE.md +15 -1
  66. package/scripts/tests/Get-SessionAccount.Tests.ps1 +350 -0
  67. package/skills/CLAUDE.md +6 -27
  68. package/skills/anthropic-plan/SKILL.md +1 -13
  69. package/skills/autoconverge/CLAUDE.md +5 -2
  70. package/skills/autoconverge/SKILL.md +112 -387
  71. package/skills/autoconverge/reference/CLAUDE.md +4 -1
  72. package/skills/autoconverge/reference/closing-report.md +9 -2
  73. package/skills/autoconverge/reference/convergence.md +26 -22
  74. package/skills/autoconverge/reference/headless-safety.md +44 -0
  75. package/skills/autoconverge/reference/multi-pr.md +88 -0
  76. package/skills/autoconverge/reference/self-closing-loop.md +84 -0
  77. package/skills/autoconverge/reference/stop-conditions.md +21 -13
  78. package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +87 -27
  79. package/skills/autoconverge/workflow/converge.contract.test.mjs +40 -10
  80. package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +179 -47
  81. package/skills/autoconverge/workflow/converge.fix-progress.test.mjs +1 -1
  82. package/skills/autoconverge/workflow/converge.mjs +286 -96
  83. package/skills/bugteam/CLAUDE.md +1 -1
  84. package/skills/bugteam/CONSTRAINTS.md +3 -3
  85. package/skills/bugteam/PROMPTS.md +8 -19
  86. package/skills/bugteam/SKILL.md +13 -14
  87. package/skills/bugteam/reference/CLAUDE.md +1 -1
  88. package/skills/bugteam/reference/README.md +2 -3
  89. package/skills/bugteam/reference/audit-and-teammates.md +2 -2
  90. package/skills/bugteam/reference/copilot-gap-analysis.md +2 -0
  91. package/skills/bugteam/reference/github-pr-reviews.md +56 -38
  92. package/skills/bugteam/reference/team-setup.md +11 -11
  93. package/skills/bugteam/scripts/CLAUDE.md +6 -6
  94. package/skills/bugteam/scripts/README.md +20 -8
  95. package/skills/bugteam/scripts/bugteam_code_rules_gate.py +13 -1463
  96. package/skills/bugteam/scripts/bugteam_fix_hookspath.py +23 -358
  97. package/skills/bugteam/scripts/bugteam_preflight.py +13 -315
  98. package/skills/bugteam/scripts/bugteam_scripts_constants/CLAUDE.md +2 -3
  99. package/skills/bugteam/scripts/test_bugteam_code_rules_gate.py +34 -1053
  100. package/skills/bugteam/scripts/test_bugteam_fix_hookspath.py +30 -347
  101. package/skills/bugteam/scripts/test_bugteam_preflight.py +30 -305
  102. package/skills/closeout/SKILL.md +153 -0
  103. package/skills/closeout/reference/handoff-prompt-template.md +72 -0
  104. package/skills/closeout/reference/issue-body-templates.md +108 -0
  105. package/skills/closeout/reference/pii-redaction-checklist.md +36 -0
  106. package/skills/copilot-finding-triage/SKILL.md +80 -28
  107. package/skills/copilot-finding-triage/reference/tier-rubric.md +70 -11
  108. package/skills/copilot-finding-triage/templates/notification.md +8 -3
  109. package/skills/everything-search/SKILL.md +1 -2
  110. package/skills/grokify/SKILL.md +58 -0
  111. package/skills/grokify/templates/handoff-template.md +92 -0
  112. package/skills/orchestrator/SKILL.md +35 -25
  113. package/skills/orchestrator-refresh/SKILL.md +15 -11
  114. package/skills/pr-converge/CLAUDE.md +1 -1
  115. package/skills/pr-converge/SKILL.md +132 -233
  116. package/skills/pr-converge/reference/convergence-gates.md +102 -66
  117. package/skills/pr-converge/reference/examples.md +37 -25
  118. package/skills/pr-converge/reference/fix-protocol.md +7 -7
  119. package/skills/pr-converge/reference/multi-pr-orchestration.md +10 -4
  120. package/skills/pr-converge/reference/per-tick.md +64 -41
  121. package/skills/pr-converge/reference/state-schema.md +27 -2
  122. package/skills/pr-converge/scripts/CLAUDE.md +8 -0
  123. package/skills/pr-converge/scripts/README.md +4 -3
  124. package/skills/pr-converge/scripts/_pr_converge_path_setup.py +23 -0
  125. package/skills/pr-converge/scripts/check_convergence.py +189 -645
  126. package/skills/pr-converge/scripts/check_convergence_gates.py +235 -0
  127. package/skills/pr-converge/scripts/check_convergence_thread_gates.py +219 -0
  128. package/skills/pr-converge/scripts/conftest.py +8 -0
  129. package/skills/pr-converge/scripts/pr_converge_scripts_constants/CLAUDE.md +1 -0
  130. package/skills/pr-converge/scripts/pr_converge_scripts_constants/convergence_gate_constants.py +36 -0
  131. package/skills/pr-converge/scripts/test__pr_converge_path_setup.py +26 -0
  132. package/skills/pr-converge/scripts/test_check_convergence.py +73 -228
  133. package/skills/pr-converge/scripts/test_check_convergence_contract.py +321 -0
  134. package/skills/pr-converge/scripts/test_check_convergence_gates.py +68 -0
  135. package/skills/pr-converge/scripts/test_check_convergence_thread_gates.py +75 -0
  136. package/skills/pr-fix-protocol/SKILL.md +20 -20
  137. package/skills/privacy-hygiene/SKILL.md +4 -3
  138. package/skills/reviewer-gates/SKILL.md +26 -8
  139. package/skills/session-log/SKILL.md +11 -12
  140. package/skills/session-tidy/SKILL.md +40 -30
  141. package/skills/skill-builder/CLAUDE.md +13 -10
  142. package/skills/skill-builder/SKILL.md +31 -31
  143. package/skills/skill-builder/references/CLAUDE.md +10 -6
  144. package/skills/skill-builder/references/delegation-map.md +13 -4
  145. package/skills/skill-builder/references/description-field.md +113 -0
  146. package/skills/skill-builder/references/self-audit-checklist.md +21 -4
  147. package/skills/skill-builder/references/skill-modularity.md +113 -0
  148. package/skills/skill-builder/references/skill-types.md +4 -0
  149. package/skills/skill-builder/templates/CLAUDE.md +4 -4
  150. package/skills/skill-builder/templates/gap-analysis.md +25 -1
  151. package/skills/skill-builder/workflows/CLAUDE.md +6 -4
  152. package/skills/skill-builder/workflows/improve-skill.md +17 -16
  153. package/skills/skill-builder/workflows/new-skill.md +34 -10
  154. package/skills/skill-builder/workflows/polish-skill.md +59 -43
  155. package/skills/team-advisor/SKILL.md +15 -11
  156. package/skills/usage-pause/SKILL.md +10 -14
  157. package/system-prompts/software-engineer.xml +7 -6
  158. package/hooks/validators/test_verify_paths.py +0 -32
  159. package/hooks/validators/verify_paths.py +0 -57
  160. package/skills/bdd-protocol/CLAUDE.md +0 -26
  161. package/skills/bdd-protocol/SKILL.md +0 -30
  162. package/skills/bdd-protocol/references/CLAUDE.md +0 -10
  163. package/skills/bdd-protocol/references/anti-patterns.md +0 -26
  164. package/skills/bdd-protocol/references/example-mapping.md +0 -23
  165. package/skills/bg-agent/CLAUDE.md +0 -17
  166. package/skills/bg-agent/SKILL.md +0 -69
  167. package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_code_rules_gate_constants.py +0 -55
  168. package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_fix_hookspath_constants.py +0 -26
  169. package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_preflight_constants.py +0 -35
  170. package/skills/caveman/CLAUDE.md +0 -15
  171. package/skills/caveman/SKILL.md +0 -38
  172. package/skills/code/CLAUDE.md +0 -17
  173. package/skills/code/SKILL.md +0 -178
  174. package/skills/deep-research/CLAUDE.md +0 -17
  175. package/skills/deep-research/SKILL.md +0 -80
  176. package/skills/findbugs/CLAUDE.md +0 -20
  177. package/skills/findbugs/SKILL.md +0 -241
  178. package/skills/fixbugs/CLAUDE.md +0 -19
  179. package/skills/fixbugs/SKILL.md +0 -142
  180. package/skills/gh-paginate/CLAUDE.md +0 -18
  181. package/skills/gh-paginate/SKILL.md +0 -84
  182. package/skills/gotcha/CLAUDE.md +0 -33
  183. package/skills/gotcha/SKILL.md +0 -73
  184. package/skills/implement/CLAUDE.md +0 -27
  185. package/skills/implement/SKILL.md +0 -66
  186. package/skills/implement/scripts/CLAUDE.md +0 -22
  187. package/skills/implement/scripts/append_note.py +0 -133
  188. package/skills/implement/scripts/implement_scripts_constants/CLAUDE.md +0 -22
  189. package/skills/implement/scripts/implement_scripts_constants/__init__.py +0 -0
  190. package/skills/implement/scripts/implement_scripts_constants/notes_constants.py +0 -12
  191. package/skills/implement/scripts/test_append_note.py +0 -191
  192. package/skills/log-audit/CLAUDE.md +0 -20
  193. package/skills/log-audit/SKILL.md +0 -66
  194. package/skills/log-audit/reference/CLAUDE.md +0 -9
  195. package/skills/log-audit/reference/charter.md +0 -52
  196. package/skills/log-audit/scripts/CLAUDE.md +0 -27
  197. package/skills/log-audit/scripts/cluster_recurrences.py +0 -261
  198. package/skills/log-audit/scripts/collect_log_window.py +0 -199
  199. package/skills/log-audit/scripts/log_audit_constants/CLAUDE.md +0 -12
  200. package/skills/log-audit/scripts/log_audit_constants/__init__.py +0 -0
  201. package/skills/log-audit/scripts/log_audit_constants/cluster_recurrences_constants.py +0 -23
  202. package/skills/log-audit/scripts/log_audit_constants/collect_log_window_constants.py +0 -24
  203. package/skills/log-audit/scripts/log_audit_constants/mine_copilot_findings_constants.py +0 -49
  204. package/skills/log-audit/scripts/mine_copilot_findings.py +0 -302
  205. package/skills/log-audit/scripts/test_cluster_recurrences.py +0 -160
  206. package/skills/log-audit/scripts/test_collect_log_window.py +0 -111
  207. package/skills/log-audit/scripts/test_mine_copilot_findings.py +0 -126
  208. package/skills/logifix/CLAUDE.md +0 -36
  209. package/skills/logifix/SKILL.md +0 -69
  210. package/skills/logifix/scripts/CLAUDE.md +0 -16
  211. package/skills/logifix/scripts/logifix.ps1 +0 -205
  212. package/skills/monitor-open-prs/CLAUDE.md +0 -34
  213. package/skills/monitor-open-prs/SKILL.md +0 -88
  214. package/skills/monitor-open-prs/scripts/CLAUDE.md +0 -17
  215. package/skills/monitor-open-prs/scripts/discover_open_prs.py +0 -69
  216. package/skills/monitor-open-prs/scripts/test_discover_open_prs.py +0 -149
  217. package/skills/monitor-open-prs/test_skill_contract.py +0 -27
  218. package/skills/post-audit-findings/SKILL.md +0 -80
  219. package/skills/pr-consistency-audit/CLAUDE.md +0 -34
  220. package/skills/pr-consistency-audit/SKILL.md +0 -116
  221. package/skills/pr-consistency-audit/reference/CLAUDE.md +0 -16
  222. package/skills/pr-consistency-audit/reference/detection-rules.md +0 -96
  223. package/skills/pr-consistency-audit/reference/illustrations.md +0 -78
  224. package/skills/pr-review-responder/CLAUDE.md +0 -35
  225. package/skills/pr-review-responder/EXAMPLES.md +0 -590
  226. package/skills/pr-review-responder/PRINCIPLES.md +0 -533
  227. package/skills/pr-review-responder/README.md +0 -168
  228. package/skills/pr-review-responder/SKILL.md +0 -203
  229. package/skills/pr-review-responder/TESTING.md +0 -350
  230. package/skills/pr-scope-resolve/SKILL.md +0 -45
  231. package/skills/pre-compact/CLAUDE.md +0 -24
  232. package/skills/pre-compact/SKILL.md +0 -134
  233. package/skills/qbug/CLAUDE.md +0 -40
  234. package/skills/qbug/SKILL.md +0 -387
  235. package/skills/qbug/test_qbug_skill_audit_schema.py +0 -152
  236. package/skills/qbug/test_qbug_skill_post_fix_audit.py +0 -103
  237. package/skills/refine/CLAUDE.md +0 -44
  238. package/skills/refine/SKILL.md +0 -262
  239. package/skills/refine/templates/CLAUDE.md +0 -17
  240. package/skills/refine/templates/implementation-notes-template.html +0 -56
  241. package/skills/refine/templates/plan-template.md +0 -60
  242. package/skills/research-mode/CLAUDE.md +0 -35
  243. package/skills/research-mode/SKILL.md +0 -53
  244. package/skills/structure-prompt/CLAUDE.md +0 -42
  245. package/skills/structure-prompt/SKILL.md +0 -44
  246. package/skills/structure-prompt/reference/CLAUDE.md +0 -28
  247. package/skills/structure-prompt/reference/adversarial-tuning.md +0 -62
  248. package/skills/structure-prompt/reference/block-classification.md +0 -27
  249. package/skills/structure-prompt/reference/canonical-case.md +0 -48
  250. package/skills/structure-prompt/reference/citation-depth.md +0 -70
  251. package/skills/structure-prompt/reference/cleanup.md +0 -33
  252. package/skills/structure-prompt/reference/constraints.md +0 -33
  253. package/skills/structure-prompt/reference/directives.md +0 -37
  254. package/skills/structure-prompt/reference/examples.md +0 -72
  255. package/skills/structure-prompt/reference/instantiation.md +0 -51
  256. package/skills/structure-prompt/reference/output-contract.md +0 -72
  257. package/skills/structure-prompt/reference/per-category.md +0 -23
  258. package/skills/structure-prompt/reference/persona.md +0 -38
  259. package/skills/structure-prompt/reference/research.md +0 -33
  260. package/skills/structure-prompt/reference/structure.md +0 -28
  261. package/skills/verified-build/CLAUDE.md +0 -33
  262. package/skills/verified-build/SKILL.md +0 -33
package/rules/CLAUDE.md CHANGED
@@ -12,6 +12,7 @@ Rule files installed into `~/.claude/rules/` by `bin/install.mjs`. Claude Code l
12
12
  | File | Rule |
13
13
  |---|---|
14
14
  | `agent-spawn-protocol.md` | Protocol for spawning subagents: context sufficiency check, prompt generation via `/prompt-generator`, then spawn |
15
+ | `anti-corollary-tests.md` | Tests must carry information: no corollary matrices over canonical reductions, no suite that only matches a dead-implementation default, stated mutation in the audit lane |
15
16
  | `ask-user-question-required.md` | Every user-directed question goes through the `AskUserQuestion` tool — no plain-text questions |
16
17
  | `bdd.md` | BDD discovery-driven development workflow and Example Mapping reference |
17
18
  | `claude-md-orphan-file.md` | Every backticked bare filename in a per-directory `CLAUDE.md` table's first column names a file in that directory's subtree |
@@ -0,0 +1,69 @@
1
+ ---
2
+ paths:
3
+ - "**/test_*.py"
4
+ - "**/*_test.py"
5
+ - "**/*.test.*"
6
+ - "**/*.spec.*"
7
+ - "**/conftest.py"
8
+ - "**/tests/**"
9
+ ---
10
+
11
+ # Anti-Corollary Tests
12
+
13
+ **When this applies:** Any Write or Edit that adds or changes tests.
14
+
15
+ ## Rule
16
+
17
+ A large green suite can prove almost nothing. Before you keep a case, answer three questions. If any answer fails, cut the case or replace it with one that carries information.
18
+
19
+ ## The three questions
20
+
21
+ ### 1. Is this a corollary?
22
+
23
+ When the code reduces each input to a canonical form and then compares the forms, every pairwise combination of input spellings follows once the reduction is proven canonical. Walking the full N×N matrix restates that fact. It adds cases and runtime, and it buries the few cases that carry information.
24
+
25
+ **Do this:** test the reduction once. Test the comparison with a few discriminating cases. Do not walk the cross product of spellings.
26
+
27
+ ### 2. Could this test pass if the mechanism were dead?
28
+
29
+ Name the degenerate value a dead implementation would return — empty string, `None`, `False`, a blanket refusal, an empty collection. When the test's expected value equals that default, the test passes whether the mechanism works or not. On its own it proves nothing.
30
+
31
+ **Do this:** keep at least one case that expects the **non-default** answer, and drive the real code path — not a mock that only records that a call happened.
32
+
33
+ ### 3. What single change to the code would make this test fail?
34
+
35
+ When the honest answer is "none," or "only a change that also breaks everything else," the test is decoration. Drop it or rewrite it so one named mutation kills it.
36
+
37
+ ## What a mechanism with a degenerate failure mode needs
38
+
39
+ 1. **At least one non-default case** that exercises the live path and expects the non-default answer.
40
+ 2. **A stated mutation (audit lane):** name one specific change to the code and record how many tests it kills. A mutation that kills zero tests means the suite proves nothing. A reviewer or an audit skill checks this; a hook does not compute it.
41
+ 3. **A few discriminating cases** in place of a large matrix.
42
+
43
+ ## Worked shape (sanitized)
44
+
45
+ A write guard decides whether a write is about to hit a production database. It reduces each database URL to a canonical endpoint identity, then compares identities.
46
+
47
+ Two independent mutations show the two halves of a useful suite:
48
+
49
+ - Gut the reduction so it always returns the empty string: the guard fails **closed** and refuses everything. Only the *allow*-expecting cases die.
50
+ - Abandon the reduction and compare raw hostnames: the guard fails **open** and allows a production write. Only the *refuse*-expecting cases die.
51
+
52
+ Opposite breaks kill opposite halves. A suite with only one half cannot see one of those breaks. Build both halves; skip the spelling matrix once the reduction is covered.
53
+
54
+ ## What this is not
55
+
56
+ A structural hook is the wrong tool here. "Is this a corollary?" and "would this pass against a dead implementation?" need the intent of the code under test. A hook that pattern-matches `parametrize` breadth or counts assertions fires on correct suites and trains people to ignore it. A false-positive gate on a judgment call is worse than no gate. This rule stays judgment-only, with the stated-mutation check in the audit lane — the same pattern as other judgment rules in this package that have no Write/Edit blocker.
57
+
58
+ ## Sibling rules
59
+
60
+ | Rule | Role |
61
+ |---|---|
62
+ | `tdd.md` | Write a failing test before production code |
63
+ | `testing.md` | Mocks and test infrastructure standards |
64
+ | `paired-test-coverage.md` | Every public function in an established suite gets a behavioral test |
65
+ | `anti-corollary-tests.md` | Each test carries information; no corollary matrices; no suite that only matches the dead default |
66
+
67
+ ## Enforcement
68
+
69
+ The AI review lane and audit skills carry this rule: an agent applies it to the test lines a PR changes. No blocking hook backs it, because corollary and dead-default judgments need meaning a regex cannot read.
package/rules/bdd.md CHANGED
@@ -2,8 +2,6 @@
2
2
 
3
3
  **Canonical detail:** `~/.claude/system-prompts/software-engineer.xml` → `<behavior_protocol>`.
4
4
 
5
- **On-demand depth:** `@~/.claude/skills/bdd-protocol/SKILL.md` (Example Mapping §6.4, §7.6 catalog, solo patterns). Tracking design: [jl-cmd/claude-dev-env#82](https://github.com/jl-cmd/claude-dev-env/issues/82).
6
-
7
5
  **Optional long-form references (load when needed):**
8
6
 
9
7
  - `@~/.claude/docs/BDD_SCENARIO_QUALITY.md` — seven scenario quality patterns (§7.6-style)
@@ -21,7 +19,7 @@ Conversation is the essential practice: if discovery is skipped, structured form
21
19
 
22
20
  ## Solo developer
23
21
 
24
- You are often the stakeholder. Use **Example Mapping** in chat ("the one where …", probes, parking lot). Load **`bdd-protocol`** when you need the full algorithm and anti-pattern list.
22
+ You are often the stakeholder. Use **Example Mapping** in chat ("the one where …", probes, parking lot). See the optional long-form references above for the full algorithm and anti-pattern list.
25
23
 
26
24
  ## Naming
27
25
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  **When this applies:** GitHub PR review feedback on a branch you are fixing.
4
4
 
5
- **MANDATORY PROTOCOL (use pr-review-responder skill):**
5
+ **MANDATORY PROTOCOL:**
6
6
 
7
7
  1. Fetch ALL reviewer comments BEFORE any fixes
8
8
  2. Create TodoWrite checklist - One item per comment
@@ -1,3 +1,3 @@
1
1
  # gh API Pagination
2
2
 
3
- Every `gh api` read of a paginated GitHub list endpoint (PR `reviews`/`comments`/`files`, issue `comments`, `pulls`, `issues`) uses `--paginate --slurp` piped to **external** `jq` — `gh`'s built-in `--jq` runs per page, so cross-page operations like `sort_by | last` give wrong-but-confident results. Single-object endpoints (`pulls/<n>`, `issues/<n>`) skip pagination and may use `--jq` directly. Full safe patterns (single-page bounds, newest-first walks): gh-paginate skill.
3
+ Every `gh api` read of a paginated GitHub list endpoint (PR `reviews`/`comments`/`files`, issue `comments`, `pulls`, `issues`) uses `--paginate --slurp` piped to **external** `jq` — `gh`'s built-in `--jq` runs per page, so cross-page operations like `sort_by | last` give wrong-but-confident results. Single-object endpoints (`pulls/<n>`, `issues/<n>`) skip pagination and may use `--jq` directly. For a newest-first walk, sort the slurped array and take the last element; for single-page bounds, cap with a `per_page` query parameter.
@@ -3,3 +3,5 @@
3
3
  All prose a person reads (chat, `AskUserQuestion`, docs, PR/issue bodies, commits): everyday words, short active sentences, lead with the answer, define jargon on first use, and give only the detail the reader needs to act (progressive disclosure). Aim for first-pass readability by a non-specialist. Exact identifiers, file paths, and API names stay exact; code is out of scope.
4
4
 
5
5
  The `plain_language_blocker` PreToolUse hook (AskUserQuestion + `.md` Write/Edit/MultiEdit) blocks a heavy word and names the everyday swap; code fences, inline code, blockquotes, URLs, and file paths are skipped.
6
+
7
+ A project can keep its own domain words out of the check with a `.claude/plain-language-allow.json` file: a JSON array of terms. An exact, case-insensitive, whole-word match on any term passes. The hook reads this file only from inside the project tree, up to the repository root, so each project's allowlist stays with its own code.
package/scripts/CLAUDE.md CHANGED
@@ -21,6 +21,7 @@ Utility scripts installed into `~/.claude/scripts/` by `bin/install.mjs`. Each s
21
21
  | `Install-SweepEmptyDirs.ps1` | Registers `sweep_empty_dirs.py` as a scheduled task on Windows |
22
22
  | `check.ps1` | Runs the full code-quality check suite |
23
23
  | `Show-Asset.ps1` | Opens files on screen, sizing each image window to the image's pixel dimensions (scaled to fit the screen); non-image files open in their default application |
24
+ | `Get-SessionAccount.ps1` | Reports which Claude account the current session is actually logged into by comparing `~/.claude.json`'s CLI login against a `CLAUDE_USER_DATA_DIR` desktop profile's `lastKnownAccountUuid`, recovering the desktop account's email from profile storage when the two accounts differ |
24
25
 
25
26
  ## Subdirectories
26
27
 
@@ -28,10 +29,18 @@ Utility scripts installed into `~/.claude/scripts/` by `bin/install.mjs`. Each s
28
29
  |---|---|
29
30
  | `dev_env_scripts_constants/` | Named constants (`timing.py`) for scripts in this directory |
30
31
  | `sync_to_cursor/` | Package that builds Cursor `.mdc` files from Claude rules and docs |
31
- | `tests/` | pytest suite for the Python scripts in this directory |
32
+ | `tests/` | pytest suite for the Python scripts and Pester (`*.Tests.ps1`) suite for the PowerShell scripts in this directory |
32
33
 
33
34
  ## Running tests
34
35
 
36
+ Python scripts (pytest):
37
+
35
38
  ```bash
36
39
  python -m pytest packages/claude-dev-env/scripts/tests/
37
40
  ```
41
+
42
+ PowerShell scripts (Pester 5+, `*.Tests.ps1`):
43
+
44
+ ```powershell
45
+ Invoke-Pester -Path packages/claude-dev-env/scripts/tests/
46
+ ```
@@ -0,0 +1,210 @@
1
+ #!/usr/bin/env pwsh
2
+ [CmdletBinding()]
3
+ param(
4
+ [switch]$AsJson
5
+ )
6
+
7
+ $noiseEmailPatterns = @(
8
+ '@example\.(com|org|net)$',
9
+ '(@|\.)sentry\.io$',
10
+ '@anthropic\.',
11
+ '^Claude@\d'
12
+ )
13
+
14
+ $isoLatin1CodePage = 28591
15
+
16
+ $profileStorageDirectoryNames = @('sentry', 'IndexedDB', 'Session Storage', 'WebStorage')
17
+
18
+ $emailAddressPattern = '(?<![a-zA-Z0-9._%+-])[a-zA-Z0-9][a-zA-Z0-9._%+-]*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+(?![a-zA-Z0-9-])'
19
+
20
+ $maximumStorageFileBytes = 50MB
21
+
22
+ function Test-NoiseEmail {
23
+ param([string]$Email)
24
+ foreach ($pattern in $noiseEmailPatterns) {
25
+ if ($Email -imatch $pattern) { return $true }
26
+ }
27
+ return $false
28
+ }
29
+
30
+ function Get-EncodingAgnosticTextView {
31
+ param([string]$FilePath)
32
+ try {
33
+ $fileBytes = [System.IO.File]::ReadAllBytes($FilePath)
34
+ if ($fileBytes.Length -eq 0) { return @() }
35
+ $isoLatin1Encoding = [System.Text.Encoding]::GetEncoding($isoLatin1CodePage)
36
+ return @(
37
+ $isoLatin1Encoding.GetString($fileBytes),
38
+ [System.Text.Encoding]::Unicode.GetString($fileBytes)
39
+ )
40
+ }
41
+ catch {
42
+ return @()
43
+ }
44
+ }
45
+
46
+ function Get-EmailCandidatesFromProfile {
47
+ param(
48
+ [string]$ProfileDirectory,
49
+ [string]$ExcludeEmail
50
+ )
51
+ $scanDirectories = $profileStorageDirectoryNames |
52
+ ForEach-Object { Join-Path $ProfileDirectory $_ } |
53
+ Where-Object { Test-Path -LiteralPath $_ }
54
+ $foundEmails = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
55
+
56
+ foreach ($scanDirectory in $scanDirectories) {
57
+ $candidateFiles = Get-ChildItem -LiteralPath $scanDirectory -Recurse -File -ErrorAction SilentlyContinue |
58
+ Where-Object { $_.Length -lt $maximumStorageFileBytes }
59
+ foreach ($candidateFile in $candidateFiles) {
60
+ $decodedTextViews = Get-EncodingAgnosticTextView -FilePath $candidateFile.FullName
61
+ foreach ($decodedText in $decodedTextViews) {
62
+ foreach ($match in [regex]::Matches($decodedText, $emailAddressPattern)) {
63
+ $candidateEmail = $match.Value
64
+ $isKnownCliEmail = $ExcludeEmail -and ($candidateEmail -ieq $ExcludeEmail)
65
+ if (-not (Test-NoiseEmail -Email $candidateEmail) -and -not $isKnownCliEmail) {
66
+ [void]$foundEmails.Add($candidateEmail)
67
+ }
68
+ }
69
+ }
70
+ }
71
+ }
72
+ return $foundEmails
73
+ }
74
+
75
+ function Write-AccountResult {
76
+ param(
77
+ [string]$Email,
78
+ [string]$AccountUuid,
79
+ [string]$Source,
80
+ [string]$CliEmail,
81
+ [string]$CliAccountUuid,
82
+ [switch]$AsJson
83
+ )
84
+ if ($AsJson) {
85
+ [pscustomobject]@{
86
+ account = $Email
87
+ accountUuid = $AccountUuid
88
+ source = $Source
89
+ cliAccount = $CliEmail
90
+ cliAccountUuid = $CliAccountUuid
91
+ } | ConvertTo-Json
92
+ return
93
+ }
94
+ Write-Output "Account: $Email"
95
+ Write-Output "AccountUuid: $AccountUuid"
96
+ Write-Output "Source: $Source"
97
+ if ($CliEmail) {
98
+ Write-Output "CliAccount: $CliEmail"
99
+ Write-Output "CliAccountUuid: $CliAccountUuid"
100
+ }
101
+ }
102
+
103
+ function Get-SessionAccountResult {
104
+ param(
105
+ [int]$ExitCode,
106
+ [string]$Email = $null,
107
+ [string]$AccountUuid = $null,
108
+ [string]$Source = $null,
109
+ [string]$CliEmail = $null,
110
+ [string]$CliAccountUuid = $null,
111
+ [string]$ErrorMessage = $null
112
+ )
113
+ [pscustomobject]@{
114
+ ExitCode = $ExitCode
115
+ Email = $Email
116
+ AccountUuid = $AccountUuid
117
+ Source = $Source
118
+ CliEmail = $CliEmail
119
+ CliAccountUuid = $CliAccountUuid
120
+ ErrorMessage = $ErrorMessage
121
+ }
122
+ }
123
+
124
+ function Resolve-SessionAccount {
125
+ param(
126
+ [string]$CliConfigPath,
127
+ [string]$ProfileDirectory
128
+ )
129
+ if (-not (Test-Path -LiteralPath $CliConfigPath)) {
130
+ return Get-SessionAccountResult -ExitCode 2 -ErrorMessage "CLI config not found: $CliConfigPath"
131
+ }
132
+
133
+ try {
134
+ $cliConfig = Get-Content -LiteralPath $CliConfigPath -Raw -ErrorAction Stop | ConvertFrom-Json -AsHashtable -ErrorAction Stop
135
+ }
136
+ catch {
137
+ return Get-SessionAccountResult -ExitCode 2 -ErrorMessage "Failed to parse CLI config as JSON: $CliConfigPath"
138
+ }
139
+
140
+ if ($cliConfig -isnot [System.Collections.IDictionary]) {
141
+ return Get-SessionAccountResult -ExitCode 2 -ErrorMessage "CLI config top level is not a JSON object: $CliConfigPath"
142
+ }
143
+
144
+ $oauthAccount = $cliConfig['oauthAccount']
145
+ $cliEmail = if ($oauthAccount) { $oauthAccount['emailAddress'] } else { $null }
146
+ $cliAccountUuid = if ($oauthAccount) { $oauthAccount['accountUuid'] } else { $null }
147
+ if (-not $cliEmail -or -not $cliAccountUuid) {
148
+ return Get-SessionAccountResult -ExitCode 2 -ErrorMessage "CLI config is missing oauthAccount.emailAddress or oauthAccount.accountUuid: $CliConfigPath"
149
+ }
150
+
151
+ if (-not $ProfileDirectory -or -not (Test-Path -LiteralPath $ProfileDirectory)) {
152
+ return Get-SessionAccountResult -ExitCode 0 -Email $cliEmail -AccountUuid $cliAccountUuid -Source 'cli-config'
153
+ }
154
+
155
+ $profileConfigPath = Join-Path $ProfileDirectory 'config.json'
156
+ if (-not (Test-Path -LiteralPath $profileConfigPath)) {
157
+ return Get-SessionAccountResult -ExitCode 2 -ErrorMessage "Desktop profile config not found: $profileConfigPath"
158
+ }
159
+
160
+ try {
161
+ $profileConfig = Get-Content -LiteralPath $profileConfigPath -Raw -ErrorAction Stop | ConvertFrom-Json -AsHashtable -ErrorAction Stop
162
+ }
163
+ catch {
164
+ return Get-SessionAccountResult -ExitCode 2 -ErrorMessage "Failed to parse desktop profile config as JSON: $profileConfigPath"
165
+ }
166
+
167
+ if ($profileConfig -isnot [System.Collections.IDictionary]) {
168
+ return Get-SessionAccountResult -ExitCode 2 -ErrorMessage "Desktop profile config top level is not a JSON object: $profileConfigPath"
169
+ }
170
+
171
+ $profileAccountUuid = $profileConfig['lastKnownAccountUuid']
172
+ if (-not $profileAccountUuid) {
173
+ return Get-SessionAccountResult -ExitCode 2 -ErrorMessage "Desktop profile config is missing lastKnownAccountUuid: $profileConfigPath"
174
+ }
175
+
176
+ if ($profileAccountUuid -eq $cliAccountUuid) {
177
+ return Get-SessionAccountResult -ExitCode 0 -Email $cliEmail -AccountUuid $cliAccountUuid -Source 'desktop-profile (matches cli-config)'
178
+ }
179
+
180
+ $candidateEmails = @(Get-EmailCandidatesFromProfile -ProfileDirectory $ProfileDirectory -ExcludeEmail $cliEmail)
181
+ if ($candidateEmails.Count -eq 1) {
182
+ return Get-SessionAccountResult -ExitCode 0 -Email $candidateEmails[0] -AccountUuid $profileAccountUuid -Source 'desktop-profile (differs from cli-config)' -CliEmail $cliEmail -CliAccountUuid $cliAccountUuid
183
+ }
184
+
185
+ if ($candidateEmails.Count -eq 0) {
186
+ return Get-SessionAccountResult -ExitCode 1 -ErrorMessage "Desktop profile account ($profileAccountUuid) differs from CLI config, but no candidate email was recovered from profile storage: $ProfileDirectory"
187
+ }
188
+
189
+ return Get-SessionAccountResult -ExitCode 1 -ErrorMessage "Desktop profile account ($profileAccountUuid) differs from CLI config, and multiple candidate emails were found in profile storage: $($candidateEmails -join ', ')"
190
+ }
191
+
192
+ function Invoke-GetSessionAccount {
193
+ param(
194
+ [switch]$AsJson
195
+ )
196
+ $cliConfigPath = Join-Path $HOME '.claude.json'
197
+ $sessionAccount = Resolve-SessionAccount -CliConfigPath $cliConfigPath -ProfileDirectory $env:CLAUDE_USER_DATA_DIR
198
+ if ($sessionAccount.ErrorMessage) {
199
+ Write-Error $sessionAccount.ErrorMessage -ErrorAction Continue
200
+ exit $sessionAccount.ExitCode
201
+ }
202
+ Write-AccountResult -Email $sessionAccount.Email -AccountUuid $sessionAccount.AccountUuid -Source $sessionAccount.Source -CliEmail $sessionAccount.CliEmail -CliAccountUuid $sessionAccount.CliAccountUuid -AsJson:$AsJson
203
+ exit $sessionAccount.ExitCode
204
+ }
205
+
206
+ if ($MyInvocation.InvocationName -ne '.') {
207
+ Set-StrictMode -Version Latest
208
+ $ErrorActionPreference = 'Stop'
209
+ Invoke-GetSessionAccount -AsJson:$AsJson
210
+ }
@@ -1,6 +1,6 @@
1
1
  # scripts/tests
2
2
 
3
- pytest suite for the Python scripts in `scripts/`.
3
+ pytest suite for the Python scripts and Pester suite for the PowerShell scripts in `scripts/`.
4
4
 
5
5
  ## Test files
6
6
 
@@ -11,8 +11,22 @@ pytest suite for the Python scripts in `scripts/`.
11
11
  | `test_sweep_empty_dirs.py` | `sweep_empty_dirs.py` — age check, one-shot mode, and continuous-watch behavior |
12
12
  | `test_sync_to_cursor.py` | `sync_to_cursor/` package — mapping, hashing, manifest, and path resolution |
13
13
 
14
+ ## PowerShell test files
15
+
16
+ | File | Covers |
17
+ |---|---|
18
+ | `Get-SessionAccount.Tests.ps1` | `Get-SessionAccount.ps1` — noise filtering, profile-storage email recovery, CLI-vs-desktop account resolution, and output formatting |
19
+
14
20
  ## Running
15
21
 
22
+ Python scripts (pytest):
23
+
16
24
  ```bash
17
25
  python -m pytest packages/claude-dev-env/scripts/tests/
18
26
  ```
27
+
28
+ PowerShell scripts (Pester 5+, `*.Tests.ps1`):
29
+
30
+ ```powershell
31
+ Invoke-Pester -Path packages/claude-dev-env/scripts/tests/
32
+ ```