@mmerterden/multi-agent-pipeline 20.6.0 → 20.7.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 (69) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/docs/facts.json +1 -1
  3. package/manifest.json +71 -71
  4. package/package.json +1 -1
  5. package/pipeline/multi-agent-refs/features/design-conformance.md +62 -64
  6. package/pipeline/scripts/_notices.mjs +11 -0
  7. package/pipeline/scripts/gen-skills-index.mjs +1 -1
  8. package/pipeline/skills/.skill-manifest.json +39 -39
  9. package/pipeline/skills/shared/README.md +1 -1
  10. package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +1 -0
  11. package/pipeline/skills/shared/external/android-architecture/SKILL.md +2 -0
  12. package/pipeline/skills/shared/external/android-performance/SKILL.md +2 -0
  13. package/pipeline/skills/shared/external/android-security/SKILL.md +2 -0
  14. package/pipeline/skills/shared/external/backlog/BACKLOG.md +1 -1
  15. package/pipeline/skills/shared/external/backlog/SKILL.md +56 -33
  16. package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +1 -0
  17. package/pipeline/skills/shared/external/compose-components/SKILL.md +2 -0
  18. package/pipeline/skills/shared/external/compose-navigation/SKILL.md +3 -2
  19. package/pipeline/skills/shared/external/compose-testing/SKILL.md +2 -0
  20. package/pipeline/skills/shared/external/council/SKILL.md +1 -0
  21. package/pipeline/skills/shared/external/css-modern/SKILL.md +1 -0
  22. package/pipeline/skills/shared/external/database-patterns/SKILL.md +1 -0
  23. package/pipeline/skills/shared/external/evidence-github/SKILL.md +2 -0
  24. package/pipeline/skills/shared/external/evidence-registry/SKILL.md +2 -0
  25. package/pipeline/skills/shared/external/gradle-kotlin-dsl/SKILL.md +2 -0
  26. package/pipeline/skills/shared/external/html-semantic/SKILL.md +1 -0
  27. package/pipeline/skills/shared/external/humanizer/SKILL.md +1 -0
  28. package/pipeline/skills/shared/external/ios-coding-standard/SKILL.md +1 -0
  29. package/pipeline/skills/shared/external/ios-module-structure/SKILL.md +1 -0
  30. package/pipeline/skills/shared/external/ios-security/SKILL.md +2 -0
  31. package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +91 -283
  32. package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +119 -151
  33. package/pipeline/skills/shared/external/localization-reuse-map/reference/publish-and-snapshot.md +60 -90
  34. package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +119 -156
  35. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-artifact.py +726 -787
  36. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-spreadsheet.py +253 -288
  37. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-annotations.py +243 -304
  38. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py +88 -104
  39. package/pipeline/skills/shared/external/localization-reuse-map/scripts/publish-confluence.py +181 -235
  40. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-key-shots.py +198 -263
  41. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-overlay.py +461 -466
  42. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-legacy-values.py +145 -151
  43. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +123 -141
  44. package/pipeline/skills/shared/external/localization-reuse-map/scripts/scan-screen-keys.py +146 -157
  45. package/pipeline/skills/shared/external/localization-reuse-map/scripts/snapshot-resources.sh +22 -19
  46. package/pipeline/skills/shared/external/localization-reuse-map/scripts/verify-map.py +156 -140
  47. package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +1 -0
  48. package/pipeline/skills/shared/external/play-store-review/SKILL.md +2 -0
  49. package/pipeline/skills/shared/external/python-patterns/SKILL.md +1 -0
  50. package/pipeline/skills/shared/external/react-best-practices/SKILL.md +1 -0
  51. package/pipeline/skills/shared/external/rest-api-design/SKILL.md +1 -0
  52. package/pipeline/skills/shared/external/retrofit-networking/SKILL.md +2 -0
  53. package/pipeline/skills/shared/external/room-database/SKILL.md +2 -0
  54. package/pipeline/skills/shared/external/search-first/SKILL.md +1 -0
  55. package/pipeline/skills/shared/external/signal-community/SKILL.md +2 -0
  56. package/pipeline/skills/shared/external/skill-creator/SKILL.md +80 -41
  57. package/pipeline/skills/shared/external/skill-creator/audit.md +63 -59
  58. package/pipeline/skills/shared/external/skill-creator/checklist.md +28 -20
  59. package/pipeline/skills/shared/external/skill-creator/examples.md +40 -40
  60. package/pipeline/skills/shared/external/skill-creator/label-check.md +48 -36
  61. package/pipeline/skills/shared/external/skill-creator/scripts/audit-panel.js +91 -100
  62. package/pipeline/skills/shared/external/skill-creator/template.md +51 -39
  63. package/pipeline/skills/shared/external/tailwind-css/SKILL.md +1 -0
  64. package/pipeline/skills/shared/external/testing-backend/SKILL.md +1 -0
  65. package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +1 -0
  66. package/pipeline/skills/shared/external/vue-composition/SKILL.md +1 -0
  67. package/pipeline/skills/shared/external/web-accessibility/SKILL.md +1 -0
  68. package/pipeline/skills/shared/external/web-performance/SKILL.md +1 -0
  69. package/pipeline/skills/shared/external/web-testing/SKILL.md +1 -0
