@su-record/vibe 2.8.23 → 2.8.25

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 (223) hide show
  1. package/commands/vibe.figma.md +2 -2
  2. package/dist/cli/commands/config.d.ts +17 -0
  3. package/dist/cli/commands/config.d.ts.map +1 -0
  4. package/dist/cli/commands/config.js +207 -0
  5. package/dist/cli/commands/config.js.map +1 -0
  6. package/dist/cli/commands/index.d.ts +2 -0
  7. package/dist/cli/commands/index.d.ts.map +1 -1
  8. package/dist/cli/commands/index.js +2 -0
  9. package/dist/cli/commands/index.js.map +1 -1
  10. package/dist/cli/commands/info.d.ts.map +1 -1
  11. package/dist/cli/commands/info.js +2 -0
  12. package/dist/cli/commands/info.js.map +1 -1
  13. package/dist/cli/commands/init.d.ts.map +1 -1
  14. package/dist/cli/commands/init.js +78 -54
  15. package/dist/cli/commands/init.js.map +1 -1
  16. package/dist/cli/commands/stats.d.ts +13 -0
  17. package/dist/cli/commands/stats.d.ts.map +1 -0
  18. package/dist/cli/commands/stats.js +280 -0
  19. package/dist/cli/commands/stats.js.map +1 -0
  20. package/dist/cli/index.d.ts.map +1 -1
  21. package/dist/cli/index.js +33 -1
  22. package/dist/cli/index.js.map +1 -1
  23. package/dist/cli/postinstall/constants.d.ts.map +1 -1
  24. package/dist/cli/postinstall/constants.js +1 -0
  25. package/dist/cli/postinstall/constants.js.map +1 -1
  26. package/dist/cli/setup/GlobalInstaller.d.ts.map +1 -1
  27. package/dist/cli/setup/GlobalInstaller.js +7 -7
  28. package/dist/cli/setup/GlobalInstaller.js.map +1 -1
  29. package/dist/cli/setup/ProjectSetup.d.ts.map +1 -1
  30. package/dist/cli/setup/ProjectSetup.js +18 -12
  31. package/dist/cli/setup/ProjectSetup.js.map +1 -1
  32. package/dist/infra/lib/ContextCompressor.d.ts.map +1 -1
  33. package/dist/infra/lib/ContextCompressor.js +10 -4
  34. package/dist/infra/lib/ContextCompressor.js.map +1 -1
  35. package/dist/infra/lib/ProjectCache.d.ts +2 -2
  36. package/dist/infra/lib/ProjectCache.d.ts.map +1 -1
  37. package/dist/infra/lib/ProjectCache.js +4 -3
  38. package/dist/infra/lib/ProjectCache.js.map +1 -1
  39. package/dist/infra/lib/utils.d.ts +24 -0
  40. package/dist/infra/lib/utils.d.ts.map +1 -1
  41. package/dist/infra/lib/utils.js +41 -0
  42. package/dist/infra/lib/utils.js.map +1 -1
  43. package/dist/infra/orchestrator/SmartRouter.d.ts +3 -0
  44. package/dist/infra/orchestrator/SmartRouter.d.ts.map +1 -1
  45. package/dist/infra/orchestrator/SmartRouter.js +11 -1
  46. package/dist/infra/orchestrator/SmartRouter.js.map +1 -1
  47. package/dist/infra/orchestrator/SmartRouter.test.d.ts +5 -0
  48. package/dist/infra/orchestrator/SmartRouter.test.d.ts.map +1 -0
  49. package/dist/infra/orchestrator/SmartRouter.test.js +457 -0
  50. package/dist/infra/orchestrator/SmartRouter.test.js.map +1 -0
  51. package/dist/tools/convention/analyzeComplexity.d.ts.map +1 -1
  52. package/dist/tools/convention/analyzeComplexity.js +18 -10
  53. package/dist/tools/convention/analyzeComplexity.js.map +1 -1
  54. package/dist/tools/convention/checkCouplingCohesion.d.ts.map +1 -1
  55. package/dist/tools/convention/checkCouplingCohesion.js +14 -6
  56. package/dist/tools/convention/checkCouplingCohesion.js.map +1 -1
  57. package/dist/tools/semantic/analyzeDependencyGraph.d.ts.map +1 -1
  58. package/dist/tools/semantic/analyzeDependencyGraph.js +1 -1
  59. package/dist/tools/semantic/analyzeDependencyGraph.js.map +1 -1
  60. package/dist/tools/semantic/findReferences.d.ts.map +1 -1
  61. package/dist/tools/semantic/findReferences.js +13 -13
  62. package/dist/tools/semantic/findReferences.js.map +1 -1
  63. package/dist/tools/semantic/findSymbol.d.ts.map +1 -1
  64. package/dist/tools/semantic/findSymbol.js +12 -13
  65. package/dist/tools/semantic/findSymbol.js.map +1 -1
  66. package/dist/tools/semantic/lsp.d.ts.map +1 -1
  67. package/dist/tools/semantic/lsp.js +22 -14
  68. package/dist/tools/semantic/lsp.js.map +1 -1
  69. package/hooks/hooks.json +29 -0
  70. package/hooks/scripts/__tests__/keyword-detector.test.js +199 -0
  71. package/hooks/scripts/__tests__/pre-tool-guard.test.js +286 -0
  72. package/hooks/scripts/__tests__/sentinel-guard.test.js +210 -0
  73. package/hooks/scripts/auto-commit.js +65 -0
  74. package/hooks/scripts/auto-format.js +64 -0
  75. package/hooks/scripts/auto-test.js +81 -0
  76. package/hooks/scripts/code-check.js +139 -0
  77. package/hooks/scripts/command-log.js +32 -0
  78. package/hooks/scripts/context-save.js +60 -6
  79. package/hooks/scripts/hud-status.js +32 -2
  80. package/hooks/scripts/llm-orchestrate.js +95 -17
  81. package/hooks/scripts/pr-test-gate.js +52 -0
  82. package/package.json +1 -1
  83. package/skills/agents-md/rubrics/what-to-keep.md +49 -0
  84. package/skills/agents-md/templates/agents-md.md +36 -0
  85. package/skills/arch-guard/agents/detector.md +48 -0
  86. package/skills/arch-guard/agents/reporter.md +48 -0
  87. package/skills/arch-guard/agents/rule-generator.md +49 -0
  88. package/skills/arch-guard/agents/violation-checker.md +51 -0
  89. package/skills/arch-guard/frameworks/clean-architecture.md +108 -0
  90. package/skills/arch-guard/frameworks/solid.md +102 -0
  91. package/skills/arch-guard/scripts/check-boundaries.js +90 -0
  92. package/skills/arch-guard/templates/arch-rules.json +47 -0
  93. package/skills/arch-guard/templates/violation-report.md +53 -0
  94. package/skills/brand-assets/rubrics/asset-checklist.md +98 -0
  95. package/skills/brand-assets/templates/brand-guide.md +161 -0
  96. package/skills/capability-loop/agents/capability-designer.md +61 -0
  97. package/skills/capability-loop/agents/failure-analyst.md +55 -0
  98. package/skills/capability-loop/agents/implementer.md +50 -0
  99. package/skills/capability-loop/agents/tester.md +53 -0
  100. package/skills/capability-loop/templates/capability-spec.md +118 -0
  101. package/skills/capability-loop/templates/failure-analysis.md +118 -0
  102. package/skills/characterization-test/agents/behavior-capturer.md +50 -0
  103. package/skills/characterization-test/agents/coverage-checker.md +54 -0
  104. package/skills/characterization-test/agents/reporter.md +50 -0
  105. package/skills/characterization-test/agents/test-writer.md +49 -0
  106. package/skills/characterization-test/rubrics/coverage-criteria.md +53 -0
  107. package/skills/characterization-test/templates/test-template.ts +101 -0
  108. package/skills/claude-md-guide/rubrics/anti-patterns.md +88 -0
  109. package/skills/claude-md-guide/templates/claude-md.md +54 -0
  110. package/skills/commerce-patterns/rubrics/checkout-flow.md +48 -0
  111. package/skills/commerce-patterns/templates/product-schema.md +85 -0
  112. package/skills/commit-push-pr/agents/change-analyzer.md +55 -0
  113. package/skills/commit-push-pr/agents/message-writer.md +50 -0
  114. package/skills/commit-push-pr/agents/pr-writer.md +58 -0
  115. package/skills/commit-push-pr/agents/reviewer.md +52 -0
  116. package/skills/commit-push-pr/rubrics/commit-message.md +73 -0
  117. package/skills/commit-push-pr/templates/pr-body.md +63 -0
  118. package/skills/context7-usage/rubrics/when-to-use.md +50 -0
  119. package/skills/create-prd/agents/edge-case-finder.md +48 -0
  120. package/skills/create-prd/agents/prioritizer.md +60 -0
  121. package/skills/create-prd/agents/requirements-writer.md +48 -0
  122. package/skills/create-prd/agents/researcher.md +55 -0
  123. package/skills/create-prd/agents/reviewer.md +54 -0
  124. package/skills/create-prd/frameworks/jobs-to-be-done.md +96 -0
  125. package/skills/create-prd/frameworks/rice-scoring.md +97 -0
  126. package/skills/create-prd/orchestrator.md +70 -0
  127. package/skills/create-prd/rubrics/completeness.md +58 -0
  128. package/skills/create-prd/templates/prd.md +139 -0
  129. package/skills/design-audit/agents/a11y-auditor.md +43 -0
  130. package/skills/design-audit/agents/performance-auditor.md +46 -0
  131. package/skills/design-audit/agents/responsive-auditor.md +46 -0
  132. package/skills/design-audit/agents/scorer.md +47 -0
  133. package/skills/design-audit/agents/slop-detector.md +47 -0
  134. package/skills/design-audit/frameworks/core-web-vitals.md +107 -0
  135. package/skills/design-audit/frameworks/wcag-checklist.md +64 -0
  136. package/skills/design-audit/orchestrator.md +64 -0
  137. package/skills/design-audit/rubrics/ai-slop-patterns.md +83 -0
  138. package/skills/design-audit/rubrics/scoring.md +63 -0
  139. package/skills/design-audit/templates/report.md +88 -0
  140. package/skills/design-critique/rubrics/ux-heuristics.md +143 -0
  141. package/skills/design-critique/templates/critique-report.md +86 -0
  142. package/skills/design-distill/templates/design-system.md +132 -0
  143. package/skills/design-normalize/rubrics/token-naming.md +117 -0
  144. package/skills/design-normalize/templates/token-audit.md +89 -0
  145. package/skills/design-polish/rubrics/polish-checklist.md +68 -0
  146. package/skills/design-polish/templates/polish-report.md +64 -0
  147. package/skills/design-teach/rubrics/brand-personality.md +73 -0
  148. package/skills/design-teach/templates/design-context.json +36 -0
  149. package/skills/e2e-commerce/templates/test-scenarios.md +170 -0
  150. package/skills/event-comms/templates/email-invite.md +99 -0
  151. package/skills/event-comms/templates/sns-post.md +133 -0
  152. package/skills/event-ops/rubrics/contingency.md +85 -0
  153. package/skills/event-ops/templates/d-day-checklist.md +65 -0
  154. package/skills/event-planning/rubrics/timeline.md +70 -0
  155. package/skills/event-planning/templates/event-plan.md +91 -0
  156. package/skills/exec-plan/agents/decomposer.md +47 -0
  157. package/skills/exec-plan/agents/dependency-mapper.md +44 -0
  158. package/skills/exec-plan/agents/estimator.md +43 -0
  159. package/skills/exec-plan/agents/validator.md +55 -0
  160. package/skills/exec-plan/orchestrator.md +70 -0
  161. package/skills/exec-plan/rubrics/complexity-scoring.md +75 -0
  162. package/skills/exec-plan/templates/plan.md +147 -0
  163. package/skills/git-worktree/rubrics/when-to-use.md +55 -0
  164. package/skills/handoff/agents/context-summarizer.md +51 -0
  165. package/skills/handoff/agents/document-writer.md +63 -0
  166. package/skills/handoff/agents/state-collector.md +53 -0
  167. package/skills/handoff/agents/verifier.md +48 -0
  168. package/skills/handoff/rubrics/completeness.md +62 -0
  169. package/skills/handoff/templates/handoff.md +107 -0
  170. package/skills/parallel-research/agents/best-practices.md +43 -0
  171. package/skills/parallel-research/agents/codebase-patterns.md +46 -0
  172. package/skills/parallel-research/agents/framework-docs.md +45 -0
  173. package/skills/parallel-research/agents/security-advisory.md +46 -0
  174. package/skills/parallel-research/agents/synthesizer.md +52 -0
  175. package/skills/parallel-research/experts/best-practices.md +50 -0
  176. package/skills/parallel-research/experts/codebase-patterns.md +70 -0
  177. package/skills/parallel-research/experts/framework-docs.md +65 -0
  178. package/skills/parallel-research/experts/security-advisory.md +69 -0
  179. package/skills/parallel-research/orchestrator.md +65 -0
  180. package/skills/parallel-research/templates/synthesis.md +101 -0
  181. package/skills/prioritization-frameworks/rubrics/frameworks.md +79 -0
  182. package/skills/prioritization-frameworks/templates/scoring-matrix.md +69 -0
  183. package/skills/priority-todos/rubrics/prioritization.md +70 -0
  184. package/skills/priority-todos/templates/todo-board.md +59 -0
  185. package/skills/seo-checklist/frameworks/structured-data.md +153 -0
  186. package/skills/seo-checklist/rubrics/content-seo.md +42 -0
  187. package/skills/seo-checklist/rubrics/technical-seo.md +48 -0
  188. package/skills/techdebt/agents/analyzer.md +50 -0
  189. package/skills/techdebt/agents/fixer.md +41 -0
  190. package/skills/techdebt/agents/reviewer.md +47 -0
  191. package/skills/techdebt/agents/scanner.md +44 -0
  192. package/skills/techdebt/orchestrator.md +70 -0
  193. package/skills/techdebt/rubrics/severity.md +51 -0
  194. package/skills/techdebt/scripts/scan.js +90 -0
  195. package/skills/techdebt/templates/report.md +86 -0
  196. package/skills/tool-fallback/rubrics/fallback-chain.md +58 -0
  197. package/skills/typescript-advanced-types/rubrics/type-patterns.md +109 -0
  198. package/skills/ui-ux-pro-max/rubrics/interaction-states.md +83 -0
  199. package/skills/ui-ux-pro-max/rubrics/responsive-breakpoints.md +99 -0
  200. package/skills/user-personas/rubrics/research-methods.md +56 -0
  201. package/skills/user-personas/templates/persona.md +89 -0
  202. package/skills/vercel-react-best-practices/rubrics/performance.md +82 -0
  203. package/skills/vercel-react-best-practices/rubrics/server-components.md +86 -0
  204. package/skills/vibe-docs/SKILL.md +171 -0
  205. package/skills/vibe-docs/templates/architecture.md +80 -0
  206. package/skills/vibe-docs/templates/readme.md +84 -0
  207. package/skills/vibe-docs/templates/release-notes.md +74 -0
  208. package/skills/vibe-figma/SKILL.md +173 -54
  209. package/skills/vibe-figma/rubrics/extraction-checklist.md +51 -0
  210. package/skills/vibe-figma/templates/figma-handoff.md +96 -0
  211. package/skills/vibe-figma-analyze/rubrics/analysis-dimensions.md +53 -0
  212. package/skills/vibe-figma-codegen/rubrics/code-quality.md +54 -0
  213. package/skills/vibe-figma-consolidate/templates/consolidation-report.md +95 -0
  214. package/skills/vibe-figma-convert/SKILL.md +176 -1
  215. package/skills/vibe-figma-convert/rubrics/conversion-rules.md +83 -0
  216. package/skills/vibe-figma-convert/templates/component.md +152 -0
  217. package/skills/vibe-figma-extract/rubrics/image-rules.md +67 -0
  218. package/skills/vibe-figma-frame/rubrics/frame-selection.md +55 -0
  219. package/skills/vibe-figma-pipeline/rubrics/pipeline-stages.md +96 -0
  220. package/skills/vibe-figma-rules/rubrics/naming-conventions.md +70 -0
  221. package/skills/vibe-figma-style/rubrics/style-mapping.md +100 -0
  222. package/skills/video-production/rubrics/quality-checklist.md +58 -0
  223. package/skills/video-production/templates/production-plan.md +104 -0
@@ -24,6 +24,43 @@ const summaryMap = {
24
24
  // Debounce: track last reflection level per session to avoid duplicates
25
25
  let lastReflectionLevel = null;
26
26
 
27
+ // Guard against recursive reflection and rapid threshold crossings
28
+ let reflectionInProgress = false;
29
+ const DEBOUNCE_LOCKFILE = path.join(
30
+ process.env.HOME || process.env.USERPROFILE || '/tmp',
31
+ '.claude', '.vibe-hud', '.context-save-lock'
32
+ );
33
+ const DEBOUNCE_INTERVAL_MS = 30000; // 30 seconds between saves at same urgency
34
+
35
+ /**
36
+ * Check if a recent save at the same urgency already happened (cross-process debounce)
37
+ */
38
+ function isDebounceLocked(level) {
39
+ try {
40
+ const lockFile = `${DEBOUNCE_LOCKFILE}-${level}`;
41
+ if (fs.existsSync(lockFile)) {
42
+ const stat = fs.statSync(lockFile);
43
+ const ageMs = Date.now() - stat.mtimeMs;
44
+ if (ageMs < DEBOUNCE_INTERVAL_MS) return true;
45
+ }
46
+ } catch { /* ignore */ }
47
+ return false;
48
+ }
49
+
50
+ /**
51
+ * Set debounce lock for a given urgency level
52
+ */
53
+ function setDebounceLock(level) {
54
+ try {
55
+ const lockFile = `${DEBOUNCE_LOCKFILE}-${level}`;
56
+ const lockDir = path.dirname(lockFile);
57
+ if (!fs.existsSync(lockDir)) {
58
+ fs.mkdirSync(lockDir, { recursive: true });
59
+ }
60
+ fs.writeFileSync(lockFile, String(Date.now()));
61
+ } catch { /* ignore */ }
62
+ }
63
+
27
64
  /**
28
65
  * 프로젝트 환경에서 구조적 메타데이터를 추출.
29
66
  * 실패 시 빈 객체 반환 (non-blocking).
@@ -102,6 +139,11 @@ function buildStructuredSummary(baseMessage, metadata) {
102
139
  }
103
140
 
104
141
  async function main() {
142
+ // Cross-process debounce: skip if recently saved at same urgency
143
+ if (isDebounceLocked(urgency)) {
144
+ return;
145
+ }
146
+
105
147
  try {
106
148
  const metadata = extractStructuredMetadata();
107
149
  const baseMessage = summaryMap[urgency] || summaryMap.medium;
@@ -117,6 +159,9 @@ async function main() {
117
159
  const percent = urgency === 'critical' ? '95' : urgency === 'high' ? '90' : '80';
118
160
  console.log(`[CONTEXT ${percent}%]`, result.content[0].text);
119
161
 
162
+ // Mark this urgency level as recently saved
163
+ setDebounceLock(urgency);
164
+
120
165
  // Sync TokenBudgetTracker with current context usage
121
166
  try {
122
167
  const { TokenBudgetTracker } = await import(`${LIB_URL}TokenBudgetTracker.js`);
@@ -132,14 +177,20 @@ async function main() {
132
177
  // 무시
133
178
  }
134
179
 
180
+ // Guard against recursive reflection (setImmediate can re-enter)
181
+ if (reflectionInProgress) return;
182
+
135
183
  // Minor Reflection (80% = medium): 컨텍스트 압력 시 자동 성찰
136
184
  if (urgency === 'medium' && lastReflectionLevel !== 'medium') {
137
185
  lastReflectionLevel = 'medium';
186
+ reflectionInProgress = true;
138
187
  // Fire-and-forget: 기존 context-save에 영향 없음
139
188
  setImmediate(() => {
140
- triggerMinorReflection().catch(e => {
141
- process.stderr.write(`[REFLECTION] minor reflection failed: ${e.message}\n`);
142
- });
189
+ triggerMinorReflection()
190
+ .catch(e => {
191
+ process.stderr.write(`[REFLECTION] minor reflection failed: ${e.message}\n`);
192
+ })
193
+ .finally(() => { reflectionInProgress = false; });
143
194
  });
144
195
  }
145
196
 
@@ -165,10 +216,13 @@ async function main() {
165
216
  // Major Reflection: 세션 종료 시 전체 성찰
166
217
  if (lastReflectionLevel !== 'critical') {
167
218
  lastReflectionLevel = 'critical';
219
+ reflectionInProgress = true;
168
220
  setImmediate(() => {
169
- triggerMajorReflection().catch(e => {
170
- process.stderr.write(`[REFLECTION] major reflection failed: ${e.message}\n`);
171
- });
221
+ triggerMajorReflection()
222
+ .catch(e => {
223
+ process.stderr.write(`[REFLECTION] major reflection failed: ${e.message}\n`);
224
+ })
225
+ .finally(() => { reflectionInProgress = false; });
172
226
  });
173
227
  }
174
228
  }
@@ -12,6 +12,11 @@ import os from 'os';
12
12
  const STATE_DIR = path.join(os.homedir(), '.claude', '.vibe-hud');
13
13
  const STATE_FILE = path.join(STATE_DIR, 'state.json');
14
14
 
15
+ // Write debounce configuration
16
+ const WRITE_DEBOUNCE_MS = 500;
17
+ let pendingState = null;
18
+ let writeTimer = null;
19
+
15
20
  // 기본 상태
16
21
  const DEFAULT_STATE = {
17
22
  mode: 'idle', // idle | ultrawork | spec | review
@@ -65,14 +70,31 @@ function loadState() {
65
70
  }
66
71
 
67
72
  /**
68
- * 상태 저장
73
+ * 상태를 디스크에 즉시 기록
69
74
  */
70
- function saveState(state) {
75
+ function flushState(state) {
71
76
  ensureStateDir();
72
77
  state.lastUpdate = new Date().toISOString();
73
78
  fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
74
79
  }
75
80
 
81
+ /**
82
+ * 상태 저장 (debounced - 빈번한 호출을 병합)
83
+ * CLI 종료 전 pending write가 있으면 flush 됨
84
+ */
85
+ function saveState(state) {
86
+ state.lastUpdate = new Date().toISOString();
87
+ pendingState = state;
88
+ if (writeTimer) clearTimeout(writeTimer);
89
+ writeTimer = setTimeout(() => {
90
+ if (pendingState) {
91
+ flushState(pendingState);
92
+ pendingState = null;
93
+ writeTimer = null;
94
+ }
95
+ }, WRITE_DEBOUNCE_MS);
96
+ }
97
+
76
98
  /**
77
99
  * 상태 업데이트
78
100
  */
@@ -286,6 +308,14 @@ Examples:
286
308
  }
287
309
  }
