@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,237 @@
|
|
|
1
|
+
// adapters/mcp-ts/src/tools/vibe_pm/intent/verifier.ts
|
|
2
|
+
// Intent-based Verification
|
|
3
|
+
// inspect_code에서 Intent 문서 기반 검증
|
|
4
|
+
import { loadIntent, updateIntentStatus } from "./generator.js";
|
|
5
|
+
// ============================================================
|
|
6
|
+
// Intent-based Verification
|
|
7
|
+
// ============================================================
|
|
8
|
+
/**
|
|
9
|
+
* Verify implementation against Intent acceptance criteria
|
|
10
|
+
*/
|
|
11
|
+
export function verifyAgainstIntent(intent, inspectionResults) {
|
|
12
|
+
const details = [];
|
|
13
|
+
let passCount = 0;
|
|
14
|
+
let totalCount = 0;
|
|
15
|
+
// Check each acceptance criterion
|
|
16
|
+
for (const ac of intent.acceptance) {
|
|
17
|
+
const detail = verifyAcceptanceCriterion(ac, inspectionResults);
|
|
18
|
+
details.push(detail);
|
|
19
|
+
if (detail.status === "pass") {
|
|
20
|
+
passCount++;
|
|
21
|
+
}
|
|
22
|
+
if (detail.status !== "skip") {
|
|
23
|
+
totalCount++;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Check scope compliance
|
|
27
|
+
const scopeDetails = verifyScopeCompliance(intent, inspectionResults);
|
|
28
|
+
details.push(...scopeDetails);
|
|
29
|
+
for (const sd of scopeDetails) {
|
|
30
|
+
if (sd.status === "pass")
|
|
31
|
+
passCount++;
|
|
32
|
+
if (sd.status !== "skip")
|
|
33
|
+
totalCount++;
|
|
34
|
+
}
|
|
35
|
+
// Check risk mitigations
|
|
36
|
+
const riskDetails = verifyRiskMitigation(intent, inspectionResults);
|
|
37
|
+
details.push(...riskDetails);
|
|
38
|
+
for (const rd of riskDetails) {
|
|
39
|
+
if (rd.status === "pass")
|
|
40
|
+
passCount++;
|
|
41
|
+
if (rd.status !== "skip")
|
|
42
|
+
totalCount++;
|
|
43
|
+
}
|
|
44
|
+
// Calculate score
|
|
45
|
+
const score = totalCount > 0 ? Math.round((passCount / totalCount) * 100) : 100;
|
|
46
|
+
const passed = score >= 70; // 70% threshold
|
|
47
|
+
// Generate summary
|
|
48
|
+
const failedCount = details.filter((d) => d.status === "fail").length;
|
|
49
|
+
const warnCount = details.filter((d) => d.status === "warn").length;
|
|
50
|
+
let summary;
|
|
51
|
+
if (passed && failedCount === 0) {
|
|
52
|
+
summary = "모든 검증 기준을 충족했습니다.";
|
|
53
|
+
}
|
|
54
|
+
else if (passed) {
|
|
55
|
+
summary = `대부분 충족했지만 ${warnCount}개 주의 항목이 있습니다.`;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
summary = `${failedCount}개 기준이 미충족되었습니다. 수정이 필요합니다.`;
|
|
59
|
+
}
|
|
60
|
+
return { passed, score, details, summary };
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Verify a single acceptance criterion
|
|
64
|
+
*/
|
|
65
|
+
function verifyAcceptanceCriterion(ac, data) {
|
|
66
|
+
// If not verifiable, skip
|
|
67
|
+
if (!ac.verifiable) {
|
|
68
|
+
return {
|
|
69
|
+
criterion_id: ac.id,
|
|
70
|
+
criterion: ac.description,
|
|
71
|
+
status: "skip",
|
|
72
|
+
message: "수동 검증이 필요합니다"
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// Check if related issues exist
|
|
76
|
+
const relatedIssues = data.issues.filter((issue) => issue.description.includes(ac.description) ||
|
|
77
|
+
ac.description.includes(issue.type));
|
|
78
|
+
if (relatedIssues.length > 0) {
|
|
79
|
+
const critical = relatedIssues.some((i) => i.severity === "critical");
|
|
80
|
+
return {
|
|
81
|
+
criterion_id: ac.id,
|
|
82
|
+
criterion: ac.description,
|
|
83
|
+
status: critical ? "fail" : "warn",
|
|
84
|
+
message: `관련 이슈 ${relatedIssues.length}개 발견: ${relatedIssues[0].description}`
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
// Check if verification passed
|
|
88
|
+
if (data.buildSuccess) {
|
|
89
|
+
return {
|
|
90
|
+
criterion_id: ac.id,
|
|
91
|
+
criterion: ac.description,
|
|
92
|
+
status: "pass",
|
|
93
|
+
message: "검증 통과"
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
criterion_id: ac.id,
|
|
98
|
+
criterion: ac.description,
|
|
99
|
+
status: "warn",
|
|
100
|
+
message: "빌드 실패로 검증 불가"
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Verify scope compliance - check do_not_touch files weren't modified
|
|
105
|
+
*/
|
|
106
|
+
function verifyScopeCompliance(intent, data) {
|
|
107
|
+
const details = [];
|
|
108
|
+
// Check do_not_touch files
|
|
109
|
+
for (const protectedItem of intent.scope.do_not_touch) {
|
|
110
|
+
const modified = data.modifiedFiles.some((f) => f.toLowerCase().includes(protectedItem.toLowerCase()));
|
|
111
|
+
if (modified) {
|
|
112
|
+
details.push({
|
|
113
|
+
criterion_id: "SCOPE-DNT",
|
|
114
|
+
criterion: `"${protectedItem}" 수정 금지`,
|
|
115
|
+
status: "fail",
|
|
116
|
+
message: `보호된 항목 "${protectedItem}"이(가) 수정되었습니다`
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Check exclude items weren't implemented
|
|
121
|
+
for (const excluded of intent.scope.exclude) {
|
|
122
|
+
const implemented = data.implementedFeatures.some((f) => f.toLowerCase().includes(excluded.item.toLowerCase()) ||
|
|
123
|
+
excluded.item.toLowerCase().includes(f.toLowerCase()));
|
|
124
|
+
if (implemented) {
|
|
125
|
+
details.push({
|
|
126
|
+
criterion_id: "SCOPE-EXC",
|
|
127
|
+
criterion: `"${excluded.item}" 제외 범위`,
|
|
128
|
+
status: "warn",
|
|
129
|
+
message: `제외 범위 항목 "${excluded.item}"이(가) 구현된 것 같습니다`
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// If no scope issues, add pass
|
|
134
|
+
if (details.length === 0) {
|
|
135
|
+
details.push({
|
|
136
|
+
criterion_id: "SCOPE-ALL",
|
|
137
|
+
criterion: "범위 준수",
|
|
138
|
+
status: "pass",
|
|
139
|
+
message: "범위 제약을 준수했습니다"
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
return details;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Verify risk mitigations were applied
|
|
146
|
+
*/
|
|
147
|
+
function verifyRiskMitigation(intent, data) {
|
|
148
|
+
const details = [];
|
|
149
|
+
for (const risk of intent.risks) {
|
|
150
|
+
if (risk.impact === "high" && risk.mitigation) {
|
|
151
|
+
// Check if related issues exist for high-impact risks
|
|
152
|
+
const relatedIssues = data.issues.filter((issue) => issue.description.includes(risk.description) ||
|
|
153
|
+
risk.description.includes(issue.type));
|
|
154
|
+
if (relatedIssues.length > 0) {
|
|
155
|
+
details.push({
|
|
156
|
+
criterion_id: risk.id,
|
|
157
|
+
criterion: `리스크 완화: ${risk.description}`,
|
|
158
|
+
status: "warn",
|
|
159
|
+
message: `고위험 항목에 관련 이슈 발견: ${risk.mitigation}`
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
details.push({
|
|
164
|
+
criterion_id: risk.id,
|
|
165
|
+
criterion: `리스크 완화: ${risk.description}`,
|
|
166
|
+
status: "pass",
|
|
167
|
+
message: "리스크 완화 조치 적용됨"
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return details;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Convert one-loop results to InspectionData
|
|
176
|
+
*/
|
|
177
|
+
export function toInspectionData(oneLoopResult) {
|
|
178
|
+
return {
|
|
179
|
+
buildSuccess: oneLoopResult.status === "OK",
|
|
180
|
+
issues: (oneLoopResult.issues || []).map((i) => ({
|
|
181
|
+
type: i.type,
|
|
182
|
+
description: i.message || i.business_risk || "",
|
|
183
|
+
severity: i.type.includes("FAIL") ? "critical" : "minor"
|
|
184
|
+
})),
|
|
185
|
+
modifiedFiles: oneLoopResult.modified_files || [],
|
|
186
|
+
implementedFeatures: oneLoopResult.features || []
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
// ============================================================
|
|
190
|
+
// Integration with inspect_code
|
|
191
|
+
// ============================================================
|
|
192
|
+
/**
|
|
193
|
+
* Run intent verification and update status
|
|
194
|
+
*/
|
|
195
|
+
export async function runIntentVerification(runsDir, runId, inspectionData) {
|
|
196
|
+
// Load intent
|
|
197
|
+
const intent = await loadIntent(runsDir, runId);
|
|
198
|
+
if (!intent) {
|
|
199
|
+
return null; // No intent document
|
|
200
|
+
}
|
|
201
|
+
// Run verification
|
|
202
|
+
const result = verifyAgainstIntent(intent, inspectionData);
|
|
203
|
+
// Update intent status based on result
|
|
204
|
+
if (result.passed && result.score >= 90) {
|
|
205
|
+
await updateIntentStatus(runsDir, runId, "verified");
|
|
206
|
+
}
|
|
207
|
+
else if (result.passed) {
|
|
208
|
+
await updateIntentStatus(runsDir, runId, "implemented");
|
|
209
|
+
}
|
|
210
|
+
return result;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Generate verification summary for PM output
|
|
214
|
+
*/
|
|
215
|
+
export function formatVerificationForPM(result) {
|
|
216
|
+
const failedCriteria = result.details
|
|
217
|
+
.filter((d) => d.status === "fail")
|
|
218
|
+
.map((d) => `${d.criterion_id}: ${d.criterion}`);
|
|
219
|
+
let status;
|
|
220
|
+
if (result.score >= 90) {
|
|
221
|
+
status = "PASS";
|
|
222
|
+
}
|
|
223
|
+
else if (result.passed) {
|
|
224
|
+
status = "WARN";
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
status = "FAIL";
|
|
228
|
+
}
|
|
229
|
+
return {
|
|
230
|
+
intent_check: {
|
|
231
|
+
status,
|
|
232
|
+
score: result.score,
|
|
233
|
+
summary: result.summary,
|
|
234
|
+
failed_criteria: failedCriteria
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// adapters/mcp-ts/src/tools/vibe_pm/kce/doc_usage.ts
|
|
2
|
+
// Internal: run evidence for deterministic doc usage (touched/injected).
|
|
3
|
+
import * as fs from "node:fs";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
import { resolveRunDir } from "../context.js";
|
|
6
|
+
function normalizeRelPosix(p) {
|
|
7
|
+
return (p ?? "").trim().replace(/\\\\/g, "/");
|
|
8
|
+
}
|
|
9
|
+
function tryReadJson(p) {
|
|
10
|
+
try {
|
|
11
|
+
if (!fs.existsSync(p))
|
|
12
|
+
return null;
|
|
13
|
+
return JSON.parse(fs.readFileSync(p, "utf-8"));
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function uniqSorted(items) {
|
|
20
|
+
return Array.from(new Set(items.map(normalizeRelPosix).filter(Boolean))).sort();
|
|
21
|
+
}
|
|
22
|
+
export function kceDocUsagePath(run_id, basePath) {
|
|
23
|
+
const resolved = resolveRunDir(run_id, basePath);
|
|
24
|
+
if (!resolved)
|
|
25
|
+
return null;
|
|
26
|
+
return path.join(resolved.run_dir, "kce", "kce_doc_usage.json");
|
|
27
|
+
}
|
|
28
|
+
export function updateKceDocUsage(args) {
|
|
29
|
+
const p = kceDocUsagePath(args.run_id, args.basePath);
|
|
30
|
+
if (!p)
|
|
31
|
+
return;
|
|
32
|
+
const existing = tryReadJson(p);
|
|
33
|
+
const touched = uniqSorted([...(existing?.touched_docs ?? []), ...(args.touched_docs ?? [])]);
|
|
34
|
+
const injected = uniqSorted([...(existing?.injected_docs ?? []), ...(args.injected_docs ?? [])]);
|
|
35
|
+
const entities = uniqSorted([...(existing?.referenced_entities ?? []), ...(args.referenced_entities ?? [])]);
|
|
36
|
+
const out = {
|
|
37
|
+
version: "kce_doc_usage.v1",
|
|
38
|
+
created_at: new Date().toISOString(),
|
|
39
|
+
run_id: args.run_id,
|
|
40
|
+
touched_docs: touched,
|
|
41
|
+
injected_docs: injected,
|
|
42
|
+
referenced_entities: entities
|
|
43
|
+
};
|
|
44
|
+
try {
|
|
45
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
46
|
+
fs.writeFileSync(p, JSON.stringify(out, null, 2) + "\n", "utf-8");
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// ignore (best-effort evidence)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// adapters/mcp-ts/src/tools/vibe_pm/kce/on_finalize.ts
|
|
2
|
+
// Internal: best-effort KCE sync after finalize_work (keeps KCE warm).
|
|
3
|
+
import { runEngine } from "../../../engine.js";
|
|
4
|
+
export async function kickoffKceSyncBestEffort() {
|
|
5
|
+
try {
|
|
6
|
+
await runEngine("vibecoding-helper", ["kce-sync", "--prune", "--skip-missing-roots"], { timeoutMs: 300_000 });
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
// ignore
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
// adapters/mcp-ts/src/tools/vibe_pm/kce/preflight.ts
|
|
2
|
+
// Internal: KCE preflight (FTS + Chroma readiness) with auto-heal + 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 KceStatusSchema = z.object({
|
|
10
|
+
version: z.literal("kce_status.v1"),
|
|
11
|
+
readiness_state: z.enum(["READY", "HEALING", "FAILED"]),
|
|
12
|
+
roots_snapshot_hash: z.string().min(1),
|
|
13
|
+
missing_roots: z.array(z.string()).optional(),
|
|
14
|
+
last_indexed_at: z.string().optional(),
|
|
15
|
+
fts: z.object({
|
|
16
|
+
ok: z.boolean(),
|
|
17
|
+
db_path: z.string().min(1),
|
|
18
|
+
doc_count: z.number().int().nonnegative(),
|
|
19
|
+
chunk_count: z.number().int().nonnegative()
|
|
20
|
+
}),
|
|
21
|
+
chroma: z.object({
|
|
22
|
+
ok: z.boolean(),
|
|
23
|
+
persist_dir: z.string().min(1),
|
|
24
|
+
collection: z.string().min(1),
|
|
25
|
+
chunk_count: z.number().int().nonnegative(),
|
|
26
|
+
manifest_path: z.string().min(1)
|
|
27
|
+
}),
|
|
28
|
+
fingerprints: z.object({
|
|
29
|
+
embedding: z.string().min(1),
|
|
30
|
+
chunking: z.string().min(1),
|
|
31
|
+
policy: z.string().min(1)
|
|
32
|
+
}),
|
|
33
|
+
issues: z.array(z.string()),
|
|
34
|
+
next_action: z
|
|
35
|
+
.object({
|
|
36
|
+
tool: z.string().min(1),
|
|
37
|
+
reason: z.string().min(1)
|
|
38
|
+
})
|
|
39
|
+
.nullable()
|
|
40
|
+
.optional()
|
|
41
|
+
});
|
|
42
|
+
const KceRetrieveOutputSchema = z.object({
|
|
43
|
+
version: z.literal("kce_retrieve.v1"),
|
|
44
|
+
query: z.string(),
|
|
45
|
+
hits: z.array(z.object({
|
|
46
|
+
path: z.string(),
|
|
47
|
+
abs_path: z.string().optional().default(""),
|
|
48
|
+
start_line: z.number().int().optional().default(1),
|
|
49
|
+
end_line: z.number().int().optional().default(1),
|
|
50
|
+
section_title: z.string().optional().default(""),
|
|
51
|
+
content: z.string().optional().default(""),
|
|
52
|
+
score: z.number().optional().default(0),
|
|
53
|
+
backend: z.string().optional().default("unknown")
|
|
54
|
+
})),
|
|
55
|
+
context_block: z.string()
|
|
56
|
+
});
|
|
57
|
+
const inFlight = new Map();
|
|
58
|
+
function inFlightKey(args) {
|
|
59
|
+
return `${args.basePath}::${args.run_id ?? "no_run"}::${args.purpose}`;
|
|
60
|
+
}
|
|
61
|
+
function tryWriteJson(pathAbs, doc) {
|
|
62
|
+
try {
|
|
63
|
+
fs.mkdirSync(path.dirname(pathAbs), { recursive: true });
|
|
64
|
+
fs.writeFileSync(pathAbs, JSON.stringify(doc, null, 2) + "\n", "utf-8");
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// ignore (best-effort evidence)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function tryAppendJsonl(pathAbs, record) {
|
|
71
|
+
try {
|
|
72
|
+
fs.mkdirSync(path.dirname(pathAbs), { recursive: true });
|
|
73
|
+
fs.appendFileSync(pathAbs, JSON.stringify(record) + "\n", "utf-8");
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// ignore (best-effort evidence)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export function kceEvidenceDir(run_id, basePath) {
|
|
80
|
+
const resolved = resolveRunDir(run_id, basePath);
|
|
81
|
+
if (!resolved)
|
|
82
|
+
return null;
|
|
83
|
+
return path.join(resolved.run_dir, "kce");
|
|
84
|
+
}
|
|
85
|
+
function kceEventsPath(run_id, basePath) {
|
|
86
|
+
const dir = kceEvidenceDir(run_id, basePath);
|
|
87
|
+
if (!dir)
|
|
88
|
+
return null;
|
|
89
|
+
return path.join(dir, "kce_events.jsonl");
|
|
90
|
+
}
|
|
91
|
+
export async function runKceStatus() {
|
|
92
|
+
const { code, stdout, stderr } = await runEngine("vibecoding-helper", ["kce-status"], { timeoutMs: 60_000 });
|
|
93
|
+
if (code !== 0) {
|
|
94
|
+
throw new Error(`kce-status failed: ${stderr || `exit_code=${code}`}`);
|
|
95
|
+
}
|
|
96
|
+
const parsed = safeJsonParse(stdout);
|
|
97
|
+
if (!parsed.ok)
|
|
98
|
+
throw new Error(`kce-status invalid_json: ${parsed.error}`);
|
|
99
|
+
const validated = KceStatusSchema.safeParse(parsed.value);
|
|
100
|
+
if (!validated.success)
|
|
101
|
+
throw new Error("kce-status schema mismatch");
|
|
102
|
+
return validated.data;
|
|
103
|
+
}
|
|
104
|
+
export async function runKceSync(args) {
|
|
105
|
+
const cmd = ["kce-sync"];
|
|
106
|
+
if (args.prune)
|
|
107
|
+
cmd.push("--prune");
|
|
108
|
+
if (args.skipMissingRoots)
|
|
109
|
+
cmd.push("--skip-missing-roots");
|
|
110
|
+
if (args.forceFull)
|
|
111
|
+
cmd.push("--force-full");
|
|
112
|
+
const { code, stdout, stderr } = await runEngine("vibecoding-helper", cmd, {
|
|
113
|
+
timeoutMs: Math.max(60_000, args.timeoutMs ?? 300_000)
|
|
114
|
+
});
|
|
115
|
+
if (code !== 0) {
|
|
116
|
+
throw new Error(`kce-sync failed: ${stderr || `exit_code=${code}`}`);
|
|
117
|
+
}
|
|
118
|
+
const parsed = safeJsonParse(stdout);
|
|
119
|
+
if (!parsed.ok)
|
|
120
|
+
throw new Error(`kce-sync invalid_json: ${parsed.error}`);
|
|
121
|
+
const validated = KceStatusSchema.safeParse(parsed.value);
|
|
122
|
+
if (!validated.success)
|
|
123
|
+
throw new Error("kce-sync schema mismatch");
|
|
124
|
+
return validated.data;
|
|
125
|
+
}
|
|
126
|
+
export async function runKceRetrieve(args) {
|
|
127
|
+
const cmd = [
|
|
128
|
+
"kce-retrieve",
|
|
129
|
+
"--query",
|
|
130
|
+
args.query,
|
|
131
|
+
"--limit",
|
|
132
|
+
String(Math.max(1, Math.min(20, args.limit ?? 10))),
|
|
133
|
+
"--max-items",
|
|
134
|
+
String(Math.max(1, Math.min(10, args.maxItems ?? 6))),
|
|
135
|
+
"--max-total-chars",
|
|
136
|
+
String(Math.max(500, Math.min(20_000, args.maxTotalChars ?? 6000))),
|
|
137
|
+
"--max-chunk-chars",
|
|
138
|
+
String(Math.max(200, Math.min(4000, args.maxChunkChars ?? 1200)))
|
|
139
|
+
];
|
|
140
|
+
if (args.pathPrefix)
|
|
141
|
+
cmd.push("--path-prefix", args.pathPrefix);
|
|
142
|
+
const { code, stdout, stderr } = await runEngine("vibecoding-helper", cmd, { timeoutMs: 60_000 });
|
|
143
|
+
if (code !== 0) {
|
|
144
|
+
throw new Error(`kce-retrieve failed: ${stderr || `exit_code=${code}`}`);
|
|
145
|
+
}
|
|
146
|
+
const parsed = safeJsonParse(stdout);
|
|
147
|
+
if (!parsed.ok)
|
|
148
|
+
throw new Error(`kce-retrieve invalid_json: ${parsed.error}`);
|
|
149
|
+
const validated = KceRetrieveOutputSchema.safeParse(parsed.value);
|
|
150
|
+
if (!validated.success)
|
|
151
|
+
throw new Error("kce-retrieve schema mismatch");
|
|
152
|
+
return validated.data;
|
|
153
|
+
}
|
|
154
|
+
export async function kcePreflight(args) {
|
|
155
|
+
const key = inFlightKey({ basePath: args.basePath, run_id: args.run_id, purpose: args.purpose });
|
|
156
|
+
const existing = inFlight.get(key);
|
|
157
|
+
if (existing)
|
|
158
|
+
return existing;
|
|
159
|
+
const promise = (async () => {
|
|
160
|
+
const eventsPath = args.writeEvidence && args.run_id ? kceEventsPath(args.run_id, args.basePath) : null;
|
|
161
|
+
let loggedOutcome = false;
|
|
162
|
+
try {
|
|
163
|
+
let status = await runKceStatus();
|
|
164
|
+
if (status.readiness_state === "HEALING") {
|
|
165
|
+
const needsBootstrap = Array.isArray(status.issues) && status.issues.some((i) => typeof i === "string" && i.startsWith("chroma_unavailable"));
|
|
166
|
+
const syncTimeoutMs = needsBootstrap ? 900_000 : 300_000;
|
|
167
|
+
if (eventsPath) {
|
|
168
|
+
tryAppendJsonl(eventsPath, {
|
|
169
|
+
ts: new Date().toISOString(),
|
|
170
|
+
event: "KCE_SYNC",
|
|
171
|
+
phase: "start",
|
|
172
|
+
prune: true,
|
|
173
|
+
skip_missing_roots: true,
|
|
174
|
+
force_full: false,
|
|
175
|
+
timeout_ms: syncTimeoutMs
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
status = await runKceSync({
|
|
179
|
+
prune: true,
|
|
180
|
+
skipMissingRoots: true,
|
|
181
|
+
forceFull: false,
|
|
182
|
+
timeoutMs: syncTimeoutMs
|
|
183
|
+
});
|
|
184
|
+
if (eventsPath) {
|
|
185
|
+
tryAppendJsonl(eventsPath, {
|
|
186
|
+
ts: new Date().toISOString(),
|
|
187
|
+
event: "KCE_SYNC",
|
|
188
|
+
phase: "finish",
|
|
189
|
+
readiness_state: status.readiness_state,
|
|
190
|
+
issues: status.issues ?? []
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (args.writeEvidence && args.run_id) {
|
|
195
|
+
const dir = kceEvidenceDir(args.run_id, args.basePath);
|
|
196
|
+
if (dir) {
|
|
197
|
+
tryWriteJson(path.join(dir, "kce_status.json"), status);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (eventsPath) {
|
|
201
|
+
tryAppendJsonl(eventsPath, {
|
|
202
|
+
ts: new Date().toISOString(),
|
|
203
|
+
event: "KCE_PREFLIGHT",
|
|
204
|
+
purpose: args.purpose,
|
|
205
|
+
readiness_state: status.readiness_state,
|
|
206
|
+
issues: status.issues ?? []
|
|
207
|
+
});
|
|
208
|
+
loggedOutcome = true;
|
|
209
|
+
}
|
|
210
|
+
if (status.readiness_state !== "READY") {
|
|
211
|
+
const why = status.issues?.slice(0, 3).join("; ") || "unknown";
|
|
212
|
+
throw new Error(`KCE_NOT_READY: ${why}`);
|
|
213
|
+
}
|
|
214
|
+
return status;
|
|
215
|
+
}
|
|
216
|
+
catch (e) {
|
|
217
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
218
|
+
if (eventsPath && !loggedOutcome) {
|
|
219
|
+
tryAppendJsonl(eventsPath, {
|
|
220
|
+
ts: new Date().toISOString(),
|
|
221
|
+
event: "KCE_PREFLIGHT",
|
|
222
|
+
purpose: args.purpose,
|
|
223
|
+
error: msg
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
throw e;
|
|
227
|
+
}
|
|
228
|
+
})();
|
|
229
|
+
inFlight.set(key, promise);
|
|
230
|
+
promise.finally(() => inFlight.delete(key)).catch(() => { });
|
|
231
|
+
return promise;
|
|
232
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// adapters/mcp-ts/src/tools/vibe_pm/local_memory.ts
|
|
2
|
+
// Internal helper: best-effort Local Memory reference injection (client-agnostic)
|
|
3
|
+
import { runKceRetrieve, runKceStatus } from "./kce/preflight.js";
|
|
4
|
+
export async function maybeGetLocalMemoryContextBlock(args) {
|
|
5
|
+
const query = (args.query ?? "").trim();
|
|
6
|
+
if (!query)
|
|
7
|
+
return null;
|
|
8
|
+
// Avoid expensive fallback scans: only inject when KCE is READY.
|
|
9
|
+
try {
|
|
10
|
+
const status = await runKceStatus();
|
|
11
|
+
if (status.readiness_state !== "READY")
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const maxItems = Math.max(1, Math.min(5, args.maxItems ?? 3));
|
|
19
|
+
const out = await runKceRetrieve({ query, maxItems, limit: 10, maxTotalChars: 4000, maxChunkChars: 900 });
|
|
20
|
+
const block = out.context_block.trim();
|
|
21
|
+
return block ? block : null;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// adapters/mcp-ts/src/tools/vibe_pm/memory_status.ts
|
|
2
|
+
// vibe_pm.memory_status - Local Memory status (ChromaDB index readiness)
|
|
3
|
+
import { runEngine } from "../../engine.js";
|
|
4
|
+
import { safeJsonParse } from "../../cli.js";
|
|
5
|
+
import { validateToolInput } from "../../security/input-validator.js";
|
|
6
|
+
import { resolveProjectId } from "./context.js";
|
|
7
|
+
import { MemoryStatusOutputSchema } from "../../generated/memory_status_output.js";
|
|
8
|
+
function isOfflineMode() {
|
|
9
|
+
const v = (process.env.VIBECODE_OFFLINE ?? "").trim().toLowerCase();
|
|
10
|
+
return v === "1" || v === "true" || v === "yes" || v === "on";
|
|
11
|
+
}
|
|
12
|
+
function defaultPersistDir() {
|
|
13
|
+
return ".vibe/chroma";
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* vibe_pm.memory_status - Local Memory readiness check
|
|
17
|
+
*
|
|
18
|
+
* Internal mapping:
|
|
19
|
+
* → vibecoding-helper memory-status --project-id ... --docs-root ... --persist-dir ...
|
|
20
|
+
*/
|
|
21
|
+
export async function memoryStatus(input) {
|
|
22
|
+
const basePath = process.cwd();
|
|
23
|
+
validateToolInput({
|
|
24
|
+
project_id: input.project_id,
|
|
25
|
+
custom_input: input.docs_root,
|
|
26
|
+
});
|
|
27
|
+
const project_id = input.project_id ?? resolveProjectId(undefined, basePath);
|
|
28
|
+
const docsRoot = (input.docs_root ?? "docs").trim();
|
|
29
|
+
const persistDir = (input.persist_dir ?? defaultPersistDir()).trim();
|
|
30
|
+
try {
|
|
31
|
+
const args = ["memory-status", "--project-id", project_id, "--docs-root", docsRoot, "--persist-dir", persistDir];
|
|
32
|
+
const { code, stdout, stderr } = await runEngine("vibecoding-helper", args, { timeoutMs: 60_000 });
|
|
33
|
+
if (code !== 0) {
|
|
34
|
+
return MemoryStatusOutputSchema.parse({
|
|
35
|
+
project_id,
|
|
36
|
+
status: "ERROR",
|
|
37
|
+
summary: "Local Memory 상태 확인에 실패했습니다.",
|
|
38
|
+
docs_root: docsRoot,
|
|
39
|
+
persist_dir: persistDir,
|
|
40
|
+
collection: "vibe_docs",
|
|
41
|
+
stats: { document_count: 0, chunk_count: 0, last_sync_at: null },
|
|
42
|
+
embedding: { backend: "unknown", model: null, ready: false },
|
|
43
|
+
offline_mode: isOfflineMode(),
|
|
44
|
+
issues: [stderr?.trim() ? `engine_error: ${stderr.trim()}` : `engine_exit_code: ${code}`],
|
|
45
|
+
next_action: { tool: "vibe_pm.doctor", reason: "설치/환경 상태를 점검하겠습니다." }
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
const parsed = safeJsonParse(stdout);
|
|
49
|
+
if (!parsed.ok) {
|
|
50
|
+
return MemoryStatusOutputSchema.parse({
|
|
51
|
+
project_id,
|
|
52
|
+
status: "ERROR",
|
|
53
|
+
summary: "Local Memory 상태 응답을 해석할 수 없습니다.",
|
|
54
|
+
docs_root: docsRoot,
|
|
55
|
+
persist_dir: persistDir,
|
|
56
|
+
collection: "vibe_docs",
|
|
57
|
+
stats: { document_count: 0, chunk_count: 0, last_sync_at: null },
|
|
58
|
+
embedding: { backend: "unknown", model: null, ready: false },
|
|
59
|
+
offline_mode: isOfflineMode(),
|
|
60
|
+
issues: [`invalid_json: ${parsed.error}`],
|
|
61
|
+
next_action: { tool: "vibe_pm.doctor", reason: "설치/환경 상태를 점검하겠습니다." }
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return MemoryStatusOutputSchema.parse(parsed.value);
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
68
|
+
return MemoryStatusOutputSchema.parse({
|
|
69
|
+
project_id,
|
|
70
|
+
status: "ERROR",
|
|
71
|
+
summary: "Local Memory 상태 확인 중 오류가 발생했습니다.",
|
|
72
|
+
docs_root: docsRoot,
|
|
73
|
+
persist_dir: persistDir,
|
|
74
|
+
collection: "vibe_docs",
|
|
75
|
+
stats: { document_count: 0, chunk_count: 0, last_sync_at: null },
|
|
76
|
+
embedding: { backend: "unknown", model: null, ready: false },
|
|
77
|
+
offline_mode: isOfflineMode(),
|
|
78
|
+
issues: [msg],
|
|
79
|
+
next_action: { tool: "vibe_pm.doctor", reason: "설치/환경 상태를 점검하겠습니다." }
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|