@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ottocode/sdk",
3
- "version": "0.1.188",
3
+ "version": "0.1.189",
4
4
  "description": "AI agent SDK for building intelligent assistants - tree-shakable and comprehensive",
5
5
  "author": "nitishxyz",
6
6
  "license": "MIT",
@@ -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 parts = p.replace(/\\/g, '/').split('/');
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 = abs.slice(0, abs.lastIndexOf('/'));
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 idx1 = line.indexOf(':');
114
- const idx2 = idx1 === -1 ? -1 : line.indexOf(':', idx1 + 1);
115
- if (idx1 === -1 || idx2 === -1) continue;
116
- const filePath = line.slice(0, idx1);
117
- const lineNumStr = line.slice(idx1 + 1, idx2);
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 parts = l.split(':');
100
- if (parts.length < 3) return { file: '', line: 0, text: l };
101
- const file = parts[0];
102
- const line = Number.parseInt(parts[1], 10);
103
- const text = parts.slice(2).join(':');
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 parts = p.replace(/\\/g, '/').split('/');
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 = process.env.SHELL || '/bin/sh';
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
 
@@ -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('/').pop();
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(
@@ -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
- await execAsync('pkill -f "tunnel tunnel --url" 2>/dev/null || true');
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 {