metame-cli 1.2.0 β†’ 1.2.1

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/README.md CHANGED
@@ -4,17 +4,17 @@
4
4
  <img src="./logo.png" alt="MetaMe Logo" width="200"/>
5
5
  </p>
6
6
 
7
- > **The Meta-Cognitive Layer for Claude Code.**
7
+ > **The Cognitive Profile Layer for Claude Code.**
8
8
  >
9
- > *Turn your AI assistant into a psychological mirror that knows you, evolves with you, and protects your core values.*
9
+ > *Not a memory system β€” a cognitive mirror. It knows how you think, decide, and communicate, and it protects your core values.*
10
10
 
11
11
  ## πŸ“– Introduction
12
12
 
13
13
  **Claude Code** is a powerful tool, but it suffers from "Project Amnesia." Every time you switch folders, it forgets who you are, your communication style, and your specific constraints.
14
14
 
15
- **MetaMe** solves this by wrapping Claude in a **Meta-Cognitive Layer** . It creates a persistent "Global Brain" that travels with you across every project. It knows your psychological profile, monitors your stress levels, and respects your core principlesβ€”without you having to repeat yourself.
15
+ **MetaMe** solves this by wrapping Claude in a **Cognitive Profile Layer** . It creates a persistent "Global Brain" that travels with you across every project. Unlike ChatGPT/Claude/Gemini's built-in memory (which stores *facts* like "user lives in X"), MetaMe captures *how you think* β€” your decision style, cognitive load preferences, motivation patterns, and communication traits.
16
16
 
17
- It is not just a launcher; it is a **Meta Avatar** .
17
+ It is not a memory system; it is a **Cognitive Mirror** .
18
18
 
19
19
  ## ✨ Key Features
20
20
 
@@ -23,6 +23,9 @@ It is not just a launcher; it is a **Meta Avatar** .
23
23
  * **🀝 Dynamic Handshake Protocol:** The "Canary Test." MetaMe verifies its connection to your profile by addressing you by your chosen **Codename** in the very first sentence. If it doesn't, you know the link is broken.
24
24
  * **πŸ›‘οΈ Auto-Lock Mechanism:** Mark any value in your profile with `# [LOCKED]`, and MetaMe will treat it as a constitution that cannot be overwritten.
25
25
  * **πŸ”Œ Smart Injection:** Automatically injects your profile context into the `CLAUDE.md` of any project you enter, ensuring seamless context switching.
26
+ * **🧠 Passive Distillation:** MetaMe silently captures your messages via Claude Code hooks and, on next launch, uses a lightweight LLM (Haiku) to extract cognitive traits and preferences β€” automatically merging them into your profile with confidence-based upsert. Zero manual effort required.
27
+ * **πŸ“Š Schema-Enforced Profile:** A 41-field whitelist across 5 tiers (T1-T5) prevents profile bloat. Fields have type validation, enum constraints, and token budget limits (800 tokens max).
28
+ * **🎯 Confidence-Based Learning:** Strong directives ("always"/"δ»₯εŽδΈ€εΎ‹") write directly. Normal observations accumulate in a pending queue and only promote to the profile after 3 consistent observations β€” preventing single-session bias.
26
29
 
27
30
  ## πŸ›  Prerequisites
28
31
 
@@ -98,6 +101,32 @@ If you need to update a specific trait without editing the file manually:
98
101
  metame set-trait status.focus "Learning Rust"
