@vibecodetown/mcp-server 2.1.4 → 2.2.1

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 (80) hide show
  1. package/README.md +10 -10
  2. package/build/auth/credential_store.js +146 -0
  3. package/build/auth/public_key.js +6 -4
  4. package/build/bootstrap/doctor.js +113 -5
  5. package/build/bootstrap/installer.js +85 -15
  6. package/build/bootstrap/registry.js +11 -6
  7. package/build/bootstrap/skills-installer.js +365 -0
  8. package/build/control_plane/gate.js +52 -70
  9. package/build/dx/activity.js +26 -3
  10. package/build/engine.js +151 -0
  11. package/build/errors.js +107 -0
  12. package/build/generated/bridge_build_seed_input.js +2 -0
  13. package/build/generated/bridge_build_seed_output.js +2 -0
  14. package/build/generated/bridge_confirm_reference_input.js +2 -0
  15. package/build/generated/bridge_confirm_reference_output.js +2 -0
  16. package/build/generated/bridge_confirmed_reference_file.js +2 -0
  17. package/build/generated/bridge_generate_references_input.js +2 -0
  18. package/build/generated/bridge_generate_references_output.js +2 -0
  19. package/build/generated/bridge_references_file.js +2 -0
  20. package/build/generated/bridge_work_order_seed_file.js +2 -0
  21. package/build/generated/contracts_bundle_info.js +3 -3
  22. package/build/generated/index.js +14 -0
  23. package/build/generated/ingress_input.js +2 -0
  24. package/build/generated/ingress_output.js +2 -0
  25. package/build/generated/ingress_resolution_file.js +2 -0
  26. package/build/generated/ingress_summary_file.js +2 -0
  27. package/build/generated/message_template_id_mapping_file.js +2 -0
  28. package/build/generated/run_app_input.js +1 -1
  29. package/build/index.js +4 -1
  30. package/build/local-mode/git.js +36 -22
  31. package/build/local-mode/paths.js +1 -0
  32. package/build/local-mode/project-state.js +176 -0
  33. package/build/local-mode/setup.js +21 -1
  34. package/build/local-mode/templates.js +3 -3
  35. package/build/path-utils.js +68 -0
  36. package/build/runtime/cli_invoker.js +416 -0
  37. package/build/tools/vibe_pm/advisory_review.js +5 -3
  38. package/build/tools/vibe_pm/bridge_build_seed.js +164 -0
  39. package/build/tools/vibe_pm/bridge_confirm_reference.js +91 -0
  40. package/build/tools/vibe_pm/bridge_generate_references.js +258 -0
  41. package/build/tools/vibe_pm/briefing.js +26 -1
  42. package/build/tools/vibe_pm/context.js +79 -0
  43. package/build/tools/vibe_pm/create_work_order.js +200 -3
  44. package/build/tools/vibe_pm/doctor.js +95 -0
  45. package/build/tools/vibe_pm/entity_gate/preflight.js +8 -3
  46. package/build/tools/vibe_pm/export_output.js +14 -13
  47. package/build/tools/vibe_pm/finalize_work.js +74 -0
  48. package/build/tools/vibe_pm/force_override.js +104 -0
  49. package/build/tools/vibe_pm/get_decision.js +2 -2
  50. package/build/tools/vibe_pm/index.js +160 -3
  51. package/build/tools/vibe_pm/ingress.js +645 -0
  52. package/build/tools/vibe_pm/ingress_gate.js +116 -0
  53. package/build/tools/vibe_pm/inspect_code.js +90 -20
  54. package/build/tools/vibe_pm/kce/doc_usage.js +4 -9
  55. package/build/tools/vibe_pm/kce/on_finalize.js +2 -2
  56. package/build/tools/vibe_pm/kce/preflight.js +11 -7
  57. package/build/tools/vibe_pm/list_rules.js +135 -0
  58. package/build/tools/vibe_pm/memory_status.js +11 -8
  59. package/build/tools/vibe_pm/memory_sync.js +11 -8
  60. package/build/tools/vibe_pm/pm_language.js +17 -16
  61. package/build/tools/vibe_pm/pre_commit_analysis.js +292 -0
  62. package/build/tools/vibe_pm/publish_mcp.js +271 -0
  63. package/build/tools/vibe_pm/python_error.js +115 -0
  64. package/build/tools/vibe_pm/run_app.js +215 -86
  65. package/build/tools/vibe_pm/run_app_podman.js +64 -2
  66. package/build/tools/vibe_pm/save_rule.js +120 -0
  67. package/build/tools/vibe_pm/search_oss.js +5 -3
  68. package/build/tools/vibe_pm/spec_rag.js +185 -0
  69. package/build/tools/vibe_pm/status.js +50 -3
  70. package/build/tools/vibe_pm/submit_decision.js +2 -2
  71. package/build/tools/vibe_pm/types.js +28 -0
  72. package/build/tools/vibe_pm/undo_last_task.js +23 -20
  73. package/build/tools/vibe_pm/waiter_mapping.js +155 -0
  74. package/build/tools/vibe_pm/zoekt_evidence.js +5 -3
  75. package/build/tools.js +13 -5
  76. package/build/version-check.js +5 -5
  77. package/build/vibe-cli.js +742 -39
  78. package/package.json +5 -4
  79. package/skills/VRIP_INSTALL_MANIFEST_DOCTOR.skill.md +288 -0
  80. package/skills/index.json +14 -0
