wave-agent-sdk 0.7.1 → 0.8.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.
Files changed (168) hide show
  1. package/dist/agent.d.ts +9 -79
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +85 -302
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +2 -1
  7. package/dist/managers/aiManager.d.ts.map +1 -1
  8. package/dist/managers/aiManager.js +20 -13
  9. package/dist/managers/backgroundTaskManager.d.ts +1 -1
  10. package/dist/managers/backgroundTaskManager.d.ts.map +1 -1
  11. package/dist/managers/backgroundTaskManager.js +1 -1
  12. package/dist/managers/{bashManager.d.ts → bangManager.d.ts} +4 -4
  13. package/dist/managers/{bashManager.d.ts.map → bangManager.d.ts.map} +1 -1
  14. package/dist/managers/{bashManager.js → bangManager.js} +5 -6
  15. package/dist/managers/hookManager.d.ts.map +1 -1
  16. package/dist/managers/hookManager.js +12 -3
  17. package/dist/managers/messageManager.d.ts +18 -6
  18. package/dist/managers/messageManager.d.ts.map +1 -1
  19. package/dist/managers/messageManager.js +42 -20
  20. package/dist/managers/permissionManager.d.ts +22 -1
  21. package/dist/managers/permissionManager.d.ts.map +1 -1
  22. package/dist/managers/permissionManager.js +106 -85
  23. package/dist/managers/planManager.d.ts +6 -0
  24. package/dist/managers/planManager.d.ts.map +1 -1
  25. package/dist/managers/planManager.js +21 -0
  26. package/dist/managers/skillManager.d.ts +7 -2
  27. package/dist/managers/skillManager.d.ts.map +1 -1
  28. package/dist/managers/skillManager.js +30 -10
  29. package/dist/managers/slashCommandManager.d.ts +7 -0
  30. package/dist/managers/slashCommandManager.d.ts.map +1 -1
  31. package/dist/managers/slashCommandManager.js +57 -45
  32. package/dist/managers/subagentManager.d.ts +4 -0
  33. package/dist/managers/subagentManager.d.ts.map +1 -1
  34. package/dist/managers/subagentManager.js +47 -13
  35. package/dist/managers/toolManager.d.ts +7 -1
  36. package/dist/managers/toolManager.d.ts.map +1 -1
  37. package/dist/managers/toolManager.js +15 -2
  38. package/dist/prompts/index.d.ts +0 -4
  39. package/dist/prompts/index.d.ts.map +1 -1
  40. package/dist/prompts/index.js +0 -9
  41. package/dist/services/aiService.d.ts.map +1 -1
  42. package/dist/services/aiService.js +6 -6
  43. package/dist/services/configurationService.d.ts +2 -2
  44. package/dist/services/configurationService.d.ts.map +1 -1
  45. package/dist/services/configurationService.js +4 -4
  46. package/dist/services/hook.d.ts.map +1 -1
  47. package/dist/services/hook.js +6 -0
  48. package/dist/services/initializationService.d.ts +44 -0
  49. package/dist/services/initializationService.d.ts.map +1 -0
  50. package/dist/services/initializationService.js +170 -0
  51. package/dist/services/interactionService.d.ts +29 -0
  52. package/dist/services/interactionService.d.ts.map +1 -0
  53. package/dist/services/interactionService.js +97 -0
  54. package/dist/services/session.js +1 -1
  55. package/dist/services/taskManager.d.ts +5 -0
  56. package/dist/services/taskManager.d.ts.map +1 -1
  57. package/dist/services/taskManager.js +16 -2
  58. package/dist/tools/bashTool.d.ts.map +1 -1
  59. package/dist/tools/bashTool.js +7 -18
  60. package/dist/tools/editTool.js +1 -1
  61. package/dist/tools/exitPlanMode.js +1 -1
  62. package/dist/tools/lspTool.d.ts +2 -0
  63. package/dist/tools/lspTool.d.ts.map +1 -1
  64. package/dist/tools/lspTool.js +144 -52
  65. package/dist/tools/skillTool.d.ts.map +1 -1
  66. package/dist/tools/skillTool.js +97 -2
  67. package/dist/tools/taskManagementTools.d.ts.map +1 -1
  68. package/dist/tools/taskManagementTools.js +23 -2
  69. package/dist/tools/taskTool.d.ts.map +1 -1
  70. package/dist/tools/taskTool.js +9 -15
  71. package/dist/tools/types.d.ts +1 -2
  72. package/dist/tools/types.d.ts.map +1 -1
  73. package/dist/tools/writeTool.js +1 -1
  74. package/dist/types/agent.d.ts +64 -0
  75. package/dist/types/agent.d.ts.map +1 -0
  76. package/dist/types/agent.js +1 -0
  77. package/dist/types/commands.d.ts +0 -4
  78. package/dist/types/commands.d.ts.map +1 -1
  79. package/dist/types/config.d.ts +1 -1
  80. package/dist/types/config.d.ts.map +1 -1
  81. package/dist/types/hooks.d.ts +3 -1
  82. package/dist/types/hooks.d.ts.map +1 -1
  83. package/dist/types/hooks.js +1 -0
  84. package/dist/types/index.d.ts +1 -0
  85. package/dist/types/index.d.ts.map +1 -1
  86. package/dist/types/index.js +1 -0
  87. package/dist/types/messaging.d.ts +3 -3
  88. package/dist/types/messaging.d.ts.map +1 -1
  89. package/dist/types/skills.d.ts +13 -0
  90. package/dist/types/skills.d.ts.map +1 -1
  91. package/dist/utils/commandPathResolver.d.ts +3 -36
  92. package/dist/utils/commandPathResolver.d.ts.map +1 -1
  93. package/dist/utils/commandPathResolver.js +16 -93
  94. package/dist/utils/configValidator.d.ts +2 -2
  95. package/dist/utils/configValidator.d.ts.map +1 -1
  96. package/dist/utils/configValidator.js +4 -6
  97. package/dist/utils/containerSetup.d.ts +3 -4
  98. package/dist/utils/containerSetup.d.ts.map +1 -1
  99. package/dist/utils/containerSetup.js +14 -9
  100. package/dist/utils/customCommands.d.ts +2 -3
  101. package/dist/utils/customCommands.d.ts.map +1 -1
  102. package/dist/utils/customCommands.js +20 -60
  103. package/dist/utils/gitUtils.d.ts +25 -0
  104. package/dist/utils/gitUtils.d.ts.map +1 -1
  105. package/dist/utils/gitUtils.js +75 -0
  106. package/dist/utils/markdownParser.d.ts +4 -0
  107. package/dist/utils/markdownParser.d.ts.map +1 -1
  108. package/dist/utils/markdownParser.js +33 -0
  109. package/dist/utils/messageOperations.d.ts +16 -7
  110. package/dist/utils/messageOperations.d.ts.map +1 -1
  111. package/dist/utils/messageOperations.js +45 -20
  112. package/dist/utils/nameGenerator.d.ts +1 -1
  113. package/dist/utils/nameGenerator.d.ts.map +1 -1
  114. package/dist/utils/nameGenerator.js +10 -6
  115. package/dist/utils/skillParser.d.ts.map +1 -1
  116. package/dist/utils/skillParser.js +48 -0
  117. package/package.json +1 -1
  118. package/src/agent.ts +103 -458
  119. package/src/index.ts +2 -2
  120. package/src/managers/aiManager.ts +23 -17
  121. package/src/managers/backgroundTaskManager.ts +2 -2
  122. package/src/managers/{bashManager.ts → bangManager.ts} +11 -8
  123. package/src/managers/hookManager.ts +13 -3
  124. package/src/managers/messageManager.ts +55 -26
  125. package/src/managers/permissionManager.ts +121 -98
  126. package/src/managers/planManager.ts +26 -0
  127. package/src/managers/skillManager.ts +51 -14
  128. package/src/managers/slashCommandManager.ts +75 -55
  129. package/src/managers/subagentManager.ts +57 -13
  130. package/src/managers/toolManager.ts +22 -2
  131. package/src/prompts/index.ts +0 -15
  132. package/src/services/aiService.ts +9 -12
  133. package/src/services/configurationService.ts +4 -4
  134. package/src/services/hook.ts +7 -0
  135. package/src/services/initializationService.ts +291 -0
  136. package/src/services/interactionService.ts +171 -0
  137. package/src/services/session.ts +1 -1
  138. package/src/services/taskManager.ts +18 -2
  139. package/src/tools/bashTool.ts +8 -18
  140. package/src/tools/editTool.ts +1 -1
  141. package/src/tools/exitPlanMode.ts +1 -1
  142. package/src/tools/lsTool.ts +1 -1
  143. package/src/tools/lspTool.ts +184 -52
  144. package/src/tools/skillTool.ts +127 -2
  145. package/src/tools/taskManagementTools.ts +32 -2
  146. package/src/tools/taskTool.ts +13 -15
  147. package/src/tools/types.ts +1 -2
  148. package/src/tools/writeTool.ts +1 -1
  149. package/src/types/agent.ts +83 -0
  150. package/src/types/commands.ts +0 -6
  151. package/src/types/config.ts +1 -1
  152. package/src/types/hooks.ts +5 -1
  153. package/src/types/index.ts +1 -0
  154. package/src/types/messaging.ts +3 -3
  155. package/src/types/skills.ts +13 -0
  156. package/src/utils/commandPathResolver.ts +14 -117
  157. package/src/utils/configValidator.ts +5 -9
  158. package/src/utils/containerSetup.ts +17 -14
  159. package/src/utils/customCommands.ts +20 -83
  160. package/src/utils/gitUtils.ts +75 -0
  161. package/src/utils/markdownParser.ts +47 -0
  162. package/src/utils/messageOperations.ts +58 -28
  163. package/src/utils/nameGenerator.ts +10 -6
  164. package/src/utils/skillParser.ts +52 -0
  165. package/dist/managers/backgroundBashManager.d.ts +0 -27
  166. package/dist/managers/backgroundBashManager.d.ts.map +0 -1
  167. package/dist/managers/backgroundBashManager.js +0 -169
  168. package/src/managers/backgroundBashManager.ts +0 -206
