@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
|
@@ -8,40 +8,11 @@
|
|
|
8
8
|
"description": "Personal agents and skills marketplace"
|
|
9
9
|
},
|
|
10
10
|
"plugins": [
|
|
11
|
-
{
|
|
12
|
-
"name": "tech-graph",
|
|
13
|
-
"source": "./plugins/tech-graph",
|
|
14
|
-
"description": "6-step wizard for technical diagrams (SVG/PNG) via fireworks-tech-graph",
|
|
15
|
-
"version": "0.0.44",
|
|
16
|
-
"category": "diagram",
|
|
17
|
-
"keywords": [
|
|
18
|
-
"diagram",
|
|
19
|
-
"svg",
|
|
20
|
-
"architecture",
|
|
21
|
-
"uml",
|
|
22
|
-
"flowchart"
|
|
23
|
-
]
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
"name": "html-effectiveness",
|
|
27
|
-
"source": "./plugins/html-effectiveness",
|
|
28
|
-
"description": "Generate self-contained interactive HTML reports from 20 upstream templates via a conversational agent.",
|
|
29
|
-
"version": "0.0.44",
|
|
30
|
-
"category": "reports",
|
|
31
|
-
"keywords": [
|
|
32
|
-
"html",
|
|
33
|
-
"report",
|
|
34
|
-
"status",
|
|
35
|
-
"incident",
|
|
36
|
-
"slide-deck",
|
|
37
|
-
"decision-matrix"
|
|
38
|
-
]
|
|
39
|
-
},
|
|
40
11
|
{
|
|
41
12
|
"name": "plugin-validator",
|
|
42
13
|
"source": "./plugins/plugin-validator",
|
|
43
14
|
"description": "Orchestrated validator for Claude Code plugins — validates skills, agents, commands, and hooks across every plugin under plugins/**.",
|
|
44
|
-
"version": "0.0.
|
|
15
|
+
"version": "0.0.46",
|
|
45
16
|
"category": "tooling",
|
|
46
17
|
"keywords": [
|
|
47
18
|
"validation",
|
|
@@ -53,27 +24,11 @@
|
|
|
53
24
|
"hooks"
|
|
54
25
|
]
|
|
55
26
|
},
|
|
56
|
-
{
|
|
57
|
-
"name": "team-share",
|
|
58
|
-
"source": "./plugins/team-share",
|
|
59
|
-
"description": "Onboard your team with an interactive setup menu: install CodeGraph, build the Understand-Anything knowledge graph, and share Claude Code settings — run any combination, all idempotent.",
|
|
60
|
-
"version": "0.0.44",
|
|
61
|
-
"category": "tooling",
|
|
62
|
-
"keywords": [
|
|
63
|
-
"onboarding",
|
|
64
|
-
"team",
|
|
65
|
-
"claude",
|
|
66
|
-
"settings",
|
|
67
|
-
"codegraph",
|
|
68
|
-
"understand-anything",
|
|
69
|
-
"git-lfs"
|
|
70
|
-
]
|
|
71
|
-
},
|
|
72
27
|
{
|
|
73
28
|
"name": "multica-tool",
|
|
74
29
|
"source": "./plugins/multica-tool",
|
|
75
30
|
"description": "Export, import, and sync Multica skills, agents, and squads between workspaces.",
|
|
76
|
-
"version": "0.0.
|
|
31
|
+
"version": "0.0.46",
|
|
77
32
|
"category": "workflow",
|
|
78
33
|
"keywords": [
|
|
79
34
|
"multica",
|
package/README.md
CHANGED
|
@@ -8,11 +8,8 @@ Published on npm as [`@drunkcoding/agents-and-skills`](https://www.npmjs.com/pac
|
|
|
8
8
|
|
|
9
9
|
| Plugin | Description |
|
|
10
10
|
|--------|-------------|
|
|
11
|
-
| [`tech-graph`](plugins/tech-graph) | 6-step wizard for technical diagrams (SVG + PNG). |
|
|
12
|
-
| [`html-effectiveness`](plugins/html-effectiveness) | Conversational agent that generates self-contained interactive HTML reports from 20 templates. |
|
|
13
11
|
| [`team-superpower`](plugins/team-superpower) | Shape-adaptive engineering team running the Superpowers skill chain — up to 8 roles (designer, planner, software-architect, security-engineer, backend-developer, frontend-developer, qa-engineer, reviewer); spawns 7 or 8 depending on stack (`full-stack` / `be-only` / `fe-only`) declared in `CLAUDE.md`. Test/build commands, contract publish + sync, security checklist, and CI gate before merge are all driven by the project's `CLAUDE.md` `team-superpower` block. |
|
|
14
12
|
| [`plugin-validator`](plugins/plugin-validator) | Orchestrated validator that checks every plugin's skills, agents, commands, and hooks for spec compliance — runs in parallel and proposes batched fixes. |
|
|
15
|
-
| [`team-share`](plugins/team-share) | Onboarding helper that shares Claude plugin settings, scaffolds `CLAUDE.md` when missing, refreshes the Understand-Anything graph, and stages the resulting repo artifacts for review. |
|
|
16
13
|
| [`auto-power`](plugins/auto-power) | Single-command hands-off pipeline that wraps `obra/superpowers`. Auto-answers safe clarifying questions during spec, then runs plan → arch+sec → impl → verify → review → ff-merge with no further touchpoints. Checkpointed and resumable. Escalates on substantive failures. |
|
|
17
14
|
| [`multica-tool`](plugins/multica-tool) | Export, import, and sync Multica skills, agents, and squads between workspaces. |
|
|
18
15
|
|
|
@@ -52,8 +49,8 @@ Add the marketplace once:
|
|
|
52
49
|
Then install any plugin individually:
|
|
53
50
|
|
|
54
51
|
```text
|
|
55
|
-
/plugin install
|
|
56
|
-
/plugin install
|
|
52
|
+
/plugin install plugin-validator@drunkcoding
|
|
53
|
+
/plugin install multica-tool@drunkcoding --scope local
|
|
57
54
|
```
|
|
58
55
|
|
|
59
56
|
Reload after install:
|
|
@@ -69,7 +66,7 @@ Only the plugin you install loads; others stay dormant.
|
|
|
69
66
|
Install into a project's `.claude/` instead of your user profile:
|
|
70
67
|
|
|
71
68
|
```text
|
|
72
|
-
/plugin install
|
|
69
|
+
/plugin install plugin-validator@drunkcoding --scope project
|
|
73
70
|
```
|
|
74
71
|
|
|
75
72
|
#### Manage installed plugins
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@drunkcoding/agents-and-skills",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.46",
|
|
4
4
|
"description": "Personal collection of Claude Code skills and agents, installable via `npx skills`.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|
|
@@ -26,6 +26,9 @@
|
|
|
26
26
|
"README.md",
|
|
27
27
|
"LICENSE"
|
|
28
28
|
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build:opencode": "node plugins/multica-tool/scripts/build-dist.mjs"
|
|
31
|
+
},
|
|
29
32
|
"private": false,
|
|
30
33
|
"publishConfig": {
|
|
31
34
|
"access": "public"
|
|
@@ -59,6 +59,21 @@ export const listAgentsIncludingArchived = (cli) => cli.json(["agent", "list", "
|
|
|
59
59
|
export const listSquads = (cli) => cli.json(["squad", "list"]);
|
|
60
60
|
export const listWorkspaceMembers = (cli) => cli.json(["workspace", "member", "list"]);
|
|
61
61
|
|
|
62
|
+
// --- Workspace MCP server library ------------------------------------------
|
|
63
|
+
// `workspace mcp list` returns only id/name/transport — the server entry JSON
|
|
64
|
+
// (command/args/env, and any token inside it) is write-only: `workspace mcp
|
|
65
|
+
// add/update` take it, nothing reads it back. So the library travels as a
|
|
66
|
+
// NAME + TRANSPORT roster only, and import reports each entry as needing its
|
|
67
|
+
// config re-entered by hand rather than pretending it round-trips.
|
|
68
|
+
export const listWorkspaceMcpServers = (cli) =>
|
|
69
|
+
(cli.json(["workspace", "mcp", "list"]) ?? []).map((s) => ({ id: s.id, name: s.name, transport: s.transport }));
|
|
70
|
+
|
|
71
|
+
// Which workspace MCP servers an agent uses, and whether each is on. Keyed by
|
|
72
|
+
// NAME (the server id is re-minted per workspace); `agent mcp add/enable/disable`
|
|
73
|
+
// take a server id, which import resolves from the destination library by name.
|
|
74
|
+
export const listAgentMcpServers = (cli, agentId) =>
|
|
75
|
+
(cli.json(["agent", "mcp", "list", agentId]) ?? []).map((s) => ({ name: s.name, enabled: !!s.enabled }));
|
|
76
|
+
|
|
62
77
|
// Get-wrappers: the ONLY place that knows the raw CLI field names — an
|
|
63
78
|
// explicit allow-list, so unexpected/internal CLI fields never leak into a
|
|
64
79
|
// bundle. Field names mirror the CLI's own snake_case; nothing is renamed.
|
|
@@ -137,16 +152,19 @@ export const listAutopilots = (cli) => cli.json(["autopilot", "list"]).autopilot
|
|
|
137
152
|
|
|
138
153
|
// `autopilot get` wraps the record as {autopilot, collaborators, triggers} — only
|
|
139
154
|
// autopilot + triggers are portable; collaborators/can_write/can_manage_access are
|
|
140
|
-
// caller-relative and never bundled. `priority` is
|
|
141
|
-
//
|
|
142
|
-
//
|
|
155
|
+
// caller-relative and never bundled. `priority` is deliberately absent: the API
|
|
156
|
+
// never returns it on read, and as of multica 0.4.36 `autopilot create`/`update`
|
|
157
|
+
// no longer accept `--priority` either — so it can be neither captured nor
|
|
158
|
+
// restored. Import reports every autopilot under `priorityNotCaptured`.
|
|
143
159
|
export function getAutopilot(cli, id) {
|
|
144
160
|
const r = cli.json(["autopilot", "get", id]);
|
|
145
161
|
const a = r.autopilot;
|
|
146
162
|
return {
|
|
147
163
|
id: a.id, title: a.title, description: a.description ?? "",
|
|
164
|
+
// Source status (active|paused). Import never auto-activates — it is captured
|
|
165
|
+
// so the operator can be told which autopilots were live at the source.
|
|
166
|
+
status: a.status ?? null,
|
|
148
167
|
execution_mode: a.execution_mode, issue_title_template: a.issue_title_template ?? null,
|
|
149
|
-
priority: a.priority ?? null,
|
|
150
168
|
project_id: a.project_id ?? null,
|
|
151
169
|
assignee_id: a.assignee_id, assignee_type: a.assignee_type,
|
|
152
170
|
subscribers: (a.subscribers ?? []).map((s) => ({ user_id: s.user_id, user_type: s.user_type })),
|
|
@@ -156,3 +174,34 @@ export function getAutopilot(cli, id) {
|
|
|
156
174
|
})),
|
|
157
175
|
};
|
|
158
176
|
}
|
|
177
|
+
|
|
178
|
+
// --- Workspace-scoped issue taxonomy (multica 0.4.36) -----------------------
|
|
179
|
+
// Labels and custom properties are defined per WORKSPACE, not per project — the
|
|
180
|
+
// CLI has no project-scoped variant of either. They are bundled with project
|
|
181
|
+
// exports (and `all`) so a migrated project lands somewhere its issues can
|
|
182
|
+
// actually be labelled and filled in.
|
|
183
|
+
|
|
184
|
+
// `id` is kept: `label update` addresses a label by id only.
|
|
185
|
+
// `resource_type`/`usage_count`/timestamps are server-owned with no CLI setter.
|
|
186
|
+
// `description` IS captured, but `label create`/`update` expose no
|
|
187
|
+
// `--description` flag — import reports it as unrestorable rather than dropping
|
|
188
|
+
// it silently.
|
|
189
|
+
export const listLabels = (cli) =>
|
|
190
|
+
(cli.json(["label", "list"]) ?? []).map((l) => ({
|
|
191
|
+
id: l.id, name: l.name, color: l.color, description: l.description ?? "",
|
|
192
|
+
}));
|
|
193
|
+
|
|
194
|
+
// Archived definitions included so an archived property round-trips as archived
|
|
195
|
+
// instead of silently reappearing in every picker at the destination.
|
|
196
|
+
// No `id`: `property get/update/archive/unarchive` all accept `<id-or-name>`,
|
|
197
|
+
// and names are what issue values reference, so name is the portable key.
|
|
198
|
+
// `position`/`usage_count` are server-assigned and have no CLI setter.
|
|
199
|
+
export const listProperties = (cli) =>
|
|
200
|
+
(cli.json(["property", "list", "--include-archived"]) ?? []).map((p) => ({
|
|
201
|
+
name: p.name, type: p.type, description: p.description ?? "", icon: p.icon ?? "",
|
|
202
|
+
archived: !!p.archived,
|
|
203
|
+
// Only select/multi_select carry options; every other type reports null.
|
|
204
|
+
// Option ids are re-minted at the destination — `property update` re-matches
|
|
205
|
+
// existing options BY NAME, so name+color is the whole portable payload.
|
|
206
|
+
options: (p.config?.options ?? []).map((o) => ({ name: o.name, color: o.color ?? null })),
|
|
207
|
+
}));
|
|
@@ -1,10 +1,30 @@
|
|
|
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, listAgentsIncludingArchived, listSquads, listProjects, getProject, getProjectResources, listWorkspaceMembers, getAutopilot, makeCli, realExec, requireAuth, resolveWorkspaceId } from "./lib.mjs";
|
|
4
|
+
import { slugify, getSkill, getAgent, getAgentCustomEnv, getSquad, getSquadMembers, listRuntimes, listSkills, listAgents, listAgentsIncludingArchived, listSquads, listProjects, getProject, getProjectResources, listWorkspaceMembers, listAutopilots, getAutopilot, listLabels, listProperties, listWorkspaceMcpServers, listAgentMcpServers, makeCli, realExec, requireAuth, resolveWorkspaceId } from "./lib.mjs";
|
|
5
|
+
|
|
6
|
+
// Export levels for a WHOLE-WORKSPACE export, lowest tier first. A level pulls
|
|
7
|
+
// in its own tier plus every tier below it, so `project` is the full workspace
|
|
8
|
+
// and `skill` is skills alone. Single-resource exports (`--scope <type> --id`)
|
|
9
|
+
// ignore this and always bundle what that one resource needs.
|
|
10
|
+
export const LEVELS = ["skill", "agent", "squad", "project"];
|
|
11
|
+
export const levelRank = (level) => LEVELS.indexOf(level);
|
|
12
|
+
export const levelAtLeast = (level, tier) => levelRank(level) >= levelRank(tier);
|
|
5
13
|
|
|
6
14
|
const nonEmpty = (v) => v && typeof v === "object" && Object.keys(v).length > 0;
|
|
7
15
|
|
|
16
|
+
// Externalize a prose field to a sibling .md next to `jsonRel`
|
|
17
|
+
// (e.g. agents/x.json + ".description.md" → agents/x.description.md), and record
|
|
18
|
+
// the `<field>_file` pointer on `record`. Empty prose writes nothing and adds no
|
|
19
|
+
// key — same sibling-file pattern as avatar_file, applied uniformly to every
|
|
20
|
+
// resource's instructions/description so no prose is ever embedded in the JSON.
|
|
21
|
+
function writeSidecar(fs, outDir, jsonRel, suffix, text, record, key) {
|
|
22
|
+
if (!text) return;
|
|
23
|
+
const rel = jsonRel.replace(/\.json$/, suffix);
|
|
24
|
+
fs.writeFileSync(`${outDir}/${rel}`, text);
|
|
25
|
+
record[key] = rel;
|
|
26
|
+
}
|
|
27
|
+
|
|
8
28
|
// An avatar_url is either an uploaded-image URL (http[s]://…) or an inline
|
|
9
29
|
// "emoji:🦍" marker. Only image URLs carry bytes worth bundling; emoji markers
|
|
10
30
|
// are just carried as strings in the record.
|
|
@@ -31,7 +51,7 @@ export function redactAgent(a) {
|
|
|
31
51
|
// a is a normalized agent from getAgent, with `custom_env`/
|
|
32
52
|
// `custom_env_fetch_failed` attached by the caller (collectAgent) — getAgent
|
|
33
53
|
// itself never fetches custom_env, since it requires a separate audited call.
|
|
34
|
-
const { id, has_custom_env, mcp_config_redacted, custom_env_fetch_failed, mcp_config, custom_env, skills, runtime_id, instructions, archived_at, ...rest } = a;
|
|
54
|
+
const { id, has_custom_env, mcp_config_redacted, custom_env_fetch_failed, mcp_config, custom_env, skills, runtime_id, instructions, description, archived_at, ...rest } = a;
|
|
35
55
|
const mcpUsable = !mcp_config_redacted && nonEmpty(mcp_config);
|
|
36
56
|
const envUsable = !custom_env_fetch_failed && nonEmpty(custom_env);
|
|
37
57
|
// mcp_config_redacted / custom_env_fetch_failed alone still flag hadSecrets even
|
|
@@ -51,13 +71,14 @@ export function redactAgent(a) {
|
|
|
51
71
|
had_secrets: hadSecrets,
|
|
52
72
|
},
|
|
53
73
|
hadSecrets,
|
|
54
|
-
// instructions are written to
|
|
55
|
-
// never embedded in the JSON record.
|
|
74
|
+
// instructions and description are written to sibling .md files by the caller
|
|
75
|
+
// (see avatar_file), never embedded in the JSON record.
|
|
56
76
|
instructions: instructions ?? "",
|
|
77
|
+
description: description ?? "",
|
|
57
78
|
};
|
|
58
79
|
}
|
|
59
80
|
|
|
60
|
-
export function buildManifest({ scope, sourceWorkspaceId, skills, agents, squads, projects, autopilots }) {
|
|
81
|
+
export function buildManifest({ scope, level, sourceWorkspaceId, skills, agents, squads, projects, autopilots, labels, properties, mcpServers }) {
|
|
61
82
|
const seenSkills = new Map();
|
|
62
83
|
for (const s of skills) if (!seenSkills.has(s.name)) seenSkills.set(s.name, s);
|
|
63
84
|
const seenAgents = new Map();
|
|
@@ -67,14 +88,18 @@ export function buildManifest({ scope, sourceWorkspaceId, skills, agents, squads
|
|
|
67
88
|
return {
|
|
68
89
|
version: "1",
|
|
69
90
|
scope,
|
|
91
|
+
// null for a single-resource export; the requested tier for a workspace export.
|
|
92
|
+
level: level ?? null,
|
|
70
93
|
source_workspace_id: sourceWorkspaceId,
|
|
71
94
|
skills: [...seenSkills.values()].map((s) => ({ name: s.name, dir: `skills/${slugify(s.name)}`, source_id: s.source_id })),
|
|
72
95
|
agents: [...seenAgents.values()].map((a) => ({ name: a.name, file: `agents/${slugify(a.name)}.json`, source_id: a.source_id, source_runtime_id: a.source_runtime_id, source_runtime_provider: a.source_runtime_provider ?? null, skill_names: a.skill_names, had_secrets: !!a.had_secrets })),
|
|
73
96
|
squads: (squads ?? []).map((squad) => {
|
|
74
97
|
const file = `squads/${slugify(squad.name)}.json`;
|
|
75
|
-
const entry = { name: squad.name, file,
|
|
76
|
-
// Instructions go to
|
|
98
|
+
const entry = { name: squad.name, file, avatar_url: squad.avatar_url ?? null, leader_name: squad.leader_name, members: squad.members };
|
|
99
|
+
// Instructions and description go to sibling .md files (see squad write
|
|
100
|
+
// loop); only referenced when non-empty.
|
|
77
101
|
if (squad.instructions) entry.instructions_file = file.replace(/\.json$/, ".md");
|
|
102
|
+
if (squad.description) entry.description_file = file.replace(/\.json$/, ".description.md");
|
|
78
103
|
return entry;
|
|
79
104
|
}),
|
|
80
105
|
projects: [...seenProjects.values()].map((p) => ({
|
|
@@ -86,6 +111,30 @@ export function buildManifest({ scope, sourceWorkspaceId, skills, agents, squads
|
|
|
86
111
|
assignee_type: ap.assignee_type, assignee_name: ap.assignee_name,
|
|
87
112
|
project_title: ap.project_title, had_webhook_trigger: ap.had_webhook_trigger,
|
|
88
113
|
})),
|
|
114
|
+
// Labels, properties and the workspace MCP roster are workspace-wide sets
|
|
115
|
+
// with no per-resource file to point at, so each gets its own flat folder
|
|
116
|
+
// (labels/, properties/, mcp/) and the manifest carries only a pointer plus
|
|
117
|
+
// a count. Import reads the pointer, falling back to a legacy bundle's
|
|
118
|
+
// inline array. `id` is dropped from labels: both labels and properties are
|
|
119
|
+
// matched by NAME at the destination.
|
|
120
|
+
labels_file: (labels ?? []).length ? "labels/labels.json" : null,
|
|
121
|
+
labels_count: (labels ?? []).length,
|
|
122
|
+
properties_file: (properties ?? []).length ? "properties/properties.json" : null,
|
|
123
|
+
properties_count: (properties ?? []).length,
|
|
124
|
+
mcp_servers_file: (mcpServers ?? []).length ? "mcp/servers.json" : null,
|
|
125
|
+
mcp_servers_count: (mcpServers ?? []).length,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// The payloads written to labels/, properties/ and mcp/. Kept next to
|
|
130
|
+
// buildManifest so the manifest pointers and the files they name can't drift.
|
|
131
|
+
export function buildTaxonomyFiles({ labels, properties, mcpServers }) {
|
|
132
|
+
return {
|
|
133
|
+
"labels/labels.json": (labels ?? []).map(({ id, ...rest }) => rest),
|
|
134
|
+
"properties/properties.json": properties ?? [],
|
|
135
|
+
// The server entry JSON is not readable via the CLI — only name/transport
|
|
136
|
+
// travel, so import re-creates the roster as a to-do list, not a copy.
|
|
137
|
+
"mcp/servers.json": (mcpServers ?? []).map(({ id, ...rest }) => rest),
|
|
89
138
|
};
|
|
90
139
|
}
|
|
91
140
|
|
|
@@ -115,6 +164,13 @@ function collectAgent(cli, id, agentsById, skills, providerById) {
|
|
|
115
164
|
a.custom_env_fetch_failed = true; // e.g. insufficient permission — non-fatal, warned via hadSecrets
|
|
116
165
|
}
|
|
117
166
|
}
|
|
167
|
+
// Workspace MCP servers assigned to this agent (name + enabled). Separate from
|
|
168
|
+
// `mcp_config`, which is the agent's own inline server JSON.
|
|
169
|
+
try {
|
|
170
|
+
a.mcp_servers = listAgentMcpServers(cli, id);
|
|
171
|
+
} catch {
|
|
172
|
+
a.mcp_servers = []; // e.g. insufficient permission — non-fatal
|
|
173
|
+
}
|
|
118
174
|
const skill_names = a.skills.map((sk) => collectSkill(cli, sk.id, skills));
|
|
119
175
|
const red = redactAgent(a);
|
|
120
176
|
const entry = { raw: a, red, skill_names };
|
|
@@ -122,7 +178,7 @@ function collectAgent(cli, id, agentsById, skills, providerById) {
|
|
|
122
178
|
return entry;
|
|
123
179
|
}
|
|
124
180
|
|
|
125
|
-
export function exportResource({ cli, scope, ids, outDir, sourceWorkspaceId, fs = nodeFs, download = fetchBinary }) {
|
|
181
|
+
export function exportResource({ cli, scope, level, ids, outDir, sourceWorkspaceId, fs = nodeFs, download = fetchBinary }) {
|
|
126
182
|
const skills = new Map(); // name -> normalized skill
|
|
127
183
|
const agentsById = new Map(); // id -> { raw, red, skill_names }
|
|
128
184
|
const squads = [];
|
|
@@ -169,6 +225,12 @@ export function exportResource({ cli, scope, ids, outDir, sourceWorkspaceId, fs
|
|
|
169
225
|
};
|
|
170
226
|
}
|
|
171
227
|
|
|
228
|
+
// A squad can be reached twice in one export (listed at level `squad`, then
|
|
229
|
+
// again as an autopilot's assignee) — keep exactly one copy, by name.
|
|
230
|
+
function pushSquad(built) {
|
|
231
|
+
if (built && !squads.some((s) => s.name === built.name)) squads.push(built);
|
|
232
|
+
}
|
|
233
|
+
|
|
172
234
|
// Collect a project's portable metadata + its lead agent (bundled, like a
|
|
173
235
|
// squad leader) and github_repo/other resources. An archived lead is dropped
|
|
174
236
|
// (project still exports, just with no lead set) — unlike a squad, a project
|
|
@@ -193,19 +255,26 @@ export function exportResource({ cli, scope, ids, outDir, sourceWorkspaceId, fs
|
|
|
193
255
|
};
|
|
194
256
|
}
|
|
195
257
|
|
|
196
|
-
// Collect a single autopilot
|
|
197
|
-
//
|
|
258
|
+
// Collect a single autopilot. Its assignee is BUNDLED (like a squad leader or a
|
|
259
|
+
// project lead) so the bundle is self-contained — import resolves the assignee
|
|
260
|
+
// by name, which only works if that agent/squad travels with it or already
|
|
261
|
+
// exists at the destination.
|
|
198
262
|
// Webhook trigger secrets (url/token) are never read into the bundle — only
|
|
199
263
|
// kind/label/enabled, matching the existing agent MCP/env redaction pattern.
|
|
200
264
|
function collectOneAutopilot(autopilotId) {
|
|
201
265
|
const ap = getAutopilot(cli, autopilotId);
|
|
202
266
|
let assignee_name = null;
|
|
203
267
|
if (ap.assignee_type === "agent") {
|
|
204
|
-
|
|
205
|
-
|
|
268
|
+
try {
|
|
269
|
+
const entry = collectAgent(cli, ap.assignee_id, agentsById, skills, getProviderById());
|
|
270
|
+
if (entry.archived) recordArchivedSkip(entry.name, "autopilot assignee", ap.title);
|
|
271
|
+
else assignee_name = entry.raw.name;
|
|
272
|
+
} catch { assignee_name = null; }
|
|
206
273
|
} else if (ap.assignee_type === "squad") {
|
|
207
|
-
|
|
208
|
-
|
|
274
|
+
try {
|
|
275
|
+
const built = collectOneSquad(ap.assignee_id);
|
|
276
|
+
if (built) { pushSquad(built); assignee_name = built.name; }
|
|
277
|
+
} catch { assignee_name = null; }
|
|
209
278
|
}
|
|
210
279
|
let project_title = null;
|
|
211
280
|
if (ap.project_id) {
|
|
@@ -218,6 +287,7 @@ export function exportResource({ cli, scope, ids, outDir, sourceWorkspaceId, fs
|
|
|
218
287
|
}
|
|
219
288
|
return {
|
|
220
289
|
title: ap.title, source_id: ap.id, description: ap.description,
|
|
290
|
+
status: ap.status,
|
|
221
291
|
execution_mode: ap.execution_mode, issue_title_template: ap.issue_title_template,
|
|
222
292
|
priority: ap.priority, project_title, assignee_type: ap.assignee_type, assignee_name,
|
|
223
293
|
subscriber_names, had_webhook_trigger: ap.triggers.some((t) => t.kind === "webhook"),
|
|
@@ -227,33 +297,51 @@ export function exportResource({ cli, scope, ids, outDir, sourceWorkspaceId, fs
|
|
|
227
297
|
};
|
|
228
298
|
}
|
|
229
299
|
|
|
300
|
+
// Labels, custom properties and the workspace MCP roster are workspace-wide,
|
|
301
|
+
// not per-project — three cheap list calls, bundled only where issue-bearing
|
|
302
|
+
// work travels: a single `project` export, or a workspace export at level
|
|
303
|
+
// `project`. Never for a lone skill/agent/squad/autopilot.
|
|
304
|
+
const wantsTaxonomy = scope === "project" || (scope === "workspace" && levelAtLeast(level, "project"));
|
|
305
|
+
const labels = wantsTaxonomy ? listLabels(cli) : [];
|
|
306
|
+
const properties = wantsTaxonomy ? listProperties(cli) : [];
|
|
307
|
+
const mcpServers = wantsTaxonomy ? listWorkspaceMcpServers(cli) : [];
|
|
308
|
+
|
|
230
309
|
if (scope === "skill") collectSkill(cli, ids.skillId, skills);
|
|
231
310
|
else if (scope === "agent") {
|
|
232
311
|
const entry = collectAgent(cli, ids.agentId, agentsById, skills, getProviderById());
|
|
233
312
|
if (entry.archived) throw new Error(`Cannot export agent "${entry.name}": it is archived`);
|
|
234
313
|
}
|
|
235
|
-
else if (scope === "squad")
|
|
314
|
+
else if (scope === "squad") pushSquad(collectOneSquad(ids.squadId));
|
|
236
315
|
else if (scope === "project") projects.push(collectProject(ids.projectId));
|
|
237
|
-
else if (scope === "projects") for (const p of listProjects(cli)) projects.push(collectProject(p.id));
|
|
238
316
|
else if (scope === "autopilot") autopilots.push(collectOneAutopilot(ids.autopilotId));
|
|
239
|
-
else if (scope === "
|
|
317
|
+
else if (scope === "workspace") {
|
|
318
|
+
// Tiers are cumulative: every level bundles skills; `agent` and up add every
|
|
319
|
+
// agent; `squad` and up add every squad; `project` adds projects + autopilots
|
|
320
|
+
// (plus the taxonomy above).
|
|
240
321
|
for (const s of listSkills(cli)) collectSkill(cli, s.id, skills);
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
322
|
+
if (levelAtLeast(level, "agent")) {
|
|
323
|
+
for (const a of listAgents(cli)) collectAgent(cli, a.id, agentsById, skills, getProviderById());
|
|
324
|
+
// listAgents() above already excludes archived agents server-side — surface
|
|
325
|
+
// that exclusion in the report too, not just the squad/project paths.
|
|
326
|
+
for (const a of listAgentsIncludingArchived(cli)) {
|
|
327
|
+
if (a.archived_at) recordArchivedSkip(a.name, "workspace listing");
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (levelAtLeast(level, "squad")) {
|
|
331
|
+
for (const sq of listSquads(cli)) pushSquad(collectOneSquad(sq.id));
|
|
332
|
+
}
|
|
333
|
+
if (levelAtLeast(level, "project")) {
|
|
334
|
+
for (const p of listProjects(cli)) projects.push(collectProject(p.id));
|
|
335
|
+
for (const ap of listAutopilots(cli)) autopilots.push(collectOneAutopilot(ap.id));
|
|
248
336
|
}
|
|
249
337
|
}
|
|
250
338
|
|
|
251
339
|
// Orphan-skill cleanup: drop skills that no exported agent references via its
|
|
252
|
-
// skill_names.
|
|
253
|
-
//
|
|
254
|
-
//
|
|
340
|
+
// skill_names. Skipped for `scope: skill` (its one skill IS the target, not an
|
|
341
|
+
// orphan) and for a workspace export at level `skill`, where the skills tier is
|
|
342
|
+
// itself what was asked for.
|
|
255
343
|
const pruned_skills = [];
|
|
256
|
-
if (scope !== "skill") {
|
|
344
|
+
if (scope !== "skill" && !(scope === "workspace" && level === "skill")) {
|
|
257
345
|
const referenced = new Set();
|
|
258
346
|
for (const a of agentsById.values()) for (const n of a.skill_names) referenced.add(n);
|
|
259
347
|
for (const name of [...skills.keys()]) {
|
|
@@ -262,12 +350,15 @@ export function exportResource({ cli, scope, ids, outDir, sourceWorkspaceId, fs
|
|
|
262
350
|
}
|
|
263
351
|
|
|
264
352
|
const manifest = buildManifest({
|
|
265
|
-
scope, sourceWorkspaceId,
|
|
353
|
+
scope, level, sourceWorkspaceId,
|
|
266
354
|
skills: [...skills.values()].map((s) => ({ name: s.name, source_id: s.id })),
|
|
267
355
|
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 })),
|
|
268
356
|
squads,
|
|
269
357
|
projects,
|
|
270
358
|
autopilots,
|
|
359
|
+
labels,
|
|
360
|
+
properties,
|
|
361
|
+
mcpServers,
|
|
271
362
|
});
|
|
272
363
|
|
|
273
364
|
const warnings = [];
|
|
@@ -304,37 +395,57 @@ export function exportResource({ cli, scope, ids, outDir, sourceWorkspaceId, fs
|
|
|
304
395
|
record.avatar_file = rel;
|
|
305
396
|
}
|
|
306
397
|
}
|
|
307
|
-
// Instructions live in a sibling .md for reviewability
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
const rel = entry.file.replace(/\.json$/, ".md");
|
|
311
|
-
fs.writeFileSync(`${outDir}/${rel}`, red.instructions);
|
|
312
|
-
record.instructions_file = rel;
|
|
313
|
-
}
|
|
398
|
+
// Instructions and description each live in a sibling .md for reviewability.
|
|
399
|
+
writeSidecar(fs, outDir, entry.file, ".md", red.instructions, record, "instructions_file");
|
|
400
|
+
writeSidecar(fs, outDir, entry.file, ".description.md", red.description, record, "description_file");
|
|
314
401
|
fs.writeFileSync(`${outDir}/${entry.file}`, JSON.stringify(record, null, 2));
|
|
315
402
|
}
|
|
316
|
-
const
|
|
403
|
+
const squadByName = new Map(squads.map((s) => [s.name, s]));
|
|
317
404
|
for (const entry of manifest.squads) {
|
|
318
405
|
fs.mkdirSync(`${outDir}/squads`, { recursive: true });
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
}
|
|
406
|
+
const s = squadByName.get(entry.name);
|
|
407
|
+
if (entry.instructions_file) fs.writeFileSync(`${outDir}/${entry.instructions_file}`, s?.instructions ?? "");
|
|
408
|
+
if (entry.description_file) fs.writeFileSync(`${outDir}/${entry.description_file}`, s?.description ?? "");
|
|
322
409
|
fs.writeFileSync(`${outDir}/${entry.file}`, JSON.stringify(entry, null, 2));
|
|
323
410
|
}
|
|
324
411
|
const projectByTitle = new Map(projects.map((p) => [p.title, p]));
|
|
325
412
|
for (const entry of manifest.projects) {
|
|
326
413
|
fs.mkdirSync(`${outDir}/projects`, { recursive: true });
|
|
327
|
-
|
|
414
|
+
const record = { ...projectByTitle.get(entry.title) };
|
|
415
|
+
const desc = record.description; delete record.description;
|
|
416
|
+
writeSidecar(fs, outDir, entry.file, ".description.md", desc, record, "description_file");
|
|
417
|
+
fs.writeFileSync(`${outDir}/${entry.file}`, JSON.stringify(record, null, 2));
|
|
328
418
|
}
|
|
329
419
|
const autopilotByTitle = new Map(autopilots.map((a) => [a.title, a]));
|
|
330
420
|
for (const entry of manifest.autopilots) {
|
|
331
421
|
fs.mkdirSync(`${outDir}/autopilots`, { recursive: true });
|
|
332
|
-
|
|
422
|
+
const record = { ...autopilotByTitle.get(entry.title) };
|
|
423
|
+
const desc = record.description; delete record.description;
|
|
424
|
+
writeSidecar(fs, outDir, entry.file, ".description.md", desc, record, "description_file");
|
|
425
|
+
fs.writeFileSync(`${outDir}/${entry.file}`, JSON.stringify(record, null, 2));
|
|
426
|
+
}
|
|
427
|
+
// labels/, properties/, mcp/ — one flat folder per object type, matching the
|
|
428
|
+
// per-type folders written above. Only written when the manifest points at them.
|
|
429
|
+
const taxonomyFiles = buildTaxonomyFiles({ labels, properties, mcpServers });
|
|
430
|
+
for (const rel of [manifest.labels_file, manifest.properties_file, manifest.mcp_servers_file]) {
|
|
431
|
+
if (!rel) continue;
|
|
432
|
+
fs.mkdirSync(`${outDir}/${dirname(rel)}`, { recursive: true });
|
|
433
|
+
fs.writeFileSync(`${outDir}/${rel}`, JSON.stringify(taxonomyFiles[rel], null, 2));
|
|
333
434
|
}
|
|
334
435
|
fs.writeFileSync(`${outDir}/manifest.json`, JSON.stringify(manifest, null, 2));
|
|
335
436
|
return {
|
|
336
437
|
manifest, warnings, pruned_skills, archivedAgentsSkipped,
|
|
438
|
+
// Autopilots that were live at the source. Import always lands them paused,
|
|
439
|
+
// so activation is a deliberate manual step at the destination.
|
|
440
|
+
autopilotsActiveAtSource: autopilots.filter((a) => a.status === "active").map((a) => a.title),
|
|
441
|
+
// Only name/transport are readable via `workspace mcp list` — the server
|
|
442
|
+
// entry JSON (and any token in it) must be re-entered at the destination.
|
|
443
|
+
mcpServerConfigsNotPortable: mcpServers.map((m) => m.name),
|
|
337
444
|
autopilotWebhookTriggers: autopilots.filter((a) => a.had_webhook_trigger).map((a) => a.title),
|
|
445
|
+
// Captured in the bundle for review, but `label create`/`update` have no
|
|
446
|
+
// --description flag — flagged at export time so the gap is known before the
|
|
447
|
+
// migration, not discovered at the far end.
|
|
448
|
+
labelDescriptionsNotPortable: labels.filter((l) => l.description).map((l) => l.name),
|
|
338
449
|
};
|
|
339
450
|
}
|
|
340
451
|
|
|
@@ -342,14 +453,24 @@ function main() {
|
|
|
342
453
|
const args = process.argv.slice(2);
|
|
343
454
|
const get = (flag) => { const i = args.indexOf(flag); return i !== -1 ? args[i + 1] : null; };
|
|
344
455
|
|
|
345
|
-
const
|
|
456
|
+
const rawScope = get("--scope");
|
|
457
|
+
const rawLevel = get("--level");
|
|
346
458
|
const id = get("--id");
|
|
347
459
|
const out = get("--out");
|
|
348
460
|
const workspace = get("--workspace"); // optional: source workspace name
|
|
349
461
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
462
|
+
const USAGE = "Usage: multica-export.mjs --out <dir> [--level skill|agent|squad|project] [--workspace <name>]\n" +
|
|
463
|
+
" or: multica-export.mjs --scope <skill|agent|squad|project|autopilot> --id <id> --out <dir> [--workspace <name>]\n" +
|
|
464
|
+
" --level exports the WHOLE workspace down to that tier (default: squad); --scope exports one named resource.";
|
|
465
|
+
|
|
466
|
+
// No --scope means a whole-workspace export driven by --level (default squad).
|
|
467
|
+
const scope = rawScope ?? "workspace";
|
|
468
|
+
const level = scope === "workspace" ? (rawLevel ?? "squad") : null;
|
|
469
|
+
|
|
470
|
+
if (rawScope && rawLevel) { console.error("--scope and --level are mutually exclusive.\n" + USAGE); process.exit(1); }
|
|
471
|
+
if (!out || (scope !== "workspace" && !id)) { console.error(USAGE); process.exit(1); }
|
|
472
|
+
if (scope === "workspace" && !LEVELS.includes(level)) {
|
|
473
|
+
console.error(`Unknown level "${level}" — use ${LEVELS.join("|")}`); process.exit(1);
|
|
353
474
|
}
|
|
354
475
|
|
|
355
476
|
requireAuth(realExec);
|
|
@@ -364,12 +485,11 @@ function main() {
|
|
|
364
485
|
else if (scope === "agent") ids.agentId = id;
|
|
365
486
|
else if (scope === "squad") ids.squadId = id;
|
|
366
487
|
else if (scope === "project") ids.projectId = id;
|
|
367
|
-
else if (scope === "projects") { /* all projects — no id */ }
|
|
368
488
|
else if (scope === "autopilot") ids.autopilotId = id;
|
|
369
|
-
else if (scope === "
|
|
370
|
-
else { console.error(`Unknown scope "${scope}" — use skill|agent|squad|project|
|
|
489
|
+
else if (scope === "workspace") { /* whole workspace — no id */ }
|
|
490
|
+
else { console.error(`Unknown scope "${scope}" — use skill|agent|squad|project|autopilot`); process.exit(1); }
|
|
371
491
|
|
|
372
|
-
const result = exportResource({ cli, scope, ids, outDir: out, sourceWorkspaceId, fs: nodeFs });
|
|
492
|
+
const result = exportResource({ cli, scope, level, ids, outDir: out, sourceWorkspaceId, fs: nodeFs });
|
|
373
493
|
console.log(JSON.stringify(result, null, 2));
|
|
374
494
|
}
|
|
375
495
|
|