@gethmy/mcp 2.3.2 → 2.3.4

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.
@@ -1,21 +1,4 @@
1
- import { createRequire } from "node:module";
2
- var __create = Object.create;
3
- var __getProtoOf = Object.getPrototypeOf;
4
1
  var __defProp = Object.defineProperty;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __toESM = (mod, isNodeMode, target) => {
8
- target = mod != null ? __create(__getProtoOf(mod)) : {};
9
- const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
- for (let key of __getOwnPropNames(mod))
11
- if (!__hasOwnProp.call(to, key))
12
- __defProp(to, key, {
13
- get: () => mod[key],
14
- enumerable: true
15
- });
16
- return to;
17
- };
18
- var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
19
2
  var __export = (target, all) => {
20
3
  for (var name in all)
21
4
  __defProp(target, name, {
@@ -26,7 +9,6 @@ var __export = (target, all) => {
26
9
  });
27
10
  };
28
11
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
29
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
30
12
 
31
13
  // ../memory/dist/schema.js
32
14
  var init_schema = () => {};
@@ -122,18 +104,6 @@ async function discoverRelatedContext(client, startIds, maxDepth = 2, maxEntitie
122
104
  }
123
105
 
124
106
  // ../memory/dist/lifecycle.js
125
- function computeDecayScore(tier, lastAccessedAt, accessCount) {
126
- const halfLife = DECAY_HALF_LIVES[tier];
127
- const now = Date.now();
128
- let daysSinceAccess = 0;
129
- if (lastAccessedAt) {
130
- daysSinceAccess = (now - new Date(lastAccessedAt).getTime()) / (1000 * 60 * 60 * 24);
131
- }
132
- const timeDecay = 0.5 ** (daysSinceAccess / halfLife);
133
- const accessBonus = Math.log10(accessCount + 1) * 0.1;
134
- const score = Math.min(timeDecay + accessBonus, 1);
135
- return { score, daysSinceAccess, halfLife, accessBonus };
136
- }
137
107
  function checkPromotion(currentTier, accessCount, confidence, createdAt) {
138
108
  const ageDays = (Date.now() - new Date(createdAt).getTime()) / (1000 * 60 * 60 * 24);
139
109
  const base = {
@@ -169,29 +139,8 @@ function checkPromotion(currentTier, accessCount, confidence, createdAt) {
169
139
  }
170
140
  return base;
171
141
  }
172
- function evaluateLifecycle(entity) {
173
- const decay = computeDecayScore(entity.memory_tier, entity.last_accessed_at, entity.access_count);
174
- const promotion = checkPromotion(entity.memory_tier, entity.access_count, entity.confidence, entity.created_at);
175
- const shouldArchive = entity.confidence < ARCHIVE_THRESHOLD;
176
- const archiveReason = shouldArchive ? `Confidence ${entity.confidence} below threshold ${ARCHIVE_THRESHOLD}` : undefined;
177
- const shouldFlagForReview = decay.daysSinceAccess >= STALE_DAYS && entity.access_count < STALE_MIN_ACCESS;
178
- const reviewReason = shouldFlagForReview ? `Not accessed in ${Math.round(decay.daysSinceAccess)} days with only ${entity.access_count} accesses` : undefined;
179
- return {
180
- decay,
181
- promotion,
182
- shouldArchive,
183
- shouldFlagForReview,
184
- archiveReason,
185
- reviewReason
186
- };
187
- }
188
- var DECAY_HALF_LIVES, PROMOTION_RULES, ARCHIVE_THRESHOLD = 0.3, STALE_DAYS = 90, STALE_MIN_ACCESS = 3;
142
+ var PROMOTION_RULES;
189
143
  var init_lifecycle = __esm(() => {
190
- DECAY_HALF_LIVES = {
191
- draft: 7,
192
- episode: 30,
193
- reference: 180
194
- };
195
144
  PROMOTION_RULES = {
196
145
  draftToEpisode: {
197
146
  minAccessCount: 5,
@@ -205,388 +154,7 @@ var init_lifecycle = __esm(() => {
205
154
  }
206
155
  };
207
156
  });
208
-
209
- // ../memory/dist/sync-storage.js
210
- function parseSyncMarkdown(markdown) {
211
- const trimmed = markdown.trim();
212
- let frontmatter = {
213
- type: "context",
214
- scope: "project",
215
- tier: "reference",
216
- confidence: 1,
217
- tags: []
218
- };
219
- let body = trimmed;
220
- const fmMatch = trimmed.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
221
- if (fmMatch) {
222
- frontmatter = parseSyncYamlFrontmatter(fmMatch[1]);
223
- body = fmMatch[2].trim();
224
- }
225
- let title = "";
226
- const titleMatch = body.match(/^#\s+(.+)/m);
227
- if (titleMatch) {
228
- title = titleMatch[1].trim();
229
- body = body.replace(/^#\s+.+\n?/, "").trim();
230
- }
231
- return { frontmatter, title, content: body };
232
- }
233
- function serializeSyncMarkdown(entity) {
234
- const lines = ["---"];
235
- lines.push(`id: ${entity.id}`);
236
- lines.push(`workspace_id: ${entity.workspace_id}`);
237
- if (entity.project_id) {
238
- lines.push(`project_id: ${entity.project_id}`);
239
- }
240
- lines.push(`type: ${entity.type}`);
241
- lines.push(`scope: ${entity.scope}`);
242
- lines.push(`tier: ${entity.memory_tier || "reference"}`);
243
- lines.push(`confidence: ${entity.confidence}`);
244
- if (entity.tags.length > 0) {
245
- lines.push(`tags: [${entity.tags.join(", ")}]`);
246
- } else {
247
- lines.push("tags: []");
248
- }
249
- if (entity.agent_identifier) {
250
- lines.push(`agent: ${entity.agent_identifier}`);
251
- }
252
- lines.push(`created_at: ${entity.created_at}`);
253
- lines.push(`updated_at: ${entity.updated_at}`);
254
- lines.push("---");
255
- lines.push("");
256
- lines.push(`# ${entity.title}`);
257
- lines.push("");
258
- lines.push(entity.content);
259
- return lines.join(`
260
- `);
261
- }
262
- function parseSyncYamlFrontmatter(yaml) {
263
- const result = {
264
- type: "context",
265
- scope: "project",
266
- tier: "reference",
267
- confidence: 1,
268
- tags: []
269
- };
270
- for (const line of yaml.split(`
271
- `)) {
272
- const colonIndex = line.indexOf(":");
273
- if (colonIndex === -1)
274
- continue;
275
- const key = line.slice(0, colonIndex).trim();
276
- const value = line.slice(colonIndex + 1).trim();
277
- switch (key) {
278
- case "id":
279
- result.id = value;
280
- break;
281
- case "workspace_id":
282
- result.workspace_id = value;
283
- break;
284
- case "project_id":
285
- result.project_id = value;
286
- break;
287
- case "type":
288
- result.type = value;
289
- break;
290
- case "scope":
291
- result.scope = value;
292
- break;
293
- case "tier":
294
- result.tier = value;
295
- break;
296
- case "confidence":
297
- result.confidence = parseFloat(value) || 1;
298
- break;
299
- case "tags":
300
- result.tags = parseYamlArray(value);
301
- break;
302
- case "related":
303
- result.related = parseYamlArray(value);
304
- break;
305
- case "agent":
306
- result.agent = value;
307
- break;
308
- case "created_at":
309
- result.created_at = value;
310
- break;
311
- case "updated_at":
312
- result.updated_at = value;
313
- break;
314
- }
315
- }
316
- return result;
317
- }
318
- function parseYamlArray(value) {
319
- const match = value.match(/^\[(.*)]\s*$/);
320
- if (!match)
321
- return [];
322
- return match[1].split(",").map((s) => s.trim()).filter(Boolean);
323
- }
324
-
325
157
  // ../memory/dist/sync.js
326
- import { createHash } from "node:crypto";
327
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync2, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
328
- import { join as join2, relative, sep } from "node:path";
329
- function computeFileHash(content) {
330
- return `sha256:${createHash("sha256").update(content).digest("hex")}`;
331
- }
332
- function slugifyTitle(title) {
333
- return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
334
- }
335
- function entityToFilename(entity) {
336
- const slug = slugifyTitle(entity.title);
337
- const shortId = entity.id.slice(0, 8);
338
- return `${entity.type}--${slug}--${shortId}.md`;
339
- }
340
- function entityToDirectoryPath(entity, memoryDir) {
341
- const wsDir = join2(memoryDir, entity.workspace_id);
342
- if (entity.scope === "private") {
343
- return join2(wsDir, "_private");
344
- }
345
- if (entity.scope === "workspace" || !entity.project_id) {
346
- return join2(wsDir, "_workspace");
347
- }
348
- return join2(wsDir, entity.project_id);
349
- }
350
- function emptySyncState() {
351
- return { version: 1, lastPullAt: null, entities: {} };
352
- }
353
- function loadSyncState(memoryDir) {
354
- const statePath = join2(memoryDir, ".sync-state.json");
355
- if (!existsSync2(statePath))
356
- return emptySyncState();
357
- try {
358
- return JSON.parse(readFileSync2(statePath, "utf-8"));
359
- } catch {
360
- return emptySyncState();
361
- }
362
- }
363
- function saveSyncState(memoryDir, state) {
364
- if (!existsSync2(memoryDir)) {
365
- mkdirSync2(memoryDir, { recursive: true });
366
- }
367
- writeFileSync2(join2(memoryDir, ".sync-state.json"), JSON.stringify(state, null, 2));
368
- }
369
- function writeEntityFile(entity, memoryDir) {
370
- const dir = entityToDirectoryPath(entity, memoryDir);
371
- if (!existsSync2(dir))
372
- mkdirSync2(dir, { recursive: true });
373
- const filename = entityToFilename(entity);
374
- const filePath = join2(dir, filename);
375
- const markdown = serializeSyncMarkdown(entity);
376
- writeFileSync2(filePath, markdown);
377
- return relative(memoryDir, filePath);
378
- }
379
- function deleteEntityFile(relPath, memoryDir) {
380
- const absPath = join2(memoryDir, relPath);
381
- if (existsSync2(absPath)) {
382
- rmSync(absPath);
383
- }
384
- }
385
- function findMarkdownFiles(dir) {
386
- const results = [];
387
- if (!existsSync2(dir))
388
- return results;
389
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
390
- const fullPath = join2(dir, entry.name);
391
- if (entry.isDirectory()) {
392
- results.push(...findMarkdownFiles(fullPath));
393
- } else if (entry.isFile() && entry.name.endsWith(".md")) {
394
- results.push(fullPath);
395
- }
396
- }
397
- return results;
398
- }
399
- async function syncPull(client, config, workspaceId, projectId) {
400
- const { memoryDir } = config;
401
- const state = loadSyncState(memoryDir);
402
- const result = {
403
- pulled: 0,
404
- pushed: 0,
405
- deleted: 0,
406
- conflicts: 0,
407
- errors: []
408
- };
409
- const allEntities = [];
410
- let offset = 0;
411
- const batchSize = 100;
412
- while (true) {
413
- try {
414
- const resp = await client.listMemoryEntities({
415
- workspace_id: workspaceId,
416
- project_id: projectId,
417
- limit: batchSize,
418
- offset
419
- });
420
- const batch = resp.entities;
421
- allEntities.push(...batch);
422
- if (batch.length < batchSize)
423
- break;
424
- offset += batchSize;
425
- } catch (err) {
426
- result.errors.push(`Failed to fetch entities: ${err}`);
427
- return result;
428
- }
429
- }
430
- const remoteIds = new Set(allEntities.map((e) => e.id));
431
- for (const entity of allEntities) {
432
- const existing = state.entities[entity.id];
433
- const markdown = serializeSyncMarkdown(entity);
434
- const hash = computeFileHash(markdown);
435
- if (!existing) {
436
- const relPath = writeEntityFile(entity, memoryDir);
437
- state.entities[entity.id] = {
438
- filePath: relPath,
439
- remoteUpdatedAt: entity.updated_at,
440
- lastSyncedHash: hash
441
- };
442
- result.pulled++;
443
- } else {
444
- const remoteChanged = entity.updated_at > existing.remoteUpdatedAt;
445
- if (!remoteChanged)
446
- continue;
447
- const absPath = join2(memoryDir, existing.filePath);
448
- let localChanged = false;
449
- if (existsSync2(absPath)) {
450
- const localContent = readFileSync2(absPath, "utf-8");
451
- const localHash = computeFileHash(localContent);
452
- localChanged = localHash !== existing.lastSyncedHash;
453
- }
454
- if (localChanged) {
455
- result.conflicts++;
456
- }
457
- const newRelPath = writeEntityFile(entity, memoryDir);
458
- if (existing.filePath !== newRelPath) {
459
- deleteEntityFile(existing.filePath, memoryDir);
460
- }
461
- state.entities[entity.id] = {
462
- filePath: newRelPath,
463
- remoteUpdatedAt: entity.updated_at,
464
- lastSyncedHash: hash
465
- };
466
- result.pulled++;
467
- }
468
- }
469
- for (const [entityId, entry] of Object.entries(state.entities)) {
470
- if (!remoteIds.has(entityId)) {
471
- deleteEntityFile(entry.filePath, memoryDir);
472
- delete state.entities[entityId];
473
- result.deleted++;
474
- }
475
- }
476
- state.lastPullAt = new Date().toISOString();
477
- saveSyncState(memoryDir, state);
478
- return result;
479
- }
480
- async function syncPush(client, config, workspaceId) {
481
- const { memoryDir } = config;
482
- const state = loadSyncState(memoryDir);
483
- const result = {
484
- pulled: 0,
485
- pushed: 0,
486
- deleted: 0,
487
- conflicts: 0,
488
- errors: []
489
- };
490
- const mdFiles = findMarkdownFiles(memoryDir);
491
- for (const absPath of mdFiles) {
492
- const content = readFileSync2(absPath, "utf-8");
493
- const hash = computeFileHash(content);
494
- const parsed = parseSyncMarkdown(content);
495
- if (parsed.frontmatter.id) {
496
- const entityId = parsed.frontmatter.id;
497
- const existing = state.entities[entityId];
498
- if (existing && hash === existing.lastSyncedHash)
499
- continue;
500
- try {
501
- const resp = await client.updateMemoryEntity(entityId, {
502
- title: parsed.title || "Untitled",
503
- content: parsed.content,
504
- type: parsed.frontmatter.type,
505
- scope: parsed.frontmatter.scope,
506
- confidence: parsed.frontmatter.confidence,
507
- tags: parsed.frontmatter.tags
508
- });
509
- const updated = resp.entity;
510
- const newMarkdown = serializeSyncMarkdown(updated);
511
- writeFileSync2(absPath, newMarkdown);
512
- const relPath = relative(memoryDir, absPath);
513
- state.entities[entityId] = {
514
- filePath: relPath,
515
- remoteUpdatedAt: updated.updated_at,
516
- lastSyncedHash: computeFileHash(newMarkdown)
517
- };
518
- result.pushed++;
519
- } catch (err) {
520
- result.errors.push(`Failed to update ${entityId}: ${err}`);
521
- }
522
- } else {
523
- const relPath = relative(memoryDir, absPath);
524
- const parts = relPath.split(sep);
525
- const fileWorkspaceId = parts.length >= 2 ? parts[0] : workspaceId;
526
- let fileProjectId;
527
- if (parts.length >= 3) {
528
- const scopeDir = parts[1];
529
- if (scopeDir !== "_workspace" && scopeDir !== "_private") {
530
- fileProjectId = scopeDir;
531
- }
532
- }
533
- let scope = parsed.frontmatter.scope || "project";
534
- if (parts.length >= 3) {
535
- const scopeDir = parts[1];
536
- if (scopeDir === "_private")
537
- scope = "private";
538
- else if (scopeDir === "_workspace")
539
- scope = "workspace";
540
- }
541
- try {
542
- const resp = await client.createMemoryEntity({
543
- workspace_id: fileWorkspaceId,
544
- project_id: fileProjectId,
545
- type: parsed.frontmatter.type,
546
- scope,
547
- title: parsed.title || "Untitled",
548
- content: parsed.content,
549
- confidence: parsed.frontmatter.confidence,
550
- tags: parsed.frontmatter.tags,
551
- agent_identifier: parsed.frontmatter.agent
552
- });
553
- const created = resp.entity;
554
- const dir = entityToDirectoryPath(created, memoryDir);
555
- if (!existsSync2(dir))
556
- mkdirSync2(dir, { recursive: true });
557
- const newFilename = entityToFilename(created);
558
- const newAbsPath = join2(dir, newFilename);
559
- const newMarkdown = serializeSyncMarkdown(created);
560
- writeFileSync2(newAbsPath, newMarkdown);
561
- if (absPath !== newAbsPath && existsSync2(absPath)) {
562
- rmSync(absPath);
563
- }
564
- const newRelPath = relative(memoryDir, newAbsPath);
565
- state.entities[created.id] = {
566
- filePath: newRelPath,
567
- remoteUpdatedAt: created.updated_at,
568
- lastSyncedHash: computeFileHash(newMarkdown)
569
- };
570
- result.pushed++;
571
- } catch (err) {
572
- result.errors.push(`Failed to create entity from ${relPath}: ${err}`);
573
- }
574
- }
575
- }
576
- saveSyncState(memoryDir, state);
577
- return result;
578
- }
579
- async function syncFull(client, config, workspaceId, projectId) {
580
- const pullResult = await syncPull(client, config, workspaceId, projectId);
581
- const pushResult = await syncPush(client, config, workspaceId);
582
- return {
583
- pulled: pullResult.pulled,
584
- pushed: pushResult.pushed,
585
- deleted: pullResult.deleted,
586
- conflicts: pullResult.conflicts,
587
- errors: [...pullResult.errors, ...pushResult.errors]
588
- };
589
- }
590
158
  var init_sync = () => {};
591
159
 
592
160
  // ../memory/dist/index.js
@@ -1,21 +1,4 @@
1
- import { createRequire } from "node:module";
2
- var __create = Object.create;
3
- var __getProtoOf = Object.getPrototypeOf;
4
1
  var __defProp = Object.defineProperty;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __toESM = (mod, isNodeMode, target) => {
8
- target = mod != null ? __create(__getProtoOf(mod)) : {};
9
- const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
- for (let key of __getOwnPropNames(mod))
11
- if (!__hasOwnProp.call(to, key))
12
- __defProp(to, key, {
13
- get: () => mod[key],
14
- enumerable: true
15
- });
16
- return to;
17
- };
18
- var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
19
2
  var __export = (target, all) => {
20
3
  for (var name in all)
21
4
  __defProp(target, name, {
@@ -26,7 +9,6 @@ var __export = (target, all) => {
26
9
  });
27
10
  };
28
11
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
29
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
30
12
 
31
13
  // src/config.ts
32
14
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gethmy/mcp",
3
- "version": "2.3.2",
3
+ "version": "2.3.4",
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 && bun build src/*.ts src/tui/*.ts --outdir dist/lib --root src --target node --external bun",
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",
package/src/api-client.ts CHANGED
@@ -502,6 +502,9 @@ export class HarmonyApiClient {
502
502
  phase?: string;
503
503
  filesChanged?: number;
504
504
  costCents?: number;
505
+ inputTokens?: number;
506
+ outputTokens?: number;
507
+ recentActions?: { action: string; ts: string }[];
505
508
  },
506
509
  ): Promise<{ session: unknown; created: boolean }> {
507
510
  return this.request("POST", `/cards/${cardId}/agent-context`, data);
@@ -512,6 +515,9 @@ export class HarmonyApiClient {
512
515
  data?: {
513
516
  status?: "completed" | "paused";
514
517
  progressPercent?: number;
518
+ costCents?: number;
519
+ inputTokens?: number;
520
+ outputTokens?: number;
515
521
  },
516
522
  ): Promise<{ session: unknown }> {
517
523
  return this.request("DELETE", `/cards/${cardId}/agent-context`, data);