wave-agent-sdk 1.0.8 → 1.0.10

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 (74) hide show
  1. package/dist/builtin/index.d.ts +1 -0
  2. package/dist/builtin/index.js +20 -0
  3. package/dist/builtin/plugins.d.ts +1 -0
  4. package/dist/builtin/plugins.js +225 -0
  5. package/dist/builtin/skills/artifact.d.ts +1 -0
  6. package/dist/builtin/skills/artifact.js +18 -0
  7. package/dist/builtin/skills/code-review.d.ts +1 -0
  8. package/{builtin/skills/code-review/SKILL.md → dist/builtin/skills/code-review.js} +23 -19
  9. package/dist/builtin/skills/deep-research.d.ts +1 -0
  10. package/{builtin/skills/deep-research/SKILL.md → dist/builtin/skills/deep-research.js} +18 -14
  11. package/dist/builtin/skills/init.d.ts +1 -0
  12. package/{builtin/skills/init/SKILL.md → dist/builtin/skills/init.js} +6 -3
  13. package/dist/builtin/skills/loop.d.ts +1 -0
  14. package/dist/builtin/skills/loop.js +83 -0
  15. package/dist/builtin/skills/settings.d.ts +1 -0
  16. package/dist/builtin/skills/settings.js +1224 -0
  17. package/dist/builtin/skills/simplify.d.ts +1 -0
  18. package/{builtin/skills/simplify/SKILL.md → dist/builtin/skills/simplify.js} +7 -3
  19. package/dist/builtin/subagents.d.ts +1 -0
  20. package/dist/builtin/subagents.js +164 -0
  21. package/dist/managers/aiManager.js +18 -1
  22. package/dist/managers/backgroundTaskManager.js +52 -4
  23. package/dist/managers/mcpManager.js +1 -1
  24. package/dist/managers/messageManager.d.ts +6 -0
  25. package/dist/managers/messageManager.js +33 -0
  26. package/dist/managers/skillManager.d.ts +13 -0
  27. package/dist/managers/skillManager.js +30 -0
  28. package/dist/managers/subagentManager.d.ts +8 -0
  29. package/dist/managers/subagentManager.js +57 -2
  30. package/dist/managers/toolManager.d.ts +8 -0
  31. package/dist/managers/toolManager.js +11 -0
  32. package/dist/services/artifactAvailability.d.ts +3 -1
  33. package/dist/services/artifactAvailability.js +9 -1
  34. package/dist/services/remoteSettingsService.js +2 -0
  35. package/dist/tools/bashTool.js +13 -2
  36. package/dist/tools/editTool.js +8 -2
  37. package/dist/tools/readTool.js +6 -3
  38. package/dist/tools/types.d.ts +1 -0
  39. package/dist/tools/writeTool.js +1 -0
  40. package/dist/types/messaging.d.ts +1 -0
  41. package/dist/utils/builtinEmbed.d.ts +21 -0
  42. package/dist/utils/builtinEmbed.js +53 -0
  43. package/dist/utils/configPaths.d.ts +0 -1
  44. package/dist/utils/configPaths.js +5 -26
  45. package/dist/utils/containerSetup.js +8 -0
  46. package/dist/utils/convertMessagesForAPI.js +21 -2
  47. package/dist/utils/encoding.d.ts +28 -0
  48. package/dist/utils/encoding.js +99 -0
  49. package/dist/utils/messageOperations.d.ts +1 -0
  50. package/dist/utils/toolImagePersistence.d.ts +25 -0
  51. package/dist/utils/toolImagePersistence.js +56 -0
  52. package/package.json +1 -3
  53. package/builtin/plugins/sdd/.wave-plugin/plugin.json +0 -8
  54. package/builtin/plugins/sdd/hooks/hooks.json +0 -14
  55. package/builtin/plugins/sdd/scripts/session-start.js +0 -24
  56. package/builtin/plugins/sdd/scripts/spec-count.js +0 -77
  57. package/builtin/plugins/sdd/skills/specify/SKILL.md +0 -47
  58. package/builtin/plugins/sdd/skills/specify/templates/spec-template.md +0 -47
  59. package/builtin/skills/loop/SKILL.md +0 -79
  60. package/builtin/skills/settings/ENV.md +0 -78
  61. package/builtin/skills/settings/HOOKS.md +0 -227
  62. package/builtin/skills/settings/MCP.md +0 -137
  63. package/builtin/skills/settings/MEMORY.md +0 -76
  64. package/builtin/skills/settings/MODELS.md +0 -119
  65. package/builtin/skills/settings/PERMISSIONS.md +0 -88
  66. package/builtin/skills/settings/PLUGINS.md +0 -171
  67. package/builtin/skills/settings/SKILL.md +0 -125
  68. package/builtin/skills/settings/SKILLS.md +0 -107
  69. package/builtin/skills/settings/SUBAGENTS.md +0 -77
  70. package/builtin/subagents/bash.md +0 -19
  71. package/builtin/subagents/explore.md +0 -43
  72. package/builtin/subagents/general-purpose.md +0 -20
  73. package/builtin/subagents/plan.md +0 -56
  74. package/builtin/subagents/vision.md +0 -18
