devrites 3.2.24 → 3.2.26

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 (69) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1 -1
  3. package/engine/hooks_agent_dispatch.go +4 -3
  4. package/engine/tests/hook_test.go +8 -0
  5. package/pack/.claude/skills/devrites-lib/reference/standards/agents.md +5 -6
  6. package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +4 -5
  7. package/pack/generated/claude/skills/devrites-lib/reference/standards/agents.md +5 -6
  8. package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +4 -5
  9. package/pack/generated/codex/AGENTS.md +1 -1
  10. package/pack/generated/codex/skills/devrites-api-interface/SKILL.md +4 -4
  11. package/pack/generated/codex/skills/devrites-audit/SKILL.md +4 -4
  12. package/pack/generated/codex/skills/devrites-browser-proof/SKILL.md +4 -4
  13. package/pack/generated/codex/skills/devrites-debug-recovery/SKILL.md +4 -4
  14. package/pack/generated/codex/skills/devrites-doubt/SKILL.md +4 -4
  15. package/pack/generated/codex/skills/devrites-frontend-craft/SKILL.md +4 -4
  16. package/pack/generated/codex/skills/devrites-interview/SKILL.md +4 -4
  17. package/pack/generated/codex/skills/devrites-lib/SKILL.md +4 -4
  18. package/pack/generated/codex/skills/devrites-lib/reference/standards/agents.md +5 -6
  19. package/pack/generated/codex/skills/devrites-prose-craft/SKILL.md +4 -4
  20. package/pack/generated/codex/skills/devrites-refresh-indexes/SKILL.md +4 -4
  21. package/pack/generated/codex/skills/devrites-source-driven/SKILL.md +4 -4
  22. package/pack/generated/codex/skills/devrites-ux-shape/SKILL.md +4 -4
  23. package/pack/generated/codex/skills/rite/SKILL.md +4 -4
  24. package/pack/generated/codex/skills/rite-adopt/SKILL.md +4 -4
  25. package/pack/generated/codex/skills/rite-autocomplete/SKILL.md +4 -4
  26. package/pack/generated/codex/skills/rite-build/SKILL.md +4 -4
  27. package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +4 -5
  28. package/pack/generated/codex/skills/rite-clarify/SKILL.md +4 -4
  29. package/pack/generated/codex/skills/rite-converge/SKILL.md +4 -4
  30. package/pack/generated/codex/skills/rite-customize/SKILL.md +4 -4
  31. package/pack/generated/codex/skills/rite-define/SKILL.md +4 -4
  32. package/pack/generated/codex/skills/rite-doctor/SKILL.md +4 -4
  33. package/pack/generated/codex/skills/rite-dogfood/SKILL.md +4 -4
  34. package/pack/generated/codex/skills/rite-explain/SKILL.md +4 -4
  35. package/pack/generated/codex/skills/rite-frame/SKILL.md +4 -4
  36. package/pack/generated/codex/skills/rite-handoff/SKILL.md +4 -4
  37. package/pack/generated/codex/skills/rite-learn/SKILL.md +4 -4
  38. package/pack/generated/codex/skills/rite-plan/SKILL.md +4 -4
  39. package/pack/generated/codex/skills/rite-polish/SKILL.md +4 -4
  40. package/pack/generated/codex/skills/rite-pov/SKILL.md +4 -4
  41. package/pack/generated/codex/skills/rite-pr-feedback/SKILL.md +4 -4
  42. package/pack/generated/codex/skills/rite-pressure-test/SKILL.md +4 -4
  43. package/pack/generated/codex/skills/rite-prototype/SKILL.md +4 -4
  44. package/pack/generated/codex/skills/rite-prove/SKILL.md +4 -4
  45. package/pack/generated/codex/skills/rite-quick/SKILL.md +4 -4
  46. package/pack/generated/codex/skills/rite-resolve/SKILL.md +4 -4
  47. package/pack/generated/codex/skills/rite-review/SKILL.md +4 -4
  48. package/pack/generated/codex/skills/rite-seal/SKILL.md +4 -4
  49. package/pack/generated/codex/skills/rite-ship/SKILL.md +4 -4
  50. package/pack/generated/codex/skills/rite-spec/SKILL.md +4 -4
  51. package/pack/generated/codex/skills/rite-status/SKILL.md +4 -4
  52. package/pack/generated/codex/skills/rite-temper/SKILL.md +4 -4
  53. package/pack/generated/codex/skills/rite-upgrade/SKILL.md +4 -4
  54. package/pack/generated/codex/skills/rite-vet/SKILL.md +4 -4
  55. package/pack/generated/codex/skills/rite-zoom-out/SKILL.md +4 -4
  56. package/package.json +6 -2
  57. package/scripts/build-host-artifacts.sh +2 -2
  58. package/scripts/build-release-tarball.sh +4 -4
  59. package/scripts/check-generated-skill-budget.mjs +2 -1
  60. package/scripts/check-instruction-size-baseline.mjs +2 -1
  61. package/scripts/check-reference-governance.mjs +2 -1
  62. package/scripts/codex-generate.sh +6 -6
  63. package/scripts/pin.sh +70 -24
  64. package/scripts/run-tests.mjs +2 -1
  65. package/scripts/scan-pack-security.py +1 -1
  66. package/scripts/skill-pruning-audit.mjs +2 -1
  67. package/scripts/skills-inventory.mjs +2 -1
  68. package/scripts/validate-workflow-security.py +68 -9
  69. package/scripts/validate.sh +11 -9
@@ -15,11 +15,11 @@ This is the Codex mirror of a DevRites skill. In Codex:
15
15
  - For automatic Engram calls, omit optional `project` and `session_id` unless an exact value came from Engram or repository configuration. Never derive either from `task_name`, a run ID, directory name, or normalized slug. Call `mem_session_summary` without them by default; on `unknown_session` or `unknown_project`, retry once with both optional fields omitted. If auto-detection is ambiguous, ask the user instead of guessing.
16
16
  - Use the installed `devrites-engine` binary as the canonical runtime helper surface for orientation, gates, and state mutation.
17
17
  - **Invocation and dispatch are different:** invoke means run a skill in this context; dispatch means start a fresh agent with `spawn_agent`, await it, and reconcile its result. Never describe inline skill work as a dispatch.
