@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.
- package/LICENSE +21 -0
- package/README.md +269 -0
- package/build/auth/gate.js +225 -0
- package/build/auth/index.js +55 -0
- package/build/auth/public_key.js +27 -0
- package/build/auth/token_cache.js +122 -0
- package/build/auth/token_verifier.js +103 -0
- package/build/bootstrap/doctor.js +115 -0
- package/build/bootstrap/installer.js +673 -0
- package/build/bootstrap/lock.js +37 -0
- package/build/bootstrap/platform.js +26 -0
- package/build/bootstrap/registry.js +37 -0
- package/build/cache/index.js +147 -0
- package/build/cli.js +101 -0
- package/build/contracts.js +22 -0
- package/build/control_plane/gate.js +161 -0
- package/build/control_plane/index.js +6 -0
- package/build/dx/activity.js +139 -0
- package/build/engine.js +106 -0
- package/build/errors.js +171 -0
- package/build/generated/activate_input.js +2 -0
- package/build/generated/activate_output.js +57 -0
- package/build/generated/advisory_review_input.js +2 -0
- package/build/generated/advisory_review_output.js +35 -0
- package/build/generated/auth_token_file.js +2 -0
- package/build/generated/briefing_input.js +2 -0
- package/build/generated/briefing_output.js +2 -0
- package/build/generated/clinic_bridge_file.js +13 -0
- package/build/generated/contracts_bundle_info.js +5 -0
- package/build/generated/create_work_order_input.js +2 -0
- package/build/generated/create_work_order_output.js +2 -0
- package/build/generated/current_work_order_file.js +2 -0
- package/build/generated/doctor_input.js +2 -0
- package/build/generated/doctor_output.js +24 -0
- package/build/generated/execution_result.js +2 -0
- package/build/generated/execution_task.js +2 -0
- package/build/generated/export_output_input.js +2 -0
- package/build/generated/export_output_output.js +2 -0
- package/build/generated/finalize_work_input.js +2 -0
- package/build/generated/finalize_work_output.js +2 -0
- package/build/generated/gate_input.js +2 -0
- package/build/generated/gate_output.js +2 -0
- package/build/generated/gate_result_v1.js +2 -0
- package/build/generated/get_decision_input.js +2 -0
- package/build/generated/get_decision_output.js +13 -0
- package/build/generated/handoff_to_clinic.js +2 -0
- package/build/generated/index.js +75 -0
- package/build/generated/inspect_code_input.js +2 -0
- package/build/generated/inspect_code_output.js +13 -0
- package/build/generated/memory_retrieve_output.js +2 -0
- package/build/generated/memory_state_file.js +2 -0
- package/build/generated/memory_status_input.js +2 -0
- package/build/generated/memory_status_output.js +13 -0
- package/build/generated/memory_sync_input.js +2 -0
- package/build/generated/memory_sync_output.js +13 -0
- package/build/generated/plugin_result.js +2 -0
- package/build/generated/react_perf_check_patterns_input.js +2 -0
- package/build/generated/react_perf_check_patterns_output.js +2 -0
- package/build/generated/react_perf_generate_report_input.js +2 -0
- package/build/generated/react_perf_generate_report_output.js +2 -0
- package/build/generated/repair_plan_input.js +2 -0
- package/build/generated/repair_plan_output.js +2 -0
- package/build/generated/run_app_input.js +2 -0
- package/build/generated/run_app_output.js +2 -0
- package/build/generated/run_state_file.js +13 -0
- package/build/generated/scaffold_input.js +2 -0
- package/build/generated/scaffold_output.js +2 -0
- package/build/generated/search_oss_input.js +2 -0
- package/build/generated/search_oss_output.js +2 -0
- package/build/generated/selection_validation_result.js +2 -0
- package/build/generated/signal_agent_input.js +2 -0
- package/build/generated/spec_high_ask_queue_items_file.js +2 -0
- package/build/generated/spec_high_clinic_bridge_output.js +2 -0
- package/build/generated/spec_high_decision_draft_output.js +2 -0
- package/build/generated/spec_high_validate_output.js +2 -0
- package/build/generated/status_input.js +2 -0
- package/build/generated/status_output.js +2 -0
- package/build/generated/submit_decision_input.js +2 -0
- package/build/generated/submit_decision_output.js +2 -0
- package/build/generated/tool_error_output.js +2 -0
- package/build/generated/undo_last_task_input.js +2 -0
- package/build/generated/undo_last_task_output.js +2 -0
- package/build/generated/update_input.js +2 -0
- package/build/generated/update_output.js +2 -0
- package/build/generated/vibe_pm_inspection_result.js +2 -0
- package/build/generated/vibe_pm_report_markdown.js +2 -0
- package/build/generated/vibe_pm_verdict.js +2 -0
- package/build/generated/vibe_repo_config.js +2 -0
- package/build/generated/vibecoding_helper_answer_output.js +2 -0
- package/build/generated/vibecoding_helper_one_loop_selection_output.js +2 -0
- package/build/generated/vibecoding_helper_show_ask_queue_output.js +2 -0
- package/build/generated/work_order_v1.js +2 -0
- package/build/generated/zoekt_evidence_input.js +2 -0
- package/build/generated/zoekt_evidence_output.js +2 -0
- package/build/index.js +111 -0
- package/build/legacy_alias.js +65 -0
- package/build/local-mode/bash.js +61 -0
- package/build/local-mode/config.js +171 -0
- package/build/local-mode/git.js +33 -0
- package/build/local-mode/init.js +110 -0
- package/build/local-mode/paths.js +24 -0
- package/build/local-mode/templates.js +856 -0
- package/build/local-mode/work-order.js +41 -0
- package/build/resources/index.js +246 -0
- package/build/security/input-validator.js +119 -0
- package/build/security/path-policy.js +289 -0
- package/build/security/sandbox.js +228 -0
- package/build/tools/react_perf/check_patterns.js +172 -0
- package/build/tools/react_perf/generate_report.js +337 -0
- package/build/tools/react_perf/index.js +119 -0
- package/build/tools/react_perf/rules/advanced.js +325 -0
- package/build/tools/react_perf/rules/async.js +104 -0
- package/build/tools/react_perf/rules/bundle.js +101 -0
- package/build/tools/react_perf/rules/client.js +186 -0
- package/build/tools/react_perf/rules/index.js +74 -0
- package/build/tools/react_perf/rules/js.js +148 -0
- package/build/tools/react_perf/rules/rendering.js +166 -0
- package/build/tools/react_perf/rules/rerender.js +161 -0
- package/build/tools/react_perf/rules/server.js +141 -0
- package/build/tools/react_perf/types.js +127 -0
- package/build/tools/vibe_pm/activate.js +102 -0
- package/build/tools/vibe_pm/advisory_review.js +77 -0
- package/build/tools/vibe_pm/briefing.js +178 -0
- package/build/tools/vibe_pm/context.js +439 -0
- package/build/tools/vibe_pm/create_work_order.js +271 -0
- package/build/tools/vibe_pm/doc_status_gate.js +370 -0
- package/build/tools/vibe_pm/doctor.js +262 -0
- package/build/tools/vibe_pm/entity_gate/preflight.js +78 -0
- package/build/tools/vibe_pm/export_output.js +135 -0
- package/build/tools/vibe_pm/finalize_work.js +393 -0
- package/build/tools/vibe_pm/gate.js +33 -0
- package/build/tools/vibe_pm/get_decision.js +281 -0
- package/build/tools/vibe_pm/index.js +593 -0
- package/build/tools/vibe_pm/inspect_code.js +828 -0
- package/build/tools/vibe_pm/intent/generator.js +294 -0
- package/build/tools/vibe_pm/intent/index.js +5 -0
- package/build/tools/vibe_pm/intent/prompt_density.js +227 -0
- package/build/tools/vibe_pm/intent/types.js +70 -0
- package/build/tools/vibe_pm/intent/verifier.js +237 -0
- package/build/tools/vibe_pm/kce/doc_usage.js +51 -0
- package/build/tools/vibe_pm/kce/on_finalize.js +11 -0
- package/build/tools/vibe_pm/kce/preflight.js +232 -0
- package/build/tools/vibe_pm/local_memory.js +26 -0
- package/build/tools/vibe_pm/memory_status.js +82 -0
- package/build/tools/vibe_pm/memory_sync.js +134 -0
- package/build/tools/vibe_pm/modules/decision_snapshot.js +29 -0
- package/build/tools/vibe_pm/modules/ensure.js +100 -0
- package/build/tools/vibe_pm/modules/fingerprint.js +30 -0
- package/build/tools/vibe_pm/modules/fix_dependencies.js +394 -0
- package/build/tools/vibe_pm/modules/planning_v1.js +110 -0
- package/build/tools/vibe_pm/modules/repo_context.js +56 -0
- package/build/tools/vibe_pm/modules/research_v1.js +114 -0
- package/build/tools/vibe_pm/modules/skills_v1.js +100 -0
- package/build/tools/vibe_pm/pm_language.js +222 -0
- package/build/tools/vibe_pm/repair_plan.js +199 -0
- package/build/tools/vibe_pm/run_app.js +597 -0
- package/build/tools/vibe_pm/run_app_podman.js +64 -0
- package/build/tools/vibe_pm/scaffold.js +550 -0
- package/build/tools/vibe_pm/search_oss.js +124 -0
- package/build/tools/vibe_pm/status.js +153 -0
- package/build/tools/vibe_pm/submit_decision.js +87 -0
- package/build/tools/vibe_pm/system_design/issue_mapping.js +47 -0
- package/build/tools/vibe_pm/system_design/rulebook.js +112 -0
- package/build/tools/vibe_pm/system_design/semgrep.js +132 -0
- package/build/tools/vibe_pm/types.js +229 -0
- package/build/tools/vibe_pm/undo_last_task.js +163 -0
- package/build/tools/vibe_pm/update.js +146 -0
- package/build/tools/vibe_pm/zoekt_evidence.js +96 -0
- package/build/tools.js +269 -0
- package/build/version-check.js +239 -0
- package/build/vibe-cli.js +631 -0
- package/package.json +76 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
// adapters/mcp-ts/src/tools/vibe_pm/doctor.ts
|
|
2
|
+
// vibe_pm.doctor - Installation health check and auto-repair
|
|
3
|
+
import { DoctorInputSchema } from "../../generated/doctor_input.js";
|
|
4
|
+
import { doctor as runDoctor, healthCheck, checkForUpdates } from "../../bootstrap/doctor.js";
|
|
5
|
+
import { CONTRACTS_BUNDLE_SHA256, CONTRACTS_SCHEMA_COUNT, CONTRACTS_VERSION } from "../../generated/contracts_bundle_info.js";
|
|
6
|
+
import { getAuthGate } from "../../auth/index.js";
|
|
7
|
+
import { resolveProjectId } from "./context.js";
|
|
8
|
+
import { memoryStatus } from "./memory_status.js";
|
|
9
|
+
import { memorySync } from "./memory_sync.js";
|
|
10
|
+
// ============================================================
|
|
11
|
+
// Input/Output Types
|
|
12
|
+
// ============================================================
|
|
13
|
+
export { DoctorInputSchema };
|
|
14
|
+
// ============================================================
|
|
15
|
+
// Status Mapping (PM Language)
|
|
16
|
+
// ============================================================
|
|
17
|
+
function mapEngineStatus(status) {
|
|
18
|
+
switch (status) {
|
|
19
|
+
case "ok":
|
|
20
|
+
return "정상";
|
|
21
|
+
case "needs_update":
|
|
22
|
+
return "업데이트 필요";
|
|
23
|
+
case "missing":
|
|
24
|
+
return "설치 필요";
|
|
25
|
+
case "corrupted":
|
|
26
|
+
return "손상됨";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// ============================================================
|
|
30
|
+
// Doctor Implementation
|
|
31
|
+
// ============================================================
|
|
32
|
+
/**
|
|
33
|
+
* vibe_pm.doctor - Installation health check and auto-repair
|
|
34
|
+
*
|
|
35
|
+
* PM-friendly description:
|
|
36
|
+
* 설치 상태를 점검하고 문제가 있으면 자동으로 수정합니다.
|
|
37
|
+
*/
|
|
38
|
+
export async function doctor(input) {
|
|
39
|
+
const autoFix = input.auto_fix ?? true;
|
|
40
|
+
const actionsTaken = [];
|
|
41
|
+
const basePath = process.cwd();
|
|
42
|
+
const contracts = {
|
|
43
|
+
version: CONTRACTS_VERSION,
|
|
44
|
+
bundle_sha256: CONTRACTS_BUNDLE_SHA256,
|
|
45
|
+
schema_count: CONTRACTS_SCHEMA_COUNT
|
|
46
|
+
};
|
|
47
|
+
async function getLocalMemoryStatus() {
|
|
48
|
+
try {
|
|
49
|
+
const projectId = resolveProjectId(undefined, basePath);
|
|
50
|
+
return await memoryStatus({
|
|
51
|
+
project_id: projectId,
|
|
52
|
+
docs_root: "docs",
|
|
53
|
+
persist_dir: ".vibe/chroma"
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
// Step 1: Quick health check
|
|
62
|
+
const quickCheck = await healthCheck();
|
|
63
|
+
if (quickCheck.status === "OK") {
|
|
64
|
+
// All good, return immediately
|
|
65
|
+
const engines = quickCheck.engines.map((e) => ({
|
|
66
|
+
name: e.name,
|
|
67
|
+
status: mapEngineStatus(e.status),
|
|
68
|
+
version: e.version,
|
|
69
|
+
current_version: e.currentVersion
|
|
70
|
+
}));
|
|
71
|
+
// Local Memory check (best-effort)
|
|
72
|
+
let localMemory = await getLocalMemoryStatus();
|
|
73
|
+
if (autoFix && localMemory?.status === "NEEDS_SYNC") {
|
|
74
|
+
try {
|
|
75
|
+
const gate = getAuthGate();
|
|
76
|
+
const allowed = await gate.check("local_memory_pro_tools");
|
|
77
|
+
if (allowed.allowed) {
|
|
78
|
+
actionsTaken.push("Local Memory 갱신 시도");
|
|
79
|
+
const projectId = resolveProjectId(undefined, basePath);
|
|
80
|
+
const syncRes = await memorySync({
|
|
81
|
+
project_id: projectId,
|
|
82
|
+
docs_root: "docs",
|
|
83
|
+
mode: "on_demand",
|
|
84
|
+
force_full: false
|
|
85
|
+
});
|
|
86
|
+
actionsTaken.push(`Local Memory 결과: ${syncRes.status}`);
|
|
87
|
+
localMemory = await getLocalMemoryStatus();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// ignore
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (localMemory && localMemory.status !== "READY") {
|
|
95
|
+
const msg = localMemory.status === "NEEDS_SYNC"
|
|
96
|
+
? "Local Memory 갱신이 필요합니다. vibe_pm.memory_sync를 실행해 보세요."
|
|
97
|
+
: localMemory.status === "OFFLINE_NO_INDEX"
|
|
98
|
+
? "오프라인 모드라 Local Memory 인덱스를 만들 수 없습니다. 온라인 환경에서 한 번 동기화가 필요합니다."
|
|
99
|
+
: "Local Memory 설치/환경 상태를 확인해 주세요.";
|
|
100
|
+
return {
|
|
101
|
+
status: "NEEDS_ATTENTION",
|
|
102
|
+
contracts,
|
|
103
|
+
summary: "엔진은 정상입니다. 다만 Local Memory 준비가 필요합니다.",
|
|
104
|
+
engines,
|
|
105
|
+
local_memory: localMemory,
|
|
106
|
+
actions_taken: actionsTaken.length > 0 ? actionsTaken : undefined,
|
|
107
|
+
next_action: {
|
|
108
|
+
type: "MANUAL_FIX",
|
|
109
|
+
message: msg
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
status: "OK",
|
|
115
|
+
contracts,
|
|
116
|
+
summary: "모든 엔진이 정상적으로 설치되어 있습니다.",
|
|
117
|
+
engines,
|
|
118
|
+
local_memory: localMemory,
|
|
119
|
+
actions_taken: actionsTaken.length > 0 ? actionsTaken : undefined,
|
|
120
|
+
next_action: {
|
|
121
|
+
type: "NONE",
|
|
122
|
+
message: "추가 조치가 필요하지 않습니다."
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
// Step 2: Issues found - check details
|
|
127
|
+
const updateCheck = await checkForUpdates();
|
|
128
|
+
if (autoFix) {
|
|
129
|
+
// Step 3: Auto-fix by running full doctor
|
|
130
|
+
actionsTaken.push("설치 상태 점검 시작");
|
|
131
|
+
const doctorResult = await runDoctor();
|
|
132
|
+
// Map results
|
|
133
|
+
const engines = Object.entries(doctorResult.engines).map(([name, info]) => ({
|
|
134
|
+
name,
|
|
135
|
+
status: mapEngineStatus(info.status),
|
|
136
|
+
version: info.version,
|
|
137
|
+
current_version: info.currentVersion
|
|
138
|
+
}));
|
|
139
|
+
if (doctorResult.status === "OK") {
|
|
140
|
+
actionsTaken.push("문제 자동 수정 완료");
|
|
141
|
+
// Local Memory check (best-effort)
|
|
142
|
+
let localMemory = await getLocalMemoryStatus();
|
|
143
|
+
if (localMemory?.status === "NEEDS_SYNC") {
|
|
144
|
+
try {
|
|
145
|
+
const gate = getAuthGate();
|
|
146
|
+
const allowed = await gate.check("local_memory_pro_tools");
|
|
147
|
+
if (allowed.allowed) {
|
|
148
|
+
actionsTaken.push("Local Memory 갱신 시도");
|
|
149
|
+
const projectId = resolveProjectId(undefined, basePath);
|
|
150
|
+
const syncRes = await memorySync({
|
|
151
|
+
project_id: projectId,
|
|
152
|
+
docs_root: "docs",
|
|
153
|
+
mode: "on_demand",
|
|
154
|
+
force_full: false
|
|
155
|
+
});
|
|
156
|
+
actionsTaken.push(`Local Memory 결과: ${syncRes.status}`);
|
|
157
|
+
localMemory = await getLocalMemoryStatus();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
// ignore
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (localMemory && localMemory.status !== "READY") {
|
|
165
|
+
const msg = localMemory.status === "NEEDS_SYNC"
|
|
166
|
+
? "Local Memory 갱신이 필요합니다. vibe_pm.memory_sync를 실행해 보세요."
|
|
167
|
+
: localMemory.status === "OFFLINE_NO_INDEX"
|
|
168
|
+
? "오프라인 모드라 Local Memory 인덱스를 만들 수 없습니다. 온라인 환경에서 한 번 동기화가 필요합니다."
|
|
169
|
+
: "Local Memory 설치/환경 상태를 확인해 주세요.";
|
|
170
|
+
return {
|
|
171
|
+
status: "NEEDS_ATTENTION",
|
|
172
|
+
contracts,
|
|
173
|
+
summary: "엔진은 정상입니다. 다만 Local Memory 준비가 필요합니다.",
|
|
174
|
+
engines,
|
|
175
|
+
actions_taken: actionsTaken,
|
|
176
|
+
local_memory: localMemory,
|
|
177
|
+
next_action: {
|
|
178
|
+
type: "MANUAL_FIX",
|
|
179
|
+
message: msg
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
status: "FIXED",
|
|
185
|
+
contracts,
|
|
186
|
+
summary: "문제를 발견하고 자동으로 수정했습니다.",
|
|
187
|
+
engines,
|
|
188
|
+
actions_taken: actionsTaken,
|
|
189
|
+
local_memory: localMemory,
|
|
190
|
+
next_action: {
|
|
191
|
+
type: "NONE",
|
|
192
|
+
message: "모든 문제가 해결되었습니다."
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
// Some issues couldn't be fixed
|
|
197
|
+
if (doctorResult.status === "NEEDS_UPDATE") {
|
|
198
|
+
const localMemory = await getLocalMemoryStatus();
|
|
199
|
+
return {
|
|
200
|
+
status: "NEEDS_ATTENTION",
|
|
201
|
+
contracts,
|
|
202
|
+
summary: "일부 엔진 업데이트가 필요합니다.",
|
|
203
|
+
engines,
|
|
204
|
+
actions_taken: actionsTaken,
|
|
205
|
+
local_memory: localMemory,
|
|
206
|
+
next_action: {
|
|
207
|
+
type: "MANUAL_FIX",
|
|
208
|
+
message: "vibe_pm.update 도구를 실행하여 업데이트하세요."
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
// Error state
|
|
213
|
+
const localMemory = await getLocalMemoryStatus();
|
|
214
|
+
return {
|
|
215
|
+
status: "ERROR",
|
|
216
|
+
contracts,
|
|
217
|
+
summary: doctorResult.error ?? "알 수 없는 오류가 발생했습니다.",
|
|
218
|
+
engines,
|
|
219
|
+
actions_taken: actionsTaken,
|
|
220
|
+
local_memory: localMemory,
|
|
221
|
+
next_action: {
|
|
222
|
+
type: "CONTACT_SUPPORT",
|
|
223
|
+
message: "문제가 지속되면 지원팀에 문의하세요."
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
// Auto-fix disabled - just report
|
|
228
|
+
const engines = quickCheck.engines.map((e) => ({
|
|
229
|
+
name: e.name,
|
|
230
|
+
status: mapEngineStatus(e.status),
|
|
231
|
+
version: e.version,
|
|
232
|
+
current_version: e.currentVersion
|
|
233
|
+
}));
|
|
234
|
+
const issueCount = engines.filter((e) => e.status !== "정상").length;
|
|
235
|
+
const localMemory = await getLocalMemoryStatus();
|
|
236
|
+
return {
|
|
237
|
+
status: "NEEDS_ATTENTION",
|
|
238
|
+
contracts,
|
|
239
|
+
summary: `${issueCount}개의 엔진에 문제가 있습니다.`,
|
|
240
|
+
engines,
|
|
241
|
+
local_memory: localMemory,
|
|
242
|
+
next_action: {
|
|
243
|
+
type: "MANUAL_FIX",
|
|
244
|
+
message: "auto_fix: true로 다시 실행하여 자동 수정하세요."
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
catch (e) {
|
|
249
|
+
const localMemory = await getLocalMemoryStatus();
|
|
250
|
+
return {
|
|
251
|
+
status: "ERROR",
|
|
252
|
+
contracts,
|
|
253
|
+
summary: e instanceof Error ? e.message : "알 수 없는 오류",
|
|
254
|
+
engines: [],
|
|
255
|
+
local_memory: localMemory,
|
|
256
|
+
next_action: {
|
|
257
|
+
type: "CONTACT_SUPPORT",
|
|
258
|
+
message: "설치 점검 중 오류가 발생했습니다. 지원팀에 문의하세요."
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// adapters/mcp-ts/src/tools/vibe_pm/entity_gate/preflight.ts
|
|
2
|
+
// Internal: Entity Gate runner (schema + rules) + run evidence
|
|
3
|
+
import * as fs from "node:fs";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { runEngine } from "../../../engine.js";
|
|
7
|
+
import { safeJsonParse } from "../../../cli.js";
|
|
8
|
+
import { resolveRunDir } from "../context.js";
|
|
9
|
+
const EntityGateReportSchema = z.object({
|
|
10
|
+
status: z.enum(["OK", "FAIL"]),
|
|
11
|
+
errors: z.array(z.string()),
|
|
12
|
+
entity_files: z.number().int().nonnegative().optional()
|
|
13
|
+
});
|
|
14
|
+
function tryWriteJson(pathAbs, doc) {
|
|
15
|
+
try {
|
|
16
|
+
fs.mkdirSync(path.dirname(pathAbs), { recursive: true });
|
|
17
|
+
fs.writeFileSync(pathAbs, JSON.stringify(doc, null, 2) + "\n", "utf-8");
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
// ignore
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function tryAppendJsonl(pathAbs, record) {
|
|
24
|
+
try {
|
|
25
|
+
fs.mkdirSync(path.dirname(pathAbs), { recursive: true });
|
|
26
|
+
fs.appendFileSync(pathAbs, JSON.stringify(record) + "\n", "utf-8");
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// ignore
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function kceEventsPath(run_id, basePath) {
|
|
33
|
+
const resolved = resolveRunDir(run_id, basePath);
|
|
34
|
+
if (!resolved)
|
|
35
|
+
return null;
|
|
36
|
+
return path.join(resolved.run_dir, "kce", "kce_events.jsonl");
|
|
37
|
+
}
|
|
38
|
+
export async function runEntityGate(args) {
|
|
39
|
+
const eventsPath = args.writeEvidence && args.run_id ? kceEventsPath(args.run_id, args.basePath) : null;
|
|
40
|
+
try {
|
|
41
|
+
const { code, stdout, stderr } = await runEngine("vibecoding-helper", ["entity-gate"], { timeoutMs: 60_000 });
|
|
42
|
+
if (code !== 0 && !stdout.trim()) {
|
|
43
|
+
throw new Error(`entity-gate failed: ${stderr || `exit_code=${code}`}`);
|
|
44
|
+
}
|
|
45
|
+
const parsed = safeJsonParse(stdout);
|
|
46
|
+
if (!parsed.ok)
|
|
47
|
+
throw new Error(`entity-gate invalid_json: ${parsed.error}`);
|
|
48
|
+
const validated = EntityGateReportSchema.safeParse(parsed.value);
|
|
49
|
+
if (!validated.success)
|
|
50
|
+
throw new Error("entity-gate schema mismatch");
|
|
51
|
+
const report = validated.data;
|
|
52
|
+
if (args.writeEvidence && args.run_id) {
|
|
53
|
+
const resolved = resolveRunDir(args.run_id, args.basePath);
|
|
54
|
+
if (resolved) {
|
|
55
|
+
tryWriteJson(path.join(resolved.run_dir, "kce", "entity_gate_report.json"), report);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (eventsPath) {
|
|
59
|
+
tryAppendJsonl(eventsPath, {
|
|
60
|
+
ts: new Date().toISOString(),
|
|
61
|
+
event: report.status === "OK" ? "ENTITY_GATE_OK" : "ENTITY_GATE_FAIL",
|
|
62
|
+
errors_count: report.errors?.length ?? 0
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return report;
|
|
66
|
+
}
|
|
67
|
+
catch (e) {
|
|
68
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
69
|
+
if (eventsPath) {
|
|
70
|
+
tryAppendJsonl(eventsPath, {
|
|
71
|
+
ts: new Date().toISOString(),
|
|
72
|
+
event: "ENTITY_GATE_FAIL",
|
|
73
|
+
error: msg
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
throw e;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// adapters/mcp-ts/src/tools/vibe_pm/export_output.ts
|
|
2
|
+
// vibe_pm.export_output - Export a feature into a chosen output format via Output Adapters
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import { runEngine } from "../../engine.js";
|
|
6
|
+
import { safeJsonParse } from "../../cli.js";
|
|
7
|
+
import { validateToolInput } from "../../security/input-validator.js";
|
|
8
|
+
import { isPathForbidden, validatePath } from "../../security/path-policy.js";
|
|
9
|
+
import { resolveProjectId, resolveRunId, resolveRunDir } from "./context.js";
|
|
10
|
+
function normalizeRelPosix(p) {
|
|
11
|
+
return p.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
12
|
+
}
|
|
13
|
+
function resolveDefaultOutputDir(target) {
|
|
14
|
+
const t = (target ?? "").trim();
|
|
15
|
+
return t ? `export/${t}` : "export/out";
|
|
16
|
+
}
|
|
17
|
+
function featureSpecPath(runDirAbs) {
|
|
18
|
+
return path.join(runDirAbs, "export", "feature_output_spec.json");
|
|
19
|
+
}
|
|
20
|
+
function exportPlanPath(runDirAbs) {
|
|
21
|
+
return path.join(runDirAbs, "export", "export_plan.json");
|
|
22
|
+
}
|
|
23
|
+
function exportArtifactsPath(runDirAbs) {
|
|
24
|
+
return path.join(runDirAbs, "export", "export_artifacts.json");
|
|
25
|
+
}
|
|
26
|
+
export async function exportOutput(input) {
|
|
27
|
+
const basePath = process.cwd();
|
|
28
|
+
validateToolInput({ project_id: input.project_id, run_id: input.run_id });
|
|
29
|
+
const resolvedRun = resolveRunId(input.run_id ?? input.project_id, basePath);
|
|
30
|
+
const run_id = resolvedRun.run_id;
|
|
31
|
+
const project_id = resolveProjectId(run_id, basePath);
|
|
32
|
+
const resolved = resolveRunDir(run_id, basePath);
|
|
33
|
+
if (!resolved) {
|
|
34
|
+
throw new McpError(ErrorCode.InvalidParams, "Run not found.");
|
|
35
|
+
}
|
|
36
|
+
const runDirAbs = resolved.run_dir;
|
|
37
|
+
const specAbs = featureSpecPath(runDirAbs);
|
|
38
|
+
const target = String(input.target ?? "").trim();
|
|
39
|
+
if (!target) {
|
|
40
|
+
throw new McpError(ErrorCode.InvalidParams, "[VALIDATION] target is required");
|
|
41
|
+
}
|
|
42
|
+
const outputDirInput = (input.output_dir ?? "").trim() || resolveDefaultOutputDir(target);
|
|
43
|
+
const outputDirAbs = validatePath(outputDirInput, basePath);
|
|
44
|
+
const outputDirRel = normalizeRelPosix(path.relative(basePath, outputDirAbs));
|
|
45
|
+
const forbid = isPathForbidden(outputDirRel);
|
|
46
|
+
if (forbid.forbidden) {
|
|
47
|
+
throw new McpError(ErrorCode.InvalidParams, "Forbidden export output_dir.", {
|
|
48
|
+
category: "SECURITY",
|
|
49
|
+
context: { path: outputDirRel, zone: forbid.zone, reason: forbid.reason },
|
|
50
|
+
recovery: "Choose a different output directory inside the project."
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const cliLanguage = input.cli_language ?? "python";
|
|
54
|
+
const dryRun = Boolean(input.dry_run);
|
|
55
|
+
const templateRepo = typeof input.template_pin?.repo === "string" ? input.template_pin.repo.trim() : "";
|
|
56
|
+
const templateRef = typeof input.template_pin?.ref === "string" ? input.template_pin.ref.trim() : "";
|
|
57
|
+
const needsTemplatePin = target === "web_template_repo";
|
|
58
|
+
const planCmd = ["export-plan", run_id, "--target", target, "--output-dir", outputDirRel, "--cli-language", cliLanguage];
|
|
59
|
+
if (needsTemplatePin) {
|
|
60
|
+
if (!templateRepo || !templateRef) {
|
|
61
|
+
throw new McpError(ErrorCode.InvalidParams, "[VALIDATION] template_pin.repo/ref is required for target=web_template_repo");
|
|
62
|
+
}
|
|
63
|
+
planCmd.push("--template-repo", templateRepo, "--template-ref", templateRef);
|
|
64
|
+
}
|
|
65
|
+
if (dryRun)
|
|
66
|
+
planCmd.push("--dry-run");
|
|
67
|
+
const planRes = await runEngine("vibecoding-helper", planCmd, { timeoutMs: 120_000 });
|
|
68
|
+
if (planRes.code !== 0) {
|
|
69
|
+
throw new Error(`export-plan failed: ${planRes.stderr || `exit_code=${planRes.code}`}`);
|
|
70
|
+
}
|
|
71
|
+
const planParsed = safeJsonParse(planRes.stdout);
|
|
72
|
+
if (!planParsed.ok) {
|
|
73
|
+
throw new Error(`export-plan invalid_json: ${planParsed.error}`);
|
|
74
|
+
}
|
|
75
|
+
const warnings = [];
|
|
76
|
+
if (Array.isArray(planParsed.value?.warnings)) {
|
|
77
|
+
for (const w of planParsed.value.warnings) {
|
|
78
|
+
if (typeof w === "string" && w.trim())
|
|
79
|
+
warnings.push(w.trim());
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const createdPaths = [];
|
|
83
|
+
let artifactsPathRel;
|
|
84
|
+
if (!dryRun) {
|
|
85
|
+
const genCmd = [
|
|
86
|
+
"export-generate",
|
|
87
|
+
run_id,
|
|
88
|
+
"--target",
|
|
89
|
+
target,
|
|
90
|
+
"--output-dir",
|
|
91
|
+
outputDirRel,
|
|
92
|
+
"--cli-language",
|
|
93
|
+
cliLanguage
|
|
94
|
+
];
|
|
95
|
+
if (needsTemplatePin)
|
|
96
|
+
genCmd.push("--template-repo", templateRepo, "--template-ref", templateRef);
|
|
97
|
+
const genRes = await runEngine("vibecoding-helper", genCmd, { timeoutMs: 300_000 });
|
|
98
|
+
if (genRes.code !== 0) {
|
|
99
|
+
throw new Error(`export-generate failed: ${genRes.stderr || `exit_code=${genRes.code}`}`);
|
|
100
|
+
}
|
|
101
|
+
const genParsed = safeJsonParse(genRes.stdout);
|
|
102
|
+
if (!genParsed.ok) {
|
|
103
|
+
throw new Error(`export-generate invalid_json: ${genParsed.error}`);
|
|
104
|
+
}
|
|
105
|
+
const files = genParsed.value?.files;
|
|
106
|
+
if (Array.isArray(files)) {
|
|
107
|
+
for (const f of files) {
|
|
108
|
+
const p = typeof f?.path === "string" ? f.path : "";
|
|
109
|
+
if (p)
|
|
110
|
+
createdPaths.push(normalizeRelPosix(p));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
artifactsPathRel = normalizeRelPosix(path.relative(basePath, exportArtifactsPath(runDirAbs)));
|
|
114
|
+
}
|
|
115
|
+
const out = {
|
|
116
|
+
success: true,
|
|
117
|
+
project_id,
|
|
118
|
+
run_id,
|
|
119
|
+
target,
|
|
120
|
+
output_dir: outputDirRel,
|
|
121
|
+
dry_run: dryRun,
|
|
122
|
+
paths: {
|
|
123
|
+
feature_output_spec_path: normalizeRelPosix(path.relative(basePath, specAbs)),
|
|
124
|
+
export_plan_path: normalizeRelPosix(path.relative(basePath, exportPlanPath(runDirAbs))),
|
|
125
|
+
export_artifacts_path: artifactsPathRel
|
|
126
|
+
},
|
|
127
|
+
created_paths: createdPaths,
|
|
128
|
+
warnings,
|
|
129
|
+
next_action: {
|
|
130
|
+
tool: "vibe_pm.status",
|
|
131
|
+
reason: "Review the export outputs and continue."
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
return out;
|
|
135
|
+
}
|