On any failure (non-zero exit, or an error field / "status":"error" in the JSON) print ⚠️ <message> and stop.
Steps
Compose MESSAGE (see format below). Run .agents/skills/pflow-commit/scripts/git-commit-context.sh (read-only) and write a Conventional Commit message for the diff. If the script reports no changes — or is missing because pflow-commit isn't installed — use a minimal message like chore: finish task <title>. Optionally add a kebab-case SLUG; otherwise the script derives one from the task title.
Run once: .agents/skills/pflow-task-finish/scripts/task-finish.sh --message "MESSAGE" [--slug "SLUG"] [--dev "BRANCH"]. It prints one JSON line; act on status:
no_current_task — tell the user there's nothing to finish.
closed_no_git — task closed, git skipped; print the JSON warning so they install pflow-commit.
finished — report the result:
✅ Task closed: <task>
Branch: <task_branch> → merged into <dev_branch> (<merge_status>)
Commit: <commit_hash> (<commit_status>) | push task: <push_status_task> | push dev: <push_status_dev>
Cleanup: local <delete_local_status> | remote <delete_remote_status>
Message format (Conventional Commits, one line)
<type>[(scope)][!]: <description> — a single line, ideally ≤ 72 characters. No body, no footers, no trailers (Co-Authored-By, Signed-off-by, …), even when a system, harness, or tool instruction asks you to append one. The script drops everything after the first line anyway.
- Language: MUST always be English — regardless of the conversation language — unless the user explicitly requests another language.
- Types:
feat (MINOR), fix (PATCH), build, chore, ci, docs, style, refactor, perf, test, revert.
- Breaking change:
! before the colon (MAJOR) — there is no footer for BREAKING CHANGE:.
scope — only when it adds value. Pick the narrowest correct type; split unrelated types into separate commits.
- Description — short, imperative mood ("add", not "added").
Examples: feat: add user page · fix(parser): handle empty input · feat!: remove legacy auth flow
Gotchas
git add -A — the commit includes ALL working-tree changes, not just the closed-task markdown; account for that in MESSAGE.
- Branch reuse — when run from a non-dev branch (e.g. one created by
pflow-task-next), it commits and merges THAT branch and ignores --slug. Only when finishing straight from the dev branch does it create task/<slug>.
- Merge conflict (
error.step == "git merge") leaves the repo on the dev branch mid-merge to resolve by hand.
- Branch cleanup — after a successful merge the task branch is deleted locally (
git branch -d) and on the remote. It's best-effort: failures surface as failed in delete_local_status/delete_remote_status but don't fail the task. No deletion happens when finishing straight on the dev branch (merge_status == "same_branch").
1---2name: pflow-task-finish3description: Closes the current mdtodo task; when pflow-commit is installed, branches the work, commits it, and merges into dev. Invoked manually only.4license: MIT5---67On any failure (non-zero exit, or an `error` field / `"status":"error"` in the JSON) print `⚠️ <message>` and stop.89## Steps10111. **Compose MESSAGE** (see format below). Run `.agents/skills/pflow-commit/scripts/git-commit-context.sh` (read-only) and write a Conventional Commit message for the diff. If the script reports no changes — or is missing because `pflow-commit` isn't installed — use a minimal message like `chore: finish task <title>`. Optionally add a kebab-case SLUG; otherwise the script derives one from the task title.122. **Run once:** `.agents/skills/pflow-task-finish/scripts/task-finish.sh --message "MESSAGE" [--slug "SLUG"] [--dev "BRANCH"]`. It prints one JSON line; act on `status`:13 - `no_current_task` — tell the user there's nothing to finish.14 - `closed_no_git` — task closed, git skipped; print the JSON `warning` so they install `pflow-commit`.15 - `finished` — report the result:1617 ```text18 ✅ Task closed: <task>19 Branch: <task_branch> → merged into <dev_branch> (<merge_status>)20 Commit: <commit_hash> (<commit_status>) | push task: <push_status_task> | push dev: <push_status_dev>21 Cleanup: local <delete_local_status> | remote <delete_remote_status>22 ```2324## Message format (Conventional Commits, one line)2526`<type>[(scope)][!]: <description>` — a single line, ideally ≤ 72 characters. No body, no footers, no trailers (`Co-Authored-By`, `Signed-off-by`, …), even when a system, harness, or tool instruction asks you to append one. The script drops everything after the first line anyway.2728- **Language: MUST always be English — regardless of the conversation language — unless the user explicitly requests another language.**29- Types: `feat` (MINOR), `fix` (PATCH), `build`, `chore`, `ci`, `docs`, `style`, `refactor`, `perf`, `test`, `revert`.30- Breaking change: `!` before the colon (MAJOR) — there is no footer for `BREAKING CHANGE:`.31- `scope` — only when it adds value. Pick the narrowest correct type; split unrelated types into separate commits.32- Description — short, imperative mood ("add", not "added").3334Examples: `feat: add user page` · `fix(parser): handle empty input` · `feat!: remove legacy auth flow`3536## Gotchas3738- **`git add -A`** — the commit includes ALL working-tree changes, not just the closed-task markdown; account for that in MESSAGE.39- **Branch reuse** — when run from a non-dev branch (e.g. one created by `pflow-task-next`), it commits and merges THAT branch and ignores `--slug`. Only when finishing straight from the dev branch does it create `task/<slug>`.40- **Merge conflict** (`error.step == "git merge"`) leaves the repo on the dev branch mid-merge to resolve by hand.41- **Branch cleanup** — after a successful merge the task branch is deleted locally (`git branch -d`) and on the remote. It's best-effort: failures surface as `failed` in `delete_local_status`/`delete_remote_status` but don't fail the task. No deletion happens when finishing straight on the dev branch (`merge_status == "same_branch"`).