trae-coding-engine 0.1.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/README.md +91 -0
- package/bin/coding-engine.js +16 -0
- package/lib/cli.js +86 -0
- package/lib/index.js +6 -0
- package/lib/setup.js +290 -0
- package/package.json +32 -0
- package/templates/.agents/skills/coding-architecture/SKILL.md +347 -0
- package/templates/.agents/skills/coding-code-review/SKILL.md +95 -0
- package/templates/.agents/skills/coding-cve-remediation/SKILL.md +163 -0
- package/templates/.agents/skills/coding-db-schema/SKILL.md +273 -0
- package/templates/.agents/skills/coding-deploy-config/SKILL.md +129 -0
- package/templates/.agents/skills/coding-docs-audit/SKILL.md +285 -0
- package/templates/.agents/skills/coding-docs-audit-autofix/SKILL.md +33 -0
- package/templates/.agents/skills/coding-http-api/SKILL.md +269 -0
- package/templates/.agents/skills/coding-issue-autodev/SKILL.md +172 -0
- package/templates/.agents/skills/coding-merge-request/SKILL.md +199 -0
- package/templates/.agents/skills/coding-observability/SKILL.md +193 -0
- package/templates/.agents/skills/coding-refactor-insight/SKILL.md +89 -0
- package/templates/.agents/skills/coding-release-merge/SKILL.md +224 -0
- package/templates/.agents/skills/coding-runtime-adapter/SKILL.md +115 -0
- package/templates/.agents/skills/coding-testing/SKILL.md +169 -0
- package/templates/AGENTS.md +179 -0
- package/templates/MR-Template-Default.md +33 -0
- package/templates/Makefile +370 -0
- package/templates/REGISTRY.md +53 -0
- package/templates/scripts/check-adr.sh +283 -0
- package/templates/scripts/check-comment-i18n.py +209 -0
- package/templates/scripts/check-comment-i18n.sh +38 -0
- package/templates/scripts/check-doc-refs.sh +40 -0
- package/templates/scripts/check-docs.sh +103 -0
- package/templates/scripts/check-go-names.sh +59 -0
- package/templates/scripts/check-iam-actions.py +126 -0
- package/templates/scripts/check-migration-sql-immutability.sh +232 -0
- package/templates/scripts/check-mr-desc.sh +165 -0
- package/templates/scripts/check-mr-size.sh +128 -0
- package/templates/scripts/check-mr-title.sh +123 -0
- package/templates/scripts/check-openapi.py +221 -0
- package/templates/scripts/check-rdb-structured-queries.sh +98 -0
- package/templates/scripts/check-repository-transactions.sh +100 -0
- package/templates/scripts/check-runbooks.sh +229 -0
- package/templates/scripts/check-skill-router-sync.py +331 -0
- package/templates/scripts/check-skills.sh +243 -0
- package/templates/scripts/docs-audit-to-issues.py +373 -0
- package/templates/scripts/docs-verification-reminder.sh +63 -0
- package/templates/scripts/find-ci-failures.sh +133 -0
- package/templates/scripts/find-stale-mrs.sh +72 -0
- package/templates/scripts/gen-adr-index.sh +122 -0
- package/templates/scripts/open-mr.sh +536 -0
- package/templates/scripts/regen-agent-md.sh +149 -0
- package/templates/scripts/run-mr-hygiene.sh +140 -0
- package/templates/scripts/runbook.sh +91 -0
- package/templates/scripts/self-maintenance-digest.py +125 -0
- package/templates/scripts/send-self-maintenance-lark-card.py +116 -0
- package/templates/scripts/skill-graph.sh +114 -0
- package/templates/scripts/skill-impact.sh +134 -0
- package/templates/scripts/skill-propose.sh +302 -0
- package/templates/scripts/skill-router-hook.sh +152 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Skill impact report — measures how often each coding-* skill is referenced
|
|
3
|
+
# in commit messages, to inform retire / promote / keep decisions.
|
|
4
|
+
#
|
|
5
|
+
# This is a metric, not a check. Always exits 0. Intended cadence: run every
|
|
6
|
+
# 90 days (or before deciding whether to retire a skill). Output is advisory.
|
|
7
|
+
#
|
|
8
|
+
# Counting model:
|
|
9
|
+
# refs = # commits whose message contains the skill name in a window
|
|
10
|
+
# maint = # commits that touch .agents/skills/<skill>/* in same window
|
|
11
|
+
# (this is the "self-maintenance" signal — typically excluded
|
|
12
|
+
# from the actual usage signal, but shown alongside for context)
|
|
13
|
+
# age = days since the first commit that introduced the skill dir
|
|
14
|
+
#
|
|
15
|
+
# Bands (heuristic, tunable below):
|
|
16
|
+
# age < 30d → NEW (give it time, no judgment)
|
|
17
|
+
# age ≥ 90d AND refs_365 == 0 → DEAD (retirement candidate)
|
|
18
|
+
# age ≥ 30d AND refs_365 == 0 → WATCH (cooling — re-check in 60d)
|
|
19
|
+
# refs_365 ≥ 20 → HOT (consider promoting to AGENTS.md)
|
|
20
|
+
# refs_365 ≥ 5 → WARM
|
|
21
|
+
# refs_365 ≥ 1 → COLD
|
|
22
|
+
|
|
23
|
+
set -euo pipefail
|
|
24
|
+
shopt -s nullglob
|
|
25
|
+
|
|
26
|
+
cd "$(dirname "$0")/.."
|
|
27
|
+
|
|
28
|
+
# shellcheck source=scripts/lib/skill-frontmatter.sh
|
|
29
|
+
. scripts/lib/skill-frontmatter.sh
|
|
30
|
+
|
|
31
|
+
NOW_EPOCH=$(date +%s)
|
|
32
|
+
|
|
33
|
+
# Tunable thresholds — adjust to your team's commit cadence
|
|
34
|
+
DEAD_AGE_DAYS=90 # below this age, never call something DEAD
|
|
35
|
+
NEW_AGE_DAYS=30 # below this age, classify as NEW (no judgment)
|
|
36
|
+
HOT_REFS_YEAR=20 # at or above this, propose promotion
|
|
37
|
+
WARM_REFS_YEAR=5
|
|
38
|
+
COLD_REFS_YEAR=1
|
|
39
|
+
|
|
40
|
+
count_refs() {
|
|
41
|
+
local pattern=$1 since=$2
|
|
42
|
+
git log --since="$since" --grep="$pattern" --format=%h 2>/dev/null | wc -l | tr -d ' '
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
count_touches() {
|
|
46
|
+
local dir=$1 since=$2
|
|
47
|
+
git log --since="$since" --format=%h -- "$dir" 2>/dev/null | wc -l | tr -d ' '
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
first_commit_date() {
|
|
51
|
+
git log --reverse --format=%cs -- "$1" 2>/dev/null | head -1
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
classify() {
|
|
55
|
+
local age=$1 refs365=$2
|
|
56
|
+
if [[ "$age" -lt "$NEW_AGE_DAYS" ]]; then echo "NEW"; return; fi
|
|
57
|
+
if [[ "$refs365" -eq 0 ]]; then
|
|
58
|
+
if [[ "$age" -ge "$DEAD_AGE_DAYS" ]]; then echo "DEAD"; else echo "WATCH"; fi
|
|
59
|
+
return
|
|
60
|
+
fi
|
|
61
|
+
if [[ "$refs365" -ge "$HOT_REFS_YEAR" ]]; then echo "HOT"; return; fi
|
|
62
|
+
if [[ "$refs365" -ge "$WARM_REFS_YEAR" ]]; then echo "WARM"; return; fi
|
|
63
|
+
echo "COLD"
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
[[ -d "$SKILLS_ROOT" ]] || { echo "[ERROR] $SKILLS_ROOT not found" >&2; exit 0; }
|
|
67
|
+
|
|
68
|
+
today=$(date +%Y-%m-%d)
|
|
69
|
+
echo "==> Skill impact report (as of $today)"
|
|
70
|
+
echo ""
|
|
71
|
+
|
|
72
|
+
new=0; watch=0; dead=0; cold=0; warm=0; hot=0
|
|
73
|
+
|
|
74
|
+
for dir in "$SKILLS_ROOT"/coding-*; do
|
|
75
|
+
[[ -d "$dir" ]] || continue
|
|
76
|
+
name=$(basename "$dir")
|
|
77
|
+
|
|
78
|
+
first=$(first_commit_date "$dir")
|
|
79
|
+
if [[ -n "$first" ]]; then
|
|
80
|
+
first_epoch=$(parse_date "$first")
|
|
81
|
+
if [[ -n "$first_epoch" ]]; then
|
|
82
|
+
age=$(( (NOW_EPOCH - first_epoch) / 86400 ))
|
|
83
|
+
else
|
|
84
|
+
age=0
|
|
85
|
+
fi
|
|
86
|
+
else
|
|
87
|
+
age=0
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
refs30=$(count_refs "$name" "30 days ago")
|
|
91
|
+
refs90=$(count_refs "$name" "90 days ago")
|
|
92
|
+
refs365=$(count_refs "$name" "365 days ago")
|
|
93
|
+
maint=$(count_touches "$dir" "365 days ago")
|
|
94
|
+
|
|
95
|
+
cls=$(classify "$age" "$refs365")
|
|
96
|
+
case "$cls" in
|
|
97
|
+
NEW) new=$((new+1)) ;;
|
|
98
|
+
WATCH) watch=$((watch+1)) ;;
|
|
99
|
+
DEAD) dead=$((dead+1)) ;;
|
|
100
|
+
COLD) cold=$((cold+1)) ;;
|
|
101
|
+
WARM) warm=$((warm+1)) ;;
|
|
102
|
+
HOT) hot=$((hot+1)) ;;
|
|
103
|
+
esac
|
|
104
|
+
|
|
105
|
+
note=""
|
|
106
|
+
case "$cls" in
|
|
107
|
+
DEAD) note=" ← retirement candidate" ;;
|
|
108
|
+
HOT) note=" ← consider promoting to AGENTS.md" ;;
|
|
109
|
+
WATCH) note=" ← re-check in 60d" ;;
|
|
110
|
+
esac
|
|
111
|
+
|
|
112
|
+
printf "%-18s age:%4dd refs:%3d / %3d / %3d (30d / 90d / 365d) maint:%3d %s%s\n" \
|
|
113
|
+
"$name" "$age" "$refs30" "$refs90" "$refs365" "$maint" "$cls" "$note"
|
|
114
|
+
|
|
115
|
+
if [[ "$refs365" -gt 0 ]]; then
|
|
116
|
+
git log --since="365 days ago" --grep="$name" --format=" %h %s" -3 2>/dev/null \
|
|
117
|
+
| head -3
|
|
118
|
+
fi
|
|
119
|
+
done
|
|
120
|
+
|
|
121
|
+
total=$((new + watch + dead + cold + warm + hot))
|
|
122
|
+
echo ""
|
|
123
|
+
echo "── summary ────────────────────────────────────"
|
|
124
|
+
printf " %d skill(s) analyzed\n" "$total"
|
|
125
|
+
[[ "$new" -gt 0 ]] && printf " %d NEW (< %dd old, no judgment yet)\n" "$new" "$NEW_AGE_DAYS"
|
|
126
|
+
[[ "$watch" -gt 0 ]] && printf " %d WATCH (cooling — re-check in 60d)\n" "$watch"
|
|
127
|
+
[[ "$dead" -gt 0 ]] && printf " %d DEAD (≥ %dd old, 0 refs/year — retire?)\n" "$dead" "$DEAD_AGE_DAYS"
|
|
128
|
+
[[ "$cold" -gt 0 ]] && printf " %d COLD (1-%d refs/year)\n" "$cold" "$((WARM_REFS_YEAR - 1))"
|
|
129
|
+
[[ "$warm" -gt 0 ]] && printf " %d WARM (%d-%d refs/year)\n" "$warm" "$WARM_REFS_YEAR" "$((HOT_REFS_YEAR - 1))"
|
|
130
|
+
[[ "$hot" -gt 0 ]] && printf " %d HOT (≥ %d refs/year — promote to AGENTS.md?)\n" "$hot" "$HOT_REFS_YEAR"
|
|
131
|
+
echo ""
|
|
132
|
+
echo " Thresholds (tune in scripts/skill-impact.sh):"
|
|
133
|
+
echo " NEW < ${NEW_AGE_DAYS}d | DEAD ≥ ${DEAD_AGE_DAYS}d & 0 refs"
|
|
134
|
+
echo " HOT ≥ ${HOT_REFS_YEAR}/y | WARM ≥ ${WARM_REFS_YEAR}/y | COLD ≥ ${COLD_REFS_YEAR}/y"
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Detects skill gaps and generates draft evolution proposals.
|
|
3
|
+
# Run via: make skill-propose
|
|
4
|
+
# Proposals are created at .agents/skills/proposals/ for human review before MR.
|
|
5
|
+
#
|
|
6
|
+
# Each probe is a self-contained function. Add a new probe by writing a new
|
|
7
|
+
# `probe_<skill>_<concern>` function and calling it at the bottom.
|
|
8
|
+
|
|
9
|
+
set -euo pipefail
|
|
10
|
+
shopt -s nullglob
|
|
11
|
+
|
|
12
|
+
PROPOSALS_DIR=".agents/skills/proposals"
|
|
13
|
+
DATE=$(date +%Y-%m-%d)
|
|
14
|
+
GENERATED=0
|
|
15
|
+
|
|
16
|
+
mkdir -p "$PROPOSALS_DIR"
|
|
17
|
+
|
|
18
|
+
# emit_proposal <skill> <slug> <type-line> <body>
|
|
19
|
+
# Skips silently if a proposal with the same skill+slug already exists.
|
|
20
|
+
emit_proposal() {
|
|
21
|
+
local skill=$1 slug=$2 type=$3 body=$4
|
|
22
|
+
local target="$PROPOSALS_DIR/$DATE-${skill}-${slug}.md"
|
|
23
|
+
|
|
24
|
+
if compgen -G "$PROPOSALS_DIR/*-${skill}-${slug}.md" > /dev/null; then
|
|
25
|
+
echo " [SKIP] proposal already exists: $skill / $slug"
|
|
26
|
+
return
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
cat > "$target" <<EOF
|
|
30
|
+
# Skill Evolution Proposal
|
|
31
|
+
<!-- Auto-generated by scripts/skill-propose.sh on $DATE -->
|
|
32
|
+
<!-- Review and correct any inferences before generating the MR description -->
|
|
33
|
+
<!-- from .gitlab/merge_request_templates/Default.md. -->
|
|
34
|
+
|
|
35
|
+
**Skill:** ${skill}
|
|
36
|
+
**Type:** ${type}
|
|
37
|
+
**Status:** DRAFT — requires human review before MR
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
${body}
|
|
42
|
+
EOF
|
|
43
|
+
echo " [PROPOSAL] $target"
|
|
44
|
+
GENERATED=$((GENERATED + 1))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
# ── Probe 1: coding-testing Layer→Strategy table gaps ────────────────────────
|
|
48
|
+
# Trigger: a new outbound adapter package has no entry in the strategy table.
|
|
49
|
+
probe_testing_layer_strategy() {
|
|
50
|
+
local skill=".agents/skills/coding-testing/SKILL.md"
|
|
51
|
+
[[ -f "$skill" ]] || return
|
|
52
|
+
|
|
53
|
+
while IFS= read -r dir; do
|
|
54
|
+
local pkg="${dir##*/}"
|
|
55
|
+
if grep -q "adapters/outbound/${pkg}" "$skill"; then
|
|
56
|
+
continue
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# Infer strategy from package naming heuristics
|
|
60
|
+
local strategy=2
|
|
61
|
+
local note="K8s/HTTP outbound adapter"
|
|
62
|
+
case "$pkg" in
|
|
63
|
+
*redis*|*stream*|*queue*|*eventbus*|*message_bus*)
|
|
64
|
+
strategy=3; note="Redis/stream adapter" ;;
|
|
65
|
+
*postgres*|*persistence*|*repository*)
|
|
66
|
+
strategy=5; note="Persistence adapter (requires testcontainers)" ;;
|
|
67
|
+
*registry*|*noop*)
|
|
68
|
+
strategy=1; note="Pure unit — no external dependency" ;;
|
|
69
|
+
esac
|
|
70
|
+
|
|
71
|
+
emit_proposal "coding-testing" "layer-strategy-${pkg}" \
|
|
72
|
+
"New rule — Layer→Strategy table entry" \
|
|
73
|
+
"## What changes
|
|
74
|
+
|
|
75
|
+
Add \`adapters/outbound/${pkg}/\` → Strategy ${strategy} to the Layer→Strategy decision table in \`SKILL.md\`.
|
|
76
|
+
|
|
77
|
+
## Evidence
|
|
78
|
+
|
|
79
|
+
**Trigger:** \`make check-skills\` detected \`internal/adapters/outbound/${pkg}/\` has no strategy mapping.
|
|
80
|
+
|
|
81
|
+
**Auto-inferred classification:** ${note} → Strategy ${strategy}
|
|
82
|
+
|
|
83
|
+
> ⚠ Verify this inference is correct by inspecting \`internal/adapters/outbound/${pkg}/\`
|
|
84
|
+
> before submitting. Wrong strategy mapping is worse than no mapping.
|
|
85
|
+
|
|
86
|
+
| Metric | Value |
|
|
87
|
+
|--------|-------|
|
|
88
|
+
| Detected by | \`make check-skills\` |
|
|
89
|
+
| Package path | \`internal/adapters/outbound/${pkg}/\` |
|
|
90
|
+
| Inferred strategy | ${strategy} |
|
|
91
|
+
|
|
92
|
+
## TODO before submitting as MR
|
|
93
|
+
|
|
94
|
+
- [ ] Open \`internal/adapters/outbound/${pkg}/\` and confirm strategy ${strategy} is correct
|
|
95
|
+
- [ ] Add the line to SKILL.md Layer→Strategy table
|
|
96
|
+
- [ ] If a new infrastructure pattern is involved, update the relevant strategy detail file
|
|
97
|
+
- [ ] Run \`make check-skills\` — should pass after your edit"
|
|
98
|
+
done < <(find internal/adapters/outbound -mindepth 1 -maxdepth 1 -type d 2>/dev/null)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
# ── Probe 2: coding-testing missing template references ──────────────────────
|
|
102
|
+
# Trigger: a strategy file references a template that doesn't exist.
|
|
103
|
+
probe_testing_missing_templates() {
|
|
104
|
+
for strategy_file in .agents/skills/coding-testing/strategies/*.md; do
|
|
105
|
+
[[ -f "$strategy_file" ]] || continue
|
|
106
|
+
while IFS= read -r raw; do
|
|
107
|
+
local tpl="${raw#templates/}"
|
|
108
|
+
if [[ ! -f ".agents/skills/coding-testing/templates/$tpl" ]]; then
|
|
109
|
+
local base="${tpl%.*}"
|
|
110
|
+
emit_proposal "coding-testing" "missing-template-${base}" \
|
|
111
|
+
"Missing template file" \
|
|
112
|
+
"## What changes
|
|
113
|
+
|
|
114
|
+
Strategy file \`${strategy_file##*/}\` references a template that does not exist:
|
|
115
|
+
\`.agents/skills/coding-testing/templates/${tpl}\`
|
|
116
|
+
|
|
117
|
+
## Evidence
|
|
118
|
+
|
|
119
|
+
**Trigger:** \`scripts/skill-propose.sh\` detected a broken template reference.
|
|
120
|
+
|
|
121
|
+
## TODO
|
|
122
|
+
|
|
123
|
+
- [ ] Create the missing template, or remove the reference from the strategy file
|
|
124
|
+
- [ ] Generate an MR description from the standard template and submit via \`make open-mr ... ARGS=\"--body-file ...\"\`"
|
|
125
|
+
fi
|
|
126
|
+
done < <(grep -o 'templates/[^ )]*' "$strategy_file" || true)
|
|
127
|
+
done
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
# ── Probe 3: coding-architecture application/port/ inventory drift ───────────────────
|
|
131
|
+
# Trigger: a port file exists in code but isn't listed in the inventory table.
|
|
132
|
+
probe_architecture_ports() {
|
|
133
|
+
local skill=".agents/skills/coding-architecture/SKILL.md"
|
|
134
|
+
[[ -f "$skill" ]] || return
|
|
135
|
+
|
|
136
|
+
for port_file in internal/application/port/*.go; do
|
|
137
|
+
[[ -f "$port_file" ]] || continue
|
|
138
|
+
local base="${port_file##*/}"
|
|
139
|
+
|
|
140
|
+
# Skip generated and helper files that aren't supposed to be in the table
|
|
141
|
+
case "$base" in
|
|
142
|
+
*_test.go) continue ;;
|
|
143
|
+
esac
|
|
144
|
+
|
|
145
|
+
# Inventory entries are written as backtick-wrapped filenames, e.g. `embedding.go`
|
|
146
|
+
if grep -qF "\`${base}\`" "$skill"; then
|
|
147
|
+
continue
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
emit_proposal "coding-architecture" "port-inventory-${base%.go}" \
|
|
151
|
+
"Port inventory drift — file not listed" \
|
|
152
|
+
"## What changes
|
|
153
|
+
|
|
154
|
+
The file \`internal/application/port/${base}\` exists in the codebase but is not
|
|
155
|
+
listed in the \`application/port/\` inventory table in \`SKILL.md\`. Either add
|
|
156
|
+
it to the table (with interface name + primary user) or document it as a helper-only file.
|
|
157
|
+
|
|
158
|
+
## Evidence
|
|
159
|
+
|
|
160
|
+
**Trigger:** \`scripts/skill-propose.sh\` scanned \`internal/application/port/*.go\`
|
|
161
|
+
and found \`${base}\` is not mentioned in \`coding-architecture/SKILL.md\`.
|
|
162
|
+
|
|
163
|
+
## TODO before submitting as MR
|
|
164
|
+
|
|
165
|
+
- [ ] Open \`internal/application/port/${base}\` and identify the exported interface (or helpers)
|
|
166
|
+
- [ ] Add a row to the \`application/port/\` inventory table
|
|
167
|
+
- [ ] Generate an MR description from the standard template and submit via \`make open-mr ... ARGS=\"--body-file ...\"\`"
|
|
168
|
+
done
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
# ── Probe 4: coding-runtime-adapter runtime adapter not registered ──────────────────
|
|
172
|
+
# Trigger: a directory under outbound/ has BOTH executor.go AND controller.go
|
|
173
|
+
# (the canonical "full runtime" signature) but is not mentioned in
|
|
174
|
+
# coding-runtime-adapter's existing-providers reference.
|
|
175
|
+
probe_runtime_adapters() {
|
|
176
|
+
local skill=".agents/skills/coding-runtime-adapter/SKILL.md"
|
|
177
|
+
[[ -f "$skill" ]] || return
|
|
178
|
+
|
|
179
|
+
for ctl_file in internal/adapters/outbound/*/controller.go; do
|
|
180
|
+
[[ -f "$ctl_file" ]] || continue
|
|
181
|
+
local dir="${ctl_file%/controller.go}"
|
|
182
|
+
[[ -f "$dir/executor.go" ]] || continue # full-runtime gate
|
|
183
|
+
|
|
184
|
+
local pkg="${dir##*/}"
|
|
185
|
+
local pkg_cap
|
|
186
|
+
pkg_cap="$(printf '%s' "${pkg:0:1}" | tr '[:lower:]' '[:upper:]')${pkg:1}"
|
|
187
|
+
|
|
188
|
+
# Already mentioned in bandari? Look for `pkg` in EngineKind cells, table cells, or prose
|
|
189
|
+
if grep -qE "(\b${pkg}\b|EngineKind${pkg_cap})" "$skill"; then
|
|
190
|
+
continue
|
|
191
|
+
fi
|
|
192
|
+
|
|
193
|
+
# Probe each component of the quad for completeness
|
|
194
|
+
local missing=()
|
|
195
|
+
[[ -f "$dir/discovery.go" ]] || missing+=("discovery.go")
|
|
196
|
+
local has_cred=0 cred
|
|
197
|
+
for cred in "$dir"/apikey*.go "$dir"/token*.go "$dir"/credential*.go; do
|
|
198
|
+
[[ -f "$cred" && "$cred" != *_test.go ]] || continue
|
|
199
|
+
has_cred=1
|
|
200
|
+
break
|
|
201
|
+
done
|
|
202
|
+
[[ "$has_cred" -eq 1 ]] || missing+=("credential file (apikey.go / token.go / credential*.go)")
|
|
203
|
+
|
|
204
|
+
local missing_summary="(quad complete)"
|
|
205
|
+
if [[ ${#missing[@]} -gt 0 ]]; then
|
|
206
|
+
missing_summary="missing: $(IFS=', '; echo "${missing[*]}")"
|
|
207
|
+
fi
|
|
208
|
+
|
|
209
|
+
emit_proposal "coding-runtime-adapter" "runtime-${pkg}" \
|
|
210
|
+
"New runtime adapter not registered" \
|
|
211
|
+
"## What changes
|
|
212
|
+
|
|
213
|
+
A new runtime adapter exists at \`internal/adapters/outbound/${pkg}/\` (with both
|
|
214
|
+
\`executor.go\` and \`controller.go\`) but is not mentioned in \`coding-runtime-adapter/SKILL.md\`.
|
|
215
|
+
Add it to the Existing Providers Quick Reference table and verify the four-file
|
|
216
|
+
quad is complete.
|
|
217
|
+
|
|
218
|
+
## Evidence
|
|
219
|
+
|
|
220
|
+
**Trigger:** \`scripts/skill-propose.sh\` scanned \`internal/adapters/outbound/\`
|
|
221
|
+
and found \`${pkg}\` matches the runtime-adapter signature but is not referenced
|
|
222
|
+
in \`coding-runtime-adapter\`.
|
|
223
|
+
|
|
224
|
+
| Item | Value |
|
|
225
|
+
|---|---|
|
|
226
|
+
| Package path | \`internal/adapters/outbound/${pkg}/\` |
|
|
227
|
+
| Quad status | ${missing_summary} |
|
|
228
|
+
|
|
229
|
+
## TODO before submitting as MR
|
|
230
|
+
|
|
231
|
+
- [ ] Confirm \`${pkg}\` is intended as a full runtime provider (not a placeholder)
|
|
232
|
+
- [ ] Verify the four-file quad: \`executor.go\` / \`controller.go\` / \`discovery.go\` / credential file
|
|
233
|
+
- [ ] Add a row to the Existing Providers Quick Reference in \`SKILL.md\`
|
|
234
|
+
- [ ] Add \`EngineKind${pkg_cap}\` to \`internal/domain/shared/enums.go\` if not present
|
|
235
|
+
- [ ] Wire in \`internal/bootstrap/worker/registry.go\` via \`reg.Register(...)\`
|
|
236
|
+
- [ ] Reconcile worker config surfaces (see \`coding-deploy-config\` 5-surface gate)"
|
|
237
|
+
done
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
# ── Probe 5: coding-http-api IDL service has no handler binding ──────────────────
|
|
241
|
+
# Trigger: an IDL `service XxxService { ... }` has no compile-time interface
|
|
242
|
+
# assertion (`var _ <pkg>.XxxService = (*<H>Handler)(nil)`) in any handler.
|
|
243
|
+
probe_http_api_idl_handlers() {
|
|
244
|
+
local handlers_glob='internal/adapters/inbound/http/handlers/*.go'
|
|
245
|
+
|
|
246
|
+
while IFS= read -r service_name; do
|
|
247
|
+
[[ -n "$service_name" ]] || continue
|
|
248
|
+
|
|
249
|
+
# Look for `var _ <pkg>.<ServiceName> = (`-style interface assertion
|
|
250
|
+
if grep -qE "\.${service_name}[[:space:]]*=[[:space:]]*\(" $handlers_glob 2>/dev/null; then
|
|
251
|
+
continue
|
|
252
|
+
fi
|
|
253
|
+
|
|
254
|
+
local agg
|
|
255
|
+
agg=$(echo "$service_name" | sed 's/Service$//' | tr '[:upper:]' '[:lower:]')
|
|
256
|
+
|
|
257
|
+
emit_proposal "coding-http-api" "idl-handler-${agg}" \
|
|
258
|
+
"IDL service without handler binding" \
|
|
259
|
+
"## What changes
|
|
260
|
+
|
|
261
|
+
The Thrift IDL defines \`service ${service_name}\` but no Hertz handler under
|
|
262
|
+
\`internal/adapters/inbound/http/handlers/\` carries a compile-time
|
|
263
|
+
interface-assertion line of the form:
|
|
264
|
+
\`\`\`go
|
|
265
|
+
var _ <pkg>.${service_name} = (*<Handler>)(nil)
|
|
266
|
+
\`\`\`
|
|
267
|
+
|
|
268
|
+
Either implement the handler, OR if the service is intentionally deferred,
|
|
269
|
+
document it as \`[规划中]\` per \`docs/CONVENTIONS.md\` and explicitly note the
|
|
270
|
+
gap in the relevant component doc.
|
|
271
|
+
|
|
272
|
+
## Evidence
|
|
273
|
+
|
|
274
|
+
**Trigger:** \`scripts/skill-propose.sh\` cross-referenced \`api/idl/*.thrift\`
|
|
275
|
+
service definitions against \`var _ <pkg>.<ServiceName> = (\` assertions in
|
|
276
|
+
the handlers directory and found no match for \`${service_name}\`.
|
|
277
|
+
|
|
278
|
+
## TODO before submitting as MR
|
|
279
|
+
|
|
280
|
+
- [ ] Decide intent: implement now, or mark \`[规划中]\` in the component doc
|
|
281
|
+
- [ ] If implementing: create handler file, add interface assertion line
|
|
282
|
+
- [ ] If deferring: open an issue tracking the work, link from doc
|
|
283
|
+
- [ ] If skill rule needs an exception clause for known-deferred services, update SKILL.md"
|
|
284
|
+
done < <(grep -hE '^[[:space:]]*service [A-Z][A-Za-z]+' api/idl/*.thrift 2>/dev/null \
|
|
285
|
+
| awk '{print $2}' | sort -u)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
# ── Run all probes ──────────────────────────────────────────────────────────
|
|
289
|
+
|
|
290
|
+
probe_testing_layer_strategy
|
|
291
|
+
probe_testing_missing_templates
|
|
292
|
+
probe_architecture_ports
|
|
293
|
+
probe_runtime_adapters
|
|
294
|
+
probe_http_api_idl_handlers
|
|
295
|
+
|
|
296
|
+
echo ""
|
|
297
|
+
if [ "$GENERATED" -eq 0 ]; then
|
|
298
|
+
echo " [OK] No skill gaps detected — no proposals generated"
|
|
299
|
+
else
|
|
300
|
+
echo " [$GENERATED proposal(s) created in $PROPOSALS_DIR/]"
|
|
301
|
+
echo " Review each file, correct inferences, then submit as MR."
|
|
302
|
+
fi
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Path-triggered skill-router hook — nudges an agent to invoke the relevant
|
|
3
|
+
# coding-* skill whenever an edit touches a path with a known skill mapping.
|
|
4
|
+
#
|
|
5
|
+
# Currently wired by Claude Code through .claude/settings.json
|
|
6
|
+
# hooks.PostToolUse. Other tools may reuse this script if they provide the same
|
|
7
|
+
# JSON shape on stdin or wrap it with an adapter.
|
|
8
|
+
#
|
|
9
|
+
# Input contract: JSON containing .tool_name and .tool_input.file_path.
|
|
10
|
+
# Output contract: Claude-compatible hookSpecificOutput.additionalContext.
|
|
11
|
+
#
|
|
12
|
+
# This script is a thin mirror of the §Skill Router table in AGENTS.md.
|
|
13
|
+
# AGENTS.md is the cross-tool source of truth; when the table changes, update
|
|
14
|
+
# AGENTS.md first, then mirror in this file and .cursor/rules/skill-router.mdc.
|
|
15
|
+
# `make check-skill-router-sync` (in `make check` + pre-commit) fails the build
|
|
16
|
+
# if the three mirrors drift, so a forgotten mirror is caught, not silent.
|
|
17
|
+
#
|
|
18
|
+
# Performance note: this fires on EVERY Edit/Write tool call. Hot path is
|
|
19
|
+
# kept minimal — read stdin once, single jq invocation, and resolve the repo
|
|
20
|
+
# root only when an edit-capable tool provides a file path.
|
|
21
|
+
|
|
22
|
+
set -euo pipefail
|
|
23
|
+
|
|
24
|
+
# Skip silently if jq is missing (don't break the user's session)
|
|
25
|
+
command -v jq >/dev/null 2>&1 || exit 0
|
|
26
|
+
|
|
27
|
+
# Single jq call extracts both fields as a tab-separated record
|
|
28
|
+
INPUT=$(cat 2>/dev/null || true)
|
|
29
|
+
[[ -z "$INPUT" ]] && exit 0
|
|
30
|
+
IFS=$'\t' read -r TOOL FILE < <(
|
|
31
|
+
printf '%s' "$INPUT" | jq -r '[.tool_name // "", .tool_input.file_path // ""] | @tsv'
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
case "$TOOL" in
|
|
35
|
+
Edit|Write|MultiEdit|NotebookEdit) ;;
|
|
36
|
+
*) exit 0 ;;
|
|
37
|
+
esac
|
|
38
|
+
[[ -z "$FILE" ]] && exit 0
|
|
39
|
+
|
|
40
|
+
# Repo root: prefer the harness-provided env var, fall back to git. Do not
|
|
41
|
+
# require a specific checkout directory name; CI may check the repo out at
|
|
42
|
+
# /home/code while local worktrees often end in coding_engine.
|
|
43
|
+
REPO="${CLAUDE_PROJECT_DIR:-}"
|
|
44
|
+
if [[ -n "$REPO" ]]; then
|
|
45
|
+
REPO="${REPO%/}"
|
|
46
|
+
else
|
|
47
|
+
REPO=$(git -C "$(dirname "$FILE")" rev-parse --show-toplevel 2>/dev/null || true)
|
|
48
|
+
fi
|
|
49
|
+
[[ -z "$REPO" ]] && exit 0
|
|
50
|
+
|
|
51
|
+
REL="${FILE#"$REPO"/}"
|
|
52
|
+
[[ "$REL" == "$FILE" ]] && exit 0 # path was outside repo
|
|
53
|
+
|
|
54
|
+
# ── Path → skill mapping ─────────────────────────────────────────────────────
|
|
55
|
+
SKILL=""
|
|
56
|
+
REASON=""
|
|
57
|
+
|
|
58
|
+
case "$REL" in
|
|
59
|
+
# Redis-backed code — cluster compatibility plus test strategy both apply.
|
|
60
|
+
internal/adapters/outbound/**/redis*.go | \
|
|
61
|
+
internal/adapters/outbound/eventbus/* | \
|
|
62
|
+
internal/adapters/outbound/messagebus/* | \
|
|
63
|
+
internal/adapters/outbound/presence/* | \
|
|
64
|
+
internal/adapters/outbound/lane/redislane/* | \
|
|
65
|
+
internal/adapters/outbound/sessionlease/* | \
|
|
66
|
+
pkg/platform/redis/*)
|
|
67
|
+
SKILL="coding-architecture and coding-testing"
|
|
68
|
+
REASON="Redis-backed code — covers single/sentinel/cluster compatibility, Lua hash tags, and miniredis/testcontainer strategy" ;;
|
|
69
|
+
|
|
70
|
+
# Tests — covers the 5 strategies and t.Parallel/race conventions.
|
|
71
|
+
*_test.go | tests/*)
|
|
72
|
+
SKILL="coding-testing"
|
|
73
|
+
REASON="tests — covers the 5 strategies and t.Parallel/race conventions" ;;
|
|
74
|
+
|
|
75
|
+
# Runtime adapters — executor/controller, provider registry, and config sync.
|
|
76
|
+
internal/adapters/outbound/hermes/* | \
|
|
77
|
+
internal/adapters/outbound/workspacejob/* | \
|
|
78
|
+
internal/bootstrap/worker/registry.go)
|
|
79
|
+
SKILL="coding-runtime-adapter and coding-deploy-config"
|
|
80
|
+
REASON="runtime adapter — covers Executor/Controller, CRD/StatefulSet patterns, worker provider wiring, and config sync gate" ;;
|
|
81
|
+
|
|
82
|
+
# Schema — explicit SQL migrations own coding-engine main DB schema.
|
|
83
|
+
internal/adapters/outbound/persistence/rdb/po/* | \
|
|
84
|
+
migration/*)
|
|
85
|
+
SKILL="coding-db-schema"
|
|
86
|
+
REASON="schema change — explicit migration SQL, RDB repository behavior, and forward-only safety apply" ;;
|
|
87
|
+
|
|
88
|
+
# IDL / handlers / generated OpenAPI HTML — cross-layer cleanup and OpenAPI sync.
|
|
89
|
+
api/idl/*.thrift | \
|
|
90
|
+
internal/adapters/inbound/http/handlers/* | \
|
|
91
|
+
docs/openapi/coding-engine-api.html)
|
|
92
|
+
SKILL="coding-http-api"
|
|
93
|
+
REASON="HTTP API surface — IDL/handler/frontend and OpenAPI YAML + HTML must align" ;;
|
|
94
|
+
|
|
95
|
+
# Observability — collector binary, otel pipeline, metrics catalogue.
|
|
96
|
+
# Listed BEFORE the deploy-config catch-all so collector chart templates
|
|
97
|
+
# route to the more-specific observability skill. Glob shape matches
|
|
98
|
+
# AGENTS.md §Skill Router (`configs/otel*.yaml*` covers otel-config.yaml,
|
|
99
|
+
# otel-config.yaml.example, and otelcol-build-config.yaml).
|
|
100
|
+
internal/observability/* | \
|
|
101
|
+
pkg/platform/telemetrydb/* | \
|
|
102
|
+
coding-otelcol/* | \
|
|
103
|
+
configs/otel*.yaml* | \
|
|
104
|
+
build/docker/otelcol.Dockerfile | \
|
|
105
|
+
deployments/coding-engine/templates/collector/*)
|
|
106
|
+
SKILL="coding-observability"
|
|
107
|
+
REASON="observability — metric catalogue + smoke guard, port.Metrics DI shape, ocb collector pipeline (ADR-0023)" ;;
|
|
108
|
+
|
|
109
|
+
# Deployment / config — 5-surface sync.
|
|
110
|
+
deployments/* | \
|
|
111
|
+
configs/engine.yaml* | \
|
|
112
|
+
internal/bootstrap/app/engine_config.go | \
|
|
113
|
+
internal/adapters/outbound/*/config.go)
|
|
114
|
+
SKILL="coding-deploy-config"
|
|
115
|
+
REASON="deployment/config — 5-surface sync gate applies" ;;
|
|
116
|
+
|
|
117
|
+
# New aggregate / port — DDD layer map and ServiceDeps shape.
|
|
118
|
+
internal/domain/* | \
|
|
119
|
+
internal/application/port/* | \
|
|
120
|
+
internal/application/*/service.go)
|
|
121
|
+
SKILL="coding-architecture"
|
|
122
|
+
REASON="aggregate/port — DDD layer map and ServiceDeps shape" ;;
|
|
123
|
+
esac
|
|
124
|
+
|
|
125
|
+
# ── ADR-trigger note ─────────────────────────────────────────────────────────
|
|
126
|
+
# Architectural-surface edits may warrant an ADR. Narrow set (3 strongest
|
|
127
|
+
# "new architectural surface" signals) to avoid alert fatigue; appended to the
|
|
128
|
+
# advisory context below rather than emitted separately, to stay on the hot path.
|
|
129
|
+
ADR_NOTE=""
|
|
130
|
+
case "$REL" in
|
|
131
|
+
internal/domain/* | \
|
|
132
|
+
internal/application/port/* | \
|
|
133
|
+
api/idl/*.thrift)
|
|
134
|
+
ADR_NOTE=" If this introduces or reverses an architectural decision, record an ADR (see docs/adr/CONVENTIONS.md, Mode A/B)." ;;
|
|
135
|
+
esac
|
|
136
|
+
|
|
137
|
+
[[ -z "$SKILL" ]] && exit 0
|
|
138
|
+
|
|
139
|
+
# Emit advisory context for the next turn. The host agent reads
|
|
140
|
+
# additionalContext but is not forced to invoke; the skill advisory is a nudge,
|
|
141
|
+
# not an enforcement gate.
|
|
142
|
+
jq -n \
|
|
143
|
+
--arg skill "$SKILL" \
|
|
144
|
+
--arg rel "$REL" \
|
|
145
|
+
--arg reason "$REASON" \
|
|
146
|
+
--arg adr "$ADR_NOTE" \
|
|
147
|
+
'{
|
|
148
|
+
hookSpecificOutput: {
|
|
149
|
+
hookEventName: "PostToolUse",
|
|
150
|
+
additionalContext: ("[skill-router] Edit touched `" + $rel + "`. The `" + $skill + "` skill(s) apply (" + $reason + ")." + $adr + " Invoke the relevant skill tool if you have not already in this turn.")
|
|
151
|
+
}
|
|
152
|
+
}'
|