@@ -1,26 +1,9 @@
1
1
  #!/usr/bin/env python3
2
- """Fidelity check - cross-verify a mapping against the REAL sources before publishing.
3
-
4
- The map can drift from reality (stale keys parsed from an old doc, hallucinated keys,
5
- a wired key that never made it into a row, an empty value). This script reconciles the
6
- mapping JSON against the live repos and reports what doesn't hold up - so the page you
7
- publish reflects the code, not a guess. Read-only.
8
-
9
- Checks (each reported, never auto-fixed):
10
- 1. newKey ∈ screen code - every row's `newKey` should appear as a wired
11
- `LocalizationStringKey.<...>` call site (or `"Key".localized`, or a web
12
- `t('Key')` / `$t('Key')` i18n call) in --screen-path.
13
- Component-owned keys (`(owned by ...)` / `(DS bileşen...)`) are exempt (they live in the
14
- component, not the screen file) and listed separately.
15
- 2. code keys ∉ map - `LocalizationStringKey.<...>` wired in the screen but with no
16
- row → missing coverage (add a row or confirm it's out of scope).
17
- 3. value gaps - `new.tr` empty (verdict≠new), or legacy entirely empty where
18
- the verdict claims reuse/review.
19
- 4. untranslated - a row whose `new.tr` == `new.en` (flag, as the renderer does).
20
- 5. resources (optional) - with --resources-root, the new key's Suggested/<Key>.json exists.
21
-
22
- Exit code 2 when any hard fidelity issue is found (unverified non-component key, code key
23
- missing from the map, or a reuse/review row with no legacy value), so it can gate a run.
2
+ """Fidelity gate: reconcile a reuse-map mapping against the implemented screen.
3
+
4
+ Checks that every mapped newKey is wired in the screen code, that every key
5
+ the code wires is mapped, and that no row carries an empty or invented value.
6
+ Exit 2 when a hard issue is found (blocks publishing), 0 otherwise.
24
7
  """
25
8
  import argparse
26
9
  import json
@@ -28,145 +11,178 @@ import os
28
11
  import re
29
12
  import sys
30
13
 
31
- EXTS = (".swift", ".kt", ".kts", ".xml", ".ts", ".tsx", ".js", ".jsx", ".vue")
32
- RE_LSK = re.compile(r"LocalizationStringKey\.([A-Za-z0-9_]+(?:\.[A-Za-z0-9_]+)+)")
33
- RE_LITERAL = re.compile(r'"([A-Za-z0-9_.]+)"\s*\.\s*localized')
34
- # Web (react-i18next / next-intl `t('key')`; vue-i18n `$t('key')`)
35
- RE_WEB_T = re.compile(r"(?<!\$)\bt\(\s*['\"]([A-Za-z0-9_.\-]+)['\"]")
36
- RE_WEB_VUE_T = re.compile(r"\$t\(\s*['\"]([A-Za-z0-9_.\-]+)['\"]")
37
- # RE_LSK is greedy across dots, so `...StringKey.Ns.leaf.localized` captures the accessor
38
- # too. Strip it, or no call site ever matches its mapping row and every wired key is
39
- # reported unmapped.
40
- RE_ACCESSOR = re.compile(r"\.localized\w*$")
41
- MARKER = re.compile(r"\(.*(owned by|bileşen|component).*\)", re.I)
14
+ EXTENSIONS = {".swift", ".kt", ".kts", ".xml", ".ts", ".tsx", ".js", ".jsx", ".vue"}
15
+ SKIP_PARTS = ("/.git", "/Generated/")
42
16
 
17
+ WORD = "A-Za-z0-9_"
18
+ LSK = re.compile(rf"LocalizationStringKey((?:\.[A-Za-z_][{WORD}]*)+)")
19
+ LITERAL_LOCALIZED = re.compile(rf"\"([{WORD}.]+)\"\.localized\b")
20
+ WEB_T = re.compile(r"(?<![\w$])t\(\s*(['\"])([A-Za-z0-9_.\-]+)\1")
21
+ WEB_DOLLAR_T = re.compile(r"\$t\(\s*(['\"])([A-Za-z0-9_.\-]+)\1")
22
+ TRAILING_MARKER = re.compile(r"\s*\([^()]*\)\s*$")
23
+ COMPONENT_MARKER = re.compile(r"\([^)]*(owned by|bile\u015fen|component)[^)]*\)", re.IGNORECASE)
24
+ PLACEHOLDER = re.compile(r"<[^<>]+>")
43
25
 
44
- def clean_key(k):
45
- """Strip a trailing '(owned by ...)' / '(DS bileşene ait)' marker → bare dotted key."""
46
- return re.sub(r"\s*\(.*\)\s*$", "", (k or "")).strip()
47
26
 
27
+ def strip_accessor(chain):
28
+ segments = chain.strip(".").split(".")
29
+ while segments and segments[-1].startswith("localized"):
30
+ segments.pop()
31
+ return ".".join(segments)
48
32
 
49
- def is_component(k):
50
- return bool(MARKER.search(k or ""))
51
33
 
34
+ def normalize_key(key):
35
+ return ".".join(seg[:1].upper() + seg[1:] for seg in key.split("."))
52
36
 
53
- def norm_key(k):
54
- return clean_key(k).lower()
55
37
 
38
+ def source_files(root):
39
+ if os.path.isfile(root):
40
+ yield root
41
+ return
42
+ for current, dirs, files in os.walk(root):
43
+ probe = current.replace(os.sep, "/") + "/"
44
+ if any(part in probe for part in SKIP_PARTS):
45
+ dirs[:] = []
46
+ continue
47
+ for name in sorted(files):
48
+ if os.path.splitext(name)[1] in EXTENSIONS:
49
+ yield os.path.join(current, name)
56
50
 
57
- def scan_code(screen_path):
58
- """Return (set of wired dotted keys lowercased, full lowercased corpus text)."""
59
- keys, corpus = set(), []
60
- files = []
61
- if os.path.isfile(screen_path):
62
- files = [screen_path]
63
- else:
64
- for dp, _d, names in os.walk(screen_path):
65
- if "/.git" in dp:
66
- continue
67
- files += [os.path.join(dp, n) for n in names if n.endswith(EXTS)]
68
- for f in files:
51
+
52
+ def scan_code(root):
53
+ keys = set()
54
+ corpus = []
55
+ scanned = 0
56
+ for path in source_files(root):
69
57
  try:
70
- txt = open(f, encoding="utf-8", errors="replace").read()
71
- except OSError:
58
+ with open(path, encoding="utf-8") as stream:
59
+ text = stream.read()
60
+ except (OSError, UnicodeDecodeError):
72
61
  continue
73
- corpus.append(txt.lower())
74
- for m in RE_LSK.finditer(txt):
75
- keys.add(RE_ACCESSOR.sub("", m.group(1)).lower())
76
- for m in RE_LITERAL.finditer(txt):
77
- keys.add(m.group(1).lower())
78
- for m in RE_WEB_T.finditer(txt):
79
- keys.add(m.group(1).lower())
80
- for m in RE_WEB_VUE_T.finditer(txt):
81
- keys.add(m.group(1).lower())
82
- return keys, "\n".join(corpus), len(files)
62
+ scanned += 1
63
+ corpus.append(text.lower())
64
+ chains = (strip_accessor(hit.group(1)) for hit in LSK.finditer(text))
65
+ keys.update(chain.lower() for chain in chains if chain)
66
+ for pattern, group in ((LITERAL_LOCALIZED, 1), (WEB_T, 2), (WEB_DOLLAR_T, 2)):
67
+ keys.update(hit.group(group).lower() for hit in pattern.finditer(text))
68
+ return keys, "\n".join(corpus), scanned
69
+
70
+
71
+ def is_component(row):
72
+ text = " ".join(str(row.get(f) or "") for f in ("newKey", "element", "note"))
73
+ return bool(COMPONENT_MARKER.search(text))
83
74
 
84
75
 
85
- def leaf_in_corpus(key, corpus):
86
- """Weak check: the key's last segment appears as a word in the code corpus."""
87
- leaf = clean_key(key).split(".")[-1]
88
- return bool(leaf) and re.search(r"\b" + re.escape(leaf.lower()) + r"\b", corpus) is not None
76
+ def is_dynamic(row, key):
77
+ element = str(row.get("element") or "").lower()
78
+ note = str(row.get("note") or "").lower()
79
+ return (
80
+ key.startswith("(dynamic")
81
+ or bool(PLACEHOLDER.search(key))
82
+ or "dinamik" in element
83
+ or "dynamic" in element
84
+ or "enumerate at runtime" in note
85
+ )
86
+
87
+
88
+ def suggested_exists(root, key):
89
+ name = normalize_key(key) + ".json"
90
+ candidates = [
91
+ os.path.join(root, "Localization", "Suggested", name),
92
+ os.path.join(root, "Resources", "Localization", "LocalizationStrings", "Sources", "Suggested", name),
93
+ ]
94
+ return any(os.path.isfile(p) for p in candidates)
89
95
 
