aiwcli 0.9.2 → 0.9.4

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 (65) hide show
  1. package/dist/templates/_shared/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
  2. package/dist/templates/_shared/hooks/__pycache__/context_enforcer.cpython-313.pyc +0 -0
  3. package/dist/templates/_shared/hooks/__pycache__/context_monitor.cpython-313.pyc +0 -0
  4. package/dist/templates/_shared/hooks/__pycache__/file-suggestion.cpython-313.pyc +0 -0
  5. package/dist/templates/_shared/hooks/__pycache__/session_start.cpython-313.pyc +0 -0
  6. package/dist/templates/_shared/hooks/__pycache__/task_create_atomicity.cpython-313.pyc +0 -0
  7. package/dist/templates/_shared/hooks/__pycache__/task_create_capture.cpython-313.pyc +0 -0
  8. package/dist/templates/_shared/hooks/__pycache__/task_update_capture.cpython-313.pyc +0 -0
  9. package/dist/templates/_shared/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
  10. package/dist/templates/_shared/hooks/archive_plan.py +28 -38
  11. package/dist/templates/_shared/hooks/context_enforcer.py +6 -6
  12. package/dist/templates/_shared/hooks/context_monitor.py +4 -8
  13. package/dist/templates/_shared/hooks/file-suggestion.py +4 -10
  14. package/dist/templates/_shared/hooks/session_start.py +4 -9
  15. package/dist/templates/_shared/hooks/task_create_atomicity.py +90 -84
  16. package/dist/templates/_shared/hooks/task_create_capture.py +83 -146
  17. package/dist/templates/_shared/hooks/task_update_capture.py +116 -167
  18. package/dist/templates/_shared/hooks/user_prompt_submit.py +4 -9
  19. package/dist/templates/_shared/lib/__pycache__/__init__.cpython-313.pyc +0 -0
  20. package/dist/templates/_shared/lib/base/__pycache__/__init__.cpython-313.pyc +0 -0
  21. package/dist/templates/_shared/lib/base/__pycache__/atomic_write.cpython-313.pyc +0 -0
  22. package/dist/templates/_shared/lib/base/__pycache__/constants.cpython-313.pyc +0 -0
  23. package/dist/templates/_shared/lib/base/__pycache__/hook_utils.cpython-313.pyc +0 -0
  24. package/dist/templates/_shared/lib/base/__pycache__/utils.cpython-313.pyc +0 -0
  25. package/dist/templates/_shared/lib/base/hook_utils.py +169 -0
  26. package/dist/templates/_shared/lib/context/__init__.py +9 -0
  27. package/dist/templates/_shared/lib/context/__pycache__/__init__.cpython-313.pyc +0 -0
  28. package/dist/templates/_shared/lib/context/__pycache__/cache.cpython-313.pyc +0 -0
  29. package/dist/templates/_shared/lib/context/__pycache__/context_extractor.cpython-313.pyc +0 -0
  30. package/dist/templates/_shared/lib/context/__pycache__/context_manager.cpython-313.pyc +0 -0
  31. package/dist/templates/_shared/lib/context/__pycache__/discovery.cpython-313.pyc +0 -0
  32. package/dist/templates/_shared/lib/context/__pycache__/plan_archive.cpython-313.pyc +0 -0
  33. package/dist/templates/_shared/lib/context/context_extractor.py +115 -0
  34. package/dist/templates/_shared/lib/context/discovery.py +4 -4
  35. package/dist/templates/_shared/lib/templates/__pycache__/__init__.cpython-313.pyc +0 -0
  36. package/dist/templates/_shared/lib/templates/__pycache__/formatters.cpython-313.pyc +0 -0
  37. package/dist/templates/cc-native/.claude/agents/cc-native/ARCHITECT-REVIEWER.md +20 -47
  38. package/dist/templates/cc-native/.claude/agents/cc-native/ASSUMPTION-CHAIN-TRACER.md +25 -203
  39. package/dist/templates/cc-native/.claude/agents/cc-native/CLARITY-AUDITOR.md +24 -75
  40. package/dist/templates/cc-native/.claude/agents/cc-native/COMPLETENESS-CHECKER.md +31 -76
  41. package/dist/templates/cc-native/.claude/agents/cc-native/DEVILS-ADVOCATE.md +25 -188
  42. package/dist/templates/cc-native/.claude/agents/cc-native/DOCUMENTATION-REVIEWER.md +30 -52
  43. package/dist/templates/cc-native/.claude/agents/cc-native/FEASIBILITY-ANALYST.md +26 -62
  44. package/dist/templates/cc-native/.claude/agents/cc-native/FRESH-PERSPECTIVE.md +31 -80
  45. package/dist/templates/cc-native/.claude/agents/cc-native/HANDOFF-READINESS.md +24 -105
  46. package/dist/templates/cc-native/.claude/agents/cc-native/HIDDEN-COMPLEXITY-DETECTOR.md +23 -208
  47. package/dist/templates/cc-native/.claude/agents/cc-native/INCENTIVE-MAPPER.md +25 -199
  48. package/dist/templates/cc-native/.claude/agents/cc-native/PRECEDENT-FINDER.md +35 -205
  49. package/dist/templates/cc-native/.claude/agents/cc-native/REVERSIBILITY-ANALYST.md +26 -176
  50. package/dist/templates/cc-native/.claude/agents/cc-native/RISK-ASSESSOR.md +22 -65
  51. package/dist/templates/cc-native/.claude/agents/cc-native/SECOND-ORDER-ANALYST.md +25 -161
  52. package/dist/templates/cc-native/.claude/agents/cc-native/SIMPLICITY-GUARDIAN.md +28 -58
  53. package/dist/templates/cc-native/.claude/agents/cc-native/SKEPTIC.md +27 -311
  54. package/dist/templates/cc-native/.claude/agents/cc-native/STAKEHOLDER-ADVOCATE.md +22 -73
  55. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/add_plan_context.cpython-313.pyc +0 -0
  56. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-plan-review.cpython-313.pyc +0 -0
  57. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/suggest-fresh-perspective.cpython-313.pyc +0 -0
  58. package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +17 -3
  59. package/dist/templates/cc-native/_cc-native/lib/__pycache__/debug.cpython-313.pyc +0 -0
  60. package/dist/templates/cc-native/_cc-native/lib/debug.py +124 -0
  61. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/agent.cpython-313.pyc +0 -0
  62. package/dist/templates/cc-native/_cc-native/lib/reviewers/agent.py +33 -1
  63. package/dist/templates/cc-native/_cc-native/plan-review.config.json +1 -1
  64. package/oclif.manifest.json +1 -1
  65. package/package.json +1 -1
@@ -27,47 +27,31 @@ Usage in .claude/settings.json:
27
27
  }
28
28
  """
29
29
  import json
30
+ import re
30
31
  import sys
31
32
  from pathlib import Path
32
33
  from typing import Optional
33
34
 
34
35
  # Add parent directories to path for imports
35
- script_dir = Path(__file__).resolve().parent
36
- lib_dir = script_dir.parent / "lib"
37
- sys.path.insert(0, str(lib_dir.parent))
36
+ SCRIPT_DIR = Path(__file__).resolve().parent
37
+ SHARED_LIB = SCRIPT_DIR.parent / "lib"
38
+ sys.path.insert(0, str(SHARED_LIB.parent))
38
39
 
40
+ from lib.base.hook_utils import load_hook_input
39
41
  from lib.context.plan_archive import archive_plan_to_context
40
42
  from lib.context.context_manager import get_all_contexts
43
+ from lib.context.context_extractor import extract_context_id_for_session
41
44
  from lib.base.utils import eprint, project_dir
45
+ from lib.base.constants import get_context_dir
42
46
 
43
-
44
- def get_context_for_session(session_id: str, project_root: Path) -> Optional[str]:
45
- """
46
- Find context that matches this session_id.
47
-
48
- Args:
49
- session_id: Session ID to match
50
- project_root: Project root directory
51
-
52
- Returns:
53
- Context ID or None if not found
54
- """
55
- contexts = get_all_contexts(status="active", project_root=project_root)
56
-
57
- # Primary strategy: Find context with matching session_id
58
- for ctx in contexts:
59
- if ctx.in_flight and ctx.in_flight.session_ids and session_id in ctx.in_flight.session_ids:
60
- eprint(f"[archive_plan] Found context by session: {ctx.id}")
61
- return ctx.id
62
-
63
- # Fallback: If only one context is planning, assume it's the one
64
- planning_contexts = [c for c in contexts if c.in_flight and c.in_flight.mode == "planning"]
65
- if len(planning_contexts) == 1:
66
- eprint(f"[archive_plan] Fallback: Single planning context: {planning_contexts[0].id}")
67
- return planning_contexts[0].id
68
-
69
- eprint(f"[archive_plan] Could not find context for session {session_id}")
70
- return None
47
+ # Import debug cleanup function from cc-native lib
48
+ _cc_native_lib = SCRIPT_DIR.parent / "_cc-native" / "lib"
49
+ sys.path.insert(0, str(_cc_native_lib))
50
+ try:
51
+ from debug import cleanup_debug_folder
52
+ except ImportError:
53
+ def cleanup_debug_folder(context_path):
54
+ pass # Fallback if debug module not available
71
55
 
72
56
 
73
57
  def extract_plan_path_from_result(tool_result: str) -> Optional[str]:
@@ -76,7 +60,6 @@ def extract_plan_path_from_result(tool_result: str) -> Optional[str]:
76
60
 
77
61
  Looks for pattern: "Your plan has been saved to: <path>"
78
62
  """
