aiwcli 0.9.6 → 0.9.8

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.
Files changed (91) hide show
  1. package/dist/commands/init/index.d.ts +0 -8
  2. package/dist/commands/init/index.js +5 -35
  3. package/dist/lib/index.d.ts +3 -4
  4. package/dist/lib/index.js +3 -5
  5. package/dist/lib/settings-hierarchy.js +5 -16
  6. package/dist/lib/template-installer.d.ts +9 -0
  7. package/dist/lib/template-installer.js +3 -14
  8. package/dist/lib/template-merger.js +1 -12
  9. package/dist/lib/windsurf-hooks-hierarchy.js +2 -13
  10. package/dist/templates/CLAUDE.md +49 -18
  11. package/dist/templates/_shared/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  12. package/dist/templates/_shared/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
  13. package/dist/templates/_shared/hooks/__pycache__/context_enforcer.cpython-313.pyc +0 -0
  14. package/dist/templates/_shared/hooks/__pycache__/context_monitor.cpython-313.pyc +0 -0
  15. package/dist/templates/_shared/hooks/__pycache__/file-suggestion.cpython-313.pyc +0 -0
  16. package/dist/templates/_shared/hooks/__pycache__/session_start.cpython-313.pyc +0 -0
  17. package/dist/templates/_shared/hooks/__pycache__/task_create_atomicity.cpython-313.pyc +0 -0
  18. package/dist/templates/_shared/hooks/__pycache__/task_create_capture.cpython-313.pyc +0 -0
  19. package/dist/templates/_shared/hooks/__pycache__/task_update_capture.cpython-313.pyc +0 -0
  20. package/dist/templates/_shared/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
  21. package/dist/templates/_shared/hooks/context_enforcer.py +21 -71
  22. package/dist/templates/_shared/hooks/context_monitor.py +78 -1
  23. package/dist/templates/_shared/hooks/pre_compact.py +89 -0
  24. package/dist/templates/_shared/hooks/session_end.py +111 -0
  25. package/dist/templates/_shared/hooks/session_start.py +104 -47
  26. package/dist/templates/_shared/hooks/task_create_atomicity.py +33 -61
  27. package/dist/templates/_shared/hooks/task_create_capture.py +1 -0
  28. package/dist/templates/_shared/hooks/task_update_capture.py +15 -0
  29. package/dist/templates/_shared/hooks/user_prompt_submit.py +13 -27
  30. package/dist/templates/_shared/lib/__pycache__/__init__.cpython-313.pyc +0 -0
  31. package/dist/templates/_shared/lib/base/__pycache__/__init__.cpython-313.pyc +0 -0
  32. package/dist/templates/_shared/lib/base/__pycache__/atomic_write.cpython-313.pyc +0 -0
  33. package/dist/templates/_shared/lib/base/__pycache__/constants.cpython-313.pyc +0 -0
  34. package/dist/templates/_shared/lib/base/__pycache__/hook_utils.cpython-313.pyc +0 -0
  35. package/dist/templates/_shared/lib/base/__pycache__/inference.cpython-313.pyc +0 -0
  36. package/dist/templates/_shared/lib/base/__pycache__/stop_words.cpython-313.pyc +0 -0
  37. package/dist/templates/_shared/lib/base/__pycache__/subprocess_utils.cpython-313.pyc +0 -0
  38. package/dist/templates/_shared/lib/base/__pycache__/utils.cpython-313.pyc +0 -0
  39. package/dist/templates/_shared/lib/base/constants.py +18 -4
  40. package/dist/templates/_shared/lib/base/hook_utils.py +0 -24
  41. package/dist/templates/_shared/lib/base/stop_words.py +23 -0
  42. package/dist/templates/_shared/lib/base/utils.py +9 -4
  43. package/dist/templates/_shared/lib/context/__init__.py +0 -8
  44. package/dist/templates/_shared/lib/context/__pycache__/__init__.cpython-313.pyc +0 -0
  45. package/dist/templates/_shared/lib/context/__pycache__/cache.cpython-313.pyc +0 -0
  46. package/dist/templates/_shared/lib/context/__pycache__/context_extractor.cpython-313.pyc +0 -0
  47. package/dist/templates/_shared/lib/context/__pycache__/context_manager.cpython-313.pyc +0 -0
  48. package/dist/templates/_shared/lib/context/__pycache__/discovery.cpython-313.pyc +0 -0
  49. package/dist/templates/_shared/lib/context/__pycache__/event_log.cpython-313.pyc +0 -0
  50. package/dist/templates/_shared/lib/context/__pycache__/plan_archive.cpython-313.pyc +0 -0
  51. package/dist/templates/_shared/lib/context/__pycache__/task_sync.cpython-313.pyc +0 -0
  52. package/dist/templates/_shared/lib/context/auto_state.py +167 -0
  53. package/dist/templates/_shared/lib/context/context_manager.py +6 -3
  54. package/dist/templates/_shared/lib/context/discovery.py +167 -57
  55. package/dist/templates/_shared/lib/context/event_log.py +8 -0
  56. package/dist/templates/_shared/lib/context/plan_archive.py +0 -146
  57. package/dist/templates/_shared/lib/context/task_sync.py +160 -43
  58. package/dist/templates/_shared/lib/templates/__pycache__/__init__.cpython-313.pyc +0 -0
  59. package/dist/templates/_shared/lib/templates/__pycache__/formatters.cpython-313.pyc +0 -0
  60. package/dist/templates/_shared/lib/templates/__pycache__/persona_questions.cpython-313.pyc +0 -0
  61. package/dist/templates/_shared/lib/templates/__pycache__/plan_context.cpython-313.pyc +0 -0
  62. package/dist/templates/_shared/lib/templates/plan_context.py +24 -41
  63. package/dist/templates/cc-native/.claude/settings.json +32 -10
  64. package/dist/templates/cc-native/_cc-native/agents/CLAUDE.md +1 -1
  65. package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +8 -1
  66. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/add_plan_context.cpython-313.pyc +0 -0
  67. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-plan-review.cpython-313.pyc +0 -0
  68. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/suggest-fresh-perspective.cpython-313.pyc +0 -0
  69. package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.py +1 -8
  70. package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +65 -47
  71. package/dist/templates/cc-native/_cc-native/lib/CLAUDE.md +29 -6
  72. package/dist/templates/cc-native/_cc-native/lib/__init__.py +0 -4
  73. package/dist/templates/cc-native/_cc-native/lib/__pycache__/__init__.cpython-313.pyc +0 -0
  74. package/dist/templates/cc-native/_cc-native/lib/__pycache__/atomic_write.cpython-313.pyc +0 -0
  75. package/dist/templates/cc-native/_cc-native/lib/__pycache__/constants.cpython-313.pyc +0 -0
  76. package/dist/templates/cc-native/_cc-native/lib/__pycache__/debug.cpython-313.pyc +0 -0
  77. package/dist/templates/cc-native/_cc-native/lib/__pycache__/orchestrator.cpython-313.pyc +0 -0
  78. package/dist/templates/cc-native/_cc-native/lib/__pycache__/state.cpython-313.pyc +0 -0
  79. package/dist/templates/cc-native/_cc-native/lib/__pycache__/utils.cpython-313.pyc +0 -0
  80. package/dist/templates/cc-native/_cc-native/lib/orchestrator.py +71 -15
  81. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/__init__.cpython-313.pyc +0 -0
  82. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/agent.cpython-313.pyc +0 -0
  83. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/base.cpython-313.pyc +0 -0
  84. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/codex.cpython-313.pyc +0 -0
  85. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/gemini.cpython-313.pyc +0 -0
  86. package/dist/templates/cc-native/_cc-native/lib/reviewers/agent.py +6 -4
  87. package/dist/templates/cc-native/_cc-native/lib/state.py +11 -9
  88. package/dist/templates/cc-native/_cc-native/lib/utils.py +26 -109
  89. package/dist/templates/cc-native/_cc-native/scripts/__pycache__/aggregate_agents.cpython-313.pyc +0 -0
  90. package/oclif.manifest.json +1 -1
  91. package/package.json +1 -1
