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.
Files changed (232) hide show
  1. package/.claude-plugin/marketplace.json +24 -0
  2. package/.claude-plugin/plugin.json +43 -0
  3. package/CHANGELOG.md +391 -0
  4. package/LICENSE +56 -0
  5. package/NOTICE.md +18 -0
  6. package/README.md +582 -0
  7. package/SECURITY.md +193 -0
  8. package/bin/devrites.mjs +100 -0
  9. package/docs/architecture.md +272 -0
  10. package/docs/cli-mcp.md +57 -0
  11. package/docs/command-map.md +143 -0
  12. package/docs/flow.md +360 -0
  13. package/docs/release.md +29 -0
  14. package/docs/skills.md +214 -0
  15. package/docs/usage.md +325 -0
  16. package/install.sh +359 -0
  17. package/mcp/devrites-mcp.mjs +103 -0
  18. package/pack/.claude/agents/devrites-code-reviewer.md +50 -0
  19. package/pack/.claude/agents/devrites-doubt-reviewer.md +55 -0
  20. package/pack/.claude/agents/devrites-frontend-reviewer.md +52 -0
  21. package/pack/.claude/agents/devrites-performance-reviewer.md +47 -0
  22. package/pack/.claude/agents/devrites-plan-reviewer.md +79 -0
  23. package/pack/.claude/agents/devrites-security-auditor.md +53 -0
  24. package/pack/.claude/agents/devrites-simplifier-reviewer.md +75 -0
  25. package/pack/.claude/agents/devrites-slice-wright.md +181 -0
  26. package/pack/.claude/agents/devrites-spec-reviewer.md +72 -0
  27. package/pack/.claude/agents/devrites-strategy-reviewer.md +62 -0
  28. package/pack/.claude/agents/devrites-test-analyst.md +47 -0
  29. package/pack/.claude/hooks/devrites-a1-guard.sh +81 -0
  30. package/pack/.claude/hooks/devrites-allow.sh +44 -0
  31. package/pack/.claude/hooks/devrites-cursor.sh +28 -0
  32. package/pack/.claude/hooks/devrites-orient.sh +53 -0
  33. package/pack/.claude/hooks/devrites-redwatch.sh +39 -0
  34. package/pack/.claude/hooks/devrites-refresh-indexes.sh +127 -0
  35. package/pack/.claude/hooks/devrites-reviewer-readonly.sh +28 -0
  36. package/pack/.claude/hooks/devrites-statusline.sh +18 -0
  37. package/pack/.claude/hooks/devrites-stop-gate.sh +45 -0
  38. package/pack/.claude/hooks/devrites-wright-scope.sh +35 -0
  39. package/pack/.claude/hooks/hooks.json +52 -0
  40. package/pack/.claude/rules/README.md +48 -0
  41. package/pack/.claude/rules/afk-hitl.md +245 -0
  42. package/pack/.claude/rules/agents.md +98 -0
  43. package/pack/.claude/rules/anti-patterns.md +48 -0
  44. package/pack/.claude/rules/code-review.md +38 -0
  45. package/pack/.claude/rules/coding-style.md +55 -0
  46. package/pack/.claude/rules/context-hygiene.md +97 -0
  47. package/pack/.claude/rules/core.md +119 -0
  48. package/pack/.claude/rules/development-workflow.md +40 -0
  49. package/pack/.claude/rules/documentation.md +27 -0
  50. package/pack/.claude/rules/error-handling.md +33 -0
  51. package/pack/.claude/rules/git-workflow.md +35 -0
  52. package/pack/.claude/rules/hooks.md +38 -0
  53. package/pack/.claude/rules/patterns.md +45 -0
  54. package/pack/.claude/rules/performance.md +27 -0
  55. package/pack/.claude/rules/prose-style.md +101 -0
  56. package/pack/.claude/rules/security.md +63 -0
  57. package/pack/.claude/rules/testing.md +88 -0
  58. package/pack/.claude/rules/tooling.md +72 -0
  59. package/pack/.claude/settings.json +53 -0
  60. package/pack/.claude/skills/devrites-api-interface/SKILL.md +45 -0
  61. package/pack/.claude/skills/devrites-audit/SKILL.md +73 -0
  62. package/pack/.claude/skills/devrites-browser-proof/SKILL.md +38 -0
  63. package/pack/.claude/skills/devrites-debug-recovery/SKILL.md +50 -0
  64. package/pack/.claude/skills/devrites-debug-recovery/reference/build-the-loop.md +47 -0
  65. package/pack/.claude/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +17 -0
  66. package/pack/.claude/skills/devrites-debug-recovery/reference/hypotheses.md +17 -0
  67. package/pack/.claude/skills/devrites-debug-recovery/reference/instrumentation.md +21 -0
  68. package/pack/.claude/skills/devrites-debug-recovery/reference/regression-test.md +31 -0
  69. package/pack/.claude/skills/devrites-doubt/SKILL.md +75 -0
  70. package/pack/.claude/skills/devrites-frontend-craft/SKILL.md +96 -0
  71. package/pack/.claude/skills/devrites-frontend-craft/reference/craft.md +59 -0
  72. package/pack/.claude/skills/devrites-frontend-craft/reference/design-references.md +116 -0
  73. package/pack/.claude/skills/devrites-frontend-craft/reference/fullstack.md +45 -0
  74. package/pack/.claude/skills/devrites-frontend-craft/reference/quality-standards.md +215 -0
  75. package/pack/.claude/skills/devrites-frontend-craft/reference/reuse-first.md +59 -0
  76. package/pack/.claude/skills/devrites-frontend-craft/reference/shape.md +60 -0
  77. package/pack/.claude/skills/devrites-interview/SKILL.md +81 -0
  78. package/pack/.claude/skills/devrites-lib/SKILL.md +76 -0
  79. package/pack/.claude/skills/devrites-lib/scripts/analyze.sh +78 -0
  80. package/pack/.claude/skills/devrites-lib/scripts/check-acceptance.sh +75 -0
  81. package/pack/.claude/skills/devrites-lib/scripts/close-out.sh +47 -0
  82. package/pack/.claude/skills/devrites-lib/scripts/conventions.py +273 -0
  83. package/pack/.claude/skills/devrites-lib/scripts/coverage.sh +51 -0
  84. package/pack/.claude/skills/devrites-lib/scripts/devrites.sh +69 -0
  85. package/pack/.claude/skills/devrites-lib/scripts/doctor.sh +92 -0
  86. package/pack/.claude/skills/devrites-lib/scripts/evidence-fresh.sh +63 -0
  87. package/pack/.claude/skills/devrites-lib/scripts/footprint.sh +45 -0
  88. package/pack/.claude/skills/devrites-lib/scripts/learnings.sh +74 -0
  89. package/pack/.claude/skills/devrites-lib/scripts/mutation-gate.sh +52 -0
  90. package/pack/.claude/skills/devrites-lib/scripts/package-existence.sh +68 -0
  91. package/pack/.claude/skills/devrites-lib/scripts/preamble.sh +76 -0
  92. package/pack/.claude/skills/devrites-lib/scripts/progress.sh +103 -0
  93. package/pack/.claude/skills/devrites-lib/scripts/readiness.sh +62 -0
  94. package/pack/.claude/skills/devrites-lib/scripts/reconcile.sh +123 -0
  95. package/pack/.claude/skills/devrites-lib/scripts/resolve.sh +279 -0
  96. package/pack/.claude/skills/devrites-lib/scripts/stuck.sh +67 -0
  97. package/pack/.claude/skills/devrites-lib/scripts/test-integrity.sh +87 -0
  98. package/pack/.claude/skills/devrites-lib/scripts/tick-afk.sh +52 -0
  99. package/pack/.claude/skills/devrites-prose-craft/SKILL.md +105 -0
  100. package/pack/.claude/skills/devrites-prose-craft/reference/banned-phrases.md +95 -0
  101. package/pack/.claude/skills/devrites-prose-craft/reference/examples.md +88 -0
  102. package/pack/.claude/skills/devrites-prose-craft/reference/structures.md +134 -0
  103. package/pack/.claude/skills/devrites-refresh-indexes/SKILL.md +54 -0
  104. package/pack/.claude/skills/devrites-source-driven/SKILL.md +36 -0
  105. package/pack/.claude/skills/devrites-ux-shape/SKILL.md +121 -0
  106. package/pack/.claude/skills/devrites-ux-shape/reference/brief-template.md +93 -0
  107. package/pack/.claude/skills/devrites-ux-shape/reference/visual-direction-probe.md +48 -0
  108. package/pack/.claude/skills/rite/SKILL.md +135 -0
  109. package/pack/.claude/skills/rite/reference/menu.md +32 -0
  110. package/pack/.claude/skills/rite-adopt/SKILL.md +83 -0
  111. package/pack/.claude/skills/rite-adopt/reference/adoption.md +58 -0
  112. package/pack/.claude/skills/rite-adopt/reference/anti-patterns.md +19 -0
  113. package/pack/.claude/skills/rite-autocomplete/SKILL.md +96 -0
  114. package/pack/.claude/skills/rite-autocomplete/reference/decision-policy.md +35 -0
  115. package/pack/.claude/skills/rite-autocomplete/reference/loop.md +54 -0
  116. package/pack/.claude/skills/rite-autocomplete/reference/stop-conditions.md +59 -0
  117. package/pack/.claude/skills/rite-build/SKILL.md +261 -0
  118. package/pack/.claude/skills/rite-build/reference/afk-discipline.md +145 -0
  119. package/pack/.claude/skills/rite-build/reference/anti-patterns.md +25 -0
  120. package/pack/.claude/skills/rite-build/reference/checkpoint-protocol.md +149 -0
  121. package/pack/.claude/skills/rite-build/reference/evidence-standard.md +32 -0
  122. package/pack/.claude/skills/rite-build/reference/frontend-trigger.md +39 -0
  123. package/pack/.claude/skills/rite-build/reference/one-slice-cycle.md +38 -0
  124. package/pack/.claude/skills/rite-build/reference/spec-drift-guard.md +43 -0
  125. package/pack/.claude/skills/rite-build/reference/tdd.md +26 -0
  126. package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +115 -0
  127. package/pack/.claude/skills/rite-define/SKILL.md +157 -0
  128. package/pack/.claude/skills/rite-define/reference/anti-patterns.md +25 -0
  129. package/pack/.claude/skills/rite-define/reference/gates.md +152 -0
  130. package/pack/.claude/skills/rite-define/reference/plan-template.md +65 -0
  131. package/pack/.claude/skills/rite-doctor/SKILL.md +50 -0
  132. package/pack/.claude/skills/rite-frame/SKILL.md +116 -0
  133. package/pack/.claude/skills/rite-frame/reference/failure-modes.md +68 -0
  134. package/pack/.claude/skills/rite-handoff/SKILL.md +95 -0
  135. package/pack/.claude/skills/rite-handoff/reference/handoff-template.md +34 -0
  136. package/pack/.claude/skills/rite-learn/SKILL.md +82 -0
  137. package/pack/.claude/skills/rite-plan/SKILL.md +82 -0
  138. package/pack/.claude/skills/rite-plan/reference/anti-patterns.md +24 -0
  139. package/pack/.claude/skills/rite-plan/reference/dependency-graph.md +33 -0
  140. package/pack/.claude/skills/rite-plan/reference/replan-and-repair.md +42 -0
  141. package/pack/.claude/skills/rite-plan/reference/slicing.md +52 -0
  142. package/pack/.claude/skills/rite-plan/reference/task-breakdown.md +34 -0
  143. package/pack/.claude/skills/rite-polish/SKILL.md +90 -0
  144. package/pack/.claude/skills/rite-polish/reference/anti-ai-slop.md +177 -0
  145. package/pack/.claude/skills/rite-polish/reference/anti-patterns.md +27 -0
  146. package/pack/.claude/skills/rite-polish/reference/backend-polish.md +80 -0
  147. package/pack/.claude/skills/rite-polish/reference/browser-polish-evidence.md +31 -0
  148. package/pack/.claude/skills/rite-polish/reference/code.md +85 -0
  149. package/pack/.claude/skills/rite-polish/reference/design-system-discovery.md +35 -0
  150. package/pack/.claude/skills/rite-polish/reference/harden-checklist.md +109 -0
  151. package/pack/.claude/skills/rite-polish/reference/ui.md +136 -0
  152. package/pack/.claude/skills/rite-pressure-test/SKILL.md +43 -0
  153. package/pack/.claude/skills/rite-prototype/SKILL.md +87 -0
  154. package/pack/.claude/skills/rite-prove/SKILL.md +120 -0
  155. package/pack/.claude/skills/rite-prove/reference/anti-patterns.md +25 -0
  156. package/pack/.claude/skills/rite-prove/reference/browser-proof.md +26 -0
  157. package/pack/.claude/skills/rite-prove/reference/failure-triage.md +25 -0
  158. package/pack/.claude/skills/rite-prove/reference/proof-ladder.md +26 -0
  159. package/pack/.claude/skills/rite-prove/reference/test-command-discovery.md +30 -0
  160. package/pack/.claude/skills/rite-quick/SKILL.md +81 -0
  161. package/pack/.claude/skills/rite-resolve/SKILL.md +113 -0
  162. package/pack/.claude/skills/rite-resolve/reference/answer-protocol.md +114 -0
  163. package/pack/.claude/skills/rite-review/SKILL.md +170 -0
  164. package/pack/.claude/skills/rite-review/reference/anti-patterns.md +32 -0
  165. package/pack/.claude/skills/rite-review/reference/cognitive-load.md +90 -0
  166. package/pack/.claude/skills/rite-review/reference/feature-scoped-review.md +26 -0
  167. package/pack/.claude/skills/rite-review/reference/five-axis-review.md +46 -0
  168. package/pack/.claude/skills/rite-review/reference/nielsen-heuristics.md +130 -0
  169. package/pack/.claude/skills/rite-review/reference/parallel-dispatch.md +62 -0
  170. package/pack/.claude/skills/rite-review/reference/performance-review.md +28 -0
  171. package/pack/.claude/skills/rite-review/reference/security-review.md +32 -0
  172. package/pack/.claude/skills/rite-seal/SKILL.md +183 -0
  173. package/pack/.claude/skills/rite-seal/reference/anti-patterns.md +27 -0
  174. package/pack/.claude/skills/rite-seal/reference/conventions-ledger.md +63 -0
  175. package/pack/.claude/skills/rite-seal/reference/final-evidence.md +72 -0
  176. package/pack/.claude/skills/rite-seal/reference/go-no-go.md +37 -0
  177. package/pack/.claude/skills/rite-seal/reference/parallel-dispatch.md +69 -0
  178. package/pack/.claude/skills/rite-seal/reference/risk-and-rollback.md +30 -0
  179. package/pack/.claude/skills/rite-seal/reference/seal-template.md +36 -0
  180. package/pack/.claude/skills/rite-ship/SKILL.md +120 -0
  181. package/pack/.claude/skills/rite-ship/reference/anti-patterns.md +25 -0
  182. package/pack/.claude/skills/rite-ship/reference/close-out.md +31 -0
  183. package/pack/.claude/skills/rite-ship/reference/design-memory.md +120 -0
  184. package/pack/.claude/skills/rite-ship/reference/git-ship.md +42 -0
  185. package/pack/.claude/skills/rite-ship/reference/ship-template.md +33 -0
  186. package/pack/.claude/skills/rite-spec/SKILL.md +126 -0
  187. package/pack/.claude/skills/rite-spec/reference/acceptance-criteria.md +31 -0
  188. package/pack/.claude/skills/rite-spec/reference/anti-patterns.md +25 -0
  189. package/pack/.claude/skills/rite-spec/reference/interview-patterns.md +56 -0
  190. package/pack/.claude/skills/rite-spec/reference/investigation.md +64 -0
  191. package/pack/.claude/skills/rite-spec/reference/question-protocol.md +61 -0
  192. package/pack/.claude/skills/rite-spec/reference/references-intake.md +57 -0
  193. package/pack/.claude/skills/rite-spec/reference/spec-checklists.md +73 -0
  194. package/pack/.claude/skills/rite-spec/reference/spec-template.md +124 -0
  195. package/pack/.claude/skills/rite-spec/reference/state-workspace.md +159 -0
  196. package/pack/.claude/skills/rite-status/SKILL.md +101 -0
  197. package/pack/.claude/skills/rite-temper/SKILL.md +119 -0
  198. package/pack/.claude/skills/rite-temper/reference/anti-patterns.md +29 -0
  199. package/pack/.claude/skills/rite-temper/reference/review-dimensions.md +65 -0
  200. package/pack/.claude/skills/rite-temper/reference/scope-modes.md +53 -0
  201. package/pack/.claude/skills/rite-temper/reference/significance.md +46 -0
  202. package/pack/.claude/skills/rite-temper/reference/strategy-template.md +90 -0
  203. package/pack/.claude/skills/rite-vet/SKILL.md +155 -0
  204. package/pack/.claude/skills/rite-vet/reference/anti-patterns.md +29 -0
  205. package/pack/.claude/skills/rite-vet/reference/artifacts.md +135 -0
  206. package/pack/.claude/skills/rite-vet/reference/cross-model.md +41 -0
  207. package/pack/.claude/skills/rite-vet/reference/depth.md +53 -0
  208. package/pack/.claude/skills/rite-vet/reference/eng-lenses.md +48 -0
  209. package/pack/.claude/skills/rite-vet/reference/review-axes.md +167 -0
  210. package/pack/.claude/skills/rite-zoom-out/SKILL.md +75 -0
  211. package/package.json +68 -0
  212. package/scripts/build-release-tarball.sh +74 -0
  213. package/scripts/check-cross-refs.py +121 -0
  214. package/scripts/check-no-global-writes.sh +44 -0
  215. package/scripts/check-rule-uniqueness.sh +73 -0
  216. package/scripts/devrites-detect.sh +175 -0
  217. package/scripts/eval-runner.py +273 -0
  218. package/scripts/grade-feature.sh +104 -0
  219. package/scripts/install-lib.sh +83 -0
  220. package/scripts/pin.sh +166 -0
  221. package/scripts/render-eval-summary.py +48 -0
  222. package/scripts/run-evals.sh +149 -0
  223. package/scripts/run-outcome-evals.sh +49 -0
  224. package/scripts/scan-pack-security.py +209 -0
  225. package/scripts/scan-supply-chain-iocs.py +127 -0
  226. package/scripts/supply-chain-iocs.json +11 -0
  227. package/scripts/sync-version.sh +56 -0
  228. package/scripts/validate-frontmatter.py +149 -0
  229. package/scripts/validate-workflow-security.py +86 -0
  230. package/scripts/validate.sh +234 -0
  231. package/uninstall.sh +137 -0
  232. package/update.sh +196 -0
