| name | extending-pi |
| description | Plan and build Pi for Excel extensions safely: choose skill vs extension plugin vs connection, create flat SKILL.md skills, and handle API keys without asking users to paste secrets in chat. |
Extending Pi (Pi for Excel)
Use this skill when the user asks to extend Pi, add capabilities, or integrate an external API/tool.
Default behavior (non-negotiable)
- Prefer solutions the assistant can set up and run itself.
- Never ask users to paste API keys/tokens/passwords in chat.
- Never create
/set-key ... style commands that take secrets from the prompt input.
- Prefer first-class connection setup UX (
/tools → Connections) for secret entry.
- For API-backed plugin implementation/testing, proactively recommend local bridge setup:
- Preferred: Python bridge (best for API prototyping, payload transforms, quick debugging scripts).
- Fallback: tmux bridge (assistant can still run
curl, Node scripts, and CLI checks).
- If neither is available, still scaffold the extension, but explicitly note reduced verification and offer setup help.
When recommending bridge setup, read the relevant setup skill before giving commands:
python-bridge
tmux-bridge
1) Choose the right extension surface
| Goal | Build | Why |
|---|
| Teach Pi a workflow, process, or decision rubric | Skill | Lowest maintenance; mostly instruction logic |
| Add runtime behavior (slash command, tool, widget, event handling, storage/http) | Extension plugin | Requires executable code |
| Add secure credential/config setup for extension tools | Extension connection | First-class setup/status/preflight/error guidance |
| Save one-off user/workbook preferences | instructions tool | Not an extension |
Heuristic: if instructions + existing tools are enough, build a skill first.
2) Skill authoring (adapted from skill-creator, flat profile)
For this repo, default to flat skills: one skills/<name>/SKILL.md file.
Only add extra folders if clearly needed.
Core principles
- Conciseness: only include domain-specific guidance the model cannot infer reliably.
- Activation quality: trigger matching comes from frontmatter
description; make it precise.
- Context over directives: explain why an approach works, not just rigid commands.
- Progressive disclosure (lightweight): keep SKILL.md focused; avoid dumping long reference text.
Required format and naming
- Required frontmatter:
name, description
- Directory name must equal
name
- Name rules:
- 1-64 chars
- lowercase letters, digits, hyphens
- no leading/trailing/consecutive hyphens
Workflow
- Capture 2-4 concrete user prompts the skill should handle.
- Create
skills/<name>/SKILL.md.
- Write frontmatter:
---
name: my-skill
description: What it does + when to use it.
compatibility: Optional constraints.
---
- Write body with:
- purpose and boundaries
- mapping to relevant tools/integrations
- step-by-step workflow
- guardrails/pitfalls
- Install/update via
skills tool (install/uninstall) when available; otherwise use /extensions → Skills.
- Validate with
skills tool:
action="list"
action="read" for the new skill.
3) Extension plugin workflow
Use when the user needs new executable behavior.
- Build a single-file ES module with
activate(api).
- Register only required surfaces (
registerCommand, registerTool, widget, etc.).
- Keep capabilities least-privilege.
- Install from chat using
extensions_manager (install_code, set_enabled, reload, uninstall).
- Validate by invoking the new command/tool end-to-end.
Minimal skeleton:
export function activate(api) {
api.registerCommand("hello_ext", {
description: "Example command",
handler: () => api.toast("Hello from extension"),
});
return () => {
api.widget.dismiss();
api.overlay.dismiss();
};
}
4) API keys/secrets: secure-by-default pattern
Tier 1 (default): host-injected auth via http.fetch(..., { connection })
For API-backed extension tools:
- Register a connection in
activate(api).
- Add
httpAuth on the connection definition with a strict allowedHosts list.
- Mark tools with
requiresConnection.
- Direct user to
/tools → Connections for secret entry.
- Call
api.http.fetch(url, { connection: "<id>" }).
Connection registration template:
api.connections.register({
id: "acme",
title: "Acme API",
capability: "query Acme records",
authKind: "api_key",
secretFields: [{ id: "apiKey", label: "API key", required: true, maskInUi: true }],
httpAuth: {
placement: "header",
headerName: "Authorization",
valueTemplate: "Bearer {apiKey}",
allowedHosts: ["api.acme.com"],
},
setupHint: "Open /tools → Connections → Extension connections",
});
Tool call template:
const response = await api.http.fetch("https://api.acme.com/v1/search?q=...", {
method: "GET",
connection: "acme",
});
Tier 2 (escape hatch): connections.getSecrets
Only when Tier 1 cannot support the integration (SDK bootstrap, custom request signing, etc.):
const secrets = await api.connections.getSecrets("acme");
Guardrails:
- Requires
connections.secrets.read capability.
- Never echo/log secrets.
- Still keep user secret entry in
/tools → Connections.
Forbidden patterns
/set-key ... slash commands
- "Paste your API key in chat"
- Logging/echoing secrets in tool output/errors
5) Standard reusable “secure connection bundle”
When generating API-backed extension code, use docs/extensions-secure-connection-bundle.md as the default scaffold.
Default scaffold requirements:
- connection definition +
httpAuth
- strict
allowedHosts
- tool
requiresConnection
- host-injected auth path first
- no-chat-secret policy in comments/docs
References
docs/extensions.md
docs/extensions-secure-connection-bundle.md
docs/agent-skills-interop.md
- skill:
skill-creator