@vibecodetown/mcp-server 2.2.0 → 2.2.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.
Files changed (70) hide show
  1. package/README.md +10 -10
  2. package/build/auth/index.js +0 -2
  3. package/build/auth/public_key.js +6 -4
  4. package/build/bootstrap/doctor.js +113 -5
  5. package/build/bootstrap/installer.js +85 -15
  6. package/build/bootstrap/registry.js +11 -6
  7. package/build/bootstrap/skills-installer.js +365 -0
  8. package/build/dx/activity.js +26 -3
  9. package/build/engine.js +151 -0
  10. package/build/errors.js +107 -0
  11. package/build/generated/bridge_build_seed_input.js +2 -0
  12. package/build/generated/bridge_build_seed_output.js +2 -0
  13. package/build/generated/bridge_confirm_reference_input.js +2 -0
  14. package/build/generated/bridge_confirm_reference_output.js +2 -0
  15. package/build/generated/bridge_confirmed_reference_file.js +2 -0
  16. package/build/generated/bridge_generate_references_input.js +2 -0
  17. package/build/generated/bridge_generate_references_output.js +2 -0
  18. package/build/generated/bridge_references_file.js +2 -0
  19. package/build/generated/bridge_work_order_seed_file.js +2 -0
  20. package/build/generated/contracts_bundle_info.js +3 -3
  21. package/build/generated/index.js +14 -0
  22. package/build/generated/ingress_input.js +2 -0
  23. package/build/generated/ingress_output.js +2 -0
  24. package/build/generated/ingress_resolution_file.js +2 -0
  25. package/build/generated/ingress_summary_file.js +2 -0
  26. package/build/generated/message_template_id_mapping_file.js +2 -0
  27. package/build/generated/run_app_input.js +1 -1
  28. package/build/index.js +4 -3
  29. package/build/local-mode/paths.js +1 -0
  30. package/build/local-mode/setup.js +21 -1
  31. package/build/path-utils.js +68 -0
  32. package/build/runtime/cli_invoker.js +1 -1
  33. package/build/tools/vibe_pm/advisory_review.js +5 -3
  34. package/build/tools/vibe_pm/bridge_build_seed.js +164 -0
  35. package/build/tools/vibe_pm/bridge_confirm_reference.js +91 -0
  36. package/build/tools/vibe_pm/bridge_generate_references.js +258 -0
  37. package/build/tools/vibe_pm/briefing.js +27 -3
  38. package/build/tools/vibe_pm/context.js +79 -0
  39. package/build/tools/vibe_pm/create_work_order.js +200 -3
  40. package/build/tools/vibe_pm/doctor.js +95 -0
  41. package/build/tools/vibe_pm/entity_gate/preflight.js +8 -3
  42. package/build/tools/vibe_pm/export_output.js +14 -13
  43. package/build/tools/vibe_pm/finalize_work.js +78 -40
  44. package/build/tools/vibe_pm/get_decision.js +2 -2
  45. package/build/tools/vibe_pm/index.js +128 -42
  46. package/build/tools/vibe_pm/ingress.js +645 -0
  47. package/build/tools/vibe_pm/ingress_gate.js +116 -0
  48. package/build/tools/vibe_pm/inspect_code.js +90 -20
  49. package/build/tools/vibe_pm/kce/doc_usage.js +4 -9
  50. package/build/tools/vibe_pm/kce/on_finalize.js +2 -2
  51. package/build/tools/vibe_pm/kce/preflight.js +11 -7
  52. package/build/tools/vibe_pm/memory_status.js +11 -8
  53. package/build/tools/vibe_pm/memory_sync.js +11 -8
  54. package/build/tools/vibe_pm/pm_language.js +17 -16
  55. package/build/tools/vibe_pm/python_error.js +115 -0
  56. package/build/tools/vibe_pm/run_app.js +169 -43
  57. package/build/tools/vibe_pm/run_app_podman.js +64 -2
  58. package/build/tools/vibe_pm/search_oss.js +5 -3
  59. package/build/tools/vibe_pm/spec_rag.js +185 -0
  60. package/build/tools/vibe_pm/status.js +50 -3
  61. package/build/tools/vibe_pm/submit_decision.js +2 -2
  62. package/build/tools/vibe_pm/types.js +28 -0
  63. package/build/tools/vibe_pm/undo_last_task.js +9 -2
  64. package/build/tools/vibe_pm/waiter_mapping.js +155 -0
  65. package/build/tools/vibe_pm/zoekt_evidence.js +5 -3
  66. package/build/tools.js +13 -5
  67. package/build/vibe-cli.js +245 -7
  68. package/package.json +5 -4
  69. package/skills/VRIP_INSTALL_MANIFEST_DOCTOR.skill.md +288 -0
  70. package/skills/index.json +14 -0
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # @vibecodetown/mcp-server
1
+ # @vibecode/mcp-server
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/@vibecodetown/mcp-server.svg)](https://www.npmjs.com/package/@vibecodetown/mcp-server)
3
+ [![npm version](https://img.shields.io/npm/v/@vibecode/mcp-server.svg)](https://www.npmjs.com/package/@vibecode/mcp-server)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
  [![Tests](https://img.shields.io/badge/tests-140%20passing-brightgreen.svg)](#testing)
6
6
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.0-blue.svg)](https://www.typescriptlang.org/)
@@ -28,13 +28,13 @@ curl -fsSL https://vibecode.town/install.sh | sh
28
28
  ### Manual Install
29
29
 
30
30
  ```bash
31
- npm install -g @vibecodetown/mcp-server
31
+ npm install -g @vibecode/mcp-server
32
32
  ```
33
33
 
34
34
  ### Add to Claude Code
35
35
 
36
36
  ```bash
37
- claude mcp add vibecode npx @vibecodetown/mcp-server
37
+ claude mcp add vibe-pm -- npx @vibecode/mcp-server
38
38
  ```
39
39
 
40
40
  ### Add to Cursor
@@ -118,8 +118,8 @@ On first tool call, the server automatically:
118
118
 
119
119
  ```bash
120
120
  # Clone and install
121
- git clone https://github.com/vibecode/vibecoding-helper
122
- cd vibecoding-helper/adapters/mcp-ts
121
+ git clone https://github.com/yellowhama/vibe-pm
122
+ cd vibe-pm/adapters/mcp-ts
123
123
  npm install
124
124
 
125
125
  # Build
@@ -176,7 +176,7 @@ tests/
176
176
  "mcpServers": {
177
177
  "vibecode": {
178
178
  "command": "npx",
179
- "args": ["@vibecodetown/mcp-server"]
179
+ "args": ["@vibecode/mcp-server"]
180
180
  }
181
181
  }
182
182
  }
@@ -249,7 +249,7 @@ User (natural language)
249
249
 
250
250
  AI Agent (Claude/Cursor/Codex)
251
251
  ↓ AGENTS.md rules
252
- MCP Server (@vibecodetown/mcp-server)
252
+ MCP Server (@vibecode/mcp-server)
253
253
  ↓ vibe_pm.* tools
254
254
  Bootstrap Layer
255
255
  ↓ auto-download if missing
@@ -265,5 +265,5 @@ MIT
265
265
  ## Links
266
266
 
267
267
  - [Documentation](https://vibecode.town/docs)
268
- - [GitHub](https://github.com/vibecode/vibecoding-helper)
269
- - [Issues](https://github.com/vibecode/vibecoding-helper/issues)
268
+ - [GitHub](https://github.com/yellowhama/vibe-pm)
269
+ - [Issues](https://github.com/yellowhama/vibe-pm/issues)
@@ -5,13 +5,11 @@
5
5
  * - Token caching and persistence
6
6
  * - Offline JWT verification
7
7
  * - Feature gating based on entitlements
8
- * - Credential storage (GitHub/NPM tokens)
9
8
  */
10
9
  export { TokenCache } from './token_cache.js';
11
10
  export { TokenVerifier } from './token_verifier.js';
12
11
  export { AuthGate } from './gate.js';
13
12
  export { PUBLIC_KEY } from './public_key.js';
14
- export { CredentialStore, getCredentialStore } from './credential_store.js';
15
13
  // Re-export convenience functions
16
14
  import { TokenCache } from './token_cache.js';
17
15
  import { TokenVerifier } from './token_verifier.js';
@@ -12,11 +12,13 @@
12
12
  * 3. Release new client version
13
13
  */
14
14
  const ENV_PUBLIC_KEY = (process.env.VIBECODE_AUTH_PUBLIC_KEY || process.env.VIBE_AUTH_PUBLIC_KEY || "").trim();
15
- export const PUBLIC_KEY = ENV_PUBLIC_KEY ||
16
- `-----BEGIN PUBLIC KEY-----
17
- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
18
- REPLACE_WITH_YOUR_PRODUCTION_PUBLIC_KEY
15
+ // Development key - REPLACE WITH PRODUCTION KEY FOR RELEASE
16
+ // Generate with: openssl ecparam -name prime256v1 -genkey -noout | openssl ec -pubout
17
+ const DEV_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
18
+ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEE85F1OitXNxlIXw3/YD4EiBGmlTe
19
+ yLIZWPhyHfOxe+01IeO/iYP9lbKXMDd0u8gN8KRfBwHel1lMddRA2Hnm9Q==
19
20
  -----END PUBLIC KEY-----`;
21
+ export const PUBLIC_KEY = ENV_PUBLIC_KEY || DEV_PUBLIC_KEY;
20
22
  /**
21
23
  * JWT issuer for verification
22
24
  */
@@ -1,7 +1,68 @@
1
1
  // adapters/mcp-ts/src/bootstrap/doctor.ts
2
2
  // Health check tool for installation status
3
3
  import { ensureEngines, getEngineHealth, checkUpdates, validateCache } from "./installer.js";
4
- import { ENGINE_SPECS } from "./registry.js";
4
+ import { ENGINE_SPECS, SKILL_SPEC } from "./registry.js";
5
+ import { getSkillsHealth } from "./skills-installer.js";
6
+ /**
7
+ * Build skill gates from skills health status
8
+ */
9
+ function buildSkillGates(skillsHealth) {
10
+ const gates = [];
11
+ // Check if bundle is installed
12
+ if (!skillsHealth.installed) {
13
+ gates.push({
14
+ code: "SKILL_BUNDLE_MISSING",
15
+ severity: "FAIL",
16
+ message: "Skill bundle not found in package"
17
+ });
18
+ return gates;
19
+ }
20
+ // Check if index is valid
21
+ if (!skillsHealth.version) {
22
+ gates.push({
23
+ code: "SKILL_INDEX_INVALID",
24
+ severity: "FAIL",
25
+ message: "Skills index.json not found or invalid"
26
+ });
27
+ return gates;
28
+ }
29
+ // Check version mismatch
30
+ if (skillsHealth.version !== SKILL_SPEC.version) {
31
+ gates.push({
32
+ code: "SKILL_VERSION_MISMATCH",
33
+ severity: "WARN",
34
+ message: `Version mismatch: expected ${SKILL_SPEC.version}, got ${skillsHealth.version}`
35
+ });
36
+ }
37
+ // Check each skill
38
+ for (const skill of skillsHealth.skills) {
39
+ if (skill.status === "missing") {
40
+ gates.push({
41
+ code: "SKILL_BUNDLE_CORRUPTED",
42
+ severity: "FAIL",
43
+ message: skill.error ?? `Skill file missing: ${skill.id}`,
44
+ skillId: skill.id
45
+ });
46
+ }
47
+ else if (skill.status === "sha_mismatch") {
48
+ gates.push({
49
+ code: "SKILL_SHA_MISMATCH",
50
+ severity: "FAIL",
51
+ message: skill.error ?? `SHA256 mismatch for skill: ${skill.id}`,
52
+ skillId: skill.id
53
+ });
54
+ }
55
+ else if (skill.status === "ok") {
56
+ gates.push({
57
+ code: "SKILL_OK",
58
+ severity: "INFO",
59
+ message: `Skill OK: ${skill.id}`,
60
+ skillId: skill.id
61
+ });
62
+ }
63
+ }
64
+ return gates;
65
+ }
5
66
  /**
6
67
  * Check engine installation status and return detailed health info
7
68
  * Triggers installation if engines are missing or outdated
@@ -42,9 +103,22 @@ export async function doctor() {
42
103
  missing: healthAfter.filter((h) => h.status === "missing").length,
43
104
  corrupted: healthAfter.filter((h) => h.status === "corrupted").length
44
105
  };
106
+ // Check skills health
107
+ const skillsHealth = getSkillsHealth();
108
+ const skillGates = buildSkillGates(skillsHealth);
109
+ // Determine skill status
110
+ let skillStatus = "OK";
111
+ const hasSkillFail = skillGates.some(g => g.severity === "FAIL");
112
+ const hasSkillWarn = skillGates.some(g => g.severity === "WARN");
113
+ if (hasSkillFail) {
114
+ skillStatus = "ERROR";
115
+ }
116
+ else if (hasSkillWarn) {
117
+ skillStatus = "WARN";
118
+ }
45
119
  // Determine overall status
46
120
  let status = "OK";
47
- if (summaryAfter.missing > 0 || summaryAfter.corrupted > 0) {
121
+ if (summaryAfter.missing > 0 || summaryAfter.corrupted > 0 || hasSkillFail) {
48
122
  status = "ERROR";
49
123
  }
50
124
  else if (summaryAfter.needsUpdate > 0) {
@@ -53,6 +127,13 @@ export async function doctor() {
53
127
  return {
54
128
  status,
55
129
  engines,
130
+ skills: {
131
+ status: skillStatus,
132
+ version: skillsHealth.version,
133
+ bundlePath: skillsHealth.bundlePath,
134
+ gates: skillGates,
135
+ summary: skillsHealth.summary
136
+ },
56
137
  summary: summaryAfter
57
138
  };
58
139
  }
@@ -60,6 +141,17 @@ export async function doctor() {
60
141
  return {
61
142
  status: "ERROR",
62
143
  engines: {},
144
+ skills: {
145
+ status: "ERROR",
146
+ version: null,
147
+ bundlePath: null,
148
+ gates: [{
149
+ code: "SKILL_BUNDLE_MISSING",
150
+ severity: "FAIL",
151
+ message: "Failed to check skills health"
152
+ }],
153
+ summary: { total: 0, ok: 0, missing: 0, corrupted: 0 }
154
+ },
63
155
  summary: {
64
156
  total: Object.keys(ENGINE_SPECS).length,
65
157
  ok: 0,
@@ -76,10 +168,26 @@ export async function doctor() {
76
168
  */
77
169
  export async function healthCheck() {
78
170
  const health = await getEngineHealth();
79
- const allOk = health.every((h) => h.status === "ok");
171
+ const skillsHealth = getSkillsHealth();
172
+ const engineOk = health.every((h) => h.status === "ok");
173
+ const skillsOk = skillsHealth.installed &&
174
+ skillsHealth.summary.missing === 0 &&
175
+ skillsHealth.summary.corrupted === 0;
176
+ let skillStatus = "OK";
177
+ if (!skillsHealth.installed || skillsHealth.summary.missing > 0 || skillsHealth.summary.corrupted > 0) {
178
+ skillStatus = "ERROR";
179
+ }
180
+ else if (skillsHealth.version !== SKILL_SPEC.version) {
181
+ skillStatus = "WARN";
182
+ }
80
183
  return {
81
- status: allOk ? "OK" : "NEEDS_ATTENTION",
82
- engines: health
184
+ status: (engineOk && skillsOk) ? "OK" : "NEEDS_ATTENTION",
185
+ engines: health,
186
+ skills: {
187
+ status: skillStatus,
188
+ version: skillsHealth.version,
189
+ summary: skillsHealth.summary
190
+ }
83
191
  };
84
192
  }
85
193
  /**
@@ -10,13 +10,25 @@ import { promisify } from "node:util";
10
10
  import { ENGINE_SPECS } from "./registry.js";
11
11
  import { detectPlatform, exeName } from "./platform.js";
12
12
  import { withLock } from "./lock.js";
13
+ import { isDownloadTimeoutError, isDownload404Error, isDownloadFailedError, } from "../errors.js";
13
14
  const execFileAsync = promisify(execFile);
14
15
  // ============================================================
15
- // Configuration
16
+ // Configuration (P2-26: Environment variable support)
16
17
  // ============================================================
17
- const MAX_RETRIES = 3;
18
- const RETRY_DELAY_MS = 1000;
19
- const DOWNLOAD_TIMEOUT_MS = 60_000;
18
+ function getEnvInt(name, defaultValue) {
19
+ const value = process.env[name];
20
+ if (!value)
21
+ return defaultValue;
22
+ const parsed = parseInt(value, 10);
23
+ return Number.isNaN(parsed) ? defaultValue : parsed;
24
+ }
25
+ // Configurable via environment variables:
26
+ // VIBECODE_MAX_RETRIES - Maximum download retry attempts (default: 3)
27
+ // VIBECODE_RETRY_DELAY_MS - Delay between retries in ms (default: 1000)
28
+ // VIBECODE_DOWNLOAD_TIMEOUT_MS - Download timeout in ms (default: 60000)
29
+ const MAX_RETRIES = getEnvInt("VIBECODE_MAX_RETRIES", 3);
30
+ const RETRY_DELAY_MS = getEnvInt("VIBECODE_RETRY_DELAY_MS", 1000);
31
+ const DOWNLOAD_TIMEOUT_MS = getEnvInt("VIBECODE_DOWNLOAD_TIMEOUT_MS", 60_000);
20
32
  function isTruthyEnv(value) {
21
33
  if (!value)
22
34
  return false;
@@ -29,6 +41,19 @@ function isOfflineMode() {
29
41
  function isDebugMode() {
30
42
  return isTruthyEnv(process.env.VIBECODE_DEBUG);
31
43
  }
44
+ function getGitHubToken() {
45
+ return process.env.GITHUB_TOKEN?.trim() || process.env.GH_TOKEN?.trim() || null;
46
+ }
47
+ function getAuthHeaders() {
48
+ const token = getGitHubToken();
49
+ const headers = {
50
+ "User-Agent": "vibecode-installer",
51
+ };
52
+ if (token) {
53
+ headers["Authorization"] = `token ${token}`;
54
+ }
55
+ return headers;
56
+ }
32
57
  // ============================================================
33
58
  // Cache Management
34
59
  // ============================================================
@@ -162,11 +187,12 @@ function makeBootstrapError(userMessage, internal) {
162
187
  }
163
188
  function userMessageForBootstrapFailure(err) {
164
189
  const msg = err instanceof Error ? err.message : String(err);
165
- if (msg.includes("download_timeout") || msg.includes("AbortError")) {
190
+ // P1-4: Use centralized error patterns for consistent detection
191
+ if (isDownloadTimeoutError(msg)) {
166
192
  return "다운로드 시간이 초과되었습니다. 인터넷 연결/방화벽 설정을 확인한 뒤 다시 시도해주세요.";
167
193
  }
168
194
  // P0-2: Better 404 error handling with recovery options
169
- if (msg.includes("404") || msg.includes("no_matching_asset") || msg.includes("download_failed:404")) {
195
+ if (isDownload404Error(msg)) {
170
196
  return `릴리스를 찾지 못했습니다.
171
197
 
172
198
  해결 방법:
@@ -174,7 +200,7 @@ function userMessageForBootstrapFailure(err) {
174
200
  2. VIBECODE_OFFLINE=1로 업데이트 스킵
175
201
  3. VIBECODE_DEBUG=1로 상세 로그 확인`;
176
202
  }
177
- if (msg.startsWith("download_failed") || msg.includes("fetch")) {
203
+ if (isDownloadFailedError(msg)) {
178
204
  return "다운로드에 실패했습니다. 인터넷 연결/방화벽 설정을 확인한 뒤 다시 시도해주세요.";
179
205
  }
180
206
  if (msg.startsWith("sha_mismatch")) {
@@ -206,16 +232,17 @@ function sha256File(filePath) {
206
232
  async function sleep(ms) {
207
233
  return new Promise((resolve) => setTimeout(resolve, ms));
208
234
  }
209
- async function fetchWithRetry(url, retries = MAX_RETRIES) {
235
+ async function fetchWithRetry(url, retries = MAX_RETRIES, extraHeaders) {
210
236
  if (isOfflineMode()) {
211
237
  throw makeBootstrapError("오프라인 모드에서는 다운로드를 진행할 수 없습니다. 인터넷을 연결하거나, 사전 설치된 도구 경로(VIBECODE_BIN_PATH)를 지정해주세요.");
212
238
  }
213
239
  let lastError = null;
240
+ const headers = { ...getAuthHeaders(), ...extraHeaders };
214
241
  for (let attempt = 1; attempt <= retries; attempt++) {
215
242
  try {
216
243
  const controller = new AbortController();
217
244
  const timeoutId = setTimeout(() => controller.abort(), DOWNLOAD_TIMEOUT_MS);
218
- const res = await fetch(url, { signal: controller.signal });
245
+ const res = await fetch(url, { signal: controller.signal, headers });
219
246
  clearTimeout(timeoutId);
220
247
  if (res.ok) {
221
248
  return res;
@@ -276,8 +303,8 @@ async function discoverRelease(repo, version) {
276
303
  const res = await fetch(`${apiBase}/tags/${encodeURIComponent(tag)}`, {
277
304
  signal: controller.signal,
278
305
  headers: {
306
+ ...getAuthHeaders(),
279
307
  "Accept": "application/vnd.github+json",
280
- "User-Agent": "vibecode-installer",
281
308
  },
282
309
  });
283
310
  clearTimeout(timeoutId);
@@ -300,8 +327,8 @@ async function discoverRelease(repo, version) {
300
327
  const res = await fetch(`${apiBase}/latest`, {
301
328
  signal: controller.signal,
302
329
  headers: {
330
+ ...getAuthHeaders(),
303
331
  "Accept": "application/vnd.github+json",
304
- "User-Agent": "vibecode-installer",
305
332
  },
306
333
  });
307
334
  clearTimeout(timeoutId);
@@ -321,6 +348,24 @@ async function discoverRelease(repo, version) {
321
348
  function findAsset(release, name) {
322
349
  return release.assets.find(a => a.name === name);
323
350
  }
351
+ /**
352
+ * Download asset from GitHub release (supports private repos with GITHUB_TOKEN)
353
+ */
354
+ async function downloadAsset(asset, outPath) {
355
+ const token = getGitHubToken();
356
+ // For private repos, use API URL with Accept: application/octet-stream
357
+ if (token) {
358
+ const res = await fetchWithRetry(asset.url, MAX_RETRIES, {
359
+ "Accept": "application/octet-stream",
360
+ });
361
+ await fs.promises.mkdir(path.dirname(outPath), { recursive: true });
362
+ const buf = Buffer.from(await res.arrayBuffer());
363
+ await fs.promises.writeFile(outPath, buf);
364
+ return;
365
+ }
366
+ // For public repos, use browser_download_url
367
+ await download(asset.browser_download_url, outPath);
368
+ }
324
369
  async function readShaSumsFromUrl(url) {
325
370
  const res = await fetchWithRetry(url);
326
371
  const text = await res.text();
@@ -533,15 +578,35 @@ async function ensureOne(name, platform) {
533
578
  // P0-2: Try GitHub API discovery first for better 404 tolerance
534
579
  let assetUrl;
535
580
  let shaMap;
581
+ let assetInfo;
536
582
  const release = await discoverRelease(spec.repo, spec.version);
537
583
  if (release) {
538
- const assetInfo = findAsset(release, asset);
584
+ assetInfo = findAsset(release, asset);
539
585
  const shaInfo = findAsset(release, spec.shaSumsAsset);
540
586
  if (assetInfo) {
541
587
  assetUrl = assetInfo.browser_download_url;
542
588
  // Read SHA from discovered release if available
543
589
  if (shaInfo) {
544
- shaMap = await readShaSumsFromUrl(shaInfo.browser_download_url);
590
+ // Use API URL for SHA file too (for private repos)
591
+ const token = getGitHubToken();
592
+ if (token) {
593
+ const res = await fetchWithRetry(shaInfo.url, MAX_RETRIES, {
594
+ "Accept": "application/octet-stream",
595
+ });
596
+ const text = await res.text();
597
+ shaMap = new Map();
598
+ for (const line of text.split("\n")) {
599
+ const t = line.trim();
600
+ if (!t)
601
+ continue;
602
+ const m = t.match(/^([a-fA-F0-9]{64})\s+(.+)$/);
603
+ if (m)
604
+ shaMap.set(m[2].trim(), m[1].toLowerCase());
605
+ }
606
+ }
607
+ else {
608
+ shaMap = await readShaSumsFromUrl(shaInfo.browser_download_url);
609
+ }
545
610
  }
546
611
  else {
547
612
  // Fallback to legacy URL for SHA
@@ -566,8 +631,13 @@ async function ensureOne(name, platform) {
566
631
  // Clean and prepare directory
567
632
  await fs.promises.rm(dir, { recursive: true, force: true }).catch(() => { });
568
633
  await fs.promises.mkdir(dir, { recursive: true });
569
- // Download archive (with retry)
570
- await download(assetUrl, archivePath);
634
+ // Download archive (with retry) - use API URL for private repos
635
+ if (assetInfo && getGitHubToken()) {
636
+ await downloadAsset(assetInfo, archivePath);
637
+ }
638
+ else {
639
+ await download(assetUrl, archivePath);
640
+ }
571
641
  // Verify SHA256
572
642
  const got = (await sha256File(archivePath)).toLowerCase();
573
643
  if (got !== expected)
@@ -14,11 +14,11 @@ export const ENGINE_SPECS = {
14
14
  assetPrefix: "spec-high",
15
15
  shaSumsAsset: "SHA256SUMS.txt"
16
16
  },
17
- "vibecoding-helper": {
18
- name: "vibecoding-helper",
17
+ "vibe-execution-engine": {
18
+ name: "vibe-execution-engine",
19
19
  version: "1.0.0",
20
20
  repo: "yellowhama/vibe-pm",
21
- assetPrefix: "vibecoding-helper",
21
+ assetPrefix: "vibe-execution-engine",
22
22
  shaSumsAsset: "SHA256SUMS.txt"
23
23
  },
24
24
  "clinic": {
@@ -30,8 +30,13 @@ export const ENGINE_SPECS = {
30
30
  }
31
31
  };
32
32
  /**
33
- * Asset naming convention:
34
- * {assetPrefix}_{version}_{platform}.tar.gz
33
+ * Skill Bundle Specification (SSOT)
35
34
  *
36
- * Example: spec-high_1.0.0_linux_x64.tar.gz
35
+ * 스킬 파일은 npm 패키지의 skills/ 디렉토리에 prebundled로 포함됨.
36
+ * - 오프라인 지원
37
+ * - CLI 버전과 스킬 버전 동기화 자동 보장
37
38
  */
39
+ export const SKILL_SPEC = {
40
+ version: "1.0.0",
41
+ bundlePath: "skills" // adapters/mcp-ts/skills/ (패키지 루트 기준)
42
+ };