18
- - On MultiAgent V2, call `spawn_agent` with the exact named `agent_type=devrites-<role>`, a unique `task_name`, and `fork_turns="none"`. Codex loads that role TOML's `developer_instructions` natively. Because V2 collaboration lifecycle calls bypass hooks, DevRites verifies the current durable parent/child rollout for the exact role, wait, completion, and non-empty delivered result.
19
- - On MultiAgent V1, when the named role is not exposed, use generic `explorer` for a read-only role with `fork_turns="none"` and name exactly one `.codex/agents/devrites-<role>.toml` contract in the message. Trusted `.codex/hooks.json` injects that contract's exact `developer_instructions` and binds the child to the fail-closed reviewer read-only guard.
20
- - On MultiAgent V1, `devrites-slice-wright` uses generic `worker` with `fork_turns="none"` and the exact role TOML named in the message. Trusted `.codex/hooks.json` binds it to the active reconcile window and `.wright-allowlist`; do not substitute `worker` for an exposed V2 named role.
18
+ - On MultiAgent V2, call `spawn_agent` with the exact named `agent_type=devrites-<role>`, a unique `task_name`, and `fork_turns="none"`. A missing visible `agent_type` field is still V2—not capability loss, V1, or HITL—so send it anyway. If the named call rejects it, stop before any generic/default spawn. Codex loads the role TOML's `developer_instructions` natively; DevRites verifies the durable rollout, wait, completion, and delivered result.
19
+ - Only after the runtime explicitly identifies MultiAgent V1, use generic `explorer` for a read-only role with `fork_turns="none"` and name exactly one `.codex/agents/devrites-<role>.toml` contract in the message. Trusted `.codex/hooks.json` injects that contract's exact `developer_instructions` and binds the child to the fail-closed reviewer read-only guard.
20
+ - On explicitly identified MultiAgent V1, `devrites-slice-wright` uses generic `worker` with `fork_turns="none"` and the exact role TOML named in the message. Trusted `.codex/hooks.json` binds it to the active reconcile window and `.wright-allowlist`.
21
21
  - The invoked skill's `required-agent-roles` frontmatter arms the fail-closed Stop receipt. Every listed role must have a confirmed start, wait, and non-empty result in this turn.
22
- - If any required named or generic agent dispatch is unavailable or rejected, stop for HITL. Never execute a DevRites specialist role in the root context.
22
+ - If the required dispatch for the explicitly identified runtime is unavailable or rejected, stop for HITL. Never switch runtime lanes. Never execute a DevRites specialist role in the root context.
23
23
  - Wait for every required fresh-context dispatch before reconciling or advancing. A backgrounded or lost result is incomplete.
24
24
  - Codex project hooks are installed in `.codex/hooks.json`; declared-leaf hooks are scoped inside `.codex/agents/devrites-*.toml`. Review and trust them with `/hooks` before relying on hook enforcement.
25
25
  - When this skill asks a HITL question via `AskUserQuestion`: Codex's equivalent (`request_user_input`) exists only in Plan mode. Outside Plan mode, render the option set as a plain numbered list in chat and **end the turn** so the human answers: NEVER silently pick an option yourself; auto-picking is AFK's contract, gated by the `.devrites/AFK` sentinel.
@@ -15,11 +15,11 @@ This is the Codex mirror of a DevRites skill. In Codex:
15
15
  - For automatic Engram calls, omit optional `project` and `session_id` unless an exact value came from Engram or repository configuration. Never derive either from `task_name`, a run ID, directory name, or normalized slug. Call `mem_session_summary` without them by default; on `unknown_session` or `unknown_project`, retry once with both optional fields omitted. If auto-detection is ambiguous, ask the user instead of guessing.
16
16
  - Use the installed `devrites-engine` binary as the canonical runtime helper surface for orientation, gates, and state mutation.
17
17
  - **Invocation and dispatch are different:** invoke means run a skill in this context; dispatch means start a fresh agent with `spawn_agent`, await it, and reconcile its result. Never describe inline skill work as a dispatch.
18
- - On MultiAgent V2, call `spawn_agent` with the exact named `agent_type=devrites-<role>`, a unique `task_name`, and `fork_turns="none"`. Codex loads that role TOML's `developer_instructions` natively. Because V2 collaboration lifecycle calls bypass hooks, DevRites verifies the current durable parent/child rollout for the exact role, wait, completion, and non-empty delivered result.
19
- - On MultiAgent V1, when the named role is not exposed, use generic `explorer` for a read-only role with `fork_turns="none"` and name exactly one `.codex/agents/devrites-<role>.toml` contract in the message. Trusted `.codex/hooks.json` injects that contract's exact `developer_instructions` and binds the child to the fail-closed reviewer read-only guard.
20
- - On MultiAgent V1, `devrites-slice-wright` uses generic `worker` with `fork_turns="none"` and the exact role TOML named in the message. Trusted `.codex/hooks.json` binds it to the active reconcile window and `.wright-allowlist`; do not substitute `worker` for an exposed V2 named role.
18
+ - On MultiAgent V2, call `spawn_agent` with the exact named `agent_type=devrites-<role>`, a unique `task_name`, and `fork_turns="none"`. A missing visible `agent_type` field is still V2—not capability loss, V1, or HITL—so send it anyway. If the named call rejects it, stop before any generic/default spawn. Codex loads the role TOML's `developer_instructions` natively; DevRites verifies the durable rollout, wait, completion, and delivered result.
19
+ - Only after the runtime explicitly identifies MultiAgent V1, use generic `explorer` for a read-only role with `fork_turns="none"` and name exactly one `.codex/agents/devrites-<role>.toml` contract in the message. Trusted `.codex/hooks.json` injects that contract's exact `developer_instructions` and binds the child to the fail-closed reviewer read-only guard.
20
+ - On explicitly identified MultiAgent V1, `devrites-slice-wright` uses generic `worker` with `fork_turns="none"` and the exact role TOML named in the message. Trusted `.codex/hooks.json` binds it to the active reconcile window and `.wright-allowlist`.
21
21
  - The invoked skill's `required-agent-roles` frontmatter arms the fail-closed Stop receipt. Every listed role must have a confirmed start, wait, and non-empty result in this turn.
22
- - If any required named or generic agent dispatch is unavailable or rejected, stop for HITL. Never execute a DevRites specialist role in the root context.
22
+ - If the required dispatch for the explicitly identified runtime is unavailable or rejected, stop for HITL. Never switch runtime lanes. Never execute a DevRites specialist role in the root context.
23
23
  - Wait for every required fresh-context dispatch before reconciling or advancing. A backgrounded or lost result is incomplete.
24
24
  - Codex project hooks are installed in `.codex/hooks.json`; declared-leaf hooks are scoped inside `.codex/agents/devrites-*.toml`. Review and trust them with `/hooks` before relying on hook enforcement.
25
25
  - When this skill asks a HITL question via `AskUserQuestion`: Codex's equivalent (`request_user_input`) exists only in Plan mode. Outside Plan mode, render the option set as a plain numbered list in chat and **end the turn** so the human answers: NEVER silently pick an option yourself; auto-picking is AFK's contract, gated by the `.devrites/AFK` sentinel.
@@ -16,11 +16,11 @@ This is the Codex mirror of a DevRites skill. In Codex:
16
16
  - For automatic Engram calls, omit optional `project` and `session_id` unless an exact value came from Engram or repository configuration. Never derive either from `task_name`, a run ID, directory name, or normalized slug. Call `mem_session_summary` without them by default; on `unknown_session` or `unknown_project`, retry once with both optional fields omitted. If auto-detection is ambiguous, ask the user instead of guessing.
17
17
  - Use the installed `devrites-engine` binary as the canonical runtime helper surface for orientation, gates, and state mutation.
18
18
  - **Invocation and dispatch are different:** invoke means run a skill in this context; dispatch means start a fresh agent with `spawn_agent`, await it, and reconcile its result. Never describe inline skill work as a dispatch.