90
96
 
91
97
  def main():
92
- ap = argparse.ArgumentParser(description=__doc__,
93
- formatter_class=argparse.RawDescriptionHelpFormatter)
94
- ap.add_argument("--mapping", required=True)
95
- ap.add_argument("--screen-path", required=True, help="implemented screen dir/file")
96
- ap.add_argument("--resources-root", help="resources / snapshot root (checks Suggested/<Key>.json)")
97
- ap.add_argument("--out", help="write the full report JSON here (default: stdout summary only)")
98
- args = ap.parse_args()
99
-
100
- m = json.load(open(args.mapping, encoding="utf-8"))
101
- rows = m.get("rows", [])
102
- code_keys, corpus, nfiles = scan_code(args.screen_path)
103
-
104
- verified, weak, unverified, component = [], [], [], []
105
- value_gaps, untranslated = [], []
98
+ parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
99
+ parser.add_argument("--mapping", required=True, help="the reuse-map JSON")
100
+ parser.add_argument("--screen-path", required=True, help="folder or single file of the built screen")
101
+ parser.add_argument("--resources-root", help="resources checkout or snapshot; turns on the Suggested-file check")
102
+ parser.add_argument("--out", help="JSON report destination; stdout when omitted")
103
+ args = parser.parse_args()
104
+
105
+ with open(args.mapping, encoding="utf-8") as stream:
106
+ mapping = json.load(stream)
107
+ rows = mapping.get("rows") or []
108
+ code_keys, corpus, scanned = scan_code(args.screen_path)
109
+
110
+ verified = 0
111
+ exempt = 0
112
+ weak, unverified = [], []
113
+ gaps, untranslated = [], []
106
114
  map_keys = set()
