@mkterswingman/5mghost-twinkler 0.1.0 → 0.1.3
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 +4 -1
- package/dist/cli.js +41 -2
- package/dist/skillInstall.d.ts +5 -2
- package/dist/skillInstall.js +77 -49
- package/package.json +13 -3
- package/scripts/postinstall.mjs +51 -0
- package/skills/setup-5mghost-twinkler/SKILL.md +71 -0
- package/skills/update-5mghost-twinkler/SKILL.md +41 -0
- package/skills/use-5mghost-twinkler/SKILL.md +39 -36
- package/skills.manifest.json +46 -2
package/README.md
CHANGED
|
@@ -4,10 +4,13 @@ Lightweight AI runtime for the hosted Twinkler API.
|
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
npm install -g @mkterswingman/5mghost-twinkler
|
|
7
|
-
twinkler setup
|
|
8
7
|
twinkler call GET /api/v1/channel/ibai/summary --query days=30
|
|
9
8
|
```
|
|
10
9
|
|
|
10
|
+
`npm install -g` installs bundled AI skills automatically for detected AI
|
|
11
|
+
clients. `twinkler setup` remains an idempotent repair command when a skill
|
|
12
|
+
target was unavailable during install.
|
|
13
|
+
|
|
11
14
|
Auth uses the shared mkterswingman PAT. Do not commit `.env`, `auth.json`,
|
|
12
15
|
PATs, bearer tokens, or any generated secret file to a remote repository. Enter
|
|
13
16
|
PATs through `twinkler auth login --pat-stdin`; do not paste PATs into AI chat.
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
2
|
+
import { readFileSync, realpathSync } from "node:fs";
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
3
4
|
import { dirname, join } from "node:path";
|
|
4
5
|
import { fileURLToPath } from "node:url";
|
|
5
6
|
import { getAuthStatus, logout, PAT_LOGIN_URL, savePat } from "./auth.js";
|
|
@@ -17,6 +18,7 @@ const HELP = [
|
|
|
17
18
|
" twinkler auth login --pat <TOKEN>",
|
|
18
19
|
" twinkler auth login --pat-stdin",
|
|
19
20
|
" twinkler call <GET|POST|DELETE> /api/v1/... [--query k=v] [--json '{...}']",
|
|
21
|
+
" twinkler update",
|
|
20
22
|
" twinkler version",
|
|
21
23
|
"",
|
|
22
24
|
"mkterswingman PAT:",
|
|
@@ -72,6 +74,9 @@ export async function runCli(context = {}) {
|
|
|
72
74
|
].join("\n"));
|
|
73
75
|
return results.some((result) => result.status === "error") ? 1 : 0;
|
|
74
76
|
}
|
|
77
|
+
case "update":
|
|
78
|
+
case "upgrade":
|
|
79
|
+
return runUpdateCommand(args, { env, out, err });
|
|
75
80
|
case "doctor": {
|
|
76
81
|
const auth = getAuthStatus({ authJsonPath: paths.authJsonPath, env });
|
|
77
82
|
out([
|
|
@@ -188,6 +193,30 @@ function renderSkillInstallResults(results) {
|
|
|
188
193
|
})
|
|
189
194
|
].join("\n");
|
|
190
195
|
}
|
|
196
|
+
function runUpdateCommand(args, context) {
|
|
197
|
+
const dryRun = args.includes("--dry-run") || context.env.TWINKLER_UPDATE_DRY_RUN === "1";
|
|
198
|
+
const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
199
|
+
const installArgs = ["install", "-g", "@mkterswingman/5mghost-twinkler@latest"];
|
|
200
|
+
if (dryRun) {
|
|
201
|
+
context.out(`Update command: ${npmCommand} ${installArgs.join(" ")}`);
|
|
202
|
+
return 0;
|
|
203
|
+
}
|
|
204
|
+
context.out("Updating Twinkler helper...");
|
|
205
|
+
const result = spawnSync(npmCommand, installArgs, {
|
|
206
|
+
stdio: "inherit",
|
|
207
|
+
env: context.env
|
|
208
|
+
});
|
|
209
|
+
if (result.error) {
|
|
210
|
+
context.err(`Update failed: ${result.error.message}`);
|
|
211
|
+
return 1;
|
|
212
|
+
}
|
|
213
|
+
if (result.status !== 0) {
|
|
214
|
+
context.err(`Update failed: npm exited with ${result.status ?? "unknown status"}`);
|
|
215
|
+
return result.status ?? 1;
|
|
216
|
+
}
|
|
217
|
+
context.out("Twinkler updated. Restart your AI session so newly installed skill text is loaded.");
|
|
218
|
+
return 0;
|
|
219
|
+
}
|
|
191
220
|
async function readAllStdin(stdin) {
|
|
192
221
|
const chunks = [];
|
|
193
222
|
for await (const chunk of stdin) {
|
|
@@ -208,7 +237,17 @@ function readPackageVersion() {
|
|
|
208
237
|
}
|
|
209
238
|
return "0.0.0";
|
|
210
239
|
}
|
|
211
|
-
|
|
240
|
+
function isDirectCliEntrypoint() {
|
|
241
|
+
if (!process.argv[1])
|
|
242
|
+
return false;
|
|
243
|
+
try {
|
|
244
|
+
return realpathSync(fileURLToPath(import.meta.url)) === realpathSync(process.argv[1]);
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
return import.meta.url === `file://${process.argv[1]}`;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (isDirectCliEntrypoint()) {
|
|
212
251
|
runCli().then((code) => {
|
|
213
252
|
process.exitCode = code;
|
|
214
253
|
});
|
package/dist/skillInstall.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export interface InstallSkillsOptions {
|
|
2
|
-
homeDir
|
|
2
|
+
homeDir?: string;
|
|
3
|
+
detectedAgents?: string[];
|
|
4
|
+
repairTargets?: boolean;
|
|
3
5
|
}
|
|
4
6
|
export interface SkillInstallResult {
|
|
5
7
|
agent: string;
|
|
@@ -7,5 +9,6 @@ export interface SkillInstallResult {
|
|
|
7
9
|
status: "installed" | "skipped" | "error";
|
|
8
10
|
targetDir: string;
|
|
9
11
|
reason?: string;
|
|
12
|
+
contentHash?: string;
|
|
10
13
|
}
|
|
11
|
-
export declare function installBundledSkills(options
|
|
14
|
+
export declare function installBundledSkills(options?: InstallSkillsOptions): SkillInstallResult[];
|
package/dist/skillInstall.js
CHANGED
|
@@ -1,58 +1,86 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { applyAgentOverrides, getTargetDir, installSkills, listDetectedAgents, loadBuiltInRegistry, RECEIPT_FILENAME } from "@mkterswingman/5mghost-agent-skills";
|
|
2
|
+
import { existsSync, readFileSync, rmSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
3
4
|
import { resolveBundledAssetPath } from "./paths.js";
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
const AGENT_OVERRIDES = {
|
|
6
|
+
agents: {
|
|
7
|
+
label: "Agents legacy skills",
|
|
8
|
+
detect: { kind: "path", path: "~/.agents" },
|
|
9
|
+
skillsDir: "~/.agents/skills",
|
|
10
|
+
installMethod: "copy"
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
const TWINKLER_SKILLS = [
|
|
14
|
+
"setup-5mghost-twinkler",
|
|
15
|
+
"use-5mghost-twinkler",
|
|
16
|
+
"update-5mghost-twinkler"
|
|
7
17
|
];
|
|
8
|
-
|
|
18
|
+
const LEGACY_RECEIPT_FILENAME = ".install-receipt.json";
|
|
19
|
+
export function installBundledSkills(options = {}) {
|
|
9
20
|
const manifestPath = resolveBundledAssetPath("skills.manifest.json");
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
const detectedAgents = options.detectedAgents ??
|
|
22
|
+
listDetectedAgents({
|
|
23
|
+
homeDir: options.homeDir,
|
|
24
|
+
agentOverrides: AGENT_OVERRIDES
|
|
25
|
+
});
|
|
26
|
+
const setupTargets = options.repairTargets === false
|
|
27
|
+
? detectedAgents
|
|
28
|
+
: Array.from(new Set([...detectedAgents, "codex", "agents"]));
|
|
29
|
+
removeLegacyOwnedTargets(options.homeDir, setupTargets);
|
|
30
|
+
return normalizeSummary(installSkills({
|
|
31
|
+
manifestPath,
|
|
32
|
+
homeDir: options.homeDir,
|
|
33
|
+
agentOverrides: AGENT_OVERRIDES,
|
|
34
|
+
detectedAgents: setupTargets
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
function removeLegacyOwnedTargets(homeDir, agentNames) {
|
|
38
|
+
const registry = applyAgentOverrides(loadBuiltInRegistry(), AGENT_OVERRIDES);
|
|
39
|
+
for (const agentName of agentNames) {
|
|
40
|
+
const agent = registry.agents[agentName];
|
|
41
|
+
if (!agent)
|
|
42
|
+
continue;
|
|
43
|
+
for (const skillName of TWINKLER_SKILLS) {
|
|
44
|
+
const targetDir = getTargetDir(homeDir, agent.skillsDir, skillName);
|
|
45
|
+
const receiptPath = findReceiptPath(targetDir);
|
|
46
|
+
if (!existsSync(receiptPath))
|
|
47
|
+
continue;
|
|
48
|
+
let receipt;
|
|
18
49
|
try {
|
|
19
|
-
|
|
20
|
-
results.push({
|
|
21
|
-
agent: target.agent,
|
|
22
|
-
skill: skill.name,
|
|
23
|
-
status: "error",
|
|
24
|
-
targetDir,
|
|
25
|
-
reason: `missing bundled skill source ${sourceDir}`
|
|
26
|
-
});
|
|
27
|
-
continue;
|
|
28
|
-
}
|
|
29
|
-
mkdirSync(rootDir, { recursive: true });
|
|
30
|
-
rmSync(targetDir, { recursive: true, force: true });
|
|
31
|
-
cpSync(sourceDir, targetDir, { recursive: true });
|
|
32
|
-
writeFileSync(join(targetDir, ".install-receipt.json"), JSON.stringify({
|
|
33
|
-
product: manifest.product,
|
|
34
|
-
skill: skill.name,
|
|
35
|
-
agent: target.agent,
|
|
36
|
-
source: basename(sourceDir),
|
|
37
|
-
installed_at: new Date().toISOString()
|
|
38
|
-
}, null, 2));
|
|
39
|
-
results.push({
|
|
40
|
-
agent: target.agent,
|
|
41
|
-
skill: skill.name,
|
|
42
|
-
status: "installed",
|
|
43
|
-
targetDir
|
|
44
|
-
});
|
|
50
|
+
receipt = JSON.parse(readFileSync(receiptPath, "utf8"));
|
|
45
51
|
}
|
|
46
|
-
catch
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
});
|
|
52
|
+
catch {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (receipt.installedBy === undefined &&
|
|
56
|
+
receipt.product === "5mghost-twinkler" &&
|
|
57
|
+
receipt.skill === skillName &&
|
|
58
|
+
receipt.agent === agentName) {
|
|
59
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
54
60
|
}
|
|
55
61
|
}
|
|
56
62
|
}
|
|
57
|
-
|
|
63
|
+
}
|
|
64
|
+
function findReceiptPath(targetDir) {
|
|
65
|
+
const current = join(targetDir, RECEIPT_FILENAME);
|
|
66
|
+
if (existsSync(current))
|
|
67
|
+
return current;
|
|
68
|
+
return join(targetDir, LEGACY_RECEIPT_FILENAME);
|
|
69
|
+
}
|
|
70
|
+
function normalizeSummary(summary) {
|
|
71
|
+
return summary.results.map((result) => {
|
|
72
|
+
const status = result.status === "installed" || result.status === "updated"
|
|
73
|
+
? "installed"
|
|
74
|
+
: result.status === "error"
|
|
75
|
+
? "error"
|
|
76
|
+
: "skipped";
|
|
77
|
+
return {
|
|
78
|
+
agent: result.agent,
|
|
79
|
+
skill: result.skill,
|
|
80
|
+
status,
|
|
81
|
+
targetDir: result.targetDir,
|
|
82
|
+
reason: result.reason,
|
|
83
|
+
contentHash: result.contentHash
|
|
84
|
+
};
|
|
85
|
+
});
|
|
58
86
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mkterswingman/5mghost-twinkler",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Lightweight AI helper for the 5mghost Twinkler API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -9,8 +9,13 @@
|
|
|
9
9
|
"publishConfig": {
|
|
10
10
|
"access": "public"
|
|
11
11
|
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/5mghost/mcp_projects.git",
|
|
15
|
+
"directory": "5mghost-twinkler/client"
|
|
16
|
+
},
|
|
12
17
|
"bin": {
|
|
13
|
-
"twinkler": "
|
|
18
|
+
"twinkler": "dist/cli.js"
|
|
14
19
|
},
|
|
15
20
|
"exports": {
|
|
16
21
|
".": {
|
|
@@ -23,13 +28,15 @@
|
|
|
23
28
|
"skills",
|
|
24
29
|
"skills.manifest.json",
|
|
25
30
|
"install",
|
|
31
|
+
"scripts",
|
|
26
32
|
"README.md"
|
|
27
33
|
],
|
|
28
34
|
"scripts": {
|
|
29
35
|
"build": "tsc -p tsconfig.json",
|
|
30
36
|
"test": "npm run build && node --test tests/*.test.mjs",
|
|
31
37
|
"prepack": "npm run build",
|
|
32
|
-
"pack:dry": "npm pack --dry-run"
|
|
38
|
+
"pack:dry": "npm pack --dry-run",
|
|
39
|
+
"postinstall": "node scripts/postinstall.mjs"
|
|
33
40
|
},
|
|
34
41
|
"keywords": [
|
|
35
42
|
"twinkler",
|
|
@@ -37,6 +44,9 @@
|
|
|
37
44
|
"ai",
|
|
38
45
|
"mkterswingman"
|
|
39
46
|
],
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@mkterswingman/5mghost-agent-skills": "^0.0.1"
|
|
49
|
+
},
|
|
40
50
|
"devDependencies": {
|
|
41
51
|
"@types/node": "^25.3.2",
|
|
42
52
|
"typescript": "^5.9.3"
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Runs after `npm install -g` to install Twinkler skills into detected AI clients.
|
|
2
|
+
// This script is intentionally non-fatal: npm install must still succeed when
|
|
3
|
+
// an AI client is not installed or a skill target is locked down.
|
|
4
|
+
|
|
5
|
+
const agentOverrides = {
|
|
6
|
+
agents: {
|
|
7
|
+
label: "Agents legacy skills",
|
|
8
|
+
detect: { kind: "path", path: "~/.agents" },
|
|
9
|
+
skillsDir: "~/.agents/skills",
|
|
10
|
+
installMethod: "copy"
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
let listDetectedAgents;
|
|
15
|
+
let installBundledSkills;
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
({ listDetectedAgents } = await import("@mkterswingman/5mghost-agent-skills"));
|
|
19
|
+
({ installBundledSkills } = await import("../dist/skillInstall.js"));
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.log("[twinkler] agent-skills not available; skipping skill install.");
|
|
22
|
+
process.exit(0);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let agents;
|
|
26
|
+
try {
|
|
27
|
+
agents = listDetectedAgents({ agentOverrides });
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.log("[twinkler] Could not detect AI clients; skipping skill install.");
|
|
30
|
+
process.exit(0);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!agents || agents.length === 0) {
|
|
34
|
+
console.log("[twinkler] No AI clients detected; skipping skill install.");
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const results = installBundledSkills({ detectedAgents: agents, repairTargets: false });
|
|
40
|
+
const installed = results
|
|
41
|
+
.filter((entry) => entry.status === "installed")
|
|
42
|
+
.map((entry) => `${entry.agent}:${entry.skill}`);
|
|
43
|
+
if (installed.length > 0) {
|
|
44
|
+
console.log(`[twinkler] Installed skills: ${installed.join(", ")}`);
|
|
45
|
+
console.log("[twinkler] Restart your AI session before invoking the new skill text.");
|
|
46
|
+
} else {
|
|
47
|
+
console.log("[twinkler] No skill targets updated.");
|
|
48
|
+
}
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.log(`[twinkler] Skill install failed (non-fatal): ${String(error)}`);
|
|
51
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: setup-5mghost-twinkler
|
|
3
|
+
preamble-tier: 3
|
|
4
|
+
version: 0.2.0
|
|
5
|
+
description: |
|
|
6
|
+
Set up or repair the local Twinkler helper, bundled skills, and mkterswingman
|
|
7
|
+
auth for AI-driven Twitch data workflows.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Setup 5mghost Twinkler
|
|
11
|
+
|
|
12
|
+
Use when Twinkler is missing, auth is missing or expired, skills may be stale,
|
|
13
|
+
or the user asks to prepare Twinkler for an AI workflow.
|
|
14
|
+
|
|
15
|
+
## Default Path
|
|
16
|
+
|
|
17
|
+
1. Check the helper:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
twinkler doctor
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
2. If the helper is missing, install it:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install -g @mkterswingman/5mghost-twinkler
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The npm install runs the bundled skill installer automatically. Use `twinkler
|
|
30
|
+
setup` only as a repair command when postinstall could not update the local AI
|
|
31
|
+
skill directories.
|
|
32
|
+
|
|
33
|
+
3. Repair skills when needed:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
twinkler setup
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
4. If auth is missing, send the user to:
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
https://mkterswingman.com/pat/login
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Ask the user to copy the mkterswingman PAT, then start:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
twinkler auth login --pat-stdin
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Have the user paste the PAT into that local input. Do not ask them to paste the
|
|
52
|
+
PAT into chat, screenshots, docs, shell history, or source files.
|
|
53
|
+
|
|
54
|
+
## Success
|
|
55
|
+
|
|
56
|
+
`twinkler doctor` should report auth ready. Then the AI can use the
|
|
57
|
+
`use-5mghost-twinkler` skill and `twinkler call`.
|
|
58
|
+
|
|
59
|
+
## Recovery
|
|
60
|
+
|
|
61
|
+
- Missing command: run `npm install -g @mkterswingman/5mghost-twinkler`.
|
|
62
|
+
- Missing skills after install: run `twinkler setup`, then restart the AI session.
|
|
63
|
+
- Missing auth: use the PAT page and `twinkler auth login --pat-stdin`.
|
|
64
|
+
- HTTP 401/403 from API calls: run `twinkler auth status`; if stale, log in again.
|
|
65
|
+
- Permission or npm global install failure: use the same package manager and
|
|
66
|
+
Node installation method that originally installed global npm packages.
|
|
67
|
+
|
|
68
|
+
## Secret Policy
|
|
69
|
+
|
|
70
|
+
Never print, echo, log, commit, or store PATs in project files. The only normal
|
|
71
|
+
local storage is `~/.mkterswingman/auth.json`, written by the helper.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: update-5mghost-twinkler
|
|
3
|
+
preamble-tier: 3
|
|
4
|
+
version: 0.2.0
|
|
5
|
+
description: |
|
|
6
|
+
Update the Twinkler helper package and refresh bundled AI skills.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Update 5mghost Twinkler
|
|
10
|
+
|
|
11
|
+
Use when the user asks to update, upgrade, repair stale skills, or pick up a new
|
|
12
|
+
Twinkler helper release.
|
|
13
|
+
|
|
14
|
+
## Default Path
|
|
15
|
+
|
|
16
|
+
Run:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
twinkler update
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
This runs the npm global update for `@mkterswingman/5mghost-twinkler@latest`.
|
|
23
|
+
The package postinstall refreshes bundled skills automatically.
|
|
24
|
+
|
|
25
|
+
If `twinkler update` is unavailable because the installed version is old, run:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install -g @mkterswingman/5mghost-twinkler@latest
|
|
29
|
+
twinkler setup
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Then ask the user to restart the AI session so updated skill text is loaded.
|
|
33
|
+
|
|
34
|
+
## Verify
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
twinkler version
|
|
38
|
+
twinkler doctor
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Do not ask the user to edit skill directories or hidden config files manually.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: use-5mghost-twinkler
|
|
3
3
|
preamble-tier: 3
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
description: |
|
|
6
6
|
Use when the user wants Twitch streamer, game, ranking, stream-session, CCV,
|
|
7
7
|
or chat-watch data from the mkterswingman Twinkler API.
|
|
@@ -10,7 +10,7 @@ description: |
|
|
|
10
10
|
|
|
11
11
|
# Use 5mghost Twinkler
|
|
12
12
|
|
|
13
|
-
Use the local `twinkler` helper. It
|
|
13
|
+
Use the local `twinkler` helper. It handles auth, refreshes short-lived
|
|
14
14
|
mkterswingman access tokens when available, and calls the hosted Twinkler REST
|
|
15
15
|
API. Prefer the helper over hand-written `curl`.
|
|
16
16
|
|
|
@@ -22,62 +22,67 @@ Run:
|
|
|
22
22
|
twinkler doctor
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
If the helper is missing
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
npm install -g @mkterswingman/5mghost-twinkler
|
|
29
|
-
twinkler setup
|
|
30
|
-
```
|
|
25
|
+
If the helper is missing or auth is missing, use `setup-5mghost-twinkler`.
|
|
26
|
+
Do not ask non-technical users to understand CLI concepts; operate the helper
|
|
27
|
+
for them and explain only the action they need to take.
|
|
31
28
|
|
|
32
29
|
## Auth
|
|
33
30
|
|
|
34
|
-
Twinkler uses the shared mkterswingman PAT. Do not call it a Twinkler PAT.
|
|
31
|
+
Twinkler uses the shared mkterswingman login/PAT. Do not call it a Twinkler PAT.
|
|
35
32
|
|
|
36
33
|
If auth is missing:
|
|
37
34
|
|
|
38
35
|
1. Give the user this URL: <https://mkterswingman.com/pat/login>
|
|
39
36
|
2. Ask the user to copy the mkterswingman PAT.
|
|
40
|
-
3. Start
|
|
41
|
-
|
|
37
|
+
3. Start `twinkler auth login --pat-stdin`.
|
|
38
|
+
4. Have the user paste the PAT into that local input, not into AI chat.
|
|
39
|
+
|
|
40
|
+
Never echo the PAT back to the user. Never put the PAT in source files, docs,
|
|
41
|
+
logs, URLs, screenshots, PR text, or committed `.env` files. Do not commit
|
|
42
|
+
`~/.mkterswingman/auth.json` or any generated secret/config file.
|
|
43
|
+
|
|
44
|
+
## Common Calls
|
|
42
45
|
|
|
43
|
-
|
|
46
|
+
Use:
|
|
44
47
|
|
|
45
48
|
```bash
|
|
46
|
-
twinkler
|
|
49
|
+
twinkler call GET /api/v1/channel/ibai/summary --query days=30
|
|
50
|
+
twinkler call GET /api/v1/rankings/channels --query sort_by=mostfollowers --query days=30 --query page_size=5
|
|
47
51
|
```
|
|
48
52
|
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
`twinkler call` only allows Twinkler `/api/v1/*` paths. It returns JSON on
|
|
54
|
+
stdout.
|
|
55
|
+
|
|
56
|
+
## Watch Jobs
|
|
57
|
+
|
|
58
|
+
Use watch jobs when the user asks to monitor a currently live Twitch channel,
|
|
59
|
+
collect CCV points, collect chat messages, or stop when the stream ends or
|
|
60
|
+
changes game.
|
|
51
61
|
|
|
52
|
-
|
|
62
|
+
Create:
|
|
53
63
|
|
|
54
64
|
```bash
|
|
55
|
-
twinkler
|
|
65
|
+
twinkler call POST /api/v1/twitch/watch --json '{"logins":["theburntpeanut"],"collect":["ccv","chat"],"start":{"type":"time","at":"now"},"stop":{"type":"game","game_name":"ARC Raiders"}}'
|
|
56
66
|
```
|
|
57
67
|
|
|
58
|
-
|
|
59
|
-
logs, URLs, screenshots, PR text, or committed `.env` files. Do not commit
|
|
60
|
-
`~/.mkterswingman/auth.json` or any generated secret/config file to a remote
|
|
61
|
-
repository. Do not ask non-technical users to paste PATs into chat; use the
|
|
62
|
-
helper's stdin path or a secure secret-entry UI.
|
|
63
|
-
|
|
64
|
-
For AI-managed cloud/server environments, store the PAT as a secret named
|
|
65
|
-
`MKTERSWINGMAN_PAT`. Do not explain environment variables to non-technical
|
|
66
|
-
users unless they ask; configure the target environment yourself when you have
|
|
67
|
-
tool access.
|
|
68
|
+
The response contains a `job_id`. Save it in your working notes. Then poll:
|
|
68
69
|
|
|
69
|
-
|
|
70
|
+
```bash
|
|
71
|
+
twinkler call GET /api/v1/twitch/watch/<job_id>
|
|
72
|
+
twinkler call GET /api/v1/twitch/watch/<job_id>/ccv
|
|
73
|
+
twinkler call GET /api/v1/twitch/watch/<job_id>/chat
|
|
74
|
+
```
|
|
70
75
|
|
|
71
|
-
|
|
76
|
+
Stop/cleanup when the user is done:
|
|
72
77
|
|
|
73
78
|
```bash
|
|
74
|
-
twinkler call
|
|
75
|
-
twinkler call GET /api/v1/rankings/channels --query sort_by=mostfollowers --query days=30 --query page_size=5
|
|
76
|
-
twinkler call POST /api/v1/twitch/watch --json '{"logins":["theburntpeanut"],"collect":["ccv","chat"],"start":{"type":"time","at":"now"},"stop":{"type":"game","game_name":"ARC Raiders"}}'
|
|
79
|
+
twinkler call DELETE /api/v1/twitch/watch/<job_id>
|
|
77
80
|
```
|
|
78
81
|
|
|
79
|
-
|
|
80
|
-
|
|
82
|
+
Normal logged-in users can create and delete their own watch jobs. If the API
|
|
83
|
+
returns `WATCH_DISABLED`, explain that the hosted watch worker is not enabled
|
|
84
|
+
right now. If it returns `AUTH_*`, repair mkterswingman auth. If it returns a
|
|
85
|
+
limit error, tell the user which limit was hit and stop creating more jobs.
|
|
81
86
|
|
|
82
87
|
## Calling The Helper From Scripts
|
|
83
88
|
|
|
@@ -110,8 +115,6 @@ store the mkterswingman PAT once, then scripts can call `twinkler call`.
|
|
|
110
115
|
Use direct REST only when the helper cannot be installed or the runtime is
|
|
111
116
|
non-Node, such as a Python-only cloud job. Direct REST still requires auth.
|
|
112
117
|
|
|
113
|
-
Python example:
|
|
114
|
-
|
|
115
118
|
```python
|
|
116
119
|
import os
|
|
117
120
|
import requests
|
package/skills.manifest.json
CHANGED
|
@@ -1,10 +1,54 @@
|
|
|
1
1
|
{
|
|
2
|
+
"schemaVersion": 1,
|
|
2
3
|
"product": "5mghost-twinkler",
|
|
3
4
|
"skills": [
|
|
5
|
+
{
|
|
6
|
+
"name": "setup-5mghost-twinkler",
|
|
7
|
+
"source": { "type": "local", "path": "./skills/setup-5mghost-twinkler" },
|
|
8
|
+
"targets": [
|
|
9
|
+
"claude",
|
|
10
|
+
"claude-internal",
|
|
11
|
+
"codex",
|
|
12
|
+
"codex-internal",
|
|
13
|
+
"gemini",
|
|
14
|
+
"gemini-internal",
|
|
15
|
+
"openclaw",
|
|
16
|
+
"workbuddy",
|
|
17
|
+
"codebuddy",
|
|
18
|
+
"agents"
|
|
19
|
+
]
|
|
20
|
+
},
|
|
4
21
|
{
|
|
5
22
|
"name": "use-5mghost-twinkler",
|
|
6
|
-
"source": "skills/use-5mghost-twinkler",
|
|
7
|
-
"
|
|
23
|
+
"source": { "type": "local", "path": "./skills/use-5mghost-twinkler" },
|
|
24
|
+
"targets": [
|
|
25
|
+
"claude",
|
|
26
|
+
"claude-internal",
|
|
27
|
+
"codex",
|
|
28
|
+
"codex-internal",
|
|
29
|
+
"gemini",
|
|
30
|
+
"gemini-internal",
|
|
31
|
+
"openclaw",
|
|
32
|
+
"workbuddy",
|
|
33
|
+
"codebuddy",
|
|
34
|
+
"agents"
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "update-5mghost-twinkler",
|
|
39
|
+
"source": { "type": "local", "path": "./skills/update-5mghost-twinkler" },
|
|
40
|
+
"targets": [
|
|
41
|
+
"claude",
|
|
42
|
+
"claude-internal",
|
|
43
|
+
"codex",
|
|
44
|
+
"codex-internal",
|
|
45
|
+
"gemini",
|
|
46
|
+
"gemini-internal",
|
|
47
|
+
"openclaw",
|
|
48
|
+
"workbuddy",
|
|
49
|
+
"codebuddy",
|
|
50
|
+
"agents"
|
|
51
|
+
]
|
|
8
52
|
}
|
|
9
53
|
]
|
|
10
54
|
}
|