@su-record/vibe 2.9.40 → 2.10.2

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 (182) hide show
  1. package/CLAUDE.md +28 -12
  2. package/README.en.md +2 -3
  3. package/README.md +33 -27
  4. package/agents/{teams/figma → figma}/figma-analyst.md +2 -2
  5. package/agents/research/{best-practices-agent.md → best-practices.md} +1 -1
  6. package/agents/research/{codebase-patterns-agent.md → codebase-patterns.md} +1 -1
  7. package/agents/research/{framework-docs-agent.md → framework-docs.md} +1 -1
  8. package/agents/research/{security-advisory-agent.md → security-advisory.md} +1 -1
  9. package/agents/teams/research-team.md +4 -4
  10. package/agents/teams/review-debate-team.md +2 -2
  11. package/agents/teams/security-team.md +4 -4
  12. package/dist/cli/auth.d.ts +0 -1
  13. package/dist/cli/auth.d.ts.map +1 -1
  14. package/dist/cli/auth.js +1 -18
  15. package/dist/cli/auth.js.map +1 -1
  16. package/dist/cli/collaborator.d.ts +3 -3
  17. package/dist/cli/collaborator.js +4 -4
  18. package/dist/cli/collaborator.js.map +1 -1
  19. package/dist/cli/commands/info.d.ts.map +1 -1
  20. package/dist/cli/commands/info.js +0 -1
  21. package/dist/cli/commands/info.js.map +1 -1
  22. package/dist/cli/commands/init.d.ts +3 -4
  23. package/dist/cli/commands/init.d.ts.map +1 -1
  24. package/dist/cli/commands/init.js +15 -20
  25. package/dist/cli/commands/init.js.map +1 -1
  26. package/dist/cli/commands/remove.d.ts.map +1 -1
  27. package/dist/cli/commands/remove.js +2 -7
  28. package/dist/cli/commands/remove.js.map +1 -1
  29. package/dist/cli/commands/update.d.ts.map +1 -1
  30. package/dist/cli/commands/update.js +10 -10
  31. package/dist/cli/commands/update.js.map +1 -1
  32. package/dist/cli/index.js +1 -3
  33. package/dist/cli/index.js.map +1 -1
  34. package/dist/cli/postinstall/claude-agents.d.ts +3 -1
  35. package/dist/cli/postinstall/claude-agents.d.ts.map +1 -1
  36. package/dist/cli/postinstall/claude-agents.js +47 -9
  37. package/dist/cli/postinstall/claude-agents.js.map +1 -1
  38. package/dist/cli/postinstall/constants.d.ts +5 -0
  39. package/dist/cli/postinstall/constants.d.ts.map +1 -1
  40. package/dist/cli/postinstall/constants.js +165 -23
  41. package/dist/cli/postinstall/constants.js.map +1 -1
  42. package/dist/cli/postinstall/cursor-skills.js +2 -2
  43. package/dist/cli/postinstall/main.d.ts.map +1 -1
  44. package/dist/cli/postinstall/main.js +26 -24
  45. package/dist/cli/postinstall/main.js.map +1 -1
  46. package/dist/cli/setup/LegacyMigration.d.ts +3 -3
  47. package/dist/cli/setup/LegacyMigration.d.ts.map +1 -1
  48. package/dist/cli/setup/LegacyMigration.js +3 -5
  49. package/dist/cli/setup/LegacyMigration.js.map +1 -1
  50. package/dist/cli/setup/ProjectSetup.d.ts +18 -8
  51. package/dist/cli/setup/ProjectSetup.d.ts.map +1 -1
  52. package/dist/cli/setup/ProjectSetup.js +70 -19
  53. package/dist/cli/setup/ProjectSetup.js.map +1 -1
  54. package/dist/cli/setup.d.ts +1 -1
  55. package/dist/cli/setup.d.ts.map +1 -1
  56. package/dist/cli/setup.js +1 -1
  57. package/dist/cli/setup.js.map +1 -1
  58. package/dist/cli/utils/cli-detector.d.ts +0 -7
  59. package/dist/cli/utils/cli-detector.d.ts.map +1 -1
  60. package/dist/cli/utils/cli-detector.js +0 -95
  61. package/dist/cli/utils/cli-detector.js.map +1 -1
  62. package/dist/cli/utils.d.ts +1 -1
  63. package/dist/cli/utils.d.ts.map +1 -1
  64. package/dist/cli/utils.js +1 -2
  65. package/dist/cli/utils.js.map +1 -1
  66. package/dist/infra/lib/OrchestrateWorkflow.js +1 -1
  67. package/dist/infra/lib/OrchestrateWorkflow.js.map +1 -1
  68. package/dist/infra/lib/memory/MemoryStorage.d.ts +1 -1
  69. package/dist/infra/lib/memory/MemoryStorage.d.ts.map +1 -1
  70. package/dist/infra/lib/memory/MemoryStorage.js +2 -3
  71. package/dist/infra/lib/memory/MemoryStorage.js.map +1 -1
  72. package/dist/infra/lib/telemetry/SkillTelemetry.test.js +4 -4
  73. package/dist/infra/lib/telemetry/SkillTelemetry.test.js.map +1 -1
  74. package/dist/infra/orchestrator/parallelResearch.js +4 -4
  75. package/dist/infra/orchestrator/parallelResearch.js.map +1 -1
  76. package/hooks/scripts/__tests__/pre-tool-guard.test.js +1 -1
  77. package/hooks/scripts/clone-extract.js +712 -0
  78. package/hooks/scripts/clone-refine.js +510 -0
  79. package/hooks/scripts/clone-to-scss.js +275 -0
  80. package/hooks/scripts/clone-validate.js +280 -0
  81. package/hooks/scripts/codex-notify.js +49 -0
  82. package/hooks/scripts/command-log.js +1 -1
  83. package/hooks/scripts/lib/dispatcher.js +2 -3
  84. package/hooks/scripts/lib/scope-from-spec.js +2 -4
  85. package/hooks/scripts/llm-orchestrate.js +2 -7
  86. package/hooks/scripts/prompt-dispatcher.js +3 -3
  87. package/hooks/scripts/step-counter.js +1 -1
  88. package/hooks/scripts/utils.js +5 -10
  89. package/package.json +2 -1
  90. package/skills/agents-md/SKILL.md +2 -0
  91. package/skills/arch-guard/SKILL.md +2 -0
  92. package/skills/brand-assets/SKILL.md +1 -0
  93. package/skills/capability-loop/SKILL.md +2 -0
  94. package/skills/characterization-test/SKILL.md +2 -0
  95. package/skills/chub-usage/SKILL.md +1 -0
  96. package/skills/claude-md-guide/SKILL.md +2 -0
  97. package/skills/clone/SKILL.md +361 -0
  98. package/skills/commerce-patterns/SKILL.md +1 -0
  99. package/skills/commit-push-pr/SKILL.md +1 -0
  100. package/skills/context7-usage/SKILL.md +1 -0
  101. package/skills/{vibe-contract → contract}/SKILL.md +7 -8
  102. package/skills/create-prd/SKILL.md +1 -0
  103. package/skills/design-audit/SKILL.md +1 -0
  104. package/skills/design-critique/SKILL.md +1 -0
  105. package/skills/design-distill/SKILL.md +1 -0
  106. package/skills/design-normalize/SKILL.md +1 -0
  107. package/skills/design-polish/SKILL.md +1 -0
  108. package/skills/design-teach/SKILL.md +2 -0
  109. package/skills/devlog/SKILL.md +1 -0
  110. package/skills/{vibe-docs → docs}/SKILL.md +8 -8
  111. package/skills/e2e-commerce/SKILL.md +1 -0
  112. package/skills/event-comms/SKILL.md +1 -0
  113. package/skills/event-ops/SKILL.md +1 -0
  114. package/skills/event-planning/SKILL.md +1 -0
  115. package/skills/exec-plan/SKILL.md +2 -0
  116. package/skills/{vibe-figma → figma}/SKILL.md +4 -3
  117. package/skills/{vibe-figma-convert → figma-convert}/SKILL.md +4 -3
  118. package/skills/{vibe-figma-extract → figma-extract}/SKILL.md +4 -3
  119. package/skills/git-worktree/SKILL.md +1 -0
  120. package/skills/handoff/SKILL.md +2 -0
  121. package/skills/{vibe-interview → interview}/SKILL.md +16 -16
  122. package/skills/parallel-research/SKILL.md +2 -0
  123. package/skills/{vibe-plan → plan}/SKILL.md +9 -9
  124. package/skills/prioritization-frameworks/SKILL.md +1 -0
  125. package/skills/priority-todos/SKILL.md +2 -0
  126. package/skills/{vibe-regress → regress}/SKILL.md +5 -6
  127. package/skills/rob-pike/SKILL.md +2 -0
  128. package/skills/seo-checklist/SKILL.md +1 -0
  129. package/skills/{vibe-spec → spec}/SKILL.md +14 -14
  130. package/skills/{vibe-spec-review → spec-review}/SKILL.md +8 -9
  131. package/skills/systematic-debugging/SKILL.md +2 -0
  132. package/skills/techdebt/SKILL.md +2 -0
  133. package/skills/{vibe-test → test}/SKILL.md +19 -19
  134. package/skills/tool-fallback/SKILL.md +1 -0
  135. package/skills/typescript-advanced-types/SKILL.md +1 -0
  136. package/skills/ui-ux-pro-max/SKILL.md +1 -0
  137. package/skills/user-personas/SKILL.md +1 -0
  138. package/skills/vercel-react-best-practices/SKILL.md +1 -0
  139. package/skills/vibe/SKILL.md +288 -0
  140. package/{commands/vibe.analyze.md → skills/vibe.analyze/SKILL.md} +2 -0
  141. package/skills/vibe.clone/SKILL.md +117 -0
  142. package/{commands/vibe.contract.md → skills/vibe.contract/SKILL.md} +3 -1
  143. package/{commands/vibe.docs.md → skills/vibe.docs/SKILL.md} +3 -1
  144. package/{commands/vibe.event.md → skills/vibe.event/SKILL.md} +2 -0
  145. package/{commands/vibe.figma.md → skills/vibe.figma/SKILL.md} +25 -23
  146. package/{commands/vibe.harness.md → skills/vibe.harness/SKILL.md} +2 -0
  147. package/{commands/vibe.reason.md → skills/vibe.reason/SKILL.md} +2 -0
  148. package/{commands/vibe.regress.md → skills/vibe.regress/SKILL.md} +5 -3
  149. package/{commands/vibe.review.md → skills/vibe.review/SKILL.md} +2 -0
  150. package/{commands/vibe.run.md → skills/vibe.run/SKILL.md} +5 -1
  151. package/{commands/vibe.scaffold.md → skills/vibe.scaffold/SKILL.md} +2 -0
  152. package/{commands/vibe.spec.md → skills/vibe.spec/SKILL.md} +36 -34
  153. package/{commands/vibe.test.md → skills/vibe.test/SKILL.md} +8 -6
  154. package/{commands/vibe.trace.md → skills/vibe.trace/SKILL.md} +7 -0
  155. package/{commands/vibe.utils.md → skills/vibe.utils/SKILL.md} +2 -0
  156. package/{commands/vibe.verify.md → skills/vibe.verify/SKILL.md} +4 -2
  157. package/skills/video-production/SKILL.md +1 -0
  158. package/vibe/rules/principles/dual-harness-doctrine.md +50 -0
  159. /package/agents/{teams/figma → figma}/figma-architect.md +0 -0
  160. /package/agents/{teams/figma → figma}/figma-auditor.md +0 -0
  161. /package/agents/{teams/figma → figma}/figma-builder.md +0 -0
  162. /package/skills/{vibe-docs → docs}/templates/architecture.md +0 -0
  163. /package/skills/{vibe-docs → docs}/templates/behavioral-principles.md +0 -0
  164. /package/skills/{vibe-docs → docs}/templates/readme.md +0 -0
  165. /package/skills/{vibe-docs → docs}/templates/release-notes.md +0 -0
  166. /package/skills/{vibe-figma → figma}/rubrics/extraction-checklist.md +0 -0
  167. /package/skills/{vibe-figma → figma}/templates/component-index.md +0 -0
  168. /package/skills/{vibe-figma → figma}/templates/component-spec.md +0 -0
  169. /package/skills/{vibe-figma → figma}/templates/figma-handoff.md +0 -0
  170. /package/skills/{vibe-figma → figma}/templates/remapped-tree.md +0 -0
  171. /package/skills/{vibe-figma-convert → figma-convert}/rubrics/conversion-rules.md +0 -0
  172. /package/skills/{vibe-figma-convert → figma-convert}/templates/component.md +0 -0
  173. /package/skills/{vibe-figma-extract → figma-extract}/rubrics/image-rules.md +0 -0
  174. /package/skills/{vibe-interview → interview}/checklists/api.md +0 -0
  175. /package/skills/{vibe-interview → interview}/checklists/feature.md +0 -0
  176. /package/skills/{vibe-interview → interview}/checklists/library.md +0 -0
  177. /package/skills/{vibe-interview → interview}/checklists/mobile.md +0 -0
  178. /package/skills/{vibe-interview → interview}/checklists/webapp.md +0 -0
  179. /package/skills/{vibe-interview → interview}/checklists/website.md +0 -0
  180. /package/skills/{vibe-regress → regress}/templates/bug.md +0 -0
  181. /package/skills/{vibe-regress → regress}/templates/test-jest.md +0 -0
  182. /package/skills/{vibe-regress → regress}/templates/test-vitest.md +0 -0
