wave-agent-sdk 1.1.5 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/dist/agent.d.ts +70 -24
  2. package/dist/agent.js +110 -30
  3. package/dist/builtin/plugins.js +11 -20
  4. package/dist/builtin/skills/settings.js +6 -8
  5. package/dist/core/plugin.d.ts +4 -0
  6. package/dist/core/plugin.js +7 -1
  7. package/dist/index.d.ts +2 -1
  8. package/dist/index.js +2 -1
  9. package/dist/managers/aiManager.d.ts +0 -24
  10. package/dist/managers/aiManager.js +14 -181
  11. package/dist/managers/bashModeManager.d.ts +33 -0
  12. package/dist/managers/bashModeManager.js +110 -0
  13. package/dist/managers/hookManager.d.ts +5 -0
  14. package/dist/managers/hookManager.js +7 -0
  15. package/dist/managers/liveConfigManager.js +3 -3
  16. package/dist/managers/mcpManager.d.ts +23 -0
  17. package/dist/managers/mcpManager.js +162 -14
  18. package/dist/managers/messageManager.d.ts +12 -13
  19. package/dist/managers/messageManager.js +63 -60
  20. package/dist/managers/permissionManager.d.ts +29 -0
  21. package/dist/managers/permissionManager.js +158 -70
  22. package/dist/managers/planManager.d.ts +9 -0
  23. package/dist/managers/planManager.js +19 -1
  24. package/dist/managers/skillManager.d.ts +31 -0
  25. package/dist/managers/skillManager.js +122 -12
  26. package/dist/managers/slashCommandManager.js +9 -28
  27. package/dist/managers/subagentManager.d.ts +7 -0
  28. package/dist/managers/subagentManager.js +61 -9
  29. package/dist/managers/workflowManager.js +6 -0
  30. package/dist/prompts/index.d.ts +0 -1
  31. package/dist/prompts/index.js +0 -4
  32. package/dist/services/MarketplaceService.js +36 -14
  33. package/dist/services/configurationService.d.ts +34 -2
  34. package/dist/services/configurationService.js +123 -16
  35. package/dist/services/initializationService.js +2 -2
  36. package/dist/services/jsonlHandler.d.ts +14 -0
  37. package/dist/services/jsonlHandler.js +44 -1
  38. package/dist/services/memory.d.ts +14 -0
  39. package/dist/services/memory.js +33 -0
  40. package/dist/services/officialMarketplaceMirror.d.ts +85 -0
  41. package/dist/services/officialMarketplaceMirror.js +289 -0
  42. package/dist/services/remoteSettingsService.js +4 -4
  43. package/dist/services/session.js +30 -13
  44. package/dist/services/worktreeHooks.js +6 -1
  45. package/dist/stdio/index.d.ts +10 -0
  46. package/dist/stdio/index.js +10 -0
  47. package/dist/stdio/notificationRouter.d.ts +38 -0
  48. package/dist/stdio/notificationRouter.js +96 -0
  49. package/dist/stdio/rpcClient.d.ts +18 -0
  50. package/dist/stdio/rpcClient.js +10 -0
  51. package/dist/stdio/stdioAgent.d.ts +222 -0
  52. package/dist/stdio/stdioAgent.js +341 -0
  53. package/dist/tools/bashTool.js +2 -0
  54. package/dist/tools/exitPlanMode.js +10 -2
  55. package/dist/types/agent.d.ts +8 -0
  56. package/dist/types/commands.d.ts +7 -0
  57. package/dist/types/configuration.d.ts +6 -1
  58. package/dist/types/hooks.d.ts +1 -0
  59. package/dist/types/hooks.js +19 -0
  60. package/dist/types/mcp.d.ts +3 -0
  61. package/dist/types/messaging.d.ts +1 -8
  62. package/dist/types/skills.d.ts +11 -0
  63. package/dist/utils/bashParser.d.ts +17 -0
  64. package/dist/utils/bashParser.js +72 -0
  65. package/dist/utils/bashStructure/bashLexer.d.ts +96 -0
  66. package/dist/utils/bashStructure/bashLexer.js +676 -0
  67. package/dist/utils/bashStructure/bashParser.d.ts +144 -0
  68. package/dist/utils/bashStructure/bashParser.js +606 -0
  69. package/dist/utils/bashStructure/bashSemantics.d.ts +70 -0
  70. package/dist/utils/bashStructure/bashSemantics.js +477 -0
  71. package/dist/utils/bashStructure/index.d.ts +26 -0
  72. package/dist/utils/bashStructure/index.js +27 -0
  73. package/dist/utils/bashStructure/types.d.ts +62 -0
  74. package/dist/utils/bashStructure/types.js +47 -0
  75. package/dist/utils/containerSetup.js +5 -6
  76. package/dist/utils/fileUtils.d.ts +11 -0
  77. package/dist/utils/fileUtils.js +37 -0
  78. package/dist/utils/messageOperations.d.ts +0 -18
  79. package/dist/utils/messageOperations.js +0 -62
  80. package/dist/utils/subagentParser.js +9 -2
  81. package/dist/utils/tokenCalculation.js +0 -8
  82. package/dist/utils/worktreeUtils.d.ts +2 -1
  83. package/dist/utils/worktreeUtils.js +64 -34
  84. package/package.json +6 -1
  85. package/dist/managers/bangManager.d.ts +0 -26
  86. package/dist/managers/bangManager.js +0 -78
