Merge commit '35e6a301aa36e43df8458cb7c6ed40f60c642b78' into lsp-server
This commit is contained in:
commit
c33f9942a8
|
|
@ -6,8 +6,8 @@ SUPERPOWERS_BRANCH="${SUPERPOWERS_BRANCH:-thirdparty/skill}"
|
||||||
SUPERPOWERS_DIR="${SUPERPOWERS_DIR:-superpowers}"
|
SUPERPOWERS_DIR="${SUPERPOWERS_DIR:-superpowers}"
|
||||||
SUPERPOWERS_LIST="${SUPERPOWERS_LIST:-codex/skills/.sources/superpowers.list}"
|
SUPERPOWERS_LIST="${SUPERPOWERS_LIST:-codex/skills/.sources/superpowers.list}"
|
||||||
TARGET_BRANCH="${TARGET_BRANCH:-main}"
|
TARGET_BRANCH="${TARGET_BRANCH:-main}"
|
||||||
COMMIT_AUTHOR_NAME="${COMMIT_AUTHOR_NAME:-playbook-bot}"
|
COMMIT_AUTHOR_NAME="${COMMIT_AUTHOR_NAME:-ci-bot}"
|
||||||
COMMIT_AUTHOR_EMAIL="${COMMIT_AUTHOR_EMAIL:-playbook-bot@local}"
|
COMMIT_AUTHOR_EMAIL="${COMMIT_AUTHOR_EMAIL:-ci-bot@local}"
|
||||||
|
|
||||||
cd "$REPO_DIR"
|
cd "$REPO_DIR"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,159 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REPO_DIR="${REPO_DIR:-$(pwd)}"
|
||||||
|
TARGET_BRANCH="${TARGET_BRANCH:-thirdparty/skill}"
|
||||||
|
SNAPSHOT_DIR="${SNAPSHOT_DIR:-superpowers}"
|
||||||
|
SOURCE_FILE="${SOURCE_FILE:-${SNAPSHOT_DIR}/SOURCE.md}"
|
||||||
|
UPSTREAM_REPO="${UPSTREAM_REPO:-https://github.com/obra/superpowers.git}"
|
||||||
|
UPSTREAM_REF="${UPSTREAM_REF:-main}"
|
||||||
|
COMMIT_AUTHOR_NAME="${COMMIT_AUTHOR_NAME:-ci-bot}"
|
||||||
|
COMMIT_AUTHOR_EMAIL="${COMMIT_AUTHOR_EMAIL:-ci-bot@local}"
|
||||||
|
|
||||||
|
retry_cmd() {
|
||||||
|
local retries="$1"
|
||||||
|
shift
|
||||||
|
local delay="$1"
|
||||||
|
shift
|
||||||
|
|
||||||
|
local attempt=1
|
||||||
|
while true; do
|
||||||
|
if "$@"; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [ "$attempt" -ge "$retries" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
echo "Retry ($attempt/$retries): $*" >&2
|
||||||
|
sleep "$delay"
|
||||||
|
attempt=$((attempt + 1))
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
github_owner_repo() {
|
||||||
|
case "$1" in
|
||||||
|
https://github.com/*)
|
||||||
|
echo "$1" | sed -E 's#^https://github.com/([^/]+/[^/.]+)(\.git)?$#\1#'
|
||||||
|
;;
|
||||||
|
http://github.com/*)
|
||||||
|
echo "$1" | sed -E 's#^http://github.com/([^/]+/[^/.]+)(\.git)?$#\1#'
|
||||||
|
;;
|
||||||
|
git@github.com:*)
|
||||||
|
echo "$1" | sed -E 's#^git@github.com:([^/]+/[^/.]+)(\.git)?$#\1#'
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_latest_sha() {
|
||||||
|
local repo="$1"
|
||||||
|
local ref="$2"
|
||||||
|
local tmp_json="$3"
|
||||||
|
local gh_repo="$4"
|
||||||
|
local sha=""
|
||||||
|
|
||||||
|
# Prefer GitHub API when possible to avoid git+gnutls handshake failures.
|
||||||
|
if [ -n "$gh_repo" ]; then
|
||||||
|
local api_url="https://api.github.com/repos/${gh_repo}/commits/${ref}"
|
||||||
|
if retry_cmd 3 2 curl -fsSL --retry 3 --retry-delay 2 "$api_url" -o "$tmp_json"; then
|
||||||
|
sha="$(sed -n 's/^[[:space:]]*"sha":[[:space:]]*"\([0-9a-f]\{40\}\)".*/\1/p' "$tmp_json" | head -n 1)"
|
||||||
|
if [ -n "$sha" ]; then
|
||||||
|
echo "$sha"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Fallback to git transport.
|
||||||
|
sha="$(retry_cmd 3 2 git -c http.version=HTTP/1.1 ls-remote "$repo" "refs/heads/$ref" | awk 'NR==1 {print $1}')"
|
||||||
|
if [ -n "$sha" ]; then
|
||||||
|
echo "$sha"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
cd "$REPO_DIR"
|
||||||
|
|
||||||
|
git config user.name "$COMMIT_AUTHOR_NAME"
|
||||||
|
git config user.email "$COMMIT_AUTHOR_EMAIL"
|
||||||
|
|
||||||
|
git fetch origin "$TARGET_BRANCH"
|
||||||
|
git checkout -B "$TARGET_BRANCH" "origin/$TARGET_BRANCH"
|
||||||
|
|
||||||
|
tmp_dir="$(mktemp -d)"
|
||||||
|
cleanup() {
|
||||||
|
rm -rf "$tmp_dir"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
gh_repo=""
|
||||||
|
if gh_repo="$(github_owner_repo "$UPSTREAM_REPO" 2>/dev/null)"; then
|
||||||
|
:
|
||||||
|
fi
|
||||||
|
|
||||||
|
latest_sha="$(resolve_latest_sha "$UPSTREAM_REPO" "$UPSTREAM_REF" "$tmp_dir/latest.json" "$gh_repo" || true)"
|
||||||
|
if [ -z "$latest_sha" ]; then
|
||||||
|
echo "ERROR: failed to resolve upstream ref: $UPSTREAM_REPO $UPSTREAM_REF" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
current_sha=""
|
||||||
|
if [ -f "$SOURCE_FILE" ]; then
|
||||||
|
current_sha="$(sed -n 's/^- Ref:[[:space:]]*//p' "$SOURCE_FILE" | head -n 1)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$latest_sha" = "$current_sha" ]; then
|
||||||
|
echo "Third-party snapshot is up to date: $latest_sha"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -rf "$SNAPSHOT_DIR"
|
||||||
|
mkdir -p "$SNAPSHOT_DIR"
|
||||||
|
|
||||||
|
snapshot_loaded=0
|
||||||
|
|
||||||
|
if [ -n "$gh_repo" ]; then
|
||||||
|
tar_url="https://codeload.github.com/${gh_repo}/tar.gz/${latest_sha}"
|
||||||
|
if retry_cmd 3 2 curl -fsSL --retry 3 --retry-delay 2 "$tar_url" -o "$tmp_dir/upstream.tar.gz"; then
|
||||||
|
tar -xzf "$tmp_dir/upstream.tar.gz" -C "$SNAPSHOT_DIR" --strip-components=1
|
||||||
|
snapshot_loaded=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$snapshot_loaded" -eq 0 ]; then
|
||||||
|
upstream_dir="$tmp_dir/upstream"
|
||||||
|
git init "$upstream_dir" >/dev/null
|
||||||
|
git -C "$upstream_dir" remote add origin "$UPSTREAM_REPO"
|
||||||
|
retry_cmd 3 2 git -C "$upstream_dir" fetch --depth 1 origin "$latest_sha"
|
||||||
|
git -C "$upstream_dir" checkout --detach FETCH_HEAD
|
||||||
|
git -C "$upstream_dir" archive --format=tar HEAD | tar -xf - -C "$SNAPSHOT_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
snapshot_date="$(date -u +%Y-%m-%d)"
|
||||||
|
cat > "$SOURCE_FILE" <<EOF
|
||||||
|
# Source
|
||||||
|
|
||||||
|
- Repo: ${UPSTREAM_REPO%".git"}
|
||||||
|
- Ref: $latest_sha
|
||||||
|
- Snapshot: $snapshot_date
|
||||||
|
- Notes: vendored into playbook branch $TARGET_BRANCH
|
||||||
|
EOF
|
||||||
|
|
||||||
|
git add "$SNAPSHOT_DIR"
|
||||||
|
|
||||||
|
if git diff --cached --quiet; then
|
||||||
|
echo "No changes detected after snapshot refresh."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
git commit -m ":package: deps(superpowers): vendor snapshot"
|
||||||
|
|
||||||
|
TOKEN="${WORKFLOW:-}"
|
||||||
|
if [ -n "$TOKEN" ] && [ -n "${GITHUB_SERVER_URL:-}" ] && [ -n "${GITHUB_REPOSITORY:-}" ]; then
|
||||||
|
git remote set-url origin "https://oauth2:${TOKEN}@${GITHUB_SERVER_URL#https://}/${GITHUB_REPOSITORY}.git"
|
||||||
|
fi
|
||||||
|
|
||||||
|
git push origin "$TARGET_BRANCH"
|
||||||
|
|
@ -9,6 +9,7 @@ concurrency:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
WORKSPACE_DIR: "/home/workspace"
|
WORKSPACE_DIR: "/home/workspace"
|
||||||
|
TARGET_BRANCH: "main"
|
||||||
SUPERPOWERS_BRANCH: "thirdparty/skill"
|
SUPERPOWERS_BRANCH: "thirdparty/skill"
|
||||||
SUPERPOWERS_DIR: "superpowers"
|
SUPERPOWERS_DIR: "superpowers"
|
||||||
SUPERPOWERS_LIST: "codex/skills/.sources/superpowers.list"
|
SUPERPOWERS_LIST: "codex/skills/.sources/superpowers.list"
|
||||||
|
|
@ -51,18 +52,10 @@ jobs:
|
||||||
cd "$REPO_DIR"
|
cd "$REPO_DIR"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
TARGET_SHA="${{ github.sha }}"
|
# Always run sync from the latest target branch state.
|
||||||
TARGET_REF="${{ github.ref }}"
|
# This prevents stale/manual dispatch refs from using outdated scripts.
|
||||||
if git cat-file -e "$TARGET_SHA^{commit}" 2>/dev/null; then
|
git fetch origin "${{ env.TARGET_BRANCH }}"
|
||||||
git checkout -f "$TARGET_SHA"
|
git checkout -B "${{ env.TARGET_BRANCH }}" "origin/${{ env.TARGET_BRANCH }}"
|
||||||
else
|
|
||||||
if [ -n "$TARGET_REF" ]; then
|
|
||||||
git fetch origin "$TARGET_REF"
|
|
||||||
git checkout -f FETCH_HEAD
|
|
||||||
else
|
|
||||||
git checkout -f "${{ github.ref_name }}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
git config --global --add safe.directory "$REPO_DIR"
|
git config --global --add safe.directory "$REPO_DIR"
|
||||||
echo "REPO_DIR=$REPO_DIR" >> $GITHUB_ENV
|
echo "REPO_DIR=$REPO_DIR" >> $GITHUB_ENV
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
name: Update Third-party Superpowers
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: thirdparty-superpowers-update-${{ github.repository }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
WORKSPACE_DIR: "/home/workspace"
|
||||||
|
TARGET_BRANCH: "thirdparty/skill"
|
||||||
|
UPSTREAM_REPO: "https://github.com/obra/superpowers.git"
|
||||||
|
UPSTREAM_REF: "main"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update:
|
||||||
|
name: Update thirdparty/skill snapshot
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Prepare repo
|
||||||
|
run: |
|
||||||
|
echo "========================================"
|
||||||
|
echo "Prepare repo to WORKSPACE_DIR"
|
||||||
|
echo "========================================"
|
||||||
|
|
||||||
|
REPO_NAME="${{ github.event.repository.name }}"
|
||||||
|
REPO_DIR="${{ env.WORKSPACE_DIR }}/$REPO_NAME"
|
||||||
|
TOKEN="${{ secrets.WORKFLOW }}"
|
||||||
|
if [ -n "$TOKEN" ]; then
|
||||||
|
REPO_URL="https://oauth2:${TOKEN}@${GITHUB_SERVER_URL#https://}/${{ github.repository }}.git"
|
||||||
|
else
|
||||||
|
REPO_URL="${GITHUB_SERVER_URL}/${{ github.repository }}.git"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d "$REPO_DIR" ]; then
|
||||||
|
if [ -d "$REPO_DIR/.git" ]; then
|
||||||
|
cd "$REPO_DIR"
|
||||||
|
git clean -fdx
|
||||||
|
git reset --hard
|
||||||
|
git fetch --all --tags --force --prune --prune-tags
|
||||||
|
else
|
||||||
|
rm -rf "$REPO_DIR"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "$REPO_DIR/.git" ]; then
|
||||||
|
mkdir -p "${{ env.WORKSPACE_DIR }}"
|
||||||
|
git clone "$REPO_URL" "$REPO_DIR"
|
||||||
|
cd "$REPO_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
git fetch origin main
|
||||||
|
git checkout -B main origin/main
|
||||||
|
|
||||||
|
git config --global --add safe.directory "$REPO_DIR"
|
||||||
|
echo "REPO_DIR=$REPO_DIR" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Update thirdparty/skill snapshot
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
cd "$REPO_DIR"
|
||||||
|
bash .gitea/ci/update_thirdparty_superpowers.sh
|
||||||
|
|
@ -1,6 +1,13 @@
|
||||||
# playbook
|
# playbook
|
||||||
|
|
||||||
Playbook:TSL(`.tsl`/`.tsf`)+ C++ + Python + Markdown(代码格式化)工程规范与代理规则合集。
|
Playbook:工程规范与代理规则合集,当前覆盖:
|
||||||
|
|
||||||
|
- TSL(`.tsl`/`.tsf`)
|
||||||
|
- C++
|
||||||
|
- Python
|
||||||
|
- TypeScript(`.ts`/`.tsx`)
|
||||||
|
- JavaScript(`.js`/`.mjs`/`.cjs`)
|
||||||
|
- Markdown(代码格式化)
|
||||||
|
|
||||||
## 原则
|
## 原则
|
||||||
|
|
||||||
|
|
@ -10,14 +17,14 @@ Playbook:TSL(`.tsl`/`.tsf`)+ C++ + Python + Markdown(代码格式化)
|
||||||
|
|
||||||
## 适用范围
|
## 适用范围
|
||||||
|
|
||||||
- 本指南适用于所有 TSL/C++/Python/Markdown 相关仓库与脚本
|
- 本指南适用于所有 TSL/C++/Python/TypeScript/JavaScript/Markdown 相关仓库与脚本
|
||||||
- 当现有代码与本指南冲突时,**以保持局部一致性为优先**,逐步迁移
|
- 当现有代码与本指南冲突时,**以保持局部一致性为优先**,逐步迁移
|
||||||
|
|
||||||
## docs/(开发规范)
|
## docs/(开发规范)
|
||||||
|
|
||||||
`docs/` 目录是给开发者阅读的工程规范,约束代码写法、命名与提交信息。
|
`docs/` 目录是给开发者阅读的工程规范,约束代码写法、命名与提交信息。
|
||||||
|
|
||||||
- `docs/index.md`:文档导航(跨语言 common / TSL / C++ / Python / Markdown)。
|
- `docs/index.md`:文档导航(跨语言 common / TSL / C++ / Python / TypeScript / Markdown)。
|
||||||
- `docs/common/commit_message.md`:提交信息与版本号规范(type/scope/subject/body/footer、可选 Emoji 图例、SemVer)。
|
- `docs/common/commit_message.md`:提交信息与版本号规范(type/scope/subject/body/footer、可选 Emoji 图例、SemVer)。
|
||||||
- `docs/tsl/code_style.md`:TSL 代码结构、格式、`begin/end`
|
- `docs/tsl/code_style.md`:TSL 代码结构、格式、`begin/end`
|
||||||
代码块、注释与通用最佳实践。
|
代码块、注释与通用最佳实践。
|
||||||
|
|
@ -35,6 +42,10 @@ Playbook:TSL(`.tsl`/`.tsf`)+ C++ + Python + Markdown(代码格式化)
|
||||||
- `docs/python/configuration.md`:Python 配置清单(落地时从 `templates/python/`
|
- `docs/python/configuration.md`:Python 配置清单(落地时从 `templates/python/`
|
||||||
复制到项目根目录)。
|
复制到项目根目录)。
|
||||||
- `docs/markdown/index.md`:Markdown 代码块与行内代码格式(仅代码格式化)。
|
- `docs/markdown/index.md`:Markdown 代码块与行内代码格式(仅代码格式化)。
|
||||||
|
- `docs/typescript/code_style.md`:TypeScript 代码风格(Google 基线)。
|
||||||
|
- `docs/typescript/naming.md`:TypeScript 命名规范。
|
||||||
|
- `docs/typescript/toolchain.md`:TypeScript 工具链(typescript/prettier/eslint/vitest)。
|
||||||
|
- `docs/typescript/configuration.md`:TypeScript 配置清单(tsconfig/eslint/prettier)。
|
||||||
- `templates/cpp/`:C++ 落地模板(`.clang-format`、`conanfile.txt`、`CMakeUserPresets.json`、`CMakeLists.txt`)。
|
- `templates/cpp/`:C++ 落地模板(`.clang-format`、`conanfile.txt`、`CMakeUserPresets.json`、`CMakeLists.txt`)。
|
||||||
- `templates/python/`:Python 落地模板(`pyproject.toml`
|
- `templates/python/`:Python 落地模板(`pyproject.toml`
|
||||||
工具配置、`.flake8`、`.pylintrc`、`.pre-commit-config.yaml`、`.editorconfig`、`.vscode/settings.json`)。
|
工具配置、`.flake8`、`.pylintrc`、`.pre-commit-config.yaml`、`.editorconfig`、`.vscode/settings.json`)。
|
||||||
|
|
@ -124,6 +135,7 @@ Layer 3: docs/ (权威静态文档)
|
||||||
- `rulesets/tsl/index.md`:TSL 核心约定(44 行)
|
- `rulesets/tsl/index.md`:TSL 核心约定(44 行)
|
||||||
- `rulesets/cpp/index.md`:C++ 核心约定(47 行)
|
- `rulesets/cpp/index.md`:C++ 核心约定(47 行)
|
||||||
- `rulesets/python/index.md`:Python 核心约定(45 行)
|
- `rulesets/python/index.md`:Python 核心约定(45 行)
|
||||||
|
- `rulesets/typescript/index.md`:TypeScript 核心约定(47 行)
|
||||||
- `rulesets/markdown/index.md`:Markdown 核心约定(31 行,仅代码格式化)
|
- `rulesets/markdown/index.md`:Markdown 核心约定(31 行,仅代码格式化)
|
||||||
|
|
||||||
更多说明:`rulesets/index.md`
|
更多说明:`rulesets/index.md`
|
||||||
|
|
@ -287,16 +299,16 @@ python scripts/playbook.py -config playbook.toml
|
||||||
- 行尾与文本规范:`.gitattributes`
|
- 行尾与文本规范:`.gitattributes`
|
||||||
- 代理最低要求:`.agents/*`(工作原则、质量底线、安全边界)
|
- 代理最低要求:`.agents/*`(工作原则、质量底线、安全边界)
|
||||||
2. **语言级(Language-specific)规范**:只对某个语言成立的风格与工具。
|
2. **语言级(Language-specific)规范**:只对某个语言成立的风格与工具。
|
||||||
- 例如 TSL 的命名/文件顶层声明限制、C++ 的 `.clang-format/.clang-tidy`、Python 的 `ruff` 等。
|
- 例如 TSL 的命名/文件顶层声明限制、C++ 的 `.clang-format/.clang-tidy`、Python 的 `ruff`、TypeScript 的 ESLint/类型约束等。
|
||||||
|
|
||||||
**建议**:仓库级规则尽量少且稳定;语言级规则各自独立,避免互相"污染"。
|
**建议**:仓库级规则尽量少且稳定;语言级规则各自独立,避免互相"污染"。
|
||||||
|
|
||||||
本仓库提供多套代理规则集(同步后位于目标项目的 `.agents/tsl/` / `.agents/cpp/` / `.agents/python/` / `.agents/markdown/`):
|
本仓库提供多套代理规则集(同步后位于目标项目的 `.agents/tsl/` / `.agents/cpp/` / `.agents/python/` / `.agents/typescript/` / `.agents/markdown/`):
|
||||||
|
|
||||||
- 三者都包含核心约定与安全红线
|
- 三者都包含核心约定与安全红线
|
||||||
- 并在 `index.md` 中叠加语言级"硬约束"(TSL/TSF 语法限制、C++23/Modules、Python 风格、Markdown 代码格式化等)
|
- 并在 `index.md` 中叠加语言级"硬约束"(TSL/TSF 语法限制、C++23/Modules、Python 风格、TypeScript 类型约束、Markdown 代码格式化等)
|
||||||
|
|
||||||
**多语言项目推荐结构**(示例:TSL + C++ + Python + Markdown):
|
**多语言项目推荐结构**(示例:TSL + C++ + Python + TypeScript + Markdown):
|
||||||
|
|
||||||
```txt
|
```txt
|
||||||
.
|
.
|
||||||
|
|
@ -305,6 +317,7 @@ python scripts/playbook.py -config playbook.toml
|
||||||
│ ├── tsl/ # 由本 Playbook 同步(适用于 .tsl/.tsf)
|
│ ├── tsl/ # 由本 Playbook 同步(适用于 .tsl/.tsf)
|
||||||
│ ├── cpp/ # 由本 Playbook 同步(适用于 C++23/Modules)
|
│ ├── cpp/ # 由本 Playbook 同步(适用于 C++23/Modules)
|
||||||
│ ├── python/ # Python 规则集(同上)
|
│ ├── python/ # Python 规则集(同上)
|
||||||
|
│ ├── typescript/ # TypeScript/JavaScript 规则集(同上)
|
||||||
│ └── markdown/ # Markdown 规则集(仅代码格式化)
|
│ └── markdown/ # Markdown 规则集(仅代码格式化)
|
||||||
├── .gitattributes # 行尾/文本规范
|
├── .gitattributes # 行尾/文本规范
|
||||||
├── docs/
|
├── docs/
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@
|
||||||
并给出与本 Playbook(`docs/` + `rulesets/`)配套的技能编写建议与内置技能清单。
|
并给出与本 Playbook(`docs/` + `rulesets/`)配套的技能编写建议与内置技能清单。
|
||||||
|
|
||||||
> 提示:Codex skills 是“按用户安装”的(默认在
|
> 提示:Codex skills 是“按用户安装”的(默认在
|
||||||
> `~/.codex/skills`)。本仓库将 skills 以可分发的形式放在
|
> `~/.agents/skills`)。本仓库将 skills 以可分发的形式放在
|
||||||
> `codex/skills/`,并提供脚本一键安装到你的 `CODEX_HOME`。
|
> `codex/skills/`,并提供脚本一键安装到你的 `~/.agents`。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -38,14 +38,14 @@ codex/skills/
|
||||||
最终安装到本机后,对应路径为:
|
最终安装到本机后,对应路径为:
|
||||||
|
|
||||||
```txt
|
```txt
|
||||||
$CODEX_HOME/skills/<skill-name>/SKILL.md
|
~/.agents/skills/<skill-name>/SKILL.md
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. 安装到本机(推荐)
|
## 3. 安装到本机(推荐)
|
||||||
|
|
||||||
使用统一入口 `playbook.py` 安装 skills(会把 `codex/skills/*` 复制到 `$CODEX_HOME/skills/`):
|
使用统一入口 `playbook.py` 安装 skills(会把 `codex/skills/*` 复制到 `~/.agents/skills/`):
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
# playbook.toml
|
# playbook.toml
|
||||||
|
|
@ -54,7 +54,7 @@ project_root = "."
|
||||||
|
|
||||||
[install_skills]
|
[install_skills]
|
||||||
mode = "all" # list|all
|
mode = "all" # list|all
|
||||||
codex_home = "~/.codex"
|
agents_home = "~/.agents"
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -74,10 +74,10 @@ skills = ["style-cleanup", "commit-message"]
|
||||||
```toml
|
```toml
|
||||||
[install_skills]
|
[install_skills]
|
||||||
mode = "all"
|
mode = "all"
|
||||||
codex_home = "./.codex"
|
agents_home = "./.agents"
|
||||||
```
|
```
|
||||||
|
|
||||||
> 注意:Codex 只会从 `CODEX_HOME` 加载 skills;使用本地安装时,启动 Codex 需设置同样的 `CODEX_HOME`。
|
> 注意:Codex 默认从 `~/.agents/skills` 加载 skills;使用本地安装时,需要确保 Codex 能发现该路径。
|
||||||
|
|
||||||
如果你的项目通过 `git subtree` vendoring 本 Playbook(推荐前缀
|
如果你的项目通过 `git subtree` vendoring 本 Playbook(推荐前缀
|
||||||
`docs/standards/playbook`),则在目标项目里执行:
|
`docs/standards/playbook`),则在目标项目里执行:
|
||||||
|
|
@ -161,22 +161,29 @@ python docs/standards/playbook/scripts/playbook.py -config playbook.toml
|
||||||
来源:`codex/skills/.sources/superpowers.list`(第三方来源清单)。
|
来源:`codex/skills/.sources/superpowers.list`(第三方来源清单)。
|
||||||
本节仅列出 superpowers 体系 skills,与本 Playbook 原生 skills 分离。
|
本节仅列出 superpowers 体系 skills,与本 Playbook 原生 skills 分离。
|
||||||
|
|
||||||
<!-- superpowers:skills:start -->
|
### Third-party Skills (superpowers)
|
||||||
|
|
||||||
- brainstorming
|
### Third-party Skills (superpowers)
|
||||||
- dispatching-parallel-agents
|
|
||||||
- executing-plans
|
### Third-party Skills (superpowers)
|
||||||
- finishing-a-development-branch
|
|
||||||
- receiving-code-review
|
### Third-party Skills (superpowers)
|
||||||
- requesting-code-review
|
|
||||||
- subagent-driven-development
|
<!-- superpowers:skills:start -->
|
||||||
- systematic-debugging
|
- \
|
||||||
- test-driven-development
|
- \
|
||||||
- using-git-worktrees
|
- \
|
||||||
- using-superpowers
|
- \
|
||||||
- verification-before-completion
|
- \
|
||||||
- writing-plans
|
- \
|
||||||
- writing-skills
|
- \
|
||||||
|
- \
|
||||||
|
- \
|
||||||
|
- \
|
||||||
|
- \
|
||||||
|
- \
|
||||||
|
- \
|
||||||
|
- \
|
||||||
<!-- superpowers:skills:end -->
|
<!-- superpowers:skills:end -->
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,13 @@
|
||||||
- 工具链:`python/tooling.md`
|
- 工具链:`python/tooling.md`
|
||||||
- 配置清单:`python/configuration.md`
|
- 配置清单:`python/configuration.md`
|
||||||
|
|
||||||
|
## TypeScript(typescript)
|
||||||
|
|
||||||
|
- 代码风格:`typescript/code_style.md`
|
||||||
|
- 命名规范:`typescript/naming.md`
|
||||||
|
- 工具链:`typescript/toolchain.md`
|
||||||
|
- 配置清单:`typescript/configuration.md`
|
||||||
|
|
||||||
## Markdown(markdown)
|
## Markdown(markdown)
|
||||||
|
|
||||||
- 代码块与行内代码格式:`markdown/index.md`
|
- 代码块与行内代码格式:`markdown/index.md`
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
# TypeScript/JavaScript 代码风格
|
||||||
|
|
||||||
|
本 Playbook 的 TypeScript/JavaScript 代码风格以 Google TypeScript Style Guide 为基线:
|
||||||
|
|
||||||
|
- Google TypeScript Style Guide: https://google.github.io/styleguide/tsguide.html
|
||||||
|
- TypeScript 官方手册: https://www.typescriptlang.org/docs/handbook/
|
||||||
|
|
||||||
|
## 项目约定
|
||||||
|
|
||||||
|
- 适用范围:业务代码默认使用 `.ts/.tsx`;`.js/.mjs/.cjs` 仅用于脚本、工具链或配置
|
||||||
|
- 行宽:100(与 Prettier 配置保持一致)
|
||||||
|
- 缩进:2 空格
|
||||||
|
- 引号:单引号(Prettier `singleQuote: true`)
|
||||||
|
- 分号:不加(Prettier `semi: false`);如仓库已有配置则以仓库为准
|
||||||
|
- 尾随逗号:`all`(ES5+)
|
||||||
|
|
||||||
|
## 类型约定
|
||||||
|
|
||||||
|
- TypeScript 禁止 `any`;需要宽泛类型时用 `unknown` 并做类型收窄
|
||||||
|
- 优先使用 `interface` 描述对象结构;`type` 用于联合/交叉/工具类型
|
||||||
|
- 函数返回类型:公共 API 必须显式标注;内部实现可依赖推断
|
||||||
|
- 泛型参数名:单字母(`T`、`K`、`V`)或描述性名称(`TItem`)
|
||||||
|
- JavaScript 文件建议启用 `// @ts-check` 与 JSDoc 进行静态检查
|
||||||
|
|
||||||
|
## 模块约定
|
||||||
|
|
||||||
|
- 使用 ES Module(`import`/`export`);禁止 `require()`(除 `.cjs` 文件)
|
||||||
|
- 每文件一个主要导出;避免桶文件(`index.ts`/`index.js` re-export 过多)
|
||||||
|
- 路径别名:以 `tsconfig.json` 的 `paths` 为准
|
||||||
|
|
||||||
|
## 异步约定
|
||||||
|
|
||||||
|
- 优先 `async/await`;避免裸 `.then()/.catch()` 链
|
||||||
|
- `async` 函数必须处理错误(`try/catch` 或调用方捕获)
|
||||||
|
- 不得忽略 Promise(使用 `void` 操作符显式标记有意忽略)
|
||||||
|
|
||||||
|
当既有代码与本约定冲突时,优先保持局部一致性,逐步迁移。
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
# TypeScript 配置清单
|
||||||
|
|
||||||
|
本文件汇总 TypeScript 项目常用配置文件说明。
|
||||||
|
|
||||||
|
## 1) `tsconfig.json`
|
||||||
|
|
||||||
|
关键编译选项:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "NodeNext",
|
||||||
|
"moduleResolution": "NodeNext",
|
||||||
|
"strict": true,
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
"exactOptionalPropertyTypes": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "src"
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `strict: true`:启用全部严格检查(必须)
|
||||||
|
- `noUncheckedIndexedAccess`:数组/对象索引访问返回 `T | undefined`(推荐)
|
||||||
|
- `skipLibCheck`:跳过 `.d.ts` 检查,加快编译
|
||||||
|
|
||||||
|
## 2) `.prettierrc`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true,
|
||||||
|
"printWidth": 100,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"tabWidth": 2
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3) `eslint.config.js`(Flat Config)
|
||||||
|
|
||||||
|
```js
|
||||||
|
import tseslint from 'typescript-eslint'
|
||||||
|
|
||||||
|
export default tseslint.config(
|
||||||
|
...tseslint.configs.recommendedTypeChecked,
|
||||||
|
{
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: { projectService: true },
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/no-explicit-any': 'error',
|
||||||
|
'@typescript-eslint/no-floating-promises': 'error',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4) `.editorconfig`
|
||||||
|
|
||||||
|
```ini
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.{ts,tsx,js,jsx,json}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
max_line_length = 100
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5) `.vscode/settings.json`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"editor.rulers": [100],
|
||||||
|
"typescript.tsdk": "node_modules/typescript/lib"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
# TypeScript 命名规范
|
||||||
|
|
||||||
|
## 文件与目录
|
||||||
|
|
||||||
|
- 文件名:`kebab-case.ts` / `kebab-case.tsx` / `kebab-case.js` / `kebab-case.jsx`
|
||||||
|
- 脚本/配置文件:`kebab-case.mjs` / `kebab-case.cjs`
|
||||||
|
- 测试文件:`kebab-case.test.ts` / `kebab-case.spec.ts` / `kebab-case.test.js` / `kebab-case.spec.js`
|
||||||
|
- 类型声明文件:`kebab-case.d.ts`
|
||||||
|
- 目录名:`kebab-case/`
|
||||||
|
|
||||||
|
## 标识符
|
||||||
|
|
||||||
|
| 类别 | 风格 | 示例 |
|
||||||
|
| ----------------- | -------------------------- | ------------------------------ |
|
||||||
|
| 类(Class) | `PascalCase` | `UserService` |
|
||||||
|
| 接口(Interface) | `PascalCase` | `UserProfile`(不加 `I` 前缀) |
|
||||||
|
| 类型别名(Type) | `PascalCase` | `ApiResponse<T>` |
|
||||||
|
| 枚举(Enum) | `PascalCase` | `HttpStatus` |
|
||||||
|
| 枚举成员 | `UPPER_WITH_UNDER` | `NOT_FOUND` |
|
||||||
|
| 函数/方法 | `camelCase` | `getUserById` |
|
||||||
|
| 变量/参数 | `camelCase` | `userId` |
|
||||||
|
| 常量(模块级) | `UPPER_WITH_UNDER` | `MAX_RETRY_COUNT` |
|
||||||
|
| 私有成员 | `_camelCase` | `_cache` |
|
||||||
|
| 泛型参数 | 单字母或 `T` 前缀 | `T`、`TItem`、`K`、`V` |
|
||||||
|
| React 组件 | `PascalCase` | `UserCard` |
|
||||||
|
| React Hook | `camelCase`,以 `use` 开头 | `useUserData` |
|
||||||
|
|
||||||
|
## 布尔值命名
|
||||||
|
|
||||||
|
布尔变量/属性以 `is`、`has`、`can`、`should` 开头:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const isLoading = true
|
||||||
|
const hasPermission = false
|
||||||
|
```
|
||||||
|
|
||||||
|
## 避免
|
||||||
|
|
||||||
|
- 单字母变量(循环索引 `i`/`j` 除外)
|
||||||
|
- 缩写(`usr`、`btn`);优先完整单词
|
||||||
|
- 匈牙利命名法(`strName`、`nCount`)
|
||||||
|
- 接口名加 `I` 前缀(`IUserService`)
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
# TypeScript/JavaScript 工具链
|
||||||
|
|
||||||
|
本 Playbook 推荐以下工具保证代码一致性与质量:
|
||||||
|
|
||||||
|
- `typescript`:编译器(`tsc`)
|
||||||
|
- `prettier`:格式化
|
||||||
|
- `eslint`:风格检查与静态分析(配合 `@typescript-eslint`)
|
||||||
|
- `vitest` / `jest`:测试(按项目选择)
|
||||||
|
- `tsx` / `ts-node`:直接运行 `.ts` 文件(开发/脚本场景)
|
||||||
|
- `node`:运行 `.js/.mjs/.cjs` 脚本
|
||||||
|
|
||||||
|
## 常用命令(示例)
|
||||||
|
|
||||||
|
安装工具(按项目实际包管理器调整):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm add -D typescript prettier eslint typescript-eslint
|
||||||
|
```
|
||||||
|
|
||||||
|
类型检查:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tsc --noEmit
|
||||||
|
```
|
||||||
|
|
||||||
|
JavaScript 检查(可选,启用 `allowJs` + `checkJs`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tsc --noEmit --allowJs --checkJs
|
||||||
|
```
|
||||||
|
|
||||||
|
格式化:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
prettier --write .
|
||||||
|
```
|
||||||
|
|
||||||
|
Lint 检查:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
eslint .
|
||||||
|
```
|
||||||
|
|
||||||
|
运行测试:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
vitest run
|
||||||
|
# 或
|
||||||
|
jest --ci
|
||||||
|
```
|
||||||
|
|
||||||
|
## 包管理器
|
||||||
|
|
||||||
|
优先使用仓库已有的包管理器(`pnpm` / `npm` / `yarn`);未经沟通不切换。
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
[sync_rules]
|
[sync_rules]
|
||||||
# 同步 AGENT_RULES.md(配置节存在即启用)
|
# 同步 AGENT_RULES.md(配置节存在即启用)
|
||||||
# force = false # 可选:覆盖已有文件
|
# force = false # 可选:覆盖已有文件
|
||||||
|
# no_backup = false # 可选:跳过备份
|
||||||
|
|
||||||
[sync_memory_bank]
|
[sync_memory_bank]
|
||||||
# 同步 memory-bank/(配置节存在即启用)
|
# 同步 memory-bank/(配置节存在即启用)
|
||||||
|
|
@ -30,13 +31,14 @@
|
||||||
# no_backup = false # 可选:跳过备份
|
# no_backup = false # 可选:跳过备份
|
||||||
|
|
||||||
[sync_standards]
|
[sync_standards]
|
||||||
# langs = ["tsl", "cpp"] # 必填:要同步的语言
|
# langs = ["tsl", "cpp", "typescript"] # 必填:要同步的语言
|
||||||
# gitattr_mode = "append" # append(补全缺失)|overwrite(覆盖)|block(插入块)|skip(跳过)
|
# gitattr_mode = "append" # append(补全缺失)|overwrite(覆盖)|block(插入块)|skip(跳过)
|
||||||
|
# no_backup = false # 可选:跳过备份(.agents/.gitattributes)
|
||||||
|
|
||||||
[install_skills]
|
[install_skills]
|
||||||
# mode = "list" # list|all
|
# mode = "list" # list|all
|
||||||
# skills = ["brainstorming"] # mode=list 时必填
|
# skills = ["brainstorming"] # mode=list 时必填
|
||||||
# codex_home = "~/.codex" # 可选:默认 ~/.codex
|
# agents_home = "~/.agents" # 可选:默认 ~/.agents
|
||||||
|
|
||||||
[format_md]
|
[format_md]
|
||||||
# tool = "prettier" # 仅支持 prettier
|
# tool = "prettier" # 仅支持 prettier
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
- `rulesets/tsl/`:TSL 相关规则集(适用于 `.tsl`/`.tsf`)
|
- `rulesets/tsl/`:TSL 相关规则集(适用于 `.tsl`/`.tsf`)
|
||||||
- `rulesets/cpp/`:C++ 相关规则集(C++23,含 Modules)
|
- `rulesets/cpp/`:C++ 相关规则集(C++23,含 Modules)
|
||||||
- `rulesets/python/`:Python 相关规则集
|
- `rulesets/python/`:Python 相关规则集
|
||||||
|
- `rulesets/typescript/`:TypeScript/JavaScript 相关规则集
|
||||||
- `rulesets/markdown/`:Markdown 相关规则集(仅代码格式化)
|
- `rulesets/markdown/`:Markdown 相关规则集(仅代码格式化)
|
||||||
|
|
||||||
目标项目落地时,通过 `scripts/playbook.py` 的 `[sync_standards]`
|
目标项目落地时,通过 `scripts/playbook.py` 的 `[sync_standards]`
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
# TypeScript 代理规则集
|
||||||
|
|
||||||
|
本规则集定义 AI/自动化代理在处理 TypeScript/JavaScript 代码时必须遵守的核心约束。
|
||||||
|
|
||||||
|
## 范围与优先级
|
||||||
|
|
||||||
|
- 作为仓库级基线规则集使用;更靠近代码目录的规则更具体并可覆盖基线。
|
||||||
|
- 当代理规则与 docs 冲突:安全/合规优先,其次保持仓库一致性。
|
||||||
|
|
||||||
|
## 代理工作原则(铁律)
|
||||||
|
|
||||||
|
1. 先理解目标与上下文,再动手改代码
|
||||||
|
2. 修改要小而清晰;避免无关重构
|
||||||
|
3. 发现安全问题(明文密钥/鉴权漏洞)立即标注或修复
|
||||||
|
4. 不引入新依赖或工具,除非明确要求
|
||||||
|
|
||||||
|
## TypeScript/JavaScript 核心约定(不可违反)
|
||||||
|
|
||||||
|
- 语言标准:业务代码优先 TypeScript(`.ts/.tsx`);JavaScript(`.js/.mjs/.cjs`)仅用于脚本、配置或兼容场景
|
||||||
|
- 类型:禁止使用 `any`;优先 `unknown`;所有公共 API 必须有明确类型注解
|
||||||
|
- 代码风格:以仓库既有 ESLint/Prettier 配置为准;未经沟通不切换 formatter/linter
|
||||||
|
- Import:使用 ES Module(`import`/`export`);避免 `require()`;按 外部 → 内部 → 相对路径 分组
|
||||||
|
- 命名:文件 `kebab-case.ts/.js`;类/接口/类型 `PascalCase`;函数/变量 `camelCase`;常量 `UPPER_WITH_UNDER`;私有成员 `_camelCase`
|
||||||
|
- 异步:优先 `async/await`;避免裸 `.then()` 链;错误必须处理
|
||||||
|
- JavaScript 质量底线:必须通过 ESLint;推荐启用 `@ts-check` + JSDoc 做静态检查
|
||||||
|
|
||||||
|
## 安全红线(不可触碰)
|
||||||
|
|
||||||
|
- 不得在代码/日志/注释中写入明文密钥、密码、Token、API Key
|
||||||
|
- 不得使用 `eval()` / `new Function()` 处理不可信输入
|
||||||
|
- 不得直接拼接用户输入到 SQL/命令/HTML(防注入/XSS)
|
||||||
|
- 修改鉴权/权限逻辑必须说明动机与风险
|
||||||
|
|
||||||
|
## 权威来源
|
||||||
|
|
||||||
|
- 代码风格:`docs/typescript/code_style.md`
|
||||||
|
- 命名规范:`docs/typescript/naming.md`
|
||||||
|
- 工具链:`docs/typescript/toolchain.md`
|
||||||
|
- 配置清单:`docs/typescript/configuration.md`
|
||||||
|
|
||||||
|
## Skills(按需加载)
|
||||||
|
|
||||||
|
- `$commit-message`
|
||||||
|
|
||||||
|
## 与开发规范的关系
|
||||||
|
|
||||||
|
- 本仓库内:`docs/typescript/` 与 `docs/common/`
|
||||||
|
- 目标项目 subtree:`docs/standards/playbook/docs/typescript/` 与 `docs/standards/playbook/docs/common/`
|
||||||
|
|
@ -269,6 +269,16 @@ def write_docs_index(dest_prefix: Path, langs: list[str]) -> None:
|
||||||
"- 工具链:`python/tooling.md`",
|
"- 工具链:`python/tooling.md`",
|
||||||
"- 配置清单:`python/configuration.md`",
|
"- 配置清单:`python/configuration.md`",
|
||||||
]
|
]
|
||||||
|
elif lang == "typescript":
|
||||||
|
lines += [
|
||||||
|
"",
|
||||||
|
"## TypeScript(typescript)",
|
||||||
|
"",
|
||||||
|
"- 代码风格:`typescript/code_style.md`",
|
||||||
|
"- 命名规范:`typescript/naming.md`",
|
||||||
|
"- 工具链:`typescript/toolchain.md`",
|
||||||
|
"- 配置清单:`typescript/configuration.md`",
|
||||||
|
]
|
||||||
elif lang == "markdown":
|
elif lang == "markdown":
|
||||||
lines += [
|
lines += [
|
||||||
"",
|
"",
|
||||||
|
|
@ -832,6 +842,7 @@ def create_agents_index(agents_root: Path, langs: list[str], docs_prefix: str |
|
||||||
"- `.agents/tsl/`:TSL 相关规则集(由 playbook 同步;适用于 `.tsl`/`.tsf`)",
|
"- `.agents/tsl/`:TSL 相关规则集(由 playbook 同步;适用于 `.tsl`/`.tsf`)",
|
||||||
"- `.agents/cpp/`:C++ 相关规则集(由 playbook 同步;适用于 C++23/Modules)",
|
"- `.agents/cpp/`:C++ 相关规则集(由 playbook 同步;适用于 C++23/Modules)",
|
||||||
"- `.agents/python/`:Python 相关规则集(由 playbook 同步)",
|
"- `.agents/python/`:Python 相关规则集(由 playbook 同步)",
|
||||||
|
"- `.agents/typescript/`:TypeScript/JavaScript 相关规则集(由 playbook 同步)",
|
||||||
"- `.agents/markdown/`:Markdown 相关规则集(仅代码格式化)",
|
"- `.agents/markdown/`:Markdown 相关规则集(仅代码格式化)",
|
||||||
"",
|
"",
|
||||||
"规则发生冲突时,建议以“更靠近代码的目录规则更具体”为准。",
|
"规则发生冲突时,建议以“更靠近代码的目录规则更具体”为准。",
|
||||||
|
|
@ -856,6 +867,7 @@ def rewrite_agents_docs_links(agents_dir: Path, docs_prefix: str) -> None:
|
||||||
"`docs/tsl/": f"`{docs_prefix}/tsl/",
|
"`docs/tsl/": f"`{docs_prefix}/tsl/",
|
||||||
"`docs/cpp/": f"`{docs_prefix}/cpp/",
|
"`docs/cpp/": f"`{docs_prefix}/cpp/",
|
||||||
"`docs/python/": f"`{docs_prefix}/python/",
|
"`docs/python/": f"`{docs_prefix}/python/",
|
||||||
|
"`docs/typescript/": f"`{docs_prefix}/typescript/",
|
||||||
"`docs/markdown/": f"`{docs_prefix}/markdown/",
|
"`docs/markdown/": f"`{docs_prefix}/markdown/",
|
||||||
"`docs/common/": f"`{docs_prefix}/common/",
|
"`docs/common/": f"`{docs_prefix}/common/",
|
||||||
}
|
}
|
||||||
|
|
@ -1056,16 +1068,19 @@ def normalize_globs(raw: object) -> list[str]:
|
||||||
|
|
||||||
def install_skills_action(config: dict, context: dict) -> int:
|
def install_skills_action(config: dict, context: dict) -> int:
|
||||||
mode = str(config.get("mode", "list")).lower()
|
mode = str(config.get("mode", "list")).lower()
|
||||||
codex_home = Path(config.get("codex_home", "~/.codex")).expanduser()
|
if "codex_home" in config:
|
||||||
if not codex_home.is_absolute():
|
print("ERROR: codex_home is no longer supported; use agents_home", file=sys.stderr)
|
||||||
codex_home = (context["project_root"] / codex_home).resolve()
|
return 2
|
||||||
|
agents_home = Path(config.get("agents_home", "~/.agents")).expanduser()
|
||||||
|
if not agents_home.is_absolute():
|
||||||
|
agents_home = (context["project_root"] / agents_home).resolve()
|
||||||
|
|
||||||
skills_src_root = PLAYBOOK_ROOT / "codex/skills"
|
skills_src_root = PLAYBOOK_ROOT / "codex/skills"
|
||||||
if not skills_src_root.is_dir():
|
if not skills_src_root.is_dir():
|
||||||
print(f"ERROR: skills source not found: {skills_src_root}", file=sys.stderr)
|
print(f"ERROR: skills source not found: {skills_src_root}", file=sys.stderr)
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
skills_dst_root = codex_home / "skills"
|
skills_dst_root = agents_home / "skills"
|
||||||
ensure_dir(skills_dst_root)
|
ensure_dir(skills_dst_root)
|
||||||
|
|
||||||
if mode == "all":
|
if mode == "all":
|
||||||
|
|
|
||||||
|
|
@ -151,6 +151,15 @@
|
||||||
- **小步快跑**:每个 Plan 应该可快速完成
|
- **小步快跑**:每个 Plan 应该可快速完成
|
||||||
- **可验证**:每个 Plan 必须包含验证步骤
|
- **可验证**:每个 Plan 必须包含验证步骤
|
||||||
|
|
||||||
|
## 复利工程
|
||||||
|
|
||||||
|
每次 Session 结束时:
|
||||||
|
|
||||||
|
- **同一错误发生 2 次以上** → 立即更新 `AGENT_RULES.local.md` 或 `memory-bank/decisions.md`,避免下次重蹈
|
||||||
|
- **发现项目特有规律**(如特定模块的注意事项、常见陷阱)→ 沉淀到 `AGENT_RULES.local.md`
|
||||||
|
|
||||||
|
> 目标:让每次 Session 的起点比上次更高。
|
||||||
|
|
||||||
## 执行约束
|
## 执行约束
|
||||||
|
|
||||||
### 代码修改
|
### 代码修改
|
||||||
|
|
@ -180,6 +189,16 @@
|
||||||
- **避免循环**:避免重复调用同一工具获取相同信息
|
- **避免循环**:避免重复调用同一工具获取相同信息
|
||||||
- **优先专用工具**:文件操作用 Read/Edit/Write,搜索用 Grep/Glob
|
- **优先专用工具**:文件操作用 Read/Edit/Write,搜索用 Grep/Glob
|
||||||
|
|
||||||
|
## Context 管理
|
||||||
|
|
||||||
|
以下情况应建议用户**开启新 Session**:
|
||||||
|
|
||||||
|
- 当前方向明显跑偏,需要从头重新理解需求
|
||||||
|
- 讨论阶段产生了多个候选方案,进入执行阶段时应清空对话
|
||||||
|
- Session 过长导致注意力涣散,重复犯同类错误
|
||||||
|
|
||||||
|
> 新 Session 在干净 context 下工作效果更好;切换不是失败,是重置起点。
|
||||||
|
|
||||||
## 需要确认的场景
|
## 需要确认的场景
|
||||||
|
|
||||||
**常规模式**(可交互):
|
**常规模式**(可交互):
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,8 @@ templates/
|
||||||
│ │ └── agent-behavior.template.md
|
│ │ └── agent-behavior.template.md
|
||||||
│ ├── coding/
|
│ ├── coding/
|
||||||
│ │ ├── clarify.template.md
|
│ │ ├── clarify.template.md
|
||||||
│ │ └── review.template.md
|
│ │ ├── review.template.md
|
||||||
|
│ │ └── code-review.template.md
|
||||||
│ └── meta/
|
│ └── meta/
|
||||||
│ └── prompt-generator.template.md
|
│ └── prompt-generator.template.md
|
||||||
├── ci/ # CI 模板
|
├── ci/ # CI 模板
|
||||||
|
|
@ -214,6 +215,7 @@ project/
|
||||||
| `system/agent-behavior.template.md` | 工作模式参考 | 切换探索/开发/调试模式 |
|
| `system/agent-behavior.template.md` | 工作模式参考 | 切换探索/开发/调试模式 |
|
||||||
| `coding/clarify.template.md` | 需求澄清模板 | 需求不明确时 |
|
| `coding/clarify.template.md` | 需求澄清模板 | 需求不明确时 |
|
||||||
| `coding/review.template.md` | 复盘总结模板 | Plan 完成后复盘 |
|
| `coding/review.template.md` | 复盘总结模板 | Plan 完成后复盘 |
|
||||||
|
| `coding/code-review.template.md` | 代码评审流程 | 执行 MR/PR 代码评审 |
|
||||||
| `meta/prompt-generator.template.md` | 元提示词生成器 | 创建新的专用提示词 |
|
| `meta/prompt-generator.template.md` | 元提示词生成器 | 创建新的专用提示词 |
|
||||||
|
|
||||||
### AGENT_RULES.template.md
|
### AGENT_RULES.template.md
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,8 @@ prompts/
|
||||||
│ └── agent-behavior.md # 工作模式参考
|
│ └── agent-behavior.md # 工作模式参考
|
||||||
├── coding/
|
├── coding/
|
||||||
│ ├── clarify.md # 需求澄清模板
|
│ ├── clarify.md # 需求澄清模板
|
||||||
│ └── review.md # 复盘总结模板
|
│ ├── review.md # 复盘总结模板
|
||||||
|
│ └── code-review.md # MR/PR 代码评审流程
|
||||||
└── meta/
|
└── meta/
|
||||||
└── prompt-generator.md # 元提示词生成器
|
└── prompt-generator.md # 元提示词生成器
|
||||||
```
|
```
|
||||||
|
|
@ -23,6 +24,7 @@ prompts/
|
||||||
| **agent-behavior.md** | 切换工作模式(探索/开发/调试) |
|
| **agent-behavior.md** | 切换工作模式(探索/开发/调试) |
|
||||||
| **clarify.md** | 需求不明确时澄清 |
|
| **clarify.md** | 需求不明确时澄清 |
|
||||||
| **review.md** | Plan 完成后复盘总结 |
|
| **review.md** | Plan 完成后复盘总结 |
|
||||||
|
| **code-review.md** | 执行 MR/PR 代码评审 |
|
||||||
| **prompt-generator.md** | 创建新的专用提示词 |
|
| **prompt-generator.md** | 创建新的专用提示词 |
|
||||||
|
|
||||||
## 工作流程
|
## 工作流程
|
||||||
|
|
@ -36,6 +38,8 @@ prompts/
|
||||||
↓
|
↓
|
||||||
执行计划 → AGENT_RULES 主循环(留痕)
|
执行计划 → AGENT_RULES 主循环(留痕)
|
||||||
↓
|
↓
|
||||||
|
代码评审(有 MR/PR 时)→ code-review.md
|
||||||
|
↓
|
||||||
完成复盘 → review.md
|
完成复盘 → review.md
|
||||||
↓
|
↓
|
||||||
沉淀提示词 → prompt-generator.md(可选)
|
沉淀提示词 → prompt-generator.md(可选)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
# Code Review 流程
|
||||||
|
|
||||||
|
## 触发场景
|
||||||
|
|
||||||
|
收到 MR/PR 需要评审时。
|
||||||
|
|
||||||
|
## 准备
|
||||||
|
|
||||||
|
切换到对应分支并获取变更内容:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# GitLab
|
||||||
|
glab mr checkout <MR_ID>
|
||||||
|
glab mr view <MR_ID> | cat
|
||||||
|
glab mr diff <MR_ID> | cat
|
||||||
|
|
||||||
|
# GitHub
|
||||||
|
gh pr checkout <PR_NUMBER>
|
||||||
|
gh pr view <PR_NUMBER>
|
||||||
|
gh pr diff <PR_NUMBER>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 评审流程
|
||||||
|
|
||||||
|
逐步执行以下维度。改动简单时可跳过某些步骤。
|
||||||
|
|
||||||
|
### 1. 理解业务目标
|
||||||
|
|
||||||
|
- 能否理解本次改动的业务目标?
|
||||||
|
- 如果目标不明确,先确认再评审。
|
||||||
|
|
||||||
|
### 2. High-level Review
|
||||||
|
|
||||||
|
- 改动是否放在了合适的位置?
|
||||||
|
- 是否尽可能复用已有实现?
|
||||||
|
- 是否有破坏现有设计与逻辑的可能?
|
||||||
|
|
||||||
|
### 3. Bug 检查
|
||||||
|
|
||||||
|
- 是否隐含业务错误、逻辑纰漏或安全问题?
|
||||||
|
- **未修改**的相关联代码是否有遗漏?
|
||||||
|
|
||||||
|
### 4. 代码清晰度
|
||||||
|
|
||||||
|
- 逻辑是否简洁易懂?
|
||||||
|
- 命名是否清晰合理?
|
||||||
|
- 一年后再读,是否能轻松理解?
|
||||||
|
|
||||||
|
### 5. KISS 原则
|
||||||
|
|
||||||
|
- 是否有不必要的复杂度?
|
||||||
|
- 是否有未使用的定义、过多参数?
|
||||||
|
- 是否重复造轮子?
|
||||||
|
|
||||||
|
### 6. 单一职责
|
||||||
|
|
||||||
|
- 每个函数/类是否只做一件事?
|
||||||
|
- 文件/类/方法行数是否合理?
|
||||||
|
|
||||||
|
### 7. 测试覆盖
|
||||||
|
|
||||||
|
- 复杂业务逻辑(含 if/else/for)是否有测试?
|
||||||
|
- 测试是否有效(非空实现)?
|
||||||
|
- 不应过度测试无控制逻辑的代码。
|
||||||
|
|
||||||
|
## 输出
|
||||||
|
|
||||||
|
评审完成后,总结发现的**重点问题**,按严重性排列。
|
||||||
|
|
||||||
|
## AI 与人工的分工
|
||||||
|
|
||||||
|
| 维度 | 负责方 | 说明 |
|
||||||
|
| ---- | ------ | ---- |
|
||||||
|
| Bug、逻辑漏洞、安全问题 | **AI + 人工** | AI 负责初筛与证据收集,结论需人工复核 |
|
||||||
|
| 代码清晰度、KISS、单一职责 | **AI + 人工** | AI 提供候选问题,人工决定是否采纳 |
|
||||||
|
| 架构合理性、业务对齐 | **人工** | AI 反馈少且准确率低,需人工把关 |
|
||||||
|
| 兼容性、历史债务、战略取舍 | **人工** | 依赖背景知识,AI 难以判断 |
|
||||||
|
|
||||||
|
> 规则:AI 结论必须附文件路径、行号或可复现依据;缺少证据时按待确认假设处理。
|
||||||
|
>
|
||||||
|
> 注意:评审不只看 diff,需结合代码库整体上下文做评估。
|
||||||
|
|
@ -11,10 +11,12 @@ tests/
|
||||||
│ └── test_playbook_cli.py # playbook.py 基础功能测试
|
│ └── test_playbook_cli.py # playbook.py 基础功能测试
|
||||||
├── test_format_md_action.py # format_md 动作测试
|
├── test_format_md_action.py # format_md 动作测试
|
||||||
├── test_gitattributes_modes.py # gitattr_mode 行为测试
|
├── test_gitattributes_modes.py # gitattr_mode 行为测试
|
||||||
|
├── test_no_backup_flags.py # no_backup 行为测试
|
||||||
├── test_sync_directory_actions.py # sync_memory_bank/sync_prompts 行为测试
|
├── test_sync_directory_actions.py # sync_memory_bank/sync_prompts 行为测试
|
||||||
├── test_vendor_snapshot_templates.py # vendor 快照模板完整性测试
|
├── test_vendor_snapshot_templates.py # vendor 快照模板完整性测试
|
||||||
├── test_plan_progress_cli.py # plan_progress CLI 测试
|
├── test_plan_progress_cli.py # plan_progress CLI 测试
|
||||||
├── test_superpowers_list_sync.py # superpowers 列表一致性测试
|
├── test_superpowers_list_sync.py # superpowers 列表一致性测试
|
||||||
|
├── test_superpowers_workflows.py # superpowers 工作流配置校验
|
||||||
├── test_sync_templates_placeholders.py # 占位符替换测试(sync_rules/sync_standards)
|
├── test_sync_templates_placeholders.py # 占位符替换测试(sync_rules/sync_standards)
|
||||||
├── test_toml_edge_cases.py # TOML 解析边界测试
|
├── test_toml_edge_cases.py # TOML 解析边界测试
|
||||||
├── templates/ # 模板验证测试
|
├── templates/ # 模板验证测试
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,27 @@ langs = ["tsl"]
|
||||||
self.assertEqual(block[bullet_idx - 1], "")
|
self.assertEqual(block[bullet_idx - 1], "")
|
||||||
|
|
||||||
def test_install_skills(self):
|
def test_install_skills(self):
|
||||||
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||||
|
target = Path(tmp_dir) / "agents"
|
||||||
|
config_body = f"""
|
||||||
|
[playbook]
|
||||||
|
project_root = "{tmp_dir}"
|
||||||
|
|
||||||
|
[install_skills]
|
||||||
|
agents_home = "{target}"
|
||||||
|
mode = "list"
|
||||||
|
skills = ["brainstorming"]
|
||||||
|
"""
|
||||||
|
config_path = Path(tmp_dir) / "playbook.toml"
|
||||||
|
config_path.write_text(config_body, encoding="utf-8")
|
||||||
|
|
||||||
|
result = run_cli("-config", str(config_path))
|
||||||
|
|
||||||
|
skill_file = target / "skills/brainstorming/SKILL.md"
|
||||||
|
self.assertEqual(result.returncode, 0)
|
||||||
|
self.assertTrue(skill_file.is_file())
|
||||||
|
|
||||||
|
def test_install_skills_rejects_codex_home(self):
|
||||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||||
target = Path(tmp_dir) / "codex"
|
target = Path(tmp_dir) / "codex"
|
||||||
config_body = f"""
|
config_body = f"""
|
||||||
|
|
@ -142,9 +163,8 @@ skills = ["brainstorming"]
|
||||||
|
|
||||||
result = run_cli("-config", str(config_path))
|
result = run_cli("-config", str(config_path))
|
||||||
|
|
||||||
skill_file = target / "skills/brainstorming/SKILL.md"
|
self.assertNotEqual(result.returncode, 0)
|
||||||
self.assertEqual(result.returncode, 0)
|
self.assertIn("codex_home", result.stdout + result.stderr)
|
||||||
self.assertTrue(skill_file.is_file())
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,8 @@ VALID_LINKS=0
|
||||||
BROKEN_LINKS=0
|
BROKEN_LINKS=0
|
||||||
SKIPPED_LINKS=0
|
SKIPPED_LINKS=0
|
||||||
|
|
||||||
BROKEN_LINKS_FILE="/tmp/broken_links.txt"
|
BROKEN_LINKS_FILE="$(mktemp "${TMPDIR:-/tmp}/broken_links.XXXXXX")"
|
||||||
REPORT_FILE="/tmp/doc_links_report.txt"
|
REPORT_FILE="$(mktemp "${TMPDIR:-/tmp}/doc_links_report.XXXXXX")"
|
||||||
|
|
||||||
> "$BROKEN_LINKS_FILE"
|
|
||||||
> "$REPORT_FILE"
|
|
||||||
|
|
||||||
echo "📁 Playbook 根目录: $PLAYBOOK_ROOT"
|
echo "📁 Playbook 根目录: $PLAYBOOK_ROOT"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ validate_file_exists "$PROMPTS_DIR/README.md" "prompts/README.md"
|
||||||
validate_file_exists "$PROMPTS_DIR/system/agent-behavior.template.md" "prompts/system/agent-behavior.template.md"
|
validate_file_exists "$PROMPTS_DIR/system/agent-behavior.template.md" "prompts/system/agent-behavior.template.md"
|
||||||
validate_file_exists "$PROMPTS_DIR/coding/clarify.template.md" "prompts/coding/clarify.template.md"
|
validate_file_exists "$PROMPTS_DIR/coding/clarify.template.md" "prompts/coding/clarify.template.md"
|
||||||
validate_file_exists "$PROMPTS_DIR/coding/review.template.md" "prompts/coding/review.template.md"
|
validate_file_exists "$PROMPTS_DIR/coding/review.template.md" "prompts/coding/review.template.md"
|
||||||
|
validate_file_exists "$PROMPTS_DIR/coding/code-review.template.md" "prompts/coding/code-review.template.md"
|
||||||
validate_file_exists "$PROMPTS_DIR/meta/prompt-generator.template.md" "prompts/meta/prompt-generator.template.md"
|
validate_file_exists "$PROMPTS_DIR/meta/prompt-generator.template.md" "prompts/meta/prompt-generator.template.md"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
import unittest
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
README = ROOT / "README.md"
|
||||||
|
|
||||||
|
|
||||||
|
class ReadmeLanguageListsTests(unittest.TestCase):
|
||||||
|
def test_rulesets_list_includes_typescript(self):
|
||||||
|
text = README.read_text(encoding="utf-8")
|
||||||
|
self.assertIn("- `rulesets/typescript/index.md`:TypeScript 核心约定", text)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
import unittest
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
SYNC_WORKFLOW = ROOT / ".gitea" / "workflows" / "sync-superpowers.yml"
|
||||||
|
AUTO_UPDATE_WORKFLOW = ROOT / ".gitea" / "workflows" / "update-thirdparty-superpowers.yml"
|
||||||
|
AUTO_UPDATE_SCRIPT = ROOT / ".gitea" / "ci" / "update_thirdparty_superpowers.sh"
|
||||||
|
SYNC_SCRIPT = ROOT / ".gitea" / "ci" / "sync_superpowers.sh"
|
||||||
|
|
||||||
|
|
||||||
|
class SuperpowersWorkflowTests(unittest.TestCase):
|
||||||
|
def test_sync_workflow_uses_manual_trigger(self):
|
||||||
|
text = SYNC_WORKFLOW.read_text(encoding="utf-8")
|
||||||
|
self.assertIn("workflow_dispatch:", text)
|
||||||
|
|
||||||
|
def test_sync_workflow_runs_from_latest_main(self):
|
||||||
|
text = SYNC_WORKFLOW.read_text(encoding="utf-8")
|
||||||
|
self.assertIn('TARGET_BRANCH: "main"', text)
|
||||||
|
self.assertIn('git fetch origin "${{ env.TARGET_BRANCH }}"', text)
|
||||||
|
self.assertIn(
|
||||||
|
'git checkout -B "${{ env.TARGET_BRANCH }}" "origin/${{ env.TARGET_BRANCH }}"',
|
||||||
|
text,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_auto_update_workflow_triggers_on_main_push(self):
|
||||||
|
text = AUTO_UPDATE_WORKFLOW.read_text(encoding="utf-8")
|
||||||
|
self.assertIn("push:", text)
|
||||||
|
self.assertIn("- main", text)
|
||||||
|
self.assertIn("workflow_dispatch:", text)
|
||||||
|
|
||||||
|
def test_auto_update_workflow_runs_update_script(self):
|
||||||
|
text = AUTO_UPDATE_WORKFLOW.read_text(encoding="utf-8")
|
||||||
|
self.assertIn("bash .gitea/ci/update_thirdparty_superpowers.sh", text)
|
||||||
|
|
||||||
|
def test_auto_update_script_targets_thirdparty_branch(self):
|
||||||
|
text = AUTO_UPDATE_SCRIPT.read_text(encoding="utf-8")
|
||||||
|
self.assertIn('TARGET_BRANCH="${TARGET_BRANCH:-thirdparty/skill}"', text)
|
||||||
|
self.assertIn("api.github.com/repos", text)
|
||||||
|
self.assertIn("ls-remote", text)
|
||||||
|
self.assertIn('git checkout -B "$TARGET_BRANCH" "origin/$TARGET_BRANCH"', text)
|
||||||
|
|
||||||
|
def test_ci_scripts_use_ci_bot_identity(self):
|
||||||
|
sync_text = SYNC_SCRIPT.read_text(encoding="utf-8")
|
||||||
|
update_text = AUTO_UPDATE_SCRIPT.read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
self.assertIn('COMMIT_AUTHOR_NAME="${COMMIT_AUTHOR_NAME:-ci-bot}"', sync_text)
|
||||||
|
self.assertIn('COMMIT_AUTHOR_EMAIL="${COMMIT_AUTHOR_EMAIL:-ci-bot@local}"', sync_text)
|
||||||
|
self.assertIn('COMMIT_AUTHOR_NAME="${COMMIT_AUTHOR_NAME:-ci-bot}"', update_text)
|
||||||
|
self.assertIn(
|
||||||
|
'COMMIT_AUTHOR_EMAIL="${COMMIT_AUTHOR_EMAIL:-ci-bot@local}"',
|
||||||
|
update_text,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
|
|
@ -16,6 +16,15 @@ def run_cli(*args):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def run_script(script_path: Path, *args, cwd: Path | None = None):
|
||||||
|
return subprocess.run(
|
||||||
|
[sys.executable, str(script_path), *args],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
cwd=str(cwd) if cwd else None,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class SyncTemplatesPlaceholdersTests(unittest.TestCase):
|
class SyncTemplatesPlaceholdersTests(unittest.TestCase):
|
||||||
def test_main_language_placeholder_replaced(self):
|
def test_main_language_placeholder_replaced(self):
|
||||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||||
|
|
@ -44,6 +53,49 @@ langs = [\"cpp\", \"tsl\"]
|
||||||
self.assertIn("docs/standards/playbook/scripts/plan_progress.py", rules_text)
|
self.assertIn("docs/standards/playbook/scripts/plan_progress.py", rules_text)
|
||||||
self.assertNotIn("{{PLAYBOOK_SCRIPTS}}", rules_text)
|
self.assertNotIn("{{PLAYBOOK_SCRIPTS}}", rules_text)
|
||||||
|
|
||||||
|
def test_sync_standards_rewrites_typescript_docs_prefix_for_vendored_playbook(self):
|
||||||
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||||
|
root = Path(tmp_dir)
|
||||||
|
vendor_config = root / "vendor.toml"
|
||||||
|
vendor_config.write_text(
|
||||||
|
f"""
|
||||||
|
[playbook]
|
||||||
|
project_root = "{tmp_dir}"
|
||||||
|
|
||||||
|
[vendor]
|
||||||
|
langs = ["typescript"]
|
||||||
|
""",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
vendor_result = run_cli("-config", str(vendor_config))
|
||||||
|
self.assertEqual(vendor_result.returncode, 0, msg=vendor_result.stderr)
|
||||||
|
|
||||||
|
sync_config = root / "sync.toml"
|
||||||
|
sync_config.write_text(
|
||||||
|
f"""
|
||||||
|
[playbook]
|
||||||
|
project_root = "{tmp_dir}"
|
||||||
|
|
||||||
|
[sync_standards]
|
||||||
|
langs = ["typescript"]
|
||||||
|
""",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
vendored_script = (
|
||||||
|
root / "docs" / "standards" / "playbook" / "scripts" / "playbook.py"
|
||||||
|
)
|
||||||
|
sync_result = run_script(
|
||||||
|
vendored_script, "-config", str(sync_config), cwd=root
|
||||||
|
)
|
||||||
|
self.assertEqual(sync_result.returncode, 0, msg=sync_result.stderr)
|
||||||
|
|
||||||
|
agents_index = root / ".agents" / "typescript" / "index.md"
|
||||||
|
text = agents_index.read_text(encoding="utf-8")
|
||||||
|
self.assertIn("`docs/standards/playbook/docs/typescript/", text)
|
||||||
|
self.assertNotIn("`docs/typescript/", text)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue