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.
- 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__/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 +28 -38
- package/dist/templates/_shared/hooks/context_enforcer.py +6 -6
- package/dist/templates/_shared/hooks/context_monitor.py +4 -8
- package/dist/templates/_shared/hooks/file-suggestion.py +4 -10
- package/dist/templates/_shared/hooks/session_start.py +4 -9
- package/dist/templates/_shared/hooks/task_create_atomicity.py +90 -84
- package/dist/templates/_shared/hooks/task_create_capture.py +83 -146
- package/dist/templates/_shared/hooks/task_update_capture.py +116 -167
- package/dist/templates/_shared/hooks/user_prompt_submit.py +4 -9
- package/dist/templates/_shared/lib/__pycache__/__init__.cpython-313.pyc +0 -0
- 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__/utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/hook_utils.py +169 -0
- package/dist/templates/_shared/lib/context/__init__.py +9 -0
- 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_manager.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__/plan_archive.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/context_extractor.py +115 -0
- package/dist/templates/_shared/lib/context/discovery.py +4 -4
- 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/cc-native/.claude/agents/cc-native/ARCHITECT-REVIEWER.md +20 -47
- package/dist/templates/cc-native/.claude/agents/cc-native/ASSUMPTION-CHAIN-TRACER.md +25 -203
- package/dist/templates/cc-native/.claude/agents/cc-native/CLARITY-AUDITOR.md +24 -75
- package/dist/templates/cc-native/.claude/agents/cc-native/COMPLETENESS-CHECKER.md +31 -76
- package/dist/templates/cc-native/.claude/agents/cc-native/DEVILS-ADVOCATE.md +25 -188
- package/dist/templates/cc-native/.claude/agents/cc-native/DOCUMENTATION-REVIEWER.md +30 -52
- package/dist/templates/cc-native/.claude/agents/cc-native/FEASIBILITY-ANALYST.md +26 -62
- package/dist/templates/cc-native/.claude/agents/cc-native/FRESH-PERSPECTIVE.md +31 -80
- package/dist/templates/cc-native/.claude/agents/cc-native/HANDOFF-READINESS.md +24 -105
- package/dist/templates/cc-native/.claude/agents/cc-native/HIDDEN-COMPLEXITY-DETECTOR.md +23 -208
- package/dist/templates/cc-native/.claude/agents/cc-native/INCENTIVE-MAPPER.md +25 -199
- package/dist/templates/cc-native/.claude/agents/cc-native/PRECEDENT-FINDER.md +35 -205
- package/dist/templates/cc-native/.claude/agents/cc-native/REVERSIBILITY-ANALYST.md +26 -176
- package/dist/templates/cc-native/.claude/agents/cc-native/RISK-ASSESSOR.md +22 -65
- package/dist/templates/cc-native/.claude/agents/cc-native/SECOND-ORDER-ANALYST.md +25 -161
- package/dist/templates/cc-native/.claude/agents/cc-native/SIMPLICITY-GUARDIAN.md +28 -58
- package/dist/templates/cc-native/.claude/agents/cc-native/SKEPTIC.md +27 -311
- package/dist/templates/cc-native/.claude/agents/cc-native/STAKEHOLDER-ADVOCATE.md +22 -73
- 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__/suggest-fresh-perspective.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +17 -3
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/debug.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/debug.py +124 -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/agent.py +33 -1
- package/dist/templates/cc-native/_cc-native/plan-review.config.json +1 -1
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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
|
-
|
|
36
|
-
|
|
37
|
-
sys.path.insert(0, str(
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
|
94
|
-
|
|
95
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
640
|
-
if not
|
|
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
|
|
305
|
-
|
|
305
|
+
# Read hook input using shared utility
|
|
306
|
+
hook_input = load_hook_input()
|
|
306
307
|
|
|
307
|
-
if not
|
|
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
|
|
171
|
-
|
|
171
|
+
# Read hook input using shared utility
|
|
172
|
+
hook_input = load_hook_input()
|
|
172
173
|
|
|
173
|
-
if not
|
|
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
|
|
53
|
-
|
|
52
|
+
# Read hook input using shared utility
|
|
53
|
+
hook_input = load_hook_input()
|
|
54
54
|
|
|
55
|
-
if not
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
sys.path.insert(0, str(
|
|
18
|
-
|
|
19
|
-
from base.
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
193
|
+
# Output hook response with additionalContext
|
|
194
|
+
out = {
|
|
195
|
+
"hookSpecificOutput": {
|
|
196
|
+
"hookEventName": "PreToolUse",
|
|
197
|
+
"additionalContext": context_msg
|
|
189
198
|
}
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
|
|
205
|
+
run_hook(main)
|