@drunkcoding/agents-and-skills 0.0.45 → 0.0.46

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.
Files changed (107) hide show
  1. package/.claude-plugin/marketplace.json +2 -47
  2. package/README.md +3 -6
  3. package/package.json +4 -1
  4. package/plugins/multica-tool/.claude-plugin/plugin.json +1 -1
  5. package/plugins/multica-tool/scripts/lib.mjs +53 -4
  6. package/plugins/multica-tool/scripts/multica-export.mjs +135 -32
  7. package/plugins/multica-tool/scripts/multica-import.mjs +192 -17
  8. package/plugins/multica-tool/skills/export/SKILL.md +84 -14
  9. package/plugins/multica-tool/skills/import/SKILL.md +22 -7
  10. package/plugins/multica-tool/skills/sync/SKILL.md +1 -1
  11. package/plugins/plugin-validator/.claude-plugin/plugin.json +1 -1
  12. package/plugins/html-effectiveness/.claude-plugin/plugin.json +0 -17
  13. package/plugins/html-effectiveness/README.md +0 -28
  14. package/plugins/html-effectiveness/agents/report-builder.md +0 -66
  15. package/plugins/html-effectiveness/assets/base.css +0 -51
  16. package/plugins/html-effectiveness/assets/base.js +0 -11
  17. package/plugins/html-effectiveness/assets/charts.js +0 -1
  18. package/plugins/html-effectiveness/assets/components.css +0 -10
  19. package/plugins/html-effectiveness/commands/html-report.md +0 -13
  20. package/plugins/html-effectiveness/docs/template-gallery.md +0 -28
  21. package/plugins/html-effectiveness/package.json +0 -6
  22. package/plugins/html-effectiveness/scripts/_tmp.js +0 -37
  23. package/plugins/html-effectiveness/scripts/catalog.js +0 -47
  24. package/plugins/html-effectiveness/scripts/extract.js +0 -104
  25. package/plugins/html-effectiveness/scripts/mustache.js +0 -90
  26. package/plugins/html-effectiveness/scripts/render.js +0 -176
  27. package/plugins/html-effectiveness/skills/html-effectiveness/SKILL.md +0 -94
  28. package/plugins/html-effectiveness/templates/01-exploration-code-approaches.html.tmpl +0 -400
  29. package/plugins/html-effectiveness/templates/02-exploration-visual-designs.html.tmpl +0 -456
  30. package/plugins/html-effectiveness/templates/03-code-review-pr.html.tmpl +0 -581
  31. package/plugins/html-effectiveness/templates/04-code-understanding.html.tmpl +0 -443
  32. package/plugins/html-effectiveness/templates/05-design-system.html.tmpl +0 -578
  33. package/plugins/html-effectiveness/templates/06-component-variants.html.tmpl +0 -557
  34. package/plugins/html-effectiveness/templates/07-prototype-animation.html.tmpl +0 -417
  35. package/plugins/html-effectiveness/templates/08-prototype-interaction.html.tmpl +0 -363
  36. package/plugins/html-effectiveness/templates/09-slide-deck.html.tmpl +0 -535
  37. package/plugins/html-effectiveness/templates/10-svg-illustrations.html.tmpl +0 -444
  38. package/plugins/html-effectiveness/templates/11-status-report.html.tmpl +0 -152
  39. package/plugins/html-effectiveness/templates/12-incident-report.html.tmpl +0 -547
  40. package/plugins/html-effectiveness/templates/13-flowchart-diagram.html.tmpl +0 -361
  41. package/plugins/html-effectiveness/templates/14-research-feature-explainer.html.tmpl +0 -348
  42. package/plugins/html-effectiveness/templates/15-research-concept-explainer.html.tmpl +0 -334
  43. package/plugins/html-effectiveness/templates/16-implementation-plan.html.tmpl +0 -657
  44. package/plugins/html-effectiveness/templates/17-pr-writeup.html.tmpl +0 -553
  45. package/plugins/html-effectiveness/templates/18-editor-triage-board.html.tmpl +0 -541
  46. package/plugins/html-effectiveness/templates/19-editor-feature-flags.html.tmpl +0 -627
  47. package/plugins/html-effectiveness/templates/20-editor-prompt-tuner.html.tmpl +0 -687
  48. package/plugins/html-effectiveness/templates/manifest.json +0 -441
  49. package/plugins/team-share/.claude-plugin/plugin.json +0 -18
  50. package/plugins/team-share/README.md +0 -46
  51. package/plugins/team-share/agents/team-share.md +0 -53
  52. package/plugins/team-share/commands/team-share.md +0 -4
  53. package/plugins/team-share/skills/claude-config/SKILL.md +0 -143
  54. package/plugins/team-share/skills/codegraph-setup/SKILL.md +0 -81
  55. package/plugins/team-share/skills/understand-setup/SKILL.md +0 -115
  56. package/plugins/tech-graph/.claude-plugin/plugin.json +0 -17
  57. package/plugins/tech-graph/README.md +0 -66
  58. package/plugins/tech-graph/agents/tech-graph.md +0 -97
  59. package/plugins/tech-graph/commands/tech-graph.md +0 -13
  60. package/plugins/tech-graph/scripts/check-deps.sh +0 -55
  61. package/plugins/tech-graph/skills/tech-graph/LICENSE +0 -21
  62. package/plugins/tech-graph/skills/tech-graph/README.md +0 -555
  63. package/plugins/tech-graph/skills/tech-graph/README.zh.md +0 -514
  64. package/plugins/tech-graph/skills/tech-graph/SKILL.md +0 -263
  65. package/plugins/tech-graph/skills/tech-graph/agents/openai.yaml +0 -4
  66. package/plugins/tech-graph/skills/tech-graph/assets/samples/sample-style1-flat.png +0 -0
  67. package/plugins/tech-graph/skills/tech-graph/assets/samples/sample-style2-dark.png +0 -0
  68. package/plugins/tech-graph/skills/tech-graph/assets/samples/sample-style3-blueprint.png +0 -0
  69. package/plugins/tech-graph/skills/tech-graph/assets/samples/sample-style4-notion.png +0 -0
  70. package/plugins/tech-graph/skills/tech-graph/assets/samples/sample-style5-glass.png +0 -0
  71. package/plugins/tech-graph/skills/tech-graph/assets/samples/sample-style6-claude.png +0 -0
  72. package/plugins/tech-graph/skills/tech-graph/assets/samples/sample-style7-openai.png +0 -0
  73. package/plugins/tech-graph/skills/tech-graph/fixtures/agent-memory-types-style4.json +0 -181
  74. package/plugins/tech-graph/skills/tech-graph/fixtures/api-flow-style7.json +0 -40
  75. package/plugins/tech-graph/skills/tech-graph/fixtures/mem0-style1.json +0 -297
  76. package/plugins/tech-graph/skills/tech-graph/fixtures/microservices-style3.json +0 -64
  77. package/plugins/tech-graph/skills/tech-graph/fixtures/multi-agent-style5.json +0 -45
  78. package/plugins/tech-graph/skills/tech-graph/fixtures/system-architecture-style6.json +0 -48
  79. package/plugins/tech-graph/skills/tech-graph/fixtures/tool-call-style2.json +0 -182
  80. package/plugins/tech-graph/skills/tech-graph/package.json +0 -42
  81. package/plugins/tech-graph/skills/tech-graph/references/diagram-types.md +0 -171
  82. package/plugins/tech-graph/skills/tech-graph/references/icons.md +0 -406
  83. package/plugins/tech-graph/skills/tech-graph/references/style-1-flat-icon.md +0 -108
  84. package/plugins/tech-graph/skills/tech-graph/references/style-2-dark-terminal.md +0 -107
  85. package/plugins/tech-graph/skills/tech-graph/references/style-3-blueprint.md +0 -113
  86. package/plugins/tech-graph/skills/tech-graph/references/style-4-notion-clean.md +0 -95
  87. package/plugins/tech-graph/skills/tech-graph/references/style-5-glassmorphism.md +0 -125
  88. package/plugins/tech-graph/skills/tech-graph/references/style-6-claude-official.md +0 -209
  89. package/plugins/tech-graph/skills/tech-graph/references/style-7-openai.md +0 -215
  90. package/plugins/tech-graph/skills/tech-graph/references/style-diagram-matrix.md +0 -135
  91. package/plugins/tech-graph/skills/tech-graph/references/svg-layout-best-practices.md +0 -100
  92. package/plugins/tech-graph/skills/tech-graph/references/svg-to-png.md +0 -115
  93. package/plugins/tech-graph/skills/tech-graph/scripts/README.md +0 -269
  94. package/plugins/tech-graph/skills/tech-graph/scripts/generate-diagram.sh +0 -181
  95. package/plugins/tech-graph/skills/tech-graph/scripts/generate-from-template.py +0 -1587
  96. package/plugins/tech-graph/skills/tech-graph/scripts/test-all-styles.sh +0 -143
  97. package/plugins/tech-graph/skills/tech-graph/scripts/validate-svg.sh +0 -306
  98. package/plugins/tech-graph/skills/tech-graph/templates/agent-architecture.svg +0 -28
  99. package/plugins/tech-graph/skills/tech-graph/templates/architecture.svg +0 -23
  100. package/plugins/tech-graph/skills/tech-graph/templates/comparison-matrix.svg +0 -14
  101. package/plugins/tech-graph/skills/tech-graph/templates/data-flow.svg +0 -28
  102. package/plugins/tech-graph/skills/tech-graph/templates/er-diagram.svg +0 -21
  103. package/plugins/tech-graph/skills/tech-graph/templates/flowchart.svg +0 -21
  104. package/plugins/tech-graph/skills/tech-graph/templates/sequence.svg +0 -20
  105. package/plugins/tech-graph/skills/tech-graph/templates/state-machine.svg +0 -20
  106. package/plugins/tech-graph/skills/tech-graph/templates/timeline.svg +0 -19
  107. package/plugins/tech-graph/skills/tech-graph/templates/use-case.svg +0 -21
