@tw93/waza 3.28.0 → 3.29.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.
@@ -46,6 +46,7 @@ FORCED_GITHUB_TOOL_RE = re.compile(
46
46
  r'for\s+all\s+GitHub\s+interactions,\s+not\s+MCP\s+or\s+raw\s+API)',
47
47
  re.IGNORECASE,
48
48
  )
49
+ CJK_RE = re.compile(r"[\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff]")
49
50
 
50
51
  DURABLE_CONTEXT_SKILLS = {"think", "check", "hunt", "design", "write", "health"}
51
52
 
@@ -59,6 +60,19 @@ ATTRIBUTION_PATTERNS = (
59
60
  "noreply@anthropic.com",
60
61
  "cursoragent@cursor.com",
61
62
  )
63
+ CODEX_MIRROR_IGNORED_DIRS = {
64
+ "__pycache__",
65
+ ".mypy_cache",
66
+ ".pytest_cache",
67
+ ".ruff_cache",
68
+ }
69
+ CODEX_MIRROR_IGNORED_NAMES = {
70
+ ".DS_Store",
71
+ }
72
+ CODEX_MIRROR_IGNORED_SUFFIXES = {
73
+ ".pyc",
74
+ ".pyo",
75
+ }
62
76
 
63
77
 
64
78
  def pipe_count(s: str) -> int:
@@ -75,6 +89,14 @@ def pipe_count(s: str) -> int:
75
89
  return n
76
90
 
77
91
 
92
+ def should_include_codex_mirror_file(path: Path) -> bool:
93
+ if any(part in CODEX_MIRROR_IGNORED_DIRS for part in path.parts):
94
+ return False
95
+ if path.name in CODEX_MIRROR_IGNORED_NAMES:
96
+ return False
97
+ return path.suffix not in CODEX_MIRROR_IGNORED_SUFFIXES
98
+
99
+
78
100
  def check_skill_files(root: Path):
79
101
  skill_files = sorted((root / "skills").glob("*/SKILL.md"))
80
102
  if not skill_files:
@@ -105,7 +127,14 @@ def check_skill_files(root: Path):
105
127
 
106
128
 
107
129
  def check_marketplace(root: Path, expected_version: str, skill_names: set[str], skill_descriptions: dict[str, str]):
108
- """Validate marketplace.json shape:
130
+ """Validate generated Claude Code and Codex marketplace metadata."""
131
+ check_claude_marketplace(root, expected_version, skill_names, skill_descriptions)
132
+ check_codex_plugin(root, expected_version)
133
+ check_codex_marketplace(root)
134
+
135
+
136
+ def check_claude_marketplace(root: Path, expected_version: str, skill_names: set[str], skill_descriptions: dict[str, str]):
137
+ """Validate Claude Code marketplace.json shape:
109
138
 
110
139
  - One bundle entry: name == "waza", source == "./".
111
140
  - Per-skill entries: name == "waza-<skill>", source == "./skills/<skill>".
