@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,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
+ }