@hegemonart/get-design-done 1.28.6 → 1.28.8
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 -2
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +81 -0
- package/README.de.md +14 -0
- package/README.fr.md +14 -0
- package/README.it.md +14 -0
- package/README.ja.md +14 -0
- package/README.ko.md +14 -0
- package/README.md +18 -0
- package/README.zh-CN.md +14 -0
- package/SKILL.md +10 -10
- package/package.json +3 -1
- package/scripts/build-distribution-bundles.cjs +549 -0
- package/scripts/install.cjs +68 -0
- package/scripts/lib/install/config-dir.cjs +26 -0
- package/scripts/lib/install/converters/antigravity.cjs +48 -0
- package/scripts/lib/install/converters/augment.cjs +68 -0
- package/scripts/lib/install/converters/cline.cjs +206 -0
- package/scripts/lib/install/converters/codebuddy.cjs +55 -0
- package/scripts/lib/install/converters/codex-plugin.cjs +407 -0
- package/scripts/lib/install/converters/codex.cjs +61 -0
- package/scripts/lib/install/converters/copilot.cjs +47 -0
- package/scripts/lib/install/converters/cursor-marketplace.cjs +309 -0
- package/scripts/lib/install/converters/cursor.cjs +49 -0
- package/scripts/lib/install/converters/gemini.cjs +116 -0
- package/scripts/lib/install/converters/kilo.cjs +62 -0
- package/scripts/lib/install/converters/opencode.cjs +64 -0
- package/scripts/lib/install/converters/qwen.cjs +51 -0
- package/scripts/lib/install/converters/shared.cjs +377 -0
- package/scripts/lib/install/converters/trae.cjs +47 -0
- package/scripts/lib/install/converters/windsurf.cjs +47 -0
- package/scripts/lib/install/doctor-codex-plugin.cjs +388 -0
- package/scripts/lib/install/doctor-cursor-marketplace.cjs +366 -0
- package/scripts/lib/install/doctor-tier2.cjs +586 -0
- package/scripts/lib/install/installer.cjs +529 -47
- package/scripts/lib/install/merge.cjs +31 -1
- package/scripts/lib/install/runtime-artifact-layout.cjs +431 -0
- package/scripts/lib/install/runtime-homes.cjs +225 -0
- package/scripts/lib/install/runtime-slash.cjs +172 -0
- package/scripts/lib/install/runtimes.cjs +73 -32
- package/scripts/lint-agentskills-spec.cjs +457 -0
- package/skills/compare/SKILL.md +2 -2
- package/skills/compare/compare-rubric.md +1 -1
- package/skills/darkmode/SKILL.md +2 -2
- package/skills/darkmode/darkmode-audit-procedure.md +1 -1
- package/skills/figma-write/SKILL.md +2 -2
- package/skills/graphify/SKILL.md +2 -2
- package/skills/style/SKILL.md +2 -2
- package/skills/style/style-doc-procedure.md +1 -1
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* scripts/lib/install/converters/cursor-marketplace.cjs — Phase 28.8 (Plan B1).
|
|
5
|
+
*
|
|
6
|
+
* Cursor Marketplace Tier-2 distribution-channel converter. SEPARATE from
|
|
7
|
+
* Phase 28.7's `cursor.cjs` SKILL.md file-drop converter — that one rewrites
|
|
8
|
+
* per-skill content for the runtime install path. THIS one builds the
|
|
9
|
+
* `.cursor-plugin/plugin.json` manifest and emits the marketplace bundle
|
|
10
|
+
* layout consumed by `build-distribution-bundles.cjs` (Plan 28-8-X1).
|
|
11
|
+
*
|
|
12
|
+
* Architecture note: per CONTEXT D-05 (additive), Tier-1 file-drop and
|
|
13
|
+
* Tier-2 marketplace coexist — `cursor.cjs` is UNCHANGED. Per CONTEXT D-06,
|
|
14
|
+
* `skills/` is the SHARED source; this converter passes skill content
|
|
15
|
+
* through verbatim (Cursor accepts Claude-compatible SKILL.md per Wave A
|
|
16
|
+
* research, so no per-skill content transform is required at the Tier-2
|
|
17
|
+
* bundle layer).
|
|
18
|
+
*
|
|
19
|
+
* Source mapping: see `.planning/research/cursor-marketplace-2026-05-19.md`
|
|
20
|
+
* § Schema Mapping (lines 234-256) for the authoritative field-by-field spec.
|
|
21
|
+
*
|
|
22
|
+
* GDD-original pattern (no gsd-build/get-shit-done counterpart): Tier-2
|
|
23
|
+
* distribution channels do not exist in the upstream multi-runtime install
|
|
24
|
+
* reference (CONTEXT line 34). No port attribution required.
|
|
25
|
+
*
|
|
26
|
+
* Exports:
|
|
27
|
+
* - `buildManifest(sources, opts)` — pure function, returns the manifest
|
|
28
|
+
* object ready to `JSON.stringify(obj, null, 2)`.
|
|
29
|
+
* - `convert({ skillsDir, outDir, manifest }, opts)` — file-emission
|
|
30
|
+
* function for `build-distribution-bundles.cjs`. The only side-effect
|
|
31
|
+
* surface; touches only paths under `outDir`.
|
|
32
|
+
* - `CURATED_KEYWORDS` — frozen 8-tag default keyword subset.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
const fs = require('node:fs');
|
|
36
|
+
const path = require('node:path');
|
|
37
|
+
|
|
38
|
+
// Curated keyword subset for Cursor's marketplace card display.
|
|
39
|
+
// Per Wave A research § Schema Mapping `keywords` row: marketplace card
|
|
40
|
+
// surfaces ~5-8 tags — picking the most Cursor-user-relevant subset out
|
|
41
|
+
// of the 60+ tags in package.json.keywords.
|
|
42
|
+
const CURATED_KEYWORDS = Object.freeze([
|
|
43
|
+
'design',
|
|
44
|
+
'ui',
|
|
45
|
+
'ux',
|
|
46
|
+
'frontend',
|
|
47
|
+
'design-system',
|
|
48
|
+
'accessibility',
|
|
49
|
+
'figma',
|
|
50
|
+
'skill',
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Build the .cursor-plugin/plugin.json manifest object from GDD source
|
|
55
|
+
* artifacts. Pure function — no fs, env, or path access.
|
|
56
|
+
*
|
|
57
|
+
* Field-by-field source mapping per Wave A research § Schema Mapping:
|
|
58
|
+
*
|
|
59
|
+
* name ← claudePluginJson.name (canonical, kebab-case)
|
|
60
|
+
* description ← packageJson.description (verbatim)
|
|
61
|
+
* version ← packageJson.version (verbatim, lockstep per D-08)
|
|
62
|
+
* author ← {name: claudePluginJson.author.name} (transform)
|
|
63
|
+
* homepage ← packageJson.homepage (verbatim, omit if absent)
|
|
64
|
+
* repository ← packageJson.repository.url with trailing .git stripped
|
|
65
|
+
* license ← packageJson.license (verbatim, omit if absent)
|
|
66
|
+
* keywords ← opts.keywords || CURATED_KEYWORDS
|
|
67
|
+
*
|
|
68
|
+
* OMITTED (per research § Schema Mapping rationale):
|
|
69
|
+
* logo, rules, agents, skills, commands, hooks, mcpServers
|
|
70
|
+
*
|
|
71
|
+
* @param {Object} sources Source metadata.
|
|
72
|
+
* @param {Object} sources.packageJson Parsed package.json.
|
|
73
|
+
* @param {Object} [sources.claudePluginJson] Parsed .claude-plugin/plugin.json.
|
|
74
|
+
* @param {Object} [opts]
|
|
75
|
+
* @param {string[]} [opts.keywords] Override keyword subset
|
|
76
|
+
* (defaults to CURATED_KEYWORDS).
|
|
77
|
+
* @returns {Object} Manifest object,
|
|
78
|
+
* keys in documented order, ready to JSON.stringify with 2-space indent.
|
|
79
|
+
*/
|
|
80
|
+
function buildManifest(sources, opts) {
|
|
81
|
+
const opts2 = opts || {};
|
|
82
|
+
const pkg = sources && sources.packageJson;
|
|
83
|
+
const claudePlugin = sources && sources.claudePluginJson;
|
|
84
|
+
|
|
85
|
+
if (!pkg || typeof pkg !== 'object') {
|
|
86
|
+
throw new Error('cursor-marketplace: sources.packageJson is required');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// name — prefer .claude-plugin/plugin.json.name (canonical, already
|
|
90
|
+
// kebab-case as "get-design-done"); fall back to stripping npm scope
|
|
91
|
+
// prefix from package.json.name.
|
|
92
|
+
let name;
|
|
93
|
+
if (claudePlugin && typeof claudePlugin.name === 'string') {
|
|
94
|
+
name = claudePlugin.name;
|
|
95
|
+
} else if (typeof pkg.name === 'string') {
|
|
96
|
+
name = pkg.name.replace(/^@[^/]+\//, '');
|
|
97
|
+
} else {
|
|
98
|
+
throw new Error('cursor-marketplace: name is required (no source)');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// description — required (we want predictable failure if package.json
|
|
102
|
+
// is malformed).
|
|
103
|
+
if (typeof pkg.description !== 'string' || pkg.description.length === 0) {
|
|
104
|
+
throw new Error('cursor-marketplace: packageJson.description is required');
|
|
105
|
+
}
|
|
106
|
+
const description = pkg.description;
|
|
107
|
+
|
|
108
|
+
// version — required, semver-shaped.
|
|
109
|
+
if (typeof pkg.version !== 'string' || !/^\d+\.\d+\.\d+/.test(pkg.version)) {
|
|
110
|
+
throw new Error(
|
|
111
|
+
'cursor-marketplace: packageJson.version is required and must be semver-shaped'
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
const version = pkg.version;
|
|
115
|
+
|
|
116
|
+
// author — resolve in order: claudePluginJson.author.name → pkg.author
|
|
117
|
+
// (string form) → pkg.author.name. Email only if claudePluginJson source
|
|
118
|
+
// carries one (GDD does not today).
|
|
119
|
+
let authorName;
|
|
120
|
+
let authorEmail;
|
|
121
|
+
if (
|
|
122
|
+
claudePlugin
|
|
123
|
+
&& claudePlugin.author
|
|
124
|
+
&& typeof claudePlugin.author === 'object'
|
|
125
|
+
&& typeof claudePlugin.author.name === 'string'
|
|
126
|
+
) {
|
|
127
|
+
authorName = claudePlugin.author.name;
|
|
128
|
+
if (typeof claudePlugin.author.email === 'string') {
|
|
129
|
+
authorEmail = claudePlugin.author.email;
|
|
130
|
+
}
|
|
131
|
+
} else if (typeof pkg.author === 'string' && pkg.author.length > 0) {
|
|
132
|
+
authorName = pkg.author;
|
|
133
|
+
} else if (
|
|
134
|
+
pkg.author
|
|
135
|
+
&& typeof pkg.author === 'object'
|
|
136
|
+
&& typeof pkg.author.name === 'string'
|
|
137
|
+
) {
|
|
138
|
+
authorName = pkg.author.name;
|
|
139
|
+
if (typeof pkg.author.email === 'string') {
|
|
140
|
+
authorEmail = pkg.author.email;
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
throw new Error('cursor-marketplace: author.name is required (no source)');
|
|
144
|
+
}
|
|
145
|
+
const author = authorEmail
|
|
146
|
+
? { name: authorName, email: authorEmail }
|
|
147
|
+
: { name: authorName };
|
|
148
|
+
|
|
149
|
+
// homepage — verbatim, omit if absent.
|
|
150
|
+
const homepage =
|
|
151
|
+
typeof pkg.homepage === 'string' && pkg.homepage.length > 0
|
|
152
|
+
? pkg.homepage
|
|
153
|
+
: undefined;
|
|
154
|
+
|
|
155
|
+
// repository — package.json may store as object {type, url} or string.
|
|
156
|
+
// Strip trailing .git for cleaner display.
|
|
157
|
+
let repository;
|
|
158
|
+
if (pkg.repository) {
|
|
159
|
+
let rawUrl;
|
|
160
|
+
if (typeof pkg.repository === 'string') {
|
|
161
|
+
rawUrl = pkg.repository;
|
|
162
|
+
} else if (typeof pkg.repository === 'object'
|
|
163
|
+
&& typeof pkg.repository.url === 'string') {
|
|
164
|
+
rawUrl = pkg.repository.url;
|
|
165
|
+
}
|
|
166
|
+
if (rawUrl) {
|
|
167
|
+
repository = rawUrl.replace(/\.git$/, '');
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// license — verbatim, omit if absent.
|
|
172
|
+
const license =
|
|
173
|
+
typeof pkg.license === 'string' && pkg.license.length > 0
|
|
174
|
+
? pkg.license
|
|
175
|
+
: undefined;
|
|
176
|
+
|
|
177
|
+
// keywords — opts override → CURATED_KEYWORDS default. Always materialize
|
|
178
|
+
// a fresh array (don't expose the frozen module-level constant directly
|
|
179
|
+
// in user-mutable output).
|
|
180
|
+
const keywords =
|
|
181
|
+
Array.isArray(opts2.keywords) && opts2.keywords.length > 0
|
|
182
|
+
? opts2.keywords.slice()
|
|
183
|
+
: CURATED_KEYWORDS.slice();
|
|
184
|
+
|
|
185
|
+
// Assemble in documented order: name, description, version, author,
|
|
186
|
+
// homepage, repository, license, keywords. Omit undefined fields so
|
|
187
|
+
// JSON.stringify produces a clean diff.
|
|
188
|
+
const manifest = {};
|
|
189
|
+
manifest.name = name;
|
|
190
|
+
manifest.description = description;
|
|
191
|
+
manifest.version = version;
|
|
192
|
+
manifest.author = author;
|
|
193
|
+
if (homepage !== undefined) manifest.homepage = homepage;
|
|
194
|
+
if (repository !== undefined) manifest.repository = repository;
|
|
195
|
+
if (license !== undefined) manifest.license = license;
|
|
196
|
+
manifest.keywords = keywords;
|
|
197
|
+
|
|
198
|
+
return manifest;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Copy a directory tree recursively. Vanilla fs only — no deps.
|
|
203
|
+
* Returns the list of relative paths written (relative to `dest`).
|
|
204
|
+
*/
|
|
205
|
+
function copyDirRecursive(src, dest, relPrefix) {
|
|
206
|
+
const written = [];
|
|
207
|
+
const stack = [{ s: src, d: dest, rel: relPrefix || '' }];
|
|
208
|
+
while (stack.length > 0) {
|
|
209
|
+
const { s, d, rel } = stack.pop();
|
|
210
|
+
fs.mkdirSync(d, { recursive: true });
|
|
211
|
+
const entries = fs.readdirSync(s);
|
|
212
|
+
for (const entry of entries) {
|
|
213
|
+
const sp = path.join(s, entry);
|
|
214
|
+
const dp = path.join(d, entry);
|
|
215
|
+
const relPath = rel ? `${rel}/${entry}` : entry;
|
|
216
|
+
const stat = fs.statSync(sp);
|
|
217
|
+
if (stat.isDirectory()) {
|
|
218
|
+
stack.push({ s: sp, d: dp, rel: relPath });
|
|
219
|
+
} else if (stat.isFile()) {
|
|
220
|
+
fs.copyFileSync(sp, dp);
|
|
221
|
+
written.push(relPath);
|
|
222
|
+
}
|
|
223
|
+
// symlinks + other: ignored (skills tree is regular files only)
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return written;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Convert/emit the cursor-marketplace bundle into a destination directory.
|
|
231
|
+
* Called by build-distribution-bundles.cjs (Plan 28-8-X1).
|
|
232
|
+
*
|
|
233
|
+
* Per CONTEXT D-06, `skills/` is the shared source — this converter emits
|
|
234
|
+
* the marketplace bundle as:
|
|
235
|
+
*
|
|
236
|
+
* <outDir>/
|
|
237
|
+
* .cursor-plugin/
|
|
238
|
+
* plugin.json ← the manifest object, JSON.stringified
|
|
239
|
+
* skills/
|
|
240
|
+
* <each skill copied verbatim from input.skillsDir>
|
|
241
|
+
*
|
|
242
|
+
* Cursor accepts Claude-compatible SKILL.md so no per-skill content
|
|
243
|
+
* transform is required at this layer. The Tier-1 cursor.cjs converter
|
|
244
|
+
* remains responsible for the per-runtime SKILL.md rewrites needed by the
|
|
245
|
+
* file-drop install path; those rewrites are irrelevant to a marketplace
|
|
246
|
+
* bundle (Cursor's marketplace reads the SKILL.md content directly).
|
|
247
|
+
*
|
|
248
|
+
* Idempotent: rerunning with the same inputs produces identical files
|
|
249
|
+
* (no partial-state corruption, no append-only emissions).
|
|
250
|
+
*
|
|
251
|
+
* Touches only paths under `outDir`. The source `skillsDir` is read-only.
|
|
252
|
+
*
|
|
253
|
+
* @param {Object} input
|
|
254
|
+
* @param {string} input.skillsDir Path to source skills/ tree.
|
|
255
|
+
* @param {string} input.outDir Path to destination bundle directory.
|
|
256
|
+
* @param {Object} input.manifest Manifest object from buildManifest().
|
|
257
|
+
* @param {Object} [opts]
|
|
258
|
+
* @returns {{ filesWritten: string[] }} Sorted relative paths under outDir.
|
|
259
|
+
*/
|
|
260
|
+
function convert(input, opts) {
|
|
261
|
+
if (!input || typeof input !== 'object') {
|
|
262
|
+
throw new Error('cursor-marketplace.convert: input is required');
|
|
263
|
+
}
|
|
264
|
+
const { skillsDir, outDir, manifest } = input;
|
|
265
|
+
if (typeof skillsDir !== 'string' || skillsDir.length === 0) {
|
|
266
|
+
throw new Error('cursor-marketplace.convert: input.skillsDir is required');
|
|
267
|
+
}
|
|
268
|
+
if (typeof outDir !== 'string' || outDir.length === 0) {
|
|
269
|
+
throw new Error('cursor-marketplace.convert: input.outDir is required');
|
|
270
|
+
}
|
|
271
|
+
if (!manifest || typeof manifest !== 'object') {
|
|
272
|
+
throw new Error('cursor-marketplace.convert: input.manifest is required');
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const skillsStat = fs.statSync(skillsDir);
|
|
276
|
+
if (!skillsStat.isDirectory()) {
|
|
277
|
+
throw new Error(
|
|
278
|
+
`cursor-marketplace.convert: skillsDir is not a directory: ${skillsDir}`
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const written = [];
|
|
283
|
+
|
|
284
|
+
// Create outDir if absent.
|
|
285
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
286
|
+
|
|
287
|
+
// Write manifest at <outDir>/.cursor-plugin/plugin.json.
|
|
288
|
+
const manifestDir = path.join(outDir, '.cursor-plugin');
|
|
289
|
+
fs.mkdirSync(manifestDir, { recursive: true });
|
|
290
|
+
const manifestPath = path.join(manifestDir, 'plugin.json');
|
|
291
|
+
fs.writeFileSync(
|
|
292
|
+
manifestPath,
|
|
293
|
+
JSON.stringify(manifest, null, 2) + '\n',
|
|
294
|
+
'utf8'
|
|
295
|
+
);
|
|
296
|
+
written.push('.cursor-plugin/plugin.json');
|
|
297
|
+
|
|
298
|
+
// Copy skills/ tree verbatim under <outDir>/skills.
|
|
299
|
+
const skillsDest = path.join(outDir, 'skills');
|
|
300
|
+
const copied = copyDirRecursive(skillsDir, skillsDest, 'skills');
|
|
301
|
+
for (const rel of copied) {
|
|
302
|
+
written.push(rel);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
written.sort();
|
|
306
|
+
return { filesWritten: written };
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
module.exports = { buildManifest, convert, CURATED_KEYWORDS };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* scripts/lib/install/converters/cursor.cjs — Phase 28.7 (Plan 28.7-04).
|
|
5
|
+
*
|
|
6
|
+
* Cursor SKILL.md converter. Translates Claude-shape source into
|
|
7
|
+
* Cursor's expected shape:
|
|
8
|
+
*
|
|
9
|
+
* - Frontmatter `name:` normalized to `gdd-<skill>` (no double-prefix).
|
|
10
|
+
* - Slash references in prose pass through unchanged — Cursor consumes
|
|
11
|
+
* the same `/gdd-<name>` shape Claude does (see runtime-slash.cjs).
|
|
12
|
+
* Mixed-shape inputs (`gdd-x`, `/gdd:x`) are normalized to `/gdd-x`
|
|
13
|
+
* so the installed skill is consistent.
|
|
14
|
+
* - Tool names in code fences pass through unchanged — Cursor accepts
|
|
15
|
+
* the Claude vocabulary (Read/Write/Bash/Edit/Grep/Glob).
|
|
16
|
+
* - A 1-line HTML adapter header is injected at the top of the body
|
|
17
|
+
* to record that this file was auto-generated from Claude source.
|
|
18
|
+
*
|
|
19
|
+
* Architecture ported from gsd-build/get-shit-done (MIT) — per Phase
|
|
20
|
+
* 28.7 D-02 (port architecture, not source). See NOTICE for upstream
|
|
21
|
+
* attribution. gsd-build's equivalent function is
|
|
22
|
+
* `convertClaudeCommandToCursorSkill` in bin/install.js; our modular
|
|
23
|
+
* factor delegates the actual rewrites to ./shared.cjs.
|
|
24
|
+
*
|
|
25
|
+
* Pure / side-effect-free: no fs, no env, no path. `convert` is a
|
|
26
|
+
* deterministic string → string transform.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
const shared = require('./shared.cjs');
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Convert Claude-source SKILL.md content for the Cursor runtime.
|
|
33
|
+
*
|
|
34
|
+
* @param {string} content Full source SKILL.md content (frontmatter + body).
|
|
35
|
+
* @param {string} skillName The bare skill name (e.g. `'help'`, `'explore'`).
|
|
36
|
+
* @param {{ runtime?: string }} [opts] Optional context — `runtime` defaults
|
|
37
|
+
* to `'cursor'`. Currently informational only; future per-tier branching
|
|
38
|
+
* may consume it.
|
|
39
|
+
* @returns {string}
|
|
40
|
+
*/
|
|
41
|
+
function convert(content, skillName, opts) {
|
|
42
|
+
const { frontmatter, body } = shared.extractFrontmatterAndBody(content);
|
|
43
|
+
const fm = shared.buildFrontmatter(frontmatter, skillName, 'gdd-');
|
|
44
|
+
let out = shared.rewriteSlashRefs(body, 'cursor');
|
|
45
|
+
out = shared.ensureAdapterHeader(out, 'Cursor');
|
|
46
|
+
return fm + out;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = { convert };
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* scripts/lib/install/converters/gemini.cjs — Phase 28.7 (Plan 28.7-07).
|
|
5
|
+
*
|
|
6
|
+
* Gemini CLI command-file converter. Translates Claude-shape SKILL.md
|
|
7
|
+
* source into the command-format output Gemini expects under
|
|
8
|
+
* `<config>/commands/gdd/<name>.md` (see Phase 28.7 D-05 +
|
|
9
|
+
* `runtime-artifact-layout.cjs#gemini`, which stages this converter via
|
|
10
|
+
* `commandsKind('commands/gdd', 'gdd-', ...)`).
|
|
11
|
+
*
|
|
12
|
+
* Translation rules:
|
|
13
|
+
*
|
|
14
|
+
* - Frontmatter `name:` normalized to `gdd-<skill>` (no double-prefix).
|
|
15
|
+
* - Slash references in prose pass through as `/gdd-<name>` — Gemini
|
|
16
|
+
* accepts the Claude-canonical slash shape via `runtime-slash.cjs`
|
|
17
|
+
* (rt: 'gemini' → `/gdd-`). Legacy colon and shell-variable forms
|
|
18
|
+
* are normalized to `/gdd-`.
|
|
19
|
+
* - Tool names in code fences are rewritten per `GEMINI_TOOL_MAP`
|
|
20
|
+
* (Phase 28.7 D-06 — reuse Phase 21 `reference/gemini-tools.md`
|
|
21
|
+
* authoritative table). Source of truth for the map is the Phase 21
|
|
22
|
+
* reference doc, NOT this file; the constant below is a frozen
|
|
23
|
+
* snapshot of that table as of `Last verified: 2026-04-24`. If
|
|
24
|
+
* Gemini ships a tool-vocabulary change, update
|
|
25
|
+
* `reference/gemini-tools.md` FIRST and then re-sync the constant.
|
|
26
|
+
* Currently mapped:
|
|
27
|
+
* Read → read_file
|
|
28
|
+
* Write → write_file
|
|
29
|
+
* Edit → replace
|
|
30
|
+
* Bash → run_shell_command
|
|
31
|
+
* Grep → search_file_content
|
|
32
|
+
* Glob → glob
|
|
33
|
+
* WebSearch → google_web_search
|
|
34
|
+
* WebFetch → web_fetch
|
|
35
|
+
* `Task` is intentionally absent — per Phase 21 gemini-tools.md
|
|
36
|
+
* "Known gaps", Gemini handles Task via a nested-CLI invocation, not
|
|
37
|
+
* a tool call; skills that rely on Task fall back to documentation
|
|
38
|
+
* prose ("on Gemini this becomes a nested gemini CLI run") that the
|
|
39
|
+
* converter leaves untouched.
|
|
40
|
+
* - Prose mentions of tool names (e.g. "use the Bash tool") are NOT
|
|
41
|
+
* rewritten — only the parenthesized invocation form inside fenced
|
|
42
|
+
* blocks gets rewritten. This matches the codex + augment converter
|
|
43
|
+
* policies (Phase 28.7 D-06 invocation-only convention).
|
|
44
|
+
* - A 1-line HTML adapter header is injected at the top of the body
|
|
45
|
+
* to record that this file was auto-generated from Claude source.
|
|
46
|
+
*
|
|
47
|
+
* Wave 4 layout note: Gemini uses `commands/gdd/<name>.md` (nested
|
|
48
|
+
* `gdd/` subdirectory under `commands/`), distinct from OpenCode + Kilo
|
|
49
|
+
* which use the XDG singular `command/<name>.md` layout. Both shapes
|
|
50
|
+
* are command-format runtimes — they differ only in destSubpath, which
|
|
51
|
+
* is encoded in `runtime-artifact-layout.cjs#commandsKind`.
|
|
52
|
+
*
|
|
53
|
+
* Architecture ported from gsd-build/get-shit-done (MIT) — per Phase
|
|
54
|
+
* 28.7 D-02 (port architecture, not source). See NOTICE for upstream
|
|
55
|
+
* attribution. gsd-build's equivalent function is
|
|
56
|
+
* `convertClaudeCommandToGeminiCommand` in bin/install.js; our modular
|
|
57
|
+
* factor delegates the actual rewrites to ./shared.cjs and follows the
|
|
58
|
+
* same `tool-map + slash-rewrite + adapter-header` pattern as the codex
|
|
59
|
+
* (CODEX_TOOL_MAP) and augment (AUGMENT_TOOL_MAP) converters.
|
|
60
|
+
*
|
|
61
|
+
* Phase 21 reference cite (D-06): reference/gemini-tools.md is the
|
|
62
|
+
* canonical, version-pinned source for the tool-name mapping. Do not
|
|
63
|
+
* edit GEMINI_TOOL_MAP without first updating that file.
|
|
64
|
+
*
|
|
65
|
+
* Pure / side-effect-free: no fs, no env, no path. `convert` is a
|
|
66
|
+
* deterministic string → string transform.
|
|
67
|
+
*/
|
|
68
|
+
|
|
69
|
+
const shared = require('./shared.cjs');
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Claude tool name → Gemini tool name. Locked by Phase 21
|
|
73
|
+
* `reference/gemini-tools.md` (per Phase 28.7 D-06). Skills referenced
|
|
74
|
+
* as `Read(...)`, `Write(...)`, etc. in Claude-source code fences are
|
|
75
|
+
* rewritten to Gemini's vocabulary at install time.
|
|
76
|
+
*
|
|
77
|
+
* Note: `Task` is intentionally absent. Per Phase 21 gemini-tools.md
|
|
78
|
+
* "Known gaps", Gemini handles Task via nested-CLI invocation (not a
|
|
79
|
+
* tool call); the converter leaves `Task(...)` references untouched so
|
|
80
|
+
* fallback prose ("on Gemini this becomes a nested gemini CLI run")
|
|
81
|
+
* is still readable.
|
|
82
|
+
*
|
|
83
|
+
* Frozen to prevent accidental mutation. The same Object.freeze pattern
|
|
84
|
+
* is used by CODEX_TOOL_MAP (shared.cjs) and AUGMENT_TOOL_MAP
|
|
85
|
+
* (augment.cjs).
|
|
86
|
+
*/
|
|
87
|
+
const GEMINI_TOOL_MAP = Object.freeze({
|
|
88
|
+
Read: 'read_file',
|
|
89
|
+
Write: 'write_file',
|
|
90
|
+
Edit: 'replace',
|
|
91
|
+
Bash: 'run_shell_command',
|
|
92
|
+
Grep: 'search_file_content',
|
|
93
|
+
Glob: 'glob',
|
|
94
|
+
WebSearch: 'google_web_search',
|
|
95
|
+
WebFetch: 'web_fetch',
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Convert Claude-source SKILL.md content for the Gemini runtime.
|
|
100
|
+
*
|
|
101
|
+
* @param {string} content Full source SKILL.md content (frontmatter + body).
|
|
102
|
+
* @param {string} skillName The bare skill name (e.g. `'help'`, `'explore'`).
|
|
103
|
+
* @param {{ runtime?: string }} [opts] Optional context — `runtime` defaults
|
|
104
|
+
* to `'gemini'`. Currently informational only.
|
|
105
|
+
* @returns {string}
|
|
106
|
+
*/
|
|
107
|
+
function convert(content, skillName, opts) {
|
|
108
|
+
const { frontmatter, body } = shared.extractFrontmatterAndBody(content);
|
|
109
|
+
const fm = shared.buildFrontmatter(frontmatter, skillName, 'gdd-');
|
|
110
|
+
let out = shared.rewriteSlashRefs(body, 'gemini');
|
|
111
|
+
out = shared.rewriteCodeFenceTools(out, GEMINI_TOOL_MAP);
|
|
112
|
+
out = shared.ensureAdapterHeader(out, 'Gemini');
|
|
113
|
+
return fm + out;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
module.exports = { convert, GEMINI_TOOL_MAP };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* scripts/lib/install/converters/kilo.cjs — Phase 28.7 (Plan 28.7-07).
|
|
5
|
+
*
|
|
6
|
+
* Kilo command-file converter. Translates Claude-shape SKILL.md source
|
|
7
|
+
* into the command-format output Kilo expects under its XDG
|
|
8
|
+
* `command/<name>.md` slash-command directory (see Phase 28.7 D-05 +
|
|
9
|
+
* `runtime-artifact-layout.cjs#kilo`, which stages this converter via
|
|
10
|
+
* `commandsKind('command', 'gdd-', ...)`).
|
|
11
|
+
*
|
|
12
|
+
* Translation rules:
|
|
13
|
+
*
|
|
14
|
+
* - Frontmatter `name:` normalized to `gdd-<skill>` (no double-prefix).
|
|
15
|
+
* - Slash references in prose pass through as `/gdd-<name>` — Kilo
|
|
16
|
+
* accepts the Claude-canonical slash shape via the
|
|
17
|
+
* `runtime-slash.cjs` map (rt: 'kilo' → `/gdd-`). Legacy colon and
|
|
18
|
+
* shell-variable forms are normalized to `/gdd-`.
|
|
19
|
+
* - Tool names in code fences pass through unchanged — per Phase 21
|
|
20
|
+
* verification, Kilo accepts the Claude vocabulary
|
|
21
|
+
* (Read/Write/Bash/Edit/Grep/Glob) natively. No tool-map rewrite.
|
|
22
|
+
* - A 1-line HTML adapter header is injected at the top of the body
|
|
23
|
+
* to record that this file was auto-generated from Claude source.
|
|
24
|
+
*
|
|
25
|
+
* Wave 4 layout note: Kilo uses the same `command/<name>.md` flat
|
|
26
|
+
* layout as OpenCode (its sibling Wave 4 runtime). The converter is
|
|
27
|
+
* structurally identical to opencode.cjs — only the runtime string
|
|
28
|
+
* and adapter-display label differ. Both runtimes share the XDG
|
|
29
|
+
* `command/` directory convention (singular `command`, not the
|
|
30
|
+
* Gemini-style `commands/gdd/` nested path).
|
|
31
|
+
*
|
|
32
|
+
* Architecture ported from gsd-build/get-shit-done (MIT) — per Phase
|
|
33
|
+
* 28.7 D-02 (port architecture, not source). See NOTICE for upstream
|
|
34
|
+
* attribution. gsd-build's equivalent function is
|
|
35
|
+
* `convertClaudeCommandToKiloCommand` in bin/install.js; our modular
|
|
36
|
+
* factor delegates the actual rewrites to ./shared.cjs and follows
|
|
37
|
+
* the same uniform pattern as opencode / cursor / qwen.
|
|
38
|
+
*
|
|
39
|
+
* Pure / side-effect-free: no fs, no env, no path. `convert` is a
|
|
40
|
+
* deterministic string → string transform.
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
const shared = require('./shared.cjs');
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Convert Claude-source SKILL.md content for the Kilo runtime.
|
|
47
|
+
*
|
|
48
|
+
* @param {string} content Full source SKILL.md content (frontmatter + body).
|
|
49
|
+
* @param {string} skillName The bare skill name (e.g. `'help'`, `'explore'`).
|
|
50
|
+
* @param {{ runtime?: string }} [opts] Optional context — `runtime` defaults
|
|
51
|
+
* to `'kilo'`. Currently informational only.
|
|
52
|
+
* @returns {string}
|
|
53
|
+
*/
|
|
54
|
+
function convert(content, skillName, opts) {
|
|
55
|
+
const { frontmatter, body } = shared.extractFrontmatterAndBody(content);
|
|
56
|
+
const fm = shared.buildFrontmatter(frontmatter, skillName, 'gdd-');
|
|
57
|
+
let out = shared.rewriteSlashRefs(body, 'kilo');
|
|
58
|
+
out = shared.ensureAdapterHeader(out, 'Kilo');
|
|
59
|
+
return fm + out;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = { convert };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* scripts/lib/install/converters/opencode.cjs — Phase 28.7 (Plan 28.7-07).
|
|
5
|
+
*
|
|
6
|
+
* OpenCode command-file converter. Translates Claude-shape SKILL.md
|
|
7
|
+
* source into the command-format output OpenCode expects under its
|
|
8
|
+
* XDG `command/<name>.md` slash-command directory (see Phase 28.7
|
|
9
|
+
* D-05 + `runtime-artifact-layout.cjs#opencode`, which stages this
|
|
10
|
+
* converter via `commandsKind('command', 'gdd-', ...)`).
|
|
11
|
+
*
|
|
12
|
+
* Translation rules:
|
|
13
|
+
*
|
|
14
|
+
* - Frontmatter `name:` normalized to `gdd-<skill>` (no double-prefix).
|
|
15
|
+
* - Slash references in prose pass through as `/gdd-<name>` —
|
|
16
|
+
* OpenCode accepts the Claude-canonical slash shape via the
|
|
17
|
+
* `runtime-slash.cjs` map (rt: 'opencode' → `/gdd-`). Legacy
|
|
18
|
+
* colon and shell-variable forms are normalized to `/gdd-`.
|
|
19
|
+
* - Tool names in code fences pass through unchanged — per Phase 21
|
|
20
|
+
* verification, OpenCode accepts the Claude vocabulary
|
|
21
|
+
* (Read/Write/Bash/Edit/Grep/Glob) natively. No tool-map rewrite.
|
|
22
|
+
* - A 1-line HTML adapter header is injected at the top of the body
|
|
23
|
+
* to record that this file was auto-generated from Claude source.
|
|
24
|
+
*
|
|
25
|
+
* Command-format vs skills-format note: OpenCode is one of three Wave 4
|
|
26
|
+
* runtimes (alongside kilo + gemini) whose layout is a flat
|
|
27
|
+
* `command/<name>.md` file rather than the per-skill folder structure
|
|
28
|
+
* (`skills/<name>/SKILL.md`) used by Wave 1/2/3 runtimes. The converter
|
|
29
|
+
* itself does NOT know its destination directory — the destSubpath is
|
|
30
|
+
* encoded in `runtime-artifact-layout.cjs#commandsKind`. From the
|
|
31
|
+
* converter's perspective, the output is still a single markdown +
|
|
32
|
+
* YAML-frontmatter string; the installer routes it to the right path.
|
|
33
|
+
*
|
|
34
|
+
* Architecture ported from gsd-build/get-shit-done (MIT) — per Phase
|
|
35
|
+
* 28.7 D-02 (port architecture, not source). See NOTICE for upstream
|
|
36
|
+
* attribution. gsd-build's equivalent function is
|
|
37
|
+
* `convertClaudeCommandToOpenCodeCommand` in bin/install.js; our
|
|
38
|
+
* modular factor delegates the actual rewrites to ./shared.cjs and
|
|
39
|
+
* follows the same uniform pattern as cursor / qwen / copilot.
|
|
40
|
+
*
|
|
41
|
+
* Pure / side-effect-free: no fs, no env, no path. `convert` is a
|
|
42
|
+
* deterministic string → string transform.
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
const shared = require('./shared.cjs');
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Convert Claude-source SKILL.md content for the OpenCode runtime.
|
|
49
|
+
*
|
|
50
|
+
* @param {string} content Full source SKILL.md content (frontmatter + body).
|
|
51
|
+
* @param {string} skillName The bare skill name (e.g. `'help'`, `'explore'`).
|
|
52
|
+
* @param {{ runtime?: string }} [opts] Optional context — `runtime` defaults
|
|
53
|
+
* to `'opencode'`. Currently informational only.
|
|
54
|
+
* @returns {string}
|
|
55
|
+
*/
|
|
56
|
+
function convert(content, skillName, opts) {
|
|
57
|
+
const { frontmatter, body } = shared.extractFrontmatterAndBody(content);
|
|
58
|
+
const fm = shared.buildFrontmatter(frontmatter, skillName, 'gdd-');
|
|
59
|
+
let out = shared.rewriteSlashRefs(body, 'opencode');
|
|
60
|
+
out = shared.ensureAdapterHeader(out, 'OpenCode');
|
|
61
|
+
return fm + out;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = { convert };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* scripts/lib/install/converters/qwen.cjs — Phase 28.7 (Plan 28.7-05).
|
|
5
|
+
*
|
|
6
|
+
* Qwen Code SKILL.md converter. Translates Claude-shape source into
|
|
7
|
+
* Qwen's expected shape:
|
|
8
|
+
*
|
|
9
|
+
* - Frontmatter `name:` normalized to `gdd-<skill>` (no double-prefix).
|
|
10
|
+
* - Slash references in prose pass through as `/gdd-<name>` —
|
|
11
|
+
* Qwen accepts the Claude shape. Mixed-shape inputs are normalized
|
|
12
|
+
* via the runtime-slash module.
|
|
13
|
+
* - Tool names in code fences pass through unchanged — per Phase 21
|
|
14
|
+
* verification, Qwen Code is Claude-compatible and accepts the
|
|
15
|
+
* Claude vocabulary (Read/Write/Bash/Edit/Grep/Glob) natively.
|
|
16
|
+
* gsd-build reuses `convertClaudeCommandToClaudeSkill` for Qwen on
|
|
17
|
+
* the same grounds; our modular equivalent simply omits the tool-map
|
|
18
|
+
* rewrite step (cf. cursor.cjs / windsurf.cjs / trae.cjs).
|
|
19
|
+
* - A 1-line HTML adapter header is injected at the top of the body
|
|
20
|
+
* to record that this file was auto-generated from Claude source.
|
|
21
|
+
*
|
|
22
|
+
* Architecture ported from gsd-build/get-shit-done (MIT) — per Phase
|
|
23
|
+
* 28.7 D-02 (port architecture, not source). See NOTICE for upstream
|
|
24
|
+
* attribution. gsd-build's qwen runtime reuses Claude-shape conversion
|
|
25
|
+
* (see runtime-artifact-layout.cjs case 'qwen'); our modular factor
|
|
26
|
+
* delegates the actual rewrites to ./shared.cjs.
|
|
27
|
+
*
|
|
28
|
+
* Pure / side-effect-free: no fs, no env, no path. `convert` is a
|
|
29
|
+
* deterministic string → string transform.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
const shared = require('./shared.cjs');
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Convert Claude-source SKILL.md content for the Qwen runtime.
|
|
36
|
+
*
|
|
37
|
+
* @param {string} content Full source SKILL.md content (frontmatter + body).
|
|
38
|
+
* @param {string} skillName The bare skill name (e.g. `'help'`, `'explore'`).
|
|
39
|
+
* @param {{ runtime?: string }} [opts] Optional context — `runtime` defaults
|
|
40
|
+
* to `'qwen'`. Currently informational only.
|
|
41
|
+
* @returns {string}
|
|
42
|
+
*/
|
|
43
|
+
function convert(content, skillName, opts) {
|
|
44
|
+
const { frontmatter, body } = shared.extractFrontmatterAndBody(content);
|
|
45
|
+
const fm = shared.buildFrontmatter(frontmatter, skillName, 'gdd-');
|
|
46
|
+
let out = shared.rewriteSlashRefs(body, 'qwen');
|
|
47
|
+
out = shared.ensureAdapterHeader(out, 'Qwen');
|
|
48
|
+
return fm + out;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = { convert };
|