@gethmy/mcp 2.7.0 → 2.8.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.
package/dist/cli.js CHANGED
@@ -2378,6 +2378,294 @@ async function onboardNewUser(params) {
2378
2378
  };
2379
2379
  }
2380
2380
 
2381
+ // src/skills.ts
2382
+ import {
2383
+ existsSync as existsSync4,
2384
+ mkdirSync as mkdirSync3,
2385
+ readFileSync as readFileSync4,
2386
+ renameSync,
2387
+ writeFileSync as writeFileSync3
2388
+ } from "node:fs";
2389
+ import { homedir as homedir3 } from "node:os";
2390
+ import { dirname, join as join4 } from "node:path";
2391
+
2392
+ // src/hmy-config.ts
2393
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
2394
+ import { homedir as homedir2 } from "node:os";
2395
+ import { join as join3 } from "node:path";
2396
+ var DEFAULTS = { updateCheck: true, pin: null };
2397
+ function getHmyConfigPath() {
2398
+ return join3(homedir2(), ".hmy", "config.yaml");
2399
+ }
2400
+ function loadHmyConfig() {
2401
+ const path = getHmyConfigPath();
2402
+ if (!existsSync3(path))
2403
+ return { ...DEFAULTS };
2404
+ try {
2405
+ return parseHmyConfig(readFileSync3(path, "utf-8"));
2406
+ } catch {
2407
+ return { ...DEFAULTS };
2408
+ }
2409
+ }
2410
+ function parseHmyConfig(text) {
2411
+ const cfg = { ...DEFAULTS };
2412
+ for (const rawLine of text.split(`
2413
+ `)) {
2414
+ const line = rawLine.trim();
2415
+ if (!line || line.startsWith("#"))
2416
+ continue;
2417
+ const sep2 = line.indexOf(":");
2418
+ if (sep2 === -1)
2419
+ continue;
2420
+ const key = line.slice(0, sep2).trim();
2421
+ let value = line.slice(sep2 + 1).trim();
2422
+ const hash = value.indexOf(" #");
2423
+ if (hash !== -1)
2424
+ value = value.slice(0, hash).trim();
2425
+ value = value.replace(/^["']|["']$/g, "");
2426
+ switch (key) {
2427
+ case "update_check":
2428
+ cfg.updateCheck = value !== "false";
2429
+ break;
2430
+ case "pin":
2431
+ case "pin_version":
2432
+ cfg.pin = value || null;
2433
+ break;
2434
+ }
2435
+ }
2436
+ return cfg;
2437
+ }
2438
+
2439
+ // src/skills.ts
2440
+ var HARMONY_WORKFLOW_PROMPT = `# Harmony Card Workflow
2441
+
2442
+ Start work on a Harmony card. Card reference: $ARGUMENTS
2443
+
2444
+ ## 1. Find & Fetch Card
2445
+
2446
+ Parse the reference and fetch the card:
2447
+ - \`#42\` or \`42\` → \`harmony_get_card_by_short_id\` with \`shortId: 42\`
2448
+ - UUID → \`harmony_get_card\` with \`cardId\`
2449
+ - Name/text → \`harmony_search_cards\` with \`query\`
2450
+
2451
+ ## 2. Get Board State
2452
+
2453
+ Call \`harmony_get_board\` to get columns and labels. From the response:
2454
+ - Find the "In Progress" (or "Progress") column ID
2455
+ - Find the "agent" label ID
2456
+
2457
+ ## 3. Setup Card for Work
2458
+
2459
+ Execute these in sequence:
2460
+ 1. \`harmony_move_card\` → Move to "In Progress" column
2461
+ 2. \`harmony_add_label_to_card\` → Add "agent" label
2462
+ 3. \`harmony_start_agent_session\`:
2463
+ - \`cardId\`: Card UUID
2464
+ - \`agentIdentifier\`: Your agent identifier
2465
+ - \`agentName\`: Your agent name
2466
+ - \`currentTask\`: "Analyzing card requirements"
2467
+
2468
+ ## 4. Generate Work Prompt
2469
+
2470
+ Call \`harmony_generate_prompt\` with:
2471
+ - \`cardId\` or \`shortId\` (+ \`projectId\` if using shortId)
2472
+ - \`variant\`: Select based on task:
2473
+ - \`"execute"\` (default) → Clear tasks, bug fixes, well-defined work
2474
+ - \`"analysis"\` → Complex features, unclear requirements
2475
+ - \`"draft"\` → Medium complexity, want feedback first
2476
+
2477
+ The generated prompt provides role framing, focus areas, subtasks, linked cards, and suggested outputs.
2478
+
2479
+ ## 5. Display Card Summary
2480
+
2481
+ Show the user: Card title, short ID, role, priority, labels, due date, description, and subtasks.
2482
+
2483
+ ## 6. Implement Solution
2484
+
2485
+ Work on the card following the generated prompt's guidance. Update progress at milestones:
2486
+ - \`harmony_update_agent_progress\` with \`progressPercent\` (0-100), \`currentTask\`, \`status\`, \`blockers\`
2487
+
2488
+ **Progress checkpoints:** 20% (exploration), 50% (implementation), 80% (testing), 100% (done)
2489
+
2490
+ ## 7. Complete Work
2491
+
2492
+ When finished:
2493
+ 1. \`harmony_end_agent_session\` with \`status: "completed"\`, \`progressPercent: 100\`
2494
+ 2. \`harmony_move_card\` to "Review" column
2495
+ 3. Summarize accomplishments
2496
+
2497
+ If pausing: \`harmony_end_agent_session\` with \`status: "paused"\`
2498
+
2499
+ ## Key Tools Reference
2500
+
2501
+ **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\`
2502
+
2503
+ **Subtasks:** \`harmony_create_subtask\`, \`harmony_toggle_subtask\`, \`harmony_delete_subtask\`
2504
+
2505
+ **Labels:** \`harmony_add_label_to_card\`, \`harmony_remove_label_from_card\`, \`harmony_create_label\`
2506
+
2507
+ **Links:** \`harmony_add_link_to_card\`, \`harmony_remove_link_from_card\`, \`harmony_get_card_links\`
2508
+
2509
+ **Board:** \`harmony_get_board\`, \`harmony_list_projects\`, \`harmony_get_context\`, \`harmony_set_project_context\`
2510
+
2511
+ **Sessions:** \`harmony_start_agent_session\`, \`harmony_update_agent_progress\`, \`harmony_end_agent_session\`, \`harmony_get_agent_session\`
2512
+
2513
+ **AI:** \`harmony_generate_prompt\`, \`harmony_process_command\`
2514
+ `;
2515
+ function buildSkillFile(skill) {
2516
+ const content = stripSkillPreamble(skill.content);
2517
+ if (skill.skillVersion !== undefined && !hasMetadataVersion(content)) {
2518
+ return injectMetadataVersion(content, skill.skillVersion);
2519
+ }
2520
+ return content;
2521
+ }
2522
+ var PREAMBLE_START = "<!-- hmy-skills-preamble:start -->";
2523
+ var PREAMBLE_END = "<!-- hmy-skills-preamble:end -->";
2524
+ function stripSkillPreamble(content) {
2525
+ const start = content.indexOf(PREAMBLE_START);
2526
+ const end = content.indexOf(PREAMBLE_END);
2527
+ if (start === -1 || end === -1 || end < start)
2528
+ return content;
2529
+ const stripped = content.slice(0, start) + content.slice(end + PREAMBLE_END.length);
2530
+ return `${stripped.replace(/\n{3,}/g, `
2531
+
2532
+ `).replace(/\s+$/, "")}
2533
+ `;
2534
+ }
2535
+ function atomicWrite(filePath, content) {
2536
+ const dir = dirname(filePath);
2537
+ if (!existsSync4(dir))
2538
+ mkdirSync3(dir, { recursive: true });
2539
+ const tmp = `${filePath}.tmp-${process.pid}-${Date.now()}`;
2540
+ writeFileSync3(tmp, content);
2541
+ renameSync(tmp, filePath);
2542
+ }
2543
+ function hasMetadataVersion(content) {
2544
+ return parseSkillVersion(content) !== null;
2545
+ }
2546
+ function injectMetadataVersion(content, version) {
2547
+ const fmMatch = content.match(/^(---\n[\s\S]*?\n)(---\n)([\s\S]*)$/);
2548
+ if (!fmMatch)
2549
+ return content;
2550
+ const [, head, close, body] = fmMatch;
2551
+ const block = `metadata:
2552
+ version: "${version}"
2553
+ `;
2554
+ return `${head}${block}${close}${body}`;
2555
+ }
2556
+ function parseSkillVersion(content) {
2557
+ const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
2558
+ if (fmMatch) {
2559
+ const fm = fmMatch[1];
2560
+ const verMatch = fm.match(/^metadata:[\s\S]*?\n[ \t]+version:[ \t]*["']?(\d+)["']?\s*$/m);
2561
+ if (verMatch)
2562
+ return verMatch[1];
2563
+ }
2564
+ const legacy = content.match(/<!-- skills-version:(\d+) -->/);
2565
+ return legacy ? legacy[1] : null;
2566
+ }
2567
+ function findSkillFiles(paths, knownNames) {
2568
+ const results = [];
2569
+ for (const filePath of paths) {
2570
+ if (!existsSync4(filePath))
2571
+ continue;
2572
+ for (const name of knownNames) {
2573
+ if (filePath.includes(`/${name}/`) || filePath.includes(`/${name}.md`)) {
2574
+ results.push({ name, filePath });
2575
+ break;
2576
+ }
2577
+ }
2578
+ }
2579
+ return results;
2580
+ }
2581
+ var HMY_DIR = join4(homedir3(), ".hmy");
2582
+ var HMY_VERSION_FILE = join4(HMY_DIR, "VERSION");
2583
+ var LAST_CHECK_FILE = join4(HMY_DIR, "last-update-check");
2584
+ var CHECK_TTL_MS = 24 * 60 * 60 * 1000;
2585
+ function checkedRecently(now = Date.now()) {
2586
+ try {
2587
+ if (!existsSync4(LAST_CHECK_FILE))
2588
+ return false;
2589
+ const ts = Number.parseInt(readFileSync4(LAST_CHECK_FILE, "utf-8").trim(), 10);
2590
+ if (!Number.isFinite(ts))
2591
+ return false;
2592
+ return now - ts < CHECK_TTL_MS;
2593
+ } catch {
2594
+ return false;
2595
+ }
2596
+ }
2597
+ function recordCheck(now = Date.now()) {
2598
+ try {
2599
+ if (!existsSync4(HMY_DIR))
2600
+ mkdirSync3(HMY_DIR, { recursive: true });
2601
+ writeFileSync3(LAST_CHECK_FILE, String(now));
2602
+ } catch {}
2603
+ }
2604
+ async function refreshSkills(opts = {}) {
2605
+ try {
2606
+ if (!isConfigured())
2607
+ return { updated: false };
2608
+ const cfg = loadHmyConfig();
2609
+ if (!cfg.updateCheck || cfg.pin)
2610
+ return { updated: false };
2611
+ if (!opts.force && checkedRecently())
2612
+ return { updated: false };
2613
+ const status = areSkillsInstalled();
2614
+ if (!status.installed)
2615
+ return { updated: false };
2616
+ const client3 = new HarmonyApiClient;
2617
+ const versionInfo = await client3.fetchSkillsVersion();
2618
+ recordCheck();
2619
+ const skillFiles = findSkillFiles(status.paths, versionInfo.skills);
2620
+ if (skillFiles.length > 0) {
2621
+ const samplePath = skillFiles[0].filePath;
2622
+ for (const name of versionInfo.skills) {
2623
+ if (skillFiles.some((sf) => sf.name === name))
2624
+ continue;
2625
+ let siblingPath;
2626
+ if (samplePath.endsWith("SKILL.md")) {
2627
+ const parentDir = dirname(dirname(samplePath));
2628
+ siblingPath = `${parentDir}/${name}/SKILL.md`;
2629
+ } else {
2630
+ const parentDir = dirname(samplePath);
2631
+ siblingPath = `${parentDir}/${name}.md`;
2632
+ }
2633
+ if (existsSync4(siblingPath)) {
2634
+ skillFiles.push({ name, filePath: siblingPath });
2635
+ }
2636
+ }
2637
+ }
2638
+ if (skillFiles.length === 0)
2639
+ return { updated: false };
2640
+ let updated = false;
2641
+ for (const { name, filePath } of skillFiles) {
2642
+ try {
2643
+ const currentContent = readFileSync4(filePath, "utf-8");
2644
+ const localVersion = parseSkillVersion(currentContent);
2645
+ const fetched = await client3.fetchSkill(name);
2646
+ const remoteVersion = fetched.skillVersion;
2647
+ if (remoteVersion !== undefined && localVersion !== null && Number(localVersion) >= remoteVersion) {
2648
+ continue;
2649
+ }
2650
+ atomicWrite(filePath, buildSkillFile(fetched));
2651
+ updated = true;
2652
+ } catch (err) {
2653
+ const msg = err instanceof Error ? err.message : String(err);
2654
+ console.error(`Harmony: skill "${name}" refresh failed: ${msg}`);
2655
+ }
2656
+ }
2657
+ if (updated) {
2658
+ try {
2659
+ atomicWrite(HMY_VERSION_FILE, versionInfo.version);
2660
+ } catch {}
2661
+ console.error("Harmony: Refreshed skills from server");
2662
+ }
2663
+ return { updated };
2664
+ } catch {
2665
+ return { updated: false };
2666
+ }
2667
+ }
2668
+
2381
2669
  // src/server.ts
2382
2670
  var memorySessions = new Map;
2383
2671
  function parseLabelList(raw) {
@@ -2515,7 +2803,11 @@ var TOOLS = {
2515
2803
  enum: ["low", "medium", "high", "urgent"],
2516
2804
  description: "Priority level"
2517
2805
  },
2518
- assigneeId: { type: "string", description: "Assignee user ID" }
2806
+ assigneeId: { type: "string", description: "Assignee user ID" },
2807
+ planId: {
2808
+ type: "string",
2809
+ description: "Plan ID to link this card to (optional). Links the card to that plan via its plan_id."
2810
+ }
2519
2811
  },
2520
2812
  required: ["title"]
2521
2813
  }
@@ -3830,7 +4122,7 @@ function registerHandlers(server, deps) {
3830
4122
  {
3831
4123
  uri,
3832
4124
  mimeType: "text/markdown",
3833
- text: fetched.content
4125
+ text: stripSkillPreamble(fetched.content)
3834
4126
  }
3835
4127
  ]
3836
4128
  };
@@ -3880,7 +4172,8 @@ async function handleToolCall(name, args, deps) {
3880
4172
  columnId: args.columnId,
3881
4173
  description: args.description,
3882
4174
  priority: args.priority,
3883
- assigneeId: args.assigneeId
4175
+ assigneeId: args.assigneeId,
4176
+ planId: args.planId
3884
4177
  });
3885
4178
  return { success: true, ...result };
3886
4179
  }
