@gethmy/mcp 2.1.2 → 2.2.0
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/dist/cli.js +165 -18
- package/dist/index.js +152 -9
- package/dist/lib/auto-session.js +8 -3
- package/dist/lib/cli.js +1 -1
- package/dist/lib/config.js +1 -1
- package/dist/lib/http.js +1 -0
- package/dist/lib/prompt-builder.js +1 -0
- package/dist/lib/remote.js +2 -2
- package/dist/lib/server.js +179 -4
- package/dist/lib/skills.js +3 -3
- package/dist/lib/tui/setup.js +11 -5
- package/package.json +1 -1
- package/src/auto-session.ts +10 -3
- package/src/cli.ts +1 -1
- package/src/config.ts +1 -1
- package/src/http.ts +1 -0
- package/src/prompt-builder.ts +3 -0
- package/src/remote.ts +2 -2
- package/src/server.ts +244 -4
- package/src/skills.ts +3 -3
- package/src/tui/setup.ts +11 -5
package/dist/cli.js
CHANGED
|
@@ -8735,7 +8735,7 @@ var {
|
|
|
8735
8735
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
8736
8736
|
import { homedir } from "node:os";
|
|
8737
8737
|
import { join } from "node:path";
|
|
8738
|
-
var DEFAULT_API_URL = "https://gethmy.com/api";
|
|
8738
|
+
var DEFAULT_API_URL = "https://app.gethmy.com/api";
|
|
8739
8739
|
var LOCAL_CONFIG_FILENAME = ".harmony-mcp.json";
|
|
8740
8740
|
function getConfigDir() {
|
|
8741
8741
|
return join(homedir(), ".harmony-mcp");
|
|
@@ -26672,18 +26672,22 @@ async function trackActivity(cardId, options) {
|
|
|
26672
26672
|
agentName: "Auto-detected Agent"
|
|
26673
26673
|
});
|
|
26674
26674
|
}
|
|
26675
|
-
function markExplicit(cardId) {
|
|
26675
|
+
function markExplicit(cardId, options) {
|
|
26676
26676
|
const existing = activeSessions.get(cardId);
|
|
26677
26677
|
if (existing) {
|
|
26678
26678
|
existing.isExplicit = true;
|
|
26679
|
+
if (options?.agentIdentifier)
|
|
26680
|
+
existing.agentIdentifier = options.agentIdentifier;
|
|
26681
|
+
if (options?.agentName)
|
|
26682
|
+
existing.agentName = options.agentName;
|
|
26679
26683
|
} else {
|
|
26680
26684
|
activeSessions.set(cardId, {
|
|
26681
26685
|
cardId,
|
|
26682
26686
|
startedAt: Date.now(),
|
|
26683
26687
|
lastActivityAt: Date.now(),
|
|
26684
26688
|
isExplicit: true,
|
|
26685
|
-
agentIdentifier: "explicit",
|
|
26686
|
-
agentName: "Explicit Agent"
|
|
26689
|
+
agentIdentifier: options?.agentIdentifier ?? "explicit",
|
|
26690
|
+
agentName: options?.agentName ?? "Explicit Agent"
|
|
26687
26691
|
});
|
|
26688
26692
|
}
|
|
26689
26693
|
}
|
|
@@ -27774,6 +27778,7 @@ ${card.description}`);
|
|
|
27774
27778
|
roleFraming.focus.forEach((f) => {
|
|
27775
27779
|
sections.push(`- ${f}`);
|
|
27776
27780
|
});
|
|
27781
|
+
sections.push(`- **Memory:** When you discover important domain knowledge, architectural decisions, or infrastructure details, store them via \`harmony_remember\`. Focus on durable knowledge that future agents would benefit from — not ephemeral task details (those are auto-extracted from your session).`);
|
|
27777
27782
|
sections.push(`
|
|
27778
27783
|
## Suggested Outputs`);
|
|
27779
27784
|
roleFraming.outputSuggestions.forEach((s) => {
|
|
@@ -27913,6 +27918,96 @@ function synthesizeOneThing(card, subtasks, links, assembledContext) {
|
|
|
27913
27918
|
}
|
|
27914
27919
|
|
|
27915
27920
|
// src/server.ts
|
|
27921
|
+
var memorySessions = new Map;
|
|
27922
|
+
function initMemorySession(cardId, agentIdentifier, agentName) {
|
|
27923
|
+
memorySessions.set(cardId, {
|
|
27924
|
+
cardId,
|
|
27925
|
+
agentIdentifier,
|
|
27926
|
+
agentName,
|
|
27927
|
+
memoryReadCount: 0,
|
|
27928
|
+
pendingActions: [],
|
|
27929
|
+
allActions: [],
|
|
27930
|
+
dirty: false
|
|
27931
|
+
});
|
|
27932
|
+
}
|
|
27933
|
+
function getMemorySession(cardId) {
|
|
27934
|
+
return memorySessions.get(cardId);
|
|
27935
|
+
}
|
|
27936
|
+
function appendMemoryAction(cardId, action) {
|
|
27937
|
+
const session = memorySessions.get(cardId);
|
|
27938
|
+
if (!session)
|
|
27939
|
+
return;
|
|
27940
|
+
const truncated = action.length > 512 ? action.slice(0, 509) + "..." : action;
|
|
27941
|
+
const entry = { action: truncated, ts: new Date().toISOString() };
|
|
27942
|
+
session.pendingActions.push(entry);
|
|
27943
|
+
session.dirty = true;
|
|
27944
|
+
}
|
|
27945
|
+
function incrementMemoryReads(cardId) {
|
|
27946
|
+
const session = memorySessions.get(cardId);
|
|
27947
|
+
if (!session)
|
|
27948
|
+
return;
|
|
27949
|
+
session.memoryReadCount++;
|
|
27950
|
+
session.dirty = true;
|
|
27951
|
+
}
|
|
27952
|
+
async function flushMemoryActions(client3, cardId) {
|
|
27953
|
+
const session = memorySessions.get(cardId);
|
|
27954
|
+
if (!session || !session.dirty)
|
|
27955
|
+
return;
|
|
27956
|
+
try {
|
|
27957
|
+
if (session.memoryReadCount > 0) {
|
|
27958
|
+
session.allActions.push({
|
|
27959
|
+
action: `Recalled ${session.memoryReadCount} memor${session.memoryReadCount === 1 ? "y" : "ies"}`,
|
|
27960
|
+
ts: new Date().toISOString()
|
|
27961
|
+
});
|
|
27962
|
+
session.memoryReadCount = 0;
|
|
27963
|
+
}
|
|
27964
|
+
if (session.pendingActions.length > 0) {
|
|
27965
|
+
session.allActions.push(...session.pendingActions);
|
|
27966
|
+
session.pendingActions = [];
|
|
27967
|
+
}
|
|
27968
|
+
if (session.allActions.length > 10) {
|
|
27969
|
+
session.allActions = session.allActions.slice(-10);
|
|
27970
|
+
}
|
|
27971
|
+
await client3.updateAgentProgress(cardId, {
|
|
27972
|
+
agentIdentifier: session.agentIdentifier,
|
|
27973
|
+
agentName: session.agentName,
|
|
27974
|
+
recentActions: session.allActions
|
|
27975
|
+
});
|
|
27976
|
+
session.dirty = false;
|
|
27977
|
+
} catch (err) {
|
|
27978
|
+
console.error("[memory-session] flush failed:", err);
|
|
27979
|
+
}
|
|
27980
|
+
}
|
|
27981
|
+
function mergeMemoryActionsInto(cardId, callerActions) {
|
|
27982
|
+
const session = memorySessions.get(cardId);
|
|
27983
|
+
if (!session)
|
|
27984
|
+
return callerActions;
|
|
27985
|
+
if (session.memoryReadCount > 0) {
|
|
27986
|
+
session.allActions.push({
|
|
27987
|
+
action: `Recalled ${session.memoryReadCount} memor${session.memoryReadCount === 1 ? "y" : "ies"}`,
|
|
27988
|
+
ts: new Date().toISOString()
|
|
27989
|
+
});
|
|
27990
|
+
session.memoryReadCount = 0;
|
|
27991
|
+
}
|
|
27992
|
+
if (session.pendingActions.length > 0) {
|
|
27993
|
+
session.allActions.push(...session.pendingActions);
|
|
27994
|
+
session.pendingActions = [];
|
|
27995
|
+
}
|
|
27996
|
+
const merged = [...callerActions, ...session.allActions];
|
|
27997
|
+
const trimmed = merged.length > 10 ? merged.slice(-10) : merged;
|
|
27998
|
+
session.allActions = trimmed;
|
|
27999
|
+
session.dirty = false;
|
|
28000
|
+
return trimmed;
|
|
28001
|
+
}
|
|
28002
|
+
function getActiveMemorySession() {
|
|
28003
|
+
for (const session of memorySessions.values()) {
|
|
28004
|
+
return session;
|
|
28005
|
+
}
|
|
28006
|
+
return;
|
|
28007
|
+
}
|
|
28008
|
+
function cleanupMemorySession(cardId) {
|
|
28009
|
+
memorySessions.delete(cardId);
|
|
28010
|
+
}
|
|
27916
28011
|
var TOOLS = {
|
|
27917
28012
|
harmony_create_card: {
|
|
27918
28013
|
description: "Create a new card in a Kanban column",
|
|
@@ -29426,7 +29521,7 @@ async function handleToolCall(name, args, deps) {
|
|
|
29426
29521
|
const unauthenticatedTools = ["harmony_signup", "harmony_onboard"];
|
|
29427
29522
|
if (!unauthenticatedTools.includes(name) && !deps.isConfigured()) {
|
|
29428
29523
|
throw new Error(`Not configured. Run "npx @gethmy/mcp setup" to set your API key.
|
|
29429
|
-
` + `You can generate an API key at https://gethmy.com → Settings → API Keys.
|
|
29524
|
+
` + `You can generate an API key at https://app.gethmy.com → Settings → API Keys.
|
|
29430
29525
|
` + 'Or use "harmony_onboard" to create an account and configure automatically.');
|
|
29431
29526
|
}
|
|
29432
29527
|
const client3 = deps.isConfigured() ? deps.getClient() : null;
|
|
@@ -29729,7 +29824,8 @@ async function handleToolCall(name, args, deps) {
|
|
|
29729
29824
|
currentTask: args.currentTask,
|
|
29730
29825
|
estimatedMinutesRemaining: args.estimatedMinutesRemaining
|
|
29731
29826
|
});
|
|
29732
|
-
markExplicit(cardId);
|
|
29827
|
+
markExplicit(cardId, { agentIdentifier, agentName });
|
|
29828
|
+
initMemorySession(cardId, agentIdentifier, agentName);
|
|
29733
29829
|
let prefetchedMemoryIds = [];
|
|
29734
29830
|
try {
|
|
29735
29831
|
const workspaceId = deps.getActiveWorkspaceId();
|
|
@@ -29776,6 +29872,14 @@ async function handleToolCall(name, args, deps) {
|
|
|
29776
29872
|
const agentIdentifier = exports_external.string().min(1).max(100).parse(args.agentIdentifier);
|
|
29777
29873
|
const agentName = exports_external.string().min(1).max(100).parse(args.agentName);
|
|
29778
29874
|
const progressPercent = args.progressPercent !== undefined ? exports_external.number().min(0).max(100).parse(args.progressPercent) : undefined;
|
|
29875
|
+
const callerRecentActions = args.recentActions;
|
|
29876
|
+
const memSession = getMemorySession(cardId);
|
|
29877
|
+
let mergedRecentActions;
|
|
29878
|
+
if (memSession?.dirty) {
|
|
29879
|
+
mergedRecentActions = mergeMemoryActionsInto(cardId, callerRecentActions || []);
|
|
29880
|
+
} else if (callerRecentActions) {
|
|
29881
|
+
mergedRecentActions = callerRecentActions;
|
|
29882
|
+
}
|
|
29779
29883
|
const result = await client3.updateAgentProgress(cardId, {
|
|
29780
29884
|
agentIdentifier,
|
|
29781
29885
|
agentName,
|
|
@@ -29783,7 +29887,8 @@ async function handleToolCall(name, args, deps) {
|
|
|
29783
29887
|
progressPercent,
|
|
29784
29888
|
currentTask: args.currentTask,
|
|
29785
29889
|
blockers: args.blockers,
|
|
29786
|
-
estimatedMinutesRemaining: args.estimatedMinutesRemaining
|
|
29890
|
+
estimatedMinutesRemaining: args.estimatedMinutesRemaining,
|
|
29891
|
+
...mergedRecentActions && { recentActions: mergedRecentActions }
|
|
29787
29892
|
});
|
|
29788
29893
|
let midSessionLearnings = 0;
|
|
29789
29894
|
try {
|
|
@@ -29808,6 +29913,8 @@ async function handleToolCall(name, args, deps) {
|
|
|
29808
29913
|
const moveToColumn = args.moveToColumn;
|
|
29809
29914
|
const sessionStatus = args.status || "completed";
|
|
29810
29915
|
const endProgressPercent = args.progressPercent !== undefined ? exports_external.number().min(0).max(100).parse(args.progressPercent) : undefined;
|
|
29916
|
+
await flushMemoryActions(client3, cardId);
|
|
29917
|
+
cleanupMemorySession(cardId);
|
|
29811
29918
|
const result = await client3.endAgentSession(cardId, {
|
|
29812
29919
|
status: sessionStatus,
|
|
29813
29920
|
progressPercent: endProgressPercent
|
|
@@ -29995,6 +30102,7 @@ async function handleToolCall(name, args, deps) {
|
|
|
29995
30102
|
}
|
|
29996
30103
|
const entityType = args.type || "context";
|
|
29997
30104
|
const entityTags = args.tags || [];
|
|
30105
|
+
const activeMemSession = getActiveMemorySession();
|
|
29998
30106
|
const result = await client3.createMemoryEntity({
|
|
29999
30107
|
workspace_id: workspaceId,
|
|
30000
30108
|
project_id: args.projectId || deps.getActiveProjectId() || undefined,
|
|
@@ -30006,7 +30114,7 @@ async function handleToolCall(name, args, deps) {
|
|
|
30006
30114
|
metadata: args.metadata,
|
|
30007
30115
|
confidence: args.confidence !== undefined ? exports_external.number().min(0).max(1).parse(args.confidence) : undefined,
|
|
30008
30116
|
tags: entityTags.length > 0 ? entityTags : undefined,
|
|
30009
|
-
agent_identifier:
|
|
30117
|
+
agent_identifier: activeMemSession?.agentIdentifier || undefined
|
|
30010
30118
|
});
|
|
30011
30119
|
const newEntityIdForGraph = result.entity?.id;
|
|
30012
30120
|
if (newEntityIdForGraph) {
|
|
@@ -30019,6 +30127,10 @@ async function handleToolCall(name, args, deps) {
|
|
|
30019
30127
|
potentialContradictions = await detectContradictions(client3, newEntityId, entityType, title, content, entityTags, workspaceId, args.projectId || deps.getActiveProjectId() || undefined);
|
|
30020
30128
|
} catch {}
|
|
30021
30129
|
}
|
|
30130
|
+
if (activeMemSession) {
|
|
30131
|
+
appendMemoryAction(activeMemSession.cardId, `Stored memory: ${title}`);
|
|
30132
|
+
flushMemoryActions(client3, activeMemSession.cardId).catch(() => {});
|
|
30133
|
+
}
|
|
30022
30134
|
return {
|
|
30023
30135
|
success: true,
|
|
30024
30136
|
...result,
|
|
@@ -30082,6 +30194,10 @@ async function handleToolCall(name, args, deps) {
|
|
|
30082
30194
|
} catch (_) {}
|
|
30083
30195
|
})).catch(() => {});
|
|
30084
30196
|
}
|
|
30197
|
+
const recallMemSession = getActiveMemorySession();
|
|
30198
|
+
if (recallMemSession) {
|
|
30199
|
+
incrementMemoryReads(recallMemSession.cardId);
|
|
30200
|
+
}
|
|
30085
30201
|
return markdown || "No memories found.";
|
|
30086
30202
|
}
|
|
30087
30203
|
case "harmony_update_memory": {
|
|
@@ -30102,11 +30218,29 @@ async function handleToolCall(name, args, deps) {
|
|
|
30102
30218
|
if (args.metadata !== undefined)
|
|
30103
30219
|
updates.metadata = args.metadata;
|
|
30104
30220
|
const result = await client3.updateMemoryEntity(entityId, updates);
|
|
30221
|
+
const updateMemSession = getActiveMemorySession();
|
|
30222
|
+
if (updateMemSession) {
|
|
30223
|
+
const updateTitle = updates.title || entityId.slice(0, 8);
|
|
30224
|
+
appendMemoryAction(updateMemSession.cardId, `Updated memory: ${updateTitle}`);
|
|
30225
|
+
flushMemoryActions(client3, updateMemSession.cardId).catch(() => {});
|
|
30226
|
+
}
|
|
30105
30227
|
return { success: true, ...result };
|
|
30106
30228
|
}
|
|
30107
30229
|
case "harmony_forget": {
|
|
30108
30230
|
const entityId = exports_external.string().uuid().parse(args.entityId);
|
|
30231
|
+
let forgetTitle = null;
|
|
30232
|
+
const forgetMemSession = getActiveMemorySession();
|
|
30233
|
+
if (forgetMemSession) {
|
|
30234
|
+
try {
|
|
30235
|
+
const { entity } = await client3.getMemoryEntity(entityId);
|
|
30236
|
+
forgetTitle = entity?.title || null;
|
|
30237
|
+
} catch {}
|
|
30238
|
+
}
|
|
30109
30239
|
await client3.deleteMemoryEntity(entityId);
|
|
30240
|
+
if (forgetMemSession) {
|
|
30241
|
+
appendMemoryAction(forgetMemSession.cardId, `Removed memory: ${forgetTitle || entityId.slice(0, 8)}`);
|
|
30242
|
+
flushMemoryActions(client3, forgetMemSession.cardId).catch(() => {});
|
|
30243
|
+
}
|
|
30110
30244
|
return { success: true };
|
|
30111
30245
|
}
|
|
30112
30246
|
case "harmony_relate": {
|
|
@@ -30128,6 +30262,11 @@ async function handleToolCall(name, args, deps) {
|
|
|
30128
30262
|
relation_type: relationType,
|
|
30129
30263
|
confidence: args.confidence !== undefined ? exports_external.number().min(0).max(1).parse(args.confidence) : undefined
|
|
30130
30264
|
});
|
|
30265
|
+
const relateMemSession = getActiveMemorySession();
|
|
30266
|
+
if (relateMemSession) {
|
|
30267
|
+
appendMemoryAction(relateMemSession.cardId, `Linked memories: ${relationType}`);
|
|
30268
|
+
flushMemoryActions(client3, relateMemSession.cardId).catch(() => {});
|
|
30269
|
+
}
|
|
30131
30270
|
return { success: true, ...result };
|
|
30132
30271
|
}
|
|
30133
30272
|
case "harmony_memory_search": {
|
|
@@ -30141,6 +30280,10 @@ async function handleToolCall(name, args, deps) {
|
|
|
30141
30280
|
type: args.type,
|
|
30142
30281
|
limit: args.limit
|
|
30143
30282
|
});
|
|
30283
|
+
const searchMemSession = getActiveMemorySession();
|
|
30284
|
+
if (searchMemSession) {
|
|
30285
|
+
incrementMemoryReads(searchMemSession.cardId);
|
|
30286
|
+
}
|
|
30144
30287
|
return markdown || "No memories found.";
|
|
30145
30288
|
}
|
|
30146
30289
|
case "harmony_vault_index": {
|
|
@@ -30235,7 +30378,7 @@ async function handleToolCall(name, args, deps) {
|
|
|
30235
30378
|
source: args.source || "agent",
|
|
30236
30379
|
tasks: args.tasks
|
|
30237
30380
|
});
|
|
30238
|
-
const planUrl = `https://gethmy.com/plans/${result.plan.id}`;
|
|
30381
|
+
const planUrl = `https://app.gethmy.com/plans/${result.plan.id}`;
|
|
30239
30382
|
return {
|
|
30240
30383
|
success: true,
|
|
30241
30384
|
planId: result.plan.id,
|
|
@@ -31000,7 +31143,7 @@ Once you have the plan ID, call \`harmony_get_plan\` to fetch the full plan with
|
|
|
31000
31143
|
## [Plan Title]
|
|
31001
31144
|
**Status:** draft/active/archived | **Phase:** plan/execute/verify/done
|
|
31002
31145
|
**Tasks:** N total (X pending, Y in_progress, Z completed)
|
|
31003
|
-
**URL:** https://gethmy.com/plans/{id}
|
|
31146
|
+
**URL:** https://app.gethmy.com/plans/{id}
|
|
31004
31147
|
\\\`\\\`\\\`
|
|
31005
31148
|
|
|
31006
31149
|
If plan is in execute phase and tasks already have linked cards, note which tasks have cards and which don't.
|
|
@@ -31034,7 +31177,7 @@ Only offer **(A) Single card**, **(C) Analyze only**, or **(D) Skip**.
|
|
|
31034
31177
|
#### Option A — Single card
|
|
31035
31178
|
1. Call \`harmony_create_card\` with:
|
|
31036
31179
|
- \`title\`: Plan title
|
|
31037
|
-
- \`description\`: Brief 2-3 sentence summary of the plan + \`\\n\\n[View plan](https://gethmy.com/plans/{planId})\`
|
|
31180
|
+
- \`description\`: Brief 2-3 sentence summary of the plan + \`\\n\\n[View plan](https://app.gethmy.com/plans/{planId})\`
|
|
31038
31181
|
- \`priority\`: based on plan task priorities (use highest)
|
|
31039
31182
|
2. Call \`harmony_update_plan\` to set \`status: "active"\`, \`workflowPhase: "execute"\`
|
|
31040
31183
|
|
|
@@ -31156,7 +31299,7 @@ Call \`harmony_create_plan\` with:
|
|
|
31156
31299
|
### 2B.5 — User Approval
|
|
31157
31300
|
|
|
31158
31301
|
Show the user:
|
|
31159
|
-
- Plan URL: \`https://gethmy.com/plans/{id}\`
|
|
31302
|
+
- Plan URL: \`https://app.gethmy.com/plans/{id}\`
|
|
31160
31303
|
- Number of tasks created
|
|
31161
31304
|
- Brief summary
|
|
31162
31305
|
|
|
@@ -32936,7 +33079,7 @@ function getWriteSummary(files, options = {}) {
|
|
|
32936
33079
|
|
|
32937
33080
|
// src/tui/setup.ts
|
|
32938
33081
|
var GLOBAL_SKILLS_DIR = join5(homedir4(), ".agents", "skills");
|
|
32939
|
-
var API_URL = "https://gethmy.com/api";
|
|
33082
|
+
var API_URL = "https://app.gethmy.com/api";
|
|
32940
33083
|
async function registerMcpServer() {
|
|
32941
33084
|
try {
|
|
32942
33085
|
const { execSync } = await import("node:child_process");
|
|
@@ -33286,7 +33429,7 @@ async function runSetup(options = {}) {
|
|
|
33286
33429
|
if (!validation.valid) {
|
|
33287
33430
|
spinner.stop(colors.error("API key validation failed"));
|
|
33288
33431
|
M2.error(validation.error || "Could not connect to Harmony API");
|
|
33289
|
-
M2.info("Get an API key at: https://gethmy.com/user/keys");
|
|
33432
|
+
M2.info("Get an API key at: https://app.gethmy.com/user/keys");
|
|
33290
33433
|
process.exit(1);
|
|
33291
33434
|
}
|
|
33292
33435
|
if (!userEmail) {
|
|
@@ -33467,7 +33610,9 @@ async function runSetup(options = {}) {
|
|
|
33467
33610
|
console.log(` ${colors.bold("Project:")} ${selectedProjectName || selectedProjectId}`);
|
|
33468
33611
|
}
|
|
33469
33612
|
if (allFiles.length > 0) {
|
|
33470
|
-
const summary = getWriteSummary(allFiles, {
|
|
33613
|
+
const summary = getWriteSummary(allFiles, {
|
|
33614
|
+
force: options.force || needsSkills
|
|
33615
|
+
});
|
|
33471
33616
|
if (summary.toCreate.length > 0) {
|
|
33472
33617
|
console.log("");
|
|
33473
33618
|
console.log(` ${colors.success("Files to create:")}`);
|
|
@@ -33508,7 +33653,9 @@ async function runSetup(options = {}) {
|
|
|
33508
33653
|
}
|
|
33509
33654
|
console.log("");
|
|
33510
33655
|
if (allFiles.length > 0) {
|
|
33511
|
-
await writeFilesWithProgress(allFiles, {
|
|
33656
|
+
await writeFilesWithProgress(allFiles, {
|
|
33657
|
+
force: options.force || needsSkills
|
|
33658
|
+
});
|
|
33512
33659
|
}
|
|
33513
33660
|
if (allSymlinks.length > 0) {
|
|
33514
33661
|
for (const symlink of allSymlinks) {
|
|
@@ -33582,7 +33729,7 @@ async function runSetup(options = {}) {
|
|
|
33582
33729
|
}
|
|
33583
33730
|
console.log("");
|
|
33584
33731
|
console.log(` ${colors.dim("Add to new project: npx @gethmy/mcp setup")}`);
|
|
33585
|
-
console.log(` ${colors.dim("Need help? Visit https://gethmy.com/docs/mcp")}`);
|
|
33732
|
+
console.log(` ${colors.dim("Need help? Visit https://app.gethmy.com/docs/mcp")}`);
|
|
33586
33733
|
console.log("");
|
|
33587
33734
|
}
|
|
33588
33735
|
|
|
@@ -33646,7 +33793,7 @@ Context:`);
|
|
|
33646
33793
|
console.log(`Status: Not configured
|
|
33647
33794
|
`);
|
|
33648
33795
|
console.log("Run: npx @gethmy/mcp setup");
|
|
33649
|
-
console.log("Get an API key at: https://gethmy.com/user/keys");
|
|
33796
|
+
console.log("Get an API key at: https://app.gethmy.com/user/keys");
|
|
33650
33797
|
}
|
|
33651
33798
|
});
|
|
33652
33799
|
program.command("reset").description("Remove stored configuration").action(() => {
|
package/dist/index.js
CHANGED
|
@@ -22849,7 +22849,7 @@ class StdioServerTransport {
|
|
|
22849
22849
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
22850
22850
|
import { homedir } from "node:os";
|
|
22851
22851
|
import { join as join2 } from "node:path";
|
|
22852
|
-
var DEFAULT_API_URL = "https://gethmy.com/api";
|
|
22852
|
+
var DEFAULT_API_URL = "https://app.gethmy.com/api";
|
|
22853
22853
|
var LOCAL_CONFIG_FILENAME = ".harmony-mcp.json";
|
|
22854
22854
|
function getConfigDir() {
|
|
22855
22855
|
return join2(homedir(), ".harmony-mcp");
|
|
@@ -24432,18 +24432,22 @@ async function trackActivity(cardId, options) {
|
|
|
24432
24432
|
agentName: "Auto-detected Agent"
|
|
24433
24433
|
});
|
|
24434
24434
|
}
|
|
24435
|
-
function markExplicit(cardId) {
|
|
24435
|
+
function markExplicit(cardId, options) {
|
|
24436
24436
|
const existing = activeSessions.get(cardId);
|
|
24437
24437
|
if (existing) {
|
|
24438
24438
|
existing.isExplicit = true;
|
|
24439
|
+
if (options?.agentIdentifier)
|
|
24440
|
+
existing.agentIdentifier = options.agentIdentifier;
|
|
24441
|
+
if (options?.agentName)
|
|
24442
|
+
existing.agentName = options.agentName;
|
|
24439
24443
|
} else {
|
|
24440
24444
|
activeSessions.set(cardId, {
|
|
24441
24445
|
cardId,
|
|
24442
24446
|
startedAt: Date.now(),
|
|
24443
24447
|
lastActivityAt: Date.now(),
|
|
24444
24448
|
isExplicit: true,
|
|
24445
|
-
agentIdentifier: "explicit",
|
|
24446
|
-
agentName: "Explicit Agent"
|
|
24449
|
+
agentIdentifier: options?.agentIdentifier ?? "explicit",
|
|
24450
|
+
agentName: options?.agentName ?? "Explicit Agent"
|
|
24447
24451
|
});
|
|
24448
24452
|
}
|
|
24449
24453
|
}
|
|
@@ -25534,6 +25538,7 @@ ${card.description}`);
|
|
|
25534
25538
|
roleFraming.focus.forEach((f) => {
|
|
25535
25539
|
sections.push(`- ${f}`);
|
|
25536
25540
|
});
|
|
25541
|
+
sections.push(`- **Memory:** When you discover important domain knowledge, architectural decisions, or infrastructure details, store them via \`harmony_remember\`. Focus on durable knowledge that future agents would benefit from — not ephemeral task details (those are auto-extracted from your session).`);
|
|
25537
25542
|
sections.push(`
|
|
25538
25543
|
## Suggested Outputs`);
|
|
25539
25544
|
roleFraming.outputSuggestions.forEach((s) => {
|
|
@@ -25673,6 +25678,96 @@ function synthesizeOneThing(card, subtasks, links, assembledContext) {
|
|
|
25673
25678
|
}
|
|
25674
25679
|
|
|
25675
25680
|
// src/server.ts
|
|
25681
|
+
var memorySessions = new Map;
|
|
25682
|
+
function initMemorySession(cardId, agentIdentifier, agentName) {
|
|
25683
|
+
memorySessions.set(cardId, {
|
|
25684
|
+
cardId,
|
|
25685
|
+
agentIdentifier,
|
|
25686
|
+
agentName,
|
|
25687
|
+
memoryReadCount: 0,
|
|
25688
|
+
pendingActions: [],
|
|
25689
|
+
allActions: [],
|
|
25690
|
+
dirty: false
|
|
25691
|
+
});
|
|
25692
|
+
}
|
|
25693
|
+
function getMemorySession(cardId) {
|
|
25694
|
+
return memorySessions.get(cardId);
|
|
25695
|
+
}
|
|
25696
|
+
function appendMemoryAction(cardId, action) {
|
|
25697
|
+
const session = memorySessions.get(cardId);
|
|
25698
|
+
if (!session)
|
|
25699
|
+
return;
|
|
25700
|
+
const truncated = action.length > 512 ? action.slice(0, 509) + "..." : action;
|
|
25701
|
+
const entry = { action: truncated, ts: new Date().toISOString() };
|
|
25702
|
+
session.pendingActions.push(entry);
|
|
25703
|
+
session.dirty = true;
|
|
25704
|
+
}
|
|
25705
|
+
function incrementMemoryReads(cardId) {
|
|
25706
|
+
const session = memorySessions.get(cardId);
|
|
25707
|
+
if (!session)
|
|
25708
|
+
return;
|
|
25709
|
+
session.memoryReadCount++;
|
|
25710
|
+
session.dirty = true;
|
|
25711
|
+
}
|
|
25712
|
+
async function flushMemoryActions(client3, cardId) {
|
|
25713
|
+
const session = memorySessions.get(cardId);
|
|
25714
|
+
if (!session || !session.dirty)
|
|
25715
|
+
return;
|
|
25716
|
+
try {
|
|
25717
|
+
if (session.memoryReadCount > 0) {
|
|
25718
|
+
session.allActions.push({
|
|
25719
|
+
action: `Recalled ${session.memoryReadCount} memor${session.memoryReadCount === 1 ? "y" : "ies"}`,
|
|
25720
|
+
ts: new Date().toISOString()
|
|
25721
|
+
});
|
|
25722
|
+
session.memoryReadCount = 0;
|
|
25723
|
+
}
|
|
25724
|
+
if (session.pendingActions.length > 0) {
|
|
25725
|
+
session.allActions.push(...session.pendingActions);
|
|
25726
|
+
session.pendingActions = [];
|
|
25727
|
+
}
|
|
25728
|
+
if (session.allActions.length > 10) {
|
|
25729
|
+
session.allActions = session.allActions.slice(-10);
|
|
25730
|
+
}
|
|
25731
|
+
await client3.updateAgentProgress(cardId, {
|
|
25732
|
+
agentIdentifier: session.agentIdentifier,
|
|
25733
|
+
agentName: session.agentName,
|
|
25734
|
+
recentActions: session.allActions
|
|
25735
|
+
});
|
|
25736
|
+
session.dirty = false;
|
|
25737
|
+
} catch (err) {
|
|
25738
|
+
console.error("[memory-session] flush failed:", err);
|
|
25739
|
+
}
|
|
25740
|
+
}
|
|
25741
|
+
function mergeMemoryActionsInto(cardId, callerActions) {
|
|
25742
|
+
const session = memorySessions.get(cardId);
|
|
25743
|
+
if (!session)
|
|
25744
|
+
return callerActions;
|
|
25745
|
+
if (session.memoryReadCount > 0) {
|
|
25746
|
+
session.allActions.push({
|
|
25747
|
+
action: `Recalled ${session.memoryReadCount} memor${session.memoryReadCount === 1 ? "y" : "ies"}`,
|
|
25748
|
+
ts: new Date().toISOString()
|
|
25749
|
+
});
|
|
25750
|
+
session.memoryReadCount = 0;
|
|
25751
|
+
}
|
|
25752
|
+
if (session.pendingActions.length > 0) {
|
|
25753
|
+
session.allActions.push(...session.pendingActions);
|
|
25754
|
+
session.pendingActions = [];
|
|
25755
|
+
}
|
|
25756
|
+
const merged = [...callerActions, ...session.allActions];
|
|
25757
|
+
const trimmed = merged.length > 10 ? merged.slice(-10) : merged;
|
|
25758
|
+
session.allActions = trimmed;
|
|
25759
|
+
session.dirty = false;
|
|
25760
|
+
return trimmed;
|
|
25761
|
+
}
|
|
25762
|
+
function getActiveMemorySession() {
|
|
25763
|
+
for (const session of memorySessions.values()) {
|
|
25764
|
+
return session;
|
|
25765
|
+
}
|
|
25766
|
+
return;
|
|
25767
|
+
}
|
|
25768
|
+
function cleanupMemorySession(cardId) {
|
|
25769
|
+
memorySessions.delete(cardId);
|
|
25770
|
+
}
|
|
25676
25771
|
var TOOLS = {
|
|
25677
25772
|
harmony_create_card: {
|
|
25678
25773
|
description: "Create a new card in a Kanban column",
|
|
@@ -27186,7 +27281,7 @@ async function handleToolCall(name, args, deps) {
|
|
|
27186
27281
|
const unauthenticatedTools = ["harmony_signup", "harmony_onboard"];
|
|
27187
27282
|
if (!unauthenticatedTools.includes(name) && !deps.isConfigured()) {
|
|
27188
27283
|
throw new Error(`Not configured. Run "npx @gethmy/mcp setup" to set your API key.
|
|
27189
|
-
` + `You can generate an API key at https://gethmy.com → Settings → API Keys.
|
|
27284
|
+
` + `You can generate an API key at https://app.gethmy.com → Settings → API Keys.
|
|
27190
27285
|
` + 'Or use "harmony_onboard" to create an account and configure automatically.');
|
|
27191
27286
|
}
|
|
27192
27287
|
const client3 = deps.isConfigured() ? deps.getClient() : null;
|
|
@@ -27489,7 +27584,8 @@ async function handleToolCall(name, args, deps) {
|
|
|
27489
27584
|
currentTask: args.currentTask,
|
|
27490
27585
|
estimatedMinutesRemaining: args.estimatedMinutesRemaining
|
|
27491
27586
|
});
|
|
27492
|
-
markExplicit(cardId);
|
|
27587
|
+
markExplicit(cardId, { agentIdentifier, agentName });
|
|
27588
|
+
initMemorySession(cardId, agentIdentifier, agentName);
|
|
27493
27589
|
let prefetchedMemoryIds = [];
|
|
27494
27590
|
try {
|
|
27495
27591
|
const workspaceId = deps.getActiveWorkspaceId();
|
|
@@ -27536,6 +27632,14 @@ async function handleToolCall(name, args, deps) {
|
|
|
27536
27632
|
const agentIdentifier = exports_external.string().min(1).max(100).parse(args.agentIdentifier);
|
|
27537
27633
|
const agentName = exports_external.string().min(1).max(100).parse(args.agentName);
|
|
27538
27634
|
const progressPercent = args.progressPercent !== undefined ? exports_external.number().min(0).max(100).parse(args.progressPercent) : undefined;
|
|
27635
|
+
const callerRecentActions = args.recentActions;
|
|
27636
|
+
const memSession = getMemorySession(cardId);
|
|
27637
|
+
let mergedRecentActions;
|
|
27638
|
+
if (memSession?.dirty) {
|
|
27639
|
+
mergedRecentActions = mergeMemoryActionsInto(cardId, callerRecentActions || []);
|
|
27640
|
+
} else if (callerRecentActions) {
|
|
27641
|
+
mergedRecentActions = callerRecentActions;
|
|
27642
|
+
}
|
|
27539
27643
|
const result = await client3.updateAgentProgress(cardId, {
|
|
27540
27644
|
agentIdentifier,
|
|
27541
27645
|
agentName,
|
|
@@ -27543,7 +27647,8 @@ async function handleToolCall(name, args, deps) {
|
|
|
27543
27647
|
progressPercent,
|
|
27544
27648
|
currentTask: args.currentTask,
|
|
27545
27649
|
blockers: args.blockers,
|
|
27546
|
-
estimatedMinutesRemaining: args.estimatedMinutesRemaining
|
|
27650
|
+
estimatedMinutesRemaining: args.estimatedMinutesRemaining,
|
|
27651
|
+
...mergedRecentActions && { recentActions: mergedRecentActions }
|
|
27547
27652
|
});
|
|
27548
27653
|
let midSessionLearnings = 0;
|
|
27549
27654
|
try {
|
|
@@ -27568,6 +27673,8 @@ async function handleToolCall(name, args, deps) {
|
|
|
27568
27673
|
const moveToColumn = args.moveToColumn;
|
|
27569
27674
|
const sessionStatus = args.status || "completed";
|
|
27570
27675
|
const endProgressPercent = args.progressPercent !== undefined ? exports_external.number().min(0).max(100).parse(args.progressPercent) : undefined;
|
|
27676
|
+
await flushMemoryActions(client3, cardId);
|
|
27677
|
+
cleanupMemorySession(cardId);
|
|
27571
27678
|
const result = await client3.endAgentSession(cardId, {
|
|
27572
27679
|
status: sessionStatus,
|
|
27573
27680
|
progressPercent: endProgressPercent
|
|
@@ -27755,6 +27862,7 @@ async function handleToolCall(name, args, deps) {
|
|
|
27755
27862
|
}
|
|
27756
27863
|
const entityType = args.type || "context";
|
|
27757
27864
|
const entityTags = args.tags || [];
|
|
27865
|
+
const activeMemSession = getActiveMemorySession();
|
|
27758
27866
|
const result = await client3.createMemoryEntity({
|
|
27759
27867
|
workspace_id: workspaceId,
|
|
27760
27868
|
project_id: args.projectId || deps.getActiveProjectId() || undefined,
|
|
@@ -27766,7 +27874,7 @@ async function handleToolCall(name, args, deps) {
|
|
|
27766
27874
|
metadata: args.metadata,
|
|
27767
27875
|
confidence: args.confidence !== undefined ? exports_external.number().min(0).max(1).parse(args.confidence) : undefined,
|
|
27768
27876
|
tags: entityTags.length > 0 ? entityTags : undefined,
|
|
27769
|
-
agent_identifier:
|
|
27877
|
+
agent_identifier: activeMemSession?.agentIdentifier || undefined
|
|
27770
27878
|
});
|
|
27771
27879
|
const newEntityIdForGraph = result.entity?.id;
|
|
27772
27880
|
if (newEntityIdForGraph) {
|
|
@@ -27779,6 +27887,10 @@ async function handleToolCall(name, args, deps) {
|
|
|
27779
27887
|
potentialContradictions = await detectContradictions(client3, newEntityId, entityType, title, content, entityTags, workspaceId, args.projectId || deps.getActiveProjectId() || undefined);
|
|
27780
27888
|
} catch {}
|
|
27781
27889
|
}
|
|
27890
|
+
if (activeMemSession) {
|
|
27891
|
+
appendMemoryAction(activeMemSession.cardId, `Stored memory: ${title}`);
|
|
27892
|
+
flushMemoryActions(client3, activeMemSession.cardId).catch(() => {});
|
|
27893
|
+
}
|
|
27782
27894
|
return {
|
|
27783
27895
|
success: true,
|
|
27784
27896
|
...result,
|
|
@@ -27842,6 +27954,10 @@ async function handleToolCall(name, args, deps) {
|
|
|
27842
27954
|
} catch (_) {}
|
|
27843
27955
|
})).catch(() => {});
|
|
27844
27956
|
}
|
|
27957
|
+
const recallMemSession = getActiveMemorySession();
|
|
27958
|
+
if (recallMemSession) {
|
|
27959
|
+
incrementMemoryReads(recallMemSession.cardId);
|
|
27960
|
+
}
|
|
27845
27961
|
return markdown || "No memories found.";
|
|
27846
27962
|
}
|
|
27847
27963
|
case "harmony_update_memory": {
|
|
@@ -27862,11 +27978,29 @@ async function handleToolCall(name, args, deps) {
|
|
|
27862
27978
|
if (args.metadata !== undefined)
|
|
27863
27979
|
updates.metadata = args.metadata;
|
|
27864
27980
|
const result = await client3.updateMemoryEntity(entityId, updates);
|
|
27981
|
+
const updateMemSession = getActiveMemorySession();
|
|
27982
|
+
if (updateMemSession) {
|
|
27983
|
+
const updateTitle = updates.title || entityId.slice(0, 8);
|
|
27984
|
+
appendMemoryAction(updateMemSession.cardId, `Updated memory: ${updateTitle}`);
|
|
27985
|
+
flushMemoryActions(client3, updateMemSession.cardId).catch(() => {});
|
|
27986
|
+
}
|
|
27865
27987
|
return { success: true, ...result };
|
|
27866
27988
|
}
|
|
27867
27989
|
case "harmony_forget": {
|
|
27868
27990
|
const entityId = exports_external.string().uuid().parse(args.entityId);
|
|
27991
|
+
let forgetTitle = null;
|
|
27992
|
+
const forgetMemSession = getActiveMemorySession();
|
|
27993
|
+
if (forgetMemSession) {
|
|
27994
|
+
try {
|
|
27995
|
+
const { entity } = await client3.getMemoryEntity(entityId);
|
|
27996
|
+
forgetTitle = entity?.title || null;
|
|
27997
|
+
} catch {}
|
|
27998
|
+
}
|
|
27869
27999
|
await client3.deleteMemoryEntity(entityId);
|
|
28000
|
+
if (forgetMemSession) {
|
|
28001
|
+
appendMemoryAction(forgetMemSession.cardId, `Removed memory: ${forgetTitle || entityId.slice(0, 8)}`);
|
|
28002
|
+
flushMemoryActions(client3, forgetMemSession.cardId).catch(() => {});
|
|
28003
|
+
}
|
|
27870
28004
|
return { success: true };
|
|
27871
28005
|
}
|
|
27872
28006
|
case "harmony_relate": {
|
|
@@ -27888,6 +28022,11 @@ async function handleToolCall(name, args, deps) {
|
|
|
27888
28022
|
relation_type: relationType,
|
|
27889
28023
|
confidence: args.confidence !== undefined ? exports_external.number().min(0).max(1).parse(args.confidence) : undefined
|
|
27890
28024
|
});
|
|
28025
|
+
const relateMemSession = getActiveMemorySession();
|
|
28026
|
+
if (relateMemSession) {
|
|
28027
|
+
appendMemoryAction(relateMemSession.cardId, `Linked memories: ${relationType}`);
|
|
28028
|
+
flushMemoryActions(client3, relateMemSession.cardId).catch(() => {});
|
|
28029
|
+
}
|
|
27891
28030
|
return { success: true, ...result };
|
|
27892
28031
|
}
|
|
27893
28032
|
case "harmony_memory_search": {
|
|
@@ -27901,6 +28040,10 @@ async function handleToolCall(name, args, deps) {
|
|
|
27901
28040
|
type: args.type,
|
|
27902
28041
|
limit: args.limit
|
|
27903
28042
|
});
|
|
28043
|
+
const searchMemSession = getActiveMemorySession();
|
|
28044
|
+
if (searchMemSession) {
|
|
28045
|
+
incrementMemoryReads(searchMemSession.cardId);
|
|
28046
|
+
}
|
|
27904
28047
|
return markdown || "No memories found.";
|
|
27905
28048
|
}
|
|
27906
28049
|
case "harmony_vault_index": {
|
|
@@ -27995,7 +28138,7 @@ async function handleToolCall(name, args, deps) {
|
|
|
27995
28138
|
source: args.source || "agent",
|
|
27996
28139
|
tasks: args.tasks
|
|
27997
28140
|
});
|
|
27998
|
-
const planUrl = `https://gethmy.com/plans/${result.plan.id}`;
|
|
28141
|
+
const planUrl = `https://app.gethmy.com/plans/${result.plan.id}`;
|
|
27999
28142
|
return {
|
|
28000
28143
|
success: true,
|
|
28001
28144
|
planId: result.plan.id,
|
package/dist/lib/auto-session.js
CHANGED
|
@@ -82,11 +82,16 @@ export async function trackActivity(cardId, options) {
|
|
|
82
82
|
}
|
|
83
83
|
/**
|
|
84
84
|
* Mark a session as explicitly started (won't be auto-ended by card switching or inactivity).
|
|
85
|
+
* Optionally accepts the real agent identifier/name to store in the session.
|
|
85
86
|
*/
|
|
86
|
-
export function markExplicit(cardId) {
|
|
87
|
+
export function markExplicit(cardId, options) {
|
|
87
88
|
const existing = activeSessions.get(cardId);
|
|
88
89
|
if (existing) {
|
|
89
90
|
existing.isExplicit = true;
|
|
91
|
+
if (options?.agentIdentifier)
|
|
92
|
+
existing.agentIdentifier = options.agentIdentifier;
|
|
93
|
+
if (options?.agentName)
|
|
94
|
+
existing.agentName = options.agentName;
|
|
90
95
|
}
|
|
91
96
|
else {
|
|
92
97
|
// Track the explicit session even if we didn't auto-start it
|
|
@@ -95,8 +100,8 @@ export function markExplicit(cardId) {
|
|
|
95
100
|
startedAt: Date.now(),
|
|
96
101
|
lastActivityAt: Date.now(),
|
|
97
102
|
isExplicit: true,
|
|
98
|
-
agentIdentifier: "explicit",
|
|
99
|
-
agentName: "Explicit Agent",
|
|
103
|
+
agentIdentifier: options?.agentIdentifier ?? "explicit",
|
|
104
|
+
agentName: options?.agentName ?? "Explicit Agent",
|
|
100
105
|
});
|
|
101
106
|
}
|
|
102
107
|
}
|