Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions skills/canhelp/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
name: canhelp
description: Display a human-readable summary of a canister's interface given its mainnet canister ID or name. Like --help but for canisters.
allowed-tools: Bash(./scripts/resolve-canister-id.sh *), Bash(./scripts/fetch-candid.sh *), Read, Grep, Glob
argument-hint: <canister-id-or-name>
metadata:
title: Canister Help
category: Infrastructure
---

Given a canister ID or name in `$ARGUMENTS`, fetch and summarize its Candid interface.

## Steps

1. Resolve the canister ID by running the resolve script from the skill's base directory:
```bash
./scripts/resolve-canister-id.sh $ARGUMENTS
```
If `$ARGUMENTS` is already a valid principal, the script echoes it back.
Otherwise, it queries the IC Dashboard API and outputs matches as `<canister-id> <name>` (one per line).
- If there is a single result, use it directly.
- If there are multiple results, present the list to the user and ask them to pick one before continuing.

2. Fetch the Candid interface using the resolved canister ID:
```bash
./scripts/fetch-candid.sh <resolved-canister-id>
```
The script outputs the path to the downloaded `.did` file.

3. Read the file using the `Read` tool.

4. Present the output as a readable summary with the following structure:

**Canister `<canister-id>`**

**Query methods:**
- `method_name(arg1: type1, arg2: type2) → return_type` — one-line description if inferable from the name

**Update methods:**
- `method_name(arg1: type1) → return_type`

**Types:**
- List any custom record/variant types defined in the interface, with their fields

## Guidelines

- Group methods by query vs update
- Sort methods alphabetically within each group
- For complex nested types, show the top-level structure and note nesting
- If the candid is very large (>100 methods), show a summary count and list only the most important-looking methods, offering to show the full list on request
- If the fetch fails, suggest the user verify the canister ID and that `icp` is installed
13 changes: 13 additions & 0 deletions skills/canhelp/scripts/fetch-candid.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash
set -euo pipefail

if ! command -v icp &>/dev/null; then
echo "Error: 'icp' CLI not found. Install it from https://dfinity.github.io/icp-cli" >&2
exit 1
fi

CANISTER_ID="${1:?Usage: fetch-candid.sh <canister-id>}"
OUT="/tmp/candid_${CANISTER_ID}.did"

icp canister metadata "$CANISTER_ID" candid:service --network ic > "$OUT"
echo "$OUT"
27 changes: 27 additions & 0 deletions skills/canhelp/scripts/resolve-canister-id.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/bin/bash
set -euo pipefail

INPUT="${1:?Usage: resolve-canister-id.sh <canister-id-or-name>}"

# Principal: Base32(CRC32 · blob) grouped into 5-char chunks separated by dashes.
# Each group is exactly 5 lowercase alphanumeric chars, except the last which is 1-5.
# Max 63 chars (29-byte blob → 53 base32 chars + 10 dashes). Must have at least 2 groups.
if [[ "$INPUT" =~ ^[a-z2-7]{5}(-[a-z2-7]{5})*(-[a-z2-7]{1,5})$ ]]; then
echo "$INPUT"
exit 0
fi

# Otherwise, query IC Dashboard API for name-based lookup
QUERY=$(python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1]))" "$INPUT")
RESPONSE=$(curl -sf "https://ic-api.internetcomputer.org/api/v4/canisters?format=json&has_name=true&query=${QUERY}&limit=50")

python3 -c "
import sys, json
data = json.load(sys.stdin)
entries = data.get('data', [])
if not entries:
print('Error: no canister found matching \"$INPUT\"', file=sys.stderr)
sys.exit(1)
for e in entries:
print(f\"{e['canister_id']} {e.get('name', 'N/A')}\")
" <<< "$RESPONSE"
Loading