@gethmy/mcp 2.0.0 → 2.1.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.
Files changed (62) hide show
  1. package/README.md +6 -1
  2. package/dist/cli.js +711 -59
  3. package/dist/index.js +5 -3
  4. package/dist/lib/__tests__/active-learning.test.js +386 -0
  5. package/dist/lib/__tests__/agent-performance-profiles.test.js +325 -0
  6. package/dist/lib/__tests__/auto-session.test.js +661 -0
  7. package/dist/lib/__tests__/context-assembly.test.js +362 -0
  8. package/dist/lib/__tests__/graph-expansion.test.js +150 -0
  9. package/dist/lib/__tests__/integration-memory-crud.test.js +797 -0
  10. package/dist/lib/__tests__/integration-memory-system.test.js +281 -0
  11. package/dist/lib/__tests__/lifecycle-maintenance.test.js +207 -0
  12. package/dist/lib/__tests__/pattern-detection.test.js +295 -0
  13. package/dist/lib/__tests__/prompt-builder.test.js +418 -0
  14. package/dist/lib/active-learning.js +878 -0
  15. package/dist/lib/api-client.js +550 -0
  16. package/dist/lib/auto-session.js +173 -0
  17. package/dist/lib/cli.js +127 -0
  18. package/dist/lib/config.js +205 -0
  19. package/dist/lib/consolidation.js +243 -0
  20. package/dist/lib/context-assembly.js +606 -0
  21. package/dist/lib/graph-expansion.js +163 -0
  22. package/dist/lib/http.js +174 -0
  23. package/dist/lib/index.js +7 -0
  24. package/dist/lib/lifecycle-maintenance.js +88 -0
  25. package/dist/lib/prompt-builder.js +483 -0
  26. package/dist/lib/remote.js +166 -0
  27. package/dist/lib/server.js +3132 -0
  28. package/dist/lib/tui/agents.js +116 -0
  29. package/dist/lib/tui/docs.js +744 -0
  30. package/dist/lib/tui/setup.js +1068 -0
  31. package/dist/lib/tui/theme.js +95 -0
  32. package/dist/lib/tui/writer.js +200 -0
  33. package/package.json +15 -6
  34. package/src/__tests__/active-learning.test.ts +483 -0
  35. package/src/__tests__/agent-performance-profiles.test.ts +468 -0
  36. package/src/__tests__/auto-session.test.ts +912 -0
  37. package/src/__tests__/context-assembly.test.ts +506 -0
  38. package/src/__tests__/graph-expansion.test.ts +285 -0
  39. package/src/__tests__/integration-memory-crud.test.ts +948 -0
  40. package/src/__tests__/integration-memory-system.test.ts +321 -0
  41. package/src/__tests__/lifecycle-maintenance.test.ts +238 -0
  42. package/src/__tests__/pattern-detection.test.ts +438 -0
  43. package/src/__tests__/prompt-builder.test.ts +505 -0
  44. package/src/active-learning.ts +1227 -0
  45. package/src/api-client.ts +969 -0
  46. package/src/auto-session.ts +218 -0
  47. package/src/cli.ts +166 -0
  48. package/src/config.ts +285 -0
  49. package/src/consolidation.ts +314 -0
  50. package/src/context-assembly.ts +842 -0
  51. package/src/graph-expansion.ts +234 -0
  52. package/src/http.ts +265 -0
  53. package/src/index.ts +8 -0
  54. package/src/lifecycle-maintenance.ts +120 -0
  55. package/src/prompt-builder.ts +681 -0
  56. package/src/remote.ts +227 -0
  57. package/src/server.ts +3858 -0
  58. package/src/tui/agents.ts +154 -0
  59. package/src/tui/docs.ts +863 -0
  60. package/src/tui/setup.ts +1281 -0
  61. package/src/tui/theme.ts +114 -0
  62. package/src/tui/writer.ts +260 -0