@@ -202,6 +231,138 @@ def check_marketplace(root: Path, expected_version: str, skill_names: set[str],
202
231
  print(f"ok: all versions in lock-step with VERSION={expected_version}")
203
232
 
204
233
 
234
+ def check_codex_plugin(root: Path, expected_version: str):
235
+ """Validate Codex plugin manifest shape."""
236
+ plugin_root = root / "plugins" / "waza"
237
+ manifest_path = plugin_root / ".codex-plugin" / "plugin.json"
238
+ if not manifest_path.exists():
239
+ fail(
240
+ "MISSING CODEX PLUGIN MANIFEST: expected "
241
+ "plugins/waza/.codex-plugin/plugin.json "
242
+ "so Codex can install Waza as a plugin from the repo marketplace"
243
+ )
244
+ manifest = json.loads(manifest_path.read_text())
245
+ required = {
246
+ "name": "waza",
247
+ "version": expected_version,
248
+ "skills": "./skills/",
249
+ "license": "MIT",
250
+ "homepage": "https://github.com/tw93/Waza",
251
+ "repository": "https://github.com/tw93/Waza",
252
+ }
253
+ for key, expected in required.items():
254
+ actual = manifest.get(key)
255
+ if actual != expected:
256
+ fail(
257
+ f"CODEX PLUGIN FIELD DRIFT: plugins/waza/.codex-plugin/plugin.json {key}="
258
+ f"{actual!r} expected {expected!r}"
259
+ )
260
+ if not (manifest.get("description") or "").strip():
261
+ fail("CODEX PLUGIN DESCRIPTION: plugins/waza/.codex-plugin/plugin.json needs description")
262
+ author = manifest.get("author")
263
+ if not isinstance(author, dict) or not author.get("name"):
264
+ fail("CODEX PLUGIN AUTHOR: plugins/waza/.codex-plugin/plugin.json needs author.name")
265
+ interface = manifest.get("interface")
266
+ if not isinstance(interface, dict):
267
+ fail("CODEX PLUGIN INTERFACE: plugins/waza/.codex-plugin/plugin.json needs interface object")
268
+ interface_required = {
269
+ "displayName": "Waza",
270
+ "developerName": "Tw93",
271
+ "category": "Developer Tools",
272
+ "websiteURL": "https://github.com/tw93/Waza",
273
+ }
274
+ for key, expected in interface_required.items():
275
+ actual = interface.get(key)
276
+ if actual != expected:
277
+ fail(
278
+ f"CODEX PLUGIN INTERFACE DRIFT: plugins/waza/.codex-plugin/plugin.json "
279
+ f"interface.{key}={actual!r} expected {expected!r}"
280
+ )
281
+ default_prompt = interface.get("defaultPrompt")
282
+ if (
283
+ not isinstance(default_prompt, list)
284
+ or not default_prompt
285
+ or len(default_prompt) > 3
286
+ or any(not isinstance(item, str) or len(item) > 128 for item in default_prompt)
287
+ ):
288
+ fail(
289
+ "CODEX PLUGIN DEFAULT PROMPTS: interface.defaultPrompt must contain "
290
+ "1-3 strings, each <=128 chars"
291
+ )
292
+ if not (plugin_root / "skills").is_dir():
293
+ fail(
294
+ "CODEX PLUGIN SKILLS PATH: plugins/waza/.codex-plugin/plugin.json "
295
+ "points at missing plugins/waza/skills/"
296
+ )
297
+ for source_name in ("skills", "rules"):
298
+ source_root = root / source_name
299
+ mirror_root = plugin_root / source_name
300
+ for source_path in sorted(source_root.rglob("*")):
301
+ if not source_path.is_file():
302
+ continue
303
+ source_rel = source_path.relative_to(source_root)
304
+ if not should_include_codex_mirror_file(source_rel):
305
+ continue
306
+ mirror_path = mirror_root / source_rel
307
+ if not mirror_path.exists():
308
+ fail(
309
+ f"CODEX PLUGIN MIRROR MISSING: {mirror_path.relative_to(root)} "
310
+ f"must mirror {source_path.relative_to(root)}"
311
+ )
312
+ if mirror_path.read_bytes() != source_path.read_bytes():
313
+ fail(
314
+ f"CODEX PLUGIN MIRROR DRIFT: {mirror_path.relative_to(root)} "
315
+ f"differs from {source_path.relative_to(root)}"
316
+ )
317
+ print(f"ok: Codex plugin manifest pinned to {expected_version}")
318
+
319
+
320
+ def check_codex_marketplace(root: Path):
321
+ """Validate repo-local Codex marketplace shape."""
322
+ marketplace_path = root / ".agents" / "plugins" / "marketplace.json"
323
+ if not marketplace_path.exists():
324
+ fail(
325
+ "MISSING CODEX MARKETPLACE: expected .agents/plugins/marketplace.json "
326
+ "so `codex plugin marketplace add tw93/Waza` can discover Waza"
327
+ )
328
+ marketplace = json.loads(marketplace_path.read_text())
329
+ if marketplace.get("name") != "waza":
330
+ fail("CODEX MARKETPLACE NAME: .agents/plugins/marketplace.json name must be 'waza'")
331
+ interface = marketplace.get("interface")
332
+ if not isinstance(interface, dict) or interface.get("displayName") != "Waza":
333
+ fail(
334
+ "CODEX MARKETPLACE DISPLAY NAME: .agents/plugins/marketplace.json "
335
+ "must set interface.displayName to 'Waza'"
336
+ )
337
+ plugins = marketplace.get("plugins")
338
+ if not isinstance(plugins, list) or len(plugins) != 1:
339
+ fail("CODEX MARKETPLACE PLUGINS: expected exactly one Waza plugin entry")
340
+ entry = plugins[0]
341
+ expected_entry = {
342
+ "name": "waza",
343
+ "source": {
344
+ "source": "local",
345
+ "path": "./plugins/waza",
346
+ },
347
+ "policy": {
348
+ "installation": "AVAILABLE",
349
+ "authentication": "ON_INSTALL",
350
+ },
351
+ "category": "Developer Tools",
352
+ }
353
+ if entry != expected_entry:
354
+ fail(
355
+ "CODEX MARKETPLACE ENTRY DRIFT: .agents/plugins/marketplace.json "
356
+ f"plugins[0]={entry!r} expected {expected_entry!r}"
357
+ )
358
+ if not (root / "plugins" / "waza" / ".codex-plugin" / "plugin.json").exists():
359
+ fail(
360
+ "CODEX MARKETPLACE SOURCE: source.path './plugins/waza' must resolve to a plugin "
361
+ "root containing .codex-plugin/plugin.json"
362
+ )
363
+ print("ok: Codex marketplace exposes waza plugin")
364
+
365
+
205
366
  def check_references(root: Path, skill_files: list[Path]):
206
367
  for path in skill_files:
207
368
  skill_dir = path.parent.name
@@ -245,6 +406,11 @@ def check_description_conformance(skill_descriptions: dict[str, str]):
245
406
  f"DESCRIPTION MISSING EXCLUSION CLAUSE: {skill}\n"
246
407
  f" Must contain a 'Not for ...' clause so the resolver learns when NOT to fire. Got: {clean[:120]!r}"
247
408
  )
409
+ if CJK_RE.search(clean):
410
+ fail(
411
+ f"DESCRIPTION CONTAINS CJK: {skill}\n"
412
+ f" Keep public-facing description metadata English-only. Put multilingual trigger phrases in when_to_use."
413
+ )
248
414
  print(f"ok: description {skill} ({length} chars)")
249
415
 
250
416
 
@@ -638,6 +804,21 @@ def check_readme_install_command(root: Path):
638
804
  f"README INSTALL COMMAND: README.md must include {expected!r}\n"
639
805
  f" Waza's public install path depends on this exact string."
640
806
  )
807
+ expected_codex_marketplace = "codex plugin marketplace add tw93/Waza"
808
+ if expected_codex_marketplace not in text:
809
+ fail(
810
+ "README CODEX MARKETPLACE COMMAND: README.md must include "
811
+ f"{expected_codex_marketplace!r}\n"
812
+ f" Codex plugin installs should use the repo marketplace so users can "
813
+ f"upgrade without rerunning npx skills add."
814
+ )
815
+ expected_codex_install = "codex plugin add waza@waza"
816
+ if expected_codex_install not in text:
817
+ fail(
818
+ "README CODEX PLUGIN COMMAND: README.md must include "
819
+ f"{expected_codex_install!r}\n"
820
+ f" The Codex marketplace entry must document the plugin install selector."
821
+ )
641
822
  expected_pi = "pi install npm:@tw93/waza"
642
823
  if expected_pi not in text:
643
824
  fail(
@@ -668,8 +849,8 @@ def check_readme_install_command(root: Path):
668
849
  f"without per-release README churn."
669
850
  )
670
851
  print(
671
- "ok: README installs nested skills, Pi package, Antigravity, OpenCode, "
672
- "and latest installer assets"
852
+ "ok: README installs nested skills, Codex plugin marketplace, Pi package, "
853
+ "Antigravity, OpenCode, and latest installer assets"
673
854
  )
674
855
 
675
856
 
@@ -10,6 +10,7 @@ HIGHWATER_LOCK_DIR="$CACHE_DIR/highwater.lock"
10
10
  CACHE_MAX_AGE=21600 # 6 hours: one full rate_limit window
11
11
  HIGHWATER_LOCK_MAX_AGE=10
12
12
  HIGHWATER_RESET_SKEW_MAX=7200 # tolerate session jitter, reject crossed windows
13
+ HIGHWATER_DROP_RESET_MIN=5 # fresh lower live values must drop by at least 5%
13
14
 
14
15
  input=$(cat)
15
16
 
@@ -20,6 +21,9 @@ jq_full='[
20
21
  + (.context_window.current_usage.cache_creation_input_tokens // 0)
21
22
  + (.context_window.current_usage.cache_read_input_tokens // 0) | tostring),
22
23
  (.context_window.context_window_size // 0 | tostring),
24
+ (.session_id // "null" | tostring),
25
+ (.cost.total_api_duration_ms // 0 | tonumber? // 0 | floor | tostring),
26
+ (.context_window.total_output_tokens // 0 | tonumber? // 0 | floor | tostring),
23
27
  (.rate_limits.five_hour.used_percentage // null | if . then (. | round | tostring) else "null" end),
24
28
  (.rate_limits.five_hour.resets_at // "" | tostring),
25
29
  (.rate_limits.seven_day.used_percentage // null | if . then (. | round | tostring) else "null" end),
@@ -33,6 +37,16 @@ jq_rl='[
33
37
  (.rate_limits.seven_day.resets_at // "" | tostring)
34
38
  ] | @tsv'
35
39
 
40
+ jq_hw='[
41
+ (.five_hour.used_percentage // null | if . then (. | round | tostring) else "null" end),
42
+ (.five_hour.resets_at // "null" | tostring),
43
+ (.seven_day.used_percentage // null | if . then (. | round | tostring) else "null" end),
44
+ (.seven_day.resets_at // "null" | tostring),
45
+ (._last.session_id // "null" | tostring),
46
+ (._last.api_duration_ms // 0 | tonumber? // 0 | floor | tostring),
47
+ (._last.output_tokens // 0 | tonumber? // 0 | floor | tostring)
48
+ ] | @tsv'
49
+
36
50
  cache_file_mtime() {
37
51
  local path="$1"
38
52
  local ts=""
@@ -79,11 +93,40 @@ read_highwater() {
79
93
  hw_5h_reset=""
80
94
  hw_7d_pct=""
81
95
  hw_7d_reset=""
96
+ hw_last_session_id=""
97
+ hw_last_api_ms="0"
98
+ hw_last_output_tokens="0"
82
99
  [ -f "$HIGHWATER_FILE" ] || return
83
- hw_5h_pct=$(jq -r 'if .five_hour.used_percentage == null then "" else (.five_hour.used_percentage | round | tostring) end' "$HIGHWATER_FILE" 2>/dev/null)
84
- hw_5h_reset=$(jq -r 'if .five_hour.resets_at == null then "" else (.five_hour.resets_at | tostring) end' "$HIGHWATER_FILE" 2>/dev/null)
85
- hw_7d_pct=$(jq -r 'if .seven_day.used_percentage == null then "" else (.seven_day.used_percentage | round | tostring) end' "$HIGHWATER_FILE" 2>/dev/null)
86
- hw_7d_reset=$(jq -r 'if .seven_day.resets_at == null then "" else (.seven_day.resets_at | tostring) end' "$HIGHWATER_FILE" 2>/dev/null)
100
+ highwater_data=$(jq -r "$jq_hw" "$HIGHWATER_FILE" 2>/dev/null)
101
+ IFS="$tab" read -r hw_5h_pct hw_5h_reset hw_7d_pct hw_7d_reset hw_last_session_id hw_last_api_ms hw_last_output_tokens <<EOF
102
+ $highwater_data
103
+ EOF
104
+ [ "$hw_5h_pct" = "null" ] && hw_5h_pct=""
105
+ [ "$hw_5h_reset" = "null" ] && hw_5h_reset=""
106
+ [ "$hw_7d_pct" = "null" ] && hw_7d_pct=""
107
+ [ "$hw_7d_reset" = "null" ] && hw_7d_reset=""
108
+ [ "$hw_last_session_id" = "null" ] && hw_last_session_id=""
109
+ is_uint "$hw_last_api_ms" || hw_last_api_ms=0
110
+ is_uint "$hw_last_output_tokens" || hw_last_output_tokens=0
111
+ }
112
+
113
+ json_quote() {
114
+ printf '%s' "$1" | jq -Rs . 2>/dev/null
115
+ }
116
+
117
+ compute_fresh_activity() {
118
+ fresh_activity=0
119
+ [ "$live_rate_limits_present" = "1" ] || return
120
+ [ -n "$live_session_id" ] && [ "$live_session_id" != "null" ] || return
121
+ [ "$live_session_id" = "$hw_last_session_id" ] || return
122
+ is_uint "$live_api_ms" || live_api_ms=0
123
+ is_uint "$live_output_tokens" || live_output_tokens=0
124
+ is_uint "$hw_last_api_ms" || hw_last_api_ms=0
125
+ is_uint "$hw_last_output_tokens" || hw_last_output_tokens=0
126
+ if [ "$live_api_ms" -gt "$hw_last_api_ms" ] 2>/dev/null \
127
+ || [ "$live_output_tokens" -gt "$hw_last_output_tokens" ] 2>/dev/null; then
128
+ fresh_activity=1
129
+ fi
87
130
  }
88
131
 
89
132
  # apply_hw: compares live vs high-water marks for a single counter (5h or 7d).
@@ -132,28 +175,62 @@ apply_hw() {
132
175
  applied_hw_reset="$hw_reset"
133
176
  return
134
177
  fi
135
- applied_pct="$live_pct"
136
- applied_reset="$live_reset"
137
- applied_hw_pct="$live_pct"
138
- applied_hw_reset="$live_reset"
178
+ if [ "$hw_ok" = "0" ] || [ "$live_pct" -gt "$hw_pct" ] 2>/dev/null; then
179
+ applied_pct="$live_pct"
180
+ applied_reset="$live_reset"
181
+ applied_hw_pct="$live_pct"
182
+ applied_hw_reset="$live_reset"
183
+ return
184
+ fi
185
+ if [ "$fresh_activity" = "1" ] \
186
+ && is_uint "$live_reset" && is_uint "$hw_reset" \
187
+ && [ "$live_pct" -lt "$hw_pct" ] 2>/dev/null \
188
+ && [ $((hw_pct - live_pct)) -ge "$HIGHWATER_DROP_RESET_MIN" ] 2>/dev/null; then
189
+ applied_pct="$live_pct"
190
+ applied_reset="$live_reset"
191
+ applied_hw_pct="$live_pct"
192
+ applied_hw_reset="$live_reset"
193
+ return
194
+ fi
195
+
196
+ applied_pct="$hw_pct"
197
+ applied_reset="${live_reset:-$hw_reset}"
198
+ applied_hw_pct="$hw_pct"
199
+ applied_hw_reset="$hw_reset"
139
200
  }
140
201
 
141
202
  write_highwater() {
142
203
  is_uint "$new_hw_5h_pct" || is_uint "$new_hw_7d_pct" || return
143
204
  mkdir -p "$CACHE_DIR" 2>/dev/null || return
144
205
  local r5="${new_hw_5h_reset:-0}" r7="${new_hw_7d_reset:-0}"
206
+ local wrote=0 sid_json
145
207
  is_uint "$r5" || r5=0
146
208
  is_uint "$r7" || r7=0
147
209
  if ! {
148
210
  {
149
211
  printf '{\n'
212
+ if [ "$live_rate_limits_present" = "1" ] \
213
+ && [ -n "$live_session_id" ] && [ "$live_session_id" != "null" ]; then
214
+ sid_json=$(json_quote "$live_session_id")
215
+ printf ' "_last": {"session_id": %s, "api_duration_ms": %s, "output_tokens": %s}' \
216
+ "${sid_json:-\"\"}" "${live_api_ms:-0}" "${live_output_tokens:-0}"
217
+ wrote=1
218
+ elif [ -n "$hw_last_session_id" ]; then
219
+ sid_json=$(json_quote "$hw_last_session_id")
220
+ printf ' "_last": {"session_id": %s, "api_duration_ms": %s, "output_tokens": %s}' \
221
+ "${sid_json:-\"\"}" "${hw_last_api_ms:-0}" "${hw_last_output_tokens:-0}"
222
+ wrote=1
223
+ fi
150
224
  if is_uint "$new_hw_5h_pct"; then
225
+ [ "$wrote" = "1" ] && printf ',\n'
151
226
  printf ' "five_hour": {"used_percentage": %s, "resets_at": %s}' "$new_hw_5h_pct" "$r5"
152
- is_uint "$new_hw_7d_pct" && printf ','
153
- printf '\n'
227
+ wrote=1
154
228
  fi
155
229
  if is_uint "$new_hw_7d_pct"; then
230
+ [ "$wrote" = "1" ] && printf ',\n'
156
231
  printf ' "seven_day": {"used_percentage": %s, "resets_at": %s}\n' "$new_hw_7d_pct" "$r7"
232
+ else
233
+ printf '\n'
157
234
  fi
158
235
  printf '}\n'
159
236
  } > "${HIGHWATER_FILE}.tmp" 2>/dev/null \
@@ -165,6 +242,7 @@ write_highwater() {
165
242
 
166
243
  apply_highwater_all() {
167
244
  read_highwater
245
+ compute_fresh_activity
168
246
 
169
247
  apply_hw "$five_pct" "$five_reset" "$hw_5h_pct" "$hw_5h_reset"
170
248
  five_pct="$applied_pct"
@@ -183,14 +261,23 @@ apply_highwater_all() {
183
261
  parsed=""
184
262
  [ -n "$input" ] && parsed=$(printf '%s' "$input" | jq -r "$jq_full" 2>/dev/null)
185
263
 
186
- IFS="$tab" read -r used_tokens window_size live_five_pct live_five_reset live_seven_pct live_seven_reset <<EOF
264
+ IFS="$tab" read -r used_tokens window_size live_session_id live_api_ms live_output_tokens live_five_pct live_five_reset live_seven_pct live_seven_reset <<EOF
187
265
  $parsed
188
266
  EOF
189
267
 
268
+ live_session_id="${live_session_id:-}"
269
+ [ "$live_session_id" = "null" ] && live_session_id=""
270
+ live_api_ms="${live_api_ms:-0}"
271
+ live_output_tokens="${live_output_tokens:-0}"
190
272
  five_pct="${live_five_pct:-}"
191
273
  five_reset="${live_five_reset:-}"
192
274
  seven_pct="${live_seven_pct:-}"
193
275
  seven_reset="${live_seven_reset:-}"
276
+ live_rate_limits_present=0
277
+ if { [ "$five_pct" != "null" ] && [ -n "$five_pct" ]; } \
278
+ || { [ "$seven_pct" != "null" ] && [ -n "$seven_pct" ]; }; then
279
+ live_rate_limits_present=1
280
+ fi
194
281
 
195
282
  # If rate_limits missing from live input, read from cache
196
283
  if [ "$five_pct" = "null" ] || [ -z "$five_pct" ]; then
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: check
3
- description: "Reviews code diffs, PRs, issue queues, release readiness, commits, pushes, publishing, and project audits. Use when users ask review/看看代码/合并前/看看issue/PR/release/push or to implement an approved plan, with safety gates for dirty and untracked worktrees. Not for exploring ideas, debugging root causes, or prose review."
3
+ description: "Reviews code diffs, PRs, issue queues, release readiness, commits, pushes, publishing, and project audits. Use when users ask in any language for code review, issue or PR triage, release gates, publishing follow-through, or project audits. Not for debugging root causes or prose review."
4
4
  when_to_use: "review, 看看代码, 检查一下, 有没有问题, 是否需要优化, 合并前, 继续优化, 优化代码, 看看issue, 看看PR, release, publish, push, release reaction, GitHub reaction, 发布, 提交, 关闭issue, 发布表情, release表情, close issue, issue close, review my code, check changes, before merge, before release, code review, code-review, audit, project audit, 项目体检, 项目评分, 给项目打分, 深入分析项目代码, 评估项目质量, 代码质量评分, scorecard, linus review, rate this codebase, score this project"
5
5
  dispatch_intent: "Code review, before merge, release gates, generated artifacts, safety sinks, publish/push/reaction follow-through, triage issues/PRs, project-wide code-quality audit scorecard"
6
6
  ---
@@ -9,6 +9,8 @@ dispatch_intent: "Code review, before merge, release gates, generated artifacts,
9
9
 
10
10
  Prefix your first line with 🥷 inline, not as its own paragraph.
11
11
 
12
+ **Update check (non-blocking).** Before starting, run `bash ../../scripts/check-update.sh` once; if it prints a line, relay it to the user, then continue. It runs at most once a day, only reads a public version file, sends no data, and fails silently.
13
+
12
14
  > Note: `/review` is a built-in Anthropic plugin command for PR review. Waza uses `/check` (or the alias `code-review`) instead. Do not re-trigger `/review` from within this skill.
13
15
 
14
16
  Read the diff, find the problems, fix what can be fixed safely, ask about the rest. Done means verification ran in this session and passed.
@@ -106,7 +108,7 @@ Activate when the user mentions: issue, PR, "review all", triage, "batch", or "
106
108
 
107
109
  **Status answer order:** For "都解决了吗", "is this fixed", "is this ready", or similar status checks, answer in this order: code or commit state, branch or CI state, release artifact or registry state, then public issue or PR state. Do not collapse fixed-on-main, available in pre-release, next stable release, and already shipped.
108
110
 
109
- **Flow:** First identify the project's issue/PR host from public context. For GitHub projects, pull open items with `gh issue list -R <repo> --state open --limit 20` and `gh pr list -R <repo> --state open`. For non-GitHub projects, use the platform CLI/API named by the project docs or user request; if none exists, stop and report the missing integration instead of pretending GitHub commands apply. For each item, check if a fix already shipped: `git log --oneline <latest-tag>..HEAD | grep -i "<keyword>"`. If shipped: close with note. If merged but unreleased: reply "已修复,等下一个版本 release" and close. If no fix: analyze and act. Fix now if possible (`fix: closes #N` commit); when the target project documents a nightly, beta, or pre-release channel that already contains the fix, reply with that exact upgrade path and close; for valid-but-unreleased items acknowledge and leave open; for invalid items give one-two sentence reason and close.
111
+ **Flow:** First identify the project's issue/PR host from public context. For GitHub projects, pull open items with `gh issue list -R <repo> --state open --limit 20` and `gh pr list -R <repo> --state open`. For non-GitHub projects, use the platform CLI/API named by the project docs or user request; if none exists, stop and report the missing integration instead of pretending GitHub commands apply. For each item, check current state with the project's release boundary: latest public release, main branch, preview/nightly/beta channel, registry/appcast, and target issue/PR status. If the fix is already in the current public release or documented pre-release channel, close with that exact upgrade path. If fixed on `main` but unreleased, reply "已修复,等下一个版本 release" and close only when project convention or the current user request allows fixed-on-main closure; otherwise leave it open with the next-release note. If no fix exists, analyze and act. Fix now if possible (`fix: closes #N` commit); for valid-but-unreleased items acknowledge and leave open; for invalid items give one-two sentence reason and close.
110
112
 
111
113
  Before final conclusions in a live queue, refresh the issue/PR list once more and re-read any item that changed during the run. If evidence is incomplete, hold the item instead of closing it on a guess.
112
114
 
@@ -141,7 +143,8 @@ This mode extends review; it does not skip review. Before any public or irrevers
141
143
  1. Extract release rules from public project context: README, manifests, CI workflows, release notes, package scripts, changelogs, and explicit user instructions in the current thread.
142
144
  2. Fill the Release Gate 2.0 matrix from `references/project-context.md`: review base, dirty/staged/untracked state, latest tag, origin sync, version fields, generated artifacts, package/archive contents, release assets, registry/appcast/CI, and public issue/PR state.
143
145
  3. Verify generated or bundled outputs, version fields, release notes, package contents, and required artifacts are in sync. Prefer dry-run commands when the ecosystem provides them.
144
- Generated deliverables include tracked archives, ignored dist files, appcasts, site/download copy, registry packages, checksums, and release assets. If project docs require them, regenerate, inspect, and stage or upload them explicitly even when they are ignored by git; do not infer readiness from source-only tests.
146
+ Generated deliverables include tracked archives, ignored dist files, appcasts, site/download copy, registry packages, checksums, and release assets. If project docs require them, regenerate, inspect, and stage or upload them explicitly even when they are ignored by git; do not infer readiness from source-only tests. For remote assets, prefer downloading or reading back the published artifact and comparing entries, checksums, or manifest contents; release page text, file size, or workflow success alone is not artifact proof.
147
+ If the project has preview, beta, nightly, stable, or App Store lanes, name the lane explicitly. Do not use a preview or beta artifact to claim stable release readiness, and do not touch stable appcast, registry, or download surfaces when the requested lane is preview-only unless project docs require it.
145
148
  4. Commit only intended files. Preserve unrelated dirty work, serialize git operations so index locks or overlapping adds do not corrupt the workflow, and re-check HEAD/status before pushing so concurrent agent or maintainer commits are not swept into your ship action.
146
149
  5. Push, publish, tag, or create a release only when the user has explicitly approved that action. If auth, OTP, CI, registry, or network state blocks the operation, pause and report the exact blocker.
147
150
  6. For issue/PR follow-through, confirm the item identity with the host's read command before posting. On GitHub, use `gh issue view` or `gh pr view`; on other hosts, use the CLI/API named by project docs or the current request. Use `references/public-reply.md` for the maintainer reply template (mention, single thanks, facts, explicit next release or verification step) and its closure criteria.
@@ -229,6 +232,8 @@ Measure the diff and classify depth:
229
232
 
230
233
  State the depth before proceeding.
231
234
 
235
+ Static content diffs can stay quick even when they touch several generated files: version strings, dates, release-copy mirrors, sitemap dates, or one-for-one localization copy changes usually need line-by-line readback plus grep consistency, not a specialist fleet. Escalate only when the diff changes logic, generation rules, public distribution behavior, or user-facing semantics beyond the literal text replacement.
236
+
232
237
  ## Did We Build What Was Asked?
233
238
 
234
239
  Before reading code, check scope drift: do the diff and the stated goal match? Label: **on target** / **drift** / **incomplete**.
@@ -259,6 +264,16 @@ Check command contract and installed-runtime behavior, not just library tests: h
259
264
 
260
265
  For mutating CLI commands, also run the Safety Sink Review: dry-run or confirmation path, operation log or rollback story, retry/idempotency, signal/partial-failure handling, and test-mode guards for auth prompts or real system changes. For cleanup, uninstall, prune, reset, or cache-removal commands, add two checks before approval: can a normal user verify each selected item is safe, and is the deleted content locally rebuildable rather than a downloaded dependency or user data? If either answer is no, require narrower matching, explicit user selection, or leave the item visible but non-destructive.
261
266
 
267
+ ## Skill, Plugin, And Packaged Install Surface
268
+
269
+ When a diff touches a skill, plugin, marketplace entry, installer, package allowlist, package manifest, generated mirror, or published archive, verify the installed runtime contract, not just the source tree:
270
+
271
+ 1. Identify the install path a real user will get: package manager, release archive, marketplace entry, plugin source path, or installer script default ref.
272
+ 2. Build or regenerate the package exactly as project docs require, then inspect the archive or generated mirror for every new script, reference, template, rule, manifest, and executable bit.
273
+ 3. Run an isolated install smoke when the surface is installable: fresh temp home/config/cache, add the marketplace or package, install the skill or plugin, list it, and invoke the smallest command or entrypoint that proves scripts and references resolve from the installed path.
274
+ 4. Filter generated mirrors and archives for cache/noise files such as `__pycache__`, `*.pyc`, `.pytest_cache`, `.ruff_cache`, `.mypy_cache`, `.DS_Store`, local logs, and screenshots unless the project explicitly ships them.
275
+ 5. If network, auth, or host tooling prevents the install smoke, state the missing layer as a blocker or gap. Do not replace installed-runtime proof with manifest JSON, source tests, or a successful local import.
276
+
262
277
  ## Hard Stops (fix before merging)
263
278
 
264
279
  Examples, not exhaustive -- flag any diff that could cause irreversible harm if merged unreviewed.
@@ -271,6 +286,7 @@ Examples, not exhaustive -- flag any diff that could cause irreversible harm if
271
286
  - **Generated artifact drift**: if source changes require generated or bundled outputs, verify the output was regenerated and included.
272
287
  - **Verifier failure layer unclear**: if a verifier fails before assertions or due to missing optional dependencies, bootstrap noise, transient build-service crashes, unavailable simulators, or tool setup, classify setup versus product failure. Retry only with new evidence or a narrower environment. Do not call the repo broken until the intended test body or artifact check actually ran.
273
288
  - **Tracked package omissions**: if a package script builds from tracked files, allowlists, or generated manifests, verify every new helper module, reference file, template, or script used by the diff is tracked and present in the built archive before sign-off.
289
+ - **Manifest-only install proof**: if a diff changes a skill, plugin, installer, marketplace entry, package wrapper, or installable archive, metadata and source tests are not enough. Build or install through the real user path in an isolated environment, or mark the install/runtime layer unverified.
274
290
  - **Version skew**: release version fields across manifests, package metadata, app configs, changelogs, tags, or lockfiles must stay synchronized.
275
291
  - **Unknown identifiers in diff**: any function, variable, or type introduced in the diff that does not exist in the codebase is a hard stop. Grep before writing or approving any reference: `grep -r "name" .` -- no results outside the diff = does not exist.
276
292
  - **Dead-code or YAGNI deletion without proof**: any "zero callers" or "unused" claim must be checked across the whole repository, including top-level entrypoints, docs, tests, generated dispatch tables, scripts, CI, and dynamic lookup patterns. Treat sub-agent or tool reports as leads, not proof. Before deleting, batch-grep all candidates, classify test-only references separately from production references, and chase written variables or data tables that may become orphaned together. If the grep scope is partial, do not delete.
@@ -21,11 +21,14 @@ Use this template to compress repository context before running Waza `/check`. T
21
21
  - Generated or bundled artifacts that must stay in sync with source changes.
22
22
  - Packaging source of truth: whether archives are built from `git ls-files`, explicit allowlists, generated manifests, or source directories.
23
23
  - Delivery surfaces: whether generated outputs are tracked, ignored, external release assets, registry uploads, appcasts, installer metadata, checksums, or site/download copy; how they are regenerated, inspected, staged, or uploaded.
24
+ - Distribution lanes: preview, beta, nightly, stable, App Store, or registry channels, and which generated artifacts belong to each lane.
24
25
  - CLI command surfaces: entrypoints, subcommands, flags, help/version behavior, exit codes, stdout/stderr contract, TTY and non-interactive paths, config/env precedence, and installed-runtime checks.
25
26
  - Runtime dependencies introduced by the diff: Python packages, CLIs, network services, package managers, or platform tools that are not already declared in CI/docs.
27
+ - Skill, plugin, marketplace, or package install surfaces: installer default ref, marketplace source path, generated mirror, package allowlist, archive root, executable bits, and the installed-runtime smoke command.
26
28
  - Domain-specific safety rules.
27
29
  - Release artifacts that must exist.
28
30
  - GitHub release reactions or other public release follow-through expected by the project.
31
+ - Release-asset verification method: download, archive entry comparison, checksum manifest, package metadata readback, appcast readback, or registry query.
29
32
  - Public issue or PR reply conventions.
30
33
  - Known CI or test flakes documented by the project and how to distinguish them from real failures.
31
34
  - Release, publish, push, or issue-closure prerequisites documented by the project.
@@ -56,6 +59,14 @@ Use this template to compress repository context before running Waza `/check`. T
56
59
  - Install/run proof: built package, temp prefix, PATH shim, shebang/executable bit, or package-manager path checked with `<command>`.
57
60
  - Mutating commands: dry-run/confirmation, operation log, rollback/retry behavior, signal/partial-failure handling.
58
61
 
62
+ ## Skill Or Plugin Install Surface
63
+
64
+ - User install path: `<package manager / release archive / marketplace entry / plugin id / installer script>`.
65
+ - Source path and generated mirror: `<source dir>` -> `<installed dir>`.
66
+ - Package/archive inclusion: new scripts, references, templates, rules, manifests, and executable bits checked with `<command>`.
67
+ - Isolated install smoke: fresh temp home/config/cache plus `<install command>` and `<list or invoke command>`.
68
+ - Noise filtering: cache files, local logs, screenshots, and temp outputs excluded or intentionally shipped.
69
+
59
70
  ## Project Hard Stops
60
71
 
61
72
  - Do not modify `<protected path>` unless explicitly requested.
@@ -78,7 +89,9 @@ See `public-reply.md` for the full reply template (language match, `@user` + tha
78
89
 
79
90
  - Version fields to check: `<manifest>`, `<app config>`, `<lockfile>`.
80
91
  - Generated artifacts to check: `<artifact>` from `<source>`.
92
+ - Distribution lane: `<preview/beta/nightly/stable/etc.>` and which public surfaces it is allowed to touch.
81
93
  - Dry-run command before publishing: `<command>`.
94
+ - Remote asset proof: `<download/readback command>` that checks content, manifest, digest, appcast, or registry state.
82
95
  - GitHub release reactions to add after asset verification: `<+1/laugh/heart/hooray/rocket/eyes or none>`.
83
96
  - Public state to re-read after publishing or closing: `<registry/release/issue URL or command>`.
84
97
  ```
@@ -95,10 +108,12 @@ Fill this before claiming a change is release-ready. Use "n/a" only when the pro
95
108
  | Worktree state | Dirty, staged, and untracked files accounted for |
96
109
  | Remote state | `origin/main` or release branch sync checked |
97
110
  | Version fields | Manifest, app config, changelog, appcast, and lockfile versions aligned |
111
+ | Distribution lane | Preview, beta, nightly, stable, registry, or app-store lane named, with unrelated lanes left untouched |
98
112
  | Runtime dependencies | Newly introduced Python packages, CLIs, package managers, and network tools declared and available in CI |
99
113
  | Generated artifacts | Tracked archives, ignored dist outputs, bundled/minified files, appcasts, installer metadata, checksums, and site/download copy regenerated or proven not needed |
100
114
  | Package/archive contents | Built package inspected for required files, newly introduced helpers/references, and missing extras |
101
- | Release assets | GitHub release, appcast, download archive, checksum, or installer assets verified |
115
+ | Installed runtime | Package, skill, plugin, CLI, or marketplace install exercised from a clean environment when the diff changes installable surfaces |
116
+ | Release assets | GitHub release, appcast, download archive, checksum, or installer assets downloaded or read back and verified beyond page text or file size |
102
117
  | Registry/appcast | npm/crates/Homebrew/appcast/App Store or equivalent state re-read after publish |
103
118
  | CI status | Latest required checks passed or blocker named |
104
119
  | Issue/PR state | Target issue or PR re-read before commenting, closing, merging, or saying shipped |
@@ -6,8 +6,9 @@ Reusable by both Triage Mode and Ship / Release Follow-through. Default to this
6
6
  2. **Language:** Match the **opener's** language when it is Chinese or English. If the opener used Japanese or Korean, use English for the maintainer reply unless project docs override.
7
7
  3. Open with `@<login>` and **at most one** short thanks (`感谢反馈`, `thank you for the report`, etc.). Do **not** add closing thanks stacks (`再次感谢`, `Thanks again`, long courtesy endings).
8
8
  4. One or two short paragraphs: factual reason, what shipped or what is blocked, no ceremony.
9
- 5. Always give a **next step tied to releases or verification**: next App Store or GitHub release, nightly upgrade command, cache path to clear once, or exactly what info is still needed.
10
- 6. Prefer **editing** an existing maintainer comment (`PATCH /repos/{owner}/{repo}/issues/comments/{comment_id}`) when updating wording; avoid delete plus repost unless the old text must disappear from history.
9
+ 5. Name the exact boundary: already released, fixed on `main` but unreleased, available in nightly/beta/preview, next release, not planned, duplicate, or still needs evidence. Do not write "shipped", "released", or "verified" unless that state was checked in the current turn.
10
+ 6. Always give a **next step tied to releases or verification**: next App Store or GitHub release, nightly upgrade command, cache path to clear once, or exactly what info is still needed.
11
+ 7. Prefer **editing** an existing maintainer comment (`PATCH /repos/{owner}/{repo}/issues/comments/{comment_id}`) when updating wording; avoid delete plus repost unless the old text must disappear from history.
11
12
 
12
13
  ## When closing
13
14
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: design
3
- description: "Produces distinctive, production-grade UI for pages, components, visual interfaces, typography, and screenshot-driven polish. Use when users ask 设计/做页面/做组件/UI/前端/截图 or say a screen is ugly, unclear, inconsistent, or visually wrong. Not for backend logic or data pipelines."
3
+ description: "Produces distinctive, production-grade UI for pages, components, visual interfaces, typography, and screenshot-driven polish. Use when users ask in any language for UI, page, component, frontend, typography, screenshot-grounded visual polish, or complaints that a screen looks unclear, ugly, inconsistent, or visually wrong. Not for backend logic or data pipelines."
4
4
  when_to_use: "设计, 做页面, 做组件, 不好看, 不和谐, 不清晰, 很丑, 很怪, 很傻, 突兀, 不协调, 字体, 字形, 排印, 排版, 样式, 前端, UI, 截图, build page, create component, make it look good, style, design, screenshot with visual complaint, typography, font looks wrong"
5
5
  dispatch_intent: "UI, component, page, visual interface, frontend, artifact-grounded screenshot aesthetic complaint"
6
6
  ---
@@ -9,6 +9,8 @@ dispatch_intent: "UI, component, page, visual interface, frontend, artifact-grou
9
9
 
10
10
  Prefix your first line with 🥷 inline, not as its own paragraph.
11
11
 
12
+ **Update check (non-blocking).** Before starting, run `bash ../../scripts/check-update.sh` once; if it prints a line, relay it to the user, then continue. It runs at most once a day, only reads a public version file, sends no data, and fails silently.
13
+
12
14
  If it could have been generated by a default prompt, it is not good enough.
13
15
 
14
16
  ## Outcome Contract
@@ -73,6 +75,8 @@ Activate when the user sends a screenshot or image alongside a complaint ("这
73
75
  - Do not flatten specific taste feedback into generic UI adjectives. "More premium" is not a diagnosis; "caption baseline drifts above the Chinese line" is.
74
76
  - If the screenshot exposes a regression, broken render, timing issue, or generated asset defect rather than taste, route to `/hunt` and preserve the visual evidence.
75
77
 
78
+ **Native screenshot handoff.** For native apps, once you have proven the app builds, runs, and can reach the target view, do not spend repeated cycles fighting focus, window ordering, or coordinate-click automation just to capture final visual proof. Make one bounded automation attempt. If it is flaky, name the exact screen and ask the user for the screenshot to iterate against. This is a visual QA boundary, not a substitute for build/run verification.
79
+
76
80
  **Boundary**: if the fix requires changing 3 or more components, or if it reveals a direction problem rather than a specific bug, pause and run the full direction lock before continuing.
77
81
 
78
82
  **Redesign priority order** (when reworking an existing UI rather than building from scratch): font replacement → color cleanup → hover/active states → layout and whitespace → replace generic components → add loading/empty/error states → typographic polish. This order maximizes visual lift while minimizing the blast radius of each pass. Full rules in `references/design-reference.md`. Common traps and absolute CSS bans: `references/design-traps.md`.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: health
3
- description: "Runs a budget-aware agent-assisted engineering health audit for instruction/config drift, hooks/MCP, verifier surfaces, and AI maintainability. Use when users ask 检查claude/检查codex/检查pi/配置检查/健康度 or report agents ignoring instructions, missing validation, or code becoming hard to maintain. Not for debugging code or reviewing PRs."
3
+ description: "Runs a budget-aware agent-assisted engineering health audit for instruction/config drift, hooks/MCP, verifier surfaces, and AI maintainability. Use when users ask in any language to audit Claude, Codex, Pi, agent instructions, MCP or hooks, verifier coverage, or AI-maintainability drift. Not for debugging application code or reviewing PRs."
4
4
  when_to_use: "检查claude, 检查codex, 检查pi, Codex 配置, Pi 配置, AGENTS.md, config.toml, agent instructions, 健康度, 配置检查, 配置对不对, AI coding 腐化, 代码变烂, 维护性, 上下文混乱, 验证缺失, 验证命令失真, Claude ignoring instructions, Pi coding agent, check config, settings not working, audit config"
5
5
  dispatch_intent: "Codex/Claude/Pi ignoring instructions, agent config audit, hooks/MCP broken, health token usage, AI coding code rot, hotspot ownership, unclear context, missing verification, stale verifier output"
6
6
  ---
@@ -9,6 +9,8 @@ dispatch_intent: "Codex/Claude/Pi ignoring instructions, agent config audit, hoo
9
9
 
10
10
  Prefix your first line with 🥷 inline, not as its own paragraph.
11
11
 
12
+ **Update check (non-blocking).** Before starting, run `bash ../../scripts/check-update.sh` once; if it prints a line, relay it to the user, then continue. It runs at most once a day, only reads a public version file, sends no data, and fails silently.
13
+
12
14
  Audit the current project's agent setup and AI coding maintainability against this framework:
13
15
  `agent config → instruction surfaces → tools/runtime → verifiers → maintainability`
14
16