metame-cli 1.5.4 → 1.5.6
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 +6 -1
- package/index.js +277 -55
- package/package.json +3 -2
- package/scripts/agent-layer.js +4 -2
- package/scripts/bin/dispatch_to +18 -6
- package/scripts/bin/push-clean.sh +72 -0
- package/scripts/daemon-admin-commands.js +266 -64
- package/scripts/daemon-agent-commands.js +188 -66
- package/scripts/daemon-bridges.js +475 -50
- package/scripts/daemon-checkpoints.js +84 -30
- package/scripts/daemon-claude-engine.js +651 -103
- package/scripts/daemon-command-router.js +134 -27
- package/scripts/daemon-command-session-route.js +118 -0
- package/scripts/daemon-default.yaml +2 -0
- package/scripts/daemon-dispatch-cards.js +185 -0
- package/scripts/daemon-engine-runtime.js +96 -20
- package/scripts/daemon-exec-commands.js +106 -50
- package/scripts/daemon-file-browser.js +63 -7
- package/scripts/daemon-notify.js +18 -4
- package/scripts/daemon-ops-commands.js +28 -6
- package/scripts/daemon-remote-dispatch.js +34 -2
- package/scripts/daemon-session-commands.js +102 -45
- package/scripts/daemon-session-store.js +497 -66
- package/scripts/daemon-siri-bridge.js +234 -0
- package/scripts/daemon-siri-imessage.js +209 -0
- package/scripts/daemon-task-scheduler.js +10 -2
- package/scripts/{team-dispatch.js → daemon-team-dispatch.js} +150 -11
- package/scripts/daemon.js +484 -181
- package/scripts/docs/hook-config.md +7 -4
- package/scripts/docs/maintenance-manual.md +10 -3
- package/scripts/docs/pointer-map.md +2 -2
- package/scripts/feishu-adapter.js +7 -15
- package/scripts/hooks/doc-router.js +29 -0
- package/scripts/hooks/intent-doc-router.js +54 -0
- package/scripts/hooks/intent-engine.js +9 -40
- package/scripts/intent-registry.js +59 -0
- package/scripts/memory-extract.js +59 -0
- package/scripts/mentor-engine.js +6 -0
- package/scripts/schema.js +1 -0
- package/scripts/self-reflect.js +110 -12
- package/scripts/session-analytics.js +160 -0
- package/scripts/signal-capture.js +1 -1
- package/scripts/hooks/intent-agent-manage.js +0 -50
- package/scripts/hooks/intent-hook-config.js +0 -28
|
@@ -895,6 +895,161 @@ function detectSignificantSession(skeleton) {
|
|
|
895
895
|
return { significant: reasons.length > 0, reasons };
|
|
896
896
|
}
|
|
897
897
|
|
|
898
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
899
|
+
// Codex session adapter
|
|
900
|
+
// Reads ~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl (first line only, ~1KB)
|
|
901
|
+
// and ~/.codex/history.jsonl (user messages). Reuses the same state DB with
|
|
902
|
+
// a 'codex_facts' key to avoid collisions with Claude session IDs.
|
|
903
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
904
|
+
|
|
905
|
+
const CODEX_SESSIONS_ROOT = path.join(HOME, '.codex', 'sessions');
|
|
906
|
+
const CODEX_HISTORY_FILE = path.join(HOME, '.codex', 'history.jsonl');
|
|
907
|
+
// Matches: rollout-YYYY-MM-DDTHH-MM-SS-<uuid>.jsonl (colons replaced with dashes)
|
|
908
|
+
const CODEX_ROLLOUT_PATTERN = /^rollout-\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}-(.+)\.jsonl$/;
|
|
909
|
+
|
|
910
|
+
/**
|
|
911
|
+
* Load ~/.codex/history.jsonl into a Map<session_id, [{ts, text}]>.
|
|
912
|
+
* Pass sessionIds to load only the sessions you need — avoids reading the
|
|
913
|
+
* whole file (which grows unbounded) when only a few sessions are relevant.
|
|
914
|
+
*
|
|
915
|
+
* @param {string[]|null} sessionIds - allowlist; null/empty loads everything
|
|
916
|
+
*/
|
|
917
|
+
function loadCodexHistory(sessionIds = null) {
|
|
918
|
+
const map = new Map();
|
|
919
|
+
const allow = sessionIds && sessionIds.length > 0 ? new Set(sessionIds) : null;
|
|
920
|
+
try {
|
|
921
|
+
if (!fs.existsSync(CODEX_HISTORY_FILE)) return map;
|
|
922
|
+
const lines = fs.readFileSync(CODEX_HISTORY_FILE, 'utf8').split('\n');
|
|
923
|
+
for (const line of lines) {
|
|
924
|
+
if (!line.trim()) continue;
|
|
925
|
+
let entry;
|
|
926
|
+
try { entry = JSON.parse(line); } catch { continue; }
|
|
927
|
+
if (!entry.session_id || !entry.text) continue;
|
|
928
|
+
if (allow && !allow.has(entry.session_id)) continue;
|
|
929
|
+
if (!map.has(entry.session_id)) map.set(entry.session_id, []);
|
|
930
|
+
map.get(entry.session_id).push({ ts: entry.ts, text: entry.text });
|
|
931
|
+
}
|
|
932
|
+
} catch { /* non-fatal */ }
|
|
933
|
+
return map;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
/**
|
|
937
|
+
* Find all Codex rollout files not yet processed by memory-extract.
|
|
938
|
+
* Filename pattern: rollout-YYYY-MM-DDTHH-MM-SS-<uuid>.jsonl
|
|
939
|
+
*/
|
|
940
|
+
function findAllUnextractedCodexSessions(limit = 30) {
|
|
941
|
+
if (!fs.existsSync(CODEX_SESSIONS_ROOT)) return [];
|
|
942
|
+
const results = [];
|
|
943
|
+
try {
|
|
944
|
+
const years = fs.readdirSync(CODEX_SESSIONS_ROOT).filter(d => /^\d{4}$/.test(d));
|
|
945
|
+
for (const year of years) {
|
|
946
|
+
const yearDir = path.join(CODEX_SESSIONS_ROOT, year);
|
|
947
|
+
const months = fs.readdirSync(yearDir).filter(d => /^\d{2}$/.test(d));
|
|
948
|
+
for (const month of months) {
|
|
949
|
+
const monthDir = path.join(yearDir, month);
|
|
950
|
+
const days = fs.readdirSync(monthDir).filter(d => /^\d{2}$/.test(d));
|
|
951
|
+
for (const day of days) {
|
|
952
|
+
const dayDir = path.join(monthDir, day);
|
|
953
|
+
let files;
|
|
954
|
+
try { files = fs.readdirSync(dayDir); } catch { continue; }
|
|
955
|
+
for (const file of files) {
|
|
956
|
+
if (!file.startsWith('rollout-') || !file.endsWith('.jsonl')) continue;
|
|
957
|
+
// Extract UUID from: rollout-YYYY-MM-DDTHH-MM-SS-<uuid>.jsonl
|
|
958
|
+
const m = file.match(CODEX_ROLLOUT_PATTERN);
|
|
959
|
+
if (!m) continue;
|
|
960
|
+
const sessionId = m[1];
|
|
961
|
+
if (isProcessed('codex_facts', sessionId)) continue;
|
|
962
|
+
const fullPath = path.join(dayDir, file);
|
|
963
|
+
let fstat;
|
|
964
|
+
try { fstat = fs.statSync(fullPath); } catch { continue; }
|
|
965
|
+
if (fstat.size < MIN_FILE_SIZE) continue;
|
|
966
|
+
results.push({ path: fullPath, session_id: sessionId, mtime: fstat.mtimeMs });
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
} catch { return []; }
|
|
972
|
+
results.sort((a, b) => b.mtime - a.mtime);
|
|
973
|
+
return results.slice(0, limit);
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
/**
|
|
977
|
+
* Build { skeleton, evidence } for a Codex session.
|
|
978
|
+
* Reads only the first 2KB of the rollout file (session_meta line) — never
|
|
979
|
+
* loads the full transcript. Enriches with user messages from historyMap.
|
|
980
|
+
*
|
|
981
|
+
* @param {string} rolloutPath - absolute path to rollout-*.jsonl
|
|
982
|
+
* @param {Map} historyMap - returned by loadCodexHistory()
|
|
983
|
+
*/
|
|
984
|
+
function buildCodexInput(rolloutPath, historyMap) {
|
|
985
|
+
let sessionMeta = null;
|
|
986
|
+
let fileSessionId = null;
|
|
987
|
+
try {
|
|
988
|
+
const m = path.basename(rolloutPath).match(CODEX_ROLLOUT_PATTERN);
|
|
989
|
+
if (m) fileSessionId = m[1];
|
|
990
|
+
|
|
991
|
+
// Read only first 2KB to get session_meta without loading the full transcript
|
|
992
|
+
let fd;
|
|
993
|
+
try {
|
|
994
|
+
fd = fs.openSync(rolloutPath, 'r');
|
|
995
|
+
const buf = Buffer.alloc(2048);
|
|
996
|
+
const bytesRead = fs.readSync(fd, buf, 0, 2048, 0);
|
|
997
|
+
const firstLine = buf.slice(0, bytesRead).toString('utf8').split('\n')[0];
|
|
998
|
+
const parsed = JSON.parse(firstLine);
|
|
999
|
+
if (parsed.type === 'session_meta') sessionMeta = parsed.payload;
|
|
1000
|
+
} finally {
|
|
1001
|
+
if (fd !== undefined) try { fs.closeSync(fd); } catch { /* ignore */ }
|
|
1002
|
+
}
|
|
1003
|
+
} catch { /* non-fatal */ }
|
|
1004
|
+
|
|
1005
|
+
const sessionId = (sessionMeta && sessionMeta.id) || fileSessionId;
|
|
1006
|
+
const cwd = (sessionMeta && sessionMeta.cwd) || null;
|
|
1007
|
+
const { project, project_id: projectId } = deriveProjectInfo(cwd || '');
|
|
1008
|
+
|
|
1009
|
+
// User messages from history index (sorted chronologically)
|
|
1010
|
+
const userMsgs = (sessionId && historyMap.get(sessionId)) || [];
|
|
1011
|
+
userMsgs.sort((a, b) => a.ts - b.ts);
|
|
1012
|
+
|
|
1013
|
+
const firstTs = userMsgs.length > 0 ? new Date(userMsgs[0].ts * 1000).toISOString() : null;
|
|
1014
|
+
const lastTs = userMsgs.length > 1 ? new Date(userMsgs[userMsgs.length - 1].ts * 1000).toISOString() : firstTs;
|
|
1015
|
+
const durationMin = userMsgs.length > 1
|
|
1016
|
+
? Math.round((userMsgs[userMsgs.length - 1].ts - userMsgs[0].ts) / 6) / 10
|
|
1017
|
+
: 0;
|
|
1018
|
+
|
|
1019
|
+
const skeleton = {
|
|
1020
|
+
session_id: sessionId || path.basename(rolloutPath, '.jsonl'),
|
|
1021
|
+
user_snippets: userMsgs.map(m => m.text.slice(0, 200)),
|
|
1022
|
+
tool_counts: {},
|
|
1023
|
+
total_tool_calls: 0,
|
|
1024
|
+
message_count: userMsgs.length,
|
|
1025
|
+
duration_min: durationMin,
|
|
1026
|
+
project: project || 'unknown',
|
|
1027
|
+
project_id: projectId || null,
|
|
1028
|
+
project_path: cwd,
|
|
1029
|
+
branch: null,
|
|
1030
|
+
engine: 'codex',
|
|
1031
|
+
model_provider: sessionMeta && sessionMeta.model_provider,
|
|
1032
|
+
first_ts: firstTs,
|
|
1033
|
+
last_ts: lastTs,
|
|
1034
|
+
};
|
|
1035
|
+
|
|
1036
|
+
const evidence = {
|
|
1037
|
+
user_messages: userMsgs.map(m => m.text).filter(Boolean).slice(0, 15),
|
|
1038
|
+
tool_traces: [],
|
|
1039
|
+
key_results: [],
|
|
1040
|
+
file_anchors: cwd ? [cwd] : [],
|
|
1041
|
+
};
|
|
1042
|
+
|
|
1043
|
+
return { skeleton, evidence };
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
/**
|
|
1047
|
+
* Mark a Codex session as facts-extracted.
|
|
1048
|
+
*/
|
|
1049
|
+
function markCodexFactsExtracted(sessionId) {
|
|
1050
|
+
markProcessed('codex_facts', sessionId);
|
|
1051
|
+
}
|
|
1052
|
+
|
|
898
1053
|
module.exports = {
|
|
899
1054
|
findLatestUnanalyzedSession,
|
|
900
1055
|
findSessionById,
|
|
@@ -908,6 +1063,11 @@ module.exports = {
|
|
|
908
1063
|
detectSignificantSession,
|
|
909
1064
|
markAnalyzed,
|
|
910
1065
|
markFactsExtracted,
|
|
1066
|
+
// Codex adapter
|
|
1067
|
+
loadCodexHistory,
|
|
1068
|
+
findAllUnextractedCodexSessions,
|
|
1069
|
+
buildCodexInput,
|
|
1070
|
+
markCodexFactsExtracted,
|
|
911
1071
|
};
|
|
912
1072
|
|
|
913
1073
|
// Direct execution for testing
|
|
@@ -26,7 +26,7 @@ const ABSOLUTE_MAX_CAPTURE_CHARS = 6000;
|
|
|
26
26
|
|
|
27
27
|
// Strong directive signals → high confidence (direct write to T3)
|
|
28
28
|
// Allow up to 6 chars between key words (e.g. "以后代码一律" = "以后" + "代码" + "一律")
|
|
29
|
-
const STRONG_SIGNAL_ZH = /以后.{0,6}(
|
|
29
|
+
const STRONG_SIGNAL_ZH = /以后.{0,6}(都|总是|一律|每次|全部|统一)|永远.{0,4}(不要|别|不能|要)|千万.{0,4}(别|不要)|记住|一定.{0,4}(要|得)|一律|统一用/;
|
|
30
30
|
const STRONG_SIGNAL_EN = /(from now on|always|never|don't ever|remember to|every time)/i;
|
|
31
31
|
|
|
32
32
|
// Implicit preference signals → normal confidence (needs accumulation)
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Agent Management Intent Module
|
|
5
|
-
*
|
|
6
|
-
* Detects when the user asks about creating, managing, or binding agents,
|
|
7
|
-
* or asks about code structure / upgrade progress.
|
|
8
|
-
*
|
|
9
|
-
* @param {string} prompt
|
|
10
|
-
* @returns {string|null}
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
const AGENT_MANAGE_PATTERNS = [
|
|
14
|
-
// Creating / binding / managing agents
|
|
15
|
-
/(?:创建|新建|添加|注册|绑定|配置|管理).{0,8}(?:agent|机器人|bot|智能体)/i,
|
|
16
|
-
/(?:agent|bot|智能体).{0,8}(?:创建|新建|添加|注册|绑定|配置|管理)/i,
|
|
17
|
-
// English
|
|
18
|
-
/\b(?:create|add|register|bind|manage|setup|configure)\s+(?:an?\s+)?agent\b/i,
|
|
19
|
-
];
|
|
20
|
-
|
|
21
|
-
const CODE_STRUCTURE_PATTERNS = [
|
|
22
|
-
// Code structure / upgrade / script entry questions
|
|
23
|
-
/(?:代码结构|脚本入口|升级进度|模块关系|文件结构)/,
|
|
24
|
-
/(?:pointer.?map|架构图|入口文件)/i,
|
|
25
|
-
];
|
|
26
|
-
|
|
27
|
-
module.exports = function detectAgentManage(prompt) {
|
|
28
|
-
const isManage = AGENT_MANAGE_PATTERNS.some(re => re.test(prompt));
|
|
29
|
-
const isStructure = CODE_STRUCTURE_PATTERNS.some(re => re.test(prompt));
|
|
30
|
-
|
|
31
|
-
if (!isManage && !isStructure) return null;
|
|
32
|
-
|
|
33
|
-
const hints = [];
|
|
34
|
-
|
|
35
|
-
if (isManage) {
|
|
36
|
-
hints.push(
|
|
37
|
-
'[Agent 管理提示]',
|
|
38
|
-
'- 创建/管理/绑定 Agent → 先 `cat ~/.metame/docs/agent-guide.md` 获取完整流程',
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (isStructure) {
|
|
43
|
-
hints.push(
|
|
44
|
-
'[代码结构提示]',
|
|
45
|
-
'- 代码结构/脚本入口/升级进度 → 先 `cat ~/.metame/docs/pointer-map.md` 获取索引',
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return hints.join('\n');
|
|
50
|
-
};
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Hook Config Intent Module
|
|
5
|
-
*
|
|
6
|
-
* Detects when the user asks about hook/intent engine configuration.
|
|
7
|
-
*
|
|
8
|
-
* @param {string} prompt
|
|
9
|
-
* @returns {string|null}
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const HOOK_PATTERNS = [
|
|
13
|
-
// Hook / intent configuration
|
|
14
|
-
/(?:hook|intent|意图).{0,10}(?:配置|设置|开关|新增|添加|修改|怎么配|怎么设置|怎么改)/i,
|
|
15
|
-
/(?:配置|设置|开关|新增|添加|修改).{0,10}(?:hook|intent|意图)/i,
|
|
16
|
-
// Specific intent engine topics
|
|
17
|
-
/intent.?engine/i,
|
|
18
|
-
/意图引擎|意图模块/,
|
|
19
|
-
];
|
|
20
|
-
|
|
21
|
-
module.exports = function detectHookConfig(prompt) {
|
|
22
|
-
if (!HOOK_PATTERNS.some(re => re.test(prompt))) return null;
|
|
23
|
-
|
|
24
|
-
return [
|
|
25
|
-
'[Intent Engine 配置提示]',
|
|
26
|
-
'- Hook/Intent 配置操作 → 先 `cat ~/.metame/docs/hook-config.md` 获取完整手册',
|
|
27
|
-
].join('\n');
|
|
28
|
-
};
|