@@ -1,18 +1,14 @@
1
1
  #!/usr/bin/env python3
2
- """SessionStart hook for mode transitions after /clear.
2
+ """SessionStart hook for mode transitions and post-compaction restore.
3
3
 
4
- This hook fires when a new session starts. It handles the critical transition
5
- from `pending_implementation` to `implementing` when a session starts after
6
- /clear with bypass permissions.
4
+ This hook fires when a new session starts. It handles:
7
5
 
8
- The flow is:
9
- 1. User approves plan (ExitPlanMode) -> mode = pending_implementation
10
- 2. User clicks "yes and clear and bypass permissions"
11
- 3. SessionStart fires with source="clear" and permission_mode="bypassPermissions"
12
- 4. This hook transitions mode to "implementing"
6
+ 1. Mode transition from `pending_implementation` to `implementing` when
7
+ a session starts after /clear with bypass permissions.
13
8
 
14
- Without this hook, the mode stays stuck at pending_implementation because
15
- UserPromptSubmit may not receive the correct permission_mode after /clear.
9
+ 2. Post-compaction restore: when source="compact", the session is already
10
+ bound to a context. Load auto-state and inject rich restoration context
11
+ so Claude can continue seamlessly after compaction.
16
12
 
17
13
  Hook input:
18
14
  {
@@ -24,6 +20,7 @@ Hook input:
24
20
  ...
25
21
  }
