agentsclimarketplace

Init agent task md

Skill Zrzzzz/agent-codebase-skills/init-agent-task-md

Claude Code skills for using AI agents to develop and maintain large, long-lived codebases across sessions: AGENTS.md/CLAUDE.md layered conventions + auto session-notes hook.

Install
npx -y skills add Zrzzzz/agent-codebase-skills --skill init-agent-task-md

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

为项目初始化「任务管理层」v3——协调靠结构消除、纪律靠 hook 兜底、脚本只留便利: 1. docs/tasks/T-<slug>.md:每个任务一份独立文件,**文件名即 ID**——无编号、 无锁、无主树限制,任何 agent 在任何 worktree 里直接建文件直接 commit。 2. docs/TASKS.md:marker 区索引视图,由 git hook(pre-commit / post-merge) 自动重生成——没人需要「记得跑脚本」;marker 之外(更新协议 / 约束与决策 / 踩坑与教训)由人手写,脚本不动。 3. .githooks/pre-commit:流程纪律兜底——feat/bugfix/hotfix 分支必须有对应 任务文件才能 commit;禁止直接 commit 到 main;docs/tasks 变更自动刷索引。 4. .githooks/post-merge:主 checkout pull/merge 后自动刷索引。 5. CLAUDE.md / AGENTS.md「任务入口协议」(必装 marker 块):用户报 bug / 提需求 → 先登记任务 + 切分支,再动代码;免登记白名单写死。 6. scripts/tasks-new.sh(模板便利,无锁)、scripts/tasks-index.sh(hook 调用)、 scripts/tasks-release.sh(条目直接写进 CHANGELOG 的 Unreleased 段 + --cut 切版本)。v2 的 tasks-status.sh 已删——status 流转就是一行 frontmatter Edit,updated 字段已砍(由 git log 派生)。 7. CHANGELOG.md:Keep a Changelog 骨架(不存在时创建)。 8. 可选:把「自动跑索引 + 任务推进检测」追加进 init-session-notes 的 _summarize-worker.sh,作为会话结束兜底。 幂等升级:重跑 skill 按 scripts/tasks-index.sh 里的 skill-managed 版本号 分流——v1(monolith TASKS.md)与 v2.x(顺序编号 + 四脚本)都走迁移到 v3; 已是 v3.x 只覆盖脚本与 hook 文件。 本 skill 只管「任务管理」:会话归档 hook 归 [`init-session-notes`](../init-session-notes/SKILL.md), CLAUDE.md / AGENTS.md 分层约定归 [`init-agents-md`](../init-agents-md/SKILL.md)。 使用:/init-agent-task-md;或当用户说「给项目装任务管理」「初始化 TASKS.md」 「让 agent 先开任务再改代码」时也命中本 skill。

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

45.3 KB, ~15.4k tokens by cl100k_base, as published. Nobody here has run it

Init Agent Task MD v3(任务管理层 · 无编号 + hook 自动化)

v3 的三条设计原则(对应 v2 暴露的三个痛点):

  1. 协调靠结构消除:v2 的 T-XXX 顺序编号需要全局协调(mkdir 锁、必须回主树跑、mv 进 worktree),v3 改为文件名即 ID——docs/tasks/T-<slug>.md。slug 本来就要起(分支名要用),两个 agent 起了同一个 slug 说明在做同一件事,这是要暴露的语义冲突而不是要靠锁避开的编号冲突。锁、主树限制、mv 编排全部消失。
  2. 纪律靠 hook 兜底:索引刷新和流程校验挂 git hook(.githooks/pre-commit + post-merge),不再依赖"agent 记得跑脚本"。feat/bugfix/hotfix 分支没登记任务 → commit 被拦;直接 commit main → 被拦;docs/tasks 变更 → 索引自动重生成并带进本次 commit。
  3. 脚本只留便利updated 字段砍掉(由 git log 派生),status 流转退化为一行 frontmatter Edit,v2 的 tasks-status.sh 随之删除。agent 的日常动作只剩三个:建文件、改 frontmatter、commit

生命周期(默认「独立发版」模型):status: doing 开工 → 分支 merge 到 dev 联调(填 dev_verified)→ 联调通过 status: done → PR 合入 mainstatus: archived → 打 tag 部署 prod → tasks-release.sh 把条目写进 CHANGELOG 并归档任务文件。dev 是 rolling 集成沙盒(可被 reset --hard main,不流回 main),每个任务在 main 上独立打 tag 发版。hotfix 跳 dev:从 main 切 hotfix/<slug> → 修 → PR 回 main → tag,事后 backport 回 dev。

⚠️ 该 skill 只管任务管理层。会话归档 hook 归 init-session-notes;CLAUDE.md / AGENTS.md 分层约定归 init-agents-md。三者完全独立,可单装。


Step 0:探测项目状态 + 已装版本

CURRENT_VERSION="3.1.0"

git rev-parse --git-dir 2>/dev/null >/dev/null && echo "git=yes" || echo "git=no"
git branch -a 2>/dev/null | grep -E 'develop|dev|beta|main|master' | head -10

# 任务文档 & 目录
[ -d docs/tasks ] && echo "tasks_dir=exists" || echo "tasks_dir=none"
TASKS_FILE=""
for f in "docs/TASKS.md" "TASKS.md" "TODO.md" "docs/TODO.md"; do
  [ -f "$f" ] && TASKS_FILE="$f" && break
done
[ -n "$TASKS_FILE" ] && echo "tasks_file=$TASKS_FILE" || echo "tasks_file=none"

# 索引脚本 + 版本号
INSTALLED_VERSION=""
if [ -f scripts/tasks-index.sh ]; then
  INSTALLED_VERSION="$(grep -oE 'skill-managed: init-agent-task-md v[0-9]+\.[0-9]+\.[0-9]+' scripts/tasks-index.sh | head -1 | awk -F'v' '{print $NF}')"
fi
echo "installed_version=${INSTALLED_VERSION:-none}"
echo "current_version=$CURRENT_VERSION"

# TASKS.md 中是否已有 marker
if [ -n "$TASKS_FILE" ] && grep -qF 'BEGIN:TASKS-INDEX' "$TASKS_FILE" 2>/dev/null; then
  echo "marker=present"
else
  echo "marker=absent"
fi

# git hooks 环境(v3 要装 .githooks)
HOOKS_PATH="$(git config --get core.hooksPath 2>/dev/null || true)"
echo "hooks_path=${HOOKS_PATH:-unset}"
[ -d .husky ] && echo "husky=yes" || echo "husky=no"
[ -f .githooks/pre-commit ] && grep -q 'init-agent-task-md' .githooks/pre-commit && echo "taskhook=installed" || echo "taskhook=absent"

