@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.
- package/.claude-plugin/marketplace.json +2 -47
- package/README.md +3 -6
- package/package.json +4 -1
- package/plugins/multica-tool/.claude-plugin/plugin.json +1 -1
- package/plugins/multica-tool/scripts/lib.mjs +53 -4
- package/plugins/multica-tool/scripts/multica-export.mjs +135 -32
- package/plugins/multica-tool/scripts/multica-import.mjs +192 -17
- package/plugins/multica-tool/skills/export/SKILL.md +84 -14
- package/plugins/multica-tool/skills/import/SKILL.md +22 -7
- package/plugins/multica-tool/skills/sync/SKILL.md +1 -1
- package/plugins/plugin-validator/.claude-plugin/plugin.json +1 -1
- package/plugins/html-effectiveness/.claude-plugin/plugin.json +0 -17
- package/plugins/html-effectiveness/README.md +0 -28
- package/plugins/html-effectiveness/agents/report-builder.md +0 -66
- package/plugins/html-effectiveness/assets/base.css +0 -51
- package/plugins/html-effectiveness/assets/base.js +0 -11
- package/plugins/html-effectiveness/assets/charts.js +0 -1
- package/plugins/html-effectiveness/assets/components.css +0 -10
- package/plugins/html-effectiveness/commands/html-report.md +0 -13
- package/plugins/html-effectiveness/docs/template-gallery.md +0 -28
- package/plugins/html-effectiveness/package.json +0 -6
- package/plugins/html-effectiveness/scripts/_tmp.js +0 -37
- package/plugins/html-effectiveness/scripts/catalog.js +0 -47
- package/plugins/html-effectiveness/scripts/extract.js +0 -104
- package/plugins/html-effectiveness/scripts/mustache.js +0 -90
- package/plugins/html-effectiveness/scripts/render.js +0 -176
- package/plugins/html-effectiveness/skills/html-effectiveness/SKILL.md +0 -94
- package/plugins/html-effectiveness/templates/01-exploration-code-approaches.html.tmpl +0 -400
- package/plugins/html-effectiveness/templates/02-exploration-visual-designs.html.tmpl +0 -456
- package/plugins/html-effectiveness/templates/03-code-review-pr.html.tmpl +0 -581
- package/plugins/html-effectiveness/templates/04-code-understanding.html.tmpl +0 -443
- package/plugins/html-effectiveness/templates/05-design-system.html.tmpl +0 -578
- package/plugins/html-effectiveness/templates/06-component-variants.html.tmpl +0 -557
- package/plugins/html-effectiveness/templates/07-prototype-animation.html.tmpl +0 -417
- package/plugins/html-effectiveness/templates/08-prototype-interaction.html.tmpl +0 -363
- package/plugins/html-effectiveness/templates/09-slide-deck.html.tmpl +0 -535
- package/plugins/html-effectiveness/templates/10-svg-illustrations.html.tmpl +0 -444
- package/plugins/html-effectiveness/templates/11-status-report.html.tmpl +0 -152
- package/plugins/html-effectiveness/templates/12-incident-report.html.tmpl +0 -547
- package/plugins/html-effectiveness/templates/13-flowchart-diagram.html.tmpl +0 -361
- package/plugins/html-effectiveness/templates/14-research-feature-explainer.html.tmpl +0 -348
- package/plugins/html-effectiveness/templates/15-research-concept-explainer.html.tmpl +0 -334
- package/plugins/html-effectiveness/templates/16-implementation-plan.html.tmpl +0 -657
- package/plugins/html-effectiveness/templates/17-pr-writeup.html.tmpl +0 -553
- package/plugins/html-effectiveness/templates/18-editor-triage-board.html.tmpl +0 -541
- package/plugins/html-effectiveness/templates/19-editor-feature-flags.html.tmpl +0 -627
- package/plugins/html-effectiveness/templates/20-editor-prompt-tuner.html.tmpl +0 -687
- package/plugins/html-effectiveness/templates/manifest.json +0 -441
- package/plugins/team-share/.claude-plugin/plugin.json +0 -18
- package/plugins/team-share/README.md +0 -46
- package/plugins/team-share/agents/team-share.md +0 -53
- package/plugins/team-share/commands/team-share.md +0 -4
- package/plugins/team-share/skills/claude-config/SKILL.md +0 -143
- package/plugins/team-share/skills/codegraph-setup/SKILL.md +0 -81
- package/plugins/team-share/skills/understand-setup/SKILL.md +0 -115
- package/plugins/tech-graph/.claude-plugin/plugin.json +0 -17
- package/plugins/tech-graph/README.md +0 -66
- package/plugins/tech-graph/agents/tech-graph.md +0 -97
- package/plugins/tech-graph/commands/tech-graph.md +0 -13
- package/plugins/tech-graph/scripts/check-deps.sh +0 -55
- package/plugins/tech-graph/skills/tech-graph/LICENSE +0 -21
- package/plugins/tech-graph/skills/tech-graph/README.md +0 -555
- package/plugins/tech-graph/skills/tech-graph/README.zh.md +0 -514
- package/plugins/tech-graph/skills/tech-graph/SKILL.md +0 -263
- package/plugins/tech-graph/skills/tech-graph/agents/openai.yaml +0 -4
- package/plugins/tech-graph/skills/tech-graph/assets/samples/sample-style1-flat.png +0 -0
- package/plugins/tech-graph/skills/tech-graph/assets/samples/sample-style2-dark.png +0 -0
- package/plugins/tech-graph/skills/tech-graph/assets/samples/sample-style3-blueprint.png +0 -0
- package/plugins/tech-graph/skills/tech-graph/assets/samples/sample-style4-notion.png +0 -0
- package/plugins/tech-graph/skills/tech-graph/assets/samples/sample-style5-glass.png +0 -0
- package/plugins/tech-graph/skills/tech-graph/assets/samples/sample-style6-claude.png +0 -0
- package/plugins/tech-graph/skills/tech-graph/assets/samples/sample-style7-openai.png +0 -0
- package/plugins/tech-graph/skills/tech-graph/fixtures/agent-memory-types-style4.json +0 -181
- package/plugins/tech-graph/skills/tech-graph/fixtures/api-flow-style7.json +0 -40
- package/plugins/tech-graph/skills/tech-graph/fixtures/mem0-style1.json +0 -297
- package/plugins/tech-graph/skills/tech-graph/fixtures/microservices-style3.json +0 -64
- package/plugins/tech-graph/skills/tech-graph/fixtures/multi-agent-style5.json +0 -45
- package/plugins/tech-graph/skills/tech-graph/fixtures/system-architecture-style6.json +0 -48
- package/plugins/tech-graph/skills/tech-graph/fixtures/tool-call-style2.json +0 -182
- package/plugins/tech-graph/skills/tech-graph/package.json +0 -42
- package/plugins/tech-graph/skills/tech-graph/references/diagram-types.md +0 -171
- package/plugins/tech-graph/skills/tech-graph/references/icons.md +0 -406
- package/plugins/tech-graph/skills/tech-graph/references/style-1-flat-icon.md +0 -108
- package/plugins/tech-graph/skills/tech-graph/references/style-2-dark-terminal.md +0 -107
- package/plugins/tech-graph/skills/tech-graph/references/style-3-blueprint.md +0 -113
- package/plugins/tech-graph/skills/tech-graph/references/style-4-notion-clean.md +0 -95
- package/plugins/tech-graph/skills/tech-graph/references/style-5-glassmorphism.md +0 -125
- package/plugins/tech-graph/skills/tech-graph/references/style-6-claude-official.md +0 -209
- package/plugins/tech-graph/skills/tech-graph/references/style-7-openai.md +0 -215
- package/plugins/tech-graph/skills/tech-graph/references/style-diagram-matrix.md +0 -135
- package/plugins/tech-graph/skills/tech-graph/references/svg-layout-best-practices.md +0 -100
- package/plugins/tech-graph/skills/tech-graph/references/svg-to-png.md +0 -115
- package/plugins/tech-graph/skills/tech-graph/scripts/README.md +0 -269
- package/plugins/tech-graph/skills/tech-graph/scripts/generate-diagram.sh +0 -181
- package/plugins/tech-graph/skills/tech-graph/scripts/generate-from-template.py +0 -1587
- package/plugins/tech-graph/skills/tech-graph/scripts/test-all-styles.sh +0 -143
- package/plugins/tech-graph/skills/tech-graph/scripts/validate-svg.sh +0 -306
- package/plugins/tech-graph/skills/tech-graph/templates/agent-architecture.svg +0 -28
- package/plugins/tech-graph/skills/tech-graph/templates/architecture.svg +0 -23
- package/plugins/tech-graph/skills/tech-graph/templates/comparison-matrix.svg +0 -14
- package/plugins/tech-graph/skills/tech-graph/templates/data-flow.svg +0 -28
- package/plugins/tech-graph/skills/tech-graph/templates/er-diagram.svg +0 -21
- package/plugins/tech-graph/skills/tech-graph/templates/flowchart.svg +0 -21
- package/plugins/tech-graph/skills/tech-graph/templates/sequence.svg +0 -20
- package/plugins/tech-graph/skills/tech-graph/templates/state-machine.svg +0 -20
- package/plugins/tech-graph/skills/tech-graph/templates/timeline.svg +0 -19
- 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;
|
|
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
|
-
//
|
|
359
|
-
//
|
|
360
|
-
|
|
361
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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 —
|
|
21
|
+
## Step 2 — Pick a level, or a single resource
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
There are two ways to export, and they are mutually exclusive:
|
|
24
24
|
|
|
25
|
-
|
|
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
|
|
64
|
+
Then construct the default directory:
|
|
45
65
|
|
|
46
|
-
-
|
|
47
|
-
- a single resource (
|
|
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
|
|
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|
|
|
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
|
-
`--
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
-
|
|
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 `
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
}
|