@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,26 @@
1
+ // adapters/mcp-ts/src/bootstrap/platform.ts
2
+ // Platform detection and executable naming
3
+ /**
4
+ * Detect current platform for asset download
5
+ */
6
+ export function detectPlatform() {
7
+ const p = process.platform;
8
+ const a = process.arch;
9
+ if (p === "win32")
10
+ return "win_x64"; // win arm64 out of scope for v1
11
+ if (p === "darwin") {
12
+ if (a === "arm64")
13
+ return "darwin_arm64";
14
+ return "darwin_x64";
15
+ }
16
+ // linux
17
+ if (a === "arm64")
18
+ return "linux_arm64";
19
+ return "linux_x64";
20
+ }
21
+ /**
22
+ * Get executable name with platform-specific extension
23
+ */
24
+ export function exeName(base) {
25
+ return process.platform === "win32" ? `${base}.exe` : base;
26
+ }
@@ -0,0 +1,37 @@
1
+ // adapters/mcp-ts/src/bootstrap/registry.ts
2
+ // SSOT: Engine versions and release asset naming
3
+ /**
4
+ * Engine Registry (SSOT)
5
+ *
6
+ * VERSION_MATRIX.md์™€ ๋™๊ธฐํ™” ํ•„์ˆ˜.
7
+ * ์—ฌ๊ธฐ ๋ฒ„์ „์„ ๋ฐ”๊พธ๋ฉด ์›ํด๋ฆญ ์„ค์น˜ ๋Œ€์ƒ์ด ๋ฐ”๋€๋‹ค.
8
+ */
9
+ export const ENGINE_SPECS = {
10
+ "spec-high": {
11
+ name: "spec-high",
12
+ version: "1.0.0",
13
+ repo: "yellowhama/vibe-pm",
14
+ assetPrefix: "spec-high",
15
+ shaSumsAsset: "SHA256SUMS.txt"
16
+ },
17
+ "vibecoding-helper": {
18
+ name: "vibecoding-helper",
19
+ version: "1.0.0",
20
+ repo: "yellowhama/vibe-pm",
21
+ assetPrefix: "vibecoding-helper",
22
+ shaSumsAsset: "SHA256SUMS.txt"
23
+ },
24
+ "clinic": {
25
+ name: "clinic",
26
+ version: "2.3.0",
27
+ repo: "yellowhama/vibe-pm",
28
+ assetPrefix: "clinic",
29
+ shaSumsAsset: "SHA256SUMS.txt"
30
+ }
31
+ };
32
+ /**
33
+ * Asset naming convention:
34
+ * {assetPrefix}_{version}_{platform}.tar.gz
35
+ *
36
+ * Example: spec-high_1.0.0_linux_x64.tar.gz
37
+ */
@@ -0,0 +1,147 @@
1
+ // adapters/mcp-ts/src/cache/index.ts
2
+ // TieredCache - TTL + mtime-based cache with LRU eviction
3
+ /**
4
+ * TieredCache - Multi-level cache with TTL and mtime validation
5
+ *
6
+ * Features:
7
+ * - TTL-based expiration
8
+ * - Optional mtime validation for file-based caching
9
+ * - LRU eviction when maxEntries exceeded
10
+ * - Pattern-based invalidation
11
+ */
12
+ export class TieredCache {
13
+ config;
14
+ cache = new Map();
15
+ accessOrder = [];
16
+ hits = 0;
17
+ misses = 0;
18
+ constructor(config) {
19
+ this.config = config;
20
+ }
21
+ /**
22
+ * Get cached value if valid
23
+ *
24
+ * @param key - Cache key
25
+ * @param mtimeGetter - Optional function to get current mtime for validation
26
+ * @returns Cached value or null if expired/invalid
27
+ */
28
+ get(key, mtimeGetter) {
29
+ const entry = this.cache.get(key);
30
+ if (!entry) {
31
+ this.misses++;
32
+ return null;
33
+ }
34
+ // Check TTL expiration
35
+ const now = Date.now();
36
+ if (now - entry.timestamp > this.config.ttlMs) {
37
+ this.cache.delete(key);
38
+ this.removeFromAccessOrder(key);
39
+ this.misses++;
40
+ return null;
41
+ }
42
+ // Check mtime validation if provided
43
+ if (mtimeGetter && entry.mtime !== undefined) {
44
+ const currentMtime = mtimeGetter();
45
+ if (currentMtime !== null && currentMtime !== entry.mtime) {
46
+ this.cache.delete(key);
47
+ this.removeFromAccessOrder(key);
48
+ this.misses++;
49
+ return null;
50
+ }
51
+ }
52
+ // Update access order for LRU
53
+ this.updateAccessOrder(key);
54
+ this.hits++;
55
+ return entry.value;
56
+ }
57
+ /**
58
+ * Set cache value
59
+ *
60
+ * @param key - Cache key
61
+ * @param value - Value to cache
62
+ * @param mtime - Optional mtime for file-based validation
63
+ */
64
+ set(key, value, mtime) {
65
+ // Evict if at capacity
66
+ if (this.cache.size >= this.config.maxEntries && !this.cache.has(key)) {
67
+ this.evictLRU();
68
+ }
69
+ this.cache.set(key, {
70
+ value,
71
+ timestamp: Date.now(),
72
+ mtime,
73
+ });
74
+ this.updateAccessOrder(key);
75
+ }
76
+ /**
77
+ * Invalidate specific cache entry
78
+ */
79
+ invalidate(key) {
80
+ this.cache.delete(key);
81
+ this.removeFromAccessOrder(key);
82
+ }
83
+ /**
84
+ * Invalidate all entries matching a pattern
85
+ */
86
+ invalidatePattern(pattern) {
87
+ const keysToDelete = [];
88
+ for (const key of this.cache.keys()) {
89
+ if (pattern.test(key)) {
90
+ keysToDelete.push(key);
91
+ }
92
+ }
93
+ for (const key of keysToDelete) {
94
+ this.cache.delete(key);
95
+ this.removeFromAccessOrder(key);
96
+ }
97
+ }
98
+ /**
99
+ * Clear all cache entries
100
+ */
101
+ clear() {
102
+ this.cache.clear();
103
+ this.accessOrder = [];
104
+ }
105
+ /**
106
+ * Get cache statistics
107
+ */
108
+ stats() {
109
+ return {
110
+ size: this.cache.size,
111
+ maxEntries: this.config.maxEntries,
112
+ name: this.config.name,
113
+ hits: this.hits,
114
+ misses: this.misses,
115
+ };
116
+ }
117
+ /**
118
+ * Check if key exists and is valid
119
+ */
120
+ has(key, mtimeGetter) {
121
+ return this.get(key, mtimeGetter) !== null;
122
+ }
123
+ updateAccessOrder(key) {
124
+ this.removeFromAccessOrder(key);
125
+ this.accessOrder.push(key);
126
+ }
127
+ removeFromAccessOrder(key) {
128
+ const index = this.accessOrder.indexOf(key);
129
+ if (index !== -1) {
130
+ this.accessOrder.splice(index, 1);
131
+ }
132
+ }
133
+ evictLRU() {
134
+ if (this.accessOrder.length === 0)
135
+ return;
136
+ const lruKey = this.accessOrder.shift();
137
+ if (lruKey) {
138
+ this.cache.delete(lruKey);
139
+ }
140
+ }
141
+ }
142
+ /**
143
+ * Create a cache key from multiple parts
144
+ */
145
+ export function createCacheKey(...parts) {
146
+ return parts.filter((p) => p !== undefined).join("::");
147
+ }
package/build/cli.js ADDED
@@ -0,0 +1,101 @@
1
+ import { spawn } from "node:child_process";
2
+ /**
3
+ * Environment variable whitelist for subprocess execution
4
+ * Only these variables are passed through to child processes
5
+ */
6
+ const ENV_WHITELIST = new Set([
7
+ // System essentials
8
+ "PATH",
9
+ "HOME",
10
+ "USER",
11
+ "LANG",
12
+ "LC_ALL",
13
+ "LC_CTYPE",
14
+ "TZ",
15
+ "TERM",
16
+ "SHELL",
17
+ // Vibe-specific
18
+ "VIBECODE_CACHE",
19
+ "VIBECODE_ROOT",
20
+ "VIBECODE_DEBUG",
21
+ "VIBECODE_OFFLINE",
22
+ // Runtime paths
23
+ "NODE_PATH",
24
+ "PYTHONPATH",
25
+ "PYTHONIOENCODING",
26
+ // Build tools
27
+ "CC",
28
+ "CXX",
29
+ "MAKE",
30
+ ]);
31
+ /**
32
+ * Sanitize environment variables by filtering through whitelist
33
+ *
34
+ * @param additionalEnv - Optional additional env vars to include
35
+ * @returns Filtered environment object
36
+ */
37
+ export function sanitizeEnv(additionalEnv) {
38
+ const result = {};
39
+ // Filter process.env through whitelist
40
+ for (const key of ENV_WHITELIST) {
41
+ if (process.env[key] !== undefined) {
42
+ result[key] = process.env[key];
43
+ }
44
+ }
45
+ // Add any additional env vars (also filtered through whitelist for security)
46
+ if (additionalEnv) {
47
+ for (const [key, value] of Object.entries(additionalEnv)) {
48
+ if (value !== undefined && ENV_WHITELIST.has(key)) {
49
+ result[key] = value;
50
+ }
51
+ }
52
+ }
53
+ return result;
54
+ }
55
+ export async function runCmd(cmd, args, opts) {
56
+ const timeoutMs = opts?.timeoutMs ?? 120_000;
57
+ return await new Promise((resolve) => {
58
+ let resolved = false;
59
+ const p = spawn(cmd, args, {
60
+ cwd: opts?.cwd,
61
+ env: sanitizeEnv(opts?.env),
62
+ shell: false,
63
+ stdio: ["ignore", "pipe", "pipe"]
64
+ });
65
+ let stdout = "";
66
+ let stderr = "";
67
+ const finish = (code, out, err) => {
68
+ if (resolved)
69
+ return;
70
+ resolved = true;
71
+ resolve({ code, stdout: out, stderr: err });
72
+ };
73
+ const killTimer = setTimeout(() => {
74
+ try {
75
+ p.kill("SIGKILL");
76
+ }
77
+ catch { }
78
+ finish(124, stdout, stderr + "\n[TIMEOUT]");
79
+ }, timeoutMs);
80
+ p.stdout.on("data", (d) => (stdout += d.toString("utf-8")));
81
+ p.stderr.on("data", (d) => (stderr += d.toString("utf-8")));
82
+ p.on("error", (e) => {
83
+ clearTimeout(killTimer);
84
+ const msg = e instanceof Error ? e.message : String(e);
85
+ finish(127, stdout, stderr + `\n[SPAWN_ERROR] ${msg}`);
86
+ });
87
+ p.on("close", (code) => {
88
+ clearTimeout(killTimer);
89
+ finish(code ?? 1, stdout, stderr);
90
+ });
91
+ });
92
+ }
93
+ export function safeJsonParse(raw) {
94
+ try {
95
+ return { ok: true, value: JSON.parse(raw) };
96
+ }
97
+ catch (e) {
98
+ const msg = e instanceof Error ? e.message : String(e);
99
+ return { ok: false, error: msg };
100
+ }
101
+ }
@@ -0,0 +1,22 @@
1
+ // contracts.ts
2
+ // Central contract types for MCP adapter
3
+ // SelectionValidationResult is generated from JSON Schema SSOT
4
+ import { SelectionValidationResultSchema } from "./generated/selection_validation_result.js";
5
+ import { ToolErrorOutputSchema } from "./generated/tool_error_output.js";
6
+ // ============================================================
7
+ // Generated Types (from JSON Schema SSOT)
8
+ // ============================================================
9
+ export { SelectionValidationResultSchema };
10
+ export { ToolErrorOutputSchema };
11
+ export const SignalLevelSchema = SelectionValidationResultSchema.shape.signal_level;
12
+ export const DetailsSchema = SelectionValidationResultSchema.shape.details;
13
+ export const NextActionSchema = SelectionValidationResultSchema.shape.next_action;
14
+ // Backwards-compat alias (older docs / consumers)
15
+ export const NextActionObjectSchema = NextActionSchema;
16
+ export const PathsSchema = SelectionValidationResultSchema.shape.paths;
17
+ export function parseSelectionValidationResult(input) {
18
+ return SelectionValidationResultSchema.parse(input);
19
+ }
20
+ export function safeParseSelectionValidationResult(input) {
21
+ return SelectionValidationResultSchema.safeParse(input);
22
+ }
@@ -0,0 +1,161 @@
1
+ /**
2
+ * System Design Gate - Control Plane for Vibe PM
3
+ *
4
+ * Provides gate enforcement for work orders before execution.
5
+ * All work orders must pass through the gate before any work can proceed.
6
+ */
7
+ import { spawn } from "child_process";
8
+ import { WorkOrderV1Schema } from "../generated/work_order_v1.js";
9
+ import { GateResultV1Schema } from "../generated/gate_result_v1.js";
10
+ /**
11
+ * Run the system design gate check on a work order
12
+ */
13
+ export async function runSystemDesignGate(workOrder, options) {
14
+ const { repoRoot, pythonExe = "python3", timeoutMs = 30000 } = options;
15
+ // Validate work order first
16
+ const parseResult = WorkOrderV1Schema.safeParse(workOrder);
17
+ if (!parseResult.success) {
18
+ return {
19
+ success: false,
20
+ error: `Invalid work order: ${parseResult.error.message}`,
21
+ };
22
+ }
23
+ const workOrderJson = JSON.stringify(workOrder);
24
+ const overrideFlags = [];
25
+ const pushBool = (name, value) => {
26
+ if (typeof value !== "boolean")
27
+ return;
28
+ overrideFlags.push(value ? `--${name}` : `--no-${name}`);
29
+ };
30
+ pushBool("fail-fast", options.failFast);
31
+ pushBool("semgrep", options.semgrep);
32
+ pushBool("runtime", options.runtime);
33
+ return new Promise((resolve) => {
34
+ const child = spawn(pythonExe, [
35
+ "-m",
36
+ "vibecoding_helper",
37
+ "--repo-root",
38
+ repoRoot,
39
+ "gate-check",
40
+ ...overrideFlags,
41
+ "--work-order",
42
+ workOrderJson,
43
+ "--format",
44
+ "json",
45
+ ], {
46
+ cwd: repoRoot,
47
+ timeout: timeoutMs,
48
+ env: { ...process.env, PYTHONIOENCODING: "utf-8" },
49
+ });
50
+ let stdout = "";
51
+ let stderr = "";
52
+ child.stdout?.on("data", (data) => {
53
+ stdout += data.toString();
54
+ });
55
+ child.stderr?.on("data", (data) => {
56
+ stderr += data.toString();
57
+ });
58
+ child.on("close", (code) => {
59
+ // Exit code 0 = ALLOW, 2 = BLOCK, other = error
60
+ if (code === null) {
61
+ resolve({
62
+ success: false,
63
+ error: "Gate check timed out",
64
+ });
65
+ return;
66
+ }
67
+ if (code !== 0 && code !== 2) {
68
+ resolve({
69
+ success: false,
70
+ error: `Gate check failed with code ${code}: ${stderr || stdout}`,
71
+ });
72
+ return;
73
+ }
74
+ try {
75
+ const result = JSON.parse(stdout.trim());
76
+ const validated = GateResultV1Schema.safeParse(result);
77
+ if (!validated.success) {
78
+ resolve({
79
+ success: false,
80
+ error: `Invalid gate result: ${validated.error.message}`,
81
+ });
82
+ return;
83
+ }
84
+ resolve({
85
+ success: true,
86
+ result: validated.data,
87
+ });
88
+ }
89
+ catch (e) {
90
+ resolve({
91
+ success: false,
92
+ error: `Failed to parse gate result: ${e instanceof Error ? e.message : String(e)}`,
93
+ });
94
+ }
95
+ });
96
+ child.on("error", (err) => {
97
+ resolve({
98
+ success: false,
99
+ error: `Gate check process error: ${err.message}`,
100
+ });
101
+ });
102
+ });
103
+ }
104
+ /**
105
+ * Execute a work order only if it passes the gate
106
+ *
107
+ * @throws Error if gate blocks the work order
108
+ */
109
+ export async function executeWithGate(workOrder, options) {
110
+ const gateResult = await runSystemDesignGate(workOrder, options);
111
+ if (!gateResult.success || !gateResult.result) {
112
+ throw new Error(`Gate check failed: ${gateResult.error || "Unknown error"}`);
113
+ }
114
+ if (gateResult.result.verdict === "BLOCK") {
115
+ const violations = gateResult.result.violations
116
+ .map((v) => `[${v.rule_id}] ${v.message}`)
117
+ .join("; ");
118
+ throw new Error(`Gate BLOCKED: ${violations}`);
119
+ }
120
+ return gateResult.result;
121
+ }
122
+ /**
123
+ * Check if a work order would pass the gate (without executing)
124
+ */
125
+ export async function checkGate(workOrder, options) {
126
+ const gateResult = await runSystemDesignGate(workOrder, options);
127
+ if (!gateResult.success || !gateResult.result) {
128
+ return {
129
+ allowed: false,
130
+ result: null,
131
+ error: gateResult.error,
132
+ };
133
+ }
134
+ return {
135
+ allowed: gateResult.result.verdict === "ALLOW",
136
+ result: gateResult.result,
137
+ };
138
+ }
139
+ /**
140
+ * Format gate violations for display
141
+ */
142
+ export function formatViolations(result) {
143
+ if (result.violations.length === 0) {
144
+ return "No violations";
145
+ }
146
+ const lines = result.violations.map((v) => {
147
+ const emoji = v.level === "BLOCK" ? "โŒ" : v.level === "WARN" ? "โš ๏ธ" : "โ„น๏ธ";
148
+ let line = `${emoji} [${v.rule_id}] ${v.message}`;
149
+ if (v.remediation) {
150
+ line += `\n Fix: ${v.remediation}`;
151
+ }
152
+ if (v.location?.file) {
153
+ line += `\n Location: ${v.location.file}`;
154
+ if (v.location.line) {
155
+ line += `:${v.location.line}`;
156
+ }
157
+ }
158
+ return line;
159
+ });
160
+ return lines.join("\n\n");
161
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Control Plane module for Vibe PM
3
+ *
4
+ * Provides gate-based enforcement of system design rules.
5
+ */
6
+ export { runSystemDesignGate, executeWithGate, checkGate, formatViolations, } from "./gate.js";
@@ -0,0 +1,139 @@
1
+ // adapters/mcp-ts/src/dx/activity.ts
2
+ // Activity/DX helpers: branded header, status icons, optional desktop notifications
3
+ function boolEnv(name, defaultValue) {
4
+ const raw = (process.env[name] ?? "").trim().toLowerCase();
5
+ if (!raw)
6
+ return defaultValue;
7
+ if (["1", "true", "yes", "y", "on"].includes(raw))
8
+ return true;
9
+ if (["0", "false", "no", "n", "off"].includes(raw))
10
+ return false;
11
+ return defaultValue;
12
+ }
13
+ function intEnv(name, defaultValue) {
14
+ const raw = (process.env[name] ?? "").trim();
15
+ if (!raw)
16
+ return defaultValue;
17
+ const n = Number(raw);
18
+ return Number.isFinite(n) ? Math.trunc(n) : defaultValue;
19
+ }
20
+ export function isActivityHeaderEnabled() {
21
+ return boolEnv("VIBECODE_ACTIVITY_HEADER", true);
22
+ }
23
+ export function isDesktopNotifyEnabled() {
24
+ return boolEnv("VIBECODE_NOTIFY", false);
25
+ }
26
+ export function desktopNotifyMinMs() {
27
+ return intEnv("VIBECODE_NOTIFY_MIN_MS", 1500);
28
+ }
29
+ function toolBadge(toolName, data) {
30
+ const map = {
31
+ "vibe_pm.briefing": { icon: "๐Ÿงญ", label: "ํ”„๋กœ์ ํŠธ ์ ‘์ˆ˜" },
32
+ "vibe_pm.get_decision": { icon: "๐Ÿ—ณ๏ธ", label: "๊ฒฐ์žฌ ํ™•์ธ" },
33
+ "vibe_pm.submit_decision": { icon: "โœ๏ธ", label: "๊ฒฐ์žฌ ๋ฐ˜์˜" },
34
+ "vibe_pm.create_work_order": { icon: "๐Ÿงพ", label: "์ž‘์—… ์ง€์‹œ์„œ ์ƒ์„ฑ" },
35
+ "vibe_pm.inspect_code": { icon: "๐Ÿ”", label: "๊ฒ€์ˆ˜" },
36
+ "vibe_pm.repair_plan": { icon: "๐Ÿ› ๏ธ", label: "์ˆ˜์ • ์ง€์‹œ์„œ" },
37
+ "vibe_pm.status": { icon: "๐Ÿ“", label: "์ƒํƒœ ํ™•์ธ" },
38
+ "vibe_pm.scaffold": { icon: "๐Ÿงฑ", label: "๊ตฌ์กฐ ์ƒ์„ฑ" },
39
+ "vibe_pm.finalize_work": { icon: "โœ…", label: "๋งˆ๊ฐ ์ •๋ฆฌ" },
40
+ "vibe_pm.undo_last_task": { icon: "โ†ฉ๏ธ", label: "๋กค๋ฐฑ" },
41
+ "vibe_pm.run_app": { icon: "๐Ÿš€", label: "์‹คํ–‰" },
42
+ "vibe_pm.export_output": { icon: "๐Ÿ“ฆ", label: "๋‚ด๋ณด๋‚ด๊ธฐ" },
43
+ "vibe_pm.search_oss": { icon: "๐Ÿ”Ž", label: "๊ฒ€์ƒ‰" },
44
+ "vibe_pm.zoekt_evidence": { icon: "๐Ÿงพ", label: "๊ทผ๊ฑฐ ์ˆ˜์ง‘" },
45
+ "vibe_pm.gate": { icon: "๐Ÿ›ก๏ธ", label: "์•ˆ์ „ ์ ๊ฒ€" },
46
+ "vibe_pm.doctor": { icon: "๐Ÿฉบ", label: "์ง„๋‹จ" },
47
+ "vibe_pm.update": { icon: "โฌ†๏ธ", label: "์—…๋ฐ์ดํŠธ" },
48
+ "vibe_pm.activate": { icon: "๐Ÿ”‘", label: "ํ™œ์„ฑํ™”" },
49
+ "vibe_pm.memory_status": { icon: "๐Ÿง ", label: "๋ฉ”๋ชจ๋ฆฌ ์ƒํƒœ" },
50
+ "vibe_pm.memory_sync": { icon: "๐Ÿง ", label: "๋ฉ”๋ชจ๋ฆฌ ๋™๊ธฐํ™”" },
51
+ "vibe_pm.advisory_review": { icon: "๐Ÿงช", label: "์‚ฌ์ „ ์ ๊ฒ€" },
52
+ "react_perf.check_patterns": { icon: "โšก๏ธ", label: "์„ฑ๋Šฅ ์ ๊ฒ€" },
53
+ "react_perf.generate_report": { icon: "๐Ÿ“„", label: "๋ฆฌํฌํŠธ ์ƒ์„ฑ" }
54
+ };
55
+ if (toolName === "vibe_pm.run_app" && data && typeof data === "object") {
56
+ const obj = data;
57
+ const runtime = (obj.runtime ?? null);
58
+ const containerId = obj.container_id ?? runtime?.container_id;
59
+ const container = runtime?.container;
60
+ if (containerId || container === "podman") {
61
+ return { icon: "๐Ÿ“ฆ", label: "์ƒŒ๋“œ๋ฐ•์Šค ์‹คํ–‰" };
62
+ }
63
+ }
64
+ if (toolName in map)
65
+ return map[toolName];
66
+ if (toolName.startsWith("vibe_pm."))
67
+ return { icon: "โšก๏ธ", label: "์ž‘์—…" };
68
+ return { icon: "โš™๏ธ", label: "์ž‘์—…" };
69
+ }
70
+ function verdictIcon(data, outcome) {
71
+ if (outcome === "error")
72
+ return "โŒ";
73
+ if (!data || typeof data !== "object")
74
+ return "โœ…";
75
+ const obj = data;
76
+ const verdict = String(obj.verdict ?? "").toUpperCase();
77
+ if (verdict === "BLOCK")
78
+ return "โŒ";
79
+ if (verdict === "ALLOW")
80
+ return "โœ…";
81
+ const reviewSummary = obj.review_summary;
82
+ if (reviewSummary && typeof reviewSummary === "object") {
83
+ const rs = reviewSummary;
84
+ const rr = String(rs.review_result ?? "").toUpperCase();
85
+ if (rr === "GO")
86
+ return "โœ…";
87
+ if (rr === "FIX")
88
+ return "โš ๏ธ";
89
+ if (rr === "BLOCK")
90
+ return "โŒ";
91
+ }
92
+ return "โœ…";
93
+ }
94
+ export function renderActivityHeader(toolName, data, outcome = "success", durationMs) {
95
+ const badge = toolBadge(toolName, data);
96
+ const status = verdictIcon(data, outcome);
97
+ const duration = typeof durationMs === "number" && durationMs >= 0 ? ` ยท ${Math.round(durationMs)}ms` : "";
98
+ return [
99
+ "โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ",
100
+ "โ”‚ VibePM โšก๏ธ Active โ”‚",
101
+ `โ”‚ ${status} ${badge.icon} ${badge.label}${duration}`,
102
+ "โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ"
103
+ ].join("\n");
104
+ }
105
+ export function decorateToolOutputText(toolName, jsonText, data, outcome = "success", durationMs) {
106
+ if (!isActivityHeaderEnabled()) {
107
+ return [{ type: "text", text: jsonText }];
108
+ }
109
+ const header = renderActivityHeader(toolName, data, outcome, durationMs);
110
+ return [
111
+ { type: "text", text: header },
112
+ { type: "text", text: jsonText }
113
+ ];
114
+ }
115
+ export async function notifyDesktopBestEffort(args) {
116
+ if (!isDesktopNotifyEnabled())
117
+ return;
118
+ const minMs = desktopNotifyMinMs();
119
+ const durationMs = args.durationMs ?? 0;
120
+ if (args.outcome !== "error" && minMs > 0 && durationMs < minMs)
121
+ return;
122
+ try {
123
+ const mod = await import("node-notifier");
124
+ const notifier = mod.default ?? mod;
125
+ const badge = toolBadge(args.toolName, args.data);
126
+ const status = verdictIcon(args.data, args.outcome);
127
+ const duration = durationMs ? ` (${Math.round(durationMs)}ms)` : "";
128
+ notifier.notify({
129
+ title: "VibePM โšก๏ธ",
130
+ message: `${status} ${badge.icon} ${badge.label}${duration}`,
131
+ sound: false,
132
+ wait: false,
133
+ timeout: 3
134
+ });
135
+ }
136
+ catch {
137
+ // Best-effort: never fail tool execution due to notify issues (headless/permissions).
138
+ }
139
+ }