twindex-openclaw-plugin 0.7.5 → 0.8.0
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/openclaw.plugin.json +4 -4
- package/package.json +1 -1
- package/skills/twindex/SKILL.md +17 -17
- package/src/index.ts +30 -30
package/openclaw.plugin.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "twindex",
|
|
3
|
-
"name": "
|
|
3
|
+
"name": "New Lore",
|
|
4
4
|
"description": "Music intelligence for AI agents. Get notified about tours, merch drops, releases, and presales for your favorite artists.",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.8.0",
|
|
6
6
|
"skills": ["./skills"],
|
|
7
7
|
"configSchema": {
|
|
8
8
|
"type": "object",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"properties": {
|
|
11
11
|
"apiKey": {
|
|
12
12
|
"type": "string",
|
|
13
|
-
"description": "
|
|
13
|
+
"description": "New Lore API key (starts with twx_). Auto-generated on first startup."
|
|
14
14
|
},
|
|
15
15
|
"artists": {
|
|
16
16
|
"type": "array",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"uiHints": {
|
|
39
39
|
"apiKey": {
|
|
40
|
-
"label": "
|
|
40
|
+
"label": "New Lore API Key",
|
|
41
41
|
"placeholder": "twx_... (auto-generated)",
|
|
42
42
|
"sensitive": true
|
|
43
43
|
},
|
package/package.json
CHANGED
package/skills/twindex/SKILL.md
CHANGED
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
name: New Lore
|
|
3
3
|
description: Music intelligence — tours, merch drops, releases, presales for your favorite artists.
|
|
4
4
|
tools:
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
5
|
+
- newlore_setup
|
|
6
|
+
- newlore_subscribe
|
|
7
|
+
- newlore_unsubscribe
|
|
8
|
+
- newlore_check
|
|
9
|
+
- newlore_subscriptions
|
|
10
|
+
- newlore_artist
|
|
11
|
+
- newlore_search
|
|
12
|
+
- newlore_artists
|
|
13
|
+
- newlore_upload_photo
|
|
14
|
+
- newlore_try_on
|
|
15
15
|
---
|
|
16
16
|
|
|
17
17
|
# New Lore — Music Intelligence
|
|
@@ -24,7 +24,7 @@ YOU to set it up using your tools. Do NOT tell them to visit a website.
|
|
|
24
24
|
|
|
25
25
|
When a user asks for artist updates (tours, merch, releases):
|
|
26
26
|
1. Ask which artists and how often (realtime/periodic/daily)
|
|
27
|
-
2. Call `
|
|
27
|
+
2. Call `newlore_setup` with the artist slugs, frequency, and their chat ID
|
|
28
28
|
3. That's it — notifications auto-deliver from then on
|
|
29
29
|
|
|
30
30
|
You already know the user's chat ID from the conversation context.
|
|
@@ -32,36 +32,36 @@ Artist slugs are lowercase with hyphens: "linkin-park", "nick-cave-the-bad-seeds
|
|
|
32
32
|
|
|
33
33
|
## Adding more artists
|
|
34
34
|
|
|
35
|
-
Use `
|
|
35
|
+
Use `newlore_subscribe`. The existing delivery schedule picks them up automatically.
|
|
36
36
|
|
|
37
37
|
## Merch Try-On
|
|
38
38
|
|
|
39
39
|
This is the killer feature. Users can see themselves wearing merch.
|
|
40
40
|
|
|
41
41
|
**When a user sends a photo/selfie:**
|
|
42
|
-
1. Call `
|
|
42
|
+
1. Call `newlore_upload_photo` with NO parameters (it auto-finds the photo)
|
|
43
43
|
2. Tell them they're all set for try-ons
|
|
44
44
|
|
|
45
45
|
**When a user asks to try on a product:**
|
|
46
|
-
1. Call `
|
|
46
|
+
1. Call `newlore_try_on` with the artist slug and product name
|
|
47
47
|
2. The server does FUZZY MATCHING — you don't need the exact name
|
|
48
48
|
3. Partial names work: "the hat", "black hoodie", "tour tee" are all fine
|
|
49
49
|
4. If the user references something you just showed them in a notification, use that product name
|
|
50
50
|
|
|
51
51
|
**After a merch notification:**
|
|
52
52
|
- If you showed a product and the user says "try that on" or "let me see it on me", you ALREADY KNOW the product name from the notification you just delivered. Use it directly.
|
|
53
|
-
- If the user says "try that on" after a merch notification, just call `
|
|
53
|
+
- If the user says "try that on" after a merch notification, just call `newlore_try_on` with only the artist slug — no product name needed. The server remembers which product was shown.
|
|
54
54
|
- Do NOT browse the store or ask for clarification. Just call the tool.
|
|
55
55
|
|
|
56
56
|
If try-on returns a 404 (no photo), ask the user to send a selfie first.
|
|
57
57
|
|
|
58
58
|
## Checking manually
|
|
59
59
|
|
|
60
|
-
Use `
|
|
60
|
+
Use `newlore_check` to look for updates right now.
|
|
61
61
|
|
|
62
62
|
## Viewing artist details
|
|
63
63
|
|
|
64
|
-
Use `
|
|
64
|
+
Use `newlore_artist` with a slug to get their full page.
|
|
65
65
|
|
|
66
66
|
## Rules
|
|
67
67
|
|
package/src/index.ts
CHANGED
|
@@ -26,9 +26,9 @@ export default function register(api: any) {
|
|
|
26
26
|
if (!disk.plugins.entries.twindex) disk.plugins.entries.twindex = {};
|
|
27
27
|
disk.plugins.entries.twindex.config = api.config.plugins.entries.twindex.config;
|
|
28
28
|
writeFileSync(configPath, JSON.stringify(disk, null, 2) + "\n", "utf-8");
|
|
29
|
-
api.logger?.info?.("
|
|
29
|
+
api.logger?.info?.("NewLore: config persisted to disk");
|
|
30
30
|
} catch (err: any) {
|
|
31
|
-
api.logger?.warn?.(`
|
|
31
|
+
api.logger?.warn?.(`NewLore: failed to persist config: ${err.message}`);
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -78,10 +78,10 @@ export default function register(api: any) {
|
|
|
78
78
|
for (const artist of config.artists) {
|
|
79
79
|
try {
|
|
80
80
|
await twindex.subscribe(apiKey, artist);
|
|
81
|
-
api.logger?.info?.(`
|
|
81
|
+
api.logger?.info?.(`NewLore: auto-subscribed to ${artist}`);
|
|
82
82
|
} catch (err: any) {
|
|
83
83
|
api.logger?.warn?.(
|
|
84
|
-
`
|
|
84
|
+
`NewLore: failed to subscribe to ${artist}: ${err.message}`,
|
|
85
85
|
);
|
|
86
86
|
}
|
|
87
87
|
}
|
|
@@ -92,17 +92,17 @@ export default function register(api: any) {
|
|
|
92
92
|
if (chatTarget) {
|
|
93
93
|
updates.chatTarget = chatTarget;
|
|
94
94
|
updates.chatChannel = inferChatChannel();
|
|
95
|
-
api.logger?.info?.(`
|
|
95
|
+
api.logger?.info?.(`NewLore: auto-detected chatTarget=${chatTarget}`);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
persistConfig(updates);
|
|
99
99
|
pollService.start();
|
|
100
100
|
api.logger?.info?.(
|
|
101
|
-
"
|
|
101
|
+
"NewLore: auto-bootstrap complete. Delivery via polling.",
|
|
102
102
|
);
|
|
103
103
|
}
|
|
104
104
|
} catch (err: any) {
|
|
105
|
-
api.logger?.warn?.(`
|
|
105
|
+
api.logger?.warn?.(`NewLore: auto-bootstrap failed: ${err.message}`);
|
|
106
106
|
} finally {
|
|
107
107
|
bootstrapping = false;
|
|
108
108
|
}
|
|
@@ -111,7 +111,7 @@ export default function register(api: any) {
|
|
|
111
111
|
// ── Tools ──────────────────────────────────────────────────────────
|
|
112
112
|
|
|
113
113
|
api.registerTool({
|
|
114
|
-
name: "
|
|
114
|
+
name: "newlore_setup",
|
|
115
115
|
description:
|
|
116
116
|
"Set up New Lore music notifications for the user. YOU are the user's interface to New Lore — do NOT tell them to visit a website. Registers, subscribes to artists, and starts polling. Call this when the user asks for music updates, mentions newlore.ai, or wants to follow artists.",
|
|
117
117
|
parameters: {
|
|
@@ -207,9 +207,9 @@ export default function register(api: any) {
|
|
|
207
207
|
});
|
|
208
208
|
|
|
209
209
|
api.registerTool({
|
|
210
|
-
name: "
|
|
210
|
+
name: "newlore_subscribe",
|
|
211
211
|
description:
|
|
212
|
-
"Subscribe to a new artist on
|
|
212
|
+
"Subscribe to a new artist on New Lore. The existing delivery schedule will automatically pick up notifications for this artist.",
|
|
213
213
|
parameters: {
|
|
214
214
|
type: "object",
|
|
215
215
|
properties: {
|
|
@@ -225,7 +225,7 @@ export default function register(api: any) {
|
|
|
225
225
|
if (!apiKey) {
|
|
226
226
|
return {
|
|
227
227
|
content: [
|
|
228
|
-
{ type: "text", text: "Not set up yet. Use
|
|
228
|
+
{ type: "text", text: "Not set up yet. Use newlore_setup first." },
|
|
229
229
|
],
|
|
230
230
|
};
|
|
231
231
|
}
|
|
@@ -254,8 +254,8 @@ export default function register(api: any) {
|
|
|
254
254
|
});
|
|
255
255
|
|
|
256
256
|
api.registerTool({
|
|
257
|
-
name: "
|
|
258
|
-
description: "Unsubscribe from an artist on
|
|
257
|
+
name: "newlore_unsubscribe",
|
|
258
|
+
description: "Unsubscribe from an artist on New Lore.",
|
|
259
259
|
parameters: {
|
|
260
260
|
type: "object",
|
|
261
261
|
properties: {
|
|
@@ -271,7 +271,7 @@ export default function register(api: any) {
|
|
|
271
271
|
if (!apiKey) {
|
|
272
272
|
return {
|
|
273
273
|
content: [
|
|
274
|
-
{ type: "text", text: "Not set up yet. Use
|
|
274
|
+
{ type: "text", text: "Not set up yet. Use newlore_setup first." },
|
|
275
275
|
],
|
|
276
276
|
};
|
|
277
277
|
}
|
|
@@ -297,9 +297,9 @@ export default function register(api: any) {
|
|
|
297
297
|
});
|
|
298
298
|
|
|
299
299
|
api.registerTool({
|
|
300
|
-
name: "
|
|
300
|
+
name: "newlore_check",
|
|
301
301
|
description:
|
|
302
|
-
"Check for unread
|
|
302
|
+
"Check for unread New Lore notifications right now. Returns any pending artist updates.",
|
|
303
303
|
parameters: {
|
|
304
304
|
type: "object",
|
|
305
305
|
properties: {},
|
|
@@ -309,7 +309,7 @@ export default function register(api: any) {
|
|
|
309
309
|
if (!apiKey) {
|
|
310
310
|
return {
|
|
311
311
|
content: [
|
|
312
|
-
{ type: "text", text: "Not set up yet. Use
|
|
312
|
+
{ type: "text", text: "Not set up yet. Use newlore_setup first." },
|
|
313
313
|
],
|
|
314
314
|
};
|
|
315
315
|
}
|
|
@@ -352,8 +352,8 @@ export default function register(api: any) {
|
|
|
352
352
|
});
|
|
353
353
|
|
|
354
354
|
api.registerTool({
|
|
355
|
-
name: "
|
|
356
|
-
description: "List your current
|
|
355
|
+
name: "newlore_subscriptions",
|
|
356
|
+
description: "List your current New Lore subscriptions.",
|
|
357
357
|
parameters: {
|
|
358
358
|
type: "object",
|
|
359
359
|
properties: {},
|
|
@@ -363,7 +363,7 @@ export default function register(api: any) {
|
|
|
363
363
|
if (!apiKey) {
|
|
364
364
|
return {
|
|
365
365
|
content: [
|
|
366
|
-
{ type: "text", text: "Not set up yet. Use
|
|
366
|
+
{ type: "text", text: "Not set up yet. Use newlore_setup first." },
|
|
367
367
|
],
|
|
368
368
|
};
|
|
369
369
|
}
|
|
@@ -396,9 +396,9 @@ export default function register(api: any) {
|
|
|
396
396
|
});
|
|
397
397
|
|
|
398
398
|
api.registerTool({
|
|
399
|
-
name: "
|
|
399
|
+
name: "newlore_artist",
|
|
400
400
|
description:
|
|
401
|
-
"Get the full
|
|
401
|
+
"Get the full New Lore page for an artist. Returns detailed info including tours, merch, releases.",
|
|
402
402
|
parameters: {
|
|
403
403
|
type: "object",
|
|
404
404
|
properties: {
|
|
@@ -429,9 +429,9 @@ export default function register(api: any) {
|
|
|
429
429
|
});
|
|
430
430
|
|
|
431
431
|
api.registerTool({
|
|
432
|
-
name: "
|
|
432
|
+
name: "newlore_search",
|
|
433
433
|
description:
|
|
434
|
-
"Search the
|
|
434
|
+
"Search the New Lore music index. Find artists by name, genre, or keyword.",
|
|
435
435
|
parameters: {
|
|
436
436
|
type: "object",
|
|
437
437
|
properties: {
|
|
@@ -469,7 +469,7 @@ export default function register(api: any) {
|
|
|
469
469
|
});
|
|
470
470
|
|
|
471
471
|
api.registerTool({
|
|
472
|
-
name: "
|
|
472
|
+
name: "newlore_upload_photo",
|
|
473
473
|
description:
|
|
474
474
|
"Upload the user's selfie for personalized merch try-on. When the user sends a photo in chat, call this tool with NO parameters — it automatically finds the most recent photo from the conversation. One photo per user — uploading replaces the previous one.",
|
|
475
475
|
parameters: {
|
|
@@ -487,7 +487,7 @@ export default function register(api: any) {
|
|
|
487
487
|
if (!apiKey) {
|
|
488
488
|
return {
|
|
489
489
|
content: [
|
|
490
|
-
{ type: "text", text: "Not set up yet. Use
|
|
490
|
+
{ type: "text", text: "Not set up yet. Use newlore_setup first." },
|
|
491
491
|
],
|
|
492
492
|
};
|
|
493
493
|
}
|
|
@@ -563,7 +563,7 @@ export default function register(api: any) {
|
|
|
563
563
|
});
|
|
564
564
|
|
|
565
565
|
api.registerTool({
|
|
566
|
-
name: "
|
|
566
|
+
name: "newlore_try_on",
|
|
567
567
|
description:
|
|
568
568
|
"Generate an image of the user wearing a merch product. Requires a photo uploaded first. The server fuzzy-matches product names — partial or approximate names work fine (e.g. 'the hat', 'black hoodie', 'tour tee'). If the user references a product from a recent notification, use that product name directly.",
|
|
569
569
|
parameters: {
|
|
@@ -588,7 +588,7 @@ export default function register(api: any) {
|
|
|
588
588
|
if (!apiKey) {
|
|
589
589
|
return {
|
|
590
590
|
content: [
|
|
591
|
-
{ type: "text", text: "Not set up yet. Use
|
|
591
|
+
{ type: "text", text: "Not set up yet. Use newlore_setup first." },
|
|
592
592
|
],
|
|
593
593
|
};
|
|
594
594
|
}
|
|
@@ -634,8 +634,8 @@ export default function register(api: any) {
|
|
|
634
634
|
});
|
|
635
635
|
|
|
636
636
|
api.registerTool({
|
|
637
|
-
name: "
|
|
638
|
-
description: "List all artists currently indexed on
|
|
637
|
+
name: "newlore_artists",
|
|
638
|
+
description: "List all artists currently indexed on New Lore.",
|
|
639
639
|
parameters: {
|
|
640
640
|
type: "object",
|
|
641
641
|
properties: {},
|