@drunkcoding/agents-and-skills 0.0.44 → 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 +170 -50
- package/plugins/multica-tool/scripts/multica-import.mjs +209 -27
- package/plugins/multica-tool/skills/export/SKILL.md +91 -22
- package/plugins/multica-tool/skills/import/SKILL.md +24 -9
- 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,22 +1,35 @@
|
|
|
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");
|
|
8
9
|
return set;
|
|
9
10
|
}
|
|
10
11
|
|
|
11
|
-
//
|
|
12
|
-
// avatar_file). Legacy bundles carry no
|
|
13
|
-
// inline in the JSON — fall back to that so older exports
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
// A prose field (instructions/description) lives in a sibling .md referenced by
|
|
13
|
+
// `<field>_file` (mirrors avatar_file). Legacy bundles carry no `<field>_file`
|
|
14
|
+
// and keep the prose inline in the JSON — fall back to that so older exports
|
|
15
|
+
// still import.
|
|
16
|
+
function readSidecar(fs, dir, rec, fileKey, inlineKey) {
|
|
17
|
+
if (rec[fileKey] && fs.existsSync(`${dir}/${rec[fileKey]}`)) {
|
|
18
|
+
return fs.readFileSync(`${dir}/${rec[fileKey]}`, "utf8");
|
|
17
19
|
}
|
|
18
|
-
return rec
|
|
20
|
+
return rec[inlineKey] ?? "";
|
|
19
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
|
+
}
|
|
32
|
+
const readDescription = (fs, dir, rec) => readSidecar(fs, dir, rec, "description_file", "description");
|
|
20
33
|
|
|
21
34
|
// Relative paths of every file under root (recursing into subdirs like scripts/).
|
|
22
35
|
function walkSkillFiles(fs, root, rel = "") {
|
|
@@ -83,9 +96,14 @@ export function importAgents({ cli, manifest, dir, skillIdMap, runtimeMap, fs =
|
|
|
83
96
|
const avatarUnsupported = []; // emoji avatar — no CLI setter for agents
|
|
84
97
|
const permissionApplyFailures = []; // CLI rejected --public-to-member
|
|
85
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
|
|
86
101
|
// Lazy + memoized: destination member user_ids, only listed when an agent needs them.
|
|
87
102
|
let destMemberIds = null;
|
|
88
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]));
|
|
89
107
|
let created = 0, updated = 0, reused = 0;
|
|
90
108
|
// Includes archived agents so a bundle name matching a retired agent restores
|
|
91
109
|
// and reuses it instead of failing to create under the held name.
|
|
@@ -100,7 +118,8 @@ export function importAgents({ cli, manifest, dir, skillIdMap, runtimeMap, fs =
|
|
|
100
118
|
"--visibility", rec.visibility ?? "private",
|
|
101
119
|
"--max-concurrent-tasks", String(rec.max_concurrent_tasks ?? 6),
|
|
102
120
|
];
|
|
103
|
-
|
|
121
|
+
const description = readDescription(fs, dir, rec);
|
|
122
|
+
if (description) common.push("--description", description);
|
|
104
123
|
const instructions = readInstructions(fs, dir, rec);
|
|
105
124
|
if (instructions) common.push("--instructions", instructions);
|
|
106
125
|
if (rec.model) common.push("--model", rec.model);
|
|
@@ -129,6 +148,21 @@ export function importAgents({ cli, manifest, dir, skillIdMap, runtimeMap, fs =
|
|
|
129
148
|
const skillIds = (rec.skill_names ?? []).map((n) => skillIdMap.get(n)).filter(Boolean);
|
|
130
149
|
cli.run(["agent", "skills", "set", id, "--skill-ids", skillIds.join(",")]);
|
|
131
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
|
+
|
|
132
166
|
// Avatar: only ever set it when the target agent has none — never clobber an
|
|
133
167
|
// avatar an existing agent already has. Agents accept only image uploads
|
|
134
168
|
// (`agent avatar --file`); an emoji-only avatar can't be restored via the CLI.
|
|
@@ -187,7 +221,7 @@ export function importAgents({ cli, manifest, dir, skillIdMap, runtimeMap, fs =
|
|
|
187
221
|
}
|
|
188
222
|
}
|
|
189
223
|
}
|
|
190
|
-
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 };
|
|
191
225
|
}
|
|
192
226
|
|
|
193
227
|
// Rewrites `mention://agent/<id>` links (e.g. `[@dev-backend](mention://agent/<id>)`)
|
|
@@ -269,7 +303,8 @@ export function importProjects({ cli, manifest, dir, agentIdMap, fs = nodeFs })
|
|
|
269
303
|
for (const entry of manifest.projects ?? []) {
|
|
270
304
|
const rec = JSON.parse(fs.readFileSync(`${dir}/${entry.file}`, "utf8"));
|
|
271
305
|
const flags = ["--title", rec.title];
|
|
272
|
-
|
|
306
|
+
const description = readDescription(fs, dir, rec);
|
|
307
|
+
if (description) flags.push("--description", description);
|
|
273
308
|
if (rec.icon) flags.push("--icon", rec.icon);
|
|
274
309
|
if (rec.status) flags.push("--status", rec.status);
|
|
275
310
|
if (rec.due_date) flags.push("--due-date", rec.due_date);
|
|
@@ -339,6 +374,9 @@ export function importAutopilots({ cli, manifest, dir, agentIdMap, fs = nodeFs }
|
|
|
339
374
|
const idMap = new Map();
|
|
340
375
|
let created = 0, updated = 0;
|
|
341
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);
|
|
342
380
|
const existing = listAutopilots(cli);
|
|
343
381
|
let destProjects = null;
|
|
344
382
|
const resolveProjectId = (title) => (destProjects ??= listProjects(cli)).find((p) => p.title === title)?.id ?? null;
|
|
@@ -347,12 +385,14 @@ export function importAutopilots({ cli, manifest, dir, agentIdMap, fs = nodeFs }
|
|
|
347
385
|
|
|
348
386
|
for (const rec of recs) {
|
|
349
387
|
const common = [];
|
|
350
|
-
|
|
388
|
+
const description = readDescription(fs, dir, rec);
|
|
389
|
+
if (description) common.push("--description", description);
|
|
351
390
|
if (rec.issue_title_template) common.push("--issue-title-template", rec.issue_title_template);
|
|
352
|
-
//
|
|
353
|
-
//
|
|
354
|
-
|
|
355
|
-
|
|
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);
|
|
356
396
|
|
|
357
397
|
if (rec.project_title) {
|
|
358
398
|
const projectId = resolveProjectId(rec.project_title);
|
|
@@ -397,7 +437,82 @@ export function importAutopilots({ cli, manifest, dir, agentIdMap, fs = nodeFs }
|
|
|
397
437
|
if (t.kind === "webhook") webhookReissued.push(rec.title);
|
|
398
438
|
}
|
|
399
439
|
}
|
|
400
|
-
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);
|
|
401
516
|
}
|
|
402
517
|
|
|
403
518
|
export function collectSourceRuntimes(manifest) {
|
|
@@ -457,7 +572,7 @@ export function importBundle({ cli, dir, runtimeMap, include, fs = nodeFs }) {
|
|
|
457
572
|
: { idMap: new Map(), created: 0, updated: 0 };
|
|
458
573
|
const agentRes = inc.has("agents")
|
|
459
574
|
? importAgents({ cli, manifest, dir, skillIdMap: skillRes.idMap, runtimeMap: effective, fs })
|
|
460
|
-
: { 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: [] };
|
|
461
576
|
// Runs after every agent exists so forward-referencing mentions resolve.
|
|
462
577
|
const mentionRes = inc.has("agents")
|
|
463
578
|
? rewriteAgentMentions({ cli, manifest, dir, agentIdMap: agentRes.idMap, sourceIdMap: agentRes.sourceIdMap, fs })
|
|
@@ -469,6 +584,7 @@ export function importBundle({ cli, dir, runtimeMap, include, fs = nodeFs }) {
|
|
|
469
584
|
if (inc.has("squads")) {
|
|
470
585
|
for (const squad of manifest.squads ?? []) {
|
|
471
586
|
squad.instructions = readInstructions(fs, dir, squad);
|
|
587
|
+
squad.description = readDescription(fs, dir, squad);
|
|
472
588
|
const r = importSquad({ cli, squad, agentIdMap: agentRes.idMap, sourceIdMap: agentRes.sourceIdMap });
|
|
473
589
|
if (r.skipped) { squadsSkipped.push(squad.name); continue; }
|
|
474
590
|
squadIdMap.set(squad.name, r.newId);
|
|
@@ -482,15 +598,22 @@ export function importBundle({ cli, dir, runtimeMap, include, fs = nodeFs }) {
|
|
|
482
598
|
projectRes = importProjects({ cli, manifest, dir, agentIdMap: agentRes.idMap, fs });
|
|
483
599
|
}
|
|
484
600
|
|
|
485
|
-
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: [] };
|
|
486
602
|
if (inc.has("autopilots")) {
|
|
487
603
|
autopilotRes = importAutopilots({ cli, manifest, dir, agentIdMap: agentRes.idMap, fs });
|
|
488
604
|
}
|
|
489
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
|
+
|
|
490
613
|
return {
|
|
491
614
|
include: [...inc],
|
|
492
|
-
created: { skills: skillRes.created, agents: agentRes.created, squads: squadsCreated, projects: projectRes.created, autopilots: autopilotRes.created },
|
|
493
|
-
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 },
|
|
494
617
|
// Agents restored from archived + updated to the bundle's definition — distinct
|
|
495
618
|
// from a plain update (an active-name match). Only agents can be reused today.
|
|
496
619
|
reused: { agents: agentRes.reused },
|
|
@@ -506,6 +629,13 @@ export function importBundle({ cli, dir, runtimeMap, include, fs = nodeFs }) {
|
|
|
506
629
|
avatarUnsupported: agentRes.avatarUnsupported,
|
|
507
630
|
permissionApplyFailures: agentRes.permissionApplyFailures,
|
|
508
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 }),
|
|
509
639
|
squadsSkipped,
|
|
510
640
|
priorityUnsupported: projectRes.priorityUnsupported,
|
|
511
641
|
resourcesUnsupported: projectRes.resourcesUnsupported,
|
|
@@ -514,6 +644,10 @@ export function importBundle({ cli, dir, runtimeMap, include, fs = nodeFs }) {
|
|
|
514
644
|
autopilotSubscribersUnresolved: autopilotRes.subscribersUnresolved,
|
|
515
645
|
autopilotPriorityNotCaptured: autopilotRes.priorityNotCaptured,
|
|
516
646
|
autopilotWebhookReissued: autopilotRes.webhookReissued,
|
|
647
|
+
autopilotsActiveAtSource: autopilotRes.activeAtSource,
|
|
648
|
+
labelDescriptionUnsupported: labelRes.descriptionUnsupported,
|
|
649
|
+
propertyTypeConflicts: propertyRes.typeConflicts,
|
|
650
|
+
propertiesArchived: propertyRes.archivedApplied,
|
|
517
651
|
};
|
|
518
652
|
}
|
|
519
653
|
|
|
@@ -521,13 +655,20 @@ export function preflight({ cli, dir, runtimeMap, include, fs = nodeFs }) {
|
|
|
521
655
|
const inc = include ?? new Set(["skills", "agents", "squads"]);
|
|
522
656
|
const manifest = JSON.parse(fs.readFileSync(`${dir}/manifest.json`, "utf8"));
|
|
523
657
|
const count = (k) => (manifest[k] ?? []).length;
|
|
524
|
-
|
|
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 };
|
|
525
664
|
const willImport = {
|
|
526
665
|
skills: inc.has("skills") ? bundle.skills : 0,
|
|
527
666
|
agents: inc.has("agents") ? bundle.agents : 0,
|
|
528
667
|
squads: inc.has("squads") ? bundle.squads : 0,
|
|
529
668
|
projects: inc.has("projects") ? bundle.projects : 0,
|
|
530
669
|
autopilots: inc.has("autopilots") ? bundle.autopilots : 0,
|
|
670
|
+
labels: inc.has("labels") ? bundle.labels : 0,
|
|
671
|
+
properties: inc.has("properties") ? bundle.properties : 0,
|
|
531
672
|
};
|
|
532
673
|
|
|
533
674
|
const incompatibilities = [];
|
|
@@ -589,15 +730,56 @@ export function preflight({ cli, dir, runtimeMap, include, fs = nodeFs }) {
|
|
|
589
730
|
} else if (!agentAvailable(rec.assignee_name)) {
|
|
590
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)` });
|
|
591
732
|
}
|
|
592
|
-
|
|
593
|
-
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)` });
|
|
594
|
-
}
|
|
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)` });
|
|
595
734
|
if (rec.project_title && !projectAvailable(rec.project_title)) {
|
|
596
735
|
incompatibilities.push({ type: "autopilot-project-missing", detail: `${rec.title} → project "${rec.project_title}" (not found in the destination)` });
|
|
597
736
|
}
|
|
598
737
|
if (rec.had_webhook_trigger) {
|
|
599
738
|
incompatibilities.push({ type: "autopilot-webhook-reissued", detail: `${rec.title} (webhook trigger will get a newly issued URL — the old one never travels)` });
|
|
600
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
|
+
}
|
|
601
783
|
}
|
|
602
784
|
}
|
|
603
785
|
|
|
@@ -628,7 +810,7 @@ function main() {
|
|
|
628
810
|
const dryRun = args.includes("--dry-run");
|
|
629
811
|
|
|
630
812
|
if (!dir || !workspace) {
|
|
631
|
-
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]");
|
|
632
814
|
process.exit(1);
|
|
633
815
|
}
|
|
634
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
|
|
@@ -37,51 +57,100 @@ If the user specified an output directory, use it verbatim. Otherwise default to
|
|
|
37
57
|
First resolve `<workspace-name>`:
|
|
38
58
|
|
|
39
59
|
- If the user named a source workspace (the value you would pass as `--workspace <name>`), use that name.
|
|
40
|
-
- Otherwise,
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
multica workspace get --output json
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
and take its `.name`.
|
|
60
|
+
- Otherwise, run `multica workspace get --output json` and take its `.name` (the current default workspace).
|
|
47
61
|
|
|
48
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).
|
|
49
63
|
|
|
50
|
-
Then construct the default directory
|
|
64
|
+
Then construct the default directory:
|
|
51
65
|
|
|
52
|
-
-
|
|
53
|
-
- 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.
|
|
54
68
|
|
|
55
|
-
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`.
|
|
56
70
|
|
|
57
71
|
## Step 4 — Run the export
|
|
58
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
|
+
|
|
59
84
|
```bash
|
|
60
85
|
node "${CLAUDE_PLUGIN_ROOT}/scripts/multica-export.mjs" \
|
|
61
|
-
--scope <skill|agent|squad|project|
|
|
86
|
+
--scope <skill|agent|squad|project|autopilot> \
|
|
62
87
|
--id <id> \
|
|
63
88
|
--out <dir> \
|
|
64
89
|
[--workspace <workspace-name>]
|
|
65
90
|
```
|
|
66
91
|
|
|
67
|
-
`--
|
|
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:
|
|
124
|
+
|
|
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.
|
|
129
|
+
|
|
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.
|
|
68
131
|
|
|
69
|
-
|
|
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:
|
|
70
133
|
|
|
71
|
-
|
|
134
|
+
- **instructions** (system prompt / charter, agents and squads) → `<slug>.md`, referenced by an `instructions_file` key.
|
|
135
|
+
- **description** (agents, squads, projects, autopilots) → `<slug>.description.md`, referenced by a `description_file` key.
|
|
72
136
|
|
|
73
|
-
|
|
137
|
+
An empty field gets no file and no `*_file` key. Skills keep their own layout — the description lives in `SKILL.md` frontmatter and the body is the content.
|
|
74
138
|
|
|
75
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.
|
|
76
140
|
|
|
77
|
-
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.
|
|
78
142
|
|
|
79
143
|
## Step 5 — Report results
|
|
80
144
|
|
|
81
145
|
Parse the JSON output from the script and report:
|
|
82
146
|
|
|
83
147
|
- Directory written to.
|
|
84
|
-
-
|
|
148
|
+
- The level (or the single resource) exported.
|
|
149
|
+
- Count of skills, agents, squads, projects, and autopilots exported.
|
|
85
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).
|
|
86
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>`."
|
|
87
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>`."
|