@@ -1,77 +0,0 @@
1
- #!/usr/bin/env node
2
- // Generic, self-contained spec validator. Counts user stories and acceptance
3
- // scenarios under the project's specs directory and warns on missing sections.
4
- // Detects the specs dir: prefers docs/specs/, else specs/, else exits gracefully.
5
- // No dependency on any project's VitePress/docs-site modules.
6
- import fs from "node:fs";
7
- import path from "node:path";
8
-
9
- function detectSpecsDir() {
10
- for (const dir of ["docs/specs", "specs"]) {
11
- const resolved = path.resolve(process.cwd(), dir);
12
- if (fs.existsSync(resolved)) return resolved;
13
- }
14
- return null;
15
- }
16
-
17
- function walk(dir, out = []) {
18
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
19
- const fullPath = path.join(dir, entry.name);
20
- if (entry.isDirectory()) walk(fullPath, out);
21
- else if (entry.isFile() && entry.name.endsWith(".md")) out.push(fullPath);
22
- }
23
- return out;
24
- }
25
-
26
- function countUserStories(content) {
27
- const m = content.match(/^### 用户故事[::]/gm);
28
- return m ? m.length : 0;
29
- }
30
-
31
- function countAcceptanceScenarios(content) {
32
- const m = content.match(/^\d+\.\s+\*\*假设\*\*/gm);
33
- return m ? m.length : 0;
34
- }
35
-
36
- const specsDir = detectSpecsDir();
37
- if (!specsDir) {
38
- console.log("未找到规格目录(docs/specs/ 或 specs/),跳过校验。");
39
- process.exit(0);
40
- }
41
-
42
- const files = walk(specsDir).sort();
43
- const totals = { specs: 0, us: 0, ac: 0 };
44
- const warnings = [];
45
-
46
- // index.md is conventionally a directory listing page, not a spec — skip it.
47
- const specFiles = files.filter(
48
- (fp) => path.basename(fp).toLowerCase() !== "index.md",
49
- );
50
-
51
- for (const fp of specFiles) {
52
- const content = fs.readFileSync(fp, "utf-8");
53
- const usCount = countUserStories(content);
54
- const acCount = countAcceptanceScenarios(content);
55
- const rel = path.relative(process.cwd(), fp);
56
- totals.specs++;
57
- totals.us += usCount;
58
- totals.ac += acCount;
59
- if (!content.match(/^## 用户场景与测试/m))
60
- warnings.push(`${rel}: 缺少 "## 用户场景与测试" 章节`);
61
- if (usCount === 0)
62
- warnings.push(`${rel}: 未找到用户故事(期望 \`### 用户故事:\`)`);
63
- if (acCount === 0)
64
- warnings.push(
65
- `${rel}: 未找到验收场景(期望 \`N. **假设** … **当** … **则** …\`)`,
66
- );
67
- console.log(`${rel} 用户故事: ${usCount} 验收场景: ${acCount}`);
68
- }
69
-
70
- console.log("---");
71
- console.log(
72
- `规格: ${totals.specs} 用户故事: ${totals.us} 验收场景: ${totals.ac}`,
73
- );
74
- if (warnings.length) {
75
- for (const w of warnings) console.warn(`⚠ ${w}`);
76
- console.warn(`⚠ ${warnings.length} 条模板警告——见上方。`);
77
- }
@@ -1,47 +0,0 @@
1
- ---
2
- name: specify
3
- description: 根据自然语言描述创建或更新功能规格说明,生成包含用户故事与验收场景的规格文件。
4
- user-invocable: false
5
- ---
6
-
7
- ## 用户输入
8
-
9
- ```text
10
- $ARGUMENTS
11
- ```
12
-
13
- ## 流程
14
-
15
- 本技能由 AI 在会话中自动触发(不占用手动斜杠命令)。触发时机:用户提出新的需求、修改需求或涉及功能边界时,若对应规格尚未创建或已过期,则主动创建或更新规格说明。$ARGUMENTS 通常为空——需求描述直接来自对话上下文,不要让用户重复。
16
-
17
- 根据对话中的功能描述,执行以下步骤:
18
-
19
- 1. **确定规格文件路径**:
20
- - **确定规格根目录**:优先复用项目中已有的规格目录——若 `docs/specs/` 存在则用之,否则若 `specs/` 存在则用之,否则默认 `specs/`(并在完成报告中说明所选目录,便于用户纠正)。
21
- - **选择分组**:若规格目录下已有分组子目录,沿用其既有分组约定;否则默认扁平结构(直接放在规格根目录下)。
22
- - 根据功能描述生成 2-4 个词的 slug(小写、连字符、保留缩写词),与组内已有文件名不冲突
23
- - 规格文件路径:`<规格根目录>/<分组>/<slug>.md`(无分组时为 `<规格根目录>/<slug>.md`)
24
-
25
- 2. **加载模板** `${WAVE_SKILL_DIR}/templates/spec-template.md`,了解必需章节。
26
-
27
- 3. **编写规格说明**:
28
- - 解析用户描述,提取关键概念:角色、操作、数据、约束
29
- - 对于不明确的部分,根据上下文和行业标准做出合理推断
30
- - 仅在关键决策处标记 `[待澄清:具体问题]`(最多 3 处)
31
- - 填写 frontmatter(`name` 为功能中文名、`description` 为一句话简述、`order` 为控制组内排序的数字)
32
- - 填写「用户场景与测试」章节,包含按优先级排序的用户故事(P1、P2、P3...),每个故事以「作为…,我希望…,以便…」描述,附 `**为什么是这个优先级**` 与 `**独立测试**`(不适用的可省略)
33
- - 为每个用户故事编写可测试的验收场景(**假设** … **当** … **则** …)
34
- - 写入规格文件,替换所有占位符
35
-
36
- 4. **如果存在 `[待澄清]` 标记**(最多 3 处):
37
- - 将每个标记作为问题展示,附带建议答案
38
- - 等待用户回复后更新规格文件
39
-
40
- 5. 报告完成,输出规格文件路径。
41
-
42
- ## 指南
43
-
44
- - 关注用户**需要什麼**和**为什么**,而非如何实现
45
- - 不包含实现细节(不涉及技术栈、API、代码结构)
46
- - 每个验收场景必须可测试、无歧义
47
- - 删除不适用的可选章节(不要留 "N/A")
@@ -1,47 +0,0 @@
1
- ---
2
- name: "[功能名称]"
3
- description: "[一句话简短描述]"
4
- order: [数字,控制组内排序]
5
- ---
6
-
7
- # 功能规格说明:[功能名称]
8
-
9
- **创建日期**:[日期]
10
-
11
- ## 用户场景与测试 *(必填)*
12
-
13
- ### 用户故事:[简要标题](优先级:P1)
14
-
15
- 作为[角色],我希望[操作],以便[价值/目的]。
16
-
17
- **为什么是这个优先级**:[解释其价值以及为何具有此优先级]
18
-
19
- **独立测试**:[描述如何独立测试——例如,"可以通过 [具体操作] 进行完整测试,并交付 [具体价值]"]
20
-
21
- **验收场景**:
22
-
23
- 1. **假设** [初始状态],**当** [操作],**则** [预期结果]
24
- 2. **假设** [初始状态],**当** [操作],**则** [预期结果]
25
-
26
- ---
27
-
28
- ### 用户故事:[简要标题](优先级:P2)
29
-
30
- 作为[角色],我希望[操作],以便[价值/目的]。
31
-
32
- **为什么是这个优先级**:[解释其价值以及为何具有此优先级]
33
-
34
- **独立测试**:[描述如何独立测试]
35
-
36
- **验收场景**:
37
-
38
- 1. **假设** [初始状态],**当** [操作],**则** [预期结果]
39
-
40
- ---
41
-
42
- [根据需要添加更多用户故事,每个都分配优先级]
43
-
44
- ### 边界情况
45
-
46
- - **[问题?]** [答案/处理方式]
47
- - **[问题?]** [答案/处理方式]
@@ -1,79 +0,0 @@
1
- ---
2
- name: loop
3
- description: Run a prompt or slash command on a recurring interval (e.g. /loop 5m /foo, defaults to 10m)
4
- allowed-tools: CronCreate, Skill
5
- user-invocable: true
6
- ---
7
-
8
- # /loop — schedule a recurring prompt
9
-
10
- Parse the input below into `[interval] <prompt…>` and schedule it with CronCreate.
11
-
12
- ## Usage
13
-
14
- ```
15
- /loop [interval] <prompt>
16
-
17
- Run a prompt or slash command on a recurring interval.
18
-
19
- Intervals: Ns, Nm, Nh, Nd (e.g. 5m, 30m, 2h, 1d). Minimum granularity is 1 minute.
20
- If no interval is specified, defaults to 10m.
21
-
22
- Examples:
23
- /loop 5m /babysit-prs
24
- /loop 30m check the deploy
25
- /loop 1h /standup 1
26
- /loop check the deploy (defaults to 10m)
27
- /loop check the deploy every 20m
28
- ```
29
-
30
- ## Parsing (in priority order)
31
-
32
- 1. **Leading token**: if the first whitespace-delimited token matches `^\d+[smhd]$` (e.g. `5m`, `2h`), that's the interval; the rest is the prompt.
33
- 2. **Trailing "every" clause**: otherwise, if the input ends with `every <N><unit>` or `every <N> <unit-word>` (e.g. `every 20m`, `every 5 minutes`, `every 2 hours`), extract that as the interval and strip it from the prompt. Only match when what follows "every" is a time expression — `check every PR` has no interval.
34
- 3. **Default**: otherwise, interval is `10m` and the entire input is the prompt.
35
-
36
- If the resulting prompt is empty, show usage `/loop [interval] <prompt>` and stop — do not call CronCreate.
37
-
38
- Examples:
39
- - `5m /babysit-prs` → interval `5m`, prompt `/babysit-prs` (rule 1)
40
- - `check the deploy every 20m` → interval `20m`, prompt `check the deploy` (rule 2)
41
- - `run tests every 5 minutes` → interval `5m`, prompt `run tests` (rule 2)
42
- - `check the deploy` → interval `10m`, prompt `check the deploy` (rule 3)
43
- - `check every PR` → interval `10m`, prompt `check every PR` (rule 3 — "every" not followed by time)
44
- - `5m` → empty prompt → show usage
45
-
46
- ## Interval → cron
47
-
48
- Supported suffixes: `s` (seconds, rounded up to nearest minute, min 1), `m` (minutes), `h` (hours), `d` (days). Convert:
49
-
50
- | Interval pattern | Cron expression | Notes |
51
- |-----------------------|---------------------|------------------------------------------|
52
- | `Nm` where N ≤ 59 | `*/N * * * *` | every N minutes |
53
- | `Nm` where N ≥ 60 | `0 */H * * *` | round to hours (H = N/60, must divide 24)|
54
- | `Nh` where N ≤ 23 | `0 */N * * *` | every N hours |
55
- | `Nd` | `0 0 */N * *` | every N days at midnight local |
56
- | `Ns` | treat as `ceil(N/60)m` | cron minimum granularity is 1 minute |
57
-
58
- **If the interval doesn't cleanly divide its unit** (e.g. `7m` → `*/7 * * * *` gives uneven gaps at :56→:00; `90m` → 1.5h which cron can't express), pick the nearest clean interval and tell the user what you rounded to before scheduling.
59
-
60
- ## Avoid the :00 and :30 minute marks
61
-
62
- When the user's request is approximate, pick a minute that is NOT 0 or 30:
63
- - "every morning around 9" → `57 8 * * *` or `3 9 * * *` (not `0 9 * * *`)
64
- - "hourly" → `7 * * * *` (not `0 * * * *`)
65
-
66
- Only use minute 0 or 30 when the user names that exact time and clearly means it ("at 9:00 sharp", "at half past").
67
-
68
- ## Action
69
-
70
- 1. Call CronCreate with:
71
- - `cron`: the expression from the table above
72
- - `prompt`: the parsed prompt from above, verbatim (slash commands are passed through unchanged)
73
- - `recurring`: `true`
74
- 2. Briefly confirm: what's scheduled, the cron expression, the human-readable cadence, that recurring tasks auto-expire after 7 days, and that they can cancel sooner with CronDelete (include the job ID).
75
- 3. **Then immediately execute the parsed prompt now** — don't wait for the first cron fire. If it's a slash command, run it directly; otherwise act on it directly.
76
-
77
- ## Input
78
-
79
- $ARGUMENTS
@@ -1,78 +0,0 @@
1
- # Wave Environment Variables Configuration
2
-
3
- Environment variables allow you to customize Wave's behavior, configure AI models, and provide context to hooks and tools. This document provides detailed guidance on how to configure environment variables in `settings.json`.
4
-
5
- ## The `env` Field
6
-
7
- Environment variables are configured in the `env` field of `settings.json`. It is a simple key-value pair of strings.
8
-
9
- ```json
10
- {
11
- "env": {
12
- "WAVE_MODEL": "gemini-3-flash",
13
- "MY_CUSTOM_VAR": "some-value"
14
- }
15
- }
16
- ```
17
-
18
- ## Supported `WAVE_*` Environment Variables
19
-
20
- Wave uses several environment variables to control its core functionality. Variables marked **OS env only** are read from the OS environment (or constructor / stdio `initialize` params) and are **NOT** read from settings.json `env` — set them in your shell, not in the `env` field.
21
-
22
- | Variable | Description | Default |
23
- | :--- | :--- | :--- |
24
- | `WAVE_API_KEY` | API key for the AI gateway. | - |
25
- | `WAVE_BASE_URL` | Base URL for the AI gateway. | - |
26
- | `WAVE_SERVER_URL` | Server URL for SSO authentication. **OS env only** — set via OS env or `options.serverUrl`; not read from settings.json `env` (avoids a startup 401 race). | `https://codechat.codewave.163.com` |
27
- | `WAVE_CUSTOM_HEADERS` | Custom HTTP headers for the AI gateway. Newline-separated `Key: Value` pairs (e.g., `"X-Foo: bar\nAuthorization: Bearer xxx"`). | - |
28
- | `WAVE_MODEL` | The primary AI model to use for the agent. | `gemini-3-flash` |
29
- | `WAVE_FAST_MODEL` | The fast AI model to use for quick tasks. | `gemini-2.5-flash` |
30
- | `WAVE_VISION_MODEL` | Vision-capable model used by the built-in `vision` subagent for image recognition. When set, the built-in `vision` subagent is registered (its frontmatter `model: visionModel` resolves to this value); when unset, the subagent is not loaded. Useful when the main model is fast but non-vision (e.g. DeepSeek). | - (not registered) |
31
- | `WAVE_MAX_INPUT_TOKENS` | Maximum number of input tokens allowed. | `200000` |
32
- | `WAVE_MAX_OUTPUT_TOKENS` | Maximum number of output tokens allowed. | `32000` |
33
- | `WAVE_DISABLE_AUTO_MEMORY` | Set to `1` or `true` to disable the auto-memory feature. | `false` |
34
- | `WAVE_AUTO_MEMORY_FREQUENCY` | Auto memory update frequency. `1` = every turn, `2` = every 2 turns, etc. | `1` |
35
- | `WAVE_TASK_LIST_ID` | Explicitly set the task list ID for the session. | (Session ID) |
36
- | `WAVE_PLUGIN_GIT_TIMEOUT_MS` | Timeout in milliseconds for git operations when installing plugins. **OS env only** (infrastructure). | `300000` |
37
-
38
- ## Configuration Scopes
39
-
40
- Environment variables can be set in different scopes. Wave merges scopes from lowest to highest priority and stores the result in the agent's **per-session environment snapshot**. The snapshot takes priority over OS environment variables but is **NOT written to `process.env`** — this keeps multiple sessions in one `wave --stdio` process from polluting each other.
41
-
42
- Precedence (highest to lowest):
43
-
44
- 1. **Local Scope**: `.wave/settings.local.json` (Local overrides, ignored by git)
45
- 2. **Project Scope**: `.wave/settings.json` (Project-specific settings, shared via git)
46
- 3. **User Scope**: `~/.wave/settings.json` (Global settings for all projects)
47
- 4. **System Environment**: Variables set in your shell (e.g., `export WAVE_API_KEY=...`). Used as a fallback when a key is absent from the settings snapshot.
48
-
49
- > Settings `env` shadows (does not mutate) OS env: a key set in both settings.json `env` and the OS environment resolves to the settings value for that session, while the OS value remains untouched and visible to unrelated processes.
50
-
51
- ## Custom Environment Variables
52
-
53
- You can also define custom environment variables in the `env` field. These variables are stored in the session's environment snapshot and will be available to:
54
-
55
- - **Hooks**: Any shell command executed as a hook will have these variables in its environment (merged on top of OS env).
56
- - **Tools**: Tools like `Bash` will have access to these variables (merged on top of OS env).
57
-
58
- In `wave --stdio` mode one process hosts multiple sessions; each session keeps its own snapshot, so sessions with different `env` do not pollute each other (no "last session wins").
59
-
60
- Example:
61
- ```json
62
- {
63
- "env": {
64
- "PROJECT_NAME": "my-awesome-project",
65
- "DEPLOY_TARGET": "staging"
66
- }
67
- }
68
- ```
69
-
70
- ## Live Reload
71
-
72
- Environment variables configured in `settings.json` support **live reload**. When you modify the `env` field in any `settings.json` file (user, project, or local scope), the changes take effect immediately without requiring a Wave session restart — the session's environment snapshot is refreshed and subsequent resolve calls / subprocess spawns use the new values.
73
-
74
- ## Best Practices
75
-
76
- - **Use Local Overrides for Secrets**: Never commit sensitive information like `WAVE_API_KEY` to `settings.json`. Use `settings.local.json` instead.
77
- - **Standard Naming**: Use uppercase and underscores for environment variable names (e.g., `MY_VARIABLE`).
78
- - **Avoid Overriding System Variables**: Be careful not to override standard system variables like `PATH` or `HOME` unless you have a specific reason to do so.
@@ -1,227 +0,0 @@
1
- # Wave Hooks Configuration
2
-
3
- Hooks allow you to automate tasks when certain events occur in Wave. This document provides detailed guidance on how to configure hooks in `settings.json`.
4
-
5
- ## Hook Events
6
-
7
- Wave supports the following hook events:
8
-
9
- - `PreToolUse`: Triggered before a tool is executed.
10
- - `PostToolUse`: Triggered after a tool has finished executing.
11
- - `UserPromptSubmit`: Triggered when a user submits a prompt.
12
- - `PermissionRequest`: Triggered when Wave requests permission to use a tool.
13
- - `Stop`: Triggered when Wave finishes its response cycle (no more tool calls).
14
- - `SubagentStop`: Triggered when a subagent finishes its response cycle.
15
- - `WorktreeCreate`: Triggered when a new worktree is created.
16
- - `WorktreeRemove`: Triggered before a worktree is removed (e.g., via ExitWorktree with `action: "remove"`). Non-blocking. Fires **before** the worktree directory is deleted so hooks can still read files inside it. The hook receives `worktree_path` in the JSON input. Useful for cleanup tasks (e.g., `docker compose -p $(basename "$worktree_path") down`).
17
- - `CwdChanged`: Triggered when the working directory changes (e.g., entering/exiting a worktree). Non-blocking.
18
- - `SessionStart`: Triggered during session initialization. Hooks can inject `additionalContext` and `initialUserMessage` via stdout.
19
- - `SessionEnd`: Triggered during agent destruction (fire-and-forget, non-blocking). Useful for cleanup, resource teardown, and analytics.
20
- - `PreCompact`: Triggered before conversation compaction. Hook stdout is captured as additional instructions and merged into the compaction prompt.
21
- - `PostCompact`: Triggered after conversation compaction completes. Receives the compact summary text.
22
-
23
- ## Hook Configuration Structure
24
-
25
- Hooks are configured in the `hooks` field of `settings.json`. Each event can have multiple hook configurations.
26
-
27
- ```json
28
- {
29
- "hooks": {
30
- "PreToolUse": [
31
- {
32
- "matcher": "Write",
33
- "hooks": [
34
- {
35
- "command": "pnpm lint",
36
- "description": "Run lint before writing files"
37
- }
38
- ]
39
- }
40
- ],
41
- "PermissionRequest": [
42
- {
43
- "matcher": "Bash",
44
- "hooks": [
45
- {
46
- "command": "echo \"Permission requested for Bash tool\" >> hooks.log",
47
- "description": "Log permission requests for Bash"
48
- }
49
- ]
50
- }
51
- ]
52
- }
53
- }
54
- ```
55
-
56
- ## Hook Configuration Fields
57
-
58
- - `matcher`: (Optional) A pattern to match against the tool name (e.g., "Write", "Read*", "/^Edit/"). Only applicable for `PreToolUse`, `PostToolUse`, and `PermissionRequest`.
59
- - `hooks`: An array of hook commands to execute.
60
- - `command`: The shell command to execute.
61
- - `description`: A brief description of the hook's purpose.
62
- - `async`: (Optional) Whether the hook should run in the background without blocking (default: `false`).
63
- - `timeout`: (Optional) Maximum execution time in seconds (default: `600`).
64
-
65
- ## Hook Input JSON
66
-
67
- Wave provides detailed context to hook processes via `stdin` as a JSON object. This allows hooks to make informed decisions based on the current state.
68
-
69
- ### Common Fields
70
- - `session_id`: The current session ID.
71
- - `transcript_path`: Path to the session transcript file (JSON).
72
- - `cwd`: The current working directory.
73
- - `hook_event_name`: The name of the triggering event.
74
-
75
- ### Event-Specific Fields
76
- - `tool_name`: (PreToolUse, PostToolUse, PermissionRequest) The name of the tool.
77
- - `tool_input`: (PreToolUse, PostToolUse, PermissionRequest) The input parameters passed to the tool.
78
- - `tool_response`: (PostToolUse) The result of the tool execution.
79
- - `user_prompt`: (UserPromptSubmit) The text submitted by the user.
80
- - `subagent_type`: (If executed by a subagent) The type of the subagent.
81
- - `name`: (WorktreeCreate) The name of the new worktree.
82
- - `worktree_path`: (WorktreeRemove) The absolute path of the worktree about to be removed. Derive the worktree name with `basename "$worktree_path"`.
83
- - `old_cwd`: (CwdChanged) The previous working directory.
84
- - `new_cwd`: (CwdChanged) The new working directory.
85
- - `compact_instructions`: (PreCompact) Custom instructions for the compaction, if any.
86
- - `compact_summary`: (PostCompact) The AI-generated compaction summary text.
87
- - `source`: (SessionStart) The session start source: `"startup"`, `"resume"`, or `"compact"`.
88
- - `agent_type`: (SessionStart) The agent type identifier.
89
- - `end_source`: (SessionEnd) The session end source: `"exit"`, `"stop"`, or `"compact"`.
90
-
91
- ## Hook Exit Codes
92
-
93
- Hooks can communicate status and control Wave's behavior using exit codes:
94
-
95
- - **Exit 0**: Success. Wave continues its normal execution.
96
- - **Exit 2**: Blocking Error. Wave blocks the current operation and provides feedback based on the event:
97
- - `UserPromptSubmit`: Blocks prompt processing and shows `stderr` as a user error.
98
- - `PreToolUse`: Blocks tool execution and provides `stderr` to the agent as feedback.
99
- - `PostToolUse`: Appends `stderr` to the tool result as feedback for the agent.
100
- - `Stop`: Blocks the stop operation and provides `stderr` to the agent.
101
- - `WorktreeCreate` / `WorktreeRemove` / `CwdChanged` / `PreCompact` / `PostCompact`: Shows `stderr` in an error block, but does not block the operation.
102
- - `SessionStart` / `SessionEnd`: Shows `stderr` in an error block, but does not block startup or shutdown.
103
- - **Other Exits (e.g., Exit 1)**: Non-blocking error. Wave continues execution but shows `stderr` as a warning to the user.
104
-
105
- ## SessionStart Hooks
106
-
107
- `SessionStart` hooks fire during session initialization. They can inject context and messages into the session via stdout.
108
-
109
- ### Stdout Processing
110
-
111
- Hook stdout is processed as follows:
112
- - If stdout is valid JSON with `hookSpecificOutput.additionalContext` (Claude Code format), that value is injected as additional context.
113
- - If stdout is valid JSON with `initialUserMessage` at the top level, that value is injected as the initial user message.
114
- - If stdout is not JSON, the entire output is appended as additional context.
115
-
116
- Example hook output:
117
- ```json
118
- {"hookSpecificOutput": {"hookEventName": "SessionStart", "additionalContext": "User prefers concise responses"}, "initialUserMessage": "Here is my current task..."}
119
- ```
120
-
121
- ### Example Configuration
122
- ```json
123
- {
124
- "hooks": {
125
- "SessionStart": [
126
- {
127
- "hooks": [
128
- {
129
- "command": "echo '{\"hookSpecificOutput\": {\"hookEventName\": \"SessionStart\", \"additionalContext\": \"Project uses pnpm and TypeScript\"}}'",
130
- "description": "Inject project context at session start"
131
- }
132
- ]
133
- }
134
- ]
135
- }
136
- }
137
- ```
138
-
139
- ## SessionEnd Hooks
140
-
141
- `SessionEnd` hooks fire during agent destruction (fire-and-forget, non-blocking). They are useful for cleanup tasks, resource teardown, and analytics.
142
-
143
- ### Input
144
- SessionEnd hooks receive `end_source` in the JSON input indicating how the session ended:
145
- - `"exit"`: User exited the session
146
- - `"stop"`: Session was explicitly stopped
147
- - `"compact"`: Session was compacted
148
-
149
- ### Example Configuration
150
- ```json
151
- {
152
- "hooks": {
153
- "SessionEnd": [
154
- {
155
- "hooks": [
156
- {
157
- "command": "echo '{\"session_id\": \"$WAVE_SESSION_ID\"}' >> /tmp/session-analytics.log",
158
- "description": "Log session end for analytics",
159
- "async": true
160
- }
161
- ]
162
- }
163
- ]
164
- }
165
- }
166
- ```
167
-
168
- ## WorktreeRemove Hooks
169
-
170
- `WorktreeRemove` hooks fire **before** the worktree directory is deleted, so they can still read files inside it. They are non-blocking (Notification type): the hook never replaces `git worktree remove` itself. Useful for cleaning up external resources that were provisioned for the worktree (databases, containers, etc.).
171
-
172
- ### Input
173
- WorktreeRemove hooks receive `worktree_path` in the JSON input (alongside the common fields `session_id`, `transcript_path`, `cwd`, `hook_event_name`). The worktree name can be derived via `basename "$worktree_path"`.
174
-
175
- ### Example Configuration
176
- ```json
177
- {
178
- "hooks": {
179
- "WorktreeRemove": [
180
- {
181
- "hooks": [
182
- {
183
- "command": "worktree_path=$(jq -r '.worktree_path') && docker compose -p \"$(basename \"$worktree_path\")\" down || true",
184
- "description": "Tear down the worktree's docker compose project before removal"
185
- }
186
- ]
187
- }
188
- ]
189
- }
190
- }
191
- ```
192
-
193
- ## Live Reload
194
-
195
- Hook configurations support **live reload**. When you modify hooks in `settings.json`, the changes take effect immediately without restarting Wave.
196
-
197
- ## Plugin Hooks
198
-
199
- When hooks are registered via a **plugin**, Wave automatically:
200
-
201
- 1. Substitutes `${WAVE_PLUGIN_ROOT}` with the plugin's directory path in the command string
202
- 2. Injects `WAVE_PLUGIN_ROOT` as an environment variable into the hook process
203
-
204
- ```json
205
- {
206
- "hooks": {
207
- "WorktreeCreate": [
208
- {
209
- "hooks": [
210
- {
211
- "command": "${WAVE_PLUGIN_ROOT}/scripts/setup-worktree.sh"
212
- }
213
- ]
214
- }
215
- ]
216
- }
217
- }
218
- ```
219
-
220
- The shell also receives `WAVE_PLUGIN_ROOT` as an env var, so `$WAVE_PLUGIN_ROOT` works in the hook script itself.
221
-
222
- ## Best Practices
223
-
224
- - **Keep hooks fast**: Long-running hooks can slow down your workflow unless they are `async`.
225
- - **Use descriptive names**: Help yourself and others understand what each hook does.
226
- - **Test your hooks**: Run the commands manually first to ensure they work as expected.
227
- - **Use local overrides**: For machine-specific hooks, use `.wave/settings.local.json`.