@vibecodetown/mcp-server 2.1.0

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 (172) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +269 -0
  3. package/build/auth/gate.js +225 -0
  4. package/build/auth/index.js +55 -0
  5. package/build/auth/public_key.js +27 -0
  6. package/build/auth/token_cache.js +122 -0
  7. package/build/auth/token_verifier.js +103 -0
  8. package/build/bootstrap/doctor.js +115 -0
  9. package/build/bootstrap/installer.js +673 -0
  10. package/build/bootstrap/lock.js +37 -0
  11. package/build/bootstrap/platform.js +26 -0
  12. package/build/bootstrap/registry.js +37 -0
  13. package/build/cache/index.js +147 -0
  14. package/build/cli.js +101 -0
  15. package/build/contracts.js +22 -0
  16. package/build/control_plane/gate.js +161 -0
  17. package/build/control_plane/index.js +6 -0
  18. package/build/dx/activity.js +139 -0
  19. package/build/engine.js +106 -0
  20. package/build/errors.js +171 -0
  21. package/build/generated/activate_input.js +2 -0
  22. package/build/generated/activate_output.js +57 -0
  23. package/build/generated/advisory_review_input.js +2 -0
  24. package/build/generated/advisory_review_output.js +35 -0
  25. package/build/generated/auth_token_file.js +2 -0
  26. package/build/generated/briefing_input.js +2 -0
  27. package/build/generated/briefing_output.js +2 -0
  28. package/build/generated/clinic_bridge_file.js +13 -0
  29. package/build/generated/contracts_bundle_info.js +5 -0
  30. package/build/generated/create_work_order_input.js +2 -0
  31. package/build/generated/create_work_order_output.js +2 -0
  32. package/build/generated/current_work_order_file.js +2 -0
  33. package/build/generated/doctor_input.js +2 -0
  34. package/build/generated/doctor_output.js +24 -0
  35. package/build/generated/execution_result.js +2 -0
  36. package/build/generated/execution_task.js +2 -0
  37. package/build/generated/export_output_input.js +2 -0
  38. package/build/generated/export_output_output.js +2 -0
  39. package/build/generated/finalize_work_input.js +2 -0
  40. package/build/generated/finalize_work_output.js +2 -0
  41. package/build/generated/gate_input.js +2 -0
  42. package/build/generated/gate_output.js +2 -0
  43. package/build/generated/gate_result_v1.js +2 -0
  44. package/build/generated/get_decision_input.js +2 -0
  45. package/build/generated/get_decision_output.js +13 -0
  46. package/build/generated/handoff_to_clinic.js +2 -0
  47. package/build/generated/index.js +75 -0
  48. package/build/generated/inspect_code_input.js +2 -0
  49. package/build/generated/inspect_code_output.js +13 -0
  50. package/build/generated/memory_retrieve_output.js +2 -0
  51. package/build/generated/memory_state_file.js +2 -0
  52. package/build/generated/memory_status_input.js +2 -0
  53. package/build/generated/memory_status_output.js +13 -0
  54. package/build/generated/memory_sync_input.js +2 -0
  55. package/build/generated/memory_sync_output.js +13 -0
  56. package/build/generated/plugin_result.js +2 -0
  57. package/build/generated/react_perf_check_patterns_input.js +2 -0
  58. package/build/generated/react_perf_check_patterns_output.js +2 -0
  59. package/build/generated/react_perf_generate_report_input.js +2 -0
  60. package/build/generated/react_perf_generate_report_output.js +2 -0
  61. package/build/generated/repair_plan_input.js +2 -0
  62. package/build/generated/repair_plan_output.js +2 -0
  63. package/build/generated/run_app_input.js +2 -0
  64. package/build/generated/run_app_output.js +2 -0
  65. package/build/generated/run_state_file.js +13 -0
  66. package/build/generated/scaffold_input.js +2 -0
  67. package/build/generated/scaffold_output.js +2 -0
  68. package/build/generated/search_oss_input.js +2 -0
  69. package/build/generated/search_oss_output.js +2 -0
  70. package/build/generated/selection_validation_result.js +2 -0
  71. package/build/generated/signal_agent_input.js +2 -0
  72. package/build/generated/spec_high_ask_queue_items_file.js +2 -0
  73. package/build/generated/spec_high_clinic_bridge_output.js +2 -0
  74. package/build/generated/spec_high_decision_draft_output.js +2 -0
  75. package/build/generated/spec_high_validate_output.js +2 -0
  76. package/build/generated/status_input.js +2 -0
  77. package/build/generated/status_output.js +2 -0
  78. package/build/generated/submit_decision_input.js +2 -0
  79. package/build/generated/submit_decision_output.js +2 -0
  80. package/build/generated/tool_error_output.js +2 -0
  81. package/build/generated/undo_last_task_input.js +2 -0
  82. package/build/generated/undo_last_task_output.js +2 -0
  83. package/build/generated/update_input.js +2 -0
  84. package/build/generated/update_output.js +2 -0
  85. package/build/generated/vibe_pm_inspection_result.js +2 -0
  86. package/build/generated/vibe_pm_report_markdown.js +2 -0
  87. package/build/generated/vibe_pm_verdict.js +2 -0
  88. package/build/generated/vibe_repo_config.js +2 -0
  89. package/build/generated/vibecoding_helper_answer_output.js +2 -0
  90. package/build/generated/vibecoding_helper_one_loop_selection_output.js +2 -0
  91. package/build/generated/vibecoding_helper_show_ask_queue_output.js +2 -0
  92. package/build/generated/work_order_v1.js +2 -0
  93. package/build/generated/zoekt_evidence_input.js +2 -0
  94. package/build/generated/zoekt_evidence_output.js +2 -0
  95. package/build/index.js +111 -0
  96. package/build/legacy_alias.js +65 -0
  97. package/build/local-mode/bash.js +61 -0
  98. package/build/local-mode/config.js +171 -0
  99. package/build/local-mode/git.js +33 -0
  100. package/build/local-mode/init.js +110 -0
  101. package/build/local-mode/paths.js +24 -0
  102. package/build/local-mode/templates.js +856 -0
  103. package/build/local-mode/work-order.js +41 -0
  104. package/build/resources/index.js +246 -0
  105. package/build/security/input-validator.js +119 -0
  106. package/build/security/path-policy.js +289 -0
  107. package/build/security/sandbox.js +228 -0
  108. package/build/tools/react_perf/check_patterns.js +172 -0
  109. package/build/tools/react_perf/generate_report.js +337 -0
  110. package/build/tools/react_perf/index.js +119 -0
  111. package/build/tools/react_perf/rules/advanced.js +325 -0
  112. package/build/tools/react_perf/rules/async.js +104 -0
  113. package/build/tools/react_perf/rules/bundle.js +101 -0
  114. package/build/tools/react_perf/rules/client.js +186 -0
  115. package/build/tools/react_perf/rules/index.js +74 -0
  116. package/build/tools/react_perf/rules/js.js +148 -0
  117. package/build/tools/react_perf/rules/rendering.js +166 -0
  118. package/build/tools/react_perf/rules/rerender.js +161 -0
  119. package/build/tools/react_perf/rules/server.js +141 -0
  120. package/build/tools/react_perf/types.js +127 -0
  121. package/build/tools/vibe_pm/activate.js +102 -0
  122. package/build/tools/vibe_pm/advisory_review.js +77 -0
  123. package/build/tools/vibe_pm/briefing.js +178 -0
  124. package/build/tools/vibe_pm/context.js +439 -0
  125. package/build/tools/vibe_pm/create_work_order.js +271 -0
  126. package/build/tools/vibe_pm/doc_status_gate.js +370 -0
  127. package/build/tools/vibe_pm/doctor.js +262 -0
  128. package/build/tools/vibe_pm/entity_gate/preflight.js +78 -0
  129. package/build/tools/vibe_pm/export_output.js +135 -0
  130. package/build/tools/vibe_pm/finalize_work.js +393 -0
  131. package/build/tools/vibe_pm/gate.js +33 -0
  132. package/build/tools/vibe_pm/get_decision.js +281 -0
  133. package/build/tools/vibe_pm/index.js +593 -0
  134. package/build/tools/vibe_pm/inspect_code.js +828 -0
  135. package/build/tools/vibe_pm/intent/generator.js +294 -0
  136. package/build/tools/vibe_pm/intent/index.js +5 -0
  137. package/build/tools/vibe_pm/intent/prompt_density.js +227 -0
  138. package/build/tools/vibe_pm/intent/types.js +70 -0
  139. package/build/tools/vibe_pm/intent/verifier.js +237 -0
  140. package/build/tools/vibe_pm/kce/doc_usage.js +51 -0
  141. package/build/tools/vibe_pm/kce/on_finalize.js +11 -0
  142. package/build/tools/vibe_pm/kce/preflight.js +232 -0
  143. package/build/tools/vibe_pm/local_memory.js +26 -0
  144. package/build/tools/vibe_pm/memory_status.js +82 -0
  145. package/build/tools/vibe_pm/memory_sync.js +134 -0
  146. package/build/tools/vibe_pm/modules/decision_snapshot.js +29 -0
  147. package/build/tools/vibe_pm/modules/ensure.js +100 -0
  148. package/build/tools/vibe_pm/modules/fingerprint.js +30 -0
  149. package/build/tools/vibe_pm/modules/fix_dependencies.js +394 -0
  150. package/build/tools/vibe_pm/modules/planning_v1.js +110 -0
  151. package/build/tools/vibe_pm/modules/repo_context.js +56 -0
  152. package/build/tools/vibe_pm/modules/research_v1.js +114 -0
  153. package/build/tools/vibe_pm/modules/skills_v1.js +100 -0
  154. package/build/tools/vibe_pm/pm_language.js +222 -0
  155. package/build/tools/vibe_pm/repair_plan.js +199 -0
  156. package/build/tools/vibe_pm/run_app.js +597 -0
  157. package/build/tools/vibe_pm/run_app_podman.js +64 -0
  158. package/build/tools/vibe_pm/scaffold.js +550 -0
  159. package/build/tools/vibe_pm/search_oss.js +124 -0
  160. package/build/tools/vibe_pm/status.js +153 -0
  161. package/build/tools/vibe_pm/submit_decision.js +87 -0
  162. package/build/tools/vibe_pm/system_design/issue_mapping.js +47 -0
  163. package/build/tools/vibe_pm/system_design/rulebook.js +112 -0
  164. package/build/tools/vibe_pm/system_design/semgrep.js +132 -0
  165. package/build/tools/vibe_pm/types.js +229 -0
  166. package/build/tools/vibe_pm/undo_last_task.js +163 -0
  167. package/build/tools/vibe_pm/update.js +146 -0
  168. package/build/tools/vibe_pm/zoekt_evidence.js +96 -0
  169. package/build/tools.js +269 -0
  170. package/build/version-check.js +239 -0
  171. package/build/vibe-cli.js +631 -0
  172. package/package.json +76 -0
