metame-cli 1.4.31 → 1.4.32

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/index.js CHANGED
@@ -7,6 +7,7 @@ const fs = require('fs');
7
7
  const path = require('path');
8
8
  const os = require('os');
9
9
  const { spawn, execSync } = require('child_process');
10
+ const { sleepSync, findProcessesByPattern, icon } = require('./scripts/platform');
10
11
 
11
12
  // On Windows, .cmd files (like claude.cmd from npm global) need shell:true to spawn.
12
13
  // We use COMSPEC to avoid conda/PATH issues where cmd.exe can't be found.
@@ -52,7 +53,7 @@ if (!fs.existsSync(METAME_DIR)) {
52
53
  // Auto-deploy bundled scripts to ~/.metame/
53
54
  // IMPORTANT: daemon.yaml is USER CONFIG — never overwrite it. Only daemon-default.yaml (template) is synced.
54
55
  const scriptsDir = path.join(__dirname, 'scripts');
55
- const BUNDLED_BASE_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', 'providers.js', 'session-analytics.js', 'resolve-yaml.js', 'utils.js', 'skill-evolution.js', 'memory.js', 'memory-extract.js', 'memory-search.js', 'memory-gc.js', 'qmd-client.js', 'session-summarize.js', 'check-macos-control-capabilities.sh', 'usage-classifier.js', 'task-board.js', 'memory-nightly-reflect.js', 'memory-index.js'];
56
+ const BUNDLED_BASE_SCRIPTS = ['platform.js', 'signal-capture.js', 'distill.js', 'schema.js', 'pending-traits.js', 'migrate-v2.js', 'daemon.js', 'telegram-adapter.js', 'feishu-adapter.js', 'daemon-default.yaml', 'providers.js', 'session-analytics.js', 'resolve-yaml.js', 'utils.js', 'skill-evolution.js', 'memory.js', 'memory-extract.js', 'memory-search.js', 'memory-gc.js', 'qmd-client.js', 'session-summarize.js', 'check-macos-control-capabilities.sh', 'usage-classifier.js', 'task-board.js', 'memory-nightly-reflect.js', 'memory-index.js'];
56
57
  const DAEMON_MODULE_SCRIPTS = (() => {
57
58
  try {
58
59
  return fs.readdirSync(scriptsDir).filter((f) => /^daemon-[\w-]+\.js$/.test(f));
@@ -97,7 +98,7 @@ for (const script of BUNDLED_SCRIPTS) {
97
98
  // and has defer logic (waits for active Claude tasks to finish before restarting).
98
99
  // Killing here bypasses that and interrupts ongoing conversations.
99
100
  if (scriptsUpdated) {
100
- console.log('šŸ“¦ Scripts synced to ~/.metame/ — daemon will auto-restart when idle.');
101
+ console.log(`${icon("pkg")} Scripts synced to ~/.metame/ — daemon will auto-restart when idle.`);
101
102
  }
102
103
 
103
104
  // ---------------------------------------------------------
@@ -131,7 +132,7 @@ if (fs.existsSync(bundledSkillsDir)) {
131
132
  skillsInstalled.push(skillName);
132
133
  }
133
134
  if (skillsInstalled.length > 0) {
134
- console.log(`🧠 Skills installed: ${skillsInstalled.join(', ')}`);
135
+ console.log(`${icon("brain")} Skills installed: ${skillsInstalled.join(', ')}`);
135
136
  }
136
137
  } catch {
137
138
  // Non-fatal
@@ -153,7 +154,7 @@ if (!fs.existsSync(DAEMON_CONFIG_FILE)) {
153
154
  if (fs.existsSync(DAEMON_YAML_BACKUP)) {
154
155
  // Restore from backup — user had real config that was lost
155
156
  fs.copyFileSync(DAEMON_YAML_BACKUP, DAEMON_CONFIG_FILE);
156
- console.log('āš ļø daemon.yaml was missing — restored from backup.');
157
+ console.log(`${icon("warn")} daemon.yaml was missing — restored from backup.`);
157
158
  } else {
158
159
  const daemonTemplate = path.join(scriptsDir, 'daemon-default.yaml');
159
160
  if (fs.existsSync(daemonTemplate)) {
@@ -179,13 +180,34 @@ function ensureHookInstalled() {
179
180
  }
180
181
 
181
182
  // Check if our hook is already configured
182
- const hookCommand = `node ${SIGNAL_CAPTURE_SCRIPT}`;
183
+ // Use forward slashes + quotes — Claude Code runs hooks via bash even on Windows
184
+ const scriptPathForHook = SIGNAL_CAPTURE_SCRIPT.replace(/\\/g, '/');
185
+ const hookCommand = `node "${scriptPathForHook}"`;
183
186
  const existing = settings.hooks?.UserPromptSubmit || [];
184
187
  const alreadyInstalled = existing.some(entry =>
185
- entry.hooks?.some(h => h.command === hookCommand)
188
+ entry.hooks?.some(h => h.command && h.command.includes('signal-capture.js'))
186
189
  );
187
190
 
188
- if (!alreadyInstalled) {
191
+ // Remove stale hooks with backslash paths (old Windows format)
192
+ if (settings.hooks?.UserPromptSubmit) {
193
+ for (const entry of settings.hooks.UserPromptSubmit) {
194
+ if (entry.hooks) {
195
+ entry.hooks = entry.hooks.filter(h =>
196
+ !(h.command && h.command.includes('signal-capture.js') && h.command.includes('\\'))
197
+ );
198
+ }
199
+ }
200
+ settings.hooks.UserPromptSubmit = settings.hooks.UserPromptSubmit.filter(
201
+ entry => entry.hooks && entry.hooks.length > 0
202
+ );
203
+ }
204
+
205
+ // Re-check after cleanup
206
+ const stillInstalled = (settings.hooks?.UserPromptSubmit || []).some(entry =>
207
+ entry.hooks?.some(h => h.command && h.command.includes('signal-capture.js'))
208
+ );
209
+
210
+ if (!stillInstalled) {
189
211
  if (!settings.hooks) settings.hooks = {};
190
212
  if (!settings.hooks.UserPromptSubmit) settings.hooks.UserPromptSubmit = [];
191
213
 
@@ -197,11 +219,11 @@ function ensureHookInstalled() {
197
219
  });
198
220
 
199
221
  fs.writeFileSync(CLAUDE_SETTINGS, JSON.stringify(settings, null, 2), 'utf8');
200
- console.log("šŸŖ MetaMe: Signal capture hook installed.");
222
+ console.log(`${icon("hook")} MetaMe: Signal capture hook installed.`);
201
223
  }
202
224
  } catch (e) {
203
225
  // Non-fatal: hook install failure shouldn't block launch
204
- console.error("āš ļø Hook install skipped:", e.message);
226
+ console.error(`${icon("warn")} Hook install skipped:`, e.message);
205
227
  }
206
228
  }
207
229
 
@@ -264,7 +286,7 @@ function spawnDistillBackground() {
264
286
  // 4-hour cooldown: check last distill timestamp from profile
265
287
  const cooldownMs = 4 * 60 * 60 * 1000;
266
288
  try {
267
- const profilePath = path.join(process.env.HOME || '', '.claude_profile.yaml');
289
+ const profilePath = path.join(HOME_DIR, '.claude_profile.yaml');
268
290
  if (fs.existsSync(profilePath)) {
269
291
  const yaml = require('js-yaml');
270
292
  const profile = yaml.load(fs.readFileSync(profilePath, 'utf8'));
@@ -391,7 +413,7 @@ status:
391
413
  // ---------------------------------------------------------
392
414
  const PROTOCOL_NORMAL = `${METAME_START}
393
415
  ---
394
- ## 🧠 SYSTEM KERNEL: SHADOW_MODE (Active)
416
+ ## ${icon("brain")} SYSTEM KERNEL: SHADOW_MODE (Active)
395
417
 
396
418
  **1. THE BRAIN (Source of Truth):**
397
419
  * **FILE:** \`$HOME/.claude_profile.yaml\`
@@ -417,7 +439,7 @@ const PROTOCOL_NORMAL = `${METAME_START}
417
439
 
418
440
  const PROTOCOL_ONBOARDING = `${METAME_START}
419
441
  ---
420
- ## 🧠 SYSTEM KERNEL: SHADOW_MODE (Active)
442
+ ## ${icon("brain")} SYSTEM KERNEL: SHADOW_MODE (Active)
421
443
 
422
444
  **1. THE BRAIN (Source of Truth):**
423
445
  * **FILE:** \`$HOME/.claude_profile.yaml\`
@@ -474,7 +496,7 @@ This step connects the bot to the user's PRIVATE chat — this is the admin chan
474
496
 
475
497
  - If **Feishu:**
476
498
  1. Guide through: open.feishu.cn/app → create app → get App ID + Secret → enable bot → add event subscription (long connection mode) → add permissions (im:message, im:message.p2p_msg:readonly, im:message.group_at_msg:readonly, im:message:send_as_bot, im:resource) → publish.
477
- **āš ļø é‡č¦ļ¼š** åœØć€Œäŗ‹ä»¶č®¢é˜…ć€é”µé¢ļ¼Œåæ…é”»å¼€åÆć€ŒęŽ„ę”¶ę¶ˆęÆ im.message.receive_v1ć€äŗ‹ä»¶ć€‚ē„¶åŽåœØčÆ„äŗ‹ä»¶ēš„é…ē½®äø­ļ¼Œå‹¾é€‰ć€ŒčŽ·å–ē¾¤ē»„äø­ę‰€ęœ‰ę¶ˆęÆć€ļ¼ˆå¦åˆ™ bot åœØē¾¤čŠäø­åŖčƒ½ę”¶åˆ° @它 ēš„ę¶ˆęÆļ¼Œę— ę³•ęŽ„ę”¶ę™®é€šē¾¤ę¶ˆęÆļ¼‰ć€‚
499
+ **${icon("warn")} é‡č¦ļ¼š** åœØć€Œäŗ‹ä»¶č®¢é˜…ć€é”µé¢ļ¼Œåæ…é”»å¼€åÆć€ŒęŽ„ę”¶ę¶ˆęÆ im.message.receive_v1ć€äŗ‹ä»¶ć€‚ē„¶åŽåœØčÆ„äŗ‹ä»¶ēš„é…ē½®äø­ļ¼Œå‹¾é€‰ć€ŒčŽ·å–ē¾¤ē»„äø­ę‰€ęœ‰ę¶ˆęÆć€ļ¼ˆå¦åˆ™ bot åœØē¾¤čŠäø­åŖčƒ½ę”¶åˆ° @它 ēš„ę¶ˆęÆļ¼Œę— ę³•ęŽ„ę”¶ę™®é€šē¾¤ę¶ˆęÆļ¼‰ć€‚
478
500
  2. Ask user to paste App ID and App Secret.
479
501
  3. Write \`app_id\` and \`app_secret\` into \`~/.metame/daemon.yaml\` under \`feishu:\` section, set \`enabled: true\`.
480
502
  4. Tell user: "Now open Feishu and send any message to your new bot (private chat), then tell me you're done."
@@ -549,7 +571,7 @@ if (isKnownUser) {
549
571
  finalProtocol = PROTOCOL_NORMAL;
550
572
  } else {
551
573
  finalProtocol = PROTOCOL_ONBOARDING;
552
- console.log("šŸ†• New user detected — entering Genesis interview mode...");
574
+ console.log(`${icon("new")} New user detected — entering Genesis interview mode...`);
553
575
  }
554
576
 
555
577
  // ---------------------------------------------------------
@@ -659,11 +681,11 @@ try {
659
681
  if (triggerDrift || triggerComfort || trigger7th) {
660
682
  let hint = '';
661
683
  if (triggerDrift) {
662
- hint = `ęœ€čæ‘å‡ äøŖsessionēš„ę–¹å‘å’Œ"${driftDeclaredFocus}"ęœ‰åå·®ć€‚čÆ·åœØåÆ¹čÆå¼€å§‹ę—¶ęø©å’Œåœ°é—®ļ¼ššŸŖž ę˜Æę–¹å‘ęœ‰ę„č°ƒę•“äŗ†ļ¼Œčæ˜ę˜Æäøå°åæƒåäŗ†ļ¼Ÿ`;
684
+ hint = `ęœ€čæ‘å‡ äøŖsessionēš„ę–¹å‘å’Œ"${driftDeclaredFocus}"ęœ‰åå·®ć€‚čÆ·åœØåÆ¹čÆå¼€å§‹ę—¶ęø©å’Œåœ°é—®ļ¼š${icon("mirror")} ę˜Æę–¹å‘ęœ‰ę„č°ƒę•“äŗ†ļ¼Œčæ˜ę˜Æäøå°åæƒåäŗ†ļ¼Ÿ`;
663
685
  } else if (triggerComfort) {
664
- hint = 'čæžē»­å‡ ę¬”éƒ½åœØē†Ÿę‚‰é¢†åŸŸć€‚å¦‚ęžœē”Øęˆ·åœØsessionē»“ęŸę—¶č‡Ŗē„¶åœé”æļ¼ŒåÆä»„ęø©å’Œåœ°é—®ļ¼ššŸŖž å‡†å¤‡å„½ęŽ¢ē“¢ę‹‰ä¼øåŒŗäŗ†å—ļ¼Ÿ';
686
+ hint = `čæžē»­å‡ ę¬”éƒ½åœØē†Ÿę‚‰é¢†åŸŸć€‚å¦‚ęžœē”Øęˆ·åœØsessionē»“ęŸę—¶č‡Ŗē„¶åœé”æļ¼ŒåÆä»„ęø©å’Œåœ°é—®ļ¼š${icon("mirror")} å‡†å¤‡å„½ęŽ¢ē“¢ę‹‰ä¼øåŒŗäŗ†å—ļ¼Ÿ`;
665
687
  } else {
666
- hint = 'čæ™ę˜Æē¬¬' + distillCount + 'ꬔsessionć€‚å¦‚ęžœsessionč‡Ŗē„¶ē»“ęŸļ¼ŒåÆä»„é™„åŠ äø€å„ļ¼ššŸŖž äø€äøŖčÆå½¢å®¹čæ™ę¬”sessionēš„ę„Ÿå—ļ¼Ÿ';
688
+ hint = 'čæ™ę˜Æē¬¬' + distillCount + `ꬔsessionć€‚å¦‚ęžœsessionč‡Ŗē„¶ē»“ęŸļ¼ŒåÆä»„é™„åŠ äø€å„ļ¼š${icon("mirror")} äø€äøŖčÆå½¢å®¹čæ™ę¬”sessionēš„ę„Ÿå—ļ¼Ÿ`;
667
689
  }
668
690
  const timing = triggerDrift ? 'åœØåÆ¹čÆå¼€å§‹ę—¶å°±é—®äø€ę¬”' : 'åŖåœØsessionå³å°†ē»“ęŸę—¶čÆ“äø€ę¬”';
669
691
  reflectionLine = `\n[MetaMe reflection: ${hint} ${timing}ć€‚å¦‚ęžœē”Øęˆ·ę²”å›žåŗ”å°±äøč¦čæ½é—®ć€‚]\n`;
@@ -767,13 +789,13 @@ try {
767
789
  fs.writeFileSync(GLOBAL_CLAUDE_MD, finalGlobal, 'utf8');
768
790
  } catch (e) {
769
791
  // Non-fatal: global CLAUDE.md injection is best-effort
770
- console.error(`āš ļø Failed to inject global CLAUDE.md: ${e.message}`);
792
+ console.error(`${icon("warn")} Failed to inject global CLAUDE.md: ${e.message}`);
771
793
  }
772
794
 
773
795
 
774
796
 
775
797
 
776
- console.log(`šŸ”® MetaMe v${pkgVersion}: Link Established.`);
798
+ console.log(`${icon("magic")} MetaMe v${pkgVersion}: Link Established.`);
777
799
 
778
800
  // Memory system status — show live stats without blocking launch
779
801
  try {
@@ -789,7 +811,7 @@ try {
789
811
  memMod.close();
790
812
  } catch { /* memory.js not available or DB not ready */ }
791
813
  if (factCount > 0 || tagCount > 0) {
792
- console.log(`🧠 Memory: ${factCount} facts · ${tagCount} sessions tagged`);
814
+ console.log(`${icon("brain")} Memory: ${factCount} facts Ā· ${tagCount} sessions tagged`);
793
815
  }
794
816
  } catch { /* non-fatal */ }
795
817
 
@@ -801,12 +823,12 @@ try {
801
823
  : 0;
802
824
 
803
825
  if (pendingCount > 0) {
804
- console.log(`🧬 Cognition: ${pendingCount} moment${pendingCount > 1 ? 's' : ''} pending distillation`);
826
+ console.log(`${icon("dna")} Cognition: ${pendingCount} moment${pendingCount > 1 ? 's' : ''} pending distillation`);
805
827
  } else {
806
828
  // Show last distill time
807
829
  let lastDistillStr = 'ä»ŽęœŖ';
808
830
  try {
809
- const profilePath = path.join(process.env.HOME || '', '.claude_profile.yaml');
831
+ const profilePath = path.join(HOME_DIR, '.claude_profile.yaml');
810
832
  if (fs.existsSync(profilePath)) {
811
833
  const _yaml = require('js-yaml');
812
834
  const profile = _yaml.load(fs.readFileSync(profilePath, 'utf8'));
@@ -820,7 +842,7 @@ try {
820
842
  }
821
843
  }
822
844
  } catch { /* non-fatal */ }
823
- console.log(`🧬 Cognition: ę— ę–°äæ”å· Ā· äøŠę¬”č’øé¦ ${lastDistillStr}`);
845
+ console.log(`${icon("dna")} Cognition: ę— ę–°äæ”å· Ā· äøŠę¬”č’øé¦ ${lastDistillStr}`);
824
846
  }
825
847
  } catch { /* non-fatal */ }
826
848
 
@@ -846,7 +868,7 @@ const CURRENT_VERSION = pkgVersion;
846
868
  });
847
869
 
848
870
  if (latest && latest !== CURRENT_VERSION) {
849
- console.log(`šŸ“¦ MetaMe ${latest} available (current ${CURRENT_VERSION}), updating...`);
871
+ console.log(`${icon("pkg")} MetaMe ${latest} available (current ${CURRENT_VERSION}), updating...`);
850
872
  const { execSync } = require('child_process');
851
873
  try {
852
874
  execSync('npm install -g metame-cli@latest', {
@@ -854,10 +876,10 @@ const CURRENT_VERSION = pkgVersion;
854
876
  timeout: 60000,
855
877
  ...(process.platform === 'win32' ? { shell: process.env.COMSPEC || true } : {}),
856
878
  });
857
- console.log(`āœ… Updated to ${latest}. Restart metame to use the new version.`);
879
+ console.log(`${icon("ok")} Updated to ${latest}. Restart metame to use the new version.`);
858
880
  } catch (e) {
859
881
  const msg = e.stderr ? e.stderr.toString().trim().split('\n').pop() : '';
860
- console.log(`āš ļø Auto-update failed${msg ? ': ' + msg : ''}. Run manually: npm install -g metame-cli`);
882
+ console.log(`${icon("warn")} Auto-update failed${msg ? ': ' + msg : ''}. Run manually: npm install -g metame-cli`);
861
883
  }
862
884
  }
863
885
  } catch { /* network unavailable, skip silently */ }
@@ -887,14 +909,14 @@ const CURRENT_VERSION = pkgVersion;
887
909
  try { execSync(`${whichCmd} bun`, { stdio: 'pipe', timeout: 2000 }); bunAvailable = true; } catch { }
888
910
 
889
911
  console.log('');
890
- console.log('ā”Œā”€ šŸ” č®°åæ†ęœē“¢å¢žå¼ŗļ¼ˆåÆé€‰ļ¼Œå…č“¹ļ¼‰');
912
+ console.log(`ā”Œā”€ ${icon("search")} č®°åæ†ęœē“¢å¢žå¼ŗļ¼ˆåÆé€‰ļ¼Œå…č“¹ļ¼‰`);
891
913
  console.log('│');
892
914
  console.log('│ å½“å‰ęØ”å¼ļ¼šåŸŗē”€å…Øę–‡ęœē“¢ļ¼ˆFTS5)');
893
915
  console.log('│ 安装 QMD åŽļ¼šBM25 + å‘é‡čÆ­ä¹‰ + é‡ęŽ’åŗ 混合搜瓢');
894
916
  console.log('│ ę•ˆęžœļ¼šå¬å›žč“Øé‡ēŗ¦ 5xļ¼ŒęØ”ē³Šęčæ°ä¹Ÿčƒ½ē²¾å‡†å‘½äø­åŽ†å²č®°åæ†');
895
917
  if (!bunAvailable) {
896
918
  console.log('│');
897
- console.log('│ āš ļø ęœŖę£€ęµ‹åˆ° bunļ¼Œę— ę³•č‡ŖåŠØå®‰č£…ć€‚');
919
+ console.log(`│ ${icon("warn")} ęœŖę£€ęµ‹åˆ° bunļ¼Œę— ę³•č‡ŖåŠØå®‰č£…ć€‚`);
898
920
  console.log('│ ę‰‹åŠØå®‰č£…ļ¼šcurl -fsSL https://bun.sh/install | bash');
899
921
  console.log('│ bun install -g github:tobi/qmd');
900
922
  console.log('└────────────────────────────────────────────────');
@@ -913,12 +935,12 @@ const CURRENT_VERSION = pkgVersion;
913
935
  process.stdout.write('\n');
914
936
 
915
937
  if (answer === 'y' || answer === 'yes') {
916
- console.log(' ā¬‡ļø ę­£åœØå®‰č£… QMD...');
938
+ console.log(` ${icon("down")} ę­£åœØå®‰č£… QMD...`);
917
939
  try {
918
940
  execSync('bun install -g github:tobi/qmd', { stdio: 'inherit', timeout: 120000 });
919
- console.log(' āœ… QMD å·²å®‰č£…ļ¼Œäø‹ę¬”č®°åæ†ęœē“¢č‡ŖåŠØåÆē”Øå‘é‡ęØ”å¼ć€‚');
941
+ console.log(` ${icon("ok")} QMD å·²å®‰č£…ļ¼Œäø‹ę¬”č®°åæ†ęœē“¢č‡ŖåŠØåÆē”Øå‘é‡ęØ”å¼ć€‚`);
920
942
  } catch {
921
- console.log(' āš ļø å®‰č£…å¤±č“„ļ¼ŒåÆę‰‹åŠØę‰§č”Œļ¼šbun install -g github:tobi/qmd');
943
+ console.log(` ${icon("warn")} å®‰č£…å¤±č“„ļ¼ŒåÆę‰‹åŠØę‰§č”Œļ¼šbun install -g github:tobi/qmd`);
922
944
  }
923
945
  } else {
924
946
  console.log(' č·³čæ‡ć€‚å¦‚éœ€ę—„åŽå®‰č£…ļ¼šbun install -g github:tobi/qmd');
@@ -937,7 +959,7 @@ const CURRENT_VERSION = pkgVersion;
937
959
  const isRefresh = process.argv.includes('refresh') || process.argv.includes('--refresh');
938
960
 
939
961
  if (isRefresh) {
940
- console.log("āœ… MetaMe configuration re-injected.");
962
+ console.log(`${icon("ok")} MetaMe configuration re-injected.`);
941
963
  console.log(" Ask Claude to 'read CLAUDE.md' to apply the changes.");
942
964
  process.exit(0);
943
965
  }
@@ -953,7 +975,7 @@ if (isEvolve) {
953
975
  const insight = process.argv.slice(evolveIndex + 1).join(' ').trim();
954
976
 
955
977
  if (!insight) {
956
- console.error("āŒ Error: Missing insight.");
978
+ console.error(`${icon("fail")} Error: Missing insight.`);
957
979
  console.error(" Usage: metame evolve \"I realized I prefer functional programming\"");
958
980
  process.exit(1);
959
981
  }
@@ -975,14 +997,14 @@ if (isEvolve) {
975
997
  // Save back to file
976
998
  fs.writeFileSync(BRAIN_FILE, yaml.dump(doc), 'utf8');
977
999
 
978
- console.log("🧠 MetaMe Brain Updated.");
1000
+ console.log(`${icon("brain")} MetaMe Brain Updated.`);
979
1001
  console.log(` Added insight: "${insight}"`);
980
1002
  console.log(" (Run 'metame refresh' to apply this to the current session)");
981
1003
  } else {
982
- console.error("āŒ Error: No profile found. Run 'metame' first to initialize.");
1004
+ console.error(`${icon("fail")} Error: No profile found. Run 'metame' first to initialize.`);
983
1005
  }
984
1006
  } catch (e) {
985
- console.error("āŒ Error updating profile:", e.message);
1007
+ console.error(`${icon("fail")} Error updating profile:`, e.message);
986
1008
  }
987
1009
  process.exit(0);
988
1010
  }
@@ -1002,7 +1024,7 @@ if (isSetTrait) {
1002
1024
  const value = process.argv.slice(setIndex + 2).join(' ').trim();
1003
1025
 
1004
1026
  if (!key || !value) {
1005
- console.error("āŒ Error: Missing key or value.");
1027
+ console.error(`${icon("fail")} Error: Missing key or value.`);
1006
1028
  console.error(" Usage: metame set-trait identity.role \"New Role\"");
1007
1029
  process.exit(1);
1008
1030
  }
@@ -1028,14 +1050,14 @@ if (isSetTrait) {
1028
1050
 
1029
1051
  fs.writeFileSync(BRAIN_FILE, yaml.dump(doc), 'utf8');
1030
1052
 
1031
- console.log(`🧠 MetaMe Brain Surgically Updated.`);
1053
+ console.log(`${icon("brain")} MetaMe Brain Surgically Updated.`);
1032
1054
  console.log(` Set \`${key}\` = "${value}"`);
1033
1055
  console.log(" (Run 'metame refresh' to apply this to the current session)");
1034
1056
  } else {
1035
- console.error("āŒ Error: No profile found.");
1057
+ console.error(`${icon("fail")} Error: No profile found.`);
1036
1058
  }
1037
1059
  } catch (e) {
1038
- console.error("āŒ Error updating profile:", e.message);
1060
+ console.error(`${icon("fail")} Error updating profile:`, e.message);
1039
1061
  }
1040
1062
  process.exit(0);
1041
1063
  }
@@ -1052,9 +1074,9 @@ if (isQuiet) {
1052
1074
  if (!doc.growth) doc.growth = {};
1053
1075
  doc.growth.quiet_until = new Date(Date.now() + 48 * 60 * 60 * 1000).toISOString();
1054
1076
  fs.writeFileSync(BRAIN_FILE, yaml.dump(doc, { lineWidth: -1 }), 'utf8');
1055
- console.log("🤫 MetaMe: Mirror & reflections silenced for 48 hours.");
1077
+ console.log(`${icon("brain")} MetaMe: Mirror & reflections silenced for 48 hours.`);
1056
1078
  } catch (e) {
1057
- console.error("āŒ Error:", e.message);
1079
+ console.error(`${icon("fail")} Error:`, e.message);
1058
1080
  }
1059
1081
  process.exit(0);
1060
1082
  }
@@ -1068,26 +1090,26 @@ if (isInsights) {
1068
1090
  const zoneHistory = (doc.growth && doc.growth.zone_history) || [];
1069
1091
 
1070
1092
  if (patterns.length === 0) {
1071
- console.log("šŸ” MetaMe: No patterns detected yet. Keep using MetaMe and patterns will emerge after ~5 sessions.");
1093
+ console.log(`${icon("search")} MetaMe: No patterns detected yet. Keep using MetaMe and patterns will emerge after ~5 sessions.`);
1072
1094
  } else {
1073
- console.log("šŸŖž MetaMe Insights:\n");
1095
+ console.log(`${icon("mirror")} MetaMe Insights:\n`);
1074
1096
  patterns.forEach((p, i) => {
1075
- const icon = p.type === 'avoidance' ? 'āš ļø' : p.type === 'growth' ? '🌱' : p.type === 'energy' ? '⚔' : 'šŸ”„';
1076
- console.log(` ${icon} [${p.type}] ${p.summary} (confidence: ${(p.confidence * 100).toFixed(0)}%)`);
1097
+ const sym = p.type === 'avoidance' ? icon("warn") : p.type === 'growth' ? '+' : p.type === 'energy' ? '*' : icon("reload");
1098
+ console.log(` ${sym} [${p.type}] ${p.summary} (confidence: ${(p.confidence * 100).toFixed(0)}%)`);
1077
1099
  console.log(` Detected: ${p.detected}${p.surfaced ? `, Last shown: ${p.surfaced}` : ''}`);
1078
1100
  });
1079
1101
  if (zoneHistory.length > 0) {
1080
- console.log(`\n šŸ“Š Recent zone history: ${zoneHistory.join(' → ')}`);
1102
+ console.log(`\n ${icon("chart")} Recent zone history: ${zoneHistory.join(' → ')}`);
1081
1103
  console.log(` (C=Comfort, S=Stretch, P=Panic)`);
1082
1104
  }
1083
1105
  const answered = (doc.growth && doc.growth.reflections_answered) || 0;
1084
1106
  const skipped = (doc.growth && doc.growth.reflections_skipped) || 0;
1085
1107
  if (answered + skipped > 0) {
1086
- console.log(`\n šŸ’­ Reflections: ${answered} answered, ${skipped} skipped`);
1108
+ console.log(`\n ${icon("thought")} Reflections: ${answered} answered, ${skipped} skipped`);
1087
1109
  }
1088
1110
  }
1089
1111
  } catch (e) {
1090
- console.error("āŒ Error:", e.message);
1112
+ console.error(`${icon("fail")} Error:`, e.message);
1091
1113
  }
1092
1114
  process.exit(0);
1093
1115
  }
@@ -1098,7 +1120,7 @@ if (isMirror) {
1098
1120
  const mirrorIndex = process.argv.indexOf('mirror');
1099
1121
  const toggle = process.argv[mirrorIndex + 1];
1100
1122
  if (toggle !== 'on' && toggle !== 'off') {
1101
- console.error("āŒ Usage: metame mirror on|off");
1123
+ console.error(`${icon("fail")} Usage: metame mirror on|off`);
1102
1124
  process.exit(1);
1103
1125
  }
1104
1126
  try {
@@ -1106,9 +1128,9 @@ if (isMirror) {
1106
1128
  if (!doc.growth) doc.growth = {};
1107
1129
  doc.growth.mirror_enabled = (toggle === 'on');
1108
1130
  fs.writeFileSync(BRAIN_FILE, yaml.dump(doc, { lineWidth: -1 }), 'utf8');
1109
- console.log(`šŸŖž MetaMe: Mirror ${toggle === 'on' ? 'enabled' : 'disabled'}.`);
1131
+ console.log(`${icon("mirror")} MetaMe: Mirror ${toggle === 'on' ? 'enabled' : 'disabled'}.`);
1110
1132
  } catch (e) {
1111
- console.error("āŒ Error:", e.message);
1133
+ console.error(`${icon("fail")} Error:`, e.message);
1112
1134
  }
1113
1135
  process.exit(0);
1114
1136
  }
@@ -1124,7 +1146,7 @@ if (isProvider) {
1124
1146
 
1125
1147
  if (!subCmd || subCmd === 'list') {
1126
1148
  const active = providers.getActiveProvider();
1127
- console.log(`šŸ”Œ MetaMe Providers (active: ${active ? active.name : 'anthropic'})`);
1149
+ console.log(`${icon("plug")} MetaMe Providers (active: ${active ? active.name : 'anthropic'})`);
1128
1150
  console.log(providers.listFormatted());
1129
1151
  process.exit(0);
1130
1152
  }
@@ -1132,18 +1154,18 @@ if (isProvider) {
1132
1154
  if (subCmd === 'use') {
1133
1155
  const name = process.argv[providerIndex + 2];
1134
1156
  if (!name) {
1135
- console.error("āŒ Usage: metame provider use <name>");
1157
+ console.error(`${icon("fail")} Usage: metame provider use <name>`);
1136
1158
  process.exit(1);
1137
1159
  }
1138
1160
  try {
1139
1161
  providers.setActive(name);
1140
1162
  const p = providers.getActiveProvider();
1141
- console.log(`āœ… Provider switched → ${name} (${p.label || name})`);
1163
+ console.log(`${icon("ok")} Provider switched → ${name} (${p.label || name})`);
1142
1164
  if (name !== 'anthropic') {
1143
1165
  console.log(` Base URL: ${p.base_url || 'not set'}`);
1144
1166
  }
1145
1167
  } catch (e) {
1146
- console.error(`āŒ ${e.message}`);
1168
+ console.error(`${icon("fail")} ${e.message}`);
1147
1169
  process.exit(1);
1148
1170
  }
1149
1171
  process.exit(0);
@@ -1152,7 +1174,7 @@ if (isProvider) {
1152
1174
  if (subCmd === 'add') {
1153
1175
  const name = process.argv[providerIndex + 2];
1154
1176
  if (!name) {
1155
- console.error("āŒ Usage: metame provider add <name>");
1177
+ console.error(`${icon("fail")} Usage: metame provider add <name>`);
1156
1178
  process.exit(1);
1157
1179
  }
1158
1180
  const readline = require('readline');
@@ -1160,7 +1182,7 @@ if (isProvider) {
1160
1182
  const ask = (q) => new Promise(r => rl.question(q, r));
1161
1183
 
1162
1184
  (async () => {
1163
- console.log(`\nšŸ”Œ Add Provider: ${name}\n`);
1185
+ console.log(`\n${icon("plug")} Add Provider: ${name}\n`);
1164
1186
  console.log("The relay must accept Anthropic Messages API format.");
1165
1187
  console.log("(Most quality relays like OpenRouter, OneAPI, etc. support this.)\n");
1166
1188
 
@@ -1169,7 +1191,7 @@ if (isProvider) {
1169
1191
  const api_key = (await ask("API Key: ")).trim();
1170
1192
 
1171
1193
  if (!base_url) {
1172
- console.error("āŒ Base URL is required.");
1194
+ console.error(`${icon("fail")} Base URL is required.`);
1173
1195
  rl.close();
1174
1196
  process.exit(1);
1175
1197
  }
@@ -1180,10 +1202,10 @@ if (isProvider) {
1180
1202
 
1181
1203
  try {
1182
1204
  providers.addProvider(name, config);
1183
- console.log(`\nāœ… Provider "${name}" added.`);
1205
+ console.log(`\n${icon("ok")} Provider "${name}" added.`);
1184
1206
  console.log(` Switch to it: metame provider use ${name}`);
1185
1207
  } catch (e) {
1186
- console.error(`āŒ ${e.message}`);
1208
+ console.error(`${icon("fail")} ${e.message}`);
1187
1209
  }
1188
1210
  rl.close();
1189
1211
  process.exit(0);
@@ -1194,14 +1216,14 @@ if (isProvider) {
1194
1216
  if (subCmd === 'remove') {
1195
1217
  const name = process.argv[providerIndex + 2];
1196
1218
  if (!name) {
1197
- console.error("āŒ Usage: metame provider remove <name>");
1219
+ console.error(`${icon("fail")} Usage: metame provider remove <name>`);
1198
1220
  process.exit(1);
1199
1221
  }
1200
1222
  try {
1201
1223
  providers.removeProvider(name);
1202
- console.log(`āœ… Provider "${name}" removed.`);
1224
+ console.log(`${icon("ok")} Provider "${name}" removed.`);
1203
1225
  } catch (e) {
1204
- console.error(`āŒ ${e.message}`);
1226
+ console.error(`${icon("fail")} ${e.message}`);
1205
1227
  }
1206
1228
  process.exit(0);
1207
1229
  }
@@ -1210,15 +1232,15 @@ if (isProvider) {
1210
1232
  const role = process.argv[providerIndex + 2]; // distill | daemon
1211
1233
  const name = process.argv[providerIndex + 3]; // provider name or empty to clear
1212
1234
  if (!role) {
1213
- console.error("āŒ Usage: metame provider set-role <distill|daemon> [provider-name]");
1235
+ console.error(`${icon("fail")} Usage: metame provider set-role <distill|daemon> [provider-name]`);
1214
1236
  console.error(" Omit provider name to reset to active provider.");
1215
1237
  process.exit(1);
1216
1238
  }
1217
1239
  try {
1218
1240
  providers.setRole(role, name || null);
1219
- console.log(`āœ… ${role} provider ${name ? `set to "${name}"` : 'reset to active'}.`);
1241
+ console.log(`${icon("ok")} ${role} provider ${name ? `set to "${name}"` : 'reset to active'}.`);
1220
1242
  } catch (e) {
1221
- console.error(`āŒ ${e.message}`);
1243
+ console.error(`${icon("fail")} ${e.message}`);
1222
1244
  }
1223
1245
  process.exit(0);
1224
1246
  }
@@ -1229,11 +1251,11 @@ if (isProvider) {
1229
1251
  const name = targetName || prov.active;
1230
1252
  const p = prov.providers[name];
1231
1253
  if (!p) {
1232
- console.error(`āŒ Provider "${name}" not found.`);
1254
+ console.error(`${icon("fail")} Provider "${name}" not found.`);
1233
1255
  process.exit(1);
1234
1256
  }
1235
1257
 
1236
- console.log(`šŸ” Testing provider: ${name} (${p.label || name})`);
1258
+ console.log(`${icon("search")} Testing provider: ${name} (${p.label || name})`);
1237
1259
  if (name === 'anthropic') {
1238
1260
  console.log(" Using official Anthropic endpoint — testing via claude CLI...");
1239
1261
  } else {
@@ -1257,18 +1279,18 @@ if (isProvider) {
1257
1279
  const elapsed = Date.now() - start;
1258
1280
 
1259
1281
  if (result.includes('PROVIDER_OK')) {
1260
- console.log(` āœ… Connected (${elapsed}ms)`);
1282
+ console.log(` ${icon("ok")} Connected (${elapsed}ms)`);
1261
1283
  } else {
1262
- console.log(` āš ļø Response received (${elapsed}ms) but unexpected: ${result.slice(0, 80)}`);
1284
+ console.log(` ${icon("warn")} Response received (${elapsed}ms) but unexpected: ${result.slice(0, 80)}`);
1263
1285
  }
1264
1286
  } catch (e) {
1265
- console.error(` āŒ Failed: ${e.message.split('\n')[0]}`);
1287
+ console.error(` ${icon("fail")} Failed: ${e.message.split('\n')[0]}`);
1266
1288
  }
1267
1289
  process.exit(0);
1268
1290
  }
1269
1291
 
1270
1292
  // Unknown subcommand — show help
1271
- console.log("šŸ”Œ MetaMe Provider Commands:");
1293
+ console.log(`${icon("plug")} MetaMe Provider Commands:`);
1272
1294
  console.log(" metame provider — list providers");
1273
1295
  console.log(" metame provider use <name> — switch active provider");
1274
1296
  console.log(" metame provider add <name> — add a new provider");
@@ -1312,20 +1334,20 @@ if (isDaemon) {
1312
1334
  if (fs.existsSync(templateSrc)) {
1313
1335
  fs.copyFileSync(templateSrc, DAEMON_CONFIG);
1314
1336
  } else {
1315
- console.error("āŒ Template not found. Reinstall MetaMe.");
1337
+ console.error(`${icon("fail")} Template not found. Reinstall MetaMe.`);
1316
1338
  process.exit(1);
1317
1339
  }
1318
1340
  try { fs.chmodSync(METAME_DIR, 0o700); } catch { /* ignore on Windows */ }
1319
- console.log("āœ… Config created: ~/.metame/daemon.yaml\n");
1341
+ console.log(`${icon("ok")} Config created: ~/.metame/daemon.yaml\n`);
1320
1342
  } else {
1321
- console.log("āœ… Config exists: ~/.metame/daemon.yaml\n");
1343
+ console.log(`${icon("ok")} Config exists: ~/.metame/daemon.yaml\n`);
1322
1344
  }
1323
1345
 
1324
1346
  const yaml = require(path.join(__dirname, 'node_modules', 'js-yaml'));
1325
1347
  let cfg = yaml.load(fs.readFileSync(DAEMON_CONFIG, 'utf8')) || {};
1326
1348
 
1327
1349
  // --- Telegram Setup ---
1328
- console.log("━━━ šŸ“± Telegram Setup ━━━");
1350
+ console.log(`━━━ ${icon("phone")} Telegram Setup ━━━`);
1329
1351
  console.log("");
1330
1352
  console.log("Step 1: Create a Bot");
1331
1353
  console.log(" • Open Telegram app on your phone or desktop");
@@ -1370,21 +1392,21 @@ if (isDaemon) {
1370
1392
 
1371
1393
  if (chatIds.length > 0) {
1372
1394
  cfg.telegram.allowed_chat_ids = chatIds;
1373
- console.log(` āœ… Found chat ID(s): ${chatIds.join(', ')}`);
1395
+ console.log(` ${icon("ok")} Found chat ID(s): ${chatIds.join(', ')}`);
1374
1396
  } else {
1375
- console.log(" āš ļø No messages found. Make sure you messaged the bot.");
1397
+ console.log(` ${icon("warn")} No messages found. Make sure you messaged the bot.`);
1376
1398
  console.log(" You can set allowed_chat_ids manually in daemon.yaml later.");
1377
1399
  }
1378
1400
  } catch {
1379
- console.log(" āš ļø Could not fetch chat ID. Set it manually in daemon.yaml.");
1401
+ console.log(` ${icon("warn")} Could not fetch chat ID. Set it manually in daemon.yaml.`);
1380
1402
  }
1381
- console.log(" āœ… Telegram configured!\n");
1403
+ console.log(` ${icon("ok")} Telegram configured!\n`);
1382
1404
  } else {
1383
1405
  console.log(" Skipped.\n");
1384
1406
  }
1385
1407
 
1386
1408
  // --- Feishu Setup ---
1387
- console.log("━━━ šŸ“˜ Feishu (Lark) Setup ━━━");
1409
+ console.log(`━━━ ${icon("feishu")} Feishu (Lark) Setup ━━━`);
1388
1410
  console.log("");
1389
1411
  console.log("Step 1: Create an App");
1390
1412
  console.log(" • Go to: https://open.feishu.cn/app");
@@ -1428,7 +1450,7 @@ if (isDaemon) {
1428
1450
  cfg.feishu.app_id = feishuAppId;
1429
1451
  cfg.feishu.app_secret = feishuSecret;
1430
1452
  if (!cfg.feishu.allowed_chat_ids) cfg.feishu.allowed_chat_ids = [];
1431
- console.log(" āœ… Feishu configured!");
1453
+ console.log(` ${icon("ok")} Feishu configured!`);
1432
1454
  console.log(" Note: allowed_chat_ids is empty = deny all users.");
1433
1455
  console.log(" Add chat IDs to daemon.yaml or use /agent bind from target chat.\n");
1434
1456
  }
@@ -1438,7 +1460,7 @@ if (isDaemon) {
1438
1460
 
1439
1461
  // Write config
1440
1462
  fs.writeFileSync(DAEMON_CONFIG, yaml.dump(cfg, { lineWidth: -1 }), 'utf8');
1441
- console.log("━━━ āœ… Setup Complete ━━━");
1463
+ console.log(`━━━ ${icon("ok")} Setup Complete ━━━`);
1442
1464
  console.log(`Config saved: ${DAEMON_CONFIG}`);
1443
1465
  console.log("\nNext steps:");
1444
1466
  console.log(" metame start — start the daemon");
@@ -1455,7 +1477,7 @@ if (isDaemon) {
1455
1477
 
1456
1478
  if (subCmd === 'install-launchd') {
1457
1479
  if (process.platform !== 'darwin') {
1458
- console.error("āŒ launchd is macOS-only.");
1480
+ console.error(`${icon("fail")} launchd is macOS-only.`);
1459
1481
  process.exit(1);
1460
1482
  }
1461
1483
  const plistDir = path.join(HOME_DIR, 'Library', 'LaunchAgents');
@@ -1497,7 +1519,7 @@ if (isDaemon) {
1497
1519
  </dict>
1498
1520
  </plist>`;
1499
1521
  fs.writeFileSync(plistPath, plistContent, 'utf8');
1500
- console.log(`āœ… launchd plist installed: ${plistPath}`);
1522
+ console.log(`${icon("ok")} launchd plist installed: ${plistPath}`);
1501
1523
  console.log(" Load now: launchctl load " + plistPath);
1502
1524
  console.log(" Unload: launchctl unload " + plistPath);
1503
1525
  process.exit(0);
@@ -1505,7 +1527,7 @@ if (isDaemon) {
1505
1527
 
1506
1528
  if (subCmd === 'install-systemd') {
1507
1529
  if (process.platform === 'darwin') {
1508
- console.error("āŒ Use 'metame daemon install-launchd' on macOS.");
1530
+ console.error(`${icon("fail")} Use 'metame daemon install-launchd' on macOS.`);
1509
1531
  process.exit(1);
1510
1532
  }
1511
1533
 
@@ -1513,7 +1535,7 @@ if (isDaemon) {
1513
1535
  try {
1514
1536
  require('child_process').execSync('systemctl --user --no-pager status 2>/dev/null || true');
1515
1537
  } catch {
1516
- console.error("āŒ systemd not available.");
1538
+ console.error(`${icon("fail")} systemd not available.`);
1517
1539
  console.error(" WSL users: add [boot]\\nsystemd=true to /etc/wsl.conf, then restart WSL.");
1518
1540
  process.exit(1);
1519
1541
  }
@@ -1553,7 +1575,7 @@ WantedBy=default.target
1553
1575
  // Enable lingering so service runs even when user is not logged in
1554
1576
  try { es(`loginctl enable-linger ${process.env.USER || ''}`); } catch { /* may need root */ }
1555
1577
 
1556
- console.log(`āœ… systemd service installed: ${servicePath}`);
1578
+ console.log(`${icon("ok")} systemd service installed: ${servicePath}`);
1557
1579
  console.log(" Status: systemctl --user status metame-daemon");
1558
1580
  console.log(" Logs: journalctl --user -u metame-daemon -f");
1559
1581
  console.log(" Disable: systemctl --user disable metame-daemon");
@@ -1562,7 +1584,7 @@ WantedBy=default.target
1562
1584
  const isWSL = fs.existsSync('/proc/version') &&
1563
1585
  fs.readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft');
1564
1586
  if (isWSL) {
1565
- console.log("\n šŸ“Œ WSL auto-boot tip:");
1587
+ console.log(`\n ${icon("pin")} WSL auto-boot tip:`);
1566
1588
  console.log(" Add this to Windows Task Scheduler (run at login):");
1567
1589
  console.log(` wsl -d ${process.env.WSL_DISTRO_NAME || 'Ubuntu'} -- sh -c 'nohup sleep infinity &'`);
1568
1590
  console.log(" This keeps WSL alive so the daemon stays running.");
@@ -1570,17 +1592,44 @@ WantedBy=default.target
1570
1592
  process.exit(0);
1571
1593
  }
1572
1594
 
1595
+ if (subCmd === 'install-task-scheduler') {
1596
+ if (process.platform !== 'win32') {
1597
+ console.error("Task Scheduler is Windows-only. Use install-launchd (macOS) or install-systemd (Linux).");
1598
+ process.exit(1);
1599
+ }
1600
+ const nodePath = process.execPath;
1601
+ const taskName = 'MetaMe-Daemon';
1602
+ const scriptPath = DAEMON_SCRIPT.replace(/\//g, '\\');
1603
+ const nodePathWin = nodePath.replace(/\//g, '\\');
1604
+ try {
1605
+ try {
1606
+ execSync(`schtasks /delete /tn "${taskName}" /f`, { stdio: 'ignore' });
1607
+ } catch { /* task may not exist yet */ }
1608
+ execSync(
1609
+ `schtasks /create /tn "${taskName}" /tr "\\"${nodePathWin}\\" \\"${scriptPath}\\"" /sc onlogon /rl limited /f`,
1610
+ { stdio: 'inherit' }
1611
+ );
1612
+ console.log(`Task Scheduler task "${taskName}" installed.`);
1613
+ console.log(` The daemon will auto-start at login.`);
1614
+ console.log(` Remove: schtasks /delete /tn "${taskName}" /f`);
1615
+ console.log(` Query: schtasks /query /tn "${taskName}"`);
1616
+ } catch (e) {
1617
+ console.error(`Failed to create scheduled task: ${e.message}`);
1618
+ console.error(" Try running as Administrator, or create it manually in Task Scheduler.");
1619
+ process.exit(1);
1620
+ }
1621
+ process.exit(0);
1622
+ }
1623
+
1573
1624
  if (subCmd === 'start') {
1574
1625
  // Kill any lingering daemon.js processes to avoid Feishu WebSocket conflicts
1575
1626
  try {
1576
- const { execSync: es } = require('child_process');
1577
- const pids = es("pgrep -f 'node.*daemon\\.js' 2>/dev/null || true", { encoding: 'utf8' }).trim();
1578
- if (pids) {
1579
- for (const p of pids.split('\n').filter(Boolean)) {
1580
- const n = parseInt(p, 10);
1581
- if (n && n !== process.pid) try { process.kill(n, 'SIGKILL'); } catch { /* */ }
1627
+ const pids = findProcessesByPattern('node.*daemon\\.js');
1628
+ if (pids.length) {
1629
+ for (const n of pids) {
1630
+ try { process.kill(n, 'SIGKILL'); } catch { /* */ }
1582
1631
  }
1583
- es('sleep 1');
1632
+ sleepSync(1000);
1584
1633
  }
1585
1634
  } catch { /* ignore */ }
1586
1635
  // Check if already running
@@ -1588,25 +1637,26 @@ WantedBy=default.target
1588
1637
  try { fs.unlinkSync(DAEMON_PID); } catch { /* */ }
1589
1638
  }
1590
1639
  if (!fs.existsSync(DAEMON_CONFIG)) {
1591
- console.error("āŒ No config found. Run: metame daemon init");
1640
+ console.error(`${icon("fail")} No config found. Run: metame daemon init`);
1592
1641
  process.exit(1);
1593
1642
  }
1594
1643
  if (!fs.existsSync(DAEMON_SCRIPT)) {
1595
- console.error("āŒ daemon.js not found. Reinstall MetaMe.");
1644
+ console.error(`${icon("fail")} daemon.js not found. Reinstall MetaMe.`);
1596
1645
  process.exit(1);
1597
1646
  }
1598
- // Use caffeinate on macOS/Linux to prevent sleep while daemon is running
1599
- const isNotWindows = process.platform !== 'win32';
1600
- const cmd = isNotWindows ? 'caffeinate' : process.execPath;
1601
- const args = isNotWindows ? ['-i', process.execPath, DAEMON_SCRIPT] : [DAEMON_SCRIPT];
1647
+ // Use caffeinate on macOS to prevent sleep while daemon is running
1648
+ const isMac = process.platform === 'darwin';
1649
+ const isWin = process.platform === 'win32';
1650
+ const cmd = isMac ? 'caffeinate' : process.execPath;
1651
+ const args = isMac ? ['-i', process.execPath, DAEMON_SCRIPT] : [DAEMON_SCRIPT];
1602
1652
  const bg = spawn(cmd, args, {
1603
- detached: true,
1653
+ detached: !isWin,
1604
1654
  stdio: 'ignore',
1605
1655
  windowsHide: true,
1606
1656
  env: { ...process.env, HOME: HOME_DIR, METAME_ROOT: __dirname },
1607
1657
  });
1608
1658
  bg.unref();
1609
- console.log(`āœ… MetaMe daemon started (PID: ${bg.pid})`);
1659
+ console.log(`${icon("ok")} MetaMe daemon started (PID: ${bg.pid})`);
1610
1660
  console.log(" Logs: metame logs");
1611
1661
  console.log(" Stop: metame stop");
1612
1662
  process.exit(0);
@@ -1614,7 +1664,7 @@ WantedBy=default.target
1614
1664
 
1615
1665
  if (subCmd === 'stop') {
1616
1666
  if (!fs.existsSync(DAEMON_PID)) {
1617
- console.log("ā„¹ļø No daemon running (no PID file).");
1667
+ console.log(`${icon("info")} No daemon running (no PID file).`);
1618
1668
  process.exit(0);
1619
1669
  }
1620
1670
  const pid = parseInt(fs.readFileSync(DAEMON_PID, 'utf8').trim(), 10);
@@ -1623,16 +1673,15 @@ WantedBy=default.target
1623
1673
  // Wait for process to die (up to 3s), then force kill
1624
1674
  let dead = false;
1625
1675
  for (let i = 0; i < 6; i++) {
1626
- const { execSync: es } = require('child_process');
1627
- es('sleep 0.5');
1676
+ sleepSync(500);
1628
1677
  try { process.kill(pid, 0); } catch { dead = true; break; }
1629
1678
  }
1630
1679
  if (!dead) {
1631
1680
  try { process.kill(pid, 'SIGKILL'); } catch { /* already gone */ }
1632
1681
  }
1633
- console.log(`āœ… Daemon stopped (PID: ${pid})`);
1682
+ console.log(`${icon("ok")} Daemon stopped (PID: ${pid})`);
1634
1683
  } catch (e) {
1635
- console.log(`āš ļø Process ${pid} not found (may have already exited).`);
1684
+ console.log(`${icon("warn")} Process ${pid} not found (may have already exited).`);
1636
1685
  }
1637
1686
  try { fs.unlinkSync(DAEMON_PID); } catch { /* ignore */ }
1638
1687
  process.exit(0);
@@ -1649,7 +1698,7 @@ WantedBy=default.target
1649
1698
  try { process.kill(pid, 0); isRunning = true; } catch { /* dead */ }
1650
1699
  }
1651
1700
 
1652
- console.log(`šŸ¤– MetaMe Daemon: ${isRunning ? '🟢 Running' : 'šŸ”“ Stopped'}`);
1701
+ console.log(`${icon("bot")} MetaMe Daemon: ${isRunning ? icon("green") + ' Running' : icon("red") + ' Stopped'}`);
1653
1702
  if (state.started_at) console.log(` Started: ${state.started_at}`);
1654
1703
  if (state.pid) console.log(` PID: ${state.pid}`);
1655
1704
 
@@ -1677,8 +1726,8 @@ WantedBy=default.target
1677
1726
  if (taskEntries.length > 0) {
1678
1727
  console.log(" Recent tasks:");
1679
1728
  for (const [name, info] of taskEntries) {
1680
- const icon = info.status === 'success' ? 'āœ…' : 'āŒ';
1681
- console.log(` ${icon} ${name}: ${info.last_run || 'unknown'}`);
1729
+ const sym = info.status === 'success' ? icon("ok") : icon("fail");
1730
+ console.log(` ${sym} ${name}: ${info.last_run || 'unknown'}`);
1682
1731
  if (info.output_preview) console.log(` ${info.output_preview.slice(0, 80)}...`);
1683
1732
  }
1684
1733
  const hiddenStale = Object.keys(tasks).length - taskEntries.length;
@@ -1691,7 +1740,7 @@ WantedBy=default.target
1691
1740
 
1692
1741
  if (subCmd === 'logs') {
1693
1742
  if (!fs.existsSync(DAEMON_LOG)) {
1694
- console.log("ā„¹ļø No log file yet. Start the daemon first.");
1743
+ console.log(`${icon("info")} No log file yet. Start the daemon first.`);
1695
1744
  process.exit(0);
1696
1745
  }
1697
1746
  const content = fs.readFileSync(DAEMON_LOG, 'utf8');
@@ -1704,11 +1753,11 @@ WantedBy=default.target
1704
1753
  if (subCmd === 'run') {
1705
1754
  const taskName = process.argv[daemonIndex + 2];
1706
1755
  if (!taskName) {
1707
- console.error("āŒ Usage: metame daemon run <task-name>");
1756
+ console.error(`${icon("fail")} Usage: metame daemon run <task-name>`);
1708
1757
  process.exit(1);
1709
1758
  }
1710
1759
  if (!fs.existsSync(DAEMON_SCRIPT)) {
1711
- console.error("āŒ daemon.js not found. Reinstall MetaMe.");
1760
+ console.error(`${icon("fail")} daemon.js not found. Reinstall MetaMe.`);
1712
1761
  process.exit(1);
1713
1762
  }
1714
1763
  // Run in foreground using daemon.js --run
@@ -1721,7 +1770,7 @@ WantedBy=default.target
1721
1770
  }
1722
1771
 
1723
1772
  // Unknown subcommand
1724
- console.log("šŸ“– MetaMe Daemon Commands:");
1773
+ console.log(`${icon("book")} MetaMe Daemon Commands:`);
1725
1774
  console.log(" metame start — start background daemon");
1726
1775
  console.log(" metame stop — stop daemon");
1727
1776
  console.log(" metame status — show status & budget");
@@ -1729,9 +1778,11 @@ WantedBy=default.target
1729
1778
  console.log(" metame daemon init — initialize config");
1730
1779
  console.log(" metame daemon run <name> — run a task once");
1731
1780
  if (process.platform === 'darwin') {
1732
- console.log(" metame daemon install-launchd — auto-start on macOS");
1781
+ console.log(" metame daemon install-launchd — auto-start on macOS");
1782
+ } else if (process.platform === 'win32') {
1783
+ console.log(" metame daemon install-task-scheduler — auto-start on Windows");
1733
1784
  } else {
1734
- console.log(" metame daemon install-systemd — auto-start on Linux/WSL");
1785
+ console.log(" metame daemon install-systemd — auto-start on Linux/WSL");
1735
1786
  }
1736
1787
  process.exit(0);
1737
1788
  }
@@ -1780,7 +1831,7 @@ if (isSync) {
1780
1831
  process.exit(1);
1781
1832
  }
1782
1833
 
1783
- console.log(`\nšŸ”„ Resuming session ${bestSession.id.slice(0, 8)}...\n`);
1834
+ console.log(`\n${icon("reload")} Resuming session ${bestSession.id.slice(0, 8)}...\n`);
1784
1835
  const providerEnv = (() => { try { return require(path.join(__dirname, 'scripts', 'providers.js')).buildActiveEnv(); } catch { return {}; } })();
1785
1836
  const resumeArgs = ['--resume', bestSession.id];
1786
1837
  if (daemonCfg.dangerously_skip_permissions) resumeArgs.push('--dangerously-skip-permissions');
@@ -1801,7 +1852,7 @@ if (isSync) {
1801
1852
  // We rely on our own scoped variable to detect nesting,
1802
1853
  // ignoring the leaky CLAUDE_CODE_SSE_PORT from IDEs.
1803
1854
  if (process.env.METAME_ACTIVE_SESSION === 'true') {
1804
- console.error("\n🚫 ACTION BLOCKED: Nested Session Detected");
1855
+ console.error(`\n${icon("stop")} ACTION BLOCKED: Nested Session Detected`);
1805
1856
  console.error(" You are actively running inside a MetaMe session.");
1806
1857
  console.error(" To reload configuration, use: \x1b[36m!metame refresh\x1b[0m\n");
1807
1858
  process.exit(1);
@@ -1814,7 +1865,7 @@ if (process.env.METAME_ACTIVE_SESSION === 'true') {
1814
1865
  const activeProviderEnv = (() => { try { return require(path.join(__dirname, 'scripts', 'providers.js')).buildActiveEnv(); } catch { return {}; } })();
1815
1866
  const activeProviderName = (() => { try { return require(path.join(__dirname, 'scripts', 'providers.js')).getActiveName(); } catch { return 'anthropic'; } })();
1816
1867
  if (activeProviderName !== 'anthropic') {
1817
- console.log(`šŸ”Œ Provider: ${activeProviderName}`);
1868
+ console.log(`${icon("plug")} Provider: ${activeProviderName}`);
1818
1869
  }
1819
1870
 
1820
1871
  // Build launch args — inject system prompt for new users
@@ -1873,17 +1924,18 @@ try {
1873
1924
  } catch { /* PID file stale, daemon not running */ }
1874
1925
  }
1875
1926
  if (!daemonRunning) {
1876
- const isNotWindows = process.platform !== 'win32';
1877
- const dCmd = isNotWindows ? 'caffeinate' : process.execPath;
1878
- const dArgs = isNotWindows ? ['-i', process.execPath, _daemonScript] : [_daemonScript];
1927
+ const _isMac = process.platform === 'darwin';
1928
+ const _isWin = process.platform === 'win32';
1929
+ const dCmd = _isMac ? 'caffeinate' : process.execPath;
1930
+ const dArgs = _isMac ? ['-i', process.execPath, _daemonScript] : [_daemonScript];
1879
1931
  const bg = spawn(dCmd, dArgs, {
1880
- detached: true,
1932
+ detached: !_isWin,
1881
1933
  stdio: 'ignore',
1882
1934
  windowsHide: true,
1883
1935
  env: { ...process.env, HOME: HOME_DIR, METAME_ROOT: __dirname },
1884
1936
  });
1885
1937
  bg.unref();
1886
- console.log(`šŸ¤– Daemon auto-started (PID: ${bg.pid})`);
1938
+ console.log(`${icon("bot")} Daemon auto-started (PID: ${bg.pid})`);
1887
1939
  }
1888
1940
  }
1889
1941
  } catch { /* non-fatal */ }
@@ -1895,7 +1947,7 @@ const child = spawnClaude(launchArgs, {
1895
1947
  });
1896
1948
 
1897
1949
  child.on('error', () => {
1898
- console.error("\nāŒ Error: Could not launch 'claude'.");
1950
+ console.error(`\n${icon("fail")} Error: Could not launch 'claude'.`);
1899
1951
  console.error(" Please make sure Claude Code is installed globally:");
1900
1952
  console.error(" npm install -g @anthropic-ai/claude-code");
1901
1953
  });