26
22
  """
23
+ import json
27
24
  import sys
28
25
  from pathlib import Path
29
26
 
@@ -36,22 +33,106 @@ from lib.base.hook_utils import load_hook_input
36
33
  from lib.base.utils import eprint, project_dir
37
34
  from lib.context.context_manager import (
38
35
  get_all_in_flight_contexts,
36
+ get_context_by_session_id,
39
37
  update_plan_status,
40
38
  update_context_session_id,
41
39
  )
40
+ from lib.context.auto_state import load_auto_state
41
+ from lib.context.discovery import (
42
+ _build_restore_sections,
43
+ find_plan_path,
44
+ format_relative_time,
45
+ )
46
+ from lib.context.task_sync import generate_task_summary
42
47
 
43
48
 
44
- def main():
49
+ def _handle_clear_transition(hook_input, session_id, project_root):
50
+ """Handle /clear mode transitions (existing behavior)."""
51
+ permission_mode = hook_input.get("permission_mode", "default")
52
+
53
+ if permission_mode == "plan":
54
+ eprint("[session_start] Skipping: permission_mode is 'plan' (in planning mode)")
55
+ return
56
+
57
+ in_flight_contexts = get_all_in_flight_contexts(project_root)
58
+ if not in_flight_contexts:
59
+ eprint("[session_start] No in-flight contexts found")
60
+ return
61
+
62
+ pending_contexts = [
63
+ ctx for ctx in in_flight_contexts
64
+ if ctx.in_flight and ctx.in_flight.mode == "pending_implementation"
65
+ ]
66
+ for ctx in pending_contexts:
67
+ eprint(f"[session_start] Transitioning {ctx.id} from pending_implementation to implementing")
68
+ update_plan_status(ctx.id, "implementing", project_root=project_root)
69
+ update_context_session_id(ctx.id, session_id, project_root)
70
+ eprint(f"[session_start] Bound session {session_id[:8]}... to context {ctx.id}")
71
+
72
+ if pending_contexts:
73
+ eprint(f"[session_start] Transitioned {len(pending_contexts)} context(s) to implementing")
74
+
75
+
76
+ def _handle_compact_restore(hook_input, session_id, project_root):
77
+ """
78
+ Handle post-compaction restore.
79
+
80
+ After compaction, the session is already bound to a context.
81
+ Load auto-state and inject rich restoration context via additionalContext.
45
82
  """
46
- Handle mode transitions on session start.
83
+ context = get_context_by_session_id(session_id, project_root)
84
+ if not context:
85
+ eprint("[session_start] No context bound to session after compact")
86
+ return
87
+
88
+ context_id = context.id
89
+ eprint(f"[session_start] Post-compaction restore for context: {context_id}")
90
+
91
+ # Build restoration context
92
+ mode_display = "Active"
93
+ if context.in_flight and context.in_flight.mode != "none":
94
+ mode_display = context.in_flight.mode.replace("_", " ").title()
95
+
96
+ lines = [
97
+ f"## Resuming Context After Compaction: {context_id}",
98
+ "",
99
+ f"**Summary:** {context.summary}",
100
+ f"**Mode:** {mode_display}",
101
+ ]
102
+
103
+ # Add restore sections (auto-state, tasks, git)
104
+ restore = _build_restore_sections(context, project_root)
105
+ if restore:
106
+ lines.append(restore)
107
+
108
+ lines.extend([
109
+ "",
110
+ "---",
111
+ "",
112
+ "**Instructions:**",
113
+ "Context was compacted to free memory. Your previous conversation has been summarized.",
114
+ "1. Review the previous work above",
115
+ "2. Continue from where you left off",
116
+ ])
117
+
118
+ restore_context = "\n".join(lines)
119
+
120
+ # Output as additionalContext so Claude sees it
121
+ output = {
122
+ "hookSpecificOutput": {
123
+ "additionalContext": restore_context
124
+ }
125
+ }
126
+ print(json.dumps(output, ensure_ascii=False))
127
+ eprint(f"[session_start] Injected post-compaction restore context for {context_id}")
128
+
47
129
 
48
- When source is "clear" and permission_mode is "bypassPermissions" or "acceptEdits",
49
- transition any pending_implementation context to implementing.
130
+ def main():
131
+ """
132
+ Handle mode transitions and post-compaction restore on session start.
50
133
  """
51
134
  try:
52
- # Read hook input using shared utility
53
135
  hook_input = load_hook_input()
54
-
55
136
  if not hook_input:
56
137
  return
57
138
 
@@ -62,36 +143,12 @@ def main():
62
143
 
63
144
  eprint(f"[session_start] source={source}, permission_mode={permission_mode}, session={session_id[:8]}...")
64
145
 
65
- # Only handle /clear with bypass/accept permissions
66
- if source != "clear":
67
- eprint(f"[session_start] Skipping: source is '{source}', not 'clear'")
68
- return
69
-
70
- if permission_mode == "plan":
71
- eprint(f"[session_start] Skipping: permission_mode is 'plan' (in planning mode)")
72
- return
73
-
74
- # Find contexts in pending_implementation mode
75
- in_flight_contexts = get_all_in_flight_contexts(project_root)
76
- pending_contexts = [
77
- ctx for ctx in in_flight_contexts
78
- if ctx.in_flight and ctx.in_flight.mode == "pending_implementation"
79
- ]
80
-
81
- if not pending_contexts:
82
- eprint("[session_start] No pending_implementation contexts found")
83
- return
84
-
85
- # Transition each pending context to implementing
86
- for ctx in pending_contexts:
87
- eprint(f"[session_start] Transitioning {ctx.id} from pending_implementation to implementing")
88
- update_plan_status(ctx.id, "implementing", project_root=project_root)
89
-
90
- # Also bind this session to the context
91
- update_context_session_id(ctx.id, session_id, project_root)
92
- eprint(f"[session_start] Bound session {session_id[:8]}... to context {ctx.id}")
93
-
94
- eprint(f"[session_start] Transitioned {len(pending_contexts)} context(s) to implementing")
146
+ if source == "clear":
147
+ _handle_clear_transition(hook_input, session_id, project_root)
148
+ elif source == "compact":
149
+ _handle_compact_restore(hook_input, session_id, project_root)
150
+ else:
151
+ eprint(f"[session_start] No action for source='{source}'")
95
152
 
96
153
  except Exception as e:
97
154
  eprint(f"[session_start] ERROR: {e}")
@@ -34,64 +34,47 @@ from lib.base.inference import inference
34
34
  # - Direct imperative instructions
35
35
  # - Explicit JSON output format
36
36
 
37
- ASSESSMENT_SYSTEM_PROMPT = """You assess task descriptions for atomicity and forkability.
37
+ ASSESSMENT_SYSTEM_PROMPT = """Assess whether a task description is self-contained enough for a subagent with zero conversation history to execute it.
38
38
 
39
- ## Definitions
39
+ ## What Makes a Good Task
40
40
 
41
- **Atomic Task:** Contains ALL context needed for independent execution without reading prior conversation.
42
-
43
- **Forkable Task:** Can be delegated to a subagent with ZERO conversation history and still be completed successfully.
44
-
45
- ## Signs of Non-Atomic Tasks
46
-
47
- Look for these indicators:
48
- - Contextual references: "the file above", "as discussed", "the mentioned function", "this bug"
49
- - Vague descriptions assuming prior knowledge: "fix the bug", "update it", "finish the work"
50
- - Missing specifics: which file? what function? what expected behavior? what error?
51
- - Pronouns without antecedents: "it", "they", "the issue" without explicit definition
52
-
53
- ## Signs of Atomic Tasks
54
-
55
- Well-specified tasks include:
41
+ A well-specified task includes:
56
42
  - Explicit file paths: "Edit src/utils/parser.py"
57
- - Specific function names: "Modify the validate_input() function"
58
- - Clear expected behavior: "Should return 404 when user not found"
59
- - Complete error context: "TypeError on line 45 when input is None"
43
+ - Specific function/component names: "Modify validate_input()"
44
+ - Clear expected behavior: "Return 404 when user not found"
45
+ - Concrete error context: "TypeError on line 45 when input is None"
60
46
 
61
- ## Examples
47
+ ## What Makes a Poor Task
62
48
 
63
- **Example 1: Non-Atomic Task**
64
- Subject: "Fix the bug"
65
- Description: "The issue we discussed earlier needs to be resolved"
66
- Assessment: NOT atomic (no file, no function, no error details, references "discussed earlier")
49
+ Watch for context-dependent references:
50
+ - Dangling references: "the file above", "as discussed", "this bug"
51
+ - Vague actions: "fix the bug", "update it", "finish the work"
52
+ - Pronouns without antecedents: "it", "they", "the issue"
53
+ - Missing specifics: which file? what function? what behavior?
67
54
 
68
- **Example 2: Atomic Task**
69
- Subject: "Fix null pointer in user lookup"
70
- Description: "In src/services/user.py, the get_user_by_id() function raises TypeError when user_id is None. Add null check at line 23 that returns None early instead of calling database.query()."
71
- Assessment: Atomic (file path, function name, specific error, exact fix location, expected behavior)
55
+ ## Examples
72
56
 
73
- **Example 3: Partially Atomic Task**
74
- Subject: "Add validation to form"
75
- Description: "Add email validation to the signup form. Return error message if invalid."
76
- Assessment: NOT fully atomic (missing: which file contains the form? what validation rules? where to display error?)
57
+ **Atomic** — Subject: "Fix null pointer in user lookup"
58
+ Description: "In src/services/user.py, get_user_by_id() raises TypeError when user_id is None. Add null check at line 23 to return None instead of calling database.query()."
59
+ Why: file path, function, error, fix location, expected behavior.
77
60
 
78
- ## Output Format
61
+ **Not atomic** — Subject: "Fix the bug"
62
+ Description: "The issue we discussed earlier needs to be resolved"
63
+ Why: no file, no function, no error details, references conversation history.
79
64
 
80
- Respond with valid JSON only:
81
- {
82
- "atomic": true/false,
83
- "forkable": true/false,
84
- "issues": ["specific issue 1", "specific issue 2"],
85
- "recommendation": "brief actionable suggestion if issues exist, or 'Task is well-specified' if good"
86
- }"""
65
+ **Partially atomic** Subject: "Add validation to form"
66
+ Description: "Add email validation to the signup form. Return error if invalid."
67
+ Why: missing which file, what validation rules, where to display error.
87
68
 
