aiblueprint-cli 1.4.104 → 1.4.106

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.
Files changed (30) hide show
  1. package/README.md +0 -7
  2. package/package.json +1 -1
  3. package/agents-config/skills/audit/SKILL.md +0 -126
  4. package/agents-config/skills/audit/agents/openai.yaml +0 -10
  5. package/agents-config/skills/audit/assets/codex-icon.svg +0 -20
  6. package/agents-config/skills/commit/SKILL.md +0 -44
  7. package/agents-config/skills/commit/agents/openai.yaml +0 -10
  8. package/agents-config/skills/commit/assets/codex-icon.svg +0 -17
  9. package/agents-config/skills/create-pr/SKILL.md +0 -55
  10. package/agents-config/skills/create-pr/agents/openai.yaml +0 -10
  11. package/agents-config/skills/create-pr/assets/codex-icon.svg +0 -17
  12. package/agents-config/skills/oneshot/SKILL.md +0 -44
  13. package/agents-config/skills/oneshot/agents/openai.yaml +0 -10
  14. package/agents-config/skills/oneshot/assets/codex-icon.svg +0 -18
  15. package/agents-config/skills/tools/SKILL.md +0 -149
  16. package/agents-config/skills/use-artifacts/SKILL.md +0 -211
  17. package/agents-config/skills/use-artifacts/agents/openai.yaml +0 -7
  18. package/agents-config/skills/use-artifacts/assets/codex-icon.svg +0 -18
  19. package/agents-config/skills/use-artifacts/assets/local-runtime.js +0 -299
  20. package/agents-config/skills/use-artifacts/scripts/create_artifact.py +0 -317
  21. package/agents-config/skills/use-delegate/SKILL.md +0 -97
  22. package/agents-config/skills/use-delegate/agents/openai.yaml +0 -10
  23. package/agents-config/skills/use-delegate/assets/codex-icon.svg +0 -20
  24. package/agents-config/skills/use-delegate/references/models.md +0 -32
  25. package/agents-config/skills/use-goal/SKILL.md +0 -121
  26. package/agents-config/skills/use-goal/agents/openai.yaml +0 -7
  27. package/agents-config/skills/use-goal/assets/codex-icon.svg +0 -18
  28. package/agents-config/skills/use-goal/references/claude-code-goal.md +0 -65
  29. package/agents-config/skills/use-goal/references/codex-goal.md +0 -70
  30. package/agents-config/skills/use-goal/references/verification-harnesses.md +0 -108
