[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6K7QPPJM5HD7GWVHPV910HZ":3,"skill-same:01M6K7QPPJM5HD7GWVHPV910HZ":76},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":42,"linkOnly":43,"markdownContent":44,"markdownBody":45,"frontMatter":46,"refName":48,"commitSha":49,"skillDirectory":50,"forksCount":51,"repositoryHtmlUrl":52,"repositoryHomepageUrl":53,"ownerHtmlUrl":54,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":55,"install":56,"resources":69},"01M6K7QPPJM5HD7GWVHPV910HZ","add-task",null,"Add a new task to any of your local projects. Auto-detects the current project from the working directory and conforms to that project's conventions — prefix, numbering, todo-file layout, task-file template. Reads conventions from `${CLAUDE_PLUGIN_ROOT}\u002Fskills\u002Fadd-task\u002Fadd-task-projects.yaml`. Use when the user wants to add a task, capture a TODO, or track a new idea in a local project.","tbeack\u002Ffsad_playbook","tbeack","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F206807984?v=4","01M6K7Q3C3VRTCVC55T71A1SY9","Theo Beack","01M6K7Q3GDY8TXRQEH984SEE5W",8,"MIT","https:\u002F\u002Fgithub.com\u002Ftbeack\u002Ffsad_playbook\u002Fblob\u002Fmain\u002Fskills\u002Fadd-task\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41],"add","add task","task","new","directory","when","title","track","new task","layout","project","local","todo","idea","auto","use","template","local projects","projects","brief","yaml","file","plugin","2026-11-10 10:12:30",false,"---\ndescription: Add a new task to any of your local projects. Auto-detects the current project from the working directory and conforms to that project's conventions — prefix, numbering, todo-file layout, task-file template. Reads conventions from `${CLAUDE_PLUGIN_ROOT}\u002Fskills\u002Fadd-task\u002Fadd-task-projects.yaml`. Use when the user wants to add a task, capture a TODO, or track a new idea in a local project.\nargument-hint: '`[brief task title]`'\n---\n\n# fsad-harness:add-task — multi-project task adder\n\nYou add a new task entry (and, where the project warrants it, a per-task detail file) in whichever local project the user is currently working in. Project-specific conventions are defined in `${CLAUDE_PLUGIN_ROOT}\u002Fskills\u002Fadd-task\u002Fadd-task-projects.yaml` — always read that file before doing anything else.\n\n`$ARGUMENTS` (if present) is the brief task title.\n\n**What \"done\" means for a task-creation run:**\n- The new task ID is unique in the todo file — verified with a re-grep, not assumed from arithmetic alone.\n- The new bullet matches the formatting of its sibling entries (`cfg.todo_entry_template`, or the file's actual format if it has drifted from the cfg).\n- Every acceptance criterion in the generated task-detail file (when one is created) is falsifiable — a future reader can check it against the codebase and get a clear pass\u002Ffail, not a vague judgment call.\n\nThe steps below exist to satisfy these criteria, not just to move mechanically from prefix to written bullet.\n\n## Asking the user\n\nThis rule covers every question this skill asks the user: a confirmation, a choice, a missing value, a numbered decision list, or a stop-and-ask. It applies even where a later step says \"ask the user\" or \"ask exactly once\". The user may watch the decisions pane rather than the chat, so a question that only goes to chat can go unanswered.\n\n- **When `mcp__decision-tracker__decision` is in your tool list** (loaded, or named as a deferred tool; load a deferred one with `ToolSearch` first), log each question before you ask it:\n  1. Call it with `action: \"open\"`, the question (ending in `?`), and `options` when the answers are a closed set. Set `required: false` when the work can go on without an answer. A numbered list of questions opens one decision per item. Keep each returned `id`.\n  2. Ask the question in chat as this skill says. The open call does not replace the chat question.\n  3. When the user answers, in chat or as a `Decision Dn: \u003Canswer>` message from the plugin, call the tool with `action: \"close\"`, the `id` and the answer.\n  4. When the change the answer asked for is in place, call it with `action: \"implement\"`, the `id` and a `path:line`.\n- **When the tool is absent**, ask in chat only.\n- An `AskUserQuestion` call is logged on its own. Do not open it again.\n- This rule changes where a question is logged. It does not change when this skill asks, or how many times.\n\n## Step 0 — Detect the project\n\n1. Determine the current working directory.\n2. Read `${CLAUDE_PLUGIN_ROOT}\u002Fskills\u002Fadd-task\u002Fadd-task-projects.yaml`.\n3. Match cwd against each project's `match_paths` (expand `~`). The cwd may be a descendant of a match path. If multiple match, prefer the longest (most specific) match.\n4. If a project matches, refer to its entry as `cfg` and proceed.\n5. If **no** project matches in `add-task-projects.yaml`:\n   - Tell the user the project is not registered.\n   - Show the values you'd use from the `default` block.\n   - Ask whether to (a) proceed using the defaults this once, (b) register the project now, or (c) abort.\n   - If (b), collect the following fields one at a time, showing the suggested default for each:\n     - `name` — project key in the YAML (e.g. `my_project`)\n     - `match_paths` — root path (default: current working directory)\n     - `prefix` — task identifier prefix (e.g. `MYP`)\n     - `number_digits` — zero-pad width (default: `3`)\n     - `todo_file` — relative path to the todo file (default: `planning\u002Fto do\u002Ftodo.md`)\n     - `task_dir` — relative path to the task detail directory (default: `planning\u002Fto do`)\n     - `use_full_template` — whether to create per-task detail files (default: `true`)\n     - `version_scheme` — versioning convention: `semver`, `integer`, `calver`, or `none` (default: `semver`)\n     - `version_files` — file(s) that carry the version string (default: `README.md`; user may specify `package.json` or others)\n   - Write all collected fields as a new entry under `projects:` in the YAML before continuing.\n\n## Step 0.4 — Skill routing\n\nResolve which skill should actually handle this task-creation run for `cfg`'s project:\n\n1. `desired_skill` = `cfg.skills.add_task` (if set) → else the YAML's top-level `defaults.skills.add_task` (if set) → else `\"fsad-harness:add-task\"`.\n2. If `desired_skill` differs from `\"fsad-harness:add-task\"`: invoke the `Skill` tool with that skill name, passing `$ARGUMENTS` unchanged, and **stop** — none of this skill's remaining steps run in this invocation.\n3. Otherwise continue to Step 1.\n\n## Step 1 — Compute the next number\n\n**Success criteria for this step:** the computed `ID` must be numerically correct (the highest existing number for `{prefix}` in the todo file, plus one) regardless of whether `{prefix}` itself contains a hyphen (e.g. `KHB-Todo`), and must be verified as unique in the todo file — not merely assumed — before it is ever written. Uniqueness is protected two ways: **mutual exclusion** (Step 1.0's lock — only one `add-task` invocation can be computing\u002Fwriting an ID at a time) and **detection** (Step 1.6's pre-write check and Step 3's post-write re-grep, which catch stale arithmetic if the lock is ever bypassed or a lock file is manually removed).\n\n### Step 1.0 — Acquire the allocation lock\n\nBefore touching the todo file, acquire an exclusive lock so two concurrent `add-task` invocations (two CLI sessions, an autonomous agent running alongside an interactive one, etc.) can never compute the same `ID` at once — this closes the race that Step 1.6\u002FStep 3's checks can only detect after the fact.\n\n1. Lock path: `{lock_dir} = \"{dirname(todo_file_path)}\u002F.add-task.lock\"` — the lock lives next to the project's `todo_file`, so it's naturally scoped per-project; two different projects' `add-task` runs never contend for the same lock.\n2. Attempt `mkdir \"{lock_dir}\"` and check the exit code. `mkdir` is atomic on POSIX filesystems — if the directory already exists, the command fails, so exactly one concurrent caller can ever succeed.\n   - **Exit 0 (acquired)** — continue to Step 1.1.\n   - **Nonzero (lock already held)**:\n     a. Check the lock directory's age: `find \"{lock_dir}\" -maxdepth 0 -mmin +1`.\n        - **Non-empty output (older than 60s, stale)** — a prior run crashed or was killed before releasing its lock. Remove it (`rmdir \"{lock_dir}\"`) and retry `mkdir` once. If that retry also fails, treat the lock as fresh and fall through to (b).\n        - **Empty output (60s or newer, fresh)** — another `add-task` invocation is genuinely in progress; go to (b).\n     b. **Fresh lock — poll and retry.** Wait ~2s, retry `mkdir`, up to 5 attempts (~10s total). If still unacquired after 5 attempts, stop and tell the user another `add-task` run appears to be in progress; ask whether to keep waiting (repeat the poll) or abort. **Do not `rmdir` the lock on this abort path** — this invocation never acquired it, so it isn't this invocation's to release; the directory the retries were failing against belongs to whichever session is still holding it. Do not proceed to Step 1.1 without the lock.\n3. **Release discipline.** The lock must be released on *every* exit path where **this invocation held it** — never left held by this invocation when control leaves Steps 1–3, whether by success or by a stop-and-ask. The specific release points are called out inline at each exit below: the happy-path release after Step 3's post-write re-grep, and a release immediately before the two stop-and-asks that can only be reached after this invocation has the lock (Step 1.6's second collision, Step 3's retry-fails). The poll-timeout abort above is the one exit path with nothing to release, precisely because the lock was never acquired.\n\n### Step 1.1 — Find every existing ID\n\nDo **not** read the full todo file. Use targeted Bash commands instead:\n\n```bash\ngrep -oE '{prefix}-[0-9]+' \"{todo_file_path}\"\n```\nSubstitute the literal prefix (e.g. `CBP`, `TBS`, `KHB-Todo`, `FSD_Train`) for `{prefix}` and the resolved absolute path for `{todo_file_path}`. Quote the path to handle spaces. If the output is empty, no tasks exist yet — start at 1 and skip to Step 1.3.\n\n### Step 1.2 — Parse the numeric suffix yourself — do not pipe through `sort`\n\n`sort -t- -k2 -n | tail -1` assumes the number always lands in field 2 when splitting on `-`. That assumption breaks the moment the prefix itself contains a hyphen: for `KHB-Todo-0010`, splitting on `-` gives fields `KHB` \u002F `Todo` \u002F `0010`, so field 2 is the literal string `\"Todo\"` on every matched line — `sort -n` can't numerically order a constant string, and `tail -1` just returns whichever line happens to sort last (effectively grep\u002Ffile order), not the actual highest number. Instead, for each line grep returned, split on the **last** `-` (not a fixed field position) to isolate the trailing digits regardless of how many hyphens the prefix contains, parse each as an integer, and take the max. This is you reasoning over the grep output directly — not another shell pipeline.\n\n### Step 1.3 — Clean up empty placeholder entries\n\n```bash\ngrep -nE '^- \\[ \\] `{prefix}-[0-9]+`\\s*$' \"{todo_file_path}\"\n```\nIf any lines are returned, remove each with `Edit` using the exact line text as `old_string`. If the only entry removed was the last real line, treat the file as empty and start numbering from 1.\n\n### Step 1.4 — Increment\n\nIncrement the highest-seen number by 1; zero-pad to `cfg.number_digits` digits → `nnn`.\n\n### Step 1.5 — Build the ID\n\n- `ID = \"{prefix}-{nnn}\"` (e.g. `CBP-031`, `KHB-Todo-0010`, `FSD_Train-012`).\n- `prefix_lower` = `cfg.prefix_in_filename` if set, otherwise the prefix lowercased.\n- `slug` (only if the filename template uses it) = title lowercased, non-alphanumerics → `_`, collapsed runs of `_`, trimmed.\n\n### Step 1.6 — Pre-write uniqueness check\n\nBefore `ID` is used anywhere else, derive a format-agnostic match string from `cfg.todo_entry_template` rather than assuming a backtick wrapper. The template varies per project — most backtick-wrap the ID (`` `{ID}` ``), but e.g. `KHB`'s bolds it instead (`**{ID}**`) — and a hardcoded backtick produces a false \"0 hits\" (spurious retry, or a silently-skipped uniqueness check) on every run for any project whose format differs. Take the substring of `cfg.todo_entry_template` from its start through the delimiter(s) that immediately close `{ID}` (e.g. `` - [ ] `{ID}` `` for a backtick template, `- [ ] **{ID}**` for a bold template), substitute `{ID}` with the literal `ID`, and grep for that exact literal string:\n```bash\ngrep -cF \"{id_pattern}\" \"{todo_file_path}\"\n```\n- **0 hits** — `ID` is confirmed unique; continue to Step 2.\n- **≥1 hit** — a collision (stale arithmetic, or the lock was bypassed). Increment `nnn` by 1, rebuild `ID`, and re-run the check exactly once more.\n- **Second collision** — release the lock (`rmdir \"{lock_dir}\"`), then stop and ask the user how to proceed. Do not keep incrementing and guessing.\n\n## Step 2 — Get the title\n\nIf `$ARGUMENTS` is non-empty, use it. Otherwise ask:\n> What's the task title? (one-liner — I'll prompt for details next.)\n\nBefore adding, check for an existing entry with a similar title using:\n```bash\ngrep -i \"{keyword_from_title}\" \"{todo_file_path}\"\n```\nPick 2–3 distinctive words from the title as the keyword. If any lines are returned, surface them and ask whether to proceed, edit the existing one, or abandon.\n\n## Step 3 — Add the bullet to the todo file\n\nRender `cfg.todo_entry_template` by substituting `{ID}`, `{title}`, `{nnn}`, `{prefix_lower}`.\n\nInsertion point — use grep to find the right location without a full-file read:\n- If `cfg.insert_before_section` is set → run `grep -n \"{section_heading}\" \"{todo_file_path}\"` to get the line number. Then `Read` with `offset` set to ~5 lines before that line number and `limit` of ~10 lines to get enough context for a unique `Edit` match.\n- Else if `cfg.insert_under_section` is set → run `grep -n \"^##\" \"{todo_file_path}\"` to find section boundaries. Read ~10 lines around the target section's end to locate the last bullet.\n- Else → run `grep -c \"\" \"{todo_file_path}\"` to get the total line count, then `Read` with `offset` set to the last ~10 lines to find the final bullet.\n\nUse the `Edit` tool with enough surrounding context to be unique. Don't rewrite the whole file.\n\nIf you must append through Bash instead (for example, a main-tree write from a worktree session), first add a newline when the file does not end with one. Otherwise the new bullet joins the last line. Use a quoted heredoc, because the bullet holds backticks that double quotes would run as commands:\n```bash\n[ -n \"$(tail -c1 \"{todo_file_path}\")\" ] && printf '\\n' >> \"{todo_file_path}\"\ncat >> \"{todo_file_path}\" \u003C\u003C'EOF'\n{rendered bullet}\nEOF\n```\n\n**Post-write uniqueness re-grep** — immediately after the `Edit` completes, re-use the same `{id_pattern}` derived in Step 1.6 (do not fall back to a hardcoded backtick here either):\n```bash\ngrep -cF \"{id_pattern}\" \"{todo_file_path}\"\n```\n- **Exactly 1 hit** — the write is confirmed clean. Release the lock (`rmdir \"{lock_dir}\"`) — this is the happy-path release from Step 1.0 — then continue to Step 4.\n- **0 hits** — the `Edit` didn't land where expected (bad `old_string` match, wrong file, etc.). Retry the insertion once. If the retry also fails to produce exactly 1 hit, release the lock (`rmdir \"{lock_dir}\"`), then stop and ask the user.\n- **≥2 hits** — the ID already existed elsewhere in the file (the pre-write check in Step 1.6 missed it, or the lock was bypassed and a concurrent write landed in between). Release the lock (`rmdir \"{lock_dir}\"`), then stop and ask the user how to resolve the duplicate rather than silently leaving two entries with the same ID.\n\n## Step 4 — Gather task details\n\nSkip this step if `cfg.use_full_template` is `false`.\n\nAsk one question at a time (don't dump a form):\n\n1. **Source** — Where did this task come from? (release note, feedback, bug report, own idea). Omit the section if not applicable.\n2. **Summary** — What's changing and why? 1-3 sentences.\n3. **Assessment** — Current state? Where does the relevant content live? File paths and line numbers if you have them.\n4. **Plan** — Step-by-step implementation.\n5. **Acceptance Criteria** — `- [ ]` checkbox list of verification steps.\n\nIf the user says \"use your best judgment\", \"fill it in\", or similar, proceed without blocking — infer from the title and project context.\n\n## Step 5 — Write the task-detail file\n\nSkip this step if `cfg.use_full_template` is `false`.\n\nResolve the path:\n\n```\n{project_root}\u002F{cfg.task_dir}\u002F{rendered cfg.task_filename_template}\n```\n\nWrite this content (em-dash, not hyphen, in the H1):\n\n```markdown\n# {ID} — {title}\n\n## Source\n[Where this came from — or omit this section if not applicable]\n\n## Summary\n[1-3 sentences: what's changing and why]\n\n## Assessment\n[Current state of the relevant content. Does it exist? Where? What needs to change?]\n\n**Location:** `[file path]` — [section\u002Fline reference]\n\n## Plan\n\n[Step-by-step implementation]\n\n1. [Step one]\n2. [Step two]\n\n## Acceptance Criteria\n- [ ] [Verification step 1]\n- [ ] [Verification step 2]\n```\n\nIf `cfg.notes` mentions style cues (e.g. \"reference task-cbp-030.md\"), peek at that file first and match its tone.\n\n## Step 6 — Confirm completion\n\nIn one short response, report:\n- The project that was matched (and its root path).\n- The new `ID`.\n- That the bullet was appended to `cfg.todo_file`.\n- The task-detail file path (if one was created).\n\n## Conventions to honour\n\n- **Number format:** always zero-pad to `cfg.number_digits` digits.\n- **Filename casing:** lowercase the prefix unless `cfg.prefix_in_filename` overrides.\n- **Heading separator:** em-dash (`—`), not hyphen.\n- **Identifier casing in IDs:** preserve `cfg.prefix` exactly (e.g. `FSD_Train-012`, `KHB-Todo-0010`).\n- **One question at a time** when gathering details.\n\n## Guardrails\n\n- **Never duplicate titles** — scan existing entries before creating.\n- **Trust the file over the cfg.** If the file's existing entries clearly use a different format than `cfg.todo_entry_template`, follow the file and flag the drift to the user (the cfg may be stale).\n- **Don't modify other projects.** Only touch the matched project's files.\n- **Don't implement the task.** Only create planning artifacts; the user will do the work separately.\n- **Don't add a task-detail file when `cfg.use_full_template` is false.** Lightweight projects (e.g. hangman) intentionally keep the bullet alone.\n","# fsad-harness:add-task — multi-project task adder\n\nYou add a new task entry (and, where the project warrants it, a per-task detail file) in whichever local project the user is currently working in. Project-specific conventions are defined in `${CLAUDE_PLUGIN_ROOT}\u002Fskills\u002Fadd-task\u002Fadd-task-projects.yaml` — always read that file before doing anything else.\n\n`$ARGUMENTS` (if present) is the brief task title.\n\n**What \"done\" means for a task-creation run:**\n- The new task ID is unique in the todo file — verified with a re-grep, not assumed from arithmetic alone.\n- The new bullet matches the formatting of its sibling entries (`cfg.todo_entry_template`, or the file's actual format if it has drifted from the cfg).\n- Every acceptance criterion in the generated task-detail file (when one is created) is falsifiable — a future reader can check it against the codebase and get a clear pass\u002Ffail, not a vague judgment call.\n\nThe steps below exist to satisfy these criteria, not just to move mechanically from prefix to written bullet.\n\n## Asking the user\n\nThis rule covers every question this skill asks the user: a confirmation, a choice, a missing value, a numbered decision list, or a stop-and-ask. It applies even where a later step says \"ask the user\" or \"ask exactly once\". The user may watch the decisions pane rather than the chat, so a question that only goes to chat can go unanswered.\n\n- **When `mcp__decision-tracker__decision` is in your tool list** (loaded, or named as a deferred tool; load a deferred one with `ToolSearch` first), log each question before you ask it:\n  1. Call it with `action: \"open\"`, the question (ending in `?`), and `options` when the answers are a closed set. Set `required: false` when the work can go on without an answer. A numbered list of questions opens one decision per item. Keep each returned `id`.\n  2. Ask the question in chat as this skill says. The open call does not replace the chat question.\n  3. When the user answers, in chat or as a `Decision Dn: \u003Canswer>` message from the plugin, call the tool with `action: \"close\"`, the `id` and the answer.\n  4. When the change the answer asked for is in place, call it with `action: \"implement\"`, the `id` and a `path:line`.\n- **When the tool is absent**, ask in chat only.\n- An `AskUserQuestion` call is logged on its own. Do not open it again.\n- This rule changes where a question is logged. It does not change when this skill asks, or how many times.\n\n## Step 0 — Detect the project\n\n1. Determine the current working directory.\n2. Read `${CLAUDE_PLUGIN_ROOT}\u002Fskills\u002Fadd-task\u002Fadd-task-projects.yaml`.\n3. Match cwd against each project's `match_paths` (expand `~`). The cwd may be a descendant of a match path. If multiple match, prefer the longest (most specific) match.\n4. If a project matches, refer to its entry as `cfg` and proceed.\n5. If **no** project matches in `add-task-projects.yaml`:\n   - Tell the user the project is not registered.\n   - Show the values you'd use from the `default` block.\n   - Ask whether to (a) proceed using the defaults this once, (b) register the project now, or (c) abort.\n   - If (b), collect the following fields one at a time, showing the suggested default for each:\n     - `name` — project key in the YAML (e.g. `my_project`)\n     - `match_paths` — root path (default: current working directory)\n     - `prefix` — task identifier prefix (e.g. `MYP`)\n     - `number_digits` — zero-pad width (default: `3`)\n     - `todo_file` — relative path to the todo file (default: `planning\u002Fto do\u002Ftodo.md`)\n     - `task_dir` — relative path to the task detail directory (default: `planning\u002Fto do`)\n     - `use_full_template` — whether to create per-task detail files (default: `true`)\n     - `version_scheme` — versioning convention: `semver`, `integer`, `calver`, or `none` (default: `semver`)\n     - `version_files` — file(s) that carry the version string (default: `README.md`; user may specify `package.json` or others)\n   - Write all collected fields as a new entry under `projects:` in the YAML before continuing.\n\n## Step 0.4 — Skill routing\n\nResolve which skill should actually handle this task-creation run for `cfg`'s project:\n\n1. `desired_skill` = `cfg.skills.add_task` (if set) → else the YAML's top-level `defaults.skills.add_task` (if set) → else `\"fsad-harness:add-task\"`.\n2. If `desired_skill` differs from `\"fsad-harness:add-task\"`: invoke the `Skill` tool with that skill name, passing `$ARGUMENTS` unchanged, and **stop** — none of this skill's remaining steps run in this invocation.\n3. Otherwise continue to Step 1.\n\n## Step 1 — Compute the next number\n\n**Success criteria for this step:** the computed `ID` must be numerically correct (the highest existing number for `{prefix}` in the todo file, plus one) regardless of whether `{prefix}` itself contains a hyphen (e.g. `KHB-Todo`), and must be verified as unique in the todo file — not merely assumed — before it is ever written. Uniqueness is protected two ways: **mutual exclusion** (Step 1.0's lock — only one `add-task` invocation can be computing\u002Fwriting an ID at a time) and **detection** (Step 1.6's pre-write check and Step 3's post-write re-grep, which catch stale arithmetic if the lock is ever bypassed or a lock file is manually removed).\n\n### Step 1.0 — Acquire the allocation lock\n\nBefore touching the todo file, acquire an exclusive lock so two concurrent `add-task` invocations (two CLI sessions, an autonomous agent running alongside an interactive one, etc.) can never compute the same `ID` at once — this closes the race that Step 1.6\u002FStep 3's checks can only detect after the fact.\n\n1. Lock path: `{lock_dir} = \"{dirname(todo_file_path)}\u002F.add-task.lock\"` — the lock lives next to the project's `todo_file`, so it's naturally scoped per-project; two different projects' `add-task` runs never contend for the same lock.\n2. Attempt `mkdir \"{lock_dir}\"` and check the exit code. `mkdir` is atomic on POSIX filesystems — if the directory already exists, the command fails, so exactly one concurrent caller can ever succeed.\n   - **Exit 0 (acquired)** — continue to Step 1.1.\n   - **Nonzero (lock already held)**:\n     a. Check the lock directory's age: `find \"{lock_dir}\" -maxdepth 0 -mmin +1`.\n        - **Non-empty output (older than 60s, stale)** — a prior run crashed or was killed before releasing its lock. Remove it (`rmdir \"{lock_dir}\"`) and retry `mkdir` once. If that retry also fails, treat the lock as fresh and fall through to (b).\n        - **Empty output (60s or newer, fresh)** — another `add-task` invocation is genuinely in progress; go to (b).\n     b. **Fresh lock — poll and retry.** Wait ~2s, retry `mkdir`, up to 5 attempts (~10s total). If still unacquired after 5 attempts, stop and tell the user another `add-task` run appears to be in progress; ask whether to keep waiting (repeat the poll) or abort. **Do not `rmdir` the lock on this abort path** — this invocation never acquired it, so it isn't this invocation's to release; the directory the retries were failing against belongs to whichever session is still holding it. Do not proceed to Step 1.1 without the lock.\n3. **Release discipline.** The lock must be released on *every* exit path where **this invocation held it** — never left held by this invocation when control leaves Steps 1–3, whether by success or by a stop-and-ask. The specific release points are called out inline at each exit below: the happy-path release after Step 3's post-write re-grep, and a release immediately before the two stop-and-asks that can only be reached after this invocation has the lock (Step 1.6's second collision, Step 3's retry-fails). The poll-timeout abort above is the one exit path with nothing to release, precisely because the lock was never acquired.\n\n### Step 1.1 — Find every existing ID\n\nDo **not** read the full todo file. Use targeted Bash commands instead:\n\n```bash\ngrep -oE '{prefix}-[0-9]+' \"{todo_file_path}\"\n```\nSubstitute the literal prefix (e.g. `CBP`, `TBS`, `KHB-Todo`, `FSD_Train`) for `{prefix}` and the resolved absolute path for `{todo_file_path}`. Quote the path to handle spaces. If the output is empty, no tasks exist yet — start at 1 and skip to Step 1.3.\n\n### Step 1.2 — Parse the numeric suffix yourself — do not pipe through `sort`\n\n`sort -t- -k2 -n | tail -1` assumes the number always lands in field 2 when splitting on `-`. That assumption breaks the moment the prefix itself contains a hyphen: for `KHB-Todo-0010`, splitting on `-` gives fields `KHB` \u002F `Todo` \u002F `0010`, so field 2 is the literal string `\"Todo\"` on every matched line — `sort -n` can't numerically order a constant string, and `tail -1` just returns whichever line happens to sort last (effectively grep\u002Ffile order), not the actual highest number. Instead, for each line grep returned, split on the **last** `-` (not a fixed field position) to isolate the trailing digits regardless of how many hyphens the prefix contains, parse each as an integer, and take the max. This is you reasoning over the grep output directly — not another shell pipeline.\n\n### Step 1.3 — Clean up empty placeholder entries\n\n```bash\ngrep -nE '^- \\[ \\] `{prefix}-[0-9]+`\\s*$' \"{todo_file_path}\"\n```\nIf any lines are returned, remove each with `Edit` using the exact line text as `old_string`. If the only entry removed was the last real line, treat the file as empty and start numbering from 1.\n\n### Step 1.4 — Increment\n\nIncrement the highest-seen number by 1; zero-pad to `cfg.number_digits` digits → `nnn`.\n\n### Step 1.5 — Build the ID\n\n- `ID = \"{prefix}-{nnn}\"` (e.g. `CBP-031`, `KHB-Todo-0010`, `FSD_Train-012`).\n- `prefix_lower` = `cfg.prefix_in_filename` if set, otherwise the prefix lowercased.\n- `slug` (only if the filename template uses it) = title lowercased, non-alphanumerics → `_`, collapsed runs of `_`, trimmed.\n\n### Step 1.6 — Pre-write uniqueness check\n\nBefore `ID` is used anywhere else, derive a format-agnostic match string from `cfg.todo_entry_template` rather than assuming a backtick wrapper. The template varies per project — most backtick-wrap the ID (`` `{ID}` ``), but e.g. `KHB`'s bolds it instead (`**{ID}**`) — and a hardcoded backtick produces a false \"0 hits\" (spurious retry, or a silently-skipped uniqueness check) on every run for any project whose format differs. Take the substring of `cfg.todo_entry_template` from its start through the delimiter(s) that immediately close `{ID}` (e.g. `` - [ ] `{ID}` `` for a backtick template, `- [ ] **{ID}**` for a bold template), substitute `{ID}` with the literal `ID`, and grep for that exact literal string:\n```bash\ngrep -cF \"{id_pattern}\" \"{todo_file_path}\"\n```\n- **0 hits** — `ID` is confirmed unique; continue to Step 2.\n- **≥1 hit** — a collision (stale arithmetic, or the lock was bypassed). Increment `nnn` by 1, rebuild `ID`, and re-run the check exactly once more.\n- **Second collision** — release the lock (`rmdir \"{lock_dir}\"`), then stop and ask the user how to proceed. Do not keep incrementing and guessing.\n\n## Step 2 — Get the title\n\nIf `$ARGUMENTS` is non-empty, use it. Otherwise ask:\n> What's the task title? (one-liner — I'll prompt for details next.)\n\nBefore adding, check for an existing entry with a similar title using:\n```bash\ngrep -i \"{keyword_from_title}\" \"{todo_file_path}\"\n```\nPick 2–3 distinctive words from the title as the keyword. If any lines are returned, surface them and ask whether to proceed, edit the existing one, or abandon.\n\n## Step 3 — Add the bullet to the todo file\n\nRender `cfg.todo_entry_template` by substituting `{ID}`, `{title}`, `{nnn}`, `{prefix_lower}`.\n\nInsertion point — use grep to find the right location without a full-file read:\n- If `cfg.insert_before_section` is set → run `grep -n \"{section_heading}\" \"{todo_file_path}\"` to get the line number. Then `Read` with `offset` set to ~5 lines before that line number and `limit` of ~10 lines to get enough context for a unique `Edit` match.\n- Else if `cfg.insert_under_section` is set → run `grep -n \"^##\" \"{todo_file_path}\"` to find section boundaries. Read ~10 lines around the target section's end to locate the last bullet.\n- Else → run `grep -c \"\" \"{todo_file_path}\"` to get the total line count, then `Read` with `offset` set to the last ~10 lines to find the final bullet.\n\nUse the `Edit` tool with enough surrounding context to be unique. Don't rewrite the whole file.\n\nIf you must append through Bash instead (for example, a main-tree write from a worktree session), first add a newline when the file does not end with one. Otherwise the new bullet joins the last line. Use a quoted heredoc, because the bullet holds backticks that double quotes would run as commands:\n```bash\n[ -n \"$(tail -c1 \"{todo_file_path}\")\" ] && printf '\\n' >> \"{todo_file_path}\"\ncat >> \"{todo_file_path}\" \u003C\u003C'EOF'\n{rendered bullet}\nEOF\n```\n\n**Post-write uniqueness re-grep** — immediately after the `Edit` completes, re-use the same `{id_pattern}` derived in Step 1.6 (do not fall back to a hardcoded backtick here either):\n```bash\ngrep -cF \"{id_pattern}\" \"{todo_file_path}\"\n```\n- **Exactly 1 hit** — the write is confirmed clean. Release the lock (`rmdir \"{lock_dir}\"`) — this is the happy-path release from Step 1.0 — then continue to Step 4.\n- **0 hits** — the `Edit` didn't land where expected (bad `old_string` match, wrong file, etc.). Retry the insertion once. If the retry also fails to produce exactly 1 hit, release the lock (`rmdir \"{lock_dir}\"`), then stop and ask the user.\n- **≥2 hits** — the ID already existed elsewhere in the file (the pre-write check in Step 1.6 missed it, or the lock was bypassed and a concurrent write landed in between). Release the lock (`rmdir \"{lock_dir}\"`), then stop and ask the user how to resolve the duplicate rather than silently leaving two entries with the same ID.\n\n## Step 4 — Gather task details\n\nSkip this step if `cfg.use_full_template` is `false`.\n\nAsk one question at a time (don't dump a form):\n\n1. **Source** — Where did this task come from? (release note, feedback, bug report, own idea). Omit the section if not applicable.\n2. **Summary** — What's changing and why? 1-3 sentences.\n3. **Assessment** — Current state? Where does the relevant content live? File paths and line numbers if you have them.\n4. **Plan** — Step-by-step implementation.\n5. **Acceptance Criteria** — `- [ ]` checkbox list of verification steps.\n\nIf the user says \"use your best judgment\", \"fill it in\", or similar, proceed without blocking — infer from the title and project context.\n\n## Step 5 — Write the task-detail file\n\nSkip this step if `cfg.use_full_template` is `false`.\n\nResolve the path:\n\n```\n{project_root}\u002F{cfg.task_dir}\u002F{rendered cfg.task_filename_template}\n```\n\nWrite this content (em-dash, not hyphen, in the H1):\n\n```markdown\n# {ID} — {title}\n\n## Source\n[Where this came from — or omit this section if not applicable]\n\n## Summary\n[1-3 sentences: what's changing and why]\n\n## Assessment\n[Current state of the relevant content. Does it exist? Where? What needs to change?]\n\n**Location:** `[file path]` — [section\u002Fline reference]\n\n## Plan\n\n[Step-by-step implementation]\n\n1. [Step one]\n2. [Step two]\n\n## Acceptance Criteria\n- [ ] [Verification step 1]\n- [ ] [Verification step 2]\n```\n\nIf `cfg.notes` mentions style cues (e.g. \"reference task-cbp-030.md\"), peek at that file first and match its tone.\n\n## Step 6 — Confirm completion\n\nIn one short response, report:\n- The project that was matched (and its root path).\n- The new `ID`.\n- That the bullet was appended to `cfg.todo_file`.\n- The task-detail file path (if one was created).\n\n## Conventions to honour\n\n- **Number format:** always zero-pad to `cfg.number_digits` digits.\n- **Filename casing:** lowercase the prefix unless `cfg.prefix_in_filename` overrides.\n- **Heading separator:** em-dash (`—`), not hyphen.\n- **Identifier casing in IDs:** preserve `cfg.prefix` exactly (e.g. `FSD_Train-012`, `KHB-Todo-0010`).\n- **One question at a time** when gathering details.\n\n## Guardrails\n\n- **Never duplicate titles** — scan existing entries before creating.\n- **Trust the file over the cfg.** If the file's existing entries clearly use a different format than `cfg.todo_entry_template`, follow the file and flag the drift to the user (the cfg may be stale).\n- **Don't modify other projects.** Only touch the matched project's files.\n- **Don't implement the task.** Only create planning artifacts; the user will do the work separately.\n- **Don't add a task-detail file when `cfg.use_full_template` is false.** Lightweight projects (e.g. hangman) intentionally keep the bullet alone.\n",{"description":7,"argument-hint":47},"`[brief task title]`","main","bf3a412c985273327682c956ca35341c64ce9f8c","skills\u002Fadd-task",4,"https:\u002F\u002Fgithub.com\u002Ftbeack\u002Ffsad_playbook","https:\u002F\u002Ffsad-playbook.vercel.app","https:\u002F\u002Fgithub.com\u002Ftbeack","2026-11-13 22:07:09",{"source":8,"skillName":5,"commands":57,"prompt":67,"zipAvailable":68},[58,61,64],{"tool":59,"command":60},"npx","npx skills add tbeack\u002Ffsad_playbook --skill add-task",{"tool":62,"command":63},"bunx","bunx skills add tbeack\u002Ffsad_playbook --skill add-task",{"tool":65,"command":66},"pnpm","pnpm dlx skills add tbeack\u002Ffsad_playbook --skill add-task","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Ftbeack\u002Ffsad_playbook\u002Fblob\u002Fmain\u002Fskills\u002Fadd-task\u002FSKILL.md",true,[70],{"path":71,"resourceType":72,"resourceTypeDesc":73,"sizeBytes":74,"sourceHtmlUrl":75},"add-task-projects.yaml","REFERENCE","参考文档",3447,"https:\u002F\u002Fgithub.com\u002Ftbeack\u002Ffsad_playbook\u002Fblob\u002Fmain\u002Fskills\u002Fadd-task\u002Fadd-task-projects.yaml",[77,97,128,147,168,196],{"skillNo":78,"name":79,"nameZh":6,"description":80,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":81,"categories":82,"keywords":83,"publishedAt":42},"01M6K7W5RTRMRQMWAGXNSX8DM6","sync","Pre-flight sync check for registered projects. Fetches from remote, reports uncommitted changes, and handles four states — up-to-date, ahead, behind (warns + pulls), or diverged (warns, no pull). Supports single-project (cwd detection), named-project (fsad-harness:sync key1 key2), and full-sync (fsad-harness:sync ALL) modes.","https:\u002F\u002Fgithub.com\u002Ftbeack\u002Ffsad_playbook\u002Fblob\u002Fmain\u002Fskills\u002Fsync\u002FSKILL.md",[],[79,84,85,86,37,87,88,89,29,90,91,92,93,94,95,96],"pull","harness","detection","check","up","reports","full","remote","states","for","changes","modes","single",{"skillNo":98,"name":99,"nameZh":6,"description":100,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":101,"categories":102,"keywords":108,"publishedAt":42},"01M6K7VY55SWBDD379T9JZVF16","spec-review","Run a detailed, adversarial multi-agent review of a written proposal or specification (spec.md, PRD, RFC, design doc, architecture proposal, business case) — reviews the document, not code. Runs a steelman\u002Finventory pass, then up to 10 specialist lenses (logic, assumptions, evidence, completeness, feasibility, alternatives, consistency, stakeholders, testability, risk), then adversarial validation that defaults to refuting each finding unless it survives, then a loop-until-dry completeness pass. Writes a single severity-ranked SPEC-REVIEW.md next to the document. Use when the user says \"review this spec\", \"adversarial review\", \"poke holes in this proposal\", \"red team this PRD\", \"critique this design doc\", \"what's wrong with this RFC\", \"stress test this plan\", or similar. Review-only — never edits the document.","https:\u002F\u002Fgithub.com\u002Ftbeack\u002Ffsad_playbook\u002Fblob\u002Fmain\u002Fskills\u002Fspec-review\u002FSKILL.md",[103],{"categoryNo":104,"categoryName":105,"taxonomyType":106,"taxonomyTypeDesc":107},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[109,110,111,112,113,114,115,116,117,118,119,88,120,121,122,123,124,125,126,127,24],"review","spec","spec review","design","architecture","code","inventory","team","loop","never","only","specification","poke","deep","dry","proposal","lens","quick","reviews",{"skillNo":129,"name":130,"nameZh":6,"description":131,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":132,"categories":133,"keywords":134,"publishedAt":42},"01M6K7VVD3B0SM3VKJ5W26QPPV","ship","Short alias for \u002Ffsad-harness:ship-it. Wrap up finished work, cut a version, commit, push, and open a PR.","https:\u002F\u002Fgithub.com\u002Ftbeack\u002Ffsad_playbook\u002Fblob\u002Fmain\u002Fskills\u002Fship\u002FSKILL.md",[],[130,135,136,85,137,138,139,140,141,142,88,143,144,145,146,93],"commit","pr","push","open","wrap up","work","short","version","wrap","alias","cut","it",{"skillNo":148,"name":149,"nameZh":6,"description":150,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":151,"categories":152,"keywords":153,"publishedAt":42},"01M6K7VP2QDRNX57KP2RH10FAW","ship-it","Wrap up a finished round of work — verify README and CHANGELOG are current, cut a version if warranted, move completed task files into the completed\u002F folder, then create a feature branch if needed, commit, push, and open a PR. Run after `\u002Ffsad-harness:do-task` has marked one or more tasks done. Takes no arguments.","https:\u002F\u002Fgithub.com\u002Ftbeack\u002Ffsad_playbook\u002Fblob\u002Fmain\u002Fskills\u002Fship-it\u002FSKILL.md",[],[130,154,146,135,155,156,138,157,158,140,159,160,88,161,162,163,164,165,91,166,167],"ship it","tasks","create","Bash","gh pr","branch","fetch","log","files","done","checkout","move","ls","parse",{"skillNo":169,"name":170,"nameZh":6,"description":171,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":172,"categories":173,"keywords":174,"publishedAt":42},"01M6K7VEK1C0MK5BNDHV8S6HCA","set-context","Gather codebase and initiative context before planning starts — runs a graphify build or query when graphify is available locally, falls back to a manual repo sweep otherwise, then asks targeted questions and writes a single context.md. Invocable standalone or as fsad-harness:plan's mandatory Phase 0. Use when the user says \"set context\", \"gather context\", or as the first step of fsad-harness:plan.","https:\u002F\u002Fgithub.com\u002Ftbeack\u002Ffsad_playbook\u002Fblob\u002Fmain\u002Fskills\u002Fset-context\u002FSKILL.md",[],[175,176,177,178,85,179,180,181,182,183,184,34,185,186,187,188,189,190,191,192,193,96,194,195,24],"SET","context","set context","planning","first","graphify","plan","sweep","repo","user","build","back","query","as","questions","manual","standalone","path","codebase","phase","gather",{"skillNo":197,"name":198,"nameZh":6,"description":199,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":200,"categories":201,"keywords":206,"publishedAt":42},"01M6K7V17M3MQ92QZ99G294RYM","sec-review-team","Run a multi-agent security review TEAM over a codebase or recent diff. Picks a specialist roster based on detected stack signals (webapp \u002F desktop \u002F iac \u002F llm-agent \u002F container \u002F backend \u002F mobile \u002F etc.) from a library of 13 specialists. Each specialist writes structured JSONL + prose findings in parallel; input-validation-auditor and auth-authz-auditor run a loop-until-dry consensus fan-out (2-8 passes); every deduped finding is adversarially verified by a dedicated validator agent before it can appear in the report. Orchestrator consolidates into a single severity-ranked REPORT.md with deduped root issues, archived per-run under a known-findings.jsonl ledger that drives auto re-review. Distinct from the built-in `\u002Fsecurity-review` (which is a single-pass branch review). Use when the user says \"multi-agent security review\", \"security review team\", \"sec-review team\", \"audit this branch with a team\", \"pre-release hardening team pass\", or similar. Review-only — no fixes.","https:\u002F\u002Fgithub.com\u002Ftbeack\u002Ffsad_playbook\u002Fblob\u002Fmain\u002Fskills\u002Fsec-review-team\u002FSKILL.md",[202,205],{"categoryNo":203,"categoryName":204,"taxonomyType":106,"taxonomyTypeDesc":107},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":104,"categoryName":105,"taxonomyType":106,"taxonomyTypeDesc":107},[109,116,207,208,209,210,211,212,213,117,214,159,215,33,216,123,217,218,219,220,221],"sec review team","review team","agent security","security review","run","release","security-review","stack","consensus","mobile","input","re","jsonl","which","verified"]