88
- ASSESSMENT_USER_TEMPLATE = """Assess this task for atomicity and forkability:
69
+ ## Output
89
70
 
90
- **Subject:** {subject}
71
+ Respond with JSON only:
72
+ {"atomic": true/false, "forkable": true/false, "issues": ["issue 1", "issue 2"], "recommendation": "actionable suggestion or 'Well-specified'"}"""
91
73
 
74
+ ASSESSMENT_USER_TEMPLATE = """**Subject:** {subject}
92
75
  **Description:** {description}
93
76
 
94
- Evaluate whether a subagent with zero prior context could execute this task successfully."""
77
+ Could a subagent with zero conversation history execute this task?"""
95
78
 
96
79
 
97
80
  @safe_hook_main("task_create_atomicity")
@@ -167,33 +150,22 @@ def main() -> int:
167
150
 
168
151
  # Build context message based on assessment
169
152
  if atomic and forkable:
170
- # Task is good - minimal positive feedback
171
- context_msg = "Task Assessment: Well-specified and forkable."
153
+ context_msg = "Task assessment: well-specified and ready for delegation."
172
154
  else:
173
- # Task has issues - inject detailed warning
174
- status_parts = []
175
- if not atomic:
176
- status_parts.append("NOT ATOMIC")
177
- if not forkable:
178
- status_parts.append("NOT FORKABLE")
155
+ # Constructive guidance what to add, not what's wrong
156
+ issues_text = "\n".join(f"- {issue}" for issue in issues) if issues else ""
179
157
 
180
- issues_text = "\n".join(f"- {issue}" for issue in issues) if issues else "- See recommendation below"
158
+ context_msg = f"""**Enrich this task for subagent delegation**
181
159
 