79
- import re
80
63
  match = re.search(r'Your plan has been saved to:\s*(.+\.md)', tool_result)
81
64
  if match:
82
65
  return match.group(1).strip()
@@ -90,10 +73,9 @@ def on_plan_archive():
90
73
  Called from PostToolUse on ExitPlanMode - extracts plan path from result
91
74
  and archives to the active context.
92
75
  """
93
- # Read hook input from stdin
94
- try:
95
- hook_input = json.load(sys.stdin)
96
- except json.JSONDecodeError:
76
+ # Read hook input using shared utility
77
+ hook_input = load_hook_input()
78
+ if not hook_input:
97
79
  eprint("[archive_plan] No valid JSON input")
98
80
  return
99
81
 
@@ -220,9 +202,9 @@ def on_plan_archive():
220
202
  print(f"Plan archival skipped: file not found ({plan_path})")
221
203
  return
222
204
 
223
- # Find context by session ID
205
+ # Find context by session ID using shared extractor
224
206
  session_id = hook_input.get("session_id", "unknown")
225
- context_id = get_context_for_session(session_id, project_root)
207
+ context_id = extract_context_id_for_session(session_id, project_root, "archive_plan")
226
208
 
227
209
  if not context_id:
228
210
  eprint("[archive_plan] Could not determine context for session")
@@ -246,6 +228,14 @@ def on_plan_archive():
246
228
  )
247
229
 
248
230
  if archived_path:
231
+ # Clean up debug logs before completing archive
232
+ try:
233
+ context_path = get_context_dir(context_id, project_root)
234
+ cleanup_debug_folder(context_path)
235
+ print(f"[archive_plan] Cleaned up debug logs for context: {context_id}")
236
+ except Exception as e:
237
+ print(f"[archive_plan] Warning: could not clean debug folder: {e}")
238
+
249
239
  print(f"")
250
240
  print(f"[archive_plan] SUCCESS!")
251
241
  print(f"[archive_plan] Plan archived to context: {context_id}")
@@ -49,6 +49,7 @@ SCRIPT_DIR = Path(__file__).resolve().parent
49
49
  SHARED_LIB = SCRIPT_DIR.parent / "lib"
50
50
  sys.path.insert(0, str(SHARED_LIB.parent))
51
51
 
52
+ from lib.base.hook_utils import load_hook_input
52
53
  from lib.base.subprocess_utils import is_internal_call
53
54
  from lib.context.context_manager import (
54
55
  Context,
@@ -65,7 +66,7 @@ from lib.context.discovery import (
65
66
  format_context_created,
66
67
  format_pending_plan_continuation,
67
68
  format_implementation_continuation,
68
- _format_relative_time,
69
+ format_relative_time,
69
70
  )
70
71
  from lib.templates.formatters import get_mode_display
71
72
  from lib.base.utils import eprint, project_dir
@@ -251,7 +252,7 @@ def format_context_picker_stderr(contexts: List[Context]) -> str:
251
252
 
252
253
  implementing_count = 0
253
254
  for i, ctx in enumerate(contexts, 1):
254
- time_str = _format_relative_time(ctx.last_active)
255
+ time_str = format_relative_time(ctx.last_active)
255
256
 
256
257
  # Check if context is in implementing mode (selectable)
257
258
  is_implementing = ctx.in_flight and ctx.in_flight.mode == "implementing"
@@ -336,7 +337,7 @@ def format_command_feedback(
336
337
  if mode_str:
337
338
  mode_display = mode_str.strip("[]")
338
339
 
339
- time_str = _format_relative_time(selected_context.last_active)
340
+ time_str = format_relative_time(selected_context.last_active)
340
341
  lines.append(f"**Mode:** {mode_display}")
341
342
  lines.append(f"**Last Active:** {time_str}")
342
343
  lines.append("")
@@ -636,11 +637,10 @@ def main():
636
637
  In production, use user_prompt_submit.py as the unified entry point.
637
638
  """
