From 3dee67aea0bdc9127901a71dd7cb8f09ac9dd78e Mon Sep 17 00:00:00 2001 From: Ivan Pusic <450140+ivpusic@users.noreply.github.com> Date: Fri, 6 Mar 2026 10:08:48 +0100 Subject: [PATCH 1/2] Add search by contract functionality --- cli/root.go | 9 +-- cmd/dataset/dataset.go | 6 +- cmd/dataset/search.go | 41 ++++++----- cmd/dataset/search_by_contract.go | 116 ++++++++++++++++++++++++++++++ cmd/docs/search.go | 13 ++-- cmd/execution/results.go | 22 +++--- cmd/query/create.go | 25 +++---- cmd/query/query.go | 5 +- cmd/query/run.go | 29 ++++---- cmd/query/run_sql.go | 28 ++++---- cmd/query/update.go | 25 +++---- cmd/usage/usage.go | 14 ++-- go.mod | 2 +- go.sum | 4 +- 14 files changed, 244 insertions(+), 95 deletions(-) create mode 100644 cmd/dataset/search_by_contract.go diff --git a/cli/root.go b/cli/root.go index ff0dc95..73be107 100644 --- a/cli/root.go +++ b/cli/root.go @@ -24,12 +24,9 @@ var apiKeyFlag string var rootCmd = &cobra.Command{ Use: "dune", - Short: "Dune CLI — query, explore, and manage blockchain data on Dune Analytics", - Long: "A command-line interface for the Dune Analytics platform.\n\n" + - "Discover datasets across the Dune catalog, execute SQL queries (DuneSQL dialect),\n" + - "retrieve execution results, and manage your saved queries — all from the terminal.\n\n" + - "Authenticate with an API key via --api-key, the DUNE_API_KEY environment variable,\n" + - "or by running `dune auth`.", + Short: "Dune CLI — interact with the Dune Analytics API", + Long: "A command-line interface for interacting with the Dune Analytics API.\n" + + "Manage queries, execute DuneSQL queries, search datasets, browse documentation, and monitor usage.", PersistentPreRunE: func(cmd *cobra.Command, _ []string) error { if cmd.Annotations["skipAuth"] == "true" { return nil diff --git a/cmd/dataset/dataset.go b/cmd/dataset/dataset.go index d4aee38..ecf5449 100644 --- a/cmd/dataset/dataset.go +++ b/cmd/dataset/dataset.go @@ -6,8 +6,12 @@ import "github.com/spf13/cobra" func NewDatasetCmd() *cobra.Command { cmd := &cobra.Command{ Use: "dataset", - Short: "Discover and explore datasets across the Dune catalog", + Short: "Search and discover Dune datasets", + Long: "Search the Dune dataset catalog to discover tables and their schemas.\n\n" + + "Use 'dataset search' for keyword-based discovery across all tables, or\n" + + "'dataset search-by-contract' to find decoded tables for a specific contract address.", } cmd.AddCommand(newSearchCmd()) + cmd.AddCommand(newSearchByContractCmd()) return cmd } diff --git a/cmd/dataset/search.go b/cmd/dataset/search.go index 4cd61c5..8c6f0f6 100644 --- a/cmd/dataset/search.go +++ b/cmd/dataset/search.go @@ -13,26 +13,31 @@ import ( func newSearchCmd() *cobra.Command { cmd := &cobra.Command{ Use: "search", - Short: "Search for tables and datasets across the Dune catalog", - Long: "Natural-language table discovery across the Dune catalog. Use this command\n" + - "to find concrete table names for use in SQL queries.\n\n" + - "Filter by category (canonical for chain primitives, decoded for ABI-level\n" + - "events/calls, spell for curated datasets, community for user-contributed),\n" + - "by blockchain, schema, dataset type, or ownership scope.", - RunE: runSearch, + Short: "Search for datasets across the Dune catalog", + Long: "Search for datasets across the Dune catalog by keyword, category, blockchain, and more.\n\n" + + "The catalog includes canonical blockchain data, decoded contract tables,\n" + + "Spellbook transformations, and community datasets. Use --include-schema\n" + + "to get column names and types for SQL generation.\n\n" + + "Examples:\n" + + " dune dataset search --query \"uniswap swaps\"\n" + + " dune dataset search --query \"transfers\" --categories decoded --blockchains ethereum\n" + + " dune dataset search --query \"dex trades\" --categories spell --include-schema --output json\n" + + " dune dataset search --owner-scope me", + RunE: runSearch, } - cmd.Flags().String("query", "", "natural-language search intent or entity hints (e.g. 'uniswap v3 swaps'); use '*' to browse without keyword bias") - cmd.Flags().StringArray("categories", nil, "filter by table family: canonical (chain primitives), decoded (ABI-level events/calls), spell (curated datasets), community (user-contributed)") - cmd.Flags().StringArray("blockchains", nil, "chain scope to reduce ambiguity and improve ranking (e.g. ethereum, solana)") - cmd.Flags().StringArray("dataset-types", nil, "fine-grained dataset type filter: dune_table, decoded_table, spell, uploaded_table, transformation_table, transformation_view") - cmd.Flags().StringArray("schemas", nil, "schema/namespace constraint for high precision (e.g. dex, uniswap_v3_ethereum)") - cmd.Flags().String("owner-scope", "", "ownership filter: all, me, or team; does NOT automatically include private datasets") - cmd.Flags().Bool("include-private", false, "widen results to include private datasets visible to the authenticated user/team alongside public ones") - cmd.Flags().Bool("include-schema", false, "include column-level schema (name, type, nullable) for every result; useful when preparing SQL") - cmd.Flags().Bool("include-metadata", false, "include category-specific metadata (page_rank_score, description, abi_type, contract_name, project_name, etc.)") - cmd.Flags().Int32("limit", 20, "number of results per page; use 5-15 for quick checks, 20-50 for deeper exploration") - cmd.Flags().Int32("offset", 0, "pagination offset; use previous response pagination info for next page") + cmd.Flags().String("query", "", "search query text") + cmd.Flags().StringArray("categories", nil, "filter by category (canonical, decoded, spell, community)") + cmd.Flags().StringArray("blockchains", nil, "filter by blockchain") + cmd.Flags().StringArray("dataset-types", nil, + "filter by dataset type (dune_table, decoded_table, spell, uploaded_table, transformation_table, transformation_view)") + cmd.Flags().StringArray("schemas", nil, "filter by schema") + cmd.Flags().String("owner-scope", "", "ownership filter (all, me, team)") + cmd.Flags().Bool("include-private", false, "include private datasets") + cmd.Flags().Bool("include-schema", false, "include column schema in results") + cmd.Flags().Bool("include-metadata", false, "include metadata in results") + cmd.Flags().Int32("limit", 20, "maximum number of results") + cmd.Flags().Int32("offset", 0, "pagination offset") output.AddFormatFlag(cmd, "text") return cmd diff --git a/cmd/dataset/search_by_contract.go b/cmd/dataset/search_by_contract.go new file mode 100644 index 0000000..e30ffb4 --- /dev/null +++ b/cmd/dataset/search_by_contract.go @@ -0,0 +1,116 @@ +package dataset + +import ( + "fmt" + "strings" + + "github.com/duneanalytics/cli/cmdutil" + "github.com/duneanalytics/cli/output" + "github.com/duneanalytics/duneapi-client-go/models" + "github.com/spf13/cobra" +) + +func newSearchByContractCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "search-by-contract", + Short: "Search for decoded tables associated with a specific contract address", + Long: `Search for decoded tables (events and calls) associated with a specific contract address. + +This command finds all available decoded tables for a given smart contract address. +Decoded tables contain blockchain data that has been parsed according to a contract's ABI, +providing a structured view of contract interactions. + +Table types returned: + - Event tables: contain parsed event logs emitted by smart contracts + - Call tables: contain parsed function calls made to smart contracts + +When to use this command: + - You have a specific contract address (EVM or Tron) and want to find its decoded tables + - You want to analyze on-chain activity for a particular smart contract + - You need to find event or call tables for a known contract + +For broader table discovery by keyword or project name, use 'dune dataset search' instead. + +Examples: + dune dataset search-by-contract --contract-address 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984 + dune dataset search-by-contract --contract-address 0x1f98... --blockchains ethereum --blockchains arbitrum + dune dataset search-by-contract --contract-address 0x1f98... --include-schema + dune dataset search-by-contract --contract-address 0x1f98... --limit 50 --offset 20`, + RunE: runSearchByContract, + } + + cmd.Flags().String("contract-address", "", + "The contract address to search for. Accepts EVM addresses (starting with 0x) or Tron addresses. "+ + "Example: '0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984'.") + _ = cmd.MarkFlagRequired("contract-address") + cmd.Flags().StringArray("blockchains", nil, + "Filter results to specific blockchains (e.g. ethereum, arbitrum). "+ + "Can be specified multiple times. If not provided, searches across all blockchains where the contract exists.") + cmd.Flags().Bool("include-schema", false, + "If set, include the column-level schema (name, type, nullable) for every result. "+ + "Enable when preparing SQL generation.") + cmd.Flags().Int32("limit", 20, + "Maximum number of results to return. "+ + "Use smaller values (5-10) for quick checks, larger values (50-100) for comprehensive discovery.") + cmd.Flags().Int32("offset", 0, + "Number of results to skip for pagination. Use for paginating through large result sets.") + output.AddFormatFlag(cmd, "text") + + return cmd +} + +func runSearchByContract(cmd *cobra.Command, _ []string) error { + client := cmdutil.ClientFromCmd(cmd) + + contractAddress, _ := cmd.Flags().GetString("contract-address") + + req := models.SearchDatasetsByContractAddressRequest{ + ContractAddress: contractAddress, + } + + if cmd.Flags().Changed("blockchains") { + v, _ := cmd.Flags().GetStringArray("blockchains") + req.Blockchains = v + } + if cmd.Flags().Changed("include-schema") { + v, _ := cmd.Flags().GetBool("include-schema") + req.IncludeSchema = &v + } + if cmd.Flags().Changed("limit") { + v, _ := cmd.Flags().GetInt32("limit") + req.Limit = &v + } + if cmd.Flags().Changed("offset") { + v, _ := cmd.Flags().GetInt32("offset") + req.Offset = &v + } + + resp, err := client.SearchDatasetsByContractAddress(req) + if err != nil { + return err + } + + w := cmd.OutOrStdout() + switch output.FormatFromCmd(cmd) { + case output.FormatJSON: + return output.PrintJSON(w, resp) + default: + columns := []string{"FULL_NAME", "CATEGORY", "DATASET_TYPE", "BLOCKCHAINS"} + rows := make([][]string, len(resp.Results)) + for i, r := range resp.Results { + dt := "" + if r.DatasetType != nil { + dt = *r.DatasetType + } + rows[i] = []string{ + r.FullName, + r.Category, + dt, + strings.Join(r.Blockchains, ", "), + } + } + output.PrintTable(w, columns, rows) + fmt.Fprintf(w, "\n%d of %d results\n", len(resp.Results), resp.Total) + return nil + } +} diff --git a/cmd/docs/search.go b/cmd/docs/search.go index 4067f7b..d81b627 100644 --- a/cmd/docs/search.go +++ b/cmd/docs/search.go @@ -13,10 +13,15 @@ const defaultMCPEndpoint = "https://docs.dune.com/mcp" func newSearchCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "search", - Short: "Search the Dune documentation for guides, API references, and code examples", - Long: "Search across all Dune documentation pages including guides, API references,\n" + - "DuneSQL syntax, and code examples. Does not require authentication.", + Use: "search", + Short: "Search the Dune documentation", + Long: "Search the official Dune documentation. No authentication required.\n\n" + + "Look up DuneSQL syntax, API references, table naming conventions,\n" + + "and Dune platform concepts.\n\n" + + "Examples:\n" + + " dune docs search --query \"DuneSQL string functions\"\n" + + " dune docs search --query \"execute query API\" --api-reference-only\n" + + " dune docs search --query \"decoded tables\" --code-only", Annotations: map[string]string{"skipAuth": "true"}, RunE: runSearch, } diff --git a/cmd/execution/results.go b/cmd/execution/results.go index 95b4747..26a1235 100644 --- a/cmd/execution/results.go +++ b/cmd/execution/results.go @@ -17,15 +17,19 @@ var PollInterval = 2 * time.Second func newResultsCmd() *cobra.Command { cmd := &cobra.Command{ Use: "results ", - Short: "Get execution results for a query execution by execution ID", - Long: "Retrieve the results of a query execution. By default, waits for the execution\n" + - "to complete (up to the timeout) before returning results.\n\n" + - "Behavior:\n" + - " 1. Checks the current execution status\n" + - " 2. If still running: polls until complete or timeout is reached\n" + - " 3. If completed: returns the result data\n" + - " 4. If failed/cancelled: returns the error details\n\n" + - "Use --no-wait to return the current state immediately without polling.", + Short: "Fetch results of a query execution", + Long: "Fetch results of a query execution by its execution ID.\n\n" + + "Use this after submitting a query with --no-wait, or to re-fetch results from\n" + + "a completed execution. The command handles all execution states:\n" + + " - Completed: displays result rows\n" + + " - Pending/Executing: shows current state (re-run later)\n" + + " - Failed: returns the error message\n" + + " - Cancelled: returns cancellation notice\n\n" + + "Use --limit and --offset to paginate through large result sets.\n\n" + + "Examples:\n" + + " dune execution results 01JXYZ...\n" + + " dune execution results 01JXYZ... --limit 100 --offset 200\n" + + " dune execution results 01JXYZ... --output json", Args: cobra.ExactArgs(1), RunE: runResults, } diff --git a/cmd/query/create.go b/cmd/query/create.go index 31a5ae4..c3cf062 100644 --- a/cmd/query/create.go +++ b/cmd/query/create.go @@ -12,20 +12,21 @@ import ( func newCreateCmd() *cobra.Command { cmd := &cobra.Command{ Use: "create", - Short: "Create a new Dune query and return the query ID", - Long: "Create a new SQL query on Dune. Returns the query ID on success.\n\n" + - "The query is written in DuneSQL dialect. If the query targets tables with\n" + - "known partition columns, include a WHERE filter on those columns\n" + - "(e.g. WHERE block_date >= CURRENT_DATE - INTERVAL '7' DAY) to enable\n" + - "partition pruning and reduce query cost.", - RunE: runCreate, + Short: "Create a new saved query", + Long: "Create a new saved DuneSQL query. Returns the numeric query ID on success.\n" + + "Use --temp to create a temporary query that won't appear in your saved queries list.\n\n" + + "Examples:\n" + + " dune query create --name \"Recent Blocks\" --sql \"SELECT * FROM ethereum.blocks LIMIT 10\"\n" + + " dune query create --name \"My Query\" --sql \"SELECT 1\" --private --description \"Test query\"\n" + + " dune query create --name \"Throwaway\" --sql \"SELECT 1\" --temp", + RunE: runCreate, } - cmd.Flags().String("name", "", "human-readable query title, max 600 characters (required)") - cmd.Flags().String("sql", "", "the SQL query text in DuneSQL dialect, max 500,000 characters (required)") - cmd.Flags().String("description", "", "short description of what the query does, max 1,000 characters") - cmd.Flags().Bool("private", false, "make the query private; may be forced by team privacy settings") - cmd.Flags().Bool("temp", false, "create a temporary query that won't appear in the dune.com library or be accessible when shared") + cmd.Flags().String("name", "", "query name (required)") + cmd.Flags().String("sql", "", "DuneSQL query text (required)") + cmd.Flags().String("description", "", "query description") + cmd.Flags().Bool("private", false, "make the query private") + cmd.Flags().Bool("temp", false, "create a temporary query (auto-deleted, not shown in your saved queries)") _ = cmd.MarkFlagRequired("name") _ = cmd.MarkFlagRequired("sql") output.AddFormatFlag(cmd, "text") diff --git a/cmd/query/query.go b/cmd/query/query.go index bbdfe19..5e628f3 100644 --- a/cmd/query/query.go +++ b/cmd/query/query.go @@ -6,7 +6,10 @@ import "github.com/spf13/cobra" func NewQueryCmd() *cobra.Command { cmd := &cobra.Command{ Use: "query", - Short: "Create, retrieve, update, execute, and archive Dune queries", + Short: "Manage Dune queries", + Long: "Create, retrieve, update, archive, and execute DuneSQL queries.\n\n" + + "Use 'query create' to save a reusable query, 'query run' to execute a saved query,\n" + + "or 'query run-sql' to execute raw DuneSQL without saving.", } cmd.AddCommand(newCreateCmd()) cmd.AddCommand(newGetCmd()) diff --git a/cmd/query/run.go b/cmd/query/run.go index 19be0e7..036e6f6 100644 --- a/cmd/query/run.go +++ b/cmd/query/run.go @@ -13,20 +13,24 @@ import ( func newRunCmd() *cobra.Command { cmd := &cobra.Command{ Use: "run ", - Short: "Execute a saved Dune query by its ID and display results", - Long: "Execute a saved Dune query by its numeric ID. By default, waits for the\n" + - "execution to complete and displays the result rows. Use --no-wait to submit\n" + - "the execution and exit immediately with just the execution ID.\n\n" + - "Credits are consumed based on actual compute resources used. Use --performance\n" + - "to select the engine size (medium or large).", - Args: cobra.ExactArgs(1), - RunE: runRun, + Short: "Execute a saved query and display results", + Long: "Execute a saved DuneSQL query by its numeric ID and display results.\n\n" + + "By default, polls every 5 seconds for up to ~5 minutes waiting for completion.\n" + + "Use --no-wait to submit the execution and exit immediately; then fetch\n" + + "results later with 'dune execution results '.\n\n" + + "Examples:\n" + + " dune query run 12345\n" + + " dune query run 12345 --param wallet=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --param days=30\n" + + " dune query run 12345 --performance large --limit 100\n" + + " dune query run 12345 --no-wait", + Args: cobra.ExactArgs(1), + RunE: runRun, } - cmd.Flags().StringArray("param", nil, "typed query parameter in key=value format (repeatable); numbers are stringified, datetimes use YYYY-MM-DD HH:mm:ss") - cmd.Flags().String("performance", "medium", `engine size for the execution: "medium" (default) or "large"; credits are consumed based on actual compute resources used`) - cmd.Flags().Int("limit", 0, "maximum number of result rows to return (0 = all)") - cmd.Flags().Bool("no-wait", false, "submit the execution and exit immediately, printing only the execution ID and state") + cmd.Flags().StringArray("param", nil, "query parameter in key=value format (repeatable)") + cmd.Flags().String("performance", "medium", `performance tier: "medium" (default) or "large" for higher compute resources`) + cmd.Flags().Int("limit", 0, "maximum number of rows to display (0 = all)") + cmd.Flags().Bool("no-wait", false, "submit execution and exit without waiting for results") cmd.Flags().Int("timeout", 300, "maximum seconds to wait for the execution to complete before timing out") output.AddFormatFlag(cmd, "text") @@ -106,4 +110,3 @@ func parseParams(raw []string) (map[string]any, error) { } return params, nil } - diff --git a/cmd/query/run_sql.go b/cmd/query/run_sql.go index ed18752..f1c74c0 100644 --- a/cmd/query/run_sql.go +++ b/cmd/query/run_sql.go @@ -10,21 +10,25 @@ import ( func newRunSQLCmd() *cobra.Command { cmd := &cobra.Command{ Use: "run-sql", - Short: "Execute a raw DuneSQL query and display results", - Long: "Execute an inline SQL statement in DuneSQL dialect without saving it as a\n" + - "query on Dune. By default, waits for completion and displays result rows.\n\n" + - "Use --no-wait to submit the execution and exit immediately with just the\n" + - "execution ID. Credits are consumed based on actual compute resources used.", - Args: cobra.NoArgs, - RunE: runRunSQL, + Short: "Execute raw DuneSQL and display results", + Long: "Execute a DuneSQL query directly without creating a saved query.\n" + + "Ideal for ad-hoc exploration and one-off analysis.\n\n" + + "By default, polls every 5 seconds for up to ~5 minutes waiting for completion.\n" + + "Use --no-wait to submit and exit immediately.\n\n" + + "Examples:\n" + + " dune query run-sql --sql \"SELECT block_number, block_time FROM ethereum.blocks ORDER BY block_number DESC LIMIT 5\"\n" + + " dune query run-sql --sql \"SELECT * FROM ethereum.transactions WHERE block_number = {{block_num}}\" --param block_num=20000000\n" + + " dune query run-sql --sql \"SELECT COUNT(*) FROM ethereum.transactions\" --performance large", + Args: cobra.NoArgs, + RunE: runRunSQL, } - cmd.Flags().String("sql", "", "the SQL query text in DuneSQL dialect (required)") + cmd.Flags().String("sql", "", "DuneSQL query to execute (required)") _ = cmd.MarkFlagRequired("sql") - cmd.Flags().StringArray("param", nil, "typed query parameter in key=value format (repeatable); numbers are stringified, datetimes use YYYY-MM-DD HH:mm:ss") - cmd.Flags().String("performance", "medium", `engine size for the execution: "medium" (default) or "large"; credits are consumed based on actual compute resources used`) - cmd.Flags().Int("limit", 0, "maximum number of result rows to return (0 = all)") - cmd.Flags().Bool("no-wait", false, "submit the execution and exit immediately, printing only the execution ID and state") + cmd.Flags().StringArray("param", nil, "query parameter in key=value format (repeatable)") + cmd.Flags().String("performance", "medium", `performance tier: "medium" (default) or "large" for higher compute resources`) + cmd.Flags().Int("limit", 0, "maximum number of rows to display (0 = all)") + cmd.Flags().Bool("no-wait", false, "submit execution and exit without waiting for results") cmd.Flags().Int("timeout", 300, "maximum seconds to wait for the execution to complete before timing out") output.AddFormatFlag(cmd, "text") diff --git a/cmd/query/update.go b/cmd/query/update.go index 3257fca..147803f 100644 --- a/cmd/query/update.go +++ b/cmd/query/update.go @@ -12,20 +12,21 @@ import ( func newUpdateCmd() *cobra.Command { cmd := &cobra.Command{ Use: "update ", - Short: "Update an existing Dune query's SQL, title, description, privacy, or tags", - Long: "Modify a query you own or have edit access to via team membership.\n" + - "Only supply the flags you want to change; unchanged fields are preserved.\n\n" + - "The update uses optimistic locking — if someone else edited the query\n" + - "concurrently, you'll get a conflict error.", - Args: cobra.ExactArgs(1), - RunE: runUpdate, + Short: "Update an existing saved query", + Long: "Update an existing saved query. At least one flag must be provided.\n" + + "Only the specified fields are modified; omitted fields remain unchanged.\n\n" + + "Examples:\n" + + " dune query update 12345 --sql \"SELECT * FROM ethereum.blocks LIMIT 20\"\n" + + " dune query update 12345 --name \"Renamed Query\" --tags defi,ethereum", + Args: cobra.ExactArgs(1), + RunE: runUpdate, } - cmd.Flags().String("name", "", "new title for the query, max 600 characters") - cmd.Flags().String("sql", "", "new SQL content in DuneSQL dialect, max 500,000 characters") - cmd.Flags().String("description", "", "new description for the query, max 1,000 characters") - cmd.Flags().Bool("private", false, "set to true to make the query private, false to make public") - cmd.Flags().StringSlice("tags", nil, "new set of tags for the query (comma-separated); replaces all existing tags") + cmd.Flags().String("name", "", "query name") + cmd.Flags().String("sql", "", "DuneSQL query text") + cmd.Flags().String("description", "", "query description") + cmd.Flags().Bool("private", false, "make the query private") + cmd.Flags().StringSlice("tags", nil, "query tags (comma-separated)") output.AddFormatFlag(cmd, "text") return cmd diff --git a/cmd/usage/usage.go b/cmd/usage/usage.go index b061237..8998241 100644 --- a/cmd/usage/usage.go +++ b/cmd/usage/usage.go @@ -14,10 +14,16 @@ import ( func NewUsageCmd() *cobra.Command { cmd := &cobra.Command{ Use: "usage", - Short: "Get credit and resource usage for the authenticated Dune account", - Long: "Show current-period credit usage, storage consumption, and billing period\n" + - "boundaries for the authenticated user or team. Optionally filter by date range.", - RunE: runUsage, + Short: "Show credit and resource usage for your Dune account", + Long: "Show credit and resource usage for your Dune account.\n\n" + + "Displays private queries/dashboards count, storage usage, and credit\n" + + "consumption per billing period. Use --start-date and --end-date to\n" + + "filter to a specific date range.\n\n" + + "Examples:\n" + + " dune usage\n" + + " dune usage --start-date 2025-01-01 --end-date 2025-06-01\n" + + " dune usage --output json", + RunE: runUsage, } cmd.Flags().String("start-date", "", "filter usage from this date (YYYY-MM-DD format)") diff --git a/go.mod b/go.mod index 2e964ba..2a2b842 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.25.6 require ( github.com/charmbracelet/fang v0.4.4 - github.com/duneanalytics/duneapi-client-go v0.4.2 + github.com/duneanalytics/duneapi-client-go v0.4.3 github.com/modelcontextprotocol/go-sdk v1.4.0 github.com/spf13/cobra v1.10.2 github.com/stretchr/testify v1.11.1 diff --git a/go.sum b/go.sum index ce1003f..9817820 100644 --- a/go.sum +++ b/go.sum @@ -29,8 +29,8 @@ github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsV github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/duneanalytics/duneapi-client-go v0.4.2 h1:1kdSSpmcfh4+pOvY3RI0eHrtotALBE/douE+VzgGNsg= -github.com/duneanalytics/duneapi-client-go v0.4.2/go.mod h1:7pXXufWvR/Mh2KOehdyBaunJXmHI+pzjUmyQTQhJjdE= +github.com/duneanalytics/duneapi-client-go v0.4.3 h1:3meDMQPwGk6jrVntN4o2nm4CWuzobKnD4nmG52hWZ+0= +github.com/duneanalytics/duneapi-client-go v0.4.3/go.mod h1:7pXXufWvR/Mh2KOehdyBaunJXmHI+pzjUmyQTQhJjdE= github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= From 716bba135d420e3ad09231435c302f1e9e1d1f44 Mon Sep 17 00:00:00 2001 From: Ivan Pusic <450140+ivpusic@users.noreply.github.com> Date: Fri, 6 Mar 2026 10:23:19 +0100 Subject: [PATCH 2/2] Enrich command and flag descriptions for AI agent consumption Merge the detailed flag descriptions from PR #15 (max lengths, type format hints, behavioral details, category explanations) with the examples and Long descriptions from the search_by_contract work. Every Short, Long, and flag help string is now maximally descriptive so AI agents can select the right commands and flags without ambiguity. --- cli/root.go | 15 +++++++++++--- cmd/dataset/search.go | 40 ++++++++++++++++++++++---------------- cmd/docs/search.go | 15 +++++++++----- cmd/execution/execution.go | 4 ++++ cmd/execution/results.go | 28 ++++++++++++++------------ cmd/query/create.go | 21 ++++++++++++-------- cmd/query/query.go | 11 ++++++++--- cmd/query/run.go | 27 +++++++++++++++---------- cmd/query/run_sql.go | 28 +++++++++++++++----------- cmd/query/update.go | 22 ++++++++++++--------- cmd/usage/usage.go | 13 ++++++++----- 11 files changed, 141 insertions(+), 83 deletions(-) diff --git a/cli/root.go b/cli/root.go index 73be107..44eff64 100644 --- a/cli/root.go +++ b/cli/root.go @@ -24,9 +24,18 @@ var apiKeyFlag string var rootCmd = &cobra.Command{ Use: "dune", - Short: "Dune CLI — interact with the Dune Analytics API", - Long: "A command-line interface for interacting with the Dune Analytics API.\n" + - "Manage queries, execute DuneSQL queries, search datasets, browse documentation, and monitor usage.", + Short: "Dune CLI — query, explore, and manage blockchain data on Dune Analytics", + Long: "A command-line interface for the Dune Analytics platform.\n\n" + + "Discover datasets across the Dune catalog, execute SQL queries (DuneSQL dialect),\n" + + "retrieve execution results, and manage your saved queries — all from the terminal.\n\n" + + "Capabilities:\n" + + " - Search datasets by keyword, contract address, category, or blockchain\n" + + " - Create, update, archive, and retrieve saved DuneSQL queries\n" + + " - Execute saved queries or raw DuneSQL and display results\n" + + " - Browse Dune documentation for DuneSQL syntax, API references, and guides\n" + + " - Monitor credit usage, storage consumption, and billing periods\n\n" + + "Authenticate with an API key via --api-key, the DUNE_API_KEY environment variable,\n" + + "or by running `dune auth`.", PersistentPreRunE: func(cmd *cobra.Command, _ []string) error { if cmd.Annotations["skipAuth"] == "true" { return nil diff --git a/cmd/dataset/search.go b/cmd/dataset/search.go index 8c6f0f6..d29be53 100644 --- a/cmd/dataset/search.go +++ b/cmd/dataset/search.go @@ -13,31 +13,37 @@ import ( func newSearchCmd() *cobra.Command { cmd := &cobra.Command{ Use: "search", - Short: "Search for datasets across the Dune catalog", - Long: "Search for datasets across the Dune catalog by keyword, category, blockchain, and more.\n\n" + - "The catalog includes canonical blockchain data, decoded contract tables,\n" + - "Spellbook transformations, and community datasets. Use --include-schema\n" + - "to get column names and types for SQL generation.\n\n" + + Short: "Search for tables and datasets across the Dune catalog", + Long: "Natural-language table discovery across the Dune catalog. Use this command\n" + + "to find concrete table names for use in SQL queries.\n\n" + + "The catalog includes:\n" + + " - canonical: chain-native primitives (blocks, transactions, logs, traces)\n" + + " - decoded: ABI-level events and function calls parsed from contract interactions\n" + + " - spell: curated Spellbook transformation datasets (e.g. dex.trades)\n" + + " - community: user-contributed tables and uploads\n\n" + + "Filter by category, blockchain, schema, dataset type, or ownership scope.\n" + + "Use --include-schema to get column names and types for SQL generation.\n\n" + + "For contract-specific lookup, use 'dune dataset search-by-contract' instead.\n\n" + "Examples:\n" + " dune dataset search --query \"uniswap swaps\"\n" + " dune dataset search --query \"transfers\" --categories decoded --blockchains ethereum\n" + " dune dataset search --query \"dex trades\" --categories spell --include-schema --output json\n" + - " dune dataset search --owner-scope me", + " dune dataset search --query \"*\" --owner-scope me", RunE: runSearch, } - cmd.Flags().String("query", "", "search query text") - cmd.Flags().StringArray("categories", nil, "filter by category (canonical, decoded, spell, community)") - cmd.Flags().StringArray("blockchains", nil, "filter by blockchain") + cmd.Flags().String("query", "", "natural-language search intent or entity hints (e.g. 'uniswap v3 swaps'); use '*' to browse without keyword bias") + cmd.Flags().StringArray("categories", nil, "filter by table family: canonical (chain primitives), decoded (ABI-level events/calls), spell (curated datasets), community (user-contributed)") + cmd.Flags().StringArray("blockchains", nil, "chain scope to reduce ambiguity and improve ranking (e.g. ethereum, solana, arbitrum)") cmd.Flags().StringArray("dataset-types", nil, - "filter by dataset type (dune_table, decoded_table, spell, uploaded_table, transformation_table, transformation_view)") - cmd.Flags().StringArray("schemas", nil, "filter by schema") - cmd.Flags().String("owner-scope", "", "ownership filter (all, me, team)") - cmd.Flags().Bool("include-private", false, "include private datasets") - cmd.Flags().Bool("include-schema", false, "include column schema in results") - cmd.Flags().Bool("include-metadata", false, "include metadata in results") - cmd.Flags().Int32("limit", 20, "maximum number of results") - cmd.Flags().Int32("offset", 0, "pagination offset") + "fine-grained dataset type filter: dune_table, decoded_table, spell, uploaded_table, transformation_table, transformation_view") + cmd.Flags().StringArray("schemas", nil, "schema/namespace constraint for high precision (e.g. dex, uniswap_v3_ethereum)") + cmd.Flags().String("owner-scope", "", "ownership filter: all (default), me, or team; does NOT automatically include private datasets") + cmd.Flags().Bool("include-private", false, "widen results to include private datasets visible to the authenticated user/team alongside public ones") + cmd.Flags().Bool("include-schema", false, "include column-level schema (name, type, nullable) for every result; useful when preparing SQL") + cmd.Flags().Bool("include-metadata", false, "include category-specific metadata (page_rank_score, description, abi_type, contract_name, project_name, etc.)") + cmd.Flags().Int32("limit", 20, "number of results per page; use 5-15 for quick checks, 20-50 for deeper exploration") + cmd.Flags().Int32("offset", 0, "pagination offset; combine with limit to page through large result sets") output.AddFormatFlag(cmd, "text") return cmd diff --git a/cmd/docs/search.go b/cmd/docs/search.go index d81b627..27b1212 100644 --- a/cmd/docs/search.go +++ b/cmd/docs/search.go @@ -14,10 +14,15 @@ const defaultMCPEndpoint = "https://docs.dune.com/mcp" func newSearchCmd() *cobra.Command { cmd := &cobra.Command{ Use: "search", - Short: "Search the Dune documentation", - Long: "Search the official Dune documentation. No authentication required.\n\n" + - "Look up DuneSQL syntax, API references, table naming conventions,\n" + - "and Dune platform concepts.\n\n" + + Short: "Search the Dune documentation for guides, API references, and code examples", + Long: "Search across all Dune documentation pages including guides, API references,\n" + + "DuneSQL syntax, table naming conventions, and code examples.\n" + + "Does not require authentication.\n\n" + + "Useful for looking up:\n" + + " - DuneSQL functions and syntax (date/time, string, aggregate, window functions)\n" + + " - API endpoint references and authentication\n" + + " - Table naming conventions and dataset structure\n" + + " - Dune platform concepts (decoding, Spellbook, materialized views)\n\n" + "Examples:\n" + " dune docs search --query \"DuneSQL string functions\"\n" + " dune docs search --query \"execute query API\" --api-reference-only\n" + @@ -26,7 +31,7 @@ func newSearchCmd() *cobra.Command { RunE: runSearch, } - cmd.Flags().String("query", "", "search query text, e.g. 'DuneSQL date functions' or 'API authentication' (required)") + cmd.Flags().String("query", "", "natural-language search query for Dune docs; e.g. 'DuneSQL date functions', 'API authentication', 'decoded tables meaning', 'query pagination' (required)") _ = cmd.MarkFlagRequired("query") cmd.Flags().Bool("api-reference-only", false, "prioritize API reference pages over conceptual guides") cmd.Flags().Bool("code-only", false, "prioritize pages with executable examples and code snippets") diff --git a/cmd/execution/execution.go b/cmd/execution/execution.go index 8969ef5..505403b 100644 --- a/cmd/execution/execution.go +++ b/cmd/execution/execution.go @@ -7,6 +7,10 @@ func NewExecutionCmd() *cobra.Command { cmd := &cobra.Command{ Use: "execution", Short: "Retrieve and inspect query execution results", + Long: "Retrieve and inspect the results of query executions.\n\n" + + "Use 'execution results ' to fetch results from a previously\n" + + "submitted query execution. The execution ID is returned when running queries\n" + + "with 'dune query run --no-wait' or 'dune query run-sql --no-wait'.", } cmd.AddCommand(newResultsCmd()) return cmd diff --git a/cmd/execution/results.go b/cmd/execution/results.go index 26a1235..ba98245 100644 --- a/cmd/execution/results.go +++ b/cmd/execution/results.go @@ -17,27 +17,31 @@ var PollInterval = 2 * time.Second func newResultsCmd() *cobra.Command { cmd := &cobra.Command{ Use: "results ", - Short: "Fetch results of a query execution", - Long: "Fetch results of a query execution by its execution ID.\n\n" + - "Use this after submitting a query with --no-wait, or to re-fetch results from\n" + - "a completed execution. The command handles all execution states:\n" + - " - Completed: displays result rows\n" + - " - Pending/Executing: shows current state (re-run later)\n" + - " - Failed: returns the error message\n" + - " - Cancelled: returns cancellation notice\n\n" + + Short: "Retrieve execution results for a query execution by execution ID", + Long: "Retrieve the results of a query execution. By default, waits for the execution\n" + + "to complete (up to the timeout) before returning results.\n\n" + + "Behavior:\n" + + " 1. Checks the current execution status\n" + + " 2. If still running: polls every 2 seconds until complete or timeout is reached\n" + + " 3. If completed: returns the result data\n" + + " 4. If failed/cancelled: returns the error details\n\n" + + "Use --no-wait to return the current state immediately without polling.\n" + "Use --limit and --offset to paginate through large result sets.\n\n" + + "Use this after submitting a query with --no-wait, or to re-fetch results from\n" + + "a completed execution.\n\n" + "Examples:\n" + " dune execution results 01JXYZ...\n" + " dune execution results 01JXYZ... --limit 100 --offset 200\n" + + " dune execution results 01JXYZ... --no-wait\n" + " dune execution results 01JXYZ... --output json", Args: cobra.ExactArgs(1), RunE: runResults, } - cmd.Flags().Int("limit", 0, "maximum number of result rows to return (0 = all)") - cmd.Flags().Int("offset", 0, "number of rows to skip before returning results, used for pagination") - cmd.Flags().Bool("no-wait", false, "return the current execution state immediately without waiting for completion") - cmd.Flags().Int("timeout", 300, "maximum seconds to wait for the execution to complete before timing out") + cmd.Flags().Int("limit", 0, "maximum number of result rows to return (0 = all); use with --offset to paginate large result sets") + cmd.Flags().Int("offset", 0, "number of rows to skip before starting to return results; use with --limit for pagination through large result sets") + cmd.Flags().Bool("no-wait", false, "return the current execution state immediately without waiting for completion; useful for checking status of long-running queries") + cmd.Flags().Int("timeout", 300, "maximum seconds to wait for the execution to complete before timing out (default 300s = 5 minutes)") output.AddFormatFlag(cmd, "text") return cmd diff --git a/cmd/query/create.go b/cmd/query/create.go index c3cf062..cc5f0dd 100644 --- a/cmd/query/create.go +++ b/cmd/query/create.go @@ -12,9 +12,14 @@ import ( func newCreateCmd() *cobra.Command { cmd := &cobra.Command{ Use: "create", - Short: "Create a new saved query", - Long: "Create a new saved DuneSQL query. Returns the numeric query ID on success.\n" + - "Use --temp to create a temporary query that won't appear in your saved queries list.\n\n" + + Short: "Create a new saved Dune query and return the query ID", + Long: "Create a new SQL query on Dune. Returns the query ID on success.\n\n" + + "The query is written in DuneSQL dialect. If the query targets tables with\n" + + "known partition columns, include a WHERE filter on those columns\n" + + "(e.g. WHERE block_date >= CURRENT_DATE - INTERVAL '7' DAY) to enable\n" + + "partition pruning and reduce query cost.\n\n" + + "Use --temp to create a temporary query that won't appear in the dune.com\n" + + "library or be accessible when shared. Temporary queries are auto-deleted.\n\n" + "Examples:\n" + " dune query create --name \"Recent Blocks\" --sql \"SELECT * FROM ethereum.blocks LIMIT 10\"\n" + " dune query create --name \"My Query\" --sql \"SELECT 1\" --private --description \"Test query\"\n" + @@ -22,11 +27,11 @@ func newCreateCmd() *cobra.Command { RunE: runCreate, } - cmd.Flags().String("name", "", "query name (required)") - cmd.Flags().String("sql", "", "DuneSQL query text (required)") - cmd.Flags().String("description", "", "query description") - cmd.Flags().Bool("private", false, "make the query private") - cmd.Flags().Bool("temp", false, "create a temporary query (auto-deleted, not shown in your saved queries)") + cmd.Flags().String("name", "", "human-readable query title, max 600 characters (required)") + cmd.Flags().String("sql", "", "the SQL query text in DuneSQL dialect, max 500,000 characters (required)") + cmd.Flags().String("description", "", "short description of what the query does, max 1,000 characters") + cmd.Flags().Bool("private", false, "make the query private; may be forced by team privacy settings") + cmd.Flags().Bool("temp", false, "create a temporary query that won't appear in the dune.com library or be accessible when shared; auto-deleted") _ = cmd.MarkFlagRequired("name") _ = cmd.MarkFlagRequired("sql") output.AddFormatFlag(cmd, "text") diff --git a/cmd/query/query.go b/cmd/query/query.go index 5e628f3..10c3b3d 100644 --- a/cmd/query/query.go +++ b/cmd/query/query.go @@ -6,10 +6,15 @@ import "github.com/spf13/cobra" func NewQueryCmd() *cobra.Command { cmd := &cobra.Command{ Use: "query", - Short: "Manage Dune queries", + Short: "Create, retrieve, update, execute, and archive Dune queries", Long: "Create, retrieve, update, archive, and execute DuneSQL queries.\n\n" + - "Use 'query create' to save a reusable query, 'query run' to execute a saved query,\n" + - "or 'query run-sql' to execute raw DuneSQL without saving.", + "Subcommands:\n" + + " create - Save a new reusable DuneSQL query and get its query ID\n" + + " get - Fetch a saved query's SQL, metadata, and execution state\n" + + " update - Modify a query's SQL, title, description, privacy, or tags\n" + + " archive - Hide a query from the library (still retrievable by ID)\n" + + " run - Execute a saved query by ID and display results\n" + + " run-sql - Execute raw DuneSQL inline without saving a query", } cmd.AddCommand(newCreateCmd()) cmd.AddCommand(newGetCmd()) diff --git a/cmd/query/run.go b/cmd/query/run.go index 036e6f6..026ea8b 100644 --- a/cmd/query/run.go +++ b/cmd/query/run.go @@ -13,24 +13,31 @@ import ( func newRunCmd() *cobra.Command { cmd := &cobra.Command{ Use: "run ", - Short: "Execute a saved query and display results", - Long: "Execute a saved DuneSQL query by its numeric ID and display results.\n\n" + - "By default, polls every 5 seconds for up to ~5 minutes waiting for completion.\n" + - "Use --no-wait to submit the execution and exit immediately; then fetch\n" + - "results later with 'dune execution results '.\n\n" + + Short: "Execute a saved Dune query by its ID and display results", + Long: "Execute a saved Dune query by its numeric ID. By default, waits for the\n" + + "execution to complete (polling every 2 seconds) and displays the result rows.\n" + + "Use --no-wait to submit the execution and exit immediately with just the\n" + + "execution ID; then fetch results later with 'dune execution results '.\n\n" + + "Credits are consumed based on actual compute resources used. Use --performance\n" + + "to select the engine size (medium or large).\n\n" + + "Important: if the query targets tables with known partition columns (returned by\n" + + "'dune dataset search' or 'dune dataset search-by-contract'), ensure the SQL includes\n" + + "a WHERE filter on those partition columns (e.g. WHERE block_date >= CURRENT_DATE -\n" + + "INTERVAL '7' DAY). This enables partition pruning and significantly reduces query cost.\n\n" + "Examples:\n" + " dune query run 12345\n" + " dune query run 12345 --param wallet=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --param days=30\n" + " dune query run 12345 --performance large --limit 100\n" + - " dune query run 12345 --no-wait", + " dune query run 12345 --no-wait\n" + + " dune query run 12345 --timeout 600", Args: cobra.ExactArgs(1), RunE: runRun, } - cmd.Flags().StringArray("param", nil, "query parameter in key=value format (repeatable)") - cmd.Flags().String("performance", "medium", `performance tier: "medium" (default) or "large" for higher compute resources`) - cmd.Flags().Int("limit", 0, "maximum number of rows to display (0 = all)") - cmd.Flags().Bool("no-wait", false, "submit execution and exit without waiting for results") + cmd.Flags().StringArray("param", nil, "typed query parameter in key=value format (repeatable); supported types: text, number (stringified, e.g. '30'), datetime (YYYY-MM-DD HH:mm:ss), enum") + cmd.Flags().String("performance", "medium", `engine size for the execution: "medium" (default) or "large"; credits are consumed based on actual compute resources used`) + cmd.Flags().Int("limit", 0, "maximum number of result rows to return (0 = all available rows)") + cmd.Flags().Bool("no-wait", false, "submit the execution and exit immediately, printing only the execution ID and state") cmd.Flags().Int("timeout", 300, "maximum seconds to wait for the execution to complete before timing out") output.AddFormatFlag(cmd, "text") diff --git a/cmd/query/run_sql.go b/cmd/query/run_sql.go index f1c74c0..40a4f75 100644 --- a/cmd/query/run_sql.go +++ b/cmd/query/run_sql.go @@ -10,25 +10,31 @@ import ( func newRunSQLCmd() *cobra.Command { cmd := &cobra.Command{ Use: "run-sql", - Short: "Execute raw DuneSQL and display results", - Long: "Execute a DuneSQL query directly without creating a saved query.\n" + - "Ideal for ad-hoc exploration and one-off analysis.\n\n" + - "By default, polls every 5 seconds for up to ~5 minutes waiting for completion.\n" + - "Use --no-wait to submit and exit immediately.\n\n" + + Short: "Execute a raw DuneSQL query inline and display results", + Long: "Execute an inline SQL statement in DuneSQL dialect without saving it as a\n" + + "query on Dune. Ideal for ad-hoc exploration and one-off analysis.\n\n" + + "By default, waits for completion (polling every 2 seconds) and displays result rows.\n" + + "Use --no-wait to submit the execution and exit immediately with just the\n" + + "execution ID. Credits are consumed based on actual compute resources used.\n\n" + + "Important: if the SQL targets tables with known partition columns (returned by\n" + + "'dune dataset search' or 'dune dataset search-by-contract'), include a WHERE filter\n" + + "on those partition columns (e.g. WHERE block_date >= CURRENT_DATE - INTERVAL '7' DAY).\n" + + "This enables partition pruning and significantly reduces query cost.\n\n" + "Examples:\n" + " dune query run-sql --sql \"SELECT block_number, block_time FROM ethereum.blocks ORDER BY block_number DESC LIMIT 5\"\n" + " dune query run-sql --sql \"SELECT * FROM ethereum.transactions WHERE block_number = {{block_num}}\" --param block_num=20000000\n" + - " dune query run-sql --sql \"SELECT COUNT(*) FROM ethereum.transactions\" --performance large", + " dune query run-sql --sql \"SELECT COUNT(*) FROM ethereum.transactions\" --performance large\n" + + " dune query run-sql --sql \"SELECT 1\" --no-wait", Args: cobra.NoArgs, RunE: runRunSQL, } - cmd.Flags().String("sql", "", "DuneSQL query to execute (required)") + cmd.Flags().String("sql", "", "the SQL query text in DuneSQL dialect (required)") _ = cmd.MarkFlagRequired("sql") - cmd.Flags().StringArray("param", nil, "query parameter in key=value format (repeatable)") - cmd.Flags().String("performance", "medium", `performance tier: "medium" (default) or "large" for higher compute resources`) - cmd.Flags().Int("limit", 0, "maximum number of rows to display (0 = all)") - cmd.Flags().Bool("no-wait", false, "submit execution and exit without waiting for results") + cmd.Flags().StringArray("param", nil, "typed query parameter in key=value format (repeatable); supported types: text, number (stringified, e.g. '30'), datetime (YYYY-MM-DD HH:mm:ss), enum") + cmd.Flags().String("performance", "medium", `engine size for the execution: "medium" (default) or "large"; credits are consumed based on actual compute resources used`) + cmd.Flags().Int("limit", 0, "maximum number of result rows to return (0 = all available rows)") + cmd.Flags().Bool("no-wait", false, "submit the execution and exit immediately, printing only the execution ID and state") cmd.Flags().Int("timeout", 300, "maximum seconds to wait for the execution to complete before timing out") output.AddFormatFlag(cmd, "text") diff --git a/cmd/query/update.go b/cmd/query/update.go index 147803f..30d4e11 100644 --- a/cmd/query/update.go +++ b/cmd/query/update.go @@ -12,21 +12,25 @@ import ( func newUpdateCmd() *cobra.Command { cmd := &cobra.Command{ Use: "update ", - Short: "Update an existing saved query", - Long: "Update an existing saved query. At least one flag must be provided.\n" + - "Only the specified fields are modified; omitted fields remain unchanged.\n\n" + + Short: "Update an existing Dune query's SQL, title, description, privacy, or tags", + Long: "Modify a query you own or have edit access to via team membership.\n" + + "Only supply the flags you want to change; unchanged fields are preserved.\n" + + "At least one flag must be provided.\n\n" + + "The update uses optimistic locking — if someone else edited the query\n" + + "concurrently, you'll get a conflict error.\n\n" + "Examples:\n" + " dune query update 12345 --sql \"SELECT * FROM ethereum.blocks LIMIT 20\"\n" + - " dune query update 12345 --name \"Renamed Query\" --tags defi,ethereum", + " dune query update 12345 --name \"Renamed Query\" --tags defi,ethereum\n" + + " dune query update 12345 --private", Args: cobra.ExactArgs(1), RunE: runUpdate, } - cmd.Flags().String("name", "", "query name") - cmd.Flags().String("sql", "", "DuneSQL query text") - cmd.Flags().String("description", "", "query description") - cmd.Flags().Bool("private", false, "make the query private") - cmd.Flags().StringSlice("tags", nil, "query tags (comma-separated)") + cmd.Flags().String("name", "", "new title for the query, max 600 characters") + cmd.Flags().String("sql", "", "new SQL content in DuneSQL dialect, max 500,000 characters") + cmd.Flags().String("description", "", "new description for the query, max 1,000 characters") + cmd.Flags().Bool("private", false, "set to true to make the query private, false to make public") + cmd.Flags().StringSlice("tags", nil, "new set of tags for the query (comma-separated); replaces all existing tags") output.AddFormatFlag(cmd, "text") return cmd diff --git a/cmd/usage/usage.go b/cmd/usage/usage.go index 8998241..e5fe27f 100644 --- a/cmd/usage/usage.go +++ b/cmd/usage/usage.go @@ -14,11 +14,14 @@ import ( func NewUsageCmd() *cobra.Command { cmd := &cobra.Command{ Use: "usage", - Short: "Show credit and resource usage for your Dune account", - Long: "Show credit and resource usage for your Dune account.\n\n" + - "Displays private queries/dashboards count, storage usage, and credit\n" + - "consumption per billing period. Use --start-date and --end-date to\n" + - "filter to a specific date range.\n\n" + + Short: "Show credit and resource usage for the authenticated Dune account", + Long: "Show current-period credit usage, storage consumption, and billing period\n" + + "boundaries for the authenticated user or team.\n\n" + + "Displays:\n" + + " - Private queries and dashboards count\n" + + " - Storage bytes used vs. allowed\n" + + " - Credits used and included per billing period\n\n" + + "Optionally filter by date range with --start-date and --end-date.\n\n" + "Examples:\n" + " dune usage\n" + " dune usage --start-date 2025-01-01 --end-date 2025-06-01\n" +