devrites 1.19.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.
- package/.claude-plugin/marketplace.json +24 -0
- package/.claude-plugin/plugin.json +43 -0
- package/CHANGELOG.md +391 -0
- package/LICENSE +56 -0
- package/NOTICE.md +18 -0
- package/README.md +582 -0
- package/SECURITY.md +193 -0
- package/bin/devrites.mjs +100 -0
- package/docs/architecture.md +272 -0
- package/docs/cli-mcp.md +57 -0
- package/docs/command-map.md +143 -0
- package/docs/flow.md +360 -0
- package/docs/release.md +29 -0
- package/docs/skills.md +214 -0
- package/docs/usage.md +325 -0
- package/install.sh +359 -0
- package/mcp/devrites-mcp.mjs +103 -0
- package/pack/.claude/agents/devrites-code-reviewer.md +50 -0
- package/pack/.claude/agents/devrites-doubt-reviewer.md +55 -0
- package/pack/.claude/agents/devrites-frontend-reviewer.md +52 -0
- package/pack/.claude/agents/devrites-performance-reviewer.md +47 -0
- package/pack/.claude/agents/devrites-plan-reviewer.md +79 -0
- package/pack/.claude/agents/devrites-security-auditor.md +53 -0
- package/pack/.claude/agents/devrites-simplifier-reviewer.md +75 -0
- package/pack/.claude/agents/devrites-slice-wright.md +181 -0
- package/pack/.claude/agents/devrites-spec-reviewer.md +72 -0
- package/pack/.claude/agents/devrites-strategy-reviewer.md +62 -0
- package/pack/.claude/agents/devrites-test-analyst.md +47 -0
- package/pack/.claude/hooks/devrites-a1-guard.sh +81 -0
- package/pack/.claude/hooks/devrites-allow.sh +44 -0
- package/pack/.claude/hooks/devrites-cursor.sh +28 -0
- package/pack/.claude/hooks/devrites-orient.sh +53 -0
- package/pack/.claude/hooks/devrites-redwatch.sh +39 -0
- package/pack/.claude/hooks/devrites-refresh-indexes.sh +127 -0
- package/pack/.claude/hooks/devrites-reviewer-readonly.sh +28 -0
- package/pack/.claude/hooks/devrites-statusline.sh +18 -0
- package/pack/.claude/hooks/devrites-stop-gate.sh +45 -0
- package/pack/.claude/hooks/devrites-wright-scope.sh +35 -0
- package/pack/.claude/hooks/hooks.json +52 -0
- package/pack/.claude/rules/README.md +48 -0
- package/pack/.claude/rules/afk-hitl.md +245 -0
- package/pack/.claude/rules/agents.md +98 -0
- package/pack/.claude/rules/anti-patterns.md +48 -0
- package/pack/.claude/rules/code-review.md +38 -0
- package/pack/.claude/rules/coding-style.md +55 -0
- package/pack/.claude/rules/context-hygiene.md +97 -0
- package/pack/.claude/rules/core.md +119 -0
- package/pack/.claude/rules/development-workflow.md +40 -0
- package/pack/.claude/rules/documentation.md +27 -0
- package/pack/.claude/rules/error-handling.md +33 -0
- package/pack/.claude/rules/git-workflow.md +35 -0
- package/pack/.claude/rules/hooks.md +38 -0
- package/pack/.claude/rules/patterns.md +45 -0
- package/pack/.claude/rules/performance.md +27 -0
- package/pack/.claude/rules/prose-style.md +101 -0
- package/pack/.claude/rules/security.md +63 -0
- package/pack/.claude/rules/testing.md +88 -0
- package/pack/.claude/rules/tooling.md +72 -0
- package/pack/.claude/settings.json +53 -0
- package/pack/.claude/skills/devrites-api-interface/SKILL.md +45 -0
- package/pack/.claude/skills/devrites-audit/SKILL.md +73 -0
- package/pack/.claude/skills/devrites-browser-proof/SKILL.md +38 -0
- package/pack/.claude/skills/devrites-debug-recovery/SKILL.md +50 -0
- package/pack/.claude/skills/devrites-debug-recovery/reference/build-the-loop.md +47 -0
- package/pack/.claude/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +17 -0
- package/pack/.claude/skills/devrites-debug-recovery/reference/hypotheses.md +17 -0
- package/pack/.claude/skills/devrites-debug-recovery/reference/instrumentation.md +21 -0
- package/pack/.claude/skills/devrites-debug-recovery/reference/regression-test.md +31 -0
- package/pack/.claude/skills/devrites-doubt/SKILL.md +75 -0
- package/pack/.claude/skills/devrites-frontend-craft/SKILL.md +96 -0
- package/pack/.claude/skills/devrites-frontend-craft/reference/craft.md +59 -0
- package/pack/.claude/skills/devrites-frontend-craft/reference/design-references.md +116 -0
- package/pack/.claude/skills/devrites-frontend-craft/reference/fullstack.md +45 -0
- package/pack/.claude/skills/devrites-frontend-craft/reference/quality-standards.md +215 -0
- package/pack/.claude/skills/devrites-frontend-craft/reference/reuse-first.md +59 -0
- package/pack/.claude/skills/devrites-frontend-craft/reference/shape.md +60 -0
- package/pack/.claude/skills/devrites-interview/SKILL.md +81 -0
- package/pack/.claude/skills/devrites-lib/SKILL.md +76 -0
- package/pack/.claude/skills/devrites-lib/scripts/analyze.sh +78 -0
- package/pack/.claude/skills/devrites-lib/scripts/check-acceptance.sh +75 -0
- package/pack/.claude/skills/devrites-lib/scripts/close-out.sh +47 -0
- package/pack/.claude/skills/devrites-lib/scripts/conventions.py +273 -0
- package/pack/.claude/skills/devrites-lib/scripts/coverage.sh +51 -0
- package/pack/.claude/skills/devrites-lib/scripts/devrites.sh +69 -0
- package/pack/.claude/skills/devrites-lib/scripts/doctor.sh +92 -0
- package/pack/.claude/skills/devrites-lib/scripts/evidence-fresh.sh +63 -0
- package/pack/.claude/skills/devrites-lib/scripts/footprint.sh +45 -0
- package/pack/.claude/skills/devrites-lib/scripts/learnings.sh +74 -0
- package/pack/.claude/skills/devrites-lib/scripts/mutation-gate.sh +52 -0
- package/pack/.claude/skills/devrites-lib/scripts/package-existence.sh +68 -0
- package/pack/.claude/skills/devrites-lib/scripts/preamble.sh +76 -0
- package/pack/.claude/skills/devrites-lib/scripts/progress.sh +103 -0
- package/pack/.claude/skills/devrites-lib/scripts/readiness.sh +62 -0
- package/pack/.claude/skills/devrites-lib/scripts/reconcile.sh +123 -0
- package/pack/.claude/skills/devrites-lib/scripts/resolve.sh +279 -0
- package/pack/.claude/skills/devrites-lib/scripts/stuck.sh +67 -0
- package/pack/.claude/skills/devrites-lib/scripts/test-integrity.sh +87 -0
- package/pack/.claude/skills/devrites-lib/scripts/tick-afk.sh +52 -0
- package/pack/.claude/skills/devrites-prose-craft/SKILL.md +105 -0
- package/pack/.claude/skills/devrites-prose-craft/reference/banned-phrases.md +95 -0
- package/pack/.claude/skills/devrites-prose-craft/reference/examples.md +88 -0
- package/pack/.claude/skills/devrites-prose-craft/reference/structures.md +134 -0
- package/pack/.claude/skills/devrites-refresh-indexes/SKILL.md +54 -0
- package/pack/.claude/skills/devrites-source-driven/SKILL.md +36 -0
- package/pack/.claude/skills/devrites-ux-shape/SKILL.md +121 -0
- package/pack/.claude/skills/devrites-ux-shape/reference/brief-template.md +93 -0
- package/pack/.claude/skills/devrites-ux-shape/reference/visual-direction-probe.md +48 -0
- package/pack/.claude/skills/rite/SKILL.md +135 -0
- package/pack/.claude/skills/rite/reference/menu.md +32 -0
- package/pack/.claude/skills/rite-adopt/SKILL.md +83 -0
- package/pack/.claude/skills/rite-adopt/reference/adoption.md +58 -0
- package/pack/.claude/skills/rite-adopt/reference/anti-patterns.md +19 -0
- package/pack/.claude/skills/rite-autocomplete/SKILL.md +96 -0
- package/pack/.claude/skills/rite-autocomplete/reference/decision-policy.md +35 -0
- package/pack/.claude/skills/rite-autocomplete/reference/loop.md +54 -0
- package/pack/.claude/skills/rite-autocomplete/reference/stop-conditions.md +59 -0
- package/pack/.claude/skills/rite-build/SKILL.md +261 -0
- package/pack/.claude/skills/rite-build/reference/afk-discipline.md +145 -0
- package/pack/.claude/skills/rite-build/reference/anti-patterns.md +25 -0
- package/pack/.claude/skills/rite-build/reference/checkpoint-protocol.md +149 -0
- package/pack/.claude/skills/rite-build/reference/evidence-standard.md +32 -0
- package/pack/.claude/skills/rite-build/reference/frontend-trigger.md +39 -0
- package/pack/.claude/skills/rite-build/reference/one-slice-cycle.md +38 -0
- package/pack/.claude/skills/rite-build/reference/spec-drift-guard.md +43 -0
- package/pack/.claude/skills/rite-build/reference/tdd.md +26 -0
- package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +115 -0
- package/pack/.claude/skills/rite-define/SKILL.md +157 -0
- package/pack/.claude/skills/rite-define/reference/anti-patterns.md +25 -0
- package/pack/.claude/skills/rite-define/reference/gates.md +152 -0
- package/pack/.claude/skills/rite-define/reference/plan-template.md +65 -0
- package/pack/.claude/skills/rite-doctor/SKILL.md +50 -0
- package/pack/.claude/skills/rite-frame/SKILL.md +116 -0
- package/pack/.claude/skills/rite-frame/reference/failure-modes.md +68 -0
- package/pack/.claude/skills/rite-handoff/SKILL.md +95 -0
- package/pack/.claude/skills/rite-handoff/reference/handoff-template.md +34 -0
- package/pack/.claude/skills/rite-learn/SKILL.md +82 -0
- package/pack/.claude/skills/rite-plan/SKILL.md +82 -0
- package/pack/.claude/skills/rite-plan/reference/anti-patterns.md +24 -0
- package/pack/.claude/skills/rite-plan/reference/dependency-graph.md +33 -0
- package/pack/.claude/skills/rite-plan/reference/replan-and-repair.md +42 -0
- package/pack/.claude/skills/rite-plan/reference/slicing.md +52 -0
- package/pack/.claude/skills/rite-plan/reference/task-breakdown.md +34 -0
- package/pack/.claude/skills/rite-polish/SKILL.md +90 -0
- package/pack/.claude/skills/rite-polish/reference/anti-ai-slop.md +177 -0
- package/pack/.claude/skills/rite-polish/reference/anti-patterns.md +27 -0
- package/pack/.claude/skills/rite-polish/reference/backend-polish.md +80 -0
- package/pack/.claude/skills/rite-polish/reference/browser-polish-evidence.md +31 -0
- package/pack/.claude/skills/rite-polish/reference/code.md +85 -0
- package/pack/.claude/skills/rite-polish/reference/design-system-discovery.md +35 -0
- package/pack/.claude/skills/rite-polish/reference/harden-checklist.md +109 -0
- package/pack/.claude/skills/rite-polish/reference/ui.md +136 -0
- package/pack/.claude/skills/rite-pressure-test/SKILL.md +43 -0
- package/pack/.claude/skills/rite-prototype/SKILL.md +87 -0
- package/pack/.claude/skills/rite-prove/SKILL.md +120 -0
- package/pack/.claude/skills/rite-prove/reference/anti-patterns.md +25 -0
- package/pack/.claude/skills/rite-prove/reference/browser-proof.md +26 -0
- package/pack/.claude/skills/rite-prove/reference/failure-triage.md +25 -0
- package/pack/.claude/skills/rite-prove/reference/proof-ladder.md +26 -0
- package/pack/.claude/skills/rite-prove/reference/test-command-discovery.md +30 -0
- package/pack/.claude/skills/rite-quick/SKILL.md +81 -0
- package/pack/.claude/skills/rite-resolve/SKILL.md +113 -0
- package/pack/.claude/skills/rite-resolve/reference/answer-protocol.md +114 -0
- package/pack/.claude/skills/rite-review/SKILL.md +170 -0
- package/pack/.claude/skills/rite-review/reference/anti-patterns.md +32 -0
- package/pack/.claude/skills/rite-review/reference/cognitive-load.md +90 -0
- package/pack/.claude/skills/rite-review/reference/feature-scoped-review.md +26 -0
- package/pack/.claude/skills/rite-review/reference/five-axis-review.md +46 -0
- package/pack/.claude/skills/rite-review/reference/nielsen-heuristics.md +130 -0
- package/pack/.claude/skills/rite-review/reference/parallel-dispatch.md +62 -0
- package/pack/.claude/skills/rite-review/reference/performance-review.md +28 -0
- package/pack/.claude/skills/rite-review/reference/security-review.md +32 -0
- package/pack/.claude/skills/rite-seal/SKILL.md +183 -0
- package/pack/.claude/skills/rite-seal/reference/anti-patterns.md +27 -0
- package/pack/.claude/skills/rite-seal/reference/conventions-ledger.md +63 -0
- package/pack/.claude/skills/rite-seal/reference/final-evidence.md +72 -0
- package/pack/.claude/skills/rite-seal/reference/go-no-go.md +37 -0
- package/pack/.claude/skills/rite-seal/reference/parallel-dispatch.md +69 -0
- package/pack/.claude/skills/rite-seal/reference/risk-and-rollback.md +30 -0
- package/pack/.claude/skills/rite-seal/reference/seal-template.md +36 -0
- package/pack/.claude/skills/rite-ship/SKILL.md +120 -0
- package/pack/.claude/skills/rite-ship/reference/anti-patterns.md +25 -0
- package/pack/.claude/skills/rite-ship/reference/close-out.md +31 -0
- package/pack/.claude/skills/rite-ship/reference/design-memory.md +120 -0
- package/pack/.claude/skills/rite-ship/reference/git-ship.md +42 -0
- package/pack/.claude/skills/rite-ship/reference/ship-template.md +33 -0
- package/pack/.claude/skills/rite-spec/SKILL.md +126 -0
- package/pack/.claude/skills/rite-spec/reference/acceptance-criteria.md +31 -0
- package/pack/.claude/skills/rite-spec/reference/anti-patterns.md +25 -0
- package/pack/.claude/skills/rite-spec/reference/interview-patterns.md +56 -0
- package/pack/.claude/skills/rite-spec/reference/investigation.md +64 -0
- package/pack/.claude/skills/rite-spec/reference/question-protocol.md +61 -0
- package/pack/.claude/skills/rite-spec/reference/references-intake.md +57 -0
- package/pack/.claude/skills/rite-spec/reference/spec-checklists.md +73 -0
- package/pack/.claude/skills/rite-spec/reference/spec-template.md +124 -0
- package/pack/.claude/skills/rite-spec/reference/state-workspace.md +159 -0
- package/pack/.claude/skills/rite-status/SKILL.md +101 -0
- package/pack/.claude/skills/rite-temper/SKILL.md +119 -0
- package/pack/.claude/skills/rite-temper/reference/anti-patterns.md +29 -0
- package/pack/.claude/skills/rite-temper/reference/review-dimensions.md +65 -0
- package/pack/.claude/skills/rite-temper/reference/scope-modes.md +53 -0
- package/pack/.claude/skills/rite-temper/reference/significance.md +46 -0
- package/pack/.claude/skills/rite-temper/reference/strategy-template.md +90 -0
- package/pack/.claude/skills/rite-vet/SKILL.md +155 -0
- package/pack/.claude/skills/rite-vet/reference/anti-patterns.md +29 -0
- package/pack/.claude/skills/rite-vet/reference/artifacts.md +135 -0
- package/pack/.claude/skills/rite-vet/reference/cross-model.md +41 -0
- package/pack/.claude/skills/rite-vet/reference/depth.md +53 -0
- package/pack/.claude/skills/rite-vet/reference/eng-lenses.md +48 -0
- package/pack/.claude/skills/rite-vet/reference/review-axes.md +167 -0
- package/pack/.claude/skills/rite-zoom-out/SKILL.md +75 -0
- package/package.json +68 -0
- package/scripts/build-release-tarball.sh +74 -0
- package/scripts/check-cross-refs.py +121 -0
- package/scripts/check-no-global-writes.sh +44 -0
- package/scripts/check-rule-uniqueness.sh +73 -0
- package/scripts/devrites-detect.sh +175 -0
- package/scripts/eval-runner.py +273 -0
- package/scripts/grade-feature.sh +104 -0
- package/scripts/install-lib.sh +83 -0
- package/scripts/pin.sh +166 -0
- package/scripts/render-eval-summary.py +48 -0
- package/scripts/run-evals.sh +149 -0
- package/scripts/run-outcome-evals.sh +49 -0
- package/scripts/scan-pack-security.py +209 -0
- package/scripts/scan-supply-chain-iocs.py +127 -0
- package/scripts/supply-chain-iocs.json +11 -0
- package/scripts/sync-version.sh +56 -0
- package/scripts/validate-frontmatter.py +149 -0
- package/scripts/validate-workflow-security.py +86 -0
- package/scripts/validate.sh +234 -0
- package/uninstall.sh +137 -0
- package/update.sh +196 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# DevRites PreToolUse hook — A1 pre-block: keep the /rite-build ORCHESTRATOR from editing
|
|
3
|
+
# source while a slice is mid-build. The slice-wright (a Task subagent) is the only sanctioned
|
|
4
|
+
# writer of code + tests; the orchestrator writes only .devrites/ bookkeeping. This hook is the
|
|
5
|
+
# optional pre-block companion to the reconcile.sh post-hoc gate (which always runs).
|
|
6
|
+
#
|
|
7
|
+
# Safety model: FAIL-OPEN, and OBSERVE by default.
|
|
8
|
+
# - It NEVER blocks unless ALL hold: a build window is open, the edit is to a source file,
|
|
9
|
+
# it came from the MAIN thread (no agent_id), and enforce mode is on.
|
|
10
|
+
# - On any doubt — no active workspace, no open window, no node, unparseable payload, a
|
|
11
|
+
# subagent caller, a .devrites/ path, the inline fallback — it exits 0 and stays silent.
|
|
12
|
+
# - Default mode is "observe": it LOGS what it would block (to .a1-guard.log) and allows.
|
|
13
|
+
# Flip to enforce only AFTER confirming the log never flags the wright's own edits — that
|
|
14
|
+
# confirms this Claude Code build populates agent_id for subagents (older builds may not;
|
|
15
|
+
# if the log flags wright edits, agent_id is unavailable here — stay in observe).
|
|
16
|
+
#
|
|
17
|
+
# Enable enforce (either one):
|
|
18
|
+
# export DEVRITES_A1_HOOK=enforce # session-wide
|
|
19
|
+
# touch .devrites/work/<slug>/.a1-enforce # per-feature
|
|
20
|
+
#
|
|
21
|
+
# Window: opened by `reconcile.sh snapshot` (writes .reconcile-base) before dispatch, closed by
|
|
22
|
+
# a clean `reconcile.sh check`. A violation leaves it open so inline patching stays blocked
|
|
23
|
+
# until the wright is re-dispatched. A stale window (abandoned build) → clear with
|
|
24
|
+
# `rm .devrites/work/<slug>/.reconcile-base` or re-run the check.
|
|
25
|
+
set -u
|
|
26
|
+
|
|
27
|
+
input="$(cat)"
|
|
28
|
+
|
|
29
|
+
# Fast path: must look like a tool call at all.
|
|
30
|
+
case "$input" in *'"tool_name"'*) : ;; *) exit 0 ;; esac
|
|
31
|
+
|
|
32
|
+
root="${CLAUDE_PROJECT_DIR:-$PWD}"
|
|
33
|
+
|
|
34
|
+
# No active DevRites feature → not our concern (keeps non-DevRites projects untouched).
|
|
35
|
+
[ -f "$root/.devrites/ACTIVE" ] || exit 0
|
|
36
|
+
slug="$(tr -d '[:space:]' < "$root/.devrites/ACTIVE" 2>/dev/null)"
|
|
37
|
+
[ -n "$slug" ] || exit 0
|
|
38
|
+
d="$root/.devrites/work/$slug"
|
|
39
|
+
|
|
40
|
+
# Mid-build window open? Only armed between a reconcile snapshot and its check. This scoping is
|
|
41
|
+
# what keeps the hook off /rite-polish, /rite-quick, and ordinary manual edits — they edit
|
|
42
|
+
# source from the main thread too, legitimately, and no window is open then.
|
|
43
|
+
[ -f "$d/.reconcile-base" ] || exit 0
|
|
44
|
+
|
|
45
|
+
# Inline fallback: the orchestrator is legitimately the writer (no wright was dispatched).
|
|
46
|
+
[ -f "$d/.reconcile-inline" ] && exit 0
|
|
47
|
+
|
|
48
|
+
# Parse with node (ships with Claude Code). No node or a parse failure → fail open.
|
|
49
|
+
command -v node >/dev/null 2>&1 || exit 0
|
|
50
|
+
parsed="$(printf '%s' "$input" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{const j=JSON.parse(s);const ti=j.tool_input||{};const fp=ti.file_path||ti.path||"";process.stdout.write([j.tool_name||"",fp,j.agent_id||""].join("\t"))}catch(e){}})' 2>/dev/null)"
|
|
51
|
+
[ -n "$parsed" ] || exit 0
|
|
52
|
+
IFS=$'\t' read -r tool fpath agent <<EOF
|
|
53
|
+
$parsed
|
|
54
|
+
EOF
|
|
55
|
+
|
|
56
|
+
# Only Edit-family writes can touch source.
|
|
57
|
+
case "$tool" in Edit|Write|MultiEdit|NotebookEdit) : ;; *) exit 0 ;; esac
|
|
58
|
+
|
|
59
|
+
# Subagent caller (the wright) → allow. agent_id is present only inside a subagent.
|
|
60
|
+
[ -n "$agent" ] && exit 0
|
|
61
|
+
|
|
62
|
+
# Can't tell the path → fail open.
|
|
63
|
+
[ -n "$fpath" ] || exit 0
|
|
64
|
+
case "$fpath" in /*) abs="$fpath" ;; *) abs="$root/$fpath" ;; esac
|
|
65
|
+
|
|
66
|
+
# Bookkeeping under .devrites/ is the orchestrator's own legit target → allow.
|
|
67
|
+
case "$abs" in "$root/.devrites/"*) exit 0 ;; esac
|
|
68
|
+
|
|
69
|
+
# Reaching here = the MAIN thread is about to edit a SOURCE file mid-build. That is the A1
|
|
70
|
+
# breach. Observe (default) logs and allows; enforce denies.
|
|
71
|
+
mode="${DEVRITES_A1_HOOK:-observe}"
|
|
72
|
+
[ -f "$d/.a1-enforce" ] && mode="enforce"
|
|
73
|
+
|
|
74
|
+
if [ "$mode" = "enforce" ]; then
|
|
75
|
+
printf '%s\n' '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"DevRites A1: the orchestrator must not edit source mid-build — the slice-wright is the only writer. Re-dispatch the wright (continue it once) or stop + escalate; do not patch the code yourself. (devrites-a1-guard)"}}'
|
|
76
|
+
exit 0
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
# Observe mode — record what enforce WOULD have blocked, then allow.
|
|
80
|
+
printf '%s\tWOULD-BLOCK\t%s\t%s\n' "$(date '+%Y-%m-%dT%H:%M:%S' 2>/dev/null || echo '?')" "$tool" "$abs" >> "$d/.a1-guard.log" 2>/dev/null || true
|
|
81
|
+
exit 0
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# DevRites PreToolUse hook — auto-approve the pack's READ-ONLY orientation/gate scripts so
|
|
3
|
+
# they stop triggering a permission prompt on every skill run.
|
|
4
|
+
#
|
|
5
|
+
# Safety model: FAIL-OPEN. This hook never denies and never blocks. It only ever EMITS an
|
|
6
|
+
# `allow` for a command that (a) runs one of five known read-only DevRites scripts AND
|
|
7
|
+
# (b) contains no dangerous/exfiltration tokens. On any doubt it prints nothing and exits 0, pack-scan-ignore: describes the hook's own exfil filter, not a payload
|
|
8
|
+
# which leaves Claude Code's normal permission flow untouched. Mutating DevRites scripts
|
|
9
|
+
# (resolve.sh / tick-afk.sh / close-out.sh) are intentionally NOT covered — they still prompt.
|
|
10
|
+
set -u
|
|
11
|
+
|
|
12
|
+
input="$(cat)"
|
|
13
|
+
|
|
14
|
+
# Fast path: 99% of Bash commands have nothing to do with DevRites — skip without spawning node.
|
|
15
|
+
case "$input" in *devrites-lib/scripts/*) : ;; *) exit 0 ;; esac
|
|
16
|
+
|
|
17
|
+
# Extract the actual command string. Use node (ships with Claude Code) for correct JSON;
|
|
18
|
+
# if it's unavailable or parsing fails, fall through to the normal prompt.
|
|
19
|
+
command -v node >/dev/null 2>&1 || exit 0
|
|
20
|
+
cmd="$(printf '%s' "$input" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{const j=JSON.parse(s);process.stdout.write((j.tool_input&&j.tool_input.command)||"")}catch(e){}})' 2>/dev/null)"
|
|
21
|
+
[ -n "$cmd" ] || exit 0
|
|
22
|
+
|
|
23
|
+
# (a) Must actually invoke one of the five read-only helpers.
|
|
24
|
+
case "$cmd" in
|
|
25
|
+
*devrites-lib/scripts/preamble.sh*|\
|
|
26
|
+
*devrites-lib/scripts/progress.sh*|\
|
|
27
|
+
*devrites-lib/scripts/readiness.sh*|\
|
|
28
|
+
*devrites-lib/scripts/evidence-fresh.sh*|\
|
|
29
|
+
*devrites-lib/scripts/check-acceptance.sh*) : ;;
|
|
30
|
+
*) exit 0 ;;
|
|
31
|
+
esac
|
|
32
|
+
|
|
33
|
+
# (b) Defense in depth — refuse to auto-approve a compound command that could do harm or
|
|
34
|
+
# exfiltrate. The legit resolver snippets only use `[ -f ]`, var assignment, `bash "$VAR"`,
|
|
35
|
+
# `echo`, and && / ||. Anything below -> fall through to a normal prompt.
|
|
36
|
+
case "$cmd" in
|
|
37
|
+
*" rm "*|*"rm -"*|*" mv "*|*" dd "*|*mkfs*|*" > /"*|*">/"*|*" >> "*|*">> "*|\
|
|
38
|
+
*chmod*|*chown*|*sudo*|*curl*|*wget*|*" scp "*|*" ssh "*|*'$('*|*'`'*|\
|
|
39
|
+
*eval*|*base64*|*xargs*|*"git push"*|*"git commit"*|*"git reset"*|*"git checkout"*|\
|
|
40
|
+
*" npm "*|*npx\ *|*pnpm*|*" yarn "*|*" pip "*|*python\ -c*|*node\ -e*|*perl\ *|*ruby\ -e*) exit 0 ;;
|
|
41
|
+
esac
|
|
42
|
+
|
|
43
|
+
printf '%s\n' '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","permissionDecisionReason":"DevRites read-only orientation/gate script — auto-approved by the devrites-allow hook"}}'
|
|
44
|
+
exit 0
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# DevRites UserPromptSubmit hook — re-inject the active feature's cursor each turn so the
|
|
3
|
+
# load-bearing next-action sits at the high-attention end of context (fights lost-in-the-middle
|
|
4
|
+
# on a long phase; pairs with the SessionStart orientation). Tiny + read-only; silent on
|
|
5
|
+
# non-DevRites projects. Its stdout is added to the model's context for this turn.
|
|
6
|
+
set -u
|
|
7
|
+
|
|
8
|
+
root="${CLAUDE_PROJECT_DIR:-$PWD}"
|
|
9
|
+
[ -f "$root/.devrites/ACTIVE" ] || exit 0
|
|
10
|
+
slug="$(tr -d '[:space:]' < "$root/.devrites/ACTIVE" 2>/dev/null)"
|
|
11
|
+
[ -n "$slug" ] || exit 0
|
|
12
|
+
d="$root/.devrites/work/$slug"
|
|
13
|
+
[ -d "$d" ] || exit 0
|
|
14
|
+
state="$d/state.md"
|
|
15
|
+
|
|
16
|
+
next="$(grep -iE '^[-[:space:]]*Next step:' "$state" 2>/dev/null | head -1 | sed -E 's/^[-[:space:]]*Next step:[[:space:]]*//')"
|
|
17
|
+
status="$(grep -iE '^[-[:space:]]*Status:' "$state" 2>/dev/null | head -1 | sed -E 's/^[-[:space:]]*Status:[[:space:]]*//')"
|
|
18
|
+
gates="$(grep -ciE '^[[:space:]]*status:[[:space:]]*open' "$d/questions.md" 2>/dev/null || echo 0)"
|
|
19
|
+
afk=""
|
|
20
|
+
[ -f "$root/.devrites/AFK" ] && afk="$(grep -iE 'AFK slices remaining:' "$state" 2>/dev/null | head -1 | sed -E 's/.*remaining:[[:space:]]*//')"
|
|
21
|
+
|
|
22
|
+
echo "DevRites cursor — active feature: $slug"
|
|
23
|
+
[ -n "$status" ] && echo " status: $status"
|
|
24
|
+
[ -n "$next" ] && echo " next: $next"
|
|
25
|
+
echo " open questions: ${gates:-0}"
|
|
26
|
+
[ -n "$afk" ] && echo " AFK slices remaining: $afk"
|
|
27
|
+
[ -f "$d/.red" ] && echo " ⚠ tests/build RED — resolve before stopping"
|
|
28
|
+
exit 0
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# DevRites SessionStart hook — two read-only jobs, both silent unless they have something to say:
|
|
3
|
+
# 1. surface DevRites health issues (stale ACTIVE, corrupt workspace, broken hook wiring, …)
|
|
4
|
+
# via doctor.sh — only when there ARE issues (silent-when-healthy);
|
|
5
|
+
# 2. inject the active feature's orientation digest so the model knows a workflow is in flight.
|
|
6
|
+
# Silent on non-DevRites projects and on any failure — this is an enhancement, never a
|
|
7
|
+
# dependency. Read-only; never blocks.
|
|
8
|
+
set -u
|
|
9
|
+
|
|
10
|
+
# Resolve shared scripts across install layouts (mirror the skill resolver).
|
|
11
|
+
P=".claude/skills/devrites-lib/scripts/preamble.sh"
|
|
12
|
+
[ -f "$P" ] || P="${CLAUDE_PLUGIN_ROOT:-}/pack/.claude/skills/devrites-lib/scripts/preamble.sh"
|
|
13
|
+
[ -f "$P" ] || P="pack/.claude/skills/devrites-lib/scripts/preamble.sh"
|
|
14
|
+
D=".claude/skills/devrites-lib/scripts/doctor.sh"
|
|
15
|
+
[ -f "$D" ] || D="${CLAUDE_PLUGIN_ROOT:-}/pack/.claude/skills/devrites-lib/scripts/doctor.sh"
|
|
16
|
+
[ -f "$D" ] || D="pack/.claude/skills/devrites-lib/scripts/doctor.sh"
|
|
17
|
+
L=".claude/skills/devrites-lib/scripts/learnings.sh"
|
|
18
|
+
[ -f "$L" ] || L="${CLAUDE_PLUGIN_ROOT:-}/pack/.claude/skills/devrites-lib/scripts/learnings.sh"
|
|
19
|
+
[ -f "$L" ] || L="pack/.claude/skills/devrites-lib/scripts/learnings.sh"
|
|
20
|
+
|
|
21
|
+
# Not a DevRites project -> stay silent.
|
|
22
|
+
[ -d ".devrites" ] || exit 0
|
|
23
|
+
|
|
24
|
+
# 1. Health sweep — prints issue lines only (empty when healthy).
|
|
25
|
+
health=""
|
|
26
|
+
[ -f "$D" ] && health="$(bash "$D" 2>/dev/null)"
|
|
27
|
+
|
|
28
|
+
# 2. Orientation digest — only when there's an active workspace.
|
|
29
|
+
digest=""
|
|
30
|
+
if [ -f "$P" ]; then
|
|
31
|
+
slug="$(cat .devrites/ACTIVE 2>/dev/null | tr -d '[:space:]')"
|
|
32
|
+
if [ -n "$slug" ] && [ -d ".devrites/work/$slug" ]; then
|
|
33
|
+
digest="$(bash "$P" 2>/dev/null)"
|
|
34
|
+
fi
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# 3. Learnings nudge — silent unless a pattern recurs across shipped features (snoozes after review).
|
|
38
|
+
nudge=""
|
|
39
|
+
[ -f "$L" ] && nudge="$(bash "$L" nudge 2>/dev/null)"
|
|
40
|
+
|
|
41
|
+
# Nothing to say -> stay silent.
|
|
42
|
+
[ -z "$health" ] && [ -z "$digest" ] && [ -z "$nudge" ] && exit 0
|
|
43
|
+
|
|
44
|
+
ctx=""
|
|
45
|
+
[ -n "$health" ] && ctx="⚠ DevRites health — issues found at session start (advisory; run /rite-doctor for the full report):"$'\n'"$health"$'\n\n'
|
|
46
|
+
[ -n "$digest" ] && ctx="${ctx}DevRites workflow active — live workspace orientation (from preamble.sh):"$'\n\n'"$digest"$'\n\n'"This is the current state; a /rite-* command will re-run its own step-0 preamble."
|
|
47
|
+
[ -n "$nudge" ] && ctx="${ctx}"$'\n\n'"$nudge"
|
|
48
|
+
|
|
49
|
+
# Emit as additionalContext, JSON-encoded via node (skip if node is absent — each skill's
|
|
50
|
+
# own step-0 preamble still orients, and the health block degrades to nothing).
|
|
51
|
+
command -v node >/dev/null 2>&1 || exit 0
|
|
52
|
+
printf '%s' "$ctx" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:s}}));});' 2>/dev/null || exit 0
|
|
53
|
+
exit 0
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# DevRites PostToolUse hook — fail-on-red sentinel. After a Bash command that runs the
|
|
3
|
+
# project's tests / build / typecheck / lint, set or clear .devrites/work/<slug>/.red so the
|
|
4
|
+
# Stop gate (devrites-stop-gate.sh) can refuse to end a turn while the suite is red. Turns
|
|
5
|
+
# "evidence over confidence / fail-on-red" from prose into a tracked flag. Heuristic on the
|
|
6
|
+
# command + its output; FAIL-OPEN and silent on any doubt.
|
|
7
|
+
set -u
|
|
8
|
+
|
|
9
|
+
input="$(cat)"
|
|
10
|
+
case "$input" in *'"tool_name"'*) : ;; *) exit 0 ;; esac
|
|
11
|
+
root="${CLAUDE_PROJECT_DIR:-$PWD}"
|
|
12
|
+
[ -f "$root/.devrites/ACTIVE" ] || exit 0
|
|
13
|
+
slug="$(tr -d '[:space:]' < "$root/.devrites/ACTIVE" 2>/dev/null)"
|
|
14
|
+
[ -n "$slug" ] || exit 0
|
|
15
|
+
d="$root/.devrites/work/$slug"
|
|
16
|
+
[ -d "$d" ] || exit 0
|
|
17
|
+
|
|
18
|
+
command -v node >/dev/null 2>&1 || exit 0
|
|
19
|
+
parsed="$(printf '%s' "$input" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{const j=JSON.parse(s);const ti=j.tool_input||{};let r=j.tool_response;if(r&&typeof r==="object")r=JSON.stringify(r);process.stdout.write((j.tool_name||"")+""+(ti.command||"")+""+(r||""))}catch(e){}})' 2>/dev/null)"
|
|
20
|
+
[ -n "$parsed" ] || exit 0
|
|
21
|
+
tool="${parsed%%$'\001'*}"; rest="${parsed#*$'\001'}"; cmd="${rest%%$'\001'*}"; out="${rest#*$'\001'}"
|
|
22
|
+
[ "$tool" = "Bash" ] || exit 0
|
|
23
|
+
|
|
24
|
+
# Only react to test/build/lint/typecheck commands.
|
|
25
|
+
printf '%s' "$cmd" | grep -qiE '(npm|pnpm|yarn|bun)([[:space:]]+run)?[[:space:]]+(test|build|lint|typecheck|check)|jest|vitest|pytest|go[[:space:]]+test|cargo[[:space:]]+(test|build|clippy)|\bmvn\b|gradle|eslint|ruff|mypy|\btsc\b|\bmake[[:space:]]+(test|build|check)' || exit 0
|
|
26
|
+
|
|
27
|
+
red="$d/.red"
|
|
28
|
+
combo="$cmd
|
|
29
|
+
$out"
|
|
30
|
+
if printf '%s' "$combo" | grep -qiE '(^|[^A-Za-z])FAIL([^A-Za-z]|$)|[1-9][0-9]*[[:space:]]+(failed|failing|failures?|errors?)|not ok|panic:|error TS[0-9]|Traceback|AssertionError|✗|✘|BUILD FAILED|exit code [1-9]'; then
|
|
31
|
+
printf '%s\n' "$cmd" > "$red" 2>/dev/null || true
|
|
32
|
+
safe="$(printf '%s' "$cmd" | head -c 80 | tr -d '"\\')"
|
|
33
|
+
printf '{"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"DevRites: tests/build are RED (%s). Fix to green or record the failure + next step in state.md before stopping — the Stop gate blocks an end-of-turn while .red is set."}}\n' "$safe" 2>/dev/null || true
|
|
34
|
+
exit 0
|
|
35
|
+
fi
|
|
36
|
+
if printf '%s' "$combo" | grep -qiE 'PASS(ED)?|0[[:space:]]+(failed|failing|errors?)|all tests passed|BUILD SUCC(ESS|EEDED)|succeeded|no (errors|problems|issues)|(^|[^a-z])ok([^a-z]|$)|✓'; then
|
|
37
|
+
rm -f "$red" 2>/dev/null || true
|
|
38
|
+
fi
|
|
39
|
+
exit 0
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# DevRites Stop hook — keep the code-intelligence indexes fresh after code changes, so the
|
|
3
|
+
# NEXT structural lookup (see .claude/rules/tooling.md) reads current code, not a stale graph.
|
|
4
|
+
# Reindexes the three optional indexes that track this repo — codebase-memory-mcp, codegraph,
|
|
5
|
+
# graphify — each only if its binary is on PATH AND it already tracks the repo. Missing = no-op.
|
|
6
|
+
#
|
|
7
|
+
# Enhancement, never a dependency: silent on projects with no index, FAIL-OPEN, NEVER blocks.
|
|
8
|
+
# The turn never waits — work runs in a DETACHED background process; the Stop hook returns at
|
|
9
|
+
# once. Self-guards on a change-stamp so idle turns cost nothing. ON by default; disable with
|
|
10
|
+
# DEVRITES_REFRESH_INDEXES=off. Does NOT touch agentmemory (separate, judgment-based store).
|
|
11
|
+
#
|
|
12
|
+
# Modes:
|
|
13
|
+
# (no args) trigger: guard on changes, then spawn the detached worker. Used by the hook.
|
|
14
|
+
# --force [ROOT] run synchronously NOW, ignore the change-guard, print a report (the skill).
|
|
15
|
+
# --worker ROOT internal: the reindex work itself. Detached.
|
|
16
|
+
set -u
|
|
17
|
+
|
|
18
|
+
[ "${DEVRITES_REFRESH_INDEXES:-on}" = "off" ] && exit 0
|
|
19
|
+
|
|
20
|
+
# ---- portable bounded-time runner (macOS has no `timeout` by default) ----
|
|
21
|
+
run_timeout() { # <seconds> <cmd...>
|
|
22
|
+
secs="$1"; shift
|
|
23
|
+
if command -v timeout >/dev/null 2>&1; then timeout "$secs" "$@"
|
|
24
|
+
elif command -v gtimeout >/dev/null 2>&1; then gtimeout "$secs" "$@"
|
|
25
|
+
else "$@"; fi
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# ---- state-file locations derived from a repo root (all gitignore-safe) ----
|
|
29
|
+
compute_state() { # <root>
|
|
30
|
+
ROOT="$1"
|
|
31
|
+
if [ -d "$ROOT/.codegraph" ]; then STATE="$ROOT/.codegraph"
|
|
32
|
+
elif [ -d "$ROOT/.git" ]; then STATE="$ROOT/.git"
|
|
33
|
+
else STATE="$ROOT"; fi
|
|
34
|
+
STAMP="$STATE/.index_refresh_stamp"
|
|
35
|
+
LOCK="$STATE/.index_refresh.lock"
|
|
36
|
+
LOG="$STATE/index_refresh.log"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# ---- is this repo tracked by codebase-memory-mcp? ----
|
|
40
|
+
# Cheap repo-local signal first (exported artifact); fall back to the project registry.
|
|
41
|
+
cbm_registered() { # <root>
|
|
42
|
+
command -v codebase-memory-mcp >/dev/null 2>&1 || return 1
|
|
43
|
+
[ -d "$1/.codebase-memory" ] && return 0
|
|
44
|
+
codebase-memory-mcp cli --raw list_projects 2>/dev/null | grep -qF "$1"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
# ---- the reindex work; runs detached (or synchronously under --force) ----
|
|
48
|
+
worker() { # <root>
|
|
49
|
+
_root="$1"
|
|
50
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo '?')] refresh start: $_root"
|
|
51
|
+
if cbm_registered "$_root"; then
|
|
52
|
+
echo "[codebase-memory] index_repository"
|
|
53
|
+
run_timeout 300 codebase-memory-mcp cli index_repository "{\"repo_path\": \"$_root\"}" 2>&1 \
|
|
54
|
+
|| echo "[codebase-memory] index failed or timed out"
|
|
55
|
+
fi
|
|
56
|
+
if [ -d "$_root/.codegraph" ] && command -v codegraph >/dev/null 2>&1; then
|
|
57
|
+
echo "[codegraph] sync"
|
|
58
|
+
run_timeout 120 codegraph sync "$_root" 2>&1 || echo "[codegraph] sync failed or timed out"
|
|
59
|
+
fi
|
|
60
|
+
if [ -d "$_root/graphify-out" ] && command -v graphify >/dev/null 2>&1; then
|
|
61
|
+
echo "[graphify] update"
|
|
62
|
+
run_timeout 300 graphify update "$_root" 2>&1 || echo "[graphify] update failed or timed out"
|
|
63
|
+
fi
|
|
64
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo '?')] refresh done"
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# ============================ entrypoint ============================
|
|
68
|
+
|
|
69
|
+
# --- worker branch: do the work, release the lock, exit ---
|
|
70
|
+
if [ "${1:-}" = "--worker" ]; then
|
|
71
|
+
ROOT="${2:-$PWD}"
|
|
72
|
+
compute_state "$ROOT"
|
|
73
|
+
worker "$ROOT"
|
|
74
|
+
rm -rf "$LOCK" 2>/dev/null || true
|
|
75
|
+
exit 0
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
# --- parse mode ---
|
|
79
|
+
FORCE=0
|
|
80
|
+
if [ "${1:-}" = "--force" ]; then FORCE=1; shift; fi
|
|
81
|
+
ROOT="${1:-${CLAUDE_PROJECT_DIR:-$PWD}}"
|
|
82
|
+
compute_state "$ROOT"
|
|
83
|
+
|
|
84
|
+
# --- availability guard: do nothing unless at least one index tracks this repo ---
|
|
85
|
+
if [ ! -d "$ROOT/.codegraph" ] && [ ! -d "$ROOT/graphify-out" ] && ! cbm_registered "$ROOT"; then
|
|
86
|
+
exit 0
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
# --- change guard: skip if nothing changed since last refresh (trigger mode only) ---
|
|
90
|
+
if [ "$FORCE" -ne 1 ] && [ -f "$STAMP" ]; then
|
|
91
|
+
changed=$(find "$ROOT" -type f -newer "$STAMP" \
|
|
92
|
+
-not -path '*/.git/*' \
|
|
93
|
+
-not -path '*/.codegraph/*' \
|
|
94
|
+
-not -path '*/graphify-out/*' \
|
|
95
|
+
-not -path '*/.codebase-memory/*' \
|
|
96
|
+
-not -path '*/.devrites/*' \
|
|
97
|
+
-not -path '*/node_modules/*' \
|
|
98
|
+
-not -path '*/.venv/*' \
|
|
99
|
+
-not -path '*/__pycache__/*' \
|
|
100
|
+
-not -path '*/.research/*' \
|
|
101
|
+
-not -path '*/.cursor/*' \
|
|
102
|
+
-not -path '*/dist/*' \
|
|
103
|
+
-not -path '*/build/*' \
|
|
104
|
+
-print 2>/dev/null | head -n 1)
|
|
105
|
+
[ -z "$changed" ] && exit 0
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
# --- lock: never run two refreshes at once; steal a lock older than 30 min ---
|
|
109
|
+
if ! mkdir "$LOCK" 2>/dev/null; then
|
|
110
|
+
if [ -d "$LOCK" ] && find "$LOCK" -prune -mmin +30 2>/dev/null | grep -q .; then
|
|
111
|
+
rm -rf "$LOCK" 2>/dev/null
|
|
112
|
+
mkdir "$LOCK" 2>/dev/null || exit 0
|
|
113
|
+
else
|
|
114
|
+
exit 0 # another refresh in progress
|
|
115
|
+
fi
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
# debounce: stamp now so a no-op turn doesn't re-trigger
|
|
119
|
+
touch "$STAMP" 2>/dev/null || true
|
|
120
|
+
|
|
121
|
+
if [ "$FORCE" -eq 1 ]; then
|
|
122
|
+
worker "$ROOT" # synchronous: the skill wants the report
|
|
123
|
+
rm -rf "$LOCK" 2>/dev/null || true
|
|
124
|
+
else
|
|
125
|
+
nohup "$0" --worker "$ROOT" >>"$LOG" 2>&1 & # detached: Stop returns immediately
|
|
126
|
+
fi
|
|
127
|
+
exit 0
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# DevRites subagent PreToolUse hook (wired from reviewer-agent frontmatter) — keep the
|
|
3
|
+
# read-only reviewers read-only: deny a Bash command that could mutate the tree or exfiltrate.
|
|
4
|
+
# A fresh-context reviewer reads untrusted source; a silent write path is a prompt-injection
|
|
5
|
+
# surface (rules/security.md). FAIL-OPEN, OBSERVE by default; DEVRITES_REVIEWER_RO=enforce.
|
|
6
|
+
set -u
|
|
7
|
+
|
|
8
|
+
input="$(cat)"
|
|
9
|
+
case "$input" in *'"tool_name"'*) : ;; *) exit 0 ;; esac
|
|
10
|
+
command -v node >/dev/null 2>&1 || exit 0
|
|
11
|
+
parsed="$(printf '%s' "$input" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{const j=JSON.parse(s);const ti=j.tool_input||{};process.stdout.write((j.tool_name||"")+""+(ti.command||""))}catch(e){}})' 2>/dev/null)"
|
|
12
|
+
[ -n "$parsed" ] || exit 0
|
|
13
|
+
tool="${parsed%%$'\001'*}"; cmd="${parsed#*$'\001'}"
|
|
14
|
+
[ "$tool" = "Bash" ] || exit 0
|
|
15
|
+
[ -n "$cmd" ] || exit 0
|
|
16
|
+
|
|
17
|
+
# Mutating / exfiltration tokens — reviewers only ever need read-only inspection. pack-scan-ignore: this is the hook's own defensive deny-list, not a payload.
|
|
18
|
+
printf '%s' "$cmd" | grep -qE '>>|[^0-9 ]>[^>&]|[[:space:]]-i([[:space:]]|$)|sed[[:space:]]+-i|\brm\b|\bmv\b|\btee\b|\btruncate\b|\bdd\b|chmod|chown|git[[:space:]]+(add|commit|push|reset|checkout|rm|mv|stash|tag|apply)|npm[[:space:]]+(install|i|publish|run)|pnpm[[:space:]]+(install|add)|yarn[[:space:]]+add|pip[[:space:]]+install|curl[[:space:]].*[[:space:]]-o\b|\bwget\b|\bscp\b|\bssh\b|\bnc\b' || exit 0
|
|
19
|
+
|
|
20
|
+
mode="${DEVRITES_REVIEWER_RO:-observe}"
|
|
21
|
+
if [ "$mode" = "enforce" ]; then
|
|
22
|
+
printf '%s\n' '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"DevRites: reviewers are read-only. This Bash command can mutate or exfiltrate; inspect with Read/Grep/Glob and return findings — do not modify the tree or reach the network. (devrites-reviewer-readonly)"}}'
|
|
23
|
+
exit 0
|
|
24
|
+
fi
|
|
25
|
+
root="${CLAUDE_PROJECT_DIR:-$PWD}"
|
|
26
|
+
slug="$(tr -d '[:space:]' < "$root/.devrites/ACTIVE" 2>/dev/null || true)"
|
|
27
|
+
[ -n "$slug" ] && printf '%s\tWOULD-BLOCK\t%s\n' "$(date '+%Y-%m-%dT%H:%M:%S' 2>/dev/null || echo '?')" "$(printf '%s' "$cmd" | head -c 80)" >> "$root/.devrites/work/$slug/.reviewer-ro.log" 2>/dev/null || true
|
|
28
|
+
exit 0
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# DevRites statusline — one-line workspace HUD for settings.json "statusLine". Ships UNWIRED
|
|
3
|
+
# (opt-in) so it never clobbers a user's own statusline; point a "statusLine" command at it to
|
|
4
|
+
# enable. Reads the session JSON on stdin (ignored) + .devrites/ and prints:
|
|
5
|
+
# DevRites: <slug> · <phase> · gates:<n> · AFK|HITL
|
|
6
|
+
set -u
|
|
7
|
+
|
|
8
|
+
cat >/dev/null 2>&1 || true
|
|
9
|
+
root="${CLAUDE_PROJECT_DIR:-$PWD}"
|
|
10
|
+
[ -f "$root/.devrites/ACTIVE" ] || exit 0
|
|
11
|
+
slug="$(tr -d '[:space:]' < "$root/.devrites/ACTIVE" 2>/dev/null)"
|
|
12
|
+
[ -n "$slug" ] || exit 0
|
|
13
|
+
d="$root/.devrites/work/$slug"; state="$d/state.md"
|
|
14
|
+
phase="$(grep -iE '^[-[:space:]]*Phase:' "$state" 2>/dev/null | head -1 | sed -E 's/^[-[:space:]]*Phase:[[:space:]]*//')"
|
|
15
|
+
gates="$(grep -ciE '^[[:space:]]*status:[[:space:]]*open' "$d/questions.md" 2>/dev/null || echo 0)"
|
|
16
|
+
mode="HITL"; [ -f "$root/.devrites/AFK" ] && mode="AFK"
|
|
17
|
+
red=""; [ -f "$d/.red" ] && red=" · RED"
|
|
18
|
+
printf 'DevRites: %s · %s · gates:%s · %s%s\n' "$slug" "${phase:-?}" "${gates:-0}" "$mode" "$red"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# DevRites Stop hook — state-integrity gate. Refuse to end the turn while the active workspace
|
|
3
|
+
# is provably inconsistent: tests are red (.red, set by devrites-redwatch.sh), or an open
|
|
4
|
+
# blocking/validating gate is not surfaced as Awaiting human. Turns "persistence before
|
|
5
|
+
# stopping" + "fail-on-red" from prose into a real gate. FAIL-OPEN, OBSERVE by default (logs to
|
|
6
|
+
# .stop-gate.log); enable with DEVRITES_STOP_GATE=enforce. Loop-guarded by stop_hook_active so
|
|
7
|
+
# it can never wedge the session.
|
|
8
|
+
set -u
|
|
9
|
+
|
|
10
|
+
input="$(cat)"
|
|
11
|
+
root="${CLAUDE_PROJECT_DIR:-$PWD}"
|
|
12
|
+
[ -f "$root/.devrites/ACTIVE" ] || exit 0
|
|
13
|
+
slug="$(tr -d '[:space:]' < "$root/.devrites/ACTIVE" 2>/dev/null)"
|
|
14
|
+
[ -n "$slug" ] || exit 0
|
|
15
|
+
d="$root/.devrites/work/$slug"
|
|
16
|
+
[ -d "$d" ] || exit 0
|
|
17
|
+
|
|
18
|
+
# Loop guard: if we already blocked once this stop cycle, let it stop.
|
|
19
|
+
case "$input" in *'"stop_hook_active":true'*) exit 0 ;; esac
|
|
20
|
+
|
|
21
|
+
reason=""
|
|
22
|
+
# 1) Red tests/build.
|
|
23
|
+
if [ -f "$d/.red" ]; then
|
|
24
|
+
rc="$(head -c 100 "$d/.red" 2>/dev/null | tr -d '"\\')"
|
|
25
|
+
reason="tests/build are RED ($rc) — fix to green, or record the failure + the next step in state.md, before stopping"
|
|
26
|
+
fi
|
|
27
|
+
# 2) Open blocking/validating gate not surfaced as awaiting_human.
|
|
28
|
+
if [ -z "$reason" ] && [ -f "$d/questions.md" ]; then
|
|
29
|
+
if grep -qiE '^[[:space:]]*gate:[[:space:]]*(blocking|validating)' "$d/questions.md" 2>/dev/null \
|
|
30
|
+
&& grep -qiE '^[[:space:]]*status:[[:space:]]*open' "$d/questions.md" 2>/dev/null; then
|
|
31
|
+
if ! grep -qiE 'Status:[[:space:]]*awaiting_human|## Awaiting human' "$d/state.md" 2>/dev/null; then
|
|
32
|
+
reason="an open blocking/validating question is not surfaced — write the Awaiting human block to state.md (and set Status: awaiting_human) before stopping"
|
|
33
|
+
fi
|
|
34
|
+
fi
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
[ -n "$reason" ] || exit 0
|
|
38
|
+
|
|
39
|
+
mode="${DEVRITES_STOP_GATE:-observe}"
|
|
40
|
+
if [ "$mode" = "enforce" ]; then
|
|
41
|
+
printf '{"decision":"block","reason":"DevRites stop-gate: %s. (devrites-stop-gate)"}\n' "$reason"
|
|
42
|
+
exit 0
|
|
43
|
+
fi
|
|
44
|
+
printf '%s\tWOULD-BLOCK\t%s\n' "$(date '+%Y-%m-%dT%H:%M:%S' 2>/dev/null || echo '?')" "$reason" >> "$d/.stop-gate.log" 2>/dev/null || true
|
|
45
|
+
exit 0
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# DevRites subagent PreToolUse hook (wired from devrites-slice-wright frontmatter) — fence the
|
|
3
|
+
# wright to its slice: deny Edit/Write to a path not listed in touched-files.md. Feature-scope
|
|
4
|
+
# enforced at the source, the in-subagent companion to the post-hoc reconcile.sh gate. FAIL-OPEN,
|
|
5
|
+
# OBSERVE by default (logs to .wright-scope.log); enable with DEVRITES_WRIGHT_SCOPE=enforce.
|
|
6
|
+
set -u
|
|
7
|
+
|
|
8
|
+
input="$(cat)"
|
|
9
|
+
case "$input" in *'"tool_name"'*) : ;; *) exit 0 ;; esac
|
|
10
|
+
root="${CLAUDE_PROJECT_DIR:-$PWD}"
|
|
11
|
+
[ -f "$root/.devrites/ACTIVE" ] || exit 0
|
|
12
|
+
slug="$(tr -d '[:space:]' < "$root/.devrites/ACTIVE" 2>/dev/null)"
|
|
13
|
+
[ -n "$slug" ] || exit 0
|
|
14
|
+
d="$root/.devrites/work/$slug"
|
|
15
|
+
tf="$d/touched-files.md"
|
|
16
|
+
[ -f "$tf" ] || exit 0
|
|
17
|
+
|
|
18
|
+
command -v node >/dev/null 2>&1 || exit 0
|
|
19
|
+
parsed="$(printf '%s' "$input" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{const j=JSON.parse(s);const ti=j.tool_input||{};process.stdout.write((j.tool_name||"")+""+(ti.file_path||ti.path||""))}catch(e){}})' 2>/dev/null)"
|
|
20
|
+
[ -n "$parsed" ] || exit 0
|
|
21
|
+
tool="${parsed%%$'\001'*}"; fpath="${parsed#*$'\001'}"
|
|
22
|
+
case "$tool" in Edit|Write|MultiEdit|NotebookEdit) : ;; *) exit 0 ;; esac
|
|
23
|
+
[ -n "$fpath" ] || exit 0
|
|
24
|
+
case "$fpath" in *.devrites/*) exit 0 ;; esac
|
|
25
|
+
|
|
26
|
+
rel="${fpath#"$root"/}"
|
|
27
|
+
if grep -qF "$rel" "$tf" 2>/dev/null || grep -qF "$fpath" "$tf" 2>/dev/null; then exit 0; fi
|
|
28
|
+
|
|
29
|
+
mode="${DEVRITES_WRIGHT_SCOPE:-observe}"
|
|
30
|
+
if [ "$mode" = "enforce" ]; then
|
|
31
|
+
printf '%s\n' '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"DevRites scope: this path is not in touched-files.md. Build only the slice contract; if the slice genuinely needs this file, return an Escalation so the orchestrator routes it through the Spec Drift Guard — do not widen scope yourself. (devrites-wright-scope)"}}'
|
|
32
|
+
exit 0
|
|
33
|
+
fi
|
|
34
|
+
printf '%s\tWOULD-BLOCK\t%s\n' "$(date '+%Y-%m-%dT%H:%M:%S' 2>/dev/null || echo '?')" "$fpath" >> "$d/.wright-scope.log" 2>/dev/null || true
|
|
35
|
+
exit 0
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"PreToolUse": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "Bash",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{ "type": "command", "command": "bash \"${CLAUDE_PLUGIN_ROOT}/pack/.claude/hooks/devrites-allow.sh\"" }
|
|
8
|
+
]
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"matcher": "Edit|Write|MultiEdit|NotebookEdit",
|
|
12
|
+
"hooks": [
|
|
13
|
+
{ "type": "command", "command": "bash \"${CLAUDE_PLUGIN_ROOT}/pack/.claude/hooks/devrites-a1-guard.sh\"" }
|
|
14
|
+
]
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"PostToolUse": [
|
|
18
|
+
{
|
|
19
|
+
"matcher": "Bash",
|
|
20
|
+
"hooks": [
|
|
21
|
+
{ "type": "command", "command": "bash \"${CLAUDE_PLUGIN_ROOT}/pack/.claude/hooks/devrites-redwatch.sh\"" }
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"Stop": [
|
|
26
|
+
{
|
|
27
|
+
"hooks": [
|
|
28
|
+
{ "type": "command", "command": "bash \"${CLAUDE_PLUGIN_ROOT}/pack/.claude/hooks/devrites-stop-gate.sh\"" }
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"hooks": [
|
|
33
|
+
{ "type": "command", "command": "bash \"${CLAUDE_PLUGIN_ROOT}/pack/.claude/hooks/devrites-refresh-indexes.sh\"" }
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"UserPromptSubmit": [
|
|
38
|
+
{
|
|
39
|
+
"hooks": [
|
|
40
|
+
{ "type": "command", "command": "bash \"${CLAUDE_PLUGIN_ROOT}/pack/.claude/hooks/devrites-cursor.sh\"" }
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
"SessionStart": [
|
|
45
|
+
{
|
|
46
|
+
"hooks": [
|
|
47
|
+
{ "type": "command", "command": "bash \"${CLAUDE_PLUGIN_ROOT}/pack/.claude/hooks/devrites-orient.sh\"" }
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# DevRites rules
|
|
2
|
+
|
|
3
|
+
Stack-agnostic engineering rules that DevRites installs to `.claude/rules/`. They encode
|
|
4
|
+
the standards the DevRites workflow holds code to — quality, safety, testing, and review
|
|
5
|
+
discipline that apply in any language.
|
|
6
|
+
|
|
7
|
+
These are **common** by design: nothing here assumes a specific framework or language.
|
|
8
|
+
Project-specific conventions always win where they exist (DevRites reads the codebase and
|
|
9
|
+
prefers what's already there).
|
|
10
|
+
|
|
11
|
+
## Loading model — progressive disclosure
|
|
12
|
+
|
|
13
|
+
To keep context lean, the rules follow Claude's progressive-disclosure pattern. There
|
|
14
|
+
are 18 rule files (plus this README index): each DevRites `rite-*` skill Reads
|
|
15
|
+
`.claude/rules/core.md` as its first step; the other 17 rule files load on demand by the
|
|
16
|
+
phase that needs them.
|
|
17
|
+
|
|
18
|
+
### Always-on (read by each `rite-*` skill as step 0)
|
|
19
|
+
|
|
20
|
+
| Rule | Covers |
|
|
21
|
+
|---|---|
|
|
22
|
+
| `core.md` | Operating rules, universal anti-rationalizations, one-line craft disciplines (fail-fast, reuse-first, test-behaviour-not-impl, trust boundary, measure-first), persistence + hygiene reminders. |
|
|
23
|
+
|
|
24
|
+
### On-demand (read by the phase / topic that needs it)
|
|
25
|
+
|
|
26
|
+
| Rule | Covers | Typical phase |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| `coding-style.md` | Naming, function shape, guard clauses, comments, simplicity, reuse-first. | `/rite-build`, `/rite-polish` Phase 1. |
|
|
29
|
+
| `prose-style.md` | Human-voice writing for artifacts + replies; two registers (prose vs technical); the LLM-tell cut-list. Depth in the `devrites-prose-craft` skill. | Any phase that writes prose — `/rite-spec`, `/rite-define`, `/rite-review`, `/rite-seal`, `/rite-ship`; `/rite-polish` Phase 1 as the catch. |
|
|
30
|
+
| `error-handling.md` | Fail fast, no silent catches, meaningful messages, fail closed. | `/rite-build`, `/rite-polish` (backend), `/rite-review`. |
|
|
31
|
+
| `testing.md` | Pyramid, behavior over implementation, determinism. | `/rite-build`, `/rite-prove`, `/rite-review`. |
|
|
32
|
+
| `code-review.md` | Small PRs, severity labels, what to check, actionable feedback. | `/rite-review`, `/rite-seal`. |
|
|
33
|
+
| `security.md` | Untrusted input, least privilege, secrets, three-tier trust boundary, fail closed. | When input / auth / data / integrations are in scope. |
|
|
34
|
+
| `performance.md` | Measure first, common pitfalls, prove the win. | When perf is in scope. |
|
|
35
|
+
| `patterns.md` | SOLID, composition, loose coupling, avoid over-engineering. | `/rite-build`, simplification audit. |
|
|
36
|
+
| `git-workflow.md` | Conventional Commits, atomic commits, small PRs. | `/rite-ship` commit / push / tag steps. |
|
|
37
|
+
| `hooks.md` | Stage checks by cost, fast local hooks, secret scanning. | Reference-only — read when setting up the project's git hooks; not auto-loaded by any phase. |
|
|
38
|
+
| `documentation.md` | Explain why, keep current, record decisions. | `/rite-spec`, `/rite-define`, `/rite-seal`. |
|
|
39
|
+
| `development-workflow.md` | Small batches, trunk-always-green, definition of done. | `/rite-define`, `/rite-plan`. |
|
|
40
|
+
| `agents.md` | DevRites review subagents + specialist skills, when to fan out. | `/rite-review`, `/rite-seal`. |
|
|
41
|
+
| `context-hygiene.md` | `/clear` vs `/compact`, lost-in-the-middle, phase-aware hygiene footer. | Phase-end hygiene footer; choosing `/clear` vs `/compact`. |
|
|
42
|
+
| `anti-patterns.md` | Pack-wide rationalizations + red flags the agent reaches for. | Loaded by each `rite-*/reference/anti-patterns.md`; loaded directly when reluctance is broader than the active phase. |
|
|
43
|
+
| `afk-hitl.md` | AFK vs HITL contract: `.devrites/AFK` sentinel format, `questions.md` schema, four-gate taxonomy (advisory / validating / blocking / escalating), AFK-never-silently-accepts boundaries. | `/rite-build`, `/rite-status`, `/rite-resolve`, `devrites-doubt`; anywhere a pause-or-proceed decision happens. |
|
|
44
|
+
| `tooling.md` | Optional external tools — code intelligence (codebase-memory-mcp first → cross-verify with codegraph + graphify → standard methods LSP / `Read`/`Grep`/`Glob`), up-to-date library docs (context7), architecture/ADR memory. Recommended, not required. | Any phase doing structural lookups (callers / impact / placement) or relying on external library/framework facts. |
|
|
45
|
+
|
|
46
|
+
How they're used: DevRites skills follow these rules; you and Claude can reference them
|
|
47
|
+
directly. They are guidance, not enforced gates — the enforced gates live in the
|
|
48
|
+
workflow skills (Spec Drift Guard, readiness gates, the seal).
|