@byh3071/vhk 1.8.1 → 1.9.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/README.md +1 -0
- package/dist/index.js +241 -38
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -175,6 +175,7 @@ vhk 기획 끝났고 바로 시작
|
|
|
175
175
|
| `vhk memory` | `기억` | 결정사항 기억 관리 (`add` / `list` / `remove`, `.vhk/memory.json` 기반, 태그 지원) |
|
|
176
176
|
| `vhk brief` | `브리핑` | 프로젝트 정보 + git 상태 + 결정사항 + 레퍼런스 통합 보고서 `.vhk/brief.md` |
|
|
177
177
|
| `vhk goal` | `목표` | Goal 단계별 미션 관리 (`init` / `list` / `next` / `check` / `done`) — vspec/vooster 패턴 |
|
|
178
|
+
| `vhk mission` | `미션` | 미션 계약(`set` / `check` / `clear`) — 작업 범위·금지선 선언·검증 (`.vhk/mission.json`, 경로 glob) |
|
|
178
179
|
| `vhk blocker <설명>` | `블로커` | 블로커 1건 → `docs/state/blockers.md` append. 3건 누적 시 `.vhk/HARD_STOP` 자동 생성 |
|
|
179
180
|
| `vhk learn <교훈>` | `교훈` | 교훈 1건 → `docs/state/learnings.md` append (memory.json 과 별도 SoT) |
|
|
180
181
|
| `vhk resume --confirm` | `재개` | `.vhk/HARD_STOP` 해제 (사람 확인 필요, 자동 호출 금지) |
|
package/dist/index.js
CHANGED
|
@@ -48,8 +48,8 @@ import {
|
|
|
48
48
|
// src/index.ts
|
|
49
49
|
import { Command, Help } from "commander";
|
|
50
50
|
import { pathToFileURL } from "url";
|
|
51
|
-
import
|
|
52
|
-
import
|
|
51
|
+
import chalk35 from "chalk";
|
|
52
|
+
import inquirer14 from "inquirer";
|
|
53
53
|
|
|
54
54
|
// src/lib/nlp-router.ts
|
|
55
55
|
function normalize(input) {
|
|
@@ -129,6 +129,12 @@ var RULES = [
|
|
|
129
129
|
confidence: "high",
|
|
130
130
|
test: (t2) => /적대\s*검증|자기\s*검증|거짓\s*완료|완료\s*심문|^review$|^검토$/.test(t2)
|
|
131
131
|
},
|
|
132
|
+
{
|
|
133
|
+
command: "mission",
|
|
134
|
+
explanation: "\uBBF8\uC158 \uACC4\uC57D \u2014 \uC791\uC5C5 \uBC94\uC704\xB7\uAE08\uC9C0\uC120 \uC120\uC5B8/\uAC80\uC99D (vhk mission)",
|
|
135
|
+
confidence: "high",
|
|
136
|
+
test: (t2) => /미션\s*계약|작업\s*범위|범위\s*검증|^mission$|^미션$/.test(t2)
|
|
137
|
+
},
|
|
132
138
|
{
|
|
133
139
|
command: "init",
|
|
134
140
|
explanation: "\uBB38\uC11C/\uD558\uB124\uC2A4 \uD30C\uC77C\uB9CC \uC0DD\uC131 (vhk init) \u2014 git/MCP/context\uB294 \uC81C\uC678",
|
|
@@ -371,7 +377,8 @@ var CONTAINER_SUBCOMMANDS = {
|
|
|
371
377
|
secure: ["scan"],
|
|
372
378
|
design: ["palette"],
|
|
373
379
|
env: ["check"],
|
|
374
|
-
mode: ["lite", "standard", "strict"]
|
|
380
|
+
mode: ["lite", "standard", "strict"],
|
|
381
|
+
mission: ["set", "check", "clear"]
|
|
375
382
|
};
|
|
376
383
|
var CONTAINER_ALIASES = {
|
|
377
384
|
\uBAA9\uD45C: "goal",
|
|
@@ -381,7 +388,8 @@ var CONTAINER_ALIASES = {
|
|
|
381
388
|
\uBCF4\uC548: "secure",
|
|
382
389
|
\uB514\uC790\uC778: "design",
|
|
383
390
|
\uD658\uACBD\uBCC0\uC218: "env",
|
|
384
|
-
\uBAA8\uB4DC: "mode"
|
|
391
|
+
\uBAA8\uB4DC: "mode",
|
|
392
|
+
\uBBF8\uC158: "mission"
|
|
385
393
|
};
|
|
386
394
|
|
|
387
395
|
// src/lib/cli-args.ts
|
|
@@ -476,6 +484,8 @@ var KNOWN_COMMAND_TOKENS = /* @__PURE__ */ new Set([
|
|
|
476
484
|
"\uC0AC\uC804\uC810\uAC80",
|
|
477
485
|
"review",
|
|
478
486
|
"\uAC80\uD1A0",
|
|
487
|
+
"mission",
|
|
488
|
+
"\uBBF8\uC158",
|
|
479
489
|
"help"
|
|
480
490
|
]);
|
|
481
491
|
function isOptionToken(token) {
|
|
@@ -513,8 +523,8 @@ function detectNaturalLanguageInput(argv) {
|
|
|
513
523
|
}
|
|
514
524
|
|
|
515
525
|
// src/lib/nlp-run.ts
|
|
516
|
-
import
|
|
517
|
-
import
|
|
526
|
+
import chalk33 from "chalk";
|
|
527
|
+
import inquirer13 from "inquirer";
|
|
518
528
|
|
|
519
529
|
// src/commands/gate.ts
|
|
520
530
|
import inquirer from "inquirer";
|
|
@@ -6009,6 +6019,183 @@ ${result.disclaimer}`));
|
|
|
6009
6019
|
}
|
|
6010
6020
|
}
|
|
6011
6021
|
|
|
6022
|
+
// src/commands/mission.ts
|
|
6023
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync12, writeFileSync as writeFileSync13, rmSync as rmSync3 } from "fs";
|
|
6024
|
+
import { join as join12 } from "path";
|
|
6025
|
+
import chalk32 from "chalk";
|
|
6026
|
+
import inquirer12 from "inquirer";
|
|
6027
|
+
import { simpleGit as simpleGit3 } from "simple-git";
|
|
6028
|
+
var MISSION_PATH_REL = join12(".vhk", "mission.json");
|
|
6029
|
+
var MISSION_SCHEMA_VERSION = 1;
|
|
6030
|
+
var MISSION_DISCLAIMER = "\u26A0\uFE0F mission check \uB294 \uACBD\uB85C glob \uAE30\uC900\uC785\uB2C8\uB2E4 \u2014 objective \uC758\uBBF8 \uBD80\uD569\uC740 \uAC80\uC99D\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(\uC2E0\uB8B0\uB3C4 \uC2E0\uD638, \uBCF4\uC7A5 \uC544\uB2D8).";
|
|
6031
|
+
function globToRegExp(glob) {
|
|
6032
|
+
let re = "";
|
|
6033
|
+
for (let i = 0; i < glob.length; i++) {
|
|
6034
|
+
const c = glob[i];
|
|
6035
|
+
if (c === "*") {
|
|
6036
|
+
if (glob[i + 1] === "*") {
|
|
6037
|
+
re += ".*";
|
|
6038
|
+
i++;
|
|
6039
|
+
if (glob[i + 1] === "/") i++;
|
|
6040
|
+
} else {
|
|
6041
|
+
re += "[^/]*";
|
|
6042
|
+
}
|
|
6043
|
+
} else if (c === "?") {
|
|
6044
|
+
re += "[^/]";
|
|
6045
|
+
} else if (".+^${}()|[]\\".includes(c)) {
|
|
6046
|
+
re += "\\" + c;
|
|
6047
|
+
} else {
|
|
6048
|
+
re += c;
|
|
6049
|
+
}
|
|
6050
|
+
}
|
|
6051
|
+
return new RegExp("^" + re + "$");
|
|
6052
|
+
}
|
|
6053
|
+
function matchesAny(file, globs) {
|
|
6054
|
+
const norm = file.replace(/\\/g, "/");
|
|
6055
|
+
for (const g of globs) {
|
|
6056
|
+
if (globToRegExp(g).test(norm)) return g;
|
|
6057
|
+
}
|
|
6058
|
+
return null;
|
|
6059
|
+
}
|
|
6060
|
+
function checkMission(changedFiles, mission) {
|
|
6061
|
+
const violations = [];
|
|
6062
|
+
const warnings = [];
|
|
6063
|
+
for (const file of changedFiles) {
|
|
6064
|
+
const forbiddenHit = matchesAny(file, mission.forbidden);
|
|
6065
|
+
if (forbiddenHit) {
|
|
6066
|
+
violations.push({ file, pattern: forbiddenHit });
|
|
6067
|
+
continue;
|
|
6068
|
+
}
|
|
6069
|
+
if (mission.scope.length > 0 && !matchesAny(file, mission.scope)) {
|
|
6070
|
+
warnings.push({ file });
|
|
6071
|
+
}
|
|
6072
|
+
}
|
|
6073
|
+
return { violations, warnings, disclaimer: MISSION_DISCLAIMER };
|
|
6074
|
+
}
|
|
6075
|
+
function readMission(cwd = process.cwd()) {
|
|
6076
|
+
const p = join12(cwd, MISSION_PATH_REL);
|
|
6077
|
+
if (!existsSync18(p)) return null;
|
|
6078
|
+
try {
|
|
6079
|
+
const m = readJsonFile(p);
|
|
6080
|
+
if (m && typeof m.objective === "string") return m;
|
|
6081
|
+
return null;
|
|
6082
|
+
} catch {
|
|
6083
|
+
return null;
|
|
6084
|
+
}
|
|
6085
|
+
}
|
|
6086
|
+
function writeMission(cwd, mission) {
|
|
6087
|
+
mkdirSync12(join12(cwd, ".vhk"), { recursive: true });
|
|
6088
|
+
writeFileSync13(join12(cwd, MISSION_PATH_REL), JSON.stringify(mission, null, 2) + "\n", "utf-8");
|
|
6089
|
+
}
|
|
6090
|
+
async function collectChangedFiles(cwd) {
|
|
6091
|
+
const status2 = await simpleGit3(cwd).status();
|
|
6092
|
+
const set = /* @__PURE__ */ new Set();
|
|
6093
|
+
for (const f of status2.files) if (f.path) set.add(f.path.replace(/\\/g, "/"));
|
|
6094
|
+
return [...set];
|
|
6095
|
+
}
|
|
6096
|
+
async function missionSet(opts = {}) {
|
|
6097
|
+
if (!ensureNotHardStopped("mission set")) return;
|
|
6098
|
+
const cwd = process.cwd();
|
|
6099
|
+
const existing = readMission(cwd);
|
|
6100
|
+
let objective = opts.objective ?? existing?.objective ?? "";
|
|
6101
|
+
if (!objective) {
|
|
6102
|
+
if (isInteractive(opts)) {
|
|
6103
|
+
const ans = await inquirer12.prompt([
|
|
6104
|
+
{ type: "input", name: "obj", message: "\uBBF8\uC158 \uBAA9\uD45C(objective)\uB294?" }
|
|
6105
|
+
]);
|
|
6106
|
+
objective = ans.obj.trim();
|
|
6107
|
+
}
|
|
6108
|
+
if (!objective) {
|
|
6109
|
+
console.error(chalk32.red(' \u274C objective \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. --objective "..." \uB85C \uC9C0\uC815\uD558\uC138\uC694(\uBE44\uB300\uD654\uD615).'));
|
|
6110
|
+
process.exitCode = 1;
|
|
6111
|
+
return;
|
|
6112
|
+
}
|
|
6113
|
+
}
|
|
6114
|
+
const scope = opts.clearScope ? [] : opts.scope ?? existing?.scope ?? [];
|
|
6115
|
+
const forbidden = opts.clearForbidden ? [] : opts.forbidden ?? existing?.forbidden ?? [];
|
|
6116
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6117
|
+
const mission = {
|
|
6118
|
+
schemaVersion: MISSION_SCHEMA_VERSION,
|
|
6119
|
+
objective,
|
|
6120
|
+
scope,
|
|
6121
|
+
forbidden,
|
|
6122
|
+
createdAt: existing?.createdAt ?? now,
|
|
6123
|
+
updatedAt: now
|
|
6124
|
+
};
|
|
6125
|
+
try {
|
|
6126
|
+
writeMission(cwd, mission);
|
|
6127
|
+
} catch (e) {
|
|
6128
|
+
console.error(chalk32.red(` \u274C mission.json \uAE30\uB85D \uC2E4\uD328: ${e instanceof Error ? e.message : String(e)}`));
|
|
6129
|
+
process.exitCode = 1;
|
|
6130
|
+
return;
|
|
6131
|
+
}
|
|
6132
|
+
console.log(chalk32.bold("\n\u{1F3AF} \uBBF8\uC158 \uACC4\uC57D \uC800\uC7A5"));
|
|
6133
|
+
console.log(chalk32.dim(` \u{1F4C4} ${MISSION_PATH_REL}`));
|
|
6134
|
+
console.log(` objective: ${mission.objective}`);
|
|
6135
|
+
console.log(` scope: ${mission.scope.length ? mission.scope.join(", ") : "(\uC81C\uD55C \uC5C6\uC74C)"}`);
|
|
6136
|
+
console.log(` forbidden: ${mission.forbidden.length ? mission.forbidden.join(", ") : "(\uC5C6\uC74C)"}`);
|
|
6137
|
+
printNextStep({ message: "\uBCC0\uACBD\uC774 \uACC4\uC57D \uC548\uC778\uC9C0 \uAC80\uC99D\uD558\uB824\uBA74:", command: "vhk mission check", cursorHint: "\uBBF8\uC158 \uAC80\uC99D\uD574\uC918" });
|
|
6138
|
+
}
|
|
6139
|
+
async function missionShow() {
|
|
6140
|
+
const cwd = process.cwd();
|
|
6141
|
+
const mission = readMission(cwd);
|
|
6142
|
+
if (!mission) {
|
|
6143
|
+
console.error(chalk32.yellow(" \u26A0\uFE0F \uBBF8\uC158 \uACC4\uC57D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 (.vhk/mission.json)."));
|
|
6144
|
+
printNextStep({ message: "\uBA3C\uC800 \uBBF8\uC158\uC744 \uC120\uC5B8\uD558\uC138\uC694:", command: 'vhk mission set --objective "..."', cursorHint: "\uBBF8\uC158 \uC815\uD574\uC918" });
|
|
6145
|
+
process.exitCode = 1;
|
|
6146
|
+
return;
|
|
6147
|
+
}
|
|
6148
|
+
console.log(chalk32.bold("\n\u{1F3AF} \uD604\uC7AC \uBBF8\uC158 \uACC4\uC57D"));
|
|
6149
|
+
console.log(` objective: ${mission.objective}`);
|
|
6150
|
+
console.log(` scope: ${mission.scope.length ? mission.scope.join(", ") : "(\uC81C\uD55C \uC5C6\uC74C)"}`);
|
|
6151
|
+
console.log(` forbidden: ${mission.forbidden.length ? mission.forbidden.join(", ") : "(\uC5C6\uC74C)"}`);
|
|
6152
|
+
console.log(chalk32.dim(` \uC0DD\uC131 ${mission.createdAt} \xB7 \uAC31\uC2E0 ${mission.updatedAt}`));
|
|
6153
|
+
}
|
|
6154
|
+
async function missionCheck() {
|
|
6155
|
+
const cwd = process.cwd();
|
|
6156
|
+
const mission = readMission(cwd);
|
|
6157
|
+
if (!mission) {
|
|
6158
|
+
console.error(chalk32.yellow(" \u26A0\uFE0F \uBBF8\uC158 \uACC4\uC57D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uBA3C\uC800 vhk mission set \uC73C\uB85C \uC120\uC5B8\uD558\uC138\uC694."));
|
|
6159
|
+
process.exitCode = 1;
|
|
6160
|
+
return;
|
|
6161
|
+
}
|
|
6162
|
+
const changed = await collectChangedFiles(cwd);
|
|
6163
|
+
const result = checkMission(changed, mission);
|
|
6164
|
+
console.log(chalk32.bold("\n\u{1F3AF} \uBBF8\uC158 \uACC4\uC57D \uAC80\uC99D (mission check)"));
|
|
6165
|
+
console.log(chalk32.dim(` objective: ${mission.objective} \xB7 \uBCC0\uACBD \uD30C\uC77C ${changed.length}\uAC1C`));
|
|
6166
|
+
if (result.violations.length > 0) {
|
|
6167
|
+
console.log(chalk32.red.bold(`
|
|
6168
|
+
\u{1F6AB} forbidden \uC704\uBC18 ${result.violations.length}\uAC74`));
|
|
6169
|
+
for (const v of result.violations) console.log(chalk32.red(` \u2717 ${v.file} (\uAE08\uC9C0: ${v.pattern})`));
|
|
6170
|
+
}
|
|
6171
|
+
if (result.warnings.length > 0) {
|
|
6172
|
+
console.log(chalk32.yellow.bold(`
|
|
6173
|
+
\u26A0\uFE0F scope \uBC16 \uBCC0\uACBD ${result.warnings.length}\uAC74 (\uACBD\uACE0)`));
|
|
6174
|
+
for (const w of result.warnings) console.log(chalk32.yellow(` ? ${w.file}`));
|
|
6175
|
+
}
|
|
6176
|
+
if (result.violations.length === 0 && result.warnings.length === 0) {
|
|
6177
|
+
console.log(chalk32.green("\n \u2713 \uBCC0\uACBD\uC774 \uACC4\uC57D(scope/forbidden) \uC548\uC785\uB2C8\uB2E4."));
|
|
6178
|
+
}
|
|
6179
|
+
console.log(chalk32.yellow(`
|
|
6180
|
+
${result.disclaimer}`));
|
|
6181
|
+
process.exitCode = result.violations.length > 0 ? 1 : 0;
|
|
6182
|
+
}
|
|
6183
|
+
async function missionClear() {
|
|
6184
|
+
const cwd = process.cwd();
|
|
6185
|
+
const p = join12(cwd, MISSION_PATH_REL);
|
|
6186
|
+
if (!existsSync18(p)) {
|
|
6187
|
+
console.log(chalk32.dim(" \uBBF8\uC158 \uACC4\uC57D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uC9C0\uC6B8 \uAC83 \uC5C6\uC74C."));
|
|
6188
|
+
return;
|
|
6189
|
+
}
|
|
6190
|
+
try {
|
|
6191
|
+
rmSync3(p);
|
|
6192
|
+
console.log(chalk32.green(" \u2705 \uBBF8\uC158 \uACC4\uC57D \uC0AD\uC81C\uB428 (.vhk/mission.json)."));
|
|
6193
|
+
} catch (e) {
|
|
6194
|
+
console.error(chalk32.red(` \u274C \uC0AD\uC81C \uC2E4\uD328: ${e instanceof Error ? e.message : String(e)}`));
|
|
6195
|
+
process.exitCode = 1;
|
|
6196
|
+
}
|
|
6197
|
+
}
|
|
6198
|
+
|
|
6012
6199
|
// src/lib/risk-policy.ts
|
|
6013
6200
|
var HIGH_RISK_ACTIONS = [
|
|
6014
6201
|
"undo",
|
|
@@ -6176,6 +6363,8 @@ async function dispatchNlpRoute(route, input) {
|
|
|
6176
6363
|
return verify();
|
|
6177
6364
|
case "review":
|
|
6178
6365
|
return review();
|
|
6366
|
+
case "mission":
|
|
6367
|
+
return missionShow();
|
|
6179
6368
|
}
|
|
6180
6369
|
}
|
|
6181
6370
|
var STATE_CHANGING_COMMANDS = /* @__PURE__ */ new Set([
|
|
@@ -6189,23 +6378,23 @@ function requiresConfirmation(route) {
|
|
|
6189
6378
|
async function runNaturalLanguageRoute(input) {
|
|
6190
6379
|
const route = routeNaturalLanguage(input);
|
|
6191
6380
|
if (!route) {
|
|
6192
|
-
console.log(
|
|
6381
|
+
console.log(chalk33.yellow(`
|
|
6193
6382
|
\u2753 "${input}" \u2014 ${ko.nlp.notMatched}
|
|
6194
6383
|
`));
|
|
6195
6384
|
return;
|
|
6196
6385
|
}
|
|
6197
6386
|
console.log("");
|
|
6198
|
-
console.log(
|
|
6199
|
-
console.log(
|
|
6387
|
+
console.log(chalk33.cyan(` \u{1F4AC} "${input}"`));
|
|
6388
|
+
console.log(chalk33.cyan(` \u2192 ${route.explanation}`));
|
|
6200
6389
|
if (requiresConfirmation(route)) {
|
|
6201
|
-
const { confirm } = await
|
|
6390
|
+
const { confirm } = await inquirer13.prompt([{
|
|
6202
6391
|
type: "confirm",
|
|
6203
6392
|
name: "confirm",
|
|
6204
6393
|
message: `${route.explanation} \u2014 ${ko.nlp.matched}`,
|
|
6205
6394
|
default: true
|
|
6206
6395
|
}]);
|
|
6207
6396
|
if (!confirm) {
|
|
6208
|
-
console.log(
|
|
6397
|
+
console.log(chalk33.dim(` ${ko.nlp.menuHint}`));
|
|
6209
6398
|
return;
|
|
6210
6399
|
}
|
|
6211
6400
|
}
|
|
@@ -6214,7 +6403,7 @@ async function runNaturalLanguageRoute(input) {
|
|
|
6214
6403
|
if (riskAction) {
|
|
6215
6404
|
await runGuarded(
|
|
6216
6405
|
riskAction,
|
|
6217
|
-
{ channel: "nl", approved: false, log: (m) => console.log(
|
|
6406
|
+
{ channel: "nl", approved: false, log: (m) => console.log(chalk33.yellow(` ${m}`)) },
|
|
6218
6407
|
() => dispatchNlpRoute(route, input)
|
|
6219
6408
|
);
|
|
6220
6409
|
return;
|
|
@@ -6223,77 +6412,77 @@ async function runNaturalLanguageRoute(input) {
|
|
|
6223
6412
|
}
|
|
6224
6413
|
|
|
6225
6414
|
// src/commands/agent.ts
|
|
6226
|
-
import
|
|
6415
|
+
import chalk34 from "chalk";
|
|
6227
6416
|
function activeGoalId() {
|
|
6228
6417
|
const goals = listGoals("goals");
|
|
6229
6418
|
const id = selectActiveId(goals);
|
|
6230
6419
|
return id ?? void 0;
|
|
6231
6420
|
}
|
|
6232
6421
|
async function blocker(description) {
|
|
6233
|
-
console.log(
|
|
6422
|
+
console.log(chalk34.bold(`
|
|
6234
6423
|
${ko.agent.blockerTitle}
|
|
6235
6424
|
`));
|
|
6236
6425
|
if (!description || !description.trim()) {
|
|
6237
|
-
console.log(
|
|
6238
|
-
console.log(
|
|
6426
|
+
console.log(chalk34.red(" \u274C \uBE14\uB85C\uCEE4 \uC124\uBA85\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694."));
|
|
6427
|
+
console.log(chalk34.dim(' \uC608: vhk blocker "tsc \uC5D0\uB7EC \u2014 simple-git \uD0C0\uC785 \uD638\uD658"'));
|
|
6239
6428
|
process.exitCode = 1;
|
|
6240
6429
|
return;
|
|
6241
6430
|
}
|
|
6242
6431
|
const goalId = activeGoalId();
|
|
6243
6432
|
const r = appendBlocker(description, goalId);
|
|
6244
|
-
console.log(
|
|
6433
|
+
console.log(chalk34.green(` \u2705 blocker \uAE30\uB85D (\uD604\uC7AC \uD65C\uC131 ${r.count}\uAC74)`));
|
|
6245
6434
|
if (r.hardStopTripped) {
|
|
6246
|
-
console.log(
|
|
6247
|
-
console.log(
|
|
6435
|
+
console.log(chalk34.red.bold(" \u{1F6D1} HARD_STOP \uC790\uB3D9 \uC0DD\uC131 \u2014 \uBAA8\uB4E0 \uC790\uB3D9\uD654 \uC911\uB2E8."));
|
|
6436
|
+
console.log(chalk34.yellow(" \uC0AC\uB78C \uAC80\uD1A0 \uD6C4 `vhk resume --confirm` \uC73C\uB85C\uB9CC \uD574\uC81C."));
|
|
6248
6437
|
process.exitCode = 2;
|
|
6249
6438
|
}
|
|
6250
6439
|
}
|
|
6251
6440
|
async function learn(lesson) {
|
|
6252
|
-
console.log(
|
|
6441
|
+
console.log(chalk34.bold(`
|
|
6253
6442
|
${ko.agent.learnTitle}
|
|
6254
6443
|
`));
|
|
6255
6444
|
if (!lesson || !lesson.trim()) {
|
|
6256
|
-
console.log(
|
|
6257
|
-
console.log(
|
|
6445
|
+
console.log(chalk34.red(" \u274C \uAD50\uD6C8 \uB0B4\uC6A9\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694."));
|
|
6446
|
+
console.log(chalk34.dim(' \uC608: vhk learn "PowerShell \uC5D0\uC11C\uB294 ; \uC0AC\uC6A9 (&& \uBBF8\uC9C0\uC6D0)"'));
|
|
6258
6447
|
process.exitCode = 1;
|
|
6259
6448
|
return;
|
|
6260
6449
|
}
|
|
6261
6450
|
const goalId = activeGoalId();
|
|
6262
6451
|
appendLearning(lesson, goalId);
|
|
6263
|
-
console.log(
|
|
6452
|
+
console.log(chalk34.green(" \u2705 learnings.md append."));
|
|
6264
6453
|
console.log(
|
|
6265
|
-
|
|
6454
|
+
chalk34.dim(" \uACB0\uC815\uC0AC\uD56D(decision)\uC740 `vhk memory add` \uB85C \uBCC4\uB3C4 \uAE30\uB85D \u2014 SoT \uBD84\uB9AC.")
|
|
6266
6455
|
);
|
|
6267
6456
|
}
|
|
6268
6457
|
async function resume(opts = {}) {
|
|
6269
|
-
console.log(
|
|
6458
|
+
console.log(chalk34.bold(`
|
|
6270
6459
|
${ko.agent.resumeTitle}
|
|
6271
6460
|
`));
|
|
6272
6461
|
if (!isHardStopActive()) {
|
|
6273
|
-
console.log(
|
|
6462
|
+
console.log(chalk34.dim(" HARD_STOP \uD65C\uC131 \uC544\uB2D8 \u2014 \uD560 \uC77C \uC5C6\uC74C."));
|
|
6274
6463
|
return;
|
|
6275
6464
|
}
|
|
6276
6465
|
const reason = readHardStopReason();
|
|
6277
6466
|
if (reason) {
|
|
6278
|
-
console.log(
|
|
6279
|
-
console.log(
|
|
6467
|
+
console.log(chalk34.yellow(" \u{1F4CB} HARD_STOP \uC0AC\uC720:"));
|
|
6468
|
+
console.log(chalk34.dim(` ${reason.split("\n").join("\n ")}`));
|
|
6280
6469
|
console.log("");
|
|
6281
6470
|
}
|
|
6282
6471
|
if (!opts.confirm) {
|
|
6283
6472
|
console.log(
|
|
6284
|
-
|
|
6473
|
+
chalk34.red(
|
|
6285
6474
|
" \u274C --confirm \uD50C\uB798\uADF8 \uC5C6\uC774\uB294 \uD574\uC81C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC790\uB3D9 \uD638\uCD9C \uAE08\uC9C0)."
|
|
6286
6475
|
)
|
|
6287
6476
|
);
|
|
6288
|
-
console.log(
|
|
6477
|
+
console.log(chalk34.yellow(" \uC0AC\uC720\uB97C \uD655\uC778\uD55C \uD6C4 \uB2E4\uC2DC: vhk resume --confirm"));
|
|
6289
6478
|
process.exitCode = 1;
|
|
6290
6479
|
return;
|
|
6291
6480
|
}
|
|
6292
6481
|
const removed = clearHardStop();
|
|
6293
6482
|
if (removed) {
|
|
6294
|
-
console.log(
|
|
6483
|
+
console.log(chalk34.green(" \u2705 HARD_STOP \uD574\uC81C. \uC790\uB3D9\uD654 \uC7AC\uAC1C \uAC00\uB2A5."));
|
|
6295
6484
|
} else {
|
|
6296
|
-
console.log(
|
|
6485
|
+
console.log(chalk34.dim(" \uD30C\uC77C\uC774 \uC774\uBBF8 \uC5C6\uC74C \u2014 no-op."));
|
|
6297
6486
|
}
|
|
6298
6487
|
}
|
|
6299
6488
|
|
|
@@ -6306,7 +6495,7 @@ async function guardCli(action, approved, run) {
|
|
|
6306
6495
|
channel: "cli",
|
|
6307
6496
|
approved,
|
|
6308
6497
|
confirm: async () => {
|
|
6309
|
-
const { ok } = await
|
|
6498
|
+
const { ok } = await inquirer14.prompt([{
|
|
6310
6499
|
type: "confirm",
|
|
6311
6500
|
name: "ok",
|
|
6312
6501
|
message: `\u26A0\uFE0F \uC704\uD5D8 \uC791\uC5C5(${action})\uC744 \uC2E4\uD589\uD560\uAE4C\uC694?`,
|
|
@@ -6314,7 +6503,7 @@ async function guardCli(action, approved, run) {
|
|
|
6314
6503
|
}]);
|
|
6315
6504
|
return ok;
|
|
6316
6505
|
},
|
|
6317
|
-
log: (m) => console.log(
|
|
6506
|
+
log: (m) => console.log(chalk35.yellow(` ${m}`))
|
|
6318
6507
|
},
|
|
6319
6508
|
run
|
|
6320
6509
|
);
|
|
@@ -6327,7 +6516,7 @@ async function guardCliDefer(action, approved, run) {
|
|
|
6327
6516
|
approved,
|
|
6328
6517
|
// TTY 면 통과(명령이 자체 확인), 비대화형은 confirm 불가 → 가드가 차단.
|
|
6329
6518
|
confirm: async () => !!process.stdout.isTTY,
|
|
6330
|
-
log: (m) => console.log(
|
|
6519
|
+
log: (m) => console.log(chalk35.yellow(` ${m}`))
|
|
6331
6520
|
},
|
|
6332
6521
|
run
|
|
6333
6522
|
);
|
|
@@ -6367,6 +6556,7 @@ var KO_ALIASES = {
|
|
|
6367
6556
|
brief: "\uBE0C\uB9AC\uD551",
|
|
6368
6557
|
goal: "\uBAA9\uD45C",
|
|
6369
6558
|
review: "\uAC80\uD1A0",
|
|
6559
|
+
mission: "\uBBF8\uC158",
|
|
6370
6560
|
blocker: "\uBE14\uB85C\uCEE4",
|
|
6371
6561
|
learn: "\uAD50\uD6C8",
|
|
6372
6562
|
resume: "\uC7AC\uAC1C"
|
|
@@ -6494,6 +6684,19 @@ program.command("verify").alias("\uC0AC\uC804\uC810\uAC80").option("--json", "\u
|
|
|
6494
6684
|
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
6685
|
await review(opts);
|
|
6496
6686
|
});
|
|
6687
|
+
var collectGlob = (v, prev = []) => prev.concat([v]);
|
|
6688
|
+
var missionCmd = program.command("mission").alias("\uBBF8\uC158").description("\uBBF8\uC158 \uACC4\uC57D \u2014 \uC791\uC5C5 \uBAA9\uD45C\xB7\uD5C8\uC6A9/\uAE08\uC9C0 \uBC94\uC704 \uC120\uC5B8\xB7\uAC80\uC99D (scope \uAC00\uB4DC, .vhk/mission.json)").action(async () => {
|
|
6689
|
+
await missionShow();
|
|
6690
|
+
});
|
|
6691
|
+
missionCmd.command("set").option("--objective <text>", "\uBBF8\uC158 \uBAA9\uD45C(objective)").option("--scope <glob>", "\uD5C8\uC6A9 \uACBD\uB85C glob (\uBC18\uBCF5 \uAC00\uB2A5, \uC81C\uACF5 \uC2DC \uAD50\uCCB4)", collectGlob).option("--forbidden <glob>", "\uAE08\uC9C0 \uACBD\uB85C glob (\uBC18\uBCF5 \uAC00\uB2A5, \uC81C\uACF5 \uC2DC \uAD50\uCCB4)", collectGlob).option("--clear-scope", "scope \uB97C \uBE44\uC6C0(\uBA85\uC2DC\uC801)").option("--clear-forbidden", "forbidden \uC744 \uBE44\uC6C0(\uBA85\uC2DC\uC801)").option("-y, --yes", "\uB300\uD654\uD615 \uD504\uB86C\uD504\uD2B8 \uC2A4\uD0B5 (\uBE44\uB300\uD654\uD615)").description("\uBBF8\uC158 \uACC4\uC57D \uC120\uC5B8/\uAC31\uC2E0 (\uC635\uC158 \uBBF8\uC9C0\uC815 \uC2DC \uAE30\uC874 scope/forbidden \uBCF4\uC874)").action(async (opts) => {
|
|
6692
|
+
await missionSet(opts);
|
|
6693
|
+
});
|
|
6694
|
+
missionCmd.command("check").description("\uBCC0\uACBD \uD30C\uC77C\uC774 \uACC4\uC57D(scope/forbidden) \uC548\uC778\uC9C0 \uAC80\uC99D \u2014 forbidden \uC704\uBC18 \uC2DC exit 1").action(async () => {
|
|
6695
|
+
await missionCheck();
|
|
6696
|
+
});
|
|
6697
|
+
missionCmd.command("clear").description("\uBBF8\uC158 \uACC4\uC57D \uC0AD\uC81C (.vhk/mission.json)").action(async () => {
|
|
6698
|
+
await missionClear();
|
|
6699
|
+
});
|
|
6497
6700
|
program.command("context-show").alias("\uB9E5\uB77D\uBCF4\uAE30").description("\uD604\uC7AC \uCEE8\uD14D\uC2A4\uD2B8 \uD30C\uC77C \uB0B4\uC6A9 \uCD9C\uB825").action(async () => {
|
|
6498
6701
|
await contextShow();
|
|
6499
6702
|
});
|
|
@@ -6551,7 +6754,7 @@ program.on("command:*", async (operands) => {
|
|
|
6551
6754
|
});
|
|
6552
6755
|
program.action(async () => {
|
|
6553
6756
|
console.log("\n\u{1F3AF} VHK \u2014 \uBC14\uC774\uBE0C\uCF54\uB529 \uD504\uB85C\uC81D\uD2B8 \uCF54\uCE58\n");
|
|
6554
|
-
const { choice } = await
|
|
6757
|
+
const { choice } = await inquirer14.prompt([{
|
|
6555
6758
|
type: "list",
|
|
6556
6759
|
name: "choice",
|
|
6557
6760
|
message: "\uBB58 \uB3C4\uC640\uB4DC\uB9B4\uAE4C\uC694?",
|
|
@@ -6608,9 +6811,9 @@ if (isMainModule) {
|
|
|
6608
6811
|
}
|
|
6609
6812
|
} catch (err) {
|
|
6610
6813
|
if (isPromptAbortError(err)) {
|
|
6611
|
-
console.error(
|
|
6814
|
+
console.error(chalk35.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)"));
|
|
6612
6815
|
} else {
|
|
6613
|
-
console.error(
|
|
6816
|
+
console.error(chalk35.red(`
|
|
6614
6817
|
\u274C ${err instanceof Error ? err.message : String(err)}`));
|
|
6615
6818
|
}
|
|
6616
6819
|
process.exitCode = 1;
|
package/package.json
CHANGED