@gethmy/mcp 2.5.0 → 2.5.2

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/src/tui/setup.ts CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  import { homedir } from "node:os";
9
9
  import { dirname, join } from "node:path";
10
10
  import * as p from "@clack/prompts";
11
+ import { HarmonyApiClient } from "../api-client.js";
11
12
  import {
12
13
  areSkillsInstalled,
13
14
  getConfigPath,
@@ -223,56 +224,68 @@ export interface SymlinkToCreate {
223
224
  }
224
225
 
225
226
  /**
226
- * Generate agent configuration files
227
+ * Generate agent configuration files. Async because the Claude Code path
228
+ * fetches skill content from /v1/skills (skill_resource is the source of
229
+ * truth — see card #162 Phase 0b).
227
230
  */
228
- function getAgentFiles(
231
+ async function getAgentFiles(
229
232
  agentId: AgentId,
230
233
  cwd: string,
231
234
  installMode: InstallMode = "global",
232
- ): { files: FileToWrite[]; symlinks: SymlinkToCreate[] } {
235
+ ): Promise<{ files: FileToWrite[]; symlinks: SymlinkToCreate[] }> {
233
236
  const home = homedir();
234
237
  const files: FileToWrite[] = [];
235
238
  const symlinks: SymlinkToCreate[] = [];
236
239
 
237
240
  switch (agentId) {
238
241
  case "claude": {
239
- // Claude Code skill files built from the central skill registry
240
- const skillContent = buildSkillFile("hmy", "claude");
241
- const planSkillContent = buildSkillFile("hmy-plan", "claude");
242
-
243
- if (installMode === "global") {
244
- // Global mode: Write skills to ~/.agents/skills/, symlink directories to ~/.claude/skills/
245
- files.push({
246
- path: join(GLOBAL_SKILLS_DIR, "hmy", "SKILL.md"),
247
- content: skillContent,
248
- type: "text",
249
- });
250
- symlinks.push({
251
- target: join(GLOBAL_SKILLS_DIR, "hmy"),
252
- link: join(home, ".claude", "skills", "hmy"),
253
- });
242
+ // Claude Code skills come from /v1/skills (DB-backed). Fetch the
243
+ // canonical list, then each body. hmy-update-check is intentionally
244
+ // skipped it's a shell script the auto-update preamble curls on
245
+ // demand, not an installed SKILL.md.
246
+ const client = new HarmonyApiClient();
247
+ const versionInfo = await client.fetchSkillsVersion();
248
+ const installableNames = versionInfo.skills.filter(
249
+ (name) => name !== "hmy-update-check",
250
+ );
254
251
 
255
- files.push({
256
- path: join(GLOBAL_SKILLS_DIR, "hmy-plan", "SKILL.md"),
257
- content: planSkillContent,
258
- type: "text",
259
- });
260
- symlinks.push({
261
- target: join(GLOBAL_SKILLS_DIR, "hmy-plan"),
262
- link: join(home, ".claude", "skills", "hmy-plan"),
263
- });
264
- } else {
265
- // Local mode: Write skills directly to project directory
266
- files.push({
267
- path: join(cwd, ".claude", "skills", "hmy", "SKILL.md"),
268
- content: skillContent,
269
- type: "text",
270
- });
271
- files.push({
272
- path: join(cwd, ".claude", "skills", "hmy-plan", "SKILL.md"),
273
- content: planSkillContent,
274
- type: "text",
275
- });
252
+ const skillFailures: { name: string; error: string }[] = [];
253
+ for (const name of installableNames) {
254
+ try {
255
+ const fetched = await client.fetchSkill(name);
256
+ const content = buildSkillFile(fetched);
257
+
258
+ if (installMode === "global") {
259
+ // Global: write to ~/.agents/skills/<name>/SKILL.md and symlink
260
+ // ~/.claude/skills/<name> → that directory.
261
+ files.push({
262
+ path: join(GLOBAL_SKILLS_DIR, name, "SKILL.md"),
263
+ content,
264
+ type: "text",
265
+ });
266
+ symlinks.push({
267
+ target: join(GLOBAL_SKILLS_DIR, name),
268
+ link: join(home, ".claude", "skills", name),
269
+ });
270
+ } else {
271
+ files.push({
272
+ path: join(cwd, ".claude", "skills", name, "SKILL.md"),
273
+ content,
274
+ type: "text",
275
+ });
276
+ }
277
+ } catch (err) {
278
+ const msg = err instanceof Error ? err.message : String(err);
279
+ skillFailures.push({ name, error: msg });
280
+ }
281
+ }
282
+ if (skillFailures.length > 0) {
283
+ const summary = skillFailures
284
+ .map((f) => ` - ${f.name}: ${f.error}`)
285
+ .join("\n");
286
+ throw new Error(
287
+ `Failed to fetch ${skillFailures.length}/${installableNames.length} skill(s) from /v1/skills:\n${summary}`,
288
+ );
276
289
  }
277
290
 
278
291
  // Note: MCP server registration is handled separately via `claude mcp add` CLI
@@ -914,7 +927,11 @@ export async function runSetup(options: SetupOptions = {}): Promise<void> {
914
927
  // Agent-specific files
915
928
  if (needsSkills && selectedAgents.length > 0) {
916
929
  for (const agentId of selectedAgents) {
917
- const { files, symlinks } = getAgentFiles(agentId, cwd, installMode);
930
+ const { files, symlinks } = await getAgentFiles(
931
+ agentId,
932
+ cwd,
933
+ installMode,
934
+ );
918
935
  allFiles.push(...files);
919
936
  allSymlinks.push(...symlinks);
920
937
  }