@@ -4906,13 +5199,16 @@ function createConfigDeps() {
4906
5199
  class HarmonyMCPServer {
4907
5200
  server;
4908
5201
  constructor() {
4909
- this.server = new Server({ name: "@gethmy/mcp", version: "2.0.0" }, { capabilities: { tools: {}, resources: {} } });
5202
+ this.server = new Server({ name: "@gethmy/mcp", version: "2.0.0" }, { capabilities: { tools: {}, resources: { listChanged: true } } });
4910
5203
  registerHandlers(this.server, createConfigDeps());
4911
5204
  }
4912
- async run() {
5205
+ async run(opts = {}) {
4913
5206
  const transport = new StdioServerTransport;
4914
5207
  await this.server.connect(transport);
4915
5208
  console.error("Harmony MCP server running on stdio");
5209
+ if (opts.skillsUpdated) {
5210
+ this.server.sendResourceListChanged().catch(() => {});
5211
+ }
4916
5212
  const configDeps = createConfigDeps();
4917
5213
  initAutoSession(async (client3, cardId, status) => {
4918
5214
  await runEndSessionPipeline(client3, configDeps, cardId, status);
@@ -4959,209 +5255,30 @@ class HarmonyMCPServer {
4959
5255
  }
4960
5256
  }
4961
5257
 
4962
- // src/skills.ts
4963
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
4964
- import { dirname } from "node:path";
4965
- var HARMONY_WORKFLOW_PROMPT = `# Harmony Card Workflow
4966
-
4967
- Start work on a Harmony card. Card reference: $ARGUMENTS
4968
-
4969
- ## 1. Find & Fetch Card
4970
-
4971
- Parse the reference and fetch the card:
4972
- - \`#42\` or \`42\` → \`harmony_get_card_by_short_id\` with \`shortId: 42\`
4973
- - UUID → \`harmony_get_card\` with \`cardId\`
4974
- - Name/text → \`harmony_search_cards\` with \`query\`
4975
-
4976
- ## 2. Get Board State
4977
-
4978
- Call \`harmony_get_board\` to get columns and labels. From the response:
4979
- - Find the "In Progress" (or "Progress") column ID
4980
- - Find the "agent" label ID
4981
-
4982
- ## 3. Setup Card for Work
4983
-
4984
- Execute these in sequence:
4985
- 1. \`harmony_move_card\` → Move to "In Progress" column
4986
- 2. \`harmony_add_label_to_card\` → Add "agent" label
4987
- 3. \`harmony_start_agent_session\`:
4988
- - \`cardId\`: Card UUID
4989
- - \`agentIdentifier\`: Your agent identifier
4990
- - \`agentName\`: Your agent name
4991
- - \`currentTask\`: "Analyzing card requirements"
4992
-
4993
- ## 4. Generate Work Prompt
4994
-
4995
- Call \`harmony_generate_prompt\` with:
4996
- - \`cardId\` or \`shortId\` (+ \`projectId\` if using shortId)
4997
- - \`variant\`: Select based on task:
4998
- - \`"execute"\` (default) → Clear tasks, bug fixes, well-defined work
4999
- - \`"analysis"\` → Complex features, unclear requirements
5000
- - \`"draft"\` → Medium complexity, want feedback first
5001
-
5002
- The generated prompt provides role framing, focus areas, subtasks, linked cards, and suggested outputs.
5003
-
5004
- ## 5. Display Card Summary
5005
-
5006
- Show the user: Card title, short ID, role, priority, labels, due date, description, and subtasks.
5007
-
5008
- ## 6. Implement Solution
5009
-
5010
- Work on the card following the generated prompt's guidance. Update progress at milestones:
5011
- - \`harmony_update_agent_progress\` with \`progressPercent\` (0-100), \`currentTask\`, \`status\`, \`blockers\`
5012
-
5013
- **Progress checkpoints:** 20% (exploration), 50% (implementation), 80% (testing), 100% (done)
5014
-
5015
- ## 7. Complete Work
5016
-
5017
- When finished:
5018
- 1. \`harmony_end_agent_session\` with \`status: "completed"\`, \`progressPercent: 100\`
5019
- 2. \`harmony_move_card\` to "Review" column
5020
- 3. Summarize accomplishments
5021
-
5022
- If pausing: \`harmony_end_agent_session\` with \`status: "paused"\`
5023
-
5024
- ## Key Tools Reference
5025
-
5026
- **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\`
5027
-
5028
- **Subtasks:** \`harmony_create_subtask\`, \`harmony_toggle_subtask\`, \`harmony_delete_subtask\`
5029
-
5030
- **Labels:** \`harmony_add_label_to_card\`, \`harmony_remove_label_from_card\`, \`harmony_create_label\`
5031
-
5032
- **Links:** \`harmony_add_link_to_card\`, \`harmony_remove_link_from_card\`, \`harmony_get_card_links\`
5033
-
5034
- **Board:** \`harmony_get_board\`, \`harmony_list_projects\`, \`harmony_get_context\`, \`harmony_set_project_context\`
5035
-
5036
- **Sessions:** \`harmony_start_agent_session\`, \`harmony_update_agent_progress\`, \`harmony_end_agent_session\`, \`harmony_get_agent_session\`
5037
-
5038
- **AI:** \`harmony_generate_prompt\`, \`harmony_process_command\`
5039
- `;
5040
- function buildSkillFile(skill) {
5041
- if (skill.skillVersion !== undefined && !hasMetadataVersion(skill.content)) {
5042
- return injectMetadataVersion(skill.content, skill.skillVersion);
5043
- }
5044
- return skill.content;
5045
- }
5046
- function hasMetadataVersion(content) {
5047
- return parseSkillVersion(content) !== null;
5048
- }
5049
- function injectMetadataVersion(content, version) {
5050
- const fmMatch = content.match(/^(---\n[\s\S]*?\n)(---\n)([\s\S]*)$/);
5051
- if (!fmMatch)
5052
- return content;
5053
- const [, head, close, body] = fmMatch;
5054
- const block = `metadata:
5055
- version: "${version}"
5056
- `;
5057
- return `${head}${block}${close}${body}`;
5058
- }
5059
- function parseSkillVersion(content) {
5060
- const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
5061
- if (fmMatch) {
5062
- const fm = fmMatch[1];
5063
- const verMatch = fm.match(/^metadata:[\s\S]*?\n[ \t]+version:[ \t]*["']?(\d+)["']?\s*$/m);
5064
- if (verMatch)
5065
- return verMatch[1];
5066
- }
5067
- const legacy = content.match(/<!-- skills-version:(\d+) -->/);
5068
- return legacy ? legacy[1] : null;
5069
- }
5070
- function findSkillFiles(paths, knownNames) {
5071
- const results = [];
5072
- for (const filePath of paths) {
5073
- if (!existsSync3(filePath))
5074
- continue;
5075
- for (const name of knownNames) {
5076
- if (filePath.includes(`/${name}/`) || filePath.includes(`/${name}.md`)) {
5077
- results.push({ name, filePath });
5078
- break;
5079
- }
5080
- }
5081
- }
5082
- return results;
5083
- }
5084
- async function refreshSkills() {
5085
- try {
5086
- if (!isConfigured())
5087
- return;
5088
- const status = areSkillsInstalled();
5089
- if (!status.installed)
5090
- return;
5091
- const client3 = new HarmonyApiClient;
5092
- const versionInfo = await client3.fetchSkillsVersion();
5093
- const skillFiles = findSkillFiles(status.paths, versionInfo.skills);
5094
- if (skillFiles.length > 0) {
5095
- const samplePath = skillFiles[0].filePath;
5096
- for (const name of versionInfo.skills) {
5097
- if (skillFiles.some((sf) => sf.name === name))
5098
- continue;
5099
- let siblingPath;
5100
- if (samplePath.endsWith("SKILL.md")) {
5101
- const parentDir = dirname(dirname(samplePath));
5102
- siblingPath = `${parentDir}/${name}/SKILL.md`;
5103
- } else {
5104
- const parentDir = dirname(samplePath);
5105
- siblingPath = `${parentDir}/${name}.md`;
5106
- }
5107
- if (existsSync3(siblingPath)) {
5108
- skillFiles.push({ name, filePath: siblingPath });
5109
- }
5110
- }
5111
- }
5112
- if (skillFiles.length === 0)
5113
- return;
5114
- let updated = false;
5115
- for (const { name, filePath } of skillFiles) {
5116
- try {
5117
- const currentContent = readFileSync3(filePath, "utf-8");
5118
- const localVersion = parseSkillVersion(currentContent);
5119
- const fetched = await client3.fetchSkill(name);
5120
- const remoteVersion = fetched.skillVersion;
5121
- if (remoteVersion !== undefined && localVersion !== null && Number(localVersion) >= remoteVersion) {
5122
- continue;
5123
- }
5124
- const newContent = buildSkillFile(fetched);
5125
- const dir = dirname(filePath);
5126
- if (!existsSync3(dir))
5127
- mkdirSync3(dir, { recursive: true });
5128
- writeFileSync3(filePath, newContent);
5129
- updated = true;
5130
- } catch (err) {
5131
- const msg = err instanceof Error ? err.message : String(err);
5132
- console.error(`Harmony: skill "${name}" refresh failed: ${msg}`);
5133
- }
5134
- }
5135
- if (updated) {
5136
- console.error("Harmony: Refreshed skills from server");
5137
- }
5138
- } catch {}
5139
- }
5140
-
5141
5258
  // src/tui/setup.ts
5142
5259
  import { createHash as createHash3 } from "node:crypto";
5143
5260
  import {
5144
- existsSync as existsSync7,
5261
+ existsSync as existsSync8,
5145
5262
  lstatSync,
5146
5263
  mkdirSync as mkdirSync5,
5147
5264
  symlinkSync,
5148
5265
  unlinkSync
5149
5266
  } from "node:fs";
5150
- import { homedir as homedir4 } from "node:os";
5151
- import { dirname as dirname3, join as join5 } from "node:path";
5267
+ import { homedir as homedir6 } from "node:os";
5268
+ import { dirname as dirname3, join as join7 } from "node:path";
5152
5269
  import * as p3 from "@clack/prompts";
5153
5270
 
5154
5271
  // src/tui/agents.ts
5155
- import { existsSync as existsSync4 } from "node:fs";
5156
- import { homedir as homedir2 } from "node:os";
5157
- import { join as join3 } from "node:path";
5272
+ import { existsSync as existsSync5 } from "node:fs";
5273
+ import { homedir as homedir4 } from "node:os";
5274
+ import { join as join5 } from "node:path";
5158
5275
  var AGENT_DEFINITIONS = [
5159
5276
  {
5160
5277
  id: "claude",
5161
5278
  name: "Claude Code",
5162
5279
  description: "Anthropic CLI agent",
5163
5280
  hint: "/hmy <card>",
5164
- globalPaths: [join3(homedir2(), ".claude")],
5281
+ globalPaths: [join5(homedir4(), ".claude")],
5165
5282
  localPaths: [".claude"]
5166
5283
  },
5167
5284
  {
@@ -5169,7 +5286,7 @@ var AGENT_DEFINITIONS = [
5169
5286
  name: "Codex",
5170
5287
  description: "OpenAI coding agent",
5171
5288
  hint: "/prompts:hmy <card>",
5172
- globalPaths: [join3(homedir2(), ".codex")],
5289
+ globalPaths: [join5(homedir4(), ".codex")],
5173
5290
  localPaths: ["AGENTS.md"]
5174
5291
  },
5175
5292
  {
@@ -5185,20 +5302,20 @@ var AGENT_DEFINITIONS = [
5185
5302
  name: "Windsurf",
5186
5303
  description: "Codeium AI IDE",
5187
5304
  hint: "MCP tools available automatically",
5188
- globalPaths: [join3(homedir2(), ".codeium", "windsurf")],
5305
+ globalPaths: [join5(homedir4(), ".codeium", "windsurf")],
5189
5306
  localPaths: [".windsurf", ".windsurfrules"]
5190
5307
  }
5191
5308
  ];
5192
5309
  function detectAgents(cwd = process.cwd()) {
5193
5310
  return AGENT_DEFINITIONS.map((def) => {
5194
- const globalPath = def.globalPaths.find((p) => existsSync4(p)) || null;
5195
- const localPath = def.localPaths.find((p) => existsSync4(join3(cwd, p))) || null;
5311
+ const globalPath = def.globalPaths.find((p) => existsSync5(p)) || null;
5312
+ const localPath = def.localPaths.find((p) => existsSync5(join5(cwd, p))) || null;
5196
5313
  return {
5197
5314
  id: def.id,
5198
5315
  name: def.name,
5199
5316
  detected: !!(globalPath || localPath),
5200
5317
  globalPath,
5201
- localPath: localPath ? join3(cwd, localPath) : null,
5318
+ localPath: localPath ? join5(cwd, localPath) : null,
5202
5319
  description: def.description,
5203
5320
  hint: def.hint
5204
5321
  };
@@ -5206,8 +5323,8 @@ function detectAgents(cwd = process.cwd()) {
5206
5323
  }
5207
5324
 
5208
5325
  // src/tui/docs.ts
5209
- import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync } from "node:fs";
5210
- import { isAbsolute, join as join4, resolve, sep as sep2 } from "node:path";
5326
+ import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync } from "node:fs";
5327
+ import { isAbsolute, join as join6, resolve, sep as sep2 } from "node:path";
5211
5328
  import * as p from "@clack/prompts";
5212
5329
 
5213
5330
  // src/tui/theme.ts
@@ -5294,14 +5411,14 @@ var IGNORED_DIRS = new Set([
5294
5411
  ]);
5295
5412
  function readJson(filePath) {
5296
5413
  try {
5297
- return JSON.parse(readFileSync4(filePath, "utf-8"));
5414
+ return JSON.parse(readFileSync5(filePath, "utf-8"));
5298
5415
  } catch {
5299
5416
  return null;
5300
5417
  }
5301
5418
  }
5302
5419
  function readText(filePath) {
5303
5420
  try {
5304
- return readFileSync4(filePath, "utf-8");
5421
+ return readFileSync5(filePath, "utf-8");
5305
5422
  } catch {
5306
5423
  return null;
5307
5424
  }
@@ -5312,7 +5429,7 @@ function listDirs(dirPath) {
5312
5429
  if (IGNORED_DIRS.has(entry) || entry.startsWith("."))
5313
5430
  return false;
5314
5431
  try {
5315
- return statSync(join4(dirPath, entry)).isDirectory();
5432
+ return statSync(join6(dirPath, entry)).isDirectory();
5316
5433
  } catch {
5317
5434
  return false;
5318
5435
  }
@@ -5360,25 +5477,25 @@ function describeDir(name) {
5360
5477
  }
5361
5478
  function scanProject(cwd) {
5362
5479
  let packageManager = null;
5363
- if (existsSync5(join4(cwd, "bun.lock")) || existsSync5(join4(cwd, "bun.lockb"))) {
5480
+ if (existsSync6(join6(cwd, "bun.lock")) || existsSync6(join6(cwd, "bun.lockb"))) {
5364
5481
  packageManager = "bun";
5365
- } else if (existsSync5(join4(cwd, "pnpm-lock.yaml"))) {
5482
+ } else if (existsSync6(join6(cwd, "pnpm-lock.yaml"))) {
5366
5483
  packageManager = "pnpm";
5367
- } else if (existsSync5(join4(cwd, "yarn.lock"))) {
5484
+ } else if (existsSync6(join6(cwd, "yarn.lock"))) {
5368
5485
  packageManager = "yarn";
5369
- } else if (existsSync5(join4(cwd, "package.json"))) {
5486
+ } else if (existsSync6(join6(cwd, "package.json"))) {
5370
5487
  packageManager = "npm";
5371
5488
  }
5372
- const pkg = readJson(join4(cwd, "package.json"));
5489
+ const pkg = readJson(join6(cwd, "package.json"));
5373
5490
  const scripts = pkg && typeof pkg.scripts === "object" && pkg.scripts !== null ? pkg.scripts : {};
5374
5491
  let language = "unknown";
5375
- if (existsSync5(join4(cwd, "tsconfig.json"))) {
5492
+ if (existsSync6(join6(cwd, "tsconfig.json"))) {
5376
5493
  language = "typescript";
5377
- } else if (existsSync5(join4(cwd, "go.mod"))) {
5494
+ } else if (existsSync6(join6(cwd, "go.mod"))) {
5378
5495
  language = "go";
5379
- } else if (existsSync5(join4(cwd, "Cargo.toml"))) {
5496
+ } else if (existsSync6(join6(cwd, "Cargo.toml"))) {
5380
5497
  language = "rust";
5381
- } else if (existsSync5(join4(cwd, "setup.py")) || existsSync5(join4(cwd, "pyproject.toml"))) {
5498
+ } else if (existsSync6(join6(cwd, "setup.py")) || existsSync6(join6(cwd, "pyproject.toml"))) {
5382
5499
  language = "python";
5383
5500
  } else if (pkg) {
5384
5501
  language = "javascript";
@@ -5414,7 +5531,7 @@ function scanProject(cwd) {
5414
5531
  }
5415
5532
  }
5416
5533
  let linter = null;
5417
- if (existsSync5(join4(cwd, "biome.json")) || existsSync5(join4(cwd, "biome.jsonc"))) {
5534
+ if (existsSync6(join6(cwd, "biome.json")) || existsSync6(join6(cwd, "biome.jsonc"))) {
5418
5535
  linter = "biome";
5419
5536
  } else {
5420
5537
  const eslintFiles = [
@@ -5429,7 +5546,7 @@ function scanProject(cwd) {
5429
5546
  "eslint.config.cjs",
5430
5547
  "eslint.config.ts"
5431
5548
  ];
5432
- if (eslintFiles.some((f) => existsSync5(join4(cwd, f)))) {
5549
+ if (eslintFiles.some((f) => existsSync6(join6(cwd, f)))) {
5433
5550
  linter = "eslint";
5434
5551
  } else {
5435
5552
  const prettierFiles = [
@@ -5441,13 +5558,13 @@ function scanProject(cwd) {
5441
5558
  "prettier.config.js",
5442
5559
  "prettier.config.mjs"
5443
5560
  ];
5444
- if (prettierFiles.some((f) => existsSync5(join4(cwd, f)))) {
5561
+ if (prettierFiles.some((f) => existsSync6(join6(cwd, f)))) {
5445
5562
  linter = "prettier";
5446
5563
  }
5447
5564
  }
5448
5565
  }
5449
5566
  let indentStyle = null;
5450
- const biome = readJson(join4(cwd, "biome.json")) ?? readJson(join4(cwd, "biome.jsonc"));
5567
+ const biome = readJson(join6(cwd, "biome.json")) ?? readJson(join6(cwd, "biome.jsonc"));
5451
5568
  if (biome) {
5452
5569
  const formatter = biome.formatter;
5453
5570
  if (formatter) {
@@ -5457,7 +5574,7 @@ function scanProject(cwd) {
5457
5574
  }
5458
5575
  }
5459
5576
  if (!indentStyle) {
5460
- const editorConfig = readText(join4(cwd, ".editorconfig"));
5577
+ const editorConfig = readText(join6(cwd, ".editorconfig"));
5461
5578
  if (editorConfig) {
5462
5579
  const styleMatch = editorConfig.match(/indent_style\s*=\s*(space|tab)/);
5463
5580
  const sizeMatch = editorConfig.match(/indent_size\s*=\s*(\d+)/);
@@ -5470,13 +5587,13 @@ function scanProject(cwd) {
5470
5587
  }
5471
5588
  }
5472
5589
  const dirs = listDirs(cwd);
5473
- const srcDirs = existsSync5(join4(cwd, "src")) ? listDirs(join4(cwd, "src")) : [];
5474
- const monorepo = existsSync5(join4(cwd, "packages")) || existsSync5(join4(cwd, "apps"));
5590
+ const srcDirs = existsSync6(join6(cwd, "src")) ? listDirs(join6(cwd, "src")) : [];
5591
+ const monorepo = existsSync6(join6(cwd, "packages")) || existsSync6(join6(cwd, "apps"));
5475
5592
  const existingDocs = {
5476
- agentsMd: existsSync5(join4(cwd, "AGENTS.md")),
5477
- claudeMd: existsSync5(join4(cwd, "CLAUDE.md")),
5478
- docsDir: existsSync5(join4(cwd, "docs")),
5479
- architectureMd: existsSync5(join4(cwd, "docs", "architecture.md"))
5593
+ agentsMd: existsSync6(join6(cwd, "AGENTS.md")),
5594
+ claudeMd: existsSync6(join6(cwd, "CLAUDE.md")),
5595
+ docsDir: existsSync6(join6(cwd, "docs")),
5596
+ architectureMd: existsSync6(join6(cwd, "docs", "architecture.md"))
5480
5597
  };
5481
5598
  return {
5482
5599
  packageManager,
@@ -5627,9 +5744,9 @@ var VAGUE_STANDARDS = [
5627
5744
  ];
5628
5745
  function verifyDocs(cwd) {
5629
5746
  const issues = [];
5630
- const claudeMd = readText(join4(cwd, "CLAUDE.md"));
5631
- const agentsMd = readText(join4(cwd, "AGENTS.md"));
5632
- const pkg = readJson(join4(cwd, "package.json"));
5747
+ const claudeMd = readText(join6(cwd, "CLAUDE.md"));
5748
+ const agentsMd = readText(join6(cwd, "AGENTS.md"));
5749
+ const pkg = readJson(join6(cwd, "package.json"));
5633
5750
  const pkgScripts = pkg && typeof pkg.scripts === "object" && pkg.scripts !== null ? pkg.scripts : {};
5634
5751
  const projectRoot = resolve(cwd);
5635
5752
  if (claudeMd) {
@@ -5659,7 +5776,7 @@ function verifyDocs(cwd) {
5659
5776
  continue;
5660
5777
  }
5661
5778
  importedFiles.push({ ref: refPath, resolved: resolvedPath });
5662
- if (!existsSync5(resolvedPath)) {
5779
+ if (!existsSync6(resolvedPath)) {
5663
5780
  issues.push({
5664
5781
  severity: "error",
5665
5782
  file: "CLAUDE.md",
@@ -5795,7 +5912,7 @@ function verifyDocs(cwd) {
5795
5912
  }
5796
5913
  checkBacktickPaths(agentsMd, "AGENTS.md", cwd, issues);
5797
5914
  }
5798
- const archMd = readText(join4(cwd, "docs", "architecture.md"));
5915
+ const archMd = readText(join6(cwd, "docs", "architecture.md"));
5799
5916
  if (archMd) {
5800
5917
  checkBacktickPaths(archMd, "docs/architecture.md", cwd, issues);
5801
5918
  }
@@ -5842,7 +5959,7 @@ function checkBacktickPaths(content, file, cwd, issues) {
5842
5959
  const resolvedRef = resolve(root, refPath);
5843
5960
  if (resolvedRef !== root && !resolvedRef.startsWith(root + sep2))
5844
5961
  continue;
5845
- if (!existsSync5(resolvedRef)) {
5962
+ if (!existsSync6(resolvedRef)) {
5846
5963
  issues.push({
5847
5964
  severity: "warning",
5848
5965
  file,
@@ -5865,18 +5982,18 @@ async function runDocsStep(cwd) {
5865
5982
  }
5866
5983
  const files = [];
5867
5984
  files.push({
5868
- path: join4(cwd, "AGENTS.md"),
5985
+ path: join6(cwd, "AGENTS.md"),
5869
5986
  content: generateAgentsMd(info, cwd),
5870
5987
  type: "text"
5871
5988
  });
5872
5989
  files.push({
5873
- path: join4(cwd, "CLAUDE.md"),
5990
+ path: join6(cwd, "CLAUDE.md"),
5874
5991
  content: generateClaudeMd(info),
5875
5992
  type: "text"
5876
5993
  });
5877
5994
  if (info.dirs.includes("docs") || info.srcDirs.length > 0) {
5878
5995
  files.push({
5879
- path: join4(cwd, "docs", "architecture.md"),
5996
+ path: join6(cwd, "docs", "architecture.md"),
5880
5997
  content: generateArchitectureMd(info, cwd),
5881
5998
  type: "text"
5882
5999
  });
@@ -5914,21 +6031,21 @@ async function runDocsStep(cwd) {
5914
6031
  // src/tui/writer.ts
5915
6032
  import {
5916
6033
  chmodSync,
5917
- existsSync as existsSync6,
6034
+ existsSync as existsSync7,
5918
6035
  mkdirSync as mkdirSync4,
5919
- readFileSync as readFileSync5,
6036
+ readFileSync as readFileSync6,
5920
6037
  writeFileSync as writeFileSync4
5921
6038
  } from "node:fs";
5922
- import { homedir as homedir3 } from "node:os";
6039
+ import { homedir as homedir5 } from "node:os";
5923
6040
  import { dirname as dirname2 } from "node:path";
5924
6041
  import * as p2 from "@clack/prompts";
5925
6042
  function ensureDir(dirPath) {
5926
- if (!existsSync6(dirPath)) {
6043
+ if (!existsSync7(dirPath)) {
5927
6044
  mkdirSync4(dirPath, { recursive: true, mode: 493 });
5928
6045
  }
5929
6046
  }
5930
6047
  function writeFile(filePath, content, options = {}) {
5931
- const exists = existsSync6(filePath);
6048
+ const exists = existsSync7(filePath);
5932
6049
  if (exists && !options.force) {
5933
6050
  return { path: filePath, action: "skip" };
5934
6051
  }
@@ -5950,7 +6067,7 @@ function writeFile(filePath, content, options = {}) {
5950
6067
  }
5951
6068
  }
5952
6069
  function mergeJsonFile(filePath, updates, options = {}) {
5953
- const exists = existsSync6(filePath);
6070
+ const exists = existsSync7(filePath);
5954
6071
  if (!exists) {
5955
6072
  try {
5956
6073
  ensureDir(dirname2(filePath));
@@ -5967,7 +6084,7 @@ function mergeJsonFile(filePath, updates, options = {}) {
5967
6084
  }
5968
6085
  }
5969
6086
  try {
5970
- const existing = JSON.parse(readFileSync5(filePath, "utf-8"));
6087
+ const existing = JSON.parse(readFileSync6(filePath, "utf-8"));
5971
6088
  if (updates.mcpServers && existing.mcpServers) {
5972
6089
  const existingServers = existing.mcpServers;
5973
6090
  const updateServers = updates.mcpServers;
@@ -6000,7 +6117,7 @@ function mergeJsonFile(filePath, updates, options = {}) {
6000
6117
  }
6001
6118
  }
6002
6119
  function appendToToml(filePath, section, content, options = {}) {
6003
- const exists = existsSync6(filePath);
6120
+ const exists = existsSync7(filePath);
6004
6121
  if (!exists) {
6005
6122
  try {
6006
6123
  ensureDir(dirname2(filePath));
@@ -6015,7 +6132,7 @@ function appendToToml(filePath, section, content, options = {}) {
6015
6132
  }
6016
6133
  }
6017
6134
  try {
6018
- const existing = readFileSync5(filePath, "utf-8");
6135
+ const existing = readFileSync6(filePath, "utf-8");
6019
6136
  if (existing.includes(section)) {
6020
6137
  if (options.force) {
6021
6138
  const updated = existing.replace(new RegExp(`\\[${section.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\][\\s\\S]*?(?=\\[|$)`), content.trim() + `
@@ -6039,7 +6156,7 @@ function appendToToml(filePath, section, content, options = {}) {
6039
6156
  }
6040
6157
  async function writeFilesWithProgress(files, options = {}) {
6041
6158
  const results = [];
6042
- const home = homedir3();
6159
+ const home = homedir5();
6043
6160
  const spinner2 = p2.spinner();
6044
6161
  spinner2.start("Writing configuration files...");
6045
6162
  for (const file of files) {
@@ -6076,10 +6193,10 @@ function getWriteSummary(files, options = {}) {
6076
6193
  const toCreate = [];
6077
6194
  const toUpdate = [];
6078
6195
  const toSkip = [];
6079
- const home = homedir3();
6196
+ const home = homedir5();
6080
6197
  for (const file of files) {
6081
6198
  const displayPath = formatPath(file.path, home);
6082
- const exists = existsSync6(file.path);
6199
+ const exists = existsSync7(file.path);
6083
6200
  if (exists && !options.force) {
6084
6201
  toSkip.push(displayPath);
6085
6202
  } else if (exists) {
@@ -6092,7 +6209,7 @@ function getWriteSummary(files, options = {}) {
6092
6209
  }
6093
6210
 
6094
6211
  // src/tui/setup.ts
6095
- var GLOBAL_SKILLS_DIR = join5(homedir4(), ".agents", "skills");
6212
+ var GLOBAL_SKILLS_DIR = join7(homedir6(), ".agents", "skills");
6096
6213
  var API_URL = "https://app.gethmy.com/api";
6097
6214
  async function registerMcpServer() {
6098
6215
  try {
@@ -6106,16 +6223,16 @@ async function registerMcpServer() {
6106
6223
  }
6107
6224
  }
6108
6225
  async function writeMcpConfigFallback(home) {
6109
- const { readFileSync: readFileSync6, writeFileSync: writeFileSync5, mkdirSync: mkdirSync6, existsSync: existsSync8 } = await import("node:fs");
6110
- const settingsPath = join5(home, ".claude", "settings.json");
6226
+ const { readFileSync: readFileSync7, writeFileSync: writeFileSync5, mkdirSync: mkdirSync6, existsSync: existsSync9 } = await import("node:fs");
6227
+ const settingsPath = join7(home, ".claude", "settings.json");
6111
6228
  const settingsDir = dirname3(settingsPath);
6112
- if (!existsSync8(settingsDir)) {
6229
+ if (!existsSync9(settingsDir)) {
6113
6230
  mkdirSync6(settingsDir, { recursive: true });
6114
6231
  }
6115
6232
  let settings = {};
6116
- if (existsSync8(settingsPath)) {
6233
+ if (existsSync9(settingsPath)) {
6117
6234
  try {
6118
- settings = JSON.parse(readFileSync6(settingsPath, "utf-8"));
6235
+ settings = JSON.parse(readFileSync7(settingsPath, "utf-8"));
6119
6236
  } catch {}
6120
6237
  }
6121
6238
  const mcpServers = settings.mcpServers || {};
@@ -6206,7 +6323,7 @@ async function resolveProjectSlug(apiKey, slug) {
6206
6323
  return { workspaceId: data.workspaceId, projectId: data.projectId };
6207
6324
  }
6208
6325
  async function getAgentFiles(agentId, cwd, installMode = "global") {
6209
- const home = homedir4();
6326
+ const home = homedir6();
6210
6327
  const files = [];
6211
6328
  const symlinks = [];
6212
6329
  switch (agentId) {
@@ -6221,17 +6338,17 @@ async function getAgentFiles(agentId, cwd, installMode = "global") {
6221
6338
  const content = buildSkillFile(fetched);
6222
6339
  if (installMode === "global") {
6223
6340
  files.push({
6224
- path: join5(GLOBAL_SKILLS_DIR, name, "SKILL.md"),
6341
+ path: join7(GLOBAL_SKILLS_DIR, name, "SKILL.md"),
6225
6342
  content,
6226
6343
  type: "text"
6227
6344
  });
6228
6345
  symlinks.push({
6229
- target: join5(GLOBAL_SKILLS_DIR, name),
6230
- link: join5(home, ".claude", "skills", name)
6346
+ target: join7(GLOBAL_SKILLS_DIR, name),
6347
+ link: join7(home, ".claude", "skills", name)
6231
6348
  });
6232
6349
  } else {
6233
6350
  files.push({
6234
- path: join5(cwd, ".claude", "skills", name, "SKILL.md"),
6351
+ path: join7(cwd, ".claude", "skills", name, "SKILL.md"),
6235
6352
  content,
6236
6353
  type: "text"
6237
6354
  });
@@ -6254,13 +6371,13 @@ ${summary}`);
6254
6371
  throw new Error(`hmy-update-check integrity check failed: expected ${updateCheckFetched.sha256}, got ${actualHash}`);
6255
6372
  }
6256
6373
  files.push({
6257
- path: join5(home, ".hmy", "bin", "hmy-update-check"),
6374
+ path: join7(home, ".hmy", "bin", "hmy-update-check"),
6258
6375
  content: updateCheckFetched.content,
6259
6376
  type: "text",
6260
6377
  mode: 493
6261
6378
  });
6262
6379
  files.push({
6263
- path: join5(home, ".hmy", "VERSION"),
6380
+ path: join7(home, ".hmy", "VERSION"),
6264
6381
  content: versionInfo.version,
6265
6382
  type: "text"
6266
6383
  });
@@ -6306,7 +6423,7 @@ Skip if: work was already started with a card reference, or no matching card exi
6306
6423
  - \`harmony_generate_prompt\` - Get role-based guidance and focus areas for the card
6307
6424
  `;
6308
6425
  files.push({
6309
- path: join5(cwd, "AGENTS.md"),
6426
+ path: join7(cwd, "AGENTS.md"),
6310
6427
  content: agentsContent,
6311
6428
  type: "text"
6312
6429
  });
@@ -6323,17 +6440,17 @@ ${HARMONY_WORKFLOW_PROMPT.replace("$ARGUMENTS", "{{card}}").replace("Your agent
6323
6440
  `;
6324
6441
  if (installMode === "global") {
6325
6442
  files.push({
6326
- path: join5(GLOBAL_SKILLS_DIR, "codex", "hmy.md"),
6443
+ path: join7(GLOBAL_SKILLS_DIR, "codex", "hmy.md"),
6327
6444
  content: promptContent,
6328
6445
  type: "text"
6329
6446
  });
6330
6447
  symlinks.push({
6331
- target: join5(GLOBAL_SKILLS_DIR, "codex", "hmy.md"),
6332
- link: join5(home, ".codex", "prompts", "hmy.md")
6448
+ target: join7(GLOBAL_SKILLS_DIR, "codex", "hmy.md"),
6449
+ link: join7(home, ".codex", "prompts", "hmy.md")
6333
6450
  });
6334
6451
  } else {
6335
6452
  files.push({
6336
- path: join5(home, ".codex", "prompts", "hmy.md"),
6453
+ path: join7(home, ".codex", "prompts", "hmy.md"),
6337
6454
  content: promptContent,
6338
6455
  type: "text"
6339
6456
  });
@@ -6345,7 +6462,7 @@ command = "npx"
6345
6462
  args = ["-y", "@gethmy/mcp@latest", "serve"]
6346
6463
  `;
6347
6464
  files.push({
6348
- path: join5(home, ".codex", "config.toml"),
6465
+ path: join7(home, ".codex", "config.toml"),
6349
6466
  content: tomlContent,
6350
6467
  type: "toml",
6351
6468
  tomlSection: "mcp_servers.harmony"
@@ -6354,7 +6471,7 @@ args = ["-y", "@gethmy/mcp@latest", "serve"]
6354
6471
  }
6355
6472
  case "cursor": {
6356
6473
  files.push({
6357
- path: join5(cwd, ".cursor", "mcp.json"),
6474
+ path: join7(cwd, ".cursor", "mcp.json"),
6358
6475
  content: JSON.stringify({
6359
6476
  mcpServers: {
6360
6477
  harmony: {
@@ -6380,17 +6497,17 @@ ${HARMONY_WORKFLOW_PROMPT.replace("$ARGUMENTS", "the card reference").replace("Y
6380
6497
  `;
6381
6498
  if (installMode === "global") {
6382
6499
  files.push({
6383
- path: join5(GLOBAL_SKILLS_DIR, "cursor", "harmony.mdc"),
6500
+ path: join7(GLOBAL_SKILLS_DIR, "cursor", "harmony.mdc"),
6384
6501
  content: ruleContent,
6385
6502
  type: "text"
6386
6503
  });
6387
6504
  symlinks.push({
6388
- target: join5(GLOBAL_SKILLS_DIR, "cursor", "harmony.mdc"),
6389
- link: join5(home, ".cursor", "rules", "harmony.mdc")
6505
+ target: join7(GLOBAL_SKILLS_DIR, "cursor", "harmony.mdc"),
6506
+ link: join7(home, ".cursor", "rules", "harmony.mdc")
6390
6507
  });
6391
6508
  } else {
6392
6509
  files.push({
6393
- path: join5(cwd, ".cursor", "rules", "harmony.mdc"),
6510
+ path: join7(cwd, ".cursor", "rules", "harmony.mdc"),
6394
6511
  content: ruleContent,
6395
6512
  type: "text"
6396
6513
  });
@@ -6399,7 +6516,7 @@ ${HARMONY_WORKFLOW_PROMPT.replace("$ARGUMENTS", "the card reference").replace("Y
6399
6516
  }
6400
6517
  case "windsurf": {
6401
6518
  files.push({
6402
- path: join5(home, ".codeium", "windsurf", "mcp_config.json"),
6519
+ path: join7(home, ".codeium", "windsurf", "mcp_config.json"),
6403
6520
  content: JSON.stringify({
6404
6521
  mcpServers: {
6405
6522
  harmony: {
@@ -6425,17 +6542,17 @@ ${HARMONY_WORKFLOW_PROMPT.replace("$ARGUMENTS", "the card reference").replace("Y
6425
6542
  `;
6426
6543
  if (installMode === "global") {
6427
6544
  files.push({
6428
- path: join5(GLOBAL_SKILLS_DIR, "windsurf", "harmony.md"),
6545
+ path: join7(GLOBAL_SKILLS_DIR, "windsurf", "harmony.md"),
6429
6546
  content: ruleContent,
6430
6547
  type: "text"
6431
6548
  });
6432
6549
  symlinks.push({
6433
- target: join5(GLOBAL_SKILLS_DIR, "windsurf", "harmony.md"),
6434
- link: join5(home, ".codeium", "windsurf", "rules", "harmony.md")
6550
+ target: join7(GLOBAL_SKILLS_DIR, "windsurf", "harmony.md"),
6551
+ link: join7(home, ".codeium", "windsurf", "rules", "harmony.md")
6435
6552
  });
6436
6553
  } else {
6437
6554
  files.push({
6438
- path: join5(cwd, ".windsurf", "rules", "harmony.md"),
6555
+ path: join7(cwd, ".windsurf", "rules", "harmony.md"),
6439
6556
  content: ruleContent,
6440
6557
  type: "text"
6441
6558
  });
@@ -6447,7 +6564,7 @@ ${HARMONY_WORKFLOW_PROMPT.replace("$ARGUMENTS", "the card reference").replace("Y
6447
6564
  }
6448
6565
  async function runSetup(options = {}) {
6449
6566
  const cwd = process.cwd();
6450
- const home = homedir4();
6567
+ const home = homedir6();
6451
6568
  console.clear();
6452
6569
  console.log(messages.header());
6453
6570
  const existingConfig = loadConfig();
@@ -6855,7 +6972,7 @@ async function runSetup(options = {}) {
6855
6972
  for (const symlink of allSymlinks) {
6856
6973
  try {
6857
6974
  const linkDir = dirname3(symlink.link);
6858
- if (!existsSync7(linkDir)) {
6975
+ if (!existsSync8(linkDir)) {
6859
6976
  mkdirSync5(linkDir, { recursive: true });
6860
6977
  }
6861
6978
  let linkExists = false;
@@ -6884,7 +7001,7 @@ async function runSetup(options = {}) {
6884
7001
  } else {
6885
7002
  try {
6886
7003
  await writeMcpConfigFallback(home);
6887
- console.log(` ${colors.success("✓")} ${colors.dim(formatPath(join5(home, ".claude", "settings.json"), home))} ${colors.dim("(updated)")}`);
7004
+ console.log(` ${colors.success("✓")} ${colors.dim(formatPath(join7(home, ".claude", "settings.json"), home))} ${colors.dim("(updated)")}`);
6888
7005
  } catch {
6889
7006
  p3.log.warning("Could not register MCP server. Run manually: claude mcp add --transport stdio harmony -- npx -y @gethmy/mcp@latest serve");
6890
7007
  }
@@ -6951,9 +7068,9 @@ program.command("serve").description("Start the MCP server (stdio transport)").a
6951
7068
  console.error("Run: npx @gethmy/mcp setup");
6952
7069
  process.exit(1);
6953
7070
  }
6954
- await refreshSkills();
7071
+ const { updated } = await refreshSkills();
6955
7072
  const server = new HarmonyMCPServer;
6956
- await server.run();
7073
+ await server.run({ skillsUpdated: updated });
6957
7074
  });
6958
7075
  program.command("status").description("Show configuration status").action(() => {
6959
7076
  const globalConfig = loadConfig();