metame-cli 1.2.2 โ†’ 1.3.0

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
@@ -18,14 +18,11 @@ It is not a memory system; it is a **Cognitive Mirror** .
18
18
 
19
19
  ## โœจ Key Features
20
20
 
21
- * **๐Ÿง  Global Brain (`~/.claude_profile.yaml`):** A single source of truth for your identity, storing your nickname, stress status, and cognitive traits.
22
- * **๐Ÿงฌ Evolution Mechanism:** You are in control. Use `!metame evolve` to manually teach Claude about your new preferences or constraints, ensuring it gets smarter with every interaction.
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
- * **๐Ÿ›ก๏ธ Auto-Lock Mechanism:** Mark any value in your profile with `# [LOCKED]`, and MetaMe will treat it as a constitution that cannot be overwritten.
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.
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
+ * **๐Ÿงฌ 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
+ * **๐Ÿ›ก๏ธ Auto-Lock:** Mark any value with `# [LOCKED]` โ€” treated as a constitution, never auto-modified.
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.
29
26
 
30
27
  ## ๐Ÿ›  Prerequisites
31
28
 
@@ -91,41 +88,119 @@ metame interview
91
88
  ```
92
89
  (Command to be implemented in v1.3 - currently you can manually edit `~/.claude_profile.yaml` or use `set-trait`)
93
90
 
94
- ### Surgical Update (Manual Override)
91
+ ### Cognitive Evolution
95
92
 
96
- If you need to update a specific trait without editing the file manually:
93
+ MetaMe learns who you are through two paths:
97
94
 
98
- **Bash**
95
+ **Automatic (zero effort):** A global hook captures your messages. On next launch, Haiku distills cognitive traits in the background. Strong directives ("always"/"ไปฅๅŽไธ€ๅพ‹") write immediately; normal observations need 3+ consistent sightings. All writes are schema-validated (41 fields, 800 token budget). You'll see:
99
96
 
100
97
  ```
98
+ ๐Ÿง  MetaMe: Distilling 7 moments in background...
99
+ ```
100
+
101
+ **Manual:** Update a specific trait directly:
102
+
103
+ ```bash
101
104
  metame set-trait status.focus "Learning Rust"
105
+ metame evolve "I prefer functional programming patterns"
102
106
  ```
103
107
 
104
- ### Passive Distillation (Automatic)
108
+ **Anti-bias safeguards:** single observations โ‰  traits, contradictions are tracked not overwritten, pending traits expire after 30 days, context fields auto-clear on staleness.
105
109
 
106
- MetaMe automatically learns your cognitive patterns from conversations โ€” no action needed.
110
+ ### Remote Claude Code โ€” Telegram & Feishu (v1.3)
107
111
 
108
- **How it works:**
112
+ Full Claude Code from your phone โ€” stateful sessions with conversation history, tool use, and file editing. Supports both Telegram and Feishu (Lark).
109
113
 
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.
114
+ **Setup:**
115
+
116
+ ```bash
117
+ metame daemon init # Create config + setup guide
118
+ ```
115
119
 
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
120
+ Edit `~/.metame/daemon.yaml`:
121
121
 
122
- You'll see this in the startup log:
122
+ ```yaml
123
+ telegram:
124
+ enabled: true
125
+ bot_token: "YOUR_BOT_TOKEN" # From @BotFather
126
+ allowed_chat_ids:
127
+ - 123456789 # Your Telegram chat ID
123
128
 
