metame-cli 1.5.19 → 1.5.21

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.
Files changed (40) hide show
  1. package/index.js +157 -80
  2. package/package.json +2 -2
  3. package/scripts/bin/bootstrap-worktree.sh +20 -0
  4. package/scripts/core/audit.js +190 -0
  5. package/scripts/core/handoff.js +780 -0
  6. package/scripts/core/handoff.test.js +1074 -0
  7. package/scripts/core/memory-model.js +183 -0
  8. package/scripts/core/memory-model.test.js +486 -0
  9. package/scripts/core/reactive-paths.js +44 -0
  10. package/scripts/core/reactive-paths.test.js +35 -0
  11. package/scripts/core/reactive-prompt.js +51 -0
  12. package/scripts/core/reactive-prompt.test.js +88 -0
  13. package/scripts/core/reactive-signal.js +40 -0
  14. package/scripts/core/reactive-signal.test.js +88 -0
  15. package/scripts/core/thread-chat-id.js +52 -0
  16. package/scripts/core/thread-chat-id.test.js +113 -0
  17. package/scripts/daemon-bridges.js +92 -38
  18. package/scripts/daemon-claude-engine.js +373 -444
  19. package/scripts/daemon-command-router.js +82 -8
  20. package/scripts/daemon-engine-runtime.js +7 -10
  21. package/scripts/daemon-reactive-lifecycle.js +100 -33
  22. package/scripts/daemon-session-commands.js +133 -43
  23. package/scripts/daemon-session-store.js +300 -82
  24. package/scripts/daemon-team-dispatch.js +16 -16
  25. package/scripts/daemon.js +21 -175
  26. package/scripts/deploy-manifest.js +90 -0
  27. package/scripts/docs/maintenance-manual.md +14 -11
  28. package/scripts/docs/pointer-map.md +13 -4
  29. package/scripts/feishu-adapter.js +31 -27
  30. package/scripts/hooks/intent-engine.js +6 -3
  31. package/scripts/hooks/intent-memory-recall.js +1 -0
  32. package/scripts/hooks/intent-perpetual.js +1 -1
  33. package/scripts/memory-extract.js +5 -97
  34. package/scripts/memory-gc.js +35 -90
  35. package/scripts/memory-migrate-v2.js +304 -0
  36. package/scripts/memory-nightly-reflect.js +40 -41
  37. package/scripts/memory.js +340 -859
  38. package/scripts/migrate-reactive-paths.js +122 -0
  39. package/scripts/signal-capture.js +4 -0
  40. package/scripts/sync-plugin.js +56 -0
@@ -0,0 +1,122 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const os = require('os');
6
+ const { resolveReactivePaths, resolveLegacyPaths } = require('./core/reactive-paths');
7
+
8
+ /**
9
+ * Discover all project keys that have legacy reactive files.
10
+ * Scans memory/now/ for *_memory.md, *.md (state), and events/ for *.jsonl.
11
+ *
12
+ * @param {string} metameDir
13
+ * @returns {string[]} unique project keys
14
+ */
15
+ function discoverLegacyKeys(metameDir) {
16
+ const keys = new Set();
17
+
18
+ // Scan memory/now/ for <key>_memory.md and <key>.md (state files)
19
+ const nowDir = path.join(metameDir, 'memory', 'now');
20
+ if (fs.existsSync(nowDir)) {
21
+ for (const file of fs.readdirSync(nowDir)) {
22
+ if (file === 'shared.md') continue; // skip shared.md
23
+ const memMatch = file.match(/^(.+)_memory\.md$/);
24
+ if (memMatch) { keys.add(memMatch[1]); continue; }
25
+ const l2Match = file.match(/^(.+)_l2cache\.md$/);
26
+ if (l2Match) { keys.add(l2Match[1]); continue; }
27
+ // State files: <key>.md (but not <key>_memory.md or <key>_l2cache.md)
28
+ if (file.endsWith('.md') && !file.endsWith('_memory.md') && !file.endsWith('_l2cache.md')) {
29
+ keys.add(file.replace(/\.md$/, ''));
30
+ }
31
+ }
32
+ }
33
+
34
+ // Scan events/ for <key>.jsonl
35
+ const evDir = path.join(metameDir, 'events');
36
+ if (fs.existsSync(evDir)) {
37
+ for (const file of fs.readdirSync(evDir)) {
38
+ if (file.endsWith('.jsonl')) {
39
+ keys.add(file.replace(/\.jsonl$/, ''));
40
+ }
41
+ }
42
+ }
43
+
44
+ // Scan memory/agents/ for <key>_latest.md
45
+ const agentsDir = path.join(metameDir, 'memory', 'agents');
46
+ if (fs.existsSync(agentsDir)) {
47
+ for (const file of fs.readdirSync(agentsDir)) {
48
+ const latestMatch = file.match(/^(.+)_latest\.md$/);
49
+ if (latestMatch) keys.add(latestMatch[1]);
50
+ }
51
+ }
52
+
53
+ return [...keys];
54
+ }
55
+
56
+ /**
57
+ * Migrate legacy flat reactive files to per-project directory structure.
58
+ * Idempotent: skips files that already exist at the destination.
59
+ *
60
+ * @param {string} [metameDir] - defaults to ~/.metame
61
+ * @returns {{ migrated: string[], skipped: string[], errors: string[] }}
62
+ */
63
+ function migrate(metameDir) {
64
+ metameDir = metameDir || path.join(os.homedir(), '.metame');
65
+ const report = { migrated: [], skipped: [], errors: [] };
66
+
67
+ const keys = discoverLegacyKeys(metameDir);
68
+ if (keys.length === 0) return report;
69
+
70
+ for (const key of keys) {
71
+ const legacy = resolveLegacyPaths(key, metameDir);
72
+ const target = resolveReactivePaths(key, metameDir);
73
+
74
+ // Create target directory
75
+ fs.mkdirSync(target.dir, { recursive: true });
76
+
77
+ const fileMap = [
78
+ { src: legacy.memory, dst: target.memory, label: 'memory' },
79
+ { src: legacy.l2cache, dst: target.l2cache, label: 'l2cache' },
80
+ { src: legacy.state, dst: target.state, label: 'state' },
81
+ { src: legacy.events, dst: target.events, label: 'events' },
82
+ { src: legacy.latest, dst: target.latest, label: 'latest' },
83
+ ];
84
+
85
+ for (const { src, dst, label } of fileMap) {
86
+ if (!fs.existsSync(src)) continue;
87
+ if (fs.existsSync(dst)) {
88
+ report.skipped.push(`${key}/${label} (target exists)`);
89
+ continue;
90
+ }
91
+ try {
92
+ fs.copyFileSync(src, dst);
93
+ fs.unlinkSync(src);
94
+ report.migrated.push(`${key}/${label}`);
95
+ } catch (e) {
96
+ report.errors.push(`${key}/${label}: ${e.message}`);
97
+ }
98
+ }
99
+ }
100
+
101
+ return report;
102
+ }
103
+
104
+ // CLI mode
105
+ if (require.main === module) {
106
+ const metameDir = process.argv[2] || path.join(os.homedir(), '.metame');
107
+ console.log(`Migrating reactive paths in ${metameDir}...`);
108
+ const report = migrate(metameDir);
109
+ console.log(`Migrated: ${report.migrated.length}`);
110
+ for (const m of report.migrated) console.log(` + ${m}`);
111
+ if (report.skipped.length) {
112
+ console.log(`Skipped: ${report.skipped.length}`);
113
+ for (const s of report.skipped) console.log(` ~ ${s}`);
114
+ }
115
+ if (report.errors.length) {
116
+ console.log(`Errors: ${report.errors.length}`);
117
+ for (const e of report.errors) console.log(` ! ${e}`);
118
+ process.exitCode = 1;
119
+ }
120
+ }
121
+
122
+ module.exports = { migrate, discoverLegacyKeys };
@@ -8,6 +8,10 @@
8
8
  * persistent preferences/identity signals, appends to buffer.
