@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,110 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { fingerprintJson } from "./fingerprint.js";
|
|
4
|
+
import { defaultVerifyCommand } from "./repo_context.js";
|
|
5
|
+
function nowIso() {
|
|
6
|
+
return new Date().toISOString();
|
|
7
|
+
}
|
|
8
|
+
function ensureDir(p) {
|
|
9
|
+
fs.mkdirSync(p, { recursive: true });
|
|
10
|
+
}
|
|
11
|
+
export function generatePlanningModuleV1(opts) {
|
|
12
|
+
const version = "v1";
|
|
13
|
+
const verifyCmd = defaultVerifyCommand(opts.repoContext);
|
|
14
|
+
const input = {
|
|
15
|
+
module: "planning",
|
|
16
|
+
version,
|
|
17
|
+
goal: opts.goal,
|
|
18
|
+
repo_context: opts.repoContext,
|
|
19
|
+
skill_fingerprint: opts.skillFingerprint,
|
|
20
|
+
decisions: opts.decisionSnapshot.map((d) => ({ decision_id: d.decision_id, chosen: d.chosen ?? null })),
|
|
21
|
+
verify: verifyCmd,
|
|
22
|
+
};
|
|
23
|
+
const fingerprint = fingerprintJson("PLN", input);
|
|
24
|
+
const outDir = path.join(opts.specHighRunDir, "modules", "planning", "v1");
|
|
25
|
+
const outPath = path.join(outDir, "plan.json");
|
|
26
|
+
if (fs.existsSync(outPath)) {
|
|
27
|
+
try {
|
|
28
|
+
const existing = JSON.parse(fs.readFileSync(outPath, "utf-8"));
|
|
29
|
+
if (existing.fingerprint === fingerprint) {
|
|
30
|
+
return { version, fingerprint, path: outPath };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// regenerate
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
ensureDir(outDir);
|
|
38
|
+
const plan = opts.goal === "repair"
|
|
39
|
+
? [
|
|
40
|
+
{
|
|
41
|
+
step: 1,
|
|
42
|
+
title: "재현/원인 고정",
|
|
43
|
+
actions: ["문제를 재현하는 최소 시나리오를 확인", "영향 범위를 좁히기"],
|
|
44
|
+
verification: [verifyCmd],
|
|
45
|
+
rollback: ["변경 전 상태 유지(최소 단위 변경)"],
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
step: 2,
|
|
49
|
+
title: "최소 수정",
|
|
50
|
+
actions: ["가장 작은 수정으로 문제 해결", "불필요한 리팩토링 금지"],
|
|
51
|
+
verification: [verifyCmd],
|
|
52
|
+
rollback: ["해당 변경만 되돌리기"],
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
step: 3,
|
|
56
|
+
title: "재검증",
|
|
57
|
+
actions: ["수정 후 핵심 동작 확인", "테스트/실행 결과 기록"],
|
|
58
|
+
verification: [verifyCmd],
|
|
59
|
+
rollback: ["최근 수정만 되돌리기"],
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
step: 4,
|
|
63
|
+
title: "재발 방지",
|
|
64
|
+
actions: ["유사 케이스를 막는 최소 테스트/게이트 추가", "문서/규칙 드리프트 점검"],
|
|
65
|
+
verification: [verifyCmd],
|
|
66
|
+
rollback: ["테스트/게이트 변경 분리"],
|
|
67
|
+
},
|
|
68
|
+
]
|
|
69
|
+
: [
|
|
70
|
+
{
|
|
71
|
+
step: 1,
|
|
72
|
+
title: "변경 범위 확정",
|
|
73
|
+
actions: ["이번에 건드릴 파일/안 건드릴 파일 확정", "인터페이스 유지 조건 확인"],
|
|
74
|
+
verification: [verifyCmd],
|
|
75
|
+
rollback: ["변경 전 상태로 되돌릴 준비(파일/커밋)"],
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
step: 2,
|
|
79
|
+
title: "구현",
|
|
80
|
+
actions: ["작은 단위로 구현", "필요한 최소 변경만 수행"],
|
|
81
|
+
verification: [verifyCmd],
|
|
82
|
+
rollback: ["최근 변경만 되돌리기"],
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
step: 3,
|
|
86
|
+
title: "검증/정리",
|
|
87
|
+
actions: ["실행/테스트로 동작 확인", "에러 처리/엣지 케이스 확인"],
|
|
88
|
+
verification: [verifyCmd],
|
|
89
|
+
rollback: ["문제 발생 시 최근 변경만 철회"],
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
step: 4,
|
|
93
|
+
title: "SSOT 동기화",
|
|
94
|
+
actions: ["문서/출력 계약과 실제 구현 정합 점검", "필요 시 SSOT 업데이트"],
|
|
95
|
+
verification: [verifyCmd],
|
|
96
|
+
rollback: ["SSOT 변경 분리"],
|
|
97
|
+
},
|
|
98
|
+
];
|
|
99
|
+
const doc = {
|
|
100
|
+
module: "planning",
|
|
101
|
+
version,
|
|
102
|
+
fingerprint,
|
|
103
|
+
generated_at: nowIso(),
|
|
104
|
+
goal: opts.goal,
|
|
105
|
+
plan,
|
|
106
|
+
exit_criteria: ["inspect_code=GO", "SSOT drift 없음", "message_template_id 존재"],
|
|
107
|
+
};
|
|
108
|
+
fs.writeFileSync(outPath, JSON.stringify(doc, null, 2), "utf-8");
|
|
109
|
+
return { version, fingerprint, path: outPath };
|
|
110
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
function exists(basePath, rel) {
|
|
4
|
+
return fs.existsSync(path.join(basePath, rel));
|
|
5
|
+
}
|
|
6
|
+
export function detectRepoContext(basePath) {
|
|
7
|
+
const hasCargo = exists(basePath, "Cargo.toml");
|
|
8
|
+
const hasPackageJson = exists(basePath, "package.json");
|
|
9
|
+
const hasPyproject = exists(basePath, "pyproject.toml") || exists(basePath, "requirements.txt");
|
|
10
|
+
const langs = [hasCargo ? "rust" : null, hasPackageJson ? "ts" : null, hasPyproject ? "py" : null].filter(Boolean);
|
|
11
|
+
const primary_language = langs.length === 0 ? "mixed" : langs.length === 1 ? langs[0] : "mixed";
|
|
12
|
+
const package_manager = exists(basePath, "pnpm-lock.yaml")
|
|
13
|
+
? "pnpm"
|
|
14
|
+
: exists(basePath, "yarn.lock")
|
|
15
|
+
? "yarn"
|
|
16
|
+
: exists(basePath, "package-lock.json")
|
|
17
|
+
? "npm"
|
|
18
|
+
: null;
|
|
19
|
+
const repo_type = exists(basePath, "project.godot") || exists(basePath, "godot.project")
|
|
20
|
+
? "game"
|
|
21
|
+
: exists(basePath, "action.yml") || exists(basePath, "action.yaml")
|
|
22
|
+
? "tool"
|
|
23
|
+
: exists(basePath, "src")
|
|
24
|
+
? "service"
|
|
25
|
+
: "tool";
|
|
26
|
+
const frameworks = [];
|
|
27
|
+
try {
|
|
28
|
+
if (hasPackageJson) {
|
|
29
|
+
const raw = fs.readFileSync(path.join(basePath, "package.json"), "utf-8");
|
|
30
|
+
const pkg = JSON.parse(raw);
|
|
31
|
+
const deps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };
|
|
32
|
+
for (const k of ["next", "react", "vue", "svelte", "vite"]) {
|
|
33
|
+
if (deps[k])
|
|
34
|
+
frameworks.push(k);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// ignore
|
|
40
|
+
}
|
|
41
|
+
return { primary_language, repo_type, package_manager, frameworks };
|
|
42
|
+
}
|
|
43
|
+
export function defaultVerifyCommand(ctx) {
|
|
44
|
+
if (ctx.primary_language === "rust")
|
|
45
|
+
return "cargo test -q";
|
|
46
|
+
if (ctx.primary_language === "ts") {
|
|
47
|
+
if (ctx.package_manager === "pnpm")
|
|
48
|
+
return "pnpm test";
|
|
49
|
+
if (ctx.package_manager === "yarn")
|
|
50
|
+
return "yarn test";
|
|
51
|
+
return "npm test";
|
|
52
|
+
}
|
|
53
|
+
if (ctx.primary_language === "py")
|
|
54
|
+
return "pytest";
|
|
55
|
+
return "echo \"No default verify\"";
|
|
56
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { parse as parseYaml } from "yaml";
|
|
4
|
+
import { fingerprintJson } from "./fingerprint.js";
|
|
5
|
+
function nowIso() {
|
|
6
|
+
return new Date().toISOString();
|
|
7
|
+
}
|
|
8
|
+
function ensureDir(p) {
|
|
9
|
+
fs.mkdirSync(p, { recursive: true });
|
|
10
|
+
}
|
|
11
|
+
function loadAllowlist(repoRoot) {
|
|
12
|
+
const allowPath = path.join(repoRoot, "docs", "ssot", "modules", "research", "sources_allowlist.yaml");
|
|
13
|
+
if (!fs.existsSync(allowPath))
|
|
14
|
+
return { version: 1, topics: [] };
|
|
15
|
+
const raw = fs.readFileSync(allowPath, "utf-8");
|
|
16
|
+
return parseYaml(raw) ?? { version: 1, topics: [] };
|
|
17
|
+
}
|
|
18
|
+
function loadProjectBriefFallback(specHighRunDir) {
|
|
19
|
+
const p1 = path.join(specHighRunDir, "modules", "_inputs", "project_brief.txt");
|
|
20
|
+
if (fs.existsSync(p1)) {
|
|
21
|
+
try {
|
|
22
|
+
return fs.readFileSync(p1, "utf-8").trim();
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// ignore
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const p2 = path.join(specHighRunDir, "notes", "decision_discovery.md");
|
|
29
|
+
if (fs.existsSync(p2)) {
|
|
30
|
+
try {
|
|
31
|
+
return fs.readFileSync(p2, "utf-8").slice(0, 1000);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// ignore
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return "";
|
|
38
|
+
}
|
|
39
|
+
export function ensureResearchModuleV1(opts) {
|
|
40
|
+
const version = "v1";
|
|
41
|
+
const projectBrief = (opts.projectBrief ?? "").trim() || loadProjectBriefFallback(opts.specHighRunDir);
|
|
42
|
+
const input = {
|
|
43
|
+
module: "research",
|
|
44
|
+
version,
|
|
45
|
+
project_brief: projectBrief,
|
|
46
|
+
repo_context: opts.repoContext,
|
|
47
|
+
decision_candidates: opts.decisionSnapshot.slice(0, 10).map((d) => ({
|
|
48
|
+
decision_id: d.decision_id,
|
|
49
|
+
option_id: d.chosen,
|
|
50
|
+
summary: d.chosen_label ?? d.question ?? "",
|
|
51
|
+
})),
|
|
52
|
+
};
|
|
53
|
+
const fingerprint = fingerprintJson("RSH", input);
|
|
54
|
+
const outDir = path.join(opts.specHighRunDir, "modules", "research", "v1");
|
|
55
|
+
const outPath = path.join(outDir, "research.json");
|
|
56
|
+
const evidencePath = path.join(outDir, "evidence.json");
|
|
57
|
+
// Cache hit
|
|
58
|
+
if (fs.existsSync(outPath)) {
|
|
59
|
+
try {
|
|
60
|
+
const existing = JSON.parse(fs.readFileSync(outPath, "utf-8"));
|
|
61
|
+
if (existing.fingerprint === fingerprint) {
|
|
62
|
+
return { version, fingerprint, path: outPath };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// regenerate
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
ensureDir(outDir);
|
|
70
|
+
const allow = loadAllowlist(opts.repoRoot);
|
|
71
|
+
const topics = allow.topics ?? [];
|
|
72
|
+
const findings = topics.map((t) => {
|
|
73
|
+
const candidates = t.links?.map((l) => ({
|
|
74
|
+
name: t.title ?? t.id ?? "source",
|
|
75
|
+
type: "doc",
|
|
76
|
+
url: l.url ?? "",
|
|
77
|
+
why_relevant: l.note ?? "",
|
|
78
|
+
extractable_components: [],
|
|
79
|
+
risks: ["license"],
|
|
80
|
+
})) ?? [];
|
|
81
|
+
return {
|
|
82
|
+
topic: t.title ?? t.id ?? "topic",
|
|
83
|
+
candidates: candidates.filter((c) => c.url),
|
|
84
|
+
recommendation: {
|
|
85
|
+
best_fit: candidates.find((c) => c.url)?.name ?? "",
|
|
86
|
+
reason: "allowlist 기반 후보입니다.",
|
|
87
|
+
adaptation_notes: ["v1에서는 링크/키워드를 저장하고, 구체 적용은 작업 지시서에서 확정합니다."],
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
});
|
|
91
|
+
const research = {
|
|
92
|
+
module: "research",
|
|
93
|
+
version,
|
|
94
|
+
fingerprint,
|
|
95
|
+
generated_at: nowIso(),
|
|
96
|
+
summary: "조사 슬롯이 준비되었습니다.",
|
|
97
|
+
findings,
|
|
98
|
+
license_notes: [],
|
|
99
|
+
};
|
|
100
|
+
const keywords = topics.flatMap((t) => t.keywords ?? []).slice(0, 20);
|
|
101
|
+
const links = topics.flatMap((t) => (t.links ?? []).map((l) => ({ url: l.url ?? "", note: l.note ?? "" }))).filter((x) => x.url);
|
|
102
|
+
const evidence = {
|
|
103
|
+
module: "research",
|
|
104
|
+
version,
|
|
105
|
+
fingerprint,
|
|
106
|
+
generated_at: nowIso(),
|
|
107
|
+
sources_allowlist_path: "docs/ssot/modules/research/sources_allowlist.yaml",
|
|
108
|
+
keywords,
|
|
109
|
+
links,
|
|
110
|
+
};
|
|
111
|
+
fs.writeFileSync(outPath, JSON.stringify(research, null, 2), "utf-8");
|
|
112
|
+
fs.writeFileSync(evidencePath, JSON.stringify(evidence, null, 2), "utf-8");
|
|
113
|
+
return { version, fingerprint, path: outPath };
|
|
114
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { parse as parseYaml } from "yaml";
|
|
4
|
+
import { fingerprintJson } from "./fingerprint.js";
|
|
5
|
+
const SKILL_SUMMARY = {
|
|
6
|
+
"repo-scout": "레포 구조/핵심 파일 파악",
|
|
7
|
+
"code-slicer": "기능을 최소 단위로 분해",
|
|
8
|
+
"ssot-sync": "문서/출력 계약 드리프트 방지",
|
|
9
|
+
"cargo-check": "Rust 빌드/테스트 루프",
|
|
10
|
+
"clippy-fix": "Rust 품질 보완(clippy)",
|
|
11
|
+
"ts-typecheck": "TS typecheck/test 루프",
|
|
12
|
+
};
|
|
13
|
+
function nowIso() {
|
|
14
|
+
return new Date().toISOString();
|
|
15
|
+
}
|
|
16
|
+
function ensureDir(p) {
|
|
17
|
+
fs.mkdirSync(p, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
function loadBundle(repoRoot, bundleId) {
|
|
20
|
+
const p = path.join(repoRoot, "docs", "ssot", "modules", "skills", "bundles", `${bundleId}.yaml`);
|
|
21
|
+
if (!fs.existsSync(p))
|
|
22
|
+
return null;
|
|
23
|
+
try {
|
|
24
|
+
return parseYaml(fs.readFileSync(p, "utf-8"));
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function selectBundleIds(ctx) {
|
|
31
|
+
const out = ["default"];
|
|
32
|
+
if (ctx.primary_language === "rust")
|
|
33
|
+
out.push("rust");
|
|
34
|
+
if (ctx.primary_language === "ts")
|
|
35
|
+
out.push("ts");
|
|
36
|
+
return Array.from(new Set(out));
|
|
37
|
+
}
|
|
38
|
+
export function generateSkillsModuleV1(opts) {
|
|
39
|
+
const version = "v1";
|
|
40
|
+
const bundleIds = selectBundleIds(opts.repoContext);
|
|
41
|
+
const selectedBundles = bundleIds
|
|
42
|
+
.map((id) => {
|
|
43
|
+
const b = loadBundle(opts.repoRoot, id);
|
|
44
|
+
const skills = (b?.skills ?? []).filter(Boolean);
|
|
45
|
+
return { bundle_id: id, skills };
|
|
46
|
+
})
|
|
47
|
+
.filter((b) => b.skills.length > 0);
|
|
48
|
+
const input = {
|
|
49
|
+
module: "skills",
|
|
50
|
+
version,
|
|
51
|
+
repo_context: opts.repoContext,
|
|
52
|
+
selected_bundles: selectedBundles,
|
|
53
|
+
};
|
|
54
|
+
const fingerprint = fingerprintJson("SKL", input);
|
|
55
|
+
const outDir = path.join(opts.specHighRunDir, "modules", "skills", "v1");
|
|
56
|
+
const outPath = path.join(outDir, "selection.json");
|
|
57
|
+
const promptPath = path.join(outDir, "bundle_prompt.md");
|
|
58
|
+
if (fs.existsSync(outPath)) {
|
|
59
|
+
try {
|
|
60
|
+
const existing = JSON.parse(fs.readFileSync(outPath, "utf-8"));
|
|
61
|
+
if (existing.fingerprint === fingerprint && fs.existsSync(promptPath)) {
|
|
62
|
+
return { version, fingerprint, path: outPath, bundle_prompt_path: promptPath };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// regenerate
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
ensureDir(outDir);
|
|
70
|
+
const selection = {
|
|
71
|
+
module: "skills",
|
|
72
|
+
version,
|
|
73
|
+
fingerprint,
|
|
74
|
+
generated_at: nowIso(),
|
|
75
|
+
selected_bundles: selectedBundles,
|
|
76
|
+
constraints: {
|
|
77
|
+
forbidden: ["breaking_api"],
|
|
78
|
+
must_preserve: ["SSOT", "message_template_id"],
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
const lines = [];
|
|
82
|
+
lines.push("## SKILLS_PACK v1");
|
|
83
|
+
lines.push("");
|
|
84
|
+
lines.push("선택된 번들:");
|
|
85
|
+
for (const b of selectedBundles) {
|
|
86
|
+
lines.push(`- ${b.bundle_id}: ${b.skills.join(", ")}`);
|
|
87
|
+
}
|
|
88
|
+
lines.push("");
|
|
89
|
+
lines.push("스킬 요약:");
|
|
90
|
+
const allSkills = Array.from(new Set(selectedBundles.flatMap((b) => b.skills)));
|
|
91
|
+
for (const s of allSkills) {
|
|
92
|
+
lines.push(`- ${s}: ${SKILL_SUMMARY[s] ?? ""}`.trim());
|
|
93
|
+
}
|
|
94
|
+
lines.push("");
|
|
95
|
+
lines.push("검증(최소 1회):");
|
|
96
|
+
lines.push("- 변경 후 테스트/실행을 최소 1회 확인하세요.");
|
|
97
|
+
fs.writeFileSync(outPath, JSON.stringify(selection, null, 2), "utf-8");
|
|
98
|
+
fs.writeFileSync(promptPath, lines.join("\n") + "\n", "utf-8");
|
|
99
|
+
return { version, fingerprint, path: outPath, bundle_prompt_path: promptPath };
|
|
100
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
// adapters/mcp-ts/src/tools/vibe_pm/pm_language.ts
|
|
2
|
+
// PM Language transformation utilities
|
|
3
|
+
// Reference: docs/DEV_SPEC/MCP_TOOL_SCHEMA_SPEC.md Section: Status Mapping
|
|
4
|
+
// ============================================================
|
|
5
|
+
// Signal Level Mapping (Internal → User-facing)
|
|
6
|
+
// ============================================================
|
|
7
|
+
export const SIGNAL_TO_REVIEW = {
|
|
8
|
+
LEVEL_1: { result: "GO", verdict: "문제없습니다" },
|
|
9
|
+
LEVEL_2: { result: "FIX", verdict: "한 가지 수정이 필요합니다" },
|
|
10
|
+
LEVEL_3: { result: "BLOCK", verdict: "이대로 출시하면 위험합니다" }
|
|
11
|
+
};
|
|
12
|
+
export const REVIEW_TO_EMOJI = {
|
|
13
|
+
GO: "✅",
|
|
14
|
+
FIX: "⚠️",
|
|
15
|
+
BLOCK: "❌"
|
|
16
|
+
};
|
|
17
|
+
export const REVIEW_TO_STATUS_KO = {
|
|
18
|
+
GO: "진행 가능",
|
|
19
|
+
FIX: "보완 필요",
|
|
20
|
+
BLOCK: "반려"
|
|
21
|
+
};
|
|
22
|
+
// ============================================================
|
|
23
|
+
// Forbidden Terms (Never expose to user)
|
|
24
|
+
// ============================================================
|
|
25
|
+
export const FORBIDDEN_TERMS = [
|
|
26
|
+
"run_id",
|
|
27
|
+
"bridge",
|
|
28
|
+
"schema",
|
|
29
|
+
"gate",
|
|
30
|
+
"signal",
|
|
31
|
+
"FSM",
|
|
32
|
+
"spec_drift",
|
|
33
|
+
"touches_out_of_scope",
|
|
34
|
+
"LEVEL_1",
|
|
35
|
+
"LEVEL_2",
|
|
36
|
+
"LEVEL_3",
|
|
37
|
+
"decision_card",
|
|
38
|
+
"clinic_bridge"
|
|
39
|
+
];
|
|
40
|
+
// ============================================================
|
|
41
|
+
// Mode Display Mapping
|
|
42
|
+
// ============================================================
|
|
43
|
+
export const MODE_DISPLAY = {
|
|
44
|
+
mvp_fast: "MVP (속도 우선)",
|
|
45
|
+
balanced: "균형 모드",
|
|
46
|
+
quality_first: "품질 우선"
|
|
47
|
+
};
|
|
48
|
+
// ============================================================
|
|
49
|
+
// Phase Display Mapping
|
|
50
|
+
// ============================================================
|
|
51
|
+
export const PHASE_DISPLAY = {
|
|
52
|
+
init: "초기화",
|
|
53
|
+
briefing: "접수 중",
|
|
54
|
+
decision: "결재 대기",
|
|
55
|
+
work_order: "작업 지시서 발행됨",
|
|
56
|
+
implementation: "구현 중",
|
|
57
|
+
review: "검수 중",
|
|
58
|
+
completed: "완료"
|
|
59
|
+
};
|
|
60
|
+
// ============================================================
|
|
61
|
+
// Transformation Functions
|
|
62
|
+
// ============================================================
|
|
63
|
+
/**
|
|
64
|
+
* Convert internal signal level to user-facing review result
|
|
65
|
+
*/
|
|
66
|
+
export function signalToReview(signal) {
|
|
67
|
+
const mapping = SIGNAL_TO_REVIEW[signal];
|
|
68
|
+
if (mapping) {
|
|
69
|
+
return mapping;
|
|
70
|
+
}
|
|
71
|
+
// Default to most severe if unknown
|
|
72
|
+
return { result: "BLOCK", verdict: "상태를 확인할 수 없습니다" };
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get user-friendly status message with emoji
|
|
76
|
+
*/
|
|
77
|
+
export function getStatusMessage(result) {
|
|
78
|
+
const emoji = REVIEW_TO_EMOJI[result];
|
|
79
|
+
const status = REVIEW_TO_STATUS_KO[result];
|
|
80
|
+
return `${emoji} ${status}`;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Format mode for user display
|
|
84
|
+
*/
|
|
85
|
+
export function formatMode(mode) {
|
|
86
|
+
return MODE_DISPLAY[mode] ?? mode;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Format phase for user display
|
|
90
|
+
*/
|
|
91
|
+
export function formatPhase(phase) {
|
|
92
|
+
return PHASE_DISPLAY[phase] ?? phase;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Check if text contains forbidden terms
|
|
96
|
+
*/
|
|
97
|
+
export function containsForbiddenTerms(text) {
|
|
98
|
+
const lowerText = text.toLowerCase();
|
|
99
|
+
return FORBIDDEN_TERMS.some((term) => lowerText.includes(term.toLowerCase()));
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Sanitize text by warning about forbidden terms (for debugging)
|
|
103
|
+
* In production, this should be used to validate output before sending
|
|
104
|
+
*/
|
|
105
|
+
export function detectForbiddenTerms(text) {
|
|
106
|
+
const lowerText = text.toLowerCase();
|
|
107
|
+
return FORBIDDEN_TERMS.filter((term) => lowerText.includes(term.toLowerCase()));
|
|
108
|
+
}
|
|
109
|
+
// ============================================================
|
|
110
|
+
// Issue Type Mapping
|
|
111
|
+
// ============================================================
|
|
112
|
+
export const ISSUE_TYPE_DISPLAY = {
|
|
113
|
+
RULE_VIOLATION: {
|
|
114
|
+
name: "규칙 위반",
|
|
115
|
+
description: "금지된 사항을 위반했습니다"
|
|
116
|
+
},
|
|
117
|
+
VERIFY_FAIL: {
|
|
118
|
+
name: "검증 실패",
|
|
119
|
+
description: "검증 기준을 충족하지 못했습니다"
|
|
120
|
+
},
|
|
121
|
+
SCOPE_MISMATCH: {
|
|
122
|
+
name: "범위 이탈",
|
|
123
|
+
description: "이번 작업 범위를 벗어났습니다"
|
|
124
|
+
},
|
|
125
|
+
RISK_SPIKE: {
|
|
126
|
+
name: "위험 발생",
|
|
127
|
+
description: "새로운 위험이 발견되었습니다"
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* Get user-friendly issue type description
|
|
132
|
+
*/
|
|
133
|
+
export function formatIssueType(type) {
|
|
134
|
+
return (ISSUE_TYPE_DISPLAY[type] ?? {
|
|
135
|
+
name: "알 수 없는 문제",
|
|
136
|
+
description: "확인이 필요합니다"
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Generate paste_to_agent text block
|
|
141
|
+
*/
|
|
142
|
+
export function generateWorkOrderText(data) {
|
|
143
|
+
const orderNum = data.orderNumber ?? "001";
|
|
144
|
+
const modeDisplay = formatMode(data.mode);
|
|
145
|
+
const includeList = data.includes.map((item) => `- ${item}`).join("\n");
|
|
146
|
+
const excludeList = data.excludes.map((item) => `- ${item}`).join("\n");
|
|
147
|
+
const doNotTouchList = data.doNotTouch.map((item) => `- ${item}`).join("\n");
|
|
148
|
+
const verifyList = data.verifyCriteria.map((item) => `- ${item}`).join("\n");
|
|
149
|
+
return `<<< Vibe PM 작업 지시서 #${orderNum} >>>
|
|
150
|
+
|
|
151
|
+
프로젝트: ${data.projectName}
|
|
152
|
+
모드: ${modeDisplay}
|
|
153
|
+
|
|
154
|
+
[목표]
|
|
155
|
+
${data.headline}
|
|
156
|
+
|
|
157
|
+
[이번에 할 것]
|
|
158
|
+
${includeList}
|
|
159
|
+
|
|
160
|
+
[이번에 안 할 것]
|
|
161
|
+
${excludeList}
|
|
162
|
+
|
|
163
|
+
[건드리지 말 것]
|
|
164
|
+
${doNotTouchList}
|
|
165
|
+
|
|
166
|
+
[검증 기준]
|
|
167
|
+
${verifyList}
|
|
168
|
+
|
|
169
|
+
<<< 지시서 끝 >>>`;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Generate repair request prompt block
|
|
173
|
+
*/
|
|
174
|
+
export function generateRepairPrompt(data) {
|
|
175
|
+
const modeDisplay = formatMode(data.mode);
|
|
176
|
+
const fixList = data.fixDirection.map((item, i) => `${i + 1}. ${item}`).join("\n");
|
|
177
|
+
const verifyList = data.verifyCriteria.map((item) => `- ${item}`).join("\n");
|
|
178
|
+
return `<<< Vibe PM 수정 요청 >>>
|
|
179
|
+
|
|
180
|
+
프로젝트: ${data.projectName}
|
|
181
|
+
모드: ${modeDisplay}
|
|
182
|
+
|
|
183
|
+
[문제]
|
|
184
|
+
${data.problem}
|
|
185
|
+
|
|
186
|
+
[원인]
|
|
187
|
+
${data.cause}
|
|
188
|
+
|
|
189
|
+
[수정 방향]
|
|
190
|
+
${fixList}
|
|
191
|
+
|
|
192
|
+
[검증 기준]
|
|
193
|
+
${verifyList}
|
|
194
|
+
|
|
195
|
+
<<< 수정 요청 끝 >>>`;
|
|
196
|
+
}
|
|
197
|
+
// ============================================================
|
|
198
|
+
// Decision Text Transformation
|
|
199
|
+
// ============================================================
|
|
200
|
+
/**
|
|
201
|
+
* Transform technical ask_queue text to PM language
|
|
202
|
+
* This is a simplified version - full implementation would parse and restructure
|
|
203
|
+
*/
|
|
204
|
+
export function transformAskQueueText(rawText) {
|
|
205
|
+
let transformed = rawText;
|
|
206
|
+
// Replace common technical terms with PM-friendly alternatives
|
|
207
|
+
const replacements = [
|
|
208
|
+
[/run_id/gi, "프로젝트"],
|
|
209
|
+
[/bridge/gi, "작업 지시서"],
|
|
210
|
+
[/schema/gi, "구조"],
|
|
211
|
+
[/gate/gi, "검토"],
|
|
212
|
+
[/signal/gi, "상태"],
|
|
213
|
+
[/FSM/gi, "워크플로우"],
|
|
214
|
+
[/spec_drift/gi, "범위 변경"],
|
|
215
|
+
[/decision_card/gi, "결재 안건"],
|
|
216
|
+
[/clinic_bridge/gi, "검수 기준"]
|
|
217
|
+
];
|
|
218
|
+
for (const [pattern, replacement] of replacements) {
|
|
219
|
+
transformed = transformed.replace(pattern, replacement);
|
|
220
|
+
}
|
|
221
|
+
return transformed;
|
|
222
|
+
}
|