claudia-mentor 0.5.4 → 0.5.6
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.
|
@@ -101,8 +101,8 @@ def main():
|
|
|
101
101
|
|
|
102
102
|
if tip:
|
|
103
103
|
save_state(session_id, state)
|
|
104
|
+
# Only systemMessage here -- additionalContext gets wiped by compaction
|
|
104
105
|
output = json.dumps({
|
|
105
|
-
"additionalContext": tip,
|
|
106
106
|
"systemMessage": f"\033[38;5;209m{tip}\033[0m",
|
|
107
107
|
})
|
|
108
108
|
print(output)
|
|
@@ -9,6 +9,7 @@ Advisory only (exit 0 with additionalContext), never blocks.
|
|
|
9
9
|
import json
|
|
10
10
|
import os
|
|
11
11
|
import random
|
|
12
|
+
import subprocess
|
|
12
13
|
import sys
|
|
13
14
|
|
|
14
15
|
# Pool of startup tips for beginners
|
|
@@ -30,6 +31,64 @@ COMPACT_TIP = (
|
|
|
30
31
|
"conversation details. If you need to re-explain something, that's normal."
|
|
31
32
|
)
|
|
32
33
|
|
|
34
|
+
|
|
35
|
+
def gather_project_context():
|
|
36
|
+
"""Read claudia-context.json, milestones, and git state to rebuild context after compaction."""
|
|
37
|
+
parts = []
|
|
38
|
+
|
|
39
|
+
# Stack and decisions from context file
|
|
40
|
+
context_path = os.path.expanduser("~/.claude/claudia-context.json")
|
|
41
|
+
if os.path.exists(context_path):
|
|
42
|
+
try:
|
|
43
|
+
with open(context_path) as f:
|
|
44
|
+
ctx = json.load(f)
|
|
45
|
+
stack = ctx.get("stack", {})
|
|
46
|
+
decisions = ctx.get("decisions", [])
|
|
47
|
+
experience = ctx.get("experience", "intermediate")
|
|
48
|
+
if stack:
|
|
49
|
+
parts.append(f"Project stack: {json.dumps(stack)}")
|
|
50
|
+
if decisions:
|
|
51
|
+
parts.append("Decisions made this session: " + "; ".join(str(d) for d in decisions[-5:]))
|
|
52
|
+
if experience == "beginner":
|
|
53
|
+
parts.append("User experience level: beginner. Use simple language, explain jargon.")
|
|
54
|
+
except (json.JSONDecodeError, IOError):
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
# Milestones achieved
|
|
58
|
+
milestones_path = os.path.expanduser("~/.claude/claudia-milestones.json")
|
|
59
|
+
if os.path.exists(milestones_path):
|
|
60
|
+
try:
|
|
61
|
+
with open(milestones_path) as f:
|
|
62
|
+
ms = json.load(f)
|
|
63
|
+
achieved = ms.get("achieved", [])
|
|
64
|
+
if achieved:
|
|
65
|
+
parts.append(f"Milestones achieved: {', '.join(achieved)}")
|
|
66
|
+
except (json.JSONDecodeError, IOError):
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
# Recent git activity
|
|
70
|
+
try:
|
|
71
|
+
log = subprocess.run(
|
|
72
|
+
["git", "log", "--oneline", "-5"],
|
|
73
|
+
capture_output=True, text=True, timeout=5
|
|
74
|
+
)
|
|
75
|
+
if log.returncode == 0 and log.stdout.strip():
|
|
76
|
+
parts.append(f"Recent commits:\n{log.stdout.strip()}")
|
|
77
|
+
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
status = subprocess.run(
|
|
82
|
+
["git", "status", "--short"],
|
|
83
|
+
capture_output=True, text=True, timeout=5
|
|
84
|
+
)
|
|
85
|
+
if status.returncode == 0 and status.stdout.strip():
|
|
86
|
+
parts.append(f"Uncommitted changes:\n{status.stdout.strip()}")
|
|
87
|
+
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
return "\n".join(parts) if parts else None
|
|
91
|
+
|
|
33
92
|
# Beginner greeting — no command list, just reassurance
|
|
34
93
|
BEGINNER_GREETING = (
|
|
35
94
|
"IMPORTANT — Claudia plugin is loaded. On this very first response, "
|
|
@@ -161,15 +220,41 @@ def main():
|
|
|
161
220
|
state.setdefault("shown_tips_history", []).append(tip_text)
|
|
162
221
|
tip = f"\U0001f4a1 Claudia tip: {tip_text}"
|
|
163
222
|
|
|
164
|
-
elif source == "compact"
|
|
165
|
-
|
|
223
|
+
elif source == "compact":
|
|
224
|
+
# Re-inject project context after compaction (this survives because
|
|
225
|
+
# SessionStart fires AFTER compaction completes)
|
|
226
|
+
project_ctx = gather_project_context()
|
|
227
|
+
if project_ctx:
|
|
228
|
+
context_injection = (
|
|
229
|
+
"Claudia context recovery: The conversation was just compacted. "
|
|
230
|
+
"Here is what Claudia knows about the current project and session:\n\n"
|
|
231
|
+
+ project_ctx
|
|
232
|
+
+ "\n\nUse this context to stay grounded. Don't mention compaction unless the user asks."
|
|
233
|
+
)
|
|
234
|
+
# Prepend to greeting so it goes into additionalContext
|
|
235
|
+
greeting = context_injection
|
|
236
|
+
|
|
237
|
+
if is_beginner and not state.get("shown_compact_tip"):
|
|
166
238
|
state["shown_compact_tip"] = True
|
|
167
239
|
tip = f"\U0001f4a1 Claudia: {COMPACT_TIP}"
|
|
240
|
+
elif not is_beginner:
|
|
241
|
+
tip = "\U0001f4a1 Claudia: Context compacted. I've caught Claude up on your project."
|
|
168
242
|
|
|
169
243
|
elif source == "resume":
|
|
244
|
+
# Re-inject context on resume too
|
|
245
|
+
project_ctx = gather_project_context()
|
|
246
|
+
if project_ctx:
|
|
247
|
+
context_injection = (
|
|
248
|
+
"Claudia context recovery: This is a resumed session. "
|
|
249
|
+
"Here is what Claudia knows about the current project:\n\n"
|
|
250
|
+
+ project_ctx
|
|
251
|
+
+ "\n\nUse this context to stay grounded."
|
|
252
|
+
)
|
|
253
|
+
greeting = context_injection
|
|
254
|
+
|
|
170
255
|
if not state.get("shown_resume_tip"):
|
|
171
256
|
state["shown_resume_tip"] = True
|
|
172
|
-
tip = "\U0001f4a1 Claudia: Welcome back. I'
|
|
257
|
+
tip = "\U0001f4a1 Claudia: Welcome back. I've caught Claude up on your project."
|
|
173
258
|
|
|
174
259
|
# source == "clear": no tip needed
|
|
175
260
|
|
package/package.json
CHANGED