@@ -10,6 +10,60 @@ import { runDocStatusTriage } from "./doc_status_gate.js";
10
10
  import { kickoffKceSyncBestEffort } from "./kce/on_finalize.js";
11
11
  import { updateVersionFile } from "../../version-check.js";
12
12
  import { CONTRACTS_VERSION, CONTRACTS_BUNDLE_SHA256 } from "../../generated/contracts_bundle_info.js";
13
+ import { resolveRunId } from "./context.js";
14
+ /**
15
+ * Detect if a file is a spec/doc file
16
+ */
17
+ function isSpecFile(filePath) {
18
+ const normalized = filePath.replace(/\\/g, "/");
19
+ return (normalized === "docs/SSOT.md" ||
20
+ normalized === "docs/CURRENT_SPEC.md" ||
21
+ normalized.startsWith("docs/ssot/") ||
22
+ normalized.startsWith("docs/specs/") ||
23
+ normalized.startsWith("docs/dev_logs/") ||
24
+ normalized.startsWith("docs/planning/"));
25
+ }
26
+ /**
27
+ * Detect if a file is a code file
28
+ */
29
+ function isCodeFile(filePath) {
30
+ const normalized = filePath.replace(/\\/g, "/");
31
+ const codePatterns = [
32
+ /^src\//,
33
+ /^lib\//,
34
+ /^tests?\//,
35
+ /^scripts\//,
36
+ /^adapters\//,
37
+ /^engines\//,
38
+ /^vibecoding_helper\//,
39
+ /\.ts$/,
40
+ /\.tsx$/,
41
+ /\.js$/,
42
+ /\.jsx$/,
43
+ /\.py$/,
44
+ /\.rs$/,
45
+ /\.go$/
46
+ ];
47
+ return codePatterns.some((p) => p.test(normalized));
48
+ }
49
+ /**
50
+ * Write spec update evidence file for audit trail
51
+ */
52
+ function writeSpecUpdateEvidence(basePath, runId, evidence) {
53
+ try {
54
+ const specDir = path.join(basePath, "engines", "spec_high", "runs", runId, "spec");
55
+ fs.mkdirSync(specDir, { recursive: true });
56
+ const evidencePath = path.join(specDir, "spec_update.json");
57
+ const fullEvidence = {
58
+ version: "spec_update_evidence.v1",
59
+ ...evidence
60
+ };
61
+ fs.writeFileSync(evidencePath, JSON.stringify(fullEvidence, null, 2) + "\n", "utf-8");
62
+ }
63
+ catch {
64
+ // Best-effort: do not block finalize_work
65
+ }
66
+ }
13
67
  /**
14
68
  * vibe_pm.finalize_work - Complete work with documentation and Git
15
69
  *
@@ -234,6 +288,26 @@ export async function finalizeWork(input, basePath = process.cwd()) {
234
288
  // Git not available / not a repo / unexpected error: keep docs work, but surface warning
235
289
  warning = appendWarning(warning, `Git 처리 실패: ${err instanceof Error ? err.message : String(err)}`);
236
290
  }
291
+ // SRWP: Write spec update evidence (best-effort audit trail)
292
+ try {
293
+ const { run_id } = resolveRunId(undefined, basePath);
294
+ const specFiles = updatedFiles.filter((f) => isSpecFile(f));
295
+ const codeFiles = updatedFiles.filter((f) => isCodeFile(f));
296
+ if (specFiles.length > 0 || codeFiles.length > 0) {
297
+ writeSpecUpdateEvidence(basePath, run_id, {
298
+ run_id,
299
+ timestamp: new Date().toISOString(),
300
+ updated_spec_files: specFiles,
301
+ updated_code_files: codeFiles,
302
+ reason: "FinalizeWork",
303
+ commit_subject: input.git_commit.subject,
304
+ diff_summary: input.task_summary
305
+ });
306
+ }
307
+ }
308
+ catch {
309
+ // Best-effort: do not block finalize_work
310
+ }
237
311
  // Best-effort: KCE sync (do not block finalize_work)
238
312
  if (shouldKickoffAutoMemorySync(basePath)) {
239
313
  void kickoffKceSyncBestEffort();
@@ -0,0 +1,104 @@
1
+ // adapters/mcp-ts/src/tools/vibe_pm/force_override.ts
2
+ // vibe_pm.force_override - Emergency escape hatch for blocked operations
3
+ // P2-1: 탈출 경로 마련
4
+ import * as fs from "node:fs";
5
+ import * as path from "node:path";
6
+ import { z } from "zod";
7
+ // ============================================================
8
+ // Schemas
9
+ // ============================================================
10
+ export const forceOverrideInputSchema = z.object({
11
+ action: z.string().describe("차단된 작업 설명 (예: 'npm run build')"),
12
+ reason: z.string().describe("강제 실행이 필요한 이유"),
13
+ acknowledge_risks: z.literal(true).describe("위험을 인지했음을 확인 (true 필수)"),
14
+ });
15
+ export const forceOverrideOutputSchema = z.object({
16
+ success: z.boolean(),
17
+ override_id: z.string().describe("강제 실행 ID (감사 로그용)"),
18
+ action: z.string(),
19
+ reason: z.string(),
20
+ timestamp: z.string(),
21
+ audit_log_path: z.string().optional(),
22
+ warning: z.string(),
23
+ next_action: z.object({
24
+ tool: z.string(),
25
+ reason: z.string(),
26
+ }),
27
+ });
28
+ function generateOverrideId() {
29
+ const now = new Date();
30
+ const ts = now.toISOString().replace(/[-:T.]/g, "").slice(0, 15);
31
+ const rand = Math.random().toString(36).slice(2, 8);
32
+ return `FORCE_${ts}_${rand}`;
33
+ }
34
+ function writeAuditLog(basePath, entry) {
35
+ try {
36
+ const auditDir = path.join(basePath, ".vibe", "audit");
37
+ fs.mkdirSync(auditDir, { recursive: true });
38
+ const logFile = path.join(auditDir, "force_override.jsonl");
39
+ const line = JSON.stringify(entry) + "\n";
40
+ fs.appendFileSync(logFile, line, "utf-8");
41
+ return logFile;
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ }
47
+ // ============================================================
48
+ // Tool Implementation
49
+ // ============================================================
50
+ /**
51
+ * vibe_pm.force_override
52
+ *
53
+ * Emergency escape hatch for blocked operations.
54
+ * Requires explicit acknowledgment of risks.
55
+ * All uses are logged for audit purposes.
56
+ *
57
+ * This does NOT actually execute any command - it only:
58
+ * 1. Validates the acknowledgment
59
+ * 2. Creates an audit log entry
60
+ * 3. Returns an override ID that can be used to bypass gates
61
+ *
62
+ * The AI agent can then use this override_id when re-attempting
63
+ * the blocked operation.
64
+ */
65
+ export async function forceOverride(input) {
66
+ const basePath = process.cwd();
67
+ // Validate input (fail-closed)
68
+ const parsed = forceOverrideInputSchema.parse(input);
69
+ // acknowledge_risks must be true (enforced by schema, but double-check)
70
+ if (parsed.acknowledge_risks !== true) {
71
+ throw new Error("acknowledge_risks must be true to use force_override");
72
+ }
73
+ // Generate override ID
74
+ const overrideId = generateOverrideId();
75
+ const timestamp = new Date().toISOString();
76
+ // Create audit log entry
77
+ const auditEntry = {
78
+ override_id: overrideId,
79
+ timestamp,
80
+ action: parsed.action,
81
+ reason: parsed.reason,
82
+ user_acknowledged_risks: true,
83
+ cwd: basePath,
84
+ env: {
85
+ user: process.env.USER ?? process.env.USERNAME,
86
+ hostname: process.env.HOSTNAME ?? process.env.COMPUTERNAME,
87
+ },
88
+ };
89
+ // Write audit log
90
+ const auditLogPath = writeAuditLog(basePath, auditEntry);
91
+ return {
92
+ success: true,
93
+ override_id: overrideId,
94
+ action: parsed.action,
95
+ reason: parsed.reason,
96
+ timestamp,
97
+ audit_log_path: auditLogPath ?? undefined,
98
+ warning: "⚠ 강제 실행이 기록되었습니다. 이 작업은 감사 로그에 저장됩니다.",
99
+ next_action: {
100
+ tool: "vibe_pm.run_app",
101
+ reason: "override_id를 사용해 차단된 작업을 다시 시도할 수 있습니다.",
102
+ },
103
+ };
104
+ }
@@ -2,7 +2,7 @@
2
2
  // vibe_pm.get_decision - Fetch approval items
