aiwcli 0.9.0 → 0.9.2
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/README.md +19 -35
- package/dist/lib/template-installer.js +38 -0
- package/dist/templates/_shared/.claude/commands/handoff.md +219 -7
- package/dist/templates/_shared/.codex/workflows/handoff.md +219 -7
- package/dist/templates/_shared/.windsurf/workflows/handoff.md +219 -7
- package/dist/templates/_shared/hooks/__pycache__/context_enforcer.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__/user_prompt_submit.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/context_enforcer.py +73 -19
- package/dist/templates/_shared/hooks/context_monitor.py +28 -10
- package/dist/templates/_shared/hooks/file-suggestion.py +45 -15
- package/dist/templates/_shared/hooks/session_start.py +108 -0
- package/dist/templates/_shared/hooks/task_create_atomicity.py +199 -0
- package/dist/templates/_shared/hooks/task_create_capture.py +2 -2
- package/dist/templates/_shared/hooks/task_update_capture.py +2 -2
- package/dist/templates/_shared/hooks/user_prompt_submit.py +58 -23
- package/dist/templates/_shared/lib/base/__pycache__/inference.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__/utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/constants.py +45 -0
- package/dist/templates/_shared/lib/base/inference.py +29 -21
- package/dist/templates/_shared/lib/base/stop_words.py +158 -0
- package/dist/templates/_shared/lib/base/subprocess_utils.py +46 -0
- package/dist/templates/_shared/lib/base/utils.py +6 -3
- package/dist/templates/_shared/lib/context/__init__.py +0 -10
- package/dist/templates/_shared/lib/context/__pycache__/__init__.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__/task_sync.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/cache.py +2 -4
- package/dist/templates/_shared/lib/context/context_manager.py +2 -119
- package/dist/templates/_shared/lib/context/discovery.py +8 -50
- package/dist/templates/_shared/lib/context/task_sync.py +5 -82
- package/dist/templates/_shared/lib/handoff/document_generator.py +2 -5
- package/dist/templates/_shared/lib/templates/README.md +0 -1
- 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 -1
- package/dist/templates/_shared/lib/templates/persona_questions.py +113 -0
- package/dist/templates/_shared/lib/templates/plan_context.py +13 -27
- package/dist/templates/_shared/scripts/save_handoff.py +289 -43
- package/dist/templates/_shared/workflows/handoff.md +30 -16
- package/dist/templates/cc-native/.claude/agents/cc-native/ACCESSIBILITY-TESTER.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/ARCHITECT-REVIEWER.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/ASSUMPTION-CHAIN-TRACER.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/CLARITY-AUDITOR.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/CODE-REVIEWER.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/COMPLETENESS-CHECKER.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/CONTEXT-EXTRACTOR.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/DEVILS-ADVOCATE.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/FEASIBILITY-ANALYST.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/FRESH-PERSPECTIVE.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/HANDOFF-READINESS.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/HIDDEN-COMPLEXITY-DETECTOR.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/INCENTIVE-MAPPER.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/PENETRATION-TESTER.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/PERFORMANCE-ENGINEER.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/PLAN-ORCHESTRATOR.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/PRECEDENT-FINDER.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/REVERSIBILITY-ANALYST.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/RISK-ASSESSOR.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/SECOND-ORDER-ANALYST.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/SIMPLICITY-GUARDIAN.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/SKEPTIC.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/STAKEHOLDER-ADVOCATE.md +1 -1
- package/dist/templates/cc-native/.claude/agents/cc-native/TRADE-OFF-ILLUMINATOR.md +1 -1
- package/dist/templates/cc-native/.claude/settings.json +21 -0
- package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +211 -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/cc-native-plan-review.py +87 -27
- package/dist/templates/cc-native/_cc-native/lib/CLAUDE.md +240 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/orchestrator.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/orchestrator.py +10 -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 +10 -0
- package/dist/templates/cc-native/_cc-native/lib/utils.py +123 -10
- package/dist/templates/cc-native/_cc-native/plan-review.config.json +6 -0
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/dist/templates/cc-native/_cc-native/docs/PERMISSION_REQUEST_VERIFICATION.md +0 -147
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""Stop words for context ID generation.
|
|
2
|
+
|
|
3
|
+
Generated from analysis of 1,424 prompts, context summaries, and plan files.
|
|
4
|
+
Words that appear frequently but don't help identify the specific task.
|
|
5
|
+
|
|
6
|
+
Categories organized for maintainability. Add new corpus-derived words to
|
|
7
|
+
the appropriate category or to "Corpus-derived additions" at the end.
|
|
8
|
+
|
|
9
|
+
ACTION VERBS ARE INTENTIONALLY EXCLUDED from this list:
|
|
10
|
+
add, fix, update, create, implement, refactor, migrate, debug, remove, change,
|
|
11
|
+
move, rename, delete, build, test, deploy, verify, analyze, modify, write, run,
|
|
12
|
+
check, replace, save, sync, load, extract, install, refactor, clean, merge, etc.
|
|
13
|
+
|
|
14
|
+
These action verbs should REMAIN in context IDs as they identify the task type.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
STOP_WORDS = {
|
|
18
|
+
# ========================================================================
|
|
19
|
+
# ARTICLES
|
|
20
|
+
# ========================================================================
|
|
21
|
+
'a', 'an', 'the',
|
|
22
|
+
|
|
23
|
+
# ========================================================================
|
|
24
|
+
# PREPOSITIONS
|
|
25
|
+
# ========================================================================
|
|
26
|
+
'to', 'for', 'in', 'on', 'at', 'by', 'with', 'from', 'of', 'about',
|
|
27
|
+
'into', 'over', 'under', 'between', 'through', 'during', 'before', 'after',
|
|
28
|
+
'above', 'below', 'against', 'among', 'around', 'behind', 'beside', 'besides',
|
|
29
|
+
'beyond', 'down', 'inside', 'outside', 'near', 'off', 'onto', 'out',
|
|
30
|
+
'since', 'toward', 'towards', 'until', 'upon', 'within', 'without',
|
|
31
|
+
'across', 'along', 'via', 'per',
|
|
32
|
+
|
|
33
|
+
# ========================================================================
|
|
34
|
+
# PRONOUNS
|
|
35
|
+
# ========================================================================
|
|
36
|
+
# Personal pronouns
|
|
37
|
+
'i', 'you', 'he', 'she', 'it', 'we', 'they',
|
|
38
|
+
'me', 'him', 'her', 'us', 'them',
|
|
39
|
+
'myself', 'yourself', 'himself', 'herself', 'itself', 'ourselves', 'themselves',
|
|
40
|
+
|
|
41
|
+
# Possessive pronouns
|
|
42
|
+
'my', 'your', 'his', 'her', 'its', 'our', 'their',
|
|
43
|
+
'mine', 'yours', 'hers', 'ours', 'theirs',
|
|
44
|
+
|
|
45
|
+
# Demonstrative pronouns
|
|
46
|
+
'this', 'that', 'these', 'those',
|
|
47
|
+
|
|
48
|
+
# Relative pronouns
|
|
49
|
+
'who', 'whom', 'whose', 'which', 'that',
|
|
50
|
+
|
|
51
|
+
# Indefinite pronouns
|
|
52
|
+
'someone', 'somebody', 'something', 'anyone', 'anybody', 'anything',
|
|
53
|
+
'everyone', 'everybody', 'everything', 'no one', 'nobody', 'nothing',
|
|
54
|
+
'one', 'ones',
|
|
55
|
+
|
|
56
|
+
# ========================================================================
|
|
57
|
+
# AUXILIARY/MODAL VERBS
|
|
58
|
+
# ========================================================================
|
|
59
|
+
'am', 'is', 'are', 'was', 'were', 'be', 'been', 'being',
|
|
60
|
+
'have', 'has', 'had', 'having',
|
|
61
|
+
'do', 'does', 'did', 'doing', 'done',
|
|
62
|
+
'can', 'could', 'will', 'would', 'shall', 'should', 'may', 'might', 'must',
|
|
63
|
+
|
|
64
|
+
# ========================================================================
|
|
65
|
+
# CONJUNCTIONS
|
|
66
|
+
# ========================================================================
|
|
67
|
+
'and', 'or', 'but', 'nor', 'so', 'yet', 'for',
|
|
68
|
+
'if', 'then', 'else', 'whether', 'unless', 'although', 'though',
|
|
69
|
+
'because', 'since', 'while', 'whereas', 'whenever', 'wherever',
|
|
70
|
+
|
|
71
|
+
# ========================================================================
|
|
72
|
+
# QUESTION WORDS
|
|
73
|
+
# ========================================================================
|
|
74
|
+
'what', 'when', 'where', 'why', 'how',
|
|
75
|
+
|
|
76
|
+
# ========================================================================
|
|
77
|
+
# ADVERBS OF PLACE/TIME
|
|
78
|
+
# ========================================================================
|
|
79
|
+
'here', 'there', 'now', 'then', 'always', 'never', 'often', 'sometimes',
|
|
80
|
+
'already', 'still', 'yet', 'soon', 'later', 'ago', 'today', 'tomorrow',
|
|
81
|
+
'yesterday', 'currently', 'previously', 'recently', 'immediately',
|
|
82
|
+
'finally', 'eventually', 'meanwhile', 'afterwards',
|
|
83
|
+
|
|
84
|
+
# ========================================================================
|
|
85
|
+
# NEGATION
|
|
86
|
+
# ========================================================================
|
|
87
|
+
'no', 'not', 'none', 'never', 'neither', 'nor',
|
|
88
|
+
'don', 'doesn', 'didn', 'won', 'wouldn', 'couldn', 'shouldn',
|
|
89
|
+
'isn', 'aren', 'wasn', 'weren', 'hasn', 'haven', 'hadn',
|
|
90
|
+
|
|
91
|
+
# ========================================================================
|
|
92
|
+
# QUANTIFIERS
|
|
93
|
+
# ========================================================================
|
|
94
|
+
'some', 'any', 'all', 'each', 'every', 'both', 'few', 'more', 'most',
|
|
95
|
+
'many', 'much', 'several', 'other', 'another', 'enough', 'less', 'least',
|
|
96
|
+
'either', 'neither', 'such',
|
|
97
|
+
|
|
98
|
+
# ========================================================================
|
|
99
|
+
# FILLER/HEDGE WORDS
|
|
100
|
+
# ========================================================================
|
|
101
|
+
'just', 'also', 'only', 'really', 'actually', 'basically', 'simply',
|
|
102
|
+
'very', 'quite', 'rather', 'pretty', 'somewhat', 'almost', 'nearly',
|
|
103
|
+
'exactly', 'completely', 'entirely', 'totally', 'absolutely',
|
|
104
|
+
'probably', 'possibly', 'maybe', 'perhaps', 'definitely', 'certainly',
|
|
105
|
+
'apparently', 'obviously', 'clearly', 'literally', 'essentially',
|
|
106
|
+
|
|
107
|
+
# ========================================================================
|
|
108
|
+
# COMMON REQUEST PHRASES (from corpus: high frequency in prompts)
|
|
109
|
+
# ========================================================================
|
|
110
|
+
'want', 'need', 'help', 'please', 'like', 'let', 'get',
|
|
111
|
+
'think', 'know', 'see', 'try', 'make', 'give', 'take',
|
|
112
|
+
'look', 'looking', 'trying', 'going', 'getting', 'making',
|
|
113
|
+
|
|
114
|
+
# ========================================================================
|
|
115
|
+
# COMMON NON-ACTION VERBS
|
|
116
|
+
# ========================================================================
|
|
117
|
+
'go', 'come', 'put', 'say', 'tell', 'ask', 'find', 'keep',
|
|
118
|
+
'seem', 'appear', 'become', 'remain', 'stay', 'feel', 'show', 'mean',
|
|
119
|
+
'include', 'provide', 'require', 'allow', 'expect', 'cause',
|
|
120
|
+
'follow', 'consider', 'continue', 'start', 'begin', 'end',
|
|
121
|
+
'contain', 'contain', 'contains',
|
|
122
|
+
|
|
123
|
+
# ========================================================================
|
|
124
|
+
# LINKING/TRANSITION WORDS
|
|
125
|
+
# ========================================================================
|
|
126
|
+
'however', 'therefore', 'thus', 'hence', 'otherwise', 'instead',
|
|
127
|
+
'moreover', 'furthermore', 'nevertheless', 'nonetheless', 'accordingly',
|
|
128
|
+
'consequently', 'similarly', 'likewise', 'conversely', 'alternatively',
|
|
129
|
+
|
|
130
|
+
# ========================================================================
|
|
131
|
+
# CORPUS-DERIVED HIGH-FREQUENCY NOISE (from 1,424 docs analysis)
|
|
132
|
+
# ========================================================================
|
|
133
|
+
# Words appearing in >10% of documents that don't identify the task
|
|
134
|
+
|
|
135
|
+
# File extensions/technical noise (appear in paths, not task descriptors)
|
|
136
|
+
'py', 'md', 'ts', 'json', 'js', 'yaml', 'toml', 'exe', 'bat',
|
|
137
|
+
|
|
138
|
+
# Common Claude/coding terms that don't identify specific tasks
|
|
139
|
+
'using', 'used', 'uses',
|
|
140
|
+
'based', 'following',
|
|
141
|
+
'same', 'different', 'specific', 'existing', 'new', 'current', 'first',
|
|
142
|
+
'full', 'complete', 'single', 'multiple', 'simple',
|
|
143
|
+
'needed', 'required', 'provided', 'expected', 'correctly',
|
|
144
|
+
'works', 'working', 'work',
|
|
145
|
+
|
|
146
|
+
# Common structural words from plan files
|
|
147
|
+
'step', 'steps', 'phase', 'below', 'above',
|
|
148
|
+
|
|
149
|
+
# Query/clarification language (high frequency in prompts)
|
|
150
|
+
'questions', 'question', 'clarification',
|
|
151
|
+
|
|
152
|
+
# Overly generic terms
|
|
153
|
+
'thing', 'things', 'way', 'ways', 'kind', 'type', 'types',
|
|
154
|
+
'example', 'examples', 'case', 'cases',
|
|
155
|
+
'part', 'parts', 'point', 'points',
|
|
156
|
+
'time', 'times', 'next', 'last', 'end',
|
|
157
|
+
'set', 'list', 'group', 'item', 'items',
|
|
158
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Utilities for subprocess calls that invoke Claude Code CLI.
|
|
2
|
+
|
|
3
|
+
Provides centralized management of environment flags for internal subprocess calls
|
|
4
|
+
(orchestrator, agents, inference) to prevent recursion and unnecessary hook overhead.
|
|
5
|
+
"""
|
|
6
|
+
import os
|
|
7
|
+
from typing import Dict
|
|
8
|
+
|
|
9
|
+
# Environment variable names - single source of truth
|
|
10
|
+
ENV_INTERNAL_CALL = "AIWCLI_INTERNAL_CALL"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_internal_subprocess_env() -> Dict[str, str]:
|
|
14
|
+
"""Get environment dict for internal Claude Code subprocess calls.
|
|
15
|
+
|
|
16
|
+
This prevents internal subprocess calls (orchestrator, agents, inference)
|
|
17
|
+
from triggering hooks that would cause recursion or unnecessary overhead.
|
|
18
|
+
|
|
19
|
+
All hooks should check is_internal_call() and return early if True.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
Environment dict with AIWCLI_INTERNAL_CALL flag set
|
|
23
|
+
|
|
24
|
+
Example:
|
|
25
|
+
>>> env = get_internal_subprocess_env()
|
|
26
|
+
>>> subprocess.run(['claude', '--agent', 'my-agent'], env=env)
|
|
27
|
+
"""
|
|
28
|
+
env = os.environ.copy()
|
|
29
|
+
env[ENV_INTERNAL_CALL] = 'true'
|
|
30
|
+
return env
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def is_internal_call() -> bool:
|
|
34
|
+
"""Check if current process is an internal subprocess call.
|
|
35
|
+
|
|
36
|
+
Hooks should check this at the beginning and return early to avoid
|
|
37
|
+
recursion and unnecessary processing for internal subprocess calls.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
True if this is an internal call that should skip hooks
|
|
41
|
+
|
|
42
|
+
Example:
|
|
43
|
+
>>> if is_internal_call():
|
|
44
|
+
... return # Skip hook processing
|
|
45
|
+
"""
|
|
46
|
+
return os.environ.get(ENV_INTERNAL_CALL) == 'true'
|
|
@@ -157,15 +157,18 @@ def generate_context_id(summary: str, existing_ids: Optional[set] = None) -> str
|
|
|
157
157
|
from .inference import generate_semantic_summary
|
|
158
158
|
semantic = generate_semantic_summary(summary)
|
|
159
159
|
if semantic:
|
|
160
|
-
# Slugify the semantic summary
|
|
161
|
-
base_id = sanitize_title(semantic, max_len=
|
|
160
|
+
# Slugify the semantic summary (word limit already applied in inference)
|
|
161
|
+
base_id = sanitize_title(semantic, max_len=100)
|
|
162
162
|
eprint(f"[utils] Semantic context ID: {base_id}")
|
|
163
163
|
except Exception as e:
|
|
164
164
|
eprint(f"[utils] Inference failed, using fallback: {e}")
|
|
165
165
|
|
|
166
166
|
# Fallback to old method if inference failed
|
|
167
167
|
if not base_id:
|
|
168
|
-
|
|
168
|
+
# Fallback: use stop word filter, limit to 12 words
|
|
169
|
+
from .stop_words import STOP_WORDS
|
|
170
|
+
words = [w for w in summary.lower().split() if w not in STOP_WORDS and len(w) > 1][:12]
|
|
171
|
+
base_id = sanitize_title(' '.join(words), max_len=100)
|
|
169
172
|
|
|
170
173
|
if not existing_ids:
|
|
171
174
|
return base_id
|
|
@@ -12,9 +12,6 @@ from .context_manager import (
|
|
|
12
12
|
update_plan_status,
|
|
13
13
|
get_context_with_pending_plan,
|
|
14
14
|
get_context_with_in_flight_work,
|
|
15
|
-
update_handoff_status,
|
|
16
|
-
clear_handoff_status,
|
|
17
|
-
get_context_with_handoff_pending,
|
|
18
15
|
)
|
|
19
16
|
from .event_log import (
|
|
20
17
|
Task,
|
|
@@ -37,13 +34,11 @@ from .discovery import (
|
|
|
37
34
|
get_in_flight_context,
|
|
38
35
|
format_context_list,
|
|
39
36
|
format_pending_plan_continuation,
|
|
40
|
-
format_handoff_continuation,
|
|
41
37
|
format_implementation_continuation,
|
|
42
38
|
format_context_picker_prompt,
|
|
43
39
|
format_ready_for_new_work,
|
|
44
40
|
)
|
|
45
41
|
from .task_sync import (
|
|
46
|
-
generate_hydration_instructions,
|
|
47
42
|
generate_task_summary,
|
|
48
43
|
record_session_start,
|
|
49
44
|
record_task_created,
|
|
@@ -77,9 +72,6 @@ __all__ = [
|
|
|
77
72
|
"update_plan_status",
|
|
78
73
|
"get_context_with_pending_plan",
|
|
79
74
|
"get_context_with_in_flight_work",
|
|
80
|
-
"update_handoff_status",
|
|
81
|
-
"clear_handoff_status",
|
|
82
|
-
"get_context_with_handoff_pending",
|
|
83
75
|
# Event Log
|
|
84
76
|
"append_event",
|
|
85
77
|
"read_events",
|
|
@@ -97,12 +89,10 @@ __all__ = [
|
|
|
97
89
|
"get_in_flight_context",
|
|
98
90
|
"format_context_list",
|
|
99
91
|
"format_pending_plan_continuation",
|
|
100
|
-
"format_handoff_continuation",
|
|
101
92
|
"format_implementation_continuation",
|
|
102
93
|
"format_context_picker_prompt",
|
|
103
94
|
"format_ready_for_new_work",
|
|
104
95
|
# Task Sync
|
|
105
|
-
"generate_hydration_instructions",
|
|
106
96
|
"generate_task_summary",
|
|
107
97
|
"record_session_start",
|
|
108
98
|
"record_task_created",
|
|
Binary file
|
|
Binary file
|
|
@@ -115,13 +115,11 @@ def rebuild_context_from_events(context_dir: Path, project_root: Path = None) ->
|
|
|
115
115
|
context.in_flight.started_at = None
|
|
116
116
|
|
|
117
117
|
elif event_type == "handoff_created":
|
|
118
|
-
|
|
118
|
+
# Handoff events are informational only - no mode change
|
|
119
119
|
context.in_flight.handoff_path = event.get("path")
|
|
120
120
|
|
|
121
121
|
elif event_type == "handoff_cleared":
|
|
122
|
-
#
|
|
123
|
-
restored_mode = event.get("restored_mode", "none")
|
|
124
|
-
context.in_flight.mode = restored_mode
|
|
122
|
+
# Legacy event - just clear handoff_path, no mode change
|
|
125
123
|
context.in_flight.handoff_path = None
|
|
126
124
|
|
|
127
125
|
return context
|
|
@@ -933,132 +933,15 @@ def get_context_with_in_flight_work(project_root: Path = None) -> Optional[Conte
|
|
|
933
933
|
return None
|
|
934
934
|
|
|
935
935
|
|
|
936
|
-
def update_handoff_status(
|
|
937
|
-
context_id: str,
|
|
938
|
-
handoff_path: str,
|
|
939
|
-
project_root: Path = None
|
|
940
|
-
) -> Optional[Context]:
|
|
941
|
-
"""
|
|
942
|
-
Update context to indicate a handoff is pending.
|
|
943
|
-
|
|
944
|
-
Called by handoff document generator after creating handoff document.
|
|
945
|
-
Sets in_flight.mode = "handoff_pending" and in_flight.handoff_path.
|
|
946
|
-
|
|
947
|
-
Args:
|
|
948
|
-
context_id: Context identifier
|
|
949
|
-
handoff_path: Path to the handoff document
|
|
950
|
-
project_root: Project root directory
|
|
951
|
-
|
|
952
|
-
Returns:
|
|
953
|
-
Updated Context or None if not found
|
|
954
|
-
"""
|
|
955
|
-
context = get_context(context_id, project_root)
|
|
956
|
-
if not context:
|
|
957
|
-
return None
|
|
958
|
-
|
|
959
|
-
now = now_iso()
|
|
960
|
-
|
|
961
|
-
# Update in_flight state
|
|
962
|
-
context.in_flight.mode = "handoff_pending"
|
|
963
|
-
context.in_flight.handoff_path = handoff_path
|
|
964
|
-
context.in_flight.started_at = now
|
|
965
|
-
context.last_active = now
|
|
966
|
-
|
|
967
|
-
# Append event (source of truth) - MUST happen before cache updates
|
|
968
|
-
append_event(
|
|
969
|
-
context_id,
|
|
970
|
-
EVENT_HANDOFF_CREATED,
|
|
971
|
-
project_root,
|
|
972
|
-
path=handoff_path
|
|
973
|
-
)
|
|
974
|
-
|
|
975
|
-
# Update caches
|
|
976
|
-
_write_context_cache(context, project_root)
|
|
977
|
-
_update_index_cache(context, project_root)
|
|
978
|
-
|
|
979
|
-
eprint(f"[context_manager] Set handoff pending for: {context_id}")
|
|
980
|
-
return context
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
def clear_handoff_status(context_id: str, project_root: Path = None) -> Optional[Context]:
|
|
984
|
-
"""
|
|
985
|
-
Clear handoff pending status after resumption.
|
|
986
|
-
|
|
987
|
-
Called by SessionStart after successfully resuming from handoff.
|
|
988
|
-
|
|
989
|
-
Args:
|
|
990
|
-
context_id: Context identifier
|
|
991
|
-
project_root: Project root directory
|
|
992
|
-
|
|
993
|
-
Returns:
|
|
994
|
-
Updated Context or None if not found
|
|
995
|
-
"""
|
|
996
|
-
context = get_context(context_id, project_root)
|
|
997
|
-
if not context:
|
|
998
|
-
return None
|
|
999
|
-
|
|
1000
|
-
if context.in_flight.mode != "handoff_pending":
|
|
1001
|
-
return context # Nothing to clear
|
|
1002
|
-
|
|
1003
|
-
now = now_iso()
|
|
1004
|
-
|
|
1005
|
-
# Clear handoff state but preserve any artifact path (plan being implemented)
|
|
1006
|
-
# If artifact_path exists, restore to "implementing" mode; otherwise "none"
|
|
1007
|
-
if context.in_flight.artifact_path:
|
|
1008
|
-
context.in_flight.mode = "implementing"
|
|
1009
|
-
else:
|
|
1010
|
-
context.in_flight.mode = "none"
|
|
1011
|
-
context.in_flight.handoff_path = None
|
|
1012
|
-
# Don't clear started_at if we're still implementing
|
|
1013
|
-
if not context.in_flight.artifact_path:
|
|
1014
|
-
context.in_flight.started_at = None
|
|
1015
|
-
context.last_active = now
|
|
1016
|
-
|
|
1017
|
-
# Append event (source of truth) - MUST happen before cache updates
|
|
1018
|
-
append_event(
|
|
1019
|
-
context_id,
|
|
1020
|
-
EVENT_HANDOFF_CLEARED,
|
|
1021
|
-
project_root,
|
|
1022
|
-
restored_mode=context.in_flight.mode
|
|
1023
|
-
)
|
|
1024
|
-
|
|
1025
|
-
# Update caches
|
|
1026
|
-
_write_context_cache(context, project_root)
|
|
1027
|
-
_update_index_cache(context, project_root)
|
|
1028
|
-
|
|
1029
|
-
eprint(f"[context_manager] Cleared handoff status for: {context_id}")
|
|
1030
|
-
return context
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
def get_context_with_handoff_pending(project_root: Path = None) -> Optional[Context]:
|
|
1034
|
-
"""
|
|
1035
|
-
Find context with handoff pending (highest priority for SessionStart).
|
|
1036
|
-
|
|
1037
|
-
Args:
|
|
1038
|
-
project_root: Project root directory
|
|
1039
|
-
|
|
1040
|
-
Returns:
|
|
1041
|
-
Context with handoff pending, or None if not found
|
|
1042
|
-
"""
|
|
1043
|
-
contexts = get_all_contexts(status="active", project_root=project_root)
|
|
1044
|
-
|
|
1045
|
-
for context in contexts:
|
|
1046
|
-
if context.in_flight and context.in_flight.mode == "handoff_pending":
|
|
1047
|
-
return context
|
|
1048
|
-
|
|
1049
|
-
return None
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
936
|
def get_all_in_flight_contexts(project_root: Path = None) -> List[Context]:
|
|
1053
937
|
"""
|
|
1054
938
|
Return all contexts with truly in-flight work requiring attention.
|
|
1055
939
|
|
|
1056
940
|
In-flight modes (require continuation/action):
|
|
1057
|
-
- planning: Active planning session
|
|
1058
941
|
- pending_implementation: Plan created, awaiting implementation
|
|
1059
|
-
- handoff_pending: Handoff document created, awaiting pickup
|
|
1060
942
|
|
|
1061
943
|
NOT in-flight (normal working state):
|
|
944
|
+
- planning: Active planning session (doesn't auto-select context)
|
|
1062
945
|
- implementing: Active work, but doesn't block new context creation
|
|
1063
946
|
- none: No active work
|
|
1064
947
|
|
|
@@ -1073,7 +956,7 @@ def get_all_in_flight_contexts(project_root: Path = None) -> List[Context]:
|
|
|
1073
956
|
Returns:
|
|
1074
957
|
List of contexts with in-flight work requiring attention
|
|
1075
958
|
"""
|
|
1076
|
-
IN_FLIGHT_MODES = {"
|
|
959
|
+
IN_FLIGHT_MODES = {"pending_implementation"}
|
|
1077
960
|
contexts = get_all_contexts(status="active", project_root=project_root)
|
|
1078
961
|
return [c for c in contexts if c.in_flight and c.in_flight.mode in IN_FLIGHT_MODES]
|
|
1079
962
|
|
|
@@ -42,13 +42,12 @@ def discover_contexts_for_session(
|
|
|
42
42
|
|
|
43
43
|
def get_in_flight_context(project_root: Path = None) -> Optional[Context]:
|
|
44
44
|
"""
|
|
45
|
-
Get context with any in-flight work (plan,
|
|
45
|
+
Get context with any in-flight work (plan, etc.).
|
|
46
46
|
|
|
47
47
|
Priority order:
|
|
48
|
-
1.
|
|
49
|
-
2.
|
|
50
|
-
3.
|
|
51
|
-
4. planning - actively planning
|
|
48
|
+
1. pending_implementation - plan ready for implementation
|
|
49
|
+
2. implementing - implementation in progress
|
|
50
|
+
3. planning - actively planning
|
|
52
51
|
|
|
53
52
|
Args:
|
|
54
53
|
project_root: Project root directory
|
|
@@ -60,15 +59,14 @@ def get_in_flight_context(project_root: Path = None) -> Optional[Context]:
|
|
|
60
59
|
|
|
61
60
|
# Sort by in-flight priority
|
|
62
61
|
priority_order = {
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"planning": 3,
|
|
62
|
+
"pending_implementation": 0,
|
|
63
|
+
"implementing": 1,
|
|
64
|
+
"planning": 2,
|
|
67
65
|
"none": 99,
|
|
68
66
|
}
|
|
69
67
|
|
|
70
68
|
# Only auto-continue for high-priority modes (not "implementing", "planning" or "none")
|
|
71
|
-
actionable_modes = {"
|
|
69
|
+
actionable_modes = {"pending_implementation"}
|
|
72
70
|
|
|
73
71
|
in_flight_contexts = [
|
|
74
72
|
c for c in contexts
|
|
@@ -177,46 +175,6 @@ def format_pending_plan_continuation(context: Context) -> str:
|
|
|
177
175
|
return "\n".join(lines)
|
|
178
176
|
|
|
179
177
|
|
|
180
|
-
def format_handoff_continuation(context: Context) -> str:
|
|
181
|
-
"""
|
|
182
|
-
Format output for handoff continuation scenario.
|
|
183
|
-
|
|
184
|
-
This is shown when SessionStart detects a context with
|
|
185
|
-
in_flight.mode = "handoff_pending".
|
|
186
|
-
|
|
187
|
-
Args:
|
|
188
|
-
context: Context with handoff pending
|
|
189
|
-
|
|
190
|
-
Returns:
|
|
191
|
-
Formatted instructions for Claude
|
|
192
|
-
"""
|
|
193
|
-
lines = [
|
|
194
|
-
format_continuation_header("resuming", context.id),
|
|
195
|
-
"",
|
|
196
|
-
f"**Summary:** {context.summary}",
|
|
197
|
-
"",
|
|
198
|
-
]
|
|
199
|
-
|
|
200
|
-
# Add handoff document link
|
|
201
|
-
if context.in_flight and context.in_flight.handoff_path:
|
|
202
|
-
lines.append(f"**Handoff document:**")
|
|
203
|
-
lines.append(f"`{context.in_flight.handoff_path}`")
|
|
204
|
-
lines.append("")
|
|
205
|
-
|
|
206
|
-
lines.extend([
|
|
207
|
-
"---",
|
|
208
|
-
"",
|
|
209
|
-
"**Instructions:**",
|
|
210
|
-
"1. Read the handoff document above",
|
|
211
|
-
"2. Use TaskCreate to restore pending tasks",
|
|
212
|
-
"3. Continue where the previous session left off",
|
|
213
|
-
"",
|
|
214
|
-
"The context has been loaded. You may continue.",
|
|
215
|
-
])
|
|
216
|
-
|
|
217
|
-
return "\n".join(lines)
|
|
218
|
-
|
|
219
|
-
|
|
220
178
|
def format_implementation_continuation(context: Context) -> str:
|
|
221
179
|
"""
|
|
222
180
|
Format output for ongoing implementation scenario.
|
|
@@ -1,22 +1,17 @@
|
|
|
1
1
|
"""Task synchronization utilities for Claude native task integration.
|
|
2
2
|
|
|
3
|
-
Provides
|
|
3
|
+
Provides persistence for Claude Code native tasks:
|
|
4
4
|
- Claude Code native TaskCreate/TaskUpdate/TaskList tools (ephemeral)
|
|
5
5
|
- Persistent events.jsonl storage (source of truth)
|
|
6
6
|
|
|
7
|
-
SESSION START (Hydrate):
|
|
8
|
-
1. Read events.jsonl -> compute pending tasks
|
|
9
|
-
2. Output instructions for Claude to recreate tasks via TaskCreate
|
|
10
|
-
3. Claude's native TaskList now populated with persistent state
|
|
11
|
-
|
|
12
7
|
DURING SESSION (Persist):
|
|
13
8
|
1. Claude uses native TaskCreate/TaskUpdate
|
|
14
|
-
2.
|
|
15
|
-
3.
|
|
9
|
+
2. PostToolUse hooks capture events to events.jsonl
|
|
10
|
+
3. Task state preserved for future reference
|
|
16
11
|
|
|
17
12
|
SESSION END:
|
|
18
|
-
- events.jsonl
|
|
19
|
-
-
|
|
13
|
+
- events.jsonl has complete task history
|
|
14
|
+
- Can be queried for context summaries
|
|
20
15
|
"""
|
|
21
16
|
from pathlib import Path
|
|
22
17
|
from typing import List, Optional
|
|
@@ -35,78 +30,6 @@ from .event_log import (
|
|
|
35
30
|
from ..base.utils import eprint
|
|
36
31
|
|
|
37
32
|
|
|
38
|
-
def _escape_string(s: str) -> str:
|
|
39
|
-
"""
|
|
40
|
-
Escape a string for safe embedding in quoted YAML-like format.
|
|
41
|
-
|
|
42
|
-
Handles backslashes, newlines, tabs, and quotes.
|
|
43
|
-
|
|
44
|
-
Args:
|
|
45
|
-
s: Input string
|
|
46
|
-
|
|
47
|
-
Returns:
|
|
48
|
-
Escaped string safe for embedding in double quotes
|
|
49
|
-
"""
|
|
50
|
-
if not s:
|
|
51
|
-
return ""
|
|
52
|
-
# Order matters: escape backslashes first, then other special chars
|
|
53
|
-
s = s.replace('\\', '\\\\')
|
|
54
|
-
s = s.replace('\n', '\\n')
|
|
55
|
-
s = s.replace('\r', '\\r')
|
|
56
|
-
s = s.replace('\t', '\\t')
|
|
57
|
-
s = s.replace('"', '\\"')
|
|
58
|
-
return s
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def generate_hydration_instructions(
|
|
62
|
-
context_id: str,
|
|
63
|
-
project_root: Path = None
|
|
64
|
-
) -> str:
|
|
65
|
-
"""
|
|
66
|
-
Generate instructions for Claude to recreate tasks from persistent storage.
|
|
67
|
-
|
|
68
|
-
Called by SessionStart hook when resuming a context.
|
|
69
|
-
|
|
70
|
-
Args:
|
|
71
|
-
context_id: Context identifier
|
|
72
|
-
project_root: Project root directory
|
|
73
|
-
|
|
74
|
-
Returns:
|
|
75
|
-
Formatted instructions for Claude to restore tasks
|
|
76
|
-
"""
|
|
77
|
-
pending_tasks = get_pending_tasks(context_id, project_root)
|
|
78
|
-
|
|
79
|
-
if not pending_tasks:
|
|
80
|
-
return "No pending tasks to restore."
|
|
81
|
-
|
|
82
|
-
lines = [
|
|
83
|
-
"## Restoring Tasks from Previous Session",
|
|
84
|
-
"",
|
|
85
|
-
"Please recreate these tasks using TaskCreate:",
|
|
86
|
-
"",
|
|
87
|
-
]
|
|
88
|
-
|
|
89
|
-
for task in pending_tasks:
|
|
90
|
-
lines.append(f"### Task: {task.subject}")
|
|
91
|
-
lines.append("")
|
|
92
|
-
lines.append("```")
|
|
93
|
-
lines.append("TaskCreate:")
|
|
94
|
-
# Escape special characters for YAML-like format
|
|
95
|
-
subject_escaped = _escape_string(task.subject)
|
|
96
|
-
lines.append(f' subject: "{subject_escaped}"')
|
|
97
|
-
if task.description:
|
|
98
|
-
desc_escaped = _escape_string(task.description)
|
|
99
|
-
lines.append(f' description: "{desc_escaped}"')
|
|
100
|
-
if task.active_form:
|
|
101
|
-
active_form_escaped = _escape_string(task.active_form)
|
|
102
|
-
lines.append(f' activeForm: "{active_form_escaped}"')
|
|
103
|
-
lines.append(f' metadata: {{"persistent_id": "{task.id}", "context": "{context_id}", "skip_persistence": true}}')
|
|
104
|
-
lines.append("```")
|
|
105
|
-
lines.append("")
|
|
106
|
-
|
|
107
|
-
return "\n".join(lines)
|
|
108
|
-
|
|
109
|
-
|
|
110
33
|
def generate_task_summary(context_id: str, project_root: Path = None) -> str:
|
|
111
34
|
"""
|
|
112
35
|
Generate a summary of all tasks in a context.
|
|
@@ -83,7 +83,7 @@ def generate_handoff_document(
|
|
|
83
83
|
Returns:
|
|
84
84
|
HandoffDocument with file_path set, or None on failure
|
|
85
85
|
"""
|
|
86
|
-
from ..context.context_manager import get_context
|
|
86
|
+
from ..context.context_manager import get_context
|
|
87
87
|
|
|
88
88
|
context = get_context(context_id, project_root)
|
|
89
89
|
if not context:
|
|
@@ -141,7 +141,7 @@ def generate_handoff_document(
|
|
|
141
141
|
eprint(f"[handoff] ERROR: Failed to write handoff document: {error}")
|
|
142
142
|
return None
|
|
143
143
|
|
|
144
|
-
# Record event
|
|
144
|
+
# Record event (informational only - no mode change)
|
|
145
145
|
append_event(
|
|
146
146
|
context_id,
|
|
147
147
|
EVENT_HANDOFF_CREATED,
|
|
@@ -151,9 +151,6 @@ def generate_handoff_document(
|
|
|
151
151
|
session_id=session_id
|
|
152
152
|
)
|
|
153
153
|
|
|
154
|
-
# Update context in_flight state
|
|
155
|
-
update_handoff_status(context_id, str(file_path), project_root)
|
|
156
|
-
|
|
157
154
|
eprint(f"[handoff] Created handoff document: {file_path}")
|
|
158
155
|
return doc
|
|
159
156
|
|