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 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
- - **25+ MCP Tools** for full board control (cards, columns, labels, subtasks, links)
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 status # Show current config
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-project ID # Set active project
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
- Your configuration is stored in `~/.harmony-mcp/config.json`:
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
- You are starting work on a Harmony card. Follow this workflow:
13795
+ Start work on a Harmony card. Card reference: $ARGUMENTS
13796
13796
 
13797
- ## Step 1: Find the Card
13797
+ ## 1. Find & Fetch Card
13798
13798
 
13799
- The user provided: $ARGUMENTS
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
- Parse the card reference:
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
- ## Step 2: Move to In Progress
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
- Once you have the card:
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
- ## Step 3: Add Agent Label
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
- 1. From the board response, find the label named "agent"
13815
- 2. Use \`harmony_add_label_to_card\` to add it to the card
13821
+ ## 4. Generate Work Prompt
13816
13822
 
13817
- ## Step 4: Start Agent Session
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
- After adding the agent label, start tracking your work session:
13820
- 1. Use \`harmony_start_agent_session\` with:
13821
- - \`cardId\`: The card ID
13822
- - \`agentIdentifier\`: Your agent identifier
13823
- - \`agentName\`: Your agent name
13824
- - \`currentTask\`: A brief description of what you're about to do
13825
-
13826
- This enables the timer badge on the card to show your progress in real-time.
13827
-
13828
- ## Step 5: Display Card Details
13829
-
13830
- Show the user:
13831
- - Card title and short ID
13832
- - Description (if any)
13833
- - Priority
13834
- - Current labels
13835
- - Due date (if set)
13836
-
13837
- ## Step 6: Implement the Solution
13838
-
13839
- 1. Use \`harmony_generate_prompt\` with the card ID to get role-based guidance
13840
- 2. Follow the generated prompt to implement the solution
13841
- 3. Test the changes
13842
-
13843
- **During implementation, update your progress periodically:**
13844
- - Use \`harmony_update_agent_progress\` to report:
13845
- - \`progressPercent\`: Estimated completion (0-100)
13846
- - \`currentTask\`: What you're currently doing
13847
- - \`blockers\`: Any issues blocking progress (set \`status: "blocked"\` if blocked)
13848
-
13849
- ## Step 7: Complete Work
13850
-
13851
- When implementation is complete:
13852
- 1. Use \`harmony_end_agent_session\` with:
13853
- - \`cardId\`: The card ID
13854
- - \`status\`: "completed"
13855
- - \`progressPercent\`: 100
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
- saveConfig({ activeWorkspaceId: workspaceId });
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
- saveConfig({ activeProjectId: projectId });
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 config2 = loadConfig();
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: ${config2.apiKey?.slice(0, 8)}...`);
26640
- console.log(`API URL: ${config2.apiUrl}`);
26641
- if (config2.activeWorkspaceId) {
26642
- console.log(`Active Workspace: ${config2.activeWorkspaceId}`);
26643
- }
26644
- if (config2.activeProjectId) {
26645
- console.log(`Active Project: ${config2.activeProjectId}`);
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
- Config file: ${getConfigPath()}`);
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
- setActiveWorkspace(workspaceId);
26666
- console.log(`Active workspace set to: ${workspaceId}`);
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
- setActiveProject(projectId);
26670
- console.log(`Active project set to: ${projectId}`);
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
- saveConfig({ activeWorkspaceId: workspaceId });
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
- saveConfig({ activeProjectId: projectId });
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
- You are starting work on a Harmony card. Follow this workflow:
36
+ Start work on a Harmony card. Card reference: $ARGUMENTS
37
37
 
38
- ## Step 1: Find the Card
38
+ ## 1. Find & Fetch Card
39
39
 
40
- The user provided: $ARGUMENTS
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
- Parse the card reference:
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
- ## Step 2: Move to In Progress
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
- Once you have the card:
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
- ## Step 3: Add Agent Label
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
- 1. From the board response, find the label named "agent"
56
- 2. Use \`harmony_add_label_to_card\` to add it to the card
62
+ ## 4. Generate Work Prompt
57
63
 
58
- ## Step 4: Start Agent Session
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
- After adding the agent label, start tracking your work session:
61
- 1. Use \`harmony_start_agent_session\` with:
62
- - \`cardId\`: The card ID
63
- - \`agentIdentifier\`: Your agent identifier
64
- - \`agentName\`: Your agent name
65
- - \`currentTask\`: A brief description of what you're about to do
66
-
67
- This enables the timer badge on the card to show your progress in real-time.
68
-
69
- ## Step 5: Display Card Details
70
-
71
- Show the user:
72
- - Card title and short ID
73
- - Description (if any)
74
- - Priority
75
- - Current labels
76
- - Due date (if set)
77
-
78
- ## Step 6: Implement the Solution
79
-
80
- 1. Use \`harmony_generate_prompt\` with the card ID to get role-based guidance
81
- 2. Follow the generated prompt to implement the solution
82
- 3. Test the changes
83
-
84
- **During implementation, update your progress periodically:**
85
- - Use \`harmony_update_agent_progress\` to report:
86
- - \`progressPercent\`: Estimated completion (0-100)
87
- - \`currentTask\`: What you're currently doing
88
- - \`blockers\`: Any issues blocking progress (set \`status: "blocked"\` if blocked)
89
-
90
- ## Step 7: Complete Work
91
-
92
- When implementation is complete:
93
- 1. Use \`harmony_end_agent_session\` with:
94
- - \`cardId\`: The card ID
95
- - \`status\`: "completed"
96
- - \`progressPercent\`: 100
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)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "harmony-mcp",
3
- "version": "1.2.4",
3
+ "version": "1.3.1",
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",