@@ -0,0 +1,95 @@
1
+ import * as pc from "picocolors";
2
+ /**
3
+ * Consistent theme for Harmony MCP TUI
4
+ * Uses picocolors for lightweight color output
5
+ */
6
+ // Brand colors and symbols
7
+ export const symbols = {
8
+ harmony: "\u25B2", // Triangle for Harmony logo
9
+ check: "\u2713",
10
+ cross: "\u2717",
11
+ bullet: "\u2022",
12
+ arrow: "\u2192",
13
+ arrowRight: "\u25B8",
14
+ dot: "\u25CF",
15
+ dotEmpty: "\u25CB",
16
+ info: "\u2139",
17
+ warning: "\u26A0",
18
+ pointer: "\u276F",
19
+ };
20
+ // Color functions
21
+ export const colors = {
22
+ // Brand
23
+ brand: (text) => pc.cyan(text),
24
+ brandBold: (text) => pc.bold(pc.cyan(text)),
25
+ // Status
26
+ success: (text) => pc.green(text),
27
+ error: (text) => pc.red(text),
28
+ warning: (text) => pc.yellow(text),
29
+ info: (text) => pc.blue(text),
30
+ // Text
31
+ dim: (text) => pc.dim(text),
32
+ bold: (text) => pc.bold(text),
33
+ muted: (text) => pc.gray(text),
34
+ // Highlights
35
+ highlight: (text) => pc.cyan(text),
36
+ link: (text) => pc.underline(pc.cyan(text)),
37
+ };
38
+ // Styled messages
39
+ export const messages = {
40
+ header: () => {
41
+ return `
42
+ ${colors.brandBold(" HARMONY")}
43
+ ${colors.dim(" Project management for AI agents")}
44
+ `;
45
+ },
46
+ done: (message) => {
47
+ return `${colors.success(symbols.check)} ${message}`;
48
+ },
49
+ fail: (message) => {
50
+ return `${colors.error(symbols.cross)} ${message}`;
51
+ },
52
+ skip: (message) => {
53
+ return `${colors.dim("-")} ${colors.dim(message)}`;
54
+ },
55
+ step: (number, total, message) => {
56
+ return `${colors.dim(`[${number}/${total}]`)} ${message}`;
57
+ },
58
+ fileCreated: (path) => {
59
+ return ` ${colors.success(symbols.check)} ${colors.dim(path)}`;
60
+ },
61
+ fileSkipped: (path) => {
62
+ return ` ${colors.dim(symbols.bullet)} ${colors.dim(path)} ${colors.dim("(exists)")}`;
63
+ },
64
+ fileError: (path, error) => {
65
+ return ` ${colors.error(symbols.cross)} ${path}: ${colors.error(error)}`;
66
+ },
67
+ };
68
+ // Box drawing for sections
69
+ export function box(title, content) {
70
+ const lines = content.split("\n");
71
+ const maxWidth = Math.max(title.length, ...lines.map((l) => l.length));
72
+ const width = maxWidth + 4;
73
+ const top = `\u250C${"─".repeat(width)}\u2510`;
74
+ const bottom = `\u2514${"─".repeat(width)}\u2518`;
75
+ const titleLine = `\u2502 ${colors.bold(title)}${" ".repeat(width - title.length - 1)}\u2502`;
76
+ const contentLines = lines
77
+ .map((line) => `\u2502 ${line}${" ".repeat(width - line.length - 1)}\u2502`)
78
+ .join("\n");
79
+ return `${top}\n${titleLine}\n${contentLines}\n${bottom}`;
80
+ }
81
+ // Format file paths for display (shorten home directory)
82
+ export function formatPath(path, homeDir) {
83
+ if (path.startsWith(homeDir)) {
84
+ return `~${path.slice(homeDir.length)}`;
85
+ }
86
+ return path;
87
+ }
88
+ // Indent helper
89
+ export function indent(text, spaces = 2) {
90
+ const pad = " ".repeat(spaces);
91
+ return text
92
+ .split("\n")
93
+ .map((line) => `${pad}${line}`)
94
+ .join("\n");
95
+ }
@@ -0,0 +1,200 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { dirname } from "node:path";
4
+ import * as p from "@clack/prompts";
5
+ import { colors, formatPath, messages } from "./theme.js";
6
+ /**
7
+ * Ensure directory exists
8
+ */
9
+ function ensureDir(dirPath) {
10
+ if (!existsSync(dirPath)) {
11
+ mkdirSync(dirPath, { recursive: true, mode: 0o755 });
12
+ }
13
+ }
14
+ /**
15
+ * Write a file, optionally skipping if exists
16
+ */
17
+ export function writeFile(filePath, content, options = {}) {
18
+ const exists = existsSync(filePath);
19
+ if (exists && !options.force) {
20
+ return { path: filePath, action: "skip" };
21
+ }
22
+ try {
23
+ ensureDir(dirname(filePath));
24
+ // Use restrictive permissions for config files
25
+ const mode = filePath.includes(".harmony-mcp") ? 0o600 : 0o644;
26
+ writeFileSync(filePath, content, { mode });
27
+ return { path: filePath, action: exists ? "update" : "create" };
28
+ }
29
+ catch (error) {
30
+ return {
31
+ path: filePath,
32
+ action: "skip",
33
+ error: error instanceof Error ? error.message : String(error),
34
+ };
35
+ }
36
+ }
37
+ /**
38
+ * Merge JSON content into existing file
39
+ */
40
+ export function mergeJsonFile(filePath, updates, options = {}) {
41
+ const exists = existsSync(filePath);
42
+ if (!exists) {
43
+ try {
44
+ ensureDir(dirname(filePath));
45
+ writeFileSync(filePath, JSON.stringify(updates, null, 2), {
46
+ mode: 0o644,
47
+ });
48
+ return { path: filePath, action: "create" };
49
+ }
50
+ catch (error) {
51
+ return {
52
+ path: filePath,
53
+ action: "skip",
54
+ error: error instanceof Error ? error.message : String(error),
55
+ };
56
+ }
57
+ }
58
+ try {
59
+ const existing = JSON.parse(readFileSync(filePath, "utf-8"));
60
+ // Deep merge mcpServers if present (always update harmony config)
61
+ if (updates.mcpServers && existing.mcpServers) {
62
+ const existingServers = existing.mcpServers;
63
+ const updateServers = updates.mcpServers;
64
+ existing.mcpServers = { ...existingServers, ...updateServers };
65
+ }
66
+ else {
67
+ Object.assign(existing, updates);
68
+ }
69
+ writeFileSync(filePath, JSON.stringify(existing, null, 2), { mode: 0o644 });
70
+ return { path: filePath, action: "merge" };
71
+ }
72
+ catch {
73
+ if (options.force) {
74
+ try {
75
+ writeFileSync(filePath, JSON.stringify(updates, null, 2), {
76
+ mode: 0o644,
77
+ });
78
+ return { path: filePath, action: "update" };
79
+ }
80
+ catch (error) {
81
+ return {
82
+ path: filePath,
83
+ action: "skip",
84
+ error: error instanceof Error ? error.message : String(error),
85
+ };
86
+ }
87
+ }
88
+ return {
89
+ path: filePath,
90
+ action: "skip",
91
+ error: "Could not parse existing file",
92
+ };
93
+ }
94
+ }
95
+ /**
96
+ * Append to TOML file (for Codex config)
97
+ */
98
+ export function appendToToml(filePath, section, content, options = {}) {
99
+ const exists = existsSync(filePath);
100
+ if (!exists) {
101
+ try {
102
+ ensureDir(dirname(filePath));
103
+ writeFileSync(filePath, content, { mode: 0o644 });
104
+ return { path: filePath, action: "create" };
105
+ }
106
+ catch (error) {
107
+ return {
108
+ path: filePath,
109
+ action: "skip",
110
+ error: error instanceof Error ? error.message : String(error),
111
+ };
112
+ }
113
+ }
114
+ try {
115
+ const existing = readFileSync(filePath, "utf-8");
116
+ if (existing.includes(section)) {
117
+ if (options.force) {
118
+ // Replace existing section
119
+ const updated = existing.replace(new RegExp(`\\[${section.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\][\\s\\S]*?(?=\\[|$)`), content.trim() + "\n\n");
120
+ writeFileSync(filePath, updated, { mode: 0o644 });
121
+ return { path: filePath, action: "update" };
122
+ }
123
+ return { path: filePath, action: "skip" };
124
+ }
125
+ // Append new section
126
+ writeFileSync(filePath, existing + "\n" + content, { mode: 0o644 });
127
+ return { path: filePath, action: "merge" };
128
+ }
129
+ catch (error) {
130
+ return {
131
+ path: filePath,
132
+ action: "skip",
133
+ error: error instanceof Error ? error.message : String(error),
134
+ };
135
+ }
136
+ }
137
+ /**
138
+ * Write multiple files with progress display
139
+ */
140
+ export async function writeFilesWithProgress(files, options = {}) {
141
+ const results = [];
142
+ const home = homedir();
143
+ const spinner = p.spinner();
144
+ spinner.start("Writing configuration files...");
145
+ for (const file of files) {
146
+ let result;
147
+ if (file.type === "json") {
148
+ const jsonContent = JSON.parse(file.content);
149
+ result = mergeJsonFile(file.path, jsonContent, options);
150
+ }
151
+ else if (file.type === "toml" && file.tomlSection) {
152
+ result = appendToToml(file.path, file.tomlSection, file.content, options);
153
+ }
154
+ else {
155
+ result = writeFile(file.path, file.content, options);
156
+ }
157
+ results.push(result);
158
+ // Small delay for visual effect
159
+ await new Promise((resolve) => setTimeout(resolve, 50));
160
+ }
161
+ spinner.stop("Files written");
162
+ // Display results
163
+ for (const result of results) {
164
+ const displayPath = formatPath(result.path, home);
165
+ if (result.error) {
166
+ console.log(messages.fileError(displayPath, result.error));
167
+ }
168
+ else if (result.action === "skip") {
169
+ console.log(messages.fileSkipped(displayPath));
170
+ }
171
+ else {
172
+ const actionLabel = result.action === "merge" ? "updated" : "created";
173
+ console.log(` ${colors.success("\u2713")} ${colors.dim(displayPath)} ${colors.dim(`(${actionLabel})`)}`);
174
+ }
175
+ }
176
+ return results;
177
+ }
178
+ /**
179
+ * Get summary of what will be written
180
+ */
181
+ export function getWriteSummary(files, options = {}) {
182
+ const toCreate = [];
183
+ const toUpdate = [];
184
+ const toSkip = [];
185
+ const home = homedir();
186
+ for (const file of files) {
187
+ const displayPath = formatPath(file.path, home);
188
+ const exists = existsSync(file.path);
189
+ if (exists && !options.force) {
190
+ toSkip.push(displayPath);
191
+ }
192
+ else if (exists) {
193
+ toUpdate.push(displayPath);
194
+ }
195
+ else {
196
+ toCreate.push(displayPath);
197
+ }
198
+ }
199
+ return { toCreate, toUpdate, toSkip };
200
+ }
package/package.json CHANGED
@@ -1,18 +1,26 @@
1
1
  {
2
2
  "name": "@gethmy/mcp",
3
- "version": "2.0.0",
3
+ "version": "2.1.1",
4
4
  "description": "MCP server for Harmony Kanban board - enables AI coding agents to manage your boards",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
8
8
  "type": "module",
9
9
  "main": "dist/index.js",
10
+ "exports": {
11
+ ".": "./dist/index.js",
12
+ "./src/*.js": {
13
+ "types": "./src/*.ts",
14
+ "default": "./dist/lib/*.js"
15
+ }
16
+ },
10
17
  "bin": {
11
18
  "harmony-mcp": "dist/cli.js",
12
19
  "gethmy-mcp": "dist/cli.js"
13
20
  },
14
21
  "files": [
15
22
  "dist",
23
+ "src",
16
24
  "README.md"
17
25
  ],
18
26
  "repository": {
@@ -38,15 +46,16 @@
38
46
  "coding-assistant"
39
47
  ],
40
48
  "engines": {
41
- "node": ">=18.0.0"
49
+ "node": ">=20.0.0",
50
+ "bun": ">=1.0.0"
42
51
  },
43
52
  "scripts": {
44
- "build": "bun build src/index.ts src/cli.ts --outdir dist --target node",
53
+ "build": "bun build src/index.ts src/cli.ts --outdir dist --target node && tsc --outDir dist/lib --declaration false --skipLibCheck --noCheck",
45
54
  "build:bun": "bun build src/index.ts src/http.ts src/remote.ts src/cli.ts --outdir dist --target bun",
46
55
  "serve:remote": "bun src/remote.ts",
47
56
  "dev": "bun --watch src/index.ts",
48
57
  "test": "bun run test:unit && bun run test:integration",
49
- "test:unit": "bun test src/__tests__/active-learning.test.ts src/__tests__/context-assembly.test.ts",
58
+ "test:unit": "bun test src/__tests__/active-learning.test.ts src/__tests__/context-assembly.test.ts src/__tests__/prompt-builder.test.ts",
50
59
  "test:integration": "bun test src/__tests__/integration-memory-system.test.ts src/__tests__/integration-memory-crud.test.ts",
51
60
  "typecheck": "tsc --noEmit",
52
61
  "prepublishOnly": "bun run build"
@@ -60,7 +69,7 @@
60
69
  "zod": "^4.3.6"
61
70
  },
62
71
  "devDependencies": {
63
- "@types/node": "^25.2.0",
64
- "typescript": "^5.9.3"
72
+ "@types/node": "^25.5.0",
73
+ "typescript": "^6.0.1"
65
74
  }
66
75
  }