@byh3071/vhk 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-HCNU6K7D.js → chunk-ODNKCGUG.js} +35 -0
- package/dist/index.js +313 -39
- package/dist/mcp/index.js +1 -1
- package/package.json +1 -1
|
@@ -913,6 +913,11 @@ var ko = {
|
|
|
913
913
|
blockerTitle: "\u{1F6D1} Blocker \uAE30\uB85D",
|
|
914
914
|
learnTitle: "\u{1F9E0} Learning \uAE30\uB85D",
|
|
915
915
|
resumeTitle: "\u25B6\uFE0F HARD_STOP \uD574\uC81C"
|
|
916
|
+
},
|
|
917
|
+
pattern: {
|
|
918
|
+
detectTitle: "\uD328\uD134 \uAC10\uC9C0",
|
|
919
|
+
listTitle: "\uD328\uD134 \uBAA9\uB85D",
|
|
920
|
+
dismissTitle: "\uD328\uD134 dismiss"
|
|
916
921
|
}
|
|
917
922
|
};
|
|
918
923
|
function lookup(path7) {
|
|
@@ -2996,6 +3001,36 @@ ${cliStatus}
|
|
|
2996
3001
|
{ description: ".cursor/mcp.json \uC0DD\uC131/\uAC31\uC2E0 \u2014 vhk MCP \uC11C\uBC84 \uB4F1\uB85D (Cursor \uC7AC\uC2DC\uC791 \uD544\uC694)" },
|
|
2997
3002
|
async () => runVhkCli(["mcp-init"], "mcp-init")
|
|
2998
3003
|
);
|
|
3004
|
+
server.registerTool(
|
|
3005
|
+
"pattern-detect",
|
|
3006
|
+
{
|
|
3007
|
+
description: "active failures+successes 2\uCD95 \uBD84\uC11D \u2192 avoid/reinforce \uD6C4\uBCF4 \uAC10\uC9C0 \xB7 patterns[] \uAC31\uC2E0 (Goal 19)",
|
|
3008
|
+
inputSchema: {
|
|
3009
|
+
min: z.number().int().min(1).optional().describe("\uC784\uACC4 \uD69F\uC218 (\uAE30\uBCF8 3)")
|
|
3010
|
+
}
|
|
3011
|
+
},
|
|
3012
|
+
async ({ min }) => {
|
|
3013
|
+
const args = ["pattern", "detect", "--json"];
|
|
3014
|
+
if (min !== void 0) args.push("--min", String(min));
|
|
3015
|
+
return runVhkCli(args, "pattern detect");
|
|
3016
|
+
}
|
|
3017
|
+
);
|
|
3018
|
+
server.registerTool(
|
|
3019
|
+
"pattern-list",
|
|
3020
|
+
{
|
|
3021
|
+
description: "\uD328\uD134 \uD6C4\uBCF4 \uBAA9\uB85D \uC870\uD68C (avoid/reinforce \xB7 \uD65C\uC131 \uAE30\uBCF8) \u2014 Goal 19",
|
|
3022
|
+
inputSchema: {
|
|
3023
|
+
kind: z.enum(["avoid", "reinforce"]).optional().describe("\uC885\uB958 \uD544\uD130"),
|
|
3024
|
+
all: z.boolean().optional().describe("\uBCF4\uAD00(archived) \uD3EC\uD568")
|
|
3025
|
+
}
|
|
3026
|
+
},
|
|
3027
|
+
async ({ kind, all }) => {
|
|
3028
|
+
const args = ["pattern", "list", "--json"];
|
|
3029
|
+
if (kind) args.push("--kind", kind);
|
|
3030
|
+
if (all) args.push("--all");
|
|
3031
|
+
return runVhkCli(args, "pattern list");
|
|
3032
|
+
}
|
|
3033
|
+
);
|
|
2999
3034
|
return server;
|
|
3000
3035
|
}
|
|
3001
3036
|
async function startMcpServer() {
|
package/dist/index.js
CHANGED
|
@@ -43,12 +43,13 @@ import {
|
|
|
43
43
|
stripBom,
|
|
44
44
|
sync,
|
|
45
45
|
t
|
|
46
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-ODNKCGUG.js";
|
|
47
47
|
|
|
48
48
|
// src/index.ts
|
|
49
49
|
import { Command, Help } from "commander";
|
|
50
|
-
import {
|
|
51
|
-
import
|
|
50
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
51
|
+
import fs13 from "fs";
|
|
52
|
+
import chalk36 from "chalk";
|
|
52
53
|
import inquirer14 from "inquirer";
|
|
53
54
|
|
|
54
55
|
// src/lib/nlp-router.ts
|
|
@@ -57,6 +58,7 @@ function normalize(input) {
|
|
|
57
58
|
}
|
|
58
59
|
var NLP_KEYWORDS = {
|
|
59
60
|
save: ["\uC800\uC7A5", "\uC138\uC774\uBE0C", "\uCEE4\uBC0B", "\uC62C\uB824", "\uC62C\uB9AC\uAE30", "\uD478\uC2DC", "push", "commit"],
|
|
61
|
+
pattern: ["\uD328\uD134", "\uB418\uD480\uC774", "\uBC84\uB987", "pattern"],
|
|
60
62
|
undo: ["\uB418\uB3CC\uB824", "\uB418\uB3CC\uB9AC\uAE30", "\uCDE8\uC18C", "\uC6D0\uB798\uB300\uB85C", "\uB864\uBC31", "\uB9AC\uC14B", "reset", "rollback"],
|
|
61
63
|
status: ["\uC0C1\uD0DC", "\uD604\uD669", "\uC5B4\uB5BB\uAC8C", "\uC5B4\uB54C", "\uC9C0\uAE08"],
|
|
62
64
|
diff: ["\uBCC0\uACBD", "\uBC14\uB010", "\uBB50\uBC14\uB01C", "\uBC14\uB00C\uC5C8", "\uCC28\uC774", "\uB2EC\uB77C\uC9C4", "\uC218\uC815\uB41C"]
|
|
@@ -320,6 +322,12 @@ var RULES = [
|
|
|
320
322
|
confidence: "high",
|
|
321
323
|
test: (t2) => /^출시$|출시\s*해|^publish$|퍼블리시|npm\s*(배포|출시)|버전\s*올|^릴리즈$|^release$/.test(t2) && !/체크|준비|회고/.test(t2)
|
|
322
324
|
},
|
|
325
|
+
{
|
|
326
|
+
command: "pattern",
|
|
327
|
+
explanation: "\uD328\uD134 \uD6C4\uBCF4 \uBAA9\uB85D (vhk pattern list) \u2014 \uAC10\uC9C0\uB294 vhk pattern detect \uC9C1\uC811 \uC2E4\uD589",
|
|
328
|
+
confidence: "high",
|
|
329
|
+
test: (t2) => matchesKeywords(t2, "pattern") || /^pattern$/.test(t2)
|
|
330
|
+
},
|
|
323
331
|
// NLP 규칙은 한국어 표현만 매칭. 영문 `goal <sub>` 은 commander 가 직접 처리하도록
|
|
324
332
|
// 가로채기 금지 — vhk goal list / next / check / done 그대로 동작.
|
|
325
333
|
{
|
|
@@ -389,7 +397,8 @@ var CONTAINER_SUBCOMMANDS = {
|
|
|
389
397
|
design: ["palette"],
|
|
390
398
|
env: ["check"],
|
|
391
399
|
mode: ["lite", "standard", "strict"],
|
|
392
|
-
mission: ["set", "check", "clear"]
|
|
400
|
+
mission: ["set", "check", "clear"],
|
|
401
|
+
pattern: ["detect", "list", "dismiss"]
|
|
393
402
|
};
|
|
394
403
|
var CONTAINER_ALIASES = {
|
|
395
404
|
\uBAA9\uD45C: "goal",
|
|
@@ -400,7 +409,8 @@ var CONTAINER_ALIASES = {
|
|
|
400
409
|
\uB514\uC790\uC778: "design",
|
|
401
410
|
\uD658\uACBD\uBCC0\uC218: "env",
|
|
402
411
|
\uBAA8\uB4DC: "mode",
|
|
403
|
-
\uBBF8\uC158: "mission"
|
|
412
|
+
\uBBF8\uC158: "mission",
|
|
413
|
+
\uD328\uD134: "pattern"
|
|
404
414
|
};
|
|
405
415
|
|
|
406
416
|
// src/lib/cli-args.ts
|
|
@@ -534,7 +544,7 @@ function detectNaturalLanguageInput(argv) {
|
|
|
534
544
|
}
|
|
535
545
|
|
|
536
546
|
// src/lib/nlp-run.ts
|
|
537
|
-
import
|
|
547
|
+
import chalk34 from "chalk";
|
|
538
548
|
import inquirer13 from "inquirer";
|
|
539
549
|
|
|
540
550
|
// src/commands/gate.ts
|
|
@@ -774,7 +784,10 @@ function RULES_MD_TEMPLATE(name, description, stack) {
|
|
|
774
784
|
"",
|
|
775
785
|
"## \uAE30\uB85D \uADDC\uCE59",
|
|
776
786
|
"- \uC138\uC158 \uC885\uB8CC \uC2DC docs/log/YYYY-MM-DD-{\uC791\uC5C5\uBA85}.md \uC0DD\uC131",
|
|
777
|
-
"- \uAE30\uC220 \uC120\uD0DD \uC2DC docs/adr/ADR-{\uBC88\uD638}-{\uC81C\uBAA9}.md \uC0DD\uC131"
|
|
787
|
+
"- \uAE30\uC220 \uC120\uD0DD \uC2DC docs/adr/ADR-{\uBC88\uD638}-{\uC81C\uBAA9}.md \uC0DD\uC131",
|
|
788
|
+
'- \uAE30\uB2A5 \uC644\uC131 / \uC5D0\uB7EC \uD574\uACB0 / ADR / \uC138\uC158 \uC885\uB8CC \uC2DC Notion "\uBC14\uC774\uBE0C\uCF54\uB529 Dev Log" DB\uC5D0 1\uD589 \uC801\uC7AC (Notion MCP)',
|
|
789
|
+
'- \uC801\uC7AC \uC9C1\uC804 Dev Log DB \uC0C1\uB2E8 "AI \uC801\uC7AC \uADDC\uCE59" \uCF5C\uC544\uC6C3 \uD655\uC778 \u2014 \uC81C\uBAA9: (YYYY-MM-DD) \uD504\uB85C\uC81D\uD2B8\uBA85 - \uC81C\uBAA9',
|
|
790
|
+
"- \uD0DC\uADF8\uB294 \uAE30\uC874 \uC635\uC158\uB9CC \uC0AC\uC6A9, \uAC19\uC740 \uC791\uC5C5 \uC911\uBCF5 \uC801\uC7AC \uAE08\uC9C0(SoT Key)"
|
|
778
791
|
].join("\n");
|
|
779
792
|
}
|
|
780
793
|
|
|
@@ -6493,6 +6506,245 @@ async function missionClear() {
|
|
|
6493
6506
|
}
|
|
6494
6507
|
}
|
|
6495
6508
|
|
|
6509
|
+
// src/commands/pattern.ts
|
|
6510
|
+
import chalk33 from "chalk";
|
|
6511
|
+
var MIN_TAG_FREQ = 3;
|
|
6512
|
+
var STOPWORDS = /* @__PURE__ */ new Set(["the", "a", "an", "is", "are", "and", "or", "in", "on", "at", "to", "for", "of", "with", "it", "was", "be"]);
|
|
6513
|
+
function tokenize(text) {
|
|
6514
|
+
return text.toLowerCase().replace(/[^가-힣a-z0-9\s]/g, " ").split(/\s+/).filter((tok) => tok.length >= 2 && !STOPWORDS.has(tok));
|
|
6515
|
+
}
|
|
6516
|
+
function sigOf(kind, axis, signal) {
|
|
6517
|
+
return `${kind}:${axis}:${signal}`;
|
|
6518
|
+
}
|
|
6519
|
+
function isActive2(e) {
|
|
6520
|
+
return e.status !== "archived" && e.status !== "resolved";
|
|
6521
|
+
}
|
|
6522
|
+
function detectCandidates(mem, minFreq) {
|
|
6523
|
+
const candidates = [];
|
|
6524
|
+
function processBucket(entries, kind, getText) {
|
|
6525
|
+
const active = entries.filter(isActive2);
|
|
6526
|
+
const tagMap = /* @__PURE__ */ new Map();
|
|
6527
|
+
for (const e of active) {
|
|
6528
|
+
const uniqueTags = new Set(e.tags ?? []);
|
|
6529
|
+
for (const tag of uniqueTags) {
|
|
6530
|
+
if (tag === "no-goal") continue;
|
|
6531
|
+
if (!tagMap.has(tag)) tagMap.set(tag, []);
|
|
6532
|
+
tagMap.get(tag).push(e.id);
|
|
6533
|
+
}
|
|
6534
|
+
}
|
|
6535
|
+
for (const [tag, sources] of tagMap) {
|
|
6536
|
+
if (sources.length >= minFreq) {
|
|
6537
|
+
const sourceSet = new Set(sources);
|
|
6538
|
+
const involved = active.filter((e) => sourceSet.has(e.id));
|
|
6539
|
+
const sourceTags = [...new Set(involved.flatMap((e) => e.tags ?? []))];
|
|
6540
|
+
candidates.push({
|
|
6541
|
+
kind,
|
|
6542
|
+
axis: "tag",
|
|
6543
|
+
signal: tag,
|
|
6544
|
+
count: sources.length,
|
|
6545
|
+
sources,
|
|
6546
|
+
summary: `[${kind}] \uD0DC\uADF8 '${tag}' ${sources.length}\uAC74 \uBC18\uBCF5`,
|
|
6547
|
+
sourceTags
|
|
6548
|
+
});
|
|
6549
|
+
}
|
|
6550
|
+
}
|
|
6551
|
+
const tokenMap = /* @__PURE__ */ new Map();
|
|
6552
|
+
for (const e of active) {
|
|
6553
|
+
const text = getText(e);
|
|
6554
|
+
if (!text) continue;
|
|
6555
|
+
const unique = new Set(tokenize(text));
|
|
6556
|
+
for (const tok of unique) {
|
|
6557
|
+
if (!tokenMap.has(tok)) tokenMap.set(tok, /* @__PURE__ */ new Set());
|
|
6558
|
+
tokenMap.get(tok).add(e.id);
|
|
6559
|
+
}
|
|
6560
|
+
}
|
|
6561
|
+
for (const [tok, sourceSet] of tokenMap) {
|
|
6562
|
+
if (sourceSet.size >= minFreq) {
|
|
6563
|
+
const sources = [...sourceSet];
|
|
6564
|
+
const involved = active.filter((e) => sourceSet.has(e.id));
|
|
6565
|
+
const sourceTags = [...new Set(involved.flatMap((e) => e.tags ?? []))];
|
|
6566
|
+
candidates.push({
|
|
6567
|
+
kind,
|
|
6568
|
+
axis: "keyword",
|
|
6569
|
+
signal: tok,
|
|
6570
|
+
count: sourceSet.size,
|
|
6571
|
+
sources,
|
|
6572
|
+
summary: `[${kind}] \uD0A4\uC6CC\uB4DC '${tok}' ${sourceSet.size}\uAC74 \uBB38\uC11C`,
|
|
6573
|
+
sourceTags
|
|
6574
|
+
});
|
|
6575
|
+
}
|
|
6576
|
+
}
|
|
6577
|
+
}
|
|
6578
|
+
const failText = (e) => {
|
|
6579
|
+
const f = e;
|
|
6580
|
+
return f.lesson ?? f.content ?? "";
|
|
6581
|
+
};
|
|
6582
|
+
processBucket(mem.failures, "avoid", failText);
|
|
6583
|
+
processBucket(mem.successes, "reinforce", (e) => e.content ?? "");
|
|
6584
|
+
return candidates.sort((a, b) => b.count - a.count || a.signal.localeCompare(b.signal));
|
|
6585
|
+
}
|
|
6586
|
+
function nextPatternId(mem) {
|
|
6587
|
+
const re = /^p(\d+)$/;
|
|
6588
|
+
let max = 0;
|
|
6589
|
+
for (const p of mem.patterns) {
|
|
6590
|
+
const m = p.id.match(re);
|
|
6591
|
+
if (m) max = Math.max(max, Number(m[1]));
|
|
6592
|
+
}
|
|
6593
|
+
return `p${max + 1}`;
|
|
6594
|
+
}
|
|
6595
|
+
async function patternDetect(opts = {}) {
|
|
6596
|
+
const minFreq = opts.min !== void 0 ? parseInt(opts.min, 10) : MIN_TAG_FREQ;
|
|
6597
|
+
if (!Number.isFinite(minFreq) || minFreq < 1) {
|
|
6598
|
+
console.log(chalk33.red("\u274C --min \uC740 1 \uC774\uC0C1\uC758 \uC815\uC218\uC5EC\uC57C \uD569\uB2C8\uB2E4."));
|
|
6599
|
+
process.exitCode = 1;
|
|
6600
|
+
return;
|
|
6601
|
+
}
|
|
6602
|
+
const cwd = process.cwd();
|
|
6603
|
+
const loaded = loadForMutation(cwd);
|
|
6604
|
+
if (!loaded.ok) {
|
|
6605
|
+
console.log(chalk33.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."));
|
|
6606
|
+
process.exitCode = 1;
|
|
6607
|
+
return;
|
|
6608
|
+
}
|
|
6609
|
+
const mem = loaded.mem;
|
|
6610
|
+
const candidates = detectCandidates(mem, minFreq);
|
|
6611
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6612
|
+
let added = 0, updated = 0;
|
|
6613
|
+
for (const c of candidates) {
|
|
6614
|
+
const sig = sigOf(c.kind, c.axis, c.signal);
|
|
6615
|
+
const existing = mem.patterns.find((p) => {
|
|
6616
|
+
const pp = p;
|
|
6617
|
+
const sigMatch = pp._sig === sig;
|
|
6618
|
+
const fieldMatch = pp.kind === c.kind && pp.axis === c.axis && pp.signal === c.signal;
|
|
6619
|
+
return (sigMatch || fieldMatch) && pp.status !== "archived";
|
|
6620
|
+
});
|
|
6621
|
+
if (existing) {
|
|
6622
|
+
existing._sig = sig;
|
|
6623
|
+
existing.count = c.count;
|
|
6624
|
+
existing.sources = c.sources;
|
|
6625
|
+
existing.summary = c.summary;
|
|
6626
|
+
existing.tags = c.sourceTags;
|
|
6627
|
+
updated++;
|
|
6628
|
+
} else {
|
|
6629
|
+
const entry = {
|
|
6630
|
+
id: nextPatternId(mem),
|
|
6631
|
+
kind: c.kind,
|
|
6632
|
+
axis: c.axis,
|
|
6633
|
+
signal: c.signal,
|
|
6634
|
+
count: c.count,
|
|
6635
|
+
sources: c.sources,
|
|
6636
|
+
summary: c.summary,
|
|
6637
|
+
createdAt: now,
|
|
6638
|
+
status: "active",
|
|
6639
|
+
tags: c.sourceTags,
|
|
6640
|
+
_sig: sig
|
|
6641
|
+
};
|
|
6642
|
+
mem.patterns.push(entry);
|
|
6643
|
+
added++;
|
|
6644
|
+
}
|
|
6645
|
+
}
|
|
6646
|
+
if (added > 0 || updated > 0) {
|
|
6647
|
+
writeMemory(cwd, mem);
|
|
6648
|
+
}
|
|
6649
|
+
if (opts.json) {
|
|
6650
|
+
const active2 = mem.patterns.filter((p) => p.status !== "archived");
|
|
6651
|
+
console.log(JSON.stringify(active2, null, 2));
|
|
6652
|
+
return;
|
|
6653
|
+
}
|
|
6654
|
+
console.log(chalk33.bold("\n\u{1F50D} " + t("pattern.detectTitle")));
|
|
6655
|
+
console.log(chalk33.gray("\u2500".repeat(40)));
|
|
6656
|
+
console.log(chalk33.dim(` \uC784\uACC4: ${minFreq}\uD68C \uC774\uC0C1 \xB7 active failures+successes \uC785\uB825`));
|
|
6657
|
+
console.log(chalk33.dim(` \uD6C4\uBCF4: ${candidates.length}\uAC1C \uAC10\uC9C0 (\uCD94\uAC00 ${added} / \uAC31\uC2E0 ${updated})`));
|
|
6658
|
+
const active = mem.patterns.filter((p) => p.status !== "archived");
|
|
6659
|
+
if (active.length === 0) {
|
|
6660
|
+
console.log(chalk33.yellow("\n\u{1F4ED} \uC784\uACC4 \uC774\uC0C1 \uBC18\uBCF5 \uD328\uD134\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
6661
|
+
console.log(chalk33.gray(` failures/successes \uAC00 ${minFreq}\uAC1C \uC774\uC0C1 \uC313\uC774\uBA74 \uAC10\uC9C0\uB429\uB2C8\uB2E4.`));
|
|
6662
|
+
console.log(chalk33.gray(" --min N \uC73C\uB85C \uC784\uACC4\uB97C \uB0AE\uCD9C \uC218 \uC788\uC2B5\uB2C8\uB2E4."));
|
|
6663
|
+
return;
|
|
6664
|
+
}
|
|
6665
|
+
console.log(chalk33.cyan(`
|
|
6666
|
+
\uD328\uD134 \uD6C4\uBCF4 ${active.length}\uAC1C:
|
|
6667
|
+
`));
|
|
6668
|
+
for (const p of active.slice(0, 20)) {
|
|
6669
|
+
const icon = p.kind === "avoid" ? "\u26A0\uFE0F " : "\u2705";
|
|
6670
|
+
console.log(` [${p.id}] ${icon} (${p.kind}/${p.axis}) "${p.signal}" \u2014 ${p.count}\uAC74`);
|
|
6671
|
+
const preview = p.sources.slice(0, 5).join(", ") + (p.sources.length > 5 ? ` \uC678 ${p.sources.length - 5}\uAC74` : "");
|
|
6672
|
+
console.log(chalk33.dim(` \uADFC\uAC70: ${preview}`));
|
|
6673
|
+
console.log(chalk33.dim(` ${p.summary}`));
|
|
6674
|
+
}
|
|
6675
|
+
printNextStep({
|
|
6676
|
+
message: `\uD328\uD134 \uAC10\uC9C0 \uC644\uB8CC! ${active.length}\uAC1C \uD6C4\uBCF4.`,
|
|
6677
|
+
command: "vhk pattern list",
|
|
6678
|
+
cursorHint: "\uD328\uD134 \uBAA9\uB85D \uBCF4\uC5EC\uC918",
|
|
6679
|
+
alternative: "(Goal 20) vhk evolve \u2014 \uB2E4\uC74C \uB2E8\uACC4\uC5D0\uC11C \uBC18\uC601"
|
|
6680
|
+
});
|
|
6681
|
+
}
|
|
6682
|
+
async function patternList(opts = {}) {
|
|
6683
|
+
console.log(chalk33.bold("\n\u{1F50D} " + t("pattern.listTitle")));
|
|
6684
|
+
console.log(chalk33.gray("\u2500".repeat(40)));
|
|
6685
|
+
const mem = readMemory(process.cwd());
|
|
6686
|
+
let patterns = mem.patterns;
|
|
6687
|
+
if (!opts.all) patterns = patterns.filter((p) => p.status !== "archived");
|
|
6688
|
+
if (opts.kind === "avoid" || opts.kind === "reinforce") {
|
|
6689
|
+
patterns = patterns.filter((p) => p.kind === opts.kind);
|
|
6690
|
+
}
|
|
6691
|
+
if (opts.json) {
|
|
6692
|
+
console.log(JSON.stringify(patterns, null, 2));
|
|
6693
|
+
return;
|
|
6694
|
+
}
|
|
6695
|
+
if (patterns.length === 0) {
|
|
6696
|
+
console.log(chalk33.yellow("\n\u{1F4ED} \uD45C\uC2DC\uD560 \uD328\uD134\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
6697
|
+
console.log(chalk33.gray(" vhk pattern detect \uB85C \uAC10\uC9C0 \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694."));
|
|
6698
|
+
return;
|
|
6699
|
+
}
|
|
6700
|
+
console.log(chalk33.cyan(`
|
|
6701
|
+
${patterns.length}\uAC1C${opts.all ? " (\uBCF4\uAD00 \uD3EC\uD568)" : " (\uD65C\uC131)"}:
|
|
6702
|
+
`));
|
|
6703
|
+
for (const p of patterns) {
|
|
6704
|
+
const icon = p.kind === "avoid" ? "\u26A0\uFE0F " : "\u2705";
|
|
6705
|
+
const archived = p.status === "archived" ? "\u{1F4E6} " : "";
|
|
6706
|
+
console.log(` [${p.id}] ${archived}${icon} (${p.kind}/${p.axis}) "${p.signal}" \u2014 ${p.count}\uAC74`);
|
|
6707
|
+
if (p.summary) console.log(chalk33.dim(` ${p.summary}`));
|
|
6708
|
+
if (p.tags?.length) console.log(chalk33.blue(` \u{1F3F7}\uFE0F ${p.tags.join(", ")}`));
|
|
6709
|
+
}
|
|
6710
|
+
}
|
|
6711
|
+
async function patternDismiss(idStr) {
|
|
6712
|
+
if (!idStr?.trim()) {
|
|
6713
|
+
console.log(chalk33.red("\u274C \uD328\uD134 id \uB97C \uC785\uB825\uD574\uC8FC\uC138\uC694. \uC608: vhk pattern dismiss p1"));
|
|
6714
|
+
process.exitCode = 1;
|
|
6715
|
+
return;
|
|
6716
|
+
}
|
|
6717
|
+
const cwd = process.cwd();
|
|
6718
|
+
const loaded = loadForMutation(cwd);
|
|
6719
|
+
if (!loaded.ok) {
|
|
6720
|
+
console.log(chalk33.red("\u274C memory.json \uC190\uC0C1 \uC758\uC2EC \u2014 dismiss \uC911\uB2E8 (\uC6D0\uBCF8 \uBCF4\uC874)."));
|
|
6721
|
+
process.exitCode = 1;
|
|
6722
|
+
return;
|
|
6723
|
+
}
|
|
6724
|
+
const mem = loaded.mem;
|
|
6725
|
+
const pattern = mem.patterns.find((p) => p.id === idStr.trim());
|
|
6726
|
+
if (!pattern) {
|
|
6727
|
+
console.log(chalk33.red(`\u274C \uD328\uD134 '${idStr}' \uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`));
|
|
6728
|
+
console.log(chalk33.gray(" vhk pattern list --all \uB85C \uBAA9\uB85D \uD655\uC778."));
|
|
6729
|
+
process.exitCode = 1;
|
|
6730
|
+
return;
|
|
6731
|
+
}
|
|
6732
|
+
if (pattern.status === "archived") {
|
|
6733
|
+
console.log(chalk33.dim(` \uC774\uBBF8 \uBCF4\uAD00\uB41C \uD328\uD134\uC785\uB2C8\uB2E4 \u2014 \uBCC0\uACBD \uC5C6\uC74C: ${pattern.id}`));
|
|
6734
|
+
return;
|
|
6735
|
+
}
|
|
6736
|
+
pattern.status = "archived";
|
|
6737
|
+
writeMemory(cwd, mem);
|
|
6738
|
+
console.log(chalk33.green(`
|
|
6739
|
+
\u{1F4E6} \uD328\uD134 dismiss(\uBCF4\uAD00)\uB428: [${pattern.id}] ${pattern.summary ?? pattern.signal}`));
|
|
6740
|
+
console.log(chalk33.dim(" \uC624\uD0D0\uC73C\uB85C \uD310\uB2E8. detect \uC7AC\uC2E4\uD589 \uC2DC \uC7AC\uC81C\uC548 \uC548 \uB428."));
|
|
6741
|
+
printNextStep({
|
|
6742
|
+
message: "\uD328\uD134 dismiss \uC644\uB8CC!",
|
|
6743
|
+
command: "vhk pattern list",
|
|
6744
|
+
cursorHint: "\uB0A8\uC740 \uD328\uD134 \uBCF4\uC5EC\uC918"
|
|
6745
|
+
});
|
|
6746
|
+
}
|
|
6747
|
+
|
|
6496
6748
|
// src/lib/risk-policy.ts
|
|
6497
6749
|
var HIGH_RISK_ACTIONS = [
|
|
6498
6750
|
"undo",
|
|
@@ -6663,6 +6915,8 @@ async function dispatchNlpRoute(route, input) {
|
|
|
6663
6915
|
return review();
|
|
6664
6916
|
case "mission":
|
|
6665
6917
|
return missionShow();
|
|
6918
|
+
case "pattern":
|
|
6919
|
+
return patternList();
|
|
6666
6920
|
}
|
|
6667
6921
|
}
|
|
6668
6922
|
var STATE_CHANGING_COMMANDS = /* @__PURE__ */ new Set([
|
|
@@ -6676,14 +6930,14 @@ function requiresConfirmation(route) {
|
|
|
6676
6930
|
async function runNaturalLanguageRoute(input) {
|
|
6677
6931
|
const route = routeNaturalLanguage(input);
|
|
6678
6932
|
if (!route) {
|
|
6679
|
-
console.log(
|
|
6933
|
+
console.log(chalk34.yellow(`
|
|
6680
6934
|
\u2753 "${input}" \u2014 ${ko.nlp.notMatched}
|
|
6681
6935
|
`));
|
|
6682
6936
|
return;
|
|
6683
6937
|
}
|
|
6684
6938
|
console.log("");
|
|
6685
|
-
console.log(
|
|
6686
|
-
console.log(
|
|
6939
|
+
console.log(chalk34.cyan(` \u{1F4AC} "${input}"`));
|
|
6940
|
+
console.log(chalk34.cyan(` \u2192 ${route.explanation}`));
|
|
6687
6941
|
if (requiresConfirmation(route)) {
|
|
6688
6942
|
const { confirm } = await inquirer13.prompt([{
|
|
6689
6943
|
type: "confirm",
|
|
@@ -6692,7 +6946,7 @@ async function runNaturalLanguageRoute(input) {
|
|
|
6692
6946
|
default: true
|
|
6693
6947
|
}]);
|
|
6694
6948
|
if (!confirm) {
|
|
6695
|
-
console.log(
|
|
6949
|
+
console.log(chalk34.dim(` ${ko.nlp.menuHint}`));
|
|
6696
6950
|
return;
|
|
6697
6951
|
}
|
|
6698
6952
|
}
|
|
@@ -6701,7 +6955,7 @@ async function runNaturalLanguageRoute(input) {
|
|
|
6701
6955
|
if (riskAction) {
|
|
6702
6956
|
await runGuarded(
|
|
6703
6957
|
riskAction,
|
|
6704
|
-
{ channel: "nl", approved: false, log: (m) => console.log(
|
|
6958
|
+
{ channel: "nl", approved: false, log: (m) => console.log(chalk34.yellow(` ${m}`)) },
|
|
6705
6959
|
() => dispatchNlpRoute(route, input)
|
|
6706
6960
|
);
|
|
6707
6961
|
return;
|
|
@@ -6710,80 +6964,80 @@ async function runNaturalLanguageRoute(input) {
|
|
|
6710
6964
|
}
|
|
6711
6965
|
|
|
6712
6966
|
// src/commands/agent.ts
|
|
6713
|
-
import
|
|
6967
|
+
import chalk35 from "chalk";
|
|
6714
6968
|
function activeGoalId() {
|
|
6715
6969
|
const goals = listGoals("goals");
|
|
6716
6970
|
const id = selectActiveId(goals);
|
|
6717
6971
|
return id ?? void 0;
|
|
6718
6972
|
}
|
|
6719
6973
|
async function blocker(description) {
|
|
6720
|
-
console.log(
|
|
6974
|
+
console.log(chalk35.bold(`
|
|
6721
6975
|
${ko.agent.blockerTitle}
|
|
6722
6976
|
`));
|
|
6723
6977
|
if (!description || !description.trim()) {
|
|
6724
|
-
console.log(
|
|
6725
|
-
console.log(
|
|
6978
|
+
console.log(chalk35.red(" \u274C \uBE14\uB85C\uCEE4 \uC124\uBA85\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694."));
|
|
6979
|
+
console.log(chalk35.dim(' \uC608: vhk blocker "tsc \uC5D0\uB7EC \u2014 simple-git \uD0C0\uC785 \uD638\uD658"'));
|
|
6726
6980
|
process.exitCode = 1;
|
|
6727
6981
|
return;
|
|
6728
6982
|
}
|
|
6729
6983
|
const goalId = activeGoalId();
|
|
6730
6984
|
const r = appendBlocker(description, goalId);
|
|
6731
|
-
console.log(
|
|
6985
|
+
console.log(chalk35.green(` \u2705 blocker \uAE30\uB85D (\uD604\uC7AC \uD65C\uC131 ${r.count}\uAC74)`));
|
|
6732
6986
|
if (r.hardStopTripped) {
|
|
6733
|
-
console.log(
|
|
6734
|
-
console.log(
|
|
6987
|
+
console.log(chalk35.red.bold(" \u{1F6D1} HARD_STOP \uC790\uB3D9 \uC0DD\uC131 \u2014 \uBAA8\uB4E0 \uC790\uB3D9\uD654 \uC911\uB2E8."));
|
|
6988
|
+
console.log(chalk35.yellow(" \uC0AC\uB78C \uAC80\uD1A0 \uD6C4 `vhk resume --confirm` \uC73C\uB85C\uB9CC \uD574\uC81C."));
|
|
6735
6989
|
process.exitCode = 2;
|
|
6736
6990
|
}
|
|
6737
6991
|
}
|
|
6738
6992
|
async function learn(lesson) {
|
|
6739
|
-
console.log(
|
|
6993
|
+
console.log(chalk35.bold(`
|
|
6740
6994
|
${ko.agent.learnTitle}
|
|
6741
6995
|
`));
|
|
6742
6996
|
if (!lesson || !lesson.trim()) {
|
|
6743
|
-
console.log(
|
|
6744
|
-
console.log(
|
|
6997
|
+
console.log(chalk35.red(" \u274C \uAD50\uD6C8 \uB0B4\uC6A9\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694."));
|
|
6998
|
+
console.log(chalk35.dim(' \uC608: vhk learn "PowerShell \uC5D0\uC11C\uB294 ; \uC0AC\uC6A9 (&& \uBBF8\uC9C0\uC6D0)"'));
|
|
6745
6999
|
process.exitCode = 1;
|
|
6746
7000
|
return;
|
|
6747
7001
|
}
|
|
6748
7002
|
const goalId = activeGoalId();
|
|
6749
7003
|
const entry = recordLesson(process.cwd(), lesson, goalId);
|
|
6750
7004
|
if (!entry) {
|
|
6751
|
-
console.log(
|
|
7005
|
+
console.log(chalk35.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."));
|
|
6752
7006
|
process.exitCode = 1;
|
|
6753
7007
|
return;
|
|
6754
7008
|
}
|
|
6755
|
-
console.log(
|
|
6756
|
-
console.log(
|
|
7009
|
+
console.log(chalk35.green(` \u2705 \uAD50\uD6C8 \uAE30\uB85D \u2192 memory failures.lesson (${entry.id})`));
|
|
7010
|
+
console.log(chalk35.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."));
|
|
6757
7011
|
}
|
|
6758
7012
|
async function resume(opts = {}) {
|
|
6759
|
-
console.log(
|
|
7013
|
+
console.log(chalk35.bold(`
|
|
6760
7014
|
${ko.agent.resumeTitle}
|
|
6761
7015
|
`));
|
|
6762
7016
|
if (!isHardStopActive()) {
|
|
6763
|
-
console.log(
|
|
7017
|
+
console.log(chalk35.dim(" HARD_STOP \uD65C\uC131 \uC544\uB2D8 \u2014 \uD560 \uC77C \uC5C6\uC74C."));
|
|
6764
7018
|
return;
|
|
6765
7019
|
}
|
|
6766
7020
|
const reason = readHardStopReason();
|
|
6767
7021
|
if (reason) {
|
|
6768
|
-
console.log(
|
|
6769
|
-
console.log(
|
|
7022
|
+
console.log(chalk35.yellow(" \u{1F4CB} HARD_STOP \uC0AC\uC720:"));
|
|
7023
|
+
console.log(chalk35.dim(` ${reason.split("\n").join("\n ")}`));
|
|
6770
7024
|
console.log("");
|
|
6771
7025
|
}
|
|
6772
7026
|
if (!opts.confirm) {
|
|
6773
7027
|
console.log(
|
|
6774
|
-
|
|
7028
|
+
chalk35.red(
|
|
6775
7029
|
" \u274C --confirm \uD50C\uB798\uADF8 \uC5C6\uC774\uB294 \uD574\uC81C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC790\uB3D9 \uD638\uCD9C \uAE08\uC9C0)."
|
|
6776
7030
|
)
|
|
6777
7031
|
);
|
|
6778
|
-
console.log(
|
|
7032
|
+
console.log(chalk35.yellow(" \uC0AC\uC720\uB97C \uD655\uC778\uD55C \uD6C4 \uB2E4\uC2DC: vhk resume --confirm"));
|
|
6779
7033
|
process.exitCode = 1;
|
|
6780
7034
|
return;
|
|
6781
7035
|
}
|
|
6782
7036
|
const removed = clearHardStop();
|
|
6783
7037
|
if (removed) {
|
|
6784
|
-
console.log(
|
|
7038
|
+
console.log(chalk35.green(" \u2705 HARD_STOP \uD574\uC81C. \uC790\uB3D9\uD654 \uC7AC\uAC1C \uAC00\uB2A5."));
|
|
6785
7039
|
} else {
|
|
6786
|
-
console.log(
|
|
7040
|
+
console.log(chalk35.dim(" \uD30C\uC77C\uC774 \uC774\uBBF8 \uC5C6\uC74C \u2014 no-op."));
|
|
6787
7041
|
}
|
|
6788
7042
|
}
|
|
6789
7043
|
|
|
@@ -6804,7 +7058,7 @@ async function guardCli(action, approved, run) {
|
|
|
6804
7058
|
}]);
|
|
6805
7059
|
return ok;
|
|
6806
7060
|
},
|
|
6807
|
-
log: (m) => console.log(
|
|
7061
|
+
log: (m) => console.log(chalk36.yellow(` ${m}`))
|
|
6808
7062
|
},
|
|
6809
7063
|
run
|
|
6810
7064
|
);
|
|
@@ -6817,7 +7071,7 @@ async function guardCliDefer(action, approved, run) {
|
|
|
6817
7071
|
approved,
|
|
6818
7072
|
// TTY 면 통과(명령이 자체 확인), 비대화형은 confirm 불가 → 가드가 차단.
|
|
6819
7073
|
confirm: async () => !!process.stdout.isTTY,
|
|
6820
|
-
log: (m) => console.log(
|
|
7074
|
+
log: (m) => console.log(chalk36.yellow(` ${m}`))
|
|
6821
7075
|
},
|
|
6822
7076
|
run
|
|
6823
7077
|
);
|
|
@@ -6860,7 +7114,8 @@ var KO_ALIASES = {
|
|
|
6860
7114
|
mission: "\uBBF8\uC158",
|
|
6861
7115
|
blocker: "\uBE14\uB85C\uCEE4",
|
|
6862
7116
|
learn: "\uAD50\uD6C8",
|
|
6863
|
-
resume: "\uC7AC\uAC1C"
|
|
7117
|
+
resume: "\uC7AC\uAC1C",
|
|
7118
|
+
pattern: "\uD328\uD134"
|
|
6864
7119
|
};
|
|
6865
7120
|
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());
|
|
6866
7121
|
program.configureHelp({
|
|
@@ -7060,6 +7315,18 @@ program.command("learn <lesson>").alias("\uAD50\uD6C8").description("\uAD50\uD6C
|
|
|
7060
7315
|
program.command("resume").alias("\uC7AC\uAC1C").option("--confirm", "\uC0AC\uB78C \uD655\uC778 \u2014 \uC790\uB3D9 \uD638\uCD9C \uAE08\uC9C0 (Forbidden \uC704\uBC18)").description(".vhk/HARD_STOP \uD574\uC81C (\uC0AC\uC6A9\uC790\uAC00 \uC0AC\uC720 \uD655\uC778 \uD6C4 --confirm \uD544\uC694)").action(async (opts) => {
|
|
7061
7316
|
await guardCliDefer("resume", opts?.confirm === true, () => resume(opts));
|
|
7062
7317
|
});
|
|
7318
|
+
var patternCmd = program.command("pattern").alias("\uD328\uD134").description("\uBC18\uBCF5 \uD328\uD134 \uAC10\uC9C0\xB7\uBAA9\uB85D\xB7dismiss (avoid/reinforce \uD6C4\uBCF4) \u2014 Goal 19").action(async () => {
|
|
7319
|
+
await patternList();
|
|
7320
|
+
});
|
|
7321
|
+
patternCmd.command("detect").alias("\uAC10\uC9C0").option("--min <n>", "\uC784\uACC4 \uD69F\uC218 (\uAE30\uBCF8 3)", "3").option("--json", "JSON \uCD9C\uB825 (CI/MCP\uC6A9)").description("active failures+successes 2\uCD95 \uBD84\uC11D \u2192 patterns[] \uAC31\uC2E0").action(async (opts) => {
|
|
7322
|
+
await patternDetect(opts);
|
|
7323
|
+
});
|
|
7324
|
+
patternCmd.command("list").alias("\uBAA9\uB85D").option("--kind <kind>", "avoid|reinforce \uD544\uD130").option("--all", "\uBCF4\uAD00(archived) \uD3EC\uD568").option("--json", "JSON \uCD9C\uB825 (CI/MCP\uC6A9)").description("\uD328\uD134 \uD6C4\uBCF4 \uBAA9\uB85D (\uAE30\uBCF8 \uD65C\uC131)").action(async (opts) => {
|
|
7325
|
+
await patternList(opts);
|
|
7326
|
+
});
|
|
7327
|
+
patternCmd.command("dismiss <id>").alias("\uBCF4\uAD00").description("\uC624\uD0D0 \uD328\uD134 dismiss (\u2192archived, \uC7AC\uC81C\uC548 \uC548 \uB428)").action(async (id) => {
|
|
7328
|
+
await patternDismiss(id);
|
|
7329
|
+
});
|
|
7063
7330
|
program.on("command:*", async (operands) => {
|
|
7064
7331
|
const unknown = operands[0] ?? "";
|
|
7065
7332
|
const rest = operands.slice(1);
|
|
@@ -7114,7 +7381,14 @@ program.action(async () => {
|
|
|
7114
7381
|
return diff();
|
|
7115
7382
|
}
|
|
7116
7383
|
});
|
|
7117
|
-
var
|
|
7384
|
+
var getRealPath = (p) => {
|
|
7385
|
+
try {
|
|
7386
|
+
return fs13.realpathSync(p);
|
|
7387
|
+
} catch {
|
|
7388
|
+
return p;
|
|
7389
|
+
}
|
|
7390
|
+
};
|
|
7391
|
+
var isMainModule = !!process.argv[1] && getRealPath(fileURLToPath4(import.meta.url)) === getRealPath(process.argv[1]);
|
|
7118
7392
|
if (isMainModule) {
|
|
7119
7393
|
try {
|
|
7120
7394
|
const nlInput = detectNaturalLanguageInput(process.argv);
|
|
@@ -7125,9 +7399,9 @@ if (isMainModule) {
|
|
|
7125
7399
|
}
|
|
7126
7400
|
} catch (err) {
|
|
7127
7401
|
if (isPromptAbortError(err)) {
|
|
7128
|
-
console.error(
|
|
7402
|
+
console.error(chalk36.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)"));
|
|
7129
7403
|
} else {
|
|
7130
|
-
console.error(
|
|
7404
|
+
console.error(chalk36.red(`
|
|
7131
7405
|
\u274C ${err instanceof Error ? err.message : String(err)}`));
|
|
7132
7406
|
}
|
|
7133
7407
|
process.exitCode = 1;
|
package/dist/mcp/index.js
CHANGED
package/package.json
CHANGED