19
- - On MultiAgent V2, call `spawn_agent` with the exact named `agent_type=devrites-<role>`, a unique `task_name`, and `fork_turns="none"`. Codex loads that role TOML's `developer_instructions` natively. Because V2 collaboration lifecycle calls bypass hooks, DevRites verifies the current durable parent/child rollout for the exact role, wait, completion, and non-empty delivered result.
20
- - On MultiAgent V1, when the named role is not exposed, use generic `explorer` for a read-only role with `fork_turns="none"` and name exactly one `.codex/agents/devrites-<role>.toml` contract in the message. Trusted `.codex/hooks.json` injects that contract's exact `developer_instructions` and binds the child to the fail-closed reviewer read-only guard.
21
- - On MultiAgent V1, `devrites-slice-wright` uses generic `worker` with `fork_turns="none"` and the exact role TOML named in the message. Trusted `.codex/hooks.json` binds it to the active reconcile window and `.wright-allowlist`; do not substitute `worker` for an exposed V2 named role.
19
+ - On MultiAgent V2, call `spawn_agent` with the exact named `agent_type=devrites-<role>`, a unique `task_name`, and `fork_turns="none"`. A missing visible `agent_type` field is still V2—not capability loss, V1, or HITL—so send it anyway. If the named call rejects it, stop before any generic/default spawn. Codex loads the role TOML's `developer_instructions` natively; DevRites verifies the durable rollout, wait, completion, and delivered result.
20
+ - Only after the runtime explicitly identifies MultiAgent V1, use generic `explorer` for a read-only role with `fork_turns="none"` and name exactly one `.codex/agents/devrites-<role>.toml` contract in the message. Trusted `.codex/hooks.json` injects that contract's exact `developer_instructions` and binds the child to the fail-closed reviewer read-only guard.
21
+ - On explicitly identified MultiAgent V1, `devrites-slice-wright` uses generic `worker` with `fork_turns="none"` and the exact role TOML named in the message. Trusted `.codex/hooks.json` binds it to the active reconcile window and `.wright-allowlist`.
22
22
  - The invoked skill's `required-agent-roles` frontmatter arms the fail-closed Stop receipt. Every listed role must have a confirmed start, wait, and non-empty result in this turn.
23
- - If any required named or generic agent dispatch is unavailable or rejected, stop for HITL. Never execute a DevRites specialist role in the root context.
23
+ - If the required dispatch for the explicitly identified runtime is unavailable or rejected, stop for HITL. Never switch runtime lanes. Never execute a DevRites specialist role in the root context.
24
24
  - Wait for every required fresh-context dispatch before reconciling or advancing. A backgrounded or lost result is incomplete.
25
25
  - Codex project hooks are installed in `.codex/hooks.json`; declared-leaf hooks are scoped inside `.codex/agents/devrites-*.toml`. Review and trust them with `/hooks` before relying on hook enforcement.
26
26
  - When this skill asks a HITL question via `AskUserQuestion`: Codex's equivalent (`request_user_input`) exists only in Plan mode. Outside Plan mode, render the option set as a plain numbered list in chat and **end the turn** so the human answers: NEVER silently pick an option yourself; auto-picking is AFK's contract, gated by the `.devrites/AFK` sentinel.
@@ -15,11 +15,11 @@ This is the Codex mirror of a DevRites skill. In Codex:
15
15
  - For automatic Engram calls, omit optional `project` and `session_id` unless an exact value came from Engram or repository configuration. Never derive either from `task_name`, a run ID, directory name, or normalized slug. Call `mem_session_summary` without them by default; on `unknown_session` or `unknown_project`, retry once with both optional fields omitted. If auto-detection is ambiguous, ask the user instead of guessing.
16
16
  - Use the installed `devrites-engine` binary as the canonical runtime helper surface for orientation, gates, and state mutation.
17
17
  - **Invocation and dispatch are different:** invoke means run a skill in this context; dispatch means start a fresh agent with `spawn_agent`, await it, and reconcile its result. Never describe inline skill work as a dispatch.
18
- - On MultiAgent V2, call `spawn_agent` with the exact named `agent_type=devrites-<role>`, a unique `task_name`, and `fork_turns="none"`. Codex loads that role TOML's `developer_instructions` natively. Because V2 collaboration lifecycle calls bypass hooks, DevRites verifies the current durable parent/child rollout for the exact role, wait, completion, and non-empty delivered result.
19
- - On MultiAgent V1, when the named role is not exposed, use generic `explorer` for a read-only role with `fork_turns="none"` and name exactly one `.codex/agents/devrites-<role>.toml` contract in the message. Trusted `.codex/hooks.json` injects that contract's exact `developer_instructions` and binds the child to the fail-closed reviewer read-only guard.
20
- - On MultiAgent V1, `devrites-slice-wright` uses generic `worker` with `fork_turns="none"` and the exact role TOML named in the message. Trusted `.codex/hooks.json` binds it to the active reconcile window and `.wright-allowlist`; do not substitute `worker` for an exposed V2 named role.
18
+ - On MultiAgent V2, call `spawn_agent` with the exact named `agent_type=devrites-<role>`, a unique `task_name`, and `fork_turns="none"`. A missing visible `agent_type` field is still V2—not capability loss, V1, or HITL—so send it anyway. If the named call rejects it, stop before any generic/default spawn. Codex loads the role TOML's `developer_instructions` natively; DevRites verifies the durable rollout, wait, completion, and delivered result.
19
+ - Only after the runtime explicitly identifies MultiAgent V1, use generic `explorer` for a read-only role with `fork_turns="none"` and name exactly one `.codex/agents/devrites-<role>.toml` contract in the message. Trusted `.codex/hooks.json` injects that contract's exact `developer_instructions` and binds the child to the fail-closed reviewer read-only guard.
20
+ - On explicitly identified MultiAgent V1, `devrites-slice-wright` uses generic `worker` with `fork_turns="none"` and the exact role TOML named in the message. Trusted `.codex/hooks.json` binds it to the active reconcile window and `.wright-allowlist`.
21
21
  - The invoked skill's `required-agent-roles` frontmatter arms the fail-closed Stop receipt. Every listed role must have a confirmed start, wait, and non-empty result in this turn.
22
- - If any required named or generic agent dispatch is unavailable or rejected, stop for HITL. Never execute a DevRites specialist role in the root context.
22
+ - If the required dispatch for the explicitly identified runtime is unavailable or rejected, stop for HITL. Never switch runtime lanes. Never execute a DevRites specialist role in the root context.
23
23
  - Wait for every required fresh-context dispatch before reconciling or advancing. A backgrounded or lost result is incomplete.
24
24
  - Codex project hooks are installed in `.codex/hooks.json`; declared-leaf hooks are scoped inside `.codex/agents/devrites-*.toml`. Review and trust them with `/hooks` before relying on hook enforcement.
25
25
  - When this skill asks a HITL question via `AskUserQuestion`: Codex's equivalent (`request_user_input`) exists only in Plan mode. Outside Plan mode, render the option set as a plain numbered list in chat and **end the turn** so the human answers: NEVER silently pick an option yourself; auto-picking is AFK's contract, gated by the `.devrites/AFK` sentinel.
@@ -16,11 +16,11 @@ This is the Codex mirror of a DevRites skill. In Codex:
16
16
  - For automatic Engram calls, omit optional `project` and `session_id` unless an exact value came from Engram or repository configuration. Never derive either from `task_name`, a run ID, directory name, or normalized slug. Call `mem_session_summary` without them by default; on `unknown_session` or `unknown_project`, retry once with both optional fields omitted. If auto-detection is ambiguous, ask the user instead of guessing.
17
17
  - Use the installed `devrites-engine` binary as the canonical runtime helper surface for orientation, gates, and state mutation.
