@hegemonart/get-design-done 1.0.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 (144) hide show
  1. package/.claude-plugin/marketplace.json +63 -0
  2. package/.claude-plugin/plugin.json +54 -0
  3. package/CHANGELOG.md +221 -0
  4. package/LICENSE +21 -0
  5. package/README.md +724 -0
  6. package/SKILL.md +232 -0
  7. package/agents/README.md +226 -0
  8. package/agents/a11y-mapper.md +118 -0
  9. package/agents/component-taxonomy-mapper.md +88 -0
  10. package/agents/design-advisor.md +139 -0
  11. package/agents/design-assumptions-analyzer.md +171 -0
  12. package/agents/design-auditor.md +383 -0
  13. package/agents/design-context-builder.md +544 -0
  14. package/agents/design-context-checker-gate.md +90 -0
  15. package/agents/design-context-checker.md +260 -0
  16. package/agents/design-discussant.md +98 -0
  17. package/agents/design-doc-writer.md +229 -0
  18. package/agents/design-executor.md +452 -0
  19. package/agents/design-figma-writer.md +302 -0
  20. package/agents/design-fixer.md +180 -0
  21. package/agents/design-integration-checker-gate.md +93 -0
  22. package/agents/design-integration-checker.md +326 -0
  23. package/agents/design-pattern-mapper.md +206 -0
  24. package/agents/design-phase-researcher.md +229 -0
  25. package/agents/design-plan-checker.md +164 -0
  26. package/agents/design-planner.md +352 -0
  27. package/agents/design-reflector.md +175 -0
  28. package/agents/design-research-synthesizer.md +127 -0
  29. package/agents/design-verifier-gate.md +97 -0
  30. package/agents/design-verifier.md +605 -0
  31. package/agents/gdd-graphify-sync.md +100 -0
  32. package/agents/gdd-intel-updater.md +88 -0
  33. package/agents/gdd-learnings-extractor.md +85 -0
  34. package/agents/motion-mapper.md +103 -0
  35. package/agents/token-mapper.md +103 -0
  36. package/agents/visual-hierarchy-mapper.md +95 -0
  37. package/connections/chromatic.md +247 -0
  38. package/connections/claude-design.md +190 -0
  39. package/connections/connections.md +218 -0
  40. package/connections/figma-writer.md +139 -0
  41. package/connections/figma.md +146 -0
  42. package/connections/graphify.md +197 -0
  43. package/connections/pinterest.md +153 -0
  44. package/connections/preview.md +173 -0
  45. package/connections/refero.md +189 -0
  46. package/connections/storybook.md +280 -0
  47. package/hooks/budget-enforcer.js +318 -0
  48. package/hooks/context-exhaustion.js +127 -0
  49. package/hooks/gdd-read-injection-scanner.js +44 -0
  50. package/hooks/hooks.json +44 -0
  51. package/package.json +60 -0
  52. package/reference/BRANCH-PROTECTION.md +65 -0
  53. package/reference/DEPRECATIONS.md +41 -0
  54. package/reference/STATE-TEMPLATE.md +200 -0
  55. package/reference/accessibility.md +190 -0
  56. package/reference/anti-patterns.md +336 -0
  57. package/reference/audit-scoring.md +205 -0
  58. package/reference/checklists.md +137 -0
  59. package/reference/config-schema.md +319 -0
  60. package/reference/debugger-philosophy.md +32 -0
  61. package/reference/heuristics.md +201 -0
  62. package/reference/intel-schema.md +266 -0
  63. package/reference/model-prices.md +37 -0
  64. package/reference/model-tiers.md +118 -0
  65. package/reference/motion.md +285 -0
  66. package/reference/parallelism-rules.md +108 -0
  67. package/reference/priority-matrix.md +31 -0
  68. package/reference/project-skills-guide.md +42 -0
  69. package/reference/review-format.md +107 -0
  70. package/reference/schemas/config.schema.json +41 -0
  71. package/reference/schemas/hooks.schema.json +55 -0
  72. package/reference/schemas/intel.schema.json +191 -0
  73. package/reference/schemas/marketplace.schema.json +72 -0
  74. package/reference/schemas/plugin.schema.json +59 -0
  75. package/reference/shared-preamble.md +82 -0
  76. package/reference/typography.md +229 -0
  77. package/scripts/aggregate-agent-metrics.js +144 -0
  78. package/scripts/apply-branch-protection.sh +75 -0
  79. package/scripts/bootstrap-manifest.txt +3 -0
  80. package/scripts/bootstrap.sh +80 -0
  81. package/scripts/build-intel.cjs +458 -0
  82. package/scripts/detect-stale-refs.cjs +101 -0
  83. package/scripts/extract-changelog-section.cjs +57 -0
  84. package/scripts/release-smoke-test.cjs +169 -0
  85. package/scripts/rollback-release.sh +42 -0
  86. package/scripts/run-injection-scanner-ci.cjs +92 -0
  87. package/scripts/validate-frontmatter.cjs +68 -0
  88. package/scripts/validate-schemas.cjs +225 -0
  89. package/scripts/verify-version-sync.cjs +30 -0
  90. package/skills/add-backlog/SKILL.md +47 -0
  91. package/skills/analyze-dependencies/SKILL.md +184 -0
  92. package/skills/apply-reflections/SKILL.md +112 -0
  93. package/skills/audit/SKILL.md +54 -0
  94. package/skills/brief/SKILL.md +75 -0
  95. package/skills/cache-manager/SKILL.md +120 -0
  96. package/skills/compare/SKILL.md +322 -0
  97. package/skills/complete-cycle/SKILL.md +33 -0
  98. package/skills/darkmode/SKILL.md +331 -0
  99. package/skills/debug/SKILL.md +38 -0
  100. package/skills/design/SKILL.md +281 -0
  101. package/skills/discover/SKILL.md +172 -0
  102. package/skills/discuss/SKILL.md +67 -0
  103. package/skills/do/SKILL.md +45 -0
  104. package/skills/explore/SKILL.md +109 -0
  105. package/skills/extract-learnings/SKILL.md +98 -0
  106. package/skills/fast/SKILL.md +44 -0
  107. package/skills/figma-write/SKILL.md +40 -0
  108. package/skills/graphify/SKILL.md +48 -0
  109. package/skills/health/SKILL.md +48 -0
  110. package/skills/help/SKILL.md +76 -0
  111. package/skills/list-assumptions/SKILL.md +60 -0
  112. package/skills/map/SKILL.md +112 -0
  113. package/skills/new-cycle/SKILL.md +35 -0
  114. package/skills/new-project/SKILL.md +53 -0
  115. package/skills/next/SKILL.md +42 -0
  116. package/skills/note/SKILL.md +47 -0
  117. package/skills/optimize/SKILL.md +120 -0
  118. package/skills/pause/SKILL.md +41 -0
  119. package/skills/plan/SKILL.md +251 -0
  120. package/skills/plant-seed/SKILL.md +47 -0
  121. package/skills/pr-branch/SKILL.md +31 -0
  122. package/skills/progress/SKILL.md +60 -0
  123. package/skills/quick/SKILL.md +43 -0
  124. package/skills/reapply-patches/SKILL.md +31 -0
  125. package/skills/reflect/SKILL.md +73 -0
  126. package/skills/resume/SKILL.md +37 -0
  127. package/skills/review-backlog/SKILL.md +45 -0
  128. package/skills/router/SKILL.md +67 -0
  129. package/skills/scan/SKILL.md +721 -0
  130. package/skills/settings/SKILL.md +78 -0
  131. package/skills/ship/SKILL.md +31 -0
  132. package/skills/sketch/SKILL.md +78 -0
  133. package/skills/sketch-wrap-up/SKILL.md +88 -0
  134. package/skills/skill-manifest/SKILL.md +79 -0
  135. package/skills/spike/SKILL.md +67 -0
  136. package/skills/spike-wrap-up/SKILL.md +81 -0
  137. package/skills/stats/SKILL.md +50 -0
  138. package/skills/style/SKILL.md +193 -0
  139. package/skills/synthesize/SKILL.md +93 -0
  140. package/skills/todo/SKILL.md +54 -0
  141. package/skills/undo/SKILL.md +30 -0
  142. package/skills/update/SKILL.md +36 -0
  143. package/skills/verify/SKILL.md +452 -0
  144. package/skills/warm-cache/SKILL.md +113 -0