638
639
  try:
639
- input_data = sys.stdin.read().strip()
640
- if not input_data:
640
+ hook_input = load_hook_input()
641
+ if not hook_input:
641
642
  return
642
643
 
643
- hook_input = json.loads(input_data)
644
644
  user_prompt = hook_input.get("prompt", "")
645
645
  if not user_prompt:
646
646
  return
@@ -55,6 +55,7 @@ SCRIPT_DIR = Path(__file__).resolve().parent
55
55
  SHARED_LIB = SCRIPT_DIR.parent / "lib"
56
56
  sys.path.insert(0, str(SHARED_LIB.parent))
57
57
 
58
+ from lib.base.hook_utils import load_hook_input
58
59
  from lib.base.utils import eprint, project_dir
59
60
  from lib.context.context_manager import (
60
61
  get_all_contexts,
@@ -301,15 +302,10 @@ def main():
301
302
  and prints system reminder if context is low.
302
303
  """
303
304
  try:
304
- # Read hook input from stdin
305
- input_data = sys.stdin.read().strip()
305
+ # Read hook input using shared utility
306
+ hook_input = load_hook_input()
306
307
 
307
- if not input_data:
308
- return
309
-
310
- try:
311
- hook_input = json.loads(input_data)
312
- except json.JSONDecodeError:
308
+ if not hook_input:
313
309
  return
314
310
 
315
311
  # Always check for mode transitions on implementation tools
@@ -28,6 +28,7 @@ SCRIPT_DIR = Path(__file__).resolve().parent
28
28
  SHARED_LIB = SCRIPT_DIR.parent / "lib"
29
29
  sys.path.insert(0, str(SHARED_LIB.parent))
30
30
 
31
+ from lib.base.hook_utils import load_hook_input
31
32
  from lib.base.utils import eprint, project_dir
32
33
  from lib.base.constants import (
33
34
  get_context_plans_dir,
@@ -167,17 +168,10 @@ def main():
167
168
  and outputs file suggestions as JSON array.
168
169
  """
169
170
  try:
170
- # Read hook input from stdin
171
- input_data = sys.stdin.read().strip()
171
+ # Read hook input using shared utility
172
+ hook_input = load_hook_input()
172
173
 
173
- if not input_data:
174
- print("[]")
175
- return
176
-
177
- try:
178
- hook_input = json.loads(input_data)
179
- except json.JSONDecodeError:
180
- eprint("[file-suggestion] Failed to parse input JSON")
174
+ if not hook_input:
181
175
  print("[]")
182
176
  return
183
177
 
@@ -24,7 +24,6 @@ Hook input:
24
24
  ...
25
25
  }
26
26
  """
27
- import json
28
27
  import sys
29
28
  from pathlib import Path
30
29
 
@@ -33,6 +32,7 @@ SCRIPT_DIR = Path(__file__).resolve().parent
33
32
  SHARED_LIB = SCRIPT_DIR.parent / "lib"
34
33
  sys.path.insert(0, str(SHARED_LIB.parent))
35
34
 
35
+ from lib.base.hook_utils import load_hook_input
36
36
  from lib.base.utils import eprint, project_dir
37
37
  from lib.context.context_manager import (
38
38
  get_all_in_flight_contexts,
@@ -49,15 +49,10 @@ def main():
49
49
  transition any pending_implementation context to implementing.
50
50
  """
51
51
  try:
