@erclx/canon 5.12.0 → 5.14.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 (74) hide show
  1. package/claude/.claude-plugin/plugin.json +1 -1
  2. package/claude/skills/api-design/REQUIREMENT.md +1 -1
  3. package/claude/skills/api-design/SKILL.md +4 -3
  4. package/claude/skills/api-design/references/adopted.md +1 -1
  5. package/claude/skills/auto-ship/REQUIREMENT.md +1 -0
  6. package/claude/skills/auto-ship/SKILL.md +2 -2
  7. package/claude/skills/build-in-slices/REQUIREMENT.md +45 -0
  8. package/claude/skills/build-in-slices/SKILL.md +94 -0
  9. package/claude/skills/build-in-slices/references/adopted.md +41 -0
  10. package/claude/skills/code-craft/REQUIREMENT.md +48 -0
  11. package/claude/skills/code-craft/SKILL.md +107 -0
  12. package/claude/skills/code-craft/references/adopted.md +52 -0
  13. package/claude/skills/codebase-layout/REQUIREMENT.md +1 -1
  14. package/claude/skills/deprecation-migration/REQUIREMENT.md +62 -0
  15. package/claude/skills/deprecation-migration/SKILL.md +115 -0
  16. package/claude/skills/deprecation-migration/references/adopted.md +50 -0
  17. package/claude/skills/git-followup/REQUIREMENT.md +1 -0
  18. package/claude/skills/git-followup/SKILL.md +3 -0
  19. package/claude/skills/git-pr/REQUIREMENT.md +1 -0
  20. package/claude/skills/git-pr/SKILL.md +1 -1
  21. package/claude/skills/git-pr/references/evidence.md +2 -2
  22. package/claude/skills/git-ship/REQUIREMENT.md +4 -0
  23. package/claude/skills/git-ship/SKILL.md +13 -2
  24. package/claude/skills/review-craft/REQUIREMENT.md +3 -0
  25. package/claude/skills/review-craft/SKILL.md +1 -1
  26. package/claude/skills/review-craft/references/adopted.md +8 -0
  27. package/claude/skills/review-craft/references/security.md +5 -0
  28. package/claude/skills/review-ui/REQUIREMENT.md +6 -2
  29. package/claude/skills/review-ui/SKILL.md +23 -4
  30. package/claude/skills/review-ui/references/driving.md +5 -3
  31. package/claude/skills/review-ui/references/post.md +3 -2
  32. package/claude/skills/role-worker/REQUIREMENT.md +1 -0
  33. package/claude/skills/role-worker/SKILL.md +4 -0
  34. package/claude/skills/test-first/REQUIREMENT.md +3 -0
  35. package/claude/skills/test-first/SKILL.md +2 -1
  36. package/docs/agents/commands.md +1 -1
  37. package/docs/agents/index.md +1 -0
  38. package/docs/agents/markdown-audit.md +1 -1
  39. package/docs/agents/pr-evidence.md +5 -0
  40. package/docs/agents/pr-frames.md +112 -0
  41. package/docs/agents/pr-preview.md +40 -12
  42. package/docs/workflow/skill-map.md +15 -12
  43. package/governance/rules/code/000-code.md +5 -1
  44. package/package.json +1 -1
  45. package/src/claude/cases/misc.ts +15 -0
  46. package/src/claude/skills-audit.ts +3 -0
  47. package/src/commands/pr.ts +559 -7
  48. package/src/commands/tooling.ts +39 -16
  49. package/src/context/architecture.ts +5 -2
  50. package/src/gate/boundaries.ts +509 -0
  51. package/src/gate/measures.ts +8 -19
  52. package/src/gate/stages.ts +12 -28
  53. package/src/pr/frames.ts +323 -0
  54. package/src/pr/preview.ts +66 -0
  55. package/src/tooling/manifest.ts +18 -3
  56. package/src/tooling/seeds.ts +37 -0
  57. package/src/tooling/verify-stack.ts +249 -0
  58. package/tooling/base/seeds/.cspell/tech-stack.txt +2 -0
  59. package/tooling/claude/manifest.toml +3 -3
  60. package/tooling/go/manifest.toml +3 -0
  61. package/tooling/go/reference.md +1 -0
  62. package/tooling/php/manifest.toml +3 -0
  63. package/tooling/php/reference.md +1 -0
  64. package/tooling/python/manifest.toml +3 -0
  65. package/tooling/python/reference.md +1 -1
  66. package/tooling/python/seeds/.cspell/tech-stack.txt +0 -1
  67. package/scripts/core/check-capability-seeding.sh +0 -134
  68. package/scripts/core/check-ignore-parity.sh +0 -141
  69. package/scripts/core/check-plugin-boundary.sh +0 -45
  70. package/scripts/core/check-seed-independence.sh +0 -62
  71. package/scripts/core/check-skill-paths.sh +0 -24
  72. package/scripts/core/list-seed-roots.sh +0 -18
  73. package/scripts/lib/tooling.sh +0 -37
  74. package/scripts/tooling/verify.sh +0 -184
