@byh3071/vhk 1.8.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -8
- package/dist/{chunk-RXDOM4QT.js → chunk-HCNU6K7D.js} +13 -2
- package/dist/index.js +758 -241
- package/dist/mcp/index.js +1 -1
- package/package.json +72 -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-HCNU6K7D.js";
|
|
47
47
|
|
|
48
48
|
// src/index.ts
|
|
49
49
|
import { Command, Help } from "commander";
|
|
50
50
|
import { pathToFileURL } from "url";
|
|
51
|
-
import
|
|
52
|
-
import
|
|
51
|
+
import chalk35 from "chalk";
|
|
52
|
+
import inquirer14 from "inquirer";
|
|
53
53
|
|
|
54
54
|
// src/lib/nlp-router.ts
|
|
55
55
|
function normalize(input) {
|
|
@@ -129,6 +129,12 @@ var RULES = [
|
|
|
129
129
|
confidence: "high",
|
|
130
130
|
test: (t2) => /적대\s*검증|자기\s*검증|거짓\s*완료|완료\s*심문|^review$|^검토$/.test(t2)
|
|
131
131
|
},
|
|
132
|
+
{
|
|
133
|
+
command: "mission",
|
|
134
|
+
explanation: "\uBBF8\uC158 \uACC4\uC57D \u2014 \uC791\uC5C5 \uBC94\uC704\xB7\uAE08\uC9C0\uC120 \uC120\uC5B8/\uAC80\uC99D (vhk mission)",
|
|
135
|
+
confidence: "high",
|
|
136
|
+
test: (t2) => /미션\s*계약|작업\s*범위|범위\s*검증|^mission$|^미션$/.test(t2)
|
|
137
|
+
},
|
|
132
138
|
{
|
|
133
139
|
command: "init",
|
|
134
140
|
explanation: "\uBB38\uC11C/\uD558\uB124\uC2A4 \uD30C\uC77C\uB9CC \uC0DD\uC131 (vhk init) \u2014 git/MCP/context\uB294 \uC81C\uC678",
|
|
@@ -177,11 +183,20 @@ var RULES = [
|
|
|
177
183
|
confidence: "high",
|
|
178
184
|
test: (t2) => /감사|취약점|audit|vulnerability|보안\s*감사|보안\s*취약|의존성\s*취약/.test(t2)
|
|
179
185
|
},
|
|
186
|
+
// memory 마이그레이션은 패키지매니저 migrate 보다 **먼저** 평가 — "기억/메모리 마이그레이트" 가
|
|
187
|
+
// pnpm 전환(vhk migrate)으로 새지 않도록. 기억/메모리/memory 한정이라 bare "마이그레이트"는 안 가로챔.
|
|
188
|
+
{
|
|
189
|
+
command: "memory",
|
|
190
|
+
args: ["migrate"],
|
|
191
|
+
explanation: "memory.json v1 \u2192 v2 \uB9C8\uC774\uADF8\uB808\uC774\uC158 (vhk memory migrate)",
|
|
192
|
+
confidence: "high",
|
|
193
|
+
test: (t2) => /(기억|메모리|memory)\s*(을|를)?\s*(마이그레이|migrat)/.test(t2)
|
|
194
|
+
},
|
|
180
195
|
{
|
|
181
196
|
command: "migrate",
|
|
182
197
|
explanation: "\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800 \uC804\uD658 (vhk migrate)",
|
|
183
198
|
confidence: "high",
|
|
184
|
-
test: (t2) =>
|
|
199
|
+
test: (t2) => /전환|마이그레이(트|션)|migrate|패키지\s*매니저|npm.*pnpm|pnpm.*npm|yarn.*전환|npm.*전환|pnpm.*전환/.test(t2) && !/(기억|메모리|memory)\s*(을|를)?\s*(마이그레이|migrat)/.test(t2)
|
|
185
200
|
},
|
|
186
201
|
{
|
|
187
202
|
command: "update",
|
|
@@ -205,7 +220,9 @@ var RULES = [
|
|
|
205
220
|
command: "memory",
|
|
206
221
|
explanation: "\uAE30\uC5B5 \uBAA9\uB85D \uC870\uD68C (vhk memory list)",
|
|
207
222
|
confidence: "high",
|
|
208
|
-
|
|
223
|
+
// 보관(archive)/해결(resolve)/마이그레이션은 list 가 아니다 → **제외 토큰 한 곳**에서 오라우팅 차단.
|
|
224
|
+
// archive/resolve 는 <번호> 인자가 필요해 NL 미지원 → 매칭 안 되면 notMatched 가 정직(잘못된 list 실행 금지).
|
|
225
|
+
test: (t2) => /^기억$|기억\s*(목록|보|확인|뭐)|memory.*list|결정사항\s*(목록|확인|보여)/.test(t2) && !/(추가|add|삭제|remove|저장|기록해|보관|아카이브|archive|마이그레이|migrat|해결|복구)/.test(t2)
|
|
209
226
|
},
|
|
210
227
|
{
|
|
211
228
|
command: "brief",
|
|
@@ -366,12 +383,13 @@ function extractNotionUrl(input) {
|
|
|
366
383
|
var CONTAINER_SUBCOMMANDS = {
|
|
367
384
|
goal: ["list", "next", "check", "init", "done", "sync"],
|
|
368
385
|
ref: ["add", "list", "open"],
|
|
369
|
-
memory: ["add", "list", "remove"],
|
|
386
|
+
memory: ["add", "list", "remove", "archive", "resolve", "unarchive", "migrate"],
|
|
370
387
|
cloud: ["push", "pull"],
|
|
371
388
|
secure: ["scan"],
|
|
372
389
|
design: ["palette"],
|
|
373
390
|
env: ["check"],
|
|
374
|
-
mode: ["lite", "standard", "strict"]
|
|
391
|
+
mode: ["lite", "standard", "strict"],
|
|
392
|
+
mission: ["set", "check", "clear"]
|
|
375
393
|
};
|
|
376
394
|
var CONTAINER_ALIASES = {
|
|
377
395
|
\uBAA9\uD45C: "goal",
|
|
@@ -381,7 +399,8 @@ var CONTAINER_ALIASES = {
|
|
|
381
399
|
\uBCF4\uC548: "secure",
|
|
382
400
|
\uB514\uC790\uC778: "design",
|
|
383
401
|
\uD658\uACBD\uBCC0\uC218: "env",
|
|
384
|
-
\uBAA8\uB4DC: "mode"
|
|
402
|
+
\uBAA8\uB4DC: "mode",
|
|
403
|
+
\uBBF8\uC158: "mission"
|
|
385
404
|
};
|
|
386
405
|
|
|
387
406
|
// src/lib/cli-args.ts
|
|
@@ -476,6 +495,8 @@ var KNOWN_COMMAND_TOKENS = /* @__PURE__ */ new Set([
|
|
|
476
495
|
"\uC0AC\uC804\uC810\uAC80",
|
|
477
496
|
"review",
|
|
478
497
|
"\uAC80\uD1A0",
|
|
498
|
+
"mission",
|
|
499
|
+
"\uBBF8\uC158",
|
|
479
500
|
"help"
|
|
480
501
|
]);
|
|
481
502
|
function isOptionToken(token) {
|
|
@@ -513,8 +534,8 @@ function detectNaturalLanguageInput(argv) {
|
|
|
513
534
|
}
|
|
514
535
|
|
|
515
536
|
// src/lib/nlp-run.ts
|
|
516
|
-
import
|
|
517
|
-
import
|
|
537
|
+
import chalk33 from "chalk";
|
|
538
|
+
import inquirer13 from "inquirer";
|
|
518
539
|
|
|
519
540
|
// src/commands/gate.ts
|
|
520
541
|
import inquirer from "inquirer";
|
|
@@ -1695,32 +1716,6 @@ ${line}
|
|
|
1695
1716
|
}
|
|
1696
1717
|
return { count, hardStopTripped };
|
|
1697
1718
|
}
|
|
1698
|
-
function appendLearning(lesson, goalId) {
|
|
1699
|
-
ensureStateDir();
|
|
1700
|
-
const tag = goalId !== void 0 ? `goal-${goalId}` : "no-goal";
|
|
1701
|
-
const line = `- [${isoDate()} ${tag}] ${lesson.trim()}`;
|
|
1702
|
-
if (!existsSync(LEARNINGS_PATH)) {
|
|
1703
|
-
writeFileSync(
|
|
1704
|
-
LEARNINGS_PATH,
|
|
1705
|
-
`# Learnings
|
|
1706
|
-
|
|
1707
|
-
_Append-only. \uD55C \uC904 = \uD55C \uAD50\uD6C8._
|
|
1708
|
-
|
|
1709
|
-
${line}
|
|
1710
|
-
`,
|
|
1711
|
-
"utf-8"
|
|
1712
|
-
);
|
|
1713
|
-
} else {
|
|
1714
|
-
appendFileSync(LEARNINGS_PATH, `${line}
|
|
1715
|
-
`, "utf-8");
|
|
1716
|
-
}
|
|
1717
|
-
}
|
|
1718
|
-
function getRecentLearnings(limit = 3) {
|
|
1719
|
-
if (!existsSync(LEARNINGS_PATH)) return [];
|
|
1720
|
-
const lines = readFileSync(LEARNINGS_PATH, "utf-8").split(/\r?\n/);
|
|
1721
|
-
const entries = lines.filter((l) => l.startsWith("- ["));
|
|
1722
|
-
return entries.slice(-limit);
|
|
1723
|
-
}
|
|
1724
1719
|
function getActiveBlockers(limit = 3) {
|
|
1725
1720
|
if (!existsSync(BLOCKERS_PATH)) return [];
|
|
1726
1721
|
const lines = readFileSync(BLOCKERS_PATH, "utf-8").split(/\r?\n/);
|
|
@@ -4434,15 +4429,432 @@ async function update() {
|
|
|
4434
4429
|
|
|
4435
4430
|
// src/commands/context.ts
|
|
4436
4431
|
import {
|
|
4437
|
-
existsSync as
|
|
4438
|
-
mkdirSync as
|
|
4439
|
-
readFileSync as
|
|
4432
|
+
existsSync as existsSync12,
|
|
4433
|
+
mkdirSync as mkdirSync8,
|
|
4434
|
+
readFileSync as readFileSync5,
|
|
4440
4435
|
readdirSync as readdirSync2,
|
|
4441
4436
|
statSync as statSync2,
|
|
4442
|
-
writeFileSync as
|
|
4437
|
+
writeFileSync as writeFileSync8
|
|
4443
4438
|
} from "fs";
|
|
4439
|
+
import { join as join8 } from "path";
|
|
4440
|
+
import chalk24 from "chalk";
|
|
4441
|
+
|
|
4442
|
+
// src/commands/memory.ts
|
|
4443
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync7, writeFileSync as writeFileSync7, copyFileSync, readFileSync as readFileSync4, renameSync, rmSync as rmSync3 } from "fs";
|
|
4444
4444
|
import { join as join7 } from "path";
|
|
4445
4445
|
import chalk23 from "chalk";
|
|
4446
|
+
var MEMORY_PATH_REL = join7(".vhk", "memory.json");
|
|
4447
|
+
var MEMORY_SCHEMA_VERSION = 2;
|
|
4448
|
+
function emptyV2() {
|
|
4449
|
+
return { schemaVersion: MEMORY_SCHEMA_VERSION, decisions: [], failures: [], successes: [], patterns: [] };
|
|
4450
|
+
}
|
|
4451
|
+
function isV2(raw) {
|
|
4452
|
+
return !!raw && typeof raw === "object" && raw.schemaVersion === 2;
|
|
4453
|
+
}
|
|
4454
|
+
var BUCKET_PREFIX = { decision: "d", failure: "f", success: "s" };
|
|
4455
|
+
function arr(v) {
|
|
4456
|
+
return Array.isArray(v) ? v : [];
|
|
4457
|
+
}
|
|
4458
|
+
function normalizeV2(raw) {
|
|
4459
|
+
return {
|
|
4460
|
+
schemaVersion: MEMORY_SCHEMA_VERSION,
|
|
4461
|
+
decisions: arr(raw.decisions),
|
|
4462
|
+
failures: arr(raw.failures),
|
|
4463
|
+
successes: arr(raw.successes),
|
|
4464
|
+
patterns: arr(raw.patterns)
|
|
4465
|
+
};
|
|
4466
|
+
}
|
|
4467
|
+
function parseLearnings(rawLearnings) {
|
|
4468
|
+
const out = [];
|
|
4469
|
+
for (const line of rawLearnings.split(/\r?\n/)) {
|
|
4470
|
+
const m = line.match(/^-\s*\[(\d{4}-\d{2}-\d{2})\s+([^\]]*)\]\s*(.+)$/);
|
|
4471
|
+
if (m) out.push({ date: m[1], tag: m[2].trim(), lesson: m[3].trim() });
|
|
4472
|
+
}
|
|
4473
|
+
return out;
|
|
4474
|
+
}
|
|
4475
|
+
function migrateMemory(rawMemory, rawLearnings) {
|
|
4476
|
+
if (isV2(rawMemory)) return normalizeV2(rawMemory);
|
|
4477
|
+
const v2 = emptyV2();
|
|
4478
|
+
if (Array.isArray(rawMemory)) {
|
|
4479
|
+
rawMemory.forEach((m, i) => {
|
|
4480
|
+
const item = m;
|
|
4481
|
+
if (item && typeof item.content === "string") {
|
|
4482
|
+
v2.decisions.push({
|
|
4483
|
+
id: `d${i + 1}`,
|
|
4484
|
+
content: item.content,
|
|
4485
|
+
tags: arr(item.tags),
|
|
4486
|
+
createdAt: typeof item.addedAt === "string" ? item.addedAt : "",
|
|
4487
|
+
status: "active"
|
|
4488
|
+
});
|
|
4489
|
+
}
|
|
4490
|
+
});
|
|
4491
|
+
}
|
|
4492
|
+
if (rawLearnings) {
|
|
4493
|
+
parseLearnings(rawLearnings).forEach((l, i) => {
|
|
4494
|
+
v2.failures.push({
|
|
4495
|
+
id: `f${i + 1}`,
|
|
4496
|
+
content: "",
|
|
4497
|
+
tags: l.tag ? [l.tag] : [],
|
|
4498
|
+
createdAt: l.date,
|
|
4499
|
+
status: "active",
|
|
4500
|
+
lesson: l.lesson
|
|
4501
|
+
});
|
|
4502
|
+
});
|
|
4503
|
+
}
|
|
4504
|
+
return v2;
|
|
4505
|
+
}
|
|
4506
|
+
function readRaw(cwd) {
|
|
4507
|
+
const p = join7(cwd, MEMORY_PATH_REL);
|
|
4508
|
+
if (!existsSync11(p)) return { kind: "missing" };
|
|
4509
|
+
try {
|
|
4510
|
+
return { kind: "parsed", value: readJsonFile(p) };
|
|
4511
|
+
} catch {
|
|
4512
|
+
return { kind: "error" };
|
|
4513
|
+
}
|
|
4514
|
+
}
|
|
4515
|
+
function warnUnreadable(cwd) {
|
|
4516
|
+
const p = join7(cwd, MEMORY_PATH_REL);
|
|
4517
|
+
console.error(chalk23.red(`
|
|
4518
|
+
\u26A0\uFE0F ${MEMORY_PATH_REL} \uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC190\uC0C1/\uBD80\uBD84 \uC4F0\uAE30 \uC758\uC2EC).`));
|
|
4519
|
+
console.error(chalk23.yellow(` \uB36E\uC5B4\uC4F0\uC9C0 \uC54A\uACE0 \uBE48 \uBA54\uBAA8\uB9AC\uB85C \uC9C4\uD589\uD569\uB2C8\uB2E4 \u2014 \uC6D0\uBCF8 \uBCF4\uC874\uB428.`));
|
|
4520
|
+
console.error(chalk23.dim(` \uD655\uC778/\uBCF5\uAD6C: ${p} (\uBC31\uC5C5: ${p}.bak / ${p}.v1.bak)`));
|
|
4521
|
+
}
|
|
4522
|
+
function warnUnrecognized(cwd) {
|
|
4523
|
+
const p = join7(cwd, MEMORY_PATH_REL);
|
|
4524
|
+
console.error(chalk23.red(`
|
|
4525
|
+
\u26A0\uFE0F ${MEMORY_PATH_REL} \uAC00 \uC778\uC2DD \uAC00\uB2A5\uD55C \uD615\uC2DD\uC774 \uC544\uB2D9\uB2C8\uB2E4 (v1 \uBC30\uC5F4/v2 \uAC1D\uCCB4 \uC544\uB2D8).`));
|
|
4526
|
+
console.error(chalk23.yellow(` \uBBF8\uB798 \uC2A4\uD0A4\uB9C8/\uC218\uB3D9 \uD3B8\uC9D1 \uC758\uC2EC \u2014 \uB36E\uC5B4\uC4F0\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(\uC6D0\uBCF8 \uBCF4\uC874). \uD655\uC778 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4.`));
|
|
4527
|
+
console.error(chalk23.dim(` \uD655\uC778: ${p}`));
|
|
4528
|
+
}
|
|
4529
|
+
function readLearningsRaw(cwd) {
|
|
4530
|
+
const p = join7(cwd, "docs", "state", "learnings.md");
|
|
4531
|
+
if (!existsSync11(p)) return void 0;
|
|
4532
|
+
try {
|
|
4533
|
+
return stripBom(readFileSync4(p, "utf-8"));
|
|
4534
|
+
} catch {
|
|
4535
|
+
return void 0;
|
|
4536
|
+
}
|
|
4537
|
+
}
|
|
4538
|
+
function readMemory(cwd = process.cwd()) {
|
|
4539
|
+
const raw = readRaw(cwd);
|
|
4540
|
+
if (raw.kind === "error") {
|
|
4541
|
+
warnUnreadable(cwd);
|
|
4542
|
+
return emptyV2();
|
|
4543
|
+
}
|
|
4544
|
+
if (raw.kind === "parsed") {
|
|
4545
|
+
if (isV2(raw.value)) return normalizeV2(raw.value);
|
|
4546
|
+
if (Array.isArray(raw.value)) {
|
|
4547
|
+
const v2 = migrateMemory(raw.value, readLearningsRaw(cwd));
|
|
4548
|
+
try {
|
|
4549
|
+
writeMemory(cwd, v2);
|
|
4550
|
+
} catch {
|
|
4551
|
+
console.error(chalk23.yellow(` (v2 \uC601\uAD6C\uD654 \uBCF4\uB958 \u2014 ${MEMORY_PATH_REL} \uC7A0\uAE08 \uC758\uC2EC. \uC774\uBC88\uC5D4 \uBA54\uBAA8\uB9AC\uC0C1\uC73C\uB85C\uB9CC \uC9C4\uD589)`));
|
|
4552
|
+
}
|
|
4553
|
+
return v2;
|
|
4554
|
+
}
|
|
4555
|
+
warnUnrecognized(cwd);
|
|
4556
|
+
return emptyV2();
|
|
4557
|
+
}
|
|
4558
|
+
return migrateMemory(null, readLearningsRaw(cwd));
|
|
4559
|
+
}
|
|
4560
|
+
function loadForMutation(cwd) {
|
|
4561
|
+
const raw = readRaw(cwd);
|
|
4562
|
+
if (raw.kind === "error") {
|
|
4563
|
+
warnUnreadable(cwd);
|
|
4564
|
+
return { ok: false };
|
|
4565
|
+
}
|
|
4566
|
+
if (raw.kind === "parsed") {
|
|
4567
|
+
if (isV2(raw.value)) return { ok: true, mem: normalizeV2(raw.value) };
|
|
4568
|
+
if (Array.isArray(raw.value)) return { ok: true, mem: migrateMemory(raw.value, readLearningsRaw(cwd)) };
|
|
4569
|
+
warnUnrecognized(cwd);
|
|
4570
|
+
return { ok: false };
|
|
4571
|
+
}
|
|
4572
|
+
return { ok: true, mem: migrateMemory(null, readLearningsRaw(cwd)) };
|
|
4573
|
+
}
|
|
4574
|
+
function isActive(e) {
|
|
4575
|
+
return e.status !== "archived" && e.status !== "resolved";
|
|
4576
|
+
}
|
|
4577
|
+
function writeMemory(cwd, mem) {
|
|
4578
|
+
const p = join7(cwd, MEMORY_PATH_REL);
|
|
4579
|
+
mkdirSync7(join7(cwd, ".vhk"), { recursive: true });
|
|
4580
|
+
if (existsSync11(p)) {
|
|
4581
|
+
const cur = readRaw(cwd);
|
|
4582
|
+
const curIsV2 = cur.kind === "parsed" && isV2(cur.value);
|
|
4583
|
+
if (cur.kind !== "error" && !curIsV2 && !existsSync11(p + ".v1.bak")) {
|
|
4584
|
+
try {
|
|
4585
|
+
copyFileSync(p, p + ".v1.bak");
|
|
4586
|
+
} catch {
|
|
4587
|
+
}
|
|
4588
|
+
}
|
|
4589
|
+
if (cur.kind !== "error") {
|
|
4590
|
+
try {
|
|
4591
|
+
copyFileSync(p, p + ".bak");
|
|
4592
|
+
} catch {
|
|
4593
|
+
}
|
|
4594
|
+
}
|
|
4595
|
+
}
|
|
4596
|
+
const tmpPath = p + ".tmp";
|
|
4597
|
+
writeFileSync7(tmpPath, JSON.stringify(mem, null, 2) + "\n", "utf-8");
|
|
4598
|
+
try {
|
|
4599
|
+
renameSync(tmpPath, p);
|
|
4600
|
+
} catch (err) {
|
|
4601
|
+
try {
|
|
4602
|
+
rmSync3(tmpPath, { force: true });
|
|
4603
|
+
} catch {
|
|
4604
|
+
}
|
|
4605
|
+
throw err;
|
|
4606
|
+
}
|
|
4607
|
+
}
|
|
4608
|
+
function nextId(bucket, mem) {
|
|
4609
|
+
const prefix = BUCKET_PREFIX[bucket];
|
|
4610
|
+
const list = bucket === "decision" ? mem.decisions : bucket === "failure" ? mem.failures : mem.successes;
|
|
4611
|
+
const idRe = new RegExp(`^${prefix}(\\d+)$`);
|
|
4612
|
+
let max = 0;
|
|
4613
|
+
for (const e of list) {
|
|
4614
|
+
const m = e.id.match(idRe);
|
|
4615
|
+
if (m) max = Math.max(max, Number(m[1]));
|
|
4616
|
+
}
|
|
4617
|
+
return `${prefix}${max + 1}`;
|
|
4618
|
+
}
|
|
4619
|
+
function orderedAll(mem) {
|
|
4620
|
+
return [
|
|
4621
|
+
...mem.decisions.map((entry) => ({ bucket: "decision", entry })),
|
|
4622
|
+
...mem.failures.map((entry) => ({ bucket: "failure", entry })),
|
|
4623
|
+
...mem.successes.map((entry) => ({ bucket: "success", entry }))
|
|
4624
|
+
];
|
|
4625
|
+
}
|
|
4626
|
+
var VALID_BUCKETS = ["decision", "failure", "success"];
|
|
4627
|
+
async function memoryAdd(content, opts = {}) {
|
|
4628
|
+
console.log(chalk23.bold("\n\u{1F9E0} " + t("memory.addTitle")));
|
|
4629
|
+
console.log(chalk23.gray("\u2500".repeat(40)));
|
|
4630
|
+
if (!content || !content.trim()) {
|
|
4631
|
+
console.log(chalk23.red("\u274C \uAE30\uC5B5\uD560 \uB0B4\uC6A9\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694."));
|
|
4632
|
+
console.log(chalk23.gray(' \uC608: vhk memory add "API\uB294 tRPC \uC0AC\uC6A9" --type decision'));
|
|
4633
|
+
process.exitCode = 1;
|
|
4634
|
+
return;
|
|
4635
|
+
}
|
|
4636
|
+
const typeRaw = opts.type ?? "decision";
|
|
4637
|
+
if (!VALID_BUCKETS.includes(typeRaw)) {
|
|
4638
|
+
console.log(chalk23.red(`\u274C --type \uC740 decision|failure|success \uC911 \uD558\uB098\uC5EC\uC57C \uD569\uB2C8\uB2E4 (\uBC1B\uC740 \uAC12: ${typeRaw}).`));
|
|
4639
|
+
process.exitCode = 1;
|
|
4640
|
+
return;
|
|
4641
|
+
}
|
|
4642
|
+
const type = typeRaw;
|
|
4643
|
+
if (type === "decision" && (opts.why || opts.lesson)) {
|
|
4644
|
+
console.log(chalk23.yellow("\u26A0\uFE0F --why/--lesson \uC740 --type failure|success \uC5D0\uC11C\uB9CC \uC800\uC7A5\uB429\uB2C8\uB2E4 \u2014 decision \uC5D0\uC11C\uB294 \uBB34\uC2DC\uB428."));
|
|
4645
|
+
}
|
|
4646
|
+
const cwd = process.cwd();
|
|
4647
|
+
const loaded = loadForMutation(cwd);
|
|
4648
|
+
if (!loaded.ok) {
|
|
4649
|
+
console.log(chalk23.red("\u274C memory.json \uC190\uC0C1 \uC758\uC2EC \u2014 \uC800\uC7A5 \uC911\uB2E8 (\uC6D0\uBCF8 \uBCF4\uC874). \uBC31\uC5C5 \uD655\uC778 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694."));
|
|
4650
|
+
process.exitCode = 1;
|
|
4651
|
+
return;
|
|
4652
|
+
}
|
|
4653
|
+
const mem = loaded.mem;
|
|
4654
|
+
const base = {
|
|
4655
|
+
id: nextId(type, mem),
|
|
4656
|
+
content: content.trim(),
|
|
4657
|
+
tags: opts.tags && opts.tags.length > 0 ? opts.tags : [],
|
|
4658
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4659
|
+
status: "active"
|
|
4660
|
+
};
|
|
4661
|
+
if (type === "failure") mem.failures.push({ ...base, why: opts.why, lesson: opts.lesson });
|
|
4662
|
+
else if (type === "success") mem.successes.push({ ...base, why: opts.why });
|
|
4663
|
+
else mem.decisions.push(base);
|
|
4664
|
+
writeMemory(cwd, mem);
|
|
4665
|
+
console.log(chalk23.green(`
|
|
4666
|
+
\u2705 \uAE30\uC5B5 \uC800\uC7A5\uB428 (${type} #${base.id})`));
|
|
4667
|
+
console.log(chalk23.cyan(` \u{1F4DD} ${base.content}`));
|
|
4668
|
+
printNextStep({ message: "\uAE30\uC5B5 \uC800\uC7A5 \uC644\uB8CC!", command: "vhk memory list", cursorHint: "\uAE30\uC5B5 \uBAA9\uB85D \uBCF4\uC5EC\uC918" });
|
|
4669
|
+
}
|
|
4670
|
+
var STATUS_ICON2 = { active: "\u{1F7E2}", resolved: "\u2705", archived: "\u{1F4E6}" };
|
|
4671
|
+
var BUCKET_LABEL = { decision: "\uACB0\uC815", failure: "\uC2E4\uD328", success: "\uC131\uACF5" };
|
|
4672
|
+
async function memoryList(opts = {}) {
|
|
4673
|
+
console.log(chalk23.bold("\n\u{1F9E0} " + t("memory.listTitle")));
|
|
4674
|
+
console.log(chalk23.gray("\u2500".repeat(40)));
|
|
4675
|
+
const mem = readMemory(process.cwd());
|
|
4676
|
+
const all = orderedAll(mem);
|
|
4677
|
+
const visible = all.map((x, i) => ({ ...x, n: i + 1 })).filter((x) => (opts.all || isActive(x.entry)) && (!opts.type || x.bucket === opts.type));
|
|
4678
|
+
if (visible.length === 0) {
|
|
4679
|
+
console.log(chalk23.yellow("\n\u{1F4ED} \uD45C\uC2DC\uD560 \uAE30\uC5B5\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
4680
|
+
console.log(chalk23.gray(' vhk memory add "\uB0B4\uC6A9" --type decision|failure|success'));
|
|
4681
|
+
return;
|
|
4682
|
+
}
|
|
4683
|
+
console.log(chalk23.cyan(`
|
|
4684
|
+
${visible.length}\uAC1C${opts.all ? " (\uBCF4\uAD00 \uD3EC\uD568)" : " (\uD65C\uC131)"}:
|
|
4685
|
+
`));
|
|
4686
|
+
for (const x of visible) {
|
|
4687
|
+
const e = x.entry;
|
|
4688
|
+
const fail = e;
|
|
4689
|
+
console.log(` [${x.n}] ${STATUS_ICON2[e.status] ?? "\u{1F7E2}"} (${BUCKET_LABEL[x.bucket]}) ${e.content || (fail.lesson ? "\u{1F4A1} " + fail.lesson : "(\uB0B4\uC6A9 \uC5C6\uC74C)")}`);
|
|
4690
|
+
if (fail.lesson && e.content) console.log(chalk23.dim(` \u{1F4A1} \uAD50\uD6C8: ${fail.lesson}`));
|
|
4691
|
+
if (fail.why) console.log(chalk23.dim(` \u21B3 ${fail.why}`));
|
|
4692
|
+
if (e.tags.length > 0) console.log(chalk23.blue(` \u{1F3F7}\uFE0F ${e.tags.join(", ")}`));
|
|
4693
|
+
}
|
|
4694
|
+
}
|
|
4695
|
+
function resolveIndex(indexStr, len) {
|
|
4696
|
+
const idx = parseInt(indexStr, 10) - 1;
|
|
4697
|
+
if (Number.isNaN(idx) || idx < 0 || idx >= len) return null;
|
|
4698
|
+
return idx;
|
|
4699
|
+
}
|
|
4700
|
+
async function memoryRemove(indexStr) {
|
|
4701
|
+
const cwd = process.cwd();
|
|
4702
|
+
const loaded = loadForMutation(cwd);
|
|
4703
|
+
if (!loaded.ok) {
|
|
4704
|
+
console.log(chalk23.red("\u274C memory.json \uC190\uC0C1 \uC758\uC2EC \u2014 \uC0AD\uC81C \uC911\uB2E8 (\uC6D0\uBCF8 \uBCF4\uC874)."));
|
|
4705
|
+
process.exitCode = 1;
|
|
4706
|
+
return;
|
|
4707
|
+
}
|
|
4708
|
+
const mem = loaded.mem;
|
|
4709
|
+
const all = orderedAll(mem);
|
|
4710
|
+
const idx = resolveIndex(indexStr, all.length);
|
|
4711
|
+
if (idx === null) {
|
|
4712
|
+
console.log(chalk23.red(`\u274C \uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uBC88\uD638\uC785\uB2C8\uB2E4. (1~${all.length || 0})`));
|
|
4713
|
+
process.exitCode = 1;
|
|
4714
|
+
return;
|
|
4715
|
+
}
|
|
4716
|
+
const { bucket, entry } = all[idx];
|
|
4717
|
+
const list = bucket === "decision" ? mem.decisions : bucket === "failure" ? mem.failures : mem.successes;
|
|
4718
|
+
const pos = list.findIndex((e) => e === entry);
|
|
4719
|
+
if (pos >= 0) list.splice(pos, 1);
|
|
4720
|
+
writeMemory(cwd, mem);
|
|
4721
|
+
console.log(chalk23.green("\n\u2705 \uAE30\uC5B5 \uC0AD\uC81C\uB428:"));
|
|
4722
|
+
console.log(chalk23.gray(` ${entry.content || entry.lesson || entry.id}`));
|
|
4723
|
+
}
|
|
4724
|
+
function resolveEntryForMutation(indexStr) {
|
|
4725
|
+
const cwd = process.cwd();
|
|
4726
|
+
const loaded = loadForMutation(cwd);
|
|
4727
|
+
if (!loaded.ok) {
|
|
4728
|
+
console.log(chalk23.red("\u274C memory.json \uC190\uC0C1 \uC758\uC2EC \u2014 \uC791\uC5C5 \uC911\uB2E8 (\uC6D0\uBCF8 \uBCF4\uC874)."));
|
|
4729
|
+
process.exitCode = 1;
|
|
4730
|
+
return null;
|
|
4731
|
+
}
|
|
4732
|
+
const mem = loaded.mem;
|
|
4733
|
+
const all = orderedAll(mem);
|
|
4734
|
+
const idx = resolveIndex(indexStr, all.length);
|
|
4735
|
+
if (idx === null) {
|
|
4736
|
+
console.log(chalk23.red(`\u274C \uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uBC88\uD638\uC785\uB2C8\uB2E4. (1~${all.length || 0})`));
|
|
4737
|
+
process.exitCode = 1;
|
|
4738
|
+
return null;
|
|
4739
|
+
}
|
|
4740
|
+
return { cwd, mem, entry: all[idx].entry };
|
|
4741
|
+
}
|
|
4742
|
+
function entryLabel(entry) {
|
|
4743
|
+
return entry.content || entry.lesson || entry.id;
|
|
4744
|
+
}
|
|
4745
|
+
async function memoryArchive(indexStr) {
|
|
4746
|
+
const r = resolveEntryForMutation(indexStr);
|
|
4747
|
+
if (!r) return;
|
|
4748
|
+
r.entry.status = "archived";
|
|
4749
|
+
r.entry.archivedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4750
|
+
delete r.entry.resolvedAt;
|
|
4751
|
+
writeMemory(r.cwd, r.mem);
|
|
4752
|
+
console.log(chalk23.green(`
|
|
4753
|
+
\u{1F4E6} \uBCF4\uAD00\uB428: ${entryLabel(r.entry)}`));
|
|
4754
|
+
console.log(chalk23.dim(" (\uD328\uD134 \uAC10\uC9C0\xB7\uC9C4\uD654\uC5D0\uC11C \uC81C\uC678\uB429\uB2C8\uB2E4 \u2014 \uC120\uC21C\uD658). \uB418\uB3CC\uB9AC\uAE30: vhk memory unarchive <\uBC88\uD638>"));
|
|
4755
|
+
}
|
|
4756
|
+
async function memoryResolve(indexStr) {
|
|
4757
|
+
const r = resolveEntryForMutation(indexStr);
|
|
4758
|
+
if (!r) return;
|
|
4759
|
+
r.entry.status = "resolved";
|
|
4760
|
+
r.entry.resolvedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4761
|
+
delete r.entry.archivedAt;
|
|
4762
|
+
writeMemory(r.cwd, r.mem);
|
|
4763
|
+
console.log(chalk23.green(`
|
|
4764
|
+
\u2705 \uD574\uACB0\uB428: ${entryLabel(r.entry)}`));
|
|
4765
|
+
console.log(chalk23.dim(" (vhk memory list --all \uB85C \uD655\uC778. \uB418\uB3CC\uB9AC\uAE30: vhk memory unarchive <\uBC88\uD638>)"));
|
|
4766
|
+
}
|
|
4767
|
+
async function memoryUnarchive(indexStr) {
|
|
4768
|
+
const r = resolveEntryForMutation(indexStr);
|
|
4769
|
+
if (!r) return;
|
|
4770
|
+
if (isActive(r.entry)) {
|
|
4771
|
+
console.log(chalk23.dim(` \uC774\uBBF8 \uD65C\uC131 \uD56D\uBAA9\uC785\uB2C8\uB2E4 \u2014 \uBCC0\uACBD \uC5C6\uC74C: ${entryLabel(r.entry)}`));
|
|
4772
|
+
return;
|
|
4773
|
+
}
|
|
4774
|
+
r.entry.status = "active";
|
|
4775
|
+
delete r.entry.archivedAt;
|
|
4776
|
+
delete r.entry.resolvedAt;
|
|
4777
|
+
writeMemory(r.cwd, r.mem);
|
|
4778
|
+
console.log(chalk23.green(`
|
|
4779
|
+
\u{1F7E2} \uD65C\uC131\uC73C\uB85C \uBCF5\uAD6C\uB428: ${entryLabel(r.entry)}`));
|
|
4780
|
+
}
|
|
4781
|
+
async function memoryMigrate() {
|
|
4782
|
+
const cwd = process.cwd();
|
|
4783
|
+
const raw = readRaw(cwd);
|
|
4784
|
+
if (raw.kind === "error") {
|
|
4785
|
+
warnUnreadable(cwd);
|
|
4786
|
+
console.log(chalk23.red(" \u274C \uB9C8\uC774\uADF8\uB808\uC774\uC158 \uC911\uB2E8 (\uC190\uC0C1 \uC758\uC2EC). \uC6D0\uBCF8 \uD655\uC778 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694."));
|
|
4787
|
+
process.exitCode = 1;
|
|
4788
|
+
return;
|
|
4789
|
+
}
|
|
4790
|
+
if (raw.kind === "parsed" && isV2(raw.value)) {
|
|
4791
|
+
console.log(chalk23.dim(" \uC774\uBBF8 memory schema v2 \uC785\uB2C8\uB2E4 \u2014 \uBCC0\uACBD \uC5C6\uC74C(\uBA71\uB4F1)."));
|
|
4792
|
+
return;
|
|
4793
|
+
}
|
|
4794
|
+
if (raw.kind === "parsed" && !Array.isArray(raw.value)) {
|
|
4795
|
+
warnUnrecognized(cwd);
|
|
4796
|
+
console.log(chalk23.red(" \u274C v1(\uD3C9\uBA74 \uBC30\uC5F4) \uD615\uC2DD\uC774 \uC544\uB2C8\uB77C \uB9C8\uC774\uADF8\uB808\uC774\uC158 \uB300\uC0C1\uC774 \uC544\uB2D9\uB2C8\uB2E4 \u2014 \uC911\uB2E8(\uC6D0\uBCF8 \uBCF4\uC874)."));
|
|
4797
|
+
process.exitCode = 1;
|
|
4798
|
+
return;
|
|
4799
|
+
}
|
|
4800
|
+
const learnings = readLearningsRaw(cwd);
|
|
4801
|
+
const hadFile = raw.kind === "parsed";
|
|
4802
|
+
if (raw.kind === "missing" && !learnings) {
|
|
4803
|
+
console.log(chalk23.yellow(" \u2139\uFE0F \uB9C8\uC774\uADF8\uB808\uC774\uC158\uD560 v1 memory.json / learnings.md \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uBCC0\uACBD \uC5C6\uC74C."));
|
|
4804
|
+
return;
|
|
4805
|
+
}
|
|
4806
|
+
const v2 = migrateMemory(raw.kind === "parsed" ? raw.value : null, learnings);
|
|
4807
|
+
writeMemory(cwd, v2);
|
|
4808
|
+
const backupNote = hadFile ? " (.v1.bak \uC6D0\uBCF8 \uC601\uAD6C \uBC31\uC5C5)" : " (\uC2E0\uADDC \uC0DD\uC131 \u2014 \uC6D0\uBCF8 \uC5C6\uC74C, \uBC31\uC5C5 \uC5C6\uC74C)";
|
|
4809
|
+
console.log(chalk23.green(`
|
|
4810
|
+
\u2705 memory.json \u2192 v2 \uB9C8\uC774\uADF8\uB808\uC774\uC158 \uC644\uB8CC${backupNote}`));
|
|
4811
|
+
console.log(
|
|
4812
|
+
chalk23.dim(
|
|
4813
|
+
` decisions ${v2.decisions.length} \xB7 failures ${v2.failures.length} \xB7 successes ${v2.successes.length}` + (learnings ? " (learnings.md \uAD50\uD6C8 \uD761\uC218 \u2014 \uC774\uD6C4 vhk learn \uC740 memory \uC5D0 \uAE30\uB85D)" : "")
|
|
4814
|
+
)
|
|
4815
|
+
);
|
|
4816
|
+
}
|
|
4817
|
+
function activeMemoryLines(mem, limit = 5) {
|
|
4818
|
+
const lines = [];
|
|
4819
|
+
const fmt = (e) => {
|
|
4820
|
+
const f = e;
|
|
4821
|
+
const base = e.content || (f.lesson ? `\u{1F4A1} ${f.lesson}` : e.id);
|
|
4822
|
+
return e.content && f.lesson ? `${base} \u2014 \u{1F4A1} ${f.lesson}` : base;
|
|
4823
|
+
};
|
|
4824
|
+
const section = (label, list) => {
|
|
4825
|
+
const act = list.filter(isActive);
|
|
4826
|
+
if (act.length === 0) return;
|
|
4827
|
+
lines.push(`**${label}** (${act.length})`);
|
|
4828
|
+
for (const e of act.slice(-limit)) lines.push(`- ${fmt(e)}`);
|
|
4829
|
+
if (act.length > limit) lines.push(`- \u2026 \uC678 ${act.length - limit}\uAC1C`);
|
|
4830
|
+
lines.push("");
|
|
4831
|
+
};
|
|
4832
|
+
section("\uACB0\uC815 (decisions)", mem.decisions);
|
|
4833
|
+
section("\uC2E4\uD328\xB7\uAD50\uD6C8 (failures)", mem.failures);
|
|
4834
|
+
section("\uC131\uACF5 (successes)", mem.successes);
|
|
4835
|
+
const pats = mem.patterns.length;
|
|
4836
|
+
if (pats > 0) lines.push(`**\uD328\uD134 \uD6C4\uBCF4 (patterns)**: ${pats}\uAC1C \u2014 \`vhk pattern\``, "");
|
|
4837
|
+
return lines;
|
|
4838
|
+
}
|
|
4839
|
+
function recordLesson(cwd, lesson, goalId) {
|
|
4840
|
+
const loaded = loadForMutation(cwd);
|
|
4841
|
+
if (!loaded.ok) return null;
|
|
4842
|
+
const mem = loaded.mem;
|
|
4843
|
+
const tag = goalId !== void 0 ? `goal-${goalId}` : "no-goal";
|
|
4844
|
+
const entry = {
|
|
4845
|
+
id: nextId("failure", mem),
|
|
4846
|
+
content: "",
|
|
4847
|
+
tags: [tag],
|
|
4848
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4849
|
+
status: "active",
|
|
4850
|
+
lesson: lesson.trim()
|
|
4851
|
+
};
|
|
4852
|
+
mem.failures.push(entry);
|
|
4853
|
+
writeMemory(cwd, mem);
|
|
4854
|
+
return entry;
|
|
4855
|
+
}
|
|
4856
|
+
|
|
4857
|
+
// src/commands/context.ts
|
|
4446
4858
|
var CONTEXT_PATH = ".vhk/context.md";
|
|
4447
4859
|
var IGNORE_DIRS = /* @__PURE__ */ new Set([
|
|
4448
4860
|
"node_modules",
|
|
@@ -4467,7 +4879,7 @@ function buildTree(dir, prefix = "", maxDepth = 3, depth = 0) {
|
|
|
4467
4879
|
filtered.forEach((entry, index) => {
|
|
4468
4880
|
const isLast = index === filtered.length - 1;
|
|
4469
4881
|
const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
4470
|
-
const fullPath =
|
|
4882
|
+
const fullPath = join8(dir, entry);
|
|
4471
4883
|
const stat = statSync2(fullPath);
|
|
4472
4884
|
const isDir = stat.isDirectory();
|
|
4473
4885
|
lines.push(`${prefix}${connector}${entry}${isDir ? "/" : ""}`);
|
|
@@ -4499,8 +4911,8 @@ function extractTechStack() {
|
|
|
4499
4911
|
else if (all.jest) stack["\uD14C\uC2A4\uD2B8"] = "jest";
|
|
4500
4912
|
if (all.commander) stack["CLI"] = "commander";
|
|
4501
4913
|
if (all.inquirer) stack["\uC778\uD130\uB799\uD2F0\uBE0C"] = "inquirer";
|
|
4502
|
-
if (
|
|
4503
|
-
else if (
|
|
4914
|
+
if (existsSync12("pnpm-lock.yaml")) stack["\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800"] = "pnpm";
|
|
4915
|
+
else if (existsSync12("yarn.lock")) stack["\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800"] = "yarn";
|
|
4504
4916
|
else stack["\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800"] = "npm";
|
|
4505
4917
|
if (pkg.name) stack["\uD328\uD0A4\uC9C0 \uC774\uB984"] = pkg.name;
|
|
4506
4918
|
if (pkg.version) stack["\uBC84\uC804"] = pkg.version;
|
|
@@ -4543,8 +4955,8 @@ function getVhkCommands() {
|
|
|
4543
4955
|
}
|
|
4544
4956
|
async function context(opts = {}) {
|
|
4545
4957
|
const compact = opts.compact === true;
|
|
4546
|
-
console.log(
|
|
4547
|
-
console.log(
|
|
4958
|
+
console.log(chalk24.bold("\n\u{1F9E0} " + t("context.title")));
|
|
4959
|
+
console.log(chalk24.gray("\u2500".repeat(40)));
|
|
4548
4960
|
const stack = extractTechStack();
|
|
4549
4961
|
const tree = buildTree(".", "", compact ? 2 : 3).join("\n");
|
|
4550
4962
|
const commands = getVhkCommands();
|
|
@@ -4579,27 +4991,14 @@ async function context(opts = {}) {
|
|
|
4579
4991
|
}
|
|
4580
4992
|
lines.push("");
|
|
4581
4993
|
}
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
);
|
|
4587
|
-
|
|
4588
|
-
const recentMemories = memories.slice(-5);
|
|
4589
|
-
lines.push("## \uC800\uC7A5\uB41C \uACB0\uC815\uC0AC\uD56D");
|
|
4590
|
-
lines.push("");
|
|
4591
|
-
if (memories.length > recentMemories.length) {
|
|
4592
|
-
lines.push(`_\uCD5C\uADFC ${recentMemories.length}\uAC1C\uB9CC \uD45C\uC2DC (\uC804\uCCB4 ${memories.length}\uAC1C)_`);
|
|
4593
|
-
lines.push("");
|
|
4594
|
-
}
|
|
4595
|
-
for (const m of recentMemories) {
|
|
4596
|
-
const date = new Date(m.addedAt).toLocaleDateString("ko-KR");
|
|
4597
|
-
lines.push(`- ${m.content} _(${date})_`);
|
|
4598
|
-
}
|
|
4599
|
-
lines.push("");
|
|
4600
|
-
}
|
|
4601
|
-
} catch {
|
|
4994
|
+
try {
|
|
4995
|
+
const memLines = activeMemoryLines(readMemory(process.cwd()));
|
|
4996
|
+
if (memLines.length > 0) {
|
|
4997
|
+
lines.push("## \uC800\uC7A5\uB41C \uAE30\uC5B5 (memory v2)");
|
|
4998
|
+
lines.push("");
|
|
4999
|
+
lines.push(...memLines);
|
|
4602
5000
|
}
|
|
5001
|
+
} catch {
|
|
4603
5002
|
}
|
|
4604
5003
|
const goals = listGoals("goals");
|
|
4605
5004
|
const activeId = selectActiveId(goals);
|
|
@@ -4616,13 +5015,6 @@ async function context(opts = {}) {
|
|
|
4616
5015
|
lines.push("");
|
|
4617
5016
|
}
|
|
4618
5017
|
}
|
|
4619
|
-
const recent = getRecentLearnings(3);
|
|
4620
|
-
if (recent.length > 0) {
|
|
4621
|
-
lines.push("## Recent Learnings");
|
|
4622
|
-
lines.push("");
|
|
4623
|
-
for (const r of recent) lines.push(r);
|
|
4624
|
-
lines.push("");
|
|
4625
|
-
}
|
|
4626
5018
|
const activeBlockers = getActiveBlockers(3);
|
|
4627
5019
|
if (activeBlockers.length > 0) {
|
|
4628
5020
|
lines.push("## Active Blockers");
|
|
@@ -4657,12 +5049,12 @@ async function context(opts = {}) {
|
|
|
4657
5049
|
} catch {
|
|
4658
5050
|
}
|
|
4659
5051
|
lines.push("");
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
console.log(
|
|
5052
|
+
mkdirSync8(".vhk", { recursive: true });
|
|
5053
|
+
writeFileSync8(CONTEXT_PATH, lines.join("\n"), "utf-8");
|
|
5054
|
+
console.log(chalk24.green(`
|
|
4663
5055
|
\u2705 ${CONTEXT_PATH} \uC0DD\uC131 \uC644\uB8CC!`));
|
|
4664
|
-
console.log(
|
|
4665
|
-
console.log(
|
|
5056
|
+
console.log(chalk24.gray(` \uAE30\uC220 \uC2A4\uD0DD ${Object.keys(stack).length}\uAC1C \uAC10\uC9C0`));
|
|
5057
|
+
console.log(chalk24.gray(" AI \uC5B4\uC2DC\uC2A4\uD134\uD2B8\uC5D0\uAC8C \uC774 \uD30C\uC77C\uC744 \uCC38\uC870\uD558\uAC8C \uD558\uC138\uC694."));
|
|
4666
5058
|
printNextStep({
|
|
4667
5059
|
message: "\uCEE8\uD14D\uC2A4\uD2B8 \uD30C\uC77C \uC0DD\uC131 \uC644\uB8CC!",
|
|
4668
5060
|
command: "vhk context-show",
|
|
@@ -4670,110 +5062,33 @@ async function context(opts = {}) {
|
|
|
4670
5062
|
});
|
|
4671
5063
|
}
|
|
4672
5064
|
async function contextShow() {
|
|
4673
|
-
console.log(
|
|
4674
|
-
console.log(chalk23.gray("\u2500".repeat(40)));
|
|
4675
|
-
if (!existsSync11(CONTEXT_PATH)) {
|
|
4676
|
-
console.log(chalk23.yellow("\n\u26A0\uFE0F \uCEE8\uD14D\uC2A4\uD2B8 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
4677
|
-
console.log(chalk23.gray(" vhk context\uB97C \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694."));
|
|
4678
|
-
return;
|
|
4679
|
-
}
|
|
4680
|
-
const content = readFileSync4(CONTEXT_PATH, "utf-8");
|
|
4681
|
-
console.log("\n" + content);
|
|
4682
|
-
}
|
|
4683
|
-
|
|
4684
|
-
// src/commands/memory.ts
|
|
4685
|
-
import { existsSync as existsSync12, mkdirSync as mkdirSync8, writeFileSync as writeFileSync8 } from "fs";
|
|
4686
|
-
import chalk24 from "chalk";
|
|
4687
|
-
var MEMORY_PATH = ".vhk/memory.json";
|
|
4688
|
-
function loadMemories() {
|
|
4689
|
-
if (!existsSync12(MEMORY_PATH)) return [];
|
|
4690
|
-
try {
|
|
4691
|
-
const parsed = readJsonFile(MEMORY_PATH);
|
|
4692
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
4693
|
-
} catch {
|
|
4694
|
-
return [];
|
|
4695
|
-
}
|
|
4696
|
-
}
|
|
4697
|
-
function saveMemories(memories) {
|
|
4698
|
-
mkdirSync8(".vhk", { recursive: true });
|
|
4699
|
-
writeFileSync8(MEMORY_PATH, JSON.stringify(memories, null, 2) + "\n", "utf-8");
|
|
4700
|
-
}
|
|
4701
|
-
async function memoryAdd(content, tags) {
|
|
4702
|
-
console.log(chalk24.bold("\n\u{1F9E0} " + t("memory.addTitle")));
|
|
4703
|
-
console.log(chalk24.gray("\u2500".repeat(40)));
|
|
4704
|
-
if (!content) {
|
|
4705
|
-
console.log(chalk24.red("\u274C \uAE30\uC5B5\uD560 \uB0B4\uC6A9\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694."));
|
|
4706
|
-
console.log(chalk24.gray(' \uC608: vhk memory add "API\uB294 tRPC \uC0AC\uC6A9\uD558\uAE30\uB85C \uACB0\uC815"'));
|
|
4707
|
-
process.exitCode = 1;
|
|
4708
|
-
return;
|
|
4709
|
-
}
|
|
4710
|
-
const memories = loadMemories();
|
|
4711
|
-
memories.push({
|
|
4712
|
-
content,
|
|
4713
|
-
addedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4714
|
-
tags: tags && tags.length > 0 ? tags : []
|
|
4715
|
-
});
|
|
4716
|
-
saveMemories(memories);
|
|
4717
|
-
console.log(chalk24.green(`
|
|
4718
|
-
\u2705 \uAE30\uC5B5 \uC800\uC7A5\uB428 (#${memories.length})`));
|
|
4719
|
-
console.log(chalk24.cyan(` \u{1F4DD} ${content}`));
|
|
4720
|
-
printNextStep({
|
|
4721
|
-
message: "\uAE30\uC5B5 \uC800\uC7A5 \uC644\uB8CC!",
|
|
4722
|
-
command: "vhk memory list",
|
|
4723
|
-
cursorHint: "\uAE30\uC5B5 \uBAA9\uB85D \uBCF4\uC5EC\uC918"
|
|
4724
|
-
});
|
|
4725
|
-
}
|
|
4726
|
-
async function memoryList() {
|
|
4727
|
-
console.log(chalk24.bold("\n\u{1F9E0} " + t("memory.listTitle")));
|
|
5065
|
+
console.log(chalk24.bold("\n\u{1F4C4} " + t("context.showTitle")));
|
|
4728
5066
|
console.log(chalk24.gray("\u2500".repeat(40)));
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
console.log(chalk24.
|
|
4732
|
-
console.log(chalk24.gray(' vhk memory add "\uB0B4\uC6A9"\uC73C\uB85C \uCD94\uAC00\uD558\uC138\uC694.'));
|
|
4733
|
-
return;
|
|
4734
|
-
}
|
|
4735
|
-
console.log(chalk24.cyan(`
|
|
4736
|
-
\uCD1D ${memories.length}\uAC1C\uC758 \uAE30\uC5B5:
|
|
4737
|
-
`));
|
|
4738
|
-
memories.forEach((m, index) => {
|
|
4739
|
-
const date = new Date(m.addedAt).toLocaleDateString("ko-KR");
|
|
4740
|
-
console.log(chalk24.white(` [${index + 1}] ${m.content}`));
|
|
4741
|
-
if (m.tags && m.tags.length > 0) {
|
|
4742
|
-
console.log(chalk24.blue(` \u{1F3F7}\uFE0F ${m.tags.join(", ")}`));
|
|
4743
|
-
}
|
|
4744
|
-
console.log(chalk24.gray(` \u{1F4C5} ${date}`));
|
|
4745
|
-
console.log("");
|
|
4746
|
-
});
|
|
4747
|
-
}
|
|
4748
|
-
async function memoryRemove(indexStr) {
|
|
4749
|
-
const memories = loadMemories();
|
|
4750
|
-
const idx = parseInt(indexStr, 10) - 1;
|
|
4751
|
-
if (Number.isNaN(idx) || idx < 0 || idx >= memories.length) {
|
|
4752
|
-
console.log(chalk24.red(`\u274C \uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uBC88\uD638\uC785\uB2C8\uB2E4. (1~${memories.length || 0})`));
|
|
5067
|
+
if (!existsSync12(CONTEXT_PATH)) {
|
|
5068
|
+
console.log(chalk24.yellow("\n\u26A0\uFE0F \uCEE8\uD14D\uC2A4\uD2B8 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
5069
|
+
console.log(chalk24.gray(" vhk context\uB97C \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694."));
|
|
4753
5070
|
return;
|
|
4754
5071
|
}
|
|
4755
|
-
const
|
|
4756
|
-
|
|
4757
|
-
console.log(chalk24.green("\n\u2705 \uAE30\uC5B5 \uC0AD\uC81C\uB428:"));
|
|
4758
|
-
console.log(chalk24.gray(` ${removed.content}`));
|
|
5072
|
+
const content = readFileSync5(CONTEXT_PATH, "utf-8");
|
|
5073
|
+
console.log("\n" + content);
|
|
4759
5074
|
}
|
|
4760
5075
|
|
|
4761
5076
|
// src/commands/brief.ts
|
|
4762
|
-
import { existsSync as existsSync13, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9, readFileSync as
|
|
5077
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9, readFileSync as readFileSync6 } from "fs";
|
|
4763
5078
|
import chalk25 from "chalk";
|
|
4764
5079
|
var BRIEF_PATH = ".vhk/brief.md";
|
|
4765
5080
|
function readProjectIdentity() {
|
|
4766
5081
|
const out = {};
|
|
4767
5082
|
try {
|
|
4768
5083
|
if (existsSync13("RULES.md")) {
|
|
4769
|
-
const r =
|
|
5084
|
+
const r = readFileSync6("RULES.md", "utf-8");
|
|
4770
5085
|
const m = r.split("\n")[0].match(/^#\s*(.+?)(?:\s*—.*)?$/);
|
|
4771
5086
|
if (m) out.name = m[1].trim();
|
|
4772
5087
|
const d = r.match(/한 줄 설명:\s*(.+)/);
|
|
4773
5088
|
if (d) out.description = d[1].trim();
|
|
4774
5089
|
}
|
|
4775
5090
|
if (!out.name && existsSync13("CLAUDE.md")) {
|
|
4776
|
-
const m =
|
|
5091
|
+
const m = readFileSync6("CLAUDE.md", "utf-8").match(/#\s*기록 규칙\s*\((.+?)\)/);
|
|
4777
5092
|
if (m) out.name = m[1].trim();
|
|
4778
5093
|
}
|
|
4779
5094
|
} catch {
|
|
@@ -4823,22 +5138,14 @@ async function brief() {
|
|
|
4823
5138
|
`- **\uBBF8\uCEE4\uBC0B \uBCC0\uACBD**: ${uncommitted ? `${uncommitted.split("\n").length}\uAC1C \uD30C\uC77C` : "\uC5C6\uC74C \u2705"}`
|
|
4824
5139
|
);
|
|
4825
5140
|
lines.push("");
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
for (const m of memories.slice(-5)) {
|
|
4833
|
-
lines.push(`- ${m.content}`);
|
|
4834
|
-
}
|
|
4835
|
-
if (memories.length > 5) {
|
|
4836
|
-
lines.push(`- ... \uC678 ${memories.length - 5}\uAC1C`);
|
|
4837
|
-
}
|
|
4838
|
-
lines.push("");
|
|
4839
|
-
}
|
|
4840
|
-
} catch {
|
|
5141
|
+
try {
|
|
5142
|
+
const memLines = activeMemoryLines(readMemory(process.cwd()));
|
|
5143
|
+
if (memLines.length > 0) {
|
|
5144
|
+
lines.push("## \uC800\uC7A5\uB41C \uAE30\uC5B5 (memory v2)");
|
|
5145
|
+
lines.push("");
|
|
5146
|
+
lines.push(...memLines);
|
|
4841
5147
|
}
|
|
5148
|
+
} catch {
|
|
4842
5149
|
}
|
|
4843
5150
|
if (existsSync13(".vhk/refs.json")) {
|
|
4844
5151
|
try {
|
|
@@ -4885,7 +5192,7 @@ import chalk26 from "chalk";
|
|
|
4885
5192
|
import inquirer11 from "inquirer";
|
|
4886
5193
|
import { simpleGit as simpleGit2 } from "simple-git";
|
|
4887
5194
|
import { existsSync as existsSync14 } from "fs";
|
|
4888
|
-
import { join as
|
|
5195
|
+
import { join as join9 } from "path";
|
|
4889
5196
|
var VHK_FOOTPRINT_FILES = [
|
|
4890
5197
|
"CLAUDE.md",
|
|
4891
5198
|
".cursorrules",
|
|
@@ -4894,7 +5201,7 @@ var VHK_FOOTPRINT_FILES = [
|
|
|
4894
5201
|
"docs/PRD.md"
|
|
4895
5202
|
];
|
|
4896
5203
|
function detectExistingFootprint(cwd) {
|
|
4897
|
-
return VHK_FOOTPRINT_FILES.filter((rel) => existsSync14(
|
|
5204
|
+
return VHK_FOOTPRINT_FILES.filter((rel) => existsSync14(join9(cwd, rel)));
|
|
4898
5205
|
}
|
|
4899
5206
|
async function runGitInit(cwd) {
|
|
4900
5207
|
try {
|
|
@@ -5300,7 +5607,7 @@ import chalk29 from "chalk";
|
|
|
5300
5607
|
|
|
5301
5608
|
// src/lib/config.ts
|
|
5302
5609
|
import { existsSync as existsSync15, mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "fs";
|
|
5303
|
-
import { join as
|
|
5610
|
+
import { join as join10 } from "path";
|
|
5304
5611
|
|
|
5305
5612
|
// src/lib/safety-mode.ts
|
|
5306
5613
|
var SAFETY_MODES = ["lite", "standard", "strict"];
|
|
@@ -5316,10 +5623,10 @@ function isSafetyMode(value) {
|
|
|
5316
5623
|
|
|
5317
5624
|
// src/lib/config.ts
|
|
5318
5625
|
var CONFIG_DIR = ".vhk";
|
|
5319
|
-
var CONFIG_PATH =
|
|
5626
|
+
var CONFIG_PATH = join10(CONFIG_DIR, "config.json");
|
|
5320
5627
|
var DEFAULT_CONFIG = { safetyMode: DEFAULT_SAFETY_MODE };
|
|
5321
5628
|
function readConfig(rootDir = process.cwd()) {
|
|
5322
|
-
const full =
|
|
5629
|
+
const full = join10(rootDir, CONFIG_PATH);
|
|
5323
5630
|
if (!existsSync15(full)) return { ...DEFAULT_CONFIG };
|
|
5324
5631
|
try {
|
|
5325
5632
|
const raw = readJsonFile(full);
|
|
@@ -5331,8 +5638,8 @@ function readConfig(rootDir = process.cwd()) {
|
|
|
5331
5638
|
}
|
|
5332
5639
|
}
|
|
5333
5640
|
function writeConfig(config, rootDir = process.cwd()) {
|
|
5334
|
-
mkdirSync10(
|
|
5335
|
-
writeFileSync10(
|
|
5641
|
+
mkdirSync10(join10(rootDir, CONFIG_DIR), { recursive: true });
|
|
5642
|
+
writeFileSync10(join10(rootDir, CONFIG_PATH), JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
5336
5643
|
}
|
|
5337
5644
|
|
|
5338
5645
|
// src/commands/mode.ts
|
|
@@ -5372,7 +5679,7 @@ async function mode(target) {
|
|
|
5372
5679
|
// src/commands/verify.ts
|
|
5373
5680
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
5374
5681
|
import { existsSync as existsSync16, mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "fs";
|
|
5375
|
-
import { join as
|
|
5682
|
+
import { join as join11 } from "path";
|
|
5376
5683
|
import chalk30 from "chalk";
|
|
5377
5684
|
|
|
5378
5685
|
// src/commands/verify-report.ts
|
|
@@ -5481,13 +5788,13 @@ ${actions}
|
|
|
5481
5788
|
|
|
5482
5789
|
// src/commands/verify.ts
|
|
5483
5790
|
var REPORT_SCHEMA_VERSION = 1;
|
|
5484
|
-
var REPORT_DIR_REL =
|
|
5485
|
-
var REPORT_PATH_REL =
|
|
5486
|
-
var REPORT_HTML_PATH_REL =
|
|
5791
|
+
var REPORT_DIR_REL = join11(".vhk", "reports");
|
|
5792
|
+
var REPORT_PATH_REL = join11(REPORT_DIR_REL, "latest.json");
|
|
5793
|
+
var REPORT_HTML_PATH_REL = join11(REPORT_DIR_REL, "latest.html");
|
|
5487
5794
|
var SHIM = /* @__PURE__ */ new Set(["pnpm", "npm", "npx", "yarn"]);
|
|
5488
5795
|
function detectPm(cwd) {
|
|
5489
|
-
if (existsSync16(
|
|
5490
|
-
if (existsSync16(
|
|
5796
|
+
if (existsSync16(join11(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
5797
|
+
if (existsSync16(join11(cwd, "yarn.lock"))) return "yarn";
|
|
5491
5798
|
return "npm";
|
|
5492
5799
|
}
|
|
5493
5800
|
function execGate(cmd, args, cwd) {
|
|
@@ -5530,7 +5837,7 @@ function runScriptGate(id, label, cwd, pm, argvFor) {
|
|
|
5530
5837
|
};
|
|
5531
5838
|
}
|
|
5532
5839
|
function readPackageScripts(cwd) {
|
|
5533
|
-
const pkgPath =
|
|
5840
|
+
const pkgPath = join11(cwd, "package.json");
|
|
5534
5841
|
if (!existsSync16(pkgPath)) return {};
|
|
5535
5842
|
try {
|
|
5536
5843
|
const pkg = readJsonFile(pkgPath);
|
|
@@ -5546,7 +5853,7 @@ function runGates(cwd) {
|
|
|
5546
5853
|
gates.push(
|
|
5547
5854
|
runScriptGate("typecheck", "tsc --noEmit", cwd, pm, () => {
|
|
5548
5855
|
if (scripts.typecheck) return ["run", "typecheck"];
|
|
5549
|
-
if (existsSync16(
|
|
5856
|
+
if (existsSync16(join11(cwd, "tsconfig.json"))) return pm === "npm" ? ["exec", "--", "tsc", "--noEmit"] : ["exec", "tsc", "--noEmit"];
|
|
5550
5857
|
return null;
|
|
5551
5858
|
})
|
|
5552
5859
|
);
|
|
@@ -5625,9 +5932,9 @@ function buildReport(gates, generatedAt, date) {
|
|
|
5625
5932
|
function verifyEvidence(cwd = process.cwd()) {
|
|
5626
5933
|
const gates = runGates(cwd);
|
|
5627
5934
|
const report = buildReport(gates, (/* @__PURE__ */ new Date()).toISOString(), localDate());
|
|
5628
|
-
const dir =
|
|
5935
|
+
const dir = join11(cwd, REPORT_DIR_REL);
|
|
5629
5936
|
mkdirSync11(dir, { recursive: true });
|
|
5630
|
-
const path14 =
|
|
5937
|
+
const path14 = join11(cwd, REPORT_PATH_REL);
|
|
5631
5938
|
writeFileSync11(path14, JSON.stringify(report, null, 2) + "\n", "utf-8");
|
|
5632
5939
|
try {
|
|
5633
5940
|
ensureVhkIgnored(cwd, "reports/");
|
|
@@ -5641,7 +5948,7 @@ var STATUS_BADGE = {
|
|
|
5641
5948
|
FAIL: chalk30.red.bold("FAIL")
|
|
5642
5949
|
};
|
|
5643
5950
|
async function renderVerifyReport(cwd, opts) {
|
|
5644
|
-
const jsonPath =
|
|
5951
|
+
const jsonPath = join11(cwd, REPORT_PATH_REL);
|
|
5645
5952
|
let report;
|
|
5646
5953
|
if (existsSync16(jsonPath)) {
|
|
5647
5954
|
try {
|
|
@@ -5655,9 +5962,9 @@ async function renderVerifyReport(cwd, opts) {
|
|
|
5655
5962
|
report = verifyEvidence(cwd).report;
|
|
5656
5963
|
}
|
|
5657
5964
|
const html = renderReportHtml(report);
|
|
5658
|
-
const htmlPath =
|
|
5965
|
+
const htmlPath = join11(cwd, REPORT_HTML_PATH_REL);
|
|
5659
5966
|
try {
|
|
5660
|
-
mkdirSync11(
|
|
5967
|
+
mkdirSync11(join11(cwd, REPORT_DIR_REL), { recursive: true });
|
|
5661
5968
|
writeFileSync11(htmlPath, html, "utf-8");
|
|
5662
5969
|
} catch (e) {
|
|
5663
5970
|
console.error(
|
|
@@ -5741,7 +6048,7 @@ async function verify(opts = {}) {
|
|
|
5741
6048
|
|
|
5742
6049
|
// src/commands/review.ts
|
|
5743
6050
|
import { existsSync as existsSync17, writeFileSync as writeFileSync12 } from "fs";
|
|
5744
|
-
import { join as
|
|
6051
|
+
import { join as join12 } from "path";
|
|
5745
6052
|
import chalk31 from "chalk";
|
|
5746
6053
|
var GOALS_DIR2 = "goals";
|
|
5747
6054
|
var COVERAGE_MIN = 0.5;
|
|
@@ -5899,7 +6206,7 @@ async function review(opts = {}) {
|
|
|
5899
6206
|
if (opts.id === void 0 && goalStatus === "NOT_STARTED") {
|
|
5900
6207
|
console.error(chalk31.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.`));
|
|
5901
6208
|
}
|
|
5902
|
-
const jsonPath =
|
|
6209
|
+
const jsonPath = join12(cwd, REPORT_PATH_REL);
|
|
5903
6210
|
if (!existsSync17(jsonPath)) {
|
|
5904
6211
|
console.error(chalk31.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).`));
|
|
5905
6212
|
printNextStep({
|
|
@@ -6009,6 +6316,183 @@ ${result.disclaimer}`));
|
|
|
6009
6316
|
}
|
|
6010
6317
|
}
|
|
6011
6318
|
|
|
6319
|
+
// src/commands/mission.ts
|
|
6320
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync12, writeFileSync as writeFileSync13, rmSync as rmSync4 } from "fs";
|
|
6321
|
+
import { join as join13 } from "path";
|
|
6322
|
+
import chalk32 from "chalk";
|
|
6323
|
+
import inquirer12 from "inquirer";
|
|
6324
|
+
import { simpleGit as simpleGit3 } from "simple-git";
|
|
6325
|
+
var MISSION_PATH_REL = join13(".vhk", "mission.json");
|
|
6326
|
+
var MISSION_SCHEMA_VERSION = 1;
|
|
6327
|
+
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).";
|
|
6328
|
+
function globToRegExp(glob) {
|
|
6329
|
+
let re = "";
|
|
6330
|
+
for (let i = 0; i < glob.length; i++) {
|
|
6331
|
+
const c = glob[i];
|
|
6332
|
+
if (c === "*") {
|
|
6333
|
+
if (glob[i + 1] === "*") {
|
|
6334
|
+
re += ".*";
|
|
6335
|
+
i++;
|
|
6336
|
+
if (glob[i + 1] === "/") i++;
|
|
6337
|
+
} else {
|
|
6338
|
+
re += "[^/]*";
|
|
6339
|
+
}
|
|
6340
|
+
} else if (c === "?") {
|
|
6341
|
+
re += "[^/]";
|
|
6342
|
+
} else if (".+^${}()|[]\\".includes(c)) {
|
|
6343
|
+
re += "\\" + c;
|
|
6344
|
+
} else {
|
|
6345
|
+
re += c;
|
|
6346
|
+
}
|
|
6347
|
+
}
|
|
6348
|
+
return new RegExp("^" + re + "$");
|
|
6349
|
+
}
|
|
6350
|
+
function matchesAny(file, globs) {
|
|
6351
|
+
const norm = file.replace(/\\/g, "/");
|
|
6352
|
+
for (const g of globs) {
|
|
6353
|
+
if (globToRegExp(g).test(norm)) return g;
|
|
6354
|
+
}
|
|
6355
|
+
return null;
|
|
6356
|
+
}
|
|
6357
|
+
function checkMission(changedFiles, mission) {
|
|
6358
|
+
const violations = [];
|
|
6359
|
+
const warnings = [];
|
|
6360
|
+
for (const file of changedFiles) {
|
|
6361
|
+
const forbiddenHit = matchesAny(file, mission.forbidden);
|
|
6362
|
+
if (forbiddenHit) {
|
|
6363
|
+
violations.push({ file, pattern: forbiddenHit });
|
|
6364
|
+
continue;
|
|
6365
|
+
}
|
|
6366
|
+
if (mission.scope.length > 0 && !matchesAny(file, mission.scope)) {
|
|
6367
|
+
warnings.push({ file });
|
|
6368
|
+
}
|
|
6369
|
+
}
|
|
6370
|
+
return { violations, warnings, disclaimer: MISSION_DISCLAIMER };
|
|
6371
|
+
}
|
|
6372
|
+
function readMission(cwd = process.cwd()) {
|
|
6373
|
+
const p = join13(cwd, MISSION_PATH_REL);
|
|
6374
|
+
if (!existsSync18(p)) return null;
|
|
6375
|
+
try {
|
|
6376
|
+
const m = readJsonFile(p);
|
|
6377
|
+
if (m && typeof m.objective === "string") return m;
|
|
6378
|
+
return null;
|
|
6379
|
+
} catch {
|
|
6380
|
+
return null;
|
|
6381
|
+
}
|
|
6382
|
+
}
|
|
6383
|
+
function writeMission(cwd, mission) {
|
|
6384
|
+
mkdirSync12(join13(cwd, ".vhk"), { recursive: true });
|
|
6385
|
+
writeFileSync13(join13(cwd, MISSION_PATH_REL), JSON.stringify(mission, null, 2) + "\n", "utf-8");
|
|
6386
|
+
}
|
|
6387
|
+
async function collectChangedFiles(cwd) {
|
|
6388
|
+
const status2 = await simpleGit3(cwd).status();
|
|
6389
|
+
const set = /* @__PURE__ */ new Set();
|
|
6390
|
+
for (const f of status2.files) if (f.path) set.add(f.path.replace(/\\/g, "/"));
|
|
6391
|
+
return [...set];
|
|
6392
|
+
}
|
|
6393
|
+
async function missionSet(opts = {}) {
|
|
6394
|
+
if (!ensureNotHardStopped("mission set")) return;
|
|
6395
|
+
const cwd = process.cwd();
|
|
6396
|
+
const existing = readMission(cwd);
|
|
6397
|
+
let objective = opts.objective ?? existing?.objective ?? "";
|
|
6398
|
+
if (!objective) {
|
|
6399
|
+
if (isInteractive(opts)) {
|
|
6400
|
+
const ans = await inquirer12.prompt([
|
|
6401
|
+
{ type: "input", name: "obj", message: "\uBBF8\uC158 \uBAA9\uD45C(objective)\uB294?" }
|
|
6402
|
+
]);
|
|
6403
|
+
objective = ans.obj.trim();
|
|
6404
|
+
}
|
|
6405
|
+
if (!objective) {
|
|
6406
|
+
console.error(chalk32.red(' \u274C objective \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. --objective "..." \uB85C \uC9C0\uC815\uD558\uC138\uC694(\uBE44\uB300\uD654\uD615).'));
|
|
6407
|
+
process.exitCode = 1;
|
|
6408
|
+
return;
|
|
6409
|
+
}
|
|
6410
|
+
}
|
|
6411
|
+
const scope = opts.clearScope ? [] : opts.scope ?? existing?.scope ?? [];
|
|
6412
|
+
const forbidden = opts.clearForbidden ? [] : opts.forbidden ?? existing?.forbidden ?? [];
|
|
6413
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6414
|
+
const mission = {
|
|
6415
|
+
schemaVersion: MISSION_SCHEMA_VERSION,
|
|
6416
|
+
objective,
|
|
6417
|
+
scope,
|
|
6418
|
+
forbidden,
|
|
6419
|
+
createdAt: existing?.createdAt ?? now,
|
|
6420
|
+
updatedAt: now
|
|
6421
|
+
};
|
|
6422
|
+
try {
|
|
6423
|
+
writeMission(cwd, mission);
|
|
6424
|
+
} catch (e) {
|
|
6425
|
+
console.error(chalk32.red(` \u274C mission.json \uAE30\uB85D \uC2E4\uD328: ${e instanceof Error ? e.message : String(e)}`));
|
|
6426
|
+
process.exitCode = 1;
|
|
6427
|
+
return;
|
|
6428
|
+
}
|
|
6429
|
+
console.log(chalk32.bold("\n\u{1F3AF} \uBBF8\uC158 \uACC4\uC57D \uC800\uC7A5"));
|
|
6430
|
+
console.log(chalk32.dim(` \u{1F4C4} ${MISSION_PATH_REL}`));
|
|
6431
|
+
console.log(` objective: ${mission.objective}`);
|
|
6432
|
+
console.log(` scope: ${mission.scope.length ? mission.scope.join(", ") : "(\uC81C\uD55C \uC5C6\uC74C)"}`);
|
|
6433
|
+
console.log(` forbidden: ${mission.forbidden.length ? mission.forbidden.join(", ") : "(\uC5C6\uC74C)"}`);
|
|
6434
|
+
printNextStep({ message: "\uBCC0\uACBD\uC774 \uACC4\uC57D \uC548\uC778\uC9C0 \uAC80\uC99D\uD558\uB824\uBA74:", command: "vhk mission check", cursorHint: "\uBBF8\uC158 \uAC80\uC99D\uD574\uC918" });
|
|
6435
|
+
}
|
|
6436
|
+
async function missionShow() {
|
|
6437
|
+
const cwd = process.cwd();
|
|
6438
|
+
const mission = readMission(cwd);
|
|
6439
|
+
if (!mission) {
|
|
6440
|
+
console.error(chalk32.yellow(" \u26A0\uFE0F \uBBF8\uC158 \uACC4\uC57D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 (.vhk/mission.json)."));
|
|
6441
|
+
printNextStep({ message: "\uBA3C\uC800 \uBBF8\uC158\uC744 \uC120\uC5B8\uD558\uC138\uC694:", command: 'vhk mission set --objective "..."', cursorHint: "\uBBF8\uC158 \uC815\uD574\uC918" });
|
|
6442
|
+
process.exitCode = 1;
|
|
6443
|
+
return;
|
|
6444
|
+
}
|
|
6445
|
+
console.log(chalk32.bold("\n\u{1F3AF} \uD604\uC7AC \uBBF8\uC158 \uACC4\uC57D"));
|
|
6446
|
+
console.log(` objective: ${mission.objective}`);
|
|
6447
|
+
console.log(` scope: ${mission.scope.length ? mission.scope.join(", ") : "(\uC81C\uD55C \uC5C6\uC74C)"}`);
|
|
6448
|
+
console.log(` forbidden: ${mission.forbidden.length ? mission.forbidden.join(", ") : "(\uC5C6\uC74C)"}`);
|
|
6449
|
+
console.log(chalk32.dim(` \uC0DD\uC131 ${mission.createdAt} \xB7 \uAC31\uC2E0 ${mission.updatedAt}`));
|
|
6450
|
+
}
|
|
6451
|
+
async function missionCheck() {
|
|
6452
|
+
const cwd = process.cwd();
|
|
6453
|
+
const mission = readMission(cwd);
|
|
6454
|
+
if (!mission) {
|
|
6455
|
+
console.error(chalk32.yellow(" \u26A0\uFE0F \uBBF8\uC158 \uACC4\uC57D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uBA3C\uC800 vhk mission set \uC73C\uB85C \uC120\uC5B8\uD558\uC138\uC694."));
|
|
6456
|
+
process.exitCode = 1;
|
|
6457
|
+
return;
|
|
6458
|
+
}
|
|
6459
|
+
const changed = await collectChangedFiles(cwd);
|
|
6460
|
+
const result = checkMission(changed, mission);
|
|
6461
|
+
console.log(chalk32.bold("\n\u{1F3AF} \uBBF8\uC158 \uACC4\uC57D \uAC80\uC99D (mission check)"));
|
|
6462
|
+
console.log(chalk32.dim(` objective: ${mission.objective} \xB7 \uBCC0\uACBD \uD30C\uC77C ${changed.length}\uAC1C`));
|
|
6463
|
+
if (result.violations.length > 0) {
|
|
6464
|
+
console.log(chalk32.red.bold(`
|
|
6465
|
+
\u{1F6AB} forbidden \uC704\uBC18 ${result.violations.length}\uAC74`));
|
|
6466
|
+
for (const v of result.violations) console.log(chalk32.red(` \u2717 ${v.file} (\uAE08\uC9C0: ${v.pattern})`));
|
|
6467
|
+
}
|
|
6468
|
+
if (result.warnings.length > 0) {
|
|
6469
|
+
console.log(chalk32.yellow.bold(`
|
|
6470
|
+
\u26A0\uFE0F scope \uBC16 \uBCC0\uACBD ${result.warnings.length}\uAC74 (\uACBD\uACE0)`));
|
|
6471
|
+
for (const w of result.warnings) console.log(chalk32.yellow(` ? ${w.file}`));
|
|
6472
|
+
}
|
|
6473
|
+
if (result.violations.length === 0 && result.warnings.length === 0) {
|
|
6474
|
+
console.log(chalk32.green("\n \u2713 \uBCC0\uACBD\uC774 \uACC4\uC57D(scope/forbidden) \uC548\uC785\uB2C8\uB2E4."));
|
|
6475
|
+
}
|
|
6476
|
+
console.log(chalk32.yellow(`
|
|
6477
|
+
${result.disclaimer}`));
|
|
6478
|
+
process.exitCode = result.violations.length > 0 ? 1 : 0;
|
|
6479
|
+
}
|
|
6480
|
+
async function missionClear() {
|
|
6481
|
+
const cwd = process.cwd();
|
|
6482
|
+
const p = join13(cwd, MISSION_PATH_REL);
|
|
6483
|
+
if (!existsSync18(p)) {
|
|
6484
|
+
console.log(chalk32.dim(" \uBBF8\uC158 \uACC4\uC57D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uC9C0\uC6B8 \uAC83 \uC5C6\uC74C."));
|
|
6485
|
+
return;
|
|
6486
|
+
}
|
|
6487
|
+
try {
|
|
6488
|
+
rmSync4(p);
|
|
6489
|
+
console.log(chalk32.green(" \u2705 \uBBF8\uC158 \uACC4\uC57D \uC0AD\uC81C\uB428 (.vhk/mission.json)."));
|
|
6490
|
+
} catch (e) {
|
|
6491
|
+
console.error(chalk32.red(` \u274C \uC0AD\uC81C \uC2E4\uD328: ${e instanceof Error ? e.message : String(e)}`));
|
|
6492
|
+
process.exitCode = 1;
|
|
6493
|
+
}
|
|
6494
|
+
}
|
|
6495
|
+
|
|
6012
6496
|
// src/lib/risk-policy.ts
|
|
6013
6497
|
var HIGH_RISK_ACTIONS = [
|
|
6014
6498
|
"undo",
|
|
@@ -6150,6 +6634,7 @@ async function dispatchNlpRoute(route, input) {
|
|
|
6150
6634
|
case "context-show":
|
|
6151
6635
|
return contextShow();
|
|
6152
6636
|
case "memory":
|
|
6637
|
+
if (route.args?.[0] === "migrate") return memoryMigrate();
|
|
6153
6638
|
return memoryList();
|
|
6154
6639
|
case "brief":
|
|
6155
6640
|
return brief();
|
|
@@ -6176,6 +6661,8 @@ async function dispatchNlpRoute(route, input) {
|
|
|
6176
6661
|
return verify();
|
|
6177
6662
|
case "review":
|
|
6178
6663
|
return review();
|
|
6664
|
+
case "mission":
|
|
6665
|
+
return missionShow();
|
|
6179
6666
|
}
|
|
6180
6667
|
}
|
|
6181
6668
|
var STATE_CHANGING_COMMANDS = /* @__PURE__ */ new Set([
|
|
@@ -6189,23 +6676,23 @@ function requiresConfirmation(route) {
|
|
|
6189
6676
|
async function runNaturalLanguageRoute(input) {
|
|
6190
6677
|
const route = routeNaturalLanguage(input);
|
|
6191
6678
|
if (!route) {
|
|
6192
|
-
console.log(
|
|
6679
|
+
console.log(chalk33.yellow(`
|
|
6193
6680
|
\u2753 "${input}" \u2014 ${ko.nlp.notMatched}
|
|
6194
6681
|
`));
|
|
6195
6682
|
return;
|
|
6196
6683
|
}
|
|
6197
6684
|
console.log("");
|
|
6198
|
-
console.log(
|
|
6199
|
-
console.log(
|
|
6685
|
+
console.log(chalk33.cyan(` \u{1F4AC} "${input}"`));
|
|
6686
|
+
console.log(chalk33.cyan(` \u2192 ${route.explanation}`));
|
|
6200
6687
|
if (requiresConfirmation(route)) {
|
|
6201
|
-
const { confirm } = await
|
|
6688
|
+
const { confirm } = await inquirer13.prompt([{
|
|
6202
6689
|
type: "confirm",
|
|
6203
6690
|
name: "confirm",
|
|
6204
6691
|
message: `${route.explanation} \u2014 ${ko.nlp.matched}`,
|
|
6205
6692
|
default: true
|
|
6206
6693
|
}]);
|
|
6207
6694
|
if (!confirm) {
|
|
6208
|
-
console.log(
|
|
6695
|
+
console.log(chalk33.dim(` ${ko.nlp.menuHint}`));
|
|
6209
6696
|
return;
|
|
6210
6697
|
}
|
|
6211
6698
|
}
|
|
@@ -6214,7 +6701,7 @@ async function runNaturalLanguageRoute(input) {
|
|
|
6214
6701
|
if (riskAction) {
|
|
6215
6702
|
await runGuarded(
|
|
6216
6703
|
riskAction,
|
|
6217
|
-
{ channel: "nl", approved: false, log: (m) => console.log(
|
|
6704
|
+
{ channel: "nl", approved: false, log: (m) => console.log(chalk33.yellow(` ${m}`)) },
|
|
6218
6705
|
() => dispatchNlpRoute(route, input)
|
|
6219
6706
|
);
|
|
6220
6707
|
return;
|
|
@@ -6223,77 +6710,80 @@ async function runNaturalLanguageRoute(input) {
|
|
|
6223
6710
|
}
|
|
6224
6711
|
|
|
6225
6712
|
// src/commands/agent.ts
|
|
6226
|
-
import
|
|
6713
|
+
import chalk34 from "chalk";
|
|
6227
6714
|
function activeGoalId() {
|
|
6228
6715
|
const goals = listGoals("goals");
|
|
6229
6716
|
const id = selectActiveId(goals);
|
|
6230
6717
|
return id ?? void 0;
|
|
6231
6718
|
}
|
|
6232
6719
|
async function blocker(description) {
|
|
6233
|
-
console.log(
|
|
6720
|
+
console.log(chalk34.bold(`
|
|
6234
6721
|
${ko.agent.blockerTitle}
|
|
6235
6722
|
`));
|
|
6236
6723
|
if (!description || !description.trim()) {
|
|
6237
|
-
console.log(
|
|
6238
|
-
console.log(
|
|
6724
|
+
console.log(chalk34.red(" \u274C \uBE14\uB85C\uCEE4 \uC124\uBA85\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694."));
|
|
6725
|
+
console.log(chalk34.dim(' \uC608: vhk blocker "tsc \uC5D0\uB7EC \u2014 simple-git \uD0C0\uC785 \uD638\uD658"'));
|
|
6239
6726
|
process.exitCode = 1;
|
|
6240
6727
|
return;
|
|
6241
6728
|
}
|
|
6242
6729
|
const goalId = activeGoalId();
|
|
6243
6730
|
const r = appendBlocker(description, goalId);
|
|
6244
|
-
console.log(
|
|
6731
|
+
console.log(chalk34.green(` \u2705 blocker \uAE30\uB85D (\uD604\uC7AC \uD65C\uC131 ${r.count}\uAC74)`));
|
|
6245
6732
|
if (r.hardStopTripped) {
|
|
6246
|
-
console.log(
|
|
6247
|
-
console.log(
|
|
6733
|
+
console.log(chalk34.red.bold(" \u{1F6D1} HARD_STOP \uC790\uB3D9 \uC0DD\uC131 \u2014 \uBAA8\uB4E0 \uC790\uB3D9\uD654 \uC911\uB2E8."));
|
|
6734
|
+
console.log(chalk34.yellow(" \uC0AC\uB78C \uAC80\uD1A0 \uD6C4 `vhk resume --confirm` \uC73C\uB85C\uB9CC \uD574\uC81C."));
|
|
6248
6735
|
process.exitCode = 2;
|
|
6249
6736
|
}
|
|
6250
6737
|
}
|
|
6251
6738
|
async function learn(lesson) {
|
|
6252
|
-
console.log(
|
|
6739
|
+
console.log(chalk34.bold(`
|
|
6253
6740
|
${ko.agent.learnTitle}
|
|
6254
6741
|
`));
|
|
6255
6742
|
if (!lesson || !lesson.trim()) {
|
|
6256
|
-
console.log(
|
|
6257
|
-
console.log(
|
|
6743
|
+
console.log(chalk34.red(" \u274C \uAD50\uD6C8 \uB0B4\uC6A9\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694."));
|
|
6744
|
+
console.log(chalk34.dim(' \uC608: vhk learn "PowerShell \uC5D0\uC11C\uB294 ; \uC0AC\uC6A9 (&& \uBBF8\uC9C0\uC6D0)"'));
|
|
6258
6745
|
process.exitCode = 1;
|
|
6259
6746
|
return;
|
|
6260
6747
|
}
|
|
6261
6748
|
const goalId = activeGoalId();
|
|
6262
|
-
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
6749
|
+
const entry = recordLesson(process.cwd(), lesson, goalId);
|
|
6750
|
+
if (!entry) {
|
|
6751
|
+
console.log(chalk34.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
|
+
process.exitCode = 1;
|
|
6753
|
+
return;
|
|
6754
|
+
}
|
|
6755
|
+
console.log(chalk34.green(` \u2705 \uAD50\uD6C8 \uAE30\uB85D \u2192 memory failures.lesson (${entry.id})`));
|
|
6756
|
+
console.log(chalk34.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."));
|
|
6267
6757
|
}
|
|
6268
6758
|
async function resume(opts = {}) {
|
|
6269
|
-
console.log(
|
|
6759
|
+
console.log(chalk34.bold(`
|
|
6270
6760
|
${ko.agent.resumeTitle}
|
|
6271
6761
|
`));
|
|
6272
6762
|
if (!isHardStopActive()) {
|
|
6273
|
-
console.log(
|
|
6763
|
+
console.log(chalk34.dim(" HARD_STOP \uD65C\uC131 \uC544\uB2D8 \u2014 \uD560 \uC77C \uC5C6\uC74C."));
|
|
6274
6764
|
return;
|
|
6275
6765
|
}
|
|
6276
6766
|
const reason = readHardStopReason();
|
|
6277
6767
|
if (reason) {
|
|
6278
|
-
console.log(
|
|
6279
|
-
console.log(
|
|
6768
|
+
console.log(chalk34.yellow(" \u{1F4CB} HARD_STOP \uC0AC\uC720:"));
|
|
6769
|
+
console.log(chalk34.dim(` ${reason.split("\n").join("\n ")}`));
|
|
6280
6770
|
console.log("");
|
|
6281
6771
|
}
|
|
6282
6772
|
if (!opts.confirm) {
|
|
6283
6773
|
console.log(
|
|
6284
|
-
|
|
6774
|
+
chalk34.red(
|
|
6285
6775
|
" \u274C --confirm \uD50C\uB798\uADF8 \uC5C6\uC774\uB294 \uD574\uC81C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC790\uB3D9 \uD638\uCD9C \uAE08\uC9C0)."
|
|
6286
6776
|
)
|
|
6287
6777
|
);
|
|
6288
|
-
console.log(
|
|
6778
|
+
console.log(chalk34.yellow(" \uC0AC\uC720\uB97C \uD655\uC778\uD55C \uD6C4 \uB2E4\uC2DC: vhk resume --confirm"));
|
|
6289
6779
|
process.exitCode = 1;
|
|
6290
6780
|
return;
|
|
6291
6781
|
}
|
|
6292
6782
|
const removed = clearHardStop();
|
|
6293
6783
|
if (removed) {
|
|
6294
|
-
console.log(
|
|
6784
|
+
console.log(chalk34.green(" \u2705 HARD_STOP \uD574\uC81C. \uC790\uB3D9\uD654 \uC7AC\uAC1C \uAC00\uB2A5."));
|
|
6295
6785
|
} else {
|
|
6296
|
-
console.log(
|
|
6786
|
+
console.log(chalk34.dim(" \uD30C\uC77C\uC774 \uC774\uBBF8 \uC5C6\uC74C \u2014 no-op."));
|
|
6297
6787
|
}
|
|
6298
6788
|
}
|
|
6299
6789
|
|
|
@@ -6306,7 +6796,7 @@ async function guardCli(action, approved, run) {
|
|
|
6306
6796
|
channel: "cli",
|
|
6307
6797
|
approved,
|
|
6308
6798
|
confirm: async () => {
|
|
6309
|
-
const { ok } = await
|
|
6799
|
+
const { ok } = await inquirer14.prompt([{
|
|
6310
6800
|
type: "confirm",
|
|
6311
6801
|
name: "ok",
|
|
6312
6802
|
message: `\u26A0\uFE0F \uC704\uD5D8 \uC791\uC5C5(${action})\uC744 \uC2E4\uD589\uD560\uAE4C\uC694?`,
|
|
@@ -6314,7 +6804,7 @@ async function guardCli(action, approved, run) {
|
|
|
6314
6804
|
}]);
|
|
6315
6805
|
return ok;
|
|
6316
6806
|
},
|
|
6317
|
-
log: (m) => console.log(
|
|
6807
|
+
log: (m) => console.log(chalk35.yellow(` ${m}`))
|
|
6318
6808
|
},
|
|
6319
6809
|
run
|
|
6320
6810
|
);
|
|
@@ -6327,7 +6817,7 @@ async function guardCliDefer(action, approved, run) {
|
|
|
6327
6817
|
approved,
|
|
6328
6818
|
// TTY 면 통과(명령이 자체 확인), 비대화형은 confirm 불가 → 가드가 차단.
|
|
6329
6819
|
confirm: async () => !!process.stdout.isTTY,
|
|
6330
|
-
log: (m) => console.log(
|
|
6820
|
+
log: (m) => console.log(chalk35.yellow(` ${m}`))
|
|
6331
6821
|
},
|
|
6332
6822
|
run
|
|
6333
6823
|
);
|
|
@@ -6367,6 +6857,7 @@ var KO_ALIASES = {
|
|
|
6367
6857
|
brief: "\uBE0C\uB9AC\uD551",
|
|
6368
6858
|
goal: "\uBAA9\uD45C",
|
|
6369
6859
|
review: "\uAC80\uD1A0",
|
|
6860
|
+
mission: "\uBBF8\uC158",
|
|
6370
6861
|
blocker: "\uBE14\uB85C\uCEE4",
|
|
6371
6862
|
learn: "\uAD50\uD6C8",
|
|
6372
6863
|
resume: "\uC7AC\uAC1C"
|
|
@@ -6494,22 +6985,48 @@ program.command("verify").alias("\uC0AC\uC804\uC810\uAC80").option("--json", "\u
|
|
|
6494
6985
|
program.command("review").alias("\uAC80\uD1A0").option("--id <id>", "\uB300\uC0C1 goal id (\uC5C6\uC73C\uBA74 active goal)").description("\uC801\uB300\uC801 \uC790\uAE30\uAC80\uC99D \u2014 latest.json \u2194 goal \uC644\uB8CC\uC870\uAC74 \uAD50\uCC28\uAC80\uC99D (\uAC70\uC9D3\uC644\uB8CC \uC758\uC2EC \uD0D0\uC9C0, \uBCF4\uC7A5 \uC544\uB2D8)").action(async (opts) => {
|
|
6495
6986
|
await review(opts);
|
|
6496
6987
|
});
|
|
6988
|
+
var collectGlob = (v, prev = []) => prev.concat([v]);
|
|
6989
|
+
var missionCmd = program.command("mission").alias("\uBBF8\uC158").description("\uBBF8\uC158 \uACC4\uC57D \u2014 \uC791\uC5C5 \uBAA9\uD45C\xB7\uD5C8\uC6A9/\uAE08\uC9C0 \uBC94\uC704 \uC120\uC5B8\xB7\uAC80\uC99D (scope \uAC00\uB4DC, .vhk/mission.json)").action(async () => {
|
|
6990
|
+
await missionShow();
|
|
6991
|
+
});
|
|
6992
|
+
missionCmd.command("set").option("--objective <text>", "\uBBF8\uC158 \uBAA9\uD45C(objective)").option("--scope <glob>", "\uD5C8\uC6A9 \uACBD\uB85C glob (\uBC18\uBCF5 \uAC00\uB2A5, \uC81C\uACF5 \uC2DC \uAD50\uCCB4)", collectGlob).option("--forbidden <glob>", "\uAE08\uC9C0 \uACBD\uB85C glob (\uBC18\uBCF5 \uAC00\uB2A5, \uC81C\uACF5 \uC2DC \uAD50\uCCB4)", collectGlob).option("--clear-scope", "scope \uB97C \uBE44\uC6C0(\uBA85\uC2DC\uC801)").option("--clear-forbidden", "forbidden \uC744 \uBE44\uC6C0(\uBA85\uC2DC\uC801)").option("-y, --yes", "\uB300\uD654\uD615 \uD504\uB86C\uD504\uD2B8 \uC2A4\uD0B5 (\uBE44\uB300\uD654\uD615)").description("\uBBF8\uC158 \uACC4\uC57D \uC120\uC5B8/\uAC31\uC2E0 (\uC635\uC158 \uBBF8\uC9C0\uC815 \uC2DC \uAE30\uC874 scope/forbidden \uBCF4\uC874)").action(async (opts) => {
|
|
6993
|
+
await missionSet(opts);
|
|
6994
|
+
});
|
|
6995
|
+
missionCmd.command("check").description("\uBCC0\uACBD \uD30C\uC77C\uC774 \uACC4\uC57D(scope/forbidden) \uC548\uC778\uC9C0 \uAC80\uC99D \u2014 forbidden \uC704\uBC18 \uC2DC exit 1").action(async () => {
|
|
6996
|
+
await missionCheck();
|
|
6997
|
+
});
|
|
6998
|
+
missionCmd.command("clear").description("\uBBF8\uC158 \uACC4\uC57D \uC0AD\uC81C (.vhk/mission.json)").action(async () => {
|
|
6999
|
+
await missionClear();
|
|
7000
|
+
});
|
|
6497
7001
|
program.command("context-show").alias("\uB9E5\uB77D\uBCF4\uAE30").description("\uD604\uC7AC \uCEE8\uD14D\uC2A4\uD2B8 \uD30C\uC77C \uB0B4\uC6A9 \uCD9C\uB825").action(async () => {
|
|
6498
7002
|
await contextShow();
|
|
6499
7003
|
});
|
|
6500
|
-
var memoryCmd = program.command("memory").alias("\uAE30\uC5B5").description("\
|
|
7004
|
+
var memoryCmd = program.command("memory").alias("\uAE30\uC5B5").description("\uAE30\uC5B5 \uAD00\uB9AC v2 (decisions/failures/successes 4\uBC84\uD0B7) \u2014 add/list/remove/archive/resolve/unarchive/migrate").action(async () => {
|
|
6501
7005
|
await memoryList();
|
|
6502
7006
|
});
|
|
6503
|
-
memoryCmd.command("add <content>").option("--tags <tags>", "\uD0DC\uADF8 (\uC27C\uD45C \uAD6C\uBD84)").
|
|
7007
|
+
memoryCmd.command("add <content>").option("--type <type>", "\uBC84\uD0B7: decision|failure|success (\uAE30\uBCF8 decision)").option("--tags <tags>", "\uD0DC\uADF8 (\uC27C\uD45C \uAD6C\uBD84)").option("--why <why>", "\uC6D0\uC778 (failure/success)").option("--lesson <lesson>", "\uAD50\uD6C8 (failure)").description("\uAE30\uC5B5 \uC800\uC7A5 (--type \uC73C\uB85C \uACB0\uC815/\uC2E4\uD328/\uC131\uACF5 \uAD6C\uBD84)").action(async (content, opts) => {
|
|
6504
7008
|
const tags = opts.tags ? opts.tags.split(",").map((s) => s.trim()) : void 0;
|
|
6505
|
-
await memoryAdd(content, tags);
|
|
7009
|
+
await memoryAdd(content, { type: opts.type, tags, why: opts.why, lesson: opts.lesson });
|
|
6506
7010
|
});
|
|
6507
|
-
memoryCmd.command("list").alias("\uBAA9\uB85D").description("\uC800\uC7A5\uB41C \uAE30\uC5B5 \uBAA9\uB85D").action(async () => {
|
|
6508
|
-
|
|
7011
|
+
memoryCmd.command("list").alias("\uBAA9\uB85D").option("--type <type>", "\uBC84\uD0B7 \uD544\uD130: decision|failure|success").option("--all", "\uBCF4\uAD00(archived)\xB7\uD574\uACB0(resolved) \uD3EC\uD568").description("\uC800\uC7A5\uB41C \uAE30\uC5B5 \uBAA9\uB85D (\uAE30\uBCF8 \uD65C\uC131\uB9CC)").action(async (opts) => {
|
|
7012
|
+
const type = opts.type === "decision" || opts.type === "failure" || opts.type === "success" ? opts.type : void 0;
|
|
7013
|
+
await memoryList({ type, all: opts.all });
|
|
6509
7014
|
});
|
|
6510
7015
|
memoryCmd.command("remove <index>").alias("\uC0AD\uC81C").description("\uAE30\uC5B5 \uC0AD\uC81C (1\uBD80\uD130 \uC2DC\uC791\uD558\uB294 \uBC88\uD638)").action(async (index) => {
|
|
6511
7016
|
await memoryRemove(index);
|
|
6512
7017
|
});
|
|
7018
|
+
memoryCmd.command("archive <index>").alias("\uBCF4\uAD00").description("\uAE30\uC5B5 \uBCF4\uAD00 (\uD65C\uC131\u2192archived, \uD328\uD134/\uC9C4\uD654\uC5D0\uC11C \uC81C\uC678)").action(async (index) => {
|
|
7019
|
+
await memoryArchive(index);
|
|
7020
|
+
});
|
|
7021
|
+
memoryCmd.command("resolve <index>").alias("\uD574\uACB0").description("\uAE30\uC5B5 \uD574\uACB0 \uD45C\uC2DC (\uD65C\uC131\u2192resolved, \uD328\uD134/\uC9C4\uD654\uC5D0\uC11C \uC81C\uC678)").action(async (index) => {
|
|
7022
|
+
await memoryResolve(index);
|
|
7023
|
+
});
|
|
7024
|
+
memoryCmd.command("unarchive <index>").alias("\uBCF5\uAD6C").description("\uBCF4\uAD00/\uD574\uACB0 \uD56D\uBAA9\uC744 \uB2E4\uC2DC \uD65C\uC131\uC73C\uB85C \uBCF5\uAD6C (archive/resolve \uC5ED\uC804)").action(async (index) => {
|
|
7025
|
+
await memoryUnarchive(index);
|
|
7026
|
+
});
|
|
7027
|
+
memoryCmd.command("migrate").alias("\uB9C8\uC774\uADF8\uB808\uC774\uC158").description("memory.json v1 \u2192 v2 \uB9C8\uC774\uADF8\uB808\uC774\uC158 (\uAE30\uC874 v1 \uC788\uC73C\uBA74 .v1.bak \uC6D0\uBCF8 \uBC31\uC5C5, \uBA71\uB4F1)").action(async () => {
|
|
7028
|
+
await memoryMigrate();
|
|
7029
|
+
});
|
|
6513
7030
|
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 () => {
|
|
6514
7031
|
await brief();
|
|
6515
7032
|
});
|
|
@@ -6537,7 +7054,7 @@ goalCmd.command("sync").alias("\uB3D9\uAE30\uD654").description("goals/*.md \uC2
|
|
|
6537
7054
|
program.command("blocker <description>").alias("\uBE14\uB85C\uCEE4").description("\uBE14\uB85C\uCEE4 \uAE30\uB85D \u2192 docs/state/blockers.md append (3\uAC74 \uB204\uC801 \uC2DC HARD_STOP \uC790\uB3D9 \uC0DD\uC131)").action(async (description) => {
|
|
6538
7055
|
await blocker(description);
|
|
6539
7056
|
});
|
|
6540
|
-
program.command("learn <lesson>").alias("\uAD50\uD6C8").description("\uAD50\uD6C8 \uAE30\uB85D \u2192
|
|
7057
|
+
program.command("learn <lesson>").alias("\uAD50\uD6C8").description("\uAD50\uD6C8 \uAE30\uB85D \u2192 memory v2 failures.lesson \uB2E8\uC77C SoT (v2.0 \uD1B5\uD569 \u2014 vhk memory list \uB85C \uD655\uC778)").action(async (lesson) => {
|
|
6541
7058
|
await learn(lesson);
|
|
6542
7059
|
});
|
|
6543
7060
|
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) => {
|
|
@@ -6551,7 +7068,7 @@ program.on("command:*", async (operands) => {
|
|
|
6551
7068
|
});
|
|
6552
7069
|
program.action(async () => {
|
|
6553
7070
|
console.log("\n\u{1F3AF} VHK \u2014 \uBC14\uC774\uBE0C\uCF54\uB529 \uD504\uB85C\uC81D\uD2B8 \uCF54\uCE58\n");
|
|
6554
|
-
const { choice } = await
|
|
7071
|
+
const { choice } = await inquirer14.prompt([{
|
|
6555
7072
|
type: "list",
|
|
6556
7073
|
name: "choice",
|
|
6557
7074
|
message: "\uBB58 \uB3C4\uC640\uB4DC\uB9B4\uAE4C\uC694?",
|
|
@@ -6608,9 +7125,9 @@ if (isMainModule) {
|
|
|
6608
7125
|
}
|
|
6609
7126
|
} catch (err) {
|
|
6610
7127
|
if (isPromptAbortError(err)) {
|
|
6611
|
-
console.error(
|
|
7128
|
+
console.error(chalk35.yellow("\n \u26A0\uFE0F \uB300\uD654\uD615 \uC785\uB825\uC774 \uCDE8\uC18C/\uC885\uB8CC\uB410\uC2B5\uB2C8\uB2E4. (\uBE44\uB300\uD654\uD615 \uD658\uACBD\uC5D0\uC11C\uB294 \uD574\uB2F9 \uBA85\uB839\uC744 \uC4F8 \uC218 \uC5C6\uC5B4\uC694)"));
|
|
6612
7129
|
} else {
|
|
6613
|
-
console.error(
|
|
7130
|
+
console.error(chalk35.red(`
|
|
6614
7131
|
\u274C ${err instanceof Error ? err.message : String(err)}`));
|
|
6615
7132
|
}
|
|
6616
7133
|
process.exitCode = 1;
|