wave-agent-sdk 1.0.5 → 1.0.7

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 (202) hide show
  1. package/dist/agent.d.ts +34 -1
  2. package/dist/agent.js +49 -4
  3. package/dist/index.d.ts +3 -1
  4. package/dist/index.js +1 -1
  5. package/dist/managers/aiManager.d.ts +36 -0
  6. package/dist/managers/aiManager.js +240 -11
  7. package/dist/managers/messageManager.d.ts +2 -4
  8. package/dist/managers/messageManager.js +14 -7
  9. package/dist/managers/slashCommandManager.js +28 -0
  10. package/dist/managers/subagentManager.d.ts +0 -2
  11. package/dist/services/aiService.d.ts +2 -3
  12. package/dist/services/aiService.js +4 -18
  13. package/dist/services/configurationService.d.ts +7 -0
  14. package/dist/services/configurationService.js +10 -0
  15. package/dist/telemetry/instrumentation.d.ts +1 -1
  16. package/dist/telemetry/instrumentation.js +57 -28
  17. package/dist/tools/bashTool.js +16 -6
  18. package/dist/tools/grepTool.js +11 -3
  19. package/dist/types/core.d.ts +1 -34
  20. package/dist/types/index.d.ts +4 -0
  21. package/dist/types/messaging.d.ts +2 -0
  22. package/dist/utils/cacheControlUtils.d.ts +5 -27
  23. package/dist/utils/cacheControlUtils.js +12 -73
  24. package/dist/utils/containerSetup.js +0 -3
  25. package/dist/utils/messageOperations.d.ts +2 -1
  26. package/dist/utils/messageOperations.js +2 -1
  27. package/dist/utils/notificationXml.js +5 -0
  28. package/dist/utils/ripgrep.d.ts +4 -4
  29. package/dist/utils/ripgrep.js +4 -27
  30. package/package.json +20 -8
  31. package/bin/rg +0 -79
  32. package/dist/managers/forkedAgentManager.d.ts +0 -50
  33. package/dist/managers/forkedAgentManager.js +0 -130
  34. package/scripts/install_ripgrep.js +0 -111
  35. package/scripts/postinstall.js +0 -38
  36. package/src/agent.ts +0 -1271
  37. package/src/constants/subagents.ts +0 -4
  38. package/src/constants/toolLimits.ts +0 -15
  39. package/src/constants/tools.ts +0 -24
  40. package/src/core/plugin.ts +0 -237
  41. package/src/core/session.ts +0 -9
  42. package/src/index.ts +0 -38
  43. package/src/managers/MemoryRuleManager.ts +0 -198
  44. package/src/managers/aiManager.ts +0 -2429
  45. package/src/managers/backgroundTaskManager.ts +0 -480
  46. package/src/managers/bangManager.ts +0 -119
  47. package/src/managers/cronManager.ts +0 -401
  48. package/src/managers/foregroundTaskManager.ts +0 -30
  49. package/src/managers/forkedAgentManager.ts +0 -216
  50. package/src/managers/hookManager.ts +0 -1089
  51. package/src/managers/liveConfigManager.ts +0 -445
  52. package/src/managers/lspManager.ts +0 -470
  53. package/src/managers/mcpManager.ts +0 -1010
  54. package/src/managers/messageManager.ts +0 -1184
  55. package/src/managers/messageQueue.ts +0 -157
  56. package/src/managers/permissionManager.ts +0 -1140
  57. package/src/managers/planManager.ts +0 -112
  58. package/src/managers/pluginManager.ts +0 -330
  59. package/src/managers/pluginScopeManager.ts +0 -124
  60. package/src/managers/reversionManager.ts +0 -178
  61. package/src/managers/skillManager.ts +0 -578
  62. package/src/managers/slashCommandManager.ts +0 -564
  63. package/src/managers/subagentManager.ts +0 -915
  64. package/src/managers/toolManager.ts +0 -489
  65. package/src/managers/workflowManager.ts +0 -493
  66. package/src/prompts/autoMemory.ts +0 -33
  67. package/src/prompts/autoMemoryExtraction.ts +0 -146
  68. package/src/prompts/index.ts +0 -506
  69. package/src/prompts/planModeReminders.ts +0 -126
  70. package/src/services/GitService.ts +0 -131
  71. package/src/services/MarketplaceService.ts +0 -1052
  72. package/src/services/MemoryRuleService.ts +0 -71
  73. package/src/services/aiService.ts +0 -919
  74. package/src/services/authService.ts +0 -565
  75. package/src/services/autoMemoryService.ts +0 -280
  76. package/src/services/configurationService.ts +0 -1520
  77. package/src/services/fileWatcher.ts +0 -319
  78. package/src/services/hook.ts +0 -348
  79. package/src/services/initializationService.ts +0 -357
  80. package/src/services/interactionService.ts +0 -229
  81. package/src/services/jsonlHandler.ts +0 -295
  82. package/src/services/memory.ts +0 -234
  83. package/src/services/pluginLoader.ts +0 -252
  84. package/src/services/remoteSettingsService.ts +0 -366
  85. package/src/services/reversionService.ts +0 -122
  86. package/src/services/session.ts +0 -917
  87. package/src/services/taskManager.ts +0 -321
  88. package/src/telemetry/events.ts +0 -65
  89. package/src/telemetry/instrumentation.ts +0 -499
  90. package/src/telemetry/sessionTracing.ts +0 -348
  91. package/src/tools/agentTool.ts +0 -322
  92. package/src/tools/askUserQuestion.ts +0 -160
  93. package/src/tools/bashTool.ts +0 -621
  94. package/src/tools/buildTool.ts +0 -61
  95. package/src/tools/cronCreateTool.ts +0 -161
  96. package/src/tools/cronDeleteTool.ts +0 -51
  97. package/src/tools/cronListTool.ts +0 -47
  98. package/src/tools/editTool.ts +0 -313
  99. package/src/tools/enterPlanMode.ts +0 -124
  100. package/src/tools/enterWorktreeTool.ts +0 -190
  101. package/src/tools/exitPlanMode.ts +0 -139
  102. package/src/tools/exitWorktreeTool.ts +0 -236
  103. package/src/tools/globTool.ts +0 -167
  104. package/src/tools/grepTool.ts +0 -399
  105. package/src/tools/lspTool.ts +0 -883
  106. package/src/tools/readTool.ts +0 -426
  107. package/src/tools/skillTool.ts +0 -248
  108. package/src/tools/taskManagementTools.ts +0 -574
  109. package/src/tools/taskStopTool.ts +0 -78
  110. package/src/tools/types.ts +0 -137
  111. package/src/tools/webFetchTool.ts +0 -373
  112. package/src/tools/workflowTool.ts +0 -205
  113. package/src/tools/writeTool.ts +0 -267
  114. package/src/types/agent.ts +0 -128
  115. package/src/types/auth.ts +0 -19
  116. package/src/types/commands.ts +0 -30
  117. package/src/types/config.ts +0 -43
  118. package/src/types/configuration.ts +0 -168
  119. package/src/types/core.ts +0 -105
  120. package/src/types/cron.ts +0 -13
  121. package/src/types/environment.ts +0 -106
  122. package/src/types/fileSearch.ts +0 -4
  123. package/src/types/history.ts +0 -7
  124. package/src/types/hooks.ts +0 -256
  125. package/src/types/index.ts +0 -41
  126. package/src/types/lsp.ts +0 -98
  127. package/src/types/marketplace.ts +0 -71
  128. package/src/types/mcp.ts +0 -43
  129. package/src/types/memoryRule.ts +0 -31
  130. package/src/types/messaging.ts +0 -114
  131. package/src/types/permissions.ts +0 -80
  132. package/src/types/plugins.ts +0 -39
  133. package/src/types/processes.ts +0 -99
  134. package/src/types/reversion.ts +0 -29
  135. package/src/types/session.ts +0 -12
  136. package/src/types/skills.ts +0 -108
  137. package/src/types/tasks.ts +0 -13
  138. package/src/types/telemetry.ts +0 -98
  139. package/src/types/tools.ts +0 -44
  140. package/src/types/workflow.ts +0 -6
  141. package/src/utils/abortUtils.ts +0 -118
  142. package/src/utils/atomicWrite.ts +0 -61
  143. package/src/utils/bashParser.ts +0 -899
  144. package/src/utils/cacheControlUtils.ts +0 -426
  145. package/src/utils/commandArgumentParser.ts +0 -161
  146. package/src/utils/commandPathResolver.ts +0 -88
  147. package/src/utils/configPaths.ts +0 -220
  148. package/src/utils/configValidator.ts +0 -100
  149. package/src/utils/constants.ts +0 -39
  150. package/src/utils/container.ts +0 -92
  151. package/src/utils/containerSetup.ts +0 -371
  152. package/src/utils/convertMessagesForAPI.ts +0 -326
  153. package/src/utils/cronTasks.ts +0 -128
  154. package/src/utils/cronTasksLock.ts +0 -182
  155. package/src/utils/cronToHuman.ts +0 -99
  156. package/src/utils/customCommands.ts +0 -95
  157. package/src/utils/editUtils.ts +0 -20
  158. package/src/utils/fileFormat.ts +0 -40
  159. package/src/utils/fileSearch.ts +0 -151
  160. package/src/utils/fileUtils.ts +0 -223
  161. package/src/utils/gitUtils.ts +0 -315
  162. package/src/utils/globalLogger.ts +0 -128
  163. package/src/utils/groupMessagesByApiRound.ts +0 -121
  164. package/src/utils/hookMatcher.ts +0 -176
  165. package/src/utils/markdownParser.ts +0 -317
  166. package/src/utils/mcpUtils.ts +0 -138
  167. package/src/utils/messageOperations.ts +0 -659
  168. package/src/utils/modelCapabilities.ts +0 -30
  169. package/src/utils/nameGenerator.ts +0 -99
  170. package/src/utils/notificationXml.ts +0 -57
  171. package/src/utils/openaiClient.ts +0 -251
  172. package/src/utils/parseCronExpression.ts +0 -78
  173. package/src/utils/path.ts +0 -72
  174. package/src/utils/pathEncoder.ts +0 -400
  175. package/src/utils/pathSafety.ts +0 -40
  176. package/src/utils/promptHistory.ts +0 -168
  177. package/src/utils/ripgrep.ts +0 -35
  178. package/src/utils/shellResolver.ts +0 -190
  179. package/src/utils/skillParser.ts +0 -246
  180. package/src/utils/stringUtils.ts +0 -156
  181. package/src/utils/subagentParser.ts +0 -279
  182. package/src/utils/taskReminder.ts +0 -96
  183. package/src/utils/tokenCalculation.ts +0 -43
  184. package/src/utils/tokenEstimate.ts +0 -34
  185. package/src/utils/toolResultStorage.ts +0 -117
  186. package/src/utils/worktreeSession.ts +0 -26
  187. package/src/utils/worktreeUtils.ts +0 -786
  188. package/src/workflow/budgetTracker.ts +0 -34
  189. package/src/workflow/concurrencyLimiter.ts +0 -47
  190. package/src/workflow/journal.ts +0 -95
  191. package/src/workflow/progressReporter.ts +0 -141
  192. package/src/workflow/runState.ts +0 -65
  193. package/src/workflow/scriptRuntime.ts +0 -274
  194. package/src/workflow/structuredOutput.ts +0 -123
  195. package/src/workflow/types.ts +0 -101
  196. package/src/workflow/workflowApis.ts +0 -410
  197. package/vendor/ripgrep/linux-aarch64/rg +0 -0
  198. package/vendor/ripgrep/linux-x86_64/rg +0 -0
  199. package/vendor/ripgrep/macos-aarch64/rg +0 -0
  200. package/vendor/ripgrep/macos-x86_64/rg +0 -0
  201. package/vendor/ripgrep/windows-aarch64/rg.exe +0 -0
  202. 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
- }
@@ -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
- }
@@ -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
- }