metame-cli 1.5.11 → 1.5.13
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 +64 -7
- package/package.json +3 -2
- package/scripts/daemon-agent-commands.js +6 -2
- package/scripts/daemon-bridges.js +23 -9
- package/scripts/daemon-claude-engine.js +81 -28
- package/scripts/daemon-command-router.js +16 -0
- package/scripts/daemon-command-session-route.js +3 -1
- package/scripts/daemon-engine-runtime.js +1 -5
- package/scripts/daemon-message-pipeline.js +113 -44
- package/scripts/daemon-reactive-lifecycle.js +405 -9
- package/scripts/daemon-session-commands.js +3 -2
- package/scripts/daemon-session-store.js +82 -27
- package/scripts/daemon-team-dispatch.js +21 -5
- package/scripts/daemon-utils.js +3 -1
- package/scripts/daemon.js +1 -0
- package/scripts/docs/file-transfer.md +1 -0
- package/scripts/hooks/intent-file-transfer.js +2 -1
- package/scripts/hooks/intent-perpetual.js +109 -0
- package/scripts/hooks/intent-research.js +112 -0
- package/scripts/intent-registry.js +4 -0
- package/scripts/ops-mission-queue.js +258 -0
- package/scripts/ops-verifier.js +197 -0
- package/skills/agent-browser/SKILL.md +153 -0
- package/skills/agent-reach/SKILL.md +66 -0
- package/skills/agent-reach/evolution.json +13 -0
- package/skills/deep-research/SKILL.md +77 -0
- package/skills/find-skills/SKILL.md +133 -0
- package/skills/heartbeat-task-manager/SKILL.md +63 -0
- package/skills/macos-local-orchestrator/SKILL.md +192 -0
- package/skills/macos-local-orchestrator/agents/openai.yaml +4 -0
- package/skills/macos-local-orchestrator/references/tooling-landscape.md +70 -0
- package/skills/macos-mail-calendar/SKILL.md +394 -0
- package/skills/mcp-installer/SKILL.md +138 -0
- package/skills/skill-creator/LICENSE.txt +202 -0
- package/skills/skill-creator/README.md +72 -0
- package/skills/skill-creator/SKILL.md +96 -0
- package/skills/skill-creator/evolution.json +6 -0
- package/skills/skill-creator/references/creation-guide.md +116 -0
- package/skills/skill-creator/references/evolution-guide.md +74 -0
- package/skills/skill-creator/references/output-patterns.md +82 -0
- package/skills/skill-creator/references/workflows.md +28 -0
- package/skills/skill-creator/scripts/align_all.py +32 -0
- package/skills/skill-creator/scripts/auto_evolve_hook.js +247 -0
- package/skills/skill-creator/scripts/init_skill.py +303 -0
- package/skills/skill-creator/scripts/merge_evolution.py +70 -0
- package/skills/skill-creator/scripts/package_skill.py +110 -0
- package/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/skills/skill-creator/scripts/setup.py +141 -0
- package/skills/skill-creator/scripts/smart_stitch.py +82 -0
- package/skills/skill-manager/SKILL.md +112 -0
- package/skills/skill-manager/scripts/delete_skill.py +31 -0
- package/skills/skill-manager/scripts/list_skills.py +61 -0
- package/skills/skill-manager/scripts/scan_and_check.py +125 -0
- package/skills/skill-manager/scripts/sync_index.py +144 -0
- package/skills/skill-manager/scripts/update_helper.py +39 -0
|
@@ -7,21 +7,22 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Behavior:
|
|
9
9
|
* 1. First message → process immediately
|
|
10
|
-
* 2. Follow-up
|
|
11
|
-
* 3.
|
|
12
|
-
* 4.
|
|
13
|
-
* 5.
|
|
14
|
-
* 6.
|
|
15
|
-
* 7.
|
|
10
|
+
* 2. Follow-up within merge window (15s) → SIGINT pause, start collecting
|
|
11
|
+
* 3. Follow-up outside merge window → queue for independent processing
|
|
12
|
+
* 4. More follow-ups while collecting → keep collecting (no timer yet)
|
|
13
|
+
* 5. Paused task dies → start debounce timer (5s)
|
|
14
|
+
* 6. Each new message resets the 5s debounce
|
|
15
|
+
* 7. Debounce fires → flush ALL collected messages as ONE prompt → ONE reply
|
|
16
|
+
* 8. Messages during flush → queue individually (each gets own card)
|
|
16
17
|
*
|
|
17
|
-
* Priority messages (/stop, /quit
|
|
18
|
+
* Priority messages (/stop, /quit) bypass everything and execute immediately.
|
|
18
19
|
*
|
|
19
20
|
* Public API:
|
|
20
21
|
* processMessage(chatId, text, ctx) — enqueue & serialize
|
|
21
22
|
* isActive(chatId) — check if a message is being processed
|
|
22
23
|
* interruptActive(chatId) — abort the active process
|
|
23
|
-
* clearQueue(chatId) — drop pending
|
|
24
|
-
* getQueueLength(chatId) — number of pending messages
|
|
24
|
+
* clearQueue(chatId) — drop ALL pending state
|
|
25
|
+
* getQueueLength(chatId) — number of pending messages (all queues)
|
|
25
26
|
*/
|
|
26
27
|
|
|
27
28
|
function createMessagePipeline(deps) {
|
|
@@ -35,16 +36,26 @@ function createMessagePipeline(deps) {
|
|
|
35
36
|
// Per-chatId Promise chain tail — ensures serial execution
|
|
36
37
|
const chains = new Map(); // chatId -> Promise
|
|
37
38
|
|
|
38
|
-
// Track the original message text
|
|
39
|
-
const activeTexts = new Map();
|
|
39
|
+
// Track the original message text and start time (for merge window)
|
|
40
|
+
const activeTexts = new Map(); // chatId -> string
|
|
41
|
+
const activeStartTimes = new Map(); // chatId -> timestamp (ms)
|
|
40
42
|
|
|
41
|
-
// Per-chatId burst collection state
|
|
43
|
+
// Per-chatId burst collection state (first-round merge only)
|
|
42
44
|
const collecting = new Map(); // chatId -> { messages: string[], ctx, timer, chainDead: boolean }
|
|
43
45
|
|
|
44
|
-
// chatIds where flush is processing — new messages go to
|
|
46
|
+
// chatIds where flush is processing — new messages go to pendingQueue
|
|
45
47
|
const resumed = new Set();
|
|
46
48
|
|
|
49
|
+
// Individual message queue — each item processed independently with its own card.
|
|
50
|
+
// Used for: post-resume messages, messages outside merge window.
|
|
51
|
+
const pendingQueue = new Map(); // chatId -> [{text, ctx}]
|
|
52
|
+
|
|
53
|
+
// chatIds currently being drained — prevents new messages from interrupting drain items
|
|
54
|
+
const draining = new Set();
|
|
55
|
+
|
|
47
56
|
const DEBOUNCE_MS = 5000;
|
|
57
|
+
const MERGE_WINDOW_MS = 15000;
|
|
58
|
+
const SIGINT_ESCALATE_MS = 2000;
|
|
48
59
|
|
|
49
60
|
// Messages that must bypass everything and execute immediately
|
|
50
61
|
const STOP_RE = /^\/stop(\s|$)/i;
|
|
@@ -52,27 +63,37 @@ function createMessagePipeline(deps) {
|
|
|
52
63
|
|
|
53
64
|
function _isPriorityMessage(text) {
|
|
54
65
|
const trimmed = (text || '').trim();
|
|
55
|
-
// Only hard-stop commands bypass. Natural language interrupts (等一下/hold on)
|
|
56
|
-
// are handled by command-router and should NOT bypass collecting — they would
|
|
57
|
-
// silently drop collected messages.
|
|
58
66
|
return STOP_RE.test(trimmed) || QUIT_RE.test(trimmed);
|
|
59
67
|
}
|
|
60
68
|
|
|
69
|
+
// ── Helpers ────────────────────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
function _enqueue(chatId, text, ctx) {
|
|
72
|
+
if (!pendingQueue.has(chatId)) pendingQueue.set(chatId, []);
|
|
73
|
+
pendingQueue.get(chatId).push({ text, ctx });
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function _clearAll(chatId) {
|
|
77
|
+
_cancelCollecting(chatId);
|
|
78
|
+
resumed.delete(chatId);
|
|
79
|
+
pendingQueue.delete(chatId);
|
|
80
|
+
draining.delete(chatId);
|
|
81
|
+
}
|
|
82
|
+
|
|
61
83
|
// ── Core: process / collect / flush ──────────────────────────────
|
|
62
84
|
|
|
63
85
|
function processMessage(chatId, text, ctx) {
|
|
64
86
|
// Priority messages bypass everything
|
|
65
87
|
if ((chains.has(chatId) || collecting.has(chatId)) && _isPriorityMessage(text)) {
|
|
66
88
|
log('INFO', `Pipeline: priority bypass "${text.trim()}" for ${chatId}`);
|
|
67
|
-
|
|
89
|
+
_clearAll(chatId);
|
|
68
90
|
return _processOne(chatId, text, ctx);
|
|
69
91
|
}
|
|
70
92
|
|
|
71
|
-
// Currently collecting → accumulate
|
|
93
|
+
// Currently collecting (first-round merge) → accumulate
|
|
72
94
|
if (collecting.has(chatId)) {
|
|
73
95
|
const c = collecting.get(chatId);
|
|
74
96
|
c.messages.push(text);
|
|
75
|
-
// Only reset debounce if chain is already dead (timer active)
|
|
76
97
|
if (c.chainDead) _resetDebounce(chatId, c);
|
|
77
98
|
log('INFO', `Pipeline: collecting follow-up for ${chatId} (${c.messages.length} pending)`);
|
|
78
99
|
return Promise.resolve();
|
|
@@ -81,45 +102,83 @@ function createMessagePipeline(deps) {
|
|
|
81
102
|
// Pipeline idle → start processing
|
|
82
103
|
if (!chains.has(chatId)) {
|
|
83
104
|
activeTexts.set(chatId, text);
|
|
105
|
+
activeStartTimes.set(chatId, Date.now());
|
|
84
106
|
const p = _processOne(chatId, text, ctx)
|
|
85
107
|
.finally(() => {
|
|
86
108
|
activeTexts.delete(chatId);
|
|
109
|
+
activeStartTimes.delete(chatId);
|
|
87
110
|
chains.delete(chatId);
|
|
88
111
|
resumed.delete(chatId);
|
|
89
|
-
//
|
|
112
|
+
// First-round merge: collected messages → debounce → flush
|
|
90
113
|
if (collecting.has(chatId)) {
|
|
91
114
|
const c = collecting.get(chatId);
|
|
92
115
|
c.chainDead = true;
|
|
93
116
|
_resetDebounce(chatId, c);
|
|
94
117
|
log('INFO', `Pipeline: chain ended, starting debounce for ${chatId} (${c.messages.length} collected)`);
|
|
118
|
+
return; // let debounce handle the rest; don't drain yet
|
|
95
119
|
}
|
|
120
|
+
// Drain individually queued messages
|
|
121
|
+
_drainNext(chatId);
|
|
96
122
|
});
|
|
97
123
|
chains.set(chatId, p);
|
|
98
124
|
return p;
|
|
99
125
|
}
|
|
100
126
|
|
|
101
|
-
// Pipeline busy +
|
|
102
|
-
if (resumed.has(chatId)) {
|
|
103
|
-
|
|
104
|
-
log('INFO', `Pipeline:
|
|
127
|
+
// Pipeline busy + post-flush or draining → queue individually
|
|
128
|
+
if (resumed.has(chatId) || draining.has(chatId)) {
|
|
129
|
+
_enqueue(chatId, text, ctx);
|
|
130
|
+
log('INFO', `Pipeline: queued independently for ${chatId} (${pendingQueue.get(chatId).length} queued)`);
|
|
131
|
+
return Promise.resolve();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Pipeline busy, first follow-up — check merge window
|
|
135
|
+
const elapsed = Date.now() - (activeStartTimes.get(chatId) || 0);
|
|
136
|
+
if (elapsed > MERGE_WINDOW_MS) {
|
|
137
|
+
_enqueue(chatId, text, ctx);
|
|
138
|
+
log('INFO', `Pipeline: outside merge window (${Math.round(elapsed / 1000)}s) for ${chatId}, queued independently`);
|
|
105
139
|
return Promise.resolve();
|
|
106
140
|
}
|
|
107
141
|
|
|
108
|
-
//
|
|
142
|
+
// Within merge window → interrupt and start collecting
|
|
109
143
|
return _startCollecting(chatId, text, ctx);
|
|
110
144
|
}
|
|
111
145
|
|
|
112
146
|
// ── Interrupt-Collect-Flush ────────────────────────────────────
|
|
113
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Send SIGINT with escalation: SIGINT → SIGTERM → SIGKILL.
|
|
150
|
+
* Works cross-platform — on macOS SIGINT is enough; on Windows claude
|
|
151
|
+
* ignores SIGINT so we escalate after SIGINT_ESCALATE_MS.
|
|
152
|
+
*/
|
|
153
|
+
function _killWithEscalation(child, chatId) {
|
|
154
|
+
try { process.kill(-child.pid, 'SIGINT'); } catch { try { child.kill('SIGINT'); } catch { /* */ } }
|
|
155
|
+
let sigkillTimer = null;
|
|
156
|
+
const escalateTimer = setTimeout(() => {
|
|
157
|
+
if (child.exitCode !== null || child.killed) return;
|
|
158
|
+
log('WARN', `Pipeline: SIGINT ineffective for ${chatId}, escalating to SIGTERM`);
|
|
159
|
+
try { process.kill(-child.pid, 'SIGTERM'); } catch { try { child.kill('SIGTERM'); } catch { /* */ } }
|
|
160
|
+
sigkillTimer = setTimeout(() => {
|
|
161
|
+
if (child.exitCode !== null || child.killed) return;
|
|
162
|
+
log('WARN', `Pipeline: SIGTERM ineffective for ${chatId}, escalating to SIGKILL`);
|
|
163
|
+
try { process.kill(-child.pid, 'SIGKILL'); } catch { try { child.kill('SIGKILL'); } catch { /* */ } }
|
|
164
|
+
}, SIGINT_ESCALATE_MS);
|
|
165
|
+
}, SIGINT_ESCALATE_MS);
|
|
166
|
+
child.once('close', () => {
|
|
167
|
+
clearTimeout(escalateTimer);
|
|
168
|
+
if (sigkillTimer) clearTimeout(sigkillTimer);
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
114
172
|
/**
|
|
115
173
|
* Gracefully pause the running task (SIGINT = ESC equivalent).
|
|
174
|
+
* Escalates to SIGTERM/SIGKILL if SIGINT is ineffective (e.g. claude on Windows).
|
|
116
175
|
*/
|
|
117
176
|
function _pauseActive(chatId) {
|
|
118
177
|
const proc = activeProcesses.get(chatId);
|
|
119
178
|
if (proc && proc.child) {
|
|
120
179
|
proc.aborted = true;
|
|
121
180
|
proc.abortReason = 'merge-pause';
|
|
122
|
-
|
|
181
|
+
_killWithEscalation(proc.child, chatId);
|
|
123
182
|
return true;
|
|
124
183
|
}
|
|
125
184
|
if (proc && proc.child === null) {
|
|
@@ -132,7 +191,6 @@ function createMessagePipeline(deps) {
|
|
|
132
191
|
|
|
133
192
|
/**
|
|
134
193
|
* Pause the running task and start collecting.
|
|
135
|
-
* No debounce timer — timer starts only after chain dies (.finally).
|
|
136
194
|
*/
|
|
137
195
|
function _startCollecting(chatId, text, ctx) {
|
|
138
196
|
_pauseActive(chatId);
|
|
@@ -140,23 +198,11 @@ function createMessagePipeline(deps) {
|
|
|
140
198
|
const msgs = originalText ? [originalText, text] : [text];
|
|
141
199
|
const c = { messages: msgs, ctx, timer: null, chainDead: false };
|
|
142
200
|
collecting.set(chatId, c);
|
|
143
|
-
// NO timer here — wait for chain to die
|
|
144
201
|
log('INFO', `Pipeline: paused & collecting for ${chatId} (original: "${(originalText || '').slice(0, 30)}")`);
|
|
145
202
|
ctx.bot.sendMessage(chatId, '⏸ 已暂停,继续连发,我会合并后继续').catch(() => {});
|
|
146
203
|
return Promise.resolve();
|
|
147
204
|
}
|
|
148
205
|
|
|
149
|
-
/**
|
|
150
|
-
* Add a message to collecting (create if needed).
|
|
151
|
-
*/
|
|
152
|
-
function _addToCollecting(chatId, text, ctx) {
|
|
153
|
-
if (!collecting.has(chatId)) {
|
|
154
|
-
collecting.set(chatId, { messages: [text], ctx, timer: null, chainDead: false });
|
|
155
|
-
} else {
|
|
156
|
-
collecting.get(chatId).messages.push(text);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
206
|
/**
|
|
161
207
|
* Reset the debounce timer for burst collection.
|
|
162
208
|
*/
|
|
@@ -206,6 +252,30 @@ function createMessagePipeline(deps) {
|
|
|
206
252
|
}
|
|
207
253
|
}
|
|
208
254
|
|
|
255
|
+
/**
|
|
256
|
+
* Drain the next queued message for chatId. Each gets its own card.
|
|
257
|
+
* Uses the normal pipeline path — serialization is guaranteed because
|
|
258
|
+
* we only call this from .finally() when the chain is already cleared.
|
|
259
|
+
*/
|
|
260
|
+
function _drainNext(chatId) {
|
|
261
|
+
const queue = pendingQueue.get(chatId);
|
|
262
|
+
if (!queue || queue.length === 0) {
|
|
263
|
+
pendingQueue.delete(chatId);
|
|
264
|
+
draining.delete(chatId);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
draining.add(chatId);
|
|
268
|
+
const { text, ctx } = queue.shift();
|
|
269
|
+
if (queue.length === 0) pendingQueue.delete(chatId);
|
|
270
|
+
log('INFO', `Pipeline: draining queued message for ${chatId} (${queue.length} remaining)`);
|
|
271
|
+
// processMessage will take the "idle" path and set up a new chain.
|
|
272
|
+
// That chain's .finally() will call _drainNext again for the next item.
|
|
273
|
+
const p = processMessage(chatId, text, ctx);
|
|
274
|
+
if (p && typeof p.catch === 'function') {
|
|
275
|
+
p.catch(err => log('ERROR', `Pipeline: drain error for ${chatId}: ${err.message}`));
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
209
279
|
// ── Process one message ────────────────────────────────────────
|
|
210
280
|
|
|
211
281
|
/**
|
|
@@ -232,8 +302,7 @@ function createMessagePipeline(deps) {
|
|
|
232
302
|
const proc = activeProcesses.get(chatId);
|
|
233
303
|
if (proc && proc.child) {
|
|
234
304
|
proc.aborted = true;
|
|
235
|
-
|
|
236
|
-
try { process.kill(-proc.child.pid, 'SIGINT'); } catch { try { proc.child.kill('SIGINT'); } catch { /* */ } }
|
|
305
|
+
_killWithEscalation(proc.child, chatId);
|
|
237
306
|
return true;
|
|
238
307
|
}
|
|
239
308
|
if (proc && proc.child === null) {
|
|
@@ -244,13 +313,13 @@ function createMessagePipeline(deps) {
|
|
|
244
313
|
}
|
|
245
314
|
|
|
246
315
|
function clearQueue(chatId) {
|
|
247
|
-
|
|
248
|
-
resumed.delete(chatId);
|
|
316
|
+
_clearAll(chatId);
|
|
249
317
|
}
|
|
250
318
|
|
|
251
319
|
function getQueueLength(chatId) {
|
|
252
320
|
const c = collecting.get(chatId);
|
|
253
|
-
|
|
321
|
+
const q = pendingQueue.get(chatId);
|
|
322
|
+
return (c ? c.messages.length : 0) + (q ? q.length : 0);
|
|
254
323
|
}
|
|
255
324
|
|
|
256
325
|
return {
|