# CLAUDE.md / AGENTS.md 入口协议
PROTO_FILE=""
for f in "CLAUDE.md" "AGENTS.md"; do
  [ -f "$f" ] && grep -qF 'BEGIN:TASK-PROTOCOL' "$f" && PROTO_FILE="$f" && break
done
echo "protocol=${PROTO_FILE:-absent}"

# CHANGELOG 与 session-notes worker
[ -f CHANGELOG.md ] && echo "changelog=exists" || echo "changelog=new"
[ -f .claude/hooks/_summarize-worker.sh ] && echo "worker=exists" || echo "worker=none"
[ -f .claude/hooks/_summarize-worker.sh ] && grep -q '任务文档检查' .claude/hooks/_summarize-worker.sh && echo "taskcheck=installed" || echo "taskcheck=absent"
[ -f .claude/hooks/_summarize-worker.sh ] && grep -q 'tasks-index.sh' .claude/hooks/_summarize-worker.sh && echo "autoindex=installed" || echo "autoindex=absent"

根据探测结果分流

状态分类该走哪条路径
tasks_dir=nonetasks_file=none全新装Path A:从零建
tasks_file 有内容且 marker=absenttasks_dir=nonev1 老版仓Path B:迁移到 v3(先按 v1→v2 的拆文件逻辑拆,再套 v3 命名)
tasks_dir=existsinstalled_version 是 2.xv2 仓Path B:迁移到 v3(改名 + 删字段 + 换脚本 + 装 hook)
tasks_dir=existsinstalled_version 是 3.x已是 v3Path C:重装/升级(只覆盖脚本与 hook,正文不动)

分支模型确认:默认「独立发版」模型——dev(rolling 集成沙盒)+ main(prod 发版源)。若探测到的分支不符(只有 main、用 staging / beta 等),用 AskUserQuestion 确认后写模板时替换。常见变体:

  • 线性三段develop → beta → main,有独立 staging 冒烟):done = 合 develop、archived = 合 beta 部署 staging。改 tasks-index.sh 段名文案 + Step 3 骨架协议为线性描述;hotfix 语义仍成立。
  • 两段无 dev 服(miniapp 类):archived 段挂「已上传体验版待审核」。改 emit_group 段名文案即可。
  • 单段 main:生命周期简化为「已完成 → CHANGELOG」,删掉「待发布」段和 emit_group archived 那行;pre-commit 的「禁止直 commit main」校验也要删(单段模型就是在 main 上干活)。