182
- context_msg = f"""**TASK ATOMICITY WARNING** ({', '.join(status_parts)})
160
+ A subagent receiving this task will have no conversation history. To make it actionable:
183
161
 
184
- This task may lack sufficient context for independent execution by a subagent.
185
-
186
- **Issues detected:**
187
162
  {issues_text}
188
163
 
189
- **Recommendation:** {recommendation}
190
-
191
- Consider adding specific file paths, function names, expected behaviors, or error details before creating this task."""
164
+ **Suggested enrichment:** {recommendation}"""
192
165
 
193
166
  # Output hook response with additionalContext
194
167
  out = {
195
168
  "hookSpecificOutput": {
196
- "hookEventName": "PreToolUse",
197
169
  "additionalContext": context_msg
198
170
  }
199
171
  }
@@ -115,6 +115,7 @@ def main() -> int:
115
115
  subject=subject,
116
116
  description=description,
117
117
  active_form=active_form,
118
+ session_id=session_id or "",
118
119
  project_root=project_root
119
120
  )
120
121
 
@@ -53,6 +53,7 @@ from lib.context.task_sync import (
53
53
  record_task_started,
54
54
  record_task_completed,
55
55
  record_task_blocked,
56
+ record_task_deleted,
56
57
  )
57
58
 
58
59
 
@@ -147,6 +148,7 @@ def main() -> int:
147
148
  success = record_task_started(
148
149
  context_id=context_id,
149
150
  task_id=persistent_task_id,
151
+ session_id=session_id or "",
150
152
  project_root=project_root
151
153
  )
152
154
  if success:
@@ -173,11 +175,23 @@ def main() -> int:
173
175
  work_summary=work_summary,
174
176
  files_changed=files_changed if isinstance(files_changed, list) else [],
175
177
  commit_ref=commit_ref,
178
+ session_id=session_id or "",
176
179
  project_root=project_root
177
180
  )
178
181
  if success:
179
182
  events_recorded.append("task_completed")
180
183
 
184
+ # Status: deleted
185
+ elif status == "deleted":
186
+ success = record_task_deleted(
187
+ context_id=context_id,
188
+ task_id=persistent_task_id,
189
+ session_id=session_id or "",
190
+ project_root=project_root
191
+ )
192
+ if success:
193
+ events_recorded.append("task_deleted")
194
+
181
195
  # Blocked by tasks
182
196
  if add_blocked_by and isinstance(add_blocked_by, list) and len(add_blocked_by) > 0:
183
197
  blocked_reason = f"Blocked by tasks: {', '.join(add_blocked_by)}"
@@ -185,6 +199,7 @@ def main() -> int:
185
199
  context_id=context_id,
186
200
  task_id=persistent_task_id,
187
201
  reason=blocked_reason,
202
+ session_id=session_id or "",
188
203
  project_root=project_root
189
204
  )
190
205
  if success:
@@ -46,36 +46,24 @@ def format_claudemd_reminder() -> str:
46
46
  return """
