aiwcli 0.10.2 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/run.js +1 -1
- package/dist/commands/clear.d.ts +11 -6
- package/dist/commands/clear.js +229 -381
- package/dist/commands/init/index.d.ts +1 -17
- package/dist/commands/init/index.js +22 -107
- package/dist/lib/gitignore-manager.d.ts +32 -0
- package/dist/lib/gitignore-manager.js +141 -2
- package/dist/lib/template-installer.d.ts +7 -12
- package/dist/lib/template-installer.js +69 -193
- package/dist/lib/template-settings-reconstructor.d.ts +35 -0
- package/dist/lib/template-settings-reconstructor.js +130 -0
- package/dist/templates/CLAUDE.md +8 -8
- package/dist/templates/_shared/.claude/commands/handoff-resume.md +64 -0
- package/dist/templates/_shared/.claude/commands/handoff.md +16 -10
- package/dist/templates/_shared/.claude/settings.json +7 -7
- package/dist/templates/_shared/hooks-ts/_utils/git-state.ts +2 -0
- package/dist/templates/_shared/hooks-ts/archive_plan.ts +159 -0
- package/dist/templates/_shared/hooks-ts/context_monitor.ts +147 -0
- package/dist/templates/_shared/hooks-ts/file-suggestion.ts +130 -0
- package/dist/templates/_shared/hooks-ts/pre_compact.ts +49 -0
- package/dist/templates/_shared/hooks-ts/session_end.ts +104 -0
- package/dist/templates/_shared/hooks-ts/session_start.ts +144 -0
- package/dist/templates/_shared/hooks-ts/task_create_capture.ts +48 -0
- package/dist/templates/_shared/hooks-ts/task_update_capture.ts +74 -0
- package/dist/templates/_shared/hooks-ts/user_prompt_submit.ts +83 -0
- package/dist/templates/_shared/lib-ts/CLAUDE.md +318 -0
- package/dist/templates/_shared/lib-ts/base/atomic-write.ts +138 -0
- package/dist/templates/_shared/lib-ts/base/constants.ts +306 -0
- package/dist/templates/_shared/lib-ts/base/git-state.ts +58 -0
- package/dist/templates/_shared/lib-ts/base/hook-utils.ts +439 -0
- package/dist/templates/_shared/lib-ts/base/inference.ts +252 -0
- package/dist/templates/_shared/lib-ts/base/logger.ts +250 -0
- package/dist/templates/_shared/lib-ts/base/state-io.ts +116 -0
- package/dist/templates/_shared/lib-ts/base/stop-words.ts +184 -0
- package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +162 -0
- package/dist/templates/_shared/lib-ts/base/utils.ts +184 -0
- package/dist/templates/_shared/lib-ts/context/context-formatter.ts +438 -0
- package/dist/templates/_shared/lib-ts/context/context-selector.ts +515 -0
- package/dist/templates/_shared/lib-ts/context/context-store.ts +707 -0
- package/dist/templates/_shared/lib-ts/context/plan-manager.ts +316 -0
- package/dist/templates/_shared/lib-ts/context/task-tracker.ts +185 -0
- package/dist/templates/_shared/lib-ts/handoff/document-generator.ts +216 -0
- package/dist/templates/_shared/lib-ts/handoff/handoff-reader.ts +159 -0
- package/dist/templates/_shared/lib-ts/package.json +21 -0
- package/dist/templates/_shared/lib-ts/templates/formatters.ts +104 -0
- package/dist/templates/_shared/{lib/templates/plan_context.py → lib-ts/templates/plan-context.ts} +14 -22
- package/dist/templates/_shared/lib-ts/tsconfig.json +13 -0
- package/dist/templates/_shared/lib-ts/types.ts +164 -0
- package/dist/templates/_shared/scripts/resolve_context.ts +24 -0
- package/dist/templates/_shared/scripts/resume_handoff.ts +321 -0
- package/dist/templates/_shared/scripts/save_handoff.ts +359 -0
- package/dist/templates/_shared/scripts/status_line.ts +733 -0
- package/dist/templates/cc-native/.claude/settings.json +175 -185
- package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +15 -17
- package/dist/templates/cc-native/_cc-native/agents/ARCH-EVOLUTION.md +63 -0
- package/dist/templates/cc-native/_cc-native/agents/ARCH-PATTERNS.md +62 -0
- package/dist/templates/cc-native/_cc-native/agents/ARCH-STRUCTURE.md +63 -0
- package/dist/templates/cc-native/_cc-native/agents/{ASSUMPTION-CHAIN-TRACER.md → ASSUMPTION-TRACER.md} +6 -10
- package/dist/templates/cc-native/_cc-native/agents/CLARITY-AUDITOR.md +6 -10
- package/dist/templates/cc-native/_cc-native/agents/CLAUDE.md +74 -3
- package/dist/templates/cc-native/_cc-native/agents/COMPLETENESS-FEASIBILITY.md +67 -0
- package/dist/templates/cc-native/_cc-native/agents/COMPLETENESS-GAPS.md +71 -0
- package/dist/templates/cc-native/_cc-native/agents/COMPLETENESS-ORDERING.md +63 -0
- package/dist/templates/cc-native/_cc-native/agents/CONSTRAINT-VALIDATOR.md +73 -0
- package/dist/templates/cc-native/_cc-native/agents/DESIGN-ADR-VALIDATOR.md +62 -0
- package/dist/templates/cc-native/_cc-native/agents/DESIGN-SCALE-MATCHER.md +65 -0
- package/dist/templates/cc-native/_cc-native/agents/DEVILS-ADVOCATE.md +6 -9
- package/dist/templates/cc-native/_cc-native/agents/DOCUMENTATION-PHILOSOPHY.md +87 -0
- package/dist/templates/cc-native/_cc-native/agents/HANDOFF-READINESS.md +5 -9
- package/dist/templates/cc-native/_cc-native/agents/{HIDDEN-COMPLEXITY-DETECTOR.md → HIDDEN-COMPLEXITY.md} +6 -10
- package/dist/templates/cc-native/_cc-native/agents/INCREMENTAL-DELIVERY.md +67 -0
- package/dist/templates/cc-native/_cc-native/agents/PLAN-ORCHESTRATOR.md +91 -18
- package/dist/templates/cc-native/_cc-native/agents/RISK-DEPENDENCY.md +63 -0
- package/dist/templates/cc-native/_cc-native/agents/RISK-FMEA.md +67 -0
- package/dist/templates/cc-native/_cc-native/agents/RISK-PREMORTEM.md +72 -0
- package/dist/templates/cc-native/_cc-native/agents/RISK-REVERSIBILITY.md +75 -0
- package/dist/templates/cc-native/_cc-native/agents/SCOPE-BOUNDARY.md +78 -0
- package/dist/templates/cc-native/_cc-native/agents/SIMPLICITY-GUARDIAN.md +5 -9
- package/dist/templates/cc-native/_cc-native/agents/SKEPTIC.md +16 -12
- package/dist/templates/cc-native/_cc-native/agents/TESTDRIVEN-BEHAVIOR-AUDITOR.md +62 -0
- package/dist/templates/cc-native/_cc-native/agents/TESTDRIVEN-CHARACTERIZATION.md +72 -0
- package/dist/templates/cc-native/_cc-native/agents/TESTDRIVEN-FIRST-VALIDATOR.md +62 -0
- package/dist/templates/cc-native/_cc-native/agents/TESTDRIVEN-PYRAMID-ANALYZER.md +62 -0
- package/dist/templates/cc-native/_cc-native/agents/TRADEOFF-COSTS.md +68 -0
- package/dist/templates/cc-native/_cc-native/agents/TRADEOFF-STAKEHOLDERS.md +66 -0
- package/dist/templates/cc-native/_cc-native/agents/VERIFY-COVERAGE.md +75 -0
- package/dist/templates/cc-native/_cc-native/agents/VERIFY-STRENGTH.md +70 -0
- package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +109 -135
- package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.ts +119 -0
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +921 -0
- package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +61 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +157 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/artifacts.ts +709 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +199 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +124 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/config.ts +57 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/constants.ts +83 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/debug.ts +80 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/index.ts +119 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/json-parser.ts +162 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/nul +3 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/orchestrator.ts +249 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/agent.ts +155 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/codex.ts +130 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/gemini.ts +106 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/index.ts +10 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/types.ts +23 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +243 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/tsconfig.json +18 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +310 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/verdict.ts +72 -0
- package/dist/templates/cc-native/_cc-native/plan-review.config.json +12 -16
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/dist/lib/template-merger.d.ts +0 -47
- package/dist/lib/template-merger.js +0 -162
- package/dist/templates/_shared/hooks/__init__.py +0 -16
- package/dist/templates/_shared/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/context_enforcer.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/context_monitor.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/file-suggestion.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/pre_compact.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/session_end.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/session_start.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/task_create_atomicity.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/task_create_capture.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/task_update_capture.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/archive_plan.py +0 -169
- package/dist/templates/_shared/hooks/context_monitor.py +0 -270
- package/dist/templates/_shared/hooks/file-suggestion.py +0 -215
- package/dist/templates/_shared/hooks/pre_compact.py +0 -104
- package/dist/templates/_shared/hooks/session_end.py +0 -173
- package/dist/templates/_shared/hooks/session_start.py +0 -206
- package/dist/templates/_shared/hooks/task_create_capture.py +0 -108
- package/dist/templates/_shared/hooks/task_update_capture.py +0 -145
- package/dist/templates/_shared/hooks/user_prompt_submit.py +0 -139
- package/dist/templates/_shared/lib/__init__.py +0 -1
- package/dist/templates/_shared/lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__init__.py +0 -65
- package/dist/templates/_shared/lib/base/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/atomic_write.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/constants.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/hook_utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/inference.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/logger.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/stop_words.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/subprocess_utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/atomic_write.py +0 -180
- package/dist/templates/_shared/lib/base/constants.py +0 -358
- package/dist/templates/_shared/lib/base/hook_utils.py +0 -341
- package/dist/templates/_shared/lib/base/inference.py +0 -318
- package/dist/templates/_shared/lib/base/logger.py +0 -291
- package/dist/templates/_shared/lib/base/stop_words.py +0 -213
- package/dist/templates/_shared/lib/base/subprocess_utils.py +0 -46
- package/dist/templates/_shared/lib/base/utils.py +0 -242
- package/dist/templates/_shared/lib/context/__init__.py +0 -102
- package/dist/templates/_shared/lib/context/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/cache.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_extractor.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_formatter.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_manager.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_selector.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_store.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/discovery.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/event_log.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/plan_archive.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/plan_manager.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/task_sync.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/task_tracker.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/context_formatter.py +0 -317
- package/dist/templates/_shared/lib/context/context_selector.py +0 -508
- package/dist/templates/_shared/lib/context/context_store.py +0 -653
- package/dist/templates/_shared/lib/context/plan_manager.py +0 -204
- package/dist/templates/_shared/lib/context/task_tracker.py +0 -188
- package/dist/templates/_shared/lib/handoff/__init__.py +0 -22
- package/dist/templates/_shared/lib/handoff/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/handoff/__pycache__/document_generator.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/handoff/document_generator.py +0 -278
- package/dist/templates/_shared/lib/templates/README.md +0 -206
- package/dist/templates/_shared/lib/templates/__init__.py +0 -36
- package/dist/templates/_shared/lib/templates/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/__pycache__/formatters.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/__pycache__/persona_questions.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/__pycache__/plan_context.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/formatters.py +0 -146
- package/dist/templates/_shared/scripts/__pycache__/save_handoff.cpython-313.pyc +0 -0
- package/dist/templates/_shared/scripts/__pycache__/status_line.cpython-313.pyc +0 -0
- package/dist/templates/_shared/scripts/save_handoff.py +0 -357
- package/dist/templates/_shared/scripts/status_line.py +0 -701
- package/dist/templates/cc-native/.claude/commands/cc-native/fresh-perspective.md +0 -8
- package/dist/templates/cc-native/.windsurf/workflows/cc-native/fresh-perspective.md +0 -8
- package/dist/templates/cc-native/MIGRATION.md +0 -86
- package/dist/templates/cc-native/_cc-native/agents/ACCESSIBILITY-TESTER.md +0 -79
- package/dist/templates/cc-native/_cc-native/agents/ARCHITECT-REVIEWER.md +0 -48
- package/dist/templates/cc-native/_cc-native/agents/CODE-REVIEWER.md +0 -70
- package/dist/templates/cc-native/_cc-native/agents/COMPLETENESS-CHECKER.md +0 -59
- package/dist/templates/cc-native/_cc-native/agents/CONTEXT-EXTRACTOR.md +0 -92
- package/dist/templates/cc-native/_cc-native/agents/DOCUMENTATION-REVIEWER.md +0 -51
- package/dist/templates/cc-native/_cc-native/agents/FEASIBILITY-ANALYST.md +0 -57
- package/dist/templates/cc-native/_cc-native/agents/FRESH-PERSPECTIVE.md +0 -54
- package/dist/templates/cc-native/_cc-native/agents/INCENTIVE-MAPPER.md +0 -61
- package/dist/templates/cc-native/_cc-native/agents/PENETRATION-TESTER.md +0 -79
- package/dist/templates/cc-native/_cc-native/agents/PERFORMANCE-ENGINEER.md +0 -75
- package/dist/templates/cc-native/_cc-native/agents/PRECEDENT-FINDER.md +0 -70
- package/dist/templates/cc-native/_cc-native/agents/REVERSIBILITY-ANALYST.md +0 -61
- package/dist/templates/cc-native/_cc-native/agents/RISK-ASSESSOR.md +0 -58
- package/dist/templates/cc-native/_cc-native/agents/SECOND-ORDER-ANALYST.md +0 -61
- package/dist/templates/cc-native/_cc-native/agents/STAKEHOLDER-ADVOCATE.md +0 -55
- package/dist/templates/cc-native/_cc-native/agents/TRADE-OFF-ILLUMINATOR.md +0 -204
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/add_plan_context.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-plan-review.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/mark_questions_asked.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_accepted.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_questions_early.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/suggest-fresh-perspective.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.py +0 -130
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +0 -869
- package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.py +0 -81
- package/dist/templates/cc-native/_cc-native/hooks/suggest-fresh-perspective.py +0 -340
- package/dist/templates/cc-native/_cc-native/lib/CLAUDE.md +0 -265
- package/dist/templates/cc-native/_cc-native/lib/__init__.py +0 -53
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/atomic_write.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/constants.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/debug.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/orchestrator.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/state.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/utils.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/constants.py +0 -45
- package/dist/templates/cc-native/_cc-native/lib/debug.py +0 -139
- package/dist/templates/cc-native/_cc-native/lib/orchestrator.py +0 -362
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__init__.py +0 -28
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/agent.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/base.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/codex.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/gemini.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/agent.py +0 -215
- package/dist/templates/cc-native/_cc-native/lib/reviewers/base.py +0 -88
- package/dist/templates/cc-native/_cc-native/lib/reviewers/codex.py +0 -124
- package/dist/templates/cc-native/_cc-native/lib/reviewers/gemini.py +0 -108
- package/dist/templates/cc-native/_cc-native/lib/state.py +0 -268
- package/dist/templates/cc-native/_cc-native/lib/utils.py +0 -1027
- package/dist/templates/cc-native/_cc-native/scripts/__pycache__/aggregate_agents.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/scripts/aggregate_agents.py +0 -168
- package/dist/templates/cc-native/_cc-native/workflows/fresh-perspective.md +0 -134
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""PreCompact hook - saves auto-state before context compaction.
|
|
3
|
-
|
|
4
|
-
Critical: saves state before context compaction destroys token history.
|
|
5
|
-
After compaction, SessionStart fires with source="compact" and the
|
|
6
|
-
restored auto-state provides continuity context.
|
|
7
|
-
|
|
8
|
-
Hook input (from Claude Code):
|
|
9
|
-
{
|
|
10
|
-
"hook_event_name": "PreCompact",
|
|
11
|
-
"session_id": "abc123",
|
|
12
|
-
"transcript_path": "/path/to/transcript.jsonl",
|
|
13
|
-
"cwd": "/path/to/project",
|
|
14
|
-
...
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
Hook output:
|
|
18
|
-
- Silent (no stdout output needed)
|
|
19
|
-
- Logs to stderr for debugging
|
|
20
|
-
"""
|
|
21
|
-
import sys
|
|
22
|
-
from pathlib import Path
|
|
23
|
-
|
|
24
|
-
# Add parent directories to path for imports
|
|
25
|
-
SCRIPT_DIR = Path(__file__).resolve().parent
|
|
26
|
-
SHARED_LIB = SCRIPT_DIR.parent / "lib"
|
|
27
|
-
sys.path.insert(0, str(SHARED_LIB.parent))
|
|
28
|
-
|
|
29
|
-
from lib.base.hook_utils import load_hook_input, log_debug, log_info, log_error
|
|
30
|
-
from lib.base.utils import project_dir
|
|
31
|
-
from lib.context.context_store import get_context_by_session_id, save_state
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def main():
|
|
35
|
-
"""Save auto-state before compaction."""
|
|
36
|
-
try:
|
|
37
|
-
hook_input = load_hook_input()
|
|
38
|
-
if not hook_input:
|
|
39
|
-
return
|
|
40
|
-
|
|
41
|
-
session_id = hook_input.get("session_id", "")
|
|
42
|
-
transcript_path = hook_input.get("transcript_path")
|
|
43
|
-
project_root = project_dir(hook_input)
|
|
44
|
-
|
|
45
|
-
if not session_id:
|
|
46
|
-
log_debug("pre_compact", "No session_id, skipping")
|
|
47
|
-
return
|
|
48
|
-
|
|
49
|
-
log_info("pre_compact", f"Saving state before compaction: {session_id[:8]}...")
|
|
50
|
-
|
|
51
|
-
# Find context bound to this session
|
|
52
|
-
context = get_context_by_session_id(session_id, project_root)
|
|
53
|
-
if not context:
|
|
54
|
-
log_debug("pre_compact", "No context bound to this session, skipping")
|
|
55
|
-
return
|
|
56
|
-
|
|
57
|
-
# Save last_session snapshot directly to state.json
|
|
58
|
-
import subprocess
|
|
59
|
-
git_state = {}
|
|
60
|
-
try:
|
|
61
|
-
branch = subprocess.run(
|
|
62
|
-
["git", "rev-parse", "--abbrev-ref", "HEAD"],
|
|
63
|
-
capture_output=True, text=True, timeout=5
|
|
64
|
-
)
|
|
65
|
-
git_state["branch"] = branch.stdout.strip() if branch.returncode == 0 else "unknown"
|
|
66
|
-
|
|
67
|
-
status = subprocess.run(
|
|
68
|
-
["git", "status", "--short"],
|
|
69
|
-
capture_output=True, text=True, timeout=5
|
|
70
|
-
)
|
|
71
|
-
if status.returncode == 0 and status.stdout.strip():
|
|
72
|
-
git_state["uncommitted_files"] = [
|
|
73
|
-
line.split(None, 1)[-1] for line in status.stdout.strip().split("\n")[:10]
|
|
74
|
-
]
|
|
75
|
-
|
|
76
|
-
log = subprocess.run(
|
|
77
|
-
["git", "log", "-1", "--format=%h %s"],
|
|
78
|
-
capture_output=True, text=True, timeout=5
|
|
79
|
-
)
|
|
80
|
-
if log.returncode == 0:
|
|
81
|
-
git_state["last_commit_short"] = log.stdout.strip()
|
|
82
|
-
except Exception:
|
|
83
|
-
pass
|
|
84
|
-
|
|
85
|
-
from lib.base.utils import now_iso
|
|
86
|
-
context.last_session = {
|
|
87
|
-
"session_id": session_id,
|
|
88
|
-
"saved_at": now_iso(),
|
|
89
|
-
"save_reason": "pre_compact",
|
|
90
|
-
"git_state": git_state,
|
|
91
|
-
}
|
|
92
|
-
save_state(context, project_root)
|
|
93
|
-
log_info("pre_compact", f"State saved for {context.id}")
|
|
94
|
-
|
|
95
|
-
except Exception as e:
|
|
96
|
-
import traceback
|
|
97
|
-
tb = traceback.format_exc()
|
|
98
|
-
from lib.base.hook_utils import log_hook_error
|
|
99
|
-
log_hook_error("pre_compact", e, "PreCompact", traceback_str=tb)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if __name__ == "__main__":
|
|
103
|
-
from lib.base.hook_utils import run_hook
|
|
104
|
-
run_hook(main, "pre_compact")
|
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""SessionEnd hook - saves session state to state.json.
|
|
3
|
-
|
|
4
|
-
Fires when session terminates (quit, /clear, logout). Saves last_session
|
|
5
|
-
data directly to state.json for restoration on next session.
|
|
6
|
-
|
|
7
|
-
Hook input (from Claude Code):
|
|
8
|
-
{
|
|
9
|
-
"hook_event_name": "SessionEnd",
|
|
10
|
-
"session_id": "abc123",
|
|
11
|
-
"source": "prompt_input_exit", # or "clear", "logout", "compact"
|
|
12
|
-
"transcript_path": "/path/to/transcript.jsonl",
|
|
13
|
-
"cwd": "/path/to/project",
|
|
14
|
-
...
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
Hook output:
|
|
18
|
-
- Silent (no stdout output needed for SessionEnd)
|
|
19
|
-
- Logs to stderr for debugging
|
|
20
|
-
"""
|
|
21
|
-
import hashlib
|
|
22
|
-
import subprocess
|
|
23
|
-
import sys
|
|
24
|
-
from pathlib import Path
|
|
25
|
-
|
|
26
|
-
# Add parent directories to path for imports
|
|
27
|
-
SCRIPT_DIR = Path(__file__).resolve().parent
|
|
28
|
-
SHARED_LIB = SCRIPT_DIR.parent / "lib"
|
|
29
|
-
sys.path.insert(0, str(SHARED_LIB.parent))
|
|
30
|
-
|
|
31
|
-
from lib.base.hook_utils import load_hook_input, log_debug, log_info, log_warn, log_error, log_diagnostic
|
|
32
|
-
from lib.base.utils import now_iso, project_dir
|
|
33
|
-
from lib.context.context_store import get_context_by_session_id, save_state
|
|
34
|
-
from lib.context.plan_manager import find_latest_plan, normalize_plan_content, generate_plan_id, extract_plan_anchors
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def _get_git_state(project_root: Path) -> dict:
|
|
38
|
-
"""Capture current git state for restoration."""
|
|
39
|
-
git_state = {}
|
|
40
|
-
try:
|
|
41
|
-
result = subprocess.run(
|
|
42
|
-
["git", "rev-parse", "--abbrev-ref", "HEAD"],
|
|
43
|
-
capture_output=True, text=True, cwd=str(project_root), timeout=5,
|
|
44
|
-
)
|
|
45
|
-
if result.returncode == 0:
|
|
46
|
-
git_state["branch"] = result.stdout.strip()
|
|
47
|
-
|
|
48
|
-
result = subprocess.run(
|
|
49
|
-
["git", "diff", "--name-only"],
|
|
50
|
-
capture_output=True, text=True, cwd=str(project_root), timeout=5,
|
|
51
|
-
)
|
|
52
|
-
if result.returncode == 0:
|
|
53
|
-
files = [f for f in result.stdout.strip().split("\n") if f]
|
|
54
|
-
git_state["uncommitted_files"] = files
|
|
55
|
-
|
|
56
|
-
result = subprocess.run(
|
|
57
|
-
["git", "log", "-1", "--oneline"],
|
|
58
|
-
capture_output=True, text=True, cwd=str(project_root), timeout=5,
|
|
59
|
-
)
|
|
60
|
-
if result.returncode == 0:
|
|
61
|
-
git_state["last_commit_short"] = result.stdout.strip()
|
|
62
|
-
except Exception as e:
|
|
63
|
-
log_warn("session_end", f"Git state capture error (non-fatal): {e}")
|
|
64
|
-
|
|
65
|
-
return git_state
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def main():
|
|
69
|
-
"""Save session state to state.json."""
|
|
70
|
-
try:
|
|
71
|
-
hook_input = load_hook_input()
|
|
72
|
-
if not hook_input:
|
|
73
|
-
return
|
|
74
|
-
|
|
75
|
-
session_id = hook_input.get("session_id", "")
|
|
76
|
-
source = hook_input.get("source", "other")
|
|
77
|
-
transcript_path = hook_input.get("transcript_path")
|
|
78
|
-
project_root = project_dir(hook_input)
|
|
79
|
-
|
|
80
|
-
if not session_id:
|
|
81
|
-
log_debug("session_end", "No session_id, skipping")
|
|
82
|
-
return
|
|
83
|
-
|
|
84
|
-
log_info("session_end", f"Session ending: {session_id[:8]}... reason={source}")
|
|
85
|
-
log_diagnostic("session_end", "receive", f"session={session_id[:8]}, source={source}",
|
|
86
|
-
inputs={"session_id": session_id[:12], "source": source})
|
|
87
|
-
|
|
88
|
-
# Find context bound to this session
|
|
89
|
-
state = get_context_by_session_id(session_id, project_root)
|
|
90
|
-
if not state:
|
|
91
|
-
log_debug("session_end", "No context bound to this session, skipping")
|
|
92
|
-
return
|
|
93
|
-
|
|
94
|
-
log_info("session_end", f"Found context: {state.id}")
|
|
95
|
-
|
|
96
|
-
# Capture git state
|
|
97
|
-
git_state = _get_git_state(project_root)
|
|
98
|
-
|
|
99
|
-
# Save last_session directly to state.json
|
|
100
|
-
state.last_session = {
|
|
101
|
-
"session_id": session_id,
|
|
102
|
-
"save_reason": source,
|
|
103
|
-
"saved_at": now_iso(),
|
|
104
|
-
"transcript_path": transcript_path,
|
|
105
|
-
"git_state": git_state,
|
|
106
|
-
}
|
|
107
|
-
state.last_active = now_iso()
|
|
108
|
-
|
|
109
|
-
# Only assign plan fields and stage if NOT in plan mode.
|
|
110
|
-
# If permission_mode == "plan", ExitPlanMode was rejected (user pressed Escape),
|
|
111
|
-
# so we should not stage the archived plan for the next session.
|
|
112
|
-
permission_mode = hook_input.get("permission_mode", "default")
|
|
113
|
-
|
|
114
|
-
if permission_mode != "plan":
|
|
115
|
-
# Fallback: assign plan fields if PostToolUse:ExitPlanMode didn't fire.
|
|
116
|
-
# When ExitPlanMode triggers /clear, the session terminates before PostToolUse
|
|
117
|
-
# hooks can run, so plan_accepted.py never fires. Detect this by checking
|
|
118
|
-
# for an archived plan that hasn't been assigned yet.
|
|
119
|
-
if not state.plan_hash:
|
|
120
|
-
latest_plan_path = find_latest_plan(state.id, project_root)
|
|
121
|
-
if latest_plan_path:
|
|
122
|
-
try:
|
|
123
|
-
content = Path(latest_plan_path).read_text(encoding="utf-8")
|
|
124
|
-
normalized = normalize_plan_content(content)
|
|
125
|
-
state.plan_hash = hashlib.sha256(normalized.encode("utf-8")).hexdigest()[:12]
|
|
126
|
-
state.plan_path = latest_plan_path
|
|
127
|
-
state.plan_signature = content[:200]
|
|
128
|
-
state.plan_id = generate_plan_id()
|
|
129
|
-
state.plan_anchors = extract_plan_anchors(content)
|
|
130
|
-
state.plan_consumed = False
|
|
131
|
-
log_info("session_end", f"Fallback: assigned archived plan for {state.id} (hash: {state.plan_hash})")
|
|
132
|
-
except Exception as e:
|
|
133
|
-
log_warn("session_end", f"Fallback plan assignment failed: {e}")
|
|
134
|
-
|
|
135
|
-
# If a plan is assigned, not yet consumed, and mode is active, stage it for next session
|
|
136
|
-
if state.plan_hash and state.mode == "active" and not state.plan_consumed:
|
|
137
|
-
state.mode = "has_plan"
|
|
138
|
-
log_info("session_end", f"Staged plan for next session: {state.id} -> has_plan")
|
|
139
|
-
elif state.plan_hash and state.mode == "active" and state.plan_consumed:
|
|
140
|
-
log_debug("session_end", f"Plan already consumed for {state.id}, not re-staging")
|
|
141
|
-
log_diagnostic("session_end", "decide", f"Skipping re-stage for {state.id}",
|
|
142
|
-
decision="skip_restage", reasoning="plan_hash exists but plan_consumed=True",
|
|
143
|
-
inputs={"plan_hash": state.plan_hash, "plan_consumed": True})
|
|
144
|
-
else:
|
|
145
|
-
log_info("session_end", f"Plan mode active (rejected), skipping plan staging for {state.id}")
|
|
146
|
-
|
|
147
|
-
# Handoff staging (mirrors plan staging above)
|
|
148
|
-
# Note: if plan already set has_plan, mode != "active" so handoff check skips (plan takes priority)
|
|
149
|
-
if state.handoff_path and state.mode == "active" and not state.handoff_consumed:
|
|
150
|
-
state.mode = "has_handoff"
|
|
151
|
-
log_info("session_end", f"Staged handoff for next session: {state.id} -> has_handoff")
|
|
152
|
-
elif state.handoff_path and state.mode == "active" and state.handoff_consumed:
|
|
153
|
-
log_debug("session_end", f"Handoff already consumed for {state.id}, not re-staging")
|
|
154
|
-
|
|
155
|
-
if save_state(state, project_root):
|
|
156
|
-
log_info("session_end", f"Saved last_session for {state.id}")
|
|
157
|
-
log_diagnostic("session_end", "result", f"Saved state for {state.id}",
|
|
158
|
-
decision="saved", inputs={"context_id": state.id, "mode": state.mode,
|
|
159
|
-
"has_plan_hash": bool(state.plan_hash),
|
|
160
|
-
"git_files": len(git_state.get("uncommitted_files", []))})
|
|
161
|
-
else:
|
|
162
|
-
log_error("session_end", f"Failed to save state for {state.id}")
|
|
163
|
-
|
|
164
|
-
except Exception as e:
|
|
165
|
-
import traceback
|
|
166
|
-
tb = traceback.format_exc()
|
|
167
|
-
from lib.base.hook_utils import log_hook_error
|
|
168
|
-
log_hook_error("session_end", e, "SessionEnd", traceback_str=tb)
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if __name__ == "__main__":
|
|
172
|
-
from lib.base.hook_utils import run_hook
|
|
173
|
-
run_hook(main, "session_end")
|
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""SessionStart hook for post-compaction and post-clear restore.
|
|
3
|
-
|
|
4
|
-
This hook fires when a new session starts. It handles:
|
|
5
|
-
|
|
6
|
-
1. Post-clear restore (source="clear"): After ExitPlanMode "clear context",
|
|
7
|
-
Claude Code runs /clear and auto-pastes the plan. The auto-paste bypasses
|
|
8
|
-
all hooks (UserPromptSubmit never fires), so this hook bridges the gap:
|
|
9
|
-
find the has_plan context (set by session_end moments ago), bind the new
|
|
10
|
-
session, transition has_plan → active, and inject restoration context.
|
|
11
|
-
|
|
12
|
-
2. Post-compaction restore (source="compact"): The session is already bound
|
|
13
|
-
to a context. Load state and inject rich restoration context so Claude
|
|
14
|
-
can continue seamlessly after compaction.
|
|
15
|
-
|
|
16
|
-
Hook input:
|
|
17
|
-
{
|
|
18
|
-
"hook_event_name": "SessionStart",
|
|
19
|
-
"session_id": "abc123",
|
|
20
|
-
"source": "clear", # or "startup", "resume", "compact"
|
|
21
|
-
"permission_mode": "bypassPermissions",
|
|
22
|
-
"model": "...",
|
|
23
|
-
...
|
|
24
|
-
}
|
|
25
|
-
"""
|
|
26
|
-
import sys
|
|
27
|
-
from pathlib import Path
|
|
28
|
-
|
|
29
|
-
# Add parent directories to path for imports
|
|
30
|
-
SCRIPT_DIR = Path(__file__).resolve().parent
|
|
31
|
-
SHARED_LIB = SCRIPT_DIR.parent / "lib"
|
|
32
|
-
sys.path.insert(0, str(SHARED_LIB.parent))
|
|
33
|
-
|
|
34
|
-
from lib.base.hook_utils import emit_context, load_hook_input, log_debug, log_info, log_error, log_diagnostic
|
|
35
|
-
from lib.base.utils import project_dir
|
|
36
|
-
from lib.context.context_store import get_context_by_session_id, get_all_contexts, bind_session, update_mode
|
|
37
|
-
from lib.context.context_formatter import _build_restore_sections, format_handoff_continuation
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def _handle_compact_restore(hook_input, session_id, project_root):
|
|
41
|
-
"""
|
|
42
|
-
Handle post-compaction restore.
|
|
43
|
-
|
|
44
|
-
After compaction, the session is already bound to a context.
|
|
45
|
-
Load state and inject rich restoration context via additionalContext.
|
|
46
|
-
"""
|
|
47
|
-
state = get_context_by_session_id(session_id, project_root)
|
|
48
|
-
if not state:
|
|
49
|
-
log_debug("session_start", "No context bound to session after compact")
|
|
50
|
-
return
|
|
51
|
-
|
|
52
|
-
log_info("session_start", f"Post-compaction restore for context: {state.id}")
|
|
53
|
-
|
|
54
|
-
# Build restoration context
|
|
55
|
-
mode_display = state.mode.replace("_", " ").title() if state.mode != "idle" else "Active"
|
|
56
|
-
|
|
57
|
-
lines = [
|
|
58
|
-
f"## Resuming Context After Compaction: {state.id}",
|
|
59
|
-
"",
|
|
60
|
-
f"**Summary:** {state.summary}",
|
|
61
|
-
f"**Mode:** {mode_display}",
|
|
62
|
-
]
|
|
63
|
-
|
|
64
|
-
# Add restore sections (tasks, git state, plan content)
|
|
65
|
-
# inline_plan=True because plan content is NOT auto-pasted after compaction
|
|
66
|
-
restore = _build_restore_sections(state, project_root, inline_plan=True)
|
|
67
|
-
if restore:
|
|
68
|
-
lines.append(restore)
|
|
69
|
-
|
|
70
|
-
lines.extend([
|
|
71
|
-
"",
|
|
72
|
-
"---",
|
|
73
|
-
"",
|
|
74
|
-
"**Instructions:**",
|
|
75
|
-
"Context was compacted to free memory. Your previous conversation has been summarized.",
|
|
76
|
-
"1. Review the previous work above",
|
|
77
|
-
"2. Continue from where you left off",
|
|
78
|
-
])
|
|
79
|
-
|
|
80
|
-
restore_context = "\n".join(lines)
|
|
81
|
-
emit_context(restore_context)
|
|
82
|
-
log_info("session_start", f"Injected post-compaction restore context for {state.id}")
|
|
83
|
-
log_diagnostic("session_start", "result", f"Compact restore complete for {state.id}",
|
|
84
|
-
decision="injected", inputs={"context_id": state.id, "mode": state.mode})
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def _handle_clear_restore(hook_input, session_id, project_root):
|
|
88
|
-
"""
|
|
89
|
-
Handle plan context restoration after /clear.
|
|
90
|
-
|
|
91
|
-
After ExitPlanMode "clear context", Claude Code auto-pastes the plan
|
|
92
|
-
but the auto-paste bypasses all hooks — UserPromptSubmit never fires.
|
|
93
|
-
This means the new session is never bound to a context.
|
|
94
|
-
|
|
95
|
-
Fix: find the has_plan context (set by session_end moments ago),
|
|
96
|
-
bind the new session to it, and inject restoration context.
|
|
97
|
-
"""
|
|
98
|
-
# Find has_plan contexts (sorted by last_active descending)
|
|
99
|
-
has_plan = [
|
|
100
|
-
c for c in get_all_contexts(status="active", project_root=project_root)
|
|
101
|
-
if c.mode == "has_plan"
|
|
102
|
-
]
|
|
103
|
-
|
|
104
|
-
if not has_plan:
|
|
105
|
-
# Check for has_handoff contexts (mirrors plan logic)
|
|
106
|
-
has_handoff = [
|
|
107
|
-
c for c in get_all_contexts(status="active", project_root=project_root)
|
|
108
|
-
if c.mode == "has_handoff"
|
|
109
|
-
]
|
|
110
|
-
if has_handoff:
|
|
111
|
-
target = has_handoff[0]
|
|
112
|
-
log_info("session_start", f"Found has_handoff context after /clear: {target.id}")
|
|
113
|
-
bind_session(target.id, session_id, project_root)
|
|
114
|
-
log_info("session_start", f"Bound session {session_id[:8]}... to {target.id}")
|
|
115
|
-
update_mode(target.id, "active", project_root=project_root, handoff_consumed=True)
|
|
116
|
-
log_info("session_start", f"Transitioned {target.id}: has_handoff -> active (handoff_consumed=True)")
|
|
117
|
-
restore_context = format_handoff_continuation(target, project_root)
|
|
118
|
-
emit_context(restore_context)
|
|
119
|
-
log_info("session_start", f"Injected handoff-restore context for {target.id}")
|
|
120
|
-
return
|
|
121
|
-
|
|
122
|
-
log_debug("session_start", "No has_plan or has_handoff contexts found after /clear")
|
|
123
|
-
return
|
|
124
|
-
|
|
125
|
-
# Pick the most recently active one (first in list, already sorted)
|
|
126
|
-
target = has_plan[0]
|
|
127
|
-
log_info("session_start", f"Found has_plan context after /clear: {target.id}")
|
|
128
|
-
|
|
129
|
-
# Bind new session to this context
|
|
130
|
-
bind_session(target.id, session_id, project_root)
|
|
131
|
-
log_info("session_start", f"Bound session {session_id[:8]}... to {target.id}")
|
|
132
|
-
|
|
133
|
-
# Transition has_plan → active (consume the transient state)
|
|
134
|
-
update_mode(target.id, "active", project_root=project_root, plan_consumed=True)
|
|
135
|
-
log_info("session_start", f"Transitioned {target.id}: has_plan -> active (plan_consumed=True)")
|
|
136
|
-
|
|
137
|
-
# Inject restoration context (tasks, git state, plan path reference)
|
|
138
|
-
# Plan CONTENT is not injected — Claude Code auto-pastes it after /clear
|
|
139
|
-
mode_display = "Active (Plan Restored)"
|
|
140
|
-
lines = [
|
|
141
|
-
f"## Resuming Context After Plan Clear: {target.id}",
|
|
142
|
-
"",
|
|
143
|
-
f"**Summary:** {target.summary}",
|
|
144
|
-
f"**Mode:** {mode_display}",
|
|
145
|
-
]
|
|
146
|
-
|
|
147
|
-
restore = _build_restore_sections(target, project_root)
|
|
148
|
-
if restore:
|
|
149
|
-
lines.append(restore)
|
|
150
|
-
|
|
151
|
-
lines.extend([
|
|
152
|
-
"",
|
|
153
|
-
"---",
|
|
154
|
-
"",
|
|
155
|
-
"**Instructions:**",
|
|
156
|
-
"Context was cleared for plan implementation. Your plan content has been pasted above.",
|
|
157
|
-
"1. Review the plan content above",
|
|
158
|
-
"2. Implement the plan step by step",
|
|
159
|
-
])
|
|
160
|
-
|
|
161
|
-
restore_context = "\n".join(lines)
|
|
162
|
-
emit_context(restore_context)
|
|
163
|
-
log_info("session_start", f"Injected clear-restore context for {target.id}")
|
|
164
|
-
log_diagnostic("session_start", "result", f"Clear restore complete for {target.id}",
|
|
165
|
-
decision="injected", inputs={"context_id": target.id, "mode_transition": "has_plan->active"})
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
def main():
|
|
169
|
-
"""Handle post-compaction and post-clear restore on session start."""
|
|
170
|
-
try:
|
|
171
|
-
hook_input = load_hook_input()
|
|
172
|
-
if not hook_input:
|
|
173
|
-
return
|
|
174
|
-
|
|
175
|
-
source = hook_input.get("source", "unknown")
|
|
176
|
-
permission_mode = hook_input.get("permission_mode", "default")
|
|
177
|
-
session_id = hook_input.get("session_id", "unknown")
|
|
178
|
-
project_root = project_dir(hook_input)
|
|
179
|
-
|
|
180
|
-
log_info("session_start", f"source={source}, permission_mode={permission_mode}, session={session_id[:8]}...")
|
|
181
|
-
log_diagnostic("session_start", "receive", f"source={source}, session={session_id[:8]}",
|
|
182
|
-
inputs={"source": source, "session_id": session_id[:12], "permission_mode": permission_mode})
|
|
183
|
-
|
|
184
|
-
if source == "compact":
|
|
185
|
-
log_diagnostic("session_start", "decide", "Taking compact restore path",
|
|
186
|
-
decision="compact_restore", reasoning="source=compact")
|
|
187
|
-
_handle_compact_restore(hook_input, session_id, project_root)
|
|
188
|
-
elif source == "clear":
|
|
189
|
-
log_diagnostic("session_start", "decide", "Taking clear restore path",
|
|
190
|
-
decision="clear_restore", reasoning="source=clear, looking for has_plan context")
|
|
191
|
-
_handle_clear_restore(hook_input, session_id, project_root)
|
|
192
|
-
else:
|
|
193
|
-
log_diagnostic("session_start", "decide", f"No action for source={source}",
|
|
194
|
-
decision="skip", reasoning=f"source={source} has no handler")
|
|
195
|
-
log_debug("session_start", f"No action for source='{source}'")
|
|
196
|
-
|
|
197
|
-
except Exception as e:
|
|
198
|
-
import traceback
|
|
199
|
-
tb = traceback.format_exc()
|
|
200
|
-
from lib.base.hook_utils import log_hook_error
|
|
201
|
-
log_hook_error("session_start", e, "SessionStart", traceback_str=tb)
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if __name__ == "__main__":
|
|
205
|
-
from lib.base.hook_utils import run_hook
|
|
206
|
-
run_hook(main, "session_start")
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""PostToolUse hook - captures TaskCreate operations for persistence.
|
|
3
|
-
|
|
4
|
-
This hook runs after Claude uses the TaskCreate tool and automatically
|
|
5
|
-
records the task in the context's state.json.
|
|
6
|
-
|
|
7
|
-
Hook input (from Claude Code):
|
|
8
|
-
{
|
|
9
|
-
"hook_event_name": "PostToolUse",
|
|
10
|
-
"tool_name": "TaskCreate",
|
|
11
|
-
"tool_input": {
|
|
12
|
-
"subject": "Task subject",
|
|
13
|
-
"description": "Task description",
|
|
14
|
-
"activeForm": "Present continuous form",
|
|
15
|
-
"metadata": {"context": "context-id", ...}
|
|
16
|
-
},
|
|
17
|
-
"tool_response": {"task": {"id": "1", "subject": "..."}},
|
|
18
|
-
"session_id": "abc123",
|
|
19
|
-
"cwd": "/path/to/project"
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
Hook output:
|
|
23
|
-
- Silent on success (no stdout output)
|
|
24
|
-
- Logs to stderr for debugging
|
|
25
|
-
"""
|
|
26
|
-
import sys
|
|
27
|
-
from pathlib import Path
|
|
28
|
-
|
|
29
|
-
# Add parent directories to path for imports
|
|
30
|
-
SCRIPT_DIR = Path(__file__).resolve().parent
|
|
31
|
-
SHARED_LIB = SCRIPT_DIR.parent / "lib"
|
|
32
|
-
sys.path.insert(0, str(SHARED_LIB.parent))
|
|
33
|
-
|
|
34
|
-
from lib.base.hook_utils import (
|
|
35
|
-
load_hook_input,
|
|
36
|
-
validate_hook_event,
|
|
37
|
-
get_tool_input,
|
|
38
|
-
check_skip_persistence,
|
|
39
|
-
safe_hook_main,
|
|
40
|
-
run_hook,
|
|
41
|
-
log_debug,
|
|
42
|
-
log_info,
|
|
43
|
-
log_warn,
|
|
44
|
-
log_error,
|
|
45
|
-
)
|
|
46
|
-
from lib.base.utils import project_dir
|
|
47
|
-
from lib.context.context_store import get_context_by_session_id
|
|
48
|
-
from lib.context.task_tracker import add_task, generate_next_task_id
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
@safe_hook_main("task_create_capture")
|
|
52
|
-
def main() -> int:
|
|
53
|
-
"""Main hook entry point."""
|
|
54
|
-
payload = load_hook_input()
|
|
55
|
-
if not payload:
|
|
56
|
-
return 0
|
|
57
|
-
|
|
58
|
-
if not validate_hook_event(payload, "PostToolUse", "TaskCreate"):
|
|
59
|
-
return 0
|
|
60
|
-
|
|
61
|
-
tool_input = get_tool_input(payload)
|
|
62
|
-
if not tool_input:
|
|
63
|
-
log_warn("task_create_capture", "Invalid tool_input: not a dict")
|
|
64
|
-
return 0
|
|
65
|
-
|
|
66
|
-
if check_skip_persistence(payload, "task_create_capture"):
|
|
67
|
-
return 0
|
|
68
|
-
|
|
69
|
-
project_root = project_dir(payload)
|
|
70
|
-
session_id = payload.get("session_id", "")
|
|
71
|
-
|
|
72
|
-
# Find context by session ID
|
|
73
|
-
state = get_context_by_session_id(session_id, project_root)
|
|
74
|
-
if not state:
|
|
75
|
-
log_debug("task_create_capture", "No context available - skipping persistence")
|
|
76
|
-
return 0
|
|
77
|
-
|
|
78
|
-
context_id = state.id
|
|
79
|
-
|
|
80
|
-
# Extract task data
|
|
81
|
-
subject = tool_input.get("subject", "")
|
|
82
|
-
if not subject:
|
|
83
|
-
log_warn("task_create_capture", "Missing required field: subject")
|
|
84
|
-
return 0
|
|
85
|
-
|
|
86
|
-
description = tool_input.get("description", "")
|
|
87
|
-
active_form = tool_input.get("activeForm", "")
|
|
88
|
-
|
|
89
|
-
# Add task to state.json
|
|
90
|
-
task = add_task(
|
|
91
|
-
context_id=context_id,
|
|
92
|
-
subject=subject,
|
|
93
|
-
description=description,
|
|
94
|
-
active_form=active_form,
|
|
95
|
-
session_id=session_id,
|
|
96
|
-
project_root=project_root,
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
if task:
|
|
100
|
-
log_info("task_create_capture", f"Recorded task: {task['id']} in {context_id}")
|
|
101
|
-
else:
|
|
102
|
-
log_error("task_create_capture", f"Failed to add task in {context_id}")
|
|
103
|
-
|
|
104
|
-
return 0
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if __name__ == "__main__":
|
|
108
|
-
run_hook(main, "task_create_capture")
|