cliskill 1.0.0 → 1.0.2

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/README.md CHANGED
@@ -6,9 +6,8 @@
6
6
 
7
7
  **Независимый AI-ассистент для терминала с поддержкой любых LLM провайдеров**
8
8
 
9
- [![Tests](https://img.shields.io/badge/tests-844%20passed-brightgreen)]()
10
9
  [![License](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
11
- [![Version](https://img.shields.io/badge/version-0.1.0-orange)]()
10
+ [![Version](https://img.shields.io/badge/version-1.0.0-orange)]()
12
11
  [![Node](https://img.shields.io/badge/node-%3E%3D20.0.0-green)]()
13
12
 
14
13
  [Бесплатный](#) · [Без подписок](#) · [Без vendor lock-in](#) · [Универсальный](#)
@@ -21,10 +20,13 @@
21
20
 
22
21
  - [О проекте](#-о-проекте)
23
22
  - [Возможности](#-возможности)
24
- - [Установка](#-установка)
23
+ - [Быстрый старт](#-быстрый-старт)
25
24
  - [Использование](#-использование)
26
25
  - [Конфигурация](#-конфигурация)
26
+ - [Инструменты](#-инструменты)
27
+ - [Агентская система](#-агентская-система)
27
28
  - [Архитектура](#-архитектура)
29
+ - [API провайдеры](#-api-провайдеры)
28
30
  - [Разработка](#-разработка)
29
31
  - [Технологии](#-технологии)
30
32
  - [Лицензия](#-лицензия)
@@ -58,37 +60,6 @@
58
60
  - **Автоматический fallback** — переключение на резервные модели при ошибках
59
61
  - **Rate Limit Handler** — корректная обработка лимитов без upsell
60
62
 
61
- ### 🔧 Инструменты (15 штук)
62
-
63
- | Инструмент | Описание |
64
- |------------|----------|
65
- | 🖥️ **Bash Tool** | Выполнение shell команд с инспекцией безопасности |
66
- | 📄 **File Read** | Чтение файлов с поддержкой изображений |
67
- | ✏️ **File Write** | Создание и перезапись файлов |
68
- | 📝 **File Edit** | Точечное редактирование файлов (search/replace) |
69
- | 🔍 **Glob** | Поиск файлов по паттернам |
70
- | 🔎 **Grep** | Поиск текста с поддержкой regex |
71
- | 🌐 **Web Search** | Веб-поиск через поисковые системы |
72
- | 🌍 **Web Fetch** | Загрузка и парсинг веб-страниц |
73
- | 🤖 **Agent Tool** | Порождение суб-агентов для подзадач |
74
- | 📡 **LSP Tool** | Интеграция с Language Server Protocol |
75
- | ✅ **Todo Write** | Управление списками задач |
76
- | 🖱️ **Computer Use** | Управление мышью и клавиатурой |
77
- | 🔗 **Remote Tool** | Выполнение команд на удалённых серверах через SSH |
78
- | 🌳 **Worktree Tool** | Управление git worktree для параллельной работы |
79
- | 📋 **Plan Mode** | Планирование с 3 параллельными агентами |
80
-
81
- ### 🤖 Агентская система
82
-
83
- - **Auto Mode** — 3 уровня автономности:
84
- - `full-auto` — полная автономия, все действия без подтверждения
85
- - `safe-auto` — автоматическое выполнение безопасных операций
86
- - `ask` — подтверждение каждого действия (по умолчанию)
87
- - **Fast Mode** — оптимизации скорости ответов
88
- - **Multi-Agent Plan Mode V2** — 3 параллельных агента для планирования
89
- - **Swarm Coordinator** — мульти-агентная координация сложных задач
90
- - **Task System** — фоновые задачи с отслеживанием статуса
91
-
92
63
  ### 🏗️ Инфраструктура
93
64
 
94
65
  | Система | Описание |
@@ -105,7 +76,7 @@
105
76
  ### 🛠️ Утилиты
106
77
 
107
78
  - 🎬 **Asciicast Recording** — запись сессий в формате asciicast v2
108
- - 🎨 **ANSI→SVG** — ренддеринг терминального вывода в SVG
79
+ - 🎨 **ANSI→SVG** — рендеринг терминального вывода в SVG
109
80
  - 🔄 **Conversation Recovery** — умное восстановление сессий с ремонтом
110
81
  - 🔗 **Deep Links** — протокол `cliskill://` для интеграции с ОС
111
82
  - 🧹 **Background Housekeeping** — автоматическая очистка и обслуживание
@@ -116,13 +87,13 @@
116
87
 
117
88
  ---
118
89
 
119
- ## 📦 Установка
90
+ ## 📦 Быстрый старт
120
91
 
121
- ### Из исходников
92
+ ### Установка из исходников
122
93
 
123
94
  ```bash
124
95
  # Клонирование репозитория
125
- git clone <repo-url> cliskill
96
+ git clone https://github.com/nic11/cliskill.git
126
97
  cd cliskill
127
98
 
128
99
  # Установка зависимостей
@@ -135,11 +106,27 @@ npm run build
135
106
  npm link
136
107
  ```
137
108
 
109
+ ### Установка из npm
110
+
111
+ ```bash
112
+ npm install -g cliskill
113
+ ```
114
+
138
115
  ### Требования
139
116
 
140
117
  - **Node.js** >= 20.0.0
141
118
  - **npm** >= 9.0.0
142
119
 
120
+ ### Первый запуск
121
+
122
+ ```bash
123
+ # Интерактивный мастер настройки
124
+ cliskill
125
+
126
+ # Или с явным указанием провайдера
127
+ cliskill --base-url https://api.example.com/v1 --api-key your-key
128
+ ```
129
+
143
130
  ---
144
131
 
145
132
  ## 🚀 Использование
@@ -289,8 +276,8 @@ cliskill open-uri "cliskill://prompt?text=hello" # обработать URI
289
276
  ### Переменные окружения
290
277
 
291
278
  ```bash
292
- export CLISKILL_API_KEY="b0157800fd274691a6ba19a29ca11885.Ar0TPbB9buuhh8wJ"
293
- export CLISKILL_BASE_URL="https://api.z.ai/api/coding/paas/v4"
279
+ export CLISKILL_API_KEY="your-api-key"
280
+ export CLISKILL_BASE_URL="https://api.example.com/v1"
294
281
  ```
295
282
 
296
283
  ### Форматы провайдеров
@@ -326,6 +313,68 @@ description: Описание навыка
326
313
 
327
314
  ---
328
315
 
316
+ ## 🔧 Инструменты
317
+
318
+ cliskill включает 15 встроенных инструментов для полноценной работы:
319
+
320
+ | # | Инструмент | Описание | Категория |
321
+ |---|------------|----------|-----------|
322
+ | 1 | 🖥️ **Bash** | Выполнение shell команд с инспекцией безопасности | Выполнение |
323
+ | 2 | 📄 **File Read** | Чтение файлов с поддержкой изображений | Файлы |
324
+ | 3 | ✏️ **File Write** | Создание и перезапись файлов | Файлы |
325
+ | 4 | 📝 **File Edit** | Точечное редактирование файлов (search/replace) | Файлы |
326
+ | 5 | 🔍 **Glob** | Поиск файлов по паттернам | Поиск |
327
+ | 6 | 🔎 **Grep** | Поиск текста с поддержкой regex | Поиск |
328
+ | 7 | 🌐 **Web Search** | Веб-поиск через поисковые системы | Веб |
329
+ | 8 | 🌍 **Web Fetch** | Загрузка и парсинг веб-страниц | Веб |
330
+ | 9 | 🤖 **Agent** | Порождение суб-агентов для подзадач | Агенты |
331
+ | 10 | 📡 **LSP** | Интеграция с Language Server Protocol | IDE |
332
+ | 11 | ✅ **Todo Write** | Управление списками задач | Планирование |
333
+ | 12 | 🖱️ **Computer Use** | Управление мышью и клавиатурой | Автоматизация |
334
+ | 13 | 🔗 **Remote** | Выполнение команд на удалённых серверах через SSH | Удалённый доступ |
335
+ | 14 | 🌳 **Worktree** | Управление git worktree для параллельной работы | Git |
336
+ | 15 | 📋 **Plan Mode** | Планирование с 3 параллельными агентами | Планирование |
337
+
338
+ ### Безопасность инструментов
339
+
340
+ Инструменты классифицируются по уровню безопасности:
341
+
342
+ - **readOnly** (Glob, Grep, File Read, LSP) — могут выполняться параллельно
343
+ - **write** (File Write, File Edit, Bash) — выполняются эксклюзивно
344
+ - **network** (Web Search, Web Fetch) — контролируемые сетевые операции
345
+
346
+ ---
347
+
348
+ ## 🤖 Агентская система
349
+
350
+ ### Auto Mode
351
+
352
+ 3 уровня автономности для автоматического выполнения задач:
353
+
354
+ | Уровень | Описание | Подтверждение |
355
+ |---------|----------|---------------|
356
+ | `full-auto` | Полная автономия | Все действия без подтверждения |
357
+ | `safe-auto` | Безопасная автономия | Только потенциально опасные операции |
358
+ | `ask` | Ручное управление | Каждое действие (по умолчанию) |
359
+
360
+ ### Multi-Agent система
361
+
362
+ - **Fast Mode** — оптимизации скорости ответов
363
+ - **Multi-Agent Plan Mode V2** — 3 параллельных агента для планирования
364
+ - **Swarm Coordinator** — мульти-агентная координация сложных задач
365
+ - **Task System** — фоновые задачи с отслеживанием статуса
366
+
367
+ ### Agent Loop
368
+
369
+ Ядро системы — агентский цикл с поддержкой:
370
+
371
+ - **Streaming** — потоковая обработка ответов LLM
372
+ - **Tool calling** — параллельный вызов инструментов
373
+ - **Error recovery** — 3-уровневая стратегия восстановления при `max_tokens`
374
+ - **Context management** — автоматическое сжатие при переполнении контекста
375
+
376
+ ---
377
+
329
378
  ## 🏛️ Архитектура
330
379
 
331
380
  ```
@@ -344,7 +393,7 @@ src/
344
393
  ├── 🐝 swarm/ — Мульти-агентная координация
345
394
  ├── 📋 tasks/ — Фоновые задачи
346
395
  ├── 🔗 remote/ — SSH сессии
347
- ├── 🧩 extensions/ — Расширения
396
+ ├── 🧩 extensions/ — Расширения и навыки
348
397
  ├── 🏗️ infra/ — Инфраструктурные модули
349
398
  └── 🔀 utils/ — Общие утилиты
350
399
  ```
@@ -372,6 +421,60 @@ graph TB
372
421
  Swarm --> Tasks[Task System]
373
422
  ```
374
423
 
424
+ ### Поток данных
425
+
426
+ ```mermaid
427
+ sequenceDiagram
428
+ participant U as User
429
+ participant R as REPL/TUI
430
+ participant L as Agent Loop
431
+ participant P as Provider Adapter
432
+ participant T as Tool System
433
+
434
+ U->>R: Ввод запроса
435
+ R->>L: Передача сообщения
436
+ L->>P: API запрос (streaming)
437
+ P-->>L: Потоковый ответ
438
+ L->>T: Вызов инструментов
439
+ T-->>L: Результат выполнения
440
+ L->>P: Продолжение диалога
441
+ P-->>L: Финальный ответ
442
+ L-->>R: Событие результата
443
+ R-->>U: Отображение ответа
444
+ ```
445
+
446
+ ---
447
+
448
+ ## 🌐 API провайдеры
449
+
450
+ ### Поддерживаемые форматы
451
+
452
+ cliskill работает с **любым** API, совместимым с OpenAI `/chat/completions`:
453
+
454
+ | Провайдер | Base URL | Формат |
455
+ |-----------|----------|--------|
456
+ | OpenAI | `https://api.openai.com/v1` | `openai-compatible` |
457
+ | GLM / Z.AI | `https://api.z.ai/api/coding/paas/v4` | `openai-compatible` |
458
+ | DeepSeek | `https://api.deepseek.com/v1` | `openai-compatible` |
459
+ | LocalAI | `http://localhost:8080/v1` | `openai-compatible` |
460
+ | Ollama | `http://localhost:11434/v1` | `openai-compatible` |
461
+ | LM Studio | `http://localhost:1234/v1` | `openai-compatible` |
462
+ | Любой другой | `<your-endpoint>/v1` | `openai-compatible` |
463
+
464
+ ### Модель алиасов
465
+
466
+ Предустановленные алиасы для быстрого переключения моделей:
467
+
468
+ | Алиас | Описание | Модель по умолчанию |
469
+ |-------|----------|---------------------|
470
+ | `smart` | Максимальное качество | `gpt-4o` |
471
+ | `fast` | Быстрые ответы | `gpt-4o-mini` |
472
+ | `balanced` | Баланс качества/скорости | `gpt-4o` |
473
+ | `reasoning` | Логические задачи | `o1` |
474
+ | `code` | Код-специфичные задачи | `gpt-4o` |
475
+
476
+ Алиасы настраиваются в `.cliskillrc.json` → `models.aliases`.
477
+
375
478
  ---
376
479
 
377
480
  ## 👨‍💻 Разработка
@@ -382,7 +485,7 @@ graph TB
382
485
  npm install # Установить зависимости
383
486
  npm run build # Сборка проекта (tsup)
384
487
  npm run dev # Разработка с watch-режимом
385
- npm test # Запустить тесты (844 теста, Vitest)
488
+ npm test # Запустить тесты (Vitest)
386
489
  npm run test:watch # Тесты в watch-режиме
387
490
  npm run typecheck # Проверка типов TypeScript
388
491
  npm run lint # Линтинг (= typecheck)
@@ -423,6 +526,19 @@ src/
423
526
  └── bash-parser.ts / .test.ts
424
527
  ```
425
528
 
529
+ ### Добавление нового инструмента
530
+
531
+ 1. Создайте файл в `src/tools/builtins/your-tool.ts`
532
+ 2. Реализуйте интерфейс `Tool` из `src/tools/types.ts`
533
+ 3. Зарегистрируйте в `src/tools/builtins/index.ts`
534
+ 4. Добавьте тесты `your-tool.test.ts`
535
+
536
+ ### Добавление нового провайдера
537
+
538
+ 1. Создайте адаптер в `src/connect/your-adapter.ts`
539
+ 2. Реализуйте интерфейс `ProviderAdapter` из `src/connect/types.ts`
540
+ 3. Зарегистрируйте в `src/connect/registry.ts`
541
+
426
542
  ---
427
543
 
428
544
  ## 🧪 Технологии
@@ -445,14 +561,13 @@ src/
445
561
  | [Ink](https://github.com/vadimdemedes/ink) / [React](https://react.dev) | Terminal UI |
446
562
  | [Ora](https://github.com/sindresorhus/ora) | Спиннеры загрузки |
447
563
  | [ssh2](https://github.com/mscdex/ssh2) | SSH подключения |
448
- | [dotenv](https://github.com/motdotla/dotenv) | Переменные окружения |
449
564
 
450
565
  ### Dev-зависимости
451
566
 
452
567
  | Библиотека | Назначение |
453
568
  |------------|------------|
454
569
  | [tsup](https://tsup.egoist.dev) | Сборка (esbuild-based) |
455
- | [Vitest](https://vitest.dev) | Тестирование (844 теста) |
570
+ | [Vitest](https://vitest.dev) | Тестирование |
456
571
  | [TypeScript](https://www.typescriptlang.org) | Компилятор и проверка типов |
457
572
 
458
573
  ---
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runCli
4
- } from "../chunk-ULZHJVWD.js";
4
+ } from "../chunk-GCXQJ2SV.js";
5
5
  import "../chunk-AJENHWD3.js";
6
6
  export {
7
7
  runCli
@@ -388,6 +388,19 @@ var BaseAdapter = class {
388
388
  }
389
389
  return timeoutSignal;
390
390
  }
391
+ /**
392
+ * Build an AbortSignal for streaming requests with extended timeout.
393
+ * Streaming responses can take much longer than regular requests,
394
+ * so we use 3x the configured timeout to avoid premature aborts.
395
+ */
396
+ buildStreamingSignal(external) {
397
+ const streamTimeout = Math.max(this.config.timeout * 3, 6e5);
398
+ const timeoutSignal = AbortSignal.timeout(streamTimeout);
399
+ if (external) {
400
+ return AbortSignal.any([timeoutSignal, external]);
401
+ }
402
+ return timeoutSignal;
403
+ }
391
404
  /**
392
405
  * Fetch with automatic retry for rate-limit (429), server errors (5xx),
393
406
  * and network failures (TypeError: fetch failed).
@@ -461,7 +474,7 @@ var GenericCompatAdapter = class extends BaseAdapter {
461
474
  method: "POST",
462
475
  headers: this.buildHeaders(),
463
476
  body: JSON.stringify(body),
464
- signal: this.buildSignal(request.signal)
477
+ signal: this.buildStreamingSignal(request.signal)
465
478
  }));
466
479
  if (!response.ok) {
467
480
  const text = await response.text().catch(() => "unknown error");
@@ -670,7 +683,7 @@ var GLMAdapter = class extends BaseAdapter {
670
683
  method: "POST",
671
684
  headers: this.buildHeaders(),
672
685
  body: JSON.stringify(body),
673
- signal: this.buildSignal(request.signal)
686
+ signal: this.buildStreamingSignal(request.signal)
674
687
  }));
675
688
  if (!response.ok) {
676
689
  const text = await response.text().catch(() => "unknown error");
@@ -2061,14 +2074,16 @@ import { z as z13 } from "zod";
2061
2074
  var webSearchInputSchema = z13.object({
2062
2075
  query: z13.string().describe("Search query"),
2063
2076
  max_results: z13.number().default(5).describe("Maximum number of results"),
2064
- search_engine: z13.enum(["duckduckgo", "google"]).default("duckduckgo").describe("Search engine to use")
2077
+ search_engine: z13.enum(["duckduckgo", "wikipedia"]).default("duckduckgo").describe("Search engine to use")
2065
2078
  });
2066
2079
  var SEARCH_TIMEOUT_MS = 15e3;
2067
2080
  var DDG_HTML_URL = "https://html.duckduckgo.com/html/?q=";
2068
2081
  var DDG_LITE_URL = "https://lite.duckduckgo.com/lite/?q=";
2082
+ var WIKI_API_URL = "https://en.wikipedia.org/w/api.php";
2069
2083
  var COMMON_HEADERS = {
2070
- "User-Agent": "Mozilla/5.0 (compatible; cliskill/1.0)",
2071
- Accept: "text/html"
2084
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
2085
+ Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
2086
+ "Accept-Language": "en-US,en;q=0.5"
2072
2087
  };
2073
2088
  function parseDuckDuckGoHtml(html, maxResults) {
2074
2089
  const results = [];
@@ -2120,6 +2135,15 @@ function parseDuckDuckGoLite(html, maxResults) {
2120
2135
  }
2121
2136
  return results;
2122
2137
  }
2138
+ function parseWikipediaResults(data, maxResults) {
2139
+ const search = data.query?.search;
2140
+ if (!search) return [];
2141
+ return search.slice(0, maxResults).map((item) => ({
2142
+ title: item.title,
2143
+ url: `https://en.wikipedia.org/wiki/${encodeURIComponent(item.title.replace(/ /g, "_"))}`,
2144
+ snippet: stripTags(item.snippet)
2145
+ }));
2146
+ }
2123
2147
  function stripTags(html) {
2124
2148
  return html.replace(/<[^>]+>/g, "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&nbsp;/g, " ").trim();
2125
2149
  }
@@ -2141,13 +2165,13 @@ No results found. Try rephrasing your query or use web_fetch for specific URLs.`
2141
2165
  }
2142
2166
  return lines.join("\n").trim();
2143
2167
  }
2144
- async function fetchWithTimeout(url) {
2168
+ async function fetchWithTimeout(url, headers) {
2145
2169
  const controller = new AbortController();
2146
2170
  const timeout = setTimeout(() => controller.abort(), SEARCH_TIMEOUT_MS);
2147
2171
  try {
2148
2172
  const response = await fetch(url, {
2149
2173
  signal: controller.signal,
2150
- headers: COMMON_HEADERS,
2174
+ headers: headers ?? COMMON_HEADERS,
2151
2175
  redirect: "follow"
2152
2176
  });
2153
2177
  return response;
@@ -2157,32 +2181,58 @@ async function fetchWithTimeout(url) {
2157
2181
  }
2158
2182
  var WebSearchTool = class extends BaseTool {
2159
2183
  name = "web_search";
2160
- description = "Search the web for information";
2184
+ description = "Search the web for information using DuckDuckGo or Wikipedia";
2161
2185
  inputSchema = webSearchInputSchema;
2162
2186
  riskLevel = "safe";
2163
2187
  concurrencySafe = true;
2164
2188
  readOnly = true;
2165
2189
  async execute(input, _context) {
2166
- if (input.search_engine !== "duckduckgo") {
2167
- return `Error: Only DuckDuckGo search engine is currently supported. Use web_fetch for other sources.`;
2168
- }
2169
2190
  const encodedQuery = encodeURIComponent(input.query);
2191
+ if (input.search_engine === "wikipedia") {
2192
+ return this.searchWikipedia(input.query, encodedQuery, input.max_results);
2193
+ }
2194
+ return this.searchDuckDuckGo(input.query, encodedQuery, input.max_results);
2195
+ }
2196
+ async searchDuckDuckGo(query, encodedQuery, maxResults) {
2170
2197
  try {
2171
2198
  const response = await fetchWithTimeout(`${DDG_HTML_URL}${encodedQuery}`);
2172
2199
  if (!response.ok) {
2173
- return `Error: Search request failed with HTTP ${response.status}. Try using web_fetch instead.`;
2200
+ return this.fallbackToWikipedia(
2201
+ query,
2202
+ encodedQuery,
2203
+ maxResults,
2204
+ `DuckDuckGo returned HTTP ${response.status}`
2205
+ );
2174
2206
  }
2175
2207
  const html = await response.text();
2176
- let results = parseDuckDuckGoHtml(html, input.max_results);
2208
+ let results = parseDuckDuckGoHtml(html, maxResults);
2177
2209
  if (results.length === 0) {
2178
- results = await this.fallbackLiteSearch(encodedQuery, input.max_results);
2210
+ results = await this.fallbackLiteSearch(encodedQuery, maxResults);
2179
2211
  }
2180
- return formatResults(input.query, results);
2212
+ if (results.length === 0) {
2213
+ return this.fallbackToWikipedia(
2214
+ query,
2215
+ encodedQuery,
2216
+ maxResults,
2217
+ "DuckDuckGo returned no results"
2218
+ );
2219
+ }
2220
+ return formatResults(query, results);
2181
2221
  } catch (err) {
2182
2222
  if (err.name === "AbortError") {
2183
- return "Error: Search request timed out after 15 seconds. Try using web_fetch instead.";
2223
+ return this.fallbackToWikipedia(
2224
+ query,
2225
+ encodedQuery,
2226
+ maxResults,
2227
+ "DuckDuckGo request timed out"
2228
+ );
2184
2229
  }
2185
- return `Error searching: ${err.message}. Try using web_fetch instead.`;
2230
+ return this.fallbackToWikipedia(
2231
+ query,
2232
+ encodedQuery,
2233
+ maxResults,
2234
+ err.message
2235
+ );
2186
2236
  }
2187
2237
  }
2188
2238
  async fallbackLiteSearch(encodedQuery, maxResults) {
@@ -2195,6 +2245,48 @@ var WebSearchTool = class extends BaseTool {
2195
2245
  return [];
2196
2246
  }
2197
2247
  }
2248
+ async searchWikipedia(query, encodedQuery, maxResults) {
2249
+ try {
2250
+ const url = `${WIKI_API_URL}?action=query&list=search&srsearch=${encodedQuery}&srlimit=${maxResults}&format=json&origin=*`;
2251
+ const response = await fetchWithTimeout(url, {
2252
+ "User-Agent": "cliskill/1.0 (https://github.com/nic11/cliskill)"
2253
+ });
2254
+ if (!response.ok) {
2255
+ return `Error: Wikipedia search failed with HTTP ${response.status}. Try web_fetch instead.`;
2256
+ }
2257
+ const data = await response.json();
2258
+ const results = parseWikipediaResults(data, maxResults);
2259
+ return formatResults(query, results);
2260
+ } catch (err) {
2261
+ if (err.name === "AbortError") {
2262
+ return "Error: Wikipedia search timed out after 15 seconds. Try web_fetch instead.";
2263
+ }
2264
+ return `Error searching Wikipedia: ${err.message}. Try web_fetch instead.`;
2265
+ }
2266
+ }
2267
+ async fallbackToWikipedia(query, encodedQuery, maxResults, reason) {
2268
+ const wikiResults = await this.searchWikipediaRaw(encodedQuery, maxResults);
2269
+ if (wikiResults.length > 0) {
2270
+ const header = `\u26A0\uFE0F DuckDuckGo unavailable (${reason}). Showing Wikipedia results:
2271
+
2272
+ `;
2273
+ return header + formatResults(query, wikiResults);
2274
+ }
2275
+ return `Error: Search failed (${reason}) and Wikipedia fallback returned no results. Try web_fetch for specific URLs.`;
2276
+ }
2277
+ async searchWikipediaRaw(encodedQuery, maxResults) {
2278
+ try {
2279
+ const url = `${WIKI_API_URL}?action=query&list=search&srsearch=${encodedQuery}&srlimit=${maxResults}&format=json&origin=*`;
2280
+ const response = await fetchWithTimeout(url, {
2281
+ "User-Agent": "cliskill/1.0 (https://github.com/nic11/cliskill)"
2282
+ });
2283
+ if (!response.ok) return [];
2284
+ const data = await response.json();
2285
+ return parseWikipediaResults(data, maxResults);
2286
+ } catch {
2287
+ return [];
2288
+ }
2289
+ }
2198
2290
  };
2199
2291
 
2200
2292
  // src/tools/builtins/lsp-tool.ts
@@ -9942,4 +10034,4 @@ export {
9942
10034
  MessageList,
9943
10035
  runCli
9944
10036
  };
9945
- //# sourceMappingURL=chunk-ULZHJVWD.js.map
10037
+ //# sourceMappingURL=chunk-GCXQJ2SV.js.map