9
9
  */
10
10
 
11
+ // Global safety net: hooks must NEVER crash or exit non-zero
12
+ process.on('uncaughtException', () => process.exit(0));
13
+ process.on('unhandledRejection', () => process.exit(0));
14
+
11
15
  const fs = require('fs');
12
16
  const path = require('path');
13
17
  const os = require('os');
@@ -0,0 +1,56 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ const { collectDeployGroups } = require('./deploy-manifest');
7
+
8
+ function syncDirFiles(srcDir, destDir, { fileList, chmod } = {}) {
9
+ if (!fs.existsSync(srcDir)) return false;
10
+ if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
11
+ let updated = false;
12
+ const files = fileList || fs.readdirSync(srcDir).filter((f) => fs.statSync(path.join(srcDir, f)).isFile());
13
+ for (const file of files) {
14
+ const src = path.join(srcDir, file);
15
+ const dest = path.join(destDir, file);
16
+ if (!fs.existsSync(src)) continue;
17
+ const srcContent = fs.readFileSync(src, 'utf8');
18
+ const destContent = fs.existsSync(dest) ? fs.readFileSync(dest, 'utf8') : '';
19
+ if (srcContent !== destContent) {
20
+ fs.writeFileSync(dest, srcContent, 'utf8');
21
+ if (chmod) {
22
+ try { fs.chmodSync(dest, chmod); } catch { /* ignore */ }
23
+ }
24
+ updated = true;
25
+ }
26
+ }
27
+ return updated;
28
+ }
29
+
30
+ function syncPluginScripts(projectRoot = process.cwd()) {
31
+ const scriptsDir = path.join(projectRoot, 'scripts');
32
+ const pluginScriptsDir = path.join(projectRoot, 'plugin', 'scripts');
33
+ const deployGroups = collectDeployGroups(fs, path, scriptsDir, {
34
+ excludedScripts: new Set(['sync-readme.js', 'test_daemon.js', 'daemon.yaml']),
35
+ includeNestedDirs: ['core'],
36
+ });
37
+
38
+ let updated = false;
39
+ for (const group of deployGroups) {
40
+ const destDir = group.destSubdir ? path.join(pluginScriptsDir, group.destSubdir) : pluginScriptsDir;
41
+ updated = syncDirFiles(group.srcDir, destDir, { fileList: group.fileList }) || updated;
42
+ }
43
+
44
+ updated = syncDirFiles(path.join(scriptsDir, 'hooks'), path.join(pluginScriptsDir, 'hooks')) || updated;
45
+ return updated;
46
+ }
47
+
48
+ if (require.main === module) {
49
+ syncPluginScripts(process.cwd());
50
+ console.log('Plugin scripts synced');
51
+ }
52
+
53
+ module.exports = {
54
+ syncPluginScripts,
55
+ syncDirFiles,
56
+ };