od-contribute — first-contribution flow for Open Design Locked to . Branches by contribution type , not by issue. Replaces the dev-loop with type-specific no-code validators. Designed so a product user with zero coding background can ship a real PR. Language Mirror the user's language in every user-facing message — labels and descriptions, status updates, error explanations. Detect from their first message; when uncertain, default to English. Generated artifacts (PR titles, commit messages, PR/issue body files, branch names) MUST be English regardless of the user's chat language. GitHub conve…

; then\n PUSH_REMOTE=\"fork\"\nelse\n od::warn \"no fork configured (TARGET_FORK empty) — pushing to upstream ${TARGET_REPO}. 3s to abort...\"\n sleep 3 || true\nfi\n\nod::log \"pushing to ${PUSH_REMOTE}/${BRANCH}\"\ngit push -u \"$PUSH_REMOTE\" \"$BRANCH\"\n\n# 3) Pick label set per contribution type. (OD's labels: documentation, i18n, blog, enhancement, ...)\nLABELS=()\ncase \"$TYPE\" in\n skill) LABELS+=(\"good first issue\" \"enhancement\") ;;\n design-system) LABELS+=(\"good first issue\" \"enhancement\") ;;\n i18n) LABELS+=(\"i18n\" \"documentation\") ;;\n docs) LABELS+=(\"documentation\") ;;\nesac\n\nLABEL_FLAGS=()\nfor L in \"${LABELS[@]}\"; do\n LABEL_FLAGS+=(--label \"$L\")\ndone\n\n# 4) Open the PR. `gh pr create` automatically picks `head` from the pushed branch.\nHEAD_REF=\"$BRANCH\"\nif [[ \"$PUSH_REMOTE\" == \"fork\" && -n \"$TARGET_FORK\" ]]; then\n HEAD_REF=\"${TARGET_FORK%%/*}:${BRANCH}\"\nfi\n\nPR_URL=\"$(gh pr create \\\n --repo \"$TARGET_REPO\" \\\n --base \"$OD_BASE_BRANCH\" \\\n --head \"$HEAD_REF\" \\\n --title \"$TITLE\" \\\n --body-file \"$BODY_FILE\" \\\n ${DRAFT} \\\n \"${LABEL_FLAGS[@]}\")\" || od::die \"gh pr create failed\"\n\nprintf '\\n'\nprintf '%s\\n' \"$PR_URL\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":3907,"content_sha256":"114626d689e74d0c8e6e12402d4ee99cab210df45f44fc4b556ddc7c2c998733"},{"filename":"scripts/discover-doc-gaps.sh","content":"#!/usr/bin/env bash\n# Find low-effort doc improvements in nexu-io/open-design.\n# Usage: discover-doc-gaps.sh \u003cworkdir>\n# Stdout: NDJSON rows. Three classes:\n# {\"kind\":\"todo\",\"file\":\"docs/foo.md\",\"line\":42,\"text\":\"TODO: explain the daemon\"}\n# {\"kind\":\"typo\",\"file\":\"README.md\",\"line\":17,\"word\":\"recieve\",\"suggested\":\"receive\"}\n# {\"kind\":\"deadlink\",\"file\":\"docs/bar.md\",\"line\":3,\"url\":\"https://example.com/x\",\"status\":\"404\"}\n#\n# Dead-link checks are best-effort: timeout 8s, only reports 4xx/5xx/timeout, not network errors.\n\nset -uo pipefail\nsource \"$(dirname \"$0\")/config.sh\"\n\nWORKDIR=\"${1:?workdir required}\"\n[[ -d \"$WORKDIR/.git\" ]] || od::die \"not a git workdir: $WORKDIR\"\n\ncd \"$WORKDIR\"\nod::require jq\n\n# Use ripgrep when present for speed; fall back to grep -rE.\nif command -v rg >/dev/null 2>&1; then\n GREP() { rg --no-heading --line-number --color never \"$@\"; }\nelse\n GREP() {\n # Translate a couple of rg flags we use to grep-equivalents.\n local args=()\n while (($#)); do\n case \"$1\" in\n --no-heading|--color) shift ;;\n --color=never) shift ;;\n --line-number) args+=(\"-n\"); shift ;;\n *) args+=(\"$1\"); shift ;;\n esac\n done\n grep -rE \"${args[@]}\"\n }\nfi\n\n# 1) TODOs / FIXMEs in docs.\nemit_todo() {\n while IFS=: read -r file line rest; do\n [[ -z \"$file\" ]] && continue\n jq -nc --arg file \"$file\" --argjson line \"$line\" --arg text \"$rest\" \\\n '{kind:\"todo\", file:$file, line:$line, text:($text|sub(\"^[[:space:]]+\";\"\"))}'\n done\n}\n\nGREP --no-heading --line-number --color never -e 'TODO|FIXME|XXX' \\\n -g '*.md' docs/ README*.md QUICKSTART*.md CONTRIBUTING*.md 2>/dev/null \\\n | emit_todo || true\n\n# Fallback path for environments where rg --glob isn't available — grep equivalent.\nif ! command -v rg >/dev/null 2>&1; then\n grep -rEn -- 'TODO|FIXME|XXX' docs README*.md QUICKSTART*.md CONTRIBUTING*.md 2>/dev/null \\\n | emit_todo || true\nfi\n\n# 2) Common typos. Whole-word match, case-sensitive (avoid false positives in code/links).\nTYPOS=(\n \"teh|the\"\n \"recieve|receive\"\n \"seperate|separate\"\n \"occured|occurred\"\n \"succesful|successful\"\n \"untill|until\"\n \"wich|which\"\n \"thier|their\"\n \"alot|a lot\"\n \"definately|definitely\"\n \"neccessary|necessary\"\n \"enviroment|environment\"\n \"transparant|transparent\"\n \"appearence|appearance\"\n)\n\nfor entry in \"${TYPOS[@]}\"; do\n bad=\"${entry%%|*}\"\n good=\"${entry##*|}\"\n while IFS=: read -r file line _rest; do\n [[ -z \"$file\" ]] && continue\n # Skip code blocks (rough heuristic: skip if line is inside ```).\n jq -nc --arg file \"$file\" --argjson line \"$line\" --arg word \"$bad\" --arg good \"$good\" \\\n '{kind:\"typo\", file:$file, line:$line, word:$word, suggested:$good}'\n done \u003c \u003c(GREP --no-heading --line-number --color never -e \"\\\\b${bad}\\\\b\" -g '*.md' . 2>/dev/null \\\n || grep -rEn \"\\\\b${bad}\\\\b\" --include='*.md' . 2>/dev/null \\\n || true)\ndone\n\n# 3) External link health (best-effort, capped).\n# Cap to 50 links per run so we don't hammer arbitrary hosts.\nMAX_LINKS=50\nSEEN=0\nextract_links() {\n GREP --no-heading --line-number --color never -e '\\]\\(https?://[^) ]+\\)' -g '*.md' . 2>/dev/null \\\n || grep -rEn '\\]\\(https?://[^) ]+\\)' --include='*.md' . 2>/dev/null\n}\n\nwhile IFS= read -r row; do\n [[ \"$SEEN\" -ge \"$MAX_LINKS\" ]] && break\n file=\"${row%%:*}\"\n rest=\"${row#*:}\"\n line=\"${rest%%:*}\"\n text=\"${rest#*:}\"\n # Extract first http(s) URL on the line.\n url=\"$(printf '%s' \"$text\" | grep -oE 'https?://[^) ]+' | head -1)\"\n [[ -z \"$url\" ]] && continue\n SEEN=$((SEEN+1))\n # HEAD with 8s timeout, follow redirects, take final status.\n status=\"$(curl -sS -o /dev/null -m 8 -L -w '%{http_code}' --head \"$url\" 2>/dev/null || echo \"000\")\"\n case \"$status\" in\n 2*|3*) ;; # OK\n 000) ;; # network/timeout — skip rather than spam false positives\n *)\n jq -nc --arg file \"$file\" --argjson line \"$line\" --arg url \"$url\" --arg status \"$status\" \\\n '{kind:\"deadlink\", file:$file, line:$line, url:$url, status:$status}'\n ;;\n esac\ndone \u003c \u003c(extract_links | head -n \"$MAX_LINKS\")\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":4063,"content_sha256":"62fe760c344742157b41ed25c8cff95602eb8602e94af133a5b5c249fc5c7600"},{"filename":"scripts/discover-i18n-gaps.sh","content":"#!/usr/bin/env bash\n# Find translation gaps in nexu-io/open-design.\n# Usage: discover-i18n-gaps.sh \u003cworkdir>\n# Stdout: NDJSON, one row per gap:\n# {\"doc\":\"README\",\"english\":\"README.md\",\"lang\":\"es\",\"translated\":null,\"status\":\"missing\"}\n# {\"doc\":\"QUICKSTART\",\"english\":\"QUICKSTART.md\",\"lang\":\"zh-CN\",\"translated\":\"QUICKSTART.zh-CN.md\",\"status\":\"stale\",\"english_mtime\":\"...\",\"translated_mtime\":\"...\",\"english_commits_since\":12}\n#\n# A \"stale\" translation is one whose last-touched commit is older than the most recent\n# commit touching the English source. Ranking is left to the caller (the agent).\n\nset -euo pipefail\nsource \"$(dirname \"$0\")/config.sh\"\n\nWORKDIR=\"${1:?workdir required}\"\n[[ -d \"$WORKDIR/.git\" ]] || od::die \"not a git workdir: $WORKDIR\"\n\ncd \"$WORKDIR\"\nod::require git\nod::require jq\n\n# Translatable English source files we care about (top-level docs).\nENGLISH_DOCS=(README.md QUICKSTART.md CONTRIBUTING.md MAINTAINERS.md TRANSLATIONS.md PRIVACY.md)\n\n# Common language suffixes seen in OD's tree (extend as the project grows).\nLANGS=(zh-CN zh-TW ja-JP de fr es ko ru pt-BR tr uk ar)\n\n# Languages already represented for a given doc are detected from disk;\n# the LANGS array is what we *offer* to a contributor when no translation exists.\n\nlast_commit_epoch() {\n # Last commit touching $1 — empty string if file has never been committed.\n git log -1 --format=%ct -- \"$1\" 2>/dev/null || true\n}\n\ncommits_between() {\n # How many commits touched $newer that are NOT ancestors of $older_ref's tip\n # commit. Uses commit ancestry rather than `--since=\u003cepoch>` math because\n # `--since` is inclusive of the boundary epoch — so when English source and\n # translation are touched in the SAME commit (very common: bulk i18n\n # refresh, structural change applied across all translations), `--since`\n # would count that shared commit and mark the translation \"stale\" by 1.\n #\n # `tr_sha..HEAD -- $newer` reads as: \"commits reachable from HEAD but not\n # from tr_sha, that touched $newer\". When tr_sha is HEAD's tip for $newer\n # too (same-commit update), the answer is correctly 0.\n local newer=\"$1\" older_ref=\"$2\"\n local tr_sha\n tr_sha=\"$(git log -1 --format=%H -- \"$older_ref\" 2>/dev/null)\"\n if [[ -z \"$tr_sha\" ]]; then\n # Translation never committed; count all history of $newer.\n git log --format=%H -- \"$newer\" 2>/dev/null | wc -l | tr -d ' '\n else\n git rev-list \"${tr_sha}..HEAD\" -- \"$newer\" 2>/dev/null | wc -l | tr -d ' '\n fi\n}\n\nemit() {\n jq -nc \\\n --arg doc \"$1\" --arg english \"$2\" --arg lang \"$3\" \\\n --arg translated \"$4\" --arg status \"$5\" \\\n --arg en_epoch \"$6\" --arg tr_epoch \"$7\" --arg en_commits_since \"$8\" \\\n '{\n doc: $doc, english: $english, lang: $lang,\n translated: ($translated | select(length>0)),\n status: $status,\n english_mtime_epoch: ($en_epoch | select(length>0) | tonumber? // null),\n translated_mtime_epoch: ($tr_epoch | select(length>0) | tonumber? // null),\n english_commits_since_translation: ($en_commits_since | tonumber? // null)\n }'\n}\n\nfor english in \"${ENGLISH_DOCS[@]}\"; do\n [[ -f \"$english\" ]] || continue\n doc=\"${english%.md}\"\n en_epoch=\"$(last_commit_epoch \"$english\")\"\n\n # Track observed languages for this doc as a newline-delimited string.\n # Avoids `declare -A` (associative arrays), which requires Bash 4 — macOS\n # ships with Bash 3.2 by default and most agent-spawned bash subprocesses\n # inherit that. The leading + trailing newlines let us match `\\n\u003clang>\\n`\n # without false positives on prefix overlap (e.g. zh vs zh-CN).\n SEEN_LANGS=

od-contribute — first-contribution flow for Open Design Locked to . Branches by contribution type , not by issue. Replaces the dev-loop with type-specific no-code validators. Designed so a product user with zero coding background can ship a real PR. Language Mirror the user's language in every user-facing message — labels and descriptions, status updates, error explanations. Detect from their first message; when uncertain, default to English. Generated artifacts (PR titles, commit messages, PR/issue body files, branch names) MUST be English regardless of the user's chat language. GitHub conve…

\\n'\n\n while IFS= read -r -d '' translated; do\n # Filename pattern: \u003cDOC>.\u003clang>.md (e.g. README.zh-CN.md).\n # `find . ... -print0` emits paths with a leading `./`; strip that first\n # and operate on the basename so the prefix-strip below works regardless.\n base=\"${translated#./}\"\n base=\"$(basename \"$base\")\"\n lang_part=\"${base#${doc}.}\"\n lang_part=\"${lang_part%.md}\"\n [[ -z \"$lang_part\" || \"$lang_part\" == \"$base\" ]] && continue\n SEEN_LANGS+=\"${lang_part}\"

od-contribute — first-contribution flow for Open Design Locked to . Branches by contribution type , not by issue. Replaces the dev-loop with type-specific no-code validators. Designed so a product user with zero coding background can ship a real PR. Language Mirror the user's language in every user-facing message — labels and descriptions, status updates, error explanations. Detect from their first message; when uncertain, default to English. Generated artifacts (PR titles, commit messages, PR/issue body files, branch names) MUST be English regardless of the user's chat language. GitHub conve…

\\n'\n\n tr_epoch=\"$(last_commit_epoch \"$translated\")\"\n if [[ -z \"$tr_epoch\" ]]; then\n emit \"$doc\" \"$english\" \"$lang_part\" \"$translated\" \"untracked\" \"$en_epoch\" \"\" \"\"\n continue\n fi\n en_commits_since=\"$(commits_between \"$english\" \"$translated\")\"\n if [[ \"$en_commits_since\" -gt 0 ]]; then\n emit \"$doc\" \"$english\" \"$lang_part\" \"$translated\" \"stale\" \"$en_epoch\" \"$tr_epoch\" \"$en_commits_since\"\n fi\n # else: up-to-date, skip emission entirely.\n done \u003c \u003c(find . -maxdepth 1 -type f -name \"${doc}.*.md\" -print0)\n\n # Then, for each language in LANGS that we didn't see, emit a \"missing\" row.\n for lang in \"${LANGS[@]}\"; do\n case \"$SEEN_LANGS\" in\n *

od-contribute — first-contribution flow for Open Design Locked to . Branches by contribution type , not by issue. Replaces the dev-loop with type-specific no-code validators. Designed so a product user with zero coding background can ship a real PR. Language Mirror the user's language in every user-facing message — labels and descriptions, status updates, error explanations. Detect from their first message; when uncertain, default to English. Generated artifacts (PR titles, commit messages, PR/issue body files, branch names) MUST be English regardless of the user's chat language. GitHub conve…

\\n'\"$lang\"

od-contribute — first-contribution flow for Open Design Locked to . Branches by contribution type , not by issue. Replaces the dev-loop with type-specific no-code validators. Designed so a product user with zero coding background can ship a real PR. Language Mirror the user's language in every user-facing message — labels and descriptions, status updates, error explanations. Detect from their first message; when uncertain, default to English. Generated artifacts (PR titles, commit messages, PR/issue body files, branch names) MUST be English regardless of the user's chat language. GitHub conve…

\\n'*) continue ;;\n esac\n emit \"$doc\" \"$english\" \"$lang\" \"\" \"missing\" \"$en_epoch\" \"\" \"\"\n done\ndone\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":4866,"content_sha256":"cf131305267e9b24d355c0dec8c9efaa6943898a02278bcad4f2fc09cd28b62a"},{"filename":"scripts/setup-workspace.sh","content":"#!/usr/bin/env bash\n# Clone (or reuse) nexu-io/open-design in an isolated workdir + create a feature branch.\n# Usage: setup-workspace.sh \u003ctype> \u003cslug>\n# \u003ctype> one of: skill | design-system | i18n | docs\n# \u003cslug> short kebab-case identifier (e.g. \"translate-readme-es\", \"fix-typo-quickstart\")\n#\n# Env: TARGET_FORK optional (else pushes go to upstream — create-pr.sh warns first).\n#\n# Stdout (machine-readable):\n# WORKDIR=\u003cabs path>\n# BRANCH=\u003cbranch name>\n\nset -euo pipefail\nsource \"$(dirname \"$0\")/config.sh\"\n\nTYPE=\"${1:?type required (skill|design-system|i18n|docs)}\"\nSLUG=\"${2:?slug required}\"\n\ncase \"$TYPE\" in\n skill|design-system|i18n|docs) ;;\n *) od::die \"unknown type: $TYPE (expected skill|design-system|i18n|docs)\" ;;\nesac\n\nod::require gh\nod::require git\n\n# Use second-precision timestamp so two contribution sessions on the same day\n# (or the SKILL.md i18n flow that calls setup-workspace.sh with a placeholder\n# slug like \"translate\" before the user has picked a language) don't collide\n# into the same workdir. Reusing a workdir would leak untracked / half-edited\n# files from an earlier abandoned session into a later contribution.\nSESSION_TAG=\"$(date +%Y%m%d-%H%M%S)\"\nSESSION_DIR=\"${TYPE}-${SLUG}-${SESSION_TAG}\"\nWORKDIR=\"$(od::workdir_for \"$SESSION_DIR\")\"\nBRANCH=\"od-contrib/${TYPE}/${SLUG}-${SESSION_TAG}\"\n\nmkdir -p \"$OD_WORK_ROOT\"\nod::assert_in_workroot \"$WORKDIR\"\n\nCLONE_URL=\"https://github.com/${TARGET_REPO}.git\"\n\nif [[ -d \"$WORKDIR/.git\" ]]; then\n # We reach here only if the user explicitly resumed by passing the same\n # SESSION_TAG, or if the wall clock somehow produced a duplicate. Clean any\n # untracked/dirty state so the run starts from a known good base instead of\n # inheriting whatever the previous occupant left behind.\n od::log \"reusing existing workdir: $WORKDIR\"\n git -C \"$WORKDIR\" fetch origin --prune\n git -C \"$WORKDIR\" reset --hard HEAD\n git -C \"$WORKDIR\" clean -fdx\nelse\n od::log \"cloning $CLONE_URL → $WORKDIR (depth 50)\"\n git clone --depth 50 \"$CLONE_URL\" \"$WORKDIR\"\nfi\n\n# Tell git to ignore our internal scratch dir so `git add -A` later (in\n# create-pr.sh) doesn't accidentally stage type.txt, slug.txt, PR-BODY.md\n# into the user's contribution PR. .git/info/exclude is repo-local and not\n# committed, so we don't pollute the OD repo's .gitignore.\nmkdir -p \"$WORKDIR/.git/info\"\nif ! grep -qxF '.od-contrib/' \"$WORKDIR/.git/info/exclude\" 2>/dev/null; then\n printf '\\n# od-contribute scratch dir (added by setup-workspace.sh)\\n.od-contrib/\\n' \\\n >> \"$WORKDIR/.git/info/exclude\"\nfi\n\ngit -C \"$WORKDIR\" checkout \"$OD_BASE_BRANCH\"\ngit -C \"$WORKDIR\" pull --ff-only origin \"$OD_BASE_BRANCH\"\n\n# Configure fork remote if provided.\nif [[ -n \"${TARGET_FORK}\" ]]; then\n if git -C \"$WORKDIR\" remote | grep -q '^fork

od-contribute — first-contribution flow for Open Design Locked to . Branches by contribution type , not by issue. Replaces the dev-loop with type-specific no-code validators. Designed so a product user with zero coding background can ship a real PR. Language Mirror the user's language in every user-facing message — labels and descriptions, status updates, error explanations. Detect from their first message; when uncertain, default to English. Generated artifacts (PR titles, commit messages, PR/issue body files, branch names) MUST be English regardless of the user's chat language. GitHub conve…

; then\n git -C \"$WORKDIR\" remote set-url fork \"https://github.com/${TARGET_FORK}.git\"\n else\n git -C \"$WORKDIR\" remote add fork \"https://github.com/${TARGET_FORK}.git\"\n fi\nfi\n\n# Create or reset branch off latest base.\nif git -C \"$WORKDIR\" show-ref --verify --quiet \"refs/heads/$BRANCH\"; then\n od::log \"branch $BRANCH already exists — switching\"\n git -C \"$WORKDIR\" checkout \"$BRANCH\"\nelse\n git -C \"$WORKDIR\" checkout -b \"$BRANCH\" \"$OD_BASE_BRANCH\"\nfi\n\nmkdir -p \"$WORKDIR/.od-contrib\"\nprintf '%s\\n' \"$TYPE\" > \"$WORKDIR/.od-contrib/type.txt\"\nprintf '%s\\n' \"$SLUG\" > \"$WORKDIR/.od-contrib/slug.txt\"\n\nod::log \"workspace ready\"\nprintf 'WORKDIR=%s\\n' \"$WORKDIR\"\nprintf 'BRANCH=%s\\n' \"$BRANCH\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":3476,"content_sha256":"cfe9f52bf4504416d47df234253f7be81ce3b44110acf004498915cb50e195d1"},{"filename":"scripts/validate-design-system.sh","content":"#!/usr/bin/env bash\n# Validate a user-supplied DESIGN.md (Open Design \"design system\" submission).\n# Usage: validate-design-system.sh \u003cDESIGN.md path> [--reference \u003cexisting-DESIGN.md>]\n#\n# Strategy: instead of hardcoding a schema, we read 1-3 existing DESIGN.md files\n# from the OD repo at runtime to learn which top-level sections are conventional,\n# then check the new file has at least those sections (case-insensitive H1/H2 match).\n#\n# Heuristic-only: warns rather than fails on missing optional sections; only fails\n# when the file is empty, unparseable, or has zero structural overlap with samples.\n\nset -uo pipefail\nsource \"$(dirname \"$0\")/config.sh\"\n\nNEW_FILE=\"${1:?DESIGN.md path required}\"\nshift || true\n\nREFERENCE_FILES=()\nwhile (($#)); do\n case \"$1\" in\n --reference) REFERENCE_FILES+=(\"$2\"); shift 2 ;;\n *) od::die \"unknown flag: $1\" ;;\n esac\ndone\n\n[[ -f \"$NEW_FILE\" ]] || od::die \"not a file: $NEW_FILE\"\n[[ -s \"$NEW_FILE\" ]] || od::die \"file is empty: $NEW_FILE\"\n\nextract_headings() {\n # Pull H1/H2 lines, lowercase, trim, dedupe.\n awk '/^#{1,2}[[:space:]]+/ { sub(/^#{1,2}[[:space:]]+/, \"\"); print tolower($0) }' \"$1\" \\\n | sed -E 's/[[:space:]]+$//' | sort -u\n}\n\nnew_headings=\"$(extract_headings \"$NEW_FILE\")\"\n[[ -n \"$new_headings\" ]] || { printf 'FAIL no H1/H2 headings found in %s — is this really a design system doc?\\n' \"$NEW_FILE\"; printf 'RESULT=fail\\n'; exit 1; }\n\n# If references were supplied, build the union of their headings as the \"expected\" set.\nEXPECTED=\"\"\nfor ref in \"${REFERENCE_FILES[@]}\"; do\n [[ -f \"$ref\" ]] || continue\n EXPECTED+=

od-contribute — first-contribution flow for Open Design Locked to . Branches by contribution type , not by issue. Replaces the dev-loop with type-specific no-code validators. Designed so a product user with zero coding background can ship a real PR. Language Mirror the user's language in every user-facing message — labels and descriptions, status updates, error explanations. Detect from their first message; when uncertain, default to English. Generated artifacts (PR titles, commit messages, PR/issue body files, branch names) MUST be English regardless of the user's chat language. GitHub conve…

\\n'\"$(extract_headings \"$ref\")\"\ndone\nEXPECTED=\"$(printf '%s' \"$EXPECTED\" | grep -v '^

od-contribute — first-contribution flow for Open Design Locked to . Branches by contribution type , not by issue. Replaces the dev-loop with type-specific no-code validators. Designed so a product user with zero coding background can ship a real PR. Language Mirror the user's language in every user-facing message — labels and descriptions, status updates, error explanations. Detect from their first message; when uncertain, default to English. Generated artifacts (PR titles, commit messages, PR/issue body files, branch names) MUST be English regardless of the user's chat language. GitHub conve…

| sort -u || true)\"\n\nPASS=0\nWARN=0\nFAIL=0\n\nif [[ -z \"$EXPECTED\" ]]; then\n printf 'WARN no reference DESIGN.md provided — running structure-only checks\\n'\n WARN=$((WARN+1))\nelse\n # Count overlap. >= 30% structural overlap = looks like a design system.\n overlap=0\n total=0\n while IFS= read -r h; do\n [[ -z \"$h\" ]] && continue\n total=$((total+1))\n if printf '%s\\n' \"$new_headings\" | grep -Fxq \"$h\"; then\n overlap=$((overlap+1))\n fi\n done \u003c\u003c\u003c \"$EXPECTED\"\n\n if [[ \"$total\" -eq 0 ]]; then\n printf 'WARN references parsed but had no headings\\n'; WARN=$((WARN+1))\n else\n pct=$(( overlap * 100 / total ))\n if [[ \"$pct\" -ge 30 ]]; then\n printf 'PASS structural overlap with reference DESIGN.md files: %d%% (%d/%d)\\n' \"$pct\" \"$overlap\" \"$total\"\n PASS=$((PASS+1))\n else\n printf 'FAIL structural overlap with reference DESIGN.md files only %d%% (%d/%d) — likely missing required sections\\n' \"$pct\" \"$overlap\" \"$total\"\n FAIL=$((FAIL+1))\n fi\n fi\nfi\n\n# Always-on lightweight checks:\nif grep -qE '^(#)[[:space:]]+' \"$NEW_FILE\"; then\n printf 'PASS has at least one H1 heading\\n'; PASS=$((PASS+1))\nelse\n printf 'WARN no H1 heading found — convention is one H1 with the brand/system name\\n'; WARN=$((WARN+1))\nfi\n\n# No relative path escape (../).\nif grep -nE '\\(\\.\\./' \"$NEW_FILE\" >/dev/null; then\n printf 'WARN contains ../ relative paths — make sure they resolve once placed at design-systems/\u003cbrand>/DESIGN.md\\n'; WARN=$((WARN+1))\nfi\n\nif [[ \"$FAIL\" -eq 0 ]]; then\n printf 'RESULT=pass (passes=%d warns=%d)\\n' \"$PASS\" \"$WARN\"\n exit 0\nelse\n printf 'RESULT=fail (passes=%d warns=%d fails=%d)\\n' \"$PASS\" \"$WARN\" \"$FAIL\"\n exit 1\nfi\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":3367,"content_sha256":"4499825d3a0b91ba7817b1a7c094008c625047efbcf96225fc976a3fef6a0ee9"},{"filename":"scripts/validate-markdown.sh","content":"#!/usr/bin/env bash\n# Lightweight Markdown validation for i18n / docs / blog contributions.\n#\n# Usage: validate-markdown.sh \u003cfile> [\u003cfile> ...] [--reference \u003corig>]\n#\n# Checks per file:\n# - File is non-empty.\n# - Code fences are balanced (count of ``` is even).\n# - Newly-introduced relative refs that don't resolve on disk fail.\n# Refs that ALREADY exist in the --reference file (the English source for\n# a translation, or HEAD's version for a docs edit) are NOT failed even\n# if they don't resolve — many OD docs reference website-router slugs\n# like `skills/blog-post/` that aren't files in the checked-out repo.\n# - External http(s) links return 2xx/3xx (best-effort, capped, 8s timeout).\n#\n# Without --reference, relative-ref checking is skipped entirely (since we\n# can't tell route slugs from file paths in isolation). The other checks\n# still run.\n\nset -uo pipefail\nsource \"$(dirname \"$0\")/config.sh\"\nset +e\nset -uo pipefail # restore the \"accumulate diagnostics\" stance after sourcing.\n\nREFERENCE=\"\"\nFILES=()\nwhile (($#)); do\n case \"$1\" in\n --reference) REFERENCE=\"$2\"; shift 2 ;;\n --) shift; while (($#)); do FILES+=(\"$1\"); shift; done ;;\n -*) od::die \"unknown flag: $1\" ;;\n *) FILES+=(\"$1\"); shift ;;\n esac\ndone\n\n(( ${#FILES[@]} >= 1 )) || od::die \"usage: validate-markdown.sh \u003cfile> [\u003cfile> ...] [--reference \u003corig>]\"\n\n# Build the \"already-broken in source\" set of relative refs (newline-delimited\n# string for Bash 3 compatibility — no associative arrays). Anything in this\n# set is excused from failing the new-file check.\nKNOWN_DEAD=

od-contribute — first-contribution flow for Open Design Locked to . Branches by contribution type , not by issue. Replaces the dev-loop with type-specific no-code validators. Designed so a product user with zero coding background can ship a real PR. Language Mirror the user's language in every user-facing message — labels and descriptions, status updates, error explanations. Detect from their first message; when uncertain, default to English. Generated artifacts (PR titles, commit messages, PR/issue body files, branch names) MUST be English regardless of the user's chat language. GitHub conve…

\\n'\nif [[ -n \"$REFERENCE\" ]]; then\n if [[ ! -f \"$REFERENCE\" ]]; then\n od::warn \"--reference $REFERENCE does not exist; ignoring.\"\n else\n ref_dir=\"$(cd \"$(dirname \"$REFERENCE\")\" && pwd -P)\"\n while IFS= read -r ref; do\n [[ -z \"$ref\" ]] && continue\n case \"$ref\" in http*|mailto:*|\\#*|/*) continue ;; esac\n target=\"${ref%%#*}\"; target=\"${target%%\\?*}\"\n [[ -z \"$target\" ]] && continue\n if [[ ! -e \"$ref_dir/$target\" ]]; then\n KNOWN_DEAD+=\"${ref}\"

od-contribute — first-contribution flow for Open Design Locked to . Branches by contribution type , not by issue. Replaces the dev-loop with type-specific no-code validators. Designed so a product user with zero coding background can ship a real PR. Language Mirror the user's language in every user-facing message — labels and descriptions, status updates, error explanations. Detect from their first message; when uncertain, default to English. Generated artifacts (PR titles, commit messages, PR/issue body files, branch names) MUST be English regardless of the user's chat language. GitHub conve…

\\n'\n fi\n done \u003c \u003c(grep -oE '\\!?\\[[^]]*\\]\\([^)]+\\)' \"$REFERENCE\" 2>/dev/null \\\n | sed -E 's/.*\\(([^)]+)\\).*/\\1/' \\\n | sort -u)\n fi\nfi\n\nOVERALL=0\nMAX_HTTP_PER_FILE=20\n\ncheck_file() {\n local f=\"$1\"\n local fail=0\n printf -- '--- %s ---\\n' \"$f\"\n\n if [[ ! -f \"$f\" ]]; then\n printf 'FAIL not a file: %s\\n' \"$f\"\n return 1\n fi\n if [[ ! -s \"$f\" ]]; then\n printf 'FAIL empty file: %s\\n' \"$f\"\n return 1\n fi\n printf 'PASS exists, non-empty\\n'\n\n # Code fence balance.\n local fences\n fences=\"$(grep -cE '^```' \"$f\" 2>/dev/null)\"\n if (( fences % 2 == 0 )); then\n printf 'PASS code fences balanced (%d)\\n' \"$fences\"\n else\n printf 'FAIL unbalanced code fences (%d ``` lines)\\n' \"$fences\"\n fail=1\n fi\n\n # Relative refs — tiered check:\n #\n # Image refs (![alt](path)) — always validate. No website route uses\n # image-syntax markdown; if it doesn't resolve on disk, it's broken.\n #\n # Link refs starting with ./ or ../ — always validate. Explicit relative\n # paths are unambiguously file references, not router slugs.\n #\n # Other link refs (e.g. `skills/blog-post/`) — only validated when\n # --reference is supplied (we excuse refs already broken in the source).\n # Without --reference we skip these because OD docs use slug-style refs\n # for website routes that don't resolve to files in the checkout.\n #\n # In all cases, refs already broken in --reference (when supplied) are\n # excused from failure rather than reported as regressions.\n local dir rel_bad=0 rel_excused=0 rel_skipped_ambiguous=0\n dir=\"$(cd \"$(dirname \"$f\")\" && pwd -P)\"\n while IFS= read -r entry; do\n [[ -z \"$entry\" ]] && continue\n # `!?` in grep keeps the leading `!` for image refs; case-detect here.\n is_img=0\n case \"$entry\" in '!'*) is_img=1 ;; esac\n # Extract URL: between first `(` and last `)`.\n ref=\"${entry#*\\(}\"\n ref=\"${ref%\\)*}\"\n case \"$ref\" in http*|mailto:*|\\#*|/*) continue ;; esac\n target=\"${ref%%#*}\"; target=\"${target%%\\?*}\"\n [[ -z \"$target\" ]] && continue\n\n # Should we validate this ref?\n if (( is_img == 0 )); then\n case \"$ref\" in\n ./*|../*) ;; # explicit relative — always validate\n *)\n # File-like targets (have an obvious file extension) are unambiguously\n # on-disk references — `[doc](missing.md)` is not a website route, it\n # is a sibling file. Validate without --reference. Otherwise (no\n # extension, looks like a slug), only validate when we have a\n # reference to compare against.\n case \"${target##*/}\" in\n *.md|*.markdown|*.mdx \\\n |*.png|*.jpg|*.jpeg|*.gif|*.webp|*.svg|*.ico|*.bmp \\\n |*.pdf|*.txt|*.json|*.yaml|*.yml|*.toml \\\n |*.sh|*.ts|*.tsx|*.js|*.jsx|*.css|*.html|*.xml \\\n |*.csv|*.zip|*.gz)\n ;; # file-like — always validate\n *)\n if [[ -z \"$REFERENCE\" ]]; then\n rel_skipped_ambiguous=$((rel_skipped_ambiguous+1))\n continue\n fi\n ;;\n esac\n ;;\n esac\n fi\n\n if [[ ! -e \"$dir/$target\" ]]; then\n case \"$KNOWN_DEAD\" in\n *

