@gethmy/mcp 2.3.1 → 2.3.3

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 (34) hide show
  1. package/dist/lib/api-client.js +2099 -648
  2. package/dist/lib/config.js +217 -201
  3. package/package.json +9 -5
  4. package/src/memory-cleanup.ts +2 -4
  5. package/dist/lib/__tests__/active-learning.test.js +0 -386
  6. package/dist/lib/__tests__/agent-performance-profiles.test.js +0 -325
  7. package/dist/lib/__tests__/auto-session.test.js +0 -661
  8. package/dist/lib/__tests__/context-assembly.test.js +0 -362
  9. package/dist/lib/__tests__/graph-expansion.test.js +0 -150
  10. package/dist/lib/__tests__/integration-memory-crud.test.js +0 -797
  11. package/dist/lib/__tests__/integration-memory-system.test.js +0 -281
  12. package/dist/lib/__tests__/lifecycle-maintenance.test.js +0 -207
  13. package/dist/lib/__tests__/pattern-detection.test.js +0 -295
  14. package/dist/lib/__tests__/prompt-builder.test.js +0 -418
  15. package/dist/lib/active-learning.js +0 -822
  16. package/dist/lib/auto-session.js +0 -214
  17. package/dist/lib/cli.js +0 -138
  18. package/dist/lib/consolidation.js +0 -303
  19. package/dist/lib/context-assembly.js +0 -884
  20. package/dist/lib/graph-expansion.js +0 -163
  21. package/dist/lib/http.js +0 -175
  22. package/dist/lib/index.js +0 -7
  23. package/dist/lib/lifecycle-maintenance.js +0 -88
  24. package/dist/lib/memory-cleanup.js +0 -455
  25. package/dist/lib/onboard.js +0 -36
  26. package/dist/lib/prompt-builder.js +0 -488
  27. package/dist/lib/remote.js +0 -166
  28. package/dist/lib/server.js +0 -3365
  29. package/dist/lib/skills.js +0 -593
  30. package/dist/lib/tui/agents.js +0 -116
  31. package/dist/lib/tui/docs.js +0 -744
  32. package/dist/lib/tui/setup.js +0 -934
  33. package/dist/lib/tui/theme.js +0 -95
  34. package/dist/lib/tui/writer.js +0 -200
@@ -1,205 +1,221 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, {
5
+ get: all[name],
6
+ enumerable: true,
7
+ configurable: true,
8
+ set: (newValue) => all[name] = () => newValue
9
+ });
10
+ };
11
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
12
+
13
+ // src/config.ts
1
14
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
15
  import { homedir } from "node:os";
3
16
  import { join } from "node:path";