@@ -0,0 +1,550 @@
1
+ // adapters/mcp-ts/src/tools/vibe_pm/scaffold.ts
2
+ // vibe_pm.scaffold - Vibe Standard Directory Structure (Vibe-SDS) generator
3
+ // Creates standardized project structure for AI-friendly development
4
+ import fs from "node:fs/promises";
5
+ import path from "node:path";
6
+ const ZONE_COMMENTS = {
7
+ BLACK: "[BLACK ZONE] AI 접근 금지 - 시스템 전용",
8
+ RED: "[RED ZONE] AI 수정 금지 - 설정 파일",
9
+ GREEN: "[GREEN ZONE] AI 작업 구역",
10
+ YELLOW: "[YELLOW ZONE] AI 수정 가능 - 주의 필요",
11
+ };
12
+ // ============================================================
13
+ // AI Context & Rules Templates
14
+ // ============================================================
15
+ const CURSORRULES_CONTENT = `# Vibe PM Enforced Rules (DO NOT DELETE)
16
+ # This file ensures AI agents follow Vibe PM protocol in this project.
17
+
18
+ ## IDENTITY
19
+ You are a Vibe PM Worker. You are NOT an autonomous coding agent.
20
+ You operate under the strict supervision of Vibe PM.
21
+
22
+ ## PRIME DIRECTIVES
23
+ 1. **NO TICKET, NO WORK**: You cannot modify code without an active "Work Order".
24
+ - If user asks for code changes, run \`vibe_pm.create_work_order\` first.
25
+ - Exception: Minor typo fixes or formatting (still report via \`vibe_pm.status\`).
26
+
27
+ 2. **CONTEXT AWARENESS**: At the start of every session, run \`vibe_pm.status\` or \`vibe_pm.briefing\` to understand the current phase.
28
+
29
+ 3. **SAFETY ZONES (Vibe-SDS)**:
30
+ - ✅ WRITE: \`src/\`, \`tests/\`
31
+ - ⚠️ READ-ONLY: \`docs/\`, \`AI_ENTRYPOINT.md\`
32
+ - ⛔ FORBIDDEN: \`config/\`, \`.vibe/\`, \`.env\`
33
+
34
+ ## TOOL USAGE
35
+ - Use \`vibe_pm.get_decision\` when ambiguity exists (A/B/C choices).
36
+ - Use \`vibe_pm.inspect_code\` after EVERY implementation step.
37
+ - Use \`vibe_pm.repair_plan\` when inspection fails.
38
+
39
+ ## COMMUNICATION
40
+ - Explain decisions in simple Korean (한국어) for non-technical founders.
41
+ - Always confirm before making significant changes.
42
+ - Reference \`AI_ENTRYPOINT.md\` for project-specific context.
43
+
44
+ ## CLEAN DESK POLICY (DOCUMENTATION)
45
+ 1. **START**: Read requirements from \`docs/planning/\`.
46
+ 2. **UPDATE**: As you implement, verify against the checklist in the plan file.
47
+ 3. **FINISH**: When running \`vibe_pm.finalize_work\`, ALWAYS provide \`completed_plan_path\`.
48
+ - The system will automatically move it to \`docs/archive/\`.
49
+ - Do NOT delete the file yourself. Let the tool handle it.
50
+
51
+ ## FINALIZATION PROTOCOL (CRITICAL)
52
+ 1. **Definition of Done**:
53
+ - Coding is NOT the last step.
54
+ - \`inspect_code\` passing ("GO") is NOT the last step.
55
+ - You MUST run \`vibe_pm.finalize_work\` to finish a task.
56
+
57
+ 2. **No Fragmentation**:
58
+ - NEVER create scattered markdown files like \`feature_login.md\`.
59
+ - All updates go to \`docs/specs/CURRENT_SPEC.md\` via \`finalize_work\`.
60
+
61
+ 3. **Logging Standard**:
62
+ - Write \`task_summary\` for a non-technical founder.
63
+ - ✅ "사용자가 이메일로 로그인할 수 있게 되었습니다."
64
+ - ❌ "Implemented bcrypt hash in auth.py."
65
+
66
+ 4. **Git Standard**:
67
+ - Use Conventional Commits (e.g., \`feat: user login\`).
68
+ - NEVER commit directly using shell commands.
69
+ - ALWAYS use \`vibe_pm.finalize_work\`.
70
+
71
+ ## SELF-HEALING PROTOCOL
72
+ 1. **BE PROACTIVE**: Do NOT ask for permission for trivial fixes.
73
+ - Missing package? → Just install it.
74
+ - Syntax error? → Just fix it.
75
+ - Format issue? → Just format it.
76
+
77
+ 2. **REPORT AFTERWARDS**: Instead of "Shall I fix it?", say:
78
+ "I detected a missing package and fixed it automatically. The code is now verified."
79
+
80
+ 3. **ONLY ASK WHEN RISKY**: Ask ONLY when:
81
+ - Changing database schema (data loss risk)
82
+ - Modifying business logic
83
+ - Touching security configurations
84
+ - Deleting existing data
85
+
86
+ 4. **EXAMPLE RESPONSES**:
87
+ - ✅ "검증 과정에서 'pandas' 패키지가 누락된 것을 발견하여 자동으로 설치했습니다."
88
+ - ❌ "'pandas' 패키지가 없습니다. 설치할까요?"
89
+ `;
90
+ const CURRENT_SPEC_CONTENT = `# Project Specification
91
+
92
+ > This is the **Living Spec** - the single source of truth for this project.
93
+ > Last updated: ${new Date().toISOString().split('T')[0]}
94
+
95
+ ## 1. Overview
96
+
97
+ <!-- vibe_pm.briefing 후 자동 채워짐 -->
98
+ **Project**: Not initialized
99
+ **Goal**: Run \`vibe_pm.briefing\` to set up.
100
+
101
+ ## 2. Architecture
102
+
103
+ <!-- 시스템 구조 설명 -->
104
+ TBD
105
+
106
+ ## 3. Database Schema
107
+
108
+ <!-- 데이터베이스 스키마 -->
109
+ TBD
110
+
111
+ ## 4. API Endpoints
112
+
113
+ <!-- API 엔드포인트 목록 -->
114
+ TBD
115
+
116
+ ## 5. Security
117
+
118
+ <!-- 보안 관련 설정 -->
119
+ TBD
120
+
121
+ ---
122
+
123
+ *This file is managed by \`vibe_pm.finalize_work\`. Do not edit manually.*
124
+ `;
125
+ const VIBE_CI_WORKFLOW_CONTENT = `name: Vibe PM CI
126
+
127
+ on:
128
+ push:
129
+ pull_request:
130
+
131
+ jobs:
132
+ baseline-check:
133
+ runs-on: ubuntu-latest
134
+ steps:
135
+ - uses: actions/checkout@v4
136
+
137
+ - name: Set up Node
138
+ uses: actions/setup-node@v4
139
+ with:
140
+ node-version: "20"
141
+
142
+ - name: Set up Python
143
+ uses: actions/setup-python@v5
144
+ with:
145
+ python-version: "3.11"
146
+
147
+ - name: Install & Test (Node)
148
+ if: \${{ hashFiles('package.json') != '' }}
149
+ run: |
150
+ if [ -f package-lock.json ]; then npm ci; else npm install; fi
151
+ node -e "const p=require('./package.json');process.exit(p.scripts&&p.scripts.test?0:1)" \\
152
+ && npm test \\
153
+ || echo "::notice::No npm test script found. CI ran baseline checks only."
154
+
155
+ - name: Install & Test (Python)
156
+ if: \${{ hashFiles('pyproject.toml') != '' || hashFiles('requirements.txt') != '' }}
157
+ run: |
158
+ python -m pip install --upgrade pip
159
+ if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
160
+ if [ -f pyproject.toml ]; then pip install -e .; fi
161
+ if [ -d tests ]; then
162
+ pip install pytest
163
+ pytest -q
164
+ else
165
+ echo "::notice::No tests/ directory found. CI ran baseline checks only."
166
+ fi
167
+ `;
168
+ const AI_ENTRYPOINT_CONTENT = `# AI Entrypoint: Vibe PM Project
169
+
170
+ > This project is managed by **Vibe PM**. Any AI agent accessing this codebase MUST adhere to the following protocols.
171
+
172
+ ## 1. Project Overview
173
+
174
+ <!-- This section will be auto-updated by vibe_pm.briefing -->
175
+ **Status**: Not briefed yet. Run \`vibe_pm.briefing\` to initialize.
176
+
177
+ ## 2. Directory Structure (Vibe-SDS)
178
+
179
+ | Path | Permission | Purpose |
180
+ |:-----|:-----------|:--------|
181
+ | \`src/\` | **R/W** | Main implementation code. Your workspace. |
182
+ | \`tests/\` | **R/W** | Test files. Always add tests for new features. |
183
+ | \`docs/\` | **READ** | Documentation. Read for context, don't modify without approval. |
184
+ | \`config/\` | **FORBIDDEN** | Configuration files. Protected by Vibe PM. |
185
+ | \`.vibe/\` | **FORBIDDEN** | Internal system state. Never touch. |
186
+
187
+ ## 3. Workflow Protocol
188
+
189
+ \`\`\`
190
+ 1. Briefing → vibe_pm.briefing → Understand the goal
191
+ 2. Decision → vibe_pm.get_decision → Clarify trade-offs (A/B/C)
192
+ 3. Work Order → vibe_pm.create_work_order → Receive specific tasks
193
+ 4. Implementation → Write code in src/ → Follow the work order
194
+ 5. Inspection → vibe_pm.inspect_code → Validate your work
195
+ \`\`\`
196
+
197
+ ## 4. Quick Start
198
+
199
+ \`\`\`bash
200
+ # Check current project status
201
+ vibe_pm.status
202
+
203
+ # Start a new project
204
+ vibe_pm.briefing { "project_brief": "Your idea here" }
205
+
206
+ # Get pending decisions
207
+ vibe_pm.get_decision
208
+ \`\`\`
209
+
210
+ ## 5. Rules Summary
211
+
212
+ 1. **No Ticket, No Work**: Always get a Work Order before coding.
213
+ 2. **Zone Safety**: Only modify files in \`src/\` and \`tests/\`.
214
+ 3. **Inspect After Implementation**: Run \`vibe_pm.inspect_code\` after every change.
215
+ 4. **Finalize After Approval**: Run \`vibe_pm.finalize_work\` after "GO" to update docs and commit.
216
+ 5. **Ask When Uncertain**: Use \`vibe_pm.get_decision\` for ambiguous choices.
217
+
218
+ ## 6. Documentation Architecture (Living Spec)
219
+
220
+ We follow the "Living Spec" approach - no fragmentation allowed.
221
+
222
+ | Path | Purpose |
223
+ |------|---------|
224
+ | \`docs/specs/CURRENT_SPEC.md\` | The absolute truth of the system |
225
+ | \`docs/specs/CHANGELOG.md\` | High-level version history |
226
+ | \`docs/dev_logs/\` | Daily work logs (auto-generated) |
227
+
228
+ ### Rules
229
+ - Do NOT create scattered markdown files like \`feature_login.md\`.
230
+ - Do NOT edit spec files manually. Use \`vibe_pm.finalize_work\`.
231
+ - If you touch the code, you must leave a trace in both DEV_LOG and Git.
232
+ - Every task must end with \`finalize_work\`, not just \`inspect_code\`.
233
+
234
+ ## 7. Self-Healing System
235
+
236
+ Vibe PM automatically fixes trivial issues without asking:
237
+
238
+ | Issue | Auto-Fix | Example |
239
+ |-------|----------|---------|
240
+ | Missing package | ✅ | Adds to requirements.txt |
241
+ | Syntax error | ✅ | Fixes indentation |
242
+ | Format issue | ✅ | Runs formatter |
243
+ | Logic change | ❌ | Asks user |
244
+ | Data deletion | ❌ | Asks user |
245
+
246
+ ### How It Works
247
+ 1. \`inspect_code\` runs with \`auto_fix=true\` (default)
248
+ 2. Safe issues are fixed immediately
249
+ 3. You see: "Auto-fixed 2 issues. All tests passed."
250
+ 4. Only risky changes require your approval.
251
+
252
+ ---
253
+
254
+ *This file is auto-generated by \`vibe_pm.scaffold\`. Do not delete.*
255
+ `;
256
+ const TEMPLATES = {
257
+ minimal: [
258
+ { path: "src/.gitkeep", content: "", description: "소스 코드 디렉토리" },
259
+ { path: "README.md", content: `# Project\n\nCreated with Vibe PM.\n` },
260
+ // AI Context & Rules (always included)
261
+ { path: ".cursorrules", content: CURSORRULES_CONTENT, description: "Cursor AI 행동 규칙" },
262
+ { path: "AI_ENTRYPOINT.md", content: AI_ENTRYPOINT_CONTENT, description: "AI 에이전트 진입점" },
263
+ ],
264
+ standard: [
265
+ // BLACK ZONE - System only
266
+ { path: ".vibe/.gitkeep", content: "", description: ZONE_COMMENTS.BLACK },
267
+ { path: ".vibe/state/.gitkeep", content: "" },
268
+ { path: ".vibe/decisions/.gitkeep", content: "" },
269
+ // RED ZONE - Protected config
270
+ {
271
+ path: "config/settings.yaml",
272
+ content: `# ${ZONE_COMMENTS.RED}\n# Project Settings\nenv: development\n`,
273
+ description: ZONE_COMMENTS.RED,
274
+ },
275
+ // GREEN ZONE - AI workspace
276
+ { path: "src/.gitkeep", content: "", description: ZONE_COMMENTS.GREEN },
277
+ { path: "src/components/.gitkeep", content: "" },
278
+ { path: "src/lib/.gitkeep", content: "" },
279
+ { path: "tests/.gitkeep", content: "", description: ZONE_COMMENTS.GREEN },
280
+ // YELLOW ZONE - Documentation (Living Spec)
281
+ { path: "docs/specs/CURRENT_SPEC.md", content: CURRENT_SPEC_CONTENT, description: "살아있는 스펙 (SSOT)" },
282
+ { path: "docs/specs/CHANGELOG.md", content: "# Changelog\n\nAll notable changes to this project.\n\n---\n", description: "버전 히스토리" },
283
+ { path: "docs/dev_logs/.gitkeep", content: "", description: "일일 개발 일지" },
284
+ { path: "docs/planning/.gitkeep", content: "", description: "작업대 (기획 및 체크리스트)" },
285
+ { path: "docs/archive/.gitkeep", content: "", description: "창고 (완료된 작업)" },
286
+ // GitHub Actions (CI)
287
+ {
288
+ path: ".github/workflows/vibe-ci.yml",
289
+ content: VIBE_CI_WORKFLOW_CONTENT,
290
+ description: "GitHub Actions CI (baseline-check)",
291
+ },
292
+ // Root files
293
+ {
294
+ path: "README.md",
295
+ content: `# Project
296
+
297
+ Created with [Vibe PM](https://vibecode.town) - AI Project Manager for non-technical founders.
298
+
299
+ ## Directory Structure
300
+
301
+ \`\`\`
302
+ .vibe/ # System state (do not edit)
303
+ config/ # Configuration (protected)
304
+ src/ # Source code (AI workspace)
305
+ components/ # UI components
306
+ lib/ # Utilities
307
+ tests/ # Test files
308
+ docs/ # Documentation
309
+ \`\`\`
310
+
311
+ ## Getting Started
312
+
313
+ 1. Describe your project idea
314
+ 2. Make decisions with vibe_pm.get_decision
315
+ 3. Let AI implement in src/
316
+ 4. Verify with vibe_pm.inspect_code
317
+ `,
318
+ },
319
+ {
320
+ path: ".gitignore",
321
+ content: `# Vibe PM state (local only)
322
+ .vibe/state/
323
+
324
+ # Environment
325
+ .env
326
+ .env.local
327
+ .env.*.local
328
+
329
+ # Dependencies
330
+ node_modules/
331
+ __pycache__/
332
+ *.pyc
333
+ venv/
334
+ .venv/
335
+
336
+ # Build outputs
337
+ dist/
338
+ build/
339
+ *.egg-info/
340
+
341
+ # IDE
342
+ .idea/
343
+ .vscode/
344
+ *.swp
345
+ *.swo
346
+
347
+ # OS
348
+ .DS_Store
349
+ Thumbs.db
350
+ `,
351
+ },
352
+ // AI Context & Rules (always included)
353
+ { path: ".cursorrules", content: CURSORRULES_CONTENT, description: "Cursor AI 행동 규칙" },
354
+ { path: "AI_ENTRYPOINT.md", content: AI_ENTRYPOINT_CONTENT, description: "AI 에이전트 진입점" },
355
+ ],
356
+ python_cli: [
357
+ // Inherit from standard
358
+ ...[], // Will be merged below
359
+ // Python-specific
360
+ {
361
+ path: "src/main.py",
362
+ content: `#!/usr/bin/env python3
363
+ """Main entry point for the application."""
364
+
365
+ def main():
366
+ print("Hello from Vibe PM!")
367
+
368
+ if __name__ == "__main__":
369
+ main()
370
+ `,
371
+ },
372
+ { path: "requirements.txt", content: "# Add your dependencies here\n" },
373
+ {
374
+ path: "pyproject.toml",
375
+ content: `[build-system]
376
+ requires = ["hatchling"]
377
+ build-backend = "hatchling.build"
378
+
379
+ [project]
380
+ name = "my-project"
381
+ version = "0.1.0"
382
+ description = "Created with Vibe PM"
383
+ requires-python = ">=3.10"
384
+
385
+ [project.scripts]
386
+ my-project = "src.main:main"
387
+ `,
388
+ },
389
+ ],
390
+ node_api: [
391
+ // Inherit from standard
392
+ ...[], // Will be merged below
393
+ // Node-specific
394
+ {
395
+ path: "src/index.ts",
396
+ content: `// Main entry point
397
+ console.log("Hello from Vibe PM!");
398
+ `,
399
+ },
400
+ {
401
+ path: "package.json",
402
+ content: JSON.stringify({
403
+ name: "my-project",
404
+ version: "0.1.0",
405
+ description: "Created with Vibe PM",
406
+ type: "module",
407
+ main: "dist/index.js",
408
+ scripts: {
409
+ dev: "tsx watch src/index.ts",
410
+ build: "tsc",
411
+ start: "node dist/index.js",
412
+ test: "vitest",
413
+ },
414
+ devDependencies: {
415
+ typescript: "^5.0.0",
416
+ tsx: "^4.0.0",
417
+ vitest: "^1.0.0",
418
+ },
419
+ }, null, 2) + "\n",
420
+ },
421
+ {
422
+ path: "tsconfig.json",
423
+ content: JSON.stringify({
424
+ compilerOptions: {
425
+ target: "ES2022",
426
+ module: "NodeNext",
427
+ moduleResolution: "NodeNext",
428
+ outDir: "./dist",
429
+ strict: true,
430
+ esModuleInterop: true,
431
+ skipLibCheck: true,
432
+ },
433
+ include: ["src/**/*"],
434
+ exclude: ["node_modules", "dist"],
435
+ }, null, 2) + "\n",
436
+ },
437
+ ],
438
+ };
439
+ // Merge python_cli and node_api with standard template
440
+ TEMPLATES.python_cli = [...TEMPLATES.standard, ...TEMPLATES.python_cli.filter(t => t.path !== "")];
441
+ TEMPLATES.node_api = [...TEMPLATES.standard, ...TEMPLATES.node_api.filter(t => t.path !== "")];
442
+ // ============================================================
443
+ // Helper Functions
444
+ // ============================================================
445
+ /**
446
+ * Generate ASCII tree representation of created structure
447
+ */
448
+ function generateTree(items) {
449
+ const dirs = new Set();
450
+ const files = [];
451
+ for (const item of items) {
452
+ const parts = item.path.split("/");
453
+ if (parts.length > 1) {
454
+ // Add all parent directories
455
+ let current = "";
456
+ for (let i = 0; i < parts.length - 1; i++) {
457
+ current = current ? `${current}/${parts[i]}` : parts[i];
458
+ dirs.add(current);
459
+ }
460
+ }
461
+ files.push(item.path);
462
+ }
463
+ const allPaths = [...Array.from(dirs).sort(), ...files.sort()];
464
+ const lines = [];
465
+ for (let i = 0; i < allPaths.length; i++) {
466
+ const p = allPaths[i];
467
+ const depth = p.split("/").length - 1;
468
+ const isLast = i === allPaths.length - 1 || allPaths[i + 1]?.split("/").length - 1 < depth;
469
+ const prefix = " ".repeat(depth) + (isLast ? "└── " : "├── ");
470
+ const name = p.split("/").pop() || p;
471
+ const isDir = dirs.has(p);
472
+ lines.push(`${prefix}${name}${isDir ? "/" : ""}`);
473
+ }
474
+ return lines.join("\n");
475
+ }
476
+ /**
477
+ * Check if directory is empty (or doesn't exist)
478
+ */
479
+ async function isDirectoryEmpty(dir) {
480
+ try {
481
+ const entries = await fs.readdir(dir);
482
+ // Ignore hidden files like .git
483
+ const nonHidden = entries.filter(e => !e.startsWith("."));
484
+ return nonHidden.length === 0;
485
+ }
486
+ catch {
487
+ // Directory doesn't exist - treat as empty
488
+ return true;
489
+ }
490
+ }
491
+ // ============================================================
492
+ // Main Implementation
493
+ // ============================================================
494
+ export async function scaffold(input, basePath = process.cwd()) {
495
+ const projectRoot = basePath;
496
+ const template = TEMPLATES[input.template] || TEMPLATES.standard;
497
+ // Safety check: don't overwrite non-empty directory without force
498
+ if (!input.force) {
499
+ const isEmpty = await isDirectoryEmpty(projectRoot);
500
+ if (!isEmpty) {
501
+ // Check if .vibe already exists (already scaffolded)
502
+ try {
503
+ await fs.access(path.join(projectRoot, ".vibe"));
504
+ // Already scaffolded - continue to fill in missing pieces
505
+ }
506
+ catch {
507
+ // Not scaffolded, not empty, not forced - refuse
508
+ return {
509
+ success: false,
510
+ created_paths: [],
511
+ skipped_paths: [],
512
+ structure_tree: "",
513
+ next_action: {
514
+ tool: "vibe_pm.scaffold",
515
+ reason: "디렉토리가 비어있지 않습니다. force: true 옵션을 사용하거나 빈 디렉토리에서 실행하세요.",
516
+ },
517
+ error: "directory_not_empty",
518
+ };
519
+ }
520
+ }
521
+ }
522
+ const created = [];
523
+ const skipped = [];
524
+ for (const item of template) {
525
+ const fullPath = path.join(projectRoot, item.path);
526
+ try {
527
+ // Check if already exists
528
+ await fs.access(fullPath);
529
+ skipped.push(item.path);
530
+ }
531
+ catch {
532
+ // Create parent directories
533
+ await fs.mkdir(path.dirname(fullPath), { recursive: true });
534
+ // Create file
535
+ await fs.writeFile(fullPath, item.content);
536
+ created.push(item.path);
537
+ }
538
+ }
539
+ const structureTree = generateTree(template);
540
+ return {
541
+ success: true,
542
+ created_paths: created,
543
+ skipped_paths: skipped,
544
+ structure_tree: structureTree,
545
+ next_action: {
546
+ tool: "vibe_pm.briefing",
547
+ reason: "프로젝트 구조가 준비되었습니다. 이제 프로젝트 아이디어를 브리핑하세요.",
548
+ },
549
+ };
550
+ }
@@ -0,0 +1,124 @@
1
+ // adapters/mcp-ts/src/tools/vibe_pm/search_oss.ts
2
+ // vibe_pm.search_oss - Search OSS repos (gh) and write evidence under runs/<run_id>/export/
3
+ import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
4
+ import { runEngine } from "../../engine.js";
5
+ import { safeJsonParse } from "../../cli.js";
6
+ import { validateToolInput } from "../../security/input-validator.js";
7
+ import { resolveProjectId, resolveRunId } from "./context.js";
8
+ function toCsv(values) {
9
+ return values
10
+ .map((v) => (typeof v === "string" ? v.trim() : ""))
11
+ .filter(Boolean)
12
+ .join(",");
13
+ }
14
+ function toInt(v) {
15
+ if (typeof v === "number" && Number.isFinite(v))
16
+ return Math.trunc(v);
17
+ if (typeof v === "string") {
18
+ const n = Number(v);
19
+ if (Number.isFinite(n))
20
+ return Math.trunc(n);
21
+ }
22
+ return undefined;
23
+ }
24
+ export async function searchOss(input) {
25
+ const basePath = process.cwd();
26
+ validateToolInput({ project_id: input.project_id, run_id: input.run_id });
27
+ const resolvedRun = resolveRunId(input.run_id ?? input.project_id, basePath);
28
+ const run_id = resolvedRun.run_id;
29
+ const project_id = resolveProjectId(run_id, basePath);
30
+ const keywords = Array.isArray(input.keywords) ? input.keywords.map((k) => String(k).trim()).filter(Boolean) : [];
31
+ if (keywords.length === 0) {
32
+ throw new McpError(ErrorCode.InvalidParams, "[VALIDATION] keywords is required");
33
+ }
34
+ const cmd = [
35
+ "search-oss",
36
+ run_id,
37
+ "--intent",
38
+ String(input.intent ?? "").trim(),
39
+ "--keywords",
40
+ toCsv(keywords)
41
+ ];
42
+ const constraints = input?.constraints ?? {};
43
+ const licenseAllowlist = Array.isArray(constraints?.license_allowlist)
44
+ ? constraints.license_allowlist.map((x) => String(x).trim()).filter(Boolean)
45
+ : [];
46
+ if (licenseAllowlist.length > 0) {
47
+ cmd.push("--license-allowlist", toCsv(licenseAllowlist));
48
+ }
49
+ const minStars = toInt(constraints?.min_stars);
50
+ if (minStars !== undefined)
51
+ cmd.push("--min-stars", String(minStars));
52
+ const updatedWithinDays = toInt(constraints?.updated_within_days);
53
+ if (updatedWithinDays !== undefined)
54
+ cmd.push("--updated-within-days", String(updatedWithinDays));
55
+ const language = typeof constraints?.language === "string" ? constraints.language.trim() : "";
56
+ if (language)
57
+ cmd.push("--language", language);
58
+ const limit = toInt(constraints?.limit);
59
+ if (limit !== undefined)
60
+ cmd.push("--limit", String(limit));
61
+ const sort = typeof constraints?.sort === "string" ? constraints.sort.trim() : "";
62
+ if (sort)
63
+ cmd.push("--sort", sort);
64
+ const writeEvidence = input?.write_evidence !== false;
65
+ if (!writeEvidence)
66
+ cmd.push("--no-write-evidence");
67
+ const res = await runEngine("vibecoding-helper", cmd, { timeoutMs: 180_000 });
68
+ if (res.code !== 0) {
69
+ throw new Error(`search-oss failed: ${res.stderr || res.stdout || `exit_code=${res.code}`}`);
70
+ }
71
+ const parsed = safeJsonParse(res.stdout);
72
+ if (!parsed.ok) {
73
+ throw new Error(`search-oss invalid_json: ${parsed.error}`);
74
+ }
75
+ const doc = parsed.value;
76
+ const query = typeof doc?.query === "string" && doc.query.trim()
77
+ ? String(doc.query).trim()
78
+ : keywords.map((k) => k.trim()).filter(Boolean).join(" ");
79
+ const warnings = [];
80
+ if (Array.isArray(doc?.warnings)) {
81
+ for (const w of doc.warnings) {
82
+ if (typeof w === "string" && w.trim())
83
+ warnings.push(w.trim());
84
+ }
85
+ }
86
+ const candidates = [];
87
+ const rawCandidates = Array.isArray(doc?.candidates) ? doc.candidates : [];
88
+ for (const c of rawCandidates) {
89
+ const obj = c;
90
+ const repo = typeof obj?.repo === "string" ? obj.repo.trim() : "";
91
+ if (!repo)
92
+ continue;
93
+ const urlRaw = typeof obj?.url === "string" ? obj.url.trim() : "";
94
+ const url = urlRaw || `https://github.com/${repo}`;
95
+ candidates.push({
96
+ repo,
97
+ url,
98
+ description: typeof obj?.description === "string" ? obj.description : "",
99
+ license: typeof obj?.license === "string" ? obj.license : "",
100
+ stars: typeof obj?.stars === "number" ? obj.stars : 0,
101
+ updated_at: typeof obj?.updated_at === "string" ? obj.updated_at : "",
102
+ ref_candidates: Array.isArray(obj?.ref_candidates)
103
+ ? obj.ref_candidates.map((x) => String(x)).filter(Boolean)
104
+ : []
105
+ });
106
+ }
107
+ const evidencePath = typeof doc?.evidence_path === "string" && doc.evidence_path.trim() ? String(doc.evidence_path).trim() : undefined;
108
+ const out = {
109
+ success: true,
110
+ project_id,
111
+ run_id,
112
+ intent: String(input.intent ?? "").trim(),
113
+ query,
114
+ candidates,
115
+ warnings,
116
+ next_action: {
117
+ tool: "vibe_pm.export_output",
118
+ reason: "Pick a template repo + pinned ref, then export using target=web_template_repo."
119
+ }
120
+ };
121
+ if (evidencePath)
122
+ out.evidence_path = evidencePath;
123
+ return out;
124
+ }