@kata-sh/cli 0.1.0 → 0.1.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/LICENSE +21 -0
- package/README.md +156 -0
- package/dist/app-paths.d.ts +4 -0
- package/dist/app-paths.js +6 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +56 -0
- package/dist/loader.d.ts +2 -0
- package/dist/loader.js +95 -0
- package/dist/resource-loader.d.ts +18 -0
- package/dist/resource-loader.js +50 -0
- package/dist/wizard.d.ts +15 -0
- package/dist/wizard.js +159 -0
- package/package.json +50 -21
- package/pkg/dist/modes/interactive/theme/dark.json +85 -0
- package/pkg/dist/modes/interactive/theme/light.json +84 -0
- package/pkg/dist/modes/interactive/theme/theme-schema.json +335 -0
- package/pkg/dist/modes/interactive/theme/theme.d.ts +78 -0
- package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -0
- package/pkg/dist/modes/interactive/theme/theme.js +949 -0
- package/pkg/dist/modes/interactive/theme/theme.js.map +1 -0
- package/pkg/package.json +8 -0
- package/scripts/postinstall.js +45 -0
- package/src/resources/AGENTS.md +108 -0
- package/src/resources/KATA-WORKFLOW.md +661 -0
- package/src/resources/agents/researcher.md +29 -0
- package/src/resources/agents/scout.md +56 -0
- package/src/resources/agents/worker.md +31 -0
- package/src/resources/extensions/ask-user-questions.ts +200 -0
- package/src/resources/extensions/bg-shell/index.ts +2758 -0
- package/src/resources/extensions/browser-tools/BROWSER-TOOLS-V2-PROPOSAL.md +1277 -0
- package/src/resources/extensions/browser-tools/core.js +1057 -0
- package/src/resources/extensions/browser-tools/index.ts +4916 -0
- package/src/resources/extensions/browser-tools/package.json +20 -0
- package/src/resources/extensions/context7/index.ts +428 -0
- package/src/resources/extensions/context7/package.json +11 -0
- package/src/resources/extensions/get-secrets-from-user.ts +352 -0
- package/src/resources/extensions/github/formatters.ts +207 -0
- package/src/resources/extensions/github/gh-api.ts +537 -0
- package/src/resources/extensions/github/index.ts +778 -0
- package/src/resources/extensions/kata/activity-log.ts +88 -0
- package/src/resources/extensions/kata/auto.ts +2786 -0
- package/src/resources/extensions/kata/commands.ts +355 -0
- package/src/resources/extensions/kata/crash-recovery.ts +85 -0
- package/src/resources/extensions/kata/dashboard-overlay.ts +516 -0
- package/src/resources/extensions/kata/docs/preferences-reference.md +103 -0
- package/src/resources/extensions/kata/doctor.ts +683 -0
- package/src/resources/extensions/kata/files.ts +730 -0
- package/src/resources/extensions/kata/gitignore.ts +165 -0
- package/src/resources/extensions/kata/guided-flow.ts +976 -0
- package/src/resources/extensions/kata/index.ts +556 -0
- package/src/resources/extensions/kata/metrics.ts +397 -0
- package/src/resources/extensions/kata/observability-validator.ts +408 -0
- package/src/resources/extensions/kata/package.json +11 -0
- package/src/resources/extensions/kata/paths.ts +346 -0
- package/src/resources/extensions/kata/preferences.ts +695 -0
- package/src/resources/extensions/kata/prompt-loader.ts +50 -0
- package/src/resources/extensions/kata/prompts/complete-milestone.md +25 -0
- package/src/resources/extensions/kata/prompts/complete-slice.md +27 -0
- package/src/resources/extensions/kata/prompts/discuss.md +151 -0
- package/src/resources/extensions/kata/prompts/doctor-heal.md +29 -0
- package/src/resources/extensions/kata/prompts/execute-task.md +64 -0
- package/src/resources/extensions/kata/prompts/guided-complete-slice.md +1 -0
- package/src/resources/extensions/kata/prompts/guided-discuss-milestone.md +3 -0
- package/src/resources/extensions/kata/prompts/guided-discuss-slice.md +59 -0
- package/src/resources/extensions/kata/prompts/guided-execute-task.md +1 -0
- package/src/resources/extensions/kata/prompts/guided-plan-milestone.md +23 -0
- package/src/resources/extensions/kata/prompts/guided-plan-slice.md +1 -0
- package/src/resources/extensions/kata/prompts/guided-research-slice.md +11 -0
- package/src/resources/extensions/kata/prompts/guided-resume-task.md +1 -0
- package/src/resources/extensions/kata/prompts/plan-milestone.md +47 -0
- package/src/resources/extensions/kata/prompts/plan-slice.md +63 -0
- package/src/resources/extensions/kata/prompts/queue.md +85 -0
- package/src/resources/extensions/kata/prompts/reassess-roadmap.md +48 -0
- package/src/resources/extensions/kata/prompts/replan-slice.md +39 -0
- package/src/resources/extensions/kata/prompts/research-milestone.md +37 -0
- package/src/resources/extensions/kata/prompts/research-slice.md +28 -0
- package/src/resources/extensions/kata/prompts/run-uat.md +109 -0
- package/src/resources/extensions/kata/prompts/system.md +341 -0
- package/src/resources/extensions/kata/session-forensics.ts +550 -0
- package/src/resources/extensions/kata/skill-discovery.ts +137 -0
- package/src/resources/extensions/kata/state.ts +509 -0
- package/src/resources/extensions/kata/templates/context.md +76 -0
- package/src/resources/extensions/kata/templates/decisions.md +8 -0
- package/src/resources/extensions/kata/templates/milestone-summary.md +73 -0
- package/src/resources/extensions/kata/templates/plan.md +133 -0
- package/src/resources/extensions/kata/templates/preferences.md +15 -0
- package/src/resources/extensions/kata/templates/project.md +31 -0
- package/src/resources/extensions/kata/templates/reassessment.md +28 -0
- package/src/resources/extensions/kata/templates/requirements.md +81 -0
- package/src/resources/extensions/kata/templates/research.md +46 -0
- package/src/resources/extensions/kata/templates/roadmap.md +118 -0
- package/src/resources/extensions/kata/templates/slice-context.md +58 -0
- package/src/resources/extensions/kata/templates/slice-summary.md +99 -0
- package/src/resources/extensions/kata/templates/state.md +19 -0
- package/src/resources/extensions/kata/templates/task-plan.md +52 -0
- package/src/resources/extensions/kata/templates/task-summary.md +57 -0
- package/src/resources/extensions/kata/templates/uat.md +54 -0
- package/src/resources/extensions/kata/tests/activity-log-prune.test.ts +327 -0
- package/src/resources/extensions/kata/tests/auto-preflight.test.ts +97 -0
- package/src/resources/extensions/kata/tests/auto-supervisor.test.mjs +53 -0
- package/src/resources/extensions/kata/tests/complete-milestone.test.ts +317 -0
- package/src/resources/extensions/kata/tests/cost-projection.test.ts +160 -0
- package/src/resources/extensions/kata/tests/derive-state-deps.test.ts +477 -0
- package/src/resources/extensions/kata/tests/derive-state.test.ts +1013 -0
- package/src/resources/extensions/kata/tests/doctor.test.ts +718 -0
- package/src/resources/extensions/kata/tests/idle-recovery.test.ts +490 -0
- package/src/resources/extensions/kata/tests/metrics-io.test.ts +254 -0
- package/src/resources/extensions/kata/tests/metrics.test.ts +217 -0
- package/src/resources/extensions/kata/tests/must-have-parser.test.ts +309 -0
- package/src/resources/extensions/kata/tests/parsers.test.ts +1257 -0
- package/src/resources/extensions/kata/tests/plan-milestone.test.ts +185 -0
- package/src/resources/extensions/kata/tests/plan-quality-validator.test.ts +386 -0
- package/src/resources/extensions/kata/tests/reassess-prompt.test.ts +208 -0
- package/src/resources/extensions/kata/tests/replan-slice.test.ts +686 -0
- package/src/resources/extensions/kata/tests/requirements.test.ts +151 -0
- package/src/resources/extensions/kata/tests/resolve-ts-hooks.mjs +17 -0
- package/src/resources/extensions/kata/tests/resolve-ts.mjs +11 -0
- package/src/resources/extensions/kata/tests/run-uat.test.ts +383 -0
- package/src/resources/extensions/kata/tests/unit-runtime.test.ts +388 -0
- package/src/resources/extensions/kata/tests/workspace-index.test.ts +118 -0
- package/src/resources/extensions/kata/tests/worktree.test.ts +222 -0
- package/src/resources/extensions/kata/types.ts +159 -0
- package/src/resources/extensions/kata/unit-runtime.ts +163 -0
- package/src/resources/extensions/kata/workspace-index.ts +203 -0
- package/src/resources/extensions/kata/worktree.ts +182 -0
- package/src/resources/extensions/mac-tools/index.ts +852 -0
- package/src/resources/extensions/mac-tools/swift-cli/Package.swift +22 -0
- package/src/resources/extensions/mac-tools/swift-cli/Sources/main.swift +1318 -0
- package/src/resources/extensions/search-the-web/cache.ts +78 -0
- package/src/resources/extensions/search-the-web/format.ts +258 -0
- package/src/resources/extensions/search-the-web/http.ts +238 -0
- package/src/resources/extensions/search-the-web/index.ts +68 -0
- package/src/resources/extensions/search-the-web/tool-fetch-page.ts +519 -0
- package/src/resources/extensions/search-the-web/tool-llm-context.ts +404 -0
- package/src/resources/extensions/search-the-web/tool-search.ts +503 -0
- package/src/resources/extensions/search-the-web/url-utils.ts +91 -0
- package/src/resources/extensions/shared/confirm-ui.ts +126 -0
- package/src/resources/extensions/shared/interview-ui.ts +822 -0
- package/src/resources/extensions/shared/next-action-ui.ts +235 -0
- package/src/resources/extensions/shared/progress-widget.ts +282 -0
- package/src/resources/extensions/shared/thinking-widget.ts +107 -0
- package/src/resources/extensions/shared/ui.ts +400 -0
- package/src/resources/extensions/shared/wizard-ui.ts +551 -0
- package/src/resources/extensions/slash-commands/audit.ts +92 -0
- package/src/resources/extensions/slash-commands/create-extension.ts +375 -0
- package/src/resources/extensions/slash-commands/create-slash-command.ts +280 -0
- package/src/resources/extensions/slash-commands/index.ts +12 -0
- package/src/resources/extensions/slash-commands/kata-run.ts +34 -0
- package/src/resources/extensions/subagent/agents.ts +126 -0
- package/src/resources/extensions/subagent/index.ts +1293 -0
- package/src/resources/skills/debug-like-expert/SKILL.md +231 -0
- package/src/resources/skills/debug-like-expert/references/debugging-mindset.md +253 -0
- package/src/resources/skills/debug-like-expert/references/hypothesis-testing.md +373 -0
- package/src/resources/skills/debug-like-expert/references/investigation-techniques.md +337 -0
- package/src/resources/skills/debug-like-expert/references/verification-patterns.md +425 -0
- package/src/resources/skills/debug-like-expert/references/when-to-research.md +361 -0
- package/src/resources/skills/frontend-design/SKILL.md +45 -0
- package/src/resources/skills/swiftui/SKILL.md +208 -0
- package/src/resources/skills/swiftui/references/animations.md +921 -0
- package/src/resources/skills/swiftui/references/architecture.md +1561 -0
- package/src/resources/skills/swiftui/references/layout-system.md +1186 -0
- package/src/resources/skills/swiftui/references/navigation.md +1492 -0
- package/src/resources/skills/swiftui/references/networking-async.md +214 -0
- package/src/resources/skills/swiftui/references/performance.md +1706 -0
- package/src/resources/skills/swiftui/references/platform-integration.md +204 -0
- package/src/resources/skills/swiftui/references/state-management.md +1443 -0
- package/src/resources/skills/swiftui/references/swiftdata.md +297 -0
- package/src/resources/skills/swiftui/references/testing-debugging.md +247 -0
- package/src/resources/skills/swiftui/references/uikit-appkit-interop.md +218 -0
- package/src/resources/skills/swiftui/workflows/add-feature.md +191 -0
- package/src/resources/skills/swiftui/workflows/build-new-app.md +311 -0
- package/src/resources/skills/swiftui/workflows/debug-swiftui.md +192 -0
- package/src/resources/skills/swiftui/workflows/optimize-performance.md +197 -0
- package/src/resources/skills/swiftui/workflows/ship-app.md +203 -0
- package/src/resources/skills/swiftui/workflows/write-tests.md +235 -0
- package/dist/commands/task.d.ts +0 -9
- package/dist/commands/task.d.ts.map +0 -1
- package/dist/commands/task.js +0 -129
- package/dist/commands/task.js.map +0 -1
- package/dist/commands/task.test.d.ts +0 -2
- package/dist/commands/task.test.d.ts.map +0 -1
- package/dist/commands/task.test.js +0 -169
- package/dist/commands/task.test.js.map +0 -1
- package/dist/e2e/task-e2e.test.d.ts +0 -2
- package/dist/e2e/task-e2e.test.d.ts.map +0 -1
- package/dist/e2e/task-e2e.test.js +0 -173
- package/dist/e2e/task-e2e.test.js.map +0 -1
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -93
- package/dist/index.js.map +0 -1
- package/dist/slug.d.ts +0 -2
- package/dist/slug.d.ts.map +0 -1
- package/dist/slug.js +0 -12
- package/dist/slug.js.map +0 -1
- package/dist/slug.test.d.ts +0 -2
- package/dist/slug.test.d.ts.map +0 -1
- package/dist/slug.test.js +0 -32
- package/dist/slug.test.js.map +0 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pi-browser-tools",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "node --test tests/*.test.mjs"
|
|
8
|
+
},
|
|
9
|
+
"pi": {
|
|
10
|
+
"extensions": ["./index.ts"]
|
|
11
|
+
},
|
|
12
|
+
"peerDependencies": {
|
|
13
|
+
"playwright": ">=1.40.0"
|
|
14
|
+
},
|
|
15
|
+
"peerDependenciesMeta": {
|
|
16
|
+
"playwright": {
|
|
17
|
+
"optional": true
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context7 Documentation Extension
|
|
3
|
+
*
|
|
4
|
+
* Replaces the context7 MCP server with a native pi extension.
|
|
5
|
+
* Provides two tools for the LLM:
|
|
6
|
+
*
|
|
7
|
+
* resolve_library - Search for a library by name, returns candidates with metadata
|
|
8
|
+
* get_library_docs - Fetch docs for a library ID, scoped to an optional query/topic
|
|
9
|
+
*
|
|
10
|
+
* API contract (verified against live API 2026-03-04):
|
|
11
|
+
* Search: GET /api/v2/libs/search?libraryName=&query= → { results: C7Library[] }
|
|
12
|
+
* Context: GET /api/v2/context?libraryId=&query=&tokens= → text/plain (markdown)
|
|
13
|
+
*
|
|
14
|
+
* Features:
|
|
15
|
+
* - Bearer auth via CONTEXT7_API_KEY env var (optional, increases rate limits)
|
|
16
|
+
* - In-session caching of search results and doc pages
|
|
17
|
+
* - Smart token budgeting (default 5000, configurable per call, max 10000)
|
|
18
|
+
* - Proper truncation guard so context is never overwhelmed
|
|
19
|
+
* - Custom TUI rendering for clean display in pi
|
|
20
|
+
*
|
|
21
|
+
* Setup:
|
|
22
|
+
* export CONTEXT7_API_KEY=your_key (get one at context7.com/dashboard)
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
26
|
+
import {
|
|
27
|
+
DEFAULT_MAX_BYTES,
|
|
28
|
+
DEFAULT_MAX_LINES,
|
|
29
|
+
formatSize,
|
|
30
|
+
truncateHead,
|
|
31
|
+
} from "@mariozechner/pi-coding-agent";
|
|
32
|
+
import { Text } from "@mariozechner/pi-tui";
|
|
33
|
+
import { Type } from "@sinclair/typebox";
|
|
34
|
+
|
|
35
|
+
// ─── API types ────────────────────────────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
/** Shape returned by GET /api/v2/libs/search */
|
|
38
|
+
interface C7SearchResponse {
|
|
39
|
+
results: C7Library[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface C7Library {
|
|
43
|
+
id: string;
|
|
44
|
+
title: string;
|
|
45
|
+
description?: string;
|
|
46
|
+
branch?: string;
|
|
47
|
+
lastUpdateDate?: string;
|
|
48
|
+
state?: string;
|
|
49
|
+
totalTokens?: number;
|
|
50
|
+
totalSnippets?: number;
|
|
51
|
+
stars?: number;
|
|
52
|
+
trustScore?: number;
|
|
53
|
+
benchmarkScore?: number;
|
|
54
|
+
versions?: string[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ─── In-session cache ─────────────────────────────────────────────────────────
|
|
58
|
+
|
|
59
|
+
// Keyed by lowercased query string
|
|
60
|
+
const searchCache = new Map<string, C7Library[]>();
|
|
61
|
+
|
|
62
|
+
// Keyed by `${libraryId}::${query ?? ""}::${tokens}`
|
|
63
|
+
const docCache = new Map<string, string>();
|
|
64
|
+
|
|
65
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
const BASE_URL = "https://context7.com/api/v2";
|
|
68
|
+
|
|
69
|
+
function getApiKey(): string | undefined {
|
|
70
|
+
return process.env.CONTEXT7_API_KEY;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function buildHeaders(): Record<string, string> {
|
|
74
|
+
const headers: Record<string, string> = {
|
|
75
|
+
"User-Agent": "pi-coding-agent/context7-extension",
|
|
76
|
+
};
|
|
77
|
+
const key = getApiKey();
|
|
78
|
+
if (key) headers["Authorization"] = `Bearer ${key}`;
|
|
79
|
+
return headers;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function apiFetchJson(url: string, signal?: AbortSignal): Promise<unknown> {
|
|
83
|
+
const res = await fetch(url, { headers: { ...buildHeaders(), Accept: "application/json" }, signal });
|
|
84
|
+
if (!res.ok) {
|
|
85
|
+
const body = await res.text().catch(() => "");
|
|
86
|
+
throw new Error(`Context7 API ${res.status}: ${body.slice(0, 300)}`);
|
|
87
|
+
}
|
|
88
|
+
return res.json();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function apiFetchText(url: string, signal?: AbortSignal): Promise<string> {
|
|
92
|
+
const res = await fetch(url, { headers: { ...buildHeaders(), Accept: "text/plain" }, signal });
|
|
93
|
+
if (!res.ok) {
|
|
94
|
+
const body = await res.text().catch(() => "");
|
|
95
|
+
throw new Error(`Context7 API ${res.status}: ${body.slice(0, 300)}`);
|
|
96
|
+
}
|
|
97
|
+
return res.text();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Format library search results into a compact, LLM-readable string.
|
|
102
|
+
* Each library gets a block with the key signals for picking the best match.
|
|
103
|
+
*/
|
|
104
|
+
function formatLibraryList(libs: C7Library[], query: string): string {
|
|
105
|
+
if (libs.length === 0) {
|
|
106
|
+
return `No libraries found for "${query}". Try a different name or spelling.`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const lines: string[] = [
|
|
110
|
+
`Found ${libs.length} ${libs.length === 1 ? "library" : "libraries"} matching "${query}":\n`,
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
for (const lib of libs) {
|
|
114
|
+
let line = `• ${lib.title} (ID: ${lib.id})`;
|
|
115
|
+
if (lib.description) line += `\n ${lib.description}`;
|
|
116
|
+
|
|
117
|
+
const meta: string[] = [];
|
|
118
|
+
if (lib.trustScore !== undefined) meta.push(`trust: ${lib.trustScore}/10`);
|
|
119
|
+
if (lib.benchmarkScore !== undefined) meta.push(`benchmark: ${lib.benchmarkScore.toFixed(1)}`);
|
|
120
|
+
if (lib.totalSnippets !== undefined) meta.push(`${lib.totalSnippets.toLocaleString()} snippets`);
|
|
121
|
+
if (lib.totalTokens !== undefined) meta.push(`${(lib.totalTokens / 1000).toFixed(0)}k tokens`);
|
|
122
|
+
if (lib.lastUpdateDate) meta.push(`updated: ${lib.lastUpdateDate.split("T")[0]}`);
|
|
123
|
+
if (meta.length > 0) line += `\n ${meta.join(" · ")}`;
|
|
124
|
+
|
|
125
|
+
lines.push(line);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
lines.push(
|
|
129
|
+
"\nUse the ID (e.g. /websites/react_dev) with get_library_docs to fetch documentation.",
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
return lines.join("\n");
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ─── Tool details types ───────────────────────────────────────────────────────
|
|
136
|
+
|
|
137
|
+
interface ResolveDetails {
|
|
138
|
+
query: string;
|
|
139
|
+
resultCount: number;
|
|
140
|
+
cached: boolean;
|
|
141
|
+
error?: string;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
interface DocsDetails {
|
|
145
|
+
libraryId: string;
|
|
146
|
+
query?: string;
|
|
147
|
+
tokens: number;
|
|
148
|
+
cached: boolean;
|
|
149
|
+
truncated: boolean;
|
|
150
|
+
charCount: number;
|
|
151
|
+
error?: string;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ─── Extension ───────────────────────────────────────────────────────────────
|
|
155
|
+
|
|
156
|
+
export default function (pi: ExtensionAPI) {
|
|
157
|
+
// ── resolve_library ──────────────────────────────────────────────────────
|
|
158
|
+
|
|
159
|
+
pi.registerTool({
|
|
160
|
+
name: "resolve_library",
|
|
161
|
+
label: "Resolve Library",
|
|
162
|
+
description:
|
|
163
|
+
"Search the Context7 library catalogue by name and return matching libraries with metadata. " +
|
|
164
|
+
"Use this to find the correct library ID before fetching documentation. " +
|
|
165
|
+
"Results are ranked by trustScore (0–10) and benchmarkScore — prefer the highest. " +
|
|
166
|
+
"If you already have a library ID (e.g. /vercel/next.js), skip this and call get_library_docs directly.",
|
|
167
|
+
promptSnippet: "Search Context7 for a library by name to get its ID for documentation lookup",
|
|
168
|
+
promptGuidelines: [
|
|
169
|
+
"Call resolve_library first when the user asks about a library, package, or framework you need current docs for.",
|
|
170
|
+
"Choose the result with the highest trustScore and benchmarkScore when multiple matches appear.",
|
|
171
|
+
"Pass the user's question as the query parameter — it improves result ranking.",
|
|
172
|
+
],
|
|
173
|
+
parameters: Type.Object({
|
|
174
|
+
libraryName: Type.String({
|
|
175
|
+
description:
|
|
176
|
+
"Library or framework name to search for, e.g. 'react', 'next.js', 'tailwindcss', 'prisma', 'langchain'",
|
|
177
|
+
}),
|
|
178
|
+
query: Type.Optional(
|
|
179
|
+
Type.String({
|
|
180
|
+
description:
|
|
181
|
+
"Optional: the user's question or topic. Improves search ranking. E.g. 'how do I use server actions?'",
|
|
182
|
+
}),
|
|
183
|
+
),
|
|
184
|
+
}),
|
|
185
|
+
|
|
186
|
+
async execute(_toolCallId, params, signal, _onUpdate, _ctx) {
|
|
187
|
+
const cacheKey = params.libraryName.toLowerCase().trim();
|
|
188
|
+
|
|
189
|
+
if (searchCache.has(cacheKey)) {
|
|
190
|
+
const cached = searchCache.get(cacheKey)!;
|
|
191
|
+
return {
|
|
192
|
+
content: [{ type: "text", text: formatLibraryList(cached, params.libraryName) }],
|
|
193
|
+
details: {
|
|
194
|
+
query: params.libraryName,
|
|
195
|
+
resultCount: cached.length,
|
|
196
|
+
cached: true,
|
|
197
|
+
} as ResolveDetails,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const url = new URL(`${BASE_URL}/libs/search`);
|
|
202
|
+
url.searchParams.set("libraryName", params.libraryName);
|
|
203
|
+
if (params.query) url.searchParams.set("query", params.query);
|
|
204
|
+
|
|
205
|
+
let libs: C7Library[];
|
|
206
|
+
try {
|
|
207
|
+
const data = (await apiFetchJson(url.toString(), signal)) as C7SearchResponse;
|
|
208
|
+
libs = Array.isArray(data?.results) ? data.results : [];
|
|
209
|
+
} catch (err: unknown) {
|
|
210
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
211
|
+
return {
|
|
212
|
+
content: [{ type: "text", text: `Context7 search failed: ${msg}` }],
|
|
213
|
+
isError: true,
|
|
214
|
+
details: { query: params.libraryName, resultCount: 0, cached: false, error: msg } as ResolveDetails,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
searchCache.set(cacheKey, libs);
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
content: [{ type: "text", text: formatLibraryList(libs, params.libraryName) }],
|
|
222
|
+
details: { query: params.libraryName, resultCount: libs.length, cached: false } as ResolveDetails,
|
|
223
|
+
};
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
renderCall(args, theme) {
|
|
227
|
+
let text = theme.fg("toolTitle", theme.bold("resolve_library "));
|
|
228
|
+
text += theme.fg("accent", `"${args.libraryName}"`);
|
|
229
|
+
if (args.query) text += theme.fg("muted", ` — "${args.query}"`);
|
|
230
|
+
return new Text(text, 0, 0);
|
|
231
|
+
},
|
|
232
|
+
|
|
233
|
+
renderResult(result, { isPartial }, theme) {
|
|
234
|
+
const d = result.details as ResolveDetails | undefined;
|
|
235
|
+
if (isPartial) return new Text(theme.fg("warning", "Searching Context7..."), 0, 0);
|
|
236
|
+
if (result.isError || d?.error) {
|
|
237
|
+
return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
|
|
238
|
+
}
|
|
239
|
+
let text = theme.fg("success", `${d?.resultCount ?? 0} ${d?.resultCount === 1 ? "library" : "libraries"} found`);
|
|
240
|
+
if (d?.cached) text += theme.fg("dim", " (cached)");
|
|
241
|
+
text += theme.fg("dim", ` for "${d?.query}"`);
|
|
242
|
+
return new Text(text, 0, 0);
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// ── get_library_docs ─────────────────────────────────────────────────────
|
|
247
|
+
|
|
248
|
+
pi.registerTool({
|
|
249
|
+
name: "get_library_docs",
|
|
250
|
+
label: "Get Library Docs",
|
|
251
|
+
description:
|
|
252
|
+
"Fetch up-to-date documentation from Context7 for a specific library. " +
|
|
253
|
+
"Pass the library ID from resolve_library (e.g. /websites/react_dev) and a focused topic query " +
|
|
254
|
+
"to get the most relevant snippets. " +
|
|
255
|
+
"The tokens parameter controls how much documentation to retrieve (default 5000, max 10000). " +
|
|
256
|
+
"A specific query (e.g. 'server actions form submission') returns better results than a broad one.",
|
|
257
|
+
promptSnippet: "Fetch up-to-date, version-specific documentation for a library from Context7",
|
|
258
|
+
promptGuidelines: [
|
|
259
|
+
"Use a specific topic query for best results — e.g. 'useEffect cleanup' not just 'hooks'.",
|
|
260
|
+
"Start with tokens=5000. Increase to 10000 only if the first response lacks the detail you need.",
|
|
261
|
+
"Results are cached per-session — repeated calls for the same library+query have no API cost.",
|
|
262
|
+
],
|
|
263
|
+
parameters: Type.Object({
|
|
264
|
+
libraryId: Type.String({
|
|
265
|
+
description:
|
|
266
|
+
"Context7 library ID from resolve_library, e.g. /websites/react_dev or /vercel/next.js",
|
|
267
|
+
}),
|
|
268
|
+
query: Type.Optional(
|
|
269
|
+
Type.String({
|
|
270
|
+
description:
|
|
271
|
+
"Specific topic to focus the docs on, e.g. 'server actions', 'useEffect cleanup', 'authentication middleware'. More specific = better results.",
|
|
272
|
+
}),
|
|
273
|
+
),
|
|
274
|
+
tokens: Type.Optional(
|
|
275
|
+
Type.Number({
|
|
276
|
+
description: "Max tokens of documentation to return (default 5000, max 10000).",
|
|
277
|
+
minimum: 500,
|
|
278
|
+
maximum: 10000,
|
|
279
|
+
}),
|
|
280
|
+
),
|
|
281
|
+
}),
|
|
282
|
+
|
|
283
|
+
async execute(_toolCallId, params, signal, _onUpdate, _ctx) {
|
|
284
|
+
const tokens = Math.min(Math.max(params.tokens ?? 5000, 500), 10000);
|
|
285
|
+
// Strip accidental leading @ that some models inject
|
|
286
|
+
const libraryId = params.libraryId.startsWith("@")
|
|
287
|
+
? params.libraryId.slice(1)
|
|
288
|
+
: params.libraryId;
|
|
289
|
+
const query = params.query?.trim() || undefined;
|
|
290
|
+
|
|
291
|
+
const cacheKey = `${libraryId}::${query ?? ""}::${tokens}`;
|
|
292
|
+
|
|
293
|
+
if (docCache.has(cacheKey)) {
|
|
294
|
+
const cached = docCache.get(cacheKey)!;
|
|
295
|
+
return {
|
|
296
|
+
content: [{ type: "text", text: cached }],
|
|
297
|
+
details: {
|
|
298
|
+
libraryId,
|
|
299
|
+
query,
|
|
300
|
+
tokens,
|
|
301
|
+
cached: true,
|
|
302
|
+
truncated: false,
|
|
303
|
+
charCount: cached.length,
|
|
304
|
+
} as DocsDetails,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const url = new URL(`${BASE_URL}/context`);
|
|
309
|
+
url.searchParams.set("libraryId", libraryId);
|
|
310
|
+
if (query) url.searchParams.set("query", query);
|
|
311
|
+
url.searchParams.set("tokens", String(tokens));
|
|
312
|
+
|
|
313
|
+
let rawText: string;
|
|
314
|
+
try {
|
|
315
|
+
rawText = await apiFetchText(url.toString(), signal);
|
|
316
|
+
} catch (err: unknown) {
|
|
317
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
318
|
+
return {
|
|
319
|
+
content: [{ type: "text", text: `Context7 doc fetch failed: ${msg}` }],
|
|
320
|
+
isError: true,
|
|
321
|
+
details: {
|
|
322
|
+
libraryId,
|
|
323
|
+
query,
|
|
324
|
+
tokens,
|
|
325
|
+
cached: false,
|
|
326
|
+
truncated: false,
|
|
327
|
+
charCount: 0,
|
|
328
|
+
error: msg,
|
|
329
|
+
} as DocsDetails,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (!rawText.trim()) {
|
|
334
|
+
const notFound = query
|
|
335
|
+
? `No documentation found for "${query}" in ${libraryId}. Try a broader query or different library ID.`
|
|
336
|
+
: `No documentation found for ${libraryId}. Try resolve_library to verify the library ID.`;
|
|
337
|
+
return {
|
|
338
|
+
content: [{ type: "text", text: notFound }],
|
|
339
|
+
details: {
|
|
340
|
+
libraryId,
|
|
341
|
+
query,
|
|
342
|
+
tokens,
|
|
343
|
+
cached: false,
|
|
344
|
+
truncated: false,
|
|
345
|
+
charCount: 0,
|
|
346
|
+
} as DocsDetails,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Truncation guard — Context7 already respects the token budget, but be defensive
|
|
351
|
+
const truncation = truncateHead(rawText, {
|
|
352
|
+
maxLines: DEFAULT_MAX_LINES,
|
|
353
|
+
maxBytes: DEFAULT_MAX_BYTES,
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
let finalText = truncation.content;
|
|
357
|
+
if (truncation.truncated) {
|
|
358
|
+
finalText +=
|
|
359
|
+
`\n\n[Truncated: showing ${truncation.outputLines}/${truncation.totalLines} lines` +
|
|
360
|
+
` (${formatSize(truncation.outputBytes)} of ${formatSize(truncation.totalBytes)}).` +
|
|
361
|
+
` Use a more specific query to reduce output size.]`;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
docCache.set(cacheKey, finalText);
|
|
365
|
+
|
|
366
|
+
return {
|
|
367
|
+
content: [{ type: "text", text: finalText }],
|
|
368
|
+
details: {
|
|
369
|
+
libraryId,
|
|
370
|
+
query,
|
|
371
|
+
tokens,
|
|
372
|
+
cached: false,
|
|
373
|
+
truncated: truncation.truncated,
|
|
374
|
+
charCount: finalText.length,
|
|
375
|
+
} as DocsDetails,
|
|
376
|
+
};
|
|
377
|
+
},
|
|
378
|
+
|
|
379
|
+
renderCall(args, theme) {
|
|
380
|
+
let text = theme.fg("toolTitle", theme.bold("get_library_docs "));
|
|
381
|
+
text += theme.fg("accent", args.libraryId);
|
|
382
|
+
if (args.query) text += theme.fg("muted", ` — "${args.query}"`);
|
|
383
|
+
if (args.tokens && args.tokens !== 5000) text += theme.fg("dim", ` (${args.tokens} tokens)`);
|
|
384
|
+
return new Text(text, 0, 0);
|
|
385
|
+
},
|
|
386
|
+
|
|
387
|
+
renderResult(result, { isPartial, expanded }, theme) {
|
|
388
|
+
const d = result.details as DocsDetails | undefined;
|
|
389
|
+
|
|
390
|
+
if (isPartial) return new Text(theme.fg("warning", "Fetching documentation..."), 0, 0);
|
|
391
|
+
if (result.isError || d?.error) {
|
|
392
|
+
return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
let text = theme.fg("success", `${(d?.charCount ?? 0).toLocaleString()} chars`);
|
|
396
|
+
text += theme.fg("dim", ` · ${d?.tokens ?? 5000} token budget`);
|
|
397
|
+
if (d?.cached) text += theme.fg("dim", " · cached");
|
|
398
|
+
if (d?.truncated) text += theme.fg("warning", " · truncated");
|
|
399
|
+
text += theme.fg("dim", ` · ${d?.libraryId}`);
|
|
400
|
+
if (d?.query) text += theme.fg("dim", ` — "${d.query}"`);
|
|
401
|
+
|
|
402
|
+
if (expanded) {
|
|
403
|
+
const content = result.content[0];
|
|
404
|
+
if (content?.type === "text") {
|
|
405
|
+
const preview = content.text.split("\n").slice(0, 12).join("\n");
|
|
406
|
+
text += "\n\n" + theme.fg("dim", preview);
|
|
407
|
+
if (content.text.split("\n").length > 12) {
|
|
408
|
+
text += "\n" + theme.fg("muted", "… (Ctrl+O to collapse)");
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return new Text(text, 0, 0);
|
|
414
|
+
},
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
// ── Startup notification ─────────────────────────────────────────────────
|
|
418
|
+
|
|
419
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
420
|
+
if (!getApiKey()) {
|
|
421
|
+
ctx.ui.notify(
|
|
422
|
+
"Context7: No CONTEXT7_API_KEY set. Using free tier (1000 req/month limit). " +
|
|
423
|
+
"Set CONTEXT7_API_KEY for higher limits.",
|
|
424
|
+
"warning",
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
}
|