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.
Files changed (57) hide show
  1. package/README.md +91 -0
  2. package/bin/coding-engine.js +16 -0
  3. package/lib/cli.js +86 -0
  4. package/lib/index.js +6 -0
  5. package/lib/setup.js +290 -0
  6. package/package.json +32 -0
  7. package/templates/.agents/skills/coding-architecture/SKILL.md +347 -0
  8. package/templates/.agents/skills/coding-code-review/SKILL.md +95 -0
  9. package/templates/.agents/skills/coding-cve-remediation/SKILL.md +163 -0
  10. package/templates/.agents/skills/coding-db-schema/SKILL.md +273 -0
  11. package/templates/.agents/skills/coding-deploy-config/SKILL.md +129 -0
  12. package/templates/.agents/skills/coding-docs-audit/SKILL.md +285 -0
  13. package/templates/.agents/skills/coding-docs-audit-autofix/SKILL.md +33 -0
  14. package/templates/.agents/skills/coding-http-api/SKILL.md +269 -0
  15. package/templates/.agents/skills/coding-issue-autodev/SKILL.md +172 -0
  16. package/templates/.agents/skills/coding-merge-request/SKILL.md +199 -0
  17. package/templates/.agents/skills/coding-observability/SKILL.md +193 -0
  18. package/templates/.agents/skills/coding-refactor-insight/SKILL.md +89 -0
  19. package/templates/.agents/skills/coding-release-merge/SKILL.md +224 -0
  20. package/templates/.agents/skills/coding-runtime-adapter/SKILL.md +115 -0
  21. package/templates/.agents/skills/coding-testing/SKILL.md +169 -0
  22. package/templates/AGENTS.md +179 -0
  23. package/templates/MR-Template-Default.md +33 -0
  24. package/templates/Makefile +370 -0
  25. package/templates/REGISTRY.md +53 -0
  26. package/templates/scripts/check-adr.sh +283 -0
  27. package/templates/scripts/check-comment-i18n.py +209 -0
  28. package/templates/scripts/check-comment-i18n.sh +38 -0
  29. package/templates/scripts/check-doc-refs.sh +40 -0
  30. package/templates/scripts/check-docs.sh +103 -0
  31. package/templates/scripts/check-go-names.sh +59 -0
  32. package/templates/scripts/check-iam-actions.py +126 -0
  33. package/templates/scripts/check-migration-sql-immutability.sh +232 -0
  34. package/templates/scripts/check-mr-desc.sh +165 -0
  35. package/templates/scripts/check-mr-size.sh +128 -0
  36. package/templates/scripts/check-mr-title.sh +123 -0
  37. package/templates/scripts/check-openapi.py +221 -0
  38. package/templates/scripts/check-rdb-structured-queries.sh +98 -0
  39. package/templates/scripts/check-repository-transactions.sh +100 -0
  40. package/templates/scripts/check-runbooks.sh +229 -0
  41. package/templates/scripts/check-skill-router-sync.py +331 -0
  42. package/templates/scripts/check-skills.sh +243 -0
  43. package/templates/scripts/docs-audit-to-issues.py +373 -0
  44. package/templates/scripts/docs-verification-reminder.sh +63 -0
  45. package/templates/scripts/find-ci-failures.sh +133 -0
  46. package/templates/scripts/find-stale-mrs.sh +72 -0
  47. package/templates/scripts/gen-adr-index.sh +122 -0
  48. package/templates/scripts/open-mr.sh +536 -0
  49. package/templates/scripts/regen-agent-md.sh +149 -0
  50. package/templates/scripts/run-mr-hygiene.sh +140 -0
  51. package/templates/scripts/runbook.sh +91 -0
  52. package/templates/scripts/self-maintenance-digest.py +125 -0
  53. package/templates/scripts/send-self-maintenance-lark-card.py +116 -0
  54. package/templates/scripts/skill-graph.sh +114 -0
  55. package/templates/scripts/skill-impact.sh +134 -0
  56. package/templates/scripts/skill-propose.sh +302 -0
  57. 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
+ }'