52
- # Read hook input from stdin
53
- input_data = sys.stdin.read().strip()
52
+ # Read hook input using shared utility
53
+ hook_input = load_hook_input()
54
54
 
55
- if not input_data:
56
- return
57
-
58
- try:
59
- hook_input = json.loads(input_data)
60
- except json.JSONDecodeError:
55
+ if not hook_input:
61
56
  return
62
57
 
63
58
  source = hook_input.get("source", "unknown")
@@ -12,13 +12,20 @@ import sys
12
12
  from pathlib import Path
13
13
 
14
14
  # Path setup
15
- _hook_dir = Path(__file__).resolve().parent
16
- _shared_lib = _hook_dir.parent / "lib"
17
- sys.path.insert(0, str(_shared_lib))
18
-
19
- from base.utils import eprint
20
- from base.subprocess_utils import is_internal_call
21
- from base.inference import inference
15
+ SCRIPT_DIR = Path(__file__).resolve().parent
16
+ SHARED_LIB = SCRIPT_DIR.parent / "lib"
17
+ sys.path.insert(0, str(SHARED_LIB.parent))
18
+
19
+ from lib.base.hook_utils import (
20
+ load_hook_input,
21
+ validate_hook_event,
22
+ get_tool_input,
23
+ safe_hook_main,
24
+ run_hook,
25
+ )
26
+ from lib.base.utils import eprint
27
+ from lib.base.subprocess_utils import is_internal_call
28
+ from lib.base.inference import inference
22
29
 
23
30
  # Prompt engineered per Prompting/Standards.md:
24
31
  # - Markdown-only (no XML)
@@ -87,21 +94,25 @@ ASSESSMENT_USER_TEMPLATE = """Assess this task for atomicity and forkability:
87
94
  Evaluate whether a subagent with zero prior context could execute this task successfully."""
88
95
 
89
96
 
97
+ @safe_hook_main("task_create_atomicity")
90
98
  def main() -> int:
91
99
  # Skip internal calls (prevents recursion from orchestrator/inference)
92
100
  if is_internal_call():
93
101
  return 0
94
102
 
95
- try:
96
- payload = json.load(sys.stdin)
97
- except json.JSONDecodeError:
103
+ # Load and validate hook input
104
+ payload = load_hook_input()
105
+ if not payload:
98
106
  return 0
99
107
 
100
108
  # Only process TaskCreate
101
- if payload.get("tool_name") != "TaskCreate":
109
+ if not validate_hook_event(payload, "PreToolUse", "TaskCreate"):
110
+ return 0
111
+
112
+ tool_input = get_tool_input(payload)
113
+ if not tool_input:
102
114
  return 0
103
115
 
104
- tool_input = payload.get("tool_input", {})
105
116
  subject = tool_input.get("subject", "")
106
117
  description = tool_input.get("description", "")
107
118
 
@@ -110,66 +121,65 @@ def main() -> int:
110
121
  return 0
111
122
 
112
123
  # Call inference to assess atomicity and forkability
124
+ result = inference(
125
+ system_prompt=ASSESSMENT_SYSTEM_PROMPT,
126
+ user_prompt=ASSESSMENT_USER_TEMPLATE.format(
127
+ subject=subject,
128
+ description=description
129
+ ),
130
+ level="fast", # Use Haiku for minimal latency (~1-2s)
131
+ timeout=12, # Allow up to 12s for inference
132
+ )
133
+
134
+ if not result.success:
135
+ eprint(f"[task_create_atomicity] Inference failed: {result.error}")
136
+ return 0 # Non-blocking on failure
137
+
138
+ # Parse JSON response
113
139
  try:
