wave-agent-sdk 0.11.0 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/builtin/skills/init/SKILL.md +26 -0
- package/builtin/skills/settings/ENV.md +64 -0
- package/{src/builtin-skills → builtin/skills}/settings/HOOKS.md +12 -0
- package/builtin/skills/settings/MCP.md +55 -0
- package/builtin/skills/settings/MEMORY_RULES.md +60 -0
- package/{src/builtin-skills → builtin/skills}/settings/SKILL.md +22 -15
- package/builtin/skills/settings/SKILLS.md +63 -0
- package/builtin/skills/settings/SUBAGENTS.md +60 -0
- package/builtin/subagents/bash.md +18 -0
- package/builtin/subagents/explore.md +42 -0
- package/builtin/subagents/general-purpose.md +20 -0
- package/builtin/subagents/plan.md +55 -0
- package/dist/managers/slashCommandManager.d.ts.map +1 -1
- package/dist/managers/slashCommandManager.js +0 -16
- package/dist/prompts/index.d.ts +0 -5
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +1 -136
- package/dist/utils/configPaths.d.ts +4 -0
- package/dist/utils/configPaths.d.ts.map +1 -1
- package/dist/utils/configPaths.js +11 -9
- package/dist/utils/fileSearch.d.ts.map +1 -1
- package/dist/utils/fileSearch.js +7 -1
- package/dist/utils/subagentParser.d.ts.map +1 -1
- package/dist/utils/subagentParser.js +14 -4
- package/package.json +3 -2
- package/src/managers/slashCommandManager.ts +0 -19
- package/src/prompts/index.ts +0 -144
- package/src/utils/configPaths.ts +12 -10
- package/src/utils/fileSearch.ts +7 -1
- package/src/utils/subagentParser.ts +16 -6
- package/dist/builtin-skills/builtin-skills/loop/parsing.ts +0 -159
- package/dist/builtin-skills/builtin-skills/settings/HOOKS.md +0 -82
- package/dist/builtin-skills/builtin-skills/settings/SKILL.md +0 -86
- package/dist/builtin-skills/loop/parsing.d.ts +0 -13
- package/dist/builtin-skills/loop/parsing.d.ts.map +0 -1
- package/dist/builtin-skills/loop/parsing.js +0 -125
- package/dist/utils/builtinSubagents.d.ts +0 -7
- package/dist/utils/builtinSubagents.d.ts.map +0 -1
- package/dist/utils/builtinSubagents.js +0 -94
- package/src/builtin-skills/loop/SKILL.md +0 -53
- package/src/builtin-skills/loop/parsing.ts +0 -159
- package/src/utils/builtinSubagents.ts +0 -122
- /package/{dist/builtin-skills/builtin-skills → builtin/skills}/loop/SKILL.md +0 -0
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
export interface ParsedLoop {
|
|
2
|
-
interval: string;
|
|
3
|
-
prompt: string;
|
|
4
|
-
originalInterval?: string;
|
|
5
|
-
roundedTo?: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function parseLoopInput(
|
|
9
|
-
input: string,
|
|
10
|
-
defaultInterval: string = "10m",
|
|
11
|
-
): ParsedLoop {
|
|
12
|
-
const trimmedInput = input.trim();
|
|
13
|
-
if (!trimmedInput) {
|
|
14
|
-
return { interval: defaultInterval, prompt: "" };
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// 1. Leading token: ^\d+[smhd]$
|
|
18
|
-
const tokens = trimmedInput.split(/\s+/);
|
|
19
|
-
if (tokens[0].match(/^\d+[smhd]$/)) {
|
|
20
|
-
const interval = tokens[0];
|
|
21
|
-
const prompt = tokens.slice(1).join(" ");
|
|
22
|
-
return { interval, prompt };
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// 2. Trailing "every" clause: every <N><unit> or every <N> <unit-word>
|
|
26
|
-
// Units: s, m, h, d, second(s), minute(s), hour(s), day(s)
|
|
27
|
-
const everyRegex =
|
|
28
|
-
/\s+every\s+(\d+)\s*(s|m|h|d|seconds?|minutes?|hours?|days?)$/i;
|
|
29
|
-
const match = trimmedInput.match(everyRegex);
|
|
30
|
-
if (match) {
|
|
31
|
-
const n = match[1];
|
|
32
|
-
const unitWord = match[2].toLowerCase();
|
|
33
|
-
const unit = unitWord[0];
|
|
34
|
-
const interval = `${n}${unit}`;
|
|
35
|
-
const prompt = trimmedInput.substring(0, match.index).trim();
|
|
36
|
-
return { interval, prompt };
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// 3. Default
|
|
40
|
-
return { interval: defaultInterval, prompt: trimmedInput };
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const CLEAN_MINUTES = [1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30];
|
|
44
|
-
const CLEAN_HOURS = [1, 2, 3, 4, 6, 8, 12];
|
|
45
|
-
|
|
46
|
-
function getNearestClean(value: number, cleanValues: number[]): number {
|
|
47
|
-
let nearest = cleanValues[0];
|
|
48
|
-
let minDiff = Math.abs(value - nearest);
|
|
49
|
-
for (const clean of cleanValues) {
|
|
50
|
-
const diff = Math.abs(value - clean);
|
|
51
|
-
if (diff < minDiff) {
|
|
52
|
-
minDiff = diff;
|
|
53
|
-
nearest = clean;
|
|
54
|
-
} else if (diff === minDiff) {
|
|
55
|
-
// If equal diff, prefer the larger one? Or smaller?
|
|
56
|
-
// Let's prefer the larger one to be less frequent
|
|
57
|
-
if (clean > nearest) {
|
|
58
|
-
nearest = clean;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
return nearest;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function intervalToCron(interval: string): {
|
|
66
|
-
cron: string;
|
|
67
|
-
roundedTo?: string;
|
|
68
|
-
cadence: string;
|
|
69
|
-
} {
|
|
70
|
-
const match = interval.match(/^(\d+)([smhd])$/);
|
|
71
|
-
if (!match) {
|
|
72
|
-
throw new Error(`Invalid interval format: ${interval}`);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
let n = parseInt(match[1], 10);
|
|
76
|
-
const unit = match[2];
|
|
77
|
-
let roundedTo: string | undefined;
|
|
78
|
-
|
|
79
|
-
if (unit === "s") {
|
|
80
|
-
const minutes = Math.ceil(n / 60);
|
|
81
|
-
const result = intervalToCron(`${minutes}m`);
|
|
82
|
-
return {
|
|
83
|
-
...result,
|
|
84
|
-
roundedTo:
|
|
85
|
-
result.roundedTo || (minutes * 60 !== n ? `${minutes}m` : undefined),
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (unit === "m") {
|
|
90
|
-
if (n >= 60) {
|
|
91
|
-
const hours = Math.round(n / 60);
|
|
92
|
-
const result = intervalToCron(`${hours}h`);
|
|
93
|
-
return {
|
|
94
|
-
...result,
|
|
95
|
-
roundedTo:
|
|
96
|
-
result.roundedTo || (hours * 60 !== n ? `${hours}h` : undefined),
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (!CLEAN_MINUTES.includes(n)) {
|
|
101
|
-
const nearest = getNearestClean(n, CLEAN_MINUTES);
|
|
102
|
-
roundedTo = `${nearest}m`;
|
|
103
|
-
n = nearest;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// For minutes, we use */N. Thundering herd is less of an issue for high frequency,
|
|
107
|
-
// but we could still offset it. However, the spec says "random minute for approximate requests like 'hourly'".
|
|
108
|
-
// So for minutes, we'll stick to */N.
|
|
109
|
-
return {
|
|
110
|
-
cron: `*/${n} * * * *`,
|
|
111
|
-
roundedTo,
|
|
112
|
-
cadence: `every ${n} minute${n > 1 ? "s" : ""}`,
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (unit === "h") {
|
|
117
|
-
if (n > 23) {
|
|
118
|
-
const days = Math.round(n / 24);
|
|
119
|
-
const result = intervalToCron(`${days}d`);
|
|
120
|
-
return {
|
|
121
|
-
...result,
|
|
122
|
-
roundedTo:
|
|
123
|
-
result.roundedTo || (days * 24 !== n ? `${days}d` : undefined),
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (!CLEAN_HOURS.includes(n)) {
|
|
128
|
-
const nearest = getNearestClean(n, CLEAN_HOURS);
|
|
129
|
-
roundedTo = `${nearest}h`;
|
|
130
|
-
n = nearest;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Thundering herd prevention: pick a random minute
|
|
134
|
-
const randomMinute = Math.floor(Math.random() * 60);
|
|
135
|
-
return {
|
|
136
|
-
cron: `${randomMinute} */${n} * * *`,
|
|
137
|
-
roundedTo,
|
|
138
|
-
cadence: `every ${n} hour${n > 1 ? "s" : ""}`,
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (unit === "d") {
|
|
143
|
-
if (![1, 2, 3, 4, 5, 6, 7, 10, 14, 30].includes(n)) {
|
|
144
|
-
// For days, we don't have a strict "clean" list in spec, but let's use some common ones if needed.
|
|
145
|
-
// Actually, cron supports any */N for days.
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Thundering herd prevention: pick a random minute and hour
|
|
149
|
-
const randomMinute = Math.floor(Math.random() * 60);
|
|
150
|
-
const randomHour = Math.floor(Math.random() * 24);
|
|
151
|
-
return {
|
|
152
|
-
cron: `${randomMinute} ${randomHour} */${n} * *`,
|
|
153
|
-
roundedTo,
|
|
154
|
-
cadence: `every ${n} day${n > 1 ? "s" : ""}`,
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
throw new Error(`Unsupported unit: ${unit}`);
|
|
159
|
-
}
|
|
@@ -1,82 +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
|
-
|
|
17
|
-
## Hook Configuration Structure
|
|
18
|
-
|
|
19
|
-
Hooks are configured in the `hooks` field of `settings.json`. Each event can have multiple hook configurations.
|
|
20
|
-
|
|
21
|
-
```json
|
|
22
|
-
{
|
|
23
|
-
"hooks": {
|
|
24
|
-
"PreToolUse": [
|
|
25
|
-
{
|
|
26
|
-
"matcher": "Write",
|
|
27
|
-
"hooks": [
|
|
28
|
-
{
|
|
29
|
-
"command": "pnpm lint",
|
|
30
|
-
"description": "Run lint before writing files"
|
|
31
|
-
}
|
|
32
|
-
]
|
|
33
|
-
}
|
|
34
|
-
],
|
|
35
|
-
"PermissionRequest": [
|
|
36
|
-
{
|
|
37
|
-
"matcher": "Bash",
|
|
38
|
-
"hooks": [
|
|
39
|
-
{
|
|
40
|
-
"command": "echo \"Permission requested for Bash tool\" >> hooks.log",
|
|
41
|
-
"description": "Log permission requests for Bash"
|
|
42
|
-
}
|
|
43
|
-
]
|
|
44
|
-
}
|
|
45
|
-
]
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
## Hook Configuration Fields
|
|
51
|
-
|
|
52
|
-
- `matcher`: (Optional) A pattern to match against the tool name (e.g., "Write", "Read*", "/^Edit/"). Only applicable for `PreToolUse`, `PostToolUse`, and `PermissionRequest`.
|
|
53
|
-
- `hooks`: An array of hook commands to execute.
|
|
54
|
-
- `command`: The shell command to execute.
|
|
55
|
-
- `description`: A brief description of the hook's purpose.
|
|
56
|
-
- `async`: (Optional) Whether the hook should run in the background without blocking (default: `false`).
|
|
57
|
-
- `timeout`: (Optional) Maximum execution time in seconds (default: `600`).
|
|
58
|
-
|
|
59
|
-
## Hook Input JSON
|
|
60
|
-
|
|
61
|
-
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.
|
|
62
|
-
|
|
63
|
-
### Common Fields
|
|
64
|
-
- `session_id`: The current session ID.
|
|
65
|
-
- `transcript_path`: Path to the session transcript file (JSON).
|
|
66
|
-
- `cwd`: The current working directory.
|
|
67
|
-
- `hook_event_name`: The name of the triggering event.
|
|
68
|
-
|
|
69
|
-
### Event-Specific Fields
|
|
70
|
-
- `tool_name`: (PreToolUse, PostToolUse, PermissionRequest) The name of the tool.
|
|
71
|
-
- `tool_input`: (PreToolUse, PostToolUse, PermissionRequest) The input parameters passed to the tool.
|
|
72
|
-
- `tool_response`: (PostToolUse) The result of the tool execution.
|
|
73
|
-
- `user_prompt`: (UserPromptSubmit) The text submitted by the user.
|
|
74
|
-
- `subagent_type`: (If executed by a subagent) The type of the subagent.
|
|
75
|
-
- `name`: (WorktreeCreate) The name of the new worktree.
|
|
76
|
-
|
|
77
|
-
## Best Practices
|
|
78
|
-
|
|
79
|
-
- **Keep hooks fast**: Long-running hooks can slow down your workflow unless they are `async`.
|
|
80
|
-
- **Use descriptive names**: Help yourself and others understand what each hook does.
|
|
81
|
-
- **Test your hooks**: Run the commands manually first to ensure they work as expected.
|
|
82
|
-
- **Use local overrides**: For machine-specific hooks, use `.wave/settings.local.json`.
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: settings
|
|
3
|
-
description: Manage Wave settings and get guidance on settings.json
|
|
4
|
-
allowed-tools: Bash, Read, Write
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Wave Settings Skill
|
|
8
|
-
|
|
9
|
-
This skill helps you manage your Wave configuration and provides guidance on how to use `settings.json`.
|
|
10
|
-
|
|
11
|
-
## What is `settings.json`?
|
|
12
|
-
|
|
13
|
-
`settings.json` is the central configuration file for Wave. It allows you to customize hooks, environment variables, tool permissions, and more.
|
|
14
|
-
|
|
15
|
-
Wave looks for `settings.json` in three scopes:
|
|
16
|
-
1. **User Scope**: Global settings for all projects. Located at `~/.wave/settings.json`.
|
|
17
|
-
2. **Project Scope**: Settings specific to the current project. Located at `.wave/settings.json` in your project root.
|
|
18
|
-
3. **Local Scope**: Local overrides for the current project (not committed to git). Located at `.wave/settings.local.json`.
|
|
19
|
-
|
|
20
|
-
## Common Settings
|
|
21
|
-
|
|
22
|
-
### 1. Hooks
|
|
23
|
-
Hooks allow you to automate tasks when certain events occur (e.g., `WorktreeCreate`, `TaskStart`).
|
|
24
|
-
For detailed hook configuration, see [HOOKS.md](${WAVE_SKILL_DIR}/HOOKS.md).
|
|
25
|
-
|
|
26
|
-
### 2. Environment Variables
|
|
27
|
-
Set environment variables that will be available to all tools and hooks.
|
|
28
|
-
```json
|
|
29
|
-
{
|
|
30
|
-
"env": {
|
|
31
|
-
"NODE_ENV": "development",
|
|
32
|
-
"API_KEY": "your-api-key"
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
### 3. Permissions
|
|
38
|
-
Manage tool permissions and define the "Safe Zone".
|
|
39
|
-
```json
|
|
40
|
-
{
|
|
41
|
-
"permissions": {
|
|
42
|
-
"allow": ["Bash", "Read"],
|
|
43
|
-
"deny": ["Write"],
|
|
44
|
-
"permissionMode": "default",
|
|
45
|
-
"additionalDirectories": ["/tmp/wave-exports"]
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
### 4. Enabled Plugins
|
|
51
|
-
Enable or disable specific plugins.
|
|
52
|
-
```json
|
|
53
|
-
{
|
|
54
|
-
"enabledPlugins": {
|
|
55
|
-
"git-plugin": true,
|
|
56
|
-
"experimental-plugin": false
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### 5. Model and Token Configuration
|
|
62
|
-
Define which AI models Wave should use and set token limits via environment variables.
|
|
63
|
-
```json
|
|
64
|
-
{
|
|
65
|
-
"env": {
|
|
66
|
-
"WAVE_MODEL": "gemini-3-flash",
|
|
67
|
-
"WAVE_FAST_MODEL": "gemini-2.5-flash",
|
|
68
|
-
"WAVE_MAX_INPUT_TOKENS": "100000",
|
|
69
|
-
"WAVE_MAX_OUTPUT_TOKENS": "4096"
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
### 6. Other Settings
|
|
75
|
-
- `language`: Preferred language for agent communication (e.g., `"en"`, `"zh"`).
|
|
76
|
-
- `autoMemoryEnabled`: Enable or disable auto-memory (default: `true`).
|
|
77
|
-
|
|
78
|
-
## How to use this skill
|
|
79
|
-
|
|
80
|
-
You can ask me to:
|
|
81
|
-
- "Show my current settings"
|
|
82
|
-
- "Update my project settings to enable auto-memory"
|
|
83
|
-
- "How do I configure a post-commit hook?"
|
|
84
|
-
- "What are the available permission modes?"
|
|
85
|
-
|
|
86
|
-
I will guide you through the process and ensure your configuration is valid.
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export interface ParsedLoop {
|
|
2
|
-
interval: string;
|
|
3
|
-
prompt: string;
|
|
4
|
-
originalInterval?: string;
|
|
5
|
-
roundedTo?: string;
|
|
6
|
-
}
|
|
7
|
-
export declare function parseLoopInput(input: string, defaultInterval?: string): ParsedLoop;
|
|
8
|
-
export declare function intervalToCron(interval: string): {
|
|
9
|
-
cron: string;
|
|
10
|
-
roundedTo?: string;
|
|
11
|
-
cadence: string;
|
|
12
|
-
};
|
|
13
|
-
//# sourceMappingURL=parsing.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"parsing.d.ts","sourceRoot":"","sources":["../../../src/builtin-skills/loop/parsing.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,eAAe,GAAE,MAAc,GAC9B,UAAU,CA8BZ;AAwBD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB,CA0FA"}
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
export function parseLoopInput(input, defaultInterval = "10m") {
|
|
2
|
-
const trimmedInput = input.trim();
|
|
3
|
-
if (!trimmedInput) {
|
|
4
|
-
return { interval: defaultInterval, prompt: "" };
|
|
5
|
-
}
|
|
6
|
-
// 1. Leading token: ^\d+[smhd]$
|
|
7
|
-
const tokens = trimmedInput.split(/\s+/);
|
|
8
|
-
if (tokens[0].match(/^\d+[smhd]$/)) {
|
|
9
|
-
const interval = tokens[0];
|
|
10
|
-
const prompt = tokens.slice(1).join(" ");
|
|
11
|
-
return { interval, prompt };
|
|
12
|
-
}
|
|
13
|
-
// 2. Trailing "every" clause: every <N><unit> or every <N> <unit-word>
|
|
14
|
-
// Units: s, m, h, d, second(s), minute(s), hour(s), day(s)
|
|
15
|
-
const everyRegex = /\s+every\s+(\d+)\s*(s|m|h|d|seconds?|minutes?|hours?|days?)$/i;
|
|
16
|
-
const match = trimmedInput.match(everyRegex);
|
|
17
|
-
if (match) {
|
|
18
|
-
const n = match[1];
|
|
19
|
-
const unitWord = match[2].toLowerCase();
|
|
20
|
-
const unit = unitWord[0];
|
|
21
|
-
const interval = `${n}${unit}`;
|
|
22
|
-
const prompt = trimmedInput.substring(0, match.index).trim();
|
|
23
|
-
return { interval, prompt };
|
|
24
|
-
}
|
|
25
|
-
// 3. Default
|
|
26
|
-
return { interval: defaultInterval, prompt: trimmedInput };
|
|
27
|
-
}
|
|
28
|
-
const CLEAN_MINUTES = [1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30];
|
|
29
|
-
const CLEAN_HOURS = [1, 2, 3, 4, 6, 8, 12];
|
|
30
|
-
function getNearestClean(value, cleanValues) {
|
|
31
|
-
let nearest = cleanValues[0];
|
|
32
|
-
let minDiff = Math.abs(value - nearest);
|
|
33
|
-
for (const clean of cleanValues) {
|
|
34
|
-
const diff = Math.abs(value - clean);
|
|
35
|
-
if (diff < minDiff) {
|
|
36
|
-
minDiff = diff;
|
|
37
|
-
nearest = clean;
|
|
38
|
-
}
|
|
39
|
-
else if (diff === minDiff) {
|
|
40
|
-
// If equal diff, prefer the larger one? Or smaller?
|
|
41
|
-
// Let's prefer the larger one to be less frequent
|
|
42
|
-
if (clean > nearest) {
|
|
43
|
-
nearest = clean;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
return nearest;
|
|
48
|
-
}
|
|
49
|
-
export function intervalToCron(interval) {
|
|
50
|
-
const match = interval.match(/^(\d+)([smhd])$/);
|
|
51
|
-
if (!match) {
|
|
52
|
-
throw new Error(`Invalid interval format: ${interval}`);
|
|
53
|
-
}
|
|
54
|
-
let n = parseInt(match[1], 10);
|
|
55
|
-
const unit = match[2];
|
|
56
|
-
let roundedTo;
|
|
57
|
-
if (unit === "s") {
|
|
58
|
-
const minutes = Math.ceil(n / 60);
|
|
59
|
-
const result = intervalToCron(`${minutes}m`);
|
|
60
|
-
return {
|
|
61
|
-
...result,
|
|
62
|
-
roundedTo: result.roundedTo || (minutes * 60 !== n ? `${minutes}m` : undefined),
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
if (unit === "m") {
|
|
66
|
-
if (n >= 60) {
|
|
67
|
-
const hours = Math.round(n / 60);
|
|
68
|
-
const result = intervalToCron(`${hours}h`);
|
|
69
|
-
return {
|
|
70
|
-
...result,
|
|
71
|
-
roundedTo: result.roundedTo || (hours * 60 !== n ? `${hours}h` : undefined),
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
if (!CLEAN_MINUTES.includes(n)) {
|
|
75
|
-
const nearest = getNearestClean(n, CLEAN_MINUTES);
|
|
76
|
-
roundedTo = `${nearest}m`;
|
|
77
|
-
n = nearest;
|
|
78
|
-
}
|
|
79
|
-
// For minutes, we use */N. Thundering herd is less of an issue for high frequency,
|
|
80
|
-
// but we could still offset it. However, the spec says "random minute for approximate requests like 'hourly'".
|
|
81
|
-
// So for minutes, we'll stick to */N.
|
|
82
|
-
return {
|
|
83
|
-
cron: `*/${n} * * * *`,
|
|
84
|
-
roundedTo,
|
|
85
|
-
cadence: `every ${n} minute${n > 1 ? "s" : ""}`,
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
if (unit === "h") {
|
|
89
|
-
if (n > 23) {
|
|
90
|
-
const days = Math.round(n / 24);
|
|
91
|
-
const result = intervalToCron(`${days}d`);
|
|
92
|
-
return {
|
|
93
|
-
...result,
|
|
94
|
-
roundedTo: result.roundedTo || (days * 24 !== n ? `${days}d` : undefined),
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
if (!CLEAN_HOURS.includes(n)) {
|
|
98
|
-
const nearest = getNearestClean(n, CLEAN_HOURS);
|
|
99
|
-
roundedTo = `${nearest}h`;
|
|
100
|
-
n = nearest;
|
|
101
|
-
}
|
|
102
|
-
// Thundering herd prevention: pick a random minute
|
|
103
|
-
const randomMinute = Math.floor(Math.random() * 60);
|
|
104
|
-
return {
|
|
105
|
-
cron: `${randomMinute} */${n} * * *`,
|
|
106
|
-
roundedTo,
|
|
107
|
-
cadence: `every ${n} hour${n > 1 ? "s" : ""}`,
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
if (unit === "d") {
|
|
111
|
-
if (![1, 2, 3, 4, 5, 6, 7, 10, 14, 30].includes(n)) {
|
|
112
|
-
// For days, we don't have a strict "clean" list in spec, but let's use some common ones if needed.
|
|
113
|
-
// Actually, cron supports any */N for days.
|
|
114
|
-
}
|
|
115
|
-
// Thundering herd prevention: pick a random minute and hour
|
|
116
|
-
const randomMinute = Math.floor(Math.random() * 60);
|
|
117
|
-
const randomHour = Math.floor(Math.random() * 24);
|
|
118
|
-
return {
|
|
119
|
-
cron: `${randomMinute} ${randomHour} */${n} * *`,
|
|
120
|
-
roundedTo,
|
|
121
|
-
cadence: `every ${n} day${n > 1 ? "s" : ""}`,
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
throw new Error(`Unsupported unit: ${unit}`);
|
|
125
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { SubagentConfiguration } from "./subagentParser.js";
|
|
2
|
-
/**
|
|
3
|
-
* Get all built-in subagent configurations
|
|
4
|
-
* Built-in subagents have priority 3 (lowest) and can be overridden by user/project configs
|
|
5
|
-
*/
|
|
6
|
-
export declare function getBuiltinSubagents(): SubagentConfiguration[];
|
|
7
|
-
//# sourceMappingURL=builtinSubagents.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"builtinSubagents.d.ts","sourceRoot":"","sources":["../../src/utils/builtinSubagents.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAEjE;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,qBAAqB,EAAE,CAQ7D"}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { BASH_SUBAGENT_TYPE, EXPLORE_SUBAGENT_TYPE, PLAN_SUBAGENT_TYPE, GENERAL_PURPOSE_SUBAGENT_TYPE, } from "../constants/subagents.js";
|
|
2
|
-
import { BASH_SUBAGENT_SYSTEM_PROMPT, GENERAL_PURPOSE_SYSTEM_PROMPT, PLAN_SUBAGENT_SYSTEM_PROMPT, EXPLORE_SUBAGENT_SYSTEM_PROMPT, } from "../prompts/index.js";
|
|
3
|
-
import { BASH_TOOL_NAME, GLOB_TOOL_NAME, GREP_TOOL_NAME, READ_TOOL_NAME, LSP_TOOL_NAME, } from "../constants/tools.js";
|
|
4
|
-
/**
|
|
5
|
-
* Get all built-in subagent configurations
|
|
6
|
-
* Built-in subagents have priority 3 (lowest) and can be overridden by user/project configs
|
|
7
|
-
*/
|
|
8
|
-
export function getBuiltinSubagents() {
|
|
9
|
-
return [
|
|
10
|
-
createBashSubagent(),
|
|
11
|
-
createExploreSubagent(),
|
|
12
|
-
createGeneralPurposeSubagent(),
|
|
13
|
-
createPlanSubagent(),
|
|
14
|
-
// Add more built-in subagents here as needed
|
|
15
|
-
];
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Create the Bash built-in subagent configuration
|
|
19
|
-
* Specialized for executing bash commands and git operations
|
|
20
|
-
*/
|
|
21
|
-
function createBashSubagent() {
|
|
22
|
-
return {
|
|
23
|
-
name: BASH_SUBAGENT_TYPE,
|
|
24
|
-
description: "Command execution specialist for running bash commands. Use this for git operations, command execution, and other terminal tasks.",
|
|
25
|
-
systemPrompt: BASH_SUBAGENT_SYSTEM_PROMPT,
|
|
26
|
-
tools: [BASH_TOOL_NAME],
|
|
27
|
-
model: "inherit",
|
|
28
|
-
filePath: `<builtin:${BASH_SUBAGENT_TYPE}>`,
|
|
29
|
-
scope: "builtin",
|
|
30
|
-
priority: 3,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Create the General-Purpose built-in subagent configuration
|
|
35
|
-
* Specialized for multi-step research and implementation tasks
|
|
36
|
-
*/
|
|
37
|
-
function createGeneralPurposeSubagent() {
|
|
38
|
-
return {
|
|
39
|
-
name: GENERAL_PURPOSE_SUBAGENT_TYPE,
|
|
40
|
-
description: "General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you.",
|
|
41
|
-
systemPrompt: GENERAL_PURPOSE_SYSTEM_PROMPT,
|
|
42
|
-
filePath: `<builtin:${GENERAL_PURPOSE_SUBAGENT_TYPE}>`,
|
|
43
|
-
scope: "builtin",
|
|
44
|
-
priority: 3,
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Create the Explore built-in subagent configuration
|
|
49
|
-
* Specialized for codebase exploration and file search tasks
|
|
50
|
-
*/
|
|
51
|
-
function createExploreSubagent() {
|
|
52
|
-
// Define allowed tools for read-only operations
|
|
53
|
-
const allowedTools = [
|
|
54
|
-
GLOB_TOOL_NAME,
|
|
55
|
-
GREP_TOOL_NAME,
|
|
56
|
-
READ_TOOL_NAME,
|
|
57
|
-
BASH_TOOL_NAME,
|
|
58
|
-
LSP_TOOL_NAME,
|
|
59
|
-
];
|
|
60
|
-
return {
|
|
61
|
-
name: EXPLORE_SUBAGENT_TYPE,
|
|
62
|
-
description: 'Fast agent specialized for exploring codebases. Use this when you need to quickly find files by patterns (eg. "src/components/**/*.tsx"), search code for keywords (eg. "API endpoints"), or answer questions about the codebase (eg. "how do API endpoints work?"). When calling this agent, specify the desired thoroughness level: "quick" for basic searches, "medium" for moderate exploration, or "very thorough" for comprehensive analysis across multiple locations and naming conventions.',
|
|
63
|
-
systemPrompt: EXPLORE_SUBAGENT_SYSTEM_PROMPT,
|
|
64
|
-
tools: allowedTools,
|
|
65
|
-
model: "fastModel", // Special value that will use parent's fastModel
|
|
66
|
-
filePath: `<builtin:${EXPLORE_SUBAGENT_TYPE}>`,
|
|
67
|
-
scope: "builtin",
|
|
68
|
-
priority: 3, // Lowest priority - can be overridden by user configs
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Create the Plan built-in subagent configuration
|
|
73
|
-
* Specialized for designing implementation plans and exploring codebases in read-only mode
|
|
74
|
-
*/
|
|
75
|
-
function createPlanSubagent() {
|
|
76
|
-
// Define allowed tools for read-only operations
|
|
77
|
-
const allowedTools = [
|
|
78
|
-
GLOB_TOOL_NAME,
|
|
79
|
-
GREP_TOOL_NAME,
|
|
80
|
-
READ_TOOL_NAME,
|
|
81
|
-
BASH_TOOL_NAME,
|
|
82
|
-
LSP_TOOL_NAME,
|
|
83
|
-
];
|
|
84
|
-
return {
|
|
85
|
-
name: PLAN_SUBAGENT_TYPE,
|
|
86
|
-
description: "Software architect agent for designing implementation plans. Use this when you need to plan the implementation strategy for a task. Returns step-by-step plans, identifies critical files, and considers architectural trade-offs.",
|
|
87
|
-
systemPrompt: PLAN_SUBAGENT_SYSTEM_PROMPT,
|
|
88
|
-
tools: allowedTools,
|
|
89
|
-
model: "inherit", // Uses parent agent's model
|
|
90
|
-
filePath: `<builtin:${PLAN_SUBAGENT_TYPE}>`,
|
|
91
|
-
scope: "builtin",
|
|
92
|
-
priority: 3, // Lowest priority - can be overridden by user configs
|
|
93
|
-
};
|
|
94
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: loop
|
|
3
|
-
description: Parses user input into an interval and prompt, converts the interval to a cron expression, and schedules a recurring task
|
|
4
|
-
allowed-tools: CronCreate, Skill
|
|
5
|
-
---
|
|
6
|
-
# /loop — schedule a recurring prompt
|
|
7
|
-
|
|
8
|
-
Parse the input below into `[interval] <prompt…>` and schedule it with CronCreate.
|
|
9
|
-
|
|
10
|
-
## Parsing (in priority order)
|
|
11
|
-
|
|
12
|
-
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.
|
|
13
|
-
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.
|
|
14
|
-
3. **Default**: otherwise, interval is `10m` and the entire input is the prompt.
|
|
15
|
-
|
|
16
|
-
If the resulting prompt is empty, show usage `/loop [interval] <prompt>` and stop — do not call CronCreate.
|
|
17
|
-
|
|
18
|
-
Examples:
|
|
19
|
-
- `5m /babysit-prs` → interval `5m`, prompt `/babysit-prs` (rule 1)
|
|
20
|
-
- `check the deploy every 20m` → interval `20m`, prompt `check the deploy` (rule 2)
|
|
21
|
-
- `run tests every 5 minutes` → interval `5m`, prompt `run tests` (rule 2)
|
|
22
|
-
- `check the deploy` → interval `10m`, prompt `check the deploy` (rule 3)
|
|
23
|
-
- `check every PR` → interval `10m`, prompt `check every PR` (rule 3 — "every" not followed by time)
|
|
24
|
-
- `5m` → empty prompt → show usage
|
|
25
|
-
|
|
26
|
-
## Interval → cron
|
|
27
|
-
|
|
28
|
-
Supported suffixes: `s` (seconds, rounded up to nearest minute, min 1), `m` (minutes), `h` (hours), `d` (days). Convert:
|
|
29
|
-
|
|
30
|
-
| Interval pattern | Cron expression | Notes |
|
|
31
|
-
|-----------------------|---------------------|------------------------------------------|
|
|
32
|
-
| `Nm` where N ≤ 59 | `*/N * * * *` | every N minutes |
|
|
33
|
-
| `Nm` where N ≥ 60 | `0 */H * * *` | round to hours (H = N/60, must divide 24)|
|
|
34
|
-
| `Nh` where N ≤ 23 | `0 */N * * *` | every N hours |
|
|
35
|
-
| `Nd` | `0 0 */N * *` | every N days at midnight local |
|
|
36
|
-
| `Ns` | treat as `ceil(N/60)m` | cron minimum granularity is 1 minute |
|
|
37
|
-
|
|
38
|
-
**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.
|
|
39
|
-
|
|
40
|
-
**Thundering herd prevention**: For approximate requests like "hourly" or "every morning", pick a random minute (e.g., 7) instead of 0 to avoid global sync issues.
|
|
41
|
-
|
|
42
|
-
## Action
|
|
43
|
-
|
|
44
|
-
1. Call CronCreate with:
|
|
45
|
-
- `cron`: the expression from the table above
|
|
46
|
-
- `prompt`: the parsed prompt from above, verbatim (slash commands are passed through unchanged)
|
|
47
|
-
- `recurring`: `true`
|
|
48
|
-
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).
|
|
49
|
-
3. **Then immediately execute the parsed prompt now** — don't wait for the first cron fire. If it's a slash command, invoke it via the Skill tool; otherwise act on it directly.
|
|
50
|
-
|
|
51
|
-
## Input
|
|
52
|
-
|
|
53
|
-
$ARGUMENTS
|