od-contribute — first-contribution flow for Open Design Locked to . Branches by contribution type , not by issue. Replaces the dev-loop with type-specific no-code validators. Designed so a product user with zero coding background can ship a real PR. Language Mirror the user's language in every user-facing message — labels and descriptions, status updates, error explanations. Detect from their first message; when uncertain, default to English. Generated artifacts (PR titles, commit messages, PR/issue body files, branch names) MUST be English regardless of the user's chat language. GitHub conve…

\\n'\"$ref\"

od-contribute — first-contribution flow for Open Design Locked to . Branches by contribution type , not by issue. Replaces the dev-loop with type-specific no-code validators. Designed so a product user with zero coding background can ship a real PR. Language Mirror the user's language in every user-facing message — labels and descriptions, status updates, error explanations. Detect from their first message; when uncertain, default to English. Generated artifacts (PR titles, commit messages, PR/issue body files, branch names) MUST be English regardless of the user's chat language. GitHub conve…

\\n'*) rel_excused=$((rel_excused+1)) ;;\n *)\n printf 'FAIL broken relative reference: %s\\n' \"$ref\"\n rel_bad=$((rel_bad+1))\n fail=1\n ;;\n esac\n fi\n done \u003c \u003c(grep -oE '!?\\[[^]]*\\]\\([^)]+\\)' \"$f\" 2>/dev/null | sort -u)\n\n if (( rel_bad == 0 )); then\n msg=\"PASS relative refs OK\"\n (( rel_excused > 0 )) && msg+=\" (${rel_excused} pre-existing dead refs kept as-is)\"\n (( rel_skipped_ambiguous > 0 )) && msg+=\" (${rel_skipped_ambiguous} slug-style refs skipped — pass --reference to check)\"\n printf '%s\\n' \"$msg\"\n fi\n\n # External link health (best-effort).\n local http_seen=0 http_bad=0\n while IFS= read -r url; do\n [[ -z \"$url\" ]] && continue\n (( http_seen >= MAX_HTTP_PER_FILE )) && break\n http_seen=$((http_seen+1))\n local code\n code=\"$(curl -sS -o /dev/null -m 8 -L -w '%{http_code}' --head \"$url\" 2>/dev/null)\"\n [[ -z \"$code\" ]] && code=\"000\"\n case \"$code\" in\n 2*|3*|000) ;; # OK, or network-flaky — don't punish.\n *)\n printf 'FAIL external link %s returned %s\\n' \"$url\" \"$code\"\n http_bad=$((http_bad+1))\n fail=1\n ;;\n esac\n # URL extraction: stop at whitespace, ), \", ', \u003c, >, [, ]. HTML \u003cimg src=\"...\"> in\n # OD docs would otherwise leak a trailing quote into the URL and cause false 404s.\n done \u003c \u003c(grep -oE 'https?://[^][[:space:]\"'\\''\u003c>)]+' \"$f\" 2>/dev/null | sort -u)\n\n if (( http_bad == 0 && http_seen > 0 )); then\n printf 'PASS %d external links return 2xx/3xx (or network-skipped)\\n' \"$http_seen\"\n fi\n\n return \"$fail\"\n}\n\nfor f in \"${FILES[@]}\"; do\n if ! check_file \"$f\"; then\n OVERALL=1\n fi\ndone\n\nif [[ \"$OVERALL\" -eq 0 ]]; then\n printf 'RESULT=pass\\n'\n exit 0\nelse\n printf 'RESULT=fail\\n'\n exit 1\nfi\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":7075,"content_sha256":"4fca46f0e9aaf6018a855707428ce802e95748e496c2ba7c7b2d5333bf57dc81"},{"filename":"scripts/validate-skill-submission.sh","content":"#!/usr/bin/env bash\n# Validate a user-supplied OD skill folder before staging it for PR.\n# Usage: validate-skill-submission.sh \u003cskill-folder>\n# Checks (each prints PASS/FAIL line on stdout):\n# - SKILL.md exists\n# - SKILL.md has frontmatter with `name` and `description`\n# - `name` matches folder name (warn-only, since OD may rename on merge)\n# - all relative paths in SKILL.md resolve to files inside the folder\n# - no path escapes the skill folder (../ in references)\n# Exit 0 = all PASS or only warnings. Exit 1 = at least one FAIL.\n\nset -uo pipefail\nsource \"$(dirname \"$0\")/config.sh\"\n\nSKILL_DIR=\"${1:?skill folder path required}\"\n[[ -d \"$SKILL_DIR\" ]] || od::die \"not a directory: $SKILL_DIR\"\n\nABS_SKILL_DIR=\"$(cd \"$SKILL_DIR\" && pwd -P)\"\nFAIL=0\n\npass() { printf 'PASS %s\\n' \"$1\"; }\nwarn() { printf 'WARN %s\\n' \"$1\"; }\nfail() { printf 'FAIL %s\\n' \"$1\"; FAIL=1; }\n\nSKILL_MD=\"$ABS_SKILL_DIR/SKILL.md\"\nif [[ ! -f \"$SKILL_MD\" ]]; then\n fail \"SKILL.md missing — every OD skill folder must contain SKILL.md at its root\"\n printf 'RESULT=%s\\n' \"fail\"\n exit 1\nfi\npass \"SKILL.md exists\"\n\n# Frontmatter parse: extract YAML between the first two '---' lines.\n#\n# The opening fence MUST be on line 1 — both Claude Code's loader and Codex\n# CLI's loader (codex-rs/core-skills) parse the top of the file, so a SKILL.md\n# that starts with prose, a BOM, or whitespace and only contains a `---` block\n# later will load as having no frontmatter, even if this validator picks it up.\n# Reject leading content explicitly so the validator can't pass a file the\n# real loaders will reject.\nFIRST_LINE=\"$(head -n 1 \"$SKILL_MD\")\"\nif [[ ! \"$FIRST_LINE\" =~ ^---[[:space:]]*$ ]]; then\n fail \"SKILL.md must start with a YAML frontmatter fence ('---') on line 1 — found: $(printf '%q' \"$FIRST_LINE\" | head -c 80)\"\n printf 'RESULT=%s\\n' \"fail\"\n exit 1\nfi\n\nFRONT=$(awk '\n BEGIN { in_fm=0; fence=0 }\n /^---[[:space:]]*$/ {\n fence++\n if (fence==1) { in_fm=1; next }\n if (fence==2) { exit }\n }\n in_fm { print }\n' \"$SKILL_MD\")\n\nif [[ -z \"$FRONT\" ]]; then\n fail \"SKILL.md has a leading '---' but no closing fence or empty frontmatter\"\nelse\n pass \"SKILL.md frontmatter present\"\n\n name_line=\"$(printf '%s' \"$FRONT\" | grep -E '^name:' | head -1 || true)\"\n desc_line=\"$(printf '%s' \"$FRONT\" | grep -E '^description:' | head -1 || true)\"\n [[ -n \"$name_line\" ]] && pass \"frontmatter has 'name'\" || fail \"frontmatter missing 'name:'\"\n [[ -n \"$desc_line\" ]] && pass \"frontmatter has 'description'\" || fail \"frontmatter missing 'description:'\"\n\n # Sanity: name should look like a slug.\n fm_name=\"$(printf '%s' \"$name_line\" | sed -E 's/^name:[[:space:]]*//; s/^[\"'\\''\"]//; s/[\"'\\''\"]$//')\"\n folder_name=\"$(basename \"$ABS_SKILL_DIR\")\"\n if [[ -n \"$fm_name\" && \"$fm_name\" != \"$folder_name\" ]]; then\n warn \"frontmatter name '$fm_name' differs from folder name '$folder_name' (maintainer may rename — OK)\"\n fi\nfi\n\n# Relative path scan: every non-URL, non-anchor markdown link target must\n# resolve inside the skill folder.\n#\n# We extract ALL markdown links (`[label](target)`) and filter out URLs and\n# anchors here, rather than only matching dot-prefixed paths in the regex.\n# Plain intra-skill references like `[ref](references/foo.md)` or\n# `[script](scripts/run.sh)` are common and must be validated too — the\n# contract for SKILL.md says every relative path resolves on disk, regardless\n# of whether the author wrote `./references/foo.md` or `references/foo.md`.\n# A narrower `\\(\\.{1,2}/...\\)` pattern would silently let bare paths through.\nBAD_REFS=0\nESCAPE=0\n# Lexical escape check: count path segments and ensure no prefix walks above\n# the skill root. We do this on the literal target rather than from `cd … &&\n# pwd -P` so that a missing intermediate directory (which is itself a fail\n# we want to report) doesn't masquerade as an escape.\nescapes_root() {\n local p=\"$1\" depth=0 seg\n # Strip a leading \"./\" if present.\n p=\"${p#./}\"\n IFS='/' read -r -a _segs \u003c\u003c\u003c \"$p\"\n for seg in \"${_segs[@]}\"; do\n case \"$seg\" in\n ''|.) ;;\n ..) depth=$((depth-1)); (( depth \u003c 0 )) && return 0 ;;\n *) depth=$((depth+1)) ;;\n esac\n done\n return 1\n}\nwhile IFS= read -r ref; do\n # Skip protocol URLs, mailto, anchors-only, and absolute paths.\n case \"$ref\" in\n http*|https*|mailto:*|tel:*|\\#*|/*) continue ;;\n esac\n # Strip query and fragment components before resolving.\n target=\"${ref%%#*}\"\n target=\"${target%%\\?*}\"\n [[ -z \"$target\" ]] && continue\n if escapes_root \"$target\"; then\n ESCAPE=1\n fail \"path escapes skill folder: $ref\"\n continue\n fi\n if [[ ! -e \"$ABS_SKILL_DIR/$target\" ]]; then\n BAD_REFS=$((BAD_REFS+1))\n fail \"referenced file does not exist: $ref\"\n fi\ndone \u003c \u003c(grep -oE '\\!?\\[[^]]*\\]\\([^)]+\\)' \"$SKILL_MD\" 2>/dev/null \\\n | sed -E 's/.*\\(([^)]+)\\).*/\\1/' \\\n | sort -u)\n\nif [[ \"$BAD_REFS\" -eq 0 && \"$ESCAPE\" -eq 0 ]]; then\n pass \"all relative references resolve inside the skill folder\"\nfi\n\nif [[ \"$FAIL\" -eq 0 ]]; then\n printf 'RESULT=%s\\n' \"pass\"\n exit 0\nelse\n printf 'RESULT=%s\\n' \"fail\"\n exit 1\nfi\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":5095,"content_sha256":"05edc5d286fc8acf764936d3581109eb57d2ed015b3fe97fb36e1392c75ad6d2"},{"filename":"templates/ISSUE-BODY-bug.md","content":"### What happened?\n\n{{WHAT_HAPPENED}}\n\n### Steps to reproduce\n\n{{STEPS}}\n\n### Expected behavior\n\n{{EXPECTED}}\n\n### Open Design version\n\n{{OD_VERSION}}\n\n### Platform\n\n{{PLATFORM}}\n\n### Logs (optional)\n\n```\n{{LOGS}}\n```\n\n### Screenshots (optional)\n\n{{SCREENSHOTS}}\n\n### Additional context\n\n{{CONTEXT}}\n\n---\n\n_Reported via the `od-contribute` skill. If you can reproduce or have more context, please add a comment — every signal helps narrow the fix._\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":451,"content_sha256":"ee457b04ffd5e1525491db2144cc0a9926f1e8a4bbdeb14c51c11b532876ecf8"},{"filename":"templates/PR-BODY-design-system.md","content":"## What this PR adds\n\nA new Design System — **{{BRAND_NAME}}** — at `design-systems/{{BRAND_SLUG}}/DESIGN.md`.\n\n> {{PITCH}}\n\n## What this design system covers\n\n{{COVERAGE_NOTES}}\n\n## How to try it\n\n1. `cd open-design`\n2. `pnpm tools-dev run web`\n3. Start a new project and pick **{{BRAND_NAME}}** from the design system picker.\n4. Ask the model: _\"{{TRY_PROMPT}}\"_\n\n{{SCREENSHOT_BLOCK}}\n\n## What's in this PR\n\n- `design-systems/{{BRAND_SLUG}}/DESIGN.md` — the canonical design brief OD loads.\n- Any supporting assets in `design-systems/{{BRAND_SLUG}}/` are referenced from `DESIGN.md`.\n\n## Checklist\n\n- [x] DESIGN.md has the conventional sections (compared against existing OD design systems)\n- [x] No `../` path escapes outside the brand folder\n- [ ] Maintainer review\n\n---\n\n👋 This is my first OD contribution. Hi! If anything looks off, tell me what to change and I'll happily push a fixup commit.\n\nIf you want to chat (or you're another newcomer reading this and want help shipping your first PR), come hang out in the OD Discord: {{DISCORD_INVITE}}\n\n_Generated with the `od-contribute` skill._\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1107,"content_sha256":"5fc1c5256adea871f606ac02bc47d70e475a2bfb2e03fa015c4b3263d2ba09b1"},{"filename":"templates/PR-BODY-docs.md","content":"## What this PR fixes\n\n{{ONE_LINE_SUMMARY}}\n\n## Details\n\n{{DETAILS}}\n\n\u003c!--\nUse this for the body when there's nuance:\n- which file/section\n- the exact sentence/typo/dead link\n- what you replaced it with and why\n-->\n\n## Files touched\n\n{{FILES_LIST}}\n\n## Checklist\n\n- [x] Markdown still parses cleanly (no broken fences or structure)\n- [x] All links and image paths still resolve\n- [ ] Maintainer review\n\n---\n\n👋 This is my first OD contribution. Hi! Small fix, but I figured every typo / dead link costs the next reader 30 seconds, and this saves that.\n\nIf you want to chat or there's something you'd love help getting fixed, come find us in the OD Discord: {{DISCORD_INVITE}}\n\n_Generated with the `od-contribute` skill._\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":723,"content_sha256":"dae4a477c330238606bdb82d0e9d54b0dd1745bd17d76c99862f51589589abd0"},{"filename":"templates/PR-BODY-i18n.md","content":"## What this PR translates\n\n**{{DOC_NAME}}** → **{{LANG_DISPLAY_NAME}}** (`{{LANG_CODE}}`)\n\n- New file: `{{TRANSLATED_PATH}}`\n- Source: `{{ENGLISH_PATH}}`\n- Status: {{STATUS}} \u003c!-- \"missing\" (new translation) or \"stale\" (refreshed) -->\n\n## What I preserved\n\n- Every Markdown structure element (headings, lists, tables, callouts, link/image targets)\n- Code blocks — left untranslated\n- Brand names and product names — left untranslated\n- Internal cross-links — adjusted to point to the localized file when one exists, else to the English source\n\n## What I changed\n\n{{TRANSLATION_NOTES}}\n\n## How to verify\n\n```bash\n# Render preview locally\ncd open-design\n# (or just open the .md file in any Markdown viewer)\n```\n\n## Checklist\n\n- [x] Markdown parses cleanly (code fences balanced, no broken structure)\n- [x] All relative links and image paths still resolve\n- [x] External links return 2xx/3xx\n- [ ] Maintainer review\n\n---\n\n👋 This is my first OD contribution. I'm a native {{LANG_DISPLAY_NAME}} speaker (or close to it!) and want to help OD reach more people in my language.\n\nIf you want to chat or you're another translator reading this, come find us in the OD Discord: {{DISCORD_INVITE}}\n\n_Generated with the `od-contribute` skill._\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1243,"content_sha256":"e13575addf8c1dc9eb00af962908d5a0373204c6cb412e901cca4649bb308180"},{"filename":"templates/PR-BODY-skill.md","content":"## What this PR adds\n\nA new Skill — **{{SKILL_NAME}}** — at `skills/{{SKILL_SLUG}}/`.\n\n> {{PITCH}}\n\n## Why I made it\n\n{{MOTIVATION}}\n\n## How to try it\n\n1. `cd open-design`\n2. Run OD locally: `pnpm tools-dev run web`\n3. Open a project, start a chat, and ask: _\"{{TRY_PROMPT}}\"_\n\n{{SCREENSHOT_BLOCK}}\n\n## What's in this PR\n\n- `skills/{{SKILL_SLUG}}/SKILL.md` — the skill itself (frontmatter + instructions)\n- everything else inside `skills/{{SKILL_SLUG}}/` is referenced from `SKILL.md`\n\n## Checklist\n\n- [x] `SKILL.md` has a `name` and `description` in the frontmatter\n- [x] Every relative path in `SKILL.md` resolves\n- [x] No path escapes the skill folder\n- [ ] Maintainer review\n\n---\n\n👋 This is my first OD contribution. Hi! If anything looks off, tell me what to change and I'll happily push a fixup commit.\n\nIf you want to chat (or you're another newcomer reading this and want help shipping your first PR), come hang out in the OD Discord: {{DISCORD_INVITE}}\n\n_Generated with the `od-contribute` skill._\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1016,"content_sha256":"835646511ba142a45410bd19c6aace027203acce1bf47547c7fe95392ac92549"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"od-contribute — first-contribution flow for Open Design","type":"text"}]},{"type":"paragraph","content":[{"text":"Locked to ","type":"text"},{"text":"nexu-io/open-design","type":"text","marks":[{"type":"code_inline"}]},{"text":". Branches by ","type":"text"},{"text":"contribution type","type":"text","marks":[{"type":"strong"}]},{"text":", not by issue. Replaces the dev-loop with type-specific no-code validators. Designed so a product user with zero coding background can ship a real PR.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Language","type":"text"}]},{"type":"paragraph","content":[{"text":"Mirror the user's language in every user-facing message — ","type":"text"},{"text":"AskUserQuestion","type":"text","marks":[{"type":"code_inline"}]},{"text":" labels and descriptions, status updates, error explanations. Detect from their first message; when uncertain, default to English.","type":"text"}]},{"type":"paragraph","content":[{"text":"Generated artifacts (PR titles, commit messages, PR/issue body files, branch names) MUST be English","type":"text","marks":[{"type":"strong"}]},{"text":" regardless of the user's chat language. GitHub conventions, maintainer review, and search all assume English. The templates under ","type":"text"},{"text":"templates/","type":"text","marks":[{"type":"code_inline"}]},{"text":" are already English — keep them that way when rendering.","type":"text"}]},{"type":"paragraph","content":[{"text":"Scripts live under ","type":"text"},{"text":"scripts/","type":"text","marks":[{"type":"code_inline"}]},{"text":". Source the shared helpers from any script:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"source \"$(dirname \"$0\")/config.sh\"","type":"text"}]},{"type":"paragraph","content":[{"text":"SKILL_DIR","type":"text","marks":[{"type":"code_inline"}]},{"text":" below = the directory that contains this ","type":"text"},{"text":"SKILL.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Step 1 — Prereq check (always first)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash \"$SKILL_DIR/scripts/check-prereqs.sh\"","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Exit 0: capture ","type":"text"},{"text":"GH_USER=\u003clogin>","type":"text","marks":[{"type":"code_inline"}]},{"text":" from stdout. Default ","type":"text"},{"text":"TARGET_FORK=\"${GH_USER}/open-design\"","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Exit 2: surface the printed install / auth hint ","type":"text"},{"text":"verbatim","type":"text","marks":[{"type":"strong"}]},{"text":" and stop. Do not attempt token workarounds.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"If ","type":"text"},{"text":"gh repo view \"$TARGET_FORK\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" fails, ask the user (one ","type":"text"},{"text":"AskUserQuestion","type":"text","marks":[{"type":"code_inline"}]},{"text":") whether to fork now via ","type":"text"},{"text":"gh repo fork nexu-io/open-design --clone=false","type":"text","marks":[{"type":"code_inline"}]},{"text":". Default to yes.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Step 2 — Pick contribution type","type":"text"}]},{"type":"paragraph","content":[{"text":"Single ","type":"text"},{"text":"AskUserQuestion","type":"text","marks":[{"type":"code_inline"}]},{"text":" (header: \"Contribution\", multiSelect: false), four options. Translate option labels/descriptions into the user's chat language; the branch routing is unchanged.","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"🎨 Ship something I made with OD","type":"text","marks":[{"type":"strong"}]},{"text":" — ","type":"text"},{"text":"a Skill, Design System, HyperFrame, or template I want to contribute upstream","type":"text","marks":[{"type":"em"}]},{"text":" → branch ","type":"text"},{"text":"3a","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"🌍 Translate OD docs","type":"text","marks":[{"type":"strong"}]},{"text":" — ","type":"text"},{"text":"README / QUICKSTART / CONTRIBUTING into a new language","type":"text","marks":[{"type":"em"}]},{"text":" → branch ","type":"text"},{"text":"3b","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"📝 Fix docs / write a blog / fix a typo","type":"text","marks":[{"type":"strong"}]},{"text":" — ","type":"text"},{"text":"typo fix, dead link, use-case writeup","type":"text","marks":[{"type":"em"}]},{"text":" → branch ","type":"text"},{"text":"3c","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"🐛 Report a bug","type":"text","marks":[{"type":"strong"}]},{"text":" — ","type":"text"},{"text":"something broke; I'll help turn it into a high-quality issue","type":"text","marks":[{"type":"em"}]},{"text":" → branch ","type":"text"},{"text":"3d","type":"text","marks":[{"type":"code_inline"}]},{"text":" (issue path, no PR)","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Each branch below is self-contained. Steps 7–8 (preview + push) are shared across branches ","type":"text"},{"text":"3a","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"3b","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"3c","type":"text","marks":[{"type":"code_inline"}]},{"text":". Branch ","type":"text"},{"text":"3d","type":"text","marks":[{"type":"code_inline"}]},{"text":" skips them entirely.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 3a — OD product submission (Skill / Design System)","type":"text"}]},{"type":"paragraph","content":[{"text":"3a.1","type":"text","marks":[{"type":"strong"}]},{"text":" Ask user: \"What's the local path to the artifact you want to ship?\" (single free-text, translated into the user's chat language). Common: a folder path (Skill) or a single ","type":"text"},{"text":"DESIGN.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" file (Design System).","type":"text"}]},{"type":"paragraph","content":[{"text":"3a.2","type":"text","marks":[{"type":"strong"}]},{"text":" Sniff type:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Skill: folder containing SKILL.md with frontmatter.\n# Design System: file matching DESIGN.md anatomy.","type":"text"}]},{"type":"paragraph","content":[{"text":"If ambiguous, ask the user to confirm.","type":"text"}]},{"type":"paragraph","content":[{"text":"3a.3","type":"text","marks":[{"type":"strong"}]},{"text":" Run setup:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash \"$SKILL_DIR/scripts/setup-workspace.sh\" skill \u003cslug>\n# or\nbash \"$SKILL_DIR/scripts/setup-workspace.sh\" design-system \u003cslug>","type":"text"}]},{"type":"paragraph","content":[{"text":"\u003cslug>","type":"text","marks":[{"type":"code_inline"}]},{"text":" is ","type":"text"},{"text":"od::slugify","type":"text","marks":[{"type":"code_inline"}]},{"text":" of the Skill ","type":"text"},{"text":"name","type":"text","marks":[{"type":"code_inline"}]},{"text":" frontmatter field or of the brand name. Capture ","type":"text"},{"text":"WORKDIR","type":"text","marks":[{"type":"code_inline"}]},{"text":" from stdout.","type":"text"}]},{"type":"paragraph","content":[{"text":"3a.4","type":"text","marks":[{"type":"strong"}]},{"text":" Copy artifact into workspace at the right target dir:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Skill → ","type":"text"},{"text":"$WORKDIR/skills/\u003cslug>/","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Design System → ","type":"text"},{"text":"$WORKDIR/design-systems/\u003cbrand-slug>/DESIGN.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" (+ any sibling assets in the same folder)","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"3a.5","type":"text","marks":[{"type":"strong"}]},{"text":" Validate:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash \"$SKILL_DIR/scripts/validate-skill-submission.sh\" \"$WORKDIR/skills/\u003cslug>\"\n# or, with 1-2 reference DESIGN.md files passed in:\nbash \"$SKILL_DIR/scripts/validate-design-system.sh\" \\\n \"$WORKDIR/design-systems/\u003cslug>/DESIGN.md\" \\\n --reference \"$WORKDIR/design-systems/airbnb/DESIGN.md\" \\\n --reference \"$WORKDIR/design-systems/apple/DESIGN.md\"","type":"text"}]},{"type":"paragraph","content":[{"text":"If validation fails, surface the FAIL lines verbatim, ask the user to fix, retry. ","type":"text"},{"text":"Never push a failing artifact.","type":"text","marks":[{"type":"strong"}]}]},{"type":"paragraph","content":[{"text":"3a.6","type":"text","marks":[{"type":"strong"}]},{"text":" Ask 3 short questions via ","type":"text"},{"text":"AskUserQuestion","type":"text","marks":[{"type":"code_inline"}]},{"text":" (translate the labels into the user's chat language):","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"What name should we credit you under in the PR?\" — free-text","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"One-line pitch for this Skill / Design System?\" — free-text","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Path to a screenshot (optional)?\" — free-text","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"3a.7","type":"text","marks":[{"type":"strong"}]},{"text":" Render ","type":"text"},{"text":"templates/PR-BODY-skill.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" (or ","type":"text"},{"text":"PR-BODY-design-system.md","type":"text","marks":[{"type":"code_inline"}]},{"text":") with substitutions:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"{{SKILL_NAME}}","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"{{SKILL_SLUG}}","type":"text","marks":[{"type":"code_inline"}]},{"text":" (or ","type":"text"},{"text":"{{BRAND_NAME}}","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"{{BRAND_SLUG}}","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"{{PITCH}}","type":"text","marks":[{"type":"code_inline"}]},{"text":" (the one-line)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"{{MOTIVATION}}","type":"text","marks":[{"type":"code_inline"}]},{"text":" (free-text — agent can offer to draft this from the skill body, but user confirms)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"{{TRY_PROMPT}}","type":"text","marks":[{"type":"code_inline"}]},{"text":" (a prompt they recommend trying — agent suggests a default, user confirms)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"{{SCREENSHOT_BLOCK}}","type":"text","marks":[{"type":"code_inline"}]},{"text":" (Markdown image block if a screenshot path was given, else empty)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"{{DISCORD_INVITE}}","type":"text","marks":[{"type":"code_inline"}]},{"text":" from ","type":"text"},{"text":"$OD_DISCORD_INVITE","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"Write to ","type":"text"},{"text":"$WORKDIR/.od-contrib/PR-BODY.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"→ Jump to ","type":"text"},{"text":"Step 7","type":"text","marks":[{"type":"strong"}]},{"text":".","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 3b — i18n translation","type":"text"}]},{"type":"paragraph","content":[{"text":"3b.1","type":"text","marks":[{"type":"strong"}]},{"text":" Setup workspace (slug = ","type":"text"},{"text":"translate-\u003cdoc>-\u003clang>","type":"text","marks":[{"type":"code_inline"}]},{"text":" if known, else ","type":"text"},{"text":"translate","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash \"$SKILL_DIR/scripts/setup-workspace.sh\" i18n translate\n# capture WORKDIR","type":"text"}]},{"type":"paragraph","content":[{"text":"3b.2","type":"text","marks":[{"type":"strong"}]},{"text":" Discover gaps:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash \"$SKILL_DIR/scripts/discover-i18n-gaps.sh\" \"$WORKDIR\" > /tmp/od-i18n-gaps.json","type":"text"}]},{"type":"paragraph","content":[{"text":"Each line is JSON. Rank by:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"status: \"missing\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" first (missing language is highest leverage)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"then ","type":"text"},{"text":"status: \"stale\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" ordered by ","type":"text"},{"text":"english_commits_since_translation","type":"text","marks":[{"type":"code_inline"}]},{"text":" desc","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"README family before QUICKSTART before CONTRIBUTING","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"3b.3","type":"text","marks":[{"type":"strong"}]},{"text":" Take the top 3–4 gaps and present via ","type":"text"},{"text":"AskUserQuestion","type":"text","marks":[{"type":"code_inline"}]},{"text":" (header: \"Translation target\"). Each option label like: ","type":"text"},{"text":"README → 한국어 (Korean)","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"QUICKSTART (zh-CN) refresh — 12 commits behind","type":"text","marks":[{"type":"code_inline"}]},{"text":". Translate the header text into the user's chat language but keep the option labels descriptive (the language names belong in their native script).","type":"text"}]},{"type":"paragraph","content":[{"text":"3b.4","type":"text","marks":[{"type":"strong"}]},{"text":" Once user picks, ","type":"text"},{"text":"rename branch","type":"text","marks":[{"type":"strong"}]},{"text":" to be specific:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"git -C \"$WORKDIR\" branch -m \"od-contrib/i18n/\u003cdoc>-\u003clang>-\u003cdate>\"","type":"text"}]},{"type":"paragraph","content":[{"text":"(or pre-set the slug in step 3b.1 if the user confirmed earlier.)","type":"text"}]},{"type":"paragraph","content":[{"text":"3b.5","type":"text","marks":[{"type":"strong"}]},{"text":" Translate. Read the English source. Translate ","type":"text"},{"text":"structure-preserving","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Code blocks: leave untranslated","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Brand / product names: leave untranslated","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Filenames in inline code: leave untranslated","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Image / link targets: leave untranslated; if a localized version of a linked doc exists, swap the link to the localized file","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Headings: translate, keep the heading depth identical","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Tables: translate cell text only, keep alignment / pipes","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Write the result to ","type":"text"},{"text":"$WORKDIR/\u003cTRANSLATED_PATH>","type":"text","marks":[{"type":"code_inline"}]},{"text":" (e.g. ","type":"text"},{"text":"QUICKSTART.es.md","type":"text","marks":[{"type":"code_inline"}]},{"text":"). Show user a unified diff vs. the English source for visual sanity-check (line-count delta within ±15% is a healthy signal).","type":"text"}]},{"type":"paragraph","content":[{"text":"3b.6","type":"text","marks":[{"type":"strong"}]},{"text":" Validate the translated file against the English source. The ","type":"text"},{"text":"--reference","type":"text","marks":[{"type":"code_inline"}]},{"text":" flag tells the validator to ignore relative refs that were already broken in the source — OD docs frequently link to website route slugs (e.g. ","type":"text"},{"text":"skills/blog-post/","type":"text","marks":[{"type":"code_inline"}]},{"text":") that aren't files on disk; we don't want a structure-preserving translation to fail because of pre-existing dead refs.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash \"$SKILL_DIR/scripts/validate-markdown.sh\" \\\n \"$WORKDIR/\u003cTRANSLATED_PATH>\" \\\n --reference \"$WORKDIR/\u003cENGLISH_PATH>\"","type":"text"}]},{"type":"paragraph","content":[{"text":"If FAIL → surface verbatim, fix, retry.","type":"text"}]},{"type":"paragraph","content":[{"text":"3b.7","type":"text","marks":[{"type":"strong"}]},{"text":" Render ","type":"text"},{"text":"templates/PR-BODY-i18n.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" with ","type":"text"},{"text":"{{DOC_NAME}}","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"{{LANG_DISPLAY_NAME}}","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"{{LANG_CODE}}","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"{{TRANSLATED_PATH}}","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"{{ENGLISH_PATH}}","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"{{STATUS}}","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"{{TRANSLATION_NOTES}}","type":"text","marks":[{"type":"code_inline"}]},{"text":" (one paragraph from the agent: anything tricky, untranslated terms it kept, etc.), ","type":"text"},{"text":"{{DISCORD_INVITE}}","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"→ ","type":"text"},{"text":"Step 7","type":"text","marks":[{"type":"strong"}]},{"text":".","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 3c — Docs / blog / typo","type":"text"}]},{"type":"paragraph","content":[{"text":"3c.1","type":"text","marks":[{"type":"strong"}]},{"text":" Setup workspace (slug ","type":"text"},{"text":"docs","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash \"$SKILL_DIR/scripts/setup-workspace.sh\" docs \u003cslug>","type":"text"}]},{"type":"paragraph","content":[{"text":"3c.2","type":"text","marks":[{"type":"strong"}]},{"text":" Ask user (one ","type":"text"},{"text":"AskUserQuestion","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Auto-discover small fixes","type":"text","marks":[{"type":"strong"}]},{"text":" (run discover-doc-gaps, pick something)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"I have a specific fix in mind","type":"text","marks":[{"type":"strong"}]},{"text":" (free-text)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"I want to write a blog / case study","type":"text","marks":[{"type":"strong"}]},{"text":" (free-text — what's the use case?)","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"3c.3 (Auto-discover branch)","type":"text","marks":[{"type":"strong"}]},{"text":" Run:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash \"$SKILL_DIR/scripts/discover-doc-gaps.sh\" \"$WORKDIR\" > /tmp/od-doc-gaps.json","type":"text"}]},{"type":"paragraph","content":[{"text":"Group by ","type":"text"},{"text":"kind","type":"text","marks":[{"type":"code_inline"}]},{"text":" (typo / deadlink / todo). Show the user up to 6 candidates via ","type":"text"},{"text":"AskUserQuestion","type":"text","marks":[{"type":"code_inline"}]},{"text":". Once picked, apply the fix in code (typo: replace word; deadlink: ask user for the new URL; todo: that's a proper task, ask user to write the missing prose).","type":"text"}]},{"type":"paragraph","content":[{"text":"3c.4 (Specific-fix branch)","type":"text","marks":[{"type":"strong"}]},{"text":" Read the file, apply user's described change. Confirm via diff.","type":"text"}]},{"type":"paragraph","content":[{"text":"3c.5 (Blog branch)","type":"text","marks":[{"type":"strong"}]},{"text":" First check whether OD has a blog directory:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"ls \"$WORKDIR/docs\" 2>/dev/null","type":"text"}]},{"type":"paragraph","content":[{"text":"If a ","type":"text"},{"text":"docs/blog/","type":"text","marks":[{"type":"code_inline"}]},{"text":" or similar exists, place the new post there. If not, ask the user where it should live, defaulting to ","type":"text"},{"text":"docs/\u003cslug>.md","type":"text","marks":[{"type":"code_inline"}]},{"text":". Generate an outline → user fills in user-specific bits (their use case, screenshots, the prompt they used, the rendered output) → agent stitches into a final Markdown.","type":"text"}]},{"type":"paragraph","content":[{"text":"3c.6","type":"text","marks":[{"type":"strong"}]},{"text":" Validate every changed/added file. For files that already exist in the repo (typo fix, dead-link fix, doc edit), pass ","type":"text"},{"text":"--reference","type":"text","marks":[{"type":"code_inline"}]},{"text":" pointing at HEAD's version so we only fail on relative refs the user ","type":"text"},{"text":"introduced","type":"text","marks":[{"type":"em"}]},{"text":", not on pre-existing route slugs:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# For modifications to existing files:\ngit -C \"$WORKDIR\" show \"HEAD:\u003cpath>\" > \"/tmp/od-contrib-orig-\u003cbasename>\" 2>/dev/null\nbash \"$SKILL_DIR/scripts/validate-markdown.sh\" \\\n \"$WORKDIR/\u003cchanged-path>\" \\\n --reference \"/tmp/od-contrib-orig-\u003cbasename>\"\n\n# For brand-new files (e.g. a blog post the user is creating from scratch),\n# omit --reference. The validator will skip the relative-ref check entirely\n# (since it can't tell route slugs from real paths in isolation).","type":"text"}]},{"type":"paragraph","content":[{"text":"3c.7","type":"text","marks":[{"type":"strong"}]},{"text":" Render ","type":"text"},{"text":"templates/PR-BODY-docs.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" with ","type":"text"},{"text":"{{ONE_LINE_SUMMARY}}","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"{{DETAILS}}","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"{{FILES_LIST}}","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"{{DISCORD_INVITE}}","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"→ ","type":"text"},{"text":"Step 7","type":"text","marks":[{"type":"strong"}]},{"text":".","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 3d — Bug report (issue path, no PR)","type":"text"}]},{"type":"paragraph","content":[{"text":"3d.1","type":"text","marks":[{"type":"strong"}]},{"text":" Read OD's actual schema at runtime to make sure we mirror it:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"gh api \"repos/${TARGET_REPO}/contents/.github/ISSUE_TEMPLATE/bug-report.yml\" --jq .content | base64 -d > /tmp/od-bug-report.yml","type":"text"}]},{"type":"paragraph","content":[{"text":"If the schema has drifted from the template (","type":"text"},{"text":"templates/ISSUE-BODY-bug.md","type":"text","marks":[{"type":"code_inline"}]},{"text":"), regenerate the body to match.","type":"text"}]},{"type":"paragraph","content":[{"text":"3d.2","type":"text","marks":[{"type":"strong"}]},{"text":" Ask the user via ","type":"text"},{"text":"AskUserQuestion","type":"text","marks":[{"type":"code_inline"}]},{"text":", one structured prompt per critical field. Use ","type":"text"},{"text":"plain language","type":"text","marks":[{"type":"strong"}]},{"text":", not the YAML field names:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Bug-report field","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Prompt to user","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"description","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"What went wrong? One sentence is fine.\"","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"steps","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"How can I reproduce it? Walk me through step by step.\"","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"expected","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"What did you expect to happen?\"","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"version","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"Which OD version are you running? (About menu, or ","type":"text"},{"text":"od --version","type":"text","marks":[{"type":"code_inline"}]},{"text":")\"","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"platform","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"dropdown: macOS (Apple Silicon) / macOS (Intel) / Windows / Linux / Other","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"logs","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"Any error logs you can paste? Skip if you don't have them.\"","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"screenshots","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"Path to a screenshot? Skip if you don't have one.\"","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Translate every prompt above into the user's chat language at runtime.","type":"text"}]},{"type":"paragraph","content":[{"text":"3d.3","type":"text","marks":[{"type":"strong"}]},{"text":" Auto-collect what we can (these don't need to ask the user):","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"OS family from ","type":"text"},{"text":"uname","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Node version from ","type":"text"},{"text":"node -v","type":"text","marks":[{"type":"code_inline"}]},{"text":" if relevant","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"3d.4","type":"text","marks":[{"type":"strong"}]},{"text":" Dedupe: extract 3–5 keywords from the description, run:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"gh search issues \"\u003ckeywords>\" --repo \"$TARGET_REPO\" --state open --limit 5 --json number,title,url","type":"text"}]},{"type":"paragraph","content":[{"text":"If matches exist, present them to the user via ","type":"text"},{"text":"AskUserQuestion","type":"text","marks":[{"type":"code_inline"}]},{"text":" (translate to user's language): \"These existing issues look related. Do you want to: (a) comment on an existing one, (b) open a new issue anyway, (c) cancel?\"","type":"text"}]},{"type":"paragraph","content":[{"text":"3d.5","type":"text","marks":[{"type":"strong"}]},{"text":" If proceeding with new issue, render ","type":"text"},{"text":"templates/ISSUE-BODY-bug.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" and submit:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash \"$SKILL_DIR/scripts/create-issue.sh\" \\\n --title \"$TITLE\" \\\n --body-file \"$WORKDIR_OR_TMP/.od-contrib/ISSUE-BODY.md\" \\\n --dedupe-keywords \"\u003ckeywords>\"","type":"text"}]},{"type":"paragraph","content":[{"text":"3d.6","type":"text","marks":[{"type":"strong"}]},{"text":" Print the issue URL on its own line. ","type":"text"},{"text":"Do not","type":"text","marks":[{"type":"strong"}]},{"text":" push branches or open PRs from this branch.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Step 7 — Preview + confirm (shared, PR branches only)","type":"text"}]},{"type":"paragraph","content":[{"text":"Show the user a clean summary:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"text"},"content":[{"text":"About to commit:\n Branch: od-contrib/\u003ctype>/\u003cslug>-\u003cdate>\n Files:\n + skills/foo/SKILL.md (1.2 KB)\n + skills/foo/preview.png (54 KB)\n Push to: \u003cfork or upstream>\n Open PR: nexu-io/open-design:main ← \u003cfork>:\u003cbranch>","type":"text"}]},{"type":"paragraph","content":[{"text":"Then ","type":"text"},{"text":"git -C \"$WORKDIR\" diff --stat","type":"text","marks":[{"type":"code_inline"}]},{"text":" and a ","type":"text"},{"text":"head -40","type":"text","marks":[{"type":"code_inline"}]},{"text":" of the rendered PR body for visual sanity.","type":"text"}]},{"type":"paragraph","content":[{"text":"Required ","type":"text"},{"text":"AskUserQuestion","type":"text","marks":[{"type":"code_inline"}]},{"text":" confirmation (translate to user's language): ","type":"text"},{"text":"\"Push this PR?\"","type":"text","marks":[{"type":"strong"}]},{"text":" with three options:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Ship it","type":"text","marks":[{"type":"strong"}]},{"text":" — proceed to Step 8","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Let me revise","type":"text","marks":[{"type":"strong"}]},{"text":" — return to the relevant Step 3 sub-step","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Cancel","type":"text","marks":[{"type":"strong"}]},{"text":" — leave the workspace on disk, tell the user the path so they can return later, exit","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Never push without an explicit \"Ship it\".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Step 8 — Push & open PR","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash \"$SKILL_DIR/scripts/create-pr.sh\" \\\n --workdir \"$WORKDIR\" \\\n --type \"\u003cskill|design-system|i18n|docs>\" \\\n --title \"\u003cPR title from references/newcomer-tone.md>\" \\\n --body-file \"$WORKDIR/.od-contrib/PR-BODY.md\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Print the PR URL on its own line. Done.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Safety rails (mandatory)","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Never push to ","type":"text"},{"text":"main","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"master","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"develop","type":"text","marks":[{"type":"code_inline"}]},{"text":". The push scripts refuse.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Never ","type":"text"},{"text":"--force","type":"text","marks":[{"type":"code_inline"}]},{"text":" push. Just don't.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"All workspace activity stays under ","type":"text"},{"text":"$OD_WORK_ROOT","type":"text","marks":[{"type":"code_inline"}]},{"text":" (default ","type":"text"},{"text":"$HOME/od-contrib-work","type":"text","marks":[{"type":"code_inline"}]},{"text":"). ","type":"text"},{"text":"od::assert_in_workroot","type":"text","marks":[{"type":"code_inline"}]},{"text":" enforces this.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Bug-report path ","type":"text"},{"text":"always","type":"text","marks":[{"type":"strong"}]},{"text":" runs the dedupe search before ","type":"text"},{"text":"gh issue create","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Honor user memory: skip GitHub user ","type":"text"},{"text":"xxiaoxiong","type":"text","marks":[{"type":"code_inline"}]},{"text":" from any contributor lookup (","type":"text"},{"type":"wikilink","attrs":{"alias":null,"embed":false,"target":"feedback_no_outreach_xxiaoxiong","blockId":null,"heading":null}},{"text":").","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When NOT to use this skill","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"The user wants to fix a daemon / web bug or add a feature with code changes → use ","type":"text"},{"text":"auto-github-contributor","type":"text","marks":[{"type":"code_inline"}]},{"text":" instead (it has the TDD loop). This skill deliberately doesn't run lint/typecheck/tests because content paths don't need them.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"The user wants to ","type":"text"},{"text":"generate","type":"text","marks":[{"type":"em"}]},{"text":" a Skill / Design System from scratch → that's Open Design itself. Run OD first, get an artifact, then come back here to ship it.","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"od-contribute","author":"@skillopedia","source":{"stars":56938,"repo_name":"open-design","origin_url":"https://github.com/nexu-io/open-design/blob/HEAD/.claude/skills/od-contribute/SKILL.md","repo_owner":"nexu-io","body_sha256":"5caf275dc46e5f964ca4206bb31536305997974adc28d821ed7c5d019569aca6","cluster_key":"584a2784f21c1dc985183e239fa54e243a259db765edf606b61c23b9a3c181e3","clean_bundle":{"format":"clean-skill-bundle-v1","source":"nexu-io/open-design/.claude/skills/od-contribute/SKILL.md","attachments":[{"id":"c00e0d8d-3ec1-58d3-abe0-94cd898f4197","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/c00e0d8d-3ec1-58d3-abe0-94cd898f4197/attachment.yaml","path":"agents/openai.yaml","size":652,"sha256":"31b36bbae6611cb0cb9f46af7ce54d1d0fc8b0db17af0b1fc8b92242636d782b","contentType":"application/yaml; charset=utf-8"},{"id":"1fe500c9-68e1-5662-8f6f-4e33c820e06b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/1fe500c9-68e1-5662-8f6f-4e33c820e06b/attachment.sh","path":"install.sh","size":5225,"sha256":"b7f0806d46f845a17eb5f3ff15e1f1963d707244b5f09ba5203f438a746856af","contentType":"application/x-sh; charset=utf-8"},{"id":"0ea3106b-f802-579f-9fc7-be6dc9aea7a7","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/0ea3106b-f802-579f-9fc7-be6dc9aea7a7/attachment.md","path":"references/design-system-anatomy.md","size":1997,"sha256":"2c62669d3011a258a084173916dfdf50990a7935182b956fa464a2411f46b7c0","contentType":"text/markdown; charset=utf-8"},{"id":"e9fcd3ab-728a-51f5-89a0-ffcf33441a1f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/e9fcd3ab-728a-51f5-89a0-ffcf33441a1f/attachment.md","path":"references/newcomer-tone.md","size":2717,"sha256":"b33ddc3deec44270b82636ad1b8b075ad6b509a1dbe9037bf552bc556a745f24","contentType":"text/markdown; charset=utf-8"},{"id":"198e5cc7-50dc-5f9a-90b5-b6fe2d6488ff","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/198e5cc7-50dc-5f9a-90b5-b6fe2d6488ff/attachment.md","path":"references/od-repo-map.md","size":2261,"sha256":"01b1e9cbeb070973901118082cfe3b203b3f18daf2e7771b156bda7da2a4782f","contentType":"text/markdown; charset=utf-8"},{"id":"60b3362e-cc92-5ed7-8207-658780a35472","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/60b3362e-cc92-5ed7-8207-658780a35472/attachment.md","path":"references/skill-anatomy.md","size":2334,"sha256":"df9b55f415f6ebf30ea3ec4d4d8542ce60f3580a5d875556df2c50495f515d87","contentType":"text/markdown; charset=utf-8"},{"id":"c32dd8ef-67ed-5d50-9971-030e2dc61616","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/c32dd8ef-67ed-5d50-9971-030e2dc61616/attachment.sh","path":"scripts/check-prereqs.sh","size":4217,"sha256":"82dbb5fdb1cb3d50ad59189ec1113a769539da4da18df30eb6a0009b6ea16229","contentType":"application/x-sh; charset=utf-8"},{"id":"46950479-c37e-5d6d-a8a3-7246f9831881","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/46950479-c37e-5d6d-a8a3-7246f9831881/attachment.sh","path":"scripts/config.sh","size":2472,"sha256":"b860663b801054ddf11ffcc92ee6712d4057d65799351ad5c754a726a144102d","contentType":"application/x-sh; charset=utf-8"},{"id":"913fca2c-c4ac-5baf-acb8-ba461b56a826","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/913fca2c-c4ac-5baf-acb8-ba461b56a826/attachment.sh","path":"scripts/create-issue.sh","size":3803,"sha256":"241b682cd1e2d36299f7587e77649bb0ddd427d001257db0484d4aef5cd579fe","contentType":"application/x-sh; charset=utf-8"},{"id":"8b4e44cd-2f88-57b0-bae9-476daa77b597","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/8b4e44cd-2f88-57b0-bae9-476daa77b597/attachment.sh","path":"scripts/create-pr.sh","size":3907,"sha256":"114626d689e74d0c8e6e12402d4ee99cab210df45f44fc4b556ddc7c2c998733","contentType":"application/x-sh; charset=utf-8"},{"id":"966da60d-1638-5900-b499-f5ac52a1d081","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/966da60d-1638-5900-b499-f5ac52a1d081/attachment.sh","path":"scripts/discover-doc-gaps.sh","size":4063,"sha256":"62fe760c344742157b41ed25c8cff95602eb8602e94af133a5b5c249fc5c7600","contentType":"application/x-sh; charset=utf-8"},{"id":"ddb67dac-600c-5490-a81a-f1300d06c39d","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ddb67dac-600c-5490-a81a-f1300d06c39d/attachment.sh","path":"scripts/discover-i18n-gaps.sh","size":4866,"sha256":"cf131305267e9b24d355c0dec8c9efaa6943898a02278bcad4f2fc09cd28b62a","contentType":"application/x-sh; charset=utf-8"},{"id":"1a6c1899-64e8-549b-b255-07f9c7452994","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/1a6c1899-64e8-549b-b255-07f9c7452994/attachment.sh","path":"scripts/setup-workspace.sh","size":3476,"sha256":"cfe9f52bf4504416d47df234253f7be81ce3b44110acf004498915cb50e195d1","contentType":"application/x-sh; charset=utf-8"},{"id":"ff8d2910-43af-53d6-b18f-fbcdc66e764c","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ff8d2910-43af-53d6-b18f-fbcdc66e764c/attachment.sh","path":"scripts/validate-design-system.sh","size":3367,"sha256":"4499825d3a0b91ba7817b1a7c094008c625047efbcf96225fc976a3fef6a0ee9","contentType":"application/x-sh; charset=utf-8"},{"id":"21589bfc-89e8-5e17-a51f-198eee016568","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/21589bfc-89e8-5e17-a51f-198eee016568/attachment.sh","path":"scripts/validate-markdown.sh","size":7075,"sha256":"4fca46f0e9aaf6018a855707428ce802e95748e496c2ba7c7b2d5333bf57dc81","contentType":"application/x-sh; charset=utf-8"},{"id":"2ea5ae57-9091-5f95-a4ae-3cac58074c8b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/2ea5ae57-9091-5f95-a4ae-3cac58074c8b/attachment.sh","path":"scripts/validate-skill-submission.sh","size":5095,"sha256":"05edc5d286fc8acf764936d3581109eb57d2ed015b3fe97fb36e1392c75ad6d2","contentType":"application/x-sh; charset=utf-8"},{"id":"92e99dda-11e5-5f40-93b1-a4ba434d29ee","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/92e99dda-11e5-5f40-93b1-a4ba434d29ee/attachment.md","path":"templates/ISSUE-BODY-bug.md","size":451,"sha256":"ee457b04ffd5e1525491db2144cc0a9926f1e8a4bbdeb14c51c11b532876ecf8","contentType":"text/markdown; charset=utf-8"},{"id":"7ba317fc-a0e1-5f65-81fd-f6bfee826ff3","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/7ba317fc-a0e1-5f65-81fd-f6bfee826ff3/attachment.md","path":"templates/PR-BODY-design-system.md","size":1107,"sha256":"5fc1c5256adea871f606ac02bc47d70e475a2bfb2e03fa015c4b3263d2ba09b1","contentType":"text/markdown; charset=utf-8"},{"id":"e81da434-1c34-53d6-98a6-e09b82649bd0","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/e81da434-1c34-53d6-98a6-e09b82649bd0/attachment.md","path":"templates/PR-BODY-docs.md","size":723,"sha256":"dae4a477c330238606bdb82d0e9d54b0dd1745bd17d76c99862f51589589abd0","contentType":"text/markdown; charset=utf-8"},{"id":"f33f7e04-19d7-5523-a727-4f6841a07a07","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f33f7e04-19d7-5523-a727-4f6841a07a07/attachment.md","path":"templates/PR-BODY-i18n.md","size":1243,"sha256":"e13575addf8c1dc9eb00af962908d5a0373204c6cb412e901cca4649bb308180","contentType":"text/markdown; charset=utf-8"},{"id":"ea6f0d14-a0ac-5fc4-9047-c35fe2cc2168","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ea6f0d14-a0ac-5fc4-9047-c35fe2cc2168/attachment.md","path":"templates/PR-BODY-skill.md","size":1016,"sha256":"835646511ba142a45410bd19c6aace027203acce1bf47547c7fe95392ac92549","contentType":"text/markdown; charset=utf-8"}],"bundle_sha256":"bc479fd9b000051af7c40e59692e4f12f3d0bbe4210df71f14460adf12bf158a","attachment_count":21,"text_attachments":21,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":".claude/skills/od-contribute/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"documents-office","category_label":"Documents"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"documents-office","import_tag":"clean-skills-v1","description":"One-click contribution flow for Open Design (nexu-io/open-design) — even for non-coders. Pick one of four cards (ship a Skill or Design System you made with OD; translate docs; fix a typo / write a blog; report a bug), the agent validates and opens a PR (or issue) for you. Trigger words contribute to open design, ship my OD skill, ship my OD design system, translate OD docs, report an OD bug, od-contribute.","allowed-tools":["Bash","Read","Write","Edit","AskUserQuestion","TaskCreate","TaskUpdate","WebFetch"]}},"renderedAt":1782980881867}

od-contribute — first-contribution flow for Open Design Locked to . Branches by contribution type , not by issue. Replaces the dev-loop with type-specific no-code validators. Designed so a product user with zero coding background can ship a real PR. Language Mirror the user's language in every user-facing message — labels and descriptions, status updates, error explanations. Detect from their first message; when uncertain, default to English. Generated artifacts (PR titles, commit messages, PR/issue body files, branch names) MUST be English regardless of the user's chat language. GitHub conve…