@byh3071/vhk 1.7.1 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/{chunk-GXFZ7JXX.js → chunk-RXDOM4QT.js} +12 -1
- package/dist/index.js +316 -32
- package/dist/mcp/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ AI 코딩 도구는 저마다 규칙 파일이 다르고(`.cursorrules`·`CLAUDE
|
|
|
21
21
|
|
|
22
22
|
| 문제 | VHK 해결 | 명령 |
|
|
23
23
|
|------|----------|------|
|
|
24
|
-
| 도구마다 규칙 파일이 따로 논다 | `RULES.md` 한 벌 → Cursor·Claude·Windsurf·Copilot·Antigravity 규칙 동시 생성 | `vhk sync` |
|
|
24
|
+
| 도구마다 규칙 파일이 따로 논다 | `RULES.md` 한 벌 → Cursor·Claude·Windsurf·Copilot·Antigravity·Gemini·Cline 규칙 동시 생성 | `vhk sync` |
|
|
25
25
|
| 컴퓨터·환경 바뀌면 맥락 유실 | `.vhk/` 맥락을 GitHub gist로 백업·복원 | `vhk cloud push` / `pull` |
|
|
26
26
|
| 새 프로젝트 세팅 반복 | 유형별 문서·규칙·맥락 뼈대를 한 번에 | `vhk init` |
|
|
27
27
|
|
|
@@ -145,7 +145,7 @@ vhk 기획 끝났고 바로 시작
|
|
|
145
145
|
| `vhk gate` | `검증`, `아이디어` | 아이디어 검증 (퀵 5문항 / 풀 13문항 / 스킵) |
|
|
146
146
|
| `vhk init` | `시작`, `만들기` | 프로젝트 초기화 + 하네스 생성 |
|
|
147
147
|
| `vhk recap` | `정리`, `오늘` | Git 변경 → `docs/log/` 세션 로그 |
|
|
148
|
-
| `vhk sync` | `규칙`, `맞추기` | RULES.md → `.cursorrules` + CLAUDE.md + `.windsurfrules` + `.github/copilot-instructions.md` + `.agents/rules/vhk-rules.md`
|
|
148
|
+
| `vhk sync` | `규칙`, `맞추기` | RULES.md → `.cursorrules` + CLAUDE.md + `.windsurfrules` + `.github/copilot-instructions.md` + `.agents/rules/vhk-rules.md` + `AGENTS.md` + `GEMINI.md` + `.clinerules/vhk-rules.md` |
|
|
149
149
|
| `vhk check` | `점검`, `린트` | RULES.md 규칙 위반 검사 |
|
|
150
150
|
| `vhk secure` | `보안` | 시크릿·키 유출 스캔 (`scan` / `스캔` 동일). **CRITICAL/HIGH 발견 시 exit code 1** (CI용) |
|
|
151
151
|
| `vhk ship` | `출하` | 배포 체크리스트 + 회고 + 빌드 로그 |
|
|
@@ -750,6 +750,8 @@ var ko = {
|
|
|
750
750
|
copilotDone: "\u2705 .github/copilot-instructions.md \uB9DE\uCDA4 \uC644\uB8CC",
|
|
751
751
|
antigravityDone: "\u2705 .agents/rules/vhk-rules.md \uB9DE\uCDA4 \uC644\uB8CC",
|
|
752
752
|
agentsDone: "\u2705 AGENTS.md \uB9DE\uCDA4 \uC644\uB8CC",
|
|
753
|
+
geminiDone: "\u2705 GEMINI.md \uB9DE\uCDA4 \uC644\uB8CC",
|
|
754
|
+
clineDone: "\u2705 .clinerules/vhk-rules.md \uB9DE\uCDA4 \uC644\uB8CC",
|
|
753
755
|
antigravityTruncated: "Antigravity 12,000\uC790 \uC81C\uD55C\uC73C\uB85C \uC77C\uBD80 \uC808\uC0AD\uB428 \u2014 \uC804\uCCB4\uB294 RULES.md \uCC38\uC870",
|
|
754
756
|
done: "\u{1F504} \uB9DE\uCD94\uAE30 \uC644\uB8CC!",
|
|
755
757
|
// 안전 가드 (배치 0) — 덮어쓰기 전 백업·드리프트 확인·미리보기
|
|
@@ -1318,6 +1320,12 @@ function toWindsurfrules(sections, projectName) {
|
|
|
1318
1320
|
function toCopilotInstructions(sections, projectName) {
|
|
1319
1321
|
return buildCodingDoc("GitHub Copilot Instructions", sections, projectName);
|
|
1320
1322
|
}
|
|
1323
|
+
function toGeminiMd(sections, projectName) {
|
|
1324
|
+
return buildCodingDoc("Gemini CLI Rules", sections, projectName);
|
|
1325
|
+
}
|
|
1326
|
+
function toClineRules(sections, projectName) {
|
|
1327
|
+
return buildCodingDoc("Cline Rules", sections, projectName);
|
|
1328
|
+
}
|
|
1321
1329
|
var ANTIGRAVITY_CHAR_LIMIT = 12e3;
|
|
1322
1330
|
var ANTIGRAVITY_TRUNCATE_MARKER = "\n\n<!-- \u26A0\uFE0F Antigravity 12,000\uC790 \uC81C\uD55C\uC73C\uB85C \uC808\uC0AD\uB428 \u2014 \uC804\uCCB4 \uADDC\uCE59\uC740 RULES.md \uCC38\uC870 -->\n";
|
|
1323
1331
|
function truncateForAntigravity(content, limit = ANTIGRAVITY_CHAR_LIMIT) {
|
|
@@ -1404,7 +1412,10 @@ var SYNC_TARGETS = [
|
|
|
1404
1412
|
{ path: ".github/copilot-instructions.md", generate: toCopilotInstructions, doneMessage: ko.sync.copilotDone },
|
|
1405
1413
|
{ path: ".agents/rules/vhk-rules.md", generate: toAntigravityRules, doneMessage: ko.sync.antigravityDone },
|
|
1406
1414
|
// AGENTS.md — 6번째 타겟. 항목 1개 추가로 sync·드리프트·백업 가드가 자동 반영된다.
|
|
1407
|
-
{ path: "AGENTS.md", generate: toAgentsMd, doneMessage: ko.sync.agentsDone }
|
|
1415
|
+
{ path: "AGENTS.md", generate: toAgentsMd, doneMessage: ko.sync.agentsDone },
|
|
1416
|
+
// Goal 16 — Gemini CLI / Cline (공식 경로 검증). 레지스트리 추가만으로 drift·백업 자동 반영.
|
|
1417
|
+
{ path: "GEMINI.md", generate: toGeminiMd, doneMessage: ko.sync.geminiDone },
|
|
1418
|
+
{ path: ".clinerules/vhk-rules.md", generate: toClineRules, doneMessage: ko.sync.clineDone }
|
|
1408
1419
|
];
|
|
1409
1420
|
var BACKUP_KEEP = 10;
|
|
1410
1421
|
var SYNCED_MARKER_REL = path4.join(".vhk", ".synced");
|
package/dist/index.js
CHANGED
|
@@ -43,12 +43,12 @@ import {
|
|
|
43
43
|
stripBom,
|
|
44
44
|
sync,
|
|
45
45
|
t
|
|
46
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-RXDOM4QT.js";
|
|
47
47
|
|
|
48
48
|
// src/index.ts
|
|
49
49
|
import { Command, Help } from "commander";
|
|
50
50
|
import { pathToFileURL } from "url";
|
|
51
|
-
import
|
|
51
|
+
import chalk34 from "chalk";
|
|
52
52
|
import inquirer13 from "inquirer";
|
|
53
53
|
|
|
54
54
|
// src/lib/nlp-router.ts
|
|
@@ -123,6 +123,12 @@ var RULES = [
|
|
|
123
123
|
confidence: "high",
|
|
124
124
|
test: (t2) => /검증\s*묶음|사전\s*검증|저장\s*전\s*(검증|확인)|^verify$/.test(t2)
|
|
125
125
|
},
|
|
126
|
+
{
|
|
127
|
+
command: "review",
|
|
128
|
+
explanation: "\uC801\uB300\uC801 \uC790\uAE30\uAC80\uC99D \u2014 \uC99D\uAC70\uB85C \uAC70\uC9D3\uC644\uB8CC \uC758\uC2EC (vhk review)",
|
|
129
|
+
confidence: "high",
|
|
130
|
+
test: (t2) => /적대\s*검증|자기\s*검증|거짓\s*완료|완료\s*심문|^review$|^검토$/.test(t2)
|
|
131
|
+
},
|
|
126
132
|
{
|
|
127
133
|
command: "init",
|
|
128
134
|
explanation: "\uBB38\uC11C/\uD558\uB124\uC2A4 \uD30C\uC77C\uB9CC \uC0DD\uC131 (vhk init) \u2014 git/MCP/context\uB294 \uC81C\uC678",
|
|
@@ -468,6 +474,8 @@ var KNOWN_COMMAND_TOKENS = /* @__PURE__ */ new Set([
|
|
|
468
474
|
"\uBAA8\uB4DC",
|
|
469
475
|
"verify",
|
|
470
476
|
"\uC0AC\uC804\uC810\uAC80",
|
|
477
|
+
"review",
|
|
478
|
+
"\uAC80\uD1A0",
|
|
471
479
|
"help"
|
|
472
480
|
]);
|
|
473
481
|
function isOptionToken(token) {
|
|
@@ -505,7 +513,7 @@ function detectNaturalLanguageInput(argv) {
|
|
|
505
513
|
}
|
|
506
514
|
|
|
507
515
|
// src/lib/nlp-run.ts
|
|
508
|
-
import
|
|
516
|
+
import chalk32 from "chalk";
|
|
509
517
|
import inquirer12 from "inquirer";
|
|
510
518
|
|
|
511
519
|
// src/commands/gate.ts
|
|
@@ -5731,6 +5739,276 @@ async function verify(opts = {}) {
|
|
|
5731
5739
|
}
|
|
5732
5740
|
}
|
|
5733
5741
|
|
|
5742
|
+
// src/commands/review.ts
|
|
5743
|
+
import { existsSync as existsSync17, writeFileSync as writeFileSync12 } from "fs";
|
|
5744
|
+
import { join as join11 } from "path";
|
|
5745
|
+
import chalk31 from "chalk";
|
|
5746
|
+
var GOALS_DIR2 = "goals";
|
|
5747
|
+
var COVERAGE_MIN = 0.5;
|
|
5748
|
+
var STALE_AGE_MS = 6 * 60 * 60 * 1e3;
|
|
5749
|
+
var REVIEW_DISCLAIMER = [
|
|
5750
|
+
"\u26A0\uFE0F \uC774 \uD310\uC815\uC740 \uBCF4\uC7A5\uC774 \uC544\uB2C8\uB77C \uC2E0\uB8B0\uB3C4 \uC2E0\uD638\uC785\uB2C8\uB2E4 \u2014 \uD1B5\uACFC\uD574\uB3C4 \uAC70\uC9D3\uC644\uB8CC \uAC00\uB2A5\uC131\uC740 \uB0A8\uC2B5\uB2C8\uB2E4.",
|
|
5751
|
+
' \xB7 \uAE30\uB2A5 \uACE0\uC720 \uC644\uB8CC\uC870\uAC74\uC740 \uAC8C\uC774\uD2B8 \uD0A4\uC6CC\uB4DC(tsc/test/build/secure)\uC5D0 \uB9E4\uD551\uB418\uC9C0 \uC54A\uC73C\uBA74 "\uBBF8\uAC80\uC99D"\uC73C\uB85C \uB0A8\uC2B5\uB2C8\uB2E4.',
|
|
5752
|
+
" \xB7 \uC99D\uAC70(latest.json)\uB294 commit/goal \uBC14\uC778\uB529\uC774 \uC5C6\uC5B4 \uC2E0\uC120\uB3C4\uB294 \uC0DD\uC131\uC2DC\uAC01\uC73C\uB85C\uB9CC \uCD94\uC815\uD569\uB2C8\uB2E4 \u2014 \uCF54\uB4DC \uBCC0\uACBD \uD6C4\uC5D4 vhk verify \uC7AC\uC2E4\uD589 \uD544\uC694.",
|
|
5753
|
+
" \xB7 git diff \uBBF8\uC0AC\uC6A9(v0) \u2014 \uAE30\uC874 \uD14C\uC2A4\uD2B8\uAC00 green \uC774\uC5B4\uB3C4 \uC774\uBC88 \uBCC0\uACBD\uC744 \uCEE4\uBC84\uD558\uC9C0 \uBABB\uD588\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4."
|
|
5754
|
+
].join("\n");
|
|
5755
|
+
function parseCompletionChecks(body) {
|
|
5756
|
+
const lines = body.split(/\r?\n/);
|
|
5757
|
+
const out = [];
|
|
5758
|
+
let inSection = false;
|
|
5759
|
+
for (const raw of lines) {
|
|
5760
|
+
const line = raw.trimEnd();
|
|
5761
|
+
const heading = line.match(/^#{1,6}\s+(.*)$/);
|
|
5762
|
+
if (heading) {
|
|
5763
|
+
inSection = /completion\s*check/i.test(heading[1]);
|
|
5764
|
+
continue;
|
|
5765
|
+
}
|
|
5766
|
+
if (!inSection) continue;
|
|
5767
|
+
const box = line.match(/^\s*-\s*\[([ xX])\]\s*(.+)$/);
|
|
5768
|
+
if (box) out.push({ text: box[2].trim(), checked: box[1].toLowerCase() === "x" });
|
|
5769
|
+
}
|
|
5770
|
+
return out;
|
|
5771
|
+
}
|
|
5772
|
+
function impliedGates(text) {
|
|
5773
|
+
if (/게이트|공통\s*게이트|goal\s*check/i.test(text)) {
|
|
5774
|
+
return ["typecheck", "test", "build", "secure"];
|
|
5775
|
+
}
|
|
5776
|
+
const gates = [];
|
|
5777
|
+
if (/tsc|typecheck|타입\s*체크/i.test(text)) gates.push("typecheck");
|
|
5778
|
+
if (/테스트|test|회귀|vitest/i.test(text)) gates.push("test");
|
|
5779
|
+
if (/빌드|build/i.test(text)) gates.push("build");
|
|
5780
|
+
if (/시크릿|secret|secure|누출|보안\s*스캔/i.test(text)) gates.push("secure");
|
|
5781
|
+
return gates;
|
|
5782
|
+
}
|
|
5783
|
+
function assessFreshness(report, nowMs) {
|
|
5784
|
+
const generatedAt = report?.generatedAt ?? null;
|
|
5785
|
+
const parsed = generatedAt ? Date.parse(generatedAt) : NaN;
|
|
5786
|
+
if (!Number.isFinite(parsed)) {
|
|
5787
|
+
return { generatedAt, ageMs: null, stale: true, confirmed: false, note: "\uC0DD\uC131\uC2DC\uAC01 \uBD88\uBA85 \u2014 \uC2E0\uC120\uB3C4 \uBBF8\uD655\uC778" };
|
|
5788
|
+
}
|
|
5789
|
+
const ageMs = nowMs - parsed;
|
|
5790
|
+
const stale = ageMs > STALE_AGE_MS || ageMs < 0;
|
|
5791
|
+
const mins = Math.round(ageMs / 6e4);
|
|
5792
|
+
const human = mins >= 120 ? `${Math.round(mins / 60)}\uC2DC\uAC04` : `${mins}\uBD84`;
|
|
5793
|
+
return {
|
|
5794
|
+
generatedAt,
|
|
5795
|
+
ageMs,
|
|
5796
|
+
stale,
|
|
5797
|
+
confirmed: true,
|
|
5798
|
+
note: stale ? `\uC99D\uAC70\uAC00 ${human} \uC804 \uC0DD\uC131(\uB610\uB294 \uBBF8\uB798\uC2DC\uAC01) \u2014 \uCF54\uB4DC \uBCC0\uACBD \uC2DC \uBB34\uD6A8, vhk verify \uC7AC\uC2E4\uD589 \uAD8C\uC7A5` : `\uC99D\uAC70 ${human} \uC804 \uC0DD\uC131`
|
|
5799
|
+
};
|
|
5800
|
+
}
|
|
5801
|
+
function crossCheck(checks, goalStatus, report, nowMs) {
|
|
5802
|
+
const suspicions = [];
|
|
5803
|
+
const gaps = [];
|
|
5804
|
+
const gateById = /* @__PURE__ */ new Map();
|
|
5805
|
+
if (report) for (const g of report.gates) gateById.set(g.id, g);
|
|
5806
|
+
const freshness = assessFreshness(report, nowMs);
|
|
5807
|
+
if (goalStatus === "DONE" && report && report.status === "FAIL") {
|
|
5808
|
+
suspicions.push({
|
|
5809
|
+
check: "goal status = DONE",
|
|
5810
|
+
reason: "verify \uC804\uCCB4 \uACB0\uACFC\uAC00 FAIL \u2014 \uC644\uB8CC \uC120\uC5B8\uACFC \uC99D\uAC70\uAC00 \uBAA8\uC21C(\uAC70\uC9D3\uC644\uB8CC \uAC15\uD55C \uC758\uC2EC)."
|
|
5811
|
+
});
|
|
5812
|
+
}
|
|
5813
|
+
const checked = checks.filter((c) => c.checked);
|
|
5814
|
+
let mappedCount = 0;
|
|
5815
|
+
for (const c of checked) {
|
|
5816
|
+
const gates = impliedGates(c.text);
|
|
5817
|
+
if (gates.length === 0) {
|
|
5818
|
+
gaps.push({ check: c.text, note: "\uBBF8\uAC80\uC99D \u2014 \uAC8C\uC774\uD2B8 \uD0A4\uC6CC\uB4DC \uB9E4\uD551 \uBD88\uAC00(\uAE30\uB2A5 \uC644\uB8CC\uC870\uAC74, \uC218\uB3D9 \uD655\uC778 \uD544\uC694)." });
|
|
5819
|
+
continue;
|
|
5820
|
+
}
|
|
5821
|
+
mappedCount++;
|
|
5822
|
+
for (const gid of gates) {
|
|
5823
|
+
const g = gateById.get(gid);
|
|
5824
|
+
if (!report || !g) {
|
|
5825
|
+
suspicions.push({ check: c.text, reason: `${gid} \uAC8C\uC774\uD2B8 \uC99D\uAC70 \uC5C6\uC74C(latest.json \uBD80\uC7AC/\uBBF8\uC2E4\uD589) \u2014 \uCCB4\uD06C\uB428\uC774\uB098 \uB4B7\uBC1B\uCE68 \uBABB \uD568.` });
|
|
5826
|
+
} else if (g.status === "fail") {
|
|
5827
|
+
suspicions.push({ check: c.text, reason: `${gid} \uAC8C\uC774\uD2B8 FAIL(\uC885\uB8CC\uCF54\uB4DC ${g.exitCode ?? "?"}) \u2014 \uCCB4\uD06C\uB428\uACFC \uBAA8\uC21C.` });
|
|
5828
|
+
} else if (g.status === "skip") {
|
|
5829
|
+
suspicions.push({ check: c.text, reason: `${gid} \uAC8C\uC774\uD2B8 skip \u2014 \uAC80\uC99D\uC774 \uBCC0\uACBD\uC744 \uC548 \uAC74\uB4DC\uB838\uC744 \uC218 \uC788\uC74C(\uAC70\uC9D3\uC644\uB8CC \uC758\uC2EC).` });
|
|
5830
|
+
}
|
|
5831
|
+
}
|
|
5832
|
+
}
|
|
5833
|
+
const coverage = checked.length === 0 ? 0 : mappedCount / checked.length;
|
|
5834
|
+
let confidence;
|
|
5835
|
+
if (checked.length === 0 || suspicions.length > 0) {
|
|
5836
|
+
confidence = "low";
|
|
5837
|
+
} else if (gaps.length > 0 || coverage < COVERAGE_MIN || !freshness.confirmed || freshness.stale) {
|
|
5838
|
+
confidence = "medium";
|
|
5839
|
+
} else {
|
|
5840
|
+
confidence = "high";
|
|
5841
|
+
}
|
|
5842
|
+
let reprompt;
|
|
5843
|
+
if (checked.length === 0) {
|
|
5844
|
+
reprompt = "\uCCB4\uD06C\uB41C \uC644\uB8CC\uC870\uAC74\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uC644\uB8CC \uC8FC\uC7A5\uC774 \uC5C6\uC5B4 \uC2EC\uBB38\uD560 \uB300\uC0C1\uC774 \uC5C6\uC2B5\uB2C8\uB2E4(vacuous).";
|
|
5845
|
+
} else if (suspicions.length > 0) {
|
|
5846
|
+
reprompt = "\uB2E4\uC74C \uC644\uB8CC\uC870\uAC74\uC774 \uC99D\uAC70\uC640 \uBAA8\uC21C\uB418\uAC70\uB098 \uC99D\uAC70\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4:\n" + suspicions.map((s) => ` - ${s.check} \u2192 ${s.reason}`).join("\n") + "\n\uAC01 \uD56D\uBAA9\uC758 \uC2E4\uC81C \uC99D\uAC70(\uAC8C\uC774\uD2B8 \uD1B5\uACFC/\uCD94\uAC00\uB41C \uD14C\uC2A4\uD2B8/\uBCC0\uACBD \uD30C\uC77C)\uB97C \uC81C\uC2DC\uD558\uAC70\uB098, \uCDA9\uC871 \uBABB \uD558\uBA74 done \uC744 \uCCA0\uD68C\uD558\uC138\uC694.";
|
|
5847
|
+
} else if (gaps.length > 0) {
|
|
5848
|
+
reprompt = "\uB2E4\uC74C \uC644\uB8CC\uC870\uAC74\uC740 \uAC8C\uC774\uD2B8 \uC99D\uAC70\uB85C \uC790\uB3D9 \uD655\uC778\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4(\uBBF8\uAC80\uC99D \u2014 \uC218\uB3D9 \uD655\uC778 \uD544\uC694):\n" + gaps.map((g) => ` - ${g.check}`).join("\n");
|
|
5849
|
+
} else {
|
|
5850
|
+
reprompt = "\uCCB4\uD06C\uB41C \uBAA8\uB4E0 \uC644\uB8CC\uC870\uAC74\uC774 \uAC8C\uC774\uD2B8 \uC99D\uAC70\uB85C \uB4B7\uBC1B\uCE68\uB429\uB2C8\uB2E4(\uB2E8, \uBCF4\uC7A5\uC740 \uC544\uB2D8).";
|
|
5851
|
+
}
|
|
5852
|
+
return {
|
|
5853
|
+
confidence,
|
|
5854
|
+
coverage,
|
|
5855
|
+
checkedCount: checked.length,
|
|
5856
|
+
mappedCount,
|
|
5857
|
+
unmappedCount: gaps.length,
|
|
5858
|
+
freshness,
|
|
5859
|
+
suspicions,
|
|
5860
|
+
gaps,
|
|
5861
|
+
disclaimer: REVIEW_DISCLAIMER,
|
|
5862
|
+
reprompt
|
|
5863
|
+
};
|
|
5864
|
+
}
|
|
5865
|
+
function resolveGoal(optId, goals) {
|
|
5866
|
+
let id;
|
|
5867
|
+
if (optId !== void 0) {
|
|
5868
|
+
const n = Number(optId);
|
|
5869
|
+
id = Number.isFinite(n) ? n : null;
|
|
5870
|
+
} else {
|
|
5871
|
+
id = selectActiveId(goals);
|
|
5872
|
+
}
|
|
5873
|
+
if (id === null) return null;
|
|
5874
|
+
return goals.find((g) => g.frontmatter.id === id) ?? null;
|
|
5875
|
+
}
|
|
5876
|
+
var CONFIDENCE_LABEL = {
|
|
5877
|
+
low: chalk31.red.bold("\uB0AE\uC74C (\uAC70\uC9D3\uC644\uB8CC \uC758\uC2EC \uB610\uB294 \uC644\uB8CC \uC8FC\uC7A5 \uC5C6\uC74C)"),
|
|
5878
|
+
medium: chalk31.yellow.bold("\uC911\uAC04 (\uCEE4\uBC84\uB9AC\uC9C0/\uC2E0\uC120\uB3C4 \uBD80\uC871 \u2014 \uC99D\uAC70 \uC5C6\uC74C \u2260 \uD1B5\uACFC)"),
|
|
5879
|
+
high: chalk31.green.bold("\uB192\uC74C (\uC758\uC2EC 0 + \uCEE4\uBC84\uB9AC\uC9C0\xB7\uC2E0\uC120\uB3C4 \uCDA9\uBD84 \u2014 \uB2E8 \uBCF4\uC7A5 \uC544\uB2D8)")
|
|
5880
|
+
};
|
|
5881
|
+
async function review(opts = {}) {
|
|
5882
|
+
if (!ensureNotHardStopped("review")) return;
|
|
5883
|
+
const cwd = process.cwd();
|
|
5884
|
+
const goals = listGoals(GOALS_DIR2);
|
|
5885
|
+
if (goals.length === 0) {
|
|
5886
|
+
console.error(chalk31.yellow(" \u26A0\uFE0F goals/ \uC5D0 goal \uC774 \uC5C6\uC2B5\uB2C8\uB2E4. vhk goal init \uC73C\uB85C \uC2DC\uC791\uD558\uC138\uC694."));
|
|
5887
|
+
process.exitCode = 1;
|
|
5888
|
+
return;
|
|
5889
|
+
}
|
|
5890
|
+
const goal = resolveGoal(opts.id, goals);
|
|
5891
|
+
if (!goal || typeof goal.frontmatter.id !== "number") {
|
|
5892
|
+
console.error(chalk31.red(` \u274C \uB300\uC0C1 goal \uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4${opts.id ? ` (--id ${opts.id})` : " (active goal \uC5C6\uC74C)"}.`));
|
|
5893
|
+
process.exitCode = 1;
|
|
5894
|
+
return;
|
|
5895
|
+
}
|
|
5896
|
+
const goalId = goal.frontmatter.id;
|
|
5897
|
+
const goalStatus = goal.frontmatter.status ?? "NOT_STARTED";
|
|
5898
|
+
const checks = parseCompletionChecks(goal.body);
|
|
5899
|
+
if (opts.id === void 0 && goalStatus === "NOT_STARTED") {
|
|
5900
|
+
console.error(chalk31.yellow(` \u26A0\uFE0F active goal ${goalId} \uAC00 NOT_STARTED \u2014 \uC644\uB8CC \uC8FC\uC7A5\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uAC80\uC99D \uB300\uC0C1\uC740 --id \uB85C \uC9C0\uC815\uD558\uC138\uC694.`));
|
|
5901
|
+
}
|
|
5902
|
+
const jsonPath = join11(cwd, REPORT_PATH_REL);
|
|
5903
|
+
if (!existsSync17(jsonPath)) {
|
|
5904
|
+
console.error(chalk31.yellow(` \u26A0\uFE0F \uC99D\uAC70 \uBD80\uC7AC \u2014 ${REPORT_PATH_REL} \uC5C6\uC74C. review \uB97C \uC911\uB2E8\uD569\uB2C8\uB2E4(\uC0C8 \uC99D\uAC70 \uC548 \uB9CC\uB4E6).`));
|
|
5905
|
+
printNextStep({
|
|
5906
|
+
message: "\uC99D\uAC70(latest.json)\uAC00 \uC788\uC5B4\uC57C review \uAC00 \uAD50\uCC28\uAC80\uC99D\uD569\uB2C8\uB2E4:",
|
|
5907
|
+
command: "vhk verify",
|
|
5908
|
+
cursorHint: "\uBA3C\uC800 \uAC80\uC99D \uB3CC\uB824\uC918"
|
|
5909
|
+
});
|
|
5910
|
+
process.exitCode = 1;
|
|
5911
|
+
return;
|
|
5912
|
+
}
|
|
5913
|
+
let report;
|
|
5914
|
+
try {
|
|
5915
|
+
report = readJsonFile(jsonPath);
|
|
5916
|
+
} catch {
|
|
5917
|
+
console.error(chalk31.red(` \u274C ${REPORT_PATH_REL} \uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4(\uC190\uC0C1). vhk verify \uB85C \uC7AC\uC0DD\uC131\uD558\uC138\uC694.`));
|
|
5918
|
+
process.exitCode = 1;
|
|
5919
|
+
return;
|
|
5920
|
+
}
|
|
5921
|
+
const analysis = crossCheck(checks, goalStatus, report, Date.now());
|
|
5922
|
+
const result = {
|
|
5923
|
+
...analysis,
|
|
5924
|
+
reviewedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5925
|
+
goalId,
|
|
5926
|
+
goalStatus,
|
|
5927
|
+
reportStatus: report.status
|
|
5928
|
+
};
|
|
5929
|
+
console.log(chalk31.bold(`
|
|
5930
|
+
\u{1F52C} \uC801\uB300\uC801 \uC790\uAE30\uAC80\uC99D (review) \u2014 Goal ${goalId}`));
|
|
5931
|
+
console.log(chalk31.gray("\u2500".repeat(44)));
|
|
5932
|
+
console.log(
|
|
5933
|
+
chalk31.dim(
|
|
5934
|
+
` goal status: ${goalStatus} \xB7 verify: ${report.status} \xB7 \uCCB4\uD06C\uB41C \uC644\uB8CC\uC870\uAC74 ${result.checkedCount}\uAC1C (\uB9E4\uD551 ${result.mappedCount} / \uBBF8\uAC80\uC99D ${result.unmappedCount}, coverage ${result.coverage * 100 | 0}%)`
|
|
5935
|
+
)
|
|
5936
|
+
);
|
|
5937
|
+
console.log(chalk31.dim(` \uC99D\uAC70 \uC2E0\uC120\uB3C4: ${result.freshness.note}`));
|
|
5938
|
+
if (result.checkedCount === 0) {
|
|
5939
|
+
console.log(chalk31.yellow("\n \u26AA \uC644\uB8CC \uC8FC\uC7A5 \uC5C6\uC74C(\uCCB4\uD06C\uB41C \uC644\uB8CC\uC870\uAC74 0\uAC1C) \u2014 \uC2EC\uBB38\uD560 \uB300\uC0C1\uC774 \uC5C6\uC2B5\uB2C8\uB2E4(vacuous)."));
|
|
5940
|
+
}
|
|
5941
|
+
if (result.suspicions.length > 0) {
|
|
5942
|
+
console.log(chalk31.red.bold(`
|
|
5943
|
+
\u{1F6A9} \uAC70\uC9D3\uC644\uB8CC \uC758\uC2EC ${result.suspicions.length}\uAC74`));
|
|
5944
|
+
for (const s of result.suspicions) console.log(chalk31.red(` \u2717 ${s.check}
|
|
5945
|
+
\u21B3 ${s.reason}`));
|
|
5946
|
+
}
|
|
5947
|
+
if (result.gaps.length > 0) {
|
|
5948
|
+
console.log(chalk31.yellow.bold(`
|
|
5949
|
+
\u26A0\uFE0F \uBBF8\uAC80\uC99D(unmapped) ${result.gaps.length}\uAC74 \u2014 \uAC8C\uC774\uD2B8\uB85C \uC790\uB3D9 \uD655\uC778 \uBD88\uAC00`));
|
|
5950
|
+
for (const g of result.gaps) console.log(chalk31.yellow(` ? ${g.check}`));
|
|
5951
|
+
}
|
|
5952
|
+
if (result.checkedCount > 0 && result.suspicions.length === 0 && result.gaps.length === 0) {
|
|
5953
|
+
console.log(chalk31.green("\n \u2713 \uCCB4\uD06C\uB41C \uC644\uB8CC\uC870\uAC74\uC774 \uBAA8\uB450 \uAC8C\uC774\uD2B8 \uC99D\uAC70\uB85C \uB4B7\uBC1B\uCE68\uB428."));
|
|
5954
|
+
}
|
|
5955
|
+
console.log(`
|
|
5956
|
+
\uC2E0\uB8B0\uB3C4: ${CONFIDENCE_LABEL[result.confidence]}`);
|
|
5957
|
+
console.log(chalk31.yellow(`
|
|
5958
|
+
${result.disclaimer}`));
|
|
5959
|
+
let mergeOk = false;
|
|
5960
|
+
try {
|
|
5961
|
+
const merged = { ...report, review: result };
|
|
5962
|
+
writeFileSync12(jsonPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
5963
|
+
mergeOk = true;
|
|
5964
|
+
console.log(chalk31.dim(` \u{1F4C4} \uD310\uC815 \uBCD1\uD569: ${REPORT_PATH_REL} (review \uC139\uC158)`));
|
|
5965
|
+
} catch (e) {
|
|
5966
|
+
console.error(chalk31.red(` \u274C review \uD310\uC815 \uAE30\uB85D \uC2E4\uD328: ${e instanceof Error ? e.message : String(e)}`));
|
|
5967
|
+
}
|
|
5968
|
+
if (!mergeOk) {
|
|
5969
|
+
process.exitCode = 1;
|
|
5970
|
+
printNextStep({
|
|
5971
|
+
message: "review \uD310\uC815 \uAE30\uB85D \uC2E4\uD328(exit 1) \u2014 .vhk/reports \uC4F0\uAE30 \uAD8C\uD55C \uD655\uC778 \uD6C4 \uC7AC\uC2E4\uD589:",
|
|
5972
|
+
command: "vhk review",
|
|
5973
|
+
cursorHint: "\uAD8C\uD55C \uD655\uC778 \uD6C4 \uB2E4\uC2DC \uAC80\uD1A0\uD574\uC918"
|
|
5974
|
+
});
|
|
5975
|
+
return;
|
|
5976
|
+
}
|
|
5977
|
+
const vacuous = result.checkedCount === 0;
|
|
5978
|
+
const cleanHigh = result.suspicions.length === 0 && result.gaps.length === 0 && result.confidence === "high";
|
|
5979
|
+
process.exitCode = vacuous || cleanHigh ? 0 : 1;
|
|
5980
|
+
if (vacuous) {
|
|
5981
|
+
printNextStep({
|
|
5982
|
+
message: "\uC644\uB8CC \uC8FC\uC7A5\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uC644\uB8CC\uC870\uAC74\uC744 \uCC44\uC6B4 \uB4A4 \uAC80\uC99D\uD558\uC138\uC694:",
|
|
5983
|
+
command: "vhk verify",
|
|
5984
|
+
cursorHint: "\uC644\uB8CC\uC870\uAC74 \uCC44\uC6CC\uC918"
|
|
5985
|
+
});
|
|
5986
|
+
} else if (result.suspicions.length > 0) {
|
|
5987
|
+
console.log(chalk31.dim("\n AI \uC7AC\uC9C8\uBB38 \uD504\uB86C\uD504\uD2B8:"));
|
|
5988
|
+
console.log(chalk31.cyan(result.reprompt.split("\n").map((l) => ` ${l}`).join("\n")));
|
|
5989
|
+
printNextStep({
|
|
5990
|
+
message: "\uAC70\uC9D3\uC644\uB8CC \uC758\uC2EC\uC73C\uB85C \uC2E4\uD328(exit 1) \u2014 \uC99D\uAC70 \uBCF4\uAC15 \uD6C4 \uB2E4\uC2DC \uAC80\uC99D\uD558\uC138\uC694:",
|
|
5991
|
+
command: "vhk verify",
|
|
5992
|
+
cursorHint: "\uC758\uC2EC \uD56D\uBAA9 \uC99D\uAC70 \uBCF4\uAC15\uD574\uC918",
|
|
5993
|
+
alternative: result.suspicions[0].reason
|
|
5994
|
+
});
|
|
5995
|
+
} else if (cleanHigh) {
|
|
5996
|
+
printNextStep({
|
|
5997
|
+
message: "\uC2EC\uBB38 \uD1B5\uACFC(\uC2E0\uB8B0\uB3C4 \uB192\uC74C, \uBCF4\uC7A5 \uC544\uB2D8). \uC644\uB8CC \uCC98\uB9AC\uD558\uB824\uBA74:",
|
|
5998
|
+
command: `vhk goal done --id ${goalId}`,
|
|
5999
|
+
cursorHint: "goal \uC644\uB8CC \uCC98\uB9AC\uD574\uC918"
|
|
6000
|
+
});
|
|
6001
|
+
} else {
|
|
6002
|
+
console.log(chalk31.dim("\n AI \uC7AC\uC9C8\uBB38 \uD504\uB86C\uD504\uD2B8:"));
|
|
6003
|
+
console.log(chalk31.cyan(result.reprompt.split("\n").map((l) => ` ${l}`).join("\n")));
|
|
6004
|
+
printNextStep({
|
|
6005
|
+
message: `\uC99D\uAC70 \uBD88\uCDA9\uBD84(\uC2E0\uB8B0\uB3C4 ${result.confidence}) \u2014 goal done \uAE08\uC9C0. \uCEE4\uBC84\uB9AC\uC9C0/\uC2E0\uC120\uB3C4 \uBCF4\uAC15 \uD6C4 \uC7AC\uAC80\uC99D:`,
|
|
6006
|
+
command: "vhk verify",
|
|
6007
|
+
cursorHint: "\uC99D\uAC70 \uBCF4\uAC15 \uD6C4 \uB2E4\uC2DC \uAC80\uC99D\uD574\uC918"
|
|
6008
|
+
});
|
|
6009
|
+
}
|
|
6010
|
+
}
|
|
6011
|
+
|
|
5734
6012
|
// src/lib/risk-policy.ts
|
|
5735
6013
|
var HIGH_RISK_ACTIONS = [
|
|
5736
6014
|
"undo",
|
|
@@ -5896,6 +6174,8 @@ async function dispatchNlpRoute(route, input) {
|
|
|
5896
6174
|
return mode();
|
|
5897
6175
|
case "verify":
|
|
5898
6176
|
return verify();
|
|
6177
|
+
case "review":
|
|
6178
|
+
return review();
|
|
5899
6179
|
}
|
|
5900
6180
|
}
|
|
5901
6181
|
var STATE_CHANGING_COMMANDS = /* @__PURE__ */ new Set([
|
|
@@ -5909,14 +6189,14 @@ function requiresConfirmation(route) {
|
|
|
5909
6189
|
async function runNaturalLanguageRoute(input) {
|
|
5910
6190
|
const route = routeNaturalLanguage(input);
|
|
5911
6191
|
if (!route) {
|
|
5912
|
-
console.log(
|
|
6192
|
+
console.log(chalk32.yellow(`
|
|
5913
6193
|
\u2753 "${input}" \u2014 ${ko.nlp.notMatched}
|
|
5914
6194
|
`));
|
|
5915
6195
|
return;
|
|
5916
6196
|
}
|
|
5917
6197
|
console.log("");
|
|
5918
|
-
console.log(
|
|
5919
|
-
console.log(
|
|
6198
|
+
console.log(chalk32.cyan(` \u{1F4AC} "${input}"`));
|
|
6199
|
+
console.log(chalk32.cyan(` \u2192 ${route.explanation}`));
|
|
5920
6200
|
if (requiresConfirmation(route)) {
|
|
5921
6201
|
const { confirm } = await inquirer12.prompt([{
|
|
5922
6202
|
type: "confirm",
|
|
@@ -5925,7 +6205,7 @@ async function runNaturalLanguageRoute(input) {
|
|
|
5925
6205
|
default: true
|
|
5926
6206
|
}]);
|
|
5927
6207
|
if (!confirm) {
|
|
5928
|
-
console.log(
|
|
6208
|
+
console.log(chalk32.dim(` ${ko.nlp.menuHint}`));
|
|
5929
6209
|
return;
|
|
5930
6210
|
}
|
|
5931
6211
|
}
|
|
@@ -5934,7 +6214,7 @@ async function runNaturalLanguageRoute(input) {
|
|
|
5934
6214
|
if (riskAction) {
|
|
5935
6215
|
await runGuarded(
|
|
5936
6216
|
riskAction,
|
|
5937
|
-
{ channel: "nl", approved: false, log: (m) => console.log(
|
|
6217
|
+
{ channel: "nl", approved: false, log: (m) => console.log(chalk32.yellow(` ${m}`)) },
|
|
5938
6218
|
() => dispatchNlpRoute(route, input)
|
|
5939
6219
|
);
|
|
5940
6220
|
return;
|
|
@@ -5943,77 +6223,77 @@ async function runNaturalLanguageRoute(input) {
|
|
|
5943
6223
|
}
|
|
5944
6224
|
|
|
5945
6225
|
// src/commands/agent.ts
|
|
5946
|
-
import
|
|
6226
|
+
import chalk33 from "chalk";
|
|
5947
6227
|
function activeGoalId() {
|
|
5948
6228
|
const goals = listGoals("goals");
|
|
5949
6229
|
const id = selectActiveId(goals);
|
|
5950
6230
|
return id ?? void 0;
|
|
5951
6231
|
}
|
|
5952
6232
|
async function blocker(description) {
|
|
5953
|
-
console.log(
|
|
6233
|
+
console.log(chalk33.bold(`
|
|
5954
6234
|
${ko.agent.blockerTitle}
|
|
5955
6235
|
`));
|
|
5956
6236
|
if (!description || !description.trim()) {
|
|
5957
|
-
console.log(
|
|
5958
|
-
console.log(
|
|
6237
|
+
console.log(chalk33.red(" \u274C \uBE14\uB85C\uCEE4 \uC124\uBA85\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694."));
|
|
6238
|
+
console.log(chalk33.dim(' \uC608: vhk blocker "tsc \uC5D0\uB7EC \u2014 simple-git \uD0C0\uC785 \uD638\uD658"'));
|
|
5959
6239
|
process.exitCode = 1;
|
|
5960
6240
|
return;
|
|
5961
6241
|
}
|
|
5962
6242
|
const goalId = activeGoalId();
|
|
5963
6243
|
const r = appendBlocker(description, goalId);
|
|
5964
|
-
console.log(
|
|
6244
|
+
console.log(chalk33.green(` \u2705 blocker \uAE30\uB85D (\uD604\uC7AC \uD65C\uC131 ${r.count}\uAC74)`));
|
|
5965
6245
|
if (r.hardStopTripped) {
|
|
5966
|
-
console.log(
|
|
5967
|
-
console.log(
|
|
6246
|
+
console.log(chalk33.red.bold(" \u{1F6D1} HARD_STOP \uC790\uB3D9 \uC0DD\uC131 \u2014 \uBAA8\uB4E0 \uC790\uB3D9\uD654 \uC911\uB2E8."));
|
|
6247
|
+
console.log(chalk33.yellow(" \uC0AC\uB78C \uAC80\uD1A0 \uD6C4 `vhk resume --confirm` \uC73C\uB85C\uB9CC \uD574\uC81C."));
|
|
5968
6248
|
process.exitCode = 2;
|
|
5969
6249
|
}
|
|
5970
6250
|
}
|
|
5971
6251
|
async function learn(lesson) {
|
|
5972
|
-
console.log(
|
|
6252
|
+
console.log(chalk33.bold(`
|
|
5973
6253
|
${ko.agent.learnTitle}
|
|
5974
6254
|
`));
|
|
5975
6255
|
if (!lesson || !lesson.trim()) {
|
|
5976
|
-
console.log(
|
|
5977
|
-
console.log(
|
|
6256
|
+
console.log(chalk33.red(" \u274C \uAD50\uD6C8 \uB0B4\uC6A9\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694."));
|
|
6257
|
+
console.log(chalk33.dim(' \uC608: vhk learn "PowerShell \uC5D0\uC11C\uB294 ; \uC0AC\uC6A9 (&& \uBBF8\uC9C0\uC6D0)"'));
|
|
5978
6258
|
process.exitCode = 1;
|
|
5979
6259
|
return;
|
|
5980
6260
|
}
|
|
5981
6261
|
const goalId = activeGoalId();
|
|
5982
6262
|
appendLearning(lesson, goalId);
|
|
5983
|
-
console.log(
|
|
6263
|
+
console.log(chalk33.green(" \u2705 learnings.md append."));
|
|
5984
6264
|
console.log(
|
|
5985
|
-
|
|
6265
|
+
chalk33.dim(" \uACB0\uC815\uC0AC\uD56D(decision)\uC740 `vhk memory add` \uB85C \uBCC4\uB3C4 \uAE30\uB85D \u2014 SoT \uBD84\uB9AC.")
|
|
5986
6266
|
);
|
|
5987
6267
|
}
|
|
5988
6268
|
async function resume(opts = {}) {
|
|
5989
|
-
console.log(
|
|
6269
|
+
console.log(chalk33.bold(`
|
|
5990
6270
|
${ko.agent.resumeTitle}
|
|
5991
6271
|
`));
|
|
5992
6272
|
if (!isHardStopActive()) {
|
|
5993
|
-
console.log(
|
|
6273
|
+
console.log(chalk33.dim(" HARD_STOP \uD65C\uC131 \uC544\uB2D8 \u2014 \uD560 \uC77C \uC5C6\uC74C."));
|
|
5994
6274
|
return;
|
|
5995
6275
|
}
|
|
5996
6276
|
const reason = readHardStopReason();
|
|
5997
6277
|
if (reason) {
|
|
5998
|
-
console.log(
|
|
5999
|
-
console.log(
|
|
6278
|
+
console.log(chalk33.yellow(" \u{1F4CB} HARD_STOP \uC0AC\uC720:"));
|
|
6279
|
+
console.log(chalk33.dim(` ${reason.split("\n").join("\n ")}`));
|
|
6000
6280
|
console.log("");
|
|
6001
6281
|
}
|
|
6002
6282
|
if (!opts.confirm) {
|
|
6003
6283
|
console.log(
|
|
6004
|
-
|
|
6284
|
+
chalk33.red(
|
|
6005
6285
|
" \u274C --confirm \uD50C\uB798\uADF8 \uC5C6\uC774\uB294 \uD574\uC81C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC790\uB3D9 \uD638\uCD9C \uAE08\uC9C0)."
|
|
6006
6286
|
)
|
|
6007
6287
|
);
|
|
6008
|
-
console.log(
|
|
6288
|
+
console.log(chalk33.yellow(" \uC0AC\uC720\uB97C \uD655\uC778\uD55C \uD6C4 \uB2E4\uC2DC: vhk resume --confirm"));
|
|
6009
6289
|
process.exitCode = 1;
|
|
6010
6290
|
return;
|
|
6011
6291
|
}
|
|
6012
6292
|
const removed = clearHardStop();
|
|
6013
6293
|
if (removed) {
|
|
6014
|
-
console.log(
|
|
6294
|
+
console.log(chalk33.green(" \u2705 HARD_STOP \uD574\uC81C. \uC790\uB3D9\uD654 \uC7AC\uAC1C \uAC00\uB2A5."));
|
|
6015
6295
|
} else {
|
|
6016
|
-
console.log(
|
|
6296
|
+
console.log(chalk33.dim(" \uD30C\uC77C\uC774 \uC774\uBBF8 \uC5C6\uC74C \u2014 no-op."));
|
|
6017
6297
|
}
|
|
6018
6298
|
}
|
|
6019
6299
|
|
|
@@ -6034,7 +6314,7 @@ async function guardCli(action, approved, run) {
|
|
|
6034
6314
|
}]);
|
|
6035
6315
|
return ok;
|
|
6036
6316
|
},
|
|
6037
|
-
log: (m) => console.log(
|
|
6317
|
+
log: (m) => console.log(chalk34.yellow(` ${m}`))
|
|
6038
6318
|
},
|
|
6039
6319
|
run
|
|
6040
6320
|
);
|
|
@@ -6047,7 +6327,7 @@ async function guardCliDefer(action, approved, run) {
|
|
|
6047
6327
|
approved,
|
|
6048
6328
|
// TTY 면 통과(명령이 자체 확인), 비대화형은 confirm 불가 → 가드가 차단.
|
|
6049
6329
|
confirm: async () => !!process.stdout.isTTY,
|
|
6050
|
-
log: (m) => console.log(
|
|
6330
|
+
log: (m) => console.log(chalk34.yellow(` ${m}`))
|
|
6051
6331
|
},
|
|
6052
6332
|
run
|
|
6053
6333
|
);
|
|
@@ -6086,6 +6366,7 @@ var KO_ALIASES = {
|
|
|
6086
6366
|
memory: "\uAE30\uC5B5",
|
|
6087
6367
|
brief: "\uBE0C\uB9AC\uD551",
|
|
6088
6368
|
goal: "\uBAA9\uD45C",
|
|
6369
|
+
review: "\uAC80\uD1A0",
|
|
6089
6370
|
blocker: "\uBE14\uB85C\uCEE4",
|
|
6090
6371
|
learn: "\uAD50\uD6C8",
|
|
6091
6372
|
resume: "\uC7AC\uAC1C"
|
|
@@ -6210,6 +6491,9 @@ program.command("mode [target]").alias("\uBAA8\uB4DC").description("Safety Mode
|
|
|
6210
6491
|
program.command("verify").alias("\uC0AC\uC804\uC810\uAC80").option("--json", "\uB9AC\uD3EC\uD2B8 JSON \uC744 stdout \uC73C\uB85C \uCD9C\uB825 (CI\uC6A9 \u2014 \uACBD\uB85C \uB300\uC2E0)").option("--report", "latest.json \uC744 \uC0AC\uB78C\uC6A9 \uC815\uC801 HTML(.vhk/reports/latest.html) \uB85C \uB80C\uB354 (\uC678\uBD80 \uC758\uC874 0)").option("--open", "\uB9AC\uD3EC\uD2B8 \uC0DD\uC131 \uD6C4 \uAE30\uBCF8 \uBE0C\uB77C\uC6B0\uC800\uB85C \uC5F4\uAE30 (\uBE44\uB300\uD654\uD615/CI/MCP \uC790\uB3D9 \uC2A4\uD0B5)").description("\uAC80\uC99D \uAC8C\uC774\uD2B8(tsc/test/build/secure) \uC2E4\uC81C \uC2E4\uD589 + \uC99D\uAC70 \uAE30\uB85D (.vhk/reports/latest.json)").action(async (opts) => {
|
|
6211
6492
|
await verify(opts);
|
|
6212
6493
|
});
|
|
6494
|
+
program.command("review").alias("\uAC80\uD1A0").option("--id <id>", "\uB300\uC0C1 goal id (\uC5C6\uC73C\uBA74 active goal)").description("\uC801\uB300\uC801 \uC790\uAE30\uAC80\uC99D \u2014 latest.json \u2194 goal \uC644\uB8CC\uC870\uAC74 \uAD50\uCC28\uAC80\uC99D (\uAC70\uC9D3\uC644\uB8CC \uC758\uC2EC \uD0D0\uC9C0, \uBCF4\uC7A5 \uC544\uB2D8)").action(async (opts) => {
|
|
6495
|
+
await review(opts);
|
|
6496
|
+
});
|
|
6213
6497
|
program.command("context-show").alias("\uB9E5\uB77D\uBCF4\uAE30").description("\uD604\uC7AC \uCEE8\uD14D\uC2A4\uD2B8 \uD30C\uC77C \uB0B4\uC6A9 \uCD9C\uB825").action(async () => {
|
|
6214
6498
|
await contextShow();
|
|
6215
6499
|
});
|
|
@@ -6324,9 +6608,9 @@ if (isMainModule) {
|
|
|
6324
6608
|
}
|
|
6325
6609
|
} catch (err) {
|
|
6326
6610
|
if (isPromptAbortError(err)) {
|
|
6327
|
-
console.error(
|
|
6611
|
+
console.error(chalk34.yellow("\n \u26A0\uFE0F \uB300\uD654\uD615 \uC785\uB825\uC774 \uCDE8\uC18C/\uC885\uB8CC\uB410\uC2B5\uB2C8\uB2E4. (\uBE44\uB300\uD654\uD615 \uD658\uACBD\uC5D0\uC11C\uB294 \uD574\uB2F9 \uBA85\uB839\uC744 \uC4F8 \uC218 \uC5C6\uC5B4\uC694)"));
|
|
6328
6612
|
} else {
|
|
6329
|
-
console.error(
|
|
6613
|
+
console.error(chalk34.red(`
|
|
6330
6614
|
\u274C ${err instanceof Error ? err.message : String(err)}`));
|
|
6331
6615
|
}
|
|
6332
6616
|
process.exitCode = 1;
|
package/dist/mcp/index.js
CHANGED
package/package.json
CHANGED