From 2054694f43b997b81400564806092f3a16f68234 Mon Sep 17 00:00:00 2001 From: gregorydemay Date: Tue, 17 Mar 2026 15:09:19 +0100 Subject: [PATCH 1/7] =?UTF-8?q?feat:=20add=20canhelp=20skill=20=E2=80=94?= =?UTF-8?q?=20display=20canister=20interface=20summaries=20from=20ID=20or?= =?UTF-8?q?=20name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 (1M context) --- skills/canhelp/SKILL.md | 48 +++++++++++++++++++ skills/canhelp/scripts/fetch-candid.sh | 13 +++++ skills/canhelp/scripts/resolve-canister-id.sh | 27 +++++++++++ 3 files changed, 88 insertions(+) create mode 100644 skills/canhelp/SKILL.md create mode 100755 skills/canhelp/scripts/fetch-candid.sh create mode 100755 skills/canhelp/scripts/resolve-canister-id.sh diff --git a/skills/canhelp/SKILL.md b/skills/canhelp/SKILL.md new file mode 100644 index 0000000..0e158ee --- /dev/null +++ b/skills/canhelp/SKILL.md @@ -0,0 +1,48 @@ +--- +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: +--- + +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: + ``` + ./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 ` ` (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: + ``` + ./scripts/fetch-candid.sh + ``` + 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 ``** + + **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 \ No newline at end of file diff --git a/skills/canhelp/scripts/fetch-candid.sh b/skills/canhelp/scripts/fetch-candid.sh new file mode 100755 index 0000000..d494f49 --- /dev/null +++ b/skills/canhelp/scripts/fetch-candid.sh @@ -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 }" +OUT="/tmp/candid_${CANISTER_ID}.did" + +icp canister metadata "$CANISTER_ID" candid:service --network ic > "$OUT" +echo "$OUT" \ No newline at end of file diff --git a/skills/canhelp/scripts/resolve-canister-id.sh b/skills/canhelp/scripts/resolve-canister-id.sh new file mode 100755 index 0000000..2661f73 --- /dev/null +++ b/skills/canhelp/scripts/resolve-canister-id.sh @@ -0,0 +1,27 @@ +#!/bin/bash +set -euo pipefail + +INPUT="${1:?Usage: resolve-canister-id.sh }" + +# 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" From 8b8ac6e9825e8def56c4f456fcab658d1604b9be Mon Sep 17 00:00:00 2001 From: gregorydemay Date: Tue, 17 Mar 2026 15:25:19 +0100 Subject: [PATCH 2/7] feat: refine --- skills/canhelp/SKILL.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/skills/canhelp/SKILL.md b/skills/canhelp/SKILL.md index 0e158ee..a8a3f72 100644 --- a/skills/canhelp/SKILL.md +++ b/skills/canhelp/SKILL.md @@ -3,6 +3,9 @@ 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: +metadata: + title: Canister Help + category: Infrastructure --- Given a canister ID or name in `$ARGUMENTS`, fetch and summarize its Candid interface. @@ -10,7 +13,7 @@ Given a canister ID or name in `$ARGUMENTS`, fetch and summarize its Candid inte ## 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. @@ -19,7 +22,7 @@ Given a canister ID or name in `$ARGUMENTS`, fetch and summarize its Candid inte - 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 ``` The script outputs the path to the downloaded `.did` file. @@ -45,4 +48,4 @@ Given a canister ID or name in `$ARGUMENTS`, fetch and summarize its Candid inte - 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 \ No newline at end of file +- If the fetch fails, suggest the user verify the canister ID and that `icp` is installed From dc2f06f00964196337b93d55035fac83da10890a Mon Sep 17 00:00:00 2001 From: gregorydemay Date: Thu, 26 Mar 2026 18:28:22 +0100 Subject: [PATCH 3/7] add license and compatibility information to --- skills/canhelp/SKILL.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/skills/canhelp/SKILL.md b/skills/canhelp/SKILL.md index a8a3f72..88eb7ac 100644 --- a/skills/canhelp/SKILL.md +++ b/skills/canhelp/SKILL.md @@ -1,6 +1,8 @@ --- 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. +license: Apache-2.0 +compatibility: "icp-cli >= 0.1.0" allowed-tools: Bash(./scripts/resolve-canister-id.sh *), Bash(./scripts/fetch-candid.sh *), Read, Grep, Glob argument-hint: metadata: From 529da13da1db0ad5923d0dc2af854d62788a67ff Mon Sep 17 00:00:00 2001 From: gregorydemay Date: Thu, 26 Mar 2026 18:30:17 +0100 Subject: [PATCH 4/7] improve description --- skills/canhelp/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skills/canhelp/SKILL.md b/skills/canhelp/SKILL.md index 88eb7ac..a8355bc 100644 --- a/skills/canhelp/SKILL.md +++ b/skills/canhelp/SKILL.md @@ -1,6 +1,6 @@ --- 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. +description: Display a human-readable summary of a canister's interface given its mainnet canister ID or a human-readable name. Like --help but for canisters. Only for mainnet canisters — for local canisters, read the generated .did file in your project directly. license: Apache-2.0 compatibility: "icp-cli >= 0.1.0" allowed-tools: Bash(./scripts/resolve-canister-id.sh *), Bash(./scripts/fetch-candid.sh *), Read, Grep, Glob From aada64c0d2faf6053bd7a19a08b5b1e9fbc36daf Mon Sep 17 00:00:00 2001 From: gregorydemay Date: Thu, 26 Mar 2026 18:31:08 +0100 Subject: [PATCH 5/7] add new line at end of file. --- skills/canhelp/scripts/fetch-candid.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skills/canhelp/scripts/fetch-candid.sh b/skills/canhelp/scripts/fetch-candid.sh index d494f49..435b26d 100755 --- a/skills/canhelp/scripts/fetch-candid.sh +++ b/skills/canhelp/scripts/fetch-candid.sh @@ -10,4 +10,4 @@ CANISTER_ID="${1:?Usage: fetch-candid.sh }" OUT="/tmp/candid_${CANISTER_ID}.did" icp canister metadata "$CANISTER_ID" candid:service --network ic > "$OUT" -echo "$OUT" \ No newline at end of file +echo "$OUT" From e622baaa390e892bd9cfe575b5f9e25a42a2c93a Mon Sep 17 00:00:00 2001 From: gregorydemay Date: Thu, 26 Mar 2026 18:35:26 +0100 Subject: [PATCH 6/7] improve skill --- skills/canhelp/SKILL.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/skills/canhelp/SKILL.md b/skills/canhelp/SKILL.md index a8355bc..c655ee8 100644 --- a/skills/canhelp/SKILL.md +++ b/skills/canhelp/SKILL.md @@ -16,11 +16,11 @@ Given a canister ID or name in `$ARGUMENTS`, fetch and summarize its Candid inte 1. Resolve the canister ID by running the resolve script from the skill's base directory: ```bash - ./scripts/resolve-canister-id.sh $ARGUMENTS + ./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 ` ` (one per line). - - If there is a single result, use it directly. + - If there is a single result, clearly display the resolved canister ID and 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: @@ -50,4 +50,5 @@ Given a canister ID or name in `$ARGUMENTS`, fetch and summarize its Candid inte - 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 succeeds, but the Candid interface is empty,explain that the canister is not exposing its Candid interface in the wasm metadata - If the fetch fails, suggest the user verify the canister ID and that `icp` is installed From e7d2b5d598ca313ea2599bef8743f623977b7725 Mon Sep 17 00:00:00 2001 From: gregorydemay Date: Thu, 26 Mar 2026 18:50:50 +0100 Subject: [PATCH 7/7] add evaluation file --- evaluations/canhelp.json | 116 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 evaluations/canhelp.json diff --git a/evaluations/canhelp.json b/evaluations/canhelp.json new file mode 100644 index 0000000..613c535 --- /dev/null +++ b/evaluations/canhelp.json @@ -0,0 +1,116 @@ +{ + "skill": "canhelp", + "description": "Evaluation cases for the canhelp skill. Tests whether agents correctly resolve canister names/IDs, fetch Candid interfaces, and present structured summaries grouped by query vs update methods.", + + "output_evals": [ + { + "name": "Lookup by canister ID", + "prompt": "What can canister ryjl3-tyaaa-aaaaa-aaaba-cai do?", + "expected_behaviors": [ + "Runs resolve-canister-id.sh with the provided principal", + "Runs fetch-candid.sh with the resolved canister ID", + "Reads the downloaded .did file", + "Groups methods into Query and Update sections", + "Sorts methods alphabetically within each group", + "Lists key custom types (records, variants) defined in the interface" + ] + }, + { + "name": "Lookup by human-readable name", + "prompt": "Show me the interface for the NNS governance canister", + "expected_behaviors": [ + "Runs resolve-canister-id.sh with the name (not a hardcoded canister ID)", + "Displays the resolved canister ID before proceeding", + "Runs fetch-candid.sh with the resolved ID", + "Presents a structured summary with Query and Update method groups" + ] + }, + { + "name": "Ambiguous name with multiple results", + "prompt": "What does the SNS canister do?", + "expected_behaviors": [ + "Runs resolve-canister-id.sh with the name", + "When multiple results are returned, presents the list to the user", + "Asks the user to pick one before continuing", + "Does NOT arbitrarily pick one and proceed without asking" + ] + }, + { + "name": "Large interface summarization", + "prompt": "Show me what the NNS governance canister can do", + "expected_behaviors": [ + "Resolves 'NNS governance' to a canister ID", + "Fetches and reads the Candid interface", + "Groups methods by query vs update", + "Sorts methods alphabetically within each group", + "Shows method signatures with argument types and return types", + "Lists key types like Neuron, ProposalInfo, ManageNeuronRequest" + ] + }, + { + "name": "Output format structure", + "prompt": "Tell me about canister rrkah-fqaaa-aaaaa-aaaaq-cai", + "expected_behaviors": [ + "Starts the summary with 'Canister '", + "Has a Query methods section", + "Has an Update methods section", + "Has a Types section listing custom record/variant types", + "Method signatures include argument names/types and return types", + "Includes brief one-line descriptions where inferable from method names" + ] + }, + { + "name": "Empty Candid interface", + "prompt": "What methods does canister uf6dk-hyaaa-aaaaq-qaaaq-cai expose?", + "expected_behaviors": [ + "Runs fetch-candid.sh with the canister ID", + "If the Candid interface is empty, explains that the canister is not exposing its Candid interface in the wasm metadata", + "Does NOT fabricate or hallucinate methods" + ] + }, + { + "name": "Adversarial: local canister", + "prompt": "Show me the interface for my local canister bkyz2-fmaaa-aaaaa-qaaaq-cai", + "expected_behaviors": [ + "Attempts to resolve and fetch the canister", + "If the fetch fails, suggests verifying the canister ID and that icp is installed", + "Does NOT hallucinate a Candid interface" + ] + }, + { + "name": "Adversarial: invalid input", + "prompt": "Show me the canister interface for notavalidid", + "expected_behaviors": [ + "Runs resolve-canister-id.sh with the input", + "If no results are found, clearly communicates the failure", + "Does NOT fabricate a canister ID or interface" + ] + } + ], + + "trigger_evals": { + "description": "Queries to test whether the skill activates correctly. 'should_trigger' queries should cause the skill to load; 'should_not_trigger' queries should NOT activate this skill.", + "should_trigger": [ + "What can canister ryjl3-tyaaa-aaaaa-aaaba-cai do?", + "Show me the interface for the NNS ledger", + "What methods does the ICP ledger canister expose?", + "canhelp ckBTC minter", + "Describe the API of canister rrkah-fqaaa-aaaaa-aaaaq-cai", + "What's the Candid interface for the cycles minting canister?", + "How do I call the Internet Identity canister? What methods are available?", + "List the methods on the SNS governance canister" + ], + "should_not_trigger": [ + "Deploy my canister to mainnet", + "How do I write a Candid interface file?", + "Set up a new ICP project with Rust", + "How does inter-canister communication work?", + "Explain how the NNS governance works", + "Write a Motoko function that transfers ICP", + "How do I upgrade my canister without losing state?", + "What is the Internet Computer?", + "Add ICRC-1 support to my token canister", + "How do I test my canister locally?" + ] + } +} \ No newline at end of file