107
115
 
108
- for r in rows:
109
- k = r.get("newKey", "")
110
- ck = clean_key(k)
111
- map_keys.add(norm_key(k))
112
- # 1. newKey in code
113
- if is_component(k) or k.startswith("(dynamic") or "dinamik" in (r.get("element", "").lower()):
114
- component.append(k)
115
- elif norm_key(k) in code_keys:
116
- verified.append(k)
117
- elif leaf_in_corpus(k, corpus):
118
- weak.append(k)
116
+ for row in rows:
117
+ raw_key = str(row.get("newKey") or "").strip()
118
+ key = raw_key if raw_key.startswith("(dynamic") else TRAILING_MARKER.sub("", raw_key)
119
+ component = is_component(row)
120
+ dynamic = is_dynamic(row, key)
121
+ if key:
122
+ map_keys.add(key.lower())
123
+
124
+ if component or dynamic:
125
+ exempt += 1
126
+ elif key.lower() in code_keys:
127
+ verified += 1
119
128
  else:
120
- unverified.append({"key": k, "element": r.get("element")})
121
- # 3. value gaps
122
- nw, lg, vd = r.get("new") or {}, r.get("legacy") or {}, r.get("verdict")
123
- if vd != "new" and not (nw.get("tr") or "").strip() and not k.startswith("(dynamic"):
124
- value_gaps.append({"key": k, "issue": "new.tr empty"})
125
- if vd in ("reuse", "review") and not any((lg.get(l) or "").strip() for l in ("en", "tr")):
126
- value_gaps.append({"key": k, "issue": f"verdict={vd} but no legacy value"})
127
- # 4. untranslated
128
- if (nw.get("tr") or "").strip() and nw.get("tr") == nw.get("en"):
129
- untranslated.append(k)
130
- # 5. resources
131
- if args.resources_root and not is_component(k) and not k.startswith("(dynamic"):
132
- seg = ".".join(s[:1].upper() + s[1:] if s else s for s in ck.split("."))
133
- p1 = os.path.join(args.resources_root, "Localization", "Suggested", seg + ".json")
134
- p2 = os.path.join(args.resources_root, "Resources", "Localization",
135
- "LocalizationStrings", "Sources", "Suggested", seg + ".json")
136
- if not (os.path.isfile(p1) or os.path.isfile(p2)):
137
- value_gaps.append({"key": k, "issue": "no Suggested/<Key>.json"})
138
-
139
- # 2. code keys not in map
140
- code_not_mapped = sorted(k for k in code_keys if k not in map_keys)
141
-
129
+ leaf = key.rsplit(".", 1)[-1].lower()
130
+ whole_word = r"\b%s\b" % re.escape(leaf)
131
+ if leaf and re.search(whole_word, corpus):
132
+ weak.append(key)
133
+ else:
134
+ unverified.append({"key": key, "element": row.get("element")})
135
+
136
+ verdict = row.get("verdict")
137
+ new = row.get("new") or {}
138
+ legacy = row.get("legacy") or {}
139
+ if verdict != "new" and not new.get("tr") and not dynamic:
140
+ gaps.append({"key": key, "issue": "new.tr empty"})
141
+ if verdict in ("reuse", "review") and not (legacy.get("en") or legacy.get("tr")):
142
+ gaps.append({"key": key, "issue": f"verdict={verdict} but no legacy value"})
143
+ if new.get("tr") and new.get("tr") == new.get("en"):
144
+ untranslated.append(key)
145
+ if args.resources_root and not component and not dynamic and key and not suggested_exists(args.resources_root, key):
146
+ gaps.append({"key": key, "issue": f"no Suggested/{normalize_key(key)}.json"})
147
+
148
+ not_in_map = sorted(code_keys - map_keys)
142
149
  report = {
143
- "screen": m.get("screen"), "rows": len(rows), "filesScanned": nfiles,
144
- "newKeyInCode": {"verified": len(verified), "weak": weak,
145
- "unverified": unverified, "component_or_dynamic": len(component)},
146
- "codeKeysNotInMap": code_not_mapped,
147
- "valueGaps": value_gaps, "untranslated": untranslated,
150
+ "screen": mapping.get("screen"),
151
+ "rows": len(rows),
152
+ "filesScanned": scanned,
153
+ "newKeyInCode": {
154
+ "verified": verified,
155
+ "weak": weak,
156
+ "unverified": unverified,
157
+ "component_or_dynamic": exempt,
158
+ },
159
+ "codeKeysNotInMap": not_in_map,
160
+ "valueGaps": gaps,
161
+ "untranslated": untranslated,
148
162
  }