hooks 环境确认:若 hooks_path 已被设置且不是 .githooks(如 husky 的 .husky/_),不要抢——用 AskUserQuestion 确认后,把 Step 5 两个 hook 的内容作为 skill-managed 块(# BEGIN:init-agent-task-md# END:init-agent-task-md)追加进现有 hooks 目录的同名文件;husky 项目追加进 .husky/pre-commit / .husky/post-merge


Step 1:三条路径

Path A · 全新装

按顺序执行 Step 2 → 3 → 4 → 5 → 6 → 7 → 8。

Path B · 迁移到 v3(v1 或 v2.x)

AskUserQuestion 明确告知用户 v3 是 breaking(文件名方案变了),会做的事:

  • 每个任务文件按 branch 字段的 / 后缀改名:T-042.mdT-<slug>.md(如 branch: feat/coupon-checkoutT-coupon-checkout.md);
  • frontmatter:id 改成 T-<slug>updated(改由 git log 派生);加 priority: 3type: fix 改成 type: bugfix
  • trash scripts/tasks-status.sh(v3 不再需要);覆盖其余脚本;装 .githooks
  • TASKS.md 顶部「更新协议」换成 v3 文案(marker 外的「约束与决策」「踩坑与教训」原样不动);
  • CLAUDE.md / AGENTS.md 里旧的发布联动段替换为 v3「任务入口协议」marker 块。

用户确认后:

  1. v1 仓先拆文件(v1 = monolith TASKS.md,无 docs/tasks/):cp "$TASKS_FILE" "$TASKS_FILE.v1.bak"(别 mv),mkdir -p docs/tasks,用 Read 读全文,找每个含 **T-XXX** 的清单段,按所在章节推断 status(进行中→doing / 待办→todo / 已完成→done / 归档→archived),抽出 title / agent / branch / files / type,直接用 v3 命名 Write 出 docs/tasks/T-<slug>.md(slug = branch 的 / 后缀;无 branch 时用 title slug 化)。
  2. v2 仓改名 + 改 frontmatter:对每个 docs/tasks/T-*.md
    • slug 取 branch 字段 / 后的部分;branch 为空时退化用旧 id 小写(T-042.mdt-042 不改名也行,唯一性不受影响,但建议补 branch 后重命名);
    • 已被 git 跟踪的用 git mv docs/tasks/T-042.md docs/tasks/T-<slug>.md,未跟踪的用 mv
    • Edit frontmatter:id: T-<slug>;删除 updated: 行;在 type 行后加 priority: 3type: fixtype: bugfix
    • 想保住原待办排序的话,按旧索引里 todo 段的顺序给 priority 依次填 1、2、3……(可选,默认全 3 也行)。
  3. trash scripts/tasks-status.sh(存在时)。
  4. 走 Step 3 重写 TASKS.md 顶部协议(保留手写段)→ Step 4 覆盖脚本 → Step 5 装 hook → Step 7 写入口协议 → 跑一次 bash scripts/tasks-index.sh 校验。
  5. 让用户 review:v1 仓对照 .v1.bak,v2 仓 git diff 看改名与 frontmatter 变更;满意后由用户自行 trash 备份(skill 不代删)。

Path C · 已装 v3,重装/升级

  • 覆盖 3 个脚本(Step 4 最新版)+ 2 个 hook(Step 5 最新版);
  • CLAUDE.md / AGENTS.md 里 <!-- BEGIN:TASK-PROTOCOL ... --> ↔ <!-- END:TASK-PROTOCOL --> 之间整块替换成 Step 7 最新版(marker 块是 skill-managed,marker 外的内容一律不动);
  • TASKS.md 的索引 marker 语法变了就同步替换 marker 之间的注释文案;手写段一律不动
  • bash scripts/tasks-index.sh 重新生成一次索引。

Step 2:建 docs/tasks/ + 任务文件模板

mkdir -p docs/tasks

任务文件模板(docs/tasks/T-<slug>.md;用 scripts/tasks-new.sh 生成,或 agent 直接按此 Write——v3 无编号无锁,直接 Write 是一等公民,行尾注释可保留,索引脚本会剥掉):

---
id: T-<slug>            # 与文件名一致;slug = 分支名 / 后面的部分
title: <一句话讲清做什么、为什么>
status: todo            # todo | doing | done | archived
type: feat              # feat | bugfix | chore | hotfix
priority: 3             # 1 最高 … 5 最低;索引 todo/doing 段按此排序
agent: ""               # 开工时填 @agent-id,避免多 agent 抢同一个
branch: feat/<slug>     # 分支前缀与 type 同名:bugfix/ chore/ hotfix/
release: independent    # independent | batch-<name>——独立发版 or 随某批发
dev_verified: ""        # dev 联调通过日期 YYYY-MM-DD;hotfix 填 "skipped (hotfix)"
created: <YYYY-MM-DD>
files: ""               # 逗号分隔的影响文件通配,如 src/a.js, src/modules/b/**
---

## 描述

<一句话讲清做什么、为什么。bugfix 类补一句「命中场景 → 根因」。hotfix 类必须写清「命中场景 → 用户影响 → 为什么等不了 dev 集成」。>

## 子任务

- [ ]

## 备注

<可选:设计取舍、遗留问题、下一步。>

字段规则

  • id 必须与文件名一致(都是 T-<slug>)。slug 只用 [a-z0-9-]同 slug 冲突是语义信号:建文件前发现 T-<slug>.md 已存在,先读它——大概率另一个 agent 在做同一件事,该接手或换个真正不同的 slug,而不是绕过。
  • status 是索引脚本唯一识别的分组依据。流转 = 直接 Edit 这一行(todo → doing → done → archived),改完随代码一起 commit 即可——索引由 pre-commit hook 自动重生成,"最近改动日期"由 git log 派生,没有任何需要手工同步的伴生字段
  • priority:1 最高、5 最低,缺省 3。索引里 todo/doing 段按它升序排(同级按文件名字典序),非默认值会渲染成 P1/P2 徽标。调优先级就是改这个数字,不用像 v2 那样受 ID 字典序绑架。
  • type 影响两处:tasks-release.sh 归类到 CHANGELOG(feat→Added / bugfix|hotfix→Fixed / chore→Changed);分支名前缀。
  • releaseindependent(默认,单独发 tag)| batch-<name>(合车发版,索引行尾贴 🚂)。
  • dev_verified:dev 联调通过日期,手工填;hotfix 填 "skipped (hotfix)"
  • files 是逗号分隔单行,索引原样渲染。项目可自行扩展扁平字段(如 deps:),索引脚本不识别的字段静默忽略。
  • 没有 updated 字段(v3 起废除)。别加回来——它只会重新制造"改 status 忘了改日期"的同步负担。

Step 3:写 docs/TASKS.md

Path A / Path B 都从这个骨架写起(Path B 把老 TASKS.md 顶部项目说明和底部「约束与决策」「踩坑与教训」原样搬进对应位置)。<项目名> 与分支名按 Step 0 探测结果替换:

---
project: <项目名>
updated: <YYYY-MM-DD>
---

# 进度 · <项目名>

> **更新协议(v3 · 无编号 + hook 自动化)**
> - **每个任务 = `docs/tasks/T-<slug>.md`**,文件名即 ID——无编号无锁,任何 worktree 里直接建。本文件 4 个状态段是索引视图,由 git hook(pre-commit / post-merge)自动重生成,一般无需手工跑 `scripts/tasks-index.sh`。
> - **入口协议**:用户报告的 bug / 提出的需求,先登记任务 + 切 `<type>/<slug>` 分支,再动代码(详见 CLAUDE.md / AGENTS.md「任务入口协议」;pre-commit hook 会校验)。登记:`bash scripts/tasks-new.sh <feat|bugfix|chore|hotfix> <slug> "<标题>" [priority]`,或直接按 `docs/tasks/` 现有文件的模板 Write。
> - **状态流转 = 直接改任务文件 frontmatter 的 `status`**,改完 commit:
>   1. `status: doing` — 开工,填 `agent` / `files`,切 `<type>/<slug>` 分支;
>   2. 分支 merge 到 `dev` 联调(不改 status;联调通过手工填 `dev_verified: <日期>`);
>   3. dev 联调通过 → `status: done`;
>   4. 分支 PR 到 `main` 且 merge → `status: archived`;
>   5. 打 tag 部署 prod → `bash scripts/tasks-release.sh T-<slug>`(条目自动写进 `CHANGELOG.md` 的 Unreleased 段 + trash 任务文件 + 刷索引);发版切号:`bash scripts/tasks-release.sh --cut <版本号>`。
> - **hotfix fast lane**:`tasks-new.sh hotfix <slug> "..."` → 从 `main` 切 `hotfix/<slug>` → 修 → PR 回 `main` → `status: archived` → `tasks-release.sh` 打 tag。跳过 dev,`dev_verified` 填 `"skipped (hotfix)"`;事后开 `chore/backport-hotfix-<slug>` 把 fix merge 回 dev。**任务描述必须写清「命中场景 → 用户影响 → 为什么等不了 dev 集成」**,否则用 bugfix 走正常流程。
> - **dev 分支纪律**:dev 是 rolling 集成沙盒,允许被 `reset --hard main` 推倒重建;**禁止 dev → main**;**禁止基于 dev 拉分支**。
> - 待办排序看 `priority`(1 最高,缺省 3);挑任务优先选 `files` 不重叠的,减少并行冲突。
> - 决策 / 踩坑不进任务文件,写到本文件底部「🧭 约束与决策」「⚠️ 踩坑与教训」(手写区,索引脚本不动)。
> - 新 clone 后跑一次 `git config core.hooksPath .githooks` 启用流程 hook。

<!-- BEGIN:TASKS-INDEX (auto — do not edit; run scripts/tasks-index.sh) -->

## 🔨 进行中

_(暂无。tasks-new.sh 登记;开工把任务文件 status 改成 doing。)_

## 📋 待办(priority 升序)

_(暂无。)_

## ✅ 已完成(dev 联调通过,待合 main 独立发版)

_(暂无。)_

## 🗄️ 待发布(已合 main,待打 tag / 部署 prod)

_(暂无。)_

<!-- END:TASKS-INDEX -->

---

## 🧭 约束与决策(只增不删)

_(格式:`- **D-N** (YYYY-MM-DD):<决策> — 原因:<...>`)_

## ⚠️ 踩坑与教训

_(写这里的都是「已经付出过代价的教训」,不写空口猜想。格式:`<坑> → 根因:<...> → 结论:<...>`)_

关键<!-- BEGIN:TASKS-INDEX ... --><!-- END:TASKS-INDEX --> 两行必须一字不差,索引脚本靠它们定位重写区间。


Step 4:写三个脚本

mkdir -p scripts

4a · scripts/tasks-index.sh

#!/usr/bin/env bash
# scripts/tasks-index.sh
# skill-managed: init-agent-task-md v3.0.0
#
# 从 docs/tasks/T-*.md 生成 docs/TASKS.md 的索引段。
# 只重写 <!-- BEGIN:TASKS-INDEX --> 到 <!-- END:TASKS-INDEX --> 之间,
# 其余内容(更新协议 / 决策 / 踩坑)原样保留。
#
# v3:一般不需要人工跑——.githooks/pre-commit 与 post-merge 会自动跑。
# 排序:doing/todo 按 priority 升序(1 最高,缺省 3),同级按文件名;
#       done/archived 按最近改动降序(git log 派生——v3 已废除 updated 字段)。

set -euo pipefail

TASKS_DIR="${TASKS_DIR:-docs/tasks}"
INDEX_FILE="${INDEX_FILE:-docs/TASKS.md}"
BEGIN_MARK='<!-- BEGIN:TASKS-INDEX (auto — do not edit; run scripts/tasks-index.sh) -->'
END_MARK='<!-- END:TASKS-INDEX -->'

[ -d "$TASKS_DIR" ]  || { echo "tasks dir not found: $TASKS_DIR"  >&2; exit 1; }
[ -f "$INDEX_FILE" ] || { echo "index file not found: $INDEX_FILE" >&2; exit 1; }
grep -qF "$BEGIN_MARK" "$INDEX_FILE" || { echo "marker missing (BEGIN) in $INDEX_FILE" >&2; exit 1; }
grep -qF "$END_MARK"   "$INDEX_FILE" || { echo "marker missing (END) in $INDEX_FILE"   >&2; exit 1; }

get_field() {
  # 用法:get_field <file> <field>;剥行尾注释与首尾引号(模板可带 # 注释)
  awk -v f="$2" '
    BEGIN { inf = 0 }
    /^---[[:space:]]*$/ { if (inf) exit; inf = 1; next }
    inf && $0 ~ "^"f"[[:space:]]*:" {
      sub("^"f"[[:space:]]*:[[:space:]]*", "", $0)
      sub(/[[:space:]]+#.*$/, "", $0)
      gsub(/^"|"$/, "", $0)
      sub(/[[:space:]]+$/, "", $0)
      print
      exit
    }
  ' "$1"
}

# 任务文件最近改动日期:git 派生;尚未 commit 的新文件用今天
last_touched() {
  local d=""
  if git rev-parse --git-dir >/dev/null 2>&1; then
    d="$(git log -1 --format=%as -- "$1" 2>/dev/null || true)"
  fi
  [ -n "$d" ] || d="$(date +%Y-%m-%d)"
  printf '%s' "$d"
}

emit_group() {
  local heading="$1" want="$2" empty="$3" f rows=""
  printf '\n## %s\n\n' "$heading"
  for f in "$TASKS_DIR"/T-*.md; do
    [ -e "$f" ] || continue
    local status id title agent branch priority release_ files touched checkbox line key
    status="$(get_field "$f" status)"
    [ "$status" = "$want" ] || continue
    id="$(get_field "$f" id)"; [ -n "$id" ] || id="$(basename "$f" .md)"
    title="$(get_field "$f" title)"
    agent="$(get_field "$f" agent)"
    branch="$(get_field "$f" branch)"
    priority="$(get_field "$f" priority)"
    case "$priority" in [1-5]) ;; *) priority=3 ;; esac
    release_="$(get_field "$f" release)"
    files="$(get_field "$f" files)"
    touched="$(last_touched "$f")"
    case "$want" in done|archived) checkbox="[x]" ;; *) checkbox="[ ]" ;; esac
    line="- $checkbox **$id** · $title"
    # priority 仅非默认值时渲染徽标,避免行太长
    [ "$priority" != "3" ] && line="$line · P${priority}"
    [ -n "$agent" ]  && line="$line · \`$agent\`"
    [ -n "$branch" ] && line="$line · \`$branch\`"
    [ -n "$release_" ] && [ "$release_" != "independent" ] && line="$line · 🚂 \`$release_\`"
    [ -n "$files" ]  && line="$line · 影响文件: $files"
    case "$want" in done|archived) line="$line · $touched" ;; esac
    line="$line · [详情](tasks/$(basename "$f"))"
    case "$want" in
      doing|todo) key="p${priority}·$(basename "$f")" ;;
      *)          key="${touched}·$(basename "$f")" ;;
    esac
    rows="${rows}${key}"$'\t'"${line}"$'\n'
  done
  if [ -z "$rows" ]; then
    printf '_%s_\n' "$empty"
  else
    case "$want" in
      done|archived) printf '%s' "$rows" | sort -t$'\t' -k1,1r | cut -f2- ;;
      *)             printf '%s' "$rows" | sort -t$'\t' -k1,1  | cut -f2- ;;
    esac
  fi
}

