claude-mpm 5.6.4__py3-none-any.whl → 5.6.8__py3-none-any.whl
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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/base_agent.json +1 -1
- claude_mpm/cli/commands/skill_source.py +51 -2
- claude_mpm/cli/commands/skills.py +5 -3
- claude_mpm/cli/parsers/skill_source_parser.py +4 -0
- claude_mpm/cli/parsers/skills_parser.py +5 -0
- claude_mpm/config/skill_sources.py +16 -0
- claude_mpm/core/config.py +27 -19
- claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BQaXIfA_.js +16 -16
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/auto_pause_handler.py +29 -30
- claude_mpm/hooks/claude_hooks/installer.py +41 -0
- claude_mpm/hooks/claude_hooks/memory_integration.py +30 -21
- claude_mpm/hooks/claude_hooks/response_tracking.py +39 -58
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +23 -28
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +22 -26
- claude_mpm/hooks/claude_hooks/services/state_manager.py +23 -36
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +47 -73
- claude_mpm/hooks/session_resume_hook.py +22 -18
- claude_mpm/hooks/templates/pre_tool_use_template.py +10 -2
- claude_mpm/scripts/claude-hook-handler.sh +5 -5
- claude_mpm/services/agents/agent_selection_service.py +2 -2
- claude_mpm/services/agents/single_tier_deployment_service.py +4 -4
- claude_mpm/services/skills/git_skill_source_manager.py +79 -8
- claude_mpm/services/skills/selective_skill_deployer.py +28 -0
- claude_mpm/services/skills/skill_discovery_service.py +17 -1
- claude_mpm/services/skills_deployer.py +31 -5
- {claude_mpm-5.6.4.dist-info → claude_mpm-5.6.8.dist-info}/METADATA +1 -1
- {claude_mpm-5.6.4.dist-info → claude_mpm-5.6.8.dist-info}/RECORD +73 -34
- {claude_mpm-5.6.4.dist-info → claude_mpm-5.6.8.dist-info}/WHEEL +0 -0
- {claude_mpm-5.6.4.dist-info → claude_mpm-5.6.8.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.6.4.dist-info → claude_mpm-5.6.8.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.6.4.dist-info → claude_mpm-5.6.8.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.6.4.dist-info → claude_mpm-5.6.8.dist-info}/top_level.txt +0 -0
claude_mpm/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.6.
|
|
1
|
+
5.6.8
|
|
@@ -11,6 +11,7 @@ for better UX. Handles errors gracefully with actionable messages.
|
|
|
11
11
|
|
|
12
12
|
import json
|
|
13
13
|
import logging
|
|
14
|
+
import os
|
|
14
15
|
import re
|
|
15
16
|
|
|
16
17
|
from ...config.skill_sources import SkillSource, SkillSourceConfiguration
|
|
@@ -20,6 +21,33 @@ from ...services.skills.skill_discovery_service import SkillDiscoveryService
|
|
|
20
21
|
logger = logging.getLogger(__name__)
|
|
21
22
|
|
|
22
23
|
|
|
24
|
+
def _get_github_token(source: SkillSource | None = None) -> str | None:
|
|
25
|
+
"""Get GitHub token with source-specific override support.
|
|
26
|
+
|
|
27
|
+
Priority: source.token > GITHUB_TOKEN > GH_TOKEN
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
source: Optional SkillSource to check for per-source token
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
GitHub token if found, None otherwise
|
|
34
|
+
|
|
35
|
+
Security Note:
|
|
36
|
+
Token is never logged or printed to avoid exposure.
|
|
37
|
+
"""
|
|
38
|
+
# Priority 1: Per-source token (env var reference or direct)
|
|
39
|
+
if source and source.token:
|
|
40
|
+
if source.token.startswith("$"):
|
|
41
|
+
# Env var reference: $VAR_NAME -> os.environ.get("VAR_NAME")
|
|
42
|
+
env_var_name = source.token[1:]
|
|
43
|
+
return os.environ.get(env_var_name)
|
|
44
|
+
# Direct token (not recommended but supported)
|
|
45
|
+
return source.token
|
|
46
|
+
|
|
47
|
+
# Priority 2-3: Global environment variables
|
|
48
|
+
return os.environ.get("GITHUB_TOKEN") or os.environ.get("GH_TOKEN")
|
|
49
|
+
|
|
50
|
+
|
|
23
51
|
def _test_skill_repository_access(source: SkillSource) -> dict:
|
|
24
52
|
"""Test if skill repository is accessible via GitHub API.
|
|
25
53
|
|
|
@@ -58,7 +86,13 @@ def _test_skill_repository_access(source: SkillSource) -> dict:
|
|
|
58
86
|
# Test GitHub API access
|
|
59
87
|
api_url = f"https://api.github.com/repos/{owner_repo}"
|
|
60
88
|
|
|
61
|
-
|
|
89
|
+
# Build headers with authentication if token available
|
|
90
|
+
headers = {"Accept": "application/vnd.github+json"}
|
|
91
|
+
token = _get_github_token(source)
|
|
92
|
+
if token:
|
|
93
|
+
headers["Authorization"] = f"token {token}"
|
|
94
|
+
|
|
95
|
+
response = requests.get(api_url, headers=headers, timeout=10)
|
|
62
96
|
|
|
63
97
|
if response.status_code == 200:
|
|
64
98
|
return {"accessible": True, "error": None}
|
|
@@ -68,9 +102,14 @@ def _test_skill_repository_access(source: SkillSource) -> dict:
|
|
|
68
102
|
"error": f"Repository not found: {owner_repo}",
|
|
69
103
|
}
|
|
70
104
|
if response.status_code == 403:
|
|
105
|
+
error_msg = "Access denied (private repository or rate limit)"
|
|
106
|
+
if not token:
|
|
107
|
+
error_msg += (
|
|
108
|
+
". Try setting GITHUB_TOKEN environment variable for private repos"
|
|
109
|
+
)
|
|
71
110
|
return {
|
|
72
111
|
"accessible": False,
|
|
73
|
-
"error":
|
|
112
|
+
"error": error_msg,
|
|
74
113
|
}
|
|
75
114
|
return {
|
|
76
115
|
"accessible": False,
|
|
@@ -263,6 +302,15 @@ def handle_add_skill_source(args) -> int:
|
|
|
263
302
|
|
|
264
303
|
# Create new source
|
|
265
304
|
enabled = not args.disabled
|
|
305
|
+
token = getattr(args, "token", None)
|
|
306
|
+
|
|
307
|
+
# Security warning for direct tokens
|
|
308
|
+
if token and not token.startswith("$"):
|
|
309
|
+
print("⚠️ Warning: Direct token values in config are not recommended")
|
|
310
|
+
print(" Consider using environment variable reference instead:")
|
|
311
|
+
print(" --token $MY_PRIVATE_TOKEN")
|
|
312
|
+
print()
|
|
313
|
+
|
|
266
314
|
source = SkillSource(
|
|
267
315
|
id=source_id,
|
|
268
316
|
type="git",
|
|
@@ -270,6 +318,7 @@ def handle_add_skill_source(args) -> int:
|
|
|
270
318
|
branch=args.branch,
|
|
271
319
|
priority=args.priority,
|
|
272
320
|
enabled=enabled,
|
|
321
|
+
token=token,
|
|
273
322
|
)
|
|
274
323
|
|
|
275
324
|
# Determine if we should test
|
|
@@ -538,6 +538,7 @@ class SkillsManagementCommand(BaseCommand):
|
|
|
538
538
|
toolchain = getattr(args, "toolchain", None)
|
|
539
539
|
categories = getattr(args, "categories", None)
|
|
540
540
|
force = getattr(args, "force", False)
|
|
541
|
+
deploy_all = getattr(args, "all", False)
|
|
541
542
|
|
|
542
543
|
if collection:
|
|
543
544
|
console.print(
|
|
@@ -548,14 +549,15 @@ class SkillsManagementCommand(BaseCommand):
|
|
|
548
549
|
"\n[bold cyan]Deploying skills from default collection...[/bold cyan]\n"
|
|
549
550
|
)
|
|
550
551
|
|
|
551
|
-
#
|
|
552
|
-
#
|
|
552
|
+
# Use selective deployment unless --all flag is provided
|
|
553
|
+
# Selective mode deploys only agent-referenced skills
|
|
554
|
+
# --all mode deploys all available skills from the collection
|
|
553
555
|
result = self.skills_deployer.deploy_skills(
|
|
554
556
|
collection=collection,
|
|
555
557
|
toolchain=toolchain,
|
|
556
558
|
categories=categories,
|
|
557
559
|
force=force,
|
|
558
|
-
selective=
|
|
560
|
+
selective=not deploy_all,
|
|
559
561
|
)
|
|
560
562
|
|
|
561
563
|
# Display results
|
|
@@ -76,6 +76,10 @@ def add_skill_source_subparser(subparsers) -> argparse.ArgumentParser:
|
|
|
76
76
|
dest="skip_test",
|
|
77
77
|
help="Skip immediate testing (not recommended)",
|
|
78
78
|
)
|
|
79
|
+
add_parser.add_argument(
|
|
80
|
+
"--token",
|
|
81
|
+
help="GitHub token or env var reference (e.g., ghp_xxx or $PRIVATE_TOKEN)",
|
|
82
|
+
)
|
|
79
83
|
|
|
80
84
|
# Remove repository
|
|
81
85
|
remove_parser = skill_source_subparsers.add_parser(
|
|
@@ -167,6 +167,11 @@ def add_skills_subparser(subparsers) -> argparse.ArgumentParser:
|
|
|
167
167
|
action="store_true",
|
|
168
168
|
help="Force redeployment of already deployed skills",
|
|
169
169
|
)
|
|
170
|
+
deploy_github_parser.add_argument(
|
|
171
|
+
"--all",
|
|
172
|
+
action="store_true",
|
|
173
|
+
help="Deploy all available skills, not just agent-referenced ones",
|
|
174
|
+
)
|
|
170
175
|
|
|
171
176
|
# List available GitHub skills
|
|
172
177
|
list_available_parser = skills_subparsers.add_parser(
|
|
@@ -54,6 +54,7 @@ class SkillSource:
|
|
|
54
54
|
branch: Git branch to use (default: "main")
|
|
55
55
|
priority: Priority for skill resolution (lower = higher precedence)
|
|
56
56
|
enabled: Whether this source should be synced
|
|
57
|
+
token: Optional GitHub token or env var reference (e.g., "$MY_TOKEN")
|
|
57
58
|
|
|
58
59
|
Priority System:
|
|
59
60
|
- 0: Reserved for system repository (highest precedence)
|
|
@@ -61,6 +62,12 @@ class SkillSource:
|
|
|
61
62
|
- 100-999: Normal priority custom sources
|
|
62
63
|
- 1000+: Low priority custom sources
|
|
63
64
|
|
|
65
|
+
Token Authentication:
|
|
66
|
+
- Direct token: "ghp_xxxxx" (stored in config, not recommended)
|
|
67
|
+
- Env var reference: "$PRIVATE_REPO_TOKEN" (resolved at runtime)
|
|
68
|
+
- If None, falls back to GITHUB_TOKEN or GH_TOKEN env vars
|
|
69
|
+
- Priority: source.token > GITHUB_TOKEN > GH_TOKEN
|
|
70
|
+
|
|
64
71
|
Example:
|
|
65
72
|
>>> source = SkillSource(
|
|
66
73
|
... id="system",
|
|
@@ -70,6 +77,12 @@ class SkillSource:
|
|
|
70
77
|
... )
|
|
71
78
|
>>> source.validate()
|
|
72
79
|
[]
|
|
80
|
+
>>> private_source = SkillSource(
|
|
81
|
+
... id="private",
|
|
82
|
+
... type="git",
|
|
83
|
+
... url="https://github.com/myorg/private-skills",
|
|
84
|
+
... token="$PRIVATE_REPO_TOKEN"
|
|
85
|
+
... )
|
|
73
86
|
"""
|
|
74
87
|
|
|
75
88
|
id: str
|
|
@@ -78,6 +91,7 @@ class SkillSource:
|
|
|
78
91
|
branch: str = "main"
|
|
79
92
|
priority: int = 100
|
|
80
93
|
enabled: bool = True
|
|
94
|
+
token: Optional[str] = None
|
|
81
95
|
|
|
82
96
|
def __post_init__(self):
|
|
83
97
|
"""Validate skill source configuration after initialization.
|
|
@@ -262,6 +276,7 @@ class SkillSourceConfiguration:
|
|
|
262
276
|
branch=source_data.get("branch", "main"),
|
|
263
277
|
priority=source_data.get("priority", 100),
|
|
264
278
|
enabled=source_data.get("enabled", True),
|
|
279
|
+
token=source_data.get("token"),
|
|
265
280
|
)
|
|
266
281
|
sources.append(source)
|
|
267
282
|
except (KeyError, ValueError) as e:
|
|
@@ -326,6 +341,7 @@ class SkillSourceConfiguration:
|
|
|
326
341
|
"branch": source.branch,
|
|
327
342
|
"priority": source.priority,
|
|
328
343
|
"enabled": source.enabled,
|
|
344
|
+
**({"token": source.token} if source.token else {}),
|
|
329
345
|
}
|
|
330
346
|
for source in sources
|
|
331
347
|
]
|
claude_mpm/core/config.py
CHANGED
|
@@ -12,11 +12,10 @@ import threading
|
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
14
14
|
|
|
15
|
-
import yaml
|
|
16
|
-
|
|
17
15
|
from claude_mpm.core.logging_utils import get_logger
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
# Lazy import ConfigurationManager to avoid importing yaml at module level
|
|
18
|
+
# This prevents hook errors when yaml isn't available in the execution environment
|
|
20
19
|
from .exceptions import ConfigurationError, FileOperationError
|
|
21
20
|
from .unified_paths import get_path_manager
|
|
22
21
|
|
|
@@ -104,6 +103,9 @@ class Config:
|
|
|
104
103
|
Config._initialized = True
|
|
105
104
|
logger.debug("Initializing Config singleton for the first time")
|
|
106
105
|
|
|
106
|
+
# Lazy import ConfigurationManager at runtime to avoid yaml import at module level
|
|
107
|
+
from ..utils.config_manager import ConfigurationManager
|
|
108
|
+
|
|
107
109
|
# Initialize instance variables inside the lock to ensure thread safety
|
|
108
110
|
self._config: Dict[str, Any] = {}
|
|
109
111
|
self._env_prefix = env_prefix
|
|
@@ -224,21 +226,6 @@ class Config:
|
|
|
224
226
|
f"Response logging format: {response_logging.get('format', 'json')}"
|
|
225
227
|
)
|
|
226
228
|
|
|
227
|
-
except yaml.YAMLError as e:
|
|
228
|
-
logger.error(f"YAML syntax error in {file_path}: {e}")
|
|
229
|
-
if hasattr(e, "problem_mark"):
|
|
230
|
-
mark = e.problem_mark
|
|
231
|
-
logger.error(f"Error at line {mark.line + 1}, column {mark.column + 1}")
|
|
232
|
-
logger.info(
|
|
233
|
-
"TIP: Validate your YAML at https://www.yamllint.com/ or run: python scripts/validate_configuration.py"
|
|
234
|
-
)
|
|
235
|
-
logger.info(
|
|
236
|
-
"TIP: Common issue - YAML requires spaces, not tabs. Fix with: sed -i '' 's/\t/ /g' "
|
|
237
|
-
+ str(file_path)
|
|
238
|
-
)
|
|
239
|
-
# Store error for later retrieval
|
|
240
|
-
self._config["_load_error"] = str(e)
|
|
241
|
-
|
|
242
229
|
except json.JSONDecodeError as e:
|
|
243
230
|
logger.error(f"JSON syntax error in {file_path}: {e}")
|
|
244
231
|
logger.error(f"Error at line {e.lineno}, column {e.colno}")
|
|
@@ -255,7 +242,28 @@ class Config:
|
|
|
255
242
|
},
|
|
256
243
|
) from e
|
|
257
244
|
except Exception as e:
|
|
258
|
-
#
|
|
245
|
+
# Handle YAML errors without importing yaml at module level
|
|
246
|
+
# ConfigurationManager.load_yaml raises yaml.YAMLError, but we don't want to import yaml
|
|
247
|
+
# Check if it's a YAML error by class name to avoid import
|
|
248
|
+
if e.__class__.__name__ == "YAMLError":
|
|
249
|
+
logger.error(f"YAML syntax error in {file_path}: {e}")
|
|
250
|
+
if hasattr(e, "problem_mark"):
|
|
251
|
+
mark = e.problem_mark
|
|
252
|
+
logger.error(
|
|
253
|
+
f"Error at line {mark.line + 1}, column {mark.column + 1}"
|
|
254
|
+
)
|
|
255
|
+
logger.info(
|
|
256
|
+
"TIP: Validate your YAML at https://www.yamllint.com/ or run: python scripts/validate_configuration.py"
|
|
257
|
+
)
|
|
258
|
+
logger.info(
|
|
259
|
+
"TIP: Common issue - YAML requires spaces, not tabs. Fix with: sed -i '' 's/\t/ /g' "
|
|
260
|
+
+ str(file_path)
|
|
261
|
+
)
|
|
262
|
+
# Store error for later retrieval
|
|
263
|
+
self._config["_load_error"] = str(e)
|
|
264
|
+
return # Don't re-raise, we handled it
|
|
265
|
+
|
|
266
|
+
# Not a YAML error, wrap as configuration error
|
|
259
267
|
raise ConfigurationError(
|
|
260
268
|
f"Unexpected error loading configuration from {file_path}: {e}",
|
|
261
269
|
context={
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const env={}
|
|
1
|
+
export const env={}
|
|
@@ -43,10 +43,10 @@ https://github.com/jquery/jquery/blob/master/src/event.js
|
|
|
43
43
|
vec2 v = ab*vec2(-cs.y,cs.x);
|
|
44
44
|
w = w + dot(p-u,v)/(dot(p-u,u)+dot(v,v));
|
|
45
45
|
}
|
|
46
|
-
|
|
46
|
+
|
|
47
47
|
// compute final point and distance
|
|
48
48
|
float d = length(p-ab*vec2(cos(w),sin(w)));
|
|
49
|
-
|
|
49
|
+
|
|
50
50
|
// return signed distance
|
|
51
51
|
return (dot(p/ab,p/ab)>1.0) ? d : -d;
|
|
52
52
|
}
|
|
@@ -55,16 +55,16 @@ https://github.com/jquery/jquery/blob/master/src/event.js
|
|
|
55
55
|
|
|
56
56
|
uniform mat3 uPanZoomMatrix;
|
|
57
57
|
uniform int uAtlasSize;
|
|
58
|
-
|
|
58
|
+
|
|
59
59
|
// instanced
|
|
60
60
|
in vec2 aPosition; // a vertex from the unit square
|
|
61
|
-
|
|
61
|
+
|
|
62
62
|
in mat3 aTransform; // used to transform verticies, eg into a bounding box
|
|
63
63
|
in int aVertType; // the type of thing we are rendering
|
|
64
64
|
|
|
65
65
|
// the z-index that is output when using picking mode
|
|
66
66
|
in vec4 aIndex;
|
|
67
|
-
|
|
67
|
+
|
|
68
68
|
// For textures
|
|
69
69
|
in int aAtlasId; // which shader unit/atlas to use
|
|
70
70
|
in vec4 aTex; // x/y/w/h of texture in atlas
|
|
@@ -84,7 +84,7 @@ https://github.com/jquery/jquery/blob/master/src/event.js
|
|
|
84
84
|
out vec4 vColor;
|
|
85
85
|
out vec2 vPosition;
|
|
86
86
|
// flat values are not interpolated
|
|
87
|
-
flat out int vAtlasId;
|
|
87
|
+
flat out int vAtlasId;
|
|
88
88
|
flat out int vVertType;
|
|
89
89
|
flat out vec2 vTopRight;
|
|
90
90
|
flat out vec2 vBotLeft;
|
|
@@ -92,7 +92,7 @@ https://github.com/jquery/jquery/blob/master/src/event.js
|
|
|
92
92
|
flat out vec4 vBorderColor;
|
|
93
93
|
flat out vec2 vBorderWidth;
|
|
94
94
|
flat out vec4 vIndex;
|
|
95
|
-
|
|
95
|
+
|
|
96
96
|
void main(void) {
|
|
97
97
|
int vid = gl_VertexID;
|
|
98
98
|
vec2 position = aPosition; // TODO make this a vec3, simplifies some code below
|
|
@@ -115,7 +115,7 @@ https://github.com/jquery/jquery/blob/master/src/event.js
|
|
|
115
115
|
|
|
116
116
|
gl_Position = vec4(uPanZoomMatrix * aTransform * vec3(position, 1.0), 1.0);
|
|
117
117
|
}
|
|
118
|
-
else if(aVertType == `).concat(_t," || aVertType == ").concat(pa,`
|
|
118
|
+
else if(aVertType == `).concat(_t," || aVertType == ").concat(pa,`
|
|
119
119
|
|| aVertType == `).concat(sn," || aVertType == ").concat(ga,`) { // simple shapes
|
|
120
120
|
|
|
121
121
|
// the bounding box is needed by the fragment shader
|
|
@@ -145,7 +145,7 @@ https://github.com/jquery/jquery/blob/master/src/event.js
|
|
|
145
145
|
|
|
146
146
|
gl_Position = vec4(uPanZoomMatrix * vec3(point, 1.0), 1.0);
|
|
147
147
|
vColor = aColor;
|
|
148
|
-
}
|
|
148
|
+
}
|
|
149
149
|
else if(aVertType == `).concat(Xl,`) {
|
|
150
150
|
vec2 pointA = aPointAPointB.xy;
|
|
151
151
|
vec2 pointB = aPointAPointB.zw;
|
|
@@ -194,7 +194,7 @@ https://github.com/jquery/jquery/blob/master/src/event.js
|
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
vColor = aColor;
|
|
197
|
-
}
|
|
197
|
+
}
|
|
198
198
|
else if(aVertType == `).concat(Ts,` && vid < 3) {
|
|
199
199
|
// massage the first triangle into an edge arrow
|
|
200
200
|
if(vid == 0)
|
|
@@ -246,16 +246,16 @@ https://github.com/jquery/jquery/blob/master/src/event.js
|
|
|
246
246
|
`).concat(vm,`
|
|
247
247
|
|
|
248
248
|
vec4 blend(vec4 top, vec4 bot) { // blend colors with premultiplied alpha
|
|
249
|
-
return vec4(
|
|
249
|
+
return vec4(
|
|
250
250
|
top.rgb + (bot.rgb * (1.0 - top.a)),
|
|
251
|
-
top.a + (bot.a * (1.0 - top.a))
|
|
251
|
+
top.a + (bot.a * (1.0 - top.a))
|
|
252
252
|
);
|
|
253
253
|
}
|
|
254
254
|
|
|
255
255
|
vec4 distInterp(vec4 cA, vec4 cB, float d) { // interpolate color using Signed Distance
|
|
256
256
|
// scale to the zoom level so that borders don't look blurry when zoomed in
|
|
257
257
|
// note 1.5 is an aribitrary value chosen because it looks good
|
|
258
|
-
return mix(cA, cB, 1.0 - smoothstep(0.0, 1.5 / uZoom, abs(d)));
|
|
258
|
+
return mix(cA, cB, 1.0 - smoothstep(0.0, 1.5 / uZoom, abs(d)));
|
|
259
259
|
}
|
|
260
260
|
|
|
261
261
|
void main(void) {
|
|
@@ -263,7 +263,7 @@ https://github.com/jquery/jquery/blob/master/src/event.js
|
|
|
263
263
|
// look up the texel from the texture unit
|
|
264
264
|
`).concat(i.map(function(u){return"if(vAtlasId == ".concat(u,") outColor = texture(uTexture").concat(u,", vTexCoord);")}).join(`
|
|
265
265
|
else `),`
|
|
266
|
-
}
|
|
266
|
+
}
|
|
267
267
|
else if(vVertType == `).concat(Ts,`) {
|
|
268
268
|
// mimics how canvas renderer uses context.globalCompositeOperation = 'destination-out';
|
|
269
269
|
outColor = blend(vColor, uBGColor);
|
|
@@ -272,7 +272,7 @@ https://github.com/jquery/jquery/blob/master/src/event.js
|
|
|
272
272
|
else if(vVertType == `).concat(_t,` && vBorderWidth == vec2(0.0)) { // simple rectangle with no border
|
|
273
273
|
outColor = vColor; // unit square is already transformed to the rectangle, nothing else needs to be done
|
|
274
274
|
}
|
|
275
|
-
else if(vVertType == `).concat(_t," || vVertType == ").concat(pa,`
|
|
275
|
+
else if(vVertType == `).concat(_t," || vVertType == ").concat(pa,`
|
|
276
276
|
|| vVertType == `).concat(sn," || vVertType == ").concat(ga,`) { // use SDF
|
|
277
277
|
|
|
278
278
|
float outerBorder = vBorderWidth[0];
|
|
@@ -307,7 +307,7 @@ https://github.com/jquery/jquery/blob/master/src/event.js
|
|
|
307
307
|
vec4 outerColor = outerBorder == 0.0 ? vec4(0) : vBorderColor;
|
|
308
308
|
vec4 innerBorderColor = blend(vBorderColor, vColor);
|
|
309
309
|
outColor = distInterp(innerBorderColor, outerColor, d);
|
|
310
|
-
}
|
|
310
|
+
}
|
|
311
311
|
else {
|
|
312
312
|
vec4 outerColor;
|
|
313
313
|
if(innerBorder == 0.0 && outerBorder == 0.0) {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -22,7 +22,7 @@ USAGE:
|
|
|
22
22
|
threshold_crossed = auto_pause.on_usage_update(metadata["usage"])
|
|
23
23
|
if threshold_crossed:
|
|
24
24
|
warning = auto_pause.emit_threshold_warning(threshold_crossed)
|
|
25
|
-
|
|
25
|
+
_log(f"\n⚠️ {warning}")
|
|
26
26
|
|
|
27
27
|
# Record actions during pause mode
|
|
28
28
|
if auto_pause.is_pause_active():
|
|
@@ -34,7 +34,6 @@ USAGE:
|
|
|
34
34
|
"""
|
|
35
35
|
|
|
36
36
|
import os
|
|
37
|
-
import sys
|
|
38
37
|
from datetime import datetime, timezone
|
|
39
38
|
from pathlib import Path
|
|
40
39
|
from typing import Any, Dict, Optional
|
|
@@ -45,6 +44,15 @@ from claude_mpm.services.infrastructure.context_usage_tracker import (
|
|
|
45
44
|
ContextUsageTracker,
|
|
46
45
|
)
|
|
47
46
|
|
|
47
|
+
# Try to import _log from hook_handler, fall back to no-op
|
|
48
|
+
try:
|
|
49
|
+
from claude_mpm.hooks.claude_hooks.hook_handler import _log
|
|
50
|
+
except ImportError:
|
|
51
|
+
|
|
52
|
+
def _log(msg: str) -> None:
|
|
53
|
+
pass # Silent fallback
|
|
54
|
+
|
|
55
|
+
|
|
48
56
|
logger = get_logger(__name__)
|
|
49
57
|
|
|
50
58
|
# Debug mode
|
|
@@ -100,11 +108,10 @@ class AutoPauseHandler:
|
|
|
100
108
|
self._previous_threshold = current_state.threshold_reached
|
|
101
109
|
|
|
102
110
|
if DEBUG:
|
|
103
|
-
|
|
111
|
+
_log(
|
|
104
112
|
f"AutoPauseHandler initialized: "
|
|
105
113
|
f"{current_state.percentage_used:.1f}% context used, "
|
|
106
|
-
f"threshold: {current_state.threshold_reached}"
|
|
107
|
-
file=sys.stderr,
|
|
114
|
+
f"threshold: {current_state.threshold_reached}"
|
|
108
115
|
)
|
|
109
116
|
except Exception as e:
|
|
110
117
|
logger.error(f"Failed to initialize AutoPauseHandler: {e}")
|
|
@@ -169,10 +176,9 @@ class AutoPauseHandler:
|
|
|
169
176
|
self._previous_threshold = current_threshold
|
|
170
177
|
|
|
171
178
|
if DEBUG:
|
|
172
|
-
|
|
179
|
+
_log(
|
|
173
180
|
f"Context threshold crossed: {current_threshold} "
|
|
174
|
-
f"({state.percentage_used:.1f}%)"
|
|
175
|
-
file=sys.stderr,
|
|
181
|
+
f"({state.percentage_used:.1f}%)"
|
|
176
182
|
)
|
|
177
183
|
|
|
178
184
|
# Trigger auto-pause if threshold reached
|
|
@@ -184,7 +190,7 @@ class AutoPauseHandler:
|
|
|
184
190
|
except Exception as e:
|
|
185
191
|
logger.error(f"Failed to update usage: {e}")
|
|
186
192
|
if DEBUG:
|
|
187
|
-
|
|
193
|
+
_log(f"❌ Usage update failed: {e}")
|
|
188
194
|
# Don't propagate error - auto-pause is optional
|
|
189
195
|
return None
|
|
190
196
|
|
|
@@ -220,12 +226,12 @@ class AutoPauseHandler:
|
|
|
220
226
|
)
|
|
221
227
|
|
|
222
228
|
if DEBUG:
|
|
223
|
-
|
|
229
|
+
_log(f"Recorded tool call during pause: {tool_name}")
|
|
224
230
|
|
|
225
231
|
except Exception as e:
|
|
226
232
|
logger.error(f"Failed to record tool call: {e}")
|
|
227
233
|
if DEBUG:
|
|
228
|
-
|
|
234
|
+
_log(f"❌ Failed to record tool call: {e}")
|
|
229
235
|
|
|
230
236
|
def on_assistant_response(self, response_summary: str) -> None:
|
|
231
237
|
"""Record an assistant response if auto-pause is active.
|
|
@@ -257,15 +263,14 @@ class AutoPauseHandler:
|
|
|
257
263
|
)
|
|
258
264
|
|
|
259
265
|
if DEBUG:
|
|
260
|
-
|
|
261
|
-
f"Recorded assistant response during pause (length: {len(summary)})"
|
|
262
|
-
file=sys.stderr,
|
|
266
|
+
_log(
|
|
267
|
+
f"Recorded assistant response during pause (length: {len(summary)})"
|
|
263
268
|
)
|
|
264
269
|
|
|
265
270
|
except Exception as e:
|
|
266
271
|
logger.error(f"Failed to record assistant response: {e}")
|
|
267
272
|
if DEBUG:
|
|
268
|
-
|
|
273
|
+
_log(f"❌ Failed to record assistant response: {e}")
|
|
269
274
|
|
|
270
275
|
def on_user_message(self, message_summary: str) -> None:
|
|
271
276
|
"""Record a user message if auto-pause is active.
|
|
@@ -297,15 +302,12 @@ class AutoPauseHandler:
|
|
|
297
302
|
)
|
|
298
303
|
|
|
299
304
|
if DEBUG:
|
|
300
|
-
|
|
301
|
-
f"Recorded user message during pause (length: {len(summary)})",
|
|
302
|
-
file=sys.stderr,
|
|
303
|
-
)
|
|
305
|
+
_log(f"Recorded user message during pause (length: {len(summary)})")
|
|
304
306
|
|
|
305
307
|
except Exception as e:
|
|
306
308
|
logger.error(f"Failed to record user message: {e}")
|
|
307
309
|
if DEBUG:
|
|
308
|
-
|
|
310
|
+
_log(f"❌ Failed to record user message: {e}")
|
|
309
311
|
|
|
310
312
|
def on_session_end(self) -> Optional[Path]:
|
|
311
313
|
"""Called when session ends. Finalizes any active pause.
|
|
@@ -318,7 +320,7 @@ class AutoPauseHandler:
|
|
|
318
320
|
"""
|
|
319
321
|
if not self.is_pause_active():
|
|
320
322
|
if DEBUG:
|
|
321
|
-
|
|
323
|
+
_log("No active pause to finalize")
|
|
322
324
|
return None
|
|
323
325
|
|
|
324
326
|
try:
|
|
@@ -326,14 +328,14 @@ class AutoPauseHandler:
|
|
|
326
328
|
session_path = self.pause_manager.finalize_pause(create_full_snapshot=True)
|
|
327
329
|
|
|
328
330
|
if session_path and DEBUG:
|
|
329
|
-
|
|
331
|
+
_log(f"✅ Session finalized: {session_path.name}")
|
|
330
332
|
|
|
331
333
|
return session_path
|
|
332
334
|
|
|
333
335
|
except Exception as e:
|
|
334
336
|
logger.error(f"Failed to finalize pause session: {e}")
|
|
335
337
|
if DEBUG:
|
|
336
|
-
|
|
338
|
+
_log(f"❌ Failed to finalize pause: {e}")
|
|
337
339
|
raise
|
|
338
340
|
|
|
339
341
|
def is_pause_active(self) -> bool:
|
|
@@ -417,9 +419,7 @@ class AutoPauseHandler:
|
|
|
417
419
|
# Check if pause is already active
|
|
418
420
|
if self.is_pause_active():
|
|
419
421
|
if DEBUG:
|
|
420
|
-
|
|
421
|
-
"Auto-pause already active, skipping trigger", file=sys.stderr
|
|
422
|
-
)
|
|
422
|
+
_log("Auto-pause already active, skipping trigger")
|
|
423
423
|
return
|
|
424
424
|
|
|
425
425
|
# Start incremental pause
|
|
@@ -429,16 +429,15 @@ class AutoPauseHandler:
|
|
|
429
429
|
)
|
|
430
430
|
|
|
431
431
|
if DEBUG:
|
|
432
|
-
|
|
432
|
+
_log(
|
|
433
433
|
f"✅ Auto-pause triggered: {session_id} "
|
|
434
|
-
f"({state.percentage_used:.1f}% context used)"
|
|
435
|
-
file=sys.stderr,
|
|
434
|
+
f"({state.percentage_used:.1f}% context used)"
|
|
436
435
|
)
|
|
437
436
|
|
|
438
437
|
except Exception as e:
|
|
439
438
|
logger.error(f"Failed to trigger auto-pause: {e}")
|
|
440
439
|
if DEBUG:
|
|
441
|
-
|
|
440
|
+
_log(f"❌ Failed to trigger auto-pause: {e}")
|
|
442
441
|
# Don't propagate - auto-pause is optional
|
|
443
442
|
|
|
444
443
|
def _summarize_dict(
|