@hegemonart/get-design-done 1.28.5 → 1.28.7

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 (71) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +102 -0
  4. package/README.md +2 -0
  5. package/package.json +1 -1
  6. package/reference/registry.json +0 -140
  7. package/reference/skill-authoring-contract.md +40 -9
  8. package/scripts/install.cjs +7 -0
  9. package/scripts/lib/install/converters/antigravity.cjs +48 -0
  10. package/scripts/lib/install/converters/augment.cjs +68 -0
  11. package/scripts/lib/install/converters/cline.cjs +206 -0
  12. package/scripts/lib/install/converters/codebuddy.cjs +55 -0
  13. package/scripts/lib/install/converters/codex.cjs +61 -0
  14. package/scripts/lib/install/converters/copilot.cjs +47 -0
  15. package/scripts/lib/install/converters/cursor.cjs +49 -0
  16. package/scripts/lib/install/converters/gemini.cjs +116 -0
  17. package/scripts/lib/install/converters/kilo.cjs +62 -0
  18. package/scripts/lib/install/converters/opencode.cjs +64 -0
  19. package/scripts/lib/install/converters/qwen.cjs +51 -0
  20. package/scripts/lib/install/converters/shared.cjs +377 -0
  21. package/scripts/lib/install/converters/trae.cjs +47 -0
  22. package/scripts/lib/install/converters/windsurf.cjs +47 -0
  23. package/scripts/lib/install/installer.cjs +529 -47
  24. package/scripts/lib/install/merge.cjs +31 -1
  25. package/scripts/lib/install/runtime-artifact-layout.cjs +431 -0
  26. package/scripts/lib/install/runtime-homes.cjs +225 -0
  27. package/scripts/lib/install/runtime-slash.cjs +172 -0
  28. package/scripts/lib/install/runtimes.cjs +25 -32
  29. package/skills/apply-reflections/SKILL.md +1 -1
  30. package/skills/cache-manager/SKILL.md +2 -2
  31. package/skills/compare/SKILL.md +8 -8
  32. package/skills/connections/SKILL.md +9 -9
  33. package/skills/darkmode/SKILL.md +8 -8
  34. package/skills/debug/SKILL.md +3 -3
  35. package/skills/design/SKILL.md +6 -6
  36. package/skills/discover/SKILL.md +7 -7
  37. package/skills/explore/SKILL.md +6 -6
  38. package/skills/health/SKILL.md +2 -2
  39. package/skills/new-cycle/SKILL.md +1 -1
  40. package/skills/peer-cli-add/SKILL.md +6 -6
  41. package/skills/peer-cli-customize/SKILL.md +5 -5
  42. package/skills/peers/SKILL.md +2 -2
  43. package/skills/plan/SKILL.md +10 -10
  44. package/skills/quality-gate/SKILL.md +1 -1
  45. package/skills/router/SKILL.md +3 -3
  46. package/skills/scan/SKILL.md +17 -17
  47. package/skills/start/SKILL.md +1 -1
  48. package/skills/style/SKILL.md +5 -5
  49. package/skills/turn-closeout/SKILL.md +1 -1
  50. package/skills/verify/SKILL.md +10 -10
  51. package/skills/warm-cache/SKILL.md +2 -2
  52. /package/{reference → skills/apply-reflections}/apply-reflections-procedure.md +0 -0
  53. /package/{reference → skills/cache-manager}/cache-policy.md +0 -0
  54. /package/{reference → skills/compare}/compare-rubric.md +0 -0
  55. /package/{reference → skills/connections}/connections-onboarding.md +0 -0
  56. /package/{reference → skills/darkmode}/darkmode-audit-procedure.md +0 -0
  57. /package/{reference → skills/debug}/debug-feedback-loops.md +0 -0
  58. /package/{reference → skills/design}/design-procedure.md +0 -0
  59. /package/{reference → skills/discover}/discover-procedure.md +0 -0
  60. /package/{reference → skills/explore}/explore-procedure.md +0 -0
  61. /package/{reference → skills/health}/health-mcp-detection.md +0 -0
  62. /package/{reference → skills/health}/health-skill-length-report.md +0 -0
  63. /package/{reference → skills/new-cycle}/milestone-completeness-rubric.md +0 -0
  64. /package/{reference → skills/peer-cli-add}/peer-cli-protocol.md +0 -0
  65. /package/{reference → skills/plan}/plan-procedure.md +0 -0
  66. /package/{reference → skills/quality-gate}/threat-modeling.md +0 -0
  67. /package/{reference → skills/router}/router-rules.md +0 -0
  68. /package/{reference → skills/scan}/scan-procedure.md +0 -0
  69. /package/{reference → skills/start}/start-procedure.md +0 -0
  70. /package/{reference → skills/style}/style-doc-procedure.md +0 -0
  71. /package/{reference → skills/verify}/verify-procedure.md +0 -0
