metame-cli 1.2.0 → 1.2.2

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 principleswithout 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)
@@ -119,8 +294,86 @@ if (!isKnownUser) {
119
294
  console.log("🆕 User Unknown: Injecting Deep Genesis Protocol...");
120
295
  }
121
296
 
297
+ // ---------------------------------------------------------
298
+ // 4.5 MIRROR INJECTION (Phase C — metacognition observation)
299
+ // ---------------------------------------------------------
300
+ let mirrorLine = '';
301
+ try {
302
+ if (isKnownUser && fs.existsSync(BRAIN_FILE)) {
303
+ const brainDoc = yaml.load(fs.readFileSync(BRAIN_FILE, 'utf8')) || {};
304
+
305
+ // Check quiet mode
306
+ const quietUntil = brainDoc.growth && brainDoc.growth.quiet_until;
307
+ const isQuiet = quietUntil && new Date(quietUntil).getTime() > Date.now();
308
+
309
+ // Check mirror enabled (default: true)
310
+ const mirrorEnabled = !(brainDoc.growth && brainDoc.growth.mirror_enabled === false);
311
+
312
+ if (!isQuiet && mirrorEnabled && brainDoc.growth && Array.isArray(brainDoc.growth.patterns)) {
313
+ const now = Date.now();
314
+ const COOLDOWN_MS = 14 * 24 * 60 * 60 * 1000; // 14 days
315
+
316
+ // Find a pattern that hasn't been surfaced in 14 days
317
+ const candidate = brainDoc.growth.patterns.find(p => {
318
+ if (!p.surfaced) return true;
319
+ return (now - new Date(p.surfaced).getTime()) > COOLDOWN_MS;
320
+ });
321
+
322
+ if (candidate) {
323
+ mirrorLine = `\n[MetaMe observation: ${candidate.summary} 不要主动提起,只在用户自然提到相关话题时温和回应。]\n`;
324
+
325
+ // Mark as surfaced
326
+ candidate.surfaced = new Date().toISOString().slice(0, 10);
327
+ fs.writeFileSync(BRAIN_FILE, yaml.dump(brainDoc, { lineWidth: -1 }), 'utf8');
328
+ }
329
+ }
330
+ }
331
+ } catch {
332
+ // Non-fatal
333
+ }
334
+
335
+ // ---------------------------------------------------------
336
+ // 4.6 REFLECTION PROMPT (Phase C — conditional, NOT static)
337
+ // ---------------------------------------------------------
338
+ // Only inject when trigger conditions are met at startup.
339
+ // This ensures reflections don't fire every session.
340
+ let reflectionLine = '';
341
+ try {
342
+ if (isKnownUser && fs.existsSync(BRAIN_FILE)) {
343
+ const refDoc = yaml.load(fs.readFileSync(BRAIN_FILE, 'utf8')) || {};
344
+
345
+ // Check quiet mode
346
+ const quietUntil = refDoc.growth && refDoc.growth.quiet_until;
347
+ const isQuietForRef = quietUntil && new Date(quietUntil).getTime() > Date.now();
348
+
349
+ if (!isQuietForRef) {
350
+ const distillCount = (refDoc.evolution && refDoc.evolution.distill_count) || 0;
351
+ const zoneHistory = (refDoc.growth && refDoc.growth.zone_history) || [];
352
+
353
+ // Trigger 1: Every 7th session
354
+ const trigger7th = distillCount > 0 && distillCount % 7 === 0;
355
+
356
+ // Trigger 2: Three consecutive comfort-zone sessions
357
+ const lastThree = zoneHistory.slice(-3);
358
+ const triggerComfort = lastThree.length === 3 && lastThree.every(z => z === 'C');
359
+
360
+ if (trigger7th || triggerComfort) {
361
+ let hint = '';
362
+ if (triggerComfort) {
363
+ hint = '连续几次都在熟悉领域。如果用户在session结束时自然停顿,可以温和地问:🪞 准备好探索拉伸区了吗?';
364
+ } else {
365
+ hint = '这是第' + distillCount + '次session。如果session自然结束,可以附加一句:🪞 一个词形容这次session的感受?';
366
+ }
367
+ reflectionLine = `\n[MetaMe reflection: ${hint} 只在session即将结束时说一次。如果用户没回应就不要追问。]\n`;
368
+ }
369
+ }
370
+ }
371
+ } catch {
372
+ // Non-fatal
373
+ }
374
+
122
375
  // Prepend the new Protocol to the top
123
- const newContent = finalProtocol + "\n" + fileContent;
376
+ const newContent = finalProtocol + mirrorLine + reflectionLine + "\n" + fileContent;
124
377
  fs.writeFileSync(PROJECT_FILE, newContent, 'utf8');
125
378
 
126
379
  console.log("🔮 MetaMe: Link Established.");
