@mrclrchtr/supi-web 1.4.0 → 1.6.0
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 +51 -49
- package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-core/src/api.ts +2 -0
- package/node_modules/@mrclrchtr/supi-core/src/index.ts +2 -0
- package/node_modules/@mrclrchtr/supi-core/src/path-utils.ts +40 -0
- package/node_modules/@mrclrchtr/supi-core/src/registry-utils.ts +42 -10
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -20,73 +20,75 @@ After editing the source, run `/reload`.
|
|
|
20
20
|
|
|
21
21
|
After install, pi gets three tools:
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
| Tool | Purpose |
|
|
24
|
+
|------|---------|
|
|
25
|
+
| `web_fetch_md` | Fetch a web page and convert it to clean Markdown for LLM ingestion |
|
|
26
|
+
| `web_docs_search` | Search Context7 for library IDs and metadata before fetching docs |
|
|
27
|
+
| `web_docs_fetch` | Fetch up-to-date documentation for a known Context7 library |
|
|
26
28
|
|
|
27
|
-
##
|
|
29
|
+
## `web_fetch_md`
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
Fetches a public URL and returns clean Markdown.
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
### Parameters
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
| Parameter | Type | Required | Default | Description |
|
|
36
|
+
|-----------|------|----------|---------|-------------|
|
|
37
|
+
| `url` | string | ✓ | — | `http://` or `https://` URL to fetch |
|
|
38
|
+
| `output_mode` | `"auto"` \| `"inline"` \| `"file"` | — | `"auto"` | How to return the result |
|
|
39
|
+
| `abs_links` | boolean | — | `true` | Absolutize relative links and images |
|
|
40
|
+
| `timeout_ms` | number | — | `30000` | Fetch timeout in milliseconds |
|
|
34
41
|
|
|
35
|
-
|
|
36
|
-
- defaults to `output_mode: auto`
|
|
37
|
-
- returns Markdown inline when the result is at most **15,000 characters**
|
|
38
|
-
- otherwise writes the Markdown to a temporary `.md` file and returns the file path
|
|
39
|
-
- absolutizes links and image URLs by default
|
|
40
|
-
- wraps plain-text responses as fenced code blocks instead of pretending they are prose
|
|
42
|
+
### Output modes
|
|
41
43
|
|
|
42
|
-
|
|
44
|
+
- **`auto`** — returns Markdown inline if ≤15,000 characters; otherwise writes to a temporary file and returns the path
|
|
45
|
+
- **`inline`** — always returns Markdown inline
|
|
46
|
+
- **`file`** — always writes to a temporary file and returns the path
|
|
43
47
|
|
|
44
|
-
|
|
45
|
-
2. content sniffing
|
|
46
|
-
3. sibling `.md` / `.markdown` probing
|
|
47
|
-
4. HTML fetch followed by Readability + Turndown conversion
|
|
48
|
+
### Behavior
|
|
48
49
|
|
|
49
|
-
|
|
50
|
+
- Only accepts real `http://` or `https://` URLs
|
|
51
|
+
- Access-controlled pages (login, paywall) should be skipped — ask the user for an allowed source instead
|
|
52
|
+
- Plain-text responses are wrapped in fenced code blocks
|
|
53
|
+
- Links and images are absolutized by default; set `abs_links: false` to keep them relative
|
|
50
54
|
|
|
51
|
-
|
|
55
|
+
## `web_docs_search`
|
|
52
56
|
|
|
53
|
-
|
|
57
|
+
Searches Context7 for library IDs before fetching documentation.
|
|
54
58
|
|
|
55
|
-
|
|
56
|
-
- name
|
|
57
|
-
- description
|
|
58
|
-
- trust score
|
|
59
|
-
- benchmark score
|
|
60
|
-
- snippet count
|
|
61
|
-
- versions
|
|
59
|
+
### Parameters
|
|
62
60
|
|
|
63
|
-
|
|
61
|
+
| Parameter | Type | Required | Description |
|
|
62
|
+
|-----------|------|----------|-------------|
|
|
63
|
+
| `library_name` | string | ✓ | Library name to search for (e.g. `"react"`, `"next.js"`) |
|
|
64
|
+
| `query` | string | ✓ | What you're trying to do — used for relevance ranking |
|
|
64
65
|
|
|
65
|
-
|
|
66
|
+
Results return as a Markdown table with library ID, name, description, trust score, benchmark score, snippet count, and available versions.
|
|
66
67
|
|
|
67
|
-
|
|
68
|
-
- `query` is required
|
|
69
|
-
- `raw: true` returns JSON-serialized snippet objects instead of plain text Markdown
|
|
68
|
+
## `web_docs_fetch`
|
|
70
69
|
|
|
71
|
-
|
|
70
|
+
Retrieves documentation context for a known Context7 library.
|
|
72
71
|
|
|
73
|
-
|
|
72
|
+
### Parameters
|
|
74
73
|
|
|
75
|
-
|
|
74
|
+
| Parameter | Type | Required | Default | Description |
|
|
75
|
+
|-----------|------|----------|---------|-------------|
|
|
76
|
+
| `library_id` | string | ✓ | — | Context7 library ID (e.g. `/facebook/react`, `/vercel/next.js`) |
|
|
77
|
+
| `query` | string | ✓ | — | Specific question about the library |
|
|
78
|
+
| `raw` | boolean | — | `false` | When `true`, returns JSON-serialized snippet objects instead of Markdown |
|
|
76
79
|
|
|
77
|
-
|
|
80
|
+
Default mode returns pre-formatted Markdown. Set `raw: true` when you need structured JSON for programmatic use.
|
|
78
81
|
|
|
79
|
-
|
|
80
|
-
- `@mrclrchtr/supi-web/extension` — extension entrypoint that registers all three tools
|
|
82
|
+
## Typical workflow
|
|
81
83
|
|
|
82
|
-
|
|
84
|
+
1. **`web_docs_search`** — find the right library ID
|
|
85
|
+
2. **Pick a `library_id`** from the results
|
|
86
|
+
3. **`web_docs_fetch`** — retrieve focused, version-aware docs
|
|
83
87
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
- `src/tool/web-docs-search-guidance.ts` — model-facing prompt surfaces for `web_docs_search`
|
|
92
|
-
- `src/tool/web-docs-fetch-guidance.ts` — model-facing prompt surfaces for `web_docs_fetch`
|
|
88
|
+
Skip step 1 if you already know the exact Context7 `library_id`.
|
|
89
|
+
|
|
90
|
+
## Context7 API key
|
|
91
|
+
|
|
92
|
+
`web_docs_search` and `web_docs_fetch` use [Context7](https://context7.com/) through `@upstash/context7-sdk`.
|
|
93
|
+
|
|
94
|
+
If `CONTEXT7_API_KEY` is set in your environment, the SDK uses it automatically for higher rate limits. Without a key, the tools still work with lower defaults.
|
|
@@ -49,6 +49,7 @@ export {
|
|
|
49
49
|
redactDebugData,
|
|
50
50
|
resetDebugRegistry,
|
|
51
51
|
} from "./debug-registry.ts";
|
|
52
|
+
export { fileToUri, resolveToolPath, stripToolPathPrefix, uriToFile } from "./path-utils.ts";
|
|
52
53
|
export type { KnownRootEntry } from "./project-roots.ts";
|
|
53
54
|
export {
|
|
54
55
|
buildKnownRootsMap,
|
|
@@ -63,6 +64,7 @@ export {
|
|
|
63
64
|
sortRootsBySpecificity,
|
|
64
65
|
walkProject,
|
|
65
66
|
} from "./project-roots.ts";
|
|
67
|
+
export { createRegistry, createSessionStateRegistry } from "./registry-utils.ts";
|
|
66
68
|
export { getActiveBranchEntries } from "./session-utils.ts";
|
|
67
69
|
export { registerSettingsCommand } from "./settings/settings-command.ts";
|
|
68
70
|
export type { SettingsScope, SettingsSection } from "./settings/settings-registry.ts";
|
|
@@ -49,6 +49,7 @@ export {
|
|
|
49
49
|
redactDebugData,
|
|
50
50
|
resetDebugRegistry,
|
|
51
51
|
} from "./debug-registry.ts";
|
|
52
|
+
export { fileToUri, resolveToolPath, stripToolPathPrefix, uriToFile } from "./path-utils.ts";
|
|
52
53
|
export type { KnownRootEntry } from "./project-roots.ts";
|
|
53
54
|
export {
|
|
54
55
|
buildKnownRootsMap,
|
|
@@ -63,6 +64,7 @@ export {
|
|
|
63
64
|
sortRootsBySpecificity,
|
|
64
65
|
walkProject,
|
|
65
66
|
} from "./project-roots.ts";
|
|
67
|
+
export { createRegistry, createSessionStateRegistry } from "./registry-utils.ts";
|
|
66
68
|
export { getActiveBranchEntries } from "./session-utils.ts";
|
|
67
69
|
export { registerSettingsCommand } from "./settings/settings-command.ts";
|
|
68
70
|
export type { SettingsScope, SettingsSection } from "./settings/settings-registry.ts";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
|
|
3
|
+
/** Strip pi's optional leading `@` file-path prefix from a tool input. */
|
|
4
|
+
export function stripToolPathPrefix(target: string): string {
|
|
5
|
+
return target.startsWith("@") ? target.slice(1) : target;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Resolve a tool-style file path from a session cwd.
|
|
10
|
+
*
|
|
11
|
+
* Built-in pi file tools accept a leading `@` prefix in path arguments, so
|
|
12
|
+
* shared SuPi path helpers normalize that prefix before resolving relative
|
|
13
|
+
* paths.
|
|
14
|
+
*/
|
|
15
|
+
export function resolveToolPath(cwd: string, target: string): string {
|
|
16
|
+
return path.resolve(cwd, stripToolPathPrefix(target));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Convert a file path to a file:// URI. */
|
|
20
|
+
export function fileToUri(filePath: string): string {
|
|
21
|
+
const resolved = path.resolve(filePath);
|
|
22
|
+
if (process.platform === "win32") {
|
|
23
|
+
return `file:///${resolved.replace(/\\/g, "/")}`;
|
|
24
|
+
}
|
|
25
|
+
return `file://${resolved}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Convert a file:// URI to a file path. */
|
|
29
|
+
export function uriToFile(uri: string): string {
|
|
30
|
+
if (!uri.startsWith("file://")) return uri;
|
|
31
|
+
let filePath = decodeURIComponent(uri.slice(7));
|
|
32
|
+
if (
|
|
33
|
+
process.platform === "win32" &&
|
|
34
|
+
filePath.startsWith("/") &&
|
|
35
|
+
/^[A-Za-z]:/.test(filePath.slice(1))
|
|
36
|
+
) {
|
|
37
|
+
filePath = filePath.slice(1);
|
|
38
|
+
}
|
|
39
|
+
return filePath;
|
|
40
|
+
}
|
|
@@ -5,8 +5,20 @@
|
|
|
5
5
|
// Without this, each symlink path gets its own module copy and its own Map,
|
|
6
6
|
// so registrations from one instance are invisible to consumers in another.
|
|
7
7
|
|
|
8
|
+
import * as path from "node:path";
|
|
9
|
+
|
|
8
10
|
const SYMBOL_PREFIX = "@mrclrchtr/supi-core/";
|
|
9
11
|
|
|
12
|
+
function getGlobalRegistryMap<T>(name: string): Map<string, T> {
|
|
13
|
+
const key = Symbol.for(SYMBOL_PREFIX + name);
|
|
14
|
+
let map = (globalThis as Record<symbol, unknown>)[key] as Map<string, T> | undefined;
|
|
15
|
+
if (!map) {
|
|
16
|
+
map = new Map<string, T>();
|
|
17
|
+
(globalThis as Record<symbol, unknown>)[key] = map;
|
|
18
|
+
}
|
|
19
|
+
return map;
|
|
20
|
+
}
|
|
21
|
+
|
|
10
22
|
/**
|
|
11
23
|
* Create a named registry backed by `globalThis` + `Symbol.for`.
|
|
12
24
|
*
|
|
@@ -18,16 +30,7 @@ const SYMBOL_PREFIX = "@mrclrchtr/supi-core/";
|
|
|
18
30
|
* @returns An object with `register`, `getAll`, and `clear` functions.
|
|
19
31
|
*/
|
|
20
32
|
export function createRegistry<T>(name: string) {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
const getMap = (): Map<string, T> => {
|
|
24
|
-
let map = (globalThis as Record<symbol, unknown>)[key] as Map<string, T> | undefined;
|
|
25
|
-
if (!map) {
|
|
26
|
-
map = new Map<string, T>();
|
|
27
|
-
(globalThis as Record<symbol, unknown>)[key] = map;
|
|
28
|
-
}
|
|
29
|
-
return map;
|
|
30
|
-
};
|
|
33
|
+
const getMap = (): Map<string, T> => getGlobalRegistryMap<T>(name);
|
|
31
34
|
|
|
32
35
|
return {
|
|
33
36
|
/**
|
|
@@ -52,3 +55,32 @@ export function createRegistry<T>(name: string) {
|
|
|
52
55
|
},
|
|
53
56
|
};
|
|
54
57
|
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Create a named session-state registry keyed by normalized cwd.
|
|
61
|
+
*
|
|
62
|
+
* This helper is intended for session-scoped runtime services that should be
|
|
63
|
+
* shared across duplicate jiti module instances while keeping package-specific
|
|
64
|
+
* state unions and convenience wrappers local to the calling package.
|
|
65
|
+
*/
|
|
66
|
+
export function createSessionStateRegistry<TState>(name: string) {
|
|
67
|
+
const getMap = (): Map<string, TState> => getGlobalRegistryMap<TState>(name);
|
|
68
|
+
const normalizeCwd = (cwd: string): string => path.resolve(cwd);
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
/** Get the current state for one session cwd. */
|
|
72
|
+
get: (cwd: string): TState | undefined => {
|
|
73
|
+
return getMap().get(normalizeCwd(cwd));
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
/** Store the current state for one session cwd. */
|
|
77
|
+
set: (cwd: string, state: TState): void => {
|
|
78
|
+
getMap().set(normalizeCwd(cwd), state);
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
/** Clear the current state for one session cwd. */
|
|
82
|
+
clear: (cwd: string): void => {
|
|
83
|
+
getMap().delete(normalizeCwd(cwd));
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrclrchtr/supi-web",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "SuPi Web extension — fetch web pages as clean Markdown (web_fetch_md) and library docs via Context7 (web_docs_search, web_docs_fetch)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"@mozilla/readability": "^0.6.0",
|
|
26
26
|
"turndown": "^7.2.0",
|
|
27
27
|
"turndown-plugin-gfm": "^1.0.2",
|
|
28
|
-
"@mrclrchtr/supi-core": "1.
|
|
28
|
+
"@mrclrchtr/supi-core": "1.6.0"
|
|
29
29
|
},
|
|
30
30
|
"bundledDependencies": [
|
|
31
31
|
"@mrclrchtr/supi-core"
|