@byh3071/vhk 2.3.2 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-OHGVVAKT.js → chunk-MN6LSPN6.js} +75 -11
- package/dist/index.js +619 -338
- 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-MN6LSPN6.js";
|
|
47
47
|
|
|
48
48
|
// src/index.ts
|
|
49
49
|
import { Command, Help } from "commander";
|
|
50
50
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
51
51
|
import fs13 from "fs";
|
|
52
|
-
import
|
|
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
|
|
@@ -2957,7 +3022,7 @@ function compareSemver(a, b) {
|
|
|
2957
3022
|
if (a2 !== b2) return a2 - b2;
|
|
2958
3023
|
return a3 - b3;
|
|
2959
3024
|
}
|
|
2960
|
-
async function doctor() {
|
|
3025
|
+
async function doctor(opts = {}) {
|
|
2961
3026
|
console.log(chalk9.bold(`
|
|
2962
3027
|
${ko.doctor.title}
|
|
2963
3028
|
`));
|
|
@@ -3016,14 +3081,15 @@ ${ko.doctor.title}
|
|
|
3016
3081
|
}
|
|
3017
3082
|
}
|
|
3018
3083
|
} else if (file.name === ".env" && fs8.existsSync(path9.join(cwd, ".env.local"))) {
|
|
3019
|
-
console.log(chalk9.green(" \u2705 .env.local") + chalk9.dim(" \u2014 \uB85C\
|
|
3084
|
+
console.log(chalk9.green(" \u2705 .env.local") + chalk9.dim(" \u2014 \uB85C\uCEF4 env \uC0AC\uC6A9 \uC911 (.env \uC5C6\uC5B4\uB3C4 \uC815\uC0C1)"));
|
|
3020
3085
|
} else {
|
|
3021
|
-
console.log(chalk9.dim(` \
|
|
3086
|
+
console.log(chalk9.dim(` \u26AB ${file.name}`) + chalk9.dim(` \u2014 ${file.hint}`));
|
|
3022
3087
|
}
|
|
3023
3088
|
}
|
|
3024
3089
|
console.log("");
|
|
3025
3090
|
console.log(chalk9.bold(` ${ko.doctor.driftTitle}`));
|
|
3026
3091
|
const ruleDrift = checkRuleDrift(cwd);
|
|
3092
|
+
let ruleDrifted = false;
|
|
3027
3093
|
if (!ruleDrift.checked) {
|
|
3028
3094
|
console.log(chalk9.dim(` ${ko.doctor.driftNoRules}`));
|
|
3029
3095
|
} else {
|
|
@@ -3032,6 +3098,7 @@ ${ko.doctor.title}
|
|
|
3032
3098
|
console.log(chalk9.green(` ${ko.doctor.driftRuleClean}`));
|
|
3033
3099
|
} else {
|
|
3034
3100
|
console.log(chalk9.yellow(` ${ko.doctor.driftRuleWarn(drifted.map((d) => d.path).join(", "))}`));
|
|
3101
|
+
ruleDrifted = true;
|
|
3035
3102
|
}
|
|
3036
3103
|
}
|
|
3037
3104
|
const ctxDrift = checkContextDrift(cwd);
|
|
@@ -3055,6 +3122,11 @@ ${ko.doctor.title}
|
|
|
3055
3122
|
});
|
|
3056
3123
|
process.exitCode = 1;
|
|
3057
3124
|
}
|
|
3125
|
+
if (opts.strict && ruleDrifted) {
|
|
3126
|
+
console.log("");
|
|
3127
|
+
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)"));
|
|
3128
|
+
process.exitCode = 1;
|
|
3129
|
+
}
|
|
3058
3130
|
}
|
|
3059
3131
|
|
|
3060
3132
|
// src/commands/ship.ts
|
|
@@ -4947,37 +5019,7 @@ function extractTechStack() {
|
|
|
4947
5019
|
return stack;
|
|
4948
5020
|
}
|
|
4949
5021
|
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
|
-
];
|
|
5022
|
+
return TOP_LEVEL_COMMANDS.map((c) => `${c.name} \u2014 ${c.desc}`);
|
|
4981
5023
|
}
|
|
4982
5024
|
async function context(opts = {}) {
|
|
4983
5025
|
const compact = opts.compact === true;
|
|
@@ -4992,6 +5034,16 @@ async function context(opts = {}) {
|
|
|
4992
5034
|
lines.push("> \uC774 \uD30C\uC77C\uC740 `vhk context`\uB85C \uC790\uB3D9 \uC0DD\uC131\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
|
|
4993
5035
|
lines.push("> AI \uC5B4\uC2DC\uC2A4\uD134\uD2B8\uC5D0\uAC8C \uD504\uB85C\uC81D\uD2B8 \uB9E5\uB77D\uC744 \uC81C\uACF5\uD569\uB2C8\uB2E4.");
|
|
4994
5036
|
lines.push("");
|
|
5037
|
+
lines.push("## \uC6D0\uBCF8 \uC9C0\uB3C4 (Source of Truth)");
|
|
5038
|
+
lines.push("");
|
|
5039
|
+
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.');
|
|
5040
|
+
lines.push("");
|
|
5041
|
+
lines.push("- **\uADDC\uCE59(\uC6D0\uBCF8)**: `RULES.md` \u2014 \uADDC\uCE59\uC740 \uC5EC\uAE30 \uD55C \uACF3\uC5D0\uC11C\uB9CC \uC218\uC815");
|
|
5042
|
+
lines.push("- **\uC791\uC5C5 \uC0C1\uD0DC**: `docs/state/next-task.md`, `docs/state/blockers.md`");
|
|
5043
|
+
lines.push("- **\uBC84\uC804\xB7\uB9B4\uB9AC\uC2A4**: `package.json`, `CHANGELOG.md`");
|
|
5044
|
+
lines.push("- **\uBA85\uB839 \uBAA9\uB85D**: `COMMANDS.md` (+ `vhk help`)");
|
|
5045
|
+
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");
|
|
5046
|
+
lines.push("");
|
|
4995
5047
|
lines.push("## \uAE30\uC220 \uC2A4\uD0DD");
|
|
4996
5048
|
lines.push("");
|
|
4997
5049
|
for (const [key, value] of Object.entries(stack)) {
|
|
@@ -5051,9 +5103,10 @@ async function context(opts = {}) {
|
|
|
5051
5103
|
if (compact) {
|
|
5052
5104
|
lines.push("## \uCC38\uC870 \uBB38\uC11C (\uD544\uC694\uC2DC \uC5F4\uB78C)");
|
|
5053
5105
|
lines.push("");
|
|
5106
|
+
lines.push("- \uADDC\uCE59 \uC6D0\uBCF8(SoT): `RULES.md` \u2014 \uADDC\uCE59\uC740 \uC5EC\uAE30\uC11C\uB9CC \uC218\uC815");
|
|
5054
5107
|
lines.push("- \uC791\uB3D9 \uADDC\uC57D(\uC694\uC57D): `docs/context/agent-compact.md`");
|
|
5055
5108
|
lines.push("- \uADDC\uC57D \uC0C1\uC138: `AGENTS.md`");
|
|
5056
|
-
lines.push("- \
|
|
5109
|
+
lines.push("- \uC6B4\uC601 \uC548\uB0B4\xB7\uAE30\uB85D: `CLAUDE.md`");
|
|
5057
5110
|
lines.push("- \uBA85\uB839 \uC0C1\uC138: `COMMANDS.md`");
|
|
5058
5111
|
lines.push("- \uAD6C\uC870 \uC0C1\uC138: `docs/ARCHITECTURE.md`");
|
|
5059
5112
|
lines.push("- \uD604\uC7AC \uC0C1\uD0DC: `docs/state/next-task.md`");
|
|
@@ -5213,12 +5266,227 @@ async function brief() {
|
|
|
5213
5266
|
});
|
|
5214
5267
|
}
|
|
5215
5268
|
|
|
5216
|
-
// src/commands/
|
|
5269
|
+
// src/commands/work.ts
|
|
5270
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync10, writeFileSync as writeFileSync10, readFileSync as readFileSync7 } from "fs";
|
|
5271
|
+
import { join as join9 } from "path";
|
|
5217
5272
|
import chalk26 from "chalk";
|
|
5273
|
+
|
|
5274
|
+
// src/lib/clipboard.ts
|
|
5275
|
+
import { spawnSync } from "child_process";
|
|
5276
|
+
function copyToClipboard(text) {
|
|
5277
|
+
try {
|
|
5278
|
+
if (process.platform === "win32") return copyWin32(text);
|
|
5279
|
+
if (process.platform === "darwin") return runWithInput("pbcopy", [], text);
|
|
5280
|
+
return runWithInput("wl-copy", [], text) || runWithInput("xclip", ["-selection", "clipboard"], text) || runWithInput("xsel", ["--clipboard", "--input"], text);
|
|
5281
|
+
} catch {
|
|
5282
|
+
return false;
|
|
5283
|
+
}
|
|
5284
|
+
}
|
|
5285
|
+
function copyWin32(text) {
|
|
5286
|
+
const b64 = Buffer.from(text, "utf8").toString("base64");
|
|
5287
|
+
const psScript = "$b=[Console]::In.ReadToEnd();$t=[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($b));Set-Clipboard -Value $t";
|
|
5288
|
+
const r = spawnSync(
|
|
5289
|
+
"powershell",
|
|
5290
|
+
["-NoProfile", "-NonInteractive", "-Command", psScript],
|
|
5291
|
+
{ input: b64, encoding: "utf8", windowsHide: true }
|
|
5292
|
+
);
|
|
5293
|
+
return r.status === 0 && !r.error;
|
|
5294
|
+
}
|
|
5295
|
+
function runWithInput(cmd, args, input) {
|
|
5296
|
+
const r = spawnSync(cmd, args, { input, encoding: "utf8" });
|
|
5297
|
+
return r.status === 0 && !r.error;
|
|
5298
|
+
}
|
|
5299
|
+
|
|
5300
|
+
// src/commands/work.ts
|
|
5301
|
+
var VHK_DIR2 = ".vhk";
|
|
5302
|
+
function gitShort() {
|
|
5303
|
+
const r = safeExecFile("git", ["status", "--short"]);
|
|
5304
|
+
return r.ok ? r.out.trim() : "";
|
|
5305
|
+
}
|
|
5306
|
+
function ensureVhkProject() {
|
|
5307
|
+
if (existsSync14("CLAUDE.md") || existsSync14(VHK_DIR2) || existsSync14("goals")) return true;
|
|
5308
|
+
console.log(chalk26.yellow(" \u26A0\uFE0F \uC5EC\uAE30\uB294 VHK \uD504\uB85C\uC81D\uD2B8 \uD3F4\uB354\uAC00 \uC544\uB2CC \uAC83 \uAC19\uC544\uC694."));
|
|
5309
|
+
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."));
|
|
5310
|
+
return false;
|
|
5311
|
+
}
|
|
5312
|
+
function passHardStop() {
|
|
5313
|
+
if (!isHardStopActive()) {
|
|
5314
|
+
console.log(chalk26.green(" \u2705 HARD_STOP \uC5C6\uC74C"));
|
|
5315
|
+
return true;
|
|
5316
|
+
}
|
|
5317
|
+
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."));
|
|
5318
|
+
const reason = readHardStopReason();
|
|
5319
|
+
if (reason) console.log(chalk26.red(` \uC0AC\uC720: ${reason.replace(/\s*\n\s*/g, " ")}`));
|
|
5320
|
+
console.log(chalk26.yellow(" \uD574\uC81C\uB294 \uC0AC\uB78C\uC774 \uC9C1\uC811: vhk resume --confirm"));
|
|
5321
|
+
return false;
|
|
5322
|
+
}
|
|
5323
|
+
function activeGoalLine() {
|
|
5324
|
+
const goals = listGoals("goals");
|
|
5325
|
+
if (goals.length === 0) return "\uC815\uC758\uB41C goal \uC5C6\uC74C";
|
|
5326
|
+
const id = selectActiveId(goals);
|
|
5327
|
+
if (id === null) return "\uBAA8\uB4E0 goal \uC644\uB8CC\uB428";
|
|
5328
|
+
const g = goals.find((x) => x.frontmatter.id === id);
|
|
5329
|
+
if (!g) return "\uC815\uC758\uB41C goal \uC5C6\uC74C";
|
|
5330
|
+
return `Goal ${id} \u2014 ${g.frontmatter.title ?? "(untitled)"}`;
|
|
5331
|
+
}
|
|
5332
|
+
function printGit(git3) {
|
|
5333
|
+
if (!git3) {
|
|
5334
|
+
console.log(chalk26.dim(" (\uBCC0\uACBD\uB41C \uD30C\uC77C \uC5C6\uC74C \u2014 \uAE68\uB057\uD55C \uC0C1\uD0DC)"));
|
|
5335
|
+
return;
|
|
5336
|
+
}
|
|
5337
|
+
for (const line of git3.split(/\r?\n/)) console.log(` ${line}`);
|
|
5338
|
+
}
|
|
5339
|
+
function buildStartPrompt(git3, goalLine) {
|
|
5340
|
+
const gitBlock = git3 || "(\uBCC0\uACBD \uC5C6\uC74C)";
|
|
5341
|
+
return [
|
|
5342
|
+
"\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.",
|
|
5343
|
+
"",
|
|
5344
|
+
"[\uADDC\uCE59 \uC6B0\uC120\uC21C\uC704]",
|
|
5345
|
+
"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)",
|
|
5346
|
+
"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.",
|
|
5347
|
+
"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.",
|
|
5348
|
+
"",
|
|
5349
|
+
"[\uC791\uC5C5 \uC804 Source of Truth \uCCB4\uD06C]",
|
|
5350
|
+
"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.",
|
|
5351
|
+
"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.",
|
|
5352
|
+
"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.",
|
|
5353
|
+
"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)",
|
|
5354
|
+
"",
|
|
5355
|
+
"[\uC9C0\uAE08 \uC0C1\uD0DC \u2014 \uC790\uB3D9 \uC218\uC9D1\uB428]",
|
|
5356
|
+
"- \uBCC0\uACBD\uB41C \uD30C\uC77C (git status --short):",
|
|
5357
|
+
gitBlock,
|
|
5358
|
+
"- \uD604\uC7AC \uBAA9\uD45C (active goal):",
|
|
5359
|
+
goalLine,
|
|
5360
|
+
"",
|
|
5361
|
+
"[\uD574\uC8FC\uC138\uC694]",
|
|
5362
|
+
"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.",
|
|
5363
|
+
"2. \uADF8\uB2E4\uC74C \uD560 \uC77C\uC744 3\uB2E8\uACC4 \uC774\uD558\uB85C \uB9D0\uD558\uACE0 \uC9C4\uD589\uD558\uC138\uC694.",
|
|
5364
|
+
"3. \uD14C\uC2A4\uD2B8\uAC00 \uD1B5\uACFC\uD558\uAE30 \uC804\uC5D0\uB294 \uC644\uB8CC(done) \uCC98\uB9AC\uD558\uC9C0 \uB9C8\uC138\uC694.",
|
|
5365
|
+
"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.",
|
|
5366
|
+
"\uBAA8\uB4E0 \uC751\uB2F5\uC740 \uD55C\uAD6D\uC5B4\uB85C."
|
|
5367
|
+
].join("\n");
|
|
5368
|
+
}
|
|
5369
|
+
function buildHandoffPrompt(git3) {
|
|
5370
|
+
const gitBlock = git3 || "(\uBCC0\uACBD \uC5C6\uC74C)";
|
|
5371
|
+
return [
|
|
5372
|
+
"\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.",
|
|
5373
|
+
"\uB2E4\uC74C \uC138\uC158\uC774 \uC774\uC5B4\uBC1B\uC744 \uC218 \uC788\uAC8C \uC815\uB9AC\uD574 \uC8FC\uC138\uC694.",
|
|
5374
|
+
"",
|
|
5375
|
+
"[\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.",
|
|
5376
|
+
"",
|
|
5377
|
+
"[\uC9C0\uAE08 \uC0C1\uD0DC \u2014 \uC790\uB3D9 \uC218\uC9D1\uB428]",
|
|
5378
|
+
"- \uBCC0\uACBD\uB41C \uD30C\uC77C (git status --short):",
|
|
5379
|
+
gitBlock,
|
|
5380
|
+
"",
|
|
5381
|
+
"[\uD574\uC8FC\uC138\uC694 \u2014 \uC815\uB9AC\uB9CC, \uC0C8 \uAC1C\uBC1C \uC2DC\uC791 \uAE08\uC9C0]",
|
|
5382
|
+
"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.",
|
|
5383
|
+
"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')",
|
|
5384
|
+
"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)",
|
|
5385
|
+
"4. \uC9C0\uAE08 \uCEE4\uBC0B \uAC00\uB2A5\uD55C \uC0C1\uD0DC\uC778\uC9C0 \uD310\uB2E8\uD574 \uC8FC\uC138\uC694. (\uC774\uC720 \uC124\uBA85)",
|
|
5386
|
+
"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.",
|
|
5387
|
+
"",
|
|
5388
|
+
"[\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.",
|
|
5389
|
+
"\uBAA8\uB4E0 \uC751\uB2F5\uC740 \uD55C\uAD6D\uC5B4\uB85C."
|
|
5390
|
+
].join("\n");
|
|
5391
|
+
}
|
|
5392
|
+
function emitPrompt(prompt, fileName, label) {
|
|
5393
|
+
let savedPath = "";
|
|
5394
|
+
try {
|
|
5395
|
+
mkdirSync10(VHK_DIR2, { recursive: true });
|
|
5396
|
+
savedPath = join9(VHK_DIR2, fileName);
|
|
5397
|
+
writeFileSync10(savedPath, prompt, "utf-8");
|
|
5398
|
+
} catch {
|
|
5399
|
+
savedPath = "";
|
|
5400
|
+
}
|
|
5401
|
+
const copied = copyToClipboard(prompt);
|
|
5402
|
+
if (copied) {
|
|
5403
|
+
console.log(chalk26.green(`
|
|
5404
|
+
\u{1F4CB} Claude\uC5D0\uAC8C \uC904 '${label}'\uC744 \uD074\uB9BD\uBCF4\uB4DC\uC5D0 \uBCF5\uC0AC\uD588\uC2B5\uB2C8\uB2E4! \u2705`));
|
|
5405
|
+
if (savedPath) console.log(chalk26.dim(` (\uC0AC\uBCF8 \uC800\uC7A5: ${savedPath})`));
|
|
5406
|
+
} else {
|
|
5407
|
+
console.log(chalk26.yellow(`
|
|
5408
|
+
\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:`));
|
|
5409
|
+
if (savedPath) console.log(chalk26.dim(` (\uD30C\uC77C\uB85C\uB3C4 \uC800\uC7A5\uB428: ${savedPath} \u2014 \uC5F4\uC5B4\uC11C \uBCF5\uC0AC \uAC00\uB2A5)`));
|
|
5410
|
+
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"));
|
|
5411
|
+
console.log(prompt);
|
|
5412
|
+
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"));
|
|
5413
|
+
}
|
|
5414
|
+
}
|
|
5415
|
+
async function refreshContextQuietly() {
|
|
5416
|
+
const origLog = console.log;
|
|
5417
|
+
console.log = () => {
|
|
5418
|
+
};
|
|
5419
|
+
try {
|
|
5420
|
+
await context({ compact: true });
|
|
5421
|
+
return true;
|
|
5422
|
+
} catch {
|
|
5423
|
+
return false;
|
|
5424
|
+
} finally {
|
|
5425
|
+
console.log = origLog;
|
|
5426
|
+
}
|
|
5427
|
+
}
|
|
5428
|
+
async function work() {
|
|
5429
|
+
console.log(chalk26.bold(`
|
|
5430
|
+
${ko.work.workTitle}`));
|
|
5431
|
+
console.log(chalk26.gray("\u2500".repeat(40)));
|
|
5432
|
+
if (!ensureVhkProject()) return;
|
|
5433
|
+
if (!passHardStop()) return;
|
|
5434
|
+
const git3 = gitShort();
|
|
5435
|
+
console.log("");
|
|
5436
|
+
console.log(chalk26.cyan("\u{1F4CB} \uBCC0\uACBD\uB41C \uD30C\uC77C"));
|
|
5437
|
+
printGit(git3);
|
|
5438
|
+
console.log("");
|
|
5439
|
+
console.log(chalk26.cyan("\u{1F4D6} \uADDC\uCE59"));
|
|
5440
|
+
console.log(" \xB7 CLAUDE.md \uAC00 1\uC21C\uC704 \uADDC\uCE59\uC785\uB2C8\uB2E4.");
|
|
5441
|
+
console.log(chalk26.dim(" \xB7 AGENTS.md \uB294 Codex/\uBCF4\uC870 \uC5D0\uC774\uC804\uD2B8\uC6A9 \uCC38\uACE0 \uADDC\uCE59\uC785\uB2C8\uB2E4."));
|
|
5442
|
+
const goalLine = activeGoalLine();
|
|
5443
|
+
console.log("");
|
|
5444
|
+
console.log(chalk26.cyan(`\u{1F3AF} \uD604\uC7AC \uBAA9\uD45C: ${goalLine}`));
|
|
5445
|
+
const refreshed = await refreshContextQuietly();
|
|
5446
|
+
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)"));
|
|
5447
|
+
const nextTaskPath = join9("docs", "state", "next-task.md");
|
|
5448
|
+
if (existsSync14(nextTaskPath)) {
|
|
5449
|
+
try {
|
|
5450
|
+
const lines = readFileSync7(nextTaskPath, "utf-8").split(/\r?\n/).slice(0, 12);
|
|
5451
|
+
console.log("");
|
|
5452
|
+
console.log(chalk26.cyan("\u{1F4DD} \uB2E4\uC74C \uD560 \uC77C (next-task.md)"));
|
|
5453
|
+
for (const l of lines) console.log(chalk26.dim(` ${l}`));
|
|
5454
|
+
} catch {
|
|
5455
|
+
}
|
|
5456
|
+
}
|
|
5457
|
+
const prompt = buildStartPrompt(git3, goalLine);
|
|
5458
|
+
emitPrompt(prompt, "work-prompt.md", "\uC2DC\uC791 \uD504\uB86C\uD504\uD2B8");
|
|
5459
|
+
printNextStep({
|
|
5460
|
+
message: "\uD130\uBBF8\uB110\uC5D0\uC11C claude \uB97C \uC2E4\uD589\uD55C \uB4A4, Ctrl+V \uB85C \uBD99\uC5EC\uB123\uACE0 Enter \uD558\uC138\uC694.",
|
|
5461
|
+
command: "claude"
|
|
5462
|
+
});
|
|
5463
|
+
}
|
|
5464
|
+
async function workHandoff() {
|
|
5465
|
+
console.log(chalk26.bold(`
|
|
5466
|
+
${ko.work.handoffTitle}`));
|
|
5467
|
+
console.log(chalk26.gray("\u2500".repeat(40)));
|
|
5468
|
+
if (!ensureVhkProject()) return;
|
|
5469
|
+
if (!passHardStop()) return;
|
|
5470
|
+
const git3 = gitShort();
|
|
5471
|
+
console.log("");
|
|
5472
|
+
console.log(chalk26.cyan("\u{1F4CB} \uBC14\uB010 \uD30C\uC77C"));
|
|
5473
|
+
printGit(git3);
|
|
5474
|
+
const prompt = buildHandoffPrompt(git3);
|
|
5475
|
+
emitPrompt(prompt, "handoff-prompt.md", "\uC911\uB2E8 \uC815\uB9AC \uD504\uB86C\uD504\uD2B8");
|
|
5476
|
+
console.log("");
|
|
5477
|
+
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."));
|
|
5478
|
+
printNextStep({
|
|
5479
|
+
message: "\uD130\uBBF8\uB110\uC5D0\uC11C claude \uB97C \uC2E4\uD589\uD55C \uB4A4, Ctrl+V \uB85C \uBD99\uC5EC\uB123\uACE0 Enter \uD558\uC138\uC694.",
|
|
5480
|
+
command: "claude"
|
|
5481
|
+
});
|
|
5482
|
+
}
|
|
5483
|
+
|
|
5484
|
+
// src/commands/start.ts
|
|
5485
|
+
import chalk27 from "chalk";
|
|
5218
5486
|
import inquirer11 from "inquirer";
|
|
5219
5487
|
import { simpleGit as simpleGit2 } from "simple-git";
|
|
5220
|
-
import { existsSync as
|
|
5221
|
-
import { join as
|
|
5488
|
+
import { existsSync as existsSync15 } from "fs";
|
|
5489
|
+
import { join as join10 } from "path";
|
|
5222
5490
|
var VHK_FOOTPRINT_FILES = [
|
|
5223
5491
|
"CLAUDE.md",
|
|
5224
5492
|
".cursorrules",
|
|
@@ -5227,7 +5495,7 @@ var VHK_FOOTPRINT_FILES = [
|
|
|
5227
5495
|
"docs/PRD.md"
|
|
5228
5496
|
];
|
|
5229
5497
|
function detectExistingFootprint(cwd) {
|
|
5230
|
-
return VHK_FOOTPRINT_FILES.filter((rel) =>
|
|
5498
|
+
return VHK_FOOTPRINT_FILES.filter((rel) => existsSync15(join10(cwd, rel)));
|
|
5231
5499
|
}
|
|
5232
5500
|
async function runGitInit(cwd) {
|
|
5233
5501
|
try {
|
|
@@ -5256,21 +5524,21 @@ async function runStep(label, fn) {
|
|
|
5256
5524
|
}
|
|
5257
5525
|
}
|
|
5258
5526
|
async function start(options = {}) {
|
|
5259
|
-
console.log(
|
|
5527
|
+
console.log(chalk27.bold(`
|
|
5260
5528
|
${ko.start.title}
|
|
5261
5529
|
`));
|
|
5262
|
-
console.log(
|
|
5263
|
-
console.log(
|
|
5264
|
-
console.log(
|
|
5265
|
-
console.log(
|
|
5266
|
-
console.log(
|
|
5530
|
+
console.log(chalk27.dim(ko.start.intro));
|
|
5531
|
+
console.log(chalk27.dim(` ${ko.start.step1}`));
|
|
5532
|
+
console.log(chalk27.dim(` ${ko.start.step2}`));
|
|
5533
|
+
console.log(chalk27.dim(` ${ko.start.step3}`));
|
|
5534
|
+
console.log(chalk27.dim(` ${ko.start.step4}`));
|
|
5267
5535
|
console.log();
|
|
5268
5536
|
const cwd = process.cwd();
|
|
5269
5537
|
const footprint = detectExistingFootprint(cwd);
|
|
5270
5538
|
if (footprint.length > 0 && !options.yes) {
|
|
5271
|
-
console.log(
|
|
5272
|
-
for (const f of footprint) console.log(
|
|
5273
|
-
console.log(
|
|
5539
|
+
console.log(chalk27.yellow("\u26A0\uFE0F \uC774\uBBF8 VHK \uC124\uCE58 \uD754\uC801\uC774 \uAC10\uC9C0\uB410\uC5B4\uC694:"));
|
|
5540
|
+
for (const f of footprint) console.log(chalk27.dim(` - ${f}`));
|
|
5541
|
+
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
5542
|
const { proceedExisting } = await inquirer11.prompt([{
|
|
5275
5543
|
type: "confirm",
|
|
5276
5544
|
name: "proceedExisting",
|
|
@@ -5308,7 +5576,7 @@ ${ko.start.title}
|
|
|
5308
5576
|
await runStep("[3/4] vhk mcp-init", () => mcpInit());
|
|
5309
5577
|
log.step(ko.start.step4Header);
|
|
5310
5578
|
await runStep("[4/4] vhk context", () => context());
|
|
5311
|
-
console.log(
|
|
5579
|
+
console.log(chalk27.bold.green(`
|
|
5312
5580
|
${ko.start.allDone}
|
|
5313
5581
|
`));
|
|
5314
5582
|
printNextStep({
|
|
@@ -5321,7 +5589,7 @@ ${ko.start.allDone}
|
|
|
5321
5589
|
import fs12 from "fs";
|
|
5322
5590
|
import os from "os";
|
|
5323
5591
|
import path13 from "path";
|
|
5324
|
-
import
|
|
5592
|
+
import chalk28 from "chalk";
|
|
5325
5593
|
|
|
5326
5594
|
// src/lib/vhk-cloud.ts
|
|
5327
5595
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
@@ -5339,7 +5607,7 @@ var DEFAULT_CLOUD_EXCLUDES = [
|
|
|
5339
5607
|
".gitignore"
|
|
5340
5608
|
// .vhk/ 내부 gitignore
|
|
5341
5609
|
];
|
|
5342
|
-
var
|
|
5610
|
+
var VHK_DIR3 = ".vhk";
|
|
5343
5611
|
var CLOUD_CONFIG_FILE = "cloud.json";
|
|
5344
5612
|
function loadVhkignore(rootDir) {
|
|
5345
5613
|
const ig = (0, import_ignore.default)();
|
|
@@ -5351,7 +5619,7 @@ function loadVhkignore(rootDir) {
|
|
|
5351
5619
|
return ig;
|
|
5352
5620
|
}
|
|
5353
5621
|
function collectVhkFiles(rootDir, ig = loadVhkignore(rootDir)) {
|
|
5354
|
-
const vhkDir = path12.join(rootDir,
|
|
5622
|
+
const vhkDir = path12.join(rootDir, VHK_DIR3);
|
|
5355
5623
|
let entries;
|
|
5356
5624
|
try {
|
|
5357
5625
|
entries = fs11.readdirSync(vhkDir, { withFileTypes: true });
|
|
@@ -5370,7 +5638,7 @@ function partitionGistFiles(gistFiles, ig) {
|
|
|
5370
5638
|
return { keep, excluded };
|
|
5371
5639
|
}
|
|
5372
5640
|
function readCloudConfig(rootDir) {
|
|
5373
|
-
const p = path12.join(rootDir,
|
|
5641
|
+
const p = path12.join(rootDir, VHK_DIR3, CLOUD_CONFIG_FILE);
|
|
5374
5642
|
if (!fs11.existsSync(p)) return null;
|
|
5375
5643
|
try {
|
|
5376
5644
|
const parsed = readJsonFile(p);
|
|
@@ -5383,7 +5651,7 @@ function readCloudConfig(rootDir) {
|
|
|
5383
5651
|
}
|
|
5384
5652
|
}
|
|
5385
5653
|
function writeCloudConfig(rootDir, config) {
|
|
5386
|
-
const vhkDir = path12.join(rootDir,
|
|
5654
|
+
const vhkDir = path12.join(rootDir, VHK_DIR3);
|
|
5387
5655
|
fs11.mkdirSync(vhkDir, { recursive: true });
|
|
5388
5656
|
const p = path12.join(vhkDir, CLOUD_CONFIG_FILE);
|
|
5389
5657
|
fs11.writeFileSync(p, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
@@ -5413,14 +5681,14 @@ ${CLOUD_CONFIG_FILE}
|
|
|
5413
5681
|
function ensureGhReady() {
|
|
5414
5682
|
const ver = safeExecFile("gh", ["--version"]);
|
|
5415
5683
|
if (!ver.ok) {
|
|
5416
|
-
console.log(
|
|
5417
|
-
console.log(
|
|
5684
|
+
console.log(chalk28.red(` ${ko.cloud.noGh}`));
|
|
5685
|
+
console.log(chalk28.dim(" \uC124\uCE58: https://cli.github.com/ (\uC124\uCE58 \uD6C4 `gh auth login`)"));
|
|
5418
5686
|
return false;
|
|
5419
5687
|
}
|
|
5420
5688
|
const auth = safeExecFile("gh", ["auth", "status"]);
|
|
5421
5689
|
if (!auth.ok) {
|
|
5422
|
-
console.log(
|
|
5423
|
-
console.log(
|
|
5690
|
+
console.log(chalk28.red(` ${ko.cloud.noAuth}`));
|
|
5691
|
+
console.log(chalk28.dim(" \uC2E4\uD589: gh auth login (gist \uAD8C\uD55C \uD544\uC694)"));
|
|
5424
5692
|
return false;
|
|
5425
5693
|
}
|
|
5426
5694
|
return true;
|
|
@@ -5433,26 +5701,26 @@ function parseGistId(output) {
|
|
|
5433
5701
|
return null;
|
|
5434
5702
|
}
|
|
5435
5703
|
async function cloudPush() {
|
|
5436
|
-
console.log(
|
|
5704
|
+
console.log(chalk28.bold(`
|
|
5437
5705
|
${ko.cloud.pushTitle}
|
|
5438
5706
|
`));
|
|
5439
5707
|
const cwd = process.cwd();
|
|
5440
|
-
if (!fs12.existsSync(path13.join(cwd,
|
|
5441
|
-
console.log(
|
|
5708
|
+
if (!fs12.existsSync(path13.join(cwd, VHK_DIR3))) {
|
|
5709
|
+
console.log(chalk28.yellow(` ${ko.cloud.noVhkDir}`));
|
|
5442
5710
|
return;
|
|
5443
5711
|
}
|
|
5444
5712
|
const ig = loadVhkignore(cwd);
|
|
5445
5713
|
const files = collectVhkFiles(cwd, ig);
|
|
5446
5714
|
if (files.length === 0) {
|
|
5447
|
-
console.log(
|
|
5715
|
+
console.log(chalk28.yellow(` ${ko.cloud.nothingToSync}`));
|
|
5448
5716
|
return;
|
|
5449
5717
|
}
|
|
5450
5718
|
if (!ensureGhReady()) {
|
|
5451
5719
|
process.exitCode = 1;
|
|
5452
5720
|
return;
|
|
5453
5721
|
}
|
|
5454
|
-
const filePaths = files.map((f) => path13.join(cwd,
|
|
5455
|
-
console.log(
|
|
5722
|
+
const filePaths = files.map((f) => path13.join(cwd, VHK_DIR3, f));
|
|
5723
|
+
console.log(chalk28.dim(` \u{1F4E6} \uBC31\uC5C5 \uB300\uC0C1 ${files.length}\uAC1C: ${files.join(", ")}
|
|
5456
5724
|
`));
|
|
5457
5725
|
const existing = readCloudConfig(cwd);
|
|
5458
5726
|
const desc = `vhk .vhk backup \u2014 ${path13.basename(cwd)}`;
|
|
@@ -5464,8 +5732,8 @@ ${ko.cloud.pushTitle}
|
|
|
5464
5732
|
const args = gistFiles.includes(name) ? ["gist", "edit", existing.gistId, "-f", name, src] : ["gist", "edit", existing.gistId, "-a", src];
|
|
5465
5733
|
const res2 = safeExecFile("gh", args);
|
|
5466
5734
|
if (!res2.ok) {
|
|
5467
|
-
console.log(
|
|
5468
|
-
console.log(
|
|
5735
|
+
console.log(chalk28.red(` ${ko.cloud.pushFail}: ${name}`));
|
|
5736
|
+
console.log(chalk28.dim(` ${res2.err}`));
|
|
5469
5737
|
process.exitCode = 1;
|
|
5470
5738
|
return;
|
|
5471
5739
|
}
|
|
@@ -5480,15 +5748,15 @@ ${ko.cloud.pushTitle}
|
|
|
5480
5748
|
if (!purgeFailed.includes(name)) purgeFailed.push(name);
|
|
5481
5749
|
}
|
|
5482
5750
|
}
|
|
5483
|
-
console.log(
|
|
5484
|
-
console.log(
|
|
5751
|
+
console.log(chalk28.green.bold(` ${ko.cloud.pushDone}`));
|
|
5752
|
+
console.log(chalk28.dim(` gist: ${existing.gistId} (\uAC31\uC2E0)`));
|
|
5485
5753
|
if (excluded.length > 0) {
|
|
5486
5754
|
const purged = excluded.filter((n) => !purgeFailed.includes(n));
|
|
5487
5755
|
if (purged.length > 0) {
|
|
5488
|
-
console.log(
|
|
5756
|
+
console.log(chalk28.dim(` \u{1F512} \uC81C\uC678 \uB300\uC0C1 ${purged.length}\uAC1C gist \uC5D0\uC11C \uC81C\uAC70: ${purged.join(", ")}`));
|
|
5489
5757
|
}
|
|
5490
5758
|
if (purgeFailed.length > 0) {
|
|
5491
|
-
console.log(
|
|
5759
|
+
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
5760
|
}
|
|
5493
5761
|
}
|
|
5494
5762
|
printPushNext();
|
|
@@ -5496,32 +5764,32 @@ ${ko.cloud.pushTitle}
|
|
|
5496
5764
|
}
|
|
5497
5765
|
const res = safeExecFile("gh", ["gist", "create", "--desc", desc, ...filePaths]);
|
|
5498
5766
|
if (!res.ok) {
|
|
5499
|
-
console.log(
|
|
5500
|
-
console.log(
|
|
5767
|
+
console.log(chalk28.red(` ${ko.cloud.pushFail}`));
|
|
5768
|
+
console.log(chalk28.dim(` ${res.err || res.out}`));
|
|
5501
5769
|
process.exitCode = 1;
|
|
5502
5770
|
return;
|
|
5503
5771
|
}
|
|
5504
5772
|
const gistId = parseGistId(res.out);
|
|
5505
5773
|
if (!gistId) {
|
|
5506
|
-
console.log(
|
|
5507
|
-
console.log(
|
|
5774
|
+
console.log(chalk28.red(` ${ko.cloud.pushFail} \u2014 gist id \uD30C\uC2F1 \uC2E4\uD328`));
|
|
5775
|
+
console.log(chalk28.dim(` \uCD9C\uB825: ${res.out}`));
|
|
5508
5776
|
process.exitCode = 1;
|
|
5509
5777
|
return;
|
|
5510
5778
|
}
|
|
5511
5779
|
writeCloudConfig(cwd, { gistId });
|
|
5512
|
-
console.log(
|
|
5513
|
-
console.log(
|
|
5780
|
+
console.log(chalk28.green.bold(` ${ko.cloud.pushDone}`));
|
|
5781
|
+
console.log(chalk28.dim(` gist: ${gistId} (\uC2E0\uADDC, secret) \u2192 .vhk/cloud.json \uC800\uC7A5`));
|
|
5514
5782
|
printPushNext();
|
|
5515
5783
|
}
|
|
5516
5784
|
async function cloudPull(gistIdArg) {
|
|
5517
|
-
console.log(
|
|
5785
|
+
console.log(chalk28.bold(`
|
|
5518
5786
|
${ko.cloud.pullTitle}
|
|
5519
5787
|
`));
|
|
5520
5788
|
const cwd = process.cwd();
|
|
5521
5789
|
const gistId = gistIdArg || readCloudConfig(cwd)?.gistId;
|
|
5522
5790
|
if (!gistId) {
|
|
5523
|
-
console.log(
|
|
5524
|
-
console.log(
|
|
5791
|
+
console.log(chalk28.yellow(` ${ko.cloud.noGistId}`));
|
|
5792
|
+
console.log(chalk28.dim(" \uC0AC\uC6A9\uBC95: vhk cloud pull <gistId> (\uB610\uB294 cloud.json \uC774 \uC788\uB294 \uACF3\uC5D0\uC11C \uC2E4\uD589)"));
|
|
5525
5793
|
return;
|
|
5526
5794
|
}
|
|
5527
5795
|
if (!ensureGhReady()) {
|
|
@@ -5530,34 +5798,34 @@ ${ko.cloud.pullTitle}
|
|
|
5530
5798
|
}
|
|
5531
5799
|
const allNames = listGistFiles(gistId);
|
|
5532
5800
|
if (allNames.length === 0) {
|
|
5533
|
-
console.log(
|
|
5801
|
+
console.log(chalk28.red(` ${ko.cloud.pullFail} \u2014 gist \uBE44\uC5C8\uAC70\uB098 \uC811\uADFC \uBD88\uAC00: ${gistId}`));
|
|
5534
5802
|
process.exitCode = 1;
|
|
5535
5803
|
return;
|
|
5536
5804
|
}
|
|
5537
5805
|
const { keep: names, excluded: skipped } = partitionGistFiles(allNames, loadVhkignore(cwd));
|
|
5538
5806
|
if (skipped.length > 0) {
|
|
5539
|
-
console.log(
|
|
5807
|
+
console.log(chalk28.dim(` \u{1F512} \uC81C\uC678 \uB300\uC0C1 ${skipped.length}\uAC1C \uBCF5\uC6D0 \uC2A4\uD0B5: ${skipped.join(", ")}`));
|
|
5540
5808
|
}
|
|
5541
5809
|
if (names.length === 0) {
|
|
5542
|
-
console.log(
|
|
5810
|
+
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
5811
|
return;
|
|
5544
5812
|
}
|
|
5545
|
-
const vhkDir = path13.join(cwd,
|
|
5813
|
+
const vhkDir = path13.join(cwd, VHK_DIR3);
|
|
5546
5814
|
fs12.mkdirSync(vhkDir, { recursive: true });
|
|
5547
5815
|
let restored = 0;
|
|
5548
5816
|
for (const name of names) {
|
|
5549
5817
|
const res = safeExecFile("gh", ["gist", "view", gistId, "-f", name, "--raw"]);
|
|
5550
5818
|
if (!res.ok) {
|
|
5551
|
-
console.log(
|
|
5552
|
-
console.log(
|
|
5819
|
+
console.log(chalk28.red(` ${ko.cloud.pullFail}: ${name}`));
|
|
5820
|
+
console.log(chalk28.dim(` ${res.err}`));
|
|
5553
5821
|
continue;
|
|
5554
5822
|
}
|
|
5555
5823
|
fs12.writeFileSync(path13.join(vhkDir, name), ensureTrailingNewline(res.out), "utf-8");
|
|
5556
5824
|
restored++;
|
|
5557
5825
|
}
|
|
5558
5826
|
writeCloudConfig(cwd, { gistId });
|
|
5559
|
-
console.log(
|
|
5560
|
-
console.log(
|
|
5827
|
+
console.log(chalk28.green.bold(` ${ko.cloud.pullDone}`));
|
|
5828
|
+
console.log(chalk28.dim(` ${restored}\uAC1C \uD30C\uC77C \uBCF5\uC6D0 (gist: ${gistId})`));
|
|
5561
5829
|
printNextStep({
|
|
5562
5830
|
message: "\uD074\uB77C\uC6B0\uB4DC\uC5D0\uC11C .vhk/ \uBCF5\uC6D0 \uC644\uB8CC!",
|
|
5563
5831
|
command: "vhk \uB9E5\uB77D",
|
|
@@ -5605,7 +5873,7 @@ function printPushNext() {
|
|
|
5605
5873
|
}
|
|
5606
5874
|
|
|
5607
5875
|
// src/commands/help.ts
|
|
5608
|
-
import
|
|
5876
|
+
import chalk29 from "chalk";
|
|
5609
5877
|
var QUICK_ACTIONS = [
|
|
5610
5878
|
{ say: "\uC0C1\uD0DC \uC54C\uB824\uC918", does: "vhk status" },
|
|
5611
5879
|
{ say: "\uBB50 \uBC14\uB00C\uC5C8\uC5B4?", does: "vhk diff" },
|
|
@@ -5619,21 +5887,21 @@ var QUICK_ACTIONS = [
|
|
|
5619
5887
|
{ say: "\uC804\uCCB4 \uBA85\uB839\uC5B4 \uBCF4\uAE30", does: "vhk --help" }
|
|
5620
5888
|
];
|
|
5621
5889
|
function quickActions() {
|
|
5622
|
-
console.log(
|
|
5623
|
-
console.log(
|
|
5890
|
+
console.log(chalk29.bold("\n\u{1F9ED} VHK \u2014 \uC774\uB807\uAC8C \uB9D0\uD558\uBA74 \uB429\uB2C8\uB2E4 (quick actions)"));
|
|
5891
|
+
console.log(chalk29.gray("\u2500".repeat(40)));
|
|
5624
5892
|
for (const a of QUICK_ACTIONS) {
|
|
5625
|
-
console.log(` "${
|
|
5893
|
+
console.log(` "${chalk29.cyan(a.say)}" \u2192 ${chalk29.dim(a.does)}`);
|
|
5626
5894
|
}
|
|
5627
|
-
console.log(
|
|
5895
|
+
console.log(chalk29.gray("\n \uC804\uCCB4 \uBA85\uB839\uC740 `vhk --help` \uB610\uB294 COMMANDS.md \uB97C \uBCF4\uC138\uC694."));
|
|
5628
5896
|
console.log("");
|
|
5629
5897
|
}
|
|
5630
5898
|
|
|
5631
5899
|
// src/commands/mode.ts
|
|
5632
|
-
import
|
|
5900
|
+
import chalk30 from "chalk";
|
|
5633
5901
|
|
|
5634
5902
|
// src/lib/config.ts
|
|
5635
|
-
import { existsSync as
|
|
5636
|
-
import { join as
|
|
5903
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "fs";
|
|
5904
|
+
import { join as join11 } from "path";
|
|
5637
5905
|
|
|
5638
5906
|
// src/lib/safety-mode.ts
|
|
5639
5907
|
var SAFETY_MODES = ["lite", "standard", "strict"];
|
|
@@ -5649,11 +5917,11 @@ function isSafetyMode(value) {
|
|
|
5649
5917
|
|
|
5650
5918
|
// src/lib/config.ts
|
|
5651
5919
|
var CONFIG_DIR = ".vhk";
|
|
5652
|
-
var CONFIG_PATH =
|
|
5920
|
+
var CONFIG_PATH = join11(CONFIG_DIR, "config.json");
|
|
5653
5921
|
var DEFAULT_CONFIG = { safetyMode: DEFAULT_SAFETY_MODE };
|
|
5654
5922
|
function readConfig(rootDir = process.cwd()) {
|
|
5655
|
-
const full =
|
|
5656
|
-
if (!
|
|
5923
|
+
const full = join11(rootDir, CONFIG_PATH);
|
|
5924
|
+
if (!existsSync16(full)) return { ...DEFAULT_CONFIG };
|
|
5657
5925
|
try {
|
|
5658
5926
|
const raw = readJsonFile(full);
|
|
5659
5927
|
return {
|
|
@@ -5664,23 +5932,23 @@ function readConfig(rootDir = process.cwd()) {
|
|
|
5664
5932
|
}
|
|
5665
5933
|
}
|
|
5666
5934
|
function writeConfig(config, rootDir = process.cwd()) {
|
|
5667
|
-
|
|
5668
|
-
|
|
5935
|
+
mkdirSync11(join11(rootDir, CONFIG_DIR), { recursive: true });
|
|
5936
|
+
writeFileSync11(join11(rootDir, CONFIG_PATH), JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
5669
5937
|
}
|
|
5670
5938
|
|
|
5671
5939
|
// src/commands/mode.ts
|
|
5672
5940
|
async function mode(target) {
|
|
5673
|
-
console.log(
|
|
5674
|
-
console.log(
|
|
5941
|
+
console.log(chalk30.bold("\n\u{1F6E1}\uFE0F Safety Mode"));
|
|
5942
|
+
console.log(chalk30.gray("\u2500".repeat(40)));
|
|
5675
5943
|
const current = readConfig().safetyMode;
|
|
5676
5944
|
if (!target) {
|
|
5677
|
-
console.log(
|
|
5678
|
-
\uD604\uC7AC \uBAA8\uB4DC: ${
|
|
5679
|
-
console.log(
|
|
5945
|
+
console.log(chalk30.cyan(`
|
|
5946
|
+
\uD604\uC7AC \uBAA8\uB4DC: ${chalk30.bold(current)}`));
|
|
5947
|
+
console.log(chalk30.dim(` ${SAFETY_MODE_DESC[current]}`));
|
|
5680
5948
|
console.log("");
|
|
5681
5949
|
for (const m of SAFETY_MODES) {
|
|
5682
5950
|
const mark = m === current ? "\u25CF" : "\u25CB";
|
|
5683
|
-
console.log(` ${mark} ${m.padEnd(9)} ${
|
|
5951
|
+
console.log(` ${mark} ${m.padEnd(9)} ${chalk30.dim(SAFETY_MODE_DESC[m])}`);
|
|
5684
5952
|
}
|
|
5685
5953
|
printNextStep({
|
|
5686
5954
|
message: "\uBAA8\uB4DC\uB97C \uBC14\uAFB8\uB824\uBA74:",
|
|
@@ -5690,23 +5958,23 @@ async function mode(target) {
|
|
|
5690
5958
|
return;
|
|
5691
5959
|
}
|
|
5692
5960
|
if (!isSafetyMode(target)) {
|
|
5693
|
-
console.log(
|
|
5961
|
+
console.log(chalk30.red(`
|
|
5694
5962
|
\u274C \uC54C \uC218 \uC5C6\uB294 \uBAA8\uB4DC: ${target}`));
|
|
5695
|
-
console.log(
|
|
5963
|
+
console.log(chalk30.dim(` \uAC00\uB2A5: ${SAFETY_MODES.join(" | ")}`));
|
|
5696
5964
|
process.exitCode = 1;
|
|
5697
5965
|
return;
|
|
5698
5966
|
}
|
|
5699
5967
|
writeConfig({ ...readConfig(), safetyMode: target });
|
|
5700
|
-
console.log(
|
|
5701
|
-
\u2705 Safety Mode \u2192 ${
|
|
5702
|
-
console.log(
|
|
5968
|
+
console.log(chalk30.green(`
|
|
5969
|
+
\u2705 Safety Mode \u2192 ${chalk30.bold(target)}`));
|
|
5970
|
+
console.log(chalk30.dim(` ${SAFETY_MODE_DESC[target]}`));
|
|
5703
5971
|
}
|
|
5704
5972
|
|
|
5705
5973
|
// src/commands/verify.ts
|
|
5706
5974
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
5707
|
-
import { existsSync as
|
|
5708
|
-
import { join as
|
|
5709
|
-
import
|
|
5975
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync12, writeFileSync as writeFileSync12 } from "fs";
|
|
5976
|
+
import { join as join12 } from "path";
|
|
5977
|
+
import chalk31 from "chalk";
|
|
5710
5978
|
|
|
5711
5979
|
// src/commands/verify-report.ts
|
|
5712
5980
|
function escapeHtml(text) {
|
|
@@ -5814,13 +6082,13 @@ ${actions}
|
|
|
5814
6082
|
|
|
5815
6083
|
// src/commands/verify.ts
|
|
5816
6084
|
var REPORT_SCHEMA_VERSION = 1;
|
|
5817
|
-
var REPORT_DIR_REL =
|
|
5818
|
-
var REPORT_PATH_REL =
|
|
5819
|
-
var REPORT_HTML_PATH_REL =
|
|
6085
|
+
var REPORT_DIR_REL = join12(".vhk", "reports");
|
|
6086
|
+
var REPORT_PATH_REL = join12(REPORT_DIR_REL, "latest.json");
|
|
6087
|
+
var REPORT_HTML_PATH_REL = join12(REPORT_DIR_REL, "latest.html");
|
|
5820
6088
|
var SHIM = /* @__PURE__ */ new Set(["pnpm", "npm", "npx", "yarn"]);
|
|
5821
6089
|
function detectPm(cwd) {
|
|
5822
|
-
if (
|
|
5823
|
-
if (
|
|
6090
|
+
if (existsSync17(join12(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
6091
|
+
if (existsSync17(join12(cwd, "yarn.lock"))) return "yarn";
|
|
5824
6092
|
return "npm";
|
|
5825
6093
|
}
|
|
5826
6094
|
function execGate(cmd, args, cwd) {
|
|
@@ -5863,8 +6131,8 @@ function runScriptGate(id, label, cwd, pm, argvFor) {
|
|
|
5863
6131
|
};
|
|
5864
6132
|
}
|
|
5865
6133
|
function readPackageScripts(cwd) {
|
|
5866
|
-
const pkgPath =
|
|
5867
|
-
if (!
|
|
6134
|
+
const pkgPath = join12(cwd, "package.json");
|
|
6135
|
+
if (!existsSync17(pkgPath)) return {};
|
|
5868
6136
|
try {
|
|
5869
6137
|
const pkg = readJsonFile(pkgPath);
|
|
5870
6138
|
return pkg.scripts ?? {};
|
|
@@ -5879,7 +6147,7 @@ function runGates(cwd) {
|
|
|
5879
6147
|
gates.push(
|
|
5880
6148
|
runScriptGate("typecheck", "tsc --noEmit", cwd, pm, () => {
|
|
5881
6149
|
if (scripts.typecheck) return ["run", "typecheck"];
|
|
5882
|
-
if (
|
|
6150
|
+
if (existsSync17(join12(cwd, "tsconfig.json"))) return pm === "npm" ? ["exec", "--", "tsc", "--noEmit"] : ["exec", "tsc", "--noEmit"];
|
|
5883
6151
|
return null;
|
|
5884
6152
|
})
|
|
5885
6153
|
);
|
|
@@ -5958,10 +6226,10 @@ function buildReport(gates, generatedAt, date) {
|
|
|
5958
6226
|
function verifyEvidence(cwd = process.cwd()) {
|
|
5959
6227
|
const gates = runGates(cwd);
|
|
5960
6228
|
const report = buildReport(gates, (/* @__PURE__ */ new Date()).toISOString(), localDate());
|
|
5961
|
-
const dir =
|
|
5962
|
-
|
|
5963
|
-
const path14 =
|
|
5964
|
-
|
|
6229
|
+
const dir = join12(cwd, REPORT_DIR_REL);
|
|
6230
|
+
mkdirSync12(dir, { recursive: true });
|
|
6231
|
+
const path14 = join12(cwd, REPORT_PATH_REL);
|
|
6232
|
+
writeFileSync12(path14, JSON.stringify(report, null, 2) + "\n", "utf-8");
|
|
5965
6233
|
try {
|
|
5966
6234
|
ensureVhkIgnored(cwd, "reports/");
|
|
5967
6235
|
} catch {
|
|
@@ -5969,44 +6237,44 @@ function verifyEvidence(cwd = process.cwd()) {
|
|
|
5969
6237
|
return { report, path: REPORT_PATH_REL };
|
|
5970
6238
|
}
|
|
5971
6239
|
var STATUS_BADGE = {
|
|
5972
|
-
PASS:
|
|
5973
|
-
WARN:
|
|
5974
|
-
FAIL:
|
|
6240
|
+
PASS: chalk31.green.bold("PASS"),
|
|
6241
|
+
WARN: chalk31.yellow.bold("WARN"),
|
|
6242
|
+
FAIL: chalk31.red.bold("FAIL")
|
|
5975
6243
|
};
|
|
5976
6244
|
async function renderVerifyReport(cwd, opts) {
|
|
5977
|
-
const jsonPath =
|
|
6245
|
+
const jsonPath = join12(cwd, REPORT_PATH_REL);
|
|
5978
6246
|
let report;
|
|
5979
|
-
if (
|
|
6247
|
+
if (existsSync17(jsonPath)) {
|
|
5980
6248
|
try {
|
|
5981
6249
|
report = readJsonFile(jsonPath);
|
|
5982
6250
|
} catch {
|
|
5983
|
-
console.log(
|
|
6251
|
+
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
6252
|
report = verifyEvidence(cwd).report;
|
|
5985
6253
|
}
|
|
5986
6254
|
} else {
|
|
5987
|
-
console.log(
|
|
6255
|
+
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
6256
|
report = verifyEvidence(cwd).report;
|
|
5989
6257
|
}
|
|
5990
6258
|
const html = renderReportHtml(report);
|
|
5991
|
-
const htmlPath =
|
|
6259
|
+
const htmlPath = join12(cwd, REPORT_HTML_PATH_REL);
|
|
5992
6260
|
try {
|
|
5993
|
-
|
|
5994
|
-
|
|
6261
|
+
mkdirSync12(join12(cwd, REPORT_DIR_REL), { recursive: true });
|
|
6262
|
+
writeFileSync12(htmlPath, html, "utf-8");
|
|
5995
6263
|
} catch (e) {
|
|
5996
6264
|
console.error(
|
|
5997
|
-
|
|
6265
|
+
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
6266
|
);
|
|
5999
|
-
console.error(
|
|
6267
|
+
console.error(chalk31.dim(" \uD574\uB2F9 \uACBD\uB85C\uC758 \uC4F0\uAE30 \uAD8C\uD55C\uC744 \uD655\uC778\uD558\uC138\uC694."));
|
|
6000
6268
|
process.exitCode = 1;
|
|
6001
6269
|
return;
|
|
6002
6270
|
}
|
|
6003
|
-
console.log(
|
|
6271
|
+
console.log(chalk31.bold("\n\u{1F50E} \uAC80\uC99D \uB9AC\uD3EC\uD2B8 (verify --report)"));
|
|
6004
6272
|
console.log(` \uACB0\uACFC: ${STATUS_BADGE[report.status]}`);
|
|
6005
|
-
console.log(
|
|
6273
|
+
console.log(chalk31.dim(` \u{1F4C4} HTML: ${REPORT_HTML_PATH_REL}`));
|
|
6006
6274
|
process.exitCode = report.status === "FAIL" ? 1 : 0;
|
|
6007
6275
|
if (opts.open) {
|
|
6008
6276
|
if (isInteractive()) openReportInBrowser(htmlPath);
|
|
6009
|
-
else console.log(
|
|
6277
|
+
else console.log(chalk31.dim(" (\uBE44\uB300\uD654\uD615/CI/MCP \u2014 --open \uC790\uB3D9 \uC2A4\uD0B5)"));
|
|
6010
6278
|
return;
|
|
6011
6279
|
}
|
|
6012
6280
|
printNextStep({
|
|
@@ -6024,8 +6292,8 @@ function openReportInBrowser(filePath) {
|
|
|
6024
6292
|
} else {
|
|
6025
6293
|
result = safeExecFile("xdg-open", [filePath]);
|
|
6026
6294
|
}
|
|
6027
|
-
if (result.ok) console.log(
|
|
6028
|
-
else console.log(
|
|
6295
|
+
if (result.ok) console.log(chalk31.green(" \u2705 \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4."));
|
|
6296
|
+
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
6297
|
}
|
|
6030
6298
|
async function verify(opts = {}) {
|
|
6031
6299
|
if (!ensureNotHardStopped("verify")) return;
|
|
@@ -6040,21 +6308,21 @@ async function verify(opts = {}) {
|
|
|
6040
6308
|
process.exitCode = report.status === "FAIL" ? 1 : 0;
|
|
6041
6309
|
return;
|
|
6042
6310
|
}
|
|
6043
|
-
console.log(
|
|
6044
|
-
console.log(
|
|
6311
|
+
console.log(chalk31.bold("\n\u{1F50E} \uAC80\uC99D \uBB36\uC74C (verify)"));
|
|
6312
|
+
console.log(chalk31.gray("\u2500".repeat(40)));
|
|
6045
6313
|
const mode2 = readConfig().safetyMode;
|
|
6046
|
-
console.log(
|
|
6047
|
-
const icon = (s2) => s2 === "pass" ?
|
|
6314
|
+
console.log(chalk31.dim(` \uD604\uC7AC Safety Mode: ${mode2} \u2014 ${SAFETY_MODE_DESC[mode2]}`));
|
|
6315
|
+
const icon = (s2) => s2 === "pass" ? chalk31.green("\u2713") : s2 === "fail" ? chalk31.red("\u2717") : chalk31.yellow("\u2298");
|
|
6048
6316
|
for (const g of report.gates) {
|
|
6049
|
-
const tail = g.detail ?
|
|
6317
|
+
const tail = g.detail ? chalk31.dim(` \u2014 ${g.detail}`) : "";
|
|
6050
6318
|
console.log(` ${icon(g.status)} ${g.label}${tail}`);
|
|
6051
6319
|
}
|
|
6052
6320
|
const s = report.summary;
|
|
6053
6321
|
console.log(
|
|
6054
6322
|
`
|
|
6055
|
-
\uACB0\uACFC: ${STATUS_BADGE[report.status]} ` +
|
|
6323
|
+
\uACB0\uACFC: ${STATUS_BADGE[report.status]} ` + chalk31.dim(`(pass ${s.pass} / fail ${s.fail} / skip ${s.skip}, \uCD1D ${s.total})`)
|
|
6056
6324
|
);
|
|
6057
|
-
console.log(
|
|
6325
|
+
console.log(chalk31.dim(` \u{1F4C4} \uC99D\uAC70: ${path14}`));
|
|
6058
6326
|
process.exitCode = report.status === "FAIL" ? 1 : 0;
|
|
6059
6327
|
if (report.status === "FAIL") {
|
|
6060
6328
|
printNextStep({
|
|
@@ -6073,9 +6341,9 @@ async function verify(opts = {}) {
|
|
|
6073
6341
|
}
|
|
6074
6342
|
|
|
6075
6343
|
// src/commands/review.ts
|
|
6076
|
-
import { existsSync as
|
|
6077
|
-
import { join as
|
|
6078
|
-
import
|
|
6344
|
+
import { existsSync as existsSync18, writeFileSync as writeFileSync13 } from "fs";
|
|
6345
|
+
import { join as join13 } from "path";
|
|
6346
|
+
import chalk32 from "chalk";
|
|
6079
6347
|
var GOALS_DIR2 = "goals";
|
|
6080
6348
|
var COVERAGE_MIN = 0.5;
|
|
6081
6349
|
var STALE_AGE_MS = 6 * 60 * 60 * 1e3;
|
|
@@ -6207,22 +6475,22 @@ function resolveGoal(optId, goals) {
|
|
|
6207
6475
|
return goals.find((g) => g.frontmatter.id === id) ?? null;
|
|
6208
6476
|
}
|
|
6209
6477
|
var CONFIDENCE_LABEL = {
|
|
6210
|
-
low:
|
|
6211
|
-
medium:
|
|
6212
|
-
high:
|
|
6478
|
+
low: chalk32.red.bold("\uB0AE\uC74C (\uAC70\uC9D3\uC644\uB8CC \uC758\uC2EC \uB610\uB294 \uC644\uB8CC \uC8FC\uC7A5 \uC5C6\uC74C)"),
|
|
6479
|
+
medium: chalk32.yellow.bold("\uC911\uAC04 (\uCEE4\uBC84\uB9AC\uC9C0/\uC2E0\uC120\uB3C4 \uBD80\uC871 \u2014 \uC99D\uAC70 \uC5C6\uC74C \u2260 \uD1B5\uACFC)"),
|
|
6480
|
+
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
6481
|
};
|
|
6214
6482
|
async function review(opts = {}) {
|
|
6215
6483
|
if (!ensureNotHardStopped("review")) return;
|
|
6216
6484
|
const cwd = process.cwd();
|
|
6217
6485
|
const goals = listGoals(GOALS_DIR2);
|
|
6218
6486
|
if (goals.length === 0) {
|
|
6219
|
-
console.error(
|
|
6487
|
+
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
6488
|
process.exitCode = 1;
|
|
6221
6489
|
return;
|
|
6222
6490
|
}
|
|
6223
6491
|
const goal = resolveGoal(opts.id, goals);
|
|
6224
6492
|
if (!goal || typeof goal.frontmatter.id !== "number") {
|
|
6225
|
-
console.error(
|
|
6493
|
+
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
6494
|
process.exitCode = 1;
|
|
6227
6495
|
return;
|
|
6228
6496
|
}
|
|
@@ -6230,11 +6498,11 @@ async function review(opts = {}) {
|
|
|
6230
6498
|
const goalStatus = goal.frontmatter.status ?? "NOT_STARTED";
|
|
6231
6499
|
const checks = parseCompletionChecks(goal.body);
|
|
6232
6500
|
if (opts.id === void 0 && goalStatus === "NOT_STARTED") {
|
|
6233
|
-
console.error(
|
|
6501
|
+
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
6502
|
}
|
|
6235
|
-
const jsonPath =
|
|
6236
|
-
if (!
|
|
6237
|
-
console.error(
|
|
6503
|
+
const jsonPath = join13(cwd, REPORT_PATH_REL);
|
|
6504
|
+
if (!existsSync18(jsonPath)) {
|
|
6505
|
+
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
6506
|
printNextStep({
|
|
6239
6507
|
message: "\uC99D\uAC70(latest.json)\uAC00 \uC788\uC5B4\uC57C review \uAC00 \uAD50\uCC28\uAC80\uC99D\uD569\uB2C8\uB2E4:",
|
|
6240
6508
|
command: "vhk verify",
|
|
@@ -6247,7 +6515,7 @@ async function review(opts = {}) {
|
|
|
6247
6515
|
try {
|
|
6248
6516
|
report = readJsonFile(jsonPath);
|
|
6249
6517
|
} catch {
|
|
6250
|
-
console.error(
|
|
6518
|
+
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
6519
|
process.exitCode = 1;
|
|
6252
6520
|
return;
|
|
6253
6521
|
}
|
|
@@ -6259,44 +6527,44 @@ async function review(opts = {}) {
|
|
|
6259
6527
|
goalStatus,
|
|
6260
6528
|
reportStatus: report.status
|
|
6261
6529
|
};
|
|
6262
|
-
console.log(
|
|
6530
|
+
console.log(chalk32.bold(`
|
|
6263
6531
|
\u{1F52C} \uC801\uB300\uC801 \uC790\uAE30\uAC80\uC99D (review) \u2014 Goal ${goalId}`));
|
|
6264
|
-
console.log(
|
|
6532
|
+
console.log(chalk32.gray("\u2500".repeat(44)));
|
|
6265
6533
|
console.log(
|
|
6266
|
-
|
|
6534
|
+
chalk32.dim(
|
|
6267
6535
|
` 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
6536
|
)
|
|
6269
6537
|
);
|
|
6270
|
-
console.log(
|
|
6538
|
+
console.log(chalk32.dim(` \uC99D\uAC70 \uC2E0\uC120\uB3C4: ${result.freshness.note}`));
|
|
6271
6539
|
if (result.checkedCount === 0) {
|
|
6272
|
-
console.log(
|
|
6540
|
+
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
6541
|
}
|
|
6274
6542
|
if (result.suspicions.length > 0) {
|
|
6275
|
-
console.log(
|
|
6543
|
+
console.log(chalk32.red.bold(`
|
|
6276
6544
|
\u{1F6A9} \uAC70\uC9D3\uC644\uB8CC \uC758\uC2EC ${result.suspicions.length}\uAC74`));
|
|
6277
|
-
for (const s of result.suspicions) console.log(
|
|
6545
|
+
for (const s of result.suspicions) console.log(chalk32.red(` \u2717 ${s.check}
|
|
6278
6546
|
\u21B3 ${s.reason}`));
|
|
6279
6547
|
}
|
|
6280
6548
|
if (result.gaps.length > 0) {
|
|
6281
|
-
console.log(
|
|
6549
|
+
console.log(chalk32.yellow.bold(`
|
|
6282
6550
|
\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(
|
|
6551
|
+
for (const g of result.gaps) console.log(chalk32.yellow(` ? ${g.check}`));
|
|
6284
6552
|
}
|
|
6285
6553
|
if (result.checkedCount > 0 && result.suspicions.length === 0 && result.gaps.length === 0) {
|
|
6286
|
-
console.log(
|
|
6554
|
+
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
6555
|
}
|
|
6288
6556
|
console.log(`
|
|
6289
6557
|
\uC2E0\uB8B0\uB3C4: ${CONFIDENCE_LABEL[result.confidence]}`);
|
|
6290
|
-
console.log(
|
|
6558
|
+
console.log(chalk32.yellow(`
|
|
6291
6559
|
${result.disclaimer}`));
|
|
6292
6560
|
let mergeOk = false;
|
|
6293
6561
|
try {
|
|
6294
6562
|
const merged = { ...report, review: result };
|
|
6295
|
-
|
|
6563
|
+
writeFileSync13(jsonPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
6296
6564
|
mergeOk = true;
|
|
6297
|
-
console.log(
|
|
6565
|
+
console.log(chalk32.dim(` \u{1F4C4} \uD310\uC815 \uBCD1\uD569: ${REPORT_PATH_REL} (review \uC139\uC158)`));
|
|
6298
6566
|
} catch (e) {
|
|
6299
|
-
console.error(
|
|
6567
|
+
console.error(chalk32.red(` \u274C review \uD310\uC815 \uAE30\uB85D \uC2E4\uD328: ${e instanceof Error ? e.message : String(e)}`));
|
|
6300
6568
|
}
|
|
6301
6569
|
if (!mergeOk) {
|
|
6302
6570
|
process.exitCode = 1;
|
|
@@ -6317,8 +6585,8 @@ ${result.disclaimer}`));
|
|
|
6317
6585
|
cursorHint: "\uC644\uB8CC\uC870\uAC74 \uCC44\uC6CC\uC918"
|
|
6318
6586
|
});
|
|
6319
6587
|
} else if (result.suspicions.length > 0) {
|
|
6320
|
-
console.log(
|
|
6321
|
-
console.log(
|
|
6588
|
+
console.log(chalk32.dim("\n AI \uC7AC\uC9C8\uBB38 \uD504\uB86C\uD504\uD2B8:"));
|
|
6589
|
+
console.log(chalk32.cyan(result.reprompt.split("\n").map((l) => ` ${l}`).join("\n")));
|
|
6322
6590
|
printNextStep({
|
|
6323
6591
|
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
6592
|
command: "vhk verify",
|
|
@@ -6332,8 +6600,8 @@ ${result.disclaimer}`));
|
|
|
6332
6600
|
cursorHint: "goal \uC644\uB8CC \uCC98\uB9AC\uD574\uC918"
|
|
6333
6601
|
});
|
|
6334
6602
|
} else {
|
|
6335
|
-
console.log(
|
|
6336
|
-
console.log(
|
|
6603
|
+
console.log(chalk32.dim("\n AI \uC7AC\uC9C8\uBB38 \uD504\uB86C\uD504\uD2B8:"));
|
|
6604
|
+
console.log(chalk32.cyan(result.reprompt.split("\n").map((l) => ` ${l}`).join("\n")));
|
|
6337
6605
|
printNextStep({
|
|
6338
6606
|
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
6607
|
command: "vhk verify",
|
|
@@ -6343,12 +6611,12 @@ ${result.disclaimer}`));
|
|
|
6343
6611
|
}
|
|
6344
6612
|
|
|
6345
6613
|
// src/commands/mission.ts
|
|
6346
|
-
import { existsSync as
|
|
6347
|
-
import { join as
|
|
6348
|
-
import
|
|
6614
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync13, writeFileSync as writeFileSync14, rmSync as rmSync4 } from "fs";
|
|
6615
|
+
import { join as join14 } from "path";
|
|
6616
|
+
import chalk33 from "chalk";
|
|
6349
6617
|
import inquirer12 from "inquirer";
|
|
6350
6618
|
import { simpleGit as simpleGit3 } from "simple-git";
|
|
6351
|
-
var MISSION_PATH_REL =
|
|
6619
|
+
var MISSION_PATH_REL = join14(".vhk", "mission.json");
|
|
6352
6620
|
var MISSION_SCHEMA_VERSION = 1;
|
|
6353
6621
|
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
6622
|
function globToRegExp(glob) {
|
|
@@ -6396,8 +6664,8 @@ function checkMission(changedFiles, mission) {
|
|
|
6396
6664
|
return { violations, warnings, disclaimer: MISSION_DISCLAIMER };
|
|
6397
6665
|
}
|
|
6398
6666
|
function readMission(cwd = process.cwd()) {
|
|
6399
|
-
const p =
|
|
6400
|
-
if (!
|
|
6667
|
+
const p = join14(cwd, MISSION_PATH_REL);
|
|
6668
|
+
if (!existsSync19(p)) return null;
|
|
6401
6669
|
try {
|
|
6402
6670
|
const m = readJsonFile(p);
|
|
6403
6671
|
if (m && typeof m.objective === "string") return m;
|
|
@@ -6407,8 +6675,8 @@ function readMission(cwd = process.cwd()) {
|
|
|
6407
6675
|
}
|
|
6408
6676
|
}
|
|
6409
6677
|
function writeMission(cwd, mission) {
|
|
6410
|
-
|
|
6411
|
-
|
|
6678
|
+
mkdirSync13(join14(cwd, ".vhk"), { recursive: true });
|
|
6679
|
+
writeFileSync14(join14(cwd, MISSION_PATH_REL), JSON.stringify(mission, null, 2) + "\n", "utf-8");
|
|
6412
6680
|
}
|
|
6413
6681
|
async function collectChangedFiles(cwd) {
|
|
6414
6682
|
const status2 = await simpleGit3(cwd).status();
|
|
@@ -6429,7 +6697,7 @@ async function missionSet(opts = {}) {
|
|
|
6429
6697
|
objective = ans.obj.trim();
|
|
6430
6698
|
}
|
|
6431
6699
|
if (!objective) {
|
|
6432
|
-
console.error(
|
|
6700
|
+
console.error(chalk33.red(' \u274C objective \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. --objective "..." \uB85C \uC9C0\uC815\uD558\uC138\uC694(\uBE44\uB300\uD654\uD615).'));
|
|
6433
6701
|
process.exitCode = 1;
|
|
6434
6702
|
return;
|
|
6435
6703
|
}
|
|
@@ -6448,12 +6716,12 @@ async function missionSet(opts = {}) {
|
|
|
6448
6716
|
try {
|
|
6449
6717
|
writeMission(cwd, mission);
|
|
6450
6718
|
} catch (e) {
|
|
6451
|
-
console.error(
|
|
6719
|
+
console.error(chalk33.red(` \u274C mission.json \uAE30\uB85D \uC2E4\uD328: ${e instanceof Error ? e.message : String(e)}`));
|
|
6452
6720
|
process.exitCode = 1;
|
|
6453
6721
|
return;
|
|
6454
6722
|
}
|
|
6455
|
-
console.log(
|
|
6456
|
-
console.log(
|
|
6723
|
+
console.log(chalk33.bold("\n\u{1F3AF} \uBBF8\uC158 \uACC4\uC57D \uC800\uC7A5"));
|
|
6724
|
+
console.log(chalk33.dim(` \u{1F4C4} ${MISSION_PATH_REL}`));
|
|
6457
6725
|
console.log(` objective: ${mission.objective}`);
|
|
6458
6726
|
console.log(` scope: ${mission.scope.length ? mission.scope.join(", ") : "(\uC81C\uD55C \uC5C6\uC74C)"}`);
|
|
6459
6727
|
console.log(` forbidden: ${mission.forbidden.length ? mission.forbidden.join(", ") : "(\uC5C6\uC74C)"}`);
|
|
@@ -6463,64 +6731,64 @@ async function missionShow() {
|
|
|
6463
6731
|
const cwd = process.cwd();
|
|
6464
6732
|
const mission = readMission(cwd);
|
|
6465
6733
|
if (!mission) {
|
|
6466
|
-
console.error(
|
|
6734
|
+
console.error(chalk33.yellow(" \u26A0\uFE0F \uBBF8\uC158 \uACC4\uC57D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 (.vhk/mission.json)."));
|
|
6467
6735
|
printNextStep({ message: "\uBA3C\uC800 \uBBF8\uC158\uC744 \uC120\uC5B8\uD558\uC138\uC694:", command: 'vhk mission set --objective "..."', cursorHint: "\uBBF8\uC158 \uC815\uD574\uC918" });
|
|
6468
6736
|
process.exitCode = 1;
|
|
6469
6737
|
return;
|
|
6470
6738
|
}
|
|
6471
|
-
console.log(
|
|
6739
|
+
console.log(chalk33.bold("\n\u{1F3AF} \uD604\uC7AC \uBBF8\uC158 \uACC4\uC57D"));
|
|
6472
6740
|
console.log(` objective: ${mission.objective}`);
|
|
6473
6741
|
console.log(` scope: ${mission.scope.length ? mission.scope.join(", ") : "(\uC81C\uD55C \uC5C6\uC74C)"}`);
|
|
6474
6742
|
console.log(` forbidden: ${mission.forbidden.length ? mission.forbidden.join(", ") : "(\uC5C6\uC74C)"}`);
|
|
6475
|
-
console.log(
|
|
6743
|
+
console.log(chalk33.dim(` \uC0DD\uC131 ${mission.createdAt} \xB7 \uAC31\uC2E0 ${mission.updatedAt}`));
|
|
6476
6744
|
}
|
|
6477
6745
|
async function missionCheck() {
|
|
6478
6746
|
const cwd = process.cwd();
|
|
6479
6747
|
const mission = readMission(cwd);
|
|
6480
6748
|
if (!mission) {
|
|
6481
|
-
console.error(
|
|
6749
|
+
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
6750
|
process.exitCode = 1;
|
|
6483
6751
|
return;
|
|
6484
6752
|
}
|
|
6485
6753
|
const changed = await collectChangedFiles(cwd);
|
|
6486
6754
|
const result = checkMission(changed, mission);
|
|
6487
|
-
console.log(
|
|
6488
|
-
console.log(
|
|
6755
|
+
console.log(chalk33.bold("\n\u{1F3AF} \uBBF8\uC158 \uACC4\uC57D \uAC80\uC99D (mission check)"));
|
|
6756
|
+
console.log(chalk33.dim(` objective: ${mission.objective} \xB7 \uBCC0\uACBD \uD30C\uC77C ${changed.length}\uAC1C`));
|
|
6489
6757
|
if (result.violations.length > 0) {
|
|
6490
|
-
console.log(
|
|
6758
|
+
console.log(chalk33.red.bold(`
|
|
6491
6759
|
\u{1F6AB} forbidden \uC704\uBC18 ${result.violations.length}\uAC74`));
|
|
6492
|
-
for (const v of result.violations) console.log(
|
|
6760
|
+
for (const v of result.violations) console.log(chalk33.red(` \u2717 ${v.file} (\uAE08\uC9C0: ${v.pattern})`));
|
|
6493
6761
|
}
|
|
6494
6762
|
if (result.warnings.length > 0) {
|
|
6495
|
-
console.log(
|
|
6763
|
+
console.log(chalk33.yellow.bold(`
|
|
6496
6764
|
\u26A0\uFE0F scope \uBC16 \uBCC0\uACBD ${result.warnings.length}\uAC74 (\uACBD\uACE0)`));
|
|
6497
|
-
for (const w of result.warnings) console.log(
|
|
6765
|
+
for (const w of result.warnings) console.log(chalk33.yellow(` ? ${w.file}`));
|
|
6498
6766
|
}
|
|
6499
6767
|
if (result.violations.length === 0 && result.warnings.length === 0) {
|
|
6500
|
-
console.log(
|
|
6768
|
+
console.log(chalk33.green("\n \u2713 \uBCC0\uACBD\uC774 \uACC4\uC57D(scope/forbidden) \uC548\uC785\uB2C8\uB2E4."));
|
|
6501
6769
|
}
|
|
6502
|
-
console.log(
|
|
6770
|
+
console.log(chalk33.yellow(`
|
|
6503
6771
|
${result.disclaimer}`));
|
|
6504
6772
|
process.exitCode = result.violations.length > 0 ? 1 : 0;
|
|
6505
6773
|
}
|
|
6506
6774
|
async function missionClear() {
|
|
6507
6775
|
const cwd = process.cwd();
|
|
6508
|
-
const p =
|
|
6509
|
-
if (!
|
|
6510
|
-
console.log(
|
|
6776
|
+
const p = join14(cwd, MISSION_PATH_REL);
|
|
6777
|
+
if (!existsSync19(p)) {
|
|
6778
|
+
console.log(chalk33.dim(" \uBBF8\uC158 \uACC4\uC57D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uC9C0\uC6B8 \uAC83 \uC5C6\uC74C."));
|
|
6511
6779
|
return;
|
|
6512
6780
|
}
|
|
6513
6781
|
try {
|
|
6514
6782
|
rmSync4(p);
|
|
6515
|
-
console.log(
|
|
6783
|
+
console.log(chalk33.green(" \u2705 \uBBF8\uC158 \uACC4\uC57D \uC0AD\uC81C\uB428 (.vhk/mission.json)."));
|
|
6516
6784
|
} catch (e) {
|
|
6517
|
-
console.error(
|
|
6785
|
+
console.error(chalk33.red(` \u274C \uC0AD\uC81C \uC2E4\uD328: ${e instanceof Error ? e.message : String(e)}`));
|
|
6518
6786
|
process.exitCode = 1;
|
|
6519
6787
|
}
|
|
6520
6788
|
}
|
|
6521
6789
|
|
|
6522
6790
|
// src/commands/pattern.ts
|
|
6523
|
-
import
|
|
6791
|
+
import chalk34 from "chalk";
|
|
6524
6792
|
var MIN_TAG_FREQ = 3;
|
|
6525
6793
|
var STOPWORDS = /* @__PURE__ */ new Set(["the", "a", "an", "is", "are", "and", "or", "in", "on", "at", "to", "for", "of", "with", "it", "was", "be"]);
|
|
6526
6794
|
function tokenize(text) {
|
|
@@ -6638,14 +6906,14 @@ function reconcilePatterns(patterns, candidates, now) {
|
|
|
6638
6906
|
async function patternDetect(opts = {}) {
|
|
6639
6907
|
const minFreq = opts.min !== void 0 ? parseInt(opts.min, 10) : MIN_TAG_FREQ;
|
|
6640
6908
|
if (!Number.isFinite(minFreq) || minFreq < 1) {
|
|
6641
|
-
console.log(
|
|
6909
|
+
console.log(chalk34.red("\u274C --min \uC740 1 \uC774\uC0C1\uC758 \uC815\uC218\uC5EC\uC57C \uD569\uB2C8\uB2E4."));
|
|
6642
6910
|
process.exitCode = 1;
|
|
6643
6911
|
return;
|
|
6644
6912
|
}
|
|
6645
6913
|
const cwd = process.cwd();
|
|
6646
6914
|
const loaded = loadForMutation(cwd);
|
|
6647
6915
|
if (!loaded.ok) {
|
|
6648
|
-
console.log(
|
|
6916
|
+
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
6917
|
process.exitCode = 1;
|
|
6650
6918
|
return;
|
|
6651
6919
|
}
|
|
@@ -6661,26 +6929,26 @@ async function patternDetect(opts = {}) {
|
|
|
6661
6929
|
console.log(JSON.stringify(active2, null, 2));
|
|
6662
6930
|
return;
|
|
6663
6931
|
}
|
|
6664
|
-
console.log(
|
|
6665
|
-
console.log(
|
|
6666
|
-
console.log(
|
|
6667
|
-
console.log(
|
|
6932
|
+
console.log(chalk34.bold("\n\u{1F50D} " + t("pattern.detectTitle")));
|
|
6933
|
+
console.log(chalk34.gray("\u2500".repeat(40)));
|
|
6934
|
+
console.log(chalk34.dim(` \uC784\uACC4: ${minFreq}\uD68C \uC774\uC0C1 \xB7 active failures+successes \uC785\uB825`));
|
|
6935
|
+
console.log(chalk34.dim(` \uD6C4\uBCF4: ${candidates.length}\uAC1C \uAC10\uC9C0 (\uCD94\uAC00 ${added} / \uAC31\uC2E0 ${updated})`));
|
|
6668
6936
|
const active = mem.patterns.filter((p) => p.status !== "archived");
|
|
6669
6937
|
if (active.length === 0) {
|
|
6670
|
-
console.log(
|
|
6671
|
-
console.log(
|
|
6672
|
-
console.log(
|
|
6938
|
+
console.log(chalk34.yellow("\n\u{1F4ED} \uC784\uACC4 \uC774\uC0C1 \uBC18\uBCF5 \uD328\uD134\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
6939
|
+
console.log(chalk34.gray(` failures/successes \uAC00 ${minFreq}\uAC1C \uC774\uC0C1 \uC313\uC774\uBA74 \uAC10\uC9C0\uB429\uB2C8\uB2E4.`));
|
|
6940
|
+
console.log(chalk34.gray(" --min N \uC73C\uB85C \uC784\uACC4\uB97C \uB0AE\uCD9C \uC218 \uC788\uC2B5\uB2C8\uB2E4."));
|
|
6673
6941
|
return;
|
|
6674
6942
|
}
|
|
6675
|
-
console.log(
|
|
6943
|
+
console.log(chalk34.cyan(`
|
|
6676
6944
|
\uD328\uD134 \uD6C4\uBCF4 ${active.length}\uAC1C:
|
|
6677
6945
|
`));
|
|
6678
6946
|
for (const p of active.slice(0, 20)) {
|
|
6679
6947
|
const icon = p.kind === "avoid" ? "\u26A0\uFE0F " : "\u2705";
|
|
6680
6948
|
console.log(` [${p.id}] ${icon} (${p.kind}/${p.axis}) "${p.signal}" \u2014 ${p.count}\uAC74`);
|
|
6681
6949
|
const preview = p.sources.slice(0, 5).join(", ") + (p.sources.length > 5 ? ` \uC678 ${p.sources.length - 5}\uAC74` : "");
|
|
6682
|
-
console.log(
|
|
6683
|
-
console.log(
|
|
6950
|
+
console.log(chalk34.dim(` \uADFC\uAC70: ${preview}`));
|
|
6951
|
+
console.log(chalk34.dim(` ${p.summary}`));
|
|
6684
6952
|
}
|
|
6685
6953
|
printNextStep({
|
|
6686
6954
|
message: `\uD328\uD134 \uAC10\uC9C0 \uC644\uB8CC! ${active.length}\uAC1C \uD6C4\uBCF4.`,
|
|
@@ -6690,8 +6958,8 @@ async function patternDetect(opts = {}) {
|
|
|
6690
6958
|
});
|
|
6691
6959
|
}
|
|
6692
6960
|
async function patternList(opts = {}) {
|
|
6693
|
-
console.log(
|
|
6694
|
-
console.log(
|
|
6961
|
+
console.log(chalk34.bold("\n\u{1F50D} " + t("pattern.listTitle")));
|
|
6962
|
+
console.log(chalk34.gray("\u2500".repeat(40)));
|
|
6695
6963
|
const mem = readMemory(process.cwd());
|
|
6696
6964
|
let patterns = mem.patterns;
|
|
6697
6965
|
if (!opts.all) patterns = patterns.filter((p) => p.status !== "archived");
|
|
@@ -6703,51 +6971,51 @@ async function patternList(opts = {}) {
|
|
|
6703
6971
|
return;
|
|
6704
6972
|
}
|
|
6705
6973
|
if (patterns.length === 0) {
|
|
6706
|
-
console.log(
|
|
6707
|
-
console.log(
|
|
6974
|
+
console.log(chalk34.yellow("\n\u{1F4ED} \uD45C\uC2DC\uD560 \uD328\uD134\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
6975
|
+
console.log(chalk34.gray(" vhk pattern detect \uB85C \uAC10\uC9C0 \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694."));
|
|
6708
6976
|
return;
|
|
6709
6977
|
}
|
|
6710
|
-
console.log(
|
|
6978
|
+
console.log(chalk34.cyan(`
|
|
6711
6979
|
${patterns.length}\uAC1C${opts.all ? " (\uBCF4\uAD00 \uD3EC\uD568)" : " (\uD65C\uC131)"}:
|
|
6712
6980
|
`));
|
|
6713
6981
|
for (const p of patterns) {
|
|
6714
6982
|
const icon = p.kind === "avoid" ? "\u26A0\uFE0F " : "\u2705";
|
|
6715
6983
|
const archived = p.status === "archived" ? "\u{1F4E6} " : "";
|
|
6716
6984
|
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(
|
|
6985
|
+
if (p.summary) console.log(chalk34.dim(` ${p.summary}`));
|
|
6986
|
+
if (p.tags?.length) console.log(chalk34.blue(` \u{1F3F7}\uFE0F ${p.tags.join(", ")}`));
|
|
6719
6987
|
}
|
|
6720
6988
|
}
|
|
6721
6989
|
async function patternDismiss(idStr) {
|
|
6722
6990
|
if (!idStr?.trim()) {
|
|
6723
|
-
console.log(
|
|
6991
|
+
console.log(chalk34.red("\u274C \uD328\uD134 id \uB97C \uC785\uB825\uD574\uC8FC\uC138\uC694. \uC608: vhk pattern dismiss p1"));
|
|
6724
6992
|
process.exitCode = 1;
|
|
6725
6993
|
return;
|
|
6726
6994
|
}
|
|
6727
6995
|
const cwd = process.cwd();
|
|
6728
6996
|
const loaded = loadForMutation(cwd);
|
|
6729
6997
|
if (!loaded.ok) {
|
|
6730
|
-
console.log(
|
|
6998
|
+
console.log(chalk34.red("\u274C memory.json \uC190\uC0C1 \uC758\uC2EC \u2014 dismiss \uC911\uB2E8 (\uC6D0\uBCF8 \uBCF4\uC874)."));
|
|
6731
6999
|
process.exitCode = 1;
|
|
6732
7000
|
return;
|
|
6733
7001
|
}
|
|
6734
7002
|
const mem = loaded.mem;
|
|
6735
7003
|
const pattern = mem.patterns.find((p) => p.id === idStr.trim());
|
|
6736
7004
|
if (!pattern) {
|
|
6737
|
-
console.log(
|
|
6738
|
-
console.log(
|
|
7005
|
+
console.log(chalk34.red(`\u274C \uD328\uD134 '${idStr}' \uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`));
|
|
7006
|
+
console.log(chalk34.gray(" vhk pattern list --all \uB85C \uBAA9\uB85D \uD655\uC778."));
|
|
6739
7007
|
process.exitCode = 1;
|
|
6740
7008
|
return;
|
|
6741
7009
|
}
|
|
6742
7010
|
if (pattern.status === "archived") {
|
|
6743
|
-
console.log(
|
|
7011
|
+
console.log(chalk34.dim(` \uC774\uBBF8 \uBCF4\uAD00\uB41C \uD328\uD134\uC785\uB2C8\uB2E4 \u2014 \uBCC0\uACBD \uC5C6\uC74C: ${pattern.id}`));
|
|
6744
7012
|
return;
|
|
6745
7013
|
}
|
|
6746
7014
|
pattern.status = "archived";
|
|
6747
7015
|
writeMemory(cwd, mem);
|
|
6748
|
-
console.log(
|
|
7016
|
+
console.log(chalk34.green(`
|
|
6749
7017
|
\u{1F4E6} \uD328\uD134 dismiss(\uBCF4\uAD00)\uB428: [${pattern.id}] ${pattern.summary ?? pattern.signal}`));
|
|
6750
|
-
console.log(
|
|
7018
|
+
console.log(chalk34.dim(" \uC624\uD0D0\uC73C\uB85C \uD310\uB2E8. detect \uC7AC\uC2E4\uD589 \uC2DC \uC7AC\uC81C\uC548 \uC548 \uB428."));
|
|
6751
7019
|
printNextStep({
|
|
6752
7020
|
message: "\uD328\uD134 dismiss \uC644\uB8CC!",
|
|
6753
7021
|
command: "vhk pattern list",
|
|
@@ -6756,11 +7024,11 @@ async function patternDismiss(idStr) {
|
|
|
6756
7024
|
}
|
|
6757
7025
|
|
|
6758
7026
|
// src/commands/evolve.ts
|
|
6759
|
-
import { existsSync as
|
|
6760
|
-
import { join as
|
|
6761
|
-
import
|
|
7027
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync14, writeFileSync as writeFileSync15, readFileSync as readFileSync8, copyFileSync as copyFileSync2, renameSync as renameSync2, rmSync as rmSync5 } from "fs";
|
|
7028
|
+
import { join as join15 } from "path";
|
|
7029
|
+
import chalk35 from "chalk";
|
|
6762
7030
|
import inquirer13 from "inquirer";
|
|
6763
|
-
var QUEUE_PATH_REL =
|
|
7031
|
+
var QUEUE_PATH_REL = join15(".vhk", "evolve", "queue.json");
|
|
6764
7032
|
function buildDraft(p) {
|
|
6765
7033
|
const axisLabel = p.axis === "tag" ? `\uD0DC\uADF8 '${p.signal}'` : `\uD0A4\uC6CC\uB4DC '${p.signal}'`;
|
|
6766
7034
|
const countDesc = `${p.count}\uAC74 \uBC18\uBCF5`;
|
|
@@ -6804,10 +7072,10 @@ function stripBomStr(s) {
|
|
|
6804
7072
|
return s.charCodeAt(0) === 65279 ? s.slice(1) : s;
|
|
6805
7073
|
}
|
|
6806
7074
|
function readQueue(cwd) {
|
|
6807
|
-
const p =
|
|
6808
|
-
if (!
|
|
7075
|
+
const p = join15(cwd, QUEUE_PATH_REL);
|
|
7076
|
+
if (!existsSync20(p)) return { version: 1, items: [] };
|
|
6809
7077
|
try {
|
|
6810
|
-
const raw = stripBomStr(
|
|
7078
|
+
const raw = stripBomStr(readFileSync8(p, "utf-8"));
|
|
6811
7079
|
const parsed = JSON.parse(raw);
|
|
6812
7080
|
if (!parsed || !Array.isArray(parsed.items)) return { version: 1, items: [] };
|
|
6813
7081
|
return parsed;
|
|
@@ -6816,10 +7084,10 @@ function readQueue(cwd) {
|
|
|
6816
7084
|
}
|
|
6817
7085
|
}
|
|
6818
7086
|
function writeQueue(cwd, queue) {
|
|
6819
|
-
const p =
|
|
6820
|
-
|
|
7087
|
+
const p = join15(cwd, QUEUE_PATH_REL);
|
|
7088
|
+
mkdirSync14(join15(cwd, ".vhk", "evolve"), { recursive: true });
|
|
6821
7089
|
const tmpPath = p + ".tmp";
|
|
6822
|
-
|
|
7090
|
+
writeFileSync15(tmpPath, JSON.stringify(queue, null, 2) + "\n", "utf-8");
|
|
6823
7091
|
try {
|
|
6824
7092
|
renameSync2(tmpPath, p);
|
|
6825
7093
|
} catch (err) {
|
|
@@ -6848,8 +7116,8 @@ function checkApplyRef(pattern, queueItems) {
|
|
|
6848
7116
|
}
|
|
6849
7117
|
async function evolveSuggest(opts = {}) {
|
|
6850
7118
|
const cwd = process.cwd();
|
|
6851
|
-
if (!
|
|
6852
|
-
console.log(
|
|
7119
|
+
if (!existsSync20(join15(cwd, "RULES.md"))) {
|
|
7120
|
+
console.log(chalk35.yellow("\n\u26A0\uFE0F " + t("evolve.noRules")));
|
|
6853
7121
|
process.exitCode = 1;
|
|
6854
7122
|
return;
|
|
6855
7123
|
}
|
|
@@ -6860,10 +7128,10 @@ async function evolveSuggest(opts = {}) {
|
|
|
6860
7128
|
if (newItems.length === 0 && !opts.json) {
|
|
6861
7129
|
const activeAvoid = patterns.filter((p) => p.kind === "avoid" && p.status === "active");
|
|
6862
7130
|
if (activeAvoid.length === 0) {
|
|
6863
|
-
console.log(
|
|
7131
|
+
console.log(chalk35.yellow("\n\u{1F4ED} " + t("evolve.noPatterns")));
|
|
6864
7132
|
return;
|
|
6865
7133
|
}
|
|
6866
|
-
console.log(
|
|
7134
|
+
console.log(chalk35.dim("\n " + t("evolve.allSuggested")));
|
|
6867
7135
|
return;
|
|
6868
7136
|
}
|
|
6869
7137
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -6876,16 +7144,16 @@ async function evolveSuggest(opts = {}) {
|
|
|
6876
7144
|
console.log(JSON.stringify(pending2, null, 2));
|
|
6877
7145
|
return;
|
|
6878
7146
|
}
|
|
6879
|
-
console.log(
|
|
6880
|
-
console.log(
|
|
6881
|
-
console.log(
|
|
7147
|
+
console.log(chalk35.bold("\n\u{1F504} " + t("evolve.suggestTitle")));
|
|
7148
|
+
console.log(chalk35.gray("\u2500".repeat(40)));
|
|
7149
|
+
console.log(chalk35.dim(" " + t("evolve.newCandidates", newItems.length)));
|
|
6882
7150
|
const pending = queue.items.filter((i) => i.status === "pending");
|
|
6883
|
-
console.log(
|
|
7151
|
+
console.log(chalk35.cyan(`
|
|
6884
7152
|
\uD6C4\uBCF4 ${pending.length}\uAC1C:
|
|
6885
7153
|
`));
|
|
6886
7154
|
for (const item of pending) {
|
|
6887
7155
|
console.log(` [${item.id}] (${item.status}) \uD328\uD134 ${item.patternId} \u2192 rule`);
|
|
6888
|
-
console.log(
|
|
7156
|
+
console.log(chalk35.dim(` \uCD08\uC548: ${item.draft}`));
|
|
6889
7157
|
}
|
|
6890
7158
|
printNextStep({
|
|
6891
7159
|
message: `\uC9C4\uD654 \uD6C4\uBCF4 ${pending.length}\uAC1C \uC0DD\uC131\uB428!`,
|
|
@@ -6906,11 +7174,11 @@ async function evolveList(opts = {}) {
|
|
|
6906
7174
|
console.log(JSON.stringify(items, null, 2));
|
|
6907
7175
|
return;
|
|
6908
7176
|
}
|
|
6909
|
-
console.log(
|
|
6910
|
-
console.log(
|
|
7177
|
+
console.log(chalk35.bold("\n\u{1F504} " + t("evolve.listTitle")));
|
|
7178
|
+
console.log(chalk35.gray("\u2500".repeat(40)));
|
|
6911
7179
|
if (items.length === 0) {
|
|
6912
|
-
console.log(
|
|
6913
|
-
console.log(
|
|
7180
|
+
console.log(chalk35.yellow("\n\u{1F4ED} " + t("evolve.noQueue")));
|
|
7181
|
+
console.log(chalk35.gray(" " + t("evolve.suggestHint")));
|
|
6914
7182
|
return;
|
|
6915
7183
|
}
|
|
6916
7184
|
const STATUS_ICON3 = {
|
|
@@ -6918,68 +7186,68 @@ async function evolveList(opts = {}) {
|
|
|
6918
7186
|
rejected: "\u274C",
|
|
6919
7187
|
applied: "\u2705"
|
|
6920
7188
|
};
|
|
6921
|
-
console.log(
|
|
7189
|
+
console.log(chalk35.cyan(`
|
|
6922
7190
|
${items.length}\uAC1C:
|
|
6923
7191
|
`));
|
|
6924
7192
|
for (const item of items) {
|
|
6925
7193
|
console.log(` [${item.id}] ${STATUS_ICON3[item.status]} (${item.status}) \u2192 ${item.draft}`);
|
|
6926
|
-
if (item.appliedAt) console.log(
|
|
7194
|
+
if (item.appliedAt) console.log(chalk35.dim(` \uBC18\uC601: ${item.appliedAt}`));
|
|
6927
7195
|
}
|
|
6928
7196
|
}
|
|
6929
7197
|
async function evolveApply(idStr) {
|
|
6930
7198
|
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
7199
|
const cwd = process.cwd();
|
|
6932
|
-
const rulesPath =
|
|
6933
|
-
if (!
|
|
6934
|
-
console.log(
|
|
7200
|
+
const rulesPath = join15(cwd, "RULES.md");
|
|
7201
|
+
if (!existsSync20(rulesPath)) {
|
|
7202
|
+
console.log(chalk35.red("\n\u274C " + t("evolve.noRules")));
|
|
6935
7203
|
process.exitCode = 1;
|
|
6936
7204
|
return;
|
|
6937
7205
|
}
|
|
6938
7206
|
const queue = readQueue(cwd);
|
|
6939
7207
|
const item = queue.items.find((i) => i.id === idStr?.trim());
|
|
6940
7208
|
if (!item) {
|
|
6941
|
-
console.log(
|
|
7209
|
+
console.log(chalk35.red("\n\u274C " + t("evolve.notFound", idStr ?? "")));
|
|
6942
7210
|
process.exitCode = 1;
|
|
6943
7211
|
return;
|
|
6944
7212
|
}
|
|
6945
7213
|
if (item.status === "applied") {
|
|
6946
|
-
console.log(
|
|
7214
|
+
console.log(chalk35.yellow("\n\u26A0\uFE0F " + t("evolve.alreadyApplied")));
|
|
6947
7215
|
process.exitCode = 1;
|
|
6948
7216
|
return;
|
|
6949
7217
|
}
|
|
6950
7218
|
const hasUnresolved = queue.items.some((i) => i.status === "applied");
|
|
6951
7219
|
if (hasUnresolved) {
|
|
6952
|
-
console.log(
|
|
7220
|
+
console.log(chalk35.red("\n\u274C " + t("evolve.pendingApplyExists")));
|
|
6953
7221
|
process.exitCode = 1;
|
|
6954
7222
|
return;
|
|
6955
7223
|
}
|
|
6956
7224
|
const memLoaded = loadForMutation(cwd);
|
|
6957
7225
|
if (!memLoaded.ok) {
|
|
6958
|
-
console.log(
|
|
7226
|
+
console.log(chalk35.red("\n\u274C memory.json \uC190\uC0C1 \uC758\uC2EC \u2014 apply \uC911\uB2E8 (\uC6D0\uBCF8 \uBCF4\uC874)."));
|
|
6959
7227
|
process.exitCode = 1;
|
|
6960
7228
|
return;
|
|
6961
7229
|
}
|
|
6962
7230
|
const srcPattern = memLoaded.mem.patterns.find((p) => p.id === item.patternId);
|
|
6963
7231
|
const refResult = checkApplyRef(srcPattern, queue.items);
|
|
6964
7232
|
if (refResult === "dismissed") {
|
|
6965
|
-
console.log(
|
|
7233
|
+
console.log(chalk35.red("\n\u274C " + t("evolve.dismissed")));
|
|
6966
7234
|
process.exitCode = 1;
|
|
6967
7235
|
return;
|
|
6968
7236
|
}
|
|
6969
7237
|
if (refResult === "already-applied") {
|
|
6970
|
-
console.log(
|
|
7238
|
+
console.log(chalk35.red("\n\u274C " + t("evolve.alreadyAppliedPattern")));
|
|
6971
7239
|
process.exitCode = 1;
|
|
6972
7240
|
return;
|
|
6973
7241
|
}
|
|
6974
|
-
const rulesContent =
|
|
7242
|
+
const rulesContent = readFileSync8(rulesPath, "utf-8");
|
|
6975
7243
|
if (isDuplicateRule(rulesContent, item.draft)) {
|
|
6976
|
-
console.log(
|
|
7244
|
+
console.log(chalk35.yellow("\n\u26A0\uFE0F " + t("evolve.duplicateRule", item.draft)));
|
|
6977
7245
|
return;
|
|
6978
7246
|
}
|
|
6979
|
-
console.log(
|
|
6980
|
-
console.log(
|
|
6981
|
-
console.log(
|
|
6982
|
-
console.log(
|
|
7247
|
+
console.log(chalk35.bold("\n\u{1F504} " + t("evolve.applyTitle")));
|
|
7248
|
+
console.log(chalk35.gray("\u2500".repeat(40)));
|
|
7249
|
+
console.log(chalk35.cyan("\n\uCD94\uAC00\uB420 \uB8F0 \uCD08\uC548:"));
|
|
7250
|
+
console.log(chalk35.white(` ${item.draft}`));
|
|
6983
7251
|
const { editedDraft } = await inquirer13.prompt([{
|
|
6984
7252
|
type: "input",
|
|
6985
7253
|
name: "editedDraft",
|
|
@@ -6994,22 +7262,22 @@ async function evolveApply(idStr) {
|
|
|
6994
7262
|
default: false
|
|
6995
7263
|
}]);
|
|
6996
7264
|
if (!confirmed) {
|
|
6997
|
-
console.log(
|
|
7265
|
+
console.log(chalk35.dim(" \uCDE8\uC18C\uB428."));
|
|
6998
7266
|
return;
|
|
6999
7267
|
}
|
|
7000
7268
|
const backupPath = rulesPath + ".bak";
|
|
7001
7269
|
copyFileSync2(rulesPath, backupPath);
|
|
7002
7270
|
const appendContent = "\n" + editedDraft + "\n";
|
|
7003
7271
|
try {
|
|
7004
|
-
|
|
7272
|
+
writeFileSync15(rulesPath, rulesContent + appendContent, "utf-8");
|
|
7005
7273
|
await sync({ yes: true });
|
|
7006
7274
|
} catch (err) {
|
|
7007
7275
|
try {
|
|
7008
7276
|
copyFileSync2(backupPath, rulesPath);
|
|
7009
7277
|
} catch {
|
|
7010
7278
|
}
|
|
7011
|
-
console.error(
|
|
7012
|
-
console.error(
|
|
7279
|
+
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."));
|
|
7280
|
+
console.error(chalk35.dim(` ${err instanceof Error ? err.message : String(err)}`));
|
|
7013
7281
|
process.exitCode = 1;
|
|
7014
7282
|
return;
|
|
7015
7283
|
}
|
|
@@ -7025,12 +7293,12 @@ async function evolveApply(idStr) {
|
|
|
7025
7293
|
p.status = "archived";
|
|
7026
7294
|
writeMemory(cwd, memLoaded.mem);
|
|
7027
7295
|
} else {
|
|
7028
|
-
console.error(
|
|
7296
|
+
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
7297
|
}
|
|
7030
7298
|
}
|
|
7031
|
-
console.log(
|
|
7299
|
+
console.log(chalk35.green(`
|
|
7032
7300
|
\u2705 \uB8F0 \uBC18\uC601 \uC644\uB8CC! [${item.id}]`));
|
|
7033
|
-
console.log(
|
|
7301
|
+
console.log(chalk35.dim(" RULES.md\uC5D0 \uCD94\uAC00 + vhk sync \uC7AC\uC0DD\uC131\uB428"));
|
|
7034
7302
|
printNextStep({
|
|
7035
7303
|
message: "\uB8F0 \uBC18\uC601 \uC644\uB8CC!",
|
|
7036
7304
|
command: "vhk evolve list --status applied",
|
|
@@ -7043,25 +7311,25 @@ async function evolveReject(idStr) {
|
|
|
7043
7311
|
const queue = readQueue(cwd);
|
|
7044
7312
|
const item = queue.items.find((i) => i.id === idStr?.trim());
|
|
7045
7313
|
if (!item) {
|
|
7046
|
-
console.log(
|
|
7314
|
+
console.log(chalk35.red("\n\u274C " + t("evolve.notFound", idStr ?? "")));
|
|
7047
7315
|
process.exitCode = 1;
|
|
7048
7316
|
return;
|
|
7049
7317
|
}
|
|
7050
7318
|
if (item.status === "rejected") {
|
|
7051
|
-
console.log(
|
|
7319
|
+
console.log(chalk35.dim(` \uC774\uBBF8 \uAE30\uAC01\uB41C \uD6C4\uBCF4\uC785\uB2C8\uB2E4 \u2014 \uBCC0\uACBD \uC5C6\uC74C: ${item.id}`));
|
|
7052
7320
|
return;
|
|
7053
7321
|
}
|
|
7054
7322
|
if (item.status === "applied") {
|
|
7055
|
-
console.log(
|
|
7323
|
+
console.log(chalk35.red(`
|
|
7056
7324
|
\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
7325
|
process.exitCode = 1;
|
|
7058
7326
|
return;
|
|
7059
7327
|
}
|
|
7060
7328
|
item.status = "rejected";
|
|
7061
7329
|
writeQueue(cwd, queue);
|
|
7062
|
-
console.log(
|
|
7330
|
+
console.log(chalk35.green(`
|
|
7063
7331
|
\u274C \uD6C4\uBCF4 \uAE30\uAC01\uB428: [${item.id}] ${item.draft}`));
|
|
7064
|
-
console.log(
|
|
7332
|
+
console.log(chalk35.dim(" (A1: \uB2E4\uC74C suggest\uC5D0\uC11C \uC7AC\uC81C\uC548 \uC548 \uB428)"));
|
|
7065
7333
|
printNextStep({
|
|
7066
7334
|
message: "\uAE30\uAC01 \uC644\uB8CC!",
|
|
7067
7335
|
command: "vhk evolve list",
|
|
@@ -7074,19 +7342,19 @@ async function evolveUndo() {
|
|
|
7074
7342
|
const queue = readQueue(cwd);
|
|
7075
7343
|
const applied = queue.items.filter((i) => i.status === "applied");
|
|
7076
7344
|
if (applied.length === 0) {
|
|
7077
|
-
console.log(
|
|
7345
|
+
console.log(chalk35.yellow("\n\u{1F4ED} " + t("evolve.noAppliedToUndo")));
|
|
7078
7346
|
return;
|
|
7079
7347
|
}
|
|
7080
7348
|
const last = applied.sort(
|
|
7081
7349
|
(a, b) => (b.appliedAt ?? "").localeCompare(a.appliedAt ?? "")
|
|
7082
7350
|
)[0];
|
|
7083
|
-
if (!last.rulesBackupPath || !
|
|
7084
|
-
console.log(
|
|
7351
|
+
if (!last.rulesBackupPath || !existsSync20(last.rulesBackupPath)) {
|
|
7352
|
+
console.log(chalk35.red("\n\u274C " + t("evolve.noBackup")));
|
|
7085
7353
|
process.exitCode = 1;
|
|
7086
7354
|
return;
|
|
7087
7355
|
}
|
|
7088
|
-
console.log(
|
|
7089
|
-
console.log(
|
|
7356
|
+
console.log(chalk35.bold("\n\u{1F504} " + t("evolve.undoTitle")));
|
|
7357
|
+
console.log(chalk35.dim(` \uB418\uB3CC\uB9B4 \uD56D\uBAA9: [${last.id}] ${last.draft}`));
|
|
7090
7358
|
const { confirmed } = await inquirer13.prompt([{
|
|
7091
7359
|
type: "confirm",
|
|
7092
7360
|
name: "confirmed",
|
|
@@ -7094,16 +7362,16 @@ async function evolveUndo() {
|
|
|
7094
7362
|
default: false
|
|
7095
7363
|
}]);
|
|
7096
7364
|
if (!confirmed) {
|
|
7097
|
-
console.log(
|
|
7365
|
+
console.log(chalk35.dim(" \uCDE8\uC18C\uB428."));
|
|
7098
7366
|
return;
|
|
7099
7367
|
}
|
|
7100
|
-
copyFileSync2(last.rulesBackupPath,
|
|
7368
|
+
copyFileSync2(last.rulesBackupPath, join15(cwd, "RULES.md"));
|
|
7101
7369
|
try {
|
|
7102
7370
|
await sync({ yes: true });
|
|
7103
7371
|
} catch (err) {
|
|
7104
|
-
console.error(
|
|
7105
|
-
console.error(
|
|
7106
|
-
console.error(
|
|
7372
|
+
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."));
|
|
7373
|
+
console.error(chalk35.dim(` ${err instanceof Error ? err.message : String(err)}`));
|
|
7374
|
+
console.error(chalk35.dim(" \uC218\uB3D9\uC73C\uB85C `vhk sync` \uC2E4\uD589\uD558\uC138\uC694."));
|
|
7107
7375
|
}
|
|
7108
7376
|
last.status = "pending";
|
|
7109
7377
|
delete last.appliedAt;
|
|
@@ -7117,7 +7385,7 @@ async function evolveUndo() {
|
|
|
7117
7385
|
writeMemory(cwd, memLoaded.mem);
|
|
7118
7386
|
}
|
|
7119
7387
|
}
|
|
7120
|
-
console.log(
|
|
7388
|
+
console.log(chalk35.green("\n\u2705 \uB418\uB3CC\uB9AC\uAE30 \uC644\uB8CC! RULES.md \uBCF5\uC6D0 + sync \uC7AC\uC2E4\uD589\uB428"));
|
|
7121
7389
|
printNextStep({
|
|
7122
7390
|
message: "\uB418\uB3CC\uB9AC\uAE30 \uC644\uB8CC!",
|
|
7123
7391
|
command: "vhk evolve list",
|
|
@@ -7299,6 +7567,10 @@ async function dispatchNlpRoute(route, input) {
|
|
|
7299
7567
|
return patternList();
|
|
7300
7568
|
case "evolve":
|
|
7301
7569
|
return evolveList();
|
|
7570
|
+
case "work": {
|
|
7571
|
+
if (route.args?.[0] === "handoff") return workHandoff();
|
|
7572
|
+
return work();
|
|
7573
|
+
}
|
|
7302
7574
|
}
|
|
7303
7575
|
}
|
|
7304
7576
|
var STATE_CHANGING_COMMANDS = /* @__PURE__ */ new Set([
|
|
@@ -7312,14 +7584,14 @@ function requiresConfirmation(route) {
|
|
|
7312
7584
|
async function runNaturalLanguageRoute(input) {
|
|
7313
7585
|
const route = routeNaturalLanguage(input);
|
|
7314
7586
|
if (!route) {
|
|
7315
|
-
console.log(
|
|
7587
|
+
console.log(chalk36.yellow(`
|
|
7316
7588
|
\u2753 "${input}" \u2014 ${ko.nlp.notMatched}
|
|
7317
7589
|
`));
|
|
7318
7590
|
return;
|
|
7319
7591
|
}
|
|
7320
7592
|
console.log("");
|
|
7321
|
-
console.log(
|
|
7322
|
-
console.log(
|
|
7593
|
+
console.log(chalk36.cyan(` \u{1F4AC} "${input}"`));
|
|
7594
|
+
console.log(chalk36.cyan(` \u2192 ${route.explanation}`));
|
|
7323
7595
|
if (requiresConfirmation(route)) {
|
|
7324
7596
|
const { confirm } = await inquirer14.prompt([{
|
|
7325
7597
|
type: "confirm",
|
|
@@ -7328,7 +7600,7 @@ async function runNaturalLanguageRoute(input) {
|
|
|
7328
7600
|
default: true
|
|
7329
7601
|
}]);
|
|
7330
7602
|
if (!confirm) {
|
|
7331
|
-
console.log(
|
|
7603
|
+
console.log(chalk36.dim(` ${ko.nlp.menuHint}`));
|
|
7332
7604
|
return;
|
|
7333
7605
|
}
|
|
7334
7606
|
}
|
|
@@ -7337,7 +7609,7 @@ async function runNaturalLanguageRoute(input) {
|
|
|
7337
7609
|
if (riskAction) {
|
|
7338
7610
|
await runGuarded(
|
|
7339
7611
|
riskAction,
|
|
7340
|
-
{ channel: "nl", approved: false, log: (m) => console.log(
|
|
7612
|
+
{ channel: "nl", approved: false, log: (m) => console.log(chalk36.yellow(` ${m}`)) },
|
|
7341
7613
|
() => dispatchNlpRoute(route, input)
|
|
7342
7614
|
);
|
|
7343
7615
|
return;
|
|
@@ -7346,80 +7618,80 @@ async function runNaturalLanguageRoute(input) {
|
|
|
7346
7618
|
}
|
|
7347
7619
|
|
|
7348
7620
|
// src/commands/agent.ts
|
|
7349
|
-
import
|
|
7621
|
+
import chalk37 from "chalk";
|
|
7350
7622
|
function activeGoalId() {
|
|
7351
7623
|
const goals = listGoals("goals");
|
|
7352
7624
|
const id = selectActiveId(goals);
|
|
7353
7625
|
return id ?? void 0;
|
|
7354
7626
|
}
|
|
7355
7627
|
async function blocker(description) {
|
|
7356
|
-
console.log(
|
|
7628
|
+
console.log(chalk37.bold(`
|
|
7357
7629
|
${ko.agent.blockerTitle}
|
|
7358
7630
|
`));
|
|
7359
7631
|
if (!description || !description.trim()) {
|
|
7360
|
-
console.log(
|
|
7361
|
-
console.log(
|
|
7632
|
+
console.log(chalk37.red(" \u274C \uBE14\uB85C\uCEE4 \uC124\uBA85\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694."));
|
|
7633
|
+
console.log(chalk37.dim(' \uC608: vhk blocker "tsc \uC5D0\uB7EC \u2014 simple-git \uD0C0\uC785 \uD638\uD658"'));
|
|
7362
7634
|
process.exitCode = 1;
|
|
7363
7635
|
return;
|
|
7364
7636
|
}
|
|
7365
7637
|
const goalId = activeGoalId();
|
|
7366
7638
|
const r = appendBlocker(description, goalId);
|
|
7367
|
-
console.log(
|
|
7639
|
+
console.log(chalk37.green(` \u2705 blocker \uAE30\uB85D (\uD604\uC7AC \uD65C\uC131 ${r.count}\uAC74)`));
|
|
7368
7640
|
if (r.hardStopTripped) {
|
|
7369
|
-
console.log(
|
|
7370
|
-
console.log(
|
|
7641
|
+
console.log(chalk37.red.bold(" \u{1F6D1} HARD_STOP \uC790\uB3D9 \uC0DD\uC131 \u2014 \uBAA8\uB4E0 \uC790\uB3D9\uD654 \uC911\uB2E8."));
|
|
7642
|
+
console.log(chalk37.yellow(" \uC0AC\uB78C \uAC80\uD1A0 \uD6C4 `vhk resume --confirm` \uC73C\uB85C\uB9CC \uD574\uC81C."));
|
|
7371
7643
|
process.exitCode = 2;
|
|
7372
7644
|
}
|
|
7373
7645
|
}
|
|
7374
7646
|
async function learn(lesson) {
|
|
7375
|
-
console.log(
|
|
7647
|
+
console.log(chalk37.bold(`
|
|
7376
7648
|
${ko.agent.learnTitle}
|
|
7377
7649
|
`));
|
|
7378
7650
|
if (!lesson || !lesson.trim()) {
|
|
7379
|
-
console.log(
|
|
7380
|
-
console.log(
|
|
7651
|
+
console.log(chalk37.red(" \u274C \uAD50\uD6C8 \uB0B4\uC6A9\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694."));
|
|
7652
|
+
console.log(chalk37.dim(' \uC608: vhk learn "PowerShell \uC5D0\uC11C\uB294 ; \uC0AC\uC6A9 (&& \uBBF8\uC9C0\uC6D0)"'));
|
|
7381
7653
|
process.exitCode = 1;
|
|
7382
7654
|
return;
|
|
7383
7655
|
}
|
|
7384
7656
|
const goalId = activeGoalId();
|
|
7385
7657
|
const entry = recordLesson(process.cwd(), lesson, goalId);
|
|
7386
7658
|
if (!entry) {
|
|
7387
|
-
console.log(
|
|
7659
|
+
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
7660
|
process.exitCode = 1;
|
|
7389
7661
|
return;
|
|
7390
7662
|
}
|
|
7391
|
-
console.log(
|
|
7392
|
-
console.log(
|
|
7663
|
+
console.log(chalk37.green(` \u2705 \uAD50\uD6C8 \uAE30\uB85D \u2192 memory failures.lesson (${entry.id})`));
|
|
7664
|
+
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
7665
|
}
|
|
7394
7666
|
async function resume(opts = {}) {
|
|
7395
|
-
console.log(
|
|
7667
|
+
console.log(chalk37.bold(`
|
|
7396
7668
|
${ko.agent.resumeTitle}
|
|
7397
7669
|
`));
|
|
7398
7670
|
if (!isHardStopActive()) {
|
|
7399
|
-
console.log(
|
|
7671
|
+
console.log(chalk37.dim(" HARD_STOP \uD65C\uC131 \uC544\uB2D8 \u2014 \uD560 \uC77C \uC5C6\uC74C."));
|
|
7400
7672
|
return;
|
|
7401
7673
|
}
|
|
7402
7674
|
const reason = readHardStopReason();
|
|
7403
7675
|
if (reason) {
|
|
7404
|
-
console.log(
|
|
7405
|
-
console.log(
|
|
7676
|
+
console.log(chalk37.yellow(" \u{1F4CB} HARD_STOP \uC0AC\uC720:"));
|
|
7677
|
+
console.log(chalk37.dim(` ${reason.split("\n").join("\n ")}`));
|
|
7406
7678
|
console.log("");
|
|
7407
7679
|
}
|
|
7408
7680
|
if (!opts.confirm) {
|
|
7409
7681
|
console.log(
|
|
7410
|
-
|
|
7682
|
+
chalk37.red(
|
|
7411
7683
|
" \u274C --confirm \uD50C\uB798\uADF8 \uC5C6\uC774\uB294 \uD574\uC81C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC790\uB3D9 \uD638\uCD9C \uAE08\uC9C0)."
|
|
7412
7684
|
)
|
|
7413
7685
|
);
|
|
7414
|
-
console.log(
|
|
7686
|
+
console.log(chalk37.yellow(" \uC0AC\uC720\uB97C \uD655\uC778\uD55C \uD6C4 \uB2E4\uC2DC: vhk resume --confirm"));
|
|
7415
7687
|
process.exitCode = 1;
|
|
7416
7688
|
return;
|
|
7417
7689
|
}
|
|
7418
7690
|
const removed = clearHardStop();
|
|
7419
7691
|
if (removed) {
|
|
7420
|
-
console.log(
|
|
7692
|
+
console.log(chalk37.green(" \u2705 HARD_STOP \uD574\uC81C. \uC790\uB3D9\uD654 \uC7AC\uAC1C \uAC00\uB2A5."));
|
|
7421
7693
|
} else {
|
|
7422
|
-
console.log(
|
|
7694
|
+
console.log(chalk37.dim(" \uD30C\uC77C\uC774 \uC774\uBBF8 \uC5C6\uC74C \u2014 no-op."));
|
|
7423
7695
|
}
|
|
7424
7696
|
}
|
|
7425
7697
|
|
|
@@ -7440,7 +7712,7 @@ async function guardCli(action, approved, run) {
|
|
|
7440
7712
|
}]);
|
|
7441
7713
|
return ok;
|
|
7442
7714
|
},
|
|
7443
|
-
log: (m) => console.log(
|
|
7715
|
+
log: (m) => console.log(chalk38.yellow(` ${m}`))
|
|
7444
7716
|
},
|
|
7445
7717
|
run
|
|
7446
7718
|
);
|
|
@@ -7453,7 +7725,7 @@ async function guardCliDefer(action, approved, run) {
|
|
|
7453
7725
|
approved,
|
|
7454
7726
|
// TTY 면 통과(명령이 자체 확인), 비대화형은 confirm 불가 → 가드가 차단.
|
|
7455
7727
|
confirm: async () => !!process.stdout.isTTY,
|
|
7456
|
-
log: (m) => console.log(
|
|
7728
|
+
log: (m) => console.log(chalk38.yellow(` ${m}`))
|
|
7457
7729
|
},
|
|
7458
7730
|
run
|
|
7459
7731
|
);
|
|
@@ -7498,7 +7770,8 @@ var KO_ALIASES = {
|
|
|
7498
7770
|
learn: "\uAD50\uD6C8",
|
|
7499
7771
|
resume: "\uC7AC\uAC1C",
|
|
7500
7772
|
pattern: "\uD328\uD134",
|
|
7501
|
-
evolve: "\uC9C4\uD654"
|
|
7773
|
+
evolve: "\uC9C4\uD654",
|
|
7774
|
+
work: "\uC791\uC5C5"
|
|
7502
7775
|
};
|
|
7503
7776
|
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
7777
|
program.configureHelp({
|
|
@@ -7546,7 +7819,9 @@ cloudCmd.command("pull").alias("\uB0B4\uB9AC\uAE30").argument("[gistId]", "\uBCF
|
|
|
7546
7819
|
await guardCli("cloud-pull", opts?.yes === true, () => cloudPull(gistId));
|
|
7547
7820
|
});
|
|
7548
7821
|
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(
|
|
7822
|
+
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) => {
|
|
7823
|
+
await doctor(opts);
|
|
7824
|
+
});
|
|
7550
7825
|
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
7826
|
await guardCli("save", opts?.yes === true, () => save());
|
|
7552
7827
|
});
|
|
@@ -7668,6 +7943,12 @@ memoryCmd.command("migrate").alias("\uB9C8\uC774\uADF8\uB808\uC774\uC158").descr
|
|
|
7668
7943
|
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
7944
|
await brief();
|
|
7670
7945
|
});
|
|
7946
|
+
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 () => {
|
|
7947
|
+
await work();
|
|
7948
|
+
});
|
|
7949
|
+
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 () => {
|
|
7950
|
+
await workHandoff();
|
|
7951
|
+
});
|
|
7671
7952
|
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
7953
|
await goalList();
|
|
7673
7954
|
});
|
|
@@ -7800,9 +8081,9 @@ if (isMainModule) {
|
|
|
7800
8081
|
}
|
|
7801
8082
|
} catch (err) {
|
|
7802
8083
|
if (isPromptAbortError(err)) {
|
|
7803
|
-
console.error(
|
|
8084
|
+
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
8085
|
} else {
|
|
7805
|
-
console.error(
|
|
8086
|
+
console.error(chalk38.red(`
|
|
7806
8087
|
\u274C ${err instanceof Error ? err.message : String(err)}`));
|
|
7807
8088
|
}
|
|
7808
8089
|
process.exitCode = 1;
|