@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.
Files changed (49) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +81 -0
  4. package/README.de.md +14 -0
  5. package/README.fr.md +14 -0
  6. package/README.it.md +14 -0
  7. package/README.ja.md +14 -0
  8. package/README.ko.md +14 -0
  9. package/README.md +18 -0
  10. package/README.zh-CN.md +14 -0
  11. package/SKILL.md +10 -10
  12. package/package.json +3 -1
  13. package/scripts/build-distribution-bundles.cjs +549 -0
  14. package/scripts/install.cjs +68 -0
  15. package/scripts/lib/install/config-dir.cjs +26 -0
  16. package/scripts/lib/install/converters/antigravity.cjs +48 -0
  17. package/scripts/lib/install/converters/augment.cjs +68 -0
  18. package/scripts/lib/install/converters/cline.cjs +206 -0
  19. package/scripts/lib/install/converters/codebuddy.cjs +55 -0
  20. package/scripts/lib/install/converters/codex-plugin.cjs +407 -0
  21. package/scripts/lib/install/converters/codex.cjs +61 -0
  22. package/scripts/lib/install/converters/copilot.cjs +47 -0
  23. package/scripts/lib/install/converters/cursor-marketplace.cjs +309 -0
  24. package/scripts/lib/install/converters/cursor.cjs +49 -0
  25. package/scripts/lib/install/converters/gemini.cjs +116 -0
  26. package/scripts/lib/install/converters/kilo.cjs +62 -0
  27. package/scripts/lib/install/converters/opencode.cjs +64 -0
  28. package/scripts/lib/install/converters/qwen.cjs +51 -0
  29. package/scripts/lib/install/converters/shared.cjs +377 -0
  30. package/scripts/lib/install/converters/trae.cjs +47 -0
  31. package/scripts/lib/install/converters/windsurf.cjs +47 -0
  32. package/scripts/lib/install/doctor-codex-plugin.cjs +388 -0
  33. package/scripts/lib/install/doctor-cursor-marketplace.cjs +366 -0
  34. package/scripts/lib/install/doctor-tier2.cjs +586 -0
  35. package/scripts/lib/install/installer.cjs +529 -47
  36. package/scripts/lib/install/merge.cjs +31 -1
  37. package/scripts/lib/install/runtime-artifact-layout.cjs +431 -0
  38. package/scripts/lib/install/runtime-homes.cjs +225 -0
  39. package/scripts/lib/install/runtime-slash.cjs +172 -0
  40. package/scripts/lib/install/runtimes.cjs +73 -32
  41. package/scripts/lint-agentskills-spec.cjs +457 -0
  42. package/skills/compare/SKILL.md +2 -2
  43. package/skills/compare/compare-rubric.md +1 -1
  44. package/skills/darkmode/SKILL.md +2 -2
  45. package/skills/darkmode/darkmode-audit-procedure.md +1 -1
  46. package/skills/figma-write/SKILL.md +2 -2
  47. package/skills/graphify/SKILL.md +2 -2
  48. package/skills/style/SKILL.md +2 -2
  49. 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 };