@vibecodetown/mcp-server 2.1.3 → 2.2.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/build/auth/credential_store.js +146 -0
- package/build/auth/index.js +2 -0
- package/build/control_plane/gate.js +52 -70
- package/build/index.js +2 -0
- package/build/local-mode/git.js +36 -22
- package/build/local-mode/install-state.js +37 -0
- package/build/local-mode/paths.js +1 -0
- package/build/local-mode/project-state.js +176 -0
- package/build/local-mode/setup.js +46 -0
- package/build/local-mode/templates.js +3 -3
- package/build/local-mode/version-lock.js +53 -0
- package/build/runtime/cli_invoker.js +416 -0
- package/build/tools/vibe_pm/briefing.js +2 -1
- package/build/tools/vibe_pm/finalize_work.js +40 -4
- package/build/tools/vibe_pm/force_override.js +104 -0
- package/build/tools/vibe_pm/index.js +73 -2
- package/build/tools/vibe_pm/list_rules.js +135 -0
- package/build/tools/vibe_pm/pre_commit_analysis.js +292 -0
- package/build/tools/vibe_pm/publish_mcp.js +271 -0
- package/build/tools/vibe_pm/run_app.js +48 -45
- package/build/tools/vibe_pm/save_rule.js +120 -0
- package/build/tools/vibe_pm/undo_last_task.js +16 -20
- package/build/version-check.js +5 -5
- package/build/vibe-cli.js +610 -37
- package/package.json +4 -4
package/build/vibe-cli.js
CHANGED
|
@@ -14,6 +14,8 @@ import { setRepoConfigValue, validateRepoConfig } from "./local-mode/config.js";
|
|
|
14
14
|
import { getVibeRepoPaths } from "./local-mode/paths.js";
|
|
15
15
|
import { archiveCurrentWorkOrder, createWorkOrderTemplate, readCurrentWorkOrder, writeCurrentWorkOrder } from "./local-mode/work-order.js";
|
|
16
16
|
import { autoUpdateOnStart } from "./version-check.js";
|
|
17
|
+
import { runSetup } from "./local-mode/setup.js";
|
|
18
|
+
import { checkInstallState } from "./local-mode/install-state.js";
|
|
17
19
|
// ============================================================
|
|
18
20
|
// Constants
|
|
19
21
|
// ============================================================
|
|
@@ -110,13 +112,157 @@ async function cmdDoctor() {
|
|
|
110
112
|
function resolveRepoRoot() {
|
|
111
113
|
return getGitRoot(process.cwd()) ?? process.cwd();
|
|
112
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* P0-3: Check if hooks are properly configured before risky operations.
|
|
117
|
+
* Returns warnings if hooks are not set up (does not block execution).
|
|
118
|
+
*/
|
|
119
|
+
function checkHooksIntegrity(repoRoot) {
|
|
120
|
+
const warnings = [];
|
|
121
|
+
const hooksPath = getGitHooksPath(repoRoot);
|
|
122
|
+
if (!hooksPath) {
|
|
123
|
+
warnings.push("Git hooks 경로가 설정되어 있지 않습니다. 'vibe setup' 실행을 권장합니다.");
|
|
124
|
+
return { ok: false, hooksPath: null, prePushExists: false, validateExists: false, warnings };
|
|
125
|
+
}
|
|
126
|
+
const prePushPath = path.join(hooksPath, "pre-push");
|
|
127
|
+
const validatePath = path.join(repoRoot, ".vibe", "lib", "validate.sh");
|
|
128
|
+
const prePushExists = fs.existsSync(prePushPath);
|
|
129
|
+
const validateExists = fs.existsSync(validatePath);
|
|
130
|
+
if (!prePushExists) {
|
|
131
|
+
warnings.push("pre-push hook이 설치되어 있지 않습니다. 'vibe setup' 실행을 권장합니다.");
|
|
132
|
+
}
|
|
133
|
+
if (!validateExists) {
|
|
134
|
+
warnings.push("validate.sh가 없습니다. 'vibe setup' 실행을 권장합니다.");
|
|
135
|
+
}
|
|
136
|
+
const ok = prePushExists && validateExists;
|
|
137
|
+
return { ok, hooksPath, prePushExists, validateExists, warnings };
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* P0-3: Display hooks integrity warning if needed.
|
|
141
|
+
* Called before potentially risky commands (push, check, etc.)
|
|
142
|
+
*/
|
|
143
|
+
function warnIfHooksNotConfigured(repoRoot) {
|
|
144
|
+
// Skip in non-interactive environments
|
|
145
|
+
if (!process.stdout.isTTY)
|
|
146
|
+
return;
|
|
147
|
+
const result = checkHooksIntegrity(repoRoot);
|
|
148
|
+
if (result.ok)
|
|
149
|
+
return;
|
|
150
|
+
console.log("");
|
|
151
|
+
console.log(c("yellow", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
|
|
152
|
+
console.log(c("yellow", " ⚠ Hooks 미설정 경고"));
|
|
153
|
+
console.log(c("yellow", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
|
|
154
|
+
for (const w of result.warnings) {
|
|
155
|
+
console.log(` ${c("yellow", "•")} ${w}`);
|
|
156
|
+
}
|
|
157
|
+
console.log("");
|
|
158
|
+
}
|
|
159
|
+
async function cmdSetup() {
|
|
160
|
+
const repoRoot = resolveRepoRoot();
|
|
161
|
+
const args = process.argv.slice(2);
|
|
162
|
+
const forceFlag = args.includes("--force") || args.includes("-f");
|
|
163
|
+
const localOnly = args.includes("--local-only");
|
|
164
|
+
const checkOnly = args.includes("--check");
|
|
165
|
+
const noHooks = args.includes("--no-hooks");
|
|
166
|
+
const installCi = args.includes("--ci");
|
|
167
|
+
const ciFailOnWarn = args.includes("--strict");
|
|
168
|
+
console.log("");
|
|
169
|
+
console.log(c("cyan", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
|
|
170
|
+
console.log(c("cyan", " Vibe PM Setup"));
|
|
171
|
+
console.log(c("cyan", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
|
|
172
|
+
console.log("");
|
|
173
|
+
// P0-1: --no-hooks deprecation warning
|
|
174
|
+
if (noHooks) {
|
|
175
|
+
console.log(c("yellow", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
|
|
176
|
+
console.log(c("yellow", " ⚠ DEPRECATED: --no-hooks 옵션"));
|
|
177
|
+
console.log(c("yellow", " 이 옵션은 향후 버전에서 제거될 예정입니다."));
|
|
178
|
+
console.log(c("yellow", " Git hooks는 코드 품질과 보안을 위해 권장됩니다."));
|
|
179
|
+
console.log(c("yellow", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
|
|
180
|
+
console.log("");
|
|
181
|
+
}
|
|
182
|
+
if (checkOnly) {
|
|
183
|
+
const state = await checkInstallState(repoRoot);
|
|
184
|
+
console.log(` Local: ${state.localInitialized ? c("green", "OK") : c("red", "NOT INITIALIZED")}`);
|
|
185
|
+
console.log(` Engines: ${state.enginesReady ? c("green", "OK") : c("yellow", "INCOMPLETE")}`);
|
|
186
|
+
console.log(` Version Lock: ${state.versionLockExists ? c("green", "OK") : c("yellow", "MISSING")}`);
|
|
187
|
+
if (state.warnings.length > 0) {
|
|
188
|
+
console.log("");
|
|
189
|
+
state.warnings.forEach(w => console.log(` ${c("yellow", "\u26A0")} ${w}`));
|
|
190
|
+
}
|
|
191
|
+
console.log("");
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
// Step 1: Local mode
|
|
195
|
+
console.log(" Step 1/3: 로컬 모드 초기화...");
|
|
196
|
+
// Step 2: Engines
|
|
197
|
+
if (!localOnly) {
|
|
198
|
+
console.log(" Step 2/3: 엔진 설치...");
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
console.log(" Step 2/3: 엔진 설치 (스킵 - local-only)");
|
|
202
|
+
}
|
|
203
|
+
// Step 3: Version lock
|
|
204
|
+
console.log(" Step 3/3: 버전 잠금...");
|
|
205
|
+
const result = await runSetup(repoRoot, VERSION, {
|
|
206
|
+
force: forceFlag,
|
|
207
|
+
localOnly,
|
|
208
|
+
installHooks: !noHooks,
|
|
209
|
+
installCi,
|
|
210
|
+
ciFailOnWarn,
|
|
211
|
+
});
|
|
212
|
+
console.log("");
|
|
213
|
+
// Results
|
|
214
|
+
if (result.local.created.length > 0) {
|
|
215
|
+
console.log(` ${c("green", "\u2713")} 로컬 파일 생성: ${result.local.created.length}개`);
|
|
216
|
+
}
|
|
217
|
+
if (result.local.updated.length > 0) {
|
|
218
|
+
console.log(` ${c("yellow", "\u2191")} 로컬 파일 업데이트: ${result.local.updated.length}개`);
|
|
219
|
+
}
|
|
220
|
+
if (result.engines.length > 0) {
|
|
221
|
+
const ok = result.engines.filter(e => e.status === "ok").length;
|
|
222
|
+
console.log(` ${c("green", "\u2713")} 엔진: ${ok}/${result.engines.length} 준비됨`);
|
|
223
|
+
}
|
|
224
|
+
if (result.versionLock) {
|
|
225
|
+
console.log(` ${c("green", "\u2713")} 버전 잠금 생성됨`);
|
|
226
|
+
}
|
|
227
|
+
// Notes
|
|
228
|
+
if (result.local.notes.length > 0) {
|
|
229
|
+
console.log("");
|
|
230
|
+
console.log(" Notes:");
|
|
231
|
+
result.local.notes.forEach(n => console.log(` - ${n}`));
|
|
232
|
+
}
|
|
233
|
+
// Warnings
|
|
234
|
+
if (result.warnings.length > 0) {
|
|
235
|
+
console.log("");
|
|
236
|
+
result.warnings.forEach(w => console.log(` ${c("yellow", "\u26A0")} ${w}`));
|
|
237
|
+
}
|
|
238
|
+
console.log("");
|
|
239
|
+
console.log(c("cyan", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
|
|
240
|
+
console.log(` ${c("green", "\u2713")} Setup 완료!`);
|
|
241
|
+
console.log("");
|
|
242
|
+
console.log(` Next: ${c("cyan", "vibe status")}`);
|
|
243
|
+
console.log("");
|
|
244
|
+
}
|
|
113
245
|
function cmdInit() {
|
|
246
|
+
// Deprecation warning
|
|
247
|
+
console.log("");
|
|
248
|
+
console.log(c("yellow", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
|
|
249
|
+
console.log(c("yellow", " \u26A0 DEPRECATED: 'vibe init' \u2192 'vibe setup' 사용"));
|
|
250
|
+
console.log(c("yellow", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
|
|
114
251
|
const repoRoot = resolveRepoRoot();
|
|
115
252
|
const args = process.argv.slice(2);
|
|
116
253
|
const forceFlag = args.includes("--force") || args.includes("-f");
|
|
117
254
|
const noHooks = args.includes("--no-hooks");
|
|
118
255
|
const installCi = args.includes("--ci") || args.includes("--install-ci");
|
|
119
256
|
const ciFailOnWarn = args.includes("--strict");
|
|
257
|
+
// P0-1: --no-hooks deprecation warning
|
|
258
|
+
if (noHooks) {
|
|
259
|
+
console.log("");
|
|
260
|
+
console.log(c("yellow", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
|
|
261
|
+
console.log(c("yellow", " ⚠ DEPRECATED: --no-hooks 옵션"));
|
|
262
|
+
console.log(c("yellow", " 이 옵션은 향후 버전에서 제거될 예정입니다."));
|
|
263
|
+
console.log(c("yellow", " Git hooks는 코드 품질과 보안을 위해 권장됩니다."));
|
|
264
|
+
console.log(c("yellow", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
|
|
265
|
+
}
|
|
120
266
|
const result = initLocalModeRepo(repoRoot, { force: forceFlag, installHooks: !noHooks, installCi, ciFailOnWarn });
|
|
121
267
|
console.log("");
|
|
122
268
|
console.log(c("cyan", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
|
|
@@ -213,6 +359,8 @@ function cmdTicket() {
|
|
|
213
359
|
function cmdCheck(update) {
|
|
214
360
|
const repoRoot = resolveRepoRoot();
|
|
215
361
|
const paths = getVibeRepoPaths(repoRoot);
|
|
362
|
+
// P0-3: Hooks integrity pre-check (warning only)
|
|
363
|
+
warnIfHooksNotConfigured(repoRoot);
|
|
216
364
|
const cfg = validateRepoConfig(paths.configFile);
|
|
217
365
|
if (!cfg.ok) {
|
|
218
366
|
console.log(`[VIBE] Config invalid: ${cfg.error}`);
|
|
@@ -356,51 +504,91 @@ async function cmdReset() {
|
|
|
356
504
|
console.log("");
|
|
357
505
|
const cwd = process.cwd();
|
|
358
506
|
const runsDir = path.join(cwd, "runs");
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
507
|
+
const vibeDir = path.join(cwd, ".vibe");
|
|
508
|
+
const vibeStateDir = path.join(vibeDir, "state");
|
|
509
|
+
const vibeDecisionsDir = path.join(vibeDir, "decisions");
|
|
510
|
+
// P2-2: --keep-context 옵션 확인
|
|
511
|
+
const keepContext = process.argv.includes("--keep-context");
|
|
512
|
+
const forceFlag = process.argv.includes("--force") || process.argv.includes("-f");
|
|
513
|
+
// Collect items to delete
|
|
514
|
+
const toDelete = [];
|
|
515
|
+
// runs/ 디렉토리
|
|
516
|
+
if (fs.existsSync(runsDir)) {
|
|
517
|
+
const runs = fs.readdirSync(runsDir).filter((f) => {
|
|
518
|
+
try {
|
|
519
|
+
const stat = fs.statSync(path.join(runsDir, f));
|
|
520
|
+
return stat.isDirectory();
|
|
521
|
+
}
|
|
522
|
+
catch {
|
|
523
|
+
return false;
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
for (const run of runs) {
|
|
527
|
+
toDelete.push({ path: path.join(runsDir, run), label: `runs/${run}` });
|
|
528
|
+
}
|
|
364
529
|
}
|
|
365
|
-
//
|
|
366
|
-
|
|
367
|
-
const
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
530
|
+
// .vibe/state/ 디렉토리 (항상 삭제)
|
|
531
|
+
if (fs.existsSync(vibeStateDir)) {
|
|
532
|
+
const stateFiles = fs.readdirSync(vibeStateDir);
|
|
533
|
+
for (const file of stateFiles) {
|
|
534
|
+
toDelete.push({ path: path.join(vibeStateDir, file), label: `.vibe/state/${file}` });
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
// .vibe/decisions/는 --keep-context 시 유지
|
|
538
|
+
if (!keepContext && fs.existsSync(vibeDecisionsDir)) {
|
|
539
|
+
const decisionFiles = fs.readdirSync(vibeDecisionsDir);
|
|
540
|
+
for (const file of decisionFiles) {
|
|
541
|
+
toDelete.push({ path: path.join(vibeDecisionsDir, file), label: `.vibe/decisions/${file}` });
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
if (toDelete.length === 0) {
|
|
545
|
+
console.log(` 초기화할 내용이 없습니다.`);
|
|
372
546
|
console.log("");
|
|
373
547
|
return;
|
|
374
548
|
}
|
|
375
|
-
console.log(` ${
|
|
549
|
+
console.log(` ${toDelete.length}개의 항목을 발견했습니다.`);
|
|
550
|
+
if (keepContext) {
|
|
551
|
+
console.log(` ${c("cyan", "--keep-context")}: .vibe/decisions/는 유지됩니다.`);
|
|
552
|
+
}
|
|
376
553
|
console.log("");
|
|
377
|
-
//
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
console.log(` - runs/${run}`);
|
|
554
|
+
// Show what would be deleted
|
|
555
|
+
console.log(` 삭제될 항목:`);
|
|
556
|
+
for (const item of toDelete.slice(0, 8)) {
|
|
557
|
+
console.log(` - ${item.label}`);
|
|
382
558
|
}
|
|
383
|
-
if (
|
|
384
|
-
console.log(` ... 외 ${
|
|
559
|
+
if (toDelete.length > 8) {
|
|
560
|
+
console.log(` ... 외 ${toDelete.length - 8}개`);
|
|
385
561
|
}
|
|
386
562
|
console.log("");
|
|
387
|
-
// Check for --force flag
|
|
388
|
-
const forceFlag = process.argv.includes("--force") || process.argv.includes("-f");
|
|
389
563
|
if (!forceFlag) {
|
|
390
564
|
console.log(` ${c("yellow", "주의:")} 이 작업은 되돌릴 수 없습니다.`);
|
|
391
|
-
|
|
565
|
+
if (keepContext) {
|
|
566
|
+
console.log(` 실제로 삭제하려면 ${c("cyan", "vibe reset --keep-context --force")}를 사용하세요.`);
|
|
567
|
+
}
|
|
568
|
+
else {
|
|
569
|
+
console.log(` 실제로 삭제하려면 ${c("cyan", "vibe reset --force")}를 사용하세요.`);
|
|
570
|
+
}
|
|
392
571
|
console.log("");
|
|
393
572
|
return;
|
|
394
573
|
}
|
|
395
|
-
// Delete
|
|
574
|
+
// Delete items
|
|
396
575
|
console.log(` 삭제 중...`);
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
576
|
+
let deleted = 0;
|
|
577
|
+
for (const item of toDelete) {
|
|
578
|
+
try {
|
|
579
|
+
fs.rmSync(item.path, { recursive: true, force: true });
|
|
580
|
+
console.log(` ${c("red", "✗")} ${item.label}`);
|
|
581
|
+
deleted++;
|
|
582
|
+
}
|
|
583
|
+
catch {
|
|
584
|
+
console.log(` ${c("yellow", "⚠")} ${item.label} (삭제 실패)`);
|
|
585
|
+
}
|
|
401
586
|
}
|
|
402
587
|
console.log("");
|
|
403
|
-
console.log(` ${c("green", "✓")} ${
|
|
588
|
+
console.log(` ${c("green", "✓")} ${deleted}개의 항목이 삭제되었습니다.`);
|
|
589
|
+
if (keepContext) {
|
|
590
|
+
console.log(` ${c("cyan", "ℹ")} .vibe/decisions/는 유지되었습니다.`);
|
|
591
|
+
}
|
|
404
592
|
console.log("");
|
|
405
593
|
}
|
|
406
594
|
async function cmdUpdate() {
|
|
@@ -427,9 +615,325 @@ async function cmdUpdate() {
|
|
|
427
615
|
}
|
|
428
616
|
console.log("");
|
|
429
617
|
}
|
|
618
|
+
// ============================================================
|
|
619
|
+
// New Commands: exec, inspect, run (VRIP v1.1 compliant)
|
|
620
|
+
// ============================================================
|
|
621
|
+
/**
|
|
622
|
+
* vibe exec - execution_engine daemon commands
|
|
623
|
+
*
|
|
624
|
+
* VRIP v1.1: STDOUT = Control JSON only, results in run_dir
|
|
625
|
+
*/
|
|
626
|
+
async function cmdExec() {
|
|
627
|
+
const args = process.argv.slice(3); // after "vibe exec"
|
|
628
|
+
const subcommand = args[0] ?? "help";
|
|
629
|
+
const jsonMode = args.includes("--json");
|
|
630
|
+
const repoRoot = resolveRepoRoot();
|
|
631
|
+
switch (subcommand) {
|
|
632
|
+
case "status": {
|
|
633
|
+
// Query execution_engine status
|
|
634
|
+
console.log("");
|
|
635
|
+
console.log(c("cyan", "Vibe Exec Status"));
|
|
636
|
+
console.log("");
|
|
637
|
+
// Check if daemon is running (via lock file)
|
|
638
|
+
const lockFile = path.join(repoRoot, ".vibe", "exec", "daemon.lock");
|
|
639
|
+
if (fs.existsSync(lockFile)) {
|
|
640
|
+
try {
|
|
641
|
+
const lock = JSON.parse(fs.readFileSync(lockFile, "utf-8"));
|
|
642
|
+
console.log(` Daemon: ${c("green", "RUNNING")}`);
|
|
643
|
+
console.log(` PID: ${lock.pid}`);
|
|
644
|
+
console.log(` Started: ${lock.started_at}`);
|
|
645
|
+
}
|
|
646
|
+
catch {
|
|
647
|
+
console.log(` Daemon: ${c("yellow", "UNKNOWN")} (lock file corrupted)`);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
else {
|
|
651
|
+
console.log(` Daemon: ${c("dim", "NOT RUNNING")}`);
|
|
652
|
+
}
|
|
653
|
+
// Check queue (if exists)
|
|
654
|
+
const queueDb = path.join(repoRoot, ".vibe", "exec", "queue.sqlite");
|
|
655
|
+
if (fs.existsSync(queueDb)) {
|
|
656
|
+
console.log(` Queue: ${c("green", "✓")} ${path.relative(repoRoot, queueDb)}`);
|
|
657
|
+
}
|
|
658
|
+
else {
|
|
659
|
+
console.log(` Queue: ${c("dim", "○")} (not initialized)`);
|
|
660
|
+
}
|
|
661
|
+
// List recent runs
|
|
662
|
+
const runsDir = path.join(repoRoot, ".vibe", "runs");
|
|
663
|
+
if (fs.existsSync(runsDir)) {
|
|
664
|
+
const runs = fs.readdirSync(runsDir)
|
|
665
|
+
.filter(f => fs.statSync(path.join(runsDir, f)).isDirectory())
|
|
666
|
+
.sort()
|
|
667
|
+
.reverse()
|
|
668
|
+
.slice(0, 5);
|
|
669
|
+
if (runs.length > 0) {
|
|
670
|
+
console.log("");
|
|
671
|
+
console.log(" Recent Runs:");
|
|
672
|
+
for (const run of runs) {
|
|
673
|
+
const statusFile = path.join(runsDir, run, "status.json");
|
|
674
|
+
let status = "?";
|
|
675
|
+
if (fs.existsSync(statusFile)) {
|
|
676
|
+
try {
|
|
677
|
+
const s = JSON.parse(fs.readFileSync(statusFile, "utf-8"));
|
|
678
|
+
status = s.state ?? "?";
|
|
679
|
+
}
|
|
680
|
+
catch { /* ignore */ }
|
|
681
|
+
}
|
|
682
|
+
const statusColor = status === "SUCCEEDED" ? "green" : status === "FAILED" ? "red" : "yellow";
|
|
683
|
+
console.log(` ${run} ${c(statusColor, status)}`);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
console.log("");
|
|
688
|
+
// VRIP: JSON output
|
|
689
|
+
if (jsonMode) {
|
|
690
|
+
const output = { ok: true, daemon: fs.existsSync(lockFile), queue: fs.existsSync(queueDb) };
|
|
691
|
+
console.log(JSON.stringify(output));
|
|
692
|
+
}
|
|
693
|
+
break;
|
|
694
|
+
}
|
|
695
|
+
case "enqueue": {
|
|
696
|
+
// Add task to queue
|
|
697
|
+
const taskFile = args[1];
|
|
698
|
+
if (!taskFile) {
|
|
699
|
+
console.log("Usage: vibe exec enqueue <task.json>");
|
|
700
|
+
process.exit(1);
|
|
701
|
+
}
|
|
702
|
+
if (!fs.existsSync(taskFile)) {
|
|
703
|
+
console.log(`Task file not found: ${taskFile}`);
|
|
704
|
+
process.exit(1);
|
|
705
|
+
}
|
|
706
|
+
console.log(`[VIBE] Task enqueued from: ${taskFile}`);
|
|
707
|
+
console.log(" (Note: execution_engine daemon must be running)");
|
|
708
|
+
// VRIP: JSON output
|
|
709
|
+
if (jsonMode) {
|
|
710
|
+
console.log(JSON.stringify({ ok: true, task_file: taskFile }));
|
|
711
|
+
}
|
|
712
|
+
break;
|
|
713
|
+
}
|
|
714
|
+
case "inspect": {
|
|
715
|
+
// View run results
|
|
716
|
+
const runId = args[1];
|
|
717
|
+
if (!runId) {
|
|
718
|
+
console.log("Usage: vibe exec inspect <run_id>");
|
|
719
|
+
process.exit(1);
|
|
720
|
+
}
|
|
721
|
+
const runDir = path.join(repoRoot, ".vibe", "runs", runId);
|
|
722
|
+
if (!fs.existsSync(runDir)) {
|
|
723
|
+
console.log(`Run not found: ${runId}`);
|
|
724
|
+
process.exit(1);
|
|
725
|
+
}
|
|
726
|
+
console.log("");
|
|
727
|
+
console.log(c("cyan", `Run: ${runId}`));
|
|
728
|
+
console.log("");
|
|
729
|
+
// Show meta
|
|
730
|
+
const metaFile = path.join(runDir, "meta.json");
|
|
731
|
+
if (fs.existsSync(metaFile)) {
|
|
732
|
+
const meta = JSON.parse(fs.readFileSync(metaFile, "utf-8"));
|
|
733
|
+
console.log(` Source: ${meta.source}`);
|
|
734
|
+
console.log(` Created: ${meta.created_at}`);
|
|
735
|
+
}
|
|
736
|
+
// Show status
|
|
737
|
+
const statusFile = path.join(runDir, "status.json");
|
|
738
|
+
if (fs.existsSync(statusFile)) {
|
|
739
|
+
const status = JSON.parse(fs.readFileSync(statusFile, "utf-8"));
|
|
740
|
+
const statusColor = status.state === "SUCCEEDED" ? "green" : status.state === "FAILED" ? "red" : "yellow";
|
|
741
|
+
console.log(` State: ${c(statusColor, status.state)}`);
|
|
742
|
+
console.log(` Phase: ${status.phase}`);
|
|
743
|
+
console.log(` Attempt: ${status.attempt}`);
|
|
744
|
+
}
|
|
745
|
+
// Show result
|
|
746
|
+
const resultFile = path.join(runDir, "outputs", "result.json");
|
|
747
|
+
if (fs.existsSync(resultFile)) {
|
|
748
|
+
const result = JSON.parse(fs.readFileSync(resultFile, "utf-8"));
|
|
749
|
+
console.log("");
|
|
750
|
+
console.log(" Result:");
|
|
751
|
+
console.log(` OK: ${result.ok ? c("green", "true") : c("red", "false")}`);
|
|
752
|
+
if (result.summary) {
|
|
753
|
+
console.log(` Summary: ${JSON.stringify(result.summary)}`);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
console.log("");
|
|
757
|
+
// VRIP: JSON output
|
|
758
|
+
if (jsonMode) {
|
|
759
|
+
console.log(JSON.stringify({ ok: true, run_id: runId }));
|
|
760
|
+
}
|
|
761
|
+
break;
|
|
762
|
+
}
|
|
763
|
+
case "--daemon":
|
|
764
|
+
case "--once":
|
|
765
|
+
case "serve":
|
|
766
|
+
case "worker": {
|
|
767
|
+
// Daemon mode - call execution_engine binary
|
|
768
|
+
console.log("");
|
|
769
|
+
console.log(c("cyan", "Starting Execution Engine..."));
|
|
770
|
+
console.log("");
|
|
771
|
+
console.log(" (Note: This requires execution_engine binary)");
|
|
772
|
+
console.log(" Run: vibe update to ensure engines are installed");
|
|
773
|
+
console.log("");
|
|
774
|
+
break;
|
|
775
|
+
}
|
|
776
|
+
case "stop": {
|
|
777
|
+
// Stop daemon
|
|
778
|
+
const lockFile = path.join(repoRoot, ".vibe", "exec", "daemon.lock");
|
|
779
|
+
if (!fs.existsSync(lockFile)) {
|
|
780
|
+
console.log("Daemon is not running.");
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
try {
|
|
784
|
+
const lock = JSON.parse(fs.readFileSync(lockFile, "utf-8"));
|
|
785
|
+
console.log(`Stopping daemon (PID: ${lock.pid})...`);
|
|
786
|
+
// On Unix, we'd send SIGTERM. For now, just remove lock file.
|
|
787
|
+
fs.unlinkSync(lockFile);
|
|
788
|
+
console.log("Lock file removed. Daemon should stop gracefully.");
|
|
789
|
+
}
|
|
790
|
+
catch (e) {
|
|
791
|
+
console.log(`Failed to stop: ${e instanceof Error ? e.message : String(e)}`);
|
|
792
|
+
}
|
|
793
|
+
break;
|
|
794
|
+
}
|
|
795
|
+
case "help":
|
|
796
|
+
default: {
|
|
797
|
+
console.log("");
|
|
798
|
+
console.log("Usage: vibe exec <command>");
|
|
799
|
+
console.log("");
|
|
800
|
+
console.log("Commands:");
|
|
801
|
+
console.log(` ${c("green", "status")} 데몬/큐 상태 확인`);
|
|
802
|
+
console.log(` ${c("green", "enqueue")} 작업 추가`);
|
|
803
|
+
console.log(` ${c("green", "inspect")} 실행 결과 조회`);
|
|
804
|
+
console.log(` ${c("green", "serve")} 데몬 시작 (webhook + worker)`);
|
|
805
|
+
console.log(` ${c("green", "worker")} 워커만 시작`);
|
|
806
|
+
console.log(` ${c("green", "stop")} 데몬 종료`);
|
|
807
|
+
console.log("");
|
|
808
|
+
console.log("Options:");
|
|
809
|
+
console.log(` ${c("dim", "--json")} JSON 출력 (VRIP 모드)`);
|
|
810
|
+
console.log(` ${c("dim", "--daemon")} 데몬 모드로 실행`);
|
|
811
|
+
console.log(` ${c("dim", "--once")} 단발 실행`);
|
|
812
|
+
console.log("");
|
|
813
|
+
break;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* vibe inspect - Code inspection (shorthand for vibe check with enhanced output)
|
|
819
|
+
*
|
|
820
|
+
* VRIP v1.1 compliant: STDOUT = Control JSON, results in run_dir
|
|
821
|
+
*/
|
|
822
|
+
async function cmdInspect() {
|
|
823
|
+
const args = process.argv.slice(3);
|
|
824
|
+
const runId = args[0];
|
|
825
|
+
const jsonMode = args.includes("--json");
|
|
826
|
+
const repoRoot = resolveRepoRoot();
|
|
827
|
+
if (runId) {
|
|
828
|
+
// Inspect specific run
|
|
829
|
+
const runDir = path.join(repoRoot, ".vibe", "runs", runId);
|
|
830
|
+
if (!fs.existsSync(runDir)) {
|
|
831
|
+
if (jsonMode) {
|
|
832
|
+
console.log(JSON.stringify({ ok: false, error: "run_not_found", run_id: runId }));
|
|
833
|
+
}
|
|
834
|
+
else {
|
|
835
|
+
console.log(`Run not found: ${runId}`);
|
|
836
|
+
}
|
|
837
|
+
process.exit(1);
|
|
838
|
+
}
|
|
839
|
+
// Read and display result
|
|
840
|
+
const resultFile = path.join(runDir, "outputs", "result.json");
|
|
841
|
+
if (fs.existsSync(resultFile)) {
|
|
842
|
+
const result = JSON.parse(fs.readFileSync(resultFile, "utf-8"));
|
|
843
|
+
if (jsonMode) {
|
|
844
|
+
// VRIP: Control JSON only
|
|
845
|
+
console.log(JSON.stringify({ ok: result.ok, run_id: runId }));
|
|
846
|
+
}
|
|
847
|
+
else {
|
|
848
|
+
console.log("");
|
|
849
|
+
console.log(c("cyan", `Inspect: ${runId}`));
|
|
850
|
+
console.log("");
|
|
851
|
+
console.log(` Result: ${result.ok ? c("green", "✓ PASS") : c("red", "✗ FAIL")}`);
|
|
852
|
+
if (result.summary) {
|
|
853
|
+
console.log(` Issues: ${result.summary.issues_found ?? 0}`);
|
|
854
|
+
console.log(` Duration: ${result.summary.duration_ms ?? 0}ms`);
|
|
855
|
+
}
|
|
856
|
+
console.log("");
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
else {
|
|
860
|
+
if (jsonMode) {
|
|
861
|
+
console.log(JSON.stringify({ ok: false, error: "no_result", run_id: runId }));
|
|
862
|
+
}
|
|
863
|
+
else {
|
|
864
|
+
console.log("No result file found for this run.");
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
else {
|
|
869
|
+
// Run new inspection (delegate to vibe check)
|
|
870
|
+
const paths = getVibeRepoPaths(repoRoot);
|
|
871
|
+
if (!fs.existsSync(paths.validateScript)) {
|
|
872
|
+
if (jsonMode) {
|
|
873
|
+
console.log(JSON.stringify({ ok: false, error: "not_initialized" }));
|
|
874
|
+
}
|
|
875
|
+
else {
|
|
876
|
+
console.log(`Missing local guard: ${path.relative(repoRoot, paths.validateScript)}`);
|
|
877
|
+
console.log(`Run: ${c("cyan", "vibe setup")}`);
|
|
878
|
+
}
|
|
879
|
+
process.exit(1);
|
|
880
|
+
}
|
|
881
|
+
if (!jsonMode) {
|
|
882
|
+
console.log("");
|
|
883
|
+
console.log(c("cyan", "Running inspection..."));
|
|
884
|
+
console.log("");
|
|
885
|
+
}
|
|
886
|
+
const result = spawnBashScriptInRepoSync(repoRoot, paths.validateScript, [], { stdio: jsonMode ? "pipe" : "inherit" });
|
|
887
|
+
if (jsonMode) {
|
|
888
|
+
// Generate a run_id for VRIP compliance
|
|
889
|
+
const runId = `${new Date().toISOString().replace(/[-:T.]/g, "").slice(0, 15)}Z_inspect`;
|
|
890
|
+
console.log(JSON.stringify({
|
|
891
|
+
ok: result?.status === 0,
|
|
892
|
+
run_id: runId,
|
|
893
|
+
error: result?.status !== 0 ? "validation_failed" : undefined
|
|
894
|
+
}));
|
|
895
|
+
}
|
|
896
|
+
process.exit(result?.status ?? 1);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* vibe run - Run application (placeholder for future implementation)
|
|
901
|
+
*/
|
|
902
|
+
async function cmdRun() {
|
|
903
|
+
const jsonMode = process.argv.includes("--json");
|
|
904
|
+
console.log("");
|
|
905
|
+
console.log(c("cyan", "Vibe Run"));
|
|
906
|
+
console.log("");
|
|
907
|
+
console.log(" This command will run the application based on project config.");
|
|
908
|
+
console.log("");
|
|
909
|
+
console.log(" (Not yet implemented - use MCP tool vibe_pm.run_app)");
|
|
910
|
+
console.log("");
|
|
911
|
+
if (jsonMode) {
|
|
912
|
+
console.log(JSON.stringify({ ok: false, error: "not_implemented" }));
|
|
913
|
+
}
|
|
914
|
+
}
|
|
430
915
|
// P0-1: Subcommand-specific help messages
|
|
431
916
|
const SUBCOMMAND_HELP = {
|
|
432
|
-
|
|
917
|
+
setup: `vibe setup - 통합 설치 (권장)
|
|
918
|
+
|
|
919
|
+
Usage: vibe setup [options]
|
|
920
|
+
|
|
921
|
+
Options:
|
|
922
|
+
-f, --force 기존 파일 덮어쓰기
|
|
923
|
+
--local-only 로컬 파일만 생성 (엔진 스킵)
|
|
924
|
+
--check 설치 상태 확인만
|
|
925
|
+
--no-hooks git hooks 스킵
|
|
926
|
+
--ci CI 워크플로우 설치
|
|
927
|
+
--strict CI strict 모드
|
|
928
|
+
|
|
929
|
+
Examples:
|
|
930
|
+
$ vibe setup # 전체 설치
|
|
931
|
+
$ vibe setup --check # 상태 확인
|
|
932
|
+
$ vibe setup --local-only # 오프라인
|
|
933
|
+
`,
|
|
934
|
+
init: `vibe init - [DEPRECATED] 로컬 모드 초기화
|
|
935
|
+
|
|
936
|
+
\u26A0 deprecated: 'vibe setup' 사용 권장
|
|
433
937
|
|
|
434
938
|
Usage: vibe init [options]
|
|
435
939
|
|
|
@@ -500,9 +1004,60 @@ Usage: vibe update
|
|
|
500
1004
|
Usage: vibe reset [options]
|
|
501
1005
|
|
|
502
1006
|
Options:
|
|
503
|
-
-f, --force
|
|
1007
|
+
-f, --force 실제로 삭제 실행
|
|
1008
|
+
--keep-context .vibe/decisions/ 유지 (결정 기록 보존)
|
|
1009
|
+
|
|
1010
|
+
삭제 대상:
|
|
1011
|
+
- runs/ 실행 기록
|
|
1012
|
+
- .vibe/state/ 현재 작업 상태
|
|
1013
|
+
|
|
1014
|
+
--keep-context 사용 시 유지:
|
|
1015
|
+
- .vibe/decisions/ 결정 기록 (컨텍스트 보존)
|
|
504
1016
|
|
|
505
|
-
|
|
1017
|
+
Examples:
|
|
1018
|
+
$ vibe reset --force # 전체 초기화
|
|
1019
|
+
$ vibe reset --keep-context -f # 결정 기록 유지하며 초기화
|
|
1020
|
+
`,
|
|
1021
|
+
exec: `vibe exec - Execution Engine 데몬 관리
|
|
1022
|
+
|
|
1023
|
+
Usage: vibe exec <command> [options]
|
|
1024
|
+
|
|
1025
|
+
Commands:
|
|
1026
|
+
status 데몬/큐 상태 확인
|
|
1027
|
+
enqueue 작업 추가 (예: vibe exec enqueue task.json)
|
|
1028
|
+
inspect 실행 결과 조회 (예: vibe exec inspect <run_id>)
|
|
1029
|
+
serve 데몬 시작 (webhook + worker)
|
|
1030
|
+
worker 워커만 시작
|
|
1031
|
+
stop 데몬 종료
|
|
1032
|
+
|
|
1033
|
+
Options:
|
|
1034
|
+
--json JSON 출력 (VRIP 모드)
|
|
1035
|
+
--daemon 데몬 모드로 실행
|
|
1036
|
+
--once 단발 실행
|
|
1037
|
+
|
|
1038
|
+
Examples:
|
|
1039
|
+
$ vibe exec status # 상태 확인
|
|
1040
|
+
$ vibe exec status --json # JSON 출력
|
|
1041
|
+
$ vibe exec inspect abc123 # 특정 run 조회
|
|
1042
|
+
`,
|
|
1043
|
+
inspect: `vibe inspect - 코드 검수
|
|
1044
|
+
|
|
1045
|
+
Usage: vibe inspect [run_id] [options]
|
|
1046
|
+
|
|
1047
|
+
Options:
|
|
1048
|
+
--json JSON 출력 (VRIP 모드)
|
|
1049
|
+
|
|
1050
|
+
Examples:
|
|
1051
|
+
$ vibe inspect # 새 검수 실행
|
|
1052
|
+
$ vibe inspect abc123 # 특정 run 결과 조회
|
|
1053
|
+
$ vibe inspect --json # VRIP 모드 (MCP용)
|
|
1054
|
+
`,
|
|
1055
|
+
run: `vibe run - 앱 실행
|
|
1056
|
+
|
|
1057
|
+
Usage: vibe run [options]
|
|
1058
|
+
|
|
1059
|
+
프로젝트 설정에 따라 앱을 실행합니다.
|
|
1060
|
+
(현재 개발 중 - vibe_pm.run_app MCP 도구 사용)
|
|
506
1061
|
`,
|
|
507
1062
|
};
|
|
508
1063
|
// P0-1: Helper functions for --help priority handling
|
|
@@ -524,10 +1079,14 @@ function cmdHelp(subcommand) {
|
|
|
524
1079
|
console.log("Usage: vibe <command> [options]");
|
|
525
1080
|
console.log("");
|
|
526
1081
|
console.log("Commands:");
|
|
527
|
-
console.log(` ${c("green", "
|
|
1082
|
+
console.log(` ${c("green", "setup")} 통합 설치 (권장)`);
|
|
1083
|
+
console.log(` ${c("dim", "init")} [deprecated] 로컬 초기화`);
|
|
528
1084
|
console.log(` ${c("green", "status")} 로컬 상태 확인`);
|
|
529
1085
|
console.log(` ${c("green", "ticket")} 작업 티켓(Work Order) 생성`);
|
|
530
1086
|
console.log(` ${c("green", "check")} 로컬 가드 검증 실행`);
|
|
1087
|
+
console.log(` ${c("green", "inspect")} 코드 검수 (VRIP 모드)`);
|
|
1088
|
+
console.log(` ${c("green", "exec")} Execution Engine 관리`);
|
|
1089
|
+
console.log(` ${c("green", "run")} 앱 실행`);
|
|
531
1090
|
console.log(` ${c("green", "done")} 작업 티켓 종료/아카이브`);
|
|
532
1091
|
console.log(` ${c("green", "config")} 설정 관리 (show/validate)`);
|
|
533
1092
|
console.log(` ${c("green", "doctor")} 설치 상태 확인 및 진단`);
|
|
@@ -542,12 +1101,14 @@ function cmdHelp(subcommand) {
|
|
|
542
1101
|
console.log(` ${c("dim", "-h, --help")} 명령어별 도움말 표시`);
|
|
543
1102
|
console.log("");
|
|
544
1103
|
console.log("Examples:");
|
|
545
|
-
console.log(` ${c("dim", "$")} vibe
|
|
546
|
-
console.log(` ${c("dim", "$")} vibe
|
|
1104
|
+
console.log(` ${c("dim", "$")} vibe setup # 통합 설치 (권장)`);
|
|
1105
|
+
console.log(` ${c("dim", "$")} vibe setup --check # 설치 상태 확인`);
|
|
547
1106
|
console.log(` ${c("dim", "$")} vibe ticket \"Fix auth\" # 작업 티켓 생성`);
|
|
548
1107
|
console.log(` ${c("dim", "$")} vibe check # 푸시 전 로컬 검증`);
|
|
549
|
-
console.log(` ${c("dim", "$")} vibe
|
|
550
|
-
console.log(` ${c("dim", "$")} vibe
|
|
1108
|
+
console.log(` ${c("dim", "$")} vibe inspect --json # VRIP 모드 검수`);
|
|
1109
|
+
console.log(` ${c("dim", "$")} vibe exec status # 데몬 상태 확인`);
|
|
1110
|
+
console.log(` ${c("dim", "$")} vibe doctor # 설치 진단`);
|
|
1111
|
+
console.log(` ${c("dim", "$")} vibe reset --force # 프로젝트 초기화`);
|
|
551
1112
|
console.log("");
|
|
552
1113
|
console.log("More info:");
|
|
553
1114
|
console.log(` Documentation: ${c("blue", "https://vibecode.town/docs")}`);
|
|
@@ -576,6 +1137,9 @@ async function main() {
|
|
|
576
1137
|
});
|
|
577
1138
|
try {
|
|
578
1139
|
switch (command) {
|
|
1140
|
+
case "setup":
|
|
1141
|
+
await cmdSetup();
|
|
1142
|
+
break;
|
|
579
1143
|
case "init":
|
|
580
1144
|
cmdInit();
|
|
581
1145
|
break;
|
|
@@ -611,6 +1175,15 @@ async function main() {
|
|
|
611
1175
|
case "update":
|
|
612
1176
|
await cmdUpdate();
|
|
613
1177
|
break;
|
|
1178
|
+
case "exec":
|
|
1179
|
+
await cmdExec();
|
|
1180
|
+
break;
|
|
1181
|
+
case "inspect":
|
|
1182
|
+
await cmdInspect();
|
|
1183
|
+
break;
|
|
1184
|
+
case "run":
|
|
1185
|
+
await cmdRun();
|
|
1186
|
+
break;
|
|
614
1187
|
case "help":
|
|
615
1188
|
case "-h":
|
|
616
1189
|
case "--help":
|