@drunkcoding/agents-and-skills 0.0.32 → 0.0.34
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/.claude-plugin/marketplace.json +5 -5
- package/package.json +1 -1
- package/plugins/html-effectiveness/.claude-plugin/plugin.json +1 -1
- package/plugins/multica-tool/.claude-plugin/plugin.json +1 -1
- package/plugins/multica-tool/scripts/build-dist.mjs +71 -0
- package/plugins/multica-tool/scripts/lib.mjs +1 -1
- package/plugins/multica-tool/scripts/multica-export.mjs +8 -2
- package/plugins/multica-tool/scripts/multica-import.mjs +40 -10
- package/plugins/multica-tool/skills/export/SKILL.md +2 -2
- package/plugins/multica-tool/skills/import/SKILL.md +1 -1
- package/plugins/multica-tool/skills/sync/SKILL.md +1 -1
- package/plugins/plugin-validator/.claude-plugin/plugin.json +1 -1
- package/plugins/team-share/.claude-plugin/plugin.json +1 -1
- package/plugins/tech-graph/.claude-plugin/plugin.json +1 -1
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"name": "tech-graph",
|
|
13
13
|
"source": "./plugins/tech-graph",
|
|
14
14
|
"description": "6-step wizard for technical diagrams (SVG/PNG) via fireworks-tech-graph",
|
|
15
|
-
"version": "0.0.
|
|
15
|
+
"version": "0.0.34",
|
|
16
16
|
"category": "diagram",
|
|
17
17
|
"keywords": [
|
|
18
18
|
"diagram",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"name": "html-effectiveness",
|
|
27
27
|
"source": "./plugins/html-effectiveness",
|
|
28
28
|
"description": "Generate self-contained interactive HTML reports from 20 upstream templates via a conversational agent.",
|
|
29
|
-
"version": "0.0.
|
|
29
|
+
"version": "0.0.34",
|
|
30
30
|
"category": "reports",
|
|
31
31
|
"keywords": [
|
|
32
32
|
"html",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"name": "plugin-validator",
|
|
42
42
|
"source": "./plugins/plugin-validator",
|
|
43
43
|
"description": "Orchestrated validator for Claude Code plugins — validates skills, agents, commands, and hooks across every plugin under plugins/**.",
|
|
44
|
-
"version": "0.0.
|
|
44
|
+
"version": "0.0.34",
|
|
45
45
|
"category": "tooling",
|
|
46
46
|
"keywords": [
|
|
47
47
|
"validation",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"name": "team-share",
|
|
58
58
|
"source": "./plugins/team-share",
|
|
59
59
|
"description": "Onboard your team with an interactive setup menu: install CodeGraph, build the Understand-Anything knowledge graph, and share Claude Code settings — run any combination, all idempotent.",
|
|
60
|
-
"version": "0.0.
|
|
60
|
+
"version": "0.0.34",
|
|
61
61
|
"category": "tooling",
|
|
62
62
|
"keywords": [
|
|
63
63
|
"onboarding",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"name": "multica-tool",
|
|
74
74
|
"source": "./plugins/multica-tool",
|
|
75
75
|
"description": "Export, import, and sync Multica skills, agents, and squads between workspaces.",
|
|
76
|
-
"version": "0.0.
|
|
76
|
+
"version": "0.0.34",
|
|
77
77
|
"category": "workflow",
|
|
78
78
|
"keywords": [
|
|
79
79
|
"multica",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "html-effectiveness",
|
|
3
3
|
"displayName": "HTML Effectiveness Reports",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.34",
|
|
5
5
|
"description": "Generate self-contained interactive HTML reports from 20 upstream templates via a conversational agent.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Steven Hoang"
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Generate OpenCode discovery artifacts from the canonical Claude Code plugin.
|
|
3
|
+
// Source of truth: this plugin's skills/ + agents/ + commands/ + scripts/.
|
|
4
|
+
// Output: dist/opencode/{skills,agents,commands}/ — copy into .opencode/ (project)
|
|
5
|
+
// or ~/.config/opencode/ (global). Run: node plugins/multica-tool/scripts/build-dist.mjs
|
|
6
|
+
import { readFileSync, writeFileSync, mkdirSync, readdirSync, rmSync, copyFileSync } from "node:fs";
|
|
7
|
+
import { dirname, join } from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
|
|
10
|
+
const PLUGIN_ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
11
|
+
const REPO_ROOT = dirname(dirname(PLUGIN_ROOT));
|
|
12
|
+
const OUT = join(REPO_ROOT, "dist", "opencode");
|
|
13
|
+
const NAMES = ["export", "import", "sync"];
|
|
14
|
+
// Runtime scripts shared by every skill (exclude this generator).
|
|
15
|
+
const SCRIPTS = readdirSync(join(PLUGIN_ROOT, "scripts")).filter(
|
|
16
|
+
(f) => f.endsWith(".mjs") && f !== "build-dist.mjs",
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
// Naive frontmatter split — every source file uses single-line `key: value`.
|
|
20
|
+
function parse(text) {
|
|
21
|
+
const m = text.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
22
|
+
if (!m) throw new Error("missing frontmatter");
|
|
23
|
+
const fm = {};
|
|
24
|
+
for (const line of m[1].split("\n")) {
|
|
25
|
+
const i = line.indexOf(":");
|
|
26
|
+
if (i !== -1) fm[line.slice(0, i).trim()] = line.slice(i + 1).trim();
|
|
27
|
+
}
|
|
28
|
+
return { fm, body: m[2] };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const read = (...p) => readFileSync(join(PLUGIN_ROOT, ...p), "utf8");
|
|
32
|
+
function write(rel, content) {
|
|
33
|
+
const abs = join(OUT, rel);
|
|
34
|
+
mkdirSync(dirname(abs), { recursive: true });
|
|
35
|
+
writeFileSync(abs, content);
|
|
36
|
+
}
|
|
37
|
+
// OpenCode resolves a skill's relative paths against the skill base dir it injects.
|
|
38
|
+
const toOpencodePath = (s) => s.replace(/"\$\{CLAUDE_PLUGIN_ROOT\}\/scripts\//g, '"scripts/');
|
|
39
|
+
const stripSelfPrefix = (s) => s.replace(/multica-tool:/g, "");
|
|
40
|
+
|
|
41
|
+
rmSync(OUT, { recursive: true, force: true });
|
|
42
|
+
|
|
43
|
+
for (const name of NAMES) {
|
|
44
|
+
// Skill: keep prose, rewrite script path, drop Claude-only allowed-tools, bundle scripts.
|
|
45
|
+
const skill = parse(read("skills", name, "SKILL.md"));
|
|
46
|
+
const fm = `---\nname: ${skill.fm.name}\ndescription: ${skill.fm.description}\n---\n`;
|
|
47
|
+
write(`skills/${name}/SKILL.md`, fm + toOpencodePath(skill.body));
|
|
48
|
+
for (const s of SCRIPTS) {
|
|
49
|
+
mkdirSync(join(OUT, "skills", name, "scripts"), { recursive: true });
|
|
50
|
+
copyFileSync(join(PLUGIN_ROOT, "scripts", s), join(OUT, "skills", name, "scripts", s));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Command: description only; body invokes the local (unprefixed) skill name.
|
|
54
|
+
const cmd = parse(read("commands", `${name}.md`));
|
|
55
|
+
write(`commands/${name}.md`, `---\ndescription: ${stripSelfPrefix(cmd.fm.description)}\n---\n${stripSelfPrefix(cmd.body)}`);
|
|
56
|
+
|
|
57
|
+
// Agent: tools -> permission.bash; body unprefixed.
|
|
58
|
+
const agent = parse(read("agents", `${name}.md`));
|
|
59
|
+
const head = `---\ndescription: ${stripSelfPrefix(agent.fm.description)}\nmode: subagent\npermission:\n bash: allow\n---\n`;
|
|
60
|
+
write(`agents/${name}.md`, head + stripSelfPrefix(agent.body));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Self-check: outputs must carry no Claude-only path/env and must bundle scripts.
|
|
64
|
+
for (const name of NAMES) {
|
|
65
|
+
const md = readFileSync(join(OUT, "skills", name, "SKILL.md"), "utf8");
|
|
66
|
+
if (md.includes("CLAUDE_PLUGIN_ROOT") || md.includes("plugins/multica-tool"))
|
|
67
|
+
throw new Error(`leaked Claude path in ${name}/SKILL.md`);
|
|
68
|
+
for (const s of SCRIPTS) readFileSync(join(OUT, "skills", name, "scripts", s)); // throws if missing
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
console.log(`Wrote OpenCode artifacts for [${NAMES.join(", ")}] to ${OUT}`);
|
|
@@ -84,7 +84,7 @@ export function getAgent(cli, id) {
|
|
|
84
84
|
|
|
85
85
|
export function getSquad(cli, id) {
|
|
86
86
|
const s = cli.json(["squad", "get", id]);
|
|
87
|
-
return { id: s.id, name: s.name, description: s.description, leaderId: s.leader_id };
|
|
87
|
+
return { id: s.id, name: s.name, description: s.description, instructions: s.instructions, leaderId: s.leader_id };
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
export const getSquadMembers = (cli, id) =>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as nodeFs from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
2
3
|
import { slugify, getSkill, getAgent, getSquad, getSquadMembers, makeCli, realExec, requireAuth, resolveWorkspaceId } from "./lib.mjs";
|
|
3
4
|
|
|
4
5
|
const nonEmpty = (v) => v && typeof v === "object" && Object.keys(v).length > 0;
|
|
@@ -24,7 +25,7 @@ export function buildManifest({ scope, sourceWorkspaceId, skills, agents, squad
|
|
|
24
25
|
sourceWorkspaceId,
|
|
25
26
|
skills: [...seenSkills.values()].map((s) => ({ name: s.name, dir: `skills/${slugify(s.name)}`, sourceId: s.sourceId })),
|
|
26
27
|
agents: [...seenAgents.values()].map((a) => ({ name: a.name, file: `agents/${slugify(a.name)}.json`, sourceRuntimeId: a.sourceRuntimeId, skillNames: a.skillNames, hadSecrets: !!a.hadSecrets })),
|
|
27
|
-
squads: squad ? [{ name: squad.name, file: `squads/${slugify(squad.name)}.json`, description: squad.description ?? "", leaderName: squad.leaderName, members: squad.members }] : [],
|
|
28
|
+
squads: squad ? [{ name: squad.name, file: `squads/${slugify(squad.name)}.json`, description: squad.description ?? "", instructions: squad.instructions ?? "", leaderName: squad.leaderName, members: squad.members }] : [],
|
|
28
29
|
};
|
|
29
30
|
}
|
|
30
31
|
|
|
@@ -62,6 +63,7 @@ export function exportResource({ cli, scope, ids, outDir, sourceWorkspaceId, fs
|
|
|
62
63
|
squad = {
|
|
63
64
|
name: sq.name,
|
|
64
65
|
description: sq.description,
|
|
66
|
+
instructions: sq.instructions,
|
|
65
67
|
leaderName: nameOf(sq.leaderId),
|
|
66
68
|
members: members.map((m) => ({ agentName: nameOf(m.memberId), role: m.role })),
|
|
67
69
|
};
|
|
@@ -83,7 +85,11 @@ export function exportResource({ cli, scope, ids, outDir, sourceWorkspaceId, fs
|
|
|
83
85
|
fs.mkdirSync(dir, { recursive: true });
|
|
84
86
|
fs.writeFileSync(`${dir}/SKILL.md`, s.content ?? "");
|
|
85
87
|
fs.writeFileSync(`${dir}/config.json`, JSON.stringify(s.config ?? {}, null, 2));
|
|
86
|
-
for (const f of s.files ?? [])
|
|
88
|
+
for (const f of s.files ?? []) {
|
|
89
|
+
const target = `${dir}/${f.path}`;
|
|
90
|
+
fs.mkdirSync(dirname(target), { recursive: true }); // f.path may be nested, e.g. scripts/foo.sh
|
|
91
|
+
fs.writeFileSync(target, f.content ?? "");
|
|
92
|
+
}
|
|
87
93
|
}
|
|
88
94
|
// Index agent entries by name for the manifest writing loop.
|
|
89
95
|
const agentByName = new Map([...agentsById.values()].map((a) => [a.raw.name, a]));
|
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
import * as nodeFs from "node:fs";
|
|
2
|
-
import { listSkills, listAgents, listSquads, findByName, makeCli, realExec, requireAuth, resolveWorkspaceId } from "./lib.mjs";
|
|
2
|
+
import { listSkills, listAgents, listSquads, getSquadMembers, findByName, makeCli, realExec, requireAuth, resolveWorkspaceId } from "./lib.mjs";
|
|
3
|
+
|
|
4
|
+
// Relative paths of every file under root (recursing into subdirs like scripts/).
|
|
5
|
+
function walkSkillFiles(fs, root, rel = "") {
|
|
6
|
+
const out = [];
|
|
7
|
+
for (const ent of fs.readdirSync(rel ? `${root}/${rel}` : root, { withFileTypes: true })) {
|
|
8
|
+
const r = rel ? `${rel}/${ent.name}` : ent.name;
|
|
9
|
+
if (ent.isDirectory()) out.push(...walkSkillFiles(fs, root, r));
|
|
10
|
+
else out.push(r);
|
|
11
|
+
}
|
|
12
|
+
return out;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Pull `description:` out of a SKILL.md YAML frontmatter block.
|
|
16
|
+
// ponytail: single-line values only (the skill frontmatter convention); folded/multi-line YAML not handled.
|
|
17
|
+
function frontmatterDescription(text) {
|
|
18
|
+
const block = /^---\r?\n([\s\S]*?)\r?\n---/.exec(text);
|
|
19
|
+
if (!block) return "";
|
|
20
|
+
const line = block[1].split(/\r?\n/).find((l) => /^description\s*:/.test(l));
|
|
21
|
+
return line ? line.replace(/^description\s*:/, "").trim().replace(/^["']|["']$/g, "") : "";
|
|
22
|
+
}
|
|
3
23
|
|
|
4
24
|
export function importSkills({ cli, manifest, dir, fs = nodeFs }) {
|
|
5
25
|
const idMap = new Map();
|
|
@@ -12,19 +32,24 @@ export function importSkills({ cli, manifest, dir, fs = nodeFs }) {
|
|
|
12
32
|
const configPath = `${sdir}/config.json`;
|
|
13
33
|
const config = fs.existsSync(configPath) ? fs.readFileSync(configPath, "utf8") : "{}";
|
|
14
34
|
const match = findByName(existing, s.name);
|
|
35
|
+
// Fall back to the SKILL.md frontmatter description when the manifest carries none.
|
|
36
|
+
const fmDesc = frontmatterDescription(fs.readFileSync(contentPath, "utf8"));
|
|
15
37
|
let id;
|
|
16
38
|
if (match) {
|
|
17
|
-
|
|
39
|
+
// Only fill description when the existing skill has none — don't clobber a set one.
|
|
40
|
+
const desc = !match.description && fmDesc ? ["--description", fmDesc] : [];
|
|
41
|
+
cli.run(["skill", "update", match.id, "--content-file", contentPath, "--config", config, ...desc]);
|
|
18
42
|
id = match.id; updated++;
|
|
19
43
|
} else {
|
|
20
|
-
const
|
|
44
|
+
const desc = fmDesc ? ["--description", fmDesc] : [];
|
|
45
|
+
const out = cli.run(["skill", "create", "--name", s.name, "--content-file", contentPath, "--config", config, ...desc]);
|
|
21
46
|
id = JSON.parse(out).id; created++;
|
|
22
47
|
}
|
|
23
48
|
idMap.set(s.name, id);
|
|
24
|
-
// upsert extra files (everything except SKILL.md and config.json)
|
|
25
|
-
for (const
|
|
26
|
-
if (
|
|
27
|
-
cli.run(["skill", "files", "upsert", id, "--path",
|
|
49
|
+
// upsert extra files (everything except SKILL.md and config.json), by relative path
|
|
50
|
+
for (const rel of walkSkillFiles(fs, sdir)) {
|
|
51
|
+
if (rel === "SKILL.md" || rel === "config.json") continue;
|
|
52
|
+
cli.run(["skill", "files", "upsert", id, "--path", rel, "--content-file", `${sdir}/${rel}`]);
|
|
28
53
|
}
|
|
29
54
|
}
|
|
30
55
|
return { idMap, created, updated };
|
|
@@ -70,16 +95,21 @@ export function importSquad({ cli, squad, agentIdMap }) {
|
|
|
70
95
|
const leaderId = agentIdMap.get(squad.leaderName);
|
|
71
96
|
const match = findByName(existing, squad.name);
|
|
72
97
|
let id, created = 0, updated = 0;
|
|
98
|
+
const instr = squad.instructions ? ["--instructions", squad.instructions] : [];
|
|
73
99
|
if (match) {
|
|
74
|
-
cli.run(["squad", "update", match.id, "--leader", leaderId, "--description", squad.description ?? ""]);
|
|
100
|
+
cli.run(["squad", "update", match.id, "--leader", leaderId, "--description", squad.description ?? "", ...instr]);
|
|
75
101
|
id = match.id; updated++;
|
|
76
102
|
} else {
|
|
77
|
-
const out = cli.run(["squad", "create", "--name", squad.name, "--leader", leaderId, "--description", squad.description ?? ""]);
|
|
103
|
+
const out = cli.run(["squad", "create", "--name", squad.name, "--leader", leaderId, "--description", squad.description ?? "", ...instr]);
|
|
78
104
|
id = JSON.parse(out).id; created++;
|
|
79
105
|
}
|
|
106
|
+
// Add non-leader members, skipping any already present so re-runs are idempotent.
|
|
107
|
+
const present = new Set(getSquadMembers(cli, id).map((m) => m.memberId));
|
|
80
108
|
for (const m of squad.members) {
|
|
81
109
|
if (m.agentName === squad.leaderName) continue;
|
|
82
|
-
|
|
110
|
+
const memberId = agentIdMap.get(m.agentName);
|
|
111
|
+
if (present.has(memberId)) continue;
|
|
112
|
+
cli.run(["squad", "member", "add", id, "--member-id", memberId, "--role", m.role, "--type", "agent"]);
|
|
83
113
|
}
|
|
84
114
|
return { newId: id, created, updated };
|
|
85
115
|
}
|
|
@@ -13,7 +13,7 @@ Export a Multica resource (skill, agent, or squad) to a local bundle directory.
|
|
|
13
13
|
Run the export script; it calls `multica auth status` internally and exits with an error message if unauthenticated:
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
node
|
|
16
|
+
node "${CLAUDE_PLUGIN_ROOT}/scripts/multica-export.mjs" --help 2>&1 || true
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
If `multica login` is required, surface that message verbatim and stop.
|
|
@@ -43,7 +43,7 @@ where `<slug>` is a lowercased, hyphenated form of the resource name.
|
|
|
43
43
|
## Step 4 — Run the export
|
|
44
44
|
|
|
45
45
|
```bash
|
|
46
|
-
node
|
|
46
|
+
node "${CLAUDE_PLUGIN_ROOT}/scripts/multica-export.mjs" \
|
|
47
47
|
--scope <type> \
|
|
48
48
|
--id <id> \
|
|
49
49
|
--out <dir> \
|
|
@@ -33,7 +33,7 @@ For each distinct `sourceRuntimeId`, ask the user to pick a matching target runt
|
|
|
33
33
|
## Step 3 — Run the import
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
|
-
node
|
|
36
|
+
node "${CLAUDE_PLUGIN_ROOT}/scripts/multica-import.mjs" \
|
|
37
37
|
--dir <folder> \
|
|
38
38
|
--workspace <workspace-name> \
|
|
39
39
|
--runtime-map <srcId1=dstId1,srcId2=dstId2,...>
|
|
@@ -33,7 +33,7 @@ For skills (which have no runtime dependency), an empty runtime map is acceptabl
|
|
|
33
33
|
## Step 3 — Run the sync
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
|
-
node
|
|
36
|
+
node "${CLAUDE_PLUGIN_ROOT}/scripts/multica-sync.mjs" \
|
|
37
37
|
<type> <name> from <src-ws> <dest-ws> \
|
|
38
38
|
[--runtime-map <srcId1=dstId1,srcId2=dstId2,...>]
|
|
39
39
|
```
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plugin-validator",
|
|
3
3
|
"displayName": "Plugin Validator",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.34",
|
|
5
5
|
"description": "Orchestrated validator for Claude Code plugins — validates skills, agents, commands, and hooks across every plugin under plugins/**.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Steven Hoang"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "team-share",
|
|
3
3
|
"displayName": "Team Share",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.34",
|
|
5
5
|
"description": "Onboard your team with an interactive setup menu: install CodeGraph, build the Understand-Anything knowledge graph, and share Claude Code settings — run any combination, all idempotent.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Steven Hoang"
|