18
18
  - **Invocation and dispatch are different:** invoke means run a skill in this context; dispatch means start a fresh agent with `spawn_agent`, await it, and reconcile its result. Never describe inline skill work as a dispatch.
19
- - On MultiAgent V2, call `spawn_agent` with the exact named `agent_type=devrites-<role>`, a unique `task_name`, and `fork_turns="none"`. Codex loads that role TOML's `developer_instructions` natively. Because V2 collaboration lifecycle calls bypass hooks, DevRites verifies the current durable parent/child rollout for the exact role, wait, completion, and non-empty delivered result.
20
- - On MultiAgent V1, when the named role is not exposed, use generic `explorer` for a read-only role with `fork_turns="none"` and name exactly one `.codex/agents/devrites-<role>.toml` contract in the message. Trusted `.codex/hooks.json` injects that contract's exact `developer_instructions` and binds the child to the fail-closed reviewer read-only guard.
21
- - On MultiAgent V1, `devrites-slice-wright` uses generic `worker` with `fork_turns="none"` and the exact role TOML named in the message. Trusted `.codex/hooks.json` binds it to the active reconcile window and `.wright-allowlist`; do not substitute `worker` for an exposed V2 named role.
19
+ - On MultiAgent V2, call `spawn_agent` with the exact named `agent_type=devrites-<role>`, a unique `task_name`, and `fork_turns="none"`. A missing visible `agent_type` field is still V2—not capability loss, V1, or HITL—so send it anyway. If the named call rejects it, stop before any generic/default spawn. Codex loads the role TOML's `developer_instructions` natively; DevRites verifies the durable rollout, wait, completion, and delivered result.
20
+ - Only after the runtime explicitly identifies MultiAgent V1, use generic `explorer` for a read-only role with `fork_turns="none"` and name exactly one `.codex/agents/devrites-<role>.toml` contract in the message. Trusted `.codex/hooks.json` injects that contract's exact `developer_instructions` and binds the child to the fail-closed reviewer read-only guard.
21
+ - On explicitly identified MultiAgent V1, `devrites-slice-wright` uses generic `worker` with `fork_turns="none"` and the exact role TOML named in the message. Trusted `.codex/hooks.json` binds it to the active reconcile window and `.wright-allowlist`.
22
22
  - The invoked skill's `required-agent-roles` frontmatter arms the fail-closed Stop receipt. Every listed role must have a confirmed start, wait, and non-empty result in this turn.
23
- - If any required named or generic agent dispatch is unavailable or rejected, stop for HITL. Never execute a DevRites specialist role in the root context.
23
+ - If the required dispatch for the explicitly identified runtime is unavailable or rejected, stop for HITL. Never switch runtime lanes. Never execute a DevRites specialist role in the root context.
24
24
  - Wait for every required fresh-context dispatch before reconciling or advancing. A backgrounded or lost result is incomplete.
25
25
  - Codex project hooks are installed in `.codex/hooks.json`; declared-leaf hooks are scoped inside `.codex/agents/devrites-*.toml`. Review and trust them with `/hooks` before relying on hook enforcement.
26
26
  - When this skill asks a HITL question via `AskUserQuestion`: Codex's equivalent (`request_user_input`) exists only in Plan mode. Outside Plan mode, render the option set as a plain numbered list in chat and **end the turn** so the human answers: NEVER silently pick an option yourself; auto-picking is AFK's contract, gated by the `.devrites/AFK` sentinel.
@@ -15,11 +15,11 @@ This is the Codex mirror of a DevRites skill. In Codex:
15
15
  - For automatic Engram calls, omit optional `project` and `session_id` unless an exact value came from Engram or repository configuration. Never derive either from `task_name`, a run ID, directory name, or normalized slug. Call `mem_session_summary` without them by default; on `unknown_session` or `unknown_project`, retry once with both optional fields omitted. If auto-detection is ambiguous, ask the user instead of guessing.
16
16
  - Use the installed `devrites-engine` binary as the canonical runtime helper surface for orientation, gates, and state mutation.
17
17
  - **Invocation and dispatch are different:** invoke means run a skill in this context; dispatch means start a fresh agent with `spawn_agent`, await it, and reconcile its result. Never describe inline skill work as a dispatch.
18
- - On MultiAgent V2, call `spawn_agent` with the exact named `agent_type=devrites-<role>`, a unique `task_name`, and `fork_turns="none"`. Codex loads that role TOML's `developer_instructions` natively. Because V2 collaboration lifecycle calls bypass hooks, DevRites verifies the current durable parent/child rollout for the exact role, wait, completion, and non-empty delivered result.
19
- - On MultiAgent V1, when the named role is not exposed, use generic `explorer` for a read-only role with `fork_turns="none"` and name exactly one `.codex/agents/devrites-<role>.toml` contract in the message. Trusted `.codex/hooks.json` injects that contract's exact `developer_instructions` and binds the child to the fail-closed reviewer read-only guard.
20
- - On MultiAgent V1, `devrites-slice-wright` uses generic `worker` with `fork_turns="none"` and the exact role TOML named in the message. Trusted `.codex/hooks.json` binds it to the active reconcile window and `.wright-allowlist`; do not substitute `worker` for an exposed V2 named role.
18
+ - On MultiAgent V2, call `spawn_agent` with the exact named `agent_type=devrites-<role>`, a unique `task_name`, and `fork_turns="none"`. A missing visible `agent_type` field is still V2—not capability loss, V1, or HITL—so send it anyway. If the named call rejects it, stop before any generic/default spawn. Codex loads the role TOML's `developer_instructions` natively; DevRites verifies the durable rollout, wait, completion, and delivered result.
19
+ - Only after the runtime explicitly identifies MultiAgent V1, use generic `explorer` for a read-only role with `fork_turns="none"` and name exactly one `.codex/agents/devrites-<role>.toml` contract in the message. Trusted `.codex/hooks.json` injects that contract's exact `developer_instructions` and binds the child to the fail-closed reviewer read-only guard.
20
+ - On explicitly identified MultiAgent V1, `devrites-slice-wright` uses generic `worker` with `fork_turns="none"` and the exact role TOML named in the message. Trusted `.codex/hooks.json` binds it to the active reconcile window and `.wright-allowlist`.
21
21
  - The invoked skill's `required-agent-roles` frontmatter arms the fail-closed Stop receipt. Every listed role must have a confirmed start, wait, and non-empty result in this turn.
22
- - If any required named or generic agent dispatch is unavailable or rejected, stop for HITL. Never execute a DevRites specialist role in the root context.
22
+ - If the required dispatch for the explicitly identified runtime is unavailable or rejected, stop for HITL. Never switch runtime lanes. Never execute a DevRites specialist role in the root context.
23
23
  - Wait for every required fresh-context dispatch before reconciling or advancing. A backgrounded or lost result is incomplete.
24
24
  - Codex project hooks are installed in `.codex/hooks.json`; declared-leaf hooks are scoped inside `.codex/agents/devrites-*.toml`. Review and trust them with `/hooks` before relying on hook enforcement.
25
25
  - When this skill asks a HITL question via `AskUserQuestion`: Codex's equivalent (`request_user_input`) exists only in Plan mode. Outside Plan mode, render the option set as a plain numbered list in chat and **end the turn** so the human answers: NEVER silently pick an option yourself; auto-picking is AFK's contract, gated by the `.devrites/AFK` sentinel.