@@ -112,7 +112,6 @@ function resolveModel(providerName, config) {
112
112
  *
113
113
  * true인 경우:
114
114
  * - vibe-codex: ANTHROPIC_BASE_URL이 localhost (프록시 모드)
115
- * - coco: ~/.coco/ 존재 또는 COCO_HOME 설정
116
115
  * - 명시적: VIBE_SECONDARY_LLM=claude
117
116
  */
118
117
  function useClaudeAsSecondary() {
@@ -121,10 +120,6 @@ function useClaudeAsSecondary() {
121
120
  // 2. vibe-codex 프록시 모드
122
121
  const baseUrl = process.env.ANTHROPIC_BASE_URL || '';
123
122
  if (baseUrl.includes('localhost') || baseUrl.includes('127.0.0.1')) return true;
124
- // 3. coco 환경
125
- if (process.env.COCO_HOME) return true;
126
- const cocoDir = path.join(os.homedir(), '.coco');
127
- if (fs.existsSync(cocoDir)) return true;
128
123
  return false;
129
124
  }
130
125
 
@@ -601,10 +596,10 @@ async function main() {
601
596
  // 명시적 claude 호출
602
597
  providerChain = ['claude', 'gemini'];
603
598
  } else if (isGpt) {
604
- // GPT 주관 → claude fallback (vibe-codex/coco), gemini fallback (직접 모드)
599
+ // GPT 주관 → claude fallback (vibe-codex), gemini fallback (직접 모드)
605
600
  providerChain = claudeSecondary ? [provider, 'claude'] : [provider, 'gemini'];
606
601
  } else {
607
- // gemini 주관 → claude fallback (vibe-codex/coco), gpt fallback (직접 모드)
602
+ // gemini 주관 → claude fallback (vibe-codex), gpt fallback (직접 모드)
608
603
  providerChain = claudeSecondary ? ['gemini', 'claude'] : ['gemini', 'gpt'];
609
604
  }
610
605
 
@@ -42,11 +42,11 @@ try {
42
42
 
43
43
  if (!prompt) process.exit(0);
44
44
 
45
- // 레거시 SSOT 통합 — `/vibe.*` 진입 시 `.claude/vibe/`·`.coco/vibe/` → `.vibe/` 자동 이동.
45
+ // 레거시 SSOT 통합 — `/vibe.*` 진입 시 `.claude/vibe/` → `.vibe/` 자동 이동.
46
46
  // `vibe init`/`update` 와 동일한 `consolidateLegacyVibe` (dist/cli/setup/LegacyMigration.js) 를 직접 재사용. Idempotent.
47
47
  if (/^\s*\/vibe\b/i.test(prompt)) {
48
48
  try {
49
- const projectDir = process.env.CLAUDE_PROJECT_DIR || process.env.COCO_PROJECT_DIR || process.cwd();
49
+ const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
50
50
  const utils = await import('./utils.js');
51
51
  const CLI_BASE = utils.getCliBaseUrl();
52
52
  const { consolidateLegacyVibe } = await import(`${CLI_BASE}setup/LegacyMigration.js`);
@@ -183,7 +183,7 @@ if (!matched) {
183
183
  setImmediate(async () => {
184
184
  try {
185
185
  const utils = await import('./utils.js');
186
- const projectDir = process.env.CLAUDE_PROJECT_DIR || process.env.COCO_PROJECT_DIR || '.';
186
+ const projectDir = process.env.CLAUDE_PROJECT_DIR || '.';
187
187
  const configPath = utils.projectVibePath(projectDir, 'config.json');
188
188
  let gapEnabled = true;
189
189
  try {
@@ -68,7 +68,7 @@ function isResponseError(toolResponse) {
68
68
  }
69
69
 
70
70
  // ─────────────────────────────────────────────────────
71
- // 책임 3a: error_category 분류 (regex 만 — vibe-regress tag enum 일부)
71
+ // 책임 3a: error_category 분류 (regex 만 — regress tag enum 일부)
72
72
  // ─────────────────────────────────────────────────────
73
73
  const ERROR_CATEGORIES = [
74
74
  { tag: 'nullability', re: /Cannot read propert(?:y|ies) of (?:undefined|null)|TypeError[^\n]*(?:undefined|null)/i },
@@ -17,7 +17,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
17
17
  const DETECTED_VIBE_PATH = path.resolve(__dirname, '..', '..');
18
18
 
19
19
  export const VIBE_PATH = process.env.VIBE_PATH || DETECTED_VIBE_PATH;
20
- export const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.env.COCO_PROJECT_DIR || '.';
20
+ export const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || '.';
21
21
 
22
22
  // ~/.vibe/ 디렉토리 경로
23
23
  const VIBE_HOME_DIR = path.join(os.homedir(), '.vibe');
@@ -25,16 +25,14 @@ const VIBE_HOME_DIR = path.join(os.homedir(), '.vibe');
25
25
  /**
26
26
  * 프로젝트 내 Vibe 에셋 루트 해석.
27
27
  *
28
- * 신규 SSOT: `<project>/.vibe/` — CLI(Claude/coco)와 무관한 공용 디렉토리.
28
+ * 신규 SSOT: `<project>/.vibe/` — CLI(Claude/Codex)와 무관한 공용 디렉토리.
29
29
  * Legacy fallback:
30
30
  * - `<project>/.claude/vibe/` (Claude Code 프로젝트 초기화 흔적)
31
- * - `<project>/.coco/vibe/` (coco 프로젝트 초기화 흔적)
32
31
  *
33
32
  * 해석 규칙 — **읽기**(lookup) 시:
34
33
  * 1) `.vibe/` 가 존재하면 그 경로
35
34
  * 2) `.claude/vibe/` 가 존재하면 그 경로
36
- * 3) `.coco/vibe/` 존재하면 그 경로
37
- * 4) 아무것도 없으면 기본값 `.vibe/` (생성 대상)
35
+ * 3) 아무것도 없으면 기본값 `.vibe/` (생성 대상)
38
36
  *
39
37
  * **쓰기**(write) 시에는 항상 새 SSOT 인 `.vibe/` 로 수렴시키는 것이 원칙이지만,
40
38
  * 기존 프로젝트의 legacy 파일이 있으면 해당 위치에 쓰는 것이 덜 파괴적이다.
@@ -48,8 +46,6 @@ export function projectVibeRoot(projectDir = PROJECT_DIR) {
48
46
  if (fs.existsSync(vibeRoot)) return vibeRoot;
49
47
  const claudeVibe = path.join(projectDir, '.claude', 'vibe');
50
48
  if (fs.existsSync(claudeVibe)) return claudeVibe;
51
- const cocoVibe = path.join(projectDir, '.coco', 'vibe');
52
- if (fs.existsSync(cocoVibe)) return cocoVibe;
53
49
  } catch { /* ignore */ }
54
50
  return path.join(projectDir, '.vibe');
55
51
  }
@@ -66,14 +62,13 @@ export function projectVibePath(projectDir = PROJECT_DIR, ...sub) {
66
62
 
67
63
  /**
68
64
  * 프로젝트 메모리 DB 디렉토리 해석.
69
- * `.vibe/memories/` (신규) → `.claude/memories/` (legacy Claude) → `.coco/memories/` (legacy coco) → 기본 `.vibe/memories/`
65
+ * `.vibe/memories/` (신규) → `.claude/memories/` (legacy Claude) → 기본 `.vibe/memories/`
70
66
  */
71
67
  export function projectMemoryDir(projectDir = PROJECT_DIR) {
72
68
  try {
73
69
  const candidates = [
74
70
  path.join(projectDir, '.vibe', 'memories'),
75
71
  path.join(projectDir, '.claude', 'memories'),
76
- path.join(projectDir, '.coco', 'memories'),
77
72
  ];
78
73
  for (const c of candidates) if (fs.existsSync(c)) return c;
79
74
  } catch { /* ignore */ }
@@ -95,7 +90,7 @@ export function readVibeConfig() {
95
90
  }
96
91
 
97
92
  /**
98
- * 프로젝트 설정(.vibe/config.json) 읽기 — legacy `.claude/vibe/`, `.coco/vibe/` fallback 포함
93
+ * 프로젝트 설정(.vibe/config.json) 읽기 — legacy `.claude/vibe/` fallback 포함
99
94
  * @returns {object} 파싱된 config 또는 빈 객체
100
95
  */
101
96
  export function readProjectConfig() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@su-record/vibe",
3
- "version": "2.9.40",
3
+ "version": "2.10.2",
4
4
  "description": "AI Coding Framework for Claude Code — 56 agents, 45 skills, multi-LLM orchestration",
5
5
  "type": "module",
6
6
  "main": "dist/cli/index.js",
@@ -27,6 +27,7 @@
27
27
  "dev": "tsc --watch",
28
28
  "gen:skill-docs": "npx tsx scripts/gen-skill-docs.ts",
29
29
  "gen:skill-docs:check": "npx tsx scripts/gen-skill-docs.ts --check",
30
+ "validate:skill-invocation": "npx tsx scripts/validate-skill-invocation.ts",
30
31
  "test": "vitest run",
31
32
  "test:watch": "vitest",
32
33
  "prepublishOnly": "pnpm build",
@@ -1,5 +1,7 @@
1
1
  ---
2
2
  name: agents-md
3
+ user-invocable: false
4
+ invocation: [auto, chain]
3
5
  tier: standard
4
6
  description: "Optimize AGENTS.md / CLAUDE.md by removing discoverable info and keeping only gotchas. Based on Addy Osmani's AGENTS.md principles. Activates on agents.md, claude.md, context file optimization."
5
7
  triggers: [agents.md, claude.md, context file, optimize agents, optimize claude]
@@ -1,5 +1,7 @@
1
1
  ---
2
2
  name: arch-guard
3
+ user-invocable: false
4
+ invocation: [auto]
3
5
  tier: core
4
6
  description: "Generate architecture boundary tests that mechanically enforce layer constraints. Use when adding new modules, refactoring layers, or after detecting circular dependencies. Creates import-rule tests (e.g., 'UI must not import DB') that fail CI on violation. Must use this skill when user mentions layer enforcement, dependency rules, or architectural boundaries — even casually like 'make sure services don't import controllers'."
5
7
  triggers: [arch guard, architecture test, layer test, boundary test, structural test, arch validation]
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: brand-assets
3
+ invocation: [auto]
3
4
  tier: standard
4
5
  description: "Auto-generate app icons (iOS/Android/PWA), favicons, and OG images from SPEC brand information using Gemini Image API. Use when the project needs visual brand assets, when user mentions 'icon', 'favicon', 'logo', or 'brand assets', or when a SPEC defines brand colors/identity but no assets exist yet. Outputs multiple sizes and formats ready for deployment. Not for complex illustration or marketing graphics — focused on app identity assets."
5
6
  triggers: [icon, favicon, brand, logo, app icon, branding, assets]
@@ -1,5 +1,7 @@
1
1
  ---
2
2
  name: capability-loop
3
+ user-invocable: false
4
+ invocation: [auto]
3
5
  tier: standard
4
6
  description: "When an agent fails, diagnose which capability is missing and build it into the repo. Activates after repeated agent failures, tool errors, or when a task keeps failing in the same way. Analyzes failure transcripts, identifies the missing guardrail/tool/abstraction/doc, and creates it permanently. Use this skill whenever you see 3+ similar failures, an agent hitting the same wall repeatedly, or the user asking 'why does this keep failing'."
5
7
  triggers: [capability loop, failure loop, build capability, missing capability, agent failed, why did it fail]
@@ -1,5 +1,7 @@
1
1
  ---
2
2
  name: characterization-test
3
+ user-invocable: false
4
+ invocation: [auto]
3
5
  tier: core
4
6
  description: "Lock existing behavior with characterization tests before modifying code. Use BEFORE any refactor, rewrite, or large-scale modification of existing code — especially legacy code without tests. Captures current input/output behavior as test cases so regressions are caught immediately. Must use this skill when touching files >200 lines with no existing tests, when user says 'refactor', 'rewrite', 'modernize', or 'clean up' existing code."
5
7
  triggers: [legacy, characterization test, lock behavior, regression prevention, before refactor, large file]
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: chub-usage
3
+ invocation: [auto]
3
4
  tier: optional
4
5
  description: "Context Hub (chub) — fetch vetted, up-to-date API documentation. Write accurate code based on the latest docs instead of training data when working with external APIs/SDKs."
5
6
  triggers: [chub, context hub, API docs, latest API, deprecated API, SDK documentation, api reference, 최신 문서]
@@ -1,5 +1,7 @@
1
1
  ---
2
2
  name: claude-md-guide
3
+ user-invocable: false
4
+ invocation: [auto]
3
5
  tier: standard
4
6
  description: "Guide for writing effective CLAUDE.md files from scratch. Evidence-based methodology from 40+ sources including research papers, official docs, and real-world examples. Covers 3-layer architecture, Curse of Instructions mitigation, progressive disclosure, and maintenance. Use when creating new CLAUDE.md, improving existing ones, or teaching team members how to write project instructions for AI agents."
5
7
  triggers: [claude-md guide, write claude.md, create claude.md, claude.md 작성, 클로드 문서, project instructions, claude-md]
@@ -0,0 +1,361 @@
1
+ ---
2
+ name: clone
3
+ description: URL → markup 수준 pixel-perfect 클론 본체 — headless browser로 라이브 사이트 캡처 후 현재 스택으로 스캐폴드.
4
+ when_to_use: /vibe.clone 진입점에서 체인 호출. 직접 호출 금지.
5
+ user-invocable: false
6
+ tier: standard
7
+ ---
8
+
9
+ # vibe.clone — Markup-Level Website Clone
10
+
11
+ ## Core Principles
12
+
13
+ ```
14
+ The rendered DOM is the source of truth for markup. Screenshots are for pixel verification only.
15
+
16
+ ✅ Puppeteer-rendered DOM (post-JS) → HTML structural mapping
17
+ ✅ Computed CSS → SCSS direct conversion (no guessing)
18
+ ✅ All remote assets (images, fonts) → downloaded locally and rewritten to project paths
19
+ ✅ Claude handles semantic decisions only: tag selection, component splitting, interactions
20
+ ✅ Screenshots are used for verification only, not generation
21
+ ```
22
+
23
+ ## Immutable Rules
24
+
25
+ ```
26
+ 1. Do NOT generate CSS values by guessing or eyeballing screenshots.
27
+ ✅ Use clone-extract.js computed CSS output as-is.
28
+ ❌ Do NOT write CSS values directly inside scoped <style> blocks.
29
+
30
+ 2. Do NOT hotlink remote assets. All images/fonts must be downloaded and rewritten.
31
+
32
+ 3. Do NOT skip the pixel verification loop (Phase 5). The clone is incomplete without it.
33
+
34
+ 4. Do NOT copy textual content verbatim from copyrighted sources for production use.
35
+ This skill is for layout/markup learning ("클론 코딩"). Replace text with placeholders
36
+ or user-provided copy when shipping a real product.
37
+ ```
38
+
39
+ ## Full Flow
40
+
41
+ ```
42
+ Input: a URL (or multiple URLs for multi-page clones)
43
+
44
+ → Phase 0: Setup (stack detection, feature naming, working dir)
45
+ → Phase 1: Capture (Puppeteer → rendered HTML + computed CSS + screenshots + assets)
46
+ → Phase 2: Refine (DOM → sections.json per breakpoint)
47
+ → Phase 3: Scaffold (sections.json → SCSS auto-gen + Claude-authored HTML/components)
48
+ → Phase 4: Compile gate
49
+ → Phase 5: Pixel verification loop
50
+
51
+ Working directory:
52
+ /tmp/{feature}/
53
+ ├── mo/ (375×812) — rendered.html, computed.json, screenshot.png, assets/, sections.json
54
+ ├── pc/ (1440×900) — rendered.html, computed.json, screenshot.png, assets/, sections.json
55
+ └── tokens.json — extracted design tokens (colors/fonts/spacing)
56
+
57
+ Code output: placed directly in the project directory per detected stack
58
+ components/{feature}/, styles/{feature}/, public/images/{feature}/
59
+ ```
60
+
61
+ ---
62
+
63
+ ## Phase 0: Setup
64
+
65
+ ```
66
+ 1. Stack detection:
67
+ - .vibe/config.json → stack (react/vue/next/svelte/vanilla, scss/tailwind/css-modules)
68
+ - Fallback: package.json deps
69
+ 2. Feature name: URL hostname → kebab-case (e.g. stripe.com → stripe-clone)
70
+ - User may override with --name=<custom>
71
+ 3. Directories:
72
+ - components/{feature}/, styles/{feature}/, public/images/{feature}/
73
+ 4. Component indexing → /tmp/{feature}/component-index.json
74
+ (scan up to 50 existing components, extract props/slots/classes, within 2 minutes)
75
+ 5. Design token scan → /tmp/{feature}/project-tokens.json
76
+ (SCSS > CSS Variables > Tailwind > CSS-in-JS)
77
+ ```
78
+
79
+ ---
80
+
81
+ ## Phase 1: Capture ← Headless browser (parallel MO/PC)
82
+
83
+ **Coordinator pattern: run MO/PC capture as parallel workers.**
84
+
85
+ ### BLOCKING Command — Use only clone-extract.js for capture
86
+
87
+ ```bash
88
+ # [CLONE_SCRIPT] = {{VIBE_PATH}}/hooks/scripts/clone-extract.js
89
+
90
+ # Mobile (375×812)
91
+ node {{VIBE_PATH}}/hooks/scripts/clone-extract.js capture <URL> \
92
+ --out=/tmp/{feature}/mo/ \
93
+ --viewport=375x812 \
94
+ --bp=mo
95
+
96
+ # Desktop (1440×900)
97
+ node {{VIBE_PATH}}/hooks/scripts/clone-extract.js capture <URL> \
98
+ --out=/tmp/{feature}/pc/ \
99
+ --viewport=1440x900 \
100
+ --bp=pc
101
+ ```
102
+
103
+ ⛔ **Writing custom capture scripts (puppeteer-fetch.mjs, etc.) is forbidden.**
104
+ ⛔ **Do NOT use WebFetch or curl** — they cannot render JS-driven SPAs.
105
+ ✅ Use clone-extract.js. If output is unsatisfactory, modify the script.
106
+
107
+ ### Output per breakpoint
108
+
109
+ ```
110
+ /tmp/{feature}/{bp}/
111
+ ├── rendered.html — final DOM after JS execution
112
+ ├── computed.json — per-element computed CSS + bounding box
113
+ ├── screenshot.png — full-page screenshot
114
+ ├── assets/
115
+ │ ├── images/ — all <img>, background-image, <picture> sources
116
+ │ └── fonts/ — @font-face srcs
117
+ └── asset-map.json — remote URL → local path mapping
118
+ ```
119
+
120
+ ### Capture rules
121
+
122
+ ```
123
+ 1. Wait for `networkidle2` (no in-flight requests for 500ms) before snapshot
124
+ 2. Scroll to bottom slowly to trigger lazy-loaded content
125
+ 3. Resolve all <img src>, srcset, and computed background-image URLs
126
+ 4. Resolve @font-face src() URLs from all stylesheets
127
+ 5. Download assets in parallel (concurrency=8), preserve original extensions
128
+ 6. Rewrite asset URLs in rendered.html and computed.json to local paths
129
+ 7. Strip inline analytics/tracking scripts before saving rendered.html
130
+ ```
131
+
132
+ ---
133
+
134
+ ## Phase 2: Refine ← DOM → sections.json (independent per BP)
135
+
136
+ ### BLOCKING Command — Writing custom refine scripts is forbidden
137
+
138
+ ```bash
139
+ # MO
140
+ node {{VIBE_PATH}}/hooks/scripts/clone-refine.js \
141
+ /tmp/{feature}/mo/rendered.html \
142
+ /tmp/{feature}/mo/computed.json \
143
+ --out=/tmp/{feature}/mo/sections.json \
144
+ --bp=mo
145
+
146
+ # PC
147
+ node {{VIBE_PATH}}/hooks/scripts/clone-refine.js \
148
+ /tmp/{feature}/pc/rendered.html \
149
+ /tmp/{feature}/pc/computed.json \
150
+ --out=/tmp/{feature}/pc/sections.json \
151
+ --bp=pc
152
+ ```
153
+
154
+ ⛔ **Phase 3 is blocked until refine completes for all required BPs.**
155
+ ⛔ **Do NOT parse rendered.html with custom Python/Node scripts.**
156
+ ✅ Use clone-refine.js output as-is. If unsatisfactory, modify the script.
157
+
158
+ ### Refinement rules
159
+
160
+ ```
161
+ Refinement applied when converting rendered.html + computed.json → sections.json:
162
+ 1. Strip <script>, <noscript>, <style>, tracking pixels
163
+ 2. Strip nodes with display:none, visibility:hidden, opacity:0, size 0
164
+ 3. Detect sections: <header>, <nav>, top-level <section>/<main> children, <footer>
165
+ Fallback: top-level children of <body> with height > 100px
166
+ 4. Detect repeated patterns (sibling nodes with same tag+class signature, count >= 3)
167
+ → mark as component candidates
168
+ 5. Extract design tokens:
169
+ - colors: unique color/background-color values, sorted by frequency
170
+ - typography: font-family/size/weight combinations
171
+ - spacing: padding/margin values (px), bucketed to nearest 4px
172
+ 6. Background images (background-image: url) → images.bg
173
+ 7. Inline <img> → images.content
174
+ 8. Keep CSS subset: layout (display/flex/grid/position/inset/margin/padding/width/height/gap),
175
+ typography (font-*, line-height, letter-spacing, text-*, color),
176
+ decoration (background, border, border-radius, box-shadow, opacity),
177
+ transform/transition
178
+ ```
179
+
180
+ ### Output
181
+
182
+ ```
183
+ /tmp/{feature}/{bp}/sections.json:
184
+ {
185
+ meta: { feature, url, viewport, bp },
186
+ tokens: { colors: [...], typography: [...], spacing: [...] },
187
+ sections: [
188
+ {
189
+ name: "Header" | "Hero" | "Features" | ...,
190
+ nodeRef, tag, size, css,
191
+ text, // text content (placeholder candidates)
192
+ components: [...], // detected repeated patterns
193
+ children: [...], // full recursive subtree
194
+ images: { bg, content: [...] }
195
+ }
196
+ ]
197
+ }
198
+ ```
199
+
200
+ ---
201
+
202
+ ## Phase 3: Scaffold ← stack-specific code generation
203
+
204
+ **⛔ Implement MO fully first → pass verification → then PC. No responsive conversion in this phase.**
205
+ **⛔ CSS values must use computed.json output as-is. No vw/clamp/@media in this phase.**
206
+
207
+ ### BLOCKING Command — SCSS must only use script output
208
+
209
+ ```bash
210
+ # Step A: Auto-generate SCSS skeleton (run once per BP)
211
+ node {{VIBE_PATH}}/hooks/scripts/clone-to-scss.js \
212
+ /tmp/{feature}/{bp}/sections.json \
213
+ --out=/path/to/project/styles/{feature}/ \
214
+ --token-file=/tmp/{feature}/project-tokens.json
215
+
216
+ # Step B: Per-section validation (after writing each section's component code)
217
+ node {{VIBE_PATH}}/hooks/scripts/clone-validate.js \
218
+ /path/to/project/styles/{feature}/ \
219
+ /tmp/{feature}/{bp}/sections.json \
220
+ --section={SectionName}
221
+ ```
222
+
223
+ ⛔ **Writing SCSS files directly without calling clone-to-scss.js invalidates Phase 3.**
224
+ ⛔ **Do NOT write custom SCSS generation scripts.**
225
+ ⛔ **Do NOT proceed to the next section without a clone-validate.js PASS.**
226
+ ✅ Use clone-to-scss.js output as-is. If unsatisfactory, modify the script.
227
+
228
+ ```
229
+ Phase 3A: MO Scaffold
230
+ Input: /tmp/{feature}/mo/sections.json
231
+ ⛔ No parallelism. Process one section at a time:
232
+ 1. Read the target section from sections.json
233
+ 2. Map component candidates against component-index.json
234
+ ├─ Match → import existing
235
+ └─ No match → create new in components/{feature}/
236
+ 3. clone-to-scss.js → auto-generate SCSS skeleton (computed values as-is) — Step A once
237
+ 4. Claude: HTML structure + semantic tags + framework-specific component file
238
+ ⛔ No CSS written directly in <style> blocks — only @import/@use allowed
239
+ Framework mapping:
240
+ - React/Next → .tsx with CSS Modules or styled-components per stack
241
+ - Vue/Nuxt → .vue with scoped <style lang="scss"> @import only
242
+ - Svelte → .svelte with <style> @import only
243
+ - Vanilla → .html + linked .scss
244
+ 5. Asset references → public/images/{feature}/ (already populated in Phase 1)
245
+ 6. clone-validate.js → compare SCSS vs sections.json — Step B
246
+ ├─ PASS → next section
247
+ └─ FAIL → fix discrepancies → re-run step 6 (loop until P1=0, no round cap)
248
+ → Phase 4 (MO compile) → Phase 5 (MO pixel verification)
249
+
250
+ Phase 3B: PC Scaffold
251
+ Same process as MO, input /tmp/{feature}/pc/sections.json
252
+ → Phase 4 (PC compile) → Phase 5 (PC pixel verification)
253
+
254
+ Phase 3C: Responsive Integration (after both MO+PC pass verification)
255
+ → Merge MO+PC styles into @media-based responsive layout (separate flow, TODO)
256
+
257
+ Claude's role (restricted):
258
+ ✅ Component candidates: decide which patterns become reusable components
259
+ ✅ HTML semantics: section/h1/p/button/nav tag selection
260
+ ✅ Text replacement: substitute copyrighted copy with placeholders or user-supplied text
261
+ ✅ Interactions: hover/focus states, click handlers, conditional rendering
262
+ ❌ Do NOT modify SCSS CSS values (use clone-to-scss.js output as-is)
263
+ ❌ Do NOT write CSS directly in <style> blocks
264
+ ❌ Do NOT use vw/clamp/@media or create custom mixins in Phase 3A/3B
265
+ ❌ Do NOT hotlink remote URLs — all assets must use local public/images/ paths
266
+ ```
267
+
268
+ ---
269
+
270
+ ## Phase 4: Compile Gate
271
+
272
+ ```
273
+ No round cap. Loop until compile succeeds (or stuck → ask user).
274
+
275
+ 0. Capture baseline (before Phase 3): record existing tsc + build errors
276
+ → Phase 4 only fixes NEW errors
277
+
278
+ 1. TypeScript: vue-tsc / svelte-check / tsc --noEmit
279
+ 2. Build: npm run build (120s timeout)
280
+ 3. Dev server: npm run dev → detect port → polling
281
+
282
+ On error: parse → auto-fix → re-check
283
+ Termination:
284
+ ✅ Success: all checks pass → enter Phase 5
285
+ ⚠️ Stuck: same errors as previous round → ask user
286
+ 1. Direct fix instructions → retry
287
+ 2. "proceed" — record remaining errors as TODO, proceed to Phase 5
288
+ 3. "abort" — halt
289
+ ultrawork mode: on stuck, record TODO without prompting and proceed
290
+
291
+ ⛔ Must enter Phase 5 after Phase 4 passes. Do NOT output a "completion summary".
292
+ ```
293
+
294
+ ---
295
+
296
+ ## Phase 5: Pixel Verification Loop ← MANDATORY
297
+
298
+ **⛔ Phase 5 is mandatory, not optional. Enter automatically after Phase 4.**
299
+ **⛔ Skipping Phase 5 makes the entire clone "incomplete".**
300
+
301
+ ```
302
+ No round cap. Loop until P1=0 (or stuck → ask user).
303
+ Infrastructure: src/infra/lib/browser/ (Puppeteer + CDP) — same as figma Phase 6.
304
+
305
+ 1. Render scaffolded page in dev server at matching viewport
306
+ 2. Capture screenshot → pixelmatch comparison against /tmp/{feature}/{bp}/screenshot.png
307
+ diffRatio > 0.05 (clone target is tighter than figma) → P1
308
+ 3. CSS comparison: live computed CSS vs /tmp/{feature}/{bp}/computed.json
309
+ delta > 2px → P1, ≤ 2px → P2
310
+ 4. Asset audit: every <img>/background-image resolves to local public/images/ path → else P1
311
+ 5. Fix P1 first (refer to computed.json, no guessing) → revalidate compile → reload
312
+
313
+ Narrowing scope:
314
+ Round 1: P1+P2+P3
315
+ Round 2: P1+P2
316
+ Round 3+: P1 only
317
+
318
+ Termination:
319
+ ✅ P1=0 AND no new findings → complete
320
+ ⚠️ Stuck: same findings → ask user (resolve / proceed / abort)
321
+ ultrawork mode: on stuck, record TODO without prompting and complete
322
+
323
+ Responsive: after MO verification → change viewport → repeat against PC screenshot
324
+ Cleanup: shut down browser + dev server
325
+
326
+ ⛔ "Completion summary" output only allowed after Phase 5 completes.
327
+ ```
328
+
329
+ ---
330
+
331
+ ## Legal & Ethical Notes
332
+
333
+ ```
334
+ This skill is intended for:
335
+ ✅ "Clone coding" learning exercises (markup/layout study)
336
+ ✅ Rebuilding the user's own previously-deployed sites
337
+ ✅ Authorized redesigns where the user has rights to the source
338
+
339
+ NOT intended for:
340
+ ❌ Republishing copyrighted content (text, images, logos) without permission
341
+ ❌ Deceptive look-alike sites (phishing, brand impersonation)
342
+ ❌ Bypassing robots.txt or rate-limiting protections
343
+
344
+ Claude must:
345
+ - Replace copyrighted text content with placeholders (e.g. "[Lorem ipsum]") by default
346
+ - Skip and warn when robots.txt disallows fetching the target path
347
+ - Refuse if the user's stated intent is brand impersonation or deception
348
+ ```
349
+
350
+ ---
351
+
352
+ ## Error Recovery
353
+
354
+ | Failure | Recovery |
355
+ |---------|----------|
356
+ | clone-extract.js Puppeteer launch failure | Verify Node ≥18 and that Chromium is installed (`npx puppeteer browsers install chrome`). Retry once. |
357
+ | Target site blocks headless (403/Cloudflare) | Retry with `--stealth` flag (uses puppeteer-extra stealth plugin). If still blocked, report to user. |
358
+ | Asset download 404 | Log to asset-map.json with `status: missing`. Use a 1×1 transparent placeholder. Continue. |
359
+ | robots.txt disallows path | Halt Phase 1. Inform user; require explicit `--ignore-robots` flag to proceed. |
360
+ | clone-refine.js produces empty sections | Site likely uses Shadow DOM or canvas rendering. Report and ask whether to fall back to screenshot-only mode. |
361
+ | Pixel diff stuck > 0.05 after 5 rounds | Likely font fallback or anti-aliasing. Report metric, allow user to accept threshold. |
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: commerce-patterns
3
+ invocation: [auto]
3
4
  tier: core
4
5
  description: "E-commerce domain patterns — cart management, payment processing (Toss/Stripe/PG), inventory tracking, and order state machines with transaction safety. Use when implementing any shopping cart, checkout flow, payment integration, stock management, or order lifecycle. Covers idempotency keys, double-charge prevention, stock reservation, and refund flows. Must use this skill when the codebase involves e-commerce — even for seemingly simple 'add to cart' features."
5
6
  triggers: [commerce, ecommerce, cart, payment, checkout, inventory, stock, order, pg, toss, stripe]
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: commit-push-pr
3
+ invocation: [auto, chain]
3
4
  tier: optional
4
5
  description: "Commit, push, and create PR in one go. Auto-activates on commit, PR, push keywords."
5
6
  triggers: [commit, push, PR, pull request, merge]
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: context7-usage
3
+ invocation: [auto]
3
4
  tier: optional
4
5
  description: "Context7 plugin for latest library documentation. Auto-activates when docs, documentation, latest version, official docs, API reference, or library help is needed."
5
6
  triggers: [docs, documentation, latest version, official docs, API reference, library help, context7]
@@ -1,10 +1,9 @@
1
1
  ---
2
- name: vibe-contract
2
+ name: contract
3
+ description: API contract drift 탐지 본체 — SPEC contract 추출 → 구현 비교 → breaking drift P1을 regress로 자동 등록.
4
+ when_to_use: /vibe.contract 진입점 / /vibe.spec 완료 / /vibe.verify 통과 시 체인. 직접 호출 금지.
5
+ user-invocable: false
3
6
  tier: core
4
- description: "API contract drift detection. Extracts HTTP/GraphQL/event/public-function contracts from SPEC into .vibe/contracts/<feature>.md, compares to implementation, and fails loudly on breaking drift (missing endpoints, removed required fields, type changes). P1 drifts auto-register as regressions via vibe-regress. Must use this skill when user runs /vibe.contract, when /vibe.spec completes, when /vibe.verify passes scenarios, or when the user says 'contract', 'API schema', 'breaking change', 'drift', '계약', '스키마 바뀜'."
5
- triggers: [contract, drift, "계약", "API 변경", "breaking change", "schema drift"]
6
- priority: 70
7
- chain-next: []
8
7
  ---
9
8
 
10
9
  # vibe.contract — API Contract Drift Detection
@@ -116,7 +115,7 @@ endpoints:
116
115
 
117
116
  Auto-invoke right after the SPEC is written:
118
117
  ```
119
- Load skill `vibe-contract` with: extract <feature>
118
+ Load skill `contract` with: extract <feature>
120
119
  ```
121
120
  Failure does not stop `/vibe.spec` (extraction is optional). On success, `/vibe.run` references this contract.
122
121
 
@@ -124,7 +123,7 @@ Failure does not stop `/vibe.spec` (extraction is optional). On success, `/vibe.
124
123
 
125
124
  After all scenarios pass:
126
125
  ```
127
- Load skill `vibe-contract` with: check <feature>
126
+ Load skill `contract` with: check <feature>
128
127
  ```
129
128
  - no drift → verify still passes
130
129
  - P1 drift → demote verify to fail; auto-register
@@ -134,7 +133,7 @@ Load skill `vibe-contract` with: check <feature>
134
133
 
135
134
  On P1 drift:
136
135
  ```
137
- Load skill `vibe-regress` with:
136
+ Load skill `regress` with:
138
137
  subcommand: register --from-contract
139
138
  feature: <feature>
140
139
  symptom: "Contract drift: <endpoint-id> <drift-type>"
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: create-prd
3
+ invocation: [auto, chain]
3
4
  tier: standard
4
5
  description: "Create a Product Requirements Document using a comprehensive 8-section template covering problem, objectives, segments, value propositions, solution, and release planning."
5
6
  triggers: [prd, product requirements, feature spec, requirements document]
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: design-audit
3
+ invocation: [command, auto]
3
4
  tier: standard
4
5
  description: "Design technical quality audit — a11y, performance, responsive, theming, AI slop detection with 5-dimension scoring. Use when design-audit, ui-audit, a11y-check, design-check."
5
6
  triggers: [design-audit, ui-audit, a11y-check, design-check]
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: design-critique
3
+ invocation: [auto]
3
4
  tier: standard
4
5
  description: "UX design review — Nielsen heuristics scoring, 5-persona red flag analysis. Use when design-critique, ux-review, design-review."
5
6
  triggers: [design-critique, ux-review, design-review]