288
310
 
311
+ // Flush pending state on process exit to avoid data loss
312
+ process.on('exit', () => {
313
+ if (pendingState) {
314
+ flushState(pendingState);
315
+ pendingState = null;
316
+ }
317
+ });
318
+
289
319
  // 메인 실행
290
320
  const args = process.argv.slice(2);
291
321
  handleCommand(args);
@@ -39,10 +39,65 @@ const DEFAULT_SYSTEM_PROMPT = 'You are a helpful assistant.';
39
39
  const provider = process.argv[2] || 'gemini';
40
40
  const mode = process.argv[3] || 'orchestrate';
41
41
 
42
- // Retry configuration
42
+ // WHY 3 retries: Enough to ride out brief 503/overload blips (typically 1-2
43
+ // consecutive), but not so many that a genuinely down provider delays the
44
+ // fallback chain for minutes.
43
45
  const MAX_RETRIES = 3;
46
+ // WHY 2000ms initial delay: LLM rate-limit windows are typically 1-5s;
47
+ // starting at 2s with exponential backoff (2s, 4s, 8s) covers most reset intervals.
44
48
  const INITIAL_DELAY_MS = 2000;
45
49
 
50
+ // ============================================
51
+ // Response Cache (TTL-based, in-memory)
52
+ // ============================================
53
+ const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
54
+ const CACHE_MAX_ENTRIES = 50;
55
+ const responseCache = new Map();
56
+
57
+ function getCacheKey(providerName, prompt, sysPrompt, jsonMode) {
58
+ const hash = crypto.createHash('sha256')
59
+ .update(`${providerName}|${sysPrompt}|${prompt}|${jsonMode}`)
60
+ .digest('hex')
61
+ .slice(0, 16);
62
+ return hash;
63
+ }
64
+
65
+ function getCachedResponse(key) {
66
+ const entry = responseCache.get(key);
67
+ if (!entry) return null;
68
+ if (Date.now() - entry.timestamp > CACHE_TTL_MS) {
69
+ responseCache.delete(key);
70
+ return null;
71
+ }
72
+ return entry.result;
73
+ }
74
+
75
+ function setCachedResponse(key, result) {
76
+ // Evict oldest entries when cache is full
77
+ if (responseCache.size >= CACHE_MAX_ENTRIES) {
78
+ const oldestKey = responseCache.keys().next().value;
79
+ responseCache.delete(oldestKey);
80
+ }
81
+ responseCache.set(key, { result, timestamp: Date.now() });
82
+ }
83
+
84
+ // ============================================
85
+ // Simple Prompt Detection (early exit)
86
+ // WHY skip orchestration for simple prompts: Sending greetings/acks to an
87
+ // external LLM wastes latency and tokens — Claude handles these natively.
88
+ // ============================================
89
+ const SIMPLE_PROMPT_MAX_LEN = 20;
90
+ const SIMPLE_PROMPT_PATTERNS = [
91
+ /^(hi|hello|hey|thanks|thank you|ok|yes|no|y|n)\.?$/i,
92
+ /^(help|version|status)$/i,
93
+ ];
94
+
95
+ function isSimplePrompt(prompt) {
96
+ const trimmed = prompt.trim();
97
+ if (trimmed.length > SIMPLE_PROMPT_MAX_LEN) return false;
98
+ return SIMPLE_PROMPT_PATTERNS.some(p => p.test(trimmed));
99
+ }
100
+
46
101
  // Errors that should skip retry and go to fallback immediately
