@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.
@@ -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.44",
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.44",
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",
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.44",
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.44",
76
+ "version": "0.0.45",
77
77
  "category": "workflow",
78
78
  "keywords": [
79
79
  "multica",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drunkcoding/agents-and-skills",
3
- "version": "0.0.44",
3
+ "version": "0.0.45",
4
4
  "description": "Personal collection of Claude Code skills and agents, installable via `npx skills`.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "html-effectiveness",
3
3
  "displayName": "HTML Effectiveness Reports",
4
- "version": "0.0.44",
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"
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "multica-tool",
3
- "version": "0.0.44",
3
+ "version": "0.0.45",
4
4
  "description": "Export, import, and sync Multica skills, agents, and squads between workspaces via the multica CLI."
5
5
  }
@@ -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 a sibling .md by the caller (see avatar_file),
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, description: squad.description ?? "", avatar_url: squad.avatar_url ?? null, leader_name: squad.leader_name, members: squad.members };
76
- // Instructions go to a sibling .md (see squad write loop); only referenced when non-empty.
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 (same sibling-file
308
- // pattern as avatar_file); only written when non-empty.
309
- if (red.instructions) {
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 squadInstrByName = new Map(squads.map((s) => [s.name, s.instructions ?? ""]));
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
- if (entry.instructions_file) {
320
- fs.writeFileSync(`${outDir}/${entry.instructions_file}`, squadInstrByName.get(entry.name) ?? "");
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
- fs.writeFileSync(`${outDir}/${entry.file}`, JSON.stringify(projectByTitle.get(entry.title), null, 2));
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
- fs.writeFileSync(`${outDir}/${entry.file}`, JSON.stringify(autopilotByTitle.get(entry.title), null, 2));
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
- // Instructions live in a sibling .md referenced by `instructions_file` (mirrors
12
- // avatar_file). Legacy bundles carry no instructions_file and keep instructions
13
- // inline in the JSON — fall back to that so older exports still import.
14
- function readInstructions(fs, dir, rec) {
15
- if (rec.instructions_file && fs.existsSync(`${dir}/${rec.instructions_file}`)) {
16
- return fs.readFileSync(`${dir}/${rec.instructions_file}`, "utf8");
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.instructions ?? "";
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
- if (rec.description) common.push("--description", rec.description);
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
- if (rec.description) flags.push("--description", rec.description);
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
- if (rec.description) common.push("--description", rec.description);
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, read the current default workspace's name:
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>`. Each agent's and squad's **instructions** (system prompt / charter) are written to a sibling Markdown file `agents/<slug>.md`, `squads/<slug>.md` — referenced by an `instructions_file` key in the JSON, so the prose is easy to read, diff, and edit. Agents/squads with no instructions get no `.md`.
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
- Instructions are read back from each resource's sibling `.md` (`agents/<slug>.md`, `squads/<slug>.md`) when present editing that Markdown is the supported way to review and enhance an agent's or squad's instructions before import. Older bundles that predate the split (instructions inline in the JSON, no `instructions_file`) still import unchanged.
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.44",
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.44",
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"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tech-graph",
3
- "version": "0.0.44",
3
+ "version": "0.0.45",
4
4
  "description": "Step-by-step wizard for generating technical diagrams as SVG+PNG.",
5
5
  "author": {
6
6
  "name": "steven"