package/scripts/pin.sh ADDED
@@ -0,0 +1,166 @@
1
+ #!/usr/bin/env bash
2
+ # scripts/pin.sh — manage user-pinned slash aliases for DevRites.
3
+ #
4
+ # Adopts the same "thin wrapper SKILL.md that delegates" pattern as install.sh
5
+ # uses for --short-aliases=all, but exposes it as a runtime verb so users can
6
+ # add / remove arbitrary aliases against any rite-* skill without re-running
7
+ # the installer.
8
+ #
9
+ # Usage:
10
+ # scripts/pin.sh add <alias> <target> # /alias → /target (target = rite-spec | rite-build | ...)
11
+ # scripts/pin.sh remove <alias> # remove a previously-pinned alias
12
+ # scripts/pin.sh list # print currently-pinned aliases
13
+ # scripts/pin.sh --help
14
+ #
15
+ # Examples:
16
+ # ./scripts/pin.sh add b rite-build # /b == /rite-build
17
+ # ./scripts/pin.sh add ship rite-seal # /ship == /rite-seal
18
+ # ./scripts/pin.sh remove b
19
+ #
20
+ # Targets:
21
+ # - Default: $PWD (the installed project, which holds .claude/skills/).
22
+ # - --target <dir> to operate on a project elsewhere.
23
+ #
24
+ # Safety:
25
+ # - Refuses to write inside ~/.claude (DevRites is project-local only).
26
+ # - Refuses to overwrite a non-alias skill at .claude/skills/<alias>/.
27
+ # - Refuses if <target> is not a known rite-* skill in the target's pack.
28
+ # - Manifest-managed: pinned wrappers are recorded in .claude/devrites.manifest
29
+ # so the standard uninstall.sh cleans them up automatically.
30
+
31
+ set -u
32
+
33
+ # ---- locate install-lib + load helpers ----------------------------------
34
+ SELF_DIR="$( cd "$(dirname "$0")" && pwd -P )"
35
+ ROOT="$( cd "$SELF_DIR/.." && pwd -P )"
36
+ LIB="$SELF_DIR/install-lib.sh"
37
+ if [ ! -r "$LIB" ]; then
38
+ printf 'error: cannot find %s\n' "$LIB" >&2
39
+ exit 2
40
+ fi
41
+ # shellcheck source=scripts/install-lib.sh
42
+ . "$LIB"
43
+
44
+ # ---- parse args ----------------------------------------------------------
45
+ TARGET="$PWD"
46
+ SUBCMD=""
47
+ ALIAS=""
48
+ DEST=""
49
+ while [ $# -gt 0 ]; do
50
+ case "$1" in
51
+ --target) TARGET="$2"; shift 2 ;;
52
+ --target=*) TARGET="${1#*=}"; shift ;;
53
+ -h|--help)
54
+ sed -n '2,30p' "$0"; exit 0 ;;
55
+ add|remove|list)
56
+ SUBCMD="$1"; shift
57
+ [ "$SUBCMD" = "add" ] && { ALIAS="${1:-}"; DEST="${2:-}"; shift 2 || true; }
58
+ [ "$SUBCMD" = "remove" ] && { ALIAS="${1:-}"; shift || true; }
59
+ ;;
60
+ *)
61
+ dr_die "unknown arg: $1 (try --help)" ;;
62
+ esac
63
+ done
64
+
65
+ [ -z "$SUBCMD" ] && dr_die "missing subcommand (add | remove | list)"
66
+
67
+ TARGET="$(dr_abspath_dir "$TARGET")" || dr_die "target dir not found: $TARGET"
68
+ dr_is_global_claude "$TARGET" && dr_die "refusing to operate on ~/.claude — DevRites is project-local only"
69
+
70
+ SKILLS_DIR="$TARGET/.claude/skills"
71
+ MF="$TARGET/$DR_MANIFEST_NAME"
72
+
73
+ [ -d "$SKILLS_DIR" ] || dr_die "no .claude/skills at $TARGET — run install.sh first?"
74
+ [ -f "$MF" ] || dr_die "no manifest at $MF — run install.sh first?"
75
+
76
+ # ---- helpers -------------------------------------------------------------
77
+ valid_alias_name() {
78
+ # lowercase ASCII, digits, hyphens. No /, ., spaces. Not "rite" or "rite-*".
79
+ case "$1" in
80
+ ""|/*|*/*|*[!a-z0-9-]*) return 1 ;;
81
+ rite|rite-*) return 1 ;;
82
+ esac
83
+ return 0
84
+ }
85
+
86
+ is_known_target() {
87
+ # Target must be a rite-* skill present in the installed pack.
88
+ case "$1" in
89
+ rite-*) [ -f "$SKILLS_DIR/$1/SKILL.md" ] ;;
90
+ *) return 1 ;;
91
+ esac
92
+ }
93
+
94
+ is_pinned_alias() {
95
+ # Detect a wrapper we generated: SKILL.md description must contain "Alias of DevRites /<target>".
96
+ [ -f "$SKILLS_DIR/$1/SKILL.md" ] || return 1
97
+ grep -q 'description: Alias of DevRites /' "$SKILLS_DIR/$1/SKILL.md"
98
+ }
99
+
100
+ # ---- subcommands ---------------------------------------------------------
101
+ do_add() {
102
+ valid_alias_name "$ALIAS" || dr_die "invalid alias name '$ALIAS' (lowercase / digits / hyphens; not 'rite' or 'rite-*')"
103
+ is_known_target "$DEST" || dr_die "unknown target '$DEST' — not a rite-* skill in $SKILLS_DIR"
104
+ [ "$ALIAS" = "$DEST" ] && dr_die "alias and target are the same"
105
+
106
+ ALIAS_DIR="$SKILLS_DIR/$ALIAS"
107
+ ALIAS_FILE="$ALIAS_DIR/SKILL.md"
108
+ ALIAS_REL=".claude/skills/$ALIAS/SKILL.md"
109
+
110
+ if [ -e "$ALIAS_FILE" ]; then
111
+ if is_pinned_alias "$ALIAS"; then
112
+ dr_warn "already pinned: /$ALIAS — overwriting"
113
+ else
114
+ dr_die "$ALIAS_FILE exists and is NOT a pinned alias — refusing to overwrite"
115
+ fi
116
+ fi
117
+
118
+ mkdir -p "$ALIAS_DIR"
119
+ dr_gen_alias_wrapper "$ALIAS" "$DEST" "$ALIAS_FILE"
120
+
121
+ if ! dr_manifest_contains "$MF" "$ALIAS_REL"; then
122
+ printf '%s\n' "$ALIAS_REL" >> "$MF"
123
+ fi
124
+
125
+ dr_ok "pinned: /$ALIAS → /$DEST ($ALIAS_FILE)"
126
+ }
127
+
128
+ do_remove() {
129
+ valid_alias_name "$ALIAS" || dr_die "invalid alias name '$ALIAS'"
130
+ ALIAS_DIR="$SKILLS_DIR/$ALIAS"
131
+ ALIAS_FILE="$ALIAS_DIR/SKILL.md"
132
+ ALIAS_REL=".claude/skills/$ALIAS/SKILL.md"
133
+
134
+ [ -f "$ALIAS_FILE" ] || dr_die "no pinned alias at $ALIAS_FILE"
135
+ is_pinned_alias "$ALIAS" || dr_die "$ALIAS_FILE exists but is not a pinned alias — refusing to remove"
136
+
137
+ rm -f "$ALIAS_FILE"
138
+ rmdir "$ALIAS_DIR" 2>/dev/null || true
139
+
140
+ # Drop the alias line from the manifest (preserve header + the rest)
141
+ TMP="$(mktemp)"
142
+ grep -Fvx "$ALIAS_REL" "$MF" > "$TMP" && mv "$TMP" "$MF"
143
+
144
+ dr_ok "unpinned: /$ALIAS"
145
+ }
146
+
147
+ do_list() {
148
+ found=0
149
+ for d in "$SKILLS_DIR"/*/; do
150
+ [ -d "$d" ] || continue
151
+ nm="$(basename "$d")"
152
+ if is_pinned_alias "$nm"; then
153
+ to="$(awk '/^description: Alias of DevRites \//{ sub(/.*Alias of DevRites \//,""); sub(/\..*/,""); print; exit }' "$d/SKILL.md")"
154
+ printf ' /%-20s → /%s\n' "$nm" "$to"
155
+ found=1
156
+ fi
157
+ done
158
+ [ "$found" -eq 0 ] && dr_say "(no pinned aliases at $TARGET)"
159
+ }
160
+
161
+ case "$SUBCMD" in
162
+ add) [ -n "$ALIAS" ] && [ -n "$DEST" ] || dr_die "usage: pin.sh add <alias> <target>"; do_add ;;
163
+ remove) [ -n "$ALIAS" ] || dr_die "usage: pin.sh remove <alias>"; do_remove ;;
164
+ list) do_list ;;
165
+ *) dr_die "unknown subcommand: $SUBCMD" ;;
166
+ esac
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env python3
2
+ """Render a GitHub-Actions-flavored markdown table from an eval summary JSONL.
3
+
4
+ Reads eval-summary.jsonl (one JSON object per line, written by
5
+ `scripts/eval-runner.py --summary-file`) and prints a markdown table to
6
+ stdout. Used by `.github/workflows/evals.yml` to populate the job summary.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ import os
12
+ import pathlib
13
+ import sys
14
+
15
+
16
+ def main() -> int:
17
+ path = pathlib.Path(sys.argv[1] if len(sys.argv) > 1 else "eval-summary.jsonl")
18
+ model = os.environ.get("DEVRITES_EVAL_MODEL", "claude-haiku-4-5-20251001")
19
+ if not path.is_file():
20
+ print(f"(no summary file found at {path})")
21
+ return 0
22
+ print(f"## DevRites trigger evals\n")
23
+ print(f"Model: `{model}`\n")
24
+ print("| Skill | Correct | Accuracy | FP | FN | Passed |")
25
+ print("|---|---|---|---|---|---|")
26
+ total_correct = total_n = total_fp = total_fn = 0
27
+ any_failed = False
28
+ for line in path.read_text(encoding="utf-8").splitlines():
29
+ line = line.strip()
30
+ if not line:
31
+ continue
32
+ r = json.loads(line)
33
+ tick = "✅" if r["passed"] else "❌"
34
+ if not r["passed"]:
35
+ any_failed = True
36
+ total_correct += r["correct"]
37
+ total_n += r["total"]
38
+ total_fp += r["false_positives"]
39
+ total_fn += r["false_negatives"]
40
+ print(f"| `{r['skill']}` | {r['correct']}/{r['total']} | {r['accuracy']:.0%} | {r['false_positives']} | {r['false_negatives']} | {tick} |")
41
+ if total_n:
42
+ overall = total_correct / total_n
43
+ print(f"| **overall** | **{total_correct}/{total_n}** | **{overall:.0%}** | **{total_fp}** | **{total_fn}** | {'✅' if not any_failed else '❌'} |")
44
+ return 0
45
+
46
+
47
+ if __name__ == "__main__":
48
+ sys.exit(main())
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env bash
2
+ # scripts/run-evals.sh — validate the structure of DevRites trigger evals.
3
+ #
4
+ # Schema check + summary. Does NOT actually invoke Claude — full eval
5
+ # execution requires CLAUDE_API_KEY and a `claude` CLI invocation that is
6
+ # gated to the user, not CI. CI runs this script to enforce the shape and
7
+ # catch broken JSON / missing skills / wrong query counts.
8
+ #
9
+ # Usage:
10
+ # scripts/run-evals.sh # validate every evals/*.json
11
+ # scripts/run-evals.sh evals/rite-spec.json # validate one file
12
+
13
+ set -euo pipefail
14
+
15
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
16
+ EVALS_DIR="$ROOT/evals"
17
+ EXPECTED_QUERIES=20
18
+
19
+ if [[ $# -gt 0 ]]; then
20
+ FILES=("$@")
21
+ else
22
+ if [[ ! -d "$EVALS_DIR" ]]; then
23
+ echo "No evals/ directory at $EVALS_DIR" >&2
24
+ exit 1
25
+ fi
26
+ FILES=()
27
+ while IFS= read -r f; do
28
+ [[ "$f" == */README.md ]] && continue
29
+ FILES+=("$f")
30
+ done < <(find "$EVALS_DIR" -type f -name '*.json' | sort)
31
+ fi
32
+
33
+ if [[ ${#FILES[@]} -eq 0 ]]; then
34
+ echo "No eval files found." >&2
35
+ exit 1
36
+ fi
37
+
38
+ # Need either python3 or jq for JSON parsing. Prefer python3 (already a
39
+ # DevRites build dep).
40
+ if command -v python3 >/dev/null 2>&1; then
41
+ PARSER="python3"
42
+ elif command -v jq >/dev/null 2>&1; then
43
+ PARSER="jq"
44
+ else
45
+ echo "Need python3 or jq to validate JSON." >&2
46
+ exit 1
47
+ fi
48
+
49
+ FAILED=0
50
+ TOTAL=0
51
+
52
+ for file in "${FILES[@]}"; do
53
+ TOTAL=$((TOTAL + 1))
54
+ printf '== %s ==\n' "$file"
55
+
56
+ if [[ "$PARSER" == "python3" ]]; then
57
+ OUT=$(python3 - "$file" <<'PY'
58
+ import json, sys, pathlib
59
+ path = pathlib.Path(sys.argv[1])
60
+ try:
61
+ data = json.loads(path.read_text())
62
+ except Exception as e:
63
+ print(f"INVALID JSON: {e}")
64
+ sys.exit(1)
65
+
66
+ errors = []
67
+
68
+ for key in ("skill", "description", "queries"):
69
+ if key not in data:
70
+ errors.append(f"missing top-level key: {key}")
71
+
72
+ queries = data.get("queries", [])
73
+ if not isinstance(queries, list):
74
+ errors.append("queries is not a list")
75
+ elif len(queries) != 20:
76
+ errors.append(f"expected 20 queries, got {len(queries)}")
77
+
78
+ trig = noTrig = 0
79
+ for i, q in enumerate(queries):
80
+ if not isinstance(q, dict):
81
+ errors.append(f"query[{i}] not an object")
82
+ continue
83
+ for k in ("text", "expected", "rationale"):
84
+ if k not in q:
85
+ errors.append(f"query[{i}] missing key: {k}")
86
+ if q.get("expected") == "should_trigger":
87
+ trig += 1
88
+ elif q.get("expected") == "should_not_trigger":
89
+ noTrig += 1
90
+ else:
91
+ errors.append(f"query[{i}] invalid expected: {q.get('expected')!r}")
92
+
93
+ if errors:
94
+ for e in errors:
95
+ print(f" FAIL: {e}")
96
+ sys.exit(1)
97
+
98
+ print(f" skill: {data['skill']}")
99
+ print(f" queries: {len(queries)} (should_trigger={trig}, should_not_trigger={noTrig})")
100
+ PY
101
+ )
102
+ rc=$?
103
+ else
104
+ OUT=$(jq -r '
105
+ if (.skill and .description and (.queries|type=="array")) then
106
+ if (.queries|length) == 20 then
107
+ " skill: \(.skill)\n queries: \(.queries|length) (should_trigger=\(.queries|map(select(.expected=="should_trigger"))|length), should_not_trigger=\(.queries|map(select(.expected=="should_not_trigger"))|length))"
108
+ else
109
+ " FAIL: expected 20 queries, got \(.queries|length)"
110
+ end
111
+ else
112
+ " FAIL: missing required keys"
113
+ end
114
+ ' "$file") || rc=1
115
+ rc=${rc:-0}
116
+ fi
117
+
118
+ printf '%s\n' "$OUT"
119
+ if [[ ${rc:-0} -ne 0 ]] || [[ "$OUT" == *"FAIL"* ]]; then
120
+ FAILED=$((FAILED + 1))
121
+ fi
122
+ done
123
+
124
+ echo
125
+ printf 'Validated %d eval files; %d failed.\n' "$TOTAL" "$FAILED"
126
+
127
+ if [[ $FAILED -gt 0 ]]; then
128
+ exit 1
129
+ fi
130
+
131
+ if [[ -z "${CLAUDE_API_KEY:-}" ]]; then
132
+ echo
133
+ echo "Note: CLAUDE_API_KEY is not set; ran schema validation only."
134
+ echo "To execute the evals against a live Claude model:"
135
+ echo " pip install anthropic"
136
+ echo " CLAUDE_API_KEY=sk-... python3 scripts/eval-runner.py evals/*.json"
137
+ echo "Override the model with DEVRITES_EVAL_MODEL=claude-... ."
138
+ exit 0
139
+ fi
140
+
141
+ # Live execution path — runs each eval against a real Claude model.
142
+ if ! command -v python3 >/dev/null 2>&1; then
143
+ echo "error: CLAUDE_API_KEY is set but python3 is required for the live runner." >&2
144
+ exit 1
145
+ fi
146
+
147
+ echo
148
+ echo "CLAUDE_API_KEY set — executing live trigger evals via scripts/eval-runner.py …"
149
+ exec python3 "$ROOT/scripts/eval-runner.py" "${FILES[@]}"
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env bash
2
+ # Outcome-eval harness — proves the deterministic feature grader BOTH passes a
3
+ # known-shippable workspace AND fails a known-blocked one (see-it-fail-first:
4
+ # a grader that never returns NO-GO proves nothing). Runs in CI; no API key.
5
+ #
6
+ # Usage: run-outcome-evals.sh
7
+
8
+ set -euo pipefail
9
+
10
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
11
+ GRADER="$ROOT/scripts/grade-feature.sh"
12
+ good="$ROOT/evals/golden/shippable-feature"
13
+ bad="$ROOT/evals/golden/blocked-feature"
14
+ nearmiss="$ROOT/evals/golden/near-miss-unproven-ac"
15
+ fail=0
16
+
17
+ echo "== grade golden/shippable-feature (expect GO) =="
18
+ if bash "$GRADER" "$good"; then
19
+ echo " PASS — graded GO"
20
+ else
21
+ echo " FAIL — a known-shippable workspace should grade GO"; fail=1
22
+ fi
23
+
24
+ echo
25
+ echo "== grade golden/blocked-feature (expect NO-GO) =="
26
+ if bash "$GRADER" "$bad"; then
27
+ echo " FAIL — a known-blocked workspace should NOT grade GO"; fail=1
28
+ else
29
+ echo " PASS — correctly graded NO-GO"
30
+ fi
31
+
32
+ echo
33
+ echo "== grade golden/near-miss-unproven-ac (expect NO-GO on ONE invariant) =="
34
+ # Isolates invariant 2 (every acceptance criterion proven): identical to the
35
+ # shippable fixture except a single AC is left unchecked. Proves that gate fails
36
+ # independently — not only when six invariants trip at once.
37
+ if bash "$GRADER" "$nearmiss"; then
38
+ echo " FAIL — an unproven acceptance criterion must grade NO-GO"; fail=1
39
+ else
40
+ echo " PASS — correctly graded NO-GO on the lone unchecked AC"
41
+ fi
42
+
43
+ echo
44
+ if [ "$fail" -eq 0 ]; then
45
+ echo "Outcome evals passed."
46
+ else
47
+ echo "Outcome evals FAILED."
48
+ fi
49
+ exit "$fail"
@@ -0,0 +1,209 @@
1
+ #!/usr/bin/env python3
2
+ """Scan the shipped DevRites pack for prompt-injection and hidden-unicode risks.
3
+
4
+ DevRites ships executable instruction files (skills, agents, rules, hooks) into
5
+ other people's projects. A poisoned or invisibly-altered file is a supply-chain
6
+ vulnerability, not a style nit (cf. the Snyk Feb-2026 "ToxicSkills" study: 36%
7
+ of public skills carried prompt injection). This is the BLOCKING gate that keeps
8
+ such a file from ever being released.
9
+
10
+ Usage: scan-pack-security.py PATH [PATH ...]
11
+ PATH may be a file or a directory (directories are walked for text files).
12
+ Exits non-zero if any finding is reported.
13
+
14
+ Two finding classes:
15
+
16
+ 1. Hidden unicode (always a finding — no legitimate reason to exist in a
17
+ Markdown/shell instruction file): bidi controls, zero-width characters,
18
+ other invisibles, and homoglyph confusables (a word that mixes ASCII with
19
+ look-alike Cyrillic/Greek letters).
20
+
21
+ 2. Prompt-injection patterns (a finding unless explicitly suppressed): the
22
+ "ignore previous instructions" family, system-prompt overrides, tool /
23
+ permission escalation, and data-exfiltration phrasing.
24
+
25
+ Suppression (for DevRites' own files that legitimately *discuss* injection
26
+ defensively — e.g. security.md, the reviewer agents): put a marker on the same
27
+ line OR anywhere in the file:
28
+
29
+ ... ignore previous instructions ... <!-- pack-scan-ignore: defensive doc -->
30
+
31
+ A whole file can opt a class out with a top-of-file marker:
32
+
33
+ <!-- pack-scan-ignore-file: injection -->
34
+
35
+ Suppressions live in the file, so every exception is visible in the diff and
36
+ reviewable — consistent with DevRites' "no silent state" thesis. Hidden-unicode
37
+ findings can be suppressed the same way but should essentially never be.
38
+ """
39
+ import os
40
+ import re
41
+ import sys
42
+ import unicodedata
43
+
44
+ # --- hidden unicode -------------------------------------------------------
45
+
46
+ # Invisible / formatting code points with no business in an instruction file.
47
+ ZERO_WIDTH = {
48
+ 0x200B, # zero width space
49
+ 0x200C, # zero width non-joiner
50
+ 0x200D, # zero width joiner
51
+ 0x2060, # word joiner
52
+ 0xFEFF, # zero width no-break space / BOM (mid-file)
53
+ 0x180E, # mongolian vowel separator
54
+ 0x00AD, # soft hyphen
55
+ }
56
+ BIDI_CONTROLS = {
57
+ 0x202A, 0x202B, 0x202C, 0x202D, 0x202E, # LRE RLE PDF LRO RLO
58
+ 0x2066, 0x2067, 0x2068, 0x2069, # LRI RLI FSI PDI
59
+ 0x200E, 0x200F, # LRM RLM
60
+ }
61
+ HIDDEN = ZERO_WIDTH | BIDI_CONTROLS
62
+
63
+ # Scripts whose letters are commonly used as ASCII look-alikes.
64
+ CONFUSABLE_SCRIPTS = ("CYRILLIC", "GREEK")
65
+
66
+
67
+ def _char_script(ch):
68
+ try:
69
+ return unicodedata.name(ch).split(" ")[0]
70
+ except ValueError:
71
+ return ""
72
+
73
+
74
+ def find_hidden_unicode(text):
75
+ """Yield (line_no, col, label) for hidden code points and homoglyph words."""
76
+ for lineno, line in enumerate(text.splitlines(), start=1):
77
+ for col, ch in enumerate(line, start=1):
78
+ cp = ord(ch)
79
+ if cp == 0xFEFF and lineno == 1 and col == 1:
80
+ continue # a leading BOM is legal; only mid-file ZWNBSP is suspect
81
+ if cp in HIDDEN:
82
+ yield lineno, col, "hidden char U+%04X (%s)" % (cp, _hidden_name(cp))
83
+ # Homoglyph: a token mixing ASCII letters with confusable-script letters.
84
+ for m in re.finditer(r"\S+", line):
85
+ tok = m.group(0)
86
+ has_ascii_alpha = any(("a" <= c.lower() <= "z") for c in tok)
87
+ mixed = [c for c in tok if c.isalpha() and _char_script(c) in CONFUSABLE_SCRIPTS]
88
+ if has_ascii_alpha and mixed:
89
+ yield lineno, m.start() + 1, (
90
+ "homoglyph: ASCII word mixes %s look-alike(s) %r" % (
91
+ _char_script(mixed[0]).title(), "".join(sorted(set(mixed)))))
92
+
93
+
94
+ def _hidden_name(cp):
95
+ try:
96
+ return unicodedata.name(chr(cp))
97
+ except ValueError:
98
+ return "unnamed"
99
+
100
+
101
+ # --- prompt-injection patterns -------------------------------------------
102
+
103
+ # Each: (label, compiled regex). Kept high-signal to limit false positives on
104
+ # legitimate prose; defensive discussion is handled via suppression markers.
105
+ # Gaps use '.' (any non-newline char): scanning is per-line, so a match can't
106
+ # cross lines, and excluding periods would miss tokens like ".env" / ".ssh".
107
+ _INJECTION = [
108
+ ("ignore-previous-instructions",
109
+ r"\b(ignore|disregard|forget)\b.{0,40}\b(previous|prior|above|earlier|all)\b"
110
+ r".{0,20}\b(instruction|instructions|prompt|prompts|direction|directions|context)\b"),
111
+ ("system-prompt-override",
112
+ r"\b(you are now|act as|pretend to be|new instructions\s*:|override (your|the) (system|prompt|rules))"),
113
+ ("permission-escalation",
114
+ r"\b(bypass|ignore|disable|skip)\b.{0,30}\b(approval|permission|permissions|guardrail|guardrails|safety|sandbox|restriction|restrictions)\b"),
115
+ ("data-exfiltration",
116
+ r"\b(exfiltrat\w+|send|post|upload|leak|curl|wget)\b.{0,40}\b(env|environment|secret|secrets|credential|credentials|token|tokens|api[_-]?key|\.ssh|private key)\b"),
117
+ ]
118
+ INJECTION = [(label, re.compile(rx, re.IGNORECASE)) for label, rx in _INJECTION]
119
+
120
+
121
+ def find_injection(text, file_suppressed):
122
+ """Yield (line_no, label, excerpt) for injection patterns, honoring suppression."""
123
+ for lineno, line in enumerate(text.splitlines(), start=1):
124
+ if _line_suppressed(line):
125
+ continue
126
+ for label, rx in INJECTION:
127
+ if "injection" in file_suppressed:
128
+ break
129
+ m = rx.search(line)
130
+ if m:
131
+ yield lineno, label, line.strip()[:120]
132
+
133
+
134
+ _LINE_SUPPRESS = re.compile(r"pack-scan-ignore\b(?!-file)", re.IGNORECASE)
135
+ # Class list is word/comma/space only — a trailing "-->" comment closer must not
136
+ # get swallowed into the captured class name.
137
+ _FILE_SUPPRESS = re.compile(r"pack-scan-ignore-file\s*:\s*([\w, ]+)", re.IGNORECASE)
138
+
139
+
140
+ def _line_suppressed(line):
141
+ return bool(_LINE_SUPPRESS.search(line))
142
+
143
+
144
+ def _file_suppressions(text):
145
+ classes = set()
146
+ for m in _FILE_SUPPRESS.finditer(text):
147
+ for c in m.group(1).split(","):
148
+ classes.add(c.strip().lower())
149
+ return classes
150
+
151
+
152
+ # --- driver ---------------------------------------------------------------
153
+
154
+ TEXT_EXTS = {".md", ".sh", ".json", ".txt", ".py", ".js", ".yaml", ".yml", ""}
155
+
156
+
157
+ def iter_files(paths):
158
+ for p in paths:
159
+ if os.path.isdir(p):
160
+ for root, _dirs, names in os.walk(p):
161
+ for n in sorted(names):
162
+ fp = os.path.join(root, n)
163
+ ext = os.path.splitext(n)[1].lower()
164
+ if ext in TEXT_EXTS:
165
+ yield fp
166
+ else:
167
+ yield p
168
+
169
+
170
+ def scan(paths):
171
+ """Return a list of finding strings. Empty list == clean."""
172
+ findings = []
173
+ for path in iter_files(paths):
174
+ try:
175
+ with open(path, "r", encoding="utf-8") as fh:
176
+ text = fh.read()
177
+ except (OSError, UnicodeDecodeError) as e:
178
+ findings.append("ERROR %s: cannot read as utf-8 (%s)" % (path, e))
179
+ continue
180
+ file_sup = _file_suppressions(text)
181
+ hidden_off = ("unicode" in file_sup) or ("hidden" in file_sup)
182
+ if not hidden_off:
183
+ for lineno, col, label in find_hidden_unicode(text):
184
+ if _line_suppressed(text.splitlines()[lineno - 1]):
185
+ continue
186
+ findings.append("FINDING %s:%d:%d: %s" % (path, lineno, col, label))
187
+ for lineno, label, excerpt in find_injection(text, file_sup):
188
+ findings.append("FINDING %s:%d: injection/%s: %s" % (path, lineno, label, excerpt))
189
+ return findings
190
+
191
+
192
+ def main(argv):
193
+ paths = argv[1:]
194
+ if not paths:
195
+ print("usage: scan-pack-security.py PATH [PATH ...]", file=sys.stderr)
196
+ return 2
197
+ findings = scan(paths)
198
+ if findings:
199
+ for f in findings:
200
+ print(f)
201
+ print("\n%d security finding(s). Fix, or add an auditable "
202
+ "pack-scan-ignore marker if this is defensive content." % len(findings))
203
+ return 1
204
+ print("OK pack security scan clean (%s)" % ", ".join(paths))
205
+ return 0
206
+
207
+
208
+ if __name__ == "__main__":
209
+ sys.exit(main(sys.argv))