@drunkcoding/agents-and-skills 0.0.40 → 0.0.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +5 -5
- package/package.json +1 -1
- package/plugins/html-effectiveness/.claude-plugin/plugin.json +1 -1
- package/plugins/multica-tool/.claude-plugin/plugin.json +1 -1
- package/plugins/multica-tool/scripts/lib.mjs +23 -0
- package/plugins/multica-tool/scripts/multica-export.mjs +53 -6
- package/plugins/multica-tool/scripts/multica-import.mjs +166 -22
- package/plugins/multica-tool/scripts/multica-sync.mjs +11 -3
- package/plugins/multica-tool/skills/export/SKILL.md +11 -6
- package/plugins/multica-tool/skills/import/SKILL.md +29 -5
- package/plugins/multica-tool/skills/sync/SKILL.md +4 -2
- package/plugins/plugin-validator/.claude-plugin/plugin.json +1 -1
- package/plugins/team-share/.claude-plugin/plugin.json +1 -1
- package/plugins/tech-graph/.claude-plugin/plugin.json +1 -1
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"name": "tech-graph",
|
|
13
13
|
"source": "./plugins/tech-graph",
|
|
14
14
|
"description": "6-step wizard for technical diagrams (SVG/PNG) via fireworks-tech-graph",
|
|
15
|
-
"version": "0.0.
|
|
15
|
+
"version": "0.0.41",
|
|
16
16
|
"category": "diagram",
|
|
17
17
|
"keywords": [
|
|
18
18
|
"diagram",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"name": "html-effectiveness",
|
|
27
27
|
"source": "./plugins/html-effectiveness",
|
|
28
28
|
"description": "Generate self-contained interactive HTML reports from 20 upstream templates via a conversational agent.",
|
|
29
|
-
"version": "0.0.
|
|
29
|
+
"version": "0.0.41",
|
|
30
30
|
"category": "reports",
|
|
31
31
|
"keywords": [
|
|
32
32
|
"html",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"name": "plugin-validator",
|
|
42
42
|
"source": "./plugins/plugin-validator",
|
|
43
43
|
"description": "Orchestrated validator for Claude Code plugins — validates skills, agents, commands, and hooks across every plugin under plugins/**.",
|
|
44
|
-
"version": "0.0.
|
|
44
|
+
"version": "0.0.41",
|
|
45
45
|
"category": "tooling",
|
|
46
46
|
"keywords": [
|
|
47
47
|
"validation",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"name": "team-share",
|
|
58
58
|
"source": "./plugins/team-share",
|
|
59
59
|
"description": "Onboard your team with an interactive setup menu: install CodeGraph, build the Understand-Anything knowledge graph, and share Claude Code settings — run any combination, all idempotent.",
|
|
60
|
-
"version": "0.0.
|
|
60
|
+
"version": "0.0.41",
|
|
61
61
|
"category": "tooling",
|
|
62
62
|
"keywords": [
|
|
63
63
|
"onboarding",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"name": "multica-tool",
|
|
74
74
|
"source": "./plugins/multica-tool",
|
|
75
75
|
"description": "Export, import, and sync Multica skills, agents, and squads between workspaces.",
|
|
76
|
-
"version": "0.0.
|
|
76
|
+
"version": "0.0.41",
|
|
77
77
|
"category": "workflow",
|
|
78
78
|
"keywords": [
|
|
79
79
|
"multica",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "html-effectiveness",
|
|
3
3
|
"displayName": "HTML Effectiveness Reports",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.41",
|
|
5
5
|
"description": "Generate self-contained interactive HTML reports from 20 upstream templates via a conversational agent.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Steven Hoang"
|
|
@@ -104,3 +104,26 @@ export const getSquadMembers = (cli, id) =>
|
|
|
104
104
|
(cli.json(["squad", "member", "list", id]) ?? []).map((m) => ({
|
|
105
105
|
member_id: m.member_id, member_type: m.member_type, role: m.role || "member",
|
|
106
106
|
}));
|
|
107
|
+
|
|
108
|
+
export const listProjects = (cli) => cli.json(["project", "list"]);
|
|
109
|
+
|
|
110
|
+
export function findByTitle(list, title) {
|
|
111
|
+
const hits = (list || []).filter((x) => x.title === title);
|
|
112
|
+
if (hits.length > 1) throw new Error(`Duplicate title "${title}" — refusing to guess`);
|
|
113
|
+
return hits[0] || null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function getProject(cli, id) {
|
|
117
|
+
const p = cli.json(["project", "get", id]);
|
|
118
|
+
return {
|
|
119
|
+
id: p.id, title: p.title, description: p.description ?? "",
|
|
120
|
+
icon: p.icon ?? null, priority: p.priority ?? "none", status: p.status ?? null,
|
|
121
|
+
due_date: p.due_date ?? null, start_date: p.start_date ?? null,
|
|
122
|
+
lead_id: p.lead_id ?? null, lead_type: p.lead_type ?? null,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export const getProjectResources = (cli, id) =>
|
|
127
|
+
(cli.json(["project", "resource", "list", id]) ?? []).map((r) => ({
|
|
128
|
+
resource_type: r.resource_type, resource_ref: r.resource_ref, label: r.label ?? null,
|
|
129
|
+
}));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as nodeFs from "node:fs";
|
|
2
2
|
import { spawnSync } from "node:child_process";
|
|
3
3
|
import { dirname } from "node:path";
|
|
4
|
-
import { slugify, getSkill, getAgent, getAgentCustomEnv, getSquad, getSquadMembers, listRuntimes, listSkills, listAgents, listSquads, makeCli, realExec, requireAuth, resolveWorkspaceId } from "./lib.mjs";
|
|
4
|
+
import { slugify, getSkill, getAgent, getAgentCustomEnv, getSquad, getSquadMembers, listRuntimes, listSkills, listAgents, listSquads, listProjects, getProject, getProjectResources, makeCli, realExec, requireAuth, resolveWorkspaceId } from "./lib.mjs";
|
|
5
5
|
|
|
6
6
|
const nonEmpty = (v) => v && typeof v === "object" && Object.keys(v).length > 0;
|
|
7
7
|
|
|
@@ -57,11 +57,13 @@ export function redactAgent(a) {
|
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
export function buildManifest({ scope, sourceWorkspaceId, skills, agents, squads }) {
|
|
60
|
+
export function buildManifest({ scope, sourceWorkspaceId, skills, agents, squads, projects }) {
|
|
61
61
|
const seenSkills = new Map();
|
|
62
62
|
for (const s of skills) if (!seenSkills.has(s.name)) seenSkills.set(s.name, s);
|
|
63
63
|
const seenAgents = new Map();
|
|
64
64
|
for (const a of agents) if (!seenAgents.has(a.name)) seenAgents.set(a.name, a);
|
|
65
|
+
const seenProjects = new Map();
|
|
66
|
+
for (const p of projects ?? []) if (!seenProjects.has(p.title)) seenProjects.set(p.title, p);
|
|
65
67
|
return {
|
|
66
68
|
version: "1",
|
|
67
69
|
scope,
|
|
@@ -75,6 +77,10 @@ export function buildManifest({ scope, sourceWorkspaceId, skills, agents, squads
|
|
|
75
77
|
if (squad.instructions) entry.instructions_file = file.replace(/\.json$/, ".md");
|
|
76
78
|
return entry;
|
|
77
79
|
}),
|
|
80
|
+
projects: [...seenProjects.values()].map((p) => ({
|
|
81
|
+
title: p.title, file: `projects/${slugify(p.title)}.json`,
|
|
82
|
+
source_id: p.source_id, lead_name: p.lead_name ?? null, lead_type: p.lead_type ?? null,
|
|
83
|
+
})),
|
|
78
84
|
};
|
|
79
85
|
}
|
|
80
86
|
|
|
@@ -106,10 +112,27 @@ function collectAgent(cli, id, agentsById, skills, providerById) {
|
|
|
106
112
|
return entry;
|
|
107
113
|
}
|
|
108
114
|
|
|
115
|
+
// Collect a project's portable metadata + its lead agent (bundled, like a squad
|
|
116
|
+
// leader) and github_repo/other resources. Returns the project bundle record.
|
|
117
|
+
function collectProject(cli, id, agentsById, skills, providerById) {
|
|
118
|
+
const p = getProject(cli, id);
|
|
119
|
+
let lead_name = null;
|
|
120
|
+
if (p.lead_type === "agent" && p.lead_id) {
|
|
121
|
+
lead_name = collectAgent(cli, p.lead_id, agentsById, skills, providerById).raw.name;
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
title: p.title, description: p.description, icon: p.icon,
|
|
125
|
+
priority: p.priority, status: p.status, due_date: p.due_date, start_date: p.start_date,
|
|
126
|
+
source_id: id, lead_type: p.lead_type, lead_name, lead_source_id: p.lead_id,
|
|
127
|
+
resources: getProjectResources(cli, id),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
109
131
|
export function exportResource({ cli, scope, ids, outDir, sourceWorkspaceId, fs = nodeFs, download = fetchBinary }) {
|
|
110
132
|
const skills = new Map(); // name -> normalized skill
|
|
111
133
|
const agentsById = new Map(); // id -> { raw, red, skill_names }
|
|
112
134
|
const squads = [];
|
|
135
|
+
const projects = [];
|
|
113
136
|
// Lazy + memoized: only fetched when an agent is actually collected (skips
|
|
114
137
|
// the extra CLI call on skill-only exports).
|
|
115
138
|
let providerById = null;
|
|
@@ -132,10 +155,26 @@ export function exportResource({ cli, scope, ids, outDir, sourceWorkspaceId, fs
|
|
|
132
155
|
if (scope === "skill") collectSkill(cli, ids.skillId, skills);
|
|
133
156
|
else if (scope === "agent") collectAgent(cli, ids.agentId, agentsById, skills, getProviderById());
|
|
134
157
|
else if (scope === "squad") squads.push(collectOneSquad(ids.squadId));
|
|
158
|
+
else if (scope === "project") projects.push(collectProject(cli, ids.projectId, agentsById, skills, getProviderById()));
|
|
159
|
+
else if (scope === "projects") for (const p of listProjects(cli)) projects.push(collectProject(cli, p.id, agentsById, skills, getProviderById()));
|
|
135
160
|
else if (scope === "all") {
|
|
136
161
|
for (const s of listSkills(cli)) collectSkill(cli, s.id, skills);
|
|
137
162
|
for (const a of listAgents(cli)) collectAgent(cli, a.id, agentsById, skills, getProviderById());
|
|
138
163
|
for (const sq of listSquads(cli)) squads.push(collectOneSquad(sq.id));
|
|
164
|
+
for (const p of listProjects(cli)) projects.push(collectProject(cli, p.id, agentsById, skills, getProviderById()));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Orphan-skill cleanup: drop skills that no exported agent references via its
|
|
168
|
+
// skill_names. Only `all` ever produces these — standalone workspace skills
|
|
169
|
+
// from listSkills that no agent uses. Skipped for `skill` scope: its one
|
|
170
|
+
// skill is the explicit target, not an orphan.
|
|
171
|
+
const pruned_skills = [];
|
|
172
|
+
if (scope !== "skill") {
|
|
173
|
+
const referenced = new Set();
|
|
174
|
+
for (const a of agentsById.values()) for (const n of a.skill_names) referenced.add(n);
|
|
175
|
+
for (const name of [...skills.keys()]) {
|
|
176
|
+
if (!referenced.has(name)) { pruned_skills.push(name); skills.delete(name); }
|
|
177
|
+
}
|
|
139
178
|
}
|
|
140
179
|
|
|
141
180
|
const manifest = buildManifest({
|
|
@@ -143,6 +182,7 @@ export function exportResource({ cli, scope, ids, outDir, sourceWorkspaceId, fs
|
|
|
143
182
|
skills: [...skills.values()].map((s) => ({ name: s.name, source_id: s.id })),
|
|
144
183
|
agents: [...agentsById.values()].map((a) => ({ name: a.raw.name, source_id: a.raw.id, source_runtime_id: a.raw.runtime_id, source_runtime_provider: a.raw.source_runtime_provider, skill_names: a.skill_names, had_secrets: a.red.hadSecrets })),
|
|
145
184
|
squads,
|
|
185
|
+
projects,
|
|
146
186
|
});
|
|
147
187
|
|
|
148
188
|
const warnings = [];
|
|
@@ -196,8 +236,13 @@ export function exportResource({ cli, scope, ids, outDir, sourceWorkspaceId, fs
|
|
|
196
236
|
}
|
|
197
237
|
fs.writeFileSync(`${outDir}/${entry.file}`, JSON.stringify(entry, null, 2));
|
|
198
238
|
}
|
|
239
|
+
const projectByTitle = new Map(projects.map((p) => [p.title, p]));
|
|
240
|
+
for (const entry of manifest.projects) {
|
|
241
|
+
fs.mkdirSync(`${outDir}/projects`, { recursive: true });
|
|
242
|
+
fs.writeFileSync(`${outDir}/${entry.file}`, JSON.stringify(projectByTitle.get(entry.title), null, 2));
|
|
243
|
+
}
|
|
199
244
|
fs.writeFileSync(`${outDir}/manifest.json`, JSON.stringify(manifest, null, 2));
|
|
200
|
-
return { manifest, warnings };
|
|
245
|
+
return { manifest, warnings, pruned_skills };
|
|
201
246
|
}
|
|
202
247
|
|
|
203
248
|
function main() {
|
|
@@ -209,8 +254,8 @@ function main() {
|
|
|
209
254
|
const out = get("--out");
|
|
210
255
|
const workspace = get("--workspace"); // optional: source workspace name
|
|
211
256
|
|
|
212
|
-
if (!scope || !out || (scope !== "all" && !id)) {
|
|
213
|
-
console.error("Usage: multica-export.mjs --scope <skill|agent|squad|all> --id <id> --out <dir> [--workspace <name>] (--id not needed for --scope all)");
|
|
257
|
+
if (!scope || !out || (scope !== "all" && scope !== "projects" && !id)) {
|
|
258
|
+
console.error("Usage: multica-export.mjs --scope <skill|agent|squad|project|projects|all> --id <id> --out <dir> [--workspace <name>] (--id not needed for --scope all|projects)");
|
|
214
259
|
process.exit(1);
|
|
215
260
|
}
|
|
216
261
|
|
|
@@ -225,8 +270,10 @@ function main() {
|
|
|
225
270
|
if (scope === "skill") ids.skillId = id;
|
|
226
271
|
else if (scope === "agent") ids.agentId = id;
|
|
227
272
|
else if (scope === "squad") ids.squadId = id;
|
|
273
|
+
else if (scope === "project") ids.projectId = id;
|
|
274
|
+
else if (scope === "projects") { /* all projects — no id */ }
|
|
228
275
|
else if (scope === "all") { /* whole workspace — no id */ }
|
|
229
|
-
else { console.error(`Unknown scope "${scope}" — use skill|agent|squad|all`); process.exit(1); }
|
|
276
|
+
else { console.error(`Unknown scope "${scope}" — use skill|agent|squad|project|projects|all`); process.exit(1); }
|
|
230
277
|
|
|
231
278
|
const result = exportResource({ cli, scope, ids, outDir: out, sourceWorkspaceId, fs: nodeFs });
|
|
232
279
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import * as nodeFs from "node:fs";
|
|
2
|
-
import { listSkills, listAgents, listSquads, listRuntimes, listWorkspaceMembers, getSquadMembers, findByName, makeCli, realExec, requireAuth, resolveWorkspaceId } from "./lib.mjs";
|
|
2
|
+
import { listSkills, listAgents, listSquads, listRuntimes, listWorkspaceMembers, getSquadMembers, findByName, makeCli, realExec, requireAuth, resolveWorkspaceId, listProjects, getProjectResources, findByTitle } from "./lib.mjs";
|
|
3
|
+
|
|
4
|
+
// User-facing selectable types are agents/squads/projects; skills follow agents.
|
|
5
|
+
export function parseInclude(raw) {
|
|
6
|
+
const set = new Set((raw ? raw.split(",") : ["agents", "squads"]).map((s) => s.trim()).filter(Boolean));
|
|
7
|
+
if (set.has("agents")) set.add("skills");
|
|
8
|
+
return set;
|
|
9
|
+
}
|
|
3
10
|
|
|
4
11
|
// Instructions live in a sibling .md referenced by `instructions_file` (mirrors
|
|
5
12
|
// avatar_file). Legacy bundles carry no instructions_file and keep instructions
|
|
@@ -208,6 +215,7 @@ export function rewriteAgentMentions({ cli, manifest, dir, agentIdMap, sourceIdM
|
|
|
208
215
|
export function importSquad({ cli, squad, agentIdMap, sourceIdMap }) {
|
|
209
216
|
const existing = listSquads(cli);
|
|
210
217
|
const leaderId = agentIdMap.get(squad.leader_name);
|
|
218
|
+
if (!leaderId) return { skipped: true, created: 0, updated: 0 };
|
|
211
219
|
const match = findByName(existing, squad.name);
|
|
212
220
|
let id, created = 0, updated = 0;
|
|
213
221
|
// Squad instructions commonly list @mentions of teammate agents by their
|
|
@@ -233,12 +241,63 @@ export function importSquad({ cli, squad, agentIdMap, sourceIdMap }) {
|
|
|
233
241
|
for (const m of squad.members) {
|
|
234
242
|
if (m.agent_name === squad.leader_name) continue;
|
|
235
243
|
const memberId = agentIdMap.get(m.agent_name);
|
|
236
|
-
if (present.has(memberId)) continue;
|
|
244
|
+
if (!memberId || present.has(memberId)) continue;
|
|
237
245
|
cli.run(["squad", "member", "add", id, "--member-id", memberId, "--role", m.role, "--type", "agent"]);
|
|
238
246
|
}
|
|
239
247
|
return { newId: id, created, updated };
|
|
240
248
|
}
|
|
241
249
|
|
|
250
|
+
export function importProjects({ cli, manifest, dir, agentIdMap, fs = nodeFs }) {
|
|
251
|
+
const idMap = new Map();
|
|
252
|
+
let created = 0, updated = 0;
|
|
253
|
+
const priorityUnsupported = [], resourcesUnsupported = [], leadUnresolved = [];
|
|
254
|
+
const existing = listProjects(cli);
|
|
255
|
+
// Lead resolves against just-imported agents first, then destination agents.
|
|
256
|
+
let destAgentNames = null;
|
|
257
|
+
const leadResolvable = (name) =>
|
|
258
|
+
agentIdMap.has(name) || (destAgentNames ??= new Set(listAgents(cli).map((a) => a.name))).has(name);
|
|
259
|
+
|
|
260
|
+
for (const entry of manifest.projects ?? []) {
|
|
261
|
+
const rec = JSON.parse(fs.readFileSync(`${dir}/${entry.file}`, "utf8"));
|
|
262
|
+
const flags = ["--title", rec.title];
|
|
263
|
+
if (rec.description) flags.push("--description", rec.description);
|
|
264
|
+
if (rec.icon) flags.push("--icon", rec.icon);
|
|
265
|
+
if (rec.status) flags.push("--status", rec.status);
|
|
266
|
+
if (rec.due_date) flags.push("--due-date", rec.due_date);
|
|
267
|
+
if (rec.start_date) flags.push("--start-date", rec.start_date);
|
|
268
|
+
const wantsLead = rec.lead_type === "agent" && !!rec.lead_name;
|
|
269
|
+
const leadOk = wantsLead && leadResolvable(rec.lead_name);
|
|
270
|
+
if (leadOk) flags.push("--lead", rec.lead_name);
|
|
271
|
+
|
|
272
|
+
const match = findByTitle(existing, rec.title);
|
|
273
|
+
let id;
|
|
274
|
+
if (match) {
|
|
275
|
+
cli.run(["project", "update", match.id, ...flags]);
|
|
276
|
+
id = match.id; updated++;
|
|
277
|
+
} else {
|
|
278
|
+
id = JSON.parse(cli.run(["project", "create", ...flags])).id;
|
|
279
|
+
created++;
|
|
280
|
+
}
|
|
281
|
+
idMap.set(rec.title, id);
|
|
282
|
+
|
|
283
|
+
if (wantsLead && !leadOk) leadUnresolved.push(rec.title);
|
|
284
|
+
if (rec.priority && rec.priority !== "none") priorityUnsupported.push(rec.title);
|
|
285
|
+
|
|
286
|
+
// Resources: recreate github_repo only, idempotent by url.
|
|
287
|
+
const existingUrls = new Set(
|
|
288
|
+
getProjectResources(cli, id).filter((r) => r.resource_type === "github_repo").map((r) => r.resource_ref?.url).filter(Boolean),
|
|
289
|
+
);
|
|
290
|
+
for (const r of rec.resources ?? []) {
|
|
291
|
+
if (r.resource_type !== "github_repo") { resourcesUnsupported.push(`${rec.title}:${r.resource_type}`); continue; }
|
|
292
|
+
const url = r.resource_ref?.url;
|
|
293
|
+
if (!url || existingUrls.has(url)) continue;
|
|
294
|
+
cli.run(["project", "resource", "add", id, "--type", "github_repo", "--url", url, ...(r.label ? ["--label", r.label] : [])]);
|
|
295
|
+
existingUrls.add(url);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return { idMap, created, updated, priorityUnsupported, resourcesUnsupported, leadUnresolved };
|
|
299
|
+
}
|
|
300
|
+
|
|
242
301
|
export function collectSourceRuntimes(manifest) {
|
|
243
302
|
return [...new Set((manifest.agents ?? []).map((a) => a.source_runtime_id).filter(Boolean))];
|
|
244
303
|
}
|
|
@@ -275,46 +334,124 @@ export function resolveRuntimeMap({ cli, manifest, runtimeMap }) {
|
|
|
275
334
|
return { effective, unresolved };
|
|
276
335
|
}
|
|
277
336
|
|
|
278
|
-
export function importBundle({ cli, dir, runtimeMap, fs = nodeFs }) {
|
|
337
|
+
export function importBundle({ cli, dir, runtimeMap, include, fs = nodeFs }) {
|
|
338
|
+
const inc = include ?? new Set(["skills", "agents", "squads"]);
|
|
279
339
|
const manifest = JSON.parse(fs.readFileSync(`${dir}/manifest.json`, "utf8"));
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
340
|
+
|
|
341
|
+
let effective = new Map();
|
|
342
|
+
if (inc.has("agents")) {
|
|
343
|
+
const r = resolveRuntimeMap({ cli, manifest, runtimeMap });
|
|
344
|
+
if (r.unresolved.length) {
|
|
345
|
+
const detail = r.unresolved.map(({ srcId, provider, matchCount }) => provider
|
|
346
|
+
? `${srcId} (provider "${provider}": ${matchCount} matching runtimes in destination, expected exactly 1)`
|
|
347
|
+
: `${srcId} (no provider recorded)`).join(", ");
|
|
348
|
+
throw new Error(`Unmapped runtimes: ${detail} — pass --runtime-map, aborting before any write`);
|
|
349
|
+
}
|
|
350
|
+
effective = r.effective;
|
|
286
351
|
}
|
|
287
352
|
|
|
288
|
-
const skillRes =
|
|
289
|
-
|
|
353
|
+
const skillRes = inc.has("skills")
|
|
354
|
+
? importSkills({ cli, manifest, dir, fs })
|
|
355
|
+
: { idMap: new Map(), created: 0, updated: 0 };
|
|
356
|
+
const agentRes = inc.has("agents")
|
|
357
|
+
? importAgents({ cli, manifest, dir, skillIdMap: skillRes.idMap, runtimeMap: effective, fs })
|
|
358
|
+
: { idMap: new Map(), sourceIdMap: new Map(), created: 0, updated: 0, secretsApplyFailures: [], avatarApplyFailures: [], avatarUnsupported: [], permissionApplyFailures: [], permissionUnsupported: [] };
|
|
290
359
|
// Runs after every agent exists so forward-referencing mentions resolve.
|
|
291
|
-
const mentionRes =
|
|
360
|
+
const mentionRes = inc.has("agents")
|
|
361
|
+
? rewriteAgentMentions({ cli, manifest, dir, agentIdMap: agentRes.idMap, sourceIdMap: agentRes.sourceIdMap, fs })
|
|
362
|
+
: { updated: 0 };
|
|
363
|
+
|
|
292
364
|
const squadIdMap = new Map();
|
|
293
365
|
let squadsCreated = 0, squadsUpdated = 0;
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
366
|
+
const squadsSkipped = [];
|
|
367
|
+
if (inc.has("squads")) {
|
|
368
|
+
for (const squad of manifest.squads ?? []) {
|
|
369
|
+
squad.instructions = readInstructions(fs, dir, squad);
|
|
370
|
+
const r = importSquad({ cli, squad, agentIdMap: agentRes.idMap, sourceIdMap: agentRes.sourceIdMap });
|
|
371
|
+
if (r.skipped) { squadsSkipped.push(squad.name); continue; }
|
|
372
|
+
squadIdMap.set(squad.name, r.newId);
|
|
373
|
+
squadsCreated += r.created;
|
|
374
|
+
squadsUpdated += r.updated;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
let projectRes = { idMap: new Map(), created: 0, updated: 0, priorityUnsupported: [], resourcesUnsupported: [], leadUnresolved: [] };
|
|
379
|
+
if (inc.has("projects")) {
|
|
380
|
+
projectRes = importProjects({ cli, manifest, dir, agentIdMap: agentRes.idMap, fs });
|
|
300
381
|
}
|
|
301
382
|
|
|
302
383
|
return {
|
|
303
|
-
|
|
304
|
-
|
|
384
|
+
include: [...inc],
|
|
385
|
+
created: { skills: skillRes.created, agents: agentRes.created, squads: squadsCreated, projects: projectRes.created },
|
|
386
|
+
updated: { skills: skillRes.updated, agents: agentRes.updated, squads: squadsUpdated, projects: projectRes.updated },
|
|
305
387
|
mentionsRewritten: mentionRes.updated,
|
|
306
388
|
skillIdMap: Object.fromEntries(skillRes.idMap),
|
|
307
389
|
agentIdMap: Object.fromEntries(agentRes.idMap),
|
|
308
390
|
squadIdMap: Object.fromEntries(squadIdMap),
|
|
391
|
+
projectIdMap: Object.fromEntries(projectRes.idMap),
|
|
309
392
|
secretsReminder: (manifest.agents ?? []).filter((a) => a.had_secrets).map((a) => a.name),
|
|
310
393
|
secretsApplyFailures: agentRes.secretsApplyFailures,
|
|
311
394
|
avatarApplyFailures: agentRes.avatarApplyFailures,
|
|
312
395
|
avatarUnsupported: agentRes.avatarUnsupported,
|
|
313
396
|
permissionApplyFailures: agentRes.permissionApplyFailures,
|
|
314
397
|
permissionUnsupported: agentRes.permissionUnsupported,
|
|
398
|
+
squadsSkipped,
|
|
399
|
+
priorityUnsupported: projectRes.priorityUnsupported,
|
|
400
|
+
resourcesUnsupported: projectRes.resourcesUnsupported,
|
|
401
|
+
leadUnresolved: projectRes.leadUnresolved,
|
|
315
402
|
};
|
|
316
403
|
}
|
|
317
404
|
|
|
405
|
+
export function preflight({ cli, dir, runtimeMap, include, fs = nodeFs }) {
|
|
406
|
+
const inc = include ?? new Set(["skills", "agents", "squads"]);
|
|
407
|
+
const manifest = JSON.parse(fs.readFileSync(`${dir}/manifest.json`, "utf8"));
|
|
408
|
+
const count = (k) => (manifest[k] ?? []).length;
|
|
409
|
+
const bundle = { skills: count("skills"), agents: count("agents"), squads: count("squads"), projects: count("projects") };
|
|
410
|
+
const willImport = {
|
|
411
|
+
skills: inc.has("skills") ? bundle.skills : 0,
|
|
412
|
+
agents: inc.has("agents") ? bundle.agents : 0,
|
|
413
|
+
squads: inc.has("squads") ? bundle.squads : 0,
|
|
414
|
+
projects: inc.has("projects") ? bundle.projects : 0,
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
const incompatibilities = [];
|
|
418
|
+
let runtimes = { resolved: [], unresolved: [] };
|
|
419
|
+
if (inc.has("agents")) {
|
|
420
|
+
const { effective, unresolved } = resolveRuntimeMap({ cli, manifest, runtimeMap });
|
|
421
|
+
runtimes = {
|
|
422
|
+
resolved: [...effective.entries()].map(([s, d]) => `${s}=${d}`),
|
|
423
|
+
unresolved: unresolved.map((u) => u.srcId),
|
|
424
|
+
};
|
|
425
|
+
for (const u of unresolved) {
|
|
426
|
+
incompatibilities.push({ type: "unmapped-runtime", detail: u.provider
|
|
427
|
+
? `${u.srcId} (provider "${u.provider}": ${u.matchCount} matches, expected 1)`
|
|
428
|
+
: `${u.srcId} (no provider recorded)` });
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (inc.has("projects")) {
|
|
433
|
+
const bundleAgentNames = new Set((manifest.agents ?? []).map((a) => a.name));
|
|
434
|
+
for (const entry of manifest.projects ?? []) {
|
|
435
|
+
const rec = JSON.parse(fs.readFileSync(`${dir}/${entry.file}`, "utf8"));
|
|
436
|
+
if (rec.priority && rec.priority !== "none") {
|
|
437
|
+
incompatibilities.push({ type: "priority-not-settable", detail: `${rec.title} (priority "${rec.priority}")` });
|
|
438
|
+
}
|
|
439
|
+
for (const r of rec.resources ?? []) {
|
|
440
|
+
if (r.resource_type !== "github_repo") {
|
|
441
|
+
incompatibilities.push({ type: "resource-not-portable", detail: `${rec.title} (${r.resource_type})` });
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
const leadAvailable = inc.has("agents") && bundleAgentNames.has(rec.lead_name);
|
|
445
|
+
if (rec.lead_type === "agent" && rec.lead_name && !leadAvailable) {
|
|
446
|
+
incompatibilities.push({ type: "lead-agent-missing", detail: `${rec.title} → ${rec.lead_name} (ensure this agent exists in the destination)` });
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const secretsReminder = (manifest.agents ?? []).filter((a) => a.had_secrets).map((a) => a.name);
|
|
452
|
+
return { bundle, willImport, runtimes, incompatibilities, secretsReminder };
|
|
453
|
+
}
|
|
454
|
+
|
|
318
455
|
function parseRuntimeMap(raw) {
|
|
319
456
|
// Parse "srcId1=dstId1,srcId2=dstId2" into a Map.
|
|
320
457
|
const map = new Map();
|
|
@@ -334,9 +471,11 @@ function main() {
|
|
|
334
471
|
const dir = get("--dir");
|
|
335
472
|
const workspace = get("--workspace");
|
|
336
473
|
const rawMap = get("--runtime-map");
|
|
474
|
+
const rawInclude = get("--include");
|
|
475
|
+
const dryRun = args.includes("--dry-run");
|
|
337
476
|
|
|
338
477
|
if (!dir || !workspace) {
|
|
339
|
-
console.error("Usage: multica-import.mjs --dir <folder> --workspace <name> [--runtime-map <src=dst,...>]");
|
|
478
|
+
console.error("Usage: multica-import.mjs --dir <folder> --workspace <name> [--runtime-map <src=dst,...>] [--include <csv>] [--dry-run]");
|
|
340
479
|
process.exit(1);
|
|
341
480
|
}
|
|
342
481
|
|
|
@@ -345,8 +484,13 @@ function main() {
|
|
|
345
484
|
const wsId = resolveWorkspaceId(resolver, workspace);
|
|
346
485
|
const cli = makeCli(realExec, { workspaceId: wsId });
|
|
347
486
|
const runtimeMap = parseRuntimeMap(rawMap);
|
|
487
|
+
const include = parseInclude(rawInclude);
|
|
348
488
|
|
|
349
|
-
|
|
489
|
+
if (dryRun) {
|
|
490
|
+
console.log(JSON.stringify(preflight({ cli, dir, runtimeMap, include, fs: nodeFs }), null, 2));
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
const result = importBundle({ cli, dir, runtimeMap, include, fs: nodeFs });
|
|
350
494
|
console.log(JSON.stringify(result, null, 2));
|
|
351
495
|
}
|
|
352
496
|
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import * as nodeFs from "node:fs";
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import { makeCli, resolveWorkspaceId, findByName, listSkills, listAgents, listSquads, realExec, requireAuth } from "./lib.mjs";
|
|
4
|
+
import { makeCli, resolveWorkspaceId, findByName, findByTitle, listSkills, listAgents, listSquads, listProjects, realExec, requireAuth } from "./lib.mjs";
|
|
5
5
|
import { exportResource } from "./multica-export.mjs";
|
|
6
6
|
import { importBundle } from "./multica-import.mjs";
|
|
7
7
|
|
|
8
8
|
export function resolveScopeId(cli, type, name) {
|
|
9
|
+
if (type === "project") {
|
|
10
|
+
const match = findByTitle(listProjects(cli), name);
|
|
11
|
+
if (!match) throw new Error(`Unknown project "${name}" in source workspace`);
|
|
12
|
+
return { scope: "project", ids: { projectId: match.id } };
|
|
13
|
+
}
|
|
9
14
|
const lists = { skill: listSkills, agent: listAgents, squad: listSquads };
|
|
10
|
-
if (!lists[type]) throw new Error(`Unknown type "${type}" (skill|agent|squad)`);
|
|
15
|
+
if (!lists[type]) throw new Error(`Unknown type "${type}" (skill|agent|squad|project)`);
|
|
11
16
|
const match = findByName(lists[type](cli), name);
|
|
12
17
|
if (!match) throw new Error(`Unknown ${type} "${name}" in source workspace`);
|
|
13
18
|
const key = { skill: "skillId", agent: "agentId", squad: "squadId" }[type];
|
|
@@ -23,7 +28,10 @@ export function sync({ exec, type, name, srcWsName, destWsName, tmpDir, runtimeM
|
|
|
23
28
|
|
|
24
29
|
const { scope, ids } = resolveScopeId(srcCli, type, name);
|
|
25
30
|
exportResource({ cli: srcCli, scope, ids, outDir: tmpDir, sourceWorkspaceId: srcId, fs });
|
|
26
|
-
|
|
31
|
+
const includeByType = { skill: ["skills"], agent: ["agents"], squad: ["agents", "squads"], project: ["agents", "projects"] };
|
|
32
|
+
const include = new Set(includeByType[type] ?? ["agents", "squads"]);
|
|
33
|
+
if (include.has("agents")) include.add("skills");
|
|
34
|
+
return importBundle({ cli: destCli, dir: tmpDir, runtimeMap, include, fs });
|
|
27
35
|
}
|
|
28
36
|
|
|
29
37
|
function parseRuntimeMap(raw) {
|
|
@@ -10,17 +10,17 @@ Export a Multica resource (skill, agent, or squad) to a local bundle directory.
|
|
|
10
10
|
|
|
11
11
|
## Step 1 — Verify authentication
|
|
12
12
|
|
|
13
|
-
Run
|
|
13
|
+
Run `multica auth status` directly. The export script performs this same check internally before doing any work (via `requireAuth` in `scripts/lib.mjs`), but only after `--scope`/`--out` are supplied — so checking it here up front avoids masking an auth failure behind a later usage error:
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
|
|
16
|
+
multica auth status 2>&1 || true
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
If `multica login` is required, surface that message verbatim and stop.
|
|
20
20
|
|
|
21
21
|
## Step 2 — Determine scope and resource ID
|
|
22
22
|
|
|
23
|
-
If the user named a specific resource and type (`skill`, `agent`, or `
|
|
23
|
+
If the user named a specific resource and type (`skill`, `agent`, `squad`, or `project`), use those directly.
|
|
24
24
|
|
|
25
25
|
Otherwise, list available resources for the chosen type and present a pick list:
|
|
26
26
|
|
|
@@ -44,13 +44,17 @@ where `<slug>` is a lowercased, hyphenated form of the resource name.
|
|
|
44
44
|
|
|
45
45
|
```bash
|
|
46
46
|
node "${CLAUDE_PLUGIN_ROOT}/scripts/multica-export.mjs" \
|
|
47
|
-
--scope <
|
|
47
|
+
--scope <skill|agent|squad|project|projects|all> \
|
|
48
48
|
--id <id> \
|
|
49
49
|
--out <dir> \
|
|
50
50
|
[--workspace <workspace-name>]
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
`--id` is required for `skill`, `agent`, `squad`, and `project` (a single named resource); it is **not needed** for `projects` (every project in the workspace) or `all` (the entire workspace).
|
|
54
|
+
|
|
55
|
+
Pass `--scope all` (with no `--id`) to export the **entire workspace** — every skill, agent, squad, and project — into one flat, deduped bundle. A skill or agent shared across many agents/squads is written exactly once and referenced by name.
|
|
56
|
+
|
|
57
|
+
Exporting a project (or `projects`/`all`) also **bundles the project's lead agent** so the bundle is self-contained; projects carry metadata only (title, description, icon, priority, status, dates, lead mapping) plus their attached resource records — never issues. On import, only `github_repo` resources are portable and recreated; other resource types are reported and skipped.
|
|
54
58
|
|
|
55
59
|
The script writes `manifest.json`, skill `SKILL.md` files, agent JSON files, and squad JSON files into `<dir>`. Each agent's and squad's **instructions** (system prompt / charter) are written to a sibling Markdown file — `agents/<slug>.md`, `squads/<slug>.md` — referenced by an `instructions_file` key in the JSON, so the prose is easy to read, diff, and edit. Agents/squads with no instructions get no `.md`.
|
|
56
60
|
|
|
@@ -61,5 +65,6 @@ Avatars are captured automatically: an agent's uploaded-image avatar is download
|
|
|
61
65
|
Parse the JSON output from the script and report:
|
|
62
66
|
|
|
63
67
|
- Directory written to.
|
|
64
|
-
- Count of skills, agents, and
|
|
68
|
+
- Count of skills, agents, squads, and projects exported.
|
|
69
|
+
- 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).
|
|
65
70
|
- 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>`."
|
|
@@ -12,14 +12,36 @@ Import a local Multica bundle (produced by the export skill) into a target works
|
|
|
12
12
|
|
|
13
13
|
Ask the user to confirm the name of the target workspace if not already stated. You will need the exact workspace name as registered in Multica.
|
|
14
14
|
|
|
15
|
-
## Step 2 —
|
|
15
|
+
## Step 2 — Pre-flight (dry run)
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Before writing anything, run the import script with `--dry-run` to preview the bundle. Preview against the **full** set (`--include agents,squads,projects`) 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 cost before deciding whether to opt it in:
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
20
|
node "${CLAUDE_PLUGIN_ROOT}/scripts/multica-import.mjs" \
|
|
21
21
|
--dir <folder> \
|
|
22
|
-
--workspace <workspace-name>
|
|
22
|
+
--workspace <workspace-name> \
|
|
23
|
+
--include agents,squads,projects \
|
|
24
|
+
[--runtime-map <srcId1=dstId1,srcId2=dstId2,...>] \
|
|
25
|
+
--dry-run
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Present the `bundle` and `willImport` counts, and every entry in `incompatibilities`, to the user.
|
|
29
|
+
|
|
30
|
+
## Step 3 — Select which types to import
|
|
31
|
+
|
|
32
|
+
Ask the user which of `agents`, `squads`, `projects` they want to import. **Default is `agents,squads`** — `projects` requires explicit opt-in.
|
|
33
|
+
|
|
34
|
+
If the pre-flight's `incompatibilities` list contains an `unmapped-runtime` entry, tell the user it must be resolved with `--runtime-map` before the real import — the import **aborts** otherwise (see Step 4 below). Other incompatibility kinds are informational only and applied best-effort, fixed up afterward in the Multica UI: `priority-not-settable` (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), and `lead-agent-missing` (a non-agent lead isn't re-applied to the imported project).
|
|
35
|
+
|
|
36
|
+
## Step 4 — Run the import (auto-mapping first)
|
|
37
|
+
|
|
38
|
+
Each exported agent record carries its source runtime's `provider` (e.g. `claude`, `opencode`) alongside its ID. The import script auto-maps a source runtime to the target workspace's runtime when there is **exactly one** runtime of that provider there — no manual mapping needed in the common case. Try the import without `--runtime-map` first, passing the selected types from Step 3 via `--include`:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
node "${CLAUDE_PLUGIN_ROOT}/scripts/multica-import.mjs" \
|
|
42
|
+
--dir <folder> \
|
|
43
|
+
--workspace <workspace-name> \
|
|
44
|
+
--include <agents,squads,projects>
|
|
23
45
|
```
|
|
24
46
|
|
|
25
47
|
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:
|
|
@@ -35,6 +57,7 @@ Ask the user to pick a matching target runtime by name or ID for each unmapped `
|
|
|
35
57
|
node "${CLAUDE_PLUGIN_ROOT}/scripts/multica-import.mjs" \
|
|
36
58
|
--dir <folder> \
|
|
37
59
|
--workspace <workspace-name> \
|
|
60
|
+
--include <agents,squads,projects> \
|
|
38
61
|
--runtime-map <srcId1=dstId1,srcId2=dstId2,...>
|
|
39
62
|
```
|
|
40
63
|
|
|
@@ -44,14 +67,15 @@ Instructions are read back from each resource's sibling `.md` (`agents/<slug>.md
|
|
|
44
67
|
|
|
45
68
|
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.
|
|
46
69
|
|
|
47
|
-
## Step
|
|
70
|
+
## Step 5 — Report results
|
|
48
71
|
|
|
49
72
|
Parse the JSON output and report:
|
|
50
73
|
|
|
51
|
-
- Created and updated counts for skills, agents, and
|
|
74
|
+
- Created and updated counts for skills, agents, squads, and projects (`created.projects`/`updated.projects`).
|
|
52
75
|
- Name-to-ID maps for skills and agents (`skillIdMap`, `agentIdMap`).
|
|
53
76
|
- `squadIdMap`: name-to-ID map for every squad imported.
|
|
54
77
|
- `mentionsRewritten`: how many agents had an agent-mention link rewritten to its new id.
|
|
78
|
+
- If `leadUnresolved`, `priorityUnsupported`, `resourcesUnsupported`, or `squadsSkipped` is non-empty, surface each entry verbatim as an "applied best-effort; adjust in the UI" note — e.g. "NOTE: applied best-effort; adjust in the UI — `<entry>`."
|
|
55
79
|
- If `secretsReminder` is non-empty, surface every agent name verbatim with: "WARNING: the following agents' bundle files contained custom environment variables or MCP config in PLAINTEXT — the source export directory should be treated as sensitive: `<agent-name>`."
|
|
56
80
|
- If `secretsApplyFailures` is non-empty, surface every agent name verbatim with: "WARNING: mcp_config or custom_env failed to apply to the following agents during import (the agent itself was still created/updated) — set them manually in the Multica UI: `<agent-name>`."
|
|
57
81
|
- If `avatarApplyFailures` is non-empty, surface every agent name verbatim with: "WARNING: the avatar image failed to upload for the following agents — set it manually in the Multica UI: `<agent-name>`."
|
|
@@ -16,7 +16,9 @@ Expect the user's request in the form:
|
|
|
16
16
|
sync <type> <name> from <src-ws> to <dest-ws>
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
Where `<type>` is `skill`, `agent`, or `
|
|
19
|
+
Where `<type>` is `skill`, `agent`, `squad`, or `project`; `<name>` is the resource name (for `project`, its **title**); `<src-ws>` and `<dest-ws>` are workspace names registered in Multica.
|
|
20
|
+
|
|
21
|
+
Projects 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.
|
|
20
22
|
|
|
21
23
|
## Step 2 — Run the sync (auto-mapping first)
|
|
22
24
|
|
|
@@ -47,7 +49,7 @@ The script exports to a temporary directory, imports into the destination worksp
|
|
|
47
49
|
|
|
48
50
|
Parse the JSON output and report:
|
|
49
51
|
|
|
50
|
-
- Created and updated counts for skills, agents, and
|
|
52
|
+
- Created and updated counts for skills, agents, squads, and projects.
|
|
51
53
|
- Name-to-ID maps (`skillIdMap`, `agentIdMap`).
|
|
52
54
|
- `squadIdMap`: name-to-ID map for every squad synced.
|
|
53
55
|
- If `secretsReminder` is non-empty, surface every agent name verbatim with: "WARNING: the following agents' bundle files contained custom environment variables or MCP config in PLAINTEXT — the temporary export directory (already cleaned up) briefly held these secrets in plaintext: `<agent-name>`."
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plugin-validator",
|
|
3
3
|
"displayName": "Plugin Validator",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.41",
|
|
5
5
|
"description": "Orchestrated validator for Claude Code plugins — validates skills, agents, commands, and hooks across every plugin under plugins/**.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Steven Hoang"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "team-share",
|
|
3
3
|
"displayName": "Team Share",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.41",
|
|
5
5
|
"description": "Onboard your team with an interactive setup menu: install CodeGraph, build the Understand-Anything knowledge graph, and share Claude Code settings — run any combination, all idempotent.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Steven Hoang"
|