#!/usr/bin/env sh set -eu # Vendor a trimmed Playbook snapshot into a target project (offline copy), # then run sync_standards to materialize .agents// and .gitattributes in # the target project root. # # Usage: # sh scripts/vendor_playbook.sh -project-root # default: tsl # sh scripts/vendor_playbook.sh -project-root -langs tsl,cpp # sh scripts/vendor_playbook.sh -project-root -langs tsl,cpp -apply-templates # # Options: # -project-root PATH Target project root (required) # -langs L1,L2 Comma/space-separated list of languages (default: tsl) # -apply-templates Apply CI/lang templates to project root (skip if exists) # # Notes: # - Snapshot is written to: /docs/standards/playbook/ # - Existing snapshot is backed up before overwrite. # - Ruleset templates from rulesets/ will be copied to snapshot for sync_standards use. # - With -apply-templates, CI and lang templates are copied to project root. SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P)" SRC="$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd -P)" usage() { cat <<'EOF' >&2 Usage: sh scripts/vendor_playbook.sh -project-root # default: tsl sh scripts/vendor_playbook.sh -project-root -langs tsl,cpp sh scripts/vendor_playbook.sh -project-root -langs tsl,cpp -apply-templates Options: -project-root PATH Target project root (required) -langs L1,L2 Comma/space-separated list of languages (default: tsl) -apply-templates Apply CI/lang templates to project root (skip if exists) -h, -help Show this help EOF } if [ "${1:-}" = "-h" ] || [ "${1:-}" = "-help" ]; then usage exit 0 fi PROJECT_ROOT="" langs="" APPLY_TEMPLATES=0 # Parse arguments while [ $# -gt 0 ]; do case "$1" in -project-root) if [ $# -lt 2 ] || [ -z "${2:-}" ]; then echo "ERROR: -project-root requires a path." >&2 usage exit 1 fi PROJECT_ROOT="$2" shift 2 ;; -langs) if [ $# -lt 2 ] || [ -z "${2:-}" ]; then echo "ERROR: -langs requires a value." >&2 usage exit 1 fi langs="$2" shift 2 ;; -apply-templates) APPLY_TEMPLATES=1 shift ;; -*) echo "ERROR: Unknown option: $1" >&2 exit 1 ;; *) echo "ERROR: positional args are not supported; use -project-root/-langs." >&2 exit 1 ;; esac done if [ -z "$PROJECT_ROOT" ]; then echo "ERROR: -project-root is required." >&2 usage exit 1 fi if [ -z "${langs:-}" ]; then langs="tsl" fi timestamp="$(date +%Y%m%d%H%M%S 2>/dev/null || echo bak)" if [ ! -d "$PROJECT_ROOT" ]; then echo "ERROR: project root does not exist: $PROJECT_ROOT" >&2 exit 1 fi PROJECT_ROOT_ABS="$(CDPATH= cd -- "$PROJECT_ROOT" && pwd -P)" DEST_PREFIX="$PROJECT_ROOT_ABS/docs/standards/playbook" DEST_STANDARDS="$PROJECT_ROOT_ABS/docs/standards" mkdir -p "$DEST_STANDARDS" if [ -e "$DEST_PREFIX" ]; then mv "$DEST_PREFIX" "$DEST_STANDARDS/playbook.bak.$timestamp" echo "Backed up existing snapshot -> docs/standards/playbook.bak.$timestamp" fi mkdir -p "$DEST_PREFIX" # Always include: scripts + gitattributes + docs/common + codex/skills cp "$SRC/.gitattributes" "$DEST_PREFIX/.gitattributes" cp -R "$SRC/scripts" "$DEST_PREFIX/" cp -R "$SRC/codex" "$DEST_PREFIX/" cp "$SRC/SKILLS.md" "$DEST_PREFIX/SKILLS.md" mkdir -p "$DEST_PREFIX/docs" cp -R "$SRC/docs/common" "$DEST_PREFIX/docs/" # Copy rulesets mkdir -p "$DEST_PREFIX/rulesets" cp "$SRC/rulesets/index.md" "$DEST_PREFIX/rulesets/index.md" mkdir -p "$DEST_PREFIX/templates" if [ -d "$SRC/templates/ci" ]; then cp -R "$SRC/templates/ci" "$DEST_PREFIX/templates/" fi old_ifs="${IFS}" IFS=', ' set -- $langs IFS="${old_ifs}" langs_csv="" for lang in "$@"; do [ -n "$lang" ] || continue case "$lang" in ""|*/*|*\\*|*..*) echo "ERROR: invalid lang=$lang" >&2 exit 1 ;; esac if [ ! -d "$SRC/docs/$lang" ]; then echo "ERROR: docs not found for lang=$lang ($SRC/docs/$lang)" >&2 exit 1 fi if [ ! -d "$SRC/rulesets/$lang" ]; then echo "ERROR: rulesets not found for lang=$lang ($SRC/rulesets/$lang)" >&2 exit 1 fi cp -R "$SRC/docs/$lang" "$DEST_PREFIX/docs/" cp -R "$SRC/rulesets/$lang" "$DEST_PREFIX/rulesets/" if [ -d "$SRC/templates/$lang" ]; then cp -R "$SRC/templates/$lang" "$DEST_PREFIX/templates/" fi if [ -n "$langs_csv" ]; then langs_csv="$langs_csv,$lang" else langs_csv="$lang" fi done cat >"$DEST_PREFIX/docs/index.md" <>"$DEST_PREFIX/docs/index.md" <<'EOF' ## TSL(tsl) - 代码风格:`tsl/code_style.md` - 命名规范:`tsl/naming.md` - 语法手册:`tsl/syntax_book/index.md` - 工具链与验证命令(模板):`tsl/toolchain.md` EOF ;; cpp) cat >>"$DEST_PREFIX/docs/index.md" <<'EOF' ## C++(cpp) - 代码风格:`cpp/code_style.md` - 命名规范:`cpp/naming.md` - 工具链与验证命令(模板):`cpp/toolchain.md` - 第三方依赖(Conan):`cpp/dependencies_conan.md` - clangd 配置:`cpp/clangd.md` EOF ;; python) cat >>"$DEST_PREFIX/docs/index.md" <<'EOF' ## Python(python) - 代码风格:`python/style_guide.md` - 工具链:`python/tooling.md` - 配置清单:`python/configuration.md` EOF ;; markdown) cat >>"$DEST_PREFIX/docs/index.md" <<'EOF' ## Markdown(markdown) - 代码块与行内代码格式:`markdown/index.md` EOF ;; esac } for lang in "$@"; do [ -n "$lang" ] || continue append_docs_section "$lang" done commit="" if command -v git >/dev/null 2>&1; then commit="$(git -C "$SRC" rev-parse HEAD 2>/dev/null || true)" fi cat >"$DEST_PREFIX/README.md" <"$DEST_PREFIX/SOURCE.md" </dev/null || date) - Langs: ${langs_csv} - Generated-by: scripts/vendor_playbook.sh EOF echo "Vendored snapshot -> $DEST_PREFIX" PROJECT_AGENTS_ROOT="$PROJECT_ROOT_ABS/.agents" PROJECT_AGENTS_INDEX="$PROJECT_AGENTS_ROOT/index.md" mkdir -p "$PROJECT_AGENTS_ROOT" if [ ! -f "$PROJECT_AGENTS_INDEX" ]; then cat >"$PROJECT_AGENTS_INDEX" <>"$PROJECT_AGENTS_INDEX" ;; cpp) printf '%s\n' "- .agents/cpp/:C++ 相关规则集(C++23,含 Modules)" >>"$PROJECT_AGENTS_INDEX" ;; python) printf '%s\n' "- .agents/python/:Python 相关规则集" >>"$PROJECT_AGENTS_INDEX" ;; markdown) printf '%s\n' "- .agents/markdown/:Markdown 相关规则集(仅代码格式化)" >>"$PROJECT_AGENTS_INDEX" ;; esac done cat >>"$PROJECT_AGENTS_INDEX" <<'EOF' 入口建议从: EOF for lang in "$@"; do printf '%s\n' "- .agents/$lang/index.md" >>"$PROJECT_AGENTS_INDEX" done cat >>"$PROJECT_AGENTS_INDEX" <<'EOF' 标准快照文档入口: - docs/standards/playbook/docs/index.md EOF fi SYNC_ROOT="$PROJECT_ROOT_ABS" sh "$DEST_PREFIX/scripts/sync_standards.sh" -langs "$langs_csv" # Apply templates to project root if requested if [ "$APPLY_TEMPLATES" -eq 1 ]; then echo "" echo "Applying templates to project root..." # Helper function: copy file if not exists copy_if_not_exists() { src_file="$1" dst_file="$2" if [ -f "$src_file" ]; then if [ -f "$dst_file" ]; then echo " Skip (exists): $(basename "$dst_file")" else cp "$src_file" "$dst_file" echo " Applied: $(basename "$dst_file")" fi fi } # Apply CI templates (Gitea workflows) CI_SRC="$DEST_PREFIX/templates/ci/gitea" if [ -d "$CI_SRC/.gitea" ]; then if [ -d "$PROJECT_ROOT_ABS/.gitea" ]; then echo " Skip (exists): .gitea/" else cp -R "$CI_SRC/.gitea" "$PROJECT_ROOT_ABS/" echo " Applied: .gitea/" fi fi # Apply lang-specific templates for lang in "$@"; do [ -n "$lang" ] || continue LANG_SRC="$DEST_PREFIX/templates/$lang" [ -d "$LANG_SRC" ] || continue case "$lang" in cpp) copy_if_not_exists "$LANG_SRC/.clang-format" "$PROJECT_ROOT_ABS/.clang-format" copy_if_not_exists "$LANG_SRC/.clangd" "$PROJECT_ROOT_ABS/.clangd" copy_if_not_exists "$LANG_SRC/CMakeLists.txt" "$PROJECT_ROOT_ABS/CMakeLists.txt" copy_if_not_exists "$LANG_SRC/CMakeUserPresets.json" "$PROJECT_ROOT_ABS/CMakeUserPresets.json" copy_if_not_exists "$LANG_SRC/conanfile.txt" "$PROJECT_ROOT_ABS/conanfile.txt" if [ -d "$LANG_SRC/conan" ] && [ ! -d "$PROJECT_ROOT_ABS/conan" ]; then cp -R "$LANG_SRC/conan" "$PROJECT_ROOT_ABS/" echo " Applied: conan/" elif [ -d "$PROJECT_ROOT_ABS/conan" ]; then echo " Skip (exists): conan/" fi ;; python) copy_if_not_exists "$LANG_SRC/.editorconfig" "$PROJECT_ROOT_ABS/.editorconfig" copy_if_not_exists "$LANG_SRC/.flake8" "$PROJECT_ROOT_ABS/.flake8" copy_if_not_exists "$LANG_SRC/.pre-commit-config.yaml" "$PROJECT_ROOT_ABS/.pre-commit-config.yaml" copy_if_not_exists "$LANG_SRC/.pylintrc" "$PROJECT_ROOT_ABS/.pylintrc" copy_if_not_exists "$LANG_SRC/pyproject.toml" "$PROJECT_ROOT_ABS/pyproject.toml" if [ -d "$LANG_SRC/.vscode" ] && [ ! -d "$PROJECT_ROOT_ABS/.vscode" ]; then cp -R "$LANG_SRC/.vscode" "$PROJECT_ROOT_ABS/" echo " Applied: .vscode/" elif [ -d "$PROJECT_ROOT_ABS/.vscode" ]; then echo " Skip (exists): .vscode/" fi ;; esac done echo "Templates applied." fi echo "Done."