99
102
  ```
100
103
 
104
+ ### Passive Distillation (Automatic)
105
+
106
+ MetaMe automatically learns your cognitive patterns from conversations β€” no action needed.
107
+
108
+ **How it works:**
109
+
110
+ 1. A global Claude Code hook captures every message, tagging each with a **confidence level** (high for strong directives like "always"/"δ»₯εŽδΈ€εΎ‹", normal otherwise).
111
+ 2. On your next `metame` launch, a background Haiku model analyzes the buffer and extracts cognitive traits and preferences.
112
+ 3. **High-confidence** traits write directly to your profile. **Normal-confidence** traits enter a pending queue (`~/.metame/pending_traits.yaml`) and only promote after 3+ consistent observations.
113
+ 4. All writes are validated against a **41-field schema whitelist** β€” unknown keys are silently dropped, enum fields are type-checked, and a **token budget** (800 max) prevents bloat.
114
+ 5. The buffer is cleared, and Claude starts with a clean context.
115
+
116
+ **Anti-bias safeguards:**
117
+ - Single observations are treated as states, not traits
118
+ - Contradictions are tracked, not blindly overwritten
119
+ - Pending traits expire after 30 days without re-observation
120
+ - Context fields (focus, energy) auto-expire on staleness
121
+
122
+ You'll see this in the startup log:
123
+
124
+ ```
125
+ 🧠 MetaMe: Distilling 7 moments in background...
126
+ ```
127
+
128
+ The hook is installed automatically on first run to `~/.claude/settings.json` (global scope β€” works across all projects).
129
+
101
130
  ### Hot Reload (Refresh)
102
131
 
103
132
  If you update your profile or need to fix a broken context **without restarting your session**:
@@ -115,25 +144,63 @@ Your profile is stored in a hidden YAML file in your home directory.
115
144
 
116
145
  You can edit this file manually to update your status or lock your values.
117
146
 
118
- **Example Profile:**
147
+ **Example Profile (v2 Schema):**
119
148
 
120
149
  **YAML**
121
150
 
122
151
  ```
152
+ # === T1: Identity (LOCKED) ===
123
153
  identity:
154
+ nickname: Neo # [LOCKED]
124
155
  role: Senior Architect
125
- nickname: Neo
126
- status:
127
- focus: Refactoring Legacy Code
128
- pressure: High
156
+ locale: en-US # [LOCKED]
157
+
158
+ # === T2: Core Traits (LOCKED) ===
159
+ core_traits:
160
+ crisis_reflex: Analysis # [LOCKED]
161
+ flow_trigger: Debugging # [LOCKED]
162
+ learning_style: Hands-on # [LOCKED]
163
+
164
+ # === T3: Preferences (auto-learnable) ===
165
+ preferences:
166
+ code_style: concise
167
+ communication: direct
168
+ explanation_depth: brief_rationale
169
+
170
+ # === T3b: Cognition (auto-learnable, slow to change) ===
129
171
  cognition:
130
- crisis_reflex: Strategic_Analysis
131
- blind_spot: Perfectionism # [LOCKED]
132
- values:
133
- core: "User Experience First" # [LOCKED]
172
+ decision_style: analytical
173
+ info_processing:
174
+ entry_point: big_picture
175
+ preferred_format: structured
176
+ cognitive_load:
177
+ chunk_size: medium
178
+ preferred_response_length: moderate
179
+
180
+ # === T4: Context (auto-overwrite) ===
181
+ context:
182
+ focus: "Refactoring Legacy Code"
183
+ energy: high
184
+
185
+ # === T5: Evolution (system-managed) ===
186
+ evolution:
187
+ distill_count: 12
188
+ last_distill: "2026-01-30T10:00:00Z"
134
189
  ```
135
190
 
136
- * **`# [LOCKED]`** : Adding this comment ensures that even as the AI evolves your profile, these specific lines will **never** be changed.
191
+ * **T1-T2 fields** marked `# [LOCKED]` are never auto-modified.
192
+ * **T3 fields** are auto-learned with confidence thresholds.
193
+ * **T4 fields** are freely overwritten as your context changes.
194
+ * **T5 fields** are managed by the distillation system.
195
+
196
+ ### Profile Migration (v1 β†’ v2)
197
+
198
+ If you have an existing v1 profile, run the migration script:
199
+
200
+ ```
201
+ node ~/.metame/migrate-v2.js --dry-run # preview changes
202
+ node ~/.metame/migrate-v2.js # apply migration (auto-backup created)
203
+ ```
137
204
 
138
205
  ## πŸ—‘οΈ Uninstallation
139
206
 