@@ -1,62 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -e
3
- set -o pipefail
4
-
5
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
- PROJECT_ROOT="${PROJECT_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd)}"
7
-
8
- source "$PROJECT_ROOT/scripts/lib/tooling.sh"
9
-
10
- TOOLKIT_TOKEN="canon"
11
- TOOLING_ROOT="$PROJECT_ROOT/tooling"
12
-
13
- # `find` writes to stderr and returns non-zero for a missing root, inside a
14
- # process substitution whose status nothing reads. Without this the walk covers
15
- # nothing and the check reports the seeds independent having never read one.
16
- if [ ! -d "$TOOLING_ROOT" ]; then
17
- echo "No tooling root at ${TOOLING_ROOT#"$PROJECT_ROOT/"}, seed independence unverifiable." >&2
18
- exit 1
19
- fi
20
-
21
- # An empty discovery is a real state rather than a broken walk, and the Seed
22
- # standards stage already reports it as a skip. Failing here would have the two
23
- # stages disagree about what the same condition means.
24
- seed_roots=$(collect_seed_roots)
25
- if [ -z "$seed_roots" ]; then
26
- echo "No seed root carries .claude/ or canon/, nothing to check." >&2
27
- exit 0
28
- fi
29
-
30
- # Markdown alone. The seed tree also ships hooks that call the toolkit CLI on
31
- # purpose, each reporting a named stale-index warning when the binary is absent,
32
- # and scoping by extension leaves them outside this walk without an exemption
33
- # list that would have to be maintained against them.
34
- cited=""
35
- measured=0
36
- while IFS= read -r seed_root; do
37
- while IFS= read -r file; do
38
- measured=$((measured + 1))
39
- # A token followed by `/` is the tracked surface root, a folder the seed
40
- # itself installs, rather than the binary. Every other spelling still
41
- # reports, which keeps a verb, a plugin skill prefix, and a bare mention in.
42
- while IFS= read -r hit; do
43
- cited="$cited ${file#"$PROJECT_ROOT/"}:$hit"$'\n'
44
- done < <(grep -nE "${TOOLKIT_TOKEN}([^/]|$)" "$file" || true)
45
- done < <(find "$PROJECT_ROOT/$seed_root" -type f -name '*.md')
46
- done <<<"$seed_roots"
47
-
48
- # Roots resolved and no markdown under any of them is a walk that covered
49
- # nothing, which is the verdict-without-a-measurement the guards above refuse for
50
- # a missing tree. Reporting a pass here would say the seeds cite no CLI on the
51
- # strength of having read no prose.
52
- if [ "$measured" -eq 0 ]; then
53
- echo "Seed roots resolved but carry no markdown, seed independence unverifiable." >&2
54
- exit 1
55
- fi
56
-
57
- if [ -n "$cited" ]; then
58
- echo "Seed prose cites the toolkit CLI:" >&2
59
- printf '%s' "$cited" >&2
60
- echo "A scaffolded project may not have $TOOLKIT_TOKEN installed. State the capability the line needs rather than the binary that supplies it." >&2
61
- exit 1
62
- fi
@@ -1,24 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -e
3
- set -o pipefail
4
-
5
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
- PROJECT_ROOT="${PROJECT_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd)}"
7
-
8
- SKILLS_DIR="$PROJECT_ROOT/claude/skills"
9
-
10
- # A shipped skill runs from a target project, where the toolkit's own wiki/ does
11
- # not exist. Anchoring on a non-path character keeps a target's .claude/wiki/ legal.
12
- BANNED_PATH_PATTERN='(^|[^[:alnum:]._/-])wiki/'
13
-
14
- [ -d "$SKILLS_DIR" ] || exit 0
15
-
16
- matches=$(grep -rnE "$BANNED_PATH_PATTERN" "$SKILLS_DIR" || true)
17
-
18
- [ -z "$matches" ] && exit 0
19
-
20
- echo "Shipped skills reference a repo-local path that does not exist in a target project:"
21
- echo "$matches" | sed "s|^$PROJECT_ROOT/||"
22
- echo
23
- echo "Reach supporting prose through a canon docs command, a standard cited at the flat root, or inlined text."
24
- exit 1
@@ -1,18 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Prints every seed root carrying a `.claude/` or a `canon/`, one per line and
3
- # relative to the project root.
4
- #
5
- # The discovery rule has one definition, `collect_seed_roots` in
6
- # `scripts/lib/tooling.sh`, which `check-seed-independence.sh` already reads.
7
- # This file is the route a caller outside bash takes to that same answer, so a
8
- # stack seeding either root later reaches both readers and the two stages
9
- # measuring seed content cannot disagree about which roots exist.
10
- set -e
11
- set -o pipefail
12
-
13
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
14
- PROJECT_ROOT="${PROJECT_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd)}"
15
-
16
- source "$PROJECT_ROOT/scripts/lib/tooling.sh"
17
-
18
- collect_seed_roots
@@ -1,37 +0,0 @@
1
- #!/usr/bin/env bash
2
-
3
- TOOLING_STACK_EXCLUDE=("claude")
4
-
5
- is_tooling_stack_excluded() {
6
- local name="$1"
7
- local excluded
8
- for excluded in "${TOOLING_STACK_EXCLUDE[@]}"; do
9
- if [ "$name" = "$excluded" ]; then
10
- return 0
11
- fi
12
- done
13
- return 1
14
- }
15
-
16
- list_tooling_stacks() {
17
- local tooling_dir="${1:-$PROJECT_ROOT/tooling}"
18
- local name
19
- while IFS= read -r entry; do
20
- name=$(basename "$entry")
21
- is_tooling_stack_excluded "$name" && continue
22
- echo "$name"
23
- done < <(find "$tooling_dir" -mindepth 1 -maxdepth 1 -type d | sort)
24
- }
25
-
26
- # Seed roots that carry a `.claude/` or a `canon/`, emitted relative to
27
- # `PROJECT_ROOT`. Every stage measuring seed content discovers through this
28
- # rather than naming a stack, so a stack seeding either root later arrives
29
- # covered with no edit to any caller. A stack seeding only tracked surfaces
30
- # carries `canon/` alone, which a `.claude/` test would drop from both stages.
31
- collect_seed_roots() {
32
- local dir
33
- for dir in "$PROJECT_ROOT"/tooling/*/seeds; do
34
- [ -d "$dir/.claude" ] || [ -d "$dir/canon" ] || continue
35
- printf '%s\n' "${dir#"$PROJECT_ROOT"/}"
36
- done
37
- }
@@ -1,184 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -e
3
- set -o pipefail
4
-
5
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
- PROJECT_ROOT="${PROJECT_ROOT:-$(dirname "$(dirname "$SCRIPT_DIR")")}"
7
-
8
- source "$PROJECT_ROOT/scripts/lib/ui.sh"
9
- source "$PROJECT_ROOT/scripts/lib/tooling.sh"
10
-
11
- show_help() {
12
- echo -e "${GREY}┌${NC}"
13
- echo -e "${GREY}├${NC} ${WHITE}Usage:${NC} canon tooling verify <stack> [options]"
14
- echo -e "${GREY}│${NC}"
15
- echo -e "${GREY}│${NC} ${WHITE}Options:${NC}"
16
- echo -e "${GREY}│${NC} --keep ${GREY}# Keep tmp dir after run for inspection${NC}"
17
- echo -e "${GREY}│${NC} -h, --help ${GREY}# Show this help message${NC}"
18
- echo -e "${GREY}│${NC}"
19
- echo -e "${GREY}│${NC} Scaffolds <stack> into .canon/tmp/runs/verify-<stack>/, syncs the full"
20
- echo -e "${GREY}│${NC} layer chain, and runs check + test:e2e + screenshot."
21
- echo -e "${GREY}└${NC}"
22
- exit 0
23
- }
24
-
25
- read_manifest_field() {
26
- local toml="$1"
27
- local section="$2"
28
- local field="$3"
29
- awk -v s="$section" -v f="$field" '
30
- $0 == "[" s "]" { in_section = 1; next }
31
- in_section && /^\[/ { exit }
32
- in_section && match($0, "^" f "[[:space:]]*=") {
33
- val = substr($0, RLENGTH + 1)
34
- sub(/^[[:space:]]+/, "", val)
35
- sub(/^"/, "", val)
36
- sub(/"$/, "", val)
37
- print val
38
- exit
39
- }
40
- ' "$toml"
41
- }
42
-
43
- run_phase() {
44
- local name="$1"
45
- shift
46
- log_step "$name"
47
- if "$@" >/tmp/verify-phase.log 2>&1; then
48
- log_info "$name passed"
49
- RESULTS+=("$name:pass")
50
- return 0
51
- fi
52
- log_warn "$name failed"
53
- tail -20 /tmp/verify-phase.log >&2
54
- RESULTS+=("$name:fail")
55
- FAILED=1
56
- return 1
57
- }
58
-
59
- main() {
60
- local stack=""
61
- local keep=0
62
-
63
- while [[ $# -gt 0 ]]; do
64
- case "$1" in
65
- -h | --help) show_help ;;
66
- --keep)
67
- keep=1
68
- shift
69
- ;;
70
- -*)
71
- log_error "Unknown option: $1"
72
- ;;
73
- *)
74
- if [ -z "$stack" ]; then
75
- stack="$1"
76
- shift
77
- else
78
- log_error "Unexpected argument: $1"
79
- fi
80
- ;;
81
- esac
82
- done
83
-
84
- [ -z "$stack" ] && log_error "Stack required. See --help."
85
-
86
- export CI=1
87
-
88
- if is_tooling_stack_excluded "$stack"; then
89
- log_error "Stack '$stack' is excluded from tooling."
90
- fi
91
-
92
- local manifest="$PROJECT_ROOT/tooling/$stack/manifest.toml"
93
- [ ! -f "$manifest" ] && log_error "No manifest at $manifest"
94
-
95
- local scaffold
96
- scaffold=$(read_manifest_field "$manifest" "stack" "scaffold")
97
- [ -z "$scaffold" ] && log_error "Stack '$stack' has no scaffold command in manifest."
98
-
99
- local prepare
100
- prepare=$(read_manifest_field "$manifest" "verify" "prepare")
101
-
102
- open_timeline "canon tooling verify $stack"
103
- trap close_timeline EXIT
104
-
105
- local tmp_root="$PROJECT_ROOT/.canon/tmp/runs"
106
- local tmp_dir="$tmp_root/verify-$stack"
107
-
108
- mkdir -p "$tmp_root"
109
- rm -rf "$tmp_dir"
110
-
111
- local project_name="verify-$stack"
112
- local scaffold_cmd="${scaffold//\{\{name\}\}/$project_name}"
113
-
114
- RESULTS=()
115
- FAILED=0
116
-
117
- log_step "Scaffolding $stack"
118
- (cd "$tmp_root" && eval "$scaffold_cmd") >/tmp/verify-scaffold.log 2>&1 || {
119
- log_warn "Scaffold failed"
120
- tail -20 /tmp/verify-scaffold.log >&2
121
- FAILED=1
122
- }
123
-
124
- if [ ! -d "$tmp_dir" ]; then
125
- log_error "Scaffold did not produce $tmp_dir"
126
- fi
127
-
128
- (cd "$tmp_dir" && git init -q && git add . && git commit -m "chore: verify scaffold" -q --no-verify)
129
-
130
- if [ -n "$prepare" ]; then
131
- run_phase "Prepare" bash -c "cd '$tmp_dir' && $prepare"
132
- fi
133
-
134
- log_info "Sync resolves via bun $PROJECT_ROOT/src/cli.ts, not PATH canon"
135
- run_phase "Sync" bash -c "cd '$tmp_dir' && CANON_NON_INTERACTIVE=1 bun '$PROJECT_ROOT/src/cli.ts' tooling sync $stack . --write"
136
-
137
- if [ -f "$tmp_dir/package.json" ]; then
138
- run_phase "lint:fix" bash -c "cd '$tmp_dir' && bun run lint:fix"
139
- run_phase "check" bash -c "cd '$tmp_dir' && bun run check"
140
-
141
- if grep -q '"test:e2e"' "$tmp_dir/package.json"; then
142
- run_phase "test:e2e" bash -c "cd '$tmp_dir' && bun run test:e2e"
143
- fi
144
-
145
- if grep -q '"screenshot"' "$tmp_dir/package.json"; then
146
- if run_phase "screenshot" bash -c "cd '$tmp_dir' && timeout 60 bun run screenshot"; then
147
- local shots
148
- shots=$(find "$tmp_dir/screenshots" -type f -name '*.png' 2>/dev/null | wc -l)
149
- if [ "$shots" -eq 0 ]; then
150
- log_warn "screenshot produced no png files"
151
- RESULTS+=("screenshot-artifacts:fail")
152
- FAILED=1
153
- else
154
- RESULTS+=("screenshot-artifacts:pass")
155
- fi
156
- fi
157
- fi
158
- fi
159
-
160
- log_step "Results"
161
- for r in "${RESULTS[@]}"; do
162
- local name="${r%:*}"
163
- local status="${r##*:}"
164
- if [ "$status" = "pass" ]; then
165
- log_info "$name"
166
- else
167
- log_warn "$name"
168
- fi
169
- done
170
-
171
- if [ "$keep" -eq 0 ] && [ "$FAILED" -eq 0 ]; then
172
- rm -rf "$tmp_dir"
173
- else
174
- log_info "Kept $tmp_dir"
175
- fi
176
-
177
- if [ "$FAILED" -ne 0 ]; then
178
- exit 1
179
- fi
180
- }
181
-
182
- if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
183
- main "$@"
184
- fi