Skip to content
This repository was archived by the owner on Jan 2, 2026. It is now read-only.

Commit da79eff

Browse files
committed
refactor(templates): convert guidance templates from XML to Markdown
- Replace XML templates with Markdown format for better readability - Update guidance_builder.py to load .md files instead of .xml - Update tests to check string content instead of XML parsing - Make plugin path discovery version-agnostic in validate command
1 parent d7b76b3 commit da79eff

6 files changed

Lines changed: 83 additions & 76 deletions

File tree

commands/validate.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,10 @@ print()
179179
print("## 4. Hook Entry Points")
180180
print("-" * 40)
181181
182-
plugin_root = Path.home() / '.claude/plugins/cache/git-notes-memory/memory-capture/0.1.0'
182+
# Discover plugin root dynamically (version-agnostic)
183+
plugin_cache = Path.home() / '.claude/plugins/cache/git-notes-memory/memory-capture'
184+
plugin_versions = sorted(plugin_cache.glob('*/'), key=lambda p: p.name, reverse=True) if plugin_cache.exists() else []
185+
plugin_root = plugin_versions[0] if plugin_versions else plugin_cache / '0.3.0'
183186
hooks_dir = plugin_root / "hooks"
184187
hooks = [
185188
("sessionstart.py", "SessionStart", "Context injection at session start"),

src/git_notes_memory/hooks/guidance_builder.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
"""Response guidance builder for SessionStart hook.
22
3-
This module provides the GuidanceBuilder class which loads XML templates
3+
This module provides the GuidanceBuilder class which loads Markdown templates
44
teaching Claude how to structure responses for reliable memory signal detection.
55
6-
The guidance templates are stored as external XML files in the templates/
6+
The guidance templates are stored as external Markdown files in the templates/
77
directory for easy editing without code changes.
88
99
The guidance includes:
@@ -29,7 +29,7 @@
2929

3030
logger = logging.getLogger(__name__)
3131

32-
# Directory containing XML templates
32+
# Directory containing Markdown templates
3333
TEMPLATES_DIR = Path(__file__).parent / "templates"
3434

3535

@@ -47,9 +47,9 @@ class GuidanceLevel(Enum):
4747

4848

4949
class GuidanceBuilder:
50-
"""Builds XML response guidance for session injection.
50+
"""Builds response guidance for session injection.
5151
52-
This class loads XML templates from the templates/ directory that teach
52+
This class loads Markdown templates from the templates/ directory that teach
5353
Claude how to structure responses for reliable memory signal detection.
5454
The guidance helps improve signal detection accuracy from ~70% to ~85%+.
5555
@@ -61,8 +61,8 @@ class GuidanceBuilder:
6161
Example::
6262
6363
builder = GuidanceBuilder()
64-
xml = builder.build_guidance("standard")
65-
# Returns XML string for additionalContext prepending
64+
guidance = builder.build_guidance("standard")
65+
# Returns guidance string for additionalContext prepending
6666
"""
6767

6868
def __init__(self, templates_dir: Path | None = None) -> None:
@@ -76,7 +76,7 @@ def __init__(self, templates_dir: Path | None = None) -> None:
7676
self._cache: dict[str, str] = {}
7777

7878
def build_guidance(self, detail_level: str = "standard") -> str:
79-
"""Build response guidance XML by loading from template file.
79+
"""Build response guidance by loading from template file.
8080
8181
Args:
8282
detail_level: One of "minimal", "standard", or "detailed".
@@ -85,7 +85,7 @@ def build_guidance(self, detail_level: str = "standard") -> str:
8585
- detailed: Full templates with examples (~1200 tokens)
8686
8787
Returns:
88-
XML string for inclusion in additionalContext.
88+
Guidance string for inclusion in additionalContext.
8989
9090
Raises:
9191
ValueError: If detail_level is not recognized.
@@ -94,7 +94,7 @@ def build_guidance(self, detail_level: str = "standard") -> str:
9494
Example::
9595
9696
builder = GuidanceBuilder()
97-
xml = builder.build_guidance("standard")
97+
guidance = builder.build_guidance("standard")
9898
# Use in SessionStart additionalContext
9999
"""
100100
try:
@@ -122,7 +122,7 @@ def _load_template(self, level: str) -> str:
122122
if level in self._cache:
123123
return self._cache[level]
124124

125-
template_path = self._templates_dir / f"guidance_{level}.xml"
125+
template_path = self._templates_dir / f"guidance_{level}.md"
126126

127127
if not template_path.exists():
128128
msg = f"Template file not found: {template_path}"

src/git_notes_memory/hooks/templates/guidance_detailed.xml renamed to src/git_notes_memory/hooks/templates/guidance_detailed.md

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<session_behavior_protocol level="detailed">
2-
<mandatory_rules><![CDATA[
2+
<mandatory_rules>
33
You are operating in **memory-capture mode**. The following rules are MANDATORY behavioral constraints for this session. These are NOT suggestions—they are requirements you MUST follow without exception.
44

55
---
@@ -8,12 +8,12 @@ You are operating in **memory-capture mode**. The following rules are MANDATORY
88

99
When ANY of these triggers occur, you MUST immediately include the appropriate marker inline in your response. Do not ask permission. Do not suggest using markers. USE THEM DIRECTLY.
1010

11-
| Trigger | REQUIRED Action | Example |
12-
|---------|-----------------|---------|
13-
| Making ANY decision (architectural, design, library choice, approach) | `[decision] What + Why` | `[decision] Using PostgreSQL for persistence: better JSON support, team expertise, ACID compliance needed` |
14-
| Learning something new, discovering a pattern, TIL moment, surprise | `[learned] Insight + Application` | `[learned] pytest module-scoped fixtures share state across tests—use function scope for mutable fixtures` |
15-
| Hitting a blocker, encountering an issue, stuck on a problem | `[blocker] Issue + Needed` | `[blocker] Schema migration blocked by circular FK dependencies between users and organizations tables` |
16-
| Completing significant work, finishing a milestone | `[progress] Accomplishment` | `[progress] Implemented JWT authentication with refresh token rotation` |
11+
| Trigger | REQUIRED Action | Example |
12+
| --------------------------------------------------------------------- | --------------------------------- | ---------------------------------------------------------------------------------------------------------- |
13+
| Making ANY decision (architectural, design, library choice, approach) | `[decision] What + Why` | `[decision] Using PostgreSQL for persistence: better JSON support, team expertise, ACID compliance needed` |
14+
| Learning something new, discovering a pattern, TIL moment, surprise | `[learned] Insight + Application` | `[learned] pytest module-scoped fixtures share state across tests—use function scope for mutable fixtures` |
15+
| Hitting a blocker, encountering an issue, stuck on a problem | `[blocker] Issue + Needed` | `[blocker] Schema migration blocked by circular FK dependencies between users and organizations tables` |
16+
| Completing significant work, finishing a milestone | `[progress] Accomplishment` | `[progress] Implemented JWT authentication with refresh token rotation` |
1717

1818
### Enforcement
1919

@@ -31,11 +31,13 @@ When `<memory_context>` contains retrieved memories, you MUST actively reference
3131
### Required Behaviors
3232

3333
1. **Acknowledge relevance explicitly:**
34+
3435
- "Based on a previous decision (memory `decisions:abc123:0`)..."
3536
- "You learned earlier that..."
3637
- "This connects to prior work on..."
3738

3839
2. **Surface matching blockers:**
40+
3941
- If a past blocker matches the current issue, say: "This looks similar to a previous blocker (memory ID) which was resolved by..."
4042

4143
3. **Weave naturally:**
@@ -55,21 +57,24 @@ When `<memory_context>` contains retrieved memories, you MUST actively reference
5557
By default, implement marker usage rather than discussing or suggesting it.
5658

5759
**Correct behavior:**
60+
5861
```
5962
We'll use JWT tokens for authentication because they're stateless and scale well.
6063
[decision] Using JWT for auth: stateless, scales to multiple servers, team has experience
6164
```
6265

6366
**Incorrect behavior:**
67+
6468
```
6569
We'll use JWT tokens. Would you like me to capture this decision?
6670
```
6771

6872
These rules supersede default helpful suggestions. They configure your behavior for this session.
69-
]]></mandatory_rules>
73+
</mandatory_rules>
7074

71-
<marker_reference><![CDATA[
75+
<marker_reference>
7276
**Valid markers:**
77+
7378
- `[decision] text` — decisions namespace
7479
- `[learned] text` — learnings namespace
7580
- `[blocker] text` — blockers namespace
@@ -80,11 +85,13 @@ These rules supersede default helpful suggestions. They configure your behavior
8085
**Valid namespaces:** decisions, learnings, blockers, progress, patterns, research, reviews, retrospective
8186

8287
**Structured format (optional for detailed captures):**
88+
8389
```
8490
**Decision**: [One-line summary]
8591
**Context**: [Why this decision was needed]
8692
**Choice**: [What was chosen]
8793
**Rationale**: [Why this choice over alternatives]
8894
```
89-
]]></marker_reference>
95+
96+
</marker_reference>
9097
</session_behavior_protocol>

src/git_notes_memory/hooks/templates/guidance_minimal.xml renamed to src/git_notes_memory/hooks/templates/guidance_minimal.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
# Guidance: Minimal Instructions
2+
13
<session_behavior_protocol level="minimal">
2-
<mandatory_rules><![CDATA[
4+
<mandatory_rules>
35
**REQUIRED:** Use these markers inline when triggers occur. Do not ask—act.
46

57
- Decision made: `[decision] rationale`
@@ -10,5 +12,5 @@
1012
**REQUIRED:** Reference memories from `<memory_context>` when relevant. Say "Based on previous..."
1113

1214
Valid namespaces: decisions, learnings, blockers, progress, patterns, research
13-
]]></mandatory_rules>
15+
</mandatory_rules>
1416
</session_behavior_protocol>

src/git_notes_memory/hooks/templates/guidance_standard.xml renamed to src/git_notes_memory/hooks/templates/guidance_standard.md

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
1+
# Guidance: Standard Instructions
2+
13
<session_behavior_protocol level="standard">
2-
<mandatory_rules><![CDATA[
4+
<mandatory_rules>
35
You are operating in **memory-capture mode**. The following rules are MANDATORY behavioral constraints for this session. These are NOT suggestions—they are requirements you MUST follow.
46

57
## Rule 1: CAPTURE MARKERS (Required)
68

79
When you make a decision, learn something, hit a blocker, or complete work, you MUST use the appropriate marker inline in your response. Do not ask permission—use them directly.
810

9-
| Trigger | REQUIRED Action |
10-
|---------|-----------------|
11-
| Making any architectural/design decision | Write: `[decision] What you decided and why` |
12-
| Learning something, discovering a pattern, TIL moment | Write: `[learned] The insight and why it matters` |
13-
| Hitting a blocker or encountering an issue | Write: `[blocker] What blocks progress and what's needed` |
14-
| Completing significant work | Write: `[progress] What was accomplished` |
11+
| Trigger | REQUIRED Action |
12+
| ----------------------------------------------------- | --------------------------------------------------------- |
13+
| Making any architectural/design decision | Write: `[decision] What you decided and why` |
14+
| Learning something, discovering a pattern, TIL moment | Write: `[learned] The insight and why it matters` |
15+
| Hitting a blocker or encountering an issue | Write: `[blocker] What blocks progress and what's needed` |
16+
| Completing significant work | Write: `[progress] What was accomplished` |
1517

1618
**Do not suggest using markers. USE THEM DIRECTLY when the trigger occurs.**
1719

@@ -28,15 +30,16 @@ When `<memory_context>` contains retrieved memories, you MUST actively reference
2830
## Rule 3: DEFAULT TO ACTION
2931

3032
By default, implement marker usage rather than discussing it. When a trigger condition is met:
33+
3134
- DO: Include the marker inline in your response
3235
- DON'T: Ask "should I capture this?" or "would you like me to remember this?"
3336

3437
These rules supersede default helpful suggestions. They configure your behavior for this session.
35-
]]></mandatory_rules>
38+
</mandatory_rules>
3639

37-
<marker_reference><![CDATA[
40+
<marker_reference>
3841
**Valid markers:** `[decision]`, `[learned]`, `[blocker]`, `[progress]`, `[remember]`, `[remember:namespace]`
3942

4043
**Valid namespaces:** decisions, learnings, blockers, progress, patterns, research, reviews, retrospective
41-
]]></marker_reference>
44+
</marker_reference>
4245
</session_behavior_protocol>

tests/test_guidance_builder.py

Lines changed: 34 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,13 @@
22
33
Tests the response guidance builder including:
44
- GuidanceBuilder class
5-
- XML generation for different detail levels
5+
- Markdown template generation for different detail levels
66
- GuidanceLevel enum
77
- Configuration integration
88
"""
99

1010
from __future__ import annotations
1111

12-
import xml.etree.ElementTree as ET
13-
1412
import pytest
1513

1614
from git_notes_memory.hooks.config_loader import (
@@ -62,12 +60,12 @@ def test_all_levels_defined(self) -> None:
6260

6361

6462
# =============================================================================
65-
# XML Template Content Tests
63+
# Template Content Tests
6664
# =============================================================================
6765

6866

69-
class TestXMLTemplateContent:
70-
"""Test that XML templates contain expected content."""
67+
class TestTemplateContent:
68+
"""Test that templates contain expected content."""
7169

7270
def test_detailed_has_behavioral_examples(
7371
self, guidance_builder: GuidanceBuilder
@@ -164,42 +162,36 @@ def test_contains_inline_marker_syntax(
164162
assert "[blocker]" in xml
165163

166164

167-
class TestGuidanceBuilderXMLStructure:
168-
"""Test XML structure validity of generated guidance."""
165+
class TestGuidanceBuilderStructure:
166+
"""Test structure of generated guidance templates."""
169167

170-
def test_minimal_is_valid_xml(self, guidance_builder: GuidanceBuilder) -> None:
171-
"""Test that minimal output is valid XML."""
172-
xml = guidance_builder.build_guidance("minimal")
173-
# Should parse without error (S314 safe: parsing our own generated XML)
174-
root = ET.fromstring(xml) # noqa: S314
175-
assert root.tag == "session_behavior_protocol"
176-
assert root.attrib.get("level") == "minimal"
168+
def test_minimal_has_protocol_tag(self, guidance_builder: GuidanceBuilder) -> None:
169+
"""Test that minimal output has session_behavior_protocol structure."""
170+
content = guidance_builder.build_guidance("minimal")
171+
assert '<session_behavior_protocol level="minimal">' in content
172+
assert "</session_behavior_protocol>" in content
177173

178-
def test_standard_is_valid_xml(self, guidance_builder: GuidanceBuilder) -> None:
179-
"""Test that standard output is valid XML."""
180-
xml = guidance_builder.build_guidance("standard")
181-
root = ET.fromstring(xml) # noqa: S314
182-
assert root.tag == "session_behavior_protocol"
183-
assert root.attrib.get("level") == "standard"
174+
def test_standard_has_protocol_tag(self, guidance_builder: GuidanceBuilder) -> None:
175+
"""Test that standard output has session_behavior_protocol structure."""
176+
content = guidance_builder.build_guidance("standard")
177+
assert '<session_behavior_protocol level="standard">' in content
178+
assert "</session_behavior_protocol>" in content
184179

185-
def test_detailed_is_valid_xml(self, guidance_builder: GuidanceBuilder) -> None:
186-
"""Test that detailed output is valid XML."""
187-
xml = guidance_builder.build_guidance("detailed")
188-
root = ET.fromstring(xml) # noqa: S314
189-
assert root.tag == "session_behavior_protocol"
190-
assert root.attrib.get("level") == "detailed"
180+
def test_detailed_has_protocol_tag(self, guidance_builder: GuidanceBuilder) -> None:
181+
"""Test that detailed output has session_behavior_protocol structure."""
182+
content = guidance_builder.build_guidance("detailed")
183+
assert '<session_behavior_protocol level="detailed">' in content
184+
assert "</session_behavior_protocol>" in content
191185

192186
def test_detailed_has_mandatory_rules(
193187
self, guidance_builder: GuidanceBuilder
194188
) -> None:
195-
"""Test that detailed guidance has mandatory_rules element."""
196-
xml = guidance_builder.build_guidance("detailed")
197-
root = ET.fromstring(xml) # noqa: S314
198-
rules = root.find("mandatory_rules")
199-
assert rules is not None
200-
# Rules should contain the markdown content
201-
assert rules.text is not None
202-
assert "MUST" in rules.text
189+
"""Test that detailed guidance has mandatory_rules section."""
190+
content = guidance_builder.build_guidance("detailed")
191+
assert "<mandatory_rules>" in content
192+
assert "</mandatory_rules>" in content
193+
# Rules should contain MUST requirements
194+
assert "MUST" in content
203195

204196

205197
class TestGuidanceBuilderTokenEstimation:
@@ -328,15 +320,15 @@ def test_whitespace_level_raises(self, guidance_builder: GuidanceBuilder) -> Non
328320
with pytest.raises(ValueError):
329321
guidance_builder.build_guidance(" ")
330322

331-
def test_special_chars_in_templates_escaped(
323+
def test_special_chars_in_templates_preserved(
332324
self, guidance_builder: GuidanceBuilder
333325
) -> None:
334-
"""Test that templates with special chars are still valid XML via CDATA."""
335-
xml = guidance_builder.build_guidance("detailed")
336-
# Templates contain markdown with special chars wrapped in CDATA
337-
# Let's verify the XML is still valid (S314 safe: parsing our own generated XML)
338-
root = ET.fromstring(xml) # noqa: S314
339-
assert root is not None
326+
"""Test that templates preserve markdown formatting chars."""
327+
content = guidance_builder.build_guidance("detailed")
328+
# Templates contain markdown formatting that should be preserved
329+
assert "**" in content # Bold markers
330+
assert "`" in content # Code markers
331+
assert content # Non-empty content
340332

341333
def test_multiple_builds_are_independent(
342334
self, guidance_builder: GuidanceBuilder

0 commit comments

Comments
 (0)