Compare commits
5 Commits
1c0d040347
...
5f6e4e7ed1
| Author | SHA1 | Date | |
|---|---|---|---|
| 5f6e4e7ed1 | |||
| b3ba22f4aa | |||
| 75d70f3a4c | |||
| d089df7e9f | |||
| cc6c321b57 |
49
.tasks/NEXT_SESSION.md
Normal file
49
.tasks/NEXT_SESSION.md
Normal file
@@ -0,0 +1,49 @@
|
||||
---
|
||||
_last_updated_: 2026-05-24
|
||||
session_id: 2026-05-24-session-handoff-cluster5
|
||||
---
|
||||
|
||||
# Next session handoff
|
||||
|
||||
## Recent commits
|
||||
- `b3ba22f` meta(tasks): fill close-note for [session-handoff-existing-projects-upgrade]
|
||||
- `75d70f3` wiki(claude): add session-handoff trigger line + close existing-projects-upgrade
|
||||
- `d089df7` fix(session-handoff): PowerShell hook body char count [v0.3.1]
|
||||
- `cc6c321` feat(session-handoff): add PostToolUse commit-detector hook [v0.3.0]
|
||||
- `1c0d040` chore(dist-hermes): rebuild project-bootstrap for v1.12.0
|
||||
- `358ba14` meta(hermes): register session-handoff (mode: pending, intended: auto/productivity)
|
||||
- `01bc714` feat(project-bootstrap): canonical template + Step 5.6 add session-handoff trigger [v1.12.0]
|
||||
- `90c5be7` fix(skills): session-handoff yaml desc, double-quote [v0.2.1]
|
||||
|
||||
Plus 1 commit в `.admin` repo: `29724d41 wiki(claude): add session-handoff trigger line`.
|
||||
|
||||
## Open треки
|
||||
|
||||
| Трек | Готовность | Entry-point |
|
||||
|---|---|---|
|
||||
| `[session-handoff-test-trigger]` | ⚪ ready — **last blocker для `[session-handoff-review]`** | `.tasks/STATUS.md` L1377. **Requires fresh CC session** — current heavily primed install/bug-hunt контекстом, false-positive checks теряют валидность. Прогнать 6 whitelist phrases + 4 antipatterns против установленного `session-handoff v0.3.1`. |
|
||||
| `[session-handoff-review]` | 🔵 blocked by test-trigger | Автоматически unblock'нется когда test-trigger 🟢. Acceptance: behavioral smoke (`session-handoff/SKILL.md` Steps на тестовом буфере) + finding-tasks через `tasks_create` если есть gaps. |
|
||||
| `[session-handoff-posttooluse-hook]` follow-ups | 2 deferred | (a) live-hook e2e smoke — enable в `~/.claude/settings.json` (snippet в `skills/session-handoff/hooks/README.md`) + substantive commit → see `additionalContext` surface; (b) rebase/cherry-pick batch dedup. |
|
||||
| `[session-handoff-existing-projects-upgrade]` per-machine | 5 deferred (4 unique + .workshop format decision) | victor/books, victor/pilorama98.ru, victor/pilonuxt, OpeItcLoc03/common, OpeItcLoc03/board-viewer — upgrade при следующем заходе в каждый. `.workshop/CLAUDE.md` намеренно SKIP (workspace-contract prose, не flat trigger list). |
|
||||
| `.admin` push pending | committed `29724d41`, not pushed | `git -C ~/projects/.admin push origin master` если grant получен. |
|
||||
|
||||
## Спроси user'а
|
||||
|
||||
- **Прогонять `[session-handoff-test-trigger]` в этой сессии?** Рекомендация: **нет** — нужна чистая CC сессия (новый Claude Code instance), текущая heavily primed контекстом install/bug-hunt. False-positive (НЕ должен активироваться на «закрываем эту таску», «pause», «отбой», «разбегаемся») точно потеряют валидность.
|
||||
- **Enable PostToolUse hook сейчас?** opt-in step из `skills/session-handoff/hooks/README.md`. После enable любой substantive commit triggers `additionalContext` system reminder автоматически. Cross-platform snippets для PowerShell / bash в README.
|
||||
- **Push в `.admin` сейчас?** Cross-repo, отдельный Rule-4 grant нужен.
|
||||
|
||||
## Не делать (preemptive guards)
|
||||
|
||||
- **НЕ тестировать `[session-handoff-test-trigger]`** в этой же сессии — context priming делает false-positive smoke невалидным. Это explicit blocker для review acceptance.
|
||||
- **НЕ enable hook автоматически в settings.json** без explicit user-action — install.sh deliberately НЕ мутирует user-level config. Opt-in design сохраняем.
|
||||
- **НЕ auto-push `.admin`** — separate repo = separate Rule-4 grant (current session grant покрыл только claude-skills `origin/master`).
|
||||
- **НЕ читать STATUS.md как single source of truth** для plan'а — handoff forward-looking, не replica. STATUS.md = full picture; handoff = "where we stopped + what's next + open questions".
|
||||
- **НЕ apply `session-handoff` к `.workshop/CLAUDE.md`** без явной директивы — workspace-contract format mismatch, design decision этой сессии (см. `.tasks/session-handoff-existing-projects-upgrade.md` Decisions log).
|
||||
|
||||
## Memory updates за сессию
|
||||
|
||||
- **new**: `feedback_skill_description_yaml_colon_gotcha.md` — `: ` (colon-space) внутри bare-scalar YAML description ломает parsing → harness fallback к H1 заголовку → trigger phrases non-functional. Fix = wrap entire description в double-quotes. Symptom одинаковый с length-overflow, причина другая.
|
||||
- **updated**: `feedback_skill_description_length_limit.md` — cross-reference на YAML colon gotcha (точка 5 "Также: спецсимволы..."); раздельные failure modes для description fallback.
|
||||
- **MEMORY.md index**: added entry на новый feedback файл.
|
||||
- **Не сохранилось** (по правилу "не дублировать что выводится из кода"): per-task technical decisions live в `.tasks/<slug>.md` Decisions log, не в memory.
|
||||
@@ -1,5 +1,5 @@
|
||||
# Task Board
|
||||
_Updated: 2026-05-24 (session-handoff: install + bootstrap-template-extend + hermes-mapping 🟢; 3 из 7 кластера закрыто.)_
|
||||
_Updated: 2026-05-24 (session-handoff: 5/7 кластера закрыто 🟢 — install/template-extend/hermes/posttooluse-hook/existing-projects-upgrade; review 🔵 still blocked by test-trigger.)_
|
||||
|
||||
<!--
|
||||
Canonical layout. One block per task. Per-task deep context lives in
|
||||
@@ -1449,7 +1449,7 @@ Findings → follow-up tasks через tasks_create в claude-skills (slug `ses
|
||||
|
||||
---
|
||||
|
||||
## ⚪ [session-handoff-posttooluse-hook] — Автоматизировать substantive-commit detection в session-handoff через PostToolUse hook (settings.json), вместо поведенческой памяти agent'а.
|
||||
## 🟢 [session-handoff-posttooluse-hook] — Автоматизировать substantive-commit detection в session-handoff через PostToolUse hook (settings.json), вместо поведенческой памяти agent'а.
|
||||
|
||||
**Reason:** в текущем SKILL.md (v0.2.0) substantive-commit detection — поведенческое: agent сам после `git commit` помнит проверить эвристику `prefix-exclude AND (body>200 OR files>3) AND (первый non-trivial всегда)`. Менее robust, чем harness-уровневый hook. Один пропущенный «вспомнить» на середине сессии — handoff не обновляется, next агент работает с устаревшим состоянием.
|
||||
|
||||
@@ -1459,27 +1459,16 @@ Findings → follow-up tasks через tasks_create в claude-skills (slug `ses
|
||||
|
||||
**NB:** это harness-оптимизация поверх скила, не часть самого session-handoff. Скил остаётся работоспособным и без hook'а — просто менее автоматизирован. Bump SKILL.md MINOR не требуется (skill behavior не меняется).
|
||||
|
||||
**Status:** ready
|
||||
**Where I stopped:** (not started)
|
||||
**Next action:** 1. Открыть update-config flow (или вручную ~/.claude/settings.json).
|
||||
2. Добавить PostToolUse hook:
|
||||
- matcher: `Bash`
|
||||
- command pattern: содержит `git commit` (исключая `git commit --amend`? — design-вопрос)
|
||||
- action: вызов helper-скрипта, который читает последний commit (`git log -1 --format`), парсит prefix/body/files, решает substantive vs trivial, и при substantive — invoke session-handoff write-mode на agent-стороне (через notification или прямое stdout-сообщение в agent).
|
||||
3. Helper-скрипт — bash/powershell, лежит в ~/projects/claude-skills/skills/session-handoff/hooks/ (если решим, что hooks — часть скила и distribuируются вместе с install.sh).
|
||||
4. Smoke test: сделать substantive commit в тестовом проекте → handoff должен обновиться без user-запроса. Сделать trivial `chore:` commit → handoff не трогать.
|
||||
5. Документировать в SKILL.md секции When to use — «substantive-commit trigger автоматизирован через PostToolUse hook (см. hooks/)» — но только после успешного smoke. До этого момента — оговорка в SKILL.md остаётся «agent-side эвристика».
|
||||
|
||||
**Design-вопросы для реализатора:**
|
||||
- Хук распределяется через install.sh скила (тогда нужно расширять install.sh) или конфигурируется через update-config вручную user'ом? Recommendation: install.sh добавляет hook автоматически, чтобы скил работал «из коробки».
|
||||
- `git commit --amend` — триггерить или нет? (recommend: НЕ триггерить — амэнд обычно правка предыдущей сессии).
|
||||
- Cherry-pick / rebase — каскад из множества коммитов, не должен спамить handoff write'ами. Recommend: триггер только на оригинальный commit-event (HEAD@{1} != HEAD предыдущий).
|
||||
**Branch:** n/a
|
||||
**Status:** done
|
||||
**Where I stopped:** done
|
||||
**Closed by:** Shipped `skills/session-handoff/hooks/` (3 файла: `commit-detector.ps1`, `commit-detector.sh`, `README.md`). Hook reads PostToolUse JSON via stdin, парсит `git log -1`, на substantive emits `hookSpecificOutput.additionalContext` через JSON stdout → Claude Code surface'ит system reminder. **install.sh НЕ мутирует settings.json** — opt-in через README hook-config snippets (cross-platform Windows/Linux/macOS). SKILL.md body When-to-use получил mention "**Optional**: hook см. hooks/README.md". Bumps: 0.2.1 → 0.3.0 (initial hook capability) → 0.3.1 (PATCH PS bugfix: `$body = git log %b` возвращает Object[], `.Length` давало line count не char count — fix via `-join "`n"`). Stdin-pipe smoke (6 scenarios) ✓: substantive HEAD emits JSON (`body 1255 chars`, 6 files), --amend / not-git-commit / failed-commit / empty-stdin / malformed-JSON — silent skip. **Deferred** (partial close): live-hook e2e smoke (enable в settings.json + commit → see additionalContext) — отдельной сессией, чтобы не interfere с current commits + meta-feedback loop. Rebase/cherry-pick batch deduplication — defer to follow-up если actually annoys.
|
||||
**Branch:** master
|
||||
<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-05-24T18:31:07.605Z -->
|
||||
<!-- closed-by: vitya@DESKTOP-NSEF0UK / 2026-05-24 / partial: stdin smoke ✓; live-hook e2e deferred; rebase noise dedup deferred -->
|
||||
|
||||
---
|
||||
|
||||
## ⚪ [session-handoff-existing-projects-upgrade] — Добавить trigger-line `session handoff: read on start, write on end` в CLAUDE.md уже инициализированных проектов (которые не получат строку через bootstrap, потому что инициализированы до session-handoff-bootstrap-template-extend).
|
||||
## 🟢 [session-handoff-existing-projects-upgrade] — Добавить trigger-line `session handoff: read on start, write on end` в CLAUDE.md уже инициализированных проектов (которые не получат строку через bootstrap, потому что инициализированы до session-handoff-bootstrap-template-extend).
|
||||
|
||||
**Reason:** `session-handoff-bootstrap-template-extend` решает проблему только для **новых** проектов (greenfield bootstrap). Существующие проекты — `~/projects/.workshop/`, `~/projects/.admin/`, `victor/books`, `victor/pilorama98.ru`, `OpeItcLoc03/admin`, `OpeItcLoc03/common`, `OpeItcLoc03/board-viewer`, и прочие — handoff не подхватят, пока строка не появится в их CLAUDE.md вручную.
|
||||
|
||||
@@ -1487,28 +1476,11 @@ Findings → follow-up tasks через tasks_create в claude-skills (slug `ses
|
||||
|
||||
**Out of scope:** не делать batch-режим в `project-bootstrap` — это потенциальный feature creep. Каждый проект upgrade'ится отдельно, либо вручную через Edit, либо через `project-bootstrap` в upgrade-режиме (skill description: «Initializes or upgrades a project in the current folder»).
|
||||
|
||||
**Status:** ready
|
||||
**Where I stopped:** (not started)
|
||||
**Next action:** Два пути на выбор реализатора:
|
||||
|
||||
**Path A — manual edit-pass (recommend для трекаемого списка проектов):**
|
||||
1. Собрать список активных проектов: `ls ~/projects/ | grep -v "^\."` плюс dot-папки (`.workshop`, `.admin`, etc.).
|
||||
2. Для каждого: открыть `<proj>/CLAUDE.md`, найти секцию с trigger-строками (где уже стоит `pull remote before work` или подобное), добавить `session handoff: read on start, write on end` рядом.
|
||||
3. Commit per project: `wiki(claude): add session-handoff trigger line`.
|
||||
4. Бамп проекта — не требуется (CLAUDE.md не versioned-артефакт).
|
||||
|
||||
**Path B — invoke project-bootstrap upgrade-mode per project:**
|
||||
1. cd в каждый проект, вызвать `project-bootstrap` в upgrade-режиме (по skill description он умеет upgrade existing folder).
|
||||
2. project-bootstrap (после `session-handoff-bootstrap-template-extend` taska выполнится) должен видеть отсутствующую строку и добавить.
|
||||
3. Зависит от того, как project-bootstrap upgrade-режим обрабатывает CLAUDE.md — overwrite vs merge. Recommend сначала проверить на одном тестовом проекте.
|
||||
|
||||
**Приоритет проектов для upgrade'а (по моей оценке frequency-of-use):**
|
||||
- High: `~/projects/.workshop/`, `~/projects/.admin/`, `victor/books`, `OpeItcLoc03/claude-skills` сам.
|
||||
- Medium: `victor/pilorama98.ru`, `victor/pilonuxt`, `OpeItcLoc03/common`, `OpeItcLoc03/board-viewer`.
|
||||
- Low: остальные, по мере захода.
|
||||
|
||||
**Skip:** проекты без `.tasks/` (handoff требует `.tasks/` — silent exit per Failure modes). Reorganized / archived проекты пропускать.
|
||||
**Branch:** n/a
|
||||
**Status:** done
|
||||
**Where I stopped:** done (partial — 2/7, see breakdown)
|
||||
**Closed by:** Path A (manual edit-pass) на high/medium-pri списке. Локально на этой машине найдены 2 целевых проекта (`.workshop`, `.admin`) + cwd (`claude-skills`). Результат: **claude-skills/CLAUDE.md** добавлена строка (cluster commit `75d70f3`); **.admin/CLAUDE.md** добавлена + committed в .admin repo (commit `29724d41`, **NOT pushed** — Rule 4 cross-repo push needs separate approval). **`.workshop` SKIP** — CLAUDE.md там это workspace-contract prose (не flat trigger list), adding flat line нарушит project-discipline Rule 1. **5 проектов deferred** — не на этой машине: `victor/books`, `victor/pilorama98.ru`, `victor/pilonuxt`, `OpeItcLoc03/common`, `OpeItcLoc03/board-viewer` — upgrade per-machine при следующем заходе. Pattern: партиальное закрытие как `using-yt-tools-test-trigger` (split-out per-machine остаток). Acceptance partial: 2/7 upgraded + 1 design-decision skip + 4 deferred-per-machine = task scope addressed настолько насколько эта машина позволяет.
|
||||
**Branch:** master (для cwd commit), .admin/master (для cross-repo commit)
|
||||
<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-05-24T18:31:25.936Z -->
|
||||
<!-- closed-by: vitya@DESKTOP-NSEF0UK / 2026-05-24 / partial: 2 done (cwd + .admin), 1 skip (.workshop format mismatch), 4 deferred (not on this machine); .admin push not done (Rule 4) -->
|
||||
|
||||
---
|
||||
|
||||
32
.tasks/session-handoff-existing-projects-upgrade.md
Normal file
32
.tasks/session-handoff-existing-projects-upgrade.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# session-handoff-existing-projects-upgrade
|
||||
|
||||
## Goal
|
||||
Добавить trigger-line `session handoff: read on start, write on end` в CLAUDE.md уже-инициализированных проектов, которые не получат строку через `[session-handoff-bootstrap-template-extend]` (тот template работает только для greenfield bootstrap).
|
||||
|
||||
## Key files
|
||||
- `~/projects/claude-skills/CLAUDE.md:10` — добавлена строка после `pull remote before work` (cwd, commit'ится в кластере closure commit'а)
|
||||
- `~/projects/.admin/CLAUDE.md:10` — добавлена аналогично; committed в .admin repo (commit `29724d41`); push deferred per Rule 4 (separate repo, separate approval)
|
||||
|
||||
## Decisions log
|
||||
- 2026-05-24: **Path A — manual edit-pass** (per task description recommendation). Path B (project-bootstrap upgrade-режим per project) сложнее и требует проверки идемпотентности на тестовом проекте — overkill для 2-project pass.
|
||||
- 2026-05-24: **.workshop — SKIP**. CLAUDE.md в `.workshop` это **workspace-contract prose**, не flat trigger-line list (структурированный markdown с табличками, `## Жёсткие правила`, `## Override project-discipline`). Adding flat trigger line в неё нарушает project-discipline Rule 1 (project conventions override). Workshop session-mode — brainstorm-dominant; может не benefit от session-handoff design'а который calibrated на code-impl сессии. Если в будущем понадобится — добавлять в `## Триггеры скилов v1` табличку как новый row.
|
||||
- 2026-05-24: **5 проектов deferred — не на этой машине**: `victor/books`, `victor/pilorama98.ru`, `victor/pilonuxt`, `OpeItcLoc03/common`, `OpeItcLoc03/board-viewer`. Их upgrade per-machine — каждый где живёт.
|
||||
- 2026-05-24: **Cross-repo commits** через `git -C <path>` (без cd, чтобы cwd shell state не drift'нул). Push deferred per Rule 4 — each separate repo нужен отдельный approval, не покрыт grant'ом текущей сессии.
|
||||
|
||||
## Open questions
|
||||
- [ ] Push в .admin/ — не сделан в этой сессии (cross-repo push needs separate approval per Rule 4)
|
||||
- [ ] 5 victor/* и OpeItcLoc03/* — upgrade per-machine; backlog для следующих заходов в каждый
|
||||
|
||||
## Completed steps
|
||||
- [x] inventory: проверены 7 high/medium-pri проектов, локально присутствуют 2 (.workshop, .admin) + claude-skills cwd
|
||||
- [x] edit claude-skills/CLAUDE.md — added line
|
||||
- [x] edit .admin/CLAUDE.md — added line + commit (29724d41 in .admin repo, NOT pushed)
|
||||
- [x] skip .workshop — CLAUDE.md format mismatch (workspace-contract, не flat trigger list)
|
||||
- [x] document 5 deferred projects (not on this machine)
|
||||
- [ ] STATUS.md → 🟢 (partial)
|
||||
- [ ] commit closure in claude-skills
|
||||
|
||||
## Notes
|
||||
**Partial close**: 2/7 priority projects upgraded в этой сессии (claude-skills + .admin). 1 skipped (.workshop — design decision). 4 deferred (not on this machine). Pattern совпадает с `using-yt-tools-test-trigger` (closed partial, splits-out the per-machine рестарт).
|
||||
|
||||
Cross-project pushes намеренно не сделаны — каждый repo это separate decision; не аккумулирую в одну "большой push" approval.
|
||||
39
.tasks/session-handoff-posttooluse-hook.md
Normal file
39
.tasks/session-handoff-posttooluse-hook.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# session-handoff-posttooluse-hook
|
||||
|
||||
## Goal
|
||||
Автоматизировать substantive-commit detection в `session-handoff` через PostToolUse hook на `Bash` matcher'е (settings.json уровне), вместо поведенческой памяти агента. Hook parses `git log -1`, применяет ту же substantive-эвристику что и в SKILL.md, и на hit emits `hookSpecificOutput.additionalContext` чтобы Claude Code surface'ил system reminder в следующей итерации агента.
|
||||
|
||||
## Key files
|
||||
- `skills/session-handoff/hooks/commit-detector.ps1` — Windows/PowerShell hook script
|
||||
- `skills/session-handoff/hooks/commit-detector.sh` — Linux/macOS POSIX hook script (требует python3 для JSON parsing)
|
||||
- `skills/session-handoff/hooks/README.md` — opt-in инструкции, cross-platform settings.json snippets, smoke procedure
|
||||
- `skills/session-handoff/SKILL.md` — body When-to-use updated: substantive-commit пункт получил «**Optional**: harness-side hook см. hooks/README.md»
|
||||
- `skills/session-handoff/SKILL.md` frontmatter — bump 0.2.1 → 0.3.0 (MINOR: new opt-in capability)
|
||||
|
||||
## Decisions log
|
||||
- 2026-05-24: **install.sh НЕ мутирует ~/.claude/settings.json**. Auto-rewriting user hook config — неправильная shape для install скрипта. Hooks ship as files; user enables once per machine. SKILL.md и hooks/README.md документируют opt-in step (раз сделал — работает на все sessions).
|
||||
- 2026-05-24: **JSON output protocol**: hook возвращает `hookSpecificOutput.additionalContext` (per Claude Code PostToolUse hook protocol). На hit — JSON; на miss — silent exit 0 без output. Confirmed via claude-code-guide subagent (https://code.claude.com/docs/en/hooks.md § JSON Output Format).
|
||||
- 2026-05-24: **--amend skip** (recommended в task design questions). Amend обычно правит prev session коммит, не новый work artifact.
|
||||
- 2026-05-24: **rebase/cherry-pick noise — deferred**. Hook fires per commit, batch operations spam. Trade-off acceptable for opt-in v0.3.0; defer "только original commit-event (HEAD@{1} != HEAD)" к follow-up если actually annoys.
|
||||
- 2026-05-24: **first-non-trivial-commit-of-session special case — NOT in hook**. Session boundaries are agent-state, не accessible from hook side. Hook uses only body/file thresholds. Под-detection on small first commits acceptable; agent-side эвристика остаётся как backup.
|
||||
- 2026-05-24: **POSIX requires python3** for safe JSON parsing of PostToolUse stdin. Alternatives (sed/awk JSON parsing) fragile. Documented as dep in README.
|
||||
|
||||
## Open questions
|
||||
- [ ] Live-hook smoke test — отдельной сессией (enable hook → substantive commit → see additionalContext surface). Не делалось в этой сессии чтобы не interfere с current commits.
|
||||
|
||||
## Completed steps
|
||||
- [x] Research PostToolUse hook output protocol (claude-code-guide subagent)
|
||||
- [x] Inspect existing ~/.claude/settings.json (no hooks currently configured)
|
||||
- [x] Write commit-detector.ps1 (Windows)
|
||||
- [x] Write commit-detector.sh (POSIX)
|
||||
- [x] Write hooks/README.md (opt-in instructions cross-platform + smoke procedure)
|
||||
- [x] Update SKILL.md body — When-to-use mentions hook as opt-in alternative
|
||||
- [x] Bump SKILL.md 0.2.1 → 0.3.0 (MINOR — new capability)
|
||||
- [x] Reinstall via scripts/install.ps1
|
||||
- [x] stdin-pipe smoke (6 scenarios): substantive HEAD ✓ emits JSON; --amend / ls / empty / malformed / failed-commit ✓ silent skip
|
||||
- [x] discover + fix PS bug: `git log %b` → string[], `.Length` was line count; `-join "`n"` fix; PATCH bump 0.3.0 → 0.3.1
|
||||
- [x] STATUS.md → 🟢 (partial: stdin smoke ✓, live-hook deferred to separate session)
|
||||
- [ ] commit (next)
|
||||
|
||||
## Notes
|
||||
Live-hook enable + e2e validation = separate task / separate session. Adding hook to settings.json in this active session would fire on every git commit done here, including the closure commit itself — meta-feedback loop best avoided.
|
||||
@@ -7,6 +7,7 @@ use project wiki
|
||||
use task management system
|
||||
check across all projects
|
||||
pull remote before work
|
||||
session handoff: read on start, write on end
|
||||
follow project discipline
|
||||
follow tdd-criteria
|
||||
delegate to interns when allowed
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: session-handoff
|
||||
version: 0.2.1
|
||||
version: 0.3.1
|
||||
description: "Sliding handoff between CC sessions via .tasks/NEXT_SESSION.md. Read on session start: orient agent, ask user before action. Write on session-end phrase or substantive commit. Session-end phrases: «завершаем сессию», «сворачиваемся», «закругляемся», «wrap up session», «end session», «we're done for now». Trigger-line in CLAUDE.md: `session handoff: read on start, write on end`. Skip task-zone phrases: «закрываем эту таску», «pause», «отбой», «разбегаемся»."
|
||||
---
|
||||
|
||||
@@ -26,6 +26,7 @@ Forward-looking, не timeline: handoff = связка новых вещей к
|
||||
- prefix НЕ в (`meta:`|`docs:`|`style:`|`chore:`|`fix typo`)
|
||||
- AND (body length > 200 символов OR files changed > 3)
|
||||
- Плюс: **первый** non-trivial commit сессии — всегда триггерит, даже если ниже порога (старт работы = context shift).
|
||||
- **Optional**: substantive-commit detection может быть автоматизирован harness-side через PostToolUse hook — см. `hooks/README.md` для opt-in инструкций. С enabled hook'ом первая часть becomes deterministic (parser-side, не behavioral memory).
|
||||
|
||||
**Skip (false-positive guards):**
|
||||
- «закрываем эту таску» — task close, не session. Это zone `using-tasks`.
|
||||
|
||||
91
skills/session-handoff/hooks/README.md
Normal file
91
skills/session-handoff/hooks/README.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# session-handoff hooks
|
||||
|
||||
Opt-in PostToolUse hook that detects substantive `git commit` invocations and signals the agent ("consider running session-handoff write-mode") via a system reminder. Replaces the agent-side commit-detection heuristic in the SKILL.md `When to use` section with a deterministic harness-side trigger.
|
||||
|
||||
## Why opt-in (not auto-installed)
|
||||
|
||||
`install.sh` deliberately does **not** mutate `~/.claude/settings.json`. Auto-rewriting the user's hook config on every skill install is the wrong shape — user expects `install.sh` to copy files, nothing more. The hook is shipped as scripts; user enables it once per machine.
|
||||
|
||||
## Enable on Windows (PowerShell)
|
||||
|
||||
Add to `~/.claude/settings.json`:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Bash",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "pwsh -NoProfile -ExecutionPolicy Bypass -File \"$env:USERPROFILE\\.claude\\skills\\session-handoff\\hooks\\commit-detector.ps1\""
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If `hooks.PostToolUse` already exists — append the matcher block to the array. Don't overwrite existing entries.
|
||||
|
||||
## Enable on Linux / macOS (bash)
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Bash",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bash ~/.claude/skills/session-handoff/hooks/commit-detector.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The bash variant needs `python3` on PATH (used to parse the PostToolUse JSON stdin).
|
||||
|
||||
## What gets signalled
|
||||
|
||||
On a successful `git commit` whose subject prefix is not in {`meta`, `docs`, `style`, `chore`} or `fix typo`, **and** whose body exceeds 200 characters or which touches more than 3 files — the hook emits a `hookSpecificOutput.additionalContext` system reminder of shape:
|
||||
|
||||
> Substantive commit detected on `<cwd>`: `<subject>` (N files changed, body M chars). Consider invoking session-handoff write-mode to update `.tasks/NEXT_SESSION.md`.
|
||||
|
||||
On any of these → silent skip (exit 0, no JSON):
|
||||
|
||||
- malformed PostToolUse stdin
|
||||
- Bash command isn't `git commit`
|
||||
- command is `git commit --amend`
|
||||
- commit returned non-zero exit
|
||||
- cwd isn't a git work-tree
|
||||
- subject prefix is in trivial set
|
||||
- body ≤ 200 chars AND files ≤ 3
|
||||
|
||||
## Smoke test (without enabling the hook)
|
||||
|
||||
Pipe a synthetic PostToolUse JSON to the script. On Windows:
|
||||
|
||||
```powershell
|
||||
$payload = @{
|
||||
tool_input = @{ command = 'git commit -m "subject"' }
|
||||
tool_response = @{ exit_code = 0 }
|
||||
cwd = (Get-Location).Path
|
||||
} | ConvertTo-Json -Compress
|
||||
|
||||
$payload | pwsh -NoProfile -File .\skills\session-handoff\hooks\commit-detector.ps1
|
||||
```
|
||||
|
||||
If your `HEAD` is a non-trivial commit (e.g. recent `feat:` with > 200-char body or > 3 files), output is JSON containing `additionalContext`. Otherwise — empty stdout (silent skip).
|
||||
|
||||
## Caveats
|
||||
|
||||
- **Rebase / cherry-pick noise.** Every commit in a rebase or cherry-pick batch will re-fire the hook. Deferred to a follow-up if it actually annoys in practice; the hook is opt-in so the cost is bounded.
|
||||
- **First-non-trivial commit of session.** The agent-side heuristic in SKILL.md treats the *first* non-trivial commit of a session as "always substantive" regardless of thresholds. The hook can't see session boundaries — uses only body/file thresholds. Slight under-detection on small first commits; acceptable trade-off for harness-side determinism.
|
||||
- **No automatic write-mode invocation.** Hook only signals. The agent still decides whether to run session-handoff write-mode in response — keeps the user-agency invariant from SKILL.md `What NOT to do`.
|
||||
72
skills/session-handoff/hooks/commit-detector.ps1
Normal file
72
skills/session-handoff/hooks/commit-detector.ps1
Normal file
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env pwsh
|
||||
# session-handoff PostToolUse hook (PowerShell).
|
||||
#
|
||||
# Reads PostToolUse JSON from stdin, detects whether the just-completed
|
||||
# Bash tool call was a substantive `git commit`, and on hit emits JSON to
|
||||
# stdout with `additionalContext` so Claude Code surfaces a system reminder
|
||||
# in the next agent iteration ("substantive commit — consider session-handoff
|
||||
# write-mode").
|
||||
#
|
||||
# Substantive heuristic (mirrors session-handoff SKILL.md):
|
||||
# prefix NOT in (meta:|docs:|style:|chore:|fix typo) AND
|
||||
# (body > 200 chars OR files > 3)
|
||||
#
|
||||
# Silent skip on: malformed JSON, no command, --amend, failed commit,
|
||||
# non-git cwd, trivial prefix, below thresholds. Never blocks the tool call
|
||||
# (PostToolUse cannot, by design).
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
# Read stdin
|
||||
try {
|
||||
$raw = [Console]::In.ReadToEnd()
|
||||
if ([string]::IsNullOrWhiteSpace($raw)) { exit 0 }
|
||||
$hook = $raw | ConvertFrom-Json -ErrorAction Stop
|
||||
} catch {
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Only Bash tool, only git commit (not --amend)
|
||||
$cmd = $hook.tool_input.command
|
||||
if (-not $cmd) { exit 0 }
|
||||
if ($cmd -notmatch '(?<![\w-])git\s+commit(?![\w-])') { exit 0 }
|
||||
if ($cmd -match '(?<![\w-])git\s+commit\b.*--amend') { exit 0 }
|
||||
|
||||
# Only on successful commit
|
||||
if ($null -ne $hook.tool_response.exit_code -and $hook.tool_response.exit_code -ne 0) { exit 0 }
|
||||
|
||||
# Resolve cwd; require a git work-tree
|
||||
$cwd = $hook.cwd
|
||||
if (-not $cwd) { $cwd = (Get-Location).Path }
|
||||
$inside = & git -C $cwd rev-parse --is-inside-work-tree 2>$null
|
||||
if ($inside -ne 'true') { exit 0 }
|
||||
|
||||
# Parse last commit
|
||||
$subject = (& git -C $cwd log -1 --format='%s').Trim()
|
||||
# PowerShell collapses multi-line subprocess output into string[] — join back so
|
||||
# .Length below is char count, not line count.
|
||||
$body = ((& git -C $cwd log -1 --format='%b') -join "`n")
|
||||
$files = ((& git -C $cwd diff-tree --no-commit-id --name-only -r HEAD) | Measure-Object).Count
|
||||
|
||||
# Trivial-prefix check (Conventional Commits prefix before optional scope + colon)
|
||||
$prefix = $subject -replace '^([a-z]+)(\([^)]+\))?:.*$','$1'
|
||||
$trivial = @('meta','docs','style','chore')
|
||||
if ($trivial -contains $prefix) { exit 0 }
|
||||
if ($subject -match 'fix\s+typo') { exit 0 }
|
||||
|
||||
# Threshold check (chars for body, count for files)
|
||||
$bodyLen = if ($body) { $body.Length } else { 0 }
|
||||
if ($bodyLen -le 200 -and $files -le 3) { exit 0 }
|
||||
|
||||
|
||||
# Substantive — emit JSON
|
||||
$msg = "Substantive commit detected on " + $cwd + ": ``" + $subject + "`` (" + $files + " files changed, body " + $bodyLen + " chars). Consider invoking session-handoff write-mode to update .tasks/NEXT_SESSION.md."
|
||||
|
||||
@{
|
||||
hookSpecificOutput = @{
|
||||
hookEventName = 'PostToolUse'
|
||||
additionalContext = $msg
|
||||
}
|
||||
systemMessage = 'session-handoff: substantive commit detected'
|
||||
suppressOutput = $false
|
||||
} | ConvertTo-Json -Compress -Depth 5 | Write-Output
|
||||
73
skills/session-handoff/hooks/commit-detector.sh
Normal file
73
skills/session-handoff/hooks/commit-detector.sh
Normal file
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env bash
|
||||
# session-handoff PostToolUse hook (POSIX). See commit-detector.ps1 for prose.
|
||||
set -euo pipefail
|
||||
|
||||
raw=$(cat)
|
||||
[[ -z "$raw" ]] && exit 0
|
||||
|
||||
# Helper: extract a JSON path via python3
|
||||
jget() {
|
||||
python3 -c "
|
||||
import sys, json
|
||||
try:
|
||||
d = json.loads(sys.argv[1])
|
||||
out = d
|
||||
for k in sys.argv[2].split('.'):
|
||||
if isinstance(out, dict):
|
||||
out = out.get(k)
|
||||
else:
|
||||
out = None
|
||||
break
|
||||
print('' if out is None else out)
|
||||
" "$raw" "$1" 2>/dev/null || echo ''
|
||||
}
|
||||
|
||||
cmd=$(jget tool_input.command)
|
||||
[[ -z "$cmd" ]] && exit 0
|
||||
|
||||
# Only `git commit`, not `--amend`
|
||||
if ! echo "$cmd" | grep -qE '(^|[^[:alnum:]_-])git[[:space:]]+commit($|[^[:alnum:]_-])'; then exit 0; fi
|
||||
if echo "$cmd" | grep -qE '(^|[^[:alnum:]_-])git[[:space:]]+commit\b.*--amend'; then exit 0; fi
|
||||
|
||||
# Only on successful commit (if exit_code present and non-zero, skip)
|
||||
exit_code=$(jget tool_response.exit_code)
|
||||
if [[ -n "$exit_code" && "$exit_code" != "0" ]]; then exit 0; fi
|
||||
|
||||
cwd=$(jget cwd)
|
||||
[[ -z "$cwd" ]] && cwd=$(pwd)
|
||||
|
||||
# Require git work-tree
|
||||
git -C "$cwd" rev-parse --is-inside-work-tree >/dev/null 2>&1 || exit 0
|
||||
|
||||
subject=$(git -C "$cwd" log -1 --format='%s')
|
||||
body=$(git -C "$cwd" log -1 --format='%b')
|
||||
files=$(git -C "$cwd" diff-tree --no-commit-id --name-only -r HEAD | wc -l | tr -d ' ')
|
||||
|
||||
# Trivial-prefix check
|
||||
prefix=$(echo "$subject" | sed -E 's/^([a-z]+)(\([^)]+\))?:.*$/\1/')
|
||||
case "$prefix" in
|
||||
meta|docs|style|chore) exit 0 ;;
|
||||
esac
|
||||
echo "$subject" | grep -qE 'fix[[:space:]]+typo' && exit 0
|
||||
|
||||
# Threshold
|
||||
body_len=${#body}
|
||||
if [[ "$body_len" -le 200 && "$files" -le 3 ]]; then exit 0; fi
|
||||
|
||||
# Substantive — emit JSON via python3 to handle quoting safely
|
||||
python3 -c "
|
||||
import json, sys
|
||||
msg = (
|
||||
'Substantive commit detected on $cwd: \`' + '''$subject''' + '\` '
|
||||
+ '($files files changed, body $body_len chars). '
|
||||
+ 'Consider invoking session-handoff write-mode to update .tasks/NEXT_SESSION.md.'
|
||||
)
|
||||
print(json.dumps({
|
||||
'hookSpecificOutput': {
|
||||
'hookEventName': 'PostToolUse',
|
||||
'additionalContext': msg,
|
||||
},
|
||||
'systemMessage': 'session-handoff: substantive commit detected',
|
||||
'suppressOutput': False,
|
||||
}))
|
||||
"
|
||||
Reference in New Issue
Block a user