4
- const DEFAULT_API_URL = "https://app.gethmy.com/api";
5
- const LOCAL_CONFIG_FILENAME = ".harmony-mcp.json";
6
- export function getConfigDir() {
7
- return join(homedir(), ".harmony-mcp");
8
- }
9
- export function getConfigPath() {
10
- return join(getConfigDir(), "config.json");
11
- }
12
- export function getLocalConfigPath(cwd) {
13
- return join(cwd || process.cwd(), LOCAL_CONFIG_FILENAME);
14
- }
15
- export function loadConfig() {
16
- const configPath = getConfigPath();
17
- if (!existsSync(configPath)) {
18
- return {
19
- apiKey: null,
20
- apiUrl: DEFAULT_API_URL,
21
- activeWorkspaceId: null,
22
- activeProjectId: null,
23
- userEmail: null,
24
- memoryDir: null,
25
- };
26
- }
27
- try {
28
- const data = readFileSync(configPath, "utf-8");
29
- const config = JSON.parse(data);
30
- return {
31
- apiKey: config.apiKey || null,
32
- apiUrl: config.apiUrl || DEFAULT_API_URL,
33
- activeWorkspaceId: config.activeWorkspaceId || null,
34
- activeProjectId: config.activeProjectId || null,
35
- userEmail: config.userEmail || null,
36
- memoryDir: config.memoryDir || null,
37
- };
38
- }
39
- catch {
40
- return {
41
- apiKey: null,
42
- apiUrl: DEFAULT_API_URL,
43
- activeWorkspaceId: null,
44
- activeProjectId: null,
45
- userEmail: null,
46
- memoryDir: null,
47
- };
48
- }
49
- }
50
- export function saveConfig(config) {
51
- const configDir = getConfigDir();
52
- const configPath = getConfigPath();
53
- if (!existsSync(configDir)) {
54
- mkdirSync(configDir, { recursive: true, mode: 0o700 });
55
- }
56
- const existingConfig = loadConfig();
57
- const newConfig = { ...existingConfig, ...config };
58
- writeFileSync(configPath, JSON.stringify(newConfig, null, 2), {
59
- mode: 0o600,
60
- });
61
- }
62
- export function loadLocalConfig(cwd) {
63
- const localConfigPath = getLocalConfigPath(cwd);
64
- if (!existsSync(localConfigPath)) {
65
- return null;
66
- }
67
- try {
68
- const data = readFileSync(localConfigPath, "utf-8");
69
- const config = JSON.parse(data);
70
- return {
71
- workspaceId: config.workspaceId || null,
72
- projectId: config.projectId || null,
73
- };
74
- }
75
- catch {
76
- return null;
77
- }
78
- }
79
- export function saveLocalConfig(config, cwd) {
80
- const localConfigPath = getLocalConfigPath(cwd);
81
- const existingConfig = loadLocalConfig(cwd) || {
82
- workspaceId: null,
83
- projectId: null,
17
+ var DEFAULT_API_URL = "https://app.gethmy.com/api";
18
+ var LOCAL_CONFIG_FILENAME = ".harmony-mcp.json";
19
+ function getConfigDir() {
20
+ return join(homedir(), ".harmony-mcp");
21
+ }
22
+ function getConfigPath() {
23
+ return join(getConfigDir(), "config.json");
24
+ }
25
+ function getLocalConfigPath(cwd) {
26
+ return join(cwd || process.cwd(), LOCAL_CONFIG_FILENAME);
27
+ }
28
+ function loadConfig() {
29
+ const configPath = getConfigPath();
30
+ if (!existsSync(configPath)) {
31
+ return {
32
+ apiKey: null,
33
+ apiUrl: DEFAULT_API_URL,
34
+ activeWorkspaceId: null,
35
+ activeProjectId: null,
36
+ userEmail: null,
37
+ memoryDir: null
84
38
  };
85
- const newConfig = { ...existingConfig, ...config };
86
- // Remove null values from the saved config for cleaner output
87
- const cleanConfig = {};
88
- if (newConfig.workspaceId)
89
- cleanConfig.workspaceId = newConfig.workspaceId;
90
- if (newConfig.projectId)
91
- cleanConfig.projectId = newConfig.projectId;
92
- writeFileSync(localConfigPath, JSON.stringify(cleanConfig, null, 2));
93
- }
94
- export function hasLocalConfig(cwd) {
95
- return existsSync(getLocalConfigPath(cwd));
96
- }
97
- export function getApiKey() {
98
- const config = loadConfig();
99
- if (!config.apiKey) {
100
- throw new Error('Not configured. Run "npx @gethmy/mcp setup" to set your API key.\n' +
101
- "You can generate an API key at https://gethmy.com → Settings → API Keys.");
102
- }
103
- return config.apiKey;
104
- }
105
- export function getApiUrl() {
106
- const config = loadConfig();
107
- return config.apiUrl;
108
- }
109
- export function getUserEmail() {
110
- const config = loadConfig();
111
- return config.userEmail;
112
- }
113
- export function setUserEmail(email) {
114
- saveConfig({ userEmail: email });
115
- }
116
- export function setActiveWorkspace(workspaceId, options) {
117
- if (options?.local) {
118
- saveLocalConfig({ workspaceId }, options.cwd);
119
- }
120
- else {
121
- saveConfig({ activeWorkspaceId: workspaceId });
122
- }
123
- }
124
- export function setActiveProject(projectId, options) {
125
- if (options?.local) {
126
- saveLocalConfig({ projectId }, options.cwd);
127
- }
128
- else {
129
- saveConfig({ activeProjectId: projectId });
130
- }
131
- }
132
- export function getActiveWorkspaceId(cwd) {
133
- // Local config takes precedence over global
134
- const localConfig = loadLocalConfig(cwd);
135
- if (localConfig?.workspaceId) {
136
- return localConfig.workspaceId;
137
- }
138
- return loadConfig().activeWorkspaceId;
139
- }
140
- export function getActiveProjectId(cwd) {
141
- // Local config takes precedence over global
142
- const localConfig = loadLocalConfig(cwd);
143
- if (localConfig?.projectId) {
144
- return localConfig.projectId;
145
- }
146
- return loadConfig().activeProjectId;
147
- }
148
- export function isConfigured() {
149
- const config = loadConfig();
150
- return !!config.apiKey;
151
- }
152
- /**
153
- * Check if skills are already installed (globally or locally).
154
- * Returns installation status and location.
155
- */
156
- export function areSkillsInstalled(cwd) {
157
- const home = homedir();
158
- const workingDir = cwd || process.cwd();
159
- const foundPaths = [];
160
- // Check global skills directory
161
- const globalSkillsDir = join(home, ".agents", "skills");
162
- const globalSkillPath = join(globalSkillsDir, "hmy", "SKILL.md");
163
- if (existsSync(globalSkillPath)) {
164
- foundPaths.push(globalSkillPath);
165
- return { installed: true, location: "global", paths: foundPaths };
166
- }
167
- // Check Claude global skills (symlinked from global skills)
168
- const claudeGlobalSkill = join(home, ".claude", "skills", "hmy.md");
169
- if (existsSync(claudeGlobalSkill)) {
170
- foundPaths.push(claudeGlobalSkill);
171
- return { installed: true, location: "global", paths: foundPaths };
172
- }
173
- // Check Claude global skills (alternate SKILL.md format)
174
- const claudeGlobalSkillAlt = join(home, ".claude", "skills", "hmy", "SKILL.md");
175
- if (existsSync(claudeGlobalSkillAlt)) {
176
- foundPaths.push(claudeGlobalSkillAlt);
177
- return { installed: true, location: "global", paths: foundPaths };
178
- }
179
- // Check local skills in project directory
180
- const localSkillPath = join(workingDir, ".claude", "skills", "hmy.md");
181
- if (existsSync(localSkillPath)) {
182
- foundPaths.push(localSkillPath);
183
- return { installed: true, location: "local", paths: foundPaths };
184
- }
185
- // Check local skills in project directory (alternate SKILL.md format)
186
- const localSkillPathAlt = join(workingDir, ".claude", "skills", "hmy", "SKILL.md");
187
- if (existsSync(localSkillPathAlt)) {
188
- foundPaths.push(localSkillPathAlt);
189
- return { installed: true, location: "local", paths: foundPaths };
190
- }
191
- return { installed: false, location: null, paths: [] };
192
- }
193
- /**
194
- * Check if project context is configured in the local directory.
195
- */
196
- export function hasProjectContext(cwd) {
197
- const localConfig = loadLocalConfig(cwd);
198
- return !!(localConfig?.workspaceId || localConfig?.projectId);
199
- }
200
- export function getMemoryDir() {
201
- const config = loadConfig();
202
- if (config.memoryDir)
203
- return config.memoryDir;
204
- return join(homedir(), ".harmony", "memory");
205
- }
39
+ }
40
+ try {
41
+ const data = readFileSync(configPath, "utf-8");
42
+ const config = JSON.parse(data);
43
+ return {
44
+ apiKey: config.apiKey || null,
45
+ apiUrl: config.apiUrl || DEFAULT_API_URL,
46
+ activeWorkspaceId: config.activeWorkspaceId || null,
47
+ activeProjectId: config.activeProjectId || null,
48
+ userEmail: config.userEmail || null,
49
+ memoryDir: config.memoryDir || null
50
+ };
51
+ } catch {
52
+ return {
53
+ apiKey: null,
54
+ apiUrl: DEFAULT_API_URL,
55
+ activeWorkspaceId: null,
56
+ activeProjectId: null,
57
+ userEmail: null,
58
+ memoryDir: null
59
+ };
60
+ }
61
+ }
62
+ function saveConfig(config) {
63
+ const configDir = getConfigDir();
64
+ const configPath = getConfigPath();
65
+ if (!existsSync(configDir)) {
66
+ mkdirSync(configDir, { recursive: true, mode: 448 });
67
+ }
68
+ const existingConfig = loadConfig();
69
+ const newConfig = { ...existingConfig, ...config };
70
+ writeFileSync(configPath, JSON.stringify(newConfig, null, 2), {
71
+ mode: 384
72
+ });
73
+ }
74
+ function loadLocalConfig(cwd) {
75
+ const localConfigPath = getLocalConfigPath(cwd);
76
+ if (!existsSync(localConfigPath)) {
77
+ return null;
78
+ }
79
+ try {
80
+ const data = readFileSync(localConfigPath, "utf-8");
81
+ const config = JSON.parse(data);
82
+ return {
83
+ workspaceId: config.workspaceId || null,
84
+ projectId: config.projectId || null
85
+ };
86
+ } catch {
87
+ return null;
88
+ }
89
+ }
90
+ function saveLocalConfig(config, cwd) {
91
+ const localConfigPath = getLocalConfigPath(cwd);
92
+ const existingConfig = loadLocalConfig(cwd) || {
93
+ workspaceId: null,
94
+ projectId: null
95
+ };
96
+ const newConfig = { ...existingConfig, ...config };
97
+ const cleanConfig = {};
98
+ if (newConfig.workspaceId)
99
+ cleanConfig.workspaceId = newConfig.workspaceId;
100
+ if (newConfig.projectId)
101
+ cleanConfig.projectId = newConfig.projectId;
102
+ writeFileSync(localConfigPath, JSON.stringify(cleanConfig, null, 2));
103
+ }
104
+ function hasLocalConfig(cwd) {
105
+ return existsSync(getLocalConfigPath(cwd));
106
+ }
107
+ function getApiKey() {
108
+ const config = loadConfig();
109
+ if (!config.apiKey) {
110
+ throw new Error(`Not configured. Run "npx @gethmy/mcp setup" to set your API key.
111
+ ` + "You can generate an API key at https://gethmy.com → Settings → API Keys.");
112
+ }
113
+ return config.apiKey;
114
+ }
115
+ function getApiUrl() {
116
+ const config = loadConfig();
117
+ return config.apiUrl;
118
+ }
119
+ function getUserEmail() {
120
+ const config = loadConfig();
121
+ return config.userEmail;
122
+ }
123
+ function setUserEmail(email) {
124
+ saveConfig({ userEmail: email });
125
+ }
126
+ function setActiveWorkspace(workspaceId, options) {
127
+ if (options?.local) {
128
+ saveLocalConfig({ workspaceId }, options.cwd);
129
+ } else {
130
+ saveConfig({ activeWorkspaceId: workspaceId });
131
+ }
132
+ }
133
+ function setActiveProject(projectId, options) {
134
+ if (options?.local) {
135
+ saveLocalConfig({ projectId }, options.cwd);
136
+ } else {
137
+ saveConfig({ activeProjectId: projectId });
138
+ }
139
+ }
140
+ function getActiveWorkspaceId(cwd) {
141
+ const localConfig = loadLocalConfig(cwd);
142
+ if (localConfig?.workspaceId) {
143
+ return localConfig.workspaceId;
144
+ }
145
+ return loadConfig().activeWorkspaceId;
146
+ }
147
+ function getActiveProjectId(cwd) {
148
+ const localConfig = loadLocalConfig(cwd);
149
+ if (localConfig?.projectId) {
150
+ return localConfig.projectId;
151
+ }
152
+ return loadConfig().activeProjectId;
153
+ }
154
+ function isConfigured() {
155
+ const config = loadConfig();
156
+ return !!config.apiKey;
157
+ }
158
+ function areSkillsInstalled(cwd) {
159
+ const home = homedir();
160
+ const workingDir = cwd || process.cwd();
161
+ const foundPaths = [];
162
+ const globalSkillsDir = join(home, ".agents", "skills");
163
+ const globalSkillPath = join(globalSkillsDir, "hmy", "SKILL.md");
164
+ if (existsSync(globalSkillPath)) {
165
+ foundPaths.push(globalSkillPath);
166
+ return { installed: true, location: "global", paths: foundPaths };
167
+ }
168
+ const claudeGlobalSkill = join(home, ".claude", "skills", "hmy.md");
169
+ if (existsSync(claudeGlobalSkill)) {
170
+ foundPaths.push(claudeGlobalSkill);
171
+ return { installed: true, location: "global", paths: foundPaths };
172
+ }
173
+ const claudeGlobalSkillAlt = join(home, ".claude", "skills", "hmy", "SKILL.md");
174
+ if (existsSync(claudeGlobalSkillAlt)) {
175
+ foundPaths.push(claudeGlobalSkillAlt);
176
+ return { installed: true, location: "global", paths: foundPaths };
177
+ }
178
+ const localSkillPath = join(workingDir, ".claude", "skills", "hmy.md");
179
+ if (existsSync(localSkillPath)) {
180
+ foundPaths.push(localSkillPath);
181
+ return { installed: true, location: "local", paths: foundPaths };
182
+ }
183
+ const localSkillPathAlt = join(workingDir, ".claude", "skills", "hmy", "SKILL.md");
184
+ if (existsSync(localSkillPathAlt)) {
185
+ foundPaths.push(localSkillPathAlt);
186
+ return { installed: true, location: "local", paths: foundPaths };
187
+ }
188
+ return { installed: false, location: null, paths: [] };
189
+ }
190
+ function hasProjectContext(cwd) {
191
+ const localConfig = loadLocalConfig(cwd);
192
+ return !!(localConfig?.workspaceId || localConfig?.projectId);
193
+ }
194
+ function getMemoryDir() {
195
+ const config = loadConfig();
196
+ if (config.memoryDir)
197
+ return config.memoryDir;
198
+ return join(homedir(), ".harmony", "memory");
199
+ }
200
+ export {
201
+ setUserEmail,
202
+ setActiveWorkspace,
203
+ setActiveProject,
204
+ saveLocalConfig,
205
+ saveConfig,
206
+ loadLocalConfig,
207
+ loadConfig,
208
+ isConfigured,
209
+ hasProjectContext,
210
+ hasLocalConfig,
211
+ getUserEmail,
212
+ getMemoryDir,
213
+ getLocalConfigPath,
214
+ getConfigPath,
215
+ getConfigDir,
216
+ getApiUrl,
217
+ getApiKey,
218
+ getActiveWorkspaceId,
219
+ getActiveProjectId,
220
+ areSkillsInstalled
221
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gethmy/mcp",
3
- "version": "2.3.1",
3
+ "version": "2.3.3",
4
4
  "description": "MCP server for Harmony Kanban board - enables AI coding agents to manage your boards",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -9,9 +9,13 @@
9
9
  "main": "dist/index.js",
10
10
  "exports": {
11
11
  ".": "./dist/index.js",
12
- "./src/*.js": {
13
- "types": "./src/*.ts",
14
- "default": "./dist/lib/*.js"
12
+ "./src/api-client.js": {
13
+ "types": "./src/api-client.ts",
14
+ "default": "./dist/lib/api-client.js"
15
+ },
16
+ "./src/config.js": {
17
+ "types": "./src/config.ts",
18
+ "default": "./dist/lib/config.js"
15
19
  }
16
20
  },
17
21
  "bin": {
@@ -50,7 +54,7 @@
50
54
  "bun": ">=1.0.0"
51
55
  },
52
56
  "scripts": {
53
- "build": "bun build src/index.ts src/cli.ts --outdir dist --target node && tsc --outDir dist/lib --declaration false --skipLibCheck --noCheck",
57
+ "build": "bun build src/index.ts src/cli.ts --outdir dist --target node && bun build src/api-client.ts src/config.ts --outdir dist/lib --root src --target node",
54
58
  "build:bun": "bun build src/index.ts src/http.ts src/remote.ts src/cli.ts --outdir dist --target bun",
55
59
  "serve:remote": "bun src/remote.ts",
56
60
  "dev": "bun --watch src/index.ts",
@@ -339,8 +339,7 @@ function runPruneStep(
339
339
  const stale: PruneStepResult["items"] = [];
340
340
 
341
341
  for (const entity of drafts) {
342
- const ageDays =
343
- (now - new Date(entity.created_at).getTime()) / MS_PER_DAY;
342
+ const ageDays = (now - new Date(entity.created_at).getTime()) / MS_PER_DAY;
344
343
  if (ageDays < maxAgeDays) continue;
345
344
 
346
345
  const lifecycle = evaluateLifecycle(entity);
@@ -367,8 +366,7 @@ async function runOrphanStep(
367
366
  const candidates = entities.filter((e) => {
368
367
  if (e.memory_tier === "reference") return false;
369
368
  if (e.access_count >= 2) return false;
370
- const ageDays =
371
- (now - new Date(e.created_at).getTime()) / MS_PER_DAY;
369
+ const ageDays = (now - new Date(e.created_at).getTime()) / MS_PER_DAY;
372
370
  return ageDays >= orphanAgeDays;
373
371
  });
374
372