@@ -16,11 +16,11 @@ This is the Codex mirror of a DevRites skill. In Codex:
16
16
  - For automatic Engram calls, omit optional `project` and `session_id` unless an exact value came from Engram or repository configuration. Never derive either from `task_name`, a run ID, directory name, or normalized slug. Call `mem_session_summary` without them by default; on `unknown_session` or `unknown_project`, retry once with both optional fields omitted. If auto-detection is ambiguous, ask the user instead of guessing.
17
17
  - Use the installed `devrites-engine` binary as the canonical runtime helper surface for orientation, gates, and state mutation.
18
18
  - **Invocation and dispatch are different:** invoke means run a skill in this context; dispatch means start a fresh agent with `spawn_agent`, await it, and reconcile its result. Never describe inline skill work as a dispatch.
19
- - On MultiAgent V2, call `spawn_agent` with the exact named `agent_type=devrites-<role>`, a unique `task_name`, and `fork_turns="none"`. Codex loads that role TOML's `developer_instructions` natively. Because V2 collaboration lifecycle calls bypass hooks, DevRites verifies the current durable parent/child rollout for the exact role, wait, completion, and non-empty delivered result.
20
- - On MultiAgent V1, when the named role is not exposed, use generic `explorer` for a read-only role with `fork_turns="none"` and name exactly one `.codex/agents/devrites-<role>.toml` contract in the message. Trusted `.codex/hooks.json` injects that contract's exact `developer_instructions` and binds the child to the fail-closed reviewer read-only guard.
21
- - On MultiAgent V1, `devrites-slice-wright` uses generic `worker` with `fork_turns="none"` and the exact role TOML named in the message. Trusted `.codex/hooks.json` binds it to the active reconcile window and `.wright-allowlist`; do not substitute `worker` for an exposed V2 named role.
19
+ - On MultiAgent V2, call `spawn_agent` with the exact named `agent_type=devrites-<role>`, a unique `task_name`, and `fork_turns="none"`. A missing visible `agent_type` field is still V2—not capability loss, V1, or HITL—so send it anyway. If the named call rejects it, stop before any generic/default spawn. Codex loads the role TOML's `developer_instructions` natively; DevRites verifies the durable rollout, wait, completion, and delivered result.
20
+ - Only after the runtime explicitly identifies MultiAgent V1, use generic `explorer` for a read-only role with `fork_turns="none"` and name exactly one `.codex/agents/devrites-<role>.toml` contract in the message. Trusted `.codex/hooks.json` injects that contract's exact `developer_instructions` and binds the child to the fail-closed reviewer read-only guard.
21
+ - On explicitly identified MultiAgent V1, `devrites-slice-wright` uses generic `worker` with `fork_turns="none"` and the exact role TOML named in the message. Trusted `.codex/hooks.json` binds it to the active reconcile window and `.wright-allowlist`.
22
22
  - The invoked skill's `required-agent-roles` frontmatter arms the fail-closed Stop receipt. Every listed role must have a confirmed start, wait, and non-empty result in this turn.
23
- - If any required named or generic agent dispatch is unavailable or rejected, stop for HITL. Never execute a DevRites specialist role in the root context.
23
+ - If the required dispatch for the explicitly identified runtime is unavailable or rejected, stop for HITL. Never switch runtime lanes. Never execute a DevRites specialist role in the root context.
24
24
  - Wait for every required fresh-context dispatch before reconciling or advancing. A backgrounded or lost result is incomplete.
25
25
  - Codex project hooks are installed in `.codex/hooks.json`; declared-leaf hooks are scoped inside `.codex/agents/devrites-*.toml`. Review and trust them with `/hooks` before relying on hook enforcement.
26
26
  - When this skill asks a HITL question via `AskUserQuestion`: Codex's equivalent (`request_user_input`) exists only in Plan mode. Outside Plan mode, render the option set as a plain numbered list in chat and **end the turn** so the human answers: NEVER silently pick an option yourself; auto-picking is AFK's contract, gated by the `.devrites/AFK` sentinel.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devrites",
3
- "version": "3.2.24",
3
+ "version": "3.2.26",
4
4
  "description": "DevRites: a disciplined senior-engineer workflow pack for Claude Code and Codex",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://github.com/ViktorsBaikers/DevRites#readme",
@@ -32,10 +32,14 @@
32
32
  "engine/",
33
33
  "pack/",
34
34
  "scripts/",
35
+ "!scripts/__pycache__/",
36
+ "!scripts/*.pyc",
37
+ "!scripts/.cache/",
35
38
  "install.sh",
36
39
  "uninstall.sh",
37
40
  "update.sh",
38
41
  "docs/",
42
+ "!docs/internal/",
39
43
  "README.md",
40
44
  "LICENSE",
41
45
  "SECURITY.md",
@@ -47,7 +51,7 @@
47
51
  },