47
47
  ## CLAUDE.md Decision Capture
48
48
 
49
- When implementing changes, consider whether this work involves decisions with non-obvious rationale. If so, update or create a CLAUDE.md in the relevant directory.
49
+ **Placement rule:** CLAUDE.md files cascade subdirectories inherit from parents. Default to updating the nearest existing CLAUDE.md. Only create a new one at a semantic boundary where responsibility genuinely shifts (package roots with their own manifest, technology boundaries, domain boundaries, integration points).
50
50
 
51
- **When to update CLAUDE.md:**
52
- - Architectural choices (why this pattern over alternatives)
53
- - Non-obvious constraints (why something MUST be done a certain way)
54
- - Learned patterns (discovered issues that future work should avoid)
55
- - Integration decisions (why components connect this way)
56
- - Workarounds (temporary solutions with context on the underlying issue)
51
+ **Before creating a new CLAUDE.md, ask:** "Do the rules here actually differ from the parent?" If not, update the parent instead.
57
52
 
58
- **What to capture (use this format):**
53
+ **Keep files lean (progressive disclosure):** Each CLAUDE.md should contain only decisions relevant at that directory level. Verbose files become noise that degrades future task quality — every entry competes for attention in the context window. Capture only non-obvious rationale, not descriptions of what the code does.
59
54
 
60
- ```markdown
61
- ## [Topic]
62
-
63
- **Decision:** [What was decided]
64
- **Rationale:** [Why this approach was chosen]
65
- **Constraint:** [What breaks if this changes]
66
- ```
55
+ **What to capture** (only when decisions have non-obvious rationale):
56
+ - Architectural choices and their alternatives
57
+ - Non-obvious constraints (what breaks if this changes)
58
+ - Workarounds with context on the underlying issue
59
+ - Learned patterns that prevent future mistakes
67
60
 