149
- hard = len(unverified) + len(code_not_mapped) + sum(1 for g in value_gaps if "no legacy" in g["issue"])
150
-
163
+ text = json.dumps(report, indent=2, ensure_ascii=False) + "\n"
151
164
  if args.out:
152
- with open(args.out, "w", encoding="utf-8") as fh:
153
- json.dump(report, fh, ensure_ascii=False, indent=2)
154
- fh.write("\n")
155
- # summary to stderr (always)
156
- sys.stderr.write(
157
- f"# fidelity: {len(rows)} rows · {nfiles} files\n"
158
- f"# newKey in code: {len(verified)} verified · {len(weak)} weak(leaf) · "
159
- f"{len(unverified)} UNVERIFIED · {len(component)} component/dynamic (exempt)\n"
160
- f"# code keys not in map: {len(code_not_mapped)}\n"
161
- f"# value gaps: {len(value_gaps)} · untranslated(tr==en): {len(untranslated)}\n")
162
- if unverified:
163
- sys.stderr.write("# UNVERIFIED keys: " + ", ".join(u["key"] for u in unverified[:12]) + "\n")
164
- if code_not_mapped:
165
- sys.stderr.write("# wired-but-unmapped: " + ", ".join(code_not_mapped[:12]) + "\n")
166
- if not args.out:
167
- json.dump(report, sys.stdout, ensure_ascii=False, indent=2)
168
- sys.stdout.write("\n")
169
- sys.exit(2 if hard else 0)
165
+ with open(args.out, "w", encoding="utf-8") as sink:
166
+ sink.write(text)
167
+ else:
168
+ sys.stdout.write(text)
169
+
170
+ no_legacy = [g for g in gaps if "no legacy value" in g["issue"]]
171
+ hard = len(unverified) + len(not_in_map) + len(no_legacy)
172
+ print(
173
+ f"# rows {len(rows)}, files {scanned}: verified {verified}, weak {len(weak)}, "
174
+ f"UNVERIFIED {len(unverified)}, component/dynamic {exempt}, wired-but-unmapped {len(not_in_map)}, "
175
+ f"value gaps {len(gaps)} ({len(no_legacy)} hard), untranslated {len(untranslated)}",
176
+ file=sys.stderr,
177
+ )
178
+ for item in unverified[:12]:
179
+ print(f"# UNVERIFIED {item['key']} ({item['element']})", file=sys.stderr)
180
+ for key in not_in_map[:12]:
181
+ print(f"# wired but unmapped: {key}", file=sys.stderr)
182
+ if hard:
183
+ print(f"# FAIL: {hard} hard issue(s); fix them or record a conscious waiver before publishing", file=sys.stderr)
184
+ sys.exit(2)
185
+ print("# PASS", file=sys.stderr)
170
186
 
171
187
 
172
188
  if __name__ == "__main__":
@@ -2,6 +2,7 @@
2
2
  name: nextjs-app-router
