@vibecodetown/mcp-server 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +269 -0
- package/build/auth/gate.js +225 -0
- package/build/auth/index.js +55 -0
- package/build/auth/public_key.js +27 -0
- package/build/auth/token_cache.js +122 -0
- package/build/auth/token_verifier.js +103 -0
- package/build/bootstrap/doctor.js +115 -0
- package/build/bootstrap/installer.js +673 -0
- package/build/bootstrap/lock.js +37 -0
- package/build/bootstrap/platform.js +26 -0
- package/build/bootstrap/registry.js +37 -0
- package/build/cache/index.js +147 -0
- package/build/cli.js +101 -0
- package/build/contracts.js +22 -0
- package/build/control_plane/gate.js +161 -0
- package/build/control_plane/index.js +6 -0
- package/build/dx/activity.js +139 -0
- package/build/engine.js +106 -0
- package/build/errors.js +171 -0
- package/build/generated/activate_input.js +2 -0
- package/build/generated/activate_output.js +57 -0
- package/build/generated/advisory_review_input.js +2 -0
- package/build/generated/advisory_review_output.js +35 -0
- package/build/generated/auth_token_file.js +2 -0
- package/build/generated/briefing_input.js +2 -0
- package/build/generated/briefing_output.js +2 -0
- package/build/generated/clinic_bridge_file.js +13 -0
- package/build/generated/contracts_bundle_info.js +5 -0
- package/build/generated/create_work_order_input.js +2 -0
- package/build/generated/create_work_order_output.js +2 -0
- package/build/generated/current_work_order_file.js +2 -0
- package/build/generated/doctor_input.js +2 -0
- package/build/generated/doctor_output.js +24 -0
- package/build/generated/execution_result.js +2 -0
- package/build/generated/execution_task.js +2 -0
- package/build/generated/export_output_input.js +2 -0
- package/build/generated/export_output_output.js +2 -0
- package/build/generated/finalize_work_input.js +2 -0
- package/build/generated/finalize_work_output.js +2 -0
- package/build/generated/gate_input.js +2 -0
- package/build/generated/gate_output.js +2 -0
- package/build/generated/gate_result_v1.js +2 -0
- package/build/generated/get_decision_input.js +2 -0
- package/build/generated/get_decision_output.js +13 -0
- package/build/generated/handoff_to_clinic.js +2 -0
- package/build/generated/index.js +75 -0
- package/build/generated/inspect_code_input.js +2 -0
- package/build/generated/inspect_code_output.js +13 -0
- package/build/generated/memory_retrieve_output.js +2 -0
- package/build/generated/memory_state_file.js +2 -0
- package/build/generated/memory_status_input.js +2 -0
- package/build/generated/memory_status_output.js +13 -0
- package/build/generated/memory_sync_input.js +2 -0
- package/build/generated/memory_sync_output.js +13 -0
- package/build/generated/plugin_result.js +2 -0
- package/build/generated/react_perf_check_patterns_input.js +2 -0
- package/build/generated/react_perf_check_patterns_output.js +2 -0
- package/build/generated/react_perf_generate_report_input.js +2 -0
- package/build/generated/react_perf_generate_report_output.js +2 -0
- package/build/generated/repair_plan_input.js +2 -0
- package/build/generated/repair_plan_output.js +2 -0
- package/build/generated/run_app_input.js +2 -0
- package/build/generated/run_app_output.js +2 -0
- package/build/generated/run_state_file.js +13 -0
- package/build/generated/scaffold_input.js +2 -0
- package/build/generated/scaffold_output.js +2 -0
- package/build/generated/search_oss_input.js +2 -0
- package/build/generated/search_oss_output.js +2 -0
- package/build/generated/selection_validation_result.js +2 -0
- package/build/generated/signal_agent_input.js +2 -0
- package/build/generated/spec_high_ask_queue_items_file.js +2 -0
- package/build/generated/spec_high_clinic_bridge_output.js +2 -0
- package/build/generated/spec_high_decision_draft_output.js +2 -0
- package/build/generated/spec_high_validate_output.js +2 -0
- package/build/generated/status_input.js +2 -0
- package/build/generated/status_output.js +2 -0
- package/build/generated/submit_decision_input.js +2 -0
- package/build/generated/submit_decision_output.js +2 -0
- package/build/generated/tool_error_output.js +2 -0
- package/build/generated/undo_last_task_input.js +2 -0
- package/build/generated/undo_last_task_output.js +2 -0
- package/build/generated/update_input.js +2 -0
- package/build/generated/update_output.js +2 -0
- package/build/generated/vibe_pm_inspection_result.js +2 -0
- package/build/generated/vibe_pm_report_markdown.js +2 -0
- package/build/generated/vibe_pm_verdict.js +2 -0
- package/build/generated/vibe_repo_config.js +2 -0
- package/build/generated/vibecoding_helper_answer_output.js +2 -0
- package/build/generated/vibecoding_helper_one_loop_selection_output.js +2 -0
- package/build/generated/vibecoding_helper_show_ask_queue_output.js +2 -0
- package/build/generated/work_order_v1.js +2 -0
- package/build/generated/zoekt_evidence_input.js +2 -0
- package/build/generated/zoekt_evidence_output.js +2 -0
- package/build/index.js +111 -0
- package/build/legacy_alias.js +65 -0
- package/build/local-mode/bash.js +61 -0
- package/build/local-mode/config.js +171 -0
- package/build/local-mode/git.js +33 -0
- package/build/local-mode/init.js +110 -0
- package/build/local-mode/paths.js +24 -0
- package/build/local-mode/templates.js +856 -0
- package/build/local-mode/work-order.js +41 -0
- package/build/resources/index.js +246 -0
- package/build/security/input-validator.js +119 -0
- package/build/security/path-policy.js +289 -0
- package/build/security/sandbox.js +228 -0
- package/build/tools/react_perf/check_patterns.js +172 -0
- package/build/tools/react_perf/generate_report.js +337 -0
- package/build/tools/react_perf/index.js +119 -0
- package/build/tools/react_perf/rules/advanced.js +325 -0
- package/build/tools/react_perf/rules/async.js +104 -0
- package/build/tools/react_perf/rules/bundle.js +101 -0
- package/build/tools/react_perf/rules/client.js +186 -0
- package/build/tools/react_perf/rules/index.js +74 -0
- package/build/tools/react_perf/rules/js.js +148 -0
- package/build/tools/react_perf/rules/rendering.js +166 -0
- package/build/tools/react_perf/rules/rerender.js +161 -0
- package/build/tools/react_perf/rules/server.js +141 -0
- package/build/tools/react_perf/types.js +127 -0
- package/build/tools/vibe_pm/activate.js +102 -0
- package/build/tools/vibe_pm/advisory_review.js +77 -0
- package/build/tools/vibe_pm/briefing.js +178 -0
- package/build/tools/vibe_pm/context.js +439 -0
- package/build/tools/vibe_pm/create_work_order.js +271 -0
- package/build/tools/vibe_pm/doc_status_gate.js +370 -0
- package/build/tools/vibe_pm/doctor.js +262 -0
- package/build/tools/vibe_pm/entity_gate/preflight.js +78 -0
- package/build/tools/vibe_pm/export_output.js +135 -0
- package/build/tools/vibe_pm/finalize_work.js +393 -0
- package/build/tools/vibe_pm/gate.js +33 -0
- package/build/tools/vibe_pm/get_decision.js +281 -0
- package/build/tools/vibe_pm/index.js +593 -0
- package/build/tools/vibe_pm/inspect_code.js +828 -0
- package/build/tools/vibe_pm/intent/generator.js +294 -0
- package/build/tools/vibe_pm/intent/index.js +5 -0
- package/build/tools/vibe_pm/intent/prompt_density.js +227 -0
- package/build/tools/vibe_pm/intent/types.js +70 -0
- package/build/tools/vibe_pm/intent/verifier.js +237 -0
- package/build/tools/vibe_pm/kce/doc_usage.js +51 -0
- package/build/tools/vibe_pm/kce/on_finalize.js +11 -0
- package/build/tools/vibe_pm/kce/preflight.js +232 -0
- package/build/tools/vibe_pm/local_memory.js +26 -0
- package/build/tools/vibe_pm/memory_status.js +82 -0
- package/build/tools/vibe_pm/memory_sync.js +134 -0
- package/build/tools/vibe_pm/modules/decision_snapshot.js +29 -0
- package/build/tools/vibe_pm/modules/ensure.js +100 -0
- package/build/tools/vibe_pm/modules/fingerprint.js +30 -0
- package/build/tools/vibe_pm/modules/fix_dependencies.js +394 -0
- package/build/tools/vibe_pm/modules/planning_v1.js +110 -0
- package/build/tools/vibe_pm/modules/repo_context.js +56 -0
- package/build/tools/vibe_pm/modules/research_v1.js +114 -0
- package/build/tools/vibe_pm/modules/skills_v1.js +100 -0
- package/build/tools/vibe_pm/pm_language.js +222 -0
- package/build/tools/vibe_pm/repair_plan.js +199 -0
- package/build/tools/vibe_pm/run_app.js +597 -0
- package/build/tools/vibe_pm/run_app_podman.js +64 -0
- package/build/tools/vibe_pm/scaffold.js +550 -0
- package/build/tools/vibe_pm/search_oss.js +124 -0
- package/build/tools/vibe_pm/status.js +153 -0
- package/build/tools/vibe_pm/submit_decision.js +87 -0
- package/build/tools/vibe_pm/system_design/issue_mapping.js +47 -0
- package/build/tools/vibe_pm/system_design/rulebook.js +112 -0
- package/build/tools/vibe_pm/system_design/semgrep.js +132 -0
- package/build/tools/vibe_pm/types.js +229 -0
- package/build/tools/vibe_pm/undo_last_task.js +163 -0
- package/build/tools/vibe_pm/update.js +146 -0
- package/build/tools/vibe_pm/zoekt_evidence.js +96 -0
- package/build/tools.js +269 -0
- package/build/version-check.js +239 -0
- package/build/vibe-cli.js +631 -0
- package/package.json +76 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// adapters/mcp-ts/src/tools/vibe_pm/memory_sync.ts
|
|
2
|
+
// vibe_pm.memory_sync - Local Memory sync (incremental index update)
|
|
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 { MemorySyncOutputSchema } from "../../generated/memory_sync_output.js";
|
|
8
|
+
const inFlight = new Map();
|
|
9
|
+
function isOfflineMode() {
|
|
10
|
+
const v = (process.env.VIBECODE_OFFLINE ?? "").trim().toLowerCase();
|
|
11
|
+
return v === "1" || v === "true" || v === "yes" || v === "on";
|
|
12
|
+
}
|
|
13
|
+
function defaultPersistDir() {
|
|
14
|
+
return ".vibe/chroma";
|
|
15
|
+
}
|
|
16
|
+
function inFlightKey(args) {
|
|
17
|
+
return `${args.project_id}::${args.docs_root}::${args.persist_dir}::${args.mode}::${args.force_full ? "1" : "0"}`;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* vibe_pm.memory_sync - Local Memory sync
|
|
21
|
+
*
|
|
22
|
+
* Internal mapping:
|
|
23
|
+
* → vibecoding-helper memory-sync --project-id ... --docs-root ... --mode ... --force-full ...
|
|
24
|
+
*/
|
|
25
|
+
export async function memorySync(input) {
|
|
26
|
+
const basePath = process.cwd();
|
|
27
|
+
validateToolInput({
|
|
28
|
+
project_id: input.project_id,
|
|
29
|
+
custom_input: input.docs_root,
|
|
30
|
+
});
|
|
31
|
+
const project_id = input.project_id ?? resolveProjectId(undefined, basePath);
|
|
32
|
+
const docsRoot = (input.docs_root ?? "docs").trim();
|
|
33
|
+
const persistDir = defaultPersistDir();
|
|
34
|
+
const mode = input.mode ?? "on_demand";
|
|
35
|
+
const forceFull = input.force_full ?? false;
|
|
36
|
+
const key = inFlightKey({
|
|
37
|
+
project_id,
|
|
38
|
+
docs_root: docsRoot,
|
|
39
|
+
persist_dir: persistDir,
|
|
40
|
+
mode,
|
|
41
|
+
force_full: forceFull,
|
|
42
|
+
});
|
|
43
|
+
const existing = inFlight.get(key);
|
|
44
|
+
if (existing)
|
|
45
|
+
return existing;
|
|
46
|
+
const promise = (async () => {
|
|
47
|
+
try {
|
|
48
|
+
const args = [
|
|
49
|
+
"memory-sync",
|
|
50
|
+
"--project-id",
|
|
51
|
+
project_id,
|
|
52
|
+
"--docs-root",
|
|
53
|
+
docsRoot,
|
|
54
|
+
"--mode",
|
|
55
|
+
mode,
|
|
56
|
+
"--persist-dir",
|
|
57
|
+
persistDir
|
|
58
|
+
];
|
|
59
|
+
if (forceFull)
|
|
60
|
+
args.push("--force-full");
|
|
61
|
+
const startedAt = Date.now();
|
|
62
|
+
const { code, stdout, stderr } = await runEngine("vibecoding-helper", args, { timeoutMs: 300_000 });
|
|
63
|
+
const durationMs = Date.now() - startedAt;
|
|
64
|
+
if (code !== 0) {
|
|
65
|
+
return MemorySyncOutputSchema.parse({
|
|
66
|
+
project_id,
|
|
67
|
+
status: "ERROR",
|
|
68
|
+
summary: "Local Memory 동기화에 실패했습니다.",
|
|
69
|
+
docs_root: docsRoot,
|
|
70
|
+
persist_dir: persistDir,
|
|
71
|
+
collection: "vibe_docs",
|
|
72
|
+
result: {
|
|
73
|
+
scanned_files: 0,
|
|
74
|
+
ingested_files: 0,
|
|
75
|
+
updated_files: 0,
|
|
76
|
+
skipped_files: 0,
|
|
77
|
+
duration_ms: durationMs
|
|
78
|
+
},
|
|
79
|
+
offline_mode: isOfflineMode(),
|
|
80
|
+
issues: [stderr?.trim() ? `engine_error: ${stderr.trim()}` : `engine_exit_code: ${code}`],
|
|
81
|
+
next_action: { tool: "vibe_pm.doctor", reason: "설치/환경 상태를 점검하겠습니다." }
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
const parsed = safeJsonParse(stdout);
|
|
85
|
+
if (!parsed.ok) {
|
|
86
|
+
return MemorySyncOutputSchema.parse({
|
|
87
|
+
project_id,
|
|
88
|
+
status: "ERROR",
|
|
89
|
+
summary: "Local Memory 동기화 응답을 해석할 수 없습니다.",
|
|
90
|
+
docs_root: docsRoot,
|
|
91
|
+
persist_dir: persistDir,
|
|
92
|
+
collection: "vibe_docs",
|
|
93
|
+
result: {
|
|
94
|
+
scanned_files: 0,
|
|
95
|
+
ingested_files: 0,
|
|
96
|
+
updated_files: 0,
|
|
97
|
+
skipped_files: 0,
|
|
98
|
+
duration_ms: durationMs
|
|
99
|
+
},
|
|
100
|
+
offline_mode: isOfflineMode(),
|
|
101
|
+
issues: [`invalid_json: ${parsed.error}`],
|
|
102
|
+
next_action: { tool: "vibe_pm.doctor", reason: "설치/환경 상태를 점검하겠습니다." }
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return MemorySyncOutputSchema.parse(parsed.value);
|
|
106
|
+
}
|
|
107
|
+
catch (e) {
|
|
108
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
109
|
+
return MemorySyncOutputSchema.parse({
|
|
110
|
+
project_id,
|
|
111
|
+
status: "ERROR",
|
|
112
|
+
summary: "Local Memory 동기화 중 오류가 발생했습니다.",
|
|
113
|
+
docs_root: docsRoot,
|
|
114
|
+
persist_dir: persistDir,
|
|
115
|
+
collection: "vibe_docs",
|
|
116
|
+
result: {
|
|
117
|
+
scanned_files: 0,
|
|
118
|
+
ingested_files: 0,
|
|
119
|
+
updated_files: 0,
|
|
120
|
+
skipped_files: 0,
|
|
121
|
+
duration_ms: 0
|
|
122
|
+
},
|
|
123
|
+
offline_mode: isOfflineMode(),
|
|
124
|
+
issues: [msg],
|
|
125
|
+
next_action: { tool: "vibe_pm.doctor", reason: "설치/환경 상태를 점검하겠습니다." }
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
})();
|
|
129
|
+
inFlight.set(key, promise);
|
|
130
|
+
promise.finally(() => {
|
|
131
|
+
inFlight.delete(key);
|
|
132
|
+
});
|
|
133
|
+
return promise;
|
|
134
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
export function loadDecisionSnapshot(specHighRunDir) {
|
|
4
|
+
const p = path.join(specHighRunDir, "decisions", "decision_cards.json");
|
|
5
|
+
if (!fs.existsSync(p))
|
|
6
|
+
return [];
|
|
7
|
+
try {
|
|
8
|
+
const raw = fs.readFileSync(p, "utf-8");
|
|
9
|
+
const doc = JSON.parse(raw);
|
|
10
|
+
const cards = doc.cards ?? [];
|
|
11
|
+
const resolved = cards.filter((c) => c.resolution?.status === "resolved" && c.resolution?.chosen);
|
|
12
|
+
return resolved
|
|
13
|
+
.map((c) => {
|
|
14
|
+
const chosen = c.resolution?.chosen ?? null;
|
|
15
|
+
const chosenLabel = c.options?.find((o) => o.id === chosen)?.label;
|
|
16
|
+
return {
|
|
17
|
+
decision_id: String(c.id ?? ""),
|
|
18
|
+
category: c.category,
|
|
19
|
+
question: c.question,
|
|
20
|
+
chosen: chosen ? String(chosen) : undefined,
|
|
21
|
+
chosen_label: chosenLabel,
|
|
22
|
+
};
|
|
23
|
+
})
|
|
24
|
+
.filter((x) => x.decision_id);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { getSpecHighRunsDir } from "../context.js";
|
|
4
|
+
import { detectRepoContext } from "./repo_context.js";
|
|
5
|
+
import { loadDecisionSnapshot } from "./decision_snapshot.js";
|
|
6
|
+
import { ensureResearchModuleV1 } from "./research_v1.js";
|
|
7
|
+
import { generateSkillsModuleV1 } from "./skills_v1.js";
|
|
8
|
+
import { generatePlanningModuleV1 } from "./planning_v1.js";
|
|
9
|
+
function ensureDir(p) {
|
|
10
|
+
fs.mkdirSync(p, { recursive: true });
|
|
11
|
+
}
|
|
12
|
+
export function getWritableSpecHighRunDir(runId, basePath) {
|
|
13
|
+
const runsRoot = getSpecHighRunsDir(basePath);
|
|
14
|
+
const runDir = path.join(runsRoot, runId);
|
|
15
|
+
ensureDir(runDir);
|
|
16
|
+
return runDir;
|
|
17
|
+
}
|
|
18
|
+
export function writeProjectBriefSnapshot(specHighRunDir, projectBrief) {
|
|
19
|
+
const dir = path.join(specHighRunDir, "modules", "_inputs");
|
|
20
|
+
ensureDir(dir);
|
|
21
|
+
fs.writeFileSync(path.join(dir, "project_brief.txt"), projectBrief.trim() + "\n", "utf-8");
|
|
22
|
+
}
|
|
23
|
+
export function ensureModulesForWorkOrder(opts) {
|
|
24
|
+
const specHighRunDir = getWritableSpecHighRunDir(opts.runId, opts.basePath);
|
|
25
|
+
const repoContext = detectRepoContext(opts.basePath);
|
|
26
|
+
const decisionSnapshot = loadDecisionSnapshot(specHighRunDir);
|
|
27
|
+
if (opts.projectBrief) {
|
|
28
|
+
writeProjectBriefSnapshot(specHighRunDir, opts.projectBrief);
|
|
29
|
+
}
|
|
30
|
+
const research = ensureResearchModuleV1({
|
|
31
|
+
repoRoot: opts.basePath,
|
|
32
|
+
specHighRunDir,
|
|
33
|
+
repoContext,
|
|
34
|
+
decisionSnapshot,
|
|
35
|
+
projectBrief: opts.projectBrief,
|
|
36
|
+
});
|
|
37
|
+
const skills = generateSkillsModuleV1({ repoRoot: opts.basePath, specHighRunDir, repoContext });
|
|
38
|
+
const planning = generatePlanningModuleV1({
|
|
39
|
+
specHighRunDir,
|
|
40
|
+
goal: "implement",
|
|
41
|
+
repoContext,
|
|
42
|
+
skillFingerprint: skills.fingerprint,
|
|
43
|
+
decisionSnapshot,
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
research: { version: "v1", fingerprint: research.fingerprint, path: research.path },
|
|
47
|
+
skills: { version: "v1", fingerprint: skills.fingerprint, path: skills.path },
|
|
48
|
+
planning: { version: "v1", fingerprint: planning.fingerprint, path: planning.path },
|
|
49
|
+
skill_bundle_prompt_path: skills.bundle_prompt_path,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export function ensureResearchForBriefing(opts) {
|
|
53
|
+
const specHighRunDir = getWritableSpecHighRunDir(opts.runId, opts.basePath);
|
|
54
|
+
const repoContext = detectRepoContext(opts.basePath);
|
|
55
|
+
writeProjectBriefSnapshot(specHighRunDir, opts.projectBrief);
|
|
56
|
+
const research = ensureResearchModuleV1({
|
|
57
|
+
repoRoot: opts.basePath,
|
|
58
|
+
specHighRunDir,
|
|
59
|
+
repoContext,
|
|
60
|
+
decisionSnapshot: [],
|
|
61
|
+
projectBrief: opts.projectBrief,
|
|
62
|
+
});
|
|
63
|
+
return { version: "v1", fingerprint: research.fingerprint, path: research.path };
|
|
64
|
+
}
|
|
65
|
+
export function ensurePlanningForRepair(opts) {
|
|
66
|
+
const specHighRunDir = getWritableSpecHighRunDir(opts.runId, opts.basePath);
|
|
67
|
+
const repoContext = detectRepoContext(opts.basePath);
|
|
68
|
+
const decisionSnapshot = loadDecisionSnapshot(specHighRunDir);
|
|
69
|
+
// Try to reuse existing skill fingerprint if present
|
|
70
|
+
const skillsPath = path.join(specHighRunDir, "modules", "skills", "v1", "selection.json");
|
|
71
|
+
let skillFingerprint = "SKL-unknown";
|
|
72
|
+
if (fs.existsSync(skillsPath)) {
|
|
73
|
+
try {
|
|
74
|
+
const doc = JSON.parse(fs.readFileSync(skillsPath, "utf-8"));
|
|
75
|
+
if (doc.fingerprint)
|
|
76
|
+
skillFingerprint = String(doc.fingerprint);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// ignore
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const planning = generatePlanningModuleV1({
|
|
83
|
+
specHighRunDir,
|
|
84
|
+
goal: opts.goal ?? "repair",
|
|
85
|
+
repoContext,
|
|
86
|
+
skillFingerprint,
|
|
87
|
+
decisionSnapshot,
|
|
88
|
+
});
|
|
89
|
+
return { version: "v1", fingerprint: planning.fingerprint, path: planning.path };
|
|
90
|
+
}
|
|
91
|
+
export function readFileIfExists(p) {
|
|
92
|
+
try {
|
|
93
|
+
if (!fs.existsSync(p))
|
|
94
|
+
return null;
|
|
95
|
+
return fs.readFileSync(p, "utf-8");
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
function stableSerialize(value) {
|
|
3
|
+
if (value === null)
|
|
4
|
+
return "null";
|
|
5
|
+
const t = typeof value;
|
|
6
|
+
if (t === "string")
|
|
7
|
+
return JSON.stringify(value);
|
|
8
|
+
if (t === "number")
|
|
9
|
+
return Number.isFinite(value) ? String(value) : JSON.stringify(value);
|
|
10
|
+
if (t === "boolean")
|
|
11
|
+
return value ? "true" : "false";
|
|
12
|
+
if (Array.isArray(value))
|
|
13
|
+
return `[${value.map(stableSerialize).join(",")}]`;
|
|
14
|
+
if (t === "object") {
|
|
15
|
+
const obj = value;
|
|
16
|
+
const keys = Object.keys(obj).sort();
|
|
17
|
+
return `{${keys
|
|
18
|
+
.map((k) => `${JSON.stringify(k)}:${stableSerialize(obj[k])}`)
|
|
19
|
+
.join(",")}}`;
|
|
20
|
+
}
|
|
21
|
+
return JSON.stringify(String(value));
|
|
22
|
+
}
|
|
23
|
+
export function sha256Hex(input) {
|
|
24
|
+
return createHash("sha256").update(input, "utf8").digest("hex");
|
|
25
|
+
}
|
|
26
|
+
export function fingerprintJson(prefix, input) {
|
|
27
|
+
const canonical = stableSerialize(input);
|
|
28
|
+
const hex = sha256Hex(canonical);
|
|
29
|
+
return `${prefix}-${hex.slice(0, 12)}`;
|
|
30
|
+
}
|
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
// adapters/mcp-ts/src/tools/vibe_pm/modules/fix_dependencies.ts
|
|
2
|
+
// Auto-fix module for dependency issues
|
|
3
|
+
import * as fs from "node:fs";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
/**
|
|
6
|
+
* Fix dependency issues in the project
|
|
7
|
+
*
|
|
8
|
+
* 1. Detect project type (Node.js vs Python)
|
|
9
|
+
* 2. Extract imports from code
|
|
10
|
+
* 3. Parse dependency file
|
|
11
|
+
* 4. Calculate missing packages
|
|
12
|
+
* 5. Add missing packages to dependency file
|
|
13
|
+
*/
|
|
14
|
+
export async function fixDependencies(basePath) {
|
|
15
|
+
const fixed = [];
|
|
16
|
+
const errors = [];
|
|
17
|
+
try {
|
|
18
|
+
const checkResult = await checkDependencies(basePath);
|
|
19
|
+
if (checkResult.projectType === "unknown") {
|
|
20
|
+
return { fixed, errors: ["프로젝트 타입을 감지할 수 없습니다"] };
|
|
21
|
+
}
|
|
22
|
+
if (checkResult.missingPackages.length === 0) {
|
|
23
|
+
return { fixed, errors: [] };
|
|
24
|
+
}
|
|
25
|
+
if (!checkResult.dependencyFile) {
|
|
26
|
+
return { fixed, errors: ["의존성 파일을 찾을 수 없습니다"] };
|
|
27
|
+
}
|
|
28
|
+
// Fix missing dependencies
|
|
29
|
+
if (checkResult.projectType === "node") {
|
|
30
|
+
const fixedPackages = await fixNodeDependencies(checkResult.dependencyFile, checkResult.missingPackages);
|
|
31
|
+
for (const pkg of fixedPackages) {
|
|
32
|
+
fixed.push({
|
|
33
|
+
type: "MISSING_DEPENDENCY",
|
|
34
|
+
description: `누락된 패키지 '${pkg}'를 package.json에 추가했습니다`,
|
|
35
|
+
file: checkResult.dependencyFile,
|
|
36
|
+
status: "FIXED"
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else if (checkResult.projectType === "python") {
|
|
41
|
+
const fixedPackages = await fixPythonDependencies(checkResult.dependencyFile, checkResult.missingPackages);
|
|
42
|
+
for (const pkg of fixedPackages) {
|
|
43
|
+
fixed.push({
|
|
44
|
+
type: "MISSING_DEPENDENCY",
|
|
45
|
+
description: `누락된 패키지 '${pkg}'를 requirements.txt에 추가했습니다`,
|
|
46
|
+
file: checkResult.dependencyFile,
|
|
47
|
+
status: "FIXED"
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
errors.push(err instanceof Error ? err.message : String(err));
|
|
54
|
+
}
|
|
55
|
+
return { fixed, errors };
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Check for missing dependencies without fixing
|
|
59
|
+
*/
|
|
60
|
+
export async function checkDependencies(basePath) {
|
|
61
|
+
const packageJsonPath = path.join(basePath, "package.json");
|
|
62
|
+
const requirementsPath = path.join(basePath, "requirements.txt");
|
|
63
|
+
// Detect project type
|
|
64
|
+
const hasPackageJson = fs.existsSync(packageJsonPath);
|
|
65
|
+
const hasRequirements = fs.existsSync(requirementsPath);
|
|
66
|
+
if (hasPackageJson) {
|
|
67
|
+
const imports = await extractNodeImports(basePath);
|
|
68
|
+
const declared = parsePackageJsonDeps(packageJsonPath);
|
|
69
|
+
const missing = calculateMissingNodePackages(imports, declared);
|
|
70
|
+
return {
|
|
71
|
+
projectType: "node",
|
|
72
|
+
missingPackages: missing,
|
|
73
|
+
dependencyFile: packageJsonPath
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
if (hasRequirements) {
|
|
77
|
+
const imports = await extractPythonImports(basePath);
|
|
78
|
+
const declared = parseRequirementsTxt(requirementsPath);
|
|
79
|
+
const missing = calculateMissingPythonPackages(imports, declared);
|
|
80
|
+
return {
|
|
81
|
+
projectType: "python",
|
|
82
|
+
missingPackages: missing,
|
|
83
|
+
dependencyFile: requirementsPath
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
projectType: "unknown",
|
|
88
|
+
missingPackages: [],
|
|
89
|
+
dependencyFile: null
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Extract Node.js imports from source files
|
|
94
|
+
*/
|
|
95
|
+
async function extractNodeImports(basePath) {
|
|
96
|
+
const imports = new Set();
|
|
97
|
+
// Patterns for import extraction
|
|
98
|
+
const importPatterns = [
|
|
99
|
+
/import\s+.*?\s+from\s+['"]([^'"./][^'"]*)['"]/g,
|
|
100
|
+
/import\s+['"]([^'"./][^'"]*)['"]/g,
|
|
101
|
+
/require\s*\(\s*['"]([^'"./][^'"]*)['"]\s*\)/g
|
|
102
|
+
];
|
|
103
|
+
// Find all JS/TS files
|
|
104
|
+
const files = findSourceFiles(basePath, [".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs"]);
|
|
105
|
+
for (const file of files) {
|
|
106
|
+
try {
|
|
107
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
108
|
+
for (const pattern of importPatterns) {
|
|
109
|
+
let match;
|
|
110
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
111
|
+
const pkg = extractPackageName(match[1]);
|
|
112
|
+
if (pkg && !isNodeBuiltin(pkg)) {
|
|
113
|
+
imports.add(pkg);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
pattern.lastIndex = 0; // Reset regex state
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// Skip files that can't be read
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return imports;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Extract Python imports from source files
|
|
127
|
+
*/
|
|
128
|
+
async function extractPythonImports(basePath) {
|
|
129
|
+
const imports = new Set();
|
|
130
|
+
// Patterns for import extraction
|
|
131
|
+
const importPatterns = [
|
|
132
|
+
/^import\s+(\w+)/gm,
|
|
133
|
+
/^from\s+(\w+)/gm
|
|
134
|
+
];
|
|
135
|
+
// Find all Python files
|
|
136
|
+
const files = findSourceFiles(basePath, [".py"]);
|
|
137
|
+
for (const file of files) {
|
|
138
|
+
try {
|
|
139
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
140
|
+
for (const pattern of importPatterns) {
|
|
141
|
+
let match;
|
|
142
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
143
|
+
const pkg = match[1];
|
|
144
|
+
if (pkg && !isPythonBuiltin(pkg)) {
|
|
145
|
+
imports.add(pkg);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
pattern.lastIndex = 0;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// Skip files that can't be read
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return imports;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Find source files recursively
|
|
159
|
+
*/
|
|
160
|
+
function findSourceFiles(basePath, extensions) {
|
|
161
|
+
const files = [];
|
|
162
|
+
const ignoreDirs = ["node_modules", ".git", "__pycache__", ".venv", "venv", "dist", "build"];
|
|
163
|
+
function walk(dir) {
|
|
164
|
+
try {
|
|
165
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
166
|
+
for (const entry of entries) {
|
|
167
|
+
const fullPath = path.join(dir, entry.name);
|
|
168
|
+
if (entry.isDirectory()) {
|
|
169
|
+
if (!ignoreDirs.includes(entry.name) && !entry.name.startsWith(".")) {
|
|
170
|
+
walk(fullPath);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
else if (entry.isFile()) {
|
|
174
|
+
const ext = path.extname(entry.name);
|
|
175
|
+
if (extensions.includes(ext)) {
|
|
176
|
+
files.push(fullPath);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
// Skip directories that can't be read
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
walk(basePath);
|
|
186
|
+
return files;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Extract package name from import path
|
|
190
|
+
* e.g., "@types/node" -> "@types/node", "lodash/fp" -> "lodash"
|
|
191
|
+
*/
|
|
192
|
+
function extractPackageName(importPath) {
|
|
193
|
+
if (!importPath)
|
|
194
|
+
return null;
|
|
195
|
+
// Handle scoped packages (@org/package)
|
|
196
|
+
if (importPath.startsWith("@")) {
|
|
197
|
+
const parts = importPath.split("/");
|
|
198
|
+
if (parts.length >= 2) {
|
|
199
|
+
return `${parts[0]}/${parts[1]}`;
|
|
200
|
+
}
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
// Handle regular packages
|
|
204
|
+
return importPath.split("/")[0];
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Parse package.json dependencies
|
|
208
|
+
*/
|
|
209
|
+
function parsePackageJsonDeps(packageJsonPath) {
|
|
210
|
+
const deps = new Set();
|
|
211
|
+
try {
|
|
212
|
+
const content = fs.readFileSync(packageJsonPath, "utf-8");
|
|
213
|
+
const pkg = JSON.parse(content);
|
|
214
|
+
if (pkg.dependencies) {
|
|
215
|
+
for (const dep of Object.keys(pkg.dependencies)) {
|
|
216
|
+
deps.add(dep);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (pkg.devDependencies) {
|
|
220
|
+
for (const dep of Object.keys(pkg.devDependencies)) {
|
|
221
|
+
deps.add(dep);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (pkg.peerDependencies) {
|
|
225
|
+
for (const dep of Object.keys(pkg.peerDependencies)) {
|
|
226
|
+
deps.add(dep);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
// Return empty set on error
|
|
232
|
+
}
|
|
233
|
+
return deps;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Parse requirements.txt dependencies
|
|
237
|
+
*/
|
|
238
|
+
function parseRequirementsTxt(requirementsPath) {
|
|
239
|
+
const deps = new Set();
|
|
240
|
+
try {
|
|
241
|
+
const content = fs.readFileSync(requirementsPath, "utf-8");
|
|
242
|
+
const lines = content.split("\n");
|
|
243
|
+
for (const line of lines) {
|
|
244
|
+
const trimmed = line.trim();
|
|
245
|
+
if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("-")) {
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
// Extract package name (before version specifier)
|
|
249
|
+
const match = trimmed.match(/^([a-zA-Z0-9_-]+)/);
|
|
250
|
+
if (match) {
|
|
251
|
+
deps.add(match[1].toLowerCase());
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
// Return empty set on error
|
|
257
|
+
}
|
|
258
|
+
return deps;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Calculate missing Node.js packages
|
|
262
|
+
*/
|
|
263
|
+
function calculateMissingNodePackages(imports, declared) {
|
|
264
|
+
const missing = [];
|
|
265
|
+
for (const pkg of imports) {
|
|
266
|
+
if (!declared.has(pkg)) {
|
|
267
|
+
missing.push(pkg);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return missing;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Calculate missing Python packages
|
|
274
|
+
*/
|
|
275
|
+
function calculateMissingPythonPackages(imports, declared) {
|
|
276
|
+
const missing = [];
|
|
277
|
+
for (const pkg of imports) {
|
|
278
|
+
const normalized = pkg.toLowerCase();
|
|
279
|
+
if (!declared.has(normalized)) {
|
|
280
|
+
missing.push(normalized);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return missing;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Fix Node.js dependencies by adding to package.json
|
|
287
|
+
*/
|
|
288
|
+
async function fixNodeDependencies(packageJsonPath, missing) {
|
|
289
|
+
const fixed = [];
|
|
290
|
+
try {
|
|
291
|
+
const content = fs.readFileSync(packageJsonPath, "utf-8");
|
|
292
|
+
const pkg = JSON.parse(content);
|
|
293
|
+
if (!pkg.dependencies) {
|
|
294
|
+
pkg.dependencies = {};
|
|
295
|
+
}
|
|
296
|
+
for (const pkgName of missing) {
|
|
297
|
+
// Add with "*" version (user should run npm install to get proper version)
|
|
298
|
+
pkg.dependencies[pkgName] = "*";
|
|
299
|
+
fixed.push(pkgName);
|
|
300
|
+
}
|
|
301
|
+
// Sort dependencies alphabetically
|
|
302
|
+
pkg.dependencies = Object.fromEntries(Object.entries(pkg.dependencies).sort(([a], [b]) => a.localeCompare(b)));
|
|
303
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
304
|
+
}
|
|
305
|
+
catch {
|
|
306
|
+
// Return what we fixed so far
|
|
307
|
+
}
|
|
308
|
+
return fixed;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Fix Python dependencies by adding to requirements.txt
|
|
312
|
+
*/
|
|
313
|
+
async function fixPythonDependencies(requirementsPath, missing) {
|
|
314
|
+
const fixed = [];
|
|
315
|
+
try {
|
|
316
|
+
let content = "";
|
|
317
|
+
if (fs.existsSync(requirementsPath)) {
|
|
318
|
+
content = fs.readFileSync(requirementsPath, "utf-8");
|
|
319
|
+
if (!content.endsWith("\n")) {
|
|
320
|
+
content += "\n";
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
for (const pkg of missing) {
|
|
324
|
+
content += `${pkg}\n`;
|
|
325
|
+
fixed.push(pkg);
|
|
326
|
+
}
|
|
327
|
+
fs.writeFileSync(requirementsPath, content);
|
|
328
|
+
}
|
|
329
|
+
catch {
|
|
330
|
+
// Return what we fixed so far
|
|
331
|
+
}
|
|
332
|
+
return fixed;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Check if package is a Node.js builtin module
|
|
336
|
+
*/
|
|
337
|
+
function isNodeBuiltin(pkg) {
|
|
338
|
+
const builtins = new Set([
|
|
339
|
+
"assert", "buffer", "child_process", "cluster", "console", "constants",
|
|
340
|
+
"crypto", "dgram", "dns", "domain", "events", "fs", "http", "https",
|
|
341
|
+
"module", "net", "os", "path", "perf_hooks", "process", "punycode",
|
|
342
|
+
"querystring", "readline", "repl", "stream", "string_decoder", "sys",
|
|
343
|
+
"timers", "tls", "tty", "url", "util", "v8", "vm", "wasi", "worker_threads", "zlib",
|
|
344
|
+
// Node.js built-in module prefixes
|
|
345
|
+
"node:assert", "node:buffer", "node:child_process", "node:cluster",
|
|
346
|
+
"node:console", "node:constants", "node:crypto", "node:dgram",
|
|
347
|
+
"node:dns", "node:domain", "node:events", "node:fs", "node:http",
|
|
348
|
+
"node:https", "node:module", "node:net", "node:os", "node:path",
|
|
349
|
+
"node:perf_hooks", "node:process", "node:punycode", "node:querystring",
|
|
350
|
+
"node:readline", "node:repl", "node:stream", "node:string_decoder",
|
|
351
|
+
"node:sys", "node:timers", "node:tls", "node:tty", "node:url",
|
|
352
|
+
"node:util", "node:v8", "node:vm", "node:wasi", "node:worker_threads", "node:zlib"
|
|
353
|
+
]);
|
|
354
|
+
return builtins.has(pkg) || pkg.startsWith("node:");
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Check if package is a Python builtin module
|
|
358
|
+
*/
|
|
359
|
+
function isPythonBuiltin(pkg) {
|
|
360
|
+
const builtins = new Set([
|
|
361
|
+
"abc", "aifc", "argparse", "array", "ast", "asynchat", "asyncio",
|
|
362
|
+
"asyncore", "atexit", "audioop", "base64", "bdb", "binascii", "binhex",
|
|
363
|
+
"bisect", "builtins", "bz2", "calendar", "cgi", "cgitb", "chunk",
|
|
364
|
+
"cmath", "cmd", "code", "codecs", "codeop", "collections", "colorsys",
|
|
365
|
+
"compileall", "concurrent", "configparser", "contextlib", "contextvars",
|
|
366
|
+
"copy", "copyreg", "cProfile", "crypt", "csv", "ctypes", "curses",
|
|
367
|
+
"dataclasses", "datetime", "dbm", "decimal", "difflib", "dis",
|
|
368
|
+
"distutils", "doctest", "email", "encodings", "enum", "errno",
|
|
369
|
+
"faulthandler", "fcntl", "filecmp", "fileinput", "fnmatch", "fractions",
|
|
370
|
+
"ftplib", "functools", "gc", "getopt", "getpass", "gettext", "glob",
|
|
371
|
+
"graphlib", "grp", "gzip", "hashlib", "heapq", "hmac", "html", "http",
|
|
372
|
+
"idlelib", "imaplib", "imghdr", "imp", "importlib", "inspect", "io",
|
|
373
|
+
"ipaddress", "itertools", "json", "keyword", "lib2to3", "linecache",
|
|
374
|
+
"locale", "logging", "lzma", "mailbox", "mailcap", "marshal", "math",
|
|
375
|
+
"mimetypes", "mmap", "modulefinder", "multiprocessing", "netrc", "nis",
|
|
376
|
+
"nntplib", "numbers", "operator", "optparse", "os", "ossaudiodev",
|
|
377
|
+
"pathlib", "pdb", "pickle", "pickletools", "pipes", "pkgutil", "platform",
|
|
378
|
+
"plistlib", "poplib", "posix", "posixpath", "pprint", "profile", "pstats",
|
|
379
|
+
"pty", "pwd", "py_compile", "pyclbr", "pydoc", "queue", "quopri",
|
|
380
|
+
"random", "re", "readline", "reprlib", "resource", "rlcompleter",
|
|
381
|
+
"runpy", "sched", "secrets", "select", "selectors", "shelve", "shlex",
|
|
382
|
+
"shutil", "signal", "site", "smtpd", "smtplib", "sndhdr", "socket",
|
|
383
|
+
"socketserver", "spwd", "sqlite3", "ssl", "stat", "statistics", "string",
|
|
384
|
+
"stringprep", "struct", "subprocess", "sunau", "symtable", "sys",
|
|
385
|
+
"sysconfig", "syslog", "tabnanny", "tarfile", "telnetlib", "tempfile",
|
|
386
|
+
"termios", "test", "textwrap", "threading", "time", "timeit", "tkinter",
|
|
387
|
+
"token", "tokenize", "trace", "traceback", "tracemalloc", "tty", "turtle",
|
|
388
|
+
"turtledemo", "types", "typing", "unicodedata", "unittest", "urllib",
|
|
389
|
+
"uu", "uuid", "venv", "warnings", "wave", "weakref", "webbrowser",
|
|
390
|
+
"winreg", "winsound", "wsgiref", "xdrlib", "xml", "xmlrpc", "zipapp",
|
|
391
|
+
"zipfile", "zipimport", "zlib", "_thread"
|
|
392
|
+
]);
|
|
393
|
+
return builtins.has(pkg.toLowerCase());
|
|
394
|
+
}
|