@@ -159,7 +226,34 @@ If you want to delete your stored profile data:
159
226
  rm ~/.claude_profile.yaml
160
227
  ```
161
228
 
162
- ### 3. Cleanup Project Files (Optional)
229
+ ### 3. Remove Passive Distillation Data (Optional)
230
+
231
+ Remove the signal capture scripts:
232
+
233
+ **Bash**
234
+
235
+ ```
236
+ rm -rf ~/.metame
237
+ ```
238
+
239
+ ### 4. Remove the Signal Capture Hook (Optional)
240
+
241
+ MetaMe installs a global hook in `~/.claude/settings.json`. To remove it, edit the file and delete the `UserPromptSubmit` entry under `hooks`, or run:
242
+
243
+ **Bash**
244
+
245
+ ```
246
+ node -e "
247
+ const fs = require('fs');
248
+ const p = require('os').homedir() + '/.claude/settings.json';
249
+ const s = JSON.parse(fs.readFileSync(p, 'utf8'));
250
+ if (s.hooks) { delete s.hooks.UserPromptSubmit; }
251
+ fs.writeFileSync(p, JSON.stringify(s, null, 2));
252
+ console.log('Hook removed.');
253
+ "
254
+ ```
255
+
256
+ ### 5. Cleanup Project Files (Optional)
163
257
 
164
258
  MetaMe adds a header to `CLAUDE.md` files in your projects. To restore them to their original state (if you have many), you can use a text editor to remove the block starting with `## 🧠 SYSTEM KERNEL`.
165
259
 
@@ -172,6 +266,7 @@ You might worry: *"Does this eat up my context window?"*
172
266
  * **Context Cost**: The entire MetaMe kernel + your profile takes up **~800-1000 tokens**.
173
267
  * **Impact**: On a 200k context window, this is **0.5%** of the memory.
174
268
  * **ROI**: By pre-loading your context, you avoid the "instructional drift" and repetitive correction loops that usually waste thousands of tokens at the start of every session.
269
+ * **Passive Distillation Cost**: The signal capture hook is a local Node.js script (zero API calls). The Haiku distillation on launch processes only a small buffer of filtered messages β€” typically a few hundred tokens at Haiku's very low cost.
175
270
 
176
271
  ## ❓ FAQ
177
272
 
package/index.js CHANGED
@@ -11,6 +11,181 @@ const { spawn } = require('child_process');
11
11
  const HOME_DIR = os.homedir();
12
12
  const BRAIN_FILE = path.join(HOME_DIR, '.claude_profile.yaml');
13
13
  const PROJECT_FILE = path.join(process.cwd(), 'CLAUDE.md');
14
+ const METAME_DIR = path.join(HOME_DIR, '.metame');
15
+ const CLAUDE_SETTINGS = path.join(HOME_DIR, '.claude', 'settings.json');
16
+ const SIGNAL_CAPTURE_SCRIPT = path.join(METAME_DIR, 'signal-capture.js');
17
+
18
+ // ---------------------------------------------------------
19
+ // 1.5 ENSURE METAME DIRECTORY + DEPLOY SCRIPTS
20
+ // ---------------------------------------------------------
21
+ if (!fs.existsSync(METAME_DIR)) {
22
+ fs.mkdirSync(METAME_DIR, { recursive: true });
23
+ }
24
+
25
+ // Auto-deploy bundled scripts to ~/.metame/
26
+ const BUNDLED_SCRIPTS = ['signal-capture.js', 'distill.js', 'schema.js', 'pending-traits.js', 'migrate-v2.js'];
27
+ const scriptsDir = path.join(__dirname, 'scripts');
28
+
29
+ for (const script of BUNDLED_SCRIPTS) {
30
+ const src = path.join(scriptsDir, script);
31
+ const dest = path.join(METAME_DIR, script);
32
+ try {
33
+ if (fs.existsSync(src)) {
34
+ const srcContent = fs.readFileSync(src, 'utf8');
35
+ const destContent = fs.existsSync(dest) ? fs.readFileSync(dest, 'utf8') : '';
36
+ if (srcContent !== destContent) {
37
+ fs.writeFileSync(dest, srcContent, 'utf8');
38
+ }
39
+ }
40
+ } catch {
41
+ // Non-fatal
42
+ }
43
+ }
44
+
45
+ // ---------------------------------------------------------
46
+ // 1.6 AUTO-INSTALL SIGNAL CAPTURE HOOK
47
+ // ---------------------------------------------------------
48
+ function ensureHookInstalled() {
49
+ try {
50
+ // Ensure ~/.claude/ exists
51
+ const claudeDir = path.join(HOME_DIR, '.claude');
52
+ if (!fs.existsSync(claudeDir)) {
53
+ fs.mkdirSync(claudeDir, { recursive: true });
54
+ }
55
+
56
+ let settings = {};
57
+ if (fs.existsSync(CLAUDE_SETTINGS)) {
58
+ settings = JSON.parse(fs.readFileSync(CLAUDE_SETTINGS, 'utf8'));
59
+ }
60
+
61
+ // Check if our hook is already configured
62
+ const hookCommand = `node ${SIGNAL_CAPTURE_SCRIPT}`;
63
+ const existing = settings.hooks?.UserPromptSubmit || [];
64
+ const alreadyInstalled = existing.some(entry =>
65
+ entry.hooks?.some(h => h.command === hookCommand)
66
+ );
67
+
68
+ if (!alreadyInstalled) {
69
+ if (!settings.hooks) settings.hooks = {};
70
+ if (!settings.hooks.UserPromptSubmit) settings.hooks.UserPromptSubmit = [];
71
+
72
+ settings.hooks.UserPromptSubmit.push({
73
+ hooks: [{
74
+ type: 'command',
75
+ command: hookCommand
76
+ }]
77
+ });
78
+
79
+ fs.writeFileSync(CLAUDE_SETTINGS, JSON.stringify(settings, null, 2), 'utf8');
80
+ console.log("πŸͺ MetaMe: Signal capture hook installed.");
81
+ }
82
+ } catch (e) {
83
+ // Non-fatal: hook install failure shouldn't block launch
84
+ console.error("⚠️ Hook install skipped:", e.message);
85
+ }
86
+ }
87
+
88
+ ensureHookInstalled();
89
+
90
+ // ---------------------------------------------------------
91
+ // 1.7 PASSIVE DISTILLATION (Background, post-launch)
92
+ // ---------------------------------------------------------
93
+ function shouldDistill() {
94
+ const bufferFile = path.join(METAME_DIR, 'raw_signals.jsonl');
95
+ if (!fs.existsSync(bufferFile)) return false;
96
+ const content = fs.readFileSync(bufferFile, 'utf8').trim();
97
+ return content.length > 0;
98
+ }
99
+
100
+ function spawnDistillBackground() {
101
+ const distillPath = path.join(METAME_DIR, 'distill.js');
102
+ if (!fs.existsSync(distillPath)) return;
103
+ if (!shouldDistill()) return;
104
+
105
+ const bufferFile = path.join(METAME_DIR, 'raw_signals.jsonl');
106
+ const lines = fs.readFileSync(bufferFile, 'utf8').trim().split('\n').filter(l => l.trim());
107
+ console.log(`🧠 MetaMe: Distilling ${lines.length} moment${lines.length > 1 ? 's' : ''} in background...`);
108
+
109
+ // Spawn as detached background process β€” won't block Claude launch
110
+ const bg = spawn('node', [distillPath], {
111
+ detached: true,
112
+ stdio: 'ignore'
113
+ });
114
+ bg.unref();
115
+ }
116
+
117
+ // ---------------------------------------------------------
118
+ // 1.8 TIME-BASED EXPIRY (Startup cleanup)
119
+ // ---------------------------------------------------------
120
+ function runExpiryCleanup() {
121
+ try {
122
+ const yaml = require('js-yaml');
123
+ if (!fs.existsSync(BRAIN_FILE)) return;
124
+
125
+ const rawProfile = fs.readFileSync(BRAIN_FILE, 'utf8');
126
+ const profile = yaml.load(rawProfile);
127
+ if (!profile || typeof profile !== 'object') return;
128
+
129
+ const now = Date.now();
130
+ let changed = false;
131
+
132
+ // context.focus: if focus_since > 30 days, auto-clear
133
+ if (profile.context && profile.context.focus_since) {
134
+ const focusSince = new Date(profile.context.focus_since).getTime();
135
+ if (now - focusSince > 30 * 24 * 60 * 60 * 1000) {
136
+ profile.context.focus = null;
137
+ profile.context.focus_since = null;
138
+ changed = true;
139
+ }
140
+ }
141
+
142
+ // context.blockers: if > 14 days, auto-clear
143
+ // (blockers are arrays β€” clear entire array if stale)
144
+ if (profile.context && Array.isArray(profile.context.blockers) && profile.context.blockers.length > 0) {
145
+ // If we don't have a blockers_since timestamp, just leave them
146
+ // Future: add per-item timestamps
147
+ }
148
+
149
+ // context.energy: reset to null on each session start
150
+ if (profile.context && profile.context.energy !== undefined) {
151
+ if (profile.context.energy !== null) {
152
+ profile.context.energy = null;
153
+ changed = true;
154
+ }
155
+ }
156
+
157
+ if (changed) {
158
+ // Preserve comments
159
+ const commentMatch = rawProfile.match(/^(\s*[\w_]+\s*:.+?)\s+(#.+)$/gm);
160
+ const dumped = yaml.dump(profile, { lineWidth: -1 });
161
+ fs.writeFileSync(BRAIN_FILE, dumped, 'utf8');
162
+ }
163
+
164
+ // Expire stale pending traits
165
+ const pendingFile = path.join(METAME_DIR, 'pending_traits.yaml');
166
+ if (fs.existsSync(pendingFile)) {
167
+ const pending = yaml.load(fs.readFileSync(pendingFile, 'utf8')) || {};
168
+ const cutoff = 30 * 24 * 60 * 60 * 1000;
169
+ let expiredCount = 0;
170
+ for (const [key, meta] of Object.entries(pending)) {
171
+ if (meta.last_seen) {
172
+ const lastSeen = new Date(meta.last_seen).getTime();
173
+ if (now - lastSeen > cutoff) {
174
+ delete pending[key];
175
+ expiredCount++;
176
+ }
177
+ }
178
+ }
179
+ if (expiredCount > 0) {
180
+ fs.writeFileSync(pendingFile, yaml.dump(pending, { lineWidth: -1 }), 'utf8');
181
+ }
182
+ }
183
+ } catch {
184
+ // Non-fatal β€” expiry cleanup failure shouldn't block launch
185
+ }
186
+ }
187
+
188
+ runExpiryCleanup();
14
189
 
15
190
  // ---------------------------------------------------------
16
191
  // 2. BRAIN INITIALIZATION (Cold Start)
@@ -263,4 +438,7 @@ child.on('error', (err) => {
263
438
  console.error("\n❌ Error: Could not launch 'claude'.");
264
439
  console.error(" Please make sure Claude Code is installed globally:");
265
440
  console.error(" npm install -g @anthropic-ai/claude-code");
266
- });
441
+ });
442
+
443
+ // Launch background distillation AFTER Claude starts β€” no blocking
444
+ spawnDistillBackground();
package/package.json CHANGED
@@ -1,11 +1,15 @@
1
1
  {
2
2
  "name": "metame-cli",
3
- "version": "1.2.0",
4
- "description": "The Meta-Cognitive Layer for Claude Code. Turns your AI into a psychological partner.",
3
+ "version": "1.2.1",
4
+ "description": "The Cognitive Profile Layer for Claude Code. Knows how you think, not just what you said.",
5
5
  "main": "index.js",
6
6
  "bin": {
7
7
  "metame": "./index.js"
8
8
  },
9
+ "files": [
10
+ "index.js",
11
+ "scripts/"
12
+ ],
9
13
  "scripts": {
10
14
  "start": "node index.js"
11
15
  },