129
+ feishu:
130
+ enabled: true
131
+ app_id: "YOUR_APP_ID" # From Feishu Developer Console
132
+ app_secret: "YOUR_APP_SECRET"
133
+ allowed_chat_ids: [] # Empty = allow all
124
134
  ```
125
- ๐Ÿง  MetaMe: Distilling 7 moments in background...
135
+
136
+ **Start the daemon:**
137
+
138
+ ```bash
139
+ metame daemon start # Background process
140
+ metame daemon status # Check if running
141
+ metame daemon logs # Tail the log
142
+ metame daemon stop # Shutdown
143
+ metame daemon install-launchd # macOS auto-start (RunAtLoad + KeepAlive)
144
+ ```
145
+
146
+ **Session commands (interactive buttons on Telegram & Feishu):**
147
+
148
+ | Command | Description |
149
+ |---------|-------------|
150
+ | `/new` | Start new session โ€” pick project directory from button list |
151
+ | `/resume` | Resume a session โ€” clickable list scoped to current workdir |
152
+ | `/continue` | Continue the most recent terminal session |
153
+ | `/cd` | Change working directory โ€” with directory browser |
154
+ | `/session` | Current session info |
155
+
156
+ Just type naturally for conversation โ€” every message stays in the same Claude Code session with full context.
157
+
158
+ **How it works:**
159
+
160
+ Each chat gets a persistent session via `claude -p --resume <session-id>`. This is the same Claude Code engine as your terminal โ€” same tools (file editing, bash, code search), same conversation history. You can start work on your computer and `/resume` from your phone, or vice versa.
161
+
162
+ **Other commands:**
163
+
164
+ | Command | Description |
165
+ |---------|-------------|
166
+ | `/status` | Daemon status + profile summary |
167
+ | `/tasks` | List scheduled heartbeat tasks |
168
+ | `/run <name>` | Run a task immediately |
169
+ | `/budget` | Today's token usage |
170
+ | `/quiet` | Silence mirror/reflections for 48h |
171
+
172
+ **Heartbeat Tasks:**
173
+
174
+ Define scheduled tasks in `daemon.yaml`:
175
+
176
+ ```yaml
177
+ heartbeat:
178
+ tasks:
179
+ - name: "morning-news"
180
+ prompt: "Summarize today's top 3 AI news stories."
181
+ interval: "24h"
182
+ model: "haiku"
183
+ notify: true
184
+ precondition: "curl -s -o /dev/null -w '%{http_code}' https://news.ycombinator.com | grep 200"
126
185
  ```
127
186
 
128
- The hook is installed automatically on first run to `~/.claude/settings.json` (global scope โ€” works across all projects).
187
+ * `precondition`: Shell command โ€” empty output โ†’ task skipped, zero tokens.
188
+ * `type: "script"`: Run a local script directly instead of `claude -p`.
189
+ * `notify: true`: Push results to Telegram/Feishu.
190
+
191
+ **Token efficiency:**
192
+
193
+ * Polling, slash commands, directory browsing: **zero tokens**
194
+ * Stateful sessions: same cost as using Claude Code in terminal (conversation history managed by Claude CLI)
195
+ * Budget tracking with daily limit (default 50k tokens)
196
+ * 10-second cooldown between Claude calls
197
+
198
+ **Security:**
199
+
200
+ * `allowed_chat_ids` whitelist โ€” unauthorized users silently ignored
201
+ * No `--dangerously-skip-permissions` โ€” standard `-p` mode permissions
202
+ * `~/.metame/` directory set to mode 700
203
+ * Bot tokens stored locally, never transmitted
129
204
 
130
205
  ### Hot Reload (Refresh)
131
206
 
@@ -226,7 +301,15 @@ If you want to delete your stored profile data:
226
301
  rm ~/.claude_profile.yaml
227
302
  ```
228
303
 
229
- ### 3. Remove Passive Distillation Data (Optional)
304
+ ### 3. Stop the Daemon (if running)
305
+
306
+ ```bash
307
+ metame daemon stop
308
+ launchctl unload ~/Library/LaunchAgents/com.metame.daemon.plist 2>/dev/null
309
+ rm -f ~/Library/LaunchAgents/com.metame.daemon.plist
310
+ ```
311
+
312
+ ### 4. Remove Passive Distillation Data (Optional)
230
313
 
231
314
  Remove the signal capture scripts:
232
315
 
@@ -236,7 +319,7 @@ Remove the signal capture scripts:
236
319
  rm -rf ~/.metame
237
320
  ```
238
321
 
239
- ### 4. Remove the Signal Capture Hook (Optional)
322
+ ### 5. Remove the Signal Capture Hook (Optional)
240
323
 
241
324
  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
325
 
@@ -253,7 +336,7 @@ console.log('Hook removed.');
253
336
  "
254
337
  ```
255
338
 
256
- ### 5. Cleanup Project Files (Optional)
339
+ ### 6. Cleanup Project Files (Optional)
257
340
 
