@byh3071/vhk 2.3.2 → 2.4.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/dist/{chunk-OHGVVAKT.js → chunk-Y7SJVHGS.js} +127 -61
- package/dist/index.js +791 -420
- package/dist/mcp/index.js +1 -1
- package/package.json +73 -72
package/dist/index.js
CHANGED
|
@@ -43,13 +43,13 @@ import {
|
|
|
43
43
|
stripBom,
|
|
44
44
|
sync,
|
|
45
45
|
t
|
|
46
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-Y7SJVHGS.js";
|
|
47
47
|
|
|
48
48
|
// src/index.ts
|
|
49
49
|
import { Command, Help } from "commander";
|
|
50
50
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
51
|
-
import
|
|
52
|
-
import
|
|
51
|
+
import fs14 from "fs";
|
|
52
|
+
import chalk38 from "chalk";
|
|
53
53
|
import inquirer15 from "inquirer";
|
|
54
54
|
|
|
55
55
|
// src/lib/nlp-router.ts
|
|
@@ -62,7 +62,8 @@ var NLP_KEYWORDS = {
|
|
|
62
62
|
evolve: ["\uC9C4\uD654", "\uB8F0\uD6C4\uBCF4", "\uC9C4\uD654\uD6C4\uBCF4"],
|
|
63
63
|
undo: ["\uB418\uB3CC\uB824", "\uB418\uB3CC\uB9AC\uAE30", "\uCDE8\uC18C", "\uC6D0\uB798\uB300\uB85C", "\uB864\uBC31", "\uB9AC\uC14B", "reset", "rollback"],
|
|
64
64
|
status: ["\uC0C1\uD0DC", "\uD604\uD669", "\uC5B4\uB5BB\uAC8C", "\uC5B4\uB54C", "\uC9C0\uAE08"],
|
|
65
|
-
diff: ["\uBCC0\uACBD", "\uBC14\uB010", "\uBB50\uBC14\uB01C", "\uBC14\uB00C\uC5C8", "\uCC28\uC774", "\uB2EC\uB77C\uC9C4", "\uC218\uC815\uB41C"]
|
|
65
|
+
diff: ["\uBCC0\uACBD", "\uBC14\uB010", "\uBB50\uBC14\uB01C", "\uBC14\uB00C\uC5C8", "\uCC28\uC774", "\uB2EC\uB77C\uC9C4", "\uC218\uC815\uB41C"],
|
|
66
|
+
work: ["\uC774\uC5B4\uC11C", "\uC774\uC5B4\uD558\uAE30", "work"]
|
|
66
67
|
};
|
|
67
68
|
function matchesKeywords(text, command) {
|
|
68
69
|
const keywords = NLP_KEYWORDS[command];
|
|
@@ -372,6 +373,20 @@ var RULES = [
|
|
|
372
373
|
confidence: "high",
|
|
373
374
|
args: ["sync"],
|
|
374
375
|
test: (t2) => /(게이트|목표).*(스크립트|동기화)|체크\s*스크립트\s*(생성|만들)/.test(t2)
|
|
376
|
+
},
|
|
377
|
+
// work — handoff(인수인계)를 먼저 평가(더 구체적), 그다음 작업 시작/이어하기.
|
|
378
|
+
{
|
|
379
|
+
command: "work",
|
|
380
|
+
explanation: "\uC791\uC5C5 \uC911\uB2E8 \uC815\uB9AC \uD504\uB86C\uD504\uD2B8 (vhk work handoff)",
|
|
381
|
+
confidence: "high",
|
|
382
|
+
args: ["handoff"],
|
|
383
|
+
test: (t2) => /인수인계|핸드오프|handoff|작업\s*(넘기|넘겨|전달|마무리)|중단\s*정리/.test(t2)
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
command: "work",
|
|
387
|
+
explanation: "\uC791\uC5C5 \uC2DC\uC791/\uC774\uC5B4\uD558\uAE30 \uD504\uB86C\uD504\uD2B8 (vhk work)",
|
|
388
|
+
confidence: "high",
|
|
389
|
+
test: (t2) => matchesKeywords(t2, "work") || /작업\s*(시작|이어|이어서|계속)|이어서\s*(작업|하자|할래)|^work$/.test(t2)
|
|
375
390
|
}
|
|
376
391
|
];
|
|
377
392
|
function routeNaturalLanguage(input) {
|
|
@@ -406,7 +421,8 @@ var CONTAINER_SUBCOMMANDS = {
|
|
|
406
421
|
mode: ["lite", "standard", "strict"],
|
|
407
422
|
mission: ["set", "check", "clear"],
|
|
408
423
|
pattern: ["detect", "list", "dismiss"],
|
|
409
|
-
evolve: ["suggest", "list", "apply", "reject", "undo"]
|
|
424
|
+
evolve: ["suggest", "list", "apply", "reject", "undo"],
|
|
425
|
+
work: ["handoff"]
|
|
410
426
|
};
|
|
411
427
|
var CONTAINER_ALIASES = {
|
|
412
428
|
\uBAA9\uD45C: "goal",
|
|
@@ -419,8 +435,55 @@ var CONTAINER_ALIASES = {
|
|
|
419
435
|
\uBAA8\uB4DC: "mode",
|
|
420
436
|
\uBBF8\uC158: "mission",
|
|
421
437
|
\uD328\uD134: "pattern",
|
|
422
|
-
\uC9C4\uD654: "evolve"
|
|
438
|
+
\uC9C4\uD654: "evolve",
|
|
439
|
+
\uC791\uC5C5: "work"
|
|
423
440
|
};
|
|
441
|
+
var TOP_LEVEL_COMMANDS = [
|
|
442
|
+
{ name: "gate", desc: "\uC544\uC774\uB514\uC5B4 \uAC80\uC99D" },
|
|
443
|
+
{ name: "start", desc: "\uC0C8 \uD504\uB85C\uC81D\uD2B8 \uC2DC\uC791 \uB9C8\uBC95\uC0AC" },
|
|
444
|
+
{ name: "init", desc: "\uD558\uB124\uC2A4 \uD30C\uC77C \uC0DD\uC131" },
|
|
445
|
+
{ name: "recap", desc: "\uC624\uB298 \uD55C \uC77C \uC815\uB9AC + ADR \uBD84\uB9AC" },
|
|
446
|
+
{ name: "sync", desc: "RULES.md \u2192 \uADDC\uCE59 \uD30C\uC77C \uB3D9\uAE30\uD654" },
|
|
447
|
+
{ name: "check", desc: "RULES.md \uADDC\uCE59 \uC810\uAC80" },
|
|
448
|
+
{ name: "secure", desc: "\uBCF4\uC548 \uC2A4\uCE94 (\uC2DC\uD06C\uB9BF \uC720\uCD9C \uAC80\uC0AC)" },
|
|
449
|
+
{ name: "cloud", desc: ".vhk \uD074\uB77C\uC6B0\uB4DC \uBC31\uC5C5\xB7\uBCF5\uC6D0 (push/pull)" },
|
|
450
|
+
{ name: "ship", desc: "\uBC30\uD3EC \uCCB4\uD06C\uB9AC\uC2A4\uD2B8 + \uD68C\uACE0" },
|
|
451
|
+
{ name: "doctor", desc: "\uAC1C\uBC1C \uD658\uACBD \uC810\uAC80 (+ --strict \uB4DC\uB9AC\uD504\uD2B8 \uAC8C\uC774\uD2B8)" },
|
|
452
|
+
{ name: "save", desc: "git \uC800\uC7A5 (add \u2192 commit \u2192 push)" },
|
|
453
|
+
{ name: "undo", desc: "\uCD5C\uADFC \uCEE4\uBC0B \uB418\uB3CC\uB9AC\uAE30" },
|
|
454
|
+
{ name: "restore", desc: "sync \uBC31\uC5C5 \uBCF5\uC6D0" },
|
|
455
|
+
{ name: "status", desc: "\uD504\uB85C\uC81D\uD2B8 \uC0C1\uD0DC \uB300\uC2DC\uBCF4\uB4DC" },
|
|
456
|
+
{ name: "diff", desc: "Git \uBCC0\uACBD\uC0AC\uD56D \uD55C\uAD6D\uC5B4 \uC694\uC57D" },
|
|
457
|
+
{ name: "mcp", desc: "MCP \uC11C\uBC84 \uC2DC\uC791 (stdio)" },
|
|
458
|
+
{ name: "mcp-init", desc: "Cursor\xB7Claude Desktop MCP \uC124\uC815 \uC0DD\uC131" },
|
|
459
|
+
{ name: "deploy", desc: "\uD504\uB85C\uB355\uC158 \uBC30\uD3EC (\uC790\uB3D9 \uAC10\uC9C0)" },
|
|
460
|
+
{ name: "env", desc: ".env \u2192 .env.example \uB3D9\uAE30\uD654" },
|
|
461
|
+
{ name: "env-check", desc: "\uD544\uC218 \uD658\uACBD\uBCC0\uC218 \uB204\uB77D \uAC80\uC0AC" },
|
|
462
|
+
{ name: "publish", desc: "npm \uBC30\uD3EC (\uBC84\uC804 \uBC94\uD504 \u2192 \uBE4C\uB4DC \u2192 \uD14C\uC2A4\uD2B8)" },
|
|
463
|
+
{ name: "design", desc: "\uB514\uC790\uC778 \uD1A0\uD070 \uC0DD\uC131" },
|
|
464
|
+
{ name: "design-palette", desc: "\uCEEC\uB7EC \uD314\uB808\uD2B8 \uD504\uB9AC\uC14B \uC120\uD0DD" },
|
|
465
|
+
{ name: "theme", desc: "\uB2E4\uD06C/\uB77C\uC774\uD2B8 \uBAA8\uB4DC CSS \uC0DD\uC131" },
|
|
466
|
+
{ name: "ref", desc: "\uB808\uD37C\uB7F0\uC2A4 URL \uAD00\uB9AC (add/list/open)" },
|
|
467
|
+
{ name: "harness", desc: "\uD1B5\uD569 \uD488\uC9C8 \uC810\uAC80 (lint+type+test+build)" },
|
|
468
|
+
{ name: "audit", desc: "\uBCF4\uC548 \uCDE8\uC57D\uC810 \uAC10\uC0AC (npm audit)" },
|
|
469
|
+
{ name: "migrate", desc: "\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800 \uC804\uD658 (npm/yarn/pnpm)" },
|
|
470
|
+
{ name: "update", desc: "VHK CLI \uC140\uD504 \uC5C5\uB370\uC774\uD2B8" },
|
|
471
|
+
{ name: "context", desc: "\uD504\uB85C\uC81D\uD2B8 \uB9E5\uB77D \uD30C\uC77C \uC0DD\uC131 (.vhk/context.md)" },
|
|
472
|
+
{ name: "mode", desc: "Safety Mode \uC870\uD68C/\uBCC0\uACBD (lite|standard|strict)" },
|
|
473
|
+
{ name: "verify", desc: "\uAC80\uC99D \uAC8C\uC774\uD2B8 \uC2E4\uD589 + \uC99D\uAC70 \uAE30\uB85D" },
|
|
474
|
+
{ name: "review", desc: "\uC801\uB300\uC801 \uC790\uAE30\uAC80\uC99D (\uAC70\uC9D3\uC644\uB8CC \uC758\uC2EC \uD0D0\uC9C0)" },
|
|
475
|
+
{ name: "mission", desc: "\uBBF8\uC158 \uACC4\uC57D \u2014 \uC791\uC5C5 \uBAA9\uD45C\xB7\uD5C8\uC6A9/\uAE08\uC9C0 \uBC94\uC704 \uC120\uC5B8\xB7\uAC80\uC99D" },
|
|
476
|
+
{ name: "context-show", desc: "\uCEE8\uD14D\uC2A4\uD2B8 \uD30C\uC77C \uB0B4\uC6A9 \uCD9C\uB825" },
|
|
477
|
+
{ name: "memory", desc: "\uAE30\uC5B5 \uAD00\uB9AC v2 (decisions/failures/successes)" },
|
|
478
|
+
{ name: "brief", desc: "\uD504\uB85C\uC81D\uD2B8 \uC694\uC57D \uBCF4\uACE0\uC11C \uC0DD\uC131" },
|
|
479
|
+
{ name: "work", desc: "AI \uC791\uC5C5 \uC2DC\uC791/\uC774\uC5B4\uD558\uAE30 (+ handoff)" },
|
|
480
|
+
{ name: "goal", desc: "Goal \uB2E8\uACC4\uBCC4 \uBBF8\uC158 \uAD00\uB9AC" },
|
|
481
|
+
{ name: "blocker", desc: "\uBE14\uB85C\uCEE4 \uAE30\uB85D (3\uAC74 \uB204\uC801 \uC2DC HARD_STOP)" },
|
|
482
|
+
{ name: "learn", desc: "\uAD50\uD6C8 \uAE30\uB85D \u2192 memory v2 \uB2E8\uC77C SoT" },
|
|
483
|
+
{ name: "resume", desc: ".vhk/HARD_STOP \uD574\uC81C (--confirm \uD544\uC694)" },
|
|
484
|
+
{ name: "pattern", desc: "\uBC18\uBCF5 \uD328\uD134 \uAC10\uC9C0\xB7\uBAA9\uB85D (avoid/reinforce)" },
|
|
485
|
+
{ name: "evolve", desc: "\uD328\uD134 \u2192 \uB8F0 \uD6C4\uBCF4 \uC81C\uC548\xB7\uBC18\uC601\xB7undo" }
|
|
486
|
+
];
|
|
424
487
|
|
|
425
488
|
// src/lib/cli-args.ts
|
|
426
489
|
var KNOWN_COMMAND_TOKENS = /* @__PURE__ */ new Set([
|
|
@@ -520,6 +583,8 @@ var KNOWN_COMMAND_TOKENS = /* @__PURE__ */ new Set([
|
|
|
520
583
|
"\uD328\uD134",
|
|
521
584
|
"evolve",
|
|
522
585
|
"\uC9C4\uD654",
|
|
586
|
+
"work",
|
|
587
|
+
"\uC791\uC5C5",
|
|
523
588
|
"help"
|
|
524
589
|
]);
|
|
525
590
|
function isOptionToken(token) {
|
|
@@ -557,7 +622,7 @@ function detectNaturalLanguageInput(argv) {
|
|
|
557
622
|
}
|
|
558
623
|
|
|
559
624
|
// src/lib/nlp-run.ts
|
|
560
|
-
import
|
|
625
|
+
import chalk36 from "chalk";
|
|
561
626
|
import inquirer14 from "inquirer";
|
|
562
627
|
|
|
563
628
|
// src/commands/gate.ts
|
|
@@ -2913,9 +2978,92 @@ ${ko.secure.title}
|
|
|
2913
2978
|
|
|
2914
2979
|
// src/commands/doctor.ts
|
|
2915
2980
|
import chalk9 from "chalk";
|
|
2981
|
+
import fs9 from "fs";
|
|
2982
|
+
import path10 from "path";
|
|
2983
|
+
import { fileURLToPath } from "url";
|
|
2984
|
+
|
|
2985
|
+
// src/lib/version-check.ts
|
|
2916
2986
|
import fs8 from "fs";
|
|
2987
|
+
import os from "os";
|
|
2917
2988
|
import path9 from "path";
|
|
2918
|
-
|
|
2989
|
+
var PACKAGE_NAME = "@byh3071/vhk";
|
|
2990
|
+
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2991
|
+
var COOLDOWN_MS = 60 * 60 * 1e3;
|
|
2992
|
+
var MENU_FETCH_TIMEOUT_MS = 1500;
|
|
2993
|
+
function fetchLatestNpmVersion(packageName, timeoutMs = NETWORK_EXEC_TIMEOUT_MS) {
|
|
2994
|
+
const result = safeExecFile("npm", ["view", packageName, "version"], { timeoutMs });
|
|
2995
|
+
if (!result.ok) return void 0;
|
|
2996
|
+
const out = result.out;
|
|
2997
|
+
if (/^\d+\.\d+\.\d+/.test(out)) return out;
|
|
2998
|
+
return void 0;
|
|
2999
|
+
}
|
|
3000
|
+
function compareSemver(a, b) {
|
|
3001
|
+
const parse = (v) => v.replace(/^v/i, "").split("-")[0].split(".").map((n) => parseInt(n, 10) || 0);
|
|
3002
|
+
const [a1 = 0, a2 = 0, a3 = 0] = parse(a);
|
|
3003
|
+
const [b1 = 0, b2 = 0, b3 = 0] = parse(b);
|
|
3004
|
+
if (a1 !== b1) return a1 - b1;
|
|
3005
|
+
if (a2 !== b2) return a2 - b2;
|
|
3006
|
+
return a3 - b3;
|
|
3007
|
+
}
|
|
3008
|
+
function getCacheDir() {
|
|
3009
|
+
return path9.join(os.homedir(), ".vhk");
|
|
3010
|
+
}
|
|
3011
|
+
function getCachePath() {
|
|
3012
|
+
return path9.join(getCacheDir(), "version-check.json");
|
|
3013
|
+
}
|
|
3014
|
+
function readCache() {
|
|
3015
|
+
try {
|
|
3016
|
+
const p = getCachePath();
|
|
3017
|
+
if (!fs8.existsSync(p)) return null;
|
|
3018
|
+
const data = readJsonFile(p);
|
|
3019
|
+
if (!data || typeof data.checkedAt !== "number" || data.latest !== void 0 && typeof data.latest !== "string") {
|
|
3020
|
+
return null;
|
|
3021
|
+
}
|
|
3022
|
+
return { latest: data.latest, checkedAt: data.checkedAt, lastTriedAt: data.lastTriedAt };
|
|
3023
|
+
} catch {
|
|
3024
|
+
return null;
|
|
3025
|
+
}
|
|
3026
|
+
}
|
|
3027
|
+
function writeCache(cache) {
|
|
3028
|
+
try {
|
|
3029
|
+
fs8.mkdirSync(getCacheDir(), { recursive: true });
|
|
3030
|
+
fs8.writeFileSync(getCachePath(), JSON.stringify(cache, null, 2), "utf-8");
|
|
3031
|
+
} catch {
|
|
3032
|
+
}
|
|
3033
|
+
}
|
|
3034
|
+
function isStale(cache, now = Date.now()) {
|
|
3035
|
+
return now - cache.checkedAt > CACHE_TTL_MS;
|
|
3036
|
+
}
|
|
3037
|
+
function recordLatest(latest, now = Date.now()) {
|
|
3038
|
+
writeCache({ latest, checkedAt: now, lastTriedAt: now });
|
|
3039
|
+
}
|
|
3040
|
+
function getUpdateInfo(now = Date.now()) {
|
|
3041
|
+
const current = getVhkVersion();
|
|
3042
|
+
const cache = readCache();
|
|
3043
|
+
let latest;
|
|
3044
|
+
if (cache && !isStale(cache, now)) {
|
|
3045
|
+
latest = cache.latest;
|
|
3046
|
+
} else {
|
|
3047
|
+
const lastTried = cache?.lastTriedAt ?? 0;
|
|
3048
|
+
const cooledDown = now - lastTried > COOLDOWN_MS;
|
|
3049
|
+
if (cooledDown) {
|
|
3050
|
+
const fetched = fetchLatestNpmVersion(PACKAGE_NAME, MENU_FETCH_TIMEOUT_MS);
|
|
3051
|
+
if (fetched) {
|
|
3052
|
+
writeCache({ latest: fetched, checkedAt: now, lastTriedAt: now });
|
|
3053
|
+
latest = fetched;
|
|
3054
|
+
} else {
|
|
3055
|
+
writeCache({ latest: cache?.latest, checkedAt: cache?.checkedAt ?? 0, lastTriedAt: now });
|
|
3056
|
+
latest = cache?.latest;
|
|
3057
|
+
}
|
|
3058
|
+
} else {
|
|
3059
|
+
latest = cache?.latest;
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
3062
|
+
const updateAvailable = latest ? compareSemver(latest, current) > 0 : false;
|
|
3063
|
+
return { current, latest, updateAvailable };
|
|
3064
|
+
}
|
|
3065
|
+
|
|
3066
|
+
// src/commands/doctor.ts
|
|
2919
3067
|
function checkCommand(name, command, hint) {
|
|
2920
3068
|
const result = safeExecFile(command, ["--version"]);
|
|
2921
3069
|
if (!result.ok) return { name, command, ok: false, hint };
|
|
@@ -2923,14 +3071,14 @@ function checkCommand(name, command, hint) {
|
|
|
2923
3071
|
return { name, command, version, ok: true, hint };
|
|
2924
3072
|
}
|
|
2925
3073
|
function getVhkVersion2() {
|
|
2926
|
-
const dir =
|
|
3074
|
+
const dir = path10.dirname(fileURLToPath(import.meta.url));
|
|
2927
3075
|
const candidates = [
|
|
2928
|
-
|
|
2929
|
-
|
|
3076
|
+
path10.join(dir, "../package.json"),
|
|
3077
|
+
path10.join(dir, "../../package.json")
|
|
2930
3078
|
];
|
|
2931
3079
|
for (const pkgPath of candidates) {
|
|
2932
3080
|
try {
|
|
2933
|
-
if (
|
|
3081
|
+
if (fs9.existsSync(pkgPath)) {
|
|
2934
3082
|
const pkg = readJsonFile(pkgPath);
|
|
2935
3083
|
return pkg.version;
|
|
2936
3084
|
}
|
|
@@ -2940,24 +3088,7 @@ function getVhkVersion2() {
|
|
|
2940
3088
|
}
|
|
2941
3089
|
return void 0;
|
|
2942
3090
|
}
|
|
2943
|
-
function
|
|
2944
|
-
const result = safeExecFile("npm", ["view", packageName, "version"], {
|
|
2945
|
-
timeoutMs: NETWORK_EXEC_TIMEOUT_MS
|
|
2946
|
-
});
|
|
2947
|
-
if (!result.ok) return void 0;
|
|
2948
|
-
const out = result.out;
|
|
2949
|
-
if (/^\d+\.\d+\.\d+/.test(out)) return out;
|
|
2950
|
-
return void 0;
|
|
2951
|
-
}
|
|
2952
|
-
function compareSemver(a, b) {
|
|
2953
|
-
const parse = (v) => v.replace(/^v/i, "").split("-")[0].split(".").map((n) => parseInt(n, 10) || 0);
|
|
2954
|
-
const [a1 = 0, a2 = 0, a3 = 0] = parse(a);
|
|
2955
|
-
const [b1 = 0, b2 = 0, b3 = 0] = parse(b);
|
|
2956
|
-
if (a1 !== b1) return a1 - b1;
|
|
2957
|
-
if (a2 !== b2) return a2 - b2;
|
|
2958
|
-
return a3 - b3;
|
|
2959
|
-
}
|
|
2960
|
-
async function doctor() {
|
|
3091
|
+
async function doctor(opts = {}) {
|
|
2961
3092
|
console.log(chalk9.bold(`
|
|
2962
3093
|
${ko.doctor.title}
|
|
2963
3094
|
`));
|
|
@@ -2986,6 +3117,7 @@ ${ko.doctor.title}
|
|
|
2986
3117
|
}
|
|
2987
3118
|
if (vhkVersion) {
|
|
2988
3119
|
const latest = fetchLatestNpmVersion("@byh3071/vhk");
|
|
3120
|
+
if (latest) recordLatest(latest);
|
|
2989
3121
|
if (latest && compareSemver(latest, vhkVersion) > 0) {
|
|
2990
3122
|
console.log(chalk9.yellow(` ${ko.doctor.updateAvailable(latest)}`));
|
|
2991
3123
|
} else if (latest) {
|
|
@@ -3003,27 +3135,28 @@ ${ko.doctor.title}
|
|
|
3003
3135
|
{ name: ".env", hint: ".gitignore\uC5D0 \uD3EC\uD568\uB418\uC5B4 \uC788\uB294\uC9C0 \uD655\uC778" }
|
|
3004
3136
|
];
|
|
3005
3137
|
for (const file of projectFiles) {
|
|
3006
|
-
const exists =
|
|
3138
|
+
const exists = fs9.existsSync(path10.join(cwd, file.name));
|
|
3007
3139
|
if (exists) {
|
|
3008
3140
|
console.log(chalk9.green(` \u2705 ${file.name}`));
|
|
3009
3141
|
if (file.name === ".env") {
|
|
3010
|
-
const gitignorePath =
|
|
3011
|
-
if (
|
|
3012
|
-
const gitignore =
|
|
3142
|
+
const gitignorePath = path10.join(cwd, ".gitignore");
|
|
3143
|
+
if (fs9.existsSync(gitignorePath)) {
|
|
3144
|
+
const gitignore = fs9.readFileSync(gitignorePath, "utf-8");
|
|
3013
3145
|
if (!gitignore.includes(".env")) {
|
|
3014
3146
|
console.log(chalk9.yellow(` ${ko.doctor.envNotIgnored}`));
|
|
3015
3147
|
}
|
|
3016
3148
|
}
|
|
3017
3149
|
}
|
|
3018
|
-
} else if (file.name === ".env" &&
|
|
3019
|
-
console.log(chalk9.green(" \u2705 .env.local") + chalk9.dim(" \u2014 \uB85C\
|
|
3150
|
+
} else if (file.name === ".env" && fs9.existsSync(path10.join(cwd, ".env.local"))) {
|
|
3151
|
+
console.log(chalk9.green(" \u2705 .env.local") + chalk9.dim(" \u2014 \uB85C\uCEF4 env \uC0AC\uC6A9 \uC911 (.env \uC5C6\uC5B4\uB3C4 \uC815\uC0C1)"));
|
|
3020
3152
|
} else {
|
|
3021
|
-
console.log(chalk9.dim(` \
|
|
3153
|
+
console.log(chalk9.dim(` \u26AB ${file.name}`) + chalk9.dim(` \u2014 ${file.hint}`));
|
|
3022
3154
|
}
|
|
3023
3155
|
}
|
|
3024
3156
|
console.log("");
|
|
3025
3157
|
console.log(chalk9.bold(` ${ko.doctor.driftTitle}`));
|
|
3026
3158
|
const ruleDrift = checkRuleDrift(cwd);
|
|
3159
|
+
let ruleDrifted = false;
|
|
3027
3160
|
if (!ruleDrift.checked) {
|
|
3028
3161
|
console.log(chalk9.dim(` ${ko.doctor.driftNoRules}`));
|
|
3029
3162
|
} else {
|
|
@@ -3032,6 +3165,7 @@ ${ko.doctor.title}
|
|
|
3032
3165
|
console.log(chalk9.green(` ${ko.doctor.driftRuleClean}`));
|
|
3033
3166
|
} else {
|
|
3034
3167
|
console.log(chalk9.yellow(` ${ko.doctor.driftRuleWarn(drifted.map((d) => d.path).join(", "))}`));
|
|
3168
|
+
ruleDrifted = true;
|
|
3035
3169
|
}
|
|
3036
3170
|
}
|
|
3037
3171
|
const ctxDrift = checkContextDrift(cwd);
|
|
@@ -3055,13 +3189,18 @@ ${ko.doctor.title}
|
|
|
3055
3189
|
});
|
|
3056
3190
|
process.exitCode = 1;
|
|
3057
3191
|
}
|
|
3192
|
+
if (opts.strict && ruleDrifted) {
|
|
3193
|
+
console.log("");
|
|
3194
|
+
console.log(chalk9.red.bold(" \u274C --strict: \uADDC\uCE59 \uB4DC\uB9AC\uD504\uD2B8 \uBC1C\uACAC \u2192 \uC2E4\uD328 \uCC98\uB9AC (vhk sync \uB85C \uB3D9\uAE30\uD654 \uD6C4 \uB2E4\uC2DC \uC2E4\uD589)"));
|
|
3195
|
+
process.exitCode = 1;
|
|
3196
|
+
}
|
|
3058
3197
|
}
|
|
3059
3198
|
|
|
3060
3199
|
// src/commands/ship.ts
|
|
3061
3200
|
import chalk10 from "chalk";
|
|
3062
3201
|
import inquirer4 from "inquirer";
|
|
3063
|
-
import
|
|
3064
|
-
import
|
|
3202
|
+
import fs10 from "fs";
|
|
3203
|
+
import path11 from "path";
|
|
3065
3204
|
var CHECKLIST = [
|
|
3066
3205
|
{ id: "build", questionKey: "checkBuild", hintKey: "hintBuild" },
|
|
3067
3206
|
{ id: "test", questionKey: "checkTest", hintKey: "hintTest" },
|
|
@@ -3074,9 +3213,9 @@ function sanitizeVersion(version) {
|
|
|
3074
3213
|
return version.trim().replace(/^v/i, "").replace(/[^a-zA-Z0-9._-]/g, "-") || "0.0.0";
|
|
3075
3214
|
}
|
|
3076
3215
|
function updateChangelogUnreleased(cwd, version, date) {
|
|
3077
|
-
const changelogPath =
|
|
3078
|
-
if (!
|
|
3079
|
-
const content =
|
|
3216
|
+
const changelogPath = path11.join(cwd, "CHANGELOG.md");
|
|
3217
|
+
if (!fs10.existsSync(changelogPath)) return { status: "missing" };
|
|
3218
|
+
const content = fs10.readFileSync(changelogPath, "utf-8");
|
|
3080
3219
|
const unreleasedHeading = /^## \[Unreleased\][^\n]*$/m;
|
|
3081
3220
|
if (!unreleasedHeading.test(content)) return { status: "no-unreleased" };
|
|
3082
3221
|
const blankUnreleased = [
|
|
@@ -3093,7 +3232,7 @@ function updateChangelogUnreleased(cwd, version, date) {
|
|
|
3093
3232
|
`## [${version}] \u2014 ${date}`
|
|
3094
3233
|
].join("\n");
|
|
3095
3234
|
const updated = content.replace(unreleasedHeading, blankUnreleased);
|
|
3096
|
-
|
|
3235
|
+
fs10.writeFileSync(changelogPath, updated, "utf-8");
|
|
3097
3236
|
return { status: "updated", version };
|
|
3098
3237
|
}
|
|
3099
3238
|
async function ship() {
|
|
@@ -3152,12 +3291,12 @@ ${ko.ship.title}
|
|
|
3152
3291
|
{ type: "input", name: "learned", message: ko.ship.questionLearned },
|
|
3153
3292
|
{ type: "input", name: "nextVersion", message: ko.ship.questionNext }
|
|
3154
3293
|
]);
|
|
3155
|
-
const buildLogDir =
|
|
3156
|
-
if (!
|
|
3294
|
+
const buildLogDir = path11.join(cwd, "docs", "build-log");
|
|
3295
|
+
if (!fs10.existsSync(buildLogDir)) fs10.mkdirSync(buildLogDir, { recursive: true });
|
|
3157
3296
|
const today = localDate();
|
|
3158
3297
|
const versionSlug = sanitizeVersion(retro.version);
|
|
3159
3298
|
const fileName = `${today}-v${versionSlug}.md`;
|
|
3160
|
-
const filePath =
|
|
3299
|
+
const filePath = path11.join(buildLogDir, fileName);
|
|
3161
3300
|
const content = [
|
|
3162
3301
|
`# \uBE4C\uB4DC \uB85C\uADF8: v${versionSlug}`,
|
|
3163
3302
|
"",
|
|
@@ -3186,9 +3325,9 @@ ${ko.ship.title}
|
|
|
3186
3325
|
"---",
|
|
3187
3326
|
`*Generated by \`vhk ship\` at ${(/* @__PURE__ */ new Date()).toISOString()}*`
|
|
3188
3327
|
].join("\n");
|
|
3189
|
-
|
|
3328
|
+
fs10.writeFileSync(filePath, content, "utf-8");
|
|
3190
3329
|
console.log(chalk10.green(`
|
|
3191
|
-
${ko.ship.buildLogDone(
|
|
3330
|
+
${ko.ship.buildLogDone(path11.relative(cwd, filePath))}`));
|
|
3192
3331
|
const changelogResult = updateChangelogUnreleased(cwd, versionSlug, today);
|
|
3193
3332
|
if (changelogResult.status === "updated") {
|
|
3194
3333
|
log.success(ko.ship.changelogUpdated(changelogResult.version));
|
|
@@ -3522,8 +3661,8 @@ ${ko.restore.notFound(targetId)}`));
|
|
|
3522
3661
|
|
|
3523
3662
|
// src/commands/status.ts
|
|
3524
3663
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
3525
|
-
import
|
|
3526
|
-
import
|
|
3664
|
+
import fs11 from "fs";
|
|
3665
|
+
import path12 from "path";
|
|
3527
3666
|
import chalk14 from "chalk";
|
|
3528
3667
|
function countFileChanges(porcelain) {
|
|
3529
3668
|
const lines = porcelain.split("\n").filter(Boolean);
|
|
@@ -3562,8 +3701,8 @@ function parseRecentCommitLines(logOutput) {
|
|
|
3562
3701
|
return logOutput.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
3563
3702
|
}
|
|
3564
3703
|
function readProjectPackage(cwd = process.cwd()) {
|
|
3565
|
-
const pkgPath =
|
|
3566
|
-
if (!
|
|
3704
|
+
const pkgPath = path12.join(cwd, "package.json");
|
|
3705
|
+
if (!fs11.existsSync(pkgPath)) return null;
|
|
3567
3706
|
try {
|
|
3568
3707
|
const pkg = readJsonFile(pkgPath);
|
|
3569
3708
|
if (!pkg.name && !pkg.version) return null;
|
|
@@ -4416,6 +4555,7 @@ async function update() {
|
|
|
4416
4555
|
\u{1F4CC} \uD604\uC7AC \uBC84\uC804: v${current}`));
|
|
4417
4556
|
const spinner = ora4("\uCD5C\uC2E0 \uBC84\uC804 \uD655\uC778 \uC911...").start();
|
|
4418
4557
|
const latest = getLatestVersion();
|
|
4558
|
+
if (latest) recordLatest(latest);
|
|
4419
4559
|
if (!latest) {
|
|
4420
4560
|
spinner.fail("\uCD5C\uC2E0 \uBC84\uC804\uC744 \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
4421
4561
|
console.log(chalk22.yellow(" \uB124\uD2B8\uC6CC\uD06C\uB97C \uD655\uC778\uD558\uAC70\uB098 \uC218\uB3D9\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694:"));
|
|
@@ -4947,37 +5087,7 @@ function extractTechStack() {
|
|
|
4947
5087
|
return stack;
|
|
4948
5088
|
}
|
|
4949
5089
|
function getVhkCommands() {
|
|
4950
|
-
return
|
|
4951
|
-
"gate \u2014 \uC544\uC774\uB514\uC5B4 \uAC80\uC99D",
|
|
4952
|
-
"init \u2014 \uD504\uB85C\uC81D\uD2B8 \uCD08\uAE30\uD654",
|
|
4953
|
-
"recap \u2014 \uC138\uC158 \uC694\uC57D \uC800\uC7A5",
|
|
4954
|
-
"sync \u2014 \uADDC\uCE59 \uD30C\uC77C \uB3D9\uAE30\uD654",
|
|
4955
|
-
"check \u2014 \uADDC\uCE59 \uC810\uAC80",
|
|
4956
|
-
"secure \u2014 \uBCF4\uC548 \uC2A4\uCE94",
|
|
4957
|
-
"ship \u2014 \uBC30\uD3EC \uCCB4\uD06C + \uD68C\uACE0",
|
|
4958
|
-
"doctor \u2014 \uD658\uACBD \uC9C4\uB2E8",
|
|
4959
|
-
"save \u2014 git \uC800\uC7A5 (add+commit+push)",
|
|
4960
|
-
"undo \u2014 \uCD5C\uADFC \uCEE4\uBC0B \uB418\uB3CC\uB9AC\uAE30",
|
|
4961
|
-
"status \u2014 git \uC0C1\uD0DC \uD655\uC778",
|
|
4962
|
-
"diff \u2014 git \uBCC0\uACBD \uC0AC\uD56D \uC694\uC57D",
|
|
4963
|
-
"deploy \u2014 \uD504\uB85C\uB355\uC158 \uBC30\uD3EC",
|
|
4964
|
-
"env \u2014 \uD658\uACBD\uBCC0\uC218 \uAD00\uB9AC",
|
|
4965
|
-
"publish \u2014 npm \uBC30\uD3EC \uC790\uB3D9\uD654",
|
|
4966
|
-
"design \u2014 \uB514\uC790\uC778 \uD1A0\uD070 \uC0DD\uC131",
|
|
4967
|
-
"design-palette \u2014 \uCEEC\uB7EC \uD314\uB808\uD2B8 \uC120\uD0DD",
|
|
4968
|
-
"theme \u2014 \uB2E4\uD06C/\uB77C\uC774\uD2B8 \uBAA8\uB4DC",
|
|
4969
|
-
"ref add|list|open \u2014 \uB808\uD37C\uB7F0\uC2A4 URL \uAD00\uB9AC",
|
|
4970
|
-
"harness \u2014 \uD1B5\uD569 \uD488\uC9C8 \uC810\uAC80",
|
|
4971
|
-
"audit \u2014 \uBCF4\uC548 \uCDE8\uC57D\uC810 \uAC10\uC0AC",
|
|
4972
|
-
"migrate \u2014 \uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800 \uC804\uD658",
|
|
4973
|
-
"update \u2014 VHK CLI \uC140\uD504 \uC5C5\uB370\uC774\uD2B8",
|
|
4974
|
-
"context \u2014 \uD504\uB85C\uC81D\uD2B8 \uB9E5\uB77D \uC0DD\uC131",
|
|
4975
|
-
"context-show \u2014 \uB9E5\uB77D \uD30C\uC77C \uBCF4\uAE30",
|
|
4976
|
-
"memory add|list|remove \u2014 \uACB0\uC815\uC0AC\uD56D \uAE30\uC5B5",
|
|
4977
|
-
"brief \u2014 \uD504\uB85C\uC81D\uD2B8 \uC694\uC57D \uBCF4\uACE0\uC11C",
|
|
4978
|
-
"mcp \u2014 MCP \uC11C\uBC84 \uC2DC\uC791",
|
|
4979
|
-
"mcp-init \u2014 Cursor MCP \uC124\uC815"
|
|
4980
|
-
];
|
|
5090
|
+
return TOP_LEVEL_COMMANDS.map((c) => `${c.name} \u2014 ${c.desc}`);
|
|
4981
5091
|
}
|
|
4982
5092
|
async function context(opts = {}) {
|
|
4983
5093
|
const compact = opts.compact === true;
|
|
@@ -4992,6 +5102,16 @@ async function context(opts = {}) {
|
|
|
4992
5102
|
lines.push("> \uC774 \uD30C\uC77C\uC740 `vhk context`\uB85C \uC790\uB3D9 \uC0DD\uC131\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
|
|
4993
5103
|
lines.push("> AI \uC5B4\uC2DC\uC2A4\uD134\uD2B8\uC5D0\uAC8C \uD504\uB85C\uC81D\uD2B8 \uB9E5\uB77D\uC744 \uC81C\uACF5\uD569\uB2C8\uB2E4.");
|
|
4994
5104
|
lines.push("");
|
|
5105
|
+
lines.push("## \uC6D0\uBCF8 \uC9C0\uB3C4 (Source of Truth)");
|
|
5106
|
+
lines.push("");
|
|
5107
|
+
lines.push('> \uBB34\uC5C7\uC744 \uACE0\uCE60 \uB540 "\uC6D0\uBCF8" \uD55C \uACF3\uB9CC \uACE0\uCE58\uC138\uC694. \uB098\uBA38\uC9C0\uB294 \uD30C\uC0DD\uBCF8\uC774\uB77C \uC790\uB3D9 \uC0DD\uC131\uB429\uB2C8\uB2E4.');
|
|
5108
|
+
lines.push("");
|
|
5109
|
+
lines.push("- **\uADDC\uCE59(\uC6D0\uBCF8)**: `RULES.md` \u2014 \uADDC\uCE59\uC740 \uC5EC\uAE30 \uD55C \uACF3\uC5D0\uC11C\uB9CC \uC218\uC815");
|
|
5110
|
+
lines.push("- **\uC791\uC5C5 \uC0C1\uD0DC**: `docs/state/next-task.md`, `docs/state/blockers.md`");
|
|
5111
|
+
lines.push("- **\uBC84\uC804\xB7\uB9B4\uB9AC\uC2A4**: `package.json`, `CHANGELOG.md`");
|
|
5112
|
+
lines.push("- **\uBA85\uB839 \uBAA9\uB85D**: `COMMANDS.md` (+ `vhk help`)");
|
|
5113
|
+
lines.push("- **\uD30C\uC0DD\uBCF8(\uC9C1\uC811 \uC218\uC815 \uAE08\uC9C0)**: `.cursorrules`\xB7`.windsurfrules`\xB7`.github/copilot-instructions.md`\xB7`AGENTS.md`\xB7`GEMINI.md` \uB4F1 7\uC885 + `CLAUDE.md` \uADDC\uCE59 \uC601\uC5ED \u2192 `vhk sync` \uB85C \uC0DD\uC131");
|
|
5114
|
+
lines.push("");
|
|
4995
5115
|
lines.push("## \uAE30\uC220 \uC2A4\uD0DD");
|
|
4996
5116
|
lines.push("");
|
|
4997
5117
|
for (const [key, value] of Object.entries(stack)) {
|
|
@@ -5051,9 +5171,10 @@ async function context(opts = {}) {
|
|
|
5051
5171
|
if (compact) {
|
|
5052
5172
|
lines.push("## \uCC38\uC870 \uBB38\uC11C (\uD544\uC694\uC2DC \uC5F4\uB78C)");
|
|
5053
5173
|
lines.push("");
|
|
5174
|
+
lines.push("- \uADDC\uCE59 \uC6D0\uBCF8(SoT): `RULES.md` \u2014 \uADDC\uCE59\uC740 \uC5EC\uAE30\uC11C\uB9CC \uC218\uC815");
|
|
5054
5175
|
lines.push("- \uC791\uB3D9 \uADDC\uC57D(\uC694\uC57D): `docs/context/agent-compact.md`");
|
|
5055
5176
|
lines.push("- \uADDC\uC57D \uC0C1\uC138: `AGENTS.md`");
|
|
5056
|
-
lines.push("- \
|
|
5177
|
+
lines.push("- \uC6B4\uC601 \uC548\uB0B4\xB7\uAE30\uB85D: `CLAUDE.md`");
|
|
5057
5178
|
lines.push("- \uBA85\uB839 \uC0C1\uC138: `COMMANDS.md`");
|
|
5058
5179
|
lines.push("- \uAD6C\uC870 \uC0C1\uC138: `docs/ARCHITECTURE.md`");
|
|
5059
5180
|
lines.push("- \uD604\uC7AC \uC0C1\uD0DC: `docs/state/next-task.md`");
|
|
@@ -5213,12 +5334,227 @@ async function brief() {
|
|
|
5213
5334
|
});
|
|
5214
5335
|
}
|
|
5215
5336
|
|
|
5216
|
-
// src/commands/
|
|
5337
|
+
// src/commands/work.ts
|
|
5338
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync10, writeFileSync as writeFileSync10, readFileSync as readFileSync7 } from "fs";
|
|
5339
|
+
import { join as join9 } from "path";
|
|
5217
5340
|
import chalk26 from "chalk";
|
|
5341
|
+
|
|
5342
|
+
// src/lib/clipboard.ts
|
|
5343
|
+
import { spawnSync } from "child_process";
|
|
5344
|
+
function copyToClipboard(text) {
|
|
5345
|
+
try {
|
|
5346
|
+
if (process.platform === "win32") return copyWin32(text);
|
|
5347
|
+
if (process.platform === "darwin") return runWithInput("pbcopy", [], text);
|
|
5348
|
+
return runWithInput("wl-copy", [], text) || runWithInput("xclip", ["-selection", "clipboard"], text) || runWithInput("xsel", ["--clipboard", "--input"], text);
|
|
5349
|
+
} catch {
|
|
5350
|
+
return false;
|
|
5351
|
+
}
|
|
5352
|
+
}
|
|
5353
|
+
function copyWin32(text) {
|
|
5354
|
+
const b64 = Buffer.from(text, "utf8").toString("base64");
|
|
5355
|
+
const psScript = "$b=[Console]::In.ReadToEnd();$t=[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($b));Set-Clipboard -Value $t";
|
|
5356
|
+
const r = spawnSync(
|
|
5357
|
+
"powershell",
|
|
5358
|
+
["-NoProfile", "-NonInteractive", "-Command", psScript],
|
|
5359
|
+
{ input: b64, encoding: "utf8", windowsHide: true }
|
|
5360
|
+
);
|
|
5361
|
+
return r.status === 0 && !r.error;
|
|
5362
|
+
}
|
|
5363
|
+
function runWithInput(cmd, args, input) {
|
|
5364
|
+
const r = spawnSync(cmd, args, { input, encoding: "utf8" });
|
|
5365
|
+
return r.status === 0 && !r.error;
|
|
5366
|
+
}
|
|
5367
|
+
|
|
5368
|
+
// src/commands/work.ts
|
|
5369
|
+
var VHK_DIR2 = ".vhk";
|
|
5370
|
+
function gitShort() {
|
|
5371
|
+
const r = safeExecFile("git", ["status", "--short"]);
|
|
5372
|
+
return r.ok ? r.out.trim() : "";
|
|
5373
|
+
}
|
|
5374
|
+
function ensureVhkProject() {
|
|
5375
|
+
if (existsSync14("CLAUDE.md") || existsSync14(VHK_DIR2) || existsSync14("goals")) return true;
|
|
5376
|
+
console.log(chalk26.yellow(" \u26A0\uFE0F \uC5EC\uAE30\uB294 VHK \uD504\uB85C\uC81D\uD2B8 \uD3F4\uB354\uAC00 \uC544\uB2CC \uAC83 \uAC19\uC544\uC694."));
|
|
5377
|
+
console.log(chalk26.dim(" VHK \uD504\uB85C\uC81D\uD2B8 \uD3F4\uB354\uC5D0\uC11C \uC2E4\uD589\uD558\uAC70\uB098, vhk start \uB85C \uC0C8\uB85C \uC2DC\uC791\uD558\uC138\uC694."));
|
|
5378
|
+
return false;
|
|
5379
|
+
}
|
|
5380
|
+
function passHardStop() {
|
|
5381
|
+
if (!isHardStopActive()) {
|
|
5382
|
+
console.log(chalk26.green(" \u2705 HARD_STOP \uC5C6\uC74C"));
|
|
5383
|
+
return true;
|
|
5384
|
+
}
|
|
5385
|
+
console.log(chalk26.red.bold("\n\u{1F6D1} HARD STOP \uD65C\uC131 (.vhk/HARD_STOP) \u2014 \uC790\uB3D9\uD654\uB97C \uC911\uB2E8\uD569\uB2C8\uB2E4."));
|
|
5386
|
+
const reason = readHardStopReason();
|
|
5387
|
+
if (reason) console.log(chalk26.red(` \uC0AC\uC720: ${reason.replace(/\s*\n\s*/g, " ")}`));
|
|
5388
|
+
console.log(chalk26.yellow(" \uD574\uC81C\uB294 \uC0AC\uB78C\uC774 \uC9C1\uC811: vhk resume --confirm"));
|
|
5389
|
+
return false;
|
|
5390
|
+
}
|
|
5391
|
+
function activeGoalLine() {
|
|
5392
|
+
const goals = listGoals("goals");
|
|
5393
|
+
if (goals.length === 0) return "\uC815\uC758\uB41C goal \uC5C6\uC74C";
|
|
5394
|
+
const id = selectActiveId(goals);
|
|
5395
|
+
if (id === null) return "\uBAA8\uB4E0 goal \uC644\uB8CC\uB428";
|
|
5396
|
+
const g = goals.find((x) => x.frontmatter.id === id);
|
|
5397
|
+
if (!g) return "\uC815\uC758\uB41C goal \uC5C6\uC74C";
|
|
5398
|
+
return `Goal ${id} \u2014 ${g.frontmatter.title ?? "(untitled)"}`;
|
|
5399
|
+
}
|
|
5400
|
+
function printGit(git3) {
|
|
5401
|
+
if (!git3) {
|
|
5402
|
+
console.log(chalk26.dim(" (\uBCC0\uACBD\uB41C \uD30C\uC77C \uC5C6\uC74C \u2014 \uAE68\uB057\uD55C \uC0C1\uD0DC)"));
|
|
5403
|
+
return;
|
|
5404
|
+
}
|
|
5405
|
+
for (const line of git3.split(/\r?\n/)) console.log(` ${line}`);
|
|
5406
|
+
}
|
|
5407
|
+
function buildStartPrompt(git3, goalLine) {
|
|
5408
|
+
const gitBlock = git3 || "(\uBCC0\uACBD \uC5C6\uC74C)";
|
|
5409
|
+
return [
|
|
5410
|
+
"\uB2F9\uC2E0\uC740 VHK \uD504\uB85C\uC81D\uD2B8\uC758 \uC791\uC5C5 \uD30C\uD2B8\uB108\uC785\uB2C8\uB2E4. \uB098\uB294 \uBE44\uAC1C\uBC1C\uC790\uC785\uB2C8\uB2E4.",
|
|
5411
|
+
"",
|
|
5412
|
+
"[\uADDC\uCE59 \uC6B0\uC120\uC21C\uC704]",
|
|
5413
|
+
"1. CLAUDE.md \uB97C 1\uC21C\uC704 \uADDC\uCE59\uC73C\uB85C \uC77D\uACE0 \uADF8\uB300\uB85C \uB530\uB974\uC138\uC694. (\uCDA9\uB3CC \uC2DC CLAUDE.md \uC6B0\uC120)",
|
|
5414
|
+
"2. AGENTS.md \uB294 Codex/\uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8\uC6A9 \uCC38\uACE0 \uADDC\uCE59\uC785\uB2C8\uB2E4. \uB2F9\uC2E0\uC740 \uCC38\uACE0\uB9CC \uD558\uC138\uC694.",
|
|
5415
|
+
"3. docs/state/next-task.md \uC640 .vhk/context.md \uB97C \uC77D\uACE0 \uADF8 \uAE30\uC900\uC73C\uB85C \uC774\uC5B4\uC11C \uC791\uC5C5\uD558\uC138\uC694.",
|
|
5416
|
+
"",
|
|
5417
|
+
"[\uC791\uC5C5 \uC804 Source of Truth \uCCB4\uD06C]",
|
|
5418
|
+
"1. \uC0C8 \uD0C0\uC785\xB7\uADDC\uCE59\xB7\uC0C1\uC218\xB7\uBA85\uB839\uC5B4\xB7\uD504\uB86C\uD504\uD2B8\uB97C \uB9CC\uB4E4\uAE30 \uC804\uC5D0 \uAE30\uC874 \uC815\uC758\uAC00 \uC788\uB294\uC9C0 \uBA3C\uC800 \uCC3E\uC544\uBCF4\uACE0, \uC788\uC73C\uBA74 \uC7AC\uC0AC\uC6A9(import)\uD558\uC138\uC694.",
|
|
5419
|
+
"2. \uADDC\uCE59\uC740 RULES.md \uD55C \uACF3\uC5D0\uC11C\uB9CC \uACE0\uCE58\uC138\uC694. CLAUDE.md\xB7AGENTS.md\xB7.cursorrules \uB4F1\uC740 vhk sync \uB85C \uB9CC\uB4DC\uB294 \uD30C\uC0DD\uBCF8\uC774\uB77C \uC9C1\uC811 \uC218\uC815 \uAE08\uC9C0.",
|
|
5420
|
+
"3. \uC6D0\uBCF8 \uAD6C\uBD84 \u2014 \uADDC\uCE59=RULES.md / \uC791\uC5C5 \uC0C1\uD0DC=docs/state/next-task.md / \uBC84\uC804\xB7\uB9B4\uB9AC\uC2A4=package.json\xB7CHANGELOG.md.",
|
|
5421
|
+
"4. DTO\xB7\uACC4\uCE35 \uBD84\uB9AC\uCC98\uB7FC \uC77C\uBD80\uB7EC \uD0C0\uC785\uC744 \uB098\uB20C \uB550 \uC774\uC720\uB97C \uD55C \uC904 \uC124\uBA85\uD558\uC138\uC694. (\uC758\uBBF8\uAC00 \uAC19\uC740 \uAC78 \uC0C8\uB85C \uB610 \uC815\uC758\uD558\uB294 \uAC83\uB9CC \uAE08\uC9C0)",
|
|
5422
|
+
"",
|
|
5423
|
+
"[\uC9C0\uAE08 \uC0C1\uD0DC \u2014 \uC790\uB3D9 \uC218\uC9D1\uB428]",
|
|
5424
|
+
"- \uBCC0\uACBD\uB41C \uD30C\uC77C (git status --short):",
|
|
5425
|
+
gitBlock,
|
|
5426
|
+
"- \uD604\uC7AC \uBAA9\uD45C (active goal):",
|
|
5427
|
+
goalLine,
|
|
5428
|
+
"",
|
|
5429
|
+
"[\uD574\uC8FC\uC138\uC694]",
|
|
5430
|
+
"1. \uBA3C\uC800 \uD604\uC7AC \uC0C1\uD0DC\uB97C \uBE44\uAC1C\uBC1C\uC790\uAC00 \uC54C\uC544\uB4E3\uAC8C \uC27D\uAC8C \uC694\uC57D\uD574 \uC8FC\uC138\uC694.",
|
|
5431
|
+
"2. \uADF8\uB2E4\uC74C \uD560 \uC77C\uC744 3\uB2E8\uACC4 \uC774\uD558\uB85C \uB9D0\uD558\uACE0 \uC9C4\uD589\uD558\uC138\uC694.",
|
|
5432
|
+
"3. \uD14C\uC2A4\uD2B8\uAC00 \uD1B5\uACFC\uD558\uAE30 \uC804\uC5D0\uB294 \uC644\uB8CC(done) \uCC98\uB9AC\uD558\uC9C0 \uB9C8\uC138\uC694.",
|
|
5433
|
+
"4. \uC790\uB3D9 \uCEE4\uBC0B\uC774\uB098 vhk goal done \uC740 \uB0B4\uAC00 \uC2B9\uC778\uD558\uAE30 \uC804\uC5D0\uB294 \uD558\uC9C0 \uB9C8\uC138\uC694.",
|
|
5434
|
+
"\uBAA8\uB4E0 \uC751\uB2F5\uC740 \uD55C\uAD6D\uC5B4\uB85C."
|
|
5435
|
+
].join("\n");
|
|
5436
|
+
}
|
|
5437
|
+
function buildHandoffPrompt(git3) {
|
|
5438
|
+
const gitBlock = git3 || "(\uBCC0\uACBD \uC5C6\uC74C)";
|
|
5439
|
+
return [
|
|
5440
|
+
"\uB2F9\uC2E0\uC740 VHK \uD504\uB85C\uC81D\uD2B8\uC758 \uC791\uC5C5 \uD30C\uD2B8\uB108\uC785\uB2C8\uB2E4. \uC624\uB298\uC740 \uC5EC\uAE30\uC11C \uC791\uC5C5\uC744 \uC911\uB2E8\uD558\uACE0 \uCEF4\uD4E8\uD130\uB97C \uAEBC\uC57C \uD569\uB2C8\uB2E4.",
|
|
5441
|
+
"\uB2E4\uC74C \uC138\uC158\uC774 \uC774\uC5B4\uBC1B\uC744 \uC218 \uC788\uAC8C \uC815\uB9AC\uD574 \uC8FC\uC138\uC694.",
|
|
5442
|
+
"",
|
|
5443
|
+
"[\uAC00\uC7A5 \uBA3C\uC800] CLAUDE.md \uB97C 1\uC21C\uC704 \uADDC\uCE59\uC73C\uB85C \uC77D\uC73C\uC138\uC694. AGENTS.md \uB294 Codex/\uBCF4\uC870\uC6A9 \uCC38\uACE0\uB9CC.",
|
|
5444
|
+
"",
|
|
5445
|
+
"[\uC9C0\uAE08 \uC0C1\uD0DC \u2014 \uC790\uB3D9 \uC218\uC9D1\uB428]",
|
|
5446
|
+
"- \uBCC0\uACBD\uB41C \uD30C\uC77C (git status --short):",
|
|
5447
|
+
gitBlock,
|
|
5448
|
+
"",
|
|
5449
|
+
"[\uD574\uC8FC\uC138\uC694 \u2014 \uC815\uB9AC\uB9CC, \uC0C8 \uAC1C\uBC1C \uC2DC\uC791 \uAE08\uC9C0]",
|
|
5450
|
+
"1. \uD604\uC7AC \uBCC0\uACBD\uC0AC\uD56D\uC744 \uD655\uC778\uD558\uACE0, \uC644\uB8CC\uB41C \uC77C / \uBBF8\uC644\uB8CC\uB41C \uC77C\uB85C \uB098\uB220 \uD55C\uAD6D\uC5B4\uB85C \uC815\uB9AC\uD574 \uC8FC\uC138\uC694.",
|
|
5451
|
+
"2. \uD14C\uC2A4\uD2B8\uB97C \uC2E4\uD589\uD588\uB294\uC9C0/\uC548 \uD588\uB294\uC9C0, \uACB0\uACFC\uAC00 \uC5B4\uB560\uB294\uC9C0 \uBA85\uD655\uD788 \uAE30\uB85D\uD574 \uC8FC\uC138\uC694. (\uC548 \uB3CC\uB838\uC73C\uBA74 '\uBBF8\uC2E4\uD589')",
|
|
5452
|
+
"3. docs/state/next-task.md \uB97C \uC9C0\uAE08 \uC0C1\uD0DC\uC5D0 \uB9DE\uAC8C \uC5C5\uB370\uC774\uD2B8\uD574 \uC8FC\uC138\uC694. (\uB2E4\uC74C \uC0AC\uB78C\uC774 \uC774\uC5B4\uBC1B\uC744 \uC9C0\uC810 \uBA85\uD655\uD788)",
|
|
5453
|
+
"4. \uC9C0\uAE08 \uCEE4\uBC0B \uAC00\uB2A5\uD55C \uC0C1\uD0DC\uC778\uC9C0 \uD310\uB2E8\uD574 \uC8FC\uC138\uC694. (\uC774\uC720 \uC124\uBA85)",
|
|
5454
|
+
"5. \uC644\uB8CC(done) \uCC98\uB9AC\uD558\uBA74 \uC548 \uB418\uB294 \uC0C1\uD0DC\uB77C\uBA74 \uC815\uB9AC \uB9E8 \uC704\uC5D0 \uAD75\uAC8C \uD45C\uC2DC\uD574 \uC8FC\uC138\uC694.",
|
|
5455
|
+
"",
|
|
5456
|
+
"[\uAE08\uC9C0] \uC9C1\uC811 git commit / git stash / git reset / vhk goal done \uC2E4\uD589 \uAE08\uC9C0. \uD30C\uC77C \uB418\uB3CC\uB9AC\uAE30\xB7\uC0AD\uC81C \uAE08\uC9C0.",
|
|
5457
|
+
"\uBAA8\uB4E0 \uC751\uB2F5\uC740 \uD55C\uAD6D\uC5B4\uB85C."
|
|
5458
|
+
].join("\n");
|
|
5459
|
+
}
|
|
5460
|
+
function emitPrompt(prompt, fileName, label) {
|
|
5461
|
+
let savedPath = "";
|
|
5462
|
+
try {
|
|
5463
|
+
mkdirSync10(VHK_DIR2, { recursive: true });
|
|
5464
|
+
savedPath = join9(VHK_DIR2, fileName);
|
|
5465
|
+
writeFileSync10(savedPath, prompt, "utf-8");
|
|
5466
|
+
} catch {
|
|
5467
|
+
savedPath = "";
|
|
5468
|
+
}
|
|
5469
|
+
const copied = copyToClipboard(prompt);
|
|
5470
|
+
if (copied) {
|
|
5471
|
+
console.log(chalk26.green(`
|
|
5472
|
+
\u{1F4CB} Claude\uC5D0\uAC8C \uC904 '${label}'\uC744 \uD074\uB9BD\uBCF4\uB4DC\uC5D0 \uBCF5\uC0AC\uD588\uC2B5\uB2C8\uB2E4! \u2705`));
|
|
5473
|
+
if (savedPath) console.log(chalk26.dim(` (\uC0AC\uBCF8 \uC800\uC7A5: ${savedPath})`));
|
|
5474
|
+
} else {
|
|
5475
|
+
console.log(chalk26.yellow(`
|
|
5476
|
+
\u26A0\uFE0F \uD074\uB9BD\uBCF4\uB4DC \uBCF5\uC0AC\uC5D0 \uC2E4\uD328\uD588\uC5B4\uC694 \u2014 \uC544\uB798 \uD504\uB86C\uD504\uD2B8\uB97C \uC9C1\uC811 \uBCF5\uC0AC\uD558\uC138\uC694:`));
|
|
5477
|
+
if (savedPath) console.log(chalk26.dim(` (\uD30C\uC77C\uB85C\uB3C4 \uC800\uC7A5\uB428: ${savedPath} \u2014 \uC5F4\uC5B4\uC11C \uBCF5\uC0AC \uAC00\uB2A5)`));
|
|
5478
|
+
console.log(chalk26.gray("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
5479
|
+
console.log(prompt);
|
|
5480
|
+
console.log(chalk26.gray("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
5481
|
+
}
|
|
5482
|
+
}
|
|
5483
|
+
async function refreshContextQuietly() {
|
|
5484
|
+
const origLog = console.log;
|
|
5485
|
+
console.log = () => {
|
|
5486
|
+
};
|
|
5487
|
+
try {
|
|
5488
|
+
await context({ compact: true });
|
|
5489
|
+
return true;
|
|
5490
|
+
} catch {
|
|
5491
|
+
return false;
|
|
5492
|
+
} finally {
|
|
5493
|
+
console.log = origLog;
|
|
5494
|
+
}
|
|
5495
|
+
}
|
|
5496
|
+
async function work() {
|
|
5497
|
+
console.log(chalk26.bold(`
|
|
5498
|
+
${ko.work.workTitle}`));
|
|
5499
|
+
console.log(chalk26.gray("\u2500".repeat(40)));
|
|
5500
|
+
if (!ensureVhkProject()) return;
|
|
5501
|
+
if (!passHardStop()) return;
|
|
5502
|
+
const git3 = gitShort();
|
|
5503
|
+
console.log("");
|
|
5504
|
+
console.log(chalk26.cyan("\u{1F4CB} \uBCC0\uACBD\uB41C \uD30C\uC77C"));
|
|
5505
|
+
printGit(git3);
|
|
5506
|
+
console.log("");
|
|
5507
|
+
console.log(chalk26.cyan("\u{1F4D6} \uADDC\uCE59"));
|
|
5508
|
+
console.log(" \xB7 CLAUDE.md \uAC00 1\uC21C\uC704 \uADDC\uCE59\uC785\uB2C8\uB2E4.");
|
|
5509
|
+
console.log(chalk26.dim(" \xB7 AGENTS.md \uB294 Codex/\uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8\uC6A9 \uCC38\uACE0 \uADDC\uCE59\uC785\uB2C8\uB2E4."));
|
|
5510
|
+
const goalLine = activeGoalLine();
|
|
5511
|
+
console.log("");
|
|
5512
|
+
console.log(chalk26.cyan(`\u{1F3AF} \uD604\uC7AC \uBAA9\uD45C: ${goalLine}`));
|
|
5513
|
+
const refreshed = await refreshContextQuietly();
|
|
5514
|
+
console.log(chalk26.dim(refreshed ? "\u2699\uFE0F .vhk/context.md \uAC31\uC2E0\uB428" : "\u2699\uFE0F .vhk/context.md \uAC31\uC2E0 \uC0DD\uB7B5(\uAC74\uB108\uB700)"));
|
|
5515
|
+
const nextTaskPath = join9("docs", "state", "next-task.md");
|
|
5516
|
+
if (existsSync14(nextTaskPath)) {
|
|
5517
|
+
try {
|
|
5518
|
+
const lines = readFileSync7(nextTaskPath, "utf-8").split(/\r?\n/).slice(0, 12);
|
|
5519
|
+
console.log("");
|
|
5520
|
+
console.log(chalk26.cyan("\u{1F4DD} \uB2E4\uC74C \uD560 \uC77C (next-task.md)"));
|
|
5521
|
+
for (const l of lines) console.log(chalk26.dim(` ${l}`));
|
|
5522
|
+
} catch {
|
|
5523
|
+
}
|
|
5524
|
+
}
|
|
5525
|
+
const prompt = buildStartPrompt(git3, goalLine);
|
|
5526
|
+
emitPrompt(prompt, "work-prompt.md", "\uC2DC\uC791 \uD504\uB86C\uD504\uD2B8");
|
|
5527
|
+
printNextStep({
|
|
5528
|
+
message: "\uD130\uBBF8\uB110\uC5D0\uC11C claude \uB97C \uC2E4\uD589\uD55C \uB4A4, Ctrl+V \uB85C \uBD99\uC5EC\uB123\uACE0 Enter \uD558\uC138\uC694.",
|
|
5529
|
+
command: "claude"
|
|
5530
|
+
});
|
|
5531
|
+
}
|
|
5532
|
+
async function workHandoff() {
|
|
5533
|
+
console.log(chalk26.bold(`
|
|
5534
|
+
${ko.work.handoffTitle}`));
|
|
5535
|
+
console.log(chalk26.gray("\u2500".repeat(40)));
|
|
5536
|
+
if (!ensureVhkProject()) return;
|
|
5537
|
+
if (!passHardStop()) return;
|
|
5538
|
+
const git3 = gitShort();
|
|
5539
|
+
console.log("");
|
|
5540
|
+
console.log(chalk26.cyan("\u{1F4CB} \uBC14\uB010 \uD30C\uC77C"));
|
|
5541
|
+
printGit(git3);
|
|
5542
|
+
const prompt = buildHandoffPrompt(git3);
|
|
5543
|
+
emitPrompt(prompt, "handoff-prompt.md", "\uC911\uB2E8 \uC815\uB9AC \uD504\uB86C\uD504\uD2B8");
|
|
5544
|
+
console.log("");
|
|
5545
|
+
console.log(chalk26.dim("\u{1F4A1} \uC774 \uBA85\uB839\uC740 \uCEE4\uBC0B\xB7\uC0AD\uC81C\uB97C \uC808\uB300 \uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC815\uB9AC\xB7\uD310\uB2E8\uC740 Claude \uAC00 \uD569\uB2C8\uB2E4."));
|
|
5546
|
+
printNextStep({
|
|
5547
|
+
message: "\uD130\uBBF8\uB110\uC5D0\uC11C claude \uB97C \uC2E4\uD589\uD55C \uB4A4, Ctrl+V \uB85C \uBD99\uC5EC\uB123\uACE0 Enter \uD558\uC138\uC694.",
|
|
5548
|
+
command: "claude"
|
|
5549
|
+
});
|
|
5550
|
+
}
|
|
5551
|
+
|
|
5552
|
+
// src/commands/start.ts
|
|
5553
|
+
import chalk27 from "chalk";
|
|
5218
5554
|
import inquirer11 from "inquirer";
|
|
5219
5555
|
import { simpleGit as simpleGit2 } from "simple-git";
|
|
5220
|
-
import { existsSync as
|
|
5221
|
-
import { join as
|
|
5556
|
+
import { existsSync as existsSync15 } from "fs";
|
|
5557
|
+
import { join as join10 } from "path";
|
|
5222
5558
|
var VHK_FOOTPRINT_FILES = [
|
|
5223
5559
|
"CLAUDE.md",
|
|
5224
5560
|
".cursorrules",
|
|
@@ -5227,7 +5563,7 @@ var VHK_FOOTPRINT_FILES = [
|
|
|
5227
5563
|
"docs/PRD.md"
|
|
5228
5564
|
];
|
|
5229
5565
|
function detectExistingFootprint(cwd) {
|
|
5230
|
-
return VHK_FOOTPRINT_FILES.filter((rel) =>
|
|
5566
|
+
return VHK_FOOTPRINT_FILES.filter((rel) => existsSync15(join10(cwd, rel)));
|
|
5231
5567
|
}
|
|
5232
5568
|
async function runGitInit(cwd) {
|
|
5233
5569
|
try {
|
|
@@ -5256,21 +5592,21 @@ async function runStep(label, fn) {
|
|
|
5256
5592
|
}
|
|
5257
5593
|
}
|
|
5258
5594
|
async function start(options = {}) {
|
|
5259
|
-
console.log(
|
|
5595
|
+
console.log(chalk27.bold(`
|
|
5260
5596
|
${ko.start.title}
|
|
5261
5597
|
`));
|
|
5262
|
-
console.log(
|
|
5263
|
-
console.log(
|
|
5264
|
-
console.log(
|
|
5265
|
-
console.log(
|
|
5266
|
-
console.log(
|
|
5598
|
+
console.log(chalk27.dim(ko.start.intro));
|
|
5599
|
+
console.log(chalk27.dim(` ${ko.start.step1}`));
|
|
5600
|
+
console.log(chalk27.dim(` ${ko.start.step2}`));
|
|
5601
|
+
console.log(chalk27.dim(` ${ko.start.step3}`));
|
|
5602
|
+
console.log(chalk27.dim(` ${ko.start.step4}`));
|
|
5267
5603
|
console.log();
|
|
5268
5604
|
const cwd = process.cwd();
|
|
5269
5605
|
const footprint = detectExistingFootprint(cwd);
|
|
5270
5606
|
if (footprint.length > 0 && !options.yes) {
|
|
5271
|
-
console.log(
|
|
5272
|
-
for (const f of footprint) console.log(
|
|
5273
|
-
console.log(
|
|
5607
|
+
console.log(chalk27.yellow("\u26A0\uFE0F \uC774\uBBF8 VHK \uC124\uCE58 \uD754\uC801\uC774 \uAC10\uC9C0\uB410\uC5B4\uC694:"));
|
|
5608
|
+
for (const f of footprint) console.log(chalk27.dim(` - ${f}`));
|
|
5609
|
+
console.log(chalk27.dim(" \uACC4\uC18D \uC9C4\uD589\uD558\uBA74 \uC77C\uBD80 \uD30C\uC77C(`.cursor/mcp.json`, `.vhk/context.md`)\uC740 \uAC31\uC2E0\xB7\uB36E\uC5B4\uC4F0\uAE30\uB429\uB2C8\uB2E4."));
|
|
5274
5610
|
const { proceedExisting } = await inquirer11.prompt([{
|
|
5275
5611
|
type: "confirm",
|
|
5276
5612
|
name: "proceedExisting",
|
|
@@ -5308,7 +5644,7 @@ ${ko.start.title}
|
|
|
5308
5644
|
await runStep("[3/4] vhk mcp-init", () => mcpInit());
|
|
5309
5645
|
log.step(ko.start.step4Header);
|
|
5310
5646
|
await runStep("[4/4] vhk context", () => context());
|
|
5311
|
-
console.log(
|
|
5647
|
+
console.log(chalk27.bold.green(`
|
|
5312
5648
|
${ko.start.allDone}
|
|
5313
5649
|
`));
|
|
5314
5650
|
printNextStep({
|
|
@@ -5318,15 +5654,15 @@ ${ko.start.allDone}
|
|
|
5318
5654
|
}
|
|
5319
5655
|
|
|
5320
5656
|
// src/commands/cloud.ts
|
|
5321
|
-
import
|
|
5322
|
-
import
|
|
5323
|
-
import
|
|
5324
|
-
import
|
|
5657
|
+
import fs13 from "fs";
|
|
5658
|
+
import os2 from "os";
|
|
5659
|
+
import path14 from "path";
|
|
5660
|
+
import chalk28 from "chalk";
|
|
5325
5661
|
|
|
5326
5662
|
// src/lib/vhk-cloud.ts
|
|
5327
5663
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
5328
|
-
import
|
|
5329
|
-
import
|
|
5664
|
+
import fs12 from "fs";
|
|
5665
|
+
import path13 from "path";
|
|
5330
5666
|
var DEFAULT_CLOUD_EXCLUDES = [
|
|
5331
5667
|
"memory.json",
|
|
5332
5668
|
// 개인 의사결정 메모
|
|
@@ -5339,22 +5675,22 @@ var DEFAULT_CLOUD_EXCLUDES = [
|
|
|
5339
5675
|
".gitignore"
|
|
5340
5676
|
// .vhk/ 내부 gitignore
|
|
5341
5677
|
];
|
|
5342
|
-
var
|
|
5678
|
+
var VHK_DIR3 = ".vhk";
|
|
5343
5679
|
var CLOUD_CONFIG_FILE = "cloud.json";
|
|
5344
5680
|
function loadVhkignore(rootDir) {
|
|
5345
5681
|
const ig = (0, import_ignore.default)();
|
|
5346
5682
|
ig.add(DEFAULT_CLOUD_EXCLUDES);
|
|
5347
|
-
const ignorePath =
|
|
5348
|
-
if (
|
|
5349
|
-
ig.add(
|
|
5683
|
+
const ignorePath = path13.join(rootDir, ".vhkignore");
|
|
5684
|
+
if (fs12.existsSync(ignorePath)) {
|
|
5685
|
+
ig.add(fs12.readFileSync(ignorePath, "utf-8"));
|
|
5350
5686
|
}
|
|
5351
5687
|
return ig;
|
|
5352
5688
|
}
|
|
5353
5689
|
function collectVhkFiles(rootDir, ig = loadVhkignore(rootDir)) {
|
|
5354
|
-
const vhkDir =
|
|
5690
|
+
const vhkDir = path13.join(rootDir, VHK_DIR3);
|
|
5355
5691
|
let entries;
|
|
5356
5692
|
try {
|
|
5357
|
-
entries =
|
|
5693
|
+
entries = fs12.readdirSync(vhkDir, { withFileTypes: true });
|
|
5358
5694
|
} catch {
|
|
5359
5695
|
return [];
|
|
5360
5696
|
}
|
|
@@ -5370,8 +5706,8 @@ function partitionGistFiles(gistFiles, ig) {
|
|
|
5370
5706
|
return { keep, excluded };
|
|
5371
5707
|
}
|
|
5372
5708
|
function readCloudConfig(rootDir) {
|
|
5373
|
-
const p =
|
|
5374
|
-
if (!
|
|
5709
|
+
const p = path13.join(rootDir, VHK_DIR3, CLOUD_CONFIG_FILE);
|
|
5710
|
+
if (!fs12.existsSync(p)) return null;
|
|
5375
5711
|
try {
|
|
5376
5712
|
const parsed = readJsonFile(p);
|
|
5377
5713
|
if (parsed && typeof parsed.gistId === "string" && parsed.gistId) {
|
|
@@ -5383,17 +5719,17 @@ function readCloudConfig(rootDir) {
|
|
|
5383
5719
|
}
|
|
5384
5720
|
}
|
|
5385
5721
|
function writeCloudConfig(rootDir, config) {
|
|
5386
|
-
const vhkDir =
|
|
5387
|
-
|
|
5388
|
-
const p =
|
|
5389
|
-
|
|
5722
|
+
const vhkDir = path13.join(rootDir, VHK_DIR3);
|
|
5723
|
+
fs12.mkdirSync(vhkDir, { recursive: true });
|
|
5724
|
+
const p = path13.join(vhkDir, CLOUD_CONFIG_FILE);
|
|
5725
|
+
fs12.writeFileSync(p, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
5390
5726
|
ensureCloudConfigIgnored(vhkDir);
|
|
5391
5727
|
}
|
|
5392
5728
|
function ensureCloudConfigIgnored(vhkDir) {
|
|
5393
|
-
const giPath =
|
|
5729
|
+
const giPath = path13.join(vhkDir, ".gitignore");
|
|
5394
5730
|
let content = "";
|
|
5395
5731
|
try {
|
|
5396
|
-
if (
|
|
5732
|
+
if (fs12.existsSync(giPath)) content = fs12.readFileSync(giPath, "utf-8");
|
|
5397
5733
|
} catch {
|
|
5398
5734
|
return;
|
|
5399
5735
|
}
|
|
@@ -5404,7 +5740,7 @@ ${CLOUD_CONFIG_FILE}
|
|
|
5404
5740
|
`;
|
|
5405
5741
|
const base = content.length === 0 ? "" : content.endsWith("\n") ? content : content + "\n";
|
|
5406
5742
|
try {
|
|
5407
|
-
|
|
5743
|
+
fs12.writeFileSync(giPath, base + block, "utf-8");
|
|
5408
5744
|
} catch {
|
|
5409
5745
|
}
|
|
5410
5746
|
}
|
|
@@ -5413,14 +5749,14 @@ ${CLOUD_CONFIG_FILE}
|
|
|
5413
5749
|
function ensureGhReady() {
|
|
5414
5750
|
const ver = safeExecFile("gh", ["--version"]);
|
|
5415
5751
|
if (!ver.ok) {
|
|
5416
|
-
console.log(
|
|
5417
|
-
console.log(
|
|
5752
|
+
console.log(chalk28.red(` ${ko.cloud.noGh}`));
|
|
5753
|
+
console.log(chalk28.dim(" \uC124\uCE58: https://cli.github.com/ (\uC124\uCE58 \uD6C4 `gh auth login`)"));
|
|
5418
5754
|
return false;
|
|
5419
5755
|
}
|
|
5420
5756
|
const auth = safeExecFile("gh", ["auth", "status"]);
|
|
5421
5757
|
if (!auth.ok) {
|
|
5422
|
-
console.log(
|
|
5423
|
-
console.log(
|
|
5758
|
+
console.log(chalk28.red(` ${ko.cloud.noAuth}`));
|
|
5759
|
+
console.log(chalk28.dim(" \uC2E4\uD589: gh auth login (gist \uAD8C\uD55C \uD544\uC694)"));
|
|
5424
5760
|
return false;
|
|
5425
5761
|
}
|
|
5426
5762
|
return true;
|
|
@@ -5433,29 +5769,29 @@ function parseGistId(output) {
|
|
|
5433
5769
|
return null;
|
|
5434
5770
|
}
|
|
5435
5771
|
async function cloudPush() {
|
|
5436
|
-
console.log(
|
|
5772
|
+
console.log(chalk28.bold(`
|
|
5437
5773
|
${ko.cloud.pushTitle}
|
|
5438
5774
|
`));
|
|
5439
5775
|
const cwd = process.cwd();
|
|
5440
|
-
if (!
|
|
5441
|
-
console.log(
|
|
5776
|
+
if (!fs13.existsSync(path14.join(cwd, VHK_DIR3))) {
|
|
5777
|
+
console.log(chalk28.yellow(` ${ko.cloud.noVhkDir}`));
|
|
5442
5778
|
return;
|
|
5443
5779
|
}
|
|
5444
5780
|
const ig = loadVhkignore(cwd);
|
|
5445
5781
|
const files = collectVhkFiles(cwd, ig);
|
|
5446
5782
|
if (files.length === 0) {
|
|
5447
|
-
console.log(
|
|
5783
|
+
console.log(chalk28.yellow(` ${ko.cloud.nothingToSync}`));
|
|
5448
5784
|
return;
|
|
5449
5785
|
}
|
|
5450
5786
|
if (!ensureGhReady()) {
|
|
5451
5787
|
process.exitCode = 1;
|
|
5452
5788
|
return;
|
|
5453
5789
|
}
|
|
5454
|
-
const filePaths = files.map((f) =>
|
|
5455
|
-
console.log(
|
|
5790
|
+
const filePaths = files.map((f) => path14.join(cwd, VHK_DIR3, f));
|
|
5791
|
+
console.log(chalk28.dim(` \u{1F4E6} \uBC31\uC5C5 \uB300\uC0C1 ${files.length}\uAC1C: ${files.join(", ")}
|
|
5456
5792
|
`));
|
|
5457
5793
|
const existing = readCloudConfig(cwd);
|
|
5458
|
-
const desc = `vhk .vhk backup \u2014 ${
|
|
5794
|
+
const desc = `vhk .vhk backup \u2014 ${path14.basename(cwd)}`;
|
|
5459
5795
|
if (existing) {
|
|
5460
5796
|
const gistFiles = listGistFiles(existing.gistId);
|
|
5461
5797
|
for (let i = 0; i < files.length; i++) {
|
|
@@ -5464,8 +5800,8 @@ ${ko.cloud.pushTitle}
|
|
|
5464
5800
|
const args = gistFiles.includes(name) ? ["gist", "edit", existing.gistId, "-f", name, src] : ["gist", "edit", existing.gistId, "-a", src];
|
|
5465
5801
|
const res2 = safeExecFile("gh", args);
|
|
5466
5802
|
if (!res2.ok) {
|
|
5467
|
-
console.log(
|
|
5468
|
-
console.log(
|
|
5803
|
+
console.log(chalk28.red(` ${ko.cloud.pushFail}: ${name}`));
|
|
5804
|
+
console.log(chalk28.dim(` ${res2.err}`));
|
|
5469
5805
|
process.exitCode = 1;
|
|
5470
5806
|
return;
|
|
5471
5807
|
}
|
|
@@ -5480,15 +5816,15 @@ ${ko.cloud.pushTitle}
|
|
|
5480
5816
|
if (!purgeFailed.includes(name)) purgeFailed.push(name);
|
|
5481
5817
|
}
|
|
5482
5818
|
}
|
|
5483
|
-
console.log(
|
|
5484
|
-
console.log(
|
|
5819
|
+
console.log(chalk28.green.bold(` ${ko.cloud.pushDone}`));
|
|
5820
|
+
console.log(chalk28.dim(` gist: ${existing.gistId} (\uAC31\uC2E0)`));
|
|
5485
5821
|
if (excluded.length > 0) {
|
|
5486
5822
|
const purged = excluded.filter((n) => !purgeFailed.includes(n));
|
|
5487
5823
|
if (purged.length > 0) {
|
|
5488
|
-
console.log(
|
|
5824
|
+
console.log(chalk28.dim(` \u{1F512} \uC81C\uC678 \uB300\uC0C1 ${purged.length}\uAC1C gist \uC5D0\uC11C \uC81C\uAC70: ${purged.join(", ")}`));
|
|
5489
5825
|
}
|
|
5490
5826
|
if (purgeFailed.length > 0) {
|
|
5491
|
-
console.log(
|
|
5827
|
+
console.log(chalk28.yellow(` \u26A0\uFE0F \uC81C\uC678 \uB300\uC0C1 \uC81C\uAC70 \uC2E4\uD328: ${purgeFailed.join(", ")} (\uC218\uB3D9 \uC81C\uAC70 \uAD8C\uC7A5 \u2014 pull \uC2DC\uC5D4 \uBCF5\uC6D0 \uC548 \uB428)`));
|
|
5492
5828
|
}
|
|
5493
5829
|
}
|
|
5494
5830
|
printPushNext();
|
|
@@ -5496,32 +5832,32 @@ ${ko.cloud.pushTitle}
|
|
|
5496
5832
|
}
|
|
5497
5833
|
const res = safeExecFile("gh", ["gist", "create", "--desc", desc, ...filePaths]);
|
|
5498
5834
|
if (!res.ok) {
|
|
5499
|
-
console.log(
|
|
5500
|
-
console.log(
|
|
5835
|
+
console.log(chalk28.red(` ${ko.cloud.pushFail}`));
|
|
5836
|
+
console.log(chalk28.dim(` ${res.err || res.out}`));
|
|
5501
5837
|
process.exitCode = 1;
|
|
5502
5838
|
return;
|
|
5503
5839
|
}
|
|
5504
5840
|
const gistId = parseGistId(res.out);
|
|
5505
5841
|
if (!gistId) {
|
|
5506
|
-
console.log(
|
|
5507
|
-
console.log(
|
|
5842
|
+
console.log(chalk28.red(` ${ko.cloud.pushFail} \u2014 gist id \uD30C\uC2F1 \uC2E4\uD328`));
|
|
5843
|
+
console.log(chalk28.dim(` \uCD9C\uB825: ${res.out}`));
|
|
5508
5844
|
process.exitCode = 1;
|
|
5509
5845
|
return;
|
|
5510
5846
|
}
|
|
5511
5847
|
writeCloudConfig(cwd, { gistId });
|
|
5512
|
-
console.log(
|
|
5513
|
-
console.log(
|
|
5848
|
+
console.log(chalk28.green.bold(` ${ko.cloud.pushDone}`));
|
|
5849
|
+
console.log(chalk28.dim(` gist: ${gistId} (\uC2E0\uADDC, secret) \u2192 .vhk/cloud.json \uC800\uC7A5`));
|
|
5514
5850
|
printPushNext();
|
|
5515
5851
|
}
|
|
5516
5852
|
async function cloudPull(gistIdArg) {
|
|
5517
|
-
console.log(
|
|
5853
|
+
console.log(chalk28.bold(`
|
|
5518
5854
|
${ko.cloud.pullTitle}
|
|
5519
5855
|
`));
|
|
5520
5856
|
const cwd = process.cwd();
|
|
5521
5857
|
const gistId = gistIdArg || readCloudConfig(cwd)?.gistId;
|
|
5522
5858
|
if (!gistId) {
|
|
5523
|
-
console.log(
|
|
5524
|
-
console.log(
|
|
5859
|
+
console.log(chalk28.yellow(` ${ko.cloud.noGistId}`));
|
|
5860
|
+
console.log(chalk28.dim(" \uC0AC\uC6A9\uBC95: vhk cloud pull <gistId> (\uB610\uB294 cloud.json \uC774 \uC788\uB294 \uACF3\uC5D0\uC11C \uC2E4\uD589)"));
|
|
5525
5861
|
return;
|
|
5526
5862
|
}
|
|
5527
5863
|
if (!ensureGhReady()) {
|
|
@@ -5530,34 +5866,34 @@ ${ko.cloud.pullTitle}
|
|
|
5530
5866
|
}
|
|
5531
5867
|
const allNames = listGistFiles(gistId);
|
|
5532
5868
|
if (allNames.length === 0) {
|
|
5533
|
-
console.log(
|
|
5869
|
+
console.log(chalk28.red(` ${ko.cloud.pullFail} \u2014 gist \uBE44\uC5C8\uAC70\uB098 \uC811\uADFC \uBD88\uAC00: ${gistId}`));
|
|
5534
5870
|
process.exitCode = 1;
|
|
5535
5871
|
return;
|
|
5536
5872
|
}
|
|
5537
5873
|
const { keep: names, excluded: skipped } = partitionGistFiles(allNames, loadVhkignore(cwd));
|
|
5538
5874
|
if (skipped.length > 0) {
|
|
5539
|
-
console.log(
|
|
5875
|
+
console.log(chalk28.dim(` \u{1F512} \uC81C\uC678 \uB300\uC0C1 ${skipped.length}\uAC1C \uBCF5\uC6D0 \uC2A4\uD0B5: ${skipped.join(", ")}`));
|
|
5540
5876
|
}
|
|
5541
5877
|
if (names.length === 0) {
|
|
5542
|
-
console.log(
|
|
5878
|
+
console.log(chalk28.yellow(` \uBCF5\uC6D0 \uB300\uC0C1\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 (gist \uD30C\uC77C\uC774 \uBAA8\uB450 \uC81C\uC678 \uADDC\uCE59\uC5D0 \uD574\uB2F9).`));
|
|
5543
5879
|
return;
|
|
5544
5880
|
}
|
|
5545
|
-
const vhkDir =
|
|
5546
|
-
|
|
5881
|
+
const vhkDir = path14.join(cwd, VHK_DIR3);
|
|
5882
|
+
fs13.mkdirSync(vhkDir, { recursive: true });
|
|
5547
5883
|
let restored = 0;
|
|
5548
5884
|
for (const name of names) {
|
|
5549
5885
|
const res = safeExecFile("gh", ["gist", "view", gistId, "-f", name, "--raw"]);
|
|
5550
5886
|
if (!res.ok) {
|
|
5551
|
-
console.log(
|
|
5552
|
-
console.log(
|
|
5887
|
+
console.log(chalk28.red(` ${ko.cloud.pullFail}: ${name}`));
|
|
5888
|
+
console.log(chalk28.dim(` ${res.err}`));
|
|
5553
5889
|
continue;
|
|
5554
5890
|
}
|
|
5555
|
-
|
|
5891
|
+
fs13.writeFileSync(path14.join(vhkDir, name), ensureTrailingNewline(res.out), "utf-8");
|
|
5556
5892
|
restored++;
|
|
5557
5893
|
}
|
|
5558
5894
|
writeCloudConfig(cwd, { gistId });
|
|
5559
|
-
console.log(
|
|
5560
|
-
console.log(
|
|
5895
|
+
console.log(chalk28.green.bold(` ${ko.cloud.pullDone}`));
|
|
5896
|
+
console.log(chalk28.dim(` ${restored}\uAC1C \uD30C\uC77C \uBCF5\uC6D0 (gist: ${gistId})`));
|
|
5561
5897
|
printNextStep({
|
|
5562
5898
|
message: "\uD074\uB77C\uC6B0\uB4DC\uC5D0\uC11C .vhk/ \uBCF5\uC6D0 \uC644\uB8CC!",
|
|
5563
5899
|
command: "vhk \uB9E5\uB77D",
|
|
@@ -5569,9 +5905,9 @@ function purgeExcludedFromGist(gistId, names) {
|
|
|
5569
5905
|
const body = JSON.stringify({
|
|
5570
5906
|
files: Object.fromEntries(names.map((n) => [n, null]))
|
|
5571
5907
|
});
|
|
5572
|
-
const tmp =
|
|
5908
|
+
const tmp = path14.join(os2.tmpdir(), `vhk-gist-purge-${process.pid}.json`);
|
|
5573
5909
|
try {
|
|
5574
|
-
|
|
5910
|
+
fs13.writeFileSync(tmp, body, "utf-8");
|
|
5575
5911
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
5576
5912
|
const res = safeExecFile(
|
|
5577
5913
|
"gh",
|
|
@@ -5583,7 +5919,7 @@ function purgeExcludedFromGist(gistId, names) {
|
|
|
5583
5919
|
return false;
|
|
5584
5920
|
} finally {
|
|
5585
5921
|
try {
|
|
5586
|
-
|
|
5922
|
+
fs13.unlinkSync(tmp);
|
|
5587
5923
|
} catch {
|
|
5588
5924
|
}
|
|
5589
5925
|
}
|
|
@@ -5605,7 +5941,7 @@ function printPushNext() {
|
|
|
5605
5941
|
}
|
|
5606
5942
|
|
|
5607
5943
|
// src/commands/help.ts
|
|
5608
|
-
import
|
|
5944
|
+
import chalk29 from "chalk";
|
|
5609
5945
|
var QUICK_ACTIONS = [
|
|
5610
5946
|
{ say: "\uC0C1\uD0DC \uC54C\uB824\uC918", does: "vhk status" },
|
|
5611
5947
|
{ say: "\uBB50 \uBC14\uB00C\uC5C8\uC5B4?", does: "vhk diff" },
|
|
@@ -5619,21 +5955,21 @@ var QUICK_ACTIONS = [
|
|
|
5619
5955
|
{ say: "\uC804\uCCB4 \uBA85\uB839\uC5B4 \uBCF4\uAE30", does: "vhk --help" }
|
|
5620
5956
|
];
|
|
5621
5957
|
function quickActions() {
|
|
5622
|
-
console.log(
|
|
5623
|
-
console.log(
|
|
5958
|
+
console.log(chalk29.bold("\n\u{1F9ED} VHK \u2014 \uC774\uB807\uAC8C \uB9D0\uD558\uBA74 \uB429\uB2C8\uB2E4 (quick actions)"));
|
|
5959
|
+
console.log(chalk29.gray("\u2500".repeat(40)));
|
|
5624
5960
|
for (const a of QUICK_ACTIONS) {
|
|
5625
|
-
console.log(` "${
|
|
5961
|
+
console.log(` "${chalk29.cyan(a.say)}" \u2192 ${chalk29.dim(a.does)}`);
|
|
5626
5962
|
}
|
|
5627
|
-
console.log(
|
|
5963
|
+
console.log(chalk29.gray("\n \uC804\uCCB4 \uBA85\uB839\uC740 `vhk --help` \uB610\uB294 COMMANDS.md \uB97C \uBCF4\uC138\uC694."));
|
|
5628
5964
|
console.log("");
|
|
5629
5965
|
}
|
|
5630
5966
|
|
|
5631
5967
|
// src/commands/mode.ts
|
|
5632
|
-
import
|
|
5968
|
+
import chalk30 from "chalk";
|
|
5633
5969
|
|
|
5634
5970
|
// src/lib/config.ts
|
|
5635
|
-
import { existsSync as
|
|
5636
|
-
import { join as
|
|
5971
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "fs";
|
|
5972
|
+
import { join as join11 } from "path";
|
|
5637
5973
|
|
|
5638
5974
|
// src/lib/safety-mode.ts
|
|
5639
5975
|
var SAFETY_MODES = ["lite", "standard", "strict"];
|
|
@@ -5649,11 +5985,11 @@ function isSafetyMode(value) {
|
|
|
5649
5985
|
|
|
5650
5986
|
// src/lib/config.ts
|
|
5651
5987
|
var CONFIG_DIR = ".vhk";
|
|
5652
|
-
var CONFIG_PATH =
|
|
5988
|
+
var CONFIG_PATH = join11(CONFIG_DIR, "config.json");
|
|
5653
5989
|
var DEFAULT_CONFIG = { safetyMode: DEFAULT_SAFETY_MODE };
|
|
5654
5990
|
function readConfig(rootDir = process.cwd()) {
|
|
5655
|
-
const full =
|
|
5656
|
-
if (!
|
|
5991
|
+
const full = join11(rootDir, CONFIG_PATH);
|
|
5992
|
+
if (!existsSync16(full)) return { ...DEFAULT_CONFIG };
|
|
5657
5993
|
try {
|
|
5658
5994
|
const raw = readJsonFile(full);
|
|
5659
5995
|
return {
|
|
@@ -5664,23 +6000,23 @@ function readConfig(rootDir = process.cwd()) {
|
|
|
5664
6000
|
}
|
|
5665
6001
|
}
|
|
5666
6002
|
function writeConfig(config, rootDir = process.cwd()) {
|
|
5667
|
-
|
|
5668
|
-
|
|
6003
|
+
mkdirSync11(join11(rootDir, CONFIG_DIR), { recursive: true });
|
|
6004
|
+
writeFileSync11(join11(rootDir, CONFIG_PATH), JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
5669
6005
|
}
|
|
5670
6006
|
|
|
5671
6007
|
// src/commands/mode.ts
|
|
5672
6008
|
async function mode(target) {
|
|
5673
|
-
console.log(
|
|
5674
|
-
console.log(
|
|
6009
|
+
console.log(chalk30.bold("\n\u{1F6E1}\uFE0F Safety Mode"));
|
|
6010
|
+
console.log(chalk30.gray("\u2500".repeat(40)));
|
|
5675
6011
|
const current = readConfig().safetyMode;
|
|
5676
6012
|
if (!target) {
|
|
5677
|
-
console.log(
|
|
5678
|
-
\uD604\uC7AC \uBAA8\uB4DC: ${
|
|
5679
|
-
console.log(
|
|
6013
|
+
console.log(chalk30.cyan(`
|
|
6014
|
+
\uD604\uC7AC \uBAA8\uB4DC: ${chalk30.bold(current)}`));
|
|
6015
|
+
console.log(chalk30.dim(` ${SAFETY_MODE_DESC[current]}`));
|
|
5680
6016
|
console.log("");
|
|
5681
6017
|
for (const m of SAFETY_MODES) {
|
|
5682
6018
|
const mark = m === current ? "\u25CF" : "\u25CB";
|
|
5683
|
-
console.log(` ${mark} ${m.padEnd(9)} ${
|
|
6019
|
+
console.log(` ${mark} ${m.padEnd(9)} ${chalk30.dim(SAFETY_MODE_DESC[m])}`);
|
|
5684
6020
|
}
|
|
5685
6021
|
printNextStep({
|
|
5686
6022
|
message: "\uBAA8\uB4DC\uB97C \uBC14\uAFB8\uB824\uBA74:",
|
|
@@ -5690,23 +6026,23 @@ async function mode(target) {
|
|
|
5690
6026
|
return;
|
|
5691
6027
|
}
|
|
5692
6028
|
if (!isSafetyMode(target)) {
|
|
5693
|
-
console.log(
|
|
6029
|
+
console.log(chalk30.red(`
|
|
5694
6030
|
\u274C \uC54C \uC218 \uC5C6\uB294 \uBAA8\uB4DC: ${target}`));
|
|
5695
|
-
console.log(
|
|
6031
|
+
console.log(chalk30.dim(` \uAC00\uB2A5: ${SAFETY_MODES.join(" | ")}`));
|
|
5696
6032
|
process.exitCode = 1;
|
|
5697
6033
|
return;
|
|
5698
6034
|
}
|
|
5699
6035
|
writeConfig({ ...readConfig(), safetyMode: target });
|
|
5700
|
-
console.log(
|
|
5701
|
-
\u2705 Safety Mode \u2192 ${
|
|
5702
|
-
console.log(
|
|
6036
|
+
console.log(chalk30.green(`
|
|
6037
|
+
\u2705 Safety Mode \u2192 ${chalk30.bold(target)}`));
|
|
6038
|
+
console.log(chalk30.dim(` ${SAFETY_MODE_DESC[target]}`));
|
|
5703
6039
|
}
|
|
5704
6040
|
|
|
5705
6041
|
// src/commands/verify.ts
|
|
5706
6042
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
5707
|
-
import { existsSync as
|
|
5708
|
-
import { join as
|
|
5709
|
-
import
|
|
6043
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync12, writeFileSync as writeFileSync12 } from "fs";
|
|
6044
|
+
import { join as join12 } from "path";
|
|
6045
|
+
import chalk31 from "chalk";
|
|
5710
6046
|
|
|
5711
6047
|
// src/commands/verify-report.ts
|
|
5712
6048
|
function escapeHtml(text) {
|
|
@@ -5814,13 +6150,13 @@ ${actions}
|
|
|
5814
6150
|
|
|
5815
6151
|
// src/commands/verify.ts
|
|
5816
6152
|
var REPORT_SCHEMA_VERSION = 1;
|
|
5817
|
-
var REPORT_DIR_REL =
|
|
5818
|
-
var REPORT_PATH_REL =
|
|
5819
|
-
var REPORT_HTML_PATH_REL =
|
|
6153
|
+
var REPORT_DIR_REL = join12(".vhk", "reports");
|
|
6154
|
+
var REPORT_PATH_REL = join12(REPORT_DIR_REL, "latest.json");
|
|
6155
|
+
var REPORT_HTML_PATH_REL = join12(REPORT_DIR_REL, "latest.html");
|
|
5820
6156
|
var SHIM = /* @__PURE__ */ new Set(["pnpm", "npm", "npx", "yarn"]);
|
|
5821
6157
|
function detectPm(cwd) {
|
|
5822
|
-
if (
|
|
5823
|
-
if (
|
|
6158
|
+
if (existsSync17(join12(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
6159
|
+
if (existsSync17(join12(cwd, "yarn.lock"))) return "yarn";
|
|
5824
6160
|
return "npm";
|
|
5825
6161
|
}
|
|
5826
6162
|
function execGate(cmd, args, cwd) {
|
|
@@ -5863,8 +6199,8 @@ function runScriptGate(id, label, cwd, pm, argvFor) {
|
|
|
5863
6199
|
};
|
|
5864
6200
|
}
|
|
5865
6201
|
function readPackageScripts(cwd) {
|
|
5866
|
-
const pkgPath =
|
|
5867
|
-
if (!
|
|
6202
|
+
const pkgPath = join12(cwd, "package.json");
|
|
6203
|
+
if (!existsSync17(pkgPath)) return {};
|
|
5868
6204
|
try {
|
|
5869
6205
|
const pkg = readJsonFile(pkgPath);
|
|
5870
6206
|
return pkg.scripts ?? {};
|
|
@@ -5879,7 +6215,7 @@ function runGates(cwd) {
|
|
|
5879
6215
|
gates.push(
|
|
5880
6216
|
runScriptGate("typecheck", "tsc --noEmit", cwd, pm, () => {
|
|
5881
6217
|
if (scripts.typecheck) return ["run", "typecheck"];
|
|
5882
|
-
if (
|
|
6218
|
+
if (existsSync17(join12(cwd, "tsconfig.json"))) return pm === "npm" ? ["exec", "--", "tsc", "--noEmit"] : ["exec", "tsc", "--noEmit"];
|
|
5883
6219
|
return null;
|
|
5884
6220
|
})
|
|
5885
6221
|
);
|
|
@@ -5958,10 +6294,10 @@ function buildReport(gates, generatedAt, date) {
|
|
|
5958
6294
|
function verifyEvidence(cwd = process.cwd()) {
|
|
5959
6295
|
const gates = runGates(cwd);
|
|
5960
6296
|
const report = buildReport(gates, (/* @__PURE__ */ new Date()).toISOString(), localDate());
|
|
5961
|
-
const dir =
|
|
5962
|
-
|
|
5963
|
-
const
|
|
5964
|
-
|
|
6297
|
+
const dir = join12(cwd, REPORT_DIR_REL);
|
|
6298
|
+
mkdirSync12(dir, { recursive: true });
|
|
6299
|
+
const path15 = join12(cwd, REPORT_PATH_REL);
|
|
6300
|
+
writeFileSync12(path15, JSON.stringify(report, null, 2) + "\n", "utf-8");
|
|
5965
6301
|
try {
|
|
5966
6302
|
ensureVhkIgnored(cwd, "reports/");
|
|
5967
6303
|
} catch {
|
|
@@ -5969,44 +6305,44 @@ function verifyEvidence(cwd = process.cwd()) {
|
|
|
5969
6305
|
return { report, path: REPORT_PATH_REL };
|
|
5970
6306
|
}
|
|
5971
6307
|
var STATUS_BADGE = {
|
|
5972
|
-
PASS:
|
|
5973
|
-
WARN:
|
|
5974
|
-
FAIL:
|
|
6308
|
+
PASS: chalk31.green.bold("PASS"),
|
|
6309
|
+
WARN: chalk31.yellow.bold("WARN"),
|
|
6310
|
+
FAIL: chalk31.red.bold("FAIL")
|
|
5975
6311
|
};
|
|
5976
6312
|
async function renderVerifyReport(cwd, opts) {
|
|
5977
|
-
const jsonPath =
|
|
6313
|
+
const jsonPath = join12(cwd, REPORT_PATH_REL);
|
|
5978
6314
|
let report;
|
|
5979
|
-
if (
|
|
6315
|
+
if (existsSync17(jsonPath)) {
|
|
5980
6316
|
try {
|
|
5981
6317
|
report = readJsonFile(jsonPath);
|
|
5982
6318
|
} catch {
|
|
5983
|
-
console.log(
|
|
6319
|
+
console.log(chalk31.yellow(" \u26A0\uFE0F \uAE30\uC874 latest.json \uC190\uC0C1 \u2014 verify \uC7AC\uC2E4\uD589\uC73C\uB85C \uC99D\uAC70\uB97C \uB2E4\uC2DC \uB9CC\uB4ED\uB2C8\uB2E4."));
|
|
5984
6320
|
report = verifyEvidence(cwd).report;
|
|
5985
6321
|
}
|
|
5986
6322
|
} else {
|
|
5987
|
-
console.log(
|
|
6323
|
+
console.log(chalk31.dim(" latest.json \uC5C6\uC74C \u2014 verify 1\uD68C \uC120\uC2E4\uD589\uC73C\uB85C \uC99D\uAC70\uB97C \uB9CC\uB4ED\uB2C8\uB2E4."));
|
|
5988
6324
|
report = verifyEvidence(cwd).report;
|
|
5989
6325
|
}
|
|
5990
6326
|
const html = renderReportHtml(report);
|
|
5991
|
-
const htmlPath =
|
|
6327
|
+
const htmlPath = join12(cwd, REPORT_HTML_PATH_REL);
|
|
5992
6328
|
try {
|
|
5993
|
-
|
|
5994
|
-
|
|
6329
|
+
mkdirSync12(join12(cwd, REPORT_DIR_REL), { recursive: true });
|
|
6330
|
+
writeFileSync12(htmlPath, html, "utf-8");
|
|
5995
6331
|
} catch (e) {
|
|
5996
6332
|
console.error(
|
|
5997
|
-
|
|
6333
|
+
chalk31.red(` \u274C \uB9AC\uD3EC\uD2B8 HTML \uC744 \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (${REPORT_HTML_PATH_REL}): ${e instanceof Error ? e.message : String(e)}`)
|
|
5998
6334
|
);
|
|
5999
|
-
console.error(
|
|
6335
|
+
console.error(chalk31.dim(" \uD574\uB2F9 \uACBD\uB85C\uC758 \uC4F0\uAE30 \uAD8C\uD55C\uC744 \uD655\uC778\uD558\uC138\uC694."));
|
|
6000
6336
|
process.exitCode = 1;
|
|
6001
6337
|
return;
|
|
6002
6338
|
}
|
|
6003
|
-
console.log(
|
|
6339
|
+
console.log(chalk31.bold("\n\u{1F50E} \uAC80\uC99D \uB9AC\uD3EC\uD2B8 (verify --report)"));
|
|
6004
6340
|
console.log(` \uACB0\uACFC: ${STATUS_BADGE[report.status]}`);
|
|
6005
|
-
console.log(
|
|
6341
|
+
console.log(chalk31.dim(` \u{1F4C4} HTML: ${REPORT_HTML_PATH_REL}`));
|
|
6006
6342
|
process.exitCode = report.status === "FAIL" ? 1 : 0;
|
|
6007
6343
|
if (opts.open) {
|
|
6008
6344
|
if (isInteractive()) openReportInBrowser(htmlPath);
|
|
6009
|
-
else console.log(
|
|
6345
|
+
else console.log(chalk31.dim(" (\uBE44\uB300\uD654\uD615/CI/MCP \u2014 --open \uC790\uB3D9 \uC2A4\uD0B5)"));
|
|
6010
6346
|
return;
|
|
6011
6347
|
}
|
|
6012
6348
|
printNextStep({
|
|
@@ -6024,8 +6360,8 @@ function openReportInBrowser(filePath) {
|
|
|
6024
6360
|
} else {
|
|
6025
6361
|
result = safeExecFile("xdg-open", [filePath]);
|
|
6026
6362
|
}
|
|
6027
|
-
if (result.ok) console.log(
|
|
6028
|
-
else console.log(
|
|
6363
|
+
if (result.ok) console.log(chalk31.green(" \u2705 \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4."));
|
|
6364
|
+
else console.log(chalk31.yellow(" \u26A0\uFE0F \uBE0C\uB77C\uC6B0\uC800\uB97C \uC5F4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC704 \uD30C\uC77C\uC744 \uC9C1\uC811 \uC5EC\uC138\uC694."));
|
|
6029
6365
|
}
|
|
6030
6366
|
async function verify(opts = {}) {
|
|
6031
6367
|
if (!ensureNotHardStopped("verify")) return;
|
|
@@ -6034,27 +6370,27 @@ async function verify(opts = {}) {
|
|
|
6034
6370
|
await renderVerifyReport(cwd, opts);
|
|
6035
6371
|
return;
|
|
6036
6372
|
}
|
|
6037
|
-
const { report, path:
|
|
6373
|
+
const { report, path: path15 } = verifyEvidence(cwd);
|
|
6038
6374
|
if (opts.json) {
|
|
6039
6375
|
console.log(JSON.stringify(report, null, 2));
|
|
6040
6376
|
process.exitCode = report.status === "FAIL" ? 1 : 0;
|
|
6041
6377
|
return;
|
|
6042
6378
|
}
|
|
6043
|
-
console.log(
|
|
6044
|
-
console.log(
|
|
6379
|
+
console.log(chalk31.bold("\n\u{1F50E} \uAC80\uC99D \uBB36\uC74C (verify)"));
|
|
6380
|
+
console.log(chalk31.gray("\u2500".repeat(40)));
|
|
6045
6381
|
const mode2 = readConfig().safetyMode;
|
|
6046
|
-
console.log(
|
|
6047
|
-
const icon = (s2) => s2 === "pass" ?
|
|
6382
|
+
console.log(chalk31.dim(` \uD604\uC7AC Safety Mode: ${mode2} \u2014 ${SAFETY_MODE_DESC[mode2]}`));
|
|
6383
|
+
const icon = (s2) => s2 === "pass" ? chalk31.green("\u2713") : s2 === "fail" ? chalk31.red("\u2717") : chalk31.yellow("\u2298");
|
|
6048
6384
|
for (const g of report.gates) {
|
|
6049
|
-
const tail = g.detail ?
|
|
6385
|
+
const tail = g.detail ? chalk31.dim(` \u2014 ${g.detail}`) : "";
|
|
6050
6386
|
console.log(` ${icon(g.status)} ${g.label}${tail}`);
|
|
6051
6387
|
}
|
|
6052
6388
|
const s = report.summary;
|
|
6053
6389
|
console.log(
|
|
6054
6390
|
`
|
|
6055
|
-
\uACB0\uACFC: ${STATUS_BADGE[report.status]} ` +
|
|
6391
|
+
\uACB0\uACFC: ${STATUS_BADGE[report.status]} ` + chalk31.dim(`(pass ${s.pass} / fail ${s.fail} / skip ${s.skip}, \uCD1D ${s.total})`)
|
|
6056
6392
|
);
|
|
6057
|
-
console.log(
|
|
6393
|
+
console.log(chalk31.dim(` \u{1F4C4} \uC99D\uAC70: ${path15}`));
|
|
6058
6394
|
process.exitCode = report.status === "FAIL" ? 1 : 0;
|
|
6059
6395
|
if (report.status === "FAIL") {
|
|
6060
6396
|
printNextStep({
|
|
@@ -6073,9 +6409,9 @@ async function verify(opts = {}) {
|
|
|
6073
6409
|
}
|
|
6074
6410
|
|
|
6075
6411
|
// src/commands/review.ts
|
|
6076
|
-
import { existsSync as
|
|
6077
|
-
import { join as
|
|
6078
|
-
import
|
|
6412
|
+
import { existsSync as existsSync18, writeFileSync as writeFileSync13 } from "fs";
|
|
6413
|
+
import { join as join13 } from "path";
|
|
6414
|
+
import chalk32 from "chalk";
|
|
6079
6415
|
var GOALS_DIR2 = "goals";
|
|
6080
6416
|
var COVERAGE_MIN = 0.5;
|
|
6081
6417
|
var STALE_AGE_MS = 6 * 60 * 60 * 1e3;
|
|
@@ -6207,22 +6543,22 @@ function resolveGoal(optId, goals) {
|
|
|
6207
6543
|
return goals.find((g) => g.frontmatter.id === id) ?? null;
|
|
6208
6544
|
}
|
|
6209
6545
|
var CONFIDENCE_LABEL = {
|
|
6210
|
-
low:
|
|
6211
|
-
medium:
|
|
6212
|
-
high:
|
|
6546
|
+
low: chalk32.red.bold("\uB0AE\uC74C (\uAC70\uC9D3\uC644\uB8CC \uC758\uC2EC \uB610\uB294 \uC644\uB8CC \uC8FC\uC7A5 \uC5C6\uC74C)"),
|
|
6547
|
+
medium: chalk32.yellow.bold("\uC911\uAC04 (\uCEE4\uBC84\uB9AC\uC9C0/\uC2E0\uC120\uB3C4 \uBD80\uC871 \u2014 \uC99D\uAC70 \uC5C6\uC74C \u2260 \uD1B5\uACFC)"),
|
|
6548
|
+
high: chalk32.green.bold("\uB192\uC74C (\uC758\uC2EC 0 + \uCEE4\uBC84\uB9AC\uC9C0\xB7\uC2E0\uC120\uB3C4 \uCDA9\uBD84 \u2014 \uB2E8 \uBCF4\uC7A5 \uC544\uB2D8)")
|
|
6213
6549
|
};
|
|
6214
6550
|
async function review(opts = {}) {
|
|
6215
6551
|
if (!ensureNotHardStopped("review")) return;
|
|
6216
6552
|
const cwd = process.cwd();
|
|
6217
6553
|
const goals = listGoals(GOALS_DIR2);
|
|
6218
6554
|
if (goals.length === 0) {
|
|
6219
|
-
console.error(
|
|
6555
|
+
console.error(chalk32.yellow(" \u26A0\uFE0F goals/ \uC5D0 goal \uC774 \uC5C6\uC2B5\uB2C8\uB2E4. vhk goal init \uC73C\uB85C \uC2DC\uC791\uD558\uC138\uC694."));
|
|
6220
6556
|
process.exitCode = 1;
|
|
6221
6557
|
return;
|
|
6222
6558
|
}
|
|
6223
6559
|
const goal = resolveGoal(opts.id, goals);
|
|
6224
6560
|
if (!goal || typeof goal.frontmatter.id !== "number") {
|
|
6225
|
-
console.error(
|
|
6561
|
+
console.error(chalk32.red(` \u274C \uB300\uC0C1 goal \uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4${opts.id ? ` (--id ${opts.id})` : " (active goal \uC5C6\uC74C)"}.`));
|
|
6226
6562
|
process.exitCode = 1;
|
|
6227
6563
|
return;
|
|
6228
6564
|
}
|
|
@@ -6230,11 +6566,11 @@ async function review(opts = {}) {
|
|
|
6230
6566
|
const goalStatus = goal.frontmatter.status ?? "NOT_STARTED";
|
|
6231
6567
|
const checks = parseCompletionChecks(goal.body);
|
|
6232
6568
|
if (opts.id === void 0 && goalStatus === "NOT_STARTED") {
|
|
6233
|
-
console.error(
|
|
6569
|
+
console.error(chalk32.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.`));
|
|
6234
6570
|
}
|
|
6235
|
-
const jsonPath =
|
|
6236
|
-
if (!
|
|
6237
|
-
console.error(
|
|
6571
|
+
const jsonPath = join13(cwd, REPORT_PATH_REL);
|
|
6572
|
+
if (!existsSync18(jsonPath)) {
|
|
6573
|
+
console.error(chalk32.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).`));
|
|
6238
6574
|
printNextStep({
|
|
6239
6575
|
message: "\uC99D\uAC70(latest.json)\uAC00 \uC788\uC5B4\uC57C review \uAC00 \uAD50\uCC28\uAC80\uC99D\uD569\uB2C8\uB2E4:",
|
|
6240
6576
|
command: "vhk verify",
|
|
@@ -6247,7 +6583,7 @@ async function review(opts = {}) {
|
|
|
6247
6583
|
try {
|
|
6248
6584
|
report = readJsonFile(jsonPath);
|
|
6249
6585
|
} catch {
|
|
6250
|
-
console.error(
|
|
6586
|
+
console.error(chalk32.red(` \u274C ${REPORT_PATH_REL} \uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4(\uC190\uC0C1). vhk verify \uB85C \uC7AC\uC0DD\uC131\uD558\uC138\uC694.`));
|
|
6251
6587
|
process.exitCode = 1;
|
|
6252
6588
|
return;
|
|
6253
6589
|
}
|
|
@@ -6259,44 +6595,44 @@ async function review(opts = {}) {
|
|
|
6259
6595
|
goalStatus,
|
|
6260
6596
|
reportStatus: report.status
|
|
6261
6597
|
};
|
|
6262
|
-
console.log(
|
|
6598
|
+
console.log(chalk32.bold(`
|
|
6263
6599
|
\u{1F52C} \uC801\uB300\uC801 \uC790\uAE30\uAC80\uC99D (review) \u2014 Goal ${goalId}`));
|
|
6264
|
-
console.log(
|
|
6600
|
+
console.log(chalk32.gray("\u2500".repeat(44)));
|
|
6265
6601
|
console.log(
|
|
6266
|
-
|
|
6602
|
+
chalk32.dim(
|
|
6267
6603
|
` 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}%)`
|
|
6268
6604
|
)
|
|
6269
6605
|
);
|
|
6270
|
-
console.log(
|
|
6606
|
+
console.log(chalk32.dim(` \uC99D\uAC70 \uC2E0\uC120\uB3C4: ${result.freshness.note}`));
|
|
6271
6607
|
if (result.checkedCount === 0) {
|
|
6272
|
-
console.log(
|
|
6608
|
+
console.log(chalk32.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)."));
|
|
6273
6609
|
}
|
|
6274
6610
|
if (result.suspicions.length > 0) {
|
|
6275
|
-
console.log(
|
|
6611
|
+
console.log(chalk32.red.bold(`
|
|
6276
6612
|
\u{1F6A9} \uAC70\uC9D3\uC644\uB8CC \uC758\uC2EC ${result.suspicions.length}\uAC74`));
|
|
6277
|
-
for (const s of result.suspicions) console.log(
|
|
6613
|
+
for (const s of result.suspicions) console.log(chalk32.red(` \u2717 ${s.check}
|
|
6278
6614
|
\u21B3 ${s.reason}`));
|
|
6279
6615
|
}
|
|
6280
6616
|
if (result.gaps.length > 0) {
|
|
6281
|
-
console.log(
|
|
6617
|
+
console.log(chalk32.yellow.bold(`
|
|
6282
6618
|
\u26A0\uFE0F \uBBF8\uAC80\uC99D(unmapped) ${result.gaps.length}\uAC74 \u2014 \uAC8C\uC774\uD2B8\uB85C \uC790\uB3D9 \uD655\uC778 \uBD88\uAC00`));
|
|
6283
|
-
for (const g of result.gaps) console.log(
|
|
6619
|
+
for (const g of result.gaps) console.log(chalk32.yellow(` ? ${g.check}`));
|
|
6284
6620
|
}
|
|
6285
6621
|
if (result.checkedCount > 0 && result.suspicions.length === 0 && result.gaps.length === 0) {
|
|
6286
|
-
console.log(
|
|
6622
|
+
console.log(chalk32.green("\n \u2713 \uCCB4\uD06C\uB41C \uC644\uB8CC\uC870\uAC74\uC774 \uBAA8\uB450 \uAC8C\uC774\uD2B8 \uC99D\uAC70\uB85C \uB4B7\uBC1B\uCE68\uB428."));
|
|
6287
6623
|
}
|
|
6288
6624
|
console.log(`
|
|
6289
6625
|
\uC2E0\uB8B0\uB3C4: ${CONFIDENCE_LABEL[result.confidence]}`);
|
|
6290
|
-
console.log(
|
|
6626
|
+
console.log(chalk32.yellow(`
|
|
6291
6627
|
${result.disclaimer}`));
|
|
6292
6628
|
let mergeOk = false;
|
|
6293
6629
|
try {
|
|
6294
6630
|
const merged = { ...report, review: result };
|
|
6295
|
-
|
|
6631
|
+
writeFileSync13(jsonPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
6296
6632
|
mergeOk = true;
|
|
6297
|
-
console.log(
|
|
6633
|
+
console.log(chalk32.dim(` \u{1F4C4} \uD310\uC815 \uBCD1\uD569: ${REPORT_PATH_REL} (review \uC139\uC158)`));
|
|
6298
6634
|
} catch (e) {
|
|
6299
|
-
console.error(
|
|
6635
|
+
console.error(chalk32.red(` \u274C review \uD310\uC815 \uAE30\uB85D \uC2E4\uD328: ${e instanceof Error ? e.message : String(e)}`));
|
|
6300
6636
|
}
|
|
6301
6637
|
if (!mergeOk) {
|
|
6302
6638
|
process.exitCode = 1;
|
|
@@ -6317,8 +6653,8 @@ ${result.disclaimer}`));
|
|
|
6317
6653
|
cursorHint: "\uC644\uB8CC\uC870\uAC74 \uCC44\uC6CC\uC918"
|
|
6318
6654
|
});
|
|
6319
6655
|
} else if (result.suspicions.length > 0) {
|
|
6320
|
-
console.log(
|
|
6321
|
-
console.log(
|
|
6656
|
+
console.log(chalk32.dim("\n AI \uC7AC\uC9C8\uBB38 \uD504\uB86C\uD504\uD2B8:"));
|
|
6657
|
+
console.log(chalk32.cyan(result.reprompt.split("\n").map((l) => ` ${l}`).join("\n")));
|
|
6322
6658
|
printNextStep({
|
|
6323
6659
|
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:",
|
|
6324
6660
|
command: "vhk verify",
|
|
@@ -6332,8 +6668,8 @@ ${result.disclaimer}`));
|
|
|
6332
6668
|
cursorHint: "goal \uC644\uB8CC \uCC98\uB9AC\uD574\uC918"
|
|
6333
6669
|
});
|
|
6334
6670
|
} else {
|
|
6335
|
-
console.log(
|
|
6336
|
-
console.log(
|
|
6671
|
+
console.log(chalk32.dim("\n AI \uC7AC\uC9C8\uBB38 \uD504\uB86C\uD504\uD2B8:"));
|
|
6672
|
+
console.log(chalk32.cyan(result.reprompt.split("\n").map((l) => ` ${l}`).join("\n")));
|
|
6337
6673
|
printNextStep({
|
|
6338
6674
|
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:`,
|
|
6339
6675
|
command: "vhk verify",
|
|
@@ -6343,12 +6679,12 @@ ${result.disclaimer}`));
|
|
|
6343
6679
|
}
|
|
6344
6680
|
|
|
6345
6681
|
// src/commands/mission.ts
|
|
6346
|
-
import { existsSync as
|
|
6347
|
-
import { join as
|
|
6348
|
-
import
|
|
6682
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync13, writeFileSync as writeFileSync14, rmSync as rmSync4 } from "fs";
|
|
6683
|
+
import { join as join14 } from "path";
|
|
6684
|
+
import chalk33 from "chalk";
|
|
6349
6685
|
import inquirer12 from "inquirer";
|
|
6350
6686
|
import { simpleGit as simpleGit3 } from "simple-git";
|
|
6351
|
-
var MISSION_PATH_REL =
|
|
6687
|
+
var MISSION_PATH_REL = join14(".vhk", "mission.json");
|
|
6352
6688
|
var MISSION_SCHEMA_VERSION = 1;
|
|
6353
6689
|
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).";
|
|
6354
6690
|
function globToRegExp(glob) {
|
|
@@ -6396,8 +6732,8 @@ function checkMission(changedFiles, mission) {
|
|
|
6396
6732
|
return { violations, warnings, disclaimer: MISSION_DISCLAIMER };
|
|
6397
6733
|
}
|
|
6398
6734
|
function readMission(cwd = process.cwd()) {
|
|
6399
|
-
const p =
|
|
6400
|
-
if (!
|
|
6735
|
+
const p = join14(cwd, MISSION_PATH_REL);
|
|
6736
|
+
if (!existsSync19(p)) return null;
|
|
6401
6737
|
try {
|
|
6402
6738
|
const m = readJsonFile(p);
|
|
6403
6739
|
if (m && typeof m.objective === "string") return m;
|
|
@@ -6407,8 +6743,8 @@ function readMission(cwd = process.cwd()) {
|
|
|
6407
6743
|
}
|
|
6408
6744
|
}
|
|
6409
6745
|
function writeMission(cwd, mission) {
|
|
6410
|
-
|
|
6411
|
-
|
|
6746
|
+
mkdirSync13(join14(cwd, ".vhk"), { recursive: true });
|
|
6747
|
+
writeFileSync14(join14(cwd, MISSION_PATH_REL), JSON.stringify(mission, null, 2) + "\n", "utf-8");
|
|
6412
6748
|
}
|
|
6413
6749
|
async function collectChangedFiles(cwd) {
|
|
6414
6750
|
const status2 = await simpleGit3(cwd).status();
|
|
@@ -6429,7 +6765,7 @@ async function missionSet(opts = {}) {
|
|
|
6429
6765
|
objective = ans.obj.trim();
|
|
6430
6766
|
}
|
|
6431
6767
|
if (!objective) {
|
|
6432
|
-
console.error(
|
|
6768
|
+
console.error(chalk33.red(' \u274C objective \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. --objective "..." \uB85C \uC9C0\uC815\uD558\uC138\uC694(\uBE44\uB300\uD654\uD615).'));
|
|
6433
6769
|
process.exitCode = 1;
|
|
6434
6770
|
return;
|
|
6435
6771
|
}
|
|
@@ -6448,12 +6784,12 @@ async function missionSet(opts = {}) {
|
|
|
6448
6784
|
try {
|
|
6449
6785
|
writeMission(cwd, mission);
|
|
6450
6786
|
} catch (e) {
|
|
6451
|
-
console.error(
|
|
6787
|
+
console.error(chalk33.red(` \u274C mission.json \uAE30\uB85D \uC2E4\uD328: ${e instanceof Error ? e.message : String(e)}`));
|
|
6452
6788
|
process.exitCode = 1;
|
|
6453
6789
|
return;
|
|
6454
6790
|
}
|
|
6455
|
-
console.log(
|
|
6456
|
-
console.log(
|
|
6791
|
+
console.log(chalk33.bold("\n\u{1F3AF} \uBBF8\uC158 \uACC4\uC57D \uC800\uC7A5"));
|
|
6792
|
+
console.log(chalk33.dim(` \u{1F4C4} ${MISSION_PATH_REL}`));
|
|
6457
6793
|
console.log(` objective: ${mission.objective}`);
|
|
6458
6794
|
console.log(` scope: ${mission.scope.length ? mission.scope.join(", ") : "(\uC81C\uD55C \uC5C6\uC74C)"}`);
|
|
6459
6795
|
console.log(` forbidden: ${mission.forbidden.length ? mission.forbidden.join(", ") : "(\uC5C6\uC74C)"}`);
|
|
@@ -6463,64 +6799,64 @@ async function missionShow() {
|
|
|
6463
6799
|
const cwd = process.cwd();
|
|
6464
6800
|
const mission = readMission(cwd);
|
|
6465
6801
|
if (!mission) {
|
|
6466
|
-
console.error(
|
|
6802
|
+
console.error(chalk33.yellow(" \u26A0\uFE0F \uBBF8\uC158 \uACC4\uC57D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 (.vhk/mission.json)."));
|
|
6467
6803
|
printNextStep({ message: "\uBA3C\uC800 \uBBF8\uC158\uC744 \uC120\uC5B8\uD558\uC138\uC694:", command: 'vhk mission set --objective "..."', cursorHint: "\uBBF8\uC158 \uC815\uD574\uC918" });
|
|
6468
6804
|
process.exitCode = 1;
|
|
6469
6805
|
return;
|
|
6470
6806
|
}
|
|
6471
|
-
console.log(
|
|
6807
|
+
console.log(chalk33.bold("\n\u{1F3AF} \uD604\uC7AC \uBBF8\uC158 \uACC4\uC57D"));
|
|
6472
6808
|
console.log(` objective: ${mission.objective}`);
|
|
6473
6809
|
console.log(` scope: ${mission.scope.length ? mission.scope.join(", ") : "(\uC81C\uD55C \uC5C6\uC74C)"}`);
|
|
6474
6810
|
console.log(` forbidden: ${mission.forbidden.length ? mission.forbidden.join(", ") : "(\uC5C6\uC74C)"}`);
|
|
6475
|
-
console.log(
|
|
6811
|
+
console.log(chalk33.dim(` \uC0DD\uC131 ${mission.createdAt} \xB7 \uAC31\uC2E0 ${mission.updatedAt}`));
|
|
6476
6812
|
}
|
|
6477
6813
|
async function missionCheck() {
|
|
6478
6814
|
const cwd = process.cwd();
|
|
6479
6815
|
const mission = readMission(cwd);
|
|
6480
6816
|
if (!mission) {
|
|
6481
|
-
console.error(
|
|
6817
|
+
console.error(chalk33.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."));
|
|
6482
6818
|
process.exitCode = 1;
|
|
6483
6819
|
return;
|
|
6484
6820
|
}
|
|
6485
6821
|
const changed = await collectChangedFiles(cwd);
|
|
6486
6822
|
const result = checkMission(changed, mission);
|
|
6487
|
-
console.log(
|
|
6488
|
-
console.log(
|
|
6823
|
+
console.log(chalk33.bold("\n\u{1F3AF} \uBBF8\uC158 \uACC4\uC57D \uAC80\uC99D (mission check)"));
|
|
6824
|
+
console.log(chalk33.dim(` objective: ${mission.objective} \xB7 \uBCC0\uACBD \uD30C\uC77C ${changed.length}\uAC1C`));
|
|
6489
6825
|
if (result.violations.length > 0) {
|
|
6490
|
-
console.log(
|
|
6826
|
+
console.log(chalk33.red.bold(`
|
|
6491
6827
|
\u{1F6AB} forbidden \uC704\uBC18 ${result.violations.length}\uAC74`));
|
|
6492
|
-
for (const v of result.violations) console.log(
|
|
6828
|
+
for (const v of result.violations) console.log(chalk33.red(` \u2717 ${v.file} (\uAE08\uC9C0: ${v.pattern})`));
|
|
6493
6829
|
}
|
|
6494
6830
|
if (result.warnings.length > 0) {
|
|
6495
|
-
console.log(
|
|
6831
|
+
console.log(chalk33.yellow.bold(`
|
|
6496
6832
|
\u26A0\uFE0F scope \uBC16 \uBCC0\uACBD ${result.warnings.length}\uAC74 (\uACBD\uACE0)`));
|
|
6497
|
-
for (const w of result.warnings) console.log(
|
|
6833
|
+
for (const w of result.warnings) console.log(chalk33.yellow(` ? ${w.file}`));
|
|
6498
6834
|
}
|
|
6499
6835
|
if (result.violations.length === 0 && result.warnings.length === 0) {
|
|
6500
|
-
console.log(
|
|
6836
|
+
console.log(chalk33.green("\n \u2713 \uBCC0\uACBD\uC774 \uACC4\uC57D(scope/forbidden) \uC548\uC785\uB2C8\uB2E4."));
|
|
6501
6837
|
}
|
|
6502
|
-
console.log(
|
|
6838
|
+
console.log(chalk33.yellow(`
|
|
6503
6839
|
${result.disclaimer}`));
|
|
6504
6840
|
process.exitCode = result.violations.length > 0 ? 1 : 0;
|
|
6505
6841
|
}
|
|
6506
6842
|
async function missionClear() {
|
|
6507
6843
|
const cwd = process.cwd();
|
|
6508
|
-
const p =
|
|
6509
|
-
if (!
|
|
6510
|
-
console.log(
|
|
6844
|
+
const p = join14(cwd, MISSION_PATH_REL);
|
|
6845
|
+
if (!existsSync19(p)) {
|
|
6846
|
+
console.log(chalk33.dim(" \uBBF8\uC158 \uACC4\uC57D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uC9C0\uC6B8 \uAC83 \uC5C6\uC74C."));
|
|
6511
6847
|
return;
|
|
6512
6848
|
}
|
|
6513
6849
|
try {
|
|
6514
6850
|
rmSync4(p);
|
|
6515
|
-
console.log(
|
|
6851
|
+
console.log(chalk33.green(" \u2705 \uBBF8\uC158 \uACC4\uC57D \uC0AD\uC81C\uB428 (.vhk/mission.json)."));
|
|
6516
6852
|
} catch (e) {
|
|
6517
|
-
console.error(
|
|
6853
|
+
console.error(chalk33.red(` \u274C \uC0AD\uC81C \uC2E4\uD328: ${e instanceof Error ? e.message : String(e)}`));
|
|
6518
6854
|
process.exitCode = 1;
|
|
6519
6855
|
}
|
|
6520
6856
|
}
|
|
6521
6857
|
|
|
6522
6858
|
// src/commands/pattern.ts
|
|
6523
|
-
import
|
|
6859
|
+
import chalk34 from "chalk";
|
|
6524
6860
|
var MIN_TAG_FREQ = 3;
|
|
6525
6861
|
var STOPWORDS = /* @__PURE__ */ new Set(["the", "a", "an", "is", "are", "and", "or", "in", "on", "at", "to", "for", "of", "with", "it", "was", "be"]);
|
|
6526
6862
|
function tokenize(text) {
|
|
@@ -6638,14 +6974,14 @@ function reconcilePatterns(patterns, candidates, now) {
|
|
|
6638
6974
|
async function patternDetect(opts = {}) {
|
|
6639
6975
|
const minFreq = opts.min !== void 0 ? parseInt(opts.min, 10) : MIN_TAG_FREQ;
|
|
6640
6976
|
if (!Number.isFinite(minFreq) || minFreq < 1) {
|
|
6641
|
-
console.log(
|
|
6977
|
+
console.log(chalk34.red("\u274C --min \uC740 1 \uC774\uC0C1\uC758 \uC815\uC218\uC5EC\uC57C \uD569\uB2C8\uB2E4."));
|
|
6642
6978
|
process.exitCode = 1;
|
|
6643
6979
|
return;
|
|
6644
6980
|
}
|
|
6645
6981
|
const cwd = process.cwd();
|
|
6646
6982
|
const loaded = loadForMutation(cwd);
|
|
6647
6983
|
if (!loaded.ok) {
|
|
6648
|
-
console.log(
|
|
6984
|
+
console.log(chalk34.red("\u274C memory.json \uC190\uC0C1 \uC758\uC2EC \u2014 \uAC10\uC9C0 \uC911\uB2E8 (\uC6D0\uBCF8 \uBCF4\uC874). \uBC31\uC5C5 \uD655\uC778 \uD6C4 \uC7AC\uC2DC\uB3C4\uD558\uC138\uC694."));
|
|
6649
6985
|
process.exitCode = 1;
|
|
6650
6986
|
return;
|
|
6651
6987
|
}
|
|
@@ -6661,26 +6997,26 @@ async function patternDetect(opts = {}) {
|
|
|
6661
6997
|
console.log(JSON.stringify(active2, null, 2));
|
|
6662
6998
|
return;
|
|
6663
6999
|
}
|
|
6664
|
-
console.log(
|
|
6665
|
-
console.log(
|
|
6666
|
-
console.log(
|
|
6667
|
-
console.log(
|
|
7000
|
+
console.log(chalk34.bold("\n\u{1F50D} " + t("pattern.detectTitle")));
|
|
7001
|
+
console.log(chalk34.gray("\u2500".repeat(40)));
|
|
7002
|
+
console.log(chalk34.dim(` \uC784\uACC4: ${minFreq}\uD68C \uC774\uC0C1 \xB7 active failures+successes \uC785\uB825`));
|
|
7003
|
+
console.log(chalk34.dim(` \uD6C4\uBCF4: ${candidates.length}\uAC1C \uAC10\uC9C0 (\uCD94\uAC00 ${added} / \uAC31\uC2E0 ${updated})`));
|
|
6668
7004
|
const active = mem.patterns.filter((p) => p.status !== "archived");
|
|
6669
7005
|
if (active.length === 0) {
|
|
6670
|
-
console.log(
|
|
6671
|
-
console.log(
|
|
6672
|
-
console.log(
|
|
7006
|
+
console.log(chalk34.yellow("\n\u{1F4ED} \uC784\uACC4 \uC774\uC0C1 \uBC18\uBCF5 \uD328\uD134\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
7007
|
+
console.log(chalk34.gray(` failures/successes \uAC00 ${minFreq}\uAC1C \uC774\uC0C1 \uC313\uC774\uBA74 \uAC10\uC9C0\uB429\uB2C8\uB2E4.`));
|
|
7008
|
+
console.log(chalk34.gray(" --min N \uC73C\uB85C \uC784\uACC4\uB97C \uB0AE\uCD9C \uC218 \uC788\uC2B5\uB2C8\uB2E4."));
|
|
6673
7009
|
return;
|
|
6674
7010
|
}
|
|
6675
|
-
console.log(
|
|
7011
|
+
console.log(chalk34.cyan(`
|
|
6676
7012
|
\uD328\uD134 \uD6C4\uBCF4 ${active.length}\uAC1C:
|
|
6677
7013
|
`));
|
|
6678
7014
|
for (const p of active.slice(0, 20)) {
|
|
6679
7015
|
const icon = p.kind === "avoid" ? "\u26A0\uFE0F " : "\u2705";
|
|
6680
7016
|
console.log(` [${p.id}] ${icon} (${p.kind}/${p.axis}) "${p.signal}" \u2014 ${p.count}\uAC74`);
|
|
6681
7017
|
const preview = p.sources.slice(0, 5).join(", ") + (p.sources.length > 5 ? ` \uC678 ${p.sources.length - 5}\uAC74` : "");
|
|
6682
|
-
console.log(
|
|
6683
|
-
console.log(
|
|
7018
|
+
console.log(chalk34.dim(` \uADFC\uAC70: ${preview}`));
|
|
7019
|
+
console.log(chalk34.dim(` ${p.summary}`));
|
|
6684
7020
|
}
|
|
6685
7021
|
printNextStep({
|
|
6686
7022
|
message: `\uD328\uD134 \uAC10\uC9C0 \uC644\uB8CC! ${active.length}\uAC1C \uD6C4\uBCF4.`,
|
|
@@ -6690,8 +7026,8 @@ async function patternDetect(opts = {}) {
|
|
|
6690
7026
|
});
|
|
6691
7027
|
}
|
|
6692
7028
|
async function patternList(opts = {}) {
|
|
6693
|
-
console.log(
|
|
6694
|
-
console.log(
|
|
7029
|
+
console.log(chalk34.bold("\n\u{1F50D} " + t("pattern.listTitle")));
|
|
7030
|
+
console.log(chalk34.gray("\u2500".repeat(40)));
|
|
6695
7031
|
const mem = readMemory(process.cwd());
|
|
6696
7032
|
let patterns = mem.patterns;
|
|
6697
7033
|
if (!opts.all) patterns = patterns.filter((p) => p.status !== "archived");
|
|
@@ -6703,51 +7039,51 @@ async function patternList(opts = {}) {
|
|
|
6703
7039
|
return;
|
|
6704
7040
|
}
|
|
6705
7041
|
if (patterns.length === 0) {
|
|
6706
|
-
console.log(
|
|
6707
|
-
console.log(
|
|
7042
|
+
console.log(chalk34.yellow("\n\u{1F4ED} \uD45C\uC2DC\uD560 \uD328\uD134\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
7043
|
+
console.log(chalk34.gray(" vhk pattern detect \uB85C \uAC10\uC9C0 \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694."));
|
|
6708
7044
|
return;
|
|
6709
7045
|
}
|
|
6710
|
-
console.log(
|
|
7046
|
+
console.log(chalk34.cyan(`
|
|
6711
7047
|
${patterns.length}\uAC1C${opts.all ? " (\uBCF4\uAD00 \uD3EC\uD568)" : " (\uD65C\uC131)"}:
|
|
6712
7048
|
`));
|
|
6713
7049
|
for (const p of patterns) {
|
|
6714
7050
|
const icon = p.kind === "avoid" ? "\u26A0\uFE0F " : "\u2705";
|
|
6715
7051
|
const archived = p.status === "archived" ? "\u{1F4E6} " : "";
|
|
6716
7052
|
console.log(` [${p.id}] ${archived}${icon} (${p.kind}/${p.axis}) "${p.signal}" \u2014 ${p.count}\uAC74`);
|
|
6717
|
-
if (p.summary) console.log(
|
|
6718
|
-
if (p.tags?.length) console.log(
|
|
7053
|
+
if (p.summary) console.log(chalk34.dim(` ${p.summary}`));
|
|
7054
|
+
if (p.tags?.length) console.log(chalk34.blue(` \u{1F3F7}\uFE0F ${p.tags.join(", ")}`));
|
|
6719
7055
|
}
|
|
6720
7056
|
}
|
|
6721
7057
|
async function patternDismiss(idStr) {
|
|
6722
7058
|
if (!idStr?.trim()) {
|
|
6723
|
-
console.log(
|
|
7059
|
+
console.log(chalk34.red("\u274C \uD328\uD134 id \uB97C \uC785\uB825\uD574\uC8FC\uC138\uC694. \uC608: vhk pattern dismiss p1"));
|
|
6724
7060
|
process.exitCode = 1;
|
|
6725
7061
|
return;
|
|
6726
7062
|
}
|
|
6727
7063
|
const cwd = process.cwd();
|
|
6728
7064
|
const loaded = loadForMutation(cwd);
|
|
6729
7065
|
if (!loaded.ok) {
|
|
6730
|
-
console.log(
|
|
7066
|
+
console.log(chalk34.red("\u274C memory.json \uC190\uC0C1 \uC758\uC2EC \u2014 dismiss \uC911\uB2E8 (\uC6D0\uBCF8 \uBCF4\uC874)."));
|
|
6731
7067
|
process.exitCode = 1;
|
|
6732
7068
|
return;
|
|
6733
7069
|
}
|
|
6734
7070
|
const mem = loaded.mem;
|
|
6735
7071
|
const pattern = mem.patterns.find((p) => p.id === idStr.trim());
|
|
6736
7072
|
if (!pattern) {
|
|
6737
|
-
console.log(
|
|
6738
|
-
console.log(
|
|
7073
|
+
console.log(chalk34.red(`\u274C \uD328\uD134 '${idStr}' \uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`));
|
|
7074
|
+
console.log(chalk34.gray(" vhk pattern list --all \uB85C \uBAA9\uB85D \uD655\uC778."));
|
|
6739
7075
|
process.exitCode = 1;
|
|
6740
7076
|
return;
|
|
6741
7077
|
}
|
|
6742
7078
|
if (pattern.status === "archived") {
|
|
6743
|
-
console.log(
|
|
7079
|
+
console.log(chalk34.dim(` \uC774\uBBF8 \uBCF4\uAD00\uB41C \uD328\uD134\uC785\uB2C8\uB2E4 \u2014 \uBCC0\uACBD \uC5C6\uC74C: ${pattern.id}`));
|
|
6744
7080
|
return;
|
|
6745
7081
|
}
|
|
6746
7082
|
pattern.status = "archived";
|
|
6747
7083
|
writeMemory(cwd, mem);
|
|
6748
|
-
console.log(
|
|
7084
|
+
console.log(chalk34.green(`
|
|
6749
7085
|
\u{1F4E6} \uD328\uD134 dismiss(\uBCF4\uAD00)\uB428: [${pattern.id}] ${pattern.summary ?? pattern.signal}`));
|
|
6750
|
-
console.log(
|
|
7086
|
+
console.log(chalk34.dim(" \uC624\uD0D0\uC73C\uB85C \uD310\uB2E8. detect \uC7AC\uC2E4\uD589 \uC2DC \uC7AC\uC81C\uC548 \uC548 \uB428."));
|
|
6751
7087
|
printNextStep({
|
|
6752
7088
|
message: "\uD328\uD134 dismiss \uC644\uB8CC!",
|
|
6753
7089
|
command: "vhk pattern list",
|
|
@@ -6756,11 +7092,11 @@ async function patternDismiss(idStr) {
|
|
|
6756
7092
|
}
|
|
6757
7093
|
|
|
6758
7094
|
// src/commands/evolve.ts
|
|
6759
|
-
import { existsSync as
|
|
6760
|
-
import { join as
|
|
6761
|
-
import
|
|
7095
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync14, writeFileSync as writeFileSync15, readFileSync as readFileSync8, copyFileSync as copyFileSync2, renameSync as renameSync2, rmSync as rmSync5 } from "fs";
|
|
7096
|
+
import { join as join15 } from "path";
|
|
7097
|
+
import chalk35 from "chalk";
|
|
6762
7098
|
import inquirer13 from "inquirer";
|
|
6763
|
-
var QUEUE_PATH_REL =
|
|
7099
|
+
var QUEUE_PATH_REL = join15(".vhk", "evolve", "queue.json");
|
|
6764
7100
|
function buildDraft(p) {
|
|
6765
7101
|
const axisLabel = p.axis === "tag" ? `\uD0DC\uADF8 '${p.signal}'` : `\uD0A4\uC6CC\uB4DC '${p.signal}'`;
|
|
6766
7102
|
const countDesc = `${p.count}\uAC74 \uBC18\uBCF5`;
|
|
@@ -6804,10 +7140,10 @@ function stripBomStr(s) {
|
|
|
6804
7140
|
return s.charCodeAt(0) === 65279 ? s.slice(1) : s;
|
|
6805
7141
|
}
|
|
6806
7142
|
function readQueue(cwd) {
|
|
6807
|
-
const p =
|
|
6808
|
-
if (!
|
|
7143
|
+
const p = join15(cwd, QUEUE_PATH_REL);
|
|
7144
|
+
if (!existsSync20(p)) return { version: 1, items: [] };
|
|
6809
7145
|
try {
|
|
6810
|
-
const raw = stripBomStr(
|
|
7146
|
+
const raw = stripBomStr(readFileSync8(p, "utf-8"));
|
|
6811
7147
|
const parsed = JSON.parse(raw);
|
|
6812
7148
|
if (!parsed || !Array.isArray(parsed.items)) return { version: 1, items: [] };
|
|
6813
7149
|
return parsed;
|
|
@@ -6816,10 +7152,10 @@ function readQueue(cwd) {
|
|
|
6816
7152
|
}
|
|
6817
7153
|
}
|
|
6818
7154
|
function writeQueue(cwd, queue) {
|
|
6819
|
-
const p =
|
|
6820
|
-
|
|
7155
|
+
const p = join15(cwd, QUEUE_PATH_REL);
|
|
7156
|
+
mkdirSync14(join15(cwd, ".vhk", "evolve"), { recursive: true });
|
|
6821
7157
|
const tmpPath = p + ".tmp";
|
|
6822
|
-
|
|
7158
|
+
writeFileSync15(tmpPath, JSON.stringify(queue, null, 2) + "\n", "utf-8");
|
|
6823
7159
|
try {
|
|
6824
7160
|
renameSync2(tmpPath, p);
|
|
6825
7161
|
} catch (err) {
|
|
@@ -6848,8 +7184,8 @@ function checkApplyRef(pattern, queueItems) {
|
|
|
6848
7184
|
}
|
|
6849
7185
|
async function evolveSuggest(opts = {}) {
|
|
6850
7186
|
const cwd = process.cwd();
|
|
6851
|
-
if (!
|
|
6852
|
-
console.log(
|
|
7187
|
+
if (!existsSync20(join15(cwd, "RULES.md"))) {
|
|
7188
|
+
console.log(chalk35.yellow("\n\u26A0\uFE0F " + t("evolve.noRules")));
|
|
6853
7189
|
process.exitCode = 1;
|
|
6854
7190
|
return;
|
|
6855
7191
|
}
|
|
@@ -6860,10 +7196,10 @@ async function evolveSuggest(opts = {}) {
|
|
|
6860
7196
|
if (newItems.length === 0 && !opts.json) {
|
|
6861
7197
|
const activeAvoid = patterns.filter((p) => p.kind === "avoid" && p.status === "active");
|
|
6862
7198
|
if (activeAvoid.length === 0) {
|
|
6863
|
-
console.log(
|
|
7199
|
+
console.log(chalk35.yellow("\n\u{1F4ED} " + t("evolve.noPatterns")));
|
|
6864
7200
|
return;
|
|
6865
7201
|
}
|
|
6866
|
-
console.log(
|
|
7202
|
+
console.log(chalk35.dim("\n " + t("evolve.allSuggested")));
|
|
6867
7203
|
return;
|
|
6868
7204
|
}
|
|
6869
7205
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -6876,16 +7212,16 @@ async function evolveSuggest(opts = {}) {
|
|
|
6876
7212
|
console.log(JSON.stringify(pending2, null, 2));
|
|
6877
7213
|
return;
|
|
6878
7214
|
}
|
|
6879
|
-
console.log(
|
|
6880
|
-
console.log(
|
|
6881
|
-
console.log(
|
|
7215
|
+
console.log(chalk35.bold("\n\u{1F504} " + t("evolve.suggestTitle")));
|
|
7216
|
+
console.log(chalk35.gray("\u2500".repeat(40)));
|
|
7217
|
+
console.log(chalk35.dim(" " + t("evolve.newCandidates", newItems.length)));
|
|
6882
7218
|
const pending = queue.items.filter((i) => i.status === "pending");
|
|
6883
|
-
console.log(
|
|
7219
|
+
console.log(chalk35.cyan(`
|
|
6884
7220
|
\uD6C4\uBCF4 ${pending.length}\uAC1C:
|
|
6885
7221
|
`));
|
|
6886
7222
|
for (const item of pending) {
|
|
6887
7223
|
console.log(` [${item.id}] (${item.status}) \uD328\uD134 ${item.patternId} \u2192 rule`);
|
|
6888
|
-
console.log(
|
|
7224
|
+
console.log(chalk35.dim(` \uCD08\uC548: ${item.draft}`));
|
|
6889
7225
|
}
|
|
6890
7226
|
printNextStep({
|
|
6891
7227
|
message: `\uC9C4\uD654 \uD6C4\uBCF4 ${pending.length}\uAC1C \uC0DD\uC131\uB428!`,
|
|
@@ -6906,11 +7242,11 @@ async function evolveList(opts = {}) {
|
|
|
6906
7242
|
console.log(JSON.stringify(items, null, 2));
|
|
6907
7243
|
return;
|
|
6908
7244
|
}
|
|
6909
|
-
console.log(
|
|
6910
|
-
console.log(
|
|
7245
|
+
console.log(chalk35.bold("\n\u{1F504} " + t("evolve.listTitle")));
|
|
7246
|
+
console.log(chalk35.gray("\u2500".repeat(40)));
|
|
6911
7247
|
if (items.length === 0) {
|
|
6912
|
-
console.log(
|
|
6913
|
-
console.log(
|
|
7248
|
+
console.log(chalk35.yellow("\n\u{1F4ED} " + t("evolve.noQueue")));
|
|
7249
|
+
console.log(chalk35.gray(" " + t("evolve.suggestHint")));
|
|
6914
7250
|
return;
|
|
6915
7251
|
}
|
|
6916
7252
|
const STATUS_ICON3 = {
|
|
@@ -6918,68 +7254,68 @@ async function evolveList(opts = {}) {
|
|
|
6918
7254
|
rejected: "\u274C",
|
|
6919
7255
|
applied: "\u2705"
|
|
6920
7256
|
};
|
|
6921
|
-
console.log(
|
|
7257
|
+
console.log(chalk35.cyan(`
|
|
6922
7258
|
${items.length}\uAC1C:
|
|
6923
7259
|
`));
|
|
6924
7260
|
for (const item of items) {
|
|
6925
7261
|
console.log(` [${item.id}] ${STATUS_ICON3[item.status]} (${item.status}) \u2192 ${item.draft}`);
|
|
6926
|
-
if (item.appliedAt) console.log(
|
|
7262
|
+
if (item.appliedAt) console.log(chalk35.dim(` \uBC18\uC601: ${item.appliedAt}`));
|
|
6927
7263
|
}
|
|
6928
7264
|
}
|
|
6929
7265
|
async function evolveApply(idStr) {
|
|
6930
7266
|
if (!ensureInteractive("apply\uB294 TTY \uD655\uC778\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. \uD130\uBBF8\uB110\uC5D0\uC11C \uC9C1\uC811 \uC2E4\uD589\uD558\uC138\uC694.")) return;
|
|
6931
7267
|
const cwd = process.cwd();
|
|
6932
|
-
const rulesPath =
|
|
6933
|
-
if (!
|
|
6934
|
-
console.log(
|
|
7268
|
+
const rulesPath = join15(cwd, "RULES.md");
|
|
7269
|
+
if (!existsSync20(rulesPath)) {
|
|
7270
|
+
console.log(chalk35.red("\n\u274C " + t("evolve.noRules")));
|
|
6935
7271
|
process.exitCode = 1;
|
|
6936
7272
|
return;
|
|
6937
7273
|
}
|
|
6938
7274
|
const queue = readQueue(cwd);
|
|
6939
7275
|
const item = queue.items.find((i) => i.id === idStr?.trim());
|
|
6940
7276
|
if (!item) {
|
|
6941
|
-
console.log(
|
|
7277
|
+
console.log(chalk35.red("\n\u274C " + t("evolve.notFound", idStr ?? "")));
|
|
6942
7278
|
process.exitCode = 1;
|
|
6943
7279
|
return;
|
|
6944
7280
|
}
|
|
6945
7281
|
if (item.status === "applied") {
|
|
6946
|
-
console.log(
|
|
7282
|
+
console.log(chalk35.yellow("\n\u26A0\uFE0F " + t("evolve.alreadyApplied")));
|
|
6947
7283
|
process.exitCode = 1;
|
|
6948
7284
|
return;
|
|
6949
7285
|
}
|
|
6950
7286
|
const hasUnresolved = queue.items.some((i) => i.status === "applied");
|
|
6951
7287
|
if (hasUnresolved) {
|
|
6952
|
-
console.log(
|
|
7288
|
+
console.log(chalk35.red("\n\u274C " + t("evolve.pendingApplyExists")));
|
|
6953
7289
|
process.exitCode = 1;
|
|
6954
7290
|
return;
|
|
6955
7291
|
}
|
|
6956
7292
|
const memLoaded = loadForMutation(cwd);
|
|
6957
7293
|
if (!memLoaded.ok) {
|
|
6958
|
-
console.log(
|
|
7294
|
+
console.log(chalk35.red("\n\u274C memory.json \uC190\uC0C1 \uC758\uC2EC \u2014 apply \uC911\uB2E8 (\uC6D0\uBCF8 \uBCF4\uC874)."));
|
|
6959
7295
|
process.exitCode = 1;
|
|
6960
7296
|
return;
|
|
6961
7297
|
}
|
|
6962
7298
|
const srcPattern = memLoaded.mem.patterns.find((p) => p.id === item.patternId);
|
|
6963
7299
|
const refResult = checkApplyRef(srcPattern, queue.items);
|
|
6964
7300
|
if (refResult === "dismissed") {
|
|
6965
|
-
console.log(
|
|
7301
|
+
console.log(chalk35.red("\n\u274C " + t("evolve.dismissed")));
|
|
6966
7302
|
process.exitCode = 1;
|
|
6967
7303
|
return;
|
|
6968
7304
|
}
|
|
6969
7305
|
if (refResult === "already-applied") {
|
|
6970
|
-
console.log(
|
|
7306
|
+
console.log(chalk35.red("\n\u274C " + t("evolve.alreadyAppliedPattern")));
|
|
6971
7307
|
process.exitCode = 1;
|
|
6972
7308
|
return;
|
|
6973
7309
|
}
|
|
6974
|
-
const rulesContent =
|
|
7310
|
+
const rulesContent = readFileSync8(rulesPath, "utf-8");
|
|
6975
7311
|
if (isDuplicateRule(rulesContent, item.draft)) {
|
|
6976
|
-
console.log(
|
|
7312
|
+
console.log(chalk35.yellow("\n\u26A0\uFE0F " + t("evolve.duplicateRule", item.draft)));
|
|
6977
7313
|
return;
|
|
6978
7314
|
}
|
|
6979
|
-
console.log(
|
|
6980
|
-
console.log(
|
|
6981
|
-
console.log(
|
|
6982
|
-
console.log(
|
|
7315
|
+
console.log(chalk35.bold("\n\u{1F504} " + t("evolve.applyTitle")));
|
|
7316
|
+
console.log(chalk35.gray("\u2500".repeat(40)));
|
|
7317
|
+
console.log(chalk35.cyan("\n\uCD94\uAC00\uB420 \uB8F0 \uCD08\uC548:"));
|
|
7318
|
+
console.log(chalk35.white(` ${item.draft}`));
|
|
6983
7319
|
const { editedDraft } = await inquirer13.prompt([{
|
|
6984
7320
|
type: "input",
|
|
6985
7321
|
name: "editedDraft",
|
|
@@ -6994,22 +7330,22 @@ async function evolveApply(idStr) {
|
|
|
6994
7330
|
default: false
|
|
6995
7331
|
}]);
|
|
6996
7332
|
if (!confirmed) {
|
|
6997
|
-
console.log(
|
|
7333
|
+
console.log(chalk35.dim(" \uCDE8\uC18C\uB428."));
|
|
6998
7334
|
return;
|
|
6999
7335
|
}
|
|
7000
7336
|
const backupPath = rulesPath + ".bak";
|
|
7001
7337
|
copyFileSync2(rulesPath, backupPath);
|
|
7002
7338
|
const appendContent = "\n" + editedDraft + "\n";
|
|
7003
7339
|
try {
|
|
7004
|
-
|
|
7340
|
+
writeFileSync15(rulesPath, rulesContent + appendContent, "utf-8");
|
|
7005
7341
|
await sync({ yes: true });
|
|
7006
7342
|
} catch (err) {
|
|
7007
7343
|
try {
|
|
7008
7344
|
copyFileSync2(backupPath, rulesPath);
|
|
7009
7345
|
} catch {
|
|
7010
7346
|
}
|
|
7011
|
-
console.error(
|
|
7012
|
-
console.error(
|
|
7347
|
+
console.error(chalk35.red("\n\u274C RULES.md \uBC18\uC601 \uC911 \uC624\uB958 \u2014 \uC6D0\uBCF8 \uBCF5\uC6D0\uB428. \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694."));
|
|
7348
|
+
console.error(chalk35.dim(` ${err instanceof Error ? err.message : String(err)}`));
|
|
7013
7349
|
process.exitCode = 1;
|
|
7014
7350
|
return;
|
|
7015
7351
|
}
|
|
@@ -7025,12 +7361,12 @@ async function evolveApply(idStr) {
|
|
|
7025
7361
|
p.status = "archived";
|
|
7026
7362
|
writeMemory(cwd, memLoaded.mem);
|
|
7027
7363
|
} else {
|
|
7028
|
-
console.error(
|
|
7364
|
+
console.error(chalk35.yellow(" \u26A0\uFE0F \uC18C\uC2A4 \uD328\uD134 archived \uCC98\uB9AC \uC2E4\uD328 (memory.json \uC190\uC0C1 \uC758\uC2EC). \uB8F0\uC740 \uBC18\uC601\uB410\uC2B5\uB2C8\uB2E4."));
|
|
7029
7365
|
}
|
|
7030
7366
|
}
|
|
7031
|
-
console.log(
|
|
7367
|
+
console.log(chalk35.green(`
|
|
7032
7368
|
\u2705 \uB8F0 \uBC18\uC601 \uC644\uB8CC! [${item.id}]`));
|
|
7033
|
-
console.log(
|
|
7369
|
+
console.log(chalk35.dim(" RULES.md\uC5D0 \uCD94\uAC00 + vhk sync \uC7AC\uC0DD\uC131\uB428"));
|
|
7034
7370
|
printNextStep({
|
|
7035
7371
|
message: "\uB8F0 \uBC18\uC601 \uC644\uB8CC!",
|
|
7036
7372
|
command: "vhk evolve list --status applied",
|
|
@@ -7043,25 +7379,25 @@ async function evolveReject(idStr) {
|
|
|
7043
7379
|
const queue = readQueue(cwd);
|
|
7044
7380
|
const item = queue.items.find((i) => i.id === idStr?.trim());
|
|
7045
7381
|
if (!item) {
|
|
7046
|
-
console.log(
|
|
7382
|
+
console.log(chalk35.red("\n\u274C " + t("evolve.notFound", idStr ?? "")));
|
|
7047
7383
|
process.exitCode = 1;
|
|
7048
7384
|
return;
|
|
7049
7385
|
}
|
|
7050
7386
|
if (item.status === "rejected") {
|
|
7051
|
-
console.log(
|
|
7387
|
+
console.log(chalk35.dim(` \uC774\uBBF8 \uAE30\uAC01\uB41C \uD6C4\uBCF4\uC785\uB2C8\uB2E4 \u2014 \uBCC0\uACBD \uC5C6\uC74C: ${item.id}`));
|
|
7052
7388
|
return;
|
|
7053
7389
|
}
|
|
7054
7390
|
if (item.status === "applied") {
|
|
7055
|
-
console.log(
|
|
7391
|
+
console.log(chalk35.red(`
|
|
7056
7392
|
\u274C \uC774\uBBF8 \uBC18\uC601\uB41C \uD56D\uBAA9\uC740 \uAE30\uAC01\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 vhk evolve undo \uB85C \uB418\uB3CC\uB9AC\uC138\uC694: ${item.id}`));
|
|
7057
7393
|
process.exitCode = 1;
|
|
7058
7394
|
return;
|
|
7059
7395
|
}
|
|
7060
7396
|
item.status = "rejected";
|
|
7061
7397
|
writeQueue(cwd, queue);
|
|
7062
|
-
console.log(
|
|
7398
|
+
console.log(chalk35.green(`
|
|
7063
7399
|
\u274C \uD6C4\uBCF4 \uAE30\uAC01\uB428: [${item.id}] ${item.draft}`));
|
|
7064
|
-
console.log(
|
|
7400
|
+
console.log(chalk35.dim(" (A1: \uB2E4\uC74C suggest\uC5D0\uC11C \uC7AC\uC81C\uC548 \uC548 \uB428)"));
|
|
7065
7401
|
printNextStep({
|
|
7066
7402
|
message: "\uAE30\uAC01 \uC644\uB8CC!",
|
|
7067
7403
|
command: "vhk evolve list",
|
|
@@ -7074,19 +7410,19 @@ async function evolveUndo() {
|
|
|
7074
7410
|
const queue = readQueue(cwd);
|
|
7075
7411
|
const applied = queue.items.filter((i) => i.status === "applied");
|
|
7076
7412
|
if (applied.length === 0) {
|
|
7077
|
-
console.log(
|
|
7413
|
+
console.log(chalk35.yellow("\n\u{1F4ED} " + t("evolve.noAppliedToUndo")));
|
|
7078
7414
|
return;
|
|
7079
7415
|
}
|
|
7080
7416
|
const last = applied.sort(
|
|
7081
7417
|
(a, b) => (b.appliedAt ?? "").localeCompare(a.appliedAt ?? "")
|
|
7082
7418
|
)[0];
|
|
7083
|
-
if (!last.rulesBackupPath || !
|
|
7084
|
-
console.log(
|
|
7419
|
+
if (!last.rulesBackupPath || !existsSync20(last.rulesBackupPath)) {
|
|
7420
|
+
console.log(chalk35.red("\n\u274C " + t("evolve.noBackup")));
|
|
7085
7421
|
process.exitCode = 1;
|
|
7086
7422
|
return;
|
|
7087
7423
|
}
|
|
7088
|
-
console.log(
|
|
7089
|
-
console.log(
|
|
7424
|
+
console.log(chalk35.bold("\n\u{1F504} " + t("evolve.undoTitle")));
|
|
7425
|
+
console.log(chalk35.dim(` \uB418\uB3CC\uB9B4 \uD56D\uBAA9: [${last.id}] ${last.draft}`));
|
|
7090
7426
|
const { confirmed } = await inquirer13.prompt([{
|
|
7091
7427
|
type: "confirm",
|
|
7092
7428
|
name: "confirmed",
|
|
@@ -7094,16 +7430,16 @@ async function evolveUndo() {
|
|
|
7094
7430
|
default: false
|
|
7095
7431
|
}]);
|
|
7096
7432
|
if (!confirmed) {
|
|
7097
|
-
console.log(
|
|
7433
|
+
console.log(chalk35.dim(" \uCDE8\uC18C\uB428."));
|
|
7098
7434
|
return;
|
|
7099
7435
|
}
|
|
7100
|
-
copyFileSync2(last.rulesBackupPath,
|
|
7436
|
+
copyFileSync2(last.rulesBackupPath, join15(cwd, "RULES.md"));
|
|
7101
7437
|
try {
|
|
7102
7438
|
await sync({ yes: true });
|
|
7103
7439
|
} catch (err) {
|
|
7104
|
-
console.error(
|
|
7105
|
-
console.error(
|
|
7106
|
-
console.error(
|
|
7440
|
+
console.error(chalk35.red("\n\u274C sync \uC7AC\uC2E4\uD589 \uC911 \uC624\uB958. RULES.md\uB294 \uBCF5\uC6D0\uB410\uC73C\uB098 .cursorrules \uB4F1 \uC7AC\uC0DD\uC131 \uC2E4\uD328."));
|
|
7441
|
+
console.error(chalk35.dim(` ${err instanceof Error ? err.message : String(err)}`));
|
|
7442
|
+
console.error(chalk35.dim(" \uC218\uB3D9\uC73C\uB85C `vhk sync` \uC2E4\uD589\uD558\uC138\uC694."));
|
|
7107
7443
|
}
|
|
7108
7444
|
last.status = "pending";
|
|
7109
7445
|
delete last.appliedAt;
|
|
@@ -7117,7 +7453,7 @@ async function evolveUndo() {
|
|
|
7117
7453
|
writeMemory(cwd, memLoaded.mem);
|
|
7118
7454
|
}
|
|
7119
7455
|
}
|
|
7120
|
-
console.log(
|
|
7456
|
+
console.log(chalk35.green("\n\u2705 \uB418\uB3CC\uB9AC\uAE30 \uC644\uB8CC! RULES.md \uBCF5\uC6D0 + sync \uC7AC\uC2E4\uD589\uB428"));
|
|
7121
7457
|
printNextStep({
|
|
7122
7458
|
message: "\uB418\uB3CC\uB9AC\uAE30 \uC644\uB8CC!",
|
|
7123
7459
|
command: "vhk evolve list",
|
|
@@ -7299,6 +7635,10 @@ async function dispatchNlpRoute(route, input) {
|
|
|
7299
7635
|
return patternList();
|
|
7300
7636
|
case "evolve":
|
|
7301
7637
|
return evolveList();
|
|
7638
|
+
case "work": {
|
|
7639
|
+
if (route.args?.[0] === "handoff") return workHandoff();
|
|
7640
|
+
return work();
|
|
7641
|
+
}
|
|
7302
7642
|
}
|
|
7303
7643
|
}
|
|
7304
7644
|
var STATE_CHANGING_COMMANDS = /* @__PURE__ */ new Set([
|
|
@@ -7312,14 +7652,14 @@ function requiresConfirmation(route) {
|
|
|
7312
7652
|
async function runNaturalLanguageRoute(input) {
|
|
7313
7653
|
const route = routeNaturalLanguage(input);
|
|
7314
7654
|
if (!route) {
|
|
7315
|
-
console.log(
|
|
7655
|
+
console.log(chalk36.yellow(`
|
|
7316
7656
|
\u2753 "${input}" \u2014 ${ko.nlp.notMatched}
|
|
7317
7657
|
`));
|
|
7318
7658
|
return;
|
|
7319
7659
|
}
|
|
7320
7660
|
console.log("");
|
|
7321
|
-
console.log(
|
|
7322
|
-
console.log(
|
|
7661
|
+
console.log(chalk36.cyan(` \u{1F4AC} "${input}"`));
|
|
7662
|
+
console.log(chalk36.cyan(` \u2192 ${route.explanation}`));
|
|
7323
7663
|
if (requiresConfirmation(route)) {
|
|
7324
7664
|
const { confirm } = await inquirer14.prompt([{
|
|
7325
7665
|
type: "confirm",
|
|
@@ -7328,7 +7668,7 @@ async function runNaturalLanguageRoute(input) {
|
|
|
7328
7668
|
default: true
|
|
7329
7669
|
}]);
|
|
7330
7670
|
if (!confirm) {
|
|
7331
|
-
console.log(
|
|
7671
|
+
console.log(chalk36.dim(` ${ko.nlp.menuHint}`));
|
|
7332
7672
|
return;
|
|
7333
7673
|
}
|
|
7334
7674
|
}
|
|
@@ -7337,7 +7677,7 @@ async function runNaturalLanguageRoute(input) {
|
|
|
7337
7677
|
if (riskAction) {
|
|
7338
7678
|
await runGuarded(
|
|
7339
7679
|
riskAction,
|
|
7340
|
-
{ channel: "nl", approved: false, log: (m) => console.log(
|
|
7680
|
+
{ channel: "nl", approved: false, log: (m) => console.log(chalk36.yellow(` ${m}`)) },
|
|
7341
7681
|
() => dispatchNlpRoute(route, input)
|
|
7342
7682
|
);
|
|
7343
7683
|
return;
|
|
@@ -7346,80 +7686,80 @@ async function runNaturalLanguageRoute(input) {
|
|
|
7346
7686
|
}
|
|
7347
7687
|
|
|
7348
7688
|
// src/commands/agent.ts
|
|
7349
|
-
import
|
|
7689
|
+
import chalk37 from "chalk";
|
|
7350
7690
|
function activeGoalId() {
|
|
7351
7691
|
const goals = listGoals("goals");
|
|
7352
7692
|
const id = selectActiveId(goals);
|
|
7353
7693
|
return id ?? void 0;
|
|
7354
7694
|
}
|
|
7355
7695
|
async function blocker(description) {
|
|
7356
|
-
console.log(
|
|
7696
|
+
console.log(chalk37.bold(`
|
|
7357
7697
|
${ko.agent.blockerTitle}
|
|
7358
7698
|
`));
|
|
7359
7699
|
if (!description || !description.trim()) {
|
|
7360
|
-
console.log(
|
|
7361
|
-
console.log(
|
|
7700
|
+
console.log(chalk37.red(" \u274C \uBE14\uB85C\uCEE4 \uC124\uBA85\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694."));
|
|
7701
|
+
console.log(chalk37.dim(' \uC608: vhk blocker "tsc \uC5D0\uB7EC \u2014 simple-git \uD0C0\uC785 \uD638\uD658"'));
|
|
7362
7702
|
process.exitCode = 1;
|
|
7363
7703
|
return;
|
|
7364
7704
|
}
|
|
7365
7705
|
const goalId = activeGoalId();
|
|
7366
7706
|
const r = appendBlocker(description, goalId);
|
|
7367
|
-
console.log(
|
|
7707
|
+
console.log(chalk37.green(` \u2705 blocker \uAE30\uB85D (\uD604\uC7AC \uD65C\uC131 ${r.count}\uAC74)`));
|
|
7368
7708
|
if (r.hardStopTripped) {
|
|
7369
|
-
console.log(
|
|
7370
|
-
console.log(
|
|
7709
|
+
console.log(chalk37.red.bold(" \u{1F6D1} HARD_STOP \uC790\uB3D9 \uC0DD\uC131 \u2014 \uBAA8\uB4E0 \uC790\uB3D9\uD654 \uC911\uB2E8."));
|
|
7710
|
+
console.log(chalk37.yellow(" \uC0AC\uB78C \uAC80\uD1A0 \uD6C4 `vhk resume --confirm` \uC73C\uB85C\uB9CC \uD574\uC81C."));
|
|
7371
7711
|
process.exitCode = 2;
|
|
7372
7712
|
}
|
|
7373
7713
|
}
|
|
7374
7714
|
async function learn(lesson) {
|
|
7375
|
-
console.log(
|
|
7715
|
+
console.log(chalk37.bold(`
|
|
7376
7716
|
${ko.agent.learnTitle}
|
|
7377
7717
|
`));
|
|
7378
7718
|
if (!lesson || !lesson.trim()) {
|
|
7379
|
-
console.log(
|
|
7380
|
-
console.log(
|
|
7719
|
+
console.log(chalk37.red(" \u274C \uAD50\uD6C8 \uB0B4\uC6A9\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694."));
|
|
7720
|
+
console.log(chalk37.dim(' \uC608: vhk learn "PowerShell \uC5D0\uC11C\uB294 ; \uC0AC\uC6A9 (&& \uBBF8\uC9C0\uC6D0)"'));
|
|
7381
7721
|
process.exitCode = 1;
|
|
7382
7722
|
return;
|
|
7383
7723
|
}
|
|
7384
7724
|
const goalId = activeGoalId();
|
|
7385
7725
|
const entry = recordLesson(process.cwd(), lesson, goalId);
|
|
7386
7726
|
if (!entry) {
|
|
7387
|
-
console.log(
|
|
7727
|
+
console.log(chalk37.red(" \u274C memory.json \uC190\uC0C1 \uC758\uC2EC \u2014 \uAD50\uD6C8 \uAE30\uB85D \uC911\uB2E8. \uC6D0\uBCF8/\uBC31\uC5C5 \uD655\uC778 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694."));
|
|
7388
7728
|
process.exitCode = 1;
|
|
7389
7729
|
return;
|
|
7390
7730
|
}
|
|
7391
|
-
console.log(
|
|
7392
|
-
console.log(
|
|
7731
|
+
console.log(chalk37.green(` \u2705 \uAD50\uD6C8 \uAE30\uB85D \u2192 memory failures.lesson (${entry.id})`));
|
|
7732
|
+
console.log(chalk37.dim(" \uAD50\uD6C8\xB7\uACB0\uC815\xB7\uC2E4\uD328\xB7\uC131\uACF5 \uBAA8\uB450 vhk memory (\uB2E8\uC77C SoT). vhk memory list \uB85C \uD655\uC778."));
|
|
7393
7733
|
}
|
|
7394
7734
|
async function resume(opts = {}) {
|
|
7395
|
-
console.log(
|
|
7735
|
+
console.log(chalk37.bold(`
|
|
7396
7736
|
${ko.agent.resumeTitle}
|
|
7397
7737
|
`));
|
|
7398
7738
|
if (!isHardStopActive()) {
|
|
7399
|
-
console.log(
|
|
7739
|
+
console.log(chalk37.dim(" HARD_STOP \uD65C\uC131 \uC544\uB2D8 \u2014 \uD560 \uC77C \uC5C6\uC74C."));
|
|
7400
7740
|
return;
|
|
7401
7741
|
}
|
|
7402
7742
|
const reason = readHardStopReason();
|
|
7403
7743
|
if (reason) {
|
|
7404
|
-
console.log(
|
|
7405
|
-
console.log(
|
|
7744
|
+
console.log(chalk37.yellow(" \u{1F4CB} HARD_STOP \uC0AC\uC720:"));
|
|
7745
|
+
console.log(chalk37.dim(` ${reason.split("\n").join("\n ")}`));
|
|
7406
7746
|
console.log("");
|
|
7407
7747
|
}
|
|
7408
7748
|
if (!opts.confirm) {
|
|
7409
7749
|
console.log(
|
|
7410
|
-
|
|
7750
|
+
chalk37.red(
|
|
7411
7751
|
" \u274C --confirm \uD50C\uB798\uADF8 \uC5C6\uC774\uB294 \uD574\uC81C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC790\uB3D9 \uD638\uCD9C \uAE08\uC9C0)."
|
|
7412
7752
|
)
|
|
7413
7753
|
);
|
|
7414
|
-
console.log(
|
|
7754
|
+
console.log(chalk37.yellow(" \uC0AC\uC720\uB97C \uD655\uC778\uD55C \uD6C4 \uB2E4\uC2DC: vhk resume --confirm"));
|
|
7415
7755
|
process.exitCode = 1;
|
|
7416
7756
|
return;
|
|
7417
7757
|
}
|
|
7418
7758
|
const removed = clearHardStop();
|
|
7419
7759
|
if (removed) {
|
|
7420
|
-
console.log(
|
|
7760
|
+
console.log(chalk37.green(" \u2705 HARD_STOP \uD574\uC81C. \uC790\uB3D9\uD654 \uC7AC\uAC1C \uAC00\uB2A5."));
|
|
7421
7761
|
} else {
|
|
7422
|
-
console.log(
|
|
7762
|
+
console.log(chalk37.dim(" \uD30C\uC77C\uC774 \uC774\uBBF8 \uC5C6\uC74C \u2014 no-op."));
|
|
7423
7763
|
}
|
|
7424
7764
|
}
|
|
7425
7765
|
|
|
@@ -7440,7 +7780,7 @@ async function guardCli(action, approved, run) {
|
|
|
7440
7780
|
}]);
|
|
7441
7781
|
return ok;
|
|
7442
7782
|
},
|
|
7443
|
-
log: (m) => console.log(
|
|
7783
|
+
log: (m) => console.log(chalk38.yellow(` ${m}`))
|
|
7444
7784
|
},
|
|
7445
7785
|
run
|
|
7446
7786
|
);
|
|
@@ -7453,7 +7793,7 @@ async function guardCliDefer(action, approved, run) {
|
|
|
7453
7793
|
approved,
|
|
7454
7794
|
// TTY 면 통과(명령이 자체 확인), 비대화형은 confirm 불가 → 가드가 차단.
|
|
7455
7795
|
confirm: async () => !!process.stdout.isTTY,
|
|
7456
|
-
log: (m) => console.log(
|
|
7796
|
+
log: (m) => console.log(chalk38.yellow(` ${m}`))
|
|
7457
7797
|
},
|
|
7458
7798
|
run
|
|
7459
7799
|
);
|
|
@@ -7498,7 +7838,8 @@ var KO_ALIASES = {
|
|
|
7498
7838
|
learn: "\uAD50\uD6C8",
|
|
7499
7839
|
resume: "\uC7AC\uAC1C",
|
|
7500
7840
|
pattern: "\uD328\uD134",
|
|
7501
|
-
evolve: "\uC9C4\uD654"
|
|
7841
|
+
evolve: "\uC9C4\uD654",
|
|
7842
|
+
work: "\uC791\uC5C5"
|
|
7502
7843
|
};
|
|
7503
7844
|
program.name("vhk").description("VHK \u2014 \uBC14\uC774\uBE0C\uCF54\uB529 \uD504\uB85C\uC81D\uD2B8 \uCF54\uCE58 (\uD55C\uAD6D\uC5B4\uB85C \uC548\uB0B4\uD569\uB2C8\uB2E4)").version(getVhkVersion());
|
|
7504
7845
|
program.configureHelp({
|
|
@@ -7546,7 +7887,9 @@ cloudCmd.command("pull").alias("\uB0B4\uB9AC\uAE30").argument("[gistId]", "\uBCF
|
|
|
7546
7887
|
await guardCli("cloud-pull", opts?.yes === true, () => cloudPull(gistId));
|
|
7547
7888
|
});
|
|
7548
7889
|
program.command("ship").alias("\uCD9C\uD558").description("\uBC30\uD3EC \uCCB4\uD06C\uB9AC\uC2A4\uD2B8 + \uD68C\uACE0 + \uBE4C\uB4DC \uB85C\uADF8 \uC0DD\uC131").action(ship);
|
|
7549
|
-
program.command("doctor").alias("\uD658\uACBD").alias("\uC9C4\uB2E8").description("\uAC1C\uBC1C \uD658\uACBD \uC810\uAC80 \u2014 Node/Git/npm \uC0C1\uD0DC \uD655\uC778").action(
|
|
7890
|
+
program.command("doctor").alias("\uD658\uACBD").alias("\uC9C4\uB2E8").option("--strict", "\uADDC\uCE59 \uB4DC\uB9AC\uD504\uD2B8 \uBC1C\uACAC \uC2DC \uC2E4\uD328 \uCC98\uB9AC (exit 1, CI \uAC8C\uC774\uD2B8\uC6A9)").description("\uAC1C\uBC1C \uD658\uACBD \uC810\uAC80 \u2014 Node/Git/npm \uC0C1\uD0DC \uD655\uC778").action(async (opts) => {
|
|
7891
|
+
await doctor(opts);
|
|
7892
|
+
});
|
|
7550
7893
|
program.command("save").alias("\uC800\uC7A5").option("--yes", "\uD655\uC778 \uC5C6\uC774 \uC2E4\uD589 (strict \uBAA8\uB4DC \uAC00\uB4DC \uBA85\uC2DC \uC2B9\uC778)").description("\uBCC0\uACBD\uC0AC\uD56D \uC800\uC7A5 (git add \u2192 commit \u2192 push)").action(async (opts) => {
|
|
7551
7894
|
await guardCli("save", opts?.yes === true, () => save());
|
|
7552
7895
|
});
|
|
@@ -7668,6 +8011,12 @@ memoryCmd.command("migrate").alias("\uB9C8\uC774\uADF8\uB808\uC774\uC158").descr
|
|
|
7668
8011
|
program.command("brief").alias("\uBE0C\uB9AC\uD551").description("\uD504\uB85C\uC81D\uD2B8 \uC0C1\uD0DC \uC694\uC57D \uBCF4\uACE0\uC11C \uC0DD\uC131 (.vhk/brief.md)").action(async () => {
|
|
7669
8012
|
await brief();
|
|
7670
8013
|
});
|
|
8014
|
+
var workCmd = program.command("work").alias("\uC791\uC5C5").description("AI \uC791\uC5C5 \uC2DC\uC791/\uC774\uC5B4\uD558\uAE30 \u2014 \uC2DC\uC791 \uD504\uB86C\uD504\uD2B8 \uC0DD\uC131 \uD6C4 \uD074\uB9BD\uBCF4\uB4DC \uBCF5\uC0AC").action(async () => {
|
|
8015
|
+
await work();
|
|
8016
|
+
});
|
|
8017
|
+
workCmd.command("handoff").alias("\uC778\uC218\uC778\uACC4").description("\uC791\uC5C5 \uC911\uB2E8 \uC815\uB9AC \u2014 \uC778\uC218\uC778\uACC4 \uD504\uB86C\uD504\uD2B8 \uC0DD\uC131 \uD6C4 \uD074\uB9BD\uBCF4\uB4DC \uBCF5\uC0AC").action(async () => {
|
|
8018
|
+
await workHandoff();
|
|
8019
|
+
});
|
|
7671
8020
|
var goalCmd = program.command("goal").alias("\uBAA9\uD45C").description("Goal \uB2E8\uACC4\uBCC4 \uBBF8\uC158 \uAD00\uB9AC (init / list / next / check / done / sync)").action(async () => {
|
|
7672
8021
|
await goalList();
|
|
7673
8022
|
});
|
|
@@ -7735,27 +8084,49 @@ program.on("command:*", async (operands) => {
|
|
|
7735
8084
|
await runNaturalLanguageRoute(input);
|
|
7736
8085
|
});
|
|
7737
8086
|
program.action(async () => {
|
|
7738
|
-
|
|
8087
|
+
const info = getUpdateInfo();
|
|
8088
|
+
console.log("\n\u{1F3AF} VHK \u2014 \uBC14\uC774\uBE0C\uCF54\uB529 \uD504\uB85C\uC81D\uD2B8 \uCF54\uCE58 " + chalk38.dim(`v${info.current}`));
|
|
8089
|
+
if (info.updateAvailable && info.latest) {
|
|
8090
|
+
console.log(chalk38.yellow(`\u{1F195} \uC5C5\uB370\uC774\uD2B8 \uAC00\uB2A5: v${info.latest}`) + chalk38.dim(" \u2192 vhk update"));
|
|
8091
|
+
}
|
|
8092
|
+
const sample = QUICK_ACTIONS[0]?.say ?? "\uC0C1\uD0DC \uC54C\uB824\uC918";
|
|
8093
|
+
console.log(
|
|
8094
|
+
chalk38.dim("\u{1F4AC} \uBA85\uB839 \uC9C1\uC811 \uC785\uB825\uB3C4 \uB3FC\uC694 \u2014 \uC608: ") + chalk38.cyan("vhk status") + chalk38.dim(" \xB7 \uC790\uC5F0\uC5B4 OK: ") + chalk38.cyan(`"${sample}"`)
|
|
8095
|
+
);
|
|
8096
|
+
console.log("");
|
|
8097
|
+
const choices = [
|
|
8098
|
+
{ name: "\u{1F680} \uC791\uC5C5 \uC2DC\uC791/\uC774\uC5B4\uD558\uAE30 (work)", value: "work" },
|
|
8099
|
+
{ name: "\u{1F4A1} \uC0C8 \uC544\uC774\uB514\uC5B4 \uAC80\uC99D\uD558\uAE30", value: "gate" },
|
|
8100
|
+
{ name: "\u{1F195} \uC0C8 \uD504\uB85C\uC81D\uD2B8 \uC2DC\uC791 \uB9C8\uBC95\uC0AC (start)", value: "start" },
|
|
8101
|
+
{ name: "\u{1F3AF} \uB2E4\uC74C \uBAA9\uD45C \uBCF4\uAE30 (goal)", value: "goal-next" },
|
|
8102
|
+
{ name: "\u{1F4DD} \uC624\uB298 \uD55C \uC77C \uC815\uB9AC\uD558\uAE30", value: "recap" },
|
|
8103
|
+
{ name: "\u{1F50D} \uADDC\uCE59 \uD30C\uC77C \uC810\uAC80\uD558\uAE30", value: "check" },
|
|
8104
|
+
{ name: "\u{1F512} \uBCF4\uC548 \uC2A4\uCE94 \uB3CC\uB9AC\uAE30", value: "secure" },
|
|
8105
|
+
{ name: "\u{1F504} \uADDC\uCE59 \uD30C\uC77C \uB3D9\uAE30\uD654", value: "sync" },
|
|
8106
|
+
{ name: "\u{1F680} \uBC30\uD3EC\uD558\uAE30", value: "ship" },
|
|
8107
|
+
{ name: "\u{1FA7A} \uD658\uACBD \uC810\uAC80\uD558\uAE30", value: "doctor" },
|
|
8108
|
+
{ name: "\u{1F4BE} Git\uC5D0 \uC800\uC7A5\uD558\uAE30", value: "save" },
|
|
8109
|
+
{ name: "\u23EA \uCD5C\uADFC \uCEE4\uBC0B \uB418\uB3CC\uB9AC\uAE30", value: "undo" },
|
|
8110
|
+
{ name: "\u{1F50D} \uBCC0\uACBD\uC0AC\uD56D \uBCF4\uAE30", value: "diff" },
|
|
8111
|
+
{ name: "\u{1F4CA} \uD504\uB85C\uC81D\uD2B8 \uC0C1\uD0DC \uBCF4\uAE30", value: "status" },
|
|
8112
|
+
{ name: "\u23F8\uFE0F \uC791\uC5C5 \uC911\uB2E8 \uC815\uB9AC (handoff)", value: "work-handoff" }
|
|
8113
|
+
];
|
|
7739
8114
|
const { choice } = await inquirer15.prompt([{
|
|
7740
8115
|
type: "list",
|
|
7741
8116
|
name: "choice",
|
|
7742
8117
|
message: "\uBB58 \uB3C4\uC640\uB4DC\uB9B4\uAE4C\uC694?",
|
|
7743
|
-
|
|
7744
|
-
|
|
7745
|
-
|
|
7746
|
-
|
|
7747
|
-
{ name: "\u{1F50D} \uADDC\uCE59 \uD30C\uC77C \uC810\uAC80\uD558\uAE30", value: "check" },
|
|
7748
|
-
{ name: "\u{1F512} \uBCF4\uC548 \uC2A4\uCE94 \uB3CC\uB9AC\uAE30", value: "secure" },
|
|
7749
|
-
{ name: "\u{1F504} \uADDC\uCE59 \uD30C\uC77C \uB3D9\uAE30\uD654", value: "sync" },
|
|
7750
|
-
{ name: "\u{1F680} \uBC30\uD3EC\uD558\uAE30", value: "ship" },
|
|
7751
|
-
{ name: "\u{1FA7A} \uD658\uACBD \uC810\uAC80\uD558\uAE30", value: "doctor" },
|
|
7752
|
-
{ name: "\u{1F4BE} Git\uC5D0 \uC800\uC7A5\uD558\uAE30", value: "save" },
|
|
7753
|
-
{ name: "\u23EA \uCD5C\uADFC \uCEE4\uBC0B \uB418\uB3CC\uB9AC\uAE30", value: "undo" },
|
|
7754
|
-
{ name: "\u{1F50D} \uBCC0\uACBD\uC0AC\uD56D \uBCF4\uAE30", value: "diff" },
|
|
7755
|
-
{ name: "\u{1F4CA} \uD504\uB85C\uC81D\uD2B8 \uC0C1\uD0DC \uBCF4\uAE30", value: "status" }
|
|
7756
|
-
]
|
|
8118
|
+
pageSize: choices.length,
|
|
8119
|
+
// 스크롤 잔상/잘림 방지(Windows conhost): 한 화면에 전부
|
|
8120
|
+
loop: false,
|
|
8121
|
+
choices
|
|
7757
8122
|
}]);
|
|
7758
8123
|
switch (choice) {
|
|
8124
|
+
case "work":
|
|
8125
|
+
return work();
|
|
8126
|
+
case "work-handoff":
|
|
8127
|
+
return workHandoff();
|
|
8128
|
+
case "goal-next":
|
|
8129
|
+
return goalNext();
|
|
7759
8130
|
case "gate":
|
|
7760
8131
|
return gate();
|
|
7761
8132
|
case "start":
|
|
@@ -7784,7 +8155,7 @@ program.action(async () => {
|
|
|
7784
8155
|
});
|
|
7785
8156
|
var getRealPath = (p) => {
|
|
7786
8157
|
try {
|
|
7787
|
-
return
|
|
8158
|
+
return fs14.realpathSync(p);
|
|
7788
8159
|
} catch {
|
|
7789
8160
|
return p;
|
|
7790
8161
|
}
|
|
@@ -7800,9 +8171,9 @@ if (isMainModule) {
|
|
|
7800
8171
|
}
|
|
7801
8172
|
} catch (err) {
|
|
7802
8173
|
if (isPromptAbortError(err)) {
|
|
7803
|
-
console.error(
|
|
8174
|
+
console.error(chalk38.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)"));
|
|
7804
8175
|
} else {
|
|
7805
|
-
console.error(
|
|
8176
|
+
console.error(chalk38.red(`
|
|
7806
8177
|
\u274C ${err instanceof Error ? err.message : String(err)}`));
|
|
7807
8178
|
}
|
|
7808
8179
|
process.exitCode = 1;
|