@@ -1,211 +0,0 @@
1
- ---
2
- name: use-artifacts
3
- description: Create Claude-style local HTML artifacts under ~/.agents/artifacts. Use for plans, prototypes, visualizations, dashboards, diagrams, options, or substantial reusable content, with a portable local runtime and no Anthropic API.
4
- ---
5
-
6
- # Use Artifacts
7
-
8
- ## Overview
9
-
10
- Use this skill to simulate Claude Artifacts in agents that do not have a native artifact panel. The artifact is a small global workspace, usually a single self-contained HTML file, created at `~/.agents/artifacts/<id>/` so the user can open, inspect, and iterate on it from any repo.
11
-
12
- Always create artifacts in the global user directory: `~/.agents/artifacts/<id>/`. Never create artifacts inside a repo-local `.agents/artifacts` directory, even when the current working directory is a product repo.
13
-
14
- The HTML is the deliverable. It should turn the agent's public reasoning, plan, findings, examples, and tradeoffs into a polished page the user can scan, not just dump markdown into a file.
15
-
16
- Research basis: Claude artifacts are useful for substantial, self-contained content that the user may edit, reuse, view, or reference later. Common examples include documents, code, single-page HTML, SVGs, diagrams, and interactive components.
17
-
18
- ## Artifact Criteria
19
-
20
- Create an artifact when the work is:
21
-
22
- - substantial enough that inline chat would be hard to inspect or reuse
23
- - standalone without needing hidden conversation context
24
- - visual, interactive, document-like, or useful as a reusable reference
25
- - a feature plan, security review, product brief, implementation plan, or architecture explanation that benefits from visual structure
26
- - likely to need later iteration
27
-
28
- Do not create an artifact for a tiny answer, a short code snippet, or a change that belongs directly in an existing product codebase unless the user asks for a separate prototype.
29
-
30
- ## Artifact Modes
31
-
32
- Default to a thinking/showcase document when the request is about planning, explaining, reviewing, designing, or deciding. This is the Claude-style pattern in which an HTML page presents the answer as a readable artifact:
33
-
34
- - eyebrow with project/context
35
- - strong title and lede
36
- - high-signal finding or recommendation callout near the top
37
- - sections for model, tradeoffs, flows, edge cases, rollout, or implementation phases
38
- - code snippets, tables, pills, timelines, diagrams, or cards where they clarify the reasoning
39
- - final decisions, open questions, and validation notes
40
-
41
- For any plan artifact (`plan`, `feature-plan`, `implementation-plan`, product plan, launch plan, page plan, or strategy plan), always include both:
42
-
43
- - a draft of the page/content itself: proposed title, lede, sections, key copy, calls to action, states, or narrative blocks
44
- - croquis of the page: small visual sketches showing layout, hierarchy, content placement, and option differences
45
-
46
- The draft answers "what will this say/do?" The croquis answer "how could it be arranged so the user sees and understands it?"
47
-
48
- Use an interactive artifact when the user asks for a mini app, calculator, simulation, editor, dashboard, visualization, game, or prototype with controls.
49
-
50
- Use a variations/options artifact when the user asks for variations, options, directions, alternatives, explorations, or "show me a few versions". In this mode, do not build a real UI or final screen. Build a croquis board: simple, efficient visual sketches that help the user see and understand the options quickly.
51
-
52
- Variation croquis rules:
53
-
54
- - show 3-6 options on one page as a single vertical sequence: one direction per row, never a multi-column grid
55
- - give every direction the full available content width so its interface remains legible without opening it
56
- - start directly with the directions; do not add a masthead, hero, long lede, capability recap, or recommendation callout above them unless the user explicitly asks for that context
57
- - keep the page chrome minimal: a compact title or view switcher is enough, and omit it when the content is already self-explanatory
58
- - make each option visibly different in layout, hierarchy, rhythm, or concept
59
- - use wireframe-like boxes, simple labels, rough placeholders, arrows, swatches, and short notes
60
- - keep fidelity low-to-mid: enough to compare ideas, not enough to imply implementation is done
61
- - annotate the tradeoff under each croquis in one or two short lines
62
- - recommendations are optional, evidence-based, and shown only after all directions; never lead with generic "Best fit", ranking, or promotional copy
63
-
64
- Do not expose private chain-of-thought. Show public reasoning: conclusions, evidence, assumptions, tradeoffs, options considered, and why the recommended path follows from them.
65
-
66
- ## Design Fundamentals
67
-
68
- Approach every artifact as the design lead at a small studio known for versatility: each one gets a visual identity pitched at the treatment the task actually calls for, with deliberate palette, typography, and layout choices specific to the subject. Never ship a templated design. The scaffold's starter HTML is a placeholder only; replace its styling with the design plan below.
69
-
70
- ### Calibrate the treatment
71
-
72
- - Calibrate treatment, not whether to design. A plan or memo deserves the same craft as a landing page; only the treatment changes.
73
- - Utilitarian requests (plans, memos, reviews, dashboards, demos): polished but restrained. Real typographic hierarchy, considered spacing, a proper palette. No flashy gigantic hero; keep flourishes tasteful and limited.
74
- - Editorial requests (landing pages, games, apps or tools the user will keep or share): make opinionated calls and take one real aesthetic risk where it serves the work. Spend the boldness in one place and keep everything around it quiet.
75
- - When unsure: a well-composed page is never the wrong answer; an over-designed identity sometimes is.
76
-
77
- ### Design plan before code
78
-
79
- Sketch a compact token plan before writing any HTML, then derive every color and type decision from it:
80
-
81
- - Color: the palette as 4-6 named hex values.
82
- - Type: typefaces for 2+ roles (a characterful display face used with restraint, a complementary body face, a utility face for captions or data if needed).
83
- - Layout: the layout concept in one or two sentences.
84
-
85
- For editorial artifacts, review the plan against the subject before building: if any part reads like the generic default you would produce for any similar page, revise that part and record what changed and why in `HIGHLOGIC.md`.
86
-
87
- ### Fundamentals for every artifact
88
-
89
- - Honor what is already there. Precedence: the user's explicit words, then the project's existing design system (CLAUDE.md, tokens, theme files, components), then your own choices.
90
- - Ground it in the subject: one concrete subject, its audience, the page's single job. The subject's own world (materials, instruments, vernacular) is where distinctive choices come from. Build with real content, never lorem.
91
- - Typography carries the page. Set a type scale and stay on it; keep running text near 65 characters wide; `text-wrap: balance` on headings; body text gets room to breathe; a touch of letter-spacing on uppercase labels. Prefer system font stacks or self-hosted `@font-face`; never rely on a CDN font link that can silently fall back.
92
- - Choose neutrals, don't default to them: a pure mid-grey reads as unconsidered; a grey with a slight hue bias toward the accent reads as chosen. Pure white and near-black are fine grounds when picked, not inherited.
93
- - Let layout do the spacing: flex/grid with `gap`, not per-element margins that silently collapse or double. Wide content (tables, code, diagrams) scrolls inside its own `overflow-x: auto` container; the page body never scrolls sideways. Use `font-variant-numeric: tabular-nums` wherever digits line up.
94
- - Avoid the AI-generated look when nothing is specified: warm cream (#F4F1EA) with serif display and terracotta accent; near-black with a lone acid-green or vermilion pop; broadsheet hairline rules with dense columns; purple-to-blue gradient hero on white; Inter or Space Grotesk as the "safe" face; emoji as section markers; everything centered; `rounded-lg` everywhere; accent bars/rails on rounded cards. If the user explicitly asks for one of these looks, follow it exactly.
95
- - Build cleanly: watch overlapping elements, cascade collisions, and selector specificity fights (a `.section` rule cancelling a `.cta` rule over padding). Close every non-void element, double-quote attributes, give keyboard focus a visible state, respect `prefers-reduced-motion`. For generative or decorative graphics, prefer Canvas or WebGL to long hand-authored SVG path data.
96
- - Copy is design material: name things by what people recognize, not how the system is built; active voice; a control says exactly what happens ("Publish", then "Published"); errors explain what went wrong and how to fix it, no apologies, no vagueness.
97
- - Structure is information: numbering, eyebrows, dividers, and labels must encode something true about the content (a real sequence, a real hierarchy), never decoration. Question numbered markers (01/02/03) before using them.
98
- - When it's a UI, not a document: it is scanned and operated, not read top-to-bottom. Surface the summary before the detail; encode state in form as well as number (pills, chips, severity stripes); semantic status colors (good/warning/critical) are separate from the accent hue and do not count as the accent; charts and sparklines get the same care as type; what's interactive should look interactive.
99
- - Motion is deliberate: one orchestrated moment (page-load sequence, scroll reveal, hover micro-interaction) lands harder than scattered effects, and extra animation often reads as AI-generated. Sometimes less is more.
100
-
101
- ### Theme rules
102
-
103
- - **Inside an app, the artifact theme MUST match the app's theme.** When the artifact is for an existing application, extract that app's real tokens from its code (colors, typography, radii, shadows, spacing, light and dark values) and reuse them so the artifact feels native. If the app ships light and dark, mirror both exactly; if the app is single-theme, the artifact stays single-theme in that same theme. Never invent a parallel palette next to an existing one.
104
- - Otherwise, design both themes at token level: define the palette as custom properties on `:root`; redefine only the tokens under `@media (prefers-color-scheme: dark)`; redefine them again under `:root[data-theme="dark"]` and `:root[data-theme="light"]` so an explicit toggle beats the OS preference in both directions. Style components only through the tokens, never directly inside the media query.
105
- - Give the second theme the same care as the first: don't naively invert; keep contrast legible and the accent working on both grounds.
106
- - A design that deliberately commits to one visual world (a neon arcade screen, a letterpress invitation) may stay single-theme, recorded as a choice in `HIGHLOGIC.md`, never as an omission.
107
-
108
- ## Style Source
109
-
110
- Determine the visual direction in this order:
111
-
112
- 1. The user's explicit direction: follow it exactly, even when it differs from
113
- the application.
114
- 2. The existing application's actual visual language: when the artifact is for
115
- an existing app, inspect the relevant UI code, styles, design tokens,
116
- components, typography, spacing, colors, radii, shadows, and interaction
117
- patterns, then make the artifact feel native to the product. The theme must
118
- match the app's theme (see Theme rules above). Treat the application's
119
- current implementation as the source of truth; do not impose an unrelated
120
- look merely because the user gave no visual direction. Record the style in
121
- `HIGHLOGIC.md` and in `manifest.json` with a clear value such as
122
- `project:<app-name>`.
123
- 3. Otherwise, build a subject-specific identity per Design Fundamentals: design
124
- plan first, a chosen palette, a deliberate type pairing, restrained
125
- decoration, both themes at token level, and only the structure needed to
126
- communicate the artifact.
127
-
128
- State briefly whether the artifact uses the application style, a requested
129
- style, or a subject-specific identity.
130
-
131
- ## Runtime Capabilities (local, no Anthropic API)
132
-
133
- Published claude.ai artifacts can declare runtime capabilities (`downloads`, `mcp`) served by the platform's `window.claude` runtime. Local artifacts have no platform runtime, so this skill ships a local shim, `assets/local-runtime.js`, that provides the same call shapes with plain browser APIs only. Never call the Anthropic API, the claude.ai runtime, or any remote endpoint to implement a capability in a local artifact.
134
-
135
- Usage:
136
-
137
- 1. Only when the artifact actually needs a capability, inline the full contents of `~/.agents/skills/use-artifacts/assets/local-runtime.js` in a `<script>` tag at the top of `<body>`, before any feature code. The HTML stays self-contained; do not reference the file with `src`.
138
- 2. Write feature code against the standard surface: `window.claude.downloads.save(...)`, `window.claude.mcp.callTool/watchTool/listTools/invalidate`. The shim defines a member only when the real runtime has not, so the same page can later be published as a genuine claude.ai artifact without changing feature code.
139
- 3. Record the capabilities used in `manifest.json` (`"capabilities": ["downloads", "mcp"]`) and in `HIGHLOGIC.md`.
140
-
141
- Capabilities:
142
-
143
- - **downloads**: `window.claude.downloads.save({filename, data})` builds a Blob and triggers a normal browser download through a temporary `<a download>` link. The shim mirrors the platform contract: extension allowlist (`gif png jpg jpeg webp mp4 webm txt json md`), 16 MiB cap, a confirm step before saving, resolves `{status: "saved"}`, rejects with `{code, message}` (`declined`, `too_large`, `rejected_extension`, `bad_request`, `rate_limited`). Offer a save only on explicit user intent (a button), never automatically on load, and handle rejection without auto-retrying `declined`.
144
- - **mcp-style data**: there are no viewer connectors locally. Register local data sources with `window.claudeLocal.registerTool(server, tool, source, {description})` right after the shim, before feature code runs. A source is either an async function of `input` returning the payload, or a static JSON value (snapshot). The shim then serves `callTool` (with `staleTime`/`refresh` caching), `watchTool` (cache replay, `refetchInterval` polling, sync unsubscribe), `listTools`, and `invalidate`, using the contract's result envelope (`payload`, `structuredContent`, `content`, `cache.storedAt`) and error codes (`server_not_connected`, `tool_error`, `bad_request`).
145
-
146
- Capability rules:
147
-
148
- - Snapshots are static data: record the snapshot date and "static data" in `HIGHLOGIC.md`, and drive any "last updated" indicator in the UI from `result.cache.storedAt`, never from `Date.now()` at render time.
149
- - Branch degraded UX on the error `code`, never on message text. Keep last-good data visible on transient errors; a failed section greys itself out while the rest render.
150
- - Real data observed during the session may inform the payload shape, but never embed private values the user did not ask to include.
151
-
152
- ## Creation Workflow
153
-
154
- 1. Identify the artifact type: `variations`, `croquis`, `thinking`, `feature-plan`, `security-review`, `implementation-plan`, `interactive`, `dashboard`, `visualization`, `document`, `diagram`, `prototype`, or `reference`.
155
- 2. Determine the style source in this order: the user's explicit direction,
156
- the existing application's actual style and theme, then a subject-specific
157
- identity per Design Fundamentals.
158
- 3. Sketch the design plan (color, type, layout) per Design Fundamentals and
159
- record it in `HIGHLOGIC.md`.
160
- 4. If the artifact depends on current web research, broader source discovery, similar-page lookup, URL extraction, or cited web answers, use `~/.agents/skills/exa-search/SKILL.md`.
161
- 5. Scaffold the workspace:
162
-
163
- ```bash
164
- python3 ~/.agents/skills/use-artifacts/scripts/create_artifact.py "<short title>" --style "<requested, project:app-name, or subject-specific>" --kind thinking
165
- ```
166
-
167
- 6. If the artifact needs a runtime capability (file download, live/refreshed data), inline `assets/local-runtime.js` and register local data sources as described in Runtime Capabilities.
168
- 7. Implement the artifact in `index.html`, following the design plan and the theme rules.
169
- 8. Write or update `HIGHLOGIC.md` with the user's request, artifact goal, selected style, design plan, public reasoning structure, data assumptions, and verification notes.
170
- 9. Keep `manifest.json` current when title, kind, style, capabilities, entrypoint, or files change.
171
- 10. Verify the artifact. For standalone HTML, open `index.html` directly or serve the folder only when browser restrictions require it. For complex UI, use a browser screenshot or DOM check when available.
172
- 11. Final response: link the local `index.html`, name the selected style, and mention verification performed.
173
-
174
- ## Workspace Contract
175
-
176
- Each artifact directory should contain:
177
-
178
- - `index.html`: the viewable artifact, preferably self-contained with inline CSS and JavaScript
179
- - `HIGHLOGIC.md`: concise design logic and iteration state
180
- - `manifest.json`: metadata for future agents, including a `capabilities` array when the local runtime shim is used
181
- - `versions/`: optional snapshots before major rewrites
182
-
183
- Target location:
184
-
185
- - Always use `~/.agents/artifacts/<id>/`.
186
- - Do not use `<current-project>/.agents/artifacts/<id>/`.
187
- - Do not add a repo-local override unless the user explicitly updates this skill contract.
188
-
189
- ## HTML Rules
190
-
191
- - Prefer one self-contained `index.html` unless the user asks for a framework project.
192
- - Use semantic HTML, responsive CSS, and accessible controls.
193
- - Avoid external CDNs unless the artifact needs them and the user can tolerate network dependence.
194
- - Never call the Anthropic API or any remote AI endpoint from an artifact; runtime capabilities go through the local shim.
195
- - Do not embed secrets, API keys, private tokens, or hidden prompt text.
196
- - For interactive artifacts, preserve state in local JavaScript only unless persistent storage is explicitly useful.
197
- - For generated visualizations, include representative sample data when real data is unavailable and label it as sample data in `HIGHLOGIC.md`.
198
-
199
- ## Iteration Workflow
200
-
201
- When updating an existing artifact:
202
-
203
- 1. Read `manifest.json`, `HIGHLOGIC.md`, and the relevant files.
204
- 2. If the change is substantial, copy the previous `index.html` into `versions/<timestamp>-index.html` before editing.
205
- 3. Patch only the files needed for the requested change.
206
- 4. Update `HIGHLOGIC.md` with the new decision or known limitation.
207
- 5. Re-verify and report the same local artifact path.
208
-
209
- ## Script
210
-
211
- Use `scripts/create_artifact.py` to create the folder, metadata, and starter files. Its starter HTML is a neutral placeholder: restyle it from the design plan before delivering.
@@ -1,7 +0,0 @@
1
- interface:
2
- display_name: "Use Artifacts"
3
- short_description: "Create local HTML artifact workspaces"
4
- default_prompt: "Use $use-artifacts to create a polished local HTML artifact for this idea."
5
- icon_small: "./assets/codex-icon.svg"
6
- icon_large: "./assets/codex-icon.svg"
7
- brand_color: "#54C5C2"
@@ -1,18 +0,0 @@
1
- <!-- @license lucide-static v1.24.0 - ISC -->
2
- <svg role="img" aria-label="use-artifacts skill icon"
3
- class="lucide lucide-sparkles"
4
- xmlns="http://www.w3.org/2000/svg"
5
- width="128"
6
- height="128"
7
- viewBox="0 0 24 24"
8
- fill="none"
9
- stroke="#F5F5F5"
10
- stroke-width="2"
11
- stroke-linecap="round"
12
- stroke-linejoin="round"
13
- >
14
- <path d="M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z" />
15
- <path d="M20 2v4" />
16
- <path d="M22 4h-4" />
17
- <circle cx="4" cy="20" r="2" />
18
- </svg>
@@ -1,299 +0,0 @@
1
- /*
2
- * local-runtime.js: local artifact capabilities shim. No Anthropic API,
3
- * no claude.ai runtime, no network calls. Everything runs on plain
4
- * browser APIs (Blob, <a download>, timers, in-memory cache).
5
- *
6
- * It mirrors the claude.ai artifact runtime surface (contract 0.1.14):
7
- * - window.claude.downloads.save({filename, data})
8
- * - window.claude.mcp.callTool / watchTool / listTools / invalidate
9
- *
10
- * If a real claude.ai runtime already installed a member, the shim
11
- * leaves it untouched, so the same feature code is portable to a
12
- * genuinely published artifact without changes.
13
- *
14
- * Local data sources replace viewer connectors. Register them BEFORE
15
- * feature code runs:
16
- *
17
- * window.claudeLocal.registerTool("Analytics", "get_stats",
18
- * async (input) => ({ visits: 1234 })); // live local source
19
- * window.claudeLocal.registerTool("Analytics", "snapshot",
20
- * { visits: 1234 }, { description: "Static snapshot 2026-07-22" });
21
- *
22
- * Then use the standard surface:
23
- * const r = await window.claude.mcp.callTool("Analytics", "get_stats", { range: "7d" });
24
- * render(r.payload);
25
- */
26
- (function () {
27
- "use strict";
28
- if (typeof window === "undefined") return;
29
-
30
- var root = window.claude;
31
- if (!root || typeof root !== "object") {
32
- root = {};
33
- try { window.claude = root; } catch (e) { return; }
34
- }
35
-
36
- /* ------------------------------ downloads ------------------------------ */
37
-
38
- var EXT_ALLOWLIST = ["gif", "png", "jpg", "jpeg", "webp", "mp4", "webm", "txt", "json", "md"];
39
- var MIME_BY_EXT = {
40
- gif: "image/gif", png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg",
41
- webp: "image/webp", mp4: "video/mp4", webm: "video/webm",
42
- txt: "text/plain", json: "application/json", md: "text/markdown"
43
- };
44
- var MAX_BYTES = 16 * 1024 * 1024;
45
- var promptOpen = false;
46
-
47
- function downloadsError(code, message) {
48
- return { code: code, message: message };
49
- }
50
-
51
- function toBlob(data, mime) {
52
- if (typeof data === "string" && data.length) return new Blob([data], { type: mime });
53
- if (data instanceof Blob && data.size) return new Blob([data], { type: mime });
54
- if (data instanceof ArrayBuffer && data.byteLength) return new Blob([data], { type: mime });
55
- if (ArrayBuffer.isView(data) && data.byteLength) return new Blob([data], { type: mime });
56
- return null;
57
- }
58
-
59
- if (!root.downloads) {
60
- root.downloads = {
61
- save: function (request) {
62
- return new Promise(function (resolve, reject) {
63
- if (promptOpen) {
64
- return reject(downloadsError("rate_limited", "A save prompt is already open."));
65
- }
66
- if (!request || typeof request !== "object") {
67
- return reject(downloadsError("bad_request", "save() takes {filename, data}."));
68
- }
69
- var filename = request.filename;
70
- if (typeof filename !== "string" || !filename || filename.length > 512) {
71
- return reject(downloadsError("bad_request", "filename must be a non-empty string of at most 512 chars."));
72
- }
73
- var clean = filename.replace(/[\/\\:*?"<>|]+/g, "-").trim();
74
- var dot = clean.lastIndexOf(".");
75
- var ext = (dot > 0 ? clean.slice(dot + 1) : "").toLowerCase();
76
- if (EXT_ALLOWLIST.indexOf(ext) === -1) {
77
- return reject(downloadsError("rejected_extension",
78
- "Extension \"." + ext + "\" is outside the allowlist: " + EXT_ALLOWLIST.join(" ")));
79
- }
80
- var blob = toBlob(request.data, MIME_BY_EXT[ext]);
81
- if (!blob) {
82
- return reject(downloadsError("bad_request",
83
- "data must be a non-empty string, Blob, ArrayBuffer, or ArrayBufferView."));
84
- }
85
- if (blob.size > MAX_BYTES) {
86
- return reject(downloadsError("too_large", "File is over 16 MiB (" + blob.size + " bytes)."));
87
- }
88
- promptOpen = true;
89
- var accepted;
90
- try {
91
- accepted = window.confirm('Save "' + clean + '" (' + blob.size + " bytes)?");
92
- } finally {
93
- promptOpen = false;
94
- }
95
- if (!accepted) {
96
- return reject(downloadsError("declined", "The viewer declined the save."));
97
- }
98
- var url = URL.createObjectURL(blob);
99
- var a = document.createElement("a");
100
- a.href = url;
101
- a.download = clean;
102
- document.body.appendChild(a);
103
- a.click();
104
- a.remove();
105
- setTimeout(function () { URL.revokeObjectURL(url); }, 10000);
106
- resolve({ status: "saved" });
107
- });
108
- }
109
- };
110
- }
111
-
112
- /* --------------------------------- mcp --------------------------------- */
113
-
114
- var SEP = "\u0000";
115
- var registry = {}; // "server\0tool" -> {source, isStatic, description}
116
- var cache = {}; // identity -> {result, storedAt}
117
- var watchers = {}; // identity -> [{handler, timer, refresh}]
118
-
119
- function key(server, tool) { return server + SEP + tool; }
120
-
121
- function canonical(value) {
122
- if (value === undefined || value === null) return "null";
123
- if (Array.isArray(value)) return "[" + value.map(canonical).join(",") + "]";
124
- if (typeof value === "object") {
125
- return "{" + Object.keys(value).sort().map(function (k) {
126
- return JSON.stringify(k) + ":" + canonical(value[k]);
127
- }).join(",") + "}";
128
- }
129
- return JSON.stringify(value);
130
- }
131
-
132
- function identity(server, tool, input) {
133
- return key(server, tool) + SEP + canonical(input);
134
- }
135
-
136
- function mcpError(code, server, message, extra) {
137
- var err = { code: code, message: message };
138
- if (server !== undefined) err.server = server;
139
- if (extra) Object.keys(extra).forEach(function (k) { err[k] = extra[k]; });
140
- return err;
141
- }
142
-
143
- function envelope(payload) {
144
- var text = typeof payload === "string" ? payload : JSON.stringify(payload);
145
- var result = { content: [{ type: "text", text: text }], payload: payload };
146
- if (payload !== null && typeof payload === "object") result.structuredContent = payload;
147
- return result;
148
- }
149
-
150
- function cachedCopy(entry, revalidating) {
151
- var copy = {};
152
- Object.keys(entry.result).forEach(function (k) { copy[k] = entry.result[k]; });
153
- copy.cache = { storedAt: entry.storedAt, revalidating: !!revalidating };
154
- return copy;
155
- }
156
-
157
- function notifyData(id, result) {
158
- (watchers[id] || []).forEach(function (w) {
159
- try { w.handler({ type: "data", result: result }); } catch (e) { /* page bug */ }
160
- });
161
- }
162
-
163
- function execute(server, tool, input) {
164
- var entry = registry[key(server, tool)];
165
- if (!entry) {
166
- return Promise.reject(mcpError("server_not_connected", server,
167
- 'No local source registered for "' + server + '" / "' + tool +
168
- '". Call window.claudeLocal.registerTool(...) before feature code runs.'));
169
- }
170
- try { JSON.stringify(input === undefined ? null : input); } catch (e) {
171
- return Promise.reject(mcpError("bad_request", server, "input must be plain JSON."));
172
- }
173
- return Promise.resolve()
174
- .then(function () {
175
- return entry.isStatic ? entry.source : entry.source(input === undefined ? null : input);
176
- })
177
- .then(function (payload) {
178
- var result = envelope(payload);
179
- cache[identity(server, tool, input)] = { result: result, storedAt: Date.now() };
180
- return result;
181
- })
182
- .catch(function (e) {
183
- if (e && e.code) throw e;
184
- throw mcpError("tool_error", server, (e && e.message) || String(e), { result: e });
185
- });
186
- }
187
-
188
- if (!root.mcp) {
189
- root.mcp = {
190
- callTool: function (server, tool, input, options) {
191
- if (typeof server !== "string" || typeof tool !== "string") {
192
- return Promise.reject(mcpError("bad_request", undefined, "server and tool must be strings."));
193
- }
194
- var opts = options || {};
195
- var id = identity(server, tool, input);
196
- var entry = cache[id];
197
- var staleTime = 0;
198
- var refresh = false;
199
- if (opts.cache && typeof opts.cache === "object") {
200
- staleTime = Math.min(opts.cache.staleTime || 0, 300000);
201
- refresh = !!opts.cache.refresh;
202
- }
203
- if (opts.cache === false) entry = null;
204
- if (refresh) entry = null;
205
- if (entry && Date.now() - entry.storedAt < staleTime) {
206
- return Promise.resolve(cachedCopy(entry, false));
207
- }
208
- return execute(server, tool, input).then(function (result) {
209
- notifyData(id, result);
210
- return result;
211
- });
212
- },
213
-
214
- watchTool: function (server, tool, input, handler, options) {
215
- if (typeof handler !== "function") {
216
- throw new TypeError("watchTool handler must be a function.");
217
- }
218
- var opts = options || {};
219
- var id = identity(server, tool, input);
220
- var w = { handler: handler, timer: null, refresh: null };
221
- (watchers[id] = watchers[id] || []).push(w);
222
-
223
- w.refresh = function () {
224
- execute(server, tool, input)
225
- .then(function (result) { notifyData(id, result); })
226
- .catch(function (err) {
227
- try { w.handler({ type: "error", error: err }); } catch (e) { /* page bug */ }
228
- });
229
- };
230
-
231
- Promise.resolve().then(function () {
232
- var entry = cache[id];
233
- if (entry) {
234
- try { w.handler({ type: "data", result: cachedCopy(entry, false) }); } catch (e) { /* page bug */ }
235
- } else {
236
- w.refresh();
237
- }
238
- });
239
-
240
- if (opts.refetchInterval) {
241
- w.timer = setInterval(w.refresh, Math.max(5000, opts.refetchInterval));
242
- }
243
-
244
- var done = false;
245
- return function unsubscribe() {
246
- if (done) return;
247
- done = true;
248
- if (w.timer) clearInterval(w.timer);
249
- watchers[id] = (watchers[id] || []).filter(function (x) { return x !== w; });
250
- };
251
- },
252
-
253
- invalidate: function (server, tool, input) {
254
- Object.keys(cache).forEach(function (id) {
255
- var match = true;
256
- if (server !== undefined) match = id.indexOf(server + SEP) === 0;
257
- if (match && tool !== undefined) match = id.indexOf(key(server, tool) + SEP) === 0;
258
- if (match && input !== undefined) match = id === identity(server, tool, input);
259
- if (match) {
260
- delete cache[id];
261
- var subs = watchers[id] || [];
262
- if (subs.length) subs[0].refresh();
263
- }
264
- });
265
- return Promise.resolve();
266
- },
267
-
268
- listTools: function () {
269
- var byServer = {};
270
- Object.keys(registry).forEach(function (k) {
271
- var parts = k.split(SEP);
272
- (byServer[parts[0]] = byServer[parts[0]] || []).push({
273
- name: parts[1],
274
- description: registry[k].description || ""
275
- });
276
- });
277
- return Promise.resolve({
278
- servers: Object.keys(byServer).map(function (s) {
279
- return { server: s, authStatus: "connected", tools: byServer[s] };
280
- })
281
- });
282
- }
283
- };
284
- }
285
-
286
- /* --------------------------- local registration ------------------------ */
287
-
288
- window.claudeLocal = window.claudeLocal || {};
289
- window.claudeLocal.registerTool = function (server, tool, source, opts) {
290
- if (typeof server !== "string" || typeof tool !== "string") {
291
- throw new TypeError("registerTool(server, tool, source): server and tool must be strings.");
292
- }
293
- registry[key(server, tool)] = {
294
- source: source,
295
- isStatic: typeof source !== "function",
296
- description: (opts && opts.description) || ""
297
- };
298
- };
299
- })();