@@ -1,7 +1,8 @@
1
1
  import * as nodeFs from "node:fs";
2
- import { listSkills, listAgents, listAgentsIncludingArchived, listSquads, listRuntimes, listWorkspaceMembers, getSquadMembers, findByName, makeCli, realExec, requireAuth, resolveWorkspaceId, listProjects, getProjectResources, findByTitle, listAutopilots, getAutopilot } from "./lib.mjs";
2
+ import { listSkills, listAgents, listAgentsIncludingArchived, listSquads, listRuntimes, listWorkspaceMembers, getSquadMembers, findByName, makeCli, realExec, requireAuth, resolveWorkspaceId, listProjects, getProjectResources, findByTitle, listAutopilots, getAutopilot, listLabels, listProperties, listWorkspaceMcpServers } from "./lib.mjs";
3
3
 
4
- // User-facing selectable types are agents/squads/projects/autopilots; skills follow agents.
4
+ // User-facing selectable types are agents/squads/projects/autopilots/labels/properties;
5
+ // skills follow agents. Default stays agents,squads — everything else opts in.
5
6
  export function parseInclude(raw) {
6
7
  const set = new Set((raw ? raw.split(",") : ["agents", "squads"]).map((s) => s.trim()).filter(Boolean));
7
8
  if (set.has("agents")) set.add("skills");
@@ -19,6 +20,15 @@ function readSidecar(fs, dir, rec, fileKey, inlineKey) {
19
20
  return rec[inlineKey] ?? "";
20
21
  }
21
22
  const readInstructions = (fs, dir, rec) => readSidecar(fs, dir, rec, "instructions_file", "instructions");
23
+
24
+ // Workspace-wide sets (labels, properties, MCP roster) live in their own flat
25
+ // folder, pointed at by `<key>_file` in the manifest. Legacy bundles carried the
26
+ // array inline under `<key>` — fall back to that so older exports still import.
27
+ export function readTaxonomy(fs, dir, manifest, key) {
28
+ const rel = manifest[`${key}_file`];
29
+ if (rel && fs.existsSync(`${dir}/${rel}`)) return JSON.parse(fs.readFileSync(`${dir}/${rel}`, "utf8"));
30
+ return manifest[key] ?? [];
31
+ }
22
32
  const readDescription = (fs, dir, rec) => readSidecar(fs, dir, rec, "description_file", "description");
23
33
 
24
34
  // Relative paths of every file under root (recursing into subdirs like scripts/).
@@ -86,9 +96,14 @@ export function importAgents({ cli, manifest, dir, skillIdMap, runtimeMap, fs =
86
96
  const avatarUnsupported = []; // emoji avatar — no CLI setter for agents
87
97
  const permissionApplyFailures = []; // CLI rejected --public-to-member
88
98
  const permissionUnsupported = []; // no member target resolved in the destination
99
+ const mcpServersUnresolved = []; // bundled server name absent from the destination library
100
+ const mcpServersApplyFailures = []; // CLI rejected the enable/disable
89
101
  // Lazy + memoized: destination member user_ids, only listed when an agent needs them.
90
102
  let destMemberIds = null;
91
103
  const getDestMemberIds = () => destMemberIds ??= new Set(listWorkspaceMembers(cli).map((m) => m.user_id));
104
+ // Lazy + memoized: destination MCP library, name -> id.
105
+ let destMcp = null;
106
+ const destMcpServerIds = () => destMcp ??= new Map(listWorkspaceMcpServers(cli).map((m) => [m.name, m.id]));
92
107
  let created = 0, updated = 0, reused = 0;
93
108
  // Includes archived agents so a bundle name matching a retired agent restores
94
109
  // and reuses it instead of failing to create under the held name.
@@ -133,6 +148,21 @@ export function importAgents({ cli, manifest, dir, skillIdMap, runtimeMap, fs =
133
148
  const skillIds = (rec.skill_names ?? []).map((n) => skillIdMap.get(n)).filter(Boolean);
134
149
  cli.run(["agent", "skills", "set", id, "--skill-ids", skillIds.join(",")]);
135
150
 
151
+ // Workspace MCP server assignments, resolved by NAME against the destination
152
+ // library (ids are per-workspace). A server the destination doesn't have is
153
+ // reported, not created: `workspace mcp list` never returns the server entry
154
+ // JSON, so the bundle has no config to recreate it from.
155
+ for (const m of rec.mcp_servers ?? []) {
156
+ const serverId = destMcpServerIds().get(m.name);
157
+ if (!serverId) { mcpServersUnresolved.push(`${rec.name}:${m.name}`); continue; }
158
+ try { cli.run(["agent", "mcp", "add", id, serverId]); } catch { /* already assigned */ }
159
+ try {
160
+ cli.run(["agent", "mcp", m.enabled ? "enable" : "disable", id, serverId]);
161
+ } catch {
162
+ mcpServersApplyFailures.push(`${rec.name}:${m.name}`);
163
+ }
164
+ }
165
+
136
166
  // Avatar: only ever set it when the target agent has none — never clobber an
137
167
  // avatar an existing agent already has. Agents accept only image uploads
138
168
  // (`agent avatar --file`); an emoji-only avatar can't be restored via the CLI.
@@ -191,7 +221,7 @@ export function importAgents({ cli, manifest, dir, skillIdMap, runtimeMap, fs =
191
221
  }
192
222
  }
193
223
  }
194
- return { idMap, sourceIdMap, created, updated, reused, secretsApplyFailures, avatarApplyFailures, avatarUnsupported, permissionApplyFailures, permissionUnsupported };
224
+ return { idMap, sourceIdMap, created, updated, reused, secretsApplyFailures, avatarApplyFailures, avatarUnsupported, permissionApplyFailures, permissionUnsupported, mcpServersUnresolved, mcpServersApplyFailures };
195
225
  }
196
226
 
197
227
  // Rewrites `mention://agent/<id>` links (e.g. `[@dev-backend](mention://agent/<id>)`)
@@ -344,6 +374,9 @@ export function importAutopilots({ cli, manifest, dir, agentIdMap, fs = nodeFs }
344
374
  const idMap = new Map();
345
375
  let created = 0, updated = 0;
346
376
  const projectUnresolved = [], subscribersUnresolved = [], priorityNotCaptured = [], webhookReissued = [];
377
+ // Every import lands paused; these were live at the source and need a
378
+ // deliberate `autopilot update <id> --status active` at the destination.
379
+ const activeAtSource = recs.filter((r) => r.status === "active").map((r) => r.title);
347
380
  const existing = listAutopilots(cli);
348
381
  let destProjects = null;
349
382
  const resolveProjectId = (title) => (destProjects ??= listProjects(cli)).find((p) => p.title === title)?.id ?? null;
@@ -355,10 +388,11 @@ export function importAutopilots({ cli, manifest, dir, agentIdMap, fs = nodeFs }
355
388
  const description = readDescription(fs, dir, rec);
356
389
  if (description) common.push("--description", description);
357
390
  if (rec.issue_title_template) common.push("--issue-title-template", rec.issue_title_template);
358
- // priority is never present today — the multica CLI/API accepts it on write but
359
- // never returns it on read, so export can't capture the source's real value.
360
- if (rec.priority && rec.priority !== "none") common.push("--priority", rec.priority);
361
- else priorityNotCaptured.push(rec.title);
391
+ // Priority never round-trips: the API omits it on read, and as of multica
392
+ // 0.4.36 `autopilot create`/`update` reject `--priority` outright ("unknown
393
+ // flag"). Passing a legacy bundle's value through would abort the import, so
394
+ // it is always reported instead of applied.
395
+ priorityNotCaptured.push(rec.title);
362
396
 
363
397
  if (rec.project_title) {
364
398
  const projectId = resolveProjectId(rec.project_title);
@@ -403,7 +437,82 @@ export function importAutopilots({ cli, manifest, dir, agentIdMap, fs = nodeFs }
403
437
  if (t.kind === "webhook") webhookReissued.push(rec.title);
404
438
  }
405
439
  }
406
- return { idMap, created, updated, projectUnresolved, subscribersUnresolved, priorityNotCaptured, webhookReissued };
440
+ return { idMap, created, updated, projectUnresolved, subscribersUnresolved, priorityNotCaptured, webhookReissued, activeAtSource };
441
+ }
442
+
443
+ // Workspace issue labels, matched by name. Colour is the only field the CLI can
444
+ // set besides the name — a label's description has no `--description` flag on
445
+ // `label create`/`update` (multica 0.4.36), so it is reported, never applied.
446
+ export function importLabels({ cli, manifest, dir, fs = nodeFs }) {
447
+ const entries = readTaxonomy(fs, dir, manifest, "labels");
448
+ if (!entries.length) return { created: 0, updated: 0, descriptionUnsupported: [] };
449
+ const existing = listLabels(cli);
450
+ let created = 0, updated = 0;
451
+ const descriptionUnsupported = [];
452
+ for (const l of entries) {
453
+ const match = findByName(existing, l.name);
454
+ if (match) {
455
+ cli.run(["label", "update", match.id, "--color", l.color]);
456
+ updated++;
457
+ } else {
458
+ cli.run(["label", "create", "--name", l.name, "--color", l.color]);
459
+ created++;
460
+ }
461
+ // Only worth reporting when the destination doesn't already carry the same text.
462
+ if (l.description && match?.description !== l.description) descriptionUnsupported.push(l.name);
463
+ }
464
+ return { created, updated, descriptionUnsupported };
465
+ }
466
+
467
+ // Workspace custom issue property definitions, matched by name — the same key
468
+ // issue values reference, and the key `property update` uses to re-match existing
469
+ // options so their ids (and any values already set on issues) survive.
470
+ //
471
+ // A property's type is immutable: a name collision with a different type is
472
+ // skipped and reported rather than forced, since re-creating it would orphan
473
+ // every value already stored under that name at the destination.
474
+ export function importProperties({ cli, manifest, dir, fs = nodeFs }) {
475
+ const entries = readTaxonomy(fs, dir, manifest, "properties");
476
+ if (!entries.length) return { created: 0, updated: 0, typeConflicts: [], archivedApplied: [] };
477
+ const existing = listProperties(cli);
478
+ let created = 0, updated = 0;
479
+ const typeConflicts = [], archivedApplied = [];
480
+ for (const p of entries) {
481
+ const match = findByName(existing, p.name);
482
+ if (match && match.type !== p.type) {
483
+ typeConflicts.push(`${p.name} (destination is "${match.type}", bundle is "${p.type}" — type is immutable)`);
484
+ continue;
485
+ }
486
+ // Unarchive first so the update below always lands on an active definition;
487
+ // the desired archived state is re-applied at the end of the loop body.
488
+ if (match?.archived) cli.run(["property", "unarchive", p.name]);
489
+
490
+ const flags = [];
491
+ if (p.description) flags.push("--description", p.description);
492
+ if (p.icon) flags.push("--icon", p.icon);
493
+ // `--option` is select/multi_select only; every other type exports none.
494
+ for (const o of p.options ?? []) flags.push("--option", o.color ? `${o.name}:${o.color}` : o.name);
495
+
496
+ if (match) {
497
+ cli.run(["property", "update", p.name, ...flags]);
498
+ updated++;
499
+ } else {
500
+ cli.run(["property", "create", "--name", p.name, "--type", p.type, ...flags]);
501
+ created++;
502
+ }
503
+ if (p.archived) { cli.run(["property", "archive", p.name]); archivedApplied.push(p.name); }
504
+ }
505
+ return { created, updated, typeConflicts, archivedApplied };
506
+ }
507
+
508
+ // Workspace MCP servers named in the bundle that the destination library lacks.
509
+ // Only name + transport ever travel (see listWorkspaceMcpServers), so these are
510
+ // reported for manual re-entry — never created.
511
+ export function mcpServerGap({ cli, manifest, dir, fs = nodeFs }) {
512
+ const wanted = readTaxonomy(fs, dir, manifest, "mcp_servers");
513
+ if (!wanted.length) return [];
514
+ const have = new Set(listWorkspaceMcpServers(cli).map((m) => m.name));
515
+ return wanted.filter((m) => !have.has(m.name)).map((m) => m.name);
407
516
  }
408
517
 
409
518
  export function collectSourceRuntimes(manifest) {
@@ -463,7 +572,7 @@ export function importBundle({ cli, dir, runtimeMap, include, fs = nodeFs }) {
463
572
  : { idMap: new Map(), created: 0, updated: 0 };
464
573
  const agentRes = inc.has("agents")
465
574
  ? importAgents({ cli, manifest, dir, skillIdMap: skillRes.idMap, runtimeMap: effective, fs })
466
- : { idMap: new Map(), sourceIdMap: new Map(), created: 0, updated: 0, reused: 0, secretsApplyFailures: [], avatarApplyFailures: [], avatarUnsupported: [], permissionApplyFailures: [], permissionUnsupported: [] };
575
+ : { idMap: new Map(), sourceIdMap: new Map(), created: 0, updated: 0, reused: 0, secretsApplyFailures: [], avatarApplyFailures: [], avatarUnsupported: [], permissionApplyFailures: [], permissionUnsupported: [], mcpServersUnresolved: [], mcpServersApplyFailures: [] };
467
576
  // Runs after every agent exists so forward-referencing mentions resolve.
468
577
  const mentionRes = inc.has("agents")
469
578
  ? rewriteAgentMentions({ cli, manifest, dir, agentIdMap: agentRes.idMap, sourceIdMap: agentRes.sourceIdMap, fs })
@@ -489,15 +598,22 @@ export function importBundle({ cli, dir, runtimeMap, include, fs = nodeFs }) {
489
598
  projectRes = importProjects({ cli, manifest, dir, agentIdMap: agentRes.idMap, fs });
490
599
  }
491
600
 
492
- let autopilotRes = { idMap: new Map(), created: 0, updated: 0, projectUnresolved: [], subscribersUnresolved: [], priorityNotCaptured: [], webhookReissued: [] };
601
+ let autopilotRes = { idMap: new Map(), created: 0, updated: 0, projectUnresolved: [], subscribersUnresolved: [], priorityNotCaptured: [], webhookReissued: [], activeAtSource: [] };
493
602
  if (inc.has("autopilots")) {
494
603
  autopilotRes = importAutopilots({ cli, manifest, dir, agentIdMap: agentRes.idMap, fs });
495
604
  }
496
605
 
606
+ const labelRes = inc.has("labels")
607
+ ? importLabels({ cli, manifest, dir, fs })
608
+ : { created: 0, updated: 0, descriptionUnsupported: [] };
609
+ const propertyRes = inc.has("properties")
610
+ ? importProperties({ cli, manifest, dir, fs })
611
+ : { created: 0, updated: 0, typeConflicts: [], archivedApplied: [] };
612
+
497
613
  return {
498
614
  include: [...inc],
499
- created: { skills: skillRes.created, agents: agentRes.created, squads: squadsCreated, projects: projectRes.created, autopilots: autopilotRes.created },
500
- updated: { skills: skillRes.updated, agents: agentRes.updated, squads: squadsUpdated, projects: projectRes.updated, autopilots: autopilotRes.updated },
615
+ created: { skills: skillRes.created, agents: agentRes.created, squads: squadsCreated, projects: projectRes.created, autopilots: autopilotRes.created, labels: labelRes.created, properties: propertyRes.created },
616
+ updated: { skills: skillRes.updated, agents: agentRes.updated, squads: squadsUpdated, projects: projectRes.updated, autopilots: autopilotRes.updated, labels: labelRes.updated, properties: propertyRes.updated },
501
617
  // Agents restored from archived + updated to the bundle's definition — distinct
502
618
  // from a plain update (an active-name match). Only agents can be reused today.
503
619
  reused: { agents: agentRes.reused },
@@ -513,6 +629,13 @@ export function importBundle({ cli, dir, runtimeMap, include, fs = nodeFs }) {
513
629
  avatarUnsupported: agentRes.avatarUnsupported,
514
630
  permissionApplyFailures: agentRes.permissionApplyFailures,
515
631
  permissionUnsupported: agentRes.permissionUnsupported,
632
+ mcpServersUnresolved: agentRes.mcpServersUnresolved,
633
+ mcpServersApplyFailures: agentRes.mcpServersApplyFailures,
634
+ // Bundled workspace MCP servers missing at the destination. The CLI never
635
+ // exposes a server's entry JSON on read, so each must be re-added by hand
636
+ // (`multica workspace mcp add <name> --server-config ...`) before its agent
637
+ // assignments can land.
638
+ mcpServersNotCreatable: mcpServerGap({ cli, manifest, dir, fs }),
516
639
  squadsSkipped,
517
640
  priorityUnsupported: projectRes.priorityUnsupported,
518
641
  resourcesUnsupported: projectRes.resourcesUnsupported,
@@ -521,6 +644,10 @@ export function importBundle({ cli, dir, runtimeMap, include, fs = nodeFs }) {
521
644
  autopilotSubscribersUnresolved: autopilotRes.subscribersUnresolved,
522
645
  autopilotPriorityNotCaptured: autopilotRes.priorityNotCaptured,
523
646
  autopilotWebhookReissued: autopilotRes.webhookReissued,
647
+ autopilotsActiveAtSource: autopilotRes.activeAtSource,
648
+ labelDescriptionUnsupported: labelRes.descriptionUnsupported,
649
+ propertyTypeConflicts: propertyRes.typeConflicts,
650
+ propertiesArchived: propertyRes.archivedApplied,
524
651
  };
525
652
  }
526
653
 
@@ -528,13 +655,20 @@ export function preflight({ cli, dir, runtimeMap, include, fs = nodeFs }) {
528
655
  const inc = include ?? new Set(["skills", "agents", "squads"]);
529
656
  const manifest = JSON.parse(fs.readFileSync(`${dir}/manifest.json`, "utf8"));
530
657
  const count = (k) => (manifest[k] ?? []).length;
531
- const bundle = { skills: count("skills"), agents: count("agents"), squads: count("squads"), projects: count("projects"), autopilots: count("autopilots") };
658
+ // labels/properties/mcp live in their own folder; read through the manifest
659
+ // pointer (with the legacy inline fallback) rather than counting a key that
660
+ // current bundles no longer carry.
661
+ const bundleLabels = readTaxonomy(fs, dir, manifest, "labels");
662
+ const bundleProperties = readTaxonomy(fs, dir, manifest, "properties");
663
+ const bundle = { skills: count("skills"), agents: count("agents"), squads: count("squads"), projects: count("projects"), autopilots: count("autopilots"), labels: bundleLabels.length, properties: bundleProperties.length };
532
664
  const willImport = {
533
665
  skills: inc.has("skills") ? bundle.skills : 0,
534
666
  agents: inc.has("agents") ? bundle.agents : 0,
535
667
  squads: inc.has("squads") ? bundle.squads : 0,
536
668
  projects: inc.has("projects") ? bundle.projects : 0,
537
669
  autopilots: inc.has("autopilots") ? bundle.autopilots : 0,
670
+ labels: inc.has("labels") ? bundle.labels : 0,
671
+ properties: inc.has("properties") ? bundle.properties : 0,
538
672
  };
539
673
 
540
674
  const incompatibilities = [];
@@ -596,15 +730,56 @@ export function preflight({ cli, dir, runtimeMap, include, fs = nodeFs }) {
596
730
  } else if (!agentAvailable(rec.assignee_name)) {
597
731
  incompatibilities.push({ type: "autopilot-assignee-missing", detail: `${rec.title} → agent "${rec.assignee_name}" (ensure this agent exists in the destination or is included in this import)` });
598
732
  }
599
- if (!rec.priority) {
600
- incompatibilities.push({ type: "autopilot-priority-not-captured", detail: `${rec.title} (the multica CLI/API never returns an autopilot's priority, so export could not capture it)` });
601
- }
733
+ incompatibilities.push({ type: "autopilot-priority-not-captured", detail: `${rec.title} (the multica CLI/API never returns an autopilot's priority on read, and multica 0.4.36 dropped --priority from autopilot create/update — it can be neither captured nor restored)` });
602
734
  if (rec.project_title && !projectAvailable(rec.project_title)) {
603
735
  incompatibilities.push({ type: "autopilot-project-missing", detail: `${rec.title} → project "${rec.project_title}" (not found in the destination)` });
604
736
  }
605
737
  if (rec.had_webhook_trigger) {
606
738
  incompatibilities.push({ type: "autopilot-webhook-reissued", detail: `${rec.title} (webhook trigger will get a newly issued URL — the old one never travels)` });
607
739
  }
740
+ if (rec.status === "active") {
741
+ incompatibilities.push({ type: "autopilot-arrives-paused", detail: `${rec.title} (active at the source; every imported autopilot lands paused — activate it deliberately at the destination)` });
742
+ }
743
+ }
744
+ }
745
+
746
+ // MCP: the workspace library can never be recreated from a bundle (no CLI read
747
+ // of a server's entry JSON), so a missing server blocks its agent assignments.
748
+ if (inc.has("agents")) {
749
+ for (const name of mcpServerGap({ cli, manifest, dir, fs })) {
750
+ incompatibilities.push({ type: "mcp-server-not-creatable", detail: `${name} (in the bundle's workspace MCP roster but absent at the destination; \`workspace mcp list\` never returns a server's config, so re-add it with \`multica workspace mcp add ${name} --server-config ...\`)` });
751
+ }
752
+ let destMcpNames = null;
753
+ const mcpAvailable = (n) => (destMcpNames ??= new Set(listWorkspaceMcpServers(cli).map((m) => m.name))).has(n);
754
+ for (const a of manifest.agents ?? []) {
755
+ const rec = JSON.parse(fs.readFileSync(`${dir}/${a.file}`, "utf8"));
756
+ for (const m of rec.mcp_servers ?? []) {
757
+ if (!mcpAvailable(m.name)) {
758
+ incompatibilities.push({ type: "agent-mcp-server-missing", detail: `${rec.name} → MCP server "${m.name}" (not in the destination workspace library — the assignment will be skipped)` });
759
+ }
760
+ }
761
+ }
762
+ }
763
+
764
+ if (inc.has("labels")) {
765
+ for (const l of bundleLabels) {
766
+ if (l.description) {
767
+ incompatibilities.push({ type: "label-description-not-settable", detail: `${l.name} (the multica CLI exposes no --description flag on label create/update)` });
768
+ }
769
+ }
770
+ }
771
+
772
+ if (inc.has("properties")) {
773
+ // Read-only mirror of importProperties' immutable-type skip.
774
+ const existingProps = bundleProperties.length ? listProperties(cli) : [];
775
+ for (const prop of bundleProperties) {
776
+ const match = findByName(existingProps, prop.name);
777
+ if (match && match.type !== prop.type) {
778
+ incompatibilities.push({ type: "property-type-conflict", detail: `${prop.name} (destination is "${match.type}", bundle is "${prop.type}" — type is immutable, this property will be skipped)` });
779
+ }
780
+ if (prop.archived) {
781
+ incompatibilities.push({ type: "property-archived", detail: `${prop.name} (archived at the source — imported, then archived at the destination too)` });
782
+ }
608
783
  }
609
784
  }
610
785
 
@@ -635,7 +810,7 @@ function main() {
635
810
  const dryRun = args.includes("--dry-run");
636
811
 
637
812
  if (!dir || !workspace) {
638
- console.error("Usage: multica-import.mjs --dir <folder> --workspace <name> [--runtime-map <src=dst,...>] [--include <csv>] [--dry-run]");
813
+ console.error("Usage: multica-import.mjs --dir <folder> --workspace <name> [--runtime-map <src=dst,...>] [--include agents,squads,projects,autopilots,labels,properties] [--dry-run]");
639
814
  process.exit(1);
640
815
  }
641
816
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: export
3
- description: Use when the user wants to export Multica skills, agents, or squads to a local folder for backup, version control, or cross-workspace migration.
3
+ description: Use when the user wants to export Multica skills, agents, squads, projects, or autopilots to a local folder for backup, version control, or cross-workspace migration. Supports whole-workspace exports by level (skill, agent, squad, project) or a single named resource.
4
4
  allowed-tools: Bash, Read
5
5
  ---
6
6
 
@@ -18,11 +18,31 @@ multica auth status 2>&1 || true
18
18
 
19
19
  If `multica login` is required, surface that message verbatim and stop.
20
20
 
21
- ## Step 2 — Determine scope and resource ID
21
+ ## Step 2 — Pick a level, or a single resource
22
22
 
23
- If the user named a specific resource and type (`skill`, `agent`, `squad`, `project`, or `autopilot`), use those directly.
23
+ There are two ways to export, and they are mutually exclusive:
24
24
 
25
- Otherwise, list available resources for the chosen type and present a pick list:
25
+ - **Whole workspace, by level** (`--level`) the default shape. A level bundles its own tier plus every tier below it. Use this whenever the user asks for a workspace, a backup, or a migration.
26
+ - **One named resource** (`--scope <type> --id <id>`) — for a single skill, agent, squad, project, or autopilot.
27
+
28
+ ### Levels
29
+
30
+ Lowest tier first; **the default level is `squad`**.
31
+
32
+ | `--level` | Object folders written |
33
+ |---|---|
34
+ | `skill` | `skills/` |
35
+ | `agent` | `skills/`, `agents/` |
36
+ | `squad` *(default)* | `skills/`, `agents/`, `squads/` |
37
+ | `project` | `skills/`, `agents/`, `squads/`, `projects/`, `autopilots/`, `labels/`, `properties/`, `mcp/` |
38
+
39
+ Notes on the tiers:
40
+
41
+ - **Project** carries portable project metadata (title, description, icon, priority, status, dates, lead mapping) plus its attached resource records — **never issues, never members**. Workspace issue **labels**, custom **properties**, and the workspace **MCP server roster** ride along at this level.
42
+ - **Autopilots** are bundled **only** at `--level project`. Every other level omits them.
43
+ - `--level skill` keeps every workspace skill, including ones no agent references. Every higher level prunes those orphans, since the agents are what reference them.
44
+
45
+ If the user asked for a single resource but did not name it, list the type and present a pick list:
26
46
 
27
47
  ```bash
28
48
  multica <type> list --output json
@@ -41,28 +61,73 @@ First resolve `<workspace-name>`:
41
61
 
42
62
  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).
43
63
 
44
- Then construct the default directory by scope:
64
+ Then construct the default directory:
45
65
 
46
- - `all` or `projects` (whole workspace) → `export/<workspace-name>`
47
- - a single resource (`skill`, `agent`, `squad`, `project`, or `autopilot`) → `export/<workspace-name>/<slug>-<type>`, where `<slug>` is the slugified resource name and `<type>` is the resource type.
66
+ - a whole-workspace export (`--level`) → `export/<workspace-name>`
67
+ - a single resource (`--scope <type> --id`) → `export/<workspace-name>/<slug>-<type>`, where `<slug>` is the slugified resource name and `<type>` is the resource type.
48
68
 
49
- Examples: `export all from mx-workspace` → `export/mx-workspace`; `export skill "Foo Bar" from mx-workspace` → `export/mx-workspace/foo-bar-skill`.
69
+ Examples: `export everything from mx-workspace` → `export/mx-workspace`; `export skill "Foo Bar" from mx-workspace` → `export/mx-workspace/foo-bar-skill`.
50
70
 
51
71
  ## Step 4 — Run the export
52
72
 
73
+ Whole workspace, by level (the default path — `--level` defaults to `squad`):
74
+
75
+ ```bash
76
+ node "${CLAUDE_PLUGIN_ROOT}/scripts/multica-export.mjs" \
77
+ --out <dir> \
78
+ [--level skill|agent|squad|project] \
79
+ [--workspace <workspace-name>]
80
+ ```
81
+
82
+ One named resource:
83
+
53
84
  ```bash
54
85
  node "${CLAUDE_PLUGIN_ROOT}/scripts/multica-export.mjs" \
55
- --scope <skill|agent|squad|project|projects|autopilot|all> \
86
+ --scope <skill|agent|squad|project|autopilot> \
56
87
  --id <id> \
57
88
  --out <dir> \
58
89
  [--workspace <workspace-name>]
59
90
  ```
60
91
 
61
- `--id` is required for `skill`, `agent`, `squad`, `project`, and `autopilot` (a single named resource); it is **not needed** for `projects` (every project in the workspace) or `all` (the entire workspace). `all` does **not** include autopilots — an autopilot is always exported explicitly, by name, never auto-bundled.
92
+ `--scope` and `--level` are mutually exclusive; the script exits 1 if both are passed. `--id` is required with `--scope`.
93
+
94
+ A workspace export is one flat, deduped bundle: a skill, agent, or squad reached from several places is written exactly once and referenced by name.
95
+
96
+ Every export that reaches a resource with a dependency **bundles that dependency** so the bundle is self-contained — a squad's leader and members, a project's lead agent, an autopilot's assignee agent or squad, and every skill those agents use. On import, only `github_repo` project resources are portable and recreated; other resource types are reported and skipped.
97
+
98
+ ### Folder layout
99
+
100
+ One flat folder per object type:
101
+
102
+ ```
103
+ <dir>/
104
+ manifest.json
105
+ skills/<slug>/SKILL.md, config.json, <skill files…>
106
+ agents/<slug>.json, <slug>.md, <slug>.description.md, <slug>.avatar.<ext>
107
+ squads/<slug>.json, <slug>.md, <slug>.description.md
108
+ projects/<slug>.json, <slug>.description.md
109
+ autopilots/<slug>.json, <slug>.description.md
110
+ labels/labels.json
111
+ properties/properties.json
112
+ mcp/servers.json
113
+ ```
114
+
115
+ `manifest.json` points at `labels/`, `properties/` and `mcp/` via `labels_file`, `properties_file` and `mcp_servers_file` (with a matching `*_count`); the arrays themselves are no longer inline. Import still reads a pre-folder bundle that carried them inline.
116
+
117
+ ### Issue labels, custom properties, and the MCP roster
118
+
119
+ `--scope project` and `--level project` also bundle the workspace's **issue labels**, **custom issue property definitions**, and the **workspace MCP server roster**, so a migrated project lands somewhere its issues can actually be labelled and filled in. All three are **workspace-scoped in Multica — there is no project-scoped label, property, or MCP server** — so what travels is the whole workspace's taxonomy, not a per-project subset. State that plainly to the user when they ask for "the project's labels".
120
+
121
+ Narrower selections (`skill`, `agent`, `squad`, `autopilot` scopes; `skill`/`agent`/`squad` levels) bundle none of the three — a single resource is not a workspace migration.
122
+
123
+ What travels, and what cannot:
62
124
 
63
- Pass `--scope all` (with no `--id`) to export the **entire workspace** every skill, agent, squad, and project into one flat, deduped bundle. A skill or agent shared across many agents/squads is written exactly once and referenced by name.
125
+ - **Labels** `name` and `color`. A label's `description` is captured in the bundle for review but **cannot be restored**: `label create`/`label update` expose no `--description` flag. Every affected label is listed in `labelDescriptionsNotPortable`.
126
+ - **Properties** — `name`, `type`, `description`, `icon`, `archived`, and each select option's `name` + `color`. Server-assigned `position` and `usage_count` are not bundled (no CLI setter). Option **ids** are deliberately dropped: `property update` re-matches options by name at the destination, which is also how issue values reference them.
127
+ - Archived property definitions **are** included (`property list --include-archived`), so a retired definition arrives retired instead of reappearing in every picker.
128
+ - **MCP servers** — `name` and `transport` only. A server's entry JSON (command/args/env, and any token inside it) is **write-only in the CLI**: `workspace mcp add`/`update` accept it, and nothing reads it back. So `mcp/servers.json` is a roster, not a copy — every entry is listed in `mcpServerConfigsNotPortable`, and each must be re-added by hand at the destination before agent assignments can attach to it.
64
129
 
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.
130
+ Every agent record also carries `mcp_servers` which workspace MCP servers that agent uses, as `{name, enabled}`. This is separate from the agent's own inline `mcp_config`. Import re-links them by name against the destination library.
66
131
 
67
132
  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
133
 
@@ -73,14 +138,19 @@ An empty field gets no file and no `*_file` key. Skills keep their own layout
73
138
 
74
139
  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.
75
140
 
76
- Exporting `--scope autopilot` bundles the autopilot's **assignee** so the bundle is self-contained an agent assignee (and its skills), or a squad assignee (its members, their skills, and the squad itself). It also captures the schedule and webhook triggers, the target project's **title** (not its ID — resolved by name on import) when one is set, and human subscribers' **names** (not their IDs). A webhook trigger's secret (URL/token) is **never** written to the bundle — only that it exists (its label); a fresh secret is issued on import.
141
+ Exporting `--scope autopilot` bundles the autopilot configuration **and its assignee** (an agent, or a squad with its members and their skills), so the bundle imports on its own. The assignee is linked by name (`assignee_type` + `assignee_name`); import resolves that name against the bundle first, then the destination workspace. An autopilot's `status` is captured, but **every imported autopilot lands paused** regardless — the ones that were live at the source are listed in `autopilotsActiveAtSource` so they can be re-activated deliberately. The bundle also captures the schedule and webhook triggers, the target project's **title** (not its ID — resolved by name on import) when one is set, and human subscribers' **names** (not their IDs). A webhook trigger's secret (URL/token) is **never** written to the bundle — only that it exists (its label); a fresh secret is issued on import.
77
142
 
78
143
  ## Step 5 — Report results
79
144
 
80
145
  Parse the JSON output from the script and report:
81
146
 
82
147
  - Directory written to.
83
- - Count of skills, agents, squads, and projects exported.
148
+ - The level (or the single resource) exported.
149
+ - Count of skills, agents, squads, projects, and autopilots exported.
84
150
  - If `pruned_skills` is non-empty, note it: "Pruned N orphan skill(s) not linked to any agent: `<name>`, …" (these are standalone workspace skills that no exported agent references — only `--scope all` produces them).
85
151
  - If `warnings` is non-empty, surface every agent name verbatim with this message: "WARNING: the following agents' exported files contain custom environment variables or MCP config in PLAINTEXT — treat the export directory as sensitive (avoid committing it to a public repo, restrict file permissions, delete it once the import is done): `<agent-name>`."
86
152
  - If `autopilotWebhookTriggers` is non-empty, surface every autopilot title verbatim with: "NOTE: the following autopilots have a webhook trigger — its secret was NOT exported; a newly issued URL will be created on import: `<autopilot-title>`."
153
+ - Count of labels and custom properties bundled (`manifest.labels.length`, `manifest.properties.length`), noting that both are workspace-wide, not project-specific.
154
+ - If `autopilotsActiveAtSource` is non-empty, surface every title verbatim with: "NOTE: the following autopilots were ACTIVE at the source; every imported autopilot lands paused — re-activate each one deliberately with `multica autopilot update <id> --status active`: `<autopilot-title>`."
155
+ - If `mcpServerConfigsNotPortable` is non-empty, surface every server name verbatim with: "NOTE: the following workspace MCP servers travelled by name and transport only — the CLI never returns a server's config on read, so re-add each one at the destination (`multica workspace mcp add <name> --server-config ...`) before importing the agents that use it: `<server-name>`."
156
+ - If `labelDescriptionsNotPortable` is non-empty, surface every label name verbatim with: "NOTE: the following labels have a description that the multica CLI cannot set on import (`label create`/`update` have no `--description` flag) — re-enter it in the Multica UI at the destination: `<label-name>`."
@@ -19,13 +19,13 @@ No existence check is needed here: the Step 2 dry-run fails with `Unknown worksp
19
19
 
20
20
  ## Step 2 — Pre-flight (dry run)
21
21
 
22
- Before writing anything, run the import script with `--dry-run` to preview the bundle. Preview against the **full** set (`--include agents,squads,projects,autopilots`) regardless of what the user ultimately chooses to import — incompatibilities for a type (e.g. project caveats) are only computed when that type is included, so previewing everything up front is what lets the user see a project's or autopilot's cost before deciding whether to opt it in:
22
+ Before writing anything, run the import script with `--dry-run` to preview the bundle. Preview against the **full** set (`--include agents,squads,projects,autopilots,labels,properties`) regardless of what the user ultimately chooses to import — incompatibilities for a type (e.g. project caveats) are only computed when that type is included, so previewing everything up front is what lets the user see a project's or autopilot's cost before deciding whether to opt it in:
23
23
 
24
24
  ```bash
25
25
  node "${CLAUDE_PLUGIN_ROOT}/scripts/multica-import.mjs" \
26
26
  --dir <folder> \
27
27
  --workspace <workspace-name> \
28
- --include agents,squads,projects,autopilots \
28
+ --include agents,squads,projects,autopilots,labels,properties \
29
29
  [--runtime-map <srcId1=dstId1,srcId2=dstId2,...>] \
30
30
  --dry-run
31
31
  ```
@@ -34,9 +34,11 @@ Present the `bundle` and `willImport` counts, and every entry in `incompatibilit
34
34
 
35
35
  ## Step 3 — Select which types to import
36
36
 
37
- Ask the user which of `agents`, `squads`, `projects`, `autopilots` they want to import. **Default is `agents,squads`** — `projects` and `autopilots` each require explicit opt-in.
37
+ Ask the user which of `agents`, `squads`, `projects`, `autopilots`, `labels`, `properties` they want to import. **Default is `agents,squads`** — `projects`, `autopilots`, `labels`, and `properties` each require explicit opt-in.
38
38
 
39
- If the pre-flight's `incompatibilities` list contains an `unmapped-runtime` or `autopilot-squad-assignee-unsupported` entry, tell the user it **aborts the import before any write** (see Step 4 below) — `unmapped-runtime` needs `--runtime-map`; a squad-assigned autopilot has no fix, since the multica CLI has no command to assign a squad to an autopilot (only `--agent`) drop that autopilot from the bundle or recreate its assignment by hand after importing the squad. An `autopilot-assignee-missing` entry also aborts unless resolved by including `agents` or ensuring that agent already exists in the destination. Other incompatibility kinds are informational only and applied best-effort, fixed up afterward in the Multica UI: `priority-not-settable` (project priority isn't settable via the CLI, so it never round-trips), `resource-not-portable` (only `github_repo` resources are portable — other resource kinds are dropped), `lead-agent-missing` (a non-agent lead isn't re-applied to the imported project), `autopilot-priority-not-captured` (the multica CLI/API never returns an autopilot's priority, so it can't be captured or restored at all — a known platform gap, not a bug in this tool), `autopilot-project-missing` (the autopilot's target project isn't found by title in the destination — it's created/updated with no project set), and `autopilot-webhook-reissued` (informational — the webhook trigger always gets a freshly issued URL).
39
+ `labels` and `properties` write **workspace-wide** definitions (Multica has no project-scoped label or property), so importing them changes the target workspace's issue taxonomy for every project in it, not just the one being migrated. Say so before the user opts in.
40
+
41
+ If the pre-flight's `incompatibilities` list contains an `unmapped-runtime` or `autopilot-squad-assignee-unsupported` entry, tell the user it **aborts the import before any write** (see Step 4 below) — `unmapped-runtime` needs `--runtime-map`; a squad-assigned autopilot has no fix, since the multica CLI has no command to assign a squad to an autopilot (only `--agent`) — drop that autopilot from the bundle or recreate its assignment by hand after importing the squad. An `autopilot-assignee-missing` entry also aborts unless resolved by including `agents` or ensuring that agent already exists in the destination. Other incompatibility kinds are informational only and applied best-effort, fixed up afterward in the Multica UI: `priority-not-settable` (project priority isn't settable via the CLI, so it never round-trips), `resource-not-portable` (only `github_repo` resources are portable — other resource kinds are dropped), `lead-agent-missing` (a non-agent lead isn't re-applied to the imported project), `autopilot-priority-not-captured` (the multica CLI/API never returns an autopilot's priority, so it can't be captured or restored at all — a known platform gap, not a bug in this tool), `autopilot-project-missing` (the autopilot's target project isn't found by title in the destination — it's created/updated with no project set), `autopilot-webhook-reissued` (informational — the webhook trigger always gets a freshly issued URL), `label-description-not-settable` (the CLI has no `--description` flag on `label create`/`update`, so a label's description never round-trips — re-enter it in the UI), `property-type-conflict` (a property of that name already exists at the destination with a **different** type; type is immutable, so that property is **skipped** entirely rather than re-created, which would orphan every value already stored under that name), `property-archived` (informational — an archived source definition is imported and then archived at the destination too), `autopilot-arrives-paused` (informational — the autopilot was active at the source; every import lands paused), `mcp-server-not-creatable` (a workspace MCP server named in the bundle is absent at the destination and **cannot** be created from the bundle — `workspace mcp list` never returns a server's config, so re-add it with `multica workspace mcp add <name> --server-config ...`), and `agent-mcp-server-missing` (an agent's MCP server assignment is dropped because that server isn't in the destination library).
40
42
 
41
43
  ## Step 4 — Run the import (auto-mapping first)
42
44
 
@@ -46,7 +48,7 @@ Each exported agent record carries its source runtime's `provider` (e.g. `claude
46
48
  node "${CLAUDE_PLUGIN_ROOT}/scripts/multica-import.mjs" \
47
49
  --dir <folder> \
48
50
  --workspace <workspace-name> \
49
- --include <agents,squads,projects,autopilots>
51
+ --include <agents,squads,projects,autopilots,labels,properties>
50
52
  ```
51
53
 
52
54
  If it aborts with `Unmapped runtimes: ...` (0 or 2+ runtimes share that provider in the target workspace, or the bundle predates provider capture), resolve manually:
@@ -62,7 +64,7 @@ Ask the user to pick a matching target runtime by name or ID for each unmapped `
62
64
  node "${CLAUDE_PLUGIN_ROOT}/scripts/multica-import.mjs" \
63
65
  --dir <folder> \
64
66
  --workspace <workspace-name> \
65
- --include <agents,squads,projects,autopilots> \
67
+ --include <agents,squads,projects,autopilots,labels,properties> \
66
68
  --runtime-map <srcId1=dstId1,srcId2=dstId2,...>
67
69
  ```
68
70
 
@@ -70,17 +72,23 @@ If it instead aborts with `Unresolved autopilot assignees: ...`, that is not fix
70
72
 
71
73
  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
74
 
75
+ Workspace-wide sets are read from their own folder: `labels/labels.json`, `properties/properties.json`, and `mcp/servers.json`, located via the `labels_file` / `properties_file` / `mcp_servers_file` pointers in `manifest.json`. A pre-folder bundle that carried the arrays inline in the manifest still imports unchanged.
76
+
77
+ Agent **MCP server assignments** (`mcp_servers` on each agent record) are re-linked by **name** against the destination's workspace MCP library, then each is enabled or disabled to match the source. The library itself is **never created** — only a server's name and transport ever travel, so a missing server means its assignments are skipped and reported. Add the missing servers first (`multica workspace mcp add`), then re-run the import to attach them.
78
+
73
79
  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
80
 
75
81
  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
82
 
83
+ Importing `labels` and `properties` matches by **name** and is idempotent: an existing label is updated to the bundle's colour, an existing property is updated in place (`property update` re-matches its options by name so option ids — and any issue values already set — survive). An archived definition is unarchived before the update and re-archived afterwards, so its archived state round-trips without ever writing to an archived record. A property whose name collides with a **different type** at the destination is skipped and reported, never re-created.
84
+
77
85
  Importing `autopilots` always creates a brand-new autopilot **paused**, regardless of the source's status — activation is left as a deliberate follow-up action, never automatic. Re-importing an already-imported autopilot updates it by title (never duplicates) but leaves its current status alone. Its triggers are upserted by kind+label — a re-import never adds a duplicate trigger — and a webhook trigger always gets a freshly issued URL, never the source's.
78
86
 
79
87
  ## Step 5 — Report results
80
88
 
81
89
  Parse the JSON output and report:
82
90
 
83
- - Created and updated counts for skills, agents, squads, projects, and autopilots (`created.autopilots`/`updated.autopilots`).
91
+ - Created and updated counts for skills, agents, squads, projects, autopilots, labels, and properties (`created.labels`, `created.properties`, and the matching `updated.*`).
84
92
  - Name-to-ID maps for skills and agents (`skillIdMap`, `agentIdMap`).
85
93
  - `squadIdMap`: name-to-ID map for every squad imported.
86
94
  - `autopilotIdMap`: name-to-ID map for every autopilot imported.
@@ -96,3 +104,10 @@ Parse the JSON output and report:
96
104
  - If `autopilotSubscribersUnresolved` is non-empty, surface every `<title>:<member-name>` entry verbatim with: "NOTE: the following autopilot subscribers could not be matched by name in the target workspace — skipped: `<entry>`."
97
105
  - If `autopilotPriorityNotCaptured` is non-empty, surface every autopilot title verbatim with: "NOTE: the following autopilots' priority could not be captured from the source (the multica CLI/API never returns it) — a known platform gap, not something this tool can fix: `<autopilot-title>`."
98
106
  - If `autopilotWebhookReissued` is non-empty, surface every autopilot title verbatim with: "NOTE: the following autopilots had a webhook trigger created with a freshly issued URL — the previous URL does not carry over: `<autopilot-title>`."
107
+ - If `autopilotsActiveAtSource` is non-empty, surface every autopilot title verbatim with: "NOTE: the following autopilots were ACTIVE at the source but landed PAUSED here — activate each deliberately with `multica autopilot update <id> --status active`: `<autopilot-title>`."
108
+ - If `mcpServersNotCreatable` is non-empty, surface every server name verbatim with: "WARNING: the following workspace MCP servers are in the bundle but missing at the destination, and CANNOT be created from it (the CLI never returns a server's config on read) — add each with `multica workspace mcp add <name> --server-config ...`, then re-run the import to attach the agent assignments: `<server-name>`."
109
+ - If `mcpServersUnresolved` is non-empty, surface every `<agent-name>:<server-name>` entry verbatim with: "NOTE: the following agent MCP server assignments were skipped — no server of that name exists in the destination workspace library: `<entry>`."
110
+ - If `mcpServersApplyFailures` is non-empty, surface every `<agent-name>:<server-name>` entry verbatim with: "WARNING: the following agent MCP server assignments were added but the CLI rejected the enable/disable that follows — check their state in the Multica UI: `<entry>`."
111
+ - If `labelDescriptionUnsupported` is non-empty, surface every label name verbatim with: "NOTE: the following labels' descriptions could not be applied (the multica CLI has no `--description` flag on `label create`/`update`) — re-enter them in the Multica UI: `<label-name>`."
112
+ - If `propertyTypeConflicts` is non-empty, surface every entry verbatim with: "WARNING: the following custom properties were SKIPPED — a property of that name already exists at the destination with a different type, and a property's type is immutable: `<entry>`. Rename one side, or map the values by hand."
113
+ - If `propertiesArchived` is non-empty, surface every property name verbatim with: "NOTE: the following custom properties were imported and then archived, matching their state at the source: `<property-name>`."
@@ -18,7 +18,7 @@ sync <type> <name> from <src-ws> to <dest-ws>
18
18
 
19
19
  Where `<type>` is `skill`, `agent`, `squad`, `project`, or `autopilot`; `<name>` is the resource name (for `project` and `autopilot`, its **title**); `<src-ws>` and `<dest-ws>` are workspace names registered in Multica.
20
20
 
21
- Projects and autopilots are resolved by title, not ID, e.g. `multica-sync.mjs project "<title>" from <src-ws> <dest-ws>` — the project's lead agent is synced alongside it. `multica-sync.mjs autopilot "<title>" from <src-ws> <dest-ws>` syncs the autopilot's assignee (agent, or squad + members) alongside it; a squad-assigned autopilot aborts (see the import skill — the multica CLI has no command to assign a squad to an autopilot).
21
+ Projects and autopilots are resolved by title, not ID, e.g. `multica-sync.mjs project "<title>" from <src-ws> <dest-ws>` — the project's lead agent is synced alongside it. `multica-sync.mjs autopilot "<title>" from <src-ws> <dest-ws>` syncs only the autopilot configuration (title, description, triggers, etc.) its assignee is linked by name and must already exist in the destination workspace; the assignee is not bundled or synced alongside the autopilot. A squad-assigned autopilot aborts (see the import skill — the multica CLI has no command to assign a squad to an autopilot).
22
22
 
23
23
  ## Step 2 — Run the sync (auto-mapping first)
24
24
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "plugin-validator",
3
3
  "displayName": "Plugin Validator",
4
- "version": "0.0.45",
4
+ "version": "0.0.46",
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,17 +0,0 @@
1
- {
2
- "name": "html-effectiveness",
3
- "displayName": "HTML Effectiveness Reports",
4
- "version": "0.0.45",
5
- "description": "Generate self-contained interactive HTML reports from 20 upstream templates via a conversational agent.",
6
- "author": {
7
- "name": "Steven Hoang"
8
- },
9
- "keywords": [
10
- "html",
11
- "report",
12
- "status",
13
- "incident",
14
- "slide-deck",
15
- "decision-matrix"
16
- ]
17
- }