@byh3071/vhk 2.0.2 → 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 +303 -37
- 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,13 +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
50
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
51
51
|
import fs13 from "fs";
|
|
52
|
-
import
|
|
52
|
+
import chalk36 from "chalk";
|
|
53
53
|
import inquirer14 from "inquirer";
|
|
54
54
|
|
|
55
55
|
// src/lib/nlp-router.ts
|
|
@@ -58,6 +58,7 @@ function normalize(input) {
|
|
|
58
58
|
}
|
|
59
59
|
var NLP_KEYWORDS = {
|
|
60
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"],
|
|
61
62
|
undo: ["\uB418\uB3CC\uB824", "\uB418\uB3CC\uB9AC\uAE30", "\uCDE8\uC18C", "\uC6D0\uB798\uB300\uB85C", "\uB864\uBC31", "\uB9AC\uC14B", "reset", "rollback"],
|
|
62
63
|
status: ["\uC0C1\uD0DC", "\uD604\uD669", "\uC5B4\uB5BB\uAC8C", "\uC5B4\uB54C", "\uC9C0\uAE08"],
|
|
63
64
|
diff: ["\uBCC0\uACBD", "\uBC14\uB010", "\uBB50\uBC14\uB01C", "\uBC14\uB00C\uC5C8", "\uCC28\uC774", "\uB2EC\uB77C\uC9C4", "\uC218\uC815\uB41C"]
|
|
@@ -321,6 +322,12 @@ var RULES = [
|
|
|
321
322
|
confidence: "high",
|
|
322
323
|
test: (t2) => /^출시$|출시\s*해|^publish$|퍼블리시|npm\s*(배포|출시)|버전\s*올|^릴리즈$|^release$/.test(t2) && !/체크|준비|회고/.test(t2)
|
|
323
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
|
+
},
|
|
324
331
|
// NLP 규칙은 한국어 표현만 매칭. 영문 `goal <sub>` 은 commander 가 직접 처리하도록
|
|
325
332
|
// 가로채기 금지 — vhk goal list / next / check / done 그대로 동작.
|
|
326
333
|
{
|
|
@@ -390,7 +397,8 @@ var CONTAINER_SUBCOMMANDS = {
|
|
|
390
397
|
design: ["palette"],
|
|
391
398
|
env: ["check"],
|
|
392
399
|
mode: ["lite", "standard", "strict"],
|
|
393
|
-
mission: ["set", "check", "clear"]
|
|
400
|
+
mission: ["set", "check", "clear"],
|
|
401
|
+
pattern: ["detect", "list", "dismiss"]
|
|
394
402
|
};
|
|
395
403
|
var CONTAINER_ALIASES = {
|
|
396
404
|
\uBAA9\uD45C: "goal",
|
|
@@ -401,7 +409,8 @@ var CONTAINER_ALIASES = {
|
|
|
401
409
|
\uB514\uC790\uC778: "design",
|
|
402
410
|
\uD658\uACBD\uBCC0\uC218: "env",
|
|
403
411
|
\uBAA8\uB4DC: "mode",
|
|
404
|
-
\uBBF8\uC158: "mission"
|
|
412
|
+
\uBBF8\uC158: "mission",
|
|
413
|
+
\uD328\uD134: "pattern"
|
|
405
414
|
};
|
|
406
415
|
|
|
407
416
|
// src/lib/cli-args.ts
|
|
@@ -535,7 +544,7 @@ function detectNaturalLanguageInput(argv) {
|
|
|
535
544
|
}
|
|
536
545
|
|
|
537
546
|
// src/lib/nlp-run.ts
|
|
538
|
-
import
|
|
547
|
+
import chalk34 from "chalk";
|
|
539
548
|
import inquirer13 from "inquirer";
|
|
540
549
|
|
|
541
550
|
// src/commands/gate.ts
|
|
@@ -775,7 +784,10 @@ function RULES_MD_TEMPLATE(name, description, stack) {
|
|
|
775
784
|
"",
|
|
776
785
|
"## \uAE30\uB85D \uADDC\uCE59",
|
|
777
786
|
"- \uC138\uC158 \uC885\uB8CC \uC2DC docs/log/YYYY-MM-DD-{\uC791\uC5C5\uBA85}.md \uC0DD\uC131",
|
|
778
|
-
"- \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)"
|
|
779
791
|
].join("\n");
|
|
780
792
|
}
|
|
781
793
|
|
|
@@ -6494,6 +6506,245 @@ async function missionClear() {
|
|
|
6494
6506
|
}
|
|
6495
6507
|
}
|
|
6496
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
|
+
|
|
6497
6748
|
// src/lib/risk-policy.ts
|
|
6498
6749
|
var HIGH_RISK_ACTIONS = [
|
|
6499
6750
|
"undo",
|
|
@@ -6664,6 +6915,8 @@ async function dispatchNlpRoute(route, input) {
|
|
|
6664
6915
|
return review();
|
|
6665
6916
|
case "mission":
|
|
6666
6917
|
return missionShow();
|
|
6918
|
+
case "pattern":
|
|
6919
|
+
return patternList();
|
|
6667
6920
|
}
|
|
6668
6921
|
}
|
|
6669
6922
|
var STATE_CHANGING_COMMANDS = /* @__PURE__ */ new Set([
|
|
@@ -6677,14 +6930,14 @@ function requiresConfirmation(route) {
|
|
|
6677
6930
|
async function runNaturalLanguageRoute(input) {
|
|
6678
6931
|
const route = routeNaturalLanguage(input);
|
|
6679
6932
|
if (!route) {
|
|
6680
|
-
console.log(
|
|
6933
|
+
console.log(chalk34.yellow(`
|
|
6681
6934
|
\u2753 "${input}" \u2014 ${ko.nlp.notMatched}
|
|
6682
6935
|
`));
|
|
6683
6936
|
return;
|
|
6684
6937
|
}
|
|
6685
6938
|
console.log("");
|
|
6686
|
-
console.log(
|
|
6687
|
-
console.log(
|
|
6939
|
+
console.log(chalk34.cyan(` \u{1F4AC} "${input}"`));
|
|
6940
|
+
console.log(chalk34.cyan(` \u2192 ${route.explanation}`));
|
|
6688
6941
|
if (requiresConfirmation(route)) {
|
|
6689
6942
|
const { confirm } = await inquirer13.prompt([{
|
|
6690
6943
|
type: "confirm",
|
|
@@ -6693,7 +6946,7 @@ async function runNaturalLanguageRoute(input) {
|
|
|
6693
6946
|
default: true
|
|
6694
6947
|
}]);
|
|
6695
6948
|
if (!confirm) {
|
|
6696
|
-
console.log(
|
|
6949
|
+
console.log(chalk34.dim(` ${ko.nlp.menuHint}`));
|
|
6697
6950
|
return;
|
|
6698
6951
|
}
|
|
6699
6952
|
}
|
|
@@ -6702,7 +6955,7 @@ async function runNaturalLanguageRoute(input) {
|
|
|
6702
6955
|
if (riskAction) {
|
|
6703
6956
|
await runGuarded(
|
|
6704
6957
|
riskAction,
|
|
6705
|
-
{ channel: "nl", approved: false, log: (m) => console.log(
|
|
6958
|
+
{ channel: "nl", approved: false, log: (m) => console.log(chalk34.yellow(` ${m}`)) },
|
|
6706
6959
|
() => dispatchNlpRoute(route, input)
|
|
6707
6960
|
);
|
|
6708
6961
|
return;
|
|
@@ -6711,80 +6964,80 @@ async function runNaturalLanguageRoute(input) {
|
|
|
6711
6964
|
}
|
|
6712
6965
|
|
|
6713
6966
|
// src/commands/agent.ts
|
|
6714
|
-
import
|
|
6967
|
+
import chalk35 from "chalk";
|
|
6715
6968
|
function activeGoalId() {
|
|
6716
6969
|
const goals = listGoals("goals");
|
|
6717
6970
|
const id = selectActiveId(goals);
|
|
6718
6971
|
return id ?? void 0;
|
|
6719
6972
|
}
|
|
6720
6973
|
async function blocker(description) {
|
|
6721
|
-
console.log(
|
|
6974
|
+
console.log(chalk35.bold(`
|
|
6722
6975
|
${ko.agent.blockerTitle}
|
|
6723
6976
|
`));
|
|
6724
6977
|
if (!description || !description.trim()) {
|
|
6725
|
-
console.log(
|
|
6726
|
-
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"'));
|
|
6727
6980
|
process.exitCode = 1;
|
|
6728
6981
|
return;
|
|
6729
6982
|
}
|
|
6730
6983
|
const goalId = activeGoalId();
|
|
6731
6984
|
const r = appendBlocker(description, goalId);
|
|
6732
|
-
console.log(
|
|
6985
|
+
console.log(chalk35.green(` \u2705 blocker \uAE30\uB85D (\uD604\uC7AC \uD65C\uC131 ${r.count}\uAC74)`));
|
|
6733
6986
|
if (r.hardStopTripped) {
|
|
6734
|
-
console.log(
|
|
6735
|
-
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."));
|
|
6736
6989
|
process.exitCode = 2;
|
|
6737
6990
|
}
|
|
6738
6991
|
}
|
|
6739
6992
|
async function learn(lesson) {
|
|
6740
|
-
console.log(
|
|
6993
|
+
console.log(chalk35.bold(`
|
|
6741
6994
|
${ko.agent.learnTitle}
|
|
6742
6995
|
`));
|
|
6743
6996
|
if (!lesson || !lesson.trim()) {
|
|
6744
|
-
console.log(
|
|
6745
|
-
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)"'));
|
|
6746
6999
|
process.exitCode = 1;
|
|
6747
7000
|
return;
|
|
6748
7001
|
}
|
|
6749
7002
|
const goalId = activeGoalId();
|
|
6750
7003
|
const entry = recordLesson(process.cwd(), lesson, goalId);
|
|
6751
7004
|
if (!entry) {
|
|
6752
|
-
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."));
|
|
6753
7006
|
process.exitCode = 1;
|
|
6754
7007
|
return;
|
|
6755
7008
|
}
|
|
6756
|
-
console.log(
|
|
6757
|
-
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."));
|
|
6758
7011
|
}
|
|
6759
7012
|
async function resume(opts = {}) {
|
|
6760
|
-
console.log(
|
|
7013
|
+
console.log(chalk35.bold(`
|
|
6761
7014
|
${ko.agent.resumeTitle}
|
|
6762
7015
|
`));
|
|
6763
7016
|
if (!isHardStopActive()) {
|
|
6764
|
-
console.log(
|
|
7017
|
+
console.log(chalk35.dim(" HARD_STOP \uD65C\uC131 \uC544\uB2D8 \u2014 \uD560 \uC77C \uC5C6\uC74C."));
|
|
6765
7018
|
return;
|
|
6766
7019
|
}
|
|
6767
7020
|
const reason = readHardStopReason();
|
|
6768
7021
|
if (reason) {
|
|
6769
|
-
console.log(
|
|
6770
|
-
console.log(
|
|
7022
|
+
console.log(chalk35.yellow(" \u{1F4CB} HARD_STOP \uC0AC\uC720:"));
|
|
7023
|
+
console.log(chalk35.dim(` ${reason.split("\n").join("\n ")}`));
|
|
6771
7024
|
console.log("");
|
|
6772
7025
|
}
|
|
6773
7026
|
if (!opts.confirm) {
|
|
6774
7027
|
console.log(
|
|
6775
|
-
|
|
7028
|
+
chalk35.red(
|
|
6776
7029
|
" \u274C --confirm \uD50C\uB798\uADF8 \uC5C6\uC774\uB294 \uD574\uC81C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC790\uB3D9 \uD638\uCD9C \uAE08\uC9C0)."
|
|
6777
7030
|
)
|
|
6778
7031
|
);
|
|
6779
|
-
console.log(
|
|
7032
|
+
console.log(chalk35.yellow(" \uC0AC\uC720\uB97C \uD655\uC778\uD55C \uD6C4 \uB2E4\uC2DC: vhk resume --confirm"));
|
|
6780
7033
|
process.exitCode = 1;
|
|
6781
7034
|
return;
|
|
6782
7035
|
}
|
|
6783
7036
|
const removed = clearHardStop();
|
|
6784
7037
|
if (removed) {
|
|
6785
|
-
console.log(
|
|
7038
|
+
console.log(chalk35.green(" \u2705 HARD_STOP \uD574\uC81C. \uC790\uB3D9\uD654 \uC7AC\uAC1C \uAC00\uB2A5."));
|
|
6786
7039
|
} else {
|
|
6787
|
-
console.log(
|
|
7040
|
+
console.log(chalk35.dim(" \uD30C\uC77C\uC774 \uC774\uBBF8 \uC5C6\uC74C \u2014 no-op."));
|
|
6788
7041
|
}
|
|
6789
7042
|
}
|
|
6790
7043
|
|
|
@@ -6805,7 +7058,7 @@ async function guardCli(action, approved, run) {
|
|
|
6805
7058
|
}]);
|
|
6806
7059
|
return ok;
|
|
6807
7060
|
},
|
|
6808
|
-
log: (m) => console.log(
|
|
7061
|
+
log: (m) => console.log(chalk36.yellow(` ${m}`))
|
|
6809
7062
|
},
|
|
6810
7063
|
run
|
|
6811
7064
|
);
|
|
@@ -6818,7 +7071,7 @@ async function guardCliDefer(action, approved, run) {
|
|
|
6818
7071
|
approved,
|
|
6819
7072
|
// TTY 면 통과(명령이 자체 확인), 비대화형은 confirm 불가 → 가드가 차단.
|
|
6820
7073
|
confirm: async () => !!process.stdout.isTTY,
|
|
6821
|
-
log: (m) => console.log(
|
|
7074
|
+
log: (m) => console.log(chalk36.yellow(` ${m}`))
|
|
6822
7075
|
},
|
|
6823
7076
|
run
|
|
6824
7077
|
);
|
|
@@ -6861,7 +7114,8 @@ var KO_ALIASES = {
|
|
|
6861
7114
|
mission: "\uBBF8\uC158",
|
|
6862
7115
|
blocker: "\uBE14\uB85C\uCEE4",
|
|
6863
7116
|
learn: "\uAD50\uD6C8",
|
|
6864
|
-
resume: "\uC7AC\uAC1C"
|
|
7117
|
+
resume: "\uC7AC\uAC1C",
|
|
7118
|
+
pattern: "\uD328\uD134"
|
|
6865
7119
|
};
|
|
6866
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());
|
|
6867
7121
|
program.configureHelp({
|
|
@@ -7061,6 +7315,18 @@ program.command("learn <lesson>").alias("\uAD50\uD6C8").description("\uAD50\uD6C
|
|
|
7061
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) => {
|
|
7062
7316
|
await guardCliDefer("resume", opts?.confirm === true, () => resume(opts));
|
|
7063
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
|
+
});
|
|
7064
7330
|
program.on("command:*", async (operands) => {
|
|
7065
7331
|
const unknown = operands[0] ?? "";
|
|
7066
7332
|
const rest = operands.slice(1);
|
|
@@ -7133,9 +7399,9 @@ if (isMainModule) {
|
|
|
7133
7399
|
}
|
|
7134
7400
|
} catch (err) {
|
|
7135
7401
|
if (isPromptAbortError(err)) {
|
|
7136
|
-
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)"));
|
|
7137
7403
|
} else {
|
|
7138
|
-
console.error(
|
|
7404
|
+
console.error(chalk36.red(`
|
|
7139
7405
|
\u274C ${err instanceof Error ? err.message : String(err)}`));
|
|
7140
7406
|
}
|
|
7141
7407
|
process.exitCode = 1;
|
package/dist/mcp/index.js
CHANGED
package/package.json
CHANGED