@@ -123,7 +123,7 @@ Usage:
123
123
  return {
124
124
  success: false,
125
125
  content: "",
126
- error: `Write operation denied, reason: ${permissionResult.message || "No reason provided"}`,
126
+ error: `Write operation denied by user, reason: ${permissionResult.message || "No reason provided"}`,
127
127
  };
128
128
  }
129
129
  } catch {
@@ -0,0 +1,83 @@
1
+ import type { ClientOptions } from "openai";
2
+ import type {
3
+ Message,
4
+ Logger,
5
+ PermissionMode,
6
+ PermissionCallback,
7
+ ILspManager,
8
+ PluginConfig,
9
+ BackgroundTask,
10
+ } from "./index.js";
11
+ import type { MessageManagerCallbacks } from "../managers/messageManager.js";
12
+ import type { BackgroundTaskManagerCallbacks } from "../managers/backgroundTaskManager.js";
13
+ import type { McpManagerCallbacks } from "../managers/mcpManager.js";
14
+ import type { SubagentManagerCallbacks } from "../managers/subagentManager.js";
15
+
16
+ /**
17
+ * Configuration options for Agent instances
18
+ *
19
+ * IMPORTANT: This interface is used by both Agent constructor and Agent.create()
20
+ * Any changes to this interface must be compatible with both methods.
21
+ */
22
+ export interface AgentOptions {
23
+ // Optional configuration with environment fallbacks
24
+ apiKey?: string;
25
+ baseURL?: string;
26
+ defaultHeaders?: Record<string, string>;
27
+ fetchOptions?: ClientOptions["fetchOptions"];
28
+ fetch?: ClientOptions["fetch"];
29
+ model?: string;
30
+ fastModel?: string;
31
+ maxInputTokens?: number;
32
+ maxTokens?: number;
33
+ /** Preferred language for agent communication */
34
+ language?: string;
35
+
36
+ // Existing options (preserved)
37
+ callbacks?: AgentCallbacks;
38
+ restoreSessionId?: string;
39
+ continueLastSession?: boolean;
40
+ logger?: Logger;
41
+ /**Add optional initial messages parameter for testing convenience */
42
+ messages?: Message[];
43
+ /**Working directory - if not specified, use process.cwd() */
44
+ workdir?: string;
45
+ /**Optional custom system prompt - if provided, replaces default system prompt */
46
+ systemPrompt?: string;
47
+ /**Permission mode - defaults to "default" */
48
+ permissionMode?: PermissionMode;
49
+ /**Custom permission callback */
50
+ canUseTool?: PermissionCallback;
51
+ /**Whether to use streaming mode for AI responses - defaults to true */
52
+ stream?: boolean;
53
+ /**Optional custom LSP manager - if not provided, a standalone one will be created */
54
+ lspManager?: ILspManager;
55
+ /**Optional local plugins to load */
56
+ plugins?: PluginConfig[];
57
+ /**
58
+ * Optional list of tool names to enable.
59
+ * - undefined: Enable all built-in tools and plugins (default).
60
+ * - []: Disable all tools.
61
+ * - string[]: Enable only the tools with the specified names.
62
+ */
63
+ tools?: string[];
64
+ /**Optional worktree name */
65
+ worktreeName?: string;
66
+ /**Whether this is a newly created worktree */
67
+ isNewWorktree?: boolean;
68
+ }
69
+
70
+ export interface AgentCallbacks
71
+ extends MessageManagerCallbacks,
72
+ BackgroundTaskManagerCallbacks,
73
+ McpManagerCallbacks,
74
+ SubagentManagerCallbacks {
75
+ onBackgroundTasksChange?: (tasks: BackgroundTask[]) => void;
76
+ onTasksChange?: (tasks: import("./tasks.js").Task[]) => void;
77
+ onPermissionModeChange?: (mode: PermissionMode) => void;
78
+ onSubagentLatestTotalTokensChange?: (
79
+ subagentId: string,
80
+ tokens: number,
81
+ ) => void;
82
+ onBackgroundCurrentTask?: () => void;
83
+ }
@@ -24,12 +24,6 @@ export interface CustomSlashCommand {
24
24
  content: string;
25
25
  config?: CustomSlashCommandConfig;
26
26
 
27
- // Nested command support
28
- namespace?: string; // Parent directory for nested commands (e.g., "openspec")
29
- isNested: boolean; // Whether command is in a subdirectory
30
- depth: number; // 0 = root, 1 = nested
31
- segments: string[]; // Path components for ID generation (e.g., ["openspec", "apply"])
32
-
33
27
  // Plugin support
34
28
  pluginPath?: string; // Absolute path to the plugin root directory (only set for plugin commands)
35
29
  }
@@ -15,7 +15,7 @@ export interface GatewayConfig {
15
15
  }
16
16
 
17
17
  export interface ModelConfig {
18
- agentModel: string;
18
+ model: string;
19
19
  fastModel: string;
20
20
  maxTokens?: number;
21
21
  permissionMode?: PermissionMode;
@@ -19,7 +19,8 @@ export type HookEvent =
19
19
  | "UserPromptSubmit"
20
20
  | "Stop"
21
21
  | "SubagentStop"
22
- | "Notification";
22
+ | "Notification"
23
+ | "WorktreeCreate";
23
24
 
24
25
  // Individual hook command configuration
25
26
  export interface HookCommand {
@@ -98,6 +99,7 @@ export function isValidHookEvent(event: string): event is HookEvent {
98
99
  "Stop",
99
100
  "SubagentStop",
100
101
  "Notification",
102
+ "WorktreeCreate",
101
103
  ].includes(event);
102
104
  }
103
105
 
@@ -146,6 +148,7 @@ export interface HookJsonInput {
146
148
  subagent_type?: string; // Present when hook is executed by a subagent
147
149
  message?: string; // Present for Notification events
148
150
  notification_type?: string; // Present for Notification events
151
+ name?: string; // Present for WorktreeCreate events
149
152
  }
150
153
 
151
154
  // Extended context interface for passing additional data to hook executor
@@ -160,6 +163,7 @@ export interface ExtendedHookExecutionContext extends HookExecutionContext {
160
163
  subagentType?: string; // Subagent type when hook is executed by a subagent
161
164
  message?: string; // Notification message (Notification only)
162
165
  notificationType?: string; // Notification type (Notification only)
166
+ worktreeName?: string; // Worktree name (WorktreeCreate only)
163
167
  }
164
168
 
165
169
  // Environment variables injected into hook processes
@@ -34,3 +34,4 @@ export * from "./marketplace.js";
34
34
  export * from "./memoryRule.js";
35
35
  export * from "./history.js";
36
36
  export * from "./tasks.js";
37
+ export * from "./agent.js";
@@ -23,7 +23,7 @@ export type MessageBlock =
23
23
  | ErrorBlock
24
24
  | ToolBlock
25
25
  | ImageBlock
26
- | CommandOutputBlock
26
+ | BangBlock
27
27
  | CompressBlock
28
28
  | ReasoningBlock
29
29
  | FileHistoryBlock;
@@ -73,8 +73,8 @@ export interface ImageBlock {
73
73
  imageUrls?: string[];
74
74
  }
75
75
 
76
- export interface CommandOutputBlock {
77
- type: "command_output";
76
+ export interface BangBlock {
77
+ type: "bang";
78
78
  command: string;
79
79
  output: string;
80
80
  isRunning: boolean;
@@ -8,6 +8,12 @@ export interface SkillMetadata {
8
8
  description: string;
9
9
  type: "personal" | "project";
10
10
  skillPath: string;
11
+ allowedTools?: string[];
12
+ context?: "fork";
13
+ agent?: string;
14
+ model?: string;
15
+ disableModelInvocation?: boolean;
16
+ userInvocable?: boolean;
11
17
  }
12
18
 
13
19
  export interface Skill extends SkillMetadata {
@@ -20,6 +26,12 @@ export interface Skill extends SkillMetadata {
20
26
  export interface SkillFrontmatter {
21
27
  name: string;
22
28
  description: string;
29
+ "allowed-tools"?: string | string[];
30
+ context?: "fork";
31
+ agent?: string;
32
+ model?: string;
33
+ "disable-model-invocation"?: boolean | string;
34
+ "user-invocable"?: boolean | string;
23
35
  [key: string]: unknown;
24
36
  }
25
37
 
@@ -53,6 +65,7 @@ export interface SkillInvocationContext {
53
65
 
54
66
  export interface SkillToolArgs {
55
67
  skill_name: string;
68
+ args?: string;
56
69
  }
57
70
 
58
71
  export interface SkillManagerOptions {
@@ -1,23 +1,15 @@
1
1
  import { relative, basename } from "path";
2
2
 
3
3
  /**
4
- * Command path resolver utilities for nested command discovery
5
- * Handles conversion between file paths and command IDs with colon syntax
4
+ * Command path resolver utilities for command discovery
5
+ * Handles conversion between file paths and command IDs
6
6
  */
7
7
 
8
- export interface CommandIdParts {
9
- namespace?: string; // e.g., "openspec" for "openspec:apply"
10
- commandName: string; // e.g., "apply" for "openspec:apply"
11
- isNested: boolean; // true if command has namespace
12
- depth: number; // 0 for root, 1 for nested
13
- segments: string[]; // Path components array
14
- }
15
-
16
8
  /**
17
9
  * Generate command ID from file path
18
10
  * @param filePath - Absolute path to markdown file
19
11
  * @param rootDir - Root commands directory path
20
- * @returns Command identifier string (e.g., "openspec:apply")
12
+ * @returns Command identifier string
21
13
  * @throws Error on invalid path structure
22
14
  */
23
15
  export function generateCommandId(filePath: string, rootDir: string): string {
@@ -42,84 +34,28 @@ export function generateCommandId(filePath: string, rootDir: string): string {
42
34
  throw new Error(`Command files must have .md extension`);
43
35
  }
44
36
 
45
- segments[segments.length - 1] = basename(lastSegment, ".md");
37
+ const commandName = basename(lastSegment, ".md");
46
38
 
47
39
  // Handle empty filename after removing extension
48
- if (segments[segments.length - 1] === "") {
40
+ if (commandName === "") {
49
41
  throw new Error("Command filename cannot be empty");
50
42
  }
51
43
 
52
- // Validate depth (max 1 level of nesting)
53
- if (segments.length > 2) {
44
+ // Validate depth (no nesting allowed)
45
+ if (segments.length > 1) {
54
46
  throw new Error(
55
- `Command nesting too deep: ${relativePath}. Maximum depth is 1 level.`,
47
+ `Command nesting not supported: ${relativePath}. Commands must be in the root directory.`,
56
48
  );
57
49
  }
58
50
 
59
- // Validate segments
60
- for (const segment of segments) {
61
- if (!validateSegment(segment)) {
62
- throw new Error(
63
- `Invalid command path segment: "${segment}" in ${relativePath}. Must match pattern /^[a-zA-Z][a-zA-Z0-9_.-]*$/`,
64
- );
65
- }
66
- }
67
-
68
- // Generate command ID
69
- if (segments.length === 1) {
70
- return segments[0]; // Flat command
71
- } else {
72
- return segments.join(":"); // Nested command with colon syntax
73
- }
74
- }
75
-
76
- /**
77
- * Parse command ID into components
78
- * @param commandId - Command identifier (e.g., "openspec:apply")
79
- * @returns Object with namespace and command name
80
- * @throws Error on malformed command ID
81
- */
82
- export function parseCommandId(commandId: string): CommandIdParts {
83
- // Handle null/undefined inputs
84
- if (commandId == null) {
85
- throw new Error("Command ID cannot be null or undefined");
86
- }
87
-
88
- if (commandId === "") {
89
- throw new Error("Command ID cannot be empty");
90
- }
91
-
92
- if (!validateCommandId(commandId)) {
51
+ // Validate command name
52
+ if (!validateSegment(commandName)) {
93
53
  throw new Error(
94
- `Invalid command ID format: "${commandId}". Must match pattern /^[a-zA-Z0-9_-]+(?::[a-zA-Z0-9_-]+)?$/`,
54
+ `Invalid command name: "${commandName}" in ${relativePath}. Must match pattern /^[a-zA-Z][a-zA-Z0-9_.-]*$/`,
95
55
  );
96
56
  }
97
57
 
98
- const parts = commandId.split(":");
99
-
100
- if (parts.length === 1) {
101
- // Flat command
102
- return {
103
- namespace: undefined,
104
- commandName: parts[0],
105
- isNested: false,
106
- depth: 0,
107
- segments: [parts[0]],
108
- };
109
- } else if (parts.length === 2) {
110
- // Nested command
111
- return {
112
- namespace: parts[0],
113
- commandName: parts[1],
114
- isNested: true,
115
- depth: 1,
116
- segments: [parts[0], parts[1]],
117
- };
118
- } else {
119
- throw new Error(
120
- `Invalid command ID format: "${commandId}". Too many colon separators.`,
121
- );
122
- }
58
+ return commandName;
123
59
  }
124
60
 
125
61
  /**
@@ -133,9 +69,8 @@ export function validateCommandId(commandId: string): boolean {
133
69
  return false;
134
70
  }
135
71
 
136
- // Command ID can have multiple colons (though generateCommandId enforces max 1 level)
137
- // This validates the format but doesn't enforce depth limits
138
- const pattern = /^[a-zA-Z][a-zA-Z0-9_-]*(?::[a-zA-Z][a-zA-Z0-9_-]*)*$/;
72
+ // Command ID must be a single segment (no colons)
73
+ const pattern = /^[a-zA-Z][a-zA-Z0-9_-]*$/;
139
74
  return pattern.test(commandId);
140
75
  }
141
76
 
@@ -149,41 +84,3 @@ function validateSegment(segment: string): boolean {
149
84
  const pattern = /^[a-zA-Z][a-zA-Z0-9_.-]*$/;
150
85
  return pattern.test(segment);
151
86
  }
152
-
153
- /**
154
- * Convert file path to command segments array
155
- * @param filePath - Absolute path to markdown file
156
- * @param rootDir - Root commands directory path
157
- * @returns Array of path segments
158
- */
159
- export function getCommandSegments(
160
- filePath: string,
161
- rootDir: string,
162
- ): string[] {
163
- const relativePath = relative(rootDir, filePath);
164
- const segments = relativePath.split("/").filter((segment) => segment !== "");
165
-
166
- // Remove .md extension from the last segment
167
- const lastSegment = segments[segments.length - 1];
168
- segments[segments.length - 1] = basename(lastSegment, ".md");
169
-
170
- return segments;
171
- }
172
-
173
- /**
174
- * Get namespace from command segments
175
- * @param segments - Command path segments
176
- * @returns Namespace string or undefined for flat commands
177
- */
178
- export function getNamespace(segments: string[]): string | undefined {
179
- return segments.length > 1 ? segments[0] : undefined;
180
- }
181
-
182
- /**
183
- * Get command depth from segments
184
- * @param segments - Command path segments
185
- * @returns Depth number (0 for root, 1 for nested)
186
- */
187
- export function getDepth(segments: string[]): number {
188
- return Math.max(0, segments.length - 1);
189
- }
@@ -89,20 +89,16 @@ export class ConfigValidator {
89
89
 
90
90
  /**
91
91
  * Validates model configuration (basic validation)
92
- * @param agentModel - Agent model string
92
+ * @param model - Agent model string
93
93
  * @param fastModel - Fast model string
94
94
  * @throws ConfigurationError if invalid
95
95
  */
96
- static validateModelConfig(agentModel: string, fastModel: string): void {
97
- if (
98
- !agentModel ||
99
- typeof agentModel !== "string" ||
100
- agentModel.trim() === ""
101
- ) {
96
+ static validateModelConfig(model: string, fastModel: string): void {
97
+ if (!model || typeof model !== "string" || model.trim() === "") {
102
98
  throw new ConfigurationError(
103
99
  "Agent model must be a non-empty string.",
104
- "agentModel",
105
- agentModel,
100
+ "model",
101
+ model,
106
102
  );
107
103
  }
108
104
 
@@ -14,14 +14,14 @@ import { HookManager } from "../managers/hookManager.js";
14
14
  import { SkillManager } from "../managers/skillManager.js";
15
15
  import { SlashCommandManager } from "../managers/slashCommandManager.js";
16
16
  import { PluginManager } from "../managers/pluginManager.js";
17
- import { BashManager } from "../managers/bashManager.js";
17
+ import { BangManager } from "../managers/bangManager.js";
18
18
  import { MemoryRuleManager } from "../managers/MemoryRuleManager.js";
19
19
  import { ReversionManager } from "../managers/reversionManager.js";
20
20
  import { SubagentManager } from "../managers/subagentManager.js";
21
21
  import { LiveConfigManager } from "../managers/liveConfigManager.js";
22
22
  import { ConfigurationService } from "../services/configurationService.js";
23
23
  import { ReversionService } from "../services/reversionService.js";
24
- import type { AgentOptions } from "../agent.js";
24
+ import type { AgentOptions } from "../types/index.js";
25
25
  import type {
26
26
  PermissionMode,
27
27
  Usage,
@@ -41,9 +41,8 @@ export interface AgentContainerSetupOptions {
41
41
  stream: boolean;
42
42
 
43
43
  // Callbacks to Agent methods
44
- onSessionIdChange: (sessionId: string) => void;
45
- onTasksChange: (tasks: BackgroundTask[]) => void;
46
- onSessionTasksChange: (tasks: Task[]) => void;
44
+ onBackgroundTasksChange: (tasks: BackgroundTask[]) => void;
45
+ onTasksChange: (tasks: Task[]) => void;
47
46
  onPermissionModeChange: (mode: PermissionMode) => void;
48
47
  handlePlanModeTransition: (mode: PermissionMode) => void;
49
48
  setPermissionMode: (mode: PermissionMode) => void;
@@ -66,9 +65,8 @@ export function setupAgentContainer(
66
65
  configurationService,
67
66
  systemPrompt,
68
67
  stream,
69
- onSessionIdChange,
68
+ onBackgroundTasksChange,
70
69
  onTasksChange,
71
- onSessionTasksChange,
72
70
  onPermissionModeChange,
73
71
  handlePlanModeTransition,
74
72
  setPermissionMode,
@@ -94,7 +92,12 @@ export function setupAgentContainer(
94
92
  callbacks: {
95
93
  ...callbacks,
96
94
  onSessionIdChange: (sessionId) => {
97
- onSessionIdChange(sessionId);
95
+ const taskManager = container.get<TaskManager>("TaskManager");
96
+ if (taskManager) {
97
+ taskManager.syncWithSession().catch((error) => {
98
+ logger.error("Failed to sync task list with session:", error);
99
+ });
100
+ }
98
101
  callbacks.onSessionIdChange?.(sessionId);
99
102
  },
100
103
  },
@@ -111,15 +114,15 @@ export function setupAgentContainer(
111
114
  container.register("TaskManager", taskManager);
112
115
  taskManager.on("tasksChange", async () => {
113
116
  const tasks = await taskManager.listTasks();
114
- onSessionTasksChange(tasks);
117
+ onTasksChange(tasks);
115
118
  });
116
119
 
117
120
  const backgroundTaskManager = new BackgroundTaskManager(container, {
118
121
  callbacks: {
119
122
  ...callbacks,
120
- onTasksChange: (tasks) => {
121
- onTasksChange(tasks);
122
- callbacks.onTasksChange?.(tasks);
123
+ onBackgroundTasksChange: (tasks) => {
124
+ onBackgroundTasksChange(tasks);
125
+ callbacks.onBackgroundTasksChange?.(tasks);
123
126
  },
124
127
  },
125
128
  workdir,
@@ -272,8 +275,8 @@ export function setupAgentContainer(
272
275
  const pluginManager = new PluginManager(container, { workdir });
273
276
  container.register("PluginManager", pluginManager);
274
277
 
275
- const bashManager = new BashManager(container, { workdir });
276
- container.register("BashManager", bashManager);
278
+ const bangManager = new BangManager(container, { workdir });
279
+ container.register("BangManager", bangManager);
277
280
 
278
281
  return container;
279
282
  }
@@ -3,12 +3,7 @@ import { join, extname, basename } from "path";
3
3
  import { homedir } from "os";
4
4
  import type { CustomSlashCommand } from "../types/index.js";
5
5
  import { parseMarkdownFile } from "./markdownParser.js";
6
- import {
7
- generateCommandId,
8
- getCommandSegments,
9
- getNamespace,
10
- getDepth,
11
- } from "./commandPathResolver.js";
6
+ import { generateCommandId } from "./commandPathResolver.js";
12
7
  import { logger } from "./globalLogger.js";
13
8
 
14
9
  /**
@@ -26,104 +21,46 @@ export function getUserCommandsDir(): string {
26
21
  }
27
22
 
28
23
  /**
29
- * Scan a directory for markdown command files with nested directory support
24
+ * Scan a directory for markdown command files (flat structure only)
30
25
  * @param dirPath - Root commands directory path
31
- * @param maxDepth - Maximum nesting depth to scan (default: 1)
32
26
  */
33
- export function scanCommandsDirectory(
34
- dirPath: string,
35
- maxDepth: number = 1,
36
- ): CustomSlashCommand[] {
27
+ export function scanCommandsDirectory(dirPath: string): CustomSlashCommand[] {
37
28
  if (!existsSync(dirPath)) {
38
29
  return [];
39
30
  }
40
31
 
41
- return scanCommandsDirectoryRecursive(dirPath, dirPath, 0, maxDepth);
42
- }
43
-
44
- /**
45
- * Recursively scan directory for commands with depth control
46
- * @param currentPath - Current directory being scanned
47
- * @param rootPath - Root commands directory (for relative path calculation)
48
- * @param currentDepth - Current nesting depth
49
- * @param maxDepth - Maximum allowed depth
50
- */
51
- function scanCommandsDirectoryRecursive(
52
- currentPath: string,
53
- rootPath: string,
54
- currentDepth: number,
55
- maxDepth: number,
56
- ): CustomSlashCommand[] {
57
32
  const commands: CustomSlashCommand[] = [];
58
33
 
59
34
  try {
60
- const entries = readdirSync(currentPath);
35
+ const entries = readdirSync(dirPath);
61
36
 
62
37
  for (const entryName of entries) {
63
- const fullPath = join(currentPath, entryName);
64
-
65
- let isDirectory = false;
66
- let isFile = false;
38
+ const fullPath = join(dirPath, entryName);
67
39
 
68
40
  try {
69
41
  const stats = statSync(fullPath);
70
- isDirectory = stats.isDirectory();
71
- isFile = stats.isFile();
72
- } catch (error) {
73
- // Skip entries that cannot be stat'd
74
- logger.warn(`Cannot access ${fullPath}:`, error);
75
- continue;
76
- }
77
-
78
- if (isDirectory) {
79
- // Skip subdirectories if we're at max depth
80
- if (currentDepth >= maxDepth) {
81
- logger.warn(
82
- `Skipping directory ${fullPath}: exceeds maximum nesting depth of ${maxDepth}`,
83
- );
42
+ if (!stats.isFile() || extname(entryName) !== ".md") {
84
43
  continue;
85
44
  }
86
45
 
87
- // Recursively scan subdirectory
88
- const nestedCommands = scanCommandsDirectoryRecursive(
89
- fullPath,
90
- rootPath,
91
- currentDepth + 1,
92
- maxDepth,
93
- );
94
- commands.push(...nestedCommands);
95
- } else if (isFile && extname(entryName) === ".md") {
96
46
  // Process markdown file
97
- try {
98
- const commandId = generateCommandId(fullPath, rootPath);
99
- const segments = getCommandSegments(fullPath, rootPath);
100
- const namespace = getNamespace(segments);
101
- const depth = getDepth(segments);
102
-
103
- const { content, config } = parseMarkdownFile(fullPath);
104
-
105
- commands.push({
106
- id: commandId,
107
- name: basename(entryName, ".md"),
108
- description: config?.description,
109
- filePath: fullPath,
110
- content,
111
- config,
112
-
113
- // Nested command metadata
114
- namespace,
115
- isNested: depth > 0,
116
- depth,
117
- segments,
118
- });
119
- } catch (error) {
120
- logger.warn(`Failed to load custom command from ${fullPath}:`, error);
121
- }
47
+ const commandId = generateCommandId(fullPath, dirPath);
48
+ const { content, config } = parseMarkdownFile(fullPath);
49
+
50
+ commands.push({
51
+ id: commandId,
52
+ name: basename(entryName, ".md"),
53
+ description: config?.description,
54
+ filePath: fullPath,
55
+ content,
56
+ config,
57
+ });
58
+ } catch (error) {
59
+ logger.warn(`Failed to load custom command from ${fullPath}:`, error);
122
60
  }
123
- // Skip non-markdown files silently
124
61
  }
125
62
  } catch (error) {
126
- logger.warn(`Failed to scan commands directory ${currentPath}:`, error);
63
+ logger.warn(`Failed to scan commands directory ${dirPath}:`, error);
127
64
  }
128
65
 
129
66
  return commands;