@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.
- package/README.md +10 -10
- package/build/auth/credential_store.js +146 -0
- package/build/auth/public_key.js +6 -4
- package/build/bootstrap/doctor.js +113 -5
- package/build/bootstrap/installer.js +85 -15
- package/build/bootstrap/registry.js +11 -6
- package/build/bootstrap/skills-installer.js +365 -0
- package/build/control_plane/gate.js +52 -70
- package/build/dx/activity.js +26 -3
- package/build/engine.js +151 -0
- package/build/errors.js +107 -0
- package/build/generated/bridge_build_seed_input.js +2 -0
- package/build/generated/bridge_build_seed_output.js +2 -0
- package/build/generated/bridge_confirm_reference_input.js +2 -0
- package/build/generated/bridge_confirm_reference_output.js +2 -0
- package/build/generated/bridge_confirmed_reference_file.js +2 -0
- package/build/generated/bridge_generate_references_input.js +2 -0
- package/build/generated/bridge_generate_references_output.js +2 -0
- package/build/generated/bridge_references_file.js +2 -0
- package/build/generated/bridge_work_order_seed_file.js +2 -0
- package/build/generated/contracts_bundle_info.js +3 -3
- package/build/generated/index.js +14 -0
- package/build/generated/ingress_input.js +2 -0
- package/build/generated/ingress_output.js +2 -0
- package/build/generated/ingress_resolution_file.js +2 -0
- package/build/generated/ingress_summary_file.js +2 -0
- package/build/generated/message_template_id_mapping_file.js +2 -0
- package/build/generated/run_app_input.js +1 -1
- package/build/index.js +4 -1
- package/build/local-mode/git.js +36 -22
- package/build/local-mode/paths.js +1 -0
- package/build/local-mode/project-state.js +176 -0
- package/build/local-mode/setup.js +21 -1
- package/build/local-mode/templates.js +3 -3
- package/build/path-utils.js +68 -0
- package/build/runtime/cli_invoker.js +416 -0
- package/build/tools/vibe_pm/advisory_review.js +5 -3
- package/build/tools/vibe_pm/bridge_build_seed.js +164 -0
- package/build/tools/vibe_pm/bridge_confirm_reference.js +91 -0
- package/build/tools/vibe_pm/bridge_generate_references.js +258 -0
- package/build/tools/vibe_pm/briefing.js +26 -1
- package/build/tools/vibe_pm/context.js +79 -0
- package/build/tools/vibe_pm/create_work_order.js +200 -3
- package/build/tools/vibe_pm/doctor.js +95 -0
- package/build/tools/vibe_pm/entity_gate/preflight.js +8 -3
- package/build/tools/vibe_pm/export_output.js +14 -13
- package/build/tools/vibe_pm/finalize_work.js +74 -0
- package/build/tools/vibe_pm/force_override.js +104 -0
- package/build/tools/vibe_pm/get_decision.js +2 -2
- package/build/tools/vibe_pm/index.js +160 -3
- package/build/tools/vibe_pm/ingress.js +645 -0
- package/build/tools/vibe_pm/ingress_gate.js +116 -0
- package/build/tools/vibe_pm/inspect_code.js +90 -20
- package/build/tools/vibe_pm/kce/doc_usage.js +4 -9
- package/build/tools/vibe_pm/kce/on_finalize.js +2 -2
- package/build/tools/vibe_pm/kce/preflight.js +11 -7
- package/build/tools/vibe_pm/list_rules.js +135 -0
- package/build/tools/vibe_pm/memory_status.js +11 -8
- package/build/tools/vibe_pm/memory_sync.js +11 -8
- package/build/tools/vibe_pm/pm_language.js +17 -16
- package/build/tools/vibe_pm/pre_commit_analysis.js +292 -0
- package/build/tools/vibe_pm/publish_mcp.js +271 -0
- package/build/tools/vibe_pm/python_error.js +115 -0
- package/build/tools/vibe_pm/run_app.js +215 -86
- package/build/tools/vibe_pm/run_app_podman.js +64 -2
- package/build/tools/vibe_pm/save_rule.js +120 -0
- package/build/tools/vibe_pm/search_oss.js +5 -3
- package/build/tools/vibe_pm/spec_rag.js +185 -0
- package/build/tools/vibe_pm/status.js +50 -3
- package/build/tools/vibe_pm/submit_decision.js +2 -2
- package/build/tools/vibe_pm/types.js +28 -0
- package/build/tools/vibe_pm/undo_last_task.js +23 -20
- package/build/tools/vibe_pm/waiter_mapping.js +155 -0
- package/build/tools/vibe_pm/zoekt_evidence.js +5 -3
- package/build/tools.js +13 -5
- package/build/version-check.js +5 -5
- package/build/vibe-cli.js +742 -39
- package/package.json +5 -4
- package/skills/VRIP_INSTALL_MANIFEST_DOCTOR.skill.md +288 -0
- 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 {
|
|
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
|
|
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
|
-
|
|
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
|
};
|