harmony-mcp 1.2.4 → 1.3.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/README.md +70 -5
- package/dist/cli.js +180 -84
- package/dist/index.js +59 -6
- package/dist/init.js +63 -62
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ MCP (Model Context Protocol) server for Harmony Kanban board. Enables AI coding
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **
|
|
7
|
+
- **29 MCP Tools** for full board control (cards, columns, labels, subtasks, links)
|
|
8
8
|
- **Card Linking** - create relationships between cards (blocks, relates_to, duplicates, is_part_of)
|
|
9
9
|
- **Prompt Builder** - generate AI-ready prompts from cards with context
|
|
10
10
|
- **Agent Session Tracking** - track work progress with timer badges
|
|
@@ -98,10 +98,13 @@ harmony-mcp init # Initialize for AI agents (interactive)
|
|
|
98
98
|
harmony-mcp init --all # Configure all supported agents
|
|
99
99
|
harmony-mcp init --detect # Auto-detect and configure installed agents
|
|
100
100
|
harmony-mcp init --agent X Y # Configure specific agents
|
|
101
|
-
harmony-mcp
|
|
101
|
+
harmony-mcp init -w WS -p PROJ # Initialize with local workspace/project context
|
|
102
|
+
harmony-mcp status # Show current config (global and local)
|
|
102
103
|
harmony-mcp reset # Clear configuration
|
|
103
|
-
harmony-mcp set-workspace ID # Set active workspace
|
|
104
|
-
harmony-mcp set-
|
|
104
|
+
harmony-mcp set-workspace ID # Set active workspace (global)
|
|
105
|
+
harmony-mcp set-workspace ID -l # Set active workspace (local project)
|
|
106
|
+
harmony-mcp set-project ID # Set active project (global)
|
|
107
|
+
harmony-mcp set-project ID -l # Set active project (local project)
|
|
105
108
|
harmony-mcp serve # Start MCP server
|
|
106
109
|
```
|
|
107
110
|
|
|
@@ -233,7 +236,9 @@ curl -X GET "https://gethmy.com/api/workspaces" \
|
|
|
233
236
|
|
|
234
237
|
## Configuration
|
|
235
238
|
|
|
236
|
-
|
|
239
|
+
### Global Configuration
|
|
240
|
+
|
|
241
|
+
Your global configuration is stored in `~/.harmony-mcp/config.json`:
|
|
237
242
|
|
|
238
243
|
```json
|
|
239
244
|
{
|
|
@@ -244,6 +249,66 @@ Your configuration is stored in `~/.harmony-mcp/config.json`:
|
|
|
244
249
|
}
|
|
245
250
|
```
|
|
246
251
|
|
|
252
|
+
### Local Project Configuration
|
|
253
|
+
|
|
254
|
+
For multi-project workflows, you can set project-specific context using local configuration. This is stored in `.harmony-mcp.json` in your project root:
|
|
255
|
+
|
|
256
|
+
```json
|
|
257
|
+
{
|
|
258
|
+
"workspaceId": "uuid-here",
|
|
259
|
+
"projectId": "uuid-here"
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**Priority**: Local config overrides global config for workspace and project context.
|
|
264
|
+
|
|
265
|
+
**Security**: API key is never stored locally (only in global config) to prevent accidental commits.
|
|
266
|
+
|
|
267
|
+
#### Setting Local Context
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
# During initialization
|
|
271
|
+
harmony-mcp init --workspace <ws-id> --project <proj-id>
|
|
272
|
+
|
|
273
|
+
# Or separately
|
|
274
|
+
harmony-mcp set-workspace <id> --local
|
|
275
|
+
harmony-mcp set-project <id> --local
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
#### Viewing Configuration
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
harmony-mcp status
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Example output:
|
|
285
|
+
|
|
286
|
+
```
|
|
287
|
+
Status: Configured
|
|
288
|
+
API Key: hmy_abc1...
|
|
289
|
+
API URL: https://gethmy.com/api
|
|
290
|
+
|
|
291
|
+
Global Context:
|
|
292
|
+
Workspace: <global-ws-id>
|
|
293
|
+
Project: <global-proj-id>
|
|
294
|
+
|
|
295
|
+
Local Context (.harmony-mcp.json):
|
|
296
|
+
Workspace: <local-ws-id>
|
|
297
|
+
Project: <local-proj-id>
|
|
298
|
+
|
|
299
|
+
Active (effective):
|
|
300
|
+
Workspace: <local-ws-id> ← local
|
|
301
|
+
Project: <local-proj-id> ← local
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
#### Recommended .gitignore
|
|
305
|
+
|
|
306
|
+
Add this to prevent accidentally committing local config:
|
|
307
|
+
|
|
308
|
+
```
|
|
309
|
+
.harmony-mcp.json
|
|
310
|
+
```
|
|
311
|
+
|
|
247
312
|
## Architecture
|
|
248
313
|
|
|
249
314
|
```
|
package/dist/cli.js
CHANGED
|
@@ -13792,77 +13792,78 @@ import { join, dirname } from "node:path";
|
|
|
13792
13792
|
import { homedir } from "node:os";
|
|
13793
13793
|
var HARMONY_WORKFLOW_PROMPT = `# Harmony Card Workflow
|
|
13794
13794
|
|
|
13795
|
-
|
|
13795
|
+
Start work on a Harmony card. Card reference: $ARGUMENTS
|
|
13796
13796
|
|
|
13797
|
-
##
|
|
13797
|
+
## 1. Find & Fetch Card
|
|
13798
13798
|
|
|
13799
|
-
|
|
13799
|
+
Parse the reference and fetch the card:
|
|
13800
|
+
- \`#42\` or \`42\` → \`harmony_get_card_by_short_id\` with \`shortId: 42\`
|
|
13801
|
+
- UUID → \`harmony_get_card\` with \`cardId\`
|
|
13802
|
+
- Name/text → \`harmony_search_cards\` with \`query\`
|
|
13800
13803
|
|
|
13801
|
-
|
|
13802
|
-
- If it starts with \`#\` followed by numbers (e.g., \`#42\`), use \`harmony_get_card_by_short_id\` with the number
|
|
13803
|
-
- If it looks like a UUID, use \`harmony_get_card\` directly
|
|
13804
|
-
- Otherwise, use \`harmony_search_cards\` to find by name
|
|
13804
|
+
## 2. Get Board State
|
|
13805
13805
|
|
|
13806
|
-
|
|
13806
|
+
Call \`harmony_get_board\` to get columns and labels. From the response:
|
|
13807
|
+
- Find the "In Progress" (or "Progress") column ID
|
|
13808
|
+
- Find the "agent" label ID
|
|
13807
13809
|
|
|
13808
|
-
|
|
13809
|
-
1. Get the board using \`harmony_get_board\` to find the "In Progress" column ID
|
|
13810
|
-
2. Use \`harmony_move_card\` to move the card to "In Progress"
|
|
13810
|
+
## 3. Setup Card for Work
|
|
13811
13811
|
|
|
13812
|
-
|
|
13812
|
+
Execute these in sequence:
|
|
13813
|
+
1. \`harmony_move_card\` → Move to "In Progress" column
|
|
13814
|
+
2. \`harmony_add_label_to_card\` → Add "agent" label
|
|
13815
|
+
3. \`harmony_start_agent_session\`:
|
|
13816
|
+
- \`cardId\`: Card UUID
|
|
13817
|
+
- \`agentIdentifier\`: Your agent identifier
|
|
13818
|
+
- \`agentName\`: Your agent name
|
|
13819
|
+
- \`currentTask\`: "Analyzing card requirements"
|
|
13813
13820
|
|
|
13814
|
-
|
|
13815
|
-
2. Use \`harmony_add_label_to_card\` to add it to the card
|
|
13821
|
+
## 4. Generate Work Prompt
|
|
13816
13822
|
|
|
13817
|
-
|
|
13823
|
+
Call \`harmony_generate_prompt\` with:
|
|
13824
|
+
- \`cardId\` or \`shortId\` (+ \`projectId\` if using shortId)
|
|
13825
|
+
- \`variant\`: Select based on task:
|
|
13826
|
+
- \`"execute"\` (default) → Clear tasks, bug fixes, well-defined work
|
|
13827
|
+
- \`"analysis"\` → Complex features, unclear requirements
|
|
13828
|
+
- \`"draft"\` → Medium complexity, want feedback first
|
|
13818
13829
|
|
|
13819
|
-
|
|
13820
|
-
|
|
13821
|
-
|
|
13822
|
-
|
|
13823
|
-
|
|
13824
|
-
|
|
13825
|
-
|
|
13826
|
-
|
|
13827
|
-
|
|
13828
|
-
|
|
13829
|
-
|
|
13830
|
-
|
|
13831
|
-
|
|
13832
|
-
|
|
13833
|
-
|
|
13834
|
-
|
|
13835
|
-
|
|
13836
|
-
|
|
13837
|
-
|
|
13838
|
-
|
|
13839
|
-
|
|
13840
|
-
|
|
13841
|
-
|
|
13842
|
-
|
|
13843
|
-
**
|
|
13844
|
-
|
|
13845
|
-
|
|
13846
|
-
|
|
13847
|
-
|
|
13848
|
-
|
|
13849
|
-
|
|
13850
|
-
|
|
13851
|
-
|
|
13852
|
-
|
|
13853
|
-
|
|
13854
|
-
|
|
13855
|
-
|
|
13856
|
-
2. Use \`harmony_move_card\` to move the card to the "Review" column
|
|
13857
|
-
3. Summarize what was accomplished
|
|
13858
|
-
|
|
13859
|
-
## Important Notes
|
|
13860
|
-
- Always read the card description carefully before starting
|
|
13861
|
-
- If the task is unclear, ask for clarification
|
|
13862
|
-
- Make commits as appropriate during implementation
|
|
13863
|
-
- The "agent" label indicates AI is working on the card
|
|
13864
|
-
- Update progress at meaningful milestones, not constantly
|
|
13865
|
-
- If you need to pause work, call \`harmony_end_agent_session\` with \`status: "paused"\`
|
|
13830
|
+
The generated prompt provides role framing, focus areas, subtasks, linked cards, and suggested outputs.
|
|
13831
|
+
|
|
13832
|
+
## 5. Display Card Summary
|
|
13833
|
+
|
|
13834
|
+
Show the user: Card title, short ID, role, priority, labels, due date, description, and subtasks.
|
|
13835
|
+
|
|
13836
|
+
## 6. Implement Solution
|
|
13837
|
+
|
|
13838
|
+
Work on the card following the generated prompt's guidance. Update progress at milestones:
|
|
13839
|
+
- \`harmony_update_agent_progress\` with \`progressPercent\` (0-100), \`currentTask\`, \`status\`, \`blockers\`
|
|
13840
|
+
|
|
13841
|
+
**Progress checkpoints:** 20% (exploration), 50% (implementation), 80% (testing), 100% (done)
|
|
13842
|
+
|
|
13843
|
+
## 7. Complete Work
|
|
13844
|
+
|
|
13845
|
+
When finished:
|
|
13846
|
+
1. \`harmony_end_agent_session\` with \`status: "completed"\`, \`progressPercent: 100\`
|
|
13847
|
+
2. \`harmony_move_card\` to "Review" column
|
|
13848
|
+
3. Summarize accomplishments
|
|
13849
|
+
|
|
13850
|
+
If pausing: \`harmony_end_agent_session\` with \`status: "paused"\`
|
|
13851
|
+
|
|
13852
|
+
## Key Tools Reference
|
|
13853
|
+
|
|
13854
|
+
**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\`
|
|
13855
|
+
|
|
13856
|
+
**Subtasks:** \`harmony_create_subtask\`, \`harmony_toggle_subtask\`, \`harmony_delete_subtask\`
|
|
13857
|
+
|
|
13858
|
+
**Labels:** \`harmony_add_label_to_card\`, \`harmony_remove_label_from_card\`, \`harmony_create_label\`
|
|
13859
|
+
|
|
13860
|
+
**Links:** \`harmony_add_link_to_card\`, \`harmony_remove_link_from_card\`, \`harmony_get_card_links\`
|
|
13861
|
+
|
|
13862
|
+
**Board:** \`harmony_get_board\`, \`harmony_list_projects\`, \`harmony_get_context\`, \`harmony_set_project_context\`
|
|
13863
|
+
|
|
13864
|
+
**Sessions:** \`harmony_start_agent_session\`, \`harmony_update_agent_progress\`, \`harmony_end_agent_session\`, \`harmony_get_agent_session\`
|
|
13865
|
+
|
|
13866
|
+
**AI:** \`harmony_generate_prompt\`, \`harmony_process_command\`
|
|
13866
13867
|
`;
|
|
13867
13868
|
function ensureDir(dirPath) {
|
|
13868
13869
|
if (!existsSync(dirPath)) {
|
|
@@ -25233,12 +25234,16 @@ import { homedir as homedir2 } from "node:os";
|
|
|
25233
25234
|
import { join as join2 } from "node:path";
|
|
25234
25235
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
25235
25236
|
var DEFAULT_API_URL = "https://gethmy.com/api";
|
|
25237
|
+
var LOCAL_CONFIG_FILENAME = ".harmony-mcp.json";
|
|
25236
25238
|
function getConfigDir() {
|
|
25237
25239
|
return join2(homedir2(), ".harmony-mcp");
|
|
25238
25240
|
}
|
|
25239
25241
|
function getConfigPath() {
|
|
25240
25242
|
return join2(getConfigDir(), "config.json");
|
|
25241
25243
|
}
|
|
25244
|
+
function getLocalConfigPath(cwd) {
|
|
25245
|
+
return join2(cwd || process.cwd(), LOCAL_CONFIG_FILENAME);
|
|
25246
|
+
}
|
|
25242
25247
|
function loadConfig() {
|
|
25243
25248
|
const configPath = getConfigPath();
|
|
25244
25249
|
if (!existsSync2(configPath)) {
|
|
@@ -25277,6 +25282,39 @@ function saveConfig(config2) {
|
|
|
25277
25282
|
const newConfig = { ...existingConfig, ...config2 };
|
|
25278
25283
|
writeFileSync2(configPath, JSON.stringify(newConfig, null, 2), { mode: 384 });
|
|
25279
25284
|
}
|
|
25285
|
+
function loadLocalConfig(cwd) {
|
|
25286
|
+
const localConfigPath = getLocalConfigPath(cwd);
|
|
25287
|
+
if (!existsSync2(localConfigPath)) {
|
|
25288
|
+
return null;
|
|
25289
|
+
}
|
|
25290
|
+
try {
|
|
25291
|
+
const data = readFileSync2(localConfigPath, "utf-8");
|
|
25292
|
+
const config2 = JSON.parse(data);
|
|
25293
|
+
return {
|
|
25294
|
+
workspaceId: config2.workspaceId || null,
|
|
25295
|
+
projectId: config2.projectId || null
|
|
25296
|
+
};
|
|
25297
|
+
} catch {
|
|
25298
|
+
return null;
|
|
25299
|
+
}
|
|
25300
|
+
}
|
|
25301
|
+
function saveLocalConfig(config2, cwd) {
|
|
25302
|
+
const localConfigPath = getLocalConfigPath(cwd);
|
|
25303
|
+
const existingConfig = loadLocalConfig(cwd) || {
|
|
25304
|
+
workspaceId: null,
|
|
25305
|
+
projectId: null
|
|
25306
|
+
};
|
|
25307
|
+
const newConfig = { ...existingConfig, ...config2 };
|
|
25308
|
+
const cleanConfig = {};
|
|
25309
|
+
if (newConfig.workspaceId)
|
|
25310
|
+
cleanConfig.workspaceId = newConfig.workspaceId;
|
|
25311
|
+
if (newConfig.projectId)
|
|
25312
|
+
cleanConfig.projectId = newConfig.projectId;
|
|
25313
|
+
writeFileSync2(localConfigPath, JSON.stringify(cleanConfig, null, 2));
|
|
25314
|
+
}
|
|
25315
|
+
function hasLocalConfig(cwd) {
|
|
25316
|
+
return existsSync2(getLocalConfigPath(cwd));
|
|
25317
|
+
}
|
|
25280
25318
|
function getApiKey() {
|
|
25281
25319
|
const config2 = loadConfig();
|
|
25282
25320
|
if (!config2.apiKey) {
|
|
@@ -25289,16 +25327,32 @@ function getApiUrl() {
|
|
|
25289
25327
|
const config2 = loadConfig();
|
|
25290
25328
|
return config2.apiUrl;
|
|
25291
25329
|
}
|
|
25292
|
-
function setActiveWorkspace(workspaceId) {
|
|
25293
|
-
|
|
25330
|
+
function setActiveWorkspace(workspaceId, options) {
|
|
25331
|
+
if (options?.local) {
|
|
25332
|
+
saveLocalConfig({ workspaceId }, options.cwd);
|
|
25333
|
+
} else {
|
|
25334
|
+
saveConfig({ activeWorkspaceId: workspaceId });
|
|
25335
|
+
}
|
|
25294
25336
|
}
|
|
25295
|
-
function setActiveProject(projectId) {
|
|
25296
|
-
|
|
25337
|
+
function setActiveProject(projectId, options) {
|
|
25338
|
+
if (options?.local) {
|
|
25339
|
+
saveLocalConfig({ projectId }, options.cwd);
|
|
25340
|
+
} else {
|
|
25341
|
+
saveConfig({ activeProjectId: projectId });
|
|
25342
|
+
}
|
|
25297
25343
|
}
|
|
25298
|
-
function getActiveWorkspaceId() {
|
|
25344
|
+
function getActiveWorkspaceId(cwd) {
|
|
25345
|
+
const localConfig = loadLocalConfig(cwd);
|
|
25346
|
+
if (localConfig?.workspaceId) {
|
|
25347
|
+
return localConfig.workspaceId;
|
|
25348
|
+
}
|
|
25299
25349
|
return loadConfig().activeWorkspaceId;
|
|
25300
25350
|
}
|
|
25301
|
-
function getActiveProjectId() {
|
|
25351
|
+
function getActiveProjectId(cwd) {
|
|
25352
|
+
const localConfig = loadLocalConfig(cwd);
|
|
25353
|
+
if (localConfig?.projectId) {
|
|
25354
|
+
return localConfig.projectId;
|
|
25355
|
+
}
|
|
25302
25356
|
return loadConfig().activeProjectId;
|
|
25303
25357
|
}
|
|
25304
25358
|
function isConfigured() {
|
|
@@ -26633,19 +26687,36 @@ You can now use the MCP server with Claude Code.`);
|
|
|
26633
26687
|
}
|
|
26634
26688
|
});
|
|
26635
26689
|
program.command("status").description("Show configuration status").action(() => {
|
|
26636
|
-
const
|
|
26690
|
+
const globalConfig2 = loadConfig();
|
|
26691
|
+
const localConfig = loadLocalConfig();
|
|
26692
|
+
const hasLocal = hasLocalConfig();
|
|
26637
26693
|
if (isConfigured()) {
|
|
26638
26694
|
console.log("Status: Configured");
|
|
26639
|
-
console.log(`API Key: ${
|
|
26640
|
-
console.log(`API URL: ${
|
|
26641
|
-
|
|
26642
|
-
|
|
26643
|
-
}
|
|
26644
|
-
|
|
26645
|
-
|
|
26695
|
+
console.log(`API Key: ${globalConfig2.apiKey?.slice(0, 8)}...`);
|
|
26696
|
+
console.log(`API URL: ${globalConfig2.apiUrl}`);
|
|
26697
|
+
console.log(`
|
|
26698
|
+
Global Context:`);
|
|
26699
|
+
console.log(` Workspace: ${globalConfig2.activeWorkspaceId || "(not set)"}`);
|
|
26700
|
+
console.log(` Project: ${globalConfig2.activeProjectId || "(not set)"}`);
|
|
26701
|
+
if (hasLocal) {
|
|
26702
|
+
console.log(`
|
|
26703
|
+
Local Context (${getLocalConfigPath()}):`);
|
|
26704
|
+
console.log(` Workspace: ${localConfig?.workspaceId || "(not set)"}`);
|
|
26705
|
+
console.log(` Project: ${localConfig?.projectId || "(not set)"}`);
|
|
26646
26706
|
}
|
|
26707
|
+
const effectiveWorkspace = getActiveWorkspaceId();
|
|
26708
|
+
const effectiveProject = getActiveProjectId();
|
|
26709
|
+
console.log(`
|
|
26710
|
+
Active (effective):`);
|
|
26711
|
+
const wsSource = localConfig?.workspaceId ? "local" : globalConfig2.activeWorkspaceId ? "global" : "";
|
|
26712
|
+
const projSource = localConfig?.projectId ? "local" : globalConfig2.activeProjectId ? "global" : "";
|
|
26713
|
+
console.log(` Workspace: ${effectiveWorkspace || "(not set)"}${wsSource ? ` ← ${wsSource}` : ""}`);
|
|
26714
|
+
console.log(` Project: ${effectiveProject || "(not set)"}${projSource ? ` ← ${projSource}` : ""}`);
|
|
26647
26715
|
console.log(`
|
|
26648
|
-
|
|
26716
|
+
Global config: ${getConfigPath()}`);
|
|
26717
|
+
if (hasLocal) {
|
|
26718
|
+
console.log(`Local config: ${getLocalConfigPath()}`);
|
|
26719
|
+
}
|
|
26649
26720
|
} else {
|
|
26650
26721
|
console.log("Status: Not configured");
|
|
26651
26722
|
console.log(`
|
|
@@ -26661,15 +26732,27 @@ program.command("reset").description("Remove stored configuration").action(() =>
|
|
|
26661
26732
|
});
|
|
26662
26733
|
console.log("Configuration reset successfully");
|
|
26663
26734
|
});
|
|
26664
|
-
program.command("set-workspace <workspaceId>").description("Set the active workspace context").action((workspaceId) => {
|
|
26665
|
-
|
|
26666
|
-
|
|
26735
|
+
program.command("set-workspace <workspaceId>").description("Set the active workspace context").option("-l, --local", "Save to local project config (.harmony-mcp.json) instead of global").action((workspaceId, options) => {
|
|
26736
|
+
const isLocal = options.local;
|
|
26737
|
+
setActiveWorkspace(workspaceId, { local: isLocal });
|
|
26738
|
+
if (isLocal) {
|
|
26739
|
+
console.log(`Local workspace set to: ${workspaceId}`);
|
|
26740
|
+
console.log(`Config file: ${getLocalConfigPath()}`);
|
|
26741
|
+
} else {
|
|
26742
|
+
console.log(`Global workspace set to: ${workspaceId}`);
|
|
26743
|
+
}
|
|
26667
26744
|
});
|
|
26668
|
-
program.command("set-project <projectId>").description("Set the active project context").action((projectId) => {
|
|
26669
|
-
|
|
26670
|
-
|
|
26745
|
+
program.command("set-project <projectId>").description("Set the active project context").option("-l, --local", "Save to local project config (.harmony-mcp.json) instead of global").action((projectId, options) => {
|
|
26746
|
+
const isLocal = options.local;
|
|
26747
|
+
setActiveProject(projectId, { local: isLocal });
|
|
26748
|
+
if (isLocal) {
|
|
26749
|
+
console.log(`Local project set to: ${projectId}`);
|
|
26750
|
+
console.log(`Config file: ${getLocalConfigPath()}`);
|
|
26751
|
+
} else {
|
|
26752
|
+
console.log(`Global project set to: ${projectId}`);
|
|
26753
|
+
}
|
|
26671
26754
|
});
|
|
26672
|
-
program.command("init").description("Initialize Harmony MCP for AI coding agents (Claude Code, Codex, Cursor, Windsurf)").option("-a, --agent <agents...>", `Agent(s) to configure: ${SUPPORTED_AGENTS.join(", ")}`).option("--all", "Configure all supported agents").option("--detect", "Auto-detect installed agents and configure them").option("-f, --force", "Overwrite existing configuration files").option("-d, --directory <path>", "Project directory (default: current directory)").action(async (options) => {
|
|
26755
|
+
program.command("init").description("Initialize Harmony MCP for AI coding agents (Claude Code, Codex, Cursor, Windsurf)").option("-a, --agent <agents...>", `Agent(s) to configure: ${SUPPORTED_AGENTS.join(", ")}`).option("--all", "Configure all supported agents").option("--detect", "Auto-detect installed agents and configure them").option("-f, --force", "Overwrite existing configuration files").option("-d, --directory <path>", "Project directory (default: current directory)").option("-w, --workspace <id>", "Set local workspace context for this project").option("-p, --project <id>", "Set local project context for this project").action(async (options) => {
|
|
26673
26756
|
console.log(`Harmony MCP Initialization
|
|
26674
26757
|
`);
|
|
26675
26758
|
let agents = [];
|
|
@@ -26767,6 +26850,19 @@ ${result.agent.toUpperCase()}:`);
|
|
|
26767
26850
|
console.log(" No changes made");
|
|
26768
26851
|
}
|
|
26769
26852
|
}
|
|
26853
|
+
if (options.workspace || options.project) {
|
|
26854
|
+
console.log(`
|
|
26855
|
+
LOCAL CONTEXT:`);
|
|
26856
|
+
if (options.workspace) {
|
|
26857
|
+
saveLocalConfig({ workspaceId: options.workspace }, cwd);
|
|
26858
|
+
console.log(` ✓ Workspace: ${options.workspace}`);
|
|
26859
|
+
}
|
|
26860
|
+
if (options.project) {
|
|
26861
|
+
saveLocalConfig({ projectId: options.project }, cwd);
|
|
26862
|
+
console.log(` ✓ Project: ${options.project}`);
|
|
26863
|
+
}
|
|
26864
|
+
console.log(` Config file: ${getLocalConfigPath(cwd)}`);
|
|
26865
|
+
}
|
|
26770
26866
|
console.log(`
|
|
26771
26867
|
` + "─".repeat(60));
|
|
26772
26868
|
if (!isConfigured()) {
|
package/dist/index.js
CHANGED
|
@@ -22996,12 +22996,16 @@ import { homedir } from "node:os";
|
|
|
22996
22996
|
import { join } from "node:path";
|
|
22997
22997
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
22998
22998
|
var DEFAULT_API_URL = "https://gethmy.com/api";
|
|
22999
|
+
var LOCAL_CONFIG_FILENAME = ".harmony-mcp.json";
|
|
22999
23000
|
function getConfigDir() {
|
|
23000
23001
|
return join(homedir(), ".harmony-mcp");
|
|
23001
23002
|
}
|
|
23002
23003
|
function getConfigPath() {
|
|
23003
23004
|
return join(getConfigDir(), "config.json");
|
|
23004
23005
|
}
|
|
23006
|
+
function getLocalConfigPath(cwd) {
|
|
23007
|
+
return join(cwd || process.cwd(), LOCAL_CONFIG_FILENAME);
|
|
23008
|
+
}
|
|
23005
23009
|
function loadConfig() {
|
|
23006
23010
|
const configPath = getConfigPath();
|
|
23007
23011
|
if (!existsSync(configPath)) {
|
|
@@ -23040,6 +23044,39 @@ function saveConfig(config2) {
|
|
|
23040
23044
|
const newConfig = { ...existingConfig, ...config2 };
|
|
23041
23045
|
writeFileSync(configPath, JSON.stringify(newConfig, null, 2), { mode: 384 });
|
|
23042
23046
|
}
|
|
23047
|
+
function loadLocalConfig(cwd) {
|
|
23048
|
+
const localConfigPath = getLocalConfigPath(cwd);
|
|
23049
|
+
if (!existsSync(localConfigPath)) {
|
|
23050
|
+
return null;
|
|
23051
|
+
}
|
|
23052
|
+
try {
|
|
23053
|
+
const data = readFileSync(localConfigPath, "utf-8");
|
|
23054
|
+
const config2 = JSON.parse(data);
|
|
23055
|
+
return {
|
|
23056
|
+
workspaceId: config2.workspaceId || null,
|
|
23057
|
+
projectId: config2.projectId || null
|
|
23058
|
+
};
|
|
23059
|
+
} catch {
|
|
23060
|
+
return null;
|
|
23061
|
+
}
|
|
23062
|
+
}
|
|
23063
|
+
function saveLocalConfig(config2, cwd) {
|
|
23064
|
+
const localConfigPath = getLocalConfigPath(cwd);
|
|
23065
|
+
const existingConfig = loadLocalConfig(cwd) || {
|
|
23066
|
+
workspaceId: null,
|
|
23067
|
+
projectId: null
|
|
23068
|
+
};
|
|
23069
|
+
const newConfig = { ...existingConfig, ...config2 };
|
|
23070
|
+
const cleanConfig = {};
|
|
23071
|
+
if (newConfig.workspaceId)
|
|
23072
|
+
cleanConfig.workspaceId = newConfig.workspaceId;
|
|
23073
|
+
if (newConfig.projectId)
|
|
23074
|
+
cleanConfig.projectId = newConfig.projectId;
|
|
23075
|
+
writeFileSync(localConfigPath, JSON.stringify(cleanConfig, null, 2));
|
|
23076
|
+
}
|
|
23077
|
+
function hasLocalConfig(cwd) {
|
|
23078
|
+
return existsSync(getLocalConfigPath(cwd));
|
|
23079
|
+
}
|
|
23043
23080
|
function getApiKey() {
|
|
23044
23081
|
const config2 = loadConfig();
|
|
23045
23082
|
if (!config2.apiKey) {
|
|
@@ -23052,16 +23089,32 @@ function getApiUrl() {
|
|
|
23052
23089
|
const config2 = loadConfig();
|
|
23053
23090
|
return config2.apiUrl;
|
|
23054
23091
|
}
|
|
23055
|
-
function setActiveWorkspace(workspaceId) {
|
|
23056
|
-
|
|
23092
|
+
function setActiveWorkspace(workspaceId, options) {
|
|
23093
|
+
if (options?.local) {
|
|
23094
|
+
saveLocalConfig({ workspaceId }, options.cwd);
|
|
23095
|
+
} else {
|
|
23096
|
+
saveConfig({ activeWorkspaceId: workspaceId });
|
|
23097
|
+
}
|
|
23057
23098
|
}
|
|
23058
|
-
function setActiveProject(projectId) {
|
|
23059
|
-
|
|
23099
|
+
function setActiveProject(projectId, options) {
|
|
23100
|
+
if (options?.local) {
|
|
23101
|
+
saveLocalConfig({ projectId }, options.cwd);
|
|
23102
|
+
} else {
|
|
23103
|
+
saveConfig({ activeProjectId: projectId });
|
|
23104
|
+
}
|
|
23060
23105
|
}
|
|
23061
|
-
function getActiveWorkspaceId() {
|
|
23106
|
+
function getActiveWorkspaceId(cwd) {
|
|
23107
|
+
const localConfig = loadLocalConfig(cwd);
|
|
23108
|
+
if (localConfig?.workspaceId) {
|
|
23109
|
+
return localConfig.workspaceId;
|
|
23110
|
+
}
|
|
23062
23111
|
return loadConfig().activeWorkspaceId;
|
|
23063
23112
|
}
|
|
23064
|
-
function getActiveProjectId() {
|
|
23113
|
+
function getActiveProjectId(cwd) {
|
|
23114
|
+
const localConfig = loadLocalConfig(cwd);
|
|
23115
|
+
if (localConfig?.projectId) {
|
|
23116
|
+
return localConfig.projectId;
|
|
23117
|
+
}
|
|
23065
23118
|
return loadConfig().activeProjectId;
|
|
23066
23119
|
}
|
|
23067
23120
|
function isConfigured() {
|
package/dist/init.js
CHANGED
|
@@ -33,77 +33,78 @@ import { join, dirname } from "node:path";
|
|
|
33
33
|
import { homedir } from "node:os";
|
|
34
34
|
var HARMONY_WORKFLOW_PROMPT = `# Harmony Card Workflow
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
Start work on a Harmony card. Card reference: $ARGUMENTS
|
|
37
37
|
|
|
38
|
-
##
|
|
38
|
+
## 1. Find & Fetch Card
|
|
39
39
|
|
|
40
|
-
|
|
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\`
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
- If it starts with \`#\` followed by numbers (e.g., \`#42\`), use \`harmony_get_card_by_short_id\` with the number
|
|
44
|
-
- If it looks like a UUID, use \`harmony_get_card\` directly
|
|
45
|
-
- Otherwise, use \`harmony_search_cards\` to find by name
|
|
45
|
+
## 2. Get Board State
|
|
46
46
|
|
|
47
|
-
|
|
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
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
1. Get the board using \`harmony_get_board\` to find the "In Progress" column ID
|
|
51
|
-
2. Use \`harmony_move_card\` to move the card to "In Progress"
|
|
51
|
+
## 3. Setup Card for Work
|
|
52
52
|
|
|
53
|
-
|
|
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"
|
|
54
61
|
|
|
55
|
-
|
|
56
|
-
2. Use \`harmony_add_label_to_card\` to add it to the card
|
|
62
|
+
## 4. Generate Work Prompt
|
|
57
63
|
|
|
58
|
-
|
|
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
|
|
59
70
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
**
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
2. Use \`harmony_move_card\` to move the card to the "Review" column
|
|
98
|
-
3. Summarize what was accomplished
|
|
99
|
-
|
|
100
|
-
## Important Notes
|
|
101
|
-
- Always read the card description carefully before starting
|
|
102
|
-
- If the task is unclear, ask for clarification
|
|
103
|
-
- Make commits as appropriate during implementation
|
|
104
|
-
- The "agent" label indicates AI is working on the card
|
|
105
|
-
- Update progress at meaningful milestones, not constantly
|
|
106
|
-
- If you need to pause work, call \`harmony_end_agent_session\` with \`status: "paused"\`
|
|
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\`
|
|
107
108
|
`;
|
|
108
109
|
function ensureDir(dirPath) {
|
|
109
110
|
if (!existsSync(dirPath)) {
|