tmp="$(mktemp)"
{
  awk -v mark="$BEGIN_MARK" '
    { print }
    index($0, mark) > 0 { exit }
  ' "$INDEX_FILE"

  emit_group "🔨 进行中"                                       "doing"    "(暂无。tasks-new.sh 登记;开工把任务文件 status 改成 doing。)"
  emit_group "📋 待办(priority 升序)"                        "todo"     "(暂无。)"
  emit_group "✅ 已完成(dev 联调通过,待合 main 独立发版)"    "done"     "(暂无。)"
  emit_group "🗄️ 待发布(已合 main,待打 tag / 部署 prod)"     "archived" "(暂无。)"

  printf '\n%s\n' "$END_MARK"

  awk -v mark="$END_MARK" '
    found { print; next }
    index($0, mark) > 0 { found = 1 }
  ' "$INDEX_FILE"
} > "$tmp"

mv "$tmp" "$INDEX_FILE"
echo "✅ 索引已更新: $INDEX_FILE"

写入后 chmod +x scripts/tasks-index.sh

分支模型变体只改 emit_group 的段名文案(status 名字 doing/todo/done/archived 不动),例:线性三段 done 段用「develop 已合入,待发 staging」、miniapp 两段 archived 段用「已上传体验版(待审核发布)」;单段 main 直接删 emit_group ... archived 那行。

4b · scripts/tasks-new.sh

#!/usr/bin/env bash
# scripts/tasks-new.sh
# skill-managed: init-agent-task-md v3.0.0
#
# 用法:scripts/tasks-new.sh <feat|bugfix|chore|hotfix> <slug> [标题] [priority(1-5)]
#
# v3:文件名即 ID(T-<slug>.md),无编号无锁——任何 checkout / worktree 里都能
# 直接跑。同名冲突 = 两个 agent 在做同一件事:先读已有文件,别绕过它。
# 本脚本只是模板便利:agent 也可以直接按模板 Write docs/tasks/T-<slug>.md。
#
# type 决定分支前缀 + CHANGELOG 归类:
#   feat    → feat/<slug>     → Added
#   bugfix  → bugfix/<slug>   → Fixed
#   chore   → chore/<slug>    → Changed
#   hotfix  → hotfix/<slug>   → Fixed(fast lane:从 main 直切,跳过 dev 集成)

