claude-dev-env 7.7.1 → 8.1.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 (118) hide show
  1. package/.agents/agents/clean-coder.md +5 -6
  2. package/.agents/agents/issue-tracker.md +1 -1
  3. package/.agents/agents/pr-description-writer.md +1 -1
  4. package/.agents/agents/skill-writer-agent.md +1 -12
  5. package/.agents/agents/test_agent_frontmatter.py +2 -19
  6. package/.agents/skills/issue-tracker/SKILL.md +1 -1
  7. package/.agents/skills/orchestrator/SKILL.md +4 -5
  8. package/.agents/skills/pull-request/SKILL.md +8 -7
  9. package/.agents/skills/pull-request/reference/publication-tasks.md +1 -1
  10. package/.agents/skills/team-advisor/SKILL.md +2 -3
  11. package/_shared/advisor/advisor-protocol.md +0 -1
  12. package/_shared/advisor/reference/lifecycle.md +0 -1
  13. package/_shared/advisor/reference/warm-up.md +1 -2
  14. package/audit-rubrics/audit-categories.json +0 -4
  15. package/audit-rubrics/category_rubrics/category-e-dead-code.md +1 -1
  16. package/audit-rubrics/category_rubrics/category-j-code-rules-compliance.md +1 -2
  17. package/audit-rubrics/prompts/category-e-dead-code.md +5 -5
  18. package/audit-rubrics/prompts/category-j-code-rules-compliance.md +1 -24
  19. package/bin/install-constants.mjs +0 -5
  20. package/bin/install-pstack-plugin.mjs +192 -0
  21. package/bin/install-pstack-plugin.test.mjs +181 -0
  22. package/bin/install.agents-home.test.mjs +0 -4
  23. package/bin/install.codex-rules.test.mjs +6 -0
  24. package/bin/install.cursor-rules.test.mjs +3 -253
  25. package/bin/install.mjs +55 -160
  26. package/bin/install.test.mjs +110 -149
  27. package/bin/prune-retired-pstack-store.mjs +83 -0
  28. package/bin/prune-retired-pstack-store.test.mjs +105 -0
  29. package/bin/prune-session-continuity.mjs +57 -0
  30. package/bin/prune-session-continuity.test.mjs +70 -0
  31. package/bin/resolve-install-root.mjs +2 -0
  32. package/bin/resolve-install-root.test.mjs +69 -0
  33. package/docs/CODE_RULES.md +1 -1
  34. package/docs/agent-spawn-protocol.md +1 -1
  35. package/docs/codex-compatibility.md +4 -16
  36. package/docs/nas-ssh-invocation.md +1 -1
  37. package/docs/references/prose-style-enforcement.md +2 -2
  38. package/hooks/HOOK_SPECS_PROMPT_WORKFLOW.md +1 -3
  39. package/hooks/blocking/code_rules_constants_config.py +1 -108
  40. package/hooks/blocking/code_rules_enforcer.py +9 -5
  41. package/hooks/blocking/code_rules_test_layout.py +6 -7
  42. package/hooks/blocking/test_code_rules_constants_config.py +13 -0
  43. package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +0 -1
  44. package/hooks/blocking/test_code_rules_enforcer_dispatch_wiring.py +15 -0
  45. package/hooks/blocking/test_code_rules_enforcer_split_constants_config.py +0 -60
  46. package/hooks/blocking/test_context_control_policy_files.py +0 -11
  47. package/hooks/hooks_constants/blocking_check_limits.py +1 -1
  48. package/hooks/hooks_constants/code_rules_enforcer_constants.py +0 -1
  49. package/hooks/hooks_constants/setup_project_paths_constants.py +1 -2
  50. package/hooks/session/test_untracked_repo_detector.py +1 -1
  51. package/hooks/validators/code_quality_checks.py +15 -2
  52. package/hooks/validators/test_code_quality_checks.py +22 -0
  53. package/package.json +1 -2
  54. package/rules/anti-corollary-tests.md +3 -1
  55. package/rules/archiving-agent-config.md +70 -0
  56. package/rules/asd-ste100-language.md +1 -1
  57. package/rules/ci-owns-the-gate.md +73 -0
  58. package/rules/cleanup-temp-files.md +1 -1
  59. package/rules/code-standards.md +3 -4
  60. package/rules/docstring-prose-matches-implementation.md +3 -3
  61. package/rules/explore-thoroughly.md +2 -2
  62. package/rules/failure-blast-radius.md +10 -9
  63. package/rules/git-workflow.md +27 -12
  64. package/rules/long-horizon-autonomy.md +3 -1
  65. package/rules/nas-ssh-invocation.md +1 -1
  66. package/rules/orphan-css-class.md +1 -1
  67. package/rules/paired-test-coverage.md +1 -1
  68. package/rules/research-mode.md +3 -1
  69. package/rules/retired-hook-prose.md +10 -2
  70. package/rules/testing.md +3 -3
  71. package/rules/verify-before-asking.md +4 -1
  72. package/rules/verify-runtime-state.md +2 -2
  73. package/rules/windows-filesystem-safe.md +1 -1
  74. package/rules/workflow-substitution-slots.md +1 -1
  75. package/scripts/Sync-RepoMain.ps1 +71 -6
  76. package/scripts/policy_lint/adapter_detectors.py +116 -10
  77. package/scripts/policy_lint/config/approved_test_pairs.py +12 -0
  78. package/scripts/policy_lint/config/constants.py +3 -0
  79. package/scripts/tests/Sync-RepoMain.Tests.ps1 +108 -0
  80. package/scripts/tests/test_codex_compat_materializer.py +3 -3
  81. package/scripts/tests/test_policy_lint_rules_validator_baseline.py +126 -0
  82. package/.agents/skills/session-continuity/AGENTS.md +0 -1
  83. package/.agents/skills/session-continuity/CLAUDE.md +0 -1
  84. package/.agents/skills/session-continuity/README.md +0 -223
  85. package/.agents/skills/session-continuity/SKILL.md +0 -125
  86. package/.agents/skills/session-continuity/continuity.mjs +0 -445
  87. package/.agents/skills/session-continuity/continuity.test.mjs +0 -381
  88. package/bin/install-session-continuity.mjs +0 -131
  89. package/bin/install-session-continuity.test.mjs +0 -106
  90. package/bin/install.pstack-plugin.test.mjs +0 -115
  91. package/bin/pstack-codex-discovery.test.mjs +0 -79
  92. package/bin/pstack-model-preferences.claude.json +0 -23
  93. package/bin/pstack-model-preferences.codex.json +0 -24
  94. package/bin/pstack.mjs +0 -392
  95. package/bin/pstack.test.mjs +0 -347
  96. package/bin/refresh-pstack-plugin-skills.test.mjs +0 -211
  97. package/bin/select-pstack-models.test.mjs +0 -776
  98. package/hooks/blocking/test_code_rules_enforcer_file_global_constants.py +0 -231
  99. package/rules/agent-spawn-protocol.md +0 -9
  100. package/rules/ask-user-question-required.md +0 -31
  101. package/rules/claims-as-quotes.md +0 -55
  102. package/rules/confirm-implementation-forks.md +0 -11
  103. package/rules/file-global-constants.md +0 -81
  104. package/rules/hedging-claims.md +0 -7
  105. package/rules/measurement-denominators.md +0 -39
  106. package/rules/prompt-workflow-context-controls.md +0 -19
  107. package/rules/pstack-host-mapping.md +0 -48
  108. package/rules/pstack-models.md +0 -142
  109. package/rules/vault-context.md +0 -5
  110. package/scripts/pstack-adapters/common.md +0 -17
  111. package/scripts/pstack-adapters/create-skill.md +0 -14
  112. package/scripts/pstack-adapters/host-claude.md +0 -7
  113. package/scripts/pstack-adapters/host-codex.md +0 -11
  114. package/scripts/pstack-adapters/host-cursor.md +0 -7
  115. package/scripts/pstack.lock.json +0 -11
  116. package/scripts/refresh_pstack_plugin_skills.mjs +0 -200
  117. package/scripts/select_pstack_models.mjs +0 -469
  118. /package/.agents/{skills → skills-archived}/source-command-logifix/SKILL.md +0 -0