3
3
  import * as fs from "node:fs";
4
4
  import * as path from "node:path";
5
- import { runEngineWithCache } from "../../engine.js";
5
+ import { runPythonCliWithCache } from "../../engine.js";
6
6
  import { safeJsonParse } from "../../cli.js";
7
7
  import { getRunsDir, resolveRunId, resolveProjectId, resolveSpecHighRunDir } from "./context.js";
8
8
  import { transformAskQueueText } from "./pm_language.js";
@@ -42,7 +42,7 @@ export async function getDecision(input, basePath = process.cwd()) {
42
42
  }
43
43
  else {
44
44
  // Fallback: run show-ask-queue command (with caching)
45
- const { code, stdout } = await runEngineWithCache("vibecoding-helper", ["show-ask-queue", run_id], { timeoutMs: 60_000 });
45
+ const { code, stdout } = await runPythonCliWithCache(["show-ask-queue", run_id], { timeoutMs: 60_000 });
46
46
  if (code === 0) {
47
47
  // Parse response
48
48
  const parsed = safeJsonParse(stdout);
@@ -1,7 +1,7 @@
1
1
  // adapters/mcp-ts/src/tools/vibe_pm/index.ts
2
2
  // vibe_pm.* MCP tools registration and export
3
3
  import { McpError } from "@modelcontextprotocol/sdk/types.js";
4
- import { briefingInputSchema, briefingOutputSchema, getDecisionInputSchema, getDecisionOutputSchema, submitDecisionInputSchema, submitDecisionOutputSchema, createWorkOrderInputSchema, createWorkOrderOutputSchema, inspectCodeInputSchema, inspectCodeOutputSchema, statusInputSchema, statusOutputSchema, memoryStatusInputSchema, memoryStatusOutputSchema, memorySyncInputSchema, memorySyncOutputSchema, advisoryReviewInputSchema, advisoryReviewOutputSchema, activateInputSchema, activateOutputSchema, repairPlanInputSchema, repairPlanOutputSchema, scaffoldInputSchema, scaffoldOutputSchema, finalizeWorkInputSchema, finalizeWorkOutputSchema, undoLastTaskInputSchema, undoLastTaskOutputSchema, runAppInputSchema, runAppOutputSchema, exportOutputInputSchema, exportOutputOutputSchema, searchOssInputSchema, searchOssOutputSchema, zoektEvidenceInputSchema, zoektEvidenceOutputSchema, doctorOutputSchema, updateOutputSchema } from "./types.js";
4
+ import { briefingInputSchema, briefingOutputSchema, getDecisionInputSchema, getDecisionOutputSchema, submitDecisionInputSchema, submitDecisionOutputSchema, createWorkOrderInputSchema, createWorkOrderOutputSchema, inspectCodeInputSchema, inspectCodeOutputSchema, statusInputSchema, statusOutputSchema, memoryStatusInputSchema, memoryStatusOutputSchema, memorySyncInputSchema, memorySyncOutputSchema, advisoryReviewInputSchema, advisoryReviewOutputSchema, activateInputSchema, activateOutputSchema, repairPlanInputSchema, repairPlanOutputSchema, scaffoldInputSchema, scaffoldOutputSchema, finalizeWorkInputSchema, finalizeWorkOutputSchema, undoLastTaskInputSchema, undoLastTaskOutputSchema, runAppInputSchema, runAppOutputSchema, exportOutputInputSchema, exportOutputOutputSchema, searchOssInputSchema, searchOssOutputSchema, zoektEvidenceInputSchema, zoektEvidenceOutputSchema, ingressInputSchema, ingressOutputSchema, bridgeGenerateReferencesInputSchema, bridgeGenerateReferencesOutputSchema, bridgeConfirmReferenceInputSchema, bridgeConfirmReferenceOutputSchema, bridgeBuildSeedInputSchema, bridgeBuildSeedOutputSchema, doctorOutputSchema, updateOutputSchema } from "./types.js";
5
5
  import { briefing } from "./briefing.js";
6
6
  import { getDecision } from "./get_decision.js";
7
7
  import { submitDecision } from "./submit_decision.js";
@@ -22,10 +22,17 @@ import { runApp } from "./run_app.js";
22
22
  import { exportOutput } from "./export_output.js";
23
23
  import { searchOss } from "./search_oss.js";
24
24
  import { zoektEvidence } from "./zoekt_evidence.js";
25
+ import { ingress } from "./ingress.js";
26
+ import { bridgeGenerateReferences } from "./bridge_generate_references.js";
27
+ import { bridgeConfirmReference } from "./bridge_confirm_reference.js";
28
+ import { bridgeBuildSeed } from "./bridge_build_seed.js";
25
29
  import { gate, gateInputSchema, gateOutputSchema } from "./gate.js";
30
+ import { forceOverride, forceOverrideInputSchema, forceOverrideOutputSchema } from "./force_override.js";
26
31
  import { getAuthGate } from "../../auth/index.js";
27
32
  import { ToolErrorOutputSchema } from "../../generated/tool_error_output.js";
28
33
  import { decorateToolOutputText, notifyDesktopBestEffort } from "../../dx/activity.js";
34
+ import { resolveRunId } from "./context.js";
35
+ import { checkIngressGate, isIngressGateRequiredForTool } from "./ingress_gate.js";
29
36
  // ============================================================
30
37
  // Helper Functions
31
38
  // ============================================================
@@ -82,6 +89,24 @@ function errorResult(toolName, error) {
82
89
  details
83
90
  });
84
91
  }
