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