3
3
  description: "Next.js 15+ App Router: Server/Client Components, Server Actions, data fetching, layouts, middleware, ISR/SSG/SSR, and routing patterns. Use when building or reviewing App Router work: Server Components, Server Actions, data fetching, layouts."
4
4
  metadata:
5
+ source: multi-agent-pipeline
5
6
  tags: [nextjs, react, frontend, ssr, app-router, full-stack]
6
7
  version: "2025.1"
7
8
  ---
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: play-store-review
3
3
  description: "Prepare Android apps for Google Play Store review and compliance. Covers Google Play policy requirements, content rating questionnaire, target API level requirements, data safety section, privacy policy requirements, in-app purchase policies, common rejection reasons, app signing with Play App Signing, and AAB upload. Use when preparing for Play Store submission, handling policy violations, or auditing app compliance."
4
+ metadata:
5
+ source: multi-agent-pipeline
4
6
  ---
5
7
 
6
8
  # Google Play Store Review
@@ -2,6 +2,7 @@
2
2
  name: python-patterns
3
3
  description: "Modern Python 3.12+: type hints, dataclasses, Pydantic v2, async/await, context managers, generators, pattern matching. Use when writing or reviewing modern Python: typing, dataclasses, Pydantic, async, generators."
4
4
  metadata:
5
+ source: multi-agent-pipeline
5
6
  tags: [python, type-hints, pydantic, asyncio, dataclasses, backend]
6
7
  version: "2025.1"
7
8
  ---
@@ -2,6 +2,7 @@
2
2
  name: react-best-practices
3
3
  description: "React 19+ best practices: Server Components, use() hook, Actions, hooks patterns, state management, and performance optimization. Use when building or reviewing React work: components, hooks, state, Server Components, Actions."
4
4
  metadata:
5
+ source: multi-agent-pipeline
5
6
  tags: [react, frontend, javascript, typescript, hooks, state-management]
6
7
  version: "2025.1"
7
8
  ---
@@ -2,6 +2,7 @@
2
2
  name: rest-api-design
3
3
  description: "REST API design: HTTP methods, status codes, pagination, versioning, rate limiting, caching headers, OpenAPI documentation. Use when a REST surface needs designing or reviewing: methods, status codes, pagination, versioning, caching."
4
4
  metadata:
5
+ source: multi-agent-pipeline
5
6
  tags: [rest, api, http, openapi, pagination, backend]
6
7
  version: "2025.1"
7
8
  ---
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: retrofit-networking
3
3
  description: "Build networking layers on Android with Retrofit, OkHttp, and Kotlin Coroutines. Covers suspend functions in API interfaces, OkHttp interceptors (auth, logging, retry), sealed Result error handling, multipart uploads, WebSocket support, network-bound resource pattern (cache + API), and Hilt integration for Retrofit instances. Use when building API clients, handling network errors, implementing caching, or setting up networking infrastructure."
4
+ metadata:
5
+ source: multi-agent-pipeline
4
6
  ---
5
7
 
6
8
  # Retrofit Networking
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: room-database
3
3
  description: "Implement Room database persistence on Android with @Entity, @Dao, @Database, Flow-based reactive queries, TypeConverters for complex types, migration strategies (auto and manual), relations with @Embedded/@Relation and junction tables, in-memory database testing, and Hilt integration. Use when adding local persistence, defining database schemas, writing DAOs, handling migrations, or testing Room queries."
4
+ metadata:
5
+ source: multi-agent-pipeline
4
6
  ---
5
7
 
6
8
  # Room Database
@@ -7,6 +7,7 @@ description: |
7
7
  Use at the start of any non-trivial feature or utility, especially when you
8
8
  are about to reach for a new dependency or hand-roll something common.
9
9
  metadata:
10
+ source: multi-agent-pipeline
10
11
  version: 1.0.0
11
12
  ---
12
13
 
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: signal-community
3
3
  description: "Check whether other people hit the same problem, via Stack Overflow and Hacker News (keyless) plus opt-in Reddit and X. Use when a bug report or complaint needs corroboration beyond one user. Advisory only: it belongs in a risks section, never in a business rule."
4
+ metadata:
5
+ source: multi-agent-pipeline
4
6
  ---
5
7
 
6
8
  # Community signal
@@ -1,49 +1,88 @@
1
1
  ---
2
2
  name: skill-creator
