@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.
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/api-design/REQUIREMENT.md +1 -1
- package/claude/skills/api-design/SKILL.md +4 -3
- package/claude/skills/api-design/references/adopted.md +1 -1
- package/claude/skills/auto-ship/REQUIREMENT.md +1 -0
- package/claude/skills/auto-ship/SKILL.md +2 -2
- package/claude/skills/build-in-slices/REQUIREMENT.md +45 -0
- package/claude/skills/build-in-slices/SKILL.md +94 -0
- package/claude/skills/build-in-slices/references/adopted.md +41 -0
- package/claude/skills/code-craft/REQUIREMENT.md +48 -0
- package/claude/skills/code-craft/SKILL.md +107 -0
- package/claude/skills/code-craft/references/adopted.md +52 -0
- package/claude/skills/codebase-layout/REQUIREMENT.md +1 -1
- package/claude/skills/deprecation-migration/REQUIREMENT.md +62 -0
- package/claude/skills/deprecation-migration/SKILL.md +115 -0
- package/claude/skills/deprecation-migration/references/adopted.md +50 -0
- package/claude/skills/git-followup/REQUIREMENT.md +1 -0
- package/claude/skills/git-followup/SKILL.md +3 -0
- package/claude/skills/git-pr/REQUIREMENT.md +1 -0
- package/claude/skills/git-pr/SKILL.md +1 -1
- package/claude/skills/git-pr/references/evidence.md +2 -2
- package/claude/skills/git-ship/REQUIREMENT.md +4 -0
- package/claude/skills/git-ship/SKILL.md +13 -2
- package/claude/skills/review-craft/REQUIREMENT.md +3 -0
- package/claude/skills/review-craft/SKILL.md +1 -1
- package/claude/skills/review-craft/references/adopted.md +8 -0
- package/claude/skills/review-craft/references/security.md +5 -0
- package/claude/skills/review-ui/REQUIREMENT.md +6 -2
- package/claude/skills/review-ui/SKILL.md +23 -4
- package/claude/skills/review-ui/references/driving.md +5 -3
- package/claude/skills/review-ui/references/post.md +3 -2
- package/claude/skills/role-worker/REQUIREMENT.md +1 -0
- package/claude/skills/role-worker/SKILL.md +4 -0
- package/claude/skills/test-first/REQUIREMENT.md +3 -0
- package/claude/skills/test-first/SKILL.md +2 -1
- package/docs/agents/commands.md +1 -1
- package/docs/agents/index.md +1 -0
- package/docs/agents/markdown-audit.md +1 -1
- package/docs/agents/pr-evidence.md +5 -0
- package/docs/agents/pr-frames.md +112 -0
- package/docs/agents/pr-preview.md +40 -12
- package/docs/workflow/skill-map.md +15 -12
- package/governance/rules/code/000-code.md +5 -1
- package/package.json +1 -1
- package/src/claude/cases/misc.ts +15 -0
- package/src/claude/skills-audit.ts +3 -0
- package/src/commands/pr.ts +559 -7
- package/src/commands/tooling.ts +39 -16
- package/src/context/architecture.ts +5 -2
- package/src/gate/boundaries.ts +509 -0
- package/src/gate/measures.ts +8 -19
- package/src/gate/stages.ts +12 -28
- package/src/pr/frames.ts +323 -0
- package/src/pr/preview.ts +66 -0
- package/src/tooling/manifest.ts +18 -3
- package/src/tooling/seeds.ts +37 -0
- package/src/tooling/verify-stack.ts +249 -0
- package/tooling/base/seeds/.cspell/tech-stack.txt +2 -0
- package/tooling/claude/manifest.toml +3 -3
- package/tooling/go/manifest.toml +3 -0
- package/tooling/go/reference.md +1 -0
- package/tooling/php/manifest.toml +3 -0
- package/tooling/php/reference.md +1 -0
- package/tooling/python/manifest.toml +3 -0
- package/tooling/python/reference.md +1 -1
- package/tooling/python/seeds/.cspell/tech-stack.txt +0 -1
- package/scripts/core/check-capability-seeding.sh +0 -134
- package/scripts/core/check-ignore-parity.sh +0 -141
- package/scripts/core/check-plugin-boundary.sh +0 -45
- package/scripts/core/check-seed-independence.sh +0 -62
- package/scripts/core/check-skill-paths.sh +0 -24
- package/scripts/core/list-seed-roots.sh +0 -18
- package/scripts/lib/tooling.sh +0 -37
- 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
|
package/scripts/lib/tooling.sh
DELETED
|
@@ -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
|