@ottocode/sdk 0.1.188 → 0.1.189
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/package.json +1 -1
- package/src/core/src/tools/builtin/bash.ts +6 -1
- package/src/core/src/tools/builtin/fs/write.ts +2 -1
- package/src/core/src/tools/builtin/grep.ts +5 -7
- package/src/core/src/tools/builtin/ripgrep.ts +5 -5
- package/src/core/src/tools/builtin/terminal.ts +11 -3
- package/src/skills/loader.ts +1 -1
- package/src/tunnel/tunnel.ts +5 -1
package/package.json
CHANGED
|
@@ -6,13 +6,18 @@ import { createToolError, type ToolResponse } from '../error.ts';
|
|
|
6
6
|
import { getAugmentedPath } from '../bin-manager.ts';
|
|
7
7
|
|
|
8
8
|
function normalizePath(p: string) {
|
|
9
|
-
const
|
|
9
|
+
const normalized = p.replace(/\\/g, '/');
|
|
10
|
+
const driveMatch = normalized.match(/^([A-Za-z]):\//);
|
|
11
|
+
const drivePrefix = driveMatch ? `${driveMatch[1]}:` : '';
|
|
12
|
+
const rest = driveMatch ? normalized.slice(2) : normalized;
|
|
13
|
+
const parts = rest.split('/');
|
|
10
14
|
const stack: string[] = [];
|
|
11
15
|
for (const part of parts) {
|
|
12
16
|
if (!part || part === '.') continue;
|
|
13
17
|
if (part === '..') stack.pop();
|
|
14
18
|
else stack.push(part);
|
|
15
19
|
}
|
|
20
|
+
if (drivePrefix) return `${drivePrefix}/${stack.join('/')}`;
|
|
16
21
|
return `/${stack.join('/')}`;
|
|
17
22
|
}
|
|
18
23
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { tool, type Tool } from 'ai';
|
|
2
2
|
import { z } from 'zod/v3';
|
|
3
3
|
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
4
|
+
import { dirname } from 'node:path';
|
|
4
5
|
import {
|
|
5
6
|
buildWriteArtifact,
|
|
6
7
|
resolveSafePath,
|
|
@@ -68,7 +69,7 @@ export function buildWriteTool(projectRoot: string): {
|
|
|
68
69
|
|
|
69
70
|
try {
|
|
70
71
|
if (createDirs) {
|
|
71
|
-
const dirPath =
|
|
72
|
+
const dirPath = dirname(abs);
|
|
72
73
|
await mkdir(dirPath, { recursive: true });
|
|
73
74
|
}
|
|
74
75
|
let existed = false;
|
|
@@ -110,13 +110,11 @@ export function buildGrepTool(projectRoot: string): {
|
|
|
110
110
|
|
|
111
111
|
for (const line of lines) {
|
|
112
112
|
if (!line) continue;
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const
|
|
117
|
-
const
|
|
118
|
-
const lineText = line.slice(idx2 + 1);
|
|
119
|
-
const lineNum = parseInt(lineNumStr, 10);
|
|
113
|
+
const m = line.match(/^(.+?):(\d+):(.*)$/s);
|
|
114
|
+
if (!m) continue;
|
|
115
|
+
const filePath = m[1];
|
|
116
|
+
const lineNum = parseInt(m[2], 10);
|
|
117
|
+
const lineText = m[3];
|
|
120
118
|
if (!filePath || !Number.isFinite(lineNum)) continue;
|
|
121
119
|
matches.push({ file: filePath, line: lineNum, text: lineText });
|
|
122
120
|
}
|
|
@@ -96,11 +96,11 @@ export function buildRipgrepTool(projectRoot: string): {
|
|
|
96
96
|
.filter(Boolean)
|
|
97
97
|
.slice(0, maxResults);
|
|
98
98
|
const matches = lines.map((l) => {
|
|
99
|
-
const
|
|
100
|
-
if (
|
|
101
|
-
const file =
|
|
102
|
-
const line = Number.parseInt(
|
|
103
|
-
const text =
|
|
99
|
+
const m = l.match(/^(.+?):(\d+):(.*)$/s);
|
|
100
|
+
if (!m) return { file: '', line: 0, text: l };
|
|
101
|
+
const file = m[1];
|
|
102
|
+
const line = Number.parseInt(m[2], 10);
|
|
103
|
+
const text = m[3];
|
|
104
104
|
return { file, line, text };
|
|
105
105
|
});
|
|
106
106
|
resolve({ ok: true, count: matches.length, matches });
|
|
@@ -18,13 +18,18 @@ function formatShellCommand(parts: string[]): string {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
function normalizePath(p: string) {
|
|
21
|
-
const
|
|
21
|
+
const normalized = p.replace(/\\/g, '/');
|
|
22
|
+
const driveMatch = normalized.match(/^([A-Za-z]):\//);
|
|
23
|
+
const drivePrefix = driveMatch ? `${driveMatch[1]}:` : '';
|
|
24
|
+
const rest = driveMatch ? normalized.slice(2) : normalized;
|
|
25
|
+
const parts = rest.split('/');
|
|
22
26
|
const stack: string[] = [];
|
|
23
27
|
for (const part of parts) {
|
|
24
28
|
if (!part || part === '.') continue;
|
|
25
29
|
if (part === '..') stack.pop();
|
|
26
30
|
else stack.push(part);
|
|
27
31
|
}
|
|
32
|
+
if (drivePrefix) return `${drivePrefix}/${stack.join('/')}`;
|
|
28
33
|
return `/${stack.join('/')}`;
|
|
29
34
|
}
|
|
30
35
|
|
|
@@ -116,7 +121,10 @@ export function buildTerminalTool(
|
|
|
116
121
|
|
|
117
122
|
const cwd = resolveSafePath(projectRoot, params.cwd);
|
|
118
123
|
|
|
119
|
-
const shellPath =
|
|
124
|
+
const shellPath =
|
|
125
|
+
process.platform === 'win32'
|
|
126
|
+
? process.env.COMSPEC || 'cmd.exe'
|
|
127
|
+
: process.env.SHELL || '/bin/sh';
|
|
120
128
|
|
|
121
129
|
let command = params.command ?? shellPath;
|
|
122
130
|
let args = params.args ?? [];
|
|
@@ -124,7 +132,7 @@ export function buildTerminalTool(
|
|
|
124
132
|
|
|
125
133
|
if (runInShell) {
|
|
126
134
|
command = shellPath;
|
|
127
|
-
args = ['-i'];
|
|
135
|
+
args = process.platform === 'win32' ? [] : ['-i'];
|
|
128
136
|
const providedCommand = params.command;
|
|
129
137
|
const providedArgs = params.args ?? [];
|
|
130
138
|
|
package/src/skills/loader.ts
CHANGED
|
@@ -101,7 +101,7 @@ async function loadSkillsFromDir(
|
|
|
101
101
|
const content = await fs.readFile(filePath, 'utf-8');
|
|
102
102
|
const skill = parseSkillFile(content, filePath, scope);
|
|
103
103
|
|
|
104
|
-
const dirName = dirname(filePath).split(
|
|
104
|
+
const dirName = dirname(filePath).split(/[\\/]/).pop();
|
|
105
105
|
if (dirName !== skill.metadata.name) {
|
|
106
106
|
if (process.env.OTTO_DEBUG === '1') {
|
|
107
107
|
console.warn(
|
package/src/tunnel/tunnel.ts
CHANGED
|
@@ -210,7 +210,11 @@ export async function killStaleTunnels(): Promise<void> {
|
|
|
210
210
|
const execAsync = promisify(exec);
|
|
211
211
|
|
|
212
212
|
// Kill any existing tunnel processes (but not the parent otto process)
|
|
213
|
-
|
|
213
|
+
const killCmd =
|
|
214
|
+
process.platform === 'win32'
|
|
215
|
+
? 'taskkill /F /IM tunnel.exe 2>NUL || exit /b 0'
|
|
216
|
+
: 'pkill -f "tunnel tunnel --url" 2>/dev/null || true';
|
|
217
|
+
await execAsync(killCmd);
|
|
214
218
|
// Give processes time to die
|
|
215
219
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
216
220
|
} catch {
|