@drunkcoding/agents-and-skills 0.0.44 → 0.0.45
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/multica-export.mjs +35 -18
- package/plugins/multica-tool/scripts/multica-import.mjs +17 -10
- package/plugins/multica-tool/skills/export/SKILL.md +7 -8
- package/plugins/multica-tool/skills/import/SKILL.md +2 -2
- 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.45",
|
|
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.45",
|
|
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.45",
|
|
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.45",
|
|
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.45",
|
|
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.45",
|
|
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"
|
|
@@ -5,6 +5,18 @@ import { slugify, getSkill, getAgent, getAgentCustomEnv, getSquad, getSquadMembe
|
|
|
5
5
|
|
|
6
6
|
const nonEmpty = (v) => v && typeof v === "object" && Object.keys(v).length > 0;
|
|
7
7
|
|
|
8
|
+
// Externalize a prose field to a sibling .md next to `jsonRel`
|
|
9
|
+
// (e.g. agents/x.json + ".description.md" → agents/x.description.md), and record
|
|
10
|
+
// the `<field>_file` pointer on `record`. Empty prose writes nothing and adds no
|
|
11
|
+
// key — same sibling-file pattern as avatar_file, applied uniformly to every
|
|
12
|
+
// resource's instructions/description so no prose is ever embedded in the JSON.
|
|
13
|
+
function writeSidecar(fs, outDir, jsonRel, suffix, text, record, key) {
|
|
14
|
+
if (!text) return;
|
|
15
|
+
const rel = jsonRel.replace(/\.json$/, suffix);
|
|
16
|
+
fs.writeFileSync(`${outDir}/${rel}`, text);
|
|
17
|
+
record[key] = rel;
|
|
18
|
+
}
|
|
19
|
+
|
|
8
20
|
// An avatar_url is either an uploaded-image URL (http[s]://…) or an inline
|
|
9
21
|
// "emoji:🦍" marker. Only image URLs carry bytes worth bundling; emoji markers
|
|
10
22
|
// are just carried as strings in the record.
|
|
@@ -31,7 +43,7 @@ export function redactAgent(a) {
|
|
|
31
43
|
// a is a normalized agent from getAgent, with `custom_env`/
|
|
32
44
|
// `custom_env_fetch_failed` attached by the caller (collectAgent) — getAgent
|
|
33
45
|
// itself never fetches custom_env, since it requires a separate audited call.
|
|
34
|
-
const { id, has_custom_env, mcp_config_redacted, custom_env_fetch_failed, mcp_config, custom_env, skills, runtime_id, instructions, archived_at, ...rest } = a;
|
|
46
|
+
const { id, has_custom_env, mcp_config_redacted, custom_env_fetch_failed, mcp_config, custom_env, skills, runtime_id, instructions, description, archived_at, ...rest } = a;
|
|
35
47
|
const mcpUsable = !mcp_config_redacted && nonEmpty(mcp_config);
|
|
36
48
|
const envUsable = !custom_env_fetch_failed && nonEmpty(custom_env);
|
|
37
49
|
// mcp_config_redacted / custom_env_fetch_failed alone still flag hadSecrets even
|
|
@@ -51,9 +63,10 @@ export function redactAgent(a) {
|
|
|
51
63
|
had_secrets: hadSecrets,
|
|
52
64
|
},
|
|
53
65
|
hadSecrets,
|
|
54
|
-
// instructions are written to
|
|
55
|
-
// never embedded in the JSON record.
|
|
66
|
+
// instructions and description are written to sibling .md files by the caller
|
|
67
|
+
// (see avatar_file), never embedded in the JSON record.
|
|
56
68
|
instructions: instructions ?? "",
|
|
69
|
+
description: description ?? "",
|
|
57
70
|
};
|
|
58
71
|
}
|
|
59
72
|
|
|
@@ -72,9 +85,11 @@ export function buildManifest({ scope, sourceWorkspaceId, skills, agents, squads
|
|
|
72
85
|
agents: [...seenAgents.values()].map((a) => ({ name: a.name, file: `agents/${slugify(a.name)}.json`, source_id: a.source_id, source_runtime_id: a.source_runtime_id, source_runtime_provider: a.source_runtime_provider ?? null, skill_names: a.skill_names, had_secrets: !!a.had_secrets })),
|
|
73
86
|
squads: (squads ?? []).map((squad) => {
|
|
74
87
|
const file = `squads/${slugify(squad.name)}.json`;
|
|
75
|
-
const entry = { name: squad.name, file,
|
|
76
|
-
// Instructions go to
|
|
88
|
+
const entry = { name: squad.name, file, avatar_url: squad.avatar_url ?? null, leader_name: squad.leader_name, members: squad.members };
|
|
89
|
+
// Instructions and description go to sibling .md files (see squad write
|
|
90
|
+
// loop); only referenced when non-empty.
|
|
77
91
|
if (squad.instructions) entry.instructions_file = file.replace(/\.json$/, ".md");
|
|
92
|
+
if (squad.description) entry.description_file = file.replace(/\.json$/, ".description.md");
|
|
78
93
|
return entry;
|
|
79
94
|
}),
|
|
80
95
|
projects: [...seenProjects.values()].map((p) => ({
|
|
@@ -304,32 +319,34 @@ export function exportResource({ cli, scope, ids, outDir, sourceWorkspaceId, fs
|
|
|
304
319
|
record.avatar_file = rel;
|
|
305
320
|
}
|
|
306
321
|
}
|
|
307
|
-
// Instructions live in a sibling .md for reviewability
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
const rel = entry.file.replace(/\.json$/, ".md");
|
|
311
|
-
fs.writeFileSync(`${outDir}/${rel}`, red.instructions);
|
|
312
|
-
record.instructions_file = rel;
|
|
313
|
-
}
|
|
322
|
+
// Instructions and description each live in a sibling .md for reviewability.
|
|
323
|
+
writeSidecar(fs, outDir, entry.file, ".md", red.instructions, record, "instructions_file");
|
|
324
|
+
writeSidecar(fs, outDir, entry.file, ".description.md", red.description, record, "description_file");
|
|
314
325
|
fs.writeFileSync(`${outDir}/${entry.file}`, JSON.stringify(record, null, 2));
|
|
315
326
|
}
|
|
316
|
-
const
|
|
327
|
+
const squadByName = new Map(squads.map((s) => [s.name, s]));
|
|
317
328
|
for (const entry of manifest.squads) {
|
|
318
329
|
fs.mkdirSync(`${outDir}/squads`, { recursive: true });
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
}
|
|
330
|
+
const s = squadByName.get(entry.name);
|
|
331
|
+
if (entry.instructions_file) fs.writeFileSync(`${outDir}/${entry.instructions_file}`, s?.instructions ?? "");
|
|
332
|
+
if (entry.description_file) fs.writeFileSync(`${outDir}/${entry.description_file}`, s?.description ?? "");
|
|
322
333
|
fs.writeFileSync(`${outDir}/${entry.file}`, JSON.stringify(entry, null, 2));
|
|
323
334
|
}
|
|
324
335
|
const projectByTitle = new Map(projects.map((p) => [p.title, p]));
|
|
325
336
|
for (const entry of manifest.projects) {
|
|
326
337
|
fs.mkdirSync(`${outDir}/projects`, { recursive: true });
|
|
327
|
-
|
|
338
|
+
const record = { ...projectByTitle.get(entry.title) };
|
|
339
|
+
const desc = record.description; delete record.description;
|
|
340
|
+
writeSidecar(fs, outDir, entry.file, ".description.md", desc, record, "description_file");
|
|
341
|
+
fs.writeFileSync(`${outDir}/${entry.file}`, JSON.stringify(record, null, 2));
|
|
328
342
|
}
|
|
329
343
|
const autopilotByTitle = new Map(autopilots.map((a) => [a.title, a]));
|
|
330
344
|
for (const entry of manifest.autopilots) {
|
|
331
345
|
fs.mkdirSync(`${outDir}/autopilots`, { recursive: true });
|
|
332
|
-
|
|
346
|
+
const record = { ...autopilotByTitle.get(entry.title) };
|
|
347
|
+
const desc = record.description; delete record.description;
|
|
348
|
+
writeSidecar(fs, outDir, entry.file, ".description.md", desc, record, "description_file");
|
|
349
|
+
fs.writeFileSync(`${outDir}/${entry.file}`, JSON.stringify(record, null, 2));
|
|
333
350
|
}
|
|
334
351
|
fs.writeFileSync(`${outDir}/manifest.json`, JSON.stringify(manifest, null, 2));
|
|
335
352
|
return {
|
|
@@ -8,15 +8,18 @@ export function parseInclude(raw) {
|
|
|
8
8
|
return set;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
//
|
|
12
|
-
// avatar_file). Legacy bundles carry no
|
|
13
|
-
// inline in the JSON — fall back to that so older exports
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
// A prose field (instructions/description) lives in a sibling .md referenced by
|
|
12
|
+
// `<field>_file` (mirrors avatar_file). Legacy bundles carry no `<field>_file`
|
|
13
|
+
// and keep the prose inline in the JSON — fall back to that so older exports
|
|
14
|
+
// still import.
|
|
15
|
+
function readSidecar(fs, dir, rec, fileKey, inlineKey) {
|
|
16
|
+
if (rec[fileKey] && fs.existsSync(`${dir}/${rec[fileKey]}`)) {
|
|
17
|
+
return fs.readFileSync(`${dir}/${rec[fileKey]}`, "utf8");
|
|
17
18
|
}
|
|
18
|
-
return rec
|
|
19
|
+
return rec[inlineKey] ?? "";
|
|
19
20
|
}
|
|
21
|
+
const readInstructions = (fs, dir, rec) => readSidecar(fs, dir, rec, "instructions_file", "instructions");
|
|
22
|
+
const readDescription = (fs, dir, rec) => readSidecar(fs, dir, rec, "description_file", "description");
|
|
20
23
|
|
|
21
24
|
// Relative paths of every file under root (recursing into subdirs like scripts/).
|
|
22
25
|
function walkSkillFiles(fs, root, rel = "") {
|
|
@@ -100,7 +103,8 @@ export function importAgents({ cli, manifest, dir, skillIdMap, runtimeMap, fs =
|
|
|
100
103
|
"--visibility", rec.visibility ?? "private",
|
|
101
104
|
"--max-concurrent-tasks", String(rec.max_concurrent_tasks ?? 6),
|
|
102
105
|
];
|
|
103
|
-
|
|
106
|
+
const description = readDescription(fs, dir, rec);
|
|
107
|
+
if (description) common.push("--description", description);
|
|
104
108
|
const instructions = readInstructions(fs, dir, rec);
|
|
105
109
|
if (instructions) common.push("--instructions", instructions);
|
|
106
110
|
if (rec.model) common.push("--model", rec.model);
|
|
@@ -269,7 +273,8 @@ export function importProjects({ cli, manifest, dir, agentIdMap, fs = nodeFs })
|
|
|
269
273
|
for (const entry of manifest.projects ?? []) {
|
|
270
274
|
const rec = JSON.parse(fs.readFileSync(`${dir}/${entry.file}`, "utf8"));
|
|
271
275
|
const flags = ["--title", rec.title];
|
|
272
|
-
|
|
276
|
+
const description = readDescription(fs, dir, rec);
|
|
277
|
+
if (description) flags.push("--description", description);
|
|
273
278
|
if (rec.icon) flags.push("--icon", rec.icon);
|
|
274
279
|
if (rec.status) flags.push("--status", rec.status);
|
|
275
280
|
if (rec.due_date) flags.push("--due-date", rec.due_date);
|
|
@@ -347,7 +352,8 @@ export function importAutopilots({ cli, manifest, dir, agentIdMap, fs = nodeFs }
|
|
|
347
352
|
|
|
348
353
|
for (const rec of recs) {
|
|
349
354
|
const common = [];
|
|
350
|
-
|
|
355
|
+
const description = readDescription(fs, dir, rec);
|
|
356
|
+
if (description) common.push("--description", description);
|
|
351
357
|
if (rec.issue_title_template) common.push("--issue-title-template", rec.issue_title_template);
|
|
352
358
|
// priority is never present today — the multica CLI/API accepts it on write but
|
|
353
359
|
// never returns it on read, so export can't capture the source's real value.
|
|
@@ -469,6 +475,7 @@ export function importBundle({ cli, dir, runtimeMap, include, fs = nodeFs }) {
|
|
|
469
475
|
if (inc.has("squads")) {
|
|
470
476
|
for (const squad of manifest.squads ?? []) {
|
|
471
477
|
squad.instructions = readInstructions(fs, dir, squad);
|
|
478
|
+
squad.description = readDescription(fs, dir, squad);
|
|
472
479
|
const r = importSquad({ cli, squad, agentIdMap: agentRes.idMap, sourceIdMap: agentRes.sourceIdMap });
|
|
473
480
|
if (r.skipped) { squadsSkipped.push(squad.name); continue; }
|
|
474
481
|
squadIdMap.set(squad.name, r.newId);
|
|
@@ -37,13 +37,7 @@ If the user specified an output directory, use it verbatim. Otherwise default to
|
|
|
37
37
|
First resolve `<workspace-name>`:
|
|
38
38
|
|
|
39
39
|
- If the user named a source workspace (the value you would pass as `--workspace <name>`), use that name.
|
|
40
|
-
- Otherwise,
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
multica workspace get --output json
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
and take its `.name`.
|
|
40
|
+
- Otherwise, run `multica workspace get --output json` and take its `.name` (the current default workspace).
|
|
47
41
|
|
|
48
42
|
Slugify the resolved name for filesystem safety — lowercase it, replace each run of non-`[a-z0-9]` characters with a single `-`, and trim leading/trailing `-` (the same rule the scripts use internally).
|
|
49
43
|
|
|
@@ -70,7 +64,12 @@ Pass `--scope all` (with no `--id`) to export the **entire workspace** — every
|
|
|
70
64
|
|
|
71
65
|
Exporting a project (or `projects`/`all`) also **bundles the project's lead agent** so the bundle is self-contained; projects carry metadata only (title, description, icon, priority, status, dates, lead mapping) plus their attached resource records — never issues. On import, only `github_repo` resources are portable and recreated; other resource types are reported and skipped.
|
|
72
66
|
|
|
73
|
-
The script writes `manifest.json`, skill `SKILL.md` files, agent JSON files, and squad JSON files into `<dir>`.
|
|
67
|
+
The script writes `manifest.json`, skill `SKILL.md` files, agent JSON files, and squad JSON files into `<dir>`. Every resource's prose fields are externalized to sibling Markdown files, never embedded in the JSON — so they are easy to read, diff, and edit:
|
|
68
|
+
|
|
69
|
+
- **instructions** (system prompt / charter, agents and squads) → `<slug>.md`, referenced by an `instructions_file` key.
|
|
70
|
+
- **description** (agents, squads, projects, autopilots) → `<slug>.description.md`, referenced by a `description_file` key.
|
|
71
|
+
|
|
72
|
+
An empty field gets no file and no `*_file` key. Skills keep their own layout — the description lives in `SKILL.md` frontmatter and the body is the content.
|
|
74
73
|
|
|
75
74
|
Avatars are captured automatically: an agent's uploaded-image avatar is downloaded into the bundle (`agents/<slug>.avatar.<ext>`) and referenced by `avatar_file`; emoji avatars (agents and squads) and a squad's avatar are recorded as the `avatar_url` string.
|
|
76
75
|
|
|
@@ -13,7 +13,7 @@ Import a local Multica bundle (produced by the export skill) into a target works
|
|
|
13
13
|
You need the exact workspace name as registered in Multica for `--workspace` in the steps below.
|
|
14
14
|
|
|
15
15
|
- If the user named a target workspace, use it.
|
|
16
|
-
- If the user did **not** name one, default to the **basename of the import folder** — e.g. importing from `export/mx-workspace` defaults the target workspace to `mx-workspace`. State the inferred workspace name to the user before continuing.
|
|
16
|
+
- If the user did **not** name one, default to the **basename of the import folder** — e.g. importing from `export/mx-workspace` defaults the target workspace to `mx-workspace`. State the inferred workspace name to the user before continuing. This default is reliable for whole-workspace bundles (`export/<workspace-name>`); a single-resource bundle nested under `export/<workspace-name>/<slug>-<type>` has the resource slug as its basename, not the workspace — name the target workspace explicitly in that case.
|
|
17
17
|
|
|
18
18
|
No existence check is needed here: the Step 2 dry-run fails with `Unknown workspace "<name>"` if the inferred workspace is not present in the target account, at which point ask the user for the correct name.
|
|
19
19
|
|
|
@@ -70,7 +70,7 @@ If it instead aborts with `Unresolved autopilot assignees: ...`, that is not fix
|
|
|
70
70
|
|
|
71
71
|
The import also rewrites any `mention://agent/<id>` link inside squad and agent instructions (e.g. `[@dev-backend](mention://agent/<id>)`) from the source agent's id to its new id in the target workspace — the CLI does this automatically for every agent captured in the bundle; no extra flag needed. Mentions pointing to an agent outside the bundle are left untouched.
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
Prose fields are read back from each resource's sibling `.md` when a `*_file` key points at it — **instructions** from `<slug>.md` (agents, squads) and **description** from `<slug>.description.md` (agents, squads, projects, autopilots). Editing that Markdown is the supported way to review and enhance the prose before import. Older bundles that predate the split (prose inline in the JSON, no `*_file` key) still import unchanged.
|
|
74
74
|
|
|
75
75
|
Avatars are restored automatically, but **only when the target resource has none** — an existing agent or squad that already carries an avatar is never overwritten. New agents get their bundled image re-uploaded; new squads get their `avatar_url` (emoji or URL) set. An agent whose source avatar was an emoji can't be restored (the CLI has no emoji setter for agents) and is reported as unsupported.
|
|
76
76
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plugin-validator",
|
|
3
3
|
"displayName": "Plugin Validator",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.45",
|
|
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.45",
|
|
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"
|