mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-28 09:23:08 +08:00
feat(memorycore): add tags system, session summaries, hook injection, tag filtering, and solidify compress mode
Implement 5 interconnected memorycore enhancements: 1. Tags backend: add tags TEXT column to memories table with migration, JSON array storage, full CRUD support via upsertMemory/getMemory/getMemories 2. LLM auto-tag extraction: extend extraction prompt to produce tags, parse and validate in pipeline, create CMEM from extraction results 3. Session summary API: expose rollout_summary via new REST endpoints GET /api/core-memory/sessions/summaries and sessions/:id/summary 4. Hook injection: increase SESSION_START_LIMIT to 1500, add Component 5 (Recent Sessions) to UnifiedContextBuilder with 300-char budget 5. Tag filtering: add getMemoriesByTags() with json_each() for safe SQL matching, wire through MCP tool, CLI --tags flag, REST ?tags= param 6. Solidify compress mode: add --type compress to solidify.md with getRecentMemories(), archiveMemories(), buildCompressionMetadata() Security fixes: safeParseTags() for corrupt DB data, json_each() instead of LIKE injection, ESCAPE clause for searchSessionsByKeyword, singleton store in unified-context-builder.
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
---
|
||||
name: solidify
|
||||
description: Crystallize session learnings and user-defined constraints into permanent project guidelines
|
||||
argument-hint: "[-y|--yes] [--type <convention|constraint|learning>] [--category <category>] \"rule or insight\""
|
||||
description: Crystallize session learnings and user-defined constraints into permanent project guidelines, or compress recent memories
|
||||
argument-hint: "[-y|--yes] [--type <convention|constraint|learning|compress>] [--category <category>] [--limit <N>] \"rule or insight\""
|
||||
examples:
|
||||
- /workflow:session:solidify "Use functional components for all React code" --type convention
|
||||
- /workflow:session:solidify -y "No direct DB access from controllers" --type constraint --category architecture
|
||||
- /workflow:session:solidify "Cache invalidation requires event sourcing" --type learning --category architecture
|
||||
- /workflow:session:solidify --interactive
|
||||
- /workflow:session:solidify --type compress --limit 10
|
||||
---
|
||||
|
||||
## Auto Mode
|
||||
@@ -29,10 +30,11 @@ Crystallizes ephemeral session context (insights, decisions, constraints) into p
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `rule` | string | ✅ (unless --interactive) | The rule, convention, or insight to solidify |
|
||||
| `--type` | enum | ❌ | Type: `convention`, `constraint`, `learning` (default: auto-detect) |
|
||||
| `--category` | string | ❌ | Category for organization (see categories below) |
|
||||
| `--interactive` | flag | ❌ | Launch guided wizard for adding rules |
|
||||
| `rule` | string | Yes (unless --interactive or --type compress) | The rule, convention, or insight to solidify |
|
||||
| `--type` | enum | No | Type: `convention`, `constraint`, `learning`, `compress` (default: auto-detect) |
|
||||
| `--category` | string | No | Category for organization (see categories below) |
|
||||
| `--interactive` | flag | No | Launch guided wizard for adding rules |
|
||||
| `--limit` | number | No | Number of recent memories to compress (default: 20, only for --type compress) |
|
||||
|
||||
### Type Categories
|
||||
|
||||
@@ -42,32 +44,68 @@ Crystallizes ephemeral session context (insights, decisions, constraints) into p
|
||||
**constraint** → Hard rules that must not be violated (goes to `constraints` section)
|
||||
- Subcategories: `architecture`, `tech_stack`, `performance`, `security`
|
||||
|
||||
**learning** → Session-specific insights (goes to `learnings` array)
|
||||
**learning** -> Session-specific insights (goes to `learnings` array)
|
||||
- Subcategories: `architecture`, `performance`, `security`, `testing`, `process`, `other`
|
||||
|
||||
**compress** -> Compress/deduplicate recent memories into a single consolidated CMEM
|
||||
- No subcategories (operates on core memories, not project guidelines)
|
||||
- Fetches recent non-archived memories, LLM-compresses them, creates a new CMEM
|
||||
- Source memories are archived after successful compression
|
||||
|
||||
## Execution Process
|
||||
|
||||
```
|
||||
Input Parsing:
|
||||
├─ Parse: rule text (required unless --interactive)
|
||||
├─ Parse: --type (convention|constraint|learning)
|
||||
├─ Parse: --category (subcategory)
|
||||
└─ Parse: --interactive (flag)
|
||||
|- Parse: rule text (required unless --interactive or --type compress)
|
||||
|- Parse: --type (convention|constraint|learning|compress)
|
||||
|- Parse: --category (subcategory)
|
||||
|- Parse: --interactive (flag)
|
||||
+- Parse: --limit (number, default 20, compress only)
|
||||
|
||||
Step 1: Ensure Guidelines File Exists
|
||||
└─ If not exists → Create with empty structure
|
||||
IF --type compress:
|
||||
Step C1: Fetch Recent Memories
|
||||
+- Call getRecentMemories(limit, excludeArchived=true)
|
||||
|
||||
Step 2: Auto-detect Type (if not specified)
|
||||
└─ Analyze rule text for keywords
|
||||
Step C2: Validate Candidates
|
||||
+- If fewer than 2 memories found -> abort with message
|
||||
|
||||
Step 3: Validate and Format Entry
|
||||
└─ Build entry object based on type
|
||||
Step C3: LLM Compress
|
||||
+- Build compression prompt with all memory contents
|
||||
+- Send to LLM for consolidation
|
||||
+- Receive compressed text
|
||||
|
||||
Step 4: Update Guidelines File
|
||||
└─ Add entry to appropriate section
|
||||
Step C4: Merge Tags
|
||||
+- Collect tags from all source memories
|
||||
+- Deduplicate into a single merged tag array
|
||||
|
||||
Step 5: Display Confirmation
|
||||
└─ Show what was added and where
|
||||
Step C5: Create Compressed CMEM
|
||||
+- Generate new CMEM via upsertMemory with:
|
||||
- content: compressed text from LLM
|
||||
- summary: auto-generated
|
||||
- tags: merged deduplicated tags
|
||||
- metadata: buildCompressionMetadata(sourceIds, originalSize, compressedSize)
|
||||
|
||||
Step C6: Archive Source Memories
|
||||
+- Call archiveMemories(sourceIds)
|
||||
|
||||
Step C7: Display Compression Report
|
||||
+- Show source count, compression ratio, new CMEM ID
|
||||
|
||||
ELSE (convention/constraint/learning):
|
||||
Step 1: Ensure Guidelines File Exists
|
||||
+- If not exists -> Create with empty structure
|
||||
|
||||
Step 2: Auto-detect Type (if not specified)
|
||||
+- Analyze rule text for keywords
|
||||
|
||||
Step 3: Validate and Format Entry
|
||||
+- Build entry object based on type
|
||||
|
||||
Step 4: Update Guidelines File
|
||||
+- Add entry to appropriate section
|
||||
|
||||
Step 5: Display Confirmation
|
||||
+- Show what was added and where
|
||||
```
|
||||
|
||||
## Implementation
|
||||
@@ -197,17 +235,127 @@ Write('.workflow/project-guidelines.json', JSON.stringify(guidelines, null, 2));
|
||||
### Step 5: Display Confirmation
|
||||
|
||||
```
|
||||
✓ Guideline solidified
|
||||
Guideline solidified
|
||||
|
||||
Type: ${type}
|
||||
Category: ${category}
|
||||
Rule: "${rule}"
|
||||
|
||||
Location: .workflow/project-guidelines.json → ${type}s.${category}
|
||||
Location: .workflow/project-guidelines.json -> ${type}s.${category}
|
||||
|
||||
Total ${type}s in ${category}: ${count}
|
||||
```
|
||||
|
||||
## Compress Mode (--type compress)
|
||||
|
||||
When `--type compress` is specified, the command operates on core memories instead of project guidelines. It fetches recent memories, sends them to an LLM for consolidation, and creates a new compressed CMEM.
|
||||
|
||||
### Step C1: Fetch Recent Memories
|
||||
|
||||
```javascript
|
||||
// Uses CoreMemoryStore.getRecentMemories()
|
||||
const limit = parsedArgs.limit || 20;
|
||||
const recentMemories = store.getRecentMemories(limit, /* excludeArchived */ true);
|
||||
|
||||
if (recentMemories.length < 2) {
|
||||
console.log("Not enough non-archived memories to compress (need at least 2).");
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
### Step C2: Build Compression Prompt
|
||||
|
||||
Concatenate all memory contents and send to LLM with the following prompt:
|
||||
|
||||
```
|
||||
Given these ${N} memories, produce a single consolidated memory that:
|
||||
1. Preserves all key information and insights
|
||||
2. Removes redundancy and duplicate concepts
|
||||
3. Organizes content by theme/topic
|
||||
4. Maintains specific technical details and decisions
|
||||
|
||||
Source memories:
|
||||
---
|
||||
[Memory CMEM-XXXXXXXX-XXXXXX]:
|
||||
${memory.content}
|
||||
---
|
||||
[Memory CMEM-XXXXXXXX-XXXXXX]:
|
||||
${memory.content}
|
||||
---
|
||||
...
|
||||
|
||||
Output: A single comprehensive memory text.
|
||||
```
|
||||
|
||||
### Step C3: Merge Tags from Source Memories
|
||||
|
||||
```javascript
|
||||
// Collect all tags from source memories and deduplicate
|
||||
const allTags = new Set();
|
||||
for (const memory of recentMemories) {
|
||||
if (memory.tags) {
|
||||
for (const tag of memory.tags) {
|
||||
allTags.add(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
const mergedTags = Array.from(allTags);
|
||||
```
|
||||
|
||||
### Step C4: Create Compressed CMEM
|
||||
|
||||
```javascript
|
||||
const sourceIds = recentMemories.map(m => m.id);
|
||||
const originalSize = recentMemories.reduce((sum, m) => sum + m.content.length, 0);
|
||||
const compressedSize = compressedText.length;
|
||||
|
||||
const metadata = store.buildCompressionMetadata(sourceIds, originalSize, compressedSize);
|
||||
|
||||
const newMemory = store.upsertMemory({
|
||||
content: compressedText,
|
||||
summary: `Compressed from ${sourceIds.length} memories`,
|
||||
tags: mergedTags,
|
||||
metadata: metadata
|
||||
});
|
||||
```
|
||||
|
||||
### Step C5: Archive Source Memories
|
||||
|
||||
```javascript
|
||||
// Archive all source memories after successful compression
|
||||
store.archiveMemories(sourceIds);
|
||||
```
|
||||
|
||||
### Step C6: Display Compression Report
|
||||
|
||||
```
|
||||
Memory compression complete
|
||||
|
||||
New CMEM: ${newMemory.id}
|
||||
Sources compressed: ${sourceIds.length}
|
||||
Original size: ${originalSize} chars
|
||||
Compressed size: ${compressedSize} chars
|
||||
Compression ratio: ${(compressedSize / originalSize * 100).toFixed(1)}%
|
||||
Tags merged: ${mergedTags.join(', ') || '(none)'}
|
||||
Source memories archived: ${sourceIds.join(', ')}
|
||||
```
|
||||
|
||||
### Compressed CMEM Metadata Format
|
||||
|
||||
The compressed CMEM's `metadata` field contains a JSON string with:
|
||||
|
||||
```json
|
||||
{
|
||||
"compressed_from": ["CMEM-20260101-120000", "CMEM-20260102-140000", "..."],
|
||||
"compression_ratio": 0.45,
|
||||
"compressed_at": "2026-02-23T10:30:00.000Z"
|
||||
}
|
||||
```
|
||||
|
||||
- `compressed_from`: Array of source memory IDs that were consolidated
|
||||
- `compression_ratio`: Ratio of compressed size to original size (lower = more compression)
|
||||
- `compressed_at`: ISO timestamp of when the compression occurred
|
||||
|
||||
## Interactive Mode
|
||||
|
||||
When `--interactive` flag is provided:
|
||||
@@ -280,6 +428,20 @@ Result:
|
||||
}
|
||||
```
|
||||
|
||||
### Compress Recent Memories
|
||||
```bash
|
||||
/workflow:session:solidify --type compress --limit 10
|
||||
```
|
||||
|
||||
Result: Creates a new CMEM with consolidated content from the 10 most recent non-archived memories. Source memories are archived. The new CMEM's metadata tracks which memories were compressed:
|
||||
```json
|
||||
{
|
||||
"compressed_from": ["CMEM-20260220-100000", "CMEM-20260221-143000", "..."],
|
||||
"compression_ratio": 0.42,
|
||||
"compressed_at": "2026-02-23T10:30:00.000Z"
|
||||
}
|
||||
```
|
||||
|
||||
## Integration with Planning
|
||||
|
||||
The `project-guidelines.json` is consumed by:
|
||||
|
||||
Reference in New Issue
Block a user