@gethmy/mcp 2.1.1 → 2.1.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/skills.ts ADDED
@@ -0,0 +1,607 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname } from "node:path";
3
+ import { areSkillsInstalled } from "./config.js";
4
+
5
+ export const SKILLS_VERSION = "3";
6
+
7
+ const VERSION_MARKER_PREFIX = "<!-- skills-version:";
8
+
9
+ /**
10
+ * Legacy workflow prompt used by Codex, Cursor, Windsurf agents.
11
+ * Claude Code skills use the newer SKILL_DEFINITIONS content instead.
12
+ */
13
+ export const HARMONY_WORKFLOW_PROMPT = `# Harmony Card Workflow
14
+
15
+ Start work on a Harmony card. Card reference: $ARGUMENTS
16
+
17
+ ## 1. Find & Fetch Card
18
+
19
+ Parse the reference and fetch the card:
20
+ - \`#42\` or \`42\` → \`harmony_get_card_by_short_id\` with \`shortId: 42\`
21
+ - UUID → \`harmony_get_card\` with \`cardId\`
22
+ - Name/text → \`harmony_search_cards\` with \`query\`
23
+
24
+ ## 2. Get Board State
25
+
26
+ Call \`harmony_get_board\` to get columns and labels. From the response:
27
+ - Find the "In Progress" (or "Progress") column ID
28
+ - Find the "agent" label ID
29
+
30
+ ## 3. Setup Card for Work
31
+
32
+ Execute these in sequence:
33
+ 1. \`harmony_move_card\` → Move to "In Progress" column
34
+ 2. \`harmony_add_label_to_card\` → Add "agent" label
35
+ 3. \`harmony_start_agent_session\`:
36
+ - \`cardId\`: Card UUID
37
+ - \`agentIdentifier\`: Your agent identifier
38
+ - \`agentName\`: Your agent name
39
+ - \`currentTask\`: "Analyzing card requirements"
40
+
41
+ ## 4. Generate Work Prompt
42
+
43
+ Call \`harmony_generate_prompt\` with:
44
+ - \`cardId\` or \`shortId\` (+ \`projectId\` if using shortId)
45
+ - \`variant\`: Select based on task:
46
+ - \`"execute"\` (default) → Clear tasks, bug fixes, well-defined work
47
+ - \`"analysis"\` → Complex features, unclear requirements
48
+ - \`"draft"\` → Medium complexity, want feedback first
49
+
50
+ The generated prompt provides role framing, focus areas, subtasks, linked cards, and suggested outputs.
51
+
52
+ ## 5. Display Card Summary
53
+
54
+ Show the user: Card title, short ID, role, priority, labels, due date, description, and subtasks.
55
+
56
+ ## 6. Implement Solution
57
+
58
+ Work on the card following the generated prompt's guidance. Update progress at milestones:
59
+ - \`harmony_update_agent_progress\` with \`progressPercent\` (0-100), \`currentTask\`, \`status\`, \`blockers\`
60
+
61
+ **Progress checkpoints:** 20% (exploration), 50% (implementation), 80% (testing), 100% (done)
62
+
63
+ ## 7. Complete Work
64
+
65
+ When finished:
66
+ 1. \`harmony_end_agent_session\` with \`status: "completed"\`, \`progressPercent: 100\`
67
+ 2. \`harmony_move_card\` to "Review" column
68
+ 3. Summarize accomplishments
69
+
70
+ If pausing: \`harmony_end_agent_session\` with \`status: "paused"\`
71
+
72
+ ## Key Tools Reference
73
+
74
+ **Cards:** \`harmony_get_card\`, \`harmony_get_card_by_short_id\`, \`harmony_search_cards\`, \`harmony_create_card\`, \`harmony_update_card\`, \`harmony_move_card\`, \`harmony_delete_card\`, \`harmony_assign_card\`
75
+
76
+ **Subtasks:** \`harmony_create_subtask\`, \`harmony_toggle_subtask\`, \`harmony_delete_subtask\`
77
+
78
+ **Labels:** \`harmony_add_label_to_card\`, \`harmony_remove_label_from_card\`, \`harmony_create_label\`
79
+
80
+ **Links:** \`harmony_add_link_to_card\`, \`harmony_remove_link_from_card\`, \`harmony_get_card_links\`
81
+
82
+ **Board:** \`harmony_get_board\`, \`harmony_list_projects\`, \`harmony_get_context\`, \`harmony_set_project_context\`
83
+
84
+ **Sessions:** \`harmony_start_agent_session\`, \`harmony_update_agent_progress\`, \`harmony_end_agent_session\`, \`harmony_get_agent_session\`
85
+
86
+ **AI:** \`harmony_generate_prompt\`, \`harmony_process_command\`
87
+ `;
88
+
89
+ /**
90
+ * New Claude Code skill content with intent detection.
91
+ */
92
+ const HMY_SKILL_CONTENT = `# Harmony Card Workflow
93
+
94
+ User input: $ARGUMENTS
95
+
96
+ ## 0. Detect Intent
97
+
98
+ Parse \`$ARGUMENTS\` to determine what the user wants:
99
+
100
+ | Pattern | Intent | Go to |
101
+ |---|---|---|
102
+ | \`create ...\` or \`new ...\` | **Create** a new card | Step A |
103
+ | \`#42\`, \`42\`, UUID, or card name (no action verb) | **Work on** an existing card | Step B |
104
+ | \`move #42 to Done\`, \`update #42 ...\`, \`assign #42 ...\` | **Quick action** on a card | Step C |
105
+ | \`show #42\`, \`view #42\`, \`status #42\` | **View** card details | Step D |
106
+
107
+ If ambiguous, ask the user what they'd like to do.
108
+
109
+ ---
110
+
111
+ ## Step A: Create Card
112
+
113
+ Create a card without starting work on it.
114
+
115
+ 1. Parse the title and any details from the arguments (e.g., \`create Add dark mode toggle\` → title: "Add dark mode toggle")
116
+ 2. Call \`harmony_create_card\` with:
117
+ - \`title\`: extracted title
118
+ - \`description\`: if the user provided details beyond the title
119
+ - \`priority\`, \`columnId\`, \`assigneeId\`: only if explicitly specified
120
+ 3. Show the created card: title, short ID, column, and a link if available.
121
+ 4. **Stop here.** Do not start an agent session or begin implementation.
122
+
123
+ ---
124
+
125
+ ## Step B: Work on Existing Card
126
+
127
+ Start work on a Harmony card.
128
+
129
+ ### B1. Find & Fetch Card
130
+
131
+ Parse the reference and fetch the card:
132
+ - \`#42\` or \`42\` → \`harmony_get_card_by_short_id\` with \`shortId: 42\`
133
+ - UUID → \`harmony_get_card\` with \`cardId\`
134
+ - Name/text → \`harmony_search_cards\` with \`query\`
135
+
136
+ ### B2. Start Agent Session
137
+
138
+ Call \`harmony_start_agent_session\` with:
139
+ - \`cardId\`: Card UUID
140
+ - \`agentIdentifier\`: Your agent identifier
141
+ - \`agentName\`: Your agent name
142
+ - \`currentTask\`: A specific description of the first thing you'll do (e.g., "Exploring codebase to understand auth flow"), NOT a generic phrase like "Analyzing card requirements"
143
+ - \`moveToColumn\`: "In Progress"
144
+ - \`addLabels\`: ["agent"]
145
+
146
+ This single call moves the card, adds the label, auto-assigns, and starts the session.
147
+
148
+ ### B3. Generate Work Prompt
149
+
150
+ Call \`harmony_generate_prompt\` with:
151
+ - \`cardId\` or \`shortId\` (+ \`projectId\` if using shortId)
152
+ - \`variant\`: Select based on task:
153
+ - \`"execute"\` (default) → Clear tasks, bug fixes, well-defined work
154
+ - \`"analysis"\` → Complex features, unclear requirements
155
+ - \`"draft"\` → Medium complexity, want feedback first
156
+
157
+ The generated prompt provides role framing, focus areas, subtasks, linked cards, and suggested outputs.
158
+
159
+ ### B4. Display Card Summary
160
+
161
+ Show the user: Card title, short ID, role, priority, labels, due date, description, and subtasks.
162
+
163
+ ### B5. Implement Solution
164
+
165
+ Work on the card following the generated prompt's guidance.
166
+
167
+ **REQUIRED: Update progress at each milestone** by calling \`harmony_update_agent_progress\`. This is not optional — the card's live status badge depends on these updates.
168
+
169
+ | Milestone | \`progressPercent\` | Example \`currentTask\` |
170
+ |---|---|---|
171
+ | After exploring codebase & understanding requirements | 20 | "Reading auth middleware and identifying affected routes" |
172
+ | When starting implementation | 50 | "Refactoring token validation in auth.ts" |
173
+ | When moving to testing/verification | 80 | "Running build and verifying changes compile" |
174
+ | When done, before ending session | 100 | "All changes complete, ready for review" |
175
+
176
+ Always set \`currentTask\` to a specific description of what you're actually doing — never leave it as "Analyzing card requirements" or other generic text.
177
+
178
+ ### B6. Complete Work
179
+
180
+ When finished:
181
+ 1. \`harmony_end_agent_session\` with \`status: "completed"\`, \`progressPercent: 100\`, \`moveToColumn: "Review"\`
182
+ 2. Summarize accomplishments
183
+
184
+ If pausing: \`harmony_end_agent_session\` with \`status: "paused"\`
185
+
186
+ ---
187
+
188
+ ## Step C: Quick Action
189
+
190
+ Fetch the card first (same as B1), then perform the requested action:
191
+ - **Move:** \`harmony_move_card\` with target column
192
+ - **Update:** \`harmony_update_card\` with changed fields
193
+ - **Assign:** \`harmony_assign_card\`
194
+ - **Label:** \`harmony_add_label_to_card\` / \`harmony_remove_label_from_card\`
195
+ - **Archive:** \`harmony_archive_card\`
196
+
197
+ Show confirmation and stop. Do not start an agent session.
198
+
199
+ ---
200
+
201
+ ## Step D: View Card
202
+
203
+ Fetch the card (same as B1) and display: title, short ID, column, priority, labels, assignee, due date, description, subtasks, and links.
204
+
205
+ Do not start an agent session.
206
+
207
+ ---
208
+
209
+ ## Key Tools Reference
210
+
211
+ **Cards:** \`harmony_get_card\`, \`harmony_get_card_by_short_id\`, \`harmony_search_cards\`, \`harmony_create_card\`, \`harmony_update_card\`, \`harmony_move_card\`, \`harmony_delete_card\`, \`harmony_assign_card\`
212
+
213
+ **Subtasks:** \`harmony_create_subtask\`, \`harmony_toggle_subtask\`, \`harmony_delete_subtask\`
214
+
215
+ **Labels:** \`harmony_add_label_to_card\`, \`harmony_remove_label_from_card\`, \`harmony_create_label\`
216
+
217
+ **Links:** \`harmony_add_link_to_card\`, \`harmony_remove_link_from_card\`, \`harmony_get_card_links\`
218
+
219
+ **Board:** \`harmony_get_board\`, \`harmony_list_projects\`, \`harmony_get_context\`, \`harmony_set_project_context\`
220
+
221
+ **Sessions:** \`harmony_start_agent_session\`, \`harmony_update_agent_progress\`, \`harmony_end_agent_session\`, \`harmony_get_agent_session\`
222
+
223
+ **AI:** \`harmony_generate_prompt\`, \`harmony_process_command\`
224
+ `;
225
+
226
+ const HMY_PLAN_CONTENT = `# Harmony Plan Workflow
227
+
228
+ Create a new plan or work on an existing one. Argument: $ARGUMENTS
229
+
230
+ ## Step 1 — Detect Intent
231
+
232
+ Parse \`$ARGUMENTS\` to determine the workflow:
233
+
234
+ - **UUID** (contains dashes, 36 chars) → call \`harmony_get_plan\` with \`planId\` directly → **Step 2A**
235
+ - **\`#N\`** (short ID) → call \`harmony_get_card_by_short_id\` to get the card, then \`harmony_get_plan\` with \`cardId\` → **Step 2A**
236
+ - **Text** (anything else) → call \`harmony_list_plans\` with \`search\` set to the text
237
+ - If **one match** → use it → **Step 2A**
238
+ - If **multiple matches** → list them with title, status, phase, and updated date. Ask the user to pick one using \`AskUserQuestion\` → **Step 2A**
239
+ - If **no matches** → ask user: "No existing plans found for '$ARGUMENTS'. Would you like to create a new plan on this topic?" → **Step 2B**
240
+ - **Empty / vague topic** → **Step 2B** (create new plan)
241
+
242
+ ---
243
+
244
+ ## Step 2A — Work on Existing Plan
245
+
246
+ ### 2A.1 — Analyze & Display
247
+
248
+ Once you have the plan ID, call \`harmony_get_plan\` to fetch the full plan with tasks. Show a structured summary:
249
+
250
+ \\\`\\\`\\\`
251
+ ## [Plan Title]
252
+ **Status:** draft/active/archived | **Phase:** plan/execute/verify/done
253
+ **Tasks:** N total (X pending, Y in_progress, Z completed)
254
+ **URL:** https://gethmy.com/plans/{id}
255
+ \\\`\\\`\\\`
256
+
257
+ If plan is in execute phase and tasks already have linked cards, note which tasks have cards and which don't.
258
+
259
+ ### 2A.2 — Recall Context
260
+
261
+ Call \`harmony_memory_search\` with the plan title to find related knowledge, patterns, or decisions that may inform execution.
262
+
263
+ ### 2A.3 — Present Options
264
+
265
+ Use \`AskUserQuestion\` to offer execution choices. Adapt options based on plan state:
266
+
267
+ #### Default options (plan in \`plan\` phase):
268
+
269
+ **(A) Single card** — Create one card with plan summary + link to plan. Best for simple plans.
270
+ **(B) Multiple cards** — Create one card per task via \`harmony_advance_plan\`. Best for complex plans with independent tasks.
271
+ **(C) Analyze only** — Review the plan and design an implementation approach. Create cards later.
272
+ **(D) Skip** — Do nothing.
273
+
274
+ #### If plan has no tasks:
275
+ Only offer **(A) Single card**, **(C) Analyze only**, or **(D) Skip**.
276
+
277
+ #### If plan is already in \`execute\` phase with existing cards:
278
+ **(A) Work on existing cards** — List cards with short IDs, suggest \`/hmy #N\` to start
279
+ **(B) Create cards for remaining tasks** — Only create cards for tasks without linked cards
280
+ **(C) Analyze progress** — Review what's done vs remaining
281
+ **(D) Skip**
282
+
283
+ ### 2A.4 — Execute Choice
284
+
285
+ #### Option A — Single card
286
+ 1. Call \`harmony_create_card\` with:
287
+ - \`title\`: Plan title
288
+ - \`description\`: Brief 2-3 sentence summary of the plan + \`\\n\\n[View plan](https://gethmy.com/plans/{planId})\`
289
+ - \`priority\`: based on plan task priorities (use highest)
290
+ 2. Call \`harmony_update_plan\` to set \`status: "active"\`, \`workflowPhase: "execute"\`
291
+
292
+ #### Option B — Multiple cards (advance plan)
293
+ 1. Call \`harmony_advance_plan\` with:
294
+ - \`planId\`: the plan ID
295
+ - \`phase\`: \`"execute"\`
296
+ - \`summary\`: Brief summary of the plan scope
297
+ 2. Display the created cards with their short IDs
298
+
299
+ #### Option C — Analyze only
300
+ 1. Present a structured implementation analysis:
301
+ - Suggested order of tasks
302
+ - Dependencies between tasks
303
+ - Key technical considerations from memory search
304
+ - Estimated complexity
305
+ 2. Suggest creating cards when ready
306
+
307
+ #### Option D — Skip
308
+ Acknowledge and stop.
309
+
310
+ ### 2A.5 — Summary
311
+
312
+ After execution, show:
313
+ - Created card(s) with short IDs
314
+ - Current plan phase
315
+ - Suggest next step: \`/hmy #N\` to start working on a card
316
+
317
+ ---
318
+
319
+ ## Step 2B — Create New Plan
320
+
321
+ ### 2B.1 — Context Gathering
322
+
323
+ Before interviewing, gather existing context:
324
+
325
+ 1. Call \`harmony_get_board\` for board state (columns, cards, labels)
326
+ 2. Call \`harmony_recall\` with relevant tags to find existing knowledge on the topic
327
+ 3. Call \`harmony_memory_search\` with the topic to find related patterns/decisions/lessons
328
+
329
+ Note what you learn — reference it during the interview to ask smarter questions.
330
+
331
+ ### 2B.2 — Structured Interview (3-5 questions)
332
+
333
+ Interview the user with **3-5 focused questions** using AskUserQuestion. Adapt based on complexity.
334
+
335
+ #### Core Questions
336
+ 1. **Problem & Audience**: What problem are we solving? Who benefits?
337
+ 2. **Scope**: What's in v1, what's deferred?
338
+ 3. **Technical Constraints**: Any technical preferences, constraints, or existing patterns to follow?
339
+
340
+ #### Adaptive Follow-ups (if needed)
341
+ - Key user flows or interactions?
342
+ - Integration points with existing systems?
343
+ - Performance, security, or accessibility requirements?
344
+
345
+ #### Interview Rules
346
+ - Reference what you found in Step 2B.1 (board state, memories) to ask informed questions
347
+ - Skip questions that aren't relevant — simple features need fewer questions
348
+ - Tell the user when you have enough information to draft
349
+
350
+ ### 2B.3 — Plan Document
351
+
352
+ Create a structured markdown document:
353
+
354
+ \\\`\\\`\\\`markdown
355
+ # [Title]
356
+
357
+ ## Problem
358
+ [What problem we're solving and why]
359
+
360
+ ## Scope
361
+
362
+ ### In Scope
363
+ - [Feature/capability 1]
364
+ - [Feature/capability 2]
365
+
366
+ ### Out of Scope
367
+ - [Deferred items]
368
+
369
+ ## Approach
370
+ [Technical design, architecture decisions, key patterns]
371
+
372
+ ### Key Decisions
373
+ | Decision | Choice | Rationale |
374
+ |----------|--------|-----------|
375
+ | ... | ... | ... |
376
+
377
+ ## Tasks
378
+ 1. **[Task title]** — priority: high
379
+ [Brief description]
380
+
381
+ 2. **[Task title]** — priority: medium
382
+ [Brief description]
383
+
384
+ [Continue for all tasks...]
385
+
386
+ ## Risks & Mitigations
387
+ | Risk | Mitigation |
388
+ |------|------------|
389
+ | ... | ... |
390
+
391
+ ## Success Criteria
392
+ - [ ] [Measurable criterion]
393
+ \\\`\\\`\\\`
394
+
395
+ ### 2B.4 — Create Plan
396
+
397
+ Call \`harmony_create_plan\` with:
398
+ - \`title\`: The plan title
399
+ - \`content\`: Full markdown document from Step 2B.3
400
+ - \`source\`: \`"agent"\`
401
+ - \`workflowPhase\`: \`"plan"\`
402
+ - \`tasks\`: Array of tasks from the Tasks section, each with:
403
+ - \`content\`: Task title + brief description
404
+ - \`priority\`: \`"high"\`, \`"medium"\`, or \`"low"\`
405
+ - \`status\`: \`"pending"\`
406
+
407
+ ### 2B.5 — User Approval
408
+
409
+ Show the user:
410
+ - Plan URL: \`https://gethmy.com/plans/{id}\`
411
+ - Number of tasks created
412
+ - Brief summary
413
+
414
+ Then ask: **"Ready to execute? I'll create board cards from these tasks and start the execution phase."**
415
+
416
+ Options:
417
+ 1. **Yes, advance to Execute** — proceed to Step 2B.6
418
+ 2. **Let me review first** — end here, they can advance later from the UI
419
+ 3. **Make changes** — iterate on the plan
420
+
421
+ ### 2B.6 — Advance to Execute
422
+
423
+ Call \`harmony_advance_plan\` with:
424
+ - \`planId\`: The plan ID from Step 2B.4
425
+ - \`phase\`: \`"execute"\`
426
+ - \`summary\`: A 2-3 sentence summary of the key decisions made during planning
427
+
428
+ Report the result:
429
+ - "Created N cards in 'To Do'. Use \`/hmy #<id>\` to start working on any card."
430
+ - List the created cards with their short IDs
431
+
432
+ ## Key Tools Reference
433
+
434
+ **Discovery:** \`harmony_list_plans\`, \`harmony_get_plan\`, \`harmony_get_card_by_short_id\`
435
+ **Context:** \`harmony_get_board\`, \`harmony_recall\`, \`harmony_memory_search\`
436
+ **Planning:** \`harmony_create_plan\`, \`harmony_advance_plan\`, \`harmony_update_plan\`
437
+ **Execution:** \`harmony_create_card\`, \`harmony_update_card\`
438
+ `;
439
+
440
+ export interface SkillDefinition {
441
+ name: string;
442
+ description: string;
443
+ argumentHint: string;
444
+ content: string;
445
+ }
446
+
447
+ export const SKILL_DEFINITIONS: Record<string, SkillDefinition> = {
448
+ hmy: {
449
+ name: "hmy",
450
+ description:
451
+ "Work with Harmony cards — create, view, update, or start working on them. Use when given a card reference like #42, or commands like create, move, update.",
452
+ argumentHint: "<command or card-reference>",
453
+ content: HMY_SKILL_CONTENT,
454
+ },
455
+ "hmy-plan": {
456
+ name: "hmy-plan",
457
+ description:
458
+ "Create a new plan or work on an existing one. Use when asked to plan a feature, execute a plan, review a plan, or given a plan reference.",
459
+ argumentHint: "[plan name, ID, or topic to plan]",
460
+ content: HMY_PLAN_CONTENT,
461
+ },
462
+ };
463
+
464
+ /**
465
+ * Build a complete skill file with frontmatter and version marker.
466
+ * Optionally substitutes agent identifier/name for agent-specific builds.
467
+ */
468
+ export function buildSkillFile(skillId: string, agentId?: string): string {
469
+ const skill = SKILL_DEFINITIONS[skillId];
470
+ if (!skill) {
471
+ throw new Error(`Unknown skill: ${skillId}`);
472
+ }
473
+
474
+ let content = skill.content;
475
+
476
+ // Apply agent-specific substitutions
477
+ if (agentId === "claude") {
478
+ content = content
479
+ .replace("Your agent identifier", "claude-code")
480
+ .replace("Your agent name", "Claude Code");
481
+ }
482
+
483
+ const frontmatter = `---
484
+ name: ${skill.name}
485
+ description: ${skill.description}
486
+ argument-hint: ${skill.argumentHint}
487
+ ---`;
488
+
489
+ return `${frontmatter}\n\n${content}\n${VERSION_MARKER_PREFIX}${SKILLS_VERSION} -->`;
490
+ }
491
+
492
+ /**
493
+ * Parse the skills version from a skill file's content.
494
+ * Returns null if no version marker is found.
495
+ */
496
+ function parseSkillVersion(content: string): string | null {
497
+ const match = content.match(/<!-- skills-version:(\d+) -->/);
498
+ return match ? match[1] : null;
499
+ }
500
+
501
+ /**
502
+ * Find all skill files in a given directory (global or local).
503
+ * Returns an array of { skillId, filePath } for each found skill.
504
+ */
505
+ function findSkillFiles(
506
+ paths: string[],
507
+ ): { skillId: string; filePath: string }[] {
508
+ const results: { skillId: string; filePath: string }[] = [];
509
+
510
+ for (const filePath of paths) {
511
+ if (!existsSync(filePath)) continue;
512
+
513
+ // Determine skill ID from path
514
+ for (const skillId of Object.keys(SKILL_DEFINITIONS)) {
515
+ if (
516
+ filePath.includes(`/${skillId}/`) ||
517
+ filePath.includes(`/${skillId}.md`)
518
+ ) {
519
+ results.push({ skillId, filePath });
520
+ break;
521
+ }
522
+ }
523
+ }
524
+
525
+ return results;
526
+ }
527
+
528
+ /**
529
+ * Silently refresh installed skill files if they are outdated.
530
+ * Called at MCP server startup. Non-blocking — errors are caught and logged.
531
+ */
532
+ export async function refreshSkills(): Promise<void> {
533
+ try {
534
+ const status = areSkillsInstalled();
535
+
536
+ if (!status.installed) {
537
+ // User hasn't run setup yet — don't install skills
538
+ return;
539
+ }
540
+
541
+ // Find skill files from the detected paths
542
+ const skillFiles = findSkillFiles(status.paths);
543
+
544
+ // Also check for sibling skills in the same directory
545
+ // e.g., if hmy is installed at ~/.agents/skills/hmy/SKILL.md,
546
+ // check for hmy-plan at ~/.agents/skills/hmy-plan/SKILL.md
547
+ if (skillFiles.length > 0) {
548
+ const samplePath = skillFiles[0].filePath;
549
+
550
+ for (const skillId of Object.keys(SKILL_DEFINITIONS)) {
551
+ const alreadyFound = skillFiles.some((sf) => sf.skillId === skillId);
552
+ if (alreadyFound) continue;
553
+
554
+ // Try to find sibling skill file
555
+ let siblingPath: string;
556
+ if (samplePath.endsWith("SKILL.md")) {
557
+ // ~/.agents/skills/hmy/SKILL.md -> ~/.agents/skills/hmy-plan/SKILL.md
558
+ const parentDir = dirname(dirname(samplePath));
559
+ siblingPath = `${parentDir}/${skillId}/SKILL.md`;
560
+ } else {
561
+ // ~/.claude/skills/hmy.md -> ~/.claude/skills/hmy-plan.md
562
+ const parentDir = dirname(samplePath);
563
+ siblingPath = `${parentDir}/${skillId}.md`;
564
+ }
565
+
566
+ if (existsSync(siblingPath)) {
567
+ skillFiles.push({ skillId, filePath: siblingPath });
568
+ }
569
+ }
570
+ }
571
+
572
+ if (skillFiles.length === 0) {
573
+ return;
574
+ }
575
+
576
+ let updated = false;
577
+
578
+ for (const { skillId, filePath } of skillFiles) {
579
+ try {
580
+ const currentContent = readFileSync(filePath, "utf-8");
581
+ const currentVersion = parseSkillVersion(currentContent);
582
+
583
+ // Update if no version marker or version is older
584
+ if (
585
+ currentVersion === null ||
586
+ Number(currentVersion) < Number(SKILLS_VERSION)
587
+ ) {
588
+ const newContent = buildSkillFile(skillId, "claude");
589
+ const dir = dirname(filePath);
590
+ if (!existsSync(dir)) {
591
+ mkdirSync(dir, { recursive: true });
592
+ }
593
+ writeFileSync(filePath, newContent);
594
+ updated = true;
595
+ }
596
+ } catch {
597
+ // Silently skip files we can't read/write
598
+ }
599
+ }
600
+
601
+ if (updated) {
602
+ console.error(`Harmony: Updated skills to v${SKILLS_VERSION}`);
603
+ }
604
+ } catch {
605
+ // Non-blocking — if anything fails, continue silently
606
+ }
607
+ }