metame-cli 1.1.3 β†’ 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,20 +23,16 @@ 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
 
29
- MetaMe is a wrapper around **Claude Code** . You must have Node.js and the official Claude Code tool installed first.
32
+ MetaMe is a wrapper around **Claude Code**. You must have Node.js and the official Claude Code tool installed first.
30
33
 
31
- 1. **Node.js** : Version 14 or higher.
32
- 2. **Claude Code** :
33
- **Bash**
34
-
35
- ```
36
- npm install -g @anthropic-ai/claude-code
37
- ```
38
-
39
- 1. **Auth** : Ensure you have logged in via `claude login`.
34
+ 1. **Node.js**: Version 14 or higher.
35
+ 2. **Claude Code**: Ensure `claude` is available in your PATH and you are logged in.
40
36
 
41
37
  ## πŸ“¦ Installation
42
38
 
@@ -84,6 +80,53 @@ When you run MetaMe for the first time, it will detect that your profile is empt
84
80
  3. Claude will start and immediately say: *"Ready, [Your Name]..."*
85
81
  4. Start coding. MetaMe manages the context in the background.
86
82
 
83
+ ### Global Initialization (Reset/Interview)
84
+
85
+ If you want to restart the **Genesis Interview** to update your psychological profile:
86
+
87
+ **Bash**
88
+
89
+ ```
90
+ metame interview
91
+ ```
92
+ (Command to be implemented in v1.3 - currently you can manually edit `~/.claude_profile.yaml` or use `set-trait`)
93
+
94
+ ### Surgical Update (Manual Override)
95
+
96
+ If you need to update a specific trait without editing the file manually:
97
+
98
+ **Bash**
99
+
100
+ ```
101
+ metame set-trait status.focus "Learning Rust"
102
+ ```
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
+
87
130
  ### Hot Reload (Refresh)
88
131
 
89
132
  If you update your profile or need to fix a broken context **without restarting your session**:
@@ -101,25 +144,63 @@ Your profile is stored in a hidden YAML file in your home directory.
101
144
 
102
145
  You can edit this file manually to update your status or lock your values.
103
146
 
104
- **Example Profile:**
147
+ **Example Profile (v2 Schema):**
105
148
 
106
149
  **YAML**
107
150
 
108
151
  ```
152
+ # === T1: Identity (LOCKED) ===
109
153
  identity:
154
+ nickname: Neo # [LOCKED]
110
155
  role: Senior Architect
111
- nickname: Neo
112
- status:
113
- focus: Refactoring Legacy Code
114
- 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) ===
115
171
  cognition:
116
- crisis_reflex: Strategic_Analysis
117
- blind_spot: Perfectionism # [LOCKED]
118
- values:
119
- 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"
120
189
  ```
121
190
 
122
- * **`# [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
+ ```
123
204
 
124
205
  ## πŸ—‘οΈ Uninstallation
125
206
 
@@ -145,7 +226,34 @@ If you want to delete your stored profile data:
145
226
  rm ~/.claude_profile.yaml
146
227
  ```
147
228
 
148
- ### 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)
149
257
 
150
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`.
151
259
 
@@ -158,6 +266,7 @@ You might worry: *"Does this eat up my context window?"*
158
266
  * **Context Cost**: The entire MetaMe kernel + your profile takes up **~800-1000 tokens**.
159
267
  * **Impact**: On a 200k context window, this is **0.5%** of the memory.
160
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.
161
270
 
162
271
  ## ❓ FAQ
163
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)
@@ -48,8 +223,10 @@ const CORE_PROTOCOL = `
48
223
 
49
224
  **3. EVOLUTION MECHANISM (Manual Sync):**
50
225
  * **PHILOSOPHY:** You respect the User's flow. You do NOT interrupt.
51
- * **TOOL:** You have the capability to save insights using \`!metame evolve "Insight"\`.
52
- * **RULE:** Only use this tool when the User **EXPLICITLY** instructs you to "remember this", "save this preference", or "update my profile".
226
+ * **TOOLS:**
227
+ 1. **Log Insight:** \`!metame evolve "Insight"\` (For additive knowledge).
228
+ 2. **Surgical Update:** \`!metame set-trait key value\` (For overwriting specific fields, e.g., \`!metame set-trait status.focus "API Design"\`).
229
+ * **RULE:** Only use these tools when the User **EXPLICITLY** instructs you.
53
230
  * **REMINDER:** If the User expresses a strong persistent preference, you may gently ask *at the end of the task*: "Should I save this preference to your MetaMe profile?"
54
231
  ---
55
232
  `;
@@ -182,6 +359,59 @@ if (isEvolve) {
182
359
  process.exit(0);
183
360
  }
184
361
 
362
+ // Check for "set-trait" command (Surgical Update)
363
+ const isSetTrait = process.argv.includes('set-trait');
364
+
365
+ if (isSetTrait) {
366
+ const yaml = require('js-yaml');
367
+
368
+ // Syntax: metame set-trait <key> <value>
369
+ // Example: metame set-trait identity.role "Engineering Manager"
370
+
371
+ const setIndex = process.argv.indexOf('set-trait');
372
+ const key = process.argv[setIndex + 1];
373
+ // Join the rest as the value (allows spaces)
374
+ const value = process.argv.slice(setIndex + 2).join(' ').trim();
375
+
376
+ if (!key || !value) {
377
+ console.error("❌ Error: Missing key or value.");
378
+ console.error(" Usage: metame set-trait identity.role \"New Role\"");
379
+ process.exit(1);
380
+ }
381
+
382
+ try {
383
+ if (fs.existsSync(BRAIN_FILE)) {
384
+ const rawContent = fs.readFileSync(BRAIN_FILE, 'utf8');
385
+ const doc = yaml.load(rawContent) || {};
386
+
387
+ // Helper to set nested property
388
+ const setNested = (obj, path, val) => {
389
+ const keys = path.split('.');
390
+ let current = obj;
391
+ for (let i = 0; i < keys.length - 1; i++) {
392
+ if (!current[keys[i]]) current[keys[i]] = {};
393
+ current = current[keys[i]];
394
+ }
395
+ current[keys[keys.length - 1]] = val;
396
+ };
397
+
398
+ // Set the value
399
+ setNested(doc, key, value);
400
+
401
+ fs.writeFileSync(BRAIN_FILE, yaml.dump(doc), 'utf8');
402
+
403
+ console.log(`🧠 MetaMe Brain Surgically Updated.`);
404
+ console.log(` Set \`${key}\` = "${value}"`);
405
+ console.log(" (Run 'metame refresh' to apply this to the current session)");
406
+ } else {
407
+ console.error("❌ Error: No profile found.");
408
+ }
409
+ } catch (e) {
410
+ console.error("❌ Error updating profile:", e.message);
411
+ }
412
+ process.exit(0);
413
+ }
414
+
185
415
  // ---------------------------------------------------------
186
416
  // ---------------------------------------------------------
187
417
  // 6. SAFETY GUARD: RECURSION PREVENTION (v2)
@@ -208,4 +438,7 @@ child.on('error', (err) => {
208
438
  console.error("\n❌ Error: Could not launch 'claude'.");
209
439
  console.error(" Please make sure Claude Code is installed globally:");
210
440
  console.error(" npm install -g @anthropic-ai/claude-code");
211
- });
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.1.3",
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
  },