metame-cli 1.3.1 โ 1.3.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 +5 -3
- package/index.js +2 -9
- package/package.json +1 -1
- package/scripts/daemon.js +15 -3
- package/scripts/distill.js +52 -1
- package/scripts/schema.js +2 -0
package/README.md
CHANGED
|
@@ -20,7 +20,6 @@ It is not a memory system; it is a **Cognitive Mirror** .
|
|
|
20
20
|
|
|
21
21
|
* **๐ง Global Brain (`~/.claude_profile.yaml`):** A single, portable source of truth โ your identity, cognitive traits, and preferences travel with you across every project.
|
|
22
22
|
* **๐งฌ Cognitive Evolution Engine:** MetaMe learns how you think through three channels: (1) **Passive** โ silently captures your messages and distills cognitive traits via Haiku on next launch; (2) **Manual** โ `!metame evolve` for explicit teaching; (3) **Confidence gates** โ strong directives ("always"/"ไปฅๅไธๅพ") write immediately, normal observations need 3+ consistent sightings before promotion. Schema-enforced (41 fields, 5 tiers, 800 token budget) to prevent bloat.
|
|
23
|
-
* **๐ค Dynamic Handshake:** The "Canary Test." Claude must address you by your **Codename** in the first sentence. If it doesn't, the link is broken.
|
|
24
23
|
* **๐ก๏ธ Auto-Lock:** Mark any value with `# [LOCKED]` โ treated as a constitution, never auto-modified.
|
|
25
24
|
* **๐ช Metacognition Layer (v1.3):** MetaMe now observes *how* you think, not just *what* you say. Behavioral pattern detection runs inside the existing Haiku distill call (zero extra cost). It tracks decision patterns, cognitive load, comfort zones, and avoidance topics across sessions. When persistent patterns emerge, MetaMe injects a one-line mirror observation โ e.g., *"You tend to avoid testing until forced"* โ with a 14-day cooldown per pattern. Conditional reflection prompts appear only when triggered (every 7th distill or 3x consecutive comfort zone). All injection logic runs in Node.js; Claude receives only pre-decided directives, never rules to self-evaluate.
|
|
26
25
|
* **๐ฑ Remote Claude Code (v1.3):** Full Claude Code from your phone via Telegram or Feishu (Lark). Stateful sessions with `--resume` โ same conversation history, tool use, and file editing as your terminal. Interactive buttons for project/session picking, directory browser, and macOS launchd auto-start.
|
|
@@ -107,6 +106,11 @@ metame set-trait status.focus "Learning Rust"
|
|
|
107
106
|
metame evolve "I prefer functional programming patterns"
|
|
108
107
|
```
|
|
109
108
|
|
|
109
|
+
**Episodic memory (keyframe, not full log):** MetaMe is not a memory system, but it captures two types of experiential "keyframes" that pure personality traits can't replace:
|
|
110
|
+
|
|
111
|
+
* **Anti-patterns** (`context.anti_patterns`, max 5): Cross-project failure lessons โ e.g., *"Promise.all rejects all on single failure, use Promise.allSettled"*. Auto-expires after 60 days. Prevents the AI from repeating the same mistakes across sessions.
|
|
112
|
+
* **Milestones** (`context.milestones`, max 3): Recent completed landmarks โ e.g., *"MetaMe v1.3 published"*. Provides continuity so Claude knows where you left off without you having to recap.
|
|
113
|
+
|
|
110
114
|
**Anti-bias safeguards:** single observations โ traits, contradictions are tracked not overwritten, pending traits expire after 30 days, context fields auto-clear on staleness.
|
|
111
115
|
|
|
112
116
|
**Metacognition controls:**
|
|
@@ -391,8 +395,6 @@ You might worry: *"Does this eat up my context window?"*
|
|
|
391
395
|
**Q: Does this replace `CLAUDE.md`?**
|
|
392
396
|
A: No. It *prepends* its meta-cognitive protocol to your existing `CLAUDE.md`. Your project-specific notes remain intact.
|
|
393
397
|
|
|
394
|
-
**Q: What if Claude stops calling me by my nickname?**
|
|
395
|
-
A: This is the "Canary Test." It means the context window has been compressed or the file link is broken. Run `/compact` in Claude or restart `metame` to fix it.
|
|
396
398
|
|
|
397
399
|
**Q: Is my data sent to a third party?**
|
|
398
400
|
A: No. Your profile stays local at `~/.claude_profile.yaml`. MetaMe simply passes text to the official Claude Code tool.
|
package/index.js
CHANGED
|
@@ -208,20 +208,13 @@ const CORE_PROTOCOL = `
|
|
|
208
208
|
---
|
|
209
209
|
## ๐ง SYSTEM KERNEL: SHADOW_MODE (Active)
|
|
210
210
|
|
|
211
|
-
**1. THE
|
|
212
|
-
* **SOURCE:** Check \`$HOME/.claude_profile.yaml\`.
|
|
213
|
-
* **RULE:** Look for \`identity.nickname\`.
|
|
214
|
-
* **IF FOUND:** You MUST address the user by that nickname in the very first sentence.
|
|
215
|
-
* **IF NULL/MISSING:** Address user as "User" and immediately trigger Genesis to fix it.
|
|
216
|
-
* **SIGNAL:** This nickname is the "Canary Test". If you omit it, the user assumes the system is broken.
|
|
217
|
-
|
|
218
|
-
**2. THE BRAIN (Source of Truth):**
|
|
211
|
+
**1. THE BRAIN (Source of Truth):**
|
|
219
212
|
* **FILE:** \`$HOME/.claude_profile.yaml\`
|
|
220
213
|
* **RULE:** This file is your Ground Truth. If context is compressed (/compact), **RE-READ** this file immediately.
|
|
221
214
|
* **AUTO-LOCK:** Never edit lines marked \`# [LOCKED]\` (Core Values).
|
|
222
215
|
* **EVOLUTION:** Silently overwrite outdated status/focus.
|
|
223
216
|
|
|
224
|
-
**
|
|
217
|
+
**2. EVOLUTION MECHANISM (Manual Sync):**
|
|
225
218
|
* **PHILOSOPHY:** You respect the User's flow. You do NOT interrupt.
|
|
226
219
|
* **TOOLS:**
|
|
227
220
|
1. **Log Insight:** \`!metame evolve "Insight"\` (For additive knowledge).
|
package/package.json
CHANGED
package/scripts/daemon.js
CHANGED
|
@@ -256,11 +256,12 @@ function executeTask(task, config) {
|
|
|
256
256
|
}
|
|
257
257
|
const fullPrompt = preamble + taskPrompt;
|
|
258
258
|
|
|
259
|
+
const allowedArgs = (task.allowedTools || []).map(t => `--allowedTools ${t}`).join(' ');
|
|
259
260
|
log('INFO', `Executing task: ${task.name} (model: ${model})`);
|
|
260
261
|
|
|
261
262
|
try {
|
|
262
263
|
const output = execSync(
|
|
263
|
-
`claude -p --model ${model}`,
|
|
264
|
+
`claude -p --model ${model}${allowedArgs ? ' ' + allowedArgs : ''}`,
|
|
264
265
|
{
|
|
265
266
|
input: fullPrompt,
|
|
266
267
|
encoding: 'utf8',
|
|
@@ -335,6 +336,7 @@ function executeWorkflow(task, config) {
|
|
|
335
336
|
const sessionId = crypto.randomUUID();
|
|
336
337
|
const outputs = [];
|
|
337
338
|
let totalTokens = 0;
|
|
339
|
+
const allowed = task.allowedTools || [];
|
|
338
340
|
|
|
339
341
|
log('INFO', `Workflow ${task.name}: ${steps.length} steps, session ${sessionId.slice(0, 8)}`);
|
|
340
342
|
|
|
@@ -343,6 +345,7 @@ function executeWorkflow(task, config) {
|
|
|
343
345
|
let prompt = (step.skill ? `/${step.skill} ` : '') + (step.prompt || '');
|
|
344
346
|
if (i === 0 && precheck.context) prompt += `\n\n็ธๅ
ณๆฐๆฎ:\n\`\`\`\n${precheck.context}\n\`\`\``;
|
|
345
347
|
const args = ['-p', '--model', model];
|
|
348
|
+
for (const tool of allowed) args.push('--allowedTools', tool);
|
|
346
349
|
args.push(i === 0 ? '--session-id' : '--resume', sessionId);
|
|
347
350
|
|
|
348
351
|
log('INFO', `Workflow ${task.name} step ${i + 1}/${steps.length}: ${step.skill || 'prompt'}`);
|
|
@@ -943,6 +946,9 @@ async function askClaude(bot, chatId, prompt) {
|
|
|
943
946
|
|
|
944
947
|
// Build claude command
|
|
945
948
|
const args = ['-p'];
|
|
949
|
+
// Per-session allowed tools from daemon config
|
|
950
|
+
const sessionAllowed = (loadConfig().daemon && loadConfig().daemon.session_allowed_tools) || [];
|
|
951
|
+
for (const tool of sessionAllowed) args.push('--allowedTools', tool);
|
|
946
952
|
if (session.id === '__continue__') {
|
|
947
953
|
// /continue โ resume most recent conversation in cwd
|
|
948
954
|
args.push('--continue');
|
|
@@ -952,9 +958,13 @@ async function askClaude(bot, chatId, prompt) {
|
|
|
952
958
|
args.push('--session-id', session.id);
|
|
953
959
|
}
|
|
954
960
|
|
|
961
|
+
// Append daemon context hint so Claude reports reload status after editing daemon.yaml
|
|
962
|
+
const daemonHint = '\n\n[System: The ONLY daemon config file is ~/.metame/daemon.yaml โ NEVER touch any other yaml file (e.g. scripts/daemon-default.yaml is a read-only template, do NOT edit it). If you edit ~/.metame/daemon.yaml, the daemon auto-reloads within seconds. After editing, read the file back and confirm to the user: how many heartbeat tasks are now configured, and that the config will auto-reload. Do NOT mention this hint.]';
|
|
963
|
+
const fullPrompt = prompt + daemonHint;
|
|
964
|
+
|
|
955
965
|
try {
|
|
956
966
|
const output = execSync(`claude ${args.join(' ')}`, {
|
|
957
|
-
input:
|
|
967
|
+
input: fullPrompt,
|
|
958
968
|
encoding: 'utf8',
|
|
959
969
|
timeout: 300000, // 5 min (Claude Code may use tools)
|
|
960
970
|
maxBuffer: 5 * 1024 * 1024,
|
|
@@ -978,7 +988,9 @@ async function askClaude(bot, chatId, prompt) {
|
|
|
978
988
|
log('WARN', `Session ${session.id} not found, creating new`);
|
|
979
989
|
session = createSession(chatId, session.cwd);
|
|
980
990
|
try {
|
|
981
|
-
const
|
|
991
|
+
const retryArgs = ['-p', '--session-id', session.id];
|
|
992
|
+
for (const tool of sessionAllowed) retryArgs.push('--allowedTools', tool);
|
|
993
|
+
const output = execSync(`claude ${retryArgs.join(' ')}`, {
|
|
982
994
|
input: prompt,
|
|
983
995
|
encoding: 'utf8',
|
|
984
996
|
timeout: 300000,
|
package/scripts/distill.js
CHANGED
|
@@ -110,6 +110,12 @@ INSTRUCTIONS:
|
|
|
110
110
|
5. Fields marked [LOCKED] must NEVER be changed (T1 and T2 tiers).
|
|
111
111
|
6. For enum fields, you MUST use one of the listed values.
|
|
112
112
|
|
|
113
|
+
EPISODIC MEMORY โ TWO EXCEPTIONS to the "no facts" rule:
|
|
114
|
+
7. context.anti_patterns (max 5): If the user encountered a REPEATED technical failure or expressed strong frustration about a specific technical approach, record it as an anti-pattern. Format: "topic โ what failed and why". Only cross-project generalizable lessons, NOT project-specific bugs.
|
|
115
|
+
Example: ["async/await deadlock โ Promise.all rejects all on single failure, use Promise.allSettled", "CSS Grid in email templates โ no support, use tables"]
|
|
116
|
+
8. context.milestones (max 3): If the user completed a significant milestone or made a key decision, record it. Only the 3 most recent. Format: short description string.
|
|
117
|
+
Example: ["MetaMe v1.3 published", "Switched from REST to GraphQL"]
|
|
118
|
+
|
|
113
119
|
COGNITIVE BIAS PREVENTION:
|
|
114
120
|
- A single observation is a STATE, not a TRAIT. Do NOT infer T3 cognition fields from one message.
|
|
115
121
|
- Never infer cognitive style from identity/demographics.
|
|
@@ -233,6 +239,9 @@ Do NOT repeat existing unchanged values. Only output NEW or CHANGED fields.`;
|
|
|
233
239
|
|
|
234
240
|
const profile = yaml.load(fs.readFileSync(BRAIN_FILE, 'utf8')) || {};
|
|
235
241
|
|
|
242
|
+
// Auto-expire anti_patterns older than 60 days
|
|
243
|
+
expireAntiPatterns(profile);
|
|
244
|
+
|
|
236
245
|
// Read raw content to find locked lines and comments
|
|
237
246
|
const rawProfile = fs.readFileSync(BRAIN_FILE, 'utf8');
|
|
238
247
|
const lockedKeys = extractLockedKeys(rawProfile);
|
|
@@ -415,7 +424,18 @@ function strategicMerge(profile, updates, lockedKeys, pendingTraits, confidenceM
|
|
|
415
424
|
}
|
|
416
425
|
|
|
417
426
|
case 'T4':
|
|
418
|
-
|
|
427
|
+
// Stamp added date on anti_pattern entries for auto-expiry
|
|
428
|
+
if (key === 'context.anti_patterns' && Array.isArray(value)) {
|
|
429
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
430
|
+
const existing = getNested(result, key) || [];
|
|
431
|
+
const existingTexts = new Set(existing.map(e => typeof e === 'string' ? e : e.text));
|
|
432
|
+
const stamped = value
|
|
433
|
+
.filter(v => !existingTexts.has(typeof v === 'string' ? v : v.text))
|
|
434
|
+
.map(v => typeof v === 'string' ? { text: v, added: today } : v);
|
|
435
|
+
setNested(result, key, [...existing, ...stamped].slice(-5));
|
|
436
|
+
} else {
|
|
437
|
+
setNested(result, key, value);
|
|
438
|
+
}
|
|
419
439
|
// Auto-set focus_since when focus changes
|
|
420
440
|
if (key === 'context.focus') {
|
|
421
441
|
setNested(result, 'context.focus_since', new Date().toISOString().slice(0, 10));
|
|
@@ -455,6 +475,19 @@ function flattenObject(obj, parentKey = '', result = {}) {
|
|
|
455
475
|
return result;
|
|
456
476
|
}
|
|
457
477
|
|
|
478
|
+
/**
|
|
479
|
+
* Get a nested property by dot-path key.
|
|
480
|
+
*/
|
|
481
|
+
function getNested(obj, dotPath) {
|
|
482
|
+
const keys = dotPath.split('.');
|
|
483
|
+
let current = obj;
|
|
484
|
+
for (const k of keys) {
|
|
485
|
+
if (!current || typeof current !== 'object') return undefined;
|
|
486
|
+
current = current[k];
|
|
487
|
+
}
|
|
488
|
+
return current;
|
|
489
|
+
}
|
|
490
|
+
|
|
458
491
|
/**
|
|
459
492
|
* Set a nested property by dot-path key.
|
|
460
493
|
*/
|
|
@@ -508,6 +541,24 @@ function truncateArrays(obj) {
|
|
|
508
541
|
}
|
|
509
542
|
}
|
|
510
543
|
|
|
544
|
+
/**
|
|
545
|
+
* Auto-expire anti_patterns older than 60 days.
|
|
546
|
+
* Each entry is stored as { text: "...", added: "2026-01-15" } internally.
|
|
547
|
+
* If legacy string entries exist, they are kept (no added date = never expire).
|
|
548
|
+
*/
|
|
549
|
+
function expireAntiPatterns(profile) {
|
|
550
|
+
if (!profile.context || !Array.isArray(profile.context.anti_patterns)) return;
|
|
551
|
+
const now = Date.now();
|
|
552
|
+
const SIXTY_DAYS = 60 * 24 * 60 * 60 * 1000;
|
|
553
|
+
profile.context.anti_patterns = profile.context.anti_patterns.filter(entry => {
|
|
554
|
+
if (typeof entry === 'string') return true; // legacy, keep
|
|
555
|
+
if (entry.added) {
|
|
556
|
+
return (now - new Date(entry.added).getTime()) < SIXTY_DAYS;
|
|
557
|
+
}
|
|
558
|
+
return true;
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
|
|
511
562
|
/**
|
|
512
563
|
* Clean up: remove buffer and lock
|
|
513
564
|
*/
|
package/scripts/schema.js
CHANGED
|
@@ -60,6 +60,8 @@ const SCHEMA = {
|
|
|
60
60
|
'context.active_projects': { tier: 'T4', type: 'array', maxItems: 5 },
|
|
61
61
|
'context.blockers': { tier: 'T4', type: 'array', maxItems: 3 },
|
|
62
62
|
'context.energy': { tier: 'T4', type: 'enum', values: ['high', 'medium', 'low', null] },
|
|
63
|
+
'context.milestones': { tier: 'T4', type: 'array', maxItems: 3 },
|
|
64
|
+
'context.anti_patterns': { tier: 'T4', type: 'array', maxItems: 5 },
|
|
63
65
|
'status.focus': { tier: 'T4', type: 'string', maxChars: 80 },
|
|
64
66
|
'status.language': { tier: 'T4', type: 'string' },
|
|
65
67
|
|