92
+ async function enforceIngressGateIfNeeded(toolName, input) {
93
+ if (!isIngressGateRequiredForTool(toolName))
94
+ return null;
95
+ const basePath = process.cwd();
96
+ const project_id = input && typeof input === "object" && "project_id" in input && typeof input.project_id === "string"
97
+ ? input.project_id
98
+ : undefined;
99
+ const { run_id } = resolveRunId(project_id, basePath);
100
+ const gate = await checkIngressGate({ toolName, basePath, run_id });
101
+ if (gate.status === "BLOCK") {
102
+ return err(toolName, "ingress_gate_blocked", {
103
+ message: gate.message,
104
+ recovery: gate.recovery,
105
+ details: gate.details
106
+ });
107
+ }
108
+ return null;
109
+ }
85
110
  // ============================================================
86
111
  // Tool Handlers (with error handling and structured content)
87
112
  // ============================================================
@@ -104,9 +129,30 @@ async function vibePmBriefing(input) {
104
129
  });
105
130
  }
106
131
  }
132
+ async function vibePmIngress(input) {
133
+ const startedAt = Date.now();
134
+ try {
135
+ const result = await ingress(input);
136
+ const validated = ingressOutputSchema.parse(result);
137
+ return toolResult("vibe_pm.ingress", validated, Date.now() - startedAt);
138
+ }
139
+ catch (e) {
140
+ if (e instanceof McpError) {
141
+ return errorResult("vibe_pm.ingress", e);
142
+ }
143
+ const msg = e instanceof Error ? e.message : String(e);
144
+ return err("vibe_pm.ingress", "ingress_failed", {
145
+ message: msg,
146
+ recovery: "프로젝트 이어가기/새 작업을 확정하기 위한 입력을 확인해 주세요."
147
+ });
148
+ }
149
+ }
107
150
  async function vibePmGetDecision(input) {
108
151
  const startedAt = Date.now();
109
152
  try {
153
+ const blocked = await enforceIngressGateIfNeeded("vibe_pm.get_decision", input);
154
+ if (blocked)
155
+ return blocked;
110
156
  const result = await getDecision(input);
111
157
  const validated = getDecisionOutputSchema.parse(result);
112
158
  return toolResult("vibe_pm.get_decision", validated, Date.now() - startedAt);
@@ -125,6 +171,9 @@ async function vibePmGetDecision(input) {
125
171
  async function vibePmSubmitDecision(input) {
126
172
  const startedAt = Date.now();
127
173
  try {
174
+ const blocked = await enforceIngressGateIfNeeded("vibe_pm.submit_decision", input);
175
+ if (blocked)
176
+ return blocked;
128
177
  const result = await submitDecision(input);
129
178
  const validated = submitDecisionOutputSchema.parse(result);
130
179
  return toolResult("vibe_pm.submit_decision", validated, Date.now() - startedAt);
@@ -143,6 +192,9 @@ async function vibePmSubmitDecision(input) {
143
192
  async function vibePmCreateWorkOrder(input) {
144
193
  const startedAt = Date.now();
145
194
  try {
195
+ const blocked = await enforceIngressGateIfNeeded("vibe_pm.create_work_order", input);
196
+ if (blocked)
197
+ return blocked;
146
198
  const result = await createWorkOrder(input);
147
199
  const validated = createWorkOrderOutputSchema.parse(result);
148
200
  return toolResult("vibe_pm.create_work_order", validated, Date.now() - startedAt);
@@ -161,6 +213,9 @@ async function vibePmCreateWorkOrder(input) {
161
213
  async function vibePmInspectCode(input) {
162
214
  const startedAt = Date.now();
163
215
  try {
216
+ const blocked = await enforceIngressGateIfNeeded("vibe_pm.inspect_code", input);
217
+ if (blocked)
218
+ return blocked;
164
219
  const result = await inspectCode(input);
165
220
  const validated = inspectCodeOutputSchema.parse(result);
166
221
  return toolResult("vibe_pm.inspect_code", validated, Date.now() - startedAt);
@@ -368,6 +423,9 @@ async function vibePmScaffold(input) {
368
423
  async function vibePmFinalizeWork(input) {
369
424
  const startedAt = Date.now();
370
425
  try {
426
+ const blocked = await enforceIngressGateIfNeeded("vibe_pm.finalize_work", input);
427
+ if (blocked)
428
+ return blocked;
371
429
  const result = await finalizeWork(input);
372
430
  const validated = finalizeWorkOutputSchema.parse(result);
373
431
  return toolResult("vibe_pm.finalize_work", validated, Date.now() - startedAt);
@@ -386,6 +444,9 @@ async function vibePmFinalizeWork(input) {
386
444
  async function vibePmUndoLastTask(input) {
387
445
  const startedAt = Date.now();
388
446
  try {
447
+ const blocked = await enforceIngressGateIfNeeded("vibe_pm.undo_last_task", input);
448
+ if (blocked)
449
+ return blocked;
389
450
  const result = await undoLastTask(input);
390
451
  const validated = undoLastTaskOutputSchema.parse(result);
391
452
  return toolResult("vibe_pm.undo_last_task", validated, Date.now() - startedAt);
@@ -404,6 +465,9 @@ async function vibePmUndoLastTask(input) {
404
465
  async function vibePmRunApp(input) {
405
466
  const startedAt = Date.now();
406
467
  try {
468
+ const blocked = await enforceIngressGateIfNeeded("vibe_pm.run_app", input);
469
+ if (blocked)
470
+ return blocked;
407
471
  const result = await runApp(input);
408
472
  const validated = runAppOutputSchema.parse(result);
409
473
  return toolResult("vibe_pm.run_app", validated, Date.now() - startedAt);
@@ -473,6 +537,60 @@ async function vibePmZoektEvidence(input) {
473
537
  });
474
538
  }
475
539
  }
540
+ async function vibePmBridgeGenerateReferences(input) {
541
+ const startedAt = Date.now();
542
+ try {
543
+ const result = await bridgeGenerateReferences(input);
544
+ const validated = bridgeGenerateReferencesOutputSchema.parse(result);
545
+ return toolResult("vibe_pm.bridge_generate_references", validated, Date.now() - startedAt);
546
+ }
547
+ catch (e) {
548
+ if (e instanceof McpError) {
549
+ return errorResult("vibe_pm.bridge_generate_references", e);
550
+ }
551
+ const msg = e instanceof Error ? e.message : String(e);
552
+ return err("vibe_pm.bridge_generate_references", "bridge_generate_references_failed", {
553
+ message: msg,
554
+ recovery: "키워드/의도를 더 구체적으로 적은 뒤 다시 시도해주세요."
555
+ });
556
+ }
557
+ }
558
+ async function vibePmBridgeConfirmReference(input) {
559
+ const startedAt = Date.now();
560
+ try {
561
+ const result = await bridgeConfirmReference(input);
562
+ const validated = bridgeConfirmReferenceOutputSchema.parse(result);
563
+ return toolResult("vibe_pm.bridge_confirm_reference", validated, Date.now() - startedAt);
564
+ }
565
+ catch (e) {
566
+ if (e instanceof McpError) {
567
+ return errorResult("vibe_pm.bridge_confirm_reference", e);
568
+ }
569
+ const msg = e instanceof Error ? e.message : String(e);
570
+ return err("vibe_pm.bridge_confirm_reference", "bridge_confirm_reference_failed", {
571
+ message: msg,
572
+ recovery: "A/B/C 중 하나로 선택한 뒤 다시 시도해주세요."
573
+ });
574
+ }
575
+ }
576
+ async function vibePmBridgeBuildSeed(input) {
577
+ const startedAt = Date.now();
578
+ try {
579
+ const result = await bridgeBuildSeed(input);
580
+ const validated = bridgeBuildSeedOutputSchema.parse(result);
581
+ return toolResult("vibe_pm.bridge_build_seed", validated, Date.now() - startedAt);
582
+ }
583
+ catch (e) {
584
+ if (e instanceof McpError) {
585
+ return errorResult("vibe_pm.bridge_build_seed", e);
586
+ }
587
+ const msg = e instanceof Error ? e.message : String(e);
588
+ return err("vibe_pm.bridge_build_seed", "bridge_build_seed_failed", {
589
+ message: msg,
590
+ recovery: "레퍼런스 확정이 완료되었는지 확인한 뒤 다시 시도해주세요."
591
+ });
592
+ }
593
+ }
476
594
  async function vibePmGate(input) {
477
595
  const startedAt = Date.now();
478
596
  try {
@@ -491,6 +609,24 @@ async function vibePmGate(input) {
491
609
  });
492
610
  }
493
611
  }
612
+ async function vibePmForceOverride(input) {
613
+ const startedAt = Date.now();
614
+ try {
615
+ const result = await forceOverride(input);
616
+ const validated = forceOverrideOutputSchema.parse(result);
617
+ return toolResult("vibe_pm.force_override", validated, Date.now() - startedAt);
618
+ }
619
+ catch (e) {
620
+ if (e instanceof McpError) {
621
+ return errorResult("vibe_pm.force_override", e);
622
+ }
623
+ const msg = e instanceof Error ? e.message : String(e);
624
+ return err("vibe_pm.force_override", "force_override_failed", {
625
+ message: msg,
626
+ recovery: "acknowledge_risks: true 를 반드시 포함해야 합니다."
627
+ });
628
+ }
629
+ }
494
630
  // ============================================================
495
631
  // Export
496
632
  // ============================================================
@@ -498,6 +634,7 @@ export function defineVibePmTools() {
498
634
  return {
499
635
  // Input schemas (for validation)
500
636
  briefingInputSchema,
637
+ ingressInputSchema,
501
638
  getDecisionInputSchema,
502
639
  submitDecisionInputSchema,
503
640
  createWorkOrderInputSchema,
@@ -517,9 +654,14 @@ export function defineVibePmTools() {
517
654
  exportOutputInputSchema,
518
655
  searchOssInputSchema,
519
656
  zoektEvidenceInputSchema,
657
+ bridgeGenerateReferencesInputSchema,
658
+ bridgeConfirmReferenceInputSchema,
659
+ bridgeBuildSeedInputSchema,
520
660
  gateInputSchema,
661
+ forceOverrideInputSchema,
521
662
  // Tool handlers
522
663
  vibePmBriefing,
664
+ vibePmIngress,
523
665
  vibePmGetDecision,
524
666
  vibePmSubmitDecision,
525
667
  vibePmCreateWorkOrder,
@@ -539,7 +681,11 @@ export function defineVibePmTools() {
539
681
  vibePmExportOutput,
540
682
  vibePmSearchOss,
541
683
  vibePmZoektEvidence,
542
- vibePmGate
684
+ vibePmBridgeGenerateReferences,
685
+ vibePmBridgeConfirmReference,
686
+ vibePmBridgeBuildSeed,
687
+ vibePmGate,
688
+ vibePmForceOverride
543
689
  };
544
690
  }
545
691
  // ============================================================
@@ -548,6 +694,8 @@ export function defineVibePmTools() {
548
694
  export const VIBE_PM_TOOL_DESCRIPTIONS = {
549
695
  "vibe_pm.briefing": `프로젝트를 시작하거나 재개합니다. 사용자의 아이디어를 받아 구조화합니다.
550
696
  사용 시점: 사용자가 "시작해줘" 또는 새 아이디어를 설명할 때`,
697
+ "vibe_pm.ingress": `프로젝트를 이어가기/새 작업 시작을 확정하고, 필요한 사전 점검(컨텍스트 스캔)을 수행합니다.
698
+ 사용 시점: 기존 프로젝트를 이어서 할지, 새 작업을 시작할지 확정해야 할 때`,
551
699
  "vibe_pm.get_decision": `현재 결재가 필요한 안건을 조회합니다.
552
700
  사용 시점: 구현 전 승인이 필요할 때`,
553
701
  "vibe_pm.submit_decision": `결재를 제출합니다.
@@ -585,9 +733,18 @@ export const VIBE_PM_TOOL_DESCRIPTIONS = {
585
733
  사용 시점: export_output용 템플릿을 고르기 전에 후보를 좁히고 싶을 때`,
586
734
  "vibe_pm.zoekt_evidence": `로컬 루트들에서 패턴 증거를 수집합니다(zoekt/rg/python_scan).
587
735
  사용 시점: 특정 코드 패턴이 실제로 얼마나 쓰이는지 근거가 필요할 때`,
736
+ "vibe_pm.bridge_generate_references": `모호한 요구를 레퍼런스 후보(A/B/C)로 정리하고 파일로 저장합니다.
737
+ 사용 시점: 구현 전에 "어떤 스타일/유사 제품 기준으로 만들지"를 확정해야 할 때`,
738
+ "vibe_pm.bridge_confirm_reference": `레퍼런스 후보 중 하나(A/B/C)를 확정하고 확인 기록을 남깁니다.
739
+ 사용 시점: 레퍼런스 후보를 만든 뒤, 기준을 하나로 고정할 때`,
740
+ "vibe_pm.bridge_build_seed": `확정된 레퍼런스를 기반으로 작업 seed를 생성합니다.
741
+ 사용 시점: 레퍼런스 확정 후, create_work_order의 기준을 고정하고 싶을 때`,
588
742
  "vibe_pm.export_output": `구현된 기능을 원하는 형태로 내보냅니다. (문서/API/CLI 등)
589
743
  사용 시점: 기능 구현 및 검수 후, 결과물을 다른 형태로 배포/공유하고 싶을 때`,
590
744
  "vibe_pm.gate": `작업 지시서가 시스템 설계 규칙을 준수하는지 검증합니다. (Schema/Path/Runtime/Semgrep Gate)
591
745
  사용 시점: 실행 전 work order가 규칙을 준수하는지 직접 확인하고 싶을 때
592
- BLOCK이 없으면 실행 가능, BLOCK이 있으면 수정이 필요합니다.`
746
+ BLOCK이 없으면 실행 가능, BLOCK이 있으면 수정이 필요합니다.`,
747
+ "vibe_pm.force_override": `차단된 작업을 강제로 실행할 수 있는 탈출 경로입니다. (P2-1)
748
+ 사용 시점: 정책에 의해 차단되었지만 긴급히 실행이 필요할 때
749
+ ⚠ 주의: acknowledge_risks=true 필수, 모든 사용은 감사 로그에 기록됩니다.`
593
750
  };