set -euo pipefail

type="${1:-}"
slug="${2:-}"
title="${3:-$slug}"
priority="${4:-3}"

case "$type" in
  feat|bugfix|chore|hotfix) ;;
  *) echo "用法:$0 <feat|bugfix|chore|hotfix> <slug> [标题] [priority(1-5)]" >&2; exit 1 ;;
esac
case "$slug" in
  ''|-*|*[!a-z0-9-]*) echo "slug 只能用 [a-z0-9-] 且不能以 - 开头:'$slug'" >&2; exit 1 ;;
esac
case "$priority" in
  [1-5]) ;;
  *) echo "priority 应为 1-5(1 最高)" >&2; exit 1 ;;
esac

TASKS_DIR="${TASKS_DIR:-docs/tasks}"
mkdir -p "$TASKS_DIR"

id="T-$slug"
file="$TASKS_DIR/$id.md"
if [ -e "$file" ]; then
  echo "✗ $file 已存在——可能另一个 agent 已登记同一件事。先读它再决定接手还是换 slug。" >&2
  exit 1
fi

today="$(date +%Y-%m-%d)"
branch="$type/$slug"

cat > "$file" <<EOF
---
id: $id
title: $title
status: todo
type: $type
priority: $priority
agent: ""
branch: $branch
release: independent
dev_verified: ""
created: $today
files: ""
---

## 描述

<一句话讲清做什么、为什么。bugfix 类补一句「命中场景 → 根因」。hotfix 类必须写清「命中场景 → 用户影响 → 为什么等不了 dev 集成」。>

## 子任务

- [ ]

## 备注
EOF

echo "$file"

写入后 chmod +x scripts/tasks-new.sh

4c · scripts/tasks-release.sh

#!/usr/bin/env bash
# scripts/tasks-release.sh
# skill-managed: init-agent-task-md v3.0.0
#
# 用法 1:scripts/tasks-release.sh <T-slug>[.md]
#   打 tag 部署 prod 后的收尾:把该任务条目**直接写进** CHANGELOG.md 的
#   ## [Unreleased] 段(按 type 归入 ### Added/Fixed/Changed,缺段自动建),
#   然后 trash 任务文件 + 刷新索引(worktree 里跳过索引)。
#   v3 与 v2 的区别:append 到 Unreleased 是确定性操作,不再打印片段让人手工
#   粘贴;需要人判断的只剩「切版本号」,见用法 2。
#
# 用法 2:scripts/tasks-release.sh --cut <version>
#   把 ## [Unreleased] 现有内容整体降为 ## [<version>] - <today>,
#   并在其上新建空的 ## [Unreleased] 段。

set -euo pipefail

TASKS_DIR="${TASKS_DIR:-docs/tasks}"
CHANGELOG="${CHANGELOG:-CHANGELOG.md}"
today="$(date +%Y-%m-%d)"

[ -f "$CHANGELOG" ] || { echo "找不到 $CHANGELOG" >&2; exit 1; }

# ── 用法 2:--cut <version> ──
if [ "${1:-}" = "--cut" ]; then
  version="${2:-}"
  [ -n "$version" ] || { echo "用法:$0 --cut <version>" >&2; exit 1; }
  grep -qF '## [Unreleased]' "$CHANGELOG" || { echo "$CHANGELOG 里没有 ## [Unreleased] 段" >&2; exit 1; }
  tmp="$(mktemp)"
  awk -v ver="$version" -v today="$today" '
    /^## \[Unreleased\]/ && !done { printf "## [Unreleased]\n\n## [%s] - %s\n", ver, today; done=1; next }
    { print }
  ' "$CHANGELOG" > "$tmp"
  mv "$tmp" "$CHANGELOG"
  echo "✔ 已切版本:## [${version}] - ${today}(新的空 Unreleased 段已就位)"
  exit 0
fi

# ── 用法 1:<T-slug> ──
id="${1:-}"
[ -n "$id" ] || { echo "用法:$0 <T-slug> | --cut <version>" >&2; exit 1; }
case "$id" in
  *.md)    file="$TASKS_DIR/$(basename "$id")" ;;
  T-*|t-*) file="$TASKS_DIR/T-${id#[Tt]-}.md" ;;
  *)       file="$TASKS_DIR/T-$id.md" ;;   # 容错:漏写 T- 前缀
esac
[ -f "$file" ] || { echo "任务文件不存在: $file" >&2; exit 1; }

get_field() {
  awk -v f="$1" '
    BEGIN { inf = 0 }
    /^---[[:space:]]*$/ { if (inf) exit; inf = 1; next }
    inf && $0 ~ "^"f"[[:space:]]*:" {
      sub("^"f"[[:space:]]*:[[:space:]]*", "", $0)
      sub(/[[:space:]]+#.*$/, "", $0)
      gsub(/^"|"$/, "", $0)
      sub(/[[:space:]]+$/, "", $0)
      print
      exit
    }
  ' "$file"
}

fid="$(get_field id)"; [ -n "$fid" ] || fid="$(basename "$file" .md)"
title="$(get_field title)"
type_="$(get_field type)"
branch="$(get_field branch)"
status_="$(get_field status)"

if [ "$status_" != "archived" ] && [ "$status_" != "done" ]; then
  printf '⚠ 任务状态是 "%s",不是 archived/done。真的要发版并删除吗?(y/N) ' "$status_" >&2
  read -r ans
  case "$ans" in
    y|Y|yes|YES) ;;
    *) echo "取消。" >&2; exit 1 ;;
  esac
fi

case "$type_" in
  feat)              section="Added"   ;;
  bugfix|hotfix|fix) section="Fixed"   ;;
  *)                 section="Changed" ;;
esac

entry="- **${fid}** · ${title}(\`${branch}\`)"