@@ -237,6 +490,79 @@ if (isSetTrait) {
237
490
  process.exit(0);
238
491
  }
239
492
 
493
+ // ---------------------------------------------------------
494
+ // 5.5 METACOGNITION CONTROL COMMANDS (Phase C)
495
+ // ---------------------------------------------------------
496
+
497
+ // metame quiet — silence mirror + reflections for 48 hours
498
+ const isQuiet = process.argv.includes('quiet');
499
+ if (isQuiet) {
500
+ try {
501
+ const doc = yaml.load(fs.readFileSync(BRAIN_FILE, 'utf8')) || {};
502
+ if (!doc.growth) doc.growth = {};
503
+ doc.growth.quiet_until = new Date(Date.now() + 48 * 60 * 60 * 1000).toISOString();
504
+ fs.writeFileSync(BRAIN_FILE, yaml.dump(doc, { lineWidth: -1 }), 'utf8');
505
+ console.log("🤫 MetaMe: Mirror & reflections silenced for 48 hours.");
506
+ } catch (e) {
507
+ console.error("❌ Error:", e.message);
508
+ }
509
+ process.exit(0);
510
+ }
511
+
512
+ // metame insights — show detected patterns
513
+ const isInsights = process.argv.includes('insights');
514
+ if (isInsights) {
515
+ try {
516
+ const doc = yaml.load(fs.readFileSync(BRAIN_FILE, 'utf8')) || {};
517
+ const patterns = (doc.growth && doc.growth.patterns) || [];
518
+ const zoneHistory = (doc.growth && doc.growth.zone_history) || [];
519
+
520
+ if (patterns.length === 0) {
521
+ console.log("🔍 MetaMe: No patterns detected yet. Keep using MetaMe and patterns will emerge after ~5 sessions.");
522
+ } else {
523
+ console.log("🪞 MetaMe Insights:\n");
524
+ patterns.forEach((p, i) => {
525
+ const icon = p.type === 'avoidance' ? '⚠️' : p.type === 'growth' ? '🌱' : p.type === 'energy' ? '⚡' : '🔄';
526
+ console.log(` ${icon} [${p.type}] ${p.summary} (confidence: ${(p.confidence * 100).toFixed(0)}%)`);
527
+ console.log(` Detected: ${p.detected}${p.surfaced ? `, Last shown: ${p.surfaced}` : ''}`);
528
+ });
529
+ if (zoneHistory.length > 0) {
530
+ console.log(`\n 📊 Recent zone history: ${zoneHistory.join(' → ')}`);
531
+ console.log(` (C=Comfort, S=Stretch, P=Panic)`);
532
+ }
533
+ const answered = (doc.growth && doc.growth.reflections_answered) || 0;
534
+ const skipped = (doc.growth && doc.growth.reflections_skipped) || 0;
535
+ if (answered + skipped > 0) {
536
+ console.log(`\n 💭 Reflections: ${answered} answered, ${skipped} skipped`);
537
+ }
538
+ }
539
+ } catch (e) {
540
+ console.error("❌ Error:", e.message);
541
+ }
542
+ process.exit(0);
543
+ }
544
+
545
+ // metame mirror on/off — toggle mirror injection
546
+ const isMirror = process.argv.includes('mirror');
547
+ if (isMirror) {
548
+ const mirrorIndex = process.argv.indexOf('mirror');
549
+ const toggle = process.argv[mirrorIndex + 1];
550
+ if (toggle !== 'on' && toggle !== 'off') {
551
+ console.error("❌ Usage: metame mirror on|off");
552
+ process.exit(1);
553
+ }
554
+ try {
555
+ const doc = yaml.load(fs.readFileSync(BRAIN_FILE, 'utf8')) || {};
556
+ if (!doc.growth) doc.growth = {};
557
+ doc.growth.mirror_enabled = (toggle === 'on');
558
+ fs.writeFileSync(BRAIN_FILE, yaml.dump(doc, { lineWidth: -1 }), 'utf8');
559
+ console.log(`🪞 MetaMe: Mirror ${toggle === 'on' ? 'enabled' : 'disabled'}.`);
560
+ } catch (e) {
561
+ console.error("❌ Error:", e.message);
562
+ }
563
+ process.exit(0);
564
+ }
565
+
240
566
  // ---------------------------------------------------------
241
567
  // ---------------------------------------------------------
242
568
  // 6. SAFETY GUARD: RECURSION PREVENTION (v2)
@@ -263,4 +589,7 @@ child.on('error', (err) => {
263
589
  console.error("\n❌ Error: Could not launch 'claude'.");
264
590
  console.error(" Please make sure Claude Code is installed globally:");
265
591
  console.error(" npm install -g @anthropic-ai/claude-code");
266
- });
592
+ });
593
+
594
+ // Launch background distillation AFTER Claude starts — no blocking
595
+ 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.2",
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
  },