wave-agent-sdk 0.17.5 → 0.17.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/agent.d.ts +18 -0
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +114 -1
  4. package/dist/managers/MemoryRuleManager.d.ts.map +1 -1
  5. package/dist/managers/MemoryRuleManager.js +30 -13
  6. package/dist/managers/aiManager.d.ts +1 -0
  7. package/dist/managers/aiManager.d.ts.map +1 -1
  8. package/dist/managers/aiManager.js +20 -62
  9. package/dist/managers/hookManager.d.ts.map +1 -1
  10. package/dist/managers/hookManager.js +3 -1
  11. package/dist/managers/lspManager.d.ts.map +1 -1
  12. package/dist/managers/lspManager.js +12 -4
  13. package/dist/managers/mcpManager.d.ts.map +1 -1
  14. package/dist/managers/mcpManager.js +13 -6
  15. package/dist/managers/messageManager.d.ts.map +1 -1
  16. package/dist/managers/messageManager.js +0 -4
  17. package/dist/managers/permissionManager.d.ts.map +1 -1
  18. package/dist/managers/permissionManager.js +7 -2
  19. package/dist/managers/planManager.d.ts +3 -0
  20. package/dist/managers/planManager.d.ts.map +1 -1
  21. package/dist/managers/planManager.js +9 -0
  22. package/dist/managers/skillManager.d.ts +3 -0
  23. package/dist/managers/skillManager.d.ts.map +1 -1
  24. package/dist/managers/skillManager.js +69 -54
  25. package/dist/managers/slashCommandManager.d.ts +0 -6
  26. package/dist/managers/slashCommandManager.d.ts.map +1 -1
  27. package/dist/managers/slashCommandManager.js +0 -170
  28. package/dist/managers/toolManager.d.ts.map +1 -1
  29. package/dist/managers/toolManager.js +2 -5
  30. package/dist/prompts/planModeReminders.d.ts +0 -1
  31. package/dist/prompts/planModeReminders.d.ts.map +1 -1
  32. package/dist/prompts/planModeReminders.js +3 -12
  33. package/dist/services/MarketplaceService.d.ts.map +1 -1
  34. package/dist/services/MarketplaceService.js +12 -4
  35. package/dist/services/memory.d.ts.map +1 -1
  36. package/dist/services/memory.js +39 -5
  37. package/dist/services/pluginLoader.d.ts.map +1 -1
  38. package/dist/services/pluginLoader.js +30 -7
  39. package/dist/types/skills.d.ts +1 -0
  40. package/dist/types/skills.d.ts.map +1 -1
  41. package/dist/utils/customCommands.d.ts.map +1 -1
  42. package/dist/utils/customCommands.js +11 -9
  43. package/dist/utils/skillParser.d.ts.map +1 -1
  44. package/dist/utils/skillParser.js +3 -1
  45. package/dist/utils/subagentParser.d.ts.map +1 -1
  46. package/dist/utils/subagentParser.js +18 -7
  47. package/package.json +1 -1
  48. package/src/agent.ts +146 -1
  49. package/src/managers/MemoryRuleManager.ts +29 -14
  50. package/src/managers/aiManager.ts +28 -78
  51. package/src/managers/hookManager.ts +6 -1
  52. package/src/managers/lspManager.ts +23 -5
  53. package/src/managers/mcpManager.ts +24 -7
  54. package/src/managers/messageManager.ts +0 -5
  55. package/src/managers/permissionManager.ts +8 -1
  56. package/src/managers/planManager.ts +11 -0
  57. package/src/managers/skillManager.ts +90 -57
  58. package/src/managers/slashCommandManager.ts +0 -215
  59. package/src/managers/toolManager.ts +2 -9
  60. package/src/prompts/planModeReminders.ts +3 -15
  61. package/src/services/MarketplaceService.ts +22 -4
  62. package/src/services/memory.ts +43 -6
  63. package/src/services/pluginLoader.ts +35 -7
  64. package/src/types/skills.ts +1 -0
  65. package/src/utils/customCommands.ts +17 -12
  66. package/src/utils/skillParser.ts +3 -1
  67. package/src/utils/subagentParser.ts +22 -8
@@ -117,6 +117,22 @@ export class MemoryService {
117
117
  userMemoryFile: USER_MEMORY_FILE,
118
118
  contentLength: content.length,
119
119
  });
