diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a1e3ea5..6abab06 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,8 +17,8 @@ jobs: python: ["3.11", "3.13"] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@v7 + - uses: actions/setup-python@v7 with: python-version: ${{ matrix.python }} - name: Tests @@ -33,7 +33,7 @@ jobs: runs-on: ubuntu-latest container: ${{ matrix.image }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Python without git (debian ships none) run: | apt-get update -qq diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 44eb29e..1a73f39 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,11 +23,11 @@ jobs: env: TAG: ${{ github.event.inputs.tag || github.ref_name }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: ref: ${{ env.TAG }} - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v7 with: python-version: "3.12" @@ -54,7 +54,7 @@ jobs: gh release create "$TAG" dist/* --title "aisync ${TAG#v}" --generate-notes fi - - uses: docker/login-action@v3 + - uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.actor }} diff --git a/README.md b/README.md index b43a831..a8890b6 100644 --- a/README.md +++ b/README.md @@ -63,19 +63,19 @@ aisync also does the rules part: one rules block copied into every tool's own ru ## Use it ```bash -aisync demo # a throwaway hub with a synthetic two-week journal -aisync init # create ~/.ai-sync (override with AISYNC_HOME) -aisync install-hooks # shows a diff of every tool config it would change, then asks -aisync watch & # event-driven sync loop (or run `aisync sync` by hand) +aisync demo # a throwaway hub with a synthetic two-week journal +aisync init # create ~/.ai-sync (or wherever AISYNC_HOME points) +aisync install-hooks # shows every tool config change as a diff, then asks +aisync watch & # event-driven sync loop (or run `aisync sync` by hand) ``` What an agent does during a session: ```bash -aisync claim -a codex -p checkout-api -f app/payments/ -m "SDK upgrade" # exit 3 = someone holds it -aisync log -a codex -p checkout-api -k dev_log -m "SDK 5.0, retries kept" -f app/payments/provider.py -aisync rule add -a codex "every API change ships with a contract test" --reason "two schema breaks" -aisync release -a codex -p checkout-api +aisync claim -a codex -p api -f app/payments/ -m "SDK upgrade" # 3 = taken +aisync log -a codex -p api -m "SDK 5.0, retries kept" +aisync rule add -a codex "every API change ships a contract test" +aisync release -a codex -p api ``` | Command | What it does | @@ -150,7 +150,7 @@ aisync is an independent project, not affiliated with the projects above. ## Development ```bash -python3 -m unittest discover -s tests # includes build wheel -> fresh venv -> aisync demo + verify +python3 -m unittest discover -s tests # includes wheel -> venv -> demo ``` The images are generated: the terminal GIF is a recorded real run of diff --git a/README.zh-CN.md b/README.zh-CN.md index 8ed815a..a1cbb11 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -62,19 +62,19 @@ curl -fsSL https://maxont.github.io/aisync/install.sh | bash ## 用法 ```bash -aisync demo # 一个临时 hub,带两周的合成日志 -aisync init # 创建 ~/.ai-sync(可用 AISYNC_HOME 改位置) -aisync install-hooks # 先显示要改的每个工具配置的 diff,再问你 -aisync watch & # 事件驱动的同步循环(或手动跑 aisync sync) +aisync demo # 一个临时 hub,带两周的合成日志 +aisync init # 创建 ~/.ai-sync(可用 AISYNC_HOME 改位置) +aisync install-hooks # 先用 diff 列出要改的每个工具配置,再问你 +aisync watch & # 事件驱动的同步循环(或手动跑 aisync sync) ``` agent 在一次会话里会这样用: ```bash -aisync claim -a codex -p checkout-api -f app/payments/ -m "SDK upgrade" # 退出码 3 = 有人占着 -aisync log -a codex -p checkout-api -k dev_log -m "SDK 5.0, retries kept" -f app/payments/provider.py -aisync rule add -a codex "every API change ships with a contract test" --reason "two schema breaks" -aisync release -a codex -p checkout-api +aisync claim -a codex -p api -f app/payments/ -m "升级 SDK" # 3 = 有人占着 +aisync log -a codex -p api -m "SDK 5.0,保留重试" +aisync rule add -a codex "每个 API 改动都要带契约测试" +aisync release -a codex -p api ``` | 命令 | 作用 | @@ -145,7 +145,7 @@ aisync 是独立项目,与上述项目没有隶属关系。 ## 开发 ```bash -python3 -m unittest discover -s tests # 包括 构建 wheel -> 全新 venv -> aisync demo + verify +python3 -m unittest discover -s tests # 含 wheel -> 全新 venv -> demo ``` 这些图片都是生成的:终端 GIF 录的是 [`.github/refine/demo-terminal.zh.json`](.github/refine/demo-terminal.zh.json) diff --git a/docs/assets/demo.gif b/docs/assets/demo.gif index 44918db..6d5a871 100644 Binary files a/docs/assets/demo.gif and b/docs/assets/demo.gif differ diff --git a/docs/assets/demo.zh.gif b/docs/assets/demo.zh.gif index c12c7b7..0cadf19 100644 Binary files a/docs/assets/demo.zh.gif and b/docs/assets/demo.zh.gif differ diff --git a/docs/install/index.html b/docs/install/index.html index 5493b05..bfb27c9 100644 --- a/docs/install/index.html +++ b/docs/install/index.html @@ -79,7 +79,7 @@

3. Ask before you touch anything

Read the output to the user. After they agree:

bash /tmp/aisync-install.sh
 aisync --version
-

If aisync is not on PATH, do not edit shell config on your own. Tell the user which command fixes it (uv tool update-shell, pipx ensurepath) and ask. Until then python3 -m ai_sync is equivalent.

+

If aisync is not on PATH, do not edit shell config on your own. Tell the user which command fixes it (uv tool update-shell, pipx ensurepath) and ask. Until then, run it by the full path the script printed (for example ~/.local/bin/aisync).

4. First run

Show the user what a hub looks like. This writes only to a new temp dir:

diff --git a/tests/test_site.py b/tests/test_site.py index d0bb82f..740fcf5 100644 --- a/tests/test_site.py +++ b/tests/test_site.py @@ -50,6 +50,13 @@ def test_english_and_chinese_images_never_mix(self): index = (ROOT / "docs/index.html").read_text() self.assertFalse(re.findall(r'src="[^"]*\.zh\.[^"]*"', index)) + def test_code_lines_fit_the_github_readme_width(self): + wide = lambda line: sum(2 if "\u3000" <= c <= "\u9fff" or "\uff00" <= c <= "\uffef" else 1 for c in line) + for name, text in (("README.md", README), ("README.zh-CN.md", README_ZH)): + for block in re.findall(r"```(?:bash|text)\n(.*?)```", text, re.S): + for line in block.splitlines(): + self.assertLessEqual(wide(line), 80, f"{name}: {line}") + def test_no_endorsement_claims(self): banned = re.compile(r"\b(endorsed|endorsement|official|officially|approved by|partner(ed|ship)?|sponsored|" r"backed by|certified)\b", re.I) @@ -85,6 +92,7 @@ def test_install_page_hands_off_to_the_ai_safely(self): for must in ("For humans", "FOR YOUR AI", "Capability gate", "--dry-run", "shell config", "Be honest about failure", "Report back", "git --version"): self.assertIn(must, page) + self.assertNotIn("python3 -m ai_sync", page) # not true after a uv, pipx or venv install def test_demo_board_page_is_labelled_synthetic(self): page = (ROOT / "docs/board/index.html").read_text()