package/dist/agent.d.ts CHANGED
@@ -1,11 +1,12 @@
1
1
  import { type QueuedMessage } from "./managers/messageQueue.js";
2
2
  import { SlashCommand, CustomSlashCommand, AgentOptions } from "./types/index.js";
3
3
  import type { Message, McpServerStatus, GatewayConfig, ModelConfig, Usage, PermissionMode, ForegroundTask, SkillMetadata } from "./types/index.js";
4
+ import type { HookEvent, HookEventConfig } from "./types/hooks.js";
4
5
  import type { WorktreeSession } from "./utils/worktreeSession.js";
5
6
  export declare class Agent {
6
7
  private messageManager;
7
8
  private aiManager;
8
- private bangManager;
9
+ private bashModeManager;
9
10
  private backgroundTaskManager;
10
11
  private logger?;
11
12
  private toolManager;
@@ -65,6 +66,10 @@ export declare class Agent {
65
66
  private constructor();
66
67
  get sessionId(): string;
67
68
  get messages(): Message[];
69
+ /** Full UI display message stream (includes pre-compaction history).
70
+ * Consumed by hosts for the full-list message view (getMessages RPC,
71
+ * CLI structural refresh); the API context stays on `messages`. */
72
+ get displayMessages(): Message[];
68
73
  get usages(): Usage[];
69
74
  get sessionFilePath(): string;
70
75
  get latestTotalTokens(): number;
@@ -87,6 +92,21 @@ export declare class Agent {
87
92
  get userMemory(): string;
88
93
  /** Get combined memory content (project + user + modular rules) */
89
94
  getCombinedMemory(): Promise<string>;
95
+ /**
96
+ * Read the user-level memory file (~/.wave/AGENTS.md). Unlike the cached
97
+ * `userMemory` getter this always reads fresh content (the settings UI edits
98
+ * the file while the agent may already hold a cache).
99
+ */
100
+ readUserMemoryContent(): Promise<string>;
101
+ /**
102
+ * Read the project-level memory file (<workdir>/AGENTS.md, CLAUDE.md
103
+ * fallback). Always reads fresh content for the settings UI.
104
+ */
105
+ readProjectMemoryContent(workdir: string): Promise<string>;
106
+ /** Persist the user-level memory file (~/.wave/AGENTS.md). */
107
+ writeUserMemoryContent(content: string): Promise<void>;
108
+ /** Persist the project-level memory file (<workdir>/AGENTS.md). */
109
+ writeProjectMemoryContent(workdir: string, content: string): Promise<void>;
90
110
  /** Get AI loading status */
91
111
  get isLoading(): boolean;
92
112
  /** Get message compaction status */
@@ -229,7 +249,7 @@ export declare class Agent {
229
249
  */
230
250
  restoreSession(sessionId: string): Promise<void>;
231
251
  abortAIMessage(): void;
232
- /** Execute bash command (bang command) */
252
+ /** Execute a bash-mode command (`!command`) */
233
253
  bang(command: string): Promise<void>;
234
254
  clearMessages(): Promise<void>;
235
255
  /**
@@ -274,28 +294,6 @@ export declare class Agent {
274
294
  * @returns Promise that resolves to the AI's answer
275
295
  */
276
296
  askBtw(question: string, abortSignal?: AbortSignal, onContent?: (content: string) => void, onReasoning?: (content: string) => void): Promise<string>;
277
- /**
278
- * Start a fork subagent in the background (the "/subtask" command path).
279
- *
280
- * The fork inherits the parent's full conversation context — same system
281
- * prompt, tools, model, and message prefix — so the prompt cache is reused,
282
- * then works independently in the background. On completion its final
283
- * response is delivered back to the main conversation as a task notification
284
- * carrying the `<result>` tag. Recursion is prevented inside the fork (the
285
- * Agent tool and Task tools are denied).
286
- *
287
- * @param prompt - The task description for the fork subagent
288
- * @param options - Background task label and optional loose turn bound
289
- * @param options.description - Display label for the background task
290
- * @param options.maxTurns - Loose turn bound (defaults to 200, aligned with
291
- * Claude Code's fork subagent)
292
- * @param abortSignal - Optional signal to abort the fork
293
- * @returns Promise that resolves to the background task ID
294
- */
295
- forkSubagent(prompt: string, options: {
296
- description: string;
297
- maxTurns?: number;
298
- }, abortSignal?: AbortSignal): Promise<string>;
299
297
  /**
300
298
  * Send a message to the AI agent with optional images
301
299
  *
@@ -351,6 +349,15 @@ export declare class Agent {
351
349
  connectMcpServer(serverName: string): Promise<boolean>;
352
350
  /** Disconnect MCP server */
353
351
  disconnectMcpServer(serverName: string): Promise<boolean>;
352
+ /**
353
+ * Remove an MCP server from a persisted config file (user ~/.wave/mcp.json
354
+ * or project <workdir>/.mcp.json), then disconnect and drop it from memory.
355
+ */
356
+ removeMcpServer(scope: "user" | "project", serverName: string): Promise<boolean>;
357
+ /** The user-level MCP config path (~/.wave/mcp.json) */
358
+ getUserMcpConfigPath(): string;
359
+ /** The project-level MCP config path (<workdir>/.mcp.json) */
360
+ getProjectMcpConfigPath(): string;
354
361
  /** Get all available slash commands */
355
362
  getSlashCommands(): SlashCommand[];
356
363
  /** Check if slash command exists */
@@ -395,6 +402,14 @@ export declare class Agent {
395
402
  * Get the current plan file path (for testing and UI)
396
403
  */
397
404
  getPlanFilePath(): string | undefined;
405
+ /**
406
+ * Resolve once the plan file path has been generated and set after entering
407
+ * plan mode. Resolves `undefined` when no generation is in flight.
408
+ * Used by the CLI /plan command and the stdio getPlanFile RPC to avoid
409
+ * querying the model before the path is known (the plan mode reminder needs
410
+ * the path to be set).
411
+ */
412
+ awaitPlanFilePath(): Promise<string | undefined>;
398
413
  /**
399
414
  * Get all currently allowed rules (user-defined and default)
400
415
  */
@@ -430,6 +445,37 @@ export declare class Agent {
430
445
  * @returns The list of skill metadata
431
446
  */
432
447
  getSkillMetadata(): SkillMetadata[];
448
+ /**
449
+ * Delete a user/personal or project skill by removing its directory.
450
+ * Builtin and plugin skills are read-only and cannot be deleted.
451
+ * @param name - The skill name as shown in skill metadata
452
+ * @returns true if the skill was deleted
453
+ */
454
+ deleteSkill(name: string): Promise<boolean>;
455
+ /**
456
+ * Delete a user-level or project-level subagent by removing its markdown
457
+ * file. Builtin and plugin agents are read-only and cannot be deleted.
458
+ * @param name - The subagent name as shown in configurations
459
+ * @returns true if the subagent was deleted
460
+ */
461
+ deleteSubagent(name: string): Promise<boolean>;
462
+ /**
463
+ * Get hook configurations at a specific scope (user or project settings.json;
464
+ * plugin hooks come from the hook manager's plugin-registered set).
465
+ */
466
+ getHooksByScope(scope: "user" | "project" | "plugin"): Promise<Partial<Record<HookEvent, HookEventConfig[]>>>;
467
+ /**
468
+ * Absolute path of the settings.json holding hooks at `scope` — user
469
+ * `~/.wave/settings.json` or project `<workdir>/.wave/settings.json`. Hosts
470
+ * pass it to the GUI so editors / file panels can open the file (they cannot
471
+ * expand `~`).
472
+ */
473
+ getHookConfigPath(scope: "user" | "project"): string;
474
+ /**
475
+ * Delete a hook at a specific scope, identified by hookName of the form
476
+ * `Event:Matcher`.
477
+ */
478
+ deleteHook(scope: "user" | "project", hookName: string): Promise<void>;
433
479
  /**
434
480
  * Get currently active subagent instances (status active/initializing).
435
481
  * @returns The list of active subagent instances
package/dist/agent.js CHANGED
@@ -51,7 +51,7 @@ export class Agent {
51
51
  * @param options - Configuration options for the Agent instance
52
52
  */
53
53
  constructor(options) {
54
- this.bangManager = null;
54
+ this.bashModeManager = null;
55
55
  this.dispatchPromise = null; // Track current dispatch for teardown
56
56
  this.isAborting = false; // Transient guard: prevents tryDispatch from firing during abortMessage (reset when the abort completes)
57
57
  this.isDestroyed = false; // Terminal guard: set in destroy(), never reset — no dispatch may ever start after destroy
@@ -115,7 +115,7 @@ export class Agent {
115
115
  this.aiManager = this.container.get("AIManager");
116
116
  this.slashCommandManager = this.container.get("SlashCommandManager");
117
117
  this.pluginManager = this.container.get("PluginManager");
118
- this.bangManager = this.container.get("BangManager");
118
+ this.bashModeManager = this.container.get("BashModeManager");
119
119
  this.cronManager = this.container.get("CronManager");
120
120
  this.messageQueue = this.container.get("MessageQueue");
121
121
  this.asyncWorkRegistry = this.container.get("AsyncWorkRegistry");
@@ -135,7 +135,7 @@ export class Agent {
135
135
  this.tryDispatch();
136
136
  };
137
137
  // Wire up bang manager callback for command running changes
138
- this.bangManager.onCommandRunningChange = (running) => {
138
+ this.bashModeManager.onCommandRunningChange = (running) => {
139
139
  this.options.callbacks?.onCommandRunningChange?.(running);
140
140
  if (!running)
141
141
  this.tryDispatch();
@@ -152,6 +152,12 @@ export class Agent {
152
152
  get messages() {
153
153
  return this.messageManager.getMessages();
154
154
  }
155
+ /** Full UI display message stream (includes pre-compaction history).
156
+ * Consumed by hosts for the full-list message view (getMessages RPC,
157
+ * CLI structural refresh); the API context stays on `messages`. */
158
+ get displayMessages() {
159
+ return this.messageManager.getDisplayMessages();
160
+ }
155
161
  get usages() {
156
162
  return this.messageManager.getUsages();
157
163
  }
@@ -196,6 +202,33 @@ export class Agent {
196
202
  async getCombinedMemory() {
197
203
  return this.messageManager.getCombinedMemory();
198
204
  }
205
+ /**
206
+ * Read the user-level memory file (~/.wave/AGENTS.md). Unlike the cached
207
+ * `userMemory` getter this always reads fresh content (the settings UI edits
208
+ * the file while the agent may already hold a cache).
209
+ */
210
+ async readUserMemoryContent() {
211
+ const memoryService = this.container.get("MemoryService");
212
+ return (await memoryService?.getUserMemoryContent()) ?? "";
213
+ }
214
+ /**
215
+ * Read the project-level memory file (<workdir>/AGENTS.md, CLAUDE.md
216
+ * fallback). Always reads fresh content for the settings UI.
217
+ */
218
+ async readProjectMemoryContent(workdir) {
219
+ const memoryService = this.container.get("MemoryService");
220
+ return (await memoryService?.readMemoryFile(workdir)) ?? "";
221
+ }
222
+ /** Persist the user-level memory file (~/.wave/AGENTS.md). */
223
+ async writeUserMemoryContent(content) {
224
+ const memoryService = this.container.get("MemoryService");
225
+ await memoryService?.writeUserMemoryContent(content);
226
+ }
227
+ /** Persist the project-level memory file (<workdir>/AGENTS.md). */
228
+ async writeProjectMemoryContent(workdir, content) {
229
+ const memoryService = this.container.get("MemoryService");
230
+ await memoryService?.writeProjectMemoryContent(workdir, content);
231
+ }
199
232
  /** Get AI loading status */
200
233
  get isLoading() {
201
234
  return this.aiManager.isLoading;
@@ -206,7 +239,7 @@ export class Agent {
206
239
  }
207
240
  /** Get bash command execution status */
208
241
  get isCommandRunning() {
209
- return this.bangManager?.isCommandRunning ?? false;
242
+ return this.bashModeManager?.isCommandRunning ?? false;
210
243
  }
211
244
  /** Get queued user-facing messages (excludes background notifications) */
212
245
  get queuedMessages() {
@@ -317,7 +350,7 @@ export class Agent {
317
350
  this.messageQueue.transitionTo("running");
318
351
  this.options.callbacks?.onQueuedMessagesChange?.(this.queuedMessages);
319
352
  if (next.type === "bang") {
320
- await this.bangManager?.executeCommand(next.content);
353
+ await this.bashModeManager?.executeCommand(next.content);
321
354
  await this.messageManager.saveSession();
322
355
  }
323
356
  else if (next.type === "notification") {
@@ -516,7 +549,7 @@ export class Agent {
516
549
  abortAIMessage() {
517
550
  this.aiManager.abortAIMessage();
518
551
  }
519
- /** Execute bash command (bang command) */
552
+ /** Execute a bash-mode command (`!command`) */
520
553
  async bang(command) {
521
554
  this.assertNotDestroyed();
522
555
  // If the agent is busy, enqueue the bang command
@@ -525,7 +558,7 @@ export class Agent {
525
558
  this.options.callbacks?.onQueuedMessagesChange?.(this.queuedMessages);
526
559
  return;
527
560
  }
528
- await this.bangManager?.executeCommand(command);
561
+ await this.bashModeManager?.executeCommand(command);
529
562
  await this.messageManager.saveSession();
530
563
  }
531
564
  async clearMessages() {
@@ -620,7 +653,7 @@ export class Agent {
620
653
  }
621
654
  /** Interrupt bash command execution */
622
655
  abortBashCommand() {
623
- this.bangManager?.abortCommand();
656
+ this.bashModeManager?.abortCommand();
624
657
  }
625
658
  /** Interrupt slash command execution */
626
659
  abortSlashCommand() {
@@ -753,28 +786,6 @@ export class Agent {
753
786
  const result = await this.aiManager.runBtwFork(question, abortSignal, onContent, onReasoning);
754
787
  return result.content ?? result.error ?? "No response received";
755
788
  }
756
- /**
757
- * Start a fork subagent in the background (the "/subtask" command path).
758
- *
759
- * The fork inherits the parent's full conversation context — same system
760
- * prompt, tools, model, and message prefix — so the prompt cache is reused,
761
- * then works independently in the background. On completion its final
762
- * response is delivered back to the main conversation as a task notification
763
- * carrying the `<result>` tag. Recursion is prevented inside the fork (the
764
- * Agent tool and Task tools are denied).
765
- *
766
- * @param prompt - The task description for the fork subagent
767
- * @param options - Background task label and optional loose turn bound
768
- * @param options.description - Display label for the background task
769
- * @param options.maxTurns - Loose turn bound (defaults to 200, aligned with
770
- * Claude Code's fork subagent)
771
- * @param abortSignal - Optional signal to abort the fork
772
- * @returns Promise that resolves to the background task ID
773
- */
774
- async forkSubagent(prompt, options, abortSignal) {
775
- this.assertNotDestroyed();
776
- return this.aiManager.runForkSubagent(prompt, options, abortSignal);
777
- }
778
789
  /**
779
790
  * Send a message to the AI agent with optional images
780
791
  *
@@ -876,6 +887,21 @@ export class Agent {
876
887
  async disconnectMcpServer(serverName) {
877
888
  return await this.mcpManager.disconnectServer(serverName);
878
889
  }
890
+ /**
891
+ * Remove an MCP server from a persisted config file (user ~/.wave/mcp.json
892
+ * or project <workdir>/.mcp.json), then disconnect and drop it from memory.
893
+ */
894
+ async removeMcpServer(scope, serverName) {
895
+ return await this.mcpManager.removeServerFromConfig(scope, serverName);
896
+ }
897
+ /** The user-level MCP config path (~/.wave/mcp.json) */
898
+ getUserMcpConfigPath() {
899
+ return this.mcpManager.getUserConfigPath();
900
+ }
901
+ /** The project-level MCP config path (<workdir>/.mcp.json) */
902
+ getProjectMcpConfigPath() {
903
+ return this.mcpManager.getProjectConfigPath();
904
+ }
879
905
  // ========== Slash Command Management Methods ==========
880
906
  /** Get all available slash commands */
881
907
  getSlashCommands() {
@@ -951,6 +977,16 @@ export class Agent {
951
977
  getPlanFilePath() {
952
978
  return this.permissionManager.getPlanFilePath();
953
979
  }
980
+ /**
981
+ * Resolve once the plan file path has been generated and set after entering
982
+ * plan mode. Resolves `undefined` when no generation is in flight.
983
+ * Used by the CLI /plan command and the stdio getPlanFile RPC to avoid
984
+ * querying the model before the path is known (the plan mode reminder needs
985
+ * the path to be set).
986
+ */
987
+ awaitPlanFilePath() {
988
+ return this.planManager.awaitPlanFilePath();
989
+ }
954
990
  /**
955
991
  * Get all currently allowed rules (user-defined and default)
956
992
  */
@@ -1003,6 +1039,50 @@ export class Agent {
1003
1039
  getSkillMetadata() {
1004
1040
  return this.skillManager.getAvailableSkills();
1005
1041
  }
1042
+ /**
1043
+ * Delete a user/personal or project skill by removing its directory.
1044
+ * Builtin and plugin skills are read-only and cannot be deleted.
1045
+ * @param name - The skill name as shown in skill metadata
1046
+ * @returns true if the skill was deleted
1047
+ */
1048
+ async deleteSkill(name) {
1049
+ return await this.skillManager.deleteSkill(name);
1050
+ }
1051
+ /**
1052
+ * Delete a user-level or project-level subagent by removing its markdown
1053
+ * file. Builtin and plugin agents are read-only and cannot be deleted.
1054
+ * @param name - The subagent name as shown in configurations
1055
+ * @returns true if the subagent was deleted
1056
+ */
1057
+ async deleteSubagent(name) {
1058
+ return await this.subagentManager.deleteSubagent(name);
1059
+ }
1060
+ /**
1061
+ * Get hook configurations at a specific scope (user or project settings.json;
1062
+ * plugin hooks come from the hook manager's plugin-registered set).
1063
+ */
1064
+ async getHooksByScope(scope) {
1065
+ if (scope === "plugin") {
1066
+ return this.hookManager.getPluginHooks();
1067
+ }
1068
+ return await this.configurationService.getHooksByScope(this.workdir, scope);
1069
+ }
1070
+ /**
1071
+ * Absolute path of the settings.json holding hooks at `scope` — user
1072
+ * `~/.wave/settings.json` or project `<workdir>/.wave/settings.json`. Hosts
1073
+ * pass it to the GUI so editors / file panels can open the file (they cannot
1074
+ * expand `~`).
1075
+ */
1076
+ getHookConfigPath(scope) {
1077
+ return this.configurationService.getHookConfigPath(this.workdir, scope);
1078
+ }
1079
+ /**
1080
+ * Delete a hook at a specific scope, identified by hookName of the form
1081
+ * `Event:Matcher`.
1082
+ */
1083
+ async deleteHook(scope, hookName) {
1084
+ await this.configurationService.deleteHook(this.workdir, scope, hookName);
1085
+ }
1006
1086
  /**
1007
1087
  * Get currently active subagent instances (status active/initializing).
1008
1088
  * @returns The list of active subagent instances
@@ -41,9 +41,9 @@ const guidance = [
41
41
  "- 边界模糊时也先写 spec 草稿请用户确认,不要直接改代码。",
42
42
  "- 规格编写技能(specify)由 AI 自动触发:对话中涉及新需求或需求变更时主动创建或更新规格文件,不需要用户手动调用(不出现在斜杠命令列表中)。",
43
43
  \`- 新增或修改 spec 后运行校验:\${specCount}(自动检测 docs/specs/,否则 specs/,否则退出)。\`,
44
- "- 阶段衔接一律用 AskUserQuestion 单选让用户点击决策,不要求自然语言:spec 确认(确认通过/需要修改)、是否制作原型、是否制定技术方案(plan)均通过单选推进。",
45
- "- 可选原型阶段(在规格之后、plan 之前):仅实现前端界面,数据全部使用 mock;可选 plan 阶段:进入 plan 模式制定技术方案,批准后再编码。二者均可跳过——最短流程=规格+编码,最长=规格+原型+plan+编码。仅当需求涉及前端界面时才询问是否制作原型,无前端界面的需求(后端服务、CLI、算法库等)直接跳过原型询问。",
46
- "- 用 task 工具追踪进度:规格、原型、plan、编码各阶段开始前用 TaskCreate 创建任务并标记进行中(TaskUpdate),完成/批准/确认后标记完成,让用户在任务列表中看到当前所处阶段。",
44
+ "- 规格确认与阶段衔接一次完成:通过 AskUserQuestion 单选让用户点击决策(选项只传「直接实现 / 制定技术方案」,并在问题文案中说明如需调整规格可选「其他」输入修改意见;「其他」由 AskUserQuestion 自动附加,勿手动添加;选「其他」=规格需调整,按反馈修改后再次询问)。",
45
+ "- 可选技术方案阶段:制定技术方案并批准后再编码;也可跳过——最短流程=规格+编码,最长=规格+技术方案+编码。",
46
+ "- 用 task 工具追踪进度:规格、技术方案、编码各阶段开始前用 TaskCreate 创建任务并标记进行中(TaskUpdate),完成/批准/确认后标记完成,让用户在任务列表中看到当前所处阶段。",
47
47
  ].join("\\n");
48
48
 
49
49
  // JSON form → parsed as hookSpecificOutput.additionalContext by the hook manager.
@@ -131,7 +131,7 @@ if (warnings.length) {
131
131
  `,
132
132
  "plugins/sdd/skills/specify/SKILL.md": `---
133
133
  name: specify
134
- description: 根据自然语言描述创建或更新功能规格说明,并通过单选衔接可选原型、技术方案(plan)与编码阶段。
134
+ description: 根据自然语言描述创建或更新功能规格说明,并通过单选衔接可选技术方案与编码阶段。
135
135
  user-invocable: false
136
136
  ---
137
137
 
@@ -147,7 +147,7 @@ $ARGUMENTS
147
147
 
148
148
  根据对话中的功能描述,执行以下步骤:
149
149
 
150
- 0. **创建进度任务**:用 TaskCreate 创建「编写功能规格」任务,并用 TaskUpdate 标记进行中。后续每个阶段(原型、plan、编码)同样在开始前创建任务、结束后更新状态,让用户在任务列表中看到当前进度。
150
+ 0. **创建进度任务**:用 TaskCreate 创建「编写功能规格」任务,并用 TaskUpdate 标记进行中。后续每个阶段(技术方案、编码)同样在开始前创建任务、结束后更新状态,让用户在任务列表中看到当前进度。
151
151
 
152
152
  1. **确定规格文件路径**:
153
153
  - **确定规格根目录**:优先复用项目中已有的规格目录——若 \`docs/specs/\` 存在则用之,否则若 \`specs/\` 存在则用之,否则默认 \`specs/\`(并在完成报告中说明所选目录,便于用户纠正)。
@@ -172,23 +172,14 @@ $ARGUMENTS
172
172
 
173
173
  5. **校验并确认规格**:
174
174
  - 运行会话引导中给出的 spec-count 校验命令(自动检测 docs/specs/,否则 specs/,否则跳过)
175
- - 输出规格文件路径,并通过 AskUserQuestion 单选请求确认(选项:确认通过 / 需要修改)
176
- - 选「需要修改」→ 按用户反馈更新规格后重新校验,并再次单选确认;选「确认通过」→ 将「编写功能规格」任务标记完成
175
+ - 输出规格文件路径,并通过 AskUserQuestion 单选请求决策(选项只传:直接实现 / 制定技术方案;AskUserQuestion 会自动附加「其他」选项,勿手动添加「其他」;在问题文案中说明如需调整规格可选「其他」并输入修改意见)
176
+ - 用户选「其他」并输入意见 视为规格需要调整:按用户反馈更新规格后重新校验,并再次单选
177
+ - 选「直接实现」→ 将「编写功能规格」任务标记完成,进入编码阶段
178
+ - 选「制定技术方案」→ 将「编写功能规格」任务标记完成;用 TaskCreate 创建「制定技术方案」任务并标记进行中;调用 EnterPlanMode 进入技术方案模式,制定技术方案(技术选型、架构设计、实现步骤)并写入计划文件;用 ExitPlanMode 请求批准——被拒绝则按反馈更新方案后重新请求,批准后标记任务完成
177
179
 
178
- 6. **询问是否制作原型(可选阶段,位于规格之后、plan 之前)**:
179
- - 仅当需求涉及前端界面时弹出询问;需求不涉及前端界面(如后端服务、CLI 工具、算法库)时,跳过本阶段直接进入下一步,不弹出原型选择
180
- - 通过 AskUserQuestion 单选询问(选项:制作原型 / 跳过)
181
- - 选「制作原型」→ 用 TaskCreate 创建「制作原型」任务并标记进行中;仅实现前端界面,数据全部使用 mock(不接后端、不接真实数据);完成后展示可交互原型供用户查看,并将任务标记完成
182
- - 选「跳过」→ 直接进入下一步
183
-
184
- 7. **询问是否制定技术方案(可选 plan 阶段)**:
185
- - 通过 AskUserQuestion 单选询问(选项:进入 plan 模式 / 跳过)
186
- - 选「进入 plan 模式」→ 用 TaskCreate 创建「制定技术方案」任务并标记进行中;调用 EnterPlanMode 进入 plan 模式,制定技术方案(技术选型、架构设计、实现步骤)并写入计划文件;用 ExitPlanMode 请求批准——被拒绝则按反馈更新方案后重新请求,批准后标记任务完成
187
- - 选「跳过」→ 直接进入下一步
188
-
189
- 8. **编码阶段**:
180
+ 6. **编码阶段**:
190
181
  - 用 TaskCreate 创建「实现功能」任务并标记进行中
191
- - 按已确认的规格实现;若制作了原型则遵循其交互设计,若批准了技术方案则遵循其架构
182
+ - 按已确认的规格实现;若批准了技术方案则遵循其架构
192
183
  - 实现完成后将任务标记完成
193
184
 
194
185
  ## 指南
@@ -702,7 +702,7 @@ You can extend the Safe Zone by adding \`additionalDirectories\` to your \`permi
702
702
 
703
703
  ## Permission Modes
704
704
 
705
- The \`permissionMode\` setting determines how Wave handles requests to use restricted tools (e.g., \`Bash\`, \`Edit\`, \`Write\`, \`AskUserQuestion\`).
705
+ The \`defaultMode\` setting (under \`permissions\`, aligning with Claude Code's settings key) determines how Wave handles requests to use restricted tools (e.g., \`Bash\`, \`Edit\`, \`Write\`, \`AskUserQuestion\`).
706
706
 
707
707
  | Mode | Description |
708
708
  | :--- | :--- |
@@ -717,7 +717,7 @@ The \`permissionMode\` setting determines how Wave handles requests to use restr
717
717
  \`\`\`json
718
718
  {
719
719
  "permissions": {
720
- "permissionMode": "default",
720
+ "defaultMode": "default",
721
721
  "additionalDirectories": ["/home/user/my-exports"],
722
722
  "allow": ["ls -R", "git status"],
723
723
  "deny": ["rm -rf"]
@@ -735,7 +735,7 @@ You can pre-approve or explicitly forbid specific operations using \`allow\` and
735
735
  When a tool is called, Wave checks:
736
736
  1. If the operation matches a \`deny\` rule, it is rejected.
737
737
  2. If the operation matches an \`allow\` rule, it is permitted.
738
- 3. If no rules match, the behavior depends on the \`permissionMode\`.
738
+ 3. If no rules match, the behavior depends on the \`defaultMode\`.
739
739
 
740
740
  ### Rule Syntax
741
741
 
@@ -998,7 +998,7 @@ For detailed permission configuration and available permission modes, see [PERMI
998
998
  "permissions": {
999
999
  "allow": ["Bash", "Read"],
1000
1000
  "deny": ["Write"],
1001
- "permissionMode": "default",
1001
+ "defaultMode": "default",
1002
1002
  "additionalDirectories": ["/tmp/wave-exports"]
1003
1003
  }
1004
1004
  }
@@ -1104,10 +1104,6 @@ The \`SKILL.md\` file uses YAML frontmatter for configuration and Markdown for i
1104
1104
  ---
1105
1105
  name: my-skill
1106
1106
  description: A brief description of what the skill does.
1107
- context: fork
1108
- allowed-tools:
1109
- - Bash
1110
- - Read
1111
1107
  ---
1112
1108
 
1113
1109
  # My Skill Instructions
@@ -1117,6 +1113,8 @@ When this skill is invoked, follow these steps:
1117
1113
  2. Use the \`Bash\` tool to run \`npm test\`.
1118
1114
  \`\`\`
1119
1115
 
1116
+ By default, new skills only include \`name\` and \`description\`. Only add \`context: fork\` or \`allowed-tools\` when the user explicitly asks for them.
1117
+
1120
1118
  ### YAML Frontmatter Fields
1121
1119
 
1122
1120
  - \`name\`: (Required) Unique identifier (lowercase, numbers, hyphens).
@@ -56,6 +56,10 @@ export declare class PluginCore {
56
56
  removeMarketplace(name: string, scope?: Scope): Promise<void>;
57
57
  /**
58
58
  * Updates a specific marketplace or all marketplaces
59
+ *
60
+ * Pulls the latest marketplace source and reinstalls any plugins that are
61
+ * already installed from it, so a manual "update marketplace" also brings
62
+ * installed plugins up to date (mirrors Claude Code's refresh-and-bump).
59
63
  */
60
64
  updateMarketplace(name?: string): Promise<void>;
61
65
  /**
@@ -121,9 +121,15 @@ export class PluginCore {
121
121
  }
122
122
  /**
123
123
  * Updates a specific marketplace or all marketplaces
124
+ *
125
+ * Pulls the latest marketplace source and reinstalls any plugins that are
126
+ * already installed from it, so a manual "update marketplace" also brings
127
+ * installed plugins up to date (mirrors Claude Code's refresh-and-bump).
124
128
  */
125
129
  async updateMarketplace(name) {
126
- await this.marketplaceService.updateMarketplace(name);
130
+ await this.marketplaceService.updateMarketplace(name, {
131
+ updatePlugins: true,
132
+ });
127
133
  }
128
134
  /**
129
135
  * Lists all registered marketplaces
package/dist/index.d.ts CHANGED
@@ -25,7 +25,8 @@ export * from "./utils/nameGenerator.js";
25
25
  export * from "./utils/pathEncoder.js";
26
26
  export * from "./utils/worktreeSession.js";
27
27
  export * from "./utils/worktreeUtils.js";
28
- export { loadMergedWaveConfig, loadUserConfigEnv, } from "./services/configurationService.js";
28
+ export { loadMergedWaveConfig, loadUserConfigEnv, loadWaveConfigFromFile, } from "./services/configurationService.js";
29
+ export { getUserConfigPaths, getProjectConfigPaths, } from "./utils/configPaths.js";
29
30
  export * from "./types/index.js";
30
31
  export type { SubagentConfiguration } from "./utils/subagentParser.js";
31
32
  export type { SubagentInstance } from "./managers/subagentManager.js";
package/dist/index.js CHANGED
@@ -29,7 +29,8 @@ export * from "./utils/nameGenerator.js";
29
29
  export * from "./utils/pathEncoder.js";
30
30
  export * from "./utils/worktreeSession.js";
31
31
  export * from "./utils/worktreeUtils.js";
32
- export { loadMergedWaveConfig, loadUserConfigEnv, } from "./services/configurationService.js";
32
+ export { loadMergedWaveConfig, loadUserConfigEnv, loadWaveConfigFromFile, } from "./services/configurationService.js";
33
+ export { getUserConfigPaths, getProjectConfigPaths, } from "./utils/configPaths.js";
33
34
  export * from "./types/index.js";
34
35
  // Export tool building utilities
35
36
  export * from "./tools/buildTool.js";
@@ -189,30 +189,6 @@ export declare class AIManager {
189
189
  deniedToolMessage?: string;
190
190
  maxTurns?: number;
191
191
  }, abortSignal?: AbortSignal): Promise<ForkLoopResult>;
192
- /**
193
- * Fork subagent ("/subtask"): spawn a background agent that inherits the
194
- * parent's full conversation context — the same system prompt, tools, model,
195
- * and message prefix as the main loop — so the forked request prefix matches
196
- * exactly and the prompt cache is reused. The fork runs to completion in the
197
- * background via the shared BackgroundTaskManager (visible in /tasks, log at
198
- * `os.tmpdir()/wave-subagent-<taskId>.log`); its final response is enqueued
199
- * as a task notification carrying the `<result>` tag, so the main agent
200
- * picks it up on the next turn and can act on it.
201
- *
202
- * Recursion is prevented by denying the Agent tool and the Task management
203
- * tools (the fork cannot spawn further subagents or mutate the shared task
204
- * list), and by honoring the parent's denied rules for everything else.
205
- * Gate-approved tools execute locally in the stripped fork context (no
206
- * permission prompts), mirroring the other fork paths.
207
- *
208
- * Returns the background task ID (empty string when no BackgroundTaskManager
209
- * is registered, in which case the fork still runs but without task
210
- * bookkeeping or notification).
211
- */
212
- runForkSubagent(prompt: string, options: {
213
- description: string;
214
- maxTurns?: number;
215
- }, abortSignal?: AbortSignal): Promise<string>;
216
192
  /**
217
193
  * Parse a fork tool call's JSON arguments, recovering truncated JSON the
218
194
  * same way the main loop does. Unparseable arguments fall back to `{}` and