68
- **Directory-specific:** Place CLAUDE.md in the directory closest to the affected code. If no CLAUDE.md exists, create one with a descriptive header.
69
-
70
- **Example new CLAUDE.md:**
61
+ **Format:**
71
62
 
72
63
  ```markdown
73
- # Component Name
74
-
75
- Development decisions and patterns for this component.
76
-
77
- ## [First Decision Topic]
78
- ...
64
+ ## [Topic]
65
+ **Decision:** [What was decided]
66
+ **Rationale:** [Why the non-obvious part]
79
67
  ```
80
68
  """
81
69
 
@@ -143,10 +131,8 @@ def main():
143
131
  elif user_prompt:
144
132
  # FIRST prompt - need context detection
145
133
  try:
146
- context_id, method, context_output, remaining_prompt = determine_context(user_prompt, project_root, session_id)
134
+ context_id, method, context_output = determine_context(user_prompt, project_root, session_id)
147
135
  eprint(f"[user_prompt_submit] Context: {method} -> {context_id}")
148
- if remaining_prompt:
149
- eprint(f"[user_prompt_submit] Actual request: {remaining_prompt[:50]}...")
150
136
 
151
137
  if context_id:
152
138
  # Bind session to context
@@ -15,7 +15,7 @@ from pathlib import Path
15
15
  # Directory names (relative to project root)
16
16
  OUTPUT_DIR = "_output"
17
17
  CONTEXTS_DIR = "contexts"
18
- ARCHIVE_DIR = "archive"
18
+ ARCHIVE_DIR = "_archive"
19
19
  INDEX_FILENAME = "index.json"
20
20
 
21
21
  # Context ID validation
@@ -255,6 +255,20 @@ def get_events_file_path(context_id: str, project_root: Path = None) -> Path:
255
255
  return get_context_dir(context_id, project_root) / "events.jsonl"
256
256
 
257
257
 
258
+ def get_auto_state_path(context_id: str, project_root: Path = None) -> Path:
259
+ """
260
+ Get the auto-state.json file path for a specific context.
261
+
262
+ Args:
263
+ context_id: Context identifier
264
+ project_root: Project root directory (default: cwd)
265
+
266
+ Returns:
267
+ Path to _output/contexts/{context_id}/auto-state.json
268
+ """
269
+ return get_context_dir(context_id, project_root) / "auto-state.json"
270
+
271
+
258
272
  def get_archive_dir(project_root: Path = None) -> Path:
259
273
  """
260
274
  Get the archive directory path.
@@ -263,7 +277,7 @@ def get_archive_dir(project_root: Path = None) -> Path:
263
277
  project_root: Project root directory (default: cwd)
264
278
 
265
279
  Returns:
266
- Path to _output/contexts/archive/
280
+ Path to _output/contexts/_archive/
267
281
  """
268
282
  return get_contexts_dir(project_root) / ARCHIVE_DIR
269
283
 
@@ -277,7 +291,7 @@ def get_archive_context_dir(context_id: str, project_root: Path = None) -> Path:
277
291
  project_root: Project root directory (default: cwd)
278
292
 
279
293
  Returns:
280
- Path to _output/contexts/archive/{context_id}/
294
+ Path to _output/contexts/_archive/{context_id}/
281
295
 
282
296
  Raises:
283
297
  ValueError: If context_id is invalid
@@ -294,7 +308,7 @@ def get_archive_index_path(project_root: Path = None) -> Path:
294
308
  project_root: Project root directory (default: cwd)
295
309
 
296
310
  Returns:
297
- Path to _output/contexts/archive/index.json
311
+ Path to _output/contexts/_archive/index.json
298
312
  """
299
313
  return get_archive_dir(project_root) / INDEX_FILENAME
300
314
 
@@ -19,30 +19,6 @@ from .utils import eprint
19
19
  F = TypeVar('F', bound=Callable[..., Any])
20
20
 
21
21
 
22
- def setup_hook_paths() -> Path:
23
- """
24
- Setup import paths for hooks.
25
-
26
- Call this at module level in hook scripts to ensure
27
- the shared lib directory is in sys.path.
28
-
29
- Returns:
30
- Path to the lib directory
31
-
32
- Example:
33
- SCRIPT_DIR = Path(__file__).resolve().parent
34
- SHARED_LIB = SCRIPT_DIR.parent / "lib"
35
- sys.path.insert(0, str(SHARED_LIB.parent))
36
- """
37
- # This function exists mainly for documentation
38
- # Actual path setup must happen at module level before imports
39
- hook_dir = Path(__file__).resolve().parent.parent.parent / "hooks"
40
- lib_dir = hook_dir.parent / "lib"
41
- if str(lib_dir.parent) not in sys.path:
42
- sys.path.insert(0, str(lib_dir.parent))
43
- return lib_dir
44
-
45
-
46
22
  def load_hook_input() -> Optional[Dict[str, Any]]:
47
23
  """
48
24
  Load and parse JSON from stdin.
@@ -187,4 +187,27 @@ STOP_WORDS = {
187
187
  # FRAGMENT WORDS (artifacts from contractions/tokenization)
188
188
  # ========================================================================
189
189
  're', 'pl', 'aiw', 've', 'll', 'doesn', 't', 's',
190
+
191
+ # ========================================================================
192
+ # CORPUS-DERIVED SHORT NOISE (2026-02 analysis of 131 docs)
193
+ # ========================================================================
194
+ # Contractions with punctuation stripped (I'm -> im, etc.)
195
+ 'im', 'ive', 'id', 'ill', 'youre', 'youve', 'youll',
196
+ 'hes', 'shes', 'weve', 'theyre', 'theyve', 'dont', 'doesnt',
197
+ 'didnt', 'wont', 'wouldnt', 'cant', 'couldnt', 'shouldnt', 'isnt',
198
+ 'arent', 'wasnt', 'werent', 'hasnt', 'havent', 'hadnt', 'lets',
199
+ 'thats', 'whats', 'heres', 'theres', 'whos',
200
+
201
+ # Filler/noise from corpus (>10% frequency, non-action)
202
+ 'etc', 'up', 'as', 'cc',
203
+
204
+ # Number words (common in plans but don't identify task)
205
+ 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten',
206
+
207
+ # Single letters (artifacts from lists, paths, variables)
208
+ 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
209
+ 'q', 'r', 'u', 'v', 'w', 'x', 'y', 'z',
210
+
211
+ # Short adverbs/fillers from review
212
+ 'too', 'yes', 'ok', 'okay',
190
213
  }
@@ -138,22 +138,27 @@ def generate_context_id(summary: str, existing_ids: Optional[set] = None) -> str
138
138
  """
139
139
  Generate a context ID from a summary string.
140
140
 
141
- Filters stop words from the summary and slugifies the result.
141
+ Prepends a YYMMDD-HHMM local-time timestamp for visual
142
+ distinguishability, then filters stop words and slugifies.
142
143
 
143
144
  Args:
144
145
  summary: Context summary text
145
146
  existing_ids: Optional set of existing context IDs to avoid
146
147
 
147
148
  Returns:
148
- Unique context ID string
149
+ Unique context ID string (e.g. '260205-1700-add-user-auth')
149
150
  """
151
+ # Timestamp prefix using local time, to the minute
152
+ timestamp = datetime.now().strftime("%y%m%d-%H%M")
153
+
150
154
  if not summary or not summary.strip():
151
- base_id = "context"
155
+ base_id = f"{timestamp}-context"
152
156
  else:
153
157
  # Use stop word filter, limit to 12 words
154
158
  from .stop_words import STOP_WORDS
155
159
  words = [w for w in summary.lower().split() if w not in STOP_WORDS and len(w) > 1][:12]
156
- base_id = sanitize_title(' '.join(words), max_len=100)
160
+ slug = sanitize_title(' '.join(words), max_len=50)
161
+ base_id = f"{timestamp}-{slug}"
157
162
 
158
163
  if not existing_ids:
159
164
  return base_id
@@ -50,10 +50,6 @@ from .task_sync import (
50
50
  )
51
51
  from .plan_archive import (
52
52
  archive_plan_to_context,
53
- get_active_context_for_plan,
54
- create_context_from_plan,
55
- mark_plan_implementation_started,
56
- mark_plan_completed,
57
53
  )
58
54
  from .context_extractor import (
59
55
  extract_context_id,
@@ -108,10 +104,6 @@ __all__ = [
108
104
  "generate_next_task_id",
109
105
  # Plan Archive
110
106
  "archive_plan_to_context",
111
- "get_active_context_for_plan",
112
- "create_context_from_plan",
113
- "mark_plan_implementation_started",
114
- "mark_plan_completed",
115
107
  # Context Extractor
116
108
  "extract_context_id",
117
109
  "extract_context_id_for_session",