# ── 条目直接写进 CHANGELOG 的 Unreleased 段 ──
tmp="$(mktemp)"
awk -v sec="$section" -v entry="$entry" '
  BEGIN { inserted = 0; in_unrel = 0; unrel_seen = 0; skipblank = 0 }
  {
    if (skipblank) { skipblank = 0; if ($0 ~ /^[[:space:]]*$/) next }
  }
  /^## \[Unreleased\]/ { print; in_unrel = 1; unrel_seen = 1; next }
  /^## / {
    if (!inserted && in_unrel)    { printf "### %s\n\n%s\n\n", sec, entry; inserted = 1 }
    if (!inserted && !unrel_seen) { printf "## [Unreleased]\n\n### %s\n\n%s\n\n", sec, entry; inserted = 1; unrel_seen = 1 }
    in_unrel = 0; print; next
  }
  {
    if (in_unrel && !inserted && $0 == "### " sec) {
      print; print ""; print entry; inserted = 1; skipblank = 1; next
    }
    print
  }
  END {
    if (!inserted) {
      if (!unrel_seen) printf "\n## [Unreleased]\n"
      printf "\n### %s\n\n%s\n", sec, entry
    }
  }
' "$CHANGELOG" > "$tmp"
mv "$tmp" "$CHANGELOG"
echo "✔ 已写入 $CHANGELOG · [Unreleased] › ### ${section}:${entry}"

# ── trash 任务文件 ──
if command -v trash >/dev/null 2>&1; then
  trash "$file"
  echo "✔ 已 trash $file"
else
  echo "⚠ 未安装 trash 命令;请手动移除 $file 后再跑 scripts/tasks-index.sh" >&2
  exit 2
fi

# ── 刷新索引(worktree 是局部视图,跳过)──
case "$(git rev-parse --git-dir 2>/dev/null || echo .)" in
  */worktrees/*)
    echo "⚠ 当前在 git worktree 里,已跳过 tasks-index.sh;回主 checkout commit 时 pre-commit 会自动刷。" >&2 ;;
  *)
    [ -x scripts/tasks-index.sh ] && bash scripts/tasks-index.sh ;;
esac

写入后 chmod +x scripts/tasks-release.sh


Step 5:装 git hooks(v3 核心——自动化与纪律都在这)

mkdir -p .githooks

5a · .githooks/pre-commit

#!/usr/bin/env bash
# .githooks/pre-commit
# skill-managed: init-agent-task-md v3.0.0
#
# ① 流程纪律:feat/bugfix/hotfix 分支必须有对应任务文件;禁止直接 commit main。
# ② docs/tasks/ 有 staged 变更 → 自动重生成 docs/TASKS.md 并带进本次 commit。
# 逃生口:git commit --no-verify,或 TASKS_GUARD=off git commit ...

set -u

[ "${TASKS_GUARD:-on}" = "off" ] && exit 0

branch="$(git symbolic-ref --quiet --short HEAD || echo "")"

# ① 禁止直接 commit 到 main/master(merge commit 放行)
case "$branch" in
  main|master)
    if [ ! -e "$(git rev-parse --git-path MERGE_HEAD)" ]; then
      echo "✗ 禁止直接 commit 到 $branch —— main 只通过 PR merge 前进。" >&2
      echo "  确有必要:git commit --no-verify" >&2
      exit 1
    fi
    ;;
esac

# ② feat/bugfix/hotfix 分支必须先登记任务(chore/* 是免登记 lane,不校验)
case "$branch" in
  feat/*|bugfix/*|hotfix/*)
    if ! grep -rqsE "^branch:[[:space:]]*${branch}[[:space:]]*$" docs/tasks/ 2>/dev/null; then
      echo "✗ 分支 ${branch} 在 docs/tasks/ 里没有对应任务文件(frontmatter branch: ${branch})。" >&2
      echo "  先登记:bash scripts/tasks-new.sh ${branch%%/*} ${branch#*/} \"<一句话标题>\"" >&2
      echo "  trivial 改动(typo/注释/文档)请走 chore/* 分支;确要跳过:git commit --no-verify" >&2
      exit 1
    fi
    ;;
esac

