harmony-mcp 1.6.0 → 1.6.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/dist/init.js +0 -624
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "harmony-mcp",
3
- "version": "1.6.0",
3
+ "version": "1.6.2",
4
4
  "description": "MCP server for Harmony Kanban board - enables AI coding agents to manage your boards",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/dist/init.js DELETED
@@ -1,624 +0,0 @@
1
- import { createRequire } from "node:module";
2
- var __create = Object.create;
3
- var __getProtoOf = Object.getPrototypeOf;
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __toESM = (mod, isNodeMode, target) => {
8
- target = mod != null ? __create(__getProtoOf(mod)) : {};
9
- const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
- for (let key of __getOwnPropNames(mod))
11
- if (!__hasOwnProp.call(to, key))
12
- __defProp(to, key, {
13
- get: () => mod[key],
14
- enumerable: true
15
- });
16
- return to;
17
- };
18
- var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
19
- var __export = (target, all) => {
20
- for (var name in all)
21
- __defProp(target, name, {
22
- get: all[name],
23
- enumerable: true,
24
- configurable: true,
25
- set: (newValue) => all[name] = () => newValue
26
- });
27
- };
28
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
29
-
30
- // src/init.ts
31
- import { existsSync, mkdirSync, writeFileSync, readFileSync, symlinkSync, unlinkSync, lstatSync } from "node:fs";
32
- import { join, dirname } from "node:path";
33
- import { homedir } from "node:os";
34
- var HARMONY_WORKFLOW_PROMPT = `# Harmony Card Workflow
35
-
36
- Start work on a Harmony card. Card reference: $ARGUMENTS
37
-
38
- ## 1. Find & Fetch Card
39
-
40
- Parse the reference and fetch the card:
41
- - \`#42\` or \`42\` → \`harmony_get_card_by_short_id\` with \`shortId: 42\`
42
- - UUID → \`harmony_get_card\` with \`cardId\`
43
- - Name/text → \`harmony_search_cards\` with \`query\`
44
-
45
- ## 2. Get Board State
46
-
47
- Call \`harmony_get_board\` to get columns and labels. From the response:
48
- - Find the "In Progress" (or "Progress") column ID
49
- - Find the "agent" label ID
50
-
51
- ## 3. Setup Card for Work
52
-
53
- Execute these in sequence:
54
- 1. \`harmony_move_card\` → Move to "In Progress" column
55
- 2. \`harmony_add_label_to_card\` → Add "agent" label
56
- 3. \`harmony_start_agent_session\`:
57
- - \`cardId\`: Card UUID
58
- - \`agentIdentifier\`: Your agent identifier
59
- - \`agentName\`: Your agent name
60
- - \`currentTask\`: "Analyzing card requirements"
61
-
62
- ## 4. Generate Work Prompt
63
-
64
- Call \`harmony_generate_prompt\` with:
65
- - \`cardId\` or \`shortId\` (+ \`projectId\` if using shortId)
66
- - \`variant\`: Select based on task:
67
- - \`"execute"\` (default) → Clear tasks, bug fixes, well-defined work
68
- - \`"analysis"\` → Complex features, unclear requirements
69
- - \`"draft"\` → Medium complexity, want feedback first
70
-
71
- The generated prompt provides role framing, focus areas, subtasks, linked cards, and suggested outputs.
72
-
73
- ## 5. Display Card Summary
74
-
75
- Show the user: Card title, short ID, role, priority, labels, due date, description, and subtasks.
76
-
77
- ## 6. Implement Solution
78
-
79
- Work on the card following the generated prompt's guidance. Update progress at milestones:
80
- - \`harmony_update_agent_progress\` with \`progressPercent\` (0-100), \`currentTask\`, \`status\`, \`blockers\`
81
-
82
- **Progress checkpoints:** 20% (exploration), 50% (implementation), 80% (testing), 100% (done)
83
-
84
- ## 7. Complete Work
85
-
86
- When finished:
87
- 1. \`harmony_end_agent_session\` with \`status: "completed"\`, \`progressPercent: 100\`
88
- 2. \`harmony_move_card\` to "Review" column
89
- 3. Summarize accomplishments
90
-
91
- If pausing: \`harmony_end_agent_session\` with \`status: "paused"\`
92
-
93
- ## Key Tools Reference
94
-
95
- **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\`
96
-
97
- **Subtasks:** \`harmony_create_subtask\`, \`harmony_toggle_subtask\`, \`harmony_delete_subtask\`
98
-
99
- **Labels:** \`harmony_add_label_to_card\`, \`harmony_remove_label_from_card\`, \`harmony_create_label\`
100
-
101
- **Links:** \`harmony_add_link_to_card\`, \`harmony_remove_link_from_card\`, \`harmony_get_card_links\`
102
-
103
- **Board:** \`harmony_get_board\`, \`harmony_list_projects\`, \`harmony_get_context\`, \`harmony_set_project_context\`
104
-
105
- **Sessions:** \`harmony_start_agent_session\`, \`harmony_update_agent_progress\`, \`harmony_end_agent_session\`, \`harmony_get_agent_session\`
106
-
107
- **AI:** \`harmony_generate_prompt\`, \`harmony_process_command\`
108
- `;
109
- var HARMONY_STANDUP_PROMPT = `# Harmony Daily Standup
110
-
111
- Generate a daily standup summary for the current project.
112
-
113
- ## 1. Get Board State
114
-
115
- Call \`harmony_get_board\` to get the full board state including:
116
- - All columns and their cards
117
- - Card priorities, assignees, and due dates
118
- - Active agent sessions
119
-
120
- ## 2. Analyze Board
121
-
122
- Organize the information into standup categories:
123
-
124
- ### What was completed recently
125
- - Cards in "Done" or "Review" columns
126
- - Cards with recent activity (moved, updated)
127
-
128
- ### What's in progress
129
- - Cards in "In Progress" column
130
- - Cards with active agent sessions (show progress %)
131
- - Who's working on what
132
-
133
- ### What's blocked or at risk
134
- - High-priority cards not in progress
135
- - Overdue cards
136
- - Cards with blockers
137
-
138
- ### What's coming up
139
- - Cards in "To Do" column
140
- - Upcoming due dates
141
-
142
- ## 3. Present Summary
143
-
144
- Format the summary as a clean, readable standup report:
145
- - Use bullet points for easy scanning
146
- - Highlight priorities and blockers
147
- - Include card short IDs for easy reference (e.g., #42)
148
- - Note any agent work in progress
149
- `;
150
- var HARMONY_CLEANUP_PROMPT = `# Harmony Board Cleanup
151
-
152
- Analyze the board and suggest cleanup actions.
153
-
154
- ## 1. Get Board State
155
-
156
- Call \`harmony_get_board\` to get the full board state.
157
-
158
- ## 2. Identify Issues
159
-
160
- Look for:
161
-
162
- ### Stale cards
163
- - Cards in "In Progress" for too long without updates
164
- - Cards with past due dates
165
- - Cards with no recent activity
166
-
167
- ### Organizational issues
168
- - Cards missing priorities
169
- - Cards missing assignees in active columns
170
- - Empty descriptions on complex cards
171
-
172
- ### Potential duplicates
173
- - Cards with similar titles
174
- - Use \`harmony_search_cards\` if needed to find related cards
175
-
176
- ## 3. Suggest Actions
177
-
178
- For each issue found, suggest a specific action:
179
- - Move stale cards back to backlog
180
- - Archive completed cards
181
- - Update missing information
182
- - Merge or link duplicates
183
-
184
- Present suggestions as a prioritized list with:
185
- - Card reference (#ID)
186
- - Current state
187
- - Suggested action
188
- - Why it matters
189
-
190
- ## 4. Optional: Execute Cleanup
191
-
192
- If the user approves, execute the suggested actions:
193
- - Use \`harmony_move_card\` to reorganize
194
- - Use \`harmony_update_card\` to add missing info
195
- - Use \`harmony_add_link_to_card\` to link related cards
196
- `;
197
- function ensureDir(dirPath) {
198
- if (!existsSync(dirPath)) {
199
- mkdirSync(dirPath, { recursive: true });
200
- }
201
- }
202
- function writeFileIfNotExists(filePath, content, force) {
203
- if (existsSync(filePath) && !force) {
204
- return { created: false, skipped: true };
205
- }
206
- ensureDir(dirname(filePath));
207
- writeFileSync(filePath, content, "utf-8");
208
- return { created: true, skipped: false };
209
- }
210
- var GLOBAL_SKILLS_DIR = join(homedir(), ".agents", "skills");
211
- function pathExists(filePath) {
212
- try {
213
- lstatSync(filePath);
214
- return true;
215
- } catch {
216
- return false;
217
- }
218
- }
219
- function createSymlink(target, linkPath, force) {
220
- if (pathExists(linkPath)) {
221
- if (!force) {
222
- return { created: false, skipped: true };
223
- }
224
- try {
225
- unlinkSync(linkPath);
226
- } catch {}
227
- }
228
- ensureDir(dirname(linkPath));
229
- symlinkSync(target, linkPath);
230
- return { created: true, skipped: false };
231
- }
232
- function mergeJsonFile(filePath, updates, force) {
233
- if (!existsSync(filePath)) {
234
- ensureDir(dirname(filePath));
235
- writeFileSync(filePath, JSON.stringify(updates, null, 2), "utf-8");
236
- return { created: true, skipped: false, merged: false };
237
- }
238
- try {
239
- const existing = JSON.parse(readFileSync(filePath, "utf-8"));
240
- if (updates.mcpServers && existing.mcpServers) {
241
- if (existing.mcpServers.harmony && !force) {
242
- return { created: false, skipped: true, merged: false };
243
- }
244
- existing.mcpServers = { ...existing.mcpServers, ...updates.mcpServers };
245
- } else {
246
- Object.assign(existing, updates);
247
- }
248
- writeFileSync(filePath, JSON.stringify(existing, null, 2), "utf-8");
249
- return { created: false, skipped: false, merged: true };
250
- } catch {
251
- if (force) {
252
- writeFileSync(filePath, JSON.stringify(updates, null, 2), "utf-8");
253
- return { created: true, skipped: false, merged: false };
254
- }
255
- return { created: false, skipped: true, merged: false };
256
- }
257
- }
258
- function initClaude(cwd, force, installMode) {
259
- const result = {
260
- agent: "claude",
261
- filesCreated: [],
262
- filesSkipped: [],
263
- symlinksCreated: []
264
- };
265
- const skillContent = `---
266
- name: hmy
267
- description: Start working on a Harmony card. Use when given a card reference like #42, UUID, or card name to implement.
268
- argument-hint: <card-reference>
269
- ---
270
-
271
- ${HARMONY_WORKFLOW_PROMPT.replace("Your agent identifier", "claude-code").replace("Your agent name", "Claude Code")}
272
- `;
273
- const standupContent = `---
274
- name: hmy-standup
275
- description: Generate a daily standup summary. Use when asked for project status, daily update, or standup report.
276
- ---
277
-
278
- ${HARMONY_STANDUP_PROMPT}
279
- `;
280
- const cleanupContent = `---
281
- name: hmy-cleanup
282
- description: Analyze board for stale cards and suggest cleanup. Use when asked to clean up, audit, or organize the board.
283
- ---
284
-
285
- ${HARMONY_CLEANUP_PROMPT}
286
- `;
287
- if (installMode === "global") {
288
- const skills = [
289
- { name: "hmy", content: skillContent },
290
- { name: "hmy-standup", content: standupContent },
291
- { name: "hmy-cleanup", content: cleanupContent }
292
- ];
293
- for (const skill of skills) {
294
- const centralPath = join(GLOBAL_SKILLS_DIR, skill.name, "SKILL.md");
295
- const { created, skipped } = writeFileIfNotExists(centralPath, skill.content, force);
296
- if (created)
297
- result.filesCreated.push(centralPath);
298
- if (skipped)
299
- result.filesSkipped.push(centralPath);
300
- const symlinkPath = join(homedir(), ".claude", "skills", skill.name);
301
- const symlinkTarget = join(GLOBAL_SKILLS_DIR, skill.name);
302
- const symlinkResult = createSymlink(symlinkTarget, symlinkPath, force);
303
- if (symlinkResult.created)
304
- result.symlinksCreated.push(`${symlinkPath} → ${symlinkTarget}`);
305
- if (symlinkResult.skipped)
306
- result.filesSkipped.push(symlinkPath);
307
- }
308
- } else {
309
- const skillPath = join(cwd, ".claude", "skills", "hmy", "SKILL.md");
310
- const { created, skipped } = writeFileIfNotExists(skillPath, skillContent, force);
311
- if (created)
312
- result.filesCreated.push(skillPath);
313
- if (skipped)
314
- result.filesSkipped.push(skillPath);
315
- const standupPath = join(cwd, ".claude", "skills", "hmy-standup", "SKILL.md");
316
- const standupResult = writeFileIfNotExists(standupPath, standupContent, force);
317
- if (standupResult.created)
318
- result.filesCreated.push(standupPath);
319
- if (standupResult.skipped)
320
- result.filesSkipped.push(standupPath);
321
- const cleanupPath = join(cwd, ".claude", "skills", "hmy-cleanup", "SKILL.md");
322
- const cleanupResult = writeFileIfNotExists(cleanupPath, cleanupContent, force);
323
- if (cleanupResult.created)
324
- result.filesCreated.push(cleanupPath);
325
- if (cleanupResult.skipped)
326
- result.filesSkipped.push(cleanupPath);
327
- }
328
- const globalConfigPath = join(homedir(), ".claude", "settings.json");
329
- const mcpConfig = {
330
- mcpServers: {
331
- harmony: {
332
- command: "harmony-mcp",
333
- args: ["serve"]
334
- }
335
- }
336
- };
337
- const configResult = mergeJsonFile(globalConfigPath, mcpConfig, force);
338
- if (configResult.created || configResult.merged) {
339
- result.filesCreated.push(globalConfigPath);
340
- } else if (configResult.skipped) {
341
- result.filesSkipped.push(globalConfigPath);
342
- }
343
- return result;
344
- }
345
- function initCodex(cwd, force, installMode) {
346
- const result = {
347
- agent: "codex",
348
- filesCreated: [],
349
- filesSkipped: [],
350
- symlinksCreated: []
351
- };
352
- const agentsContent = `# Harmony Integration
353
-
354
- This project uses Harmony for task management. When working on tasks:
355
-
356
- ## Starting Work on a Card
357
-
358
- When given a card reference (e.g., #42 or a card name), follow this workflow:
359
-
360
- 1. Use \`harmony_get_card_by_short_id\` or \`harmony_search_cards\` to find the card
361
- 2. Move the card to "In Progress" using \`harmony_move_card\`
362
- 3. Add the "agent" label using \`harmony_add_label_to_card\`
363
- 4. Start a session with \`harmony_start_agent_session\` (agentIdentifier: "codex", agentName: "OpenAI Codex")
364
- 5. Show the card details to the user
365
- 6. Use \`harmony_generate_prompt\` to get guidance, then implement the solution
366
- 7. Update progress periodically with \`harmony_update_agent_progress\`
367
- 8. When done, call \`harmony_end_agent_session\` and move to "Review"
368
-
369
- ## Available Harmony Tools
370
-
371
- - \`harmony_get_card\`, \`harmony_get_card_by_short_id\`, \`harmony_search_cards\` - Find cards
372
- - \`harmony_move_card\` - Move cards between columns
373
- - \`harmony_add_label_to_card\`, \`harmony_remove_label_from_card\` - Manage labels
374
- - \`harmony_start_agent_session\`, \`harmony_update_agent_progress\`, \`harmony_end_agent_session\` - Track work
375
- - \`harmony_get_board\` - Get board state
376
- - \`harmony_generate_prompt\` - Get role-based guidance and focus areas for the card
377
- `;
378
- const promptContent = `---
379
- name: hmy
380
- description: Start working on a Harmony card
381
- arguments:
382
- - name: card
383
- description: Card reference (#42, UUID, or name)
384
- required: true
385
- ---
386
-
387
- ${HARMONY_WORKFLOW_PROMPT.replace("$ARGUMENTS", "{{card}}").replace("Your agent identifier", "codex").replace("Your agent name", "OpenAI Codex")}
388
- `;
389
- if (installMode === "global") {
390
- const centralPromptPath = join(GLOBAL_SKILLS_DIR, "codex", "hmy.md");
391
- const { created, skipped } = writeFileIfNotExists(centralPromptPath, promptContent, force);
392
- if (created)
393
- result.filesCreated.push(centralPromptPath);
394
- if (skipped)
395
- result.filesSkipped.push(centralPromptPath);
396
- const symlinkPath = join(homedir(), ".codex", "prompts", "hmy.md");
397
- const symlinkResult = createSymlink(centralPromptPath, symlinkPath, force);
398
- if (symlinkResult.created)
399
- result.symlinksCreated.push(`${symlinkPath} → ${centralPromptPath}`);
400
- if (symlinkResult.skipped)
401
- result.filesSkipped.push(symlinkPath);
402
- } else {
403
- const promptPath = join(homedir(), ".codex", "prompts", "hmy.md");
404
- const { created, skipped } = writeFileIfNotExists(promptPath, promptContent, force);
405
- if (created)
406
- result.filesCreated.push(promptPath);
407
- if (skipped)
408
- result.filesSkipped.push(promptPath);
409
- }
410
- const agentsPath = join(cwd, "AGENTS.md");
411
- const { created: agentsCreated, skipped: agentsSkipped } = writeFileIfNotExists(agentsPath, agentsContent, force);
412
- if (agentsCreated)
413
- result.filesCreated.push(agentsPath);
414
- if (agentsSkipped)
415
- result.filesSkipped.push(agentsPath);
416
- const configPath = join(homedir(), ".codex", "config.toml");
417
- const mcpConfigSection = `
418
- # Harmony MCP Server
419
- [mcp_servers.harmony]
420
- command = "harmony-mcp"
421
- args = ["serve"]
422
- `;
423
- if (!existsSync(configPath)) {
424
- ensureDir(dirname(configPath));
425
- writeFileSync(configPath, mcpConfigSection, "utf-8");
426
- result.filesCreated.push(configPath);
427
- } else {
428
- const existingConfig = readFileSync(configPath, "utf-8");
429
- if (!existingConfig.includes("[mcp_servers.harmony]")) {
430
- writeFileSync(configPath, existingConfig + `
431
- ` + mcpConfigSection, "utf-8");
432
- result.filesCreated.push(configPath);
433
- } else if (force) {
434
- const updated = existingConfig.replace(/\[mcp_servers\.harmony\][\s\S]*?(?=\[|$)/, mcpConfigSection.trim() + `
435
-
436
- `);
437
- writeFileSync(configPath, updated, "utf-8");
438
- result.filesCreated.push(configPath);
439
- } else {
440
- result.filesSkipped.push(configPath);
441
- }
442
- }
443
- return result;
444
- }
445
- function initCursor(cwd, force, installMode) {
446
- const result = {
447
- agent: "cursor",
448
- filesCreated: [],
449
- filesSkipped: [],
450
- symlinksCreated: []
451
- };
452
- const ruleContent = `---
453
- description: Harmony card workflow rule
454
- globs:
455
- - "**/*"
456
- alwaysApply: false
457
- ---
458
-
459
- # Harmony Integration
460
-
461
- When the user asks you to work on a Harmony card (references like #42, card names, or UUIDs):
462
-
463
- ${HARMONY_WORKFLOW_PROMPT.replace("$ARGUMENTS", "the card reference").replace("Your agent identifier", "cursor").replace("Your agent name", "Cursor AI")}
464
- `;
465
- const mcpConfigPath = join(cwd, ".cursor", "mcp.json");
466
- const mcpConfig = {
467
- mcpServers: {
468
- harmony: {
469
- command: "harmony-mcp",
470
- args: ["serve"]
471
- }
472
- }
473
- };
474
- const mcpResult = mergeJsonFile(mcpConfigPath, mcpConfig, force);
475
- if (mcpResult.created || mcpResult.merged) {
476
- result.filesCreated.push(mcpConfigPath);
477
- } else if (mcpResult.skipped) {
478
- result.filesSkipped.push(mcpConfigPath);
479
- }
480
- if (installMode === "global") {
481
- const centralRulePath = join(GLOBAL_SKILLS_DIR, "cursor", "harmony.mdc");
482
- const { created, skipped } = writeFileIfNotExists(centralRulePath, ruleContent, force);
483
- if (created)
484
- result.filesCreated.push(centralRulePath);
485
- if (skipped)
486
- result.filesSkipped.push(centralRulePath);
487
- const symlinkPath = join(homedir(), ".cursor", "rules", "harmony.mdc");
488
- const symlinkResult = createSymlink(centralRulePath, symlinkPath, force);
489
- if (symlinkResult.created)
490
- result.symlinksCreated.push(`${symlinkPath} → ${centralRulePath}`);
491
- if (symlinkResult.skipped)
492
- result.filesSkipped.push(symlinkPath);
493
- } else {
494
- const rulePath = join(cwd, ".cursor", "rules", "harmony.mdc");
495
- const { created, skipped } = writeFileIfNotExists(rulePath, ruleContent, force);
496
- if (created)
497
- result.filesCreated.push(rulePath);
498
- if (skipped)
499
- result.filesSkipped.push(rulePath);
500
- }
501
- return result;
502
- }
503
- function initWindsurf(cwd, force, installMode) {
504
- const result = {
505
- agent: "windsurf",
506
- filesCreated: [],
507
- filesSkipped: [],
508
- symlinksCreated: []
509
- };
510
- const ruleContent = `---
511
- trigger: model_decision
512
- description: Activate when user asks to work on a Harmony card (references like #42, card names, or task management)
513
- ---
514
-
515
- # Harmony Card Workflow
516
-
517
- When working on a Harmony card:
518
-
519
- ${HARMONY_WORKFLOW_PROMPT.replace("$ARGUMENTS", "the card reference").replace("Your agent identifier", "windsurf").replace("Your agent name", "Windsurf AI")}
520
- `;
521
- const globalMcpPath = join(homedir(), ".codeium", "windsurf", "mcp_config.json");
522
- const mcpConfig = {
523
- mcpServers: {
524
- harmony: {
525
- command: "harmony-mcp",
526
- args: ["serve"],
527
- disabled: false,
528
- alwaysAllow: []
529
- }
530
- }
531
- };
532
- const mcpResult = mergeJsonFile(globalMcpPath, mcpConfig, force);
533
- if (mcpResult.created || mcpResult.merged) {
534
- result.filesCreated.push(globalMcpPath);
535
- } else if (mcpResult.skipped) {
536
- result.filesSkipped.push(globalMcpPath);
537
- }
538
- if (installMode === "global") {
539
- const centralRulePath = join(GLOBAL_SKILLS_DIR, "windsurf", "harmony.md");
540
- const { created, skipped } = writeFileIfNotExists(centralRulePath, ruleContent, force);
541
- if (created)
542
- result.filesCreated.push(centralRulePath);
543
- if (skipped)
544
- result.filesSkipped.push(centralRulePath);
545
- const symlinkPath = join(homedir(), ".codeium", "windsurf", "rules", "harmony.md");
546
- const symlinkResult = createSymlink(centralRulePath, symlinkPath, force);
547
- if (symlinkResult.created)
548
- result.symlinksCreated.push(`${symlinkPath} → ${centralRulePath}`);
549
- if (symlinkResult.skipped)
550
- result.filesSkipped.push(symlinkPath);
551
- } else {
552
- const rulePath = join(cwd, ".windsurf", "rules", "harmony.md");
553
- const { created, skipped } = writeFileIfNotExists(rulePath, ruleContent, force);
554
- if (created)
555
- result.filesCreated.push(rulePath);
556
- if (skipped)
557
- result.filesSkipped.push(rulePath);
558
- }
559
- return result;
560
- }
561
- function initHarmony(options = {}) {
562
- const cwd = options.cwd || process.cwd();
563
- const force = options.force || false;
564
- const agents = options.agents || ["claude", "codex", "cursor", "windsurf"];
565
- const installMode = options.installMode || "local";
566
- const results = [];
567
- for (const agent of agents) {
568
- try {
569
- switch (agent) {
570
- case "claude":
571
- results.push(initClaude(cwd, force, installMode));
572
- break;
573
- case "codex":
574
- results.push(initCodex(cwd, force, installMode));
575
- break;
576
- case "cursor":
577
- results.push(initCursor(cwd, force, installMode));
578
- break;
579
- case "windsurf":
580
- results.push(initWindsurf(cwd, force, installMode));
581
- break;
582
- default:
583
- results.push({
584
- agent,
585
- filesCreated: [],
586
- filesSkipped: [],
587
- symlinksCreated: [],
588
- error: `Unknown agent: ${agent}`
589
- });
590
- }
591
- } catch (error) {
592
- results.push({
593
- agent,
594
- filesCreated: [],
595
- filesSkipped: [],
596
- symlinksCreated: [],
597
- error: error instanceof Error ? error.message : String(error)
598
- });
599
- }
600
- }
601
- return results;
602
- }
603
- function detectAgents(cwd = process.cwd()) {
604
- const detected = [];
605
- if (existsSync(join(cwd, ".claude")) || existsSync(join(homedir(), ".claude"))) {
606
- detected.push("claude");
607
- }
608
- if (existsSync(join(cwd, "AGENTS.md")) || existsSync(join(homedir(), ".codex"))) {
609
- detected.push("codex");
610
- }
611
- if (existsSync(join(cwd, ".cursor"))) {
612
- detected.push("cursor");
613
- }
614
- if (existsSync(join(cwd, ".windsurf")) || existsSync(join(cwd, ".windsurfrules")) || existsSync(join(homedir(), ".codeium", "windsurf"))) {
615
- detected.push("windsurf");
616
- }
617
- return detected;
618
- }
619
- var SUPPORTED_AGENTS = ["claude", "codex", "cursor", "windsurf"];
620
- export {
621
- initHarmony,
622
- detectAgents,
623
- SUPPORTED_AGENTS
624
- };