@@ -0,0 +1,169 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ // release-smoke-test.cjs — release-time smoke test per D-22.
4
+ //
5
+ // Validates that the freshly-checked-out tag produces a consistent plugin
6
+ // surface. Runs deterministic (non-LLM) portions of /gdd:explore against
7
+ // test-fixture/src/ in an isolated temp dir and diffs resulting artifacts
8
+ // against the provided baseline directory. Exit code:
9
+ // 0 — zero diffs, zero missing artifacts
10
+ // 1 — one or more diffs or missing artifacts
11
+ // 2 — baseline not found / argument error
12
+ //
13
+ // Does NOT invoke the `claude` CLI (unavailable on stock GitHub runners).
14
+ // The LLM-dependent portions of /gdd:explore are intentionally out of scope;
15
+ // this smoke test covers the intel builder + static analysis surface.
16
+ //
17
+ // Usage:
18
+ // node scripts/release-smoke-test.cjs --baseline <path>
19
+ // node scripts/release-smoke-test.cjs --baseline test-fixture/baselines/phase-13
20
+ // node scripts/release-smoke-test.cjs --baseline <path> --keep # keep tmp dir
21
+
22
+ const fs = require('fs');
23
+ const path = require('path');
24
+ const os = require('os');
25
+ const { spawnSync } = require('child_process');
26
+
27
+ const args = process.argv.slice(2);
28
+ const baselineIdx = args.indexOf('--baseline');
29
+ const KEEP = args.includes('--keep');
30
+
31
+ if (baselineIdx < 0 || !args[baselineIdx + 1]) {
32
+ console.error('Usage: node scripts/release-smoke-test.cjs --baseline <path> [--keep]');
33
+ process.exit(2);
34
+ }
35
+
36
+ const baselineDir = path.resolve(args[baselineIdx + 1]);
37
+ if (!fs.existsSync(baselineDir) || !fs.statSync(baselineDir).isDirectory()) {
38
+ console.error(`baseline not found: ${baselineDir}`);
39
+ process.exit(2);
40
+ }
41
+
42
+ const REPO_ROOT = path.resolve(__dirname, '..');
43
+ const FIXTURE_SRC = path.join(REPO_ROOT, 'test-fixture', 'src');
44
+
45
+ if (!fs.existsSync(FIXTURE_SRC)) {
46
+ console.error(`fixture not found: ${FIXTURE_SRC}`);
47
+ process.exit(2);
48
+ }
49
+
50
+ const tmpDir = path.join(os.tmpdir(), `gdd-smoke-${Date.now()}`);
51
+ fs.mkdirSync(tmpDir, { recursive: true });
52
+
53
+ function copyRecursive(src, dst) {
54
+ fs.mkdirSync(dst, { recursive: true });
55
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
56
+ const s = path.join(src, entry.name);
57
+ const d = path.join(dst, entry.name);
58
+ if (entry.isDirectory()) copyRecursive(s, d);
59
+ else fs.copyFileSync(s, d);
60
+ }
61
+ }
62
+
63
+ function cleanup() {
64
+ if (KEEP) {
65
+ console.log(`(kept) ${tmpDir}`);
66
+ return;
67
+ }
68
+ try {
69
+ fs.rmSync(tmpDir, { recursive: true, force: true });
70
+ } catch (_) {
71
+ /* best effort */
72
+ }
73
+ }
74
+
75
+ process.on('exit', cleanup);
76
+ process.on('SIGINT', () => {
77
+ cleanup();
78
+ process.exit(130);
79
+ });
80
+
81
+ // ── Step 1: Copy fixture into isolated temp dir.
82
+ const workDir = path.join(tmpDir, 'work');
83
+ copyRecursive(FIXTURE_SRC, path.join(workDir, 'src'));
84
+
85
+ // ── Step 2: Run deterministic intel builder against the temp dir.
86
+ // build-intel.cjs uses process.cwd(); invoke it with cwd=workDir.
87
+ const intelResult = spawnSync(
88
+ 'node',
89
+ [path.join(REPO_ROOT, 'scripts', 'build-intel.cjs')],
90
+ { cwd: workDir, encoding: 'utf8' }
91
+ );
92
+
93
+ // Non-zero intel run is OK (fixture may lack some scan inputs) — log but don't fail.
94
+ if (intelResult.status !== 0) {
95
+ console.log(`(note) build-intel exited ${intelResult.status} on fixture — continuing to baseline diff`);
96
+ }
97
+
98
+ // ── Step 3: Compare baseline files against the fresh run.
99
+ const diffs = [];
100
+ const missing = [];
101
+
102
+ function walk(dir, base, out) {
103
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
104
+ const full = path.join(dir, entry.name);
105
+ const rel = path.relative(base, full);
106
+ if (entry.isDirectory()) walk(full, base, out);
107
+ else out.push(rel);
108
+ }
109
+ }
110
+
111
+ const baselineFiles = [];
112
+ walk(baselineDir, baselineDir, baselineFiles);
113
+
114
+ // Artifact resolution: for each baseline file, look for a sibling file in the
115
+ // fresh run workDir (best-effort: baseline may contain reference manifests
116
+ // that aren't produced by the deterministic pipeline — those count as
117
+ // "present in baseline, not in run" and are recorded as missing_artifacts but
118
+ // not as diffs).
119
+ for (const rel of baselineFiles) {
120
+ const baselineFile = path.join(baselineDir, rel);
121
+ const freshCandidate1 = path.join(workDir, rel);
122
+ const freshCandidate2 = path.join(workDir, '.design', 'intel', path.basename(rel));
123
+
124
+ let freshFile = null;
125
+ if (fs.existsSync(freshCandidate1)) freshFile = freshCandidate1;
126
+ else if (fs.existsSync(freshCandidate2)) freshFile = freshCandidate2;
127
+
128
+ if (!freshFile) {
129
+ missing.push(rel);
130
+ continue;
131
+ }
132
+
133
+ const expected = fs.readFileSync(baselineFile, 'utf8').replace(/\r\n/g, '\n');
134
+ const actual = fs.readFileSync(freshFile, 'utf8').replace(/\r\n/g, '\n');
135
+
136
+ if (expected !== actual) {
137
+ diffs.push({
138
+ rel,
139
+ expected: expected.slice(0, 80),
140
+ actual: actual.slice(0, 80),
141
+ });
142
+ }
143
+ }
144
+
145
+ // ── Report.
146
+ for (const d of diffs) {
147
+ console.log(`DIFF: ${d.rel}`);
148
+ console.log(` Expected: ${d.expected.replace(/\n/g, '\\n')}`);
149
+ console.log(` Actual: ${d.actual.replace(/\n/g, '\\n')}`);
150
+ }
151
+
152
+ // Missing baseline artifacts are reported informationally. Baselines ship
153
+ // reference material (like BASELINE.md) that the deterministic pipeline does
154
+ // not regenerate — those are expected "missing in run" hits and are NOT treated
155
+ // as failures. Only actual byte-level diffs fail the build.
156
+ if (missing.length) {
157
+ console.log(`note: ${missing.length} baseline artifact(s) not regenerated by deterministic run:`);
158
+ for (const m of missing) console.log(` - ${m}`);
159
+ }
160
+
161
+ console.log(`smoke-test: ${diffs.length} diffs, ${missing.length} baseline artifacts not in fresh run`);
162
+
163
+ // Ensure .design/ was not created in the real repo root.
164
+ if (fs.existsSync(path.join(REPO_ROOT, '.design'))) {
165
+ console.error('ERROR: .design/ polluted repo root — smoke test must use temp dir only');
166
+ process.exit(1);
167
+ }
168
+
169
+ process.exit(diffs.length > 0 ? 1 : 0);
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env bash
2
+ # rollback-release.sh — manually delete a tag + GitHub Release. NOT called from CI.
3
+ # Usage: bash scripts/rollback-release.sh <version>
4
+ # Example: bash scripts/rollback-release.sh 1.0.7
5
+
6
+ set -euo pipefail
7
+
8
+ VERSION="${1:-}"
9
+ if [ -z "$VERSION" ]; then
10
+ echo "Usage: $0 <version>"
11
+ echo "Example: $0 1.0.7"
12
+ exit 1
13
+ fi
14
+
15
+ TAG="v${VERSION}"
16
+ REPO="${GITHUB_REPOSITORY:-hegemonart/get-design-done}"
17
+
18
+ if ! command -v gh >/dev/null 2>&1; then
19
+ echo "ERROR: gh CLI not found."
20
+ exit 1
21
+ fi
22
+
23
+ echo "About to delete release + tag $TAG on $REPO."
24
+ echo "This is a DESTRUCTIVE operation. Confirm (y/N): "
25
+ read -r CONFIRM
26
+ if [ "$CONFIRM" != "y" ]; then
27
+ echo "Aborted."
28
+ exit 0
29
+ fi
30
+
31
+ # Delete the GitHub Release (this also deletes the tag ref on github.com).
32
+ gh release delete "$TAG" --repo "$REPO" --yes --cleanup-tag || {
33
+ echo "Release delete failed or no release existed; attempting tag-only delete."
34
+ gh api -X DELETE "repos/${REPO}/git/refs/tags/${TAG}" || true
35
+ }
36
+
37
+ # Also delete locally if the user has the tag.
38
+ git tag -d "$TAG" 2>/dev/null || true
39
+
40
+ echo "Rollback complete for $TAG."
41
+ echo "Note: clones that already pulled the tag retain it. Consider posting"
42
+ echo " a deprecation note in the next CHANGELOG entry."
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ // CI-mode wrapper over hooks/gdd-read-injection-scanner.js.
4
+ // Scans all shipped reference/*.md, skills/**/SKILL.md, and agents/*.md for
5
+ // prompt-injection patterns. Exits 0 on a clean tree, 1 on any finding.
6
+ //
7
+ // The patterns are imported indirectly by re-declaring them — the hook itself
8
+ // is stdin-driven and cannot be required as a module. Keep these in sync with
9
+ // hooks/gdd-read-injection-scanner.js.
10
+
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+
14
+ const REPO_ROOT = path.resolve(__dirname, '..');
15
+
16
+ // Patterns mirror hooks/gdd-read-injection-scanner.js.
17
+ const INJECTION_PATTERNS = [
18
+ { name: 'ignore previous', re: /ignore\s+(all\s+)?(previous|prior|above)\s+instructions?/i },
19
+ { name: 'disregard previous', re: /disregard\s+(all\s+)?(previous|prior|above)\s+instructions?/i },
20
+ { name: 'you are now a different', re: /you\s+are\s+now\s+a\s+different/i },
21
+ { name: 'system: you are', re: /system\s*:\s*you\s+are/i },
22
+ { name: 'role tag injection', re: /<\s*\/?\s*(system|assistant|human)\s*>/i },
23
+ { name: '[INST] fragment', re: /\[INST\]/i },
24
+ { name: '### instruction fragment', re: /###\s*instruction/i },
25
+ ];
26
+
27
+ function walkMd(dir, out) {
28
+ if (!fs.existsSync(dir)) return;
29
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
30
+ const full = path.join(dir, entry.name);
31
+ if (entry.isDirectory()) walkMd(full, out);
32
+ else if (entry.isFile() && entry.name.endsWith('.md')) out.push(full);
33
+ }
34
+ }
35
+
36
+ function collectTargets() {
37
+ const targets = [];
38
+ // reference/ — all .md recursively
39
+ walkMd(path.join(REPO_ROOT, 'reference'), targets);
40
+ // skills/**/SKILL.md — only SKILL.md files
41
+ const skillsRoot = path.join(REPO_ROOT, 'skills');
42
+ const skillAccum = [];
43
+ walkMd(skillsRoot, skillAccum);
44
+ for (const f of skillAccum) {
45
+ if (path.basename(f) === 'SKILL.md') targets.push(f);
46
+ }
47
+ // agents/*.md (non-recursive)
48
+ const agentsRoot = path.join(REPO_ROOT, 'agents');
49
+ if (fs.existsSync(agentsRoot)) {
50
+ for (const entry of fs.readdirSync(agentsRoot, { withFileTypes: true })) {
51
+ if (entry.isFile() && entry.name.endsWith('.md')) {
52
+ targets.push(path.join(agentsRoot, entry.name));
53
+ }
54
+ }
55
+ }
56
+ return targets;
57
+ }
58
+
59
+ function main() {
60
+ const targets = collectTargets();
61
+ let findings = 0;
62
+
63
+ for (const file of targets) {
64
+ const body = fs.readFileSync(file, 'utf8');
65
+ const lines = body.split('\n');
66
+ let inFence = false;
67
+ for (let i = 0; i < lines.length; i++) {
68
+ const line = lines[i];
69
+ // Track code fences — prompt-injection attacks live in prose, not in
70
+ // literal code/template blocks. Skip lines inside ``` ... ``` fences.
71
+ if (/^\s*```/.test(line)) {
72
+ inFence = !inFence;
73
+ continue;
74
+ }
75
+ if (inFence) continue;
76
+ for (const pat of INJECTION_PATTERNS) {
77
+ if (pat.re.test(line)) {
78
+ const rel = path.relative(REPO_ROOT, file);
79
+ const excerpt = line.trim().slice(0, 120);
80
+ console.log(`${rel}:${i + 1}: ${pat.name}: ${excerpt}`);
81
+ findings++;
82
+ break; // one finding per line is enough
83
+ }
84
+ }
85
+ }
86
+ }
87
+
88
+ console.log(`summary: ${targets.length} files scanned, ${findings} findings`);
89
+ process.exit(findings === 0 ? 0 : 1);
90
+ }
91
+
92
+ main();
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ // CI-friendly frontmatter validator for agents/*.md.
4
+ // Enforces the Phase 7 agent frontmatter hygiene contract.
5
+ // Exits 0 on success, 1 on any violation. One finding per stdout line.
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+ const { readFrontmatter } = require('../tests/helpers.cjs');
10
+
11
+ const REQUIRED_FIELDS = [
12
+ 'name',
13
+ 'description',
14
+ 'tools',
15
+ 'color',
16
+ 'parallel-safe',
17
+ 'typical-duration-seconds',
18
+ 'reads-only',
19
+ 'writes',
20
+ ];
21
+
22
+ function walkMd(dir) {
23
+ const out = [];
24
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
25
+ const full = path.join(dir, entry.name);
26
+ if (entry.isDirectory()) out.push(...walkMd(full));
27
+ else if (entry.isFile() && entry.name.endsWith('.md')) out.push(full);
28
+ }
29
+ return out;
30
+ }
31
+
32
+ function main() {
33
+ const args = process.argv.slice(2).filter((a) => !a.startsWith('--'));
34
+ const targets = args.length ? args : ['agents/'];
35
+ const files = [];
36
+ for (const t of targets) {
37
+ if (!fs.existsSync(t)) {
38
+ console.error(`${t}: path does not exist`);
39
+ process.exit(1);
40
+ }
41
+ const stat = fs.statSync(t);
42
+ if (stat.isDirectory()) files.push(...walkMd(t));
43
+ else files.push(t);
44
+ }
45
+
46
+ let violations = 0;
47
+ for (const f of files) {
48
+ const fm = readFrontmatter(f);
49
+ if (Object.keys(fm).length === 0) {
50
+ // README.md under agents/ may have no frontmatter — skip
51
+ if (path.basename(f).toLowerCase() === 'readme.md') continue;
52
+ console.log(`${f}:frontmatter: missing`);
53
+ violations++;
54
+ continue;
55
+ }
56
+ for (const field of REQUIRED_FIELDS) {
57
+ if (!(field in fm) || fm[field] === undefined || fm[field] === null || fm[field] === '') {
58
+ console.log(`${f}:${field}: missing`);
59
+ violations++;
60
+ }
61
+ }
62
+ }
63
+
64
+ console.log(`summary: ${files.length} file(s) checked, ${violations} violation(s)`);
65
+ process.exit(violations === 0 ? 0 : 1);
66
+ }
67
+
68
+ main();
@@ -0,0 +1,225 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * validate-schemas.cjs
6
+ *
7
+ * Runs all Draft-07 JSON schemas under reference/schemas/ against their
8
+ * corresponding subject files. Used by `npm run validate:schemas` and by the
9
+ * CI validate job (added in plan 13-03).
10
+ *
11
+ * Primary path: spawn `npx --yes ajv-cli@5 validate -s <schema> -d <data>`.
12
+ *
13
+ * Fallback path: if `npx --yes` cannot fetch ajv-cli (offline/sandboxed
14
+ * environment), fall back to a structural parse check: confirm each schema is
15
+ * valid Draft-07 JSON (has $schema, type) and each data file is parseable JSON.
16
+ * The fallback exits 0 on structural validity; CI will run the real ajv pass.
17
+ *
18
+ * Usage:
19
+ * node scripts/validate-schemas.cjs # validate all schemas
20
+ * node scripts/validate-schemas.cjs --no-npx # force fallback path
21
+ *
22
+ * Exit codes:
23
+ * 0 — all present (schema, data) pairs pass
24
+ * 1 — one or more pairs failed validation or a present schema is invalid
25
+ */
26
+
27
+ const { spawnSync } = require('child_process');
28
+ const fs = require('fs');
29
+ const path = require('path');
30
+
31
+ const REPO_ROOT = path.resolve(__dirname, '..');
32
+ const SCHEMA_DIR = path.join(REPO_ROOT, 'reference', 'schemas');
33
+
34
+ /**
35
+ * (schema, data) pairs. `dataPath` is relative to repo root. `required=true`
36
+ * means the data file MUST exist in the repo; `required=false` means the data
37
+ * file is generated at runtime (gitignored) and only the schema itself is
38
+ * compiled.
39
+ */
40
+ const PAIRS = [
41
+ {
42
+ name: 'plugin',
43
+ schema: 'reference/schemas/plugin.schema.json',
44
+ data: '.claude-plugin/plugin.json',
45
+ required: true,
46
+ },
47
+ {
48
+ name: 'marketplace',
49
+ schema: 'reference/schemas/marketplace.schema.json',
50
+ data: '.claude-plugin/marketplace.json',
51
+ required: true,
52
+ },
53
+ {
54
+ name: 'hooks',
55
+ schema: 'reference/schemas/hooks.schema.json',
56
+ data: 'hooks/hooks.json',
57
+ required: true,
58
+ },
59
+ {
60
+ name: 'config',
61
+ schema: 'reference/schemas/config.schema.json',
62
+ data: '.design/config.json',
63
+ required: false,
64
+ },
65
+ {
66
+ name: 'intel',
67
+ schema: 'reference/schemas/intel.schema.json',
68
+ // intel files are runtime-only (gitignored). Only schema-compile it.
69
+ data: null,
70
+ required: false,
71
+ },
72
+ ];
73
+
74
+ const USE_NPX = !process.argv.includes('--no-npx');
75
+
76
+ /**
77
+ * Try running ajv-cli via npx. Returns { ok, stdout, stderr, status, fetchFailed }.
78
+ * fetchFailed=true if npx couldn't fetch the package (offline); caller should fall back.
79
+ */
80
+ function runAjv(args) {
81
+ const result = spawnSync(
82
+ 'npx',
83
+ ['--yes', 'ajv-cli@5', ...args],
84
+ { encoding: 'utf8', cwd: REPO_ROOT }
85
+ );
86
+ const combined = (result.stdout || '') + (result.stderr || '');
87
+ // Heuristics for "network/fetch failed" — in that case fall back silently.
88
+ const fetchFailed =
89
+ result.status !== 0 &&
90
+ /(ENOTFOUND|ECONNREFUSED|ETIMEDOUT|getaddrinfo|network|unable to resolve|unable to fetch|offline|E404|registry\.npmjs\.org)/i.test(combined);
91
+ return {
92
+ ok: result.status === 0,
93
+ stdout: result.stdout || '',
94
+ stderr: result.stderr || '',
95
+ status: result.status,
96
+ fetchFailed,
97
+ };
98
+ }
99
+
100
+ /**
101
+ * Fallback: confirm schema file is a valid Draft-07 JSON Schema (has $schema
102
+ * pointing at draft-07). Confirm data file (if present) parses as JSON.
103
+ */
104
+ function structuralCheck(schemaAbs, dataAbs) {
105
+ let schema;
106
+ try {
107
+ schema = JSON.parse(fs.readFileSync(schemaAbs, 'utf8'));
108
+ } catch (e) {
109
+ return { ok: false, reason: `schema not parseable JSON: ${e.message}` };
110
+ }
111
+ if (!schema.$schema || !/draft-07/.test(schema.$schema)) {
112
+ return { ok: false, reason: `schema missing Draft-07 $schema declaration` };
113
+ }
114
+ if (dataAbs) {
115
+ try {
116
+ JSON.parse(fs.readFileSync(dataAbs, 'utf8'));
117
+ } catch (e) {
118
+ return { ok: false, reason: `data file not parseable JSON: ${e.message}` };
119
+ }
120
+ }
121
+ return { ok: true };
122
+ }
123
+
124
+ function main() {
125
+ const results = [];
126
+ let fallbackReason = null;
127
+
128
+ for (const pair of PAIRS) {
129
+ const schemaAbs = path.join(REPO_ROOT, pair.schema);
130
+ const dataAbs = pair.data ? path.join(REPO_ROOT, pair.data) : null;
131
+
132
+ if (!fs.existsSync(schemaAbs)) {
133
+ results.push({
134
+ name: pair.name,
135
+ ok: false,
136
+ via: 'missing-schema',
137
+ reason: `schema not found at ${pair.schema}`,
138
+ });
139
+ continue;
140
+ }
141
+
142
+ const dataPresent = dataAbs && fs.existsSync(dataAbs);
143
+
144
+ if (!dataPresent && pair.required) {
145
+ results.push({
146
+ name: pair.name,
147
+ ok: false,
148
+ via: 'missing-data',
149
+ reason: `required data file missing at ${pair.data}`,
150
+ });
151
+ continue;
152
+ }
153
+
154
+ if (!dataPresent) {
155
+ // Schema-only: compile to confirm the schema is structurally valid.
156
+ if (USE_NPX && !fallbackReason) {
157
+ const r = runAjv(['compile', '-s', schemaAbs]);
158
+ if (r.ok) {
159
+ results.push({ name: pair.name, ok: true, via: 'ajv-compile', note: 'data not present in repo tree — schema compiled only' });
160
+ continue;
161
+ }
162
+ if (r.fetchFailed) {
163
+ fallbackReason = `npx fetch failed: ${r.stderr.split('\n')[0] || 'unknown'}`;
164
+ // fall through to structural check below
165
+ } else {
166
+ results.push({ name: pair.name, ok: false, via: 'ajv-compile', reason: r.stderr || r.stdout || `exit ${r.status}` });
167
+ continue;
168
+ }
169
+ }
170
+ const s = structuralCheck(schemaAbs, null);
171
+ results.push({
172
+ name: pair.name,
173
+ ok: s.ok,
174
+ via: 'structural-compile',
175
+ reason: s.ok ? 'schema is valid Draft-07 (data not in repo tree)' : s.reason,
176
+ });
177
+ continue;
178
+ }
179
+
180
+ // Both schema and data present — full validation.
181
+ if (USE_NPX && !fallbackReason) {
182
+ const r = runAjv(['validate', '-s', schemaAbs, '-d', dataAbs]);
183
+ if (r.ok) {
184
+ results.push({ name: pair.name, ok: true, via: 'ajv-validate' });
185
+ continue;
186
+ }
187
+ if (r.fetchFailed) {
188
+ fallbackReason = `npx fetch failed: ${r.stderr.split('\n')[0] || 'unknown'}`;
189
+ // fall through to structural check below
190
+ } else {
191
+ results.push({ name: pair.name, ok: false, via: 'ajv-validate', reason: r.stderr || r.stdout || `exit ${r.status}` });
192
+ continue;
193
+ }
194
+ }
195
+ const s = structuralCheck(schemaAbs, dataAbs);
196
+ results.push({
197
+ name: pair.name,
198
+ ok: s.ok,
199
+ via: 'structural-validate',
200
+ reason: s.ok ? 'schema is valid Draft-07 and data parses as JSON' : s.reason,
201
+ });
202
+ }
203
+
204
+ // Report.
205
+ console.log('validate-schemas: results');
206
+ for (const r of results) {
207
+ const status = r.ok ? 'OK' : 'FAIL';
208
+ const note = r.note ? ` (${r.note})` : '';
209
+ const reason = r.reason ? ` — ${r.reason}` : '';
210
+ console.log(` [${status}] ${r.name} via ${r.via}${note}${reason}`);
211
+ }
212
+ if (fallbackReason) {
213
+ console.log(`\nnote: ajv-cli via npx unavailable (${fallbackReason}); fell back to structural checks. CI will run the authoritative ajv pass.`);
214
+ }
215
+ const failed = results.filter(r => !r.ok);
216
+ console.log(`summary: ${results.length} pair(s) checked, ${failed.length} failure(s)`);
217
+
218
+ process.exit(failed.length === 0 ? 0 : 1);
219
+ }
220
+
221
+ if (require.main === module) {
222
+ main();
223
+ }
224
+
225
+ module.exports = { PAIRS };
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+ // Ensures package.json, .claude-plugin/plugin.json, and .claude-plugin/marketplace.json
3
+ // all declare the same version before we publish. Exits non-zero on mismatch.
4
+
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+
8
+ const repoRoot = path.resolve(__dirname, '..');
9
+ const pkg = JSON.parse(fs.readFileSync(path.join(repoRoot, 'package.json'), 'utf8'));
10
+ const plugin = JSON.parse(fs.readFileSync(path.join(repoRoot, '.claude-plugin/plugin.json'), 'utf8'));
11
+ const marketplace = JSON.parse(fs.readFileSync(path.join(repoRoot, '.claude-plugin/marketplace.json'), 'utf8'));
12
+
13
+ const versions = {
14
+ 'package.json': pkg.version,
15
+ '.claude-plugin/plugin.json': plugin.version,
16
+ '.claude-plugin/marketplace.json (metadata)': marketplace.metadata && marketplace.metadata.version,
17
+ '.claude-plugin/marketplace.json (plugins[0])': marketplace.plugins && marketplace.plugins[0] && marketplace.plugins[0].version,
18
+ };
19
+
20
+ const unique = [...new Set(Object.values(versions).filter(Boolean))];
21
+
22
+ if (unique.length !== 1) {
23
+ console.error('Version mismatch across manifests:');
24
+ for (const [file, v] of Object.entries(versions)) {
25
+ console.error(` ${file}: ${v}`);
26
+ }
27
+ process.exit(1);
28
+ }
29
+
30
+ console.log(`All manifests at version ${unique[0]}`);
@@ -0,0 +1,47 @@
1
+ ---
2
+ name: gdd-add-backlog
3
+ description: "Park a design idea for a future cycle. Writes to .design/backlog/BACKLOG.md."
4
+ argument-hint: "[text]"
5
+ tools: Read, Write, AskUserQuestion
6
+ ---
7
+
8
+ # /gdd:add-backlog
9
+
10
+ **Role:** Long-term parking lot for design ideas. Backing store: `.design/backlog/BACKLOG.md`.
11
+
12
+ ## Step 1 — Get text
13
+
14
+ If `$ARGUMENTS` is empty, ask the user: "What should be added to the backlog?"
15
+
16
+ ## Step 2 — Append
17
+
18
+ Create `.design/backlog/` directory and `BACKLOG.md` with `# Design Backlog` header if missing.
19
+
20
+ Derive `<title>` = first 60 characters of the text (strip newlines). Append:
21
+
22
+ ```markdown
23
+ ## <title>
24
+ **Added**: YYYY-MM-DD
25
+ **Status**: parked
26
+
27
+ <full text>
28
+
29
+ ---
30
+ ```
31
+
32
+ ## Output
33
+
34
+ ```
35
+ ━━━ Backlog entry parked ━━━
36
+ Title: <title>
37
+ Status: parked
38
+ Promote later via: /gdd:review-backlog
39
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━
40
+ ```
41
+
42
+ ## Constraints
43
+
44
+ - Do not modify files outside `.design/backlog/`.
45
+ - Do not set status to anything other than `parked` here — `/gdd:review-backlog` owns status transitions.
46
+
47
+ ## ADD-BACKLOG COMPLETE