wave-agent-sdk 0.17.5 → 0.17.6
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/managers/MemoryRuleManager.d.ts.map +1 -1
- package/dist/managers/MemoryRuleManager.js +30 -13
- package/dist/managers/hookManager.d.ts.map +1 -1
- package/dist/managers/hookManager.js +3 -1
- package/dist/managers/lspManager.d.ts.map +1 -1
- package/dist/managers/lspManager.js +12 -4
- package/dist/managers/mcpManager.d.ts.map +1 -1
- package/dist/managers/mcpManager.js +13 -6
- package/dist/managers/skillManager.d.ts +3 -0
- package/dist/managers/skillManager.d.ts.map +1 -1
- package/dist/managers/skillManager.js +69 -54
- package/dist/services/MarketplaceService.d.ts.map +1 -1
- package/dist/services/MarketplaceService.js +12 -4
- package/dist/services/memory.d.ts.map +1 -1
- package/dist/services/memory.js +39 -5
- package/dist/services/pluginLoader.d.ts.map +1 -1
- package/dist/services/pluginLoader.js +30 -7
- package/dist/types/skills.d.ts +1 -0
- package/dist/types/skills.d.ts.map +1 -1
- package/dist/utils/customCommands.d.ts.map +1 -1
- package/dist/utils/customCommands.js +11 -9
- package/dist/utils/skillParser.d.ts.map +1 -1
- package/dist/utils/skillParser.js +3 -1
- package/dist/utils/subagentParser.d.ts.map +1 -1
- package/dist/utils/subagentParser.js +18 -7
- package/package.json +1 -1
- package/src/managers/MemoryRuleManager.ts +29 -14
- package/src/managers/hookManager.ts +6 -1
- package/src/managers/lspManager.ts +23 -5
- package/src/managers/mcpManager.ts +24 -7
- package/src/managers/skillManager.ts +90 -57
- package/src/services/MarketplaceService.ts +22 -4
- package/src/services/memory.ts +43 -6
- package/src/services/pluginLoader.ts +35 -7
- package/src/types/skills.ts +1 -0
- package/src/utils/customCommands.ts +17 -12
- package/src/utils/skillParser.ts +3 -1
- package/src/utils/subagentParser.ts +22 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"customCommands.d.ts","sourceRoot":"","sources":["../../src/utils/customCommands.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAK5D;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAwC3E;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,kBAAkB,EAAE,
|
|
1
|
+
{"version":3,"file":"customCommands.d.ts","sourceRoot":"","sources":["../../src/utils/customCommands.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAK5D;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAwC3E;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAuB7E"}
|
|
@@ -60,16 +60,18 @@ export function scanCommandsDirectory(dirPath) {
|
|
|
60
60
|
* Load all custom slash commands from both project and user directories
|
|
61
61
|
*/
|
|
62
62
|
export function loadCustomSlashCommands(workdir) {
|
|
63
|
-
const
|
|
64
|
-
const
|
|
65
|
-
|
|
63
|
+
const userClaudeCommands = scanCommandsDirectory(join(homedir(), ".claude", "commands"));
|
|
64
|
+
const userWaveCommands = scanCommandsDirectory(getUserCommandsDir());
|
|
65
|
+
const projectClaudeCommands = scanCommandsDirectory(join(workdir, ".claude", "commands"));
|
|
66
|
+
const projectWaveCommands = scanCommandsDirectory(getProjectCommandsDir(workdir));
|
|
66
67
|
const commandMap = new Map();
|
|
67
|
-
//
|
|
68
|
-
for (const command of
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
// Write in priority order: lowest first, highest last (overwrites)
|
|
69
|
+
for (const command of [
|
|
70
|
+
...userClaudeCommands,
|
|
71
|
+
...userWaveCommands,
|
|
72
|
+
...projectClaudeCommands,
|
|
73
|
+
...projectWaveCommands,
|
|
74
|
+
]) {
|
|
73
75
|
commandMap.set(command.id, command);
|
|
74
76
|
}
|
|
75
77
|
return Array.from(commandMap.values());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skillParser.d.ts","sourceRoot":"","sources":["../../src/utils/skillParser.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,EAEjB,aAAa,EACd,MAAM,mBAAmB,CAAC;AAE3B;;GAEG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,iBAAsB,GAC9B,eAAe,
|
|
1
|
+
{"version":3,"file":"skillParser.d.ts","sourceRoot":"","sources":["../../src/utils/skillParser.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,EAEjB,aAAa,EACd,MAAM,mBAAmB,CAAC;AAE3B;;GAEG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,iBAAsB,GAC9B,eAAe,CAkGjB;AAyDD;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,EAAE,CAwCvE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOtD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAa5E"}
|
|
@@ -36,7 +36,9 @@ export function parseSkillFile(filePath, options = {}) {
|
|
|
36
36
|
// Determine skill type and path
|
|
37
37
|
const skillPath = basePath || dirname(filePath);
|
|
38
38
|
const skillType = skillPath.includes("/.wave/skills") ||
|
|
39
|
-
skillPath.includes("\\.wave\\skills")
|
|
39
|
+
skillPath.includes("\\.wave\\skills") ||
|
|
40
|
+
skillPath.includes("/.claude/skills") ||
|
|
41
|
+
skillPath.includes("\\.claude\\skills")
|
|
40
42
|
? "project"
|
|
41
43
|
: "personal";
|
|
42
44
|
// Extract allowed tools
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subagentParser.d.ts","sourceRoot":"","sources":["../../src/utils/subagentParser.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;IACjD,QAAQ,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAmKD;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,QAAQ,EACf,UAAU,EAAE,MAAM,GACjB,qBAAqB,CAEvB;AAqCD;;GAEG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,qBAAqB,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"subagentParser.d.ts","sourceRoot":"","sources":["../../src/utils/subagentParser.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;IACjD,QAAQ,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAmKD;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,QAAQ,EACf,UAAU,EAAE,MAAM,GACjB,qBAAqB,CAEvB;AAqCD;;GAEG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAoClC;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAGvC"}
|
|
@@ -160,17 +160,28 @@ function scanSubagentDirectory(dirPath, scope) {
|
|
|
160
160
|
* Load all subagent configurations from project and user directories, plus built-in subagents
|
|
161
161
|
*/
|
|
162
162
|
export async function loadSubagentConfigurations(workdir) {
|
|
163
|
-
const
|
|
164
|
-
const
|
|
163
|
+
const projectWaveDir = join(workdir, ".wave", "agents");
|
|
164
|
+
const projectClaudeDir = join(workdir, ".claude", "agents");
|
|
165
|
+
const userWaveDir = join(process.env.HOME || "~", ".wave", "agents");
|
|
166
|
+
const userClaudeDir = join(process.env.HOME || "~", ".claude", "agents");
|
|
165
167
|
const builtinDir = getBuiltinSubagentsDir();
|
|
166
168
|
// Load configurations from all sources
|
|
167
169
|
const builtinConfigs = scanSubagentDirectory(builtinDir, "builtin");
|
|
168
|
-
const
|
|
169
|
-
const
|
|
170
|
-
|
|
170
|
+
const userClaudeConfigs = scanSubagentDirectory(userClaudeDir, "user");
|
|
171
|
+
const userWaveConfigs = scanSubagentDirectory(userWaveDir, "user");
|
|
172
|
+
const projectClaudeConfigs = scanSubagentDirectory(projectClaudeDir, "project");
|
|
173
|
+
const projectWaveConfigs = scanSubagentDirectory(projectWaveDir, "project");
|
|
174
|
+
// Merge configurations, with .wave taking priority over .claude
|
|
171
175
|
const configMap = new Map();
|
|
172
|
-
//
|
|
173
|
-
|
|
176
|
+
// Merge order: builtin → userClaude → userWave → projectClaude → projectWave
|
|
177
|
+
// Later writes override earlier ones, so .wave takes priority over .claude
|
|
178
|
+
for (const config of [
|
|
179
|
+
...builtinConfigs,
|
|
180
|
+
...userClaudeConfigs,
|
|
181
|
+
...userWaveConfigs,
|
|
182
|
+
...projectClaudeConfigs,
|
|
183
|
+
...projectWaveConfigs,
|
|
184
|
+
]) {
|
|
174
185
|
configMap.set(config.name, config);
|
|
175
186
|
}
|
|
176
187
|
return Array.from(configMap.values()).sort((a, b) => {
|
package/package.json
CHANGED
|
@@ -39,23 +39,26 @@ export class MemoryRuleManager {
|
|
|
39
39
|
* Scans .wave/rules and ~/.wave/rules for memory rule files.
|
|
40
40
|
*/
|
|
41
41
|
async discoverRules(): Promise<void> {
|
|
42
|
-
const
|
|
43
|
-
const
|
|
42
|
+
const projectWaveRulesDir = path.join(this.workdir, ".wave", "rules");
|
|
43
|
+
const projectClaudeRulesDir = path.join(this.workdir, ".claude", "rules");
|
|
44
|
+
const userWaveRulesDir = path.join(os.homedir(), ".wave", "rules");
|
|
45
|
+
const userClaudeRulesDir = path.join(os.homedir(), ".claude", "rules");
|
|
44
46
|
|
|
45
47
|
logger.debug(`Scanning for modular memory rules...`);
|
|
46
|
-
logger.debug(` User rules directory: ${
|
|
47
|
-
logger.debug(` Project rules directory: ${
|
|
48
|
+
logger.debug(` User rules directory: ${userWaveRulesDir}`);
|
|
49
|
+
logger.debug(` Project rules directory: ${projectWaveRulesDir}`);
|
|
48
50
|
|
|
49
51
|
const newRules: Record<string, MemoryRule> = {};
|
|
50
52
|
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
await this.scanDirectory(
|
|
54
|
-
await this.scanDirectory(
|
|
53
|
+
// Scan order: userClaude → userWave → projectClaude → projectWave
|
|
54
|
+
// Later writes override, so .wave takes priority over .claude
|
|
55
|
+
await this.scanDirectory(userClaudeRulesDir, "user", newRules);
|
|
56
|
+
await this.scanDirectory(userWaveRulesDir, "user", newRules);
|
|
57
|
+
await this.scanDirectory(projectClaudeRulesDir, "project", newRules);
|
|
58
|
+
await this.scanDirectory(projectWaveRulesDir, "project", newRules);
|
|
55
59
|
|
|
56
60
|
this.state.rules = newRules;
|
|
57
61
|
const ruleCount = Object.keys(newRules).length;
|
|
58
|
-
// Removed verbose logging of all discovered rules
|
|
59
62
|
logger.debug(`Discovered ${ruleCount} modular memory rules`);
|
|
60
63
|
}
|
|
61
64
|
|
|
@@ -124,11 +127,23 @@ export class MemoryRuleManager {
|
|
|
124
127
|
try {
|
|
125
128
|
const content = await fs.readFile(filePath, "utf-8");
|
|
126
129
|
const rule = this.service.parseRule(content, filePath, source);
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
|
|
131
|
+
// Determine rulesRoot dynamically based on actual file path
|
|
132
|
+
let rulesRoot: string;
|
|
133
|
+
if (source === "project") {
|
|
134
|
+
if (filePath.includes(path.join(".claude", "rules"))) {
|
|
135
|
+
rulesRoot = path.join(this.workdir, ".claude", "rules");
|
|
136
|
+
} else {
|
|
137
|
+
rulesRoot = path.join(this.workdir, ".wave", "rules");
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
if (filePath.includes(path.join(".claude", "rules"))) {
|
|
141
|
+
rulesRoot = path.join(os.homedir(), ".claude", "rules");
|
|
142
|
+
} else {
|
|
143
|
+
rulesRoot = path.join(os.homedir(), ".wave", "rules");
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
132
147
|
const relativeId = path.relative(rulesRoot, filePath);
|
|
133
148
|
rule.id = relativeId;
|
|
134
149
|
registry[rule.id] = rule;
|
|
@@ -183,16 +183,21 @@ export class HookManager {
|
|
|
183
183
|
env: {
|
|
184
184
|
...("env" in context ? (context.env ?? {}) : {}),
|
|
185
185
|
WAVE_PLUGIN_ROOT: hookCommand.pluginRoot,
|
|
186
|
+
CLAUDE_PLUGIN_ROOT: hookCommand.pluginRoot,
|
|
186
187
|
},
|
|
187
188
|
}
|
|
188
189
|
: context;
|
|
189
190
|
|
|
190
|
-
// Substitute ${WAVE_PLUGIN_ROOT} in the command string
|
|
191
|
+
// Substitute ${WAVE_PLUGIN_ROOT} and ${CLAUDE_PLUGIN_ROOT} in the command string
|
|
191
192
|
if (hookCommand.pluginRoot) {
|
|
192
193
|
command = command.replace(
|
|
193
194
|
/\$\{WAVE_PLUGIN_ROOT\}/g,
|
|
194
195
|
hookCommand.pluginRoot,
|
|
195
196
|
);
|
|
197
|
+
command = command.replace(
|
|
198
|
+
/\$\{CLAUDE_PLUGIN_ROOT\}/g,
|
|
199
|
+
hookCommand.pluginRoot,
|
|
200
|
+
);
|
|
196
201
|
}
|
|
197
202
|
|
|
198
203
|
if (hookCommand.async) {
|
|
@@ -92,22 +92,40 @@ export class LspManager implements ILspManager {
|
|
|
92
92
|
try {
|
|
93
93
|
const env = { ...process.env, ...config.env };
|
|
94
94
|
|
|
95
|
-
// For plugin servers, substitute ${WAVE_PLUGIN_ROOT}
|
|
95
|
+
// For plugin servers, substitute ${WAVE_PLUGIN_ROOT} and ${CLAUDE_PLUGIN_ROOT}
|
|
96
96
|
let command = config.command;
|
|
97
97
|
let args = config.args || [];
|
|
98
98
|
if (config.pluginRoot) {
|
|
99
99
|
env.WAVE_PLUGIN_ROOT = config.pluginRoot;
|
|
100
|
+
env.CLAUDE_PLUGIN_ROOT = config.pluginRoot;
|
|
100
101
|
command = command.replace(/\$\{WAVE_PLUGIN_ROOT\}/g, config.pluginRoot);
|
|
101
|
-
|
|
102
|
-
|
|
102
|
+
command = command.replace(
|
|
103
|
+
/\$\{CLAUDE_PLUGIN_ROOT\}/g,
|
|
104
|
+
config.pluginRoot,
|
|
103
105
|
);
|
|
104
|
-
|
|
106
|
+
args = args.map((arg) => {
|
|
107
|
+
let result = arg.replace(
|
|
108
|
+
/\$\{WAVE_PLUGIN_ROOT\}/g,
|
|
109
|
+
config.pluginRoot!,
|
|
110
|
+
);
|
|
111
|
+
result = result.replace(
|
|
112
|
+
/\$\{CLAUDE_PLUGIN_ROOT\}/g,
|
|
113
|
+
config.pluginRoot!,
|
|
114
|
+
);
|
|
115
|
+
return result;
|
|
116
|
+
});
|
|
117
|
+
// Also expand plugin root in user-provided env values
|
|
105
118
|
if (config.env) {
|
|
106
119
|
for (const [key, value] of Object.entries(config.env)) {
|
|
107
|
-
|
|
120
|
+
let expanded = value.replace(
|
|
108
121
|
/\$\{WAVE_PLUGIN_ROOT\}/g,
|
|
109
122
|
config.pluginRoot!,
|
|
110
123
|
);
|
|
124
|
+
expanded = expanded.replace(
|
|
125
|
+
/\$\{CLAUDE_PLUGIN_ROOT\}/g,
|
|
126
|
+
config.pluginRoot!,
|
|
127
|
+
);
|
|
128
|
+
env[key] = expanded;
|
|
111
129
|
}
|
|
112
130
|
}
|
|
113
131
|
}
|
|
@@ -40,7 +40,7 @@ export interface McpManagerOptions {
|
|
|
40
40
|
* Expand environment variables in a string value.
|
|
41
41
|
* Supports ${VAR} and ${VAR:-default} patterns.
|
|
42
42
|
*/
|
|
43
|
-
const WAVE_TEMPLATE_VARS = ["WAVE_PLUGIN_ROOT"];
|
|
43
|
+
const WAVE_TEMPLATE_VARS = ["WAVE_PLUGIN_ROOT", "CLAUDE_PLUGIN_ROOT"];
|
|
44
44
|
|
|
45
45
|
export function expandEnvVars(value: string): string {
|
|
46
46
|
return value.replace(/\$\{([^}]+)\}/g, (_match, expr: string) => {
|
|
@@ -440,25 +440,42 @@ export class McpManager {
|
|
|
440
440
|
...(server.config.env || {}),
|
|
441
441
|
};
|
|
442
442
|
|
|
443
|
-
// For plugin servers, substitute ${WAVE_PLUGIN_ROOT}
|
|
444
|
-
// (same pattern as Claude Code's substitutePluginVariables)
|
|
443
|
+
// For plugin servers, substitute ${WAVE_PLUGIN_ROOT} and ${CLAUDE_PLUGIN_ROOT}
|
|
445
444
|
let command = server.config.command;
|
|
446
445
|
let args = server.config.args || [];
|
|
447
446
|
if (server.config.pluginRoot) {
|
|
448
447
|
env.WAVE_PLUGIN_ROOT = server.config.pluginRoot;
|
|
448
|
+
env.CLAUDE_PLUGIN_ROOT = server.config.pluginRoot;
|
|
449
449
|
command = command.replace(
|
|
450
450
|
/\$\{WAVE_PLUGIN_ROOT\}/g,
|
|
451
451
|
server.config.pluginRoot,
|
|
452
452
|
);
|
|
453
|
-
|
|
454
|
-
|
|
453
|
+
command = command.replace(
|
|
454
|
+
/\$\{CLAUDE_PLUGIN_ROOT\}/g,
|
|
455
|
+
server.config.pluginRoot,
|
|
455
456
|
);
|
|
456
|
-
|
|
457
|
+
args = args.map((arg) => {
|
|
458
|
+
let result = arg.replace(
|
|
459
|
+
/\$\{WAVE_PLUGIN_ROOT\}/g,
|
|
460
|
+
server.config.pluginRoot!,
|
|
461
|
+
);
|
|
462
|
+
result = result.replace(
|
|
463
|
+
/\$\{CLAUDE_PLUGIN_ROOT\}/g,
|
|
464
|
+
server.config.pluginRoot!,
|
|
465
|
+
);
|
|
466
|
+
return result;
|
|
467
|
+
});
|
|
468
|
+
// Also expand plugin root in user-provided env values
|
|
457
469
|
for (const [key, value] of Object.entries(server.config.env || {})) {
|
|
458
|
-
|
|
470
|
+
let expanded = value.replace(
|
|
459
471
|
/\$\{WAVE_PLUGIN_ROOT\}/g,
|
|
460
472
|
server.config.pluginRoot!,
|
|
461
473
|
);
|
|
474
|
+
expanded = expanded.replace(
|
|
475
|
+
/\$\{CLAUDE_PLUGIN_ROOT\}/g,
|
|
476
|
+
server.config.pluginRoot!,
|
|
477
|
+
);
|
|
478
|
+
env[key] = expanded;
|
|
462
479
|
}
|
|
463
480
|
}
|
|
464
481
|
transport = new StdioClientTransport({
|
|
@@ -29,6 +29,7 @@ import { logger } from "../utils/globalLogger.js";
|
|
|
29
29
|
*/
|
|
30
30
|
export class SkillManager extends EventEmitter {
|
|
31
31
|
private personalSkillsPath: string;
|
|
32
|
+
private personalClaudeSkillsPath: string;
|
|
32
33
|
private scanTimeout: number;
|
|
33
34
|
private workdir: string;
|
|
34
35
|
|
|
@@ -47,6 +48,8 @@ export class SkillManager extends EventEmitter {
|
|
|
47
48
|
super();
|
|
48
49
|
this.personalSkillsPath =
|
|
49
50
|
options.personalSkillsPath || join(homedir(), ".wave", "skills");
|
|
51
|
+
this.personalClaudeSkillsPath =
|
|
52
|
+
options.personalClaudeSkillsPath || join(homedir(), ".claude", "skills");
|
|
50
53
|
this.scanTimeout = options.scanTimeout || 5000;
|
|
51
54
|
this.workdir = options.workdir || process.cwd();
|
|
52
55
|
this.watchEnabled = options.watch ?? false;
|
|
@@ -127,7 +130,9 @@ export class SkillManager extends EventEmitter {
|
|
|
127
130
|
|
|
128
131
|
const pathsToWatch = [
|
|
129
132
|
this.personalSkillsPath,
|
|
133
|
+
this.personalClaudeSkillsPath,
|
|
130
134
|
join(this.workdir, ".wave", "skills"),
|
|
135
|
+
join(this.workdir, ".claude", "skills"),
|
|
131
136
|
];
|
|
132
137
|
|
|
133
138
|
logger?.debug(`Setting up skill watcher for: ${pathsToWatch.join(", ")}`);
|
|
@@ -220,6 +225,11 @@ export class SkillManager extends EventEmitter {
|
|
|
220
225
|
"builtin",
|
|
221
226
|
);
|
|
222
227
|
|
|
228
|
+
const personalClaudeCollection = await this.discoverSkillCollection(
|
|
229
|
+
this.personalClaudeSkillsPath,
|
|
230
|
+
"personal",
|
|
231
|
+
);
|
|
232
|
+
|
|
223
233
|
const personalCollection = await this.discoverSkillCollection(
|
|
224
234
|
this.personalSkillsPath,
|
|
225
235
|
"personal",
|
|
@@ -232,10 +242,14 @@ export class SkillManager extends EventEmitter {
|
|
|
232
242
|
|
|
233
243
|
return {
|
|
234
244
|
builtinSkills: builtinCollection.skills,
|
|
235
|
-
personalSkills:
|
|
245
|
+
personalSkills: new Map([
|
|
246
|
+
...personalClaudeCollection.skills,
|
|
247
|
+
...personalCollection.skills, // .wave overrides .claude
|
|
248
|
+
]),
|
|
236
249
|
projectSkills: projectCollection.skills,
|
|
237
250
|
errors: [
|
|
238
251
|
...builtinCollection.errors,
|
|
252
|
+
...personalClaudeCollection.errors,
|
|
239
253
|
...personalCollection.errors,
|
|
240
254
|
...projectCollection.errors,
|
|
241
255
|
],
|
|
@@ -256,77 +270,88 @@ export class SkillManager extends EventEmitter {
|
|
|
256
270
|
errors: [],
|
|
257
271
|
};
|
|
258
272
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
273
|
+
if (type === "project") {
|
|
274
|
+
// Scan .claude/skills first, then .wave/skills (wave overrides)
|
|
275
|
+
const claudePath = join(basePath, ".claude", "skills");
|
|
276
|
+
const wavePath = join(basePath, ".wave", "skills");
|
|
277
|
+
await this.scanSkillPath(claudePath, collection);
|
|
278
|
+
await this.scanSkillPath(wavePath, collection);
|
|
264
279
|
} else {
|
|
265
|
-
|
|
280
|
+
await this.scanSkillPath(basePath, collection);
|
|
266
281
|
}
|
|
267
282
|
|
|
283
|
+
return collection;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
private async scanSkillPath(
|
|
287
|
+
skillsPath: string,
|
|
288
|
+
collection: SkillCollection,
|
|
289
|
+
): Promise<void> {
|
|
268
290
|
try {
|
|
269
291
|
const skillDirs = await this.findSkillDirectories(skillsPath);
|
|
270
292
|
logger?.debug(
|
|
271
293
|
`Found ${skillDirs.length} potential skill directories in ${skillsPath}`,
|
|
272
294
|
);
|
|
295
|
+
await this.processSkillDirs(skillDirs, collection);
|
|
296
|
+
} catch (error) {
|
|
297
|
+
logger?.debug(
|
|
298
|
+
`Could not scan ${skillsPath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
273
302
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
303
|
+
private async processSkillDirs(
|
|
304
|
+
skillDirs: string[],
|
|
305
|
+
collection: SkillCollection,
|
|
306
|
+
): Promise<void> {
|
|
307
|
+
for (const skillDir of skillDirs) {
|
|
308
|
+
try {
|
|
309
|
+
const skillFilePath = join(skillDir, "SKILL.md");
|
|
277
310
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
const parsed = parseSkillFile(skillFilePath, {
|
|
286
|
-
basePath: skillDir,
|
|
287
|
-
validateMetadata: true,
|
|
288
|
-
});
|
|
311
|
+
// Check if SKILL.md exists
|
|
312
|
+
try {
|
|
313
|
+
await stat(skillFilePath);
|
|
314
|
+
} catch {
|
|
315
|
+
continue; // Skip directories without SKILL.md
|
|
316
|
+
}
|
|
289
317
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
} catch (error) {
|
|
318
|
+
const parsed = parseSkillFile(skillFilePath, {
|
|
319
|
+
basePath: skillDir,
|
|
320
|
+
validateMetadata: true,
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
if (parsed.isValid) {
|
|
324
|
+
// Override the skill type with the collection type
|
|
325
|
+
const skillMetadata: SkillMetadata = {
|
|
326
|
+
...parsed.skillMetadata,
|
|
327
|
+
type: collection.type,
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
// Create full skill object with content
|
|
331
|
+
const skill: Skill = {
|
|
332
|
+
...skillMetadata,
|
|
333
|
+
content: parsed.content,
|
|
334
|
+
frontmatter: parsed.frontmatter,
|
|
335
|
+
isValid: parsed.isValid,
|
|
336
|
+
errors: parsed.validationErrors,
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
collection.skills.set(skillMetadata.name, skillMetadata);
|
|
340
|
+
// Store the full skill content in the manager's skillContent map
|
|
341
|
+
this.skillContent.set(skillMetadata.name, skill);
|
|
342
|
+
} else {
|
|
316
343
|
collection.errors.push({
|
|
317
344
|
skillPath: skillDir,
|
|
318
|
-
message:
|
|
345
|
+
message: parsed.validationErrors.join("; "),
|
|
319
346
|
});
|
|
320
347
|
}
|
|
348
|
+
} catch (error) {
|
|
349
|
+
collection.errors.push({
|
|
350
|
+
skillPath: skillDir,
|
|
351
|
+
message: `Failed to process skill: ${error instanceof Error ? error.message : String(error)}`,
|
|
352
|
+
});
|
|
321
353
|
}
|
|
322
|
-
} catch (error) {
|
|
323
|
-
logger?.debug(
|
|
324
|
-
`Could not scan ${skillsPath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
325
|
-
);
|
|
326
|
-
// Not an error - the directory might not exist yet
|
|
327
354
|
}
|
|
328
|
-
|
|
329
|
-
return collection;
|
|
330
355
|
}
|
|
331
356
|
|
|
332
357
|
/**
|
|
@@ -450,15 +475,23 @@ export class SkillManager extends EventEmitter {
|
|
|
450
475
|
// 1. Substitute parameters ($1, $ARGUMENTS, etc.)
|
|
451
476
|
mainContent = substituteCommandParameters(mainContent, argsString);
|
|
452
477
|
|
|
453
|
-
// 2. Substitute ${WAVE_SKILL_DIR}
|
|
478
|
+
// 2. Substitute ${WAVE_SKILL_DIR} and ${CLAUDE_SKILL_DIR}
|
|
454
479
|
mainContent = mainContent.replace(/\$\{WAVE_SKILL_DIR\}/g, skill.skillPath);
|
|
480
|
+
mainContent = mainContent.replace(
|
|
481
|
+
/\$\{CLAUDE_SKILL_DIR\}/g,
|
|
482
|
+
skill.skillPath,
|
|
483
|
+
);
|
|
455
484
|
|
|
456
|
-
// 3. Substitute ${WAVE_PLUGIN_ROOT}
|
|
485
|
+
// 3. Substitute ${WAVE_PLUGIN_ROOT} and ${CLAUDE_PLUGIN_ROOT}
|
|
457
486
|
if (skill.pluginRoot) {
|
|
458
487
|
mainContent = mainContent.replace(
|
|
459
488
|
/\$\{WAVE_PLUGIN_ROOT\}/g,
|
|
460
489
|
skill.pluginRoot,
|
|
461
490
|
);
|
|
491
|
+
mainContent = mainContent.replace(
|
|
492
|
+
/\$\{CLAUDE_PLUGIN_ROOT\}/g,
|
|
493
|
+
skill.pluginRoot,
|
|
494
|
+
);
|
|
462
495
|
}
|
|
463
496
|
|
|
464
497
|
return skillPath + mainContent;
|
|
@@ -390,13 +390,21 @@ export class MarketplaceService {
|
|
|
390
390
|
async loadMarketplaceManifest(
|
|
391
391
|
marketplacePath: string,
|
|
392
392
|
): Promise<MarketplaceManifest> {
|
|
393
|
-
|
|
393
|
+
let manifestPath = path.join(
|
|
394
394
|
marketplacePath,
|
|
395
395
|
".wave-plugin",
|
|
396
396
|
"marketplace.json",
|
|
397
397
|
);
|
|
398
398
|
if (!existsSync(manifestPath)) {
|
|
399
|
-
|
|
399
|
+
// Fallback to .claude-plugin/marketplace.json
|
|
400
|
+
manifestPath = path.join(
|
|
401
|
+
marketplacePath,
|
|
402
|
+
".claude-plugin",
|
|
403
|
+
"marketplace.json",
|
|
404
|
+
);
|
|
405
|
+
if (!existsSync(manifestPath)) {
|
|
406
|
+
throw new Error(`Marketplace manifest not found at ${manifestPath}`);
|
|
407
|
+
}
|
|
400
408
|
}
|
|
401
409
|
const content = await fs.readFile(manifestPath, "utf-8");
|
|
402
410
|
const manifest = JSON.parse(content);
|
|
@@ -899,13 +907,23 @@ export class MarketplaceService {
|
|
|
899
907
|
pluginSrcPath = path.resolve(marketplacePath, pluginEntry.source);
|
|
900
908
|
}
|
|
901
909
|
|
|
902
|
-
|
|
910
|
+
let pluginManifestPath = path.join(
|
|
903
911
|
pluginSrcPath,
|
|
904
912
|
".wave-plugin",
|
|
905
913
|
"plugin.json",
|
|
906
914
|
);
|
|
907
915
|
if (!existsSync(pluginManifestPath)) {
|
|
908
|
-
|
|
916
|
+
// Fallback to .claude-plugin/plugin.json
|
|
917
|
+
pluginManifestPath = path.join(
|
|
918
|
+
pluginSrcPath,
|
|
919
|
+
".claude-plugin",
|
|
920
|
+
"plugin.json",
|
|
921
|
+
);
|
|
922
|
+
if (!existsSync(pluginManifestPath)) {
|
|
923
|
+
throw new Error(
|
|
924
|
+
`Plugin manifest not found at ${pluginManifestPath}`,
|
|
925
|
+
);
|
|
926
|
+
}
|
|
909
927
|
}
|
|
910
928
|
|
|
911
929
|
const pluginManifestContent = await fs.readFile(
|
package/src/services/memory.ts
CHANGED
|
@@ -130,6 +130,27 @@ export class MemoryService {
|
|
|
130
130
|
userMemoryFile: USER_MEMORY_FILE,
|
|
131
131
|
contentLength: content.length,
|
|
132
132
|
});
|
|
133
|
+
|
|
134
|
+
// If the file is still the default empty template, fallback to ~/.claude/AGENTS.md
|
|
135
|
+
const defaultTemplate =
|
|
136
|
+
"# User Memory\n\nThis is the user-level memory file, recording important information and context across projects.\n\n";
|
|
137
|
+
if (content === defaultTemplate) {
|
|
138
|
+
const claudeMemoryPath = path.join(homedir(), ".claude", "AGENTS.md");
|
|
139
|
+
try {
|
|
140
|
+
const claudeContent = await fs.readFile(claudeMemoryPath, "utf-8");
|
|
141
|
+
logger.debug(
|
|
142
|
+
"User memory content read from ~/.claude/AGENTS.md fallback",
|
|
143
|
+
{
|
|
144
|
+
claudeMemoryPath,
|
|
145
|
+
contentLength: claudeContent.length,
|
|
146
|
+
},
|
|
147
|
+
);
|
|
148
|
+
return claudeContent;
|
|
149
|
+
} catch {
|
|
150
|
+
// CLAUDE.md doesn't exist or can't be read, return the default template
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
133
154
|
return content;
|
|
134
155
|
} catch (error) {
|
|
135
156
|
logger.error("Failed to read user memory:", error);
|
|
@@ -139,8 +160,6 @@ export class MemoryService {
|
|
|
139
160
|
|
|
140
161
|
async readMemoryFile(workdir: string): Promise<string> {
|
|
141
162
|
const memoryFilePath = path.join(workdir, "AGENTS.md");
|
|
142
|
-
|
|
143
|
-
// Direct file access
|
|
144
163
|
try {
|
|
145
164
|
const content = await fs.readFile(memoryFilePath, "utf-8");
|
|
146
165
|
logger.debug("Memory file read successfully via direct file access", {
|
|
@@ -150,10 +169,28 @@ export class MemoryService {
|
|
|
150
169
|
return content;
|
|
151
170
|
} catch (error) {
|
|
152
171
|
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
172
|
+
// Fallback to CLAUDE.md
|
|
173
|
+
const claudeMemoryPath = path.join(workdir, "CLAUDE.md");
|
|
174
|
+
try {
|
|
175
|
+
const content = await fs.readFile(claudeMemoryPath, "utf-8");
|
|
176
|
+
logger.debug("Memory file read from CLAUDE.md fallback", {
|
|
177
|
+
memoryFilePath: claudeMemoryPath,
|
|
178
|
+
contentLength: content.length,
|
|
179
|
+
});
|
|
180
|
+
return content;
|
|
181
|
+
} catch (claudeError) {
|
|
182
|
+
if ((claudeError as NodeJS.ErrnoException).code === "ENOENT") {
|
|
183
|
+
logger.debug("Neither AGENTS.md nor CLAUDE.md found", {
|
|
184
|
+
memoryFilePath,
|
|
185
|
+
});
|
|
186
|
+
return "";
|
|
187
|
+
}
|
|
188
|
+
logger.error("Failed to read CLAUDE.md fallback", {
|
|
189
|
+
memoryFilePath: claudeMemoryPath,
|
|
190
|
+
error: claudeError,
|
|
191
|
+
});
|
|
192
|
+
return "";
|
|
193
|
+
}
|
|
157
194
|
}
|
|
158
195
|
logger.error("Failed to read memory file", { memoryFilePath, error });
|
|
159
196
|
return "";
|