@@ -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 };
@@ -0,0 +1,377 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * scripts/lib/install/converters/shared.cjs — Phase 28.7 (Plan 28.7-04).
5
+ *
6
+ * Shared helpers for per-runtime SKILL.md content converters. Each
7
+ * runtime-specific converter (cursor.cjs, codex.cjs, copilot.cjs,
8
+ * antigravity.cjs in Wave 1; windsurf/augment/trae/qwen in Wave 2; etc.)
9
+ * composes these utilities to translate Claude-source SKILL.md content
10
+ * into the runtime's expected shape.
11
+ *
12
+ * Architecture ported from gsd-build/get-shit-done (MIT) — per Phase
13
+ * 28.7 D-02 (port architecture, not source). See NOTICE for upstream
14
+ * attribution. gsd-build's `convertClaudeCommandTo<Runtime>Skill` family
15
+ * bundled all per-runtime conversion logic into a single bin/install.js
16
+ * monolith; our modular re-implementation factors the shared rewrites
17
+ * here and leaves runtime-specific composition to per-runtime files.
18
+ *
19
+ * Exports (D-05):
20
+ * - extractFrontmatterAndBody(content)
21
+ * → { frontmatter: string|null, body: string }
22
+ * - rewriteSlashRefs(body, targetRuntime) → string
23
+ * Rewrites in-prose `/gdd-name`, `gdd-name`, `/gdd:name`, `$gdd-name`
24
+ * references to the runtime-canonical form via
25
+ * `../runtime-slash.cjs#formatGddSlash`. Only operates on prose;
26
+ * fenced code blocks are passed through untouched.
27
+ * - rewriteCodeFenceTools(body, toolMap) → string
28
+ * The inverse: ONLY rewrites inside fenced code blocks. Replaces
29
+ * `OldName(` with `NewName(` for every `{ OldName: NewName }` in
30
+ * `toolMap`. Used by the codex converter to apply CODEX_TOOL_MAP.
31
+ * - ensureAdapterHeader(body, runtimeDisplay) → string
32
+ * Prepends a 2-3 line HTML comment ("Auto-generated from Claude
33
+ * SKILL.md") before the first non-blank body line. Idempotent —
34
+ * running it twice does not duplicate the header.
35
+ * - buildFrontmatter(originalFrontmatter, skillName, runtimePrefix) → string
36
+ * Re-emits a YAML frontmatter block. `name:` is normalized to
37
+ * `<runtimePrefix><skillName>` (stripping the prefix if it was
38
+ * already present in the source). Other fields (description,
39
+ * tools, etc.) round-trip verbatim. Returns a string ending in
40
+ * `\n---\n`.
41
+ * - CODEX_TOOL_MAP
42
+ * Frozen constant — Phase 21 `reference/codex-tools.md` mapping
43
+ * (Read→read_file, Write/Edit→apply_patch, Bash/Grep/Glob→shell,
44
+ * WebSearch→web_search, WebFetch→shell).
45
+ *
46
+ * Pure / side-effect-free at module load and at every export call:
47
+ * - No fs / path top-level requires (all transforms operate on the
48
+ * `content` string argument).
49
+ * - No env mutation.
50
+ * - No globals.
51
+ * The only external runtime require is `../runtime-slash.cjs`, lazy-
52
+ * loaded inside `rewriteSlashRefs` to keep the static dependency graph
53
+ * thin (this file is also imported by Wave 2/3/4 plans which may not
54
+ * need slash rewrites if their target runtime is Claude-shape).
55
+ *
56
+ * Per Phase 28.7 D-08 the converter cluster is intentionally modular
57
+ * (one file per runtime, none re-export each other). shared.cjs is the
58
+ * sole cross-runtime module — used by all 13 converters.
59
+ *
60
+ * Per Phase 28.7 D-06 only the codex converter applies CODEX_TOOL_MAP;
61
+ * the other 12 runtimes accept Claude-compatible tool names verbatim.
62
+ *
63
+ * Conventions:
64
+ * - "frontmatter" = YAML between leading `---` delimiters; matches
65
+ * `^---\r?\n([\s\S]*?)\r?\n---\r?\n` (CRLF tolerant — Phase 28.6
66
+ * Windows-line-ending lesson).
67
+ * - "body" = everything after the closing `---`.
68
+ * - "code fence" = ```...``` block (3+ backticks, language tag optional).
69
+ * We use a coarse fence-aware splitter — backtick variants and
70
+ * tilde fences are not currently supported (out of scope; the
71
+ * plugin's SKILL.md sources use triple-backtick exclusively).
72
+ */
73
+
74
+ // ---------------------------------------------------------------------------
75
+ // CODEX_TOOL_MAP — Phase 21 reference (D-06)
76
+ // ---------------------------------------------------------------------------
77
+
78
+ /**
79
+ * Claude tool name → Codex tool name. Locked by Phase 21
80
+ * `reference/codex-tools.md`. Skills referenced as `Read(...)`,
81
+ * `Write(...)`, etc. in Claude-source code fences are rewritten to
82
+ * Codex's vocabulary at install time.
83
+ *
84
+ * Note: `Task` is intentionally absent. Per Phase 21 codex-tools.md
85
+ * "Known gaps", Codex does not expose nested-session as a tool call;
86
+ * skills that rely on Task call the gdd-sdk CLI via `shell(...)`. The
87
+ * codex converter leaves `Task(...)` references untouched so prose
88
+ * fallback prose ("on Codex this becomes shell('npx gdd-sdk ...')") is
89
+ * still readable.
90
+ *
91
+ * Frozen to prevent accidental mutation by downstream converters.
92
+ */
93
+ const CODEX_TOOL_MAP = Object.freeze({
94
+ Read: 'read_file',
95
+ Write: 'apply_patch',
96
+ Edit: 'apply_patch',
97
+ Bash: 'shell',
98
+ Grep: 'shell',
99
+ Glob: 'shell',
100
+ WebSearch: 'web_search',
101
+ WebFetch: 'shell',
102
+ });
103
+
104
+ // ---------------------------------------------------------------------------
105
+ // extractFrontmatterAndBody — YAML frontmatter parser
106
+ // ---------------------------------------------------------------------------
107
+
108
+ /**
109
+ * Split a SKILL.md content string into its YAML frontmatter and body.
110
+ *
111
+ * Matches the leading `---\n...\n---\n` block (CRLF tolerant). If no
112
+ * frontmatter is present, returns `{ frontmatter: null, body: content }`.
113
+ *
114
+ * @param {string} content
115
+ * @returns {{ frontmatter: string|null, body: string }}
116
+ */
117
+ function extractFrontmatterAndBody(content) {
118
+ if (typeof content !== 'string' || content === '') {
119
+ return { frontmatter: null, body: content || '' };
120
+ }
121
+ const m = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
122
+ if (!m) return { frontmatter: null, body: content };
123
+ return { frontmatter: m[1], body: m[2] };
124
+ }
125
+
126
+ // ---------------------------------------------------------------------------
127
+ // Code-fence-aware splitter (internal)
128
+ // ---------------------------------------------------------------------------
129
+
130
+ /**
131
+ * Split `body` into alternating segments of [prose, fence, prose, fence, ...].
132
+ * Index 0 (and every even index) is prose; every odd index is a complete
133
+ * fenced code block (including the opening/closing ``` lines).
134
+ *
135
+ * This lets `rewriteSlashRefs` operate ONLY on even indices and
136
+ * `rewriteCodeFenceTools` operate ONLY on odd indices, without either
137
+ * leaking into the other's domain.
138
+ *
139
+ * Coarse: a fence is any line beginning with three backticks (with or
140
+ * without a language tag) up to the next such line. Nested fences are
141
+ * not supported (irrelevant for our SKILL.md sources).
142
+ *
143
+ * @param {string} body
144
+ * @returns {string[]} alternating prose / fence segments
145
+ */
146
+ function splitByCodeFence(body) {
147
+ // Match a complete fenced block: opening ```[lang]\n ... \n``` (closing
148
+ // fence on its own line). The opening fence's backticks must be at the
149
+ // start of a line to avoid matching inline triple-backticks in prose.
150
+ // The pattern is non-greedy so consecutive fences each form one segment.
151
+ const fenceRe = /(^```[^\n]*\n[\s\S]*?\n```)/gm;
152
+ return body.split(fenceRe);
153
+ }
154
+
155
+ // ---------------------------------------------------------------------------
156
+ // rewriteSlashRefs — prose-only slash rewrite
157
+ // ---------------------------------------------------------------------------
158
+
159
+ /**
160
+ * Rewrite every in-prose `/gdd-name`, `gdd-name`, `/gdd:name`, `gdd:name`,
161
+ * `$gdd-name`, `$gdd:name` reference to the canonical slash form for
162
+ * `targetRuntime` (via `runtime-slash.cjs#formatGddSlash`).
163
+ *
164
+ * Operates on prose only — fenced code blocks pass through unchanged.
165
+ * (For codex, that means tool calls like `Bash(command="/gdd-x")` keep
166
+ * the slash form in shell strings — those are runtime-evaluated by the
167
+ * codex shell, not the Codex tool surface, so they must remain unchanged.)
168
+ *
169
+ * Inline code spans (`` `...` ``) are also passed through — they're prose
170
+ * to the markdown renderer but Claude's literal-form quoting (`` `/gdd-x` ``)
171
+ * appears in user-facing text and should usually be rewritten too. We
172
+ * therefore DO rewrite inside inline code spans (the regex doesn't
173
+ * special-case them). This matches gsd-build's behavior — converters
174
+ * rewrite slash references everywhere except fenced blocks.
175
+ *
176
+ * Pattern: `\b[/$]?gdd[-:][a-z0-9-]+\b` (case-insensitive on prefix; the
177
+ * skill-name portion `[a-z0-9-]+` matches the actual skill-name
178
+ * convention — lowercase + dashes + digits).
179
+ *
180
+ * Defensive: if `targetRuntime` is omitted, defaults to `'claude'` (i.e.
181
+ * `/gdd-<name>` shape).
182
+ *
183
+ * @param {string} body
184
+ * @param {string} [targetRuntime]
185
+ * @returns {string}
186
+ */
187
+ function rewriteSlashRefs(body, targetRuntime) {
188
+ if (typeof body !== 'string' || body === '') return body || '';
189
+ const { formatGddSlash } = require('../runtime-slash.cjs');
190
+ const rt = targetRuntime || 'claude';
191
+
192
+ const segments = splitByCodeFence(body);
193
+ // Pattern: optional `/` or `$` prefix, `gdd-` or `gdd:`, then the
194
+ // skill-name token. Skill names are lowercase + dashes + digits per
195
+ // GDD convention; the regex is case-insensitive on the `gdd` letters
196
+ // to accept malformed inputs.
197
+ const slashRe = /[/$]?gdd[-:][a-z][a-z0-9-]*/gi;
198
+
199
+ for (let i = 0; i < segments.length; i++) {
200
+ // Even indices are prose; odd are fenced code blocks (passthrough).
201
+ if (i % 2 === 1) continue;
202
+ segments[i] = segments[i].replace(slashRe, (match) =>
203
+ formatGddSlash(match, rt)
204
+ );
205
+ }
206
+ return segments.join('');
207
+ }
208
+
209
+ // ---------------------------------------------------------------------------
210
+ // rewriteCodeFenceTools — fence-only tool-name rewrite
211
+ // ---------------------------------------------------------------------------
212
+
213
+ /**
214
+ * Rewrite Claude tool names to runtime-equivalent names INSIDE fenced
215
+ * code blocks only. For every `{ OldName: NewName }` in `toolMap`,
216
+ * replace `\bOldName(` with `NewName(` in every fenced block.
217
+ *
218
+ * Prose mentions of tool names (e.g. "Use the Bash tool to ...") are
219
+ * left untouched — they're documentation about the Claude vocabulary
220
+ * and the converter's adapter header tells the user we've adapted the
221
+ * underlying calls. Only the actual code-fenced invocation form gets
222
+ * rewritten.
223
+ *
224
+ * Used by the codex converter with CODEX_TOOL_MAP. Other converters
225
+ * pass an empty map (and the function is a no-op).
226
+ *
227
+ * @param {string} body
228
+ * @param {Record<string,string>} toolMap
229
+ * @returns {string}
230
+ */
231
+ function rewriteCodeFenceTools(body, toolMap) {
232
+ if (typeof body !== 'string' || body === '') return body || '';
233
+ if (!toolMap || typeof toolMap !== 'object') return body;
234
+ const keys = Object.keys(toolMap);
235
+ if (keys.length === 0) return body;
236
+
237
+ const segments = splitByCodeFence(body);
238
+ for (let i = 0; i < segments.length; i++) {
239
+ // Even indices are prose (skip); odd are fenced code blocks.
240
+ if (i % 2 === 0) continue;
241
+ let fence = segments[i];
242
+ for (const oldName of keys) {
243
+ const newName = toolMap[oldName];
244
+ // \b<OldName>\( — word boundary then literal `(`.
245
+ // Escape the old name even though tool names are alphanumeric;
246
+ // future map entries may include `_` or `.` (still safe under \b).
247
+ const re = new RegExp(
248
+ '\\b' + oldName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '\\(',
249
+ 'g'
250
+ );
251
+ fence = fence.replace(re, newName + '(');
252
+ }
253
+ segments[i] = fence;
254
+ }
255
+ return segments.join('');
256
+ }
257
+
258
+ // ---------------------------------------------------------------------------
259
+ // ensureAdapterHeader — idempotent header injection
260
+ // ---------------------------------------------------------------------------
261
+
262
+ /**
263
+ * Prepend a 2-line HTML comment "Auto-generated from Claude SKILL.md"
264
+ * before the first non-blank line of `body`. Idempotent — running it
265
+ * twice produces a single header.
266
+ *
267
+ * Format:
268
+ * `<!-- gdd: auto-generated from Claude SKILL.md. <runtimeDisplay> adapter -->\n\n`
269
+ *
270
+ * Detection: if `body` already contains a `<runtimeDisplay> adapter`
271
+ * marker (anywhere in the first 500 chars — fast scan), the header is
272
+ * NOT re-prepended. This is the idempotency guarantee.
273
+ *
274
+ * @param {string} body
275
+ * @param {string} runtimeDisplay e.g. `'Cursor'`, `'Codex'`, `'Copilot'`,
276
+ * `'Antigravity'`. Used inside the comment text verbatim.
277
+ * @returns {string}
278
+ */
279
+ function ensureAdapterHeader(body, runtimeDisplay) {
280
+ if (typeof body !== 'string') return body;
281
+ const display = String(runtimeDisplay || 'Adapter');
282
+
283
+ const marker = display + ' adapter';
284
+ // Fast scan first ~500 chars — header is always at the very top if
285
+ // present; we don't need to scan the whole body.
286
+ const head = body.slice(0, 500);
287
+ if (head.indexOf(marker) !== -1) {
288
+ return body;
289
+ }
290
+
291
+ const header =
292
+ '<!-- gdd: auto-generated from Claude SKILL.md. ' +
293
+ display +
294
+ ' adapter -->\n\n';
295
+
296
+ // Preserve any leading blank lines — insert the header before the
297
+ // first non-blank line. (If body starts with blank lines, those are
298
+ // retained between header and first real content.)
299
+ const m = body.match(/^(\s*)([\s\S]*)$/);
300
+ /* istanbul ignore next — regex always matches non-null body. */
301
+ if (!m) return header + body;
302
+ return header + m[2];
303
+ }
304
+
305
+ // ---------------------------------------------------------------------------
306
+ // buildFrontmatter — name-prefix normalization + frontmatter re-emit
307
+ // ---------------------------------------------------------------------------
308
+
309
+ /**
310
+ * Re-emit a YAML frontmatter block. Behavior:
311
+ *
312
+ * - If `originalFrontmatter` is `null` or empty, emit a minimal block
313
+ * containing only `name: <runtimePrefix><skillName>`.
314
+ * - If `originalFrontmatter` is non-empty, rewrite its `name:` field
315
+ * to `<runtimePrefix><skillName>` (stripping any prior `gdd-` or
316
+ * `gsd-` prefix on the existing name to avoid `gdd-gdd-`-style
317
+ * duplication). All other fields round-trip verbatim — we do NOT
318
+ * parse YAML; we operate on the raw text with a line-by-line scan.
319
+ * - If the original has no `name:` field, prepend one.
320
+ *
321
+ * Returns a complete frontmatter string with leading/trailing `---`
322
+ * delimiters and a trailing newline (ready to concatenate with body).
323
+ *
324
+ * @param {string|null} originalFrontmatter
325
+ * @param {string} skillName the bare skill name (e.g. `'sample'`,
326
+ * `'explore'`) WITHOUT runtime prefix.
327
+ * @param {string} runtimePrefix e.g. `'gdd-'`.
328
+ * @returns {string}
329
+ */
330
+ function buildFrontmatter(originalFrontmatter, skillName, runtimePrefix) {
331
+ const prefix = String(runtimePrefix || '');
332
+ // Normalize input name: strip any prior gdd-/gsd- prefix (case-insensitive)
333
+ // so we never emit gdd-gdd-foo.
334
+ const bareName = String(skillName || '').replace(/^(gdd-|gsd-)/i, '');
335
+ const finalName = prefix + bareName;
336
+
337
+ if (!originalFrontmatter || originalFrontmatter.trim() === '') {
338
+ return '---\nname: ' + finalName + '\n---\n';
339
+ }
340
+
341
+ // Line-by-line rewrite of the `name:` field. We never touch description,
342
+ // tools, or any other field — they round-trip verbatim.
343
+ const lines = originalFrontmatter.split(/\r?\n/);
344
+ let nameSeen = false;
345
+ for (let i = 0; i < lines.length; i++) {
346
+ const m = lines[i].match(/^(\s*name\s*:\s*)(.*)$/);
347
+ if (m) {
348
+ nameSeen = true;
349
+ // Replace the value with `finalName`. Preserve surrounding quotes
350
+ // if the original value had them (very common in SKILL.md sources
351
+ // — `name: "gdd-help"`).
352
+ const quoted = m[2].match(/^["'](.*)["']\s*$/);
353
+ const replacement = quoted ? '"' + finalName + '"' : finalName;
354
+ lines[i] = m[1] + replacement;
355
+ break; // only rewrite the first `name:` occurrence
356
+ }
357
+ }
358
+ if (!nameSeen) {
359
+ // Prepend a `name:` line if the original had none.
360
+ lines.unshift('name: ' + finalName);
361
+ }
362
+
363
+ return '---\n' + lines.join('\n') + '\n---\n';
364
+ }
365
+
366
+ // ---------------------------------------------------------------------------
367
+ // Exports
368
+ // ---------------------------------------------------------------------------
369
+
370
+ module.exports = {
371
+ extractFrontmatterAndBody,
372
+ rewriteSlashRefs,
373
+ rewriteCodeFenceTools,
374
+ ensureAdapterHeader,
375
+ buildFrontmatter,
376
+ CODEX_TOOL_MAP,
377
+ };
@@ -0,0 +1,47 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * scripts/lib/install/converters/trae.cjs — Phase 28.7 (Plan 28.7-05).
5
+ *
6
+ * Trae SKILL.md converter. Translates Claude-shape source into Trae's
7
+ * expected shape:
8
+ *
9
+ * - Frontmatter `name:` normalized to `gdd-<skill>` (no double-prefix).
10
+ * - Slash references in prose pass through as `/gdd-<name>` —
11
+ * Trae accepts the Claude shape. Mixed-shape inputs are normalized
12
+ * via the runtime-slash module.
13
+ * - Tool names in code fences pass through unchanged — Trae accepts
14
+ * the Claude vocabulary (Read/Write/Bash/Edit/Grep/Glob).
15
+ * - A 1-line HTML adapter header is injected at the top of the body
16
+ * to record that this file was auto-generated from Claude source.
17
+ *
18
+ * Architecture ported from gsd-build/get-shit-done (MIT) — per Phase
19
+ * 28.7 D-02 (port architecture, not source). See NOTICE for upstream
20
+ * attribution. gsd-build's equivalent function is
21
+ * `convertClaudeCommandToTraeSkill` in bin/install.js; our modular
22
+ * factor delegates the actual rewrites to ./shared.cjs.
23
+ *
24
+ * Pure / side-effect-free: no fs, no env, no path. `convert` is a
25
+ * deterministic string → string transform.
26
+ */
27
+
28
+ const shared = require('./shared.cjs');
29
+
30
+ /**
31
+ * Convert Claude-source SKILL.md content for the Trae runtime.
32
+ *
33
+ * @param {string} content Full source SKILL.md content (frontmatter + body).
34
+ * @param {string} skillName The bare skill name (e.g. `'help'`, `'explore'`).
35
+ * @param {{ runtime?: string }} [opts] Optional context — `runtime` defaults
36
+ * to `'trae'`. Currently informational only.
37
+ * @returns {string}
38
+ */
39
+ function convert(content, skillName, opts) {
40
+ const { frontmatter, body } = shared.extractFrontmatterAndBody(content);
41
+ const fm = shared.buildFrontmatter(frontmatter, skillName, 'gdd-');
42
+ let out = shared.rewriteSlashRefs(body, 'trae');
43
+ out = shared.ensureAdapterHeader(out, 'Trae');
44
+ return fm + out;
45
+ }
46
+
47
+ module.exports = { convert };
@@ -0,0 +1,47 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * scripts/lib/install/converters/windsurf.cjs — Phase 28.7 (Plan 28.7-05).
5
+ *
6
+ * Windsurf SKILL.md converter. Translates Claude-shape source into
7
+ * Windsurf'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
+ * Windsurf accepts the Claude shape. Mixed-shape inputs are
12
+ * normalized via the runtime-slash module.
13
+ * - Tool names in code fences pass through unchanged — Windsurf
14
+ * accepts the Claude vocabulary (Read/Write/Bash/Edit/Grep/Glob).
15
+ * - A 1-line HTML adapter header is injected at the top of the body
16
+ * to record that this file was auto-generated from Claude source.
17
+ *
18
+ * Architecture ported from gsd-build/get-shit-done (MIT) — per Phase
19
+ * 28.7 D-02 (port architecture, not source). See NOTICE for upstream
20
+ * attribution. gsd-build's equivalent function is
21
+ * `convertClaudeCommandToWindsurfSkill` in bin/install.js; our modular
22
+ * factor delegates the actual rewrites to ./shared.cjs.
23
+ *
24
+ * Pure / side-effect-free: no fs, no env, no path. `convert` is a
25
+ * deterministic string → string transform.
26
+ */
27
+
28
+ const shared = require('./shared.cjs');
29
+
30
+ /**
31
+ * Convert Claude-source SKILL.md content for the Windsurf runtime.
32
+ *
33
+ * @param {string} content Full source SKILL.md content (frontmatter + body).
34
+ * @param {string} skillName The bare skill name (e.g. `'help'`, `'explore'`).
35
+ * @param {{ runtime?: string }} [opts] Optional context — `runtime` defaults
36
+ * to `'windsurf'`. Currently informational only.
37
+ * @returns {string}
38
+ */
39
+ function convert(content, skillName, opts) {
40
+ const { frontmatter, body } = shared.extractFrontmatterAndBody(content);
41
+ const fm = shared.buildFrontmatter(frontmatter, skillName, 'gdd-');
42
+ let out = shared.rewriteSlashRefs(body, 'windsurf');
43
+ out = shared.ensureAdapterHeader(out, 'Windsurf');
44
+ return fm + out;
45
+ }
46
+
47
+ module.exports = { convert };