chore: bump version and changelog (v1.32.1.0)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
yihui 2026-07-11 14:31:38 +08:00
parent 74895062fb
commit e3d066efbb
4 changed files with 72 additions and 1 deletions

View File

@ -1,5 +1,22 @@
# Changelog
## [1.32.1.0] - 2026-07-11
## **Cleanup stale flat Codex skill directories on install.**
The `setup` script now detects and removes stale flat Claude-format skill directories (from pre-prefixed older Codex installs) that can shadow the generated `gstack-*` Codex skills and still point at `~/.claude/skills/gstack` runtime paths. New test coverage added for the cleanup logic.
### The numbers that matter
Diff against `main` at v1.32.0.0.
| Metric | v1.32.0.0 | v1.32.1.0 | Δ |
|---|---|---|---|
| Files changed | — | 2 | — |
| Tests added | — | 1 | — |
---
## [1.32.0.0] - 2026-05-10
## **Seven contributor PRs land. Three are security or hardening.**

View File

@ -1 +1 @@
1.32.0.0
1.32.1.0

35
setup
View File

@ -530,6 +530,39 @@ link_codex_skill_dirs() {
fi
}
# ─── Helper: remove stale flat Codex skills from pre-prefixed installs ───────
# Older Codex setup versions installed Claude-format skills as flat directories
# like ~/.codex/skills/ship. Those stale entries can shadow the generated
# gstack-ship skill and still point at ~/.claude/skills/gstack runtime paths.
cleanup_stale_codex_skill_dirs() {
local gstack_dir="$1"
local skills_dir="$2"
local removed=()
for skill_dir in "$gstack_dir"/*/; do
if [ -f "$skill_dir/SKILL.md" ]; then
skill_name="$(basename "$skill_dir")"
[ "$skill_name" = "node_modules" ] && continue
case "$skill_name" in gstack-*) continue ;; esac
stale_target="$skills_dir/$skill_name"
stale_skill="$stale_target/SKILL.md"
[ -d "$stale_target" ] || continue
[ -f "$stale_skill" ] || continue
[ -L "$stale_skill" ] && continue
if grep -q 'AUTO-GENERATED from SKILL.md.tmpl' "$stale_skill" 2>/dev/null \
&& grep -q '~/.claude/skills/gstack' "$stale_skill" 2>/dev/null; then
rm -rf "$stale_target"
removed+=("$skill_name")
fi
fi
done
if [ ${#removed[@]} -gt 0 ]; then
echo " cleaned up stale codex entries: ${removed[*]}"
fi
}
# ─── Helper: create .agents/skills/gstack/ sidecar symlinks ──────────
# Codex/Gemini/Cursor read skills from .agents/skills/. We link runtime
# assets (bin/, browse/dist/, review/, qa/, etc.) so skill templates can
@ -851,6 +884,8 @@ if [ "$INSTALL_CODEX" -eq 1 ]; then
if [ "$CODEX_REPO_LOCAL" -eq 0 ]; then
create_codex_runtime_root "$SOURCE_GSTACK_DIR" "$CODEX_GSTACK"
fi
# Remove stale flat Claude-format gstack skills before linking gstack-* Codex skills.
cleanup_stale_codex_skill_dirs "$SOURCE_GSTACK_DIR" "$CODEX_SKILLS"
# Install generated Codex-format skills (not Claude source dirs)
link_codex_skill_dirs "$SOURCE_GSTACK_DIR" "$CODEX_SKILLS"

View File

@ -2290,6 +2290,25 @@ describe('setup script validation', () => {
expect(fnBody).toContain('[ "$skill_name" = "gstack" ] && continue');
});
test('Codex install cleans stale flat gstack-generated skill directories', () => {
expect(setupContent).toContain('cleanup_stale_codex_skill_dirs');
const fnStart = setupContent.indexOf('cleanup_stale_codex_skill_dirs()');
const fnEnd = setupContent.indexOf('}', setupContent.indexOf('removed[@]}', fnStart));
const fnBody = setupContent.slice(fnStart, fnEnd);
expect(fnBody).toContain('~/.claude/skills/gstack');
expect(fnBody).toContain('AUTO-GENERATED from SKILL.md.tmpl');
expect(fnBody).toContain('rm -rf "$stale_target"');
const codexSection = setupContent.slice(
setupContent.indexOf('# 5. Install for Codex'),
setupContent.indexOf('# 6. Create')
);
expect(codexSection).toContain('cleanup_stale_codex_skill_dirs "$SOURCE_GSTACK_DIR" "$CODEX_SKILLS"');
expect(codexSection.indexOf('cleanup_stale_codex_skill_dirs')).toBeLessThan(
codexSection.indexOf('link_codex_skill_dirs')
);
});
// T2: Dynamic $GSTACK_ROOT paths in generated Codex preambles
test('generated Codex preambles use dynamic GSTACK_ROOT paths', () => {
const codexSkillDir = path.join(ROOT, '.agents', 'skills', 'gstack-ship');