@gethmy/mcp 2.3.1 → 2.3.2
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/lib/active-learning.js +939 -787
- package/dist/lib/api-client.js +2527 -644
- package/dist/lib/auto-session.js +177 -196
- package/dist/lib/cli.js +34954 -128
- package/dist/lib/config.js +235 -201
- package/dist/lib/consolidation.js +374 -289
- package/dist/lib/context-assembly.js +1265 -838
- package/dist/lib/graph-expansion.js +139 -155
- package/dist/lib/http.js +1917 -130
- package/dist/lib/index.js +29525 -5
- package/dist/lib/lifecycle-maintenance.js +663 -79
- package/dist/lib/memory-cleanup.js +1315 -409
- package/dist/lib/onboard.js +2588 -32
- package/dist/lib/prompt-builder.js +438 -445
- package/dist/lib/remote.js +31733 -143
- package/dist/lib/server.js +29388 -3229
- package/dist/lib/skills.js +315 -132
- package/dist/lib/tui/agents.js +128 -107
- package/dist/lib/tui/docs.js +1590 -687
- package/dist/lib/tui/setup.js +5698 -804
- package/dist/lib/tui/theme.js +183 -86
- package/dist/lib/tui/writer.js +1149 -176
- package/package.json +2 -2
- package/src/memory-cleanup.ts +2 -4
- package/dist/lib/__tests__/active-learning.test.js +0 -386
- package/dist/lib/__tests__/agent-performance-profiles.test.js +0 -325
- package/dist/lib/__tests__/auto-session.test.js +0 -661
- package/dist/lib/__tests__/context-assembly.test.js +0 -362
- package/dist/lib/__tests__/graph-expansion.test.js +0 -150
- package/dist/lib/__tests__/integration-memory-crud.test.js +0 -797
- package/dist/lib/__tests__/integration-memory-system.test.js +0 -281
- package/dist/lib/__tests__/lifecycle-maintenance.test.js +0 -207
- package/dist/lib/__tests__/pattern-detection.test.js +0 -295
- package/dist/lib/__tests__/prompt-builder.test.js +0 -418
package/dist/lib/skills.js
CHANGED
|
@@ -1,13 +1,227 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
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
|
+
var __export = (target, all) => {
|
|
20
|
+
for (var name in all)
|
|
21
|
+
__defProp(target, name, {
|
|
22
|
+
get: all[name],
|
|
23
|
+
enumerable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
set: (newValue) => all[name] = () => newValue
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
29
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
30
|
+
|
|
31
|
+
// src/config.ts
|
|
1
32
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
33
|
+
import { homedir } from "node:os";
|
|
34
|
+
import { join } from "node:path";
|
|
35
|
+
var DEFAULT_API_URL = "https://app.gethmy.com/api";
|
|
36
|
+
var LOCAL_CONFIG_FILENAME = ".harmony-mcp.json";
|
|
37
|
+
function getConfigDir() {
|
|
38
|
+
return join(homedir(), ".harmony-mcp");
|
|
39
|
+
}
|
|
40
|
+
function getConfigPath() {
|
|
41
|
+
return join(getConfigDir(), "config.json");
|
|
42
|
+
}
|
|
43
|
+
function getLocalConfigPath(cwd) {
|
|
44
|
+
return join(cwd || process.cwd(), LOCAL_CONFIG_FILENAME);
|
|
45
|
+
}
|
|
46
|
+
function loadConfig() {
|
|
47
|
+
const configPath = getConfigPath();
|
|
48
|
+
if (!existsSync(configPath)) {
|
|
49
|
+
return {
|
|
50
|
+
apiKey: null,
|
|
51
|
+
apiUrl: DEFAULT_API_URL,
|
|
52
|
+
activeWorkspaceId: null,
|
|
53
|
+
activeProjectId: null,
|
|
54
|
+
userEmail: null,
|
|
55
|
+
memoryDir: null
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
const data = readFileSync(configPath, "utf-8");
|
|
60
|
+
const config = JSON.parse(data);
|
|
61
|
+
return {
|
|
62
|
+
apiKey: config.apiKey || null,
|
|
63
|
+
apiUrl: config.apiUrl || DEFAULT_API_URL,
|
|
64
|
+
activeWorkspaceId: config.activeWorkspaceId || null,
|
|
65
|
+
activeProjectId: config.activeProjectId || null,
|
|
66
|
+
userEmail: config.userEmail || null,
|
|
67
|
+
memoryDir: config.memoryDir || null
|
|
68
|
+
};
|
|
69
|
+
} catch {
|
|
70
|
+
return {
|
|
71
|
+
apiKey: null,
|
|
72
|
+
apiUrl: DEFAULT_API_URL,
|
|
73
|
+
activeWorkspaceId: null,
|
|
74
|
+
activeProjectId: null,
|
|
75
|
+
userEmail: null,
|
|
76
|
+
memoryDir: null
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function saveConfig(config) {
|
|
81
|
+
const configDir = getConfigDir();
|
|
82
|
+
const configPath = getConfigPath();
|
|
83
|
+
if (!existsSync(configDir)) {
|
|
84
|
+
mkdirSync(configDir, { recursive: true, mode: 448 });
|
|
85
|
+
}
|
|
86
|
+
const existingConfig = loadConfig();
|
|
87
|
+
const newConfig = { ...existingConfig, ...config };
|
|
88
|
+
writeFileSync(configPath, JSON.stringify(newConfig, null, 2), {
|
|
89
|
+
mode: 384
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
function loadLocalConfig(cwd) {
|
|
93
|
+
const localConfigPath = getLocalConfigPath(cwd);
|
|
94
|
+
if (!existsSync(localConfigPath)) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
const data = readFileSync(localConfigPath, "utf-8");
|
|
99
|
+
const config = JSON.parse(data);
|
|
100
|
+
return {
|
|
101
|
+
workspaceId: config.workspaceId || null,
|
|
102
|
+
projectId: config.projectId || null
|
|
103
|
+
};
|
|
104
|
+
} catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function saveLocalConfig(config, cwd) {
|
|
109
|
+
const localConfigPath = getLocalConfigPath(cwd);
|
|
110
|
+
const existingConfig = loadLocalConfig(cwd) || {
|
|
111
|
+
workspaceId: null,
|
|
112
|
+
projectId: null
|
|
113
|
+
};
|
|
114
|
+
const newConfig = { ...existingConfig, ...config };
|
|
115
|
+
const cleanConfig = {};
|
|
116
|
+
if (newConfig.workspaceId)
|
|
117
|
+
cleanConfig.workspaceId = newConfig.workspaceId;
|
|
118
|
+
if (newConfig.projectId)
|
|
119
|
+
cleanConfig.projectId = newConfig.projectId;
|
|
120
|
+
writeFileSync(localConfigPath, JSON.stringify(cleanConfig, null, 2));
|
|
121
|
+
}
|
|
122
|
+
function hasLocalConfig(cwd) {
|
|
123
|
+
return existsSync(getLocalConfigPath(cwd));
|
|
124
|
+
}
|
|
125
|
+
function getApiKey() {
|
|
126
|
+
const config = loadConfig();
|
|
127
|
+
if (!config.apiKey) {
|
|
128
|
+
throw new Error(`Not configured. Run "npx @gethmy/mcp setup" to set your API key.
|
|
129
|
+
` + "You can generate an API key at https://gethmy.com → Settings → API Keys.");
|
|
130
|
+
}
|
|
131
|
+
return config.apiKey;
|
|
132
|
+
}
|
|
133
|
+
function getApiUrl() {
|
|
134
|
+
const config = loadConfig();
|
|
135
|
+
return config.apiUrl;
|
|
136
|
+
}
|
|
137
|
+
function getUserEmail() {
|
|
138
|
+
const config = loadConfig();
|
|
139
|
+
return config.userEmail;
|
|
140
|
+
}
|
|
141
|
+
function setUserEmail(email) {
|
|
142
|
+
saveConfig({ userEmail: email });
|
|
143
|
+
}
|
|
144
|
+
function setActiveWorkspace(workspaceId, options) {
|
|
145
|
+
if (options?.local) {
|
|
146
|
+
saveLocalConfig({ workspaceId }, options.cwd);
|
|
147
|
+
} else {
|
|
148
|
+
saveConfig({ activeWorkspaceId: workspaceId });
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function setActiveProject(projectId, options) {
|
|
152
|
+
if (options?.local) {
|
|
153
|
+
saveLocalConfig({ projectId }, options.cwd);
|
|
154
|
+
} else {
|
|
155
|
+
saveConfig({ activeProjectId: projectId });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function getActiveWorkspaceId(cwd) {
|
|
159
|
+
const localConfig = loadLocalConfig(cwd);
|
|
160
|
+
if (localConfig?.workspaceId) {
|
|
161
|
+
return localConfig.workspaceId;
|
|
162
|
+
}
|
|
163
|
+
return loadConfig().activeWorkspaceId;
|
|
164
|
+
}
|
|
165
|
+
function getActiveProjectId(cwd) {
|
|
166
|
+
const localConfig = loadLocalConfig(cwd);
|
|
167
|
+
if (localConfig?.projectId) {
|
|
168
|
+
return localConfig.projectId;
|
|
169
|
+
}
|
|
170
|
+
return loadConfig().activeProjectId;
|
|
171
|
+
}
|
|
172
|
+
function isConfigured() {
|
|
173
|
+
const config = loadConfig();
|
|
174
|
+
return !!config.apiKey;
|
|
175
|
+
}
|
|
176
|
+
function areSkillsInstalled(cwd) {
|
|
177
|
+
const home = homedir();
|
|
178
|
+
const workingDir = cwd || process.cwd();
|
|
179
|
+
const foundPaths = [];
|
|
180
|
+
const globalSkillsDir = join(home, ".agents", "skills");
|
|
181
|
+
const globalSkillPath = join(globalSkillsDir, "hmy", "SKILL.md");
|
|
182
|
+
if (existsSync(globalSkillPath)) {
|
|
183
|
+
foundPaths.push(globalSkillPath);
|
|
184
|
+
return { installed: true, location: "global", paths: foundPaths };
|
|
185
|
+
}
|
|
186
|
+
const claudeGlobalSkill = join(home, ".claude", "skills", "hmy.md");
|
|
187
|
+
if (existsSync(claudeGlobalSkill)) {
|
|
188
|
+
foundPaths.push(claudeGlobalSkill);
|
|
189
|
+
return { installed: true, location: "global", paths: foundPaths };
|
|
190
|
+
}
|
|
191
|
+
const claudeGlobalSkillAlt = join(home, ".claude", "skills", "hmy", "SKILL.md");
|
|
192
|
+
if (existsSync(claudeGlobalSkillAlt)) {
|
|
193
|
+
foundPaths.push(claudeGlobalSkillAlt);
|
|
194
|
+
return { installed: true, location: "global", paths: foundPaths };
|
|
195
|
+
}
|
|
196
|
+
const localSkillPath = join(workingDir, ".claude", "skills", "hmy.md");
|
|
197
|
+
if (existsSync(localSkillPath)) {
|
|
198
|
+
foundPaths.push(localSkillPath);
|
|
199
|
+
return { installed: true, location: "local", paths: foundPaths };
|
|
200
|
+
}
|
|
201
|
+
const localSkillPathAlt = join(workingDir, ".claude", "skills", "hmy", "SKILL.md");
|
|
202
|
+
if (existsSync(localSkillPathAlt)) {
|
|
203
|
+
foundPaths.push(localSkillPathAlt);
|
|
204
|
+
return { installed: true, location: "local", paths: foundPaths };
|
|
205
|
+
}
|
|
206
|
+
return { installed: false, location: null, paths: [] };
|
|
207
|
+
}
|
|
208
|
+
function hasProjectContext(cwd) {
|
|
209
|
+
const localConfig = loadLocalConfig(cwd);
|
|
210
|
+
return !!(localConfig?.workspaceId || localConfig?.projectId);
|
|
211
|
+
}
|
|
212
|
+
function getMemoryDir() {
|
|
213
|
+
const config = loadConfig();
|
|
214
|
+
if (config.memoryDir)
|
|
215
|
+
return config.memoryDir;
|
|
216
|
+
return join(homedir(), ".harmony", "memory");
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// src/skills.ts
|
|
220
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
2
221
|
import { dirname } from "node:path";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Legacy workflow prompt used by Codex, Cursor agents.
|
|
8
|
-
* Claude Code skills use the newer SKILL_DEFINITIONS content instead.
|
|
9
|
-
*/
|
|
10
|
-
export const HARMONY_WORKFLOW_PROMPT = `# Harmony Card Workflow
|
|
222
|
+
var SKILLS_VERSION = "4";
|
|
223
|
+
var VERSION_MARKER_PREFIX = "<!-- skills-version:";
|
|
224
|
+
var HARMONY_WORKFLOW_PROMPT = `# Harmony Card Workflow
|
|
11
225
|
|
|
12
226
|
Start work on a Harmony card. Card reference: $ARGUMENTS
|
|
13
227
|
|
|
@@ -82,10 +296,7 @@ If pausing: \`harmony_end_agent_session\` with \`status: "paused"\`
|
|
|
82
296
|
|
|
83
297
|
**AI:** \`harmony_generate_prompt\`, \`harmony_process_command\`
|
|
84
298
|
`;
|
|
85
|
-
|
|
86
|
-
* New Claude Code skill content with intent detection.
|
|
87
|
-
*/
|
|
88
|
-
const HMY_SKILL_CONTENT = `# Harmony Card Workflow
|
|
299
|
+
var HMY_SKILL_CONTENT = `# Harmony Card Workflow
|
|
89
300
|
|
|
90
301
|
User input: $ARGUMENTS
|
|
91
302
|
|
|
@@ -242,7 +453,7 @@ Proceed normally without a session. No action needed.
|
|
|
242
453
|
|
|
243
454
|
**AI:** \`harmony_generate_prompt\`, \`harmony_process_command\`
|
|
244
455
|
`;
|
|
245
|
-
|
|
456
|
+
var HMY_PLAN_CONTENT = `# Harmony Plan Workflow
|
|
246
457
|
|
|
247
458
|
Create a new plan or work on an existing one. Argument: $ARGUMENTS
|
|
248
459
|
|
|
@@ -455,139 +666,111 @@ Report the result:
|
|
|
455
666
|
**Planning:** \`harmony_create_plan\`, \`harmony_advance_plan\`, \`harmony_update_plan\`
|
|
456
667
|
**Execution:** \`harmony_create_card\`, \`harmony_update_card\`
|
|
457
668
|
`;
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
669
|
+
var SKILL_DEFINITIONS = {
|
|
670
|
+
hmy: {
|
|
671
|
+
name: "hmy",
|
|
672
|
+
description: "Work with Harmony cards — create, view, update, or start working on them. Use when given a card reference like #42, or commands like create, move, update.",
|
|
673
|
+
argumentHint: "<command or card-reference>",
|
|
674
|
+
content: HMY_SKILL_CONTENT
|
|
675
|
+
},
|
|
676
|
+
"hmy-plan": {
|
|
677
|
+
name: "hmy-plan",
|
|
678
|
+
description: "Create a new plan or work on an existing one. Use when asked to plan a feature, execute a plan, review a plan, or given a plan reference.",
|
|
679
|
+
argumentHint: "[plan name, ID, or topic to plan]",
|
|
680
|
+
content: HMY_PLAN_CONTENT
|
|
681
|
+
}
|
|
471
682
|
};
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
// Apply agent-specific substitutions
|
|
483
|
-
if (agentId === "claude") {
|
|
484
|
-
content = content
|
|
485
|
-
.replace("Your agent identifier", "claude-code")
|
|
486
|
-
.replace("Your agent name", "Claude Code");
|
|
487
|
-
}
|
|
488
|
-
const frontmatter = `---
|
|
683
|
+
function buildSkillFile(skillId, agentId) {
|
|
684
|
+
const skill = SKILL_DEFINITIONS[skillId];
|
|
685
|
+
if (!skill) {
|
|
686
|
+
throw new Error(`Unknown skill: ${skillId}`);
|
|
687
|
+
}
|
|
688
|
+
let content = skill.content;
|
|
689
|
+
if (agentId === "claude") {
|
|
690
|
+
content = content.replace("Your agent identifier", "claude-code").replace("Your agent name", "Claude Code");
|
|
691
|
+
}
|
|
692
|
+
const frontmatter = `---
|
|
489
693
|
name: ${skill.name}
|
|
490
694
|
description: ${skill.description}
|
|
491
695
|
argument-hint: ${skill.argumentHint}
|
|
492
696
|
---`;
|
|
493
|
-
|
|
697
|
+
return `${frontmatter}
|
|
698
|
+
|
|
699
|
+
${content}
|
|
700
|
+
${VERSION_MARKER_PREFIX}${SKILLS_VERSION} -->`;
|
|
494
701
|
}
|
|
495
|
-
/**
|
|
496
|
-
* Parse the skills version from a skill file's content.
|
|
497
|
-
* Returns null if no version marker is found.
|
|
498
|
-
*/
|
|
499
702
|
function parseSkillVersion(content) {
|
|
500
|
-
|
|
501
|
-
|
|
703
|
+
const match = content.match(/<!-- skills-version:(\d+) -->/);
|
|
704
|
+
return match ? match[1] : null;
|
|
502
705
|
}
|
|
503
|
-
/**
|
|
504
|
-
* Find all skill files in a given directory (global or local).
|
|
505
|
-
* Returns an array of { skillId, filePath } for each found skill.
|
|
506
|
-
*/
|
|
507
706
|
function findSkillFiles(paths) {
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
break;
|
|
518
|
-
}
|
|
519
|
-
}
|
|
707
|
+
const results = [];
|
|
708
|
+
for (const filePath of paths) {
|
|
709
|
+
if (!existsSync2(filePath))
|
|
710
|
+
continue;
|
|
711
|
+
for (const skillId of Object.keys(SKILL_DEFINITIONS)) {
|
|
712
|
+
if (filePath.includes(`/${skillId}/`) || filePath.includes(`/${skillId}.md`)) {
|
|
713
|
+
results.push({ skillId, filePath });
|
|
714
|
+
break;
|
|
715
|
+
}
|
|
520
716
|
}
|
|
521
|
-
|
|
717
|
+
}
|
|
718
|
+
return results;
|
|
522
719
|
}
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
if (alreadyFound)
|
|
544
|
-
continue;
|
|
545
|
-
// Try to find sibling skill file
|
|
546
|
-
let siblingPath;
|
|
547
|
-
if (samplePath.endsWith("SKILL.md")) {
|
|
548
|
-
// ~/.agents/skills/hmy/SKILL.md -> ~/.agents/skills/hmy-plan/SKILL.md
|
|
549
|
-
const parentDir = dirname(dirname(samplePath));
|
|
550
|
-
siblingPath = `${parentDir}/${skillId}/SKILL.md`;
|
|
551
|
-
}
|
|
552
|
-
else {
|
|
553
|
-
// ~/.claude/skills/hmy.md -> ~/.claude/skills/hmy-plan.md
|
|
554
|
-
const parentDir = dirname(samplePath);
|
|
555
|
-
siblingPath = `${parentDir}/${skillId}.md`;
|
|
556
|
-
}
|
|
557
|
-
if (existsSync(siblingPath)) {
|
|
558
|
-
skillFiles.push({ skillId, filePath: siblingPath });
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
if (skillFiles.length === 0) {
|
|
563
|
-
return;
|
|
720
|
+
async function refreshSkills() {
|
|
721
|
+
try {
|
|
722
|
+
const status = areSkillsInstalled();
|
|
723
|
+
if (!status.installed) {
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
const skillFiles = findSkillFiles(status.paths);
|
|
727
|
+
if (skillFiles.length > 0) {
|
|
728
|
+
const samplePath = skillFiles[0].filePath;
|
|
729
|
+
for (const skillId of Object.keys(SKILL_DEFINITIONS)) {
|
|
730
|
+
const alreadyFound = skillFiles.some((sf) => sf.skillId === skillId);
|
|
731
|
+
if (alreadyFound)
|
|
732
|
+
continue;
|
|
733
|
+
let siblingPath;
|
|
734
|
+
if (samplePath.endsWith("SKILL.md")) {
|
|
735
|
+
const parentDir = dirname(dirname(samplePath));
|
|
736
|
+
siblingPath = `${parentDir}/${skillId}/SKILL.md`;
|
|
737
|
+
} else {
|
|
738
|
+
const parentDir = dirname(samplePath);
|
|
739
|
+
siblingPath = `${parentDir}/${skillId}.md`;
|
|
564
740
|
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
try {
|
|
568
|
-
const currentContent = readFileSync(filePath, "utf-8");
|
|
569
|
-
const currentVersion = parseSkillVersion(currentContent);
|
|
570
|
-
// Update if no version marker or version is older
|
|
571
|
-
if (currentVersion === null ||
|
|
572
|
-
Number(currentVersion) < Number(SKILLS_VERSION)) {
|
|
573
|
-
const newContent = buildSkillFile(skillId, "claude");
|
|
574
|
-
const dir = dirname(filePath);
|
|
575
|
-
if (!existsSync(dir)) {
|
|
576
|
-
mkdirSync(dir, { recursive: true });
|
|
577
|
-
}
|
|
578
|
-
writeFileSync(filePath, newContent);
|
|
579
|
-
updated = true;
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
catch {
|
|
583
|
-
// Silently skip files we can't read/write
|
|
584
|
-
}
|
|
741
|
+
if (existsSync2(siblingPath)) {
|
|
742
|
+
skillFiles.push({ skillId, filePath: siblingPath });
|
|
585
743
|
}
|
|
586
|
-
|
|
587
|
-
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
if (skillFiles.length === 0) {
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
let updated = false;
|
|
750
|
+
for (const { skillId, filePath } of skillFiles) {
|
|
751
|
+
try {
|
|
752
|
+
const currentContent = readFileSync2(filePath, "utf-8");
|
|
753
|
+
const currentVersion = parseSkillVersion(currentContent);
|
|
754
|
+
if (currentVersion === null || Number(currentVersion) < Number(SKILLS_VERSION)) {
|
|
755
|
+
const newContent = buildSkillFile(skillId, "claude");
|
|
756
|
+
const dir = dirname(filePath);
|
|
757
|
+
if (!existsSync2(dir)) {
|
|
758
|
+
mkdirSync2(dir, { recursive: true });
|
|
759
|
+
}
|
|
760
|
+
writeFileSync2(filePath, newContent);
|
|
761
|
+
updated = true;
|
|
588
762
|
}
|
|
763
|
+
} catch {}
|
|
589
764
|
}
|
|
590
|
-
|
|
591
|
-
|
|
765
|
+
if (updated) {
|
|
766
|
+
console.error(`Harmony: Updated skills to v${SKILLS_VERSION}`);
|
|
592
767
|
}
|
|
768
|
+
} catch {}
|
|
593
769
|
}
|
|
770
|
+
export {
|
|
771
|
+
refreshSkills,
|
|
772
|
+
buildSkillFile,
|
|
773
|
+
SKILL_DEFINITIONS,
|
|
774
|
+
SKILLS_VERSION,
|
|
775
|
+
HARMONY_WORKFLOW_PROMPT
|
|
776
|
+
};
|