@@ -22,7 +22,7 @@ Before writing a single line — **task-local discovery only** (no project-wide
22
22
  1. **Load scoped repository instructions first.** Starting at the repository root, read every applicable `AGENTS.md` on the path to the task file. Then read the applicable `CLAUDE.md` files. Apply nearer instructions after broader ones; the closest file wins.
23
23
  2. **Read the file you are about to edit** (when editing existing code). Note every existing comment so you can leave each one untouched on lines that remain otherwise unchanged.
24
24
  3. **Discover config only next to the task files.** From each file you will write or edit, walk up to the nearest package or repo root and inspect the target package's existing constants layout — such as `config/` or a sibling `*_constants` package. Keep this task-local constants search. Do **not** force a generic `config/` layout. Do **not** glob the whole tree for every config file. Do **not** glob or open `.env`, `.env.*`, or other secret files.
25
- 4. **Reuse constants from that local table.** Reuse first: exact value match → import the existing name. Semantic match → reuse it. Add a shared constant only when the value is shared policy or has multiple consumers. When no match exists, use the target package's existing constants layout; a one-use value follows `file-global-constants` rather than becoming a new shared constant.
25
+ 4. **Reuse constants from that local table.** Reuse first: exact value match → import the existing name. Semantic match → reuse it. Add a shared constant only when the value is shared policy or has multiple consumers. When no match exists, use the target package's existing constants layout, and keep a one-use value in the scope that reads it rather than making it a new shared constant.
26
26
  5. **Search callers.** When a symbol, name, or signature changes, search its full caller boundary and update every consumer. This search may be wider than the constants search.
27
27
 
28
28
  ## Generation mindset (9 laws)
@@ -50,17 +50,16 @@ Installed paths use the active managed root and agents home resolved above; sour
50
50
  | Write-time gates | `<managed-root>/hooks/blocking/code_rules_enforcer.py` (source fallback: `packages/claude-dev-env/hooks/blocking/code_rules_enforcer.py`) |
51
51
  | Naming and abbreviations | `<managed-root>/docs/CODE_RULES.md#5-no-abbreviations` (source fallback: `packages/claude-dev-env/docs/CODE_RULES.md#5-no-abbreviations`) |
52
52
  | Policy surface map | `<managed-root>/rules/code-standards.md` (source fallback: `packages/claude-dev-env/rules/code-standards.md`) |
53
- | File-global constants | `<managed-root>/rules/file-global-constants.md` (source fallback: `packages/claude-dev-env/rules/file-global-constants.md`) |
54
53
  | Windows rmtree / mkdir | `<managed-root>/rules/windows-filesystem-safe.md` (source fallback: `packages/claude-dev-env/rules/windows-filesystem-safe.md`) |
55
54
  | `gh` body files | `<managed-root>/rules/gh-cli-conventions.md` (source fallback: `packages/claude-dev-env/rules/gh-cli-conventions.md`) |
56
55
  | Plain illustrative docstrings | `<managed-root>/rules/plain-illustrative-docstrings.md` (source fallback: `packages/claude-dev-env/rules/plain-illustrative-docstrings.md`) |
57
56
  | Tests / TDD | `<managed-root>/rules/testing.md` (source fallback: `packages/claude-dev-env/rules/testing.md`), `<managed-root>/rules/paired-test-coverage.md` (source fallback: `packages/claude-dev-env/rules/paired-test-coverage.md`), `<managed-root>/rules/bdd.md` (source fallback: `packages/claude-dev-env/rules/bdd.md`) |
58
- | Questions / task tracking | `<managed-root>/rules/ask-user-question-required.md` (source fallback: `packages/claude-dev-env/rules/ask-user-question-required.md`), `<managed-root>/rules/verify-before-asking.md` (source fallback: `packages/claude-dev-env/rules/verify-before-asking.md`) |
57
+ | Questions / task tracking | `<managed-root>/rules/verify-before-asking.md` (source fallback: `packages/claude-dev-env/rules/verify-before-asking.md`) |
59
58
  | Runtime evidence | `<managed-root>/rules/verify-runtime-state.md` (source fallback: `packages/claude-dev-env/rules/verify-runtime-state.md`) |
60
59
  | Documentation / durable artifacts | `<managed-root>/rules/doc-inventory-integrity.md` (source fallback: `packages/claude-dev-env/rules/doc-inventory-integrity.md`), `<managed-root>/rules/durable-post-artifacts.md` (source fallback: `packages/claude-dev-env/rules/durable-post-artifacts.md`) |
61
60
  | Batch / failure blast radius | `<managed-root>/rules/failure-blast-radius.md` (source fallback: `packages/claude-dev-env/rules/failure-blast-radius.md`) |
62
61
  | Git / GitHub | `<managed-root>/rules/git-workflow.md` (source fallback: `packages/claude-dev-env/rules/git-workflow.md`), `<managed-root>/rules/gh-cli-conventions.md` (source fallback: `packages/claude-dev-env/rules/gh-cli-conventions.md`), `<managed-root>/rules/re-stage-before-commit.md` (source fallback: `packages/claude-dev-env/rules/re-stage-before-commit.md`) |
63
- | Workers / completion | `<managed-root>/rules/agent-spawn-protocol.md` (source fallback: `packages/claude-dev-env/rules/agent-spawn-protocol.md`), `<managed-root>/rules/workers-done-before-complete.md` (source fallback: `packages/claude-dev-env/rules/workers-done-before-complete.md`) |
62
+ | Workers / completion | `<managed-root>/rules/workers-done-before-complete.md` (source fallback: `packages/claude-dev-env/rules/workers-done-before-complete.md`) |
64
63
  | TDD / right-size | Review contract Tests + Design; `CODE_RULES.md` §7–§8 |
65
64
 
66
65
  ## Session policy map (canonical links)
@@ -70,13 +69,13 @@ Load only the group that matches the task. Keep session policy details in these
70
69
  | Group | Canonical refs |
71
70
  |---|---|
72
71
  | Tests | `<managed-root>/rules/testing.md` (source fallback: `packages/claude-dev-env/rules/testing.md`); `<managed-root>/rules/anti-corollary-tests.md` (source fallback: `packages/claude-dev-env/rules/anti-corollary-tests.md`) |
73
- | Questions | `<managed-root>/rules/ask-user-question-required.md` (source fallback: `packages/claude-dev-env/rules/ask-user-question-required.md`); `<managed-root>/rules/verify-before-asking.md` (source fallback: `packages/claude-dev-env/rules/verify-before-asking.md`) |
72
+ | Questions | `<managed-root>/rules/verify-before-asking.md` (source fallback: `packages/claude-dev-env/rules/verify-before-asking.md`) |
74
73
  | Search and shell | `<managed-root>/rules/filesystem-search.md` (source fallback: `packages/claude-dev-env/rules/filesystem-search.md`); `<managed-root>/rules/shell-invocation.md` (source fallback: `packages/claude-dev-env/rules/shell-invocation.md`) |
75
74
  | Runtime checks | `<managed-root>/rules/verify-runtime-state.md` (source fallback: `packages/claude-dev-env/rules/verify-runtime-state.md`) |
76
75
  | Documentation | `<managed-root>/rules/doc-inventory-integrity.md` (source fallback: `packages/claude-dev-env/rules/doc-inventory-integrity.md`); `<managed-root>/rules/docstring-prose-matches-implementation.md` (source fallback: `packages/claude-dev-env/rules/docstring-prose-matches-implementation.md`) |
77
76
  | Batch failures | `<managed-root>/rules/failure-blast-radius.md` (source fallback: `packages/claude-dev-env/rules/failure-blast-radius.md`) |
78
77
  | Git | `<managed-root>/rules/git-workflow.md` (source fallback: `packages/claude-dev-env/rules/git-workflow.md`); `<managed-root>/rules/re-stage-before-commit.md` (source fallback: `packages/claude-dev-env/rules/re-stage-before-commit.md`) |
79
- | Worker coordination | `<managed-root>/rules/agent-spawn-protocol.md` (source fallback: `packages/claude-dev-env/rules/agent-spawn-protocol.md`); `<managed-root>/rules/workers-done-before-complete.md` (source fallback: `packages/claude-dev-env/rules/workers-done-before-complete.md`) |
78
+ | Worker coordination | `<managed-root>/rules/workers-done-before-complete.md` (source fallback: `packages/claude-dev-env/rules/workers-done-before-complete.md`) |
80
79
 
81
80
  Material implementation questions must return to the caller for `AskUserQuestion` handling; do not ask in plain text or guess.
82
81
 
@@ -19,7 +19,7 @@ Keep the same warm `issue-tracker` agent for follow-up actions on the **same iss
19
19
 
20
20
  ## Voice
21
21
 
22
- Use the `plain-brief` output style (`output-styles/plain-brief.md`). Final message is issue number(s) and URL(s) only.
22
+ Keep the final message to the issue number(s) and URL(s) only.
23
23
 
24
24
  ## On each turn
25
25
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: pr-description-writer
3
- description: "Writes factual pull request titles, descriptions, and comments in plain illustrative language from the current diff. The `pr_description_writer_gate` PreToolUse hook requires a spawn of this agent before `gh pr create`."
3
+ description: "Writes factual pull request titles, descriptions, and comments in plain illustrative language from the current diff. Spawn it when a pull request needs a body written from the diff or rewritten in full."
4
4
  tools: Read,Grep,Glob,Bash
5
5
  ---
6
6
 
@@ -30,18 +30,7 @@ A caller may bundle three of these fields — `Composition plan`, `Description`,
30
30
 
31
31
  A refine packet instead carries: the current SKILL.md, what was observed, what to change, new gotchas to add, what to preserve, a description rewrite, a composition change, and a deterministic fix.
32
32
 
33
- **Direct request.** A looser prompt names the skill to build or the file to edit. Fill the packet yourself from the references below, and ask the caller for any field only they hold, such as domain context or constraints.
34
-
35
- ## Read first
36
-
37
- At the start of every job, read the live references at `~/.claude/skills/skill-builder/references/` so you author to current conventions:
38
-
39
- - `delegation-map.md` — the handoff packet and the Produce contract.
40
- - `deterministic-elements.md` — the rule that deterministic work ships as code, artifacts, or session tasks.
41
- - `description-field.md` — the trigger-catalog description shape.
42
- - `skill-modularity.md` — hub layout and compose-by-name.
43
-
44
- Read them fresh each run; they carry the rules you author to.
33
+ **Direct request.** A looser prompt names the skill to build or the file to edit. Fill the packet yourself from the standards below, and ask the caller for any field only they hold, such as domain context or constraints.
45
34
 
46
35
  ## Standards every file meets
47
36
 
@@ -13,14 +13,14 @@ model on every spawn, so no agent definition names one, concrete or
13
13
  `inherit`::
14
14
 
15
15
  ok: <no model key at all>
16
- flag: model: inherit <- caller can no longer choose the model
16
+ flag: model: inherit <- caller cannot choose the model
17
17
  flag: model: opus <- pinned concrete model, caller can't override
18
18
 
19
19
  Every block must also load through `yaml.safe_load`. An unquoted colon inside
20
20
  a plain scalar reads as a mapping key and makes the whole block unloadable::
21
21
 
22
22
  ok: description: ... constraints. Examples are below.
23
- flag: description: ... constraints. Examples: <- block no longer loads
23
+ flag: description: ... constraints. Examples: <- block does not load
24
24
 
25
25
  Two parsers read these files, and a block that satisfies one can still be
26
26
  unreadable by the other. `scripts/codex_compat_materializer.py` loads the
@@ -445,10 +445,6 @@ EXPECTED_SOURCE_LINK_PAIRS = {
445
445
  "<managed-root>/rules/code-standards.md",
446
446
  "packages/claude-dev-env/rules/code-standards.md",
447
447
  ),
448
- (
449
- "<managed-root>/rules/file-global-constants.md",
450
- "packages/claude-dev-env/rules/file-global-constants.md",
451
- ),
452
448
  (
453
449
  "<managed-root>/rules/windows-filesystem-safe.md",
454
450
  "packages/claude-dev-env/rules/windows-filesystem-safe.md",
@@ -477,10 +473,6 @@ EXPECTED_SOURCE_LINK_PAIRS = {
477
473
  "<managed-root>/rules/bdd.md",
478
474
  "packages/claude-dev-env/rules/bdd.md",
479
475
  ),
480
- (
481
- "<managed-root>/rules/ask-user-question-required.md",
482
- "packages/claude-dev-env/rules/ask-user-question-required.md",
483
- ),
484
476
  (
485
477
  "<managed-root>/rules/verify-before-asking.md",
486
478
  "packages/claude-dev-env/rules/verify-before-asking.md",
@@ -521,10 +513,6 @@ EXPECTED_SOURCE_LINK_PAIRS = {
521
513
  "<managed-root>/rules/re-stage-before-commit.md",
522
514
  "packages/claude-dev-env/rules/re-stage-before-commit.md",
523
515
  ),
524
- (
525
- "<managed-root>/rules/agent-spawn-protocol.md",
526
- "packages/claude-dev-env/rules/agent-spawn-protocol.md",
527
- ),
528
516
  (
529
517
  "<managed-root>/rules/workers-done-before-complete.md",
530
518
  "packages/claude-dev-env/rules/workers-done-before-complete.md",
@@ -797,7 +785,6 @@ def test_clean_coder_links_canonical_policy_areas() -> None:
797
785
  required_links = (
798
786
  "<managed-root>/docs/CODE_RULES.md#5-no-abbreviations",
799
787
  "<managed-root>/rules/testing.md",
800
- "<managed-root>/rules/ask-user-question-required.md",
801
788
  "<managed-root>/rules/verify-runtime-state.md",
802
789
  "<managed-root>/rules/doc-inventory-integrity.md",
803
790
  "<managed-root>/rules/failure-blast-radius.md",
@@ -992,7 +979,6 @@ def test_clean_coder_follows_target_package_constant_layouts() -> None:
992
979
  assert "Reuse first" in body
993
980
  assert "shared policy" in body
994
981
  assert "multiple consumers" in body
995
- assert "file-global-constants" in body
996
982
  assert "add the constant to the appropriate config file" not in body
997
983
 
998
984
 
@@ -1029,7 +1015,6 @@ def test_clean_coder_groups_session_policy_references() -> None:
1029
1015
  "Worker coordination",
1030
1016
  ):
1031
1017
  assert each_policy_group in body
1032
- assert "ask-user-question-required.md" in body
1033
1018
  assert "workers-done-before-complete.md" in body
1034
1019
  assert "verify-runtime-state.md" in body
1035
1020
  assert "Material implementation questions must return to the caller" in body
@@ -1039,7 +1024,6 @@ def test_clean_coder_groups_session_policy_references() -> None:
1039
1024
  expected_session_policy_files = (
1040
1025
  "testing.md",
1041
1026
  "anti-corollary-tests.md",
1042
- "ask-user-question-required.md",
1043
1027
  "verify-before-asking.md",
1044
1028
  "filesystem-search.md",
1045
1029
  "shell-invocation.md",
@@ -1049,7 +1033,6 @@ def test_clean_coder_groups_session_policy_references() -> None:
1049
1033
  "failure-blast-radius.md",
1050
1034
  "git-workflow.md",
1051
1035
  "re-stage-before-commit.md",
1052
- "agent-spawn-protocol.md",
1053
1036
  "workers-done-before-complete.md",
1054
1037
  )
1055
1038
  session_policy_map = body[body.index("## Session policy map") :]
@@ -21,7 +21,7 @@ Prefer the **same warm** `issue-tracker` agent for follow-ups on the same issue
21
21
 
22
22
  ## Voice
23
23
 
24
- Use the `plain-brief` output style (`output-styles/plain-brief.md`). End with the issue number(s) and URL(s) the caller must keep.
24
+ End with the issue number(s) and URL(s) the caller must keep, and nothing else.
25
25
 
26
26
  ## Gotchas
27
27
 
@@ -36,9 +36,8 @@ verification reads.
36
36
  ## status_gate (deterministic — not optional)
37
37
 
38
38
  **Prose does not keep the loop alive.** Re-arm and terminate are gated by
39
- `scripts/status_gate.py` (and, on Claude, the PreToolUse hook
40
- `orchestrator_refresh_reschedule_gate`). The gate is host-agnostic: a
41
- single pending re-arm latch in the status file, not host product names.
39
+ `scripts/status_gate.py`. The gate is host-agnostic: a single pending re-arm
40
+ latch in the status file, not host product names.
42
41
 
43
42
  ```
44
43
  python scripts/status_gate.py set --status active|done [--run-slug SLUG] [--status-file PATH]
@@ -158,8 +157,8 @@ in the repo the run works on (working files, not committed):
158
157
  - **One assignment file per task** — scope, file list, constraints, the
159
158
  acceptance check, baseline command output. The thick context goes
160
159
  here. `/prompt-generator` authors the assignment once at plan time,
161
- and every ticket for that task reuses it — that reuse is what
162
- satisfies the agent-spawn-protocol context check at each spawn.
160
+ and every ticket for that task reuses it, so each spawn starts from
161
+ the same named files, constraints, and acceptance check.
163
162
  - **Results merge into run state.** An executor's product is its
164
163
  artifact — the branch diff, the test output, the report its agent type
165
164
  may write — and its reply is thin: status, artifact paths, blockers.
@@ -73,7 +73,7 @@ current process environment.
73
73
 
74
74
  | Skill | When | Produces | If missing |
75
75
  |---|---|---|---|
76
- | `pr-description-writer` | Before create or a full body rewrite | A reviewed title and body file | Stop the create or rewrite and report that authoring is required |
76
+ | `pr-description-writer` | Create or a full body rewrite, when you want the body drafted for you | A reviewed title and body file | Write the title and body yourself |
77
77
  | `privacy-hygiene` | Before any durable GitHub post | A clean body and repository privacy sweep | Stop before publication and report the missing gate |
78
78
  | `issue-tracker` | Issue create, edit, or comment requests | Issue state and issue URLs | Route the request there. |
79
79
  | `e-code-review` | Review or review/fix loop requests | Structured findings or a clean review result | Route the request there. |
@@ -97,10 +97,10 @@ request target. Create actions publish drafts. Record the target before any writ
97
97
 
98
98
  ### 2. Author the title and body
99
99
 
100
- For create and full body rewrite, invoke the installed
101
- `pr-description-writer`. Require its title and body file as the authoring
102
- output. A comment or review may use a supplied body file when it does not
103
- rewrite the pull request description.
100
+ For create and full body rewrite, write the title and a body file, or invoke
101
+ the installed `pr-description-writer` to draft them from the diff. A comment or
102
+ review may use a supplied body file when it does not rewrite the pull request
103
+ description.
104
104
 
105
105
  ### 3. Run the local linter
106
106
 
@@ -122,8 +122,9 @@ permanent URLs. Rerun the linter after changing the body.
122
122
 
123
123
  ### 5. Resolve the process-local author
124
124
 
125
- Run `.agents/skills/pull-request/scripts/pull_request.py` with one structured
126
- action and the selected author. The command runs the linter again, resolves
125
+ Run `~/.agents/skills/pull-request/scripts/pull_request.py` (under the agents
126
+ home, not the repository; a worktree holds no `.agents/` copy) with one
127
+ structured action and the selected author. The command runs the linter again, resolves
127
128
  the author value, and passes it only to the child `gh` process. It never
128
129
  changes the parent environment or calls `gh auth switch`.
129
130
 
@@ -6,7 +6,7 @@ reason when the task is conditional. This file is a task seed catalog, not a
6
6
  markdown progress board.
7
7
 
8
8
  1. Resolve one repository, action, and selected author. For create, resolve the source branch, base, and head. For other actions, resolve one existing pull request target. Create actions publish drafts.
9
- 2. Invoke the required `pr-description-writer` for create or full body rewrite, then record its title and body file. Mark `N/A` for comment or review bodies that do not rewrite the pull request description.
9
+ 2. Record the title and body file for create or full body rewrite, written by you or drafted by `pr-description-writer`. Mark `N/A` for comment or review bodies that do not rewrite the pull request description.
10
10
  3. Validate every title, body, artifact, and local path input. Reject inline body input.
11
11
  4. Resolve the active managed root and run `<managed-root>/scripts/durable_post_lint.py` for the matching action. Record exit code `0` before credential lookup or network work.
12
12
  5. Run `privacy-hygiene` before the GitHub post and record its clean result.
@@ -26,9 +26,8 @@ One warm advisor at the strongest tier this session can reach. This session is t
26
26
  1. Name the session identity first (protocol **Host profiles**), then walk the model floor.
27
27
  2. Claude: Fable first in-session at `ADVISOR_EFFORT` (default low). When Fable is out of usage, bind Astra at the same effort. Codex: Astra in-session. Third-party: headless Fable then Astra. When the host's walk fails, fail closed.
28
28
  3. Name: `team-advisor-agent` on Claude (Agent spawn of `session-advisor`); a native Astra subagent on Codex; one CLI `session_id` on a third-party host via the protocol Claude-chain or Astra helper.
29
- 4. A Fable-tier spawn or re-spawn carries the exact token `FABLE-SPAWN-AUTHORIZED` in its prompt (protocol warm-up; `fable_spawn_gate` requires it).
30
- 5. Skip the multi-consumer "who you are" opener. This session is the sole consumer.
31
- 6. When the bind or reply path fails, fail closed and report to the user. On a third-party host, only the bound advisor issues ENDORSE / CORRECTION / PLAN / STOP.
29
+ 4. Skip the multi-consumer "who you are" opener. This session is the sole consumer.
30
+ 5. When the bind or reply path fails, fail closed and report to the user. On a third-party host, only the bound advisor issues ENDORSE / CORRECTION / PLAN / STOP.
32
31
 
33
32
  **GOTCHA (Cursor / ThirdParty + Astra):** when the walk reaches Astra or the user asks for Astra, first tool call is `python ~/.claude/_shared/advisor/scripts/codex_astra_advisor.py --bind --enable-astra --cwd <repo-root>` with the charter on stdin. Do not use Agent or Task. Do not search for a probe path. Details: [`third-party-bind.md`](../../_shared/advisor/reference/third-party-bind.md) GOTCHA and [`astra-rung.md`](../../_shared/advisor/reference/astra-rung.md).
34
33
 
@@ -104,7 +104,6 @@ The validator checks ladder shape only; host policy sits on top.
104
104
  ## Warm-up (once per session)
105
105
 
106
106
  On a **Claude host**, spawn `session-advisor` at the Fable alias with the charter as its prompt. When Fable is out of usage and the Astra rung is open, bind Astra through the Codex helper instead.
107
- A **Fable**-tier try carries the exact token `FABLE-SPAWN-AUTHORIZED` in its prompt. `hooks/blocking/fable_spawn_gate.py` denies a Fable-tier spawn without it.
108
107
  Full spawn fields and the charter template: [`reference/warm-up.md`](reference/warm-up.md).
109
108
 
110
109
  On a **Codex host**, spawn a native in-session Astra subagent with the same charter. Bind fields: [`reference/identity.md`](reference/identity.md) and [`reference/warm-up.md`](reference/warm-up.md).
@@ -10,7 +10,6 @@ Every other consumer (executors, or any other consulting session) reaches it by
10
10
  One shared advisor exists per orchestrated session, owned by the session that spawned it.
11
11
 
12
12
  **Re-spawn on drift.** If a reply shows the agent working from a stale picture, or the session pivots to an unrelated task, the owning session ends that agent and spawns a fresh one with a new charter.
13
- A **Fable**-tier re-spawn carries the exact token `FABLE-SPAWN-AUTHORIZED` in that fresh prompt, as a Fable-tier warm-up try does.
14
13
 
15
14
  ## Codex host
16
15
 
@@ -11,14 +11,13 @@ The consuming skill's session tries Fable first. Spawn with:
11
11
  - `model`: the short alias via `resolve_cli_model_id("Fable")` (alias table: [`cli-chain.md`](cli-chain.md)): `fable`. When Fable is out of usage and the Astra rung is open, continue with the Codex helper in [`astra-rung.md`](astra-rung.md) as the Astra fallback.
12
12
  - `name`: a name the session and every consumer will use to reach it (e.g. `team-advisor-agent`).
13
13
  - `run_in_background: true`.
14
- - `prompt`: the charter below. A **Fable**-tier try carries the exact token `FABLE-SPAWN-AUTHORIZED` in that prompt. `hooks/blocking/fable_spawn_gate.py` denies every `Agent` or `Task` spawn at `model: fable` whose prompt lacks that token. A try at any other tier needs no token.
14
+ - `prompt`: the charter below.
15
15
 
16
16
  Stop at the first successful bind. That try's tier is `selected_tier`; the warm advisor lives at that tier for the rest of the session.
17
17
 
18
18
  ## Charter (the spawn prompt)
19
19
 
20
20
  The charter states the agent's role, the repo path, and the session's current goal in two or three sentences. The agent is a standing reviewer. It answers only via SendMessage. File edits and commands are out of its scope.
21
- On a Fable-tier try, include the exact token `FABLE-SPAWN-AUTHORIZED` as plain text in this prompt (substring match; the gate reads the token alone, wherever it came from).
22
21
  State plainly:
23
22
 
24
23
  - Every consult carries: who is asking (name and assignment), what changed since their last consult, the live decision or question, and any load-bearing paths or excerpts.
@@ -389,10 +389,6 @@
389
389
  "id": "J3",
390
390
  "axis_name": "Constants location"
391
391
  },
392
- {
393
- "id": "J4",
394
- "axis_name": "File-global use-count"
395
- },
396
392
  {
397
393
  "id": "J5",
398
394
  "axis_name": "Abbreviations"
@@ -26,7 +26,7 @@
26
26
  | E6 | Removed-but-not-deleted symbol references | Symbols renamed/removed elsewhere with stale import or call sites left behind. |
27
27
  | E7 | Test fixtures / helpers defined but never used | Pytest fixtures, test data builders, mock factories with no callers. |
28
28
  | E8 | Stub / placeholder code without tracking | Judge `pass`, `...`, and `raise NotImplementedError` directly; track follow-up work outside source code. |
29
- | E9 | Constants-module exports with no importer | A module-level `UPPER_SNAKE` constant added to a `*_constants.py` / `config/` module that no module in the repo imports and that the constants file itself never references. The file-global use-count gate exempts a constants module because every name it exports legitimately carries zero in-file references, so a genuinely dead export slips past the write-time gate. Distinguish dead from live by grepping the whole repo for each constant name: a sibling such as `MEDIUM_TERMINAL` imported by a consumer module is live; a `MEDIUM_TEXT` that no `from ... import` line and no in-file reference names is dead (CODE_RULES 9.8). Remove the dead export. |
29
+ | E9 | Constants-module exports with no importer | A module-level `UPPER_SNAKE` constant added to a `*_constants.py` / `config/` module that no module in the repo imports and that the constants file itself never references. No write-time check reads a constants module, and every name such a module exports carries zero in-file references by design, so a dead export reaches the audit untouched. Distinguish dead from live by grepping the whole repo for each constant name: a sibling such as `MEDIUM_TERMINAL` imported by a consumer module is live; a `MEDIUM_TEXT` that no `from ... import` line and no in-file reference names is dead (CODE_RULES 9.8). Remove the dead export. |
30
30
 
31
31
  ---
32
32
 
@@ -5,7 +5,7 @@
5
5
  **Examples of Category J findings:**
6
6
  - A literal `60` appears in a production function body (magic value rule).
7
7
  - A new `MAX_RETRIES = 3` declared at module scope outside `config/`.
8
- - A parameter named `ctx` instead of `context` (abbreviation rule).
8
+ - A parameter named `ctx` where the name `context` belongs (abbreviation rule).
9
9
  - A function that returns a value with no return-type annotation.
10
10
  - A new `# explains the loop logic` comment added to production code.
11
11
 
@@ -20,7 +20,6 @@
20
20
  | J1 | Magic values in production function bodies | Literals other than `0`, `1`, `-1` inside production function bodies. Test files exempt. |
21
21
  | J2 | String-template magic | f-strings whose structural literal text (paths, URLs, patterns) belongs in `config/`. |
22
22
  | J3 | Constants location | Module-level `UPPER_SNAKE = ...` outside `config/` in production code. Exempt path families: `config/*`, `/migrations/`, `/workflow/`, `_tab.py`, `/states.py`, `/modules.py`, test files. |
23
- | J4 | File-global use-count | A file-global constant referenced by fewer than two methods/functions/classes in the same file. |
24
23
  | J5 | Abbreviations | `ctx`, `cfg`, `msg`, `btn`, `idx`, `cnt`, `elem`, `val`, `tmp`, `str`, `num`, `arr`, `obj`, `fn`, `cb`, `req`, `res`. (Loop counters `i`/`j`/`k` and `e` for exceptions are exempt.) |
25
24
  | J6 | Vague names | `result`, `data`, `output`, `response`, `value`, `item`, `temp`, `info`, `stuff`, `thing`. Vague prefixes: `handle`, `process`, `manage`, `do`. |
26
25
  | J7 | Type hints | Missing type annotation on a parameter or return; presence of `Any` or `# type: ignore`. |
@@ -26,7 +26,7 @@ Inline the artifact under this section using the section types defined in the ch
26
26
 
27
27
  **E2. Functions / methods defined but never called**
28
28
  - For every function, method, or callable defined or modified by the artifact, enumerate call sites: direct calls, callback registrations (`onerror=`, `key=`, signal handlers), decorator applications, `__main__` guards, framework-driven discovery (pytest collection, Django URL resolution, FastAPI route decorators, Click groups), and string-form references resolved at runtime (`getattr`, dispatch tables).
29
- - Leading-underscore names: a single internal call site is sufficient; explicitly verify nothing outside the file imports the underscore-prefixed name.
29
+ - Leading-underscore names: a single internal call site is enough; explicitly verify nothing outside the file imports the underscore-prefixed name.
30
30
  - Public names: confirm at least one call site inside or outside the file, or confirm the function is part of a documented public API.
31
31
  - Framework-discovered callables (test functions, route handlers, CLI commands): state which collector picks them up and confirm the artifact's path matches that collector's pattern.
32
32
 
@@ -59,7 +59,7 @@ Inline the artifact under this section using the section types defined in the ch
59
59
 
60
60
  **E7. Test fixtures / helpers defined but never used**
61
61
  - For every test file in scope, enumerate `@pytest.fixture`, `@pytest.fixture(scope=...)`, `setUp` / `tearDown` methods, factory helpers, mock builders, and module-level test constants. Verify each has at least one consumer test.
62
- - Module-level constants in test files: each must satisfy the file-global-constants use-count rule (≥2 references) OR be referenced by exactly one test plus one helper that the tests call.
62
+ - Module-level constants in test files: each must be referenced by at least two callers, OR by exactly one test plus one helper that the tests call.
63
63
  - Helpers defined inside a single test body that are never called within that body are dead.
64
64
  - Adversarial probes for proof-of-absence: (a) any test that defines a local helper (e.g., `def make_dir(...):`) and never calls it? (b) any imported test name from the production module that no test exercises? (c) any module-level constant whose call graph collapses to zero references after the diff?
65
65
 
@@ -71,7 +71,7 @@ Inline the artifact under this section using the section types defined in the ch
71
71
 
72
72
  **E9. Constants-module exports with no importer**
73
73
  - For every module-level `UPPER_SNAKE` constant the artifact adds to a `*_constants.py` or `config/` module, grep the whole repo for the constant name and locate at least one importer (`from <module> import <NAME>`) or in-file reference.
74
- - The file-global use-count gate exempts a constants module because every name it exports carries zero in-file references by design, so a genuinely dead export slips past the write-time gate; this sub-bucket is the audit-time backstop for that exemption.
74
+ - Every name a constants module exports carries zero in-file references by design and no write-time check reads such a module, so a dead export reaches the audit untouched; this sub-bucket is the audit-time backstop.
75
75
  - A sibling that a consumer module imports is live; a constant that no `from ... import` line and no in-file reference names anywhere in the repo is dead and must be removed (CODE_RULES 9.8).
76
76
  - Constants reached only by string-form lookup (`getattr(config, name)`, settings registries) are live; name the dynamic consumer when you mark such a constant referenced.
77
77
  - Adversarial probes for proof-of-absence: (a) does the artifact add any constant to a `*_constants.py` / `config/` module whose name returns zero hits outside its own definition line? (b) is any newly added constant shadowed by a same-named constant in a sibling module so the importer resolves the other one? (c) does any constant exist only as an `__all__` re-export with no downstream importer of that re-export?
@@ -156,7 +156,7 @@ ID prefix: `find`.
156
156
  **E7. Test fixtures / helpers defined but never used**
157
157
  - `test_sweep_empty_dirs.py` defines no `@pytest.fixture` decorators. Nothing to flag under "fixture with no consumers".
158
158
  - `_set_creation_time_windows` is the only test helper; it has three call sites (E2 above). Used.
159
- - `_SCRIPTS_DIR` (line 12) is a module-level constant used at line 13 (`if str(_SCRIPTS_DIR) not in sys.path:`) and line 14 (`sys.path.insert(0, str(_SCRIPTS_DIR))`). Two references; satisfies the file-global-constants use-count rule.
159
+ - `_SCRIPTS_DIR` (line 12) is a module-level constant used at line 13 (`if str(_SCRIPTS_DIR) not in sys.path:`) and line 14 (`sys.path.insert(0, str(_SCRIPTS_DIR))`). Two references; the constant earns its module scope.
160
160
  - No test data builders or mock factories are defined in this PR.
161
161
  - Adversarial probes for proof-of-absence: (a) does any of the five tests define a local helper inside its body that is then never called? (e.g., a `def make_dir(...):` defined but unused) — scan each test body. (b) does any test import a name from `sweep_empty_dirs` that it never uses? — only `sweep` is imported, and every test calls it. (c) does the `_SCRIPTS_DIR` block survive if pytest is invoked with `sweep_empty_dirs.py` already on `sys.path`? — yes, the membership-test guards the insert, so the constant is still meaningfully consumed even on the second run.
162
162
 
@@ -178,7 +178,7 @@ ID prefix: `find`.
178
178
 
179
179
  Q1: Are there imports unused locally but consumed by a re-export pattern in another file? Cite the cross-file pair if found. (Hypothesis: none — neither `sweep_empty_dirs.py` nor `test_sweep_empty_dirs.py` defines `__all__`, so re-export is not in play. `config/sweep_config.py` declares two constants that ARE consumed by `sweep_empty_dirs.py` lines 10-11; this is normal cross-file consumption, not a re-export.)
180
180
  Q2: What's the worst unused-code hazard introduced by this PR? Cite `<file>:<line>`. Candidates to evaluate: `arguments.interval` is parsed but unreachable on the `--once` path (line 57 short-circuits before the watch loop at line 67); the scheduled task always uses `--once`, so the `--interval` argparse declaration at line 45 is dead code on the only invocation path the installer creates. Decide: P2 (style) because the manual watch-mode path still uses it, vs P1 if you treat "the only installer path never exercises this branch" as functionally dead.
181
- Q3: Which symbol most likely will *become* dead code after a near-future refactor? Candidates: `_log_walk_error` (sole call site is the `os.walk(..., onerror=...)` kwarg — if a future refactor switches to `pathlib.Path.rglob` for walking, this helper has no other consumer and silently becomes orphaned); `DEFAULT_POLL_INTERVAL` (sole consumer is `_build_parser`'s `--interval` default — if Q2's hazard is resolved by removing `--interval` from the `--once`-only installer flow, this constant has zero consumers in the script and the file-global-constants use-count rule is broken).
181
+ Q3: Which symbol most likely will *become* dead code after a near-future refactor? Candidates: `_log_walk_error` (sole call site is the `os.walk(..., onerror=...)` kwarg — if a future refactor switches to `pathlib.Path.rglob` for walking, this helper has no other consumer and silently becomes orphaned); `DEFAULT_POLL_INTERVAL` (sole consumer is `_build_parser`'s `--interval` default — if Q2's hazard is resolved by removing `--interval` from the `--once`-only installer flow, this constant has zero consumers in the script).
182
182
 
183
183
  ## Output
184
184
 
@@ -50,11 +50,6 @@ A `test_*.py` name or a `.mjs` extension takes the line out of the write-time ga
50
50
  - Distinguish *imports* (`from config.X import FOO`) from *declarations* — imports are not J3-scope.
51
51
  - Adversarial probes: (a) does any module-level constant masquerade as an "import" via a re-export pattern? (b) is there a `_PRIVATE_UPPER` declaration that escapes the visual UPPER_SNAKE filter but is still module-level? (c) does any test file declare a constant that *would* be flagged if it were in production, indicating the constant probably belongs in `config/` even if test-exempt?
52
52
 
53
- **J4. File-global use-count**
54
- - For every file-global constant outside `config/`, count references in the same file. Single ref → move to `config/`. Zero refs → delete.
55
- - The rule applies to *constants*, not functions, classes, or imports.
56
- - Adversarial probes: (a) is any imported constant referenced only once in the importing file (suggesting the import itself is gratuitous)? (b) is any helper function defined in a production file but never called from inside the same file (separate dead-code concern, surfaced here for completeness)? (c) does any constant in `config/` get imported from zero call sites across the repo?
57
-
58
53
  **J5. Abbreviations**
59
54
  - Walk every parameter, local, and attribute name across production, test, and JavaScript changed lines. Flag: `ctx`, `cfg`, `msg`, `btn`, `idx`, `cnt`, `elem`, `val`, `tmp`, `str`, `num`, `arr`, `obj`, `fn`, `cb`, `req`, `res`. Loop counters `i`/`j`/`k` and `e` for exceptions are exempt.
60
55
  - This audit walks changed test-file and `.mjs` / `.js` lines for this rule, even though the write-time hook skips them.
@@ -101,7 +96,7 @@ A `test_*.py` name or a `.mjs` extension takes the line out of the write-time ga
101
96
 
102
97
  ## Cross-bucket questions to answer at the end
103
98
 
104
- Q1: Are there literals or names that span two sub-buckets (e.g., a magic value in J1 that also appears inside an f-string scrutinized by J2; an UPPER_SNAKE in J3 that also fails the use-count test in J4)? Cite the literal/name and both sub-bucket IDs.
99
+ Q1: Are there literals or names that span two sub-buckets (e.g., a magic value in J1 that also appears inside an f-string scrutinized by J2; an UPPER_SNAKE in J3 that is also an abbreviation under J5)? Cite the literal/name and both sub-bucket IDs.
105
100
 
106
101
  Q2: What is the worst CODE_RULES drift introduced by this artifact? Cite `<file>:<line>`. (Common candidates: cross-language duplicates, stale help text mirroring a config constant, abbreviations in a public API surface, bare `Any` annotations, hardcoded user paths in installer scripts.)
107
102
 
@@ -149,13 +144,6 @@ ID prefix: `find`.
149
144
  - `Install-SweepEmptyDirs.ps1` line 245 — `$TaskName = "SweepEmptyDirs"` is a PowerShell variable; J3 enforces Python module-level UPPER_SNAKE outside `config/`. PowerShell out of scope for J3.
150
145
  - Adversarial probes: (a) does any module-level constant in `sweep_empty_dirs.py` masquerade as an "import"? (b) is there any `_PRIVATE_UPPER` declaration that escapes the visual UPPER_SNAKE filter? (c) does the test file accidentally declare a constant that *would* be flagged if it were in production?
151
146
 
152
- **J4. File-global use-count**
153
- - For every file-global constant outside `config/`, count references in the same file. Single ref → move to `config/`. Zero refs → delete.
154
- - `sweep_empty_dirs.py` — no file-global constants declared (`DEFAULT_AGE_SECONDS` and `DEFAULT_POLL_INTERVAL` are imports). Imports follow standard import-usage rules, not the file-global use-count rule.
155
- - `config/sweep_config.py:142-143` — `DEFAULT_AGE_SECONDS` and `DEFAULT_POLL_INTERVAL` live in `config/`, exempt by location.
156
- - Test file `_SCRIPTS_DIR` — test files exempt.
157
- - Adversarial probes: (a) is any imported constant in `sweep_empty_dirs.py` referenced only once (line 104 for `DEFAULT_AGE_SECONDS`, line 108 for `DEFAULT_POLL_INTERVAL`) — wait, `DEFAULT_AGE_SECONDS` is referenced TWICE on line 104 (`default=DEFAULT_AGE_SECONDS`) and line 105 (`f"...default: {DEFAULT_AGE_SECONDS} = 2 minutes"`); `DEFAULT_POLL_INTERVAL` is referenced TWICE on line 108 and line 109. Both meet the ≥2-references threshold (note: file-global use-count technically applies to declarations, not imports — listed here for completeness). (b) is any helper function in `sweep_empty_dirs.py` defined but never called from inside the file? `_log_walk_error` (line 73) is referenced once at line 84; `_build_parser` (line 101) is referenced once at line 114. The use-count rule applies to *constants*, not functions, so neither is flagged. (c) does any constant in `config/sweep_config.py` get imported from zero call sites?
158
-
159
147
  **J5. Abbreviations**
160
148
  - Walk every parameter, local, and attribute name in `sweep_empty_dirs.py`.
161
149
  - `_log_walk_error(os_error: OSError)` — `os_error` is full word, not `e` (exception loop var allowed but not required). Verified clean.
@@ -247,11 +235,9 @@ import time
247
235
  from config.sweep_config import DEFAULT_AGE_SECONDS
248
236
  from config.sweep_config import DEFAULT_POLL_INTERVAL
249
237
 
250
-
251
238
  def _log_walk_error(os_error: OSError) -> None:
252
239
  print(f"warning: cannot scan {os_error.filename} — {os_error.strerror}", file=sys.stderr)
253
240
 
254
-
255
241
  def sweep(root: str, min_age_seconds: int) -> list[str]:
256
242
  """Remove empty directories under *root* older than *min_age_seconds*."""
257
243
 
@@ -275,7 +261,6 @@ def sweep(root: str, min_age_seconds: int) -> list[str]:
275
261
 
276
262
  return removed
277
263
 
278
-
279
264
  def _build_parser() -> argparse.ArgumentParser:
280
265
  parser = argparse.ArgumentParser(description="Delete empty directories older than a given age.")
281
266
  parser.add_argument("root", help="Root directory to scan")
@@ -287,7 +272,6 @@ def _build_parser() -> argparse.ArgumentParser:
287
272
  help=f"Poll interval in seconds when looping (default: {DEFAULT_POLL_INTERVAL})")
288
273
  return parser
289
274
 
290
-
291
275
  def main() -> None:
292
276
  parser = _build_parser()
293
277
  arguments = parser.parse_args()
@@ -309,7 +293,6 @@ def main() -> None:
309
293
  print("
310
294
  stopped.")
311
295
 
312
-
313
296
  if __name__ == "__main__":
314
297
  main()
315
298
  ```
@@ -342,7 +325,6 @@ if str(_SCRIPTS_DIR) not in sys.path:
342
325
 
343
326
  from sweep_empty_dirs import sweep # noqa: E402
344
327
 
345
-
346
328
  def _set_creation_time_windows(path: str, timestamp: float) -> None:
347
329
  dt = datetime.datetime.fromtimestamp(timestamp, tz=datetime.timezone.utc)
348
330
  date_str = dt.strftime("%Y-%m-%d %H:%M:%S")
@@ -352,7 +334,6 @@ def _set_creation_time_windows(path: str, timestamp: float) -> None:
352
334
  check=True, capture_output=True,
353
335
  )
354
336
 
355
-
356
337
  def test_deletes_empty_dir_older_than_threshold() -> None:
357
338
  with tempfile.TemporaryDirectory() as tmp:
358
339
  empty_dir = os.path.join(tmp, "old_empty")
@@ -362,7 +343,6 @@ def test_deletes_empty_dir_older_than_threshold() -> None:
362
343
  assert empty_dir in removed
363
344
  assert not os.path.isdir(empty_dir)
364
345
 
365
-
366
346
  def test_skips_empty_dir_newer_than_threshold() -> None:
367
347
  with tempfile.TemporaryDirectory() as tmp:
368
348
  fresh_dir = os.path.join(tmp, "fresh_empty")
@@ -371,7 +351,6 @@ def test_skips_empty_dir_newer_than_threshold() -> None:
371
351
  assert fresh_dir not in removed
372
352
  assert os.path.isdir(fresh_dir)
373
353
 
374
-
375
354
  def test_deletes_nested_empty_dirs() -> None:
376
355
  with tempfile.TemporaryDirectory() as tmp:
377
356
  leaf = os.path.join(tmp, "parent", "child", "leaf")
@@ -384,13 +363,11 @@ def test_deletes_nested_empty_dirs() -> None:
384
363
  assert os.path.join(tmp, "parent", "child") in removed
385
364
  assert os.path.join(tmp, "parent") in removed
386
365
 
387
-
388
366
  def test_empty_root_does_not_crash() -> None:
389
367
  with tempfile.TemporaryDirectory() as tmp:
390
368
  _set_creation_time_windows(tmp, time.time() - 300)
391
369
  sweep(tmp, min_age_seconds=120)
392
370
 
393
-
394
371
  def test_skips_nonempty_dir() -> None:
395
372
  with tempfile.TemporaryDirectory() as tmp:
396
373
  nonempty_dir = os.path.join(tmp, "has_stuff")
@@ -55,12 +55,7 @@ export const RUN_BACKUP_DIRECTORY_NAME_PATTERN =
55
55
  */
56
56
  export const PACKAGE_AGENTS_HOME_DIRECTORY_NAME = '.agents';
57
57
 
58
- export const PSTACK_MODEL_RULE_FILE_NAME = 'pstack-models.mdc';
59
58
  export const SUBAGENT_MODEL_POLICY_FILE_NAME = 'subagent-model-policy.json';
60
- export const PSTACK_SEEDED_MODEL_PREFERENCES_FILE_NAMES = Object.freeze([
61
- 'pstack-model-preferences.claude.json',
62
- 'pstack-model-preferences.codex.json',
63
- ]);
64
59
 
65
60
  /**
66
61
  * The directory name skill directories carry in a package source and under