# ③ docs/tasks/ 有 staged 变更 → 刷新索引并带上(worktree 是局部视图,跳过)
if ! git diff --cached --quiet -- docs/tasks/ 2>/dev/null; then
  case "$(git rev-parse --git-dir)" in
    */worktrees/*) : ;;
    *)
      if [ -x scripts/tasks-index.sh ]; then
        bash scripts/tasks-index.sh >/dev/null 2>&1 && git add docs/TASKS.md
      fi
      ;;
  esac
fi

exit 0

5b · .githooks/post-merge

#!/usr/bin/env bash
# .githooks/post-merge
# skill-managed: init-agent-task-md v3.0.0
#
# 主 checkout pull / merge 之后,若本次合入动了 docs/tasks/,自动刷新索引。
# 索引是纯派生视图:这里只改工作区不自动 commit——随下次 commit 带上即可。

set -u

case "$(git rev-parse --git-dir)" in */worktrees/*) exit 0 ;; esac
git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD 2>/dev/null | grep -q '^docs/tasks/' || exit 0
[ -x scripts/tasks-index.sh ] && bash scripts/tasks-index.sh >/dev/null 2>&1
git diff --quiet -- docs/TASKS.md 2>/dev/null || echo "ℹ docs/TASKS.md 索引已刷新(post-merge),随下次 commit 提交即可。"
exit 0

5c · 启用

chmod +x .githooks/pre-commit .githooks/post-merge
git config core.hooksPath .githooks
  • core.hooksPathrepo-local 配置,worktree 共享(config 是全仓一份),但新 clone 需要重新跑一次 git config core.hooksPath .githooks——这句已写进 Step 3 的更新协议和 Step 7 的入口协议,agent / 人都能看到。
  • 已有 hooks 方案时不要抢(Step 0 探测到 hooks_path 非空且非 .githooks,或 husky=yes):把 5a/5b 的正文用 # BEGIN:init-agent-task-md# END:init-agent-task-md 包起来追加进现有 hooks 目录的 pre-commit / post-merge(不存在就创建),重跑 skill 时按 marker 替换该块。
  • 单段 main 分支模型:删掉 5a 的 ①(禁止直 commit main)——单段模型就是在 main 上干活。

Step 6:写入 CHANGELOG.md(不存在时)

# Changelog

本项目所有重要变更记录于此。格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),
版本遵循 [语义化版本](https://semver.org/lang/zh-CN/)。

条目来源:打 tag 部署 prod 时由 `scripts/tasks-release.sh` 从 [`docs/tasks/`](docs/tasks/)
自动写入 Unreleased 段;发版切号用 `scripts/tasks-release.sh --cut <版本号>`
(流程见 [`docs/TASKS.md`](docs/TASKS.md) 更新协议)。

## [Unreleased]

已存在 CHANGELOG.md 时不动内容;若缺 ## [Unreleased] 段,建议补一个(tasks-release.sh 缺段时也会自动建,不补也能跑)。


Step 7:写 CLAUDE.md / AGENTS.md「任务入口协议」(必装)

v2 的对应步骤是可选的,这是 v3 修的核心问题之一:agent 日常会话根本读不到 SKILL.md,规则必须常驻 always-on memory 文件,且写成「触发条件 → 动作」而不是机制说明书。

目标文件:项目有 AGENTS.md 用 AGENTS.md(跨工具生效),否则用 CLAUDE.md;两者都没有就创建 CLAUDE.md 只放这一段并提示用户之后跑 /init-agents-md 补全约定层。追加以下 marker 块(重跑 skill 按 marker 整块替换;dev/main 按 Step 0 探测的分支模型替换):

<!-- BEGIN:TASK-PROTOCOL (skill-managed: init-agent-task-md v3.1.0) -->
## 任务入口协议

**用户报告的任何 bug、提出的任何需求:动代码之前,先登记任务、切对应分支。**

1. 判断 type:`bugfix`(现有行为不对)/ `feat`(新能力)/ `chore`(重构、依赖、配置、纯文档)/ `hotfix`(线上事故且等不了 dev 联调——任务描述必须写清「命中场景 → 用户影响 → 为什么等不了」,写不出就用 bugfix)。
2. 登记:`bash scripts/tasks-new.sh <type> <slug> "<一句话标题>" [priority]`,或直接按 `docs/tasks/` 现有文件的模板 Write `docs/tasks/T-<slug>.md`(文件名即 ID,无编号无锁,任何 worktree 里直接建;`T-<slug>.md` 已存在说明别人在做同一件事——先读它)。
3. 切 `<type>/<slug>` 分支,然后才开始改代码。pre-commit hook 会校验:feat/bugfix/hotfix 分支没有对应任务文件时 commit 会被拦。

**免登记白名单**(走 `chore/*` 分支或现有分支顺带,不建任务文件):typo、纯注释/纯文档、单文件 ≤ 10 行且无行为变化。有行为变化就不是 chore 顺带——别用 chore 分支绕过校验。

**状态流转 = 直接 Edit 任务文件 frontmatter 的 `status`**(todo → doing → done → archived),改完随代码 commit,索引 `docs/TASKS.md` 由 hook 自动刷新,不用跑任何脚本:
- 开工:`status: doing` + 填 `agent` / `files`;
- merge 到 `dev` 联调通过:填 `dev_verified: <日期>`,然后 `status: done`;
- PR 合入 `main`:`status: archived`;
- 打 tag 部署 prod:`bash scripts/tasks-release.sh T-<slug>`(条目自动进 CHANGELOG 的 Unreleased 段并归档任务文件);发版切号:`bash scripts/tasks-release.sh --cut <版本号>`。

**发版后清理(部署 prod 完成后必做)**:跑完 `tasks-release.sh`(= 该任务已上 prod)之后,**主动**检查该任务对应的 `<type>/<slug>` 本地分支与 worktree 是否还在,如果在就列出来问用户是否清理——**默认不自动删**。执行前必须先看一眼:
- 分支有未推送到 `origin` 的 commit → 先提示,别默默丢工作;
- worktree 有 uncommitted 改动 / untracked 文件(`git -C <wt> status --porcelain`)→ 先提示;
- worktree 是当前所在 worktree → 不能删自己,提示用户先切走;
- 清理动作:`git worktree remove <path>`(有变更用 `--force` 前先确认)、`git branch -d <branch>`(未合入用 `-D` 前先确认)、可选 `git push origin --delete <branch>`(远程分支要另问)。

粒度(逐个确认 / 批量确认 / dry-run 先看)由 agent 根据数量和风险自行判断,不用死板套模板。

**分支纪律**:`dev` 是 rolling 集成沙盒(可被 `reset --hard main`);禁止 dev → main、禁止基于 dev 拉分支;每个任务从 `main` 拉分支、独立 PR 回 `main` 发版;hotfix 修完记得开 `chore/backport-hotfix-<slug>` 合回 dev。新 clone 后跑一次 `git config core.hooksPath .githooks` 启用流程 hook。
<!-- END:TASK-PROTOCOL -->

另建议把 @docs/TASKS.md 加进 CLAUDE.md 的 import 列表(任务索引常驻上下文,详情按需 Read docs/tasks/T-<slug>.md)。


Step 8:追加会话结束自动化(可选,需已装 init-session-notes)

仅当 Step 0 探测 worker=exists 且(autoindex=absenttaskcheck=absent)时,用 AskUserQuestion 询问是否安装。确认后分块追加到 .claude/hooks/_summarize-worker.sh 末尾——已装的块不重复追加。v3 里这层是兜底(hook 已覆盖日常刷新),价值在于 worktree 会话结束后主仓库索引的自愈 + 任务推进检测。

8a · 自动跑索引脚本(autoindex=absent 时)


# ── 任务索引自动刷新(由 init-agent-task-md v3 安装)──
if [ -x "$cwd/scripts/tasks-index.sh" ]; then
  ( cd "$cwd" && bash scripts/tasks-index.sh >>"$log" 2>&1 ) || true
fi

8b · 任务推进检测(taskcheck=absent 时)


# ── 任务文档检查(Task Doc Auto-Check,由 init-agent-task-md 安装)──
tasks_file=""
for candidate in "$cwd/docs/TASKS.md" "$cwd/TASKS.md" "$cwd/TODO.md" "$cwd/docs/TODO.md"; do
  [ -f "$candidate" ] && tasks_file="$candidate" && break
done

if [ -n "$tasks_file" ]; then
  tasks_pending="$(awk '
    /^## .*(进行中|待办|In Progress|Todo|TODO|Pending)/ { in_section=1; next }
    /^## / { in_section=0 }
    in_section { print }
  ' "$tasks_file" | head -c 8000)"

  if [ -n "$tasks_pending" ]; then
    task_prompt="下面是一次 Claude Code 开发会话记录,以及项目当前的未完成任务列表。

请分析本次会话,判断哪些任务被完成或有明显推进。仅报告有实质证据(代码已提交/功能已验证/问题已解决)的任务。

输出格式(严格按此):
- 如有完成的任务:输出 markdown 无序列表,每条格式为「✅ [任务简称]:一句话说明完成了什么」
- 如有推进但未完成的任务:输出「🔄 [任务简称]:一句话说明推进了什么」
- 如无实质推进:只输出一行 NONE

未完成任务列表:
$tasks_pending

会话记录(节选):
$(printf '%s' "$convo" | tail -c 20000)"

    task_check="$(CLAUDE_SESSION_SUMMARY_RUNNING=1 claude -p "$task_prompt" --settings '{"disableAllHooks":true}' 2>>"$log")"

    if [ -n "$task_check" ] && [ "$(printf '%s' "$task_check" | tr -d '[:space:]')" != "NONE" ]; then
      {
        printf '\n**任务进度(自动检测):**\n\n'
        printf '%s\n' "$task_check"
      } >> "$notes"
    fi
  fi
fi

worker=none(未装 init-session-notes):跳过本步,报告里提示「想要会话结束兜底刷索引 + 推进检测,先跑 /init-session-notes,再回来重跑本 skill」。


Step 9:输出初始化 / 迁移报告

Path A(全新装)

✅ 任务管理层已初始化(v3.1.0 · 无编号 + hook 自动化):

  • docs/tasks/              (新建,空目录)
  • docs/TASKS.md            (新建,含 marker 索引区)
  • scripts/tasks-index.sh   (新建,可执行;由 hook 调用,一般无需手工跑)
  • scripts/tasks-new.sh     (新建,可执行;模板便利,也可直接 Write 任务文件)
  • scripts/tasks-release.sh (新建,可执行;条目直写 CHANGELOG + --cut 切版本)
  • .githooks/pre-commit     (新建:分支↔任务校验 + 拦直 commit main + 自动刷索引)
  • .githooks/post-merge     (新建:pull/merge 后自动刷索引)
  • core.hooksPath           (已指向 .githooks;新 clone 需重跑一次 git config)
  • CHANGELOG.md             ([新建/已存在保留])
  • 任务入口协议             (已写入 [CLAUDE.md/AGENTS.md] marker 块)
  • SessionEnd 兜底          ([已追加/未装 init-session-notes/用户跳过/已存在])

登记第一个任务:bash scripts/tasks-new.sh feat my-first "登记第一个任务"
开工:Edit docs/tasks/T-my-first.md 的 status → doing,切 feat/my-first 分支

Path B(v1/v2 → v3 迁移)

✅ 任务管理层已迁移到 v3.1.0(从 [v1/v2.x]):

  • docs/tasks/T-<slug>.md   (N 个任务文件已按 branch 后缀改名 + 更新 frontmatter)
  • frontmatter 变更          (id 改 slug 形式;删 updated;加 priority: 3;type: fix → bugfix)
  • scripts/tasks-status.sh  (已 trash——v3 状态流转 = 直接 Edit status 字段)
  • scripts/tasks-index.sh / tasks-new.sh / tasks-release.sh(已覆盖到 v3)
  • .githooks/ + core.hooksPath(已装:入口校验 + 自动刷索引)
  • docs/TASKS.md            (顶部协议已换 v3 文案;手写段未动;索引已重新生成)
  • 任务入口协议             (已写入 [CLAUDE.md/AGENTS.md],替换旧发布联动段)

请 review:
  1. git diff 看任务文件改名与 frontmatter 变更是否无缺漏
  2. [v1 仓] 对照 docs/TASKS.md.v1.bak,满意后自行 trash 备份
  3. 团队同步三件事:新分支必须先有任务文件(hook 会拦)、
     状态流转直接改 status 字段、新 clone 跑 git config core.hooksPath .githooks

Path C(v3 重装/升级)

✅ 任务管理层已更新到 v3.1.0:

  • scripts/*.sh、.githooks/*(已覆盖到最新版)
  • docs/TASKS.md            (索引已重新生成;正文与手写段未动)

如无变化则说明该项目已是最新,无需操作。

注意事项

  • slug 冲突是语义信号,不是错误tasks-new.sh(或直接 Write)发现 T-<slug>.md 已存在时,先 Read 它——大概率另一个 agent 在做同一件事,该接手/协调,而不是换个近义 slug 绕过(那才是真正的重复劳动)。
  • 不要把 updated 字段加回来:v3 的"最近改动日期"由 git log -1 --format=%as 派生,加回手工字段只会重新制造同步负担。同理不要恢复 tasks-status.sh——status 流转就是一行 Edit。
  • worktree 全面解禁,但索引只在主 checkout 生成:v3 里任何脚本在任何 worktree 都能跑(无锁无编号);只有 docs/TASKS.md 的重生成会在 worktree 里自动跳过(局部视图不完整)。worktree 分支上的 commit 永远不带 TASKS.md 变更 → merge 无冲突;索引在主 checkout 的 pre-commit / post-merge / SessionEnd 三个时机自愈。worktree 里 TASKS.md 本地陈旧无妨——任务详情永远以 docs/tasks/T-<slug>.md 为准。
  • chore 是免登记 lane,不是逃生舱:有行为变化的改动走 chore 分支绕过 pre-commit 校验是纪律问题,hook 拦不住语义。入口协议里已写明白名单边界(typo / 注释 / 文档 / ≤10 行无行为变化)。
  • hook 逃生口git commit --no-verifyTASKS_GUARD=off。留给"确实特殊"的场景,agent 不应默认使用——CLAUDE.md 入口协议没授权它。
  • 不要绕过 marker 区手改 TASKS.md 的索引段——下次 hook 跑索引就没了。想法/决策/踩坑写 marker 之外(底部两段)。
  • hotfix 纪律:fast lane 跳 dev 直切 main,任务描述必须写清「命中场景 → 用户影响 → 为什么等不了 dev 集成」,写不出就回退 bugfix。事后开 chore/backport-hotfix-<slug> 合回 dev。
  • dev 分支纪律:dev 是 rolling 沙盒,可被 reset --hard main;禁止 dev → main、禁止基于 dev 拉分支、禁止 cherry-pick dev 的 commit 回 main。
  • 已有 husky 等 hooks 方案:不设 core.hooksPath,把 hook 正文按 marker 块追加进现有 hook 文件(见 Step 5c)。
  • 索引脚本的 get_field 会剥行尾 # 注释:所以 agent 直接从 Step 2 模板 Write(带注释)也能被正确解析;但 title 里别写 #(空格+井号会被当注释剥掉)。
  • 迁移备份:v1 迁移先 cp.v1.bak,用户 review 满意后由用户手动 trash绝对禁止 rm——用户级全局约定)。
  • v2 → v3 breaking 清单(Path B 迁移覆盖,此处备查):
    • 文件名 T-XXX.mdT-<slug>.mdid 字段同步;
    • updated 字段;新增 priority 字段(缺省 3,索引对缺失文件也按 3 处理,存量不补也兼容);
    • tasks-status.sh 删除;tasks-new.sh 去锁、参数变为 <type> <slug> [标题] [priority]
    • tasks-release.sh 从"打印片段"改为"直写 CHANGELOG Unreleased 段",新增 --cut <version>
    • 新增 .githooks/pre-commit.githooks/post-merge + core.hooksPath
    • CLAUDE.md/AGENTS.md 入口协议从可选建议升级为必装 marker 块(BEGIN:TASK-PROTOCOL)。
  • 绝对禁止 rm:删除任何文件时用 trash(用户级全局约定)。

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.