@mmerterden/multi-agent-pipeline 20.5.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.
- package/CHANGELOG.md +36 -0
- package/LICENSE +10 -0
- package/docs/facts.json +5 -4
- package/install/_dev-only-files.mjs +1 -0
- package/manifest.json +84 -82
- package/package.json +4 -3
- package/pipeline/lib/confusables.json +100 -0
- package/pipeline/lib/normalize-text.mjs +48 -0
- package/pipeline/lib/outbound-gate.mjs +10 -1
- package/pipeline/lib/redact.mjs +4 -1
- package/pipeline/lib/vercel-deploy.sh +5 -7
- package/pipeline/multi-agent-refs/features/design-conformance.md +62 -64
- package/pipeline/scripts/_notices.mjs +11 -0
- package/pipeline/scripts/agent-guard.py +30 -1
- package/pipeline/scripts/gen-skills-index.mjs +1 -1
- package/pipeline/scripts/pre-commit-check.sh +59 -16
- package/pipeline/scripts/pre-push-check.sh +3 -3
- package/pipeline/scripts/run-ui-tests.sh +5 -5
- package/pipeline/scripts/website-deploy-commit.sh +3 -2
- package/pipeline/skills/.skill-manifest.json +39 -39
- package/pipeline/skills/shared/README.md +1 -1
- package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +1 -0
- package/pipeline/skills/shared/external/android-architecture/SKILL.md +2 -0
- package/pipeline/skills/shared/external/android-performance/SKILL.md +2 -0
- package/pipeline/skills/shared/external/android-security/SKILL.md +2 -0
- package/pipeline/skills/shared/external/backlog/BACKLOG.md +1 -1
- package/pipeline/skills/shared/external/backlog/SKILL.md +56 -33
- package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +1 -0
- package/pipeline/skills/shared/external/compose-components/SKILL.md +2 -0
- package/pipeline/skills/shared/external/compose-navigation/SKILL.md +3 -2
- package/pipeline/skills/shared/external/compose-testing/SKILL.md +2 -0
- package/pipeline/skills/shared/external/council/SKILL.md +1 -0
- package/pipeline/skills/shared/external/css-modern/SKILL.md +1 -0
- package/pipeline/skills/shared/external/database-patterns/SKILL.md +1 -0
- package/pipeline/skills/shared/external/evidence-github/SKILL.md +2 -0
- package/pipeline/skills/shared/external/evidence-registry/SKILL.md +2 -0
- package/pipeline/skills/shared/external/gradle-kotlin-dsl/SKILL.md +2 -0
- package/pipeline/skills/shared/external/html-semantic/SKILL.md +1 -0
- package/pipeline/skills/shared/external/humanizer/SKILL.md +1 -0
- package/pipeline/skills/shared/external/ios-coding-standard/SKILL.md +1 -0
- package/pipeline/skills/shared/external/ios-module-structure/SKILL.md +1 -0
- package/pipeline/skills/shared/external/ios-security/SKILL.md +2 -0
- package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +91 -283
- package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +119 -151
- package/pipeline/skills/shared/external/localization-reuse-map/reference/publish-and-snapshot.md +60 -90
- package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +119 -156
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-artifact.py +726 -787
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-spreadsheet.py +253 -288
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-annotations.py +243 -304
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py +88 -104
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/publish-confluence.py +181 -235
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-key-shots.py +198 -263
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-overlay.py +461 -466
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-legacy-values.py +145 -151
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +123 -141
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/scan-screen-keys.py +146 -157
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/snapshot-resources.sh +22 -19
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/verify-map.py +156 -140
- package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +1 -0
- package/pipeline/skills/shared/external/play-store-review/SKILL.md +2 -0
- package/pipeline/skills/shared/external/python-patterns/SKILL.md +1 -0
- package/pipeline/skills/shared/external/react-best-practices/SKILL.md +1 -0
- package/pipeline/skills/shared/external/rest-api-design/SKILL.md +1 -0
- package/pipeline/skills/shared/external/retrofit-networking/SKILL.md +2 -0
- package/pipeline/skills/shared/external/room-database/SKILL.md +2 -0
- package/pipeline/skills/shared/external/search-first/SKILL.md +1 -0
- package/pipeline/skills/shared/external/signal-community/SKILL.md +2 -0
- package/pipeline/skills/shared/external/skill-creator/SKILL.md +80 -41
- package/pipeline/skills/shared/external/skill-creator/audit.md +63 -59
- package/pipeline/skills/shared/external/skill-creator/checklist.md +28 -20
- package/pipeline/skills/shared/external/skill-creator/examples.md +40 -40
- package/pipeline/skills/shared/external/skill-creator/label-check.md +48 -36
- package/pipeline/skills/shared/external/skill-creator/scripts/audit-panel.js +91 -100
- package/pipeline/skills/shared/external/skill-creator/template.md +51 -39
- package/pipeline/skills/shared/external/tailwind-css/SKILL.md +1 -0
- package/pipeline/skills/shared/external/testing-backend/SKILL.md +1 -0
- package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +1 -0
- package/pipeline/skills/shared/external/vue-composition/SKILL.md +1 -0
- package/pipeline/skills/shared/external/web-accessibility/SKILL.md +1 -0
- package/pipeline/skills/shared/external/web-performance/SKILL.md +1 -0
- package/pipeline/skills/shared/external/web-testing/SKILL.md +1 -0
package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py
CHANGED
|
@@ -1,25 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Refresh the
|
|
3
|
-
from your project's label endpoint and write it into the resources snapshot, so the
|
|
4
|
-
mapper resolves legacy values offline instead of a network round-trip per run. This
|
|
5
|
-
is the ONLY step that hits the network - run it manually / in CI to refresh, the same
|
|
6
|
-
way you refresh a design export.
|
|
2
|
+
"""Refresh the legacy label snapshot from the label endpoint.
|
|
7
3
|
|
|
8
|
-
Writes <out>/<lang>.json
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
supply the URL template and the headers it needs:
|
|
12
|
-
|
|
13
|
-
fetch-legacy-labels.py --out resources/Localization/Legacy --langs all \\
|
|
14
|
-
--endpoint 'https://labels.example.internal/{env}/labels/{lang}' \\
|
|
15
|
-
--headers-file .secrets/label-headers.json
|
|
16
|
-
|
|
17
|
-
Placeholders available in --endpoint: {lang} and {env}.
|
|
18
|
-
|
|
19
|
-
Prefer --headers-file (JSON object) over repeated --header for anything secret, so
|
|
20
|
-
tokens never land in shell history or CI logs. Response shape is discovered rather
|
|
21
|
-
than assumed: the largest flat {str: str} map anywhere in the JSON wins, which covers
|
|
22
|
-
both a bare map and one nested under an envelope.
|
|
4
|
+
Writes one flat <out>/<lang>.json per language with keys exactly as the
|
|
5
|
+
service returns them (backend prefix kept). This is the only step of the
|
|
6
|
+
skill that needs the network.
|
|
23
7
|
"""
|
|
24
8
|
import argparse
|
|
25
9
|
import json
|
|
@@ -27,103 +11,103 @@ import os
|
|
|
27
11
|
import sys
|
|
28
12
|
import urllib.request
|
|
29
13
|
|
|
30
|
-
|
|
14
|
+
ALL_LANGS = ["en", "tr", "ar"] + sorted(["ru", "it", "fr", "es", "de"])
|
|
31
15
|
|
|
32
16
|
|
|
33
|
-
def
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if isinstance(cur, dict):
|
|
38
|
-
strs = {k: v for k, v in cur.items() if isinstance(v, str)}
|
|
39
|
-
if len(strs) > len(best):
|
|
40
|
-
best = strs
|
|
41
|
-
stack.extend(cur.values())
|
|
42
|
-
elif isinstance(cur, list):
|
|
43
|
-
stack.extend(cur)
|
|
44
|
-
return best
|
|
17
|
+
def parse_langs(value):
|
|
18
|
+
if value.strip() == "all":
|
|
19
|
+
return list(ALL_LANGS)
|
|
20
|
+
return [part.strip() for part in value.split(",") if part.strip()]
|
|
45
21
|
|
|
46
22
|
|
|
47
|
-
def
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
with open(a.headers_file, encoding="utf-8") as fh:
|
|
51
|
-
loaded = json.load(fh)
|
|
52
|
-
if not isinstance(loaded, dict):
|
|
53
|
-
raise SystemExit(f"--headers-file must be a JSON object, got {type(loaded).__name__}")
|
|
54
|
-
headers.update({str(k): str(v) for k, v in loaded.items()})
|
|
55
|
-
for pair in a.header or []:
|
|
23
|
+
def parse_pairs(pairs, flag):
|
|
24
|
+
result = {}
|
|
25
|
+
for pair in pairs or []:
|
|
56
26
|
if "=" not in pair:
|
|
57
|
-
raise SystemExit(f"
|
|
58
|
-
|
|
59
|
-
|
|
27
|
+
raise SystemExit(f"{flag} expects K=V, got {pair!r}")
|
|
28
|
+
key, value = pair.split("=", 1)
|
|
29
|
+
result[key.strip()] = value.strip()
|
|
30
|
+
return result
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def build_headers(args):
|
|
34
|
+
headers = {"Accept": "application/json"}
|
|
35
|
+
if args.headers_file:
|
|
36
|
+
with open(args.headers_file, encoding="utf-8") as stream:
|
|
37
|
+
extra = json.load(stream)
|
|
38
|
+
if not isinstance(extra, dict):
|
|
39
|
+
raise SystemExit(f"--headers-file must hold a JSON object: {args.headers_file}")
|
|
40
|
+
headers.update({str(k): str(v) for k, v in extra.items()})
|
|
41
|
+
headers.update(parse_pairs(args.header, "--header"))
|
|
60
42
|
return headers
|
|
61
43
|
|
|
62
44
|
|
|
63
|
-
def
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if
|
|
86
|
-
|
|
87
|
-
|
|
45
|
+
def largest_string_map(data):
|
|
46
|
+
best = {}
|
|
47
|
+
stack = [data]
|
|
48
|
+
while stack:
|
|
49
|
+
node = stack.pop()
|
|
50
|
+
if isinstance(node, dict):
|
|
51
|
+
if node and all(isinstance(k, str) and isinstance(v, str) for k, v in node.items()):
|
|
52
|
+
if len(node) > len(best):
|
|
53
|
+
best = node
|
|
54
|
+
stack.extend(node.values())
|
|
55
|
+
elif isinstance(node, list):
|
|
56
|
+
stack.extend(node)
|
|
57
|
+
return best
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def fetch_labels(endpoint, lang, env, headers, status_field, fail_status, timeout):
|
|
61
|
+
url = endpoint.format(lang=lang, env=env)
|
|
62
|
+
request_headers = dict(headers)
|
|
63
|
+
request_headers.setdefault("Accept-Language", lang)
|
|
64
|
+
request = urllib.request.Request(url, headers=request_headers)
|
|
65
|
+
with urllib.request.urlopen(request, timeout=timeout) as response:
|
|
66
|
+
data = json.loads(response.read().decode("utf-8"))
|
|
67
|
+
if isinstance(data, dict) and status_field in data:
|
|
68
|
+
code = str(data[status_field])
|
|
69
|
+
if code in fail_status:
|
|
70
|
+
raise RuntimeError(f"{url}: {fail_status[code]} (status {code})")
|
|
71
|
+
labels = largest_string_map(data)
|
|
72
|
+
if not labels:
|
|
73
|
+
raise RuntimeError(f"the response for '{lang}' ({url}) contains no string-to-string label map")
|
|
74
|
+
return labels
|
|
88
75
|
|
|
89
76
|
|
|
90
77
|
def main():
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
headers = parse_headers(a)
|
|
113
|
-
fail_statuses = parse_fail_statuses(a.fail_status)
|
|
114
|
-
langs = LANGS_ALL if a.langs.strip() == "all" else [l.strip() for l in a.langs.split(",") if l.strip()]
|
|
115
|
-
os.makedirs(a.out, exist_ok=True)
|
|
78
|
+
cli = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
79
|
+
cli.add_argument("--out", dest="snapshot_dir", required=True, help="where the snapshot lives, for example resources/Localization/Legacy")
|
|
80
|
+
cli.add_argument("--endpoint", required=True, help="URL template containing {lang} and, if needed, {env}")
|
|
81
|
+
cli.add_argument("--header", action="append", help="additional request header as K=V; may be repeated")
|
|
82
|
+
cli.add_argument("--headers-file", help="request headers as a JSON object, so secrets stay out of argv")
|
|
83
|
+
cli.add_argument("--langs", default="all", help="languages separated by commas, or 'all'")
|
|
84
|
+
cli.add_argument("--env", default="", help="text put in place of {env}")
|
|
85
|
+
cli.add_argument("--status-field", default="statusCode", help="name of the response field that holds a status code")
|
|
86
|
+
cli.add_argument("--fail-status", action="append", help="CODE=message pair that marks a failed response; may be repeated")
|
|
87
|
+
cli.add_argument("--timeout", default=40, type=int)
|
|
88
|
+
opts = cli.parse_args()
|
|
89
|
+
|
|
90
|
+
if "{lang}" not in opts.endpoint:
|
|
91
|
+
print("# warning: --endpoint lacks a {lang} placeholder, so all languages request the same URL", file=sys.stderr)
|
|
92
|
+
|
|
93
|
+
headers = build_headers(opts)
|
|
94
|
+
fail_status = parse_pairs(opts.fail_status, "--fail-status")
|
|
95
|
+
langs = parse_langs(opts.langs)
|
|
96
|
+
os.makedirs(opts.snapshot_dir, exist_ok=True)
|
|
97
|
+
|
|
116
98
|
total = 0
|
|
117
99
|
for lang in langs:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
with open(
|
|
121
|
-
json.dump(
|
|
122
|
-
|
|
123
|
-
total += len(
|
|
124
|
-
print(f" {lang}: {len(
|
|
125
|
-
|
|
126
|
-
|
|
100
|
+
labels = fetch_labels(opts.endpoint, lang, opts.env, headers, opts.status_field, fail_status, opts.timeout)
|
|
101
|
+
target = os.path.join(opts.snapshot_dir, lang + ".json")
|
|
102
|
+
with open(target, "w", encoding="utf-8") as sink:
|
|
103
|
+
json.dump(labels, sink, ensure_ascii=False, sort_keys=True, indent=0)
|
|
104
|
+
sink.write("\n")
|
|
105
|
+
total += len(labels)
|
|
106
|
+
print(f" {lang}: {len(labels)} labels -> {target}")
|
|
107
|
+
|
|
108
|
+
average = total // len(langs) if langs else 0
|
|
109
|
+
print(f"snapshot refreshed: {len(langs)} languages, about {average} keys each")
|
|
110
|
+
print(f"commit {opts.snapshot_dir} so later lookups stay offline")
|
|
127
111
|
|
|
128
112
|
|
|
129
113
|
if __name__ == "__main__":
|