wave-agent-sdk 1.0.4 → 1.0.6
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/dist/agent.d.ts +23 -1
- package/dist/agent.js +34 -4
- package/dist/managers/aiManager.d.ts +36 -0
- package/dist/managers/aiManager.js +240 -8
- package/dist/managers/messageManager.d.ts +1 -3
- package/dist/managers/messageManager.js +19 -9
- package/dist/managers/slashCommandManager.js +28 -0
- package/dist/managers/subagentManager.d.ts +0 -2
- package/dist/services/session.js +2 -9
- package/dist/telemetry/instrumentation.d.ts +1 -1
- package/dist/telemetry/instrumentation.js +57 -28
- package/dist/tools/bashTool.js +16 -6
- package/dist/tools/grepTool.js +11 -3
- package/dist/types/index.d.ts +3 -0
- package/dist/types/messaging.d.ts +2 -0
- package/dist/utils/containerSetup.js +0 -3
- package/dist/utils/messageOperations.d.ts +10 -1
- package/dist/utils/messageOperations.js +19 -1
- package/dist/utils/notificationXml.js +5 -0
- package/dist/utils/ripgrep.d.ts +4 -4
- package/dist/utils/ripgrep.js +4 -27
- package/package.json +20 -8
- package/bin/rg +0 -79
- package/dist/managers/forkedAgentManager.d.ts +0 -50
- package/dist/managers/forkedAgentManager.js +0 -130
- package/scripts/install_ripgrep.js +0 -111
- package/scripts/postinstall.js +0 -38
- package/src/agent.ts +0 -1271
- package/src/constants/subagents.ts +0 -4
- package/src/constants/toolLimits.ts +0 -15
- package/src/constants/tools.ts +0 -24
- package/src/core/plugin.ts +0 -237
- package/src/core/session.ts +0 -9
- package/src/index.ts +0 -38
- package/src/managers/MemoryRuleManager.ts +0 -198
- package/src/managers/aiManager.ts +0 -2429
- package/src/managers/backgroundTaskManager.ts +0 -480
- package/src/managers/bangManager.ts +0 -119
- package/src/managers/cronManager.ts +0 -401
- package/src/managers/foregroundTaskManager.ts +0 -30
- package/src/managers/forkedAgentManager.ts +0 -216
- package/src/managers/hookManager.ts +0 -1089
- package/src/managers/liveConfigManager.ts +0 -445
- package/src/managers/lspManager.ts +0 -470
- package/src/managers/mcpManager.ts +0 -1010
- package/src/managers/messageManager.ts +0 -1180
- package/src/managers/messageQueue.ts +0 -157
- package/src/managers/permissionManager.ts +0 -1140
- package/src/managers/planManager.ts +0 -112
- package/src/managers/pluginManager.ts +0 -330
- package/src/managers/pluginScopeManager.ts +0 -124
- package/src/managers/reversionManager.ts +0 -178
- package/src/managers/skillManager.ts +0 -578
- package/src/managers/slashCommandManager.ts +0 -564
- package/src/managers/subagentManager.ts +0 -915
- package/src/managers/toolManager.ts +0 -489
- package/src/managers/workflowManager.ts +0 -493
- package/src/prompts/autoMemory.ts +0 -33
- package/src/prompts/autoMemoryExtraction.ts +0 -146
- package/src/prompts/index.ts +0 -506
- package/src/prompts/planModeReminders.ts +0 -126
- package/src/services/GitService.ts +0 -131
- package/src/services/MarketplaceService.ts +0 -1052
- package/src/services/MemoryRuleService.ts +0 -71
- package/src/services/aiService.ts +0 -919
- package/src/services/authService.ts +0 -565
- package/src/services/autoMemoryService.ts +0 -280
- package/src/services/configurationService.ts +0 -1520
- package/src/services/fileWatcher.ts +0 -319
- package/src/services/hook.ts +0 -348
- package/src/services/initializationService.ts +0 -357
- package/src/services/interactionService.ts +0 -229
- package/src/services/jsonlHandler.ts +0 -295
- package/src/services/memory.ts +0 -234
- package/src/services/pluginLoader.ts +0 -252
- package/src/services/remoteSettingsService.ts +0 -366
- package/src/services/reversionService.ts +0 -122
- package/src/services/session.ts +0 -922
- package/src/services/taskManager.ts +0 -321
- package/src/telemetry/events.ts +0 -65
- package/src/telemetry/instrumentation.ts +0 -499
- package/src/telemetry/sessionTracing.ts +0 -348
- package/src/tools/agentTool.ts +0 -322
- package/src/tools/askUserQuestion.ts +0 -160
- package/src/tools/bashTool.ts +0 -621
- package/src/tools/buildTool.ts +0 -61
- package/src/tools/cronCreateTool.ts +0 -161
- package/src/tools/cronDeleteTool.ts +0 -51
- package/src/tools/cronListTool.ts +0 -47
- package/src/tools/editTool.ts +0 -313
- package/src/tools/enterPlanMode.ts +0 -124
- package/src/tools/enterWorktreeTool.ts +0 -190
- package/src/tools/exitPlanMode.ts +0 -139
- package/src/tools/exitWorktreeTool.ts +0 -236
- package/src/tools/globTool.ts +0 -167
- package/src/tools/grepTool.ts +0 -399
- package/src/tools/lspTool.ts +0 -883
- package/src/tools/readTool.ts +0 -426
- package/src/tools/skillTool.ts +0 -248
- package/src/tools/taskManagementTools.ts +0 -574
- package/src/tools/taskStopTool.ts +0 -78
- package/src/tools/types.ts +0 -137
- package/src/tools/webFetchTool.ts +0 -373
- package/src/tools/workflowTool.ts +0 -205
- package/src/tools/writeTool.ts +0 -267
- package/src/types/agent.ts +0 -128
- package/src/types/auth.ts +0 -19
- package/src/types/commands.ts +0 -30
- package/src/types/config.ts +0 -43
- package/src/types/configuration.ts +0 -168
- package/src/types/core.ts +0 -105
- package/src/types/cron.ts +0 -13
- package/src/types/environment.ts +0 -106
- package/src/types/fileSearch.ts +0 -4
- package/src/types/history.ts +0 -7
- package/src/types/hooks.ts +0 -256
- package/src/types/index.ts +0 -41
- package/src/types/lsp.ts +0 -98
- package/src/types/marketplace.ts +0 -71
- package/src/types/mcp.ts +0 -43
- package/src/types/memoryRule.ts +0 -31
- package/src/types/messaging.ts +0 -114
- package/src/types/permissions.ts +0 -80
- package/src/types/plugins.ts +0 -39
- package/src/types/processes.ts +0 -99
- package/src/types/reversion.ts +0 -29
- package/src/types/session.ts +0 -12
- package/src/types/skills.ts +0 -108
- package/src/types/tasks.ts +0 -13
- package/src/types/telemetry.ts +0 -98
- package/src/types/tools.ts +0 -44
- package/src/types/workflow.ts +0 -6
- package/src/utils/abortUtils.ts +0 -118
- package/src/utils/atomicWrite.ts +0 -61
- package/src/utils/bashParser.ts +0 -899
- package/src/utils/cacheControlUtils.ts +0 -426
- package/src/utils/commandArgumentParser.ts +0 -161
- package/src/utils/commandPathResolver.ts +0 -88
- package/src/utils/configPaths.ts +0 -220
- package/src/utils/configValidator.ts +0 -100
- package/src/utils/constants.ts +0 -39
- package/src/utils/container.ts +0 -92
- package/src/utils/containerSetup.ts +0 -371
- package/src/utils/convertMessagesForAPI.ts +0 -326
- package/src/utils/cronTasks.ts +0 -128
- package/src/utils/cronTasksLock.ts +0 -182
- package/src/utils/cronToHuman.ts +0 -99
- package/src/utils/customCommands.ts +0 -95
- package/src/utils/editUtils.ts +0 -20
- package/src/utils/fileFormat.ts +0 -40
- package/src/utils/fileSearch.ts +0 -151
- package/src/utils/fileUtils.ts +0 -223
- package/src/utils/gitUtils.ts +0 -315
- package/src/utils/globalLogger.ts +0 -128
- package/src/utils/groupMessagesByApiRound.ts +0 -121
- package/src/utils/hookMatcher.ts +0 -176
- package/src/utils/markdownParser.ts +0 -317
- package/src/utils/mcpUtils.ts +0 -138
- package/src/utils/messageOperations.ts +0 -641
- package/src/utils/modelCapabilities.ts +0 -30
- package/src/utils/nameGenerator.ts +0 -99
- package/src/utils/notificationXml.ts +0 -57
- package/src/utils/openaiClient.ts +0 -251
- package/src/utils/parseCronExpression.ts +0 -78
- package/src/utils/path.ts +0 -72
- package/src/utils/pathEncoder.ts +0 -400
- package/src/utils/pathSafety.ts +0 -40
- package/src/utils/promptHistory.ts +0 -168
- package/src/utils/ripgrep.ts +0 -35
- package/src/utils/shellResolver.ts +0 -190
- package/src/utils/skillParser.ts +0 -246
- package/src/utils/stringUtils.ts +0 -156
- package/src/utils/subagentParser.ts +0 -279
- package/src/utils/taskReminder.ts +0 -96
- package/src/utils/tokenCalculation.ts +0 -43
- package/src/utils/tokenEstimate.ts +0 -34
- package/src/utils/toolResultStorage.ts +0 -117
- package/src/utils/worktreeSession.ts +0 -26
- package/src/utils/worktreeUtils.ts +0 -786
- package/src/workflow/budgetTracker.ts +0 -34
- package/src/workflow/concurrencyLimiter.ts +0 -47
- package/src/workflow/journal.ts +0 -95
- package/src/workflow/progressReporter.ts +0 -141
- package/src/workflow/runState.ts +0 -65
- package/src/workflow/scriptRuntime.ts +0 -274
- package/src/workflow/structuredOutput.ts +0 -123
- package/src/workflow/types.ts +0 -101
- package/src/workflow/workflowApis.ts +0 -410
- package/vendor/ripgrep/linux-aarch64/rg +0 -0
- package/vendor/ripgrep/linux-x86_64/rg +0 -0
- package/vendor/ripgrep/macos-aarch64/rg +0 -0
- package/vendor/ripgrep/macos-x86_64/rg +0 -0
- package/vendor/ripgrep/windows-aarch64/rg.exe +0 -0
- package/vendor/ripgrep/windows-x86_64/rg.exe +0 -0
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import { execFileSync } from "child_process";
|
|
4
|
-
|
|
5
|
-
export const WINDOWS_GIT_BASH_PATHS = [
|
|
6
|
-
"C:\\Program Files\\Git\\bin\\bash.exe",
|
|
7
|
-
"C:\\Program Files (x86)\\Git\\bin\\bash.exe",
|
|
8
|
-
];
|
|
9
|
-
|
|
10
|
-
// Common git.exe locations (checked before falling back to `where.exe`).
|
|
11
|
-
const WINDOWS_GIT_EXE_PATHS = [
|
|
12
|
-
"C:\\Program Files\\Git\\cmd\\git.exe",
|
|
13
|
-
"C:\\Program Files (x86)\\Git\\cmd\\git.exe",
|
|
14
|
-
];
|
|
15
|
-
|
|
16
|
-
// Common directories where bash/zsh may live, used as fallback after PATH lookup.
|
|
17
|
-
const FIXED_SHELL_DIRS = [
|
|
18
|
-
"/bin",
|
|
19
|
-
"/usr/bin",
|
|
20
|
-
"/usr/local/bin",
|
|
21
|
-
"/opt/homebrew/bin",
|
|
22
|
-
];
|
|
23
|
-
|
|
24
|
-
function isExecutable(shellPath: string): boolean {
|
|
25
|
-
try {
|
|
26
|
-
fs.accessSync(shellPath, fs.constants.X_OK);
|
|
27
|
-
return true;
|
|
28
|
-
} catch {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** Resolve a command name (e.g. "bash") to an absolute path by searching $PATH. */
|
|
34
|
-
function which(command: string): string | undefined {
|
|
35
|
-
const pathEnv = process.env.PATH;
|
|
36
|
-
if (!pathEnv) return undefined;
|
|
37
|
-
for (const dir of pathEnv.split(":")) {
|
|
38
|
-
if (!dir) continue;
|
|
39
|
-
const candidate = path.join(dir, command);
|
|
40
|
-
if (isExecutable(candidate)) {
|
|
41
|
-
return candidate;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return undefined;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function isSupportedShell(shellPath: string): boolean {
|
|
48
|
-
return shellPath.includes("bash") || shellPath.includes("zsh");
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Locate the `git` executable on Windows, then infer bash.exe from it.
|
|
53
|
-
* Checks common Git for Windows install locations first, then falls back
|
|
54
|
-
* to `where.exe git` (aligned with Claude Code's findExecutable('git')).
|
|
55
|
-
* Returns the inferred bash.exe path, or undefined if not found.
|
|
56
|
-
*/
|
|
57
|
-
function inferGitBashFromGit(): string | undefined {
|
|
58
|
-
// 1. Common git.exe locations
|
|
59
|
-
let gitExe: string | undefined;
|
|
60
|
-
for (const candidate of WINDOWS_GIT_EXE_PATHS) {
|
|
61
|
-
if (fs.existsSync(candidate)) {
|
|
62
|
-
gitExe = candidate;
|
|
63
|
-
break;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// 2. Fallback: where.exe git
|
|
68
|
-
if (!gitExe) {
|
|
69
|
-
try {
|
|
70
|
-
const result = execFileSync("where", ["git"], {
|
|
71
|
-
stdio: "pipe",
|
|
72
|
-
encoding: "utf8",
|
|
73
|
-
timeout: 3000,
|
|
74
|
-
}).trim();
|
|
75
|
-
// where.exe may return multiple lines; take the first non-empty one.
|
|
76
|
-
gitExe = result.split(/\r?\n/).find((line) => line.trim()) || undefined;
|
|
77
|
-
} catch {
|
|
78
|
-
gitExe = undefined;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (!gitExe) return undefined;
|
|
83
|
-
|
|
84
|
-
// git.exe is at <install>/cmd/git.exe → bash.exe is at <install>/bin/bash.exe
|
|
85
|
-
// join treats git.exe as a segment, so ".." cancels it (→ cmd dir),
|
|
86
|
-
// the second ".." cancels cmd (→ <install>), then bin/bash.exe.
|
|
87
|
-
const bashPath = path.win32.join(gitExe, "..", "..", "bin", "bash.exe");
|
|
88
|
-
if (fs.existsSync(bashPath)) {
|
|
89
|
-
return bashPath;
|
|
90
|
-
}
|
|
91
|
-
return undefined;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Resolve a bash or zsh binary on macOS/Linux.
|
|
96
|
-
*
|
|
97
|
-
* Priority (aligned with Claude Code's findSuitableShell):
|
|
98
|
-
* 1. WAVE_SHELL env var (only if it points to an executable bash/zsh)
|
|
99
|
-
* 2. $SHELL (only if it is bash/zsh)
|
|
100
|
-
* 3. `which bash` / `which zsh` discovered on $PATH
|
|
101
|
-
* 4. Common fixed locations (/bin, /usr/bin, /usr/local/bin, /opt/homebrew/bin)
|
|
102
|
-
*
|
|
103
|
-
* When $SHELL is bash, bash is preferred; otherwise zsh is preferred. This
|
|
104
|
-
* matters because /bin/sh may be dash (Debian/Ubuntu) or POSIX-mode bash
|
|
105
|
-
* (macOS), neither of which supports bashisms like process substitution
|
|
106
|
-
* `<()`, causing `eval '<()...'` to fail at parse time.
|
|
107
|
-
*/
|
|
108
|
-
function resolveUnixShell(): string | undefined {
|
|
109
|
-
// 1. WAVE_SHELL env override
|
|
110
|
-
const waveShell = process.env.WAVE_SHELL;
|
|
111
|
-
if (waveShell && isSupportedShell(waveShell) && isExecutable(waveShell)) {
|
|
112
|
-
return waveShell;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// 2. $SHELL (only bash/zsh)
|
|
116
|
-
const envShell = process.env.SHELL;
|
|
117
|
-
const isEnvShellSupported = !!envShell && isSupportedShell(envShell);
|
|
118
|
-
const preferBash = envShell?.includes("bash") ?? false;
|
|
119
|
-
|
|
120
|
-
// 3. Locate via PATH
|
|
121
|
-
const bashPath = which("bash");
|
|
122
|
-
const zshPath = which("zsh");
|
|
123
|
-
|
|
124
|
-
// 4. Fixed common locations, ordered by user preference
|
|
125
|
-
const shellOrder = preferBash ? ["bash", "zsh"] : ["zsh", "bash"];
|
|
126
|
-
const candidates = shellOrder.flatMap((shell) =>
|
|
127
|
-
FIXED_SHELL_DIRS.map((dir) => `${dir}/${shell}`),
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
// Discovered PATH paths: preferred type first, the other as fallback
|
|
131
|
-
if (preferBash) {
|
|
132
|
-
if (bashPath) candidates.unshift(bashPath);
|
|
133
|
-
if (zshPath) candidates.push(zshPath);
|
|
134
|
-
} else {
|
|
135
|
-
if (zshPath) candidates.unshift(zshPath);
|
|
136
|
-
if (bashPath) candidates.push(bashPath);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Always prioritize $SHELL if it is a supported, executable shell
|
|
140
|
-
if (isEnvShellSupported && envShell && isExecutable(envShell)) {
|
|
141
|
-
candidates.unshift(envShell);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return candidates.find((candidate) => isExecutable(candidate));
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Resolve the Git Bash path on Windows.
|
|
149
|
-
*
|
|
150
|
-
* Priority (aligned with Claude Code's findGitBashPath):
|
|
151
|
-
* 1. WAVE_GIT_BASH_PATH env var
|
|
152
|
-
* 2. Infer from `git` executable location (<git dir>/../../bin/bash.exe)
|
|
153
|
-
* 3. Common install paths (C:\Program Files\Git\bin\bash.exe, etc.)
|
|
154
|
-
*/
|
|
155
|
-
function resolveWindowsShell(): string | undefined {
|
|
156
|
-
// 1. Env var override
|
|
157
|
-
if (process.env.WAVE_GIT_BASH_PATH) {
|
|
158
|
-
return process.env.WAVE_GIT_BASH_PATH;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// 2. Infer from git executable location
|
|
162
|
-
const inferred = inferGitBashFromGit();
|
|
163
|
-
if (inferred) {
|
|
164
|
-
return inferred;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// 4. Common install paths
|
|
168
|
-
const paths = [
|
|
169
|
-
...WINDOWS_GIT_BASH_PATHS,
|
|
170
|
-
process.env.LOCALAPPDATA
|
|
171
|
-
? `${process.env.LOCALAPPDATA}\\Programs\\Git\\bin\\bash.exe`
|
|
172
|
-
: null,
|
|
173
|
-
].filter(Boolean) as string[];
|
|
174
|
-
|
|
175
|
-
for (const p of paths) {
|
|
176
|
-
if (fs.existsSync(p)) {
|
|
177
|
-
return p;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return undefined;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
export function resolveShellPath(): string | undefined {
|
|
185
|
-
if (process.platform === "win32") {
|
|
186
|
-
return resolveWindowsShell();
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
return resolveUnixShell();
|
|
190
|
-
}
|
package/src/utils/skillParser.ts
DELETED
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "fs";
|
|
2
|
-
import { dirname } from "path";
|
|
3
|
-
import type {
|
|
4
|
-
ParsedSkillFile,
|
|
5
|
-
SkillParseOptions,
|
|
6
|
-
SkillFrontmatter,
|
|
7
|
-
SkillMetadata,
|
|
8
|
-
} from "../types/index.js";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Parse a SKILL.md file and validate its contents
|
|
12
|
-
*/
|
|
13
|
-
export function parseSkillFile(
|
|
14
|
-
filePath: string,
|
|
15
|
-
options: SkillParseOptions = {},
|
|
16
|
-
): ParsedSkillFile {
|
|
17
|
-
const { validateMetadata = true, basePath } = options;
|
|
18
|
-
|
|
19
|
-
const result: ParsedSkillFile = {
|
|
20
|
-
frontmatter: { name: "", description: "" },
|
|
21
|
-
content: "",
|
|
22
|
-
skillMetadata: {
|
|
23
|
-
name: "",
|
|
24
|
-
description: "",
|
|
25
|
-
type: "personal",
|
|
26
|
-
skillPath: "",
|
|
27
|
-
},
|
|
28
|
-
validationErrors: [],
|
|
29
|
-
isValid: false,
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
// Read file content
|
|
34
|
-
const content = readFileSync(filePath, "utf-8");
|
|
35
|
-
result.content = content;
|
|
36
|
-
|
|
37
|
-
// Parse YAML frontmatter
|
|
38
|
-
const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
39
|
-
if (!frontmatterMatch) {
|
|
40
|
-
result.validationErrors.push("Missing YAML frontmatter");
|
|
41
|
-
return result;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const yamlContent = frontmatterMatch[1];
|
|
45
|
-
result.frontmatter = parseYamlFrontmatter(yamlContent);
|
|
46
|
-
|
|
47
|
-
if (!result.frontmatter.name || !result.frontmatter.description) {
|
|
48
|
-
result.validationErrors.push(
|
|
49
|
-
"Missing required fields: name and description",
|
|
50
|
-
);
|
|
51
|
-
return result;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Determine skill type and path
|
|
55
|
-
const skillPath = basePath || dirname(filePath);
|
|
56
|
-
const skillType =
|
|
57
|
-
skillPath.includes("/.wave/skills") ||
|
|
58
|
-
skillPath.includes("\\.wave\\skills") ||
|
|
59
|
-
skillPath.includes("/.claude/skills") ||
|
|
60
|
-
skillPath.includes("\\.claude\\skills") ||
|
|
61
|
-
skillPath.includes("/.agents/skills") ||
|
|
62
|
-
skillPath.includes("\\.agents\\skills")
|
|
63
|
-
? "project"
|
|
64
|
-
: "personal";
|
|
65
|
-
|
|
66
|
-
// Extract allowed tools
|
|
67
|
-
let allowedTools: string[] | undefined;
|
|
68
|
-
const rawAllowedTools = result.frontmatter["allowed-tools"];
|
|
69
|
-
if (Array.isArray(rawAllowedTools)) {
|
|
70
|
-
allowedTools = rawAllowedTools.map((t) => String(t).trim());
|
|
71
|
-
} else if (typeof rawAllowedTools === "string") {
|
|
72
|
-
allowedTools = rawAllowedTools
|
|
73
|
-
.split(",")
|
|
74
|
-
.map((t) => t.trim())
|
|
75
|
-
.filter((t) => t.length > 0);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Extract disable-model-invocation
|
|
79
|
-
const disableModelInvocation =
|
|
80
|
-
result.frontmatter["disable-model-invocation"] === true ||
|
|
81
|
-
result.frontmatter["disable-model-invocation"] === "true";
|
|
82
|
-
|
|
83
|
-
// Extract user-invocable (default to true)
|
|
84
|
-
const userInvocable =
|
|
85
|
-
result.frontmatter["user-invocable"] === undefined ||
|
|
86
|
-
result.frontmatter["user-invocable"] === true ||
|
|
87
|
-
result.frontmatter["user-invocable"] === "true";
|
|
88
|
-
|
|
89
|
-
result.skillMetadata = {
|
|
90
|
-
name: result.frontmatter.name,
|
|
91
|
-
description: result.frontmatter.description,
|
|
92
|
-
type: skillType,
|
|
93
|
-
skillPath,
|
|
94
|
-
allowedTools,
|
|
95
|
-
context: result.frontmatter.context,
|
|
96
|
-
agent: result.frontmatter.agent,
|
|
97
|
-
model: result.frontmatter.model as string | undefined,
|
|
98
|
-
disableModelInvocation,
|
|
99
|
-
userInvocable,
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
// Validate metadata if requested
|
|
103
|
-
if (validateMetadata) {
|
|
104
|
-
const validationErrors = validateSkillMetadata(result.skillMetadata);
|
|
105
|
-
result.validationErrors.push(...validationErrors);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
result.isValid = result.validationErrors.length === 0;
|
|
109
|
-
return result;
|
|
110
|
-
} catch (error) {
|
|
111
|
-
result.validationErrors.push(
|
|
112
|
-
`Failed to read skill file: ${error instanceof Error ? error.message : String(error)}`,
|
|
113
|
-
);
|
|
114
|
-
return result;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Simple YAML frontmatter parser for skill files
|
|
120
|
-
*/
|
|
121
|
-
function parseYamlFrontmatter(yamlContent: string): SkillFrontmatter {
|
|
122
|
-
const frontmatter: SkillFrontmatter = { name: "", description: "" };
|
|
123
|
-
|
|
124
|
-
try {
|
|
125
|
-
const lines = yamlContent.split("\n");
|
|
126
|
-
let currentKey: string | null = null;
|
|
127
|
-
|
|
128
|
-
for (const line of lines) {
|
|
129
|
-
const trimmed = line.trim();
|
|
130
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
131
|
-
|
|
132
|
-
// Check for list item
|
|
133
|
-
if (trimmed.startsWith("-") && currentKey) {
|
|
134
|
-
const value = trimmed
|
|
135
|
-
.substring(1)
|
|
136
|
-
.trim()
|
|
137
|
-
.replace(/^["']|["']$/g, "");
|
|
138
|
-
if (value) {
|
|
139
|
-
const existing = frontmatter[currentKey];
|
|
140
|
-
if (Array.isArray(existing)) {
|
|
141
|
-
(existing as string[]).push(value);
|
|
142
|
-
} else {
|
|
143
|
-
frontmatter[currentKey] = [value];
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const colonIndex = trimmed.indexOf(":");
|
|
150
|
-
if (colonIndex === -1) continue;
|
|
151
|
-
|
|
152
|
-
const key = trimmed.substring(0, colonIndex).trim();
|
|
153
|
-
const value = trimmed
|
|
154
|
-
.substring(colonIndex + 1)
|
|
155
|
-
.trim()
|
|
156
|
-
.replace(/^["']|["']$/g, "");
|
|
157
|
-
|
|
158
|
-
currentKey = key;
|
|
159
|
-
if (key && value) {
|
|
160
|
-
frontmatter[key] = value;
|
|
161
|
-
} else if (key) {
|
|
162
|
-
// Key with no value on the same line, might be a list
|
|
163
|
-
frontmatter[key] = [];
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
} catch {
|
|
167
|
-
// Return empty frontmatter on parse error - validation will catch missing fields
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return frontmatter;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Validate skill metadata according to requirements
|
|
175
|
-
*/
|
|
176
|
-
export function validateSkillMetadata(metadata: SkillMetadata): string[] {
|
|
177
|
-
const errors: string[] = [];
|
|
178
|
-
|
|
179
|
-
// Import SKILL_DEFAULTS dynamically to avoid circular imports
|
|
180
|
-
const NAME_PATTERN = /^[a-z0-9-]+$/;
|
|
181
|
-
const MAX_NAME_LENGTH = 64;
|
|
182
|
-
const MAX_DESCRIPTION_LENGTH = 1024;
|
|
183
|
-
const MIN_DESCRIPTION_LENGTH = 1;
|
|
184
|
-
|
|
185
|
-
// Validate name
|
|
186
|
-
if (!metadata.name) {
|
|
187
|
-
errors.push("Skill name is required");
|
|
188
|
-
} else {
|
|
189
|
-
if (metadata.name.length > MAX_NAME_LENGTH) {
|
|
190
|
-
errors.push(`Skill name must be ${MAX_NAME_LENGTH} characters or less`);
|
|
191
|
-
}
|
|
192
|
-
if (!NAME_PATTERN.test(metadata.name)) {
|
|
193
|
-
errors.push(
|
|
194
|
-
"Skill name must contain only lowercase letters, numbers, and hyphens",
|
|
195
|
-
);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Validate description
|
|
200
|
-
if (!metadata.description) {
|
|
201
|
-
errors.push("Skill description is required");
|
|
202
|
-
} else {
|
|
203
|
-
if (metadata.description.length < MIN_DESCRIPTION_LENGTH) {
|
|
204
|
-
errors.push(
|
|
205
|
-
`Skill description must be at least ${MIN_DESCRIPTION_LENGTH} character`,
|
|
206
|
-
);
|
|
207
|
-
}
|
|
208
|
-
if (metadata.description.length > MAX_DESCRIPTION_LENGTH) {
|
|
209
|
-
errors.push(
|
|
210
|
-
`Skill description must be ${MAX_DESCRIPTION_LENGTH} characters or less`,
|
|
211
|
-
);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return errors;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Check if a skill name is valid format
|
|
220
|
-
*/
|
|
221
|
-
export function isValidSkillName(name: string): boolean {
|
|
222
|
-
const NAME_PATTERN = /^[a-z0-9-]+$/;
|
|
223
|
-
const MAX_NAME_LENGTH = 64;
|
|
224
|
-
|
|
225
|
-
return (
|
|
226
|
-
name.length > 0 && name.length <= MAX_NAME_LENGTH && NAME_PATTERN.test(name)
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Generate user-friendly error messages for skill validation
|
|
232
|
-
*/
|
|
233
|
-
export function formatSkillError(skillPath: string, errors: string[]): string {
|
|
234
|
-
const header = `Skill validation failed for ${skillPath}:`;
|
|
235
|
-
const errorList = errors.map((error) => ` • ${error}`).join("\n");
|
|
236
|
-
|
|
237
|
-
const suggestions = [
|
|
238
|
-
"To fix this skill:",
|
|
239
|
-
" 1. Ensure SKILL.md has valid YAML frontmatter (---...---)",
|
|
240
|
-
" 2. Include required fields: name and description",
|
|
241
|
-
" 3. Use lowercase letters, numbers, and hyphens only for name",
|
|
242
|
-
" 4. Keep name under 64 characters and description under 1024 characters",
|
|
243
|
-
].join("\n");
|
|
244
|
-
|
|
245
|
-
return `${header}\n${errorList}\n\n${suggestions}`;
|
|
246
|
-
}
|
package/src/utils/stringUtils.ts
DELETED
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Remove code block wrappers
|
|
3
|
-
* @param content Content that may contain code block wrappers
|
|
4
|
-
* @returns Content after removing wrappers
|
|
5
|
-
*/
|
|
6
|
-
export function removeCodeBlockWrappers(content: string): string {
|
|
7
|
-
// Remove code block wrappers from beginning and end
|
|
8
|
-
// Supports the following formats:
|
|
9
|
-
// ```language
|
|
10
|
-
// code content
|
|
11
|
-
// ```
|
|
12
|
-
//
|
|
13
|
-
// ```
|
|
14
|
-
// code content
|
|
15
|
-
// ```
|
|
16
|
-
|
|
17
|
-
const lines = content.split("\n");
|
|
18
|
-
let startIndex = 0;
|
|
19
|
-
let endIndex = lines.length - 1;
|
|
20
|
-
|
|
21
|
-
// Check if there is a code block marker at the beginning
|
|
22
|
-
if (lines[startIndex]?.trim().startsWith("```")) {
|
|
23
|
-
startIndex = 1;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Check if there is a code block marker at the end
|
|
27
|
-
if (lines[endIndex]?.trim() === "```") {
|
|
28
|
-
endIndex = endIndex - 1;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// If no complete code block wrapper is found, return original content
|
|
32
|
-
if (startIndex === 0 && endIndex === lines.length - 1) {
|
|
33
|
-
return content;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Return content after removing wrappers
|
|
37
|
-
return lines.slice(startIndex, endIndex + 1).join("\n");
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Parse custom headers from a string (typically from environment variables)
|
|
42
|
-
* Format: Key: Value, separated by newlines
|
|
43
|
-
* @param headersString String containing headers
|
|
44
|
-
* @returns Record of headers
|
|
45
|
-
*/
|
|
46
|
-
export function parseCustomHeaders(
|
|
47
|
-
headersString: string,
|
|
48
|
-
): Record<string, string> {
|
|
49
|
-
if (!headersString || typeof headersString !== "string") {
|
|
50
|
-
return {};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const headers: Record<string, string> = {};
|
|
54
|
-
const lines = headersString.split(/\r?\n/);
|
|
55
|
-
|
|
56
|
-
for (const line of lines) {
|
|
57
|
-
const trimmedLine = line.trim();
|
|
58
|
-
if (!trimmedLine) continue;
|
|
59
|
-
|
|
60
|
-
const colonIndex = trimmedLine.indexOf(":");
|
|
61
|
-
if (colonIndex === -1) continue;
|
|
62
|
-
|
|
63
|
-
const key = trimmedLine.slice(0, colonIndex).trim();
|
|
64
|
-
const value = trimmedLine.slice(colonIndex + 1).trim();
|
|
65
|
-
|
|
66
|
-
if (key) {
|
|
67
|
-
headers[key] = value;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return headers;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Function to remove ANSI color codes
|
|
76
|
-
* @param text Text containing ANSI color codes
|
|
77
|
-
* @returns Plain text with color codes removed
|
|
78
|
-
*/
|
|
79
|
-
export const stripAnsiColors = (text: string): string => {
|
|
80
|
-
// Create the escape character dynamically to avoid control character detection
|
|
81
|
-
const escapeChar = String.fromCharCode(27); // ESC character
|
|
82
|
-
const ansiEscapeRegex = new RegExp(`${escapeChar}\\[[0-9;]*[a-zA-Z]`, "g");
|
|
83
|
-
return text.replace(ansiEscapeRegex, "");
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Format a line number prefix in cat -n style (padStart(6) + tab)
|
|
88
|
-
* @param lineNumber The line number
|
|
89
|
-
* @returns Formatted line number prefix
|
|
90
|
-
*/
|
|
91
|
-
export function formatLineNumberPrefix(lineNumber: number): string {
|
|
92
|
-
return `${lineNumber.toString().padStart(6)}\t`;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Attempt to recover truncated JSON (e.g., missing closing braces due to max tokens).
|
|
97
|
-
* Tracks brace depth and only recovers if there are unclosed `{` braces.
|
|
98
|
-
* Will NOT recover if there are unclosed `[` brackets (can't guess the content).
|
|
99
|
-
* @param jsonStr Potentially truncated JSON string
|
|
100
|
-
* @returns Recovered JSON string, or the original if unrecoverable
|
|
101
|
-
*/
|
|
102
|
-
export function recoverTruncatedJson(jsonStr: string): string {
|
|
103
|
-
let braceDepth = 0;
|
|
104
|
-
let bracketDepth = 0;
|
|
105
|
-
let inString = false;
|
|
106
|
-
let escaped = false;
|
|
107
|
-
|
|
108
|
-
for (const ch of jsonStr) {
|
|
109
|
-
if (escaped) {
|
|
110
|
-
escaped = false;
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
if (ch === "\\" && inString) {
|
|
114
|
-
escaped = true;
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
if (ch === '"') {
|
|
118
|
-
inString = !inString;
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
if (!inString) {
|
|
122
|
-
if (ch === "{") braceDepth++;
|
|
123
|
-
if (ch === "}") braceDepth--;
|
|
124
|
-
if (ch === "[") bracketDepth++;
|
|
125
|
-
if (ch === "]") bracketDepth--;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Build recovery suffix
|
|
130
|
-
let suffix = "";
|
|
131
|
-
if (inString) suffix += '"'; // Close unclosed string
|
|
132
|
-
if (braceDepth > 0 && bracketDepth === 0) {
|
|
133
|
-
suffix += "}".repeat(braceDepth);
|
|
134
|
-
}
|
|
135
|
-
return suffix ? jsonStr + suffix : jsonStr;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Efficiently get the last N lines of a string without splitting the whole string.
|
|
140
|
-
*/
|
|
141
|
-
export function getLastLines(text: string, count: number): string {
|
|
142
|
-
if (!text || count <= 0) return "";
|
|
143
|
-
let pos = text.length;
|
|
144
|
-
let found = 0;
|
|
145
|
-
while (pos > 0 && found < count) {
|
|
146
|
-
const nextNewline = text.lastIndexOf("\n", pos - 1);
|
|
147
|
-
if (nextNewline === -1) {
|
|
148
|
-
pos = 0;
|
|
149
|
-
break;
|
|
150
|
-
} else {
|
|
151
|
-
pos = nextNewline;
|
|
152
|
-
found++;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
return text.substring(pos === 0 && found < count ? 0 : pos + 1);
|
|
156
|
-
}
|