truematch-plugin 0.1.24 → 0.1.25
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/dist/plugin.js +59 -14
- package/package.json +1 -1
- package/skills/truematch/SKILL.md +45 -13
package/dist/plugin.js
CHANGED
|
@@ -33,16 +33,25 @@ function loadObservation() {
|
|
|
33
33
|
return null;
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
+
// Resolved at module load time — env is populated before the module initialises.
|
|
37
|
+
// Used in cron payload and command:new messages so paths work without $PATH tricks.
|
|
38
|
+
const openclawStateDir = process.env["OPENCLAW_STATE_DIR"] ?? join(homedir(), ".openclaw");
|
|
39
|
+
const truematchCli = `node ${join(openclawStateDir, "extensions", "truematch-plugin", "dist", "index.js")}`;
|
|
40
|
+
const pollScript = join(openclawStateDir, "extensions", "truematch-plugin", "dist", "poll.js");
|
|
36
41
|
const sessionFlagsMap = new Map();
|
|
37
42
|
function getSessionFlags(sessionKey) {
|
|
38
43
|
let flags = sessionFlagsMap.get(sessionKey);
|
|
39
44
|
if (!flags) {
|
|
40
|
-
flags = {
|
|
45
|
+
flags = {
|
|
46
|
+
signalDelivered: false,
|
|
47
|
+
notificationDelivered: false,
|
|
48
|
+
setupDelivered: false,
|
|
49
|
+
};
|
|
41
50
|
sessionFlagsMap.set(sessionKey, flags);
|
|
42
51
|
}
|
|
43
52
|
return flags;
|
|
44
53
|
}
|
|
45
|
-
// Module-scoped flags set at gateway_start
|
|
54
|
+
// Module-scoped flags set at gateway_start.
|
|
46
55
|
// Resets on every gateway restart (correct — sentinel file prevents repeat prompts).
|
|
47
56
|
const pluginState = {
|
|
48
57
|
needsSetup: false,
|
|
@@ -176,7 +185,8 @@ export default {
|
|
|
176
185
|
});
|
|
177
186
|
// ── Hook: gateway_start ────────────────────────────────────────────────────
|
|
178
187
|
// Fires once per gateway process, after channels and hooks load.
|
|
179
|
-
//
|
|
188
|
+
// Detects setup state — before_prompt_build surfaces it on the first real message,
|
|
189
|
+
// which works on all surfaces (WhatsApp, Telegram, Discord, group chats).
|
|
180
190
|
api.on("gateway_start", (_event) => {
|
|
181
191
|
const identityFile = join(getTrueMatchDir(), "identity.json");
|
|
182
192
|
const preferencesFile = join(getTrueMatchDir(), "preferences.json");
|
|
@@ -194,7 +204,6 @@ export default {
|
|
|
194
204
|
const cronDelay = parseInt(process.env["TRUEMATCH_CRON_REGISTER_DELAY_MS"] ?? "2000", 10);
|
|
195
205
|
setTimeout(() => {
|
|
196
206
|
try {
|
|
197
|
-
const openclawStateDir = process.env["OPENCLAW_STATE_DIR"] ?? join(homedir(), ".openclaw");
|
|
198
207
|
const cronJobsFile = join(openclawStateDir, "cron", "jobs.json");
|
|
199
208
|
const raw = existsSync(cronJobsFile)
|
|
200
209
|
? JSON.parse(readFileSync(cronJobsFile, "utf8"))
|
|
@@ -226,13 +235,13 @@ export default {
|
|
|
226
235
|
payload: {
|
|
227
236
|
kind: "agentTurn",
|
|
228
237
|
message: "Run the TrueMatch background routine: " +
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
238
|
+
`(1) ${truematchCli} observe --show — load your current observation of this user. ` +
|
|
239
|
+
`(2) ${truematchCli} heartbeat — keep registration fresh. ` +
|
|
240
|
+
`(3) node "${pollScript}" — ` +
|
|
241
|
+
`for each JSONL line received, register it with ${truematchCli} match --receive, then process per the negotiation protocol. ` +
|
|
242
|
+
`(4) ${truematchCli} match --status — check all active threads. ` +
|
|
234
243
|
"(5) If there are no active negotiation threads AND truematch status shows matching_eligible=true, " +
|
|
235
|
-
|
|
244
|
+
`run ${truematchCli} match --start to find a new candidate. ` +
|
|
236
245
|
"Only surface a confirmed match — do not send a message if there is nothing to report.",
|
|
237
246
|
},
|
|
238
247
|
});
|
|
@@ -252,6 +261,7 @@ export default {
|
|
|
252
261
|
sessionFlagsMap.set(key, {
|
|
253
262
|
signalDelivered: false,
|
|
254
263
|
notificationDelivered: false,
|
|
264
|
+
setupDelivered: false,
|
|
255
265
|
});
|
|
256
266
|
});
|
|
257
267
|
// ── Hook: before_prompt_build ─────────────────────────────────────────────
|
|
@@ -271,6 +281,41 @@ export default {
|
|
|
271
281
|
const key = event.sessionKey ?? "default";
|
|
272
282
|
const sessionFlags = getSessionFlags(key);
|
|
273
283
|
const parts = [];
|
|
284
|
+
// 0. Setup / preferences — highest priority, fires on first real message.
|
|
285
|
+
// before_prompt_build works on all surfaces (WhatsApp, Telegram, Discord,
|
|
286
|
+
// group chats). command:new only fires on /new and is not sufficient.
|
|
287
|
+
if (!sessionFlags.setupDelivered) {
|
|
288
|
+
if (pluginState.needsSetup) {
|
|
289
|
+
pluginState.needsSetup = false;
|
|
290
|
+
sessionFlags.setupDelivered = true;
|
|
291
|
+
parts.push(`[TrueMatch] First-time setup — greet the user warmly, then collect the following logistics one question at a time. Wait for each answer before asking the next. Do not present them as a list.\n\n` +
|
|
292
|
+
`Open with: "Welcome to TrueMatch. I'm going to learn who you are through our conversations over time — you don't need to fill out a profile. I just need a few quick logistics to know who to consider. Where are you based?"\n\n` +
|
|
293
|
+
`Collect in order:\n` +
|
|
294
|
+
`1. Location — "Where are you based?"\n` +
|
|
295
|
+
`2. Distance — "How far are you open to meeting someone — within your city, within a few hours' travel, or anywhere?" Map to: 50 / 300 / omit.\n` +
|
|
296
|
+
`3. Age range — "Any age range you'd prefer? A rough bracket, one-sided, or no preference — all fine." Both min/max optional.\n` +
|
|
297
|
+
`4. Gender preference — "Who are you looking to meet? Specific, multiple, or everyone — whatever's true." Record open/everyone as [].\n` +
|
|
298
|
+
`5. Contact — "If we find someone, I'll handle the intro first. For that moment, what contact info would you want them to have? (Email, WhatsApp, Telegram, iMessage, Discord, or anything else.)"\n\n` +
|
|
299
|
+
`Do NOT push back on open answers. Do NOT re-ask.\n\n` +
|
|
300
|
+
`Then run:\n` +
|
|
301
|
+
` ${truematchCli} setup --contact-type <type> --contact-value '<value>'\n` +
|
|
302
|
+
` ${truematchCli} preferences --set '<json>'`);
|
|
303
|
+
return { prependContext: parts.join("\n\n---\n\n") };
|
|
304
|
+
}
|
|
305
|
+
if (pluginState.needsPreferences) {
|
|
306
|
+
pluginState.needsPreferences = false;
|
|
307
|
+
sessionFlags.setupDelivered = true;
|
|
308
|
+
parts.push(`[TrueMatch] Preferences not yet set. Ask one at a time, wait for each answer:\n` +
|
|
309
|
+
`1. "Where are you based?"\n` +
|
|
310
|
+
`2. "How far are you open to meeting someone — within your city, within a few hours' travel, or anywhere?"\n` +
|
|
311
|
+
`3. "Any age range you'd prefer? Rough bracket, one-sided, or no preference — all fine."\n` +
|
|
312
|
+
`4. "Who are you looking to meet? Specific, multiple, or everyone — whatever's true."\n\n` +
|
|
313
|
+
`Accept open answers without pushback, then save:\n` +
|
|
314
|
+
` ${truematchCli} preferences --set '<json>'\n\n` +
|
|
315
|
+
`If user tries to update preferences in main conversation later, redirect: "I don't update preferences here — say /truematch-prefs and we can do it there."`);
|
|
316
|
+
return { prependContext: parts.join("\n\n---\n\n") };
|
|
317
|
+
}
|
|
318
|
+
}
|
|
274
319
|
// 1. Match notification (once per session)
|
|
275
320
|
if (!sessionFlags.notificationDelivered) {
|
|
276
321
|
const notification = loadPendingNotification();
|
|
@@ -331,9 +376,9 @@ export default {
|
|
|
331
376
|
`Do NOT push back on open/no-preference answers. Do NOT re-ask.\n\n` +
|
|
332
377
|
`Collect both the contact type and the value before running setup.\n\n` +
|
|
333
378
|
`Run setup:\n` +
|
|
334
|
-
`
|
|
379
|
+
` ${truematchCli} setup --contact-type <type> --contact-value '<value>'\n` +
|
|
335
380
|
`Save preferences:\n` +
|
|
336
|
-
`
|
|
381
|
+
` ${truematchCli} preferences --set '<json>'`);
|
|
337
382
|
return;
|
|
338
383
|
}
|
|
339
384
|
if (pluginState.needsPreferences) {
|
|
@@ -344,7 +389,7 @@ export default {
|
|
|
344
389
|
`3. "Any age range you'd prefer? You can give a rough bracket, a one-sided floor or ceiling, or just say no preference — all fine."\n` +
|
|
345
390
|
`4. "Who are you looking to meet? You can be specific, give multiple options, or say everyone — whatever's true for you."\n\n` +
|
|
346
391
|
`Accept open/no-preference answers without pushback, then save:\n` +
|
|
347
|
-
`
|
|
392
|
+
` ${truematchCli} preferences --set '<json>'\n\n` +
|
|
348
393
|
`If user tries to update preferences in main conversation later, redirect them:\n` +
|
|
349
394
|
`"I don't update preferences here because this is my observation channel. ` +
|
|
350
395
|
`Say /truematch-prefs and we can do it there."`);
|
|
@@ -381,7 +426,7 @@ export default {
|
|
|
381
426
|
`there's enough to start matching."`;
|
|
382
427
|
event.messages.push(`[TrueMatch] Session ended. Review the observation summary below and update it ` +
|
|
383
428
|
`based on what you learned this session. Save with ` +
|
|
384
|
-
|
|
429
|
+
`\`${truematchCli} observe --write '<json>'\`.\n\n` +
|
|
385
430
|
ineligibleMessage +
|
|
386
431
|
`\nDo NOT ask questions to accelerate this.\n\n` +
|
|
387
432
|
output);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: truematch
|
|
3
3
|
description: Every dating profile is a performance. TrueMatch skips it — your Claude has already built a picture of how you actually live. It negotiates on your behalf. When two agents independently reach the same conclusion, you meet. No swiping. No rejection.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.32
|
|
5
5
|
metadata:
|
|
6
6
|
{
|
|
7
7
|
"openclaw":
|
|
@@ -9,16 +9,6 @@ metadata:
|
|
|
9
9
|
"emoji": "💑",
|
|
10
10
|
"homepage": "https://clawmatch.org",
|
|
11
11
|
"requires": { "bins": ["node", "truematch"] },
|
|
12
|
-
"install":
|
|
13
|
-
[
|
|
14
|
-
{
|
|
15
|
-
"id": "node",
|
|
16
|
-
"kind": "node",
|
|
17
|
-
"package": "truematch-plugin",
|
|
18
|
-
"bins": ["truematch"],
|
|
19
|
-
"label": "Install TrueMatch CLI (npm)",
|
|
20
|
-
},
|
|
21
|
-
],
|
|
22
12
|
},
|
|
23
13
|
}
|
|
24
14
|
---
|
|
@@ -34,10 +24,52 @@ TrueMatch matches people based on their **real personality** as observed by thei
|
|
|
34
24
|
First, confirm the CLI is installed:
|
|
35
25
|
|
|
36
26
|
```bash
|
|
37
|
-
truematch --version
|
|
27
|
+
node $OPENCLAW_STATE_DIR/extensions/truematch-plugin/dist/index.js --version
|
|
38
28
|
```
|
|
39
29
|
|
|
40
|
-
If that fails,
|
|
30
|
+
If that fails, the plugin isn't installed yet. Handle it automatically — do not ask the user to do anything:
|
|
31
|
+
|
|
32
|
+
1. Use `exec` to install into `$OPENCLAW_STATE_DIR/extensions` (user-writable on all setups — Docker, macOS, VPS) and create a convenience symlink:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
mkdir -p $OPENCLAW_STATE_DIR/extensions
|
|
36
|
+
npm pack truematch-plugin --pack-destination /tmp
|
|
37
|
+
mkdir -p $OPENCLAW_STATE_DIR/extensions/truematch-plugin
|
|
38
|
+
tar -xzf /tmp/truematch-plugin-*.tgz -C $OPENCLAW_STATE_DIR/extensions/truematch-plugin --strip-components=1
|
|
39
|
+
cd $OPENCLAW_STATE_DIR/extensions/truematch-plugin && npm install --ignore-scripts --omit=dev
|
|
40
|
+
chmod +x $OPENCLAW_STATE_DIR/extensions/truematch-plugin/dist/index.js
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Throughout this skill, `truematch <args>` means `node $OPENCLAW_STATE_DIR/extensions/truematch-plugin/dist/index.js <args>`.
|
|
44
|
+
|
|
45
|
+
2. Use the `gateway` tool to patch the config. First resolve `$OPENCLAW_STATE_DIR` from the environment and `<version>` from `package.json` in the extracted directory. All three fields are required — `load.paths` alone does not trigger the `gateway_start` lifecycle hook:
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"plugins": {
|
|
50
|
+
"load": {
|
|
51
|
+
"paths": ["<OPENCLAW_STATE_DIR>/extensions/truematch-plugin"]
|
|
52
|
+
},
|
|
53
|
+
"entries": {
|
|
54
|
+
"truematch-plugin": { "enabled": true }
|
|
55
|
+
},
|
|
56
|
+
"installs": {
|
|
57
|
+
"truematch-plugin": {
|
|
58
|
+
"source": "npm",
|
|
59
|
+
"spec": "truematch-plugin@<version>",
|
|
60
|
+
"installPath": "<OPENCLAW_STATE_DIR>/extensions/truematch-plugin",
|
|
61
|
+
"version": "<version>",
|
|
62
|
+
"resolvedName": "truematch-plugin",
|
|
63
|
+
"resolvedVersion": "<version>"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
3. Use the `gateway` tool to restart the gateway.
|
|
71
|
+
|
|
72
|
+
4. Re-run `truematch --version` to confirm. The `gateway_start` hook fires on restart and auto-creates the heartbeat cron — setup continues normally from here.
|
|
41
73
|
|
|
42
74
|
Before running setup, collect these **in one session** — ask each question and wait for the answer before continuing. Do not present them all at once or as a list to the user:
|
|
43
75
|
|