47
102
  const SKIP_RETRY_PATTERNS = [
48
103
  /rate.?limit/i,
@@ -123,7 +178,8 @@ function parseAnalyzeImageArgs(args) {
123
178
  // CLI Provider Functions
124
179
  // ============================================
125
180
 
126
- const CLI_TIMEOUT_MS = 120000;
181
+ const CLI_TIMEOUT_MS = 60000;
182
+ const CLI_FALLBACK_TIMEOUT_MS = 30000;
127
183
  const IS_WINDOWS = os.platform() === 'win32';
128
184
 
129
185
  function spawnCli(cmd, args, options) {
@@ -145,7 +201,7 @@ function buildCliPrompt(prompt, sysPrompt, jsonMode) {
145
201
  }
146
202
 
147
203
 
148
- function callCodexCli(prompt, sysPrompt, jsonMode, model) {
204
+ function callCodexCli(prompt, sysPrompt, jsonMode, model, timeoutMs) {
149
205
  const fullPrompt = buildCliPrompt(prompt, sysPrompt, jsonMode);
150
206
  const outputFile = path.join(os.tmpdir(), `vibe-codex-${crypto.randomUUID()}.txt`);
151
207
  // stdin pipe로 프롬프트 전달 (shell escaping 이슈 회피)
@@ -155,11 +211,12 @@ function callCodexCli(prompt, sysPrompt, jsonMode, model) {
155
211
  const vibeConfig = readVibeConfig();
156
212
  const apiKey = vibeConfig.credentials?.gpt?.apiKey || process.env.OPENAI_API_KEY;
157
213
  const env = apiKey ? { ...process.env, OPENAI_API_KEY: apiKey } : process.env;
214
+ const effectiveTimeout = timeoutMs || CLI_TIMEOUT_MS;
158
215
 
159
216
  return new Promise((resolve, reject) => {
160
217
  const proc = spawnCli('codex', args, {
161
218
  stdio: ['pipe', 'pipe', 'pipe'],
162
- timeout: CLI_TIMEOUT_MS,
219
+ timeout: effectiveTimeout,
163
220
  env,
164
221
  });
165
222
  proc.stdin.write(fullPrompt);
@@ -186,16 +243,17 @@ function callCodexCli(prompt, sysPrompt, jsonMode, model) {
186
243
  });
187
244
  }
188
245
 
189
- function callGeminiCli(prompt, sysPrompt, jsonMode, model) {
246
+ function callGeminiCli(prompt, sysPrompt, jsonMode, model, timeoutMs) {
190
247
  const fullPrompt = buildCliPrompt(prompt, sysPrompt, jsonMode);
191
248
  // -p 로 headless 모드, stdin으로 프롬프트 전달 (stdin is appended to -p value)
192
249
  const args = ['-p', '.', '-o', 'text'];
193
250
  if (model) args.push('-m', model);
251
+ const effectiveTimeout = timeoutMs || CLI_TIMEOUT_MS;
194
252
 
195
253
  return new Promise((resolve, reject) => {
196
254
  const proc = spawnCli('gemini', args, {
197
255
  stdio: ['pipe', 'pipe', 'pipe'],
198
- timeout: CLI_TIMEOUT_MS,
256
+ timeout: effectiveTimeout,
199
257
  });
200
258
  proc.stdin.write(fullPrompt);
201
259
  proc.stdin.end();
@@ -219,7 +277,7 @@ function callGeminiCli(prompt, sysPrompt, jsonMode, model) {
219
277
  });
220
278
  }
221
279
 
222
- async function callProvider(providerName, prompt, sysPrompt, jsonMode) {
280
+ async function callProvider(providerName, prompt, sysPrompt, jsonMode, timeoutMs) {
223
281
  const vibeConfig = readVibeConfig();
224
282
 
225
283
  if (providerName === 'gpt' || providerName === 'gpt-codex' || providerName === 'gpt-spark') {
@@ -231,23 +289,23 @@ async function callProvider(providerName, prompt, sysPrompt, jsonMode) {
231
289
  } else {
232
290
  model = vibeConfig.models?.gpt || process.env.GPT_MODEL || 'gpt-5.4';
233
291
  }
234
- return await callCodexCli(prompt, sysPrompt, jsonMode, model);
292
+ return await callCodexCli(prompt, sysPrompt, jsonMode, model, timeoutMs);
235
293
  }
236
294
 
237
295
  if (providerName === 'gemini') {
238
296
  const model = vibeConfig.models?.gemini || process.env.GEMINI_MODEL || 'gemini-3.1-pro-preview';
239
- return await callGeminiCli(prompt, sysPrompt, jsonMode, model);
297
+ return await callGeminiCli(prompt, sysPrompt, jsonMode, model, timeoutMs);
240
298
  }
241
299
 
242
300
  throw new Error(`Unknown provider: ${providerName}`);
243
301
  }
244
302
 
245
- async function callWithRetry(providerName, prompt, sysPrompt, jsonMode) {
303
+ async function callWithRetry(providerName, prompt, sysPrompt, jsonMode, timeoutMs) {
246
304
  let lastError;
247
305
 
248
306
  for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
249
307
  try {
250
- return { success: true, result: await callProvider(providerName, prompt, sysPrompt, jsonMode) };
308
+ return { success: true, result: await callProvider(providerName, prompt, sysPrompt, jsonMode, timeoutMs) };
251
309
  } catch (e) {
252
310
  lastError = e;
253
311
  const errorMsg = e.message || String(e);
@@ -447,26 +505,46 @@ async function main() {
447
505
  const cleanPrompt = prompt.replace(prefixPatterns[provider] || /^/, '').trim();
448
506
  const jsonMode = mode === 'orchestrate-json';
449
507
 
508
+ // Early exit: simple prompts don't need LLM orchestration
509
+ if (isSimplePrompt(cleanPrompt)) {
510
+ return;
511
+ }
512
+
513
+ // Check cache for identical prompts
514
+ const cacheKey = getCacheKey(provider, cleanPrompt, systemPrompt, jsonMode);
515
+ const cached = getCachedResponse(cacheKey);
516
+ if (cached) {
517
+ console.log(cached);
518
+ return;
519
+ }
520
+
450
521
  // Provider chain: primary → cross fallback
451
- // gpt-codex fallback: gpt-codex gemini (codex는 full gpt로 fallback하지 않음)
522
+ // WHY GPTGemini (not reverse): GPT is the primary code/reasoning model;
523
+ // Gemini serves as cross-vendor fallback so a single vendor outage never
524
+ // blocks the user. When Gemini is primary (e.g. web-search), GPT is fallback.
452
525
  const providerLabels = { gpt: 'GPT', 'gpt-codex': 'GPT Codex', gemini: 'Gemini' };
453
526
  const isGpt = provider === 'gpt' || provider === 'gpt-codex';
454
527
  const providerChain = isGpt
455
528
  ? [provider, 'gemini']
456
529
  : ['gemini', 'gpt'];
457
530
 
458
- for (const currentProvider of providerChain) {
531
+ for (let i = 0; i < providerChain.length; i++) {
532
+ const currentProvider = providerChain[i];
459
533
  const label = providerLabels[currentProvider] || currentProvider.toUpperCase();
460
- const result = await callWithRetry(currentProvider, cleanPrompt, systemPrompt, jsonMode);
534
+ // Use shorter timeout for fallback providers
535
+ const timeoutMs = i === 0 ? CLI_TIMEOUT_MS : CLI_FALLBACK_TIMEOUT_MS;
536
+ const result = await callWithRetry(currentProvider, cleanPrompt, systemPrompt, jsonMode, timeoutMs);
461
537
 
462
538
  if (result.success) {
463
- console.log(`${label} response: ${result.result}`);
539
+ const output = `${label} response: ${result.result}`;
540
+ setCachedResponse(cacheKey, output);
541
+ console.log(output);
464
542
  return;
465
543
  }
466
544
 
467
545
  // Log failure and try fallback
468
- if (currentProvider !== providerChain[providerChain.length - 1]) {
469
- const nextProvider = providerChain[providerChain.indexOf(currentProvider) + 1];
546
+ if (i < providerChain.length - 1) {
547
+ const nextProvider = providerChain[i + 1];
470
548
  const nextLabel = providerLabels[nextProvider] || nextProvider.toUpperCase();
471
549
  console.error(`[${currentProvider.toUpperCase()}] Failed: ${result.error}. Falling back to ${nextLabel}...`);
472
550
  } else {
@@ -0,0 +1,52 @@
1
+ /**
2
+ * PreToolUse Hook - PR 생성 전 테스트 게이트
3
+ *
4
+ * mcp__github__create_pull_request 호출 시 테스트가 통과해야만 PR 생성 허용.
5
+ * exit 2 = 차단, exit 0 = 통과
6
+ */
7
+ import { execSync } from 'child_process';
8
+ import { PROJECT_DIR } from './utils.js';
9
+ import { existsSync, readFileSync } from 'fs';
10
+ import path from 'path';
11
+
12
+ function detectTestCommand() {
13
+ const pkgPath = path.join(PROJECT_DIR, 'package.json');
14
+ if (existsSync(pkgPath)) {
15
+ try {
16
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
17
+ if (pkg.scripts?.test && pkg.scripts.test !== 'echo "Error: no test specified" && exit 1') {
18
+ return 'npm test';
19
+ }
20
+ } catch { /* ignore */ }
21
+ }
22
+ // Python
23
+ if (existsSync(path.join(PROJECT_DIR, 'pytest.ini')) || existsSync(path.join(PROJECT_DIR, 'pyproject.toml'))) {
24
+ return 'python -m pytest --tb=short -q';
25
+ }
26
+ // Go
27
+ if (existsSync(path.join(PROJECT_DIR, 'go.mod'))) {
28
+ return 'go test ./...';
29
+ }
30
+ return null;
31
+ }
32
+
33
+ try {
34
+ const testCmd = detectTestCommand();
35
+ if (!testCmd) {
36
+ // No test command detected — allow PR
37
+ process.exit(0);
38
+ }
39
+
40
+ console.log(`[PR-GATE] Running tests before PR creation: ${testCmd}`);
41
+ execSync(testCmd, {
42
+ cwd: PROJECT_DIR,
43
+ stdio: ['ignore', 'pipe', 'pipe'],
44
+ timeout: 120000,
45
+ });
46
+ console.log('[PR-GATE] Tests passed — PR creation allowed');
47
+ process.exit(0);
48
+ } catch (err) {
49
+ const output = err.stdout ? err.stdout.toString().split('\n').slice(-5).join('\n') : '';
50
+ console.log(`[PR-GATE] Tests failed — PR creation blocked\n${output}`);
51
+ process.exit(2);
52
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@su-record/vibe",
3
- "version": "2.8.23",
3
+ "version": "2.8.25",
4
4
  "description": "AI Coding Framework for Claude Code — 49 agents, 41+ tools, multi-LLM orchestration",
5
5
  "type": "module",
6
6
  "main": "dist/cli/index.js",
@@ -0,0 +1,49 @@
1
+ # What to Keep vs Remove in AGENTS.md
2
+
3
+ ## The One-Line Test
4
+
5
+ > "Can the agent discover this by reading the code?" → Yes = delete.
6
+
7
+ ## Keep (Non-discoverable Gotchas)
8
+
9
+ | Type | Example | Why Keep |
10
+ |------|---------|----------|
11
+ | Runtime trap | "Bun runtime, not Node" | Not visible in package.json |
12
+ | Forbidden pattern | "Never use `require()` — ESM only" | Agent will default to CJS without this |
13
+ | SSOT location | "Edit only `constants.ts` for stack mapping" | Agent will create duplicates |
14
+ | Ordering invariant | "Build before test — always" | Violating silently breaks things |
15
+ | Non-standard convention | "Exports need `.js` extension" | Counterintuitive in TS projects |
16
+ | Tool choice | "Zod only — no joi or yup" | Agent picks any validation lib |
17
+ | Boundary | "Never edit `dist/` directly" | Agent may "fix" generated files |
18
+ | Response directive | "Respond in Korean" | Claude-specific, not in code |
19
+
20
+ ## Remove (Discoverable)
21
+
22
+ | Type | Why Remove | Where to Find Instead |
23
+ |------|------------|----------------------|
24
+ | Directory structure | `ls` or Glob reveals it | The repo itself |
25
+ | Tech stack list | Listed in `package.json` | `package.json` |
26
+ | Build/test commands | Listed in `scripts` | `package.json` |
27
+ | Phase progress tables | Historical record, not actionable | Git history |
28
+ | API endpoint list | Readable from router code | Source files |
29
+ | Architecture diagrams | Visual aid, no mistake prevention | Readme or separate doc |
30
+ | Feature descriptions | Code speaks for itself | Source files |
31
+ | General best practices | LLM already knows them | Unnecessary |
32
+
33
+ ## Anchoring Warning
34
+
35
+ Mentioning any technology name biases the agent toward it:
36
+ - "We use React" → unnecessary (visible in package.json) and creates anchoring
37
+ - "Never use jQuery, even for legacy modules" → useful (prevents a specific mistake)
38
+
39
+ Rule: Only name a technology when saying **don't use it** or when it's a **hidden runtime detail**.
40
+
41
+ ## Size Target
42
+
43
+ | Outcome | Lines |
44
+ |---------|-------|
45
+ | Ideal | Under 50 |
46
+ | Acceptable | 50-80 |
47
+ | Warning | 80+ |
48
+
49
+ If over 80 lines, almost certainly contains discoverable content. Audit again.
@@ -0,0 +1,36 @@
1
+ # {{PROJECT_NAME}} — {{ONE_LINER}}
2
+
3
+ {{OPTIONAL: What the project does in 1-2 sentences. Only include if purpose is unclear from code.}}
4
+
5
+ ## Gotchas
6
+
7
+ - **{{TRAP_TITLE}}.** {{Specific do/don't — e.g., "Use Bun, not Node. `bun run dev`, not `node`."}}
8
+ - **{{TRAP_TITLE}}.** {{Specific do/don't}}
9
+ - **{{TRAP_TITLE}}.** {{Specific do/don't}}
10
+
11
+ ## Naming
12
+
13
+ {{Only if non-standard naming patterns exist. Delete this section if standard conventions apply.}}
14
+
15
+ - {{Pattern}}: {{Example}}
16
+
17
+ ---
18
+
19
+ <!--
20
+ INSTRUCTIONS FOR FILLING THIS TEMPLATE
21
+
22
+ 1. Replace {{PROJECT_NAME}} and {{ONE_LINER}} with your project.
23
+ 2. For each Gotcha, ask: "Would an AI agent make this mistake without being told?"
24
+ - YES → keep it as a gotcha
25
+ - NO → delete it (discoverable from code)
26
+ 3. Delete the Naming section if you use standard conventions (camelCase, PascalCase, etc.).
27
+ 4. Target: under 50 lines total.
28
+ 5. Delete this comment block before committing.
29
+
30
+ WHAT NOT TO ADD:
31
+ - Directory structure (use ls/Glob)
32
+ - Tech stack list (see package.json)
33
+ - Build/test commands (see package.json scripts)
34
+ - Phase progress or history
35
+ - General best practices the LLM already knows
36
+ -->
@@ -0,0 +1,48 @@
1
+ ---
2
+ name: arch-detector
3
+ role: Detects the project's architecture pattern by analyzing directory structure and import graph
4
+ tools: [Glob, Grep, Read]
5
+ ---
6
+
7
+ # Arch Detector
8
+
9
+ ## Role
10
+ Analyzes directory layout, existing documentation, and import relationships to classify the project into a known architecture pattern. Produces a structured architecture map that downstream agents use to generate rules and check violations.
11
+
12
+ ## Responsibilities
13
+ - Scan top-level directory structure for known layer naming conventions
14
+ - Read CLAUDE.md, README, and any ADR files for explicit architecture documentation
15
+ - Sample import statements across files to infer actual dependency direction
16
+ - Classify project into one of: MVC, Clean Architecture, Hexagonal, Feature-based, Component hierarchy, or Unknown
17
+ - Produce a layer map with canonical names and glob patterns for each layer
18
+
19
+ ## Input
20
+ - Project root path
21
+ - Optional: explicit architecture hint from user (e.g., "this is Clean Architecture")
22
+
23
+ ## Output
24
+ Architecture map JSON:
25
+ ```json
26
+ {
27
+ "pattern": "Clean Architecture",
28
+ "confidence": "high",
29
+ "layers": [
30
+ { "name": "domain", "glob": "src/domain/**", "allowedDeps": [] },
31
+ { "name": "application", "glob": "src/application/**", "allowedDeps": ["domain"] },
32
+ { "name": "infrastructure", "glob": "src/infra/**", "allowedDeps": ["domain", "application"] },
33
+ { "name": "ui", "glob": "src/components/**", "allowedDeps": ["application"] }
34
+ ]
35
+ }
36
+ ```
37
+
38
+ ## Communication
39
+ - Reports architecture map to: `arch-rule-generator`
40
+ - Receives instructions from: arch-guard orchestrator (SKILL.md)
41
+
42
+ ## Domain Knowledge
43
+ Architecture pattern signals:
44
+ - **MVC**: directories named `controllers/`, `models/`, `views/` or `services/`
45
+ - **Clean Architecture**: `domain/`, `application/` or `use-cases/`, `infrastructure/` or `infra/`
46
+ - **Hexagonal**: `adapters/`, `ports/`, `core/` or `domain/`
47
+ - **Feature-based**: top-level feature folders each containing `components/`, `hooks/`, `api/`
48
+ - **Component hierarchy**: `pages/`, `features/`, `shared/`, `ui/` in frontend projects
@@ -0,0 +1,48 @@
1
+ ---
2
+ name: arch-reporter
3
+ role: Formats the violation scan results into an actionable report with fix suggestions
4
+ tools: [Read]
5
+ ---
6
+
7
+ # Arch Reporter
8
+
9
+ ## Role
10
+ Transforms the raw violation list into a human-readable report grouped by rule and severity. For each violation, it provides a specific fix suggestion explaining which layer the import should move to or be replaced by. Produces both a console summary and a machine-readable output for CI.
11
+
12
+ ## Responsibilities
13
+ - Group violations by rule name and severity
14
+ - Generate a specific fix suggestion for each violation type
15
+ - Calculate a health score (clean files / total scanned)
16
+ - Produce a markdown summary for the user
17
+ - Produce a JSON summary for CI badge / artifact storage
18
+
19
+ ## Input
20
+ Violation list JSON from `arch-violation-checker` plus the rule set from `arch-rule-generator`.
21
+
22
+ ## Output
23
+ Markdown report:
24
+ ```markdown
25
+ ## Architecture Boundary Report
26
+
27
+ Health: 139/142 files clean (97.9%)
28
+
29
+ ### Violations (3 errors)
30
+
31
+ #### Rule: domain-no-infra — Domain must not import Infrastructure
32
+ - src/domain/user.ts:3 imports `../infra/db/userRepository`
33
+ Fix: Extract a port interface in `src/domain/ports/userRepository.ts` and inject via DI
34
+
35
+ Total: 3 violations across 1 rule. Run `npx vitest run tests/arch-guard.test.ts` to enforce in CI.
36
+ ```
37
+
38
+ ## Communication
39
+ - Reports formatted output to: orchestrator / user
40
+ - Receives instructions from: arch-guard orchestrator (SKILL.md)
41
+
42
+ ## Domain Knowledge
43
+ Fix suggestion patterns:
44
+ - **Domain importing Infra**: introduce a port/interface in domain; implement in infra; inject via constructor
45
+ - **Feature importing Feature internals**: move shared code to `shared/` layer
46
+ - **Service importing Controller**: the logic belongs in a service method called by the controller
47
+ - **UI importing Domain directly**: route through an application-layer use case or store
48
+ - Suggest the minimal move — avoid recommending full refactors for single violations
@@ -0,0 +1,49 @@
1
+ ---
2
+ name: arch-rule-generator
3
+ role: Generates concrete import boundary rules from the detected architecture map
4
+ tools: [Read]
5
+ ---
6
+
7
+ # Arch Rule Generator
8
+
9
+ ## Role
10
+ Translates a detected architecture map into a precise, machine-checkable set of import boundary rules. Merges default rules for the detected pattern with any custom rules defined in `.claude/vibe/arch-rules.json`. Outputs a normalized rule set ready for the violation checker.
11
+
12
+ ## Responsibilities
13
+ - Select default rule templates for the detected architecture pattern
14
+ - Merge with custom rules from `.claude/vibe/arch-rules.json` if present
15
+ - Resolve glob patterns to concrete layer names
16
+ - Deduplicate and normalize rule list
17
+ - Flag rules with low confidence (detected layer with no matching files)
18
+
19
+ ## Input
20
+ Architecture map JSON from `arch-detector`, plus optional `.claude/vibe/arch-rules.json` for user-defined overrides.
21
+
22
+ ## Output
23
+ Normalized rule set JSON:
24
+ ```json
25
+ {
26
+ "rules": [
27
+ {
28
+ "name": "domain-no-infra",
29
+ "from": "src/domain/**",
30
+ "cannotImport": ["src/infra/**"],
31
+ "reason": "Domain layer must not depend on infrastructure",
32
+ "severity": "error"
33
+ }
34
+ ],
35
+ "warnings": ["Layer 'adapters' detected but no files found — rule may be inaccurate"]
36
+ }
37
+ ```
38
+
39
+ ## Communication
40
+ - Reports rule set to: `arch-violation-checker`
41
+ - Receives instructions from: arch-guard orchestrator (SKILL.md)
42
+
43
+ ## Domain Knowledge
44
+ Default rules by pattern:
45
+ - **Clean Architecture**: domain has no deps; application imports domain only; infra imports domain + application; ui imports application only
46
+ - **MVC**: models have no deps on controllers or views; services import models only
47
+ - **Hexagonal**: domain/core imports nothing internal; adapters import ports only
48
+ - **Feature-based**: features must not import each other's internals; only `shared/` is cross-feature
49
+ - **SOLID Dependency Inversion**: high-level modules must not import low-level modules directly