3
- description: "Author and refine skills for a plugin/toolkit efficiently. Use when creating a new skill, trimming or splitting an existing one, writing a skill's description, or choosing skill vs command vs agent vs CLAUDE.md. Encodes the house rules - lean SKILL.md as an index, progressive disclosure into bundled files, the reference (descriptive) vs workflow (imperative) split, no-prefix naming, and growing a skill from observed failures. Bundles a template, a pre-ship checklist, and good/bad description examples."
3
+ description: "House guide for authoring plugin skills: creating a new skill, trimming or splitting a bloated one, writing or sharpening a description, and choosing between a skill, slash command, subagent, CLAUDE.md entry or MCP server. Covers the lean-index SKILL.md, progressive disclosure into bundled files, the reference vs workflow vs tool split, naming without a platform prefix, growing skills from observed failures, and the pre-ship audit. Ships templates, a checklist and description examples. Use for 'write a new skill', 'this skill is too long', 'improve this skill description', 'should this be a skill or an agent'. Not for writing the domain content itself, prompt writing unrelated to plugin skills, or per-session facts that belong in CLAUDE.md."
4
4
  user-invocable: true
5
- argument-hint: [new | trim | description]
5
+ argument-hint: "[new | trim | description]"
6
6
  allowed-tools: Read, Write, Edit, Glob
7
7
  metadata:
8
8
  version: 1.0.0
9
+ source: multi-agent-pipeline
9
10
  ---
10
11
 
