@jaguilar87/gaia-ops 2.4.3 → 2.4.5
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/package.json +1 -1
- package/templates/CLAUDE.template.md +1 -14
- package/tools/5-task-management/__init__.py +0 -0
- package/tools/5-task-management/create_current_session_bundle.py +531 -0
- package/tools/5-task-management/restore_session.py +331 -0
- package/tools/5-task-management/session-manager.py +531 -0
- package/tools/5-task-management/task_manager.py +0 -0
package/package.json
CHANGED
|
@@ -35,20 +35,7 @@
|
|
|
35
35
|
| 5 | Realization | `Task` tool (re-invoke) | Yes |
|
|
36
36
|
| 6 | Update SSOT | Edit `project-context.json`, `tasks.md` | Yes |
|
|
37
37
|
|
|
38
|
-
### Rule 5.0.1 [P0]:
|
|
39
|
-
|
|
40
|
-
When receiving a user prompt, execute phases sequentially:
|
|
41
|
-
|
|
42
|
-
1. **Phase 0 (Conditional):** If prompt is ambiguous, call clarification module to enrich prompt
|
|
43
|
-
2. **Phase 1:** Call `agent_router.py` with enriched prompt → Receive agent suggestion
|
|
44
|
-
3. **Phase 2:** Call `context_provider.py` with selected agent → Receive provisioned context
|
|
45
|
-
4. **Phase 3:** Invoke `Task` tool with `subagent_type=<agent>`, `prompt=<enriched_prompt>`, and provisioned context
|
|
46
|
-
- **Checkpoint:** Agent must return a plan. If no plan received, halt workflow and report error
|
|
47
|
-
5. **Phase 4 (T3 only):** Run approval gate (MANDATORY for T3 operations)
|
|
48
|
-
6. **Phase 5:** After user approval, re-invoke `Task` tool for realization
|
|
49
|
-
7. **Phase 6:** Update `project-context.json` and `tasks.md` with results
|
|
50
|
-
|
|
51
|
-
### Rule 5.0.2 [P0]: Phase 0 Implementation
|
|
38
|
+
### Rule 5.0.1 [P0]: Phase 0 Implementation
|
|
52
39
|
|
|
53
40
|
**When to invoke Phase 0:**
|
|
54
41
|
- User prompt contains generic terms: "the service", "the API", "the cluster"
|
|
File without changes
|
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Create a new bundle for the current Claude session with complete context capture.
|
|
3
|
+
|
|
4
|
+
This script creates a fresh bundle directory with the current session's context,
|
|
5
|
+
capturing the complete conversation, git operations, modified files, and project state.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
python3 .claude/session/scripts/create_current_session_bundle.py [--no-git-ops] [--label LABEL]
|
|
9
|
+
|
|
10
|
+
Options:
|
|
11
|
+
--no-git-ops Skip capturing git operations
|
|
12
|
+
--label LABEL Add a custom label to the bundle name (e.g., agent-upgrades)
|
|
13
|
+
|
|
14
|
+
Examples:
|
|
15
|
+
python3 .claude/session/scripts/create_current_session_bundle.py
|
|
16
|
+
# Creates: 2025-10-16-session-163244-eca75cdd
|
|
17
|
+
|
|
18
|
+
python3 .claude/session/scripts/create_current_session_bundle.py --label agent-upgrades
|
|
19
|
+
# Creates: 2025-10-16-agent-upgrades-163244-eca75cdd
|
|
20
|
+
|
|
21
|
+
Features:
|
|
22
|
+
- Generates unique bundle ID with current timestamp
|
|
23
|
+
- Optional custom label for descriptive bundle names
|
|
24
|
+
- Captures complete session transcript (if available)
|
|
25
|
+
- Records git operations performed during session
|
|
26
|
+
- Copies modified files to artifacts
|
|
27
|
+
- Generates comprehensive metadata and summary
|
|
28
|
+
- Creates structured active context
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
import argparse
|
|
34
|
+
import json
|
|
35
|
+
import logging
|
|
36
|
+
import os
|
|
37
|
+
import shutil
|
|
38
|
+
import subprocess
|
|
39
|
+
import sys
|
|
40
|
+
from datetime import datetime
|
|
41
|
+
from pathlib import Path
|
|
42
|
+
from typing import Dict, List, Any, Optional
|
|
43
|
+
import hashlib
|
|
44
|
+
import time
|
|
45
|
+
|
|
46
|
+
# Configure logging
|
|
47
|
+
logging.basicConfig(
|
|
48
|
+
level=logging.INFO,
|
|
49
|
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
50
|
+
)
|
|
51
|
+
logger = logging.getLogger(__name__)
|
|
52
|
+
|
|
53
|
+
# Configurable artifact patterns (can be overridden by environment variable or config file)
|
|
54
|
+
DEFAULT_PRIORITY_PATTERNS = [
|
|
55
|
+
"change-log-devops.md",
|
|
56
|
+
"HEALTH-CHECK-CONFIG.md",
|
|
57
|
+
"health.controller.ts",
|
|
58
|
+
"health-server.ts",
|
|
59
|
+
"route.ts",
|
|
60
|
+
"validate-health-checks.js",
|
|
61
|
+
"tasks.md"
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def load_artifact_patterns() -> List[str]:
|
|
66
|
+
"""Load artifact patterns from environment or use defaults"""
|
|
67
|
+
env_patterns = os.environ.get('GAIA_ARTIFACT_PATTERNS', '')
|
|
68
|
+
if env_patterns:
|
|
69
|
+
logger.debug(f"Loaded artifact patterns from environment: {env_patterns}")
|
|
70
|
+
return env_patterns.split(',')
|
|
71
|
+
return DEFAULT_PRIORITY_PATTERNS
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# Detect SESSION_ROOT from current working directory instead of script location
|
|
75
|
+
def find_session_root() -> Path:
|
|
76
|
+
"""Find the .claude/session directory from current working directory"""
|
|
77
|
+
current = Path.cwd()
|
|
78
|
+
|
|
79
|
+
# First try: look for .claude/session in current directory or parents
|
|
80
|
+
search_path = current
|
|
81
|
+
for _ in range(5): # Search up to 5 levels up
|
|
82
|
+
candidate = search_path / ".claude" / "session"
|
|
83
|
+
if candidate.exists() and candidate.is_dir():
|
|
84
|
+
logger.debug(f"Found session root at: {candidate}")
|
|
85
|
+
return candidate
|
|
86
|
+
if search_path.parent == search_path:
|
|
87
|
+
break
|
|
88
|
+
search_path = search_path.parent
|
|
89
|
+
|
|
90
|
+
# Fallback: use script location (original behavior)
|
|
91
|
+
fallback = Path(__file__).resolve().parents[1]
|
|
92
|
+
logger.warning(f"Using fallback session root: {fallback}")
|
|
93
|
+
return fallback
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
SESSION_ROOT = find_session_root()
|
|
97
|
+
BUNDLES_DIR = SESSION_ROOT / "bundles"
|
|
98
|
+
ACTIVE_DIR = SESSION_ROOT / "active"
|
|
99
|
+
REPO_ROOT = SESSION_ROOT.parents[1]
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def generate_bundle_id(label: Optional[str] = None) -> str:
|
|
103
|
+
"""Generate a unique bundle ID with timestamp and hash
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
label: Optional custom label to include in bundle name (e.g., 'agent-upgrades')
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
Bundle ID in format: YYYY-MM-DD-{label|session}-HHMMSS-hash
|
|
110
|
+
|
|
111
|
+
Examples:
|
|
112
|
+
generate_bundle_id() -> "2025-10-16-session-163244-eca75cdd"
|
|
113
|
+
generate_bundle_id("agent-upgrades") -> "2025-10-16-agent-upgrades-163244-eca75cdd"
|
|
114
|
+
"""
|
|
115
|
+
now = datetime.now()
|
|
116
|
+
date_part = now.strftime("%Y-%m-%d")
|
|
117
|
+
time_part = now.strftime("%H%M%S")
|
|
118
|
+
|
|
119
|
+
# Use custom label or default "session"
|
|
120
|
+
label_part = label if label else "session"
|
|
121
|
+
|
|
122
|
+
# Create a hash based on timestamp + pid for uniqueness
|
|
123
|
+
hash_input = f"{now.isoformat()}-{os.getpid()}"
|
|
124
|
+
short_hash = hashlib.md5(hash_input.encode()).hexdigest()[:8]
|
|
125
|
+
|
|
126
|
+
bundle_id = f"{date_part}-{label_part}-{time_part}-{short_hash}"
|
|
127
|
+
logger.debug(f"Generated bundle ID: {bundle_id}")
|
|
128
|
+
return bundle_id
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def get_git_operations() -> List[Dict[str, Any]]:
|
|
132
|
+
"""Capture recent git operations from the current session"""
|
|
133
|
+
operations = []
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
# Get recent commits (last 5)
|
|
137
|
+
result = subprocess.run(
|
|
138
|
+
["git", "log", "--oneline", "-5", "--pretty=format:%H|%s|%ai"],
|
|
139
|
+
cwd=REPO_ROOT,
|
|
140
|
+
capture_output=True,
|
|
141
|
+
text=True,
|
|
142
|
+
check=True,
|
|
143
|
+
timeout=10
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
for line in result.stdout.strip().split('\n'):
|
|
147
|
+
if line:
|
|
148
|
+
parts = line.split('|')
|
|
149
|
+
if len(parts) >= 3:
|
|
150
|
+
operations.append({
|
|
151
|
+
"type": "commit",
|
|
152
|
+
"hash": parts[0],
|
|
153
|
+
"message": parts[1],
|
|
154
|
+
"timestamp": parts[2]
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
except subprocess.CalledProcessError as e:
|
|
158
|
+
logger.warning(f"Failed to get git commits: {e}")
|
|
159
|
+
except subprocess.TimeoutExpired:
|
|
160
|
+
logger.warning("Git commit log command timed out")
|
|
161
|
+
|
|
162
|
+
try:
|
|
163
|
+
# Get current git status
|
|
164
|
+
result = subprocess.run(
|
|
165
|
+
["git", "status", "--porcelain"],
|
|
166
|
+
cwd=REPO_ROOT,
|
|
167
|
+
capture_output=True,
|
|
168
|
+
text=True,
|
|
169
|
+
check=True,
|
|
170
|
+
timeout=10
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
if result.stdout.strip():
|
|
174
|
+
operations.append({
|
|
175
|
+
"type": "status",
|
|
176
|
+
"modified_files": result.stdout.strip().split('\n'),
|
|
177
|
+
"timestamp": datetime.now().isoformat()
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
except subprocess.CalledProcessError as e:
|
|
181
|
+
logger.warning(f"Failed to get git status: {e}")
|
|
182
|
+
except subprocess.TimeoutExpired:
|
|
183
|
+
logger.warning("Git status command timed out")
|
|
184
|
+
|
|
185
|
+
logger.info(f"Captured {len(operations)} git operations")
|
|
186
|
+
return operations
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def get_modified_files() -> List[str]:
|
|
190
|
+
"""Get list of files that were likely modified during this session"""
|
|
191
|
+
modified_files = []
|
|
192
|
+
|
|
193
|
+
try:
|
|
194
|
+
# Get files modified in the last 2 hours (rough session duration)
|
|
195
|
+
result = subprocess.run([
|
|
196
|
+
"find", str(REPO_ROOT),
|
|
197
|
+
"-type", "f",
|
|
198
|
+
"-mmin", "-120", # Modified in last 2 hours
|
|
199
|
+
"-not", "-path", "*/.*/*", # Exclude hidden directories
|
|
200
|
+
"-not", "-path", "*/node_modules/*", # Exclude node_modules
|
|
201
|
+
"-not", "-path", "*/postgres-data/*" # Exclude postgres data
|
|
202
|
+
], capture_output=True, text=True, check=True, timeout=15)
|
|
203
|
+
|
|
204
|
+
for file_path in result.stdout.strip().split('\n'):
|
|
205
|
+
if file_path and Path(file_path).is_file():
|
|
206
|
+
rel_path = str(Path(file_path).relative_to(REPO_ROOT))
|
|
207
|
+
modified_files.append(rel_path)
|
|
208
|
+
|
|
209
|
+
except subprocess.CalledProcessError as e:
|
|
210
|
+
logger.warning(f"Failed to find modified files: {e}")
|
|
211
|
+
except subprocess.TimeoutExpired:
|
|
212
|
+
logger.warning("Find command timed out")
|
|
213
|
+
except ValueError as e:
|
|
214
|
+
logger.warning(f"Failed to compute relative path: {e}")
|
|
215
|
+
|
|
216
|
+
logger.info(f"Found {len(modified_files)} modified files")
|
|
217
|
+
return modified_files
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def create_bundle_structure(bundle_id: str) -> Path:
|
|
221
|
+
"""Create the bundle directory structure"""
|
|
222
|
+
bundle_path = BUNDLES_DIR / bundle_id
|
|
223
|
+
bundle_path.mkdir(parents=True, exist_ok=True)
|
|
224
|
+
|
|
225
|
+
# Create subdirectories
|
|
226
|
+
(bundle_path / "artifacts").mkdir(exist_ok=True)
|
|
227
|
+
(bundle_path / "active-context").mkdir(exist_ok=True)
|
|
228
|
+
|
|
229
|
+
logger.info(f"Created bundle structure at: {bundle_path}")
|
|
230
|
+
return bundle_path
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def generate_metadata(bundle_id: str, git_ops: List[Dict], modified_files: List[str]) -> Dict[str, Any]:
|
|
234
|
+
"""Generate comprehensive metadata for the session"""
|
|
235
|
+
return {
|
|
236
|
+
"session_id": f"current-session-{int(time.time())}",
|
|
237
|
+
"bundle_id": bundle_id,
|
|
238
|
+
"timestamp": datetime.now().isoformat(),
|
|
239
|
+
"task_info": {
|
|
240
|
+
"description": "Health check system implementation and repository updates",
|
|
241
|
+
"context": "Implemented comprehensive health checks across TCM services, updated changelog, and pushed to repositories",
|
|
242
|
+
"working_dir": str(REPO_ROOT),
|
|
243
|
+
"project_context": "Multi-project repository with TCM application and spec-kit"
|
|
244
|
+
},
|
|
245
|
+
"git_operations": git_ops,
|
|
246
|
+
"modified_files": modified_files,
|
|
247
|
+
"artifacts_count": len(modified_files),
|
|
248
|
+
"bundle_version": "2.0"
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def generate_summary(metadata: Dict[str, Any]) -> str:
|
|
253
|
+
"""Generate a human-readable summary of the session
|
|
254
|
+
|
|
255
|
+
This generates a generic template summary. For specific session details,
|
|
256
|
+
manually update the summary.md file in the bundle after creation.
|
|
257
|
+
"""
|
|
258
|
+
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
259
|
+
bundle_id = metadata.get('bundle_id', 'unknown')
|
|
260
|
+
|
|
261
|
+
# Extract label from bundle_id if present
|
|
262
|
+
label = None
|
|
263
|
+
if bundle_id != 'unknown':
|
|
264
|
+
parts = bundle_id.split('-')
|
|
265
|
+
if len(parts) >= 4:
|
|
266
|
+
# Format: YYYY-MM-DD-{label}-HHMMSS-hash
|
|
267
|
+
label = parts[3]
|
|
268
|
+
|
|
269
|
+
# Infer session type from label or modified files
|
|
270
|
+
session_type = "Development Session"
|
|
271
|
+
if label and label != "session":
|
|
272
|
+
session_type = label.replace('-', ' ').title()
|
|
273
|
+
|
|
274
|
+
summary = f"""# Session Summary - {timestamp}
|
|
275
|
+
|
|
276
|
+
## Bundle ID
|
|
277
|
+
{bundle_id}
|
|
278
|
+
|
|
279
|
+
## Overview
|
|
280
|
+
This session involved work on the Claude Code agent system and related infrastructure.
|
|
281
|
+
|
|
282
|
+
**Note:** This is an auto-generated summary template. For detailed session information,
|
|
283
|
+
please update this file manually or review the transcript.md and metadata.json files.
|
|
284
|
+
|
|
285
|
+
## Session Type
|
|
286
|
+
{session_type}
|
|
287
|
+
|
|
288
|
+
## Technical Changes
|
|
289
|
+
|
|
290
|
+
"""
|
|
291
|
+
|
|
292
|
+
# Git operations
|
|
293
|
+
if metadata.get("git_operations"):
|
|
294
|
+
summary += "### Git Operations Captured\n"
|
|
295
|
+
commit_count = sum(1 for op in metadata["git_operations"] if op.get("type") == "commit")
|
|
296
|
+
summary += f"- {len(metadata['git_operations'])} total operations\n"
|
|
297
|
+
summary += f"- {commit_count} commits\n\n"
|
|
298
|
+
|
|
299
|
+
summary += "**Recent commits:**\n"
|
|
300
|
+
for op in metadata["git_operations"]:
|
|
301
|
+
if op.get("type") == "commit":
|
|
302
|
+
summary += f"- `{op.get('hash', 'N/A')[:8]}` - {op.get('message', 'No message')}\n"
|
|
303
|
+
else:
|
|
304
|
+
summary += "### Git Operations\n"
|
|
305
|
+
summary += "No git operations captured in this session.\n\n"
|
|
306
|
+
|
|
307
|
+
# Modified files
|
|
308
|
+
if metadata.get("modified_files"):
|
|
309
|
+
summary += f"\n### Files Modified\n"
|
|
310
|
+
summary += f"Total: {len(metadata['modified_files'])} files\n\n"
|
|
311
|
+
|
|
312
|
+
# Group by extension
|
|
313
|
+
by_extension = {}
|
|
314
|
+
for file_path in metadata["modified_files"]:
|
|
315
|
+
ext = Path(file_path).suffix or "no-extension"
|
|
316
|
+
by_extension.setdefault(ext, []).append(file_path)
|
|
317
|
+
|
|
318
|
+
summary += "**By file type:**\n"
|
|
319
|
+
for ext, files in sorted(by_extension.items()):
|
|
320
|
+
summary += f"- {ext}: {len(files)} files\n"
|
|
321
|
+
|
|
322
|
+
summary += "\n**Key files:**\n"
|
|
323
|
+
# Show first 15 files
|
|
324
|
+
for file_path in list(metadata["modified_files"])[:15]:
|
|
325
|
+
summary += f"- {file_path}\n"
|
|
326
|
+
|
|
327
|
+
if len(metadata["modified_files"]) > 15:
|
|
328
|
+
summary += f"\n... and {len(metadata['modified_files']) - 15} more files\n"
|
|
329
|
+
else:
|
|
330
|
+
summary += "\n### Files Modified\n"
|
|
331
|
+
summary += "No modified files tracked in this session.\n"
|
|
332
|
+
|
|
333
|
+
summary += f"""
|
|
334
|
+
## Session Context
|
|
335
|
+
- **Working Directory**: {metadata['task_info']['working_dir']}
|
|
336
|
+
- **Bundle ID**: {metadata['bundle_id']}
|
|
337
|
+
- **Created**: {timestamp}
|
|
338
|
+
- **Label**: {label if label else 'general-session'}
|
|
339
|
+
|
|
340
|
+
## Artifacts
|
|
341
|
+
- `transcript.md` - Session conversation (if captured)
|
|
342
|
+
- `metadata.json` - Complete session metadata
|
|
343
|
+
- `artifacts/` - Modified files and artifacts
|
|
344
|
+
- `active-context/` - Session state for restoration
|
|
345
|
+
|
|
346
|
+
## How to Use This Bundle
|
|
347
|
+
|
|
348
|
+
### View Session Details
|
|
349
|
+
```bash
|
|
350
|
+
# Read this summary
|
|
351
|
+
cat summary.md
|
|
352
|
+
|
|
353
|
+
# View session metadata
|
|
354
|
+
cat metadata.json | jq
|
|
355
|
+
|
|
356
|
+
# Check transcript (if available)
|
|
357
|
+
cat transcript.md
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Restore Session
|
|
361
|
+
```bash
|
|
362
|
+
python3 .claude/session/scripts/restore_session.py {bundle_id}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
## Next Steps
|
|
366
|
+
**IMPORTANT:** Please update this summary with:
|
|
367
|
+
1. Actual key accomplishments from the session
|
|
368
|
+
2. Specific technical changes made
|
|
369
|
+
3. Relevant next steps or follow-up actions
|
|
370
|
+
4. Any important decisions or findings
|
|
371
|
+
|
|
372
|
+
This will help when reviewing or restoring this session in the future.
|
|
373
|
+
"""
|
|
374
|
+
|
|
375
|
+
return summary
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def copy_artifacts(bundle_path: Path, modified_files: List[str]) -> None:
|
|
379
|
+
"""Copy important modified files to the artifacts directory"""
|
|
380
|
+
artifacts_dir = bundle_path / "artifacts"
|
|
381
|
+
|
|
382
|
+
# Load configurable patterns
|
|
383
|
+
priority_patterns = load_artifact_patterns()
|
|
384
|
+
|
|
385
|
+
copied_count = 0
|
|
386
|
+
for file_path in modified_files:
|
|
387
|
+
if any(pattern in file_path for pattern in priority_patterns):
|
|
388
|
+
source = REPO_ROOT / file_path
|
|
389
|
+
if source.exists() and source.is_file():
|
|
390
|
+
# Create directory structure in artifacts
|
|
391
|
+
dest = artifacts_dir / file_path
|
|
392
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
393
|
+
|
|
394
|
+
try:
|
|
395
|
+
shutil.copy2(source, dest)
|
|
396
|
+
copied_count += 1
|
|
397
|
+
except (OSError, shutil.Error) as e:
|
|
398
|
+
logger.warning(f"Failed to copy {file_path}: {e}")
|
|
399
|
+
|
|
400
|
+
logger.info(f"Copied {copied_count} key files to artifacts/")
|
|
401
|
+
print(f" Copied {copied_count} key files to artifacts/")
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def copy_active_context(bundle_path: Path) -> None:
|
|
405
|
+
"""Copy the active session context"""
|
|
406
|
+
if ACTIVE_DIR.exists():
|
|
407
|
+
target = bundle_path / "active-context"
|
|
408
|
+
if target.exists():
|
|
409
|
+
shutil.rmtree(target)
|
|
410
|
+
try:
|
|
411
|
+
shutil.copytree(ACTIVE_DIR, target)
|
|
412
|
+
logger.info("Active context copied to active-context/")
|
|
413
|
+
print(" Active context copied to active-context/")
|
|
414
|
+
except (OSError, shutil.Error) as e:
|
|
415
|
+
logger.warning(f"Failed to copy active context: {e}")
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def create_transcript(bundle_path: Path) -> None:
|
|
419
|
+
"""Create a basic transcript placeholder (Claude doesn't provide direct access)"""
|
|
420
|
+
transcript_content = f"""# Session Transcript - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
|
421
|
+
|
|
422
|
+
## Session Overview
|
|
423
|
+
This session focused on implementing health check improvements across the TCM application services.
|
|
424
|
+
|
|
425
|
+
## Key Activities
|
|
426
|
+
1. **Health Check Implementation**
|
|
427
|
+
- Enhanced health check endpoints in API service (apps/api/src/health.controller.ts)
|
|
428
|
+
- Added comprehensive health checks to Bot service (apps/bot/src/index.ts)
|
|
429
|
+
- Implemented readiness probes in Jobs service (apps/jobs/src/health-server.ts)
|
|
430
|
+
- Created detailed health endpoint for Web service (apps/web/app/api/health/)
|
|
431
|
+
|
|
432
|
+
2. **Documentation Updates**
|
|
433
|
+
- Updated DevOps changelog (change-log-devops.md) with health check implementation details
|
|
434
|
+
- Created comprehensive health check configuration guide (HEALTH-CHECK-CONFIG.md)
|
|
435
|
+
|
|
436
|
+
3. **Repository Management**
|
|
437
|
+
- Successfully committed changes to training-compliance-management repository
|
|
438
|
+
- Updated spec-kit-tcm-plan repository with task definitions
|
|
439
|
+
- Resolved git submodule status issues
|
|
440
|
+
|
|
441
|
+
## Session Commands Executed
|
|
442
|
+
- Git status checks across multiple repositories
|
|
443
|
+
- File modifications using Edit tool
|
|
444
|
+
- Git commits with comprehensive commit messages
|
|
445
|
+
- Git push operations to remote repositories
|
|
446
|
+
- Bundle creation and session saving
|
|
447
|
+
|
|
448
|
+
## Files Modified
|
|
449
|
+
See artifacts/ directory for copies of key modified files.
|
|
450
|
+
|
|
451
|
+
---
|
|
452
|
+
*Note: This transcript is auto-generated. The actual conversation history is preserved in Claude's session context.*
|
|
453
|
+
"""
|
|
454
|
+
|
|
455
|
+
try:
|
|
456
|
+
(bundle_path / "transcript.md").write_text(transcript_content)
|
|
457
|
+
logger.info("Generated session transcript")
|
|
458
|
+
print(" Generated session transcript")
|
|
459
|
+
except (OSError, IOError) as e:
|
|
460
|
+
logger.error(f"Failed to create transcript: {e}")
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
def main() -> None:
|
|
464
|
+
parser = argparse.ArgumentParser(
|
|
465
|
+
description="Create new bundle for current Claude session",
|
|
466
|
+
epilog="Examples:\n"
|
|
467
|
+
" %(prog)s\n"
|
|
468
|
+
" %(prog)s --label agent-upgrades\n"
|
|
469
|
+
" %(prog)s --label infrastructure-setup --no-git-ops",
|
|
470
|
+
formatter_class=argparse.RawDescriptionHelpFormatter
|
|
471
|
+
)
|
|
472
|
+
parser.add_argument("--no-git-ops", action="store_true", help="Skip git operations capture")
|
|
473
|
+
parser.add_argument("--label", type=str, help="Custom label for bundle name (e.g., agent-upgrades, feature-xyz)")
|
|
474
|
+
parser.add_argument("--verbose", action="store_true", help="Enable verbose logging")
|
|
475
|
+
args = parser.parse_args()
|
|
476
|
+
|
|
477
|
+
if args.verbose:
|
|
478
|
+
logger.setLevel(logging.DEBUG)
|
|
479
|
+
|
|
480
|
+
logger.info("Starting session bundle creation")
|
|
481
|
+
|
|
482
|
+
# Ensure bundles directory exists
|
|
483
|
+
BUNDLES_DIR.mkdir(parents=True, exist_ok=True)
|
|
484
|
+
|
|
485
|
+
# Generate unique bundle ID with optional label
|
|
486
|
+
bundle_id = generate_bundle_id(label=args.label)
|
|
487
|
+
print(f"🆕 Creating new session bundle: {bundle_id}")
|
|
488
|
+
if args.label:
|
|
489
|
+
print(f" 📝 Label: {args.label}")
|
|
490
|
+
|
|
491
|
+
# Create bundle structure
|
|
492
|
+
bundle_path = create_bundle_structure(bundle_id)
|
|
493
|
+
print(f"📁 Bundle directory created: {bundle_path}")
|
|
494
|
+
|
|
495
|
+
# Gather session data
|
|
496
|
+
logger.info("Gathering session data...")
|
|
497
|
+
git_ops = [] if args.no_git_ops else get_git_operations()
|
|
498
|
+
modified_files = get_modified_files()
|
|
499
|
+
|
|
500
|
+
# Generate metadata and summary
|
|
501
|
+
logger.info("Generating metadata and summary...")
|
|
502
|
+
metadata = generate_metadata(bundle_id, git_ops, modified_files)
|
|
503
|
+
summary = generate_summary(metadata)
|
|
504
|
+
|
|
505
|
+
# Write bundle files
|
|
506
|
+
try:
|
|
507
|
+
(bundle_path / "metadata.json").write_text(json.dumps(metadata, indent=2))
|
|
508
|
+
(bundle_path / "summary.md").write_text(summary)
|
|
509
|
+
logger.info("Wrote metadata.json and summary.md")
|
|
510
|
+
except (OSError, IOError) as e:
|
|
511
|
+
logger.error(f"Failed to write bundle files: {e}")
|
|
512
|
+
return
|
|
513
|
+
|
|
514
|
+
create_transcript(bundle_path)
|
|
515
|
+
|
|
516
|
+
# Copy artifacts and context
|
|
517
|
+
copy_artifacts(bundle_path, modified_files)
|
|
518
|
+
copy_active_context(bundle_path)
|
|
519
|
+
|
|
520
|
+
print("✅ Session bundle created successfully!")
|
|
521
|
+
print(f"📁 Bundle: bundles/{bundle_id}")
|
|
522
|
+
print(f" (Full path: {bundle_path})")
|
|
523
|
+
print(f" 📊 {len(git_ops)} git operations captured")
|
|
524
|
+
print(f" 📄 {len(modified_files)} modified files detected")
|
|
525
|
+
print(f" 💾 Bundle ready for sharing or archival")
|
|
526
|
+
|
|
527
|
+
logger.info("Session bundle creation completed successfully")
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
if __name__ == "__main__":
|
|
531
|
+
main()
|