@hegemonart/get-design-done 1.28.6 → 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.
@@ -0,0 +1,225 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * scripts/lib/install/runtime-homes.cjs — Phase 28.7 (Plan 28.7-01).
5
+ *
6
+ * Pure config-dir + skills-base resolver per runtime. Returns absolute paths
7
+ * to each runtime's global config directory and global skills directory,
8
+ * respecting env-var overrides + XDG conventions.
9
+ *
10
+ * Architecture ported from gsd-build/get-shit-done (MIT) — per Phase 28.7 D-02
11
+ * (port architecture, not source). See NOTICE for upstream attribution.
12
+ *
13
+ * Scope: the 14 GDD runtimes locked by Phase 24 D-02. Phase 28.7 D-03 + D-10
14
+ * keep `hermes` OUT of scope; the upstream `hermes` and `grok` branches are
15
+ * deliberately NOT ported.
16
+ *
17
+ * Pure / side-effect-free: no `fs.*` calls at any time, no `process.env`
18
+ * writes, no top-level work beyond `require('os')` + `require('path')`.
19
+ * Safe to require() at module-load time.
20
+ *
21
+ * Runtime-specific notes:
22
+ * opencode + kilo — XDG: honor `XDG_CONFIG_HOME` when the runtime's own
23
+ * env var is unset. Default: `~/.config/<runtime>`.
24
+ * antigravity — Nested under Gemini's home (`~/.gemini/antigravity`).
25
+ * windsurf — Nested under Codeium's home (`~/.codeium/windsurf`).
26
+ * cline — Rules-based runtime (D-09). `getGlobalSkillsBase('cline')`
27
+ * returns `null` so callers can route to the `.clinerules`
28
+ * embedding path instead of a skills/ tree.
29
+ */
30
+
31
+ const os = require('os');
32
+ const path = require('path');
33
+
34
+ /**
35
+ * Expand a leading `~` to `os.homedir()`. Returns the input unchanged if no
36
+ * leading tilde is present (including `null`/`undefined`/empty).
37
+ *
38
+ * @param {string} p
39
+ * @returns {string}
40
+ */
41
+ function expandTilde(p) {
42
+ if (!p) return p;
43
+ if (p === '~') return os.homedir();
44
+ if (p.startsWith('~/') || p.startsWith('~\\')) {
45
+ return path.join(os.homedir(), p.slice(2));
46
+ }
47
+ return p;
48
+ }
49
+
50
+ /**
51
+ * Return the global config base directory for the given runtime.
52
+ *
53
+ * Precedence per branch:
54
+ * 1. Runtime's dedicated env var (e.g. `CLAUDE_CONFIG_DIR`, `CODEX_HOME`),
55
+ * tilde-expanded if present.
56
+ * 2. XDG path for opencode + kilo (when `XDG_CONFIG_HOME` is set).
57
+ * 3. Home-relative default (`~/.claude`, `~/.codex`, etc.).
58
+ *
59
+ * Throws `RangeError` on unknown runtime IDs (loud-fail — typos in caller
60
+ * code should fail at the resolver, not silently fall back to Claude paths).
61
+ *
62
+ * @param {string} runtime
63
+ * @returns {string} Absolute path to the runtime's global config directory.
64
+ */
65
+ function getGlobalConfigDir(runtime) {
66
+ const home = os.homedir();
67
+ const env = process.env;
68
+
69
+ switch (runtime) {
70
+ // ── Claude Code ──────────────────────────────────────────────────────────
71
+ case 'claude':
72
+ return env.CLAUDE_CONFIG_DIR
73
+ ? expandTilde(env.CLAUDE_CONFIG_DIR)
74
+ : path.join(home, '.claude');
75
+
76
+ // ── OpenCode (XDG) ───────────────────────────────────────────────────────
77
+ case 'opencode': {
78
+ if (env.OPENCODE_CONFIG_DIR) return expandTilde(env.OPENCODE_CONFIG_DIR);
79
+ if (env.XDG_CONFIG_HOME) return path.join(expandTilde(env.XDG_CONFIG_HOME), 'opencode');
80
+ return path.join(home, '.config', 'opencode');
81
+ }
82
+
83
+ // ── Gemini CLI ───────────────────────────────────────────────────────────
84
+ case 'gemini':
85
+ return env.GEMINI_CONFIG_DIR
86
+ ? expandTilde(env.GEMINI_CONFIG_DIR)
87
+ : path.join(home, '.gemini');
88
+
89
+ // ── Kilo Code (XDG) ──────────────────────────────────────────────────────
90
+ case 'kilo': {
91
+ if (env.KILO_CONFIG_DIR) return expandTilde(env.KILO_CONFIG_DIR);
92
+ if (env.XDG_CONFIG_HOME) return path.join(expandTilde(env.XDG_CONFIG_HOME), 'kilo');
93
+ return path.join(home, '.config', 'kilo');
94
+ }
95
+
96
+ // ── Codex ────────────────────────────────────────────────────────────────
97
+ case 'codex':
98
+ return env.CODEX_HOME
99
+ ? expandTilde(env.CODEX_HOME)
100
+ : path.join(home, '.codex');
101
+
102
+ // ── Copilot (VS Code / CLI) ──────────────────────────────────────────────
103
+ case 'copilot':
104
+ return env.COPILOT_CONFIG_DIR
105
+ ? expandTilde(env.COPILOT_CONFIG_DIR)
106
+ : path.join(home, '.copilot');
107
+
108
+ // ── Cursor ───────────────────────────────────────────────────────────────
109
+ case 'cursor':
110
+ return env.CURSOR_CONFIG_DIR
111
+ ? expandTilde(env.CURSOR_CONFIG_DIR)
112
+ : path.join(home, '.cursor');
113
+
114
+ // ── Windsurf (nested under Codeium home) ─────────────────────────────────
115
+ case 'windsurf':
116
+ return env.WINDSURF_CONFIG_DIR
117
+ ? expandTilde(env.WINDSURF_CONFIG_DIR)
118
+ : path.join(home, '.codeium', 'windsurf');
119
+
120
+ // ── Antigravity (nested under Gemini home) ───────────────────────────────
121
+ case 'antigravity':
122
+ return env.ANTIGRAVITY_CONFIG_DIR
123
+ ? expandTilde(env.ANTIGRAVITY_CONFIG_DIR)
124
+ : path.join(home, '.gemini', 'antigravity');
125
+
126
+ // ── Augment ──────────────────────────────────────────────────────────────
127
+ case 'augment':
128
+ return env.AUGMENT_CONFIG_DIR
129
+ ? expandTilde(env.AUGMENT_CONFIG_DIR)
130
+ : path.join(home, '.augment');
131
+
132
+ // ── Trae ─────────────────────────────────────────────────────────────────
133
+ case 'trae':
134
+ return env.TRAE_CONFIG_DIR
135
+ ? expandTilde(env.TRAE_CONFIG_DIR)
136
+ : path.join(home, '.trae');
137
+
138
+ // ── Qwen Code ────────────────────────────────────────────────────────────
139
+ case 'qwen':
140
+ return env.QWEN_CONFIG_DIR
141
+ ? expandTilde(env.QWEN_CONFIG_DIR)
142
+ : path.join(home, '.qwen');
143
+
144
+ // ── CodeBuddy ────────────────────────────────────────────────────────────
145
+ case 'codebuddy':
146
+ return env.CODEBUDDY_CONFIG_DIR
147
+ ? expandTilde(env.CODEBUDDY_CONFIG_DIR)
148
+ : path.join(home, '.codebuddy');
149
+
150
+ // ── Cline (rules-based — see getGlobalSkillsBase) ────────────────────────
151
+ case 'cline':
152
+ return env.CLINE_CONFIG_DIR
153
+ ? expandTilde(env.CLINE_CONFIG_DIR)
154
+ : path.join(home, '.cline');
155
+
156
+ // ── Unknown ──────────────────────────────────────────────────────────────
157
+ default:
158
+ throw new RangeError(
159
+ `Unknown runtime "${runtime}". Known: claude, opencode, gemini, kilo, ` +
160
+ 'codex, copilot, cursor, windsurf, antigravity, augment, trae, qwen, ' +
161
+ 'codebuddy, cline.'
162
+ );
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Return the global skills base directory for the given runtime.
168
+ *
169
+ * - `cline` → `null` (Phase 28.7 D-09: rules-based, no skills dir; caller
170
+ * routes to the `.clinerules` embedding path).
171
+ * - All other 13 runtimes → `<configDir>/skills`.
172
+ *
173
+ * Note: gsd-build's upstream nests Hermes skills under `skills/gsd/<name>/`
174
+ * (their #2841). Hermes is NOT in our 14-runtime set (D-03 + D-10), so the
175
+ * nested layout is NOT ported.
176
+ *
177
+ * @param {string} runtime
178
+ * @returns {string|null} Absolute path to skills base, or `null` for cline.
179
+ */
180
+ function getGlobalSkillsBase(runtime) {
181
+ if (runtime === 'cline') return null;
182
+ const configDir = getGlobalConfigDir(runtime);
183
+ return path.join(configDir, 'skills');
184
+ }
185
+
186
+ /**
187
+ * Return the full path to a specific skill's directory for the given runtime.
188
+ * Returns `null` for runtimes that don't use a skills directory (cline).
189
+ *
190
+ * @param {string} runtime
191
+ * @param {string} skillName e.g. `'gdd-executor'`.
192
+ * @returns {string|null}
193
+ */
194
+ function getGlobalSkillDir(runtime, skillName) {
195
+ const base = getGlobalSkillsBase(runtime);
196
+ if (base === null) return null;
197
+ return path.join(base, skillName);
198
+ }
199
+
200
+ /**
201
+ * Return a human-readable display path for a global skill (for log messages).
202
+ * Replaces the home-directory prefix with `~` for readability.
203
+ *
204
+ * @param {string} runtime
205
+ * @param {string} skillName
206
+ * @returns {string}
207
+ */
208
+ function getGlobalSkillDisplayPath(runtime, skillName) {
209
+ const dir = getGlobalSkillDir(runtime, skillName);
210
+ if (!dir) return `(${runtime} does not use a skills directory)`;
211
+ const home = os.homedir();
212
+ if (dir === home) return '~';
213
+ if (dir.startsWith(home + path.sep)) {
214
+ return '~' + dir.slice(home.length);
215
+ }
216
+ return dir;
217
+ }
218
+
219
+ module.exports = {
220
+ expandTilde,
221
+ getGlobalConfigDir,
222
+ getGlobalSkillsBase,
223
+ getGlobalSkillDir,
224
+ getGlobalSkillDisplayPath,
225
+ };
@@ -0,0 +1,172 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * scripts/lib/install/runtime-slash.cjs — Phase 28.7 (Plan 28.7-03).
5
+ *
6
+ * Per-runtime slash-command surface emitter. Maps a bare command name
7
+ * (e.g. `'explore'`) to the runtime's expected slash-command shape:
8
+ * codex → `$gdd-explore` (shell-variable form)
9
+ * all other 13 runtimes → `/gdd-explore`
10
+ *
11
+ * The legacy colon form `/gdd:explore` is NEVER emitted — Phase 28.7
12
+ * standardizes on the dash form across the GDD brand. Inputs in legacy
13
+ * colon shape are accepted (and normalized) for backward compatibility.
14
+ *
15
+ * Architecture ported from gsd-build/get-shit-done (MIT) — per Phase
16
+ * 28.7 D-02 (port architecture, not source). See NOTICE for upstream
17
+ * attribution. Per Phase 28.7 D-11 this is the v1 surface; future
18
+ * per-runtime variations beyond codex shell-var may extend the switch.
19
+ *
20
+ * Idempotency: any prior shape (`/gdd-x`, `gdd-x`, `/gdd:x`, `gdd:x`,
21
+ * `$gdd-x`, `$gdd:x`) is stripped and re-emitted in canonical form for
22
+ * the target runtime. Argument tails (whitespace + remainder) round-trip
23
+ * untouched — this preserves Windows paths verbatim (Phase 28.6 lesson).
24
+ *
25
+ * Pure / side-effect-free at module load: only `require('fs')` and
26
+ * `require('path')` are taken at top level. `fs.readFileSync` is the
27
+ * sole `fs.*` caller, invoked exclusively from `resolveRuntime` when a
28
+ * project directory is provided — and wrapped in try/catch so malformed
29
+ * `.planning/config.json` cannot throw.
30
+ */
31
+
32
+ const fs = require('fs');
33
+ const path = require('path');
34
+
35
+ // ---------------------------------------------------------------------------
36
+ // formatGddSlash — pure rewrite of a single command token
37
+ // ---------------------------------------------------------------------------
38
+
39
+ /**
40
+ * Strip any known GDD prefix from the head of `s`.
41
+ *
42
+ * Matches (case-insensitive):
43
+ * `/gdd-`, `/gdd:`, `gdd-`, `gdd:`, `$gdd-`, `$gdd:`
44
+ *
45
+ * Returns the substring after the matched prefix, or the original string
46
+ * unchanged if no prefix matches.
47
+ *
48
+ * @param {string} s
49
+ * @returns {string}
50
+ */
51
+ function stripGddPrefix(s) {
52
+ const m = s.match(/^[/$]?gdd[-:]/i);
53
+ if (m) return s.slice(m[0].length);
54
+ return s;
55
+ }
56
+
57
+ /**
58
+ * Rewrite a command name to the slash-command shape for `runtime`.
59
+ *
60
+ * Behavior:
61
+ * - Non-string `commandName` returned unchanged (type-guard).
62
+ * - Empty string returned as `''`.
63
+ * - Any of `/gdd-`, `/gdd:`, `gdd-`, `gdd:`, `$gdd-`, `$gdd:` is
64
+ * stripped first (case-insensitive). Bare names pass through.
65
+ * - If the stripped result is empty / whitespace-only, return `''`
66
+ * (never re-emit `/gdd-` or `$gdd-` with no token — degenerate input).
67
+ * - Split on the first whitespace: the leading token is rewritten,
68
+ * everything after the first space (the argument tail) round-trips
69
+ * untouched. This preserves Windows paths in argument position.
70
+ * - codex → `$gdd-<token-lowercased><tail>`
71
+ * - all other runtimes → `/gdd-<token><tail>`
72
+ * - Unknown / falsy runtime → defaults to `'claude'` shape (`/gdd-`).
73
+ *
74
+ * @param {string} commandName e.g. `'explore'`, `'/gdd-debug'`, `'do x y'`.
75
+ * @param {string} [runtime] runtime ID; defaults to `'claude'`.
76
+ * @returns {string}
77
+ */
78
+ function formatGddSlash(commandName, runtime) {
79
+ // Type-guard: only operate on strings; pass everything else through.
80
+ if (typeof commandName !== 'string') return commandName;
81
+ if (commandName === '') return '';
82
+
83
+ // 1. Strip any prior GDD prefix (idempotent normalization).
84
+ const bare = stripGddPrefix(commandName);
85
+
86
+ // 2. Defensive: empty / whitespace-only token → empty (never `/gdd-`).
87
+ if (bare === '' || /^\s+$/.test(bare)) return '';
88
+
89
+ // 3. Split into leading token + argument tail. The tail (including its
90
+ // leading whitespace) round-trips untouched for Windows-path safety.
91
+ const m = bare.match(/^(\S+)(\s[\s\S]*)?$/);
92
+ /* istanbul ignore next — `bare` is non-empty + non-whitespace-only, so
93
+ this regex always matches; defensive fallback only. */
94
+ if (!m) return '';
95
+ const token = m[1];
96
+ const tail = m[2] || '';
97
+
98
+ // 4. Runtime-specific emission.
99
+ const rt = String(runtime || 'claude').toLowerCase();
100
+ if (rt === 'codex') {
101
+ return '$gdd-' + token.toLowerCase() + tail;
102
+ }
103
+ return '/gdd-' + token + tail;
104
+ }
105
+
106
+ // ---------------------------------------------------------------------------
107
+ // resolveRuntime — read-only runtime ID resolution
108
+ // ---------------------------------------------------------------------------
109
+
110
+ /**
111
+ * Resolve the active runtime ID for the given project directory.
112
+ *
113
+ * Precedence:
114
+ * 1. `process.env.GDD_RUNTIME` (lowercased) if truthy.
115
+ * 2. `<projectDir>/.planning/config.json#runtime` (lowercased) if the
116
+ * file exists, parses as JSON, and contains a non-empty `runtime`
117
+ * string. Malformed JSON, missing file, or missing key all fall
118
+ * through silently — this function MUST NOT throw on bad config.
119
+ * 3. Default `'claude'`.
120
+ *
121
+ * Side-effect-free: only `fs.readFileSync` (wrapped in try/catch), no
122
+ * env mutation, no writes.
123
+ *
124
+ * @param {string} [projectDir] absolute path to project root.
125
+ * @returns {string} runtime ID, always lowercase.
126
+ */
127
+ function resolveRuntime(projectDir) {
128
+ // 1. Env-var override
129
+ if (process.env.GDD_RUNTIME) {
130
+ return String(process.env.GDD_RUNTIME).toLowerCase();
131
+ }
132
+
133
+ // 2. .planning/config.json#runtime
134
+ if (projectDir) {
135
+ try {
136
+ const cfgPath = path.join(projectDir, '.planning', 'config.json');
137
+ const raw = fs.readFileSync(cfgPath, 'utf8');
138
+ const parsed = JSON.parse(raw);
139
+ if (parsed && typeof parsed.runtime === 'string' && parsed.runtime) {
140
+ return String(parsed.runtime).toLowerCase();
141
+ }
142
+ } catch {
143
+ /* fall through — malformed JSON / missing file / missing key */
144
+ }
145
+ }
146
+
147
+ // 3. Default
148
+ return 'claude';
149
+ }
150
+
151
+ // ---------------------------------------------------------------------------
152
+ // formatGddSlashFor — convenience that combines the two
153
+ // ---------------------------------------------------------------------------
154
+
155
+ /**
156
+ * Resolve the runtime for `projectDir` and rewrite `commandName` accordingly.
157
+ * Convenience wrapper for callers that already have a project directory in
158
+ * hand and don't want to wire `resolveRuntime` themselves.
159
+ *
160
+ * @param {string} projectDir
161
+ * @param {string} commandName
162
+ * @returns {string}
163
+ */
164
+ function formatGddSlashFor(projectDir, commandName) {
165
+ return formatGddSlash(commandName, resolveRuntime(projectDir));
166
+ }
167
+
168
+ module.exports = {
169
+ formatGddSlash,
170
+ resolveRuntime,
171
+ formatGddSlashFor,
172
+ };
@@ -7,15 +7,21 @@
7
7
  // (Phase 24 D-02). Two `kind`s exist:
8
8
  //
9
9
  // - `claude-marketplace` — register a marketplace entry + flip
10
- // `enabledPlugins[<name>@<marketplace>]` in settings.json. Today only
11
- // Claude Code uses this.
10
+ // `enabledPlugins[<plugin>@<marketplace>]` in settings.json. Claude
11
+ // Code only.
12
12
  //
13
- // - `agents-md` — drop a runtime-specific instructions file (AGENTS.md /
14
- // GEMINI.md) into the runtime's config directory. Most modern AI coding
15
- // CLIs follow this convention.
13
+ // - `multi-artifact` — per Phase 28.7: install one or more artifact kinds
14
+ // (skills, commands, agents) using runtime-artifact-layout.cjs +
15
+ // per-runtime converters at scripts/lib/install/converters/<runtime>.cjs.
16
+ // Replaces the broken Phase 24 `agents-md` placeholder which dropped a
17
+ // single AGENTS.md file per runtime (Phase 28.7 D-02 + D-08). The actual
18
+ // install logic now lives in installer.cjs → runtime-artifact-layout.cjs;
19
+ // `files:` is intentionally absent on these entries because destination
20
+ // paths are computed by the artifact-layout resolver.
16
21
  //
17
22
  // Adding a new runtime: append to RUNTIMES below, append the same id to the
18
- // alphabetised baseline at test-fixture/baselines/phase-24/runtimes.txt.
23
+ // alphabetised baseline at test-fixture/baselines/phase-24/runtimes.txt,
24
+ // and add a matching branch in runtime-artifact-layout.cjs (Phase 28.7).
19
25
 
20
26
  const REPO = 'hegemonart/get-design-done';
21
27
  const MARKETPLACE_NAME = 'get-design-done';
@@ -40,16 +46,14 @@ const RUNTIMES = Object.freeze([
40
46
  displayName: 'OpenCode',
41
47
  configDirEnv: 'OPENCODE_CONFIG_DIR',
42
48
  configDirFallback: '.config/opencode',
43
- kind: 'agents-md',
44
- files: ['AGENTS.md'],
49
+ kind: 'multi-artifact',
45
50
  },
46
51
  {
47
52
  id: 'gemini',
48
53
  displayName: 'Gemini CLI',
49
54
  configDirEnv: 'GEMINI_CONFIG_DIR',
50
55
  configDirFallback: '.gemini',
51
- kind: 'agents-md',
52
- files: ['GEMINI.md'],
56
+ kind: 'multi-artifact',
53
57
  // Phase 27 (Plan 27-11): peer-CLI delegation binary, ACP protocol.
54
58
  peerBinary: process.platform === 'win32' ? 'gemini.cmd' : 'gemini',
55
59
  },
@@ -58,16 +62,14 @@ const RUNTIMES = Object.freeze([
58
62
  displayName: 'Kilo Code',
59
63
  configDirEnv: 'KILO_CONFIG_DIR',
60
64
  configDirFallback: '.kilo',
61
- kind: 'agents-md',
62
- files: ['AGENTS.md'],
65
+ kind: 'multi-artifact',
63
66
  },
64
67
  {
65
68
  id: 'codex',
66
69
  displayName: 'OpenAI Codex CLI',
67
70
  configDirEnv: 'CODEX_HOME',
68
71
  configDirFallback: '.codex',
69
- kind: 'agents-md',
70
- files: ['AGENTS.md'],
72
+ kind: 'multi-artifact',
71
73
  // Phase 27 (Plan 27-11): peer-CLI delegation binary, ASP protocol.
72
74
  peerBinary: process.platform === 'win32' ? 'codex.cmd' : 'codex',
73
75
  },
@@ -76,8 +78,7 @@ const RUNTIMES = Object.freeze([
76
78
  displayName: 'GitHub Copilot CLI',
77
79
  configDirEnv: 'COPILOT_CONFIG_DIR',
78
80
  configDirFallback: '.copilot',
79
- kind: 'agents-md',
80
- files: ['AGENTS.md'],
81
+ kind: 'multi-artifact',
81
82
  // Phase 27 (Plan 27-11): peer-CLI delegation binary, ACP protocol.
82
83
  peerBinary: process.platform === 'win32' ? 'copilot.cmd' : 'copilot',
83
84
  },
@@ -86,8 +87,7 @@ const RUNTIMES = Object.freeze([
86
87
  displayName: 'Cursor',
87
88
  configDirEnv: 'CURSOR_CONFIG_DIR',
88
89
  configDirFallback: '.cursor',
89
- kind: 'agents-md',
90
- files: ['AGENTS.md'],
90
+ kind: 'multi-artifact',
91
91
  // Phase 27 (Plan 27-11): peer-CLI delegation binary, ACP protocol.
92
92
  peerBinary: process.platform === 'win32' ? 'cursor-agent.cmd' : 'cursor-agent',
93
93
  },
@@ -96,40 +96,35 @@ const RUNTIMES = Object.freeze([
96
96
  displayName: 'Windsurf',
97
97
  configDirEnv: 'WINDSURF_CONFIG_DIR',
98
98
  configDirFallback: '.windsurf',
99
- kind: 'agents-md',
100
- files: ['AGENTS.md'],
99
+ kind: 'multi-artifact',
101
100
  },
102
101
  {
103
102
  id: 'antigravity',
104
103
  displayName: 'Antigravity',
105
104
  configDirEnv: 'ANTIGRAVITY_CONFIG_DIR',
106
105
  configDirFallback: '.antigravity',
107
- kind: 'agents-md',
108
- files: ['AGENTS.md'],
106
+ kind: 'multi-artifact',
109
107
  },
110
108
  {
111
109
  id: 'augment',
112
110
  displayName: 'Augment',
113
111
  configDirEnv: 'AUGMENT_CONFIG_DIR',
114
112
  configDirFallback: '.augment',
115
- kind: 'agents-md',
116
- files: ['AGENTS.md'],
113
+ kind: 'multi-artifact',
117
114
  },
118
115
  {
119
116
  id: 'trae',
120
117
  displayName: 'Trae',
121
118
  configDirEnv: 'TRAE_CONFIG_DIR',
122
119
  configDirFallback: '.trae',
123
- kind: 'agents-md',
124
- files: ['AGENTS.md'],
120
+ kind: 'multi-artifact',
125
121
  },
126
122
  {
127
123
  id: 'qwen',
128
124
  displayName: 'Qwen Code',
129
125
  configDirEnv: 'QWEN_CONFIG_DIR',
130
126
  configDirFallback: '.qwen',
131
- kind: 'agents-md',
132
- files: ['AGENTS.md'],
127
+ kind: 'multi-artifact',
133
128
  // Phase 27 (Plan 27-11): peer-CLI delegation binary, ACP protocol.
134
129
  peerBinary: process.platform === 'win32' ? 'qwen.cmd' : 'qwen',
135
130
  },
@@ -138,16 +133,14 @@ const RUNTIMES = Object.freeze([
138
133
  displayName: 'CodeBuddy',
139
134
  configDirEnv: 'CODEBUDDY_CONFIG_DIR',
140
135
  configDirFallback: '.codebuddy',
141
- kind: 'agents-md',
142
- files: ['AGENTS.md'],
136
+ kind: 'multi-artifact',
143
137
  },
144
138
  {
145
139
  id: 'cline',
146
140
  displayName: 'Cline',
147
141
  configDirEnv: 'CLINE_CONFIG_DIR',
148
142
  configDirFallback: '.cline',
149
- kind: 'agents-md',
150
- files: ['AGENTS.md'],
143
+ kind: 'multi-artifact',
151
144
  },
152
145
  ]);
153
146