11
- # skill-creator - how to write skills here
12
-
13
- > **Layer:** reference (knowledge)
14
- > **Use when:** creating/trimming a skill, writing a description, or choosing skill vs command/agent/CLAUDE.md.
15
- > **Bundled (read on demand):** [template.md](template.md) · [checklist.md](checklist.md) · [examples.md](examples.md) · [label-check.md](label-check.md) · [audit.md](audit.md)
16
-
17
- ## 1. Is a skill the right tool?
18
- - **CLAUDE.md** - broad facts/rules true *every* session.
19
- - **Skill** - on-demand procedure or knowledge for a recurring task.
20
- - **Subagent/agent** - needs independent reasoning + its own context/tools.
21
- - **MCP server** - structured integration with an external system's API.
22
-
23
- ## 2. The description is discovery (the 90% lever)
24
- It's the *only* thing the model sees when deciding to load the skill. **Write it first.** Include **what + when + trigger words + one example trigger**, in third person. (Anthropic's data: vague→specific lifts activation ~20%→50%; adding examples ~72%→90%.) Good/bad pairs in [examples.md](examples.md).
25
-
26
- ## 3. Keep it lean (it stays in context every turn once loaded)
27
- - `SKILL.md` is an **index** - aim for ~1 page, hard cap ~500 lines.
28
- - Push detail into bundled files **one level deep** (reference, examples, scripts). They cost zero tokens until read; nested-deeper files get partially read - don't nest.
29
- - A **script executes without its source entering context** - prefer a script over asking the model to regenerate code.
30
- - Challenge every line: *"would Claude err without this?"* If no, cut it.
31
-
32
- ## 4. Pick the right degree of freedom (= our layers)
33
- - **reference skill → descriptive:** invariants, conventions + pointers to canonical files, decision heuristics, pitfalls, escape hatches. **No numbered procedures.**
34
- - **workflow skill → imperative spine:** a numbered procedure **only** for deterministic/irreversible steps (branch/PR, codegen, build/verify). Cite reference skills for the judgment.
35
- - **tool skill → thin index over a script/CLI:** lean SKILL.md (when + the run command + sub-commands at a glance), heavy detail in a bundled `reference.md`; the script is the engine (e.g. `tools/figma-utility`).
36
-
37
- ## 5. Naming
38
- No platform prefix - the toolkit name + description already carry it. Reference = aspect noun (`navigation`); workflow = verb-phrase (`branch-and-pr`). Avoid vague/reserved (`helper`, `utils`, `claude-*`). For a name/title/strict-rule label that must carry its **whole intent**, validate it with the fresh-subagent inference test in [label-check.md](label-check.md) before committing.
39
-
40
- ## 6. Grow it from real usage - don't pre-write
41
- 1. Do the task **without** a skill; note where Claude actually fails.
42
- 2. Write the **minimum** that fixes those gaps.
43
- 3. Watch Claude use it: move reused detail up into SKILL.md, cut detail it ignores, sharpen the description if it didn't trigger.
44
-
45
- ## Before shipping
46
- Start from [template.md](template.md); run [checklist.md](checklist.md). For a new or
47
- substantially-changed skill that must be trusted, run the **multi-architect audit**
48
- ([audit.md](audit.md)) - a panel of distinct-lens reviewers + synthesis, looped
49
- fix → re-audit until no blockers/majors remain.
12
+ # skill-creator - authoring plugin skills
13
+
14
+ > **Layer:** knowledge (reference). **Use when:** creating, trimming, splitting, naming or describing a plugin skill, or gating one before it ships.
15
+ > **Not for:** the domain content inside a skill (hand that to the domain skill), prompt writing unrelated to plugin skills, or facts every session needs (those go in CLAUDE.md).
16
+
17
+ Bundled, read when the step needs it:
18
+
19
+ - [template.md](template.md) - reference and workflow skeletons with frontmatter.
20
+ - [checklist.md](checklist.md) - the pre-ship checklist.
21
+ - [examples.md](examples.md) - weak and strong descriptions, wrong and right layer shapes.
22
+ - [audit.md](audit.md) - the multi-architect audit loop and its lens sets; driven by `scripts/audit-panel.js`.
23
+ - [label-check.md](label-check.md) - the fresh-subagent inference test for names and rule labels.
24
+
25
+ ## Arguments
26
+
27
+ | Argument | Start at |
28
+ |---|---|
29
+ | `new` | Pick the artifact, write the description, then the template |
30
+ | `trim` | Leanness and the cut test, then the checklist |
31
+ | `description` | The description recipe and examples.md |
32
+
33
+ ## Pick the artifact first
34
+
35
+ | Artifact | Holds |
36
+ |---|---|
37
+ | CLAUDE.md | facts and rules that are true in every session |
38
+ | Skill | a procedure or body of knowledge for a recurring task, loaded on demand |
39
+ | Subagent | work that needs its own reasoning, context window and tool set |
40
+ | MCP server | a structured integration with an external API |
41
+
42
+ ## The description is the skill's only advertisement
43
+
44
+ Before a skill loads, the model sees its name and description and nothing else. Write the description before the body. It needs:
45
+
46
+ - **what** the skill covers or does, and **when** to use it;
47
+ - concrete **trigger phrases** a user would actually type, plus one sample request;
48
+ - the **platform** where it matters (iOS/SwiftUI, Compose, backend);
49
+ - an implied **boundary** against sibling skills;
50
+ - third person throughout.
51
+
52
+ Anthropic's measurements: a specific description instead of a vague one lifted activation from about 20% to about 50%; adding examples took it from about 72% to about 90%. A one-liner with no when, no triggers and no platform gives the router nothing to match. See examples.md.
53
+
54
+ ## Keep SKILL.md an index
55
+
56
+ - Everything in SKILL.md sits in context for every turn after it loads. Aim for about one page; 500 lines is the hard ceiling.
57
+ - Push detail into bundled files **one level below** SKILL.md (reference, examples, scripts). They cost nothing until read. A file reached only through another bundled file tends to be read in part, so do not nest.
58
+ - Deterministic work belongs in a script. Running it keeps its source out of context; never ask the model to write that code again each time.
59
+ - **Cut test:** keep a line only if Claude would get the task wrong without it. Do not explain what the model already knows: a paragraph on how PDFs are structured loses to "use pdfplumber".
60
+
61
+ ## Layer sets the degrees of freedom
62
+
63
+ | Layer | Voice | Contains | Never contains |
64
+ |---|---|---|---|
65
+ | reference | descriptive | invariants, conventions pointing at canonical files, decision heuristics, pitfalls, escape hatches | numbered procedures |
66
+ | workflow | imperative | numbered steps only for deterministic or irreversible actions (branch and PR, codegen, build and verify), citations of reference skills for judgement, a verification gate | open-ended judgement dressed as steps |
67
+ | tool | index | when to use, the run command, a sub-command overview; heavy detail in a bundled `reference.md`; the script is the engine | the engine's logic in prose |
68
+
69
+ `tools/figma-utility` in the iOS toolkit is the model tool skill.
70
+
71
+ ## Naming
72
+
73
+ - No platform prefix: the toolkit name and the description already say it.
74
+ - Reference skills take an aspect noun (`navigation`); workflow skills a verb phrase (`branch-and-pr`).
75
+ - Avoid vague or reserved names: `helper`, `utils`, anything starting with `claude-`.
76
+ - When a name, title or strict-rule label has to carry its whole intent, run the label check before committing to it.
77
+
78
+ ## Grow skills from failures
79
+
80
+ 1. Do the task with no skill and note exactly where Claude goes wrong.
81
+ 2. Write the smallest skill that fixes those failures. Nothing speculative.
82
+ 3. Watch real use: promote detail that keeps getting read into SKILL.md, cut what is ignored, and sharpen the description whenever the skill failed to trigger.
83
+
84
+ ## Before it ships
85
+
86
+ 1. Run checklist.md as the self-review floor.
87
+ 2. New or substantially changed skill: run the audit loop in audit.md until `clearsBar` is true.
88
+ 3. Wire it: add the path to the `skills` array in `<plugin>/.claude-plugin/plugin.json`, bump the plugin version in `.github/plugin/marketplace.json`, and add a row to the plugin's `index` skill route table.