120
+ // If the file is still the default empty template, fallback to ~/.claude/AGENTS.md
121
+ const defaultTemplate = "# User Memory\n\nThis is the user-level memory file, recording important information and context across projects.\n\n";
122
+ if (content === defaultTemplate) {
123
+ const claudeMemoryPath = path.join(homedir(), ".claude", "AGENTS.md");
124
+ try {
125
+ const claudeContent = await fs.readFile(claudeMemoryPath, "utf-8");
126
+ logger.debug("User memory content read from ~/.claude/AGENTS.md fallback", {
127
+ claudeMemoryPath,
128
+ contentLength: claudeContent.length,
129
+ });
130
+ return claudeContent;
131
+ }
132
+ catch {
133
+ // CLAUDE.md doesn't exist or can't be read, return the default template
134
+ }
135
+ }
120
136
  return content;
121
137
  }
122
138
  catch (error) {
@@ -126,7 +142,6 @@ export class MemoryService {
126
142
  }
127
143
  async readMemoryFile(workdir) {
128
144
  const memoryFilePath = path.join(workdir, "AGENTS.md");
129
- // Direct file access
130
145
  try {
131
146
  const content = await fs.readFile(memoryFilePath, "utf-8");
132
147
  logger.debug("Memory file read successfully via direct file access", {
@@ -137,10 +152,29 @@ export class MemoryService {
137
152
  }
138
153
  catch (error) {
139
154
  if (error.code === "ENOENT") {
140
- logger.debug("Memory file does not exist, returning empty content", {
141
- memoryFilePath,
142
- });
143
- return "";
155
+ // Fallback to CLAUDE.md
156
+ const claudeMemoryPath = path.join(workdir, "CLAUDE.md");
157
+ try {
158
+ const content = await fs.readFile(claudeMemoryPath, "utf-8");
159
+ logger.debug("Memory file read from CLAUDE.md fallback", {
160
+ memoryFilePath: claudeMemoryPath,
161
+ contentLength: content.length,
162
+ });
163
+ return content;
164
+ }
165
+ catch (claudeError) {
166
+ if (claudeError.code === "ENOENT") {
167
+ logger.debug("Neither AGENTS.md nor CLAUDE.md found", {
168
+ memoryFilePath,
169
+ });
170
+ return "";
171
+ }
172
+ logger.error("Failed to read CLAUDE.md fallback", {
173
+ memoryFilePath: claudeMemoryPath,
174
+ error: claudeError,
175
+ });
176
+ return "";
177
+ }
144
178
  }
145
179
  logger.error("Failed to read memory file", { memoryFilePath, error });
146
180
  return "";
@@ -1 +1 @@
1
- {"version":3,"file":"pluginLoader.d.ts","sourceRoot":"","sources":["../../src/services/pluginLoader.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,KAAK,EACL,SAAS,EACT,SAAS,EACT,wBAAwB,EACzB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,4BAA4B,CAAC;AAGpC,qBAAa,YAAY;IACvB;;;OAGG;WACU,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IA6CtE;;;OAGG;IACH,MAAM,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,kBAAkB,EAAE;IAK7D;;;OAGG;WACU,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAuC7D;;OAEG;WACU,aAAa,CACxB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAUjC;;OAEG;WACU,aAAa,CACxB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAWjC;;OAEG;WACU,eAAe,CAC1B,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,wBAAwB,GAAG,SAAS,CAAC;IAgBhD;;OAEG;WACU,UAAU,CACrB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,qBAAqB,EAAE,CAAC;IA2BnC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;CAgBhC"}
1
+ {"version":3,"file":"pluginLoader.d.ts","sourceRoot":"","sources":["../../src/services/pluginLoader.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,KAAK,EACL,SAAS,EACT,SAAS,EACT,wBAAwB,EACzB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,4BAA4B,CAAC;AAGpC,qBAAa,YAAY;IACvB;;;OAGG;WACU,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAyEtE;;;OAGG;IACH,MAAM,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,kBAAkB,EAAE;IAK7D;;;OAGG;WACU,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAuC7D;;OAEG;WACU,aAAa,CACxB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAUjC;;OAEG;WACU,aAAa,CACxB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAWjC;;OAEG;WACU,eAAe,CAC1B,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,wBAAwB,GAAG,SAAS,CAAC;IAgBhD;;OAEG;WACU,UAAU,CACrB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,qBAAqB,EAAE,CAAC;IA2BnC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;CAgBhC"}
@@ -11,10 +11,12 @@ export class PluginLoader {
11
11
  */
12
12
  static async loadManifest(pluginPath) {
13
13
  const dotWavePluginPath = path.join(pluginPath, ".wave-plugin");
14
- const manifestPath = path.join(dotWavePluginPath, "plugin.json");
15
- // T018: Ensure plugin.json is the only file in .wave-plugin/
14
+ // Check if .wave-plugin/ directory exists
15
+ let wavePluginDirExists = false;
16
16
  try {
17
17
  const entries = await fs.readdir(dotWavePluginPath);
18
+ wavePluginDirExists = true;
19
+ // T018: Ensure plugin.json is the only file in .wave-plugin/
18
20
  const misplaced = entries.filter((e) => e !== "plugin.json");
19
21
  if (misplaced.length > 0) {
20
22
  throw new Error(`Misplaced files/directories in .wave-plugin/: ${misplaced.join(", ")}. Only plugin.json should be in this directory.`);
@@ -23,12 +25,33 @@ export class PluginLoader {
23
25
  catch (error) {
24
26
  if (error instanceof Error &&
25
27
  error.code === "ENOENT") {
26
- throw new Error(`Plugin manifest directory not found at ${dotWavePluginPath}`);
28
+ // .wave-plugin/ doesn't exist, will try .claude-plugin/ fallback below
29
+ }
30
+ else {
31
+ throw error;
32
+ }
33
+ }
34
+ if (wavePluginDirExists) {
35
+ const manifestPath = path.join(dotWavePluginPath, "plugin.json");
36
+ try {
37
+ const content = await fs.readFile(manifestPath, "utf-8");
38
+ const manifest = JSON.parse(content);
39
+ this.validateManifest(manifest);
40
+ return manifest;
41
+ }
42
+ catch (error) {
43
+ if (error instanceof Error &&
44
+ error.code === "ENOENT") {
45
+ throw new Error(`Plugin manifest not found at ${manifestPath}`);
46
+ }
47
+ throw new Error(`Failed to load plugin manifest at ${manifestPath}: ${error instanceof Error ? error.message : String(error)}`);
27
48
  }
28
- throw error;
29
49
  }
50
+ // Fallback to .claude-plugin/
51
+ const dotClaudePluginPath = path.join(pluginPath, ".claude-plugin");
52
+ const claudeManifestPath = path.join(dotClaudePluginPath, "plugin.json");
30
53
  try {
31
- const content = await fs.readFile(manifestPath, "utf-8");
54
+ const content = await fs.readFile(claudeManifestPath, "utf-8");
32
55
  const manifest = JSON.parse(content);
33
56
  this.validateManifest(manifest);
34
57
  return manifest;
@@ -36,9 +59,9 @@ export class PluginLoader {
36
59
  catch (error) {
37
60
  if (error instanceof Error &&
38
61
  error.code === "ENOENT") {
39
- throw new Error(`Plugin manifest not found at ${manifestPath}`);
62
+ throw new Error(`Plugin manifest not found at ${dotWavePluginPath} or ${dotClaudePluginPath}`);
40
63
  }
41
- throw new Error(`Failed to load plugin manifest at ${manifestPath}: ${error instanceof Error ? error.message : String(error)}`);
64
+ throw new Error(`Failed to load plugin manifest at ${claudeManifestPath}: ${error instanceof Error ? error.message : String(error)}`);
42
65
  }
43
66
  }
44
67
  /**
@@ -63,6 +63,7 @@ export interface SkillToolArgs {
63
63
  }
64
64
  export interface SkillManagerOptions {
65
65
  personalSkillsPath?: string;
66
+ personalClaudeSkillsPath?: string;
66
67
  scanTimeout?: number;
67
68
  workdir?: string;
68
69
  watch?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/types/skills.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/types/skills.ts"],"names":[],"mappings":"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"}
@@ -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,CAkB7E"}
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 projectCommands = scanCommandsDirectory(getProjectCommandsDir(workdir));
64
- const userCommands = scanCommandsDirectory(getUserCommandsDir());
65
- // Project commands take precedence over user commands with the same name
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
- // Add user commands first
68
- for (const command of userCommands) {
69
- commandMap.set(command.id, command);
70
- }
71
- // Add project commands (will overwrite user commands with same name)
72
- for (const command of projectCommands) {
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,CAgGjB;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"}
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,CAsBlC;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAGvC"}
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 projectDir = join(workdir, ".wave", "agents");
164
- const userDir = join(process.env.HOME || "~", ".wave", "agents");
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 projectConfigs = scanSubagentDirectory(projectDir, "project");
169
- const userConfigs = scanSubagentDirectory(userDir, "user");
170
- // Merge configurations, with project configs taking highest precedence
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
- // Process in reverse priority order (built-in first, then user, then project)
173
- for (const config of [...builtinConfigs, ...userConfigs, ...projectConfigs]) {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wave-agent-sdk",
3
- "version": "0.17.5",
3
+ "version": "0.17.7",
4
4
  "description": "SDK for building AI-powered development tools and agents",
5
5
  "keywords": [
6
6
  "ai",
package/src/agent.ts CHANGED
@@ -685,7 +685,152 @@ export class Agent {
685
685
  }
686
686
 
687
687
  public async clearMessages(): Promise<void> {
688
- await this.slashCommandManager.executeCommand("clear");
688
+ this.aiManager.abortAIMessage();
689
+
690
+ // Clear any active goal
691
+ this.goalManager.clearGoal();
692
+
693
+ // Capture old session info before clearing
694
+ const oldSessionId = this.messageManager.getSessionId();
695
+ const transcriptPath = this.messageManager.getTranscriptPath();
696
+
697
+ // Run SessionEnd hooks (cleanup before clear)
698
+ try {
699
+ await this.hookManager.executeSessionEndHooks(
700
+ "clear",
701
+ oldSessionId,
702
+ transcriptPath,
703
+ );
704
+ } catch (error) {
705
+ this.logger?.warn(
706
+ `SessionEnd hooks on clear failed: ${(error as Error).message}`,
707
+ );
708
+ }
709
+
710
+ // Clear messages and generate new session
711
+ this.messageManager.clearMessages();
712
+ const memoryService =
713
+ this.container.get<import("./services/memory.js").MemoryService>(
714
+ "MemoryService",
715
+ );
716
+ memoryService?.clearCache();
717
+ await this.taskManager.syncWithSession();
718
+
719
+ // Run SessionStart hooks (restore context for new session)
720
+ try {
721
+ const newSessionId = this.messageManager.getSessionId();
722
+ const sessionStartResult =
723
+ await this.hookManager.executeSessionStartHooks(
724
+ "clear",
725
+ newSessionId,
726
+ this.messageManager.getTranscriptPath(),
727
+ );
728
+
729
+ // Inject additionalContext as a meta user message
730
+ if (sessionStartResult.additionalContext) {
731
+ this.messageManager.addUserMessage({
732
+ content: `<system-reminder>\nSessionStart hook additional context: ${sessionStartResult.additionalContext}\n</system-reminder>`,
733
+ isMeta: true,
734
+ });
735
+ }
736
+
737
+ // Inject initialUserMessage as a meta user message
738
+ if (sessionStartResult.initialUserMessage) {
739
+ this.messageManager.addUserMessage({
740
+ content: sessionStartResult.initialUserMessage,
741
+ isMeta: true,
742
+ });
743
+ }
744
+ } catch (error) {
745
+ this.logger?.warn(
746
+ `SessionStart hooks on clear failed: ${(error as Error).message}`,
747
+ );
748
+ }
749
+
750
+ await this.messageManager.saveSession();
751
+ }
752
+
753
+ /**
754
+ * Compact conversation history to reduce context usage
755
+ * @param customInstructions - Optional custom instructions for compaction
756
+ */
757
+ public async compact(customInstructions?: string): Promise<void> {
758
+ this.aiManager.abortAIMessage();
759
+
760
+ await this.aiManager.compactConversation({
761
+ customInstructions,
762
+ });
763
+
764
+ await this.messageManager.saveSession();
765
+ }
766
+
767
+ /**
768
+ * Set an autonomous goal for the session
769
+ * @param condition - The goal condition to achieve
770
+ */
771
+ public async setGoal(condition: string): Promise<void> {
772
+ // Check plan mode
773
+ if (this.getPermissionMode() === "plan") {
774
+ this.messageManager.addUserMessage({
775
+ content:
776
+ "<system-reminder>Cannot set a goal in plan mode. Exit plan mode first.</system-reminder>",
777
+ isMeta: true,
778
+ });
779
+ return;
780
+ }
781
+
782
+ this.goalManager.setGoal(condition);
783
+ this.messageManager.addUserMessage({
784
+ content: `<system-reminder>Goal set: ${condition}. The agent will work autonomously until this goal is achieved.</system-reminder>`,
785
+ isMeta: true,
786
+ });
787
+ // Add the goal as a user directive to start working
788
+ this.messageManager.addUserMessage({
789
+ content: condition,
790
+ });
791
+ this.aiManager.sendAIMessage();
792
+
793
+ await this.messageManager.saveSession();
794
+ }
795
+
796
+ /**
797
+ * Clear the current autonomous goal
798
+ */
799
+ public async clearGoal(): Promise<void> {
800
+ if (this.goalManager.isGoalActive()) {
801
+ this.goalManager.clearGoal();
802
+ this.messageManager.addUserMessage({
803
+ content: "<system-reminder>Goal cleared.</system-reminder>",
804
+ isMeta: true,
805
+ });
806
+ } else {
807
+ this.messageManager.addUserMessage({
808
+ content: "<system-reminder>No active goal to clear.</system-reminder>",
809
+ isMeta: true,
810
+ });
811
+ }
812
+
813
+ await this.messageManager.saveSession();
814
+ }
815
+
816
+ /**
817
+ * Show the current goal status
818
+ */
819
+ public async showGoalStatus(): Promise<void> {
820
+ if (this.goalManager.isGoalActive()) {
821
+ this.messageManager.addUserMessage({
822
+ content: `<system-reminder>${this.goalManager.getStatusString()}</system-reminder>`,
823
+ isMeta: true,
824
+ });
825
+ } else {
826
+ this.messageManager.addUserMessage({
827
+ content:
828
+ "<system-reminder>No active goal. Use /goal <condition> to set one.</system-reminder>",
829
+ isMeta: true,
830
+ });
831
+ }
832
+
833
+ await this.messageManager.saveSession();
689
834
  }
690
835
 
691
836
  /** Unified interrupt method, interrupts both AI messages and command execution */
@@ -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 projectRulesDir = path.join(this.workdir, ".wave", "rules");
43
- const userRulesDir = path.join(os.homedir(), ".wave", "rules");
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: ${userRulesDir}`);
47
- logger.debug(` Project rules directory: ${projectRulesDir}`);
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
- // Discover user rules first, then project rules so project rules can override if needed
52
- // (though IDs are based on file path, so they shouldn't collide unless same path)
53
- await this.scanDirectory(userRulesDir, "user", newRules);
54
- await this.scanDirectory(projectRulesDir, "project", newRules);
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
- // Use relative path from rules root as ID to allow project rules to override user rules
128
- const rulesRoot =
129
- source === "project"
130
- ? path.join(this.workdir, ".wave", "rules")
131
- : path.join(os.homedir(), ".wave", "rules");
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;
@@ -26,7 +26,6 @@ import type { SkillManager } from "./skillManager.js";
26
26
  import { buildSystemPrompt } from "../prompts/index.js";
27
27
  import {
28
28
  buildPlanModeReminder,
29
- buildPlanModeSparseReminder,
30
29
  buildPlanModeReEntryReminder,
31
30
  buildExitedPlanModeReminder,
32
31
  } from "../prompts/planModeReminders.js";
@@ -138,6 +137,14 @@ export class AIManager {
138
137
  return this.container.get<PermissionManager>("PermissionManager");
139
138
  }
140
139
 
140
+ private get planManager():
141
+ | import("./planManager.js").PlanManager
142
+ | undefined {
143
+ return this.container.get<import("./planManager.js").PlanManager>(
144
+ "PlanManager",
145
+ );
146
+ }
147
+
141
148
  private get configurationService(): ConfigurationService {
142
149
  return this.container.get<ConfigurationService>("ConfigurationService")!;
143
150
  }
@@ -253,7 +260,6 @@ export class AIManager {
253
260
  this.permissionManager.setNeedsPlanModeExitAttachment(false);
254
261
  }
255
262
 
256
- // Handle plan mode reminders
257
263
  if (currentMode !== "plan") return messages;
258
264
 
259
265
  const planFilePath = this.permissionManager.getPlanFilePath();
@@ -261,83 +267,27 @@ export class AIManager {
261
267
 
262
268
  const planExists = existsSync(planFilePath);
263
269
 
264
- // Check for re-entry: flag is set AND plan file exists
265
- if (this.permissionManager.hasExitedPlanModeInSession() && planExists) {
266
- messages.push({
267
- role: "user",
268
- content: buildPlanModeReEntryReminder(planFilePath),
269
- });
270
- this.permissionManager.setHasExitedPlanMode(false); // One-time
271
- }
272
-
273
- // Count plan_mode system-reminders in existing messages to determine full vs sparse
274
- // and count human turns since last reminder for throttling
275
- const recentApiMessages = this.messageManager.getMessages();
276
- let planModeReminderCount = 0;
277
- let humanTurnsSinceLastReminder = 0;
278
- let foundLastReminder = false;
279
-
280
- for (let i = recentApiMessages.length - 1; i >= 0; i--) {
281
- const msg = recentApiMessages[i];
282
- if (msg.role === "user" && !msg.isMeta) {
283
- // Count human turns (non-meta user messages without tool results)
284
- const hasToolResult = msg.blocks?.some(
285
- (b: { type: string }) => b.type === "tool",
286
- );
287
- if (!hasToolResult) {
288
- if (!foundLastReminder) {
289
- humanTurnsSinceLastReminder++;
290
- }
291
- }
292
- }
293
- // Check for existing plan mode system-reminders
294
- if (msg.role === "user" && msg.isMeta) {
295
- const textContent = msg.blocks
296
- ?.filter((b) => b.type === "text")
297
- .map((b) => ("content" in b ? b.content : ""))
298
- .join("");
299
- if (
300
- textContent?.includes("Plan mode is active") ||
301
- textContent?.includes("Plan mode still active")
302
- ) {
303
- planModeReminderCount++;
304
- if (!foundLastReminder) {
305
- foundLastReminder = true;
306
- }
307
- }
270
+ // One-time plan entry reminder
271
+ const planMgr = this.planManager;
272
+ if (planMgr?.isPlanEntryReminderPending()) {
273
+ if (this.permissionManager.hasExitedPlanModeInSession() && planExists) {
274
+ // Re-entry: use small reminder
275
+ messages.push({
276
+ role: "user",
277
+ content: buildPlanModeReEntryReminder(planFilePath),
278
+ });
279
+ } else {
280
+ // First entry: use full reminder
281
+ messages.push({
282
+ role: "user",
283
+ content: buildPlanModeReminder(
284
+ planFilePath,
285
+ planExists,
286
+ !!this.subagentType,
287
+ ),
288
+ });
308
289
  }
309
- }
310
-
311
- // Throttle: only inject every 5 human turns (but always inject on first turn)
312
- const TURNS_BETWEEN_REMINDERS = 5;
313
- const FULL_REMINDER_EVERY_N = 5;
314
-
315
- if (
316
- foundLastReminder &&
317
- humanTurnsSinceLastReminder < TURNS_BETWEEN_REMINDERS
318
- ) {
319
- return messages; // Throttled — skip reminder
320
- }
321
-
322
- // Determine full vs sparse
323
- // Every 5th reminder is full; rest are sparse
324
- const reminderNumber = planModeReminderCount + 1;
325
- const isFull = reminderNumber % FULL_REMINDER_EVERY_N === 1;
326
-
327
- if (isFull) {
328
- messages.push({
329
- role: "user",
330
- content: buildPlanModeReminder(
331
- planFilePath,
332
- planExists,
333
- !!this.subagentType,
334
- ),
335
- });
336
- } else {
337
- messages.push({
338
- role: "user",
339
- content: buildPlanModeSparseReminder(planFilePath),
340
- });
290
+ planMgr.consumePlanEntryReminder();
341
291
  }
342
292
 
343
293
  return messages;
@@ -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 (same pattern as Claude Code)
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) {