258
341
  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`.
259
342
 
package/index.js CHANGED
@@ -23,7 +23,7 @@ if (!fs.existsSync(METAME_DIR)) {
23
23
  }
24
24
 
25
25
  // Auto-deploy bundled scripts to ~/.metame/
26
- const BUNDLED_SCRIPTS = ['signal-capture.js', 'distill.js', 'schema.js', 'pending-traits.js', 'migrate-v2.js'];
26
+ const BUNDLED_SCRIPTS = ['signal-capture.js', 'distill.js', 'schema.js', 'pending-traits.js', 'migrate-v2.js', 'daemon.js', 'telegram-adapter.js', 'feishu-adapter.js', 'daemon-default.yaml'];
27
27
  const scriptsDir = path.join(__dirname, 'scripts');
28
28
 
29
29
  for (const script of BUNDLED_SCRIPTS) {
@@ -563,6 +563,255 @@ if (isMirror) {
563
563
  process.exit(0);
564
564
  }
565
565
 
566
+ // ---------------------------------------------------------
567
+ // 5.6 DAEMON SUBCOMMANDS
568
+ // ---------------------------------------------------------
569
+ const isDaemon = process.argv.includes('daemon');
570
+ if (isDaemon) {
571
+ const daemonIndex = process.argv.indexOf('daemon');
572
+ const subCmd = process.argv[daemonIndex + 1];
573
+ const DAEMON_CONFIG = path.join(METAME_DIR, 'daemon.yaml');
574
+ const DAEMON_STATE = path.join(METAME_DIR, 'daemon_state.json');
575
+ const DAEMON_PID = path.join(METAME_DIR, 'daemon.pid');
576
+ const DAEMON_LOG = path.join(METAME_DIR, 'daemon.log');
577
+ const DAEMON_DEFAULT = path.join(__dirname, 'scripts', 'daemon-default.yaml');
578
+ const DAEMON_SCRIPT = path.join(METAME_DIR, 'daemon.js');
579
+
580
+ if (subCmd === 'init') {
581
+ // Create config from template
582
+ if (fs.existsSync(DAEMON_CONFIG)) {
583
+ console.log("โš ๏ธ daemon.yaml already exists at ~/.metame/daemon.yaml");
584
+ console.log(" Delete it first if you want to re-initialize.");
585
+ } else {
586
+ const templateSrc = fs.existsSync(DAEMON_DEFAULT)
587
+ ? DAEMON_DEFAULT
588
+ : path.join(METAME_DIR, 'daemon-default.yaml');
589
+ if (fs.existsSync(templateSrc)) {
590
+ fs.copyFileSync(templateSrc, DAEMON_CONFIG);
591
+ } else {
592
+ console.error("โŒ Template not found. Reinstall MetaMe.");
593
+ process.exit(1);
594
+ }
595
+ // Ensure directory permissions (700)
596
+ try { fs.chmodSync(METAME_DIR, 0o700); } catch { /* ignore on Windows */ }
597
+ console.log("โœ… MetaMe daemon initialized.");
598
+ console.log(` Config: ${DAEMON_CONFIG}`);
599
+ }
600
+
601
+ console.log("\n๐Ÿ“ฑ Telegram Setup (optional):");
602
+ console.log(" 1. Message @BotFather on Telegram โ†’ /newbot");
603
+ console.log(" 2. Copy the bot token");
604
+ console.log(" 3. Edit ~/.metame/daemon.yaml:");
605
+ console.log(" telegram:");
606
+ console.log(" enabled: true");
607
+ console.log(" bot_token: \"YOUR_TOKEN\"");
608
+ console.log(" allowed_chat_ids: [YOUR_CHAT_ID]");
609
+ console.log(" 4. To find your chat_id: message your bot, then run:");
610
+ console.log(" curl https://api.telegram.org/botYOUR_TOKEN/getUpdates");
611
+ console.log("\n๐Ÿ“˜ Feishu Setup (optional):");
612
+ console.log(" 1. Go to open.feishu.cn โ†’ Create App โ†’ get app_id & app_secret");
613
+ console.log(" 2. Enable Bot capability + im:message events");
614
+ console.log(" 3. Enable 'Long Connection' (้•ฟ่ฟžๆŽฅ) mode in Event Subscription");
615
+ console.log(" 4. Edit ~/.metame/daemon.yaml:");
616
+ console.log(" feishu:");
617
+ console.log(" enabled: true");
618
+ console.log(" app_id: \"YOUR_APP_ID\"");
619
+ console.log(" app_secret: \"YOUR_APP_SECRET\"");
620
+ console.log(" allowed_chat_ids: [CHAT_ID]");
621
+
622
+ console.log("\n Then: metame daemon start");
623
+
624
+ // Optional launchd setup (macOS only)
625
+ if (process.platform === 'darwin') {
626
+ const plistDir = path.join(HOME_DIR, 'Library', 'LaunchAgents');
627
+ const plistPath = path.join(plistDir, 'com.metame.daemon.plist');
628
+ console.log("\n๐ŸŽ Auto-start on macOS (optional):");
629
+ console.log(" To start daemon automatically on login:");
630
+ console.log(` metame daemon start (first time to verify it works)`);
631
+ console.log(` Then create: ${plistPath}`);
632
+ console.log(" Or run: metame daemon install-launchd");
633
+ }
634
+ process.exit(0);
635
+ }
636
+
637
+ if (subCmd === 'install-launchd') {
638
+ if (process.platform !== 'darwin') {
639
+ console.error("โŒ launchd is macOS-only.");
640
+ process.exit(1);
641
+ }
642
+ const plistDir = path.join(HOME_DIR, 'Library', 'LaunchAgents');
643
+ if (!fs.existsSync(plistDir)) fs.mkdirSync(plistDir, { recursive: true });
644
+ const plistPath = path.join(plistDir, 'com.metame.daemon.plist');
645
+ const nodePath = process.execPath;
646
+ // Capture current PATH so launchd can find `claude` and other tools
647
+ const currentPath = process.env.PATH || '/usr/local/bin:/usr/bin:/bin';
648
+ const plistContent = `<?xml version="1.0" encoding="UTF-8"?>
649
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
650
+ <plist version="1.0">
651
+ <dict>
652
+ <key>Label</key>
653
+ <string>com.metame.daemon</string>
654
+ <key>ProgramArguments</key>
655
+ <array>
656
+ <string>${nodePath}</string>
657
+ <string>${DAEMON_SCRIPT}</string>
658
+ </array>
659
+ <key>RunAtLoad</key>
660
+ <true/>
661
+ <key>KeepAlive</key>
662
+ <true/>
663
+ <key>StandardOutPath</key>
664
+ <string>${DAEMON_LOG}</string>
665
+ <key>StandardErrorPath</key>
666
+ <string>${DAEMON_LOG}</string>
667
+ <key>EnvironmentVariables</key>
668
+ <dict>
669
+ <key>METAME_ROOT</key>
670
+ <string>${__dirname}</string>
671
+ <key>PATH</key>
672
+ <string>${currentPath}</string>
673
+ <key>HOME</key>
674
+ <string>${HOME_DIR}</string>
675
+ </dict>
676
+ </dict>
677
+ </plist>`;
678
+ fs.writeFileSync(plistPath, plistContent, 'utf8');
679
+ console.log(`โœ… launchd plist installed: ${plistPath}`);
680
+ console.log(" Load now: launchctl load " + plistPath);
681
+ console.log(" Unload: launchctl unload " + plistPath);
682
+ process.exit(0);
683
+ }
684
+
685
+ if (subCmd === 'start') {
686
+ // Check if already running
687
+ if (fs.existsSync(DAEMON_PID)) {
688
+ const existingPid = parseInt(fs.readFileSync(DAEMON_PID, 'utf8').trim(), 10);
689
+ try {
690
+ process.kill(existingPid, 0); // test if alive
691
+ console.log(`โš ๏ธ Daemon already running (PID: ${existingPid})`);
692
+ console.log(" Use 'metame daemon stop' first.");
693
+ process.exit(1);
694
+ } catch {
695
+ // Stale PID file โ€” clean up
696
+ fs.unlinkSync(DAEMON_PID);
697
+ }
698
+ }
699
+ if (!fs.existsSync(DAEMON_CONFIG)) {
700
+ console.error("โŒ No config found. Run: metame daemon init");
701
+ process.exit(1);
702
+ }
703
+ if (!fs.existsSync(DAEMON_SCRIPT)) {
704
+ console.error("โŒ daemon.js not found. Reinstall MetaMe.");
705
+ process.exit(1);
706
+ }
707
+ const bg = spawn(process.execPath, [DAEMON_SCRIPT], {
708
+ detached: true,
709
+ stdio: 'ignore',
710
+ env: { ...process.env, HOME: HOME_DIR, METAME_ROOT: __dirname },
711
+ });
712
+ bg.unref();
713
+ console.log(`โœ… MetaMe daemon started (PID: ${bg.pid})`);
714
+ console.log(" Logs: metame daemon logs");
715
+ console.log(" Stop: metame daemon stop");
716
+ process.exit(0);
717
+ }
718
+
719
+ if (subCmd === 'stop') {
720
+ if (!fs.existsSync(DAEMON_PID)) {
721
+ console.log("โ„น๏ธ No daemon running (no PID file).");
722
+ process.exit(0);
723
+ }
724
+ const pid = parseInt(fs.readFileSync(DAEMON_PID, 'utf8').trim(), 10);
725
+ try {
726
+ process.kill(pid, 'SIGTERM');
727
+ console.log(`โœ… Daemon stopped (PID: ${pid})`);
728
+ } catch (e) {
729
+ console.log(`โš ๏ธ Process ${pid} not found (may have already exited).`);
730
+ fs.unlinkSync(DAEMON_PID);
731
+ }
732
+ process.exit(0);
733
+ }
734
+
735
+ if (subCmd === 'status') {
736
+ let state = {};
737
+ try { state = JSON.parse(fs.readFileSync(DAEMON_STATE, 'utf8')); } catch { /* empty */ }
738
+
739
+ // Check if running
740
+ let isRunning = false;
741
+ if (fs.existsSync(DAEMON_PID)) {
742
+ const pid = parseInt(fs.readFileSync(DAEMON_PID, 'utf8').trim(), 10);
743
+ try { process.kill(pid, 0); isRunning = true; } catch { /* dead */ }
744
+ }
745
+
746
+ console.log(`๐Ÿค– MetaMe Daemon: ${isRunning ? '๐ŸŸข Running' : '๐Ÿ”ด Stopped'}`);
747
+ if (state.started_at) console.log(` Started: ${state.started_at}`);
748
+ if (state.pid) console.log(` PID: ${state.pid}`);
749
+
750
+ // Budget
751
+ const budget = state.budget || {};
752
+ const config = {};
753
+ try { Object.assign(config, yaml.load(fs.readFileSync(DAEMON_CONFIG, 'utf8'))); } catch { /* empty */ }
754
+ const limit = (config.budget && config.budget.daily_limit) || 50000;
755
+ console.log(` Budget: ${budget.tokens_used || 0}/${limit} tokens (${budget.date || 'no data'})`);
756
+
757
+ // Tasks
758
+ const tasks = state.tasks || {};
759
+ if (Object.keys(tasks).length > 0) {
760
+ console.log(" Recent tasks:");
761
+ for (const [name, info] of Object.entries(tasks)) {
762
+ const icon = info.status === 'success' ? 'โœ…' : 'โŒ';
763
+ console.log(` ${icon} ${name}: ${info.last_run || 'unknown'}`);
764
+ if (info.output_preview) console.log(` ${info.output_preview.slice(0, 80)}...`);
765
+ }
766
+ }
767
+ process.exit(0);
768
+ }
769
+
770
+ if (subCmd === 'logs') {
771
+ if (!fs.existsSync(DAEMON_LOG)) {
772
+ console.log("โ„น๏ธ No log file yet. Start the daemon first.");
773
+ process.exit(0);
774
+ }
775
+ const content = fs.readFileSync(DAEMON_LOG, 'utf8');
776
+ const lines = content.split('\n');
777
+ const tail = lines.slice(-50).join('\n');
778
+ console.log(tail);
779
+ process.exit(0);
780
+ }
781
+
782
+ if (subCmd === 'run') {
783
+ const taskName = process.argv[daemonIndex + 2];
784
+ if (!taskName) {
785
+ console.error("โŒ Usage: metame daemon run <task-name>");
786
+ process.exit(1);
787
+ }
788
+ if (!fs.existsSync(DAEMON_SCRIPT)) {
789
+ console.error("โŒ daemon.js not found. Reinstall MetaMe.");
790
+ process.exit(1);
791
+ }
792
+ // Run in foreground using daemon.js --run
793
+ const result = require('child_process').spawnSync(
794
+ process.execPath,
795
+ [DAEMON_SCRIPT, '--run', taskName],
796
+ { stdio: 'inherit', env: { ...process.env, HOME: HOME_DIR, METAME_ROOT: __dirname } }
797
+ );
798
+ process.exit(result.status || 0);
799
+ }
800
+
801
+ // Unknown subcommand
802
+ console.log("๐Ÿ“– MetaMe Daemon Commands:");
803
+ console.log(" metame daemon init โ€” initialize config");
804
+ console.log(" metame daemon start โ€” start background daemon");
805
+ console.log(" metame daemon stop โ€” stop daemon");
806
+ console.log(" metame daemon status โ€” show status & budget");
807
+ console.log(" metame daemon logs โ€” tail log file");
808
+ console.log(" metame daemon run <name> โ€” run a task once");
809
+ if (process.platform === 'darwin') {
810
+ console.log(" metame daemon install-launchd โ€” auto-start on macOS");
811
+ }
812
+ process.exit(0);
813
+ }
814
+
566
815
  // ---------------------------------------------------------
567
816
  // ---------------------------------------------------------
568
817
  // 6. SAFETY GUARD: RECURSION PREVENTION (v2)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metame-cli",
3
- "version": "1.2.2",
3
+ "version": "1.3.0",
4
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": {
@@ -23,9 +23,10 @@
23
23
  "author": "Yaron",
24
24
  "license": "MIT",
25
25
  "dependencies": {
26
+ "@larksuiteoapi/node-sdk": "^1.58.0",
26
27
  "js-yaml": "^4.1.1"
27
28
  },
28
29
  "engines": {
29
30
  "node": ">=14"
30
31
  }
31
- }
32
+ }
@@ -0,0 +1,49 @@
1
+ # MetaMe Daemon Configuration
2
+ # Generated by: metame daemon init
3
+
4
+ telegram:
5
+ enabled: false
6
+ bot_token: null
7
+ allowed_chat_ids: []
8
+
9
+ feishu:
10
+ enabled: false
11
+ app_id: null
12
+ app_secret: null
13
+ allowed_chat_ids: []
14
+
15
+ heartbeat:
16
+ tasks: []
17
+ # Examples โ€” uncomment or add your own:
18
+ #
19
+ # Scheduled task (calls claude -p with your profile context):
20
+ # - name: "morning-news"
21
+ # prompt: "ๆŠ“ๅ–ไปŠๅคฉ็š„AI้ข†ๅŸŸ้‡่ฆๆ–ฐ้—ป๏ผŒๆ•ด็†ๆˆ3ๆกๆ‘˜่ฆ๏ผŒไธญ่‹ฑๅŒ่ฏญใ€‚"
22
+ # interval: "24h"
23
+ # model: "haiku"
24
+ # notify: true
25
+ #
26
+ # - name: "weekly-review"
27
+ # prompt: "ๅŸบไบŽๆˆ‘็š„profile๏ผŒ็”Ÿๆˆๆœฌๅ‘จไธชไบบๆˆ้•ฟๅ›ž้กพๅ’Œไธ‹ๅ‘จๅปบ่ฎฎใ€‚"
28
+ # interval: "7d"
29
+ # model: "haiku"
30
+ # notify: true
31
+ #
32
+ # Script task (runs a local script directly, no claude -p):
33
+ # - name: "distill"
34
+ # type: "script"
35
+ # command: "node ~/.metame/distill.js"
36
+ # interval: "6h"
37
+ # precondition: "test -s ~/.metame/raw_signals.jsonl"
38
+ # notify: false
39
+ #
40
+ # precondition: shell command, empty output โ†’ skip (zero tokens)
41
+ # type: "script" โ†’ runs command directly instead of claude -p
42
+
43
+ budget:
44
+ daily_limit: 50000
45
+ warning_threshold: 0.8
46
+
47
+ daemon:
48
+ log_max_size: 1048576
49
+ heartbeat_check_interval: 60