114
- result = inference(
115
- system_prompt=ASSESSMENT_SYSTEM_PROMPT,
116
- user_prompt=ASSESSMENT_USER_TEMPLATE.format(
117
- subject=subject,
118
- description=description
119
- ),
120
- level="fast", # Use Haiku for minimal latency (~1-2s)
121
- timeout=12, # Allow up to 12s for inference
122
- )
123
-
124
- if not result.success:
125
- eprint(f"[task-create-atomicity] Inference failed: {result.error}")
126
- return 0 # Non-blocking on failure
127
-
128
- # Parse JSON response
129
- try:
130
- # Handle potential markdown code blocks in response
131
- output = result.output.strip()
132
- if output.startswith("```"):
133
- # Extract JSON from code block
134
- lines = output.split("\n")
135
- json_lines = []
136
- in_block = False
137
- for line in lines:
138
- if line.startswith("```") and not in_block:
139
- in_block = True
140
- continue
141
- elif line.startswith("```") and in_block:
142
- break
143
- elif in_block:
144
- json_lines.append(line)
145
- output = "\n".join(json_lines)
146
-
147
- assessment = json.loads(output)
148
- except json.JSONDecodeError:
149
- eprint(f"[task-create-atomicity] Failed to parse inference response: {result.output[:100]}")
150
- return 0
151
-
152
- # Extract assessment fields
153
- atomic = assessment.get("atomic", True)
154
- forkable = assessment.get("forkable", True)
155
- issues = assessment.get("issues", [])
156
- recommendation = assessment.get("recommendation", "")
157
-
158
- # Build context message based on assessment
159
- if atomic and forkable:
160
- # Task is good - minimal positive feedback
161
- context_msg = "Task Assessment: Well-specified and forkable."
162
- else:
163
- # Task has issues - inject detailed warning
164
- status_parts = []
165
- if not atomic:
166
- status_parts.append("NOT ATOMIC")
167
- if not forkable:
168
- status_parts.append("NOT FORKABLE")
169
-
170
- issues_text = "\n".join(f"- {issue}" for issue in issues) if issues else "- See recommendation below"
171
-
172
- context_msg = f"""**TASK ATOMICITY WARNING** ({', '.join(status_parts)})
140
+ # Handle potential markdown code blocks in response
141
+ output = result.output.strip()
142
+ if output.startswith("```"):
143
+ # Extract JSON from code block
144
+ lines = output.split("\n")
145
+ json_lines = []
146
+ in_block = False
147
+ for line in lines:
148
+ if line.startswith("```") and not in_block:
149
+ in_block = True
150
+ continue
151
+ elif line.startswith("```") and in_block:
152
+ break
153
+ elif in_block:
154
+ json_lines.append(line)
155
+ output = "\n".join(json_lines)
156
+
157
+ assessment = json.loads(output)
158
+ except json.JSONDecodeError:
159
+ eprint(f"[task_create_atomicity] Failed to parse inference response: {result.output[:100]}")
160
+ return 0
161
+
162
+ # Extract assessment fields
163
+ atomic = assessment.get("atomic", True)
164
+ forkable = assessment.get("forkable", True)
165
+ issues = assessment.get("issues", [])
166
+ recommendation = assessment.get("recommendation", "")
167
+
168
+ # Build context message based on assessment
169
+ if atomic and forkable:
170
+ # Task is good - minimal positive feedback
171
+ context_msg = "Task Assessment: Well-specified and forkable."
172
+ 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")
179
+
180
+ issues_text = "\n".join(f"- {issue}" for issue in issues) if issues else "- See recommendation below"
181
+
182
+ context_msg = f"""**TASK ATOMICITY WARNING** ({', '.join(status_parts)})
173
183
 
174
184
  This task may lack sufficient context for independent execution by a subagent.
175
185
 
@@ -180,20 +190,16 @@ This task may lack sufficient context for independent execution by a subagent.
180
190
 
181
191
  Consider adding specific file paths, function names, expected behaviors, or error details before creating this task."""
182
192
 
183
- # Output hook response with additionalContext
184
- out = {
185
- "hookSpecificOutput": {
186
- "hookEventName": "PreToolUse",
187
- "additionalContext": context_msg
188
- }
193
+ # Output hook response with additionalContext
194
+ out = {
195
+ "hookSpecificOutput": {
196
+ "hookEventName": "PreToolUse",
197
+ "additionalContext": context_msg
189
198
  }
190
- print(json.dumps(out, ensure_ascii=False))
191
- return 0
192
-
193
- except Exception as e:
194
- eprint(f"[task-create-atomicity] Error: {e}")
195
- return 0 # Non-blocking on error
199
+ }
200
+ print(json.dumps(out, ensure_ascii=False))
201
+ return 0
196
202
 
197
203
 
198
204
  if __name__ == "__main__":
199
- raise SystemExit(main())
205
+ run_hook(main)