@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,388 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * scripts/lib/install/doctor-codex-plugin.cjs — Phase 28.8 (Plan 28-8-C2).
5
+ *
6
+ * Codex Plugin doctor-mode reporter. Pure, read-only function that
7
+ * surfaces the maintainer's local Codex Plugin readiness state to
8
+ * `scripts/install.cjs --doctor`.
9
+ *
10
+ * Phase 28.8 D-03: Codex install-by-URL works today — `codex plugin
11
+ * marketplace add hegemonart/get-design-done` is a single command per
12
+ * developers.openai.com/codex/plugins/build. This reporter inspects the
13
+ * local repo for the artifacts that the Codex CLI consumes during that
14
+ * single step:
15
+ * - `.codex-plugin/plugin.json` (manifest, built by Plan 28-8-C1)
16
+ * - `.claude-plugin/marketplace.json` (catalog reused per D-14)
17
+ *
18
+ * Phase 28.8 D-10: tmpdir-safe. Read-only fs access; no writes anywhere;
19
+ * no `codex` CLI invocation; no access to `~/.codex/`. The cache install
20
+ * path is COMPUTED (pure string composition via `os.homedir()`), NOT
21
+ * verified. The maintainer verifies the cache after running the field-
22
+ * test command on a Codex-installed machine (see
23
+ * docs/codex-plugin-field-test.md).
24
+ *
25
+ * Phase 28.8 D-14: the `.claude-plugin/marketplace.json` catalog file is
26
+ * reused from Claude Code's marketplace per Codex's legacy-compatible
27
+ * catalog path. Whenever the catalog is present, `reusedFromClaude` is
28
+ * true — there is no separate Codex-specific catalog artifact.
29
+ *
30
+ * Phase 28.8 D-16: Codex is single-step (D-03). The multi-step pattern
31
+ * is Cursor Marketplace's domain (see doctor-cursor-marketplace.cjs).
32
+ * No review-window state machine here — verdict is binary:
33
+ * `ready-to-install` or `manifest-only-not-ready`.
34
+ *
35
+ * Design pattern (mirrors doctor-cursor-marketplace.cjs from Plan B2):
36
+ * - `checkCodexPlugin(projectRoot)` returns a structured result object.
37
+ * - `renderCodexPluginSection(result)` formats it as text.
38
+ * - `computeCacheSimulationPath(...)` is pure string composition.
39
+ *
40
+ * Exports:
41
+ * - `checkCodexPlugin(projectRoot)` — structured readiness status.
42
+ * - `computeCacheSimulationPath(marketplaceName, pluginName, version)` —
43
+ * pure path composition; no fs access.
44
+ * - `renderCodexPluginSection(result)` — text formatter for the doctor
45
+ * section.
46
+ * - `MARKETPLACE_NAME` / `PLUGIN_NAME` / `MANIFEST_REL_PATH` /
47
+ * `CATALOG_REL_PATH` — exposed for test cross-checks.
48
+ */
49
+
50
+ const fs = require('node:fs');
51
+ const os = require('node:os');
52
+ const path = require('node:path');
53
+
54
+ const MARKETPLACE_NAME = 'get-design-done';
55
+ const PLUGIN_NAME = 'get-design-done';
56
+ const MANIFEST_REL_PATH = '.codex-plugin/plugin.json';
57
+ const CATALOG_REL_PATH = '.claude-plugin/marketplace.json';
58
+
59
+ // Sentinel rendered when neither manifest nor package.json yields a version.
60
+ const VERSION_PLACEHOLDER = '<version-from-package.json>';
61
+
62
+ // Reuse C1's required-fields tuple. Lazy-require keeps the doctor module
63
+ // independent of the converter's runtime cost when only the formatter is
64
+ // imported (e.g., for a unit test of `computeCacheSimulationPath`).
65
+ function loadConverterRequiredFields() {
66
+ // eslint-disable-next-line global-require
67
+ const c1 = require('./converters/codex-plugin.cjs');
68
+ return c1.MANIFEST_REQUIRED_FIELDS;
69
+ }
70
+
71
+ /**
72
+ * Validate a parsed `.codex-plugin/plugin.json` object against the C1
73
+ * spec (required fields + kebab-case name + semver version). Returns
74
+ * `{valid, errors}` — never throws. Mirrors the validateManifest helper
75
+ * in doctor-cursor-marketplace.cjs but uses Codex schema rules.
76
+ *
77
+ * @param {*} parsed Parsed JSON value.
78
+ * @returns {{ valid: boolean, errors: string[] }}
79
+ */
80
+ function validateCodexManifest(parsed) {
81
+ const errors = [];
82
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
83
+ return { valid: false, errors: ['manifest is not a JSON object'] };
84
+ }
85
+
86
+ const required = loadConverterRequiredFields();
87
+ for (const field of required) {
88
+ if (parsed[field] === undefined || parsed[field] === null) {
89
+ errors.push(`missing required field "${field}"`);
90
+ }
91
+ }
92
+
93
+ if (parsed.name !== undefined && parsed.name !== null) {
94
+ if (typeof parsed.name !== 'string' || parsed.name.length === 0) {
95
+ errors.push('name must be a non-empty string');
96
+ } else if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(parsed.name)) {
97
+ errors.push('name must be kebab-case (lowercase letters, digits, single hyphens)');
98
+ }
99
+ }
100
+
101
+ if (parsed.version !== undefined && parsed.version !== null) {
102
+ if (typeof parsed.version !== 'string' || !/^\d+\.\d+\.\d+/.test(parsed.version)) {
103
+ errors.push('version must be semver-shaped (x.y.z)');
104
+ }
105
+ }
106
+
107
+ if (parsed.description !== undefined && parsed.description !== null) {
108
+ if (typeof parsed.description !== 'string' || parsed.description.length === 0) {
109
+ errors.push('description must be a non-empty string');
110
+ }
111
+ }
112
+
113
+ return { valid: errors.length === 0, errors };
114
+ }
115
+
116
+ /**
117
+ * Safely read + parse a JSON file. Returns `{exists, parsed, error}`.
118
+ * @param {string} filePath
119
+ * @returns {{ exists: boolean, parsed: *, error: string|null }}
120
+ */
121
+ function readJsonFileSafe(filePath) {
122
+ let raw;
123
+ try {
124
+ raw = fs.readFileSync(filePath, 'utf8');
125
+ } catch (e) {
126
+ if (e && e.code === 'ENOENT') {
127
+ return { exists: false, parsed: null, error: null };
128
+ }
129
+ return { exists: false, parsed: null, error: 'read failed: ' + e.message };
130
+ }
131
+ try {
132
+ return { exists: true, parsed: JSON.parse(raw), error: null };
133
+ } catch (e) {
134
+ return { exists: true, parsed: null, error: 'JSON parse error: ' + e.message };
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Compute the install cache path WITHOUT touching the filesystem outside
140
+ * `projectRoot` (or anywhere, actually — this is pure string composition).
141
+ * The path schema is documented in research § Plugin cache layout:
142
+ *
143
+ * ~/.codex/plugins/cache/$MARKETPLACE_NAME/$PLUGIN_NAME/$VERSION/
144
+ *
145
+ * Per D-10 we do NOT verify this path exists — `codex` CLI may not be
146
+ * installed locally. The maintainer field-test (post-merge, on a Codex-
147
+ * installed machine) is the only place this path is actually checked.
148
+ *
149
+ * @param {string} marketplaceName Catalog `.name` field (default
150
+ * 'get-design-done' for GDD).
151
+ * @param {string} pluginName Manifest `.name` field (same as
152
+ * marketplaceName for GDD).
153
+ * @param {string|null|undefined} version Manifest `.version` field, or
154
+ * null/undefined to render the
155
+ * `<version-from-package.json>`
156
+ * placeholder.
157
+ * @returns {string} Absolute path with `~` expanded to
158
+ * `os.homedir()`. Forward slashes per
159
+ * Codex docs convention.
160
+ */
161
+ function computeCacheSimulationPath(marketplaceName, pluginName, version) {
162
+ const home = os.homedir().replace(/\\/g, '/');
163
+ const ver = (typeof version === 'string' && version.length > 0)
164
+ ? version
165
+ : VERSION_PLACEHOLDER;
166
+ return home + '/.codex/plugins/cache/' + marketplaceName + '/' + pluginName + '/' + ver + '/';
167
+ }
168
+
169
+ /**
170
+ * Read-only Codex Plugin readiness reporter. Inspects `projectRoot` for
171
+ * the manifest + catalog artifacts and returns a structured verdict.
172
+ *
173
+ * No writes, no network, no `codex` CLI invocation. Tmpdir-safe per D-10.
174
+ *
175
+ * @param {string} projectRoot Path to inspect.
176
+ * @returns {{
177
+ * manifest: {
178
+ * present: boolean,
179
+ * path: string,
180
+ * valid: boolean | null,
181
+ * version: string | null,
182
+ * errors: string[],
183
+ * },
184
+ * catalog: {
185
+ * present: boolean,
186
+ * path: string,
187
+ * referencesCodexPlugin: boolean,
188
+ * reusedFromClaude: boolean,
189
+ * },
190
+ * cacheSimulation: {
191
+ * path: string,
192
+ * verified: false,
193
+ * note: string,
194
+ * },
195
+ * verdict: 'ready-to-install' | 'manifest-only-not-ready',
196
+ * verdictReasons: string[],
197
+ * }}
198
+ */
199
+ function checkCodexPlugin(projectRoot) {
200
+ if (typeof projectRoot !== 'string' || projectRoot.length === 0) {
201
+ throw new Error('checkCodexPlugin: projectRoot is required');
202
+ }
203
+
204
+ const manifestPath = path.join(projectRoot, MANIFEST_REL_PATH);
205
+ const catalogPath = path.join(projectRoot, CATALOG_REL_PATH);
206
+ const pkgPath = path.join(projectRoot, 'package.json');
207
+
208
+ const manifestRead = readJsonFileSafe(manifestPath);
209
+ const catalogRead = readJsonFileSafe(catalogPath);
210
+ const pkgRead = readJsonFileSafe(pkgPath);
211
+
212
+ // ── Manifest ────────────────────────────────────────────────────────
213
+ const manifest = {
214
+ present: false,
215
+ path: manifestPath,
216
+ valid: null,
217
+ version: null,
218
+ errors: [],
219
+ };
220
+
221
+ if (manifestRead.exists) {
222
+ manifest.present = true;
223
+ if (manifestRead.error) {
224
+ manifest.valid = false;
225
+ manifest.errors = [manifestRead.error];
226
+ } else {
227
+ const validation = validateCodexManifest(manifestRead.parsed);
228
+ manifest.valid = validation.valid;
229
+ manifest.errors = validation.errors;
230
+ if (manifestRead.parsed && typeof manifestRead.parsed.version === 'string') {
231
+ manifest.version = manifestRead.parsed.version;
232
+ }
233
+ }
234
+ }
235
+
236
+ // ── Catalog ─────────────────────────────────────────────────────────
237
+ const catalog = {
238
+ present: false,
239
+ path: catalogPath,
240
+ referencesCodexPlugin: false,
241
+ reusedFromClaude: false,
242
+ };
243
+
244
+ if (catalogRead.exists && !catalogRead.error
245
+ && catalogRead.parsed && typeof catalogRead.parsed === 'object') {
246
+ catalog.present = true;
247
+ catalog.reusedFromClaude = true;
248
+ // Reference check: any entry in `plugins[]` with name === manifest.name
249
+ // (or PLUGIN_NAME if manifest absent / unparsed) signals an explicit
250
+ // reference. Per D-14 the catalog is reused regardless.
251
+ const refName = (manifestRead.exists
252
+ && manifestRead.parsed
253
+ && typeof manifestRead.parsed.name === 'string'
254
+ && manifestRead.parsed.name.length > 0)
255
+ ? manifestRead.parsed.name
256
+ : PLUGIN_NAME;
257
+ if (Array.isArray(catalogRead.parsed.plugins)) {
258
+ catalog.referencesCodexPlugin = catalogRead.parsed.plugins.some(
259
+ (entry) => entry && typeof entry === 'object' && entry.name === refName
260
+ );
261
+ }
262
+ } else if (catalogRead.exists && catalogRead.error) {
263
+ // Malformed catalog — mark present but unreusable. Keep
264
+ // referencesCodexPlugin false. reusedFromClaude stays false since
265
+ // we couldn't actually parse it. This is a Rule 1 safety: the
266
+ // doctor should not lie about reusable catalogs.
267
+ catalog.present = true;
268
+ catalog.reusedFromClaude = false;
269
+ }
270
+
271
+ // ── Version (prefer manifest, fall back to package.json) ───────────
272
+ let resolvedVersion = manifest.version;
273
+ if (!resolvedVersion
274
+ && pkgRead.exists && !pkgRead.error
275
+ && pkgRead.parsed && typeof pkgRead.parsed.version === 'string') {
276
+ resolvedVersion = pkgRead.parsed.version;
277
+ }
278
+
279
+ // ── Cache simulation (computed, never verified) ─────────────────────
280
+ const cacheSimulation = {
281
+ path: computeCacheSimulationPath(MARKETPLACE_NAME, PLUGIN_NAME, resolvedVersion),
282
+ verified: false,
283
+ note: 'codex CLI may not be installed locally — path computed not verified',
284
+ };
285
+
286
+ // ── Verdict ─────────────────────────────────────────────────────────
287
+ const verdictReasons = [];
288
+ if (!manifest.present) {
289
+ verdictReasons.push('manifest absent');
290
+ } else if (manifest.valid === false) {
291
+ if (manifest.errors.length > 0 && /JSON parse error/.test(manifest.errors[0])) {
292
+ verdictReasons.push('manifest JSON parse error: ' + manifest.errors[0].replace(/^JSON parse error:\s*/, ''));
293
+ } else {
294
+ verdictReasons.push('manifest schema invalid: ' + (manifest.errors[0] || 'unknown reason'));
295
+ }
296
+ }
297
+ if (!catalog.present) {
298
+ verdictReasons.push('catalog absent');
299
+ }
300
+
301
+ const verdict = (manifest.present && manifest.valid === true && catalog.present)
302
+ ? 'ready-to-install'
303
+ : 'manifest-only-not-ready';
304
+
305
+ return {
306
+ manifest,
307
+ catalog,
308
+ cacheSimulation,
309
+ verdict,
310
+ verdictReasons,
311
+ };
312
+ }
313
+
314
+ /**
315
+ * Render the inspection result as the doctor section text. Pure — no IO.
316
+ *
317
+ * Output shape (per plan <interfaces>):
318
+ *
319
+ * Codex Plugin status
320
+ * manifest .codex-plugin/plugin.json: present (version 1.28.8) — schema valid
321
+ * catalog .claude-plugin/marketplace.json: present — referenced by codex-plugin per D-14 (legacy-compatible catalog reuse)
322
+ * install path (computed, not verified): ~/.codex/plugins/cache/get-design-done/get-design-done/1.28.8/
323
+ * verdict: ready-to-install
324
+ *
325
+ * @param {ReturnType<checkCodexPlugin>} result
326
+ * @returns {string} Multi-line text ending with a trailing newline.
327
+ */
328
+ function renderCodexPluginSection(result) {
329
+ if (!result || typeof result !== 'object') {
330
+ throw new Error('renderCodexPluginSection: result is required');
331
+ }
332
+
333
+ const lines = ['Codex Plugin status'];
334
+
335
+ // Manifest line
336
+ let manifestState;
337
+ if (!result.manifest.present) {
338
+ manifestState = 'absent';
339
+ } else if (result.manifest.errors.length > 0
340
+ && /^JSON parse error/.test(result.manifest.errors[0])) {
341
+ manifestState = 'present — ' + result.manifest.errors[0];
342
+ } else {
343
+ const ver = result.manifest.version
344
+ ? '(version ' + result.manifest.version + ')'
345
+ : '(version unknown)';
346
+ if (result.manifest.valid === true) {
347
+ manifestState = 'present ' + ver + ' — schema valid';
348
+ } else {
349
+ const firstErr = result.manifest.errors[0] || 'unknown error';
350
+ manifestState = 'present ' + ver + ' — schema invalid: ' + firstErr;
351
+ }
352
+ }
353
+ lines.push(' manifest .codex-plugin/plugin.json: ' + manifestState);
354
+
355
+ // Catalog line
356
+ let catalogState;
357
+ if (!result.catalog.present) {
358
+ catalogState = 'absent';
359
+ } else if (result.catalog.referencesCodexPlugin) {
360
+ catalogState = 'present — referenced by codex-plugin per D-14 (legacy-compatible catalog reuse)';
361
+ } else {
362
+ catalogState = 'present — would be reused per D-14 (legacy-compatible catalog reuse)';
363
+ }
364
+ lines.push(' catalog .claude-plugin/marketplace.json: ' + catalogState);
365
+
366
+ // Install path line — always shows the "computed, not verified" guarantee
367
+ lines.push(' install path (computed, not verified): ' + result.cacheSimulation.path);
368
+
369
+ // Verdict line — parenthetical reasons only when non-ready
370
+ let verdictLine = ' verdict: ' + result.verdict;
371
+ if (result.verdict !== 'ready-to-install' && result.verdictReasons.length > 0) {
372
+ verdictLine += ' (' + result.verdictReasons.join('; ') + ')';
373
+ }
374
+ lines.push(verdictLine);
375
+
376
+ return lines.join('\n') + '\n';
377
+ }
378
+
379
+ module.exports = {
380
+ checkCodexPlugin,
381
+ computeCacheSimulationPath,
382
+ renderCodexPluginSection,
383
+ validateCodexManifest,
384
+ MARKETPLACE_NAME,
385
+ PLUGIN_NAME,
386
+ MANIFEST_REL_PATH,
387
+ CATALOG_REL_PATH,
388
+ };