48
52
  "scripts": {
49
53
  "prepare": "husky || true",
50
- "prepack": "bash scripts/build-host-artifacts.sh && rm -rf scripts/__pycache__ scripts/.cache docs/internal",
54
+ "prepack": "bash scripts/build-host-artifacts.sh",
51
55
  "commitlint": "commitlint --edit",
52
56
  "validate": "bash scripts/validate.sh",
53
57
  "test": "node scripts/run-tests.mjs",
@@ -4,7 +4,7 @@
4
4
  #
5
5
  # Default output is pack/generated/ so npm pack can ship prebuilt surfaces.
6
6
  # Tests may set DEVRITES_HOST_ARTIFACT_DIR to a temporary directory.
7
- set -u
7
+ set -euo pipefail
8
8
 
9
9
  ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
10
10
  PACK_SRC="$ROOT/pack/.claude"
@@ -13,7 +13,7 @@ OUT_ROOT="${DEVRITES_HOST_ARTIFACT_DIR:-$ROOT/pack/generated}"
13
13
  [ -d "$PACK_SRC/skills" ] || { echo "build-host-artifacts: missing $PACK_SRC/skills" >&2; exit 1; }
14
14
  [ -d "$PACK_SRC/agents" ] || { echo "build-host-artifacts: missing $PACK_SRC/agents" >&2; exit 1; }
15
15
 
16
- TMP_GEN_DIR="$(mktemp -d 2>/dev/null || echo "${TMPDIR:-/tmp}/devrites-host-artifacts.$$")"
16
+ TMP_GEN_DIR="$(mktemp -d)"
17
17
  cleanup() { rm -rf "$TMP_GEN_DIR"; }
18
18
  trap cleanup EXIT
19
19
 
@@ -57,14 +57,14 @@ PAYLOAD=(
57
57
 
58
58
  for item in "${PAYLOAD[@]}"; do
59
59
  if [[ -e "$item" ]]; then
60
- if [[ "$item" == "engine" ]] && git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
60
+ if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
61
61
  while IFS= read -r -d '' path; do
62
- [[ -e "$path" ]] || continue
62
+ [[ -e "$path" || -L "$path" ]] || continue
63
63
  if [[ "$path" == engine/testdata/golden/* ]]; then
64
64
  continue
65
65
  fi
66
66
  mkdir -p "$STAGE/$(dirname "$path")"
67
- cp "$path" "$STAGE/$path"
67
+ cp -P "$path" "$STAGE/$path"
68
68
  done < <(git ls-files -z -- "$item")
69
69
  else
70
70
  cp -R "$item" "$STAGE/"
@@ -73,7 +73,7 @@ for item in "${PAYLOAD[@]}"; do
73
73
  done
74
74
 
75
75
  # Include the same prebuilt host artifacts as the npm package.
76
- DEVRITES_HOST_ARTIFACT_DIR="$STAGE/pack/generated" bash "$ROOT/scripts/build-host-artifacts.sh" >/dev/null
76
+ DEVRITES_HOST_ARTIFACT_DIR="$STAGE/pack/generated" bash "$STAGE/scripts/build-host-artifacts.sh" >/dev/null
77
77
 
78
78
  # Remove development files copied with the payload.
79
79
  rm -rf "$STAGE/docs/internal" "$STAGE/scripts/.cache" 2>/dev/null || true
@@ -2,8 +2,9 @@
2
2
  // Guard generated host skill payloads against accidental context bloat.
3
3
  import { existsSync, readFileSync, readdirSync } from 'node:fs';
4
4
  import { join, relative } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
5
6
 
6
- const root = new URL('..', import.meta.url).pathname.replace(/\/$/, '');
7
+ const root = fileURLToPath(new URL('..', import.meta.url));
7
8
  const base = process.argv[2] || join(root, 'pack', '.claude', 'skills');
8
9
  const totalLimit = Number(process.env.DEVRITES_SKILL_TOTAL_BUDGET || 900_000);
9
10
  const fileLimit = Number(process.env.DEVRITES_SKILL_FILE_BUDGET || 64_000);
@@ -2,8 +2,9 @@
2
2
  // Track canonical instruction files individually and skills as a group.
3
3
  import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
4
4
  import { dirname, join, relative, resolve } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
5
6
 
6
- const defaultRoot = new URL('..', import.meta.url).pathname.replace(/\/$/, '');
7
+ const defaultRoot = fileURLToPath(new URL('..', import.meta.url));
7
8
  const argv = process.argv.slice(2);
8
9
  function option(name, fallback) {
9
10
  const i = argv.indexOf(name);
@@ -2,8 +2,9 @@
2
2
  // Ensure supporting skill references are reachable or explicitly time-bounded.
3
3
  import { existsSync, readFileSync, readdirSync } from 'node:fs';
4
4
  import { basename, dirname, join, normalize, relative, resolve, sep } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
5
6
 
6
- const defaultRoot = new URL('..', import.meta.url).pathname.replace(/\/$/, '');
7
+ const defaultRoot = fileURLToPath(new URL('..', import.meta.url));
7
8
  const argv = process.argv.slice(2);
8
9
  function option(name, fallback) {
9
10
  const i = argv.indexOf(name);
@@ -62,11 +62,11 @@ gen_codex_skill_file() {
62
62
  print "- For automatic Engram calls, omit optional `project` and `session_id` unless an exact value came from Engram or repository configuration. Never derive either from `task_name`, a run ID, directory name, or normalized slug. Call `mem_session_summary` without them by default; on `unknown_session` or `unknown_project`, retry once with both optional fields omitted. If auto-detection is ambiguous, ask the user instead of guessing."
63
63
  print "- Use the installed `devrites-engine` binary as the canonical runtime helper surface for orientation, gates, and state mutation."
64
64
  print "- **Invocation and dispatch are different:** invoke means run a skill in this context; dispatch means start a fresh agent with `spawn_agent`, await it, and reconcile its result. Never describe inline skill work as a dispatch."
65
- print "- On MultiAgent V2, call `spawn_agent` with the exact named `agent_type=devrites-<role>`, a unique `task_name`, and `fork_turns=\"none\"`. Codex loads that role TOML'\''s `developer_instructions` natively. Because V2 collaboration lifecycle calls bypass hooks, DevRites verifies the current durable parent/child rollout for the exact role, wait, completion, and non-empty delivered result."
66
- print "- On MultiAgent V1, when the named role is not exposed, use generic `explorer` for a read-only role with `fork_turns=\"none\"` and name exactly one `.codex/agents/devrites-<role>.toml` contract in the message. Trusted `.codex/hooks.json` injects that contract'\''s exact `developer_instructions` and binds the child to the fail-closed reviewer read-only guard."
67
- print "- On MultiAgent V1, `devrites-slice-wright` uses generic `worker` with `fork_turns=\"none\"` and the exact role TOML named in the message. Trusted `.codex/hooks.json` binds it to the active reconcile window and `.wright-allowlist`; do not substitute `worker` for an exposed V2 named role."
65
+ print "- On MultiAgent V2, call `spawn_agent` with the exact named `agent_type=devrites-<role>`, a unique `task_name`, and `fork_turns=\"none\"`. A missing visible `agent_type` field is still V2—not capability loss, V1, or HITL—so send it anyway. If the named call rejects it, stop before any generic/default spawn. Codex loads the role TOML'\''s `developer_instructions` natively; DevRites verifies the durable rollout, wait, completion, and delivered result."
66
+ print "- Only after the runtime explicitly identifies MultiAgent V1, use generic `explorer` for a read-only role with `fork_turns=\"none\"` and name exactly one `.codex/agents/devrites-<role>.toml` contract in the message. Trusted `.codex/hooks.json` injects that contract'\''s exact `developer_instructions` and binds the child to the fail-closed reviewer read-only guard."
67
+ print "- On explicitly identified MultiAgent V1, `devrites-slice-wright` uses generic `worker` with `fork_turns=\"none\"` and the exact role TOML named in the message. Trusted `.codex/hooks.json` binds it to the active reconcile window and `.wright-allowlist`."
68
68
  print "- The invoked skill'\''s `required-agent-roles` frontmatter arms the fail-closed Stop receipt. Every listed role must have a confirmed start, wait, and non-empty result in this turn."
69
- print "- If any required named or generic agent dispatch is unavailable or rejected, stop for HITL. Never execute a DevRites specialist role in the root context."
69
+ print "- If the required dispatch for the explicitly identified runtime is unavailable or rejected, stop for HITL. Never switch runtime lanes. Never execute a DevRites specialist role in the root context."
70
70
  print "- Wait for every required fresh-context dispatch before reconciling or advancing. A backgrounded or lost result is incomplete."
71
71
  print "- Codex project hooks are installed in `.codex/hooks.json`; declared-leaf hooks are scoped inside `.codex/agents/devrites-*.toml`. Review and trust them with `/hooks` before relying on hook enforcement."
72
72
  print "- When this skill asks a HITL question via `AskUserQuestion`: Codex'\''s equivalent (`request_user_input`) exists only in Plan mode. Outside Plan mode, render the option set as a plain numbered list in chat and **end the turn** so the human answers: NEVER silently pick an option yourself; auto-picking is AFK'\''s contract, gated by the `.devrites/AFK` sentinel."
@@ -81,7 +81,7 @@ gen_codex_skill_file() {
81
81
  print ""
82
82
  print "## Codex compatibility"
83
83
  print ""
84
- print "Load DevRites engineering standards from `.agents/skills/devrites-lib/reference/standards/`. Invocation runs a skill inline; dispatch starts a fresh agent with `spawn_agent` and must be awaited. On V2 use the exact named `agent_type=devrites-<role>`, a unique `task_name`, and `fork_turns=\"none\"`; on V1 use guarded `explorer`/`worker` with the unchanged packet and exact role TOML. If any required dispatch is unavailable or rejected, stop for HITL; never execute the specialist role in the root context."
84
+ print "Load DevRites engineering standards from `.agents/skills/devrites-lib/reference/standards/`. Invocation runs a skill inline; dispatch starts a fresh agent with `spawn_agent` and must be awaited. On V2 use the exact named `agent_type=devrites-<role>` even when hidden from the schema; omission is not V1 or HITL. Use guarded `explorer`/`worker` only after explicit V1 identification. If the selected runtime lane rejects dispatch, stop before switching lanes or running the role inline."
85
85
  }
86
86
  }
87
87
  ' "$_src" > "$_tmp"
@@ -177,7 +177,7 @@ This project has DevRites installed for both Claude Code and Codex.
177
177
  - For automatic Engram calls, omit optional `project` and `session_id` unless an exact value came from Engram or repository configuration. Never derive either from `task_name`, a run ID, directory name, or normalized slug. Call `mem_session_summary` without them by default; on `unknown_session` or `unknown_project`, retry once with both optional fields omitted. If auto-detection is ambiguous, ask the user instead of guessing.
178
178
  - Custom Codex subagents generated from the DevRites review agents live in `.codex/agents`.
179
179
  - In DevRites guidance, **invoke** means run a skill inline in the current context; **dispatch** means start a fresh agent with `spawn_agent`, wait for it, and reconcile its result.
180
- - Follow the invoked skill's generated **Codex compatibility** section for exact dispatch mechanics: V2 uses the named `devrites-<role>`; V1 alone may use the guarded generic compatibility path. Both use `fork_turns="none"`.
180
+ - Follow the invoked skill's generated **Codex compatibility** section for exact dispatch mechanics: V2 uses the named `devrites-<role>` even when `agent_type` is hidden from the visible schema; omission is not V1 or HITL. Only explicit V1 may use guarded generic compatibility. Both use `fork_turns="none"`.
181
181
  - The invoked skill's `required-agent-roles` frontmatter arms a fail-closed receipt. If a required dispatch is unavailable or rejected, stop for HITL; never execute a DevRites specialist role in the root context.
182
182
  - Claude Code agent hook metadata is not active in Codex. The generated Codex agents preserve read-only intent with Codex sandbox settings where possible; still follow DevRites' scope and no-mutation rules explicitly.
183
183
 
package/scripts/pin.sh CHANGED
@@ -29,7 +29,7 @@
29
29
  # - Manifest-managed: pinned wrappers are recorded in .claude/devrites.manifest
30
30
  # so the standard uninstall.sh cleans them up automatically.
31
31
 
32
- set -u
32
+ set -euo pipefail
33
33
 
34
34
  # ---- locate install-lib + load helpers ----------------------------------
35
35
  SELF_DIR="$( cd "$(dirname "$0")" && pwd -P )"
@@ -77,6 +77,16 @@ MF="$TARGET/$DR_MANIFEST_NAME"
77
77
  [ -f "$MF" ] || dr_die "no manifest at $MF: run install.sh first?"
78
78
 
79
79
  # ---- helpers -------------------------------------------------------------
80
+ PIN_TMP=""
81
+ cleanup_pin_tmp() {
82
+ [ -z "$PIN_TMP" ] || rm -rf "$PIN_TMP"
83
+ }
84
+ trap cleanup_pin_tmp EXIT
85
+
86
+ path_exists() {
87
+ [ -e "$1" ] || [ -L "$1" ]
88
+ }
89
+
80
90
  valid_alias_name() {
81
91
  # lowercase ASCII, digits, hyphens. No /, ., spaces. Not "rite" or "rite-*".
82
92
  case "$1" in
@@ -105,6 +115,20 @@ is_pinned_alias_file() {
105
115
  grep -q 'description: Alias of DevRites /' "$1"
106
116
  }
107
117
 
118
+ preflight_alias_destination() {
119
+ _dir="$1"
120
+ _file="$2"
121
+ if [ -L "$_dir" ] || { path_exists "$_dir" && [ ! -d "$_dir" ]; }; then
122
+ dr_die "$_dir exists and is not a managed alias directory: refusing to overwrite"
123
+ fi
124
+ if path_exists "$_file"; then
125
+ [ ! -L "$_file" ] && is_pinned_alias_file "$_file" \
126
+ || dr_die "$_file exists and is NOT a pinned alias: refusing to overwrite"
127
+ elif [ -d "$_dir" ]; then
128
+ dr_die "$_dir exists without a pinned alias: refusing to overwrite"
129
+ fi
130
+ }
131
+
108
132
  # ---- subcommands ---------------------------------------------------------
109
133
  do_add() {
110
134
  valid_alias_name "$ALIAS" || dr_die "invalid alias name '$ALIAS' (lowercase / digits / hyphens; not 'rite' or 'rite-*')"
@@ -118,34 +142,47 @@ do_add() {
118
142
  CODEX_ALIAS_FILE="$CODEX_ALIAS_DIR/SKILL.md"
119
143
  CODEX_ALIAS_REL=".agents/skills/$ALIAS/SKILL.md"
120
144
 
121
- if [ -e "$ALIAS_FILE" ]; then
122
- if is_pinned_alias "$ALIAS"; then
123
- dr_warn "already pinned: /$ALIAS: overwriting"
124
- else
125
- dr_die "$ALIAS_FILE exists and is NOT a pinned alias: refusing to overwrite"
126
- fi
145
+ preflight_alias_destination "$ALIAS_DIR" "$ALIAS_FILE"
146
+ if path_exists "$ALIAS_FILE"; then
147
+ dr_warn "already pinned: /$ALIAS: overwriting"
127
148
  fi
128
- if [ -d "$CODEX_SKILLS_DIR" ] && [ -e "$CODEX_ALIAS_FILE" ]; then
129
- if is_pinned_alias_file "$CODEX_ALIAS_FILE"; then
149
+ if [ -d "$CODEX_SKILLS_DIR" ]; then
150
+ preflight_alias_destination "$CODEX_ALIAS_DIR" "$CODEX_ALIAS_FILE"
151
+ if path_exists "$CODEX_ALIAS_FILE"; then
130
152
  dr_warn "already pinned for Codex: /$ALIAS: overwriting"
131
- else
132
- dr_die "$CODEX_ALIAS_FILE exists and is NOT a pinned alias: refusing to overwrite"
133
153
  fi
134
154
  fi
135
155
 
156
+ PIN_TMP="$(mktemp -d "$SKILLS_DIR/.devrites-pin.XXXXXX")"
157
+ dr_gen_alias_wrapper "$ALIAS" "$DEST" "$PIN_TMP/claude"
158
+ if [ -d "$CODEX_SKILLS_DIR" ]; then
159
+ dr_gen_alias_wrapper "$ALIAS" "$DEST" "$PIN_TMP/codex"
160
+ fi
161
+ cp -p "$MF" "$PIN_TMP/manifest"
162
+ if ! dr_manifest_contains "$PIN_TMP/manifest" "$ALIAS_REL"; then
163
+ printf '%s\n' "$ALIAS_REL" >> "$PIN_TMP/manifest"
164
+ fi
165
+ if [ -d "$CODEX_SKILLS_DIR" ] && ! dr_manifest_contains "$PIN_TMP/manifest" "$CODEX_ALIAS_REL"; then
166
+ printf '%s\n' "$CODEX_ALIAS_REL" >> "$PIN_TMP/manifest"
167
+ fi
168
+
136
169
  mkdir -p "$ALIAS_DIR"
137
- dr_gen_alias_wrapper "$ALIAS" "$DEST" "$ALIAS_FILE"
138
170
  if [ -d "$CODEX_SKILLS_DIR" ]; then
139
171
  mkdir -p "$CODEX_ALIAS_DIR"
140
- dr_gen_alias_wrapper "$ALIAS" "$DEST" "$CODEX_ALIAS_FILE"
141
172
  fi
142
-
143
- if ! dr_manifest_contains "$MF" "$ALIAS_REL"; then
144
- printf '%s\n' "$ALIAS_REL" >> "$MF"
173
+ [ -w "$ALIAS_DIR" ] || dr_die "$ALIAS_DIR is not writable"
174
+ [ -w "$(dirname "$MF")" ] || dr_die "$(dirname "$MF") is not writable"
175
+ if [ -d "$CODEX_SKILLS_DIR" ]; then
176
+ [ -w "$CODEX_ALIAS_DIR" ] || dr_die "$CODEX_ALIAS_DIR is not writable"
145
177
  fi
146
- if [ -d "$CODEX_SKILLS_DIR" ] && ! dr_manifest_contains "$MF" "$CODEX_ALIAS_REL"; then
147
- printf '%s\n' "$CODEX_ALIAS_REL" >> "$MF"
178
+
179
+ mv "$PIN_TMP/claude" "$ALIAS_FILE"
180
+ if [ -d "$CODEX_SKILLS_DIR" ]; then
181
+ mv "$PIN_TMP/codex" "$CODEX_ALIAS_FILE"
148
182
  fi
183
+ mv "$PIN_TMP/manifest" "$MF"
184
+ rm -rf "$PIN_TMP"
185
+ PIN_TMP=""
149
186
 
150
187
  if [ -d "$CODEX_SKILLS_DIR" ]; then
151
188
  dr_ok "pinned: /$ALIAS → /$DEST ($ALIAS_FILE, $CODEX_ALIAS_FILE)"
@@ -166,17 +203,24 @@ do_remove() {
166
203
  [ -f "$ALIAS_FILE" ] || dr_die "no pinned alias at $ALIAS_FILE"
167
204
  is_pinned_alias "$ALIAS" || dr_die "$ALIAS_FILE exists but is not a pinned alias: refusing to remove"
168
205
 
206
+ if path_exists "$CODEX_ALIAS_FILE"; then
207
+ is_pinned_alias_file "$CODEX_ALIAS_FILE" || dr_die "$CODEX_ALIAS_FILE exists but is not a pinned alias: refusing to remove"
208
+ fi
209
+
210
+ PIN_TMP="$(mktemp -d "$SKILLS_DIR/.devrites-pin.XXXXXX")"
211
+ awk -v claude="$ALIAS_REL" -v codex="$CODEX_ALIAS_REL" \
212
+ '$0 != claude && $0 != codex' "$MF" > "$PIN_TMP/manifest"
213
+
169
214
  rm -f "$ALIAS_FILE"
170
215
  rmdir "$ALIAS_DIR" 2>/dev/null || true
171
- if [ -f "$CODEX_ALIAS_FILE" ]; then
172
- is_pinned_alias_file "$CODEX_ALIAS_FILE" || dr_die "$CODEX_ALIAS_FILE exists but is not a pinned alias: refusing to remove"
216
+ if path_exists "$CODEX_ALIAS_FILE"; then
173
217
  rm -f "$CODEX_ALIAS_FILE"
174
218
  rmdir "$CODEX_ALIAS_DIR" 2>/dev/null || true
175
219
  fi
176
220
 
177
- # Drop the alias line from the manifest (preserve header + the rest)
178
- TMP="$(mktemp)"
179
- grep -Fvx "$ALIAS_REL" "$MF" | grep -Fvx "$CODEX_ALIAS_REL" > "$TMP" && mv "$TMP" "$MF"
221
+ mv "$PIN_TMP/manifest" "$MF"
222
+ rm -rf "$PIN_TMP"
223
+ PIN_TMP=""
180
224
 
181
225
  dr_ok "unpinned: /$ALIAS"
182
226
  }
@@ -192,7 +236,9 @@ do_list() {
192
236
  found=1
193
237
  fi
194
238
  done
195
- [ "$found" -eq 0 ] && dr_say "(no pinned aliases at $TARGET)"
239
+ if [ "$found" -eq 0 ]; then
240
+ dr_say "(no pinned aliases at $TARGET)"
241
+ fi
196
242
  }
197
243
 
198
244
  case "$SUBCMD" in
@@ -3,8 +3,9 @@ import { spawn } from 'node:child_process';
3
3
  import { existsSync, mkdtempSync, readFileSync, readdirSync, rmSync } from 'node:fs';
4
4
  import { tmpdir } from 'node:os';
5
5
  import { basename, join } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
6
7
 
7
- const root = new URL('..', import.meta.url).pathname.replace(/\/$/, '');
8
+ const root = fileURLToPath(new URL('..', import.meta.url));
8
9
  const testsDir = join(root, 'tests');
9
10
  const args = process.argv.slice(2);
10
11
  let jobs = Math.max(1, Math.min(5, Math.floor(Number(process.env.DEVRITES_TEST_JOBS || 4)) || 4));
@@ -151,7 +151,7 @@ def _file_suppressions(text):
151
151
 
152
152
  # --- driver ---------------------------------------------------------------
153
153
 
154
- TEXT_EXTS = {".md", ".sh", ".json", ".txt", ".py", ".js", ".yaml", ".yml", ""}
154
+ TEXT_EXTS = {".md", ".sh", ".json", ".toml", ".txt", ".py", ".js", ".yaml", ".yml", ""}
155
155
 
156
156
 
157
157
  def iter_files(paths):
@@ -2,8 +2,9 @@
2
2
  // Advisory pruning audit plus blocking ordered-step completion contracts.
3
3
  import { readdirSync, readFileSync, statSync } from 'node:fs';
4
4
  import { join, relative } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
5
6
 
6
- const root = new URL('..', import.meta.url).pathname.replace(/\/$/, '');
7
+ const root = fileURLToPath(new URL('..', import.meta.url));
7
8
  const skillsArg = process.argv.indexOf('--skills-dir');
8
9
  const skillsDir = skillsArg >= 0 ? process.argv[skillsArg + 1] : join(root, 'pack', '.claude', 'skills');
9
10
  const quiet = process.argv.includes('--quiet');
@@ -2,8 +2,9 @@
2
2
  // Verify the authored DevRites skill inventory and documentation counts.
3
3
  import { readdirSync, readFileSync, statSync } from 'node:fs';
4
4
  import { join, relative } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
5
6
 
6
- const root = new URL('..', import.meta.url).pathname.replace(/\/$/, '');
7
+ const root = fileURLToPath(new URL('..', import.meta.url));
7
8
  const skillsDir = join(root, 'pack', '.claude', 'skills');
8
9
  const docsSkills = join(root, 'docs', 'skills.md');
9
10
  const docsCommandMap = join(root, 'docs', 'command-map.md');