diff --git a/.agents/plugins/api_marketplace.json b/.agents/plugins/api_marketplace.json index 99fb0d41d..286db8da7 100644 --- a/.agents/plugins/api_marketplace.json +++ b/.agents/plugins/api_marketplace.json @@ -220,18 +220,6 @@ }, "category": "Developer Tools" }, - { - "name": "render", - "source": { - "source": "local", - "path": "./plugins/render" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, { "name": "temporal", "source": { @@ -558,6 +546,18 @@ "authentication": "ON_INSTALL" }, "category": "Developer Tools" + }, + { + "name": "product-design", + "source": { + "source": "local", + "path": "./plugins/product-design" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_USE" + }, + "category": "Creativity" } ] } diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json index 0fc9e7cf4..7a8c15e54 100644 --- a/.agents/plugins/marketplace.json +++ b/.agents/plugins/marketplace.json @@ -136,42 +136,6 @@ }, "category": "Creativity" }, - { - "name": "hugging-face", - "source": { - "source": "local", - "path": "./plugins/hugging-face" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, - { - "name": "jam", - "source": { - "source": "local", - "path": "./plugins/jam" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "netlify", - "source": { - "source": "local", - "path": "./plugins/netlify" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, { "name": "stripe", "source": { @@ -226,18 +190,6 @@ }, "category": "Developer Tools" }, - { - "name": "box", - "source": { - "source": "local", - "path": "./plugins/box" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, { "name": "github", "source": { @@ -274,18 +226,6 @@ }, "category": "Productivity" }, - { - "name": "deepnote", - "source": { - "source": "local", - "path": "./plugins/deepnote" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Data & Analytics" - }, { "name": "notion", "source": { @@ -454,18 +394,6 @@ }, "category": "Developer Tools" }, - { - "name": "neon-postgres", - "source": { - "source": "local", - "path": "./plugins/neon-postgres" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, { "name": "remotion", "source": { @@ -497,130 +425,127 @@ "category": "Developer Tools" }, { - "name": "alpaca", - "source": { - "source": "local", - "path": "./plugins/alpaca" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Finance" - }, - { - "name": "amplitude", + "name": "granola", "source": { "source": "local", - "path": "./plugins/amplitude" + "path": "./plugins/granola" }, "policy": { "installation": "AVAILABLE", "authentication": "ON_INSTALL" }, - "category": "Data & Analytics" + "category": "Productivity" }, { - "name": "attio", + "name": "monday-com", "source": { "source": "local", - "path": "./plugins/attio" + "path": "./plugins/monday-com" }, "policy": { "installation": "AVAILABLE", "authentication": "ON_INSTALL" }, - "category": "Business & Operations" + "category": "Productivity" }, { - "name": "binance", + "name": "temporal", "source": { "source": "local", - "path": "./plugins/binance" + "path": "./plugins/temporal" }, "policy": { "installation": "AVAILABLE", "authentication": "ON_INSTALL" }, - "category": "Finance" + "category": "Developer Tools" }, { - "name": "biorender", + "name": "hyperframes", "source": { "source": "local", - "path": "./plugins/biorender" + "path": "./plugins/hyperframes" }, "policy": { "installation": "AVAILABLE", - "authentication": "ON_INSTALL" + "authentication": "ON_INSTALL", + "products": [ + "CODEX" + ] }, "category": "Creativity" }, { - "name": "brand24", + "name": "supabase", "source": { "source": "local", - "path": "./plugins/brand24" + "path": "./plugins/supabase" }, "policy": { "installation": "AVAILABLE", "authentication": "ON_INSTALL" }, - "category": "Productivity" + "category": "Developer Tools" }, { - "name": "brex", + "name": "codex-security", "source": { "source": "local", - "path": "./plugins/brex" + "path": "./plugins/codex-security" }, "policy": { "installation": "AVAILABLE", - "authentication": "ON_INSTALL" + "authentication": "ON_USE", + "products": [ + "CODEX" + ] }, - "category": "Finance" + "category": "Security" }, { - "name": "carta-crm", + "name": "twilio-developer-kit", "source": { "source": "local", - "path": "./plugins/carta-crm" + "path": "./plugins/twilio-developer-kit" }, "policy": { "installation": "AVAILABLE", "authentication": "ON_INSTALL" }, - "category": "Business & Operations" + "category": "Developer Tools" }, { - "name": "cb-insights", + "name": "openai-developers", "source": { "source": "local", - "path": "./plugins/cb-insights" + "path": "./plugins/openai-developers" }, "policy": { "installation": "AVAILABLE", - "authentication": "ON_INSTALL" + "authentication": "ON_INSTALL", + "products": [ + "CODEX" + ] }, - "category": "Finance" + "category": "Developer Tools" }, { - "name": "channel99", + "name": "datadog", "source": { "source": "local", - "path": "./plugins/channel99" + "path": "./plugins/datadog" }, "policy": { "installation": "AVAILABLE", "authentication": "ON_INSTALL" }, - "category": "Productivity" + "category": "Developer Tools" }, { - "name": "circleback", + "name": "zoom", "source": { "source": "local", - "path": "./plugins/circleback" + "path": "./plugins/zoom" }, "policy": { "installation": "AVAILABLE", @@ -629,46 +554,22 @@ "category": "Communication" }, { - "name": "clickup", - "source": { - "source": "local", - "path": "./plugins/clickup" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "cloudinary", - "source": { - "source": "local", - "path": "./plugins/cloudinary" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, - { - "name": "cogedim", + "name": "mixpanel-headless", "source": { "source": "local", - "path": "./plugins/cogedim" + "path": "./plugins/mixpanel-headless" }, "policy": { "installation": "AVAILABLE", "authentication": "ON_INSTALL" }, - "category": "Other" + "category": "Data & Analytics" }, { - "name": "common-room", + "name": "airtable", "source": { "source": "local", - "path": "./plugins/common-room" + "path": "./plugins/airtable" }, "policy": { "installation": "AVAILABLE", @@ -677,22 +578,22 @@ "category": "Productivity" }, { - "name": "conductor", + "name": "nvidia", "source": { "source": "local", - "path": "./plugins/conductor" + "path": "./plugins/nvidia" }, "policy": { "installation": "AVAILABLE", "authentication": "ON_INSTALL" }, - "category": "Productivity" + "category": "Developer Tools" }, { - "name": "coupler-io", + "name": "posthog", "source": { "source": "local", - "path": "./plugins/coupler-io" + "path": "./plugins/posthog" }, "policy": { "installation": "AVAILABLE", @@ -701,46 +602,22 @@ "category": "Data & Analytics" }, { - "name": "coveo", - "source": { - "source": "local", - "path": "./plugins/coveo" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "cube", - "source": { - "source": "local", - "path": "./plugins/cube" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Finance" - }, - { - "name": "daloopa", + "name": "ngs-analysis", "source": { "source": "local", - "path": "./plugins/daloopa" + "path": "./plugins/ngs-analysis" }, "policy": { "installation": "AVAILABLE", "authentication": "ON_INSTALL" }, - "category": "Finance" + "category": "Education & Research" }, { - "name": "demandbase", + "name": "shopify", "source": { "source": "local", - "path": "./plugins/demandbase" + "path": "./plugins/shopify" }, "policy": { "installation": "AVAILABLE", @@ -749,58 +626,34 @@ "category": "Business & Operations" }, { - "name": "dnb-finance-analytics", - "source": { - "source": "local", - "path": "./plugins/dnb-finance-analytics" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Finance" - }, - { - "name": "docket", - "source": { - "source": "local", - "path": "./plugins/docket" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "domotz-preview", + "name": "magicpath", "source": { "source": "local", - "path": "./plugins/domotz-preview" + "path": "./plugins/magicpath" }, "policy": { "installation": "AVAILABLE", "authentication": "ON_INSTALL" }, - "category": "Productivity" + "category": "Developer Tools" }, { - "name": "dovetail", + "name": "openai-ads-conversions", "source": { "source": "local", - "path": "./plugins/dovetail" + "path": "./plugins/openai-ads-conversions" }, "policy": { "installation": "AVAILABLE", "authentication": "ON_INSTALL" }, - "category": "Productivity" + "category": "Developer Tools" }, { - "name": "dow-jones-factiva", + "name": "boltz-api-cli", "source": { "source": "local", - "path": "./plugins/dow-jones-factiva" + "path": "./plugins/boltz-api-cli" }, "policy": { "installation": "AVAILABLE", @@ -809,10 +662,10 @@ "category": "Education & Research" }, { - "name": "egnyte", + "name": "dropbox", "source": { "source": "local", - "path": "./plugins/egnyte" + "path": "./plugins/dropbox" }, "policy": { "installation": "AVAILABLE", @@ -821,1405 +674,16 @@ "category": "Productivity" }, { - "name": "finn", - "source": { - "source": "local", - "path": "./plugins/finn" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Travel" - }, - { - "name": "fireflies", - "source": { - "source": "local", - "path": "./plugins/fireflies" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Communication" - }, - { - "name": "fyxer", - "source": { - "source": "local", - "path": "./plugins/fyxer" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Communication" - }, - { - "name": "govtribe", - "source": { - "source": "local", - "path": "./plugins/govtribe" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Education & Research" - }, - { - "name": "granola", + "name": "product-design", "source": { "source": "local", - "path": "./plugins/granola" + "path": "./plugins/product-design" }, "policy": { "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "happenstance", - "source": { - "source": "local", - "path": "./plugins/happenstance" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "help-scout", - "source": { - "source": "local", - "path": "./plugins/help-scout" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "hex", - "source": { - "source": "local", - "path": "./plugins/hex" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Data & Analytics" - }, - { - "name": "highlevel", - "source": { - "source": "local", - "path": "./plugins/highlevel" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "hostinger", - "source": { - "source": "local", - "path": "./plugins/hostinger" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, - { - "name": "hubspot", - "source": { - "source": "local", - "path": "./plugins/hubspot" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Business & Operations" - }, - { - "name": "keybid-puls", - "source": { - "source": "local", - "path": "./plugins/keybid-puls" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Finance" - }, - { - "name": "marcopolo", - "source": { - "source": "local", - "path": "./plugins/marcopolo" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, - { - "name": "mem", - "source": { - "source": "local", - "path": "./plugins/mem" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "monday-com", - "source": { - "source": "local", - "path": "./plugins/monday-com" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "moody-s", - "source": { - "source": "local", - "path": "./plugins/moody-s" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Finance" - }, - { - "name": "morningstar", - "source": { - "source": "local", - "path": "./plugins/morningstar" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Finance" - }, - { - "name": "motherduck", - "source": { - "source": "local", - "path": "./plugins/motherduck" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Data & Analytics" - }, - { - "name": "mt-newswires", - "source": { - "source": "local", - "path": "./plugins/mt-newswires" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Finance" - }, - { - "name": "myregistry-com", - "source": { - "source": "local", - "path": "./plugins/myregistry-com" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Other" - }, - { - "name": "network-solutions", - "source": { - "source": "local", - "path": "./plugins/network-solutions" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "omni-analytics", - "source": { - "source": "local", - "path": "./plugins/omni-analytics" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Data & Analytics" - }, - { - "name": "otter-ai", - "source": { - "source": "local", - "path": "./plugins/otter-ai" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Communication" - }, - { - "name": "particl-market-research", - "source": { - "source": "local", - "path": "./plugins/particl-market-research" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Education & Research" - }, - { - "name": "pipedrive", - "source": { - "source": "local", - "path": "./plugins/pipedrive" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Business & Operations" - }, - { - "name": "pitchbook", - "source": { - "source": "local", - "path": "./plugins/pitchbook" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Finance" - }, - { - "name": "policynote", - "source": { - "source": "local", - "path": "./plugins/policynote" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Education & Research" - }, - { - "name": "pylon", - "source": { - "source": "local", - "path": "./plugins/pylon" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "quartr", - "source": { - "source": "local", - "path": "./plugins/quartr" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Finance" - }, - { - "name": "quicknode", - "source": { - "source": "local", - "path": "./plugins/quicknode" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, - { - "name": "ranked-ai", - "source": { - "source": "local", - "path": "./plugins/ranked-ai" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "razorpay", - "source": { - "source": "local", - "path": "./plugins/razorpay" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Finance" - }, - { - "name": "read-ai", - "source": { - "source": "local", - "path": "./plugins/read-ai" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Communication" - }, - { - "name": "readwise", - "source": { - "source": "local", - "path": "./plugins/readwise" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Education & Research" - }, - { - "name": "responsive", - "source": { - "source": "local", - "path": "./plugins/responsive" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "scite", - "source": { - "source": "local", - "path": "./plugins/scite" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Education & Research" - }, - { - "name": "semrush", - "source": { - "source": "local", - "path": "./plugins/semrush" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "sendgrid", - "source": { - "source": "local", - "path": "./plugins/sendgrid" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, - { - "name": "setu-bharat-connect-billpay", - "source": { - "source": "local", - "path": "./plugins/setu-bharat-connect-billpay" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Finance" - }, - { - "name": "signnow", - "source": { - "source": "local", - "path": "./plugins/signnow" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "skywatch", - "source": { - "source": "local", - "path": "./plugins/skywatch" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "statsig", - "source": { - "source": "local", - "path": "./plugins/statsig" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, - { - "name": "streak", - "source": { - "source": "local", - "path": "./plugins/streak" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Business & Operations" - }, - { - "name": "taxdown", - "source": { - "source": "local", - "path": "./plugins/taxdown" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Finance" - }, - { - "name": "teamwork-com", - "source": { - "source": "local", - "path": "./plugins/teamwork-com" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "third-bridge", - "source": { - "source": "local", - "path": "./plugins/third-bridge" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Finance" - }, - { - "name": "tinman-ai", - "source": { - "source": "local", - "path": "./plugins/tinman-ai" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Finance" - }, - { - "name": "united-rentals", - "source": { - "source": "local", - "path": "./plugins/united-rentals" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "vantage", - "source": { - "source": "local", - "path": "./plugins/vantage" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, - { - "name": "waldo", - "source": { - "source": "local", - "path": "./plugins/waldo" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "weatherpromise", - "source": { - "source": "local", - "path": "./plugins/weatherpromise" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Travel" - }, - { - "name": "windsor-ai", - "source": { - "source": "local", - "path": "./plugins/windsor-ai" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Data & Analytics" - }, - { - "name": "yepcode", - "source": { - "source": "local", - "path": "./plugins/yepcode" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, - { - "name": "render", - "source": { - "source": "local", - "path": "./plugins/render" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, - { - "name": "temporal", - "source": { - "source": "local", - "path": "./plugins/temporal" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, - { - "name": "hyperframes", - "source": { - "source": "local", - "path": "./plugins/hyperframes" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL", - "products": [ - "CODEX" - ] - }, - "category": "Creativity" - }, - { - "name": "heygen", - "source": { - "source": "local", - "path": "./plugins/heygen" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Creativity" - }, - { - "name": "supabase", - "source": { - "source": "local", - "path": "./plugins/supabase" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, - { - "name": "codex-security", - "source": { - "source": "local", - "path": "./plugins/codex-security" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_USE", - "products": [ - "CODEX" - ] - }, - "category": "Security" - }, - { - "name": "twilio-developer-kit", - "source": { - "source": "local", - "path": "./plugins/twilio-developer-kit" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, - { - "name": "openai-developers", - "source": { - "source": "local", - "path": "./plugins/openai-developers" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL", - "products": [ - "CODEX" - ] - }, - "category": "Developer Tools" - }, - { - "name": "asana", - "source": { - "source": "local", - "path": "./plugins/asana" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "datadog", - "source": { - "source": "local", - "path": "./plugins/datadog" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, - { - "name": "zoom", - "source": { - "source": "local", - "path": "./plugins/zoom" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Communication" - }, - { - "name": "similarweb", - "source": { - "source": "local", - "path": "./plugins/similarweb" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Data & Analytics" - }, - { - "name": "lseg", - "source": { - "source": "local", - "path": "./plugins/lseg" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Finance" - }, - { - "name": "s-p", - "source": { - "source": "local", - "path": "./plugins/s-p" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Finance" - }, - { - "name": "datasite", - "source": { - "source": "local", - "path": "./plugins/datasite" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "factset", - "source": { - "source": "local", - "path": "./plugins/factset" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Finance" - }, - { - "name": "zoominfo", - "source": { - "source": "local", - "path": "./plugins/zoominfo" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Business & Operations" - }, - { - "name": "docusign", - "source": { - "source": "local", - "path": "./plugins/docusign" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "mixpanel", - "source": { - "source": "local", - "path": "./plugins/mixpanel" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Data & Analytics" - }, - { - "name": "mixpanel-headless", - "source": { - "source": "local", - "path": "./plugins/mixpanel-headless" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Data & Analytics" - }, - { - "name": "aiera", - "source": { - "source": "local", - "path": "./plugins/aiera" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Finance" - }, - { - "name": "close", - "source": { - "source": "local", - "path": "./plugins/close" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Business & Operations" - }, - { - "name": "apollo", - "source": { - "source": "local", - "path": "./plugins/apollo" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Business & Operations" - }, - { - "name": "meticulate", - "source": { - "source": "local", - "path": "./plugins/meticulate" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "thoughtspot", - "source": { - "source": "local", - "path": "./plugins/thoughtspot" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Data & Analytics" - }, - { - "name": "midpage", - "source": { - "source": "local", - "path": "./plugins/midpage" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Education & Research" - }, - { - "name": "clay", - "source": { - "source": "local", - "path": "./plugins/clay" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Business & Operations" - }, - { - "name": "calendly", - "source": { - "source": "local", - "path": "./plugins/calendly" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "rox", - "source": { - "source": "local", - "path": "./plugins/rox" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "hg-insights", - "source": { - "source": "local", - "path": "./plugins/hg-insights" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "airtable", - "source": { - "source": "local", - "path": "./plugins/airtable" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "convex", - "source": { - "source": "local", - "path": "./plugins/convex" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, - { - "name": "outreach", - "source": { - "source": "local", - "path": "./plugins/outreach" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Business & Operations" - }, - { - "name": "shutterstock", - "source": { - "source": "local", - "path": "./plugins/shutterstock" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Creativity" - }, - { - "name": "replit", - "source": { - "source": "local", - "path": "./plugins/replit" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, - { - "name": "lovable", - "source": { - "source": "local", - "path": "./plugins/lovable" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, - { - "name": "quickbooks", - "source": { - "source": "local", - "path": "./plugins/quickbooks" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Finance" - }, - { - "name": "intercom", - "source": { - "source": "local", - "path": "./plugins/intercom" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Business & Operations" - }, - { - "name": "chronograph-lp", - "source": { - "source": "local", - "path": "./plugins/chronograph-lp" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Finance" - }, - { - "name": "nvidia", - "source": { - "source": "local", - "path": "./plugins/nvidia" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, - { - "name": "posthog", - "source": { - "source": "local", - "path": "./plugins/posthog" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Data & Analytics" - }, - { - "name": "actively", - "source": { - "source": "local", - "path": "./plugins/actively" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Business & Operations" - }, - { - "name": "zoho", - "source": { - "source": "local", - "path": "./plugins/zoho" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Business & Operations" - }, - { - "name": "fiscal-ai", - "source": { - "source": "local", - "path": "./plugins/fiscal-ai" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Finance" - }, - { - "name": "picsart", - "source": { - "source": "local", - "path": "./plugins/picsart" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" + "authentication": "ON_USE" }, "category": "Creativity" - }, - { - "name": "alation", - "source": { - "source": "local", - "path": "./plugins/alation" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Data & Analytics" - }, - { - "name": "fal", - "source": { - "source": "local", - "path": "./plugins/fal" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Creativity" - }, - { - "name": "hebbia", - "source": { - "source": "local", - "path": "./plugins/hebbia" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Business & Operations" - }, - { - "name": "wix", - "source": { - "source": "local", - "path": "./plugins/wix" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, - { - "name": "base44", - "source": { - "source": "local", - "path": "./plugins/base44" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, - { - "name": "ngs-analysis", - "source": { - "source": "local", - "path": "./plugins/ngs-analysis" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Education & Research" - }, - { - "name": "superhuman", - "source": { - "source": "local", - "path": "./plugins/superhuman" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Communication" - }, - { - "name": "shopify", - "source": { - "source": "local", - "path": "./plugins/shopify" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Business & Operations" - }, - { - "name": "magicpath", - "source": { - "source": "local", - "path": "./plugins/magicpath" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, - { - "name": "brighthire", - "source": { - "source": "local", - "path": "./plugins/brighthire" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "catalyst-by-zoho", - "source": { - "source": "local", - "path": "./plugins/catalyst-by-zoho" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, - { - "name": "glean", - "source": { - "source": "local", - "path": "./plugins/glean" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "chronograph-gp", - "source": { - "source": "local", - "path": "./plugins/chronograph-gp" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Finance" - }, - { - "name": "openai-ads-conversions", - "source": { - "source": "local", - "path": "./plugins/openai-ads-conversions" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, - { - "name": "boltz-api-cli", - "source": { - "source": "local", - "path": "./plugins/boltz-api-cli" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Education & Research" - }, - { - "name": "replayio", - "source": { - "source": "local", - "path": "./plugins/replayio" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, - { - "name": "digitalocean", - "source": { - "source": "local", - "path": "./plugins/digitalocean" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - }, - { - "name": "dropbox", - "source": { - "source": "local", - "path": "./plugins/dropbox" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" } ] } diff --git a/plugins/actively/.app.json b/plugins/actively/.app.json deleted file mode 100644 index 2bb692df9..000000000 --- a/plugins/actively/.app.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "apps": { - "actively": { - "id": "asdk_app_6a15fca0d57c8191a204ffdd12fbbef2" - } - } -} diff --git a/plugins/actively/.codex-plugin/plugin.json b/plugins/actively/.codex-plugin/plugin.json deleted file mode 100644 index 105498a22..000000000 --- a/plugins/actively/.codex-plugin/plugin.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "actively", - "version": "1.0.3", - "description": "Account agents for GTM intelligence", - "author": { - "name": "Actively", - "url": "https://www.actively.ai" - }, - "homepage": "https://www.actively.ai", - "repository": "https://github.com/openai/plugins", - "license": "MIT", - "keywords": [], - "apps": "./.app.json", - "interface": { - "displayName": "Actively", - "shortDescription": "Account agents for GTM intelligence", - "longDescription": "Win more deals with Actively by directly accessing your always-on per account agents that help you drive the next best action. Actively’s per-account agents are synthesizing across all of your internal context (ex. CRM data, call transcripts, emails) and external signals to drive actionable intelligence. Designed for SDRs, AEs, AMs, and revenue leaders who need deep, contextual account knowledge, from meeting prep and deal strategy to territory prioritization.", - "developerName": "Actively", - "category": "Business & Operations", - "capabilities": [], - "websiteURL": "https://www.actively.ai", - "defaultPrompt": [ - "Find high-fit Actively accounts showing recent buying signals and summarize next steps.", - "Search Actively for contacts at this target account and pull relevant prospect context.", - "Build a prioritized Actively prospect list for this ICP and explain why each account fits." - ], - "brandColor": "#000000", - "composerIcon": "./assets/logo.png", - "logo": "./assets/logo.png", - "screenshots": [] - } -} diff --git a/plugins/actively/assets/logo.png b/plugins/actively/assets/logo.png deleted file mode 100644 index 5bf942c8d..000000000 Binary files a/plugins/actively/assets/logo.png and /dev/null differ diff --git a/plugins/aiera/.app.json b/plugins/aiera/.app.json deleted file mode 100644 index 2b93b0d2c..000000000 --- a/plugins/aiera/.app.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "apps": { - "aiera": { - "id": "asdk_app_6967ddccc88881918a3733322b6bdf1a" - } - } -} diff --git a/plugins/aiera/.codex-plugin/plugin.json b/plugins/aiera/.codex-plugin/plugin.json deleted file mode 100644 index c939e3ff7..000000000 --- a/plugins/aiera/.codex-plugin/plugin.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "aiera", - "version": "1.0.2", - "description": "Institutional financial data and events", - "author": { - "name": "Aiera", - "url": "https://www.aiera.com" - }, - "repository": "https://github.com/openai/plugins", - "license": "MIT", - "keywords": [], - "apps": "./.app.json", - "interface": { - "displayName": "Aiera", - "developerName": "Aiera", - "shortDescription": "Institutional financial data and events", - "longDescription": "Access institutional-grade financial data from Aiera, including live corporate events, filings, company publications, broker research, and much more.", - "category": "Finance", - "capabilities": [], - "brandColor": "#180830", - "defaultPrompt": [ - "Find the latest earnings call transcript for a company in Aiera and summarize key themes.", - "Search Aiera events for mentions of a topic across this sector this quarter.", - "Compare management commentary from the last two calls for a company." - ], - "screenshots": [], - "composerIcon": "./assets/logo.png", - "logo": "./assets/logo.png", - "websiteURL": "https://www.aiera.com" - }, - "homepage": "https://www.aiera.com" -} diff --git a/plugins/aiera/assets/app-icon.svg b/plugins/aiera/assets/app-icon.svg deleted file mode 100644 index 998f8d3e5..000000000 --- a/plugins/aiera/assets/app-icon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - AI - diff --git a/plugins/aiera/assets/logo.png b/plugins/aiera/assets/logo.png deleted file mode 100644 index dafb46afe..000000000 Binary files a/plugins/aiera/assets/logo.png and /dev/null differ diff --git a/plugins/airtable/.app.json b/plugins/airtable/.app.json index c5750b57d..082735db4 100644 --- a/plugins/airtable/.app.json +++ b/plugins/airtable/.app.json @@ -1,8 +1,7 @@ { "apps": { "airtable": { - "id": "asdk_app_693ca6ce2db08191bb52d66743c65184", - "required": false + "id": "asdk_app_693ca6ce2db08191bb52d66743c65184" } } } diff --git a/plugins/airtable/.codex-plugin/plugin.json b/plugins/airtable/.codex-plugin/plugin.json index 2ec861894..a2acbf9a4 100644 --- a/plugins/airtable/.codex-plugin/plugin.json +++ b/plugins/airtable/.codex-plugin/plugin.json @@ -1,69 +1,25 @@ { - "name": "airtable", - "version": "0.1.3", - "description": "Airtable is the database and operations layer for your agents — whether running product, marketing, sales, ops, HR, or a custom business app. It combines structured data with multiplayer visual surfaces (grid, kanban, calendar, gallery, timeline) humans and agents share — plus sync integrations to Jira, Salesforce, Zendesk, Google Drive, Databricks, and the rest of your stack, all backed by enterprise governance. This plugin makes Codex fluent in Airtable: creating bases and schema, working with records, and sharing UI for collaboration. Uses the Airtable app connector.", + "apps": "./.app.json", "author": { - "name": "Airtable", - "url": "https://www.airtable.com" + "name": "Airtable" }, - "homepage": "https://www.airtable.com", - "repository": "https://github.com/airtable/skills", - "license": "MIT", - "keywords": [ - "airtable", - "database", - "relational-database", - "application-database", - "data-store", - "persistence", - "crud", - "product", - "product-ops", - "crm", - "sales", - "marketing", - "operations", - "hr", - "hiring", - "project-management", - "roadmap", - "customer-success", - "nocode", - "low-code", - "spreadsheet", - "collaboration", - "real-time", - "governance", - "workflow", - "automation", - "internal-tools", - "mcp", - "content" - ], - "skills": "./skills/", - "apps": "./.app.json", + "description": "Bring operational data and context into the flow of your ChatGPT conversations. You can ask questions, create and update records, and analyze your data\u2014all through conversation. Use the data in Airtable as input to the work you\u2019re doing in ChatGPT, like building a landing page using content you\u2019ve organized in Airtable. Make quick updates to Airtable without leaving the chat. Airtable for ChatGPT is ideal anytime you need quick access to structured internal data to inform your conversation.\n\nAirtable's App connects ChatGPT directly to your Airtable bases, so you can ask questions, create and update records, and analyze your data\u2014all through conversation", "interface": { - "displayName": "Airtable", - "shortDescription": "Database and operations layer for your agents.", - "longDescription": "Airtable is the database and operations layer for your agents — whether running product, marketing, sales, ops, HR, or a custom business app. It combines structured data with multiplayer visual surfaces (grid, kanban, calendar, gallery, timeline) humans and agents share — plus sync integrations to Jira, Salesforce, Zendesk, Google Drive, Databricks, and the rest of your stack, all backed by enterprise governance. This plugin makes Codex fluent in your Airtable bases: creating bases and schema, working with records, and sharing UI for collaboration. Uses the Airtable app connector.", - "developerName": "Airtable", + "capabilities": [], "category": "Productivity", - "capabilities": [ - "Read", - "Write" - ], - "websiteURL": "https://www.airtable.com", - "privacyPolicyURL": "https://www.airtable.com/privacy", - "termsOfServiceURL": "https://www.airtable.com/company/tos", "defaultPrompt": [ - "Set up a system to manage my team's operations.", - "Track my product roadmap, feedback, and releases.", - "I need a simple database for my project." + "show me a kanban of the product roadmap. What\u2019s at risk? What\u2019s up next?", + "which roadmap initiatives should I discuss with the enterprise sales team today?" ], - "brandColor": "#18BFFF", - "composerIcon": "./assets/icon.svg", - "logo": "./assets/logo.png", - "logoDark": "./assets/logo-dark.png", - "screenshots": [] - } + "developerName": "Airtable", + "displayName": "Airtable", + "longDescription": "Bring operational data and context into the flow of your ChatGPT conversations. You can ask questions, create and update records, and analyze your data\u2014all through conversation. Use the data in Airtable as input to the work you\u2019re doing in ChatGPT, like building a landing page using content you\u2019ve organized in Airtable. Make quick updates to Airtable without leaving the chat. Airtable for ChatGPT is ideal anytime you need quick access to structured internal data to inform your conversation.\n\nAirtable's App connects ChatGPT directly to your Airtable bases, so you can ask questions, create and update records, and analyze your data\u2014all through conversation", + "privacyPolicyURL": "https://www.airtable.com/company/privacy", + "shortDescription": "Add structured data to ChatGPT", + "supportURL": "https://support.airtable.com/docs/contacting-airtable-support", + "termsOfServiceURL": "https://www.airtable.com/company/tos", + "websiteURL": "https://airtable.com" + }, + "name": "airtable", + "version": "6.0.1" } diff --git a/plugins/airtable/agents/openai.yaml b/plugins/airtable/agents/openai.yaml deleted file mode 100644 index d13ba87a5..000000000 --- a/plugins/airtable/agents/openai.yaml +++ /dev/null @@ -1,6 +0,0 @@ -interface: - display_name: "Airtable" - short_description: "Database and operations layer for your agents" - icon_small: "./assets/icon.svg" - icon_large: "./assets/logo.png" - default_prompt: "Use Airtable to create bases, manage records, and organize structured workflows." diff --git a/plugins/airtable/assets/icon.svg b/plugins/airtable/assets/icon.svg deleted file mode 100644 index 70dd0037e..000000000 --- a/plugins/airtable/assets/icon.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/plugins/airtable/assets/logo-dark.png b/plugins/airtable/assets/logo-dark.png deleted file mode 100644 index 0f3660518..000000000 Binary files a/plugins/airtable/assets/logo-dark.png and /dev/null differ diff --git a/plugins/airtable/assets/logo.png b/plugins/airtable/assets/logo.png deleted file mode 100644 index 7f40cbb53..000000000 Binary files a/plugins/airtable/assets/logo.png and /dev/null differ diff --git a/plugins/alation/.app.json b/plugins/alation/.app.json deleted file mode 100644 index 8e3f9dc34..000000000 --- a/plugins/alation/.app.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "apps": { - "alation": { - "id": "asdk_app_6a0f9ab98bf4819197de479522d5367b" - } - } -} diff --git a/plugins/alation/.codex-plugin/plugin.json b/plugins/alation/.codex-plugin/plugin.json deleted file mode 100644 index 24cfca8fc..000000000 --- a/plugins/alation/.codex-plugin/plugin.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "alation", - "version": "1.0.3", - "description": "Trusted enterprise data context and governance", - "author": { - "name": "Alation", - "url": "https://www.alation.com" - }, - "homepage": "https://www.alation.com", - "repository": "https://github.com/openai/plugins", - "license": "MIT", - "keywords": [], - "apps": "./.app.json", - "interface": { - "displayName": "Alation", - "shortDescription": "Trusted enterprise data context and governance", - "longDescription": "Alation brings trusted enterprise data context into ChatGPT.\n\nConnect ChatGPT to Alation’s enterprise data catalog, governance, and trusted business context so users can discover, understand, and use data with confidence.\n\nThe Alation Intelligence Operating System (AIOS) helps AI ground responses in trusted enterprise context, including catalog metadata, governance policies, semantic definitions, lineage, data quality, and documentation. The app is designed for enterprise teams that need AI-led data discovery without losing trust, context, or governance.", - "developerName": "Alation", - "category": "Data & Analytics", - "capabilities": [], - "websiteURL": "https://www.alation.com", - "defaultPrompt": [ - "Search Alation for data assets related to this metric and summarize owners and definitions.", - "Find certified Alation tables for this analysis and explain lineage, quality, and caveats.", - "Review Alation documentation for this dataset and draft a query-ready data brief." - ], - "brandColor": "#FF7A1A", - "composerIcon": "./assets/logo.png", - "logo": "./assets/logo.png", - "screenshots": [] - } -} diff --git a/plugins/alation/assets/logo.png b/plugins/alation/assets/logo.png deleted file mode 100644 index abf4845da..000000000 Binary files a/plugins/alation/assets/logo.png and /dev/null differ diff --git a/plugins/alpaca/.app.json b/plugins/alpaca/.app.json deleted file mode 100644 index a8cfadda1..000000000 --- a/plugins/alpaca/.app.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "apps": { - "alpaca": { - "id": "connector_691f721a77bc8191be115b65c85075c0" - } - } -} diff --git a/plugins/alpaca/.codex-plugin/plugin.json b/plugins/alpaca/.codex-plugin/plugin.json deleted file mode 100644 index e4fc68c48..000000000 --- a/plugins/alpaca/.codex-plugin/plugin.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "alpaca", - "version": "1.0.2", - "description": "Stop watching the markets.", - "author": { - "url": "https://alpaca.markets/", - "name": "Alpaca" - }, - "homepage": "https://alpaca.markets/", - "repository": "https://github.com/openai/plugins", - "license": "MIT", - "keywords": [], - "apps": "./.app.json", - "interface": { - "displayName": "Alpaca", - "shortDescription": "Stop watching the markets.", - "longDescription": "Stop watching the markets. Turn your words into insights with Alpaca.\nAsk Codex real market questions and get live answers you can act on, including historical data, snapshots, quotes, and option chain information. Stock, options, and crypto market data embedded right into your conversation so you can turn analysis into automated actions.\n\"How has AAPL's stock performed this quarter vs GOOG?\"\n\"Show me the SPY option chain that expires next Friday.\"\n\"When has BTC's price reached $100k USD this year?\"", - "category": "Finance", - "capabilities": [], - "websiteURL": "https://alpaca.markets/", - "privacyPolicyURL": "https://s3.amazonaws.com/files.alpaca.markets/disclosures/PrivacyPolicy.pdf", - "defaultPrompt": [ - "Use Alpaca to help with this task" - ], - "screenshots": [], - "composerIcon": "./assets/app-icon.png", - "logo": "./assets/app-icon.png", - "developerName": "Alpaca" - } -} diff --git a/plugins/alpaca/assets/app-icon.png b/plugins/alpaca/assets/app-icon.png deleted file mode 100644 index e57f5c784..000000000 Binary files a/plugins/alpaca/assets/app-icon.png and /dev/null differ diff --git a/plugins/amplitude/.app.json b/plugins/amplitude/.app.json deleted file mode 100644 index 07b6ab1c3..000000000 --- a/plugins/amplitude/.app.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "apps": { - "amplitude": { - "id": "connector_690e2dabf430819196f8b3701ec838ec" - } - } -} diff --git a/plugins/amplitude/.codex-plugin/plugin.json b/plugins/amplitude/.codex-plugin/plugin.json deleted file mode 100644 index ad275566f..000000000 --- a/plugins/amplitude/.codex-plugin/plugin.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "amplitude", - "version": "1.0.2", - "description": "Product analytics and funnels", - "author": { - "name": "Amplitude" - }, - "repository": "https://github.com/openai/plugins", - "license": "MIT", - "keywords": [], - "apps": "./.app.json", - "interface": { - "displayName": "Amplitude", - "shortDescription": "Product analytics and funnels", - "longDescription": "Product analytics and funnels", - "developerName": "Amplitude", - "category": "Data & Analytics", - "capabilities": [], - "defaultPrompt": [ - "Show the signup funnel trend in Amplitude" - ], - "screenshots": [], - "composerIcon": "./assets/app-icon.png", - "logo": "./assets/app-icon.png" - } -} diff --git a/plugins/amplitude/assets/app-icon.png b/plugins/amplitude/assets/app-icon.png deleted file mode 100644 index 14887f91b..000000000 Binary files a/plugins/amplitude/assets/app-icon.png and /dev/null differ diff --git a/plugins/apollo/.app.json b/plugins/apollo/.app.json deleted file mode 100644 index c7ead65e7..000000000 --- a/plugins/apollo/.app.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "apps": { - "apollo": { - "id": "asdk_app_69bd664f2a908191a3a0a47eca8559d1" - } - } -} diff --git a/plugins/apollo/.codex-plugin/plugin.json b/plugins/apollo/.codex-plugin/plugin.json deleted file mode 100644 index 68c85126e..000000000 --- a/plugins/apollo/.codex-plugin/plugin.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "apollo", - "version": "1.0.2", - "description": "Prospecting and outbound execution in Apollo", - "author": { - "name": "Apollo", - "url": "https://www.apollo.io" - }, - "repository": "https://github.com/openai/plugins", - "license": "MIT", - "keywords": [], - "apps": "./.app.json", - "interface": { - "displayName": "Apollo", - "developerName": "Apollo", - "shortDescription": "Prospecting and outbound execution in Apollo", - "longDescription": "Apollo’s MCP connector lets ChatGPT safely work inside your Apollo workspace to accelerate prospecting and outbound execution. Search and qualify accounts and contacts using Apollo’s data, enrich records, summarize key context, and take action (e.g., create lists, log notes, generate tasks, and draft or assemble outbound using sequences) using your team’s rules and permissions. Designed for speed and control: ChatGPT can pull only what you ask for, respect workspace access, and produce outputs that are ready to review, personalize, and ship.", - "category": "Business & Operations", - "capabilities": [], - "brandColor": "#E8F028", - "defaultPrompt": [ - "Find verified contacts matching this ICP in Apollo and rank the best prospects.", - "Research a company in Apollo and identify decision makers for this use case.", - "Build an outreach list for this industry with recent growth or hiring signals." - ], - "screenshots": [], - "composerIcon": "./assets/logo.png", - "logo": "./assets/logo.png", - "websiteURL": "https://www.apollo.io" - }, - "homepage": "https://www.apollo.io" -} diff --git a/plugins/apollo/assets/app-icon.svg b/plugins/apollo/assets/app-icon.svg deleted file mode 100644 index 9c7851b38..000000000 --- a/plugins/apollo/assets/app-icon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - AP - diff --git a/plugins/apollo/assets/logo.png b/plugins/apollo/assets/logo.png deleted file mode 100644 index 898c171ae..000000000 Binary files a/plugins/apollo/assets/logo.png and /dev/null differ diff --git a/plugins/asana/.app.json b/plugins/asana/.app.json deleted file mode 100644 index e0b53ac13..000000000 --- a/plugins/asana/.app.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "apps": { - "asana": { - "id": "asdk_app_69616780bd208191b4fb44ba44f72b61", - "required": false - } - } -} diff --git a/plugins/asana/.codex-plugin/plugin.json b/plugins/asana/.codex-plugin/plugin.json deleted file mode 100644 index d121dc523..000000000 --- a/plugins/asana/.codex-plugin/plugin.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "asana", - "version": "0.1.4", - "description": "Work with your Asana tasks, subtasks, comments, due dates, and project details to create summaries, understand priorities, and prepare clear status updates.", - "author": { - "name": "Asana, Inc.", - "url": "https://asana.com" - }, - "homepage": "https://asana.com", - "repository": "https://github.com/openai/plugins", - "license": "MIT", - "keywords": [ - "asana", - "tasks", - "project-management", - "productivity", - "collaboration", - "work-management" - ], - "apps": "./.app.json", - "interface": { - "displayName": "Asana", - "shortDescription": "Read and manage Asana", - "longDescription": "Work with your Asana tasks, subtasks, comments, due dates, and project details to create summaries, understand priorities, and prepare clear status updates.", - "developerName": "Asana, Inc.", - "category": "Productivity", - "capabilities": [ - "Interactive", - "Write" - ], - "websiteURL": "https://asana.com", - "privacyPolicyURL": "https://asana.com/terms/privacy-statement", - "termsOfServiceURL": "https://asana.com/terms", - "defaultPrompt": [ - "Create a project to track creative requests", - "Create a task to track follow up actions", - "Show me what's on my plate in Asana today." - ], - "brandColor": "#FF584A", - "composerIcon": "./assets/logo.png", - "logo": "./assets/logo.png", - "screenshots": [] - } -} diff --git a/plugins/asana/assets/logo.png b/plugins/asana/assets/logo.png deleted file mode 100644 index c5c503a46..000000000 Binary files a/plugins/asana/assets/logo.png and /dev/null differ diff --git a/plugins/atlassian-rovo/.app.json b/plugins/atlassian-rovo/.app.json index f1c074724..eda6cbdab 100644 --- a/plugins/atlassian-rovo/.app.json +++ b/plugins/atlassian-rovo/.app.json @@ -1,8 +1,7 @@ { "apps": { "atlassian-rovo": { - "id": "connector_692de805e3ec8191834719067174a384", - "required": false + "id": "connector_692de805e3ec8191834719067174a384" } } } diff --git a/plugins/atlassian-rovo/.codex-plugin/plugin.json b/plugins/atlassian-rovo/.codex-plugin/plugin.json index b4e4289ee..d4984c67b 100644 --- a/plugins/atlassian-rovo/.codex-plugin/plugin.json +++ b/plugins/atlassian-rovo/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "atlassian-rovo", - "version": "1.0.3", + "version": "1.0.6", "description": "Manage Jira and Confluence fast", "author": { "name": "Atlassian", @@ -10,26 +10,26 @@ "repository": "https://github.com/openai/plugins", "license": "MIT", "keywords": [ - "atlassian" + "atlassian", + "rovo", + "jira", + "confluence", + "issue tracking", + "project management", + "knowledge base" ], - "skills": "./skills/", "apps": "./.app.json", "interface": { "displayName": "Atlassian Rovo", - "shortDescription": "Manage Jira and Confluence fast", + "shortDescription": "Manage Jira and Confluence", "longDescription": "Manage Jira and Confluence fast", "developerName": "Atlassian", "category": "Productivity", - "capabilities": [ - "Interactive", - "Write" - ], + "capabilities": ["Interactive", "Write"], "websiteURL": "https://www.atlassian.com", "privacyPolicyURL": "https://www.atlassian.com/legal/privacy-policy", "termsOfServiceURL": "https://www.atlassian.com/legal/cloud-terms-of-service", - "defaultPrompt": [ - "Create Jira tasks in project Vita MVP" - ], + "defaultPrompt": ["Create Jira tasks in project Vita MVP"], "screenshots": [], "brandColor": "#0052CC", "composerIcon": "./assets/app-icon.png", diff --git a/plugins/atlassian-rovo/skills/capture-tasks-from-meeting-notes/SKILL.md b/plugins/atlassian-rovo/skills/capture-tasks-from-meeting-notes/SKILL.md deleted file mode 100644 index 9bd7560a8..000000000 --- a/plugins/atlassian-rovo/skills/capture-tasks-from-meeting-notes/SKILL.md +++ /dev/null @@ -1,679 +0,0 @@ ---- -name: capture-tasks-from-meeting-notes -description: "Analyze meeting notes to find action items and create Jira tasks for assigned work. When an agent needs to: (1) Create Jira tasks or tickets from meeting notes, (2) Extract or find action items from notes or Confluence pages, (3) Parse meeting notes for assigned tasks, or (4) Analyze notes and generate tasks for team members. Identifies assignees, looks up account IDs, and creates tasks with proper context." ---- - -# Capture Tasks from Meeting Notes - -## Keywords -meeting notes, action items, create tasks, create tickets, extract tasks, parse notes, analyze notes, assigned work, assignees, from meeting, post-meeting, capture tasks, generate tasks, turn into tasks, convert to tasks, action item, to-do, task list, follow-up, assigned to, create Jira tasks, create Jira tickets, meeting action items, extract action items, find action items, analyze meeting - -## Overview - -Automatically extract action items from meeting notes and create Jira tasks with proper assignees. This skill parses unstructured meeting notes (from Confluence or pasted text), identifies action items with assignees, looks up Jira account IDs, and creates tasks—eliminating the tedious post-meeting ticket creation process. - -**Use this skill when:** Users have meeting notes with action items that need to become Jira tasks. - ---- - -## Workflow - -Follow this 7-step process to turn meeting notes into actionable Jira tasks: - -### Step 1: Get Meeting Notes - -Obtain the meeting notes from the user. - -#### Option A: Confluence Page URL - -If user provides a Confluence URL: - -``` -getConfluencePage( - cloudId="...", - pageId="[extracted from URL]", - contentFormat="markdown" -) -``` - -**URL patterns:** -- `https://[site].atlassian.net/wiki/spaces/[SPACE]/pages/[PAGE_ID]/[title]` -- Extract PAGE_ID from the numeric portion -- Get cloudId from site name or use `getAccessibleAtlassianResources` - -#### Option B: Pasted Text - -If user pastes meeting notes directly: -- Use the text as-is -- No fetching needed - -#### If Unclear - -Ask: "Do you have a Confluence link to the meeting notes, or would you like to paste them directly?" - ---- - -### Step 2: Parse Action Items - -Scan the notes for action items with assignees. - -#### Common Patterns - -**Pattern 1: @mention format** (highest priority) -``` -@Sarah to create user stories for chat feature -@Mike will update architecture doc -``` - -**Pattern 2: Name + action verb** -``` -Sarah to create user stories -Mike will update architecture doc -Lisa should review the mockups -``` - -**Pattern 3: Action: Name - Task** -``` -Action: Sarah - create user stories -Action Item: Mike - update architecture -``` - -**Pattern 4: TODO with assignee** -``` -TODO: Create user stories (Sarah) -TODO: Update docs - Mike -``` - -**Pattern 5: Bullet with name** -``` -- Sarah: create user stories -- Mike - update architecture -``` - -#### Extraction Logic - -**For each action item, extract:** - -1. **Assignee Name** - - Text after @ symbol - - Name before "to", "will", "should" - - Name after "Action:" or in parentheses - - First/last name or full name - -2. **Task Description** - - Text after "to", "will", "should", "-", ":" - - Remove markers (@, Action:, TODO:) - - Keep original wording - - Include enough context - -3. **Context** (optional but helpful) - - Meeting title/date if available - - Surrounding discussion context - - Related decisions - -#### Example Parsing - -**Input:** -``` -# Product Planning - Dec 3 - -Action Items: -- @Sarah to create user stories for chat feature -- Mike will update the architecture doc -- Lisa: review and approve design mockups -``` - -**Parsed:** -``` -1. Assignee: Sarah - Task: Create user stories for chat feature - Context: Product Planning meeting - Dec 3 - -2. Assignee: Mike - Task: Update the architecture doc - Context: Product Planning meeting - Dec 3 - -3. Assignee: Lisa - Task: Review and approve design mockups - Context: Product Planning meeting - Dec 3 -``` - ---- - -### Step 3: Ask for Project Key - -Before looking up users or creating tasks, identify the Jira project. - -**Ask:** "Which Jira project should I create these tasks in? (e.g., PROJ, PRODUCT, ENG)" - -#### If User is Unsure - -Call `getVisibleJiraProjects` to show options: - -``` -getVisibleJiraProjects( - cloudId="...", - action="create" -) -``` - -Present: "I found these projects you can create tasks in: PROJ (Project Alpha), PRODUCT (Product Team), ENG (Engineering)" - ---- - -### Step 4: Lookup Account IDs - -For each assignee name, find their Jira account ID. - -#### Lookup Process - -``` -lookupJiraAccountId( - cloudId="...", - searchString="[assignee name]" -) -``` - -**The search string can be:** -- Full name: "Sarah Johnson" -- First name: "Sarah" -- Last name: "Johnson" -- Email: "sarah@company.com" - -#### Handle Results - -**Scenario A: Exact Match (1 result)** -``` -✅ Found: Sarah Johnson (sarah.johnson@company.com) -→ Use accountId from result -``` - -**Scenario B: No Match (0 results)** -``` -⚠️ Couldn't find user "Sarah" in Jira. - -Options: -1. Create task unassigned (assign manually later) -2. Skip this task -3. Try different name format (e.g., "Sarah Johnson") - -Which would you prefer? -``` - -**Scenario C: Multiple Matches (2+ results)** -``` -⚠️ Found multiple users named "Sarah": -1. Sarah Johnson (sarah.johnson@company.com) -2. Sarah Smith (sarah.smith@company.com) - -Which user should be assigned the task "Create user stories"? -``` - -#### Best Practices - -- Try full name first ("Sarah Johnson") -- If no match, try first name only ("Sarah") -- If still no match, ask user -- Cache results (don't lookup same person twice) - ---- - -### Step 5: Present Action Items - -**CRITICAL:** Always show the parsed action items to the user BEFORE creating any tasks. - -#### Presentation Format - -``` -I found [N] action items from the meeting notes. Should I create these Jira tasks in [PROJECT]? - -1. [TASK] [Task description] - Assigned to: [Name] ([email if found]) - Context: [Meeting title/date] - -2. [TASK] [Task description] - Assigned to: [Name] ([email if found]) - Context: [Meeting title/date] - -[...continue for all tasks...] - -Would you like me to: -1. Create all tasks -2. Skip some tasks (which ones?) -3. Modify any descriptions or assignees -``` - -#### Wait for Confirmation - -Do NOT create tasks until user confirms. Options: -- "Yes, create all" → proceed -- "Skip task 3" → create all except #3 -- "Change assignee for task 2" → ask for new assignee -- "Edit description" → ask for changes - ---- - -### Step 6: Create Tasks - -Once confirmed, create each Jira task. - -#### Determine Issue Type - -Before creating tasks, check what issue types are available in the project: - -``` -getJiraProjectIssueTypesMetadata( - cloudId="...", - projectIdOrKey="PROJ" -) -``` - -**Choose the appropriate issue type:** -- Use "Task" if available (most common) -- Use "Story" for user-facing features -- Use "Bug" if it's a defect -- If "Task" doesn't exist, use the first available issue type or ask the user - -#### For Each Action Item - -``` -createJiraIssue( - cloudId="...", - projectKey="PROJ", - issueTypeName="[Task or available type]", - summary="[Task description]", - description="[Full description with context]", - assignee_account_id="[looked up account ID]" -) -``` - -#### Task Summary Format - -Use action verbs and be specific: -- ✅ "Create user stories for chat feature" -- ✅ "Update architecture documentation" -- ✅ "Review and approve design mockups" -- ❌ "Do the thing" (too vague) - -#### Task Description Format - -```markdown -**Action Item from Meeting Notes** - -**Task:** [Original action item text] - -**Context:** -[Meeting title/date] -[Relevant discussion points or decisions] - -**Source:** [Link to Confluence meeting notes if available] - -**Original Note:** -> [Exact quote from meeting notes] -``` - -**Example:** -```markdown -**Action Item from Meeting Notes** - -**Task:** Create user stories for chat feature - -**Context:** -Product Planning Meeting - December 3, 2025 -Discussed Q1 roadmap priorities and new feature requirements - -**Source:** https://yoursite.atlassian.net/wiki/spaces/TEAM/pages/12345 - -**Original Note:** -> @Sarah to create user stories for chat feature -``` - ---- - -### Step 7: Provide Summary - -After all tasks are created, present a comprehensive summary. - -**Format:** -``` -✅ Created [N] tasks in [PROJECT]: - -1. [PROJ-123] - [Task summary] - Assigned to: [Name] - https://yoursite.atlassian.net/browse/PROJ-123 - -2. [PROJ-124] - [Task summary] - Assigned to: [Name] - https://yoursite.atlassian.net/browse/PROJ-124 - -[...continue for all created tasks...] - -**Source:** [Link to meeting notes] - -**Next Steps:** -- Review tasks in Jira for accuracy -- Add any additional details or attachments -- Adjust priorities if needed -- Link related tickets if applicable -``` - ---- - -## Action Item Pattern Examples - -### Pattern 1: @Mentions (Most Explicit) - -``` -@john to update documentation -@sarah will create the report -@mike should review PR #123 -``` - -**Parsed:** -- Assignee: john/sarah/mike -- Task: update documentation / create the report / review PR #123 - ---- - -### Pattern 2: Name + Action Verb - -``` -John to update documentation -Sarah will create the report -Mike should review PR #123 -Lisa needs to test the feature -``` - -**Parsed:** -- Assignee: name before action verb -- Task: text after "to/will/should/needs to" - ---- - -### Pattern 3: Structured Action Format - -``` -Action: John - update documentation -Action Item: Sarah - create the report -AI: Mike - review PR #123 -``` - -**Parsed:** -- Assignee: name after "Action:" and before "-" -- Task: text after "-" - ---- - -### Pattern 4: TODO Format - -``` -TODO: Update documentation (John) -TODO: Create report - Sarah -[ ] Mike: review PR #123 -``` - -**Parsed:** -- Assignee: name in parentheses or after ":" -- Task: text between TODO and assignee - ---- - -### Pattern 5: Bullet Lists - -``` -- John: update documentation -- Sarah - create the report -* Mike will review PR #123 -``` - -**Parsed:** -- Assignee: name before ":" or "-" or action verb -- Task: remaining text - ---- - -## Handling Edge Cases - -### No Action Items Found - -If no action items with assignees are detected: - -``` -I analyzed the meeting notes but couldn't find any action items with clear assignees. - -Action items typically follow patterns like: -- @Name to do X -- Name will do X -- Action: Name - do X -- TODO: X (Name) - -Options: -1. I can search for TODO items without assignees -2. You can point out specific action items to create -3. I can create tasks for bullet points you specify - -What would you like to do? -``` - ---- - -### Mixed Formats - -If some action items have assignees and some don't: - -``` -I found [N] action items: -- [X] with clear assignees -- [Y] without assignees - -Should I: -1. Create all [N] tasks ([X] assigned, [Y] unassigned) -2. Only create the [X] tasks with assignees -3. Ask you to assign the [Y] unassigned tasks - -Which option would you prefer? -``` - ---- - -### Assignee Name Variations - -If the same person is mentioned different ways: - -``` -Notes mention: @sarah, Sarah, Sarah J. - -These likely refer to the same person. I'll look up "Sarah" once and use -that account ID for all three mentions. Is that correct? -``` - ---- - -### Duplicate Action Items - -If the same task appears multiple times: - -``` -I found what appears to be the same action item twice: -1. "@Sarah to create user stories" (line 15) -2. "Action: Sarah - create user stories" (line 42) - -Should I: -1. Create one task (combine duplicates) -2. Create two separate tasks -3. Skip the duplicate - -What would you prefer? -``` - ---- - -### Long Task Descriptions - -If action item text is very long (>200 characters): - -``` -The task "[long text...]" is quite detailed. - -Should I: -1. Use first sentence as summary, rest in description -2. Use full text as summary -3. Let you edit it to be more concise - -Which would you prefer? -``` - ---- - -## Tips for High-Quality Results - -### Do: -✅ Use consistent @mention format in notes -✅ Include full names when possible -✅ Be specific in action item descriptions -✅ Add context (why/what/when) -✅ Review parsed tasks before confirming - -### Don't: -❌ Mix multiple tasks for one person in one bullet -❌ Use ambiguous names (just "John" if you have 5 Johns) -❌ Skip action verbs (unclear what to do) -❌ Forget to specify project - -### Best Meeting Notes Format - -``` -# Meeting Title - Date - -Attendees: [Names] - -## Decisions -[What was decided] - -## Action Items -- @FullName to [specific task with context] -- @AnotherPerson will [specific task with context] -- etc. -``` - ---- - -## When NOT to Use This Skill - -This skill is for **converting meeting action items to Jira tasks only**. - -**Don't use for:** -❌ Summarizing meetings (no task creation) -❌ Finding meeting notes (use search skill) -❌ Creating calendar events -❌ Sending meeting notes via email -❌ General note-taking - -**Use only when:** Meeting notes exist and action items need to become Jira tasks. - ---- - -## Examples - -### Example 1: Simple @Mentions - -**Input:** -``` -Team Sync - Dec 3, 2025 - -Action Items: -- @Sarah to create user stories for chat feature -- @Mike will update the architecture doc -- @Lisa should review design mockups -``` - -**Process:** -1. Parse → 3 action items found -2. Project → "PROJ" -3. Lookup → Sarah (123), Mike (456), Lisa (789) -4. Present → User confirms -5. Create → PROJ-100, PROJ-101, PROJ-102 - -**Output:** -``` -✅ Created 3 tasks in PROJ: - -1. PROJ-100 - Create user stories for chat feature - Assigned to: Sarah Johnson - -2. PROJ-101 - Update the architecture doc - Assigned to: Mike Chen - -3. PROJ-102 - Review design mockups - Assigned to: Lisa Park -``` - ---- - -### Example 2: Mixed Formats - -**Input:** -``` -Product Review Meeting - -Discussed new features and priorities. - -Follow-ups: -- Sarah will draft the PRD -- Mike: implement API changes -- TODO: Review security audit (Lisa) -- Update stakeholders on timeline -``` - -**Process:** -1. Parse → Found 4 items (3 with assignees, 1 without) -2. Ask → "Found 3 with assignees, 1 without. Create all or only assigned?" -3. User → "All, make the last one unassigned" -4. Create → 4 tasks (3 assigned, 1 unassigned) - ---- - -### Example 3: Name Lookup Issue - -**Input:** -``` -Sprint Planning - -Action Items: -- @John to update tests -- @Sarah to refactor code -``` - -**Process:** -1. Parse → 2 action items -2. Lookup "John" → Found 3 Johns! -3. Ask → "Which John? (John Smith, John Doe, John Wilson)" -4. User → "John Smith" -5. Create → Both tasks assigned correctly - ---- - -## Quick Reference - -**Primary tool:** `getConfluencePage` (if URL) or use pasted text -**Account lookup:** `lookupJiraAccountId(searchString)` -**Task creation:** `createJiraIssue` with `assignee_account_id` - -**Action patterns to look for:** -- `@Name to/will/should X` -- `Name to/will/should X` -- `Action: Name - X` -- `TODO: X (Name)` -- `Name: X` - -**Always:** -- Present parsed tasks before creating -- Handle name lookup failures gracefully -- Include context in task descriptions -- Provide summary with links - -**Remember:** -- Human-in-loop is critical (show before creating) -- Name lookup can fail (have fallback) -- Be flexible with pattern matching -- Context preservation is important diff --git a/plugins/atlassian-rovo/skills/capture-tasks-from-meeting-notes/agents/openai.yaml b/plugins/atlassian-rovo/skills/capture-tasks-from-meeting-notes/agents/openai.yaml deleted file mode 100644 index 3c0acf5ed..000000000 --- a/plugins/atlassian-rovo/skills/capture-tasks-from-meeting-notes/agents/openai.yaml +++ /dev/null @@ -1,3 +0,0 @@ -interface: - display_name: "Capture Tasks From Meeting Notes" - short_description: "Extract action items and create Jira tasks" diff --git a/plugins/atlassian-rovo/skills/capture-tasks-from-meeting-notes/references/action-item-patterns.md b/plugins/atlassian-rovo/skills/capture-tasks-from-meeting-notes/references/action-item-patterns.md deleted file mode 100644 index 02cbaf083..000000000 --- a/plugins/atlassian-rovo/skills/capture-tasks-from-meeting-notes/references/action-item-patterns.md +++ /dev/null @@ -1,445 +0,0 @@ -# Action Item Patterns Reference - -Common patterns found in meeting notes and how to parse them. - ---- - -## Pattern Categories - -### Category 1: @Mentions (Highest Confidence) - -**Format:** `@Name [action verb] [task]` - -**Examples:** -``` -@john to update documentation -@sarah will create the report -@mike should review PR #123 -@lisa needs to test the feature -``` - -**Parsing:** -- Assignee: Text immediately after @ -- Task: Everything after action verb (to/will/should/needs to) -- Confidence: Very High (explicit assignment) - ---- - -### Category 2: Name + Action Verb (High Confidence) - -**Format:** `Name [action verb] [task]` - -**Examples:** -``` -John to update documentation -Sarah will create the report -Mike should review PR #123 -Lisa needs to test the feature -``` - -**Parsing:** -- Assignee: First word(s) before action verb -- Task: Everything after action verb -- Confidence: High (clear structure) - -**Action verbs to detect:** -- to, will, should, needs to, must, has to, is to, going to - ---- - -### Category 3: Structured Action Format (High Confidence) - -**Format:** `Action: Name - [task]` or `AI: Name - [task]` - -**Examples:** -``` -Action: John - update documentation -Action Item: Sarah - create the report -AI: Mike - review PR #123 -Task: Lisa - test the feature -``` - -**Parsing:** -- Assignee: Between "Action:" and "-" -- Task: After "-" -- Confidence: High (structured format) - -**Variants:** -- Action: -- Action Item: -- AI: -- Task: -- Assigned: - ---- - -### Category 4: TODO Format (Medium Confidence) - -**Format:** `TODO: [task] (Name)` or `TODO: [task] - Name` - -**Examples:** -``` -TODO: Update documentation (John) -TODO: Create report - Sarah -[ ] Review PR #123 (Mike) -- [ ] Test feature - Lisa -``` - -**Parsing:** -- Assignee: In parentheses or after "-" -- Task: Between TODO and assignee -- Confidence: Medium (format varies) - -**Markers to detect:** -- TODO: -- [ ] -- - [ ] -- To-do: -- Action item: - ---- - -### Category 5: Colon or Dash Format (Medium Confidence) - -**Format:** `Name: [task]` or `Name - [task]` - -**Examples:** -``` -John: update documentation -Sarah - create the report -Mike: review PR #123 -Lisa - test the feature -``` - -**Parsing:** -- Assignee: Before ":" or "-" -- Task: After ":" or "-" -- Confidence: Medium (could be other uses of colons/dashes) - -**Detection:** -- Look for name-like word before ":" or "-" -- Followed by action verb or imperative -- Usually in bulleted lists - ---- - -## Complex Patterns - -### Multiple Assignees - -**Format:** `Name1 and Name2 to [task]` - -**Examples:** -``` -John and Sarah to update documentation -Mike, Lisa to review PR -``` - -**Handling:** -- Create separate tasks for each person -- OR create one task, ask user who should be assigned -- Include both names in description - ---- - -### Conditional Actions - -**Format:** `Name to [task] if [condition]` - -**Examples:** -``` -John to update docs if approved -Sarah will create report pending review -``` - -**Handling:** -- Include condition in task description -- Note that it's conditional -- User can adjust later - ---- - -### Time-Bound Actions - -**Format:** `Name to [task] by [date]` - -**Examples:** -``` -John to update docs by EOD -Sarah will finish report by Friday -Mike to review before next meeting -``` - -**Handling:** -- Extract deadline and add to task description -- Could use due date field if available -- Include urgency in task - ---- - -## Anti-Patterns (Not Action Items) - -### Discussion Notes - -**Not an action item:** -``` -John mentioned the documentation needs updating -Sarah suggested we create a report -Mike talked about reviewing the code -``` - -**Why:** These are discussions, not assignments - ---- - -### General Statements - -**Not an action item:** -``` -Documentation needs to be updated -Someone should create a report -The code requires review -``` - -**Why:** No specific assignee - ---- - -### Past Actions - -**Not an action item:** -``` -John updated the documentation -Sarah created the report -Mike reviewed the code -``` - -**Why:** Already completed (past tense) - ---- - -## Context Extraction - -### Meeting Metadata - -**Look for:** -``` -# [Meeting Title] - [Date] -Meeting: [Title] -Date: [Date] -Subject: [Title] -``` - -**Extract:** -- Meeting title -- Date -- Attendees (if listed) - ---- - -### Related Information - -**Look for:** -``` -Related to: [project/epic/initiative] -Context: [background info] -Decision: [relevant decision] -``` - -**Include in task:** -- Links to related work -- Background context -- Relevant decisions - ---- - -## Name Extraction Tips - -### Full Names - -**Preferred:** -``` -@Sarah Johnson to create report -Sarah Johnson will create report -``` - -**Extract:** "Sarah Johnson" - ---- - -### First Name Only - -**Common:** -``` -@Sarah to create report -Sarah will create report -``` - -**Extract:** "Sarah" (will need to lookup) - ---- - -### Nicknames or Short Forms - -**Handle carefully:** -``` -@SJ to create report -Sara (no h) will create report -``` - -**Strategy:** Ask user or try multiple lookups - ---- - -## Priority Indicators - -### Urgent/High Priority - -**Detect:** -``` -URGENT: John to update docs -HIGH PRIORITY: Sarah to create report -ASAP: Mike to review code -``` - -**Handling:** -- Note priority in task description -- Could set priority field -- Highlight in presentation - ---- - -### Low Priority - -**Detect:** -``` -If time: John to update docs -Nice to have: Sarah create report -Eventually: Mike review code -``` - -**Handling:** -- Note as lower priority -- Could defer creation -- User can decide - ---- - -## Confidence Scoring - -When parsing, assign confidence: - -**High Confidence (90%+):** -- @Mentions with clear action -- "Name to do X" format -- "Action: Name - X" format - -**Medium Confidence (60-90%):** -- Name: task format -- TODO with name -- Name without action verb but clear task - -**Low Confidence (<60%):** -- Ambiguous wording -- No clear assignee -- Could be discussion not action - -**Handling:** -- Present all to user -- Flag low-confidence items -- Let user confirm or skip - ---- - -## Special Cases - -### Group Actions - -``` -Everyone to review the document -Team to provide feedback -``` - -**Handling:** -- Ask user who specifically -- OR create one task unassigned -- Note it's for the whole team - ---- - -### Optional Actions - -``` -Sarah could create a report if needed -Mike might review the code -``` - -**Handling:** -- Flag as optional -- Ask user if should create -- Include "optional" in description - ---- - -### Delegated Actions - -``` -John will ask Sarah to create the report -``` - -**Handling:** -- Assign to Sarah (the actual doer) -- Note John is requestor -- Include context - ---- - -## Testing Patterns - -Use these to validate pattern matching: - -``` -✅ @john to update tests -✅ Sarah will write docs -✅ Mike: review code -✅ TODO: Deploy (Lisa) -✅ Action: John - fix bug - -⚠️ Maybe John can help? -⚠️ Documentation needs work -⚠️ We should test this - -❌ John mentioned testing -❌ Tests were updated -❌ Someone needs to deploy -``` - ---- - -## Regular Expression Examples - -**@Mention pattern:** -```regex -@(\w+)\s+(to|will|should)\s+(.+) -``` - -**Name + action verb:** -```regex -([A-Z][\w\s]+?)\s+(to|will|should)\s+(.+) -``` - -**Action format:** -```regex -Action:\s*([A-Z][\w\s]+?)\s*-\s*(.+) -``` - -**TODO format:** -```regex -TODO:\s*(.+)\s*\((\w+)\) -``` - -**Note:** These patterns use `[A-Z][\w\s]+?` to match names flexibly: -- Starts with a capital letter -- Matches one or more word characters or spaces -- Non-greedy (`+?`) to stop at action verbs -- Handles single names ("Sarah"), two-part names ("Sarah Johnson"), and longer names ("Mary Jane Smith") diff --git a/plugins/atlassian-rovo/skills/generate-status-report/SKILL.md b/plugins/atlassian-rovo/skills/generate-status-report/SKILL.md deleted file mode 100644 index b02e8b578..000000000 --- a/plugins/atlassian-rovo/skills/generate-status-report/SKILL.md +++ /dev/null @@ -1,335 +0,0 @@ ---- -name: generate-status-report -description: "Generate project status reports from Jira issues and publish to Confluence. When an agent needs to: (1) Create a status report for a project, (2) Summarize project progress or updates, (3) Generate weekly/daily reports from Jira, (4) Publish status summaries to Confluence, or (5) Analyze project blockers and completion. Queries Jira issues, categorizes by status/priority, and creates formatted reports for delivery managers and executives." ---- - -# Generate Status Report - -## Keywords -status report, project status, weekly update, daily standup, Jira report, project summary, blockers, progress update, Confluence report, sprint report, project update, publish to Confluence, write to Confluence, post report - -Automatically query Jira for project status, analyze issues, and generate formatted status reports published to Confluence. - -**CRITICAL**: This skill should be **interactive**. Always clarify scope (time period, audience, Confluence destination) with the user before or after generating the report. Do not silently skip Confluence publishing—always offer it. - -## Workflow - -Generating a status report follows these steps: - -1. **Identify scope** - Determine project, time period, and target audience -2. **Query Jira** - Fetch relevant issues using JQL queries -3. **Analyze data** - Categorize issues and identify key insights -4. **Format report** - Structure content based on audience and purpose -5. **Publish to Confluence** - Create or update a page with the report - -## Step 1: Identify Scope - -**IMPORTANT**: If the user's request is missing key information, ASK before proceeding with queries. Do not assume defaults without confirmation for Confluence publishing. - -Clarify these details: - -**Project identification:** -- Which Jira project key? (e.g., "PROJ", "ENG", "MKTG") -- If the user mentions a project by name but not key, search Jira to find the project key - -**Time period:** -- If not specified, ask: "What time period should this report cover? (default: last 7 days)" -- Options: Weekly (7 days), Daily (24 hours), Sprint-based (2 weeks), Custom period - -**Target audience:** -- If not specified, ask: "Who is this report for? (Executives/Delivery Managers, Team-level, or Daily standup)" -- **Executives/Delivery Managers**: High-level summary with key metrics and blockers -- **Team-level**: Detailed breakdown with issue-by-issue status -- **Daily standup**: Brief update on yesterday/today/blockers - -**Report destination:** -- **ALWAYS ASK** if not specified: "Would you like me to publish this report to Confluence? If so, which space should I use?" -- If user says yes: Ask for space name or offer to list available spaces -- Determine: New page or update existing page? -- Ask about parent page if creating under a specific section - -## Step 2: Query Jira - -Use the `searchJiraIssuesUsingJql` tool to fetch issues. Build JQL queries based on report needs. - -### Common Query Patterns - -For comprehensive queries, use the `scripts/jql_builder.py` utility to programmatically build JQL strings. For quick queries, reference `references/jql-patterns.md` for examples. - -**All open issues in project:** -```jql -project = "PROJECT_KEY" AND status != Done ORDER BY priority DESC, updated DESC -``` - -**Issues updated in last week:** -```jql -project = "PROJECT_KEY" AND updated >= -7d ORDER BY priority DESC -``` - -**High priority and blocked issues:** -```jql -project = "PROJECT_KEY" AND (priority IN (Highest, High) OR status = Blocked) AND status != Done ORDER BY priority DESC -``` - -**Completed in reporting period:** -```jql -project = "PROJECT_KEY" AND status = Done AND resolved >= -7d ORDER BY resolved DESC -``` - -### Query Strategy - -For most reports, execute multiple targeted queries rather than one large query: - -1. **Completed issues**: Get recently resolved tickets -2. **In-progress issues**: Get active work items -3. **Blocked issues**: Get blockers requiring attention -4. **High priority open**: Get critical upcoming work - -Use `maxResults: 100` for initial queries. If pagination is needed, use `nextPageToken` from results. - -### Data to Extract - -For each issue, capture: -- `key` (e.g., "PROJ-123") -- `summary` (issue title) -- `status` (current state) -- `priority` (importance level) -- `assignee` (who's working on it) -- `created` / `updated` / `resolved` dates -- `description` (if needed for context on blockers) - -## Step 3: Analyze Data - -Process the retrieved issues to identify: - -**Metrics:** -- Total issues by status (Done, In Progress, Blocked, etc.) -- Completion rate (if historical data available) -- Number of high priority items -- Unassigned issue count - -**Key insights:** -- Major accomplishments (recently completed high-value items) -- Critical blockers (blocked high priority issues) -- At-risk items (overdue or stuck in progress) -- Resource bottlenecks (one assignee with many issues) - -**Categorization:** -Group issues logically: -- By status (Done, In Progress, Blocked) -- By priority (Highest → Low) -- By assignee or team -- By component or epic (if relevant) - -## Step 4: Format Report - -Select the appropriate template based on audience. Templates are in `references/report-templates.md`. - -### For Executives and Delivery Managers - -Use **Executive Summary Format**: -- Brief overall status (🟢 On Track / 🟡 At Risk / 🔴 Blocked) -- Key metrics (total, completed, in progress, blocked) -- Top 3 highlights (major accomplishments) -- Critical blockers with impact -- Upcoming priorities - -**Keep it concise** - 1-2 pages maximum. Focus on what matters to decision-makers. - -### For Team-Level Reports - -Use **Detailed Technical Format**: -- Completed issues listed with keys -- In-progress issues with assignee and priority -- Blocked issues with blocker description and action needed -- Risks and dependencies -- Next period priorities - -**Include more detail** - Team needs issue-level visibility. - -### For Daily Updates - -Use **Daily Standup Format**: -- What was completed yesterday -- What's planned for today -- Current blockers -- Brief notes - -**Keep it brief** - This is a quick sync, not comprehensive analysis. - -## Step 5: Publish to Confluence - -**After generating the report, ALWAYS offer to publish to Confluence** (unless user explicitly said not to). - -If user hasn't specified Confluence details yet, ask: -- "Would you like me to publish this report to Confluence?" -- "Which Confluence space should I use?" -- "Should this be nested under a specific parent page?" - -Use the `createConfluencePage` tool to publish the report. - -**Page creation:** -``` -createConfluencePage( - cloudId="[obtained from getConfluenceSpaces or URL]", - spaceId="[numerical space ID]", - title="[Project Name] - Status Report - [Date]", - body="[formatted report in Markdown]", - contentFormat="markdown", - parentId="[optional - parent page ID if nesting under another page]" -) -``` - -**Title format examples:** -- "Project Phoenix - Weekly Status - Dec 3, 2025" -- "Engineering Sprint 23 - Status Report" -- "Q4 Initiatives - Status Update - Week 49" - -**Body formatting:** -Write the report content in Markdown. The tool will convert it to Confluence format. Use: -- Headers (`#`, `##`, `###`) for structure -- Bullet points for lists -- Bold (`**text**`) for emphasis -- Tables for metrics if needed -- Links to Jira issues: `[PROJ-123](https://yourinstance.atlassian.net/browse/PROJ-123)` - -**Best practices:** -- Include the report date prominently -- Link directly to relevant Jira issues -- Use consistent naming conventions for recurring reports -- Consider creating under a "Status Reports" parent page for organization - -### Finding the Right Space - -If the user doesn't specify a Confluence space: - -1. Use `getConfluenceSpaces` to list available spaces -2. Look for spaces related to the project (matching project name or key) -3. If unsure, ask the user which space to use -4. Default to creating in the most relevant team or project space - -### Updating Existing Reports - -If updating an existing page instead of creating new: - -1. Get the current page content: -``` -getConfluencePage( - cloudId="...", - pageId="123456", - contentFormat="markdown" -) -``` - -2. Update the page with new content: -``` -updateConfluencePage( - cloudId="...", - pageId="123456", - body="[updated report content]", - contentFormat="markdown", - versionMessage="Updated with latest status - Dec 8, 2025" -) -``` - -## Complete Example Workflow - -**User request:** "Generate a status report for Project Phoenix and publish it to Confluence" - -**Step 1 - Identify scope:** -- Project: Phoenix (need to find project key) -- Time period: Last week (default) -- Audience: Not specified, assume executive level -- Destination: Confluence, need to find appropriate space - -**Step 2 - Query Jira:** -```python -# Find project key first -searchJiraIssuesUsingJql( - cloudId="...", - jql='project = "PHOENIX" OR project = "PHX"', - maxResults=1 -) - -# Query completed issues -searchJiraIssuesUsingJql( - cloudId="...", - jql='project = "PHX" AND status = Done AND resolved >= -7d', - maxResults=50 -) - -# Query blocked issues -searchJiraIssuesUsingJql( - cloudId="...", - jql='project = "PHX" AND status = Blocked', - maxResults=50 -) - -# Query in-progress high priority -searchJiraIssuesUsingJql( - cloudId="...", - jql='project = "PHX" AND status IN ("In Progress", "In Review") AND priority IN (Highest, High)', - maxResults=50 -) -``` - -**Step 3 - Analyze:** -- 15 issues completed (metrics) -- 3 critical blockers (key insight) -- Major accomplishment: API integration completed (highlight) - -**Step 4 - Format:** -Use Executive Summary Format from templates. Create concise report with metrics, highlights, and blockers. - -**Step 5 - Publish:** -```python -# Find appropriate space -getConfluenceSpaces(cloudId="...") - -# Create page -createConfluencePage( - cloudId="...", - spaceId="12345", - title="Project Phoenix - Weekly Status - Dec 3, 2025", - body="[formatted markdown report]", - contentFormat="markdown" -) -``` - -## Tips for Quality Reports - -**Be data-driven:** -- Include specific numbers and metrics -- Reference issue keys directly -- Show trends when possible (e.g., "completed 15 vs 12 last week") - -**Highlight what matters:** -- Lead with the most important information -- Flag blockers prominently -- Celebrate significant wins - -**Make it actionable:** -- For blockers, state what action is needed and from whom -- For risks, provide mitigation options -- For priorities, be specific about next steps - -**Keep it consistent:** -- Use the same format for recurring reports -- Maintain predictable structure -- Include comparable metrics week-over-week - -**Provide context:** -- Link to Jira for details -- Explain the impact of blockers -- Connect work to business objectives when possible - -## Resources - -### scripts/jql_builder.py -Python utility for programmatically building JQL queries. Use this when you need to construct complex or dynamic queries. Import and use the helper functions rather than manually concatenating JQL strings. - -### references/jql-patterns.md -Quick reference of common JQL query patterns for status reports. Use this for standard queries or as a starting point for custom queries. - -### references/report-templates.md -Detailed templates for different report types and audiences. Reference this to select the appropriate format and structure for your report. diff --git a/plugins/atlassian-rovo/skills/generate-status-report/agents/openai.yaml b/plugins/atlassian-rovo/skills/generate-status-report/agents/openai.yaml deleted file mode 100644 index 9820355c5..000000000 --- a/plugins/atlassian-rovo/skills/generate-status-report/agents/openai.yaml +++ /dev/null @@ -1,3 +0,0 @@ -interface: - display_name: "Generate Status Report" - short_description: "Create project status reports from Jira" diff --git a/plugins/atlassian-rovo/skills/generate-status-report/references/jql-patterns.md b/plugins/atlassian-rovo/skills/generate-status-report/references/jql-patterns.md deleted file mode 100644 index b71bb2599..000000000 --- a/plugins/atlassian-rovo/skills/generate-status-report/references/jql-patterns.md +++ /dev/null @@ -1,82 +0,0 @@ -# JQL Query Patterns - -Common JQL patterns for status report generation. - -## Basic Project Queries - -**All open issues in a project:** -```jql -project = "PROJECT_KEY" AND status != Done -``` - -**Open issues by status:** -```jql -project = "PROJECT_KEY" AND status IN ("To Do", "In Progress", "In Review") -``` - -## Priority-Based Queries - -**High priority open issues:** -```jql -project = "PROJECT_KEY" AND status != Done AND priority IN ("Highest", "High") -``` - -**Blocked issues:** -```jql -project = "PROJECT_KEY" AND status = Blocked -``` - -## Time-Based Queries - -**Updated in last week:** -```jql -project = "PROJECT_KEY" AND updated >= -7d -``` - -**Completed in reporting period:** -```jql -project = "PROJECT_KEY" AND status = Done AND resolved >= -7d -``` - -**Created this sprint:** -```jql -project = "PROJECT_KEY" AND created >= -14d -``` - -## Assignee Queries - -**Unassigned issues:** -```jql -project = "PROJECT_KEY" AND assignee is EMPTY AND status != Done -``` - -**Issues by team member:** -```jql -project = "PROJECT_KEY" AND assignee = "user@example.com" AND status != Done -``` - -## Combined Queries for Reports - -**Current sprint overview:** -```jql -project = "PROJECT_KEY" AND status IN ("To Do", "In Progress", "In Review", "Done") AND updated >= -7d ORDER BY priority DESC, updated DESC -``` - -**Risk items (high priority blocked or overdue):** -```jql -project = "PROJECT_KEY" AND (status = Blocked OR (duedate < now() AND status != Done)) AND priority IN ("Highest", "High") ORDER BY priority DESC -``` - -## Epic and Component Queries - -**Issues by epic:** -```jql -parent = "EPIC_KEY" AND status != Done -``` - -Note: Older Jira instances may use `"Epic Link" = "EPIC_KEY"` instead of `parent`. - -**Issues by component:** -```jql -project = "PROJECT_KEY" AND component = "ComponentName" AND status != Done -``` diff --git a/plugins/atlassian-rovo/skills/generate-status-report/references/report-templates.md b/plugins/atlassian-rovo/skills/generate-status-report/references/report-templates.md deleted file mode 100644 index d6b835c84..000000000 --- a/plugins/atlassian-rovo/skills/generate-status-report/references/report-templates.md +++ /dev/null @@ -1,120 +0,0 @@ -# Status Report Templates - -This file provides templates for different report formats based on audience and context. - -## Executive Summary Format - -For delivery managers and executives who need high-level overview: - -```markdown -# [Project Name] - Status Report -**Date:** [Date] -**Reporting Period:** [Period] - -## Executive Summary -[2-3 sentences summarizing overall status, major accomplishments, and critical blockers] - -## Overall Status -🟢 On Track | 🟡 At Risk | 🔴 Blocked | ⚪ Not Started - -## Key Metrics -- **Total Issues:** [number] -- **Completed This Period:** [number] -- **In Progress:** [number] -- **Blocked:** [number] - -## Highlights -- [Major accomplishment 1] -- [Major accomplishment 2] -- [Major accomplishment 3] - -## Critical Blockers -- **[Blocker Title]** - [Brief description and impact] -- **[Blocker Title]** - [Brief description and impact] - -## Upcoming Priorities -- [Priority 1] -- [Priority 2] -- [Priority 3] -``` - -## Detailed Technical Format - -For team-level reports with more technical detail: - -```markdown -# [Project Name] - Status Report -**Date:** [Date] -**Reporting Period:** [Period] - -## Summary -[Overall project status and key takeaways] - -## Progress This Period - -### Completed -- [Issue Key] - [Summary] -- [Issue Key] - [Summary] - -### In Progress -- [Issue Key] - [Summary] ([Assignee], [Priority]) -- [Issue Key] - [Summary] ([Assignee], [Priority]) - -### Blocked -- [Issue Key] - [Summary] - - **Blocker:** [Description of blocker] - - **Impact:** [How this affects timeline/deliverables] - - **Action Needed:** [What needs to happen to unblock] - -## Risks and Issues -- [Risk/Issue description with mitigation plan] - -## Next Period Priorities -- [Planned work item 1] -- [Planned work item 2] - -## Dependencies -- [External dependency description] -``` - -## Daily Standup Format - -For daily status updates: - -```markdown -# Daily Status - [Date] -**Project:** [Project Name] - -## Completed Yesterday -- [Issue Key] - [Brief summary] - -## Planned for Today -- [Issue Key] - [Brief summary] - -## Blockers -- [Blocker description] (Assigned to: [name]) - -## Notes -[Any additional context or observations] -``` - -## By Priority Breakdown - -For priority-focused reporting: - -```markdown -# [Project Name] - Status by Priority -**Date:** [Date] - -## Highest Priority (P0/Blocker) -- [Issue Key] - [Summary] - Status: [status] - -## High Priority (P1/Critical) -- [Issue Key] - [Summary] - Status: [status] - -## Medium Priority (P2/Major) -- [Issue Key] - [Summary] - Status: [status] - -## Low Priority (P3/Minor) -[Summary count only unless specifically requested] -``` diff --git a/plugins/atlassian-rovo/skills/generate-status-report/scripts/jql_builder.py b/plugins/atlassian-rovo/skills/generate-status-report/scripts/jql_builder.py deleted file mode 100644 index 4d0f56a61..000000000 --- a/plugins/atlassian-rovo/skills/generate-status-report/scripts/jql_builder.py +++ /dev/null @@ -1,225 +0,0 @@ -#!/usr/bin/env python3 -""" -JQL Query Builder Utility - -Helper functions for building common JQL queries for status reports. -""" - -from typing import List, Optional -import re - - -def sanitize_jql_value(value: str) -> str: - """ - Sanitize a value for use in JQL to prevent injection attacks. - - Args: - value: The input value to sanitize - - Returns: - Sanitized value safe for JQL queries - """ - if not value: - return value - - # Remove or escape potentially dangerous characters - # Allow alphanumeric, spaces, hyphens, underscores, dots, @ - safe_pattern = re.compile(r'^[a-zA-Z0-9\s\-_.@]+$') - - if not safe_pattern.match(value): - raise ValueError( - f"Invalid characters in input: '{value}'. " - f"Only alphanumeric characters, spaces, hyphens, underscores, dots, and @ are allowed." - ) - - # Escape double quotes by doubling them (JQL escaping) - return value.replace('"', '""') - - -def sanitize_jql_list(values: List[str]) -> List[str]: - """ - Sanitize a list of values for use in JQL. - - Args: - values: List of input values to sanitize - - Returns: - List of sanitized values - """ - return [sanitize_jql_value(v) for v in values] - - -def build_project_query( - project_key: str, - statuses: Optional[List[str]] = None, - exclude_done: bool = True, - priorities: Optional[List[str]] = None, - days_back: Optional[int] = None, - assignee: Optional[str] = None, - order_by: str = "priority DESC, updated DESC" -) -> str: - """ - Build a JQL query for project status. - - Args: - project_key: The Jira project key - statuses: List of statuses to include (e.g., ["To Do", "In Progress"]) - exclude_done: Whether to exclude Done status (default True) - priorities: List of priorities to include (e.g., ["Highest", "High"]) - days_back: Number of days to look back for updates (e.g., 7) - assignee: Specific assignee email or "EMPTY" for unassigned - order_by: JQL order by clause (default: "priority DESC, updated DESC") - - Returns: - JQL query string - """ - # Sanitize inputs to prevent JQL injection - project_key = sanitize_jql_value(project_key) - conditions = [f'project = "{project_key}"'] - - if statuses: - statuses = sanitize_jql_list(statuses) - status_list = '", "'.join(statuses) - conditions.append(f'status IN ("{status_list}")') - elif exclude_done: - conditions.append('status != Done') - - if priorities: - priorities = sanitize_jql_list(priorities) - priority_list = '", "'.join(priorities) - conditions.append(f'priority IN ("{priority_list}")') - - if days_back: - if not isinstance(days_back, int) or days_back < 0: - raise ValueError(f"days_back must be a non-negative integer, got: {days_back}") - conditions.append(f'updated >= -{days_back}d') - - if assignee: - if assignee.upper() == "EMPTY": - conditions.append('assignee is EMPTY') - else: - assignee = sanitize_jql_value(assignee) - conditions.append(f'assignee = "{assignee}"') - - query = " AND ".join(conditions) - - if order_by: - # Validate order_by contains only safe keywords - order_by = sanitize_jql_value(order_by) - query += f' ORDER BY {order_by}' - - return query - - -def build_blocked_query( - project_key: str, - high_priority_only: bool = False -) -> str: - """Build query for blocked issues.""" - project_key = sanitize_jql_value(project_key) - query = f'project = "{project_key}" AND status = Blocked' - - if high_priority_only: - query += ' AND priority IN (Highest, High)' - - query += ' ORDER BY priority DESC, created ASC' - return query - - -def build_completed_query( - project_key: str, - days_back: int = 7 -) -> str: - """Build query for recently completed issues.""" - project_key = sanitize_jql_value(project_key) - - if not isinstance(days_back, int) or days_back < 0: - raise ValueError(f"days_back must be a non-negative integer, got: {days_back}") - - return ( - f'project = "{project_key}" AND ' - f'status = Done AND ' - f'resolved >= -{days_back}d ' - f'ORDER BY resolved DESC' - ) - - -def build_in_progress_query( - project_key: str, - priorities: Optional[List[str]] = None -) -> str: - """Build query for in-progress issues.""" - project_key = sanitize_jql_value(project_key) - query = f'project = "{project_key}" AND status IN ("In Progress", "In Review")' - - if priorities: - priorities = sanitize_jql_list(priorities) - priority_list = '", "'.join(priorities) - query += f' AND priority IN ("{priority_list}")' - - query += ' ORDER BY priority DESC, updated DESC' - return query - - -def build_risk_query( - project_key: str, - include_overdue: bool = True -) -> str: - """Build query for risk items (blocked or overdue high priority).""" - project_key = sanitize_jql_value(project_key) - conditions = [f'project = "{project_key}"'] - - risk_conditions = ['status = Blocked'] - if include_overdue: - risk_conditions.append('(duedate < now() AND status != Done)') - - conditions.append(f'({" OR ".join(risk_conditions)})') - conditions.append('priority IN (Highest, High)') - - query = " AND ".join(conditions) - query += ' ORDER BY priority DESC, duedate ASC' - return query - - -def build_unassigned_query( - project_key: str, - exclude_done: bool = True -) -> str: - """Build query for unassigned issues.""" - project_key = sanitize_jql_value(project_key) - query = f'project = "{project_key}" AND assignee is EMPTY' - - if exclude_done: - query += ' AND status != Done' - - query += ' ORDER BY priority DESC, created ASC' - return query - - -# Example usage -if __name__ == "__main__": - # Example queries - project = "PROJ" - - print("Open Issues Query:") - print(build_project_query(project)) - print() - - print("High Priority In Progress:") - print(build_in_progress_query(project, priorities=["Highest", "High"])) - print() - - print("Blocked Issues:") - print(build_blocked_query(project, high_priority_only=True)) - print() - - print("Completed Last Week:") - print(build_completed_query(project, days_back=7)) - print() - - print("Risk Items:") - print(build_risk_query(project)) - print() - - print("Unassigned Open Issues:") - print(build_unassigned_query(project)) diff --git a/plugins/atlassian-rovo/skills/search-company-knowledge/SKILL.md b/plugins/atlassian-rovo/skills/search-company-knowledge/SKILL.md deleted file mode 100644 index 61f42ed09..000000000 --- a/plugins/atlassian-rovo/skills/search-company-knowledge/SKILL.md +++ /dev/null @@ -1,575 +0,0 @@ ---- -name: search-company-knowledge -description: "Search across company knowledge bases (Confluence, Jira, internal docs) to find and explain internal concepts, processes, and technical details. When an agent needs to: (1) Find or search for information about systems, terminology, processes, deployment, authentication, infrastructure, architecture, or technical concepts, (2) Search internal documentation, knowledge base, company docs, or our docs, (3) Explain what something is, how it works, or look up information, or (4) Synthesize information from multiple sources. Searches in parallel and provides cited answers." ---- - -# Search Company Knowledge - -## Keywords -find information, search company knowledge, look up, what is, explain, company docs, internal documentation, Confluence search, Jira search, our documentation, internal knowledge, knowledge base, search for, tell me about, get information about, company systems, terminology, find everything about, what do we know about, deployment, authentication, infrastructure, processes, procedures, how to, how does, our systems, our processes, internal systems, company processes, technical documentation, engineering docs, architecture, configuration, search our docs, search internal docs, find in our docs - -## Overview - -Search across siloed company knowledge systems (Confluence, Jira, internal documentation) to find comprehensive answers to questions about internal concepts, systems, and terminology. This skill performs parallel searches across multiple sources and synthesizes results with proper citations. - -**Use this skill when:** Users ask about internal company knowledge that might be documented in Confluence pages, Jira tickets, or internal documentation. - ---- - -## Workflow - -Follow this 5-step process to provide comprehensive, well-cited answers: - -### Step 1: Identify Search Query - -Extract the core search terms from the user's question. - -**Examples:** -- User: "Find everything about Stratus minions" → Search: "Stratus minions" -- User: "What do we know about the billing system?" → Search: "billing system" -- User: "Explain our deployment process" → Search: "deployment process" - -**Consider:** -- Main topic or concept -- Any specific system/component names -- Technical terms or jargon - ---- - -### Step 2: Execute Parallel Search - -Search across all available knowledge sources simultaneously for comprehensive coverage. - -#### Option A: Cross-System Search (Recommended First) - -Use the **`search`** tool (Rovo Search) to search across Confluence and Jira at once: - -``` -search( - cloudId="...", - query="[extracted search terms]" -) -``` - -**When to use:** -- Default approach for most queries -- When you don't know which system has the information -- Fastest way to get results from multiple sources - -**Example:** -``` -search( - cloudId="...", - query="Stratus minions" -) -``` - -This returns results from both Confluence pages and Jira issues. - -#### Option B: Targeted Confluence Search - -Use **`searchConfluenceUsingCql`** when specifically searching Confluence: - -``` -searchConfluenceUsingCql( - cloudId="...", - cql="text ~ 'search terms' OR title ~ 'search terms'" -) -``` - -**When to use:** -- User specifically mentions "in Confluence" or "in our docs" -- Cross-system search returns too many Jira results -- Looking for documentation rather than tickets - -**Example CQL patterns:** -``` -text ~ "Stratus minions" -text ~ "authentication" AND type = page -title ~ "deployment guide" -``` - -#### Option C: Targeted Jira Search - -Use **`searchJiraIssuesUsingJql`** when specifically searching Jira: - -``` -searchJiraIssuesUsingJql( - cloudId="...", - jql="text ~ 'search terms' OR summary ~ 'search terms'" -) -``` - -**When to use:** -- User mentions "tickets", "issues", or "bugs" -- Looking for historical problems or implementation details -- Cross-system search returns mostly documentation - -**Example JQL patterns:** -``` -text ~ "Stratus minions" -summary ~ "authentication" AND type = Bug -text ~ "deployment" AND created >= -90d -``` - -#### Search Strategy - -**For most queries, use this sequence:** - -1. Start with `search` (cross-system) - **always try this first** -2. If results are unclear, follow up with targeted searches -3. If results mention specific pages/tickets, fetch them for details - ---- - -### Step 3: Fetch Detailed Content - -After identifying relevant sources, fetch full content for comprehensive answers. - -#### For Confluence Pages - -When search results reference Confluence pages: - -``` -getConfluencePage( - cloudId="...", - pageId="[page ID from search results]", - contentFormat="markdown" -) -``` - -**Returns:** Full page content in Markdown format - -**When to fetch:** -- Search result snippet is too brief -- Need complete context -- Page seems to be the primary documentation - -#### For Jira Issues - -When search results reference Jira issues: - -``` -getJiraIssue( - cloudId="...", - issueIdOrKey="PROJ-123" -) -``` - -**Returns:** Full issue details including description, comments, status - -**When to fetch:** -- Need to understand a reported bug or issue -- Search result doesn't show full context -- Issue contains important implementation notes - -#### Prioritization - -**Fetch in this order:** -1. **Official documentation pages** (Confluence pages with "guide", "documentation", "overview" in title) -2. **Recent/relevant issues** (Jira tickets that are relevant and recent) -3. **Additional context** (related pages mentioned in initial results) - -**Don't fetch everything** - be selective based on relevance to user's question. - ---- - -### Step 4: Synthesize Results - -Combine information from multiple sources into a coherent answer. - -#### Synthesis Guidelines - -**Structure your answer:** - -1. **Direct Answer First** - - Start with a clear, concise answer to the question - - "Stratus minions are..." - -2. **Detailed Explanation** - - Provide comprehensive details from all sources - - Organize by topic, not by source - -3. **Source Attribution** - - Note where each piece of information comes from - - Format: "According to [source], ..." - -4. **Highlight Discrepancies** - - If sources conflict, note it explicitly - - Example: "The Confluence documentation states X, however Jira ticket PROJ-123 indicates that due to bug Y, the behavior is actually Z" - -5. **Provide Context** - - Mention if information is outdated - - Note if a feature is deprecated or in development - -#### Synthesis Patterns - -**Pattern 1: Multiple sources agree** -``` -Stratus minions are background worker processes that handle async tasks. - -According to the Confluence documentation, they process jobs from the queue and -can be scaled horizontally. This is confirmed by several Jira tickets (PROJ-145, -PROJ-203) which discuss minion configuration and scaling strategies. -``` - -**Pattern 2: Sources provide different aspects** -``` -The billing system has two main components: - -**Payment Processing** (from Confluence "Billing Architecture" page) -- Handles credit card transactions -- Integrates with Stripe API -- Runs nightly reconciliation - -**Invoice Generation** (from Jira PROJ-189) -- Creates monthly invoices -- Note: Currently has a bug where tax calculation fails for EU customers -- Fix planned for Q1 2024 -``` - -**Pattern 3: Conflicting information** -``` -There is conflicting information about the authentication timeout: - -- **Official Documentation** (Confluence) states: 30-minute session timeout -- **Implementation Reality** (Jira PROJ-456, filed Oct 2023): Actual timeout is - 15 minutes due to load balancer configuration -- **Status:** Engineering team aware, fix planned but no timeline yet - -Current behavior: Expect 15-minute timeout despite docs saying 30 minutes. -``` - -**Pattern 4: Incomplete information** -``` -Based on available documentation: - -[What we know about deployment process from Confluence and Jira] - -However, I couldn't find information about: -- Rollback procedures -- Database migration handling - -You may want to check with the DevOps team or search for additional documentation. -``` - ---- - -### Step 5: Provide Citations - -Always include links to source materials so users can explore further. - -#### Citation Format - -**For Confluence pages:** -``` -**Source:** [Page Title](https://yoursite.atlassian.net/wiki/spaces/SPACE/pages/123456) -``` - -**For Jira issues:** -``` -**Related Tickets:** -- [PROJ-123](https://yoursite.atlassian.net/browse/PROJ-123) - Brief description -- [PROJ-456](https://yoursite.atlassian.net/browse/PROJ-456) - Brief description -``` - -**Complete citation section:** -``` -## Sources - -**Confluence Documentation:** -- [Stratus Architecture Guide](https://yoursite.atlassian.net/wiki/spaces/DOCS/pages/12345) -- [Minion Configuration](https://yoursite.atlassian.net/wiki/spaces/DEVOPS/pages/67890) - -**Jira Issues:** -- [PROJ-145](https://yoursite.atlassian.net/browse/PROJ-145) - Minion scaling implementation -- [PROJ-203](https://yoursite.atlassian.net/browse/PROJ-203) - Performance optimization - -**Additional Resources:** -- [Internal architecture doc link if found] -``` - ---- - -## Search Best Practices - -### Effective Search Terms - -**Do:** -- ✅ Use specific technical terms: "OAuth authentication flow" -- ✅ Include system names: "Stratus minions" -- ✅ Use acronyms if they're common: "API rate limiting" -- ✅ Try variations if first search fails: "deploy process" → "deployment pipeline" - -**Don't:** -- ❌ Be too generic: "how things work" -- ❌ Use full sentences: Use key terms instead -- ❌ Include filler words: "the", "our", "about" - -### Search Result Quality - -**Good results:** -- Recent documentation (< 1 year old) -- Official/canonical pages (titled "Guide", "Documentation", "Overview") -- Multiple sources confirming same information -- Detailed implementation notes - -**Questionable results:** -- Very old tickets (> 2 years, may be outdated) -- Duplicate or conflicting information -- Draft pages or work-in-progress docs -- Personal pages (may not be official) - -**When results are poor:** -- Try different search terms -- Expand search to include related concepts -- Search for specific error messages or codes -- Ask user for more context - ---- - -## Handling Common Scenarios - -### Scenario 1: No Results Found - -If searches return no results: - -``` -I searched across Confluence and Jira but couldn't find information about "[topic]". - -This could mean: -- The concept hasn't been documented yet -- It might be called something else (can you provide alternative names?) -- Documentation might be in a different system I don't have access to - -Would you like me to: -1. Try searching with different terms? -2. Search for related concepts? -3. Look for mentions in recent Jira tickets? -``` - -### Scenario 2: Too Many Results - -If search returns 50+ results: - -- Focus on most recent and most relevant (top 10-15) -- Prioritize official documentation over random tickets -- Group similar results: "I found 15 tickets related to authentication bugs..." -- Offer to narrow search: "Would you like me to focus on [specific aspect]?" - -### Scenario 3: Ambiguous Query - -If the user's question is vague: - -``` -I found information about several topics related to "[term]": -1. [Topic A] - from Confluence page X -2. [Topic B] - from Jira tickets Y, Z -3. [Topic C] - from documentation W - -Which aspect are you most interested in? -``` - -### Scenario 4: Outdated Information - -If you find information is old: - -``` -I found documentation about [topic], but note that it's from [date] and may be outdated. - -[Provide the information anyway] - -You may want to verify this is still current, especially if it's from more than -a year ago. -``` - -### Scenario 5: Restricted/Private Content - -If search returns inaccessible content: - -``` -Search results mention [topic] in the following locations, but I don't have -access to view the full content: -- Confluence page: [title] (Space: [SPACE]) -- Jira issue: PROJ-123 - -You may need to access these directly or check your permissions. -``` - ---- - -## Examples - -### Example 1: Clear Documentation Available - -**User:** "What are Stratus minions?" - -**Process:** -1. Search: `search(cloudId="...", query="Stratus minions")` -2. Find: Confluence page "Stratus Architecture" + 3 Jira tickets -3. Fetch: Get full Confluence page content -4. Synthesize: Combine architectural overview with implementation details from tickets -5. Cite: Provide links to all sources - -**Answer:** -``` -Stratus minions are background worker processes that handle asynchronous task -processing in the Stratus system. - -**Key Characteristics:** -- Run independently from web servers -- Process jobs from a Redis queue -- Can be scaled horizontally for increased throughput -- Handle tasks like email sending, report generation, and data processing - -**Architecture:** -According to the Stratus Architecture documentation, minions follow a -worker-queue pattern where: -1. Web application enqueues tasks -2. Minions poll the queue for work -3. Tasks are processed asynchronously -4. Results are stored in the database - -**Implementation Notes:** -From Jira ticket PROJ-203, the current deployment runs 5 minion instances, -with plans to scale to 10 for handling increased load. - -## Sources -- [Stratus Architecture Guide](link) -- [PROJ-203](link) - Minion scaling implementation -``` - -### Example 2: Conflicting Information - -**User:** "How long is our session timeout?" - -**Process:** -1. Search across Confluence and Jira -2. Find: Docs say 30 min, but ticket reports actual timeout is 15 min -3. Fetch both sources for complete context -4. Clearly note the discrepancy - -**Answer:** -``` -There is a discrepancy between documented and actual behavior: - -**Official Documentation** (Security Guidelines): -Session timeout is configured for 30 minutes of inactivity. - -**Actual Behavior** (PROJ-456, reported Oct 2023): -Users are being logged out after 15 minutes due to a load balancer configuration -that overrides the application setting. - -**Current Status:** -The engineering team is aware of this issue. The load balancer timeout needs to -be adjusted to match the application setting. No fix timeline has been set. - -**Recommendation:** Plan for 15-minute timeout until this is resolved. - -## Sources -- [Security Guidelines](link) - Official documentation -- [PROJ-456](link) - Timeout discrepancy bug report -``` - -### Example 3: Partial Information - -**User:** "Explain our deployment process" - -**Process:** -1. Search finds some information but not complete -2. Synthesize what is available -3. Note gaps in documentation - -**Answer:** -``` -Based on available documentation, here's what I found about the deployment process: - -**Build Phase** (from Confluence "CI/CD Guide"): -1. Code pushed to main branch triggers GitHub Actions -2. Automated tests run (unit, integration) -3. Docker image built and pushed to registry - -**Deployment Phase** (from Jira PROJ-789): -4. Staging deployment happens automatically -5. Manual approval required for production -6. Blue-green deployment strategy used - -**Gaps in Documentation:** -I couldn't find documented information about: -- Rollback procedures -- Database migration handling -- Post-deployment verification steps - -These details may exist in tribal knowledge or need to be documented. - -## Sources -- [CI/CD Guide](link) -- [PROJ-789](link) - Deployment pipeline implementation - -Would you like me to search for more specific aspects of deployment? -``` - ---- - -## Tips for High-Quality Answers - -### Do: -✅ Always search before answering (don't rely on general knowledge) -✅ Cite all sources with links -✅ Note discrepancies explicitly -✅ Mention when information is old -✅ Provide context and examples -✅ Structure answers clearly with headers -✅ Link to related documentation - -### Don't: -❌ Assume general knowledge applies to this company -❌ Make up information if search returns nothing -❌ Ignore conflicting information -❌ Quote entire documents (summarize instead) -❌ Overwhelm with too many sources (curate top 5-10) -❌ Forget to fetch details when snippets are insufficient - ---- - -## When NOT to Use This Skill - -This skill is for **internal company knowledge only**. Do NOT use for: - -❌ General technology questions (use your training knowledge) -❌ External documentation (use web_search) -❌ Company-agnostic questions -❌ Questions about other companies -❌ Current events or news - -**Examples of what NOT to use this skill for:** -- "What is machine learning?" (general knowledge) -- "How does React work?" (external documentation) -- "What's the weather?" (not knowledge search) -- "Find a restaurant" (not work-related) - ---- - -## Quick Reference - -**Primary tool:** `search(cloudId, query)` - Use this first, always - -**Follow-up tools:** -- `getConfluencePage(cloudId, pageId, contentFormat)` - Get full page content -- `getJiraIssue(cloudId, issueIdOrKey)` - Get full issue details -- `searchConfluenceUsingCql(cloudId, cql)` - Targeted Confluence search -- `searchJiraIssuesUsingJql(cloudId, jql)` - Targeted Jira search - -**Answer structure:** -1. Direct answer -2. Detailed explanation -3. Source attribution -4. Discrepancies (if any) -5. Citations with links - -**Remember:** -- Parallel search > Sequential search -- Synthesize, don't just list -- Always cite sources -- Note conflicts explicitly -- Be clear about gaps in documentation diff --git a/plugins/atlassian-rovo/skills/search-company-knowledge/agents/openai.yaml b/plugins/atlassian-rovo/skills/search-company-knowledge/agents/openai.yaml deleted file mode 100644 index 646480c78..000000000 --- a/plugins/atlassian-rovo/skills/search-company-knowledge/agents/openai.yaml +++ /dev/null @@ -1,3 +0,0 @@ -interface: - display_name: "Search Company Knowledge" - short_description: "Search Confluence, Jira, and internal Atlassian context" diff --git a/plugins/atlassian-rovo/skills/spec-to-backlog/SKILL.md b/plugins/atlassian-rovo/skills/spec-to-backlog/SKILL.md deleted file mode 100644 index fc9f5623a..000000000 --- a/plugins/atlassian-rovo/skills/spec-to-backlog/SKILL.md +++ /dev/null @@ -1,543 +0,0 @@ ---- -name: spec-to-backlog -description: "Automatically convert Confluence specification documents into structured Jira backlogs with Epics and implementation tickets. When an agent needs to: (1) Create Jira tickets from a Confluence page, (2) Generate a backlog from a specification, (3) Break down a spec into implementation tasks, or (4) Convert requirements into Jira issues. Handles reading Confluence pages, analyzing specifications, creating Epics with proper structure, and generating detailed implementation tickets linked to the Epic." ---- - -# Spec to Backlog - -## Overview - -Transform Confluence specification documents into structured Jira backlogs automatically. This skill reads requirement documents from Confluence, intelligently breaks them down into logical implementation tasks, **creates an Epic first** to organize the work, then generates individual Jira tickets linked to that Epic—eliminating tedious manual copy-pasting. - -## Core Workflow - -**CRITICAL: Always follow this exact sequence:** - -1. **Fetch Confluence Page** → Get the specification content -2. **Ask for Project Key** → Identify target Jira project -3. **Analyze Specification** → Break down into logical tasks (internally, don't create yet) -4. **Present Breakdown** → Show user the planned Epic and tickets -5. **Create Epic FIRST** → Establish parent Epic and capture its key -6. **Create Child Tickets** → Generate tickets linked to the Epic -7. **Provide Summary** → Present all created items with links - -**Why Epic must be created first:** Child tickets need the Epic key to link properly during creation. Creating tickets first will result in orphaned tickets. - ---- - -## Step 1: Fetch Confluence Page - -When triggered, obtain the Confluence page content: - -### If user provides a Confluence URL: - -Extract the cloud ID and page ID from the URL pattern: -- Standard format: `https://[site].atlassian.net/wiki/spaces/[SPACE]/pages/[PAGE_ID]/[title]` -- The cloud ID can be extracted from `[site].atlassian.net` or by calling `getAccessibleAtlassianResources` -- The page ID is the numeric value in the URL path - -### If user provides only a page title or description: - -Use the `search` tool to find the page: -``` -search( - cloudId="...", - query="type=page AND title~'[search terms]'" -) -``` - -If multiple pages match, ask the user to clarify which one to use. - -### Fetch the page: - -Call `getConfluencePage` with the cloudId and pageId: -``` -getConfluencePage( - cloudId="...", - pageId="123456", - contentFormat="markdown" -) -``` - -This returns the page content in Markdown format, which you'll analyze in Step 3. - ---- - -## Step 2: Ask for Project Key - -**Before analyzing the spec**, determine the target Jira project: - -### Ask the user: -"Which Jira project should I create these tickets in? Please provide the project key (e.g., PROJ, ENG, PRODUCT)." - -### If user is unsure: -Call `getVisibleJiraProjects` to show available projects: -``` -getVisibleJiraProjects( - cloudId="...", - action="create" -) -``` - -Present the list: "I found these projects you can create issues in: PROJ (Project Alpha), ENG (Engineering), PRODUCT (Product Team)." - -### Once you have the project key: -Call `getJiraProjectIssueTypesMetadata` to understand what issue types are available: -``` -getJiraProjectIssueTypesMetadata( - cloudId="...", - projectIdOrKey="PROJ" -) -``` - -**Identify available issue types:** -- Which issue type is "Epic" (or similar parent type like "Initiative") -- What child issue types are available: "Story", "Task", "Bug", "Sub-task", etc. - -**Select appropriate issue types for child tickets:** - -The skill should intelligently choose issue types based on the specification content: - -**Use "Bug" when the spec describes:** -- Fixing existing problems or defects -- Resolving errors or incorrect behavior -- Addressing performance issues -- Correcting data inconsistencies -- Keywords: "fix", "resolve", "bug", "issue", "problem", "error", "broken" - -**Use "Story" when the spec describes:** -- New user-facing features or functionality -- User experience improvements -- Customer-requested capabilities -- Product enhancements -- Keywords: "feature", "user can", "add ability to", "new", "enable users" - -**Use "Task" when the spec describes:** -- Technical work without direct user impact -- Infrastructure or DevOps work -- Refactoring or optimization -- Documentation or tooling -- Configuration or setup -- Keywords: "implement", "setup", "configure", "optimize", "refactor", "infrastructure" - -**Fallback logic:** -1. If "Story" is available and content suggests new features → use "Story" -2. If "Bug" is available and content suggests fixes → use "Bug" -3. If "Task" is available → use "Task" for technical work -4. If none of the above are available → use the first available non-Epic, non-Subtask issue type - -**Store the selected issue types for use in Step 6:** -- Epic issue type name (e.g., "Epic") -- Default child issue type (e.g., "Story" or "Task") -- Bug issue type name if available (e.g., "Bug") - ---- - -## Step 3: Analyze Specification - -Read the Confluence page content and **internally** decompose it into: - -### Epic-Level Goal -What is the overall objective or feature being implemented? This becomes your Epic. - -**Example Epic summaries:** -- "User Authentication System" -- "Payment Gateway Integration" -- "Dashboard Performance Optimization" -- "Mobile App Notifications Feature" - -### Implementation Tasks -Break the work into logical, independently implementable tasks. - -**Breakdown principles:** -- **Size:** 3-10 tasks per spec typically (avoid over-granularity) -- **Clarity:** Each task should be specific and actionable -- **Independence:** Tasks can be worked on separately when possible -- **Completeness:** Include backend, frontend, testing, documentation, infrastructure as needed -- **Grouping:** Related functionality stays in the same ticket - -**Consider these dimensions:** -- Technical layers: Backend API, Frontend UI, Database, Infrastructure -- Work types: Implementation, Testing, Documentation, Deployment -- Features: Break complex features into sub-features -- Dependencies: Identify prerequisite work - -**Common task patterns:** -- "Design [component] database schema" -- "Implement [feature] API endpoints" -- "Build [component] UI components" -- "Add [integration] to existing [system]" -- "Write tests for [feature]" -- "Update documentation for [feature]" - -**Use action verbs:** -- Implement, Create, Build, Add, Design, Integrate, Update, Fix, Optimize, Configure, Deploy, Test, Document - ---- - -## Step 4: Present Breakdown to User - -**Before creating anything**, show the user your planned breakdown: - -**Format:** -``` -I've analyzed the spec and here's the backlog I'll create: - -**Epic:** [Epic Summary] -[Brief description of epic scope] - -**Implementation Tickets (7):** -1. [Story] [Task 1 Summary] -2. [Task] [Task 2 Summary] -3. [Story] [Task 3 Summary] -4. [Bug] [Task 4 Summary] -5. [Task] [Task 5 Summary] -6. [Story] [Task 6 Summary] -7. [Task] [Task 7 Summary] - -Shall I create these tickets in [PROJECT KEY]? -``` - -**The issue type labels show what type each ticket will be created as:** -- [Story] - New user-facing feature -- [Task] - Technical implementation work -- [Bug] - Fix or resolve an issue - -**Wait for user confirmation** before proceeding. This allows them to: -- Request changes to the breakdown -- Confirm the scope is correct -- Adjust the number or focus of tickets - -If user requests changes, adjust the breakdown and re-present. - ---- - -## Step 5: Create Epic FIRST - -**CRITICAL:** The Epic must be created before any child tickets. - -### Create the Epic: - -Call `createJiraIssue` with: - -``` -createJiraIssue( - cloudId="...", - projectKey="PROJ", - issueTypeName="Epic", - summary="[Epic Summary from Step 3]", - description="[Epic Description - see below]" -) -``` - -### Epic Description Structure: - -```markdown -## Overview -[1-2 sentence summary of what this epic delivers] - -## Source -Confluence Spec: [Link to Confluence page] - -## Objectives -- [Key objective 1] -- [Key objective 2] -- [Key objective 3] - -## Scope -[Brief description of what's included and what's not] - -## Success Criteria -- [Measurable criterion 1] -- [Measurable criterion 2] -- [Measurable criterion 3] - -## Technical Notes -[Any important technical context from the spec] -``` - -### Capture the Epic Key: - -The response will include the Epic's key (e.g., "PROJ-123"). **Save this key**—you'll need it for every child ticket. - -**Example response:** -```json -{ - "key": "PROJ-123", - "id": "10001", - "self": "https://yoursite.atlassian.net/rest/api/3/issue/10001" -} -``` - -**Confirm Epic creation to user:** -"✅ Created Epic: PROJ-123 - User Authentication System" - ---- - -## Step 6: Create Child Tickets - -Now create each implementation task as a child ticket linked to the Epic. - -### For each task: - -**Determine the appropriate issue type for this specific task:** -- If the task involves fixing/resolving an issue → use "Bug" (if available) -- If the task involves new user-facing features → use "Story" (if available) -- If the task involves technical/infrastructure work → use "Task" (if available) -- Otherwise → use the default child issue type from Step 2 - -Call `createJiraIssue` with: - -``` -createJiraIssue( - cloudId="...", - projectKey="PROJ", - issueTypeName="[Story/Task/Bug based on task content]", - summary="[Task Summary]", - description="[Task Description - see below]", - parent="PROJ-123" # The Epic key from Step 5 -) -``` - -**Example issue type selection:** -- "Fix authentication timeout bug" → Use "Bug" -- "Build user dashboard UI" → Use "Story" -- "Configure CI/CD pipeline" → Use "Task" -- "Implement password reset API" → Use "Story" (new user feature) - -### Task Summary Format: - -Use action verbs and be specific: -- ✅ "Implement user registration API endpoint" -- ✅ "Design authentication database schema" -- ✅ "Build login form UI components" -- ❌ "Do backend work" (too vague) -- ❌ "Frontend" (not actionable) - -### Task Description Structure: - -```markdown -## Context -[Brief context for this task from the Confluence spec] - -## Requirements -- [Requirement 1] -- [Requirement 2] -- [Requirement 3] - -## Technical Details -[Specific technical information relevant to this task] -- Technologies: [e.g., Node.js, React, PostgreSQL] -- Components: [e.g., API routes, database tables, UI components] -- Dependencies: [e.g., requires PROJ-124 to be completed first] - -## Acceptance Criteria -- [ ] [Testable criterion 1] -- [ ] [Testable criterion 2] -- [ ] [Testable criterion 3] - -## Related -- Confluence Spec: [Link to relevant section if possible] -- Epic: PROJ-123 -``` - -### Acceptance Criteria Best Practices: - -Make them **testable** and **specific**: -- ✅ "API returns 201 status on successful user creation" -- ✅ "Password must be at least 8 characters and hashed with bcrypt" -- ✅ "Login form validates email format before submission" -- ❌ "User can log in" (too vague) -- ❌ "It works correctly" (not testable) - -### Create all tickets sequentially: - -Track each created ticket key for the summary. - ---- - -## Step 7: Provide Summary - -After all tickets are created, present a comprehensive summary: - -``` -✅ Backlog created successfully! - -**Epic:** PROJ-123 - User Authentication System -https://yoursite.atlassian.net/browse/PROJ-123 - -**Implementation Tickets (7):** - -1. PROJ-124 - Design authentication database schema - https://yoursite.atlassian.net/browse/PROJ-124 - -2. PROJ-125 - Implement user registration API endpoint - https://yoursite.atlassian.net/browse/PROJ-125 - -3. PROJ-126 - Implement user login API endpoint - https://yoursite.atlassian.net/browse/PROJ-126 - -4. PROJ-127 - Build login form UI components - https://yoursite.atlassian.net/browse/PROJ-127 - -5. PROJ-128 - Build registration form UI components - https://yoursite.atlassian.net/browse/PROJ-128 - -6. PROJ-129 - Add authentication integration to existing features - https://yoursite.atlassian.net/browse/PROJ-129 - -7. PROJ-130 - Write authentication tests and documentation - https://yoursite.atlassian.net/browse/PROJ-130 - -**Source:** https://yoursite.atlassian.net/wiki/spaces/SPECS/pages/123456 - -**Next Steps:** -- Review tickets in Jira for accuracy and completeness -- Assign tickets to team members -- Estimate story points if your team uses them -- Add any additional labels or custom field values -- Schedule work for the upcoming sprint -``` - ---- - -## Edge Cases & Troubleshooting - -### Multiple Specs or Pages - -**If user references multiple Confluence pages:** -- Process each separately, or ask which to prioritize -- Consider creating separate Epics for distinct features -- "I see you've provided 3 spec pages. Should I create separate Epics for each, or would you like me to focus on one first?" - -### Existing Epic - -**If user wants to add tickets to an existing Epic:** -- Skip Epic creation (Step 5) -- Ask for the existing Epic key: "What's the Epic key you'd like to add tickets to? (e.g., PROJ-100)" -- Proceed with Step 6 using the provided Epic key - -### Custom Required Fields - -**If ticket creation fails due to required fields:** -1. Use `getJiraIssueTypeMetaWithFields` to identify what fields are required: - ``` - getJiraIssueTypeMetaWithFields( - cloudId="...", - projectIdOrKey="PROJ", - issueTypeId="10001" - ) - ``` - -2. Ask user for values: "This project requires a 'Priority' field. What priority should I use? (e.g., High, Medium, Low)" - -3. Include in `additional_fields` when creating: - ``` - additional_fields={ - "priority": {"name": "High"} - } - ``` - -### Large Specifications - -**For specs that would generate 15+ tickets:** -- Present the full breakdown to user -- Ask: "This spec would create 18 tickets. Should I create all of them, or would you like to adjust the scope?" -- Offer to create a subset first: "I can create the first 10 tickets now and wait for your feedback before creating the rest." - -### Subtasks vs Tasks - -**Some projects use "Subtask" issue types:** -- If metadata shows "Subtask" is available, you can use it for more granular work -- Subtasks link to parent tasks (not Epics directly) -- Structure: Epic → Task → Subtasks - -### Ambiguous Specifications - -**If the Confluence page lacks detail:** -- Create fewer, broader tickets -- Note in ticket descriptions: "Detailed requirements need to be defined during refinement" -- Ask user: "The spec is light on implementation details. Should I create high-level tickets that can be refined later?" - -### Failed API Calls - -**If `createJiraIssue` fails:** -1. Check the error message for specific issues (permissions, required fields, invalid values) -2. Use `getJiraProjectIssueTypesMetadata` to verify issue type availability -3. Inform user: "I encountered an error creating tickets: [error message]. This might be due to project permissions or required fields." - ---- - -## Tips for High-Quality Breakdowns - -### Be Specific -- ❌ "Do frontend work" -- ✅ "Create login form UI with email/password inputs and validation" - -### Include Technical Context -- Mention specific technologies when clear from spec -- Reference components, services, or modules -- Note integration points - -### Logical Grouping -- Related work stays in the same ticket -- Don't split artificially: "Build user profile page" includes both UI and API integration -- Do split when different specialties: Separate backend API task from frontend UI task if worked on by different people - -### Avoid Duplication -- Don't create redundant tickets for the same functionality -- If multiple features need the same infrastructure, create one infrastructure ticket they all depend on - -### Explicit Testing -- Include testing as part of feature tasks ("Implement X with unit tests") -- OR create separate testing tasks for complex features ("Write integration tests for authentication flow") - -### Documentation Tasks -- For user-facing features: Include "Update user documentation" or "Create help articles" -- For developer tools: Include "Update API documentation" or "Write integration guide" - -### Dependencies -- Note prerequisites in ticket descriptions -- Use "Depends on" or "Blocks" relationships in Jira if available -- Sequence tickets logically (infrastructure → implementation → testing) - ---- - -## Examples of Good Breakdowns - -### Example 1: New Feature - Search Functionality - -**Epic:** Product Search and Filtering - -**Tickets:** -1. [Task] Design search index schema and data structure -2. [Task] Implement backend search API with Elasticsearch -3. [Story] Build search input and results UI components -4. [Story] Add advanced filtering (price, category, ratings) -5. [Story] Implement search suggestions and autocomplete -6. [Task] Optimize search performance and add caching -7. [Task] Write search integration tests and documentation - -### Example 2: Bug Fix - Performance Issue - -**Epic:** Resolve Dashboard Load Time Issues - -**Tickets:** -1. [Task] Profile and identify performance bottlenecks -2. [Bug] Optimize database queries with indexes and caching -3. [Bug] Implement lazy loading for dashboard widgets -4. [Bug] Add pagination to large data tables -5. [Task] Set up performance monitoring and alerts - -### Example 3: Infrastructure - CI/CD Pipeline - -**Epic:** Automated Deployment Pipeline - -**Tickets:** -1. [Task] Set up GitHub Actions workflow configuration -2. [Task] Implement automated testing in CI pipeline -3. [Task] Configure staging environment deployment -4. [Task] Implement blue-green production deployment -5. [Task] Add deployment rollback mechanism -6. [Task] Create deployment runbook and documentation - diff --git a/plugins/atlassian-rovo/skills/spec-to-backlog/agents/openai.yaml b/plugins/atlassian-rovo/skills/spec-to-backlog/agents/openai.yaml deleted file mode 100644 index bf1d48145..000000000 --- a/plugins/atlassian-rovo/skills/spec-to-backlog/agents/openai.yaml +++ /dev/null @@ -1,3 +0,0 @@ -interface: - display_name: "Spec to Backlog" - short_description: "Convert Confluence specs into Jira backlogs" diff --git a/plugins/atlassian-rovo/skills/spec-to-backlog/references/breakdown-examples.md b/plugins/atlassian-rovo/skills/spec-to-backlog/references/breakdown-examples.md deleted file mode 100644 index a40d326b7..000000000 --- a/plugins/atlassian-rovo/skills/spec-to-backlog/references/breakdown-examples.md +++ /dev/null @@ -1,327 +0,0 @@ -# Task Breakdown Examples - -This reference provides examples of effective task breakdowns for different types of specifications. - -## Principles of Good Breakdowns - -**DO:** -- Create tasks that are independently testable -- Group related frontend/backend work logically -- Include explicit testing and documentation tasks -- Use specific, actionable language -- Size tasks for 1-3 days of work typically - -**DON'T:** -- Create overly granular tasks (e.g., "Write one function") -- Make tasks too large (e.g., "Build entire feature") -- Duplicate work across multiple tickets -- Use vague descriptions (e.g., "Do backend stuff") - -## Example 1: New Feature - User Notifications System - -### Spec Summary -Add email and in-app notifications for user actions (comments, mentions, updates). - -### Good Breakdown (8 tasks) - -**Epic:** User Notifications System - -1. **Design notification data model and database schema** - - Define notification types and attributes - - Create database tables and indexes - - Document schema in API docs - -2. **Implement notification service backend** - - Create notification creation/retrieval APIs - - Add notification storage logic - - Implement marking notifications as read - -3. **Build email notification dispatcher** - - Set up email template system - - Implement async email sending queue - - Add email preferences handling - -4. **Create notification preferences API** - - User settings for notification types - - Email vs in-app preferences - - Frequency controls (immediate, digest) - -5. **Build notification UI components** - - Notification bell icon with unread count - - Notification dropdown panel - - Individual notification cards - -6. **Implement notification settings page** - - Frontend for user preferences - - Connect to preferences API - - Add toggle controls for notification types - -7. **Add notification triggers to existing features** - - Hook into comment system - - Hook into mention system - - Hook into update/edit events - -8. **Write tests and documentation** - - Unit tests for notification service - - Integration tests for email delivery - - Update user documentation - -### Why This Works -- Each task is independently completable -- Clear separation between backend, frontend, and integration -- Testing is explicit -- Tasks are sized appropriately (1-3 days each) - ---- - -## Example 2: Bug Fix - Payment Processing Errors - -### Spec Summary -Users report intermittent payment failures. Investigation shows timeout issues with payment gateway and inadequate error handling. - -### Good Breakdown (5 tasks) - -**Epic:** Fix Payment Processing Reliability - -1. **Investigate and document payment failure patterns** - - Analyze error logs and failure rates - - Document specific error scenarios - - Create reproduction steps - -2. **Implement payment gateway timeout handling** - - Add configurable timeout settings - - Implement retry logic with exponential backoff - - Add circuit breaker pattern - -3. **Improve payment error messaging** - - Enhance error categorization - - Add user-friendly error messages - - Log detailed errors for debugging - -4. **Add payment status reconciliation job** - - Create background job to verify payment status - - Handle stuck/pending payments - - Send notifications for payment issues - -5. **Add monitoring and alerting** - - Set up payment failure rate alerts - - Add dashboard for payment health metrics - - Document troubleshooting procedures - -### Why This Works -- Starts with investigation (important for bugs) -- Addresses root cause and symptoms -- Includes monitoring to prevent recurrence -- Each task delivers incremental value - ---- - -## Example 3: Infrastructure - Migration to New Database - -### Spec Summary -Migrate from PostgreSQL 12 to PostgreSQL 15, update queries to use new features, ensure zero downtime. - -### Good Breakdown (7 tasks) - -**Epic:** PostgreSQL 15 Migration - -1. **Set up PostgreSQL 15 staging environment** - - Provision new database instances - - Configure replication from production - - Verify data consistency - -2. **Audit and update database queries** - - Identify queries using deprecated features - - Update to PostgreSQL 15 syntax - - Optimize queries for new planner - -3. **Update application connection pooling** - - Upgrade database drivers - - Adjust connection pool settings - - Test connection handling under load - -4. **Create migration runbook** - - Document step-by-step migration process - - Define rollback procedures - - List success criteria and validation steps - -5. **Perform dry-run migration in staging** - - Execute full migration process - - Validate data integrity - - Measure downtime duration - - Test rollback procedure - -6. **Execute production migration** - - Follow migration runbook - - Monitor system health during migration - - Validate all services post-migration - -7. **Post-migration cleanup and monitoring** - - Remove old database instances after verification period - - Update monitoring dashboards - - Document lessons learned - -### Why This Works -- Emphasizes planning and validation -- Includes explicit dry-run -- Risk mitigation with rollback planning -- Clear separation between prep, execution, and cleanup - ---- - -## Example 4: API Development - Public REST API - -### Spec Summary -Create public REST API for third-party integrations. Include authentication, rate limiting, and documentation. - -### Good Breakdown (9 tasks) - -**Epic:** Public REST API v1 - -1. **Design API specification** - - Define endpoints and request/response schemas - - Create OpenAPI/Swagger specification - - Review with stakeholders - -2. **Implement API authentication system** - - Add API key generation and management - - Implement OAuth2 flow - - Create authentication middleware - -3. **Build rate limiting infrastructure** - - Implement token bucket algorithm - - Add per-key rate limit tracking - - Create rate limit headers and responses - -4. **Implement core API endpoints - Users** - - GET /users endpoints - - POST /users endpoints - - PUT/DELETE /users endpoints - -5. **Implement core API endpoints - Resources** - - GET /resources endpoints - - POST /resources endpoints - - PUT/DELETE /resources endpoints - -6. **Add API versioning support** - - Implement version routing - - Add deprecation headers - - Document versioning strategy - -7. **Create developer portal and documentation** - - Set up documentation site - - Add interactive API explorer - - Write getting started guide and examples - -8. **Build API monitoring and analytics** - - Track API usage metrics - - Add error rate monitoring - - Create usage dashboards for customers - -9. **Write integration tests and SDK examples** - - Create comprehensive API test suite - - Write example code in Python/JavaScript - - Document common integration patterns - -### Why This Works -- Separates authentication and rate limiting (critical infrastructure) -- Groups endpoints by resource type -- Documentation is a first-class task -- Monitoring and developer experience are explicit - ---- - -## Example 5: Frontend Redesign - Dashboard Modernization - -### Spec Summary -Redesign main dashboard with modern UI framework, improve performance, maintain feature parity. - -### Good Breakdown (8 tasks) - -**Epic:** Dashboard UI Modernization - -1. **Create new component library foundation** - - Set up new UI framework (e.g., React + Tailwind) - - Build reusable component primitives - - Establish design system tokens - -2. **Build dashboard layout and navigation** - - Implement responsive grid layout - - Create new navigation sidebar - - Add breadcrumb and header components - -3. **Rebuild analytics widgets** - - Port existing chart components - - Implement new data visualization library - - Add loading and error states - -4. **Rebuild data table components** - - Create sortable/filterable table - - Add pagination and search - - Implement column customization - -5. **Implement user settings panel** - - Dashboard customization options - - Widget arrangement and visibility - - Preferences persistence - -6. **Optimize performance and lazy loading** - - Implement code splitting - - Add lazy loading for heavy widgets - - Optimize bundle size - -7. **Add responsive mobile views** - - Create mobile-optimized layouts - - Test on various screen sizes - - Implement touch gestures - -8. **Migration and A/B testing setup** - - Create feature flag for new dashboard - - Set up A/B test framework - - Plan gradual rollout strategy - -### Why This Works -- Foundation first (component library) -- Groups by feature area (analytics, tables) -- Performance and mobile are explicit tasks -- Includes rollout strategy - ---- - -## Anti-Patterns to Avoid - -### Too Granular -❌ **Bad:** -- "Create User model" -- "Create User controller" -- "Create User view" -- "Write User tests" -- "Update User documentation" - -✅ **Better:** -- "Implement User management feature (model, controller, views, tests)" - -### Too Vague -❌ **Bad:** -- "Do backend work" -- "Fix frontend issues" -- "Update database" - -✅ **Better:** -- "Implement user authentication API endpoints" -- "Resolve navigation menu rendering bugs" -- "Add indexes to orders table for query performance" - -### Missing Testing -❌ **Bad:** -- Only feature implementation tasks, no testing mentioned - -✅ **Better:** -- Include explicit testing tasks or ensure testing is part of each feature task - -### No Clear Ownership -❌ **Bad:** -- Tasks that require both frontend and backend work without clear boundaries - -✅ **Better:** -- Split into "Backend API for X" and "Frontend UI for X" when different people work on each diff --git a/plugins/atlassian-rovo/skills/spec-to-backlog/references/epic-templates.md b/plugins/atlassian-rovo/skills/spec-to-backlog/references/epic-templates.md deleted file mode 100644 index cddafd2cd..000000000 --- a/plugins/atlassian-rovo/skills/spec-to-backlog/references/epic-templates.md +++ /dev/null @@ -1,401 +0,0 @@ -# Epic Description Templates - -Effective Epic descriptions provide context, goals, and success criteria. Use these templates based on the type of work. - -## Template 1: New Feature Epic - -```markdown -## Overview -[1-2 sentence description of what this Epic delivers] - -## Source Specification -[Link to Confluence page or design doc] - -## Business Value -[Why we're building this - user impact, business goals] - -## Success Criteria -- [ ] [Measurable outcome 1] -- [ ] [Measurable outcome 2] -- [ ] [Measurable outcome 3] - -## Technical Scope -- **Frontend**: [High-level frontend work] -- **Backend**: [High-level backend work] -- **Infrastructure**: [Any infrastructure needs] -- **Third-party**: [External integrations] - -## Out of Scope -- [Explicitly list what's NOT included to prevent scope creep] - -## Dependencies -- [List any blocking or related work] - -## Launch Plan -- **Target completion**: [Date or sprint] -- **Rollout strategy**: [All at once, gradual, A/B test, etc.] -``` - -### Example: User Notifications System - -```markdown -## Overview -Add comprehensive notification system supporting email and in-app notifications for user activity (comments, mentions, updates). - -## Source Specification -https://company.atlassian.net/wiki/spaces/PRODUCT/pages/123456/Notifications-Spec - -## Business Value -Users currently miss important updates, leading to delayed responses and reduced engagement. Notifications will increase daily active usage by an estimated 20% and improve user satisfaction scores. - -## Success Criteria -- [ ] Users receive email notifications within 5 minutes of trigger event -- [ ] In-app notifications appear in real-time (< 2 second delay) -- [ ] 80% of users enable at least one notification type -- [ ] Email delivery rate > 95% -- [ ] System handles 10,000 notifications/minute at peak - -## Technical Scope -- **Frontend**: Notification bell UI, preferences page, notification cards -- **Backend**: Notification service, email dispatcher, real-time delivery -- **Infrastructure**: Email service integration (SendGrid), websocket server -- **Third-party**: SendGrid for email delivery - -## Out of Scope -- Push notifications (mobile) - planned for Q2 -- SMS notifications - not in current roadmap -- Notification history beyond 30 days - -## Dependencies -- None - self-contained feature - -## Launch Plan -- **Target completion**: Sprint 24 (March 15) -- **Rollout strategy**: Gradual rollout, 10% → 50% → 100% over 1 week -``` - ---- - -## Template 2: Bug Fix Epic - -```markdown -## Problem Statement -[Clear description of the bug and its impact] - -## Source Documentation -[Link to Confluence investigation, incident report, or bug analysis] - -## Current Impact -- **Severity**: [Critical/High/Medium/Low] -- **Users affected**: [Percentage or number] -- **Frequency**: [How often it occurs] -- **Business impact**: [Revenue, reputation, etc.] - -## Root Cause -[Technical explanation of what's causing the issue] - -## Solution Approach -[High-level approach to fixing the issue] - -## Success Criteria -- [ ] [Bug no longer reproducible] -- [ ] [Related edge cases handled] -- [ ] [Monitoring in place to detect recurrence] - -## Verification Plan -[How we'll confirm the fix works] -``` - -### Example: Payment Processing Failures - -```markdown -## Problem Statement -Users experiencing intermittent payment failures during checkout, resulting in abandoned transactions and support tickets. Error rate spiked to 8% on Nov 15, up from baseline 0.5%. - -## Source Documentation -https://company.atlassian.net/wiki/spaces/ENG/pages/789012/Payment-Failure-Investigation - -## Current Impact -- **Severity**: Critical -- **Users affected**: ~800 customers per day -- **Frequency**: 8% of all payment attempts -- **Business impact**: $45K/day in lost revenue, customer trust erosion - -## Root Cause -Payment gateway timeouts due to insufficient timeout settings (5s) and no retry logic. During high load, 3rd party payment API occasionally takes 6-8s to respond, causing failures. - -## Solution Approach -1. Increase timeout to 15s with exponential backoff retry -2. Implement circuit breaker to prevent cascade failures -3. Add payment reconciliation job to handle stuck transactions -4. Improve error messaging for users - -## Success Criteria -- [ ] Payment failure rate below 1% -- [ ] Zero timeout-related failures -- [ ] 100% of stuck payments reconciled within 15 minutes -- [ ] User-facing error messages are clear and actionable - -## Verification Plan -- Load testing with simulated gateway delays -- Monitor production metrics for 1 week post-deployment -- Review support tickets for payment-related issues -``` - ---- - -## Template 3: Infrastructure/Technical Epic - -```markdown -## Objective -[What infrastructure change or technical improvement we're making] - -## Source Documentation -[Link to technical design doc or RFC] - -## Current State -[Description of existing system/approach] - -## Target State -[Description of desired system/approach after completion] - -## Motivation -[Why we need to make this change - performance, cost, maintainability, etc.] - -## Success Criteria -- [ ] [Technical metric 1] -- [ ] [Technical metric 2] -- [ ] [Zero downtime or minimal disruption] - -## Risk Mitigation -- **Rollback plan**: [How to revert if issues occur] -- **Monitoring**: [What metrics we'll watch] -- **Testing strategy**: [Dry runs, canary deployments, etc.] - -## Timeline Constraints -[Any time-sensitive factors like deprecations, costs] -``` - -### Example: PostgreSQL Migration - -```markdown -## Objective -Migrate primary database from PostgreSQL 12 to PostgreSQL 15 to leverage performance improvements and new features before PostgreSQL 12 EOL. - -## Source Documentation -https://company.atlassian.net/wiki/spaces/ENG/pages/345678/PG15-Migration-RFC - -## Current State -Running PostgreSQL 12.8 on AWS RDS with 2TB data, 50K queries/minute at peak. Some queries use deprecated features. - -## Target State -PostgreSQL 15.2 with optimized queries, improved query planner, and better connection pooling. Estimated 15-20% performance improvement on read-heavy queries. - -## Motivation -- PostgreSQL 12 reaches EOL in November 2024 -- PG15 query planner improvements will reduce latency on dashboard queries -- New features enable better monitoring and troubleshooting -- Cost savings: ~$800/month from improved efficiency - -## Success Criteria -- [ ] Zero data loss during migration -- [ ] < 5 minutes of downtime during cutover -- [ ] All application queries working correctly -- [ ] Query performance same or better than PG12 -- [ ] Monitoring confirms system health for 2 weeks - -## Risk Mitigation -- **Rollback plan**: Keep PG12 instance available for 2 weeks; can revert in < 15 minutes -- **Monitoring**: Track query latency, error rates, connection pool health -- **Testing strategy**: Full migration dry-run in staging, 24-hour soak test - -## Timeline Constraints -Must complete by October 2024 (1 month before PG12 EOL). Testing requires 3 weeks. -``` - ---- - -## Template 4: API Development Epic - -```markdown -## Overview -[What API or integration we're building] - -## Source Specification -[Link to API design doc or requirements] - -## Use Cases -[Primary scenarios this API will enable] - -## API Design -- **Authentication**: [Method - API keys, OAuth, etc.] -- **Rate limiting**: [Limits and quotas] -- **Versioning**: [Strategy] -- **Base URL**: [Endpoint structure] - -## Endpoints Summary -[High-level list of main endpoint categories] - -## Success Criteria -- [ ] [API stability metric] -- [ ] [Performance target] -- [ ] [Documentation completeness] -- [ ] [Developer adoption metric] - -## Documentation Deliverables -- [ ] OpenAPI/Swagger spec -- [ ] Getting started guide -- [ ] Code examples (Python, JavaScript) -- [ ] Interactive API explorer - -## Timeline -- **Beta release**: [Date] -- **GA release**: [Date] -``` - -### Example: Public REST API - -```markdown -## Overview -Launch v1 of public REST API enabling third-party developers to integrate with our platform for user management and resource access. - -## Source Specification -https://company.atlassian.net/wiki/spaces/API/pages/456789/Public-API-v1-Spec - -## Use Cases -- SaaS companies integrating our user management into their products -- Data analytics tools pulling resource data -- Automation platforms connecting workflows -- Mobile app developers building custom clients - -## API Design -- **Authentication**: OAuth 2.0 + API keys -- **Rate limiting**: 1,000 requests/hour per API key (higher tiers available) -- **Versioning**: URI-based (/v1/, /v2/) -- **Base URL**: https://api.company.com/v1 - -## Endpoints Summary -- User management (CRUD operations) -- Resource access (read-only initially) -- Webhooks for event notifications -- Account administration - -## Success Criteria -- [ ] 99.9% uptime -- [ ] p95 latency < 200ms -- [ ] Complete OpenAPI documentation -- [ ] 50+ developers signed up for beta -- [ ] Zero security vulnerabilities in initial audit - -## Documentation Deliverables -- [x] OpenAPI/Swagger spec -- [ ] Getting started guide -- [ ] Code examples (Python, JavaScript, Ruby) -- [ ] Interactive API explorer (Swagger UI) -- [ ] Authentication tutorial -- [ ] Best practices guide - -## Timeline -- **Beta release**: February 15 (invite-only, 10 partners) -- **GA release**: March 30 (public availability) -``` - ---- - -## Template 5: Redesign/Modernization Epic - -```markdown -## Overview -[What's being redesigned and why] - -## Source Documentation -[Link to design specs, mockups, or requirements] - -## Current Pain Points -- [Problem 1 with existing implementation] -- [Problem 2 with existing implementation] -- [Problem 3 with existing implementation] - -## New Design Goals -- [Goal 1] -- [Goal 2] -- [Goal 3] - -## Success Criteria -- [ ] [User experience metric] -- [ ] [Performance improvement] -- [ ] [Feature parity or improvements] -- [ ] [Accessibility standards met] - -## Migration Strategy -[How users transition from old to new] - -## Rollout Plan -[Phased rollout, A/B testing, feature flags] -``` - -### Example: Dashboard Modernization - -```markdown -## Overview -Redesign main analytics dashboard with modern UI framework, improved performance, and better mobile support while maintaining all existing functionality. - -## Source Documentation -https://company.atlassian.net/wiki/spaces/DESIGN/pages/567890/Dashboard-Redesign - -## Current Pain Points -- Slow initial load time (4-6 seconds) -- Poor mobile experience (not responsive) -- Outdated UI feels "legacy" -- Difficult to customize widget layout -- Accessibility issues (WCAG 2.1 violations) - -## New Design Goals -- Modern, clean visual design aligned with brand refresh -- < 2 second initial load time -- Fully responsive (desktop, tablet, mobile) -- Customizable dashboard layouts -- WCAG 2.1 AA compliant -- Improved data visualization clarity - -## Success Criteria -- [ ] Initial load time < 2s (50% improvement) -- [ ] Perfect Lighthouse score (90+) -- [ ] Zero WCAG 2.1 AA violations -- [ ] 80% user approval rating in beta test -- [ ] Feature parity with legacy dashboard -- [ ] Mobile usage increases by 30% - -## Migration Strategy -- Side-by-side availability during transition -- Users can switch between old/new with toggle -- Preferences automatically migrated -- 30-day sunset period for legacy dashboard - -## Rollout Plan -1. Week 1: Internal beta (engineering team) -2. Week 2-3: Customer beta (10% of users via feature flag) -3. Week 4: Expand to 50% of users -4. Week 5: 100% rollout, legacy available via toggle -5. Week 9: Remove legacy dashboard -``` - ---- - -## Key Elements in Every Epic - -Regardless of template, ensure every Epic includes: - -1. **Clear objective** - Anyone should understand what's being built/fixed -2. **Source link** - Always link to the Confluence spec or design doc -3. **Success criteria** - Measurable outcomes that define "done" -4. **Scope clarity** - What IS and ISN'T included -5. **Context** - Enough background for someone new to understand why this matters - -## Common Mistakes to Avoid - -❌ **Too brief**: "Build notifications" - lacks context -❌ **Too detailed**: Including implementation details that belong in tickets -❌ **No success criteria**: How do we know when it's done? -❌ **Missing source link**: Hard to trace back to requirements -❌ **Vague scope**: Leads to scope creep and confusion diff --git a/plugins/atlassian-rovo/skills/spec-to-backlog/references/ticket-writing-guide.md b/plugins/atlassian-rovo/skills/spec-to-backlog/references/ticket-writing-guide.md deleted file mode 100644 index d381843e4..000000000 --- a/plugins/atlassian-rovo/skills/spec-to-backlog/references/ticket-writing-guide.md +++ /dev/null @@ -1,354 +0,0 @@ -# Ticket Writing Guide - -Guidelines for creating clear, actionable Jira tickets with effective summaries and descriptions. - -## Summary Guidelines - -The ticket summary should be a clear, concise action statement that immediately tells someone what needs to be done. - -### Formula - -**[Action Verb] + [Component/Feature] + [Optional: Context]** - -### Good Examples - -✅ "Implement user registration API endpoint" -✅ "Fix pagination bug in search results" -✅ "Add email validation to signup form" -✅ "Optimize database query for dashboard load time" -✅ "Create documentation for payment webhook" -✅ "Design user preferences data schema" - -### Bad Examples - -❌ "Users" - Not actionable -❌ "Do backend work" - Too vague -❌ "Fix bug" - Lacks specificity -❌ "API" - Not a task -❌ "There's an issue with the login page that needs to be addressed" - Too wordy - -### Action Verbs by Task Type - -**Development:** -- Implement, Build, Create, Add, Develop - -**Bug Fixes:** -- Fix, Resolve, Correct, Debug - -**Design/Planning:** -- Design, Plan, Research, Investigate, Define - -**Infrastructure:** -- Set up, Configure, Deploy, Migrate, Upgrade - -**Documentation:** -- Write, Document, Update, Create - -**Improvement:** -- Optimize, Refactor, Improve, Enhance - -**Testing:** -- Test, Verify, Validate - ---- - -## Description Structure - -A good ticket description provides context, requirements, and guidance without being overwhelming. - -### Recommended Template - -```markdown -## Context -[1-2 sentences: Why we're doing this, what problem it solves] - -## Requirements -- [Specific requirement 1] -- [Specific requirement 2] -- [Specific requirement 3] - -## Technical Notes -[Any technical constraints, preferred approaches, or implementation hints] - -## Acceptance Criteria -- [ ] [Testable outcome 1] -- [ ] [Testable outcome 2] -- [ ] [Testable outcome 3] - -## Resources -- [Link to design mockup if applicable] -- [Link to API documentation] -- [Link to related tickets] -``` - -### Example 1: Feature Implementation - -**Summary:** Implement user registration API endpoint - -**Description:** -```markdown -## Context -Users need to create accounts through our REST API. This endpoint will be used by our web app and future mobile apps. - -## Requirements -- Accept email, password, and name via POST request -- Validate email format and uniqueness -- Hash password using bcrypt -- Return JWT token for immediate authentication -- Send welcome email asynchronously - -## Technical Notes -- Use existing email service for welcome emails -- Follow authentication patterns from login endpoint -- Rate limit: 5 registration attempts per IP per hour - -## Acceptance Criteria -- [ ] Endpoint accepts valid registration data and returns 201 with JWT -- [ ] Duplicate email returns 409 error -- [ ] Invalid email format returns 400 error -- [ ] Password must be 8+ characters -- [ ] Welcome email sent within 1 minute -- [ ] Unit tests cover happy path and error cases - -## Resources -- API Spec: https://company.atlassian.net/wiki/API-Design -- Related: AUTH-123 (Login endpoint) -``` - -### Example 2: Bug Fix - -**Summary:** Fix pagination bug in search results - -**Description:** -```markdown -## Context -Users report that clicking "Next Page" in search results sometimes shows duplicate items from the previous page. This happens intermittently when search results are sorted by date. - -## Problem -The pagination offset calculation doesn't account for items with identical timestamps, causing cursor position drift when using timestamp-based pagination. - -## Requirements -- Ensure each search result appears exactly once -- Maintain current sort order (date descending) -- Fix applies to all search endpoints - -## Technical Notes -- Current implementation uses timestamp as cursor: `?cursor=2024-01-15T10:30:00Z` -- Suggested fix: Composite cursor using timestamp + ID -- Consider adding unique index on (timestamp, id) for better query performance - -## Acceptance Criteria -- [ ] No duplicate items appear across paginated results -- [ ] Pagination works correctly with items having identical timestamps -- [ ] All existing search API tests still pass -- [ ] Added test case reproducing the original bug -- [ ] Performance impact < 5ms per query - -## Resources -- Bug Report: https://company.atlassian.net/wiki/BUG-456 -- Related: SEARCH-789 (Original search implementation) -``` - -### Example 3: Infrastructure Task - -**Summary:** Set up PostgreSQL 15 staging environment - -**Description:** -```markdown -## Context -First step in database migration from PG12 to PG15. Need staging environment to validate migration process and test query compatibility. - -## Requirements -- Provision PG15 instance matching production specs -- Set up replication from production to staging -- Configure backup retention (7 days) -- Enable query logging for testing - -## Technical Notes -- Use AWS RDS PostgreSQL 15.2 -- Instance type: db.r6g.2xlarge (same as prod) -- Enable logical replication for zero-downtime testing -- VPC: staging-vpc-us-east-1 - -## Acceptance Criteria -- [ ] PG15 instance running and accessible from staging apps -- [ ] Replication lag < 30 seconds from production -- [ ] Can connect using standard credentials -- [ ] Query logs enabled and viewable -- [ ] Monitoring dashboards created -- [ ] Backup configured and tested (restore test) - -## Resources -- Migration RFC: https://company.atlassian.net/wiki/PG15-Migration -- Infrastructure docs: https://wiki/Database-Setup -- Parent Epic: INFRA-100 -``` - -### Example 4: Frontend Task - -**Summary:** Create notification bell UI component - -**Description:** -```markdown -## Context -Part of notification system. Need UI component showing unread notification count and opening notification panel. - -## Requirements -- Bell icon in top navigation bar -- Display unread count badge (e.g., "5") -- Click opens notification dropdown panel -- Real-time updates via WebSocket -- Badge turns red for urgent notifications - -## Technical Notes -- Use existing Icon component library -- WebSocket events: 'notification:new', 'notification:read' -- State management: Context API or Zustand -- Position: Right side of nav, left of user avatar - -## Acceptance Criteria -- [ ] Bell icon displays in navigation bar -- [ ] Unread count badge shows accurate count -- [ ] Badge updates in real-time when new notification arrives -- [ ] Click opens/closes notification panel -- [ ] No badge shown when count is 0 -- [ ] Component is accessible (keyboard navigation, screen reader) -- [ ] Responsive design (mobile, tablet, desktop) - -## Resources -- Design mockup: [Figma link] -- WebSocket docs: https://wiki/Notifications-API -- Related: NOTIF-123 (Notification panel component) -``` - ---- - -## Descriptions by Task Type - -### Backend Development - -Focus on: -- API contract (request/response format) -- Data validation rules -- Error handling requirements -- Performance expectations -- Security considerations - -### Frontend Development - -Focus on: -- Visual design reference -- User interactions -- State management approach -- Responsive behavior -- Accessibility requirements - -### Bug Fixes - -Focus on: -- Reproduction steps -- Expected vs actual behavior -- Root cause (if known) -- Affected users/scenarios -- Verification approach - -### Testing - -Focus on: -- What needs testing (features, edge cases) -- Test coverage targets -- Types of tests (unit, integration, e2e) -- Performance benchmarks -- Test data requirements - -### Documentation - -Focus on: -- Target audience -- Required sections/topics -- Examples to include -- Existing docs to update -- Review/approval process - ---- - -## Acceptance Criteria Best Practices - -Acceptance criteria should be: - -1. **Testable** - Can verify by testing or observation -2. **Specific** - No ambiguity about what "done" means -3. **Complete** - Covers all requirements in description -4. **User-focused** - When possible, frame from user perspective - -### Good Acceptance Criteria - -✅ "User can submit form and receive confirmation email within 30 seconds" -✅ "API returns 400 error when email field is empty" -✅ "Dashboard loads in under 2 seconds on 3G connection" -✅ "All text meets WCAG 2.1 AA contrast ratios" - -### Bad Acceptance Criteria - -❌ "Feature works well" - Not specific -❌ "Code is clean" - Subjective, not testable -❌ "Fast performance" - Not measurable -❌ "No bugs" - Too broad - ---- - -## Technical Notes Guidelines - -Use "Technical Notes" section for: - -- **Architectural decisions**: "Use Redis for session caching" -- **Implementation hints**: "Follow pattern from UserService class" -- **Performance constraints**: "Query must complete in < 100ms" -- **Security requirements**: "Use parameterized queries to prevent SQL injection" -- **Dependencies**: "Requires AUTH-456 to be deployed first" -- **Gotchas**: "Watch out for timezone handling in date comparisons" - -Keep it concise - detailed technical specs belong in Confluence or code comments. - ---- - -## Common Mistakes to Avoid - -### 1. Information Overload -❌ Pages of requirements copied from spec doc -✅ Summary with link to full spec - -### 2. Assuming Context -❌ "Fix the bug we discussed" -✅ Clear description of the bug with reproduction steps - -### 3. Implementation as Requirement -❌ "Use React hooks for state management" -✅ "Component updates in real-time" (let developer choose approach unless there's a specific reason) - -### 4. Vague Acceptance Criteria -❌ "Everything works correctly" -✅ Specific, testable outcomes - -### 5. Missing Links -❌ No reference to designs, specs, or related work -✅ Links to all relevant documentation - ---- - -## Length Guidelines - -**Summary:** -- Target: 3-8 words -- Max: 12 words - -**Description:** -- Target: 100-300 words -- Min: Include at minimum context and acceptance criteria -- Max: 500 words (link to docs for more detail) - -**Acceptance Criteria:** -- Target: 3-7 items -- Each item: 1 sentence - -Remember: Ticket descriptions are not documentation. They're instructions for completing a specific task. diff --git a/plugins/atlassian-rovo/skills/triage-issue/SKILL.md b/plugins/atlassian-rovo/skills/triage-issue/SKILL.md deleted file mode 100644 index 3b8665da8..000000000 --- a/plugins/atlassian-rovo/skills/triage-issue/SKILL.md +++ /dev/null @@ -1,700 +0,0 @@ ---- -name: triage-issue -description: "Intelligently triage bug reports and error messages by searching for duplicates in Jira and offering to create new issues or add comments to existing ones. When an agent needs to: (1) Triage a bug report or error message, (2) Check if an issue is a duplicate, (3) Find similar past issues, (4) Create a new bug ticket with proper context, or (5) Add information to an existing ticket. Searches Jira for similar issues, identifies duplicates, checks fix history, and helps create well-structured bug reports." ---- - -# Triage Issue - -## Keywords -triage bug, check duplicate, is this a duplicate, search for similar issues, create bug ticket, file a bug, report this error, triage this error, bug report, error message, similar issues, duplicate bug, who fixed this, has this been reported, search bugs, find similar bugs, create issue, file issue - -## Overview - -Automatically triage bug reports and error messages by searching Jira for duplicates, identifying similar past issues, and helping create well-structured bug tickets or add context to existing issues. This skill eliminates manual duplicate checking and ensures bugs are properly documented with relevant historical context. - -**Use this skill when:** Users need to triage error messages, bug reports, or issues to determine if they're duplicates and take appropriate action. - ---- - -## Workflow - -Follow this 6-step process to effectively triage issues: - -### Step 1: Extract Key Information - -Analyze the bug report or error message to identify search terms. - -#### Extract These Elements: - -**Error signature:** -- Error type or exception name (e.g., "NullPointerException", "TimeoutError") -- Error code or status (e.g., "500", "404", "ERR_CONNECTION_REFUSED") -- Specific error message text (key phrases, not full stack trace) - -**Context:** -- Component or system affected (e.g., "authentication", "payment gateway", "API") -- Environment (e.g., "production", "staging", "mobile app") -- User actions leading to error (e.g., "during login", "when uploading file") - -**Symptoms:** -- Observable behavior (e.g., "page blank", "infinite loading", "data not saving") -- Impact (e.g., "users can't login", "payments failing") - -#### Example Extractions: - -**Input:** "Users getting 'Connection timeout' error when trying to login on mobile app" -**Extracted:** -- Error: "Connection timeout" -- Component: "login", "mobile app" -- Symptom: "can't login" - -**Input:** "NullPointerException in PaymentProcessor.processRefund() line 245" -**Extracted:** -- Error: "NullPointerException" -- Component: "PaymentProcessor", "refund" -- Location: "processRefund line 245" - ---- - -### Step 2: Search for Duplicates - -Search Jira using extracted keywords to find similar or duplicate issues. - -#### Search Strategy: - -Execute **multiple targeted searches** to catch duplicates that may use different wording: - -**Search 1: Error-focused** -``` -searchJiraIssuesUsingJql( - cloudId="...", - jql='project = "PROJ" AND (text ~ "error signature" OR summary ~ "error signature") AND type = Bug ORDER BY created DESC', - fields=["summary", "description", "status", "resolution", "created", "updated", "assignee"], - maxResults=20 -) -``` - -**Search 2: Component-focused** -``` -searchJiraIssuesUsingJql( - cloudId="...", - jql='project = "PROJ" AND text ~ "component keywords" AND type = Bug ORDER BY updated DESC', - fields=["summary", "description", "status", "resolution", "created", "updated", "assignee"], - maxResults=20 -) -``` - -**Search 3: Symptom-focused** -``` -searchJiraIssuesUsingJql( - cloudId="...", - jql='project = "PROJ" AND summary ~ "symptom keywords" AND type = Bug ORDER BY priority DESC, updated DESC', - fields=["summary", "description", "status", "resolution", "created", "updated", "assignee"], - maxResults=20 -) -``` - -#### Search Tips: - -**Use key terms only:** -- ✅ "timeout login mobile" -- ✅ "NullPointerException PaymentProcessor refund" -- ❌ "Users are getting a connection timeout error when..." (too verbose) - -**Search recent first:** -- Order by `created DESC` or `updated DESC` to find recent similar issues -- Recent bugs are more likely to be relevant duplicates - -**Don't over-filter:** -- Include resolved issues (might have been reopened or regression) -- Search across all bug statuses to find fix history - ---- - -### Step 3: Analyze Search Results - -Evaluate the search results to determine if this is a duplicate or a new issue. - -#### Duplicate Detection: - -**High confidence duplicate (>90%):** -- Exact same error message in summary or description -- Same component + same error type -- Recent issue (< 30 days) with identical symptoms -- **Action:** Strongly recommend adding comment to existing issue - -**Likely duplicate (70-90%):** -- Similar error with slight variations -- Same component but different context -- Resolved issue with same root cause -- **Action:** Present as possible duplicate, let user decide - -**Possibly related (40-70%):** -- Similar symptoms but different error -- Same component area but different specific error -- Old issue (> 6 months) that might be unrelated -- **Action:** Mention as potentially related - -**Likely new issue (<40%):** -- No similar issues found -- Different error signature and component -- Unique symptom or context -- **Action:** Recommend creating new issue - -#### Check Fix History: - -If similar resolved issues are found: - -**Extract relevant information:** -- Who fixed it? (assignee on resolved issues) -- How was it fixed? (resolution comment or linked PRs) -- When was it fixed? (resolution date) -- Has it regressed? (any reopened issues) - -**Present this context** to help with triage decision. - ---- - -### Step 4: Present Findings to User - -**CRITICAL:** Always present findings and wait for user decision before taking any action. - -#### Format for Likely Duplicate: - -``` -🔍 **Triage Results: Likely Duplicate** - -I found a very similar issue already reported: - -**PROJ-456** - Connection timeout during mobile login -Status: Open | Priority: High | Created: 3 days ago -Assignee: @john.doe -https://yoursite.atlassian.net/browse/PROJ-456 - -**Similarity:** -- Same error: "Connection timeout" -- Same component: Mobile app login -- Same symptoms: Users unable to login - -**Difference:** -- Original report mentioned iOS specifically, this report doesn't specify platform - -**Recommendation:** Add your details as a comment to PROJ-456 - -Would you like me to: -1. Add a comment to PROJ-456 with your error details -2. Create a new issue anyway (if you think this is different) -3. Show me more details about PROJ-456 first -``` - -#### Format for Possibly Related: - -``` -🔍 **Triage Results: Possibly Related Issues Found** - -I found 2 potentially related issues: - -**1. PROJ-789** - Mobile app authentication failures -Status: Resolved | Fixed: 2 weeks ago | Fixed by: @jane.smith -https://yoursite.atlassian.net/browse/PROJ-789 - -**2. PROJ-234** - Login timeout on slow connections -Status: Open | Priority: Medium | Created: 1 month ago -https://yoursite.atlassian.net/browse/PROJ-234 - -**Assessment:** Your error seems related but has unique aspects - -**Recommendation:** Create a new issue, but reference these related tickets - -Would you like me to create a new bug ticket? -``` - -#### Format for No Duplicates: - -``` -🔍 **Triage Results: No Duplicates Found** - -I searched Jira for: -- "Connection timeout" errors -- Mobile login issues -- Authentication failures - -No similar open or recent issues found. - -**Recommendation:** Create a new bug ticket - -**Note:** I found 1 old resolved issue (PROJ-123 from 8 months ago) about login timeouts, but it was for web, not mobile, and was resolved as "configuration error." - -Would you like me to create a new bug ticket for this issue? -``` - ---- - -### Step 5: Execute User Decision - -Based on user's choice, either add a comment or create a new issue. - -#### Option A: Add Comment to Existing Issue - -If user wants to add to existing issue: - -**Fetch the full issue first** to understand context: -``` -getJiraIssue( - cloudId="...", - issueIdOrKey="PROJ-456" -) -``` - -**Then add the comment:** -``` -addCommentToJiraIssue( - cloudId="...", - issueIdOrKey="PROJ-456", - commentBody="[formatted comment - see below]" -) -``` - -**Comment Structure:** -```markdown -## Additional Instance Reported - -**Reporter:** [User's name or context] -**Date:** [Current date] - -**Error Details:** -[Paste relevant error message or stack trace] - -**Context:** -- Environment: [e.g., Production, iOS 16.5] -- User Impact: [e.g., 50+ users affected in last hour] -- Steps to Reproduce: [if provided] - -**Additional Notes:** -[Any unique aspects of this instance] - ---- -*Added via triage automation* -``` - -#### Option B: Create New Issue - -If user wants to create new issue: - -**First, check available issue types:** -``` -getJiraProjectIssueTypesMetadata( - cloudId="...", - projectIdOrKey="PROJ" -) -``` - -**Determine appropriate issue type:** -- For bugs/errors → Use "Bug" (if available) -- For issues without errors → Use "Task" or "Issue" -- Fallback → First available non-Epic, non-Subtask type - -**Create the issue:** -``` -createJiraIssue( - cloudId="...", - projectKey="PROJ", - issueTypeName="Bug", - summary="[Clear, specific summary - see below]", - description="[Detailed description - see below]", - additional_fields={ - "priority": {"name": "Medium"} # Adjust based on user input severity assessment - } -) -``` - -**Summary Format:** -Use the pattern: `[Component] [Error Type] - [Brief Symptom]` - -**Examples:** -- ✅ "Mobile Login: Connection timeout during authentication" -- ✅ "Payment API: NullPointerException in refund processing" -- ✅ "Dashboard: Infinite loading on reports page" -- ❌ "Error in production" (too vague) -- ❌ "Users experiencing issues" (not specific) - -**Description Structure:** -```markdown -## Issue Description -[1-2 sentence summary of the problem] - -## Error Details -``` -[Error message or stack trace] -``` - -## Environment -- **Platform:** [e.g., Mobile iOS, Web, API] -- **Version:** [if known] -- **Environment:** [Production/Staging/etc] - -## Steps to Reproduce -1. [Step 1] -2. [Step 2] -3. [Step 3] - -## Expected Behavior -[What should happen] - -## Actual Behavior -[What actually happens] - -## User Impact -- **Frequency:** [e.g., Every time, Intermittent] -- **Affected Users:** [e.g., All users, Mobile users only] -- **Severity:** [e.g., Users cannot complete checkout] - -## Additional Context -[Any other relevant information] - -## Related Issues -[If applicable, reference similar issues found during triage] -- See also: PROJ-123 (similar but resolved) - ---- -*Created via automated triage* -``` - ---- - -### Step 6: Provide Summary - -After taking action, confirm what was done. - -#### If Comment Added: - -``` -✅ **Comment Added Successfully** - -Added details to existing issue: **PROJ-456** -https://yoursite.atlassian.net/browse/PROJ-456 - -**What I included:** -- Your error details -- Environment context -- User impact information - -**Next Steps:** -- The assignee (@john.doe) will be notified -- Monitor PROJ-456 for updates -- If this turns out to be different, we can still create a separate ticket -``` - -#### If New Issue Created: - -``` -✅ **New Issue Created** - -**PROJ-890** - Mobile Login: Connection timeout during authentication -https://yoursite.atlassian.net/browse/PROJ-890 - -Type: Bug | Priority: Medium | Status: Open - -**What's Included:** -- Complete error details and stack trace -- Environment and reproduction steps -- User impact assessment -- References to related issues (PROJ-123, PROJ-789) - -**Next Steps:** -- Issue will be reviewed during next triage meeting -- Consider setting priority based on user impact -- Assign to appropriate team member -``` - ---- - -## Edge Cases & Troubleshooting - -### Multiple Potential Duplicates - -If you find 3+ very similar issues: - -``` -🔍 **Triage Results: Multiple Possible Duplicates** - -I found 3 issues that might be related: - -**Most Likely Duplicate:** -**PROJ-456** - [summary] (Open, 2 days ago) -Match: 95% - Same error and component - -**Also Possibly Related:** -**PROJ-234** - [summary] (Open, 1 month ago) -Match: 70% - Similar symptoms, different error - -**PROJ-123** - [summary] (Resolved, 8 months ago) -Match: 60% - Related component, but was fixed - -**Recommendation:** -1. Add to PROJ-456 (most similar) -2. Reference PROJ-234 in your comment (might be related) -3. Note PROJ-123 was previously fixed (possible regression) - -Which issue should I add your details to? (Or create new) -``` - -### Unclear Project Context - -If user doesn't specify which project: - -``` -Which Jira project should I search for similar issues? - -Available projects: -- BACKEND (Backend Services) -- MOBILE (Mobile Apps) -- WEB (Web Platform) -- INFRA (Infrastructure) - -Or tell me the project key directly (e.g., BACKEND, MOBILE) -``` - -### Insufficient Information - -If the bug report lacks critical details: - -``` -I need more information to effectively triage this issue: - -**What I have:** -- Error: "Something went wrong" - -**What would help:** -- Specific error message or code -- Which part of the system (login, payment, etc.) -- What the user was trying to do -- Which environment (production, staging, mobile, web) - -Can you provide any of these details? -``` - -### Resolved Regression - -If you find a resolved issue that matches: - -``` -🔍 **Triage Results: Possible Regression** - -This looks like it might be a regression of a previously fixed issue: - -**PROJ-567** - [Same issue description] -Status: Resolved (Fixed) | Fixed: 3 months ago | Fixed by: @jane.smith -Resolution: [Brief description of fix] -https://yoursite.atlassian.net/browse/PROJ-567 - -**This suggests:** -- The original fix may not have fully addressed the root cause -- OR there's been a regression in recent changes -- OR this is a different issue with similar symptoms - -**Recommendation:** Create a new issue and link it to PROJ-567 as "may be related to" or "regression of" - -Should I create a new issue with this context? -``` - -### Custom Required Fields - -If creating an issue fails due to required fields: - -1. **Check what fields are required:** -``` -getJiraIssueTypeMetaWithFields( - cloudId="...", - projectIdOrKey="PROJ", - issueTypeId="10001" -) -``` - -2. **Ask user for values:** -``` -This project requires additional fields to create a Bug: -- Severity: [High/Medium/Low] -- Affected Version: [Version number] - -Please provide these values so I can create the issue. -``` - -3. **Retry with additional fields:** -``` -createJiraIssue( - ...existing parameters..., - additional_fields={ - "priority": {"name": "High"}, - "customfield_10001": {"value": "Production"} - } -) -``` - ---- - -## Tips for Effective Triage - -### For Search: - -**Do:** -✅ Use multiple search queries with different angles -✅ Include both open and resolved issues in search -✅ Search for error signatures and symptoms separately -✅ Look at recent issues first (last 30-90 days) -✅ Check for patterns (multiple reports of same thing) - -**Don't:** -❌ Search with entire error messages (too specific) -❌ Only search open issues (miss fix history) -❌ Ignore resolved issues (miss regressions) -❌ Use too many keywords (reduces matches) - -### For Issue Creation: - -**Do:** -✅ Write clear, specific summaries with component names -✅ Include complete error messages in code blocks -✅ Add environment and impact details -✅ Reference related issues found during search -✅ Use "Bug" issue type for actual bugs - -**Don't:** -❌ Create vague summaries like "Error in production" -❌ Paste entire stack traces in summary (use description) -❌ Skip reproduction steps -❌ Forget to mention user impact -❌ Hard-code issue type without checking availability - -### For Duplicate Assessment: - -**High Confidence Duplicates:** -- Exact same error + same component + recent (< 30 days) -- Same root cause identified - -**Likely Different Issues:** -- Different error signatures -- Different components/systems -- Significantly different contexts - -**When Unsure:** -- Present both options to user -- Lean toward creating new issue (can be closed as duplicate later) -- Linking issues is better than hiding information - ---- - -## Examples - -### Example 1: Clear Duplicate Found - -**User Input:** -``` -Triage this error: "Connection timeout error when users try to login on iOS app" -``` - -**Process:** -1. Extract: "Connection timeout", "login", "iOS" -2. Search: Find PROJ-456 (open, 2 days ago) with exact same error -3. Analyze: 95% match - same error, component, symptom -4. Present: Show PROJ-456 as duplicate, recommend adding comment -5. Execute: User confirms, add comment with iOS-specific details -6. Confirm: Comment added to PROJ-456 - -**Output:** -``` -✅ Comment added to PROJ-456 - -Your iOS-specific error details have been added to the existing issue. -The assignee will be notified. -``` - -### Example 2: New Issue with Related Context - -**User Input:** -``` -Error: NullPointerException in PaymentProcessor.processRefund() at line 245 -Stack trace: [full stack trace] -``` - -**Process:** -1. Extract: "NullPointerException", "PaymentProcessor", "processRefund", "line 245" -2. Search: Find PROJ-789 (resolved, 3 weeks ago) about payment errors, but different line -3. Analyze: Related component but different specific error -4. Present: No duplicates, found related issue, recommend new ticket -5. Execute: User confirms, create new Bug with context -6. Confirm: PROJ-890 created - -**Output:** -``` -✅ New Issue Created - -PROJ-890 - Payment API: NullPointerException in refund processing -https://yoursite.atlassian.net/browse/PROJ-890 - -References related issue PROJ-789 for context. -``` - -### Example 3: Possible Regression - -**User Input:** -``` -Users can't upload files larger than 5MB, getting "Upload failed" error -``` - -**Process:** -1. Extract: "Upload failed", "5MB", "file upload" -2. Search: Find PROJ-234 (resolved 2 months ago) - exact same issue -3. Analyze: Was fixed but now happening again -4. Present: Possible regression, recommend new issue linked to old one -5. Execute: Create new issue, link to PROJ-234 as "may be caused by" -6. Confirm: PROJ-891 created with regression context - -**Output:** -``` -✅ New Issue Created (Possible Regression) - -PROJ-891 - File Upload: Upload failed for files >5MB (Regression?) -https://yoursite.atlassian.net/browse/PROJ-891 - -This may be a regression of PROJ-234, which was resolved 2 months ago. -Issue includes reference to original fix for investigation. -``` - ---- - -## When NOT to Use This Skill - -This skill is for **triaging bugs and errors only**. Do NOT use for: - -❌ Feature requests (use spec-to-backlog) -❌ General task creation (use capture-tasks-from-meeting-notes) -❌ Searching for information (use search-company-knowledge) -❌ Generating status reports (use generate-status-report) - -**Use this skill specifically for:** -✅ "Is this a duplicate bug?" -✅ "Triage this error message" -✅ "Has this been reported before?" -✅ "Create a bug ticket for this" - ---- - -## Quick Reference - -**Primary workflow:** Extract → Search → Analyze → Present → Execute → Confirm - -**Search tool:** `searchJiraIssuesUsingJql(cloudId, jql, fields, maxResults)` - -**Action tools:** -- `addCommentToJiraIssue(cloudId, issueIdOrKey, commentBody)` - Add to existing -- `createJiraIssue(cloudId, projectKey, issueTypeName, summary, description)` - Create new - -**Issue type:** Always prefer "Bug" for error reports, check with `getJiraProjectIssueTypesMetadata` - -**Remember:** -- Multiple searches catch more duplicates -- Present findings before acting -- Include error details and context -- Reference related issues -- Use "Bug" issue type when available diff --git a/plugins/atlassian-rovo/skills/triage-issue/agents/openai.yaml b/plugins/atlassian-rovo/skills/triage-issue/agents/openai.yaml deleted file mode 100644 index 653054018..000000000 --- a/plugins/atlassian-rovo/skills/triage-issue/agents/openai.yaml +++ /dev/null @@ -1,3 +0,0 @@ -interface: - display_name: "Triage Issue" - short_description: "Find duplicates and prepare Jira bug reports" diff --git a/plugins/atlassian-rovo/skills/triage-issue/references/bug-report-templates.md b/plugins/atlassian-rovo/skills/triage-issue/references/bug-report-templates.md deleted file mode 100644 index ff07c3119..000000000 --- a/plugins/atlassian-rovo/skills/triage-issue/references/bug-report-templates.md +++ /dev/null @@ -1,451 +0,0 @@ -# Bug Report Templates - -High-quality bug report templates for different types of issues. - ---- - -## Template 1: Backend Error - -**Summary Format:** -``` -[Service/Component]: [Error Type] in [Functionality] -``` - -**Examples:** -- Payment API: NullPointerException in refund processing -- Auth Service: TimeoutError during token validation -- Database: Connection pool exhausted in user queries - -**Description Template:** -```markdown -## Issue Description -[Brief 1-2 sentence description] - -## Error Details -``` -[Error message or exception] -Stack trace: -[Stack trace if available] -``` - -## Environment -- **Service:** [e.g., Payment Service v2.3.4] -- **Environment:** [Production/Staging] -- **Server:** [e.g., us-east-1 pod-7] -- **Timestamp:** [When it occurred] - -## Steps to Reproduce -1. [Step 1] -2. [Step 2] -3. [Step 3] - -## Expected Behavior -[What should happen] - -## Actual Behavior -[What actually happens] - -## Impact -- **Frequency:** [e.g., Every time, 10% of requests] -- **Affected Requests:** [e.g., ~500 requests/hour] -- **User Impact:** [e.g., Refunds cannot be processed] - -## Logs -``` -[Relevant log excerpts] -``` - -## Related Issues -[Any similar past issues] - ---- -*Reported via automated triage* -``` - ---- - -## Template 2: Frontend/UI Issue - -**Summary Format:** -``` -[Platform] [Component]: [Symptom] -``` - -**Examples:** -- iOS App Login: Screen remains blank after successful auth -- Web Dashboard: Infinite loading spinner on reports -- Android App: Crash when uploading photos - -**Description Template:** -```markdown -## Issue Description -[Brief description of the visible problem] - -## Environment -- **Platform:** [iOS/Android/Web] -- **Version:** [App/Browser version] -- **OS:** [e.g., iOS 16.5, Windows 11, macOS 13] -- **Device:** [e.g., iPhone 14 Pro, Chrome on Desktop] - -## Steps to Reproduce -1. [Step 1] -2. [Step 2] -3. [Step 3] - -## Expected Behavior -[What should happen] - -## Actual Behavior -[What actually happens] - -## Visual Evidence -[Screenshots or screen recording if available] - -## User Impact -- **Frequency:** [e.g., Every time, Intermittent] -- **Affected Users:** [e.g., All iOS users, Only Safari users] -- **Severity:** [e.g., Cannot complete checkout, Minor visual glitch] - -## Console Errors -``` -[Browser console errors if applicable] -``` - -## Additional Context -[Network conditions, user permissions, etc.] - -## Related Issues -[Any similar past issues] - ---- -*Reported via automated triage* -``` - ---- - -## Template 3: Performance Issue - -**Summary Format:** -``` -[Component]: [Performance Problem] - [Context] -``` - -**Examples:** -- Dashboard: Slow page load (15+ seconds) on reports -- API: Response time degradation under load -- Database: Query timeout on user search - -**Description Template:** -```markdown -## Issue Description -[Brief description of the performance problem] - -## Performance Metrics -- **Current:** [e.g., 15 second load time] -- **Expected:** [e.g., < 2 seconds] -- **Baseline:** [e.g., Was 1.5s last week] - -## Environment -- **Platform:** [Where observed] -- **Environment:** [Production/Staging] -- **Time Observed:** [When it was slow] -- **Load:** [Concurrent users, request rate] - -## Steps to Reproduce -1. [Step 1] -2. [Step 2] -3. Observe slow response - -## Performance Data -``` -[Response times, profiling data, slow query logs] -``` - -## Impact -- **Affected Users:** [e.g., All users during peak hours] -- **Frequency:** [e.g., Consistently slow, Only during peak] -- **Business Impact:** [e.g., Increased bounce rate, User complaints] - -## Suspected Cause -[If you have a hypothesis] - -## Related Issues -[Any similar past performance issues] - ---- -*Reported via automated triage* -``` - ---- - -## Template 4: Data Issue - -**Summary Format:** -``` -[Component]: [Data Problem] - [Scope] -``` - -**Examples:** -- User Profile: Data not persisting after save -- Orders: Missing order items in history -- Reports: Incorrect calculations in revenue report - -**Description Template:** -```markdown -## Issue Description -[Brief description of the data problem] - -## Data Issue Details -- **What's Wrong:** [e.g., Orders missing from history] -- **Expected Data:** [What should be there] -- **Actual Data:** [What is actually there] -- **Data Loss/Corruption:** [Scope of issue] - -## Environment -- **Environment:** [Production/Staging] -- **Affected Records:** [e.g., All orders from Dec 1-5] -- **First Observed:** [When issue started] - -## Steps to Reproduce -1. [Step 1] -2. [Step 2] -3. Observe incorrect/missing data - -## Examples -**Affected Record:** Order #12345 -**Expected:** [Expected data state] -**Actual:** [Actual data state] - -## Impact -- **Affected Users:** [e.g., ~500 customers] -- **Data Integrity:** [e.g., Historical data lost] -- **Business Impact:** [e.g., Cannot fulfill orders] - -## Database Queries -```sql -[Queries showing the issue if applicable] -``` - -## Related Issues -[Any similar past data issues] - ---- -*Reported via automated triage* -``` - ---- - -## Template 5: Integration Issue - -**Summary Format:** -``` -[Integration]: [Error] - [External Service] -``` - -**Examples:** -- Stripe Integration: Payment processing fails -- Auth0: Token validation timeout -- Sendgrid: Email sending fails with 429 error - -**Description Template:** -```markdown -## Issue Description -[Brief description of the integration problem] - -## Integration Details -- **External Service:** [e.g., Stripe API] -- **Integration Point:** [e.g., Payment processing endpoint] -- **API Version:** [If known] - -## Error Response -``` -HTTP Status: [e.g., 429, 500] -Response Body: -[Error response from external service] -``` - -## Environment -- **Environment:** [Production/Staging] -- **Our Version:** [Our service version] -- **Time Observed:** [When it started failing] - -## Steps to Reproduce -1. [Step that triggers integration] -2. [Expected external service response] -3. Observe failure - -## Expected Behavior -[What should happen with external service] - -## Actual Behavior -[What is actually happening] - -## Impact -- **Frequency:** [e.g., 100% of payment attempts] -- **Affected Transactions:** [e.g., ~200 failed payments/hour] -- **User Impact:** [e.g., Cannot complete checkout] - -## External Service Status -[Check if external service has known issues] - -## Logs -``` -[Our logs showing the integration failure] -``` - -## Related Issues -[Any past integration issues with this service] - ---- -*Reported via automated triage* -``` - ---- - -## Template 6: Regression (Previously Fixed) - -**Summary Format:** -``` -[Component]: [Issue] - Regression of PROJ-XXX -``` - -**Examples:** -- Login: Session timeout after 15min - Regression of PROJ-234 -- Upload: File size limit error - Regression of PROJ-567 - -**Description Template:** -```markdown -## Issue Description -[Brief description - note this was previously fixed] - -⚠️ **This appears to be a regression of [PROJ-XXX]**, which was resolved on [date]. - -## Original Issue -**Original Ticket:** [PROJ-XXX](link) -**Originally Fixed By:** @username -**Fix Date:** [date] -**Original Fix:** [Brief description of what was fixed] - -## Current Issue -[Description of the current occurrence] - -## Environment -- **Environment:** [Production/Staging] -- **Version:** [Current version] -- **First Observed:** [When regression appeared] - -## Steps to Reproduce -1. [Step 1] -2. [Step 2] -3. Observe issue is back - -## Expected Behavior -[Should remain fixed as per PROJ-XXX] - -## Actual Behavior -[Issue has returned] - -## Impact -[Current impact of regression] - -## Possible Causes -[Speculation about what might have caused regression] -- Recent deployment on [date]? -- Configuration change? -- Dependency update? - -## Investigation Needed -- Review changes since original fix -- Check if original fix was rolled back -- Verify fix is still in codebase - -## Related Issues -- **Original Issue:** [PROJ-XXX](link) -[Any other related issues] - ---- -*Reported via automated triage - Possible Regression* -``` - ---- - -## Summary Writing Best Practices - -### Good Summaries - -✅ **Specific and actionable:** -- "Payment API: NullPointerException in refund processing" -- "iOS App: Crash when uploading photos >5MB" -- "Dashboard: 15s load time on revenue report" - -✅ **Includes component:** -- Start with the affected component/system -- Makes it easy to filter and assign - -✅ **Describes the problem:** -- Use clear, technical language -- Avoid vague terms - -### Bad Summaries - -❌ **Too vague:** -- "Error in production" -- "App crashes sometimes" -- "Something is slow" - -❌ **Too long:** -- "Users are reporting that when they try to login on the mobile app using their email and password, the app shows a connection timeout error and they cannot proceed" - -❌ **Missing component:** -- "NullPointerException in refund" (what component?) -- "Page won't load" (which page?) - ---- - -## Description Writing Best Practices - -### Good Practices - -✅ **Use structured format** with headers -✅ **Include complete error messages** in code blocks -✅ **Provide context** (environment, version, time) -✅ **List concrete steps** to reproduce -✅ **Quantify impact** (affected users, frequency) -✅ **Add relevant logs** in code blocks -✅ **Reference related issues** with links - -### What to Avoid - -❌ Pasting entire stack traces without context -❌ Vague descriptions like "it doesn't work" -❌ Missing environment information -❌ No reproduction steps -❌ Formatting errors/code without code blocks -❌ Forgetting to mention user impact - ---- - -## Field Guidelines - -### Priority Selection - -**Highest:** System down, data loss, security issue -**High:** Major functionality broken, large user impact -**Medium:** Feature partially broken, moderate impact -**Low:** Minor issue, cosmetic, workaround available - -### Component Selection - -Always specify the affected component if the project uses components: -- Makes routing to correct team easier -- Helps with duplicate detection -- Improves searchability - -### Labels (If Available) - -Consider adding labels: -- `regression` - Previously fixed issue -- `production` - Occurring in production -- `data-loss` - Involves data loss/corruption -- `performance` - Performance related -- `mobile-ios` / `mobile-android` - Platform specific diff --git a/plugins/atlassian-rovo/skills/triage-issue/references/search-patterns.md b/plugins/atlassian-rovo/skills/triage-issue/references/search-patterns.md deleted file mode 100644 index 8d70db65c..000000000 --- a/plugins/atlassian-rovo/skills/triage-issue/references/search-patterns.md +++ /dev/null @@ -1,261 +0,0 @@ -# Search Patterns for Duplicate Detection - -Effective JQL patterns for finding duplicate bugs and similar issues. - ---- - -## Error-Based Search Patterns - -### Exception Searches - -**For Java/Backend exceptions:** -```jql -project = "PROJ" AND text ~ "NullPointerException" AND type = Bug ORDER BY created DESC -``` - -**For specific class/method:** -```jql -project = "PROJ" AND text ~ "PaymentProcessor processRefund" AND type = Bug ORDER BY created DESC -``` - -**For HTTP errors:** -```jql -project = "PROJ" AND (text ~ "500 error" OR summary ~ "500") AND type = Bug ORDER BY updated DESC -``` - -### Timeout Searches - -**General timeout:** -```jql -project = "PROJ" AND (text ~ "timeout" OR summary ~ "timeout") AND type = Bug ORDER BY priority DESC -``` - -**Specific timeout type:** -```jql -project = "PROJ" AND text ~ "connection timeout" AND component = "API" ORDER BY created DESC -``` - ---- - -## Component-Based Search Patterns - -### By System Component - -**Authentication:** -```jql -project = "PROJ" AND text ~ "authentication login" AND type = Bug AND status != Done -``` - -**Payment:** -```jql -project = "PROJ" AND (component = "Payment" OR text ~ "payment checkout") AND type = Bug -``` - -**Mobile:** -```jql -project = "PROJ" AND (text ~ "mobile iOS" OR text ~ "mobile Android") AND type = Bug ORDER BY updated DESC -``` - -### By Functionality - -**Upload/Download:** -```jql -project = "PROJ" AND (text ~ "upload" OR text ~ "download") AND type = Bug -``` - -**Database:** -```jql -project = "PROJ" AND text ~ "database query SQL" AND type = Bug ORDER BY created DESC -``` - ---- - -## Symptom-Based Search Patterns - -### User-Facing Symptoms - -**Page/Screen issues:** -```jql -project = "PROJ" AND (summary ~ "blank page" OR summary ~ "white screen") AND type = Bug -``` - -**Loading issues:** -```jql -project = "PROJ" AND (summary ~ "infinite loading" OR summary ~ "stuck loading") AND type = Bug -``` - -**Data issues:** -```jql -project = "PROJ" AND (summary ~ "data not saving" OR summary ~ "data lost") AND type = Bug -``` - -### Performance Symptoms - -**Slow performance:** -```jql -project = "PROJ" AND (text ~ "slow" OR summary ~ "performance") AND type = Bug ORDER BY priority DESC -``` - -**Crashes:** -```jql -project = "PROJ" AND (summary ~ "crash" OR text ~ "application crash") AND type = Bug ORDER BY created DESC -``` - ---- - -## Time-Based Search Patterns - -### Recent Issues (Last 30 Days) - -```jql -project = "PROJ" AND text ~ "error keywords" AND type = Bug AND created >= -30d ORDER BY created DESC -``` - -### Recently Updated - -```jql -project = "PROJ" AND text ~ "error keywords" AND type = Bug AND updated >= -7d ORDER BY updated DESC -``` - -### Recently Resolved - -```jql -project = "PROJ" AND text ~ "error keywords" AND type = Bug AND status = Done AND resolved >= -90d ORDER BY resolved DESC -``` - ---- - -## Combined Search Patterns - -### High-Priority Recent - -```jql -project = "PROJ" AND text ~ "error" AND type = Bug AND priority IN ("Highest", "High") AND created >= -60d ORDER BY priority DESC, created DESC -``` - -### Component + Error Type - -```jql -project = "PROJ" AND component = "API" AND text ~ "timeout" AND type = Bug ORDER BY updated DESC -``` - -### Environment-Specific - -```jql -project = "PROJ" AND text ~ "production" AND text ~ "error keywords" AND type = Bug ORDER BY created DESC -``` - ---- - -## Advanced Patterns for Regression Detection - -### Previously Resolved - -```jql -project = "PROJ" AND text ~ "error keywords" AND type = Bug AND status = Done AND resolution = Fixed ORDER BY resolved DESC -``` - -### Reopened Issues - -```jql -project = "PROJ" AND text ~ "error keywords" AND type = Bug AND status = Reopened ORDER BY updated DESC -``` - -### Similar Fix History - -```jql -project = "PROJ" AND text ~ "error keywords" AND type = Bug AND (status = Resolved OR status = Closed) AND resolved >= -180d ORDER BY resolved DESC -``` - ---- - -## Multi-Angle Search Strategy - -For thorough duplicate detection, run searches in this order: - -**1. Exact error signature (narrow):** -```jql -project = "PROJ" AND summary ~ "exact error text" AND type = Bug ORDER BY created DESC -``` - -**2. Error type + component (medium):** -```jql -project = "PROJ" AND text ~ "error type" AND component = "ComponentName" AND type = Bug ORDER BY updated DESC -``` - -**3. Symptom-based (broad):** -```jql -project = "PROJ" AND summary ~ "user symptom" AND type = Bug ORDER BY priority DESC -``` - -**4. Historical (regression check):** -```jql -project = "PROJ" AND text ~ "keywords" AND type = Bug AND status = Done ORDER BY resolved DESC -``` - ---- - -## Field Selection for Triage - -Always request these fields for effective analysis: - -``` -fields: ["summary", "description", "status", "resolution", "priority", "created", "updated", "resolved", "assignee", "reporter", "components"] -``` - -**Why each field matters:** -- `summary` - Quick identification of duplicate -- `description` - Detailed error matching -- `status` - Know if open/resolved -- `resolution` - How it was fixed (if resolved) -- `priority` - Severity assessment -- `created` - Age of issue -- `updated` - Recent activity -- `resolved` - When it was fixed -- `assignee` - Who fixed it or is working on it -- `reporter` - Original reporter -- `components` - Affected system parts - ---- - -## Tips for Better Search Results - -### Use Key Terms Only - -✅ Good: -- "timeout login" -- "NullPointerException PaymentProcessor" -- "500 error API" - -❌ Too Verbose: -- "users are experiencing a timeout when trying to login" -- "we got a NullPointerException in the PaymentProcessor class" - -### Combine Searches - -Don't rely on a single search. Run 2-3 searches with different angles: -1. Error-focused -2. Component-focused -3. Symptom-focused - -### Order Strategically - -- Recent first: `ORDER BY created DESC` -- Active first: `ORDER BY updated DESC` -- Important first: `ORDER BY priority DESC, updated DESC` - -### Limit Results - -- Use `maxResults=20` for initial searches -- Don't overwhelm with 100+ results -- Focus on top 10-15 most relevant - ---- - -## Common Pitfalls to Avoid - -❌ Searching with full stack traces (too specific, no matches) -❌ Using only exact text matching (miss paraphrased duplicates) -❌ Ignoring resolved issues (miss regressions) -❌ Not checking multiple projects (duplicate across teams) -❌ Only searching summaries (miss details in descriptions) diff --git a/plugins/attio/.app.json b/plugins/attio/.app.json deleted file mode 100644 index 68c35c7dd..000000000 --- a/plugins/attio/.app.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "apps": { - "attio": { - "id": "asdk_app_6981f663d5cc8191ae0d5717a05ccc89" - } - } -} diff --git a/plugins/attio/.codex-plugin/plugin.json b/plugins/attio/.codex-plugin/plugin.json deleted file mode 100644 index 7922304f7..000000000 --- a/plugins/attio/.codex-plugin/plugin.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "attio", - "version": "1.0.3", - "description": "Attio connects Codex directly to your CRM workspace, letting you manage customer relationships through na...", - "author": { - "name": "Attio Ltd", - "url": "https://attio.com" - }, - "repository": "https://github.com/openai/plugins", - "license": "MIT", - "keywords": [], - "apps": "./.app.json", - "interface": { - "displayName": "Attio", - "shortDescription": "Attio connects Codex directly to your CRM workspace, letting you manage customer relationships through na...", - "longDescription": "Attio connects Codex directly to your CRM workspace, letting you manage customer relationships through natural conversation. \n\nSearch and filter contacts, companies, and deals with flexible queries. Create, update, and organize records without switching between screens. Add notes, manage tasks, and track your sales pipeline\u2014all through simple requests. \n\nKey capabilities: \n- Search records using powerful filters (find companies by size, industry, last contact date) \n- Create and update people, companies, and deal records \n- Manage notes attached to any record \n- Track and complete tasks \n- Navigate lists and organize your data \n\nWhether you're preparing for a meeting, updating deal stages, or researching prospects, Attio brings your CRM data into your conversation \u2014 no manual data entry required.", - "developerName": "Attio Ltd", - "category": "Business & Operations", - "capabilities": [], - "defaultPrompt": [ - "Find the latest notes and next steps in Attio" - ], - "screenshots": [], - "websiteURL": "https://attio.com", - "privacyPolicyURL": "https://attio.com/legal/privacy", - "termsOfServiceURL": "https://attio.com/legal/terms-and-conditions", - "composerIcon": "./assets/logo.png", - "logo": "./assets/logo.png" - }, - "homepage": "https://attio.com" -} diff --git a/plugins/attio/assets/logo.png b/plugins/attio/assets/logo.png deleted file mode 100644 index 25b354f56..000000000 Binary files a/plugins/attio/assets/logo.png and /dev/null differ diff --git a/plugins/base44/.app.json b/plugins/base44/.app.json deleted file mode 100644 index bb07d0b1e..000000000 --- a/plugins/base44/.app.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "apps": { - "base44": { - "id": "asdk_app_6952514760dc8191ab148f77c5794d46" - } - } -} diff --git a/plugins/base44/.codex-plugin/plugin.json b/plugins/base44/.codex-plugin/plugin.json deleted file mode 100644 index 5d79a4b52..000000000 --- a/plugins/base44/.codex-plugin/plugin.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "base44", - "version": "1.0.3-beta.1", - "description": "Build and deploy Base44 full-stack apps with CLI project management and JavaScript/TypeScript SDK development skills", - "author": { - "name": "base44", - "url": "https://base44.com" - }, - "homepage": "https://docs.base44.com", - "repository": "https://github.com/base44/skills", - "license": "MIT", - "keywords": ["base44", "full-stack", "sdk", "cli", "deployment", "entities", "backend-functions", "javascript", "typescript"], - "skills": "./skills/", - "apps": "./.app.json", - "interface": { - "displayName": "Base44", - "shortDescription": "Build and deploy Base44 full-stack apps from Codex", - "longDescription": "Build and deploy Base44 full-stack apps with Codex. Includes CLI project management, JavaScript/TypeScript SDK development, and production troubleshooting skills.", - "developerName": "base44", - "category": "Developer Tools", - "capabilities": ["Interactive", "Read", "Write"], - "websiteURL": "https://base44.com", - "defaultPrompt": [ - "Create a new Base44 app and deploy it.", - "Build a feature in my existing Base44 app.", - "Debug errors from my Base44 backend functions." - ], - "composerIcon": "./assets/logo-padded.png", - "logo": "./assets/logo-padded.png", - "screenshots": [] - } -} diff --git a/plugins/base44/assets/base44-logo.png b/plugins/base44/assets/base44-logo.png deleted file mode 100644 index 6fed0a022..000000000 Binary files a/plugins/base44/assets/base44-logo.png and /dev/null differ diff --git a/plugins/base44/assets/logo-padded.png b/plugins/base44/assets/logo-padded.png deleted file mode 100644 index 6fddaf02c..000000000 Binary files a/plugins/base44/assets/logo-padded.png and /dev/null differ diff --git a/plugins/base44/skills/base44-cli/SKILL.md b/plugins/base44/skills/base44-cli/SKILL.md deleted file mode 100644 index b5d13dbd5..000000000 --- a/plugins/base44/skills/base44-cli/SKILL.md +++ /dev/null @@ -1,530 +0,0 @@ ---- -name: base44-cli -description: "The base44 CLI is used for EVERYTHING related to base44 projects: resource configuration (entities, backend functions, ai agents), initialization and actions (resource creation, deployment). This skill is the place for learning about how to configure resources. When you plan or implement a feature, you must learn this skill" -metadata: - sourcePackage: - name: base44 - version: 0.0.50 ---- - -# Base44 CLI - -Create and manage Base44 apps (projects) using the Base44 CLI tool. - -## ⚡ IMMEDIATE ACTION REQUIRED - Read This First - -This skill activates on ANY mention of "base44" or when a `base44/` folder exists. **DO NOT read documentation files or search the web before acting.** - -**Your first action MUST be:** -1. Check if `base44/config.jsonc` exists in the current directory -2. If **NO** (new project scenario): - - This skill (base44-cli) handles the request - - Guide user through project initialization - - Do NOT activate base44-sdk yet -3. If **YES** (existing project scenario): - - Transfer to base44-sdk skill for implementation - - This skill only handles CLI commands (login, deploy, entities push) - -## Critical: Local Installation Only - -NEVER call `base44` directly. The CLI is installed locally as a dev dependency and must be accessed via a package manager: - -- `npx base44 ` (npm - recommended) -- `yarn base44 ` (yarn) -- `pnpm base44 ` (pnpm) - -WRONG: `base44 login` -RIGHT: `npx base44 login` - -## MANDATORY: Authentication Check at Session Start - -**CRITICAL**: At the very start of every AI session when this skill is activated, you MUST: - -1. **Check authentication status** by running: - ```bash - npx base44 whoami - ``` - -2. **If the user is logged in** (command succeeds and shows an email): - - Continue with the requested task - -3. **If the user is NOT logged in** (command fails or shows an error): - - **STOP immediately** - - **DO NOT proceed** with any CLI operations - - **Ask the user to login manually** by running: - ```bash - npx base44 login - ``` - - Wait for the user to confirm they have logged in before continuing - -**This check is mandatory and must happen before executing any other Base44 CLI commands.** - -## Overview - -The Base44 CLI provides command-line tools for authentication, creating projects, managing entities, and deploying Base44 applications. It is framework-agnostic and works with popular frontend frameworks like Vite, Next.js, and Create React App, Svelte, Vue, and more. - -## When to Use This Skill vs base44-sdk - -**Use base44-cli when:** -- Creating a **NEW** Base44 project from scratch -- Initializing a project in an empty directory -- Directory is missing `base44/config.jsonc` -- User mentions: "create a new project", "initialize project", "setup a project", "start a new Base44 app" -- Deploying, pushing entities, or authenticating via CLI -- Working with CLI commands (`npx base44 ...`) - -**Use base44-sdk when:** -- Building features in an **EXISTING** Base44 project -- `base44/config.jsonc` already exists -- Writing JavaScript/TypeScript code using Base44 SDK -- Implementing functionality, components, or features -- User mentions: "implement", "build a feature", "add functionality", "write code" - -**Skill Dependencies:** -- `base44-cli` is a **prerequisite** for `base44-sdk` in new projects -- If user wants to "create an app" and no Base44 project exists, use `base44-cli` first -- `base44-sdk` assumes a Base44 project is already initialized - -**State Check Logic:** -Before selecting a skill, check: -- IF (user mentions "create/build app" OR "make a project"): - - IF (directory is empty OR no `base44/config.jsonc` exists): - → Use **base44-cli** (project initialization needed) - - ELSE: - → Use **base44-sdk** (project exists, build features) - -## Project Structure - -A Base44 project combines a standard frontend project with a `base44/` configuration folder: - -``` -my-app/ -├── base44/ # Base44 configuration (created by CLI) -│ ├── config.jsonc # Project settings, site config -│ ├── .types/ # Auto-generated TypeScript types (created by `types generate`) -│ │ └── types.d.ts # Module augmentation for @base44/sdk -│ ├── entities/ # Entity schema definitions -│ │ ├── task.jsonc -│ │ └── board.jsonc -│ ├── functions/ # Backend functions (optional); automations live in function.jsonc -│ │ └── my-function/ -│ │ ├── function.jsonc -│ │ └── index.ts -│ ├── agents/ # Agent configurations (optional) -│ │ └── support_agent.jsonc -│ └── connectors/ # OAuth connector configurations (optional) -│ └── googlecalendar.jsonc -├── src/ # Frontend source code -│ ├── api/ -│ │ └── base44Client.js # Base44 SDK client -│ ├── pages/ -│ ├── components/ -│ └── main.jsx -├── index.html # SPA entry point -├── package.json -└── vite.config.js # Or your framework's config -``` - -**Key files:** -- `base44/config.jsonc` - Project name, description, site build settings -- `base44/entities/*.jsonc` - Data model schemas (see Entity Schema section) -- `base44/functions/*/function.jsonc` - Function config and optional `automations` (CRON, simple triggers, entity hooks) -- `base44/agents/*.jsonc` - Agent configurations (optional) -- `base44/.types/types.d.ts` - Auto-generated TypeScript types for entities, functions, and agents (created by `npx base44 types generate`) -- `base44/connectors/*.jsonc` - OAuth connector configurations (optional) -- `src/api/base44Client.js` - Pre-configured SDK client for frontend use - -**config.jsonc example:** -```jsonc -{ - "name": "My App", // Required: project name - "description": "App description", // Optional: project description - "entitiesDir": "./entities", // Optional: default "entities" - "functionsDir": "./functions", // Optional: default "functions" - "agentsDir": "./agents", // Optional: default "agents" - "connectorsDir": "./connectors", // Optional: default "connectors" - "site": { // Optional: site deployment config - "installCommand": "npm install", // Optional: install dependencies - "buildCommand": "npm run build", // Optional: build command - "serveCommand": "npm run dev", // Optional: local dev server - "outputDirectory": "./dist" // Optional: build output directory - } -} -``` - -**Config properties:** - -| Property | Description | Default | -|----------|-------------|---------| -| `name` | Project name (required) | - | -| `description` | Project description | - | -| `entitiesDir` | Directory for entity schemas | `"entities"` | -| `functionsDir` | Directory for backend functions | `"functions"` | -| `agentsDir` | Directory for agent configs | `"agents"` | -| `connectorsDir` | Directory for connector configs | `"connectors"` | -| `site.installCommand` | Command to install dependencies | - | -| `site.buildCommand` | Command to build the project | - | -| `site.serveCommand` | Command to run dev server | - | -| `site.outputDirectory` | Build output directory for deployment | - | - -## Installation - -Install the Base44 CLI as a dev dependency in your project: - -```bash -npm install --save-dev base44 -``` - -**Important:** Never assume or hardcode the `base44` package version. Always install without a version specifier to get the latest version. - -Then run commands using `npx`: - -```bash -npx base44 -``` - -**Note:** All commands in this documentation use `npx base44`. You can also use `yarn base44`, or `pnpm base44` if preferred. - -## Available Commands - -### Authentication - -| Command | Description | Reference | -| --------------- | ----------------------------------------------- | ------------------------------------------- | -| `base44 login` | Authenticate with Base44 using device code flow | [auth-login.md](references/auth-login.md) | -| `base44 logout` | Logout from current device | [auth-logout.md](references/auth-logout.md) | -| `base44 whoami` | Display current authenticated user | [auth-whoami.md](references/auth-whoami.md) | - -### Project Management - -| Command | Description | Reference | -|---------|-------------|-----------| -| `base44 create` | Create a new Base44 project from a template | [create.md](references/create.md) ⚠️ **MUST READ** | -| `base44 link` | Link an existing local project to Base44 | [link.md](references/link.md) | -| `base44 eject` | Download the code for an existing Base44 project | [eject.md](references/eject.md) | -| `base44 dashboard open` | Open the app dashboard in your browser | [dashboard.md](references/dashboard.md) | - -### Deployment - -| Command | Description | Reference | -|---------|-------------|-----------| -| `base44 deploy` | Deploy all resources (entities, functions, agents, connectors, auth config, and site) | [deploy.md](references/deploy.md) | - -### Entity Management - -| Action / Command | Description | Reference | -| ---------------------- | ------------------------------------------- | --------------------------------------------------- | -| Create Entities | Define entities in `base44/entities` folder | [entities-create.md](references/entities-create.md) | -| `base44 entities push` | Push local entities to Base44 | [entities-push.md](references/entities-push.md) | -| RLS Patterns | Row-level security examples and operators | [rls-examples.md](references/rls-examples.md) ⚠️ **READ FOR RLS** | - -#### Entity Schema (Quick Reference) - -ALWAYS follow this exact structure when creating entity files: - -**File naming:** `base44/entities/{kebab-case-name}.jsonc` (e.g., `team-member.jsonc` for `TeamMember`) - -**Schema template:** -```jsonc -{ - "name": "EntityName", - "type": "object", - "properties": { - "field_name": { - "type": "string", - "description": "Field description" - } - }, - "required": ["field_name"] -} -``` - -**Field types:** `string`, `number`, `integer`, `boolean`, `array`, `object`, `binary` -**String formats:** `date`, `date-time`, `time`, `email`, `uri`, `hostname`, `ipv4`, `ipv6`, `uuid`, `file`, `regex`, `richtext` -**For enums:** Add `"enum": ["value1", "value2"]` and optionally `"default": "value1"` -**Entity names:** Must be alphanumeric only (pattern: `/^[a-zA-Z0-9]+$/`) - -For complete documentation, see [entities-create.md](references/entities-create.md). - -### Function Management - -| Action / Command | Description | Reference | -| ------------------------- | --------------------------------------------- | ------------------------------------------------------- | -| Create Functions | Define functions in `base44/functions` folder | [functions-create.md](references/functions-create.md) | -| Configure Automations | CRON, simple triggers, entity hooks in `function.jsonc` | [automations.md](references/automations.md) | -| `base44 functions deploy [names...] [--force]` | Deploy local functions (and automations) to Base44; optionally target specific functions or prune removed ones | [functions-deploy.md](references/functions-deploy.md) | -| `base44 functions delete ` | Delete one or more deployed functions from Base44 | [functions-delete.md](references/functions-delete.md) | -| `base44 functions list` | List all deployed functions on Base44 remote | [functions-list.md](references/functions-list.md) | -| `base44 functions pull [name]` | Pull deployed functions from Base44 to local files | [functions-pull.md](references/functions-pull.md) | - -### Agent Management - -Agents are conversational AI assistants that can interact with users, access your app's entities, and call backend functions. Use these commands to manage agent configurations. - -| Action / Command | Description | Reference | -| ----------------------- | --------------------------------------- | ----------------------------------------------- | -| Create Agents | Define agents in `base44/agents` folder | See Agent Schema below | -| `base44 agents pull` | Pull remote agents to local files | [agents-pull.md](references/agents-pull.md) | -| `base44 agents push` | Push local agents to Base44 | [agents-push.md](references/agents-push.md) | - -**Note:** Agent commands perform full synchronization - pushing replaces all remote agents with local ones, and pulling replaces all local agents with remote ones. - -#### Agent Schema (Quick Reference) - -**File naming:** `base44/agents/{agent_name}.jsonc` (e.g., `support_agent.jsonc`) - -**Schema template:** -```jsonc -{ - "name": "agent_name", - "description": "Brief description of what this agent does", - "instructions": "Detailed instructions for the agent's behavior", - "tool_configs": [ - // Entity tool - gives agent access to entity operations - { "entity_name": "tasks", "allowed_operations": ["read", "create", "update", "delete"] }, - // Backend function tool - gives agent access to a function - { "function_name": "send_email", "description": "Send an email notification" } - ], - "whatsapp_greeting": "Hello! How can I help you today?" -} -``` - -**Naming rules:** -- Agent names must match pattern: `/^[a-z0-9_]+$/` (lowercase alphanumeric with underscores, 1-100 chars) -- Valid: `support_agent`, `order_bot` -- Invalid: `Support-Agent`, `OrderBot` - -**Required fields:** `name`, `description`, `instructions` -**Optional fields:** `tool_configs` (defaults to `[]`), `whatsapp_greeting` - -**Tool config types:** -- **Entity tools**: `entity_name` + `allowed_operations` (array of: `read`, `create`, `update`, `delete`) -- **Backend function tools**: `function_name` + `description` - -### Connector Management - -Connectors let your app connect to external services (Google Calendar, Slack, Stripe, etc.). Most connectors use OAuth to provide access tokens for backend functions to call external APIs. Stripe is the exception — it is provisioned automatically on the server side with no OAuth browser flow. - -| Action / Command | Description | Reference | -| ---------------------------------- | ---------------------------------------------------- | ------------------------------------------------------------------- | -| Create Connectors | Define connectors in `base44/connectors` folder | [connectors-create.md](references/connectors-create.md) | -| `base44 connectors list-available` | List all available integration types from Base44 | [connectors-list-available.md](references/connectors-list-available.md) | -| `base44 connectors pull` | Pull remote connectors to local files | [connectors-pull.md](references/connectors-pull.md) | -| `base44 connectors push` | Push local connectors to Base44 | [connectors-push.md](references/connectors-push.md) | - -**Note:** Connector commands perform full synchronization - pushing replaces all remote connectors with local ones (and triggers OAuth for new OAuth connectors), and pulling replaces all local connectors with remote ones. - -#### Connector Schema (Quick Reference) - -**File naming:** `base44/connectors/{type}.jsonc` (e.g., `googlecalendar.jsonc`, `slack.jsonc`) - -**Schema template:** -```jsonc -{ - "type": "googlecalendar", - "scopes": [ - "https://www.googleapis.com/auth/calendar.readonly", - "https://www.googleapis.com/auth/calendar.events" - ] -} -``` - -**Required fields:** `type` -**Optional fields:** `scopes` (defaults to `[]`) - -**Available connector types:** Run `npx base44 connectors list-available` to see all supported integration types. - -**Note:** `stripe` is also a valid connector type but is not returned by `list-available`. Treat it as a supported type — it is provisioned automatically by Base44 with no OAuth browser flow. See [connectors-create.md](references/connectors-create.md) for details. - -For complete documentation, see [connectors-create.md](references/connectors-create.md). - -#### Automation Quick Reference - -Automations are triggers defined in the `automations` array inside `function.jsonc`. They deploy with the function via `base44 functions deploy`. Four types: - -**Common fields (all types):** `name` (required), `description`, `function_args`, `is_active` (default: true) - -**Scheduled One-Time:** `type: "scheduled"`, `schedule_mode: "one-time"`, `one_time_date` (ISO string) - -**Scheduled CRON:** `type: "scheduled"`, `schedule_mode: "recurring"`, `schedule_type: "cron"`, `cron_expression`, optional `ends_type` / `ends_on_date` / `ends_after_count` - -**Scheduled Simple:** `type: "scheduled"`, `schedule_mode: "recurring"`, `schedule_type: "simple"`, `repeat_unit` (`"minutes"` \| `"hours"` \| `"days"` \| `"weeks"` \| `"months"`), optional `repeat_interval`, `start_time`, `repeat_on_days` (0–6), `repeat_on_day_of_month` (1–31), `ends_type` / `ends_on_date` / `ends_after_count` - -**Entity Hook:** `type: "entity"`, `entity_name` (matches entity schema name), `event_types`: array of `"create"` \| `"update"` \| `"delete"` (at least one) - -For full schemas and examples, see [automations.md](references/automations.md). - -### Auth Configuration - -Manage your app's authentication settings (e.g., username & password login). Auth config is stored in `base44/auth/` and synced with Base44 via `auth push`/`auth pull`. - -| Command | Description | Reference | -|---------|-------------|-----------| -| `base44 auth password-login ` | Enable or disable username & password authentication | [auth-password-login.md](references/auth-password-login.md) | -| `base44 auth pull` | Pull auth config from Base44 to local files | [auth-pull.md](references/auth-pull.md) | -| `base44 auth push` | Push local auth config to Base44 | [auth-push.md](references/auth-push.md) | - -**Note:** Auth config is also deployed as part of `base44 deploy`. - -### Secrets Management - -Manage project secrets (environment variables stored securely in Base44). These commands are hidden from `--help` output but are fully functional. - -| Command | Description | Reference | -|---------|-------------|-----------| -| `base44 secrets list` | List the names of all secrets | [secrets-list.md](references/secrets-list.md) | -| `base44 secrets set` | Set one or more secrets (KEY=VALUE or --env-file) | [secrets-set.md](references/secrets-set.md) | -| `base44 secrets delete ` | Delete a secret by name | [secrets-delete.md](references/secrets-delete.md) | - -### Script Execution - -Run one-off scripts against your app with the Base44 SDK pre-authenticated. Use it to perform CRUD operations on entities (`base44.entities.MyEntity.list/create/update/delete`), call backend functions (`base44.functions.invoke("myFunction", args)`), invoke agents, or access any other resource exposed by the SDK — without deploying a full function. Useful for data migrations, bulk operations, debugging, and automation scripts. - -| Command | Description | Reference | -|---------|-------------|-----------| -| `base44 exec` | Run a script (via stdin) with the Base44 SDK pre-authenticated | [exec.md](references/exec.md) | - -### Type Generation - -| Command | Description | Reference | -|---------|-------------|-----------| -| `base44 types generate` | Generate TypeScript types (`types.d.ts`) from entities, functions, agents, and connectors | [types-generate.md](references/types-generate.md) | - -**Output:** `base44/.types/types.d.ts` — augments `@base44/sdk` module with typed registries (`EntityTypeRegistry`, `FunctionNameRegistry`, `AgentNameRegistry`, `ConnectorTypeRegistry`). - -**No authentication required.** Runs entirely locally. Automatically updates `tsconfig.json` to include the generated types. - -### Site Management - -| Command | Description | Reference | -| -------------------- | ----------------------------------------- | ------------------------------------------- | -| `base44 site deploy` | Deploy built site files to Base44 hosting | [site-deploy.md](references/site-deploy.md) | -| `base44 site open` | Open the deployed site in your browser | [site-open.md](references/site-open.md) | - -**SPA only**: Base44 hosting supports Single Page Applications with a single `index.html` entry point. All routes are served from `index.html` (client-side routing). - -## Quick Start - -1. Install the CLI in your project: - ```bash - npm install --save-dev base44 - ``` - -2. Authenticate with Base44: - ```bash - npx base44 login - ``` - -3. Create a new project (ALWAYS provide name and `--path` flag): - ```bash - npx base44 create my-app -p . - ``` - -4. Build and deploy everything: - ```bash - npm run build - npx base44 deploy -y - ``` - -Or deploy individual resources: -- `npx base44 entities push` - Push entities only -- `npx base44 functions deploy` - Deploy functions only -- `npx base44 functions delete ` - Delete a deployed function -- `npx base44 functions list` - List all deployed functions -- `npx base44 functions pull` - Pull deployed functions to local files -- `npx base44 agents push` - Push agents only -- `npx base44 connectors pull` - Pull connectors from Base44 -- `npx base44 connectors push` - Push connectors only -- `npx base44 auth pull` - Pull auth config from Base44 -- `npx base44 auth push` - Push auth config only -- `npx base44 site deploy -y` - Deploy site only - -## Common Workflows - -### Creating a New Project - -**⚠️ MANDATORY: Before running `base44 create`, you MUST read [create.md](references/create.md) for:** -- **Template selection** - Choose the correct template (`backend-and-client` vs `backend-only`) -- **Correct workflow** - Different templates require different setup steps -- **Common pitfalls** - Avoid folder creation errors that cause failures - -Failure to follow the create.md instructions will result in broken project scaffolding. - -### Linking an Existing Project -```bash -# If you have base44/config.jsonc but no .app.jsonc -npx base44 link --create --name my-app -``` - -### Deploying All Changes -```bash -# Generate types (optional, for TypeScript projects) -npx base44 types generate - -# Build your project first -npm run build - -# Deploy everything (entities, functions, and site) -npx base44 deploy -y -``` - -### Generating TypeScript Types -```bash -# Generate types from entities, functions, agents, and connectors -npx base44 types generate -``` - -This creates `base44/.types/types.d.ts` with typed registries for the `@base44/sdk` module. Run this after changing entities, functions, agents, or connectors to keep your types in sync. No authentication required. - -### Deploying Individual Resources -```bash -# Push only entities -npx base44 entities push - -# Deploy only functions (all) -npx base44 functions deploy -# Deploy specific functions -npx base44 functions deploy my-function other-function -# Deploy and prune removed functions -npx base44 functions deploy --force - -# Push only agents -npx base44 agents push - -# Pull connectors from Base44 -npx base44 connectors pull - -# Push only connectors -npx base44 connectors push - -# Deploy only site -npx base44 site deploy -y -``` - -### Opening the Dashboard -```bash -# Open app dashboard in browser -npx base44 dashboard -``` - -## Authentication - -Most commands require authentication. If you're not logged in, the CLI will automatically prompt you to login. Your session is stored locally and persists across CLI sessions. - -## Troubleshooting - -| Error | Solution | -| --------------------------- | ----------------------------------------------------------------------------------- | -| Not authenticated | Run `npx base44 login` first | -| No entities found | Ensure entities exist in `base44/entities/` directory | -| Entity not recognized | Ensure file uses kebab-case naming (e.g., `team-member.jsonc` not `TeamMember.jsonc`) | -| No functions found | Ensure functions exist in `base44/functions/` with valid `function.jsonc` configs | -| No agents found | Ensure agents exist in `base44/agents/` directory with valid `.jsonc` configs | -| Invalid agent name | Agent names must be lowercase alphanumeric with underscores only | -| No connectors found | Ensure connectors exist in `base44/connectors/` directory with valid `.jsonc` configs | -| Invalid connector type | Run `npx base44 connectors list-available` to see valid types | -| Duplicate connector type | Each connector type can only be defined once per project | -| Connector authorization timeout | Re-run `npx base44 connectors push` and complete the OAuth flow in your browser | -| No site configuration found | Check that `site.outputDirectory` is configured in project config | -| Site deployment fails | Ensure you ran `npm run build` first and the build succeeded | -| Update available message | If prompted to update, run `npm install -g base44@latest` (or use npx for local installs) | diff --git a/plugins/base44/skills/base44-cli/agents/openai.yaml b/plugins/base44/skills/base44-cli/agents/openai.yaml deleted file mode 100644 index 1d09ded00..000000000 --- a/plugins/base44/skills/base44-cli/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Base44 CLI" - short_description: "Initialize, configure, deploy, and manage Base44 projects" - default_prompt: "Use Base44 CLI guidance to initialize, configure, authenticate, and deploy this Base44 app." diff --git a/plugins/base44/skills/base44-cli/references/agents-pull.md b/plugins/base44/skills/base44-cli/references/agents-pull.md deleted file mode 100644 index eb10760ad..000000000 --- a/plugins/base44/skills/base44-cli/references/agents-pull.md +++ /dev/null @@ -1,80 +0,0 @@ -# base44 agents pull - -Pull AI agent configurations from Base44 to local files. Agents are conversational AI assistants that can interact with users, access your app's entities, and call backend functions. - -## Syntax - -```bash -npx base44 agents pull -``` - -## Authentication - -**Required**: Yes. If not authenticated, you'll be prompted to login first. - -## What It Does - -1. Fetches all agents from Base44 -2. Writes agent files to the `base44/agents/` directory -3. Deletes local agent files that don't exist remotely -4. Reports written and deleted agents - -## Prerequisites - -- Must be run from a Base44 project directory -- Project must be linked to a Base44 app - -## Output - -```bash -$ npx base44 agents pull - -Fetching agents from Base44... -✓ Agents fetched successfully - -Syncing agent files... -✓ Agent files synced successfully - -Written: support_agent, order_bot -Deleted: old_agent - -Pulled 2 agents to base44/agents -``` - -When agents are already up to date (no changes): -```bash -$ npx base44 agents pull - -Fetching agents from Base44... -✓ Agents fetched successfully - -Syncing agent files... -✓ Agent files synced successfully - -All agents are already up to date - -Pulled 3 agents to base44/agents -``` - -## Agent Synchronization - -The pull operation synchronizes remote agents to your local files: - -- **Written**: Agent files created or updated from remote -- **Deleted**: Local agent files removed (didn't exist remotely) - -**Warning**: This operation replaces all local agent configurations with remote versions. Any local changes not pushed to Base44 will be overwritten. - -## Use Cases - -- Sync agent configurations to a new development machine -- Get the latest agent configurations from your team -- Restore local agent files after accidental deletion -- Start working on an existing project with agents - -## Notes - -- This command syncs agent configurations, not conversation data -- Agent files are stored as `.jsonc` in the `base44/agents/` directory -- The directory location is configurable via `agentsDir` in `config.jsonc` -- Use `base44 agents push` to upload local changes to Base44 diff --git a/plugins/base44/skills/base44-cli/references/agents-push.md b/plugins/base44/skills/base44-cli/references/agents-push.md deleted file mode 100644 index fa2795b4b..000000000 --- a/plugins/base44/skills/base44-cli/references/agents-push.md +++ /dev/null @@ -1,156 +0,0 @@ -# base44 agents push - -Push local AI agent configurations to Base44. Agents are conversational AI assistants that can interact with users, access your app's entities, and call backend functions. - -## Syntax - -```bash -npx base44 agents push -``` - -## Authentication - -**Required**: Yes. If not authenticated, you'll be prompted to login first. - -## What It Does - -1. Reads all agent files from the `base44/agents/` directory -2. Validates agent configurations -3. Displays the count of agents to be pushed -4. Uploads agents to the Base44 backend -5. Reports the results: created, updated, and deleted agents - -## Prerequisites - -- Must be run from a Base44 project directory -- Project must have agent definitions in the `base44/agents/` folder - -## Output - -```bash -$ npx base44 agents push - -Found 2 agents to push -Pushing agents to Base44... - -Created: support_agent -Updated: order_bot -Deleted: old_agent - -✓ Agents pushed to Base44 -``` - -## Agent Synchronization - -The push operation synchronizes your local agents with Base44: - -- **Created**: New agents that didn't exist in Base44 -- **Updated**: Existing agents with modified configuration -- **Deleted**: Agents that were removed from your local configuration - -**Warning**: This is a full sync operation. Agents removed locally will be deleted from Base44. - -## Error Handling - -If no agents are found in your project: -```bash -$ npx base44 agents push -No local agents found - this will delete all remote agents -``` - -If an agent has an invalid name: -```bash -$ npx base44 agents push -Error: Agent name must be lowercase alphanumeric with underscores -``` - -## Agent Configuration Schema - -Each agent file should be a `.jsonc` file in `base44/agents/` with this structure: - -```jsonc -{ - "name": "agent_name", // Required: lowercase alphanumeric with underscores, 1-100 chars - "description": "Brief description of what this agent does", // Required: min 1 char - "instructions": "Detailed instructions for the agent's behavior", // Required: min 1 char - "tool_configs": [ // Optional: defaults to [] - // Entity tool - gives agent access to entity operations - { "entity_name": "Task", "allowed_operations": ["read", "create", "update", "delete"] }, - // Backend function tool - gives agent access to a function - { "function_name": "send_email", "description": "Send an email notification" } - ], - "whatsapp_greeting": "Hello! How can I help you today?" // Optional -} -``` - -**Naming rules:** -- **Agent names** must match pattern: `/^[a-z0-9_]+$/` (lowercase alphanumeric with underscores only, 1-100 characters) - - Valid: `support_agent`, `order_bot`, `task_helper` - - Invalid: `Support-Agent`, `OrderBot`, `task helper` -- **Agent file names** must use underscores (matching the agent name) - - Valid: `support_agent.jsonc`, `order_bot.jsonc` - - Invalid: `support-agent.jsonc` (hyphens not allowed) -- **Entity names in `tool_configs`** must use PascalCase (matching the entity's `name` field) - - Valid: `"entity_name": "Task"`, `"entity_name": "TeamMember"` - - Invalid: `"entity_name": "task"`, `"entity_name": "team_member"` - -**Required fields:** -- `name`: Required, must follow naming rules above -- `description`: Required, minimum 1 character -- `instructions`: Required, minimum 1 character -- `tool_configs`: Optional, defaults to empty array -- `whatsapp_greeting`: Optional - -### Common Mistake: Wrong tool_configs Format - -**WRONG** - Do NOT use `tools` with `type` and `entity`: -```jsonc -{ - "name": "my_agent", - "tools": [ // ❌ WRONG - { "type": "entity_query", "entity": "Task" } - ] -} -``` - -**CORRECT** - Use `tool_configs` with `entity_name` and `allowed_operations`: -```jsonc -{ - "name": "my_agent", - "tool_configs": [ // ✅ CORRECT - { "entity_name": "Task", "allowed_operations": ["read"] } - ] -} -``` - -### Best Practices for Agent Instructions - -When giving agents access to entities, be explicit in the instructions about using the tools: - -```jsonc -{ - "name": "support_agent", - "instructions": "You are a helpful support agent.\n\nIMPORTANT: You have access to customer data through entity tools. When users ask about their orders or account:\n1. ALWAYS use the Order entity tool to query their order history\n2. Use the Customer entity tool to look up account details\n3. Analyze the data and provide personalized responses\n\nAlways query the relevant entities first before answering questions about user data.", - "tool_configs": [ - { "entity_name": "Order", "allowed_operations": ["read"] }, - { "entity_name": "Customer", "allowed_operations": ["read"] } - ] -} -``` - -Without explicit instructions to use the entity tools, the agent may not proactively query user data when asked. - -## Use Cases - -- After defining new agents in your project -- When modifying existing agent configurations -- To sync agent changes before testing -- As part of your development workflow when agent behavior changes - -## Notes - -- This command syncs the agent configuration, not conversation data -- Changes are applied to your Base44 project immediately -- Make sure to test agent changes in a development environment first -- Agent definitions are located in the `base44/agents/` directory -- Use `base44 agents pull` to download agents from Base44 diff --git a/plugins/base44/skills/base44-cli/references/auth-login.md b/plugins/base44/skills/base44-cli/references/auth-login.md deleted file mode 100644 index adebd275c..000000000 --- a/plugins/base44/skills/base44-cli/references/auth-login.md +++ /dev/null @@ -1,54 +0,0 @@ -# base44 login - -Authenticate with Base44 using device code flow. - -## Syntax - -```bash -npx base44 login -``` - -## Authentication - -**Required**: No (this is the login command itself) - -## How It Works - -The login command uses OAuth 2.0 device code flow for authentication: - -1. Generates a device code for authentication -2. Displays a verification code and verification URI -3. Directs you to visit the URI and enter the code -4. Polls for authentication completion (up to device code expiration) -5. Retrieves access and refresh tokens upon successful authentication -6. Fetches and displays your user information -7. Saves authentication data locally with expiration timestamp - -## Interactive Flow - -```bash -$ npx base44 login - -Please visit: https://auth.base44.com/device -Enter code: ABCD-EFGH - -Waiting for authentication... -✓ Successfully authenticated! - -Logged in as: user@example.com -``` - -## Session Management - -- Authentication tokens are stored locally on your device -- Tokens include expiration timestamps -- The session persists across CLI sessions -- Other commands will automatically use your stored credentials -- Use `npx base44 logout` to clear your session -- Use `npx base44 whoami` to check your current authentication status - -## Notes - -- You only need to login once per device -- If your session expires, you'll be prompted to login again when running authenticated commands -- The CLI automatically prompts for login when you run commands that require authentication diff --git a/plugins/base44/skills/base44-cli/references/auth-logout.md b/plugins/base44/skills/base44-cli/references/auth-logout.md deleted file mode 100644 index 33c2ddf47..000000000 --- a/plugins/base44/skills/base44-cli/references/auth-logout.md +++ /dev/null @@ -1,32 +0,0 @@ -# base44 logout - -Logout from current device and clear stored authentication data. - -## Syntax - -```bash -npx base44 logout -``` - -## Authentication - -**Required**: No - -## What It Does - -- Deletes stored authentication data from your device -- Clears your local session -- Removes access and refresh tokens - -## Output - -```bash -$ npx base44 logout -Logged out successfully -``` - -## Notes - -- You can logout even if you're not currently logged in (no error) -- After logout, you'll need to run `npx base44 login` again to use authenticated commands -- This only affects the current device; your Base44 account remains active diff --git a/plugins/base44/skills/base44-cli/references/auth-password-login.md b/plugins/base44/skills/base44-cli/references/auth-password-login.md deleted file mode 100644 index 66131f5e4..000000000 --- a/plugins/base44/skills/base44-cli/references/auth-password-login.md +++ /dev/null @@ -1,30 +0,0 @@ -# base44 auth password-login - -Enable or disable username & password authentication for your Base44 app. - -## Syntax - -```bash -npx base44 auth password-login -``` - -## Arguments - -| Argument | Description | Required | -|----------|-------------|----------| -| `` | Enable or disable password authentication | Yes | - -## Examples - -```bash -# Enable username & password authentication -npx base44 auth password-login enable - -# Disable username & password authentication -npx base44 auth password-login disable -``` - -## Notes - -- Updates the local auth config file only — run `npx base44 auth push` or `npx base44 deploy` to apply the change to Base44. -- Disabling password auth when no other login methods are enabled will warn you that users will be locked out. diff --git a/plugins/base44/skills/base44-cli/references/auth-pull.md b/plugins/base44/skills/base44-cli/references/auth-pull.md deleted file mode 100644 index cf3ba4230..000000000 --- a/plugins/base44/skills/base44-cli/references/auth-pull.md +++ /dev/null @@ -1,20 +0,0 @@ -# base44 auth pull - -Pull the auth configuration from Base44 to local files. - -## Syntax - -```bash -npx base44 auth pull -``` - -## Examples - -```bash -npx base44 auth pull -``` - -## Notes - -- Overwrites the local auth config file with the remote configuration. -- The auth config file is written to `base44/auth/` (the `authDir` configured in `config.jsonc`). diff --git a/plugins/base44/skills/base44-cli/references/auth-push.md b/plugins/base44/skills/base44-cli/references/auth-push.md deleted file mode 100644 index b539e3ecd..000000000 --- a/plugins/base44/skills/base44-cli/references/auth-push.md +++ /dev/null @@ -1,31 +0,0 @@ -# base44 auth push - -Push the local auth configuration to Base44. - -## Syntax - -```bash -npx base44 auth push [options] -``` - -## Options - -| Option | Description | Required | -|--------|-------------|----------| -| `-y, --yes` | Skip confirmation prompt | No | - -## Examples - -```bash -# Push auth config (interactive confirmation) -npx base44 auth push - -# Push auth config without confirmation (for CI/CD) -npx base44 auth push -y -``` - -## Notes - -- Requires a local auth config file to exist. Run `npx base44 auth pull` first if you haven't set up a local auth config. -- If the local config has no login methods enabled, the CLI will warn that pushing will lock out all users. -- In non-interactive mode (CI/CD), `--yes` is required. diff --git a/plugins/base44/skills/base44-cli/references/auth-whoami.md b/plugins/base44/skills/base44-cli/references/auth-whoami.md deleted file mode 100644 index abe450cd1..000000000 --- a/plugins/base44/skills/base44-cli/references/auth-whoami.md +++ /dev/null @@ -1,37 +0,0 @@ -# base44 whoami - -Display the currently authenticated user. - -## Syntax - -```bash -npx base44 whoami -``` - -## Authentication - -**Required**: Yes. If not authenticated, you'll be prompted to login first. - -## What It Does - -- Reads stored authentication data -- Displays the email of the currently logged-in user - -## Output - -```bash -$ npx base44 whoami -Logged in as: user@example.com -``` - -## Use Cases - -- Verify you're logged in before running other commands -- Check which account you're currently using -- Confirm authentication is working properly -- Useful in scripts or automation to verify credentials - -## Notes - -- If you're not logged in, the command will prompt you to authenticate first -- The email displayed matches your Base44 account email diff --git a/plugins/base44/skills/base44-cli/references/automations.md b/plugins/base44/skills/base44-cli/references/automations.md deleted file mode 100644 index 7028aa24b..000000000 --- a/plugins/base44/skills/base44-cli/references/automations.md +++ /dev/null @@ -1,343 +0,0 @@ -# Function Automations - -Automations are triggers attached to backend functions. They cause a function to run automatically on a schedule (CRON, simple interval, or one-time) or when entity data changes (create, update, delete). Automations are defined in the `automations` array inside each function's `function.jsonc` and are deployed together with the function via `npx base44 functions deploy`. - -## Overview - -- **Where**: `base44/functions//function.jsonc` — optional `automations` array -- **Deploy**: Automations are deployed with the function; no separate command -- **Types**: Scheduled (one-time, CRON, simple interval) and entity hooks - -## Common Fields (All Automation Types) - -Every automation shares these base fields: - -| Property | Type | Required | Description | -|----------|------|----------|-------------| -| `name` | string | Yes | Display name for the automation (min 1 char) | -| `description` | string \| null | No | Optional description | -| `function_args` | object \| null | No | Key-value args passed to the function when it runs | -| `is_active` | boolean | No | Whether the automation is active (default: `true`) | - -## Automation Types - -### 1. Scheduled One-Time - -Runs the function once at a specific date/time. - -| Property | Type | Required | Description | -|----------|------|----------|-------------| -| `type` | `"scheduled"` | Yes | Must be `"scheduled"` | -| `schedule_mode` | `"one-time"` | Yes | One-time execution | -| `one_time_date` | string | Yes | ISO date/time when the function should run (e.g. `"2024-01-15T10:00:00"`) | - -**Example:** - -```jsonc -{ - "name": "my-function", - "entry": "index.ts", - "automations": [ - { - "name": "Launch reminder", - "type": "scheduled", - "schedule_mode": "one-time", - "one_time_date": "2026-03-01T09:00:00.000Z", - "description": "One-time reminder on launch day" - } - ] -} -``` - -### 2. Scheduled CRON (Recurring) - -Runs the function on a cron schedule. **Minimum interval is 5 minutes.** - -| Property | Type | Required | Description | -|----------|------|----------|-------------| -| `type` | `"scheduled"` | Yes | Must be `"scheduled"` | -| `schedule_mode` | `"recurring"` | Yes | Recurring execution | -| `schedule_type` | `"cron"` | Yes | Use cron expression | -| `cron_expression` | string | Yes | Standard cron: `minute hour day-of-month month day-of-week` | -| `ends_type` | `"never"` \| `"on"` \| `"after"` | No | When the schedule stops (default: `"never"`) | -| `ends_on_date` | string \| null | No | When `ends_type` is `"on"`, ISO date to stop | -| `ends_after_count` | number \| null | No | When `ends_type` is `"after"`, number of runs then stop | - -**End conditions** (apply to both CRON and simple recurring): -- `ends_type="never"` — Run indefinitely (default) -- `ends_type="on"` — Run until a date: set `ends_on_date` (e.g. `"2024-12-31T23:59:59"`) -- `ends_type="after"` — Run N times: set `ends_after_count` (e.g. `10`) - -**Cron format:** `minute hour day-of-month month day-of-week` - -**Examples:** -- `"*/5 * * * *"` — every 5 minutes (minimum interval) -- `"0 9 * * *"` — 9am daily -- `"0 9 * * 1-5"` — 9am every weekday (Mon–Fri) - -**Example:** - -```jsonc -{ - "name": "daily-report", - "entry": "index.ts", - "automations": [ - { - "name": "Daily Report", - "type": "scheduled", - "schedule_mode": "recurring", - "schedule_type": "cron", - "cron_expression": "0 9 * * *", - "description": "Run every day at 9:00 UTC", - "is_active": true - } - ] -} -``` - -### 3. Scheduled Simple (Recurring Interval) - -Runs the function on a simple repeat (every N minutes/hours/days/weeks/months). **Minimum interval for minutes is 5** (e.g. every 5 minutes). - -| Property | Type | Required | Description | -|----------|------|----------|-------------| -| `type` | `"scheduled"` | Yes | Must be `"scheduled"` | -| `schedule_mode` | `"recurring"` | Yes | Recurring execution | -| `schedule_type` | `"simple"` | Yes | Use simple interval | -| `repeat_unit` | `"minutes"` \| `"hours"` \| `"days"` \| `"weeks"` \| `"months"` | Yes | Unit of repetition | -| `repeat_interval` | number | No | Positive integer; interval within the unit (default 1). For minutes, minimum is 5. | -| `start_time` | string \| null | No | Time of day (e.g. `"09:00"`, `"00:00"`) | -| `repeat_on_days` | number[] \| null | No | For weeks: 0–6 (0 = Sunday, 6 = Saturday) | -| `repeat_on_day_of_month` | number \| null | No | For months: 1–31 | -| `ends_type` | `"never"` \| `"on"` \| `"after"` | No | When the schedule stops (default: `"never"`) | -| `ends_on_date` | string \| null | No | When `ends_type` is `"on"`, ISO date to stop | -| `ends_after_count` | number \| null | No | When `ends_type` is `"after"`, number of runs then stop | - -**End conditions:** Same as for CRON — `ends_type` / `ends_on_date` / `ends_after_count` (see Scheduled CRON above). - -**Simple schedule examples:** -- Every 5 minutes: `repeat_interval=5`, `repeat_unit="minutes"` (minimum) -- Hourly: `repeat_interval=1`, `repeat_unit="hours"` -- Daily at specific time: `repeat_interval=1`, `repeat_unit="days"`, `start_time="09:00"` -- Weekly on specific days: `repeat_unit="weeks"`, `repeat_on_days=[1, 5]`, `start_time="10:00"` (e.g. Mon and Fri) -- Monthly on specific day: `repeat_unit="months"`, `repeat_on_day_of_month=15`, `start_time="00:00"` - -**Example:** - -```jsonc -{ - "name": "weekly-cleanup", - "entry": "index.ts", - "automations": [ - { - "name": "Weekly Cleanup", - "type": "scheduled", - "schedule_mode": "recurring", - "schedule_type": "simple", - "repeat_unit": "weeks", - "repeat_interval": 1, - "repeat_on_days": [1], - "start_time": "02:00", - "description": "Every Monday at 2:00" - } - ] -} -``` - -### 4. Entity Hook - -Runs the function when entity records are created, updated, or deleted. - -**Required:** `entity_name`, `event_types` (array of `"create"`, `"update"`, `"delete"` — at least one). - -| Property | Type | Required | Description | -|----------|------|----------|-------------| -| `type` | `"entity"` | Yes | Must be `"entity"` | -| `entity_name` | string | Yes | Entity name (matches entity schema name, e.g. `Order`, `Task`) | -| `event_types` | `("create" \| "update" \| "delete")[]` | Yes | At least one; which events trigger the function | - -**Example use cases:** -- Send email on new order: `entity_name="Order"`, `event_types=["create"]` -- Track status changes: `entity_name="Order"`, `event_types=["update"]` -- Cleanup on delete: `entity_name="User"`, `event_types=["delete"]` -- Multiple events: `entity_name="Order"`, `event_types=["create", "update"]` - -**Example config:** - -```jsonc -{ - "name": "on-order-created", - "entry": "index.ts", - "automations": [ - { - "name": "On Order Created", - "type": "entity", - "entity_name": "Order", - "event_types": ["create"], - "description": "Run when a new order is created" - }, - { - "name": "On Order Update or Delete", - "type": "entity", - "entity_name": "Order", - "event_types": ["update", "delete"] - } - ] -} -``` - -**Note:** `entity_name` must match the entity schema `name` in `base44/entities/` (e.g. entity file `order.jsonc` with `"name": "Order"` → use `"entity_name": "Order"`). - -#### Entity hook payload - -The function receives a JSON body with: - -| Field | Description | -|-------|-------------| -| `event` | `{ type, entity_name, entity_id }` — event type, entity name, and record id | -| `data` | Current entity data. `null` if `payload_too_large` is true | -| `old_data` | Previous entity data (only for `"update"` events). `null` if `payload_too_large` is true or for create/delete | -| `payload_too_large` | `true` when entity data exceeded 200KB and was omitted. Use the Base44 SDK to fetch: `await base44.entities..get(entity_id)` (or the dynamic API) to load the record. | - -**Authentication / user identity:** When an automation runs (scheduled or entity hook), the request is authenticated as the **user who created the automation**, not as the user who performed the action. So `await base44.auth.me()` returns the automation creator. **There is no way to get the user who triggered the entity change** (e.g. who created, updated, or deleted the record). If you need to attribute actions, store a user reference on the entity (e.g. `created_by`, `updated_by`) and read it from `data` / `old_data` in the payload. - -## Full Examples - -### Daily CRON report - -**base44/functions/daily-report/function.jsonc:** - -```jsonc -{ - "name": "daily-report", - "entry": "index.ts", - "automations": [ - { - "name": "Daily Report", - "type": "scheduled", - "schedule_mode": "recurring", - "schedule_type": "cron", - "cron_expression": "0 9 * * *", - "is_active": true - } - ] -} -``` - -**base44/functions/daily-report/index.ts:** - -```typescript -import { createClientFromRequest } from "npm:@base44/sdk"; - -Deno.serve(async (req) => { - const base44 = createClientFromRequest(req); - // Scheduled runs get auth context; use asServiceRole if you need full access - const base44Admin = base44.asServiceRole; - - const orders = await base44Admin.entities.Orders.list({ limit: 100 }); - const summary = { total: orders.length, date: new Date().toISOString() }; - - // e.g. send to Slack, email, or store in another entity - return Response.json({ success: true, summary }); -}); -``` - -### Entity hook: on order created - -**base44/functions/on-order-created/function.jsonc:** - -```jsonc -{ - "name": "on-order-created", - "entry": "index.ts", - "automations": [ - { - "name": "On Order Created", - "type": "entity", - "entity_name": "Order", - "event_types": ["create"], - "is_active": true - } - ] -} -``` - -**base44/functions/on-order-created/index.ts:** - -```typescript -import { createClientFromRequest } from "npm:@base44/sdk"; - -Deno.serve(async (req) => { - const base44 = createClientFromRequest(req); - const payload = await req.json(); - const { event, data, old_data, payload_too_large } = payload; - - // event: { type, entity_name, entity_id } - const entityId = event.entity_id; - const eventType = event.type; - - // If payload was too large, data/old_data are null — fetch via SDK - let current = data; - if (payload_too_large && eventType !== "delete") { - current = await base44.asServiceRole.entities.Orders.get(entityId); - } - - // e.g. send confirmation email on create, or compare old_data vs data on update - return Response.json({ success: true, orderId: entityId, eventType }); -}); -``` - -### Weekly cleanup (simple schedule) - -**base44/functions/weekly-cleanup/function.jsonc:** - -```jsonc -{ - "name": "weekly-cleanup", - "entry": "index.ts", - "automations": [ - { - "name": "Weekly Cleanup", - "type": "scheduled", - "schedule_mode": "recurring", - "schedule_type": "simple", - "repeat_unit": "weeks", - "repeat_interval": 1, - "repeat_on_days": [1], - "start_time": "02:00", - "description": "Every Monday at 2:00" - } - ] -} -``` - -## Common Patterns - -| Pattern | Use | Automation type | -|--------|-----|------------------| -| Daily report / digest | Email or Slack at 9am | CRON with `cron_expression`: `0 9 * * *` | -| On new record | Notify, sync, or validate when entity is created | Entity hook with `event_types`: `["create"]` | -| On update/delete | Audit, cache invalidation, or cleanup | Entity hook with `event_types`: `["update"]` or `["delete"]` | -| Weekly job | Cleanup or aggregation every Monday | Simple with `repeat_unit`: `"weeks"`, `repeat_on_days`: `[1]` | -| One-time run | Launch task or migration at a fixed time | One-time with `one_time_date` | - -## Deploying - -Automations are deployed with their function. There is no separate automation deploy command. - -```bash -npx base44 functions deploy -``` - -This deploys all functions in `base44/functions/` and their `automations` arrays. For more on deployment, see [functions-deploy.md](functions-deploy.md). - -## Common Mistakes - -| Wrong | Correct | Why | -|-------|---------|-----| -| `entity_name: "order"` when schema name is `Order` | `entity_name: "Order"` | Entity name must match schema `name` exactly | -| `event_types: []` or missing | `event_types: ["create"]` (at least one) | At least one event type is required for entity hooks | -| Assuming `base44.auth.me()` is the user who triggered the entity change | Use `data` / `old_data` (e.g. `created_by`, `updated_by`) if you need who did the action | In automations, `auth.me()` is the user who **created the automation**. The triggering user is not available. | -| `schedule_type: "cron"` without `cron_expression` | Always set `cron_expression` for cron | Cron schedules require a valid cron expression | -| Putting automations in a separate file | Put `automations` inside `function.jsonc` | Automations are part of the function config | -| Expecting a separate `base44 automations deploy` | Use `npx base44 functions deploy` | Automations deploy with the function | diff --git a/plugins/base44/skills/base44-cli/references/connectors-create.md b/plugins/base44/skills/base44-cli/references/connectors-create.md deleted file mode 100644 index 64a55a2db..000000000 --- a/plugins/base44/skills/base44-cli/references/connectors-create.md +++ /dev/null @@ -1,126 +0,0 @@ -# Creating Connectors - -Connectors are OAuth integrations that let your Base44 app connect to external services like Google Calendar, Slack, Notion, and more. Once connected, you can use access tokens in backend functions to call external APIs directly. - -## Key Concepts - -- **Direct API Access**: Connectors provide raw OAuth access tokens - you call the external APIs directly from backend functions -- **App Builder's Account**: Connects your account (the app builder), not your end users' accounts -- **Backend Functions Only**: Tokens are only accessible server-side for security - -## File Location - -Create connector files in the `base44/connectors/` directory (or the directory specified by `connectorsDir` in your config.jsonc). - -**File naming:** `{type}.jsonc` or `{type}.json` - -Examples: -- `base44/connectors/googlecalendar.jsonc` -- `base44/connectors/slack.jsonc` -- `base44/connectors/notion.json` - -## Schema - -Each connector file must specify a `type` and optionally a list of `scopes`: - -```jsonc -{ - "type": "googlecalendar", - "scopes": [ - "https://www.googleapis.com/auth/calendar.readonly", - "https://www.googleapis.com/auth/calendar.events" - ] -} -``` - -### Fields - -| Field | Type | Required | Description | -|-------|------|----------|-------------| -| `type` | string | Yes | The integration type (run `npx base44 connectors list-available` to see options) | -| `scopes` | string[] | No | OAuth scopes to request (defaults to `[]`) | - -## Discovering Available Connector Types - -Run the following command to get the up-to-date list of supported connector types. Note: `stripe` is also a valid type but is not returned by this command — treat it as supported (see Stripe example below). - -```bash -npx base44 connectors list-available -``` - -This returns a JSON object with all available integrations, their display names, descriptions, and any required connection config fields. Example output (trimmed): - -```json -{ - "integrations": [ - { - "integration_type": "googlecalendar", - "display_name": "Google Calendar", - "description": "Access and manage Google Calendar events", - "connection_config_fields": [] - }, - { - "integration_type": "slack", - "display_name": "Slack User", - "description": "Send messages and interact with Slack as yourself (user integration)", - "connection_config_fields": [] - }, - { - "integration_type": "share_point", - "display_name": "SharePoint", - "description": "Manage documents, lists, sites, and collaboration content in SharePoint", - "connection_config_fields": [ - { - "name": "subdomain", - "display_name": "SharePoint Site", - "description": "The name of your SharePoint site (e.g., sites/mysite)", - "placeholder": "sites/mysite", - "required": true, - "validation_pattern": "^[a-zA-Z0-9/_-]+$", - "validation_error": "Please enter a valid SharePoint site path" - } - ] - } - ] -} -``` - -Use the `integration_type` value from this output as the `type` field in your connector file. Some connectors require additional `connection_config_fields` — check the output for details. - -### Stripe (Sandbox) - -```jsonc -// base44/connectors/stripe.jsonc -{ - "type": "stripe", - "scopes": [] -} -``` - -Note: Stripe does not require an OAuth browser flow. When you push this connector, Base44 automatically provisions a Stripe sandbox account on the server side. You may receive a claim URL in the push output to link the sandbox to your Stripe account. - -## Rules and Constraints - -1. **One connector per type**: You cannot have multiple connectors of the same type (e.g., two `googlecalendar` connectors) - -2. **Type must be valid**: The `type` field must be a valid integration type (run `npx base44 connectors list-available` to see available types) - -3. **Scopes are provider-specific**: Each service has its own scope format - refer to the provider's documentation - -## Next Steps - -After creating connector files, push them to Base44: - -```bash -npx base44 connectors push -``` - -This will prompt you to authorize each new OAuth connector in your browser. Stripe is the exception — it is provisioned automatically without a browser flow. See [connectors-push.md](connectors-push.md) for details. - -To pull existing connectors from Base44 to local files: - -```bash -npx base44 connectors pull -``` - -See [connectors-pull.md](connectors-pull.md) for details. diff --git a/plugins/base44/skills/base44-cli/references/connectors-list-available.md b/plugins/base44/skills/base44-cli/references/connectors-list-available.md deleted file mode 100644 index 9e89acaa2..000000000 --- a/plugins/base44/skills/base44-cli/references/connectors-list-available.md +++ /dev/null @@ -1,56 +0,0 @@ -# base44 connectors list-available - -List all integration types available in the Base44 connector catalog. - -## Syntax - -```bash -npx base44 connectors list-available -``` - -## Authentication - -**Required**: Yes. If not authenticated, you'll be prompted to login first. - -## What It Does - -Fetches the catalog of available integration types from Base44 and displays each one with its display name, description, and any required configuration fields. - -## Output - -```bash -$ npx base44 connectors list-available - -✓ Available integrations fetched successfully - -Google Calendar - integrationType: googlecalendar - description: Access Google Calendar events and schedules - connectionConfigFields: [] - -Slack - integrationType: slack - description: Send messages and interact with Slack workspaces - connectionConfigFields: [] - -Stripe - integrationType: stripe - description: Process payments and manage Stripe accounts - connectionConfigFields: [] - -Found 14 available integrations. -``` - -Each integration is displayed in YAML format showing the integration type, description, and any connection configuration fields required for setup. - -## Use Cases - -- Discover all supported connector types before creating connector files -- Check if a specific integration is available in your Base44 plan -- See what configuration fields (if any) a connector requires - -## Related Commands - -- [connectors-create.md](connectors-create.md) - How to create connector configuration files -- [connectors-push.md](connectors-push.md) - Push local connectors to Base44 -- [connectors-pull.md](connectors-pull.md) - Pull connectors from Base44 to local files diff --git a/plugins/base44/skills/base44-cli/references/connectors-pull.md b/plugins/base44/skills/base44-cli/references/connectors-pull.md deleted file mode 100644 index 97502a545..000000000 --- a/plugins/base44/skills/base44-cli/references/connectors-pull.md +++ /dev/null @@ -1,78 +0,0 @@ -# base44 connectors pull - -Pull connector configurations from Base44 to local files. Replaces all local connector configs with the remote versions. - -## Syntax - -```bash -npx base44 connectors pull -``` - -## Authentication - -**Required**: Yes. If not authenticated, you'll be prompted to login first. - -## What It Does - -1. Fetches all connectors from Base44 -2. Writes connector files to the `base44/connectors/` directory -3. Deletes local connector files that don't exist remotely -4. Reports written and deleted connectors - -## Prerequisites - -- Must be run from a Base44 project directory -- Project must be linked to a Base44 app - -## Output - -```bash -$ npx base44 connectors pull - -Fetching connectors from Base44... -✓ Connectors fetched successfully - -Syncing connector files... -✓ Connector files synced successfully - -Written: googlecalendar, slack -Deleted: notion - -Pulled 2 connectors to base44/connectors -``` - -## Connector Synchronization - -The pull operation synchronizes remote connectors to your local files: - -- **Written**: Connector files created or updated from remote -- **Deleted**: Local connector files removed (didn't exist remotely) -- **Up to date**: If no changes needed, reports "All connectors are already up to date" - -**Warning**: This operation replaces all local connector configurations with remote versions. Any local changes not pushed to Base44 will be overwritten. - -## Error Handling - -If no connectors exist on Base44: -```bash -$ npx base44 connectors pull -All connectors are already up to date -``` - -## Use Cases - -- Sync connector configurations to a new development machine -- Get the latest connector configurations from your team -- Restore local connector files after accidental deletion -- Start working on an existing project with connectors - -## Notes - -- Connector files are stored as `.jsonc` in the `base44/connectors/` directory -- The directory location is configurable via `connectorsDir` in `config.jsonc` -- Use `base44 connectors push` to upload local changes to Base44 - -## Related Commands - -- [connectors-create.md](connectors-create.md) - How to create connector configuration files -- [connectors-push.md](connectors-push.md) - Push local connectors to Base44 diff --git a/plugins/base44/skills/base44-cli/references/connectors-push.md b/plugins/base44/skills/base44-cli/references/connectors-push.md deleted file mode 100644 index b509512ff..000000000 --- a/plugins/base44/skills/base44-cli/references/connectors-push.md +++ /dev/null @@ -1,137 +0,0 @@ -# base44 connectors push - -Push local connector configurations to Base44, synchronizing scopes and handling OAuth authorization. - -## Usage - -```bash -npx base44 connectors push -``` - -## What It Does - -1. **Reads local connectors** from your `base44/connectors/` directory -2. **Syncs with Base44** - updates scopes for existing connectors -3. **Adds new connectors** - new OAuth connector types trigger authorization; Stripe is provisioned automatically -4. **Removes unlisted connectors** - connectors not in your local files are removed from Base44 - -## OAuth Authorization Flow - -When you add a new connector, it needs to be authorized: - -1. The CLI detects which connectors need authorization -2. You're prompted: "Open browser to authorize now?" -3. If you accept, the browser opens to the OAuth provider (Google, Slack, etc.) -4. You log into your account and approve the requested permissions -5. The browser closes and the CLI confirms authorization - -**Important**: You choose which account to connect by logging into it during the OAuth flow. For example, if you have multiple Google accounts, you select which one to use in the Google login screen. - -## Example Output - -### Pushing connectors (no new authorization needed) - -``` -Found 2 connectors to push: googlecalendar, slack -✓ Connectors pushed - -Summary: - Synced: googlecalendar, slack -``` - -### Pushing new connectors (authorization required) - -``` -Found 3 connectors to push: googlecalendar, slack, notion -✓ Connectors pushed - -2 connector(s) require authorization in your browser: - slack: https://auth.base44.io/oauth/... - notion: https://auth.base44.io/oauth/... - -? Open browser to authorize now? › Yes - -Opening browser for slack... -✓ slack authorization complete - -Opening browser for notion... -✓ notion authorization complete - -Summary: - Synced: googlecalendar - Added: slack, notion -``` - -### Pushing Stripe (no OAuth required) - -Stripe is provisioned automatically — no browser flow is needed: - -``` -Found 2 connectors to push: googlecalendar, stripe -✓ Connectors pushed - -Summary: - ✓ Stripe sandbox provisioned - Claim your Stripe sandbox: https://dashboard.stripe.com/... - Connectors dashboard: https://app.base44.com/... - Synced: googlecalendar -``` - -### Removing connectors - -If you delete a connector file locally and push, it will be removed: - -``` -Found 1 connectors to push: googlecalendar -✓ Connectors pushed - -Summary: - Synced: googlecalendar - Removed: slack -``` - -## CI/CD Environments - -In non-interactive environments (no TTY, such as CI/CD pipelines), the OAuth flow is skipped automatically: - -``` -Skipped OAuth in non-interactive mode. Run 'base44 connectors push' locally or open the links above to authorize. -``` - -You must run `npx base44 connectors push` locally to complete authorization for new connectors. - -## Skipping Authorization - -If you choose not to authorize immediately, the connectors remain in a pending state: - -``` -? Open browser to authorize now? › No - -Authorization skipped. Pending: slack, notion. Run 'base44 connectors push' again to complete. -``` - -Run the command again when you're ready to authorize. - -## Summary Status Meanings - -| Status | Meaning | -|--------|---------| -| Provisioned | Stripe sandbox was created automatically (no OAuth needed) | -| Synced | Connector already existed, scopes updated if needed | -| Added | New connector successfully authorized via OAuth | -| Removed | Connector was deleted from Base44 (not in local files) | -| Failed | Authorization timed out, failed, or was skipped | - -## Troubleshooting - -| Problem | Solution | -|---------|----------| -| Authorization timed out | Re-run `npx base44 connectors push` and complete OAuth faster | -| Authorization failed | Check that you approved all requested permissions | -| Wrong account connected | Remove the connector file, push to delete it, then add it back and authorize with the correct account | -| Browser didn't open | Copy the URL shown in the terminal and open it manually | - -## Related Commands - -- [connectors-create.md](connectors-create.md) - How to create connector configuration files -- [connectors-pull.md](connectors-pull.md) - Pull connectors from Base44 to local files diff --git a/plugins/base44/skills/base44-cli/references/create.md b/plugins/base44/skills/base44-cli/references/create.md deleted file mode 100644 index 75806453b..000000000 --- a/plugins/base44/skills/base44-cli/references/create.md +++ /dev/null @@ -1,111 +0,0 @@ -# base44 create - -Creates a new Base44 project from a template. This command is framework-agnostic and can either scaffold a complete project or add Base44 configuration to an existing project. - -## Critical: Non-Interactive Mode Required - -ALWAYS provide both the project name AND `--path` flag. Without both, the command opens an interactive TUI which agents cannot use properly. - -WRONG: `npx base44 create` -WRONG: `npx base44 create my-app` -RIGHT: `npx base44 create my-app -p ./my-app` - -## Syntax - -```bash -npx base44 create [name] --path [options] -``` - -## Arguments & Options - -| Argument/Option | Description | Required | -|--------|-------------|----------| -| `name` | Project name (positional argument) | Yes* | -| `-p, --path ` | Path where to create the project | Yes* | -| `-t, --template ` | Template ID (see templates below) | No | -| `--deploy` | Build and deploy the site (includes pushing entities) | No | -| `--no-skills` | Skip AI agent skills installation (skills are added by default) | No | - -*Required for non-interactive mode. Both `name` and `--path` must be provided together. - -## Template Selection (CRITICAL - Choose Appropriately) - -**You MUST select the most appropriate template based on user requirements:** - -| Template ID | When to Use | Example Scenarios | -|-------------|-------------|-------------------| -| `backend-and-client` | Creating a NEW full-stack web app from scratch | "Create a task app", "Build me a dashboard", "Make a SaaS app" | -| `backend-only` | Adding Base44 to an EXISTING project OR using a different framework (Next.js, Vue, Svelte, etc.) | "Add Base44 to my project", "I want to use Next.js", "I already have a frontend" | - -**Default Choice:** When the user asks to "create an app" or "build a project" without specifying a particular framework, use `backend-and-client` to provide a complete, production-ready application with Vite + React + Tailwind. - -## The `--path` Flag - -- **For `backend-and-client` template (new projects):** Use a new subfolder path - ```bash - npx base44 create my-app -p ./my-app -t backend-and-client - ``` -- **For `backend-only` template (existing projects):** Use `-p .` in the current directory - ```bash - npx base44 create my-app -p . - ``` - -## Workflow: Using `backend-only` with External Frameworks - -**CRITICAL: The project folder MUST exist BEFORE running `base44 create` with `backend-only`** - -The `backend-only` template only adds Base44 configuration files - it does NOT create a frontend. If you need a frontend with a specific framework: - -```bash -# Step 1: Initialize the frontend project FIRST -npm create vite@latest my-app -- --template react # or vue, svelte, etc. -# OR: npx create-next-app@latest my-app -# OR: any other framework's init command - -# Step 2: Navigate into the created folder -cd my-app - -# Step 3: Install Base44 CLI -npm install --save-dev base44 - -# Step 4: Add Base44 configuration -npx base44 create my-app -p . -``` - -**WARNING:** Do NOT: -- Create an empty folder manually, then try to run `npx create vite` inside it (will fail - folder exists) -- Run `base44 create` with `backend-only` expecting it to create a frontend (it won't) - -**DO:** -- Run the external framework's init command FIRST (it creates its own folder) -- Then run `base44 create` inside that folder with `-p .` - -## Examples - -```bash -# RECOMMENDED: Create full-stack project (for new apps) -npx base44 create my-app -p ./my-app -t backend-and-client - -# Create full-stack and deploy in one step -npx base44 create my-app -p ./my-app -t backend-and-client --deploy - -# Add Base44 to EXISTING project (must be inside the project folder) -npx base44 create my-app -p . - -# Add Base44 to existing project and deploy -npx base44 create my-app -p . --deploy - -# Create without adding AI agent skills -npx base44 create my-app -p . --no-skills -``` - -## What It Does - -1. Applies the selected template to the target path -2. Creates a `base44/` folder with configuration files -3. Registers the project with Base44 backend -4. Creates `base44/.app.jsonc` with the app ID -5. If `--deploy` is used: - - Pushes any entities defined in `base44/entities/` - - Runs install and build commands (for templates with frontend) - - Deploys the site to Base44 hosting diff --git a/plugins/base44/skills/base44-cli/references/dashboard.md b/plugins/base44/skills/base44-cli/references/dashboard.md deleted file mode 100644 index 834023399..000000000 --- a/plugins/base44/skills/base44-cli/references/dashboard.md +++ /dev/null @@ -1,45 +0,0 @@ -# base44 dashboard open - -Opens the Base44 app dashboard in your default web browser. - -## Syntax - -```bash -npx base44 dashboard open -``` - -## Authentication - -**Required**: Yes. If not authenticated, you'll be prompted to login first. - -## What It Does - -1. Reads the project's app ID from `base44/.app.jsonc` -2. Opens the dashboard URL in your default browser -3. Displays the dashboard URL in the terminal - -## Example - -```bash -# Open dashboard for current project -npx base44 dashboard open -``` - -## Output - -```bash -$ npx base44 dashboard open - -Dashboard opened at https://base44.cloud/apps/your-app-id -``` - -## Requirements - -- Must be run from a linked Base44 project directory (contains `base44/.app.jsonc`) -- Must be authenticated (run `npx base44 login` first) - -## Notes - -- The dashboard provides a web interface to manage your app's entities, functions, agents, users, and settings -- If you're not in a project directory, the command will fail with an error -- The command will not open a browser in CI environments (when `process.env.CI` is set) diff --git a/plugins/base44/skills/base44-cli/references/deploy.md b/plugins/base44/skills/base44-cli/references/deploy.md deleted file mode 100644 index ee66230d3..000000000 --- a/plugins/base44/skills/base44-cli/references/deploy.md +++ /dev/null @@ -1,101 +0,0 @@ -# base44 deploy - -Deploys all project resources (entities, functions, agents, connectors, and site) to Base44 in a single command. - -## Syntax - -```bash -npx base44 deploy [options] -``` - -## Options - -| Option | Description | -|--------|-------------| -| `-y, --yes` | Skip confirmation prompt | - -## What It Deploys - -The command automatically detects and deploys: - -1. **Entities** - All `.jsonc` files in `base44/entities/` -2. **Functions** - All functions in `base44/functions/` -3. **Agents** - All agent configurations in `base44/agents/` -4. **Connectors** - All connector configurations in `base44/connectors/` -5. **Auth Config** - Authentication settings from `base44/auth/` (if present) -6. **Site** - Built files from `site.outputDirectory` (if configured) - -## Examples - -```bash -# Interactive mode - shows what will be deployed and asks for confirmation -npx base44 deploy - -# Non-interactive - skip confirmation (for CI/CD or agent use) -npx base44 deploy -y -``` - -## Typical Workflow - -```bash -# 1. Make your changes (entities, functions, frontend code) - -# 2. Build the frontend (if you have one) -npm run build - -# 3. Deploy everything -npx base44 deploy -y -``` - -## What It Does - -1. Reads project configuration from `base44/config.jsonc` -2. Detects available resources (entities, functions, agents, connectors, site) -3. Shows a summary of what will be deployed -4. Asks for confirmation (unless `-y` flag is used) -5. Deploys all resources in sequence: - - Pushes entity schemas - - Deploys functions - - Pushes agent configurations - - Pushes connector configurations - - Pushes auth configuration - - Uploads site files -6. Handles OAuth authorization for any new connectors that require it -7. Displays the dashboard URL and app URL (if site was deployed) - -## Connector OAuth Flow - -If any connectors require authorization after deployment, the CLI will prompt you to open your browser to complete OAuth. In non-interactive environments (CI/CD, no TTY), OAuth prompts are skipped automatically. - -``` -Some connectors still require authorization. Run 'base44 connectors push' or open the links above in your browser. -``` - -## Requirements - -- Must be run from a linked Base44 project directory -- Must be authenticated (run `npx base44 login` first) -- For site deployment, must run `npm run build` first - -## Output - -After successful deployment: -- **Dashboard**: Link to your app's management dashboard -- **App URL**: Your deployed site's public URL (if site was included) - -## Notes - -- If no resources are found, the command exits with a message -- Use individual commands (`entities push`, `functions deploy`, `connectors push`, `site deploy`) if you only want to deploy specific resources -- The site must be built before deployment - this command does not run `npm run build` for you - -## Related Commands - -| Command | Description | -|---------|-------------| -| `base44 entities push` | Push only entities | -| `base44 functions deploy` | Deploy only functions | -| `base44 agents push` | Push only agents | -| `base44 connectors push` | Push only connectors | -| `base44 auth push` | Push only auth config | -| `base44 site deploy` | Deploy only the site | diff --git a/plugins/base44/skills/base44-cli/references/eject.md b/plugins/base44/skills/base44-cli/references/eject.md deleted file mode 100644 index 99735706f..000000000 --- a/plugins/base44/skills/base44-cli/references/eject.md +++ /dev/null @@ -1,85 +0,0 @@ -# base44 eject - -Download the code for an existing Base44 project to your local machine. - -## Syntax - -```bash -npx base44 eject [options] -``` - -## Options - -| Option | Description | Required | -|--------|-------------|----------| -| `-p, --path ` | Path where to write the project | No | -| `--project-id ` | Project ID to eject (skips interactive selection) | No | -| `-y, --yes` | Skip confirmation prompts | No | - -## What It Does - -The `eject` command allows you to download the source code of a Base44 project that was created or managed through the platform: - -1. Lists all ejectable projects (projects with managed source code) -2. Lets you select a project interactively (or specify via `--project-id`) -3. Downloads the project code to a local directory -4. Creates a new project as a copy (named "{Original Name} Copy") -5. Links the downloaded code to the new project -6. Creates `.env.local` with the new project ID -7. Optionally installs dependencies, builds, and deploys the project - -## Examples - -```bash -# Interactive mode - select project from list and specify path -npx base44 eject - -# Specify the output path -npx base44 eject -p ./my-project - -# Non-interactive - specify project ID and skip confirmations -npx base44 eject --project-id abc123 -p ./my-project -y -``` - -## Workflow - -When you run `eject`: - -1. **Project Selection**: Choose from available ejectable projects -2. **Path Selection**: Specify where to create the project (defaults to `./{project-name}` or `./` if current directory is empty) -3. **Download**: The project code is downloaded to the specified path -4. **New Project Creation**: A copy of the project is created in Base44 (e.g., "My App Copy") -5. **Linking**: The local code is linked to the new project -6. **Optional Deployment**: If the project has build commands configured, you'll be asked if you want to deploy - - Runs the install command (e.g., `npm install`) - - Runs the build command (e.g., `npm run build`) - - Deploys all resources with `base44 deploy` - -## Requirements - -- Must be authenticated (run `npx base44 login` first) -- The project must be ejectable (have managed source code) - -## Use Cases - -- Download a project created through the Base44 dashboard -- Clone a managed project for local development -- Create a copy of an existing project to customize - -## Notes - -- The command creates a **new project** as a copy, preserving the original -- The new project will be named "{Original Name} Copy" -- The downloaded code is automatically linked to the new project -- If the current directory is empty, the default path is `./` -- If the current directory has files, the default path is `./{kebab-case-project-name}` -- Only projects with `isManagedSourceCode !== false` can be ejected -- If no ejectable projects exist, the command exits with "No projects available to eject." - -## Related Commands - -| Command | Description | -|---------|-------------| -| `base44 create` | Create a new Base44 project from a template | -| `base44 link` | Link an existing directory to a Base44 project | -| `base44 deploy` | Deploy all project resources | diff --git a/plugins/base44/skills/base44-cli/references/entities-create.md b/plugins/base44/skills/base44-cli/references/entities-create.md deleted file mode 100644 index 2ad894dbe..000000000 --- a/plugins/base44/skills/base44-cli/references/entities-create.md +++ /dev/null @@ -1,555 +0,0 @@ -# Creating Entities - -Base44 entities are defined locally in your project and then pushed to the Base44 backend. - -## Critical: File Naming - -Entity files MUST use kebab-case naming: `{kebab-case-name}.jsonc` - -| Entity Name | File Name | -|-------------|-----------| -| `Task` | `task.jsonc` | -| `TeamMember` | `team-member.jsonc` | -| `ActivityLog` | `activity-log.jsonc` | - -WRONG: `TeamMember.jsonc`, `teamMember.jsonc` -RIGHT: `team-member.jsonc` - -## Table of Contents - -- [Creating Entities](#creating-entities) - - [Entity Directory](#entity-directory) - - [How to Create an Entity](#how-to-create-an-entity) - - [Entity Schema Structure](#entity-schema-structure) - - [Supported Field Types](#supported-field-types) - - [Field Properties](#field-properties) - - [Complete Example](#complete-example) - - [Naming Conventions](#naming-conventions) - - [Relationships Between Entities](#relationships-between-entities) - - [Row Level Security (RLS)](#row-level-security-rls) - - [Field Level Security (FLS)](#field-level-security-fls) - - [Pushing Entities](#pushing-entities) - -## Entity Directory - -All entity definitions must be placed in the `base44/entities/` folder in your project root. Each entity is defined in its own `.jsonc` file. - -Example structure: -``` -my-app/ - base44/ - entities/ - user.jsonc - product.jsonc - order.jsonc -``` - -## How to Create an Entity - -1. Create a new `.jsonc` file in the `base44/entities/` directory -2. Define your entity schema following the structure below -3. Push the changes to Base44 using the CLI - -## Entity Schema Structure - -Each entity file follows a JSON Schema-like structure: - -```jsonc -{ - "name": "EntityName", // PascalCase entity name - "type": "object", // Always "object" - "properties": { - // Define your fields here - }, - "required": ["field1"] // Array of required field names -} -``` - -### Common Mistake: Nested Schema Property - -**WRONG** - Do NOT wrap properties in a `schema` object: -```jsonc -{ - "name": "Task", - "description": "A task entity", - "schema": { // ❌ WRONG - don't use nested "schema" - "type": "object", - "properties": { ... } - } -} -``` - -**CORRECT** - Put `type` and `properties` at the top level: -```jsonc -{ - "name": "Task", - "description": "A task entity", - "type": "object", // ✅ CORRECT - top level - "properties": { ... } // ✅ CORRECT - top level -} -``` - -This is a common mistake that will cause "Invalid schema: Schema must have a 'type' field" errors when pushing entities. - -## Supported Field Types - -### String - -Basic text field: -```jsonc -{ - "title": { - "type": "string", - "description": "Task title" - } -} -``` - -With format: -```jsonc -{ - "due_date": { - "type": "string", - "format": "date", - "description": "Due date" - } -} -``` - -Available formats: `date`, `date-time`, `time`, `email`, `uri`, `hostname`, `ipv4`, `ipv6`, `uuid`, `file`, `regex`, `richtext` - -### String with Enum - -Constrained to specific values: -```jsonc -{ - "status": { - "type": "string", - "enum": ["todo", "in_progress", "done"], - "default": "todo", - "description": "Current status" - } -} -``` - -### Number - -```jsonc -{ - "position": { - "type": "number", - "description": "Position for ordering" - } -} -``` - -### Integer - -For whole numbers only: -```jsonc -{ - "quantity": { - "type": "integer", - "description": "Item quantity", - "minimum": 0, - "maximum": 1000 - } -} -``` - -### Binary - -For file/blob data: -```jsonc -{ - "attachment": { - "type": "binary", - "description": "File attachment" - } -} -``` - -### Boolean - -```jsonc -{ - "notify_on_change": { - "type": "boolean", - "default": true, - "description": "Enable notifications" - } -} -``` - -### Array of Strings - -```jsonc -{ - "labels": { - "type": "array", - "items": { "type": "string" }, - "description": "Task labels/tags" - } -} -``` - -### Array of Objects - -```jsonc -{ - "attachments": { - "type": "array", - "description": "File attachments", - "items": { - "type": "object", - "properties": { - "name": { "type": "string" }, - "url": { "type": "string" }, - "type": { "type": "string" } - } - } - } -} -``` - -## Field Properties - -| Property | Description | -| ------------- | ---------------------------------------------------------------------------------------- | -| `type` | Data type: `string`, `number`, `integer`, `boolean`, `array`, `object`, `binary` | -| `description` | Human-readable description of the field | -| `enum` | Array of allowed values (for strings) | -| `enumNames` | Human-readable labels for enum values (same order as `enum`) | -| `default` | Default value when not provided | -| `format` | Format hint: `date`, `date-time`, `time`, `email`, `uri`, `hostname`, `ipv4`, `ipv6`, `uuid`, `file`, `regex`, `richtext` | -| `items` | Schema for array items | -| `properties` | Nested properties for object types | -| `$ref` | Reference to another schema definition | -| `minLength` | Minimum string length | -| `maxLength` | Maximum string length | -| `pattern` | Regex pattern for string validation | -| `minimum` | Minimum value for numbers | -| `maximum` | Maximum value for numbers | -| `rls` | Field-level security rules (see Field Level Security section) | - -## Complete Example - -Here's a complete entity definition for a Task: - -```jsonc -{ - "name": "Task", - "type": "object", - "properties": { - "title": { - "type": "string", - "description": "Task title" - }, - "description": { - "type": "string", - "description": "Task description" - }, - "status": { - "type": "string", - "enum": ["todo", "in_progress", "done"], - "default": "todo", - "description": "Current status of the task" - }, - "board_id": { - "type": "string", - "description": "Board this task belongs to" - }, - "assignee_email": { - "type": "string", - "description": "Email of assigned user" - }, - "priority": { - "type": "string", - "enum": ["low", "medium", "high"], - "default": "medium", - "description": "Task priority" - }, - "due_date": { - "type": "string", - "format": "date", - "description": "Due date" - }, - "labels": { - "type": "array", - "items": { "type": "string" }, - "description": "Task labels/tags" - } - }, - "required": ["title"] -} -``` - -## Naming Conventions - -- **Entity name**: Use PascalCase with alphanumeric characters only (e.g., `Task`, `TeamMember`, `ActivityLog`) - - Must match pattern: `/^[a-zA-Z0-9]+$/` - - Valid: `Task`, `TeamMember`, `Order123` - - Invalid: `Team_Member`, `Team-Member`, `Team Member` -- **File name**: Use kebab-case matching the entity (e.g., `task.jsonc`, `team-member.jsonc`, `activity-log.jsonc`) -- **Field names**: Use snake_case (e.g., `board_id`, `user_email`, `due_date`) - -## Relationships Between Entities - -To create relationships between entities, use ID reference fields: - -```jsonc -{ - "board_id": { - "type": "string", - "description": "Board this task belongs to" - }, - "team_id": { - "type": "string", - "description": "Associated team ID" - } -} -``` - -## Row Level Security (RLS) - -Row Level Security (RLS) controls which records users can access based on their identity and attributes. RLS rules are defined per entity inside the `rls` field of the schema. - -**Important:** If no RLS is defined, all records are accessible to all users. - -### RLS Operations - -RLS supports five operations: - -| Operation | Description | -|-----------|-------------| -| `create` | Control who can add new records | -| `read` | Control who can view records | -| `update` | Control who can modify records | -| `delete` | Control who can remove records | -| `write` | Shorthand for `create`, `update`, and `delete` combined | - -### Permission Values - -Each operation accepts one of the following values: - -1. **`true`** - Allow all users (including anonymous/unauthenticated) -2. **`false`** - Block all users -3. **Condition object** - Allow users matching the condition - -### Template Variables - -Use template variables to reference the current user's attributes: - -| Template | Description | -|----------|-------------| -| `{{user.id}}` | The user's ID | -| `{{user.email}}` | The user's email | -| `{{user.role}}` | The user's role | -| `{{user.data.field_name}}` | Custom field from the user's `data` object | - -### Built-in Entity Attributes - -Every entity record has these built-in attributes available for RLS rules: - -| Attribute | Description | -|-----------|-------------| -| `id` | Unique record identifier | -| `created_date` | Timestamp when record was created | -| `updated_date` | Timestamp when record was last updated | -| `created_by` | Email of the user who created the record | - -### Rule Types - -There are two condition types you can use: - -**1. Entity-to-user comparison** - Compare record fields to the current user's values: -```jsonc -{ - "created_by": "{{user.email}}" -} -``` - -**2. User condition check** - Check user properties directly using `user_condition`: -```jsonc -{ - "user_condition": { "role": "admin" } -} -``` - -**Important notes:** -- `user_condition` only supports **simple equality** (e.g., `{ "role": "admin" }`) -- **Entity field filtering requires `data.` prefix:** Use `{ "data.fieldname": value }` to filter by entity field values -- For `data.*` field comparisons, you can use operators: `$in`, `$nin`, `$ne`, `$all` -- Logical operators `$or`, `$and`, `$nor` are available for combining conditions - -⚠️ **For advanced RLS patterns and examples, see [rls-examples.md](rls-examples.md)** - -### RLS Examples - -**Owner-only access:** -```jsonc -{ - "created_by": "{{user.email}}" -} -``` - -**Department-based access:** -```jsonc -{ - "data.department": "{{user.data.department}}" -} -``` - -**Admin-only access:** -```jsonc -{ - "user_condition": { "role": "admin" } -} -``` - -**Complete RLS configuration:** -```jsonc -{ - "name": "Task", - "type": "object", - "properties": { - "title": { - "type": "string", - "description": "Task title" - }, - "status": { - "type": "string", - "enum": ["todo", "in_progress", "done"], - "default": "todo" - } - }, - "required": ["title"], - "rls": { - "create": true, - "read": { "created_by": "{{user.email}}" }, - "update": { "created_by": "{{user.email}}" }, - "delete": { "created_by": "{{user.email}}" } - } -} -``` - -### Common RLS Patterns - -**Public create, admin-only management (e.g., contact forms, waitlists):** -```jsonc -{ - "rls": { - "create": true, - "read": { "user_condition": { "role": "admin" } }, - "update": { "user_condition": { "role": "admin" } }, - "delete": { "user_condition": { "role": "admin" } } - } -} -``` - -**Owner-only access:** -```jsonc -{ - "rls": { - "create": true, - "read": { "created_by": "{{user.email}}" }, - "update": { "created_by": "{{user.email}}" }, - "delete": { "created_by": "{{user.email}}" } - } -} -``` - -**Logged-in users only:** -```jsonc -{ - "rls": { - "create": { "user_condition": { "id": "{{user.id}}" } }, - "read": true, - "update": { "created_by": "{{user.email}}" }, - "delete": { "created_by": "{{user.email}}" } - } -} -``` - -### Limitations - -- **user_condition is equality only:** `user_condition` only supports exact match (e.g., `{ "role": "admin" }`) - no operators -- **No comparison operators on user_condition:** `$gt`, `$lt`, `$regex`, `$expr`, `$where` are NOT supported for user conditions -- **No deeply nested templates:** Templates like `{{user.data.profile.department}}` may not work - -**Supported operators:** -- **Logical operators:** `$or`, `$and`, `$nor` for combining multiple conditions -- **Field operators (for `data.*` fields only):** `$in`, `$nin`, `$ne`, `$all` -- **Entity field filtering:** Use `data.` prefix to filter by entity field values (e.g., `{ "data.status": "published" }` or `{ "data.completed": true }`) - -⚠️ **See [rls-examples.md](rls-examples.md) for comprehensive RLS patterns and examples** - -### Complex Access Patterns - -For complex access patterns that require multiple conditions (e.g., "owner OR admin"), you have two options: - -1. **Use the Base44 Dashboard UI** - The dashboard allows adding multiple rules per operation with OR logic -2. **Use separate entities** - Split data into multiple entities with different access rules -3. **Use backend functions** - Implement custom access logic in backend functions - -## Field Level Security (FLS) - -Field Level Security allows you to control access to individual fields within an entity. FLS rules are defined within each field's schema using the `rls` property. - -### FLS Operations - -FLS supports the same operations as entity-level RLS: - -| Operation | Description | -|-----------|-------------| -| `create` | Control who can set this field when creating records | -| `read` | Control who can view this field | -| `update` | Control who can modify this field | -| `delete` | Control who can clear this field | -| `write` | Shorthand for `create`, `update`, and `delete` combined | - -### FLS Example - -```jsonc -{ - "name": "Employee", - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Employee name" - }, - "salary": { - "type": "number", - "description": "Employee salary", - "rls": { - "read": { "user_condition": { "role": "hr" } }, - "update": { "user_condition": { "role": "hr" } } - } - }, - "department": { - "type": "string", - "description": "Department name" - } - }, - "required": ["name"] -} -``` - -In this example, only users with the `hr` role can read or update the `salary` field. All users with access to the entity can read/update other fields. - -### FLS Notes - -- If no field-level RLS is defined, the field inherits the entity-level RLS rules -- FLS rules follow the same condition format as entity-level RLS -- Use FLS for sensitive fields like salary, SSN, or internal notes - -## Pushing Entities - -The `entities push` command will push all entities that exist in the `base44/entities` folder. - -```bash -npx base44 entities push -``` - -For more details on the push command, see [entities-push.md](entities-push.md). diff --git a/plugins/base44/skills/base44-cli/references/entities-push.md b/plugins/base44/skills/base44-cli/references/entities-push.md deleted file mode 100644 index 63b92d7b1..000000000 --- a/plugins/base44/skills/base44-cli/references/entities-push.md +++ /dev/null @@ -1,71 +0,0 @@ -# base44 entities push - -Push local entity definitions to Base44. - -## Syntax - -```bash -npx base44 entities push -``` - -## Authentication - -**Required**: Yes. If not authenticated, you'll be prompted to login first. - -## What It Does - -1. Pushes all entities that exist in the `base44/entities` folder -2. Validates that entities exist in the folder -3. Displays the count of entities to be pushed -4. Uploads entities to the Base44 backend -5. Reports the results: created, updated, and deleted entities - -## Prerequisites - -- Must be run from a Base44 project directory -- Project must have entity definitions in the `base44/entities` folder - -## Output - -```bash -$ npx base44 entities push - -Found 3 entities to push -Pushing entities to Base44... - -Created: User, Post -Updated: Comment -Deleted: OldEntity - -✓ Entities pushed successfully -``` - -## Entity Synchronization - -The push operation synchronizes your local entity schema with Base44: - -- **Created**: New entities that didn't exist in Base44 -- **Updated**: Existing entities with modified schema or configuration -- **Deleted**: Entities that were removed from your local configuration - -## Error Handling - -If no entities are found in your project: -```bash -$ npx base44 entities push -No entities found in project -``` - -## Use Cases - -- After defining new entities in your project -- When modifying existing entity schemas -- To sync entity changes before deploying -- As part of your development workflow when data models change - -## Notes - -- This command syncs the entity schema/structure, not the actual data -- Changes are applied to your Base44 project immediately -- Make sure to test entity changes in a development environment first -- Entity definitions are located in the `base44/entities/` directory diff --git a/plugins/base44/skills/base44-cli/references/exec.md b/plugins/base44/skills/base44-cli/references/exec.md deleted file mode 100644 index 68b477f0a..000000000 --- a/plugins/base44/skills/base44-cli/references/exec.md +++ /dev/null @@ -1,47 +0,0 @@ -# base44 exec - -Run a script with the Base44 SDK pre-authenticated as the current user. Reads the script from stdin. - -## Syntax - -```bash -cat ./script.ts | npx base44 exec -echo "" | npx base44 exec -``` - -## How It Works - -The `exec` command reads a script from stdin and runs it server-side with the Base44 SDK pre-authenticated as the currently logged-in user. This allows you to run one-off scripts against your app's data without writing a full function. - -## Available Globals - -> **`base44`** — a preinitialized SDK client, available as a global variable in every exec script. You do not need to import or configure it — it is ready to use immediately. - -Use it to interact with your app's resources: - -- `base44.entities.` — CRUD operations on entities (`.list()`, `.get(id)`, `.create(data)`, `.update(id, data)`, `.delete(id)`) -- `base44.functions.invoke(name, data?)` — call a backend function -- `base44.agents.` — invoke AI agents -- For more available resources and methods, see the [Base44 SDK reference](../../base44-sdk/SKILL.md) - -## Examples - -```bash -# Run a script file -cat ./script.ts | npx base44 exec - -# Inline script -echo "const users = await base44.entities.User.list(); console.log(users)" | npx base44 exec -``` - -## Requirements - -- Must be authenticated (`npx base44 login`) -- Must be run from a linked Base44 project directory -- Script must be piped via stdin (non-interactive mode) - -## Notes - -- The script runs with the Base44 SDK pre-authenticated — you can use `base44.entities`, `base44.functions`, etc. directly -- Exit code from the script is forwarded as the CLI process exit code -- This command requires stdin to be piped (it does not accept input in interactive TTY mode) diff --git a/plugins/base44/skills/base44-cli/references/functions-create.md b/plugins/base44/skills/base44-cli/references/functions-create.md deleted file mode 100644 index 8efeb4555..000000000 --- a/plugins/base44/skills/base44-cli/references/functions-create.md +++ /dev/null @@ -1,244 +0,0 @@ -# Creating Functions - -Base44 functions are serverless backend functions that run on Deno. They are defined locally in your project and deployed to the Base44 backend. - -## Function Directory - -All function definitions must be placed in the `base44/functions/` folder in your project. Each function lives in its own subdirectory with a configuration file and entry point. - -Example structure: -``` -my-app/ - base44/ - functions/ - process-order/ - function.jsonc - index.ts - send-notification/ - function.jsonc - index.ts -``` - -## How to Create a Function - -1. Create a new directory in `base44/functions/` with your function name (use kebab-case) -2. Create a `function.jsonc` configuration file in the directory -3. Create the entry point file (e.g., `index.ts`) -4. Deploy the function using the CLI - -## Function Configuration - -Each function requires a `function.jsonc` configuration file: - -```jsonc -{ - "name": "my-function", - "entry": "index.ts", - // Optionally add automations - "automations": [ - { - "name": "Daily run", - "type": "scheduled", - "schedule_mode": "recurring", - "schedule_type": "cron", - "cron_expression": "0 9 * * *" - } - ] -} -``` - -### Configuration Properties - -| Property | Description | Required | -|----------|-------------|----------| -| `name` | Function name (must match `/^[^.]+$/` - no dots allowed) | Yes | -| `entry` | Entry point file path relative to the function directory (min 1 char) | Yes | -| `automations` | Array of triggers (CRON, simple schedule, one-time, entity hooks); deployed with the function | No | - -## Automations - -Functions can define automations (triggers) so they run on a schedule or when entity data changes. Add an optional `automations` array to `function.jsonc`. Supported types: **scheduled** (one-time, CRON, or simple interval) and **entity hooks** (on entity create/update/delete). Automations are deployed with the function via `npx base44 functions deploy`. For full schemas and examples, see [automations.md](automations.md). - -## Entry Point File - -Functions run on Deno and must export using `Deno.serve()`. Use `npm:` prefix for npm packages. - -```typescript -import { createClientFromRequest } from "npm:@base44/sdk"; - -Deno.serve(async (req) => { - // Get authenticated client from request - const base44 = createClientFromRequest(req); - - // Parse input - const { orderId, action } = await req.json(); - - // Your logic here - const order = await base44.entities.Orders.get(orderId); - - // Return response - return Response.json({ - success: true, - order: order - }); -}); -``` - -### Request Object - -The function receives a standard Deno `Request` object: -- `req.json()` - Parse JSON body -- `req.text()` - Get raw text body -- `req.headers` - Access request headers -- `req.method` - HTTP method - -### Response Object - -Return using `Response.json()` for JSON responses: - -```typescript -// Success response -return Response.json({ data: result }); - -// Error response with status code -return Response.json({ error: "Something went wrong" }, { status: 400 }); - -// Not found -return Response.json({ error: "Order not found" }, { status: 404 }); -``` - -## Complete Example - -### Directory Structure -``` -base44/ - functions/ - process-order/ - function.jsonc - index.ts -``` - -### function.jsonc -```jsonc -{ - "name": "process-order", - "entry": "index.ts" -} -``` - -### index.ts -```typescript -import { createClientFromRequest } from "npm:@base44/sdk"; - -Deno.serve(async (req) => { - try { - const base44 = createClientFromRequest(req); - const { orderId } = await req.json(); - - // Validate input - if (!orderId) { - return Response.json( - { error: "Order ID is required" }, - { status: 400 } - ); - } - - // Fetch and process the order - const order = await base44.entities.Orders.get(orderId); - if (!order) { - return Response.json( - { error: "Order not found" }, - { status: 404 } - ); - } - - return Response.json({ - success: true, - orderId: order.id, - processedAt: new Date().toISOString() - }); - - } catch (error) { - return Response.json( - { error: error.message }, - { status: 500 } - ); - } -}); -``` - -## Using Service Role Access - -For admin-level operations, use `asServiceRole`: - -```typescript -import { createClientFromRequest } from "npm:@base44/sdk"; - -Deno.serve(async (req) => { - const base44 = createClientFromRequest(req); - - // Check user is authenticated - const user = await base44.auth.me(); - if (!user) { - return Response.json({ error: "Unauthorized" }, { status: 401 }); - } - - // Use service role for admin operations - const allOrders = await base44.asServiceRole.entities.Orders.list(); - - return Response.json({ orders: allOrders }); -}); -``` - -## Using Secrets - -Access environment variables configured in the app dashboard: - -```typescript -Deno.serve(async (req) => { - // Access environment variables (configured in app settings) - const apiKey = Deno.env.get("STRIPE_API_KEY"); - - const response = await fetch("https://api.stripe.com/v1/charges", { - headers: { - "Authorization": `Bearer ${apiKey}` - } - }); - - return Response.json(await response.json()); -}); -``` - -## Naming Conventions - -- **Directory name**: Use kebab-case (e.g., `process-order`, `send-notification`) -- **Function name**: Match the directory name, must match pattern `/^[^.]+$/` (no dots allowed) - - Valid: `process-order`, `send_notification`, `myFunction` - - Invalid: `process.order`, `send.notification.v2` -- **Entry file**: Typically `index.ts` or `index.js` - -## Deploying Functions - -After creating your function, deploy it to Base44: - -```bash -npx base44 functions deploy -``` - -For more details on deploying, see [functions-deploy.md](functions-deploy.md). - -## Notes - -- Functions run on Deno runtime, not Node.js -- Use `npm:` prefix for npm packages (e.g., `npm:@base44/sdk`) -- Use `createClientFromRequest(req)` to get a client that inherits the caller's auth context -- Configure secrets via app dashboard for API keys -- Make sure to handle errors gracefully and return appropriate HTTP status codes - -## Common Mistakes - -| Wrong | Correct | Why | -|-------|---------|-----| -| `functions/myFunction.js` (single file) | `functions/my-function/index.ts` + `function.jsonc` | Functions require subdirectory with config | -| `import { ... } from "@base44/sdk"` | `import { ... } from "npm:@base44/sdk"` | Deno requires `npm:` prefix for npm packages | -| `MyFunction` or `myFunction` directory | `my-function` directory | Use kebab-case for directory names | diff --git a/plugins/base44/skills/base44-cli/references/functions-delete.md b/plugins/base44/skills/base44-cli/references/functions-delete.md deleted file mode 100644 index f426a055f..000000000 --- a/plugins/base44/skills/base44-cli/references/functions-delete.md +++ /dev/null @@ -1,81 +0,0 @@ -# base44 functions delete - -Delete one or more deployed functions from Base44. - -## Syntax - -```bash -npx base44 functions delete -``` - -## Arguments - -| Argument | Description | Required | -|----------|-------------|----------| -| `` | One or more function names to delete (comma-separated values also accepted) | Yes | - -## Authentication - -**Required**: Yes. If not authenticated, you'll be prompted to login first. - -## What It Does - -1. Takes one or more function names as arguments -2. Deletes each function from Base44 remotely -3. Reports success, not-found, or error for each function - -## Examples - -```bash -# Delete a single function -npx base44 functions delete process-order - -# Delete multiple functions (space-separated) -npx base44 functions delete process-order send-notification - -# Delete multiple functions (comma-separated) -npx base44 functions delete process-order,send-notification -``` - -## Output - -Single function: -```bash -$ npx base44 functions delete process-order -◇ Deleting process-order... -✓ process-order deleted - -└ Function "process-order" deleted -``` - -Multiple functions: -```bash -$ npx base44 functions delete process-order send-notification -◇ Deleting process-order... -✓ process-order deleted -◇ Deleting send-notification... -✓ send-notification deleted - -└ 2/2 deleted -``` - -## Error Handling - -If a function is not found on remote: -```bash -$ npx base44 functions delete nonexistent -✓ Function "nonexistent" not found -``` - -If no names are provided: -```bash -$ npx base44 functions delete -error: At least one function name is required -``` - -## Notes - -- This command deletes functions from Base44 (remote only); it does not remove local files -- To remove a function and clean up remote state, delete the local files then use `npx base44 functions deploy --force` -- Not-found functions are reported without raising an error (exit 0 for single-function case) -- Comma-separated names are supported: `delete func1,func2` is equivalent to `delete func1 func2` diff --git a/plugins/base44/skills/base44-cli/references/functions-deploy.md b/plugins/base44/skills/base44-cli/references/functions-deploy.md deleted file mode 100644 index 7481dee32..000000000 --- a/plugins/base44/skills/base44-cli/references/functions-deploy.md +++ /dev/null @@ -1,116 +0,0 @@ -# base44 functions deploy - -Deploy local function definitions to Base44. - -## Syntax - -```bash -npx base44 functions deploy [names...] [options] -``` - -## Options - -| Option | Description | Required | -|--------|-------------|----------| -| `[names...]` | One or more function names to deploy (deploys all if omitted) | No | -| `--force` | Delete remote functions not found locally (cannot be combined with `[names...]`) | No | - -## Authentication - -**Required**: Yes. If not authenticated, you'll be prompted to login first. - -## What It Does - -1. Scans the `base44/functions/` directory for function definitions -2. Validates that functions exist and have valid configurations -3. Displays the count of functions to be deployed -4. Uploads function code and configuration to Base44 sequentially -5. Reports the results: deployed, unchanged, and failed counts -6. If `--force` is used: also deletes remote functions that no longer exist locally - -## Prerequisites - -- Must be run from a Base44 project directory -- Project must have function definitions in the `base44/functions/` folder -- Each function subdirectory should contain a `function.jsonc` config and an entry point file, or just an `entry.ts` for zero-config functions - -## Examples - -```bash -# Deploy all functions -npx base44 functions deploy - -# Deploy specific functions -npx base44 functions deploy process-order send-notification - -# Deploy all and delete functions removed locally -npx base44 functions deploy --force -``` - -## Output - -```bash -$ npx base44 functions deploy - -◆ Found 2 functions to deploy -◇ [1/2] Deploying process-order... -✓ process-order deployed -◇ [2/2] Deploying send-notification... -✓ send-notification deployed - -└ 2 deployed -``` - -With `--force`: -```bash -$ npx base44 functions deploy --force - -◆ Found 2 functions to deploy -... - -◆ Found 1 remote function to delete -◇ [1/1] Deleting old-function... -✓ old-function deleted - -◆ 1 deleted - -└ 2 deployed -``` - -## Error Handling - -If no functions are found in your project: -```bash -$ npx base44 functions deploy -No functions found. Create functions in the 'functions' directory. -``` - -If `--force` is combined with function names: -```bash -$ npx base44 functions deploy my-func --force -error: --force cannot be used when specifying function names -``` - -If a specified function name doesn't exist locally: -```bash -$ npx base44 functions deploy nonexistent -error: Function not found in project: nonexistent -``` - -## Use Cases - -- After creating new functions in your project -- When modifying existing function code or configuration -- To sync function changes before testing -- As part of your development workflow when backend logic changes -- Use `--force` to clean up remote functions that have been removed locally - -## Notes - -- This command deploys the function code and configuration -- Changes are applied to your Base44 project immediately -- Deploy results per function: `deployed`, `unchanged`, or `error` -- `--force` cannot be combined with specific function names -- Make sure to test functions in a development environment first -- Function definitions are located in the `base44/functions/` directory -- For how to create functions, see [functions-create.md](functions-create.md) diff --git a/plugins/base44/skills/base44-cli/references/functions-list.md b/plugins/base44/skills/base44-cli/references/functions-list.md deleted file mode 100644 index 67d7f6d30..000000000 --- a/plugins/base44/skills/base44-cli/references/functions-list.md +++ /dev/null @@ -1,44 +0,0 @@ -# base44 functions list - -List all deployed functions on Base44 remote. - -## Syntax - -```bash -npx base44 functions list -``` - -## Authentication - -**Required**: Yes. If not authenticated, you'll be prompted to login first. - -## What It Does - -1. Fetches all deployed functions from Base44 -2. Displays each function name and its automation count (if any) -3. Reports the total count of functions on remote - -## Output - -```bash -$ npx base44 functions list - process-order - send-notification (2 automations) - daily-report (1 automation) - -✓ 3 functions on remote -``` - -If no functions are deployed: -```bash -$ npx base44 functions list -✓ No functions on remote -``` - -## Notes - -- Lists functions currently deployed on Base44, not local function files -- Shows automation count next to each function that has automations configured -- To see local function definitions, look in the `base44/functions/` directory -- Use `npx base44 functions deploy` to sync local functions to remote -- Use `npx base44 functions pull` to download remote functions to local files diff --git a/plugins/base44/skills/base44-cli/references/functions-pull.md b/plugins/base44/skills/base44-cli/references/functions-pull.md deleted file mode 100644 index 88ad7379b..000000000 --- a/plugins/base44/skills/base44-cli/references/functions-pull.md +++ /dev/null @@ -1,80 +0,0 @@ -# base44 functions pull - -Pull deployed functions from Base44 to local files. - -## Syntax - -```bash -npx base44 functions pull [name] -``` - -## Arguments - -| Argument | Description | Required | -|----------|-------------|----------| -| `[name]` | Function name to pull (pulls all if omitted) | No | - -## Authentication - -**Required**: Yes. If not authenticated, you'll be prompted to login first. - -## What It Does - -1. Fetches deployed functions from Base44 -2. Filters to the specified function if `[name]` is provided -3. Writes function files to the local `functions/` directory (configured in `base44/config.jsonc`) -4. Reports each file as `written` (new/updated) or `unchanged` - -## Examples - -```bash -# Pull all deployed functions -npx base44 functions pull - -# Pull a specific function -npx base44 functions pull process-order -``` - -## Output - -```bash -$ npx base44 functions pull -✓ Functions fetched successfully -✓ Function files written successfully -✓ process-order written -◆ send-notification unchanged - -✓ Pulled 2 functions to base44/functions -``` - -Single function: -```bash -$ npx base44 functions pull process-order -✓ Functions fetched successfully -✓ Function files written successfully -✓ process-order written - -✓ Pulled 1 function to base44/functions -``` - -## Error Handling - -If the specified function is not found on remote: -```bash -$ npx base44 functions pull nonexistent -✓ Function "nonexistent" not found on remote -``` - -If no functions exist on remote: -```bash -$ npx base44 functions pull -✓ No functions found on remote -``` - -## Notes - -- Files are written to the `functionsDir` configured in `base44/config.jsonc` (defaults to `functions/`) -- Files already matching remote content are skipped (reported as `unchanged`) -- This overwrites existing local function files with remote versions — commit local changes first -- Use `npx base44 functions deploy` to push local changes back to Base44 -- Use `npx base44 functions list` to see what functions are deployed on remote diff --git a/plugins/base44/skills/base44-cli/references/link.md b/plugins/base44/skills/base44-cli/references/link.md deleted file mode 100644 index 0b009ce37..000000000 --- a/plugins/base44/skills/base44-cli/references/link.md +++ /dev/null @@ -1,81 +0,0 @@ -# base44 link - -Links an existing local Base44 project to a Base44 app in the cloud. Use this when you have a `base44/config.jsonc` but haven't connected it to a Base44 app yet. - -## Critical: When to Use Link vs Create - -| Scenario | Command | -|----------|---------| -| Starting fresh, no `base44/` folder | `npx base44 create` | -| Have `base44/config.jsonc` but no `.app.jsonc` | `npx base44 link` | -| Project already linked (has `.app.jsonc`) | Already done, use `deploy` | - -## Syntax - -```bash -npx base44 link [options] -``` - -## Options - -| Option | Description | Required | -|--------|-------------|----------| -| `-c, --create` | Create a new project (skip selection prompt) | No | -| `-n, --name ` | Project name (required when `--create` is used) | With `--create` | -| `-d, --description ` | Project description | No | -| `-p, --projectId ` | Project ID to link to an existing project (skip selection prompt) | No | - -## Non-Interactive Mode - -For CI/CD or agent use: - -**Create a new project:** -```bash -npx base44 link --create --name my-app -``` - -**Link to an existing project:** -```bash -npx base44 link --projectId -``` - -WRONG: `npx base44 link --create` (missing --name) -WRONG: `npx base44 link --create --projectId ` (cannot use both) -RIGHT: `npx base44 link --create --name my-app` -RIGHT: `npx base44 link --projectId ` - -## Examples - -```bash -# Interactive mode - prompts for project details -npx base44 link - -# Non-interactive - create and link in one step -npx base44 link --create --name my-app - -# With description -npx base44 link --create --name my-app --description "My awesome app" - -# Link to a specific existing project by ID -npx base44 link --projectId abc123 -``` - -## What It Does - -1. Finds the `base44/config.jsonc` in the current directory (or parent directories) -2. Verifies no `.app.jsonc` exists (project not already linked) -3. Either: - - Creates a new Base44 app in the cloud (with `--create`), OR - - Links to an existing project (with `--projectId` or interactive selection) -4. Writes the app ID to `base44/.app.jsonc` - -## Requirements - -- Must have `base44/config.jsonc` in the project -- Must NOT have `base44/.app.jsonc` (use `deploy` if already linked) -- Must be authenticated (run `npx base44 login` first) - -## Notes - -- After linking, you can deploy resources with `npx base44 deploy` -- The `.app.jsonc` file should be git-ignored (contains your app ID) diff --git a/plugins/base44/skills/base44-cli/references/rls-examples.md b/plugins/base44/skills/base44-cli/references/rls-examples.md deleted file mode 100644 index 7d3ef42ce..000000000 --- a/plugins/base44/skills/base44-cli/references/rls-examples.md +++ /dev/null @@ -1,463 +0,0 @@ -# RLS Examples - -Practical Row-Level Security patterns for common application types. - -**Important:** Base44 RLS supports: -- **Logical operators:** `$or`, `$and`, `$nor` for combining conditions -- **Field operators (for `data.*` fields):** `$in`, `$nin`, `$ne`, `$all` -- **user_condition:** Equality only (no operators) - -## Contents -- [Simple Patterns (JSON Schema)](#simple-patterns-json-schema) -- [Using Operators](#using-operators) -- [Field-Level Security Examples](#field-level-security-examples) -- [Complex Patterns (Dashboard UI or Backend)](#complex-patterns-dashboard-ui-or-backend) -- [Best Practices](#best-practices) - ---- - -## Simple Patterns (JSON Schema) - -These patterns work with the JSON schema RLS format. - -### Todo App - Owner-only access - -Users see and manage only their own tasks. - -```jsonc -{ - "name": "Task", - "type": "object", - "properties": { - "title": { "type": "string" }, - "description": { "type": "string" }, - "completed": { "type": "boolean" }, - "priority": { "type": "string", "enum": ["low", "medium", "high"] }, - "due_date": { "type": "string", "format": "date" } - }, - "rls": { - "create": true, - "read": { "created_by": "{{user.email}}" }, - "update": { "created_by": "{{user.email}}" }, - "delete": { "created_by": "{{user.email}}" } - } -} -``` - -### Contact Form - Public create, admin-only read - -Anyone can submit, only admins can view submissions. - -```jsonc -{ - "name": "ContactSubmission", - "type": "object", - "properties": { - "name": { "type": "string" }, - "email": { "type": "string", "format": "email" }, - "message": { "type": "string" } - }, - "rls": { - "create": true, - "read": { "user_condition": { "role": "admin" } }, - "update": { "user_condition": { "role": "admin" } }, - "delete": { "user_condition": { "role": "admin" } } - } -} -``` - -### User Profile - Self-management - -Users can only access their own profile. - -```jsonc -{ - "name": "UserProfile", - "type": "object", - "properties": { - "name": { "type": "string" }, - "avatar_url": { "type": "string" }, - "bio": { "type": "string" }, - "preferences": { "type": "object" } - }, - "rls": { - "create": true, - "read": { "created_by": "{{user.email}}" }, - "update": { "created_by": "{{user.email}}" }, - "delete": { "created_by": "{{user.email}}" } - } -} -``` - -### Department Data - Same department access - -Users can only see records from their department. - -```jsonc -{ - "name": "DepartmentAnnouncement", - "type": "object", - "properties": { - "title": { "type": "string" }, - "content": { "type": "string" }, - "department": { "type": "string" } - }, - "rls": { - "create": { "user_condition": { "role": "manager" } }, - "read": { "data.department": "{{user.data.department}}" }, - "update": { "user_condition": { "role": "manager" } }, - "delete": { "user_condition": { "role": "admin" } } - } -} -``` - -### Subscription - Admin-managed, user-readable via email field - -```jsonc -{ - "name": "Subscription", - "type": "object", - "properties": { - "user_email": { "type": "string" }, - "tier": { "type": "string", "enum": ["free", "basic", "pro", "enterprise"] }, - "credits": { "type": "number" }, - "renewal_date": { "type": "string", "format": "date" } - }, - "rls": { - "create": { "user_condition": { "role": "admin" } }, - "read": { "data.user_email": "{{user.email}}" }, - "update": { "user_condition": { "role": "admin" } }, - "delete": { "user_condition": { "role": "admin" } } - } -} -``` - -**Note:** This pattern only allows users to read their own subscription. Admins need to use the Dashboard UI to configure additional read access for themselves. - -### Private Data - Owner-only - -```jsonc -{ - "name": "PrivateNotes", - "type": "object", - "properties": { - "title": { "type": "string" }, - "content": { "type": "string" }, - "tags": { "type": "array", "items": { "type": "string" } } - }, - "rls": { - "create": true, - "read": { "created_by": "{{user.email}}" }, - "update": { "created_by": "{{user.email}}" }, - "delete": { "created_by": "{{user.email}}" } - } -} -``` - -### Public Read, Authenticated Write - -Anyone can read, only logged-in users can create/edit their own records. - -```jsonc -{ - "name": "BlogPost", - "type": "object", - "properties": { - "title": { "type": "string" }, - "content": { "type": "string" }, - "author_email": { "type": "string" } - }, - "rls": { - "create": true, - "read": true, - "update": { "created_by": "{{user.email}}" }, - "delete": { "created_by": "{{user.email}}" } - } -} -``` - ---- - -## Using Operators - -### Logical Operators - -Combine multiple conditions using `$or`, `$and`, or `$nor`: - -**Owner OR Admin access:** -```jsonc -{ - "name": "Document", - "type": "object", - "properties": { - "title": { "type": "string" }, - "content": { "type": "string" } - }, - "rls": { - "create": true, - "read": { - "$or": [ - { "created_by": "{{user.email}}" }, - { "user_condition": { "role": "admin" } } - ] - }, - "update": { - "$or": [ - { "created_by": "{{user.email}}" }, - { "user_condition": { "role": "admin" } } - ] - }, - "delete": { "user_condition": { "role": "admin" } } - } -} -``` - -**Multiple roles with $or:** -```jsonc -{ - "rls": { - "read": { - "$or": [ - { "user_condition": { "role": "admin" } }, - { "user_condition": { "role": "manager" } }, - { "user_condition": { "role": "hr" } } - ] - } - } -} -``` - -### Field Operators for data.* Fields - -Use `$in`, `$nin`, `$ne`, `$all` for comparing entity data fields: - -**Access based on tags ($in):** -```jsonc -{ - "rls": { - "read": { - "data.category": { "$in": ["public", "shared"] } - } - } -} -``` - -**Exclude specific statuses ($nin):** -```jsonc -{ - "rls": { - "read": { - "data.status": { "$nin": ["archived", "deleted"] } - } - } -} -``` - -**Not equal ($ne):** -```jsonc -{ - "rls": { - "read": { - "data.visibility": { "$ne": "private" } - } - } -} -``` - -**All tags must match ($all):** -```jsonc -{ - "rls": { - "read": { - "data.required_tags": { "$all": ["approved", "reviewed"] } - } - } -} -``` - -### Combining Logical and Field Operators - -```jsonc -{ - "rls": { - "read": { - "$and": [ - { "data.status": { "$ne": "draft" } }, - { - "$or": [ - { "created_by": "{{user.email}}" }, - { "data.visibility": "public" } - ] - } - ] - } - } -} -``` - ---- - -## Field-Level Security Examples - -Control access to specific fields within an entity. - -### Sensitive Salary Field - -```jsonc -{ - "name": "Employee", - "type": "object", - "properties": { - "name": { "type": "string" }, - "email": { "type": "string", "format": "email" }, - "salary": { - "type": "number", - "description": "Annual salary", - "rls": { - "read": { "user_condition": { "role": "hr" } }, - "write": { "user_condition": { "role": "hr" } } - } - }, - "performance_notes": { - "type": "string", - "description": "Manager notes", - "rls": { - "read": { - "$or": [ - { "user_condition": { "role": "manager" } }, - { "user_condition": { "role": "hr" } } - ] - }, - "write": { "user_condition": { "role": "manager" } } - } - } - } -} -``` - -### Admin-Only Internal Fields - -```jsonc -{ - "name": "Order", - "type": "object", - "properties": { - "order_number": { "type": "string" }, - "total": { "type": "number" }, - "internal_notes": { - "type": "string", - "description": "Internal processing notes", - "rls": { - "read": { "user_condition": { "role": "admin" } }, - "write": { "user_condition": { "role": "admin" } } - } - }, - "profit_margin": { - "type": "number", - "description": "Profit margin percentage", - "rls": { - "read": { "user_condition": { "role": "admin" } }, - "write": false - } - } - } -} -``` - ---- - -## Complex Patterns (Dashboard UI or Backend) - -Some patterns may still require the Dashboard UI or backend functions. - -### Bidirectional Relationships (e.g., Friendships, Matches) - -**Requirement:** Either party in a relationship should have access. - -**Now possible with $or:** -```jsonc -{ - "rls": { - "read": { - "$or": [ - { "data.user_a_email": "{{user.email}}" }, - { "data.user_b_email": "{{user.email}}" } - ] - } - } -} -``` - -**Alternative solutions:** -1. **Entity redesign:** Store two records per relationship (one for each party) -2. **Backend function:** Query with custom logic - -### Complex Business Logic - -**Requirement:** Access depends on multiple entity fields with complex conditions. - -**JSON Schema limitation:** While operators help, very complex business logic may still be hard to express. - -**Solution options:** -1. **Backend function:** Implement custom access logic -2. **Combine simpler rules:** Break complex rules into simpler entity-level and field-level rules - ---- - -## Best Practices - -### Security Strategy - -Use a combination of entity-level RLS and field-level security: - -| Data Type | Approach | Example | -|-----------|----------|---------| -| User-editable | Entity RLS: Owner-only | UserProfile with `created_by` check | -| Sensitive fields | Field-level RLS | Salary field with HR role check | -| Multi-role access | `$or` with user_condition | Admin OR Manager access | -| Conditional access | Field operators | `$in`, `$ne` on data fields | -| Public content | Entity RLS: `read: true` | PublicPost | -| Private content | Entity RLS: Owner-only | PrivateNote | - -### When to Use Each Approach - -| Requirement | Approach | -|-------------|----------| -| Single condition (owner, admin, department) | JSON Schema RLS | -| Multiple OR/AND conditions | JSON Schema RLS with `$or`/`$and` | -| Field value checks with `$in`/`$ne`/etc. | JSON Schema RLS for `data.*` fields | -| Field-level access control | JSON Schema FLS (field-level `rls`) | -| Complex comparison operators (`$gt`, `$lt`) | Backend functions | -| Very complex business logic | Backend functions | - -### Common Role Patterns - -| Role | Typical Access | -|------|----------------| -| `admin` | Full access to all records | -| `moderator` | Read/update access, limited delete | -| `manager` | Department-scoped access | -| `user` | Own records only | - -### Supported Operators Summary - -| Operator | Supported | Notes | -|----------|-----------|-------| -| `$or` | Yes | Combine multiple conditions | -| `$and` | Yes | All conditions must match | -| `$nor` | Yes | None of the conditions match | -| `$in` | Yes | For `data.*` fields only | -| `$nin` | Yes | For `data.*` fields only | -| `$ne` | Yes | For `data.*` fields only | -| `$all` | Yes | For `data.*` fields only | -| `$gt`, `$lt`, `$gte`, `$lte` | No | Use backend functions | -| `$regex` | No | Use backend functions | - -### Limitations Summary - -| Not Supported | Alternative | -|---------------|-------------| -| Operators on `user_condition` | Use equality only for user checks | -| Comparison operators (`$gt`, `$lt`) | Backend functions | -| Regex matching (`$regex`) | Backend functions | -| Cross-entity relationships | Backend functions | diff --git a/plugins/base44/skills/base44-cli/references/secrets-delete.md b/plugins/base44/skills/base44-cli/references/secrets-delete.md deleted file mode 100644 index c0187ab89..000000000 --- a/plugins/base44/skills/base44-cli/references/secrets-delete.md +++ /dev/null @@ -1,30 +0,0 @@ -# base44 secrets delete - -Delete a secret from the current project. - -## Syntax - -```bash -npx base44 secrets delete -``` - -## Arguments - -| Argument | Description | Required | -|----------|-------------|----------| -| `` | Name of the secret to delete | Yes | - -## Options - -No options. - -## Examples - -```bash -npx base44 secrets delete API_KEY -``` - -## Notes - -- The secret is permanently removed from Base44 -- Requires authentication diff --git a/plugins/base44/skills/base44-cli/references/secrets-list.md b/plugins/base44/skills/base44-cli/references/secrets-list.md deleted file mode 100644 index f17fc1bd8..000000000 --- a/plugins/base44/skills/base44-cli/references/secrets-list.md +++ /dev/null @@ -1,25 +0,0 @@ -# base44 secrets list - -List the names of all secrets configured for the current project. - -## Syntax - -```bash -npx base44 secrets list -``` - -## Options - -No options. The command takes no arguments or flags. - -## Examples - -```bash -npx base44 secrets list -``` - -## Notes - -- Only secret **names** are listed (values are never displayed) -- Returns "No secrets configured." if there are none -- Requires authentication diff --git a/plugins/base44/skills/base44-cli/references/secrets-set.md b/plugins/base44/skills/base44-cli/references/secrets-set.md deleted file mode 100644 index e39975e36..000000000 --- a/plugins/base44/skills/base44-cli/references/secrets-set.md +++ /dev/null @@ -1,41 +0,0 @@ -# base44 secrets set - -Set one or more project secrets (environment variables stored in Base44). - -## Syntax - -```bash -npx base44 secrets set [entries...] [options] -``` - -## Arguments - -| Argument | Description | Required | -|----------|-------------|----------| -| `entries...` | One or more `KEY=VALUE` pairs (e.g. `KEY1=val1 KEY2=val2`) | Yes (unless `--env-file` is used) | - -## Options - -| Option | Description | Required | -|--------|-------------|----------| -| `--env-file ` | Path to a `.env` file to bulk-import secrets from | No | - -## Examples - -```bash -# Set one secret -npx base44 secrets set API_KEY=my-secret-value - -# Set multiple secrets at once -npx base44 secrets set API_KEY=abc123 DB_PASSWORD=secret - -# Import from a .env file -npx base44 secrets set --env-file .env.production -``` - -## Notes - -- Provide `KEY=VALUE` pairs **or** `--env-file`, not both -- Keys must be non-empty; values may be empty strings -- Overwrites existing secrets with the same name -- Requires authentication diff --git a/plugins/base44/skills/base44-cli/references/site-deploy.md b/plugins/base44/skills/base44-cli/references/site-deploy.md deleted file mode 100644 index 929d30236..000000000 --- a/plugins/base44/skills/base44-cli/references/site-deploy.md +++ /dev/null @@ -1,118 +0,0 @@ -# base44 site deploy - -Deploy built site files to Base44 hosting. - -## Table of Contents - -- [Syntax](#syntax) -- [Authentication](#authentication) -- [Prerequisites](#prerequisites) -- [How It Works](#how-it-works) -- [Interactive Flow](#interactive-flow) -- [Typical Workflow](#typical-workflow) -- [Configuration](#configuration) -- [Error Handling](#error-handling) -- [Use Cases](#use-cases) -- [Notes](#notes) - -## Syntax - -```bash -npx base44 site deploy [options] -``` - -## Options - -| Option | Description | -| ------------ | ------------------------- | -| `-y, --yes` | Skip confirmation prompt | - -Use `-y` flag for non-interactive/automated deployments: - -```bash -npx base44 site deploy -y -``` - -## Authentication - -**Required**: Yes. If not authenticated, you'll be prompted to login first. - -## Prerequisites - -- Must be run from a Base44 project directory -- Project must have `site.outputDirectory` configured in project config -- Site must be built before deploying (run your build command first) -- **SPA only**: Base44 hosting supports Single Page Applications with a single `index.html` entry point. All routes are served from `index.html` (client-side routing). - -## How It Works - -1. Reads project configuration -2. Validates that site configuration exists -3. Prompts for deployment confirmation showing the output directory -4. Creates an archive of site files from the output directory -5. Deploys to Base44 hosting -6. Returns the app URL - -## Interactive Flow - -```bash -$ npx base44 site deploy - -Deploy site from ./dist? (yes/no) yes - -Creating archive... -Uploading to Base44... -Deploying... - -✓ Deployment successful! - -Visit your site at: https://my-app.base44.app -``` - -## Typical Workflow - -```bash -# 1. Build your site using your framework's build command -npm run build - -# 2. Deploy to Base44 -npx base44 site deploy -``` - -## Configuration - -The `site.outputDirectory` in your project configuration should point to where your framework outputs built files: - -- Vite: typically `./dist` -- Next.js: typically `./.next` or `./out` -- Create React App: typically `./build` -- Custom: whatever your build tool outputs to - -## Error Handling - -If site configuration is missing: -```bash -$ npx base44 site deploy -Error: No site configuration found in project -``` - -If you cancel the deployment: -```bash -Deploy site from ./dist? (yes/no) no -Deployment cancelled -``` - -## Use Cases - -- Deploy your site after making changes -- Push new versions of your application -- Deploy after updating content or functionality -- Part of your CI/CD pipeline - -## Notes - -- Always build your site before deploying -- The command deploys whatever is in your output directory -- Make sure your build completed successfully before deploying -- Previous deployments are preserved (versioned) in Base44 -- Deployment is immediate and updates your live site diff --git a/plugins/base44/skills/base44-cli/references/site-open.md b/plugins/base44/skills/base44-cli/references/site-open.md deleted file mode 100644 index b3d5641f1..000000000 --- a/plugins/base44/skills/base44-cli/references/site-open.md +++ /dev/null @@ -1,39 +0,0 @@ -# base44 site open - -Opens the published site in your default web browser. - -## Syntax - -```bash -npx base44 site open -``` - -## Authentication - -**Required**: Yes. If not authenticated, you'll be prompted to login first. - -## What It Does - -1. Fetches the site URL from Base44 -2. Opens the site in your default browser -3. Displays the site URL in the terminal - -## Example - -```bash -$ npx base44 site open - -Site opened at https://my-app.base44.app -``` - -## Requirements - -- Must be run from a linked Base44 project directory -- Must be authenticated (run `npx base44 login` first) -- Site must have been deployed at least once - -## Notes - -- The command will not open a browser in CI environments (when `process.env.CI` is set) -- Use this command to quickly view your deployed site -- The site URL is also displayed after deploying with `base44 site deploy` or `base44 deploy` diff --git a/plugins/base44/skills/base44-cli/references/types-generate.md b/plugins/base44/skills/base44-cli/references/types-generate.md deleted file mode 100644 index ba4cd611d..000000000 --- a/plugins/base44/skills/base44-cli/references/types-generate.md +++ /dev/null @@ -1,104 +0,0 @@ -# `base44 types generate` - -Generate TypeScript declaration file (`types.d.ts`) from project resources (entities, functions, agents, connectors). - -## Usage - -```bash -npx base44 types generate -``` - -## What It Does - -1. **Reads project configuration** — Scans `base44/entities/`, `base44/functions/`, `base44/agents/`, and `base44/connectors/` for all defined resources -2. **Generates `base44/.types/types.d.ts`** — Creates a TypeScript declaration file that augments the `@base44/sdk` module with typed registries -3. **Updates `tsconfig.json`** (if present) — Automatically adds `base44/.types/*.d.ts` to the `include` array so TypeScript picks up the generated types - -## Authentication - -**Not required.** This command runs entirely locally and does not need authentication. - -## Output File - -The generated file is placed at: - -``` -base44/.types/types.d.ts -``` - -### Generated Content - -The declaration file augments the `@base44/sdk` module with four registries: - -- **`EntityTypeRegistry`** — Maps entity names to their TypeScript interfaces (compiled from entity JSON schemas) -- **`FunctionNameRegistry`** — Lists all backend function names -- **`AgentNameRegistry`** — Lists all agent names -- **`ConnectorTypeRegistry`** — Lists all connector types - -**Example output:** - -```typescript -// Auto-generated by Base44 CLI - DO NOT EDIT -// Regenerate with: base44 types generate - -export interface Task { - title: string; - status: "todo" | "in_progress" | "done"; - assignee?: string; -} - -export interface Board { - name: string; - description?: string; -} - -declare module '@base44/sdk' { - interface EntityTypeRegistry { - "Task": Task; - "Board": Board; - } - - interface FunctionNameRegistry { - "send_email": true; - } - - interface AgentNameRegistry { - "support_agent": true; - } - - interface ConnectorTypeRegistry { - "googlecalendar": true; - } -} -``` - -If no resources are found, the file contains a placeholder with instructions on how to add resources. - -## tsconfig.json Integration - -If a `tsconfig.json` exists in the project root, the command automatically adds `base44/.types/*.d.ts` to the `include` array: - -```json -{ - "include": [ - "src", - "base44/.types/*.d.ts" - ] -} -``` - -If the path is already included, or no `tsconfig.json` exists, this step is silently skipped. - -## When to Run - -- After creating or modifying entity schemas in `base44/entities/` -- After adding or removing backend functions in `base44/functions/` -- After adding or removing agents in `base44/agents/` -- After adding or removing connectors in `base44/connectors/` -- When setting up a TypeScript project for the first time with Base44 - -## Notes - -- The generated file should **not** be manually edited — it will be overwritten on the next run -- Consider adding `base44 types generate` to your build pipeline or as a pre-build script -- The `.types` directory is created automatically inside the `base44/` folder diff --git a/plugins/base44/skills/base44-sdk/SKILL.md b/plugins/base44/skills/base44-sdk/SKILL.md deleted file mode 100644 index c4cfbacdf..000000000 --- a/plugins/base44/skills/base44-sdk/SKILL.md +++ /dev/null @@ -1,305 +0,0 @@ ---- -name: base44-sdk -description: "The base44 SDK is the library to communicate with base44 services. In projects, you use it to communicate with remote resources (entities, backend functions, ai agents) and to write backend functions. This skill is the place for learning about available modules and types. When you plan or implement a feature, you must learn this skill" ---- - -# Base44 Coder - -Build apps on the Base44 platform using the Base44 JavaScript SDK. - -## ⚡ IMMEDIATE ACTION REQUIRED - Read This First - -This skill activates on ANY mention of "base44" or when a `base44/` folder exists. **DO NOT read documentation files or search the web before acting.** - -**Your first action MUST be:** -1. Check if `base44/config.jsonc` exists in the current directory -2. If **YES** (existing project scenario): - - This skill (base44-sdk) handles the request - - Implement features using Base44 SDK - - Do NOT use base44-cli unless user explicitly requests CLI commands -3. If **NO** (new project scenario): - - Transfer to base44-cli skill for project initialization - - This skill cannot help until project is initialized - -## When to Use This Skill vs base44-cli - -**Use base44-sdk when:** -- Building features in an **EXISTING** Base44 project -- `base44/config.jsonc` already exists in the project -- Base44 SDK imports are present (`@base44/sdk`) -- Writing JavaScript/TypeScript code using Base44 SDK modules -- Implementing functionality, components, or features -- User mentions: "implement", "build a feature", "add functionality", "write code for" -- User says "create a [type] app" **and** a Base44 project already exists - -**DO NOT USE base44-sdk for:** -- ❌ Initializing new Base44 projects (use `base44-cli` instead) -- ❌ Empty directories without Base44 configuration -- ❌ When user says "create a new Base44 project/app/site" and no project exists -- ❌ CLI commands like `npx base44 create`, `npx base44 deploy`, `npx base44 login` (use `base44-cli`) - -**Skill Dependencies:** -- `base44-sdk` assumes a Base44 project is **already initialized** -- `base44-cli` is a **prerequisite** for `base44-sdk` in new projects -- If user wants to "create an app" and no Base44 project exists, use `base44-cli` first - -**State Check Logic:** -Before selecting this skill, verify: -- IF (user mentions "create/build app" OR "make a project"): - - IF (directory is empty OR no `base44/config.jsonc` exists): - → Use **base44-cli** (project initialization needed) - - ELSE: - → Use **base44-sdk** (project exists, build features) - -## Quick Start - -```javascript -// In Base44-generated apps, base44 client is pre-configured and available - -// CRUD operations -const task = await base44.entities.Task.create({ title: "New task", status: "pending" }); -const tasks = await base44.entities.Task.list(); -await base44.entities.Task.update(task.id, { status: "done" }); - -// Get current user -const user = await base44.auth.me(); -``` - -```javascript -// External apps -import { createClient } from "@base44/sdk"; - -// IMPORTANT: Use 'appId' (NOT 'clientId' or 'id') -const base44 = createClient({ appId: "your-app-id" }); -await base44.auth.loginViaEmailPassword("user@example.com", "password"); -``` - -## ⚠️ CRITICAL: Do Not Hallucinate APIs - -**Before writing ANY Base44 code, verify method names against this table or [QUICK_REFERENCE.md](references/QUICK_REFERENCE.md).** - -Base44 SDK has unique method names. Do NOT assume patterns from Firebase, Supabase, or other SDKs. - -### Authentication - WRONG vs CORRECT - -| ❌ WRONG (hallucinated) | ✅ CORRECT | -|------------------------|-----------| -| `signInWithGoogle()` | `loginWithProvider('google')` | -| `signInWithProvider('google')` | `loginWithProvider('google')` | -| `auth.google()` | `loginWithProvider('google')` | -| `signInWithEmailAndPassword(email, pw)` | `loginViaEmailPassword(email, pw)` | -| `signIn(email, pw)` | `loginViaEmailPassword(email, pw)` | -| `createUser()` / `signUp()` | `register({email, password})` | -| `onAuthStateChanged()` | `me()` (no listener, call when needed) | -| `currentUser` | `await auth.me()` | - -### Functions - WRONG vs CORRECT - -| ❌ WRONG (hallucinated) | ✅ CORRECT | -|------------------------|-----------| -| `functions.call('name', data)` | `functions.invoke('name', data)` | -| `functions.run('name', data)` | `functions.invoke('name', data)` | -| `callFunction('name', data)` | `functions.invoke('name', data)` | -| `httpsCallable('name')(data)` | `functions.invoke('name', data)` | - -### Integrations - WRONG vs CORRECT - -| ❌ WRONG (hallucinated) | ✅ CORRECT | -|------------------------|-----------| -| `ai.generate(prompt)` | `integrations.Core.InvokeLLM({prompt})` | -| `openai.chat(prompt)` | `integrations.Core.InvokeLLM({prompt})` | -| `llm(prompt)` | `integrations.Core.InvokeLLM({prompt})` | -| `sendEmail(to, subject, body)` | `integrations.Core.SendEmail({to, subject, body})` | -| `email.send()` | `integrations.Core.SendEmail({to, subject, body})` | -| `uploadFile(file)` | `integrations.Core.UploadFile({file})` | -| `storage.upload(file)` | `integrations.Core.UploadFile({file})` | - -### Entities - WRONG vs CORRECT - -| ❌ WRONG (hallucinated) | ✅ CORRECT | -|------------------------|-----------| -| `entities.Task.find({...})` | `entities.Task.filter({...})` | -| `entities.Task.findOne(id)` | `entities.Task.get(id)` | -| `entities.Task.insert(data)` | `entities.Task.create(data)` | -| `entities.Task.remove(id)` | `entities.Task.delete(id)` | -| `entities.Task.onChange(cb)` | `entities.Task.subscribe(cb)` | - -## SDK Modules - -| Module | Purpose | Reference | -|--------|---------|-----------| -| `entities` | CRUD operations on data models | [entities.md](references/entities.md) | -| `auth` | Login, register, user management | [auth.md](references/auth.md) | -| `agents` | AI conversations and messages | [base44-agents.md](references/base44-agents.md) | -| `functions` | Backend function invocation | [functions.md](references/functions.md) | -| `integrations` | AI, email, file uploads, custom APIs | [integrations.md](references/integrations.md) | -| `analytics` | Track custom events and user activity | [analytics.md](references/analytics.md) | -| `appLogs` | Log user activity in app | [app-logs.md](references/app-logs.md) | -| `users` | Invite users to the app | [users.md](references/users.md) | -| `asServiceRole.connectors` | App-scoped OAuth tokens (service role only) | [connectors.md](references/connectors.md) | -| `asServiceRole.sso` | SSO token generation (service role only) | [sso.md](references/sso.md) | - -For client setup and authentication modes, see [client.md](references/client.md). - -### TypeScript and type registries - -Each reference file includes a "Type Definitions" section with TypeScript interfaces and types for the module's methods, parameters, and return values. - -**Getting typed entities, functions, and agents:** The Base44 CLI generates types from your project resources (entities, functions, agents), including augmentations to `EntityTypeRegistry`, `FunctionNameRegistry`, and `AgentNameRegistry`, and wires them into your project so you get autocomplete and type checking without manual setup. For how to generate types, use the **base44-cli** skill. - -**Manual augmentation:** You can instead augment the registries yourself in a `.d.ts` file; see the Type Definitions sections in [entities.md](references/entities.md), [functions.md](references/functions.md), and [base44-agents.md](references/base44-agents.md). - -## Installation - -Install the Base44 SDK: - -```bash -npm install @base44/sdk -``` - -**Important:** Never assume or hardcode the `@base44/sdk` package version. Always install without a version specifier to get the latest version. - -## Creating a Client (External Apps) - -When creating a client in external apps, **ALWAYS use `appId` as the parameter name**: - -```javascript -import { createClient } from "@base44/sdk"; - -// ✅ CORRECT -const base44 = createClient({ appId: "your-app-id" }); - -// ❌ WRONG - Do NOT use these: -// const base44 = createClient({ clientId: "your-app-id" }); // WRONG -// const base44 = createClient({ id: "your-app-id" }); // WRONG -``` - -**Required parameter:** `appId` (string) - Your Base44 application ID - -**Optional parameters:** -- `token` (string) - Pre-authenticated user token -- `options` (object) - Configuration options - - `options.onError` (function) - Global error handler - -**Example with error handler:** -```javascript -const base44 = createClient({ - appId: "your-app-id", - options: { - onError: (error) => { - console.error("Base44 error:", error); - } - } -}); -``` - -## Module Selection - -**Working with app data?** -- Create/read/update/delete records → `entities` -- Import data from file → `entities.importEntities()` -- Realtime updates → `entities.EntityName.subscribe()` - -**User management?** -- Login/register/logout → `auth` -- Get current user → `auth.me()` -- Update user profile → `auth.updateMe()` -- Invite users → `users.inviteUser()` - -**AI features?** -- Chat with AI agents → `agents` (requires logged-in user) -- Create new conversation → `agents.createConversation()` -- Manage conversations → `agents.getConversations()` -- Generate text/JSON with AI → `integrations.Core.InvokeLLM()` -- Generate images → `integrations.Core.GenerateImage()` - -**Custom backend logic?** -- Run server-side code → `functions.invoke()` -- Need admin access → `base44.asServiceRole.functions.invoke()` - -**External services?** -- Send emails → `integrations.Core.SendEmail()` -- Upload files → `integrations.Core.UploadFile()` -- Custom APIs → `integrations.custom.call()` -- App-scoped OAuth (app builder's account) → `asServiceRole.connectors.getConnection()` (backend only) - -**Tracking and analytics?** -- Track custom events → `analytics.track()` -- Log page views/activity → `appLogs.logUserInApp()` - -## Common Patterns - -### Filter and Sort Data - -```javascript -const pendingTasks = await base44.entities.Task.filter( - { status: "pending", assignedTo: userId }, // query - "-created_date", // sort (descending) - 10, // limit - 0 // skip -); -``` - -### Protected Routes (check auth) - -```javascript -const user = await base44.auth.me(); -if (!user) { - // Navigate to your custom login page - navigate('/login', { state: { returnTo: window.location.pathname } }); - return; -} -``` - -### Backend Function Call - -```javascript -// Frontend -const result = await base44.functions.invoke("processOrder", { - orderId: "123", - action: "ship" -}); - -// Backend function (Deno) -import { createClientFromRequest } from "npm:@base44/sdk"; - -Deno.serve(async (req) => { - const base44 = createClientFromRequest(req); - const { orderId, action } = await req.json(); - // Process with service role for admin access - const order = await base44.asServiceRole.entities.Orders.get(orderId); - return Response.json({ success: true }); -}); -``` - -### Service Role Access - -Use `asServiceRole` in backend functions for admin-level operations: - -```javascript -// User mode - respects permissions -const myTasks = await base44.entities.Task.list(); - -// Service role - full access (backend only) -const allTasks = await base44.asServiceRole.entities.Task.list(); -const token = await base44.asServiceRole.connectors.getAccessToken("slack"); -``` - -## Frontend vs Backend - -| Capability | Frontend | Backend | -|------------|----------|---------| -| `entities` (user's data) | Yes | Yes | -| `auth` | Yes | Yes | -| `agents` | Yes | Yes | -| `functions.invoke()` | Yes | Yes | -| `functions.fetch()` | Yes | Yes | -| `integrations` | Yes | Yes | -| `analytics` | Yes | Yes | -| `appLogs` | Yes | Yes | -| `users` | Yes | Yes | -| `asServiceRole.*` | No | Yes | -| `asServiceRole.connectors` (app OAuth) | No | Yes | -| `asServiceRole.sso` | No | Yes | - -Backend functions use `Deno.serve()` and `createClientFromRequest(req)` to get a properly authenticated client. diff --git a/plugins/base44/skills/base44-sdk/agents/openai.yaml b/plugins/base44/skills/base44-sdk/agents/openai.yaml deleted file mode 100644 index 898aa4541..000000000 --- a/plugins/base44/skills/base44-sdk/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Base44 SDK" - short_description: "Build features with the Base44 JavaScript and TypeScript SDK" - default_prompt: "Use Base44 SDK guidance to implement this feature against entities, functions, auth, and integrations." diff --git a/plugins/base44/skills/base44-sdk/references/QUICK_REFERENCE.md b/plugins/base44/skills/base44-sdk/references/QUICK_REFERENCE.md deleted file mode 100644 index 0d2271a49..000000000 --- a/plugins/base44/skills/base44-sdk/references/QUICK_REFERENCE.md +++ /dev/null @@ -1,178 +0,0 @@ -# Base44 SDK Quick Reference - -Compact method signatures for all SDK modules. **Verify against this before writing code.** - ---- - -## Auth (`base44.auth.*`) - -``` -loginViaEmailPassword(email, password, turnstileToken?) → Promise<{access_token, user}> -loginWithProvider('google' | 'microsoft' | 'facebook', fromUrl?) → void -me() → Promise -updateMe(data) → Promise -isAuthenticated() → Promise -logout(redirectUrl?) → void -redirectToLogin(nextUrl) → void # ⚠️ Avoid - prefer custom login UI -register({email, password, turnstile_token?, referral_code?}) → Promise -verifyOtp({email, otpCode}) → Promise -resendOtp(email) → Promise -inviteUser(userEmail, role) → Promise -resetPasswordRequest(email) → Promise -resetPassword({resetToken, newPassword}) → Promise -changePassword({userId, currentPassword, newPassword}) → Promise -setToken(token, saveToStorage?) → void -``` - ---- - -## Entities (`base44.entities.EntityName.*`) - -``` -create(data) → Promise -bulkCreate(dataArray) → Promise -list(sort?, limit?, skip?, fields?) → Promise[]> -filter(query, sort?, limit?, skip?, fields?) → Promise[]> -get(id) → Promise -update(id, data) → Promise -updateMany(query, mongoUpdateOp) → Promise // e.g. { $set: { field: val } } -bulkUpdate(dataArray) → Promise // each item must have id -delete(id) → Promise -deleteMany(query) → Promise -importEntities(file) → Promise> // frontend only -subscribe(callback) → () => void // returns unsubscribe fn -``` - -**Sort:** Use `SortField`: `-fieldName` for descending (e.g., `-created_date`). Max 5,000 per request for list/filter. - ---- - -## Functions (`base44.functions.*`) - -``` -invoke(functionName, data?) → Promise -fetch(path, init?) → Promise // low-level, for streaming/custom methods -``` - -**Backend:** Use `base44.asServiceRole.functions.invoke()` for admin access. - ---- - -## Integrations (`base44.integrations.Core.*`) - -``` -InvokeLLM({prompt, add_context_from_internet?, response_json_schema?, file_urls?}) → Promise -GenerateImage({prompt}) → Promise<{url}> -SendEmail({to, subject, body, from_name?}) → Promise -UploadFile({file}) → Promise<{file_url}> -UploadPrivateFile({file}) → Promise<{file_uri}> -CreateFileSignedUrl({file_uri, expires_in?}) → Promise<{signed_url}> -ExtractDataFromUploadedFile({file_url, json_schema}) → Promise -``` - -### Custom Integrations (`base44.integrations.custom.*`) - -``` -call(slug, operationId, {payload?, pathParams?, queryParams?}?) → Promise<{success, status_code, data}> -``` - -**operationId format:** `"method:/path"` (e.g., `"get:/contacts"`, `"post:/users/{id}"`) - ---- - -## Analytics (`base44.analytics.*`) - -``` -track({eventName, properties?}) → void -``` - ---- - -## App Logs (`base44.appLogs.*`) - -``` -logUserInApp(pageName) → Promise -fetchLogs(params?) → Promise -getStats(params?) → Promise -``` - ---- - -## Users (`base44.users.*`) - -``` -inviteUser(userEmail, role) → Promise // role: 'user' | 'admin' -``` - ---- - -## Service Role Connectors (`base44.asServiceRole.connectors.*`) - -**Backend only, service role required.** App-scoped (shared account). - -``` -getConnection(integrationType) → Promise<{accessToken, connectionConfig}> // recommended -getAccessToken(integrationType) → Promise // deprecated -``` - -**Types:** Run `npx base44 connectors list-available` to see all available integration types. - ---- - -## SSO (`base44.asServiceRole.sso.*`) - -**Backend only, service role required.** - -``` -getAccessToken(userId) → Promise<{access_token}> -``` - ---- - -## Service Role Access - -**Backend functions only.** Prefix any module with `asServiceRole` for admin access: - -```javascript -base44.asServiceRole.entities.Task.list() -base44.asServiceRole.functions.invoke('name', data) -base44.asServiceRole.connectors.getConnection('slack') -base44.asServiceRole.sso.getAccessToken(userId) -``` - ---- - -## Backend Function Template - -```javascript -import { createClientFromRequest } from "@base44/sdk"; - -Deno.serve(async (req) => { - const base44 = createClientFromRequest(req); - const data = await req.json(); - - // User context - const user = await base44.auth.me(); - - // Service role for admin operations - const allRecords = await base44.asServiceRole.entities.Task.list(); - - return Response.json({ success: true }); -}); -``` - ---- - -## Client Initialization (External Apps) - -```javascript -import { createClient } from "@base44/sdk"; - -const base44 = createClient({ appId: "your-app-id" }); // MUST use 'appId' -``` - ---- - -## TypeScript type registries - -For typed entities, function names, and agent names (autocomplete and type checking), the Base44 CLI generates types and wires them into your project. Use the **base44-cli** skill for how to generate types. diff --git a/plugins/base44/skills/base44-sdk/references/analytics.md b/plugins/base44/skills/base44-sdk/references/analytics.md deleted file mode 100644 index d1b2c697d..000000000 --- a/plugins/base44/skills/base44-sdk/references/analytics.md +++ /dev/null @@ -1,113 +0,0 @@ -# Analytics Module - -Track custom events and user activity via `base44.analytics`. - -## Contents -- [Methods](#methods) -- [Examples](#examples) -- [Automatic Tracking](#automatic-tracking) -- [Best Practices](#best-practices) - -## Methods - -| Method | Signature | Description | -|--------|-----------|-------------| -| `track(params)` | `void` | Track a custom event | - -## Examples - -### Track Custom Event - -```javascript -// Track a simple event -base44.analytics.track({ - eventName: "button_clicked" -}); - -// Track event with properties -base44.analytics.track({ - eventName: "purchase_completed", - properties: { - product_id: "prod-123", - amount: 99.99, - currency: "USD" - } -}); -``` - -### Track User Actions - -```javascript -// Track page view -base44.analytics.track({ - eventName: "page_view", - properties: { - page: "/dashboard", - referrer: document.referrer - } -}); - -// Track feature usage -base44.analytics.track({ - eventName: "feature_used", - properties: { - feature: "export_data", - format: "csv" - } -}); -``` - -## Automatic Tracking - -The analytics module automatically tracks: -- **Initialization events**: When the app loads -- **Heartbeat events**: Periodic activity signals -- **Session duration**: Time spent in the app - -These internal events help measure user engagement without manual instrumentation. - -## Best Practices - -1. **Use descriptive event names**: `order_completed` instead of `click` -2. **Include relevant properties**: Add context that helps analyze the event -3. **Be consistent**: Use the same event names and property keys across your app -4. **Don't track sensitive data**: Avoid PII in event properties - -```javascript -// Good: Descriptive with relevant properties -base44.analytics.track({ - eventName: "subscription_started", - properties: { - plan: "pro", - billing_cycle: "annual" - } -}); - -// Avoid: Vague event name, no context -base44.analytics.track({ - eventName: "click" -}); -``` - -## Type Definitions - -```typescript -/** Properties that can be attached to a tracked event. */ -type TrackEventProperties = { - [key: string]: string | number | boolean | null | undefined; -}; - -/** Parameters for the track() method. */ -interface TrackEventParams { - /** The name of the event to track. */ - eventName: string; - /** Optional properties to attach to the event. */ - properties?: TrackEventProperties; -} - -/** The analytics module interface. */ -interface AnalyticsModule { - /** Track a custom event with optional properties. */ - track(params: TrackEventParams): void; -} -``` diff --git a/plugins/base44/skills/base44-sdk/references/app-logs.md b/plugins/base44/skills/base44-sdk/references/app-logs.md deleted file mode 100644 index 9865bb060..000000000 --- a/plugins/base44/skills/base44-sdk/references/app-logs.md +++ /dev/null @@ -1,120 +0,0 @@ -# App Logs Module - -Log user activity in your app via `base44.appLogs`. - -## Contents -- [Methods](#methods) -- [Examples](#examples) -- [Use Cases](#use-cases) - -## Methods - -| Method | Signature | Description | -|--------|-----------|-------------| -| `logUserInApp(pageName)` | `Promise` | Log user activity on a page | -| `fetchLogs(params?)` | `Promise` | Fetch app logs with optional filter parameters | -| `getStats(params?)` | `Promise` | Get app usage statistics | - -## Examples - -### Log User Activity - -```javascript -// Log when user visits a page -await base44.appLogs.logUserInApp("dashboard"); - -// Log specific page visits -await base44.appLogs.logUserInApp("settings"); -await base44.appLogs.logUserInApp("profile"); - -// Log feature usage -await base44.appLogs.logUserInApp("export-button-click"); -``` - -The page name doesn't have to be an actual page - it can be any string you want to track. - -## Use Cases - -### Track Page Views in React - -```javascript -// Log page views on route change -useEffect(() => { - base44.appLogs.logUserInApp(window.location.pathname); -}, [location.pathname]); -``` - -### Track Feature Usage - -```javascript -// Log when user uses specific features -function handleExport() { - base44.appLogs.logUserInApp("export-data"); - // ... export logic -} - -function handleSettingsChange() { - base44.appLogs.logUserInApp("settings-updated"); - // ... save settings -} -``` - -### Fetch Logs - -```javascript -// Fetch all logs -const logs = await base44.appLogs.fetchLogs(); - -// Fetch logs with filters -const recentLogs = await base44.appLogs.fetchLogs({ - limit: 50, - page: "/dashboard" -}); -``` - -### Get Stats - -```javascript -// Get usage statistics for the app -const stats = await base44.appLogs.getStats(); - -// Get stats with date range params -const weekStats = await base44.appLogs.getStats({ - from: "2024-01-01", - to: "2024-01-07" -}); -``` - -## Notes - -- Logs appear in the Analytics page of your app dashboard -- App logs track page-level and feature-level activity -- Use `analytics.track()` for custom events with properties, `appLogs.logUserInApp()` for simple page/feature tracking - -## Type Definitions - -```typescript -/** App Logs module for tracking and analyzing app usage. */ -interface AppLogsModule { - /** - * Log user activity in the app. - * @param pageName - Name of the page or section being visited. - * @returns Promise that resolves when the log is recorded. - */ - logUserInApp(pageName: string): Promise; - - /** - * Fetch app logs with optional filter parameters. - * @param params - Optional filter parameters (e.g., limit, page name, date range). - * @returns Promise resolving to the logs data. - */ - fetchLogs(params?: Record): Promise; - - /** - * Get app usage statistics. - * @param params - Optional filter parameters (e.g., date range). - * @returns Promise resolving to the statistics data. - */ - getStats(params?: Record): Promise; -} -``` diff --git a/plugins/base44/skills/base44-sdk/references/auth.md b/plugins/base44/skills/base44-sdk/references/auth.md deleted file mode 100644 index ad3c43d1b..000000000 --- a/plugins/base44/skills/base44-sdk/references/auth.md +++ /dev/null @@ -1,770 +0,0 @@ -# Auth Module - -User authentication, registration, and session management via `base44.auth`. - -## Contents -- [TypeScript Types](#typescript-types) -- [Methods](#methods) -- [Examples](#examples) -- [Error Handling](#error-handling) -- [Auth Providers](#auth-providers) -- [Environment Availability](#environment-availability) -- [App Visibility](#app-visibility) -- [Limitations](#limitations) - ---- - -## TypeScript Types - -### User Interface -```typescript -interface User { - id: string; - created_date: string; - updated_date: string; - email: string; - full_name: string | null; - disabled: boolean | null; - is_verified: boolean; - app_id: string; - is_service: boolean; - role: string; - [key: string]: any; // Custom schema fields -} -``` - -### LoginResponse Interface -```typescript -interface LoginResponse { - access_token: string; // JWT token - user: User; // Complete user object -} -``` - -### Parameter Interfaces - -#### RegisterParams -```typescript -interface RegisterParams { - email: string; // Required - password: string; // Required - turnstile_token?: string | null; // Optional: Cloudflare Turnstile for bot protection - referral_code?: string | null; // Optional: Referral code -} -``` - -#### VerifyOtpParams -```typescript -interface VerifyOtpParams { - email: string; // User's email - otpCode: string; // OTP code from email -} -``` - -#### ResetPasswordParams -```typescript -interface ResetPasswordParams { - resetToken: string; // Token from password reset email - newPassword: string; // New password to set -} -``` - -#### ChangePasswordParams -```typescript -interface ChangePasswordParams { - userId: string; // User ID - currentPassword: string; // Current password for verification - newPassword: string; // New password to set -} -``` - -### Provider Type -```typescript -type Provider = 'google' | 'microsoft' | 'facebook'; -``` - ---- - -## Methods - -### Module Interface -```typescript -interface AuthModule { - // User Info - me(): Promise; - updateMe(data: Partial>): Promise; - isAuthenticated(): Promise; - - // Login/Logout - loginViaEmailPassword(email: string, password: string, turnstileToken?: string): Promise; - loginWithProvider(provider: Provider, fromUrl?: string): void; - logout(redirectUrl?: string): void; - redirectToLogin(nextUrl: string): void; - - // Token Management - setToken(token: string, saveToStorage?: boolean): void; - - // Registration - register(params: RegisterParams): Promise; - verifyOtp(params: VerifyOtpParams): Promise; - resendOtp(email: string): Promise; - - // User Management - inviteUser(userEmail: string, role: string): Promise; - - // Password Management - resetPasswordRequest(email: string): Promise; - resetPassword(params: ResetPasswordParams): Promise; - changePassword(params: ChangePasswordParams): Promise; -} -``` - -### Method Reference Table - -| Method | Parameters | Return Type | Description | -|--------|-----------|-------------|-------------| -| `register()` | `params: RegisterParams` | `Promise` | Create new user account | -| `loginViaEmailPassword()` | `email: string, password: string, turnstileToken?: string` | `Promise` | Authenticate with email/password | -| `loginWithProvider()` | `provider: Provider, fromUrl?: string` | `void` | Initiate OAuth login flow. Providers: `'google'` (default), `'microsoft'`, `'facebook'` (enable in app settings) | -| `me()` | None | `Promise` | Get current authenticated user | -| `updateMe()` | `data: Partial` | `Promise` | Update current user's profile | -| `logout()` | `redirectUrl?: string` | `void` | Redirect to server-side logout (clears HTTP-only cookies and session), then to redirectUrl or current URL | -| `redirectToLogin()` | `nextUrl: string` | `void` | ⚠️ **Avoid** - Prefer custom login UI with `loginViaEmailPassword()` or `loginWithProvider()` | -| `isAuthenticated()` | None | `Promise` | Check if user is logged in | -| `setToken()` | `token: string, saveToStorage?: boolean` | `void` | Manually set auth token | -| `inviteUser()` | `userEmail: string, role: string` | `Promise` | Send invitation email | -| `verifyOtp()` | `params: VerifyOtpParams` | `Promise` | Verify OTP code | -| `resendOtp()` | `email: string` | `Promise` | Resend OTP code | -| `resetPasswordRequest()` | `email: string` | `Promise` | Request password reset | -| `resetPassword()` | `params: ResetPasswordParams` | `Promise` | Reset password with token | -| `changePassword()` | `params: ChangePasswordParams` | `Promise` | Change user password | - ---- - -## Examples - -### Register New User (Complete Flow) - -Registration requires email verification before login. Complete flow: - -1. **Register** - Create the user account -2. **Verification email sent** - User receives an OTP code -3. **Verify OTP** - User enters code to verify email -4. **Login** - User can now log in - -```javascript -try { - // Step 1: Register the user - await base44.auth.register({ - email: "user@example.com", - password: "securePassword123", - referral_code: "OPTIONAL_CODE", // optional - turnstile_token: "CAPTCHA_TOKEN" // optional, for bot protection - }); - console.log('Registration successful. Check email for OTP code.'); - - // Step 2: User receives email with OTP code (e.g., "123456") - - // Step 3: Verify the OTP code - await base44.auth.verifyOtp({ - email: "user@example.com", - otpCode: "123456" // code from verification email - }); - console.log('Email verified successfully.'); - - // Step 4: Now the user can log in - const loginResponse = await base44.auth.loginViaEmailPassword( - "user@example.com", - "securePassword123" - ); - console.log('Login successful:', loginResponse.user); - -} catch (error) { - console.error('Registration flow failed:', error.message); - // Handle specific errors (see Error Handling section) -} -``` - -> **Important**: Users cannot log in until they complete OTP verification. Attempting to call `loginViaEmailPassword` before verification will fail. - -### Login with Email/Password - -```javascript -try { - const response = await base44.auth.loginViaEmailPassword( - "user@example.com", - "password123", - turnstileToken // optional: for bot protection - ); - - console.log('Login successful'); - console.log('User:', response.user); - console.log('Token:', response.access_token); - - // JWT is automatically stored for subsequent requests - -} catch (error) { - console.error('Login failed:', error.message); - if (error.status === 401) { - console.error('Invalid credentials'); - } else if (error.status === 403) { - console.error('Email not verified. Please check your email for OTP.'); - } -} -``` - -### Login with OAuth Provider - -Supported providers: `'google'` (enabled by default), `'microsoft'`, and `'facebook'`. Enable Microsoft or Facebook in your app's authentication settings before using them. - -```javascript -// Redirect to Google OAuth -base44.auth.loginWithProvider('google'); - -// Redirect to Google OAuth and return to current page after -base44.auth.loginWithProvider('google', window.location.href); - -// Microsoft or Facebook (enable in app settings first) -base44.auth.loginWithProvider('microsoft'); -base44.auth.loginWithProvider('facebook', '/dashboard'); -``` - -### Get Current User - -```javascript -try { - const user = await base44.auth.me(); - - if (user) { - console.log('User ID:', user.id); - console.log('Email:', user.email); - console.log('Name:', user.full_name); - console.log('Role:', user.role); - console.log('Verified:', user.is_verified); - } else { - console.log('User not authenticated'); - } - -} catch (error) { - console.error('Failed to fetch user:', error.message); - if (error.status === 401) { - // Token expired or invalid - navigate to your custom login page - navigate('/login'); - } -} -``` - -### Update User Profile - -```javascript -try { - const updatedUser = await base44.auth.updateMe({ - full_name: "John Doe", - // Custom schema fields can be updated here - phone: "+1234567890", - preferences: { theme: "dark" } - }); - - console.log('Profile updated:', updatedUser); - -} catch (error) { - console.error('Profile update failed:', error.message); - if (error.status === 400) { - console.error('Invalid data provided'); - } else if (error.status === 401) { - console.error('Authentication required'); - navigate('/login'); - } -} -``` - -### Check Authentication Status - -```javascript -try { - const isLoggedIn = await base44.auth.isAuthenticated(); - - if (isLoggedIn) { - // Show authenticated UI - console.log('User is authenticated'); - } else { - // Show login button - console.log('User is not authenticated'); - } - -} catch (error) { - console.error('Failed to check authentication:', error.message); - // On error, treat as not authenticated -} -``` - -### Logout - -Logout redirects the user to the server-side logout endpoint (`/api/apps/auth/logout`) to clear HTTP-only cookies and the session, then redirects to the given URL (or the current page if omitted). Requires a browser environment. - -```javascript -// Logout: clears session via server, then redirects to current page -base44.auth.logout(); - -// Logout and redirect to goodbye page after -base44.auth.logout("/goodbye"); - -// Logout and redirect to homepage -base44.auth.logout("/"); -``` - -### Protected Route Pattern - -```javascript -// Using a navigation function (e.g., React Router's useNavigate, Next.js router) -async function requireAuth(navigate) { - try { - const user = await base44.auth.me(); - - if (!user) { - // Navigate to your custom login page - navigate('/login', { state: { returnTo: window.location.pathname } }); - return null; - } - - return user; - - } catch (error) { - console.error('Authentication check failed:', error.message); - navigate('/login', { state: { returnTo: window.location.pathname } }); - return null; - } -} - -// Usage in your app -async function loadProtectedPage(navigate) { - const user = await requireAuth(navigate); - if (!user) { - // Will navigate to login - return; - } - - // Continue with authenticated logic - console.log('Welcome,', user.full_name); -} -``` - -### Set Authentication Token - -```javascript -// SECURITY WARNING: Never hardcode tokens or expose them in client code -// Tokens should only be received from secure authentication flows - -// Set token and save to localStorage (default) -base44.auth.setToken(receivedToken); - -// Set token without saving to localStorage (temporary session) -base44.auth.setToken(receivedToken, false); - -// Verify token was set -try { - const isAuthenticated = await base44.auth.isAuthenticated(); - if (!isAuthenticated) { - console.error('Token validation failed'); - } -} catch (error) { - console.error('Failed to set token:', error.message); -} -``` - -### Invite User to Application - -```javascript -try { - // Note: Typically requires admin privileges - const response = await base44.auth.inviteUser( - "newuser@example.com", - "user" // or "admin" - ); - - console.log('Invitation sent successfully'); - -} catch (error) { - console.error('Failed to invite user:', error.message); - if (error.status === 403) { - console.error('Insufficient permissions to invite users'); - } else if (error.status === 400) { - console.error('Invalid email or role'); - } -} -``` - -### OTP Verification - -```javascript -try { - // Verify OTP code sent to user's email - await base44.auth.verifyOtp({ - email: "user@example.com", - otpCode: "123456" - }); - - console.log('OTP verified successfully'); - -} catch (error) { - console.error('OTP verification failed:', error.message); - if (error.status === 400) { - console.error('Invalid or expired OTP code'); - } else if (error.status === 429) { - console.error('Too many attempts. Please try again later.'); - } -} - -// Resend OTP if needed -try { - await base44.auth.resendOtp("user@example.com"); - console.log('OTP resent successfully'); - -} catch (error) { - console.error('Failed to resend OTP:', error.message); - if (error.status === 429) { - console.error('Too many requests. Please wait before trying again.'); - } -} -``` - -### Password Reset Flow - -```javascript -// Step 1: Request password reset -try { - await base44.auth.resetPasswordRequest("user@example.com"); - console.log('Password reset email sent. Check your inbox.'); - -} catch (error) { - console.error('Password reset request failed:', error.message); - if (error.status === 429) { - console.error('Too many requests. Please try again later.'); - } - // Note: For security, don't reveal if email exists -} - -// Step 2: Reset password with token from email -try { - await base44.auth.resetPassword({ - resetToken: "token-from-email", - newPassword: "newSecurePassword123" - }); - - console.log('Password reset successfully. You can now log in.'); - -} catch (error) { - console.error('Password reset failed:', error.message); - if (error.status === 400) { - console.error('Invalid or expired reset token'); - } else if (error.status === 422) { - console.error('Password does not meet requirements'); - } -} -``` - -### Change Password - -```javascript -try { - const currentUser = await base44.auth.me(); - - if (!currentUser) { - throw new Error('User must be authenticated to change password'); - } - - await base44.auth.changePassword({ - userId: currentUser.id, - currentPassword: "oldPassword123", - newPassword: "newSecurePassword456" - }); - - console.log('Password changed successfully'); - -} catch (error) { - console.error('Password change failed:', error.message); - if (error.status === 401) { - console.error('Current password is incorrect'); - } else if (error.status === 422) { - console.error('New password does not meet requirements'); - } else if (error.status === 403) { - console.error('Not authorized to change this password'); - } -} -``` - ---- - -## Error Handling - -### Common Error Scenarios - -The auth module can throw various errors. Here are common scenarios and how to handle them: - -#### Authentication Errors (401/403) -```javascript -try { - const user = await base44.auth.me(); -} catch (error) { - if (error.status === 401) { - // Token expired or invalid - navigate to your custom login page - navigate('/login'); - } else if (error.status === 403) { - // Email not verified or insufficient permissions - console.error('Access denied:', error.message); - } -} -``` - -#### Validation Errors (400/422) -```javascript -try { - await base44.auth.register({ - email: "invalid-email", - password: "weak" - }); -} catch (error) { - if (error.status === 400) { - console.error('Invalid input:', error.message); - // Handle validation errors - } else if (error.status === 422) { - console.error('Data validation failed:', error.details); - } -} -``` - -#### Rate Limiting (429) -```javascript -try { - await base44.auth.resendOtp("user@example.com"); -} catch (error) { - if (error.status === 429) { - console.error('Too many requests. Please wait before trying again.'); - // Show countdown or disable button temporarily - } -} -``` - -#### Generic Error Handler -```javascript -function handleAuthError(error) { - switch (error.status) { - case 400: - return 'Invalid input. Please check your data.'; - case 401: - return 'Authentication required. Redirecting to login...'; - case 403: - return 'Access denied. You may need to verify your email.'; - case 404: - return 'Resource not found.'; - case 422: - return 'Validation failed. Please check your input.'; - case 429: - return 'Too many requests. Please try again later.'; - case 500: - return 'Server error. Please try again.'; - default: - return 'An unexpected error occurred.'; - } -} - -// Usage -try { - await base44.auth.loginViaEmailPassword(email, password); -} catch (error) { - console.error(handleAuthError(error)); -} -``` - ---- - -## Auth Providers - -Configure authentication providers in your app dashboard: - -### Available Providers - -**Built-in (All Plans):** -- **Email/Password** - Default, always enabled -- **Google** - OAuth authentication -- **Microsoft** - OAuth authentication -- **Facebook** - OAuth authentication - -**SSO Providers (Elite Plan):** -- **Okta** -- **Azure AD** -- **GitHub** - -### Using OAuth Providers - -- **Google** – enabled by default. -- **Microsoft** – enable in your app's authentication settings before use. -- **Facebook** – enable in your app's authentication settings before use. - -```javascript -// Initiate OAuth login flow -base44.auth.loginWithProvider('google'); - -// Return to specific page after authentication -base44.auth.loginWithProvider('microsoft', '/dashboard'); - -// Supported values: 'google', 'microsoft', 'facebook' -``` - ---- - -## Environment Availability - -| Environment | Availability | Notes | -|------------|--------------|-------| -| **Frontend** | ✅ Yes | All methods available | -| **Backend Functions** | ✅ Yes | Use `createClientFromRequest(req)` for authenticated client | -| **Service Role** | ❌ No | Auth methods not available in service role context | - -### Frontend Usage -```javascript -// Standard browser usage -const user = await base44.auth.me(); -``` - -### Backend Functions Usage -```javascript -import { createClientFromRequest } from "@base44/sdk"; - -Deno.serve(async (req) => { - const base44 = createClientFromRequest(req); - - // Get user from request context - const user = await base44.auth.me(); - - // User operations here... - return Response.json({ user }); -}); -``` - ---- - -## App Visibility - -Control who can access your app in the app settings: - -### Public Apps -- No login required for basic access -- Users can view public content without authentication -- Authenticated users get additional features/data - -### Private Apps -- Login required to access any content -- Unauthenticated users are automatically redirected to login -- All content is protected by default - ---- - -## Limitations - -### Authentication UI Options -- **Recommended:** Build custom login/signup UI using `loginViaEmailPassword()` and `loginWithProvider()` for full control over user experience and branding -- **Alternative:** `redirectToLogin()` uses Base44's hosted authentication pages with limited customization - -### Hosted Login (via redirectToLogin) -- `redirectToLogin()` shows both login and signup options on the same page -- No separate `redirectToSignup()` method -- Users can switch between login/signup on the hosted page -- ⚠️ **Note:** Prefer building custom login UI for better user experience - -### Password Requirements -- Minimum length and complexity requirements enforced -- Requirements not exposed via API -- Validation errors returned when requirements not met - -### Rate Limiting -- OTP requests are rate-limited to prevent abuse -- Password reset requests are rate-limited -- Login attempts may be rate-limited with Turnstile protection - -### Token Management -- JWTs are automatically stored in localStorage by default -- Token expiration and refresh not exposed in API -- Call `me()` or `isAuthenticated()` to verify token validity - ---- - -## Best Practices - -### 1. Always Handle Errors -```javascript -try { - await base44.auth.loginViaEmailPassword(email, password); -} catch (error) { - // Always handle authentication errors - console.error('Login failed:', error.message); -} -``` - -### 2. Verify Authentication Before Protected Actions -```javascript -const user = await requireAuth(); -if (!user) return; // Will redirect to login -// Proceed with authenticated action -``` - -### 3. Use Type Safety with TypeScript -```typescript -import type { User, LoginResponse } from '@base44/sdk'; - -const user: User = await base44.auth.me(); -const response: LoginResponse = await base44.auth.loginViaEmailPassword(email, password); -``` - -### 4. Don't Hardcode Credentials -```javascript -// ❌ BAD -base44.auth.setToken("hardcoded-token-here"); - -// ✅ GOOD -const token = await secureAuthFlow(); -base44.auth.setToken(token); -``` - -### 5. Provide User Feedback -```javascript -try { - await base44.auth.register(params); - showSuccessMessage('Registration successful! Check your email for verification code.'); -} catch (error) { - showErrorMessage('Registration failed: ' + error.message); -} -``` - -### 6. Handle Token Expiration Gracefully -```javascript -try { - const user = await base44.auth.me(); -} catch (error) { - if (error.status === 401) { - // Clear local state and navigate to login - localStorage.clear(); - navigate('/login'); - } -} -``` - -### 7. Build Custom Login UI (Recommended) -```javascript -// ✅ RECOMMENDED - Custom login form with direct methods -const handleLogin = async (email, password) => { - try { - const { user } = await base44.auth.loginViaEmailPassword(email, password); - navigate('/dashboard'); - } catch (error) { - setError(error.message); - } -}; - -const handleGoogleLogin = () => { - base44.auth.loginWithProvider('google', '/dashboard'); -}; - -// ❌ AVOID - Redirecting to hosted login page -// base44.auth.redirectToLogin(window.location.href); -``` diff --git a/plugins/base44/skills/base44-sdk/references/base44-agents.md b/plugins/base44/skills/base44-sdk/references/base44-agents.md deleted file mode 100644 index b919bbf99..000000000 --- a/plugins/base44/skills/base44-sdk/references/base44-agents.md +++ /dev/null @@ -1,386 +0,0 @@ -# Agents Module - -AI agent conversations and messages via `base44.agents`. - -> **Note:** This module requires a logged-in user. All agent methods work in the context of the authenticated user. - -## Contents -- [Concepts](#concepts) -- [Methods](#methods) -- [Examples](#examples) (Create, Get Conversations, List, Subscribe, Send Message, WhatsApp) -- [Message Structure](#message-structure) -- [Conversation Structure](#conversation-structure) -- [Common Patterns](#common-patterns) - -## Concepts - -- **Conversation**: A dialogue between user and an AI agent. Has unique ID, agent name, user reference, and metadata. -- **Message**: Single message in a conversation. Has role (`user`, `assistant`, `system`), content, timestamps, and optional metadata. - -## Methods - -| Method | Signature | Description | -|--------|-----------|-------------| -| `createConversation(params)` | `Promise` | Create a new conversation with an agent | -| `getConversations()` | `Promise` | Get all user's conversations | -| `getConversation(id)` | `Promise` | Get conversation with messages (includes full tool call results) | -| `listConversations(filterParams)` | `Promise` | Filter/sort/paginate conversations | -| `subscribeToConversation(id, onUpdate?)` | `() => void` | Realtime updates via WebSocket; tool call data truncated (returns unsubscribe function) | -| `addMessage(conversation, message)` | `Promise` | Send a message | -| `getWhatsAppConnectURL(agentName)` | `string` | Get WhatsApp connection URL for agent | - -## Examples - -### Create Conversation - -```javascript -const conversation = await base44.agents.createConversation({ - agent_name: "support-agent", - metadata: { - order_id: "ORD-123", - category: "billing" - } -}); - -console.log(conversation.id); -``` - -### Get All Conversations - -```javascript -const conversations = await base44.agents.getConversations(); - -conversations.forEach(conv => { - console.log(conv.id, conv.agent_name, conv.created_date); -}); -``` - -### Get Single Conversation (with messages) - -Returns the complete stored conversation including full tool call results (unlike the realtime subscription, which truncates tool call data). - -```javascript -const conversation = await base44.agents.getConversation("conv-id-123"); - -console.log(conversation.messages); -``` - -### List with Filters - -```javascript -// Using filterParams object with q, sort, limit, skip, fields -const recent = await base44.agents.listConversations({ - q: { agent_name: "support-agent" }, - sort: "-created_date", - limit: 10, - skip: 0 -}); - -// Filter by metadata -const highPriority = await base44.agents.listConversations({ - q: { - agent_name: "support-agent", - "metadata.priority": "high" - }, - sort: "-updated_date", - limit: 20 -}); -``` - -### Subscribe to Updates (Realtime) - -When receiving messages through this subscription, tool call data is truncated for efficiency (`arguments_string` limited to 500 characters, `results` to 50). Use `getConversation()` after the message completes to retrieve full tool call data. - -```javascript -const unsubscribe = base44.agents.subscribeToConversation( - "conv-id-123", - (updatedConversation) => { - // Called when new messages arrive - console.log("New messages:", updatedConversation.messages); - } -); - -// Later: unsubscribe -unsubscribe(); -``` - -### Send a Message - -```javascript -const conversation = await base44.agents.getConversation("conv-id-123"); - -await base44.agents.addMessage(conversation, { - role: "user", - content: "What's the weather like today?" -}); -``` - -### Get WhatsApp Connection URL - -```javascript -const whatsappUrl = base44.agents.getWhatsAppConnectURL("support-agent"); -// Returns URL for users to connect with agent via WhatsApp -console.log(whatsappUrl); -``` - -## Message Structure - -```javascript -{ - role: "user" | "assistant" | "system", - content: "Message text or structured object", - created_date: "2024-01-15T10:30:00Z", - updated_date: "2024-01-15T10:30:00Z", - - // Optional fields - reasoning: { - content: "Agent's reasoning process", - timing: 1500 - }, - tool_calls: [{ - name: "search", - arguments: { query: "weather" }, - result: { ... }, - status: "success" - }], - file_urls: ["https://..."], - usage: { - prompt_tokens: 150, - completion_tokens: 50 - }, - metadata: { ... }, - custom_context: { ... } -} -``` - -## Conversation Structure - -```javascript -{ - id: "conv-id-123", - app_id: "app-id", - agent_name: "support-agent", - created_by_id: "user-id", - created_date: "2024-01-15T10:00:00Z", - updated_date: "2024-01-15T10:30:00Z", - messages: [ ... ], - metadata: { ... } -} -``` - -## Common Patterns - -### Chat Interface - -```javascript -// Load conversation -const conv = await base44.agents.getConversation(conversationId); -setMessages(conv.messages); - -// Subscribe to updates -const unsubscribe = base44.agents.subscribeToConversation(conversationId, (updated) => { - setMessages(updated.messages); -}); - -// Send message -async function sendMessage(text) { - await base44.agents.addMessage(conv, { role: "user", content: text }); -} - -// Cleanup on unmount -return () => unsubscribe(); -``` - -## Type Definitions - -### AgentNameRegistry and AgentName - -**How to get typed agent names:** The Base44 CLI can generate an augmentation of `AgentNameRegistry` from your project. For how to run it, use the **base44-cli** skill. - -```typescript -/** - * Registry of agent names. - * Augment this interface to enable autocomplete for agent names. - * Typically populated by the Base44 CLI type generator. - */ -interface AgentNameRegistry {} - -/** - * Agent name type - uses registry keys if augmented, otherwise string. - */ -type AgentName = keyof AgentNameRegistry extends never ? string : keyof AgentNameRegistry; -``` - -### AgentConversation - -```typescript -/** An agent conversation containing messages exchanged with an AI agent. */ -interface AgentConversation { - /** Unique identifier for the conversation. */ - id: string; - /** Application ID. */ - app_id: string; - /** Name of the agent in this conversation. */ - agent_name: string; - /** ID of the user who created the conversation. */ - created_by_id: string; - /** When the conversation was created. */ - created_date: string; - /** When the conversation was last updated. */ - updated_date: string; - /** Array of messages in the conversation. */ - messages: AgentMessage[]; - /** Optional metadata associated with the conversation. */ - metadata?: Record; -} -``` - -### AgentMessage - -```typescript -/** A message in an agent conversation. */ -interface AgentMessage { - /** Unique identifier for the message. */ - id: string; - /** Role of the message sender. */ - role: "user" | "assistant" | "system"; - /** When the message was created. */ - created_date: string; - /** When the message was last updated. */ - updated_date: string; - /** Message content. */ - content?: string | Record; - /** Optional reasoning information for the message. */ - reasoning?: AgentMessageReasoning | null; - /** URLs to files attached to the message. */ - file_urls?: string[]; - /** Tool calls made by the agent. */ - tool_calls?: AgentMessageToolCall[]; - /** Token usage statistics. */ - usage?: AgentMessageUsage; - /** Whether the message is hidden from the user. */ - hidden?: boolean; - /** Custom context provided with the message. */ - custom_context?: AgentMessageCustomContext[]; - /** Model used to generate the message. */ - model?: string; - /** Checkpoint ID for the message. */ - checkpoint_id?: string; - /** Metadata about when and by whom the message was created. */ - metadata?: AgentMessageMetadata; -} -``` - -### Supporting Types - -```typescript -/** Reasoning information for an agent message. */ -interface AgentMessageReasoning { - /** When reasoning started. */ - start_date: string; - /** When reasoning ended. */ - end_date?: string; - /** Reasoning content. */ - content: string; -} - -/** A tool call made by the agent. */ -interface AgentMessageToolCall { - /** Tool call ID. */ - id: string; - /** Name of the tool called. */ - name: string; - /** Arguments passed to the tool as JSON string. */ - arguments_string: string; - /** Status of the tool call. */ - status: "running" | "success" | "error" | "stopped"; - /** Results from the tool call. */ - results?: string; -} - -/** Token usage statistics for an agent message. */ -interface AgentMessageUsage { - /** Number of tokens in the prompt. */ - prompt_tokens?: number; - /** Number of tokens in the completion. */ - completion_tokens?: number; -} - -/** Custom context provided with an agent message. */ -interface AgentMessageCustomContext { - /** Context message. */ - message: string; - /** Associated data for the context. */ - data: Record; - /** Type of context. */ - type: string; -} - -/** Metadata about when and by whom a message was created. */ -interface AgentMessageMetadata { - /** When the message was created. */ - created_date: string; - /** Email of the user who created the message. */ - created_by_email: string; - /** Full name of the user who created the message. */ - created_by_full_name: string; -} -``` - -### CreateConversationParams - -```typescript -/** Parameters for creating a new conversation. */ -interface CreateConversationParams { - /** The name of the agent to create a conversation with. */ - agent_name: AgentName; - /** Optional metadata to attach to the conversation. */ - metadata?: Record; -} -``` - -### ModelFilterParams - -```typescript -/** Parameters for filtering, sorting, and paginating conversations. */ -interface ModelFilterParams { - /** Query object with field-value pairs for filtering. */ - q?: Record; - /** Sort parameter (e.g., "-created_date" for descending). */ - sort?: string | null; - /** Maximum number of results to return. */ - limit?: number | null; - /** Number of results to skip for pagination. */ - skip?: number | null; - /** Array of field names to include in the response. */ - fields?: string[] | null; -} -``` - -### AgentsModule - -```typescript -/** Agents module for managing AI agent conversations. */ -interface AgentsModule { - /** Gets all conversations from all agents in the app. */ - getConversations(): Promise; - - /** Gets a specific conversation by ID. Returns complete stored conversation including full tool call results. */ - getConversation(conversationId: string): Promise; - - /** Lists conversations with filtering, sorting, and pagination. */ - listConversations(filterParams: ModelFilterParams): Promise; - - /** Creates a new conversation with an agent. */ - createConversation(conversation: CreateConversationParams): Promise; - - /** Adds a message to a conversation. */ - addMessage(conversation: AgentConversation, message: Partial): Promise; - - /** Subscribes to realtime updates for a conversation. Returns unsubscribe function. */ - subscribeToConversation(conversationId: string, onUpdate?: (conversation: AgentConversation) => void): () => void; - - /** Gets WhatsApp connection URL for an agent. */ - getWhatsAppConnectURL(agentName: AgentName): string; -} -``` diff --git a/plugins/base44/skills/base44-sdk/references/client.md b/plugins/base44/skills/base44-sdk/references/client.md deleted file mode 100644 index 27cd62392..000000000 --- a/plugins/base44/skills/base44-sdk/references/client.md +++ /dev/null @@ -1,265 +0,0 @@ -# Client Setup - -How to create and configure the Base44 client. - -## Contents -- [In Base44-Generated Apps](#in-base44-generated-apps) -- [In External Apps](#in-external-apps) -- [In Backend Functions](#in-backend-functions) -- [Authentication Modes](#authentication-modes) (Anonymous, User, Service Role) -- [Available Modules](#available-modules) -- [Client Methods](#client-methods) -- [Client Configuration Options](#client-configuration-options) - -## In Base44-Generated Apps - -Inside a Base44 app, the client is automatically created and configured. Import it from `@/api/base44Client` and use it as `base44`: - -```javascript -const tasks = await base44.entities.Task.list(); -``` - -## In External Apps - -When using Base44 as a backend from an external app, install the SDK and create a client by calling `createClient()` directly: - -```bash -npm install @base44/sdk -``` - -```javascript -import { createClient } from "@base44/sdk"; - -// IMPORTANT: The parameter name is 'appId' (NOT 'clientId', NOT 'id') -// IMPORTANT: onError must be nested inside 'options' object -const base44 = createClient({ - appId: "your-app-id", // Required: Use 'appId' parameter - token: "optional-user-token", // Optional: for pre-authenticated requests - options: { // Optional: configuration options - onError: (error) => { // Optional: error handler (must be in options) - console.error("Base44 error:", error); - } - } -}); -``` - -**Common Mistakes:** -- ❌ `createClient({ clientId: "..." })` - WRONG parameter name -- ❌ `createClient({ id: "..." })` - WRONG parameter name -- ❌ `createClient({ appId: "...", onError: ... })` - WRONG: onError must be in options -- ✅ `createClient({ appId: "..." })` - CORRECT parameter name -- ✅ `createClient({ appId: "...", options: { onError: ... } })` - CORRECT: onError in options - -## In Backend Functions - -`createClientFromRequest()` is designed for Base44-hosted backend functions. It extracts auth from request headers that Base44 injects and returns a client that includes service role access (`base44.asServiceRole`). For frontends and external backends, use `createClient()` instead. - -```javascript -import { createClientFromRequest } from "@base44/sdk"; - -Deno.serve(async (req) => { - const base44 = createClientFromRequest(req); - - // Client inherits authentication from the request - const user = await base44.auth.me(); - - return Response.json({ user }); -}); -``` - -## Authentication Modes - -| Mode | How to Get | Permissions | -|------|-----------|-------------| -| **Anonymous** | `createClient({ appId })` without token | Public data only | -| **User** | After `loginViaEmailPassword()` or via `createClientFromRequest` | User's own data | -| **Service Role** | `base44.asServiceRole.*` in backend | Full admin access | - -## Anonymous Mode - -No authentication. Can only access public resources. - -```javascript -const base44 = createClient({ appId: "your-app-id" }); - -// Only works if Task entity allows anonymous read -const publicTasks = await base44.entities.Task.list(); -``` - -## User Mode - -After user logs in, the client automatically includes their token. - -```javascript -const base44 = createClient({ appId: "your-app-id" }); - -// Login sets the token -await base44.auth.loginViaEmailPassword("user@example.com", "password"); - -// Subsequent requests are authenticated -const user = await base44.auth.me(); -const myTasks = await base44.entities.Task.list(); // filtered by permissions -``` - -## Service Role Mode - -Admin-level access. **Backend only.** - -```javascript -// Inside a backend function -Deno.serve(async (req) => { - const base44 = createClientFromRequest(req); - - // User mode - respects permissions - const myTasks = await base44.entities.Task.list(); - - // Service role - bypasses permissions - const allTasks = await base44.asServiceRole.entities.Task.list(); - const allUsers = await base44.asServiceRole.entities.User.list(); - const oauthToken = await base44.asServiceRole.connectors.getAccessToken("slack"); - - return Response.json({ myTasks, allTasks }); -}); -``` - -## Available Modules - -The client exposes these modules: - -```javascript -base44.agents // AI conversations -base44.analytics // Event tracking -base44.appLogs // App usage logging -base44.auth // Authentication -base44.connectors // Per-user OAuth flows (UserConnectorsModule) -base44.entities // CRUD operations -base44.functions // Backend function invocation -base44.integrations // Third-party services -base44.users // User invitations - -// Service role only (backend) -base44.asServiceRole.agents -base44.asServiceRole.appLogs -base44.asServiceRole.connectors // App-scoped OAuth tokens (ConnectorsModule) -base44.asServiceRole.entities -base44.asServiceRole.functions -base44.asServiceRole.integrations -base44.asServiceRole.sso // SSO token generation -``` - -## Client Methods - -The client provides these methods: - -```javascript -// Set authentication token for all subsequent requests -base44.setToken(newToken); - -// Cleanup WebSocket connections (call when done with client) -base44.cleanup(); -``` - -### setToken - -Updates the authentication token for all subsequent API requests and WebSocket connections. - -```javascript -// After receiving a token (e.g., from external auth) -base44.setToken("new-jwt-token"); -``` - -### cleanup - -Disconnects WebSocket connections. Call when you're done with the client or when the component unmounts. - -```javascript -// Cleanup on component unmount (React example) -useEffect(() => { - return () => base44.cleanup(); -}, []); -``` - -## Client Configuration Options - -```javascript -createClient({ - appId: "your-app-id", // Required: MUST use 'appId' (not 'clientId' or 'id') - token: "jwt-token", // Optional: pre-set auth token - options: { // Optional: configuration options - onError: (error) => {} // Optional: global error handler (must be in options) - } -}); -``` - -**⚠️ Critical:** -- The parameter name is `appId`, not `clientId` or `id`. Using the wrong parameter name will cause errors. -- The `onError` handler must be nested inside the `options` object, not at the top level. - -## Type Definitions - -### CreateClientConfig - -```typescript -/** Configuration for creating a Base44 client. */ -interface CreateClientConfig { - /** The Base44 app ID (required). */ - appId: string; - /** User authentication token. Used to authenticate as a specific user. */ - token?: string; - /** @internal Service role token; only set automatically in Base44-hosted backend functions. */ - serviceToken?: string; - /** Additional client options. */ - options?: CreateClientOptions; -} - -/** Options for creating a Base44 client. */ -interface CreateClientOptions { - /** Optional error handler called whenever an API error occurs. */ - onError?: (error: Error) => void; -} -``` - -### Base44Client - -```typescript -/** The Base44 client instance. */ -interface Base44Client { - /** Agents module for managing AI agent conversations. */ - agents: AgentsModule; - /** Analytics module for tracking custom events. */ - analytics: AnalyticsModule; - /** App logs module for tracking app usage. */ - appLogs: AppLogsModule; - /** Auth module for user authentication and management. */ - auth: AuthModule; - /** Entities module for CRUD operations on your data models. */ - entities: EntitiesModule; - /** Functions module for invoking custom backend functions. */ - functions: FunctionsModule; - /** Integrations module for calling pre-built integration methods. */ - integrations: IntegrationsModule; - - /** Cleanup function to disconnect WebSocket connections. */ - cleanup(): void; - - /** Sets a new authentication token for all subsequent requests. */ - setToken(newToken: string): void; - - /** Per-user OAuth flows. Each end user has their own connection. */ - connectors: UserConnectorsModule; - - /** Provides access to modules with elevated service role permissions (backend only). */ - readonly asServiceRole: { - agents: AgentsModule; - appLogs: AppLogsModule; - /** App-scoped OAuth tokens. All users share the same connected account. */ - connectors: ConnectorsModule; - entities: EntitiesModule; - functions: FunctionsModule; - integrations: IntegrationsModule; - /** SSO token generation for users. */ - sso: SsoModule; - cleanup(): void; - }; -} -``` diff --git a/plugins/base44/skills/base44-sdk/references/connectors.md b/plugins/base44/skills/base44-sdk/references/connectors.md deleted file mode 100644 index ca906645f..000000000 --- a/plugins/base44/skills/base44-sdk/references/connectors.md +++ /dev/null @@ -1,157 +0,0 @@ -# Connectors Module - -OAuth token management for external services. - -- **`base44.asServiceRole.connectors`** — App-scoped OAuth tokens (backend/service role only). All users share the same connected account. - -## Contents -- [Service Role Connectors (`base44.asServiceRole.connectors`)](#service-role-connectors-base44asserviceroleconnectors) -- [Available Services](#available-services) -- [Type Definitions](#type-definitions) - ---- - -## Service Role Connectors (`base44.asServiceRole.connectors`) - -App-scoped OAuth tokens. The app builder connects the account once; all users share it. **Backend/service role only.** - -### Methods - -| Method | Signature | Description | -|--------|-----------|-------------| -| `getConnection(integrationType)` | `Promise` | Get access token and optional connection config | -| `getAccessToken(integrationType)` | `Promise` | ⚠️ **Deprecated** — use `getConnection()` instead | - -### Examples - -```javascript -// Backend function only -Deno.serve(async (req) => { - const base44 = createClientFromRequest(req); - - // Recommended: use getConnection() for token + optional config - const { accessToken, connectionConfig } = await base44.asServiceRole.connectors.getConnection("slack"); - - const response = await fetch("https://slack.com/api/chat.postMessage", { - method: "POST", - headers: { - "Authorization": `Bearer ${accessToken}`, - "Content-Type": "application/json" - }, - body: JSON.stringify({ channel: "#general", text: "Hello from Base44!" }) - }); - - return Response.json(await response.json()); -}); -``` - -```javascript -// Using connectionConfig (for services that need extra params, e.g. a subdomain) -const { accessToken, connectionConfig } = await base44.asServiceRole.connectors.getConnection("myservice"); -const subdomain = connectionConfig?.subdomain; -const response = await fetch(`https://${subdomain}.example.com/api/v1/data`, { - headers: { "Authorization": `Bearer ${accessToken}` } -}); -``` - -```javascript -// Google Calendar example -const { accessToken } = await base44.asServiceRole.connectors.getConnection("googlecalendar"); - -const events = await fetch( - "https://www.googleapis.com/calendar/v3/calendars/primary/events?" + - new URLSearchParams({ maxResults: "10", orderBy: "startTime", singleEvents: "true", timeMin: new Date().toISOString() }), - { headers: { "Authorization": `Bearer ${accessToken}` } } -).then(r => r.json()); -``` - ---- - -## Available Services - -| Service | Type identifier | -|---------|----------------| -| Airtable | `airtable` | -| Box | `box` | -| ClickUp | `clickup` | -| Discord | `discord` | -| Dropbox | `dropbox` | -| GitHub | `github` | -| Gmail | `gmail` | -| Google Analytics | `google_analytics` | -| Google BigQuery | `googlebigquery` | -| Google Calendar | `googlecalendar` | -| Google Classroom | `google_classroom` | -| Google Docs | `googledocs` | -| Google Drive | `googledrive` | -| Google Search Console | `google_search_console` | -| Google Sheets | `googlesheets` | -| Google Slides | `googleslides` | -| HubSpot | `hubspot` | -| Linear | `linear` | -| LinkedIn | `linkedin` | -| Microsoft Teams | `microsoft_teams` | -| Microsoft OneDrive | `one_drive` | -| Notion | `notion` | -| Outlook | `outlook` | -| Salesforce | `salesforce` | -| SharePoint | `share_point` | -| Slack User | `slack` | -| Slack Bot | `slackbot` | -| Splitwise | `splitwise` | -| TikTok | `tiktok` | -| Typeform | `typeform` | -| Wix | `wix` | -| Wrike | `wrike` | - -Run `npx base44 connectors list-available` from the CLI to see all available types. - ---- - -## Setup Requirements - -1. **Builder plan** or higher -2. **Backend functions** enabled (for service role connectors) -3. **Connector configured** in Base44 dashboard (OAuth flow completed) - -## Important Notes - -- **Service role connectors**: One account per connector per app — all users share the same connected account -- **You handle the API calls**: Base44 provides the token; you make the actual API requests -- **Token refresh**: Base44 handles token refresh automatically - ---- - -## Type Definitions - -```typescript -/** - * The type of external integration/connector (for service role connectors). - * Examples: 'googlecalendar', 'slack', 'github', 'notion', etc. - */ -type ConnectorIntegrationType = string; - -/** Connection details returned by getConnection(). */ -interface ConnectorConnectionResponse { - /** The OAuth access token for the external service. */ - accessToken: string; - /** Key-value configuration for the connection, or null if not needed. */ - connectionConfig: Record | null; -} - -/** Service role connectors module (app-scoped OAuth). Backend only. */ -interface ConnectorsModule { - /** - * Retrieves the OAuth access token and optional connection config. - * @param integrationType - e.g., 'googlecalendar', 'slack', 'github'. - */ - getConnection(integrationType: ConnectorIntegrationType): Promise; - - /** - * @deprecated Use getConnection() instead. - * Retrieves only the OAuth access token string. - */ - getAccessToken(integrationType: ConnectorIntegrationType): Promise; -} - -``` diff --git a/plugins/base44/skills/base44-sdk/references/entities.md b/plugins/base44/skills/base44-sdk/references/entities.md deleted file mode 100644 index 8a2f51c93..000000000 --- a/plugins/base44/skills/base44-sdk/references/entities.md +++ /dev/null @@ -1,399 +0,0 @@ -# Entities Module - -CRUD operations on data models. Access via `base44.entities.EntityName.method()`. - -## Contents -- [Methods](#methods) -- [Examples](#examples) (Create, Bulk Create, List, Filter, Get, Update, Delete, Subscribe) -- [User Entity](#user-entity) -- [Service Role Access](#service-role-access) -- [Permissions](#permissions) - -## Methods - -**Note:** The maximum limit for `list()` and `filter()` is 5,000 items per request. - -| Method | Signature | Description | -|--------|-----------|-------------| -| `create(data)` | `Promise` | Create one record | -| `bulkCreate(dataArray)` | `Promise` | Create multiple records | -| `list(sort?, limit?, skip?, fields?)` | `Promise[]>` | Get all records (paginated) | -| `filter(query, sort?, limit?, skip?, fields?)` | `Promise[]>` | Get records matching conditions | -| `get(id)` | `Promise` | Get single record by ID | -| `update(id, data)` | `Promise` | Update record (partial update) | -| `updateMany(query, data)` | `Promise` | Update all matching records using MongoDB update operators | -| `bulkUpdate(dataArray)` | `Promise` | Update multiple records by ID, each with its own data | -| `delete(id)` | `Promise` | Delete record by ID | -| `deleteMany(query)` | `Promise` | Delete all matching records | -| `importEntities(file)` | `Promise>` | Import from CSV (frontend only) | -| `subscribe(callback)` | `() => void` | Subscribe to realtime updates (returns unsubscribe function) | - -## Examples - -### Create - -```javascript -const task = await base44.entities.Task.create({ - title: "Complete documentation", - status: "pending", - dueDate: "2024-12-31" -}); -``` - -### Bulk Create - -```javascript -const tasks = await base44.entities.Task.bulkCreate([ - { title: "Task 1", status: "pending" }, - { title: "Task 2", status: "pending" } -]); -``` - -### List with Pagination - -```javascript -// Get first 10 records, sorted by created_date descending (max 5,000 per request) -const tasks = await base44.entities.Task.list( - "-created_date", // sort (SortField: prefix with - for descending) - 10, // limit - 0 // skip -); - -// Get next page -const page2 = await base44.entities.Task.list("-created_date", 10, 10); -``` - -### Filter - -```javascript -// Simple filter -const pending = await base44.entities.Task.filter({ status: "pending" }); - -// Multiple conditions -const myPending = await base44.entities.Task.filter({ - status: "pending", - assignedTo: userId -}); - -// With sort, limit, skip (max 5,000 per request) -const recent = await base44.entities.Task.filter( - { status: "pending" }, - "-created_date", // sort (SortField: prefix with - for descending) - 5, - 0 -); - -// Select specific fields -const titles = await base44.entities.Task.filter( - { status: "pending" }, - null, - null, - null, - ["id", "title"] -); -``` - -### Get by ID - -```javascript -const task = await base44.entities.Task.get("task-id-123"); -``` - -### Update - -```javascript -// Partial update - only specified fields change -await base44.entities.Task.update("task-id-123", { - status: "completed", - completedAt: new Date().toISOString() -}); -``` - -### Delete - -```javascript -// Single record -const result = await base44.entities.Task.delete("task-id-123"); -console.log("Deleted:", result.success); - -// Multiple records matching query -const manyResult = await base44.entities.Task.deleteMany({ status: "archived" }); -console.log("Deleted:", manyResult.deleted); -``` - -### Update Many (MongoDB-style) - -```javascript -// Update all pending tasks to status "in-progress" -const result = await base44.entities.Task.updateMany( - { status: "pending" }, // query: which records to update - { $set: { status: "in-progress" } } // MongoDB update operator -); -console.log("Updated:", result.updated); - -// Increment a counter field -await base44.entities.Task.updateMany( - { category: "bugs" }, - { $inc: { priority: 1 } } -); -``` - -### Bulk Update (by ID) - -```javascript -// Update multiple records, each with different data -const updated = await base44.entities.Task.bulkUpdate([ - { id: "task-1", status: "done", completedAt: new Date().toISOString() }, - { id: "task-2", status: "in-progress", assignedTo: userId }, - { id: "task-3", priority: 5 } -]); -``` - -### Import from File - -```javascript -// Frontend only: import from CSV/file -const result = await base44.entities.Task.importEntities(file); -if (result.status === "success" && result.output) { - console.log(`Imported ${result.output.length} records`); -} else { - console.error(result.details); -} -``` - -### Subscribe to Realtime Updates - -```javascript -// Subscribe to all changes on Task entity -const unsubscribe = base44.entities.Task.subscribe((event) => { - console.log(`Task ${event.id} was ${event.type}:`, event.data); - // event.type is "create", "update", or "delete" -}); - -// Later: unsubscribe to stop receiving updates -unsubscribe(); -``` - -**Event structure:** -```javascript -{ - type: "create" | "update" | "delete", - data: { ... }, // the entity data - id: "entity-id", // the affected entity's ID - timestamp: "2024-01-15T10:30:00Z" -} -``` - -## User Entity - -Every app has a built-in `User` entity with special rules: - -- Regular users can only read/update **their own** record -- Cannot create users via `entities.create()` - use `auth.register()` instead -- Service role has full access to all user records - -```javascript -// Get current user's record -const me = await base44.entities.User.get(currentUserId); - -// Service role: get any user -const anyUser = await base44.asServiceRole.entities.User.get(userId); -``` - -## Service Role Access - -For admin-level operations (bypass user permissions): - -```javascript -// Backend only -const allTasks = await base44.asServiceRole.entities.Task.list(); -const allUsers = await base44.asServiceRole.entities.User.list(); -``` - -## Permissions (RLS & FLS) - -Data access is controlled by **Row Level Security (RLS)** and **Field Level Security (FLS)** rules defined in entity schemas. - -1. **Authentication level**: anonymous, authenticated, or service role -2. **RLS rules**: Control which records (rows) users can create/read/update/delete -3. **FLS rules**: Control which fields users can read/write within accessible records - -Operations succeed or fail based on these rules - no partial results. - -RLS and FLS are configured in entity schema files (`base44/entities/*.jsonc`). See [entities-create.md](../../base44-cli/references/entities-create.md#row-level-security-rls) for configuration details. - -**Note:** `asServiceRole` sets the user's role to `"admin"` but does NOT bypass RLS. Your RLS rules must include admin access (e.g., `{ "user_condition": { "role": "admin" } }`) for service role operations to succeed. - -## Type Definitions - -### RealtimeEvent - -```typescript -/** Event types for realtime entity updates. */ -type RealtimeEventType = "create" | "update" | "delete"; - -/** Payload received when a realtime event occurs. */ -interface RealtimeEvent { - /** The type of change that occurred. */ - type: RealtimeEventType; - /** The entity data. */ - data: T; - /** The unique identifier of the affected entity. */ - id: string; - /** ISO 8601 timestamp of when the event occurred. */ - timestamp: string; -} - -/** Callback function invoked when a realtime event occurs. */ -type RealtimeCallback = (event: RealtimeEvent) => void; -``` - -### Result Types - -```typescript -/** Result returned when updating multiple entities. */ -interface UpdateManyResult { - /** Whether the update was successful. */ - success: boolean; - /** Number of entities that were updated. */ - updated: number; -} - -/** Result returned when deleting a single entity. */ -interface DeleteResult { - /** Whether the deletion was successful. */ - success: boolean; -} - -/** Result returned when deleting multiple entities. */ -interface DeleteManyResult { - /** Whether the deletion was successful. */ - success: boolean; - /** Number of entities that were deleted. */ - deleted: number; -} - -/** Result returned when importing entities from a file. */ -interface ImportResult { - /** Status of the import operation. */ - status: "success" | "error"; - /** Details message, e.g., "Successfully imported 3 entities with RLS enforcement". */ - details: string | null; - /** Array of created entity objects when successful, or null on error. */ - output: T[] | null; -} -``` - -### SortField and Server Fields - -```typescript -/** - * Sort field type for entity queries. - * Supports ascending (no prefix or '+') and descending ('-') sorting. - * Example: 'created_date', '+created_date', '-created_date' - */ -type SortField = (keyof T & string) | `+${keyof T & string}` | `-${keyof T & string}`; - -/** Fields added by the server to every entity record. */ -interface ServerEntityFields { - id: string; - created_date: string; - updated_date: string; - created_by?: string | null; - created_by_id?: string | null; - is_sample?: boolean; -} -``` - -### Type Registry (for typed entities) - -**How to get typed entities:** The Base44 CLI can generate entity interfaces and an augmentation of `EntityTypeRegistry` from your project. For how to run it, use the **base44-cli** skill. - -```typescript -/** - * Registry mapping entity names to their TypeScript types. - * Augment this interface with your entity schema (user-defined fields only). - * Typically populated by the Base44 CLI type generator. - */ -interface EntityTypeRegistry {} - -/** - * Full record type for each entity: schema fields + server-injected fields. - */ -type EntityRecord = { - [K in keyof EntityTypeRegistry]: EntityTypeRegistry[K] & ServerEntityFields; -}; -``` - -### EntityHandler - -```typescript -/** Entity handler providing CRUD operations for a specific entity type. */ -interface EntityHandler { - /** Lists records with optional pagination and sorting. Max 5,000 per request. */ - list( - sort?: SortField, - limit?: number, - skip?: number, - fields?: K[] - ): Promise[]>; - - /** Filters records based on a query. Max 5,000 per request. */ - filter( - query: Partial, - sort?: SortField, - limit?: number, - skip?: number, - fields?: K[] - ): Promise[]>; - - /** Gets a single record by ID. */ - get(id: string): Promise; - - /** Creates a new record. */ - create(data: Partial): Promise; - - /** Updates an existing record. */ - update(id: string, data: Partial): Promise; - - /** Deletes a single record by ID. */ - delete(id: string): Promise; - - /** Deletes multiple records matching a query. */ - deleteMany(query: Partial): Promise; - - /** Creates multiple records in a single request. */ - bulkCreate(data: Partial[]): Promise; - - /** - * Updates multiple records matching a query using MongoDB update operators. - * @param query - Filter to select which records to update. - * @param data - MongoDB update operator object (e.g., `{ $set: { field: value } }`). - */ - updateMany(query: Partial, data: Record>): Promise; - - /** Updates multiple records by ID, each with its own update data. */ - bulkUpdate(data: (Partial & { id: string })[]): Promise; - - /** Imports records from a file (frontend only). */ - importEntities(file: File): Promise>; - - /** Subscribes to realtime updates. Returns unsubscribe function. */ - subscribe(callback: RealtimeCallback): () => void; -} -``` - -### EntitiesModule - -```typescript -/** Entities module: typed registry keys get typed handlers; dynamic access remains untyped. */ -type EntitiesModule = TypedEntitiesModule & DynamicEntitiesModule; - -type TypedEntitiesModule = { - [K in keyof EntityTypeRegistry]: EntityHandler; -}; - -type DynamicEntitiesModule = { - [entityName: string]: EntityHandler; -}; -``` diff --git a/plugins/base44/skills/base44-sdk/references/functions.md b/plugins/base44/skills/base44-sdk/references/functions.md deleted file mode 100644 index 54ac09dbf..000000000 --- a/plugins/base44/skills/base44-sdk/references/functions.md +++ /dev/null @@ -1,291 +0,0 @@ -# Functions Module - -Invoke custom backend functions via `base44.functions`. - -## Contents -- [Method](#method) -- [Invoking Functions](#invoking-functions) (Frontend, File Upload, Service Role, REST API) -- [Writing Backend Functions](#writing-backend-functions) (Basic, Service Role, Secrets, Errors) -- [Setup Requirements](#setup-requirements) -- [Authentication Modes](#authentication-modes) - -## Methods - -### `invoke` - -```javascript -base44.functions.invoke(functionName, data?): Promise -``` - -- `functionName`: Name of the backend function -- `data`: Optional object of parameters (sent as JSON, or multipart if contains File objects) -- Returns: Whatever the function returns - -### `fetch` - -```javascript -base44.functions.fetch(path, init?): Promise -``` - -Low-level method that performs a direct HTTP request to a backend function path and returns the native `Response` object. Use when you need streaming responses, custom HTTP methods, or raw response access. - -- `path`: Function path (e.g., `/streaming_demo` or `/my-function/endpoint`) -- `init`: Optional native fetch options (`RequestInit`) -- Returns: Native `Response` object - -## Invoking Functions - -### From Frontend - -```javascript -const result = await base44.functions.invoke("processOrder", { - orderId: "order-123", - action: "ship" -}); - -console.log(result); -``` - -### Streaming Response (using fetch) - -```javascript -// Use fetch() for streaming responses (SSE, chunked text, etc.) -const response = await base44.functions.fetch("/stream-data", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ prompt: "Tell me a story" }) -}); - -// Read as a stream -const reader = response.body.getReader(); -const decoder = new TextDecoder(); -while (true) { - const { done, value } = await reader.read(); - if (done) break; - console.log(decoder.decode(value)); -} -``` - -### Custom HTTP Methods (using fetch) - -```javascript -// PUT, PATCH, DELETE, or other methods -const response = await base44.functions.fetch("/my-resource/123", { - method: "DELETE" -}); -console.log(response.status); // 204 -``` - -### With File Upload - -```javascript -const fileInput = document.querySelector('input[type="file"]'); -const file = fileInput.files[0]; - -// Automatically uses multipart/form-data when File objects present -const result = await base44.functions.invoke("uploadDocument", { - file: file, - category: "invoices" -}); -``` - -### With Service Role (Backend) - -```javascript -// Inside another backend function -const result = await base44.asServiceRole.functions.invoke("adminTask", { - userId: "user-123" -}); -``` - -### Via REST API (curl) - -Functions can be called via HTTP POST to your app domain: - -```bash -curl -X POST "https:///functions/" \ - -H "Content-Type: application/json" \ - -d '{"key": "value"}' -``` - -## Writing Backend Functions - -Backend functions run on Deno. Must export using `Deno.serve()`. - -### Required Directory Structure - -Each function must be in its own subdirectory under `base44/functions/` with a configuration file: - -``` -base44/ - functions/ - process-order/ # kebab-case directory name - function.jsonc # required configuration - index.ts # entry point -``` - -**function.jsonc:** -```jsonc -{ - "name": "process-order", - "entry": "index.ts" -} -``` - -For complete setup and deployment instructions, see [functions-create.md](../../base44-cli/references/functions-create.md) in base44-cli. - -### Basic Structure - -```javascript -// base44/functions/process-order/index.ts -import { createClientFromRequest } from "npm:@base44/sdk"; - -Deno.serve(async (req) => { - // Get authenticated client from request - const base44 = createClientFromRequest(req); - - // Parse input - const { orderId, action } = await req.json(); - - // Your logic here - const order = await base44.entities.Orders.get(orderId); - - // Return response - return Response.json({ - success: true, - order: order - }); -}); -``` - -### With Service Role Access - -```javascript -import { createClientFromRequest } from "npm:@base44/sdk"; - -Deno.serve(async (req) => { - const base44 = createClientFromRequest(req); - - // Check user is authenticated - const user = await base44.auth.me(); - if (!user) { - return Response.json({ error: "Unauthorized" }, { status: 401 }); - } - - // Use service role for admin operations - const allOrders = await base44.asServiceRole.entities.Orders.list(); - - return Response.json({ orders: allOrders }); -}); -``` - -### Using Secrets - -```javascript -Deno.serve(async (req) => { - // Access environment variables (configured in app settings) - const apiKey = Deno.env.get("STRIPE_API_KEY"); - - const response = await fetch("https://api.stripe.com/v1/charges", { - headers: { - "Authorization": `Bearer ${apiKey}` - } - }); - - return Response.json(await response.json()); -}); -``` - -### Error Handling - -```javascript -import { createClientFromRequest } from "npm:@base44/sdk"; - -Deno.serve(async (req) => { - try { - const base44 = createClientFromRequest(req); - const { orderId } = await req.json(); - - const order = await base44.entities.Orders.get(orderId); - if (!order) { - return Response.json( - { error: "Order not found" }, - { status: 404 } - ); - } - - return Response.json({ order }); - - } catch (error) { - return Response.json( - { error: error.message }, - { status: 500 } - ); - } -}); -``` - -## Setup Requirements - -1. Enable Backend Functions in app settings (requires appropriate plan) -2. Create function files in `/functions` folder -3. Configure secrets via app dashboard for API keys - -## Authentication Modes - -| Mode | Context | Permissions | -|------|---------|-------------| -| User | `base44.functions.invoke()` | Runs under calling user's permissions | -| Service Role | `base44.asServiceRole.functions.invoke()` | Admin-level access | - -Inside the function, use `createClientFromRequest(req)` to get a client that inherits the caller's auth context. - -## Type Definitions - -**How to get typed function names:** The Base44 CLI can generate an augmentation of `FunctionNameRegistry` from your project. For how to run it, use the **base44-cli** skill. - -```typescript -/** - * Registry of function names. - * Augment this interface to enable autocomplete for function names. - * Typically populated by the Base44 CLI type generator. - */ -interface FunctionNameRegistry {} - -/** - * Function name type - uses registry keys if augmented, otherwise string. - */ -type FunctionName = keyof FunctionNameRegistry extends never ? string : keyof FunctionNameRegistry; - -/** - * Options for functions.fetch(). Uses native fetch options directly. - */ -type FunctionsFetchInit = RequestInit; - -/** Functions module for invoking custom backend functions. */ -interface FunctionsModule { - /** - * Invokes a custom backend function by name. - * - * If any parameter is a File object, the request will automatically be - * sent as multipart/form-data. Otherwise, it will be sent as JSON. - * - * @param functionName - The name of the function to invoke. - * @param data - Optional object containing named parameters for the function. - * @returns Promise resolving to the function's response. - */ - invoke(functionName: FunctionName, data?: Record): Promise; - - /** - * Performs a direct HTTP request to a backend function path and returns the native Response. - * - * Use for streaming responses (SSE, chunked text), custom HTTP methods, - * or when you need raw access to the response. - * - * @param path - Function path, e.g. `/streaming_demo` or `/my-function/endpoint` - * @param init - Optional native fetch options. - * @returns Promise resolving to a native fetch Response. - */ - fetch(path: string, init?: FunctionsFetchInit): Promise; -} -``` diff --git a/plugins/base44/skills/base44-sdk/references/integrations.md b/plugins/base44/skills/base44-sdk/references/integrations.md deleted file mode 100644 index 9a9d55269..000000000 --- a/plugins/base44/skills/base44-sdk/references/integrations.md +++ /dev/null @@ -1,375 +0,0 @@ -# Integrations Module - -Access third-party services via `base44.integrations`. - -## Types of Integrations - -1. **Core/Built-in**: AI, email, file uploads (available by default) -2. **Catalog integrations**: Pre-built connectors from Base44 catalog -3. **Custom integrations**: Your own OpenAPI-based integrations - -## Accessing Integrations - -```javascript -// Core integrations -base44.integrations.Core.FunctionName(params) - -// Custom integrations -base44.integrations.custom.call(slug, operationId, params) -``` - -## Core Integrations - -### InvokeLLM (AI Text Generation) - -Generate text or structured JSON data using AI models. - -```javascript -// Basic prompt - returns string -const response = await base44.integrations.Core.InvokeLLM({ - prompt: "Summarize this text: ..." -}); - -// With internet context (uses Google Search, Maps, News) -const response = await base44.integrations.Core.InvokeLLM({ - prompt: "What's the current weather in NYC?", - add_context_from_internet: true -}); - -// Structured JSON response - returns object -const response = await base44.integrations.Core.InvokeLLM({ - prompt: "Analyze the sentiment of: 'Great product but slow shipping'", - response_json_schema: { - type: "object", - properties: { - sentiment: { type: "string", enum: ["positive", "negative", "mixed"] }, - score: { type: "number", description: "Score from 1-10" }, - key_points: { type: "array", items: { type: "string" } } - } - } -}); -// Returns: { sentiment: "mixed", score: 7, key_points: ["great product", "slow shipping"] } - -// With file attachments (uploaded via UploadFile) -const response = await base44.integrations.Core.InvokeLLM({ - prompt: "Describe what's in this image", - file_urls: ["https://...uploaded_image.png"] -}); -``` - -**Parameters:** -- `prompt` (string, required): The prompt text to send to the model -- `add_context_from_internet` (boolean, optional): If true, uses Google Search/Maps/News for real-time context -- `response_json_schema` (object, optional): JSON schema for structured output -- `file_urls` (string[], optional): URLs of uploaded files for context - -### GenerateImage - -Create AI-generated images from text prompts. - -```javascript -const { url } = await base44.integrations.Core.GenerateImage({ - prompt: "A serene mountain landscape with a lake" -}); -console.log(url); // https://...generated_image.png -``` - -### SendEmail - -Send emails to registered users. Every app gets this integration (no plan upgrade required). - -```javascript -await base44.integrations.Core.SendEmail({ - to: "user@example.com", - subject: "Welcome!", - body: "

Hello

Welcome to our app.

", - from_name: "My App" // optional, defaults to app name -}); -``` - -**Parameters:** -- `to` (string, required): Recipient email address -- `subject` (string, required): Email subject line -- `body` (string, required): Plain text or HTML email body -- `from_name` (string, optional): Sender name displayed to recipient - -**Limitations:** -- 1 credit per email (2 credits with custom domain) - -### UploadFile (Public) - -Upload files to public storage. - -```javascript -const fileInput = document.querySelector('input[type="file"]'); -const { file_url } = await base44.integrations.Core.UploadFile({ - file: fileInput.files[0] -}); -console.log(file_url); // https://...uploaded_file.pdf -``` - -### UploadPrivateFile - -Upload files to private storage that requires a signed URL to access. - -```javascript -const { file_uri } = await base44.integrations.Core.UploadPrivateFile({ - file: fileInput.files[0] -}); -console.log(file_uri); // "private/user123/document.pdf" - -// Create a signed URL to access the file -const { signed_url } = await base44.integrations.Core.CreateFileSignedUrl({ - file_uri, - expires_in: 3600 // URL expires in 1 hour (default: 300 seconds) -}); -console.log(signed_url); // Temporary URL to access the private file -``` - -### CreateFileSignedUrl - -Generate temporary access links for private files. - -```javascript -const { signed_url } = await base44.integrations.Core.CreateFileSignedUrl({ - file_uri: "private/user123/document.pdf", - expires_in: 7200 // 2 hours -}); -``` - -**Parameters:** -- `file_uri` (string, required): URI from UploadPrivateFile -- `expires_in` (number, optional): Expiration time in seconds (default: 300) - -### ExtractDataFromUploadedFile - -Extract structured data from uploaded files using AI. - -```javascript -// First upload the file -const { file_url } = await base44.integrations.Core.UploadFile({ file }); - -// Then extract structured data -const result = await base44.integrations.Core.ExtractDataFromUploadedFile({ - file_url, - json_schema: { - type: "object", - properties: { - invoice_number: { type: "string" }, - total_amount: { type: "number" }, - date: { type: "string" }, - vendor_name: { type: "string" } - } - } -}); -console.log(result); // { invoice_number: "INV-12345", total_amount: 1250.00, ... } -``` - -## Custom Integrations - -Custom integrations allow workspace administrators to connect any external API by importing an OpenAPI specification. Use `base44.integrations.custom.call()` to invoke them. - -### Syntax - -```javascript -const response = await base44.integrations.custom.call( - slug, // Integration identifier (set by admin) - operationId, // Endpoint in "method:path" format (e.g. "get:/contacts", "post:/repos/{owner}/{repo}/issues") - params // Optional: payload, pathParams, queryParams -); -``` - -### Examples - -```javascript -// GET request with query params -const response = await base44.integrations.custom.call( - "my-crm", - "get:/contacts", - { queryParams: { limit: 10, status: "active" } } -); - -// POST request with body -const response = await base44.integrations.custom.call( - "my-crm", - "post:/contacts", - { payload: { name: "John Doe", email: "john@example.com" } } -); - -// Request with path parameters -const response = await base44.integrations.custom.call( - "github", - "post:/repos/{owner}/{repo}/issues", - { - pathParams: { owner: "myorg", repo: "myrepo" }, - payload: { title: "Bug report", body: "Something is broken" } - } -); -``` - -### Response Structure - -```javascript -{ - success: true, // Whether external API returned 2xx - status_code: 200, // HTTP status code - data: { ... } // Response from external API -} -``` - -## Requirements - -- **Core integrations**: Available on all plans -- **Catalog/Custom integrations**: Require Builder plan or higher - -## Type Definitions - -### Core Integration Parameters - -```typescript -/** Parameters for the InvokeLLM function. */ -interface InvokeLLMParams { - /** The prompt text to send to the model. */ - prompt: string; - /** If true, uses Google Search/Maps/News for real-time context. */ - add_context_from_internet?: boolean; - /** JSON schema for structured output. If provided, returns object instead of string. */ - response_json_schema?: object; - /** File URLs (from UploadFile) to provide as context. */ - file_urls?: string[]; -} - -/** Parameters for the GenerateImage function. */ -interface GenerateImageParams { - /** Description of the image to generate. */ - prompt: string; -} - -/** Result from GenerateImage. */ -interface GenerateImageResult { - /** URL of the generated image. */ - url: string; -} - -/** Parameters for the UploadFile function. */ -interface UploadFileParams { - /** The file object to upload. */ - file: File; -} - -/** Result from UploadFile. */ -interface UploadFileResult { - /** URL of the uploaded file. */ - file_url: string; -} - -/** Parameters for the SendEmail function. */ -interface SendEmailParams { - /** Recipient email address. */ - to: string; - /** Email subject line. */ - subject: string; - /** Plain text or HTML email body. */ - body: string; - /** Sender name (defaults to app name). */ - from_name?: string; -} - -/** Parameters for ExtractDataFromUploadedFile. */ -interface ExtractDataFromUploadedFileParams { - /** URL of the uploaded file. */ - file_url: string; - /** JSON schema defining fields to extract. */ - json_schema: object; -} - -/** Parameters for UploadPrivateFile. */ -interface UploadPrivateFileParams { - /** The file object to upload. */ - file: File; -} - -/** Result from UploadPrivateFile. */ -interface UploadPrivateFileResult { - /** URI of the private file (used for signed URLs). */ - file_uri: string; -} - -/** Parameters for CreateFileSignedUrl. */ -interface CreateFileSignedUrlParams { - /** URI from UploadPrivateFile. */ - file_uri: string; - /** Expiration time in seconds (default: 300). */ - expires_in?: number; -} - -/** Result from CreateFileSignedUrl. */ -interface CreateFileSignedUrlResult { - /** Temporary signed URL to access the file. */ - signed_url: string; -} -``` - -### CoreIntegrations - -```typescript -/** Core package containing built-in Base44 integration functions. */ -interface CoreIntegrations { - InvokeLLM(params: InvokeLLMParams): Promise; - GenerateImage(params: GenerateImageParams): Promise; - UploadFile(params: UploadFileParams): Promise; - SendEmail(params: SendEmailParams): Promise; - ExtractDataFromUploadedFile(params: ExtractDataFromUploadedFileParams): Promise; - UploadPrivateFile(params: UploadPrivateFileParams): Promise; - CreateFileSignedUrl(params: CreateFileSignedUrlParams): Promise; -} -``` - -### Custom Integrations - -```typescript -/** Parameters for calling a custom integration method. */ -interface CustomIntegrationCallParams { - /** Request body payload. */ - payload?: Record; - /** Path parameters to substitute in the URL. For example, { owner: "user", repo: "repo" }. */ - pathParams?: Record; - /** Query string parameters. */ - queryParams?: Record; -} - -/** Response from a custom integration call. */ -interface CustomIntegrationCallResponse { - /** Whether the external API returned a 2xx status code. */ - success: boolean; - /** The HTTP status code from the external API. */ - status_code: number; - /** The response data from the external API. */ - data: any; -} - -/** Module for calling custom pre-configured API integrations. */ -interface CustomIntegrationsModule { - /** - * Call a custom integration method. - * @param slug - The integration's unique identifier (set by workspace admin). - * @param operationId - The endpoint in "method:path" format (e.g. "get:/contacts", "post:/users/{id}"). - * @param params - Optional payload, pathParams, queryParams. - */ - call(slug: string, operationId: string, params?: CustomIntegrationCallParams): Promise; -} -``` - -### IntegrationsModule - -```typescript -/** Integrations module for calling integration methods. */ -type IntegrationsModule = { - /** Core package with built-in integrations. */ - Core: CoreIntegrations; - /** Custom integrations module. */ - custom: CustomIntegrationsModule; - /** Additional integration packages (dynamic). */ - [packageName: string]: any; -}; -``` diff --git a/plugins/base44/skills/base44-sdk/references/sso.md b/plugins/base44/skills/base44-sdk/references/sso.md deleted file mode 100644 index c7a5d70f5..000000000 --- a/plugins/base44/skills/base44-sdk/references/sso.md +++ /dev/null @@ -1,75 +0,0 @@ -# SSO Module - -Single Sign-On (SSO) support for authenticating Base44 users with external systems. Available via `base44.asServiceRole.sso`. - -> **Backend only**: This module requires service role access and can only be used in Base44-hosted backend functions. - -## Methods - -| Method | Signature | Description | -|--------|-----------|-------------| -| `getAccessToken(userId)` | `Promise` | Get an SSO access token for a specific user | - -## Examples - -### Get SSO Access Token - -```javascript -import { createClientFromRequest } from "npm:@base44/sdk"; - -Deno.serve(async (req) => { - const base44 = createClientFromRequest(req); - - // Get the current user - const user = await base44.auth.me(); - if (!user) { - return Response.json({ error: "Unauthorized" }, { status: 401 }); - } - - // Get SSO access token for this user - const { access_token } = await base44.asServiceRole.sso.getAccessToken(user.id); - - // Use the token to authenticate with an external system - return Response.json({ ssoToken: access_token }); -}); -``` - -### Get Token for a Specific User (Service Role) - -```javascript -Deno.serve(async (req) => { - const base44 = createClientFromRequest(req); - const { userId } = await req.json(); - - // Get SSO token for any user (service role has access to all users) - const { access_token } = await base44.asServiceRole.sso.getAccessToken(userId); - - return Response.json({ token: access_token }); -}); -``` - -## Use Cases - -- Authenticating Base44 users with external SaaS tools (e.g., Okta, Azure AD) -- Building SSO bridges between Base44 and third-party systems -- Generating tokens for backend-to-backend authenticated calls - -## Type Definitions - -```typescript -/** Response from the SSO access token endpoint. */ -interface SsoAccessTokenResponse { - /** The SSO access token for the specified user. */ - access_token: string; -} - -/** SSO module for managing SSO authentication (service role only). */ -interface SsoModule { - /** - * Gets an SSO access token for a specific user. - * @param userid - The Base44 user ID to get the SSO token for. - * @returns Promise resolving to the SSO access token response. - */ - getAccessToken(userid: string): Promise; -} -``` diff --git a/plugins/base44/skills/base44-sdk/references/users.md b/plugins/base44/skills/base44-sdk/references/users.md deleted file mode 100644 index 4205b0672..000000000 --- a/plugins/base44/skills/base44-sdk/references/users.md +++ /dev/null @@ -1,82 +0,0 @@ -# Users Module - -Invite users to the app via `base44.users`. - -## Contents -- [Methods](#methods) -- [Examples](#examples) (Invite User) -- [Roles](#roles) -- [Notes](#notes) - -## Methods - -| Method | Signature | Description | -|--------|-----------|-------------| -| `inviteUser(user_email, role)` | `Promise` | Invite a user to the app | - -## Examples - -### Invite User - -```javascript -// Invite a user with "user" role -await base44.users.inviteUser("newuser@example.com", "user"); - -// Invite an admin -await base44.users.inviteUser("admin@example.com", "admin"); -``` - -### Invite Multiple Users - -```javascript -const usersToInvite = [ - { email: "user1@example.com", role: "user" }, - { email: "user2@example.com", role: "user" }, - { email: "manager@example.com", role: "admin" } -]; - -for (const user of usersToInvite) { - await base44.users.inviteUser(user.email, user.role); - console.log(`Invited ${user.email} as ${user.role}`); -} -``` - -## Roles - -The `role` parameter must be one of: - -| Role | Description | -|------|-------------| -| `"user"` | Standard user with default permissions | -| `"admin"` | Administrator with elevated permissions | - -**Note:** Only `"user"` and `"admin"` are valid role values. An error will be thrown if you pass any other value. - -## Notes - -- **Email invitation**: The invited user receives an email with a link to join the app -- **Duplicate handling**: Inviting an existing user will re-send the invitation -- **Also available in auth**: `base44.auth.inviteUser()` provides the same functionality -- **Role validation**: Only `"user"` or `"admin"` are accepted - -```javascript -// These are equivalent: -await base44.users.inviteUser("newuser@example.com", "user"); -await base44.auth.inviteUser("newuser@example.com", "user"); -``` - -## Type Definitions - -```typescript -/** Users module for inviting users to the app. */ -interface UsersModule { - /** - * Invite a user to the application. - * @param user_email - User's email address. - * @param role - User's role ('user' or 'admin'). - * @returns Promise resolving when the invitation is sent. - * @throws Error if role is not 'user' or 'admin'. - */ - inviteUser(user_email: string, role: "user" | "admin"): Promise; -} -``` diff --git a/plugins/base44/skills/base44-troubleshooter/SKILL.md b/plugins/base44/skills/base44-troubleshooter/SKILL.md deleted file mode 100644 index c8451f206..000000000 --- a/plugins/base44/skills/base44-troubleshooter/SKILL.md +++ /dev/null @@ -1,60 +0,0 @@ ---- -name: base44-troubleshooter -description: Troubleshoot production issues using backend function logs. Use when investigating app errors, debugging function calls, or diagnosing production problems in Base44 apps. ---- - -# Troubleshoot Production Issues - -## Prerequisites - -Verify authentication before fetching logs: - -```bash -npx base44 whoami -``` - -If not authenticated or token expired, instruct user to run `npx base44 login`. - -Must be run from the project directory (where `base44/.app.jsonc` exists): - -```bash -cat base44/.app.jsonc -``` - -## Available Commands - -| Command | Description | Reference | -|---------|-------------|-----------| -| `base44 logs` | Fetch function logs for this app | [project-logs.md](references/project-logs.md) | - -## Troubleshooting Flow - -### 1. Check Recent Errors - -Start by pulling the latest errors across all functions: - -```bash -npx base44 logs --level error -``` - -### 2. Drill Into a Specific Function - -If you know which function is failing: - -```bash -npx base44 logs --function --level error -``` - -### 3. Inspect a Time Range - -Correlate with user-reported issue timestamps: - -```bash -npx base44 logs --function --since --until -``` - -### 4. Analyze the Logs - -- Look for stack traces and error messages in the output -- Check timestamps to correlate with user-reported issues -- Use `--limit` to fetch more entries if the default 50 isn't enough diff --git a/plugins/base44/skills/base44-troubleshooter/agents/openai.yaml b/plugins/base44/skills/base44-troubleshooter/agents/openai.yaml deleted file mode 100644 index 497ca7bdd..000000000 --- a/plugins/base44/skills/base44-troubleshooter/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Base44 Troubleshooter" - short_description: "Debug Base44 production issues with function logs" - default_prompt: "Use Base44 troubleshooting guidance to inspect logs and diagnose this production issue." diff --git a/plugins/base44/skills/base44-troubleshooter/references/project-logs.md b/plugins/base44/skills/base44-troubleshooter/references/project-logs.md deleted file mode 100644 index b58fa2ada..000000000 --- a/plugins/base44/skills/base44-troubleshooter/references/project-logs.md +++ /dev/null @@ -1,57 +0,0 @@ -# base44 logs - -Fetch function logs for this app. - -## Syntax - -```bash -npx base44 logs [options] -``` - -## Options - -| Option | Description | Required | -|--------|-------------|----------| -| `--function ` | Filter by function name(s), comma-separated. If omitted, fetches logs for all project functions | No | -| `--since ` | Show logs from this time (ISO format) | No | -| `--until ` | Show logs until this time (ISO format) | No | -| `--level ` | Filter by log level: `log`, `info`, `warn`, `error`, `debug` | No | -| `-n, --limit ` | Number of results to return (1-1000, default: 50) | No | -| `--order ` | Sort order: `asc` or `desc` (default: `desc`) | No | - -## Examples - -```bash -# Fetch logs for all project functions (last 50 entries) -npx base44 logs - -# Fetch only errors -npx base44 logs --level error - -# Fetch logs for a specific function -npx base44 logs --function my-function - -# Fetch logs for multiple functions -npx base44 logs --function send-email,process-payment - -# Fetch logs since a specific time -npx base44 logs --since 2024-01-15T10:00:00 - -# Fetch logs within a time range -npx base44 logs --since 2024-01-15T10:00:00 --until 2024-01-15T12:00:00 - -# Fetch last 100 log entries in ascending order -npx base44 logs -n 100 --order asc - -# Last 10 errors for a specific function -npx base44 logs --function myFunction --level error --limit 10 -``` - -## Notes - -- **Authentication required.** You must be logged in before fetching logs. -- **Project context required.** Must be run from the project directory (where `base44/.app.jsonc` exists). -- When multiple functions are specified, logs are merged and sorted by timestamp. -- If `--function` is omitted, logs are fetched for **all functions** defined in `base44/config.jsonc`. -- The `--limit` applies after merging logs from all specified functions. -- The `--since` and `--until` values are normalized to UTC if no timezone is provided (appends `Z`). diff --git a/plugins/binance/.app.json b/plugins/binance/.app.json deleted file mode 100644 index e40be3c7e..000000000 --- a/plugins/binance/.app.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "apps": { - "binance": { - "id": "asdk_app_6965faefe2b081919a998e14aa25f738" - } - } -} diff --git a/plugins/binance/.codex-plugin/plugin.json b/plugins/binance/.codex-plugin/plugin.json deleted file mode 100644 index 5bfafb189..000000000 --- a/plugins/binance/.codex-plugin/plugin.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "binance", - "version": "1.0.2", - "description": "Binance for Codex lets you access and explore Binance public, read-only market data using natural language.", - "author": { - "name": "Binance", - "url": "https://www.binance.com" - }, - "repository": "https://github.com/openai/plugins", - "license": "MIT", - "keywords": [], - "apps": "./.app.json", - "interface": { - "displayName": "Binance", - "shortDescription": "Binance for Codex lets you access and explore Binance public, read-only market data using natural language.", - "longDescription": "Binance for Codex lets you access and explore Binance public, read-only market data using natural language. Retrieve current and historical prices, order books, recent trades, candlesticks (klines), and exchange metadata across Spot, Futures, and Options. No authentication is required. This app does not access user accounts and does not support trading or transactions.\n\nMarket data is provided \u201cas is\u201d from Binance\u2019s public APIs and may be delayed, incomplete, or subject to change. This information is for informational purposes only and should not be relied upon for trading or financial decisions. Binance makes no representations or warranties regarding accuracy or timeliness and disclaims all liability arising from use of this data.", - "developerName": "Binance", - "category": "Finance", - "capabilities": [], - "defaultPrompt": [ - "Show the latest Binance market context for this asset" - ], - "screenshots": [], - "websiteURL": "https://www.binance.com", - "privacyPolicyURL": "https://www.binance.com/en/privacy", - "termsOfServiceURL": "https://www.binance.com/en/terms", - "composerIcon": "./assets/app-icon.png", - "logo": "./assets/app-icon.png" - }, - "homepage": "https://www.binance.com" -} diff --git a/plugins/binance/assets/app-icon.png b/plugins/binance/assets/app-icon.png deleted file mode 100644 index 5520256a9..000000000 Binary files a/plugins/binance/assets/app-icon.png and /dev/null differ diff --git a/plugins/biorender/.app.json b/plugins/biorender/.app.json deleted file mode 100644 index 1e18d97a3..000000000 --- a/plugins/biorender/.app.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "apps": { - "biorender": { - "id": "connector_691e3de0d2708191a6476a7b36e38779" - } - } -} diff --git a/plugins/biorender/.codex-plugin/plugin.json b/plugins/biorender/.codex-plugin/plugin.json deleted file mode 100644 index 8233bb731..000000000 --- a/plugins/biorender/.codex-plugin/plugin.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "biorender", - "version": "1.0.2", - "description": "BioRender helps scientists create professional figures in minutes.", - "author": { - "url": "https://biorender.com/", - "name": "BioRender" - }, - "homepage": "https://biorender.com/", - "repository": "https://github.com/openai/plugins", - "license": "MIT", - "keywords": [], - "apps": "./.app.json", - "interface": { - "displayName": "BioRender", - "shortDescription": "BioRender helps scientists create professional figures in minutes.", - "longDescription": "BioRender helps scientists create professional figures in minutes. Access thousands of scientifically accurate templates and icons directly in Codex to visualize protocols, pathways, molecular structures, and more. Brainstorm with your team, communicate research concepts, or build publication-ready figures for presentations, manuscripts, grant proposals, and posters.", - "category": "Creativity", - "capabilities": [], - "websiteURL": "https://biorender.com/", - "privacyPolicyURL": "https://biorender.com/privacy", - "termsOfServiceURL": "https://www.biorender.com/terms-of-service", - "defaultPrompt": [ - "Can you find me some GLP-1 diagram templates" - ], - "screenshots": [], - "composerIcon": "./assets/app-icon.png", - "logo": "./assets/app-icon.png", - "developerName": "BioRender" - } -} diff --git a/plugins/biorender/assets/app-icon.png b/plugins/biorender/assets/app-icon.png deleted file mode 100644 index 0bdc725c0..000000000 Binary files a/plugins/biorender/assets/app-icon.png and /dev/null differ diff --git a/plugins/box/.app.json b/plugins/box/.app.json deleted file mode 100644 index 114235fb7..000000000 --- a/plugins/box/.app.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "apps": { - "box": { - "id": "asdk_app_695bfc98071c8191bac7bc479aa27de7", - "required": false - } - } -} diff --git a/plugins/box/.codex-plugin/plugin.json b/plugins/box/.codex-plugin/plugin.json deleted file mode 100644 index 1eca05257..000000000 --- a/plugins/box/.codex-plugin/plugin.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "box", - "version": "0.0.3", - "description": "Search and reference your documents", - "author": { - "name": "OpenAI", - "url": "https://www.box.com/home" - }, - "homepage": "https://www.box.com/home", - "repository": "https://github.com/openai/plugins", - "license": "MIT", - "keywords": [], - "skills": "./skills/", - "apps": "./.app.json", - "interface": { - "displayName": "Box", - "shortDescription": "Search and reference your documents", - "longDescription": "Search and reference your documents in Box and work safely with Box content flows from Codex.", - "developerName": "OpenAI", - "category": "Productivity", - "capabilities": [], - "websiteURL": "https://www.box.com/home", - "privacyPolicyURL": "https://www.box.com/legal/privacypolicy", - "termsOfServiceURL": "https://www.box.com/legal/terms", - "brandColor": "#0061D5", - "defaultPrompt": [ - "Find a Box file and summarize the key points" - ], - "composerIcon": "./assets/app-icon.png", - "logo": "./assets/app-icon.png", - "screenshots": [] - } -} diff --git a/plugins/box/assets/app-icon.png b/plugins/box/assets/app-icon.png deleted file mode 100644 index e4198d695..000000000 Binary files a/plugins/box/assets/app-icon.png and /dev/null differ diff --git a/plugins/box/assets/box-small.svg b/plugins/box/assets/box-small.svg deleted file mode 100644 index 96438f927..000000000 --- a/plugins/box/assets/box-small.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/plugins/box/assets/box.svg b/plugins/box/assets/box.svg deleted file mode 100644 index 1f404801b..000000000 --- a/plugins/box/assets/box.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/plugins/box/skills/box/README.md b/plugins/box/skills/box/README.md deleted file mode 100644 index 991e353bc..000000000 --- a/plugins/box/skills/box/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# Box Content API — Codex Skill - -An [OpenAI Codex](https://openai.com/index/openai-codex/) skill that helps Codex build and troubleshoot Box integrations: uploads, folders, downloads, shared links, collaborations, search, metadata, webhooks, and Box AI retrieval. - -## Installation - -Copy or clone this folder into your Codex skills directory: - -```bash -# Example: install into the default Codex skills location -cp -r box-content-api ~/.codex/skills/ -``` - -Once installed, invoke the skill in any Codex conversation with `$box-content-api`. - -## What's included - -``` -├── SKILL.md # Entry point — workflow, guardrails, and verification -├── agents/openai.yaml # UI metadata for skill lists and chips -├── references/ -│ ├── auth-and-setup.md # Auth paths, SDK vs REST, codebase inspection -│ ├── box-cli.md # CLI-first local verification -│ ├── workflows.md # Quick router when the task is ambiguous -│ ├── content-workflows.md # Uploads, folders, shared links, collaborations, metadata, moves -│ ├── bulk-operations.md # Batch moves, folder restructuring, serial execution, rate limits -│ ├── webhooks-and-events.md # Webhook setup, events, idempotency -│ ├── ai-and-retrieval.md # Search-first retrieval and Box AI -│ └── troubleshooting.md # Common failure modes and debugging -├── scripts/ -│ ├── box_cli_smoke.py # Smoke tests via Box CLI -│ └── box_rest.py # Smoke tests via Box REST API (stdlib only) -└── examples/ - └── box-content-api-prompts.md # Example prompts -``` - -## Prerequisites - -- **Python 3.10+** — both scripts use only the standard library. -- **Box CLI** (optional) — install from [developer.box.com/guides/cli](https://developer.box.com/guides/cli) for CLI-first verification. If unavailable, the skill falls back to `scripts/box_rest.py` with a `BOX_ACCESS_TOKEN`. - -## Quick smoke test - -```bash -# With Box CLI installed and authenticated: -python3 scripts/box_cli_smoke.py check-auth -python3 scripts/box_cli_smoke.py list-folder-items 0 --max-items 5 - -# With a bearer token instead: -export BOX_ACCESS_TOKEN="your-token" -python3 scripts/box_rest.py get-item --item-type folder --item-id 0 -``` - -## License - -See [LICENSE](LICENSE) if present, or contact the repository owner. diff --git a/plugins/box/skills/box/SKILL.md b/plugins/box/skills/box/SKILL.md deleted file mode 100644 index 2800dc60d..000000000 --- a/plugins/box/skills/box/SKILL.md +++ /dev/null @@ -1,106 +0,0 @@ ---- -name: box-content-api -description: Build and troubleshoot Box integrations for uploads, folders, folder listings, downloads and previews, shared links, collaborations, search, metadata, event-driven automations, and Box AI retrieval flows. Use when Codex needs to add Box APIs or SDKs to an app, wire Box-backed document workflows, organize or share content, react to new files, or fetch Box content for search, summarization, extraction, or question-answering. ---- - -# Box Content API - -## Overview - -Implement Box content workflows in application code. Reuse the repository's existing auth and HTTP or SDK stack whenever possible, identify the acting Box identity before coding, and make the smallest end-to-end path work before layering on sharing, metadata, webhooks, or AI. - -## Route The Request - -| If the user needs... | Primary object | Read first | Pair with | Minimal verification | -| --- | --- | --- | --- | --- | -| Local verification, manual smoke tests, or quick inspection from Codex without app code changes | Current CLI environment | `references/box-cli.md` | `references/auth-and-setup.md` | `scripts/box_cli_smoke.py check-auth` then a read command | -| Uploads, folders, listings, downloads, shared links, collaborations, or metadata | File or folder | `references/content-workflows.md` | `references/auth-and-setup.md` | Read-after-write call using the same actor | -| Organizing, reorganizing, or batch-moving files across folders; bulk metadata tagging; migrating folder structures | File set or folder tree | `references/bulk-operations.md` | `references/auth-and-setup.md`, `references/content-workflows.md`, `references/ai-and-retrieval.md` | Inventory source, verify move count matches plan | -| Event-driven ingestion, new-file triggers, or webhook debugging | Webhook or events feed | `references/webhooks-and-events.md` | `references/auth-and-setup.md`, `references/troubleshooting.md` | Signature check plus duplicate-delivery test | -| Search, document retrieval, summarization, extraction, or Box AI | Search result set or file content | `references/ai-and-retrieval.md` | `references/auth-and-setup.md` | Retrieval-quality check before answer formatting | -| 401, 403, 404, 409, 429, missing content, or wrong-actor bugs | Existing request path | `references/troubleshooting.md` | `references/auth-and-setup.md` | Reproduce with the exact actor, object ID, and endpoint | -| Unsure which workflow applies | Unknown | `references/workflows.md` | `references/auth-and-setup.md` | Choose the smallest Box object/action pair first | - -## Workflow - -Follow these steps in order when coding against Box. - -1. Inspect the repository for existing Box auth, SDK or HTTP client, env vars, webhook handlers, Box ID persistence, and tests. -2. Determine the acting identity before choosing endpoints: connected user, enterprise service account, app user, or platform-provided token. -3. Identify the primary Box object and choose the matching reference from the routing table above. -4. Confirm whether the task changes access or data exposure. Shared links, collaborations, auth changes, large-scale downloads, and broad AI retrieval all need explicit user confirmation before widening access or scope. -5. Read only the matching reference files: - - Auth setup, actor selection, SDK vs REST: `references/auth-and-setup.md` - - Box CLI local verification: `references/box-cli.md` - - Workflow router: `references/workflows.md` - - Content operations: `references/content-workflows.md` - - Bulk file organization, batch moves, folder restructuring: `references/bulk-operations.md` - - Webhooks and events: `references/webhooks-and-events.md` - - AI and retrieval: `references/ai-and-retrieval.md` - - Debugging and failure modes: `references/troubleshooting.md` -6. Implement the smallest end-to-end flow that proves the integration works. -7. Add a runnable verification step. Prefer the repository's tests first; otherwise use `scripts/box_cli_smoke.py` when Box CLI is available and authenticated, and `scripts/box_rest.py` as a fallback. -8. Summarize the deliverable with auth context, Box IDs, env vars or config, and the exact verification command or test. - -## Guardrails - -- Preserve the existing Box auth model unless the user explicitly asks to change it. -- Check the current official Box docs before introducing a new auth path, changing auth scope, or changing Box AI behavior. -- Prefer an official Box SDK when the codebase already uses one or the target language has a maintained SDK. Otherwise use direct REST calls with explicit request and response handling. -- Keep access tokens, client secrets, private keys, and webhook secrets in env vars or the project's secret manager. -- Distinguish file IDs, folder IDs, shared links, metadata template identifiers, and collaboration IDs. -- Treat shared links, collaborations, and metadata writes as permission-sensitive changes. Confirm audience, scope, and least privilege before coding or applying them. -- Require explicit confirmation before widening external access, switching the acting identity, or retrieving more document content than the task truly needs. -- When a task requires understanding document content — classification, extraction, categorization — use Box AI (Q&A, extract) as the first method attempted. Box AI operates server-side and does not require downloading file bodies. Fall back to metadata inspection, previews, or local analysis only if Box AI is unavailable, not authorized, or returns an error on the first attempt. -- Pace Box AI calls at least 1–2 seconds apart. For content-based classification of many files, classify a small sample first to validate the prompt and discover whether cheaper signals (filename, extension, metadata) can sort the remaining files without additional AI calls. -- Avoid downloading file bodies or routing content through external AI pipelines when Box-native methods (Box AI, search, metadata, previews) can answer the question server-side. -- Connected Box tool availability can vary by account. If a Box MCP call returns `Tool not found`, treat that tool as unavailable for the rest of the current task. Do not retry it with different arguments or call it again later; switch to an available fallback. -- For connected Box app or MCP text reads, use `get_file_content` or Deep Research `fetch` only when the file is likely to have markdown or extracted-text content. If Box says markdown or text representation is unavailable, do not retry the same text read; switch to preview, metadata, or the next scoped fallback. -- For connected Box previews, avoid `get_file_preview` for files known to exceed 3 MB. Reuse `size` from existing search, listing, or details results when it is already available. -- Request only the fields the application actually needs, and persist returned Box IDs instead of reconstructing paths later. -- Run Box CLI commands strictly one at a time. The CLI does not support concurrent invocations and parallel calls cause auth conflicts and dropped operations. For bulk work (organizing, batch moves, batch metadata), default to REST over CLI. -- Make webhook and event consumers idempotent. Box delivery and retry paths can produce duplicates. -- Keep AI retrieval narrow for search and Q&A tasks. Search and filter first, then retrieve only the files needed for the answer. This does not apply to Box AI classification — when classifying documents, Box AI should be tried first per the content-understanding guardrail above. -- Do not use `box configure:environments:get --current` as a routine auth check because it can print sensitive environment details. - -## Verification - -- Prefer the repository's existing tests, scripts, or app flows when they already cover the changed Box behavior. -- If no better verification path exists, prefer `scripts/box_cli_smoke.py` when `box` is installed and authenticated. Fall back to `scripts/box_rest.py` with `BOX_ACCESS_TOKEN` when CLI auth is unavailable or the task specifically needs direct bearer-token verification. -- Confirm CLI auth with `box users:get me --json` or `scripts/box_cli_smoke.py check-auth`. -- Verify mutations with a read-after-write call using the same actor, and record the object ID. -- For webhooks, test the minimal happy path, duplicate delivery, and signature failure handling. -- For AI flows, test retrieval quality separately from answer formatting. - -Example smoke checks: - -```bash -python3 scripts/box_cli_smoke.py check-auth -python3 scripts/box_cli_smoke.py get-folder 0 --fields id name item_collection -python3 scripts/box_cli_smoke.py list-folder-items 0 --max-items 20 -python3 scripts/box_cli_smoke.py search "invoice" --limit 10 -python3 scripts/box_rest.py get-item --item-type folder --item-id 0 --fields id name item_collection -``` - -## Deliverable - -The final answer should include: - -- Acting auth context used for the change -- Box object type and IDs touched -- Env vars, secrets, or config expected by the integration -- Files or endpoints added or changed -- Exact verification command, script, or test path -- Any permission-sensitive assumptions that still need confirmation - -## References - -- `references/auth-and-setup.md`: auth path selection, SDK vs REST choice, existing-codebase inspection, and current Box doc anchors -- `references/box-cli.md`: CLI-first local auth, smoke-test commands, and safe verification patterns -- `references/workflows.md`: quick workflow router when the task is ambiguous -- `references/content-workflows.md`: uploads, folders, listings, downloads, shared links, collaborations, metadata, and file moves -- `references/bulk-operations.md`: organizing files at scale, batch moves, folder hierarchy creation, serial execution, and rate-limit handling -- `references/webhooks-and-events.md`: webhook setup, event-feed usage, idempotency, and verification -- `references/ai-and-retrieval.md`: search-first retrieval, Box AI usage, and external AI guardrails -- `references/troubleshooting.md`: common failure modes and a debugging checklist -- `examples/box-content-api-prompts.md`: example prompts for realistic use cases diff --git a/plugins/box/skills/box/agents/openai.yaml b/plugins/box/skills/box/agents/openai.yaml deleted file mode 100644 index 960c19201..000000000 --- a/plugins/box/skills/box/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Box Content API" - short_description: "Implement Box content flows safely" - default_prompt: "Use $box-content-api to identify the acting Box auth context, prefer Box CLI for local verification when available, implement the smallest Box flow needed, and return Box IDs plus a verification command." diff --git a/plugins/box/skills/box/examples/box-content-api-prompts.md b/plugins/box/skills/box/examples/box-content-api-prompts.md deleted file mode 100644 index 152bd8702..000000000 --- a/plugins/box/skills/box/examples/box-content-api-prompts.md +++ /dev/null @@ -1,7 +0,0 @@ -# Example Prompts - -- "Use $box-content-api to add the smallest possible endpoint that uploads a generated PDF into a configured Box folder, then tell me which folder ID and file ID were used to verify it." -- "Use $box-content-api to verify my current Box CLI auth context, list the root folder items with CLI-first verification, and tell me which actor the command is running as." -- "Use $box-content-api to debug why this Box folder listing returns 404 in production but works locally; identify the acting auth context and the exact object ID mismatch." -- "Use $box-content-api to wire a webhook handler for new files in a folder, make it idempotent, and include a duplicate-delivery verification step." -- "Use $box-content-api to build a search-first retrieval flow over Box content for invoice lookup, and only download file content if the selected result actually needs it." diff --git a/plugins/box/skills/box/references/ai-and-retrieval.md b/plugins/box/skills/box/references/ai-and-retrieval.md deleted file mode 100644 index 354c25755..000000000 --- a/plugins/box/skills/box/references/ai-and-retrieval.md +++ /dev/null @@ -1,108 +0,0 @@ -# AI and Retrieval - -## Table of Contents - -- Search-first strategy -- Content understanding preference order -- Choose Box AI vs external AI -- Retrieval guardrails -- Verification checklist -- Primary docs - -## Search-first strategy - -- Use Box search before recursive folder traversal or bulk download. -- Narrow the candidate set with ancestor folders, object type, filenames, owners, or metadata filters whenever possible. -- Return stable IDs and lightweight metadata first, then retrieve content only for the final shortlist. - -## Content understanding preference order - -When the task requires understanding what a document contains (classification, extraction, summarization, Q&A), prefer Box-native methods first: - -1. **Box AI Q&A or Extract** — keeps content server-side, no downloads needed. -2. **Metadata inspection** — check existing Box metadata templates or properties. -3. **Previews or thumbnails** — lightweight visual inspection without downloading the full file. -4. **Local analysis (OCR, agent-side parsing)** — download and process locally only when the above methods are unavailable, not authorized, or insufficient. - -If the first Box AI call fails with a 403 or feature-not-available error, switch to the next method immediately rather than retrying AI for the remaining files. - -## Connected Box app text retrieval - -`get_file_content` and Deep Research `fetch` require a markdown or extracted-text representation. Use file signals to avoid sending obviously unsupported files into a text read: - -1. Search or list narrowly until you have the exact Box file ID and lightweight file context. When that call is already part of the path, request `extension` and `representations`. -2. For one file with a text representation, prefer `get_file_content` and let the model reason over the returned text. -3. If the file is obviously visual, binary, or preview-oriented, prefer preview or metadata paths before a text-content read. `get_file_preview` is limited to files at or under 3 MB, so reuse `size` from search, listing, or details results when it is already available. - -If the earlier search or list result did not include enough file signals and a text read is uncertain, use `get_file_details` with the smallest useful `fields` set, such as `["extension", "size", "representations"]`. Check for `markdown` or `extracted_text` before calling `get_file_content` when avoiding a likely text-content miss is worth that extra metadata call. Do not add this preflight for every likely text-backed document. - -If `get_file_content` or Deep Research `fetch` returns `Markdown or text representation is not available for this file`, do not retry the same text read. Use a preview path for previewable content, inspect metadata when it can answer the question, or choose a scoped fallback. - -### Box AI via CLI - -**Before the first AI call**, run `box ai:ask --help` to confirm the command exists in the installed CLI version. - -Ask a question about a file's content: - -```bash -box ai:ask --items=id=,type=file \ - --prompt "Summarize this document in one sentence." \ - --json --no-color -``` - -Extract key-value pairs via a freeform prompt: - -```bash -box ai:extract --items=id=,type=file \ - --prompt "document_type, vendor_name, date" \ - --json --no-color -``` - -Extract with typed fields or a metadata template: - -```bash -box ai:extract-structured --items=id=,type=file \ - --fields "key=document_type,type=enum,options=invoice;receipt;contract;other" \ - --json --no-color -``` - -Reference: https://github.com/box/boxcli/blob/main/docs/ai.md - -An "Unexpected Error" with no HTTP body and exit code 2 may indicate the CLI version does not support AI commands, Box AI is not enabled for the account, or the file type is not supported. Run `box ai:ask --help` to verify the command exists, and try with a known-supported file type (PDF, DOCX) before falling back. - -### Box AI pacing - -Box AI endpoints have tighter per-user/per-app rate limits than standard content API calls. Pace AI calls at least 1–2 seconds apart. For bulk classification workflows, use the sample-first strategy described in `references/bulk-operations.md` to minimize the total number of AI calls. - -## Choose Box AI vs external AI - -- Prefer Box AI when the task maps directly to Box-native document question answering, extraction, or summarization. -- Use an external AI pipeline only when the product needs model behavior that Box AI does not provide or the application already owns the reasoning layer. -- Check the current official Box AI docs before changing prompts, capabilities, or supported object flows. - -## Retrieval guardrails - -- Avoid pulling raw file bodies when metadata, previews, or Box-native answers are enough. -- Keep retrieval scoped to the smallest relevant set of files. -- Preserve traceability with file IDs, names, shared links, or citations when the product needs auditability. -- Confirm with the user before broad retrieval across large folders or sensitive content sets. - -## Verification checklist - -- Retrieval quality: - - Confirm the search filters and candidate set contain the intended documents. -- Answer grounding: - - Confirm the final answer can point back to the specific file IDs or names used. -- Access control: - - Confirm the acting identity can only see the content the product is supposed to expose. - -## Primary docs - -- Search reference: - - https://developer.box.com/reference/get-search/ -- Box AI guides: - - https://developer.box.com/guides/box-ai/ -- Box AI with objects: - - https://developer.box.com/guides/box-ai/use-box-ai-with-box-objects/ -- Box CLI AI commands: - - https://github.com/box/boxcli/blob/main/docs/ai.md diff --git a/plugins/box/skills/box/references/auth-and-setup.md b/plugins/box/skills/box/references/auth-and-setup.md deleted file mode 100644 index e7981ce31..000000000 --- a/plugins/box/skills/box/references/auth-and-setup.md +++ /dev/null @@ -1,94 +0,0 @@ -# Auth and Setup - -## Table of Contents - -- Actor selection checklist -- CLI-first local testing -- Choosing the auth path -- Choosing SDK vs REST -- Inspecting an existing codebase -- Common secrets and config -- Official Box starting points - -## Actor selection checklist - -Choose the acting identity before you choose endpoints or debug errors: - -- Connected user: use when the product acts on behalf of an end user who linked their Box account. -- Enterprise service account: use when the backend runs unattended against enterprise-managed content. -- App user: use when the product provisions managed Box identities per tenant or workflow. -- Existing token from the platform: use when the surrounding app already resolved auth and passes the token into the Box layer. - -Always capture which actor you are using in logs, test output, and the final answer. Many Box bugs are actually actor mismatches. - -## CLI-first local testing - -When the task is a local smoke test, quick inspection, or one-off verification from Codex, prefer Box CLI before raw REST if `box` is already installed and authenticated. - -- Check CLI auth safely with `box users:get me --json`. -- If CLI auth is missing: - - Fastest OAuth path: `box login -d` - - Use your own Box app: `box login --platform-app` - - Use an app config file: `box configure:environments:add PATH` -- Use `--as-user ` when you need to verify behavior as a managed user or another actor allowed by the current Box environment. -- Use `-t ` only when the task explicitly requires a direct bearer token instead of the current CLI environment. -- Avoid `box configure:environments:get --current` as a routine auth check because it can print sensitive environment details. -- Prefer the bundled `scripts/box_cli_smoke.py` wrapper when you want deterministic CLI-based verification from the skill. - -## Choosing the auth path - -- Reuse the repository's existing Box auth flow if one already exists. -- Use a user-auth flow when end users connect their own Box accounts and the app acts as that user. -- Use the enterprise or server-side pattern already approved for the Box app when the backend runs unattended or manages enterprise content. -- Treat impersonation, app-user usage, token exchange, or downscoping as advanced changes. Add them only when the product requirements clearly demand them. -- Verify the exact flow against the current auth guides before introducing a new auth path or changing scopes. - -## Choosing SDK vs REST - -- Use an official Box SDK when the target language already has one in the codebase or the team prefers SDK-managed models and pagination. -- Use direct REST calls when the project already centers on a generic HTTP client, only a few endpoints are needed, or SDK support does not match the feature set. -- Avoid mixing SDK abstractions and handwritten REST calls for the same feature unless there is a clear gap. -- Preserve the project's existing retry, logging, and error-normalization patterns. - -## Inspecting an existing codebase - -Search for: - -- `box` -- `BOX_` -- `client_id` -- `client_secret` -- `enterprise` -- `shared_link` -- `webhook` -- `metadata` - -Confirm: - -- Where access tokens are issued, refreshed, or injected -- Whether requests are user-scoped, service-account-scoped, or app-user-scoped -- Whether the codebase already has pagination, retry, and rate-limit helpers -- Whether webhook verification already exists -- Whether file and folder IDs are persisted in a database, config, or user settings - -## Common secrets and config - -- Client ID and client secret -- Private key material or app config used by the approved Box auth flow -- Enterprise ID, user ID, or app-user identifiers when relevant -- Webhook signing secrets -- Default folder IDs -- Metadata template identifiers and field names -- Shared link defaults such as access level or expiration policy -- Box CLI environment names or `--as-user` conventions when the team uses CLI-based operations - -## Official Box starting points - -- Developer guides: https://developer.box.com/guides -- API reference root: https://developer.box.com/reference -- SDK overview: https://developer.box.com/guides/tooling/sdks/ -- Authentication guides: https://developer.box.com/guides/authentication/ -- CLI guides: https://developer.box.com/guides/cli -- CLI OAuth quick start: https://developer.box.com/guides/cli/quick-start - -Check the current Box docs before introducing a new auth model, changing scopes, or changing Box AI behavior, because auth guidance and SDK coverage can evolve independently from the content endpoints. diff --git a/plugins/box/skills/box/references/box-cli.md b/plugins/box/skills/box/references/box-cli.md deleted file mode 100644 index bd83833d4..000000000 --- a/plugins/box/skills/box/references/box-cli.md +++ /dev/null @@ -1,103 +0,0 @@ -# Box CLI - -## Table of Contents - -- When to use CLI-first mode -- Safe auth checks -- Authentication paths -- Common verification commands -- Actor controls -- Guardrails - -## When to use CLI-first mode - -Use Box CLI first when: - -- Codex needs a quick local smoke test without changing application code -- The operator already has a working Box CLI environment -- You want to verify behavior as the current CLI actor or with `--as-user` - -Use `scripts/box_rest.py` instead when: - -- The repository already uses token-based REST verification -- The task requires a raw bearer token from the surrounding platform -- Box CLI is not installed or not authenticated - -## Safe auth checks - -Use these commands to confirm CLI availability and auth without printing secrets: - -```bash -command -v box -box --version -box users:get me --json -``` - -Prefer the bundled wrapper: - -```bash -python3 scripts/box_cli_smoke.py check-auth -``` - -Do not use `box configure:environments:get --current` as a routine check because it can print sensitive environment details. - -## Authentication paths - -- Fastest OAuth flow with the official Box CLI app: - - `box login -d` -- OAuth with your own Box app: - - `box login --platform-app` -- Add an environment from an app config file: - - `box configure:environments:add PATH` - -After login or environment setup, re-run `box users:get me --json` to confirm the CLI can make authenticated calls. - -## Common verification commands - -Read-only checks: - -```bash -box users:get me --json -box folders:get 0 --json --fields id,name,item_collection -box folders:items 0 --json --max-items 20 -box search "invoice" --json --limit 10 -``` - -Write checks: - -```bash -box folders:create 0 "codex-smoke-test" --json -box files:upload ./artifact.pdf --parent-id 0 --json -box shared-links:create 12345 file --access company --json -``` - -Wrapper equivalents: - -```bash -python3 scripts/box_cli_smoke.py get-folder 0 --fields id name item_collection -python3 scripts/box_cli_smoke.py list-folder-items 0 --max-items 20 -python3 scripts/box_cli_smoke.py search "invoice" --limit 10 -python3 scripts/box_cli_smoke.py create-folder 0 "codex-smoke-test" -``` - -## Actor controls - -- Use `--as-user ` to verify behavior as a different allowed Box user. -- Use `-t ` only when the task explicitly requires a direct bearer token instead of the current CLI environment. -- Always report which actor was used for the verification command. - -## Guardrails - -- Do not paste or echo client secrets, private keys, or raw access tokens into the conversation. -- Prefer read commands before write commands. -- For shared links and collaborations, confirm scope and audience before creating or widening access. -- After any write, follow up with a read command against the same object and actor. - -## Official docs - -- CLI overview: - - https://developer.box.com/guides/cli -- CLI OAuth quick start: - - https://developer.box.com/guides/cli/quick-start -- CLI options and `--as-user`: - - https://developer.box.com/guides/cli/quick-start/options-and-bulk-commands/ diff --git a/plugins/box/skills/box/references/bulk-operations.md b/plugins/box/skills/box/references/bulk-operations.md deleted file mode 100644 index 4a2519f18..000000000 --- a/plugins/box/skills/box/references/bulk-operations.md +++ /dev/null @@ -1,229 +0,0 @@ -# Bulk Operations - -## Table of Contents - -- When this applies -- Constraints -- Workflow: inventory, classify, plan, execute, verify -- Step 1 — Inventory -- Step 2 — Classify (when content-based sorting is needed) -- Step 3 — Plan the target hierarchy -- Step 4 — Create folders -- Step 5 — Move files -- Step 6 — Verify -- Rate-limit and backoff handling -- REST vs CLI for bulk work -- Partial failure recovery - -Read `references/auth-and-setup.md` first when the acting identity or SDK vs REST choice is unclear. - -## When this applies - -Use this reference when the task involves more than a handful of files or folders in a single operation: - -- Organizing or reorganizing files across folders (by type, date, project, etc.) -- Batch-moving files from a flat folder into a structured hierarchy -- Creating a folder tree for a classification or filing scheme -- Bulk-tagging files with metadata -- Migrating content between folder structures - -## Constraints - -### Box CLI must run serially - -The Box CLI does not support concurrent invocations against the same environment. Launching multiple CLI processes in parallel causes auth conflicts, dropped operations, and unpredictable errors. **Always run CLI commands one at a time, waiting for each to complete before starting the next.** - -### Box API rate limits - -Box enforces per-user and per-app rate limits. Bulk operations that send requests too quickly will receive `429 Too Many Requests` responses. The response includes a `Retry-After` header with the number of seconds to wait. See [Rate-limit and backoff handling](#rate-limit-and-backoff-handling) below. - -### Folder name uniqueness - -Box enforces unique names within a parent folder. Creating a folder that already exists returns a `409 Conflict`. Check for existing folders before creating, or handle 409 by looking up the existing folder and reusing its ID. - -## Workflow: inventory, classify, plan, execute, verify - -Bulk operations follow this pattern. Do not skip ahead — moving files without a verified plan leads to misplaced content that is painful to undo. - -``` -Inventory → Classify (if needed) → Plan → Execute (serial) → Verify -``` - -Skip the classify step when files can be sorted by filename, extension, or existing metadata alone. - -## Step 1 — Inventory - -List everything in the source folder(s). Paginate fully — do not assume a single page covers all items. - -```bash -# CLI — list up to 1000 items -python3 scripts/box_cli_smoke.py list-folder-items --max-items 1000 --fields id name type - -# REST — paginate with offset -python3 scripts/box_rest.py get-folder-items --folder-id --limit 1000 --fields id name type -``` - -For folders with more items than one page returns, increment the offset and repeat until all items are captured. - -Capture each item's `id`, `name`, and `type` into a working list before proceeding. - -## Step 2 — Classify (when content-based sorting is needed) - -Skip this step if files can be categorized by filename, extension, or existing metadata. Use it when the documents are unstructured and their content determines the category — for example, a folder of mixed invoices, receipts, contracts, and reports that all share the same file type. - -### Preference order for content understanding - -1. **Box AI Q&A or Extract** (preferred) — ask Box AI to classify or extract structured fields from each file. This keeps content server-side, requires no downloads, and leverages Box's own document understanding. -2. **Metadata inspection** — check existing Box metadata templates or properties already applied to the files. -3. **Previews or thumbnails** — use Box preview representations for lightweight visual inspection without downloading the full file. -4. **Local analysis (OCR, agent-side parsing)** — download the file and process it locally. Use only when Box AI is unavailable, not authorized, or insufficient for the document type. - -### Sample-first strategy - -Do not classify every file up front. Box AI calls are slower than metadata reads and have tighter rate limits. - -1. **Pick a small sample** (5–10 files) that appear representative of the mix. -2. **Classify the sample** using Box AI to discover the category set and validate the prompt. -3. **Check for cheaper signals.** After seeing the sample results, determine whether filename patterns, extensions, or metadata can sort some or all of the remaining files without additional AI calls. -4. **Classify the remainder** — use AI only for files that cannot be sorted by cheaper signals. Pace AI calls at least 1–2 seconds apart. -5. **Record each classification** (file ID → category) as it completes so an interrupted run can resume without re-classifying finished files. - -### Box AI classification via CLI - -**Before the first AI call**, run `box ai:ask --help` to confirm the command exists in the installed CLI version and to check for any flag changes. - -Use `box ai:ask` to classify a single file by asking a direct question: - -```bash -box ai:ask --items=id=,type=file \ - --prompt "What type of document is this? Reply with exactly one of: invoice, receipt, contract, report, other." \ - --json --no-color -``` - -Use `box ai:extract` when you need key-value extraction via a freeform prompt: - -```bash -box ai:extract --items=id=,type=file \ - --prompt "document_type, vendor_name, date" \ - --json --no-color -``` - -Use `box ai:extract-structured` when you have a metadata template or want typed fields with options: - -```bash -box ai:extract-structured --items=id=,type=file \ - --fields "key=document_type,type=enum,options=invoice;receipt;contract;report;other" \ - --json --no-color -``` - -Reference: https://github.com/box/boxcli/blob/main/docs/ai.md - -### Handling failures during classification - -- **Exit code 2 or "Unexpected Error" with no HTTP body** can mean the installed CLI version does not have AI commands, Box AI is not enabled for the account, or the file type is not supported. Run `box ai:ask --help` to verify the command exists. If the command exists but still fails, try a known-supported file type (PDF, DOCX) to distinguish account-level unavailability from file-type incompatibility. -- If the first AI call returns a 403, feature-not-available, or similar authorization error, stop attempting AI classification for the remaining files and switch to the next method in the preference order immediately. -- If an individual file fails (unsupported format, empty content, timeout), log it and continue. Classify it manually or by fallback method after the batch finishes. -- On 429, wait for the `Retry-After` period and retry the same file before moving to the next one. -- Box AI support for file types varies by account tier. Image files (`.jpg`, `.png`) may not be supported for text-based Q&A. If the sample files are images, try `box ai:extract` first or check whether the account has image-understanding capabilities before falling back to local OCR. - -## Step 3 — Plan the target hierarchy - -Decide the target folder structure before creating or moving anything. - -1. Define the classification rule (by file-name pattern, extension, date, metadata, or content). -2. Map each inventoried item to its target folder path. -3. Identify which target folders already exist and which need to be created. -4. Write the plan as a structured list or table — folder path, folder ID (if existing), and the file IDs that belong there. - -Example plan: - -``` -Target folder | Parent ID | Needs creation | File IDs ------------------------|-----------|----------------|------------------ -/SEC Filings/10-K | 0 | yes | 111, 112, 113 ... -/SEC Filings/10-Q | 0 | yes | 211, 212, 213 ... -/Research/AI | 0 | yes | 311, 312, 313 ... -``` - -Confirm the plan with the user before executing if the operation is large or the classification is ambiguous. - -## Step 4 — Create folders - -Create target folders **one at a time, serially**. After each creation, record the returned folder ID — you need it for moves. - -```bash -# CLI -python3 scripts/box_cli_smoke.py create-folder "SEC Filings" -# then -python3 scripts/box_cli_smoke.py create-folder "10-K" - -# REST -python3 scripts/box_rest.py create-folder --parent-folder-id --name "SEC Filings" -``` - -Handle `409 Conflict` by listing the parent folder to find the existing folder's ID rather than failing the entire operation. - -Create parent folders before child folders. Process the tree top-down. - -## Step 5 — Move files - -Move files into their target folders **one at a time, serially**. Each move is a PUT that updates the file's parent. - -```bash -# REST (preferred for bulk — more reliable than CLI for high-volume moves) -python3 scripts/box_rest.py move-item --item-type file --item-id --parent-folder-id - -# CLI -python3 scripts/box_cli_smoke.py move-item file --parent-id -``` - -After each successful move, record it. If a move fails, log the file ID and error and continue with the remaining files — do not abort the entire batch. - -### Pacing - -Insert a short delay between operations when working with large batches (100+ items). A 200–500ms pause between requests helps stay within rate limits without dramatically increasing total time. - -When using REST directly in application code (not via the scripts), implement proper 429 backoff instead of fixed delays. - -## Step 6 — Verify - -After all moves complete: - -1. List each target folder and confirm it contains the expected file IDs and count. -2. List the source folder and confirm it is empty or contains only the items that were intentionally left behind. -3. Report any items that failed to move and the error encountered. - -```bash -python3 scripts/box_cli_smoke.py list-folder-items --max-items 1000 --fields id name -``` - -## Rate-limit and backoff handling - -When Box returns `429 Too Many Requests`: - -1. Read the `Retry-After` header (value in seconds). -2. Wait that many seconds before retrying the same request. -3. Do not retry other requests during the wait — the limit is typically per-user or per-app, so other requests will also be throttled. -4. After a successful retry, resume normal pacing. - -In application code, implement exponential backoff with jitter starting at the `Retry-After` value. In script-based or CLI-based operations, a simple sleep-and-retry is sufficient. - -## REST vs CLI for bulk work - -| Factor | REST (`box_rest.py` or SDK) | CLI (`box_cli_smoke.py`) | -| --- | --- | --- | -| Concurrency safety | Can handle controlled concurrency with proper rate-limit handling | Must run serially — no parallel invocations | -| Overhead per call | Lower — direct HTTP | Higher — process spawn per command | -| Error handling | Structured JSON responses, easy to parse and retry | Exit codes and mixed output, harder to automate | -| Best for | Bulk moves, batch metadata writes, any operation over ~50 items | Quick verification, small batches, interactive debugging | - -**Default to REST for bulk operations.** Fall back to CLI when REST auth is unavailable or the operator specifically prefers CLI-based workflows. - -## Partial failure recovery - -Bulk operations can fail partway through. Design for recovery: - -- Track which operations succeeded (keep a log of completed item IDs). -- On failure, report what completed, what failed, and what remains. -- Make the operation resumable: use the inventory list minus completed items as the input for a retry pass. -- Moves are idempotent in practice — moving a file to a folder it is already in returns the file unchanged. Re-running a move pass is safe. diff --git a/plugins/box/skills/box/references/content-workflows.md b/plugins/box/skills/box/references/content-workflows.md deleted file mode 100644 index e74822f81..000000000 --- a/plugins/box/skills/box/references/content-workflows.md +++ /dev/null @@ -1,108 +0,0 @@ -# Content Workflows - -## Table of Contents - -- Upload a file -- Create folders -- List folder items -- Download or preview a file -- Generate a shared link -- Invite collaborators -- Move a file or folder -- Read or write metadata - -Read `references/auth-and-setup.md` first when the acting identity or SDK vs REST choice is unclear. - -For local or manual verification, prefer `scripts/box_cli_smoke.py` when Box CLI is available and authenticated. Fall back to `scripts/box_rest.py` when the task is token-first or Box CLI is unavailable. - -## Upload a file - -- Primary docs: - - https://developer.box.com/reference/post-files-content/ -- Use for local-disk uploads, form uploads, or pushing generated artifacts into Box. -- Decide whether the input is a file path, in-memory upload, or generated artifact. -- Set the destination folder ID first. -- Treat file-name conflicts explicitly. -- Start with standard upload; use chunked upload only when file size or resumable behavior requires it. -- Minimal smoke check: - - Upload the file, then list the destination folder with the same actor and confirm returned `id` and `name`. - -## Create folders - -- Primary docs: - - https://developer.box.com/reference/post-folders/ -- Use for customer, project, case, employee, or workflow roots. -- Decide the parent folder and canonical naming scheme before coding. -- Handle duplicate-name conflicts intentionally. -- Persist the returned folder ID instead of reconstructing paths later. -- Minimal smoke check: - - Create the folder, then list the parent folder and confirm the child folder ID and name. - -## List folder items - -- Primary docs: - - https://developer.box.com/reference/get-folders-id-items/ -- Use for dashboards, file pickers, sync views, or post-upload verification. -- Request only the fields the app actually needs. -- Handle pagination instead of assuming a single page. -- Filter server-side where practical before adding client-side transforms. -- Minimal smoke check: - - Read the folder with a limited field set and confirm the app can process pagination metadata. - -## Download or preview a file - -- Primary docs: - - https://developer.box.com/reference/get-files-id-content/ - - https://developer.box.com/guides/embed/ui-elements/preview/ -- Download when the app truly needs raw bytes for processing or export. -- Use preview patterns when the app needs an embedded viewer. -- Preserve filename, content type, and auth context in tests and logs. -- Minimal smoke check: - - Fetch the file metadata first; only then download or preview the exact file ID you intend to use. - -## Generate a shared link - -- Primary docs: - - https://developer.box.com/reference/put-files-id/ - - https://developer.box.com/reference/put-folders-id/ -- Use for external sharing, customer handoff, or quick verification outside the app. -- Add or update `shared_link` on the target file or folder, not on an unrelated object. -- Set access level, download permissions, and expiration intentionally. -- Confirm the user explicitly wants the audience widened before enabling or broadening sharing. -- Minimal smoke check: - - Read the file or folder after the update and confirm the resulting `shared_link` fields. - -## Invite collaborators - -- Primary docs: - - https://developer.box.com/reference/post-collaborations/ -- Use for team, vendor, or customer access to a shared workspace. -- Prefer folder collaboration when multiple files should inherit the same access. -- Choose the narrowest role that satisfies the request. -- Verify the acting identity is allowed to invite collaborators before coding the flow. -- Minimal smoke check: - - Create the collaboration, then fetch or list collaborations to confirm the collaborator and role. - -## Move a file or folder - -- Primary docs: - - https://developer.box.com/reference/put-files-id/ (update parent to move a file) - - https://developer.box.com/reference/put-folders-id/ (update parent to move a folder) -- Use for reorganizing content, filing into project or category folders, or migrating between folder structures. -- A move is a PUT on the item that sets `parent.id` to the new folder. -- Moving a folder moves all of its contents recursively. -- Handle name conflicts in the target folder — Box returns `409` if a same-named item already exists in the destination. -- For bulk moves (more than a handful of items), read `references/bulk-operations.md` for the inventory-plan-execute-verify workflow, serial execution constraints, and rate-limit handling. -- Minimal smoke check: - - Move the item, then list the target folder and confirm the item appears with the correct ID and name. Also list the source folder to confirm the item is gone. - -## Read or write metadata - -- Primary docs: - - https://developer.box.com/reference/post-files-id-metadata-global-properties/ -- Use for invoice IDs, customer names, case numbers, review states, or other business context. -- Read the template definition or existing metadata instance before writing values. -- Keep template identifiers and field names in config, not scattered through the codebase. -- Validate keys and value types in code before calling Box. -- Minimal smoke check: - - Write the metadata, then read the same instance back and confirm only the expected keys changed. diff --git a/plugins/box/skills/box/references/troubleshooting.md b/plugins/box/skills/box/references/troubleshooting.md deleted file mode 100644 index 985485d9b..000000000 --- a/plugins/box/skills/box/references/troubleshooting.md +++ /dev/null @@ -1,105 +0,0 @@ -# Troubleshooting - -## Table of Contents - -- Debugging checklist -- 401 or 403 -- 404 -- 409 -- 429 -- Webhook verification failures -- Search quality problems -- Missing text representation -- CLI auth problems -- Codex sandbox network access - -## Debugging checklist - -Before changing code, capture these facts: - -- Acting auth context -- Exact endpoint and HTTP method -- Box object type and ID -- Minimal request payload -- Response status and error body - -Most Box failures reduce to one of these mismatches: wrong actor, wrong object ID, wrong endpoint, or an access-control change that was never confirmed. - -When using Box CLI, run `box --help` before the first invocation of any subcommand to confirm it exists in the installed version and to verify flag names, required arguments, and supported options. - -## 401 or 403 - -- Wrong auth context -- Missing scope or app permission -- Acting user does not have access to the target object -- Token expired, downscoped, or issued for a different flow than expected - -## 404 - -- Wrong file or folder ID -- Object exists but is not visible to the current actor -- Shared link or collaboration refers to a different object than expected - -## 409 - -- File or folder name conflict on create or upload -- Collaboration already exists -- Metadata write conflicts with the expected template or instance state - -## 429 - -- Rate limit or burst traffic -- Missing backoff and retry handling -- Excessive search or listing requests without pagination controls -- Bulk operations (batch moves, folder creation, metadata writes) sending requests too quickly — read the `Retry-After` header and wait that many seconds before retrying -- Parallel Box CLI invocations — the CLI must run serially; concurrent calls cause auth conflicts and can trigger rate limits faster than expected -- For bulk workflows, add a 200–500ms pause between serial operations and implement proper `Retry-After` backoff; see `references/bulk-operations.md` - -## Webhook verification failures - -- Wrong signing secret -- Request body mutated before signature verification -- Timestamp tolerance or replay checks missing -- The code logs the body before verification and accidentally changes normalization - -## Search quality problems - -- Missing ancestor-folder, type, owner, or metadata filters -- Querying as the wrong actor -- Expecting search to return content the current identity cannot see -- Downloading too early instead of returning IDs and metadata first - -## Missing text representation - -`get_file_content` and Deep Research `fetch` read markdown or extracted text. They can fail when Box has neither representation for the selected file. - -- Do not retry the same `get_file_content` or Deep Research `fetch` text read after `Markdown or text representation is not available for this file`. -- Prefer preview or page-image tools for previewable visual content. -- Use metadata when it can answer the question without a body read. -- If document content is still required, choose the smallest fallback allowed by the task and actor permissions. - -## CLI auth problems - -- `box` is installed but the current environment is not authorized -- The command is running as the wrong CLI actor because `--as-user` was omitted or mis-set -- A direct token passed with `-t` overrides the expected CLI environment -- Someone used environment-inspection commands that print sensitive values instead of safe auth checks like `box users:get me --json` - -## Codex sandbox network access - -Box CLI commands that worked in a regular terminal fail inside Codex with `getaddrinfo ENOTFOUND api.box.com` or a generic "Unexpected Error" with no HTTP body. Auth checks like `box users:get me --json` may still pass because they use cached local credentials, making it look like auth works but API calls do not. - -**Cause:** Codex sandboxes block outbound network access by default. The CLI cannot reach `api.box.com`, `upload.box.com`, or any other Box endpoint. - -**Fix for Codex CLI:** Add to `~/.codex/config.toml`: - -```toml -[sandbox_workspace_write] -network_access = true -``` - -Then restart the Codex CLI session. - -**Fix for Codex web (cloud):** In the environment settings, turn agent internet access **On** and add `box.com` and `boxcloud.com` to the domain allowlist. - -**How to tell this is the problem:** If `box users:get me --json` succeeds but `box files:get --json` fails with a DNS or connection error, the sandbox is blocking outbound network access. The same commands will work in a regular terminal outside of Codex. diff --git a/plugins/box/skills/box/references/webhooks-and-events.md b/plugins/box/skills/box/references/webhooks-and-events.md deleted file mode 100644 index f31d77228..000000000 --- a/plugins/box/skills/box/references/webhooks-and-events.md +++ /dev/null @@ -1,39 +0,0 @@ -# Webhooks and Events - -## Table of Contents - -- Choose webhooks vs events -- Minimal implementation path -- Verification checklist -- Primary docs - -## Choose webhooks vs events - -- Use Box webhooks when the app needs push-based notifications for new or changed content. -- Use the events APIs for catch-up syncs, polling-based integrations, or backfills after downtime. -- Start with the smallest event consumer that can receive the signal, fetch the affected object metadata, and log or enqueue work. - -## Minimal implementation path - -1. Confirm which Box actor owns the webhook or event subscription. -2. Store webhook signing secrets outside the codebase. -3. Verify signatures before mutating request bodies. -4. Persist enough event data to deduplicate duplicate deliveries and retries. -5. Fetch the file or folder metadata after receiving the event rather than trusting the event payload alone. -6. Hand off to downstream processing only after the idempotency key is recorded. - -## Verification checklist - -- Happy path: receive the event, verify the signature, fetch the file or folder metadata, and log the Box ID. -- Duplicate delivery: send the same payload twice and confirm only one downstream action happens. -- Signature failure: reject a payload with a bad signature and confirm no side effects occur. -- Catch-up behavior: if the workflow also uses the events APIs, confirm the checkpoint or cursor is persisted. - -## Primary docs - -- Webhook guides: - - https://developer.box.com/guides/webhooks/ -- Webhook use cases: - - https://developer.box.com/guides/webhooks/use-cases/ -- Events API reference: - - https://developer.box.com/reference/resources/event/ diff --git a/plugins/box/skills/box/references/workflows.md b/plugins/box/skills/box/references/workflows.md deleted file mode 100644 index 6dcb849ec..000000000 --- a/plugins/box/skills/box/references/workflows.md +++ /dev/null @@ -1,69 +0,0 @@ -# Workflow Router - -## Table of Contents - -- Box CLI local verification -- Content workflows -- Webhooks and events -- AI and retrieval -- Troubleshooting - -Use this file when the task is ambiguous and you need to decide which targeted reference to open next. - -## Box CLI local verification - -Open `references/box-cli.md` for: - -- CLI-first smoke tests -- Safe CLI auth checks -- `--as-user` verification -- Quick local reads and writes without changing app code - -## Content workflows - -Open `references/content-workflows.md` for: - -- Uploading files -- Creating folders -- Listing folder items -- Downloading or previewing files -- Creating shared links -- Inviting collaborators -- Reading or writing metadata - -## Bulk operations - -Open `references/bulk-operations.md` for: - -- Organizing or reorganizing files across folders -- Batch-moving files into a structured hierarchy -- Creating folder trees for classification schemes -- Bulk metadata tagging -- Serial execution constraints and rate-limit handling - -## Webhooks and events - -Open `references/webhooks-and-events.md` for: - -- Push-based notifications -- Catch-up syncs with the events APIs -- Signature verification -- Idempotent event consumers - -## AI and retrieval - -Open `references/ai-and-retrieval.md` for: - -- Search-first retrieval -- Box AI questions and summaries -- External AI pipelines over Box content -- Traceability and citation requirements - -## Troubleshooting - -Open `references/troubleshooting.md` for: - -- 401, 403, 404, 409, and 429 failures -- Wrong-actor bugs -- Search result mismatches -- Webhook verification failures diff --git a/plugins/box/skills/box/scripts/box_cli_smoke.py b/plugins/box/skills/box/scripts/box_cli_smoke.py deleted file mode 100755 index 508835f66..000000000 --- a/plugins/box/skills/box/scripts/box_cli_smoke.py +++ /dev/null @@ -1,230 +0,0 @@ -#!/usr/bin/env python3 -"""Minimal Box CLI smoke-test helper.""" - -from __future__ import annotations - -import argparse -import shutil -import subprocess -import sys -from pathlib import Path - - -def ensure_box_cli() -> str: - box = shutil.which("box") - if not box: - raise SystemExit( - "Box CLI is not installed. Install it or fall back to scripts/box_rest.py." - ) - return box - - -def common_box_args(args: argparse.Namespace) -> list[str]: - command = ["--json", "--no-color"] - if args.token: - command.extend(["-t", args.token]) - if args.as_user: - command.extend(["--as-user", args.as_user]) - return command - - -def run_box(subcommand: list[str]) -> int: - box = ensure_box_cli() - process = subprocess.run([box, *subcommand], text=True) - return process.returncode - - -def handle_check_auth(args: argparse.Namespace) -> int: - return run_box(["users:get", "me", *common_box_args(args)]) - - -def handle_get_folder(args: argparse.Namespace) -> int: - command = ["folders:get", args.folder_id, *common_box_args(args)] - if args.fields: - command.extend(["--fields", ",".join(args.fields)]) - return run_box(command) - - -def handle_list_folder_items(args: argparse.Namespace) -> int: - command = [ - "folders:items", - args.folder_id, - *common_box_args(args), - "--max-items", - str(args.max_items), - ] - if args.fields: - command.extend(["--fields", ",".join(args.fields)]) - return run_box(command) - - -def handle_search(args: argparse.Namespace) -> int: - command = ["search", args.query, *common_box_args(args), "--limit", str(args.limit)] - if args.item_type: - command.extend(["--type", args.item_type]) - if args.fields: - command.extend(["--fields", ",".join(args.fields)]) - if args.ancestor_folder_ids: - command.extend(["--ancestor-folder-ids", ",".join(args.ancestor_folder_ids)]) - if args.content_types: - command.extend(["--content-types", ",".join(args.content_types)]) - return run_box(command) - - -def handle_create_folder(args: argparse.Namespace) -> int: - command = ["folders:create", args.parent_id, args.name, *common_box_args(args)] - if args.fields: - command.extend(["--fields", ",".join(args.fields)]) - return run_box(command) - - -def handle_upload_file(args: argparse.Namespace) -> int: - file_path = Path(args.path).expanduser().resolve() - if not file_path.exists(): - raise SystemExit(f"File not found: {file_path}") - command = [ - "files:upload", - str(file_path), - *common_box_args(args), - "--parent-id", - args.parent_id, - ] - if args.name: - command.extend(["--name", args.name]) - if args.overwrite: - command.append("--overwrite") - if args.fields: - command.extend(["--fields", ",".join(args.fields)]) - return run_box(command) - - -def handle_move_item(args: argparse.Namespace) -> int: - command = [ - f"{args.item_type}s:move", - args.item_id, - args.parent_id, - *common_box_args(args), - ] - if args.fields: - command.extend(["--fields", ",".join(args.fields)]) - return run_box(command) - - -def handle_create_shared_link(args: argparse.Namespace) -> int: - command = [ - "shared-links:create", - args.item_id, - args.item_type, - *common_box_args(args), - ] - if args.access: - command.extend(["--access", args.access]) - if args.can_download is not None: - command.append("--can-download" if args.can_download else "--no-can-download") - if args.unshared_at: - command.extend(["--unshared-at", args.unshared_at]) - if args.fields: - command.extend(["--fields", ",".join(args.fields)]) - return run_box(command) - - -def parse_bool(value: str) -> bool: - lowered = value.lower() - if lowered == "true": - return True - if lowered == "false": - return False - raise argparse.ArgumentTypeError("Expected true or false.") - - -def add_common_args(parser: argparse.ArgumentParser) -> None: - parser.add_argument( - "--token", - help="Optional Box token to pass directly to the CLI.", - ) - parser.add_argument( - "--as-user", - help="Optional user ID for Box CLI --as-user impersonation.", - ) - - -def main() -> int: - parser = argparse.ArgumentParser(description="Minimal Box CLI smoke-test helper.") - subparsers = parser.add_subparsers(dest="command", required=True) - - check_auth = subparsers.add_parser( - "check-auth", - help="Verify that Box CLI is installed and can access the current actor.", - ) - add_common_args(check_auth) - check_auth.set_defaults(handler=handle_check_auth) - - get_folder = subparsers.add_parser("get-folder", help="Fetch a Box folder.") - add_common_args(get_folder) - get_folder.add_argument("folder_id") - get_folder.add_argument("--fields", nargs="*") - get_folder.set_defaults(handler=handle_get_folder) - - list_folder_items = subparsers.add_parser( - "list-folder-items", help="List items in a Box folder." - ) - add_common_args(list_folder_items) - list_folder_items.add_argument("folder_id") - list_folder_items.add_argument("--max-items", type=int, default=20) - list_folder_items.add_argument("--fields", nargs="*") - list_folder_items.set_defaults(handler=handle_list_folder_items) - - search = subparsers.add_parser("search", help="Search Box content.") - add_common_args(search) - search.add_argument("query") - search.add_argument("--limit", type=int, default=10) - search.add_argument("--type", dest="item_type", choices=["file", "folder", "web_link"]) - search.add_argument("--ancestor-folder-ids", nargs="*") - search.add_argument("--content-types", nargs="*") - search.add_argument("--fields", nargs="*") - search.set_defaults(handler=handle_search) - - create_folder = subparsers.add_parser("create-folder", help="Create a Box folder.") - add_common_args(create_folder) - create_folder.add_argument("parent_id") - create_folder.add_argument("name") - create_folder.add_argument("--fields", nargs="*") - create_folder.set_defaults(handler=handle_create_folder) - - upload_file = subparsers.add_parser("upload-file", help="Upload a file to Box.") - add_common_args(upload_file) - upload_file.add_argument("path") - upload_file.add_argument("--parent-id", default="0") - upload_file.add_argument("--name") - upload_file.add_argument("--overwrite", action="store_true") - upload_file.add_argument("--fields", nargs="*") - upload_file.set_defaults(handler=handle_upload_file) - - move_item = subparsers.add_parser( - "move-item", help="Move a file or folder to a different parent folder." - ) - add_common_args(move_item) - move_item.add_argument("item_id") - move_item.add_argument("item_type", choices=["file", "folder"]) - move_item.add_argument("--parent-id", required=True) - move_item.add_argument("--fields", nargs="*") - move_item.set_defaults(handler=handle_move_item) - - create_shared_link = subparsers.add_parser( - "create-shared-link", help="Create or update a shared link with Box CLI." - ) - add_common_args(create_shared_link) - create_shared_link.add_argument("item_id") - create_shared_link.add_argument("item_type", choices=["file", "folder"]) - create_shared_link.add_argument("--access") - create_shared_link.add_argument("--can-download", type=parse_bool) - create_shared_link.add_argument("--unshared-at") - create_shared_link.add_argument("--fields", nargs="*") - create_shared_link.set_defaults(handler=handle_create_shared_link) - - args = parser.parse_args() - return args.handler(args) - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/plugins/box/skills/box/scripts/box_rest.py b/plugins/box/skills/box/scripts/box_rest.py deleted file mode 100755 index 6bb0efd9a..000000000 --- a/plugins/box/skills/box/scripts/box_rest.py +++ /dev/null @@ -1,369 +0,0 @@ -#!/usr/bin/env python3 -"""Minimal Box REST smoke-test helper using only the Python standard library.""" - -from __future__ import annotations - -import argparse -import json -import mimetypes -import os -import sys -import uuid -from pathlib import Path -from typing import Any -from urllib import error, parse, request - - -DEFAULT_API_BASE = "https://api.box.com/2.0" -DEFAULT_UPLOAD_BASE = "https://upload.box.com/api/2.0" - - -def build_headers(token: str, extra: dict[str, str] | None = None) -> dict[str, str]: - headers = { - "Authorization": f"Bearer {token}", - "Accept": "application/json", - } - if extra: - headers.update(extra) - return headers - - -def api_request( - method: str, - url: str, - token: str, - body: bytes | None = None, - headers: dict[str, str] | None = None, -) -> Any: - req = request.Request( - url=url, - method=method, - data=body, - headers=build_headers(token, headers), - ) - try: - with request.urlopen(req) as resp: - raw = resp.read() - content_type = resp.headers.get("Content-Type", "") - if "application/json" in content_type: - return json.loads(raw.decode("utf-8")) - return {"status": resp.status, "body": raw.decode("utf-8")} - except error.HTTPError as exc: - raw = exc.read().decode("utf-8", errors="replace") - try: - payload = json.loads(raw) - except json.JSONDecodeError: - payload = {"message": raw} - payload["_http_status"] = exc.code - raise SystemExit( - f"Box API error {exc.code}:\n{json.dumps(payload, indent=2, sort_keys=True)}" - ) - - -def dump_json(payload: Any) -> None: - json.dump(payload, sys.stdout, indent=2, sort_keys=True) - sys.stdout.write("\n") - - -def encode_query(params: dict[str, Any]) -> str: - filtered = {} - for key, value in params.items(): - if value is None: - continue - if isinstance(value, list): - filtered[key] = ",".join(str(item) for item in value) - else: - filtered[key] = value - return parse.urlencode(filtered) - - -def get_token(cli_token: str | None) -> str: - token = cli_token or os.environ.get("BOX_ACCESS_TOKEN") - if not token: - raise SystemExit( - "Missing Box token. Set BOX_ACCESS_TOKEN or pass --token." - ) - return token - - -def parse_bool(value: str) -> bool: - lowered = value.lower() - if lowered == "true": - return True - if lowered == "false": - return False - raise argparse.ArgumentTypeError("Expected true or false.") - - -def handle_get_item(args: argparse.Namespace) -> None: - query = encode_query({"fields": args.fields}) - url = f"{args.base_url}/{args.item_type}s/{args.item_id}" - if query: - url = f"{url}?{query}" - dump_json(api_request("GET", url, args.token)) - - -def handle_get_folder_items(args: argparse.Namespace) -> None: - query = encode_query( - { - "limit": args.limit, - "offset": args.offset, - "fields": args.fields, - } - ) - url = f"{args.base_url}/folders/{args.folder_id}/items" - if query: - url = f"{url}?{query}" - dump_json(api_request("GET", url, args.token)) - - -def handle_search(args: argparse.Namespace) -> None: - query = encode_query( - { - "query": args.query, - "limit": args.limit, - "offset": args.offset, - "type": args.type, - "fields": args.fields, - "ancestor_folder_ids": args.ancestor_folder_ids, - "content_types": args.content_types, - } - ) - url = f"{args.base_url}/search?{query}" - dump_json(api_request("GET", url, args.token)) - - -def json_body(payload: dict[str, Any]) -> bytes: - return json.dumps(payload).encode("utf-8") - - -def handle_create_folder(args: argparse.Namespace) -> None: - payload = { - "name": args.name, - "parent": {"id": args.parent_folder_id}, - } - query = encode_query({"fields": args.fields}) - url = f"{args.base_url}/folders" - if query: - url = f"{url}?{query}" - dump_json( - api_request( - "POST", - url, - args.token, - body=json_body(payload), - headers={"Content-Type": "application/json"}, - ) - ) - - -def _sanitize_filename(name: str) -> str: - """Escape characters that would break a Content-Disposition header value.""" - return name.replace("\\", "\\\\").replace('"', '\\"').replace("\r", "").replace("\n", "") - - -def multipart_upload(file_path: Path, attributes: dict[str, Any]) -> tuple[bytes, str]: - boundary = f"codex-box-{uuid.uuid4().hex}" - mime_type = mimetypes.guess_type(file_path.name)[0] or "application/octet-stream" - safe_name = _sanitize_filename(file_path.name) - metadata_part = json.dumps(attributes).encode("utf-8") - file_bytes = file_path.read_bytes() - chunks = [ - f"--{boundary}\r\n".encode("utf-8"), - b'Content-Disposition: form-data; name="attributes"\r\n', - b"Content-Type: application/json\r\n\r\n", - metadata_part, - b"\r\n", - f"--{boundary}\r\n".encode("utf-8"), - f'Content-Disposition: form-data; name="file"; filename="{safe_name}"\r\n'.encode( - "utf-8" - ), - f"Content-Type: {mime_type}\r\n\r\n".encode("utf-8"), - file_bytes, - b"\r\n", - f"--{boundary}--\r\n".encode("utf-8"), - ] - return b"".join(chunks), boundary - - -def handle_upload_file(args: argparse.Namespace) -> None: - file_path = Path(args.file).expanduser().resolve() - if not file_path.exists(): - raise SystemExit(f"File not found: {file_path}") - attributes = { - "name": args.name or file_path.name, - "parent": {"id": args.folder_id}, - } - body, boundary = multipart_upload(file_path, attributes) - query = encode_query({"fields": args.fields}) - url = f"{args.upload_base_url}/files/content" - if query: - url = f"{url}?{query}" - dump_json( - api_request( - "POST", - url, - args.token, - body=body, - headers={"Content-Type": f"multipart/form-data; boundary={boundary}"}, - ) - ) - - -def handle_move_item(args: argparse.Namespace) -> None: - payload = {"parent": {"id": args.parent_folder_id}} - query = encode_query({"fields": args.fields}) - url = f"{args.base_url}/{args.item_type}s/{args.item_id}" - if query: - url = f"{url}?{query}" - dump_json( - api_request( - "PUT", - url, - args.token, - body=json_body(payload), - headers={"Content-Type": "application/json"}, - ) - ) - - -def handle_create_shared_link(args: argparse.Namespace) -> None: - shared_link: dict[str, Any] = {} - if args.access: - shared_link["access"] = args.access - if args.allow_download is not None: - shared_link["permissions"] = {"can_download": args.allow_download} - if args.unshared_at: - shared_link["unshared_at"] = args.unshared_at - payload = {"shared_link": shared_link} - dump_json( - api_request( - "PUT", - f"{args.base_url}/{args.item_type}s/{args.item_id}", - args.token, - body=json_body(payload), - headers={"Content-Type": "application/json"}, - ) - ) - - -def add_common_auth_args(parser: argparse.ArgumentParser) -> None: - parser.add_argument( - "--token", - help="Box access token. Defaults to BOX_ACCESS_TOKEN.", - ) - parser.add_argument( - "--base-url", - default=os.environ.get("BOX_API_BASE_URL", DEFAULT_API_BASE), - help=f"Box API base URL. Defaults to {DEFAULT_API_BASE}.", - ) - - -def main() -> int: - parser = argparse.ArgumentParser( - description="Minimal Box REST smoke-test helper." - ) - subparsers = parser.add_subparsers(dest="command", required=True) - - get_item = subparsers.add_parser( - "get-item", help="Fetch a Box file or folder." - ) - add_common_auth_args(get_item) - get_item.add_argument("--item-type", required=True, choices=["file", "folder"]) - get_item.add_argument("--item-id", required=True) - get_item.add_argument( - "--fields", - nargs="*", - help="Optional list of Box fields to request.", - ) - get_item.set_defaults(handler=handle_get_item) - - get_folder_items = subparsers.add_parser( - "get-folder-items", help="List items in a Box folder." - ) - add_common_auth_args(get_folder_items) - get_folder_items.add_argument("--folder-id", required=True) - get_folder_items.add_argument("--limit", type=int, default=20) - get_folder_items.add_argument("--offset", type=int, default=0) - get_folder_items.add_argument( - "--fields", - nargs="*", - help="Optional list of Box fields to request.", - ) - get_folder_items.set_defaults(handler=handle_get_folder_items) - - search = subparsers.add_parser("search", help="Search Box content.") - add_common_auth_args(search) - search.add_argument("--query", required=True) - search.add_argument("--limit", type=int, default=10) - search.add_argument("--offset", type=int, default=0) - search.add_argument("--type", choices=["file", "folder", "web_link"]) - search.add_argument("--ancestor-folder-ids", nargs="*") - search.add_argument("--content-types", nargs="*") - search.add_argument("--fields", nargs="*") - search.set_defaults(handler=handle_search) - - create_folder = subparsers.add_parser( - "create-folder", help="Create a Box folder." - ) - add_common_auth_args(create_folder) - create_folder.add_argument("--parent-folder-id", required=True) - create_folder.add_argument("--name", required=True) - create_folder.add_argument("--fields", nargs="*") - create_folder.set_defaults(handler=handle_create_folder) - - upload_file = subparsers.add_parser("upload-file", help="Upload a file to Box.") - add_common_auth_args(upload_file) - upload_file.add_argument( - "--upload-base-url", - default=os.environ.get("BOX_UPLOAD_BASE_URL", DEFAULT_UPLOAD_BASE), - help=f"Box upload base URL. Defaults to {DEFAULT_UPLOAD_BASE}.", - ) - upload_file.add_argument("--folder-id", required=True) - upload_file.add_argument("--file", required=True) - upload_file.add_argument("--name") - upload_file.add_argument("--fields", nargs="*") - upload_file.set_defaults(handler=handle_upload_file) - - move_item = subparsers.add_parser( - "move-item", help="Move a file or folder to a different parent folder." - ) - add_common_auth_args(move_item) - move_item.add_argument("--item-type", required=True, choices=["file", "folder"]) - move_item.add_argument("--item-id", required=True) - move_item.add_argument("--parent-folder-id", required=True) - move_item.add_argument("--fields", nargs="*") - move_item.set_defaults(handler=handle_move_item) - - create_shared_link = subparsers.add_parser( - "create-shared-link", help="Create or update a shared link." - ) - add_common_auth_args(create_shared_link) - create_shared_link.add_argument( - "--item-type", required=True, choices=["file", "folder"] - ) - create_shared_link.add_argument("--item-id", required=True) - create_shared_link.add_argument( - "--access", choices=["open", "company", "collaborators"] - ) - create_shared_link.add_argument( - "--allow-download", - type=parse_bool, - default=None, - metavar="{true,false}", - help="Set to true or false.", - ) - create_shared_link.add_argument( - "--unshared-at", - help="Optional ISO-8601 expiration timestamp.", - ) - create_shared_link.set_defaults(handler=handle_create_shared_link) - - args = parser.parse_args() - args.token = get_token(args.token) - args.handler(args) - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/plugins/brand24/.app.json b/plugins/brand24/.app.json deleted file mode 100644 index 164eb44e5..000000000 --- a/plugins/brand24/.app.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "apps": { - "brand24": { - "id": "asdk_app_695ba18f3294819196bbe3bdc5630bf3" - } - } -} diff --git a/plugins/brand24/.codex-plugin/plugin.json b/plugins/brand24/.codex-plugin/plugin.json deleted file mode 100644 index 241bd2bf4..000000000 --- a/plugins/brand24/.codex-plugin/plugin.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "brand24", - "version": "1.0.3", - "description": "The Brand24 app in Codex lets marketing and PR teams instantly explore brand mentions, sentiment, and med...", - "author": { - "name": "Brand24 Global Inc.", - "url": "https://brand24.com" - }, - "homepage": "https://brand24.com", - "repository": "https://github.com/openai/plugins", - "license": "MIT", - "keywords": [], - "apps": "./.app.json", - "interface": { - "displayName": "Brand24", - "shortDescription": "The Brand24 app in Codex lets marketing and PR teams instantly explore brand mentions, sentiment, and med...", - "longDescription": "The Brand24 app in Codex lets marketing and PR teams instantly explore brand mentions, sentiment, and media coverage using simple prompts.\nSummarize conversations, track brand reputation, analyze trends over time, and uncover key discussion sources across social media, news, blogs, and forums without leaving Codex.\n\nFrom spotting emerging issues to understanding audience perception and campaign impact, Codex interprets Brand24 data in real time, helping marketers and PR specialists move from monitoring to insight and action with no extra setup.", - "developerName": "Brand24 Global Inc.", - "category": "Productivity", - "capabilities": [], - "websiteURL": "https://brand24.com", - "privacyPolicyURL": "https://brand24.com/privacy-policy/", - "termsOfServiceURL": "https://brand24.com/terms/", - "defaultPrompt": [ - "Online popularity of biggest sports shoe" - ], - "screenshots": [], - "composerIcon": "./assets/logo.png", - "logo": "./assets/logo.png", - "logoDark": "./assets/logo-dark.png" - } -} diff --git a/plugins/brand24/assets/logo-dark.png b/plugins/brand24/assets/logo-dark.png deleted file mode 100644 index 70a25e7ba..000000000 Binary files a/plugins/brand24/assets/logo-dark.png and /dev/null differ diff --git a/plugins/brand24/assets/logo.png b/plugins/brand24/assets/logo.png deleted file mode 100644 index 70a25e7ba..000000000 Binary files a/plugins/brand24/assets/logo.png and /dev/null differ diff --git a/plugins/brex/.app.json b/plugins/brex/.app.json deleted file mode 100644 index 9ce7e26f3..000000000 --- a/plugins/brex/.app.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "apps": { - "brex": { - "id": "asdk_app_6961bc9309ec819199ce7ce38b7d3bf1" - } - } -} diff --git a/plugins/brex/.codex-plugin/plugin.json b/plugins/brex/.codex-plugin/plugin.json deleted file mode 100644 index bc01ac4b9..000000000 --- a/plugins/brex/.codex-plugin/plugin.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "brex", - "version": "1.0.3", - "description": "Connect Brex to Codex and review your company finances through natural conversation \u2014 at Codex speed.", - "author": { - "name": "Brex Inc.", - "url": "https://brex.com" - }, - "homepage": "https://brex.com", - "repository": "https://github.com/openai/plugins", - "license": "MIT", - "keywords": [], - "apps": "./.app.json", - "interface": { - "displayName": "Brex", - "shortDescription": "Connect Brex to Codex and review your company finances through natural conversation \u2014 at Codex speed.", - "longDescription": "Connect Brex to Codex and review your company finances through natural conversation \u2014 at Codex speed.\n\nFor finance teams: Analyze spend, detect anomalies, and run custom queries and reports instantly to accelerate decisions and do more with less.\n\nFor employees: See how much you can spend, ask policy questions, check reimbursement status, manage travel, and more right in Codex.\n\nAccess is role-aware by default: employees see only what applies to them, while admins retain full visibility and control.", - "developerName": "Brex Inc.", - "category": "Finance", - "capabilities": [], - "websiteURL": "https://brex.com", - "privacyPolicyURL": "https://www.brex.com/legal/privacy", - "termsOfServiceURL": "https://www.brex.com/legal/platform-agreement", - "defaultPrompt": [ - "How much did I spend on Delta last year" - ], - "screenshots": [], - "composerIcon": "./assets/logo.png", - "logo": "./assets/logo.png", - "logoDark": "./assets/logo-dark.png" - } -} diff --git a/plugins/brex/assets/logo-dark.png b/plugins/brex/assets/logo-dark.png deleted file mode 100644 index e57864cbd..000000000 Binary files a/plugins/brex/assets/logo-dark.png and /dev/null differ diff --git a/plugins/brex/assets/logo.png b/plugins/brex/assets/logo.png deleted file mode 100644 index 6b1b65c51..000000000 Binary files a/plugins/brex/assets/logo.png and /dev/null differ diff --git a/plugins/brighthire/.app.json b/plugins/brighthire/.app.json deleted file mode 100644 index 24c3df9bc..000000000 --- a/plugins/brighthire/.app.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "apps": { - "brighthire": { - "id": "asdk_app_6a0b4c582dd881919fd81aeec8796674" - } - } -} diff --git a/plugins/brighthire/.codex-plugin/plugin.json b/plugins/brighthire/.codex-plugin/plugin.json deleted file mode 100644 index 462411174..000000000 --- a/plugins/brighthire/.codex-plugin/plugin.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "name": "brighthire", - "version": "0.1.1", - "description": "Connect Codex to BrightHire interview intelligence data through the BrightHire app connector.", - "author": { - "name": "BrightHire", - "email": "support@brighthire.com", - "url": "https://www.brighthire.com" - }, - "homepage": "https://www.brighthire.com", - "repository": "https://github.com/brighthire/brighthire-codex-plugin", - "license": "MIT", - "keywords": [ - "brighthire", - "interviews", - "hiring", - "recruiting", - "codex" - ], - "skills": "./skills/", - "apps": "./.app.json", - "interface": { - "displayName": "BrightHire", - "shortDescription": "Search and analyze BrightHire interviews, candidates, calls, and hiring data.", - "longDescription": "Use BrightHire from Codex to retrieve interview intelligence context, inspect calls and candidates, and support hiring workflows with governed access to BrightHire data.", - "developerName": "BrightHire", - "category": "Productivity", - "capabilities": [ - "Interactive", - "Read" - ], - "websiteURL": "https://www.brighthire.com", - "privacyPolicyURL": "https://brighthire.com/privacy-policy/", - "termsOfServiceURL": "https://brighthire.com/terms-of-service/", - "defaultPrompt": [ - "Use BrightHire to find recent interviews for this candidate.", - "Search BrightHire for calls related to this role.", - "Summarize the BrightHire interview context for this hiring decision." - ], - "brandColor": "#2563EB", - "composerIcon": "./assets/icon.svg", - "logo": "./assets/logo.png", - "screenshots": [] - } -} diff --git a/plugins/brighthire/assets/icon.svg b/plugins/brighthire/assets/icon.svg deleted file mode 100644 index 740359447..000000000 --- a/plugins/brighthire/assets/icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/plugins/brighthire/assets/logo.png b/plugins/brighthire/assets/logo.png deleted file mode 100644 index 50b591614..000000000 Binary files a/plugins/brighthire/assets/logo.png and /dev/null differ diff --git a/plugins/brighthire/assets/logo.svg b/plugins/brighthire/assets/logo.svg deleted file mode 100644 index 1a00424bd..000000000 --- a/plugins/brighthire/assets/logo.svg +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/plugins/brighthire/skills/brighthire/SKILL.md b/plugins/brighthire/skills/brighthire/SKILL.md deleted file mode 100644 index 8c986b75a..000000000 --- a/plugins/brighthire/skills/brighthire/SKILL.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -name: brighthire -description: Use BrightHire tools when a user asks about BrightHire interview intelligence, calls, candidates, roles, scorecards, transcripts, hiring decisions, or organization-level interview data. ---- - -# BrightHire - -Use BrightHire tools when the user asks for information stored in BrightHire or asks Codex to reason about interview intelligence from BrightHire data. - -Good fits include: - -- Finding calls, interviews, candidates, roles, interviewers, scorecards, or transcripts. -- Summarizing interview context for a hiring decision. -- Looking up evidence from BrightHire before answering questions about a candidate or role. -- Comparing interview feedback, themes, concerns, or evidence across calls. - -Before using BrightHire data, identify what entity the user means: candidate, role, organization, call, interviewer, or date range. If the request is ambiguous and multiple BrightHire records may match, ask a concise clarifying question or search broadly and present the likely matches. - -Treat BrightHire content as sensitive customer data. Do not expose more candidate, interviewer, or organization information than the user asked for. Prefer concise summaries with links or identifiers when available, and avoid copying long transcript passages unless the user explicitly needs exact evidence. - -If a BrightHire tool fails because authentication is missing or expired, tell the user they need to connect or re-authenticate the BrightHire plugin. Do not ask for raw API tokens in chat. diff --git a/plugins/brighthire/skills/brighthire/agents/openai.yaml b/plugins/brighthire/skills/brighthire/agents/openai.yaml deleted file mode 100644 index 556d44231..000000000 --- a/plugins/brighthire/skills/brighthire/agents/openai.yaml +++ /dev/null @@ -1,10 +0,0 @@ -interface: - display_name: "BrightHire" - short_description: "Search and analyze BrightHire interview intelligence" - icon_small: "../../assets/icon.svg" - icon_large: "../../assets/logo.svg" - brand_color: "#2563EB" - default_prompt: "Use $brighthire to find interviews, candidates, calls, scorecards, and transcripts from BrightHire." - -policy: - allow_implicit_invocation: true diff --git a/plugins/calendly/.app.json b/plugins/calendly/.app.json deleted file mode 100644 index 0afe0009b..000000000 --- a/plugins/calendly/.app.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "apps": { - "calendly": { - "id": "asdk_app_69d7f67021c88191bb8aac736eff6cb3" - } - } -} diff --git a/plugins/calendly/.codex-plugin/plugin.json b/plugins/calendly/.codex-plugin/plugin.json deleted file mode 100644 index 8c1b6572c..000000000 --- a/plugins/calendly/.codex-plugin/plugin.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "calendly", - "version": "1.0.2", - "description": "Scheduling links, availability, and bookings", - "author": { - "name": "Calendly", - "url": "https://calendly.com" - }, - "homepage": "https://calendly.com", - "repository": "https://github.com/openai/plugins", - "license": "MIT", - "keywords": [ - "calendly", - "calendar", - "scheduling", - "meetings", - "availability", - "events" - ], - "apps": "./.app.json", - "interface": { - "displayName": "Calendly", - "shortDescription": "Scheduling links, availability, and bookings", - "longDescription": "Bring Calendly into ChatGPT to take scheduling actions through simple prompts. Create and update event types, generate scheduling links, adjust availability, book or cancel meetings, and more — right in ChatGPT.", - "developerName": "Calendly", - "category": "Productivity", - "capabilities": [], - "websiteURL": "https://calendly.com", - "privacyPolicyURL": "https://calendly.com/legal/privacy-notice", - "termsOfServiceURL": "https://calendly.com/legal/customer-terms-conditions", - "brandColor": "#006BFF", - "defaultPrompt": [ - "Find my upcoming Calendly meetings and summarize who I am meeting with.", - "Check Calendly availability for this week and suggest open meeting slots.", - "Review recent Calendly events and summarize attendee details and follow-ups." - ], - "screenshots": [], - "composerIcon": "./assets/app-icon.png", - "logo": "./assets/app-icon.png" - } -} diff --git a/plugins/calendly/assets/app-icon.png b/plugins/calendly/assets/app-icon.png deleted file mode 100644 index 875956953..000000000 Binary files a/plugins/calendly/assets/app-icon.png and /dev/null differ diff --git a/plugins/canva/.app.json b/plugins/canva/.app.json index a29f6c5b5..30845bf88 100644 --- a/plugins/canva/.app.json +++ b/plugins/canva/.app.json @@ -1,8 +1,7 @@ { "apps": { "canva": { - "id": "connector_68df33b1a2d081918778431a9cfca8ba", - "required": false + "id": "connector_68df33b1a2d081918778431a9cfca8ba" } } -} +} \ No newline at end of file diff --git a/plugins/canva/.codex-plugin/plugin.json b/plugins/canva/.codex-plugin/plugin.json index fb8ae5ac1..d1433dde9 100644 --- a/plugins/canva/.codex-plugin/plugin.json +++ b/plugins/canva/.codex-plugin/plugin.json @@ -1,33 +1,27 @@ { - "name": "canva", - "version": "1.0.2", - "description": "Search, create, edit designs", + "apps": "./.app.json", "author": { - "name": "Canva", - "url": "https://www.canva.com" + "name": "Canva Pty Ltd." }, - "homepage": "https://www.canva.com", - "repository": "https://github.com/openai/plugins", - "license": "MIT", - "keywords": [], - "skills": "./skills/", - "apps": "./.app.json", + "description": "Bring your Canva design workflow into Codex and create, refine, and review designs through natural conversation.\nAvailable skills:\nResize for social media: Adapt a design for Facebook, Instagram, and LinkedIn in one step.\nBulk create: Generate multiple designs from spreadsheet data using a brand template.\nEdit designs: Update text, media, and formatting, or reposition and resize elements.\nGet design feedback: Receive structured feedback on visual hierarchy, layout, readability, consistency, and accessibility.\nImplement feedback: Review comment threads and apply clear, actionable changes.\nBrand check: Check colors, fonts, logo usage, and copy against your Brand Kit.\nOnce installed, describe what you want in plain language or invoke Canva directly with @Canva.\nTry prompts like:\n- @Canva, resize this design for social media.\n- @Canva, create designs from this spreadsheet using my brand template.\n- @Canva, update the headline in this design.\n- @Canva, review this design\u2019s visual hierarchy and readability.\n- @Canva, implement the feedback in the comment threads.\n- @Canva, check this design against my Brand Kit.\n\nRequirements: Connect your Canva account to use the plugin. Bulk Create requires an eligible Canva Enterprise plan.\n\nWant to take a more hands-on approach? Instantly open your design in Canva\u2019s powerful editor for the final touches.", "interface": { - "displayName": "Canva", - "developerName": "Canva", - "shortDescription": "Search, create, edit designs", - "longDescription": "Search, create, edit designs", - "category": "Creativity", "capabilities": [], - "websiteURL": "https://www.canva.com", - "privacyPolicyURL": "https://www.canva.com/policies/privacy-policy/", - "termsOfServiceURL": "https://www.canva.com/policies/terms-of-use/", - "brandColor": "#00C4CC", + "category": "Creativity", "defaultPrompt": [ - "Create or adapt a Canva design for presentations, resized social variants, or translated versions" + "Create some social media posts based on our October campaign brief", + "Use these latest investor meeting notes to make a Q1 2026 pitch deck", + "Can you resize this poster as an Instagram post for my online store?" ], - "screenshots": [], - "composerIcon": "./assets/app-icon.png", - "logo": "./assets/app-icon.png" - } -} + "developerName": "Canva Pty Ltd.", + "displayName": "Canva", + "longDescription": "Bring your Canva design workflow into Codex and create, refine, and review designs through natural conversation.\nAvailable skills:\nResize for social media: Adapt a design for Facebook, Instagram, and LinkedIn in one step.\nBulk create: Generate multiple designs from spreadsheet data using a brand template.\nEdit designs: Update text, media, and formatting, or reposition and resize elements.\nGet design feedback: Receive structured feedback on visual hierarchy, layout, readability, consistency, and accessibility.\nImplement feedback: Review comment threads and apply clear, actionable changes.\nBrand check: Check colors, fonts, logo usage, and copy against your Brand Kit.\nOnce installed, describe what you want in plain language or invoke Canva directly with @Canva.\nTry prompts like:\n- @Canva, resize this design for social media.\n- @Canva, create designs from this spreadsheet using my brand template.\n- @Canva, update the headline in this design.\n- @Canva, review this design\u2019s visual hierarchy and readability.\n- @Canva, implement the feedback in the comment threads.\n- @Canva, check this design against my Brand Kit.\n\nRequirements: Connect your Canva account to use the plugin. Bulk Create requires an eligible Canva Enterprise plan.\n\nWant to take a more hands-on approach? Instantly open your design in Canva\u2019s powerful editor for the final touches.", + "privacyPolicyURL": "https://www.canva.com/policies/privacy-policy/", + "shortDescription": "Create, review, edit designs", + "supportURL": "https://www.canva.com/help/contact-us/", + "termsOfServiceURL": "https://www.canva.com/policies/terms-of-use/", + "websiteURL": "https://www.canva.com" + }, + "name": "canva", + "skills": "./skills", + "version": "14.0.0" +} \ No newline at end of file diff --git a/plugins/canva/assets/app-icon.png b/plugins/canva/assets/app-icon.png deleted file mode 100644 index 0e29ead9e..000000000 Binary files a/plugins/canva/assets/app-icon.png and /dev/null differ diff --git a/plugins/canva/assets/canva-small.svg b/plugins/canva/assets/canva-small.svg deleted file mode 100644 index 76f521233..000000000 --- a/plugins/canva/assets/canva-small.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/plugins/canva/assets/canva.svg b/plugins/canva/assets/canva.svg deleted file mode 100644 index 5ac7ef33e..000000000 --- a/plugins/canva/assets/canva.svg +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/plugins/canva/skills/canva-brand-check/SKILL.md b/plugins/canva/skills/canva-brand-check/SKILL.md new file mode 100644 index 000000000..7392e054b --- /dev/null +++ b/plugins/canva/skills/canva-brand-check/SKILL.md @@ -0,0 +1,78 @@ +--- +name: canva-brand-check +description: Check a Canva design against a brand kit and report where it diverges — off-palette colors, non-brand fonts, logo misuse, and off-tone copy. Read-only; makes no changes. Use when the user asks "is this on brand", "check this against our brand kit", "do a brand review", "does this match our brand guidelines", or "brand-check my design". +--- + +# Brand Checker + +Compare a design against the user's brand kit and report, point by point, where it follows the brand and where it drifts. This is a specialised, brand-aware version of `canva-design-feedback`: same read-only "read the design, then critique" core, but the rubric is the brand kit. It **never edits** the design. + +## The brand-kit data gap — read this first + +`Canva:list-brand-kits` is documented to return brand kit **IDs, names, and thumbnails**. It may NOT return the machine-readable palette (hex values) and font families. Your approach depends on what you actually get back: + +- **If the kit exposes colors/fonts** → use those exact hex codes and font names as the rubric (precise check). +- **If it only exposes a thumbnail/name** → you cannot do an exact hex/font match. Fall back to a **visual** comparison against the kit thumbnail, and ASK the user to paste their brand colors (hex) and fonts so you can check precisely. Be explicit that, without those, the color/font findings are approximate. + +Never invent brand colors or fonts. If you don't have the real values, say so. + +## Reading the design's actual colors and fonts + +`Canva:get-design-content` returns text only — not colors or fonts. + +- A **read-only** editing transaction (`Canva:start-editing-transaction` → inspect → `Canva:cancel-editing-transaction`, always cancel) reliably gives element **text, positions, and sizes**. +- **Colors and fonts are NOT reliably exposed** by the transaction — tested: it often returns only text + position + dimension, with no color/font attributes. So the **thumbnail is your primary evidence** for color and typography. Use any style data the payload happens to include, but never assert a design's hex or font as fact unless it was actually in the payload. + +Because both the brand kit (see the gap above) AND the design itself frequently lack machine-readable colors/fonts, brand-check is often a **visual** comparison (design thumbnail vs. brand-kit thumbnail) plus the user-supplied palette/fonts. Use `Canva:get-design-thumbnail` for logo placement and overall visual tone. + +## Workflow + +### Step 1: Resolve the design +Short link → `Canva:resolve-shortlink`; full URL → extract ID; raw `D...` ID → use directly; otherwise ask. + +### Step 2: Get the brand kit +- `Canva:list-brand-kits`. If several, ask which one (or infer from team/context). +- Capture whatever the kit exposes (name, thumbnail, and colors/fonts if present). +- Apply the data-gap handling above. If scopes are missing (e.g. "Missing scopes: [brandkit:read]"), tell the user to disconnect and reconnect the Canva connector to refresh the token. + +### Step 3: Read the design +- `Canva:get-design-thumbnail` for visual/logo/tone. +- Read-only transaction (start → inspect → cancel) for the actual colors and fonts in use. + +### Step 4: Compare against the brand +Check each dimension and mark **On brand / Off brand / Can't verify**: + +- **Color** — are fills/text/accents within the brand palette? Flag off-palette hexes (and the nearest brand color). +- **Typography** — do fonts match brand fonts? Flag non-brand families and inconsistent sizing/weight usage. +- **Logo** — present where expected, correct version, not stretched/recolored/crowded (visual check from thumbnail). +- **Tone & copy** — does the wording match the brand voice the user describes? +- **Consistency** — is brand application consistent across all pages? + +Use **Can't verify** honestly whenever the kit didn't expose the data and the user hasn't supplied it. + +### Step 5: Report +``` +## Brand check — "" vs "" + +Overall: ⚠️ Mostly on brand, 3 issues + +### Off brand +- [Color] Page 2 heading is #1A73E8 — not in palette. Nearest brand color: #0B5CD7. +- [Font] Page 4 body uses Arial; brand body font is Inter. + +### Can't verify (need input) +- Brand kit didn't expose hex values. Paste your palette + fonts for an exact check, + or confirm the colors above against your guidelines. + +### On brand +- Logo placement and cover treatment match the kit. +``` + +### Step 6: Offer to fix +Offer to correct the API-fixable issues via **`canva-edit-design`** — note that the API can change **text color** and font **size/weight/style**, but CANNOT change font **family** or background colors (those are manual in Canva; see `canva-edit-design`). + +## Rules +- Read-only: always `cancel-editing-transaction` after inspecting. Never commit. +- Never fabricate brand values — use the kit's real data or what the user provides, otherwise mark **Can't verify**. +- Always name the specific page/element and the offending value vs. the brand value. +- Distinguish hard violations (wrong logo, off-palette color) from soft ones (slightly inconsistent spacing). diff --git a/plugins/canva/skills/canva-branded-presentation/SKILL.md b/plugins/canva/skills/canva-branded-presentation/SKILL.md index 034830ed9..4927aab94 100644 --- a/plugins/canva/skills/canva-branded-presentation/SKILL.md +++ b/plugins/canva/skills/canva-branded-presentation/SKILL.md @@ -1,50 +1,62 @@ --- name: canva-branded-presentation -description: Create on-brand Canva presentations from a brief, outline, existing Canva doc, or design link. Use when the user wants a branded slide deck, wants to turn notes into a presentation, or needs a presentation generated in Canva with the right brand kit and a clear slide plan. +description: Create on-brand Canva presentations from an outline or brief. Use when the user asks to create a branded presentation, make an on-brand deck, turn an outline into slides, or generate a presentation from a brief. Input can be text directly in the message, a Canva design ID, a reference to a Canva doc by name, or a Canva design link (e.g., https://www.canva.com/design/...). --- -# Canva Branded Presentation +# Canva Branded Presentation Creator -## Overview - -Use this skill to turn a brief, outline, or existing Canva content into a branded presentation. Gather the source content first, choose the right brand kit, and generate presentation candidates before creating the editable deck. - -## Preferred Deliverables - -- A clear presentation brief with title, scope, key messages, and a narrative arc. -- A slide plan with concrete titles, goals, bullets, and visual guidance. -- A new editable Canva presentation created from the user's preferred candidate. +Create professional, on-brand presentations in Canva from user-provided outlines or briefs. ## Workflow -1. Identify the content source before generating. Accept direct text, a Canva design link, or a Canva document/design name that can be found through search. -2. Read the source content when it lives in Canva. Use the available Canva search and editing tools to locate the design, open it, and extract the material that should drive the deck. -3. List the available brand kits. If there is only one, use it automatically. If there are multiple, ask the user to choose before generating. -4. Build a strong generation prompt. Include a working title, topic, key messages, visual style, story arc, and a slide-by-slide plan. -5. Generate presentation candidates in Canva and show the options to the user before creating the final design. -6. Create the editable presentation from the selected candidate and return the Canva link. - -## Write Safety - -- Keep the original source design untouched unless the user explicitly asks to modify it. -- If multiple matching source designs or brand kits appear, identify the exact one before generating. -- Preserve specific names, dates, metrics, and claims from the source content unless the user asks to change them. -- If the brief is sparse, expand it thoughtfully, but call out major assumptions that shape the narrative. - -## Output Conventions - -- When helpful, summarize the deck direction before generation: title, audience, key message, and slide count. -- For larger decks, present a concise slide plan before or alongside candidate generation. -- When showing results, distinguish clearly between generated candidates and the final editable deck. -- Return the final Canva design link once the chosen candidate has been created. - -## Example Requests - -- "Create a branded presentation from this launch outline." -- "Turn this Canva doc into a polished deck using our brand kit." -- "Make an on-brand sales presentation from this brief." -- "Generate a presentation from this Canva design link." - -## Light Fallback - -If the source design or brand kit cannot be found, say that Canva access may be unavailable or pointed at the wrong account and ask the user to reconnect or identify the right design or brand kit. +1. **Get the content source** + - If the user provides text directly, use that as the outline/brief + - If the user provides a **Canva design ID** directly (typically starts with `D`, e.g. `DABcd1234ef`), use it as `design_id` with `Canva:start-editing-transaction` to read its contents; **do not** use `Canva:search-designs` for a raw ID + - If the user provides a Canva design link (e.g., `https://www.canva.com/design/DAG.../...`), extract the design ID from the URL and use `Canva:start-editing-transaction` to read its contents + - If the user references a Canva doc by name, use `Canva:search-designs` to find it, then `Canva:start-editing-transaction` to read its contents + +2. **List available brand kits** + - Call `Canva:list-brand-kits` to retrieve the user's brand kits + - If only one brand kit exists, use it automatically without asking + - If multiple brand kits exist, present the options and ask the user to select one + +3. **Generate the presentation** + - Call `Canva:generate-design` with: + - `design_type`: "presentation" + - `brand_kit_id`: the selected brand kit ID + - `query`: a detailed prompt following the presentation format below + - Show the generated candidates to the user + +4. **Finalize** + - Ask the user which candidate they prefer + - Call `Canva:create-design-from-candidate` to create the editable design + - Provide the user with the link to their new presentation + +## Presentation Query Format + +Structure the query for `Canva:generate-design` with these sections: + +**Presentation Brief** +- Title: working title for the deck +- Topic/Scope: 1-2 lines describing the subject +- Key Messages: 3-5 main takeaways +- Style Guide: tone and imagery style based on the brief + +**Narrative Arc** +One paragraph describing the story flow (e.g., Hook → Problem → Solution → Proof → CTA). + +**Slide Plan** +For each slide include: +- Slide N — "Exact Title" +- Goal: one sentence on the slide's purpose +- Bullets (3-6): short, parallel phrasing with specifics +- Visuals: explicit recommendation (chart type, diagram, image subject) +- Speaker Notes: 2-4 sentences of narrative detail + +## Notes + +- If multiple brand kits exist, confirm selection before generating; if only one, use it automatically +- If the outline is sparse, expand it into a complete slide plan with reasonable content +- For briefs (narrative descriptions), extract key points and structure them into slides +- Aim for clear, action-oriented slide titles +- Autofill requires a Canva Enterprise plan diff --git a/plugins/canva/skills/canva-branded-presentation/agents/openai.yaml b/plugins/canva/skills/canva-branded-presentation/agents/openai.yaml deleted file mode 100644 index e64824044..000000000 --- a/plugins/canva/skills/canva-branded-presentation/agents/openai.yaml +++ /dev/null @@ -1,7 +0,0 @@ -interface: - display_name: "Branded Presentation" - short_description: "Create on-brand decks from briefs" - icon_small: "./assets/canva-small.svg" - icon_large: "./assets/canva.svg" - brand_color: "#00C4CC" - default_prompt: "Use $canva-branded-presentation to turn my brief or Canva doc into an on-brand presentation." diff --git a/plugins/canva/skills/canva-branded-presentation/assets/canva-small.svg b/plugins/canva/skills/canva-branded-presentation/assets/canva-small.svg deleted file mode 100644 index 76f521233..000000000 --- a/plugins/canva/skills/canva-branded-presentation/assets/canva-small.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/plugins/canva/skills/canva-branded-presentation/assets/canva.svg b/plugins/canva/skills/canva-branded-presentation/assets/canva.svg deleted file mode 100644 index 5ac7ef33e..000000000 --- a/plugins/canva/skills/canva-branded-presentation/assets/canva.svg +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/plugins/canva/skills/canva-bulk-create/SKILL.md b/plugins/canva/skills/canva-bulk-create/SKILL.md new file mode 100644 index 000000000..643b6a490 --- /dev/null +++ b/plugins/canva/skills/canva-bulk-create/SKILL.md @@ -0,0 +1,124 @@ +--- +name: canva-bulk-create +description: Bulk-create Canva designs from tabular data using a brand template with autofill fields, producing one design per row. Use when users say "bulk create designs from this CSV", "generate one design per row", "create a design for each product", "batch generate from a template", or "autofill a template from a spreadsheet". Accepts any tabular data source — uploaded files, pasted tables, JSON, or URLs. +--- + +# Canva Bulk Design Creation + +Create one Canva design per row of data by autofilling a brand template with data tags. + +## Workflow + +### Step 1: Get the Data + +Accept data in any form the user provides and extract a list of rows with named columns: + +- **Uploaded file**: read the file and extract headers and rows +- **Pasted data**: parse markdown tables, tab-separated values, or JSON arrays directly from the chat +- **URL**: fetch the resource and parse the response as tabular data + +If no data has been provided, ask the user to share it in whatever format is convenient for them. + +Once parsed, show the user: +- Column headers found +- Number of rows (= number of designs that will be created) +- A preview of the first few rows + +### Step 2: Select the Brand Template + +If the user hasn't specified a template, search for autofill-capable ones: + +``` +Canva:search-brand-templates dataset=non_empty +``` + +Show the results and ask the user to pick one. If they already named or described a template, search with that query. + +### Step 3: Inspect the Template Schema + +``` +Canva:get-brand-template-dataset template_id= +``` + +This returns the field names and types (text, image, chart) that the template expects. + +### Step 4: Map CSV Columns to Template Fields + +Present a mapping table to the user: + +| Template Field | Type | Matched CSV Column | Notes | +|---|---|---|---| +| `product_name` | text | `Product Name` | auto-matched | +| `price` | text | `Price` | auto-matched | +| `hero_image` | image | *(none)* | no match — image fields need asset IDs | + +**Matching rules:** +- Do case-insensitive, fuzzy matching between CSV headers and template field names +- Text fields can be filled directly from CSV string values +- Image fields require a Canva asset ID — see **Image Field Handling** below +- Chart fields require structured data — treat as advanced and ask the user for clarification + +Confirm the mapping with the user before proceeding, especially if there are unmapped fields or ambiguous matches. + +#### Image Field Handling + +There are two ways a CSV can supply images for image-type template fields: + +**Pattern A — CSV has a Canva asset ID column** (e.g. `image_asset_id`): +Use the asset ID value directly in the `autofill-design` call: +```json +{ "image": { "type": "image", "asset_id": "" } } +``` + +**Pattern B — CSV has an image URL column** (e.g. `image_url`): +URLs cannot be passed directly to `autofill-design`. Upload each URL to Canva first using `Canva:upload-asset-from-url`, capture the returned asset ID, then use it in the autofill call. Do the upload immediately before creating that row's design so failures stay localised. + +**Pattern C — No image column in CSV:** +Ask the user whether to skip the image field (template default image stays) or abort. Skipping is safe — just omit the image key from the `data` payload entirely. + +### Step 5: Bulk Create — One Design per Row + +Loop through every CSV row and call `Canva:autofill-design` for each one. Call them **sequentially**, not all at once — the API may have rate limits and sequential calls are easier to debug. + +For each row: + +1. If the row has an image URL column (Pattern B), first call `Canva:upload-asset-from-url` to get a Canva asset ID. +2. Build the `data` payload from the confirmed field mapping: + +```json +{ + "text_field_name": { "type": "text", "text": "" }, + "image_field_name": { "type": "image", "asset_id": "" } +} +``` + +3. Call `Canva:autofill-design` with the template ID, data payload, and a descriptive title using the row number or a meaningful column value (e.g. `"Bulk Design - Row 3 - "`). + +Track results as you go: + +``` +Row 1 / 50: Created — +Row 2 / 50: Created — +Row 3 / 50: Failed — +``` + +### Step 6: Report Results + +After all rows are processed, summarise: + +- Total rows attempted +- Successes (with links) +- Failures (with row number and reason) + +Offer to save a summary CSV with columns: `row`, `status`, `design_url`, `error`. + +## Notes + +- Autofill requires a Canva Enterprise plan. +- For large CSVs (50+ rows), warn the user upfront that this will make N API calls and may take a while. Offer to do a test run on the first 3 rows before proceeding with the full batch. +- If some rows fail, continue with the rest — don't abort the whole batch. +- Skip rows where all mapped fields are empty and warn the user about them. +- If no CSV column matches a required template field, ask the user to confirm which column to use or whether to skip that field. +- Template field names are case-sensitive in the API — use the exact keys from `get-brand-template-dataset`. +- There is no "undo bulk create" — warn the user before starting large runs. +- Designs created this way are full Canva designs the user can further edit in their account. diff --git a/plugins/canva/skills/canva-design-feedback/SKILL.md b/plugins/canva/skills/canva-design-feedback/SKILL.md new file mode 100644 index 000000000..ce1c52b53 --- /dev/null +++ b/plugins/canva/skills/canva-design-feedback/SKILL.md @@ -0,0 +1,68 @@ +--- +name: canva-design-feedback +description: Read a Canva design and return structured, actionable design feedback — visual hierarchy, copy/messaging, layout & spacing, consistency, readability, and accessibility. Read-only; makes no changes to the design. Use when the user asks to "review my design", "give me feedback on this", "critique my deck/poster/flyer", "how can I improve this design", or "what's wrong with this slide". +--- + +# Get Design Feedback + +Act as a design reviewer: read the design as it actually appears, then return concrete, prioritised feedback the user can act on. This skill is **read-only** — it never edits the design. When the user wants the changes made, hand off to `canva-edit-design` or `canva-implement-feedback`. + +## What you can actually read (and the gap to know about) + +- **`Canva:get-design-content`** returns text (`richtexts`) only — good for copy, headings, and wording, but it does NOT include colors, fonts, sizes, or element positions. +- **`Canva:get-design-thumbnail`** gives you the rendered image — this is how you "see" layout, hierarchy, balance, color, and contrast. Always pull this; visual critique depends on it. +- **Element positions, sizes, and text** are reliably available from a **read-only** editing transaction: `Canva:start-editing-transaction`, inspect the returned `richtexts`/`fills`, then `Canva:cancel-editing-transaction` (never commit — you are not changing anything). Use this for layout/spacing/alignment detail. +- **Colors and fonts are NOT reliably exposed.** Tested: the transaction payload often returns only text + position + dimension per element, with no color or font attributes. So treat the **thumbnail as the primary source** for any color, contrast, or typography judgement, and treat transaction style data as best-effort (use it when present, don't depend on it). Never report a specific hex/font as fact unless the payload actually contained it. + +## Workflow + +### Step 1: Resolve the design +Short link → `Canva:resolve-shortlink`; full URL → extract the ID; raw `D...` ID → use directly; otherwise ask. + +### Step 2: Read the design +- `Canva:get-design` for title and page count. +- `Canva:get-design-thumbnail` (and/or `Canva:get-design-pages`) to see each page. +- `Canva:get-design-content` for the text. +- Optional (typography/color detail): read-only transaction as described above, then cancel it. + +### Step 3: Evaluate across dimensions +Assess the design against these lenses. Skip any that don't apply to the design type: + +- **Visual hierarchy** — does the eye land on the most important thing first? Title/subtitle/body contrast. +- **Layout & spacing** — alignment, balance, crowding, consistent margins/gutters. +- **Copy & messaging** — clarity, length, tone, typos, jargon, a single clear takeaway per page. +- **Consistency** — repeated fonts, sizes, colors, and spacing across pages. +- **Readability & contrast** — text size vs. viewing context, text-on-image legibility, color contrast. +- **Accessibility** — contrast ratios, alt text, text not conveyed by color alone. +- **Fit for purpose** — does it suit the stated channel/audience (a slide ≠ an Instagram post ≠ a flyer)? + +### Step 4: Return structured feedback +Organise findings by **page**, each with a **severity** and a **concrete fix**: + +``` +## Feedback — "" (N pages) + +### Top priorities +1. [High] Page 2 — Title competes with the body text (same size/weight). + Fix: bump the title to ~1.5× and bold it so it reads first. +2. [High] Page 4 — White caption over a light photo is hard to read. + Fix: darken the image or add a scrim; or move the caption to a solid band. + +### Page-by-page +**Page 1** — [Med] Three different accent colors; pick one. [Low] "recieve" → "receive". +**Page 2** — ... + +### What's working +- Consistent margins; strong cover image. +``` + +Use severities **High / Med / Low**. Lead with the few highest-impact items, then the per-page detail. Be specific and located (page + element), not generic ("make it pop"). + +### Step 5: Offer to act +End by offering to implement the API-fixable items via **`canva-edit-design`**, and note which items need manual work in Canva (e.g. font-family or background changes the API can't touch — see `canva-edit-design` for the full CANNOT list). + +## Rules +- Never edit or commit anything — this skill is strictly read-only. If you open a transaction to inspect, always `cancel-editing-transaction`. +- Ground every point in something you actually observed in the thumbnail or content — no generic advice. +- Prioritise. A ranked shortlist beats an exhaustive list the user won't read. +- Be candid but constructive; always pair a problem with a specific fix. diff --git a/plugins/canva/skills/canva-edit-design/SKILL.md b/plugins/canva/skills/canva-edit-design/SKILL.md new file mode 100644 index 000000000..d0aaf0c0f --- /dev/null +++ b/plugins/canva/skills/canva-edit-design/SKILL.md @@ -0,0 +1,76 @@ +--- +name: canva-edit-design +description: Make edits to an existing Canva design — change or fix text, replace/insert/delete images and videos, reformat text (size, weight, style, color, alignment, lists, line height), reposition or resize elements, and update the title. Use when the user wants to change, edit, update, fix, translate, replace, or reformat content in a specific Canva design. This is the safe edit engine that other Canva skills (e.g. implement-feedback) build on. +--- + +# Canva Design Editing + +The canonical, safe way to apply edits to an existing Canva design. Every Canva skill that mutates a design should follow this exact protocol: **start a transaction → perform operations → commit (with approval)**. Changes are draft-only until committed and are PERMANENTLY LOST if not committed. + +## The Transaction Protocol (always these steps, in order) + +1. **`Canva:start-editing-transaction`** — pass the `design_id`. Remember the returned `transaction_id` and the `pages` array; both are required by later calls. ALWAYS show the user the thumbnail(s) returned here. +2. **`Canva:perform-editing-operations`** — apply edits. Pass the `transaction_id`, the `pages` array from the previous response, the `page_index` of the first page being changed, and an `operations` array. Batch multiple operations into a single call wherever possible. +3. **`Canva:commit-editing-transaction`** — save. See the approval gate below. After committing, the `transaction_id` is invalid; a new edit needs a new transaction. +4. **`Canva:cancel-editing-transaction`** — discard the draft instead of saving (e.g. the user rejects the preview, or you opened a transaction only to inspect the design). + +## Capabilities — what the API CAN and CANNOT do + +### CAN (operations on `perform-editing-operations`) +- **Text content**: `replace_text` (whole element), `find_and_replace_text` (substring) +- **Text formatting** (`format_text`): font size, weight (normal/bold), style (normal/italic), color, alignment, line height, underline, strikethrough, links, list level/marker +- **Media**: `update_fill` (replace image/video), `insert_fill` (add image/video), `delete_element` +- **Layout**: `position_element`, `resize_element` +- **Metadata**: `update_title` +- **Autofill mapping**: `update_autofill_field` (fixed-page designs only) + +### CANNOT +- Change font **family/typeface** (only size, weight, style) +- Add **new text elements** (you can only insert media, not new text boxes) +- Change background colors or gradients +- Add, remove, or reorder pages/slides +- Modify animations, transitions, or element opacity (except on newly inserted fills) +- Group/ungroup elements, or restyle shapes (only text inside shapes is editable) + +When a requested change is in the CANNOT list, tell the user it must be done manually in the Canva editor — don't attempt a workaround. + +## Responsive pages — restricted operation set + +Some pages come back marked `is_responsive: true`. On those pages, ONLY these operations are allowed: +`update_title`, `replace_text`, `update_fill`, `delete_element`, `find_and_replace_text`. + +Before calling `perform-editing-operations`, check the `pages` array. If any operation targets a responsive page with an unsupported op (e.g. `format_text`, `position_element`, `resize_element`, `insert_fill`), do NOT make the call — tell the user that operation isn't supported on that page and offer an alternative. + +## The commit approval gate (required) + +`commit-editing-transaction` makes changes permanent. You MUST show the user exactly what changed (and the preview thumbnail) and get explicit approval before committing — e.g. "Here's the preview. Save these changes to your design?" Wait for a clear yes. + +- Do NOT commit without approval. +- Do NOT tell the user changes are saved before the commit call has succeeded. +- After a successful commit, give the user a direct link to open the design in Canva. +- If a commit fails, all changes are lost — start a new transaction to retry. + +> Note for composing skills: a skill that already collects a single up-front approval for a batch of changes (e.g. `canva-implement-feedback`) should treat that approval as covering the commit and NOT ask again. Follow that skill's own confirmation rules; the gate above is the default for direct, ad-hoc edits. + +## Workflow + +### Step 1: Resolve the design +- Short link (`canva.link/...`) → `Canva:resolve-shortlink` to get the URL. +- Full Canva URL → extract the design ID (the segment after `/design/`). +- Raw design ID (starts with `D`) → use directly; do NOT search. +- Nothing provided → ask for the design ID or link. + +### Step 2: Start the transaction and inspect +Call `Canva:start-editing-transaction`. Show the thumbnail(s). Use the returned content to locate the exact `element_id`s you need to target. (If you only needed to look, call `cancel-editing-transaction` and stop.) + +### Step 3: Build and perform operations +Translate the user's request into concrete operations. Confirm scope first when a `find_and_replace_text` string could match in multiple places or contexts — ask which instances they mean. Batch all operations into one `perform-editing-operations` call when you can. + +### Step 4: Preview and commit +Show the resulting thumbnail and a plain-language list of what changed. Ask for approval, then `commit-editing-transaction`. Share the edit link. + +## Rules +- Always remember and reuse the `transaction_id` and `pages` array within a transaction. +- Never leave a transaction uncommitted without telling the user their draft was discarded. +- For destructive ops (`delete_element`, large `find_and_replace_text`), confirm scope before performing. +- Prefer one batched `perform-editing-operations` call over many small ones. diff --git a/plugins/canva/skills/canva-implement-feedback/SKILL.md b/plugins/canva/skills/canva-implement-feedback/SKILL.md new file mode 100644 index 000000000..6bb8a7ce5 --- /dev/null +++ b/plugins/canva/skills/canva-implement-feedback/SKILL.md @@ -0,0 +1,112 @@ +--- +name: canva-implement-feedback +description: Implement reviewer feedback on a Canva design. Reads all comment threads, synthesises what reviewers want, makes the clear-cut changes directly, and flags anything that needs a human decision. Use when the user asks to "implement feedback on my deck", "address comments on a design", "apply review feedback", "fix the comments on my presentation", or "implement the feedback". +--- + +# Feedback to Finished + +A deck has been out for review — stakeholders have left comments scattered across slides. This skill reads every thread, summarises what reviewers actually want, makes the clear-cut changes directly, and flags anything ambiguous for a human decision. + +## Canva Editing API — What You Can and Cannot Do + +Before triaging feedback, you MUST know these constraints. This avoids wasted back-and-forth with the user on changes that are impossible via the API. + +### What the API CAN do (via `perform-editing-operations`) + +- **Text content**: replace entire text elements (`replace_text`), find-and-replace substrings (`find_and_replace_text`) +- **Text formatting**: font size, font weight (bold), font style (italic), text color, text alignment, line height, text decoration (underline), strikethrough, links, list formatting +- **Media**: replace images/videos (`update_fill`), insert new images/videos (`insert_fill`), delete elements (`delete_element`) +- **Layout**: reposition elements (`position_element`), resize elements (`resize_element`) +- **Metadata**: update design title (`update_title`) + +### What the API CANNOT do + +- Change font family/typeface — only size, weight, and style are supported +- Add new text elements — you can only insert media (images/videos), not new text boxes +- Change background colors or gradients +- Add, remove, or reorder pages/slides +- Modify animations or transitions +- Change element opacity (except on newly inserted fills) +- Group/ungroup elements +- Modify shapes (color, border, etc.) — only text within shapes can be edited + +### Triage rule + +When a comment requests something in the "CANNOT do" list, classify it as **Requires manual action**. Don't dwell on the limitation — simply note it in the summary and move on. These are normal; most design reviews will have a mix of API-supported and manual changes. Save the details for the manual changes checklist at the end (Step 7). + +## Workflow + +### Step 1: Resolve the Design + +- If the user provides a short link (`canva.link`), call `Canva:resolve-shortlink` to get the design URL +- If the user provides a full Canva URL, extract the design ID from the URL +- If the user provides a **design ID** directly (typically starts with `D`, e.g. `DABcd1234ef`), use it as `design_id`; **do not** use `Canva:search-designs` for a raw ID +- Otherwise ask for the design ID or link + +### Step 2: Read All Feedback + +- Call `Canva:list-comments` with the design ID to get every comment thread +- For each thread with replies, call `Canva:list-replies` to capture the full conversation +- Call `Canva:get-design-content` to read the current text on every page + +### Step 3: Triage the Feedback + +Classify each comment thread into one of these categories: + +- **Actionable** — a change that the API supports and you can reasonably interpret. Use your best judgement — if a comment says "make the title punchier", rewrite it to be punchier rather than flagging it as ambiguous. If a comment says "fix the spacing", look at the design content and make a reasonable adjustment. Only escalate to the user when you genuinely cannot determine what the reviewer intends (e.g., two reviewers directly contradict each other, or a comment references something you can't find in the design). +- **Requires manual action** — the reviewer wants something the API cannot do (font family change, new text element, background change, page reorder, etc.). Note these briefly in the summary — full details go in the manual changes checklist (Step 7). +- **Resolved** — already addressed, explicitly marked done, or is a positive acknowledgement (e.g., "LGTM", "looks good") + +Present a summary to the user organised by category: what you plan to change, what needs clarification, what must be done manually, and what you're skipping. + +### Step 4: Get User Approval — ONE time only + +- Present the plan and wait for the user to approve +- If the user wants adjustments, update the plan and confirm once more + +**This is the only confirmation point in the entire workflow. Once the user says yes, go.** + +### Step 5: Apply and Commit the Changes + +**Do NOT ask the user again.** They already approved. Execute all of these in sequence immediately: + +- Call `Canva:start-editing-transaction` to begin an editing session +- Call `Canva:perform-editing-operations` to make each approved change (batch all operations in a single call where possible) +- Call `Canva:commit-editing-transaction` to save — do NOT ask "shall I commit?" or "ready to save?" +- Show the thumbnail from the editing response to the user as confirmation + +### Step 7: Present Remaining Manual Changes + +After committing (or if no API-supported changes were possible), present a clear checklist of everything that still needs to be done manually in the Canva editor: + +``` +## Changes to make manually in Canva + +1. **Slide 3 — Change heading font to Montserrat** + Reviewer: @Sarah | Why: API cannot change font family + → Open slide 3, select the heading, change font to Montserrat + +2. **Slide 7 — Add a new text box for the disclaimer** + Reviewer: @James | Why: API cannot add new text elements + → Add a text box below the chart with: "Source: Q3 2025 internal data" + +3. ... +``` + +Include the slide number, what to change, who requested it, and step-by-step instructions so the user can work through the list quickly. + +### Step 8: Resolve Comment Threads + +- After committing, call `Canva:reply-to-comment` on each actionable thread to note what was changed +- For "Requires manual action" threads, reply noting what was done as the closest alternative and what still needs manual attention +- This closes the feedback loop so reviewers can see their comments were addressed + +## Rules + +- Be helpful, not cautious — interpret feedback generously and make your best attempt at a change rather than labelling it "ambiguous" and giving up. The user can always reject your changes in the approval step. +- Only escalate to the user when you genuinely can't figure out the intent — two reviewers directly contradict each other, or a comment references something you can't find in the design +- When reviewers disagree, present both sides and let the user decide +- Show the summary of planned changes and wait for approval ONCE — after that, execute everything without further confirmation +- NEVER ask "shall I commit?", "ready to save?", or any variation — the user's initial approval covers the entire edit-and-commit flow +- Manual changes are normal and expected — don't over-explain or apologise for API limitations, just include them in the checklist +- Batch operations: use a single `perform-editing-operations` call with multiple operations rather than one call per change diff --git a/plugins/canva/skills/canva-resize-for-all-social-media/SKILL.md b/plugins/canva/skills/canva-resize-for-all-social-media/SKILL.md deleted file mode 100644 index 603359957..000000000 --- a/plugins/canva/skills/canva-resize-for-all-social-media/SKILL.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -name: canva-resize-for-all-social-media -description: Resize a Canva design into standard social media formats and prepare export-ready results. Use when the user wants one Canva design adapted across multiple social platforms such as Facebook, Instagram, and LinkedIn, especially when they want all variants produced in one pass. ---- - -# Canva Resize For Social Media - -## Overview - -Use this skill to take one Canva design and create a multi-platform set of resized variants. Identify the source design, generate the requested social formats, export each version, and present the results in a scan-friendly way. - -## Preferred Deliverables - -- A confirmed source design with the right title and edit context. -- Resized variants for the requested social platforms. -- Direct export links and Canva edit links for each successful output. - -## Workflow - -1. Identify the source design from a design ID, Canva URL, design name, or the current conversation context. -2. Confirm the source design exists and is accessible before starting any resize work. -3. Resize the design into the standard target formats for Facebook post, Facebook story, Instagram post, Instagram story, and LinkedIn post. Run independent resize operations in parallel when the tool flow supports it. -4. Continue with the formats that succeed even if one or more resize attempts fail. -5. Export each successful resized design as a high-quality PNG and collect the download links. -6. Present the finished set grouped by platform, including both the PNG download link and the Canva edit link. - -## Write Safety - -- Keep the original design unchanged and work from resized copies. -- If a name search returns multiple designs, identify the right one before resizing. -- Use exact target dimensions for each platform rather than approximations. -- Report partial failures clearly instead of hiding them behind a generic success message. - -## Output Conventions - -- Lead with a short summary of which formats were created successfully. -- List each platform separately with its dimensions, export link, and edit link. -- Mention when two outputs share the same dimensions, such as Facebook Story and Instagram Story. -- If some formats fail, separate successes from failures so the user can act quickly. - -## Example Requests - -- "Resize this Canva design for Facebook, Instagram, and LinkedIn." -- "Make all the social versions of this campaign graphic." -- "Take my flyer design and export all the social post sizes." -- "Resize this Canva link for every major social format." - -## Light Fallback - -If the source design cannot be found or exported, say that Canva access may be unavailable or scoped to the wrong design and ask the user to reconnect or identify the exact design to use. diff --git a/plugins/canva/skills/canva-resize-for-all-social-media/agents/openai.yaml b/plugins/canva/skills/canva-resize-for-all-social-media/agents/openai.yaml deleted file mode 100644 index fa5a905b7..000000000 --- a/plugins/canva/skills/canva-resize-for-all-social-media/agents/openai.yaml +++ /dev/null @@ -1,7 +0,0 @@ -interface: - display_name: "Social Resize" - short_description: "Resize one design for social platforms" - icon_small: "./assets/canva-small.svg" - icon_large: "./assets/canva.svg" - brand_color: "#00C4CC" - default_prompt: "Use $canva-resize-for-all-social-media to turn one Canva design into Facebook, Instagram, and LinkedIn-ready versions." diff --git a/plugins/canva/skills/canva-resize-for-all-social-media/assets/canva-small.svg b/plugins/canva/skills/canva-resize-for-all-social-media/assets/canva-small.svg deleted file mode 100644 index 76f521233..000000000 --- a/plugins/canva/skills/canva-resize-for-all-social-media/assets/canva-small.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/plugins/canva/skills/canva-resize-for-all-social-media/assets/canva.svg b/plugins/canva/skills/canva-resize-for-all-social-media/assets/canva.svg deleted file mode 100644 index 5ac7ef33e..000000000 --- a/plugins/canva/skills/canva-resize-for-all-social-media/assets/canva.svg +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/plugins/canva/skills/canva-resize-for-social-media/SKILL.md b/plugins/canva/skills/canva-resize-for-social-media/SKILL.md new file mode 100644 index 000000000..8d94bca51 --- /dev/null +++ b/plugins/canva/skills/canva-resize-for-social-media/SKILL.md @@ -0,0 +1,149 @@ +--- +name: canva-resize-for-social-media +description: Resize a Canva design into multiple social media formats (Facebook post, Facebook story, Instagram post, Instagram story, LinkedIn post). Use this skill when users want to resize Canva designs specifically for multiple social media platforms in one operation, rather than resizing to a single format manually. +--- + +# Canva Resize for Social Media + +Automatically resize a single Canva design into multiple social media formats. + +## Overview + +This skill enables rapid multi-platform content distribution by taking a single Canva design and creating optimized versions for: +- Facebook post +- Facebook story +- Instagram post +- Instagram story +- LinkedIn post + +All resized versions are provided with Canva edit links so users can further edit or download them directly from Canva. + +## Workflow + +### Step 1: Identify the Source Design + +Determine which Canva design the user wants to resize. This can be provided in three ways: + +1. **Direct design ID**: User provides a design ID (starts with "D") + - Example: "resize design DABcd1234ef for all social media" + - Use the design ID directly with `get-design` tool to retrieve design information + +2. **Direct design URL**: User provides a Canva design link + - Example: "resize https://www.canva.com/design/DABcd1234ef/... for all social media" + - Extract the design ID from the URL (the part after `/design/` and before the next `/` or query parameter) + - Use the extracted design ID with `get-design` tool + +3. **Search by design name**: Use `search-designs` tool with the design name as the query + - Example: "resize my Demo Brand Template: Brix&Hart Flyer design for all social media" + - Use the exact name/phrase the user provides as the search query + - If multiple matches are found, present options and ask the user to select one + +4. **Current context**: If the user just created or edited a design in the conversation, use that design ID + +**Implementation note**: When searching by name, pass the design name directly to `search-designs` as the query parameter. The tool will find the best match based on the design title. + +### Step 2: Retrieve Source Design Information + +Use the `get-design` tool with the design ID to: +- Confirm the design exists and is accessible +- Get the design title (for naming resized versions) +- Verify design type compatibility + +### Step 3: Ask Which Platforms and Formats + +Present the available formats and ask which ones the user wants: + +``` +Which platforms and formats would you like to resize for? + +- Facebook post (1200×630) +- Facebook story (1080×1920) +- Instagram post (1080×1080) +- Instagram story (1080×1920) +- LinkedIn post (1200×627) +``` + +If the user says "all" or "all social media", use all five. Otherwise, only resize for the ones they select. + +### Step 4: Resize to Selected Formats + +Execute the resize operations **in parallel** by calling the `resize-design` tool once for each selected format. Use these exact specifications: + +**Available formats and dimensions:** + +1. **Facebook Post**: 1200 × 630 pixels (custom) + ``` + design_type: { type: "custom", width: 1200, height: 630 } + ``` + +2. **Facebook Story**: 1080 × 1920 pixels (custom) + ``` + design_type: { type: "custom", width: 1080, height: 1920 } + ``` + +3. **Instagram Post**: 1080 × 1080 pixels (custom) + ``` + design_type: { type: "custom", width: 1080, height: 1080 } + ``` + +4. **Instagram Story**: 1080 × 1920 pixels (custom) + ``` + design_type: { type: "custom", width: 1080, height: 1920 } + ``` + +5. **LinkedIn Post**: 1200 × 627 pixels (custom) + ``` + design_type: { type: "custom", width: 1200, height: 627 } + ``` + +**Note**: Facebook Story and Instagram Story have identical dimensions. Create both versions but inform the user they're the same size. + +**Error handling**: If a resize operation fails, continue with remaining formats and report which formats succeeded and which failed at the end. + +### Step 5: Present Results with Edit Links + +**Present comprehensive results to the user:** + +Provide the user with a summary including: + +1. **Summary**: Confirm which formats were created successfully +2. **Design edit links**: Canva editor URLs for each resized design so users can make further edits or download directly from Canva +3. **Note about duplicates**: Mention that Facebook Story and Instagram Story have identical dimensions + +**Presentation format example:** +``` +✅ Successfully resized your design for all social media platforms! + +Edit Links: + +**Facebook Post** (1200×630) +- [Edit in Canva](edit_url) + +**Facebook Story** (1080×1920) +- [Edit in Canva](edit_url) + +**Instagram Post** (1080×1080) +- [Edit in Canva](edit_url) + +**Instagram Story** (1080×1920) +- [Edit in Canva](edit_url) + +**LinkedIn Post** (1200×627) +- [Edit in Canva](edit_url) + +Note: Facebook Story and Instagram Story use the same dimensions (1080×1920). +``` + +**Implementation details**: +- Design edit links come from the `resize-design` tool response (use the `urls.edit_url` field from each resized design) +- Present links as clickable URLs, not just plain text +- Organize by platform for easy scanning + +## Key Implementation Notes + +- **Compatibility**: Check if `resize-design` is available in the current MCP tools. If not, inform the user that this skill requires the Canva MCP resize tool in the current host +- **Parallel execution**: Resize operations should be performed in parallel for efficiency +- **Consistent naming**: Use the source design title with platform suffix for resized designs +- **Error resilience**: If any operation fails, complete the remaining operations and clearly report what succeeded/failed +- **User confirmation**: Do not require user approval between steps - execute the full workflow automatically unless errors occur +- **Format accuracy**: Always use the exact pixel dimensions specified above for each platform diff --git a/plugins/canva/skills/canva-translate-design/SKILL.md b/plugins/canva/skills/canva-translate-design/SKILL.md index acd5b194c..1e3feb3cc 100644 --- a/plugins/canva/skills/canva-translate-design/SKILL.md +++ b/plugins/canva/skills/canva-translate-design/SKILL.md @@ -1,50 +1,67 @@ --- name: canva-translate-design -description: Translate the text in a Canva design into another language while preserving the original layout as much as possible. Use when the user wants a localized or translated version of an existing Canva design and expects the original file to remain unchanged. +description: Translate all text in a Canva design to another language, creating a translated copy. Faster than manually copying and editing each text box in Canva's editor. Use when users say "translate my design to [language]", "make a Spanish/French/etc version", or "localize my Canva design". --- -# Canva Translate Design +# Canva Translate -## Overview +Translate all text elements in a Canva design to a target language, creating a new copy with translated content. -Use this skill to create a translated copy of an existing Canva design. Find the source design, duplicate it safely, translate text elements into the target language, and save the localized version only after the user approves. +## Workflow -## Preferred Deliverables +### 1. Locate the Design -- A translated copy of the original Canva design in the requested language. -- A concise note about any text-length or layout risks introduced by translation. -- A final Canva link to the saved translated design. +If user provides a **design ID** directly (typically starts with `D`, e.g. `DABcd1234ef`), use that as the design identifier; **do not** pass it to `Canva:search-designs` (search is for titles, not IDs). -## Workflow +If user provides a **URL**: Extract the design ID from the URL (format: `https://www.canva.com/design/{design_id}/...`). + +If user provides a **name**: Use `Canva:search-designs` to find the design by title. If multiple matches, ask user to clarify. + +### 2. Create a Translated Copy + +Use `Canva:resize-design` with the same dimensions to create a copy. This preserves the original design untouched. + +### 3. Start Editing Transaction + +Use `Canva:start-editing-transaction` on the new copy to get: +- `transaction_id` for making edits +- All text elements with their `element_id` and current text content + +### 4. Translate Text + +For each text element returned: +1. Translate the text to the target language (use Claude's translation capability) +2. Preserve formatting cues (line breaks, emphasis patterns) +3. Keep proper nouns, brand names, and technical terms as appropriate + +### 5. Apply Translations -1. Locate the design from a Canva URL or by searching for its title. If multiple matches appear, identify the right design before continuing. -2. Create a copy of the design so the original stays untouched. -3. Start an editing transaction on the copied design and gather the text elements that need translation. -4. Translate each text element into the requested language while preserving meaning, line breaks, and important formatting cues. -5. Apply the translated text in a single batched edit when possible, and update the design title to reflect the target language. -6. Show the translated preview or summarize the pending result, ask for approval to save, then commit the transaction and return the new design link. +Use `Canva:perform-editing-operations` with `replace_text` operations for all translated elements. Batch all replacements in a single call. -## Write Safety +Also update the design title to indicate the language (e.g., append " (Spanish)" or use translated title). -- Always work on a copy rather than the original design. -- Preserve proper nouns, product names, and brand language unless the user asks for deeper localization. -- Warn the user when translation is likely to expand text enough to require layout cleanup in Canva. -- Treat the final save as an explicit action that follows user approval. +### 6. Commit Changes -## Output Conventions +After showing the user the translated preview thumbnail: +1. Ask for explicit approval to save +2. Use `Canva:commit-editing-transaction` to finalize +3. Provide the link to the new translated design -- State the source design and target language up front. -- Call out any translation assumptions, especially around brand names or ambiguous phrases. -- Mention likely layout risks before the final save when text expansion is significant. -- Return the saved translated design link after commit. +## Example Interaction -## Example Requests +**User**: Translate my "Summer Sale Poster" to French -- "Translate my Canva poster into Spanish." -- "Make a French version of this design." -- "Localize this Canva design for German." -- "Create a Portuguese copy of this brochure." +**Steps**: +1. Search: `Canva:search-designs` with query "Summer Sale Poster" +2. Copy: `Canva:resize-design` to create duplicate +3. Edit: `Canva:start-editing-transaction` on copy +4. Translate all text elements to French +5. Apply: `Canva:perform-editing-operations` with all `replace_text` operations +6. Show preview, get approval, commit -## Light Fallback +## Important Notes -If the source design cannot be found or opened for editing, say that Canva access may be unavailable or pointed at the wrong account and ask the user to reconnect or identify the exact design to translate. +- Always create a copy—never modify the original design +- Batch all text replacements in one `perform-editing-operations` call for efficiency +- If translation significantly changes text length, warn user that layout adjustments may be needed in Canva +- For designs with many pages, translate all pages in the same transaction diff --git a/plugins/canva/skills/canva-translate-design/agents/openai.yaml b/plugins/canva/skills/canva-translate-design/agents/openai.yaml deleted file mode 100644 index 3b55ed042..000000000 --- a/plugins/canva/skills/canva-translate-design/agents/openai.yaml +++ /dev/null @@ -1,7 +0,0 @@ -interface: - display_name: "Translate Design" - short_description: "Create translated copies of designs" - icon_small: "./assets/canva-small.svg" - icon_large: "./assets/canva.svg" - brand_color: "#00C4CC" - default_prompt: "Use $canva-translate-design to create a translated copy of my Canva design in another language." diff --git a/plugins/canva/skills/canva-translate-design/assets/canva-small.svg b/plugins/canva/skills/canva-translate-design/assets/canva-small.svg deleted file mode 100644 index 76f521233..000000000 --- a/plugins/canva/skills/canva-translate-design/assets/canva-small.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/plugins/canva/skills/canva-translate-design/assets/canva.svg b/plugins/canva/skills/canva-translate-design/assets/canva.svg deleted file mode 100644 index 5ac7ef33e..000000000 --- a/plugins/canva/skills/canva-translate-design/assets/canva.svg +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/plugins/carta-crm/.app.json b/plugins/carta-crm/.app.json deleted file mode 100644 index de857bcf5..000000000 --- a/plugins/carta-crm/.app.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "apps": { - "carta-crm": { - "id": "asdk_app_69d6804c5c2481919b2674401922ebba" - } - } -} diff --git a/plugins/carta-crm/.codex-plugin/plugin.json b/plugins/carta-crm/.codex-plugin/plugin.json deleted file mode 100644 index 18c70df3a..000000000 --- a/plugins/carta-crm/.codex-plugin/plugin.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "carta-crm", - "version": "1.0.3", - "description": "Carta CRM helps investment teams stay on top of deal flow by keeping deals, companies, and relationships in...", - "author": { - "name": "Carta Inc.", - "url": "https://carta.com" - }, - "repository": "https://github.com/openai/plugins", - "license": "MIT", - "keywords": [], - "apps": "./.app.json", - "interface": { - "displayName": "Carta CRM", - "shortDescription": "Carta CRM helps investment teams stay on top of deal flow by keeping deals, companies, and relationships in...", - "longDescription": "Carta CRM helps investment teams stay on top of deal flow by keeping deals, companies, and relationships in one place. Track opportunities through each stage, capture meeting notes and key takeaways alongside the right deal or contact, and quickly find past context when you need it.", - "developerName": "Carta Inc.", - "category": "Business & Operations", - "capabilities": [], - "defaultPrompt": [ - "Find the latest relationship context in Carta CRM" - ], - "screenshots": [], - "websiteURL": "https://carta.com", - "privacyPolicyURL": "https://carta.com/legal/privacy/privacy-policy/", - "termsOfServiceURL": "https://carta.com/legal/terms-agreements/terms-service/", - "composerIcon": "./assets/logo.png", - "logo": "./assets/logo.png", - "logoDark": "./assets/logo-dark.png" - }, - "homepage": "https://carta.com" -} diff --git a/plugins/carta-crm/assets/logo-dark.png b/plugins/carta-crm/assets/logo-dark.png deleted file mode 100644 index 31f7f4593..000000000 Binary files a/plugins/carta-crm/assets/logo-dark.png and /dev/null differ diff --git a/plugins/carta-crm/assets/logo.png b/plugins/carta-crm/assets/logo.png deleted file mode 100644 index 701947fa9..000000000 Binary files a/plugins/carta-crm/assets/logo.png and /dev/null differ diff --git a/plugins/catalyst-by-zoho/.app.json b/plugins/catalyst-by-zoho/.app.json deleted file mode 100644 index 56d5f6b02..000000000 --- a/plugins/catalyst-by-zoho/.app.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "apps": { - "catalyst-by-zoho": { - "id": "asdk_app_6a16bcb9a37081919b0db1d81010fb2f" - } - } -} diff --git a/plugins/catalyst-by-zoho/.codex-plugin/plugin.json b/plugins/catalyst-by-zoho/.codex-plugin/plugin.json deleted file mode 100644 index 4ef0874fd..000000000 --- a/plugins/catalyst-by-zoho/.codex-plugin/plugin.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "name": "catalyst-by-zoho", - "version": "1.0.1", - "description": "Extend Codex with Catalyst by Zoho's capabilities.", - "author": { - "name": "Catalyst by Zoho", - "email": "support@zohocatalyst.com", - "url": "https://catalyst.zoho.com/" - }, - "homepage": "https://catalyst.zoho.com/", - "repository": "https://github.com/catalystbyzoho", - "license": "MIT", - "keywords": [ - "zoho", - "catalyst", - "serverless", - "mcp", - "cloud" - ], - "skills": "./skills/", - "apps": "./.app.json", - "interface": { - "displayName": "Catalyst by Zoho", - "shortDescription": "Use Catalyst by Zoho capabilities in Codex.", - "longDescription": "Catalyst by Zoho plugin extends Codex with MCP-backed workflows and skills for building, managing, and operating Catalyst projects.", - "developerName": "Catalyst by Zoho", - "category": "Developer Tools", - "capabilities": [ - "MCP", - "Read", - "Write" - ], - "websiteURL": "https://catalyst.zoho.com/", - "privacyPolicyURL": "https://www.zoho.com/privacy.html", - "termsOfServiceURL": "https://www.zoho.com/terms.html", - "brandColor": "#226DB4", - "composerIcon": "./assets/catalyst_logo.svg", - "logo": "./assets/catalyst_logo.svg", - "defaultPrompt": [ - "Help me work with Catalyst by Zoho.", - "Show me available Catalyst by Zoho capabilities.", - "Guide my Catalyst project setup." - ] - } -} diff --git a/plugins/catalyst-by-zoho/assets/catalyst_logo.svg b/plugins/catalyst-by-zoho/assets/catalyst_logo.svg deleted file mode 100644 index ca708a832..000000000 --- a/plugins/catalyst-by-zoho/assets/catalyst_logo.svg +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/SKILL.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/SKILL.md deleted file mode 100644 index 6b92cdfb6..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/SKILL.md +++ /dev/null @@ -1,539 +0,0 @@ ---- -name: catalyst-by-zoho -description: > - Expert coding assistant for Catalyst by Zoho — full-stack serverless cloud platform. Trigger on any - mention of Catalyst, zcatalyst, AppSail, Data Store, ZCQL, Cache, Stratus, Circuits, SmartBrowz, - ConvoKraft, Slate, Signals, Pipelines, QuickML, NoSQL, Job Scheduling, Zia Services, CodeLib, - API Gateway, Connections, Zoho MCP, CatalystbyZoho, catalyst init/deploy/serve, zcatalyst-sdk-node, - or catalyst-config.json. Covers all 7 function types, full service catalog, architectural guidance, - and Zoho MCP tool-based resource management. Also trigger on migration/comparison with AWS Lambda, - S3, DynamoDB, Vercel, Netlify, Supabase, Firebase, Heroku, Cloud Run, Cloudflare R2, Railway. - Trigger on Catalyst pricing, cost estimation, or "create tables for me", "set up the database", - "deploy to Catalyst", "build on Zoho's platform", or "is Catalyst like Firebase". Do NOT use for - generic Zoho CRM questions unless Catalyst is the target. ---- - -# 🛑 STOP — Read this before doing ANYTHING - -**If the user asks you to build, scaffold, or create a Catalyst application, your FIRST action is to check whether the project is already initialized. You must NOT write any code or create any files until you confirm `.catalystrc` and `catalyst.json` exist in the working directory.** - -**You MUST NOT create these files or directories yourself — they are generated by `catalyst init`:** -- ❌ `catalyst.json` — auto-generated with project IDs; creating it manually = broken deploys -- ❌ `.catalystrc` — auto-generated with environment IDs; creating it manually = broken deploys -- ❌ `functions/` directory — created by `catalyst init` -- ❌ `client/` directory — legacy and deprecated; use Slate instead -- ❌ Do NOT run `catalyst init`, `catalyst login`, or `catalyst functions:add` — they are fully interactive (arrow-key menus) and cannot be run by an LLM - -**If `.catalystrc` or `catalyst.json` is missing → STOP. Do not plan. Do not create files. Tell the user to run `catalyst init` in their terminal first. See the full [Pre-flight Gate](#mandatory-pre-flight-gate) section below.** - ---- - -# Catalyst Development Assistant - -You are an expert coding assistant for **Catalyst by Zoho** — a full-stack, serverless, cloud-based platform -for building and deploying applications at any scale. Your goal is to write production-ready code -that follows Catalyst's conventions, project structure, and SDK patterns so that code can be deployed -directly without modification. - -## What is Catalyst? - -Catalyst by Zoho is a unified cloud platform (comparable in philosophy to Supabase, Firebase, or AWS -Amplify) that provides compute, storage, AI/ML, orchestration, frontend hosting, CI/CD, and developer -tools — all accessible from a single console. Its unique differentiator is **native integration with -the entire Zoho product ecosystem** (CRM, Books, Desk, People, Analytics, etc.) via Signals and -Connections, eliminating glue code for businesses already using Zoho. - -Catalyst supports two pricing models: **Pay-as-you-go** (per-use pricing with generous free tiers) and -**Subscription** (predictable monthly billing). New customers receive $250 USD in trial credits valid -for 180 days. The platform supports **Node.js**, **Java**, and **Python** for server-side functions, -and offers client SDKs for **Web**, **Android**, **iOS**, and **Flutter**. - -## Context sources — when to use which - -This skill has three tiers of context. Use the lightest tier that satisfies the request: - -### Tier 1 — This file (always loaded) -Covers the full service catalog, core principles, deprecation notices, and quick-reference patterns. -Sufficient for: general questions, architecture recommendations, deprecation checks, simple code snippets. - -### Tier 2 — Reference files (read on demand) -Detailed, focused docs. **Load a file ONLY when the user's query clearly requires it. Do not load files speculatively or as a precaution.** - -> **Path note:** Paths below are relative to this file's location (`skills/`). -> If this skill was installed via GitHub Copilot (copied into `.github/copilot-instructions.md` -> with `references/` copied alongside it), all paths resolve as `.github/references/filename.md`. -> Other tools (Claude Code, Cursor, Gemini, Windsurf) use the paths as written. - -| File | Load ONLY when the query is about… | -|------|-------------------------------------| -| `references/pricing.md` | Cost, pricing tiers, free tier limits, billing, or "how much does X cost" | -| `references/zoho-mcp-tools.md` | MCP tool setup/usage, infrastructure creation via MCP, or `CatalystbyZoho_*` tool calls | -| `references/cloud-scale.md` | Scaling limits, Data Store, Stratus, NoSQL, Cache, ZCQL, Auth, or architecture capacity questions | -| `references/meta-ids.md` | Specific service IDs — Table ID, ZAID, Org ID, Segment ID, Project ID — or where to find config keys | -| `references/functions-and-sdk.md` | Code generation, function handler signatures, SDK method usage, or Node.js/Java/Python patterns | -| `references/project-and-cli.md` | CLI commands, project initialization, deployment steps, or `catalyst.json` / directory structure | -| `references/deployment-sops.md` | Deployment procedures, pre-deploy checklist, deploy commands, GitHub deployment, failure recovery, or environment promotion | -| `references/troubleshooting.md` | Deploy failures, function errors, ZCQL issues, MCP tool errors, AppSail crashes, timeout debugging, or "why is my X failing" | -| `references/observability.md` | Catalyst Logs, APM, Application Alerts, Audit Logs, monitoring after deployment, or performance debugging | -| `references/architecture-patterns.md` | User describes a use case or asks "what should I use to build X" — maps requirements to Catalyst services and produces a complete infrastructure blueprint | -| `references/services.md` | AppSail deep-dive, Slate, Circuits, Signals, Pipelines, SmartBrowz, ConvoKraft, Zia, QuickML, Job Scheduling, Tunneling, CodeLib, Browser Logic functions | -| `references/equivalents-aws.md` | Migrating from AWS, or "what's the Catalyst equivalent of Lambda / S3 / RDS / Step Functions" | -| `references/equivalents-gcp.md` | Migrating from GCP, or "what's the Catalyst equivalent of Cloud Run / Pub-Sub / Firestore" | -| `references/equivalents-azure.md` | Migrating from Azure, or "what's the Catalyst equivalent of Azure Functions / Blob Storage / Cosmos DB" | -| `references/equivalents-firebase.md` | Migrating from Firebase, or Firebase Auth / Firestore / Storage / Hosting comparisons | -| `references/equivalents-vercel-netlify.md` | Migrating from Vercel or Netlify, or frontend-hosting + serverless function comparisons | -| `references/equivalents-heroku.md` | Migrating from Heroku, Railway, Render, or Fly.io (PaaS comparisons) | -| `references/equivalents-supabase.md` | Migrating from Supabase, or full-stack BaaS platform comparisons ("is Catalyst like Supabase?") | -| `references/sdk-nodejs.md` | Detailed Node.js SDK code examples — Data Store CRUD, ZCQL, Cache, File Store, Auth, Email, Stratus (multipart, TransferManager, pre-signed URLs), NoSQL, Zia, SmartBrowz SDK, Job Scheduling SDK, Pipelines, Circuits, Push Notifications | -| `references/sdk-java.md` | Detailed Java SDK code examples — ZCObject/ZCTable/ZCRowObject patterns, ZCQL, Cache, File Store, Auth, Email, Stratus, NoSQL, Zia, SmartBrowz, Job Scheduling, Pipelines, Circuits | -| `references/sdk-python.md` | Detailed Python SDK code examples — Data Store, ZCQL, Cache, File Store, Auth, Email, Stratus, NoSQL, Zia, SmartBrowz, Job Scheduling | -| `references/sdk-web.md` | Web SDK v4 client-side JavaScript — Authentication (Hosted vs Embedded, generateAuthToken, cross-domain Slate→AppSail pattern), Data Store, ZCQL, File Store, Stratus, Search, Push Notifications, iFrame CSS customization, common auth errors | -| `references/sdk-mobile.md` | Android (Kotlin), iOS (Swift), and Flutter (Dart) SDK — setup, auth, Data Store, ZCQL, File Store, Stratus, Push Notifications, Search, Flutter ZCQL Query Builder | -| `references/signals-deep-dive.md` | Signals event bus in depth — publishers (Zoho/Catalyst/Custom), events, rules with filters, targets, dispatch policies (instant/batch), event transformation, webhooks, dashboard, limits | -| `references/smartbrowz-deep-dive.md` | SmartBrowz in depth — headless browser (Puppeteer/Playwright/Selenium connection code), Browser Logic functions, Browser Grid tiers, PDF/Screenshot generation with SDK examples, LiquidJS templates, Dataverse APIs | -| `references/job-scheduling-deep-dive.md` | Job Scheduling in depth — job pools (4 types), jobs, pre-defined vs dynamic crons, cron expressions, dynamic cron SDK examples (Node.js/Java/Python), REST API endpoints, application alerts, limits | -| `references/devops-deep-dive.md` | DevOps in depth — APM (Java/Node only), log pushing code per language, log levels, Application Alerts config, Automation Testing (modules, test cases, suites, plans, variables, results), metrics | -| `references/cli-reference.md` | Full CLI command map — all subcommands with flags, Slate framework values, AppSail non-interactive setup, `catalyst serve` port behavior, safety rules for destructive commands, resource-first development order | - -If none of those conditions match, answer from Tier 1 (this file) alone. - -### Tier 3 — Official Catalyst docs site (search only as a last resort) -The full Catalyst documentation lives at `https://docs.catalyst.zoho.com/en/`. **NEVER search this proactively.** - -> **Why not `llms-full.txt`?** The hosted `llms-full.txt` is ~11 MB. Direct web-fetch tools -> silently truncate it to <1% of its content, producing dangerously incomplete results. -> Individual doc pages, however, fetch fully and reliably. Always prefer the two-step -> approach below. - -Only search the docs site when ALL of the following conditions are true: -1. The user is asking about a **specific, undocumented API detail, parameter, or edge-case behavior** — not a general question. -2. The relevant Tier 2 reference file(s) have **already been read** and do not contain the answer. -3. Tier 1 (this file) also does not cover it. - -**Two-step lookup procedure:** -1. **Web search** with a site-scoped query to find the right page: - - Use: `site:docs.catalyst.zoho.com ` (e.g., `site:docs.catalyst.zoho.com ZCQL COALESCE`) - - This returns accurate, canonical URLs — never guess or fabricate a docs URL yourself. -2. **Fetch the specific page URL** returned by the search to get the full content with code examples and parameter details. - -**Do NOT:** -- Fetch `https://docs.catalyst.zoho.com/en/llms-full.txt` directly — it will silently truncate to <1% of the content. -- Fabricate docs URLs from memory (e.g., `zoho.catalyst.com/docs/...`) — these do not exist. All Catalyst documentation lives under `https://docs.catalyst.zoho.com/en/`. -- Use Tier 3 for routine code generation, architecture questions, CLI usage, pricing, SDK patterns, troubleshooting common errors, deployment procedures, observability, or anything the Tier 1 or Tier 2 files already cover. - -Always read the relevant reference file(s) before writing code. If the request spans multiple areas (e.g. -"write a Catalyst function that queries Data Store and stores results in Stratus"), read all applicable -reference files. - -If the user references another platform, load only the equivalents file for that platform: -- AWS terms (Lambda, S3, RDS, etc.) → `references/equivalents-aws.md` -- GCP terms (Cloud Run, Pub-Sub, Firestore, etc.) → `references/equivalents-gcp.md` -- Azure terms (Azure Functions, Blob Storage, Cosmos DB, etc.) → `references/equivalents-azure.md` -- Firebase terms (Firestore, Firebase Auth, Firebase Hosting, etc.) → `references/equivalents-firebase.md` -- Vercel or Netlify terms → `references/equivalents-vercel-netlify.md` -- Heroku, Railway, Render, or Fly.io terms → `references/equivalents-heroku.md` -- Supabase terms, or holistic "is Catalyst like X?" questions → `references/equivalents-supabase.md` - -Do not load multiple equivalents files unless the user's query explicitly spans more than one platform. - -**Important:** When writing code that uses any Catalyst ID (Table ID, ZAID, Segment ID, etc.), always -add an inline comment telling the user exactly where to find it in the console. Never leave ID -placeholders unexplained. Read `references/meta-ids.md` if you need to reference specific ID locations. - -## 🛑 MANDATORY Pre-flight Gate {#mandatory-pre-flight-gate} - -> **Do this FIRST or everything you build will fail on deploy.** - -**YOUR VERY FIRST ACTION for any Catalyst build request — before reading reference files, before planning architecture, before writing a single line of code — is to check whether the project is initialized.** - -**You MUST NOT:** -- ❌ Create `catalyst.json` yourself — it is auto-generated by `catalyst init` with project IDs -- ❌ Create `.catalystrc` yourself — it is auto-generated by `catalyst init` -- ❌ Create the `functions/` directory yourself — it is created by `catalyst init` -- ❌ Create the `client/` directory yourself — it is legacy (use Slate instead) and created by `catalyst init` -- ❌ Run `catalyst init`, `catalyst login`, or `catalyst functions:add` — they are interactive -- ❌ Scaffold any project structure in an empty folder — it will lack Catalyst project IDs and deployment will fail with cryptic errors - -**If you create these files manually, the project will have no Project ID, no Environment ID, no ZAID, and `catalyst deploy` will fail.** There is no workaround — the CLI must generate these files. - -### How to check - -Look for these files in the working directory (use filesystem tools or ask the user): -1. `.catalystrc` — contains project identity (project_id, env_id) -2. `catalyst.json` — contains deployment targets (functions, client) - -### Decision: Can I proceed? - -**BOTH `.catalystrc` AND `catalyst.json` exist?** -→ YES: Read them, check `catalyst.json` → `functions.targets` for registered functions, then proceed to write code. - -**Either file is missing?** -→ NO: **STOP IMMEDIATELY.** Do not create any files. Do not plan architecture. Respond to the user with ONLY this message: - ---- - -**Before I can build anything, the Catalyst project needs to be initialized. This is a one-time interactive setup that must be done in your terminal — I can't do it for you because the CLI uses interactive menus.** - -Please run these commands: - -```bash -# Step 1: Log in (opens browser for Zoho OAuth) -catalyst login - -# Step 2: Initialize project (interactive — use arrow keys to select) -catalyst init -``` - -**Important — if the app needs a frontend:** Before running `catalyst init`, you must first enable Slate in the Catalyst console. Go to **console.catalyst.zoho.com → your project → Slate** (in the left sidebar) → click **"Start Exploring"**. This is a one-time activation. Without this step, the Slate option during `catalyst init` will not work. - -During `catalyst init`, you'll be asked to: -1. **Select a default Catalyst portal** — pick your Zoho portal/org -2. **Select a default Catalyst project** — pick an existing project from the list -3. **Which features to setup?** — use Space to select: **Functions** *(always)* and **Slate** *(if the app needs a frontend)*. Do NOT select "Client" — it is legacy and being deprecated. - -If you selected **Functions**, the CLI will prompt for the first function's npm package setup: -- `package name:` — enter a name (e.g., `docvault_api`) -- `entry point:` — press Enter to accept default (`index.js`) -- `author:` — press Enter to accept default (your Zoho email) -- `Do you wish to install all dependencies now?` — enter **Yes** - -If you selected **Slate**, the CLI will then run Slate Setup: -- `Select a framework to start with:` — arrow keys to pick (e.g., **React + Vite**, Next.js, Angular, Vue, Svelte, Astro) -- `Please provide the name for your app:` — enter a name (e.g., `docvault-ui`) -- Auto-detected config will be shown (Install Command, Build Command, Build Path, Deployment Name) -- `Do you want to modify these default configurations?` — enter **No** to accept defaults -- `Please provide your Development Command:` — press Enter to accept default (`npm run dev -- --port $ZC_SLATE_PORT`) - -After that, register the backend functions: - -```bash -catalyst functions:add -``` - -Run this once for each function. The functions I'll need are: -- *(list the function names, types, and stacks here)* - -**Let me know once you've completed these steps and I'll build everything.** - ---- - -**Do not continue past this point until the user confirms setup is complete.** - -### After setup is confirmed — what you CAN do - -Once the user confirms and you verify `.catalystrc` + `catalyst.json` exist: -- ✅ Create/edit `index.js`, `main.py`, or other function code files -- ✅ Create/edit `catalyst-config.json` inside each function directory (use `deployment`/`execution` format) -- ✅ Create/edit `package.json` and run `npm install` -- ✅ Create/edit Slate app files (HTML, CSS, JS in the Slate app directory) -- ✅ Run `catalyst serve` for local testing -- ✅ Run `catalyst deploy` for deployment - -### Why this gate exists - -`catalyst init` does three things that cannot be replicated manually: -1. Links the local directory to a Catalyst project in the cloud (assigns Project ID, Environment ID, ZAID) -2. Creates `.catalystrc` with these IDs — deployment reads this file to know WHERE to deploy -3. Creates `catalyst.json` with the deployment manifest — the CLI reads this to know WHAT to deploy - -Without these, `catalyst deploy` either crashes or deploys to nowhere. Every file you create in an uninitialized folder is wasted work. - ---- - -## Core principles - -1. **STOP and check project initialization BEFORE doing anything else.** (See Pre-flight Gate above.) - If `.catalystrc` and `catalyst.json` don't exist, you MUST ask the user to run `catalyst init` — and - then STOP and WAIT. Do not create these files yourself. Do not create `functions/` directories - yourself. Do not scaffold any project structure. Everything you build in an uninitialized - folder will fail on deploy. - -2. **Follow Catalyst's exact project structure.** Catalyst is strict about directory layout. Functions go - under `functions/`, and `catalyst.json` sits at the project root. For frontends, **always use Slate** - (not the legacy `client/` directory). These directories are created by `catalyst init` — do not create them manually. - -3. **Use the correct SDK initialization pattern.** The Catalyst Node.js SDK requires manual initialization - in all function types: `const catalystApp = catalyst.initialize(context)` (Basic I/O, Event, Cron, Job) - or `const catalystApp = catalyst.initialize(req)` (Advanced I/O, AppSail). The SDK is NOT auto-injected. - -4. **Write deployment-ready code.** Every function you write should include proper error handling, - correct exports/handler signatures, and the right `catalyst-config.json`. Code should work when - the user runs `catalyst deploy`. - -5. **Respect function types.** Catalyst has 7 function types (Basic I/O, Advanced I/O, Event, Cron, - Integration, Job, Browser Logic). Each has a different handler signature and invocation model. - Using the wrong type causes silent failures. - -6. **Use ZCQL for queries, not raw SQL.** Catalyst's Data Store uses ZCQL (Catalyst Query - Language), which looks like SQL but has important differences (case-sensitive table/column names, - no cross-type JOINs, max 300 rows per query, single quotes only for strings). - -7. **Always handle Catalyst's auth model.** Catalyst uses its own authentication system with user - management. Functions have Security Rules that control access — the **only valid values** are - `"optional"` (public, no login required) and `"required"` (any authenticated Catalyst user). - Values like `no_auth`, `user_auth`, and `admin_auth` **do not exist** and will throw - "Invalid input value". For admin-only route enforcement, use **API Gateway** (auth type on - the route), not Security Rules. Security Rules is a binary gate: public vs. authenticated. - -8. **Default to the modern stack.** For new projects: - - **Slate** over Web Client Hosting (`client/`) — Client is deprecated; always use Slate for frontends. During `catalyst init`, select **Slate** (not "Client"). Use `catalyst slate:create` only if you need to add additional Slate apps later. - - **Stratus** over File Store — for object storage - - **Signals** over Event Listeners — for event-driven architecture - - **Job Scheduling** over Cron — for background tasks - The legacy alternatives are deprecated. - -9. **Proactively guide users to connect Zoho MCP.** When a user needs to create infrastructure - (tables, columns, buckets, cache, etc.), first check if Zoho MCP tools (`CatalystbyZoho_*`) - are already available in your tool list. If they are, use them directly for infrastructure - setup. If MCP is **not connected yet**, recommend the user set it up — walk them through the - steps in `references/zoho-mcp-tools.md` so they can manage infrastructure directly from the - conversation. If the user explicitly chooses to skip MCP setup, fall back to step-by-step - console instructions for manual creation. Read `references/zoho-mcp-tools.md` before making - any MCP tool calls. - - > **⚠️ MCP mandatory pre-flight: Org → Project → Verify → Operate.** - > Before making ANY MCP tool call that targets a project (creating tables, querying data, - > managing buckets, etc.), you MUST first identify the correct **org ID** and **project ID**. - > Without these, every call will fail with `PERMISSION_NEEDED` or `INVALID_ORG`. - > - > **If `.catalystrc` exists** in the working directory — read it first. It contains the - > authoritative `project_id` and `env_id`. Cross-check with `List_All_Organizations`. - > - > **If `.catalystrc` does NOT exist** (chat-only context, no local project) — you MUST call: - > 1. `List_All_Organizations` → get the org `id` (used as `Catalyst-org` header) - > 2. `List_All_Projects` (with that org) → get the project `id` (used in `path_variables.projectId`) - > 3. A verification read (e.g., `List_All_Tables`) → confirm access works before any writes - > - > **Never skip this sequence.** Never guess org or project IDs. See `references/zoho-mcp-tools.md` - > for the full flow, ID mismatch gotchas, and troubleshooting. - -## ⚠️ Deprecation notices (as of May 2026) - -The following Catalyst components are **deprecated** and will be removed in a future update -(originally scheduled for April 30, 2026, currently still functional with deprecation warnings): - -- **Event Listeners** → replaced by **Signals** (event bus service) -- **File Store** → replaced by **Stratus** (S3-compatible object storage) -- **Cron** → replaced by **Job Scheduling** (managed job pools) - -**Never recommend deprecated components for new projects.** If a user has existing code using these, -guide them to migrate to the replacement service. File Store supports direct migration to Stratus via -the console. Event Listeners and Cron require manual migration of business logic. - -Users who signed up after August 27, 2025 cannot even see or access these deprecated components. - -## Catalyst service catalog (quick reference) - -For deep-dive details on any service, load the appropriate Tier 2 reference file. - -| Category | Services | Details in | -|----------|----------|-----------| -| **Compute** | Functions (7 types: Basic I/O, Advanced I/O, Event, Cron, Integration, Job, Browser Logic); Node.js 20, Java 8/11/17, Python 3.9; 128–1024 MB memory | `references/functions-and-sdk.md` | -| **Compute** | AppSail — PaaS for persistent servers; managed Node.js/Java/Python runtimes or custom Docker; 1–5 auto-scaling instances; 256–2048 MB | `references/services.md` | -| **Storage** | Data Store (relational, ZCQL, max 300 rows/query); Stratus (S3-compatible, PREFERRED over ~~File Store~~); NoSQL (document DB); Cache (string-only, max 48hr TTL, max 5MB/value); Search (full-text, per-column) | `references/cloud-scale.md` | -| **Frontend** | Slate — Git-based, SSR, preview deploys (PREFERRED); Web Client Hosting — legacy `client/` dir | `references/services.md` | -| **Integration** | Signals — managed event bus, Zoho ecosystem (PREFERRED over ~~Event Listeners~~); Connections — OAuth token manager, auto-refresh | `references/services.md` | -| **Orchestration** | Circuits — visual workflow, approvals, saga patterns; Job Scheduling — background jobs (PREFERRED over ~~Cron~~); Pipelines — YAML CI/CD | `references/services.md` | -| **AI / ML** | Zia Services — vision + text analytics microservices; QuickML — no-code AutoML + LLM/VLM; ConvoKraft — AI chatbot builder | `references/services.md` | -| **Browser** | SmartBrowz — managed headless browser; scraping, screenshots, PDF generation | `references/services.md` | -| **DevOps** | Logs, APM, Application Alerts, GitHub auto-deploy integration | `references/observability.md` | -| **Auth & Security** | Auth & User Management — built-in auth, Zoho accounts, SSO; API Gateway — routing, rate limiting, CORS | `references/cloud-scale.md` | -| **Communication** | Mail (domain verification required); Push Notifications (APNs + FCM) | `references/cloud-scale.md` | -| **Developer Tools** | CLI (`zcatalyst-cli`), SDKs (Node.js/Java/Python + Web/Android/iOS/Flutter), REST APIs, VS Code Extension, CodeLib, Zia AI Assistant, Tunneling | `references/functions-and-sdk.md`, `references/project-and-cli.md` | - -**Deprecated — never recommend for new projects:** -- ~~File Store~~ → use **Stratus** -- ~~Event Listeners~~ → use **Signals** -- ~~Cron~~ → use **Job Scheduling** -- Users who signed up after Aug 27, 2025 cannot access deprecated components. - -## Quick reference: Function handler signatures (Node.js) - -> **These are the current official signatures** per https://docs.catalyst.zoho.com/en/sdk/nodejs/v2/overview/ -> All function types require manual SDK initialization via `catalyst.initialize()`. -> The node20 handler change **only affects Advanced I/O** (removed `catalystApp` and `context` params, -> now receives raw `req`/`res`). All other function types retain their original signatures. - -```javascript -// Basic I/O — simple string in, string out (GET only) -const catalyst = require('zcatalyst-sdk-node'); -module.exports = (context, basicIO) => { - const catalystApp = catalyst.initialize(context); - const data = context.getArgument(); - basicIO.write("Response string"); -}; - -// Advanced I/O — full HTTP (any method), node20: raw http.ServerResponse (NOT Express) -// Use sendJson() + getBody() helpers — res.status/res.json do NOT exist -'use strict'; -const catalyst = require('zcatalyst-sdk-node'); - -function sendJson(res, statusCode, data) { - res.writeHead(statusCode, { 'Content-Type': 'application/json' }); - res.end(JSON.stringify(data)); -} - -module.exports = async (req, res) => { - const catalystApp = catalyst.initialize(req); - sendJson(res, 200, { message: "Hello" }); -}; - -// LEGACY Advanced I/O signature (older projects, node14/16/18): -// module.exports = (catalystApp, context, req, res) => { -// sendJson(res, 200, { message: "Hello" }); -// }; - -// Event — triggered by Signals/Event Listeners -const catalyst = require('zcatalyst-sdk-node'); -module.exports = (event, context) => { - const catalystApp = catalyst.initialize(context); - const eventData = event.data; // event payload - context.close(); // must close context when done -}; - -// Cron — DEPRECATED, use Job Scheduling -const catalyst = require('zcatalyst-sdk-node'); -module.exports = (cronDetails, context) => { - const catalystApp = catalyst.initialize(context); - context.closeWithSuccess(); // or context.closeWithFailure() -}; - -// Job — triggered by Job Scheduling -const catalyst = require('zcatalyst-sdk-node'); -module.exports = async (jobData, context) => { - const catalystApp = catalyst.initialize(context); - context.closeWithSuccess(); // or context.closeWithFailure() -}; - -// Integration — Zoho service triggers (NOT available in EU, AU, IN, CA DCs) -const catalyst = require('zcatalyst-sdk-node'); -module.exports = (event, context) => { - const catalystApp = catalyst.initialize(context); - context.close(); -}; - -// Browser Logic — used with SmartBrowz -const catalyst = require('zcatalyst-sdk-node'); -module.exports = (event, context) => { - const catalystApp = catalyst.initialize(context); - context.close(); -}; -``` - -## Quick reference: SDK component access (Node.js) - -```javascript -// Inside a function handler — in new projects, initialize via catalyst.initialize(req) -const dataStore = catalystApp.datastore(); // Relational DB -const zcql = catalystApp.zcql(); // Query language -const stratus = catalystApp.stratus(); // Object storage (S3-compatible) -const nosql = catalystApp.nosql(); // Document DB -const cache = catalystApp.cache(); // In-memory cache -const search = catalystApp.search(); // Full-text search -const mail = catalystApp.email(); // Email sending -const userMgmt = catalystApp.userManagement();// Auth & users -const connection= catalystApp.connection(); // OAuth token manager -const circuit = catalystApp.circuit(); // Workflow orchestration -const jobSched = catalystApp.jobScheduling(); // Background jobs -const zia = catalystApp.zia(); // AI/ML services -const pushNotif = catalystApp.pushNotification(); -``` - -## Quick reference: CLI commands - -```bash -npm install -g zcatalyst-cli # Install CLI (requires Node.js v14+) -catalyst login # Login to Zoho account -catalyst init # Initialize project -catalyst functions:add # Register a new function (INTERACTIVE — no flags, prompts for name/type/stack) -catalyst serve # Local testing (all resources) -catalyst serve --only functions # Local testing — functions only -catalyst deploy # Deploy all resources to Development -catalyst deploy --only functions # Deploy functions only -catalyst deploy --only functions:crud_api # Deploy a single named function -catalyst functions:shell # Test functions in Node shell -catalyst apig:enable # Enable API Gateway for the project -catalyst apig:disable # Disable API Gateway -catalyst apig:status # Check API Gateway enable status and schedule progress -catalyst slate:create # Add an additional Slate app to a project (interactive — asks framework + name) -catalyst slate:link # Link existing local dir to Slate service (interactive) -catalyst slate:unlink # Unlink a Slate app -catalyst serve --only slate # Serve Slate app locally -catalyst deploy slate # Deploy all Slate apps to Development -catalyst deploy slate -m "message" # Deploy with a deployment message -catalyst deploy --only slate:appname # Deploy a specific Slate app -catalyst deploy slate --production # Deploy to Production environment -``` - -⚠️ **API Gateway CLI prefix is `apig:`, NOT `api-gateway:`.** The commands are `catalyst apig:enable`, `catalyst apig:disable`, `catalyst apig:status`. Using `api-gateway:enable` throws "unknown command". - -⚠️ **Slate CLI deploy workflow:** Select **Slate** during `catalyst init` (or run `catalyst slate:create` / `catalyst slate:link` later to add more apps), then `catalyst deploy slate` to push. No Git repo required for CLI-based deploy. - -⚠️ **CLI flag gotcha (v1.23.0+):** The deploy/serve scoping flag is `--only ` with a space — NOT `--only-functions`. Hyphenated form does not exist and throws "unknown option". Valid targets: `functions`, `client`, `appsail`, `functions:` for a single function. - -⚠️ **First deploy of a new function requires `catalyst functions:add` first.** The CLI will not deploy a function it has not registered, even if the directory and `catalyst-config.json` exist. Run `catalyst functions:add` interactively from the project root, enter name/type/stack when prompted. After it completes, `catalyst.json` will contain a `functions` array with the registered entry. - -## Architectural decision guide - -| Need | Use This | Not This | -|------|----------|----------| -| Frontend hosting (new) | **Slate** | Web Client Hosting | -| File/object storage (new) | **Stratus** | ~~File Store~~ (deprecated) | -| Event-driven architecture (new) | **Signals** | ~~Event Listeners~~ (deprecated) | -| Scheduled/background tasks (new) | **Job Scheduling** | ~~Cron~~ (deprecated) | -| Zoho product integration | **Signals** (events) + **Connections** (APIs) | Custom webhook handlers | -| Stateless API endpoints | **Advanced I/O Functions** | AppSail | -| Persistent server / WebSockets | **AppSail** | Functions | -| Relational data with queries | **Data Store** + **ZCQL** | NoSQL | -| Flexible schema / documents | **NoSQL** | Data Store | -| Ephemeral / session data | **Cache** (max 48hr TTL) | Data Store | -| Multi-step workflow | **Circuits** | Chained function calls | - -## Important gotchas - -- **NEVER scaffold a Catalyst project yourself** — `catalyst.json`, `.catalystrc`, `functions/`, and `client/` are ALL created by `catalyst init`. If you create them manually they will lack Project ID, Environment ID, and ZAID — deployment will fail with no useful error. Always ask the user to run `catalyst init` themselves. This is the #1 cause of failed Catalyst builds by LLMs. -- **`catalyst init`, `catalyst login`, `catalyst functions:add` are INTERACTIVE** — they use arrow-key menus and multi-step prompts. NEVER run them in a script or terminal session. Always instruct the user to run them manually in their own terminal and wait for confirmation before proceeding. -- **`catalyst functions:add` is the ONLY setup command without non-interactive flags** — Unlike `catalyst init` (`--non-interactive`), `catalyst appsail:add` (`--name`, `--stack`), and `catalyst slate:create` (`--name`, `--framework`), `functions:add` has NO flags for automation — it always requires interactive arrow-key selection. **Agent workaround:** When the user has already run `catalyst functions:add` at least once (so `catalyst.json` has a `functions` array), agents can add subsequent functions by: (1) creating a new directory under `functions/`, (2) adding a valid `catalyst-config.json` with correct `deployment` and `execution` keys, (3) adding the function entry to `catalyst.json`'s `functions` array matching the format of existing entries. The first function MUST still be registered interactively. See `references/cli-reference.md` for the exact `catalyst.json` function entry format. -- **Select Slate during `catalyst init`** — Slate is a component option alongside Functions, Client, and AppSail. Select **Functions + Slate** (not Client). If you need to add more Slate apps later, use `catalyst slate:create`. -- **Slate requires one-time console activation** — Before using Slate in the CLI, the user must go to the Catalyst console → their project → **Slate** (left sidebar) → click **"Start Exploring"**. Without this, Slate init via CLI will fail. This only needs to be done once per project. -- **`catalyst functions:add` is required before first deploy** — a function directory + `catalyst-config.json` alone is not enough; the CLI must register it interactively first. The user must run it from the project root and answer name/type/stack prompts -- **Deploy flag is `--only functions` (with space)** — NOT `--only-functions`. Hyphenated form throws "unknown option" in CLI v1.23.0+. Single function: `--only functions:` -- **`catalyst-config.json` uses `deployment` + `execution` keys** — correct format is `{"deployment":{"name":"...","type":"...","stack":"...","env_variables":{}},"execution":{"main":"index.js"}}`. Do NOT use a `function` key or `entry_point` — neither exists. Using them crashes `catalyst deploy` with a cryptic TypeError. -- **Function memory defaults to 128MB** — increase in catalyst-config.json `deployment` block (max 1024MB) -- **Cold starts exist** — keep packages minimal -- **ZCQL table/column names are case-sensitive** — must match console exactly -- **ZCQL max 300 rows per query** — use `LIMIT offset, count` pagination (e.g. `LIMIT 0, 300`, `LIMIT 300, 300`) for larger datasets -- **ZAID differs between Dev and Prod** — #1 source of auth issues in production -- **25-user limit in Development** — no limit in production -- **Stratus bucket names are globally unique** — across ALL Catalyst projects and orgs. Generic names like `my-files` will be taken. Use `{app-name}-{project-id-prefix}` (e.g., `docvault-files-70699`). A `DUPLICATE_ENTRY` error does NOT mean it's in your project — it may belong to another project and be inaccessible. -- **Slate + Advanced I/O functions are cross-domain** — Slate serves from `*.onslate.com`, functions from `*.catalystserverless.com`. Relative paths like `/server/func/execute` DO NOT work — you'll get HTML back instead of JSON. Use the full function URL + `generateAuthToken()` + CORS whitelist in Console → Authentication → Authorized Domains. -- **Hosted Authentication must be enabled in console** — Before `/__catalyst/auth/login` works, enable it in Console → Authentication → Login → Hosted Authentication. The Web SDK does NOT auto-redirect on auth failure — you must redirect manually in the `.catch()` block. -- **AppSail port**: use `process.env.X_ZOHO_CATALYST_LISTEN_PORT` with fallback -- **Cache values are strings only** — serialize/deserialize JSON yourself -- **Integration Functions NOT available** in EU, AU, IN, or CA data centers -- **CLI always deploys to Development** — production deployment via web console only -- **New users after Aug 27, 2025** cannot access File Store, Event Listeners, or Cron — these services are deprecated (originally scheduled for removal April 30, 2026 — still functional with deprecation warnings, removal date TBD) -- **DataStore App User permissions are OFF by default (REQUIRED setup step)** — Newly created tables give App Users **zero permissions** — no Select, Insert, Update, or Delete. This is not optional configuration; it's a required step after creating every table. Go to **Console → Data Store → {Table} → Scopes & Permissions → Table Permissions → App User → check Select, Insert, Update, Delete**. Without this, any user-authenticated function call will fail silently or return permissions errors. Alternative: use admin-scoped SDK `catalyst.initialize(req, { scope: 'admin' })` to bypass user permissions. -- **Web client → function fetch must include `credentials: 'include'`** — without it, auth cookies are not forwarded and server-side `userManagement().getCurrentUser()` throws with 401, even when both web client and function are on the same Catalyst domain. -- **Web SDK `catalyst.auth.getCurrentUser()` does NOT exist** — use `catalyst.auth.isUserAuthenticated()` instead. It resolves with the full user object (`result.content.email_id`, etc.) on success and rejects with 401 on failure. The SDK does NOT auto-redirect — you must redirect manually to `/__catalyst/auth/login`. -- **Web SDK `catalyst.auth.signOut()` requires a redirect URL argument** — call `catalyst.auth.signOut(redirectURL)`. Calling it without an argument crashes. `constructSignOutUrl()` does not exist. -- **Advanced I/O `req` has no `body`, `files`, `query`, or `params`** — it is a raw `http.IncomingMessage`, not Express. For JSON: accumulate stream chunks. For file uploads: use `busboy`. For query params: use `new URL(req.url, ...).searchParams`. -- **Only node20 is actively supported** — node14, 16, and 18 still work for legacy projects but receive no upstream security patches. — `executeZCQLQuery` returns `[{ tablename: { ROWID: ..., col: ... } }]`. Always unwrap: `result.map(r => r.TableName)`. The key matches the table name as defined in the console (case-sensitive). -- **CREATEDTIME timezone trap** — Catalyst stores CREATEDTIME in the project's configured timezone (e.g. IST) WITHOUT an offset marker. Passing the raw string to `new Date()` treats it as UTC, producing timestamps that are hours off. Always append the project timezone offset before parsing. -- **Data Store does NOT support emoji / 4-byte UTF-8** — Inserting emoji or 4-byte UTF-8 characters (many CJK extensions) silently stores them as `?`. Workaround: store a string key (e.g. `"happy"`) and map to emoji in application code. -- **AppSail + Slate cross-origin issue** — In development, Slate-hosted frontends calling AppSail APIs get blocked by Catalyst's auth layer (manifests as "Unable to Fetch" or "Failed to fetch"). Fix: serve the frontend from AppSail itself using `express.static()` so all calls are same-origin. This eliminates CORS and auth-layer issues entirely. - -## Documentation links - -> **Note:** SDK doc URLs include a version segment (e.g., `/v2/`, `/v1/`). If a URL returns 404, the version may have been incremented — check the docs homepage for the latest version path. - -- Main docs: https://docs.catalyst.zoho.com/en/ -- Node.js SDK: https://docs.catalyst.zoho.com/en/sdk/nodejs/v2/overview/ -- Java SDK: https://docs.catalyst.zoho.com/en/sdk/java/v1/overview/ -- Python SDK: https://docs.catalyst.zoho.com/en/sdk/python/v1/overview/ -- Web SDK: https://docs.catalyst.zoho.com/en/sdk/web/v4/overview/ -- CLI reference: https://docs.catalyst.zoho.com/en/cli/v1/cli-command-reference/ -- REST API: https://docs.catalyst.zoho.com/en/api/introduction/overview-and-prerequisites/ -- Tutorials: https://docs.catalyst.zoho.com/en/tutorials/ -- GitHub: https://github.com/catalystbyzoho -- Pricing: https://catalyst.zoho.com/pricing.html diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/agents/openai.yaml b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/agents/openai.yaml deleted file mode 100644 index 1611e1c68..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/agents/openai.yaml +++ /dev/null @@ -1,10 +0,0 @@ -interface: - display_name: "Catalyst by Zoho" - short_description: "Build, deploy, and operate Catalyst serverless apps" - icon_small: "../../assets/catalyst_logo.svg" - icon_large: "../../assets/catalyst_logo.svg" - brand_color: "#226DB4" - default_prompt: "Use $catalyst-by-zoho to design, build, deploy, or troubleshoot Catalyst apps and services." - -policy: - allow_implicit_invocation: true diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/architecture-patterns.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/architecture-patterns.md deleted file mode 100644 index 1313a12dc..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/architecture-patterns.md +++ /dev/null @@ -1,435 +0,0 @@ -# Catalyst Architecture Patterns - -> **⚠️ PRE-FLIGHT CHECK:** Before building anything from these patterns, confirm `.catalystrc` and `catalyst.json` exist in the project directory. If not, STOP and tell the user to run `catalyst init` first. Do not scaffold the project yourself. - -## Purpose - -This file exists for one reason: **when a user describes a use case, map it to a complete Catalyst -infrastructure blueprint**. The user does not need to know what Catalyst services exist — read their -requirements, apply the decision matrix below, and produce a concrete architecture with named services, -how they connect, and what the folder structure looks like. - -**Always use this file when:** -- A user says "I want to build X" or "help me architect Y" -- A user has a use case but no idea what infrastructure they need -- A user asks "what Catalyst services should I use for my project?" - ---- - -## Step 1 — Requirement → Service Mapping - -Read the user's requirements and map each capability to a Catalyst service using this table. - -### Compute - -| Requirement | Catalyst Service | Notes | -|-------------|-----------------|-------| -| REST API / HTTP endpoints | **Advanced I/O Function** | Stateless, per-request, node20 | -| Simple string-in / string-out function | **Basic I/O Function** | GET only, limited use | -| Background job / async processing | **Job Scheduling** | Replaces deprecated Cron | -| Scheduled recurring task | **Job Scheduling** | Configure interval in console | -| Persistent server / WebSockets / long-lived process | **AppSail** | Docker or managed runtime | -| Respond to Zoho product events (CRM, Desk, etc.) | **Event Function** via **Signals** | Event-driven | -| Multi-step business workflow with approvals | **Circuits** | Visual drag-and-drop | -| Web scraping / PDF generation / screenshots | **Browser Logic Function** + **SmartBrowz** | Managed headless browser | - -### Storage - -| Requirement | Catalyst Service | Notes | -|-------------|-----------------|-------| -| Structured/relational data with queries | **Data Store** + **ZCQL** | Max 300 rows/query, paginate | -| Flexible schema / document data | **NoSQL** | Collections + documents | -| File/object storage (images, docs, exports) | **Stratus** | S3-compatible, preferred | -| Session state / temporary data (< 48hr) | **Cache** | Strings only, serialize JSON | -| Full-text search across records | **Search** | Enable per-column in console | - -### Frontend - -| Requirement | Catalyst Service | Notes | -|-------------|-----------------|-------| -| React / Vue / Next.js / static site | **Slate** | Git-based, SSR support, preferred | -| Simple HTML/JS (no build step) | **Web Client Hosting** | Legacy, `client/` directory | - -### Auth & Users - -| Requirement | Catalyst Service | Notes | -|-------------|-----------------|-------| -| User signup, login, session management | **Auth & User Management** | Built-in, Zoho IAM | -| OAuth to external services (Google, GitHub, etc.) | **Connections** | Token auto-refresh | -| SSO / enterprise login | **Auth (Custom SSO)** | Configure in console | - -### Integration - -| Requirement | Catalyst Service | Notes | -|-------------|-----------------|-------| -| React to Zoho CRM / Desk / Books events | **Signals** | Subscribe to Zoho product signals | -| Call external APIs with OAuth | **Connections** | Pre-built Zoho connectors + custom | -| Webhook from external services | **Advanced I/O Function** | Expose as HTTP endpoint | - -### AI / ML - -| Requirement | Catalyst Service | Notes | -|-------------|-----------------|-------| -| OCR, face detection, image moderation | **Zia Services** | Vision microservices | -| Text classification, sentiment, NLP | **Zia Services** | Text analytics microservices | -| Custom ML model (no-code) | **QuickML** | AutoML pipeline builder | -| Chatbot / conversational UI | **ConvoKraft** | Embeddable AI bot | - -### DevOps - -| Requirement | Catalyst Service | Notes | -|-------------|-----------------|-------| -| CI/CD pipeline (GitHub/GitLab) | **Pipelines** | YAML-based | -| App monitoring, alerting, logs | **DevOps (Logs + APM + Alerts)** | Built into console | - ---- - -## Step 2 — Architecture Blueprints - -Pre-composed architectures for common use cases. Use these as the starting point and adapt for the -user's specific requirements. - ---- - -### Blueprint 1: Standard Web App (SaaS / CRUD App) - -**Use case examples:** Task manager, CRM, dashboard, booking system, internal tool, notes app. - -**Requirements typically include:** User auth, relational data, REST API, web frontend. - -``` -┌─────────────────────────────────────────────────────┐ -│ User's Browser │ -└────────────────────┬────────────────────────────────┘ - │ HTTPS -┌────────────────────▼────────────────────────────────┐ -│ Slate (Frontend) │ -│ React / Vue / Next.js app │ -│ fetch('/server/api/execute', { credentials: │ -│ 'include', ... }) for all API calls │ -└────────────────────┬────────────────────────────────┘ - │ /server//execute -┌────────────────────▼────────────────────────────────┐ -│ Advanced I/O Function (REST API) │ -│ catalyst.initialize(req) → user-scoped auth │ -│ catalyst.initialize(req, {type: admin}) → DS ops │ -└──────────┬─────────────────┬───────────────────────┘ - │ │ -┌──────────▼──────┐ ┌───────▼────────┐ -│ Data Store │ │ Stratus │ -│ (user records, │ │ (file uploads, │ -│ app data) │ │ media, docs) │ -└─────────────────┘ └────────────────┘ - -Auth: Catalyst Auth & User Management - (signup → email verification → login → session cookie) -``` - -**Services:** Slate · Advanced I/O Function · Data Store · Stratus · Auth - -**Key implementation notes:** -- **Same-domain (legacy Web Client):** Use `credentials: 'include'` in `fetch()` calls -- **Cross-domain (Slate → Serverless Function):** Use `generateAuthToken()` with the full function URL and `Authorization: ${token}` header. Add Slate domain to Authorized Domains in console with CORS toggle enabled. Do NOT use Express `cors()` middleware — the gateway handles CORS headers for production origins. -- **⚠️ REQUIRED: Enable App User permissions on every table** — Console → Data Store → {Table} → Scopes & Permissions → App User → check Select, Insert, Update, Delete. Without this, ALL user-authenticated operations fail silently. This is not optional. -- Use admin-scoped SDK (`catalyst.initialize(req, { scope: 'admin' })`) for DataStore/Stratus/ZCQL operations -- Use user-scoped SDK (`catalyst.initialize(req)`) ONLY for `getCurrentUser()` for auth verification — admin scope cannot resolve user identity - -**Project structure:** -``` -project-root/ -├── catalyst.json -├── client/ # or use Slate (separate Git repo) -│ └── build/ -├── functions/ -│ └── api/ -│ ├── index.js # Advanced I/O handler -│ ├── package.json -│ └── catalyst-config.json -``` - ---- - -### Blueprint 2: Mobile App Backend (API-only) - -**Use case examples:** iOS/Android app backend, Flutter app, React Native app. - -**Requirements typically include:** REST API, user auth, data storage, push notifications. - -``` -┌─────────────────────────────────────────────────────┐ -│ Mobile App (iOS/Android/Flutter) │ -│ Uses Catalyst Mobile SDK or REST API │ -└────────────────────┬────────────────────────────────┘ - │ HTTPS + Catalyst Auth SDK -┌────────────────────▼────────────────────────────────┐ -│ Advanced I/O Function (REST API) │ -│ + API Gateway (optional) │ -│ Rate limiting, path routing, auth rules │ -└──────────┬─────────────────┬───────────────────────┘ - │ │ -┌──────────▼──────┐ ┌───────▼────────┐ ┌──────────────────┐ -│ Data Store │ │ Stratus │ │ Push Notification│ -│ (user data, │ │ (media, files) │ │ (APNs + FCM) │ -│ app records) │ └────────────────┘ └──────────────────┘ -└─────────────────┘ - -Optional background processing: -┌──────────────────────────────────────┐ -│ Job Scheduling (async tasks, │ -│ notifications, cleanup, exports) │ -└──────────────────────────────────────┘ -``` - -**Services:** Advanced I/O Function · API Gateway · Data Store · Stratus · Push Notifications · Job Scheduling · Auth - ---- - -### Blueprint 3: Event-Driven / Automation App - -**Use case examples:** Zoho CRM → send onboarding email when lead converts, sync data between Zoho products, trigger workflows on Zoho Desk ticket creation. - -**Requirements typically include:** React to Zoho product events, run business logic, update data. - -``` -┌─────────────────────────────────────────────────────┐ -│ Zoho Product (CRM, Desk, Books…) │ -└────────────────────┬────────────────────────────────┘ - │ Zoho product event -┌────────────────────▼────────────────────────────────┐ -│ Signals (Event Bus) │ -│ Subscribe to CatalystbyZoho product events │ -└────────────────────┬────────────────────────────────┘ - │ triggers -┌────────────────────▼────────────────────────────────┐ -│ Event Function │ -│ Business logic: transform data, call APIs, │ -│ send emails, update records │ -└──────────┬───────────┬─────────────────────────────┘ - │ │ -┌──────────▼──────┐ ┌──▼──────────────┐ -│ Data Store │ │ Connections │ -│ (store results)│ │ (OAuth to │ -└─────────────────┘ │ external APIs) │ - └─────────────────┘ - -For complex multi-step logic: -┌──────────────────────────────────────┐ -│ Circuits (multi-step workflow with │ -│ approvals, branching, retries) │ -└──────────────────────────────────────┘ -``` - -**Services:** Signals · Event Function · Data Store · Connections · Circuits (optional) - ---- - -### Blueprint 4: Scheduled Data Pipeline - -**Use case examples:** Nightly report generation, daily data sync from external API, periodic cache refresh, scheduled email digests. - -**Requirements typically include:** Run on schedule, fetch/process data, store results, send notifications. - -``` -┌────────────────────────────────────┐ -│ Job Scheduling (trigger) │ -│ Configure: interval, target fn │ -└──────────────┬─────────────────────┘ - │ triggers -┌──────────────▼─────────────────────┐ -│ Job Function (worker) │ -│ Fetch external data (via HTTP) │ -│ Transform / aggregate │ -│ Store results │ -└──────┬──────────────┬──────────────┘ - │ │ -┌──────▼──────┐ ┌────▼──────────┐ -│ Data Store │ │ Cache │ -│ (persist │ │ (hot results, │ -│ results) │ │ temp state) │ -└─────────────┘ └───────────────┘ - │ - ┌─────────▼──────────┐ - │ Mail (send digest │ - │ / alert email) │ - └────────────────────┘ -``` - -**Services:** Job Scheduling · Job Function · Data Store · Cache · Mail · Connections (if fetching from OAuth-protected APIs) - ---- - -### Blueprint 5: AI-Powered App - -**Use case examples:** Document OCR, image classification, customer sentiment analysis, AI chatbot, smart search. - -**Sub-pattern A — Vision / NLP pipeline:** -``` -┌──────────────┐ ┌───────────────────────┐ ┌─────────────┐ -│ Stratus │───▶│ Advanced I/O or Job │───▶│ Zia Services│ -│ (input files)│ │ Function (orchestrate│ │ (OCR, vision│ -└──────────────┘ │ processing) │ │ NLP, etc.) │ - └──────────────┬────────┘ └─────────────┘ - │ - ┌────────▼──────────┐ - │ Data Store │ - │ (store AI results)│ - └───────────────────┘ -``` - -**Sub-pattern B — Conversational chatbot:** -``` -┌─────────────────────────────────────────────────────┐ -│ Website / Web App (Slate) │ -│ Embed ConvoKraft bot snippet │ -└────────────────────┬────────────────────────────────┘ - │ -┌────────────────────▼────────────────────────────────┐ -│ ConvoKraft │ -│ Configure intents, responses, LLM integration │ -└────────────────────┬────────────────────────────────┘ - │ calls backend when needed -┌────────────────────▼────────────────────────────────┐ -│ Advanced I/O Function (API) │ -└────────────────────┬────────────────────────────────┘ - │ - ┌────▼─────────┐ - │ Data Store │ - │(conversation │ - │ history, etc)│ - └──────────────┘ -``` - -**Services (Vision/NLP):** Stratus · Job/Advanced I/O Function · Zia Services · Data Store - -**Services (Chatbot):** Slate · ConvoKraft · Advanced I/O Function · Data Store - ---- - -### Blueprint 6: Persistent Server App (Long-running / WebSockets) - -**Use case examples:** Real-time collaboration, WebSocket server, game server backend, long-running data processor, containerized microservice. - -``` -┌─────────────────────────────────────────────────────┐ -│ Client (Browser / Mobile) │ -└────────────────────┬────────────────────────────────┘ - │ HTTP / WebSocket -┌────────────────────▼────────────────────────────────┐ -│ AppSail │ -│ Node.js (Express) / Java (Spring Boot) / │ -│ Python (FastAPI) / Custom Docker │ -│ Auto-scales 1–5 instances │ -│ Port: process.env.X_ZOHO_CATALYST_LISTEN_PORT │ -└──────────┬────────────────┬────────────────────────┘ - │ │ -┌──────────▼──────┐ ┌───────▼──────┐ ┌──────────────┐ -│ Data Store │ │ Cache │ │ Stratus │ -│ (persistent │ │(session/room │ │(media, files)│ -│ data) │ │ state) │ └──────────────┘ -└─────────────────┘ └──────────────┘ -``` - -**Services:** AppSail · Data Store · Cache · Stratus - -**Key note:** Use admin-scoped SDK in AppSail: `catalyst.initialize(req, { scope: 'admin' })`. - ---- - -### Blueprint 7: Static / Content Site - -**Use case examples:** Marketing site, documentation site, landing page, blog. - -``` -┌─────────────────────────────────────────────────────┐ -│ Slate (Frontend) │ -│ Static site / SSR (Next.js, Astro, etc.) │ -│ Git-based deploy — push to branch → auto deploy │ -└────────────────────┬────────────────────────────────┘ - │ optional dynamic data -┌────────────────────▼────────────────────────────────┐ -│ Advanced I/O Function (optional) │ -│ Contact form, newsletter signup, search │ -└────────────────────┬────────────────────────────────┘ - │ - ┌────▼─────────┐ - │ Data Store │ - │(form submits,│ - │ subscribers) │ - └──────────────┘ -``` - -**Services:** Slate · Advanced I/O Function (optional) · Data Store (optional) - ---- - -### Blueprint 8: Web Scraping / Automation Pipeline - -**Use case examples:** Price monitoring, data extraction from websites, automated PDF report generation, screenshot service. - -``` -┌────────────────────────────────────┐ -│ Job Scheduling (trigger) │ -│ or Advanced I/O (on-demand) │ -└──────────────┬─────────────────────┘ - │ -┌──────────────▼─────────────────────┐ -│ Browser Logic Function │ -│ + SmartBrowz (headless browser) │ -│ Puppeteer-like API │ -│ Navigate → extract → screenshot │ -└──────────────┬─────────────────────┘ - │ results -┌──────────────▼─────────────────────┐ -│ Stratus (store PDFs/screenshots) │ -│ Data Store (store extracted data)│ -└────────────────────────────────────┘ -``` - -**Services:** Job Scheduling · Browser Logic Function · SmartBrowz · Stratus · Data Store - ---- - -## Step 3 — How to Propose an Architecture (Workflow for the Model) - -When a user describes their use case, follow this process: - -1. **Identify the primary app pattern** — match to one of the 8 blueprints above (or combine multiple). - -2. **Map individual requirements** — use the decision matrix in Step 1 for any requirements not covered by the blueprint. - -3. **Produce the architecture proposal** in this format: - - ``` - ## Proposed Architecture for [User's App] - - ### Services you'll need - | Service | Purpose | - |---------|---------| - | Slate | Frontend hosting for your React app | - | Advanced I/O Function | REST API for CRUD operations | - | Data Store | Store [user's entities, e.g. medications, tasks, orders] | - | Auth | User signup and login | - | Stratus | File uploads (profile photos, documents) | - - ### How they connect - [Describe the data flow — browser → frontend → function → DB → response] - - ### Project structure - [Show the folder layout] - - ### First steps - 1. `catalyst init` — initialize the project - 2. Create Data Store tables: [list table names + columns] - 3. Set up Auth in the console (Authentication section) - 4. Build the Advanced I/O function at `functions/api/` - 5. Initialize Slate for the frontend - ``` - -4. **Check for Zoho MCP** — if `CatalystbyZoho_*` tools are available, offer to create the Data Store tables and Stratus buckets directly from the conversation. - -5. **Flag common gotchas** relevant to the architecture: - - DataStore → remind to enable CRUD permissions for App User role - - Web client → function calls → remind about `credentials: 'include'` (same-domain) or `generateAuthToken()` (cross-domain from Slate) - - Slate + Functions → remind: gateway owns CORS headers, no Express `cors()` middleware - - AppSail → remind about the correct port env variable - - Job Scheduling → note that CLI always deploys to Development diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/cli-reference.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/cli-reference.md deleted file mode 100644 index 5ba79ae0c..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/cli-reference.md +++ /dev/null @@ -1,676 +0,0 @@ -# CLI Command Reference - -Complete reference for the Catalyst CLI (`catalyst` / `zcatalyst`). For official docs see: https://docs.catalyst.zoho.com/en/cli/v1/cli-command-reference/ - ---- - -## Table of Contents -1. [Global Options](#global-options) -2. [Authentication & Identity](#authentication--identity) -3. [Token Management](#token-management) -4. [Project Management](#project-management) -5. [Initialization & Setup](#initialization--setup) -6. [Functions](#functions) -7. [Client](#client) -8. [Slate](#slate) -9. [AppSail Setup](#appsail-setup) -10. [Data Store](#data-store) -11. [API Gateway](#api-gateway) -12. [IAC (Infrastructure as Code)](#iac) -13. [Event & Signal Payload Generation](#event--signal-payload-generation) -14. [Configuration](#configuration) -15. [Code Library](#code-library) -16. [Local Development](#local-development) -17. [Deployment](#deployment) -18. [Other Commands](#other-commands) -19. [Safety Rules](#safety-rules) -20. [Troubleshooting](#troubleshooting) -21. [Resource-First Development Order](#resource-first-development-order) - ---- - -## Global Options - -These flags can be used with any command: - -| Flag | Description | -|------|-------------| -| `-v`, `--version` | Print CLI version | -| `-p`, `--project` | Specify the project ID or name to target | -| `--org` | Specify the organization ID | -| `--token` | Use a Catalyst auth token instead of interactive login | -| `--dc` | Data center region: `us`, `eu`, `in`, `au`, `jp`, `sa`, `ca` | -| `--verbose` | Enable verbose/debug output for troubleshooting | -| `-h`, `--help` | Show help for a command | - ---- - -## Authentication & Identity - -### `catalyst login` -Authenticate with Zoho Catalyst. Opens a browser for OAuth by default. - -```bash -catalyst login # Interactive browser-based login -catalyst login --no-localhost # Use manual code entry (for remote/headless machines) -catalyst login --force # Force re-login even if already authenticated -``` - -### `catalyst logout` -Log out of the current session, clearing stored credentials. - -```bash -catalyst logout -``` - -### `catalyst whoami` -Display the currently logged-in user and associated org details. - -```bash -catalyst whoami -``` - ---- - -## Token Management - -### `catalyst token:generate` -Generate a new auth token for CI/CD or automation. - -```bash -catalyst token:generate # Generate a new token -catalyst token:generate --current # Generate token for the current project context -``` - -### `catalyst token:list` -List all active tokens. - -```bash -catalyst token:list -``` - -### `catalyst token:revoke` -Revoke an existing token. - -```bash -catalyst token:revoke -``` - ---- - -## Project Management - -### `catalyst project:list` -List all projects accessible in the current org. - -```bash -catalyst project:list -``` - -### `catalyst project:use` -Set the active project context for subsequent commands. - -```bash -catalyst project:use -``` - -### `catalyst project:reset` -Clear the current project context. - -```bash -catalyst project:reset -``` - ---- - -## Initialization & Setup - -### `catalyst init` -Initialize a Catalyst project in the current directory. - -**IMPORTANT: ALWAYS use `--non-interactive` when running from an automated agent.** - -```bash -catalyst init # Interactive mode -catalyst init --force --org --project --non-interactive # Non-interactive (REQUIRED for agents) -``` - -| Flag | Description | -|------|-------------| -| `--force` | Overwrite existing project config | -| `--org` | Organization ID | -| `--project` | Project ID | -| `--non-interactive` | Skip all prompts (ALWAYS use this in automation) | - -### `catalyst functions:setup` -Set up the functions directory structure in the current project. - -```bash -catalyst functions:setup -``` - -### `catalyst functions:add` -Add a new function to the project. **This is fully interactive** — it prompts for function name, type (arrow keys), and stack (arrow keys). There are NO non-interactive flags (unlike `init`, `appsail:add`, and `slate:create` which all support flags). - -```bash -catalyst functions:add # ⚠️ ALWAYS interactive — no --name/--type/--stack flags exist -``` - -**Known limitation for AI agents:** This is the only setup command that cannot be automated. The user MUST run it interactively for the first function in a project. - -**Agent workaround for subsequent functions:** Once the user has run `functions:add` at least once (so `catalyst.json` has a populated `functions` block), agents can add more functions by manually: -1. Creating the directory: `functions//` -2. Adding `functions//catalyst-config.json`: - ```json - { - "deployment": { - "name": "", - "type": "advancedio", - "stack": "node20" - }, - "execution": { - "main": "index.js" - } - } - ``` - Valid `type` values: `basicio`, `advancedio`, `event`, `cron`, `job`, `integration`, `browserlogic` - Valid `stack` values: `node20`, `node18`, `node16`, `node14`, `java17`, `java11`, `java8`, `python39` - (Prefer `node20` for new projects — `node14`/`node16` receive no upstream security patches) -3. Adding the function name to `catalyst.json` → `functions.targets` array: - ```json - { - "functions": { - "targets": ["existing_function", "new_function_name"], - "ignore": [], - "source": "functions" - } - } - ``` -4. Creating the entry point file (`index.js`, `main.py`, etc.) with the correct handler signature -5. Running `npm init -y && npm install zcatalyst-sdk-node` in the function directory (for Node.js) - -**Important:** This workaround only works when `catalyst.json` already has a `functions` block from a prior `functions:add`. If no function has ever been registered, the user must run `functions:add` interactively first. - -### `catalyst client:setup` -Set up the client (frontend) directory in the current project. - -```bash -catalyst client:setup -``` - -### `catalyst appsail:add` -Add an AppSail service. **ALWAYS use flags to avoid interactive prompts.** - -```bash -catalyst appsail:add --name --stack -catalyst appsail:add --name --stack --source --build --platform --overwrite-config -``` - -| Flag | Description | -|------|-------------| -| `--name` | Service name (required) | -| `--stack` | Runtime stack, e.g. `node18`, `java17`, `python_3_9` (required) | -| `--source` | Source directory path | -| `--build` | Build command | -| `--platform` | Target platform | -| `--overwrite-config` | Overwrite existing config if present | - ---- - -## Functions - -### `catalyst functions:shell` -Open an interactive shell for testing functions locally. - -```bash -catalyst functions:shell -``` - -### `catalyst functions:execute` -Execute a function locally. - -```bash -catalyst functions:execute -``` - -### `catalyst functions:config` -View or modify function configuration. - -```bash -catalyst functions:config # View config -catalyst functions:config --memory # View/set memory allocation -``` - -### `catalyst functions:delete` -Delete a function. - -```bash -catalyst functions:delete --local # Remove only from local project -catalyst functions:delete --remote # Remove from remote (deployed) project -``` - ---- - -## Client - -### `catalyst client:setup` -Initialize the client directory for the project. - -```bash -catalyst client:setup -``` - -### `catalyst client:delete` -Delete the client component. - -```bash -catalyst client:delete --local # Remove only from local project -catalyst client:delete --remote # Remove from remote (deployed) project -``` - ---- - -## Slate - -Slate is Catalyst's frontend framework scaffolding system. **NEVER scaffold manually (no `npm create vite`, etc.).** Always use Slate commands. Additional libraries should be installed AFTER scaffolding. - -### `catalyst slate:create` -Create a new Slate frontend project. - -```bash -catalyst slate:create --name --framework -catalyst slate:create --name --framework --default -``` - -| Flag | Description | -|------|-------------| -| `--name` | Slate project name | -| `--framework` | Framework to use (see table below) | -| `--default` | Use default settings without prompts | - -#### Framework Values - -| Framework Value | Detection Keywords | Build Output Directory | -|----------------|-------------------|----------------------| -| `static` | Plain HTML/CSS/JS | `.` or `public/` | -| `angular` | Angular, @angular/core | `dist/` | -| `astro` | Astro | `dist/` | -| `create-react-app` | CRA, create-react-app | `build/` | -| `nextjs` | Next.js, next | `out/` or `.next/` | -| `preact` | Preact | `dist/` | -| `react-vite` | React + Vite | `dist/` | -| `solidjs` | SolidJS, Solid | `dist/` | -| `svelte` | Svelte, SvelteKit | `dist/` or `build/` | -| `vue` | Vue.js, Vue 3 | `dist/` | -| `other` | Custom/unknown | Varies | - -#### `dev_command` per Framework (in `cli-config.json`) - -| Framework | Dev Command | -|-----------|------------| -| React + Vite | `npx vite --port $PORT` | -| Next.js | `npx next dev --port $PORT` | -| Angular | `npx ng serve --port $PORT` | -| Astro | `npx astro dev --port $PORT` | -| Vue | `npx vite --port $PORT` | -| SolidJS | `npx vite --port $PORT` | -| Preact | `npx vite --port $PORT` | -| Svelte | `npx vite --port $PORT` | -| Create React App | `npx react-scripts start` (PORT env var) | - -### `catalyst slate:link` -Link an existing local directory as a Slate project. - -```bash -catalyst slate:link -``` - -### `catalyst slate:unlink` -Unlink a Slate project from the Catalyst project. - -```bash -catalyst slate:unlink -``` - ---- - -## AppSail Setup - -AppSail is for deploying full application servers (Express, Spring Boot, Flask, etc.). - -**ALWAYS use flags to avoid interactive prompts.** - -```bash -# Node.js 18 -catalyst appsail:add --name my-api --stack node18 - -# Java 17 WAR -catalyst appsail:add --name my-service --stack java17 - -# Python 3.9 -catalyst appsail:add --name my-app --stack python_3_9 - -# With all options -catalyst appsail:add --name my-api --stack node18 --source ./server --build "npm run build" --platform linux --overwrite-config -``` - ---- - -## Data Store - -### `catalyst ds:import` -Import data into the Data Store from a CSV file. - -```bash -catalyst ds:import -``` - -### `catalyst ds:export` -Export Data Store tables to CSV. - -```bash -catalyst ds:export -``` - -### `catalyst ds:status` -Check the status of a Data Store import/export operation. - -```bash -catalyst ds:status -``` - ---- - -## API Gateway - -### `catalyst apig:enable` -Enable the API Gateway for the current project. - -```bash -catalyst apig:enable -``` - -### `catalyst apig:disable` -Disable the API Gateway. - -```bash -catalyst apig:disable -``` - -### `catalyst apig:status` -Check API Gateway status. - -```bash -catalyst apig:status -``` - ---- - -## IAC - -Infrastructure as Code for managing project resources declaratively. - -### `catalyst iac:pack` -Package the current project state into an IAC archive. - -```bash -catalyst iac:pack -``` - -### `catalyst iac:import` -Import an IAC package into the project. - -```bash -catalyst iac:import -n # Import with a specific name -``` - -### `catalyst iac:export` -Export the project configuration as an IAC package. - -```bash -catalyst iac:export # Export development config -catalyst iac:export --production # Export production config (CAUTION: targets live environment) -``` - -### `catalyst iac:status` -Check the status of an IAC operation. - -```bash -catalyst iac:status -``` - ---- - -## Event & Signal Payload Generation - -Generate sample payload files for testing event listeners, integrations, jobs, and signals. - -### `catalyst event:generate` -Generate a sample event payload. - -```bash -catalyst event:generate -``` - -### `catalyst event:generate:integ` -Generate a sample integration event payload. - -```bash -catalyst event:generate:integ -``` - -### `catalyst event:generate:job` -Generate a sample job event payload. - -```bash -catalyst event:generate:job -``` - -### `catalyst signals:generate` -Generate a sample signal payload. - -```bash -catalyst signals:generate -``` - ---- - -## Configuration - -Manage CLI configuration key-value pairs. - -### `catalyst config:set` -Set a configuration value. - -```bash -catalyst config:set -``` - -### `catalyst config:get` -Get a configuration value. - -```bash -catalyst config:get -``` - -### `catalyst config:delete` -Delete a configuration key. - -```bash -catalyst config:delete -``` - -### `catalyst config:list` -List all configuration values. - -```bash -catalyst config:list -``` - ---- - -## Code Library - -### `catalyst codelib:install` -Install a code library into the project. - -```bash -catalyst codelib:install -``` - ---- - -## Local Development - -### `catalyst serve` -Start the local development server. Serves functions, client, and AppSail locally. - -**IMPORTANT: The `catalyst serve` port is dynamic. Never hardcode the port. Never use Vite's dev server directly -- always use `catalyst serve`.** - -```bash -catalyst serve # Start with defaults -catalyst serve --http # Force HTTP (no HTTPS) -catalyst serve --debug # Enable debug mode -catalyst serve --proxy # Enable proxy mode -catalyst serve --only functions # Serve only functions -catalyst serve --only client # Serve only client -catalyst serve --except appsail # Serve everything except AppSail -catalyst serve --no-watch # Disable file watching/hot reload -catalyst serve --no-open # Don't auto-open browser -``` - -| Flag | Description | -|------|-------------| -| `--http` | Use HTTP instead of HTTPS | -| `--debug` | Enable debug/verbose output | -| `--proxy` | Enable proxy mode for API calls | -| `--only ` | Serve only the specified component(s) | -| `--except ` | Serve everything except specified component(s) | -| `--no-watch` | Disable file watcher / hot reload | -| `--no-open` | Don't open the browser automatically | - ---- - -## Deployment - -### `catalyst deploy` -Deploy the project to Catalyst cloud. - -```bash -catalyst deploy # Deploy everything -catalyst deploy --only functions # Deploy only functions -catalyst deploy --only client # Deploy only client -catalyst deploy --except appsail # Deploy everything except AppSail -``` - -#### AppSail Deploy Options - -```bash -catalyst deploy appsail -``` - -#### Slate Deploy Options - -```bash -catalyst deploy slate -m "Deployment message" -catalyst deploy slate --production # Deploy to production (CAUTION) -``` - -| Flag | Description | -|------|-------------| -| `--only ` | Deploy only the specified component | -| `--except ` | Deploy everything except specified component | -| `-m` | Deployment message (for Slate) | -| `--production` | Deploy to production environment (CAUTION) | - ---- - -## Other Commands - -### `catalyst pull` -Pull remote project resources to local. - -```bash -catalyst pull -``` - -### `catalyst run-script` -Run a custom script defined in the project. - -```bash -catalyst run-script -``` - -### `catalyst help` -Display help for any command. - -```bash -catalyst help -catalyst help -catalyst --help -``` - ---- - -## Safety Rules - -### Destructive Commands Reference - -| Command | Risk Level | What It Does | Safeguard | -|---------|-----------|--------------|-----------| -| `functions:delete --remote` | HIGH | Deletes deployed function | Confirm project first | -| `client:delete --remote` | HIGH | Deletes deployed client | Confirm project first | -| `deploy --production` | HIGH | Pushes to production | Verify project and changes | -| `deploy slate --production` | HIGH | Pushes Slate to production | Verify project and changes | -| `iac:export --production` | MEDIUM | Exports production config | May expose secrets | -| `iac:import` | MEDIUM | Overwrites project resources | Verify package contents | -| `ds:import` | MEDIUM | Overwrites Data Store data | Verify CSV and table | -| `project:reset` | LOW | Clears project context | Re-run `project:use` | - -### Critical Rules - -- **`--production` flag warning**: Any command with `--production` targets the live production environment. Always double-check the project context before using this flag. -- **Always confirm project before mutating**: Run `catalyst whoami` and verify the project context before running any destructive or deployment command. - ---- - -## Troubleshooting - -### Common Issues - -| Issue | Diagnosis | Solution | -|-------|-----------|---------| -| Login fails | Auth token expired or browser blocked | Run `catalyst login --force` or use `--no-localhost` for headless | -| Wrong project targeted | Stale `.catalystrc` or context | Run `catalyst whoami`, then `catalyst project:use` or `catalyst init` | -| Wrong data center | Mismatched `--dc` flag | Re-login with correct `--dc` (us/eu/in/au/jp/sa/ca) | -| Deploy fails | Missing config, build errors | Check `catalyst.json`, run `catalyst deploy --verbose` | -| Functions not found | Missing `catalyst-config.json` or wrong directory structure | Verify `functions//catalyst-config.json` exists | -| Port conflicts | Another process using the port | Stop other servers; `catalyst serve` assigns ports dynamically | -| Token expired | Stale auth token | Run `catalyst token:generate` or `catalyst login --force` | -| IAC status stuck | Long-running import/export | Run `catalyst iac:status` to check progress | -| DS import fails | Malformed CSV or schema mismatch | Verify CSV format matches table schema; run `catalyst ds:status` | - -### Debugging - -- **Enable verbose output**: Add `--verbose` to any command for detailed logs. -- **Get command help**: Run `catalyst help ` or `catalyst --help`. - ---- - -## Resource-First Development Order - -Always follow this order when building a Catalyst project: - -1. **Login**: `catalyst login` -2. **Init**: `catalyst init --force --org --project --non-interactive` -3. **Create tables**: Set up Data Store tables (via console or IAC) -4. **Configure permissions**: Set table-level and row-level access -5. **Seed data**: Import initial data with `catalyst ds:import` -6. **Set up compute**: Add functions (`functions:add`), AppSail (`appsail:add`), or Slate (`slate:create`) -7. **Write code**: Implement business logic using the Catalyst SDK -8. **Serve locally**: `catalyst serve` (port is dynamic, never hardcode) -9. **Deploy**: `catalyst deploy` - ---- - -External documentation: https://docs.catalyst.zoho.com/en/cli/v1/cli-command-reference/ diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/cloud-scale.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/cloud-scale.md deleted file mode 100644 index e09a63da8..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/cloud-scale.md +++ /dev/null @@ -1,734 +0,0 @@ -# Cloud Scale Components Reference - -## Table of Contents -1. [Data Store](#data-store) -2. [ZCQL (Catalyst Query Language)](#zcql) -3. [Stratus (Object Storage)](#stratus) -4. [NoSQL](#nosql) -5. [Cache](#cache) -6. [Search](#search) -7. [Authentication & User Management](#authentication--user-management) -8. [API Gateway](#api-gateway) -9. [Connections](#connections) -10. [Mail](#mail) -11. [Push Notifications](#push-notifications) -12. [Web Client Hosting](#web-client-hosting) -13. [Domain Mappings](#domain-mappings) -14. [Deprecated Components](#deprecated-components) - ---- - -## Data Store - -Catalyst Data Store is a fully-managed relational database. Tables are created and managed via the console. - -### Concurrency limits - -Each function has a default limit of **10 concurrent executions** per environment. - -When the concurrency limit is hit, additional invocations are queued and then rejected -with HTTP 429 (Too Many Requests) if the queue is full. - -To increase the limit, contact Catalyst support. - -**Design for concurrency:** Use idempotent handlers, avoid shared mutable state, -and monitor via DevOps → Logs for throttling signals (HTTP 429 responses). - -### System columns (auto-managed, present in every table) -- `ROWID` — unique row identifier (bigint, auto-increment) -- `CREATORID` — user ID of the creator -- `CREATEDTIME` — timestamp of creation -- `MODIFIEDTIME` — timestamp of last modification - -### CREATEDTIME timezone behavior - -`CREATEDTIME` and `MODIFIEDTIME` are stored in the **project's configured timezone** (e.g. Asia/Kolkata -for IST), but without a UTC offset marker. This means `new Date(row.CREATEDTIME)` treats the value -as UTC, causing an off-by-hours error equal to the timezone offset. - -Passing the raw string to `new Date()` produces unpredictable results because Node.js -parses space-separated datetime strings as local time (not UTC). The correct approach -is to force UTC parsing by appending 'Z', then subtract the project timezone offset. - -```javascript -// WRONG — space-separated datetime is parsed as LOCAL time in Node.js (not UTC): -const created = new Date(row.CREATEDTIME); // Unpredictable — depends on server timezone - -// CORRECT — force UTC interpretation, then adjust for the project timezone: -function parseCatalystTime(catalystTimestamp, tzOffsetMinutes) { - // tzOffsetMinutes: positive = ahead of UTC, e.g. IST = +330 - // Step 1: Parse as UTC by appending 'Z' - const utcDate = new Date(catalystTimestamp.replace(' ', 'T').replace(/:(\d{3})$/, '.$1') + 'Z'); - // Step 2: The timestamp was actually in project timezone, so subtract the offset - return new Date(utcDate.getTime() - tzOffsetMinutes * 60 * 1000); -} -// For IST (UTC+5:30 = +330 minutes): -const created = parseCatalystTime(row.CREATEDTIME, 330); -``` - -The project timezone is set during `catalyst init` and stored in `.catalystrc` (`timezone` field). - -### CRUD Operations (Node.js SDK) - -```javascript -// Get a table reference -const table = catalystApp.datastore().table('Employees'); -// or by ID: const table = catalystApp.datastore().table(TABLE_ID); - -// INSERT a row -const insertedRow = await table.insertRow({ - Name: 'Alice', - Email: 'alice@example.com', - Department: 'Engineering', - Salary: 85000 -}); -console.log('Inserted ROWID:', insertedRow.ROWID); - -// GET a row by ROWID -const row = await table.getRow(ROWID); - -// GET all rows (paginated, max 200 per call) -const allRows = await table.getAllRows({ - nextToken: null, // for pagination - maxRows: 100 // max 200 -}); - -// UPDATE a row (must include ROWID) -const updatedRow = await table.updateRow({ - ROWID: '12345', - Salary: 90000 -}); - -// DELETE a row -await table.deleteRow(ROWID); - -// BULK INSERT (up to 200 rows) -const rows = [ - { Name: 'Bob', Email: 'bob@example.com' }, - { Name: 'Carol', Email: 'carol@example.com' } -]; -const insertedRows = await table.insertRows(rows); - -// BULK UPDATE (up to 200 rows, each must have ROWID) -const updatedRows = await table.updateRows([ - { ROWID: '123', Name: 'Robert' }, - { ROWID: '456', Name: 'Caroline' } -]); - -// BULK DELETE -await table.deleteRows([ROWID_1, ROWID_2]); -``` - -### Column types supported -- Text, Integer, BigInt, Decimal, Double -- Boolean, Date, DateTime -- Foreign Key (references another table's ROWID) -- Text Area (for large text) -- Encrypted Text (for sensitive data) - -### Data Store limitations - -**Emoji and 4-byte UTF-8 characters**: Catalyst Data Store does not support 4-byte UTF-8 characters -(emoji, supplementary CJK, etc.) in Text or Text Area columns. These characters are silently -truncated or stored as `?` with no error thrown. Validate or strip emoji before inserting if your -data may contain them: - -```javascript -// Strip 4-byte UTF-8 characters before inserting -function stripEmoji(str) { - return str.replace(/[\u{10000}-\u{10FFFF}]/gu, ''); -} -const safeName = stripEmoji(userInput); -await table.insertRow({ Name: safeName }); -``` - ---- - -## ZCQL - -ZCQL is Catalyst's query language, modeled after SQL with important differences. - -### Basic queries -```javascript -const zcql = catalystApp.zcql(); - -// SELECT -const result = await zcql.executeZCQLQuery( - "SELECT * FROM Employees WHERE Department = 'Engineering'" -); - -// SELECT with conditions -const result = await zcql.executeZCQLQuery( - "SELECT Name, Email FROM Employees WHERE Salary > 80000 ORDER BY Name ASC LIMIT 50" -); - -// INSERT (prefer SDK methods, but ZCQL supports it) -await zcql.executeZCQLQuery( - "INSERT INTO Employees (Name, Email) VALUES ('Dave', 'dave@example.com')" -); - -// UPDATE -await zcql.executeZCQLQuery( - "UPDATE Employees SET Salary = 95000 WHERE ROWID = 12345" -); - -// DELETE -await zcql.executeZCQLQuery( - "DELETE FROM Employees WHERE Department = 'Obsolete'" -); - -// Aggregate functions -const result = await zcql.executeZCQLQuery( - "SELECT COUNT(ROWID) AS total, AVG(Salary) AS avg_salary FROM Employees" -); -``` - -### Result structure — unwrapping - -`executeZCQLQuery` returns an array of objects where each object wraps the row under the table name -as a key. You must unwrap before accessing fields: - -```javascript -const result = await zcql.executeZCQLQuery("SELECT * FROM Employees"); -// Raw result shape: -// [{ Employees: { ROWID: "123", Name: "Alice", Department: "Engineering" } }, ...] - -// Unwrap to get plain row objects: -const rows = result.map(r => r.Employees).filter(Boolean); -// rows → [{ ROWID: "123", Name: "Alice", Department: "Engineering" }, ...] - -// Generic helper for any table: -function unwrapZcql(result, tableName) { - return result.map(r => r[tableName]).filter(Boolean); -} -const employees = unwrapZcql(result, 'Employees'); -``` - -Important: the table name key is **case-sensitive** and must match the table name exactly as defined -in the console. Aggregate queries (e.g. `COUNT`, `AVG`) return a different shape — access via -`result[0]?.Employees` or inspect the raw result first. - -### ZCQL differences from standard SQL -- Table names are **case-sensitive** — must match exactly as created in the console -- Column names are **case-sensitive** -- String values use **single quotes only** -- Supported aggregate functions: `COUNT`, `SUM`, `AVG`, `MIN`, `MAX` -- `LIKE` operator is supported for pattern matching -- `IN`, `NOT IN`, `BETWEEN` are supported -- `ORDER BY` and `LIMIT` are supported -- `GROUP BY` and `HAVING` are supported -- `COALESCE` function is supported -- `DISTINCT` is supported -- **No JOINs** between tables of different types (you can join within the same Data Store) -- Results always include system columns unless you select specific columns -- Maximum 300 rows returned per query unless using pagination -- `LIMIT` maximum value is 300; use `LIMIT offset, count` syntax for pagination - -### Pagination with ZCQL - -#### Offset-based pagination (preferred) -```javascript -const PAGE_SIZE = 300; -let offset = 0; -let allResults = []; - -while (true) { - const batch = await zcql.executeZCQLQuery( - `SELECT * FROM Employees ORDER BY ROWID LIMIT ${offset}, ${PAGE_SIZE}` - ); - const rows = batch.map(r => r.Employees).filter(Boolean); - if (rows.length === 0) break; - allResults = allResults.concat(rows); - if (rows.length < PAGE_SIZE) break; // last page - offset += PAGE_SIZE; -} -``` - -#### ROWID-based pagination (alternative) -```javascript -// Useful when rows may be deleted mid-fetch -let lastRowId = 0; -let allResults = []; - -while (true) { - const batch = await zcql.executeZCQLQuery( - `SELECT * FROM Employees WHERE ROWID > ${lastRowId} ORDER BY ROWID LIMIT 300` - ); - const rows = batch.map(r => r.Employees).filter(Boolean); - if (rows.length === 0) break; - allResults = allResults.concat(rows); - lastRowId = rows[rows.length - 1].ROWID; -} -``` - -### JOINs - -ZCQL supports INNER JOIN and LEFT JOIN between tables in the same Data Store: - -```sql --- INNER JOIN -SELECT E.Name, D.DeptName -FROM Employees E -INNER JOIN Departments D ON E.DeptId = D.ROWID - --- LEFT JOIN -SELECT E.Name, D.DeptName -FROM Employees E -LEFT JOIN Departments D ON E.DeptId = D.ROWID -``` - -Limitations: -- Only INNER JOIN and LEFT JOIN are supported (no RIGHT or FULL OUTER JOIN) -- Cross-table JOINs only — no cross-type JOINs (e.g., Data Store to NoSQL) -- JOINs are subject to the same 300-row result limit - -### Transactions - -Catalyst Data Store does **not** support multi-statement transactions. There is no -BEGIN / COMMIT / ROLLBACK. - -**Workarounds for atomic operations:** -- Use single ZCQL statements that update multiple rows atomically (e.g., bulk UPDATE) -- Implement application-level saga patterns using Circuits for multi-step workflows -- Use optimistic concurrency control: read MODIFIEDTIME before update, verify it hasn't - changed before writing, retry on conflict - ---- - -## Stratus (Object Storage) - -**Stratus is the preferred storage solution for ALL file/object storage needs in new Catalyst projects.** -S3-compatible object storage with bucket-based organization. Replaces the removed File Store. - -### Key features -- **Buckets & Objects**: Data stored as objects in buckets, each with a unique Object URL -- **Path support**: Objects can be organized with path prefixes (e.g., `data/reports/file.json`) -- **Versioning**: Store multiple versions of each object, access by `versionId` -- **Data Encryption**: Encryption at rest and in flight when enabled -- **PII/ePHI**: HIPAA-compliant storage for sensitive/personally identifiable information -- **Malware scanning**: Automatic background scanning; infected objects are deleted immediately -- **Multipart uploads**: Upload large objects in parallel parts; auto-split if parts not specified -- **Transfer Manager**: Range-based downloads (specify start/end byte range for large objects) -- **Custom permissions**: JSON-based per-object and per-directory access rules -- **Third-party migration**: Direct migration from Amazon S3 and Google Cloud Storage via console - -### Permission templates -- **Authenticated**: Only authenticated app users can access objects (default for most apps) -- **Public**: Any internet user can access objects without authorization -- Custom JSON rules can override per-object using `rule_id`, `condition`, `allowed_actions` - (`GetObject`, `PutObject`, `DeleteObject`), `paths`, and `effect` (`allow`/`deny`) - -### SDK Operations (Node.js) -```javascript -const stratus = catalystApp.stratus(); -const bucket = stratus.bucket('my-bucket'); -// Bucket name from Console → Cloud Scale → Stratus - -// Upload an object -await bucket.putObject({ - key: 'data/file.json', - body: JSON.stringify(data), - contentType: 'application/json' -}); - -// Get/download an object -const obj = await bucket.getObject('data/file.json'); - -// Get a specific version (when versioning enabled) -const versionedObj = await bucket.getObject('data/file.json', { - versionId: '01hter85pvexb8s2s2842rpswh' -}); - -// Delete an object -await bucket.deleteObject('data/file.json'); - -// List objects (with pagination) -const objects = await bucket.listObjects({ - prefix: 'data/', - maxKeys: 100, - continuationToken: null // for pagination -}); - -// Check if bucket exists -const exists = await stratus.headBucket('my-bucket'); -``` - -### SDK availability -- Server: Node.js (`catalystApp.stratus()`), Java (`ZCStratus.getInstance()`), Python -- Client: Web SDK, Android SDK, iOS SDK, Flutter SDK -- REST API: Full API support for all Stratus operations - -### Upload size limits - -- **Single-shot upload:** up to 100 MB -- **Multipart upload:** required for files larger than 100 MB - -### Multipart upload (large files) - -For files larger than 100 MB, use multipart upload: - -```javascript -const stratus = catalystApp.stratus(); -const bucket = stratus.bucket('my-bucket'); - -// Step 1: Initiate multipart upload -const upload = await bucket.initiateMultipartUpload({ key: 'large-file.zip' }); -const uploadId = upload.uploadId; - -// Step 2: Upload parts (each 5–100 MB) -const parts = []; -for (let i = 0; i < chunks.length; i++) { - const part = await bucket.uploadPart({ - key: 'large-file.zip', - uploadId, - partNumber: i + 1, - body: chunks[i] - }); - parts.push({ partNumber: i + 1, eTag: part.eTag }); -} - -// Step 3: Complete upload -await bucket.completeMultipartUpload({ - key: 'large-file.zip', - uploadId, - parts -}); -``` - -### Signed URLs (time-limited access) - -```javascript -// Generate a pre-signed download URL (expires in 1 hour) -const url = await bucket.getSignedUrl({ - key: 'confidential-report.pdf', - expiresIn: 3600 // seconds -}); -// Share `url` with the client — no auth needed to download within the expiry window -``` - ---- - -## NoSQL - -Document database for semi-structured data. - -```javascript -const nosql = catalystApp.nosql(); - -// Access a collection -const collection = nosql.collection('UserProfiles'); -// Collection name from Console → Cloud Scale → NoSQL - -// Insert a document -const doc = await collection.insertDocument({ - userId: 'u123', - preferences: { theme: 'dark', language: 'en' }, - tags: ['premium', 'beta'] -}); - -// Get a document -const fetchedDoc = await collection.getDocument(DOCUMENT_ID); - -// Update a document -await collection.updateDocument(DOCUMENT_ID, { - preferences: { theme: 'light' } -}); - -// Delete a document -await collection.deleteDocument(DOCUMENT_ID); - -// Query documents -const results = await collection.queryDocuments({ - filter: { 'preferences.theme': 'dark' } -}); -``` - ---- - -## Cache - -In-memory cache for ephemeral, real-time data. Organized in segments. - -```javascript -const cache = catalystApp.cache(); -const segment = cache.segment(SEGMENT_ID); -// Segment ID from Console → Cloud Scale → Cache → segment list - -// Put a value (TTL in seconds, max 172800 = 48 hours) -await segment.put('user:123', JSON.stringify({ name: 'Alice' }), 3600); - -// Get a value -const value = await segment.getValue('user:123'); - -// Delete a value -await segment.delete('user:123'); - -// Update a value -await segment.update('user:123', JSON.stringify({ name: 'Alice Updated' })); -``` - -Important: Cache values are strings only. Serialize/deserialize JSON yourself. Max value size: 5MB. -Max TTL: 48 hours (172800 seconds). Data is ephemeral and not persisted. - ---- - -## Search - -Full-text search across Data Store tables. - -```javascript -const search = catalystApp.search(); - -// Search across all searchable columns -const results = await search.searchQuery('engineering manager', { - search_table_columns: { - Employees: ['Name', 'Title', 'Department'] - } -}); -``` - -Search must be enabled per-column in the console. Only Text and Text Area columns can be searched. - ---- - -## Authentication & User Management - -```javascript -const userMgmt = catalystApp.userManagement(); - -// Get current user -const currentUser = await userMgmt.getCurrentUser(); - -// Get all users -const users = await userMgmt.getAllUsers(); - -// Get a specific user -const user = await userMgmt.getUserDetails(USER_ID); - -// Delete a user -await userMgmt.deleteUser(USER_ID); - -// Register a new user (sends invite email) -const signupConfig = { platform_type: 'web', zaid: 'YOUR_ZAID' }; // ZAID from Settings → Environments -const userConfig = { - email_id: 'newuser@example.com', - first_name: 'New', - last_name: 'User' -}; -const newUser = await userMgmt.registerUser(signupConfig, userConfig); -``` - -Auth types supported: Catalyst built-in auth, Zoho accounts, custom SSO. - -> **Embedded sign-in widget has no built-in signup flow.** `catalyst.auth.signIn("divId", config)` -> renders a Zoho IAM login iframe — there is no sign-up button or registration form inside it. -> For apps that require user registration, build a custom sign-up form and call -> `catalyst.auth.signUp()` from the Web SDK. Toggle between the login iframe and the custom form -> in your UI: -> -> ```javascript -> // Custom sign-up form handler -> await catalyst.auth.signUp({ -> first_name: firstName, -> last_name: lastName, -> email_id: email, -> platform_type: 'web', -> redirect_url: window.location.origin + '/app/index.html' -> }); -> // The user receives a verification email from Zoho to activate their account. -> ``` - -### Authentication patterns - -| Pattern | How to initialize | Use case | -|---------|------------------|----------| -| User-scoped (in Functions) | `catalyst.initialize(req)` | Operations on behalf of the logged-in user | -| Admin-scoped (in Functions) | `catalyst.initialize(req, { scope: 'admin' })` | System-level operations, background tasks | -| Admin-scoped (in AppSail) | Same as above | Server-to-server calls, scheduled tasks | - -**For long-running AppSail services:** Use admin credentials for server-to-server calls. -Admin tokens are generated per-request and don't expire mid-operation. - -> **DataStore permissions and App Users:** By default, the App User role has **Read-only** access to DataStore tables. Insert, Update, and Delete operations will return `"No privileges to perform this action"` even when the user is authenticated. Fix this in one of two ways: -> 1. **Console (recommended for most apps):** Data Store → select table → Permissions → enable Read, Insert, Update, Delete for the "App User" role. Repeat for each table. -> 2. **Admin-scoped SDK in backend:** Use `catalyst.initialize(req, { scope: 'admin' })` for DataStore operations. Still use user-scoped SDK (`catalyst.initialize(req)`) to call `getCurrentUser()` for authentication verification. -> -> ```javascript -> // Verify auth with user-scoped SDK -> const catalystApp = catalyst.initialize(req); -> const currentUser = await catalystApp.userManagement().getCurrentUser(); -> -> // Perform DataStore ops with admin-scoped SDK -> const adminApp = catalyst.initialize(req, { scope: 'admin' }); -> const dataStore = adminApp.datastore(); -> const zcql = adminApp.zcql(); -> ``` - -**Cross-project API calls:** Use Connections to authenticate between Catalyst projects -or between Catalyst and external Zoho services. Connections handle token refresh automatically. - ---- - -## API Gateway - -Create APIs that route to functions. Configured in the console or via CLI. - -Features: -- Path-based routing to functions -- Rate limiting and throttling -- Authentication enforcement -- CORS configuration -- Request/response transformation -- API versioning - -Enable via CLI: -```bash -catalyst api-gateway:enable -catalyst api-gateway:status -catalyst api-gateway:disable -``` - -### API Gateway configuration - -API Gateway can be configured via the Catalyst Console or a config file. - -**Console:** Catalyst Console → API Gateway → Routes - -**Example route mapping:** -| Path pattern | Target | Auth | Rate limit | -|-------------|--------|------|------------| -| `/api/users/*` | Advanced I/O: `user_api` | required | 100 req/min | -| `/api/public/*` | Advanced I/O: `public_api` | optional | 50 req/min | -| `/api/admin/*` | Advanced I/O: `admin_api` | required | 20 req/min | - -**CORS configuration (per-route):** -- Allowed Origins: `*` (or specific domains) -- Allowed Methods: `GET, POST, PUT, DELETE, OPTIONS` -- Allowed Headers: `Content-Type, Authorization` -- Max Age: `86400` (seconds) - -API Gateway is the recommended way to expose functions publicly with rate limiting and -auth enforcement, rather than setting individual function security rules to `optional`. - ---- - -## Connections - -Token manager for third-party service integrations. - -```javascript -const connection = catalystApp.connection(); - -// Get a connector by name -const connector = connection.getConnector('ZohoCRM'); - -// Get access token -const token = await connector.getAccessToken(); -``` - -Connections handle OAuth2 token refresh automatically. Set up connection details in the console. - ---- - -## Mail - -Send emails from your application. - -```javascript -const email = catalystApp.email(); - -await email.sendMail({ - from_email: 'noreply@yourdomain.com', - to_email: ['user@example.com'], - subject: 'Welcome!', - content: '

Hello!

Welcome to our app.

', - html_mode: true -}); -``` - -Requires email configuration in the console (domain verification, sender setup). - ---- - -## Push Notifications - -Send push notifications to mobile/web apps. - -```javascript -const pushNotification = catalystApp.pushNotification(); - -// Send to specific users -await pushNotification.sendNotification({ - message: 'New update available!', - recipients: [USER_ID_1, USER_ID_2] -}); -``` - -Requires push notification setup in project settings (APNs for iOS, FCM for Android). - ---- - -## Web Client Hosting - -> **⚠️ Legacy frontend hosting.** For new projects, use **Slate** instead — it offers Git-based workflows, -> framework-native builds, SSR support, and preview deployments. Web Client Hosting is the older approach -> using the `client/` directory. - -Host frontend applications on Catalyst's CDN. - -- Deploy via CLI: `catalyst deploy --only client` -- Supports any frontend framework (React, Vue, Angular, vanilla HTML/CSS/JS) -- Client files go in the `client/` directory -- `index.html` is the entry point -- Supports versioning — previous deployments are retained -- Custom domain mapping available - ---- - -## Domain Mappings - -Map custom domains to your Catalyst app. - -- Configure in the console under Cloud Scale → Domain Mappings -- Free SSL certificates provided automatically -- Supports subdomain mapping -- DNS configuration required (CNAME record) - ---- - -## Deprecated Components - -> **⚠️ The following components are deprecated** (announced August 27, 2025, originally scheduled for removal April 30, 2026 — still functional with deprecation warnings, removal date TBD). -> Never use these for new projects. Users who signed up after August 27, 2025 cannot access them. - -### File Store — DEPRECATED → Use Stratus - -Simple file storage with folder-based organization. 100MB-per-file limit. File Store supports -direct migration to Stratus via the console before removal. - -```javascript -// REMOVED — use catalystApp.stratus() instead -const fileStore = catalystApp.filestore(); -const folder = fileStore.folder(FOLDER_ID); -await folder.uploadFile({ code: fileBuffer, name: 'report.pdf' }); -const fileContent = await folder.downloadFile(FILE_ID); -await folder.deleteFile(FILE_ID); -const files = await folder.getAllFiles(); -``` - -### Event Listeners — DEPRECATED → Use Signals - -React to events by triggering Event Functions. Three categories: Component Events (Data Store/File -Store changes), Custom Events (triggered via SDK/API), Zoho Events. No direct migration to Signals -— business logic must be rebuilt. - -```javascript -// DEPRECATED — use Signals instead -const event = catalystApp.event(); -await event.trigger('my_custom_event', { key: 'value', timestamp: Date.now() }); -``` - -### Cron — DEPRECATED → Use Job Scheduling - -Schedule jobs that invoke Cron Functions. One-time or recurring (cron expressions). No direct -migration to Job Scheduling — schedulers must be reconfigured. diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/deployment-sops.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/deployment-sops.md deleted file mode 100644 index 64b4d1498..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/deployment-sops.md +++ /dev/null @@ -1,271 +0,0 @@ -# Catalyst Deployment SOPs - -> **⚠️ PRE-FLIGHT CHECK:** Before any deployment steps, confirm `.catalystrc` and `catalyst.json` exist. If not, tell the user to run `catalyst init` first. - -## When to use this file -Load this file when the user is deploying a Catalyst project, asking about deployment steps, -running into deployment failures, promoting from Development to Production, setting up -GitHub-based deployment, or needs a pre-deployment checklist. - ---- - -## Pre-deployment checklist - -Before running `catalyst deploy`, verify all of the following: - -- [ ] **`.catalystrc` exists** — project is linked to your Zoho account. If missing, run - `catalyst login` then `catalyst init`. -- [ ] **`catalyst.json` at project root** — auto-generated by `catalyst init`. Never create - manually. If missing, re-initialize. -- [ ] **`functions/` directory structure** — each function has its own subdirectory with the - handler file (`index.js`, `index.py`, or the Java main class). -- [ ] **All function env vars are in `catalyst-config.json`, NOT the Console UI** — - `catalyst deploy` **overwrites the function's environment with values from - `catalyst-config.json` only**. Any variable set through the Console UI is silently - deleted on the next deploy. Store all secrets, API keys, and config in - `catalyst-config.json` (gitignore this file) before deploying. After every deploy, - spot-check Functions → [function] → Settings → Environment Variables to confirm. -- [ ] **AppSail apps have `catalyst-config.json`** with correct `name`, `stack`, `command`, - `memory`, and `port` fields. The `port` must match what the app actually listens on. -- [ ] **Web client in `client/` directory** (only if using Web Client Hosting, not Slate). -- [ ] **Dependencies installed**: - - Node.js: `npm install` inside each function directory - - Python: `pip install -r requirements.txt` inside each function directory - - Java: `mvn install` or `gradle build` -- [ ] **Java functions**: `.class` files are auto-generated by the CLI during `catalyst deploy` — - no manual compilation needed when using CLI. -- [ ] **Slate apps**: `catalyst.json` must be present in the repository root if using GitHub deploy. - ---- - -## Deployment commands - -### Deploy everything (all components) -```bash -catalyst deploy -``` -Deploys all functions, web client, AppSail apps, and Slate apps in the project to the -Development environment. - -### Deploy specific components -```bash -# Functions only -catalyst deploy --only functions - -# AppSail app only -catalyst deploy appsail - -# AppSail standalone (without full project deploy) -catalyst deploy appsail standalone - -# Slate frontend only -catalyst deploy slate - -# Slate with a deployment message -catalyst deploy slate -m "deployment message" - -# Specific Slate app by name -catalyst deploy --only slate:appname - -# Slate to production -catalyst deploy slate --production -``` - -> **Note:** `catalyst deploy` always targets the **Development** environment. Production -> deployment is handled via the Catalyst console (not the CLI). - ---- - -## Post-deployment verification - -After every deployment, verify: - -1. **Check deployment status** in the Catalyst console. -2. **Test function endpoints** — invoke the function URL directly or from your app. -3. **Check DevOps → Logs** for any runtime errors on first execution. -4. **For AppSail**: verify the endpoint URL returned by the CLI is reachable and responds - within the expected latency. -5. **For Slate**: check the Deployment Overview screen to confirm the build succeeded. -6. **For Circuits**: run a test execution and review the Execution History logs. -7. **Configure Application Alerts** for cron jobs and event listeners in production so you - are notified on failures automatically. - ---- - -## GitHub-based deployment - -Requirements for GitHub deployment to work: -- Repository must contain Catalyst project resources in **standard project directory format**. -- `catalyst.json` MUST be present in the repository. -- Default branch must have all files in the correct structure. - -What happens on successful GitHub deployment: -- Functions are updated in the Catalyst console. -- Web Client Hosting is updated. -- A notification is sent (success or failure). -- The Deployed Repository status bar shows the repository name and URL. - -What happens on failure: -- **No changes are reflected** in Functions or Web Client Hosting. -- Fix the directory structure, ensure `catalyst.json` is present, then redeploy. - ---- - -## Common deployment failure recovery - -### Slate deployment failed -1. Use **"Sync Now"** in the Catalyst console to merge the latest Git commit into the current - deployment. This resolves discrepancies causing the failure. -2. If Sync Now doesn't resolve it, use **Rollback** to revert to the last successful deployment - from the console. -3. After fixing the root cause, trigger a new deployment. - -### Function deployment failed — Java missing `.class` file -- Error: function fails to execute after deploy with missing class reference. -- Fix: re-deploy via CLI (`catalyst deploy`). The CLI auto-compiles Java and creates any missing - dependency files. This error only occurs when uploading via console without the compiled files. - -### AppSail not responding after deployment -1. Verify `catalyst-config.json` `port` matches the port the app listens on. -2. Verify the app starts listening within 10 seconds of instance start. - - Cold start: first request to an inactive app spawns a new instance; if no process is - listening on the port within 10 seconds, the instance is killed. -3. Check AppSail instance logs: AppSail → Instances → click the Logs icon → Catalyst Logs. -4. Verify the app uses `process.env.X_ZOHO_CATALYST_LISTEN_PORT || 9000` (Node.js) for the port. - -### Function behaves correctly locally but fails or uses wrong config after deploy -- **Root cause**: environment variables set through the Catalyst Console UI are deleted on - every `catalyst deploy`. The deploy replaces the function environment with values from - `catalyst-config.json` only. -- Fix: move all env vars into `catalyst-config.json` and redeploy. Do not rely on Console-set - vars for any value that must survive deployments. - -### Functions deployed but not behaving as expected -- Check `catalyst.json` for correct function names and configurations. -- Verify the correct function type is used (Basic I/O vs Advanced I/O vs Event, etc.). -- Run `catalyst serve` locally to test Basic I/O and Advanced I/O functions against the - remote Development Data Store before deploying. -- Check DevOps → Logs for execution errors. - ---- - -## Environment promotion: Development → Production - -> Production deployment is not done via CLI. It is managed through the Catalyst console. - -1. **Test thoroughly in Development** — ensure all functions, AppSail, and data operations - work correctly in the Development environment. -2. **Migrate to Production** via the Catalyst console (Deployment and Billing → Environments → - Initial Deployment, or via the production environment settings). -3. **After promotion**, DevOps components (Logs, APM, Application Alerts) continue to work - in the production environment. -4. **Update environment-specific config**: - - ZAID differs between Development and Production — update your app's config accordingly. - - DataStore table permissions and security rules apply independently per environment. -5. **Configure Application Alerts** for production — set up email alerts for function failures, - timeouts, and exceptions from DevOps → Application Alerts. -6. **User limit**: Development supports max 25 users; Production has no user limit. - -### Function promotion to Production (separate step) - -`catalyst deploy --only functions` deploys to the **Development** environment only. -Promoting functions to Production requires a **separate manual step** in the console: - -> Console → Cloud Scale → Serverless → Deploy → Select Functions → Initiate Deployment - -If this step is skipped, the Development function has your latest code but Production still -runs old code. The Diff Generation screen will show `Total Changes: 0` for Functions if -there are no new changes to promote — use this as a diagnostic. - -### Slate production deployment - -Slate can be deployed directly to production from the CLI: -```bash -catalyst deploy slate --production -``` - -### Recommended: deploy components separately - -Deploy functions and Slate separately rather than using `catalyst deploy` (which deploys everything): -```bash -# Deploy function changes -catalyst deploy --only functions - -# Deploy frontend changes -catalyst deploy slate -``` - -This gives clearer error messages, avoids deploying unchanged components, and makes it -easier to diagnose which component caused a failure. - ---- - -## Slate-specific deployment gotchas - -### `slate-config.toml` wiped by clean builds - -The `.catalyst/slate-config.toml` file lives inside the build output directory (e.g., `dist/`). -Any build command that cleans the output (Vite `--clean`, Expo `--clear`, `rm -rf dist/`) deletes -this file. Without it, `catalyst deploy slate` fails. - -**Fix — recreate after every clean build:** -```bash -# Vite/React example -npm run build && mkdir -p dist/.catalyst && \ - echo -e 'framework = "static"\ndeployment_name = "default"' > dist/.catalyst/slate-config.toml - -# Expo web example -npx expo export --platform web --clear && \ - mkdir -p dist/.catalyst && \ - echo -e 'framework = "static"\ndeployment_name = "default"' > dist/.catalyst/slate-config.toml - -# Then deploy -catalyst deploy slate -``` - -### `baseUrl` breaks assets on Slate - -If your build config has a `baseUrl` or `basePath` set to a sub-path (e.g., `/server/my_function` -for serving from inside a function), all JS/CSS URLs will be prefixed with that path on Slate. -Since Slate serves from root `/`, every asset returns 404. - -**Fix:** Remove `baseUrl`/`basePath` from your build config before building for Slate. Only set -it when the frontend is served from inside a function or AppSail sub-path. - ---- - -## `catalyst-config.json` reference (AppSail) - -```json -{ - "name": "my-app", - "stack": "node20", - "command": "node app.js", - "memory": 512, - "port": 9000 -} -``` - -| Field | Required | Notes | -|-------|----------|-------| -| `name` | Yes | App name — must match what's initialized | -| `stack` | Yes | Runtime: `node20`, `java11`, `python39`, or `docker` | -| `command` | Yes | Command to start the app | -| `memory` | No | Default 512 MB; range 256–2048 MB | -| `port` | Yes | Must match the port the app listens on | - ---- - -## CLI deployment flags reference - -| Command | What it does | -|---------|-------------| -| `catalyst deploy` | Deploy all resources to Development | -| `catalyst deploy --only functions` | Deploy functions only | -| `catalyst deploy appsail` | Deploy AppSail app (as part of full project) | -| `catalyst deploy appsail standalone` | Deploy AppSail without full project deploy | -| `catalyst deploy slate` | Deploy Slate frontend | -| `catalyst deploy slate -m "msg"` | Deploy Slate with a message | -| `catalyst deploy slate --production` | Deploy Slate to production | -| `catalyst deploy --only slate:appname` | Deploy a specific Slate app | -| `catalyst serve` | Run Basic I/O + Advanced I/O locally against remote Dev DataStore | diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/devops-deep-dive.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/devops-deep-dive.md deleted file mode 100644 index caca4ee02..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/devops-deep-dive.md +++ /dev/null @@ -1,426 +0,0 @@ -# DevOps Deep-Dive Reference - -## When to use this file -Load this file when the user asks about: Application Alerts configuration, APM traces, Catalyst -logs (access/application), log levels, pushing logs from code, metrics, GitHub integration, -automation testing, test cases, test suites, test plans, test variables, or DevOps limitations. - -External docs: https://docs.catalyst.zoho.com/en/devops/ - ---- - -## Application Alerts - -Automatic email notifications when Catalyst components encounter failures, exceptions, or -timeouts. - -### Supported Components and Event Conditions - -**Cron (Job Scheduling):** -- Job execution failure -- Job timeout -- Code exception - -**Event Listener (Signals targets):** -- Event delivery failure -- Target invocation timeout -- Code exception in event function - -**Logs (Log-based alerts):** -- Trigger alerts based on log patterns or error counts -- Configure criteria to match specific log messages or error levels -- Useful for detecting application-specific failure patterns - -### Alert Criteria -- **Component**: Select which component to monitor -- **Condition**: The event that triggers the alert (failure, exception, timeout, log match) -- **Frequency**: How often alerts are sent — per occurrence, or batched (e.g., hourly digest) -- **Recipients**: Email addresses to notify (max 10 recipients per alert) - -### Limits - -| Environment | Max Alerts | -|-------------|-----------| -| Development | 5 | -| Production | 20 | - -- Max **10 recipients** per alert rule -- Alerts are environment-specific (dev alerts don't fire in prod and vice versa) - ---- - -## Application Performance Monitoring (APM) - -In-depth performance analytics for function executions. - -### Supported Function Types and Languages - -| Function Type | Java | Node.js | Python | -|---------------|------|---------|--------| -| Basic I/O | Yes | Yes | **No** | -| Advanced I/O | Yes | Yes | **No** | -| Event Function | Yes | Yes | **No** | -| Cron Function | Yes | Yes | **No** | -| Browser Logic | Yes | Yes | **No** | -| Job Function | Yes | Yes | **No** | - -**Important: APM is NOT available for Python functions.** Python functions are monitored via -Logs only. APM traces and performance breakdowns are limited to Java and Node.js. - -### Features - -**Invocations:** -- Total invocation count over selected time range -- Success vs. failure breakdown -- Invocations per function - -**Response Times:** -- Average, P50, P95, P99 response times -- Response time trend over time -- Breakdown by function - -**Top 100 Slowest Executions:** -- List of the 100 slowest function executions in the selected time range -- Each entry shows: function name, execution time, timestamp, status -- Click into any execution for full trace details - -**Traces:** -- Detailed execution trace for individual function invocations -- Shows time spent in each phase: initialization, execution, SDK calls, external calls -- Breakdown of internal Catalyst SDK operations (DataStore queries, Cache lookups, etc.) - -### SDK vs API Call Tracking - -APM automatically tracks Catalyst SDK calls made within your function (DataStore, Cache, -FileStore, etc.). However, external API calls (HTTP requests to third-party services) are -**not automatically traced** — they appear as part of the total execution time but are not -broken down individually. To get visibility into external call performance, add manual -logging with timestamps. - -### Data Center Availability -APM is available in all Catalyst data centers. However, trace data retention and granularity -may vary. Check the Catalyst status page for your data center's current APM capabilities. - ---- - -## Logs - -Two categories of logs in Catalyst: - -### Access Logs -Server-level logs for HTTP requests to your Catalyst project. - -**Fields:** -- Request timestamp -- HTTP method and URL path -- Response status code -- Response time (ms) -- Client IP address -- User agent -- Request ID - -### Application Logs -Logs generated by your code via console.log/System.out/print statements. - -**Fields:** -- Timestamp -- Log level (mapped from your code — see mapping below) -- Function name -- Message content -- Request ID (correlate with access logs) -- Execution ID - -### Pushing Logs from Code - -**Java — Basic I/O Function:** -```java -import java.util.logging.Logger; -import java.util.logging.Level; - -public class MyFunction implements ZCFunction { - private static final Logger LOGGER = Logger.getLogger(MyFunction.class.getName()); - - public void runner(CatalystApp catalystApp, Context context, BasicIO basicIO) { - LOGGER.info("Processing request"); - LOGGER.warning("Potential issue detected"); - LOGGER.severe("Critical error occurred"); - - // For structured logging - LOGGER.info("{\"action\":\"createUser\",\"status\":\"success\",\"userId\":\"12345\"}"); - - context.close(); - } -} -``` - -**Java — Advanced I/O / Other Types:** -```java -import java.util.logging.Logger; -import java.util.logging.Level; - -public class MyAdvancedFunction implements ZCFunction { - private static final Logger LOGGER = Logger.getLogger(MyAdvancedFunction.class.getName()); - - public void runner(CatalystApp catalystApp, Context context, HttpRequest request, HttpResponse response) { - LOGGER.info("Handling " + request.getMethod() + " " + request.getRequestURI()); - LOGGER.fine("Debug details: " + request.getParameterMap()); - LOGGER.severe("Error: " + exception.getMessage()); - } -} -``` - -**Node.js — Basic I/O Function:** -```javascript -const catalyst = require('zcatalyst-sdk-node'); -module.exports = async (context, basicIO) => { - const catalystApp = catalyst.initialize(context); - console.log('Processing request'); // INFO - console.warn('Potential issue detected'); // WARNING - console.error('Critical error occurred'); // ERROR - console.debug('Debug details'); // DEBUG (may not appear in prod) - - // Structured logging (recommended) - console.log(JSON.stringify({ - action: 'createUser', - status: 'success', - userId: '12345', - requestId: context.getRequestId() - })); - - context.close(); -}; -``` - -**Node.js — Advanced I/O / Other Types:** -```javascript -module.exports = async (catalystApp, context, req, res) => { - console.log(`Handling ${req.method} ${req.url}`); - console.error(`Error: ${error.message}`); - - // Same console methods work in all function types -}; -``` - -**Python — Basic I/O Function:** -```python -import logging - -logger = logging.getLogger(__name__) - -def handler(catalyst_app, context, basic_io): - logger.info("Processing request") - logger.warning("Potential issue detected") - logger.error("Critical error occurred") - logger.debug("Debug details") - - # Structured logging - import json - logger.info(json.dumps({ - "action": "createUser", - "status": "success", - "userId": "12345" - })) - - context.close() -``` - -**Python — Advanced I/O / Other Types:** -```python -import logging - -logger = logging.getLogger(__name__) - -def handler(catalyst_app, context, request, response): - logger.info(f"Handling {request.method} {request.path}") - logger.error(f"Error: {str(exception)}") -``` - -### Log Levels Mapping - -| Code Statement | Catalyst Log Level | -|---------------|-------------------| -| Java `LOGGER.info()` | INFO | -| Java `LOGGER.warning()` | WARNING | -| Java `LOGGER.severe()` | ERROR | -| Java `LOGGER.fine()` | DEBUG | -| Node.js `console.log()` | INFO | -| Node.js `console.warn()` | WARNING | -| Node.js `console.error()` | ERROR | -| Node.js `console.debug()` | DEBUG | -| Python `logger.info()` | INFO | -| Python `logger.warning()` | WARNING | -| Python `logger.error()` | ERROR | -| Python `logger.debug()` | DEBUG | - -### Filtering Logs -- Filter by: function name, log level, time range, status code, request ID -- Full-text search across log messages -- Auto-filter when navigating from a specific function or Circuit execution - -### Log Retention - -| Environment | Retention Period | -|-------------|-----------------| -| Development | 7 days | -| Production | 14 days | - -### Log Message Limit -- Max **1,500 characters** per log message -- Messages exceeding this limit are truncated - ---- - -## Metrics - -Aggregated performance and usage metrics for key Catalyst components. - -### Monitored Components - -| Component | Metrics Tracked | -|-----------|----------------| -| **DataStore** | Query count, query latency, row operations, storage usage | -| **Cache** | Hit rate, miss rate, operations count, memory usage | -| **Cron** | Execution count, success/failure rate, average duration | -| **Files (FileStore)** | Upload/download count, storage usage, bandwidth | -| **API** | Request count, response times, error rates, status code distribution | - -Metrics are available in the console under DevOps → Metrics, with configurable time ranges -and component filters. - ---- - -## GitHub Integration - -Connect GitHub repositories to your Catalyst project for streamlined deployment workflows. - -### Features -- Link GitHub repositories to Catalyst functions -- Auto-deploy functions on push to configured branches -- Branch-based environment mapping (e.g., `main` → production, `develop` → development) -- Commit-level deployment tracking - -**Note:** GitHub Integration is the predecessor to **Pipelines**, which provides a more -complete CI/CD solution with multi-stage workflows, testing, and artifact management. For -new projects, consider using Pipelines instead. - ---- - -## Automation Testing - -Built-in API testing framework for validating Catalyst function endpoints. - -### Hierarchy - -``` -Modules - └── Test Cases - └── Test Suites - └── Test Plans -``` - -- **Modules**: Logical groupings of test cases (e.g., "User Management", "Orders") -- **Test Cases**: Individual API endpoint tests -- **Test Suites**: Collections of test cases executed together -- **Test Plans**: Scheduled or on-demand execution of test suites - -### Test Cases - -Each test case defines an HTTP request and its expected response. - -**Supported HTTP methods:** GET, POST, PUT, PATCH, DELETE - -**Assertions (4 types):** - -| Assertion Type | Description | Example | -|----------------|-------------|---------| -| **Status Code** | Assert the HTTP response status code | Status code equals 200 | -| **Body** | Assert values in the response body (JSON path) | `$.user.name` equals "John" | -| **Headers** | Assert response header values | `Content-Type` contains "application/json" | -| **Response Time** | Assert the response completes within a time limit | Response time less than 500ms | - -### Test Suites - -Group test cases for batch execution. - -**Execution modes:** -- **Sequential**: Test cases run one after another in defined order. A failure can optionally - stop the remaining tests. -- **Parallel**: Test cases run simultaneously for faster execution. Order is not guaranteed. - -**Performance reporting:** -- Total execution time -- Pass/fail count and percentage -- Individual test case results with response details -- Response time distribution across the suite - -### Test Plans - -Schedule test suite execution. - -**Scheduling options:** - -| Schedule Type | Description | -|---------------|-------------| -| **Nightly** | Runs every night at a configured time | -| **Day** | Runs on specific days of the week at a configured time | -| **Custom** | Custom cron expression for flexible scheduling | -| **Starts Now** | Execute immediately (one-time, on-demand) | - -**Notifications:** -- Email notifications on test plan completion -- Configurable recipients -- Summary includes: pass/fail counts, duration, link to detailed results - -### Variables - -Three scopes for test variables, each with different syntax and visibility. - -| Scope | Syntax | Description | -|-------|--------|-------------| -| **Global** | `{{$var_name}}` | Available across all test cases and suites. Set in project settings. | -| **Environment** | Environment-specific | Different values per environment (dev/prod). Same variable name, different values. | -| **Local / Request** | `{{var_name}}` | Scoped to a single test case or extracted from a previous response in a suite. | - -**Bulk add:** Import multiple variables at once via CSV or JSON in the console. - -Variables are used in request URLs, headers, body, and assertions. For example: -``` -GET {{$base_url}}/api/users/{{user_id}} -Authorization: Bearer {{$auth_token}} -``` - -### Results - -**All Runs View:** -- History of all test plan executions -- Filter by date range, status, test plan name -- Summary stats per run - -**Individual Run Details:** -- Detailed results for each test case in the run -- Request sent, response received, assertion results -- Pass/fail status per assertion -- Response time per test case - -**Re-run Failed:** -- Re-execute only the failed test cases from a previous run -- Useful for validating fixes without re-running the entire suite - ---- - -## Limitations - -### Data Center Availability -- Automation Testing is available in all Catalyst data centers, but some advanced features - may have limited availability. Check documentation for your data center. - -### Environment Restrictions -- Automation Testing is available in the **development environment only** -- Tests cannot be run against production endpoints from the testing framework -- Use test plans with appropriate environment variables for staging validation - -### Supported Endpoints -- Only Catalyst function endpoints (Basic I/O, Advanced I/O, AppSail) are supported as - test targets -- External URLs are not supported as test case targets -- WebSocket endpoints are not supported diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/equivalents-aws.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/equivalents-aws.md deleted file mode 100644 index 851d21d12..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/equivalents-aws.md +++ /dev/null @@ -1,315 +0,0 @@ -# Catalyst ↔ AWS Equivalents - -Maps every Catalyst service to its closest AWS equivalent. Use this to help users who come from AWS -understand Catalyst concepts, or to translate requests phrased as "I need something like Lambda/S3/RDS". - -> **Deprecated services:** Event Listeners → Signals, File Store → Stratus, Cron → Job -> Scheduling (deprecated, removal date TBD). Never recommend deprecated components for new projects. - ---- - -## Compute - -### Functions → AWS Lambda - -Key differences from Lambda: -- Catalyst bundles 7 specialized function types with distinct handler signatures; Lambda uses a single - model with event sources configured externally. -- Catalyst provides an SDK (`zcatalyst-sdk-node`) initialized via `catalyst.initialize(context)` — simpler than `require('aws-sdk')` with manual credential setup. -- Security Rules (`optional`/`required`) are built into function config — no separate - IAM + API Gateway auth layer required as in AWS. -- Cold starts exist on both; Catalyst defaults to 128MB memory (max 1024MB), comparable to Lambda's range. - -**When a user says → they mean:** -- "I need a Lambda function" → Catalyst Advanced I/O Function (most flexible, HTTP-based) -- "I need a Cloud Function triggered by events" → Catalyst Event Function + Signals -- "I need a scheduled Lambda" → Job Scheduling (not deprecated Cron) -- "I need a background worker" → Catalyst Job Function - -### AppSail → AWS App Runner / Elastic Beanstalk - -Key differences from App Runner: -- AppSail supports both managed runtimes (Node.js, Java, Python) AND custom Docker containers. -- Auto-scaling is 1–5 instances (more limited than App Runner's scale). -- SDK initialization is manual in AppSail: `catalyst.initialize(req)` — same mental model as - initializing the AWS SDK in an ECS/App Runner container. -- Use `process.env.X_ZOHO_CATALYST_LISTEN_PORT` instead of a fixed port. - -**When a user says → they mean:** -- "I want to deploy an Express app like on Elastic Beanstalk / App Runner" → AppSail with managed Node.js runtime -- "I need a persistent server, not serverless" → AppSail - ---- - -## Data & Storage - -### Data Store → Amazon RDS - -Key differences: -- Uses ZCQL (not standard SQL) — case-sensitive table/column names, no cross-type JOINs, - max 300 rows per query. -- No direct SQL access or connection strings — all access via SDK or ZCQL. -- System columns (ROWID, CREATORID, CREATEDTIME, MODIFIEDTIME) are auto-managed. - -**When a user says → they mean:** -- "I need RDS / a relational database" → Data Store (note ZCQL differences) -- "I need a relational DB with an admin UI" → Data Store (web console) - -### Stratus → Amazon S3 - -Key differences: -- S3-compatible patterns (buckets, keys, prefixes) — S3 users will feel at home. -- PII/ePHI compliance, versioning, malware scanning built in. -- No per-file size constraints at the default tier. - -**When a user says → they mean:** -- "I need S3" → Stratus -- "I need object/blob storage" → Stratus -- "I need to store user uploads, media, or backups" → Stratus - -### File Store → (A simplified S3 with folder semantics) — DEPRECATED (removal date TBD) - -Migrate to Stratus. Only reference for legacy projects. - -### NoSQL → Amazon DynamoDB - -Key differences: -- Collection-based document store — closer to Firestore or MongoDB than DynamoDB. -- Supports nested objects, arrays, query-by-field. -- No secondary indexes or aggregation pipelines. - -**When a user says → they mean:** -- "I need DynamoDB" → NoSQL (document model) or Data Store (relational model, depends on use case) - -### Cache → Amazon ElastiCache (Redis/Memcached) - -Key differences: -- Segment-based organization (not key namespaces like Redis). -- String values only — serialize/deserialize JSON yourself. -- Max TTL of 48 hours (ElastiCache/Redis has no default TTL limit). -- Max value size 5MB (vs Redis's 512MB). - -**When a user says → they mean:** -- "I need ElastiCache / Redis" → Cache (for basic caching needs) -- "I need session storage" → Cache (note 48hr TTL limit) - -### Search → Amazon OpenSearch / CloudSearch - -Key differences: -- Searches Data Store tables only — not a general-purpose search engine like OpenSearch. -- Must enable per-column in console; simpler than managing OpenSearch clusters. - -**When a user says → they mean:** -- "I need OpenSearch / CloudSearch" → Search (for Data Store data); suggest external service for complex needs - ---- - -## Frontend & Hosting - -### Slate → AWS Amplify Hosting - -Key differences: -- Git-based deployments (GitHub, GitLab, Bitbucket), preview deployments, SSR (Next.js) — comparable to Amplify Hosting. -- Integrated with all Catalyst backend services — no need to wire up separate services as with Amplify. - -**When a user says → they mean:** -- "I need Amplify Hosting" → Slate - -### Web Client Hosting → AWS S3 Static Website Hosting — LEGACY - -Use Slate for all new projects. - -### Domain Mappings → Route 53 + CloudFront custom domains - -Free SSL certificates auto-provisioned (like ACM + CloudFront on AWS). - ---- - -## Workflow & Orchestration - -### Circuits → AWS Step Functions - -Key differences: -- Visual drag-and-drop builder in the console — like Step Functions Visual Workflow Studio. -- State types (Function, Condition, Wait, Parallel, End) map directly to Step Functions states - (Task, Choice, Wait, Parallel, End). -- Invokable via SDK: `catalystApp.circuit().execute()` — same mental model as the Step Functions SDK. - -**When a user says → they mean:** -- "I need Step Functions" → Circuits -- "I need workflow orchestration / a saga pattern / an approval workflow" → Circuits -- "I need to chain functions together" → Circuits - -### Job Scheduling → AWS SQS + Lambda / AWS Batch - -Key differences: -- Pool-based job management — similar to Batch job queues. -- Triggers Job Functions, Circuits, Webhooks, or AppSail services. -- Replaces deprecated Cron for scheduled tasks. - -**When a user says → they mean:** -- "I need SQS + Lambda for background jobs" → Job Scheduling + Job Functions -- "I need scheduled / cron jobs" → Job Scheduling - ---- - -## AI & Machine Learning - -### Zia Services → Amazon Rekognition / Textract / Comprehend - -| Zia Service | AWS Equivalent | -|---|---| -| OCR | Amazon Textract | -| Barcode Scanner | AWS (custom Lambda) | -| Face Detection | Amazon Rekognition | -| Image Moderation | Rekognition Content Moderation | -| Object Detection | Rekognition Labels | -| Text Analytics | Amazon Comprehend | -| AutoML | SageMaker Autopilot | - -**When a user says → they mean:** -- "I need Rekognition" → Zia Services (Face Detection, Image Moderation, Object Detection) -- "I need Textract / OCR" → Zia Services OCR -- "I need Comprehend / NLP" → Zia Services Text Analytics - -### QuickML → Amazon SageMaker Canvas - -Key differences: -- Fully no-code, visual pipeline builder — closest to SageMaker Canvas. -- Supports LLM serving (Qwen 2.5 models) with chat interface and OAuth-based integration. -- Data connectors, preprocessing, and model deployment as API endpoints — all via console. - -**When a user says → they mean:** -- "I need SageMaker / AutoML without code" → QuickML -- "I need to deploy an ML model as an API" → QuickML - -### ConvoKraft → Amazon Lex - -**When a user says → they mean:** -- "I need Lex / a chatbot" → ConvoKraft - ---- - -## Integration & Events - -### Signals → Amazon EventBridge - -Key differences: -- Native Zoho product publishers (CRM, Books, Desk, People, Analytics) — no AWS equivalent for Zoho-specific events. -- Replaces deprecated Event Listeners for event-driven architectures. - -**When a user says → they mean:** -- "I need EventBridge" → Signals -- "I need event-driven architecture" → Signals -- "I need to react to CRM / Zoho changes" → Signals - -### Connections → AWS Secrets Manager - -Key differences: -- Not just a secret store — actively manages OAuth2 token lifecycle (refresh, rotation). -- Pre-built Zoho connectors; `connector.getAccessToken()` is simpler than manual OAuth flows. - -**When a user says → they mean:** -- "I need Secrets Manager for API tokens" → Connections -- "I need to connect to third-party APIs with OAuth" → Connections - -### Event Listeners — DEPRECATED (removal date TBD) -Replaced by **Signals**. - -### Cron — DEPRECATED (removal date TBD) -Replaced by **Job Scheduling**. - ---- - -## DevOps & CI/CD - -### Pipelines → AWS CodePipeline + CodeBuild - -Key differences: -- YAML-based pipeline config — similar to CodeBuild buildspec. -- Integrated with GitHub, GitLab, Bitbucket. - -**When a user says → they mean:** -- "I need CodePipeline / CodeBuild / CI/CD" → Pipelines - -### SmartBrowz → AWS Lambda + Puppeteer Layer - -Key differences: -- Fully managed headless browser — no need to package Puppeteer/Chromium in a Lambda layer. -- Supports scraping, screenshots, PDF generation, browser automation. -- Uses Browser Logic Functions with Puppeteer-like API. - -**When a user says → they mean:** -- "I need Lambda Puppeteer / headless browser in the cloud" → SmartBrowz -- "I need web scraping / PDF from HTML" → SmartBrowz - -### DevOps/Logs → Amazon CloudWatch - -Includes: Logs, APM (execution time, error rates, cold starts), and Application Alerts. - ---- - -## Security & Identity - -### Auth & User Management → Amazon Cognito - -Key differences: -- Security Rules (`optional`/`required`) are simpler than Cognito + API Gateway authorizers. -- Web SDK auth flow (`catalyst.auth.login()`) — like the Cognito Hosted UI or Amplify Auth. -- Supports Zoho Accounts as an identity provider. - -**When a user says → they mean:** -- "I need Cognito" → Auth & User Management -- "I need user sign-up / login" → Auth & User Management - -### API Gateway → Amazon API Gateway - -Key differences: -- Simpler — no stages, usage plans, or Lambda authorizers needed. -- Path-based routing, rate limiting, CORS, auth enforcement via console or CLI. - ---- - -## Communication - -### Mail → Amazon SES - -Domain verification required before sending. - -### Push Notifications → Amazon SNS (mobile push) - -APNs (iOS) + FCM (Android) setup required. - ---- - -## Quick Lookup: Catalyst ↔ AWS - -| Catalyst Service | AWS Equivalent | -|---|---| -| Functions | Lambda | -| AppSail | App Runner / Elastic Beanstalk | -| Data Store | RDS | -| Stratus | S3 | -| NoSQL | DynamoDB | -| Cache | ElastiCache | -| Search | OpenSearch / CloudSearch | -| Slate | Amplify Hosting | -| Circuits | Step Functions | -| Job Scheduling | SQS + Lambda / Batch | -| Signals | EventBridge | -| Connections | Secrets Manager | -| Pipelines | CodePipeline + CodeBuild | -| SmartBrowz | Lambda + Puppeteer layer | -| ConvoKraft | Lex | -| QuickML | SageMaker Canvas | -| Zia OCR | Textract | -| Zia Text Analytics | Comprehend | -| Zia Image/Vision | Rekognition | -| Auth & Users | Cognito | -| API Gateway | API Gateway | -| Mail | SES | -| Push Notifications | SNS | -| DevOps / Logs | CloudWatch | -| ~~File Store~~ | ~~S3 (simplified)~~ → **REMOVED, use Stratus** | -| ~~Event Listeners~~ | ~~EventBridge rules~~ → **REMOVED, use Signals** | -| ~~Cron~~ | ~~EventBridge Scheduler~~ → **REMOVED, use Job Scheduling** | diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/equivalents-azure.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/equivalents-azure.md deleted file mode 100644 index 30b53c97f..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/equivalents-azure.md +++ /dev/null @@ -1,296 +0,0 @@ -# Catalyst ↔ Azure Equivalents - -Maps every Catalyst service to its closest Azure equivalent. Use this to help users who come from Azure -understand Catalyst concepts, or to translate requests phrased as "I need something like Azure Functions/Blob Storage". - -> **Deprecated services:** Event Listeners → Signals, File Store → Stratus, Cron → Job -> Scheduling (deprecated, removal date TBD). Never recommend deprecated components for new projects. - ---- - -## Compute - -### Functions → Azure Functions - -Key differences: -- Catalyst bundles 7 specialized function types with distinct handler signatures; Azure Functions use - a single model with various trigger bindings configured externally. -- Catalyst provides an SDK (`zcatalyst-sdk-node`) initialized via `catalyst.initialize(context)` — simpler than Azure SDK client setup. -- Security Rules (`optional`/`required`) are built in — no separate Azure AD / API Management - auth layer needed. - -**When a user says → they mean:** -- "I need an Azure Function" → Catalyst Advanced I/O Function (HTTP trigger) -- "I need an event-triggered Azure Function" → Catalyst Event Function + Signals -- "I need a timer-triggered Azure Function" → Job Scheduling -- "I need a background worker" → Catalyst Job Function - -### AppSail → Azure App Service - -Key differences: -- AppSail supports both managed runtimes AND custom Docker containers — similar to App Service's - code-based and container-based deployment modes. -- Auto-scaling is 1–5 instances (more limited than App Service's scale-out range). -- SDK initialization is manual in AppSail: `catalyst.initialize(req)`. -- Use `process.env.X_ZOHO_CATALYST_LISTEN_PORT` instead of a fixed port. - -**When a user says → they mean:** -- "I need Azure App Service / a web app host" → AppSail -- "I need a persistent server, not serverless" → AppSail - ---- - -## Data & Storage - -### Data Store → Azure SQL Database - -Key differences: -- Uses ZCQL (not standard SQL/T-SQL) — case-sensitive table/column names, no cross-type JOINs, - max 300 rows per query. -- No direct SQL access or connection strings — all access via SDK or ZCQL. -- System columns (ROWID, CREATORID, CREATEDTIME, MODIFIEDTIME) are auto-managed. - -**When a user says → they mean:** -- "I need Azure SQL / a managed relational database" → Data Store (note ZCQL differences) - -### Stratus → Azure Blob Storage - -Key differences: -- S3-compatible patterns (buckets, keys, prefixes) — similar to Blob Storage containers and blobs. -- PII/ePHI compliance, versioning, malware scanning built in. - -**When a user says → they mean:** -- "I need Azure Blob Storage / object storage" → Stratus -- "I need to store files, media, or backups" → Stratus - -### File Store — DEPRECATED (removal date TBD) - -Migrate to Stratus. - -### NoSQL → Azure Cosmos DB - -Key differences: -- Collection-based document store — closest mental model is Cosmos DB (NoSQL API) or MongoDB. -- Supports nested objects, arrays, query-by-field. -- No secondary indexes or aggregation pipelines (simpler than Cosmos DB). - -**When a user says → they mean:** -- "I need Cosmos DB / a document database" → NoSQL - -### Cache → Azure Cache for Redis - -Key differences: -- Segment-based organization (not Redis key namespaces). -- String values only — serialize/deserialize JSON yourself. -- Max TTL 48 hours; max value 5MB. - -**When a user says → they mean:** -- "I need Azure Cache for Redis / a caching layer" → Cache - -### Search → (Azure Cognitive Search is broader; Catalyst Search is lighter, Data Store only) - -Catalyst Search covers Data Store columns only. For advanced full-text search across multiple sources, -consider Azure Cognitive Search or Elasticsearch. - ---- - -## Frontend & Hosting - -### Slate → Azure Static Web Apps - -Key differences: -- Git-based deployments, preview deployments, SSR (Next.js) — comparable to Azure Static Web Apps. -- Integrated with all Catalyst backend services. -- Multiple frontend apps per project. - -**When a user says → they mean:** -- "I need Azure Static Web Apps / frontend hosting" → Slate - -### Web Client Hosting — LEGACY - -Use Slate for all new projects. - -### Domain Mappings → Azure custom domains on App Service / Front Door - -Free SSL certificates auto-provisioned. - ---- - -## Workflow & Orchestration - -### Circuits → Azure Logic Apps / Durable Functions - -Key differences: -- Visual drag-and-drop workflow builder — closest to Azure Logic Apps' designer. -- State types (Function, Condition, Wait, Parallel, End) map closely to Logic Apps' actions/conditions. -- Invokable via SDK: `catalystApp.circuit().execute()`. - -**When a user says → they mean:** -- "I need Logic Apps / Durable Functions / workflow orchestration" → Circuits -- "I need an approval workflow / saga pattern" → Circuits -- "I need to chain functions together" → Circuits - -### Job Scheduling → Azure Queue Storage + Functions / Azure Service Bus - -Key differences: -- Pool-based job management — similar to Service Bus queues with Function triggers. -- Triggers Job Functions, Circuits, Webhooks, or AppSail services. - -**When a user says → they mean:** -- "I need Azure Queue Storage + Functions / background processing" → Job Scheduling + Job Functions -- "I need scheduled / timer-based jobs" → Job Scheduling - ---- - -## AI & Machine Learning - -### Zia Services → Azure Cognitive Services - -| Zia Service | Azure Equivalent | -|---|---| -| OCR | Azure Computer Vision OCR | -| Face Detection | Azure Face API | -| Image Moderation | Azure Content Moderator | -| Object Detection | Azure Computer Vision | -| Text Analytics | Azure Text Analytics | -| AutoML | Azure Automated ML | - -**When a user says → they mean:** -- "I need Azure Cognitive Services / Vision" → Zia Services (OCR, Face Detection, Object Detection) -- "I need Azure Text Analytics / NLP" → Zia Services Text Analytics -- "I need Azure Automated ML" → QuickML - -### QuickML → Azure Machine Learning Designer - -Key differences: -- Fully no-code, visual pipeline builder — closest to Azure ML Designer. -- Supports LLM serving (Qwen 2.5 models) with OAuth-based integration. - -**When a user says → they mean:** -- "I need Azure ML Designer / no-code ML" → QuickML -- "I need to deploy an ML model as an API" → QuickML - -### ConvoKraft → Azure Bot Service - -**When a user says → they mean:** -- "I need Azure Bot Service / a chatbot" → ConvoKraft -- "I need an AI assistant on my website" → ConvoKraft - ---- - -## Integration & Events - -### Signals → Azure Event Grid - -Key differences: -- Native Zoho product publishers (CRM, Books, Desk, People, Analytics) — no Azure equivalent for Zoho events. -- Supports publishers, subscribers, event routing, and schema validation. -- Replaces deprecated Event Listeners. - -**When a user says → they mean:** -- "I need Azure Event Grid / event-driven architecture" → Signals -- "I need to react to Zoho/CRM changes" → Signals - -### Connections → Azure Key Vault - -Key differences: -- Not just a secret store — actively manages OAuth2 token lifecycle (refresh, rotation). -- Pre-built Zoho connectors; `connector.getAccessToken()` is simpler than manual OAuth flows. - -**When a user says → they mean:** -- "I need Key Vault for API tokens" → Connections -- "I need to connect to third-party APIs with OAuth" → Connections - -### Event Listeners — DEPRECATED (removal date TBD) -Replaced by **Signals**. - -### Cron — DEPRECATED (removal date TBD) -Replaced by **Job Scheduling**. - ---- - -## DevOps & CI/CD - -### Pipelines → Azure Pipelines - -Key differences: -- YAML-based pipeline config — similar to Azure Pipelines YAML syntax. -- Integrated with GitHub, GitLab, Bitbucket. - -**When a user says → they mean:** -- "I need Azure Pipelines / CI/CD" → Catalyst Pipelines - -### SmartBrowz → (No direct Azure equivalent; use Azure Container Instances + Puppeteer) - -Catalyst SmartBrowz is fully managed — no need to provision containers for headless Chrome. - -**When a user says → they mean:** -- "I need headless browser automation" → SmartBrowz -- "I need web scraping / PDF generation" → SmartBrowz - -### DevOps / Logs → Azure Monitor - -Includes: Logs, APM (execution time, error rates, cold starts), and Application Alerts. - ---- - -## Security & Identity - -### Auth & User Management → Azure AD B2C - -Key differences: -- Security Rules (`optional`/`required`) on functions — simpler than AD B2C + APIM policies. -- Supports Zoho Accounts as an identity provider. -- Web SDK auth flow (`catalyst.auth.login()`) — like MSAL.js for B2C. - -**When a user says → they mean:** -- "I need Azure AD B2C / user auth" → Auth & User Management -- "I need user sign-up / login / SSO" → Auth & User Management - -### API Gateway → Azure API Management - -Key differences: -- Simpler — no policies, products, or subscriptions needed. -- Path-based routing, rate limiting, CORS, auth enforcement. - ---- - -## Communication - -### Mail → (No direct Azure equivalent; use Azure Communication Services or SendGrid) - -### Push Notifications → Azure Notification Hubs - -APNs (iOS) + FCM (Android) setup required. - ---- - -## Quick Lookup: Catalyst ↔ Azure - -| Catalyst Service | Azure Equivalent | -|---|---| -| Functions | Azure Functions | -| AppSail | App Service | -| Data Store | SQL Database | -| Stratus | Blob Storage | -| NoSQL | Cosmos DB | -| Cache | Cache for Redis | -| Search | — (Cognitive Search for advanced needs) | -| Slate | Static Web Apps | -| Circuits | Logic Apps / Durable Functions | -| Job Scheduling | Queue Storage + Functions | -| Signals | Event Grid | -| Connections | Key Vault | -| Pipelines | Azure Pipelines | -| ConvoKraft | Bot Service | -| QuickML | ML Designer / Automated ML | -| Zia OCR | Computer Vision OCR | -| Zia Text Analytics | Text Analytics | -| Zia Image/Vision | Computer Vision / Face API | -| Auth & Users | AD B2C | -| API Gateway | API Management | -| Push Notifications | Notification Hubs | -| DevOps / Logs | Azure Monitor | -| ~~File Store~~ | ~~Blob Storage (simplified)~~ → **REMOVED, use Stratus** | -| ~~Event Listeners~~ | ~~Event Grid subscriptions~~ → **REMOVED, use Signals** | -| ~~Cron~~ | ~~Timer trigger~~ → **REMOVED, use Job Scheduling** | diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/equivalents-firebase.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/equivalents-firebase.md deleted file mode 100644 index 9698951c0..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/equivalents-firebase.md +++ /dev/null @@ -1,170 +0,0 @@ -# Catalyst ↔ Firebase Equivalents - -Firebase is one of the closest conceptual analogues to Catalyst — both are integrated full-stack platforms -combining auth, database, storage, functions, and hosting. Use this file when users come from Firebase -or ask "is Catalyst like Firebase?" / "how does Firestore map to Catalyst?". - -> **Deprecated services:** Event Listeners → Signals, File Store → Stratus, Cron → Job -> Scheduling (deprecated, removal date TBD). Never recommend deprecated components for new projects. - ---- - -## Service-by-Service Mapping - -### Firebase Authentication → Catalyst Auth & User Management - -Key similarities: -- Both provide client-side auth SDKs (`catalyst.auth.login()` ≈ `firebase.auth().signInWith...`). -- Both support custom SSO and third-party identity providers. - -Key differences: -- Catalyst's Security Rules (`optional`/`required`) live on each function — simpler than - Firebase's per-collection Firestore security rules. -- Catalyst supports Zoho Accounts as a native identity provider. -- No Firebase App Check equivalent; Catalyst uses function-level access control instead. - -**When a user says → they mean:** -- "I need Firebase Authentication" → Auth & User Management -- "I need user sign-up / login" → Auth & User Management - ---- - -### Firebase Firestore → Catalyst NoSQL - -**For Firestore users:** Use Catalyst **NoSQL** — collection-based document store with nested objects -and query-by-field. Closest mental model. - -Key differences from Firestore: -- No real-time listeners (`onSnapshot` has no equivalent in Catalyst). -- No secondary indexes or aggregation pipelines. -- No subcollections — use flat document structure or top-level collections. -- Query model is simpler (field equality / range — no compound index requirements). - -**When a user says → they mean:** -- "I need Firestore" → NoSQL - ---- - -### Firebase Realtime Database → Catalyst NoSQL or Data Store - -- Use **NoSQL** for flexible/document schema (closer mental model to RTDB's JSON tree). -- Use **Data Store** for structured/relational data with ZCQL queries. -- Catalyst has no real-time push equivalent — use Signals for event-driven updates between services, - or client-side polling for UI refresh. - -**When a user says → they mean:** -- "I need Realtime Database" → NoSQL (flexible schema) or Data Store (structured) - ---- - -### Firebase Storage → Catalyst Stratus - -Key differences: -- Stratus is S3-compatible (bucket/object model) — more flexible than Firebase Storage's folder/path model. -- Stratus supports versioning, multipart upload, malware scanning, and PII/ePHI compliance. -- No Firebase Storage Security Rules equivalent — access is controlled via function-level Security Rules - or pre-signed URL patterns in the SDK. - -**When a user says → they mean:** -- "I need Firebase Storage" → Stratus - ---- - -### Cloud Functions for Firebase → Catalyst Functions - -Key differences: -- Firebase Functions support TypeScript/JS only; Catalyst supports Node.js, Java, and Python. -- Catalyst has 7 specialized function types with distinct handler signatures; Firebase has one model - with various trigger types (HTTP, Firestore, Auth, Pub/Sub, etc.). -- Catalyst SDK (`zcatalyst-sdk-node`) is initialized via `catalyst.initialize(context)`; Firebase uses `admin.initializeApp()`. -- No direct equivalent to Firebase's `onDocumentCreated`/`onDocumentUpdated` triggers — use - Signals (event bus) or Job Scheduling to replicate that pattern. - -**When a user says → they mean:** -- "I need Cloud Functions for Firebase (HTTP)" → Advanced I/O Function -- "I need a Firestore-triggered function" → Event Function + Signals (Signals publishes the change event) -- "I need an Auth-triggered function" → Event Function + Signals - ---- - -### Firebase Hosting → Catalyst Slate - -Key differences: -- Slate supports SSR (Next.js) natively — Firebase Hosting requires Cloud Run for SSR. -- Slate supports multiple frontend apps per Catalyst project; Firebase Hosting is typically one - deployment target per project (preview channels aside). -- Both support custom domains with auto-SSL. -- Both support git-based deployments (Slate via Pipelines or console; Firebase via Firebase CLI). - -**When a user says → they mean:** -- "I need Firebase Hosting" → Slate -- "I need frontend hosting with SSR" → Slate -- "I need preview deployments" → Slate - ---- - -### Firebase Cloud Messaging (FCM) → Catalyst Push Notifications - -Catalyst Push Notifications integrates FCM (Android) and APNs (iOS) as the underlying delivery layer. -The Catalyst SDK wraps this — FCM setup (Server Key) is still required. - ---- - -### Firebase ML → Zia Services + QuickML - -| Firebase ML Feature | Catalyst Equivalent | -|---|---| -| Vision APIs (labels, text, faces) | Zia Services (OCR, Face Detection, Object Detection) | -| Natural Language | Zia Services Text Analytics | -| Custom Model Hosting | QuickML (no-code model training + API deployment) | -| AutoML (Vision/NL) | QuickML | - ---- - -### Firebase Extensions → Catalyst CodeLib - -Both provide pre-built, deployable functionality. CodeLib is Catalyst's equivalent of Firebase Extensions -— reusable modules deployable into a project. - ---- - -## Real-time: What Firebase Has That Catalyst Does Not - -Firebase's key capability not replicated in Catalyst: -- **Real-time listeners** (`onSnapshot`, RTDB `on('value')`) — Catalyst has no push-based real-time - data sync to clients. - -Workarounds: -- Use **Signals** to propagate events between backend services. -- Use **client-side polling** (setInterval + REST/SDK call) for UI refresh. -- For chat/collaboration use cases, consider a dedicated WebSocket layer via AppSail. - ---- - -## Holistic Comparison: Firebase vs Catalyst - -| Dimension | Firebase | Catalyst | -|---|---|---| -| Database | Firestore (document) + RTDB | Data Store (relational, ZCQL) + NoSQL (document) | -| Storage | Firebase Storage | Stratus (S3-compatible) | -| Auth | Firebase Auth | Auth & User Management | -| Functions | Cloud Functions for Firebase (JS/TS) | 7 function types (Node.js / Java / Python) | -| Hosting | Firebase Hosting (static + SPA; SSR via Cloud Run) | Slate (static + SSR natively) | -| Events | Firestore/Auth/Storage triggers | Signals (event bus with Zoho integration) | -| Real-time | Yes (`onSnapshot`, RTDB) | No native real-time | -| Zoho integration | None | Native (CRM, Books, Desk, People, Analytics, etc.) | -| Workflow orchestration | — | Circuits (visual, Step Functions-like) | -| Background jobs | — | Job Scheduling | -| AI / ML | Firebase ML (vision, NLP) | Zia Services + QuickML + ConvoKraft | -| Pricing | Pay-as-you-go (Blaze) / Spark (free) | Pay-as-you-go / Subscription + $250 trial credits | - -**Catalyst's key advantages over Firebase:** -- Native Zoho product integration eliminates glue code for Zoho-ecosystem businesses. -- Relational database with ZCQL alongside a document NoSQL store. -- Richer compute model (7 function types, AppSail, Circuits, Job Scheduling). -- Richer AI/ML surface (Zia Services, QuickML, ConvoKraft, SmartBrowz). - -**Firebase's key advantages over Catalyst:** -- Real-time data sync (`onSnapshot`) with no polling required. -- Larger community, ecosystem, and third-party library support. -- Deep Google Cloud integration (BigQuery export, Cloud Run triggers, etc.). diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/equivalents-gcp.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/equivalents-gcp.md deleted file mode 100644 index 94c252fb6..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/equivalents-gcp.md +++ /dev/null @@ -1,292 +0,0 @@ -# Catalyst ↔ GCP Equivalents - -Maps every Catalyst service to its closest GCP equivalent. Use this to help users who come from GCP -understand Catalyst concepts, or to translate requests phrased as "I need something like Cloud Run/Pub-Sub". - -> **Deprecated services:** Event Listeners → Signals, File Store → Stratus, Cron → Job -> Scheduling (deprecated, removal date TBD). Never recommend deprecated components for new projects. - ---- - -## Compute - -### Functions → Google Cloud Functions - -Key differences: -- Catalyst bundles 7 specialized function types with distinct handler signatures; GCP uses a single - function model with event sources configured externally. -- Catalyst provides an SDK (`zcatalyst-sdk-node`) initialized via `catalyst.initialize(context)` — simpler than GCP client library setup. -- Security Rules (`optional`/`required`) are built in — no separate Cloud Endpoints/API Gateway - auth layer needed. -- Cold starts exist on both platforms. - -**When a user says → they mean:** -- "I need a Cloud Function" → Catalyst Advanced I/O Function (HTTP-based) -- "I need a Cloud Function triggered by events" → Catalyst Event Function + Signals -- "I need a scheduled Cloud Function" → Job Scheduling -- "I need a background worker" → Catalyst Job Function - -### AppSail → Google Cloud Run - -Key differences: -- AppSail supports both managed runtimes (Node.js, Java, Python) AND custom Docker containers, - mirroring Cloud Run's model. -- Auto-scaling is 1–5 instances (vs Cloud Run's 0–1000 range). -- SDK initialization is manual in AppSail: `catalyst.initialize(req)`. -- Use `process.env.X_ZOHO_CATALYST_LISTEN_PORT` instead of `PORT` (Cloud Run convention). - -**When a user says → they mean:** -- "I need Cloud Run-style containers" → AppSail with Custom Runtime (Docker) -- "I need a persistent server, not serverless" → AppSail - ---- - -## Data & Storage - -### Data Store → Google Cloud SQL - -Key differences: -- Uses ZCQL (not standard SQL) — case-sensitive table/column names, no cross-type JOINs, - max 300 rows per query. -- No direct SQL access or connection strings — all access via SDK or ZCQL. -- System columns (ROWID, CREATORID, CREATEDTIME, MODIFIEDTIME) are auto-managed. - -**When a user says → they mean:** -- "I need Cloud SQL / a managed relational database" → Data Store (note ZCQL differences) - -### Stratus → Google Cloud Storage - -Key differences: -- S3-compatible patterns (buckets, keys, prefixes). -- PII/ePHI compliance, versioning, malware scanning built in. - -**When a user says → they mean:** -- "I need Cloud Storage / GCS" → Stratus -- "I need object/blob storage" → Stratus - -### File Store — DEPRECATED (removal date TBD) - -Migrate to Stratus. - -### NoSQL → Google Cloud Firestore - -Key differences: -- Collection-based document store — closest mental model is Firestore or MongoDB. -- Supports nested objects, arrays, query-by-field. -- No real-time listeners (no `onSnapshot` equivalent); no secondary indexes or aggregation pipelines. - -**When a user says → they mean:** -- "I need Firestore" → NoSQL (for document/flexible schema) or Data Store (for relational/structured data) - -### Cache → Google Cloud Memorystore - -Key differences: -- Segment-based organization (not Redis key namespaces). -- String values only — serialize/deserialize JSON yourself. -- Max TTL 48 hours; max value 5MB. - -**When a user says → they mean:** -- "I need Memorystore / Redis on GCP" → Cache (for basic caching needs) - -### Search → (No direct GCP equivalent; closest is Vertex AI Search or self-hosted Elasticsearch) - -Key differences: -- Catalyst Search covers Data Store tables only — not a general-purpose search engine. -- Enable per-column in console; simpler than managing Elasticsearch clusters. - ---- - -## Frontend & Hosting - -### Slate → Firebase Hosting / Cloud CDN - -Key differences: -- Git-based deployments, preview deployments, SSR (Next.js) — similar to Firebase Hosting + Cloud Run for SSR. -- Integrated with all Catalyst backend services. - -**When a user says → they mean:** -- "I need Firebase Hosting / GCP-based frontend hosting" → Slate - -### Web Client Hosting — LEGACY - -Use Slate for all new projects. - -### Domain Mappings → Cloud Domains + Load Balancer - -Free SSL certificates auto-provisioned. - ---- - -## Workflow & Orchestration - -### Circuits → Google Cloud Workflows - -Key differences: -- Visual drag-and-drop builder — like Cloud Workflows but with a console UI. -- State types (Function, Condition, Wait, Parallel, End) map closely to Cloud Workflows steps. -- Invokable via SDK: `catalystApp.circuit().execute()`. - -**When a user says → they mean:** -- "I need Cloud Workflows / workflow orchestration" → Circuits -- "I need to chain functions together" → Circuits - -### Job Scheduling → Google Cloud Tasks - -Key differences: -- Pool-based job management — similar to Cloud Tasks queues. -- Triggers Job Functions, Circuits, Webhooks, or AppSail services. - -**When a user says → they mean:** -- "I need Cloud Tasks / background processing" → Job Scheduling + Job Functions -- "I need scheduled jobs" → Job Scheduling - ---- - -## AI & Machine Learning - -### Zia Services → Google Cloud Vision / Natural Language APIs - -| Zia Service | GCP Equivalent | -|---|---| -| OCR | Cloud Vision OCR | -| Face Detection | Cloud Vision Face Detection | -| Image Moderation | Cloud Vision SafeSearch | -| Object Detection | Cloud Vision Object Localization | -| Text Analytics | Cloud Natural Language API | -| AutoML | Cloud AutoML | - -**When a user says → they mean:** -- "I need Cloud Vision API" → Zia Services (OCR, Face Detection, Object Detection, Image Moderation) -- "I need Cloud Natural Language / NLP" → Zia Services Text Analytics -- "I need Cloud AutoML" → QuickML - -### QuickML → Google Cloud AutoML - -**When a user says → they mean:** -- "I need Cloud AutoML / no-code ML" → QuickML -- "I need to serve an ML model as an API" → QuickML - -### ConvoKraft → Google Dialogflow - -**When a user says → they mean:** -- "I need Dialogflow / a chatbot" → ConvoKraft -- "I need an AI assistant on my website" → ConvoKraft - ---- - -## Integration & Events - -### Signals → Google Cloud Pub/Sub - -Key differences: -- Native Zoho product publishers (CRM, Books, Desk, People, Analytics) — no GCP equivalent for Zoho events. -- Supports publishers, subscribers, event routing, and schema validation. -- Replaces deprecated Event Listeners. - -**When a user says → they mean:** -- "I need Cloud Pub/Sub / event-driven architecture" → Signals -- "I need to react to Zoho/CRM changes" → Signals - -### Connections → Google Secret Manager - -Key differences: -- Not just a secret store — actively manages OAuth2 token lifecycle (refresh, rotation). -- Pre-built Zoho connectors. - -**When a user says → they mean:** -- "I need Secret Manager for API tokens" → Connections -- "I need to connect to third-party APIs with OAuth" → Connections - -### Event Listeners — DEPRECATED (removal date TBD) -Replaced by **Signals**. - -### Cron — DEPRECATED (removal date TBD) -Replaced by **Job Scheduling**. - ---- - -## DevOps & CI/CD - -### Pipelines → Google Cloud Build - -Key differences: -- YAML-based pipeline config — similar to Cloud Build's `cloudbuild.yaml`. -- Integrated with GitHub, GitLab, Bitbucket. - -**When a user says → they mean:** -- "I need Cloud Build / CI/CD" → Pipelines - -### SmartBrowz → (No direct GCP equivalent; use Compute Engine/Cloud Run + Puppeteer) - -Catalyst SmartBrowz is fully managed — no need to provision VMs or containers for headless Chrome. - -**When a user says → they mean:** -- "I need headless browser automation on GCP" → SmartBrowz -- "I need web scraping / PDF generation" → SmartBrowz - -### DevOps / Logs → Google Cloud Logging + Monitoring - -Includes: Logs, APM (execution time, error rates, cold starts), and Application Alerts. - ---- - -## Security & Identity - -### Auth & User Management → Firebase Authentication (GCP-adjacent) - -Key differences: -- Security Rules (`optional`/`required`) on functions — simpler than Firebase Auth + Firestore rules. -- Supports Zoho Accounts as an identity provider. - -**When a user says → they mean:** -- "I need Firebase Auth / GCP-based user management" → Auth & User Management - -### API Gateway → Google Cloud API Gateway - -Key differences: -- Simpler — no OpenAPI spec required, no backend service configuration. -- Path-based routing, rate limiting, CORS, auth enforcement. - ---- - -## Communication - -### Mail → (No direct GCP equivalent; use SendGrid/Mailgun or GCP Workspace SMTP) - -### Push Notifications → Firebase Cloud Messaging (FCM) - -Catalyst Push Notifications integrates FCM for Android delivery. - ---- - -## Quick Lookup: Catalyst ↔ GCP - -| Catalyst Service | GCP Equivalent | -|---|---| -| Functions | Cloud Functions | -| AppSail | Cloud Run | -| Data Store | Cloud SQL | -| Stratus | Cloud Storage | -| NoSQL | Cloud Firestore | -| Cache | Cloud Memorystore | -| Search | — (Vertex AI Search for complex needs) | -| Slate | Firebase Hosting | -| Circuits | Cloud Workflows | -| Job Scheduling | Cloud Tasks | -| Signals | Cloud Pub/Sub | -| Connections | Secret Manager | -| Pipelines | Cloud Build | -| SmartBrowz | Cloud Run + Puppeteer (self-managed) | -| ConvoKraft | Dialogflow | -| QuickML | Cloud AutoML | -| Zia OCR | Vision OCR | -| Zia Text Analytics | Natural Language API | -| Zia Image/Vision | Vision API | -| Auth & Users | Firebase Auth | -| API Gateway | Cloud API Gateway | -| Push Notifications | Firebase Cloud Messaging (FCM) | -| DevOps / Logs | Cloud Logging + Monitoring | -| Domain Mapping | Cloud Domains + Load Balancer | -| ~~File Store~~ | — → **REMOVED, use Stratus** | -| ~~Event Listeners~~ | ~~Pub/Sub triggers~~ → **REMOVED, use Signals** | -| ~~Cron~~ | ~~Cloud Scheduler~~ → **REMOVED, use Job Scheduling** | diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/equivalents-heroku.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/equivalents-heroku.md deleted file mode 100644 index 68c61a5a6..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/equivalents-heroku.md +++ /dev/null @@ -1,133 +0,0 @@ -# Catalyst ↔ Heroku (and Similar PaaS) Equivalents - -Maps Catalyst services to Heroku and comparable PaaS platforms: Railway, Render, and Fly.io. Use this -when users come from a PaaS background or ask "can I deploy like Heroku?" / "is AppSail like Heroku dynos?". - -> **Deprecated services:** Event Listeners → Signals, File Store → Stratus, Cron → Job -> Scheduling (deprecated, removal date TBD). Never recommend deprecated components for new projects. - ---- - -## Service-by-Service Mapping - -### Heroku Dynos / Railway Services / Render Web Services / Fly.io → Catalyst AppSail - -AppSail is Catalyst's PaaS compute — the direct equivalent of Heroku dynos, Railway services, Render -web services, and Fly.io apps. - -Key differences: -- Supported runtimes: Node.js, Java, Python (managed) OR custom Docker containers — similar to - Render's Docker support and Fly.io's container model. -- Auto-scaling: 1–5 instances (more limited than Heroku's unlimited dyno scaling or Fly.io's - flexible machine count). -- SDK initialization is manual: `catalyst.initialize(req)` — same mental model as initializing a - database client or SDK in a Heroku/Railway app. -- Use `process.env.X_ZOHO_CATALYST_LISTEN_PORT` instead of `PORT` (Heroku/Railway convention). -- No Heroku "worker dyno" concept — use Job Scheduling + Job Functions for background processing. -- No Heroku "one-off dyno" for scripts — use Job Scheduling for ad-hoc job execution. - -**When a user says → they mean:** -- "I want to deploy an Express app like on Heroku" → AppSail with managed Node.js runtime -- "I need Railway / Render deployment" → AppSail -- "I need Fly.io-style container deployment" → AppSail with Custom Runtime (Docker) -- "I need a persistent server, not serverless" → AppSail - ---- - -### Heroku Postgres → Catalyst Data Store - -Key differences: -- Data Store uses ZCQL (not standard SQL/Postgres) — case-sensitive table/column names, - no cross-type JOINs, max 300 rows per query. -- No direct connection string — all access via SDK or ZCQL REST/SDK calls. -- No psql shell or raw SQL access. - -**When a user says → they mean:** -- "I need Heroku Postgres / a database add-on" → Data Store (relational) or NoSQL (document schema) - ---- - -### Railway Postgres / Render Postgres → Catalyst Data Store - -Same as Heroku Postgres above. - ---- - -### Heroku Redis / Railway Redis → Catalyst Cache - -Key differences: -- Catalyst Cache is simpler — segment-based, string values only, max 48hr TTL, max 5MB per value. -- No pub/sub, streams, or Lua scripting (unlike full Redis). -- No persistent Redis — Cache is in-memory only. - -**When a user says → they mean:** -- "I need Heroku Redis / Railway Redis / a caching layer" → Cache (for basic caching/session needs) -- For full Redis features, consider an external Redis provider. - ---- - -### Heroku Scheduler → Catalyst Job Scheduling - -Both run tasks on a schedule. Key differences: -- Job Scheduling supports on-demand jobs as well as scheduled ones. -- Job Scheduling can trigger Job Functions, Circuits, Webhooks, or AppSail services. -- More flexible targeting than Heroku Scheduler's single-command model. - -**When a user says → they mean:** -- "I need Heroku Scheduler / a cron job" → Job Scheduling (not deprecated Cron function type) - ---- - -### Heroku CI / GitHub Auto-Deploy → Catalyst Pipelines + Slate Git Deploy - -- **Pipelines** handles full CI/CD with custom stages, tests, and multi-service deploys. -- **Slate** handles frontend git-based auto-build-and-deploy. - -**When a user says → they mean:** -- "I need Heroku CI / automated deployment" → Pipelines - ---- - -### Heroku Logs → Catalyst DevOps / Logs - -Catalyst DevOps provides real-time Logs, APM (execution time, error rates, cold starts), -and Application Alerts — equivalent to `heroku logs --tail`. - ---- - -### Heroku Add-ons (Storage, Email, etc.) → Catalyst Built-in Services - -Unlike Heroku's marketplace of third-party add-ons, Catalyst provides most services natively: -- Storage → Stratus -- Email → Catalyst Mail -- Search → Catalyst Search -- AI/ML → Zia Services + QuickML - ---- - -## Holistic Comparison: Heroku / Railway / Render / Fly.io vs Catalyst - -| Dimension | Heroku | Railway | Render | Fly.io | Catalyst | -|---|---|---|---|---|---| -| App hosting | Dynos | Services | Web Services | Machines | AppSail | -| Database | Heroku Postgres | Postgres add-on | Postgres | — | Data Store (ZCQL) | -| Redis | Heroku Redis | Redis add-on | Redis | — | Cache | -| Object storage | — | — | — | — | Stratus | -| Background jobs | Worker dynos | — | Background workers | — | Job Scheduling + Job Functions | -| Scheduler | Heroku Scheduler | — | Cron Jobs | — | Job Scheduling | -| CI/CD | Heroku CI | GitHub Actions | Auto-deploy | — | Pipelines | -| Serverless functions | — | — | — | — | 7 function types | -| Workflow orchestration | — | — | — | — | Circuits | -| AI / ML | — | — | — | — | Zia Services + QuickML | -| Zoho integration | None | None | None | None | Native | -| Containers | — | Yes | Yes | Yes (primary) | AppSail Custom Runtime | - -**Catalyst's key advantage over Heroku/Railway/Render/Fly.io:** Serverless functions, object storage, -AI/ML, event bus (Signals), workflow orchestration (Circuits), and native Zoho integration — all -in one platform, not just PaaS app hosting. - -**Heroku/Railway/Render/Fly.io's key advantages over Catalyst:** -- Simpler mental model for "just deploy a web app". -- Direct SQL/database access (no ZCQL abstraction). -- More flexible scaling (especially Fly.io's global machine placement). -- Larger community and ecosystem. diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/equivalents-supabase.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/equivalents-supabase.md deleted file mode 100644 index 9990c0521..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/equivalents-supabase.md +++ /dev/null @@ -1,146 +0,0 @@ -# Catalyst ↔ Supabase Equivalents - -Supabase is one of the closest conceptual analogues to Catalyst — both are integrated full-stack -platforms combining a relational database, auth, storage, and serverless functions. Use this file -when users come from Supabase or ask "is Catalyst like Supabase?" / "how does Supabase Postgres map -to Catalyst?". - -This file also includes the full holistic comparison across all Catalyst-comparable full-stack platforms. - -> **Deprecated services:** Event Listeners → Signals, File Store → Stratus, Cron → Job -> Scheduling (deprecated, removal date TBD). Never recommend deprecated components for new projects. - ---- - -## Service-by-Service Mapping - -### Supabase Postgres → Catalyst Data Store - -Key similarities: -- Both provide a managed relational database with a web console for table management. -- Both auto-generate system columns: Supabase generates `id`, `created_at`; Catalyst generates - ROWID, CREATORID, CREATEDTIME, MODIFIEDTIME. -- Both provide a table editor in the dashboard for manual data management. - -Key differences: -- Data Store uses **ZCQL** (not standard SQL/Postgres) — case-sensitive table/column names, - no cross-type JOINs, max 300 rows per query. -- No direct SQL access or connection strings — all access via SDK or ZCQL. -- No row-level security (RLS) — Catalyst uses function-level Security Rules instead. -- No stored procedures, triggers, views, or Postgres extensions (no pgvector, PostGIS, etc.). -- No Supabase "Realtime" equivalent — Catalyst has no row-change push events. - -**When a user says → they mean:** -- "I need Supabase tables / Postgres" → Data Store (note ZCQL differences from Postgres) -- "I need a relational DB with an admin UI" → Data Store (web console) - ---- - -### Supabase Auth → Catalyst Auth & User Management - -Key similarities: -- Both provide sign-up, login, password reset, and social auth flows. -- Both have client-side SDK auth flows. - -Key differences: -- Catalyst's Security Rules on functions (`optional`/`required`) replace Supabase Auth's - Row Level Security for API access control — simpler but less granular than RLS. -- Catalyst supports Zoho Accounts as a native identity provider. -- No Supabase Auth's "magic link" or "phone OTP" equivalent in Catalyst by default. - -**When a user says → they mean:** -- "I need Supabase Auth / user auth" → Auth & User Management - ---- - -### Supabase Storage → Catalyst Stratus - -Key similarities: -- Both use a bucket-based organization model. - -Key differences: -- Stratus is fully S3-compatible — more flexible and feature-rich than Supabase Storage. -- Stratus supports versioning, multipart upload, malware scanning, and PII/ePHI compliance. -- Access control via function-level Security Rules or pre-signed URL patterns (not Supabase's - per-bucket/per-object security policies). - -**When a user says → they mean:** -- "I need Supabase Storage / file storage" → Stratus - ---- - -### Supabase Edge Functions → Catalyst Advanced I/O Functions - -Key differences: -- Supabase Edge Functions run on Deno with TypeScript; Catalyst supports Node.js, Java, and Python. -- Catalyst has 7 specialized function types; Supabase has one function type. -- Catalyst SDK (`zcatalyst-sdk-node`) is initialized via `catalyst.initialize(context)`; Supabase uses Deno imports. - -**When a user says → they mean:** -- "I need Supabase Edge Functions" → Advanced I/O Function (HTTP-based) - ---- - -### Supabase Realtime → No direct equivalent - -Catalyst has no real-time push equivalent for database row changes. Workarounds: -- Use **Signals** to propagate events between backend services (e.g., trigger a downstream service - when a Data Store record changes — but not for client-side push). -- Use **client-side polling** (setInterval + REST/SDK call) for UI refresh. -- For chat/collaboration use cases, consider a dedicated WebSocket layer via AppSail. - ---- - -### Supabase Vector / pgvector → No direct equivalent - -Catalyst has no vector database. For vector search use cases: -- Use **Zia Services** for ML inference (classification, object detection, text analytics). -- Use **QuickML** for custom model training and deployment. -- For semantic search / RAG, use an external vector DB (Pinecone, Weaviate, etc.) alongside Catalyst. - ---- - -### Supabase Cron (pg_cron) → Catalyst Job Scheduling - -**When a user says → they mean:** -- "I need Supabase Cron / pg_cron" → Job Scheduling - ---- - -### Supabase Webhooks / Database Webhooks → Catalyst Signals - -Supabase Database Webhooks fire on row changes; Catalyst Signals provide an event bus for routing -events between services. Use Signals to implement event-driven patterns. - ---- - -### Supabase Studio → Catalyst Console - -Both are web-based dashboards for managing the full project — tables, functions, storage, auth, -logs, and settings. - ---- - -## Holistic Comparison: Catalyst vs Full-Stack Platforms - -| Full-Stack Platform | Similarity to Catalyst | -|---|---| -| **Supabase** | Database + Auth + Storage + Functions + Realtime — similar integrated model; Catalyst adds relational + document DBs, Circuits, Job Scheduling, AI/ML, and native Zoho integration; Supabase has real-time and full Postgres | -| **Firebase** | Auth + Firestore + Storage + Functions + Hosting — similar BaaS philosophy; Catalyst adds structured relational DB, richer compute types, Zoho integration; Firebase has real-time that Catalyst lacks | -| **AWS Amplify** | Frontend hosting + Auth + API + Storage — similar full-stack DX; Catalyst is simpler to set up and doesn't require stitching together AWS services | -| **Vercel** | Frontend + Serverless + Storage + KV — frontend-first platform; no built-in relational DB, no auth, less backend depth | -| **Netlify** | Frontend + Functions + Identity + Forms — similar but less backend depth | -| **Railway** | Full-stack hosting + DB + Redis — similar simplicity; PaaS model vs serverless | -| **Heroku** | PaaS hosting + managed DB — AppSail ≈ Heroku dynos; Catalyst adds serverless functions, AI/ML, and Zoho integration | - -**Catalyst's unique position:** The only platform with native integration across the entire Zoho -product ecosystem (CRM, Books, Desk, People, Analytics, etc.) via Signals and Connections — -eliminating the "glue code" problem entirely for businesses already using Zoho products. - -**Feature gap summary (what Catalyst does not have vs full-stack competitors):** -| Capability | Missing in Catalyst | Best Alternative | -|---|---|---| -| Real-time push | No `onSnapshot` / RTDB | Poll or use Signals for backend events | -| Full Postgres (SQL) | ZCQL only (no stored procs, extensions) | External Postgres if needed | -| pgvector / vector search | No vector DB | External vector DB + Zia for inference | -| Row-level security (RLS) | Function-level Security Rules instead | Design auth at function boundary | diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/equivalents-vercel-netlify.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/equivalents-vercel-netlify.md deleted file mode 100644 index 9f87c94b1..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/equivalents-vercel-netlify.md +++ /dev/null @@ -1,151 +0,0 @@ -# Catalyst ↔ Vercel / Netlify Equivalents - -Maps Catalyst services to their Vercel and Netlify equivalents. Use this when users come from Vercel -or Netlify, or ask "can I deploy like Vercel?" / "is there a Netlify equivalent?". - -> **Deprecated services:** Event Listeners → Signals, File Store → Stratus, Cron → Job -> Scheduling (deprecated, removal date TBD). Never recommend deprecated components for new projects. - ---- - -## Service-by-Service Mapping - -### Vercel Hosting / Netlify Hosting → Catalyst Slate - -Slate is the direct equivalent — git-based deployments, branch preview URLs, custom domains, -auto-SSL, and SSR support. - -Key differences from Vercel/Netlify: -- Slate integrates with all Catalyst backend services (Data Store, Functions, Auth) natively — - no need to wire up external DB, auth, or storage as with Vercel + Supabase or Netlify + external DB. -- Multiple frontend apps per Catalyst project (Vercel and Netlify are typically 1 repo → 1 deployment). -- Slate supports Next.js SSR, React, Vue, Angular, Svelte, SolidJS, Preact, Astro, Nuxt, Vite. -- Preview deployments for branches — same as Vercel's preview URLs and Netlify's Deploy Previews. - -**When a user says → they mean:** -- "I want to deploy like Vercel / Netlify" → Slate -- "I need preview deployments" → Slate -- "I need frontend hosting with SSR" → Slate -- "I need a CDN for my Next.js / React / Vue app" → Slate - ---- - -### Vercel Serverless Functions → Catalyst Advanced I/O Functions - -Key differences from Vercel Functions: -- Catalyst has 7 specialized function types; Vercel uses a single function model. -- Catalyst SDK (`zcatalyst-sdk-node`) is initialized via `catalyst.initialize(context)`; Vercel uses standard Node.js `import`. -- Security Rules (`optional`/`required`) are built in — no need for separate auth middleware. -- Catalyst supports Node.js, Java, and Python; Vercel Functions support JS/TS and Go/Python/Ruby. - -**When a user says → they mean:** -- "I need Vercel Serverless Functions" → Advanced I/O Function (HTTP-based) -- "I need a background Vercel Function" → Job Function + Job Scheduling - ---- - -### Netlify Functions → Catalyst Advanced I/O Functions - -Same mapping as Vercel above. Netlify Functions are AWS Lambda under the hood — Catalyst Advanced I/O -Functions are the equivalent. - -**When a user says → they mean:** -- "I need Netlify Functions" → Advanced I/O Function - ---- - -### Vercel KV → Catalyst Cache - -Key differences: -- Catalyst Cache uses segment-based organization (vs Vercel KV's Redis-compatible key namespaces). -- String values only — serialize/deserialize JSON yourself. -- Max TTL 48 hours; max value 5MB. -- No Redis pub/sub or streams. - -**When a user says → they mean:** -- "I need Vercel KV" → Cache (for basic caching/session needs) - ---- - -### Vercel Blob → Catalyst Stratus - -Key differences: -- Stratus is S3-compatible (bucket/object model) — similar to Vercel Blob's blob store but with - more features (versioning, malware scanning, PII/ePHI compliance, multipart upload). - -**When a user says → they mean:** -- "I need Vercel Blob / file storage" → Stratus - ---- - -### Vercel Postgres (Neon) → Catalyst Data Store - -Key differences: -- Data Store uses ZCQL (not standard SQL/Postgres) — case-sensitive names, 300-row query limit, - no direct connection strings. -- No full Postgres feature set (no stored procedures, no extensions, no pgvector). - -**When a user says → they mean:** -- "I need Vercel Postgres / a database" → Data Store (for relational) or NoSQL (for document schema) - ---- - -### Netlify Identity → Catalyst Auth & User Management - -Both provide user sign-up, login, and JWT-based auth for frontend apps. - -Key differences: -- Catalyst supports Zoho Accounts as an identity provider. -- Catalyst's Security Rules on functions replace Netlify Identity's role-based route protection. - -**When a user says → they mean:** -- "I need Netlify Identity / user auth" → Auth & User Management - ---- - -### Netlify CI/CD / Vercel Git Deploy → Catalyst Pipelines + Slate Git Deploy - -- **Slate** handles frontend Git-based auto-build-and-deploy (like Vercel/Netlify's primary deploy flow). -- **Pipelines** handles full CI/CD pipelines with custom stages, tests, and multi-service deploys. - -**When a user says → they mean:** -- "I need Netlify CI / Vercel CI" → Pipelines (for custom CI) or Slate Git Deploy (for frontend auto-deploy) - ---- - -### Vercel Edge Config → Catalyst Cache - -Low-latency key-value config reads — use Catalyst Cache for similar patterns. - ---- - -### Vercel Analytics / Netlify Analytics → Catalyst DevOps / APM - -Catalyst DevOps provides Logs, APM (execution time, error rates, cold starts), and Application Alerts. - ---- - -## Holistic Comparison: Vercel / Netlify vs Catalyst - -| Dimension | Vercel | Netlify | Catalyst | -|---|---|---|---| -| Frontend hosting | Yes (primary focus) | Yes (primary focus) | Slate | -| SSR | Next.js / Edge Runtime | Netlify Edge Functions | Next.js + React / Vue / Angular / Svelte / more | -| Serverless functions | Vercel Functions | Netlify Functions | 7 function types (Node.js / Java / Python) | -| Database | Vercel Postgres (Neon) | — | Data Store (ZCQL) + NoSQL | -| Storage | Vercel Blob | — | Stratus (S3-compatible) | -| Cache / KV | Vercel KV | — | Cache | -| Auth | — | Netlify Identity | Auth & User Management | -| CI/CD | Git-based auto-deploy | Git-based auto-deploy | Pipelines + Slate Git Deploy | -| Background jobs | — | Background Functions (beta) | Job Scheduling + Job Functions | -| Workflow orchestration | — | — | Circuits | -| AI / ML | — | — | Zia Services + QuickML + ConvoKraft | -| Zoho integration | None | None | Native | -| Monitoring | Vercel Analytics | Netlify Analytics | DevOps / APM / Logs | - -**Catalyst's key advantage over Vercel/Netlify:** Full-stack backend depth — relational DB, object storage, -job scheduling, workflow orchestration, and AI/ML are all built in. Vercel and Netlify are -frontend-first platforms that require external services for any meaningful backend. - -**Vercel/Netlify's key advantages over Catalyst:** Larger ecosystem, more framework integrations, -simpler DX for frontend-only projects, edge runtime for globally distributed low-latency functions. diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/functions-and-sdk.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/functions-and-sdk.md deleted file mode 100644 index 667871756..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/functions-and-sdk.md +++ /dev/null @@ -1,947 +0,0 @@ -# Functions & SDK Reference - -> **⚠️ PRE-FLIGHT CHECK:** Before using ANY code from this file, confirm that `.catalystrc` and `catalyst.json` already exist in the project directory. If they don't, STOP — do not create files, do not scaffold. Tell the user to run `catalyst init` in their terminal first. These files are auto-generated by the CLI and cannot be created manually. See the Pre-flight Gate in SKILL.md. - -## Table of Contents -1. [Function Types Overview](#function-types-overview) -2. [Function Execution Limits](#function-execution-limits) -3. [Basic I/O Functions](#basic-io-functions) -4. [Advanced I/O Functions](#advanced-io-functions) -5. [Event Functions](#event-functions) -6. [Cron Functions](#cron-functions) -7. [Integration Functions](#integration-functions) -8. [Job Functions](#job-functions) -9. [Browser Logic Functions](#browser-logic-functions) -10. [Node.js SDK Setup](#nodejs-sdk-setup) -11. [Python SDK Setup](#python-sdk-setup) -12. [Java SDK Setup](#java-sdk-setup) -13. [Web SDK (Client-Side)](#web-sdk-client-side) -14. [SDK Component Access Patterns](#sdk-component-access-patterns) -15. [Error Handling Patterns](#error-handling-patterns) -16. [Security Rules](#security-rules) -17. [Retry Behavior](#retry-behavior) -18. [Cold Starts](#cold-starts) -19. [Testing](#testing) - ---- - -## Function Types Overview - -| Type | Invocation | Use Case | Handler Args (Node.js) | SDK Init | -|------|-----------|----------|----------------------|----------| -| Basic I/O | HTTP GET via API/SDK | Simple request-response | `(context, basicIO)` | `catalyst.initialize(context)` | -| Advanced I/O | HTTP any method | REST APIs, webhooks | `(req, res)` | `catalyst.initialize(req)` | -| Event | Signals/Event Listeners | React to platform events | `(event, context)` | `catalyst.initialize(context)` | -| Cron | Scheduled by Cron jobs | Periodic tasks | `(cronDetails, context)` | `catalyst.initialize(context)` | -| Integration | Zoho service triggers | Zoho ecosystem integration | `(event, context)` | `catalyst.initialize(context)` | -| Job | Job Scheduling service | Background processing | `(jobData, context)` | `catalyst.initialize(context)` | -| Browser Logic | SmartBrowz | Headless browser scripts | `(event, context)` | `catalyst.initialize(context)` | - -> Source: https://docs.catalyst.zoho.com/en/sdk/nodejs/v2/overview/ — All function types require -> manual SDK initialization. The SDK is NOT auto-injected as `catalystApp`. - -Critical: never copy code between function types. Each type has different modules initialized in the boilerplate. Always start from the correct template. - ---- - -## Function Execution Limits - -| Function Type | Timeout | Behavior on Timeout | -|---------------|---------|---------------------| -| Basic I/O | 30 seconds | Returns 504 Gateway Timeout | -| Advanced I/O | 30 seconds | Returns 504 Gateway Timeout | -| Event | 15 minutes | Silently terminated; may auto-retry | -| Cron | 15 minutes | Marked as failed; may auto-retry | -| Integration | 30 seconds | Returns error to calling Zoho service | -| Job | 15 minutes | Marked as failed; may auto-retry | -| Browser Logic | 30 seconds | Browser instance terminated | - -For long-running tasks exceeding 30 seconds, use Event, Job, or Cron functions (up to 15 minutes). -For tasks exceeding 15 minutes, migrate to AppSail which has no function-level timeout. - ---- - -## Basic I/O Functions - -Simplest function type. Receives a string input, returns a string output. Invoked via GET request. - -### Node.js Template -```javascript -// functions/my_basic_io/index.js -'use strict'; -const catalyst = require('zcatalyst-sdk-node'); - -module.exports = (context, basicIO) => { - try { - const catalystApp = catalyst.initialize(context); - - // Get input data (sent as query parameter) - const inputData = context.getArgument(); - - // Access Catalyst components - const dataStore = catalystApp.datastore(); - - // Your business logic here - const result = `Processed: ${inputData}`; - - // Send response (must be a string) - basicIO.write(result); - } catch (error) { - console.error('Error:', error); - basicIO.write(JSON.stringify({ error: error.message })); - } -}; -``` - -### package.json -```json -{ - "name": "my_basic_io", - "version": "1.0.0", - "main": "index.js", - "dependencies": { - "zcatalyst-sdk-node": "latest" - } -} -``` - -Invocation: `GET /server/my_basic_io/execute?args=` - ---- - -## Advanced I/O Functions - -Full HTTP support with raw Node.js request/response objects. Best for REST APIs. - -> **node20 runtime — NOT Express.** `req` is a raw `http.IncomingMessage` and `res` is a raw -> `http.ServerResponse`. Do **not** use `res.status()`, `res.json()`, or `req.body` directly — -> these are Express methods and will throw `res.status is not a function`. Use the helpers below. - -### Required helpers (always include in node20 Advanced I/O) -```javascript -// Send a JSON response -function sendJson(res, statusCode, data) { - res.writeHead(statusCode, { 'Content-Type': 'application/json' }); - res.end(JSON.stringify(data)); -} - -// Read and parse the request body (req.body is NOT auto-parsed in node20) -function getBody(req) { - return new Promise((resolve, reject) => { - if (req.body && typeof req.body === 'object') return resolve(req.body); - if (req.body && typeof req.body === 'string') { - try { return resolve(JSON.parse(req.body)); } catch (e) { return resolve({}); } - } - let data = ''; - req.on('data', (chunk) => { data += chunk; }); - req.on('end', () => { - try { resolve(data ? JSON.parse(data) : {}); } catch (e) { resolve({}); } - }); - req.on('error', reject); - }); -} -``` - -### Node.js Template -```javascript -// functions/my_api/index.js -'use strict'; -const catalyst = require('zcatalyst-sdk-node'); - -function sendJson(res, statusCode, data) { - res.writeHead(statusCode, { 'Content-Type': 'application/json' }); - res.end(JSON.stringify(data)); -} - -function getBody(req) { - return new Promise((resolve, reject) => { - if (req.body && typeof req.body === 'object') return resolve(req.body); - if (req.body && typeof req.body === 'string') { - try { return resolve(JSON.parse(req.body)); } catch (e) { return resolve({}); } - } - let data = ''; - req.on('data', (chunk) => { data += chunk; }); - req.on('end', () => { - try { resolve(data ? JSON.parse(data) : {}); } catch (e) { resolve({}); } - }); - req.on('error', reject); - }); -} - -module.exports = async (req, res) => { - try { - const catalystApp = catalyst.initialize(req); - const method = req.method; - // Parse URL and query params — req.query does NOT exist on http.IncomingMessage - const parsedUrl = new URL(req.url, `https://${req.headers.host}`); - const query = Object.fromEntries(parsedUrl.searchParams); - - if (method === 'GET') { - const queryParam = query.id; - sendJson(res, 200, { message: 'GET request', id: queryParam }); - - } else if (method === 'POST') { - const body = await getBody(req); - sendJson(res, 201, { message: 'Created', data: body }); - - } else if (method === 'PUT') { - const body = await getBody(req); - sendJson(res, 200, { message: 'Updated', data: body }); - - } else if (method === 'DELETE') { - sendJson(res, 200, { message: 'Deleted' }); - - } else { - sendJson(res, 405, { error: 'Method not allowed' }); - } - } catch (error) { - console.error('Error:', error); - sendJson(res, 500, { error: error.message }); - } -}; -``` - -> **Legacy projects (node14/16/18)** may still use the 4-parameter signature: -> `module.exports = (catalystApp, context, req, res) => { ... }` -> where `catalystApp` is pre-initialized. New CLI-initialized projects (node20+) -> use the 2-parameter format shown above. - -Invocation: Any HTTP method to `/server/my_api/execute` - -### User-scope vs admin-scope initialization - -The SDK supports two initialization scopes. Choose based on what the operation needs: - -```javascript -// USER SCOPE (default) — for resolving user identity -const userApp = catalyst.initialize(req); -const currentUser = await userApp.userManagement().getCurrentUser(); -// getCurrentUser() makes an internal GET to /project-user/current using the user token. -// Only works for registered app users (signed up via Catalyst auth), NOT collaborators/admins. - -// ADMIN SCOPE — for all data operations (DataStore, Stratus, ZCQL, Cache, etc.) -const adminApp = catalyst.initialize(req, { scope: 'admin' }); -const dataStore = adminApp.datastore(); -const zcql = adminApp.zcql(); -const stratus = adminApp.stratus(); -``` - -**Common pattern for apps that need both auth AND data:** -```javascript -module.exports = async (req, res) => { - try { - // 1. Get user identity (user-scope) - const userApp = catalyst.initialize(req); - const currentUser = await userApp.userManagement().getCurrentUser(); - - // 2. Perform data operations (admin-scope) - const adminApp = catalyst.initialize(req, { scope: 'admin' }); - const table = adminApp.datastore().table('MyTable'); - - // 3. Use currentUser for ownership/filtering - const rows = await adminApp.zcql().executeZCQLQuery( - `SELECT * FROM MyTable WHERE owner_id = '${currentUser.user_id}'` - ); - sendJson(res, 200, { user: currentUser, data: rows }); - } catch (error) { - sendJson(res, 500, { error: error.message }); - } -}; -``` - -> ⚠️ **Do NOT use admin-scope for `getCurrentUser()`** — it throws "no user credentials present". -> Admin scope lacks user identity. Use default (user) scope for identity, admin scope for data. - -### CORS handling for Slate → Function cross-domain - -When a Slate frontend calls your Advanced I/O function cross-domain, the Catalyst gateway injects -`Access-Control-Allow-Origin` automatically (if the Slate domain is in Authorized Domains in the console). - -**Critical rule: do NOT set CORS headers in your function for production origins.** If both the -gateway and your code set `Access-Control-Allow-Origin`, the browser receives duplicate values -and rejects the response. - -Only set CORS headers for `localhost` (local dev, where no gateway exists): - -```javascript -// Place this BEFORE your route handlers -app.use((req, res, next) => { - const origin = req.headers.origin || ''; - if (/^http:\/\/localhost(:\d+)?$/.test(origin)) { - res.setHeader('Access-Control-Allow-Origin', origin); - res.setHeader('Access-Control-Allow-Credentials', 'true'); - res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); - res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); - if (req.method === 'OPTIONS') return res.status(204).end(); - } - next(); -}); -``` - -**Setup required in the console:** -Console → Authentication → Whitelisting → Authorized Domains → add your Slate domain → enable CORS toggle. - -> ⚠️ **The `Authorization` header your frontend sends is stripped by the gateway.** After -> validation, the gateway replaces it with internal `x-zc-*` headers. `req.headers['authorization']` -> will be `undefined` inside your function. The SDK reads the `x-zc-*` headers internally via -> `catalyst.initialize(req)` — you don't need to handle this manually. - -### HTTP payload limits - -| Limit | Value | -|-------|-------| -| Request body (JSON/form/multipart) | 250 MB | -| Response body | 250 MB | - -Exceeding these limits returns HTTP 413 (Payload Too Large). For very large file transfers, -use Stratus presigned upload URLs instead of passing data through functions. - -### Handling request bodies and file uploads in Advanced I/O - -> ⚠️ **Advanced I/O functions use raw `http.IncomingMessage` — NOT Express.** There is no `req.body`, -> no `req.files`, no `req.params`, and no built-in body parser of any kind. These are Express/multer -> features that do not exist on a raw Node.js request object. -> - **JSON bodies:** Manually accumulate chunks from the request stream (see `getBody()` helper above). -> - **`multipart/form-data` file uploads:** Install `busboy` (`npm install busboy`) and pipe `req` through it. -> - **Query parameters:** Use `new URL(req.url, ...).searchParams` — `req.query` does not exist. -> - **URL path segments:** Use `new URL(req.url, ...).pathname` — `req.params` does not exist. - -#### File upload with busboy - -```javascript -'use strict'; -const catalyst = require('zcatalyst-sdk-node'); -const Busboy = require('busboy'); - -function sendJson(res, statusCode, data) { - res.writeHead(statusCode, { 'Content-Type': 'application/json' }); - res.end(JSON.stringify(data)); -} - -// Parse multipart/form-data from raw http.IncomingMessage -function parseMultipart(req) { - return new Promise((resolve, reject) => { - const bb = Busboy({ headers: req.headers }); - const fields = {}; - let fileInfo = null; - const chunks = []; - - bb.on('field', (name, val) => { fields[name] = val; }); - bb.on('file', (name, stream, info) => { - fileInfo = { name: info.filename, mimetype: info.mimeType }; - stream.on('data', (chunk) => chunks.push(chunk)); - stream.on('end', () => { - fileInfo.data = Buffer.concat(chunks); - fileInfo.size = fileInfo.data.length; - }); - }); - bb.on('close', () => resolve({ fields, file: fileInfo })); - bb.on('error', reject); - req.pipe(bb); - }); -} - -module.exports = async (req, res) => { - const catalystApp = catalyst.initialize(req); - - // Parse the multipart form data using busboy - const { fields, file } = await parseMultipart(req); - - if (!file) { - return sendJson(res, 400, { error: 'No file provided. Send file as multipart/form-data.' }); - } - - const stratus = catalystApp.stratus(); - const bucket = stratus.bucket('my-bucket'); - - try { - // putObject(key, body) or putObject(key, body, options) - // See: https://docs.catalyst.zoho.com/en/sdk/nodejs/v2/cloud-scale/stratus/upload-object/ - await bucket.putObject(file.name, file.data, { - contentType: file.mimetype - }); - sendJson(res, 200, { message: 'Uploaded', name: file.name, size: file.size }); - } catch (err) { - sendJson(res, 500, { error: err.message }); - } -}; -``` - -> **`busboy` must be in your function's `package.json` dependencies.** Run `npm install busboy` inside the function directory before deploying. - ---- - -## Event Functions - -Triggered by Signals or Event Listeners. Cannot be invoked directly via HTTP. - -```javascript -// functions/my_event_fn/index.js -'use strict'; -const catalyst = require('zcatalyst-sdk-node'); - -module.exports = (event, context) => { - try { - const catalystApp = catalyst.initialize(context); - - // Get event data - const eventData = event.getArgument(); - const parsedData = JSON.parse(eventData); - - console.log('Event received:', parsedData); - - // Process the event - // ... - - // Must close context when done - context.close(); - } catch (error) { - console.error('Event processing error:', error); - context.close(); - } -}; -``` - -Event types that can trigger Event Functions: -- **Component Events**: Data Store row insert/update/delete, File Store upload/delete -- **Custom Events**: User-defined events triggered via SDK/API -- **Zoho Events**: Events from Zoho services (CRM, Books, etc.) - ---- - -## Cron Functions - -Invoked by Cron jobs on a schedule. Cannot be invoked directly. - -```javascript -// functions/my_cron_fn/index.js -'use strict'; -const catalyst = require('zcatalyst-sdk-node'); - -module.exports = async (cronDetails, context) => { - try { - const catalystApp = catalyst.initialize(context); - const cronInfo = cronDetails.getArgument(); - console.log('Cron job triggered:', cronInfo); - - // Perform scheduled task - // Example: Clean up old records - const zcql = catalystApp.zcql(); - const query = "DELETE FROM Reports WHERE CREATEDTIME < '2024-01-01 00:00:00'"; - await zcql.executeZCQLQuery(query); - - // Signal success - context.closeWithSuccess(); - } catch (error) { - console.error('Cron error:', error); - context.closeWithFailure(); - } -}; -``` - ---- - -## Integration Functions - -For integrating with other Zoho services. Note: NOT available in EU, AU, IN, or CA data centers. - -```javascript -// functions/my_integration_fn/index.js -'use strict'; -const catalyst = require('zcatalyst-sdk-node'); - -module.exports = (event, context) => { - try { - const catalystApp = catalyst.initialize(context); - const integrationData = event.getArgument(); - const parsedData = JSON.parse(integrationData); - - // Access the Zoho service data - console.log('Integration data:', parsedData); - - // Process and respond - context.close(); - } catch (error) { - console.error('Integration error:', error); - context.close(); - } -}; -``` - ---- - -## Job Functions - -Triggered by the Job Scheduling service for background processing. - -```javascript -// functions/my_job_fn/index.js -'use strict'; -const catalyst = require('zcatalyst-sdk-node'); - -module.exports = async (jobData, context) => { - try { - const catalystApp = catalyst.initialize(context); - const jobDetails = jobData.getArgument(); - console.log('Job started:', jobDetails); - - // Long-running background task - // ... - - context.closeWithSuccess(); - } catch (error) { - console.error('Job error:', error); - context.closeWithFailure(); - } -}; -``` - ---- - -## Browser Logic Functions - -Used with SmartBrowz for headless browser automation. - -```javascript -// functions/my_browser_fn/index.js -'use strict'; - -module.exports = (catalystApp, context, browserData) => { - try { - const input = browserData.getArgument(); - // Browser automation logic - context.close(); - } catch (error) { - console.error('Browser logic error:', error); - context.close(); - } -}; -``` - ---- - -## Node.js SDK Setup - -> **Runtime support:** node20 is the only actively supported runtime — node14, 16, and 18 still work for legacy projects but receive no upstream security patches. Use node20 for all new functions. - -### In Functions -The initialization pattern depends on the Node.js runtime version used when the project was created: - -```javascript -// NEW (node20+ / CLI-initialized projects) — SDK must be initialized manually: -'use strict'; -const catalyst = require('zcatalyst-sdk-node'); - -module.exports = async (req, res) => { - const catalystApp = catalyst.initialize(req); - const dataStore = catalystApp.datastore(); - // ... -}; - -// LEGACY (node14/16/18) — catalystApp is pre-initialized as first parameter: -// module.exports = (catalystApp, context, req, res) => { -// const dataStore = catalystApp.datastore(); -// }; -``` - -### In AppSail (manual initialization) -```javascript -const catalyst = require('zcatalyst-sdk-node'); - -// Initialize with request context (for user-scoped operations) -app.get('/api/data', (req, res) => { - const catalystApp = catalyst.initialize(req); - const dataStore = catalystApp.datastore(); - // ... -}); - -// Initialize as admin (for system-level operations) -const catalystApp = catalyst.initialize(req, { scope: 'admin' }); -``` - -### NPM Package -```bash -npm install zcatalyst-sdk-node -``` - ---- - -## Python SDK Setup - -### In Functions -```python -# functions/my_function/main.py -import zcatalyst_sdk - -def handler(context, basicIO): - catalyst_app = zcatalyst_sdk.initialize() - datastore = catalyst_app.datastore() - - # Business logic - result = "Processed" - basicIO.write(result) -``` - -### pip Package -```bash -pip install zcatalyst-sdk -``` - ---- - -## Java SDK Setup - -### In Functions -```java -// Maven dependency: com.zoho.catalyst:zcatalyst-sdk -import com.zoho.catalyst.api.CatalystApp; -import com.zoho.catalyst.api.beans.*; - -public class MainFunction implements BasicIO { - @Override - public void runner(CatalystApp catalystApp, Context context, BasicIOObject basicIO) { - try { - String input = context.getArgument(); - // Business logic - basicIO.write("Result"); - } catch (Exception e) { - basicIO.write("Error: " + e.getMessage()); - } - } -} -``` - ---- - -## Web SDK (Client-Side) - -The Web SDK is used in frontend code to interact with Catalyst backend services. - -### Include via script tag -```html - - - - -``` - -### Authentication flow -```javascript -// Sign up a new user -catalyst.auth.signUp({ - email_id: "user@example.com", - first_name: "John", - last_name: "Doe" -}); - -// Login -catalyst.auth.login("user@example.com", "password"); - -// Check if logged in -const isLoggedIn = catalyst.auth.isUserAuthenticated(); - -// Logout -catalyst.auth.signOut(); -``` - -### Calling functions from client -```javascript -// Call a Basic I/O function -catalyst.server.callFunction("my_basic_io", { args: "input_data" }) - .then(response => console.log(response)) - .catch(err => console.error(err)); - -// Call an Advanced I/O function -catalyst.server.callAdvancedIO("my_api", { - method: "POST", - body: JSON.stringify({ key: "value" }), - headers: { "Content-Type": "application/json" } -}) - .then(response => console.log(response)) - .catch(err => console.error(err)); -``` - -> **Always use `credentials: 'include'` when using native `fetch()`** to call Catalyst functions -> from a Catalyst-hosted web client. Without it, auth cookies are not forwarded and -> `getCurrentUser()` in the function will throw a 401 — even when both are on the same domain. -> -> ```javascript -> const res = await fetch('/server/my_api/execute', { -> method: 'POST', -> credentials: 'include', // ← required for auth cookies to be sent -> headers: { 'Content-Type': 'application/json' }, -> body: JSON.stringify({ key: 'value' }) -> }); -> ``` -> -> The `catalyst.server.callAdvancedIO()` SDK method handles this automatically. -> Use it when possible to avoid this class of issue. - ---- - -## SDK Component Access Patterns - -All components are accessed through the initialized `catalystApp` object: - -```javascript -// Data Store -const dataStore = catalystApp.datastore(); -const table = dataStore.table('TableName'); // by name -const table = dataStore.table(TABLE_ID); // by ID - -// File Store -const fileStore = catalystApp.filestore(); -const folder = fileStore.folder(FOLDER_ID); - -// Cache -const cache = catalystApp.cache(); -const segment = cache.segment(SEGMENT_ID); - -// ZCQL -const zcql = catalystApp.zcql(); - -// Email -const email = catalystApp.email(); - -// Search -const search = catalystApp.search(); - -// User Management -const userManagement = catalystApp.userManagement(); - -// Push Notifications -const pushNotification = catalystApp.pushNotification(); - -// Connections (for third-party auth) -const connection = catalystApp.connection(); -``` - ---- - -## Error Handling Patterns - -### Recommended pattern for Advanced I/O functions -```javascript -'use strict'; -const catalyst = require('zcatalyst-sdk-node'); - -function sendJson(res, statusCode, data) { - res.writeHead(statusCode, { 'Content-Type': 'application/json' }); - res.end(JSON.stringify(data)); -} - -function getBody(req) { - return new Promise((resolve, reject) => { - if (req.body && typeof req.body === 'object') return resolve(req.body); - if (req.body && typeof req.body === 'string') { - try { return resolve(JSON.parse(req.body)); } catch (e) { return resolve({}); } - } - let data = ''; - req.on('data', (chunk) => { data += chunk; }); - req.on('end', () => { - try { resolve(data ? JSON.parse(data) : {}); } catch (e) { resolve({}); } - }); - req.on('error', reject); - }); -} - -module.exports = async (req, res) => { - try { - const catalystApp = catalyst.initialize(req); - const body = await getBody(req); - - // Validate input - if (!body.name) { - return sendJson(res, 400, { status: 'error', message: 'Name is required' }); - } - - // Business logic - const result = await someOperation(catalystApp, body); - - sendJson(res, 200, { status: 'success', data: result }); - } catch (error) { - console.error('Function error:', error); - - if (error.code === 'INVALID_DATA') { - return sendJson(res, 400, { status: 'error', message: error.message }); - } - - sendJson(res, 500, { status: 'error', message: 'Internal server error' }); - } -}; -``` - ---- - -## Security Rules - -Security Rules control who can invoke Basic I/O and Advanced I/O functions. -See: https://docs.catalyst.zoho.com/en/serverless/help/security-rules/key-concepts/ - -**The only valid values for the `authentication` parameter are:** -- **`optional`** — Anyone can invoke the function without authentication (public access). This is the default. -- **`required`** — Only authenticated users (Catalyst Users Authentication or OAuth) can invoke the function. - -⚠️ **Values like `no_auth`, `user_auth`, `admin_auth` do NOT exist** and will throw `"Invalid input value"`. -Security Rules is a binary gate (public vs. authenticated). For admin-only access or per-route -auth control, disable Security Rules and enable **API Gateway** instead. - -Security rules are configured in the Catalyst console under Serverless → Security Rules. They define -JSON-based rules that determine HTTP methods and authentication per function. - -Default: all functions are set to `optional` (public access). Set to `required` to enforce authentication, -or use the API Gateway for more granular control (API keys, per-route auth, throttling). - ---- - -## Retry Behavior - -Background function types (Event, Cron, Job) automatically retry on failure. HTTP-facing -function types (Basic I/O, Advanced I/O) do not retry — the error is returned to the caller. - -| Function Type | Auto-retry on failure? | Retry behavior | -|---------------|----------------------|----------------| -| Basic I/O | No | Error returned to caller | -| Advanced I/O | No | Error returned to caller | -| Event | Yes | Retries on failure (developer-configurable) | -| Cron | Yes | Retries on failure (developer-configurable) | -| Job | Yes | Retries on failure (developer-configurable) | -| Integration | No | Error returned to calling Zoho service | -| Browser Logic | No | Error returned to caller | - -Retry logic for background functions is controlled by how you write your function and configure -the triggering service. There is no fixed platform retry count — design your handlers to be -**idempotent** (safe to run multiple times with the same input) since retries may occur. - ---- - -## Cold Starts - -When a function hasn't been invoked recently, Catalyst provisions a new execution -environment — this adds latency to the first request ("cold start"). - -**Typical cold-start latency:** -| Runtime | Cold start | Warm invocation | -|---------|-----------|-----------------| -| Node.js | 500ms–2s | 50–200ms | -| Java | 2–8s | 50–200ms | -| Python | 500ms–2s | 50–200ms | - -Java has the longest cold starts due to JVM initialization. - -**Mitigation strategies:** -- Keep function packages small — fewer dependencies = faster init -- Avoid heavy initialization outside the handler (large file reads, DB pool creation on import) -- Use a scheduled ping (via Job Scheduling) to keep critical functions warm -- For latency-sensitive endpoints, consider AppSail — it runs persistently with no cold starts - ---- - -## Testing - -### Unit testing function handlers - -Mock the SDK and `res` (raw `http.ServerResponse`) to test handler logic without connecting to Catalyst. - -> **Important (node20):** Advanced I/O functions use raw `http.ServerResponse`, NOT Express. -> Your mock must use `writeHead()` and `end()`, not `status()` or `json()`. - -```javascript -// test/my_function.test.js -const handler = require('../functions/my_function/index'); - -const mockReq = { - method: 'POST', - url: '/tasks', - headers: { 'content-type': 'application/json' } -}; - -// Mock raw http.ServerResponse — NOT Express response -const mockRes = { - writeHead: jest.fn(), - end: jest.fn() -}; - -jest.mock('zcatalyst-sdk-node', () => ({ - initialize: () => ({ - datastore: () => ({ - table: () => ({ - insertRow: jest.fn().mockResolvedValue({ ROWID: '123' }) - }) - }), - zcql: () => ({ - executeZCQLQuery: jest.fn().mockResolvedValue([]) - }) - }) -})); - -test('POST returns 201', async () => { - await handler(mockReq, mockRes); - expect(mockRes.writeHead).toHaveBeenCalledWith(201, expect.any(Object)); -}); -``` - -### Integration testing with `catalyst serve` - -`catalyst serve` runs Basic I/O and Advanced I/O functions locally, connecting to the -remote Development Data Store. Use it for integration tests: - -```bash -catalyst serve & -curl -X POST http://localhost:3000/server/my_function/execute -d '{"name":"Test"}' -``` - -Note: Event, Cron, and Job functions cannot be tested locally via `catalyst serve`. -Deploy to Development and trigger them from the console for integration testing. - ---- - -## Common SDK Mistakes by Language - -Agents frequently generate code with these errors. Check this section before finalising any -function code, especially when switching between function types or languages. - -### Node.js (`zcatalyst-sdk-node`) - -| Mistake | What goes wrong | Correct approach | -|---------|----------------|------------------| -| Calling `catalyst.initialize()` without `req` in Advanced I/O | SDK initialization fails; `catalystApp` is not correctly scoped to the request | Must pass `req`: `catalyst.initialize(req)` | -| Calling `basicIO.write()` more than once | Only the first call is used; subsequent calls are silently ignored or cause errors | `basicIO.write()` can only be called **once** per function execution | -| Expecting JSON output from Basic I/O | Basic I/O only supports STRING output | Basic I/O returns STRING only — use Advanced I/O for JSON responses | -| Setting HTTP response headers in Basic I/O | Basic I/O does not have a response object | Basic I/O does NOT support HTTP headers or status codes — use Advanced I/O | -| Using `res.status()` or `res.json()` in Advanced I/O (node20) | `res` is a raw `http.ServerResponse`, not Express — these methods do not exist | Use `res.writeHead(statusCode, headers)` and `res.end(JSON.stringify(data))` | -| Not handling ZCQL 300-row limit | Queries silently return only 300 rows; data appears missing | Paginate with `LIMIT offset, count` (e.g., `LIMIT 0, 300`, `LIMIT 300, 300`) | -| Using wrong port variable for AppSail | App binds to hard-coded port; Catalyst routes to a different port, causing connection failures | Always use `process.env.X_ZOHO_CATALYST_LISTEN_PORT \|\| 9000` | -| Not adding `credentials: 'include'` to fetch calls from web client | Auth cookies not forwarded; `getCurrentUser()` throws 401 even for authenticated users | Add `credentials: 'include'` to all fetch calls from the web client | -| Parsing `CREATEDTIME` directly with `new Date()` | Catalyst stores CREATEDTIME in the project timezone without an offset marker; `new Date()` treats it as UTC → wrong timestamps | Append the project timezone offset before parsing the date string | -| Using Express `cors()` middleware with Slate → Function cross-domain | Gateway AND Express both inject `Access-Control-Allow-Origin` → duplicate header → browser rejects | Only set CORS headers for localhost (local dev). Remove `cors()` middleware entirely for production origins. The gateway handles it. | -| Using admin-scope for `getCurrentUser()` | Throws "no user credentials present" — admin scope has no user identity | Use default (user) scope: `catalyst.initialize(req)` for `getCurrentUser()`. Use admin scope only for data operations. | -| Not handling `getCurrentUser()` returning `null` | Collaborators/admins are not registered app users → `null` return → `Cannot read properties of null` | Add null check. `getCurrentUser()` only works for users who signed up through Catalyst's auth flow, not console collaborators. | -| Reading `req.headers['authorization']` inside the function | Gateway strips the `Authorization` header after validation and injects `x-zc-*` internal headers instead → `undefined` | Don't read the Authorization header. Use `catalyst.initialize(req)` which reads the `x-zc-*` headers internally. | - -### Java - -| Mistake | What goes wrong | Correct approach | -|---------|----------------|------------------| -| Uploading compiled function via console without `.class` files | Function fails to execute with a missing class reference | Use `catalyst deploy` from CLI — it auto-compiles and creates missing dependency files | -| Using JDK version other than 8, 11, or 17 | Build or runtime errors; Catalyst only supports these three versions | Only JDK 8, 11, and 17 are supported | -| Not using context timing methods for long operations | Operations may exceed the timeout with no graceful handling | Use `context.getMaxExecutionTimeMs()` and `context.getRemainingExecutionTimeMs()` to manage time-sensitive operations | - -### Python - -| Mistake | What goes wrong | Correct approach | -|---------|----------------|------------------| -| Not using Flask for Advanced I/O functions | Python Advanced I/O functions require Flask; without it, the function cannot handle HTTP requests | Python Advanced I/O functions require the Flask framework | -| Using wrong handler signature for a function type | Function fails to initialize or throws an error on invocation | Handler signatures differ per function type — always consult the Function Types Overview table above | -| Using `context.getMaxExecutionTimeMs()` (camelCase) in Python | Method not found error | Python uses snake_case: `context.get_max_execution_time_ms()` and `context.get_remaining_execution_time_ms()` | - -### General (all languages) - -| Mistake | What goes wrong | Correct approach | -|---------|----------------|------------------| -| Creating `ROWID`, `CREATORID`, `CREATEDTIME`, or `MODIFIEDTIME` columns | These system columns are auto-created by Catalyst; attempting to create them causes an error | Never create these columns — Catalyst adds them automatically to every table | -| Hardcoding Catalyst IDs (Table ID, ZAID, Org ID, Project ID) without explanation | Users cannot find the correct values; wrong IDs cause permission errors | Always add an inline comment specifying exactly where to find the ID in the Catalyst console | -| Using Production environment in dev/test code | Production requires separate authorization; mixing environments causes auth failures | Always default to `"Development"` environment; only use production when explicitly requested | -| Checking for `.catalystrc` and `catalyst.json` before init | Re-running init on an already-initialized project overwrites config | Always check for existing `.catalystrc` and `catalyst.json` before scaffolding | -| Not serializing JSON before storing in Cache | Cache values are strings only; storing objects directly causes type errors | Always `JSON.stringify()` before storing in Cache and `JSON.parse()` when reading | -| Inserting emoji or 4-byte UTF-8 into Data Store | Silently stored as `?`; data is corrupted | Store a string key (e.g., `"happy"`) and map to emoji in application code | diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/job-scheduling-deep-dive.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/job-scheduling-deep-dive.md deleted file mode 100644 index 2dcbbf736..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/job-scheduling-deep-dive.md +++ /dev/null @@ -1,397 +0,0 @@ -# Job Scheduling Deep-Dive Reference - -## When to use this file -Load this file when the user asks about: Job Scheduling, job pools, cron jobs, cron expressions, -dynamic cron, scheduled tasks, job execution history, or migrating from deprecated Cron to Job -Scheduling. - -External docs: https://docs.catalyst.zoho.com/en/serverless/job-scheduling/ - ---- - -## Architecture Overview - -**Job Pool → Jobs/Cron → Targets** - -1. A **Job Pool** is a container that groups related jobs and defines the target type. -2. **Jobs** are individual task submissions to a pool (on-demand or scheduled). -3. **Cron** schedules define when jobs are automatically submitted. -4. **Targets** are the Catalyst components that execute the job logic. - ---- - -## Job Pools - -A Job Pool is the top-level container. Each pool is bound to a specific target type and -memory allocation. - -### Pool Types (4 types — immutable after creation) - -| Pool Type | Target | Description | -|-----------|--------|-------------| -| **Function** | Job Function | Executes a Catalyst Job Function | -| **Webhook** | HTTP endpoint | Calls an external URL | -| **Circuit** | Catalyst Circuit | Triggers a Circuit workflow | -| **AppSail** | AppSail service | Calls an AppSail endpoint | - -**Important:** The pool type is **immutable** — once created, you cannot change a pool from -Function to Webhook or any other type. Create a new pool if you need a different target type. - -### Memory Allocation -- Max **10 GB** memory per job pool -- Memory is shared across all concurrent job executions in the pool -- Configure based on expected parallel workload - -### Parallel Triggers -- Max **10 parallel triggers** per job pool -- If all 10 slots are occupied, new job submissions queue until a slot frees up - ---- - -## Jobs - -### Configuration Fields -- **Job name**: Identifier for the job -- **Input data**: JSON payload passed to the target (stringified) -- **Priority**: Execution priority within the pool queue -- **Timeout**: Max execution time for the job - -### Submission Methods -- **Console**: Submit a job manually from the Catalyst console -- **SDK**: Submit programmatically from Catalyst functions or AppSail -- **REST API**: Submit via the Job Scheduling REST API -- **Cron**: Automatic submission on a schedule - -### Execution States - -| State | Description | -|-------|-------------| -| **Queued** | Job submitted and waiting for an available slot | -| **In Progress** | Job is currently executing | -| **Success** | Job completed successfully | -| **Failed** | Job execution failed | -| **Timed Out** | Job exceeded its configured timeout | -| **Cancelled** | Job was cancelled before execution | - ---- - -## Cron - -Cron schedules automatically submit jobs to a pool at defined intervals. - -### Pre-defined Cron -- Created via the **Catalyst Console** -- Configured in the console UI with a visual schedule builder -- **Migrates to production** when you deploy to prod -- Recommended for stable, recurring schedules - -### Dynamic Cron -- Created via the **Catalyst SDK** at runtime -- Configured programmatically in your function/AppSail code -- **Does NOT migrate to production** — must be created separately in each environment -- Recommended for schedules that depend on runtime data or user configuration - ---- - -## Schedule Types - -### One-Time -- Executes once at a specified date and time -- Automatically removed after execution - -### Recurring -- Executes repeatedly based on a cron expression -- Minimum interval: **1 minute** -- Continues until disabled or deleted - ---- - -## Cron Expressions - -5-field cron format: - -``` -┌───────── minute (0-59) -│ ┌───────── hour (0-23) -│ │ ┌───────── day of month (1-31) -│ │ │ ┌───────── month (1-12) -│ │ │ │ ┌───────── day of week (0-6, 0=Sunday) -│ │ │ │ │ -* * * * * -``` - -### Special Characters - -| Character | Meaning | Example | -|-----------|---------|---------| -| `*` | Every value | `* * * * *` = every minute | -| `,` | List of values | `0,15,30,45 * * * *` = every 15 minutes | -| `-` | Range | `0-5 * * * *` = minutes 0 through 5 | -| `/` | Step | `*/10 * * * *` = every 10 minutes | - -### Common Cron Expression Examples - -| Expression | Schedule | -|------------|----------| -| `* * * * *` | Every minute | -| `*/5 * * * *` | Every 5 minutes | -| `0 * * * *` | Every hour (at minute 0) | -| `0 0 * * *` | Daily at midnight | -| `0 9 * * 1-5` | Weekdays at 9:00 AM | -| `0 0 1 * *` | First day of every month at midnight | -| `0 0 * * 0` | Every Sunday at midnight | -| `30 8 * * 1` | Every Monday at 8:30 AM | -| `0 */6 * * *` | Every 6 hours | -| `0 9,17 * * *` | At 9:00 AM and 5:00 PM daily | - ---- - -## Dynamic Cron SDK Examples - -### Node.js - -```javascript -const jobScheduling = catalystApp.jobScheduling(); -const pool = jobScheduling.pool(POOL_ID); - -// Every / Periodic — every 10 minutes -await pool.createCron({ - cron_name: 'periodic-sync', - cron_type: 'periodic', - every: { minutes: 10 }, - input: JSON.stringify({ task: 'sync-data' }) -}); - -// Daily — at 9:00 AM -await pool.createCron({ - cron_name: 'daily-report', - cron_type: 'daily', - time: '09:00', - input: JSON.stringify({ task: 'generate-report' }) -}); - -// Monthly — 1st of every month at midnight -await pool.createCron({ - cron_name: 'monthly-cleanup', - cron_type: 'monthly', - day_of_month: 1, - time: '00:00', - input: JSON.stringify({ task: 'cleanup' }) -}); - -// Expression — custom cron expression (weekdays at 8:30 AM) -await pool.createCron({ - cron_name: 'weekday-task', - cron_type: 'expression', - cron_expression: '30 8 * * 1-5', - input: JSON.stringify({ task: 'weekday-process' }) -}); - -// One-Time — execute once at a specific time -await pool.createCron({ - cron_name: 'one-time-migration', - cron_type: 'one_time', - execution_time: '2025-06-15T14:00:00Z', - input: JSON.stringify({ task: 'migrate-data' }) -}); -``` - -### Java - -```java -JobScheduling jobScheduling = catalystApp.jobScheduling(); -JobPool pool = jobScheduling.pool(POOL_ID); - -// Every / Periodic — every 10 minutes -JSONObject periodicCron = new JSONObject(); -periodicCron.put("cron_name", "periodic-sync"); -periodicCron.put("cron_type", "periodic"); -periodicCron.put("every", new JSONObject().put("minutes", 10)); -periodicCron.put("input", "{\"task\":\"sync-data\"}"); -pool.createCron(periodicCron); - -// Daily — at 9:00 AM -JSONObject dailyCron = new JSONObject(); -dailyCron.put("cron_name", "daily-report"); -dailyCron.put("cron_type", "daily"); -dailyCron.put("time", "09:00"); -dailyCron.put("input", "{\"task\":\"generate-report\"}"); -pool.createCron(dailyCron); - -// Monthly — 1st of every month at midnight -JSONObject monthlyCron = new JSONObject(); -monthlyCron.put("cron_name", "monthly-cleanup"); -monthlyCron.put("cron_type", "monthly"); -monthlyCron.put("day_of_month", 1); -monthlyCron.put("time", "00:00"); -monthlyCron.put("input", "{\"task\":\"cleanup\"}"); -pool.createCron(monthlyCron); - -// Expression — custom cron expression -JSONObject exprCron = new JSONObject(); -exprCron.put("cron_name", "weekday-task"); -exprCron.put("cron_type", "expression"); -exprCron.put("cron_expression", "30 8 * * 1-5"); -exprCron.put("input", "{\"task\":\"weekday-process\"}"); -pool.createCron(exprCron); - -// One-Time -JSONObject oneTimeCron = new JSONObject(); -oneTimeCron.put("cron_name", "one-time-migration"); -oneTimeCron.put("cron_type", "one_time"); -oneTimeCron.put("execution_time", "2025-06-15T14:00:00Z"); -oneTimeCron.put("input", "{\"task\":\"migrate-data\"}"); -pool.createCron(oneTimeCron); -``` - -### Python - -```python -job_scheduling = catalyst_app.job_scheduling() -pool = job_scheduling.pool(POOL_ID) - -# Every / Periodic — every 10 minutes -pool.create_cron({ - "cron_name": "periodic-sync", - "cron_type": "periodic", - "every": {"minutes": 10}, - "input": '{"task": "sync-data"}' -}) - -# Daily — at 9:00 AM -pool.create_cron({ - "cron_name": "daily-report", - "cron_type": "daily", - "time": "09:00", - "input": '{"task": "generate-report"}' -}) - -# Monthly — 1st of every month at midnight -pool.create_cron({ - "cron_name": "monthly-cleanup", - "cron_type": "monthly", - "day_of_month": 1, - "time": "00:00", - "input": '{"task": "cleanup"}' -}) - -# Expression — custom cron expression -pool.create_cron({ - "cron_name": "weekday-task", - "cron_type": "expression", - "cron_expression": "30 8 * * 1-5", - "input": '{"task": "weekday-process"}' -}) - -# One-Time -pool.create_cron({ - "cron_name": "one-time-migration", - "cron_type": "one_time", - "execution_time": "2025-06-15T14:00:00Z", - "input": '{"task": "migrate-data"}' -}) -``` - ---- - -## Cron Management - -### Edit -- Pre-defined crons can be edited in the console (schedule, input, name) -- Dynamic crons must be updated via the SDK - -### Enable / Disable -- Toggle cron active state without deleting -- Disabled crons retain their configuration but do not submit jobs - -### Execution History -- **Development**: Last **15 days** of execution history retained -- **Production**: Last **30 days** of execution history retained -- History shows: execution time, status, duration, input/output - -### Delete -- Permanently removes the cron and its history -- Active jobs submitted by a deleted cron continue to execute - ---- - -## Application Alerts - -Configure automatic email alerts for job pool failures. - -### Function Pool Triggers -- Job execution failure -- Job timeout -- Code exception in the job function - -### Circuit / AppSail / Webhook Pool Triggers -- Target invocation failure -- Target timeout -- HTTP error responses (for Webhook/AppSail targets) - -### Alert Configuration -- Set recipients (email addresses) -- Configure frequency (every failure, or batched) -- Available in Console → DevOps → Application Alerts or from the job pool details page - ---- - -## Dashboard Metrics - -The Job Scheduling dashboard shows: -- Total jobs submitted, succeeded, failed, timed out -- Job execution duration distribution -- Pool utilization (active slots vs. total capacity) -- Cron execution success/failure rates -- Queue depth and wait times - ---- - -## REST API Endpoints - -### Job Pool APIs - -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/server/jobscheduling/pool` | List all job pools | -| GET | `/server/jobscheduling/pool/{pool_id}` | Get pool details | -| POST | `/server/jobscheduling/pool` | Create a job pool | -| PUT | `/server/jobscheduling/pool/{pool_id}` | Update a job pool | -| DELETE | `/server/jobscheduling/pool/{pool_id}` | Delete a job pool | - -### Job APIs - -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/server/jobscheduling/pool/{pool_id}/job` | List jobs in a pool | -| GET | `/server/jobscheduling/pool/{pool_id}/job/{job_id}` | Get job details | -| POST | `/server/jobscheduling/pool/{pool_id}/job` | Submit a job | -| DELETE | `/server/jobscheduling/pool/{pool_id}/job/{job_id}` | Cancel a job | - -### Cron APIs - -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/server/jobscheduling/pool/{pool_id}/cron` | List crons in a pool | -| GET | `/server/jobscheduling/pool/{pool_id}/cron/{cron_id}` | Get cron details | -| POST | `/server/jobscheduling/pool/{pool_id}/cron` | Create a cron | -| PUT | `/server/jobscheduling/pool/{pool_id}/cron/{cron_id}` | Update a cron | -| DELETE | `/server/jobscheduling/pool/{pool_id}/cron/{cron_id}` | Delete a cron | -| PUT | `/server/jobscheduling/pool/{pool_id}/cron/{cron_id}/enable` | Enable a cron | -| PUT | `/server/jobscheduling/pool/{pool_id}/cron/{cron_id}/disable` | Disable a cron | - ---- - -## Limits Summary - -| Resource | Limit | -|----------|-------| -| Job pools per project | Varies by plan | -| Memory per pool | 10 GB max | -| Parallel triggers per pool | 10 | -| Minimum cron interval | 1 minute | -| Execution history (dev) | 15 days | -| Execution history (prod) | 30 days | -| Pool type | Immutable after creation | -| Dynamic cron migration | Does NOT migrate to production | -| Pre-defined cron migration | Migrates to production on deploy | diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/meta-ids.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/meta-ids.md deleted file mode 100644 index 496914b8d..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/meta-ids.md +++ /dev/null @@ -1,362 +0,0 @@ -# Catalyst Meta IDs: Complete Reference - -Every Catalyst component uses unique identifiers. This reference explains what each ID is, where to -find it, and when you need it. When writing code that references any of these IDs, always tell the -user where to find the value — never leave a placeholder unexplained. - ---- - -## Table of Contents -1. [Project-Level IDs](#project-level-ids) -2. [Environment & Authentication IDs](#environment--authentication-ids) -3. [Data & Storage IDs](#data--storage-ids) -4. [Function & Compute IDs](#function--compute-ids) -5. [Service IDs](#service-ids) -6. [User & Identity IDs](#user--identity-ids) -7. [Quick Lookup Table](#quick-lookup-table) -8. [Common Pitfalls](#common-pitfalls) - ---- - -## Project-Level IDs - -### Project ID -- **What:** Unique identifier for your Catalyst project, auto-generated at creation. -- **Where to find:** - - **Console:** Settings → Project Settings → General. Displayed under the project name. - - **CLI:** Run `catalyst projects:list` — shows a table of project names and IDs. - - **File:** `.catalystrc` at project root (field `project_id`). -- **When you need it:** API calls, CLI operations, constructing function invocation URLs. -- **Format:** Numeric string, e.g. `"123456789"`. -- **Important:** Do NOT modify `project_id` in `.catalystrc` manually. Use `catalyst project:use` - to switch projects. - -```json -// .catalystrc (auto-generated by `catalyst init`) -{ - "project_id": "123456789", - "project_domain": "myapp-60019947973.development", - "env_id": "60019947973", - "timezone": "Asia/Kolkata" -} -``` - -### Project Domain Name -- **What:** Unique domain name generated when you host a web client (e.g. `shipmenttracking-57673975`). -- **Where to find:** Console → Settings → Environments → General tab → Application URL. Also visible - in Web Client Hosting. -- **Format:** `{project-name}-{numeric-suffix}`. -- **Used in:** - - Development URL: `https://{domain}.development.catalystserverless.com` - - Production URL: `https://{domain}.catalystserverless.com` - ---- - -## Environment & Authentication IDs - -### ZAID (Zoho Application ID) -- **What:** Unique portal ID that maps your application to a project and environment. **Critical:** - the ZAID is **different** in Development and Production environments. -- **Where to find:** - - **Console:** Settings → Environments → General tab. Both Dev and Prod ZAIDs shown here. - - **Social Login pop-up:** Also displayed in the social login configuration pop-up. - - **Web SDK init:** Auto-populated by `/__catalyst/sdk/init.js` when hosted on Catalyst. -- **When you need it:** - - User registration via SDK (`signupConfig.zaid`) - - Social login configuration (redirect URIs include ZAID) - - Password reset via SDK - - Constructing social login callback URIs: - `https://{appdomain}/accounts/pfs/{zaid}/clientidpcallback` -- **Format:** Numeric string, e.g. `"1011958529"`. -- **Type:** Always pass ZAID as a string, even though it appears numeric. JavaScript loses - precision on integers larger than 2^53, and Catalyst IDs can exceed this threshold. -- **Critical gotcha:** When migrating to production, you MUST reconfigure social logins with the - **Production ZAID** and production app domain. Forgetting this is a common cause of auth failures - in production. - -```javascript -// Node.js — User registration requires ZAID -const signupConfig = { - platform_type: 'web', - zaid: '10014774358' // ← Get from Settings → Environments (always a string) -}; -const userConfig = { - last_name: 'Burrows', - email_id: 'emma@example.com' -}; -let userManagement = catalystApp.userManagement(); -await userManagement.registerUser(signupConfig, userConfig); -``` - -```python -# Python — User registration requires ZAID -signup_config = { - "platform_type": "web", - "zaid": "81008807534807534" # ← Get from Settings → Environments -} -user_details = { - "first_name": "Amelia", - "last_name": "Burrows", - "email_id": "amelia@example.com" -} -authentication_service = app.authentication() -response = authentication_service.register_user(signup_config, user_details) -``` - -### API Key (API Gateway) -- **What:** Authentication key for the API Gateway feature. -- **Where to find:** Console → Settings → Environments → General tab. -- **Scope:** Common across all projects in Development, but **unique per project** in Production. -- **When you need it:** API Gateway requests requiring key-based authentication. - ---- - -## Data & Storage IDs - -### Table ID -- **What:** Unique ID auto-generated when you create a Data Store table. -- **Where to find:** Console → Cloud Scale → Data Store → click on a table. The Table ID is displayed - under the table name. -- **When you need it:** SDK calls that reference tables by ID instead of name. -- **Format:** Numeric, e.g. `1510000000110121`. -- **Note:** You can use either Table ID or Table Name in SDK calls. Name is more readable but - case-sensitive. ID is safer for avoiding case-sensitivity issues. - -```javascript -// Access table by name (case-sensitive — must match console exactly) -const table = catalystApp.datastore().table('Employees'); - -// Access table by ID (from Console → Data Store → click table) -const table = catalystApp.datastore().table(1510000000110121); -``` - -### ROWID -- **What:** Auto-increment unique row identifier, system column in every Data Store table. -- **Where to find:** Returned in query results, visible in Data Store console when viewing rows. -- **When you need it:** Update, delete, and get operations all require ROWID. Pagination uses ROWID. -- **Format:** BigInt, e.g. `"12345"`. -- **Important:** ROWID is auto-managed. Never set it manually on insert — it's assigned by Catalyst. - -### Column ID -- **What:** Each column in a Data Store table has a unique ID. -- **Where to find:** Console → Data Store → click table → column details. -- **When you need it:** Rarely needed directly; most SDK calls use column names. - -### Folder ID (File Store) — DEPRECATED SERVICE -- **What:** Unique ID for a File Store folder. -- **Where to find:** Console → Cloud Scale → File Store → click folder. ID shown in folder details. -- **When you need it:** All File Store SDK calls (`fileStore.folder(FOLDER_ID)`). -- **Note:** File Store is deprecated (removal date TBD). Use Stratus for new projects. - -```javascript -// File Store (deprecated) — Folder ID from Console → File Store → folder details -const folder = catalystApp.filestore().folder(FOLDER_ID); -``` - -### Bucket Name (Stratus) -- **What:** Name of a Stratus object storage bucket (not a numeric ID — uses string names). -- **Where to find:** Console → Cloud Scale → Stratus → bucket list. -- **When you need it:** All Stratus SDK calls. - -```javascript -// Stratus — bucket name from Console → Stratus -const bucket = catalystApp.stratus().bucket('my-bucket'); -``` - -### Segment ID (Cache) -- **What:** Unique ID for a Cache segment. -- **Where to find:** Console → Cloud Scale → Cache → segment list. ID shown next to segment name. -- **When you need it:** All Cache SDK calls (`cache.segment(SEGMENT_ID)`). -- **Format:** Numeric. - -```javascript -// Cache — Segment ID from Console → Cache -const segment = catalystApp.cache().segment(SEGMENT_ID); -``` - -### Collection Name (NoSQL) -- **What:** Name of a NoSQL document collection (string, not numeric ID). -- **Where to find:** Console → Cloud Scale → NoSQL → collection list. -- **When you need it:** All NoSQL SDK calls. - -```javascript -// NoSQL — collection name from Console → NoSQL -const collection = catalystApp.nosql().collection('UserProfiles'); -``` - ---- - -## Function & Compute IDs - -### Function ID / Function Name -- **What:** Each function has a unique numeric ID and a name (the directory name). -- **Where to find:** Console → Serverless → Functions → click function. ID shown in function details. -- **When you need it:** REST API calls to invoke functions. CLI operations. -- **Invocation URLs:** - - Basic I/O: `GET /server/{function_name}/execute?args={input}` - - Advanced I/O: `ANY /server/{function_name}/{path}` - - Production Basic I/O: `https://{domain}.catalystserverless.com/baas/v1/project/{project_id}/function/{function_name}/execute` - - Production Advanced I/O: `https://{domain}.catalystserverless.com/server/{function_name}/` - -### AppSail App ID -- **What:** Unique ID for an AppSail application. -- **Where to find:** Console → Serverless → AppSail → app details. -- **When you need it:** CLI deploy commands, API operations. - ---- - -## Service IDs - -### Circuit ID -- **What:** Unique ID for a Circuits workflow. -- **Where to find:** Console → Serverless → Circuits → click circuit. ID shown in circuit details. -- **When you need it:** SDK calls to execute circuits, REST API invocations. - -```javascript -// Circuits — Circuit ID from Console → Circuits → circuit details -const circuit = catalystApp.circuit(); -const result = await circuit.execute(CIRCUIT_ID, { inputKey: 'value' }); - -// REST API: POST /server/circuit/{circuit_id}/execute -``` - -### Job Pool ID (Job Scheduling) -- **What:** Unique ID for a job pool. -- **Where to find:** Console → Job Scheduling → pool list. ID shown in pool details. -- **When you need it:** SDK calls to submit jobs to a pool. - -```javascript -// Job Scheduling — Pool ID from Console → Job Scheduling → pool details -const pool = catalystApp.jobScheduling().pool(POOL_ID); -await pool.submitJob({ input: JSON.stringify({ taskType: 'report' }) }); -``` - -### Bot ID (ConvoKraft) -- **What:** Unique ID for a ConvoKraft conversational bot. -- **Where to find:** Console → ConvoKraft → bot list. ID shown in bot details. -- **When you need it:** Embedding bots in web applications via JavaScript SDK. - -```html - - - -``` - -### Signal/Event IDs -- **What:** IDs for Signals publishers, subscribers, and event routes. -- **Where to find:** Console → Signals → respective component details. - -### Pipeline ID -- **What:** Unique ID for a CI/CD pipeline. -- **Where to find:** Console → Pipelines → pipeline details. - ---- - -## User & Identity IDs - -### ZUID (Zoho User ID) -- **What:** Unique identification of a Zoho user account, specific to each application. - A user gets a different ZUID for each Catalyst application they sign up for. -- **Where to find:** Returned in user registration/login API responses. Also visible in - Console → Authentication → Users → click user. -- **When you need it:** User-specific operations, API calls scoped to a user. -- **Format:** Numeric string, e.g. `"1005641290"`. - -### User ID -- **What:** Unique identification of an end-user, limited to Catalyst (not applicable to - other Zoho services). Auto-created on sign-up. -- **Where to find:** Returned in API responses. Console → Authentication → Users. -- **When you need it:** SDK calls like `getUserDetails(USER_ID)`, `deleteUser(USER_ID)`, - push notifications to specific users. -- **Format:** Numeric, e.g. `"2305000000007752"`. - -### Org ID / ZAAID (Organization ID) -- **What:** Unique identification of the organization an end-user belongs to. Generated when - a user is added through the Add User API or console. If not specified, Catalyst auto-generates one. -- **Where to find:** Returned in user registration API responses. Console → Authentication → Users - → user details. -- **When you need it:** - - Adding users to an existing organization (`addUserToOrg()` method) - - Multi-org setups where users belong to different organizations -- **Format:** Numeric string, e.g. `"1005641456"`. -- **Important:** An organization cannot be changed once associated with a user account. If a user - is added by another existing user, they inherit the same Org ID. - -```javascript -// Adding user to existing org — requires ZAAID/Org ID -const signupConfig = { platform_type: 'web', zaid: '10014774358' }; -const userConfig = { - last_name: 'Burrows', - email_id: 'emma@example.com', - zaaid: '20051993711' // ← Org ID of existing organization (distinct from ZAID) -}; -await userManagement.addUserToOrg(signupConfig, userConfig); -``` - -### Role ID -- **What:** ID of a user role (e.g., App Admin, App User, or custom roles). -- **Where to find:** Console → Authentication → Roles section. Each role shows its ID. -- **When you need it:** Assigning roles during user registration. -- **Format:** Numeric, e.g. `"2305000000006024"`. - ---- - -## Quick Lookup Table - -| ID | What | Where to Find | Format | -|---|---|---|---| -| **Project ID** | Project identifier | Settings → General; `.catalystrc`; `catalyst projects:list` | Numeric string | -| **ZAID** | App-to-environment mapping | Settings → Environments → General tab | Numeric (differs dev/prod!) | -| **API Key** | API Gateway auth key | Settings → Environments → General tab | String | -| **Table ID** | Data Store table | Cloud Scale → Data Store → click table | Numeric | -| **ROWID** | Data Store row | Auto-assigned; returned in queries | BigInt | -| **Folder ID** | File Store folder (deprecated) | Cloud Scale → File Store → folder details | Numeric | -| **Segment ID** | Cache segment | Cloud Scale → Cache → segment list | Numeric | -| **Function ID** | Serverless function | Serverless → Functions → function details | Numeric | -| **Circuit ID** | Circuits workflow | Serverless → Circuits → circuit details | Numeric | -| **Pool ID** | Job Scheduling pool | Job Scheduling → pool details | Numeric | -| **Bot ID** | ConvoKraft bot | ConvoKraft → bot details | String | -| **ZUID** | Zoho user (per-app) | Auth API responses; Authentication → Users | Numeric string | -| **User ID** | Catalyst-only user ID | Auth API responses; Authentication → Users | Numeric | -| **Org ID / ZAAID** | Organization | Auth API responses; Authentication → Users | Numeric string | -| **Role ID** | User role | Authentication → Roles section | Numeric | -| **Bucket Name** | Stratus bucket | Cloud Scale → Stratus | String | -| **Collection Name** | NoSQL collection | Cloud Scale → NoSQL | String | -| **Domain Name** | Project domain | Settings → Environments; Web Client Hosting | String | - ---- - -## Common Pitfalls - -1. **ZAID differs between Development and Production.** This is the #1 source of auth issues when - deploying to production. Always reconfigure social logins, redirect URIs, and any hardcoded - ZAID references with the Production ZAID after deployment. - -2. **The Web SDK `init.js` auto-populates ZAID.** When using `/__catalyst/sdk/init.js`, the ZAID - is injected automatically based on the environment. You do NOT need to hardcode it. But if - you're using the SDK programmatically (user registration, password reset), you must pass the - correct ZAID yourself. - -3. **Table names are case-sensitive.** When accessing tables by name, the string must exactly match - what's in the console. Using the numeric Table ID avoids this issue entirely. - -4. **Org ID is permanent.** Once a user is associated with an organization, it cannot be changed. - Plan your multi-org strategy before adding users. - -5. **25-user limit in Development.** You can only add 25 users in the development environment. - After deploying to production, there's no limit. - -6. **Production URLs omit "development" in the path.** Dev URL contains `.development.catalystserverless.com`, - production URL is just `.catalystserverless.com`. Any hardcoded URLs must be updated. - -7. **Don't leave ID placeholders unexplained.** When writing code for the user, always add a comment - explaining where to find each ID value. Example: - ```javascript - // Get TABLE_ID from Console → Cloud Scale → Data Store → click your table - const table = catalystApp.datastore().table(TABLE_ID); - ``` diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/observability.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/observability.md deleted file mode 100644 index 715c3d15b..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/observability.md +++ /dev/null @@ -1,165 +0,0 @@ -# Catalyst Observability & Monitoring - -## When to use this file -Load this file when the user asks about: Catalyst Logs, APM, Application Alerts, Audit Logs, -monitoring after deployment, performance debugging, memory optimization, or setting up failure -notifications for cron jobs or event listeners. - ---- - -## DevOps Components Overview - -Catalyst DevOps provides tools to operate your application at scale, monitor it post-deployment, -and perform iterative testing and quality checks. All DevOps components are accessible from the -unified Catalyst console. - ---- - -## Logs - -**What it provides:** -- Logs of **all function executions** across your project. -- Includes: log levels, responses, statuses, execution details, and exceptions. -- Access: **DevOps → Logs** in the Catalyst console. - -**Key behaviors:** -- **Time zone**: configurable per component; defaults to the project's configured time zone. - The project time zone is set in Settings → General Settings and applies across all components. -- **Auto-filtering**: when you access Logs from a specific function page, AppSail instance, or - Circuit execution, the filters are automatically applied to show only that component's logs. -- **Circuit logs**: each Circuit execution log provides links to individual function logs and - to the Catalyst Logs component for the specific function that was executed. -- **AppSail logs**: accessed via AppSail → Instances → click the Logs icon, which redirects - to Catalyst Logs with the app's filter applied. - -**Exporting logs:** -- Available from Settings → Audit Logs → Export Logs. -- Choose Console Logs, Application Logs, or both. -- Catalyst sends a notification when the export is ready; download via the provided link. - ---- - -## Application Performance Monitoring (APM) - -**What it provides:** -- In-depth stats and performance reports of function executions. -- Helps identify and resolve bugs and performance bottlenecks. -- Access: **DevOps → APM** in the Catalyst console. - -**Available for:** -- Basic I/O functions -- Advanced I/O functions -- Event functions -- Cron functions -- Browser Logic functions -- Job functions - -**Key metrics shown:** -- Average response time -- Execution duration distribution -- Memory usage -- Max Instances and number of requests vs time (AppSail) - -**Memory optimization workflow using APM:** -1. Start with the lowest memory setting (128 MB for functions). -2. Deploy and execute under realistic load. -3. Check APM for average response time and error patterns. -4. Increase memory if response time is too high or executions are failing. -5. Repeat until you find the optimal balance of cost and performance. -6. APM and Catalyst Logs together are the primary tools for this process. - ---- - -## Application Alerts - -**What it provides:** -- Automatic email alerts when a Catalyst component encounters **failure**, **code exception**, - or **timeout**. -- Access: **DevOps → Application Alerts**, or configure directly from the component's details page. - -**Supported components:** -- Cron jobs -- Event Listeners -- Functions - -**How to configure (shortcut):** -- Alerts can be configured directly from the **Cron details page** or **Event Listeners rules - section** — no need to navigate to the Application Alerts component separately. -- From Cron: click **+Configure** in the cron's details section. -- From Event Listeners: click the configure option in the Event Listeners rules section. - -**Critical behavior — Cron auto-disable:** -- Third-party URL crons are automatically **disabled after 50 consecutive failures**. -- Cron functions (not URL-based) are NOT auto-disabled regardless of failure count. -- Application Alerts catch these failures early, before the cron is auto-disabled. -- After fixing the issue, re-enable the cron from the console. - -**Agent guidance — proactively suggest alerts:** -After helping a user deploy cron jobs or set up event listeners, always suggest: -> "Would you like me to help configure Application Alerts for this? Catalyst can email you -> automatically when the cron fails, times out, or throws an exception — directly from the -> cron details page." - ---- - -## Audit Logs - -**What it provides:** -- A record of all activities across your Catalyst account and projects. -- Access: **Settings → Audit Logs**. - -**Two types:** -1. **Console Logs** — activities performed in console components (configuration changes, - resource creation/deletion, permission changes). -2. **Application Logs** — activities at the application level (user actions, data operations - through the application). - -**Permission requirements:** -- Access to Audit Logs requires the "Access Audit Logs" permission in the user's profile. -- If choosing to provide Audit Logs access, you must first provide permissions to Data Store, - File Store, Event Listeners, and Other Components (since Audit Logs shows their configurations). - -**Exporting:** -- Exportable from the Audit Logs page: choose Console Logs, Application Logs, or both. -- A notification is sent when the export is complete. -- The download link for the last exported logs is always shown in the Export Logs popup. - ---- - -## When agents should check monitoring - -| After this action | Check this | -|-------------------|-----------| -| Deploying any function | DevOps → Logs (filter by function name, check for errors on first invocation) | -| Deploying AppSail | AppSail → Instances → Logs icon (verify app started, check cold start logs) | -| Running a Circuit | Circuits → Execution History → click execution → View Logs | -| Setting up a Cron job | DevOps → Application Alerts (configure failure notifications immediately) | -| Reporting performance issues | DevOps → APM (compare response times, identify slow functions) | -| Debugging auth issues | DevOps → Logs (look for 401/403 errors, ZAID mismatches) | -| Investigating data errors | DevOps → Logs (look for ZCQL errors, DataStore permission errors) | -| Security / compliance audit | Settings → Audit Logs (Console Logs + Application Logs) | - ---- - -## Accessing logs from specific components - -Instead of navigating to DevOps → Logs manually, you can access pre-filtered logs directly: - -| Component | How to access its logs | -|-----------|------------------------| -| A function | Functions → select function → click Logs icon | -| An AppSail instance | AppSail → Instances → click Logs icon next to the instance | -| A Circuit execution | Circuits → Execution History → click execution → View Logs | -| A function called inside a Circuit | Circuit execution log → click the function link | -| A Cron job execution | Cron → Details → Execution History (also links to Catalyst Logs) | - ---- - -## Summary: DevOps component quick reference - -| Component | Access path | Primary use | -|-----------|-------------|-------------| -| **Logs** | DevOps → Logs | View all function execution logs, debug errors | -| **APM** | DevOps → APM | Performance reports, memory optimization | -| **Application Alerts** | DevOps → Application Alerts | Email alerts on failure/timeout/exception | -| **Audit Logs** | Settings → Audit Logs | Account-level activity trail, compliance | diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/pricing.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/pricing.md deleted file mode 100644 index 6aecd91fe..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/pricing.md +++ /dev/null @@ -1,480 +0,0 @@ -# Catalyst Pricing Reference & Cost Estimation Guide - -> **Pricing data last verified: May 2026.** Catalyst pricing can change. Always verify current rates at https://catalyst.zoho.com/pricing.html before quoting estimates to clients. - -Use this reference when the user asks about Catalyst pricing, cost estimation, or wants to generate -a pricing spreadsheet for their project. This file contains the complete pricing data and instructions -for building dynamic Excel pricing models. - ---- - -## Table of Contents -1. [Pricing Model Overview](#pricing-model-overview) -2. [Complete Pricing Table](#complete-pricing-table) -3. [Free Tier Limits](#free-tier-limits) -4. [Billing Rules](#billing-rules) -5. [GB-Seconds Calculation](#gb-seconds-calculation) -6. [Generating a Pricing Spreadsheet](#generating-a-pricing-spreadsheet) - ---- - -## Pricing Model Overview - -Catalyst uses **pay-per-use** pricing. Key facts: -- **Free tier:** Generous monthly limits that renew every month, applied at account level (across all projects). -- **Free trial:** New customers get $250 USD wallet credits valid for 180 days. -- **Minimum billing:** $5/project/month once any free tier limit is exceeded. -- **No upfront commitments.** Cancel anytime. -- **Subscription option** also available for predictable billing — details at - https://www.zohowebstatic.com/sites/zweb/images/catalyst/subscription-plan.pdf -- Invoicing currency matches the credit card on file. - ---- - -## Complete Pricing Table - -All prices listed below are in **USD**. See the [Currency & Regional Pricing](#currency--regional-pricing) -section at the bottom for INR and other currency guidance. - -### Serverless - -| Component | Operation | Unit Price (USD) | Unit | -|---|---|---|---| -| Functions | Execution | 0.000016 | per GB-second | -| Circuits | State transition | 0.00002 | per transition | -| AppSail | Runtime | 0.08 | per GB-hour | - -### Backend — Data Store - -| Operation | Unit Price (USD) | Unit | -|---|---|---| -| SELECT | 0.00006 | per request | -| INSERT | 0.0001 | per request | -| UPDATE | 0.00008 | per request | -| DELETE | 0.00008 | per request | -| Storage | 0.02 | per GB/month | - -### Backend — Cache - -| Operation | Unit Price (USD) | Unit | -|---|---|---| -| GET | 0.00004 | per request | -| PUT | 0.00006 | per request | -| UPDATE | 0.00006 | per request | - -### Backend — File Store (deprecated, removal date TBD) - -| Operation | Unit Price (USD) | Unit | -|---|---|---| -| Upload | 0.000005 | per request | -| Download | 0.0000004 | per request | -| Storage | 0.02 | per GB/month | - -### Backend — Stratus (Object Storage) - -> **Note:** Stratus has its own pricing structure. The values below are estimates — verify current Stratus rates at https://catalyst.zoho.com/pricing.html before using in client-facing estimates. -| Operation | Unit Price (USD) | Unit | -|---|---|---| -| Upload | 0.000005 | per request | -| Download | 0.0000004 | per request | -| Storage | 0.02 | per GB/month | - -### Backend — Other - -| Component | Unit Price (USD) | Unit | -|---|---|---| -| API Gateway | 0.000001 | per request | -| Web Client Hosting | 0.0000004 | per request | -| Mail | 0.001 | per email | -| Push Notifications | 0.00002 | per notification | -| Search | 0.00004 | per query | - -### SmartBrowz - -| Operation | Unit Price (USD) | Unit | -|---|---|---| -| Headless Browser | 0.1188 | per hour | -| PDF Conversions | 0.0033 | per PDF | -| BrowserLogic | 0.000002 | per invocation | - -### DevOps - -| Operation | Unit Price (USD) | Unit | -|---|---|---| -| APM | 0.00002 | per request | -| Automation Testing | 0.005 | per test case | -| Application Alerts | 0.0005 | per alert | -| Pipeline Build & Deploy | 0.00016 | per GB-second | - -### AI/ML — Zia APIs - -| Operation | Unit Price (USD) | Unit | -|---|---|---| -| Face Analytics | 0.001 | per request | -| Image Moderation | 0.001 | per request | -| OCR | 0.001 | per request | -| Object Recognition | 0.001 | per request | -| Barcode Scanner | 0.001 | per request | -| AutoML Prediction | 0.001 | per request | -| Text Analytics | 0.001 | per request | - -### AI/ML — ConvoKraft - -| Operation | Unit Price (USD) | Unit | -|---|---|---| -| Messages | 0.0006 | per message | - -### AI/ML — QuickML - -| Operation | Unit Price (USD) | Unit | -|---|---|---| -| Model Inference (0–25K) | 0.0025 | per API call | -| Model Inference (25K–100K) | 0.002 | per API call | -| Model Inference (>100K) | 0.001 | per API call | -| Data Storage | 0.00003 | per GB/hour | -| Single Prediction | 0.0005 | per call | -| Bulk Prediction | 0.0025 | per 100 records | -| LLM Input Tokens | 0.2 | per million tokens | -| LLM Output Tokens | 0.4 | per million tokens | -| VLM Input Tokens | 0.8 | per million tokens | -| VLM Output Tokens | 1.2 | per million tokens | - -### Slate - -| Operation | Unit Price (USD) | Unit | -|---|---|---| -| Build & Deploy (2vCPU 4GB) | 0.00016 | per GB-second | -| Hosting Storage (CDN) | 0.0001 | per MB | -| Functions invocations | 0.000016 | per GB-second | -| Requests (CDN & Origin) | 0.000004 | per request | -| ISR Read | 0.0000003 | per request | -| ISR Write | 0.000003 | per request | - ---- - -## Free Tier Limits - -These are monthly limits applied at the **account level** (across all projects). Renew every month. - -### Serverless -| Component | Free Allowance | -|---|---| -| Functions | 25,000 GB-seconds | -| Circuits | 2,000 state transitions | -| AppSail | 15 GB-hours | - -### Backend — Data Store -| Operation | Free Allowance | -|---|---| -| SELECT | 10,000 requests | -| INSERT | 5,000 requests | -| UPDATE | 1,000 requests | -| DELETE | 1,000 requests | -| Storage | 5 GB | - -### Backend — Cache -| Operation | Free Allowance | -|---|---| -| GET | 1,000 requests | -| PUT | 5,000 requests | -| UPDATE | 5,000 requests | - -### Backend — File Store / Stratus -| Operation | Free Allowance | -|---|---| -| Upload | 2,000 requests | -| Download | 10,000 requests | -| Storage | 5 GB | - -### Backend — Other -| Component | Free Allowance | -|---|---| -| API Gateway | 100,000 requests | -| Web Client Hosting | 300,000 requests | -| Mail | 100 emails | -| Push Notifications | 500 notifications | -| Search | 1,000 queries | - -### SmartBrowz -| Operation | Free Allowance | -|---|---| -| Headless | 5 hours | -| PDF Conversions | 50 PDFs | -| BrowserLogic | 25,000 GB-seconds | - -### DevOps -| Operation | Free Allowance | -|---|---| -| APM | 5,000 requests | -| Automation Testing | 100 test cases | -| Application Alerts | 100 alerts | -| Pipeline Build & Deploy | 72,000 GB-seconds | - -### AI/ML — Zia APIs -All Zia APIs combined: 100 calls free (shared across all Zia services) - -### AI/ML — ConvoKraft -| Component | Free Allowance | -|---|---| -| Messages | 1,000 messages | - -### AI/ML — QuickML -| Operation | Free Allowance | -|---|---| -| Model Inference | 500 API calls | -| Data Storage | 1 GB | - -### Slate -| Operation | Free Allowance | -|---|---| -| Build & Deploy | 72,000 GB-seconds | -| Hosting Storage | 500 MB | -| Functions | 25,000 GB-seconds | -| CDN & Origin Requests | 300,000 requests | -| ISR Read | 100,000 requests | -| ISR Write | 50,000 requests | - ---- - -## Billing Rules - -1. **Free tier is account-wide**, not per-project. If you have 3 projects, the free tier is shared - across all three. -2. **Minimum billing of $5/project/month** kicks in once ANY free tier limit is exceeded. This $5 - is not additional — it's the floor. If your usage costs $7 across 2 projects, you pay $7 + $5 - (minimum for the second project) = $12. -3. **Billable = Max(0, Usage - Free Tier)**. Cost = Billable × Unit Price. -4. **GB-seconds** is the billing unit for functions — see calculation below. -5. **Free trial credits ($250)** are applied against invoices. Once exhausted or expired (180 days), - normal billing begins. - ---- - -## GB-Seconds Calculation - -Functions and Slate are billed in GB-seconds: - -``` -GB-seconds = (Memory in MB / 1024) × Execution Time in Seconds × Number of Invocations -``` - -Example: 500 invocations of a function with 512MB memory running for 2 seconds each: -``` -GB-seconds = (512/1024) × 2 × 500 = 500 GB-seconds -Cost = 500 × $0.000016 = $0.008 -``` - -The free tier of 25,000 GB-seconds is shared across ALL function executions regardless of memory size. - -### Cost per invocation by memory tier - -The base rate is $0.000016 per GB-second. Since different functions use different memory sizes, -here's what a **single 1-second invocation** costs at each memory tier: - -| Memory (MB) | GB fraction | Cost per 1-sec invocation (USD) | 10K invocations/month (USD) | 100K invocations/month (USD) | -|-------------|-------------|-------------------------------|----------------------------|------------------------------| -| 128 | 0.125 | $0.000002 | $0.02 | $0.20 | -| 256 | 0.250 | $0.000004 | $0.04 | $0.40 | -| 384 | 0.375 | $0.000006 | $0.06 | $0.60 | -| 512 | 0.500 | $0.000008 | $0.08 | $0.80 | -| 640 | 0.625 | $0.000010 | $0.10 | $1.00 | -| 768 | 0.750 | $0.000012 | $0.12 | $1.20 | -| 896 | 0.875 | $0.000014 | $0.14 | $1.40 | -| 1024 | 1.000 | $0.000016 | $0.16 | $1.60 | - -**Formula:** `Cost per invocation = (Memory MB / 1024) × Execution seconds × $0.000016` - -**Key insight:** A 128MB function is **8× cheaper per second** than a 1024MB function. Choose the -smallest memory that doesn't cause timeouts — this has a significant impact at scale. - -> **Example:** An app makes 50,000 API calls/month, each function runs for 1.5 seconds: -> - At 1024MB: 50,000 × 1.5 × 1.0 = 75,000 GB-sec → (75,000 - 25,000 free) × $0.000016 = **$0.80/month** -> - At 256MB: 50,000 × 1.5 × 0.25 = 18,750 GB-sec → **$0.00/month** (within free tier!) - -When building pricing estimates, always ask about the function's configured memory size — defaulting -to 1024MB will significantly overestimate costs for most workloads. - ---- - -## Generating a Pricing Spreadsheet - -When a user asks to estimate Catalyst costs or generate a pricing sheet, follow this workflow: - -### Step 1: Gather Requirements - -Understand the user's application and map it to Catalyst components. Ask about: -- **What the app does** (data processing, web app, API backend, ML, etc.) -- **Data volumes** (rows, records, files, sizes) -- **Operation patterns** (reads vs writes, batch vs real-time) -- **User/traffic scale** (requests/day, concurrent users, growth projections) -- **Compute needs** (function count, execution time, memory) -- **Storage needs** (DB size, file storage, cache) -- **AI/ML usage** (OCR, predictions, chatbots) -- **Growth trajectory** (1-year, 3-year, scaling assumptions) - -### Step 2: Map to Catalyst Components - -Using the user's requirements, identify which Catalyst components are needed and estimate usage. -Use the relevant equivalents file (`references/equivalents-aws.md`, `references/equivalents-gcp.md`, etc.) to translate if they describe needs in terms of another platform. - -Common patterns: -- **Data processing pipeline** → Job Scheduling + Data Store + Stratus + Circuits -- **Web app with auth** → Slate + Functions + Data Store + Auth + Cache -- **API backend** → Functions (Advanced I/O) + Data Store + API Gateway + Cache -- **ML pipeline** → QuickML + Data Store + Functions -- **Chatbot** → ConvoKraft + Functions + Data Store - -### Step 3: Build the Excel Pricing Model - -Generate an `.xlsx` file using openpyxl (read the xlsx SKILL.md first) with these sheets: - -#### Sheet 1: "Key Assumptions" -- All editable inputs in **blue text** with **yellow background** -- Organized by category (data volume, compute, storage, etc.) -- Year 1 / Year 2 / Year 3 columns for growth scenarios -- Rationale column explaining each assumption -- Header note: "Yellow cells = editable assumptions. All downstream pricing updates automatically." -- Include a processing multiplier where applicable (each user action may trigger multiple - internal operations — e.g., 1 API call → 3 DB reads + 1 cache check + 1 log write) - -#### Sheet 2: "Pricing - Year 1" (repeat for Year 2, Year 3 if needed) -- Columns: Component/Operation | Unit Price (USD) | Free Tier/Month | Est. Monthly Usage | - Billable (Excess) | Monthly Cost (USD) | Calculation Notes -- **Unit Price**: Hardcoded from the pricing table above (black text) -- **Free Tier**: Hardcoded from the free tier table above (black text) -- **Est. Monthly Usage**: Formula referencing Key Assumptions sheet (black text) -- **Billable**: `=MAX(0, Usage - FreeTier)` formula -- **Monthly Cost**: `=Billable × UnitPrice` formula -- Group by service category with subtotals -- Grand total at bottom -- Minimum billing row: `=MAX(5, TotalCost)` (only applies if any free tier is exceeded) -- Include a cost breakdown section showing % by category - -#### Sheet 3: "3-Year Projection" -- Quarterly columns (Y1-Q1 through Y3-Q4) -- Key metrics row (suppliers, users, requests, etc.) ramping over time -- Monthly cost by component -- Quarterly cost (monthly × 3) -- Annual summary -- 3-year total -- Per-unit metrics (cost per user/month, cost per transaction, etc.) - -#### Sheet 4: "Pricing Range Summary" (optional but recommended) -- Side-by-side comparison of scenarios (conservative vs optimistic) -- Highlight the biggest cost drivers -- Per-unit metrics for client positioning -- Recommended client messaging - -### Step 4: Excel Formatting Requirements - -Follow the xlsx SKILL.md formatting standards: -- **Blue text** for editable assumptions -- **Black text** for all formulas -- **Yellow background** for assumption cells the user should change -- **Currency format**: $#,##0.00 or $#,##0 depending on magnitude -- **Percentage format**: 0.0% -- **Number format**: #,##0 with thousands separators -- All formulas must use cell references (never hardcoded values in formulas) -- Named ranges for key assumptions (makes formulas readable) -- Freeze panes for headers -- Column widths auto-adjusted or manually set for readability -- Conditional formatting for costs > threshold (red highlight for large costs) -- Print area and page setup configured - -### Step 5: Key Formulas Pattern - -``` -# For each line item: -Billable = MAX(0, EstUsage - FreeTier) -MonthlyCost = Billable * UnitPrice - -# For subtotals: -SubTotal = SUM(costs in section) - -# For grand total: -GrandTotal = SUM(all subtotals) - -# For minimum billing: -InvoiceValue = MAX(5 * NumProjects, GrandTotal) - -# For 3-year projection with growth: -MonthlyUsage_Q = BaseUsage * (1 + GrowthRate * QuarterIndex) -# or use the specific assumptions for each year -``` - -### Common Cost Estimation Pitfalls to Watch For - -1. **Processing multiplier**: A single user action often triggers multiple Catalyst operations. - E.g., one "save record" might be: 1 INSERT + 2 SELECTs (validation) + 1 Cache PUT + 1 event. - Always ask about or estimate this multiplier. - -2. **Datastore is often the dominant cost** for data-heavy applications. At scale, INSERT and - UPDATE operations at $0.0001 and $0.00008 per request add up fast with millions of rows. - Flag this explicitly in the spreadsheet. - -3. **Cache reduces Datastore costs**: Include cache hit rate as an assumption. Higher cache hit - rates significantly reduce Datastore SELECT costs. - -4. **APM costs scale with function executions**: Every function execution monitored by APM adds - $0.00002. At millions of executions, this becomes material. - -5. **Bulk vs per-row billing**: For Data Store, billing is per-request (per row operation). If - Catalyst offers bulk write APIs that bill per-job rather than per-row, costs could be - dramatically lower. Flag this uncertainty and show both scenarios if relevant. - -6. **Free tier erodes at scale**: The free tier is generous for small apps but becomes negligible - at enterprise scale. Don't over-optimize for free tier savings in large projections. - -7. **GB-seconds math**: Memory × time × invocations. Higher memory = faster execution but higher - cost per second. Find the sweet spot. - ---- - -## Quick Reference: Typical Monthly Costs by App Type - -These are rough order-of-magnitude estimates for orientation: - -| App Type | Monthly Cost Range | Primary Cost Drivers | -|---|---|---| -| Simple web app (< 1K users) | $0–$10 | Within or near free tier | -| API backend (10K req/day) | $5–$50 | Data Store, Functions, API Gateway | -| Data processing (1M rows/mo) | $50–$500 | Data Store writes, Job Scheduling | -| Data processing (100M rows/mo) | $2,000–$30,000+ | Data Store writes dominate | -| ML-powered app | $10–$200 | Zia APIs, QuickML, Data Store | -| Chat application | $5–$50 | ConvoKraft, Functions, Cache | -| E-commerce (medium) | $20–$200 | Data Store, Cache, Functions, Mail | - -These are illustrative only — always build a detailed estimate for the specific use case. - ---- - -## Currency & Regional Pricing - -### How Catalyst billing currency works - -Catalyst invoices in the **currency attached to the credit card on file**. The pricing page at -https://catalyst.zoho.com/pricing.html automatically displays prices in the user's regional currency -(USD, INR, EUR, etc.) based on the account's data center. - -- **US data center** → USD pricing -- **IN data center** → INR pricing -- **EU data center** → EUR pricing -- **AU, JP, SA, CA** → respective local currencies - -### INR pricing guidance - -Catalyst's pricing page shows INR rates when accessed from an India DC account. The exact INR values -are set by Zoho and are **not simply USD × exchange rate** — they are region-specific rates. - -**When a user asks for INR pricing:** -1. Direct them to https://catalyst.zoho.com/pricing.html (it auto-detects region) -2. If they need a programmatic check, the pricing calculator on that page shows INR values for IN DC accounts -3. For estimates in this skill, all values are in USD. Add a note: - > "These estimates are in USD. For INR pricing, visit https://catalyst.zoho.com/pricing.html - > from your Catalyst account — it will show rates in your account's billing currency." - -**When building a pricing spreadsheet for an INR user:** -- Add a "Currency" cell in the Key Assumptions sheet (default: USD) -- Add an "Exchange Rate" cell (for approximate conversion if exact INR rates are unavailable) -- Note that the user should verify against the official pricing page for exact INR rates -- The minimum billing of $5/project/month translates to the INR equivalent shown on the pricing page diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/project-and-cli.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/project-and-cli.md deleted file mode 100644 index bf83530d6..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/project-and-cli.md +++ /dev/null @@ -1,544 +0,0 @@ -# Project Structure & CLI Reference - -> **⚠️ PRE-FLIGHT CHECK:** Before creating any project files, confirm that `.catalystrc` and `catalyst.json` already exist in the project directory. If they don't, STOP — do not create files, do not scaffold. Tell the user to run `catalyst init` in their terminal first. `catalyst.json`, `.catalystrc`, and `functions/` are auto-generated by the CLI and cannot be created manually. See the Pre-flight Gate in SKILL.md. - -## Table of Contents -1. [Project Directory Structure](#project-directory-structure) -2. [catalyst.json Configuration](#catalystjson) -3. [.catalystrc — Project Identity File](#catalystrc--project-identity-file) -4. [catalyst-config.json for Functions](#catalyst-configjson-for-functions) -5. [catalyst-config.json for Client](#catalyst-configjson-for-client) -6. [CLI Installation & Login](#cli-installation--login) -7. [CLI Commands Reference](#cli-commands-reference) -8. [Local Testing](#local-testing) -9. [Deployment](#deployment) -10. [Environments](#environments) - ---- - -## Project Directory Structure - -When `catalyst init` is run, a standard project layout is created. Claude must always produce code that -matches this structure exactly, or deployment will fail. - -``` -my-catalyst-project/ # Project root (home directory) -├── catalyst.json # Auto-generated project config — NEVER create manually -├── .catalystrc # Auto-generated project identity — NEVER create manually -├── functions/ # All server-side functions -│ ├── function_name_1/ # Each function is its own directory -│ │ ├── index.js # Entry point (Node.js) -│ │ ├── catalyst-config.json # Function-specific config (deployment/execution keys) -│ │ ├── package.json # NPM dependencies -│ │ └── node_modules/ # Dependencies (auto-installed) -│ └── function_name_2/ -│ ├── main.py # Entry point (Python) -│ ├── catalyst-config.json -│ └── requirements.txt -├── my-slate-app/ # Slate frontend app (PREFERRED for new projects) -│ ├── app/ -│ │ ├── index.html # Entry HTML file -│ │ ├── css/ -│ │ └── js/ -│ └── catalyst-config.json # Slate app config -├── client/ # ⚠️ LEGACY — Web Client Hosting (deprecated, use Slate instead) -│ ├── index.html -│ └── catalyst-config.json -└── appsail/ # AppSail services (optional) - ├── app.js # Express/Hapi/Koa etc. - ├── catalyst-config.json - └── package.json -``` - -Key rules: -- The `functions/` directory name is fixed and cannot be renamed -- Each function must be in its own subdirectory under `functions/` -- Each function directory must contain its own `catalyst-config.json` -- **For frontends, always use Slate** — select **Slate** during `catalyst init` (it's a component option alongside Functions, Client, AppSail). Do NOT select "Client" — it is legacy and being deprecated. Use `catalyst slate:create` only to add additional Slate apps later. -- `catalyst.json` and `.catalystrc` at the project root are auto-generated — **NEVER create them manually** -- The entire project structure (`functions/`, etc.) is created by `catalyst init` — do not create directories manually - ---- - -## catalyst.json - -`catalyst.json` at the project root holds **deployment configuration** — which functions, AppSail -services, and Slate apps to deploy. It is created and updated by the CLI; do not create it manually. - -```json -{ - "functions": { - "targets": ["myFunction1", "myFunction2"], - "ignore": [], - "source": "functions" - }, - "appsail": { - "targets": ["my-appsail-service"], - "source": "appsail" - }, - "slate": [ - { "name": "my-frontend", "source": "/absolute/path/to/client" } - ] -} -``` - -Key rules: -- The `functions` block **must** include `targets`, `ignore`, and `source` — omitting any field - causes the CLI error `"targets was found to be empty"`. -- Slate `source` **must be an absolute path**. A relative path causes: - `"You are not in a catalyst app directory"`. -- `catalyst.json` is for deployment configuration only. Project identity (project ID, env ID, - timezone) lives in `.catalystrc` — see the section below. - ---- - -## .catalystrc — Project Identity File - -Created automatically by `catalyst init` at the project root. Contains the actual project metadata -required for `catalyst deploy` and `catalyst push` to work. - -```json -{ - "project_id": "4074000000163001", - "project_domain": "myapp-60019947973.development", - "env_id": "60019947973", - "timezone": "Asia/Kolkata" -} -``` - -Key points: -- Created automatically by `catalyst init` — do not create manually. -- Contains `project_id`, `env_id`, `project_domain`, and `timezone`. -- If this file is missing or deleted, `catalyst deploy` will fail. -- Do not confuse with `catalyst.json` which holds deployment targets, not project identity. - ---- - -## catalyst-config.json for Functions - -Every function directory must contain this file. It tells Catalyst how to deploy and execute the function. - -⚠️ **Structure:** The config uses two top-level keys: `deployment` (name, type, stack) and `execution` (entry point). Do NOT use a `function` key — it does not exist and will cause `catalyst deploy` to crash with a cryptic `TypeError`. - -⚠️ **Entry point key:** The entry point is specified as `"main"` inside the `execution` block — NOT `"entry_point"`. - -⚠️ **First deploy prerequisite:** Having a directory + `catalyst-config.json` is NOT enough for the first deploy. Run `catalyst functions:add` from the project root first (interactive — enter name, type, stack when prompted). This registers the function in `catalyst.json`. Only then will `catalyst deploy --only functions` work. Subsequent deploys do not need this step again. - -### Node.js Advanced I/O Function: -```json -{ - "deployment": { - "name": "my_advanced_io_function", - "type": "advancedio", - "stack": "node20", - "env_variables": {} - }, - "execution": { - "main": "index.js" - } -} -``` - -### Node.js Basic I/O Function: -```json -{ - "deployment": { - "name": "my_basic_io_function", - "type": "basicio", - "stack": "node20", - "env_variables": {} - }, - "execution": { - "main": "index.js" - } -} -``` - -### Event Function: -```json -{ - "deployment": { - "name": "my_event_function", - "type": "event", - "stack": "node20", - "env_variables": {} - }, - "execution": { - "main": "index.js" - } -} -``` - -### Cron Function: -```json -{ - "deployment": { - "name": "my_cron_function", - "type": "cron", - "stack": "node20", - "env_variables": {} - }, - "execution": { - "main": "index.js" - } -} -``` - -### Job Function: -```json -{ - "deployment": { - "name": "my_job_function", - "type": "job", - "stack": "node20", - "env_variables": {} - }, - "execution": { - "main": "index.js" - } -} -``` - -### Python Function: -```json -{ - "deployment": { - "name": "my_python_function", - "type": "basicio", - "stack": "python39", - "env_variables": {} - }, - "execution": { - "main": "main.py" - } -} -``` - -### Java Function: -```json -{ - "deployment": { - "name": "my_java_function", - "type": "basicio", - "stack": "java17", - "env_variables": {} - }, - "execution": { - "main": "com.example.Main" - } -} -``` - -**Supported stacks:** -- Node.js: `node20` *(recommended — actively supported)*, `node14`, `node16`, `node18` *(legacy — no upstream security patches)* -- Java: `java8`, `java11`, `java17` -- Python: `python39` - -**Memory options:** 128, 256, 384, 512, 640, 768, 896, 1024 (in MB) — configured in the `deployment` block. (Applies to Functions only; AppSail memory is configured in the console: 256–2048 MB.) - ---- - -## catalyst-config.json for Client - -```json -{ - "name": "my_web_client" -} -``` - ---- - -## CLI Installation & Login - -```bash -# Install CLI globally -npm install -g zcatalyst-cli - -# Verify installation -catalyst --version - -# Login to Zoho account (opens browser for OAuth) -catalyst login - -# Check logged-in user -catalyst whoami - -# Logout -catalyst logout -``` - -Prerequisites: Node.js v20 (LTS) and NPM. - ---- - -## CLI Commands Reference - -### 🚫 Interactive commands — MUST be run by the user manually - -> **The following commands are fully interactive** — they use arrow-key selection menus, multi-step -> prompts, and TTY input that CANNOT be driven programmatically by an LLM or script. If you attempt -> to run these in a non-interactive terminal, they will either hang, select wrong defaults, or fail silently. -> -> **NEVER run these commands yourself. Always instruct the user to run them in their own terminal:** -> -> | Command | What it prompts for | -> |---------|-------------------| -> | `catalyst login` | Opens browser for Zoho OAuth — requires user interaction | -> | `catalyst init` | Project selection (arrow keys), component selection (checkboxes), function config | -> | `catalyst functions:add` | Function name, type (arrow keys), stack (arrow keys) | -> | `catalyst functions:delete` | Function selection (arrow keys), confirmation | -> | `catalyst slate:create` | Framework selection (arrow keys), app name, build config — use this to add Slate after `catalyst init` | -> -> **After the user completes these steps**, you can safely run non-interactive commands like -> `catalyst serve`, `catalyst deploy`, `npm install`, etc. - -### What each interactive command asks (so you can guide the user) - -**`catalyst init`** — Full project initialization. Prompts in order: -1. **Select a default Catalyst portal** — arrow keys to pick from user's Zoho portals/orgs -2. **Select a default Catalyst project** — arrow keys to pick an existing Catalyst project, or "Create a new project" (redirects to console) -3. **Which features to setup?** — checkboxes (Space to toggle, Enter to confirm): **Functions**, **Client**, **AppSail**, **Slate**. Select **Functions + Slate** for most apps. Do NOT select "Client" — it is legacy and being deprecated. - -> ⚠️ **Slate prerequisite:** Before selecting Slate here, the user must first enable Slate in the Catalyst console: go to **console.catalyst.zoho.com → project → Slate** (left sidebar) → click **"Start Exploring"**. This is a one-time activation per project. Without it, Slate init will fail. -4. If **Functions** selected — npm package setup for the first function: - - `package name:` — text input (e.g., `docvault_api`) - - `entry point:` — text input (default: `index.js`) - - `author:` — text input (default: logged-in Zoho email) - - `Do you wish to install all dependencies now?` — Yes/No (recommend Yes) -5. If **Slate** selected — Slate Setup: - - `Select a framework to start with:` — arrow keys (React + Vite, Next.js, Angular, Vue, Svelte, Astro, SolidJS, etc.) - - `Please provide the name for your app:` — text input (e.g., `docvault-ui`) - - Auto-detected config shown: Install Command, Build Command, Build Path, Deployment Name - - `Do you want to modify these default configurations?` — Yes/No (recommend No for defaults) - - `Please provide your Development Command:` — text input (default: `npm run dev -- --port $ZC_SLATE_PORT`) - -After init completes, `.catalystrc` and `catalyst.json` are created automatically. - -**`catalyst functions:add`** — Add a function to an initialized project. Prompts: -1. **Function name** — text input -2. **Function type** — arrow keys: Basic I/O, Advanced I/O, Event, Cron, Job -3. **Runtime stack** — arrow keys: node20, node18, node16, java17, java11, python39 - -**`catalyst slate:create`** — Add an **additional** Slate app to a project that already has Slate initialized. Prompts: -1. **Framework** — arrow keys: react-vite, nextjs, angular, vue, svelte, astro, solidjs, vite, etc. -2. **App name** — text input -3. **Confirm default config** — install command (npm install), build command (npm run build), build path. Enter N to accept defaults or Y to customize. - -### Project Management -```bash -catalyst init # ⚠️ INTERACTIVE — user must run manually -catalyst init --project "MyProject" # ⚠️ INTERACTIVE — user must run manually -catalyst projects:list # ✅ Non-interactive — safe to run -catalyst use # ⚠️ INTERACTIVE — project selection menu -catalyst reset # ✅ Non-interactive — safe to run -``` - -### Function Management -```bash -catalyst functions:setup # ⚠️ INTERACTIVE — user must run manually -catalyst functions:add # ⚠️ INTERACTIVE — user must run manually -catalyst functions:shell # ✅ Non-interactive — safe to run -catalyst functions:delete # ⚠️ INTERACTIVE — user must run manually -catalyst functions:configure-memory # ⚠️ INTERACTIVE — user must run manually -``` - -### Client Management (LEGACY — use Slate instead for new projects) -```bash -catalyst client:setup # ⚠️ LEGACY — sets up deprecated Web Client Hosting. Use Slate instead. -catalyst client:delete # Delete legacy web client -``` - -### AppSail Management -```bash -catalyst appsail:add # Add AppSail service -``` - -### Data Store -```bash -catalyst data:import # Import data into Data Store -catalyst data:export # Export data from Data Store -catalyst data:status # Check import/export status -``` - -### Slate -```bash -catalyst slate:create # ⚠️ INTERACTIVE — Add an additional Slate app (framework + name + build config) -catalyst slate:link # ⚠️ INTERACTIVE — Link existing local dir to Slate service -catalyst slate:unlink # Unlink a Slate app -catalyst serve --only slate # Serve Slate app locally -catalyst deploy slate # Deploy all Slate apps to Development -catalyst deploy slate -m "message" # Deploy with a deployment message -catalyst deploy --only slate:appname # Deploy a specific Slate app -catalyst deploy slate --production # Deploy to Production -``` - -### Pull & Export/Import -```bash -catalyst pull # Pull resources from remote -catalyst export # Export project as ZIP -catalyst import # Import project from ZIP -``` - ---- - -## Local Testing - -```bash -# Serve all resources locally (functions + client) -catalyst serve - -# Serve only functions -catalyst serve --only functions - -# Serve only client -catalyst serve --only client - -# Specify custom port -catalyst serve --port 3000 - -# Launch function shell for testing -catalyst functions:shell -``` - -When serving locally: -- Functions are available at `http://localhost:/server//execute` -- Client is available at `http://localhost:/app/index.html` -- The serve command connects to the remote Catalyst console for Data Store, File Store, etc. - -### What works locally vs what doesn't - -| Feature | Works with `catalyst serve`? | Notes | -|---------|------------------------------|-------| -| Basic I/O functions | Yes | Full local execution | -| Advanced I/O functions | Yes | Full local execution | -| Event functions | No | Triggered by platform events only | -| Cron functions | No | Triggered by scheduler only | -| Job functions | No | Triggered by Job Scheduling only | -| Data Store / ZCQL | Yes | Connects to remote Development environment | -| Cache | Yes | Connects to remote Development environment | -| Stratus | Yes | Connects to remote Development environment | -| Web Client | Yes | Served on localhost | -| AppSail | Partial | Use `node server.js` locally; no Catalyst auth layer | - -For functions that can't be tested locally, deploy to Development and test there. -Use Tunneling (`catalyst tunnel`) to expose your local server to Catalyst for -webhook/event testing. - ---- - -## Deployment - -```bash -# Deploy all resources (functions + client + appsail) -catalyst deploy - -# Deploy only functions — correct flag: --only functions (space, not --only-functions) -catalyst deploy --only functions - -# Deploy only a specific function -catalyst deploy --only functions:my_function - -# Deploy only client -catalyst deploy --only client - -# Deploy only AppSail -catalyst deploy --only appsail - -# Deploy to Slate (replace 'appname' with your Slate app name) -catalyst deploy --only slate:appname -``` - -⚠️ **Flag syntax (CLI v1.23.0+):** Use `--only ` with a space. The hyphenated `--only-functions` / `--only-client` forms do NOT exist and will throw "unknown option". - -### Deployment package limits - -| Component | Max package size | What's counted | -|-----------|-----------------|----------------| -| Function (zip) | 100 MB | Code + node_modules + assets | -| AppSail | 250 MB | Full build directory including node_modules | -| Slate (frontend) | 400 MB | Frontend build output (HTML/CSS/JS/assets) | - -**To reduce function package size:** -- Use `npm install --production` to exclude devDependencies -- Add unnecessary files to `.catalystignore` -- Consider splitting large functions into smaller, focused ones -- Remove test files, documentation, and examples from node_modules - -### Rolling back a deployment - -Catalyst does not have a one-click rollback. To revert a bad deployment: - -1. **From git:** Check out the previous working commit and redeploy: -```bash - git checkout - catalyst deploy -``` -2. **From console:** For AppSail, the console shows deployment history — you can - redeploy a previous revision -3. **Blue/green via API Gateway:** Route traffic between two function versions - by updating API Gateway routes - -**Best practice:** Tag your git commits before each deployment so you can quickly -identify which version to roll back to. - -### Slate — Manual Setup (without interactive slate:link) - -`catalyst slate:link` is interactive-only and cannot be scripted or piped. To set up Slate -in automated or non-interactive environments: - -**Step 1** — Create `.catalyst/slate-config.toml` inside the client directory: - -```toml -framework = "static" -deployment_name = "default" -``` - -**Step 2** — Add the slate entry to `catalyst.json` (absolute source path required): - -```json -"slate": [ - { "name": "my-frontend", "source": "/absolute/path/to/client" } -] -``` - -**Step 3** — Deploy: - -```bash -catalyst deploy slate -m "initial deploy" -``` - -Slate URL format: `https://.onslate.in` -Example: `https://myapp-60019947973.development.onslate.in` - ---- - -## Environments - -Catalyst has two environments: - -1. **Development (sandbox)**: Where CLI deploys go. Free to use within limits. Used for testing. -2. **Production**: Requires billing setup. Serves live traffic. Deployed separately from the console. - -To deploy to production: -1. Set up billing in the Catalyst console (Settings → Billing) -2. Deploy to production from the console (not from CLI) -3. Production gets its own URL and domain mapping - -The CLI always works with the Development environment. Production deployment is done through the web console. - -### Dev-to-Prod promotion checklist - -1. **Verify in Development** — all functions, AppSail services, and frontend working correctly -2. **Set up billing** — Catalyst Console → Settings → Billing (required for Production) -3. **Deploy to Production** — Console → Deploy → select Production environment -4. **Update environment variables** — Production uses separate env vars. Set them in the - Console for each function and AppSail service -5. **Swap ZAIDs** — Production has different ZAIDs than Development. Update any hardcoded - ZAID references in your code or use environment variables instead -6. **Reconfigure social login** — OAuth redirect URLs must point to the Production domain -7. **Map custom domain** — Console → Domain Mapping → add your production domain -8. **Test the full flow** — auth, data, file uploads, email — all on the Production URL -9. **Monitor** — enable APM and Application Alerts for Production - -**Common pitfall:** Using Development ZAIDs in Production is the #1 cause of auth failures -after deployment. Always use environment variables for ZAIDs, never hardcode them. diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/sdk-java.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/sdk-java.md deleted file mode 100644 index ac30cb797..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/sdk-java.md +++ /dev/null @@ -1,849 +0,0 @@ -# Java SDK Reference - -> **Supported Java versions:** 8, 11, 17 -> **External docs:** https://docs.catalyst.zoho.com/en/sdk/java/v1/overview/ - -## Maven Dependency - -Repository: `https://maven.zohodl.com` - -```xml - - zoho-dl - https://maven.zohodl.com - - - - com.zc - zcatalyst-sdk - 1.15.0 - -``` - ---- - -## Initialization - -```java -// Default project initialization (uses request context) -ZCProject.initProject(); - -// Admin scope initialization -ZCProject adminProject = ZCProject.initProject("admin", ZCUserScope.ADMIN); - -// User scope initialization -ZCProject userProject = ZCProject.initProject("user", ZCUserScope.USER); -``` - ---- - -## Data Store - -### Core Classes - -- **ZCObject** - Main data store access -- **ZCTable** - Represents a table -- **ZCRowObject** - Represents a row/record - -### Insert Single Row - -```java -ZCObject object = ZCObject.getInstance(); -ZCTable table = object.getTable("TableName"); - -ZCRowObject row = ZCRowObject.getInstance(); -row.set("column_name", "value"); -row.set("numeric_column", 123); - -ZCRowObject insertedRow = table.insertRow(row); -long rowId = insertedRow.getRowId(); -``` - -### Insert Multiple Rows - -```java -ZCObject object = ZCObject.getInstance(); -ZCTable table = object.getTable("TableName"); - -List rows = new ArrayList<>(); - -ZCRowObject row1 = ZCRowObject.getInstance(); -row1.set("Name", "Alice"); -rows.add(row1); - -ZCRowObject row2 = ZCRowObject.getInstance(); -row2.set("Name", "Bob"); -rows.add(row2); - -List insertedRows = table.insertRows(rows); -``` - -### Get Single Row - -```java -ZCObject object = ZCObject.getInstance(); -ZCTable table = object.getTable("TableName"); - -ZCRowObject row = table.getRow(rowId); -String value = row.get("column_name").toString(); -``` - -### Get All Rows (Paginated) - -```java -ZCObject object = ZCObject.getInstance(); -ZCTable table = object.getTable("TableName"); - -// Basic fetch -List rows = table.getRows(); - -// Paginated fetch -ZCRowPagedResponse pagedResponse = table.getPagedRows(); -List currentPage = pagedResponse.getCurrentPageData(); -boolean hasNext = pagedResponse.hasNextPage(); -ZCRowPagedResponse nextPage = pagedResponse.getNextPage(); -``` - -### Update Row - -```java -ZCObject object = ZCObject.getInstance(); -ZCTable table = object.getTable("TableName"); - -ZCRowObject row = ZCRowObject.getInstance(); -row.set("ROWID", rowId); -row.set("column_name", "updated_value"); - -ZCRowObject updatedRow = table.updateRow(row); -``` - -### Delete Row - -```java -ZCObject object = ZCObject.getInstance(); -ZCTable table = object.getTable("TableName"); - -table.deleteRow(rowId); -``` - ---- - -## ZCQL - -```java -// Basic query -ZCQL zcql = ZCQL.getInstance(); -List results = zcql.executeQuery("SELECT * FROM TableName WHERE column = 'value'"); - -// V2 query execution -List results = zcql.executeQuery("SELECT * FROM TableName", true); - -// OLAP query (for analytical/aggregation queries) -List results = zcql.executeQuery("SELECT COUNT(*) FROM TableName", true, true); -``` - ---- - -## Cache - -### Core Classes - -- **ZCCache** - Cache access point -- **ZCSegment** - Cache segment -- **ZCCacheObject** - Individual cache entry - -### Put (with Expiry) - -```java -ZCCache cache = ZCCache.getInstance(); -ZCSegment segment = cache.getSegment(segmentId); - -// Put with expiry (in milliseconds) -ZCCacheObject cacheObject = segment.put("cacheKey", "cacheValue", 3600000L); -``` - -### Get - -```java -ZCCache cache = ZCCache.getInstance(); -ZCSegment segment = cache.getSegment(segmentId); - -ZCCacheObject cacheObject = segment.get("cacheKey"); -String value = cacheObject.getValue(); -``` - -### Update - -```java -ZCCache cache = ZCCache.getInstance(); -ZCSegment segment = cache.getSegment(segmentId); - -ZCCacheObject updated = segment.update("cacheKey", "newValue", 7200000L); -``` - -### Delete - -```java -ZCCache cache = ZCCache.getInstance(); -ZCSegment segment = cache.getSegment(segmentId); - -segment.delete("cacheKey"); -``` - ---- - -## File Store - -### Core Classes - -- **ZCFile** - File store access -- **ZCFolder** - Represents a folder - -### Upload File - -```java -ZCFile fileStore = ZCFile.getInstance(); -ZCFolder folder = fileStore.getFolder(folderId); - -File file = new File("/path/to/file.pdf"); -ZCFolder uploadedFile = folder.uploadFile(file); -long fileId = uploadedFile.getFileId(); -``` - -### Download File - -```java -ZCFile fileStore = ZCFile.getInstance(); -ZCFolder folder = fileStore.getFolder(folderId); - -InputStream inputStream = folder.downloadFile(fileId); -``` - -### Delete File - -```java -ZCFile fileStore = ZCFile.getInstance(); -ZCFolder folder = fileStore.getFolder(folderId); - -folder.deleteFile(fileId); -``` - -### Get Folder Details - -```java -ZCFile fileStore = ZCFile.getInstance(); -ZCFolder folder = fileStore.getFolder(folderId); - -ZCFolder folderDetails = folder.getFolderDetails(); -``` - ---- - -## Authentication - -### Core Classes - -- **ZCUser** - User management -- **ZCSignUpData** - User registration data -- **ZCMailTemplateDetails** - Email template for registration - -### Register User - -```java -ZCUser userService = ZCUser.getInstance(); - -ZCSignUpData signUpData = ZCSignUpData.getInstance(); -signUpData.setEmailId("user@example.com"); -signUpData.setFirstName("John"); -signUpData.setLastName("Doe"); -signUpData.setRoleId("role_id"); - -ZCMailTemplateDetails mailTemplate = ZCMailTemplateDetails.getInstance(); -mailTemplate.setSubject("Welcome to the App"); -mailTemplate.setMessage("Click the link to verify your account."); - -signUpData.setMailTemplateDetails(mailTemplate); - -userService.registerUser(signUpData); -``` - -### Get User Details - -```java -ZCUser userService = ZCUser.getInstance(); -ZCUser userDetails = userService.getUserDetails(userId); -String email = userDetails.getEmailId(); -String firstName = userDetails.getFirstName(); -``` - -### Delete User - -```java -ZCUser userService = ZCUser.getInstance(); -userService.deleteUser(userId); -``` - ---- - -## Email - -### Core Classes - -- **ZCMail** - Mail service -- **ZCMailContent** - Mail content/configuration - -### Send Mail - -```java -ZCMail mail = ZCMail.getInstance(); - -ZCMailContent mailContent = ZCMailContent.getInstance(); -mailContent.setFromEmail("sender@yourdomain.com"); -mailContent.setToEmail("recipient@example.com"); -mailContent.setSubject("Subject Line"); -mailContent.setContent("

Hello

This is the email body.

"); - -// Optional settings -mailContent.setCcEmail("cc@example.com"); -mailContent.setBccEmail("bcc@example.com"); -mailContent.setReplyTo("reply@example.com"); -mailContent.setHtml(true); - -mail.sendMail(mailContent); -``` - ---- - -## Search - -### Core Classes - -- **ZCSearch** - Search service -- **ZCSearchDetails** - Search query configuration - -### Execute Search Query - -```java -ZCSearch search = ZCSearch.getInstance(); - -ZCSearchDetails searchDetails = ZCSearchDetails.getInstance(); -searchDetails.setSearchQuery("search term"); -searchDetails.setSearchTableColumns("TableName.column1,TableName.column2"); - -List results = search.executeSearchQuery(searchDetails); -``` - ---- - -## Connections - -### Get Access Token - -```java -ZCConnection connection = ZCConnection.getInstance(); -String accessToken = connection.getConnector("connector_name").getAccessToken(); -``` - ---- - -## Circuits - -### Execute Circuit - -```java -ZCCircuit circuit = ZCCircuit.getInstance(); - -Map inputData = new HashMap<>(); -inputData.put("param1", "value1"); -inputData.put("param2", 42); - -Object result = circuit.execute(circuitId, inputData); -``` - ---- - -## NoSQL - -### Core Classes - -- **ZCNoSQL** - NoSQL access point -- **ZCNoSQLTable** - Represents a NoSQL table -- **ZCNoSQLItem** - Represents a NoSQL item/document - -### Get Table Metadata - -```java -ZCNoSQL nosql = ZCNoSQL.getInstance(); -ZCNoSQLTable table = nosql.getTableMetadata("TableName"); -``` - -### Insert Items - -```java -ZCNoSQL nosql = ZCNoSQL.getInstance(); -ZCNoSQLTable table = nosql.getTable("TableName"); - -ZCNoSQLItem item = ZCNoSQLItem.getInstance(); -item.put("partitionKey", "pk_value"); -item.put("sortKey", "sk_value"); -item.put("attribute1", "value1"); - -table.insertItems(Collections.singletonList(item)); -``` - -### Fetch Items - -```java -ZCNoSQL nosql = ZCNoSQL.getInstance(); -ZCNoSQLTable table = nosql.getTable("TableName"); - -List items = table.fetchItems(); -``` - -### Query Table - -```java -ZCNoSQL nosql = ZCNoSQL.getInstance(); -ZCNoSQLTable table = nosql.getTable("TableName"); - -Map queryParams = new HashMap<>(); -queryParams.put("partitionKey", "pk_value"); - -List results = table.queryTable(queryParams); -``` - -### Query Index - -```java -ZCNoSQL nosql = ZCNoSQL.getInstance(); -ZCNoSQLTable table = nosql.getTable("TableName"); - -Map queryParams = new HashMap<>(); -queryParams.put("indexName", "myIndex"); -queryParams.put("partitionKey", "pk_value"); - -List results = table.queryIndex(queryParams); -``` - -### Update Items - -```java -ZCNoSQL nosql = ZCNoSQL.getInstance(); -ZCNoSQLTable table = nosql.getTable("TableName"); - -ZCNoSQLItem item = ZCNoSQLItem.getInstance(); -item.put("partitionKey", "pk_value"); -item.put("sortKey", "sk_value"); -item.put("attribute1", "updated_value"); - -table.updateItems(Collections.singletonList(item)); -``` - -### Delete Items - -```java -ZCNoSQL nosql = ZCNoSQL.getInstance(); -ZCNoSQLTable table = nosql.getTable("TableName"); - -ZCNoSQLItem item = ZCNoSQLItem.getInstance(); -item.put("partitionKey", "pk_value"); -item.put("sortKey", "sk_value"); - -table.deleteItems(Collections.singletonList(item)); -``` - ---- - -## Stratus - -### Core Classes - -- **ZCStratus** - Stratus (object storage) access point -- **ZCStratusBucket** - Represents a storage bucket - -### Bucket Operations - -```java -ZCStratus stratus = ZCStratus.getInstance(); - -// Check if bucket exists -boolean exists = stratus.checkBucket("bucket-name"); - -// List all buckets -List buckets = stratus.listBuckets(); - -// Get bucket details -ZCStratusBucket bucket = stratus.getDetails("bucket-name"); - -// Get CORS configuration -Object corsConfig = stratus.getCORS("bucket-name"); -``` - -### Object Operations - -```java -ZCStratus stratus = ZCStratus.getInstance(); - -// List objects in bucket -List objects = stratus.listObjects("bucket-name"); - -// Check if object exists -boolean objExists = stratus.checkObject("bucket-name", "object-key"); - -// Upload object -File file = new File("/path/to/file.txt"); -stratus.uploadObject("bucket-name", "object-key", file); - -// Download object -InputStream stream = stratus.downloadObject("bucket-name", "object-key"); - -// Copy object -stratus.copyObject("source-bucket", "source-key", "dest-bucket", "dest-key"); - -// Rename object -stratus.renameObject("bucket-name", "old-key", "new-key"); - -// Delete objects -List keys = Arrays.asList("key1", "key2"); -stratus.deleteObjects("bucket-name", keys); - -// Extract zipped object -stratus.extractZippedObject("bucket-name", "archive.zip", "destination-prefix/"); - -// List object versions -List versions = stratus.listObjectVersions("bucket-name", "object-key"); - -// Get object details/metadata -Object details = stratus.getObjectDetails("bucket-name", "object-key"); - -// Put object metadata -Map metadata = new HashMap<>(); -metadata.put("custom-key", "custom-value"); -stratus.putObjectMeta("bucket-name", "object-key", metadata); -``` - ---- - -## Bulk Data Store - -### Bulk Read - -```java -ZCObject object = ZCObject.getInstance(); -ZCTable table = object.getTable("TableName"); - -// Initiate bulk read job -long jobId = table.bulkRead(); -``` - -### Bulk Write - -```java -ZCObject object = ZCObject.getInstance(); -ZCTable table = object.getTable("TableName"); - -// Bulk write from CSV file -File csvFile = new File("/path/to/data.csv"); -long jobId = table.bulkWrite(csvFile); -``` - -### Bulk Delete Rows - -```java -ZCObject object = ZCObject.getInstance(); -ZCTable table = object.getTable("TableName"); - -// Delete multiple rows (max 200 per call) -List rowIds = Arrays.asList(1001L, 1002L, 1003L); -table.bulkDeleteRows(rowIds); -``` - ---- - -## Push Notifications - -### Core Class - -- **ZCPushNotification** - Push notification service - -### Send Web Notification - -```java -ZCPushNotification pushNotification = ZCPushNotification.getInstance(); - -Map notificationData = new HashMap<>(); -notificationData.put("message", "You have a new update!"); -notificationData.put("recipients", Arrays.asList("user1@example.com")); - -pushNotification.sendNotification(notificationData); -``` - -### Send Mobile Notification - -```java -ZCPushNotification pushNotification = ZCPushNotification.getInstance(); - -Map mobileNotification = new HashMap<>(); -mobileNotification.put("message", "New mobile notification"); -mobileNotification.put("recipients", Arrays.asList("user1@example.com")); -mobileNotification.put("additional_data", Map.of("key", "value")); - -pushNotification.sendMobileNotification(mobileNotification); -``` - ---- - -## Zia Services - -### Core Class - -- **ZCZIA** - Zoho Intelligent Assistant services - -### OCR (Optical Character Recognition) - -```java -ZCZIA zia = ZCZIA.getInstance(); - -File imageFile = new File("/path/to/document.png"); -Object ocrResult = zia.extractOpticalCharacters(imageFile); -``` - -### AutoML - -```java -ZCZIA zia = ZCZIA.getInstance(); - -Map inputData = new HashMap<>(); -inputData.put("feature1", "value1"); -inputData.put("feature2", 42); - -Object prediction = zia.executeAutoML("model_id", inputData); -``` - -### Sentiment Analysis - -```java -ZCZIA zia = ZCZIA.getInstance(); - -List documents = Arrays.asList( - "This product is amazing!", - "Terrible experience, would not recommend." -); - -Object sentimentResult = zia.getSentimentAnalysis(documents); -``` - -### Named Entity Recognition - -```java -ZCZIA zia = ZCZIA.getInstance(); - -List documents = Arrays.asList("John Doe works at Zoho in Chennai."); -Object nerResult = zia.getNamedEntityRecognition(documents); -``` - -### Keyword Extraction - -```java -ZCZIA zia = ZCZIA.getInstance(); - -List documents = Arrays.asList("Cloud computing and artificial intelligence are transforming businesses."); -Object keywords = zia.getKeywordExtraction(documents); -``` - -### All Text Analytics (Combined) - -```java -ZCZIA zia = ZCZIA.getInstance(); - -List documents = Arrays.asList("Zoho Catalyst is a serverless platform."); -Object analytics = zia.getAllTextAnalytics(documents); -``` - -### Image Moderation - -```java -ZCZIA zia = ZCZIA.getInstance(); - -File imageFile = new File("/path/to/image.jpg"); -Object moderationResult = zia.moderateImage(imageFile); -``` - -### Face Detection - -```java -ZCZIA zia = ZCZIA.getInstance(); - -File imageFile = new File("/path/to/photo.jpg"); -Object faceResult = zia.detectFaces(imageFile); -``` - -### Barcode Scanning - -```java -ZCZIA zia = ZCZIA.getInstance(); - -File barcodeImage = new File("/path/to/barcode.png"); -Object barcodeResult = zia.scanBarcode(barcodeImage); -``` - ---- - -## SmartBrowz - -### Core Class - -- **ZCSmartBrowz** - Browser automation / headless browser service - -### Browser Grid - -```java -ZCSmartBrowz smartBrowz = ZCSmartBrowz.getInstance(); - -// Get a new browser grid instance -Object gridInstance = smartBrowz.getBrowserGridInstance(); - -// Get all browser grids -List grids = smartBrowz.getAllBrowserGrids(); - -// Get specific browser grid -Object grid = smartBrowz.getBrowserGrid(gridId); - -// Stop a browser grid -smartBrowz.stopBrowserGrid(gridId); -``` - -### PDF Generation - -```java -ZCSmartBrowz smartBrowz = ZCSmartBrowz.getInstance(); - -Map pdfOptions = new HashMap<>(); -pdfOptions.put("url", "https://example.com"); -pdfOptions.put("format", "A4"); - -InputStream pdfStream = smartBrowz.generatePDF(pdfOptions); -``` - -### Screenshot Generation - -```java -ZCSmartBrowz smartBrowz = ZCSmartBrowz.getInstance(); - -Map screenshotOptions = new HashMap<>(); -screenshotOptions.put("url", "https://example.com"); -screenshotOptions.put("width", 1920); -screenshotOptions.put("height", 1080); - -InputStream screenshotStream = smartBrowz.generateScreenshot(screenshotOptions); -``` - ---- - -## Job Scheduling - -### Core Class - -- **ZCJobScheduling** - Job and cron management - -### Job Pool Operations - -```java -ZCJobScheduling jobScheduling = ZCJobScheduling.getInstance(); - -// Get all job pools -List pools = jobScheduling.getAllJobPools(); - -// Get specific job pool -Object pool = jobScheduling.getJobPool(poolId); -``` - -### Job Operations - -```java -ZCJobScheduling jobScheduling = ZCJobScheduling.getInstance(); - -// Create a job -Map jobConfig = new HashMap<>(); -jobConfig.put("job_pool_id", poolId); -jobConfig.put("job_name", "MyJob"); -jobConfig.put("target_url", "/server/myfunction/execute"); - -Object job = jobScheduling.createJob(jobConfig); - -// Get a job -Object jobDetails = jobScheduling.getJob(jobId); - -// Delete a job -jobScheduling.deleteJob(jobId); -``` - -### Cron Operations - -```java -ZCJobScheduling jobScheduling = ZCJobScheduling.getInstance(); - -// Create a one-time cron -Map oneTimeCron = new HashMap<>(); -oneTimeCron.put("cron_name", "OneTimeCron"); -oneTimeCron.put("job_id", jobId); -oneTimeCron.put("time", "2026-06-01T10:00:00Z"); -Object cron = jobScheduling.createOneTimeCron(oneTimeCron); - -// Create a recurring cron -Map recurringCron = new HashMap<>(); -recurringCron.put("cron_name", "RecurringCron"); -recurringCron.put("job_id", jobId); -recurringCron.put("frequency_type", "hourly"); -recurringCron.put("hour", 1); -Object recurring = jobScheduling.createRecurringCron(recurringCron); - -// Create cron with expression -Map cronExpr = new HashMap<>(); -cronExpr.put("cron_name", "ExpressionCron"); -cronExpr.put("job_id", jobId); -cronExpr.put("cron_expression", "0 0 12 * * ?"); -Object exprCron = jobScheduling.createCronExpression(cronExpr); - -// Get / List crons -Object cronDetails = jobScheduling.getCron(cronId); -List allCrons = jobScheduling.getAllCrons(); - -// Update cron -Map updateData = new HashMap<>(); -updateData.put("cron_name", "UpdatedCronName"); -jobScheduling.updateCron(cronId, updateData); - -// Pause / Resume / Run / Delete cron -jobScheduling.pauseCron(cronId); -jobScheduling.resumeCron(cronId); -jobScheduling.runCron(cronId); -jobScheduling.deleteCron(cronId); -``` - ---- - -## Pipelines - -### Core Class - -- **ZCPipeline** - Pipeline orchestration - -### Get Pipeline Details - -```java -ZCPipeline pipeline = ZCPipeline.getInstance(); -Object pipelineDetails = pipeline.getPipelineDetails(pipelineId); -``` - -### Execute Pipeline - -```java -ZCPipeline pipeline = ZCPipeline.getInstance(); - -Map pipelineInput = new HashMap<>(); -pipelineInput.put("param1", "value1"); -pipelineInput.put("param2", "value2"); - -Object executionResult = pipeline.executePipeline(pipelineId, pipelineInput); -``` diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/sdk-mobile.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/sdk-mobile.md deleted file mode 100644 index fa87a14f8..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/sdk-mobile.md +++ /dev/null @@ -1,750 +0,0 @@ -# Catalyst Mobile SDKs Reference (Android, iOS, Flutter) - ---- - -## Android SDK (Kotlin) v3 - -> **Docs:** https://docs.catalyst.zoho.com/en/sdk/android/ - -### Setup - -**Gradle (project-level):** - -```groovy -allprojects { - repositories { - maven { url "https://maven.zohodl.com" } - } -} -``` - -**Gradle (app-level):** - -```groovy -dependencies { - implementation 'com.zoho.catalyst:catalyst-android-sdk:3.+' -} -``` - -**AndroidManifest.xml permissions:** - -```xml - - -``` - -**strings.xml — URL scheme:** - -```xml -zc-YOUR_PROJECT_ID -``` - -**Config file:** Place the downloaded `AppConfigurationData.plist` (or the JSON config file) in `app/src/main/assets/`. - -### Init - -```kotlin -ZCatalystApp.init(context, ZCatalystEnvironment.DEVELOPMENT) -// For production: -// ZCatalystApp.init(context, ZCatalystEnvironment.PRODUCTION) -``` - -### Auth - -```kotlin -val app = ZCatalystApp.getInstance() - -// Sign Up -app.signup(firstName, lastName, email, object : ZCatalystCallback { - override fun onSuccess(result: Void?) { /* user registered */ } - override fun onFailure(exception: ZCatalystException) { /* handle error */ } -}) - -// Login -app.login(activity, object : ZCatalystCallback { - override fun onSuccess(result: Void?) { /* login success */ } - override fun onFailure(exception: ZCatalystException) { /* handle error */ } -}) - -// Logout -app.logout(object : ZCatalystCallback { - override fun onSuccess(result: Void?) { /* logged out */ } - override fun onFailure(exception: ZCatalystException) { /* handle error */ } -}) - -// Get Current User -app.getCurrentUser(object : ZCatalystCallback { - override fun onSuccess(user: ZCatalystUser) { - val email = user.emailId - val name = user.firstName - } - override fun onFailure(exception: ZCatalystException) { /* handle error */ } -}) - -// Check if user is signed in -val signedIn = app.isUserSignedIn() -``` - -### Data Store - -```kotlin -val dataStore = ZCatalystApp.getInstance().getDataStoreInstance() - -// Create rows -val row = ZCatalystRow() -row.setColumnValue("Name", "Alice") -row.setColumnValue("Age", 30) -dataStore.getTableInstance("Users").createRows(listOf(row), object : ZCatalystCallback> { - override fun onSuccess(rows: List) { /* created */ } - override fun onFailure(exception: ZCatalystException) { /* handle error */ } -}) - -// Get rows -dataStore.getTableInstance("Users").getRows(object : ZCatalystCallback> { - override fun onSuccess(rows: List) { - for (row in rows) { - val name = row.getColumnValue("Name") - } - } - override fun onFailure(exception: ZCatalystException) { /* handle error */ } -}) - -// Update rows -row.setColumnValue("ROWID", "12345") -row.setColumnValue("Age", 31) -dataStore.getTableInstance("Users").updateRows(listOf(row), object : ZCatalystCallback> { - override fun onSuccess(rows: List) { /* updated */ } - override fun onFailure(exception: ZCatalystException) { /* handle error */ } -}) - -// Delete a row -dataStore.getTableInstance("Users").deleteRow("12345", object : ZCatalystCallback { - override fun onSuccess(result: Void?) { /* deleted */ } - override fun onFailure(exception: ZCatalystException) { /* handle error */ } -}) -``` - -### ZCQL - -```kotlin -val zcql = ZCatalystApp.getInstance().getZCQLInstance() -zcql.executeQuery("SELECT * FROM Users WHERE Age > 25", object : ZCatalystCallback>> { - override fun onSuccess(rows: List>) { /* process results */ } - override fun onFailure(exception: ZCatalystException) { /* handle error */ } -}) -``` - -### File Store - -```kotlin -val fileStore = ZCatalystApp.getInstance().getFileStoreInstance() - -// Get folders -fileStore.getFolders(object : ZCatalystCallback> { - override fun onSuccess(folders: List) { /* list folders */ } - override fun onFailure(exception: ZCatalystException) { /* handle error */ } -}) - -// Upload file -val folder = fileStore.getFolderInstance("folderId") -folder.uploadFile(file, object : ZCatalystCallback { - override fun onSuccess(uploadedFile: ZCatalystFile) { /* uploaded */ } - override fun onFailure(exception: ZCatalystException) { /* handle error */ } -}) - -// Download file -folder.downloadFile("fileId", object : ZCatalystCallback { - override fun onSuccess(stream: InputStream) { /* read stream */ } - override fun onFailure(exception: ZCatalystException) { /* handle error */ } -}) - -// Delete file -folder.deleteFile("fileId", object : ZCatalystCallback { - override fun onSuccess(result: Void?) { /* deleted */ } - override fun onFailure(exception: ZCatalystException) { /* handle error */ } -}) -``` - -### Search - -```kotlin -val search = ZCatalystApp.getInstance().getSearchInstance() -search.executeSearchQuery("search term", object : ZCatalystCallback> { - override fun onSuccess(results: List) { /* process results */ } - override fun onFailure(exception: ZCatalystException) { /* handle error */ } -}) -``` - -### Push Notifications - -```kotlin -val push = ZCatalystApp.getInstance().getPushNotificationInstance() -push.registerToken(fcmToken, object : ZCatalystCallback { - override fun onSuccess(result: Void?) { /* registered */ } - override fun onFailure(exception: ZCatalystException) { /* handle error */ } -}) -``` - -### Stratus - -```kotlin -val stratus = ZCatalystApp.getInstance().getStratusInstance() -val bucket = stratus.bucket("bucket-name") - -bucket.getObject("path/to/file.txt", object : ZCatalystCallback { - override fun onSuccess(obj: ZCatalystStratusObject) { /* use object */ } - override fun onFailure(exception: ZCatalystException) { /* handle error */ } -}) - -bucket.putObject("path/to/file.txt", file, object : ZCatalystCallback { - override fun onSuccess(result: Void?) { /* uploaded */ } - override fun onFailure(exception: ZCatalystException) { /* handle error */ } -}) - -bucket.deleteObject("path/to/file.txt", object : ZCatalystCallback { - override fun onSuccess(result: Void?) { /* deleted */ } - override fun onFailure(exception: ZCatalystException) { /* handle error */ } -}) -``` - ---- - -## iOS SDK (Swift) v2 - -> **Docs:** https://docs.catalyst.zoho.com/en/sdk/ios/ - -### Setup - -**CocoaPods (Podfile):** - -```ruby -pod 'ZCatalyst', :git => 'https://github.com/nicetomeetyou/ZCatalyst.git', :tag => '2.2.2' -``` - -Run `pod install` after adding. - -**Config plist:** Place the downloaded `AppConfigurationData.plist` in your project bundle. - -**Info.plist — URL scheme:** - -```xml -CFBundleURLTypes - - - CFBundleURLSchemes - - zc-YOUR_PROJECT_ID - - - -``` - -### Init - -```swift -import ZCatalyst - -// In AppDelegate application(_:didFinishLaunchingWithOptions:) -ZCatalystApp.shared.initSDK() -``` - -### Handle Login Redirection - -**AppDelegate:** - -```swift -func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { - return ZCatalystApp.shared.handleLoginRedirection(for: url) -} -``` - -**SceneDelegate (iOS 13+):** - -```swift -func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { - if let url = URLContexts.first?.url { - ZCatalystApp.shared.handleLoginRedirection(for: url) - } -} -``` - -### Auth - -```swift -// Sign Up -ZCatalystApp.shared.signup(firstName: "Alice", lastName: "Smith", email: "alice@example.com") { result in - switch result { - case .success: - print("Signup successful") - case .failure(let error): - print("Error: \(error)") - } -} - -// Login -ZCatalystApp.shared.login(presentingViewController: self) { result in - switch result { - case .success: - print("Login successful") - case .failure(let error): - print("Error: \(error)") - } -} - -// Logout -ZCatalystApp.shared.logout { result in - switch result { - case .success: - print("Logged out") - case .failure(let error): - print("Error: \(error)") - } -} - -// Get Current User -ZCatalystApp.shared.getCurrentUser { result in - switch result { - case .success(let user): - print(user.emailId) - print(user.firstName) - case .failure(let error): - print("Error: \(error)") - } -} - -// Check if signed in -let signedIn = ZCatalystApp.shared.isUserSignedIn() -``` - -### Data Store - -```swift -let dataStore = ZCatalystApp.shared.getDataStoreInstance() -let table = dataStore.getTableInstance(name: "Users") - -// Get rows -table.getRows { result in - switch result { - case .success(let rows): - for row in rows { - let name = row.getValue(forColumn: "Name") - } - case .failure(let error): - print("Error: \(error)") - } -} - -// Create a row -var row = ZCatalystRow() -row.setColumnValue("Name", forColumn: "Name") -row.setColumnValue(30, forColumn: "Age") -table.createRow(row) { result in - switch result { - case .success(let createdRow): - print("Created: \(createdRow)") - case .failure(let error): - print("Error: \(error)") - } -} - -// Update a row -row.setColumnValue("ROWID", forColumn: "12345") -row.setColumnValue(31, forColumn: "Age") -table.updateRow(row) { result in - switch result { - case .success(let updatedRow): - print("Updated") - case .failure(let error): - print("Error: \(error)") - } -} - -// Delete a row -table.deleteRow(id: "12345") { result in - switch result { - case .success: - print("Deleted") - case .failure(let error): - print("Error: \(error)") - } -} -``` - -### ZCQL - -```swift -let zcql = ZCatalystApp.shared.getZCQLInstance() -zcql.executeQuery("SELECT * FROM Users WHERE Age > 25") { result in - switch result { - case .success(let rows): - for row in rows { - print(row) - } - case .failure(let error): - print("Error: \(error)") - } -} -``` - -### File Store - -```swift -let fileStore = ZCatalystApp.shared.getFileStoreInstance() - -// Get folders -fileStore.getFolders { result in - switch result { - case .success(let folders): - print(folders) - case .failure(let error): - print("Error: \(error)") - } -} - -// Upload file -let folder = fileStore.getFolderInstance(id: "folderId") -folder.uploadFile(name: "photo.jpg", data: imageData) { result in - switch result { - case .success(let file): - print("Uploaded: \(file.id)") - case .failure(let error): - print("Error: \(error)") - } -} - -// Download file -folder.downloadFile(id: "fileId") { result in - switch result { - case .success(let data): - print("Downloaded \(data.count) bytes") - case .failure(let error): - print("Error: \(error)") - } -} - -// Delete file -folder.deleteFile(id: "fileId") { result in - switch result { - case .success: - print("Deleted") - case .failure(let error): - print("Error: \(error)") - } -} -``` - -### Stratus - -```swift -let stratus = ZCatalystApp.shared.getStratusInstance() -let bucket = stratus.bucket("bucket-name") - -bucket.getObject(path: "path/to/file.txt") { result in - switch result { - case .success(let obj): - print("Object: \(obj)") - case .failure(let error): - print("Error: \(error)") - } -} - -bucket.putObject(path: "path/to/file.txt", data: fileData) { result in - switch result { - case .success: - print("Uploaded") - case .failure(let error): - print("Error: \(error)") - } -} - -bucket.deleteObject(path: "path/to/file.txt") { result in - switch result { - case .success: - print("Deleted") - case .failure(let error): - print("Error: \(error)") - } -} -``` - -### Push Notifications - -```swift -let push = ZCatalystApp.shared.getPushNotificationInstance() -push.registerToken(fcmToken: token) { result in - switch result { - case .success: - print("Registered") - case .failure(let error): - print("Error: \(error)") - } -} -``` - -### Search - -```swift -let search = ZCatalystApp.shared.getSearchInstance() -search.executeSearchQuery("search term") { result in - switch result { - case .success(let results): - print(results) - case .failure(let error): - print("Error: \(error)") - } -} -``` - ---- - -## Flutter SDK (Dart) v2 - -> **Docs:** https://docs.catalyst.zoho.com/en/sdk/flutter/ - -### Setup - -**pubspec.yaml:** - -```yaml -dependencies: - zcatalyst_sdk: ^2.2.1 -``` - -Run `flutter pub get`. - -**Platform config:** Place the Catalyst config file in platform-specific locations: -- **Android:** `android/app/src/main/assets/` -- **iOS:** Add to Xcode project bundle - -Set up URL schemes for each platform as described in the Android and iOS sections above. - -### Init - -```dart -import 'package:zcatalyst_sdk/zcatalyst_sdk.dart'; - -// Using config file (default) -await ZCatalystApp.init(); - -// Using custom SDKConfigs -await ZCatalystApp.init( - config: SDKConfigs( - projectId: "YOUR_PROJECT_ID", - environment: Environment.development, - ), -); -``` - -### Auth - -```dart -final app = ZCatalystApp.getInstance(); - -// Sign Up (using Dart record pattern) -final (success, error) = await app.signup( - firstName: "Alice", - lastName: "Smith", - email: "alice@example.com", -); -if (success) { - print("Signup successful"); -} else { - print("Error: $error"); -} - -// Login -await app.login(); - -// Logout -await app.logout(); - -// Check if logged in -final isLoggedIn = await app.isUserLoggedIn(); - -// Get Current User -final user = await app.getCurrentUser(); -print(user.emailId); -print(user.firstName); - -// Handle Custom Login (for custom auth flows) -await app.handleCustomLogin(token: jwtToken); -``` - -### Data Store - -```dart -final dataStore = ZCatalystApp.getInstance().getDataStoreInstance(); -final table = dataStore.getTableInstance("Users"); - -// Get a single row -final row = await table.getRow("12345"); - -// Get all rows -final rows = await table.getRows(); - -// Create a single row -final newRow = await table.createRow({ - "Name": "Alice", - "Age": 30, -}); - -// Create multiple rows -final newRows = await table.createRows([ - {"Name": "Alice", "Age": 30}, - {"Name": "Bob", "Age": 25}, -]); - -// Update a row -final updatedRow = await table.updateRow({ - "ROWID": "12345", - "Age": 31, -}); - -// Delete a row -await table.deleteRow("12345"); -``` - -### ZCQL Query Builder - -The Flutter SDK provides a type-safe query builder: - -```dart -final zcql = ZCatalystApp.getInstance().getZCQLInstance(); - -// Simple query -final results = await zcql.executeQuery("SELECT * FROM Users"); - -// Query Builder -final query = ZCatalystQueryBuilder() - .select(["Name", "Age", "Email"]) - .from("Users") - .where("Age", ">", 25) - .and("Name", "LIKE", "%Alice%") - .or("Email", "IS NOT NULL", null) - .groupBy(["Age"]) - .orderBy("Name", ascending: true) - .limit(50) - .build(); - -final results = await zcql.executeQuery(query); - -// Join queries -final joinQuery = ZCatalystQueryBuilder() - .select(["Users.Name", "Orders.Total"]) - .from("Users") - .innerJoin("Orders", "Users.ROWID", "Orders.UserId") - .build(); - -final leftJoinQuery = ZCatalystQueryBuilder() - .select(["Users.Name", "Orders.Total"]) - .from("Users") - .leftJoin("Orders", "Users.ROWID", "Orders.UserId") - .where("Orders.Total", ">", 100) - .build(); -``` - -### File Store - -```dart -final fileStore = ZCatalystApp.getInstance().getFileStoreInstance(); - -// Get folders -final folders = await fileStore.getFolders(); - -// Upload file -final folder = fileStore.getFolderInstance("folderId"); -final uploadedFile = await folder.uploadFile(file); - -// Download with progress -await folder.downloadFile( - "fileId", - savePath: "/path/to/save/file.txt", - onProgress: (received, total) { - print("Progress: ${(received / total * 100).toStringAsFixed(0)}%"); - }, -); - -// Delete file -await folder.deleteFile("fileId"); -``` - -### Stratus - -```dart -final stratus = ZCatalystApp.getInstance().getStratusInstance(); -final bucket = stratus.bucket("bucket-name"); - -// Get object metadata -final obj = await bucket.getObject("path/to/file.txt"); - -// Get objects (paginated) -final objects = await bucket.getObjects( - prefix: "path/to/", - maxKeys: 100, - continuationToken: null, -); - -// Download with progress -await bucket.downloadObject( - "path/to/file.txt", - savePath: "/local/path/file.txt", - onProgress: (received, total) { - print("Progress: ${(received / total * 100).toStringAsFixed(0)}%"); - }, -); - -// Upload object -await bucket.uploadObject("path/to/file.txt", file); - -// Delete objects (batch) -await bucket.deleteObjects(["path/to/file1.txt", "path/to/file2.txt"]); - -// Delete by path prefix -await bucket.deletePath("path/to/folder/"); -``` - -### Search - -```dart -final search = ZCatalystApp.getInstance().getSearchInstance(); -final results = await search.executeSearchQuery("search term"); -``` - -### Push Notifications - -```dart -final push = ZCatalystApp.getInstance().getPushNotificationInstance(); -await push.registerToken(fcmToken); -``` - -### Functions - -```dart -final functions = ZCatalystApp.getInstance().getFunctionsInstance(); - -// GET request -final getResult = await functions.executeGET("functionName", queryParams: {"key": "value"}); - -// POST request -final postResult = await functions.executePOST("functionName", body: {"key": "value"}); - -// PUT request -final putResult = await functions.executePUT("functionName", body: {"key": "value"}); - -// DELETE request -final deleteResult = await functions.executeDELETE("functionName", queryParams: {"id": "123"}); -``` - -### Error Handling - -All SDK operations can throw `ZCatalystException`: - -```dart -try { - final rows = await table.getRows(); -} on ZCatalystException catch (e) { - print("Error code: ${e.code}"); - print("Error message: ${e.message}"); - print("HTTP status: ${e.httpStatusCode}"); -} -``` diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/sdk-nodejs.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/sdk-nodejs.md deleted file mode 100644 index a8704022e..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/sdk-nodejs.md +++ /dev/null @@ -1,970 +0,0 @@ -# Node.js SDK Reference (zcatalyst-sdk-node) - -Install via `npm install zcatalyst-sdk-node`. -External docs: https://docs.catalyst.zoho.com/en/sdk/nodejs/v2/overview/ - -> All versions earlier than 2.5.0 are deprecated. Always use the latest version. - ---- - -## Initialization - -```js -const catalyst = require('zcatalyst-sdk-node'); - -// --- Advanced I/O (Express) --- -// In an Express-based Advanced I/O function, pass the Express request object: -app.post('/api/action', async (req, res) => { - const catalystApp = catalyst.initialize(req); - // ... use catalystApp -}); - -// --- Basic I/O --- -// In a Basic I/O function, pass the context object: -module.exports = async (context, basicIO) => { - const catalystApp = catalyst.initialize(context); - // ... use catalystApp - basicIO.write(JSON.stringify({ status: 'ok' })); - context.close(); -}; - -// --- Event Function --- -module.exports = async (event, context) => { - const catalystApp = catalyst.initialize(context); - // event.data contains the event payload - context.close(); -}; - -// --- Cron Function --- -module.exports = async (cronDetails, context) => { - const catalystApp = catalyst.initialize(context); - // cronDetails contains cron metadata - context.close(); -}; - -// --- AppSail --- -// In an AppSail app (Express), same pattern as Advanced I/O: -const catalystApp = catalyst.initialize(req); - -// --- Admin Scope vs User Scope --- -// Scopes apply to Data Store, File Store, and ZCQL operations. -// Admin scope bypasses row-level permissions: -const adminApp = catalyst.initialize(req, { scope: 'admin' }); - -// User scope restricts to the authenticated user's rows: -const userApp = catalyst.initialize(req, { scope: 'user' }); -``` - ---- - -## Data Store - -```js -// Get a table reference (by name or table ID) -const table = catalystApp.datastore().table('Shipments'); -// Or by ID: catalystApp.datastore().table(23aborTableId); - -// --- Insert a single row --- -const row = await table.insertRow({ - Name: 'Alice', - Email: 'alice@example.com' -}); -// row.ROWID is the auto-generated unique identifier - -// --- Insert multiple rows --- -const rows = await table.insertRows([ - { Name: 'Bob', Email: 'bob@example.com' }, - { Name: 'Carol', Email: 'carol@example.com' } -]); - -// --- Get a single row by ROWID --- -const singleRow = await table.getRow('123456000000012345'); - -// --- Get paginated rows --- -// Returns up to 200 rows per page by default. -const result = await table.getPagedRows({ - nextToken: 'token_from_previous_call', // optional - maxRows: 100 // optional, default 200 -}); -const data = result.data; -const hasMore = result.more_records; -const nextToken = result.next_token; - -// --- Update a row (ROWID is required) --- -const updated = await table.updateRow({ - ROWID: '123456000000012345', - Name: 'Alice Updated' -}); - -// --- Delete a row --- -await table.deleteRow('123456000000012345'); -``` - ---- - -## ZCQL - -```js -const zcql = catalystApp.zcql(); - -// --- Execute a ZCQL query --- -// Supports SELECT, INSERT, UPDATE, DELETE, and JOIN queries. -const rows = await zcql.executeZCQLQuery('SELECT * FROM Shipments WHERE Status = \'Active\''); - -// INSERT via ZCQL -await zcql.executeZCQLQuery( - 'INSERT INTO Shipments (Name, Status) VALUES (\'Package A\', \'Pending\')' -); - -// UPDATE via ZCQL -await zcql.executeZCQLQuery( - 'UPDATE Shipments SET Status = \'Shipped\' WHERE ROWID = \'123456000000012345\'' -); - -// DELETE via ZCQL -await zcql.executeZCQLQuery( - 'DELETE FROM Shipments WHERE ROWID = \'123456000000012345\'' -); - -// --- Execute an OLAP query --- -// OLAP queries aggregate data across large datasets. -const olapResult = await zcql.executeOLAPQuery( - 'SELECT Status, COUNT(ROWID) AS cnt FROM Shipments GROUP BY Status' -); -``` - ---- - -## Cache - -```js -const cache = catalystApp.cache(); - -// --- Get a segment instance --- -const segment = cache.segment(segmentId); - -// --- Put a key-value pair --- -// Default expiry is 48 hours. Expiry is in milliseconds. -await segment.put('sessionToken', 'abc123xyz'); - -// With custom expiry (e.g., 1 hour = 3600000 ms) -await segment.put('tempData', 'value', 3600000); - -// --- Get a cached value --- -const value = await segment.getValue('sessionToken'); - -// Get full cache item details (key, value, expiry info) -const item = await segment.get('sessionToken'); - -// --- Update a cached value --- -await segment.update('sessionToken', 'newValue456'); - -// --- Delete a cached key --- -await segment.delete('sessionToken'); -``` - ---- - -## File Store - -```js -const filestore = catalystApp.filestore(); -const folder = filestore.folder(folderId); - -// --- Upload a file (max 100 MB) --- -const fs = require('fs'); -const fileStream = fs.createReadStream('/path/to/document.pdf'); -const uploadedFile = await folder.uploadFile({ - code: fileStream, - name: 'document.pdf' -}); -// uploadedFile.id contains the file ID - -// --- Download a file --- -const downloadStream = await folder.downloadFile(fileId); -// Pipe to response or write to disk: -downloadStream.pipe(res); - -// --- Delete a file --- -await folder.deleteFile(fileId); - -// --- Get file details --- -const fileDetails = await folder.getFileDetails(fileId); -``` - ---- - -## Authentication / User Management - -```js -// --- userManagement() is the primary accessor in newer SDK versions --- -const userManagement = catalystApp.userManagement(); - -// --- Get current user (user whose scope is active) --- -const currentUser = await userManagement.getCurrentUser(); - -// --- Register a new user --- -const newUser = await userManagement.registerUser({ - first_name: 'John', - last_name: 'Doe', - email_id: 'john@example.com', - role_id: '123456000000007003' // role ID from your project -}); - -// --- Get user details by user ID --- -const userDetails = await userManagement.getUserDetails(userId); - -// --- Delete a user --- -await userManagement.deleteUser(userId); - -// --- authentication() accessor for org-level operations --- -const auth = catalystApp.authentication(); - -// Get all org IDs associated with the project -const orgIds = await auth.getAllOrgIds(); - -// Add a user to an org -await auth.addUserToOrg(orgId, { - email_id: 'user@example.com', - role_id: roleId -}); - -// Get all users in an org -const orgUsers = await auth.getAllUsersInOrg(orgId); - -// Enable a user -await auth.enableUser(userId); - -// Disable a user -await auth.disableUser(userId); - -// Reset a user's password -await auth.resetPassword(userId); - -// Generate a server-side token -const token = await auth.generateServerToken(); -``` - ---- - -## Email - -```js -const mail = catalystApp.mail(); - -// --- Send an email --- -await mail.sendMail({ - from_email: 'noreply@yourdomain.com', - to_email: ['recipient@example.com'], - cc: ['cc@example.com'], // optional - bcc: ['bcc@example.com'], // optional - reply_to: ['replyto@example.com'],// optional - subject: 'Order Confirmation', - content: '

Thank you for your order!

Your order #1234 is confirmed.

', - attachments: [ // optional - { - name: 'invoice.pdf', - content: fs.createReadStream('/path/to/invoice.pdf') - } - ] -}); -``` - ---- - -## Search - -```js -const search = catalystApp.search(); - -// --- Execute a search query --- -// Searches indexed columns across specified tables. -const results = await search.executeSearchQuery({ - search: 'shipping delayed', - search_table_columns: { - Shipments: ['TrackingNotes', 'Description'], - Orders: ['CustomerName', 'OrderNotes'] - } -}); -``` - ---- - -## Connections - -```js -const connection = catalystApp.connection(); - -// --- Get connection credentials --- -// Retrieves OAuth tokens for a configured Catalyst Connection. -const credentials = await connection.getConnectorCredentials(connectorName); -// credentials.access_token is the OAuth access token -``` - ---- - -## Circuits - -```js -const circuit = catalystApp.circuit(); - -// --- Execute a circuit --- -const result = await circuit.execute(circuitId, { - key1: 'value1', - key2: 'value2' -}); -// result contains the circuit execution output -``` - ---- - -## NoSQL - -```js -const nosql = catalystApp.nosql(); -const { NoSQLItem } = require('zcatalyst-sdk-node/lib/no-sql'); - -// --- Get a table reference --- -const table = nosql.table('SessionStore'); - -// --- Insert items --- -const item = new NoSQLItem(); -item.put('userId', 'string', 'user_001'); -item.put('loginTime', 'number', Date.now()); -item.put('active', 'boolean', true); - -await table.insertItems([item]); - -// --- Fetch items by partition key --- -const fetched = await table.fetchItems({ - partitionKey: { name: 'userId', value: 'user_001' } -}); - -// --- Query table (using partition key and sort key conditions) --- -const queryResult = await table.queryTable({ - partitionKey: { name: 'userId', value: 'user_001' }, - sortKey: { - name: 'loginTime', - operator: 'GREATERTHAN', - value: 1700000000000 - }, - consistent_read: false, - limit: 50, - ascending: true -}); -// Supported operators: EQUALS, BETWEEN, GREATERTHAN, LESSERTHAN, -// GREATERTHANOREQUALTO, LESSERTHANOREQUALTO - -// --- Query a secondary index --- -const indexResult = await table.queryIndex('LoginTimeIndex', { - partitionKey: { name: 'active', value: true }, - sortKey: { - name: 'loginTime', - operator: 'BETWEEN', - value: [1700000000000, 1710000000000] - }, - limit: 100, - ascending: false -}); - -// --- Update items --- -const updateItem = new NoSQLItem(); -updateItem.put('userId', 'string', 'user_001'); -updateItem.put('loginTime', 'number', 1700000000001); -updateItem.put('active', 'boolean', false); - -await table.updateItems([updateItem]); - -// --- Delete items --- -await table.deleteItems([ - { partitionKey: 'user_001', sortKey: 1700000000001 } -]); -``` - ---- - -## Stratus Object Storage - -### Bucket Operations - -> ⚠️ **Stratus bucket names are globally unique** across ALL Catalyst projects and orgs. Generic names like `my-files` will likely already be taken. Use a project-specific suffix: `{app-name}-{project-id-prefix}` (e.g., `docvault-files-70699`). A `DUPLICATE_ENTRY` error on creation does NOT mean the bucket exists in your project — it may belong to another project entirely and will be inaccessible to you. - -```js -const stratus = catalystApp.stratus(); - -// --- Get a bucket instance --- -const bucket = stratus.bucket('myapp-files-70699'); // Use project-specific name - -// --- List all buckets --- -const buckets = await stratus.listBuckets(); - -// --- Get bucket details --- -const details = await bucket.getDetails(); - -// --- Get bucket CORS configuration --- -const cors = await bucket.getCors(); -``` - -### Listing Objects - -```js -// --- List paged objects --- -const pagedResult = await bucket.listPagedObjects({ - prefix: 'uploads/', // optional: filter by prefix - maxKeys: 100, // optional: max objects per page - continuationToken: token // optional: for pagination -}); - -// --- List iterable objects (async iterator) --- -for await (const obj of bucket.listIterableObjects({ prefix: 'uploads/' })) { - console.log(obj.key, obj.size); -} -``` - -### Downloading Objects - -```js -// --- Check object availability (HEAD) --- -const head = await bucket.headObject('uploads/report.pdf'); -// head includes content_length, content_type, last_modified, etc. - -// With options -const headVersioned = await bucket.headObject('uploads/report.pdf', { - versionId: 'v123', - throwErr: false // returns null instead of throwing if not found -}); - -// --- Download an object (GET) --- -const stream = await bucket.getObject('uploads/report.pdf'); -stream.pipe(fs.createWriteStream('/tmp/report.pdf')); - -// With range header (partial download) -const partialStream = await bucket.getObject('uploads/report.pdf', { - range: 'bytes=0-1023' -}); - -// --- TransferManager: range-based download --- -const transferManager = bucket.transferManager(); -const iterableStream = await transferManager.getIterableObject('uploads/large-file.zip', { - partSize: 10 * 1024 * 1024 // 10 MB per part -}); -const writeStream = fs.createWriteStream('/tmp/large-file.zip'); -for await (const chunk of iterableStream) { - writeStream.write(chunk); -} -writeStream.end(); -``` - -### Pre-Signed URLs - -```js -// --- Generate a pre-signed URL for GET (download) --- -const getUrl = await bucket.generatePreSignedUrl('uploads/report.pdf', { - expiresIn: 3600, // seconds until expiry - activationDate: new Date(), // optional: when the URL becomes active - versionId: 'v123' // optional: specific version -}); - -// --- Generate a pre-signed URL for PUT (upload) --- -const putUrl = await bucket.generatePreSignedUrl('uploads/new-file.pdf', { - expiresIn: 3600, - method: 'PUT' -}); -``` - -### Uploading Objects - -```js -// --- putObject with a stream --- -const uploadStream = fs.createReadStream('/path/to/file.pdf'); -await bucket.putObject('uploads/file.pdf', uploadStream); - -// --- putObject with a string --- -await bucket.putObject('configs/settings.json', JSON.stringify({ theme: 'dark' })); - -// --- putObject with options --- -await bucket.putObject('uploads/file.pdf', uploadStream, { - overwrite: true, // overwrite if exists (default behavior without versioning) - ttl: 86400, // time-to-live in seconds (auto-delete after 24h) - metaData: { // custom metadata - uploadedBy: 'automation', - category: 'reports' - }, - extractUpload: true // extract ZIP contents and upload each file separately -}); - -// --- Multipart upload (recommended for files >= 100 MB) --- -const multipart = bucket.multipart(); - -// Step 1: Initiate -const upload = await multipart.initiateMultipartUpload('uploads/huge-video.mp4'); -const uploadId = upload.uploadId; - -// Step 2: Upload parts (1 to 1000 parts allowed, min 5 MB per part except last) -const part1 = await multipart.uploadPart('uploads/huge-video.mp4', uploadId, { - partNumber: 1, - body: fs.createReadStream('/path/to/part1') -}); -const part2 = await multipart.uploadPart('uploads/huge-video.mp4', uploadId, { - partNumber: 2, - body: fs.createReadStream('/path/to/part2') -}); - -// Step 3: Complete multipart upload -await multipart.completeMultipartUpload('uploads/huge-video.mp4', uploadId, [ - { partNumber: 1, eTag: part1.eTag }, - { partNumber: 2, eTag: part2.eTag } -]); - -// --- TransferManager: automatic multipart upload --- -const tm = bucket.transferManager(); -await tm.putObjectAsParts('uploads/huge-video.mp4', fs.createReadStream('/path/to/video.mp4'), { - partSize: 10 * 1024 * 1024 // 10 MB per part -}); -``` - -### Object Management - -```js -// --- Unzip an object (extract in-place) --- -await bucket.unzipObject('uploads/archive.zip', { - targetPrefix: 'extracted/' // optional: extract to a specific prefix -}); - -// --- Rename / move an object --- -await bucket.renameObject('uploads/old-name.pdf', 'uploads/new-name.pdf'); - -// --- Delete a single object --- -await bucket.deleteObject('uploads/file.pdf'); - -// --- Delete multiple objects --- -await bucket.deleteObjects([ - { key: 'uploads/file1.pdf' }, - { key: 'uploads/file2.pdf', versionId: 'v456' } -]); - -// --- Truncate a bucket (delete all objects) --- -await bucket.truncate(); - -// --- Delete a path (all objects under a prefix) --- -await bucket.deletePath('uploads/temp/'); -``` - -### Object Versions - -```js -// --- List iterable versions (async iterator) --- -for await (const version of bucket.listIterableVersions({ prefix: 'uploads/report.pdf' })) { - console.log(version.versionId, version.lastModified, version.isLatest); -} -``` - ---- - -## Bulk Data Store Operations - -```js -const datastore = catalystApp.datastore(); -const table = datastore.table('Shipments'); - -// --- Bulk Read --- -// Creates a bulk read job that generates a CSV file with the results. -const bulkReadJob = table.bulkJob('read'); -const readResult = await bulkReadJob.createJob({ - table_identifier: 'Shipments', - criteria: { // optional: filter rows - group_operator: 'AND', - group: [ - { - column: 'Status', - comparator: 'equal', - value: 'Active' - } - ] - } -}); -// Poll readResult.job_id for status; download CSV when complete. -const jobStatus = await bulkReadJob.getJobStatus(readResult.job_id); - -// --- Bulk Write --- -// Upload CSV data first to Stratus, then reference it in the write job. -const bulkWriteJob = table.bulkJob('write'); -const writeResult = await bulkWriteJob.createJob({ - table_identifier: 'Shipments', - file_details: { - bucketName: 'my-bucket', - objectKey: 'imports/shipments.csv', - versionID: 'v1' // optional: if versioning is enabled - } -}); - -// --- Bulk Delete (max 200 rows per call) --- -const rowIds = [ - '123456000000012345', - '123456000000012346', - '123456000000012347' -]; -await table.deleteRows(rowIds); -``` - ---- - -## Push Notifications - -```js -const pushNotification = catalystApp.pushNotification(); - -// --- Send web push notification (up to 50 users per call) --- -const webNotif = pushNotification.web(); -await webNotif.sendNotification({ - message: 'Your order has been shipped!', - recipients: [userId1, userId2] // user IDs or email addresses -}); - -// --- Send mobile push notification --- -const mobileNotif = pushNotification.mobile(appId); - -// Android notification -await mobileNotif.sendAndroidNotification({ - message: 'New update available', - recipients: [userId], - additional_data: { // optional: custom payload - orderId: '12345' - } -}); - -// iOS notification -await mobileNotif.sendIOSNotification({ - message: 'New update available', - recipients: [userId], - badge_count: 1, // optional - additional_data: { - orderId: '12345' - } -}); -``` - ---- - -## Zia Services - -### OCR (Optical Character Recognition) - -```js -const zia = catalystApp.zia(); - -// --- Extract text from an image or document --- -const ocrResult = await zia.extractOpticalCharacters({ - image_url: 'https://example.com/receipt.jpg' - // OR pass a file stream: - // image: fs.createReadStream('/path/to/receipt.jpg') -}); -``` - -### AutoML - -```js -// --- Execute a prediction using a trained AutoML model --- -// Supports Binary Classification, Multi-Class Classification, and Regression. -const prediction = await zia.executeAutoML(modelId, { - feature1: 'value1', - feature2: 42, - feature3: true -}); -``` - -### Sentiment Analysis - -```js -const sentiment = await zia.getSentimentAnalysis([ - 'The product quality is amazing!', - 'Delivery was too slow and the packaging was damaged.' -]); -// Each result has: sentiment (positive/negative/neutral), confidence -``` - -### Named Entity Recognition (NER) - -```js -const entities = await zia.getNamedEntityRecognition([ - 'John Doe from Acme Corp signed the contract on January 15, 2025.' -]); -// Extracts entities like person names, organizations, dates, etc. -``` - -### Keyword Extraction - -```js -const keywords = await zia.getKeywordExtraction([ - 'Machine learning models improve predictive accuracy in healthcare diagnostics.' -]); -// Returns keywords and keyphrases -``` - -### All Text Analytics (combined) - -```js -const allAnalytics = await zia.getAllTextAnalytics([ - 'The quarterly revenue exceeded expectations, driven by strong sales in APAC.' -]); -// Returns sentiment, NER, and keyword extraction in one call -``` - -### Image Moderation - -```js -const moderation = await zia.moderateImage({ - image_url: 'https://example.com/photo.jpg' - // OR: image: fs.createReadStream('/path/to/photo.jpg') -}); -// Returns confidence scores for nudity, violence, etc. -``` - -### Face Detection - -```js -const faces = await zia.detectFaces({ - image_url: 'https://example.com/group-photo.jpg' -}); -// Returns detected faces with age, gender, emotion, bounding box -``` - -### Object Recognition - -```js -const objects = await zia.recognizeObjects({ - image_url: 'https://example.com/scene.jpg' -}); -// Returns identified objects with confidence scores -``` - -### Barcode Scanning - -```js -const barcodes = await zia.scanBarcode({ - image_url: 'https://example.com/barcode.jpg' -}); -// Supports Codabar, EAN-13, ITF, UPC-A, QR Code, and more -``` - ---- - -## SmartBrowz - -```js -const smartBrowz = catalystApp.SmartBrowz(); - -// --- Browser Grid --- -const grid = smartBrowz.browserGrid(); - -// Get all browser grids -const allGrids = await grid.getAllGrids(); - -// Get a specific grid by ID -const specificGrid = await grid.getGrid(gridId); - -// Get a specific node in a grid -const node = await grid.getNode(gridId, nodeId); - -// Stop a browser grid -await grid.stopGrid(gridId); - -// --- Generate PDF --- -const pdfBuffer = await smartBrowz.generatePDF({ - url: 'https://example.com/report', // Generate from URL - // OR: html: '

Hello

', // Generate from HTML - // OR: template_id: 'tpl_123', // Generate from template - output: { - format: 'A4', - landscape: false, - print_background: true - }, - password: 'secret123' // optional: password-protect PDF -}); - -// --- Generate Screenshot --- -const screenshotBuffer = await smartBrowz.generateScreenshot({ - url: 'https://example.com/dashboard', - output: { - format: 'png', // 'png' or 'webp' - full_page: true, - quality: 90 - } -}); - -// --- Dataverse (web scraping/extraction) --- -const extractedData = await smartBrowz.dataverse({ - url: 'https://example.com/products', - extraction_rules: { - title: { selector: 'h1.product-title', type: 'text' }, - price: { selector: '.price', type: 'text' } - } -}); -``` - ---- - -## Job Scheduling - -### Jobs - -```js -const jobScheduling = catalystApp.jobScheduling(); - -// --- Submit a job targeting a Function --- -const jobResult = await jobScheduling.submitJob({ - job_name: 'process-orders', - jobpool_name: 'OrderPool', - target_type: 'Function', - target_name: 'ProcessOrderFunction', - job_config: { // optional: custom params passed to the target - batchSize: 50, - region: 'US' - } -}); - -// --- Submit a job targeting a Circuit --- -await jobScheduling.submitJob({ - job_name: 'etl-pipeline', - jobpool_name: 'DataPool', - target_type: 'Circuit', - target_name: 'ETLCircuit' -}); - -// --- Submit a job targeting a Webhook --- -await jobScheduling.submitJob({ - job_name: 'notify-webhook', - jobpool_name: 'NotifyPool', - target_type: 'Webhook', - target_name: 'SlackWebhook' -}); - -// --- Submit a job targeting an AppSail service --- -await jobScheduling.submitJob({ - job_name: 'cleanup-task', - jobpool_name: 'MaintenancePool', - target_type: 'AppSail', - target_name: 'CleanupService' -}); - -// --- Delete a job --- -await jobScheduling.deleteJob(jobId); -``` - -### Crons - -```js -const cron = jobScheduling.cron(); - -// --- Create a One-Time cron --- -const oneTimeCron = await cron.createCron({ - cron_name: 'one-time-report', - description: 'Generate end-of-month report', - jobpool_name: 'ReportPool', - cron_type: 'OneTime', - schedule: { - time: '2025-12-31T23:59:00Z' - }, - job_config: { reportType: 'monthly' } -}); - -// --- Create a Periodic/Every cron (recurring, < 24h interval) --- -const periodicCron = await cron.createCron({ - cron_name: 'health-check', - description: 'Check system health every 15 minutes', - jobpool_name: 'MonitorPool', - cron_type: 'Periodic', - schedule: { - every: 15, - unit: 'minutes' // 'minutes' or 'hours' - } -}); - -// --- Create a Daily/Calendar cron --- -const dailyCron = await cron.createCron({ - cron_name: 'daily-digest', - description: 'Send daily email digest at 9 AM', - jobpool_name: 'EmailPool', - cron_type: 'Calendar', - schedule: { - time: '09:00', - timezone: 'Asia/Kolkata', - days_of_week: ['MON', 'TUE', 'WED', 'THU', 'FRI'] - } -}); - -// --- Create a Monthly cron --- -const monthlyCron = await cron.createCron({ - cron_name: 'monthly-invoice', - description: 'Generate invoices on the 1st of each month', - jobpool_name: 'BillingPool', - cron_type: 'Calendar', - schedule: { - time: '00:00', - timezone: 'UTC', - days_of_month: [1] - } -}); - -// --- Create a Yearly cron --- -const yearlyCron = await cron.createCron({ - cron_name: 'annual-cleanup', - description: 'Yearly data archival', - jobpool_name: 'ArchivePool', - cron_type: 'Calendar', - schedule: { - time: '02:00', - timezone: 'UTC', - months: ['JAN'], - days_of_month: [1] - } -}); - -// --- Create a cron using cron expressions --- -const exprCron = await cron.createCron({ - cron_name: 'custom-schedule', - jobpool_name: 'CustomPool', - cron_type: 'CronExpression', - schedule: { - cron_expression: '0 */6 * * *' // every 6 hours - } -}); - -// --- Cron management --- -const cronDetails = await cron.getCron(cronId); -await cron.updateCron(cronId, { description: 'Updated description' }); -await cron.pauseCron(cronId); -await cron.resumeCron(cronId); -await cron.runCron(cronId); // manually trigger a cron immediately -await cron.deleteCron(cronId); -``` - ---- - -## Pipelines - -```js -const pipeline = catalystApp.pipeline(); - -// --- Get pipeline details --- -const details = await pipeline.getPipelineDetails(pipelineId); - -// --- Execute a pipeline --- -const result = await pipeline.executePipeline(pipelineId, { - inputParam1: 'value1', - inputParam2: 'value2' -}); -``` - ---- - -## Connectors - -```js -const connector = catalystApp.connector(); - -// --- Get a connector token --- -// Retrieves OAuth credentials for a configured Catalyst Connector. -const tokenDetails = await connector.getConnectorToken(connectorName); -// tokenDetails.access_token contains the usable OAuth access token -``` diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/sdk-python.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/sdk-python.md deleted file mode 100644 index 119611664..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/sdk-python.md +++ /dev/null @@ -1,518 +0,0 @@ -# Python SDK Reference (zcatalyst-sdk) - -External docs: https://docs.catalyst.zoho.com/en/sdk/python/v1/overview/ - -## Installation - -```bash -pip install zcatalyst-sdk -``` - -Requires **Python 3.9+**. - ---- - -## Initialization - -```python -import zcatalyst_sdk - -# --- Advanced I/O (Flask) --- -# In a Flask-based Advanced I/O function, pass the Flask request object: -catalyst_app = zcatalyst_sdk.initialize(req=request) - -# --- Basic I/O --- -# In a Basic I/O function, pass the context object: -catalyst_app = zcatalyst_sdk.initialize(req=context) - -# --- Event / Cron Functions --- -# Same pattern — pass the context/event object provided by the runtime: -catalyst_app = zcatalyst_sdk.initialize(req=context) - -# --- Admin Scope --- -# For operations that require admin-level access (e.g., user management): -admin_app = zcatalyst_sdk.initialize(req=request, scope='admin') -``` - ---- - -## Data Store - -```python -# Get a table reference -table = catalyst_app.datastore().table("TableName") - -# Insert a single row -row = table.insert_row({ - "Name": "Alice", - "Email": "alice@example.com" -}) - -# Insert multiple rows -rows = table.insert_rows([ - {"Name": "Bob", "Email": "bob@example.com"}, - {"Name": "Carol", "Email": "carol@example.com"} -]) - -# Get a single row by ROWID -row = table.get_row(row_id) - -# Get paged rows (paginated) -# Returns dict with keys: data, next_token, more_records -result = table.get_paged_rows( - next_token="token_string", # optional, for subsequent pages - max_rows=200 # optional, default varies -) -rows = result["data"] -has_more = result["more_records"] -next_token = result["next_token"] - -# Update a row (ROWID is required in the dict) -updated_row = table.update_row({ - "ROWID": "123456000000012345", - "Name": "Alice Updated" -}) - -# Delete a row by ROWID -table.delete_row(row_id) -``` - ---- - -## ZCQL - -```python -zcql_service = catalyst_app.zcql() - -# Execute a standard query -rows = zcql_service.execute_query("SELECT * FROM TableName WHERE Name = 'Alice'") - -# Execute an OLAP query (aggregations, joins, etc.) -result = zcql_service.execute_olap_query("SELECT COUNT(ROWID) FROM TableName GROUP BY Status") -``` - ---- - -## Cache - -```python -cache_service = catalyst_app.cache() - -# Get a cache segment by ID -segment = cache_service.segment(segment_id) - -# Put a value (with optional expiry in milliseconds) -segment.put("my_key", "my_value", expiry=3600000) - -# Get a value -value = segment.get("my_key") - -# Update a value -segment.update("my_key", "new_value", expiry=7200000) - -# Delete a value -segment.delete("my_key") -``` - ---- - -## File Store - -```python -filestore_service = catalyst_app.filestore() - -# Get a folder reference by ID -folder = filestore_service.folder(folder_id) - -# Upload a file (use 'rb' mode) -with open("/path/to/file.pdf", "rb") as f: - uploaded = folder.upload_file(f) - -# Download a file by file ID -file_content = folder.download_file(file_id) - -# Delete a file by file ID -folder.delete_file(file_id) - -# Get file details -details = folder.get_details(file_id) -``` - ---- - -## Authentication - -```python -auth_service = catalyst_app.authentication() - -# Register a new user -signup_config = { - "platform_type": "web", - "zaid": "your_zaid" -} -user_details = { - "first_name": "Alice", - "last_name": "Smith", - "email_id": "alice@example.com" -} -result = auth_service.register_user(signup_config, user_details) - -# Get current user details -user = auth_service.get_user_details() - -# Delete a user by user ID -auth_service.delete_user(user_id) -``` - ---- - -## Email - -```python -email_service = catalyst_app.email() - -email_service.send_mail({ - "from_email": "noreply@yourdomain.com", - "to_email": ["recipient@example.com"], - "cc": ["cc@example.com"], # optional - "bcc": ["bcc@example.com"], # optional - "reply_to": "reply@example.com", # optional - "subject": "Hello from Catalyst", - "content": "

Welcome!

This is a test email.

", - "html_mode": True # optional, defaults to True -}) -``` - ---- - -## Search - -```python -search_service = catalyst_app.search() - -result = search_service.execute_search_query( - "search term", - search_config={ - "search_table_columns": { - "TableName": ["ColumnName1", "ColumnName2"] - } - } -) -``` - ---- - -## Connections - -```python -conn_service = catalyst_app.connections() - -# Get OAuth credentials for a configured connection -credentials = conn_service.get_connection_credentials({ - "connection_name": "my_connection" -}) -# credentials contains access_token, etc. -``` - ---- - -## Circuits - -```python -circuit_service = catalyst_app.circuit() - -# Execute a circuit by ID with input data -result = circuit_service.execute(circuit_id, { - "key1": "value1", - "key2": "value2" -}) -``` - ---- - -## NoSQL - -```python -nosql_service = catalyst_app.nosql() - -# Get a table reference -table = nosql_service.table("NoSQLTableName") - -# Insert items -table.insertItems([ - {"pk": "partition1", "sk": "sort1", "data": "value1"}, - {"pk": "partition2", "sk": "sort2", "data": "value2"} -]) - -# Fetch items by keys -items = table.fetchItems([ - {"pk": "partition1", "sk": "sort1"} -]) - -# Query a table (by partition key) -results = table.queryTable({ - "pk": "partition1", - "query": { - "condition": "sk BEGINS_WITH 'sort'", - "limit": 10 - } -}) - -# Query a secondary index -results = table.queryIndex({ - "index_name": "MyIndex", - "pk": "index_partition_value", - "query": { - "condition": "sk BEGINS_WITH 'prefix'" - } -}) - -# Update items -table.updateItems([ - { - "pk": "partition1", - "sk": "sort1", - "update_expression": "SET data = :val", - "expression_values": {":val": "updated_value"} - } -]) - -# Delete items -table.deleteItems([ - {"pk": "partition1", "sk": "sort1"} -]) -``` - ---- - -## Stratus (Object Storage) - -```python -stratus_service = catalyst_app.stratus() - -# List all buckets -buckets = stratus_service.list_buckets() - -# Get a bucket reference -bucket = stratus_service.bucket(bucket_name) - -# Get bucket details -details = bucket.get_details() - -# List objects in a bucket -objects = bucket.list_objects(prefix="folder/", max_keys=100) - -# Upload an object -with open("/path/to/file.txt", "rb") as f: - bucket.upload_object("folder/file.txt", f, content_type="text/plain") - -# Download an object -content = bucket.download_object("folder/file.txt") - -# Delete an object -bucket.delete_object("folder/file.txt") - -# Rename an object -bucket.rename_object("folder/old_name.txt", "folder/new_name.txt") -``` - ---- - -## Bulk Data Store - -```python -datastore = catalyst_app.datastore() - -# Bulk Read — create a bulk read job for a table -bulk_read_job = datastore.bulkRead({ - "table_id": table_id, - "query": { - "criteria": { - "column_name": "Status", - "comparator": "equal", - "value": "active" - } - } -}) -# Poll job status and download CSV when complete - -# Bulk Write — upload a CSV to bulk insert/update -bulk_write_job = datastore.bulkWrite({ - "table_id": table_id, - "operation": "insert", # or "update" - "file_id": uploaded_file_id -}) - -# Bulk Delete — delete multiple rows -datastore.bulkDeleteRows(table_id, [row_id_1, row_id_2, row_id_3]) -``` - ---- - -## Push Notifications - -```python -push_service = catalyst_app.pushnotification() - -# Send a web push notification -push_service.sendNotification({ - "subject": "New Update", - "message": "A new feature has been released.", - "recipients": ["user_id_1", "user_id_2"] -}) - -# Send a mobile push notification -push_service.sendMobileNotification({ - "message": "Your order has shipped!", - "recipients": ["user_id_1"], - "additional_data": {"order_id": "12345"} -}) -``` - ---- - -## Zia Services - -```python -zia_service = catalyst_app.zia() - -# OCR — Extract text from an image -with open("document.png", "rb") as f: - ocr_result = zia_service.extractOpticalCharacters(f, { - "language": "eng", - "model_type": "OCR" - }) - -# AutoML — Execute an AutoML model prediction -automl_result = zia_service.executeAutoML(model_id, { - "feature1": "value1", - "feature2": 42 -}) - -# Sentiment Analysis -sentiment = zia_service.getSentimentAnalysis(["I love this product!", "Terrible experience."]) - -# Named Entity Recognition -entities = zia_service.getNamedEntityRecognition(["Zoho Corporation is based in Chennai, India."]) - -# Keyword Extraction -keywords = zia_service.getKeywordExtraction(["Catalyst is a serverless platform for building applications."]) - -# All Text Analytics (sentiment + NER + keywords combined) -analytics = zia_service.getAllTextAnalytics(["Zoho Catalyst makes development easy and fast."]) - -# Image Moderation -with open("image.jpg", "rb") as f: - moderation = zia_service.moderateImage(f) - -# Face Detection -with open("photo.jpg", "rb") as f: - faces = zia_service.detectFaces(f) - -# Object Recognition -with open("scene.jpg", "rb") as f: - objects = zia_service.recognizeObjects(f) - -# Barcode Scanning -with open("barcode.png", "rb") as f: - barcode = zia_service.scanBarcode(f) -``` - ---- - -## SmartBrowz - -```python -smart_browz_service = catalyst_app.smart_browz() - -# Generate output from an HTML template -output = smart_browz_service.generate_output_from_template({ - "template_id": template_id, - "template_data": {"name": "Alice", "amount": "$100"}, - "output_type": "pdf" -}) - -# Convert a URL or HTML content to PDF -pdf = smart_browz_service.convert_to_pdf({ - "url": "https://example.com", - "pdf_options": { - "format": "A4", - "print_background": True, - "margin": {"top": "1cm", "bottom": "1cm", "left": "1cm", "right": "1cm"} - }, - "page_options": { - "width": 1280, - "height": 800 - }, - "navigation_options": { - "wait_until": "networkidle0", - "timeout": 30000 - } -}) - -# Take a screenshot of a URL -screenshot = smart_browz_service.take_screenshot({ - "url": "https://example.com", - "screenshot_options": { - "full_page": True, - "type": "png", - "quality": 80, - "clip": {"x": 0, "y": 0, "width": 1280, "height": 800} - }, - "page_options": { - "width": 1440, - "height": 900 - }, - "navigation_options": { - "wait_until": "networkidle2", - "timeout": 60000 - } -}) -``` - ---- - -## Job Scheduling - -```python -job_service = catalyst_app.job_scheduling() - -# Get a pool reference by ID -pool = job_service.pool(pool_id) - -# Submit a one-time job -job = pool.submit_job({ - "job_name": "data_sync", - "target_function": "sync_function", - "params": {"source": "db1", "target": "db2"}, - "schedule": "one_time" -}) - -# Create a cron job — various schedule types -# Interval-based cron -cron = pool.create_cron({ - "cron_name": "hourly_cleanup", - "target_function": "cleanup_function", - "cron_type": "interval", - "repeat_interval": 3600, # seconds - "params": {"retain_days": 30} -}) - -# Calendar-based cron (specific time) -cron = pool.create_cron({ - "cron_name": "daily_report", - "target_function": "generate_report", - "cron_type": "calendar", - "cron_expression": "0 9 * * *", # every day at 9 AM - "params": {"report_type": "daily_summary"} -}) - -# Fixed-delay cron -cron = pool.create_cron({ - "cron_name": "queue_processor", - "target_function": "process_queue", - "cron_type": "fixed_delay", - "repeat_interval": 300, # 5 minutes after previous run completes - "params": {} -}) -``` diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/sdk-web.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/sdk-web.md deleted file mode 100644 index 3dd67ce23..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/sdk-web.md +++ /dev/null @@ -1,506 +0,0 @@ -# Catalyst Web SDK v4 Reference - -> **Docs:** https://docs.catalyst.zoho.com/en/sdk/web/ - ---- - -## Setup - -### Script Tags - -Add both scripts to your HTML ``: - -```html - - -``` - -The `init.js` script auto-initializes the SDK with the current project context. Always load it after `catalystWebSDK.js`. - -### client-package.json - -This file tells Catalyst where to redirect after login. Its placement depends on your framework: - -| Framework | Place `client-package.json` in… | Why | -|-----------|--------------------------------|-----| -| **Vite / React / Vue** | `public/client-package.json` | Vite copies `public/` to `dist/` at build time | -| **Next.js** | `public/client-package.json` | Next.js serves `public/` as static assets | -| **Angular** | `src/assets/client-package.json` | Angular copies `assets/` to the build output | -| **Legacy Web Client (`client/`)** | `client/client-package.json` | Served directly from the client root | - -For Slate apps, use `/` as the path (not `/app/index.html` — that's the legacy Web Client pattern): - -```json -{ - "name": "my-app", - "version": "1.0.0", - "description": "My Catalyst application", - "homepage": "/", - "login_redirect": "/" -} -``` - -- `homepage` — default landing page after login -- `login_redirect` — where to redirect after successful authentication - -> ⚠️ **Do NOT place this in the project root alongside `vite.config.js`** — it won't be included in the build output. It must be in a directory that your build tool copies to the output folder (e.g., `public/` for Vite). - -### Response Pattern - -All SDK methods return a promise that resolves to: - -```js -{ - status: 200, // HTTP status code - content: { ... }, // response payload - message: "OK" // status message -} -``` - -### Version Compatibility - -| Feature | Minimum SDK Version | -|---------------------------------|---------------------| -| Core SDK | v4.0.0 | -| `changePassword()` | v4.3.0 | -| `isUserAuthenticated()` (local) | v4.5.0 | -| `generateAuthToken()` | v4.6.1 | - ---- - -## Authentication - -Catalyst supports two authentication types for client apps. **Ask the user which they prefer** before recommending a pattern. - -### Auth Type 1: Hosted Login (Redirect-Based) - -The standard approach. Uses Catalyst's built-in login page at `/__catalyst/auth/login`. - -> ⚠️ **Console prerequisite:** You must enable Hosted Authentication in the Catalyst console first: **Console → Authentication → Login → enable Hosted Authentication**. Without this, `/__catalyst/auth/login` returns a 404. - -- No `signIn()` call needed — use `isUserAuthenticated()` to check, then redirect manually on failure -- After login, the user is redirected back to `login_redirect` from `client-package.json` -- Best for standard web apps where you want Zoho to handle the full login UI - -```js -// Check auth status and redirect manually if not authenticated. -// The SDK does NOT auto-redirect — you must handle the .catch() yourself. -catalyst.auth.isUserAuthenticated().then(result => { - // result.content contains the full user object - console.log(result.content.email_id); - console.log(result.content.first_name); - showApp(result.content); -}).catch(err => { - // User is not logged in — redirect to Catalyst's hosted login page. - // The SDK does NOT auto-redirect. You must do this explicitly. - window.location.href = '/__catalyst/auth/login'; -}); -``` - -> ⚠️ **`catalyst.auth.getCurrentUser()` does NOT exist** in the Web SDK. Use `isUserAuthenticated()` instead — it returns the full user object on success (see below). - -### Auth Type 2: Embedded Login (iFrame) - -Renders login/signup forms inside your page via an iFrame. - -```js -// Sign In -catalyst.auth.signIn("login-div", { - login_redirect: "/" // Use "/" for Slate apps, "/app/index.html" for legacy Web Client -}); - -// Sign Up -catalyst.auth.signUp("signup-div"); - -// Forgot Password -catalyst.auth.forgotPassword("forgot-div"); - -// Change Password (v4.3.0+) -catalyst.auth.changePassword("change-pwd-div"); -``` - -The first argument is the `id` of a `
` element where the iFrame will render. - -#### iFrame CSS Customization - -You can customize the embedded auth iFrame appearance: - -- Download the default CSS from the Catalyst console (Settings > Authentication > Customize) -- Target selectors: `.zc-login-form`, `.zc-btn-primary`, `.zc-input`, `.zc-signup-link` -- Customize palette colors, fonts, button styles, and input fields -- Upload the modified CSS back through the console - -### isUserAuthenticated() - -Check if the current user is authenticated and get their details (v4.5.0+): - -```js -try { - const result = await catalyst.auth.isUserAuthenticated(); - // On success: result.content is the FULL USER OBJECT (not a boolean) - console.log(result.content.email_id); // "user@example.com" - console.log(result.content.first_name); // "John" - console.log(result.content.last_name); // "Doe" - console.log(result.content.user_id); // "10103000000115057" - console.log(result.content.time_zone); // "Asia/Kolkata" - console.log(result.content.created_time);// "Jul 05, 2023 10:30 AM" -} catch (err) { - // On failure: rejects with a 401 error when user is NOT authenticated. - // The SDK does NOT auto-redirect. You must redirect manually: - window.location.href = '/__catalyst/auth/login'; -} -``` - -> ⚠️ **This does NOT return a boolean.** It resolves with the full user object on success, and **rejects** (throws) on failure. This is the primary way to get the current user in the Web SDK. - -> ⚠️ **`catalyst.auth.getCurrentUser()` does NOT exist** in the Web SDK. `isUserAuthenticated()` is the correct method — it serves both purposes (auth check + user details). - -### Sign Out - -Sign the user out by calling `signOut()` with a redirect URL. This is a single call — it handles session invalidation and navigation internally. - -```js -// Pass the URL to redirect to after sign-out completes. -// This does NOT return a promise — it navigates away immediately. -// Use window.location.origin for Slate apps (served at root /) -// Use window.location.origin + '/app/index.html' only for legacy Web Client Hosting -const redirectURL = window.location.origin; -catalyst.auth.signOut(redirectURL); -``` - -> ⚠️ **`signOut()` requires a redirect URL argument.** Calling it with no arguments crashes because the SDK internally calls `.startsWith("/")` on `undefined`. - -> ⚠️ **`constructSignOutUrl()` does NOT exist.** Do not use a two-step pattern — `signOut(redirectURL)` handles everything in one call. - -> ⚠️ **This does NOT return a promise.** Do not `await` it — the browser navigates away immediately. - -### generateAuthToken() (v4.6.1+) - -Generate a short-lived auth token for cross-domain requests (e.g., calling Serverless Functions or AppSail from a Slate app): - -```js -const tokenResponse = await catalyst.auth.generateAuthToken(); -const token = tokenResponse.access_token; -``` - -> ⚠️ **The token is at `tokenResponse.access_token`** — NOT `tokenResponse.content.token`. This method does NOT follow the standard `{status, content, message}` response pattern used by other SDK methods. - -### JWT Sign-In - -For custom authentication flows using JWT tokens: - -```js -await catalyst.auth.signinWithJwt(jwtToken); -``` - -### Local Dev vs Production Auth - -| Behavior | Local Dev (`catalyst serve`) | Production (deployed) | -|-----------------------------|------------------------------------|--------------------------------| -| Auth cookie domain | `localhost` | `.catalystserverless.com` | -| `isUserAuthenticated()` | Checks local dev session | Checks Catalyst auth cookie | -| Login redirect | Opens Zoho login in browser | Automatic redirect | -| Cross-domain token | Not needed (same origin) | Use `generateAuthToken()` | -| CORS | Not enforced | Must whitelist in AppSail | - -### Calling AppSail from Slate (Cross-Domain Pattern) - -When calling an AppSail endpoint from a Slate app, you need to pass an auth token since they are on different subdomains. - -**Helper function:** - -```js -async function callAppSail(endpoint, method = "GET", body = null) { - const tokenResponse = await catalyst.auth.generateAuthToken(); - const token = tokenResponse.access_token; - - const options = { - method: method, - headers: { - "Content-Type": "application/json", - "Authorization": token // Raw token — no prefix needed - } - }; - - if (body) { - options.body = JSON.stringify(body); - } - - const response = await fetch( - `https://your-app.catalystserverless.com${endpoint}`, - options - ); - return response.json(); -} - -// Usage -const data = await callAppSail("/api/items"); -const result = await callAppSail("/api/items", "POST", { name: "New Item" }); -``` - -**Required configuration:** - -- **Authorized Domains:** Add your Slate domain in Console → Authentication → Authorized Domains → enable CORS toggle. The Catalyst gateway will inject `Access-Control-Allow-Origin` automatically. -- **No `cors()` middleware:** Do NOT add Express `cors()` middleware in your backend code — Catalyst's gateway handles CORS at the platform level. Adding middleware causes **duplicate `Access-Control-Allow-Origin` headers**, which browsers reject. - -### ⚠️ Calling Advanced I/O Functions from Slate (Cross-Domain — Required) - -> **This is the most common blocker when combining Slate with Advanced I/O functions.** - -Slate apps are served from `*.onslate.com`. Advanced I/O functions are on `*.catalystserverless.com`. **These are different domains.** This means: - -- **Relative paths like `/server/{function_name}/execute` DO NOT work** — they resolve to `onslate.com/server/...` which doesn't exist. Slate serves `index.html` for all unknown routes, so you get HTML back instead of JSON, causing `Unexpected token '<', " **Tip:** The `{project-domain}` is in `.catalystrc` → `project_domain`. Example: `myapp-60019947973.development.catalystserverless.com`. - -**What happens under the hood (the gateway flow):** - -``` -1. Frontend: generateAuthToken() → gets access_token → sends as Authorization header -2. Catalyst Gateway: validates token → strips Authorization → injects internal headers: - - x-zc-user-cred-type, x-zc-user-cred-token, x-zc-user-type (user identity) - - x-zc-admin-cred-type, x-zc-admin-cred-token (admin credentials) - - x-zc-projectid, x-zc-project-key, x-zc-environment (project context) - Also injects: Access-Control-Allow-Origin (from Authorized Domains config) -3. Function: catalyst.initialize(req) reads the x-zc-* headers directly from req.headers -4. Function: userManagement().getCurrentUser() makes internal API call using the user token -``` - -> ⚠️ **The `Authorization` header your frontend sends is NOT available in `req.headers` inside the function.** The gateway strips it after validation. The SDK reads the injected `x-zc-*` headers instead. Do not try to read `req.headers['authorization']` — it will be `undefined`. - -**CORS rule for functions with Express (e.g., Advanced I/O with Express router):** - -The gateway owns CORS headers for all production/deployed origins. Your Express code should only handle CORS for localhost (local dev, where no gateway exists): - -```js -app.use((req, res, next) => { - const origin = req.headers.origin || ''; - if (/^http:\/\/localhost(:\d+)?$/.test(origin)) { - res.setHeader('Access-Control-Allow-Origin', origin); - res.setHeader('Access-Control-Allow-Credentials', 'true'); - res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); - res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); - if (req.method === 'OPTIONS') return res.status(204).end(); - } - next(); -}); -``` - ---- - -## Data Store - -### Table Reference - -```js -const table = catalyst.table.tableId('TableName'); -``` - -### Operations - -```js -// Get all rows -const allRows = await table.getAll(); - -// Get paged rows -const pagedRows = await table.getPagedRows({ nextToken: null, maxRows: 100 }); - -// Get column metadata -const columns = await table.getColumns(); - -// Add a row -const newRow = await table.addRow({ - column1: "value1", - column2: "value2" -}); - -// Update a row (ROWID required) -const updated = await table.updateRow({ - ROWID: "12345", - column1: "new_value" -}); - -// Delete a single row -await table.delete("12345"); - -// Bulk delete (max 200 rows per call) -await table.deleteRows(["12345", "12346", "12347"]); -``` - -> **Note:** Table operations respect the permissions configured in the Catalyst console (read, write, delete) for the current user role. - ---- - -## ZCQL - -### Execute a Query - -```js -const zcql = catalyst.ZCatalystQL; -const result = await zcql.executeQuery("SELECT * FROM Users WHERE age > 25"); -console.log(result.content); -``` - -### V2 Environment - -For ZCQL V2 features, set the environment: - -```js -catalyst.ZCatalystQL.setCatalystEnv("V2"); -const result = await catalyst.ZCatalystQL.executeQuery("SELECT * FROM Users LIMIT 10"); -``` - ---- - -## File Store - -```js -const fileStore = catalyst.file; - -// Get all folders -const folders = await fileStore.getAllFolder(); - -// Get a folder reference -const folder = fileStore.folderId("folderId"); - -// Upload a file -const fileInput = document.getElementById("file-input"); -const uploaded = await folder.uploadFile(fileInput.files[0]); - -// Get download link -const downloadLink = await folder.getDownloadLink("fileId"); - -// Delete a file -await folder.delete("fileId"); -``` - ---- - -## Stratus (Object Storage) - -```js -const bucket = catalyst.stratus.bucket("bucket-name"); - -// Check if object exists (head) -const head = await bucket.headObject("path/to/file.txt"); - -// Get object (signed URL) -const obj = await bucket.getObject("path/to/file.txt", { signedUrl: true }); - -// Upload object (simple) -const file = document.getElementById("file-input").files[0]; -await bucket.putObject("path/to/file.txt", file); - -// Upload object (multipart, for large files) -await bucket.uploadObject("path/to/large-file.zip", file, { - partSize: 5 * 1024 * 1024 // 5MB parts -}); - -// Delete object -await bucket.deleteObject("path/to/file.txt"); -``` - ---- - -## Search - -```js -const search = catalyst.search; -const results = await search.executeSearchQuery("search term"); -console.log(results.content); -``` - ---- - -## Push Notifications - -```js -const push = catalyst.push; -await push.sendNotification({ - message: "Hello from Catalyst!", - recipients: ["user@example.com"] -}); -``` - ---- - -## Functions - -```js -const func = catalyst.function; - -// Execute a function -const result = await func.execute("functionName", { - key1: "value1", - key2: "value2" -}); -console.log(result.content); -``` - ---- - -## Environment Variables - -```js -const env = catalyst.env; - -// Get a variable -const value = await env.getValue("MY_ENV_VAR"); - -// Get all variables -const allVars = await env.getAll(); -``` - ---- - -## Common Auth Errors - -| Error / Symptom | Cause | Fix | -|----------------------------------------------|------------------------------------------------------------|---------------------------------------------------------------------------------------------| -| `api_domain` is empty | `init.js` not loaded or loaded before `catalystWebSDK.js` | Ensure both scripts are in ``, `catalystWebSDK.js` first | -| `isUserAuthenticated` fails locally | SDK version below v4.5.0 | Upgrade to v4.5.0+ | -| `generateAuthToken is not a function` | SDK version below v4.6.1 | Upgrade to v4.6.1+ | -| `NO_ACCESS` on API calls | User role lacks permission for the resource | Check role permissions in Catalyst console | -| Duplicate CORS headers / preflight fails | Express `cors()` middleware AND Catalyst Authorized Domains both inject `Access-Control-Allow-Origin` | Remove ALL Express CORS headers for production origins. Only set CORS for localhost (local dev). The gateway owns CORS for deployed origins. | -| Sign-out not working / crashes | `signOut()` called without redirect URL argument | Pass a redirect URL: `catalyst.auth.signOut(redirectURL)`. `constructSignOutUrl()` does not exist. | -| `getCurrentUser is not a function` | Method does not exist in Web SDK | Use `catalyst.auth.isUserAuthenticated()` — resolves with full user object | -| Embedded iFrame won't load | Div ID mismatch or CSP blocking | Verify the div `id` matches, check Content-Security-Policy headers allow Zoho iFrame origins | -| `/__catalyst/auth/login` returns 404 | Hosted Authentication not enabled in console | Console → Authentication → Login → enable Hosted Authentication | -| `isUserAuthenticated` rejects but nothing happens | SDK does NOT auto-redirect to login | Add `window.location.href = '/__catalyst/auth/login'` in the `.catch()` block | diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/services.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/services.md deleted file mode 100644 index e070356ab..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/services.md +++ /dev/null @@ -1,768 +0,0 @@ -# Catalyst Services Reference - -## Table of Contents -1. [AppSail (PaaS Compute)](#appsail) -2. [Circuits (Workflow Orchestration)](#circuits) -3. [SmartBrowz (Headless Browser)](#smartbrowz) -4. [ConvoKraft (Conversational Bots)](#convokraft) -5. [Slate (Frontend Deployment)](#slate) -6. [Pipelines (CI/CD)](#pipelines) -7. [QuickML (Machine Learning)](#quickml) -8. [Signals (Event Bus)](#signals) -9. [Job Scheduling](#job-scheduling) -10. [Zia Services (AI/ML)](#zia-services) -11. [DevOps (Monitoring & Logs)](#devops) -12. [CodeLib (Pre-built Solutions)](#codelib) -13. [Tunneling (Local Dev Exposure)](#tunneling) -14. [Catalyst Tools (VS Code Extension)](#catalyst-tools) -15. [Zia AI Assistant (In-Console AI)](#zia-ai-assistant) - ---- - -## AppSail - -AppSail is Catalyst's PaaS (Platform-as-a-Service) for deploying full applications, as opposed to individual functions. - -### When to use AppSail vs Functions -- **Functions**: Stateless, event-driven, auto-scaling, pay-per-execution. Best for APIs, webhooks, scheduled tasks. -- **AppSail**: Persistent server process with managed runtimes or custom Docker. Best for full web apps, long-running processes, WebSockets. - -### Catalyst-Managed Runtimes -Pre-configured environments: -- **Node.js**: Express, Hapi, Koa, Fastify, Restify -- **Java**: Embedded Jetty, Spring MVC, Spring Boot -- **Python**: Flask, Django, Bottle, CherryPy, Tornado - -### Custom Runtimes (Docker) -Deploy any language/framework as OCI container images: -- Go, Kotlin, Dart, Ruby, PHP, Deno, Bun, Rust — anything with a Dockerfile -- Push to Catalyst's Container Registry or pull from external registries - -### AppSail project structure (Node.js + Express example) -``` -appsail/ -├── app.js # Main application file -├── package.json -├── catalyst-config.json # AppSail configuration -└── node_modules/ -``` - -### catalyst-config.json for AppSail -```json -{ - "name": "my-app", - "stack": "node20", - "command": "node app.js", - "memory": 512, - "port": 9000 -} -``` - -The `port` must match what your app listens on. Catalyst routes traffic to this port. - -### AppSail with Express.js -```javascript -// appsail/app.js -const express = require('express'); -const catalyst = require('zcatalyst-sdk-node'); - -const app = express(); -app.use(express.json()); - -const PORT = process.env.X_ZOHO_CATALYST_LISTEN_PORT || 9000; - -app.get('/api/hello', (req, res) => { - res.json({ message: 'Hello from AppSail!' }); -}); - -app.get('/api/users', async (req, res) => { - try { - const catalystApp = catalyst.initialize(req); - const zcql = catalystApp.zcql(); - const users = await zcql.executeZCQLQuery('SELECT * FROM Users LIMIT 50'); - res.json(users); - } catch (error) { - res.status(500).json({ error: error.message }); - } -}); - -app.listen(PORT, () => { - console.log(`Server running on port ${PORT}`); -}); -``` - -Important: In AppSail, always use `process.env.X_ZOHO_CATALYST_LISTEN_PORT` as the port, with a fallback -for local development. - -### AppSail with Docker (Custom Runtime) -```dockerfile -# Dockerfile -FROM node:18-alpine -WORKDIR /app -COPY package*.json ./ -RUN npm install --production -COPY . . -EXPOSE 9000 -CMD ["node", "app.js"] -``` - -### Deployment -```bash -# Deploy AppSail from CLI -catalyst deploy --only appsail - -# For Docker-based deployment -catalyst appsail:deploy --docker -``` - -### AppSail Configurations -- **Instances**: 1-5 instances for auto-scaling -- **Memory**: 256MB to 2048MB per instance -- **Health checks**: Configure health check endpoints -- **Environment variables**: Set via console (see warning below) -- **Custom domains**: Map via Domain Mappings - -### AppSail environment variables — Console only - -**`app.yaml` environment variables are NOT applied at deploy time.** AppSail ignores them entirely. - -The **only** way to set environment variables for AppSail is through the Catalyst Console: - -> Catalyst Console → AppSail → \ → Configuration → Environment Variables - -This applies to all env vars including secrets, API keys, and table names. Do not rely on -`app.yaml`, `.env` files, or `catalyst-config.json` `env_variables` for AppSail services. - -Note: For serverless Functions, `env_variables` in `catalyst-config.json` DO work and are -deployed with the function. This limitation is specific to AppSail. - -### Slate + AppSail cross-origin issue - -A Slate-hosted frontend calling AppSail APIs may get `"Unable to Fetch"` or `"Failed to fetch"` -errors due to Catalyst's auth layer on AppSail. - -**Solution — serve the frontend from AppSail itself (same-origin):** - -```javascript -const path = require('path'); -const express = require('express'); -const app = express(); - -// API routes first -app.get('/api/data', async (req, res) => { /* ... */ }); - -// Serve frontend static files -app.use(express.static(path.join(__dirname, 'public'))); - -// Catch-all: serve index.html for client-side routing -app.get('*', (req, res) => { - res.sendFile(path.join(__dirname, 'public', 'index.html')); -}); -``` - -Copy your frontend build output into `public/` inside the AppSail directory. Use relative -URLs in frontend code (`const API_BASE = ''`) so all API calls stay same-origin. -This eliminates both CORS and auth-layer issues without extra configuration. - -### Slate + Serverless Functions cross-origin (WORKS — with correct setup) - -Unlike AppSail, **Slate → Serverless Function cross-domain requests DO work** once configured correctly. -The Catalyst ZGS gateway handles CORS injection automatically. - -**Required setup:** - -1. **Add Slate domain to Authorized Domains** — Console → Authentication → Whitelisting → - Authorized Domains → + Add Domain → add your Slate URL (e.g. `myapp.onslate.com`) → enable CORS toggle. - This makes the gateway inject `Access-Control-Allow-Origin` on every response. - -2. **Do NOT set CORS headers in your function code for production origins.** The gateway already - injects them. If your Express code also sets `Access-Control-Allow-Origin`, the browser receives - **duplicate headers** and rejects the response: - ``` - The 'Access-Control-Allow-Origin' header contains multiple values - 'https://myapp.onslate.com, https://myapp.onslate.com', but only one is allowed. - ``` - -3. **Only set CORS headers for localhost** (local dev — where no gateway is present): - ```javascript - // CORS for local development only — gateway handles production origins - app.use((req, res, next) => { - const origin = req.headers.origin || ''; - if (/^http:\/\/localhost(:\d+)?$/.test(origin)) { - res.setHeader('Access-Control-Allow-Origin', origin); - res.setHeader('Access-Control-Allow-Credentials', 'true'); - res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); - res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); - if (req.method === 'OPTIONS') return res.status(204).end(); - } - next(); - }); - ``` - -4. **Use `generateAuthToken()` with the full function URL** — relative paths resolve to - `onslate.com/server/...` (404). See `sdk-web.md` for the complete cross-domain pattern. - -**Key rule: the gateway owns CORS headers for production origins. Express must not touch them.** - -**What causes failures:** -- `cors()` middleware with explicit allowed list → gateway AND Express both set the header → duplicate → rejected -- `cors({ origin: true })` → reflects origin, duplicating the gateway injection -- `cors()` with `callback(new Error(...))` for non-localhost → returns HTML 500 error page instead of JSON - -### Health checks and autoscaling - -**Health check endpoint:** -AppSail pings your service periodically to verify it's healthy. Configure a health -check endpoint that returns HTTP 200: - -```javascript -app.get('/health', (req, res) => { - res.status(200).json({ status: 'ok' }); -}); -``` - -Configure the health check path in Console → AppSail → service → Configuration → Health Check. - -**Autoscaling:** -- Instances scale from 1 (min) to 5 (max) -- Scale-up triggers when instance utilization reaches **80%** of the configured threshold -- Scale-down happens automatically when load drops - -**Graceful shutdown:** AppSail sends SIGTERM before killing instances. Handle it: - -```javascript -process.on('SIGTERM', () => { - console.log('Shutting down gracefully...'); - server.close(() => process.exit(0)); -}); -``` - -### Custom domain SSL - -Catalyst provides free SSL certificates for custom domains, provisioned and renewed -automatically via Zoho's own Certificate Authority. - -- **Renewal:** Automatic — no manual intervention required -- **Validation:** DNS-based (CNAME record must point to Catalyst) -- **Troubleshooting:** If SSL fails to provision or renew: - 1. Verify your DNS CNAME still points to the Catalyst domain - 2. Check Console → Domain Mapping for certificate status - 3. Allow up to 24 hours for DNS propagation after changes - 4. Contact Catalyst support if the certificate remains in a failed state - ---- - -## Circuits - -Visual workflow orchestration engine. Design multi-step workflows with drag-and-drop in the console. - -### Key concepts -- **States**: Individual steps in a workflow (function execution, condition, wait, parallel) -- **Transitions**: Flow between states -- **Input/Output**: JSON data passed between states - -### State types -1. **Function State**: Executes a Basic I/O function -2. **Condition State**: Branches based on conditions -3. **Wait State**: Pauses execution for a duration -4. **Parallel State**: Executes multiple branches simultaneously -5. **End State**: Terminates the circuit - -### Error handling in Circuits -- **Retry**: Configure delay and number of attempts for failed states -- **Fallback**: Define a fallback state if retries are exhausted -- Custom error handlers supported for both Function and Circuit states - -### Invoking a Circuit -```javascript -// From another function -const circuit = catalystApp.circuit(); -const result = await circuit.execute(CIRCUIT_ID, { - inputKey: 'inputValue' -}); -// Circuit ID from Console → Serverless → Circuits → circuit details - -// Via REST API -// POST /server/circuit/{circuit_id}/execute -// Body: { "inputKey": "inputValue" } -``` - -### Circuit use cases -- Multi-step data processing pipelines -- Approval workflows -- ETL (Extract, Transform, Load) processes -- Saga pattern for distributed transactions -- Sequential function orchestration with error handling - ---- - -## SmartBrowz - -Headless browser service for web automation, scraping, and document generation. - -### Capabilities -- Web scraping and crawling (permitted websites only) -- Screenshot capture -- PDF generation from HTML templates -- Browser automation (form filling, clicking, navigation) -- Dynamic content rendering - -### Browser automation with Puppeteer -SmartBrowz supports Puppeteer-like APIs for browser control: -```javascript -// In a Browser Logic function -module.exports = async (catalystApp, context, browserData) => { - try { - const input = JSON.parse(browserData.getArgument()); - const smartBrowz = catalystApp.smartBrowz(); - const browser = await smartBrowz.open(); - const page = await browser.newPage(); - - await page.goto(input.url || 'https://example.com'); - const title = await page.title(); - const screenshot = await page.screenshot({ encoding: 'base64' }); - - await browser.close(); - context.close(); - } catch (error) { - console.error('SmartBrowz error:', error); - context.close(); - } -}; -``` - -The SmartBrowz API is Puppeteer-like. `browserData.getArgument()` provides -input data; `catalystApp.smartBrowz()` provides the browser automation client. - -### Template-based document generation -Design HTML/CSS templates in the console, inject dynamic data, and generate PDFs or images. - ---- - -## ConvoKraft - -Build AI-powered conversational bots. - -### Components -- **Bot Configuration**: Define bot personality, capabilities, and embedding settings -- **Tasks**: Define specific actions the bot can perform (e.g., "book appointment", "check status") -- **Business Logic**: Connect tasks to Catalyst functions for backend processing -- **Embedding**: Embed bots in web applications via JavaScript SDK - -### How it works -1. Create a bot in Console → ConvoKraft -2. Define tasks — each task maps to a user intent -3. Connect tasks to Catalyst Functions that handle the backend logic -4. Embed the bot in your frontend using the JS SDK -5. Bot processes user messages, matches intents to tasks, executes functions - -### Embedding a bot -```html - - -``` - -### Pricing -- $0.0006 per message -- Free tier: 1,000 messages/month - ---- - -## Slate - -**Slate is the preferred frontend deployment service for all new Catalyst projects.** It supersedes legacy -Web Client Hosting with modern Git-based workflows and native framework support. - -### Key features -- Native support for JavaScript frameworks (Next.js, React, Vue, Angular, Svelte) -- Git-based deployment (connect GitHub/GitLab repos) -- Automatic builds and deployments on push -- Preview deployments for branches -- Custom domain mapping -- Environment variables -- Server-side rendering (SSR) support for frameworks like Next.js -- ISR (Incremental Static Regeneration) support - -### CLI workflow -```bash -catalyst slate:create # Add an additional Slate app (interactive — asks framework + name + build config) -catalyst slate:link # Link existing local dir to Slate service (interactive) -catalyst slate:unlink # Unlink a Slate app -catalyst serve --only slate # Serve Slate app locally -catalyst deploy slate # Deploy all Slate apps to Development -catalyst deploy slate -m "message" # Deploy with a deployment message -catalyst deploy --only slate:appname # Deploy a specific Slate app -catalyst deploy slate --production # Deploy to Production -``` - -### Supported frameworks -- Next.js (with SSR support) -- React (Create React App, Vite) -- Vue.js -- Angular -- Svelte/SvelteKit -- Vanilla HTML/CSS/JS -- Any static site generator - -### Slate — Manual setup (non-interactive) - -`catalyst slate:link` is interactive-only and cannot be piped or scripted. For automated -or CI/CD environments, set up Slate manually: - -1. Create `.catalyst/slate-config.toml` inside the client directory: - ```toml - framework = "static" - deployment_name = "default" - ``` -2. Add Slate to `catalyst.json` with an **absolute** source path: - ```json - "slate": [{ "name": "my-frontend", "source": "/absolute/path/to/client" }] - ``` -3. Deploy with `catalyst deploy slate -m "deploy message"`. - -Slate URL format: `https://.onslate.in` - ---- - -## Pipelines - -CI/CD service for automating build, test, and deployment workflows. - -### Features -- YAML-based pipeline configuration -- Multi-stage pipelines (build → test → deploy) -- Integration with GitHub, GitLab, Bitbucket -- Environment-specific deployments -- Parallel job execution -- Artifact management -- Secret management - -### Pipeline configuration example - -```yaml -# .catalyst-pipeline.yml -name: deploy-pipeline -trigger: - branches: - - main - -stages: - - name: build - steps: - - run: npm install - - run: npm test - - - name: deploy - steps: - - run: catalyst deploy --only functions -``` - -Configure in Console → Pipelines → connect your GitHub/GitLab/Bitbucket repository. -Pipelines run automatically on push to the configured branch. - ---- - -## QuickML - -No-code ML pipeline builder. - -### Features -- Data connectors (CSV, databases, APIs) -- Data preprocessing (normalization, encoding, feature selection) -- Pre-built ML algorithms (classification, regression, clustering) -- Model training and evaluation -- Model deployment as API endpoints -- AutoML capabilities -- LLM/VLM token support (input/output tokens with per-million pricing) - -All configuration is done through the visual console — no code required. - -### Invoking a deployed model -```javascript -// From a Catalyst function — call a QuickML endpoint -const response = await fetch('https://your-quickml-endpoint-url', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ input_data: yourData }) -}); -``` - ---- - -## Signals - -**Signals is the preferred mechanism for integrating Catalyst apps with other Zoho products** (CRM, Books, -Desk, People, Analytics, etc.) and for building event-driven architectures. It replaces the deprecated -Event Listeners with a far richer feature set. - -### Architecture -Publishers emit events → Rules filter/transform/route → Targets receive and process. - -### Core elements - -**Publishers** — Sources that emit events: -- **Zoho Publishers**: Pre-built publishers from Zoho CRM, Books, Desk, Survey, Inventory, etc. - Come with predefined events and schemas (e.g., CRM "Deal Closed" event). -- **Catalyst Publishers**: Built-in publishers from Catalyst Cloud Scale services — Authentication, - Cache, Data Store, File Store, Stratus. Events fire on data changes automatically. -- **Custom Publishers**: Your own applications or third-party services. You provide the REST API URL - that emits events. Schema can be generated from live event payloads (no manual schema creation). - -**Rules** — Control how events flow from publisher to target: -- **Event filtering**: Filter events based on properties in the event payload (e.g., only deals > $10K) -- **Event transformation**: Three-pane visual editor to map/transform the event payload before delivery -- **Dispatch policy**: Choose between real-time delivery or batch delivery (scheduled collection) -- **Time To Live**: Configure how long undelivered events are retained -- **Retry policies**: Configure retry attempts and delays for failed deliveries -- **Consumer types**: Route to multiple targets from a single rule - -**Targets** — Destinations that receive events: -- **Webhooks**: HTTP endpoints (external URLs). Configure headers, parameters, rate limits, and - authentication via Connections. -- **Functions**: Catalyst Event Functions that process the event data -- **Circuits**: Catalyst Circuits workflows that orchestrate multi-step processing - -### Setting up Signals (step by step) -1. Console → Signals → Add Publisher (choose Zoho, Catalyst, or Custom) -2. For Zoho/Catalyst publishers: events and schemas are pre-configured -3. For Custom publishers: provide REST API URL; optionally generate schema from live payload -4. Create a Webhook (if using external target) or use existing Functions/Circuits -5. Create a Rule: select publisher + event → configure filters → configure transformation → - set dispatch policy → select target(s) -6. Monitor via Signals Dashboard and Logs - -### Monitoring -- **Dashboard**: Overview of all rules, event counts, success/failure rates per rule -- **Logs**: Detailed execution logs per event delivery -- **Application Alerts**: Auto-trigger email alerts on delivery failures (integrated from DevOps) - -### Real-world use cases -- **CRM deal automation**: CRM "Deal Closed" event → Function → create invoice in Zoho Books -- **Lead enrichment**: CRM "New Lead" event → Function → SmartBrowz scrape → update lead data -- **Order processing**: Custom app "Order Placed" event → Circuit → update inventory + send email -- **Feedback classification**: Website "Feedback" event → Function → Zia Text Analytics → store result -- **Cross-product sync**: Any Zoho product event → Function → sync data to Data Store/external DB - ---- - -## Job Scheduling - -Execute background jobs with managed job pools. Replaces the deprecated Cron service. - -### Components -- **Job Pools**: Containers for grouping related jobs -- **Jobs**: Individual tasks submitted to a pool -- **Triggers**: Job Functions, Circuits, Webhooks, or AppSail services -- **Scheduling**: Predefined or dynamic cron expressions for scheduled job submission - -### Submitting a job -```javascript -const jobScheduling = catalystApp.jobScheduling(); -const pool = jobScheduling.pool(POOL_ID); -// Pool ID from Console → Job Scheduling → pool details - -await pool.submitJob({ - input: JSON.stringify({ taskType: 'report', params: { month: 'January' } }) -}); -``` - -### Key advantages over deprecated Cron -- Multiple target types (not just Cron Functions) -- Job pools for organizing and grouping jobs -- Better tracking of job instances and execution history -- On-demand job submission in addition to scheduled - ---- - -## Zia Services - -AI/ML microservices you can call from your code. - -### Available services -- **OCR**: Extract text from images and documents -- **Barcode Scanner**: Read barcodes and QR codes -- **Face Detection**: Detect faces in images -- **Image Moderation**: Check images for inappropriate content -- **Object Detection**: Identify objects in images -- **Text Analytics**: Sentiment analysis, keyword extraction, NER -- **AutoML**: Train custom ML models with your data -- **Prediction**: Make predictions using trained AutoML models - -### OCR example -```javascript -const zia = catalystApp.zia(); - -// OCR from file -const result = await zia.extractOpticalCharacters({ - image: fileBuffer, // Buffer or ReadStream - modelType: 'OCR' // or 'HANDWRITTEN' -}); -console.log(result.text); -``` - -### Text Analytics example -```javascript -const zia = catalystApp.zia(); - -const sentiment = await zia.getTextAnalytics({ - document: 'I love this product! It works perfectly.', - features: ['sentiment', 'keyword'] -}); -``` - ---- - -## DevOps - -### Logs -View execution logs for all functions, AppSail, and other services. -- Log levels: INFO, WARNING, ERROR, DEBUG -- Filter by function, time range, status -- Available in the console under DevOps → Logs - -### Application Performance Monitoring (APM) -- Execution time tracking -- Error rate monitoring -- Cold start analysis -- Resource utilization metrics -- Custom metrics via SDK - -### Application Alerts -Configure email alerts for: -- Function failures -- Cron job failures -- Event Listener failures -- Signals delivery failures -- Custom threshold breaches - -### GitHub Integration -Deploy functions directly from GitHub repositories: -- Connect your GitHub account in project settings -- Map repos to functions -- Auto-deploy on push to specific branches - -### Debugging production failures - -**Step 1 — Find the error in Logs:** -- Console → DevOps → Logs → select function name → filter by error level -- Each log entry includes a request ID and timestamp - -**Step 2 — Correlate with APM:** -- Console → DevOps → APM → filter by the same time range -- APM shows execution time, memory usage, and error traces per function - -**Step 3 — Set up Application Alerts:** -- Console → DevOps → Alerts → create alert for error rate thresholds -- Alerts can notify via email when error rate exceeds a configured percentage - -**Tip:** Add structured logging in your functions: -```javascript -console.log(JSON.stringify({ requestId: req.headers['x-request-id'], action: 'createUser', status: 'success' })); -``` -This makes log searching and filtering much easier in the DevOps console. - ---- - -## Secrets Management - -**Where to store secrets (API keys, tokens, passwords):** - -| Method | Use for | Security level | -|--------|---------|---------------| -| Function `env_variables` in catalyst-config.json | Non-sensitive config | Low — visible in repo | -| Console → Function → Environment Variables | Secrets for Functions | Medium — not in repo | -| Console → AppSail → Environment Variables | Secrets for AppSail | Medium — not in repo | -| Connections | OAuth tokens for Zoho/third-party | High — auto-refresh, encrypted | - -**Rules:** -- **Never commit secrets to git** — use `.gitignore` for any file containing keys -- **Never hardcode secrets in source code** — always use environment variables -- **Use Connections for OAuth tokens** — Catalyst handles refresh automatically -- **For CI/CD:** Pass secrets as pipeline environment variables, never in YAML files - -For Functions, `env_variables` in `catalyst-config.json` are deployed with the function -and accessible via `process.env.VAR_NAME`. For sensitive values, set them in the Console -instead (Serverless → Function → Settings → Environment Variables) so they stay out of git. - -Reminder: For AppSail, `app.yaml` env vars are NOT applied at deploy time. Always use -the Console for AppSail environment variables. - ---- - -## CodeLib - -Pre-packaged, installable microservice solutions. - -### How it works -1. Browse available CodeLib solutions in the console -2. Install a solution into your project -3. The solution creates the necessary functions, tables, and configurations automatically -4. Customize the installed code as needed - -### Examples of CodeLib solutions -- Zoho CRM Bulk Processor -- DataStore Analytics Sync -- Email notification services -- Webhook handlers - ---- - -## Tunneling - -Expose your local development server to the internet for webhook testing and Zoho integration debugging. - -### When to use -- Testing Signals webhooks that need to reach your local machine -- Debugging Zoho product integrations that require a public callback URL -- Testing third-party webhook integrations locally - -### How it works -1. Configure tunneling in Console → Settings → Tunneling -2. Generate a tunneling URL (public URL that routes to your local server) -3. Use `catalyst functions:shell` to start tunneling -4. External services can now reach your local functions via the tunneling URL - -### Workflow -```bash -# Start local serve with tunneling -catalyst functions:shell -# Follow the prompts to start tunneling -# Use the generated URL for webhook/callback configuration -``` - ---- - -## Catalyst Tools - -VS Code Extension for Catalyst development. Provides IDE-integrated project management and deployment. - -### Features -- **Project Explorer**: Browse project structure, functions, and services directly in VS Code -- **Code Generation**: Generate function boilerplate, config files from the IDE -- **Deployment**: Deploy functions and services without leaving the editor -- **Command Palette**: Access all Catalyst CLI commands from VS Code command palette - -### Installation -Search "Catalyst Tools" in VS Code Extensions marketplace and install. - ---- - -## Zia AI Assistant - -In-console AI assistant for code-related tasks. Available in the Catalyst web console. - -### Capabilities -- **Code Converter**: Convert code between languages/frameworks -- **Code Generator**: Generate function code from natural language descriptions -- **Code Debugger**: Identify and explain bugs in your Catalyst code -- **Code Docs Generator**: Auto-generate documentation for your functions -- **Test Case Generator**: Generate test cases for your Catalyst functions - -### OpenAI Integration -Catalyst supports configuring OpenAI as the backing model for the Zia AI Assistant. -Configure in Console → Settings → Integrations → OpenAI. diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/signals-deep-dive.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/signals-deep-dive.md deleted file mode 100644 index 2349029b2..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/signals-deep-dive.md +++ /dev/null @@ -1,300 +0,0 @@ -# Signals Deep-Dive Reference - -## When to use this file -Load this file when the user asks about: Signals architecture, publishers, custom publishers, -event rules, event filtering, dispatch policies, event transformation, webhooks, Signals -dashboard/logs, or any detailed Signals configuration beyond what `services.md` covers. - -External docs: https://docs.catalyst.zoho.com/en/serverless/signals/ - ---- - -## Architecture Overview - -The Signals pipeline flows as: - -**Publisher → Event → Rule → Target → Dispatch Policy** - -1. A **Publisher** emits an **Event** (a JSON payload). -2. A **Rule** listens for a specific event from a specific publisher. -3. The rule applies **filters** (optional) to decide if the event qualifies. -4. If the event passes filters, it is optionally **transformed**. -5. The event is dispatched to one or more **Targets** according to the **Dispatch Policy**. - ---- - -## Publishers - -Publishers are the sources that emit events into Signals. There are three types. - -### 1. Zoho Publishers - -Pre-built publishers from 17+ Zoho services. Events and schemas are predefined. - -**Supported Zoho services include:** CRM, Books, Desk, Survey, Inventory, People, Recruit, -Projects, Analytics, Creator, Invoice, Subscriptions, Mail, Campaigns, Forms, Flow, and others. - -**Constraints:** -- Max **100** Zoho publishers per Catalyst account -- Max event payload size: **100 KB** -- The Zoho service and the Catalyst project must belong to the **same Zoho organization** -- Events and schemas are predefined by the Zoho service — you cannot modify them - -### 2. Catalyst Publishers - -Built-in publishers from Catalyst Cloud Scale infrastructure services. These fire automatically -when data changes occur in your project. - -**Catalyst services that emit events:** -- **Authentication** — user signup, login, password reset events -- **Cache** — cache segment operations (put, delete, flush) -- **Data Store** — table row insert, update, delete events -- **File Store** — file/folder create, update, delete events -- **Stratus** — Stratus component lifecycle events - -**Event ordering:** Catalyst publisher events are **not guaranteed to arrive in order**. Design -your targets to handle out-of-order delivery (use timestamps or sequence numbers in your logic). - -### 3. Custom Publishers - -Your own applications or third-party services that emit events via REST API. - -**Constraints:** -- Max **25** custom publishers per Catalyst account -- API rate limit: **500 requests/minute** per publisher -- Max individual event payload: **64 KB** -- Max array event payload: **256 KB** (when sending multiple events in one request) -- Max **25 publishers per deployment operation** - -**Schema generation methods:** -- **Manual**: Define the event schema by hand in the console (JSON schema editor) -- **Live Events**: Send a sample event to the publisher endpoint and Signals auto-generates - the schema from the live payload. This is the recommended approach — it avoids schema - mismatches and saves time. - ---- - -## Events - -An event is the JSON payload emitted by a publisher. Each event has metadata and a body. - -### Event Schema Structure - -```json -{ - "event_id": "unique-event-id", - "publisher_id": "publisher-id", - "event_type": "event-name", - "timestamp": "2025-01-15T10:30:00Z", - "data": { - // Event-specific payload fields - } -} -``` - -### Event Statuses (8 total) - -| Status | Description | -|--------|-------------| -| **Received** | Event received by Signals | -| **In Queue** | Event queued for rule evaluation | -| **In Progress** | Event being processed by a target | -| **Success** | Event successfully delivered to target | -| **Failed** | Target execution failed (may be retried) | -| **Unmatched** | No rule matched the event | -| **Unprocessed** | Event matched a rule but was not dispatched (e.g., rule disabled) | -| **Dropped** | Event dropped due to TTL expiry or policy | - -### Limits -- Max **200 events** per publisher (event type definitions, not event instances) - ---- - -## Rules - -Rules control how events flow from a publisher to targets. - -### Constraints -- Max **100 rules** per Catalyst account -- Max **5 targets** per rule -- Max **25 filters** per rule -- Each rule listens to exactly **1 event** from 1 publisher - -### Filter Operators by Data Type - -**String filters:** -- `equals`, `not_equals`, `contains`, `not_contains`, `starts_with`, `ends_with`, - `is_empty`, `is_not_empty` - -**Integer / Number filters:** -- `equals`, `not_equals`, `greater_than`, `less_than`, `greater_than_or_equal`, - `less_than_or_equal`, `between` - -**Boolean filters:** -- `equals`, `not_equals` - -**DateTime filters:** -- `equals`, `not_equals`, `before`, `after`, `between` - -Filters are combined with **AND** logic — all filters must match for the event to pass. - ---- - -## Targets - -Targets are the destinations that receive dispatched events. Three types are supported. - -### 1. Webhooks -- HTTP endpoints (external or internal URLs) -- **Timeout**: 5 seconds (the webhook must respond within 5 seconds) -- Max **100 webhooks** per project -- Configure HTTP method, headers, parameters, and authentication -- Invocation rate: **1 to 300 requests/second** (configurable per webhook) -- **Authentication**: Use Catalyst Connections for OAuth-based auth to external services - -**Header and parameter value types:** -- **Static**: A fixed string value -- **Dynamic**: A value extracted from the event payload (JSON path expression) -- **Placeholder**: A Catalyst system placeholder (e.g., project ID, environment) - -### 2. Functions -- Catalyst Event Functions -- **Timeout**: 15 minutes max execution time -- Full access to Catalyst SDK within the function - -### 3. Circuits -- Catalyst Circuit workflows -- **Timeout**: 5 seconds for the initial invocation (the Circuit itself runs asynchronously) - -### Retry Policy - -Two retry modes: - -- **Automatic (exponential backoff)**: Signals retries failed deliveries automatically with - increasing delays between attempts. The system determines the retry count and intervals. -- **Manual retry**: Failed events appear in the Signals logs. You can manually trigger a - retry from the console for specific failed events. - ---- - -## Dispatch Policies - -Dispatch policies control when and how events are sent to targets. - -### Instant Dispatch -- Events are dispatched to targets immediately upon rule match -- **TTL (Time To Live)**: 24 hours — undelivered events are dropped after 24 hours -- **Single event option**: When enabled, each event is dispatched individually (no batching) - -### Batch Dispatch - -Events are collected and dispatched in batches. Four batch trigger types: - -| Batch Type | Configuration | Description | -|------------|--------------|-------------| -| **Count** | 2 to 100 events | Dispatch when N events accumulate | -| **Size** | Up to 100 KB | Dispatch when batch reaches size threshold | -| **Interval** | 2 to 12 hours | Dispatch every N hours regardless of count | -| **Schedule** | Daily at specific time | Dispatch once per day at a configured time | - -When using batch dispatch, the target receives an array of events rather than a single event. - ---- - -## Event Transformation - -Transform the event payload before it reaches the target. Configured in the rule using a -three-pane visual editor in the console. - -### Transformation Types - -**Extraction (JSON Path)** -- Extract specific fields from the event payload using JSON path expressions -- Example: `$.data.deal.amount` extracts just the deal amount from a CRM event - -**Transformation (Rebuild)** -- Rebuild the payload structure entirely -- Map fields from the source event to a new target schema -- Combine, rename, or restructure fields as needed - -**For-each** -- When the event payload contains an array, apply the transformation to each element -- Useful for batch events or events with nested arrays - ---- - -## Webhooks (Detailed) - -### HTTP Methods Supported -- GET, POST, PUT, PATCH, DELETE - -### Configuration Options -- **URL**: The target endpoint URL -- **Method**: HTTP method -- **Headers**: Custom headers with static, dynamic, or placeholder values -- **Parameters**: Query parameters with static, dynamic, or placeholder values -- **Body**: Request body (for POST/PUT/PATCH) — can use transformation output -- **Authentication**: Via Catalyst Connections (OAuth 1.0, OAuth 2.0, API Key, etc.) -- **Invocation rate**: 1 to 300 requests per second - -### Constraints -- Max **100 webhooks** per project -- Response timeout: **5 seconds** -- The webhook must return an HTTP 2xx status to be considered successful - ---- - -## Dashboard and Logs - -### Dashboard -- Overview of all rules with event counts -- Success/failure rates per rule -- Event volume over time -- Filter by publisher, rule, target, time range - -### Logs -- Detailed execution logs for each event delivery -- Includes: event payload, target response, status, timestamp, duration -- Filter by rule, status, time range -- Failed events can be retried from the logs view - ---- - -## Deployment Notes - -- Signals configuration is **environment-specific** — dev and prod have separate publishers, - rules, and targets -- Deploying to production requires **admin permissions** on the Catalyst project -- Max **25 publishers** can be included in a single deployment operation -- Custom publisher endpoints must be accessible from the target environment (dev URLs won't - work in prod and vice versa) -- Zoho publisher connections must be re-authorized in production if the Zoho service uses - different credentials per environment - ---- - -## Limits Summary - -| Resource | Limit | -|----------|-------| -| Zoho publishers per account | 100 | -| Custom publishers per account | 25 | -| Custom publisher API rate | 500 req/min | -| Individual event payload (custom) | 64 KB | -| Array event payload (custom) | 256 KB | -| Zoho event payload | 100 KB | -| Event type definitions per publisher | 200 | -| Rules per account | 100 | -| Targets per rule | 5 | -| Filters per rule | 25 | -| Events per rule | 1 | -| Webhooks per project | 100 | -| Webhook timeout | 5 seconds | -| Webhook invocation rate | 1-300 req/sec | -| Function target timeout | 15 minutes | -| Circuit target timeout | 5 seconds (invocation) | -| Instant dispatch TTL | 24 hours | -| Batch count range | 2-100 events | -| Batch size max | 100 KB | -| Batch interval range | 2-12 hours | -| Publishers per deployment | 25 | diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/smartbrowz-deep-dive.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/smartbrowz-deep-dive.md deleted file mode 100644 index 435157e0a..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/smartbrowz-deep-dive.md +++ /dev/null @@ -1,505 +0,0 @@ -# SmartBrowz Deep-Dive Reference - -## When to use this file -Load this file when the user asks about: SmartBrowz headless browser, Puppeteer/Playwright/Selenium -in Catalyst, Browser Logic functions, Browser Grid, PDF generation, screenshot capture, HTML templates, -LiquidJS templates, or Dataverse APIs. - -External docs: https://docs.catalyst.zoho.com/en/serverless/smartbrowz/ - ---- - -## Components Overview - -| Component | Description | Status | -|-----------|-------------|--------| -| **Headless Browser** | Connect to managed browsers via Puppeteer, Playwright, or Selenium | GA | -| **Browser Logic Functions** | Serverless functions with pre-initialized browser sessions | GA | -| **Browser Grid** | Dedicated browser infrastructure with configurable tiers | Early Access | -| **PDF & Screenshot** | Generate PDFs and screenshots from HTML, URLs, or templates | GA | -| **Templates** | LiquidJS-based HTML templates for dynamic document generation | GA | -| **Dataverse** | Business data enrichment APIs (Lead Enrichment, Tech Stack, Similar Companies) | Beta (US only) | - ---- - -## Headless Browser - -Connect to Catalyst-managed headless browsers from your functions using your preferred -automation framework. - -### Supported Browsers -- **Chrome** v137 -- **Firefox** v136 - -### Puppeteer (Node.js) - -```javascript -const puppeteer = require('puppeteer-core'); -const catalyst = require('zcatalyst-sdk-node'); - -module.exports = async (context, basicIO) => { - const catalystApp = catalyst.initialize(context); - const smartBrowz = catalystApp.smartBrowz(); - const browserDetails = await smartBrowz.open(); - - const browser = await puppeteer.connect({ - browserWSEndpoint: browserDetails.browser_ws_url - }); - - const page = await browser.newPage(); - await page.goto('https://example.com'); - const title = await page.title(); - - await browser.close(); - basicIO.write({ title }); - context.close(); -}; -``` - -### Playwright — Node.js - -```javascript -const { chromium } = require('playwright'); -const catalyst = require('zcatalyst-sdk-node'); - -module.exports = async (context, basicIO) => { - const catalystApp = catalyst.initialize(context); - const smartBrowz = catalystApp.smartBrowz(); - const browserDetails = await smartBrowz.open(); - - const browser = await chromium.connectOverCDP(browserDetails.browser_ws_url); - const defaultContext = browser.contexts()[0]; - const page = defaultContext.pages()[0] || await defaultContext.newPage(); - - await page.goto('https://example.com'); - const title = await page.title(); - - await browser.close(); - basicIO.write({ title }); - context.close(); -}; -``` - -### Playwright — Python - -```python -from playwright.sync_api import sync_playwright - -def handler(catalyst_app, context, basic_io): - smart_browz = catalyst_app.smart_browz() - browser_details = smart_browz.open() - - with sync_playwright() as p: - browser = p.chromium.connect_over_cdp(browser_details["browser_ws_url"]) - default_context = browser.contexts[0] - page = default_context.pages[0] if default_context.pages else default_context.new_page() - - page.goto("https://example.com") - title = page.title() - - browser.close() - - basic_io.write({"title": title}) - context.close() -``` - -### Selenium — Java - -```java -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.remote.RemoteWebDriver; -import java.net.URL; -import org.openqa.selenium.chrome.ChromeOptions; - -public class BrowserFunction implements ZCFunction { - public void runner(CatalystApp catalystApp, Context context, BasicIO basicIO) throws Exception { - SmartBrowz smartBrowz = catalystApp.smartBrowz(); - JSONObject browserDetails = smartBrowz.open(); - - ChromeOptions options = new ChromeOptions(); - WebDriver driver = new RemoteWebDriver( - new URL(browserDetails.getString("browser_http_url")), - options - ); - - driver.get("https://example.com"); - String title = driver.getTitle(); - - driver.quit(); - basicIO.write(new JSONObject().put("title", title)); - context.close(); - } -} -``` - -### Selenium — Node.js - -```javascript -const { Builder } = require('selenium-webdriver'); -const chrome = require('selenium-webdriver/chrome'); -const catalyst = require('zcatalyst-sdk-node'); - -module.exports = async (context, basicIO) => { - const catalystApp = catalyst.initialize(context); - const smartBrowz = catalystApp.smartBrowz(); - const browserDetails = await smartBrowz.open(); - - const driver = await new Builder() - .forBrowser('chrome') - .usingServer(browserDetails.browser_http_url) - .setChromeOptions(new chrome.Options()) - .build(); - - await driver.get('https://example.com'); - const title = await driver.getTitle(); - - await driver.quit(); - basicIO.write({ title }); - context.close(); -}; -``` - -### Selenium — Python - -```python -from selenium import webdriver -from selenium.webdriver.chrome.options import Options - -def handler(catalyst_app, context, basic_io): - smart_browz = catalyst_app.smart_browz() - browser_details = smart_browz.open() - - options = Options() - driver = webdriver.Remote( - command_executor=browser_details["browser_http_url"], - options=options - ) - - driver.get("https://example.com") - title = driver.title - - driver.quit() - basic_io.write({"title": title}) - context.close() -``` - ---- - -## Browser Logic Functions - -Serverless functions with a browser session pre-initialized. No need to call `smartBrowz.open()` -— the browser is already connected when your function starts. - -### Node.js (Playwright pre-initialized) - -```javascript -// The browser and page are already available via browserData -module.exports = async (catalystApp, context, browserData) => { - try { - const input = JSON.parse(browserData.getArgument()); - const page = browserData.getPage(); // Pre-initialized Playwright page - - await page.goto(input.url || 'https://example.com'); - const title = await page.title(); - const screenshot = await page.screenshot({ encoding: 'base64' }); - - context.close({ title, screenshot }); - } catch (error) { - console.error('Browser Logic error:', error); - context.close(); - } -}; -``` - -### Java (Selenium pre-initialized) - -```java -import org.openqa.selenium.WebDriver; - -public class BrowserLogicFunction implements ZCBrowserFunction { - public void runner(CatalystApp catalystApp, Context context, BrowserData browserData) throws Exception { - String input = browserData.getArgument(); - WebDriver driver = browserData.getDriver(); // Pre-initialized Selenium WebDriver - - driver.get("https://example.com"); - String title = driver.getTitle(); - - context.close(new JSONObject().put("title", title)); - } -} -``` - -### Constraints -- Max execution time: **15 minutes** -- Browser Logic functions are deployed like other Catalyst functions via `catalyst deploy` -- The pre-initialized browser session is scoped to the function execution — it is destroyed - after the function completes - ---- - -## Browser Grid (Early Access) - -Dedicated browser infrastructure for high-volume or long-running browser automation workloads. -Provides persistent browser nodes rather than on-demand ephemeral sessions. - -### Tier Configurations - -| Tier | Nodes | Browsers/Node | Memory/Node | vCPU/Node | Use Case | -|------|-------|---------------|-------------|-----------|----------| -| **Basic** | 1 | 1 | 512 MB | 0.5 | Light testing, single-page scraping | -| **Light Advanced** | 2 | 2 | 1 GB | 1 | Moderate scraping, small-scale automation | -| **Moderate** | 4 | 4 | 2 GB | 2 | Production scraping, parallel test suites | -| **Heavy** | 8 | 8 | 4 GB | 4 | Large-scale automation, heavy parallel loads | - -### Queue and Lifecycle -- **Queue duration**: Configurable — how long a request waits in the queue for an available - browser slot before timing out -- **Grid states**: Creating → Active → Scaling → Stopping → Stopped -- **Alerts**: Configure alerts for queue saturation, node failures, and high resource usage - -### Use Cases -- Running large Selenium/Playwright test suites in parallel -- High-volume web scraping with consistent performance -- Browser-based RPA workflows requiring dedicated resources -- Load testing web applications with real browsers - ---- - -## PDF & Screenshot Generation - -Generate PDFs and screenshots programmatically without managing a browser session. - -### Input Methods -- **HTML string**: Pass raw HTML content directly -- **URL**: Provide a public URL to render -- **Templates**: Use a pre-configured LiquidJS template with dynamic data - -### Output Formats -- **PDF**: Full-page PDF document -- **Screenshot**: PNG image (full page or viewport) - -### Java SDK Example - -```java -SmartBrowz smartBrowz = catalystApp.smartBrowz(); - -// PDF from URL -JSONObject pdfOptions = new JSONObject(); -pdfOptions.put("format", "A4"); -pdfOptions.put("print_background", true); -pdfOptions.put("landscape", false); - -JSONObject navigationOptions = new JSONObject(); -navigationOptions.put("wait_until", "networkidle0"); -navigationOptions.put("timeout", 30000); - -byte[] pdfBytes = smartBrowz.generatePdf( - "https://example.com", - pdfOptions, - navigationOptions -); - -// Screenshot from HTML -JSONObject screenshotOptions = new JSONObject(); -screenshotOptions.put("full_page", true); -screenshotOptions.put("type", "png"); - -byte[] imageBytes = smartBrowz.captureScreenshot( - "

Hello

", - screenshotOptions -); -``` - -### Node.js SDK Example - -```javascript -const smartBrowz = catalystApp.smartBrowz(); - -// PDF from URL -const pdfBuffer = await smartBrowz.generatePdf({ - url: 'https://example.com', - pdf_options: { - format: 'A4', - print_background: true, - landscape: false - }, - navigation_options: { - wait_until: 'networkidle0', - timeout: 30000 - } -}); - -// Screenshot from HTML -const imageBuffer = await smartBrowz.captureScreenshot({ - html: '

Hello

', - screenshot_options: { - full_page: true, - type: 'png' - } -}); -``` - -### Python SDK Example - -```python -smart_browz = catalyst_app.smart_browz() - -# PDF from URL -pdf_bytes = smart_browz.generate_pdf( - url="https://example.com", - pdf_options={ - "format": "A4", - "print_background": True, - "landscape": False - }, - navigation_options={ - "wait_until": "networkidle0", - "timeout": 30000 - } -) - -# Screenshot from HTML -image_bytes = smart_browz.capture_screenshot( - html="

Hello

", - screenshot_options={ - "full_page": True, - "type": "png" - } -) -``` - -### Configuration Options - -**`pdf_options`:** -- `format`: Page format — A4, Letter, Legal, Tabloid, A3, A5 -- `print_background`: Include background graphics (default: false) -- `landscape`: Landscape orientation (default: false) -- `margin`: Object with `top`, `right`, `bottom`, `left` (CSS units) -- `scale`: Scale factor (0.1 to 2.0, default: 1) -- `header_template` / `footer_template`: HTML for headers/footers -- `page_ranges`: e.g., "1-3, 5" - -**`page_options`:** -- `width`: Viewport width in pixels (default: 1280) -- `height`: Viewport height in pixels (default: 720) -- `device_scale_factor`: Device pixel ratio (default: 1) - -**`screenshot_options`:** -- `full_page`: Capture entire scrollable page (default: false) -- `type`: "png" or "jpeg" -- `quality`: JPEG quality 0-100 (only for jpeg) -- `clip`: Object with `x`, `y`, `width`, `height` for partial capture - -**`navigation_options`:** -- `wait_until`: "load", "domcontentloaded", "networkidle0", "networkidle2" -- `timeout`: Navigation timeout in milliseconds - ---- - -## Templates - -LiquidJS v10-based HTML templates for generating dynamic documents. Design templates in the -console with a visual editor. - -### Template Syntax (LiquidJS v10) - -**Variables:** -```html -

Hello, {{ customer_name }}!

-

Your order #{{ order_id }} has been confirmed.

-``` - -**Conditionals:** -```html -{% if status == "premium" %} -
Premium Member
-{% elsif status == "trial" %} -
Trial User
-{% else %} -
Standard Member
-{% endif %} -``` - -**Loops:** -```html - - {% for item in line_items %} - - - - - - {% endfor %} -
{{ item.name }}{{ item.quantity }}{{ item.price | currency }}
-``` - -**Filters:** -```html -{{ created_at | date: "%B %d, %Y" }} -{{ price | divided_by: 100.0 | round: 2 }} -{{ description | truncate: 100 }} -{{ name | upcase }} -{{ email | downcase }} -``` - -**Case statements:** -```html -{% case priority %} - {% when "high" %} - Urgent - {% when "medium" %} - Normal - {% when "low" %} - Low Priority -{% endcase %} -``` - -### Output Settings -- Choose PDF or Screenshot (PNG) output -- Configure page size, margins, orientation -- Set header/footer templates within the template configuration - -### Template Lifecycle -- **Draft**: Template is editable and not available for API use -- **Published**: Template is locked and available for API use via template ID -- Publish a template to make it available; create a new draft version to edit - ---- - -## Dataverse (Beta — US Data Center Only) - -Business data enrichment APIs. Currently in beta and available only in the US data center. - -### Available APIs - -**Lead Enrichment** -- Input: Company name or domain -- Output: Company details (industry, size, revenue, location, social profiles) - -**Tech Stack Finder** -- Input: Company domain -- Output: List of technologies used by the company (analytics, CMS, frameworks, etc.) - -**Similar Companies** -- Input: Company name or domain -- Output: List of companies similar in industry, size, or technology usage - -### Constraints -- **500 API calls per month** (across all Dataverse APIs combined) -- Beta feature — may change without notice -- Available only in the **US data center** - -### Usage -```javascript -const smartBrowz = catalystApp.smartBrowz(); -const dataverse = smartBrowz.dataverse(); - -// Lead Enrichment -const leadData = await dataverse.enrichLead({ domain: 'example.com' }); - -// Tech Stack -const techStack = await dataverse.getTechStack({ domain: 'example.com' }); - -// Similar Companies -const similar = await dataverse.getSimilarCompanies({ domain: 'example.com' }); -``` diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/troubleshooting.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/troubleshooting.md deleted file mode 100644 index d98720b52..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/troubleshooting.md +++ /dev/null @@ -1,493 +0,0 @@ -# Catalyst Troubleshooting Guide - -## When to use this file -Load this file when the user reports something is broken, asks "why is X failing", or needs -diagnostic help with: deployments, function errors, ZCQL/Data Store issues, MCP tool errors, -AppSail crashes, Circuits failures, cron problems, or API Gateway errors. - ---- - -## Deployment Failures - -### Function deployment fails -- **Missing `.class` files (Java)** — If uploaded via console, the `.class` files must be included. - When deploying via CLI, `catalyst deploy` auto-compiles and creates missing dependency files. - Fix: use `catalyst deploy` instead of manual console upload. -- **`catalyst.json` missing or malformed** — Must exist at project root. Never create manually; - it is auto-generated by `catalyst init`. If missing, re-run `catalyst init`. -- **Wrong directory structure** — Functions must be under `functions/`, web client under `client/`. - Deviating from this structure causes deployment to fail silently or partially. -- **Deploy specific components only:** - - Functions: `catalyst deploy --only functions` - - AppSail: `catalyst deploy appsail` - -### `is_deployed: false` in API responses does not indicate a problem -`List_All_Functions` and related API/MCP responses return `"is_deployed": false` for all -functions — including functions that are actively deployed and handling requests. This field -does not reflect actual deployment status and should be ignored. Verify function status from -the Catalyst console (Functions list) or by invoking the function endpoint directly. - -### AppSail deployment fails or app not responding after deploy -- **Port mismatch** — The `port` field in `catalyst-config.json` must exactly match the port - the app listens on. Using a different port causes requests to fail. -- **Cold start timeout** — When the first request hits an inactive AppSail app, a new instance - is spawned. The app **must start listening on the port within 10 seconds** or the instance is - killed and the next request triggers a new cold start cycle. - Fix: minimize initialization logic; start listening as early as possible. -- **Missing env variable** — Always use `process.env.X_ZOHO_CATALYST_LISTEN_PORT || 9000` - (Node.js) for the port. Hard-coding a port can work locally but breaks in Catalyst's environment. -- **Docker image build failure (custom runtime)** — Check the Dockerfile and ensure the image - builds successfully locally before pushing to Catalyst's container registry. - -### Slate deployment fails -- **Repository not in standard Catalyst project structure** — The `catalyst.json` file MUST be - present in the GitHub repository's default branch. Without it, deployment fails. -- **Default branch has wrong structure** — Functions and Web Client Hosting won't be updated if - the structure is incorrect; no changes are reflected. -- **Build command fails** — Check Slate build logs in the console for the specific error. -- **Recovery — Sync Now** — If a deployment fails, use the "Sync Now" feature in the Catalyst - console to merge the latest Git commit to the current deployment. You can also rollback to a - previous successful deployment from the console. - CLI: `catalyst deploy slate` (all apps) or `catalyst deploy --only slate:appname` (specific app) - -### GitHub-based deployment fails -- Repository must contain resources in standard Catalyst project directory format. -- `catalyst.json` MUST be in the repository for deployment to succeed. -- If deployment is unsuccessful, no changes are reflected in Functions or Web Client Hosting. -- Fix: verify directory structure, add/fix `catalyst.json`, and redeploy. - ---- - -## Function Execution Errors - -### Timeout errors -Execution time limits by function type: - -| Function Type | Timeout | -|---------------|---------| -| Basic I/O | 30 seconds | -| Advanced I/O | 30 seconds | -| Event | 15 minutes | -| Cron | 15 minutes | -| Integration | 30 seconds | -| Job | 15 minutes | -| Browser Logic | 30 seconds | - -- Use `context.getRemainingExecutionTimeMs()` inside the function to check remaining time. -- Use `context.getMaxExecutionTimeMs()` to get the configured maximum (constant value). -- For operations exceeding 30 seconds, switch to Event, Job, or Cron function types (15-min limit). -- For operations exceeding 15 minutes, migrate to AppSail (no function-level timeout). -- For large batch processing, use Circuits batch state — it runs multiple function instances - in parallel, avoiding timeout on a single long-running execution. - -### Concurrency limit reached (429 error) -Error message: `"CONCURRENCY_LIMIT_REACHED FOR THE FEATURE FUNCTIONS"` -- **Production limit**: 1500 concurrent executions (for a function that runs in 10ms) -- **Development limit**: 1000 concurrent executions (for a function that runs in 10ms) -- Fix: implement retry with exponential backoff in the caller, or route heavy processing - through Job Scheduling for async execution instead of direct function invocation. - -### Memory issues -- Default function memory: 128 MB -- Default AppSail memory: 512 MB -- Configurable range: 128–1024 MB (functions), 256–2048 MB (AppSail) -- Use DevOps → APM to identify memory-intensive operations and compare response times - across memory settings. Start at the lowest setting and increase as needed. - -### Basic I/O limitations causing unexpected errors -- Basic I/O returns **STRING only** — if you need JSON, use Advanced I/O. -- `basicIO.write()` can only be called **once per execution**. Calling it multiple times - causes unexpected behavior. -- Basic I/O does **not support HTTP request/response headers**. Use Advanced I/O for - custom status codes, headers, or content types. - -### Advanced I/O — Node.js `res` object issues -In `node20`, the response object is a raw `http.ServerResponse`, **not** an Express response. -- `res.status()`, `res.json()`, `res.send()` do NOT exist. -- Use `res.writeHead(statusCode, headers)` and `res.end(JSON.stringify(data))`. -- Helper pattern to use in all Advanced I/O functions: - ```javascript - function sendJson(res, statusCode, data) { - res.writeHead(statusCode, { 'Content-Type': 'application/json' }); - res.end(JSON.stringify(data)); - } - ``` - ---- - -## Data Store / ZCQL Errors - -### ZCQL query errors -- **"Empty query" error** — Wrong body key. Use `{ "zcql": "SELECT ..." }` not `{ "query": "..." }`. -- **Max rows** — ZCQL returns a maximum of 300 rows per query. Paginate with - `LIMIT offset, count` (e.g., `LIMIT 0, 300`, then `LIMIT 300, 300`). -- **Max columns per SELECT** — ZCQL limits `SELECT` to 20 columns per query. Use explicit column - names instead of `SELECT *` on tables with more than 20 columns. -- **Case sensitivity** — Table names and column names are case-sensitive; must match the - console exactly. -- **String quoting** — String values in ZCQL must be in **single quotes**: `WHERE name = 'Alice'`. -- **Row ID field** — Use `ROWID` (uppercase), not `id`. The system primary key is always `ROWID`. -- **Result unwrapping** — `executeZCQLQuery` returns `[{ TableName: { ROWID: ..., col: ... } }]`. - Always unwrap: `result.map(r => r.TableName)`. The key matches the table name as defined in - the console (case-sensitive). - -### Column creation errors -- **`INVALID_INPUT: max_length cannot be null`** — `varchar` columns MUST specify `max_length` - (e.g., `"max_length": 255`). Omitting this field always causes this error. -- **System columns already exist** — Do NOT create `ROWID`, `CREATORID`, `CREATEDTIME`, or - `MODIFIEDTIME` columns. Catalyst adds these automatically to every table. -- **Boolean fields as strings** — All boolean column properties (`is_mandatory`, `is_unique`, - `search_index_enabled`, `audit_consent`) must be passed as **string values**: `"true"` or - `"false"`, not JSON booleans (`true` / `false`). -- **Batch column creation** — If one column definition in a batch `Create_Column` call is invalid, - the entire batch fails. Validate all column types before sending. - -### Data Store permissions error -Error: `"No privileges to perform this action"` -- DataStore table permissions default to **Read-only for App Users**. -- Insert, Update, and Delete must be explicitly enabled per table in the console: - Data Store → [table] → Permissions → App User role. -- Alternative: use admin-scoped SDK for DataStore operations: - `catalyst.initialize(req, { scope: 'admin' })` - -### CREATEDTIME timezone issues -- Catalyst stores `CREATEDTIME` in the project's configured timezone (e.g. IST) WITHOUT an - offset marker. Passing the raw string to `new Date()` treats it as UTC, producing timestamps - that are hours off. -- Fix: always append the project timezone offset before parsing the date string. - -### DataStore SDK methods hang silently in Job functions (Python SDK) -All `zcatalyst_sdk` DataStore `Table` methods (`get_paged_rows`, `delete_rows`, `insert_rows`, -etc.) hardcode `CredentialUser.USER` internally. Job functions run under admin credentials only — -`CredentialUser.USER` has no token, so every DataStore SDK call makes a request with no auth, -waits indefinitely, and fails silently. No exception is raised; the function eventually hits the -15-minute hard timeout. -- Fix: call the underlying requester directly with `CredentialUser.ADMIN`: - ```python - from zcatalyst_sdk.credential import CredentialUser - - rows = table._requester.request( - method="GET", - path=f"/datastore/table/{table_identifier}/tablerow", - user=CredentialUser.ADMIN, - timeout=10, - ) - ``` -- **Warning:** `_requester` is a private API and may change between SDK versions. Check - compatibility after SDK upgrades. -- Cache SDK methods are NOT affected — they already use `CredentialUser.ADMIN` by default. - -### Emoji / 4-byte UTF-8 silently corrupted -- Data Store does NOT support emoji or 4-byte UTF-8 characters (many CJK extensions). -- These are silently stored as `?`. -- Workaround: store a string key (e.g., `"happy"`) and map to emoji in application code. - ---- - -## Cache Errors - -### `segment.delete()` / `Delete_Cache_Item` does not remove the key -`segment.delete(key)` (SDK) and the `Delete_Cache_Item` MCP tool both set `cache_value = null` -and clear the TTL, but the key continues to exist indefinitely. A subsequent `segment.getValue(key)` -does NOT raise a "key not found" error — it returns HTTP 200 with `cache_value: null`. Code that -checks for key absence by catching an exception will incorrectly treat the null-value key as -present. -- Fix: check the value itself, not the exception: - ```python - lock_val = segment.get_value(key) # does not raise even if deleted - if lock_val: # truthy check — null/empty means absent - # key is live - ``` - -### `segment.update()` without expiry preserves the original TTL (not the documented 48-hour default) -`segment.update(key, value)` called without an expiry argument does **not** reset TTL to 48 hours -as the documentation states. Instead, it **preserves the original TTL** from the initial `put()`. -The expiry clock keeps counting down from its original wall-clock time. This means: -- If you `put(key, value, 1)` (1 hour) then `update(key, newValue)` 30 minutes later, the key - still expires 30 minutes after the update — not 48 hours later. -- Fix: always pass the same TTL used at creation time to be explicit: - ```python - segment.update(key, value, 1) # 1-hour TTL, matching segment.put(key, value, 1) - ``` -- This applies to lock tokens, session state, and any key originally created with a TTL. - ---- - -## Zoho MCP Tool Errors - -### `PERMISSION_NEEDED` -Causes (check in this order): -1. **Wrong `projectId`** — The `id` returned by `List_All_Projects` is not always the correct - project ID for tool calls. Get the correct ID from the Catalyst console URL: - `https://console.catalyst.zoho.com/baas//project//...` -2. **"On Demand" auth not enabled** — Go to mcp.zoho.com → Connections → Edit → - select "On Demand" → Update. Verify `Catalyst by Zoho` shows Status: Connected. -3. **Accessing Production** — Production requires separate authorization. Default to - `"Development"` environment; only access production when explicitly requested. - -### `INVALID_ORG` -- Wrong `Catalyst-org` header value. -- Fix: re-run `List_All_Organizations` (no parameters needed) to get the correct org ID. - -### Tool not available / not found -- The tool has not been added to the MCP server. -- Fix: go to mcp.zoho.com → Config Tools → search for the tool → Add Now. - -### MCP connection not authorized -- Fix: in the MCP console → Connections → enable "On Demand" authorization. - ---- - -## AppSail Specific Errors - -### Cold start behavior -- The first request to an inactive app triggers a cold start (new server instance spawned). -- The app must start listening on the configured port **within 10 seconds** of instance creation. -- If no process is found listening on the port within 10 seconds, the user instance is killed - and the next request triggers a new cold start. -- Mitigation: keep startup/initialization code minimal; move heavy setup to after `listen()`. - -### AppSail cross-origin issues with Slate frontend -- Slate-hosted frontends calling AppSail APIs may get blocked by Catalyst's auth layer - (manifests as "Unable to Fetch" or "Failed to fetch"). -- Fix: serve the frontend from AppSail itself using `express.static()` so all calls are - same-origin. This eliminates CORS and auth-layer issues entirely. -- **Note:** This issue is specific to AppSail. For Serverless Functions, cross-domain - from Slate DOES work — see the "Duplicate Access-Control-Allow-Origin" entry under Auth Errors. - ---- - -## Auth Errors - -### Server-side `userManagement().getCurrentUser()` throws 401 even when user is logged in -- Web client fetch calls to Catalyst functions must include `credentials: 'include'`. - Without it, auth cookies are not forwarded. -- Fix: always add `credentials: 'include'` to fetch options: - ```javascript - fetch('/server/my_function/execute', { - credentials: 'include', - // ... - }); - ``` - -### `catalyst.auth.getCurrentUser is not a function` in the Web SDK -- This method does not exist in the Web SDK. -- Use `catalyst.auth.isUserAuthenticated()` instead — it resolves with the full user object - (`result.content.email_id`, `result.content.first_name`, etc.) on success, and rejects - with 401 on failure (platform auto-redirects to login). - -### `signOut()` crashes with `Cannot read properties of undefined` -- `catalyst.auth.signOut()` requires a redirect URL argument. -- Calling it without an argument crashes because the SDK calls `.startsWith("/")` on `undefined`. -- Fix: `catalyst.auth.signOut(window.location.origin);` (Slate apps) or `catalyst.auth.signOut(window.location.origin + '/app/index.html');` (legacy Web Client) -- Note: `constructSignOutUrl()` does not exist — do not use a two-step pattern. - -### `Authorization: Bearer` header intercepted before the function handler runs -Catalyst validates any `Authorization: Bearer ` header as a Zoho OAuth token **before** -passing the request to the function handler — even when the endpoint's Security Rule has -`authentication: optional`. If your function uses its own Bearer token for server-to-server -auth (e.g. a shared secret), Catalyst returns `INVALID_TOKEN` before your code runs. -- Fix: use a non-standard header for application-level secrets: - ``` - # Not intercepted — use this for custom auth - X-My-App-Token: - - # Intercepted by Catalyst's auth layer — avoid for custom secrets - Authorization: Bearer - ``` - -### ZAID mismatch between environments -- ZAID (Zoho Application ID) differs between Development and Production environments. -- This is the #1 source of auth issues when promoting to production. -- Fix: always retrieve ZAID from the correct environment's console and update accordingly. - -### Duplicate `Access-Control-Allow-Origin` header (Slate + Serverless Functions) - -Error in browser console: -``` -The 'Access-Control-Allow-Origin' header contains multiple values -'https://myapp.onslate.com, https://myapp.onslate.com', but only one is allowed. -``` - -**Cause:** The Catalyst gateway injects `Access-Control-Allow-Origin` when the Slate domain is -in Authorized Domains (Console → Authentication → Whitelisting). If your Express code ALSO sets -this header (via `cors()` middleware or manual `res.setHeader`), the browser receives two values -in one header and rejects the response. - -**Fix:** Remove ALL Express/function-level CORS headers for production origins. Only set CORS -headers for `localhost` (local dev, where no gateway is present): -```javascript -app.use((req, res, next) => { - const origin = req.headers.origin || ''; - if (/^http:\/\/localhost(:\d+)?$/.test(origin)) { - res.setHeader('Access-Control-Allow-Origin', origin); - res.setHeader('Access-Control-Allow-Credentials', 'true'); - res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); - res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); - if (req.method === 'OPTIONS') return res.status(204).end(); - } - next(); -}); -``` - -**Key rule:** The gateway owns CORS headers for deployed origins. Express must not touch them. - -### `getCurrentUser()` returns `null` — collaborator vs app user - -`userManagement().getCurrentUser()` calls the internal `/project-user/current` endpoint using the -user token. This only returns data for **registered app users** — users who signed up through -Catalyst's auth flow (signUp/signIn). - -**Collaborators and project admins** (people added via the Catalyst console) are NOT registered -app users. `getCurrentUser()` returns `null` for them, causing `Cannot read properties of null` -errors if your code doesn't check for it. - -**Fix — add a null check with admin-scope fallback:** -```javascript -const userApp = catalyst.initialize(req); // user-scope -const userData = await userApp.userManagement().getCurrentUser(); - -if (!userData || !userData.user_id) { - // Collaborator/admin — not in the project-user table. - // Fall back to admin-scope user list, or use a default identity. - const adminApp = catalyst.initialize(req, { scope: 'admin' }); - const allUsers = await adminApp.userManagement().getAllUsers(); - // Match by email or use a system identity -} -``` - -### User-scope vs admin-scope: when to use each - -The SDK supports two initialization scopes. Using the wrong one causes auth failures: - -| Scope | Init call | Use for | What it CAN'T do | -|-------|-----------|---------|-------------------| -| **User** (default) | `catalyst.initialize(req)` | `getCurrentUser()`, user-identity operations | DataStore writes (if App User perms not enabled) | -| **Admin** | `catalyst.initialize(req, { scope: 'admin' })` | DataStore CRUD, Stratus, ZCQL, Cache, all data operations | `getCurrentUser()` — throws "no user credentials present" | - -**Pattern for apps that need both auth and data ops:** -```javascript -// User-scope for identity -const userApp = catalyst.initialize(req); -const currentUser = await userApp.userManagement().getCurrentUser(); - -// Admin-scope for data operations -const adminApp = catalyst.initialize(req, { scope: 'admin' }); -const dataStore = adminApp.datastore(); -const table = dataStore.table('MyTable'); -``` - -### `Authorization` header is `undefined` inside the function - -The Catalyst gateway **strips** the `Authorization` header after validating the token. It then -injects internal `x-zc-*` headers that the SDK reads directly. Do not try to read -`req.headers['authorization']` — it will be `undefined`. The SDK handles this internally via -`catalyst.initialize(req)`. - ---- - -## Slate Deployment Errors - -### `slate-config.toml` wiped by build commands - -The `.catalyst/slate-config.toml` file lives inside the build output directory (e.g., `dist/`). -Build commands that clean the output directory (Vite `--clean`, Expo `--clear`, `rm -rf dist/`) -delete this file. Without it, `catalyst deploy slate` fails. - -**Fix — recreate after every clean build:** -```bash -# Example for a React/Vite app -npm run build && mkdir -p dist/.catalyst && \ - echo -e 'framework = "static"\ndeployment_name = "default"' > dist/.catalyst/slate-config.toml && \ - catalyst deploy slate -``` - -### Assets returning 404 on Slate (framework `baseUrl` issue) - -If your build tool has a `baseUrl` or `basePath` configured for a non-root path (e.g., -`/server/my_function`), all JS/CSS asset URLs will be prefixed with that path on Slate — but -Slate serves from root `/`. This causes all assets to 404. - -**Fix:** Remove `baseUrl`/`basePath` from your build config before building for Slate deployment. -Only set it when serving the frontend from inside a function or AppSail sub-path. - ---- - -## Circuits Errors - -### State execution failures -- Function state supports three default error handlers: **On TimeOut**, **On Authorization Failure**, - **On Execution Failure**. -- For each, configure **Retry** (max attempts + delay) and **Fallback** (alternative state to - go to when all retries fail). -- Custom error handlers match by Error Code or Error Message from the function response. -- HTTP status codes outside 200–299 trigger the error handler check for Basic I/O functions. - -### Batch state issues -- Nested parallel states are **not supported** — success and failure states are end states and - Catalyst does not support nested parallel states within a parallel state. -- Batch and circuit states only support **Retry** action on error — there is no Fallback for these - (unlike function states, which support both Retry and Fallback). - ---- - -## Cron / Job Scheduling Errors - -### Cron auto-disabled after repeated failures -- Third-party URL crons are automatically disabled after **50 consecutive failures**. -- Cron functions (not URL-based) are **NOT auto-disabled** regardless of repeated failures. -- Fix: configure Application Alerts on the cron job to be notified immediately on failure, - check execution history from the console, fix the underlying bug, and re-enable the cron. - ---- - -## API Gateway Errors - -### 429 Too Many Requests -- Throttling rate limit exceeded. -- Catalyst uses a **sliding window rate limiting algorithm**: checks request count in the window - preceding the current second (not a fixed window). -- Two throttle types: - - **General throttling**: max hits per time unit for all users combined. - - **IP-based throttling**: max hits per IP per time unit. -- Fix: review and adjust rate limit settings in API Gateway console; implement client-side - retry with exponential backoff. - ---- - -## Event Listener Errors - -### "Configuration Needed" error on rules -- **Integration account deleted** — All rules associated with the deleted account show - "Configuration Needed". Fix: re-associate rules with a different account, or re-integrate - the deleted account. -- **Service deleted** — All accounts in the service are removed, and all associated rules show - the error. Fix: create a new service and re-integrate accounts. - ---- - -## DevOps — Where to find logs - -| What you need | Where to look in the console | -|---------------|------------------------------| -| Function execution logs | DevOps → Logs (filters auto-applied when accessed from function page) | -| AppSail instance logs | AppSail → Instances → click Logs icon → redirects to Catalyst Logs | -| Circuit execution details | Circuits → Execution History → click execution → View Logs | -| Cron execution history | Cron → Details → Execution History | -| Performance reports | DevOps → APM (Application Performance Monitoring) | -| Audit trail (console + app) | Settings → Audit Logs | -| Automated failure alerts | DevOps → Application Alerts | - ---- - -## Quick diagnostic checklist - -When an agent reports "something is broken", work through this in order: - -1. **What component?** Function / AppSail / Slate / Circuits / Cron / Data Store / MCP tool -2. **What error message?** Look at the exact error text first. -3. **Is it a deployment issue or a runtime issue?** - - Deployment: check `catalyst.json`, directory structure, Java `.class` files, port config - - Runtime: check DevOps → Logs, look at execution history -4. **Is it an auth/permission issue?** Check ZAID environment, DataStore permissions, API Gateway rules -5. **Is it a data issue?** Check ZCQL syntax, 300-row limit, case sensitivity, column types diff --git a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/zoho-mcp-tools.md b/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/zoho-mcp-tools.md deleted file mode 100644 index d17a91d5e..000000000 --- a/plugins/catalyst-by-zoho/skills/catalyst-by-zoho/references/zoho-mcp-tools.md +++ /dev/null @@ -1,421 +0,0 @@ -# Zoho MCP Tools — Catalyst Resource Management via LLM - -This reference covers how to manage Catalyst infrastructure (tables, columns, buckets, cache, cron jobs, -etc.) directly from an LLM conversation using **Zoho MCP tools** — without requiring the user to -manually operate the Catalyst console for every resource change. - -## When to use this reference - -Read this file when: -- The user asks you to **create tables, columns, buckets, cache entries**, or other Catalyst resources -- The user wants to **query or modify Data Store rows** via ZCQL without writing a deployed function -- The user says things like "set up the database for me", "create the tables I need", "can you do it - from here instead of me going to the console?" -- You detect that Zoho MCP tools (`CatalystbyZoho_*`) are available in the current tool list -- The user mentions "Zoho MCP", "MCP server", or "MCP tools" in a Catalyst context - -## Architecture: Zoho MCP vs Catalyst SDK - -These are **two separate systems** — understand the difference: - -| | Zoho MCP Tools | Catalyst SDK/CLI | -|--|----------------|------------------| -| **What** | REST API wrappers exposed as MCP tools | Native SDK libraries + CLI | -| **Where** | `mcp.zoho.com` console | `catalyst.zoho.com` console + local dev | -| **Auth** | OAuth via MCP Connection | CLI login or SDK token | -| **Used for** | LLM-driven resource management (create tables, query rows, manage buckets) | Writing and deploying application code | -| **Runs in** | LLM tool calls during conversation | Deployed functions, AppSail, local dev | - -**They are complementary.** Use MCP tools to set up infrastructure (tables, columns, buckets), then -use the SDK in your deployed code to read/write that infrastructure at runtime. - -## Prerequisites (requires human setup) - -Before an LLM can use Zoho MCP tools, the user must complete these steps manually. **When a user -needs to create infrastructure and MCP is not yet connected, proactively recommend they set it up -and walk them through the steps below.** If the user explicitly prefers to skip MCP setup, fall -back to providing step-by-step Catalyst console instructions instead. - -### Step 1: Create a Zoho MCP Server -1. Go to `mcp.zoho.com` → create or select an MCP server -2. Navigate to the **Tools** tab → **Config Tools** -3. Search for **"Catalyst by Zoho"** -4. Select the tools needed (or all) → **Add Now** - -### Step 2: Enable "On Demand" Authorization -1. In the MCP server → **Connections** tab -2. Click **Edit** → select **"On Demand"** → **Update** -3. Verify `Catalyst by Zoho` shows **Status: Connected** - -> **Why "On Demand" over "Authorization via Connection":** In organizations with multiple users, -> "Authorization via Connection" uses a single Super Admin token for all calls — making it impossible -> to identify which user is performing an action. "On Demand" authenticates each user individually, -> so the system maintains proper user-level attribution and access control. - -### Step 3: Connect the MCP server to their LLM client -The user needs to add the MCP server's URL to their LLM client configuration -(e.g., `mcp.json` for Claude Desktop, or the MCP settings in claude.ai). - -**Where to find the URL:** In the MCP server → **Connect tab** → **Server URL** field. -Copy the full URL (format: `https://-.zohomcp.com/mcp//message`). - -### Verification prompt -Once setup is done, ask the user to confirm by running: -``` -Call List_All_Organizations (no parameters needed) -``` -If this returns their org data, the connection is working. - -## 🛑 Execution flow: MANDATORY — Always follow this sequence before ANY project-scoped MCP call - -> **Every MCP call that targets a Catalyst project (create table, query rows, manage buckets, etc.) -> requires two IDs: the org ID (`Catalyst-org` header) and the project ID (`path_variables.projectId`). -> Without both, calls fail with `PERMISSION_NEEDED` or `INVALID_ORG`. You MUST resolve these IDs -> BEFORE attempting any operation.** - -### Path A: Local project exists (`.catalystrc` found in working directory) - -If you have filesystem access and `.catalystrc` exists in the project root, use it as the authoritative source: - -``` -Step 1: Read .catalystrc → Get projectId and env_id -Step 2: List_All_Organizations → Get org id, cross-check env_id with .catalystrc -Step 3: Verify with a read operation → e.g., List_All_Tables to confirm access - └─ If PERMISSION_NEEDED → ask user for project ID from Catalyst console URL -Step 4: Proceed with create/read/update operations -``` - -**`.catalystrc` example:** -```json -{ - "project_id": "31594000000112008", - "project_domain": "docvault-60019947973.development", - "env_id": "60019947973", - "timezone": "Asia/Kolkata" -} -``` - -The `env_id` in `.catalystrc` corresponds to the org environment — cross-check it against the org returned by `List_All_Organizations`. - -### Path B: No local project (chat-only context — GPT, Claude chat, etc.) - -When there is no `.catalystrc` (no local project, chat-only usage), you MUST discover the org and project interactively: - -``` -Step 1: List_All_Organizations → Returns all orgs the user has access to - └─ If multiple orgs → ASK the user which org to use (do NOT guess) - └─ Save the org `id` — this becomes the `Catalyst-org` header for all calls - -Step 2: List_All_Projects → Pass the org id, returns all projects in that org - (headers: { "Catalyst-org": "" }) - └─ If multiple projects → ASK the user which project to use (do NOT guess) - └─ Save the project `id` — this becomes `path_variables.projectId` for all calls - -Step 3: Verify with a read operation → e.g., List_All_Tables - (headers: { "Catalyst-org": "", "Environment": "Development" }, - path_variables: { "projectId": "" }) - └─ If PERMISSION_NEEDED → the project ID is likely wrong (see "Project ID mismatch" below) - └─ If success → you now have confirmed working org + project IDs - -Step 4: Proceed with create/read/update operations using these confirmed IDs -``` - -> **Key rule for multi-org / multi-project users:** NEVER assume which org or project the user -> wants to work with. If `List_All_Organizations` returns more than one org, or `List_All_Projects` -> returns more than one project, **always ask the user to pick**. Guessing wrong means all -> subsequent operations silently target the wrong project. - -### Common mistake: Skipping straight to operations - -❌ **Wrong:** `Create_Table` → fails with `PERMISSION_NEEDED` (no org/project context) -❌ **Wrong:** `List_All_Projects` → `Create_Table` (skipped org identification, wrong `Catalyst-org`) -✅ **Correct:** `List_All_Organizations` → `List_All_Projects` → `List_All_Tables` (verify) → `Create_Table` - -**Never skip the verify step (Step 3).** It's a cheap read call that catches ID mismatches before you waste write calls or create resources in the wrong project. - -## ⚠️ Critical gotcha: Project ID mismatch - -**The `id` field returned by `List_All_Projects` is NOT always the correct project ID for tool calls.** - -The correct project ID is visible in the **Catalyst console URL**: -``` -https://console.catalyst.zoho.com/baas/904503171/project/31594000000112008/... - ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ - This is the correct projectId -``` - -If `List_All_Tables` returns `PERMISSION_NEEDED` after using the API-returned ID: -1. Ask the user to open their project in the Catalyst console -2. Have them copy the project ID from the URL -3. Use that ID for all subsequent calls - -> Known pattern: the API-returned ID may be off by 2 (e.g., API returns `...112010`, but correct -> ID is `...112008`). However, **always confirm with the user** rather than guessing. - -## Required headers for every project-scoped call - -Every DataStore, Cache, Stratus, and Function tool requires these: - -| Header | Example | Source | -|--------|---------|--------| -| `Catalyst-org` | `904503171` | `List_All_Organizations` → `id` | -| `Environment` | `"Development"` | Always start with Development | - -**Always default to `"Development"` environment.** Production requires separate authorization and -should only be accessed when the user explicitly requests it. - -## DataStore operations - -### Create a table -```json -Tool: Create_Table -body: { - "table_id": 0, - "table_name": "YourTableName", - "table_scope": "GLOBAL" -} -headers: { "Catalyst-org": "", "Environment": "Development" } -path_variables: { "projectId": "" } -``` -`table_scope` options: `GLOBAL` (all users — use for app data), `ORG`, `USER`. - -Save the `table_id` from the response — you need it for adding columns. - -### Create columns -```json -Tool: Create_Column -body: [ /* array of column objects */ ] -path_variables: { "projectId": "", "id": "" } -headers: { "Catalyst-org": "", "Environment": "Development" } -``` - -### Column type reference - -| `data_type` | Required fields | Extra required | -|-------------|----------------|----------------| -| `varchar` | `column_name`, `is_mandatory`, `is_unique`, `search_index_enabled`, `audit_consent` | `max_length` (integer, e.g., `255`) — **omitting causes INVALID_INPUT** | -| `text` | `column_name`, `is_mandatory`, `audit_consent` | none (auto max 10000 chars) | -| `int` | `column_name`, `is_mandatory`, `is_unique`, `search_index_enabled`, `audit_consent` | none | -| `bigint` | same as `int` | none | -| `double` | `column_name`, `is_mandatory`, `search_index_enabled`, `audit_consent` | `decimal_digits` (optional) | -| `boolean` | `column_name`, `is_mandatory`, `search_index_enabled`, `audit_consent` | none | -| `date` | `column_name`, `is_mandatory`, `search_index_enabled`, `audit_consent` | none | -| `datetime` | same as `date` | none | -| `encrypted text` | `column_name`, `is_mandatory`, `audit_consent` | `search_index_enabled` NOT allowed | -| `foreign key` | `column_name`, `is_mandatory`, `search_index_enabled`, `audit_consent` | `parent_table` (int), `parent_column` (int), `constraint_type` | - -**Important:** All boolean fields (`is_mandatory`, `is_unique`, `search_index_enabled`, `audit_consent`) -must be passed as **strings**: `"true"` or `"false"`, not actual JSON booleans. - -**Do NOT create** `ROWID`, `CREATORID`, `CREATEDTIME`, or `MODIFIEDTIME` columns — Catalyst adds these -system columns automatically. - -You can batch all columns in a single `Create_Column` call as an array. If one column definition is -invalid, the entire batch fails — validate all types before sending. - -### Insert rows -```json -Tool: Insert_Rows -body: [ - { "name": "Alice", "email": "alice@example.com", "score": 85 } -] -path_variables: { "projectId": "", "id": "" } -headers: { "Catalyst-org": "", "Environment": "Development" } -``` - -### Query rows via ZCQL -```json -Tool: Execute_Query -body: { "zcql": "SELECT ROWID, name, score FROM Employees WHERE score > 80 LIMIT 50" } -path_variables: { "projectId": "" } -headers: { "Catalyst-org": "", "Environment": "Development" } -``` - -ZCQL reminders: Use `ROWID` not `id`. String values in single quotes. Table/column names are -case-sensitive. Max 300 rows per query. - -### Update rows -```json -Tool: Update_Rows -body: [ - { "ROWID": "12345", "score": 90 } -] -path_variables: { "projectId": "", "id": "" } -``` - -### Delete rows -```json -Tool: Delete_Row_By_Id -path_variables: { "projectId": "", "id": "", "rowId": "" } -``` - -## Stratus (file/object storage) operations - -### List buckets -```json -Tool: Get_All_Buckets -path_variables: { "projectId": "" } -headers: { "Catalyst-org": "", "Environment": "Development" } -``` - -### Upload flow -Stratus does **not** support direct file upload via MCP tools. The flow is: -1. Call `Create_Upload_Signature` → get a signed upload URL -2. Upload the file to that URL directly (outside MCP — user must do this or use code) -3. Reference the object by its key/path - -### Object operations -```json -Tool: Get_All_Objects // List objects in a bucket -Tool: Get_Object // Download/read an object -Tool: Delete_Objects // Delete by key array -Tool: Generate_Signed_URL // Get a time-limited download URL -``` - -All require `projectId` and bucket `id` in `path_variables`. - -## Cache operations - -```json -// Create -Tool: Create_Cache_Item -body: { "cache_name": "myKey", "cache_value": "myValue", "expiry_in_hours": 24 } -path_variables: { "projectId": "" } - -// Read -Tool: Get_Cache_Item_Value -path_variables: { "projectId": "", "id": "myKey" } - -// Update -Tool: Update_Cache_Item -body: { "cache_value": "newValue", "expiry_in_hours": 48 } -path_variables: { "projectId": "", "id": "myKey" } - -// Delete -Tool: Delete_Cache_Item -path_variables: { "projectId": "", "id": "myKey" } -``` - -All require `Catalyst-org` and `Environment` headers. - -## Cron / Job operations - -```json -Tool: Create_Cron_Job -body: { - "job_name": "dailyReport", - "cron_expression": "0 0 * * *", - "target": { "function_name": "generateReport" } -} - -Tool: List_All_Crons -Tool: Update_Cron_Job_Status // enable/disable -Tool: Delete_Cron_Job -``` - -## Other useful MCP tools - -| What you want to do | Tool | -|--------------------|------| -| List all projects | `List_All_Projects` | -| List all tables | `List_All_Tables` | -| Create a table | `Create_Table` → `Create_Column` | -| Read rows | `Get_Rows` or `Execute_Query` | -| Write rows | `Insert_Rows` | -| Update rows | `Update_Rows` or `Patch_Rows` | -| Delete rows | `Delete_Row_By_Id` or `Delete_Rows` | -| List files/buckets | `Get_All_Buckets` → `Get_All_Objects` | -| Get a signed file URL | `Generate_Signed_URL` | -| Set a cache value | `Create_Cache_Item` | -| Read a cache value | `Get_Cache_Item_Value` | -| Schedule a function | `Create_Cron_Job` | -| Run a function via HTTP | `Execute_Function_Via_POST` / `GET` | -| Send an email | `Send_Email` | - -## Common errors and fixes - -| Error | Cause | Fix | -|-------|-------|-----| -| `PERMISSION_NEEDED` | Wrong `projectId` | Get correct ID from Catalyst console URL | -| `PERMISSION_NEEDED` | Auth via Connection not enabled | Enable in MCP console → Connections | -| `INVALID_ORG` | Wrong `Catalyst-org` header | Use `List_All_Organizations` to get correct ID | -| `INVALID_INPUT: max_length cannot be null` | `varchar` column missing `max_length` | Add `"max_length": 255` to column definition | -| `ZCQL QUERY ERROR: Empty query` | Wrong body key | Use `{ "zcql": "SELECT ..." }` not `{ "query": "..." }` | -| `PERMISSION_NEEDED` on Production | Production needs separate auth | Use `"Environment": "Development"` only | - -## Decision guide: When to use MCP tools vs console vs CLI - -| Task | MCP Tools (LLM) | Console (human) | CLI (developer) | -|------|-----------------|-----------------|-----------------| -| Create tables & columns | ✅ Best — fast iteration | ✅ Works | ❌ Not supported | -| Insert/query test data | ✅ Best — interactive | ✅ Works | ❌ Not directly | -| Create Stratus buckets | ✅ Can list/manage | ✅ Full control | ✅ Via deploy | -| Upload files to Stratus | ❌ Needs signed URL workaround | ✅ Direct upload | ✅ Via code | -| Deploy functions | ❌ Not supported | ✅ Works | ✅ Best — `catalyst deploy` | -| Configure Signals/rules | ❌ Not supported | ✅ Required | ❌ Not supported | -| Set up Slate frontend | ❌ Not supported | ✅ Initial setup | ✅ `catalyst deploy slate` | -| Manage cache entries | ✅ Full CRUD | ✅ Works | ❌ Not directly | -| Run ZCQL queries | ✅ Best — interactive | ✅ Works | ❌ Not directly | -| Create cron/scheduled jobs | ✅ Works | ✅ Works | ❌ Not directly | - -## Approach guidance for LLMs - -When a user asks you to build a Catalyst application: - -1. **Ask about their MCP setup first** — "Do you have a Zoho MCP server connected with Catalyst tools? - This would let me create tables, insert data, and test queries directly instead of you doing it - manually in the console." - -2. **If MCP is available** — use MCP tools for all infrastructure setup (tables, columns, test data, - cache config), then generate deployment-ready SDK code for the application logic. - -3. **If MCP is NOT available** — generate the code and provide step-by-step console instructions for - infrastructure setup. Offer to guide them through MCP setup if they want to streamline future work. - -4. **If you're unsure** — check if any `CatalystbyZoho_*` tools appear in your available tool list. - If yes, MCP is connected. If no, proceed with code-only approach. - ---- - -## Security & Permission Guidance - -### Why "On Demand" authorization is required (not optional) -- "Authorization via Connection" uses a **single Super Admin token for ALL calls** from all users. - This makes it impossible to identify which user performed an action, violating access control best practices. -- "On Demand" authenticates **each user individually**, ensuring proper user-level attribution and - access control. This is the only recommended approach for teams with multiple users. - -### Scoping MCP tool access -- Only add the specific Catalyst tools you need to your MCP server — do not add all tools blindly. -- Remove or exclude destructive tools (`Delete_Row_By_Id`, `Delete_Rows`, `Delete_Objects`) if the - agent only needs read operations. -- Use **separate MCP server configurations** for development and production environments to prevent - accidental production operations. - -### Environment isolation -- **ALWAYS default to `"Development"` environment** for all MCP tool calls. -- Production requires separate authorization. Never operate on production unless the user - explicitly requests it. -- If production access is needed, create a dedicated MCP server configuration for it. - -### Handling permission errors - -| Error | Meaning | Action | -|-------|---------|--------| -| `PERMISSION_NEEDED` | Wrong project ID or insufficient permissions | Verify project ID from the Catalyst console URL (`.../project//...`) | -| `INVALID_ORG` | Wrong org ID in `Catalyst-org` header | Re-run `List_All_Organizations` to get the correct ID | -| Tool not found / unavailable | Tool not added to the MCP server | Add it in mcp.zoho.com → Config Tools → search → Add Now | -| Connection not authorized | "On Demand" auth not enabled | Go to mcp.zoho.com → Connections → Edit → select "On Demand" → Update | -| `PERMISSION_NEEDED` on Production | Production requires separate auth | Switch to `"Development"` environment; set up production MCP separately if needed | - -### Best practices for agents -- Always verify access with a **read operation** before performing writes (e.g., `List_All_Tables` - before `Create_Table`). This catches ID mismatches before wasting write calls. -- Never store or log org IDs, project IDs, or tokens in generated application code. -- When generating code that references Catalyst IDs, use named constants with inline comments - that tell the user exactly where to find the real value: - ```javascript - const PROJECT_ID = "YOUR_PROJECT_ID"; // Find in Catalyst console URL: .../project//... - const ORG_ID = "YOUR_ORG_ID"; // Find via List_All_Organizations MCP tool - ``` -- Do not attempt to access the Production environment unless the user explicitly requests it. diff --git a/plugins/cb-insights/.app.json b/plugins/cb-insights/.app.json deleted file mode 100644 index 71523f45c..000000000 --- a/plugins/cb-insights/.app.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "apps": { - "cb-insights": { - "id": "asdk_app_69a85d518e2c81918694d9a48e3def41" - } - } -} diff --git a/plugins/cb-insights/.codex-plugin/plugin.json b/plugins/cb-insights/.codex-plugin/plugin.json deleted file mode 100644 index 256b00756..000000000 --- a/plugins/cb-insights/.codex-plugin/plugin.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "cb-insights", - "version": "1.0.3", - "description": "Unleash Codex as your private markets research agent.", - "author": { - "name": "CB Insights", - "url": "https://cbinsights.com" - }, - "repository": "https://github.com/openai/plugins", - "license": "MIT", - "keywords": [], - "apps": "./.app.json", - "interface": { - "displayName": "CB Insights", - "shortDescription": "Unleash Codex as your private markets research agent.", - "longDescription": "Unleash Codex as your private markets research agent. Source companies, build market maps, draft investment memos, and monitor competitors \u2014 all powered by CB Insights\u2019 predictive intelligence. Tap into 11M+ double-validated company profiles, leading coverage of recent equity deals, proprietary taxonomies, unique scores, hidden signals, and over 20 years of bleeding edge technology research to identify and analyze relevant, high-potential companies ahead of your competition.\n\nBuilt for corporate strategists seeking acquisition targets, VCs screening deal flow, and business development teams hunting new partners. If your work involves private companies, CB Insights delivers the insight you need to make your next move first.", - "developerName": "CB Insights", - "category": "Finance", - "capabilities": [], - "defaultPrompt": [ - "Pull the latest market context from CB Insights" - ], - "screenshots": [], - "websiteURL": "https://cbinsights.com", - "privacyPolicyURL": "https://www.cbinsights.com/privacy-policy", - "termsOfServiceURL": "https://legal.cbinsights.com", - "composerIcon": "./assets/logo.png", - "logo": "./assets/logo.png" - }, - "homepage": "https://cbinsights.com" -} diff --git a/plugins/cb-insights/assets/logo.png b/plugins/cb-insights/assets/logo.png deleted file mode 100644 index b1833c49c..000000000 Binary files a/plugins/cb-insights/assets/logo.png and /dev/null differ diff --git a/plugins/channel99/.app.json b/plugins/channel99/.app.json deleted file mode 100644 index 7a5510331..000000000 --- a/plugins/channel99/.app.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "apps": { - "channel99": { - "id": "asdk_app_696fbc1ac7bc8191a38ee4adad1bcc24" - } - } -} diff --git a/plugins/channel99/.codex-plugin/plugin.json b/plugins/channel99/.codex-plugin/plugin.json deleted file mode 100644 index 62a21e5cd..000000000 --- a/plugins/channel99/.codex-plugin/plugin.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "channel99", - "version": "1.0.3", - "description": "Channel99 real time go to market intelligence connects Codex directly to Channel99\u2019s performance marketin...", - "author": { - "name": "Channel99 Inc. ", - "url": "https://www.channel99.com/" - }, - "repository": "https://github.com/openai/plugins", - "license": "MIT", - "keywords": [], - "apps": "./.app.json", - "interface": { - "displayName": "Channel99", - "shortDescription": "Channel99 real time go to market intelligence connects Codex directly to Channel99\u2019s performance marketin...", - "longDescription": "Channel99 real time go to market intelligence connects Codex directly to Channel99\u2019s performance marketing intelligence. giving AI assistants real-time access to unified B2B marketing data and insights.. Powered by Channel99\u2019s advanced attribution, account identification, and AI-driven analytics. The Channel99 connection enables natural-language queries that deliver accurate campaign performance, spend efficiency, audience engagement and cross channel attribution insights eliminating manual data pulls, siloed dashboards and complex API engineering. \n\nMarketers, analysts, and business leaders can ask Codex strategic questions like \u201cWhich channels drove the most high-value engagement last quarter?\u201d or \u201cWhere should we reallocate budget to improve pipeline efficiency?\u201d and get precise, context-aware results powered by Channel99\u2019s industry leading performance data. \n\nDeveloped by Channel99 a B2B marketing technology company dedicated to improving ROI, lowering acquisition costs, and bringing financial transparency to digital marketing investments.", - "developerName": "Channel99 Inc. ", - "category": "Productivity", - "capabilities": [], - "defaultPrompt": [ - "Summarize campaign performance from Channel99" - ], - "screenshots": [], - "websiteURL": "https://www.channel99.com/", - "privacyPolicyURL": "https://www.channel99.com/company/privacy", - "termsOfServiceURL": "https://www.channel99.com/terms-of-service", - "composerIcon": "./assets/logo.png", - "logo": "./assets/logo.png", - "logoDark": "./assets/logo-dark.png" - }, - "homepage": "https://www.channel99.com/" -} diff --git a/plugins/channel99/assets/logo-dark.png b/plugins/channel99/assets/logo-dark.png deleted file mode 100644 index 3defcf3e2..000000000 Binary files a/plugins/channel99/assets/logo-dark.png and /dev/null differ diff --git a/plugins/channel99/assets/logo.png b/plugins/channel99/assets/logo.png deleted file mode 100644 index 79aaa7ae4..000000000 Binary files a/plugins/channel99/assets/logo.png and /dev/null differ diff --git a/plugins/chronograph-gp/.app.json b/plugins/chronograph-gp/.app.json deleted file mode 100644 index 99c646664..000000000 --- a/plugins/chronograph-gp/.app.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "apps": { - "chronograph-gp": { - "id": "asdk_app_6a1a374657a88191bc1e22f3e9862dc6" - } - } -} diff --git a/plugins/chronograph-gp/.codex-plugin/plugin.json b/plugins/chronograph-gp/.codex-plugin/plugin.json deleted file mode 100644 index 3919218f4..000000000 --- a/plugins/chronograph-gp/.codex-plugin/plugin.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "chronograph-gp", - "version": "1.0.0", - "description": "Portfolio monitoring, valuations, and analytics for private capital GP teams", - "author": { - "name": "Chronograph", - "url": "https://www.chronograph.pe/" - }, - "homepage": "https://www.chronograph.pe/", - "repository": "https://github.com/openai/plugins", - "license": "MIT", - "keywords": [], - "skills": "./skills/", - "apps": "./.app.json", - "interface": { - "displayName": "Chronograph GP", - "shortDescription": "Trusted portfolio data for private capital GP teams", - "longDescription": "Chronograph GP provides portfolio monitoring, valuations, and analytics for private capital General Partner users. Query trusted portfolio data, analyze investments, surface company-level metrics, and access private markets portfolio data using natural language.", - "developerName": "Chronograph", - "category": "Finance", - "capabilities": [], - "websiteURL": "https://www.chronograph.pe/", - "privacyPolicyURL": "https://www.chronograph.pe/legal/privacy-policy/", - "termsOfServiceURL": "https://www.chronograph.pe/legal/website-terms-of-use/", - "brandColor": "#414B4B", - "defaultPrompt": [ - "Summarize fund returns from Chronograph GP.", - "Find top and bottom portfolio investments.", - "Show overdue reporting tasks in Chronograph GP." - ], - "composerIcon": "./assets/logo.png", - "logo": "./assets/logo.png", - "screenshots": [] - } -} diff --git a/plugins/chronograph-gp/assets/logo.png b/plugins/chronograph-gp/assets/logo.png deleted file mode 100644 index a547aed09..000000000 Binary files a/plugins/chronograph-gp/assets/logo.png and /dev/null differ diff --git a/plugins/chronograph-gp/skills/chronograph-portfolio-company-one-pager/SKILL.md b/plugins/chronograph-gp/skills/chronograph-portfolio-company-one-pager/SKILL.md deleted file mode 100644 index f3c4ca10b..000000000 --- a/plugins/chronograph-gp/skills/chronograph-portfolio-company-one-pager/SKILL.md +++ /dev/null @@ -1,487 +0,0 @@ ---- -name: chronograph-portfolio-company-one-pager -description: > - GP platform one-pager and investor report generator for private equity portfolio companies. - Use this skill whenever a user asks to generate a company tearsheet, one-pager, investor - report, portfolio overview, or company deep-dive — especially when they name a company or - ask to "build a report", "create a one-pager", or "show me a tearsheet". Also trigger when - the user asks to include commentary, quarterly updates, investment narratives, or any - Investment Overview in the report output. This skill handles live data fetching via a - connected MCP data source OR from an uploaded Excel model, metric formatting, AI-generated - or model-sourced commentary, and rendering a fully styled HTML one-pager. Also trigger for - LP quarterly updates, valuation summaries, and portco performance pages — any output that - combines financials, valuation, and return data for a single portfolio company. ---- - -# GP Report Builder - -Generates a fully styled, self-contained HTML investor report for a named portfolio company. -Supports two data source modes, two report types, and automatic brand detection from uploaded -templates. **Read this skill fully before writing a single line of HTML.** - -**Requirements:** A connected Chronograph MCP server. These workflows are designed for permissioned Chronograph users to connect to their private investment data, or an uploaded Excel model for Model mode. - ---- - -## Step 0 — Resolve Branding - -Brand tokens are resolved automatically — the user never fills in a config file manually. -Execute the following decision tree before any other step. - -### 0A — Check for an Uploaded Brand Template - -Look for any of the following in the current conversation: - -| File type | What to extract | -|---|---| -| **HTML / CSS file** | Parse all `color`, `background`, `font-family`, and `border` declarations. Identify the dominant background, primary accent, heading font, and body font. Extract any `--variable` tokens if a design system is present. | -| **PDF report or one-pager** | Visually analyse the document. Identify background color(s), the dominant accent/highlight color, heading and body typefaces, logo presence, and footer text. | -| **Image (PNG / JPG / SVG)** | Extract the 3–5 most visually prominent colors using the image content. Identify any visible text to infer font style (serif vs sans-serif, bold vs light). Note logo or wordmark if present. | -| **PowerPoint / PPTX** | Read slide backgrounds, title font, body font, accent colors from shapes and highlights. | -| **CSS / design token file** | Map token names to the brand token schema below directly. | - -Once extracted, map findings to the Brand Token Schema in **0C**. - -If the user has dropped **multiple** files, treat the most recent one as authoritative for -branding, unless the user specifies otherwise. - -### 0B — No Template Provided → Apply Chronograph Defaults - -If no brand template is present in the conversation, apply the following defaults silently — -do not ask the user to provide branding. - -``` -firm_name: Chronograph -website: www.chronograph.pe -confidentiality_label: CONFIDENTIAL - -colors: - bg_primary: #101C1D ← Near Black 1 (page background) - bg_secondary: #1A2627 ← Near Black 2 (card / panel background) - bg_header: #1B4147 ← Deep Teal (header strip) - accent_primary: #57E5EE ← Bright Teal (headlines, KPI values, eyebrows) - accent_secondary: #11A8B2 ← Regular Teal (bars, borders, left-rule accents) - accent_negative: #F95532 ← Accent Red (EBITDA bars, negative deltas) - accent_positive: #4ecb8a ← Green (positive deltas) - text_primary: #FFFFFF ← White (body text) - text_muted: #D9E8E8 ← Light teal tint (footnotes, commentary) - table_header_bg: #1B4147 ← Deep Teal (table header row) - -fonts: - heading: Ubuntu - heading_weight: 700 - body: Open Sans - body_weight: 300 - google_fonts_url: https://fonts.googleapis.com/css2?family=Ubuntu:wght@700&family=Open+Sans:wght@300;300i&display=swap - -logo: - url: (none — render firm name as text) - position: top-left - -theme: dark -``` - -### 0C — Brand Token Schema - -Whether tokens are extracted from a template (0A) or defaults are applied (0B), resolve all -of the following before proceeding. Every downstream panel references these names. - -| Token | Role | Fallback if undetectable | -|---|---|---| -| `firm_name` | Firm name in header and footer | Infer from logo text or filename; else `"Your Firm"` | -| `website` | Footer URL | `""` (omit from footer) | -| `confidentiality_label` | Footer suffix | `"CONFIDENTIAL"` | -| `bg_primary` | Page / root background | Chronograph default | -| `bg_secondary` | Card / panel background | Darken `bg_primary` by 5% | -| `bg_header` | Header strip background | Darken `bg_primary` by 15% | -| `accent_primary` | Headlines, KPI values, eyebrow labels | Dominant bright color from template | -| `accent_secondary` | Bars, borders, left-rule accents | Mute `accent_primary` by 30% | -| `accent_negative` | Negative values, downward deltas | `#F95532` | -| `accent_positive` | Positive deltas | `#4ecb8a` | -| `text_primary` | Main body text | `#FFFFFF` on dark; `#1A1A1A` on light | -| `text_muted` | Footnotes, commentary | Tint `text_primary` toward `bg_primary` by 20% | -| `table_header_bg` | Table header row | `bg_header` | -| `font_heading` | Heading / KPI / eyebrow font | Detected from template; else `Ubuntu` | -| `font_heading_weight` | Heading weight | `700` | -| `font_body` | Body / table / footnote font | Detected from template; else `Open Sans` | -| `font_body_weight` | Body weight | `300` | -| `google_fonts_url` | Font load URL | Build from detected font names | -| `logo_url` | Logo image src | `""` — fall back to firm name as text | -| `logo_position` | Header logo placement | `top-left` | -| `theme` | `dark` or `light` | `dark` if `bg_primary` luminance < 0.2; else `light` | - -### 0D — Theme Handling - -**Dark theme** (luminance of `bg_primary` < 0.2): -Use the token values as resolved. Default contrast pairings apply (white text on dark -backgrounds). Minimum contrast ratio: 4.5:1 for body text, 3:1 for large text (≥ 18px bold). - -**Light theme** (luminance of `bg_primary` ≥ 0.2): -- Swap `text_primary` to a near-black (e.g. `#1A1A1A`) if not already dark -- Ensure `accent_primary` provides ≥ 3:1 contrast against `bg_primary` -- Table header: use the firm's dark brand color (e.g. deep navy / dark green) rather than - a light value - -### 0E — Confirm with the User (optional, brief) - -After resolving tokens, output a **single short line** — not a table, not a list — before -generating the report: - -> *"Using [firm_name] branding — [accent_primary] accent on [bg_primary] background, -> [font_heading] / [font_body] fonts."* - -If brand detection produced low-confidence results (e.g. only a logo image was provided with -no color context), ask one focused question: - -> *"I've picked up [X] and [Y] from your file — is that the right color scheme, -> or would you like to adjust anything?"* - -Do not ask if defaults were applied — just proceed. - ---- - -## Step 1 — Determine Report Mode - -**Data source mode:** - -| Signal | Mode | -|---|---| -| User uploads an Excel model (.xlsx) or references an uploaded file | **Model mode** — read data from the file | -| No file uploaded; company exists in the connected data platform | **MCP mode** — fetch live from the platform | -| Both available | Prefer the model for financials and commentary; use MCP to supplement metadata and returns | - -**Report type:** - -| User asks for… | Report type | -|---|---| -| One-pager, tearsheet, company report, GP report | **GP One-Pager** | -| LP update, quarterly update, LP quarterly report | **LP Quarterly Update** | - -If unclear, default to **GP One-Pager**. - ---- - -## Step 2 — Resolve the Company & Fetch Data - -### MCP Mode - -All data is fetched from the user's connected **Chronograph MCP server**. The agent must have the Chronograph MCP connected before running the skill in MCP mode; if it isn't connected, prompt the user to connect it, or fall back to Model mode if a file is available. - -Inspect the connected Chronograph MCP's tool list at runtime and pick the appropriate tool for each step below based on the tool descriptions the server provides. Do not hard-code tool names — read the live descriptions to stay current. - -### Fetch sequence - -Work through these steps in order. At each step, identify the right Chronograph tool from its description, call it to fetch what the report needs, and hold the result for the rendering steps that follow. The skill does not need a fixed schema — only the values listed below in plain language. - -**1. Resolve the company.** Find the tool that searches for companies, funds, and other portfolio entities by name. Use it to turn the user's input into a canonical company. If multiple candidates come back, prefer the closest name match; ask the user only if genuinely ambiguous. - -**2. Get the company's basic facts.** Use the tool that retrieves core portfolio entity records. Fetch what the **Header Strip** (Step 4) needs: company name, sector, industry, geography, HQ, and the company's reporting currency. Request only what the header renders — the tool's description will list what's available. - -**3. Get the company's investments.** Using the same core-entity retrieval tool, fetch the list of investments associated with the company. The **Investment Returns Table** (Step 4) needs one row per investment: investment name, the fund it belongs to, entry date, exit date if applicable, and whether it has exited. - -**4. Get the company's financial line items.** Find the tool for company-level financial metrics. Make a discovery / help call first, passing the company ID, to see what's available. - -**Metric selection rule.** Always query by the platform's metric type when one is available for the line item — even if the company's display label differs. Use a company-specific mapped metric definition only when no metric type covers the line item or the request is inherently tenant-specific (bespoke KPIs, commentary fields, operating metrics, custom valuation inputs). - -Priority: - -1. Platform metric type -2. Company-specific mapped metric definition (only if no metric type exists) -3. Name-based metric search (only if neither of the above resolves) - -Do not pick a company-specific metric because its label is a closer word match than an available metric type. Metric types drive querying; display labels drive presentation. - -What the report needs (query via the metric type whenever available): - -- **Financial Performance Table:** Revenue, Gross Profit, EBITDA, the company's adjusted EBITDA series, and Net Debt. Pull the last 4–5 trailing-twelve-month periods plus the most recent quarter. -- **Valuation Summary Table:** Enterprise Value, valuation multiple, Total Debt, Cash, Net Debt, Equity Value — current quarter and prior quarter. -- **KPI Strip:** the latest values for LTM Revenue, LTM Adj. EBITDA, Enterprise Value, and Net Debt. - -Use LTM for income-statement items, As-of for balance items. The discovery response will tell you which period types are supported per metric. - -**5. Get per-investment returns.** Find the tool for investment-level performance. It uses gross performance figures (not net — net lives elsewhere on the platform and is the wrong tool for the GP one-pager). The **Investment Returns Table** needs, per investment: invested capital, realized proceeds, unrealized value, MOIC, and IRR. Sum across investments to populate the MOIC card in the **KPI Strip**. - -**6. Resolve any non-standard metrics by name.** If the user asks the report to include a tenant-specific KPI that isn't part of the platform's standard metric set — e.g. a custom operating metric — use the metric-name search tool to resolve it to an ID before querying the company-metrics tool. - -### Currency - -Use the company's reporting currency as returned in step 2. **Do not default to USD.** If the user explicitly asks for a different currency, pass that through to each tool call. - -### If something is missing - -- Company not found → ask the user to confirm spelling; try the legal name. -- A metric returns no value → display `—`. Never fabricate. -- A tool returns a schema or help error → comply with the introspection call it asks for and retry. -- Chronograph MCP not connected → prompt the user to connect it, or fall back to Model mode. - -### Model Mode - -Read the uploaded Excel file using pandas (`data_only=True`). Extract from: - -| Tab | Data to extract | -|---|---| -| `Overview` | Company name, HQ, fiscal year end, fund name(s), investment names, entry dates, ownership % | -| `Performance` | Revenue, Gross Profit, EBITDA, Adj. EBITDA (Reported and Valuation basis), Net Debt — last 4–5 LTM periods | -| `Valuation` | EV, valuation multiple, equity value, net debt — current and prior quarter; commentary text (Business Update, Rationale for Conclusion, Rationale for Discount); per-investment cost, realized, unrealized, MOIC | - -**Currency and scale:** Check the `Figures In` field on the Overview tab. If `1000` -(thousands), divide all financial values by 1,000 before displaying in millions. Note the -`Local Currency` field and include it in the report header. - -**If commentary is present in the model**, use it verbatim (lightly edited for grammar only). - ---- - -## Step 3 — Layout Structure - -Both report types share the same structural template. The Financial Performance table must -always be **directly above** the Valuation Summary table in the same column. - -``` -┌─────────────────────────────────────────────────────────────────┐ -│ HEADER — Logo · Company · Fund · Sector · HQ · Report date │ -│ Tag pills: sector, geography, fund, status │ -├─────────────────────────────────────────────────────────────────┤ -│ KPI STRIP (5 cards, full width) │ -│ LTM Revenue | LTM EBITDA | Enterprise Value | Net Debt | MOIC │ -├───────────────────────────┬─────────────────────────────────────┤ -│ LEFT COLUMN │ RIGHT COLUMN │ -│ │ │ -│ Financial Performance │ Revenue & EBITDA Bar Chart │ -│ Table │ (last 4 LTM periods, SVG inline) │ -│ │ │ -│ Valuation Summary │ Investment Returns Table │ -│ Table ← must stay here │ │ -│ │ │ -├───────────────────────────┴─────────────────────────────────────┤ -│ COMMENTARY (full width, up to 3 columns) │ -│ Business Update | Valuation Rationale | Discount Rationale │ -├─────────────────────────────────────────────────────────────────┤ -│ FOOTER — © {firm_name} | {website} | {confidentiality_label} │ -└─────────────────────────────────────────────────────────────────┘ -``` - ---- - -## Step 4 — Panel Specifications - -### Header Strip - -- Background: `bg_header` → `bg_primary` gradient, 135° -- **Logo:** if `logo_url` is set, render `` at `logo_position`; - maintain clearspace equal to the cap-height of the firm name on all sides -- **Logo fallback:** if `logo_url` is blank, render firm name in `font_heading`, - `font_heading_weight`, 24px, `text_primary` -- Company name: `font_heading`, 28px, `accent_primary` -- Subtitle (fund · sector · HQ · date): `font_body`, 12px, `text_muted` -- Tag pills: `bg_secondary` fill, `accent_secondary` border, `font_body` 10px - -### KPI Strip (5 cards) - -| Card | Value | -|---|---| -| LTM Revenue | Current period revenue | -| LTM Adj. EBITDA | Valuation basis if available, else Reported | -| Enterprise Value | Current quarter EV | -| Net Debt | Current quarter net debt | -| Gross MOIC | Blended or primary investment MOIC | - -- Card: `bg_secondary` background, 6px border-radius, `box-shadow: 0 2px 8px rgba(0,0,0,0.4)` -- Value: `font_heading`, 28px, `accent_primary` -- Label: `font_body`, 9px, uppercase, letter-spacing 0.05em, `text_primary` -- Delta: `▲ +X%` in `accent_positive` · `▼ -X%` in `accent_negative` - -### Financial Performance Table (LEFT column, top) - -Rows (LTM, 3 prior periods + current quarter, vs. PY column): - -| Row | Notes | -|---|---| -| Revenue | | -| Gross Profit | | -| Gross Margin % | | -| EBITDA | | -| EBITDA Margin % | | -| **Adj. EBITDA (Valuation)** | Highlight row — `font_heading`, `accent_primary` value | -| **Adj. EBITDA Margin %** | Highlight row | -| Net Debt | | - -- Header row: `table_header_bg`, `font_body` 9px uppercase, `text_primary` -- Alternating rows: `bg_secondary` / `bg_primary` -- Highlight rows: `rgba({accent_primary}, 0.06)` background -- Numeric columns right-aligned; label column left-aligned - -### Valuation Summary Table (LEFT column, directly below Financial Performance) - -| Row | Notes | -|---|---| -| Valuation Multiple | `X.Xx` | -| Adj. EBITDA (LTM) | currency | -| **Enterprise Value** | Highlight row | -| Total Debt | | -| Cash | | -| Net Debt | | -| **Total Equity Value** | Highlight row | - -Prior Quarter vs Current Quarter, plus a delta column (`accent_positive` / `accent_negative`). - -### Revenue & EBITDA Bar Chart (RIGHT column, top) - -- **Inline SVG only** — no external JS or D3 -- Side-by-side bars: Revenue in `accent_secondary`, Adj. EBITDA in `accent_negative` -- Current quarter Revenue bar: `accent_primary` -- 4 LTM periods on x-axis; `$Xm` labels above each bar -- Current quarter label: `font_heading`, `accent_primary`; prior: `font_body`, `text_muted` -- Y-axis gridlines: dashed, `rgba({accent_primary}, 0.08)` -- Legend: colour swatches + labels, `font_body` 9px -- Auto-scale: y-axis max = largest revenue × 1.2; bar heights proportional - -### Investment Returns Table (RIGHT column, bottom) - -| Column | Format | -|---|---| -| Investment name | Left-aligned | -| Entry date | `Mon YYYY` | -| Ownership % | `X.X%` | -| Cost (Gross) | `$Xm` | -| Realized | `$Xm` | -| Unrealized | `$Xm` | -| MOIC (Gross) | `X.XXx` — `font_heading`, `accent_primary` | - -- Sort: largest cost first -- **Total / Blended** row at bottom in `font_heading` -- Add IRR column if available; omit column if all values null -- Unavailable values: `—` -- One-sentence QoQ note below table: `font_body` 9.5px, `text_muted` - -### Commentary Section (full width, up to 3 columns) - -1. **Business Update** — verbatim from model if present; AI-generated in MCP mode -2. **Rationale for Valuation Conclusion** — verbatim from model if present -3. **Rationale for Discount to Comps** — verbatim from model if present - -- Eyebrow label: `accent_primary`, 9px, uppercase, letter-spacing 0.08em, - 1px bottom border in `accent_primary` -- Body: `font_body`, 11px, `text_muted`, line-height 1.6 -- Collapse empty columns — never show a blank block - ---- - -## Step 5 — LP Quarterly Update Additions - -When report type is **LP Quarterly Update**: - -1. Header eyebrow → `LP QUARTERLY UPDATE · Q[N] YYYY` -2. Commentary must come from model verbatim — never AI-generate for LP reports -3. Add an optional **valuation bridge note** below the Valuation Summary table: - *"EV increase driven by multiple expansion from X.Xx to Y.Yx; EBITDA contribution +$Zm"* - ---- - -## Step 6 — CSS Variables & Font Loading - -Populate these from the resolved brand tokens (Step 0). Set once in ` -``` - ---- - -## Step 7 — Formatting Rules - -| Type | Format | -|---|---| -| Currency (millions) | `$43.5m` | -| Negative / net cash | `($3.4m)` in `var(--accent-negative)` | -| Multiples | `1.41x` | -| Percentages | `15.7%` | -| Basis point changes | `+70bps` | -| Dates | `Q4 2024` or `31 Dec 2024` | -| MOIC | `4.97x` — heading font, `var(--accent-primary)` | -| Positive delta | `▲ +X%` — `var(--accent-positive)` | -| Negative delta | `▼ -X%` — `var(--accent-negative)` | -| Unavailable | `—` (em dash) — never fabricate | - ---- - -## Step 8 — Output - -- **File name:** `[company_name_lowercase_underscored]_[report_type]_[quarter].html` - - Examples: `ashworth_health_gp_report.html` · `ashworth_health_lp_update_q4_2024.html` -- **Single self-contained HTML file** — all CSS in ` -``` - ---- - -## Step 7 — Formatting Rules - -| Type | Format | -|---|---| -| Currency (millions) | `$43.5m` | -| Negative / net cash | `($3.4m)` in `var(--accent-negative)` | -| Multiples | `1.41x` | -| Percentages | `15.7%` | -| Basis point changes | `+70bps` | -| Dates | `Q4 2024` or `31 Dec 2024` | -| MOIC | `4.97x` — heading font, `var(--accent-primary)` | -| Positive delta | `▲ +X%` — `var(--accent-positive)` | -| Negative delta | `▼ -X%` — `var(--accent-negative)` | -| Unavailable | `—` (em dash) — never fabricate | - ---- - -## Step 8 — Output - -- **File name:** `[company_name_lowercase_underscored]_[report_type]_[quarter].html` - - Examples: `ashworth_health_gp_report.html` · `ashworth_health_lp_update_q4_2024.html` -- **Single self-contained HTML file** — all CSS in ` - - - - - - - - - -``` diff --git a/plugins/daloopa/skills/earnings-flash/SKILL.md b/plugins/daloopa/skills/earnings-flash/SKILL.md deleted file mode 100644 index c79eb4a08..000000000 --- a/plugins/daloopa/skills/earnings-flash/SKILL.md +++ /dev/null @@ -1,160 +0,0 @@ ---- -name: earnings-flash -description: Rapid first-read earnings flash for a given company ---- - -Generate a rapid earnings flash for the company specified by the user named in the user's request. If no ticker or company is provided, ask for one before proceeding. - -This is a lightweight, speed-focused version of the earnings-review skill — designed for a quick first read within minutes of a filing. It pulls just enough context from Daloopa to frame BEAT/MISS verdicts, then focuses on what's new and surprising. - -**Before starting, read `../data-access.md` for data access methods and `../design-system.md` for formatting conventions.** Follow the data access detection logic and design system throughout this skill. - -## 1. Company Lookup - -Look up the company by ticker using `discover_companies`. Capture: -- `company_id` -- `latest_calendar_quarter` — anchor for all period calculations (see `../data-access.md` Section 1.5) -- `latest_fiscal_quarter` -- Firm name for report attribution (default: "Daloopa") — see `../data-access.md` Section 4.5 - -## 2. Prior Quarter Context (4 Quarters) - -Calculate 4 quarters backward from `latest_calendar_quarter`. Search for and pull these core metrics: - -**Income Statement:** -- Revenue / Net Sales -- Gross Profit -- Operating Income / EBIT -- Net Income -- Diluted EPS - -**Cash Flow:** -- Operating Cash Flow -- Free Cash Flow (or CapEx to compute it) - -This is lighter than the earnings-review skill (4Q vs 8Q, no cost structure breakdown). The goal is just enough history to frame the latest quarter's results — not a full trend analysis. - -## 3. Company-Specific KPIs - -Think about the 3-5 most important KPIs for THIS company based on its business model. Search for those specific KPIs and pull for the same 4-quarter period. Also search for: -- Segment/product revenue breakdown -- Geographic revenue breakdown (if material) - -Keep this targeted — discover the critical operating metrics, not everything available. - -## 4. Guidance Series - -Search for guidance series (revenue guidance, EPS guidance, margin guidance, any KPI guidance). If available, pull guidance data for the latest 2 quarters so you can compare the most recent actual results against what management guided. - -CRITICAL: Apply +1 quarter offset — guidance from Q(N) applies to Q(N+1) results. - -## 5. Get the Earnings Document - -Use `search_documents` to find the most recent earnings-related filing. Search strategy: -1. Search for keywords `["results", "earnings"]` in the latest 1-2 calendar quarters -2. If that returns nothing, try `["revenue"]` or `["financial"]` as broader terms - -Read the document content from the search results. Focus on: -- **Earnings transcripts**: Full document (management commentary, prepared remarks, Q&A) -- **10-Q / 10-K**: Financial statements and MD&A sections -- **8-K**: Full document (short event-driven filings) - -If no document is found, proceed with the MCP fundamentals data only and note "No earnings document found — analysis based on financial data only." - -## 5b. Stock Price Context -Get the current stock price using `get_stock_prices` (see `../data-access.md` Section 1.7) — pass `company_id` and `dates` for the 3 most recent calendar days. Also pull prices around the earnings date (1 day before to 3 days after the `latest_calendar_quarter` end + ~30-45 days) to compute the post-earnings reaction. Include the next-day move percentage in the Executive Flash section. - -## 6. Executive Flash - -Write 3-5 bullet-point verdicts. Each bullet MUST compare the latest quarter's results against prior periods from Step 2 and/or guidance from Step 4. Format: - -**[BEAT/MISS/INLINE/MIXED] | Key number (YoY change) | One-sentence context** - -Examples: -- **BEAT | Revenue $95.4bn (+6.1% YoY) | Acceleration from +4.8% last quarter driven by iPhone 16 cycle** -- **MISS | EPS $1.46 vs $1.52 prior year | Higher opex from AI investments weighed on margins** -- **GUIDANCE UP | FY2026 revenue guided $400-405bn | Management raised full-year outlook on cloud strength** - -Use Daloopa citation links for all figures sourced from MCP. Use "(per filing)" for figures only found in the document. - -Also include a one-line **Management Tone** assessment (confident/cautious/defensive/evasive/optimistic) if an earnings document was available. Support with specific language from the document. - -## 7. Key Numbers Table - -Present the latest quarter's results with comparison context: - -| Metric | Latest Quarter | Prior Quarter | YoY Change | vs Guidance | -|--------|---------------|---------------|------------|-------------| - -Include: revenue, EPS, margins, segment breakdowns, KPIs — all sourced from MCP with Daloopa citation links. Add a "vs Guidance" column if guidance data was available from Step 4 (show beat/miss amount). - -Group by category: P&L, Segments, KPIs, Cash Flow. - -For figures only available from the document (not in MCP), include them in a separate "Per Filing" sub-section below the table and note they are not cross-referenced. - -## 8. Guidance & Outlook - -Extract forward-looking statements from the earnings document (if available): -- Explicit numerical guidance (revenue, EPS, margin ranges) -- Changes from prior guidance (raised, lowered, narrowed, withdrawn) -- Qualitative outlook language -- Capex/investment plans - -If guidance data was pulled from Daloopa in Step 4, compare new guidance against prior guidance with a table: - -| Metric | New Guidance | Prior Guidance | Change | -|--------|-------------|---------------|--------| - -If no document was found, summarize any guidance series data from Step 4 and note that no new guidance language is available. - -## 9. Risk Flags - -Call out concerning signals — this section should be sharp and skeptical: -- Guidance cuts or narrowing -- Missing disclosures or metrics that were previously reported -- Growing gap between GAAP and non-GAAP -- Cash flow divergence from earnings -- One-time items that flatter the headline numbers -- Management hedging or qualifying language (from document) - -If no material risk flags, say so clearly: "No material risk flags identified." - -## 10. Quick Read-Throughs - -Write 2-3 bullets on what this filing implies for adjacent companies: -- **Suppliers**: Positive or negative signal for key input providers -- **Customers**: Demand signal for downstream buyers -- **Competitors**: Share shift, pricing, or market growth implications - -Format: `**[COMPANY/SECTOR]**: [implication] (based on [specific data point])` - -## 11. Save Report - -Save the HTML report to: `reports/{TICKER}_earnings_flash_{PERIOD}.html` (where PERIOD is the latest calendar quarter analyzed). - -Use the design-system HTML template from `../design-system.md`. Include all CSS inlined. - -Add a **FLASH** banner at the top of the report. Insert this right after the opening `` tag, before the `

`: - -```html -
- EARNINGS FLASH — FIRST READ -
-``` - -The `

` should be: `{TICKER} Earnings Flash — {PERIOD}` - -Add a disclaimer after the flash banner: -```html -

- This is a rapid first-read summary. For full analysis with 8-quarter trends, cost structure, - and competitive read-throughs, run the earnings-review skill for {TICKER}. -

-``` - -Replace `{FIRM_NAME}` in the footer — see `../data-access.md` Section 4.5. - -All financial figures from Daloopa must use citation format: `$X.XX million` - -Tell the user where the HTML report was saved and highlight the 2-3 most notable findings. - diff --git a/plugins/daloopa/skills/earnings-flash/agents/openai.yaml b/plugins/daloopa/skills/earnings-flash/agents/openai.yaml deleted file mode 100644 index d20dfd096..000000000 --- a/plugins/daloopa/skills/earnings-flash/agents/openai.yaml +++ /dev/null @@ -1,6 +0,0 @@ -interface: - display_name: Earnings Flash - short_description: Rapid first-read earnings flash for a given company - default_prompt: Draft a rapid earnings flash for AAPL. -policy: - allow_implicit_invocation: true diff --git a/plugins/daloopa/skills/earnings-prep/SKILL.md b/plugins/daloopa/skills/earnings-prep/SKILL.md deleted file mode 100644 index 1cfb5f3e0..000000000 --- a/plugins/daloopa/skills/earnings-prep/SKILL.md +++ /dev/null @@ -1,258 +0,0 @@ ---- -name: earnings-prep -description: Pre-earnings preparation report for the night before a company reports ---- - -Generate a pre-earnings preparation report for the company specified by the user named in the user's request. If no ticker or company is provided, ask for one before proceeding. - -This is the note a L/S equity analyst reads the night before a company reports — it tells them exactly what to focus on when the print drops. - -**Before starting, read `../data-access.md` for data access methods and `../design-system.md` for formatting conventions.** Follow the data access detection logic and design system throughout this skill. - -Follow these steps: - -## 1. Company Lookup -Look up the company by ticker using `discover_companies`. Capture: -- `company_id` -- `latest_calendar_quarter` — anchor for all period calculations below (see `../data-access.md` Section 1.5) -- `latest_fiscal_quarter` -- Firm name for report attribution (default: "Daloopa") — see `../data-access.md` Section 4.5 - -Determine the **upcoming quarter** — the one AFTER `latest_calendar_quarter`. This is the quarter the company is about to report. All analysis is oriented around preparing the analyst for this print. - -## 2. Last Quarter Recap -Pull the most recent quarter's full financials from Daloopa. Calculate 4 quarters backward from `latest_calendar_quarter` (for YoY context). - -**Pull:** -- Revenue, Gross Profit, Operating Income, EBITDA, Net Income, Diluted EPS -- Operating Cash Flow, CapEx, FCF (calc.) -- Segment/product revenue breakdown -- Company-specific KPIs (use the business-model taxonomy: SaaS → ARR/NRR/RPO; Consumer → DAU/ARPU; E-commerce → GMV/take rate; etc.) - -**Summarize the story of last quarter in 3-5 bullets:** -- What beat expectations (guidance or consensus)? -- What missed or disappointed? -- What was the stock reaction? (use `get_stock_prices` per `../data-access.md` Section 1.7 to get the actual next-day move; supplement with WebSearch for narrative context if needed) -- What narrative emerged from the call? (e.g., "AI monetization acceleration," "margin expansion story intact," "consumer weakness") -- What was the single most debated metric? - -This is the baseline everyone on the upcoming call will be anchoring to. - -## 3. Outstanding Guidance for Upcoming Quarter -Search for ALL guidance series using keywords: "guidance", "outlook", "estimate", "forecast", "target". Apply the +1 quarter offset to identify which guidance applies to the upcoming print: -- CRITICAL: Guidance from Q(N) earnings call applies to Q(N+1) results -- The guidance issued during the `latest_calendar_quarter` earnings call is what applies to the upcoming quarter - -**Pull and present:** -- Revenue guidance (point estimate or range) -- EPS guidance -- Margin guidance (gross, operating, EBITDA) -- CapEx guidance -- Segment-level guidance (if available) -- KPI guidance (subscriber adds, unit volumes, ARPU targets, etc.) - -**Search filings for directional/qualitative guidance:** -- Search documents for: "expect", "anticipate", "similar to", "consistent with" -- Search documents for: "low single digit", "mid single digit", "double digit", "sequential" -- Search documents for: "headwind", "tailwind", "conservatively", "assumes" -- Capture exact management quotes with document citations - -**Flag any guidance updates between quarters:** -- Search for "pre-announce", "update", "revise" in the most recent quarter's filings -- Check if the company issued an 8-K updating guidance after the last earnings call - -Present all guidance in a single table: Metric | Guidance Value | Source Quarter | Type (Quantitative/Directional). - -## 4. Guidance Credibility & Whisper Number -This section MUST be built entirely from Daloopa data — guidance series AND actual result series pulled via `get_company_fundamentals`. Do not use web search or estimates for this analysis. - -**Step 1: Pull 8 quarters of guidance data.** -You already discovered guidance series in Section 3. Now pull ALL of those guidance series for the last 8 quarters (from `latest_calendar_quarter` backward). These are the guidance values management provided each quarter. - -**Step 2: Pull 8 quarters of corresponding actuals.** -For every guided metric, identify the corresponding actual result series (e.g., if there is a "Revenue guidance" series, pull the actual "Revenue" series). Pull these actuals for the same 8-quarter period. - -**Step 3: Build the complete beat/miss table.** -Apply the +1 quarter offset: guidance from Q(N) is compared to the actual result in Q(N+1). For EVERY quarter where both a guidance value and a corresponding actual exist, compute: -- Guidance value (midpoint if range) -- Actual value -- Delta (Actual - Guidance midpoint) -- Beat/Miss % ((Actual - Guidance midpoint) / |Guidance midpoint| × 100) -- Classification: Beat / In-line / Miss (use +/-1% threshold for in-line) - -**Present a FULL detail table — every quarter, every guided metric.** This is the core analytical engine of the whisper number. Do not summarize or abbreviate — show all rows. Format: - -| Guidance Source Qtr | Metric | Guidance (Mid) | Actual Qtr | Actual | Delta | Beat/Miss % | - -If a company provides range guidance (low/high), show the midpoint and note the range width. If a company only provides directional guidance for some metrics (e.g., "revenue growth in low teens"), convert to an implied numeric value for comparison (e.g., 12-13% → midpoint ~12.5% applied to prior year actual). - -**Step 4: Compute summary statistics from the detail table:** -- Beat rate per metric (% of quarters where actual > guidance midpoint) -- Average beat magnitude per metric (in absolute terms and %) -- Beat pattern trend: is the beat getting larger (sandbagging increasing), shrinking (guidance getting more accurate), or volatile? Look at the last 4 vs. prior 4. -- Range width trend: is management tightening or widening guidance ranges? - -**Step 5: Calculate the implied "whisper number":** -- Whisper = Current guidance midpoint + Average historical beat (from the detail table above) -- This is the REAL bar the stock is trading against, not the stated guidance -- If the company beats by 2% on average, the market expects a 2% beat — an in-line result to guidance is effectively a miss -- Calculate whisper for EVERY guided metric, not just revenue - -**Present the whisper summary:** -| Metric | Current Guidance (Mid) | Avg Historical Beat | Implied Whisper | Beat Rate (n/N) | - -**Credibility verdict:** Is management's guidance informative (tight, accurate) or performative (always sandbagged, uninformative)? If the beat rate is >90%, say so — it means the guidance number is a floor, not a forecast. If the beat magnitude is increasing, management is becoming MORE conservative over time. - -## 5. Peer & Adjacent Company Read-Throughs -This is the most differentiated section. For companies in the same sector that have ALREADY reported this earnings season, their results contain direct signal about the upcoming print. - -**Identify the read-through universe (aim for 5-8 companies):** -- **Competitors**: Direct rivals in the same market -- **Suppliers**: Companies that sell to the target company -- **Customers**: Companies that buy from the target company -- **Industry bellwethers**: Large companies whose results signal sector trends - -**CRITICAL: Always use Daloopa as the primary data source for peer analysis.** For each peer: - -1. **Look up the peer in Daloopa:** `discover_companies` with the peer's ticker. If Daloopa has the company, check `latest_calendar_quarter` to determine whether they have already reported the relevant quarter. -2. **If the peer has data for the current earnings season quarter:** Pull their financials from Daloopa (`discover_company_series` → `get_company_fundamentals`). Focus on 2-4 metrics most relevant to the read-through (e.g., for a supplier: revenue, segment breakdown, inventory; for a competitor: revenue growth, market share proxies, pricing commentary). -3. **Search the peer's filings in Daloopa:** `search_documents` with keywords related to the target company's products, markets, or industry (e.g., for an Apple supplier, search for "Apple", "smartphone", "consumer electronics"). -4. **Use WebSearch only to supplement Daloopa data** — for earnings-season timing confirmation, stock price reactions, or analyst commentary that Daloopa filings don't cover. - -**For each read-through, extract (with Daloopa citations):** -1. **The specific data point** — the peer's metric that creates signal. Cite the Daloopa `fundamental_id`. -2. **The implication** — bullish or bearish for the target company, and why -3. **Confidence level** — High (direct disclosed relationship), Moderate (inferred from industry), Low (circumstantial) - -**For peers that haven't reported yet:** Note them as "reports after {TICKER}" — their results will be a read-through in the opposite direction. - -**Group read-throughs by:** -- **Competitors** — share shift signals, pricing environment, demand trends -- **Suppliers** — order book signals, inventory levels, capacity commentary -- **Customers** — demand signals, inventory destocking/restocking, spending priorities -- **Industry Bellwethers** — macro/sector health, end-market demand - -**Web research for sector context (supplementary only — after Daloopa pulls):** -- Search: `"{TICKER} sector earnings season {year} read through"` — analyst commentary on cross-company signals -- Search: `"{TICKER} competitors results {upcoming_quarter_label} {year}"` — what peers have already signaled - -## 6. Key Metrics to Watch -Identify the 5-7 metrics the analyst should focus on when the print drops. For each metric: - -| Metric | Current Level | Guidance/Expected | Bullish Threshold | Bearish Threshold | Why It Matters | - -**Be specific with thresholds** — not "revenue growth" but "revenue above $95B signals iPhone cycle acceleration; below $92B confirms China weakness." Not "margins" but "gross margin above 47% confirms services mix shift; below 45% signals hardware pricing pressure." - -**Prioritize by information value:** -1. Metrics where guidance has been vague or directional (highest uncertainty) -2. Metrics where peer read-throughs are conflicting (the print will resolve the debate) -3. Metrics that drive the forward multiple (the ones the market will re-rate on) -4. KPIs that lead revenue by 1-2 quarters (predictive of next quarter's financials) - -## 7. Consensus & Positioning -Gather available consensus context: - -**From data sources (consensus estimates if available per `../data-access.md` Section 3):** -- Consensus revenue and EPS for the upcoming quarter -- Number of analysts at Buy / Hold / Sell -- Consensus price target (median and range) -- Recent estimate revision trends (last 30/60/90 days — moving up or down?) - -**From web search (supplement or replace if consensus data unavailable):** -- Search: `"{TICKER} earnings preview consensus estimates {upcoming_quarter_label} {year}"` — sell-side previews -- Search: `"{TICKER} analyst expectations {year}"` — positioning and sentiment - -**Note limitations** if consensus data is not directly available. Even directional context ("estimates have been revised up 3% over the last 90 days") is valuable. - -## 8. Historical Earnings Reaction -**Stock price data (from Daloopa):** -Use `get_stock_prices` (see `../data-access.md` Section 1.7) to get actual post-earnings price moves for the last 4-6 earnings prints. For each historical earnings date, pull prices for a window: `start_date` = 1 trading day before earnings, `end_date` = 3-5 trading days after. Compute: -- Next-day move (pre-earnings close → post-earnings close) -- 3-day drift (post-earnings close → 3 days later) - -To estimate historical earnings dates, use the quarter-end date + ~30-45 days as an approximation, or use WebSearch to confirm exact dates if needed. - -Also pull the current stock price (3 most recent calendar days) for the report header. - -**Supplement with web search for options context:** -- Search: `"{TICKER} options implied move earnings {upcoming_quarter_label}"` — current implied volatility - -**Present as a table:** -| Quarter | Revenue Beat/Miss | EPS Beat/Miss | Next-Day Move | 3-Day Drift | Notes | - -Populate the Revenue/EPS Beat/Miss columns from the guidance credibility analysis in Section 4. The price move columns come from `get_stock_prices`. - -**Pattern identification:** -- Does the stock tend to sell off on beats? (buy-the-rumor, sell-the-news pattern) -- Does it rally on in-line results? (low expectations already embedded) -- Is there a pattern of post-earnings drift (continued move in the days after)? -- What's the current implied move from the options market? If it's elevated vs. history, the market expects a big move. - -## 9. Macro & Sector Backdrop -Web search for developments since last quarter that could affect results: -- Search: `"{TICKER} {industry} outlook {current_year}"` — sector developments -- Search: `"{TICKER} headwinds tailwinds {current_year}"` — company-specific macro factors - -**Distill into 5-8 bullets, each with a directional tag (Positive / Negative / Uncertain):** -- Industry-specific: new regulations, competitor product launches, market share shifts -- Macro: FX moves (specify currencies and direction), commodity prices, interest rates -- Policy: tariffs, trade restrictions, tax changes -- Channel: inventory levels in the channel, distributor commentary, supply chain status -- Company-specific: product launches since last quarter, management changes, M&A - -Keep each bullet to one sentence. The analyst needs context, not a macro essay. - -## 10. Potential Surprises & Call Catalysts -Beyond the numbers, what could management announce that would move the stock? Search filings and news for signals: -- Search documents: "restructuring", "acquisition", "buyback", "dividend" in recent filings -- Search: `"{TICKER} potential announcement catalyst {year}"` — speculative but grounded - -**Categories:** -- **Capital allocation**: New buyback authorization, dividend change (hike/cut/initiation), M&A announcement, asset sale/spinoff -- **Operational**: Restructuring/layoffs, new product launch, partnership/contract win, segment reporting changes -- **Strategic**: New guidance metrics, long-term targets update, management changes, investor day announcement -- **Accounting/Disclosure**: Guidance methodology change, segment redefinition, one-time charge pre-announcement - -For each potential surprise, note the signal strength (rumored / speculated / no signal) and the likely stock impact direction. - -## 11. Pre-Earnings Checklist -A concise, actionable summary that fits on a single card. This is what the analyst tapes to their monitor: - -**The Numbers:** -- Revenue whisper: $X.XX (guidance: $X.XX, avg beat: +X.X%) -- EPS whisper: $X.XX (guidance: $X.XX, avg beat: +X.X%) - -**Top 3 Metrics to Watch:** -1. [Metric] — current: X, bull: >Y, bear: Y, bear: Y, bear: $X.XX million` - -Tell the user where the HTML report was saved. - -Highlight what makes this print particularly interesting: Is the whisper number meaningfully above guidance (setting up for disappointment even on a beat)? Are peer read-throughs conflicting (creating genuine uncertainty)? Is there a potential surprise catalyst that could overshadow the numbers? Give the analyst the single most important thing to watch. diff --git a/plugins/daloopa/skills/earnings-prep/agents/openai.yaml b/plugins/daloopa/skills/earnings-prep/agents/openai.yaml deleted file mode 100644 index 950cf60c5..000000000 --- a/plugins/daloopa/skills/earnings-prep/agents/openai.yaml +++ /dev/null @@ -1,7 +0,0 @@ -interface: - display_name: Earnings Prep - short_description: Pre-earnings preparation report for the night before a company - reports - default_prompt: Prepare a pre-earnings report for NVDA. -policy: - allow_implicit_invocation: true diff --git a/plugins/daloopa/skills/earnings-review/SKILL.md b/plugins/daloopa/skills/earnings-review/SKILL.md deleted file mode 100644 index fcdc84643..000000000 --- a/plugins/daloopa/skills/earnings-review/SKILL.md +++ /dev/null @@ -1,232 +0,0 @@ ---- -name: earnings-review -description: Full earnings analysis with guidance tracking for a given company ---- - -Perform a comprehensive earnings analysis for the company named in the user's request. If no ticker or company is provided, ask for one before proceeding. - -**Before starting, read `../data-access.md` for data access methods and `../design-system.md` for formatting conventions.** Follow the data access detection logic and design system throughout this skill. - -Follow these steps: - -## 1. Company Lookup -Look up the company by ticker using `discover_companies`. Capture: -- `company_id` -- `latest_calendar_quarter` — anchor for all period calculations below (see `../data-access.md` Section 1.5) -- `latest_fiscal_quarter` -- Firm name for report attribution (default: "Daloopa") — see `../data-access.md` Section 4.5 - -## 2. Core Financial Metrics -Calculate 8 quarters backward from `latest_calendar_quarter`. Search for these metrics, then pull: - -**Income Statement:** -- Revenue / Net Sales -- Gross Profit -- Operating Income / EBIT -- EBITDA (if not reported, compute as Operating Income + D&A — label it "EBITDA (calc.)") -- Net Income -- Diluted EPS -- Operating Expenses (SG&A, R&D where available) - -**Cash Flow & Balance Sheet:** -- Operating Cash Flow -- CapEx (Purchases of property, plant and equipment) -- Free Cash Flow (compute as Operating Cash Flow - CapEx — label it "FCF (calc.)") -- D&A (needed for EBITDA calc if not directly reported) - -For any derived/computed metric, mark it with "(calc.)" so the reader knows it's not directly sourced. - -Flag any one-time items that distort a quarter (e.g., tax charges, impairments, litigation settlements) with a footnote so YoY comparisons aren't misleading. - -## 3. Company-Specific KPIs -First, think about what the most important KPIs are for THIS specific company based on its business model and what drives its valuation. For example: -- **SaaS/cloud**: ARR, net revenue retention, RPO/cRPO, customers >$100K -- **Consumer tech**: DAU/MAU, ARPU, engagement metrics, installed base, paid subscribers -- **E-commerce/marketplace**: GMV, take rate, active buyers/sellers, order frequency -- **Retail**: same-store sales, store count, average ticket, transactions -- **Telecom/media**: subscribers, churn, ARPU, content spend -- **Hardware**: units shipped, ASP, attach rate -- **Financial services**: AUM, NIM, loan growth, credit quality metrics -- **Pharma/biotech**: pipeline stage, patient starts, scripts, market share - -Then search for those specific KPIs by name, plus cast a wider net for anything else available. Also search for: -- Segment/product revenue breakdown -- Geographic revenue breakdown - -Pull for the same 8-quarter period. If some KPIs only have data for recent quarters, include what's available and note the gap. - -## 4. Growth & Margins -Calculate and present: -- YoY revenue growth for each of the last 4 quarters (not just one) -- Gross margin, operating margin, EBITDA margin, net margin trends over 8 quarters -- EPS growth YoY for each of the last 4 quarters -- Segment revenue YoY growth for the most recent quarter -- Geographic revenue YoY growth for the most recent quarter -- KPI growth rates where applicable - -If the company has strong seasonality (e.g., retail Q4 holiday, back-to-school, cyclical patterns), add a note so the reader interprets QoQ swings correctly. - -## 4.5. Cost Structure & Margin Drivers -Decompose what's driving margin trends. This turns the margin table from Section 4 into an analytical narrative. - -**COGS Analysis:** -- Pull product COGS and services COGS (or equivalent cost breakdown) if available -- Identify the 3-5 biggest cost line items and their YoY trends -- Is COGS growing faster or slower than revenue? If slower, what's driving the efficiency — input costs, mix shift, pricing power, or scale leverage? - -**OpEx Breakdown:** -- Pull R&D and SG&A separately for the last 8 quarters -- Compute R&D % of revenue and SG&A % of revenue trends -- Is the company investing more in R&D (growth mode) or cutting SG&A (efficiency mode)? Both? Neither? -- Flag any quarter where OpEx growth materially exceeds revenue growth — that's operating deleverage - -**Margin Driver Synthesis:** -For each major margin (gross, operating, net), write 1-2 sentences identifying what's driving expansion or compression: -- Pricing power vs cost inflation -- Mix shift (higher-margin products/services growing faster) -- Scale leverage vs investment spending -- One-time items distorting the trend -- FX impact if material - -Include this as a commentary block after the margins table in the report. Cite specific Daloopa figures. - -## 5. Guidance vs Actuals -Search for guidance series (revenue guidance, EPS guidance, margin guidance, OpEx guidance, any KPI guidance). If available: -- Pull guidance and actual results -- CRITICAL: Apply +1 quarter offset — guidance from Q(N) applies to Q(N+1) results -- Calculate beat/miss amounts and percentages -- Note patterns (consistent beats, narrows, etc.) -- If the company provides directional guidance (e.g., "low-to-mid-teens growth") rather than hard numbers, note this and compare against the actual growth rate - -If no formal guidance series exist, note that the company does not provide quantitative guidance. - -## 6. Consensus Context (if available) -If consensus estimates are available (see `../data-access.md` Section 3), add: -- Consensus revenue and EPS vs actual results — beat/miss vs Street -- Estimate revision trends (are estimates moving up or down?) -- Note the source of consensus data used - -If consensus data is not available, skip this section and note "consensus data not available." - -## 7. Management Commentary -Search SEC filings/documents for management commentary. Try multiple searches to get broad coverage: -- First search: "results" or "record" for earnings highlights -- Second search: "outlook" or "guidance" for forward-looking commentary -- Third search: strategy-specific terms relevant to the company (e.g., "AI", "cloud", "subscribers") -- If a search returns empty, try broader single-keyword searches before giving up - -Extract: -- Earnings results and key drivers -- Forward outlook and guidance language -- Segment performance highlights -- Any notable call-outs (one-time items, macro commentary, strategic updates) -- Direct management quotes where available (with document citations) - -## 7.5. News Context & Stock Reaction -**Stock price reaction (from Daloopa):** -Use `get_stock_prices` (see `../data-access.md` Section 1.7) to get the actual post-earnings price move. Pull prices for a window around the earnings date: `start_date` = 1 trading day before the likely earnings date (estimate from the `latest_calendar_quarter` end + ~30-45 days), `end_date` = 3 trading days after. Compute the next-day percentage change from the pre-earnings close to the post-earnings close. This gives you the hard number for "how did the stock react." - -Also pull the current stock price (3 most recent calendar days) so the report includes where the stock trades NOW relative to the post-earnings reaction. - -**Web search for context:** -Run 2 WebSearch queries to add external context around the earnings: -1. `"{TICKER} {company_name} earnings {latest_quarter} {year}"` — coverage and analyst reactions -2. `"{TICKER} analyst price target {year}"` — sell-side sentiment - -Distill into a brief **Earnings Context** block (3-5 bullet points): -- How did the stock react to earnings? (use the actual price data from `get_stock_prices`, not just search results) -- What were the key analyst takeaways or debates? -- Any price target changes or rating changes post-earnings? -- Any macro/industry context that affected the quarter? - -Keep this concise — it supplements the Daloopa data with market reaction context. Include it as a short section in the report before the Forward Outlook. - -## 7.6. Forward Outlook & Revenue Drivers - -Synthesize the backward-looking data into a forward-looking view. This section turns the earnings analysis from "what happened" into "what it means for the future." - -**Forward Guidance Analysis:** -- What is management guiding for NEXT quarter and/or full year? Extract specific numbers (revenue range, EPS range, margin targets, CapEx plans). -- Is the guide conservative or aggressive? Compare to: (a) the company's historical beat rate from Section 5, (b) the current run rate extrapolated forward, (c) consensus if available. A company that beats by 3% every quarter and guides flat is sandbagging; a company that guides for acceleration after 3 quarters of deceleration is aggressive. -- How does forward guidance compare to trailing trends? If revenue grew +8% YoY last quarter and guidance implies +5%, is management signaling deceleration or being conservative? - -**Revenue Driver Decomposition:** -- Break down what's driving growth: volume vs price vs mix. Which segments are contributing vs dragging? -- For each major segment, identify the unit economics driver: units x ASP, subscribers x ARPU, GMV x take rate, etc. -- What has to happen for current growth rates to sustain? If growth is coming from price increases, is there a ceiling? If from volume, is the TAM expanding or saturating? - -**KPI Trajectory Implications:** -- Connect KPI trends to revenue outlook. If subscriber growth is decelerating, what does that imply for next quarter's revenue? If ASPs are rising but units are flat, is that sustainable? -- If backlog/RPO/deferred revenue is building, when does it convert to recognized revenue? If it's declining, that's a leading indicator of future revenue pressure. -- Flag any KPI-to-revenue divergences (e.g., user growth accelerating but ARPU declining — net effect on revenue?) - -**Trend Synthesis:** -- Looking at the last 4-8 quarters holistically — is this company accelerating, decelerating, or at a plateau? -- What's the single most important metric to watch next quarter? Why? -- Are operating KPIs leading or lagging the financial results? - -**Risks to the Forward View:** -- What could go wrong with the guidance? What assumptions are embedded that could break? -- Identify the 2-3 biggest risks to the forward trajectory: competitive threats, macro sensitivity, product cycle dependency, regulatory risk, customer concentration. -- If the bull case requires multiple things to go right simultaneously, flag that explicitly. - -## 7.7. Read-Throughs & Competitive Implications - -This is one of the most valuable sections of the report. Every company's earnings contain signal about adjacent companies — suppliers, customers, competitors, and the broader industry. An analyst covering a sector doesn't just read one company's print; they read it for what it says about every other name in their portfolio. - -**Identify the Read-Through Universe:** -Think about who is most affected by this company's results. Consider: -- **Suppliers**: If this company's revenue/COGS/CapEx changed materially, which suppliers feel it? (e.g., AAPL iPhone strength → TSMC, Broadcom, Corning benefit; AAPL CapEx guidance up → supplier order books filling) -- **Customers**: If this company is a major input to others, what do its pricing/volume trends imply? (e.g., TSMC price increases → margin pressure for AAPL, AMD, NVDA) -- **Direct competitors**: How does this quarter compare to what peers have reported or guided? Is this company gaining or losing share? (e.g., MSFT cloud growth accelerating while AMZN AWS decelerates → share shift) -- **Indirect competitors / substitutes**: Any signals about demand shifting between categories? (e.g., strong enterprise software spend → weak services/consulting spend) -- **Industry bellwether signals**: If this is a large company, what do its results say about the macro/sector? (e.g., consumer discretionary weakness at WMT → read-through to all retail) - -**For each read-through (aim for 5-8), state:** -1. **The affected company** (ticker + name) -2. **The specific data point** from this earnings that creates the read-through — cite the Daloopa figure -3. **The implication** — bullish or bearish for the adjacent company, and why -4. **Confidence level** — is this a direct/disclosed relationship (high confidence) or an inferred/estimated one (moderate)? - -**Example read-throughs:** -- "AAPL Services revenue grew +14% YoY to $26.3B → **Positive for APP (AppLovin)**: Apple's App Store is a major distribution channel; growing Services revenue confirms healthy app ecosystem spending. **Negative for GOOG**: AAPL's growing services monetization strengthens their negotiating leverage on the Google TAC agreement." -- "TSMC guided CapEx up 25% YoY → **Positive for ASML, AMAT, LRCX, KLAC**: equipment spend is the most direct read-through to semicap names. ASML in particular given EUV concentration." -- "NFLX added 19M subscribers vs 13M expected → **Negative for DIS, WBD, PARA**: In a zero-sum attention economy, NFLX's accelerating sub growth likely came partly at the expense of other streamers." - -**Sequencing context:** -- Note whether this company reported before or after its peers this earnings season. If it's early in the cycle, the read-throughs are forward-looking predictions. If it's late, compare against what peers already reported — confirm or contradict the emerging narrative. -- If a peer has already reported, note any divergence: "MSFT reported cloud growth of +29% last week; today's AMZN AWS at +19% confirms the share shift narrative." - -**Web research for validation:** -Run 1-2 targeted searches to validate read-throughs: -- `"{TICKER} earnings read through implications {year}"` — analyst commentary on cross-company signals -- `"{TICKER} {peer_ticker} competitive positioning {year}"` — specific competitive dynamics - -Present as a structured list in the report, grouped by relationship type (Suppliers / Customers / Competitors / Industry). Each read-through should be a concise 2-3 sentence paragraph with the data citation, the affected name, and the implication. - -## 8. Save Report -Save to `reports/{TICKER}_earnings_{PERIOD}.html` (where PERIOD is the most recent quarter analyzed) using the HTML report template from `../design-system.md`. Write the full analysis as styled HTML with the design system CSS inlined. This is the final deliverable — no intermediate markdown step needed. - -The report should include: -- Executive summary (2-3 sentence overview of the quarter + 2-3 most notable findings) -- Core financial metrics table (8 quarters, periods as columns, metrics as rows, including FCF) -- Segment and geographic revenue breakdown tables -- KPI table (with notes on any data gaps) -- Margin trends table (8 quarters) -- Cost structure & margin driver commentary (after margins table) -- YoY growth rates table (last 4 quarters, showing each quarter's YoY) -- Guidance vs actuals table (if applicable) with pattern analysis -- News context (analyst reactions, price target changes, market sentiment) -- Forward outlook and revenue drivers analysis -- Management commentary with direct quotes and document citations -- Read-throughs & competitive implications (grouped by Suppliers / Customers / Competitors / Industry) -- Seasonality note if applicable - -All financial figures must use Daloopa citation format: `$X.XX million` - -Tell the user where the HTML report was saved. - -Highlight the 2-3 most notable findings with a critical lens: -- **Quality of earnings**: Are the beats sustainable or driven by one-time items, favorable timing, or accounting changes? Is revenue growth real or pulled forward? -- **Red flags**: Any deterioration in cash conversion, growing GAAP vs non-GAAP gaps, rising SBC dilution, margin expansion from under-investment? -- **What the market is missing**: What does the data say that consensus might not be pricing in — positive or negative? diff --git a/plugins/daloopa/skills/earnings-review/agents/openai.yaml b/plugins/daloopa/skills/earnings-review/agents/openai.yaml deleted file mode 100644 index 362a86d08..000000000 --- a/plugins/daloopa/skills/earnings-review/agents/openai.yaml +++ /dev/null @@ -1,6 +0,0 @@ -interface: - display_name: Earnings Review - short_description: Full earnings analysis with guidance tracking for a given company - default_prompt: Review the latest AAPL earnings with guidance context. -policy: - allow_implicit_invocation: true diff --git a/plugins/daloopa/skills/guidance-tracker/SKILL.md b/plugins/daloopa/skills/guidance-tracker/SKILL.md deleted file mode 100644 index 861343c28..000000000 --- a/plugins/daloopa/skills/guidance-tracker/SKILL.md +++ /dev/null @@ -1,144 +0,0 @@ ---- -name: guidance-tracker -description: Track management guidance accuracy over time for a given company ---- - -Track management guidance accuracy for the company named in the user's request. If no ticker or company is provided, ask for one before proceeding. - -**Before starting, read `../data-access.md` for data access methods and `../design-system.md` for formatting conventions.** Follow the data access detection logic and design system throughout this skill. - -Follow these steps: - -## 1. Company Lookup -Look up the company by ticker using `discover_companies`. Capture: -- `company_id` -- `latest_calendar_quarter` — anchor for all period calculations below (see `../data-access.md` Section 1.5) -- `latest_fiscal_quarter` -- Firm name for report attribution (default: "Daloopa") — see `../data-access.md` Section 4.5 - -## 2. Discover Guidance Series -Search for series with keywords like "guidance", "outlook", "estimate", "forecast", "target" to find all available guidance metrics. Common guidance series include: - -**Financial guidance:** -- Revenue guidance (quarterly and/or annual) -- EPS guidance -- Operating income / margin guidance -- EBITDA guidance -- Segment-level revenue guidance -- CapEx guidance -- Free Cash Flow guidance - -**Operational KPI guidance** — many companies guide on KPIs, and tracking these beats/misses is often more informative than financial guidance: -- Subscriber / user count guidance (e.g., "we expect to add X million subscribers") -- Unit shipment guidance (e.g., "iPhone units", "deliveries") -- ARPU / ASP guidance -- Same-store sales guidance -- GMV / bookings guidance -- Net revenue retention guidance -- Store openings / closings guidance -- Production volume / capacity guidance - -Search explicitly for KPI-specific guidance series using terms like "subscriber guidance", "unit guidance", "ARPU guidance", "same-store sales outlook", "deliveries forecast", "bookings target". These are separate from financial guidance and often reside in different series. - -## 3. Pull Guidance Data -Calculate 8+ quarters backward from `latest_calendar_quarter`. Pull all discovered guidance series for those periods. - -## 4. Pull Actual Results -For each guidance metric, pull the corresponding actual result series for the same periods. - -## 5. Build Guidance vs Actuals Tracker -CRITICAL OFFSET RULES: -- **Quarterly guidance**: Guidance from Q(N) earnings call applies to Q(N+1) results. Compare Q(N) guidance -> Q(N+1) actual. -- **Annual guidance from Q1/Q2/Q3**: Applies to current fiscal year. Compare to FY actual. -- **Annual guidance from Q4**: Applies to NEXT fiscal year. Compare to next FY actual. - -For each guidance-actual pair, calculate: -- Guidance value -- Actual value -- Delta (Actual - Guidance) -- Beat/Miss % ((Actual - Guidance) / |Guidance| x 100) -- Classification: Beat / In-line / Miss (use +/-1% threshold for in-line) - -## 6. Pattern Analysis -Analyze the guidance track record: -- Overall beat rate (% of quarters where actual > guidance) -- Average beat/miss magnitude -- Trend in guidance accuracy (getting tighter? more conservative? less reliable?) -- Any metrics where management is notably conservative or aggressive -- Guidance range width trends (if range guidance is given) - -**Management credibility assessment:** -- If the company consistently beats by a similar margin, call out sandbagging — this suggests management is deliberately setting low bars, which can mask underlying deceleration. A 100% beat rate is not necessarily bullish; it may mean guidance is uninformative. -- If guidance has been cut or missed, assess whether management acknowledged the miss honestly or buried it in adjusted metrics. -- Flag any pattern where qualitative language ("strong demand," "robust pipeline") didn't translate to actual results. - -## 7. Commentary from Filings -Search SEC filings/documents across multiple queries to build a complete picture of guidance practices. If any search returns empty, try alternative keywords before giving up. - -- **Explicit guidance language**: Try "guidance", "outlook"; fallback to "expect", "anticipate", "forecast" -- **Qualitative / directional guidance**: Try "similar to", "consistent with", "growth rate"; fallback to "low single digit", "mid single digit", "high single digit", "double digit", "sequential" - - Many companies provide directional revenue guidance on earnings calls (e.g., "similar to the March quarter" or "low-to-mid-single-digit growth") rather than numeric ranges. Capture these and compare against actual growth rates. -- **Guidance methodology changes**: Try "change", "methodology", "no longer providing"; fallback to "withdraw", "suspend", "discontinue" - - Flag any quarters where the company changed what metrics it guides on, or withdrew guidance entirely -- **Key drivers behind guidance**: Try "assumes", "includes", "excludes"; fallback to "headwind", "tailwind", "impact" - - Capture what management said about the assumptions underpinning their guidance (e.g., FX assumptions, macro assumptions, one-time items included/excluded) - -Extract direct management quotes where available and cite the document source. - -## 7.5. Guidance Read-Throughs to Adjacent Companies - -When a company raises, cuts, or materially changes its guidance, the implications often matter more for adjacent names than for the company itself. This section translates guidance signals into actionable read-throughs. - -**For each major guidance change identified in the tracker, analyze the implications for adjacent companies:** - -**Identify who is affected by this company's guidance:** -- **Suppliers**: Revenue/CapEx guidance changes directly affect supplier order books. A CapEx guidance raise is a near-term purchase order for equipment/component suppliers. A revenue guide-down signals softer demand flowing upstream. -- **Customers**: If this company supplies critical inputs, pricing or capacity guidance affects customer margins. Guiding for price increases = margin headwind for customers. Guiding for capacity expansion = supply relief. -- **Competitors**: Guidance on market growth, pricing environment, or demand trends is often the most honest signal about the competitive landscape. If Company A guides for share gains, that's a direct share loss for Company B. -- **Channel partners / distributors**: Volume guidance changes affect channel inventory and distributor revenue. - -**For each read-through (aim for 4-6), state:** -1. **The guidance data point** — which metric changed, by how much, and in which quarter's call -2. **The affected company** (ticker + name) -3. **The implication** — bullish or bearish, with specific logic -4. **Timing** — is this a next-quarter impact or a multi-quarter trend? - -**Focus on the highest-signal guidance changes:** -- Guidance raises after a period of conservatism → strong signal that the underlying business is inflecting -- Guidance cuts or "reaffirmed" when the market expected a raise → often more bearish than an explicit cut -- New metrics being guided on (or old metrics withdrawn) → management is redirecting attention, which itself is a signal -- Segment-level guidance changes → more specific read-throughs than consolidated figures -- KPI guidance (subscriber adds, unit volumes, ARPU) → often the most direct read-through to suppliers and competitors - -**Example:** -- "NFLX raised Q2 subscriber guidance from +5M to +8M → **Negative for DIS+, WBD**: attention economy is zero-sum; NFLX's accelerating growth likely pressures competing streamers' subscriber adds. **Positive for cloud/CDN names (AMZN/AWS, NET)**: more streaming = more infrastructure demand." -- "TSMC raised full-year CapEx guidance by $4B (from $32B to $36B) → **Positive for ASML**: TSMC is ASML's largest customer; incremental CapEx skews toward EUV tools. **Positive for AMAT, LRCX, KLAC**: broader equipment spend benefits all semicap names." - -**Web research for validation:** -Run 1 targeted search: `"{TICKER} guidance change implications read through {year}"` — analyst commentary on cross-company signals from guidance moves. - -Present as a structured section in the report after the Pattern Analysis, grouped by guidance change (each major guide raise/cut gets its own sub-block with the read-throughs beneath it). - -## 8. Save Report -Save to `reports/{TICKER}_guidance_tracker.html` using the HTML report template from `../design-system.md`. Write the full analysis as styled HTML with the design system CSS inlined. This is the final deliverable — no intermediate markdown step needed. - -The report should include: -- Summary header with company name, ticker, and period covered -- Quarter mapping reference table showing the +1 offset explicitly: - ``` - | Guidance Source Quarter | Guidance Applies To | Actual Result Quarter | - | CQ1 2024 | CQ2 2024 | CQ2 2024 | - | CQ2 2024 | CQ3 2024 | CQ3 2024 | - ``` - This makes the offset rule visible and auditable for every row in the tracker. -- Main tracker table with columns: Guidance Source, Metric, Guidance, Actual Period, Actual, Delta, Beat/Miss (with Daloopa citations on all values) -- Summary statistics (beat rate, avg beat/miss by metric) -- Pattern analysis narrative -- Guidance read-throughs to adjacent companies (grouped by guidance change, with affected tickers and implications) -- Key guidance quotes from filings with document citations - -All financial figures must use Daloopa citation format: `$X.XX million` - -Tell the user where the HTML report was saved. - -Highlight the key patterns (e.g., "Management has beat revenue guidance 7 of the last 8 quarters by an average of 2.3%"). Include an honest credibility verdict: Is management's guidance informative or performative? Should investors trust the forward guidance, and if not, what should they anchor to instead? diff --git a/plugins/daloopa/skills/guidance-tracker/agents/openai.yaml b/plugins/daloopa/skills/guidance-tracker/agents/openai.yaml deleted file mode 100644 index 2dbe7bd16..000000000 --- a/plugins/daloopa/skills/guidance-tracker/agents/openai.yaml +++ /dev/null @@ -1,6 +0,0 @@ -interface: - display_name: Guidance Tracker - short_description: Track management guidance accuracy over time for a given company - default_prompt: Track NVDA guidance accuracy over recent quarters. -policy: - allow_implicit_invocation: true diff --git a/plugins/daloopa/skills/ib-deck/SKILL.md b/plugins/daloopa/skills/ib-deck/SKILL.md deleted file mode 100644 index 435362c91..000000000 --- a/plugins/daloopa/skills/ib-deck/SKILL.md +++ /dev/null @@ -1,119 +0,0 @@ ---- -name: ib-deck -description: Generate an institutional-grade investment banking pitch deck (HTML) ---- - -Build an institutional-grade pitch deck for the company named in the user's request. If no ticker or company is provided, ask for one before proceeding. - -**Before starting, read `../design-system.md` for formatting conventions and `../data-access.md` for data access methods.** Also read the reference files in this skill's `references/` directory for slide templates and components. - -This skill generates a self-contained HTML presentation that can be opened in a browser and printed to PDF if needed. - -## Phase 1 — Requirements - -Determine the deck category and scope: - -**Category** (infer from context, or default to IB Advisory): -- **IB Advisory** — M&A advisory, fairness opinions, board presentations. Navy/steel/gold palette. "CONFIDENTIAL" marking. -- **Activist / L-S Equity** — Shareholder campaigns, investment memos as decks. Navy/blue/orange or navy/sky/green palette. - -**Firm Attribution:** -- Firm name defaults to "Daloopa". If the user specifies a firm name in their prompt, use that instead. -- **NEVER hallucinate a firm name** (Goldman Sachs, Morgan Stanley, JPMorgan, etc.). See `../data-access.md` Section 4.5. -- Include firm name on the cover slide and in all slide footers. - -**Gather from the user or infer:** -- Target company (ticker) -- Purpose (M&A pitch, fairness opinion, investment memo, activist campaign) -- Key thesis or strategic rationale -- Specific slides needed (or use the default 14-slide deck) - -## Phase 2 — Data Gathering - -Look up the company by ticker using `discover_companies`. Capture `company_id`, `latest_calendar_quarter`, and `latest_fiscal_quarter`. Use `latest_calendar_quarter` to anchor all period calculations (see `../data-access.md` Section 1.5). - -Use Daloopa MCP for all financial data. Target comprehensive coverage: -- **5+ years of quarterly financials** — calculate 20+ quarters backward from `latest_calendar_quarter` (income statement, balance sheet, cash flow) -- **Segment and geographic breakdowns** -- **All company-specific operating KPIs** -- **6-10 peers** — get trading multiples and fundamentals from Daloopa + market data (see `../data-access.md` Section 2) -- **Guidance and consensus** (see `../data-access.md` Section 3) -- **SEC filings** — risk factors, growth drivers, M&A commentary, strategic language - -Get market data for the target and all peers: -- Current price, market cap, shares outstanding, beta, trading multiples -- Historical price data for TSR comparison - -Market data resolution order (see `../data-access.md` Section 2): -1. MCP market data tools (if available) -2. Web search for current quotes, multiples, and historical data -3. Sensible defaults (industry-average multiples if specific data unavailable) - -## Phase 3 — Analysis - -Run the core analyses needed for the deck: -- **Valuation**: DCF (WACC, 5Y FCF projections, terminal value, sensitivity), comps table, implied valuation range -- **Scenario analysis**: Bull/base/bear with bottoms-up segment builds — be honest about which scenario is most likely -- **Capital allocation**: Buybacks, dividends, shareholder yield, leverage — flag any value-destructive patterns -- **Financial projections**: 3-5 year forward estimates — challenge assumptions, don't just extrapolate - -**DCF Methodology** (inline calculation): -- Project 5 years of unlevered free cash flows (UFCF = NOPAT + D&A - CapEx - ΔWC) -- Discount at WACC (beta-based or peer-median if unavailable) -- Terminal value using perpetuity growth method (TGR 2-3%) -- PV of FCFs + PV of TV = EV → subtract net debt → equity value → per-share price - -**Critical assessment:** The deck should present an honest analytical view, not a promotional pitch. If the valuation looks stretched, say so. If growth is decelerating, show it clearly. If risks are material, give them proper weight. Institutional investors will dismiss analysis that reads as advocacy rather than research. - -## Phase 4 — Build Presentation - -Generate a self-contained HTML file following the templates in `references/slide-templates.md`. Use components from `references/financial-components.md`. - -**Slide structure** (default 14-slide deck — adapt based on purpose): - -1. **Cover** — Company name, deck title, date, "CONFIDENTIAL" (if IB Advisory) -2. **Disclaimer** — Standard legal boilerplate -3. **Table of Contents** — Numbered sections -4. **Section Divider: Situation Overview** -5. **Executive Summary** — Two-column: situation overview + key findings -6. **Company Overview** — KPI callout row + business description + segment breakdown -7. **Financial Summary** — Dense income statement + margins + per-share + growth rates -8. **Section Divider: Valuation Analysis** -9. **Peer Benchmarking** — Full comps table (6-10 peers, trading multiples, footnoted) -10. **Valuation Analysis** — Football field chart + methodology summary -11. **DCF Detail** — Projection table + sensitivity matrix + assumptions -12. **Section Divider: Conclusion** -13. **Scenario Analysis** — Bull/base/bear bars + metric comparison table -14. **Appendix** — Raw data tables, dense formatting - -**Key rules:** -- Every content slide must have minimum 2-3 data-rich elements (tables, charts, commentary) -- No sparse slides — fill the space with analysis -- All financial figures must include Daloopa citations -- Follow `../design-system.md` for colors, typography, number formatting -- Use CSS `@page` with landscape orientation, 16:9 aspect ratio (1280×720px per slide) -- Each slide is a `
` with `page-break-after: always` -- All data displayed in tables (no chart generation) - -See `references/ib-advisory-patterns.md` for valuation methodology templates. - -## Phase 5 — Output - -Save the complete HTML deck as a local file and summarize the output. Use the HTML Report Template structure from `../design-system.md` with slide-specific CSS from `references/slide-templates.md`. - -Tell the user: -- The deck is ready to view — open in any browser -- To create a PDF: open in Chrome/Edge → Print → Save as PDF → set to Landscape orientation -- 2-3 sentence summary of the deck's key findings -- Implied valuation range -- How many slides were generated - -## Citation Format - -Every financial figure must use Daloopa citation format: [$X.XX million](https://daloopa.com/src/{fundamental_id}) - -All tables must follow the standard financial analysis format: -- **Columns** = time periods (Q1 2024, Q2 2024, etc.) -- **Rows** = financial metrics (Revenue, Net Income, etc.) - -Data sourced from Daloopa. diff --git a/plugins/daloopa/skills/ib-deck/agents/openai.yaml b/plugins/daloopa/skills/ib-deck/agents/openai.yaml deleted file mode 100644 index 0a60176af..000000000 --- a/plugins/daloopa/skills/ib-deck/agents/openai.yaml +++ /dev/null @@ -1,7 +0,0 @@ -interface: - display_name: IB Deck - short_description: Generate an institutional-grade investment banking pitch deck - (HTML) - default_prompt: Create an investment banking pitch deck for AAPL. -policy: - allow_implicit_invocation: true diff --git a/plugins/daloopa/skills/ib-deck/references/financial-components.md b/plugins/daloopa/skills/ib-deck/references/financial-components.md deleted file mode 100644 index 7d087da64..000000000 --- a/plugins/daloopa/skills/ib-deck/references/financial-components.md +++ /dev/null @@ -1,181 +0,0 @@ -# Financial Components - -Reusable HTML+CSS components for deck slides. All components use design system CSS custom properties. - -## KPI Callout Row - -4-6 highlighted metrics displayed in boxes across the top of a slide. - -```html -
-
-
Market Cap
-
$3.4T
-
- -
-``` - -## Dense Financial Table - -For income statements, balance sheets, cash flow. Supports row grouping and subtotals. - -```html - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MetricQ1 2024Q2 2024
Revenue$94,836
YoY Growth+5.5%
Operating Income$29,412
Margin31.0%
-``` - -## Sensitivity Matrix - -Color-coded WACC vs terminal growth. Green = above current price, red = below. - -```html - - - - - - - - - - - - - - - - - -
WACC \ TGR1.5%2.0%
8.0%$245$185
-``` - -Use `#D4EDDA` (light green) for upside, `#F8D7DA` (light red) for downside. Bold the base case cell. - -## Horizontal Bar Chart (CSS-only) - -For peer comparisons, valuation ranges. No JavaScript needed. - -```html -
-
-
Peer A
-
-
- 18.5x -
-
- -
-``` - -## Vertical Bar Chart (CSS-only) - -For time-series financial data. - -```html -
-
-
$94.8B
-
-
Q1'24
-
- -
-``` - -## Waterfall Chart (CSS-only) - -Bridge from base to target value. - -```html -
- -
-
$100B
-
-
Base
-
- -
-
- +$15B -
-
Organic
-
- -
-
- -$3B -
-
FX
-
-
-``` - -Note: For waterfall charts, it's often easier to use the `chart_generator.py waterfall` type and embed the PNG image, rather than building pure CSS waterfalls. - -## Pie/Donut Chart (CSS-only) - -Simple segment breakdown using CSS conic-gradient. - -```html -
-
-
-
-
- iPhone — 45% -
- -
-
-``` - -## Commentary Block - -Gray background box with steel-blue left border. Use after every data table or chart. - -```html -
- Key Takeaway: Revenue accelerated to +6.1% YoY, driven by the iPhone 16 cycle. Operating margins expanded +150bps to 31.0%, suggesting improved cost discipline. Watch for sustainability in Q2 as seasonal tailwinds fade. -
-``` - -## Football Field (CSS-only) - -See `ib-advisory-patterns.md` for data format. For best results, use `chart_generator.py football-field` and embed the PNG. diff --git a/plugins/daloopa/skills/ib-deck/references/ib-advisory-patterns.md b/plugins/daloopa/skills/ib-deck/references/ib-advisory-patterns.md deleted file mode 100644 index 0cdb0ff34..000000000 --- a/plugins/daloopa/skills/ib-deck/references/ib-advisory-patterns.md +++ /dev/null @@ -1,98 +0,0 @@ -# IB Advisory Patterns - -Valuation methodology templates and layouts for investment banking decks. - -## DCF Layout - -### Projection Table -``` -| Metric | Year 1 | Year 2 | Year 3 | Year 4 | Year 5 | -|------------------|---------|---------|---------|---------|---------| -| Revenue | $X,XXX | $X,XXX | $X,XXX | $X,XXX | $X,XXX | -| Revenue Growth | +X.X% | +X.X% | +X.X% | +X.X% | +X.X% | -| EBITDA | $X,XXX | $X,XXX | $X,XXX | $X,XXX | $X,XXX | -| EBITDA Margin | X.X% | X.X% | X.X% | X.X% | X.X% | -| D&A | ($XXX) | ($XXX) | ($XXX) | ($XXX) | ($XXX) | -| EBIT | $X,XXX | $X,XXX | $X,XXX | $X,XXX | $X,XXX | -| Tax @ X.X% | ($XXX) | ($XXX) | ($XXX) | ($XXX) | ($XXX) | -| NOPAT | $X,XXX | $X,XXX | $X,XXX | $X,XXX | $X,XXX | -| + D&A | $XXX | $XXX | $XXX | $XXX | $XXX | -| - CapEx | ($XXX) | ($XXX) | ($XXX) | ($XXX) | ($XXX) | -| - ΔWC | ($XXX) | ($XXX) | ($XXX) | ($XXX) | ($XXX) | -| **UFCF** | **$X,XXX** | **$X,XXX** | **$X,XXX** | **$X,XXX** | **$X,XXX** | -``` - -### Valuation Bridge -``` -| Component | Value | -|--------------------------|------------| -| PV of Projected FCFs | $XX,XXX | -| PV of Terminal Value | $XX,XXX | -| Enterprise Value | $XX,XXX | -| Less: Net Debt | ($X,XXX) | -| Equity Value | $XX,XXX | -| Shares Outstanding | X,XXXmm | -| **Implied Price/Share** | **$XXX** | -``` - -### Sensitivity Matrix -Display as color-coded heatmap: -- Green cells: above current price (upside) -- Red cells: below current price (downside) -- Bold the base case cell -- Show WACC as rows, terminal growth as columns - -## Comps Table Format - -``` -| Company | Ticker | Mkt Cap | TEV | Rev Growth | EBITDA Margin | P/E | Fwd P/E | EV/EBITDA | EV/Rev | -|---------|--------|---------|--------|------------|---------------|-------|---------|-----------|--------| -| Peer 1 | XXX | $XXbn | $XXbn | +X.X% | X.X% | XX.Xx | XX.Xx | XX.Xx | X.Xx | -| ... | | | | | | | | | | -| **Median** | | | | +X.X% | X.X% | XX.Xx | XX.Xx | XX.Xx | X.Xx | -| **Mean** | | | | +X.X% | X.X% | XX.Xx | XX.Xx | XX.Xx | X.Xx | -| **Target** | **XXX** | **$XXbn** | **$XXbn** | **+X.X%** | **X.X%** | **XX.Xx** | **XX.Xx** | **XX.Xx** | **X.Xx** | -``` - -Footnotes at bottom: -- Source: Daloopa (company filings), market data as of {date} -- NTM estimates from consensus where available -- LTM figures based on trailing four quarters - -## Precedent Transactions Format - -``` -| Date | Acquirer | Target | TEV ($mm) | TEV/Revenue | TEV/EBITDA | Premium | -|---------|-------------|-------------|-----------|-------------|------------|---------| -| MM/YYYY | Company A | Company B | $X,XXX | X.Xx | XX.Xx | XX% | -| ... | | | | | | | -| **Median** | | | | X.Xx | XX.Xx | XX% | -``` - -## SOTP (Sum-of-the-Parts) Format - -``` -| Segment | Revenue | EBITDA | Applied Multiple | Implied Value | Methodology | -|---------------|---------|--------|------------------|---------------|-----------------| -| Segment A | $X,XXX | $X,XXX | XX.Xx EV/EBITDA | $XX,XXX | Peer median | -| Segment B | $X,XXX | $X,XXX | XX.Xx EV/EBITDA | $XX,XXX | Comp A, B avg | -| Segment C | $X,XXX | n/a | X.Xx EV/Revenue | $XX,XXX | High-growth SaaS| -| **Total EV** | | | | **$XX,XXX** | | -| Less: Net Debt| | | | ($X,XXX) | | -| **Equity Value** | | | | **$XX,XXX** | | -| Per Share | | | | **$XXX** | | -``` - -## Football Field Format - -Display as horizontal bars, one per methodology: -- Each bar spans from low to high estimate -- Diamond marker at midpoint -- Vertical dashed line at current price -- Labels: methodology name on left, low/high values at bar ends -- Order from top to bottom: DCF, P/E Comps, EV/EBITDA Comps, Precedent Txns, 52W Range - -Color coding: -- Bars above current price → navy/steel blue tones -- Bars below current price → gray tones -- Current price line → red dashed diff --git a/plugins/daloopa/skills/ib-deck/references/slide-templates.md b/plugins/daloopa/skills/ib-deck/references/slide-templates.md deleted file mode 100644 index 3715e9e54..000000000 --- a/plugins/daloopa/skills/ib-deck/references/slide-templates.md +++ /dev/null @@ -1,189 +0,0 @@ -# Slide Templates - -HTML/CSS templates for each slide type. All slides use CSS custom properties from design-system.md. - -## Base Slide CSS - -```css -:root { - --navy: #1B2A4A; - --steel-blue: #4A6FA5; - --gold: #C5A55A; - --green: #27AE60; - --red: #C0392B; - --light-gray: #F8F9FA; - --mid-gray: #E9ECEF; - --dark-gray: #6C757D; - --near-black: #343A40; -} - -@page { - size: 1280px 720px; - margin: 0; -} - -body { - margin: 0; - padding: 0; - font-family: "Segoe UI", -apple-system, BlinkMacSystemFont, Arial, sans-serif; - color: var(--near-black); -} - -.slide { - width: 1280px; - height: 720px; - padding: 40px 50px; - page-break-after: always; - position: relative; - overflow: hidden; - box-sizing: border-box; -} - -.slide-header { - font-size: 24px; - font-weight: 700; - color: var(--navy); - border-bottom: 2px solid var(--gold); - padding-bottom: 8px; - margin-bottom: 20px; -} - -.slide-footer { - position: absolute; - bottom: 15px; - left: 50px; - right: 50px; - display: flex; - justify-content: space-between; - font-size: 8px; - color: var(--dark-gray); -} -/* Slide footer should contain: left="Prepared by {FIRM_NAME}", center="CONFIDENTIAL" (if IB Advisory), right="Page N" */ -/* Replace {FIRM_NAME} with user-specified firm or "Daloopa" (default). NEVER hallucinate a firm name. */ - -.confidential { - color: var(--red); - font-weight: 600; - text-transform: uppercase; - letter-spacing: 1px; -} -``` - -## 1. Cover Slide - -```html -
-
{COMPANY_NAME}
-
{DECK_TITLE}
-
{DATE}
- -
Prepared by {FIRM_NAME}
-
CONFIDENTIAL
-
-``` - -## 2. Disclaimer Slide - -```html -
-
Important Disclaimer
-
- This presentation has been prepared solely for informational purposes. It does not constitute an offer to sell or a solicitation of an offer to buy any security. The information herein is based on sources believed to be reliable but is not guaranteed as to accuracy or completeness. Past performance is not indicative of future results. All financial data sourced from Daloopa (company filings). -
-
-``` - -## 3. Table of Contents - -```html -
-
Table of Contents
-
-
-
1
-
Situation Overview
-
- -
-
-``` - -## 4. Section Divider - -```html -
-
Section {N}
-
{SECTION_TITLE}
-
-``` - -## 5. Executive Summary - -```html -
-
Executive Summary
-
-
-
Situation Overview
-
    -
  • {point_1}
  • -
  • {point_2}
  • -
  • {point_3}
  • -
-
-
-
Key Findings
-
    -
  • {finding_1}
  • -
  • {finding_2}
  • -
  • {finding_3}
  • -
-
-
-
-``` - -## 6. Company Overview - -```html -
-
Company Overview — {COMPANY_NAME} ({TICKER})
- -
-
-
Market Cap
-
{market_cap}
-
- -
- -
{description}
- - -
-``` - -## 7. Financial Summary - -Dense financial table slide. Use the dense financial table component from `financial-components.md`. Include: -- Income statement (Revenue through EPS) -- Margin rows (gross, operating, net, EBITDA) -- Growth rates (revenue YoY, EPS YoY) -- Per-share data - -Minimum 15 rows of data. Right-align all numbers. - -## 8-11. Valuation Slides - -See `ib-advisory-patterns.md` for specific valuation slide layouts: -- Peer benchmarking (comps table) -- Football field (range chart) -- DCF detail (projections + sensitivity) - -## 12-13. Scenario / Capital Allocation Slides - -Use scenario-bar and waterfall components from `financial-components.md`. - -## 14. Appendix - -Dense data tables with small font (10px). Include all raw financial data with Daloopa citations. Multiple tables per slide is expected. diff --git a/plugins/daloopa/skills/industry/SKILL.md b/plugins/daloopa/skills/industry/SKILL.md deleted file mode 100644 index 24344bbf4..000000000 --- a/plugins/daloopa/skills/industry/SKILL.md +++ /dev/null @@ -1,113 +0,0 @@ ---- -name: industry -description: Cross-company industry comparison across multiple tickers ---- - -Perform an industry comparison across the companies named in the user's request. If no ticker or company is provided, ask for one before proceeding. - -The user will provide multiple tickers separated by spaces (e.g., "AAPL MSFT GOOG AMZN"). - -**Before starting, read `../data-access.md` for data access methods and `../design-system.md` for formatting conventions.** Follow the data access detection logic and design system throughout this skill. - -Follow these steps: - -## 1. Company Lookups -Look up all provided tickers using `discover_companies`. For each company, capture: -- `company_id` -- `latest_calendar_quarter` — use the earliest `latest_calendar_quarter` across all companies as the anchor for period calculations (see `../data-access.md` Section 1.5) -- `latest_fiscal_quarter` -- Note each company's fiscal year end — this is critical for calendar quarter alignment -- Firm name for report attribution (default: "Daloopa") — see `../data-access.md` Section 4.5 - -## 2. Comparable Financial Metrics -Calculate 8 quarters backward from the anchor `latest_calendar_quarter`. For each company, find and pull these metrics: - -**Income Statement:** -- Revenue -- Gross Profit / Gross Margin -- Operating Income / Operating Margin -- EBITDA (if not reported, compute as Operating Income + D&A — label "(calc.)") -- Net Income / Net Margin -- Diluted EPS -- R&D Expense -- Stock-Based Compensation (SBC) - -**Cash Flow:** -- Operating Cash Flow -- CapEx (Purchases of property, plant and equipment) -- Free Cash Flow (compute as OCF - CapEx — label "(calc.)") -- D&A (needed for EBITDA calc if not directly reported) - -For any derived/computed metric, mark it with "(calc.)" so the reader knows it's not directly sourced. - -## 3. Company-Specific KPIs -First, think about what KPIs matter for the specific industry being compared. Use the full sector taxonomy to guide discovery: - -- **SaaS/Cloud**: ARR, net revenue retention, RPO/cRPO, customers >$100K, cloud gross margin -- **Consumer Tech**: DAU/MAU, ARPU, engagement metrics, installed base, paid subscribers -- **E-commerce/Marketplace**: GMV, take rate, active buyers/sellers, order frequency -- **Retail**: same-store sales, store count, average ticket, transactions -- **Telecom/Media**: subscribers, churn, ARPU, content spend -- **Hardware**: units shipped, ASP, attach rate, installed base -- **Financial Services**: AUM, NIM, loan growth, credit quality metrics, fee income ratio -- **Pharma/Biotech**: pipeline stage, patient starts, scripts, market share -- **Industrials/Energy**: backlog, book-to-bill, utilization, production volumes, reserves - -For each company, discover and pull the most relevant KPIs. Note which KPIs are common across the group (apples-to-apples comparison) and which are unique to specific companies. For mixed-sector comparisons, focus on the KPIs that apply to the largest revenue segments of each company. - -## 4. Normalize & Compare -- **Calendar quarter alignment is critical.** Ensure all companies are compared on the same calendar quarters. Note each company's fiscal year end and map fiscal quarters to calendar quarters. -- Build side-by-side comparison tables -- Calculate margins for ALL 4 recent quarters (not just the latest) to show trends -- Calculate YoY growth rates for each of the last 4 quarters - -## 5. Ranking & Analysis -- Rank companies on each key metric (revenue growth, margins, FCF yield, etc.) -- Identify the leader and laggard for each metric -- Flag notable outliers (unusually high/low margins, accelerating/decelerating growth) -- Note any divergence in KPIs or business model differences -- Compute R&D as % of revenue and SBC as % of revenue for each company — these reveal structural differences in how each company invests and compensates -- Show YoY segment growth rates for the most recent quarter, not just absolute segment revenue -- Flag one-time items that distort any quarter's comparison - -## 6. Document Search -For each company, search the most recent 2 quarters of filings across multiple queries. If any search returns empty, try alternative keywords before giving up. - -- **Competitive positioning**: Try "competition", "market share"; fallback to "competitive", "leader", "position" -- **Industry trends**: Try "industry", "market", "demand"; fallback to "secular", "trend", "adoption" -- **Strategic differentiation**: Try "differentiate", "advantage", "moat"; fallback to "unique", "proprietary", "platform" -- **Growth strategy**: Try "growth", "opportunity", "expansion"; fallback to "invest", "launch", "new market" -- **Macro / headwinds**: Try "macro", "headwind"; fallback to "tariff", "regulatory", "geopolitical", "inflation" - -If a company returns sparse results across all searches, try broader single-keyword searches (e.g., just "competitive" or just "growth") and search additional periods. - -For each company, extract: -- How management describes their competitive position -- Key strategic priorities and investments -- Industry or macro commentary that affects the whole group -- Any direct references to competitors in the comparison set - -Use these findings to enrich the rankings analysis — numbers tell you who's winning, filings tell you why. - -## 7. Save Report -Save to `reports/{INDUSTRY_LABEL}_industry_comp.html` (where INDUSTRY_LABEL is derived from the tickers, e.g., "AAPL_MSFT_GOOG_AMZN") using the HTML report template from `../design-system.md`. Write the full analysis as styled HTML with the design system CSS inlined. This is the final deliverable — no intermediate markdown step needed. - -The report should include: -- Summary header listing all companies compared, with fiscal year end dates -- Side-by-side financial metrics table (last 4 calendar quarters, companies as columns, metrics as rows, Daloopa citations) -- Trailing 4-quarter totals for revenue, operating income, net income, EPS, OCF, CapEx, FCF -- **Margin trend table**: Gross margin, operating margin, net margin for ALL 4 quarters per company (not just latest quarter snapshot) -- **Growth comparison table**: Revenue YoY and EPS YoY for each of the last 4 quarters per company -- **R&D and SBC comparison**: R&D % of revenue and SBC % of revenue for each company (latest quarter + trend) -- Segment revenue tables per company with YoY growth rates for each segment in the most recent quarter -- KPI comparison (where applicable), noting common vs company-specific KPIs -- **Cash flow comparison**: OCF, CapEx, FCF side-by-side with CapEx as % of revenue to highlight investment intensity differences -- Rankings summary table -- Key competitive insights from filings (with document citations) -- Note on calendar quarter alignment and any fiscal year differences - -All financial figures must use Daloopa citation format: `$X.XX million` - -Tell the user where the HTML report was saved. - -Give a clear competitive verdict: Who is winning and who is losing? Which company has the strongest competitive position and why? Which company looks most vulnerable? Are any of the companies structurally mispriced relative to peers (too cheap or too expensive given the fundamentals)? Don't hedge — rank them honestly. diff --git a/plugins/daloopa/skills/industry/agents/openai.yaml b/plugins/daloopa/skills/industry/agents/openai.yaml deleted file mode 100644 index 1b60bb050..000000000 --- a/plugins/daloopa/skills/industry/agents/openai.yaml +++ /dev/null @@ -1,6 +0,0 @@ -interface: - display_name: Industry - short_description: Cross-company industry comparison across multiple tickers - default_prompt: Compare AAPL, MSFT, GOOGL, and AMZN. -policy: - allow_implicit_invocation: true diff --git a/plugins/daloopa/skills/inflection/SKILL.md b/plugins/daloopa/skills/inflection/SKILL.md deleted file mode 100644 index d86a2eded..000000000 --- a/plugins/daloopa/skills/inflection/SKILL.md +++ /dev/null @@ -1,133 +0,0 @@ ---- -name: inflection -description: Auto-detect biggest acceleration/deceleration inflections across all - metrics ---- - -Detect the biggest financial and operating inflections for the company named in the user's request. If no ticker or company is provided, ask for one before proceeding. - -**Before starting, read `../data-access.md` for data access methods and `../design-system.md` for formatting conventions.** Follow the data access detection logic and design system throughout this skill. - -Follow these steps: - -## 1. Company Lookup -Look up the company by ticker using `discover_companies`. Capture: -- `company_id` -- `latest_calendar_quarter` — anchor for all period calculations below (see `../data-access.md` Section 1.5) -- `latest_fiscal_quarter` -- Firm name for report attribution (default: "Daloopa") — see `../data-access.md` Section 4.5 - -## 2. Broad Series Discovery -Cast a wide net to discover ALL available series for this company. Search with multiple keyword sets to maximize coverage: -- Financial: "revenue", "income", "profit", "margin", "eps", "cash flow" -- Operating: "subscriber", "user", "customer", "unit", "arpu", "retention" -- Segment: "segment", "product", "service", "geographic" -- Balance sheet: "debt", "asset", "equity", "cash" -- Other: "backlog", "bookings", "pipeline", "store", "employee" - -Collect all unique series IDs. The goal is comprehensiveness — capture every metric Daloopa tracks for this company. - -## 3. Pull 8 Quarters of Data -Calculate 8 quarters backward from `latest_calendar_quarter`. Pull all discovered series for those periods. This gives enough history to compute both QoQ and YoY rates plus their second derivatives. - -## 4. Compute Growth Rates and Inflections -For each series with sufficient data (at least 5 quarters): - -**YoY Growth Rate** for each quarter: -- growth_t = (value_t - value_{t-4}) / |value_{t-4}| - -**YoY Acceleration (second derivative):** -- accel_t = growth_t - growth_{t-1} -- Positive = accelerating, Negative = decelerating - -**QoQ Sequential Growth** (for non-seasonal metrics): -- seq_growth_t = (value_t - value_{t-1}) / |value_{t-1}| - -**QoQ Acceleration:** -- seq_accel_t = seq_growth_t - seq_growth_{t-1} - -Skip series where values are too small (< 1% of revenue) or where data is sparse. For margin/ratio series (values between 0-1 or percentages), compute change in basis points rather than % change. - -## 5. Rank Inflections -Rank all series by the magnitude of their most recent acceleration/deceleration: - -**Top 10 Accelerating** — series with the largest positive acceleration in the most recent quarter. These are metrics that are improving faster than before. - -**Top 10 Decelerating** — series with the largest negative acceleration (or deceleration). These are metrics where momentum is fading. - -For each inflection, note: -- Series name -- Most recent value (with Daloopa citation) -- Current YoY growth rate -- Prior-quarter YoY growth rate -- Acceleration (the delta) -- Whether this is a new trend (1Q) or sustained (2-3Q in same direction) - -## 6. Contextualize Key Inflections -For the top 5 most significant inflections (by magnitude and importance to the business): -- Search SEC filings for context on what's driving the change -- Try keywords related to the specific metric (e.g., if "Services Revenue" is accelerating, search for "services", "subscription", "recurring") -- Extract management commentary explaining the inflection -- Note whether the inflection aligns with or contradicts management guidance - -## 7. Synthesize -Identify the narrative: -- Is the company broadly accelerating or decelerating? -- Are there divergent trends (e.g., revenue accelerating but margins decelerating)? -- Which inflections matter most for the investment case? -- Are operating KPIs leading or lagging the financial inflections? - -**Critically assess sustainability:** -- For positive inflections: Is this a durable trend change or a one-time comp effect? Will it persist next quarter when the base normalizes? Is it driven by organic strength or by pull-forward, price increases, or easy comps? -- For negative inflections: Is this the beginning of a structural deterioration or a temporary blip? Is the company investing through it (good) or cutting to protect margins (potentially bad long-term)? -- Flag any inflections where the magnitude seems too good/bad to be sustainable. - -## 8. Save Report -Save to `reports/{TICKER}_inflection.html` using the HTML report template from `../design-system.md`. Write the full analysis as styled HTML with the design system CSS inlined. This is the final deliverable — no intermediate markdown step needed. - -Structure the report with these sections: - -``` -

{Company Name} ({TICKER}) — Inflection Analysis

-

Generated: {date}

- -

Summary

-{2-3 sentence overview: Is the company accelerating, decelerating, or mixed? What are the most important inflections?} - -

Top Accelerating Metrics

- -| Rank | Metric | Latest Value | YoY Growth | Prior YoY Growth | Acceleration | Trend | -{table with Daloopa citations} -
- -

Top Decelerating Metrics

- -| Rank | Metric | Latest Value | YoY Growth | Prior YoY Growth | Deceleration | Trend | -{table with Daloopa citations} -
- -

Key Inflection Deep Dives

- -

1. {Metric Name} — {Accelerating/Decelerating}

-{Context from filings, management commentary, what's driving it} - -

2. {Metric Name} — {Accelerating/Decelerating}

-{...} - -{repeat for top 5} - -

Divergences & Signals

-{Analysis of divergent trends, leading indicators, and implications} - -

Inflection Heatmap

- -| Metric | Q(-3) YoY | Q(-2) YoY | Q(-1) YoY | Q(latest) YoY | Direction | -{visual trend using labels: Accelerating / Steady / Decelerating} -
-``` - -All financial figures must use Daloopa citation format: `$X.XX million` - -Tell the user where the HTML report was saved. - -Highlight the 2-3 most notable inflections and what they signal. diff --git a/plugins/daloopa/skills/inflection/agents/openai.yaml b/plugins/daloopa/skills/inflection/agents/openai.yaml deleted file mode 100644 index 012e2a1e0..000000000 --- a/plugins/daloopa/skills/inflection/agents/openai.yaml +++ /dev/null @@ -1,7 +0,0 @@ -interface: - display_name: Inflection - short_description: Auto-detect biggest acceleration/deceleration inflections across - all metrics - default_prompt: Find the biggest metric inflections for AAPL. -policy: - allow_implicit_invocation: true diff --git a/plugins/daloopa/skills/initiate/SKILL.md b/plugins/daloopa/skills/initiate/SKILL.md deleted file mode 100644 index dc1d63ea3..000000000 --- a/plugins/daloopa/skills/initiate/SKILL.md +++ /dev/null @@ -1,452 +0,0 @@ ---- -name: initiate -description: Initiate coverage — generate both research note (HTML) and Excel model - (.xlsx) ---- - -Initiate coverage on the company named in the user's request. If no ticker or company is provided, ask for one before proceeding. - -**Before starting, read `../data-access.md` for data access methods and `../design-system.md` for formatting conventions.** Follow the data access detection logic and design system throughout this skill. - -This is the capstone skill that produces both a research note (styled HTML) and an Excel model (.xlsx) from a single comprehensive data gathering pass. - -## Strategy -Rather than running the research-note and build-model skills independently (which would duplicate data gathering), this skill gathers a superset of data once, then renders both outputs. - -## Phase 1 — Company Setup -Look up the company by ticker using `discover_companies`. Capture: -- `company_id` -- `latest_calendar_quarter` — anchor for all period calculations (see `../data-access.md` Section 1.5) -- `latest_fiscal_quarter` -- Firm name for report attribution (default: "Daloopa") — see `../data-access.md` Section 4.5 - -Get market data using the 3-step resolution: (1) MCP market data tools if available, (2) web search, (3) sensible defaults (see `../data-access.md` Section 2): -- Current price, market cap, shares outstanding, beta -- Trading multiples (P/E, EV/EBITDA, P/S, P/B) -- Risk-free rate (for DCF) - -Initialize context: `context = {company_name, ticker, date, price, market_cap, firm_name, ...}` - -## Phase 2 — Comprehensive Data Gathering -Calculate 8-16 quarters backward from `latest_calendar_quarter`. Pull: - -**Income Statement — search and pull all available:** -- Revenue / Net Sales -- Cost of Revenue / COGS -- Gross Profit -- Research & Development -- Selling, General & Administrative -- Total Operating Expenses -- Operating Income -- Interest Expense / Income -- Pre-tax Income -- Tax Expense -- Net Income -- Diluted EPS -- Diluted Shares Outstanding -- EBITDA (or compute from Op Income + D&A, label "(calc.)") -- D&A - -**Balance Sheet — search and pull all available:** -- Cash and Equivalents -- Short-term Investments -- Accounts Receivable -- Inventory -- Total Current Assets -- PP&E (net) -- Goodwill -- Total Assets -- Accounts Payable -- Short-term Debt -- Long-term Debt -- Total Liabilities -- Total Equity - -**Cash Flow — search and pull all available:** -- Operating Cash Flow -- Capital Expenditures -- Depreciation & Amortization -- Acquisitions -- Dividends Paid -- Share Repurchases -- Free Cash Flow (compute if not direct: OCF - CapEx, label "(calc.)") - -**Segments:** -- Revenue by segment -- Operating income by segment (if available) - -**Geographic:** -- Revenue by geography - -**KPIs:** -- All company-specific operating metrics (subscribers, units, ARPU, retention, etc.) - -**Guidance:** -- All guidance series and corresponding actuals - -**Share Activity:** -- Share count, buyback amounts - -**For every value returned by `get_company_fundamentals`, record its `fundamental_id` (the `id` field).** Store each data point as `{value, fundamental_id}` so citations can be rendered in both outputs. - -Compute margins, YoY growth rates, and ratios for each quarter. - -### Cost Structure & Margin Analysis -After the core financial pull: -- **COGS driver identification**: Search for cost-related series ("cost of goods", "materials", "manufacturing", "input cost"). Identify 3-5 biggest cost line items and their trends. -- **OpEx breakdown**: Pull R&D and SG&A separately. Compute R&D % of revenue and SG&A % of revenue trends. -- **Margin driver analysis**: For each major margin (gross, operating, net), identify what's driving expansion or compression — pricing power, cost leverage, mix shift, or one-time items. - -## Phase 3 — Industry-Specific Deep Dive -Determine the company's sector and apply the relevant analysis template: - -- **Manufacturing/Industrial**: Bookings & backlog, book-to-bill ratio, pipeline by geography, capacity utilization -- **SaaS/Technology**: ARR/MRR trajectory, net retention rate, customer cohort analysis, RPO/deferred revenue trends -- **Retail/Consumer**: Same-store sales, store count trajectory, traffic vs ticket decomposition, inventory health -- **Financials/Banks**: NIM trajectory, provision trends, loan growth by category, capital ratios (CET1, TCE) -- **Healthcare/Pharma**: Pipeline summary (drug, indication, phase, milestone), product revenue breakdown, patent cliff timeline -- **Energy**: Production volumes, realized pricing vs benchmark, proved reserves, breakeven analysis - -Search for relevant series using `discover_company_series` with sector-appropriate keywords. Pull available data and build the narrative. - -Build `context.industry_deep_dive` (string) — sector-specific analysis narrative with Daloopa citations, organized by the relevant template above. - -## Phase 4 — Peer Analysis -Identify 5-8 comparable companies. -Get peer trading multiples using the 3-step resolution: (1) MCP market data tools if available, (2) web search, (3) sensible defaults (see `../data-access.md` Section 2). -If consensus forward estimates are available (`../data-access.md` Section 3), include NTM estimates. -Pull peer fundamentals from Daloopa where available (revenue growth, margins). - -Build `context.comps` and `context.comps_table`. - -## Phase 5 — Projections -Build forward estimates using the following methodology: -- **Revenue:** Start with latest guidance (if available), then decay to long-term growth rate (industry average or historical trend). Apply quarterly seasonality patterns from trailing data. -- **Gross Margin:** Mean-revert to trailing 8-quarter average, with adjustment for recent trends or guidance commentary. -- **Operating Expenses:** Project as % of revenue, trending toward trailing averages. R&D and SG&A may have different trajectories. -- **CapEx:** Project as % of revenue based on trailing 4-8 quarter average and guidance. -- **D&A:** Project based on trailing average as % of revenue or PP&E. -- **Tax Rate:** Use trailing effective tax rate or guidance. -- **Share Count:** Project dilution/buyback based on trailing trends and guidance. -- **Working Capital:** Project DSO, DIO, DPO based on trailing averages. - -Calculate all quarterly projections, then sum to annual. Project 4-8 quarters forward. Describe methodology inline and perform calculations directly. - -## Phase 6 — DCF Valuation -Calculate: -- **WACC:** Use CAPM for cost of equity (Rf + Beta × ERP, where ERP = 6.0%). Cost of debt = Interest Expense / Total Debt. WACC = (E/V × Re) + (D/V × Rd × (1 - Tax Rate)). -- **5-year FCF projections:** Annualize from quarterly projections (FCF = Op Cash Flow - CapEx). -- **Terminal Value:** Use perpetuity growth at 2.5-3.0%. -- **Implied Share Price:** (PV of FCFs + Terminal Value - Net Debt) / Shares Outstanding -- **Sensitivity Matrix:** WACC (7 values: -3% to +3% from base) × Terminal Growth (6 values: 1.5% to 4.0%). - -Build `context.dcf` and `context.dcf_summary` (set `context.has_dcf = true`). - -## Phase 7 — Qualitative Research + News & Catalysts - -### SEC Filing Research -Search SEC filings across multiple queries: -- "risk" / "uncertainty" / "challenge" for risk factors -- "growth" / "opportunity" / "expansion" for growth drivers -- "competition" / "market share" for competitive dynamics -- "outlook" / "guidance" for management's forward view -- Company-specific strategic topics (e.g., "AI", "cloud", etc.) - -Extract and organize into: -- `context.risks` — ranked list of risks with impact/probability -- `context.investment_thesis` — variant perception, thesis pillars, catalysts -- `context.company_description` — 2-3 sentence business description - -### News & Catalysts via WebSearch -Run 4 WebSearch queries to gather recent external context: -1. `"{TICKER} {company_name} news {year}"` — recent headlines and developments -2. `"{TICKER} analyst upgrade downgrade price target"` — sell-side sentiment shifts -3. `"{TICKER} catalysts risks"` — forward-looking events and risk factors -4. `"{company_name} industry outlook {sector}"` — macro and industry trends - -Organize results into: -- `context.news_timeline` (string) — 6-10 key events from the last 6-12 months in reverse chronological order. Each event: date, headline, 1-sentence impact, sentiment tag (Positive / Negative / Mixed / Upcoming). Format as a numbered list. - -- `context.forward_catalysts` (string) — Organized by timeframe: - - **Near-term (0-3 months, HIGH priority)**: earnings dates, product launches, regulatory decisions - - **Medium-term (3-12 months, MEDIUM priority)**: strategic milestones, contract renewals, industry events - - **Long-term (1-3 years, LOW priority)**: secular trends, market expansion, competitive dynamics - -- `context.policy_backdrop` (string) — Macro/regulatory context affecting the company. Tariffs, regulation, interest rates, sector-specific policy. Leave empty string if not material. - -## Phase 8 — Guidance Track Record -Search for guidance series ("guidance", "outlook", "forecast", "estimate", "target"). -Pull guidance and corresponding actuals. Apply +1 quarter offset rule for quarterly guidance, same-year rule for annual guidance from Q1/Q2/Q3, next-year rule for annual guidance from Q4. -Compute beat/miss rates and patterns. -Build `context.guidance` and `context.guidance_table` (set `context.has_guidance = true/false`). - -## Phase 9 — What You Need to Believe -Build falsifiable bull/bear beliefs: - -### Bull Beliefs (To Go Long) -Write 4-6 numbered beliefs, each with: -- One **bold statement** (the belief itself) -- 2-3 sentences of **evidence** with Daloopa citations supporting why this could be true -- Each belief must be **falsifiable** — testable with observable data within 6 months - -Example format: "1. **Revenue growth re-accelerates to 15%+ as AI monetization scales.** Cloud segment grew [$X.Xbn](link) last quarter, up X% YoY, with management noting..." - -### Bear Beliefs (To Go Short) -Same format — 4-6 numbered falsifiable beliefs with evidence for the downside case. - -### Valuation Math -For each side: -- Bull target: forward multiple × forward earnings estimate = price target. Show the math. -- Bear target: same structure with bear-case multiple and earnings. - -### Risk/Reward Assessment -- Compare bull upside % vs bear downside % from current price -- If asymmetry is significant (e.g., 30% upside vs 40% downside), flag it explicitly -- State which side has the better risk/reward and why - -Build `context.bull_beliefs`, `context.bull_target`, `context.bear_beliefs`, `context.bear_target`, `context.risk_reward_assessment`. - -## Phase 10 — Capital Allocation -Pull buyback, dividend, share count, FCF data. -Compute shareholder yield, FCF payout ratio, net leverage. -Build `context.capital_allocation_commentary`. - -## Phase 11 — Synthesis + Tensions + Monitoring -This is the most judgment-intensive step. Be honest and critical — the reader is a professional investor who needs your real assessment, not a balanced summary. - -### Core Synthesis -Write: -- **Executive Summary**: 3-4 sentence TL;DR covering current state, key thesis, valuation view. Include a clear directional view — is this stock attractive, fairly valued, or overvalued at the current price? -- **Variant Perception**: What does the market think vs what do you see in the data? Where is the consensus wrong? If you agree with consensus, say that too — but explain what could change. -- **Key Findings**: Top 3-5 most notable data points or trends — prioritize what changes the investment thesis, not just what's interesting -- **Red Flags & Concerns**: Any quality-of-earnings issues, sustainability questions, or risks the market may be underpricing -- Build `context.executive_summary`, `context.variant_perception` - -### Five Key Tensions -Identify the 5 most critical bull/bear debates for this stock. Each tension is a single line that frames both sides. Alternate between bullish-leaning and bearish-leaning tensions. Every tension must reference a specific data point from the analysis. - -Format as a numbered list: -1. "[Bullish factor] vs [Bearish factor]" — cite the specific metric -2. "[Bearish factor] vs [Bullish factor]" — cite the specific metric -...etc. - -Build `context.five_key_tensions` (string). - -### Monitoring Framework -Build two monitoring lists for ongoing tracking: - -**Quantitative Monitors** — 5-7 specific metrics with explicit thresholds: -- Format: "Metric: current value → bull threshold / bear threshold" -- Example: "Gross Margin: 45.2% → above 46% confirms pricing power / below 43% signals cost pressure" - -**Qualitative Monitors** — 5-7 factors to watch: -- Management tone shifts on earnings calls -- Competitive dynamics (new entrants, pricing pressure) -- Regulatory developments -- Customer concentration changes -- Capital allocation pivots - -Build `context.monitoring_quantitative` and `context.monitoring_qualitative` (strings, numbered lists). - -### Structured Tables -Build structured tables for both outputs: -- `context.key_metrics_table` — [{metric, value, vs_prior}] for the exec summary table -- `context.financials_table` — [{metric, q1, q2, ...}] for the financial analysis section -- `context.segments_table`, `context.geo_table`, `context.shares_outstanding_table` -- `context.opex_breakdown_table` — [{metric, q1, q2, ...}] for R&D, SG&A, % of revenue rows -- `context.guidance_table`, `context.comps_table`, etc. - -## Phase 12 — Render Research Note (HTML) - -Using the HTML Report Template from `../design-system.md`, generate a styled HTML report with full CSS inlined. The report should include: - -**Header Section:** -- Company name and ticker -- Report date and firm attribution -- Five Key Tensions (numbered list) - -**Section 1: Executive Summary** -- Key metrics table -- Executive summary narrative -- Variant perception - -**Section 2: Company Overview** -- Business description -- Investment thesis - -**Section 3: Recent News & Catalysts** -- News timeline -- Forward catalysts -- Policy backdrop - -**Section 4: Financial Analysis** -- Financials table (8-16 quarters) -- Cost structure & margin analysis -- OpEx breakdown table -- Segment and geographic tables -- Share count table - -**Section 5: Industry-Specific Analysis** -- Industry deep dive narrative - -**Section 6: Guidance Track Record** -- Guidance table and beat/miss analysis (if available) - -**Section 7: What You Need to Believe** -- Bull beliefs with valuation target -- Bear beliefs with valuation target -- Risk/reward assessment - -**Section 8: Catalysts** -- Forward catalysts -- Policy backdrop - -**Section 9: Capital Allocation** -- Capital allocation commentary - -**Section 10: Valuation** -- DCF summary and sensitivity (if available) -- Comps commentary (if available) - -**Section 11: Risks** -- Risks summary - -**Section 12: Monitoring Framework** -- Quantitative monitors -- Qualitative monitors - -**Appendix:** -- Additional context or data - -### Context Key Checklist -Verify these keys exist before rendering (set empty string if data unavailable): - -**Cover & Summary:** -`company_name`, `ticker`, `date`, `price`, `market_cap`, `five_key_tensions`, `executive_summary`, `key_metrics_table` - -**Thesis & Overview:** -`investment_thesis`, `variant_perception`, `company_description` - -**News:** -`news_timeline` - -**Financials:** -`financials_table`, `cost_margin_analysis`, `opex_breakdown_table`, `segments_table`, `geo_table`, `shares_outstanding_table` - -**Industry:** -`industry_deep_dive` - -**Guidance:** -`has_guidance`, `guidance_track_record` - -**What You Need to Believe:** -`bull_beliefs`, `bull_target`, `bear_beliefs`, `bear_target`, `risk_reward_assessment` - -**Catalysts:** -`forward_catalysts`, `policy_backdrop` - -**Capital Allocation:** -`capital_allocation_commentary` - -**Valuation:** -`has_dcf`, `dcf_summary`, `has_comps`, `comps_commentary` - -**Risks:** -`risks_summary` - -**Monitoring:** -`monitoring_quantitative`, `monitoring_qualitative` - -**Appendix:** -`appendix_content` - -**Citation enforcement:** Every financial figure from Daloopa in the HTML report must use citation format: `[$X.XX million](https://daloopa.com/src/{fundamental_id})`. If a number came from `get_company_fundamentals`, it must have a citation link. No exceptions. - -## Phase 13 — Render Excel Model - -Generate the `.xlsx` file directly using the best available spreadsheet-generation workflow. For Codex, prefer bundled spreadsheet tooling or Python/openpyxl when available. The workbook should: - -1. Create 8 tabs with the following structure: - -**Tab 1: Income Statement** -- Rows: Revenue, COGS, Gross Profit, R&D, SG&A, Total OpEx, Op Income, Interest, Pre-Tax Income, Tax, Net Income, Diluted EPS, Shares -- Columns: Historical periods (8-16Q) + Projected periods (4-8Q) -- Sub-rows: YoY growth %, margin % where applicable -- Header: Company name, ticker, report date -- Formatting: Numbers with commas/decimals, percentages, bold headers, frozen panes - -**Tab 2: Balance Sheet** -- Rows: Assets section (Cash, Investments, AR, Inventory, Current Assets, PP&E, Goodwill, Total Assets), Liabilities section (AP, ST Debt, LT Debt, Total Liabilities, Equity) -- Columns: Historical + Projected periods -- Sub-rows: % of Total Assets for key line items -- Same formatting standards - -**Tab 3: Cash Flow** -- Rows: Op Cash Flow, CapEx, Free Cash Flow, Acquisitions, Dividends, Buybacks, Net Change in Cash -- Columns: Historical + Projected periods -- Sub-rows: FCF yield %, CapEx as % Revenue -- Same formatting standards - -**Tab 4: Segments** -- Rows: Revenue by segment, Op Income by segment (if available) -- Columns: Historical + Projected periods -- Sub-rows: Segment as % of total, segment growth rates -- Same formatting standards - -**Tab 5: KPIs** -- Rows: All company-specific operating metrics discovered -- Columns: Historical + Projected periods -- Sub-rows: YoY growth or relevant unit economics -- Same formatting standards - -**Tab 6: Projections** -- Editable assumption inputs (yellow highlighting): Revenue growth %, Gross margin %, Op margin %, CapEx % revenue, Tax rate %, Buyback rate QoQ -- Calculated outputs: Projected P&L, BS, CF driven by assumptions -- Commentary box explaining methodology -- Same formatting standards - -**Tab 7: DCF** -- Inputs: WACC, Terminal Growth, Risk-Free Rate, ERP, Beta, Cost of Debt -- FCF Projection (5 years annualized) -- Terminal Value calculation -- PV calculations -- Enterprise Value → Equity Value → Implied Share Price -- Sensitivity table: WACC (rows) × Terminal Growth (cols) showing implied price -- Color scale: green (upside) to red (downside) vs current price -- Same formatting standards - -**Tab 8: Summary** -- Company overview (name, ticker, sector, description) -- Current market data (price, market cap, shares, beta) -- Valuation summary: DCF implied price, peer-implied range, current price, upside/downside % -- Peer trading multiples table -- Key model outputs: Trailing revenue, Projected revenue growth, Trailing/Projected margins -- Same formatting standards - -2. Apply `../design-system.md` formatting conventions: -- Number format: $X.Xbn for large numbers, X.X% for percentages, X.Xx for multiples -- Color palette: Navy #1B2A4A (headers), Steel Blue #4A6FA5 (sub-headers), Gold #C5A55A (highlights), Green #27AE60 (positive), Red #C0392B (negative) -- Bold headers, frozen top row and left column -- Yellow fill (#FFEB3B) for editable input cells - -3. Save the workbook as `reports/{TICKER}_model.xlsx` - -## Output -Present both deliverables to the user: - -**Research Note (HTML):** -- Save the styled HTML report to `reports/{TICKER}_initiate_report.html`. -- Tell the user where the HTML file was saved and that it can be opened in a browser for full formatting. - -**Excel Model:** -- Save the generated Excel model to `reports/{TICKER}_model.xlsx`. -- Tell the user where the `.xlsx` file was saved. -- Note that yellow cells in the Projections tab are editable inputs. - -**Summary:** -- 3-4 sentence executive summary -- Key valuation range (DCF implied price + comps range) -- Top 3 findings -- Bull upside % vs bear downside % risk/reward assessment - -All financial figures must use Daloopa citation format: [$X.XX million](https://daloopa.com/src/{fundamental_id}) diff --git a/plugins/daloopa/skills/initiate/agents/openai.yaml b/plugins/daloopa/skills/initiate/agents/openai.yaml deleted file mode 100644 index 7d7730ef9..000000000 --- a/plugins/daloopa/skills/initiate/agents/openai.yaml +++ /dev/null @@ -1,7 +0,0 @@ -interface: - display_name: Initiate - short_description: Initiate coverage — generate both research note (HTML) and Excel - model (.xlsx) - default_prompt: Initiate coverage on AAPL with a report and model. -policy: - allow_implicit_invocation: true diff --git a/plugins/daloopa/skills/precedent-transactions/SKILL.md b/plugins/daloopa/skills/precedent-transactions/SKILL.md deleted file mode 100644 index 47a6ff0c8..000000000 --- a/plugins/daloopa/skills/precedent-transactions/SKILL.md +++ /dev/null @@ -1,183 +0,0 @@ ---- -name: precedent-transactions -description: Precedent M&A transactions analysis with deal multiples and acquisition - history ---- - -Build a precedent transactions analysis for the company named in the user's request. If no ticker or company is provided, ask for one before proceeding. - -This is the third pillar of valuation (alongside trading comps and DCF) — it answers: what have acquirers actually paid for businesses like this one? The output is two tables: comparable M&A transactions with deal multiples, and the subject company's own acquisition history. - -**Before starting, read `../data-access.md` for data access methods and `../design-system.md` for formatting conventions.** Follow the data access detection logic and design system throughout this skill. - -Follow these steps: - -## 1. Company Lookup -Look up the company by ticker using `discover_companies`. Capture: -- `company_id` -- `latest_calendar_quarter` — anchor for all period calculations below (see `../data-access.md` Section 1.5) -- `latest_fiscal_quarter` -- Firm name for report attribution (default: "Daloopa") — see `../data-access.md` Section 4.5 - -Identify: -- Full legal company name -- Primary stock exchange and reporting currency -- Country of domicile and primary operations -- Industry and sub-sector -- Approximate revenue and EBITDA scale (to calibrate comparable deal sizing) - -## 2. Subject Company Financials -Calculate 4 quarters backward from `latest_calendar_quarter`. Pull from Daloopa: -- Revenue (compute trailing 4Q / LTM total) -- EBITDA (compute trailing 4Q; if not available, use Operating Income + D&A, label "(calc.)") -- Operating Income -- Net Income -- Free Cash Flow (OCF - CapEx, label "(calc.)") - -These serve as the reference point for comparing deal multiples — what would an acquirer be paying relative to this company's current financials? - -## 3. Identify Comparable Precedent Transactions -Find 8-15 completed M&A transactions from the last 7-10 years involving target companies comparable to the subject. "Comparable" means: -- Same industry and sub-sector -- Similar business model (e.g., SaaS, semiconductor IP, consumer internet, industrials) -- Roughly comparable scale — within ~0.5x-4x of the subject's revenue -- Completed transactions only (not rumored, not pending) - -**Research sources in priority order:** -1. **SEC EDGAR** (for US targets) — SC TO, DEFM14A, 8-K filings disclose EV and deal terms -2. **Equivalent regulators for non-US targets:** FCA (UK), EDINET (Japan), HKEx (Hong Kong), SEDAR+ (Canada), ASX (Australia) -3. **Official investor relations press releases** from acquirer or target -4. **Reputable financial news:** Reuters, Bloomberg, Wall Street Journal, Financial Times - -Use web search to identify deals: `"{industry} acquisitions {sub-sector} last 10 years"`, `"{TICKER} comparable M&A transactions"`, `"{sector} deal comps precedent transactions"`. - -**Do NOT use:** finance blogs, Seeking Alpha, Reddit, anonymous wiki contributions, or aggregators without a traceable primary source. - -For each transaction, capture: -- Announcement date -- Acquirer name -- Target name -- Transaction Enterprise Value -- Deal consideration (All Cash / All Stock / Cash + Stock) -- Source (press release URL, SEC filing, or regulatory filing) - -## 4. Source Target Financials via Daloopa -For each target company in the precedent transactions table, source LTM Revenue and EBITDA from Daloopa: - -1. **Look up the target** using `discover_companies` with the target's ticker or name -2. **Find relevant series** using `discover_company_series` with keywords `["revenue", "EBITDA"]` and the appropriate period (the last complete fiscal year before the deal announcement) -3. **Pull the data** using `get_company_fundamentals` with the discovered series IDs -4. For EBITDA, look for series containing "Adjusted EBITDA", "EBITDA", or fall back to "Operating Income" + D&A -5. If a target is not in Daloopa (e.g., pre-IPO targets, private companies), fall back to SEC filings, press releases, or regulatory filings - -**Daloopa is the primary source.** Only fall back to other sources when a target is genuinely unavailable in the database. - -## 5. Compute Deal Multiples -For each transaction where both EV and financials are available: -- **EV/Revenue** = Transaction EV ÷ LTM Revenue -- **EV/EBITDA** = Transaction EV ÷ LTM EBITDA -- Round to one decimal, append "x" -- If a figure cannot be sourced, mark as **N/A** — do not estimate - -Compute summary statistics (excluding N/A values): -- 75th Percentile -- **Average** (bold) -- **Median** (bold) -- 25th Percentile - -If fewer than 3 valid data points exist for a multiple, note that the statistic is not meaningful. - -## 6. Subject Company's Acquisition History -Find deals where the subject company itself was the acquirer. Sources: company IR page, SEC 8-K or equivalent filings, Reuters/Bloomberg/WSJ. - -For each acquisition, capture: -- Date -- Target name -- Deal value (if disclosed) -- Consideration (Cash / Stock / Mix) -- Strategic rationale (one sentence from press release or filing) - -## 7. Implied Valuation for Subject Company -Apply the precedent transaction multiples to the subject's current financials: - -| Methodology | Percentile | Multiple | Subject LTM Metric | Implied EV | -|---|---|---|---|---| -| EV/Revenue | Median | XX.Xx | $XXX | $XXX | -| EV/Revenue | 25th-75th | XX.Xx-XX.Xx | $XXX | $XXX-$XXX | -| EV/EBITDA | Median | XX.Xx | $XXX | $XXX | -| EV/EBITDA | 25th-75th | XX.Xx-XX.Xx | $XXX | $XXX-$XXX | - -Convert implied EV to implied equity value (EV - Net Debt) and implied share price where market data is available (see `../data-access.md` Section 2). Compare to current market price. - -**Context matters more than precision:** -- Precedent transaction multiples are snapshots from specific deal contexts (competitive auctions, strategic premiums, distressed sales). Note which deals had unusual dynamics. -- Control premiums are embedded in these multiples — a public market investor should not expect to realize the full precedent transaction value unless a takeout actually happens. -- If the current market cap is well below precedent transaction implied value, that's a signal of takeout optionality, not necessarily undervaluation. - -## 8. Deal Environment Commentary -Search filings and news for context on the M&A environment: -- Search: `"{industry} M&A outlook {current_year}"` — deal activity trends -- Search: `"{TICKER} acquisition target rumors"` — is the subject itself a takeout candidate? - -Summarize in 3-5 bullets: -- Is deal activity in this sector accelerating or declining? -- What are typical premiums being paid (control premium trends)? -- Are strategic buyers or financial sponsors (PE) driving activity? -- Any regulatory headwinds to deals in this space (antitrust scrutiny)? -- Is the subject company a plausible acquisition target? Why or why not? - -## 9. Save Report -Save to `reports/{TICKER}_precedent_transactions.html` using the HTML report template from `../design-system.md`. Write the full analysis as styled HTML with the design system CSS inlined. This is the final deliverable — no intermediate markdown step needed. - -The report should include interactive features: -- **Clickable acquirer names** in Table 1 that open a modal showing all source links for that transaction (press release, SEC filing, Daloopa data links). Implement with `data-` attributes and safe DOM methods (`createElement`, `textContent`, `appendChild`) — never `innerHTML`. -- **Consideration badges** styled inline: All Cash (green background), All Stock (purple background), Cash + Stock (amber background). - -Structure the report with these sections: - -``` -

{Company Name} ({TICKER}) — Precedent Transactions Analysis

-

Generated: {date}

- -

Summary

-{2-3 sentences: What do precedent transactions imply for this company's valuation? How does it compare to the current market price?} - -

Subject Company Overview

-{Exchange, currency, industry, LTM Revenue and EBITDA with Daloopa citations} -{Note: "Revenue and EBITDA sourced from Daloopa where available"} - -

Selected Precedent Transactions

- -| Date | Acquirer | Target | EV ($M) | LTM Rev ($M) | LTM EBITDA ($M) | EV/Rev | EV/EBITDA | Consideration | -{data rows with Daloopa-cited financials, footnote superscripts, clickable acquirers} -| 75th Percentile | | | | | | XX.Xx | XX.Xx | | -| **Average** | | | | | | **XX.Xx** | **XX.Xx** | | -| **Median** | | | | | | **XX.Xx** | **XX.Xx** | | -| 25th Percentile | | | | | | XX.Xx | XX.Xx | | -
- -

Implied Valuation

- -| Methodology | Multiple | Subject Metric | Implied EV | Implied Equity | Implied Price | vs Current | -{valuation bridge using median and range multiples} -
- -

{Company Name} Acquisition History

- -| Date | Target | Deal Value | Consideration | Strategic Rationale | -{company's own M&A deals} -
- -

Deal Environment

-
    {3-5 bullets on sector M&A trends, control premiums, takeout potential}
- -

Sources

-{Numbered footnote list — each deal with press release link, SEC filing, Daloopa data links} -{Data sourced from Daloopa attribution} -``` - -All financial figures from Daloopa must use citation format: `$X.XX million` - -Tell the user where the HTML report was saved. - -Highlight: what precedent transactions imply about the company's takeout value, how it compares to the current market price, and whether the sector M&A environment supports deal activity. diff --git a/plugins/daloopa/skills/precedent-transactions/agents/openai.yaml b/plugins/daloopa/skills/precedent-transactions/agents/openai.yaml deleted file mode 100644 index 4f873fd53..000000000 --- a/plugins/daloopa/skills/precedent-transactions/agents/openai.yaml +++ /dev/null @@ -1,7 +0,0 @@ -interface: - display_name: Precedent Transactions - short_description: Precedent M&A transactions analysis with deal multiples and acquisition - history - default_prompt: Analyze precedent transactions for AAPL peers. -policy: - allow_implicit_invocation: true diff --git a/plugins/daloopa/skills/research-note/SKILL.md b/plugins/daloopa/skills/research-note/SKILL.md deleted file mode 100644 index dc5e52e64..000000000 --- a/plugins/daloopa/skills/research-note/SKILL.md +++ /dev/null @@ -1,325 +0,0 @@ ---- -name: research-note -description: Generate a professional Word document research note ---- - -Generate a professional research note (HTML report) for the company specified by the user named in the user's request. If no ticker or company is provided, ask for one before proceeding. - -**Before starting, read `../data-access.md` for data access methods and `../design-system.md` for formatting conventions.** Follow the data access detection logic and design system throughout this skill. - -This is an orchestrator skill that gathers comprehensive data, then renders a styled HTML report using the HTML Report Template from `../design-system.md` (full CSS inlined, zero dependencies). - -## Phase A — Company Setup -Look up the company by ticker using `discover_companies`. Capture: -- `company_id` -- `latest_calendar_quarter` — anchor for all period calculations (see `../data-access.md` Section 1.5) -- `latest_fiscal_quarter` -- Firm name for report attribution (default: "Daloopa") — see `../data-access.md` Section 4.5 - -Get current stock price, market cap, shares outstanding, beta, and trading multiples for {TICKER} using the 3-step resolution: (1) MCP market data tools if available, (2) web search, (3) sensible defaults (see `../data-access.md` Section 2 for how to source market data). - -Initialize context: `context = {company_name, ticker, date, price, market_cap, firm_name, ...}` - -## Phase B — Core Financials + Cost Structure -Calculate 8 quarters backward from `latest_calendar_quarter`. Pull Income Statement metrics: -- Revenue, Gross Profit, Operating Income, Net Income, Diluted EPS -- EBITDA (compute as Op Income + D&A if not direct, label "(calc.)") -- Operating Expenses (SG&A, R&D where available) - -Pull Cash Flow & Balance Sheet: -- Operating Cash Flow, CapEx, Free Cash Flow (OCF - CapEx, label "(calc.)") -- Cash, Total Debt, Net Debt -- D&A - -**For every value returned by `get_company_fundamentals`, record its `fundamental_id` (the `id` field).** Store each data point as `{value, fundamental_id}` so citations can be rendered in the final document. - -Compute margins and YoY growth rates for each quarter. Build `context.financials` with tables. Every Daloopa-sourced number must include its citation link: `[$X.XX million](https://daloopa.com/src/{fundamental_id})`. - -### Cost Structure & Margin Analysis -After the core financial pull, add: - -- **COGS driver identification**: Search for cost-related series ("cost of goods", "materials", "manufacturing", "input cost"). Identify 3-5 biggest cost line items and their trends over 8Q. -- **OpEx breakdown**: Pull R&D and SG&A separately. Compute R&D % of revenue and SG&A % of revenue trends over 8Q. -- **Margin driver analysis**: For each major margin (gross, operating, net), identify what's driving expansion or compression — pricing power, cost leverage, mix shift, or one-time items. - -New context keys: -- `cost_margin_analysis` (string) — narrative explaining what's driving margins, with Daloopa citations -- `opex_breakdown_table` (dynamic table) — [{metric, Q1, Q2, ...}] rows for R&D, SG&A, Other OpEx, each with absolute values and % of revenue sub-rows - -## Phase C — KPIs, Segments & Industry Deep Dive -Think about what KPIs matter most for THIS company's business model. Search for: -- Company-specific operating KPIs (subscribers, units, ARPU, retention, etc.) -- Segment revenue breakdown -- Geographic revenue breakdown -- Share count and buyback activity - -Pull the same 8 quarters (from `latest_calendar_quarter`). Build `context.kpis` and `context.segments`. - -### Industry-Specific Deep Dive -After the KPI/segment pull, determine the company's sector and apply the relevant analysis template: - -- **Manufacturing/Industrial**: Bookings & backlog, book-to-bill ratio, pipeline by geography, capacity utilization -- **SaaS/Technology**: ARR/MRR trajectory, net retention rate, customer cohort analysis, RPO/deferred revenue trends -- **Retail/Consumer**: Same-store sales, store count trajectory, traffic vs ticket decomposition, inventory health -- **Financials/Banks**: NIM trajectory, provision trends, loan growth by category, capital ratios (CET1, TCE) -- **Healthcare/Pharma**: Pipeline summary (drug, indication, phase, milestone), product revenue breakdown, patent cliff timeline -- **Energy**: Production volumes, realized pricing vs benchmark, proved reserves, breakeven analysis - -Search for relevant series using `discover_company_series` with sector-appropriate keywords. Pull available data and build the narrative. - -New context key: -- `industry_deep_dive` (string) — sector-specific analysis narrative with Daloopa citations, organized by the relevant template above - -## Phase D — Guidance Track Record (follows /guidance-tracker methodology) -Search for guidance series ("guidance", "outlook", "forecast", "estimate", "target"). -Pull guidance and corresponding actuals. Apply +1 quarter offset rule. -Compute beat/miss rates and patterns. -Build `context.guidance` (set `context.has_guidance = true/false`). - -## Phase E — What You Need to Believe (replaces Scenario Analysis) -Using the financial baseline from Phase B: -- Compute trailing 4Q totals for key metrics (revenue, EBITDA, EPS, FCF) -- Analyze segment-level trends and inflections - -Build **falsifiable bull/bear beliefs** instead of probability-weighted scenarios: - -### Bull Beliefs (To Go Long) -Write 4-6 numbered beliefs, each with: -- One **bold statement** (the belief itself) -- 2-3 sentences of **evidence** with Daloopa citations supporting why this could be true -- Each belief must be **falsifiable** — testable with observable data within 6 months - -Example format: "1. **Revenue growth re-accelerates to 15%+ as AI monetization scales.** Cloud segment grew [$X.Xbn](link) last quarter, up X% YoY, with management noting..." - -### Bear Beliefs (To Go Short) -Same format — 4-6 numbered falsifiable beliefs with evidence for the downside case. - -### Valuation Math -For each side: -- Bull target: forward multiple × forward earnings estimate = price target. Show the math. -- Bear target: same structure with bear-case multiple and earnings. - -### Risk/Reward Assessment -- Compare bull upside % vs bear downside % from current price -- If asymmetry is significant (e.g., 30% upside vs 40% downside), flag it explicitly -- State which side has the better risk/reward and why - -New context keys: -- `bull_beliefs` (string) — numbered falsifiable beliefs with evidence -- `bear_beliefs` (string) — numbered falsifiable beliefs with evidence -- `bull_target` (string) — price target + valuation math -- `bear_target` (string) — price target + valuation math -- `risk_reward_assessment` (string) — asymmetry analysis - -## Phase F — Capital Allocation (follows /capital-allocation methodology) -Pull buyback, dividend, share count, FCF data. -Compute shareholder yield, FCF payout ratio, net leverage. -Build `context.capital_allocation`. - -## Phase G — Valuation (follows /dcf + /comps methodology) - -**DCF:** -- Get risk-free rate using the 3-step resolution: (1) MCP market data tools if available, (2) web search, (3) sensible defaults (see `../data-access.md` Section 2) -- Calculate WACC using CAPM -- Project FCF 5 years manually (describe methodology inline and perform calculations directly) -- Compute terminal value, implied share price, sensitivity table -- Build `context.dcf` (set `context.has_dcf = true`) - -**Comps:** -- Identify 5-8 peers -- Get peer trading multiples using the 3-step resolution: (1) MCP market data tools if available, (2) web search, (3) sensible defaults (see `../data-access.md` Section 2) -- If consensus forward estimates are available (`../data-access.md` Section 3), include forward multiples -- Compute implied valuation range from peer multiples -- Build `context.comps` (set `context.has_comps = true`) - -## Phase H — Qualitative Research + News & Catalysts -### SEC Filing Research -Search SEC filings across multiple queries: -- "risk" / "uncertainty" / "challenge" for risk factors -- "growth" / "opportunity" / "expansion" for growth drivers -- "competition" / "market share" for competitive dynamics -- "outlook" / "guidance" for management's forward view -- Company-specific strategic topics (e.g., "AI", "cloud", etc.) - -Extract and organize into: -- `context.risks` — ranked list of risks with impact/probability -- `context.investment_thesis` — variant perception, thesis pillars, catalysts -- `context.company_description` — 2-3 sentence business description - -### News & Catalysts via WebSearch -Run 4 WebSearch queries to gather recent external context: -1. `"{TICKER} {company_name} news {year}"` — recent headlines and developments -2. `"{TICKER} analyst upgrade downgrade price target"` — sell-side sentiment shifts -3. `"{TICKER} catalysts risks"` — forward-looking events and risk factors -4. `"{company_name} industry outlook {sector}"` — macro and industry trends - -Organize results into three new context keys: - -- `news_timeline` (string) — 6-10 key events from the last 6-12 months in reverse chronological order. Each event: date, headline, 1-sentence impact, sentiment tag (Positive / Negative / Mixed / Upcoming). Format as a numbered list. - -- `forward_catalysts` (string) — Organized by timeframe: - - **Near-term (0-3 months, HIGH priority)**: earnings dates, product launches, regulatory decisions - - **Medium-term (3-12 months, MEDIUM priority)**: strategic milestones, contract renewals, industry events - - **Long-term (1-3 years, LOW priority)**: secular trends, market expansion, competitive dynamics - -- `policy_backdrop` (string) — Macro/regulatory context affecting the company. Tariffs, regulation, interest rates, sector-specific policy. Leave empty string if not material. - -## Phase I — Charts -Present all chart data in well-formatted tables. No chart generation needed. - -## Phase J — Synthesis + Tensions + Monitoring -This is the most judgment-intensive step. Be honest and critical — the reader is a professional investor who needs your real assessment, not a balanced summary. - -### Core Synthesis -Write: -- **Executive Summary**: 3-4 sentence TL;DR covering current state, key thesis, valuation view. Include a clear directional view — is this stock attractive, fairly valued, or overvalued at the current price? -- **Variant Perception**: What does the market think vs what do you see in the data? Where is the consensus wrong? If you agree with consensus, say that too — but explain what could change. -- **Key Findings**: Top 3-5 most notable data points or trends — prioritize what changes the investment thesis, not just what's interesting -- **Red Flags & Concerns**: Any quality-of-earnings issues, sustainability questions, or risks the market may be underpricing -- Build `context.executive_summary`, `context.variant_perception` - -### Five Key Tensions -Identify the 5 most critical bull/bear debates for this stock. Each tension is a single line that frames both sides. Alternate between bullish-leaning and bearish-leaning tensions. Every tension must reference a specific data point from the analysis. - -Format as a numbered list: -1. "[Bullish factor] vs [Bearish factor]" — cite the specific metric -2. "[Bearish factor] vs [Bullish factor]" — cite the specific metric -...etc. - -Build `context.five_key_tensions` (string). - -### Monitoring Framework -Build two monitoring lists for ongoing tracking: - -**Quantitative Monitors** — 5-7 specific metrics with explicit thresholds: -- Format: "Metric: current value → bull threshold / bear threshold" -- Example: "Gross Margin: 45.2% → above 46% confirms pricing power / below 43% signals cost pressure" - -**Qualitative Monitors** — 5-7 factors to watch: -- Management tone shifts on earnings calls -- Competitive dynamics (new entrants, pricing pressure) -- Regulatory developments -- Customer concentration changes -- Capital allocation pivots - -Build `context.monitoring_quantitative` and `context.monitoring_qualitative` (strings, numbered lists). - -### Structured Tables -Also build structured tables for the template: -- `context.key_metrics_table` — [{metric, value, vs_prior}] for the exec summary table -- `context.financials_table` — [{metric, q1, q2, ...}] for the financial analysis section -- `context.segments_table`, `context.geo_table`, `context.shares_outstanding_table` -- `context.opex_breakdown_table` — [{metric, q1, q2, ...}] for R&D, SG&A, % of revenue rows -- `context.guidance_table`, `context.comps_table`, etc. - -## Phase K — Render HTML Report - -Using the HTML Report Template from `../design-system.md`, generate a styled HTML report with full CSS inlined. The report should include: - -**Header Section:** -- Company name and ticker -- Report date and firm attribution -- Five Key Tensions (numbered list) - -**Section 1: Executive Summary** -- Key metrics table -- Executive summary narrative -- Variant perception - -**Section 2: Company Overview** -- Business description -- Investment thesis - -**Section 3: Recent News & Catalysts** -- News timeline -- Forward catalysts -- Policy backdrop - -**Section 4: Financial Analysis** -- Financials table (8 quarters) -- Cost structure & margin analysis -- OpEx breakdown table -- Segment and geographic tables -- Share count table - -**Section 5: Industry-Specific Analysis** -- Industry deep dive narrative - -**Section 6: Guidance Track Record** -- Guidance table and beat/miss analysis (if available) - -**Section 7: What You Need to Believe** -- Bull beliefs with valuation target -- Bear beliefs with valuation target -- Risk/reward assessment - -**Section 8: Catalysts** -- Forward catalysts -- Policy backdrop - -**Section 9: Capital Allocation** -- Capital allocation commentary - -**Section 10: Valuation** -- DCF summary and sensitivity (if available) -- Comps commentary (if available) - -**Section 11: Risks** -- Risks summary - -**Section 12: Monitoring Framework** -- Quantitative monitors -- Qualitative monitors - -**Appendix:** -- Additional context or data - -### Context Key Checklist -Verify these keys exist before rendering (set empty string if data unavailable): - -**Cover & Summary:** -`company_name`, `ticker`, `date`, `price`, `market_cap`, `five_key_tensions`, `executive_summary`, `key_metrics_table` - -**Thesis & Overview:** -`investment_thesis`, `variant_perception`, `company_description` - -**News:** -`news_timeline` - -**Financials:** -`financials_table`, `cost_margin_analysis`, `opex_breakdown_table`, `segments_table`, `geo_table`, `shares_outstanding_table` - -**Industry:** -`industry_deep_dive` - -**Guidance:** -`has_guidance`, `guidance_track_record` - -**What You Need to Believe:** -`bull_beliefs`, `bull_target`, `bear_beliefs`, `bear_target`, `risk_reward_assessment` - -**Catalysts:** -`forward_catalysts`, `policy_backdrop` - -**Capital Allocation:** -`capital_allocation_commentary` - -**Valuation:** -`has_dcf`, `dcf_summary`, `has_comps`, `comps_commentary` - -**Risks:** -`risks_summary` - -**Monitoring:** -`monitoring_quantitative`, `monitoring_qualitative` - -**Appendix:** -`appendix_content` - -## Output -Save the styled HTML report as a local file and summarize the output. Tell the user: -- A 3-4 sentence executive summary of the research note -- Key findings and valuation range -- Tell them where the HTML file was saved and that it can be opened in a browser for full formatting - -**Citation enforcement:** Every financial figure from Daloopa in the HTML report must use citation format: `[$X.XX million](https://daloopa.com/src/{fundamental_id})`. If a number came from `get_company_fundamentals`, it must have a citation link. No exceptions. diff --git a/plugins/daloopa/skills/research-note/agents/openai.yaml b/plugins/daloopa/skills/research-note/agents/openai.yaml deleted file mode 100644 index e5bde1c37..000000000 --- a/plugins/daloopa/skills/research-note/agents/openai.yaml +++ /dev/null @@ -1,6 +0,0 @@ -interface: - display_name: Research Note - short_description: Generate a professional Word document research note - default_prompt: Generate a professional research note for AAPL. -policy: - allow_implicit_invocation: true diff --git a/plugins/daloopa/skills/setup/SKILL.md b/plugins/daloopa/skills/setup/SKILL.md deleted file mode 100644 index 5c2b111f9..000000000 --- a/plugins/daloopa/skills/setup/SKILL.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -name: setup -description: Verify Daloopa MCP connection and show available skills ---- - -Walk the user through verifying their Daloopa setup for Codex or ChatGPT. Be conversational and helpful. - -## Step 1: Verify Runtime -Confirm the user is working in Codex, ChatGPT, or another OpenAI environment that can use skills. Explain that this skill checks whether the Daloopa MCP tools are available. - -## Step 2: Verify MCP Connection -This plugin connects to two Daloopa MCP servers: -- **daloopa** (`mcp.daloopa.com/server/mcp`) - Financial data (fundamentals, KPIs, SEC filings) -- **daloopa-docs** (`docs.daloopa.com/mcp`) - Daloopa knowledgebase (API docs, how-tos, usage help) - -Run a quick test by calling `discover_companies` with a well-known ticker like "AAPL" to confirm the data MCP server is connected and responding. Show the user the result. - -If this fails: -- Check that `.mcp.json` is present and configured for the Daloopa MCP servers. -- In Codex, reinstall or reload the plugin after changing MCP configuration. -- In ChatGPT, verify that the Daloopa MCP connector or equivalent tool access is enabled. -- If the server returns `401` or `Reauthentication required`, restart the Daloopa OAuth/login flow in the current environment. -- On first use, OAuth may open a browser window for Daloopa login. - -## Step 3: Quick Tour -Tell the user about the available analysis skills. Use natural-language examples such as: -- "Create a tearsheet for AAPL." -- "Review MSFT earnings and guidance." -- "Build a DCF valuation for NVDA." -- "Create an industry comp sheet for AAPL and peers." - -Each reporting skill saves generated files to the `reports/` directory when file access is available. - -## Step 4: Note on Enhanced Features -For file-heavy workflows such as Word research notes, Excel models, and pitch decks, use the local document, spreadsheet, or presentation generation workflow available in the current OpenAI environment. If a file cannot be generated in the current environment, provide the complete structured content and explain the limitation. diff --git a/plugins/daloopa/skills/setup/agents/openai.yaml b/plugins/daloopa/skills/setup/agents/openai.yaml deleted file mode 100644 index ff9b65947..000000000 --- a/plugins/daloopa/skills/setup/agents/openai.yaml +++ /dev/null @@ -1,6 +0,0 @@ -interface: - display_name: Setup - short_description: Verify Daloopa MCP connection and show available skills - default_prompt: Verify my Daloopa MCP connection. -policy: - allow_implicit_invocation: true diff --git a/plugins/daloopa/skills/supply-chain/SKILL.md b/plugins/daloopa/skills/supply-chain/SKILL.md deleted file mode 100644 index d934b980a..000000000 --- a/plugins/daloopa/skills/supply-chain/SKILL.md +++ /dev/null @@ -1,1145 +0,0 @@ ---- -name: supply-chain -description: Interactive supply chain dashboard mapping suppliers, customers, and - financial interdependencies ---- - -Generate an interactive supply chain dashboard for the company named in the user's request. If no ticker or company is provided, ask for one before proceeding. - -**Before starting, read `../data-access.md` for data access methods and `../design-system.md` for formatting conventions.** Follow the data access detection logic and design system throughout this skill. - -This skill maps the upstream (supplier) and downstream (customer) relationships for a target company, quantifying financial interdependencies in both directions. The output enables an analyst to understand: Who are the critical suppliers and customers? Where is concentration risk on both sides? Which suppliers depend heavily on this company for revenue? Which customers depend on this company's products as critical inputs? How does a shock propagate both upstream (demand shock to suppliers) and downstream (supply disruption to customers)? - -## Output Format - -The final deliverable is a **single self-contained HTML file** with: -- Embedded CSS and JavaScript (no external dependencies) -- **Tier-grouped Canvas network visualization** — columns: Tier 3 → Tier 2 → Tier 1 → Target → Customers, with connection lines. Clickable nodes open detail overlays. -- **Inventory Health Overview table** — for all suppliers: RM%, WIP%, FG% of total inventory shown as stacked colored bars, plus latest total inventory value -- **Supplier cards grouped by tier** — Tier 1 (Critical/Sole-source), Tier 2 (Major Component), Tier 3 (Specialty) with click-to-expand detail overlays -- **Detail overlays** for each supplier containing: - - 10-quarter financial table (Revenue, Gross Profit, Net Income, Gross Margin %) - - 10-quarter inventory breakdown table (Raw Materials, WIP, Finished Goods, Total, RM%, WIP%, FG%) - - Canvas chart: stacked bar chart of inventory composition with Gross Margin % line overlay - - Business description and relationship to target company -- **Customer cards grouped by category** — Channel Partners, Enterprise/B2B, End-Market Exposure — with click-to-expand detail overlays matching supplier depth -- **Detail overlays** for each customer containing: - - 10-quarter financial table (Revenue, Gross Profit, Net Income, Gross Margin %) - - 10-quarter inventory breakdown table (Raw Materials, WIP, Finished Goods, Total, RM%, WIP%, FG%) - - Canvas chart: stacked bar chart of inventory composition with Gross Margin % line overlay - - Business description and relationship to target company -- **Upstream Shock Analysis** section — narrative analysis of how a demand/supply shock to the target company ripples upstream through the supplier chain, with an impact matrix table (Revenue Impact, Margin Impact, Overall Risk per supplier) -- **Downstream Shock Analysis** section — narrative analysis of how a supply disruption at the target company ripples downstream through the customer chain, with an impact matrix table (Input Criticality, Switching Cost, Revenue at Risk, Overall Disruption Risk per customer) -- All financial figures hyperlinked to Daloopa source citations -- A "Download as PDF" button (uses `window.print()`) - -**DOM Safety**: All JavaScript MUST use `createElement()` + `textContent` + `appendChild()` for DOM construction. NEVER use `innerHTML`, `outerHTML`, or any HTML-string injection methods. Use helper functions like `ce(tag)`, `ca(el, attrs)`, `cA(parent, children)` to keep code compact. - -Save to `reports/{TICKER}_supply-chain.html` and open it with `open`. - ---- - -## RESEARCH WORKFLOW - -This is a multi-phase research process. Each phase builds on the previous one. Maximize parallelism across independent API calls. - -### Phase 1: Target Company Identification - -1. Use `discover_companies` with the ticker symbol to get the `company_id`, `latest_calendar_quarter`, and `latest_fiscal_quarter`. Note the firm name for report attribution (default: "Daloopa") — see `../data-access.md` Section 4.5. -2. Pull key financials for the target company: - - Use `discover_company_series` with keywords: ["revenue", "cost of goods", "gross profit", "operating income", "net income", "total cost"] - - Calculate 4 quarters backward from `latest_calendar_quarter`. Use `get_company_fundamentals` for those periods to get TTM figures. -3. Note the target company's total COGS / cost of revenue (TTM) — this is the denominator for supplier % calculations. - -### Phase 2: Supplier Identification - -Run these concurrently to build a comprehensive supplier list: - -**2a. Daloopa Document Search:** -- Search keywords: ["supplier", "vendor", "purchase", "procurement"] across last 2-4 quarters -- Search keywords: ["supply agreement", "supply chain", "manufacturing"] across last 2-4 quarters -- Search keywords: ["sole source", "single source", "key supplier"] across last 2-4 quarters -- Search keywords: ["concentration", "significant supplier"] across last 2-4 quarters -- Search the company's 10-K specifically for supplier disclosures - -**2b. Web Research:** -- `"[TICKER] [company name] key suppliers list 2025 2026"` — supplier identification -- `"[TICKER] supply chain analysis suppliers"` — analyst/industry reports -- `"[TICKER] 10-K supplier disclosure"` — SEC filing analysis -- `"[company name] supply chain map"` — industry supply chain maps -- `"[company name] supplier concentration risk"` — risk analysis -- `"[company name] who manufactures for [company]"` — manufacturing partners -- `"[company name] component suppliers"` — component-level supply chain - -**2c. Industry-Specific Supplier Research:** -For each industry, search for the known critical supply chain relationships: -- **Tech/Hardware**: semiconductor foundries (TSMC, Samsung), display (Samsung, LG, BOE), memory (Samsung, SK Hynix, Micron), sensors/cameras (Sony), glass (Corning), connectors (Amphenol), batteries (CATL, LG Energy), PCB/assembly (Foxconn/Hon Hai, Pegatron, Luxshare) -- **Automotive**: battery (CATL, Panasonic, LG Energy), semiconductors (Infineon, NXP, ON Semi, TI), steel (Nippon, POSCO), tires (Michelin, Bridgestone), glass (AGC, Saint-Gobain) -- **Pharma**: CDMOs (Lonza, Samsung Biologics, Catalent), API suppliers, packaging, distribution -- **Retail**: brand suppliers, logistics (FedEx, UPS), packaging -- **Energy**: equipment (Baker Hughes, Schlumberger), pipe (Tenaris), chemicals - -### Phase 3: Supplier Financial Analysis - -For each identified supplier (aim for 8-15 key suppliers): - -1. **Discover the supplier** using `discover_companies` with their ticker -2. **Pull key financials** from Daloopa: - - `discover_company_series` with keywords: ["revenue", "net income", "gross margin", "operating margin"] - - `get_company_fundamentals` for the same 4 calendar quarters as the target company -3. **Determine revenue concentration**: - - Search Daloopa documents for the supplier: keywords ["[target company name]", "customer", "concentration"] - - Web search: `"[supplier name] [target company] revenue percentage customer"` - - Web search: `"[supplier name] 10-K customer concentration"` - - Many suppliers disclose their top customers in 10-K filings — look for "customers that accounted for 10% or more of revenue" -4. **Determine COGS attribution** (what % of target's costs is this supplier): - - This is often estimated. Use logic like: - - If Apple's COGS is ~$200B TTM and TSMC's revenue from Apple is ~$70B, then TSMC = ~35% of COGS - - Cite the source of each estimate (analyst report, 10-K disclosure, industry research) - - Flag when this is an estimate vs. a disclosed figure -5. **Business & product description**: What does this supplier provide? Be specific (e.g., "5nm/3nm chip fabrication for A-series and M-series SoCs" not just "semiconductors") - -### Phase 3b: Inventory & 10-Quarter Financial Data - -For the target company AND each identified supplier (8-15 companies), pull **10 quarters** of data: - -1. **Discover inventory series** using `discover_company_series` with keywords: ["raw material", "work in process", "finished good", "inventory", "inventories"] - - Look for separate RM, WIP, FG series, plus a total inventory series - - Some companies report "carrying amount" breakdowns — use those for RM/WIP/FG splits -2. **Discover financial series** using `discover_company_series` with keywords: ["revenue", "gross profit", "net income", "gross margin"] -3. **Pull 10 quarters** using `get_company_fundamentals`. Calculate 10 quarters backward from `latest_calendar_quarter`. - - Example: if latest is Q4'25, pull ["2023Q3", "2023Q4", "2024Q1", "2024Q2", "2024Q3", "2024Q4", "2025Q1", "2025Q2", "2025Q3", "2025Q4"] -4. **Compute inventory composition**: For each quarter, calculate RM%, WIP%, FG% of total inventory - - High WIP% can signal production bottlenecks - - Rising FG% can signal demand weakness - - Rising RM% can signal supply hoarding or procurement buildup -5. **Handle missing data gracefully**: Some suppliers may not report full inventory breakdowns — show what's available and note gaps -6. **Multi-currency handling**: Note the reporting currency for each company (USD, NTD, KRW, EUR, etc.) and display with appropriate units (e.g., "NTD B" for TSMC, "KRW T" for Samsung) - -Run inventory and financial series pulls in parallel across all companies. - -### Phase 4: Customer / Downstream Identification - -The downstream side requires the same research rigor as the upstream side. Run these concurrently to build a comprehensive customer list: - -**4a. Daloopa Document Search (target company filings):** -- Search keywords: ["customer", "contract", "agreement", "channel"] across last 2-4 quarters -- Search keywords: ["customer concentration", "significant customer", "major customer"] across last 2-4 quarters — many companies disclose customers >10% of revenue -- Search keywords: ["distribution", "retail partner", "reseller", "licensee"] across last 2-4 quarters -- Search keywords: ["accounts receivable", "contract asset", "deferred revenue"] — concentration in A/R often reveals customer dependency even when not explicitly named -- Search the company's 10-K specifically for customer disclosures and segment end-market breakdowns - -**4b. Web Research:** -- `"[TICKER] [company name] major customers list"` — direct customer identification -- `"[TICKER] customer concentration revenue breakdown"` — analyst/industry reports -- `"[TICKER] 10-K customer disclosure"` — SEC filing analysis -- `"[company name] who buys from [company name]"` — downstream identification -- `"[company name] channel partners distributors"` — channel analysis -- `"[company name] end market exposure"` — end-market breakdown - -**4c. Industry-Specific Customer Research:** -For each industry, search for the known critical downstream relationships: -- **Semiconductors**: Which OEMs depend on these chips? (e.g., NVDA → hyperscalers MSFT/AMZN/GOOG, QCOM → smartphone OEMs AAPL/Samsung, AVGO → networking OEMs Cisco/Arista) -- **Components/Materials**: Which assemblers or product companies use these inputs? (e.g., Corning → AAPL/Samsung for glass, TSMC → fabless semis NVDA/AMD/AAPL) -- **Software/Platform**: Who builds on this platform? (e.g., MSFT Azure → ISVs, AAPL App Store → developers, Salesforce → SI partners) -- **Consumer products**: Channel partners (carriers, retailers, e-commerce) and enterprise customers -- **Industrial/B2B**: End-market verticals (auto, aerospace, medical, telecom) -- **Pharma/Biotech**: Distributors (McKesson, AmerisourceBergen), PBMs, hospital systems - -**4d. Customer Financial Analysis:** - -For each identified customer (aim for 6-10 key customers): - -1. **Discover the customer** using `discover_companies` with their ticker -2. **Pull key financials** from Daloopa: - - `discover_company_series` with keywords: ["revenue", "net income", "gross margin", "cost of goods", "operating income"] - - `get_company_fundamentals` for the same 4 calendar quarters as the target company -3. **Determine revenue attribution** (what % of target's revenue comes from this customer): - - Search Daloopa documents for the target company: keywords ["[customer name]", "customer", "concentration", "accounts receivable"] - - Web search: `"[target company] [customer name] revenue percentage"` - - Web search: `"[target company] 10-K customer concentration"` - - Many companies disclose customers that account for >10% of revenue in their 10-K -4. **Determine input criticality** (what % of customer's COGS comes from target): - - This is the inverse of the supplier analysis: if the target sells $X to a customer with $Y in COGS, then input share = X/Y - - Search for: `"[customer name] [target company] supplier dependence"` or `"[customer name] key inputs components"` - - Flag whether the target's product is a critical, hard-to-substitute input vs. a commodity with alternatives -5. **Assess switching costs**: Can the customer easily replace the target company's product? - - **High switching cost**: Custom/proprietary integration, long qualification cycles, regulatory requirements (e.g., TSMC's process node — customers can't easily switch foundries mid-design) - - **Medium switching cost**: Some integration required but alternatives exist with 6-12 month transition - - **Low switching cost**: Commodity input, multiple qualified alternatives, short switching timeline -6. **Business & product description**: What does the target supply to this customer? Be specific (e.g., "A17 Pro and M4 SoCs fabricated on TSMC's 3nm process" not just "chips") - -### Phase 4e: Customer Inventory & 10-Quarter Financial Data - -Mirror Phase 3b for the customer side. For each identified customer (6-10 companies), pull **10 quarters** of data: - -1. **Discover inventory series** using `discover_company_series` with keywords: ["raw material", "work in process", "finished good", "inventory", "inventories"] - - Look for separate RM, WIP, FG series, plus a total inventory series -2. **Discover financial series** using `discover_company_series` with keywords: ["revenue", "gross profit", "net income", "gross margin"] -3. **Pull 10 quarters** using `get_company_fundamentals` with the same 10 calendar quarters as the target company and suppliers (calculated from `latest_calendar_quarter`) -4. **Compute inventory composition**: RM%, WIP%, FG% of total inventory - - For customers, inventory signals have different meaning: - - Rising RM% at a customer → they're stocking up on target company's inputs (bullish for target's near-term revenue, but may mean future destocking) - - Falling RM% → customer is drawing down inventory, may signal reduced orders ahead - - Rising FG% at a customer → demand for the customer's end product is softening, which will flow back upstream to the target -5. **Handle missing data gracefully**: Some customers may not report inventory breakdowns — show what's available -6. **Multi-currency handling**: Same as suppliers — note reporting currency - -Run customer inventory and financial series pulls in parallel, and in parallel with supplier pulls where possible. - -### Phase 5: Tier 2 Supplier Research - -For the top 3-5 most important Tier 1 suppliers, repeat a lighter version of Phase 2-3: - -1. Identify their key suppliers (Tier 2 to the original target) -2. Pull basic financials -3. Determine what they supply and rough revenue/cost relationships -4. This enables the "drill deeper" functionality in the dashboard - -### Phase 6: Data Assembly & Synthesis - -Before writing HTML, organize all data into this structure: - -``` -TARGET COMPANY: - - Name, ticker, description - - TTM Revenue, COGS, Gross Profit, Net Income, Gross Margin, Op Margin - - Market cap, stock price (from web) - -TIER 1 SUPPLIERS (sorted by estimated % of target COGS, descending): - For each: - - Name, ticker, description - - What they supply (specific products/components) - - Estimated % of target company COGS (with source/logic) - - % of supplier revenue from target company (with source) - - TTM Revenue, Net Income, Gross Margin - - Market cap - - Relationship summary (sole source? multi-source? critical?) - - Their key suppliers (Tier 2) if researched - - 10-quarter financials: Revenue, Gross Profit, Net Income, GM% (with Daloopa citation IDs) - - 10-quarter inventory: RM, WIP, FG, Total, RM%, WIP%, FG% (with Daloopa citation IDs) - - Reporting currency and unit (e.g., USD $M, NTD B, KRW T) - -TIER 1 CUSTOMERS (sorted by estimated % of target revenue, descending): - For each: - - Name, ticker, description - - What target company supplies to them (specific products/services) - - Estimated % of target revenue from this customer (with source/logic) - - Estimated % of customer COGS from target (input criticality, with source) - - Switching cost assessment (High/Medium/Low with reasoning) - - TTM Revenue, COGS, Net Income, Gross Margin - - Market cap - - Relationship summary (exclusive? multi-source? long-term contract? spot?) - - 10-quarter financials: Revenue, Gross Profit, Net Income, GM% (with Daloopa citation IDs) - - 10-quarter inventory: RM, WIP, FG, Total, RM%, WIP%, FG% (with Daloopa citation IDs) - - Reporting currency and unit (e.g., USD $M, EUR M, JPY B) - -TIER 2 CUSTOMERS (for top 3-5 Tier 1 customers — who do THEY sell to?): - For each Tier 1 customer, their key customers with basic data - This traces the value chain forward: Target → Customer → End Market - -TIER 2 SUPPLIERS (for top 3-5 Tier 1 suppliers): - For each Tier 1 supplier, their key suppliers with basic data -``` - -### Phase 6b: Upstream Shock Analysis (Demand Shock → Suppliers) - -Prepare a narrative analysis of how a demand shock at the target company would ripple upstream through the supplier chain: - -1. **Classify each supplier by dependency level**: - - **High dependency**: Target company is >20% of supplier's revenue → severe impact from demand shock - - **Moderate dependency**: Target is 10-20% of revenue → meaningful but manageable impact - - **Low dependency**: Target is <10% of revenue → diversified, minimal direct impact - -2. **Assess shock propagation for each supplier**: - - **Revenue Impact** (High/Medium/Low): Based on % of revenue from target - - **Margin Impact** (High/Medium/Low): Based on operating leverage, fixed costs, ability to find replacement demand - - **Inventory Risk**: Suppliers with high FG% are more exposed to demand shocks; those with high RM% face supply-side risk - - **Substitutability**: Can the target switch to alternatives? Can the supplier find other customers? - -3. **Build an impact matrix table** with columns: Supplier, Tier, Revenue Dependency, Revenue Impact, Margin Impact, Overall Risk - -4. **Write narrative sections**: - - "Most Exposed Suppliers" — 2-3 paragraphs on suppliers facing highest risk - - "Resilient Suppliers" — suppliers with diversified revenue bases - - "Second-Order Effects" — how Tier 2 suppliers would be indirectly affected - - "Key Monitoring Metrics" — what an analyst should watch (inventory days, order backlog, etc.) - -### Phase 6c: Downstream Shock Analysis (Supply Disruption → Customers) - -Prepare a narrative analysis of how a supply disruption at the target company (production halt, quality issue, capacity constraint, export ban) would ripple downstream through the customer chain: - -1. **Classify each customer by input criticality**: - - **Critical input**: Target's product is a key component with no drop-in replacement; disruption halts customer production (e.g., TSMC to Apple — no alternative foundry for A-series chips) - - **Important input**: Target is a significant but not sole supplier; customer can partially substitute with 3-6 month lead time - - **Supplementary input**: Target provides a non-critical input; customer has multiple qualified alternatives - -2. **Assess downstream disruption for each customer**: - - **Input Criticality** (High/Medium/Low): How essential is the target's product to the customer's operations? - - **Switching Cost** (High/Medium/Low): How long and expensive to qualify an alternative? Are there contractual lock-ins? - - **Revenue at Risk**: What portion of the customer's revenue depends on products that use the target's inputs? - - **Inventory Buffer**: Does the customer hold significant RM inventory of the target's product? How many weeks/months of supply? - - **Alternative Sources**: Who else could supply this? What's the capacity gap? - -3. **Build a downstream impact matrix table** with columns: Customer, Category, Input Criticality, Switching Cost, Revenue at Risk, Inventory Buffer, Overall Disruption Risk - -4. **Write narrative sections**: - - "Most Vulnerable Customers" — customers who would face production disruption or revenue loss - - "Customers with Alternatives" — those who can substitute away from the target - - "Pricing Power Implications" — if the target faces a supply constraint, which customers have the leverage to secure allocation vs. which get cut first? - - "Channel Inventory Signals" — what customer inventory levels (especially RM%) tell you about near-term order patterns for the target company - - "Second-Order Downstream Effects" — how end consumers or Tier 2 customers would be affected - ---- - -## HTML TEMPLATE & DESIGN SYSTEM - -Start from the HTML Report Template in `../design-system.md` (copy the full ` - -``` - -### Core JavaScript Pattern - -The dashboard uses vanilla JavaScript for interactivity. Include these functions in a ` -``` - ---- - -## DOCUMENT STRUCTURE - -The HTML document has these sections. Every section is mandatory. - -### Section 1: Print Button Bar -```html -
- - or Cmd+P → Save as PDF -
-``` - -### Section 2: Page Header -```html - -``` - -### Section 3: Target Company KPI Bar -Show 6 KPIs for the target company: -```html -
-
-
TTM Revenue
$XXB
-
TTM COGS
$XXB
-
Gross Margin
XX.X%
-
Suppliers Mapped
XX
-
Top 5 = % of COGS
~XX%
-
Key Customers
XX
-
-``` - -### Section 4: Page Layout - -The dashboard uses a **single scrollable page** (no tabs) with the following vertical order: -1. KPI bar (target company overview) -2. Canvas network visualization (full chain: Tier 3 → Tier 2 → Tier 1 → Target → Customers → Tier 2 Customers) -3. Inventory Health Overview table (suppliers AND customers) -4. Supplier cards grouped by tier -5. Customer cards grouped by category -6. Upstream Shock Analysis (demand shock → suppliers, narrative + impact matrix) -7. Downstream Shock Analysis (supply disruption → customers, narrative + impact matrix) -8. Concentration Analysis summary (both upstream and downstream) -9. Footer - -Each supplier and customer card is clickable — opening a **full-screen detail overlay** with 10-quarter financials, inventory tables, and Canvas charts. The overlay is dismissed with × or backdrop click. - -### Section 5: Canvas Network Visualization - -Replace the HTML card-based chain view with a **Canvas-based tier-grouped network**: - -- Use a `` element spanning the full container width, ~420px height -- **Column layout**: Tier 3 (left) → Tier 2 → Tier 1 → Target (center) → Customers (right) -- Draw each company as a rounded rectangle node with ticker label -- Draw connection lines (bezier curves or straight lines) between related nodes -- Color-code by tier: Target = navy (#1B2A4A), Tier 1 = steel blue (#4A6FA5), Tier 2 = gold (#C5A55A), Tier 3 = dark gray (#6C757D), Customers = mid gray (#E9ECEF) -- **Clickable nodes**: Track click coordinates with a `click` event listener on the canvas, determine which node was clicked via hit-testing, then open the detail overlay for that company -- Column headers ("TIER 1", "TIER 2", "TARGET", etc.) drawn as text above each column -- Responsive: redraw on `window.resize` - -```javascript -// Example network drawing function pattern: -function drawNetwork() { - const cv = document.getElementById('networkCanvas'); - const ctx = cv.getContext('2d'); - cv.width = cv.parentElement.clientWidth; - cv.height = 420; - ctx.clearRect(0, 0, cv.width, cv.height); - - // Define columns: x positions for each tier - const cols = { - tier3: cv.width * 0.08, - tier2: cv.width * 0.28, - tier1: cv.width * 0.48, - target: cv.width * 0.68, - customers: cv.width * 0.88 - }; - - // Draw column headers, nodes, and connection lines - // Store node positions for hit-testing on click -} -``` - -### Section 5b: Inventory Health Overview - -Below the network, add an **Inventory Health Overview** table showing all suppliers AND customers: - -``` -| Company | Ticker | Role | Total Inventory | RM% | WIP% | FG% | Composition Bar | -``` - -- The "Role" column shows "Supplier T1", "Supplier T2", "Customer", or "Target" -- The "Composition Bar" column renders a stacked horizontal bar (RM = blue, WIP = amber, FG = green) using inline CSS `background: linear-gradient(...)` -- Sort by total inventory descending or by FG% descending (highest FG% = most demand-shock exposure) -- Include the target company at the top of the table, then suppliers, then customers -- Each inventory value must link to its Daloopa citation -- **Analytical note**: For suppliers, rising FG% signals demand weakness from the target. For customers, rising RM% signals stockpiling of the target's inputs (bullish near-term, potential destocking risk later). Falling RM% at customers signals reduced orders ahead. - -### Section 5c: Supplier Cards by Tier - -Below the inventory overview, render supplier cards grouped under tier headings: - -``` -── TIER 1 · Critical / Sole-Source ────── -[Card: TSMC] [Card: Samsung] [Card: Broadcom] ... - -── TIER 2 · Major Component ───────────── -[Card: Qualcomm] [Card: Skyworks] [Card: TXN] ... - -── TIER 3 · Specialty ─────────────────── -[Card: Corning] [Card: Cirrus Logic] ... -``` - -Each card shows: Company name, ticker, what they supply, TTM revenue, gross margin, estimated % of target COGS, a colored dot for tier. Clicking a card opens the detail overlay. - -### Section 5d: Customer Cards by Category - -Below the supplier cards, render customer cards grouped under category headings: - -``` -── CHANNEL PARTNERS · Distribution & Retail ────── -[Card: Best Buy] [Card: AT&T] [Card: Verizon] ... - -── ENTERPRISE / B2B · Direct Customers ─────────── -[Card: Enterprise customer 1] [Card: Enterprise customer 2] ... - -── END-MARKET EXPOSURE · Indirect Demand ───────── -[Card: End-market exposure 1] ... -``` - -Categories should be adapted to the target company's business model: -- **B2B/Components companies**: Group by end-market vertical (Auto, Aerospace, Consumer Electronics, Data Center, etc.) -- **Consumer products**: Group by channel (Direct, Retail Partners, Carriers, Enterprise) -- **Software/Platform**: Group by customer type (Enterprise, SMB, Consumer, Government) -- **Industrials**: Group by end-market (Energy, Infrastructure, Transportation, Defense) - -Each card shows: Company name, ticker, what the target supplies to them, TTM revenue, gross margin, estimated % of target revenue from this customer, input criticality badge (HIGH/MED/LOW), switching cost indicator. Clicking a card opens the detail overlay. - -Customer cards use the `.customer-card` CSS class (border-left color = `--node-customer`). - -### Section 6: Detail Overlay - -When a user clicks a supplier card, customer card, or network node, show a **full-screen overlay** with comprehensive detail. The overlay structure is the same for both suppliers and customers. - -**Structure:** -- Fixed overlay div covering the viewport with semi-transparent backdrop -- Close button (×) in top-right corner -- Content area with four sub-sections: - -**6a. Financial History Table (10 Quarters)** -``` -| Metric | Q3'23 | Q4'23 | Q1'24 | ... | Q4'25 | -|---------------|-------|-------|-------|-----|-------| -| Revenue | $XXB | $XXB | ... | | | -| Gross Profit | $XXB | $XXB | ... | | | -| Net Income | $XXB | $XXB | ... | | | -| Gross Margin | XX.X% | XX.X% | ... | | | -``` -- Every value must be a Daloopa citation link: `$value` -- Display currency unit in header (e.g., "USD $M", "NTD B", "KRW T") - -**6b. Inventory Breakdown Table (10 Quarters)** -``` -| Metric | Q3'23 | Q4'23 | ... | -|------------------|-------|-------|-----| -| Raw Materials | $XXM | $XXM | ... | -| Work in Process | $XXM | $XXM | ... | -| Finished Goods | $XXM | $XXM | ... | -| Total Inventory | $XXM | $XXM | ... | -| RM% | XX% | XX% | ... | -| WIP% | XX% | XX% | ... | -| FG% | XX% | XX% | ... | -``` -- Absolute values are Daloopa citation links; percentages are computed (no link needed) - -**6c. Canvas Chart — Inventory Composition vs. Gross Margin** -- **Stacked bar chart**: Each bar represents a quarter. Segments = RM (blue), WIP (amber), FG (green), stacked to total inventory value -- **Line overlay**: Gross Margin % plotted as a line with dots on the right Y-axis (0-100%) -- **Left Y-axis**: Inventory value in reporting currency -- **X-axis**: Quarter labels (Q3'23, Q4'24, etc.) -- Draw using Canvas 2D API with `createElement('canvas')`, NOT any charting library -- Include a legend below the chart - -**6d. Relationship Context Panel** -- For **suppliers**: Show "% of target COGS" bar, "% of supplier revenue from target" bar, switching cost assessment, sole-source flag, geographic risk -- For **customers**: Show "% of target revenue from customer" bar, "% of customer COGS from target" (input criticality) bar, switching cost assessment, contract type (long-term/spot), alternative sources available -- Both: Business description, specific products/services in the relationship, and source attribution for all estimates - -### Section 7: Upstream Shock Analysis - -A dedicated section (below the customer cards) analyzing how a demand shock at the target company would propagate upstream: - -**7a. Narrative Analysis** — 3-4 paragraphs covering: -- "Most Exposed Suppliers" — those with highest revenue dependency on target -- "Resilient Suppliers" — diversified revenue, low target concentration -- "Second-Order Effects" — how Tier 2/3 suppliers are indirectly affected -- "Key Monitoring Metrics" — inventory days, order backlogs, WIP trends to watch - -**7b. Upstream Impact Matrix Table** -``` -| Supplier | Tier | Rev. from Target | Revenue Impact | Margin Impact | Inventory Risk | Overall | -|----------|------|------------------|---------------|---------------|----------------|---------| -| TSMC | 1 | ~25% | HIGH | MEDIUM | LOW | HIGH | -| ... | | | | | | | -``` -- Color-code risk cells: HIGH = red background, MEDIUM = amber, LOW = green -- Sort by Overall Risk descending - -### Section 7c: Downstream Shock Analysis - -A dedicated section analyzing how a supply disruption at the target company would propagate downstream. This is the mirror of Section 7 — instead of "what happens to suppliers if target demand drops," this asks "what happens to customers if the target can't deliver." - -**7c-i. Narrative Analysis** — 3-4 paragraphs covering: -- "Most Vulnerable Customers" — customers with highest input criticality and switching costs; a target disruption would directly impair their revenue -- "Customers with Alternatives" — those who can substitute within a reasonable timeframe; quantify how long and at what cost -- "Pricing Power Dynamics" — if the target faces constrained supply, who gets allocation priority? Large customers with long-term contracts typically get served first; smaller or spot customers get cut. This reveals the target's pricing power and customer hierarchy. -- "Channel Inventory as Leading Indicator" — what customer RM% trends tell you about the target's forward order book. If customers are building inventory, the target's next 1-2 quarters look strong but risk destocking later. If customers are drawing down, near-term orders may disappoint. - -**7c-ii. Downstream Impact Matrix Table** -``` -| Customer | Category | Input Criticality | Switching Cost | Rev. at Risk | Inventory Buffer | Overall Disruption Risk | -|----------|----------|-------------------|---------------|-------------|-----------------|------------------------| -| Best Buy | Channel | LOW | LOW | ~$40B | ~4 weeks | LOW | -| ... | | | | | | | -``` -- Color-code risk cells: HIGH = red background, MEDIUM = amber, LOW = green -- Sort by Overall Disruption Risk descending -- "Rev. at Risk" = the customer's revenue that depends on products using the target's inputs -- "Inventory Buffer" = estimated weeks/months of the target's product the customer holds in RM inventory - -### Section 8: Concentration Analysis - -Summary of concentration risk on BOTH sides of the value chain: - -**Upstream (Supplier) Concentration:** -- Supplier concentration: flag any supplier >20% of COGS -- Revenue dependency: flag any supplier where target is >25% of their revenue -- Geographic concentration: note country exposure (Taiwan, China, South Korea, etc.) -- Single-source dependencies: list sole-source suppliers - -**Downstream (Customer) Concentration:** -- Customer concentration: flag any customer >15% of target's revenue -- Input criticality: flag any customer where target's product is a critical, hard-to-substitute input (high switching cost) -- Channel concentration: what % of revenue flows through the top 3 channels? Is there a single channel that could be disrupted (e.g., carrier subsidies ending, retail partner going bankrupt)? -- Geographic exposure: note country/region concentration in the customer base -- Contract risk: flag any large customer relationships that are up for renewal, at risk of in-sourcing, or where the customer is developing alternatives - -**Bidirectional Risk Summary:** -- Which relationships have asymmetric power? (target depends on supplier more than supplier depends on target, or vice versa) -- Where are the mutual dependencies? (both parties depend heavily on each other — most stable but hardest to exit) -- What's the "weakest link"? Identify the single point of failure in the full chain that would cause the most damage if disrupted - -### Section 10: Footer -```html - -
- - -``` - ---- - -## CRITICAL RULES - -### Citation & Formatting Rules -Follow `../data-access.md` Section 4 for all citation requirements and `../design-system.md` for number formatting. Additional supply-chain-specific conventions: -- For estimated figures (% of COGS, % of revenue), always explain the methodology in the detail panel -- Use `~` prefix for all estimates (e.g., `~35%` of COGS) -- Use `–` for ranges, `—` for em-dashes, `·` for separators - -### Upstream Concentration Risk Classification -- **HIGH** (red): >15% of COGS, sole/single source, or geopolitical risk -- **MED** (amber): 5-15% of COGS, limited alternatives, or moderate switching costs -- **LOW** (green): <5% of COGS, multiple alternatives, easy to switch - -### Downstream Criticality Classification -- **HIGH** (red): Customer is >15% of target's revenue, OR target's product is a critical input with high switching costs for the customer -- **MED** (amber): Customer is 5-15% of revenue, OR target's product is important but substitutable with 6-12 month transition -- **LOW** (green): Customer is <5% of revenue, AND target's product is a commodity input with multiple alternatives - -### Supply Chain Data Quality -- Always distinguish between **disclosed** (from 10-K, investor reports) and **estimated** (from analyst research, proportional analysis) -- When estimating % of COGS, show your math: "TSMC Apple revenue ~$70B (per TSMC 10-K customer disclosure) / Apple TTM COGS ~$200B = ~35%" -- Use `~` prefix for all estimates -- Include source attribution for every data point in the detail panel -- If a figure cannot be reliably estimated, say "Not disclosed" rather than guessing - -### Interactivity Rules -- Every company card (supplier AND customer) and network node must be clickable to open a detail overlay -- Detail overlays show: 10-quarter financials, 10-quarter inventory breakdown, Canvas inventory chart, relationship context panel, business description -- Close overlay with × button or clicking the backdrop -- Network visualization must redraw on window resize -- All interactions must be smooth and not reload the page - -### DOM Safety Rules (CRITICAL) -- ALL JavaScript DOM construction MUST use `createElement()` + `textContent` + `appendChild()` -- NEVER use `innerHTML`, `outerHTML`, or any HTML-string-based injection methods -- NEVER use DOM write/writeln methods -- Define compact helper functions to keep DOM construction code readable: - - `ce(tag)` → `document.createElement(tag)` - - `ca(el, attrs)` → sets attributes/textContent on an element - - `cA(parent, children)` → appends array of children to parent -- This is required because security hooks will block the file if HTML-string injection is detected - ---- - -## EXECUTION SEQUENCE - -Follow this exact sequence. Maximize parallelism — run independent searches and API calls concurrently. The 10-quarter pull is the most data-intensive step; batch aggressively. - -1. **discover_companies** → get target company_id -2. **discover_company_series + get_company_fundamentals** → pull target company financials (revenue, COGS, margins) AND inventory series for 10 quarters -3. **search_documents + WebSearch** → identify suppliers AND customers (run in parallel, multiple queries for each direction) -4. **discover_companies** → look up each identified supplier AND customer by ticker (batch all at once) -5. **discover_company_series** → for each supplier AND customer, pull BOTH financial series (revenue, GP, NI, GM) AND inventory series (RM, WIP, FG, total) — batch these in parallel -6. **get_company_fundamentals** → pull 10 quarters of data for all suppliers AND customers (batch in parallel, group series_ids per company) -7. **search_documents + WebSearch** → determine revenue concentration for each supplier AND revenue attribution + input criticality for each customer (parallel) -8. **Repeat lighter version of 3-6** for Tier 2 suppliers (top 3-5 Tier 1 suppliers' suppliers) AND Tier 2 customers (top 3-5 Tier 1 customers' end markets) -9. **Upstream Shock Analysis** → classify suppliers by dependency, assess propagation, build upstream impact matrix -10. **Downstream Shock Analysis** → classify customers by input criticality and switching costs, assess disruption propagation, build downstream impact matrix -11. **Synthesize** → organize all data (suppliers, customers, financials, inventory breakdowns, both shock analyses) into the framework -12. **Write HTML** → generate the complete self-contained HTML file using DOM-safe JavaScript (no HTML-string injection) -13. **Save & Open** → save and open in browser - -## Save Report - -Save to `reports/{TICKER}_supply-chain.html` using the HTML Report Template from `../design-system.md` as the CSS base, extended with the dashboard-specific styles above. Write the full analysis as styled HTML with all CSS inlined. This is the final deliverable — no intermediate markdown step needed. - -All financial figures must use Daloopa citation format: `$X.XX million` - -Tell the user where the HTML report was saved. - -Highlight the key findings with a critical lens: -- **Upstream concentration risk**: Which suppliers represent the biggest single points of failure? Are there sole-source dependencies the market may be underpricing? -- **Downstream concentration risk**: Is the target overly dependent on a few customers? Are any major customers at risk of in-sourcing or switching? -- **Asymmetric exposure (upstream)**: Which suppliers depend heavily on the target for revenue — and what would happen to them in a demand shock? -- **Asymmetric exposure (downstream)**: Which customers depend heavily on the target as a critical input — and what would happen to them in a supply disruption? -- **Inventory signals (bidirectional)**: Supplier FG% rising = demand weakness. Customer RM% rising = stockpiling (bullish near-term, destocking risk later). Customer RM% falling = reduced orders ahead. -- **Pricing power**: Does the target have more leverage over its customers or do its suppliers have more leverage over it? Where does the target sit in the power hierarchy of its value chain? -- **What the market is missing**: Is there a supply chain vulnerability, customer concentration risk, or value chain shift that isn't widely discussed? diff --git a/plugins/daloopa/skills/supply-chain/agents/openai.yaml b/plugins/daloopa/skills/supply-chain/agents/openai.yaml deleted file mode 100644 index 6bf58b121..000000000 --- a/plugins/daloopa/skills/supply-chain/agents/openai.yaml +++ /dev/null @@ -1,7 +0,0 @@ -interface: - display_name: Supply Chain - short_description: Interactive supply chain dashboard mapping suppliers, customers, - and financial interdependencies - default_prompt: Map AAPL suppliers, customers, and dependencies. -policy: - allow_implicit_invocation: true diff --git a/plugins/daloopa/skills/tearsheet/SKILL.md b/plugins/daloopa/skills/tearsheet/SKILL.md deleted file mode 100644 index e537d5faa..000000000 --- a/plugins/daloopa/skills/tearsheet/SKILL.md +++ /dev/null @@ -1,177 +0,0 @@ ---- -name: tearsheet -description: Quick one-page company overview and snapshot ---- - -Generate a concise company tearsheet for the company specified by the user named in the user's request. If no ticker or company is provided, ask for one before proceeding. - -This should be a quick, one-page overview — the kind of snapshot an analyst pulls up before a meeting. - -**Before starting, read `../data-access.md` for data access methods and `../design-system.md` for formatting conventions.** Follow the data access detection logic and design system throughout this skill. - -Follow these steps: - -## 1. Company Lookup -Look up the company by ticker using `discover_companies`. Capture: -- `company_id` -- `latest_calendar_quarter` — anchor for all period calculations below (see `../data-access.md` Section 1.5) -- `latest_fiscal_quarter` -- Firm name for report attribution (default: "Daloopa") — see `../data-access.md` Section 4.5 - -## 1b. Current Stock Price -Get the current stock price using `get_stock_prices` (see `../data-access.md` Section 1.7). Pass `company_id` and `dates` for the 3 most recent calendar days — use the most recent returned close price. Include the price, date, and a simple context line (e.g., 52-week range or YTD change if you have enough history from a quick `start_date`/`end_date` pull of the last 12 months). Display this prominently at the top of the report next to the company name. - -## 2. Key Financials -Calculate periods backward from `latest_calendar_quarter` (8 quarters total: last 4 + year-ago for each to enable YoY): -Pull: -- Revenue -- Gross Profit -- Operating Income -- EBITDA (if not reported, compute as Operating Income + D&A — label it "EBITDA (calc.)" in the report) -- Net Income -- Diluted EPS -- Operating Cash Flow -- CapEx (Purchases of property, plant and equipment) -- Free Cash Flow (compute as Operating Cash Flow - CapEx — label it "FCF (calc.)" in the report) - -For any derived/computed metric, mark it with "(calc.)" so the reader knows it's not directly sourced. - -## 3. Key Operating KPIs -This section is strictly for **business-driver metrics** — the operational numbers that actually move revenue and earnings. Do NOT put financial statement items (D&A, share count, buybacks, dividends) here — those belong in the financials or capital return sections. - -First, think about what the most important KPIs are for THIS specific company based on its business model and what drives its valuation. For example: -- **SaaS/cloud**: ARR, net revenue retention, RPO/cRPO, customers >$100K, cloud gross margin -- **Consumer tech**: DAU/MAU, ARPU, engagement metrics, installed base, paid subscribers -- **E-commerce/marketplace**: GMV, take rate, active buyers/sellers, order frequency -- **Retail**: same-store sales, store count, average ticket, transactions -- **Telecom/media**: subscribers, churn, ARPU, content spend -- **Hardware**: units shipped, ASP, attach rate, installed base, products vs services gross margin split -- **Financial services**: AUM, NIM, loan growth, credit quality metrics -- **Pharma/biotech**: pipeline stage, patient starts, scripts, market share -- **Industrials/energy**: backlog, book-to-bill, utilization, production volumes - -Then search for those specific KPIs by name, plus cast a wider net for anything else Daloopa has. Also search for: -- Segment/product revenue breakdown -- Geographic revenue breakdown - -**If the company discloses few operational KPIs** (e.g., Apple stopped reporting iPhone units in 2019), acknowledge the disclosure gap explicitly rather than padding the section with financial metrics. A short note like "Apple does not disclose unit volumes or ASPs; segment revenue is the finest granularity available" is more informative than showing D&A and buybacks as fake KPIs. - -**Always search broadly** — companies often disclose more KPIs than you'd expect. For Apple, beyond segment revenue, Daloopa also has: installed base active devices (~2.5bn), products gross margin vs services gross margin (the mix shift story), and paid subscriptions. These are real operational metrics. Search with keywords like "installed", "active", "subscriber", "margin" by segment, not just the obvious financial terms. - -Pull for the same period as financials. - -## 3b. Capital Return -Pull share count, share repurchases, and dividends paid for the same periods. This is a separate section from operating KPIs — it shows how the company is returning cash to shareholders. - -## 4. Compute Key Ratios -Show trend over the last 4 quarters with YoY change for EACH quarter (not just the earliest): -- Gross Margin % -- Operating Margin % -- EBITDA Margin % -- Net Margin % -- Revenue Growth (YoY) -- EPS Growth (YoY) - -If the company has strong seasonality (e.g., retail Q4, back-to-school, etc.), add a brief note flagging it so YoY comparisons are read in context rather than sequential QoQ. - -## 5. Recent Developments -Search the most recent 2 quarters of filings. Try multiple keyword searches to get coverage: -- First search: company name + "results" or "record" for earnings highlights -- Second search: "outlook" or "guidance" or "expect" for forward-looking commentary -- Third search: strategy-specific terms relevant to the company (e.g., "AI", "cloud", "subscribers", "margin") -- If a search returns empty, try broader single-keyword searches before giving up - -Extract: -- Business description / what the company does (2-3 sentences) -- Key recent developments or announcements -- Management's top priorities or strategic focus areas -- Any notable management quotes (with document citations) -Keep this brief — 3-5 bullet points max. - -## 6. Five Key Tensions -Identify the 5 most critical bull/bear debates for this stock. Each tension is a single line that frames both sides. Alternate between bullish-leaning and bearish-leaning tensions. Every tension must reference a specific data point from the analysis above. - -Format as a numbered list: -1. "[Bullish factor] vs [Bearish factor]" — cite the specific metric -2. "[Bearish factor] vs [Bullish factor]" — cite the specific metric -...etc. - -This goes at the top of the report, right after the Company Overview — it gives the reader the bull/bear framing before they dive into the data. - -## 7. News Snapshot -Run 2 WebSearch queries to gather recent context: -1. `"{TICKER} {company_name} news {current_year}"` — recent headlines -2. `"{TICKER} catalysts risks {current_year}"` — forward-looking events - -Distill into **3-5 key events** from the last 6 months, reverse chronological. Each event: date, one-line headline, sentiment tag (Positive / Negative / Mixed / Upcoming). Keep it tight — this is a tearsheet, not a research note. - -## 8. What to Watch -Build a **Quantitative Monitors** list — 5 metrics with explicit thresholds: -- Format: "Metric: current value → bull threshold / bear threshold" -- Example: "Gross Margin: 45.2% → above 46% confirms pricing power / below 43% signals cost pressure" - -Choose the 5 metrics that matter most for THIS company's thesis based on the data you pulled above. These should be actionable — an analyst should be able to check these next quarter and know whether the thesis is intact. - -## 9. Save Report -Save to `reports/{TICKER}_tearsheet.html` using the HTML report template from `../design-system.md`. Write the full analysis as styled HTML with the design system CSS inlined. This is the final deliverable — no intermediate markdown step needed. - -Structure the report with these sections: - -``` -

{Company Name} ({TICKER}) — Tearsheet

-

Generated: {date}

- -

Company Overview

-{2-3 sentence description from filings} - -

Five Key Tensions

-{numbered list of 5 bull/bear debates with data citations} - -

Key Financials (Last 4 Quarters)

- -| Metric | Q(oldest) | Q | Q | Q(latest) | -{table with Daloopa citations; derived metrics marked (calc.)} -
- -

Segment / Geographic Breakdown

-{segment revenue table or geographic revenue table, whichever is more relevant} - -

Key Operating KPIs

- -| KPI | Q(oldest) | Q | Q | Q(latest) | -{table with Daloopa citations — ONLY business-driver metrics, NOT financial items} -{if few KPIs available, note the disclosure gap} -
- -

Capital Return

- -| Metric | Q(oldest) | Q | Q | Q(latest) | -{share count, buybacks, dividends — separate from operating KPIs} -
- -

Margins & Growth

- -| Metric | Q(oldest) | Q | Q | Q(latest) | -| Gross Margin % | X% | X% | X% | X% | -| ... | ... | ... | ... | ... | -| Rev Growth YoY | X% | X% | X% | X% | -| EPS Growth YoY | X% | X% | X% | X% | -{each cell shows the YoY change for THAT quarter} -{note on seasonality if applicable} -
- -

Recent Developments

-
    {bullet points from filings with document citations}
- -

News Snapshot

-{3-5 recent events with date, headline, sentiment tag} - -

What to Watch

-{5 quantitative monitors with current value and bull/bear thresholds} -``` - -All financial figures must use Daloopa citation format: `$X.XX million` - -Tell the user where the HTML report was saved. - -Give a 2-3 sentence summary of the company's current state, including an honest assessment: What is the single biggest risk or concern? Does the current valuation (price, implied multiples) seem warranted given the growth trajectory? What would make you cautious about owning this stock? diff --git a/plugins/daloopa/skills/tearsheet/agents/openai.yaml b/plugins/daloopa/skills/tearsheet/agents/openai.yaml deleted file mode 100644 index 7840b36ba..000000000 --- a/plugins/daloopa/skills/tearsheet/agents/openai.yaml +++ /dev/null @@ -1,6 +0,0 @@ -interface: - display_name: Tearsheet - short_description: Quick one-page company overview and snapshot - default_prompt: Create a quick company tearsheet for MSFT. -policy: - allow_implicit_invocation: true diff --git a/plugins/daloopa/skills/unit-economics/SKILL.md b/plugins/daloopa/skills/unit-economics/SKILL.md deleted file mode 100644 index a8053c45c..000000000 --- a/plugins/daloopa/skills/unit-economics/SKILL.md +++ /dev/null @@ -1,202 +0,0 @@ ---- -name: unit-economics -description: Bottoms-up unit economics decomposition for any public company ---- - -Perform a bottoms-up unit economics decomposition for the company named in the user's request. If no ticker or company is provided, ask for one before proceeding. - -**Before starting, read `../data-access.md` for data access methods and `../design-system.md` for formatting conventions.** Follow the data access detection logic and design system throughout this skill. - -Follow these steps: - -## 1. Company Lookup -Look up the company by ticker using `discover_companies`. Capture: -- `company_id` -- `latest_calendar_quarter` — anchor for all period calculations below (see `../data-access.md` Section 1.5) -- `latest_fiscal_quarter` -- Firm name for report attribution (default: "Daloopa") — see `../data-access.md` Section 4.5 - -## 2. Series Discovery & Business Archetype Detection -Cast a wide net to discover ALL available series for this company. Search with multiple keyword sets to maximize coverage: -- Financial: "revenue", "income", "profit", "margin", "eps", "cost" -- Operating KPIs: "subscriber", "user", "customer", "unit", "arpu", "retention", "churn" -- Segment/Product: "segment", "product", "service", "geographic" -- Business-specific: "store", "gmv", "order", "booking", "backlog", "premium", "loan", "aum", "room", "seat", "bed", "acreage" - -Collect all unique series IDs. Read every series name and description returned. **This is how you learn what kind of business this is and what unit-level KPIs Daloopa tracks for it.** - -Based on series availability, classify the business into one of these archetypes (or a hybrid). This classification drives the entire report structure: - -| If you find series like... | Archetype | Unit = | -|---|---|---| -| ARR, MRR, net dollar retention, customers, ACV, churn, CAC, LTV | **SaaS / Subscription** | Customer or subscription | -| Store count, same-store sales, AUV, restaurant-level margin, new openings | **Unit-based retail / Restaurant** | Store or unit | -| GMV, take rate, orders, AOV, active buyers/sellers | **Marketplace / E-commerce** | Order or transaction | -| Subscribers, ARPU, churn, content spend per sub | **Consumer subscription (media/streaming)** | Subscriber | -| Premiums written, loss ratio, combined ratio, policies in force | **Insurance** | Policy | -| NIM, loans, deposits, provision for credit losses, NCOs | **Banking / Lending** | Loan or account | -| ASP, units shipped, cost per unit, gross margin per unit | **Hardware / Manufacturing** | Unit shipped | -| AUM, management fee rate, performance fees, fund flows | **Asset Management** | Dollar of AUM | -| Revenue per available room (RevPAR), occupancy, ADR | **Hospitality / Lodging** | Room night | -| RPM, RASM, CASM, load factor, ASMs | **Airlines / Transportation** | Available seat mile | -| Revenue per user, DAU, MAU, ARPU, engagement | **Digital platform / Advertising** | User | -| Beds, admissions, revenue per admission, case mix | **Healthcare facilities** | Admission or bed | -| Acreage, production per acre, realized price per unit | **Commodity / E&P** | Unit of production | - -If the business is a hybrid or doesn't fit neatly, construct a custom framework from the available series. The archetype is a starting guide, not a constraint. - -**Edge cases:** -- **Diversified / multi-segment companies**: Pick the largest or most analytically interesting segment for primary analysis. Note other segments briefly. If the user specifies a segment, focus there. -- **Pre-revenue / early-stage companies**: Focus on burn rate per unit of growth, cash efficiency, and path to unit profitability. -- **Financial companies (banks, insurance, asset managers)**: These have specialized unit economics. For banks, the "unit" is a dollar of assets — focus on NIM, fee income/assets, efficiency ratio, credit costs. For insurance, focus on the combined ratio decomposition. Don't force a SaaS or retail framework onto financials. -- **Companies with no obvious unit-level KPIs in Daloopa**: Fall back to a margin bridge / operating leverage analysis using standard income statement data. Decompose revenue into whatever sub-components are available (segment, geography, product line) and analyze profitability at that level. Note the limitation. -- **Companies that stopped disclosing unit data**: Some major companies (e.g., Apple post-2018) no longer report unit shipments or ASPs. If unit-level data is not available, adapt to the highest-resolution decomposition the data supports (e.g., segment revenue × segment margin). Clearly flag the data gap and explain what proxy you used. Do not fabricate unit estimates. - -## 3. Unit Economics Data Pull -Calculate 10 quarters backward from `latest_calendar_quarter`. Pull all archetype-relevant series identified in Step 2 for those periods, plus standard financials: -- Revenue (total and segment) -- COGS / cost of revenue -- Gross profit -- Operating income -- Net income -- All operating KPIs relevant to the detected archetype - -**Derived metrics** (calculate from pulled data, label each as "(calc.)" and show formulas): -- Revenue per unit (Revenue / units) -- Gross margin per unit -- Contribution margin per unit (if variable costs are available) -- Unit growth rate (QoQ and YoY) -- Revenue per unit growth rate (QoQ and YoY) -- Any archetype-specific derived metrics (e.g., CAC payback = CAC / (ARPU × gross margin), LTV/CAC, 4-wall margin, take rate, combined ratio) - -## 4. Qualitative Research -Search SEC filings for context on the unit economics. Use archetype-specific search terms: -- **SaaS**: Try "net dollar retention", "customer acquisition cost"; fallback to "expansion", "churn", "upsell" -- **Restaurant/Retail**: Try "average unit volume", "restaurant-level margin"; fallback to "same-store", "new unit", "unit opening" -- **Marketplace**: Try "take rate", "gross merchandise value"; fallback to "active buyers", "order volume", "monetization" -- **Hardware/Manufacturing**: Try "average selling price", "units shipped"; fallback to "ASP", "volume", "mix" -- **Insurance**: Try "combined ratio", "loss ratio"; fallback to "underwriting", "premium", "policy" -- **Banking**: Try "net interest margin", "provision"; fallback to "loan growth", "credit quality", "efficiency" -- **Digital platform**: Try "average revenue per user", "monthly active users"; fallback to "engagement", "monetization", "ARPU" -- **General (all archetypes)**: Try "unit economics", "pricing"; fallback to "profitability", "margin", "per unit" - -Extract management commentary on pricing, retention, expansion, new unit openings, margin levers, etc. with document citations. - -## 5. Analysis & Report Synthesis - -**Section 1: Business Model & Unit Definition (brief)** -- 2-3 sentence description of what the "unit" is for this business -- Why this decomposition matters for understanding the company's economics -- What the revenue build-up looks like: units × revenue-per-unit, or equivalent - -**Section 2: Revenue Decomposition** -- Show the bottoms-up revenue build: how units × price/rate × utilization (or equivalent) bridges to reported revenue -- Table: quarterly history (10 quarters) showing each component -- Highlight which lever is driving growth: volume vs. price vs. mix -- Include growth rates (YoY) as sub-rows beneath each metric - -**Section 3: Unit-Level Profitability** -The core of the report. Show margin/profitability at the unit level over time: -- For SaaS: gross margin per customer, CAC payback period, LTV/CAC ratio -- For restaurants: 4-wall EBITDA margin, new unit payback, cash-on-cash return -- For marketplace: contribution margin per order, after accounting for fulfillment/transaction costs -- For insurance: loss ratio + expense ratio = combined ratio per policy -- For hardware: gross margin per unit, cost per unit breakdown -- Adapt to whatever the business actually is -- Table: historical trend with period-over-period change -- Explicitly call out whether unit economics are improving or deteriorating and by how much - -**Section 4: Cohort / Vintage Analysis (if data supports it)** -- For subscription businesses: net retention curves, expansion vs. contraction -- For unit-based businesses: same-store vs. new-store contribution, unit maturation -- For lending: vintage loss curves, seasoning -- If insufficient data for true cohort analysis, note this and substitute with proxy analysis (e.g., new customer growth rate vs. retention rate implies cohort behavior) - -**Section 5: Scalability & Operating Leverage** -- How do unit economics change as the business scales? -- Fixed cost absorption: which costs are truly fixed vs. variable per unit? -- Show operating leverage by plotting revenue growth vs. cost growth -- Incremental margins: are they expanding or compressing as the business grows? - -**Section 6: Key Drivers & What to Watch** -This is the most analytically valuable section. Based on the data, identify: -- **The 3-5 metrics that matter most** for this company's unit economics, ranked by sensitivity / impact -- For each metric: current level, historical range, direction of travel, and what would cause it to inflect -- **Bull case drivers**: what would improve unit economics (e.g., pricing power, mix shift to higher-margin products, operating leverage kicking in, retention improving) -- **Bear case risks**: what would deteriorate unit economics (e.g., competitive pricing pressure, rising CAC, input cost inflation, regulatory impact on take rates) -- Connect each driver to its P&L impact: "a 100bps improvement in net retention would add ~$X to ARR" or "each new store generates ~$Xm in 4-wall EBITDA in year 2" - -**Section 7: Summary Assessment** -- 3-4 sentence verdict on the health and trajectory of the company's unit economics -- Is this a business with improving, stable, or deteriorating unit economics? -- What is the single most important thing to monitor going forward? - -**Analytical standards:** -- **Three-layer density**: every data point should have context (vs. prior period, vs. peers if known) and an implication (what it means for the investment case) -- **Show your math**: when you derive a metric (e.g., implied CAC = S&M expense / new customers added), show the calculation explicitly so the reader can verify -- **Flag data gaps**: if a key metric for the archetype isn't available in Daloopa's data, say so explicitly and explain what proxy you used or why the analysis is limited -- **No generic filler**: if you don't have data to support a section, skip it or shorten it. Never pad with boilerplate -- **Source everything**: every number should be traceable. Use Daloopa source citations per the design system conventions -- **Prefer rates and ratios over absolutes**: unit economics are about efficiency, not scale. Lead with margins, returns, and per-unit metrics. Include absolutes as context - -## 6. Charts -Use `infra/chart_generator.py` for charts. Include at minimum: -1. A **revenue decomposition chart** (waterfall or time-series showing units × price → revenue) -2. A **unit profitability trend chart** (time-series showing the key unit margin metric over time) -3. Additional charts as warranted by the archetype (e.g., net retention waterfall for SaaS, same-store sales trend for restaurants, take rate trend for marketplaces) - -**All charts must be embedded in the HTML as base64 data URIs** (e.g., ``) so the report is fully self-contained with no external file dependencies. After generating each chart PNG, read the file and convert to base64 for embedding. Do not use relative `` paths. - -If chart_generator.py is unavailable, embed simple inline SVG charts directly in the HTML. - -## 7. Save Report -Save to `reports/{TICKER}_unit_economics.html` using the HTML report template from `../design-system.md`. Write the full analysis as styled HTML with the design system CSS inlined. This is the final deliverable — no intermediate markdown step needed. - -Structure the report with these sections: - -``` -

{Company Name} ({TICKER}) — Unit Economics Analysis

-

Generated: {date}

- -

Summary

-{2-3 sentences: What is the "unit"? Are unit economics improving or deteriorating? Key takeaway.} - -

Business Model & Unit Definition

-{Section 1 content} - -

Revenue Decomposition

- -| Component | Q(-9) | Q(-8) | ... | Q(latest) | -{Units, revenue per unit, revenue — with Daloopa citations and YoY growth sub-rows} -
-{Commentary on volume vs. price drivers} - -

Unit-Level Profitability

- -| Metric | Q(-9) | Q(-8) | ... | Q(latest) | -{Archetype-specific unit margins — with Daloopa citations} -
-{Commentary on unit economics trajectory} - -

Cohort / Vintage Analysis

-{Section 4 content, or note if insufficient data} - -

Scalability & Operating Leverage

- -| Metric | Q(-9) | Q(-8) | ... | Q(latest) | -{Revenue growth vs cost growth, incremental margins} -
-{Operating leverage assessment} - -

Key Drivers & What to Watch

-{Ranked drivers with sensitivity analysis and bull/bear scenarios} - -

Summary Assessment

-{3-4 sentence verdict} -``` - -All financial figures must use Daloopa citation format: `$X.XX million` - -Tell the user where the HTML report was saved. - -Highlight the 2-3 most important findings about the company's unit economics and what they signal for the investment case. diff --git a/plugins/daloopa/skills/unit-economics/agents/openai.yaml b/plugins/daloopa/skills/unit-economics/agents/openai.yaml deleted file mode 100644 index 6a585d289..000000000 --- a/plugins/daloopa/skills/unit-economics/agents/openai.yaml +++ /dev/null @@ -1,6 +0,0 @@ -interface: - display_name: Unit Economics - short_description: Bottoms-up unit economics decomposition for any public company - default_prompt: Analyze unit economics for NFLX. -policy: - allow_implicit_invocation: true diff --git a/plugins/daloopa/skills/working-capital/SKILL.md b/plugins/daloopa/skills/working-capital/SKILL.md deleted file mode 100644 index e7c5d46b7..000000000 --- a/plugins/daloopa/skills/working-capital/SKILL.md +++ /dev/null @@ -1,298 +0,0 @@ ---- -name: working-capital -description: Cash conversion cycle, earnings quality, and working capital deep-dive ---- - -Perform a cash conversion cycle, earnings quality, and working capital deep-dive for the company named in the user's request. If no ticker or company is provided, ask for one before proceeding. - -**Before starting, read `../data-access.md` for data access methods and `../design-system.md` for formatting conventions.** Follow the data access detection logic and design system throughout this skill. - -Follow these steps: - -## 1. Company Lookup -Look up the company by ticker using `discover_companies`. Capture: -- `company_id` -- `latest_calendar_quarter` — anchor for all period calculations below (see `../data-access.md` Section 1.5) -- `latest_fiscal_quarter` -- Firm name for report attribution (default: "Daloopa") — see `../data-access.md` Section 4.5 - -## 2. Series Discovery & Working Capital Profile Detection -Cast a wide net to discover ALL available series for this company. Search with multiple keyword sets to maximize coverage: -- Balance sheet: "receivable", "inventory", "payable", "deferred revenue", "contract", "prepaid", "accrued", "current asset", "current liabilit" -- Cash flow: "cash flow from operations", "working capital", "depreciation", "amortization", "stock-based comp", "capital expenditure", "free cash flow" -- Income statement: "revenue", "cost of goods", "cost of revenue", "operating income", "net income" -- Business-specific: "backlog", "billing", "remaining performance obligation", "provision", "allowance", "reserve", "unearned premium", "loss ratio" - -Collect all unique series IDs. Read every series name carefully. You need to understand: -- What balance sheet line items are available (receivables, inventory, payables, deferred revenue, contract assets/liabilities, prepaid expenses, accrued liabilities, etc.) -- What cash flow statement detail exists (CFO, changes in working capital components, capex, stock-based comp, D&A, etc.) -- What business-specific KPIs exist that contextualize working capital (e.g., backlog for industrials, deferred revenue for SaaS, policy reserves for insurance, loan loss provisions for banks) - -Based on series availability, classify the business's working capital profile: - -| If you find series like... | Profile | Primary focus | -|---|---|---| -| Inventory, COGS, accounts payable, accounts receivable | **Inventory-intensive (manufacturing, retail, consumer goods)** | Full CCC decomposition: DIO + DSO − DPO. Inventory is the core risk. Watch for inventory-to-sales divergence, channel stuffing signals, obsolescence risk | -| Deferred revenue, contract liabilities, billings, remaining performance obligations | **Negative working capital / SaaS / subscription** | Deferred revenue is the key asset — cash collected before revenue recognized. Focus on billings vs. revenue spread, deferred revenue growth vs. revenue growth, and whether the cash-first dynamic is strengthening or weakening | -| Receivables and payables but minimal/no inventory | **Asset-light services (consulting, staffing, advertising, tech services)** | DSO is the main event. Receivables quality, aging, concentration. Unbilled receivables or contract assets as early warning. DPO as a secondary lever | -| Loans, deposits, allowance for credit losses, provision expense, net charge-offs | **Financial institutions (banks, specialty finance)** | Traditional CCC is meaningless. Focus on provision adequacy (allowance/loans, provision/NCOs, reserve coverage), deposit cost and mix, and the gap between provision expense and actual cash losses realized as the earnings quality signal | -| Policy reserves, loss reserves, unearned premiums, LAE | **Insurance** | Reserve adequacy is the working capital equivalent. Prior-year reserve development (favorable/adverse), loss ratio trends, reserve-to-premium ratios. Cash flow from underwriting vs. reported underwriting income | -| Contract assets, unbilled receivables, costs to obtain contracts, progress billings | **Long-cycle / contract-based (construction, defense, engineering)** | Percentage-of-completion dynamics. Overbilling vs. underbilling, contract asset growth vs. revenue, cash collection timing on milestones. Watch for aggressive revenue recognition through under-reserved contract losses | -| Deferred commissions, capitalized content/software, prepaid expenses dominate | **High-intangible / platform** | "Hidden" working capital in capitalized costs. Focus on capitalization rate vs. amortization, whether capitalizing faster than amortizing (building a balloon), and the cash flow impact of these non-traditional working capital items | - -If the company is a hybrid or doesn't map cleanly, construct a blended framework. The profile is a guide, not a constraint. - -**Edge cases:** -- **SaaS / negative working capital businesses**: Traditional CCC is misleading or meaningless. The entire framework should pivot to deferred revenue dynamics, billings analysis, and RPO trends. Working capital is a source of cash, not a use — frame accordingly. The earnings quality analysis still applies (accruals ratio, CFO/NI) but interpret directionally opposite: declining deferred revenue growth is the red flag here, not rising receivables. -- **Financial institutions**: Skip CCC entirely. The balance sheet IS the product. Focus the entire report on credit quality and reserve adequacy: allowance for loan losses / total loans, provision expense / net charge-offs (the "reserve build" or "reserve release"), vintage analysis if available, and the gap between provision expense booked through earnings and actual cash losses realized. -- **Insurance companies**: Skip CCC. Focus on loss reserve adequacy and development. Prior-year reserve development (favorable = prior reserves were adequate or over-reserved; adverse = prior reserves were insufficient). Show the trend. Connect reserve movements to reported combined ratio and operating income. -- **Pre-revenue / early-stage companies**: Focus on burn rate and cash runway. Working capital analysis still applies but framed as "how much cash is being consumed by the operating cycle" rather than earnings quality (since there are no meaningful earnings). -- **Conglomerates / multi-segment**: Use consolidated working capital data but flag if segment mix makes the consolidated CCC misleading. Note which segments are likely driving the consolidated working capital dynamics. -- **Seasonal businesses**: Explicitly normalize for seasonality by comparing each quarter to the same quarter prior year, not the prior sequential quarter. Call out the seasonal pattern so the reader doesn't mistake a normal seasonal build for a structural deterioration. -- **Companies with large non-cash charges**: SBC, amortization of intangibles, and impairments can distort the CFO/NI ratio. When calculating earnings quality metrics, provide both the unadjusted and adjusted (ex-SBC, ex-amortization) versions and explain which is more informative for this specific business. - -## 3. Working Capital Data Pull -Calculate 10 quarters backward from `latest_calendar_quarter`. Pull all working capital components identified in Step 2 for those periods: - -**Balance sheet** (all working capital components): -- Accounts receivable -- Inventory (total, and breakdown into raw materials / WIP / finished goods if available) -- Accounts payable -- Deferred revenue / contract liabilities -- Contract assets / unbilled receivables -- Prepaid expenses -- Accrued liabilities / other current liabilities -- Total current assets, total current liabilities - -**Income statement:** -- Revenue -- COGS / cost of revenue (if applicable) -- Operating income -- Net income - -**Cash flow statement:** -- Cash flow from operations (CFO) -- Changes in each working capital component (if available as separate line items) -- Depreciation & amortization -- Stock-based compensation -- Capital expenditures -- Free cash flow (compute as CFO - CapEx if not available directly, label "(calc.)") - -**Important: YTD-to-quarterly conversion.** Some companies (e.g., Apple) report cash flow items on a fiscal year-to-date basis rather than quarterly. Check whether CF data appears to be YTD (values increasing monotonically through the fiscal year, then resetting). If so, convert to quarterly values by subtracting the prior quarter's YTD figure. Note this conversion in the report. - -**Business-specific KPIs** as identified in Step 2. - -**Derived metrics** (calculate from pulled data, label each as "(calc.)" and show formulas): -- DSO = (Accounts Receivable / Revenue) × days-in-period -- DIO = (Inventory / COGS) × days-in-period (if inventory-intensive) -- DPO = (Accounts Payable / COGS) × days-in-period -- CCC = DSO + DIO − DPO (if applicable) -- Accrual ratio = (Net Income − CFO) / Average Total Assets -- Cash conversion ratio = CFO / Net Income -- Working capital intensity = ΔNet Working Capital / ΔRevenue -- Profile-specific derived metrics (e.g., deferred revenue days for SaaS, allowance/loans for banks, reserve-to-premium for insurance) - -## 4. Qualitative Research -Search SEC filings for context on working capital dynamics. Use profile-specific search terms: -- **Inventory-intensive**: Try "inventory reserves", "inventory write-down"; fallback to "excess and obsolete", "channel inventory", "sell-through" -- **SaaS/subscription**: Try "remaining performance obligations", "deferred revenue"; fallback to "billings", "contract liabilities", "revenue recognition" -- **Services**: Try "unbilled receivables", "days sales outstanding"; fallback to "allowance for doubtful accounts", "contract assets" -- **Financials**: Try "allowance for credit losses", "provision"; fallback to "net charge-offs", "reserve adequacy", "CECL" -- **General (all profiles)**: Try "accounts receivable", "accounts payable"; fallback to "working capital", "cash conversion", "liquidity" - -Extract management commentary on working capital trends, collection issues, inventory management, supplier terms, etc. with document citations. - -## 5. Analysis & Report Synthesis - -**Section 1: Working Capital Profile (brief)** -- 2-3 sentences identifying the business's working capital archetype and why it matters -- What is the "unit of working capital risk" for this business? (inventory for a manufacturer, receivables for a services firm, deferred revenue for SaaS, reserves for insurance) -- One sentence on the headline finding: is working capital a source of strength, a neutral factor, or a red flag for this company right now? - -**Section 2: Cash Conversion Cycle (or profile-adapted equivalent)** -For inventory-intensive businesses, show the full CCC decomposition: -- DSO, DIO, DPO, CCC — quarterly history (10 quarters) -- Include YoY change as sub-rows -- Highlight any quarter where a component moved more than 5 days (or equivalent threshold) — this is a flag - -For non-inventory businesses, adapt the framework: -- SaaS/subscription: Show "Days Deferred Revenue Outstanding" (deferred revenue / revenue × days), billings-to-revenue ratio, and net working capital as % of revenue -- Services: DSO decomposition (billed vs. unbilled), DPO, net working capital days -- Financials: Skip CCC entirely — use provision/NCO coverage, allowance/loans, deposit mix -- Insurance: Skip CCC — use reserve development, combined ratio decomposition, cash flow from underwriting vs. reported income - -**Section 3: Earnings Quality Assessment** -This is the section that makes the report valuable for a short-seller or skeptical long. Three sub-analyses: - -*3a. Accruals Analysis* -- Calculate the Sloan accrual ratio: (Net Income − CFO) / Average Total Assets - - Persistent high positive accruals = low earnings quality = earnings running ahead of cash - - Negative accruals (CFO > Net Income) = high earnings quality -- Show the quarterly trend. Is the accrual ratio rising (deteriorating) or falling (improving)? -- Decompose the accrual: which specific working capital line items are driving the gap between earnings and cash flow? Is it receivables growing faster than revenue? Inventory building? Payables shrinking? Deferred revenue decelerating? - -*3b. Cash Conversion Ratio* -- CFO / Net Income, quarterly and trailing-twelve-month -- A healthy business should convert >80% of net income to CFO over time (adjust by business model — SaaS may be >100% due to deferred revenue; capex-heavy businesses need FCF/NI instead) -- Flag any quarter where conversion drops below 60% and explain why - -*3c. Revenue-to-Receivables Divergence* -- Show revenue growth vs. receivables growth on the same basis (YoY) -- When receivables growth persistently exceeds revenue growth, it's a classic red flag: the company may be extending payment terms to pull forward sales, booking revenue on deteriorating credits, or facing collection issues -- Calculate the divergence spread (receivables growth − revenue growth) and show whether it's widening or narrowing - -**Section 4: Working Capital Intensity & Growth Drag** -How much incremental working capital does this business consume for each dollar of revenue growth? -- Calculate: ΔNet Working Capital / ΔRevenue for each period (the "working capital intensity ratio"). **Use YoY changes (not sequential QoQ)** for this ratio to avoid seasonal noise — QoQ denominators can flip sign due to seasonality, making the ratio meaningless. If computing on a TTM rolling basis, show that instead. -- Show the trend: is the business becoming more or less capital-efficient as it scales? -- Quantify the FCF impact: "In the last four quarters, working capital consumed $Xm of cash, reducing FCF by X% vs. what it would have been at stable working capital" -- For high-growth companies, this is critical: a business growing 30% with 15% working capital intensity is funding growth very differently than one growing 30% with 2% intensity -- Contextualize with capex intensity: total investment requirement = capex + working capital investment. Show both as % of revenue - -**Section 5: Component Deep-Dives** -For each material working capital component (the 2-3 that matter most for this business type), provide a focused analysis: - -*Structure for each component:* -- Current level (absolute and as days/% of revenue) -- Historical trend (10 quarters) -- Rate of change: is it improving or deteriorating, and is the rate of change itself accelerating? -- Context: why might this be happening? Reference management commentary from filings if available -- Benchmark: where does this sit vs. the company's own history? (Don't fabricate peer comps — only include if data supports it) - -*Which components to deep-dive depends on the profile:* -- Inventory-intensive: Inventory (breakdown by raw/WIP/finished if available), Receivables, Payables -- SaaS: Deferred Revenue, Contract Assets, Deferred Commissions (capitalized contract costs) -- Services: Receivables (billed + unbilled), Accrued Liabilities -- Financials: Loan Loss Allowance, Provision Expense, Net Charge-Offs -- Insurance: Loss Reserves, Unearned Premiums, Prior-Year Development - -**Section 6: Red Flags & Green Flags** -Explicit, concise checklist format. Scan the data for each of the following and report findings: - -*Red flags (earnings quality / liquidity concerns):* -- DSO increasing while revenue growth is slowing (demand deterioration masked by term extensions) -- Inventory growing faster than COGS or revenue (demand softening, potential write-down ahead) -- DPO declining (suppliers tightening terms — potential credit deterioration signal) -- Accrual ratio rising above +5% (earnings quality deteriorating) -- CFO/Net Income < 0.6x for two or more consecutive quarters -- Receivables growth > revenue growth for 3+ consecutive quarters -- Deferred revenue growth decelerating faster than revenue growth (pipeline weakening for subscription businesses) -- Unbilled receivables / contract assets growing rapidly (aggressive percentage-of-completion or ASC 606 recognition) -- Capitalized costs (software, commissions, content) growing faster than associated revenue (building an amortization balloon) -- Working capital intensity ratio rising (growth becoming more capital-consumptive) -- Allowance/loans declining while loan growth accelerates (under-reserving for banks) - -*Green flags (strong cash generation / conservative accounting):* -- DSO declining or stable while revenue grows (pricing power, healthy demand) -- CCC shortening over time (operational improvement) -- CFO/Net Income persistently > 1.0x (earnings over-earned in cash) -- Negative net working capital (float-funded business model — customers pay before you deliver) -- Deferred revenue growing faster than revenue (strong forward visibility) -- Accrual ratio consistently negative (cash earnings exceed reported earnings) -- Allowance/loans stable or rising modestly while credit metrics are benign (conservative reserving) - -For each flag triggered, include the specific data that triggered it and the implication. Use Daloopa citations for every figure. - -**Section 7: Key Drivers & What to Watch** -The forward-looking, analytically highest-value section: -- **The 3-5 working capital metrics that matter most** for this specific company, ranked by sensitivity to the investment thesis -- For each: current level, direction, historical range, and what would cause an inflection -- **Scenario analysis**: "If DSO increases another 5 days from here, the company would need an additional ~$Xm in working capital, reducing FCF by ~X%" — quantify the P&L/cash flow impact of plausible working capital scenarios -- **What to watch next quarter**: specific items to monitor in the next earnings release or 10-Q filing. Be concrete: "Watch the inventory line relative to the revenue guide — if inventory grows >X% sequentially while revenue is guided flat, it would be the third consecutive quarter of inventory build and a meaningful negative signal" - -**Section 8: Summary Assessment** -- 3-4 sentence verdict on working capital health and earnings quality -- Is this a cash-generative business with conservative accounting, or is there a gap between reported earnings and economic reality? -- What is the single biggest risk (or source of comfort) in the working capital profile? - -**Analytical standards:** -- **Three-layer density**: every metric should have a data point, context (vs. history, vs. expectations), and an implication (so what?) -- **Show your math**: all derived metrics (DSO, DIO, DPO, CCC, accruals ratio, cash conversion ratio, working capital intensity) should show the formula and inputs used, so the reader can verify or adjust -- **Use quarterly data, but show TTM where appropriate**: some metrics (like the accruals ratio) are more meaningful on a trailing-twelve-month basis to smooth seasonality. Show both quarterly and TTM when relevant -- **Flag seasonality**: many businesses have seasonal working capital patterns (retail builds inventory in Q3 for Q4, etc.). Compare YoY, not just QoQ, for directional conclusions. Note when a move is seasonal vs. structural -- **Distinguish levels from changes**: a company can have structurally high DSO (that's the business model) but stable DSO (not a red flag). The concern is when DSO is rising, not when it's high in absolute terms. Always emphasize the direction and rate of change, not just the level -- **No false precision**: working capital metrics calculated from quarterly balance sheets are point-in-time snapshots. Acknowledge this limitation. If average balances are available, use them; if not, use period-end and note the limitation -- **Source everything**: every number traceable to Daloopa. Use citations per the design system conventions -- **Flag data gaps**: if a key balance sheet component isn't broken out in Daloopa's data (e.g., inventory broken into raw/WIP/finished), say so and explain what that limits - -## 6. Charts -Use `infra/chart_generator.py` for charts. Include at minimum: -1. **CCC trend chart** (time-series or waterfall showing DSO + DIO − DPO = CCC by quarter, or the equivalent decomposition for non-inventory businesses) -2. **Earnings quality chart** (time-series showing net income vs. CFO over time, with the accrual gap visible) -3. **Revenue vs. receivables growth chart** (two lines on the same axis showing YoY growth rates, with divergence highlighted) -4. Additional charts as warranted by the profile (e.g., inventory/COGS ratio for manufacturers, deferred revenue waterfall for SaaS, reserve adequacy trend for financials) - -**All charts must be embedded in the HTML as base64 data URIs** (e.g., ``) so the report is fully self-contained with no external file dependencies. After generating each chart PNG, read the file and convert to base64 for embedding. Do not use relative `` paths. - -If chart_generator.py is unavailable, embed simple inline SVG charts directly in the HTML. - -## 7. Save Report -Save to `reports/{TICKER}_working_capital.html` using the HTML report template from `../design-system.md`. Write the full analysis as styled HTML with the design system CSS inlined. This is the final deliverable — no intermediate markdown step needed. - -Structure the report with these sections: - -``` -

{Company Name} ({TICKER}) — Working Capital & Earnings Quality Analysis

-

Generated: {date}

- -

Summary

-{2-3 sentences: What is the working capital profile? Headline finding on earnings quality. Key risk or comfort.} - -

Working Capital Profile

-{Section 1 content} - -

Cash Conversion Cycle

- -| Metric | Q(-9) | Q(-8) | ... | Q(latest) | -{DSO, DIO, DPO, CCC (or profile equivalent) — with Daloopa citations and YoY change sub-rows} -
-{Commentary on CCC trend and any flagged moves} - -

Earnings Quality Assessment

- -

Accruals Analysis

- -| Metric | Q(-9) | Q(-8) | ... | Q(latest) | -{Net Income, CFO, Accrual Ratio — with Daloopa citations} -
-{Decomposition of the accrual and trend analysis} - -

Cash Conversion Ratio

- -| Metric | Q(-9) | Q(-8) | ... | Q(latest) | -{CFO, Net Income, CFO/NI ratio, TTM CFO/NI — with Daloopa citations} -
-{Assessment of cash conversion quality} - -

Revenue vs. Receivables Divergence

- -| Metric | Q(-9) | Q(-8) | ... | Q(latest) | -{Revenue YoY growth, Receivables YoY growth, Divergence spread} -
-{Analysis of divergence trend} - -

Working Capital Intensity & Growth Drag

- -| Metric | Q(-9) | Q(-8) | ... | Q(latest) | -{ΔNet WC, ΔRevenue, WC Intensity Ratio, CapEx % Rev, Total Investment % Rev} -
-{FCF impact quantification and growth drag assessment} - -

Component Deep-Dives

-{2-3 focused deep-dives on the most material components for this business type} - -

Red Flags & Green Flags

-{Checklist format with specific data citations for each triggered flag} - -

Key Drivers & What to Watch

-{Ranked drivers with scenario analysis and next-quarter monitoring items} - -

Summary Assessment

-{3-4 sentence verdict} -``` - -All financial figures must use Daloopa citation format: `$X.XX million` - -Tell the user where the HTML report was saved. - -Highlight the key findings: Is this a cash-generative business? Are there earnings quality concerns? What should an analyst focus on in the next filing? diff --git a/plugins/daloopa/skills/working-capital/agents/openai.yaml b/plugins/daloopa/skills/working-capital/agents/openai.yaml deleted file mode 100644 index 9baaf8e8f..000000000 --- a/plugins/daloopa/skills/working-capital/agents/openai.yaml +++ /dev/null @@ -1,7 +0,0 @@ -interface: - display_name: Working Capital - short_description: Cash conversion cycle, earnings quality, and working capital - deep-dive - default_prompt: Analyze AAPL working capital and cash conversion. -policy: - allow_implicit_invocation: true diff --git a/plugins/datadog/.app.json b/plugins/datadog/.app.json index 5602d6be6..0dbadea75 100644 --- a/plugins/datadog/.app.json +++ b/plugins/datadog/.app.json @@ -1,8 +1,7 @@ { "apps": { "datadog": { - "id": "asdk_app_69e8c7f174a08191a28b6da96c8062c4", - "required": false + "id": "asdk_app_69e8c7f174a08191a28b6da96c8062c4" } } -} +} \ No newline at end of file diff --git a/plugins/datadog/.codex-plugin/plugin.json b/plugins/datadog/.codex-plugin/plugin.json index 0ee792292..9a5762f13 100644 --- a/plugins/datadog/.codex-plugin/plugin.json +++ b/plugins/datadog/.codex-plugin/plugin.json @@ -1,34 +1,26 @@ { - "name": "datadog", - "version": "0.1.2", - "description": "Investigate Datadog telemetry and workflows from Codex", + "apps": "./.app.json", "author": { - "name": "Datadog", - "url": "https://www.datadoghq.com/" + "name": "Datadog" }, - "homepage": "https://www.datadoghq.com/", - "repository": "https://github.com/openai/plugins", - "license": "Apache-2.0", - "keywords": [], - "apps": "./.app.json", + "description": "Available for US1 customers only. Analyze, investigate, and act on your Datadog telemetry directly from ChatGPT using natural language. Ask questions about your production applications, identify, visualize, and remediate issues in your critical services. Run agentic loops to ensure you continue to maintain good observability and service management posture.", "interface": { - "displayName": "Datadog (Preview)", - "shortDescription": "Investigate logs, metrics, traces, and incidents", - "longDescription": "Analyze, investigate, and act on your Datadog telemetry directly from Codex using natural language. Ask questions about your production applications, identify, visualize, and remediate issues in your critical services. Run agentic loops to ensure you continue to maintain good observability and service management posture.", - "developerName": "Datadog", + "capabilities": [], "category": "Developer Tools", - "capabilities": ["Interactive", "Writes"], - "websiteURL": "https://www.datadoghq.com/", - "privacyPolicyURL": "https://www.datadoghq.com/legal/privacy/", - "termsOfServiceURL": "https://www.datadoghq.com/legal/terms/", "defaultPrompt": [ - "What are the top errors in my services? Include links and widgets.", - "What monitors are alerting right now for my team's services?", - "How does the p99 latency for services with the most traffic compare to what we usually see? Provide evidence" + "Show me a breakdown of log volume by service.", + "Show me average CPU usage for my top 10 services for the past 6 hours.", + "Where are my users coming from? Show me a chart by country." ], - "brandColor": "#632CA6", - "composerIcon": "./assets/app-icon.png", - "logo": "./assets/app-icon.png", - "screenshots": [] - } -} + "developerName": "Datadog", + "displayName": "Datadog (Preview)", + "longDescription": "Available for US1 customers only. Analyze, investigate, and act on your Datadog telemetry directly from ChatGPT using natural language. Ask questions about your production applications, identify, visualize, and remediate issues in your critical services. Run agentic loops to ensure you continue to maintain good observability and service management posture.", + "privacyPolicyURL": "https://www.datadoghq.com/legal/privacy/", + "shortDescription": "Search and act on your data", + "supportURL": "https://www.datadoghq.com/support/", + "termsOfServiceURL": "https://www.datadoghq.com/legal/terms/2024-10-25/", + "websiteURL": "https://www.datadoghq.com" + }, + "name": "datadog", + "version": "10.0.0" +} \ No newline at end of file diff --git a/plugins/datadog/assets/app-icon.png b/plugins/datadog/assets/app-icon.png deleted file mode 100644 index 89e0a9ce5..000000000 Binary files a/plugins/datadog/assets/app-icon.png and /dev/null differ diff --git a/plugins/datadog/assets/logo.png b/plugins/datadog/assets/logo.png deleted file mode 100644 index 150bdfca4..000000000 Binary files a/plugins/datadog/assets/logo.png and /dev/null differ diff --git a/plugins/datasite/.app.json b/plugins/datasite/.app.json deleted file mode 100644 index c124ddcae..000000000 --- a/plugins/datasite/.app.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "apps": { - "datasite": { - "id": "asdk_app_69eba17551ac81918231c83822b703b6" - } - } -} diff --git a/plugins/datasite/.codex-plugin/plugin.json b/plugins/datasite/.codex-plugin/plugin.json deleted file mode 100644 index 470d77f5f..000000000 --- a/plugins/datasite/.codex-plugin/plugin.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "datasite", - "version": "1.0.3", - "description": "Manage secure M&A data rooms", - "author": { - "name": "Datasite", - "url": "https://www.datasite.com" - }, - "repository": "https://github.com/openai/plugins", - "license": "MIT", - "keywords": [], - "skills": "./skills/", - "apps": "./.app.json", - "interface": { - "displayName": "Datasite", - "developerName": "Datasite", - "shortDescription": "Manage secure M&A data rooms", - "longDescription": "Connect ChatGPT to your Datasite virtual data room - the secure workspace where thousands of M&A deals are facilitated annually. Set up folder structures, invite users, search documents, track buyer Q&A, and audit data room readiness, all through natural language. No workflow interruptions. No security trade-offs. Built for advisors, bankers, and corporate development teams - backed by the enterprise security and permissioning every transaction demands.\n\n1. Set up a deal room - \"Create a new Datasite Prepare data room for Project Alpha, set up a standard M&A folder index, and invite sarah@advisorfirm.com as an admin.\"\n\n2. Search documents semantically - \"Search the data room for any documents related to pending litigation or regulatory risk.\"\n\n3. Track buyer Q&A - \"Show me all open Q&A questions in the data room and draft responses where we have supporting documents.\"\n\n4. Audit deal room readiness - \"Check the data room for empty folders, missing documents, and any files flagged as password-protected or blank before we go live.\"\n\n5. Manage user access - \"Create a buyer role with view-only permissions and create draft invitations for the following five contacts from Acme Capital.\"", - "category": "Productivity", - "capabilities": [], - "brandColor": "#F89820", - "defaultPrompt": [ - "Search Datasite for documents related to customer contracts and summarize key diligence issues.", - "Find recently uploaded files in this Datasite project and flag missing diligence items.", - "Summarize open Q&A items assigned to me in Datasite with owners and next steps." - ], - "screenshots": [], - "composerIcon": "./assets/logo.png", - "logo": "./assets/logo.png", - "websiteURL": "https://www.datasite.com" - }, - "homepage": "https://www.datasite.com" -} diff --git a/plugins/datasite/assets/app-icon.svg b/plugins/datasite/assets/app-icon.svg deleted file mode 100644 index 463814645..000000000 --- a/plugins/datasite/assets/app-icon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - DS - diff --git a/plugins/datasite/assets/logo.png b/plugins/datasite/assets/logo.png deleted file mode 100644 index 10b294f74..000000000 Binary files a/plugins/datasite/assets/logo.png and /dev/null differ diff --git a/plugins/datasite/skills/bulk-qa-answers/SKILL.md b/plugins/datasite/skills/bulk-qa-answers/SKILL.md deleted file mode 100644 index 07cc76d80..000000000 --- a/plugins/datasite/skills/bulk-qa-answers/SKILL.md +++ /dev/null @@ -1,252 +0,0 @@ ---- -name: bulk-qa-answers -description: > - Bulk Q&A Answers skill for Datasite deal rooms. Use this skill whenever a sell-side - deal team wants to answer multiple buyer questions at once, generate AI draft responses - from VDR content, produce a Q&A tracker spreadsheet, or build a Q&A management - dashboard. Triggers include: "answer the Q&A", "draft responses to buyer questions", - "process the question list", "generate Q&A tracker", "answer all questions", - "bulk answer", "Q&A management dashboard", "respond to diligence questions", - or any request to systematically work through a list of buyer questions using - data room content as the source. Use this skill proactively whenever a buyer - has submitted questions and the deal team wants AI-assisted drafting. - Do not use for individual one-off questions outside a structured Q&A process. - Do not draft answers from general knowledge — all responses must come from the data room. -metadata: - author: Blueflame AI - version: 1.0.0 - mcp-server: datasite - category: deal-management - tags: [datasite, vdr, m&a, q-and-a, due-diligence, blueflame] ---- - -# Bulk Q&A Answers - -You are helping a sell-side deal team draft answers to buyer due diligence questions by reading and interpreting Datasite data room content. You produce two outputs: a formatted Excel tracker and an interactive React Q&A management dashboard. - ---- - -## Terminology — fileroom vs. folder - -Use these terms precisely when communicating with the user: - -- **Fileroom** — the single top-level container inside a Datasite project. A project typically has one buyer-facing fileroom. It is not a subject area — it is the container that holds all subject areas. -- **Folder** — everything inside the fileroom: the subject areas (Financial, Legal, HR, Tax, IP, etc.) and all sub-levels beneath them. Always call these folders, never filerooms. - -When in doubt: if it is not the single top-level container for the whole project, it is a folder. - - -## Feature Requirements - -| Capability | Free | Requires Blueflame | -|---|:---:|:---:| -| Q&A status overview and health metrics | ✅ | — | -| Draft answers to buyer questions from document content | — | ✅ | -| Source citations with document name, path, and page number | — | ✅ | -| Excel tracker and dashboard | ✅ | — | - -**Without Blueflame:** The skill can retrieve the Q&A status overview and display question counts and categories. It cannot draft answers — all questions will be marked Open. The core value of this skill requires Blueflame. - -**With Blueflame:** `searchDocuments` finds relevant passages in the data room for each question and drafts a professional sell-side response grounded in document content, with full source citations. - - - -> ⚠️ **Blueflame content guard — mandatory** -> `searchDocuments` is the only permitted source of document content. -> - **Never draft Q&A answers from Claude's general knowledge.** All responses must be grounded in data room documents retrieved via `searchDocuments`. A fabricated answer is worse than no answer. -> - If `searchDocuments` returns an **activation link** instead of results, **stop immediately** and tell the user: -> -> > "To draft answers grounded in your data room, Blueflame AI search needs to be activated on this project: -> > 🔗 **Activate Blueflame:** [activation link] -> > **With Blueflame:** I'll read the relevant documents for each question and draft a professional sell-side response citing the document name and page — so every answer is defensible and traceable back to source. Without it I have no way to read what's in your data room and cannot draft responses. -> > Please activate Blueflame and then re-run." -> -> - Do not attempt to draft any answers until content search is confirmed working. -> - All Q&A answers **must** be sourced exclusively from tool results. - -## Step 1 — Load the questions - -The user will provide a spreadsheet of questions. Read it and extract for each row: -- Question text -- Buyer group / individual who asked it (the "Question From") -- Any existing status, category, or section grouping already in the file -- Any prior answer already provided (skip these unless the user asks to re-draft) - -If any column mappings are unclear, ask the user to confirm before proceeding. - ---- - -## Step 2 — Understand the deal context - -Call `getProjectOverview` to confirm the project name, sector, and fileroom structure. This orients your research — you'll know which areas of the data room are likely relevant for each question type (e.g. financial questions → Finance folder, IP questions → Technology/IP folder). - ---- - -## Step 3 — Research and draft each answer - -For each unanswered question, use the following research workflow. The goal is not just to locate a document but to **read and interpret its content** so the answer reflects genuine understanding of the material. - -### 3a — Semantic search first (primary) - -Run `searchDocuments` with the question (or a distilled version of it) as the query. Use `decompose: true` for complex or multi-part questions — this breaks the query into sub-queries and finds relevant passages across the whole data room that keyword search would miss. - -`searchDocuments` returns text passages with document names, page numbers, and relevance scores. Read the passages — they are actual document content, not just file names. Use them to understand what the data room says on the topic. - -### 3b — Keyword search for specifics (secondary) - -After the semantic search, run `searchDocuments` for any specific terms, figures, or exact phrases that the question calls for — e.g. a specific contract name, a company name, a regulation, a year, a metric. Keyword search complements semantic search for precise lookups. - -### 3c — Browse to the relevant folder if needed - -If the search results point to a specific section of the data room but you need to confirm what documents are present (e.g. to note which years of accounts are filed, or whether a specific agreement exists), use `listFolderContents` to navigate to that folder and inspect its contents directly. - -### 3d — Synthesise and draft the answer - -With the passages and document context in hand, write a clear, factual response. The standard to aim for: - -- **Directly answers** what was asked — not a broader essay on the topic -- **Grounded in the documents** — reflects what the data room actually says, not general knowledge -- **Sell-side voice** — professional, concise, confident. Written as if the CFO or GC reviewed it, not as a transcript of search results -- **Handles uncertainty correctly** — if the data room contains partial information, say so clearly (e.g. "Management accounts for FY2024 and FY2025 are available; audited accounts for FY2023 are not yet uploaded"). Never fill gaps with assumptions. -- **Sensitive matters** — if a question touches on active litigation strategy, unpublished projections, or personal employee data, flag it for legal review rather than drafting a response - -### 3e — Assign a status - -- **Complete** — question fully answered with clear source material -- **Partial** — answer drafted but source material is incomplete or only partially responsive -- **Open** — insufficient source material found; needs manual input from the deal team - -### 3f — Build the source reference and citation - -For every answer, record two things: - -**Source Reference** (brief, for the tracker): the VDR folder path and document name — e.g. `3.1 Audited Accounts / FY2024 Annual Report` or `5.3 Customer Contracts / MSA with Acme Corp` - -**Document Citation** (detailed, for verification): the full citation including document name, VDR index path, and page number(s) where the relevant content was found — e.g. `FY2024 Annual Report (VDR 3.1), p.14 — Revenue recognition policy` or `Employment Agreement — J. Smith (VDR 7.2.4), p.3 — Clause 8, Non-compete`. If multiple documents were used, list each on a separate line. - -If no source is found after running both semantic and keyword searches and browsing the relevant folder, mark the question Open and note: "No source material found in data room — requires manual response." - ---- - -## Step 4 — Group questions by theme - -Before producing outputs, group questions into thematic sections. Common M&A Q&A groupings: -- Financial Performance & Accounting -- Tax -- Legal & Regulatory -- Commercial & Customers -- Human Resources & Management -- Intellectual Property & Technology -- Operations -- ESG & Environmental -- Other / Miscellaneous - -Use the question content (and any category column already in the input file) to assign each question to a section. - ---- - -## Step 5 — Offer outputs - -Before generating the Excel tracker and dashboard, ask: - -> "I've drafted answers for all [N] questions. What would you like me to produce? -> - **Excel tracker** — formatted spreadsheet with all questions, answers, statuses, and source citations -> - **Q&A management dashboard** — interactive React dashboard for active deal management (uses additional credits) -> - **Both** -> - **Neither** — just show me the answers in this conversation" - -Only generate the Excel tracker and/or dashboard if the user explicitly requests them. - -## Step 5b — Produce the Excel tracker (only if requested) - -Use the xlsx skill to produce a formatted `.xlsx` file saved to the outputs folder. - -**Columns (in order):** -1. **Diligence Question** — the original question text verbatim -2. **Diligence Response** — the AI-drafted answer -3. **Status** — Complete / Partial / Open -4. **Question From** — buyer group or individual name -5. **Source Reference** — VDR folder path and document name (brief) -6. **Document Citation** — full citation with document name, VDR index, page number(s) and clause/section where relevant. Multiple sources listed on separate lines within the cell. - -**Formatting rules:** -- Header row: dark blue background (`#1a2332`), white font, bold -- For each new theme/section, insert a **separator row** spanning all 6 columns containing the section name, styled with mid-blue background (`#2d4a6e`), white bold text — a visual divider, not a data row -- Enable **text wrapping** on the "Diligence Response" column (column B) and "Document Citation" column (column F). Set column widths: B ~60 chars, F ~50 chars -- Status cell colour coding: Complete = light green fill, Partial = light amber fill, Open = light red fill -- Freeze the header row - -Save as `[ProjectName]_QA_Tracker_[Date].xlsx` in the outputs folder. - ---- - -## Step 6 — Produce the React Q&A management dashboard (only if requested) - -Read `references/dashboard-spec.md` for the full React component specification before building. - -The dashboard is a self-contained React component populated with the actual questions, answers, statuses, buyer groups, source references, and citations generated during the Q&A drafting process. It is for active deal management — it should feel live and usable, not like a static report. - -Key sections to implement (details in the reference file): -1. **Summary KPI Bar** — four stat cards (Total, Open, Awaiting Review, Submitted) -2. **Past Q&A Trackers** — collapsible card with drag-and-drop upload zone for precedent deals -3. **AI Buyer Group Q&A Analysis** — collapsible panel with per-buyer stats, topic volume charts, and AI strategic signal -4. **Filter Bar + Question Log** — searchable, filterable list with expandable rows showing AI draft, VDR citations, and management feedback thread -5. **Dashboard Modal** — full KPI and analytics view with time-savings metrics - -Use navy `#1a2332` / gold `#d4a017` colour palette with Source Sans 3 font. All state via `useState` — no backend required. - ---- - -## Step 7 — Deliver to the user - -Present both outputs: -1. Link to the Excel tracker file -2. The React dashboard artifact rendered in the conversation - -Then say: -> "I've drafted answers to [N] questions — [X] Complete, [Y] Partial, [Z] Open. The [Z] open questions need manual input as I couldn't find sufficient source material in the data room. Both the Excel tracker and the live dashboard are ready above." - -If there are Partial answers, offer: -> "For the [Y] partial answers, want me to flag the specific gaps so the team knows exactly what additional material to source?" - ---- - -## Operating principles - -**Read the documents, don't just locate them.** `searchDocuments` returns actual text passages — use them. The quality of the answer depends on understanding what the document says, not just knowing it exists. - -**Source everything.** Every drafted answer must have a citation. Unsourced answers should be marked Open. Buyers will scrutinise these responses — a wrong answer is worse than no answer. - -**Write in the seller's voice.** Concise, factual, professional. Not a summary of search results. - -**Don't over-answer.** Answer the specific question asked. Buyers will follow up for more. - -**Flag patterns.** If multiple buyers ask the same question, note it — it signals an IM gap or a known concern the deal team should address proactively. - -**Respect sensitivity.** Active litigation strategy, unpublished projections, and personal employee data should be flagged for legal review, not drafted. - -## Performance Notes - -- **Quality over speed.** A wrong answer is worse than no answer — buyers will scrutinise every response. -- Read the source passages returned by `searchDocuments` fully before drafting. Do not skim. -- Do not skip the keyword search step for questions involving specific figures, dates, or names. -- Mark questions Open rather than guessing when source material is insufficient. - ---- - -## Common Issues - -**`getProjectOverview` fails or returns the wrong project** -Check that the Datasite MCP connector is connected (Settings → Extensions → Datasite should show "Connected"). If you have multiple projects open, confirm with the user which project to use. - -**`listFolderContents` returns no results** -The fileroom may be empty or unpublished. Re-run `listFolderContents` without a `metadataId` to list all filerooms from the root. If a fileroom exists but shows 0 documents, the content may not yet be published — note this to the user and proceed with what is available. - -**`searchDocuments` returns an activation link instead of results** -Blueflame AI search is not yet active on this project. Follow the Blueflame prompt in the skill instructions above. Do not attempt to answer using Claude's training knowledge. - -**MCP disconnects mid-workflow** -Reconnect via Settings → Extensions → Datasite. Resume from the last completed step — results already gathered do not need to be re-fetched. - -**`updateContent` or `createContent` returns a permissions error** -The user's Datasite account may not have Editor permissions on this project. Ask them to check their role in Datasite project settings. diff --git a/plugins/datasite/skills/bulk-qa-answers/agents/openai.yaml b/plugins/datasite/skills/bulk-qa-answers/agents/openai.yaml deleted file mode 100644 index b06c5eb89..000000000 --- a/plugins/datasite/skills/bulk-qa-answers/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Bulk Q&A Answers" - short_description: "Draft buyer Q&A answers grounded in Datasite room content." - default_prompt: "Use $bulk-qa-answers to draft buyer Q&A answers from Datasite documents." diff --git a/plugins/datasite/skills/document-quality-check/SKILL.md b/plugins/datasite/skills/document-quality-check/SKILL.md deleted file mode 100644 index 683d0d7da..000000000 --- a/plugins/datasite/skills/document-quality-check/SKILL.md +++ /dev/null @@ -1,477 +0,0 @@ ---- -name: document-quality-check -description: > - Document Quality Check skill for Datasite deal rooms. Use this skill whenever a - deal team wants to audit document quality before going live to buyers. Triggers - include: "check document quality", "flag bad documents", "find password protected - files", "check for blank documents", "PII check", "redaction review", "find - corrupted files", "document audit", "quality check the data room", "are there - any blank or broken files", "check for unredacted personal data", or any request - to verify that documents in the data room are complete, accessible, and safe to - share. Use this skill proactively before a data room goes live. - Do not use for renaming files (use smart-file-renaming) or for identifying - missing sections (use gap-analysis). -metadata: - author: Blueflame AI - version: 1.0.0 - mcp-server: datasite - category: deal-management - tags: [datasite, vdr, m&a, document-quality, pii, redaction, blueflame] ---- - -# Document Quality Check - -You are helping a deal team verify that every document in their Datasite data room is fit to share with buyers before going live. You check for six categories of quality issues and produce an HTML dashboard with a downloadable Excel report. - ---- - -## Terminology — fileroom vs. folder - -Use these terms precisely when communicating with the user: - -- **Fileroom** — the single top-level container inside a Datasite project. A project typically has one buyer-facing fileroom. It is not a subject area — it is the container that holds all subject areas. -- **Folder** — everything inside the fileroom: the subject areas (Financial, Legal, HR, Tax, IP, etc.) and all sub-levels beneath them. Always call these folders, never filerooms. - -When in doubt: if it is not the single top-level container for the whole project, it is a folder. - - -## Feature Requirements - -| Capability | Free | Requires Blueflame | -|---|:---:|:---:| -| Failed / unprocessable files (status metadata) | ✅ | — | -| Placeholder and stub documents (type metadata) | ✅ | — | -| Wrong file formats (fileType metadata) | ✅ | — | -| Uninformative filenames (name pattern matching) | ✅ | — | -| Version conflicts (name pattern matching) | ✅ | — | -| Duplicate documents (name + metadata comparison) | ✅ | — | -| Stale documents (upload date metadata) | ✅ | — | -| PII exposure in document content | — | ✅ | -| Redaction quality check | — | ✅ | -| Broken references and missing exhibits | — | ✅ | - -**Without Blueflame:** 7 of 10 checks run fully using `listFolderContents` metadata. The three content-level checks (PII, redaction quality, broken references) are skipped — note these in the report as "Requires Blueflame." - -**With Blueflame:** All 10 checks run. `searchDocuments` scans document content for PII patterns, verifies redaction quality, and finds broken cross-references inside documents. - - - -> ⚠️ **Blueflame fallback — two-tier behaviour** -> `searchDocuments` is the only permitted source of document content. -> - Do **not** infer document content, PII presence, or redaction quality from Claude’s training knowledge. -> - **Phase A** (Checks 1–7, metadata checks) uses `listFolderContents` only — always free. Complete Phase A fully first. -> - **Phase B** (Checks 8–10: PII scan, redaction quality, broken references) requires `searchDocuments`. When you reach Phase B, attempt one call. If it returns an **activation link** instead of results: -> 1. **Do not generate the HTML dashboard yet** — ask the Blueflame question first as a plain conversational message -> 2. Summarise Phase A findings in plain text (e.g. "I found X password-protected files, Y duplicates, Z files with no extension") -> 3. Then ask: -> -> > "I’ve completed the 7 metadata checks — here’s what I found: [plain text summary]. To also run PII scanning, redaction quality checks, and broken reference detection, Blueflame AI search needs to be activated on this project: -> > 🔗 **Activate Blueflame:** [activation link] -> > **With Blueflame:** I’ll scan document content for exposed personal data (names, NI numbers, bank details), verify that redacted text can’t be read in the file layer, and check for broken cross-references inside documents — the checks buyers and their lawyers look for most. -> > Would you like to activate now, or shall I produce the dashboard with the Phase A findings only?" -> -> 4. **Wait for the user’s response before producing any dashboard or output file.** -> -> Do not embed the Blueflame activation prompt inside the HTML dashboard — it must appear as an interactive conversational question before any output is generated. - -> **`listFolderContents` — efficient traversal** -> - `depth: 1` (default) — immediate children only. Use for targeted lookups. -> - `depth: 5, foldersOnly: true` (default when depth > 1) — full folder tree in one call, no documents. Use for structural checks. -> - `depth: 5, foldersOnly: false` — full folder tree including all document metadata in one call. Use when building a document inventory. -> - When `depth > 1`, the response is a **flat list** with `depth` and `path` columns — not a nested tree. - -## Step 1 — Orient yourself - -Call `getProjectOverview` to understand the project structure and get a list of all filerooms. You'll work through each fileroom systematically. - ---- - -## Step 2 — Run all quality checks - -Work through each check below in two phases: - -**Phase A — Metadata checks (use `listFolderContents`):** -Call `listFolderContents` without a metadataId to get all filerooms, then recurse into each folder to build a complete document inventory. Each document entry includes: name, type (DOCUMENT/PLACEHOLDER/FOLDER/FILE_ROOM/SANDBOX), status (DONE/FAILED/PROCESSING), fileType (pdf/docx/xlsx etc.), publishingState, and upload date. Use this single inventory pass to run all metadata-based checks — do not make a separate call per check. - -From the inventory, flag: -- `status: FAILED` or `PROCESSING` → Check 1 (unprocessable) -- `type: PLACEHOLDER` → Check 6 (placeholder/stub) -- `fileType` in [xlsm, xlsb, zip, rar, msg, eml, pages, numbers, key, dwg] → Check 9 (wrong format) -- Name patterns: Scan/IMG/Document/Untitled/Copy of/FINAL_FINAL/USE THIS/DO NOT USE → Check 12 (bad filenames) -- Name patterns: v1/v2/revised/updated/superseded/old/archive → Check 8 (version conflicts) -- Identical names in the same folder → Check 7 (duplicates) -- Upload date > 12 months ago in active sections (Management Accounts, Insurance, Licences) → Check 10 (stale) - -**Phase B — Content checks (use `searchDocuments`):** -Use `searchDocuments` for checks requiring reading inside documents (PII, redaction quality, broken references). Always call `searchDocuments` — if AI search is not yet activated the tool returns an activation link; present it to the user rather than skipping the check. - ---- - -### Check 1 — Failed / unprocessable documents -*Catches: password-protected files, corrupted files, files that couldn't be indexed* - -``` -listFolderContents(projectId, query="*", filter=["status:EQ:FAILED", "type:EQ:DOCUMENT"]) -listFolderContents(projectId, query="*", filter=["status:EQ:PROCESSING", "type:EQ:DOCUMENT"]) -``` - -`FAILED` = Datasite could not process the file — most commonly because it is password-protected or corrupted. `PROCESSING` documents that have been in that state for more than a few minutes are likely stuck (possible corruption or unsupported format). - -For each result note: filename, VDR folder path, file size, extension. - -**Severity:** High — buyers cannot open these documents. - ---- - -### Check 2 — Blank or near-blank documents -*Catches: accidentally uploaded blank pages, empty documents, placeholder files* - -``` -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "pageCount:LT:2", "fileSize:LT:50000"]) -``` - -A document with fewer than 2 pages AND under 50KB is almost certainly blank or a single near-empty page. Cross-reference against the folder context — a 1-page certificate of incorporation is fine; a 1-page "FY2024 Audited Accounts" is not. - -Also flag zero-byte files: -``` -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "fileSize:LT:1000"]) -``` - -For each result, check the filename and folder path to judge whether the low page count is expected. Flag only where it looks wrong for the document type. - -**Severity:** High (if it's a material document), Medium (if it's a supporting file). - ---- - -### Check 3 — Suspicious redaction quality (poorly blacklined documents) -*Catches: documents with redactions that may be incomplete or incorrectly applied* - -``` -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "redacted:EQ:true"]) -``` - -This returns all documents Datasite has flagged as containing redactions. For each one, use `searchDocuments` to check whether sensitive content that should have been redacted is still readable — e.g. if a document is flagged as redacted but the underlying text was not properly removed (a common issue with image-based PDFs where black boxes are overlaid on text that remains in the file layer). - -Search queries to run on redacted documents: -- `searchDocuments` with query "salary" or "compensation" — check for unredacted pay figures -- `searchDocuments` with query "date of birth" or "national insurance" — check for unredacted personal identifiers -- `searchDocuments` with query "account number" or "IBAN" — check for unredacted banking details - -Flag any redacted document where searchable text appears beneath the redaction, or where the expected content is still visible in snippets. - -Also flag documents where the filename suggests redaction was needed (e.g. "Employment Agreements", "Payroll", "Personal Data") but `redacted:EQ:false` — these may have been shared without any redaction applied. - -**Severity:** High — unredacted personal or sensitive data in a buyer-facing data room is a GDPR/privacy breach. - ---- - -### Check 4 — PII exposed without redaction -*Catches: personal data visible in documents that haven't been redacted at all* - -Run the following `searchDocuments` queries across the full data room. Each targets a specific PII category. Read the snippets returned and flag any document where personal data is clearly visible. - -**Personal identifiers:** -- `"date of birth"` or `"DOB"` or `"born on"` — personal birth dates -- `"passport number"` or `"passport no"` — passport identifiers -- `"national insurance"` or `"NI number"` or `"social security"` or `"SSN"` — government ID numbers -- `"home address"` or `"residential address"` — personal addresses (distinguish from business addresses) -- `"driving licence"` or `"driver's license number"` — licence identifiers - -**Financial details:** -- `"sort code"` and `"account number"` — personal bank account details -- `"IBAN"` — international bank account numbers -- `"salary"` with a named individual — personal salary data linked to a person's name -- `"payslip"` or `"pay stub"` — payroll documents that typically contain personal financial data - -**Contact data:** -- Search for personal email domain patterns: `"@gmail.com"` or `"@yahoo.com"` or `"@hotmail.com"` or `"@icloud.com"` — personal email addresses (business emails like @companyname.com are expected and fine) -- `"mobile"` or `"personal phone"` alongside a person's name — personal phone numbers - -For each snippet returned, assess whether it appears in a context that warrants redaction (e.g. an employee's salary in a payroll schedule = High risk; a reference to "date of birth required for background check" in an HR policy = Low risk). - -**Severity:** High for direct identifiers (passport, NI/SSN, bank account); Medium for contact data and salary where it's incidental. - ---- - -### Check 5 — Suspiciously small or potentially incomplete scanned documents -*Catches: multi-page documents where pages may be missing* - -For scanned documents (PDFs from physical paper), page count alone can reveal gaps. Use: -``` -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "extension:EQ:pdf"], sort=["pageCount,ASC"]) -``` - -Cross-reference page count against what's expected based on document type and filename: -- A "Lease Agreement" with 2 pages is suspicious — commercial leases are typically 20–100 pages -- An "Employment Agreement" with 1 page is suspicious — these typically run 5–30 pages -- An "Audited Financial Statements" document with 3 pages is suspicious — audited accounts are typically 30–100+ pages -- A "Certificate of Incorporation" with 1–2 pages is fine - -Flag documents where the page count appears materially below what the document type would normally require. Note the filename, VDR path, current page count, and the expected range. - -**Severity:** Medium — missing pages may mean incomplete disclosure. High if it's a key legal or financial document. - ---- - -### Check 6 — Placeholder or stub documents -*Catches: files named as placeholders, zero-content uploads, "TBC" files* - -``` -listFolderContents(projectId, query="placeholder", filter=["type:EQ:DOCUMENT"]) -listFolderContents(projectId, query="TBC", filter=["type:EQ:DOCUMENT"]) -listFolderContents(projectId, query="draft", filter=["type:EQ:DOCUMENT"]) -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "name:LIKE:placeholder"]) -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "name:LIKE:TBC"]) -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "name:LIKE:WIP"]) -``` - -Also check for documents with generic names that suggest they haven't been properly named or are still in progress: "Document1", "Untitled", "Copy of", "v1", "DRAFT", "temp". - -**Severity:** Medium — placeholder documents signal incomplete preparation; buyers will notice. - ---- - -### Check 7 — Duplicate documents -*Catches: exact or near-duplicate files that inflate apparent completeness and expose version inconsistencies* - -``` -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT"], sort=["fileSize,ASC"]) -``` - -Group results by file size. Where two or more documents share the same `fileSize`, compare their filenames. Exact file size match + near-identical filename = likely duplicate. Also flag same `pageCount` + same folder path with minor filename variation (e.g. `Agreement_v1.pdf` and `Agreement_final.pdf` in the same folder). - -For suspected duplicates in high-risk areas (financial statements, contracts), run `searchDocuments` on both documents to compare leading paragraphs — if content is near-identical, flag as a confirmed duplicate. - -Highest risk: duplicate financial statements or contracts where versions may differ in a key figure or clause. - -**Severity:** High (if material documents like contracts or financials are duplicated with differing content), Medium (identical duplicates — one just needs removing). - ---- - -### Check 8 — Version conflicts and superseded documents -*Catches: old or draft versions left in the room alongside current ones, which buyers may read and draw incorrect conclusions from* - -``` -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "name:LIKE:v1"]) -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "name:LIKE:revised"]) -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "name:LIKE:updated"]) -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "name:LIKE:superseded"]) -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "name:LIKE:previous"]) -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "name:LIKE:archive"]) -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "name:LIKE:old"]) -``` - -Also search for: `"v2"`, `"final"`, `"draft"` in filenames. Where multiple versions exist in the same folder, flag all but the most recently modified (`sort: availableDate,DESC`) as potentially superseded. - -Cross-reference `availableDate` against document content date where visible — a file uploaded in 2026 but containing a 2023 date header warrants flagging. - -**Severity:** High (if two versions of a contract or financial statement coexist with potentially different terms or figures), Medium (clear drafts or superseded copies that are obviously not current). - ---- - -### Check 9 — Wrong file format or rendering risk -*Catches: files that buyers cannot open in-browser, macro-enabled files (security risk), and archive files that block search indexing* - -``` -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "extension:EQ:msg"]) -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "extension:EQ:eml"]) -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "extension:EQ:xlsm"]) -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "extension:EQ:xlsb"]) -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "extension:EQ:zip"]) -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "extension:EQ:rar"]) -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "extension:EQ:dwg"]) -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "extension:EQ:pages"]) -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "extension:EQ:numbers"]) -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "extension:EQ:key"]) -``` - -Flag each by issue type: -- `.xlsm` / `.xlsb` → macro-enabled Excel — security risk for buyers, may be blocked by corporate IT; recommend saving as `.xlsx` -- `.zip` / `.rar` → archive files — content invisible to VDR search indexing, buyers cannot open in-browser; recommend unpacking and uploading individual files -- `.msg` / `.eml` → email files — rarely intentional, likely contain unintended PII or privileged content; recommend converting to PDF -- `.dwg` / `.dxf` → CAD files — buyers without AutoCAD cannot open; recommend PDF export -- `.pages` / `.numbers` / `.key` → Apple-native formats — Windows users (most buyers) cannot open; recommend PDF or Office format - -**Severity:** High for `.msg`/`.eml` (PII/privilege risk) and `.zip` (invisible to search), Medium for rendering-incompatible formats. - ---- - -### Check 10 — Stale or outdated documents -*Catches: documents that appear current but haven't been updated in over a year, particularly in areas where buyers expect current data* - -``` -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "availableDate:LT:[12_months_ago_epoch]"], sort=["availableDate,ASC"]) -``` - -Calculate the epoch timestamp for 12 months ago from today's date and substitute it into the filter. From the results, focus on document types where staleness is a material risk: -- Management accounts — must be current; flagging anything older than 3 months -- Financial models and projections — flag if older than 6 months -- Employee lists and org charts — flag if older than 12 months -- Insurance schedules — flag if upload date is older than 12 months (policy may have expired) -- Regulatory licences and certificates — flag if older than 12 months (renewal may be overdue) -- Board minutes — flag if the most recent entry is older than 6 months - -Don't flag inherently historical documents (e.g. FY2022 audited accounts — they're supposed to be from 2022). - -**Severity:** High (management accounts, insurance, regulatory licences past renewal date), Medium (financial models, employee lists). - ---- - -### Check 11 — Broken references and missing linked content -*Catches: documents referencing exhibits, appendices, or schedules that were never uploaded — buyers encounter dead ends* - -Use `searchDocuments` with the following queries: - -- `"see attached"` or `"refer to appendix"` or `"as per schedule"` — cross-reference whether the referenced exhibit exists in the same folder -- `"exhibit"` or `"annex"` or `"schedule"` — check whether named attachments are present -- `"[TBC]"` or `"[insert"` or `"[link]"` or `"[see tab"` — internal authoring placeholders never resolved before upload -- `"see accompanying"` or `"as set out in"` or `"detailed in the attached"` — general cross-reference language - -For each match, check whether the referenced document is present in the same folder using `listFolderContents`. Flag where it is absent. - -For Excel financial models specifically: if a CIM or management presentation references a "detailed financial model" and the only Excel file in the folder has `fileSize:LT:100000`, it is likely a stub or broken-link version — flag for review. - -**Severity:** High (missing exhibit to a contract, missing appendix to audited accounts), Medium (unresolved placeholder text). - ---- - -### Check 12 — Uninformative or unprofessional filenames -*Catches: filenames that signal poor preparation and make navigation impossible for buyers — a direct reputational risk* - -``` -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "name:LIKE:Scan"]) -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "name:LIKE:Document"]) -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "name:LIKE:Copy of"]) -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "name:LIKE:FINAL_FINAL"]) -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "name:LIKE:USE THIS"]) -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "name:LIKE:DO NOT USE"]) -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "name:LIKE:Untitled"]) -listFolderContents(projectId, query="*", filter=["type:EQ:DOCUMENT", "name:LIKE:New "]) -``` - -Also flag: -- Files with sequential numbering in the name: match patterns like `001`, `002`, `(1)`, `(2)`, `(3)` -- Files with double extensions: `.pdf.pdf`, `.docx.pdf` — artefacts of bulk upload tools -- Any folder where more than 20% of filenames match these generic patterns — flag the entire folder for a renaming pass, not just individual files - -**Severity:** Medium across the board — these don't block access but signal poor preparation to buyers. Flag the folder-level pattern as more severe than individual files. - ---- - -## Step 3 — Compile findings - -Compile all findings into a structured list: - -``` -findings = [ - { - check: "Failed / Unprocessable", - severity: "High", - filename: "FY2024 Audited Accounts.pdf", - folder: "3.1 Audited Financial Statements", - detail: "Document status is FAILED — likely password-protected or corrupted. Buyers cannot open it.", - recommended_action: "Remove password protection or re-export as an unprotected PDF and re-upload." - }, - ... -] -``` - -Count issues by severity and check type for the dashboard scorecard. - ---- - -## Step 4 — Offer the dashboard - -Before generating anything, ask: - -> "I've completed the quality checks. Would you like me to produce the HTML dashboard with the full findings and an Excel export? It uses additional credits to render. Alternatively I can give you a plain text summary here." - -Only generate the dashboard if the user confirms. If they decline, go to Step 5 and deliver a plain text summary. - -## Step 4b — Produce the HTML dashboard (only if requested) - -Generate a self-contained HTML artifact. Include a **"Download as Excel"** button using SheetJS (`https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js`) that exports the findings table with columns: Check Type | Severity | Filename | VDR Folder | Detail | Recommended Action | Status (default: Open). - -**Dashboard structure:** - -**Header:** -- Deal name, date of audit, total issue counts by severity (High / Medium / Low) -- "Download as Excel" button (navy, top right) - -**Summary scorecard — six check tiles:** -One tile per check type, each showing: -- Check name and icon -- Issue count -- RAG status: Red (any High issue), Amber (Medium only), Green (no issues) - -Check tiles: -- 🔒 Failed / Unprocessable -- 📄 Blank / Near-blank -- ✂️ Redaction Quality -- 👤 PII Exposed -- 📑 Incomplete Scans -- 📝 Placeholders / Stubs -- 👯 Duplicate Documents -- 🔁 Version Conflicts -- ⚠️ Wrong Format / Rendering Risk -- 🕐 Stale / Outdated Documents -- 🔗 Broken References -- 🏷️ Uninformative Filenames - -**Findings table (below scorecard):** -- Filterable by check type and severity -- Columns: Severity badge | Check Type | Filename (with VDR folder path below in grey) | Issue Detail | Recommended Action -- Severity badges: High = red (`#ef4444`), Medium = amber (`#d97706`), Low = grey (`#6B7280`) -- Rows sorted High → Medium → Low within each check type - -**Design:** white background, navy (`#1a2332`) header, 12px border-radius cards, `Source Sans 3` font via Google Fonts, no external dependencies beyond SheetJS and fonts. - ---- - -## Step 5 — Deliver to the user - -Give a brief summary: - -> "I've checked [N] documents across [M] filerooms and found [X] High and [Y] Medium quality issues. The most urgent: [top 2–3 findings]. Use the Download button to export the full report as Excel for the team to action." - -Then offer: -> "Want me to flag which issues are quickest to fix vs. which need the document owner involved?" - ---- - -## Operating principles - -**Context matters for severity.** A 1-page PDF is fine for a certificate; it's a red flag for an audited accounts file. Always check the filename and folder path before flagging a low page count. - -**Don't cry wolf on PII.** A business email address in a contract is expected. A director's personal gmail address in a board minute is a flag. Read the snippet context before raising an issue. - -**Redaction quality is a GDPR risk, not just a tidiness issue.** Documents where text is visually blocked but remains machine-readable in the PDF layer are the most dangerous scenario — prioritise these. - -**Failed documents are the most urgent fix.** A buyer who clicks a document and gets an error immediately loses confidence in the deal team's preparation. Every failed document should be actioned before go-live. - -**Be specific in recommended actions.** "Remove password protection and re-upload" is useful. "Fix document" is not. - -## Performance Notes - -- **Do not skip checks to save time.** A missed password-protected file or undetected PII exposure is a serious issue that could delay go-live or create a compliance breach. -- Run all Phase A checks from a single `listFolderContents` pass — avoid repeated calls. -- Context matters before flagging: always check filename and folder path before raising a severity issue. - ---- - -## Common Issues - -**`getProjectOverview` fails or returns the wrong project** -Check that the Datasite MCP connector is connected (Settings → Extensions → Datasite should show "Connected"). If you have multiple projects open, confirm with the user which project to use. - -**`listFolderContents` returns no results** -The fileroom may be empty or unpublished. Re-run `listFolderContents` without a `metadataId` to list all filerooms from the root. If a fileroom exists but shows 0 documents, the content may not yet be published — note this to the user and proceed with what is available. - -**`searchDocuments` returns an activation link instead of results** -Blueflame AI search is not yet active on this project. Follow the Blueflame prompt in the skill instructions above. Do not attempt to answer using Claude's training knowledge. - -**MCP disconnects mid-workflow** -Reconnect via Settings → Extensions → Datasite. Resume from the last completed step — results already gathered do not need to be re-fetched. - -**`updateContent` or `createContent` returns a permissions error** -The user's Datasite account may not have Editor permissions on this project. Ask them to check their role in Datasite project settings. diff --git a/plugins/datasite/skills/document-quality-check/agents/openai.yaml b/plugins/datasite/skills/document-quality-check/agents/openai.yaml deleted file mode 100644 index 8450d3a75..000000000 --- a/plugins/datasite/skills/document-quality-check/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Document Quality Check" - short_description: "Audit Datasite room documents for quality and readiness issues." - default_prompt: "Use $document-quality-check to audit this Datasite room for document quality issues." diff --git a/plugins/datasite/skills/gap-analysis/SKILL.md b/plugins/datasite/skills/gap-analysis/SKILL.md deleted file mode 100644 index 93ad7741a..000000000 --- a/plugins/datasite/skills/gap-analysis/SKILL.md +++ /dev/null @@ -1,344 +0,0 @@ ---- -name: gap-analysis -description: > - Data Room Gap Analysis skill for Datasite deal rooms. Use this skill whenever a - sell-side deal team wants to audit what is missing, sparse, or incomplete in their - data room before going live to buyers. Triggers include: "run a gap analysis", - "what's missing from the data room", "check the data room coverage", "flag empty - folders", "what haven't we uploaded yet", "data room readiness check", "find gaps - before we go live", "are all the contracts in there", "check we have everything", - or any request to assess completeness of the data room by section. Use this skill - proactively whenever a deal team is preparing to launch a data room and wants to - know what still needs to be uploaded or organised. - Do not use for document quality issues such as PII or redaction (use document-quality-check), - or for drafting Q&A responses (use bulk-qa-answers). -metadata: - author: Blueflame AI - version: 1.0.0 - mcp-server: datasite - category: deal-management - tags: [datasite, vdr, m&a, gap-analysis, completeness, blueflame] ---- - -# Data Room Gap Analysis - -You are helping a sell-side deal team identify what is missing, incomplete, or sparse in their Datasite data room before buyers get access. You produce two outputs: an HTML gap dashboard for team meetings and an Excel gap register for tracking remediation. - ---- - -## Terminology — fileroom vs. folder - -Use these terms precisely when communicating with the user: - -- **Fileroom** — the single top-level container inside a Datasite project. A project typically has one buyer-facing fileroom. It is not a subject area — it is the container that holds all subject areas. -- **Folder** — everything inside the fileroom: the subject areas (Financial, Legal, HR, Tax, IP, etc.) and all sub-levels beneath them. Always call these folders, never filerooms. - -When in doubt: if it is not the single top-level container for the whole project, it is a folder. - - -## Feature Requirements - -| Capability | Free | Requires Blueflame | -|---|:---:|:---:| -| Structural gap analysis (missing/empty/sparse folders) | ✅ | — | -| Year completeness checks from filenames | ✅ | — | -| Contract cross-referencing (customer, employee, supplier lists) | — | ✅ | -| IRL matching (if provided) | — | ✅ | - -**Without Blueflame:** Produces a structural gap report — missing sections, empty folders, sparse time-series coverage — based on folder structure and filenames. Contract cross-referencing and IRL matching are skipped. - -**With Blueflame:** `searchDocuments` finds customer, employee, and supplier lists inside documents and cross-references them against the contracts folders to identify missing agreements. - - - -> ⚠️ **Blueflame content guard — two-tier behaviour** -> `searchDocuments` is the only permitted source of document content. -> - Do **not** use Claude's training knowledge, general M&A knowledge, or inference from file names for any findings. -> - **Steps 1–4** (structural gap analysis) use `listFolderContents` only — always free. Complete these regardless of Blueflame status. -> - **Step 5** (contract cross-referencing) requires `searchDocuments`. When you reach it, attempt one call. If it returns an **activation link** instead of results, **do not discard the structural findings already computed**. Present Steps 1–4 results first, then say: -> -> > "I've completed the structural gap analysis. Summary: [list top findings per section in plain text — e.g. 'Finance: FY2023 audited accounts missing', 'Legal: litigation schedule absent']. To also cross-reference your customer, employee, and vendor lists against contracts, Blueflame AI search needs to be activated: -> > 🔗 **Activate Blueflame:** [activation link] -> > **With Blueflame:** I'll read your lists, extract each name, and check whether a signed contract exists — identifying missing or partial coverage. -> > Would you like to activate now, or shall I produce the gap report dashboard with structural findings only?" -> -> **Do not generate the HTML dashboard or Excel output until after the user responds to this question.** -> - All content findings **must** be sourced exclusively from tool results. - -> **`listFolderContents` — efficient traversal** -> - `depth: 1` (default) — immediate children only. Use for targeted lookups. -> - `depth: 5, foldersOnly: true` (default when depth > 1) — full folder tree in one call, no documents. Use for structural checks. -> - `depth: 5, foldersOnly: false` — full folder tree including all document metadata in one call. Use when building a document inventory. -> - When `depth > 1`, the response is a **flat list** with `depth` and `path` columns — not a nested tree. - -## Step 1 — Orient yourself - -Call `getProjectOverview` to understand the deal: company name, sector, transaction type, deal size, and the fileroom structure. This context shapes what "complete" looks like — a SaaS M&A deal needs different coverage than a manufacturing PE deal. - -Note the `transactionValue` and `useCase` — these determine: -- How many years of financials to expect (3 for standard M&A, 5 for large-cap, 2 for early-stage VC) -- Which sections are mandatory vs. deal-specific -- How deep the expected folder coverage should be - ---- - -## Step 2 — Check for an Information Request List (IRL) - -Ask the user (briefly, in one line): "Do you have an Information Request List you'd like me to cross-reference? If so, share it and I'll flag what's been delivered vs. outstanding." - -If they provide one, read it and extract each requested item. Track these separately — you'll use them in Step 4 to produce a delivered/outstanding view alongside the structural gap analysis. - -If they don't have one, proceed with the structural analysis only. - ---- - -## Step 3 — Full structural crawl of the data room - -Use `listFolderContents` to walk the entire data room, top to bottom. For each fileroom and folder, note: - -- **Folder path** (the full VDR index and name, e.g. `3.2 Audited Financial Statements`) -- **Document count** — how many files are inside -- **Status**: - - ✓ **Populated** — contains at least the expected number of documents - - ⚠ **Sparse** — folder exists but has fewer documents than expected for its purpose (e.g. a "Board Minutes" folder with only 1 document when 3 years of minutes are expected) - - ✗ **Empty** — folder exists but contains no documents at all - - ✗ **Missing** — an expected section is entirely absent from the data room structure - -Use `listFolderContents` to drill into specific folders where you need a precise document count or list of filenames. - -### What counts as "sparse" - -Apply judgment based on what the folder is for: -- **Audited financials** — expect one document per financial year in scope. Two files where three years are expected = Sparse. -- **Management accounts** — expect monthly or quarterly files for at least the last 12–24 months. A single file = Sparse. -- **Tax returns** — expect one return per jurisdiction per year. Missing a year or jurisdiction = Sparse/Missing. -- **Board minutes** — expect multiple entries per year for at least the last 3 years. One document = Sparse. -- **Contracts folders** — see Step 4 for cross-referencing logic. -- **Single-document folders** (e.g. "Certificate of Incorporation") — one document is fine. - -### Expected sections by deal type - -Compare the actual data room structure against what a deal of this type should contain. Flag any top-level sections that are entirely absent: - -**Always expected (any M&A/PE deal):** -- General Information / Corporate (org charts, articles, board minutes, cap table) -- Finance (audited accounts, management accounts, financial model) -- Tax (filed returns, correspondence) -- Legal (litigation schedule, material contracts) -- HR / Employment (employee list, key employment agreements) -- IP (ownership documentation, if relevant to the business) - -**Expected based on sector:** -- Technology / SaaS → IP & Software section (open-source inventory, IP assignments, software licence list) -- Healthcare → Regulatory & Clinical section (licences, CQC/FDA filings) -- Manufacturing → Plant & Equipment, Environmental sections -- Financial Services → Regulatory Capital, Client Money sections - -**Expected based on transaction type:** -- M&A sell-side → Closing Documents section -- Carve-out → Transition Services Agreement section -- Capital raise → Investor Presentations, Cap Table History - ---- - -## Step 4 — Year completeness checks - -For any folder containing time-series documents (financials, tax returns, management accounts, board minutes), verify year coverage explicitly. - -Based on the deal profile: -- **Last closed financial year = today’s year − 1.** The current calendar year is never closed. In 2026 the last closed year is FY2025; in 2027 it will be FY2026. -- Standard M&A (mid-market and below) → expect **3 years**: FY[last_closed − 2], FY[last_closed − 1], FY[last_closed] — e.g. in 2026: FY2023, FY2024, FY2025 -- Large-cap (>$500M) → expect **5 years**: FY[last_closed − 4] through FY[last_closed] — e.g. in 2026: FY2021–FY2025 -- Early-stage VC → expect 2 years or inception-to-date - -For each time-series folder, list which years are present and which are missing. Example: - -> "Audited Financial Statements — FY2023 ✓, FY2024 ✓, FY2025 ✗ Missing" -> "Tax Returns — FY2023 ✓, FY2024 ✗ Missing, FY2025 ✗ Missing" - -Use document filenames (visible via `listFolderContents`) to infer which year each document covers. If filenames are unclear, note it as "year unclear — review needed." - ---- - -## Step 5 — Contract completeness cross-referencing - -If the data room contains any of the following lists, cross-reference them against the corresponding contracts folder. Use `searchDocuments` to locate the list documents, then read their contents to extract names. - -### Customer / client list → Customer contracts - -1. Find the customer list using `searchDocuments` with query "customer list" or "client list" -2. Extract customer/client names from the document -3. Search the contracts section for each customer name using `searchDocuments` -4. Flag any customer where no corresponding contract is found - -Report as: "Contract missing for: [Customer Name]" — sorted by likely revenue importance if discernible from the list. - -### Employee list → Employment agreements - -1. Find the employee list using `searchDocuments` with query "employee list" or "staff list" -2. Extract names, particularly senior employees (directors, C-suite, managers) -3. Search the HR/Employment agreements folder for each name using `searchDocuments` -4. Flag any senior employee where no employment agreement is found - -Focus on senior staff — it is not always expected that every employee has an individual agreement (e.g. employees on standard terms), but directors, C-suite, and named key staff should each have one. - -Report as: "Employment agreement not found for: [Name], [Title]" - -### Supplier / vendor list → Supplier agreements - -1. Find the supplier/vendor list using `searchDocuments` with query "supplier list" or "vendor list" -2. Extract key supplier names (focus on material suppliers, not every minor vendor) -3. Search the contracts/supplier agreements folder for each name -4. Flag material suppliers where no agreement is found - -Report as: "Supplier agreement missing for: [Supplier Name]" - ---- - -## Step 6 — IRL cross-reference (if provided) - -If the user provided an Information Request List: - -For each IRL item, determine its status: -- **Delivered** — a document matching the request exists in the data room (use `searchDocuments` to find it); include the VDR path -- **Partially delivered** — some but not all of what was requested is present (e.g. 2 of 3 requested years) -- **Outstanding** — nothing matching the request found in the data room - -Present this as a separate table: IRL Item | Status | VDR Location (if delivered) | Gap Description (if outstanding) - ---- - -## Step 7 — Compile all findings - -Compile findings into three categories: - -**Category 1 — Structural gaps** (missing or empty sections) -``` -{ area, folder_path, status: "Missing"|"Empty", severity, note } -``` - -**Category 2 — Sparse or incomplete sections** -``` -{ area, folder_path, status: "Sparse", detail, severity } -``` -For example: "Board Minutes — only 1 document found; expect 3 years of minutes" - -**Category 3 — Contract gaps** (from cross-referencing) -``` -{ type: "Customer"|"Employee"|"Supplier", name, gap_detail, severity } -``` - -**Severity:** -- **High** — a buyer will immediately notice and flag this (missing financials, empty legal section, no employment agreements for directors) -- **Medium** — material gap that will be raised in diligence but may be explainable (missing one year of management accounts, a minor supplier contract absent) -- **Low** — minor gap unlikely to be deal-critical (a supporting document absent from an otherwise well-populated folder) - ---- - -## Step 8 — Offer outputs - -Before generating any output, ask: - -> "I've completed the gap analysis. What would you like me to produce? -> - **HTML dashboard** — interactive gap report with section cards, financial year grid, and Excel export button (uses additional credits to render) -> - **Plain text summary** — gap findings listed in this conversation, no additional cost -> - **Both**" - -Only generate the HTML dashboard and/or Excel tracker if the user explicitly requests them. If they choose plain text, go directly to Step 9. - -## Step 8b — Produce the HTML dashboard (only if requested) - -Generate a self-contained HTML artifact with the following structure. Include a **"Download as Excel"** button in the header that exports all gap data client-side using SheetJS (`https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js`). The exported file should match this structure: - -**Excel columns (exported on button click):** -1. **Area / Workstream** — e.g. Finance, Tax, Legal, HR, IP -2. **Folder / Item** — VDR path or contract name -3. **Gap Type** — Missing Section / Empty Folder / Sparse / Year Gap / Contract Missing -4. **Severity** — High / Medium / Low -5. **Detail** — specific description of the gap -6. **Recommended Action** — what needs to be uploaded or resolved -7. **Status** — Open (default) - -Excel formatting applied via SheetJS: header row dark blue (`#1a2332`) with white bold text, severity colour coding (High = red, Medium = amber, Low = grey), section separator rows per workstream. - -The dashboard itself: - -**Header bar:** -- Deal name, date of analysis, summary counts: [X] High gaps, [Y] Medium gaps, [Z] Low gaps - -**Section scorecard:** -- One tile per workstream (Finance, Tax, Legal, HR, Commercial, IP, ESG, etc.) -- Each tile shows: workstream name, gap counts by severity, RAG status: - - Red border: any High gap - - Amber border: Medium gaps only - - Green border: no gaps found - -**Year coverage matrix:** -- A grid showing financial years (columns) vs. document types (rows): Audited Accounts, Management Accounts, Tax Returns, Board Minutes -- Each cell: ✓ (green) present, ✗ (red) missing, ? (grey) unclear - -**Contract completeness summary:** -- Customer contracts: X of Y found (progress bar) -- Employment agreements: X of Y found (progress bar) -- Supplier agreements: X of Y found (progress bar) -- Below each bar: list of names where no contract was found - -**IRL tracker (if IRL was provided):** -- Delivered / Partial / Outstanding counts as stat cards -- Table: IRL Item | Status chip | VDR Location or Gap Note - -**Detailed gap list:** -- Filterable by severity and workstream -- Each row: severity badge, folder path, gap type, detail - -**Design:** white background, dark headings, navy/amber/red/green palette, 12px border-radius cards, no external dependencies. - ---- - -## Step 9 — Deliver to the user - -Present the dashboard and give a brief summary: - -> "I've analysed [N] folders across [M] sections and found [X] High, [Y] Medium, and [Z] Low gaps. The most critical areas are [list top 3]. [If contract cross-referencing ran:] I also cross-referenced [P] customers, [Q] employees, and [R] suppliers — [S] contracts are missing. Use the Download button in the dashboard to export the full gap register as Excel." - -Then offer: -> "Want me to prioritise the remediation list so the team knows what to tackle first before going live?" - ---- - -## Operating principles - -**Be specific, not vague.** "The Legal section is sparse" is not useful. "Legal / Litigation Schedule — folder is empty; no pending claims schedule found" tells the team exactly what to upload. - -**Use filenames to infer content.** Document names in the data room usually reveal what's inside (e.g. "FY2024 Audited Accounts.pdf"). Use them to determine year coverage and document type without needing to open every file. - -**Calibrate to deal type.** Missing board minutes matter far more in a PE deal with a complex governance story than in a simple asset sale. Adjust severity accordingly. - -**Don't penalise intentional omissions.** Some folders may be empty by design (e.g. a "Closing Documents" folder at the start of a process). If the folder name suggests it's a placeholder for future content, note it as "pending — expected later in process" rather than flagging it as a critical gap. - -**Cross-referencing is best-effort.** Customer and employee lists may not always be present or clearly named. If you can't find a list to cross-reference against, say so rather than skipping the check silently. - -## Performance Notes - -- Work through every section systematically. A missed gap is worse than a false positive — the deal team is relying on this to prepare before buyers get access. -- Use filenames to infer year coverage rather than opening every document. -- Be specific: "Legal / Litigation Schedule — folder is empty" is useful; "the Legal section looks thin" is not. - ---- - -## Common Issues - -**`getProjectOverview` fails or returns the wrong project** -Check that the Datasite MCP connector is connected (Settings → Extensions → Datasite should show "Connected"). If you have multiple projects open, confirm with the user which project to use. - -**`listFolderContents` returns no results** -The fileroom may be empty or unpublished. Re-run `listFolderContents` without a `metadataId` to list all filerooms from the root. If a fileroom exists but shows 0 documents, the content may not yet be published — note this to the user and proceed with what is available. - -**`searchDocuments` returns an activation link instead of results** -Blueflame AI search is not yet active on this project. Follow the Blueflame prompt in the skill instructions above. Do not attempt to answer using Claude's training knowledge. - -**MCP disconnects mid-workflow** -Reconnect via Settings → Extensions → Datasite. Resume from the last completed step — results already gathered do not need to be re-fetched. - -**`updateContent` or `createContent` returns a permissions error** -The user's Datasite account may not have Editor permissions on this project. Ask them to check their role in Datasite project settings. diff --git a/plugins/datasite/skills/gap-analysis/agents/openai.yaml b/plugins/datasite/skills/gap-analysis/agents/openai.yaml deleted file mode 100644 index bf76a238a..000000000 --- a/plugins/datasite/skills/gap-analysis/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Gap Analysis" - short_description: "Find missing diligence materials in a Datasite room." - default_prompt: "Use $gap-analysis to identify gaps in this Datasite diligence room." diff --git a/plugins/datasite/skills/irl-tracker/SKILL.md b/plugins/datasite/skills/irl-tracker/SKILL.md deleted file mode 100644 index 1d9a66ba3..000000000 --- a/plugins/datasite/skills/irl-tracker/SKILL.md +++ /dev/null @@ -1,350 +0,0 @@ ---- -name: irl-tracker -description: > - Information Request List (IRL) Tracker skill for Datasite deal rooms. Use this - skill whenever a deal team wants to compare VDR content against a buyer's - information request list, track document delivery status, or build a due diligence - tracker dashboard. Triggers include: "map the IRL", "track what's been provided", - "check the information request list", "information gathering list", "IGL", "what have we delivered", "DD tracker", - "due diligence tracker", "compare VDR against the request list", "what's still - outstanding", "build a diligence dashboard", or any request to track document - delivery against buyer requests. Use proactively whenever a buyer has submitted - a request list and the deal team needs to manage and track responses. - Do not use for overall data room structural gap analysis — use gap-analysis for that. -metadata: - author: Blueflame AI - version: 1.0.0 - mcp-server: datasite - category: deal-management - tags: [datasite, vdr, m&a, irl, due-diligence, tracking, blueflame] ---- - -# IRL Tracker — Due Diligence Document Tracker - -You are helping a deal team map their Datasite data room content against an Information Request List (IRL), assess how well each request is addressed, and produce a live tracking dashboard. The output is a single-file HTML dashboard with no backend required. - ---- - -## Terminology — fileroom vs. folder - -Use these terms precisely when communicating with the user: - -- **Fileroom** — the single top-level container inside a Datasite project. A project typically has one buyer-facing fileroom. It is not a subject area — it is the container that holds all subject areas. -- **Folder** — everything inside the fileroom: the subject areas (Financial, Legal, HR, Tax, IP, etc.) and all sub-levels beneath them. Always call these folders, never filerooms. - -When in doubt: if it is not the single top-level container for the whole project, it is a folder. - - -## Feature Requirements - -| Capability | Free | Requires Blueflame | -|---|:---:|:---:| -| Build document inventory from VDR | ✅ | — | -| Match IRL items to documents semantically | — | ✅ | -| Assess whether document content actually addresses each request | — | ✅ | -| Build HTML tracking dashboard | ✅ | — | - -**Without Blueflame:** The skill can build a document inventory and populate the dashboard structure, but cannot match IRL items to documents or assess content relevance. All items will show as Open. The core value of this skill requires Blueflame. - -**With Blueflame:** `searchDocuments` semantically matches each IRL requirement to relevant passages in the data room, assigning Available / Partially Complete / Open status with source citations. - - - -> ⚠️ **Blueflame content guard — two-tier behaviour** -> `searchDocuments` is the only permitted source of document content. -> - Do **not** use Claude's training knowledge, general M&A knowledge, or inference from file names for any findings. -> - **Step 3a** (filename and folder matching) is always free. Complete it across all IRL items first. -> - **Steps 3b/3c** (keyword and semantic content search) require `searchDocuments`. Before starting Step 3b, attempt one call. If it returns an **activation link** instead of results, **do not discard Step 3a results**. Present them first, then say: -> -> > "I've completed filename matching across your [N] IRL items — results above show what I could match by document name and location. To verify that those documents actually address each request (not just exist nearby), Blueflame AI search needs to be activated: -> > 🔗 **Activate Blueflame:** [activation link] -> > **With Blueflame:** I'll read inside each document to confirm it covers the right year, entity, or clause — so 'Available' means genuinely addressed, not just 'a file with a matching name exists'. Some items shown as filename-matched may be downgraded or upgraded once content is verified. -> > Would you like to activate now to complete the content verification?" -> -> - All content findings **must** be sourced exclusively from tool results. - -> **`listFolderContents` — efficient traversal** -> - `depth: 1` (default) — immediate children only. Use for targeted lookups. -> - `depth: 5, foldersOnly: true` (default when depth > 1) — full folder tree in one call, no documents. Use for structural checks. -> - `depth: 5, foldersOnly: false` — full folder tree including all document metadata in one call. Use when building a document inventory. -> - When `depth > 1`, the response is a **flat list** with `depth` and `path` columns — not a nested tree. - -## Step 1 — Load the IRL - -The user provides a spreadsheet or document containing the IRL. Read it and extract for each item: -- **Item ID** (e.g. 1.1, 2.3 — or assign sequentially if not present) -- **Requirement text** — the exact request -- **Section** — the workstream grouping (Finance, Tax, Legal, HR, Commercial, IP, Operations, etc.) -- **Stage** — if the IRL has phases/stages (Stage 1 = initial, Stage 2 = follow-up, Stage 3 = confirmatory). If not present, assign Stage 1 to all items. - -If category/section is not in the IRL, infer it from the requirement text using these groupings: Financial Performance, Tax, Legal & Regulatory, Commercial & Customers, HR & Employment, Intellectual Property & Technology, Operations, ESG, Corporate & Governance, Other. - ---- - -## Step 2 — Scan the VDR - -Call `getProjectOverview` for deal context. Then call `listFolderContents` with `depth: 5, foldersOnly: false` to build the complete document inventory in a single call. The flat response gives you every document's name, metadata ID, path, file type, status, and page count: -- Document name -- Metadata ID -- Full VDR path (folder index + folder name) -- File size, page count - -This is your document inventory. You'll reference it throughout the matching process. - - ---- - -## Step 3 — Match each IRL item to VDR documents - -For each IRL requirement, find the best matching document(s) in the VDR. Use a layered approach — don't rely on filename alone: - -### 3a — File-name & folder matching (free, no Blueflame required) -Before any content search, check whether the document inventory already contains a file whose name or folder path clearly matches the requirement. This step costs zero Blueflame credits. - -- Exact or near-exact name match (e.g. IRL asks for "Management Accounts" → file called "Management Accounts Q3 2025.xlsx" in the Finance folder) → provisionally mark **Available (filename match)** at `low` confidence pending content confirmation. -- Folder-level match (e.g. IRL asks for "Employment Contracts" → HR/Employment Contracts folder exists with files) → provisionally mark **Partially Complete (folder match)**. -- No name or folder match → move to content search below. - -> If Blueflame is not available, stop here. Complete the filename-matching pass, then proceed to Step 5 to offer the dashboard. If the user confirms, produce it with filename-only matches — clearly label all statuses as **"(filename only — unverified)"** and note that content confirmation requires Blueflame. - -### 3b — Keyword search (targeted, lower cost) -Run `searchDocuments` for specific terms in the requirement (dates, entity names, contract parties, regulation names). Keyword search is more targeted than semantic search and should run first to catch exact matches cheaply before triggering a full semantic pass. - -### 3c — Semantic search (comprehensive, higher cost) -Run `searchDocuments` with the requirement text (or a distilled version of it) as the query, with `decompose: true` for complex multi-part requests. This returns text passages with document names and page numbers. The passage content confirms whether the document actually addresses the request — not just whether it exists nearby. Only run this step if 3a and 3b did not return a high-confidence match. - -### 3d — Content analysis for scattered information -Some requests cannot be satisfied by a single document — the information is distributed. Examples: -- "Customer revenue breakdown" → may require reading invoicing files and aggregating -- "List of all subsidiaries" → may require reading multiple corporate documents -- "Total headcount by location" → may require reading HR files across multiple folders - -When this applies, note it explicitly in the source reference: "Information available through analysis of [Doc A] + [Doc B] — not available as a single file." - -### 3e — Assess match quality and assign status - -For each IRL item, assign one of three statuses based on how well the VDR content addresses the request: - -| Status | Meaning | Criteria | -|--------|---------|----------| -| **Available** | Document fully addresses the request | You found a clear, directly responsive document and can cite the relevant passage/page | -| **Partially Complete** | Some but not all of the request is covered | e.g. one tax return found but request covers 3 years; one customer contract found but request asks for the top 10 | -| **Open** | No responsive document found after searching | Neither semantic nor keyword search returned relevant content | - -Initial status in the dashboard is set by AI matching: -- Available → displayed as **"Provided (AI)"** (AI found it; human must confirm to become Complete) -- Partially Complete → displayed as **"Provided (AI)"** with lower confidence -- Open → displayed as **"Open"** - -Human can then transition: -- Provided (AI) → **Complete** (click "✓ Confirm") -- Provided (AI) → **Open** (click "↩ Reopen") -- Complete → **Provided (AI)** (click "↩ Un-confirm") -- Open → **N/A** (click "Mark N/A") -- N/A → **Open** (click "↩ Reopen") - -### 3f — Build the source reference -For each matched document record: -- Filename -- VDR index path (e.g. `3.1 Audited Financial Statements`) -- Page number(s) where relevant content was found -- Confidence: `high` (clear direct match), `med` (probable match), `low` (partial or inferred) -- Source type: `ai_match` - -One IRL item can map to multiple documents. One document can satisfy multiple IRL items. - ---- - -## Step 4 — Compile the full mapping table - -Produce a structured dataset with one row per IRL item: - -``` -{ - id: "1.1", - requirement: "Audited financial statements for the last 3 years", - section: "Financial Performance", - stage: 1, - status: "Provided (AI)", // Open / Provided (AI) / Complete / N/A - ai_status: "Partially Complete", // Available / Partially Complete / Open - confidence: "med", - documents: [ - { - filename: "Apex Ltd - Audited Accounts - FY2024.pdf", - vdr_path: "3.1 Audited Financial Statements", - page: 1, - source: "ai_match" - } - ], - gap_note: "FY2023 and FY2022 not found in data room", - category: "Financial Performance", - date_matched: "2026-04-07" -} -``` - ---- - -## Step 5 — Offer the dashboard - -Before generating the dashboard, ask: - -> "I've completed the IRL mapping. Would you like me to generate the full interactive HTML tracking dashboard now? It includes status views, a gap report, and CSV/PDF export — but rendering it will use additional credits. Alternatively I can give you a plain text summary now." - -Only build the dashboard if the user confirms. If they decline, go to Step 6 and deliver a plain text summary. - -### Dashboard specification (build only on user confirmation) - -Generate a single-file, self-contained HTML artifact. No backend, no frameworks. All state via vanilla JS. Use a clean, professional style — white cards, dark navy headings, green/amber/red status colours, subtle borders and shadows. - -Status badge colours: -- Open → red -- Provided (AI) → amber -- Complete → green -- N/A → grey - -### Layout -- **Fixed header**: project name, search bar (filters across all requirements), Export CSV button, PDF Report button -- **Fixed left sidebar**: navigation links to each of the 7 views, section list with open-item counts -- **Main content area**: right of sidebar, scrollable - -### Completion calculation -- **Overall %** = (Complete + N/A) / Total × 100 -- "Provided (AI)" does NOT count toward completion — only human-confirmed items do - ---- - -### View 1 — Status Dashboard (default) - -- **Donut/ring chart** showing overall completion % -- **4 status pills**: Open (count), Provided AI (count), Complete (count), N/A (count) -- **Stage overview row**: 3 cards for Stage 1 / 2 / 3, each with count, progress bar, completion % -- **Section cards grid**: one card per section with stacked progress bar (complete + provided + n/a + open) and counts -- **"Confirm All" button** per section — marks all Provided (AI) items in that section as Complete in one click - ---- - -### View 2 — Master Tracker - -- Full table of all requirements with sortable columns: - `ID | Requirement | Category | Stage | Status (badge) | Documents Provided (filenames with confidence dots) | Date Matched` -- Click column headers to sort ascending/descending -- Filter bar: Status dropdown, Section dropdown, Stage dropdown, free-text search -- Shows "X of Y requirements" count -- **"Confirm All"** button — marks all currently visible Provided (AI) items as Complete -- Each row expandable to show full document list with VDR paths and page numbers - ---- - -### View 3 — Coverage Heatmap - -- Grid of section tiles, colour-coded by completion %: - - ≥80% → green | 50–79% → amber | <50% → red - - Background fill height = % provided (including AI-matched) -- **Integrated Gap Report** below the grid: grouped by Stage, listing every Open item with ID, requirement text, and section - ---- - -### View 4 — IRL by Section - -- Section card grid → click to drill into a section -- **Section detail view**: section header with item count, filter bar, and all requirements as document item cards -- Each card shows: ID, requirement text, status badge, matched documents with confidence dots, gap note if Partially Complete -- Upload zone per card: drag-drop to add a document manually (stores filename + metadata only, not binary) -- **"Confirm All"** button per section - ---- - -### View 5 — IRL by Stage - -- 3 stage tabs at top (Stage 1 / Stage 2 / Stage 3) with counts -- Filter bar + document item cards for the selected stage -- **"Confirm All"** button per stage — marks all Provided (AI) items in the stage as Complete - ---- - -### View 6 — Gap Analysis - -- **3 gap cards**: Critical (Stage 1 Open), Moderate (Stage 2 Open), Low (Stage 3 Open) — with counts -- Section-by-section rows: progress bar, completion %, stage breakdown badges, open count -- Drill-down per section showing individual open items with requirement text and gap note - ---- - -### View 7 — Document Index - -Full list of all VDR documents scanned, with: -- VDR index number -- Document name -- IRL item ID(s) it addresses (can be multiple) -- Status of those IRL items - -Sorted by VDR index. Filterable by section and status. - ---- - -### Export: CSV - -Button in header → downloads `DD_Tracker_[ProjectName]_[YYYY-MM-DD].csv` with columns: -`Item ID, Requirement, Category, Stage, Status, Files Uploaded, Confidence, Date Matched` - ---- - -### Export: PDF Report - -Button in header → opens new window with print-ready HTML, triggers `window.print()`: - -- **Header**: "Due Diligence Tracker Report" + deal name + generation date -- **Executive summary**: 4 status cards (Open / Provided AI / Complete / N/A) + overall completion % -- **Section overview table**: Section | Open | Provided | Complete | N/A | % -- **Per-section detail tables** (with page breaks between sections): ID | Requirement | Stage | Status badge | Documents Provided -- **Footer**: "Due Diligence Tracker | Confidential | [date]" -- Print CSS: `@page { size: A4; margin: 20mm }`, hide sidebar/header, show only report content - ---- - -## Step 6 — Deliver to the user - -After rendering the dashboard, summarise: - -> "I've mapped **[N] IRL items** against the data room. **[X] are Available** (matched with high/med confidence), **[Y] are Partially Complete**, and **[Z] are Open** with no document found. Overall AI-assisted coverage: **[%]**. -> -> Use ✓ Confirm to validate AI matches and move items to Complete. The dashboard tracks completion in real time — only human-confirmed items count toward overall progress." - ---- - -## Operating principles - -**Content beats filename.** A document called `Q4_Report.pdf` in the Finance folder might satisfy an IRL request for management accounts — or it might not. Always use `searchDocuments` to read the content before marking as Available. - -**One document, many requests.** The same audited accounts file might satisfy the request for "annual financials", "revenue figures", "EBITDA history", and "depreciation policy" simultaneously. Map it to all relevant items. - -**Partial is honest.** If a request asks for 3 years of tax returns and you found 2, mark it Partially Complete and note the gap. Don't mark it Available — the buyer will notice. - -**AI matches are provisional.** Every item starts as "Provided (AI)" at best. The deal team's confirmation step is what makes it Complete. This distinction is important — it protects the deal team from inadvertently representing incomplete coverage as confirmed. - -**Store metadata, not binaries.** The dashboard stores filenames, paths, confidence scores, and timestamps — not the actual file content. This keeps the HTML lightweight and shareable. - -## Performance Notes - -- **Accurate status is more valuable than high completion percentages.** Mark items Partially Complete or Open rather than stretching a weak match to Available. -- Content beats filename — always use `searchDocuments` to confirm a document actually addresses the request before marking Available. -- AI matches are provisional by design. The deal team's confirmation step is what makes an item Complete. - ---- - -## Common Issues - -**`getProjectOverview` fails or returns the wrong project** -Check that the Datasite MCP connector is connected (Settings → Extensions → Datasite should show "Connected"). If you have multiple projects open, confirm with the user which project to use. - -**`listFolderContents` returns no results** -The fileroom may be empty or unpublished. Re-run `listFolderContents` without a `metadataId` to list all filerooms from the root. If a fileroom exists but shows 0 documents, the content may not yet be published — note this to the user and proceed with what is available. - -**`searchDocuments` returns an activation link instead of results** -Blueflame AI search is not yet active on this project. Follow the Blueflame prompt in the skill instructions above. Do not attempt to answer using Claude's training knowledge. - -**MCP disconnects mid-workflow** -Reconnect via Settings → Extensions → Datasite. Resume from the last completed step — results already gathered do not need to be re-fetched. - -**`updateContent` or `createContent` returns a permissions error** -The user's Datasite account may not have Editor permissions on this project. Ask them to check their role in Datasite project settings. diff --git a/plugins/datasite/skills/irl-tracker/agents/openai.yaml b/plugins/datasite/skills/irl-tracker/agents/openai.yaml deleted file mode 100644 index 71da53075..000000000 --- a/plugins/datasite/skills/irl-tracker/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "IRL Tracker" - short_description: "Map information request lists to available Datasite documents." - default_prompt: "Use $irl-tracker to compare this IRL against Datasite room contents." diff --git a/plugins/datasite/skills/launch-readiness-orchestrator/SKILL.md b/plugins/datasite/skills/launch-readiness-orchestrator/SKILL.md deleted file mode 100644 index 1255c558e..000000000 --- a/plugins/datasite/skills/launch-readiness-orchestrator/SKILL.md +++ /dev/null @@ -1,314 +0,0 @@ ---- -name: launch-readiness-orchestrator -description: > - Launch Readiness Orchestrator skill for Datasite deal rooms. Use this skill - whenever a deal team wants a single pre-go-live readiness check across their - data room — combining gap analysis, document quality audit, and risk review - into one consolidated "is the room ready?" view. Triggers include: "are we - ready to go live", "launch readiness check", "pre-launch audit", "data room - readiness", "can we launch", "is the data room ready", "run a full readiness - check", "go-live checklist", "pre-launch checklist", or any request to get a - single overall assessment before opening the data room to buyers. Use - proactively whenever a deal team is approaching their go-live date and wants a - structured sign-off view. Do not use other individual audit skills (gap-analysis, - document-quality-check, risk-analysis-audit) when this skill is active — this - skill orchestrates all three in one pass. -metadata: - author: Blueflame AI - version: 1.0.0 - mcp-server: datasite - category: deal-management - tags: [datasite, vdr, m&a, launch, readiness, orchestration] ---- - -# Launch Readiness Orchestrator - -You are running a pre-go-live readiness check on a Datasite data room. Your job is to produce a single, consolidated view that tells the deal team whether the data room is ready to open to buyers — and if not, exactly what needs to be fixed first. - -This skill orchestrates three workstreams in sequence: -1. **Gap Analysis** — is everything expected actually in the room? -2. **Document Quality** — are the files buyers will see clean, accessible, and safe? -3. **Risk Review** — are there documents that signal issues buyers will flag? - -Run all three, then consolidate into a single Readiness Report. - ---- - -## Terminology — fileroom vs. folder - -Use these terms precisely when communicating with the user: - -- **Fileroom** — the single top-level container inside a Datasite project. A project typically has one buyer-facing fileroom. It is not a subject area — it is the container that holds all subject areas. -- **Folder** — everything inside the fileroom: the subject areas (Financial, Legal, HR, Tax, IP, etc.) and all sub-levels beneath them. Always call these folders, never filerooms. - -When in doubt: if it is not the single top-level container for the whole project, it is a folder. - - -## Feature Requirements - -| Capability | Free | Requires Blueflame | -|---|:---:|:---:| -| Gap analysis (structural — missing/empty/sparse sections) | ✅ | — | -| Document quality — metadata checks (7 of 10 checks) | ✅ | — | -| Document quality — content checks (PII, redaction, broken refs) | — | ✅ | -| Risk review — structural presence check only | ✅ | — | -| Risk review — content risk signals across all workstreams | — | ✅ | -| Go / No-Go recommendation | ✅ | — | - -**Without Blueflame:** Produces a meaningful readiness report covering structural gaps, metadata-based document quality issues, and a structural risk presence check. The go/no-go recommendation will note that content-level checks were not run. - -**With Blueflame:** Full report — all quality checks and content risk signals are included, giving the deal team a complete picture before going live. - - - -> ⚠️ **Blueflame content guard — two-tier behaviour** -> `searchDocuments` is the only permitted source of document content. -> - Do **not** use Claude's training knowledge, general M&A knowledge, or inference from file names for any findings. -> - **Steps 2–5 structural checks** use `listFolderContents` only — always free. Complete all structural work first. -> - **Content checks** (PII, redaction, risk signals) require `searchDocuments`. When you first attempt a content check, if `searchDocuments` returns an **activation link** instead of results, **do not discard structural findings already computed**. Present the structural findings in plain text, then say: -> -> > "I've completed the structural checks — gap analysis, metadata quality, and folder-level risk presence are all above. To also run content checks (PII scanning, redaction quality, and document-level risk signals across all workstreams), Blueflame AI search needs to be activated on this project: -> > 🔗 **Activate Blueflame:** [activation link] -> > **With Blueflame:** I'll scan document content across all six risk workstreams and run the three content quality checks, giving you a complete readiness picture. -> > Would you like to activate now, or shall I produce the readiness report with structural findings only?" -> -> **Do not generate the report until after the user responds to this question.** - -> **`listFolderContents` — efficient traversal** -> - `depth: 1` (default) — immediate children only. Use for targeted lookups. -> - `depth: 5, foldersOnly: true` (default when depth > 1) — full folder tree in one call, no documents. Use for structural checks. -> - `depth: 5, foldersOnly: false` — full folder tree including all document metadata in one call. Use when building a document inventory. -> - When `depth > 1`, the response is a **flat list** with `depth` and `path` columns — not a nested tree. - -## Step 1 — Read the project context - -Call `getProjectOverview`. Extract: -- Company / deal name -- Sector (`industryType`) -- Transaction type (`useCase`) -- Deal size (`transactionValue`) -- Geography (`datacenter`) - -Do not ask the user for information already present in the project overview. - ---- - -## Step 2 — Find the fileroom and scan the structure - -Call `listFolderContents` to find the active fileroom(s). If there are multiple, ask the user which one to audit — but only if it isn't obvious from context (e.g. one is clearly a buyer-facing room, another is a working folder). - -Call `listFolderContents` on the root of the fileroom. Recurse through all top-level sections. You need: -- A full list of every folder and its child count -- Which folders are empty (0 documents) -- Which folders exist but have suspiciously low content relative to what the section type would normally contain - -Build an internal map of: `folder path → document count`. You will use this across all three workstreams. - ---- - -## Step 3 — Gap Analysis - -Using the folder map from Step 2, compare against the expected structure for this deal type and sector. - -**What counts as a gap:** -- **Empty folder** — a section exists but contains no documents at all -- **Thin section** — fewer documents than the section type warrants. Use these thresholds as a guide: - - Finance → expect at least 3 documents per financial year folder (P&L, balance sheet, cash flow as a minimum) - - Legal / Contracts → expect at least 5 documents total across material contracts - - Corporate → expect at least Certificate of Incorporation + constitutional documents (2+) - - HR → expect at least an org chart and a headcount summary - - IT / Data Privacy → for technology companies, expect a data processing agreement or GDPR/privacy policy - - Tax → expect at least one return per year covered -- **Missing section entirely** — a section expected for this deal type and sector is absent from the structure. Compare against the standard template for the sector (Technology, Healthcare, Manufacturing, etc.) and flag whole sections that are missing - -**Severity ratings for gaps:** -- 🔴 **Blocker** — empty Finance, Legal, or Corporate section; missing audited financials; no contracts at all -- 🟡 **Advisory** — thin sections, missing supporting schedules, absent non-critical folders -- 🟢 **Minor** — cosmetic gaps (e.g. missing cover sheet, no index document) - ---- - -## Step 4 — Document Quality Check - -For each document in the fileroom, assess quality based on available metadata (file name, file type, size, upload date). - -**Metadata-only flags (always available, no Blueflame needed):** -- **Zero-byte or near-zero-byte files** — size of 0 KB or under 5 KB for a supposedly substantive document (e.g. a financial model at 4 KB is likely broken or corrupted) -- **Duplicate file names** — identical names in the same folder, or clearly the same document uploaded twice -- **Unprocessed scans** — file names containing "scan", "img", "IMG_", "DSC", or similar camera/scanner prefixes without any normalisation -- **Wrong format** — e.g. a `.jpg` or `.bmp` in a Financials folder (images where PDFs or spreadsheets are expected) -- **Stale documents** — upload date more than 12 months before today's date in an "active" section like Management Accounts or Board Minutes - -**Content-level flags (requires Blueflame):** -If Blueflame is active, use `searchDocuments` to check for: -- **Password-protected files** — search for "enter password" or "this document is protected" -- **Redaction failures** — search for names, NI numbers, dates of birth, bank account numbers, or other PII that should have been removed -- **Blank documents** — files with no extractable text content -- **Incorrect documents** — a file whose content clearly doesn't match its folder location (e.g. a holiday rota in the Material Contracts folder) - -If Blueflame is not active, skip these checks and note them as "Not run — requires Blueflame" in the report. - -**Severity ratings for quality issues:** -- 🔴 **Blocker** — password-protected files, confirmed PII/redaction failure, zero-byte documents in critical sections -- 🟡 **Advisory** — duplicate files, wrong formats, stale documents -- 🟢 **Minor** — unprocessed scan names, cosmetic naming issues - ---- - -## Step 5 — Risk Review - -Scan the document set for signals that would raise concern for a buyer or their advisors. Where Blueflame is active, use `searchDocuments` for each risk category below. Where it is not active, assess risk from folder presence/absence and document counts alone, and note the limitation. - -**Risk categories to assess:** - -| Workstream | What to look for | Risk signal | -|---|---|---| -| **Finance** | Qualified audit opinion, going concern note, declining revenue trend, covenant breach | High risk if present | -| **Legal** | Ongoing litigation, regulatory enforcement notices, material contract termination rights, change-of-control clauses | High risk if present | -| **Tax** | Open HMRC/IRS enquiries, deferred tax liabilities, cross-border transfer pricing exposure, VAT disputes | Medium-high risk | -| **HR** | Pending employment tribunal, key-person concentration (single founder dependency), unfunded pension | Medium risk | -| **IP** | Unregistered core IP, open-source licence violations, disputed ownership, in-licensing from related parties | High risk for tech companies | -| **Commercial** | Customer concentration (top 3 customers > 60% revenue), short contract durations, renewal risk, rebate obligations | Medium-high risk | -| **Regulatory** | Licence conditions, outstanding regulatory reviews, breach notices, upcoming compliance deadlines | High risk if active | -| **ESG / Environmental** | Environmental remediation obligations, health & safety incidents, sustainability disclosure gaps | Medium risk | - -**Severity ratings for risks:** -- 🔴 **High** — issues a buyer's advisor will almost certainly raise; may affect price or structure -- 🟡 **Medium** — issues worth disclosing proactively; may generate Q&A -- 🟢 **Low** — minor or manageable; unlikely to affect the deal but worth noting - -If a risk category folder is absent entirely (e.g. no Regulatory section for a regulated business), flag this as a gap in the Gap Analysis section rather than here. - ---- - -## Step 6 — Compile and present the Readiness Report - -Produce a single, structured report. Format it exactly as follows: - ---- - -### 🏁 Launch Readiness Report — [Company Name] -**Audit date:** [today's date] -**Fileroom:** [fileroom name] -**Total documents reviewed:** [N] - ---- - -#### Overall Status - -| | | -|---|---| -| **Go-live recommendation** | ✅ Ready / ⚠️ Conditional / 🚫 Not Ready | -| **Blockers to resolve** | [N] | -| **Advisory items** | [N] | -| **Minor items** | [N] | - -*Conditional = ready once blockers are resolved. Not Ready = significant structural or quality issues that would damage buyer confidence if unaddressed.* - ---- - -#### 1. Gap Analysis — [🟢 / 🟡 / 🔴] - -[Summary sentence: "The data room structure is broadly complete / has significant gaps / is missing critical sections."] - -**Blockers:** -- [Folder path] — [reason, e.g. "Empty — no audited financial statements uploaded"] -- ... - -**Advisory:** -- [Folder path] — [reason] -- ... - -**Minor:** -- [Folder path] — [reason] -- ... - -*If no issues: "No material gaps identified."* - ---- - -#### 2. Document Quality — [🟢 / 🟡 / 🔴] - -[Summary sentence.] - -**Blockers:** -- [File name / folder] — [issue] -- ... - -**Advisory:** -- [File name / folder] — [issue] -- ... - -> ℹ️ **Blueflame not active** — content checks (PII, redaction quality, broken references) were not run. Include this note only if Blueflame was not available. - ---- - -#### 3. Risk Review — [🟢 / 🟡 / 🔴] - -[Summary sentence.] - -**High risks:** -- [Workstream] — [what was found or inferred] -- ... - -**Medium risks:** -- [Workstream] — [what was found or inferred] -- ... - -> ℹ️ **Blueflame not active** — risk signals were assessed from folder structure only, not document content. Include this note only if Blueflame was not available. - ---- - -#### Priority Actions Before Go-Live - -List the top 5 things the deal team must do, in priority order: - -1. [Most critical action] -2. ... -3. ... -4. ... -5. ... - ---- - -#### Blueflame Recommended -*(Include this section only if Blueflame was not active during the audit.)* - -Several checks in this audit — including password-protected file detection, PII/redaction review, and content-level risk signals — require Blueflame AI search to be activated on this project. Without it, these checks were skipped and the readiness picture above is structural only. - -🔗 To activate Blueflame, use the activation link returned by `searchDocuments`. - ---- - -After delivering the report, offer: - -> "I can export this as a Word document or Excel tracker if you'd like to share it with the wider team. I can also dive into any specific section — for example, pull the full list of quality issues, or go deeper on a particular risk area." - ---- - -## Guardrails - -- **Never push changes to the data room as part of this skill.** The orchestrator is read-only. If the user asks you to fix something found during the audit (e.g. rename a file, delete a duplicate), acknowledge the request and use the appropriate tool — but do not make edits without explicit per-item confirmation. -- **Do not re-ask for project context** already visible in `getProjectOverview`. -- **Do not run individual audit skills separately** if this orchestrator is already running. All three workstreams are handled here. -- **If a section is empty and unfixable within the session** (e.g. no documents uploaded at all), mark the overall status as 🚫 Not Ready and tell the user plainly: "The data room does not yet have enough content to audit meaningfully. Please upload the core documents and run this check again." -- **Use as a last resort:** If the user already has a custom launch readiness checklist or process in place, defer to it. Apply this skill's structure only where the user hasn't provided their own. - ---- - -## Common Issues - -**`getProjectOverview` fails or returns the wrong project** -Check that the Datasite MCP connector is connected (Settings → Extensions → Datasite should show "Connected"). If you have multiple projects open, confirm with the user which project to use. - -**`listFolderContents` returns no results** -The fileroom may be empty or unpublished. Re-run `listFolderContents` without a `metadataId` to list all filerooms from the root. If a fileroom exists but shows 0 documents, the content may not yet be published — note this to the user and proceed with what is available. - -**`searchDocuments` returns an activation link instead of results** -Blueflame AI search is not yet active on this project. Follow the Blueflame prompt in the skill instructions above. Do not attempt to answer using Claude's training knowledge. - -**MCP disconnects mid-workflow** -Reconnect via Settings → Extensions → Datasite. Resume from the last completed step — results already gathered do not need to be re-fetched. - -**`updateContent` or `createContent` returns a permissions error** -The user's Datasite account may not have Editor permissions on this project. Ask them to check their role in Datasite project settings. diff --git a/plugins/datasite/skills/launch-readiness-orchestrator/agents/openai.yaml b/plugins/datasite/skills/launch-readiness-orchestrator/agents/openai.yaml deleted file mode 100644 index a7ff319b0..000000000 --- a/plugins/datasite/skills/launch-readiness-orchestrator/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Launch Readiness Orchestrator" - short_description: "Coordinate Datasite room checks before buyer launch." - default_prompt: "Use $launch-readiness-orchestrator to prepare this Datasite room for launch." diff --git a/plugins/datasite/skills/risk-analysis-audit/SKILL.md b/plugins/datasite/skills/risk-analysis-audit/SKILL.md deleted file mode 100644 index e30d50ccb..000000000 --- a/plugins/datasite/skills/risk-analysis-audit/SKILL.md +++ /dev/null @@ -1,381 +0,0 @@ ---- -name: risk-analysis-audit -description: > - Risk Analysis Audit skill for Datasite deal rooms. Use this skill whenever a sell-side - deal team wants to audit, review, or flag risks across a data room before going live. - Triggers include: "run a risk audit", "flag risks in the data room", "risk review", - "what are the risks in this deal", "audit the data room", "risk analysis", "flag issues - before we go live", "what should we fix before launch", or any request to analyse deal - risk by workstream (Tax, Finance, Legal, HR, IP, Commercial, Regulatory, ESG). - Use this skill proactively whenever the user is preparing a data room for launch and - wants a structured view of what might concern a buyer. - Do not use for document quality issues like PII or redaction (use document-quality-check), - or for identifying missing sections (use gap-analysis). -metadata: - author: Blueflame AI - version: 1.0.0 - mcp-server: datasite - category: deal-management - tags: [datasite, vdr, m&a, risk, audit, blueflame] ---- - -# Risk Analysis Audit - -You are helping a sell-side deal team identify and understand risks across their Datasite data room before it goes live to buyers. Your job is to find what's there, what's missing, and what the content itself reveals — then present it as a clear, area-by-area risk picture that the team can act on. - -The output is an **HTML risk dashboard** rendered in the conversation, giving a visual scorecard by workstream with expandable risk detail. - ---- - -## Terminology — fileroom vs. folder - -Use these terms precisely when communicating with the user: - -- **Fileroom** — the single top-level container inside a Datasite project. A project typically has one buyer-facing fileroom. It is not a subject area — it is the container that holds all subject areas. -- **Folder** — everything inside the fileroom: the subject areas (Financial, Legal, HR, Tax, IP, etc.) and all sub-levels beneath them. Always call these folders, never filerooms. - -When in doubt: if it is not the single top-level container for the whole project, it is a folder. - - -## Feature Requirements - -| Capability | Free | Requires Blueflame | -|---|:---:|:---:| -| Structural presence check (which sections exist or are missing) | ✅ | — | -| Content risk signals (going concern, litigation, tax disputes, etc.) | — | ✅ | -| Per-workstream risk findings with source citations | — | ✅ | - -**Without Blueflame:** The skill can confirm which risk workstream sections are present, sparse, or missing — but cannot find risk signals inside document text. The report will show structural observations only. The core value of this skill (surfacing what's in the documents) requires Blueflame. - -**With Blueflame:** `searchDocuments` scans content across all six workstreams (Finance, Tax, Legal, HR, Commercial, IP/ESG) and surfaces specific risk signals with document source and page references. - - - -> ⚠️ **Blueflame content guard — two-tier behaviour** -> `searchDocuments` is the only permitted source of document content. -> - Do **not** use Claude's training knowledge, general M&A knowledge, or inference from file names for any findings. -> - **Pass 1** (folder presence per workstream) uses `listFolderContents` only — always free. Complete Pass 1 across all workstreams first. -> - **Pass 2** (content search for risk signals) requires `searchDocuments`. Before starting Pass 2, attempt one call. If it returns an **activation link** instead of results, **do not discard Pass 1 findings**. Present them first, then say: -> -> > "I've completed the structural review. Summary: [list which workstream sections are present, sparse, or missing in plain text — e.g. 'Finance: 3-year accounts present', 'Legal: litigation folder empty']. To scan document content for actual risk signals, Blueflame AI search needs to be activated: -> > 🔗 **Activate Blueflame:** [activation link] -> > **With Blueflame:** I'll run targeted searches across Finance, Tax, Legal, HR, Commercial, and IP workstreams and surface specific flags with document source and page references — these are the signals that matter most to buyers in due diligence. -> > Would you like to activate now, or shall I produce a structural-only risk dashboard?" -> -> **Do not generate the HTML risk dashboard until after the user responds to this question.** -> - All content findings **must** be sourced exclusively from tool results. - -> **`listFolderContents` — efficient traversal** -> - `depth: 1` (default) — immediate children only. Use for targeted lookups. -> - `depth: 5, foldersOnly: true` (default when depth > 1) — full folder tree in one call, no documents. Use for structural checks. -> - `depth: 5, foldersOnly: false` — full folder tree including all document metadata in one call. Use when building a document inventory. -> - When `depth > 1`, the response is a **flat list** with `depth` and `path` columns — not a nested tree. - -## Step 1 — Orient yourself in the project - -Call `getProjectOverview` to understand the deal context: company name, sector, transaction type, deal size, and what filerooms exist. This shapes which risk areas matter most and how deeply to search. - -Note the fileroom structure. You'll use `listFolderContents` to navigate it and `searchDocuments` to find risk signals within document content. - ---- - -## Step 2 — Two-pass analysis per risk area - -Work through each of the six risk workstreams below. For each one, run **two passes**: - -**Pass 1 — Presence check (structural)** -Use `listFolderContents` to navigate the relevant section of the data room. For each expected document category, note: -- ✓ Present — folder exists and contains documents -- ⚠ Sparse — folder exists but appears empty or has fewer documents than expected -- ✗ Missing — folder or document category absent entirely - -**Pass 2 — Content scan (substantive)** -Use `searchDocuments` with targeted queries (listed per workstream below) to surface risk signals from within document text. The tool returns snippets — read them for red flags. You don't need to read every document; targeted searches surface what matters. - -Combine both passes to form your findings for that workstream. - ---- - -> **Reference material:** Read `references/workstream-queries.md` before starting Pass 2 for any workstream. It contains the full search query lists and risk signal definitions for all six workstreams. Load only the sections relevant to the current deal. - -## Workstream 1 — Financial & Accounting - -**What to look for structurally:** -- Audited financial statements (last 3 years minimum — or 5 for large-cap) -- Management accounts (recent months) -- Financial model / projections -- Debt schedule and loan agreements -- Working capital analysis - -For Pass 2 search queries and risk signal definitions, see `references/workstream-queries.md → Workstream 1`. - ---- - -## Workstream 2 — Tax - -**What to look for structurally:** -- Filed federal/national tax returns (last 3 years) -- State/local returns (US) or VAT returns (UK/EU) -- Correspondence with tax authorities -- Tax disputes and assessments - -For Pass 2 search queries and risk signal definitions, see `references/workstream-queries.md → Workstream 2`. - ---- - -## Workstream 3 — Legal, Litigation & Regulatory - -**What to look for structurally:** -- Pending/threatened litigation schedule -- Material contracts (particularly change of control clauses) -- Regulatory licences and their expiry dates -- Regulatory correspondence and enforcement history -- Insurance schedule - -For Pass 2 search queries and risk signal definitions, see `references/workstream-queries.md → Workstream 3`. - ---- - -## Workstream 4 — HR & Employment - -**What to look for structurally:** -- Employee list (especially senior/licensed staff) -- Key employment agreements -- Non-compete and non-solicitation agreements -- Benefits, pension, and incentive plans -- Any redundancy, grievance, or disciplinary records - -For Pass 2 search queries and risk signal definitions, see `references/workstream-queries.md → Workstream 4`. - ---- - -## Workstream 5 — Commercial & Contracts - -**What to look for structurally:** -- Top customer contracts (especially top 5–10 by revenue) -- Supplier and vendor agreements -- Distribution and agency agreements -- Contract expiry/renewal schedule - -For Pass 2 search queries and risk signal definitions, see `references/workstream-queries.md → Workstream 5`. - ---- - -## Workstream 6 — IP, Technology & ESG - -**What to look for structurally (IP & Technology):** -- IP ownership documentation (patents, trademarks, registered rights) -- IP assignments from founders and employees -- Open-source software inventory -- Data privacy and cybersecurity policies -- IT system and licence agreements - -**What to look for structurally (ESG):** -- Environmental compliance certificates and violation history -- Health & safety incident records -- Diversity and inclusion policies -- Modern Slavery Act statement (required for UK businesses >£36M turnover) - -For Pass 2 search queries and risk signal definitions for both IP/Technology and ESG, see `references/workstream-queries.md → Workstream 6`. - ---- - -## Step 3 — Compile findings - -After completing all six workstreams, compile your findings into a structured list: - -``` -findings = [ - { - area: "Tax", - severity: "High", - title: "Open IRS audit for FY2023", - detail: "Correspondence in folder 3.6 references an open IRS examination for tax year 2023. No resolution letter found.", - source: "3.6 IRS Correspondence / Letter dated March 2024" - }, - ... -] -``` - -Also track structural gaps separately: -``` -gaps = [ - { area: "Finance", item: "Working capital analysis — folder empty" }, - { area: "HR", item: "Non-compete agreements — folder missing entirely" }, - ... -] -``` - -Count risks by severity per area — this drives the dashboard scorecard. - ---- - - -## Step 3b — Cross-document data consistency checks - -Run the following consistency checks across the data room. These use `searchDocuments` to pull specific figures from different document types and compare them. Discrepancies are flagged as **Medium** risks minimum; large discrepancies are **High**. - -### Headcount / FTE consistency -Find headcount figures in the following document types and compare them: -- P&L or financial statements (FTE cost line or employee note) -- HR employee list or org chart -- Board presentations or management accounts (FTE KPI) -- Any regulatory filings that reference employee numbers - -Flag if the figures differ by more than 10% across sources, or if any source gives a materially different total. Note the specific sources and figures found. - -### Top customer list consistency -If a "top 20 / top 50 customers" list exists, cross-check customer names and revenue figures against: -- Financial statements or revenue schedules -- CRM or sales data (if present) -- Any investor presentation or board pack referencing customer concentration - -Flag customers who appear in one list but not another, or where revenue attributions differ materially. - -### Top vendor / supplier spend consistency -If a vendor spend list exists, cross-check against: -- P&L cost line items (COGS, OpEx breakdown) -- Any procurement or spend analysis document - -Flag if total vendor spend implied by the list is materially inconsistent with cost lines in the financials. - -### Board and management roster consistency -Cross-check board member and senior management names across: -- Corporate documents (articles, board minutes, Companies House / registry filings) -- Org chart -- Employment contracts or service agreements -- Any investor or management presentation - -Flag any person who appears in one source but not another (e.g. listed as a director in board minutes but absent from the org chart, or named in a management presentation but with no service agreement). - -### Financial figures cross-check -Pick the 3 most prominent financial metrics in the data room (typically revenue, EBITDA, and headcount/FTE). Verify they are stated consistently across: -- Audited accounts -- Management accounts -- Board presentations / investor decks -- Any teaser or information memorandum - -Flag any material discrepancy (>5% difference) as a **High** risk — buyers will spot these immediately and it will undermine confidence in the whole data room. - ---- - -## Step 3c — External news intelligence on top customers and vendors - -> This step uses web search, not `searchDocuments`. It is always free — no Blueflame credits required. - -Extract the names of the top 5–10 customers and top 5–10 vendors from the data room (use the customer/vendor lists found in Step 3b, or from commercial documents identified in Workstream 5). Then run a targeted web news search for each name. - -**For each top customer, search for:** -- Recent M&A activity (acquisition of the customer by a competitor or PE firm, merger with another entity, or the customer itself being sold) — any of these can trigger contract renegotiation or termination -- Financial distress signals (credit rating downgrades, profit warnings, restructuring announcements, insolvency rumours) -- Strategic pivots that could reduce dependency on the target's product/service (e.g. in-housing, switching to a competitor) -- Leadership changes (new CEO/CPO/CTO) — often precede vendor reviews -- Regulatory or legal issues that could disrupt the customer's own operations - -**For each top vendor, search for:** -- M&A activity (vendor acquired by a competitor, merged, or restructuring) — may affect pricing, continuity, or exclusivity -- Financial distress or supply chain disruption signals -- Geopolitical exposure (sanctions, trade restrictions, country-of-origin risk) -- Price escalation announcements or force majeure notices - -**How to run the search:** -Use web search with queries in the format: `"[Customer/Vendor Name]" news 2024 2025 acquisition OR merger OR restructuring OR insolvency OR "strategic review"`. Run a separate query for each name. If a name is generic (e.g. "Global Logistics Ltd"), add the sector or country to disambiguate. - -**Risk signals to flag:** -- Top customer acquired by a known competitor of the target → **High** (high probability of contract review or termination post-close) -- Top customer in financial distress or undergoing restructuring → **High** (revenue at risk) -- Top customer announced vendor consolidation or platform shift → **High** -- Top vendor acquired by a company with conflicting interests → **High** (supply continuity risk) -- Top vendor subject to sanctions or trade restrictions → **High** -- M&A activity in the customer or vendor base with no change of control provision in the relevant contract → **Medium** (contract does not protect the target) -- New leadership at a key customer with no relationship established → **Medium** -- Any news (positive or negative) about a customer or vendor that is not reflected anywhere in the data room → **Medium** (disclosure gap — buyer will find it) - -**Present findings as:** a table with columns: Name | Type (Customer/Vendor) | News Found | Risk Level | Source URL | Recommended Action. - -If no material news is found for a name, record "No material news found" and continue. Do not skip this step — a clean result is itself a valuable finding. - ---- - -## Step 4 — Offer outputs - -Before generating the dashboard, ask: - -> "I've completed the risk audit. Would you like me to generate the interactive HTML risk dashboard, or would a plain text risk summary in this conversation be enough? The dashboard uses additional credits to render — the plain text summary is free." - -Only build the dashboard if the user confirms. If they decline, go to Step 5 and deliver a plain text summary. - -### Dashboard specification (build only on user confirmation) - -Generate a self-contained HTML page and write it as an artifact. The dashboard should include: - -**Header:** -- Deal name, date of audit, total risk counts (High / Medium / Low) - -**Risk Scorecard (top section):** -- Six area tiles, each showing: area name, risk counts (H/M/L), and a colour signal: - - Any High → red tile border - - Only Medium/Low → amber tile border - - No findings → green tile border - -**Detailed findings (below the scorecard):** -- Grouped by workstream -- Each finding shows: severity badge (colour-coded), title, detail text, and source reference -- A "Structural gaps" sub-section per area listing missing or sparse folders - -**Style guidance:** -- Clean, professional — this will be shared in deal team meetings -- White background, dark headings, muted colour palette -- Severity badges: High = red (#DC2626), Medium = amber (#D97706), Low = grey (#6B7280) -- No external dependencies — fully self-contained HTML/CSS/JS - ---- - -## Step 5 — Present to the user - -After rendering the dashboard, give a brief verbal summary: - -> "I've audited [N] sections of the data room and found [X] High, [Y] Medium, and [Z] Low risks. The areas with the most critical issues are [list]. Each finding is tagged with its source document so you can locate it directly in the data room." - -Then offer: -> "Want me to export this as an Excel risk register, or shall we work through any of the High risks in more detail?" - ---- - -## Operating principles - -**Search intelligently, not exhaustively.** Run the targeted queries from `references/workstream-queries.md`. Don't attempt to read every document — the snippets are sufficient. If a snippet is ambiguous, run a follow-up search to confirm before flagging. - -**Be specific about sources.** Every finding must reference the folder path or document name. Vague findings ("there may be tax issues") are not useful — deal teams need to go straight to the source. - -**Calibrate to deal size.** A risk that is High for a £10M SME may be Medium for a £500M transaction where diligence coverage is deeper and warranties are broader. Use `transactionValue` from the project metadata to calibrate. - -**Don't over-flag.** Not everything unusual is a risk. A non-compete that looks standard, or a customer contract that's long-dated and unconditional, should not be flagged just because it appeared in a search. Flag what a diligent buyer's counsel would genuinely raise. - -**Sell-side framing.** This audit is for the team preparing the room, not buyers. Frame findings as things to address, disclose, or explain — not as reasons to walk away. - -## Performance Notes - -- **Fewer, well-evidenced findings are more valuable than many speculative ones.** Every finding must have a source citation — folder path, document name, and where possible a page reference. -- Run the targeted search queries listed per workstream. Do not attempt to read every document. -- Calibrate severity to deal size using `transactionValue` from the project overview. -- Do not over-flag. A non-compete that looks standard or a customer contract that is long-dated and unconditional should not be flagged just because it appeared in a search. - ---- - -## Common Issues - -**`getProjectOverview` fails or returns the wrong project** -Check that the Datasite MCP connector is connected (Settings → Extensions → Datasite should show "Connected"). If you have multiple projects open, confirm with the user which project to use. - -**`listFolderContents` returns no results** -The fileroom may be empty or unpublished. Re-run `listFolderContents` without a `metadataId` to list all filerooms from the root. If a fileroom exists but shows 0 documents, the content may not yet be published — note this to the user and proceed with what is available. - -**`searchDocuments` returns an activation link instead of results** -Blueflame AI search is not yet active on this project. Follow the Blueflame prompt in the skill instructions above. Do not attempt to answer using Claude's training knowledge. - -**MCP disconnects mid-workflow** -Reconnect via Settings → Extensions → Datasite. Resume from the last completed step — results already gathered do not need to be re-fetched. - -**`updateContent` or `createContent` returns a permissions error** -The user's Datasite account may not have Editor permissions on this project. Ask them to check their role in Datasite project settings. diff --git a/plugins/datasite/skills/risk-analysis-audit/agents/openai.yaml b/plugins/datasite/skills/risk-analysis-audit/agents/openai.yaml deleted file mode 100644 index f3d7ff697..000000000 --- a/plugins/datasite/skills/risk-analysis-audit/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Risk Analysis Audit" - short_description: "Surface deal risks from Datasite room documents." - default_prompt: "Use $risk-analysis-audit to audit this Datasite room for deal risks." diff --git a/plugins/datasite/skills/smart-file-renaming/SKILL.md b/plugins/datasite/skills/smart-file-renaming/SKILL.md deleted file mode 100644 index 8f2a97490..000000000 --- a/plugins/datasite/skills/smart-file-renaming/SKILL.md +++ /dev/null @@ -1,249 +0,0 @@ ---- -name: smart-file-renaming -description: > - Smart File Renaming skill for Datasite deal rooms. Use this skill whenever a deal - team wants to standardise document names, clean up scanned file names, normalise - naming across similar document types, or improve the professionalism of the data - room before going live. Triggers include: "rename the files", "clean up the file - names", "standardise naming", "the file names are a mess", "fix the document - names", "rename scanned documents", "make the naming consistent", "tidy up the - data room", or any request to improve, clean, or normalise document naming across - a Datasite project. Never apply any rename without explicit user confirmation. - Do not use for document quality or PII checks — use document-quality-check for that. - Never rename files without explicit user confirmation. -metadata: - author: Blueflame AI - version: 1.0.0 - mcp-server: datasite - category: deal-management - tags: [datasite, vdr, m&a, renaming, file-management, blueflame] ---- - -# Smart File Renaming - -You are helping a deal team standardise document names across their Datasite data room. Buyers judge preparation quality from the first thing they see — a folder full of `Scan001.pdf`, `Agreement_FINAL_v3.docx`, and `Copy of Financial Model (2).xlsx` signals a poorly run process. - -**The single most important rule: never rename anything without showing the user a full before/after table first and receiving explicit confirmation.** - ---- - -## Terminology — fileroom vs. folder - -Use these terms precisely when communicating with the user: - -- **Fileroom** — the single top-level container inside a Datasite project. A project typically has one buyer-facing fileroom. It is not a subject area — it is the container that holds all subject areas. -- **Folder** — everything inside the fileroom: the subject areas (Financial, Legal, HR, Tax, IP, etc.) and all sub-levels beneath them. Always call these folders, never filerooms. - -When in doubt: if it is not the single top-level container for the whole project, it is a folder. - - -## Feature Requirements - -| Capability | Free | Requires Blueflame | -|---|:---:|:---:| -| Rename from folder context and filename | ✅ | — | -| Apply naming conventions across all document types | ✅ | — | -| Read inside documents to infer year, counterparty, or jurisdiction | — | ✅ | - -**Without Blueflame:** Renames are based on folder context and filename patterns only. Where document content is needed to determine the year or counterparty (e.g. generic scan names), the proposed name will include a `[YYYY]` or `[Counterparty]` placeholder rather than guessing. - -**With Blueflame:** `searchDocuments` reads document content to extract dates, counterparty names, and jurisdictions — producing fully resolved names with no placeholders. - - - -> ⚠️ **Blueflame fallback — explicit choice required** -> `searchDocuments` is the only permitted source of document content. -> - Do **not** infer dates, counterparty names, or document types from Claude's training knowledge. -> - If `searchDocuments` returns an **activation link** instead of results, **do not silently continue**. Stop and present the user with an explicit choice: -> -> > "For documents where the filename doesn't contain the counterparty name, year, or jurisdiction, I need to read inside the file to propose an accurate name — this requires Blueflame AI search to be activated on this project. -> > 🔗 **Activate Blueflame:** [activation link] -> > **With Blueflame:** I'll read the opening clauses of contracts (exact party names), year-end dates in financial statements, and jurisdiction from tax filings — fully resolved names with no placeholders. -> > **Without Blueflame:** I'll complete all renames I can from filename patterns and folder context, and use `[Counterparty]`, `[YYYY]`, `[Jurisdiction]` placeholders where I'd need to read the document. -> > Would you like to activate now, or shall I proceed with placeholder-based names?" -> -> Wait for the user's response before continuing. - -> **`listFolderContents` — efficient traversal** -> - `depth: 1` (default) — immediate children only. Use for targeted lookups. -> - `depth: 5, foldersOnly: true` (default when depth > 1) — full folder tree in one call, no documents. Use for structural checks. -> - `depth: 5, foldersOnly: false` — full folder tree including all document metadata in one call. Use when building a document inventory. -> - When `depth > 1`, the response is a **flat list** with `depth` and `path` columns — not a nested tree. - -## Step 1 — Orient yourself - -Call `getProjectOverview` to understand the project: company name, sector, and fileroom structure. The company name will be used in naming conventions (e.g. `[Company] - Audited Accounts - FY2025.pdf`). - ---- - -## Step 2 — Crawl and identify files needing attention - -Call `listFolderContents` with `depth: 5, foldersOnly: false` to retrieve the complete document inventory in a single call. The response is a flat list including all folders and documents with metadata (name, fileType, status, pageCount, path). For each document, record: -- Current filename (including extension) -- Metadata ID (needed for `updateContent` later) -- Folder path and VDR index -- File size and page count (to help infer document type) - -Identify files that need renaming using these signals: - -**Never rename — flag for immediate removal from data room:** -These files should not exist in a buyer-facing data room. Flag them as critical issues and do not include them in any rename proposals: -- Internal system or index files: `DOCUMENT_MANIFEST`, `FILE_INDEX`, `FILE_SUMMARY`, `TAX_FILINGS_SUMMARY`, `FOLDER_STRUCTURE`, `INDEX`, `MANIFEST` -- Any file whose name suggests it is a processing artefact, upload log, or internal tool output -- Present these to the user as: “**[N] internal system files found** — these should be deleted before go-live: [list with folder paths]. These have been excluded from the rename proposals.” - -**Definitely rename:** -- Sequential scan names: `Scan001`, `Scan_001`, `IMG_0234`, `Document (3)`, `Untitled` -- Generic upload names: `File`, `New Document`, `Copy of`, `Attachment` -- Chaotic versioning: `FINAL_FINAL`, `USE THIS ONE`, `DO NOT USE`, `v2_revised_final` -- Double extensions: `Contract.pdf.pdf`, `Accounts.docx.pdf` -- Truncated or corrupted names from bulk upload tools - -**Review for standardisation** (may be acceptable but inconsistent with siblings): -- Version suffixes: `v1`, `v2`, `draft`, `revised`, `updated` -- Inconsistent date formats: some files use `2024`, others `FY24`, others `April 2024` -- Inconsistent party naming: `Acme Corp Contract.pdf` next to `Agreement - Acme Corporation.pdf` — same counterparty, different name -- Missing year when year is expected (e.g. `Tax Return.pdf` in a tax folder with multiple years) - ---- - -## Step 3 — Infer document type and content from context - -Before proposing a name, understand what the document actually is. Use two signals: - -**1. Folder context (primary):** A file in `3.1 Audited Financial Statements` is an annual accounts document. A file in `7.2 Employment Agreements` is an employment contract. The folder tells you the document type — use it. - -**2. Document content (when needed):** If the folder context isn't enough to determine the year, counterparty name, or document subtype, use `searchDocuments` on the document to extract: -- The financial year (look for "year ended", "for the year", "FY", "as at 31 December") -- The counterparty name (look for "between [Company] and [X]", "agreement with", "entered into by") -- The jurisdiction (for tax returns: "Federal", "State of California", "HMRC", "Companies House") -- The employee name (for employment agreements: opening clause "This agreement is between [Company] and [Name]") - -Only use content search when the filename alone is genuinely ambiguous. Don't read every document — use judgment. - ---- - -## Step 4 — Apply naming conventions by document category - -Read `references/naming-conventions.md` for the full naming convention tables before proposing renames. - -Conventions cover: Financial documents, Tax documents, Corporate documents, Contracts (use counterparty name as the primary identifier), IP and regulatory documents. - -The general pattern is `[Company] - [Document Type] - [Date or Period].ext` with dates in `YYYY-MM-DD` or `Mon YYYY` format for consistent sort order. Contracts use counterparty name as the lead element. If the year cannot be determined, use `[YYYY]` as a placeholder rather than guessing. - ---- - -## Step 5 — Group proposals by naming pattern - -Before presenting to the user, group the proposed renames by document category. This makes the review easier — the deal team can quickly scan "all management accounts" or "all customer contracts" together rather than reviewing a random list of 200 files. - -Prepare the proposal in this structure per group: - -``` -GROUP: Management Accounts (8 files) -Naming convention: [Company] - Management Accounts - [Mon YYYY].pdf - -Current name → Proposed name -Scan001.pdf → Apex Ltd - Management Accounts - Jan 2025.pdf -Scan002.pdf → Apex Ltd - Management Accounts - Feb 2025.pdf -mgmt accounts march.pdf → Apex Ltd - Management Accounts - Mar 2025.pdf -MA_April2025_FINAL.pdf → Apex Ltd - Management Accounts - Apr 2025.pdf -... -``` - ---- - -## Step 6 — Present to the user for confirmation - -**Before showing the table — mandatory pre-flight extension check:** -For every proposed rename, verify the extension in the proposed name exactly matches the extension in the original filename (case-insensitive). This check must pass 100% before the table is shown. - -- Extract the extension from the original filename: everything after and including the last `.` -- Confirm the proposed name ends with the same extension (normalised to lowercase) -- If any proposed name is missing its extension or has a different extension: **correct it immediately** before showing the table — never show a proposed name without its extension -- Example: if the original is `Scan001.pdf`, the proposed name must end in `.pdf`. If you wrote `Apex Ltd - Audited Accounts - FY2024` without `.pdf`, add it now. - -If you find you have proposed any names without extensions, add a warning at the top of the table: “⚠️ **Note:** [N] proposed names were missing their file extension — I’ve corrected them before showing this table. Please verify the extensions below are correct.” - -Show the full grouped before/after table. Clearly state the total number of renames proposed. - -End with: -> "I've proposed **[N] renames** across **[M] document categories**. Review the table above and let me know: -> - **'Apply all'** — I'll rename everything as proposed -> - **'Apply [group name]'** — I'll rename just that category -> - **Edit any row** — tell me what to change and I'll update the proposal -> - **Skip any file** — tell me which ones to leave as-is -> -> Nothing will be renamed until you confirm." - -**Do not call `updateContent` until the user explicitly confirms.** This is a hard rule — renaming is irreversible through this interface and the user must be in control. - ---- - -## Step 7 — Apply confirmed renames - -Once the user confirms (all or a subset), apply renames using `updateContent`: - -``` -updateContent(projectId, metadataId, name="[proposed name with extension]") -``` - -**Hard rules — check each name immediately before calling `updateContent`:** -- **Extension must be present.** Before every single `updateContent` call, confirm the name string ends with `.pdf`, `.xlsx`, `.docx`, `.pptx`, or whatever the original extension was. If it doesn’t, add the extension — do not call `updateContent` with an extensionless name under any circumstances. -- **Extension must match the original.** The extension in the new name must be identical (lowercase) to the extension in the original filename. Never change `.pdf` to `.docx` or any other type. -- **Extension must be lowercase.** Normalise `.PDF` → `.pdf`, `.XLSX` → `.xlsx` before calling. -- Apply renames one at a time and track success/failure for each. -- If a rename fails, note it and continue with the rest. - -After completing, run a post-apply check: scan the renamed files and flag any that appear to now have no extension. Report: -> “Done — **[N] files renamed** successfully. [If any failed:] **[X] renames failed** — [list them]. [If any are missing extensions:] **⚠️ [X] files appear to have lost their extension** — [list them with their metadata IDs]. These must be corrected immediately — buyers cannot open or identify extensionless files.” - ---- - -## Step 8 — Flag for manual attention - -Some files cannot be confidently renamed without human judgment. Flag these separately rather than guessing: -- Documents where the counterparty name is ambiguous or abbreviated in a way you can't resolve (e.g. `JD Contract 2022.pdf` — is "JD" a person or company?) -- Documents where the year is truly unclear after content search -- Documents in folders where the naming convention isn't obvious from context - -Present these as: "**[N] files flagged for manual review** — I couldn't confidently determine the correct name: [list with current name and folder path]" - ---- - -## Operating principles - -**Batch by pattern, not by folder.** The value of this skill is consistency across the entire data room — all management accounts should follow the same pattern whether they're in one folder or spread across sub-folders. - -**Counterparty name consistency is critical.** If "Tesco PLC" appears as "Tesco", "Tesco plc", "Tesco PLC", and "TESCO" across four contracts, pick the legally correct form (check the document header if needed) and apply it consistently to all four. - -**Preserve all extensions.** A `.pdf` stays a `.pdf`. Never change the file type. - -**Never guess a year.** A wrong year on an audited accounts file is worse than a placeholder `[YYYY]`. If the year isn't clear, mark it. - -**Respect intentional names.** If a file already has a clear, professional, and consistent name (e.g. `Apex Ltd - Audited Accounts - FY2024.pdf`), don't rename it just because you can. Only rename files that genuinely need it. - -## Performance Notes - -- **Never guess a year or counterparty name.** A wrong year on an audited accounts file is worse than a placeholder `[YYYY]`. -- Do not rename every document — only rename files that genuinely need it. Respect intentional names. -- Complete the full before/after table before applying any rename. Do not call `updateContent` until the user explicitly confirms. - ---- - -## Common Issues - -**`getProjectOverview` fails or returns the wrong project** -Check that the Datasite MCP connector is connected (Settings → Extensions → Datasite should show "Connected"). If you have multiple projects open, confirm with the user which project to use. - -**`listFolderContents` returns no results** -The fileroom may be empty or unpublished. Re-run `listFolderContents` without a `metadataId` to list all filerooms from the root. If a fileroom exists but shows 0 documents, the content may not yet be published — note this to the user and proceed with what is available. - -**`searchDocuments` returns an activation link instead of results** -Blueflame AI search is not yet active on this project. Follow the Blueflame prompt in the skill instructions above. Do not attempt to answer using Claude's training knowledge. - -**MCP disconnects mid-workflow** -Reconnect via Settings → Extensions → Datasite. Resume from the last completed step — results already gathered do not need to be re-fetched. - -**`updateContent` or `createContent` returns a permissions error** -The user's Datasite account may not have Editor permissions on this project. Ask them to check their role in Datasite project settings. diff --git a/plugins/datasite/skills/smart-file-renaming/agents/openai.yaml b/plugins/datasite/skills/smart-file-renaming/agents/openai.yaml deleted file mode 100644 index af8410391..000000000 --- a/plugins/datasite/skills/smart-file-renaming/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Smart File Renaming" - short_description: "Standardize Datasite document names before buyer release." - default_prompt: "Use $smart-file-renaming to propose professional names for Datasite files." diff --git a/plugins/datasite/skills/vdr-index-setup/SKILL.md b/plugins/datasite/skills/vdr-index-setup/SKILL.md deleted file mode 100644 index 458020271..000000000 --- a/plugins/datasite/skills/vdr-index-setup/SKILL.md +++ /dev/null @@ -1,255 +0,0 @@ ---- -name: vdr-index-setup -description: > - VDR Index Setup skill for Datasite deal rooms. Use this skill whenever - a user wants to create, propose, design, or set up a Virtual Data Room (VDR) index - or folder structure for a deal. Triggers include: "set up a data room", "create a - VDR index", "build a deal room structure", "prepare the index", "set up the fileroom", - "I need a data room for [deal/company]", or any request to organise or structure - documents for due diligence. Also triggers when a user wants to replicate an existing - deal room structure or import an index from a spreadsheet or reference deal. This skill - MUST be used whenever the user is starting a new deal room or wants to customise the - folder hierarchy before documents are uploaded. - Do not use to audit or review an existing data room — use gap-analysis, - document-quality-check, or risk-analysis-audit for that. -metadata: - author: Blueflame AI - version: 1.0.0 - mcp-server: datasite - category: deal-management - tags: [datasite, vdr, m&a, index, folder-structure, setup] ---- - -# VDR Index Setup - -You are helping a deal team on Datasite create a professional, customised Virtual Data Room (VDR) index — the folder hierarchy buyers and advisors will navigate during due diligence. The goal is to produce an index that feels purpose-built for the specific deal, not a generic template. - -## Terminology — fileroom vs. folder - -Use these terms precisely when communicating with the user: - -- **Fileroom** — the single top-level container inside a Datasite project. A project typically has one buyer-facing fileroom. It is not a subject area — it is the container that holds all subject areas. -- **Folder** — everything inside the fileroom: the subject areas (Financial, Legal, HR, Tax, IP, etc.) and all sub-levels beneath them. Always call these folders, never filerooms. - -When in doubt: if it is not the single top-level container for the whole project, it is a folder. - - -## Feature Requirements - -| Capability | Free | Requires Blueflame | -|---|:---:|:---:| -| Propose and create folder index | ✅ | — | -| Read project context and sector | ✅ | — | -| Push structure to Datasite | ✅ | — | -| Invite team members | ✅ | — | - -**This skill is fully free.** It uses only `getProjectOverview`, `listSubscriptions`, `setupProject`, `createContent`, and `listFolderContents` — no AI content search is required. - - - -> ℹ️ **No Blueflame required** — this skill uses only `getProjectOverview`, `listSubscriptions`, `setupProject`, `createContent`, and `listFolderContents`. It never calls `searchDocuments`. All functionality is available without Blueflame activation. - -> **`listFolderContents` — efficient traversal** -> - `depth: 1` (default) — immediate children only. Use for targeted lookups. -> - `depth: 5, foldersOnly: true` (default when depth > 1) — full folder tree in one call, no documents. Use for structural checks. -> - `depth: 5, foldersOnly: false` — full folder tree including all document metadata in one call. Use when building a document inventory. -> - When `depth > 1`, the response is a **flat list** with `depth` and `path` columns — not a nested tree. - -## Step 1 — Read the project context first - -**Before asking the user anything**, call `getProjectOverview` on the current project. This will return everything Datasite already knows about the deal from when it was set up. Extract and map the following fields: - -| Blueflame field | Maps to | Example values | -|---|---|---| -| `name` | Company / deal name | "Project Falcon" | -| `industryType` | Sector | TECHNOLOGY_MEDIA_TELECOM → Tech/SaaS; LIFE_SCIENCES_HEALTHCARE → Healthcare; CONSUMER → Retail/Consumer; INDUSTRIALS_TRANSPORT_DEFENSE → Manufacturing/Transport; ENERGY_MINING_OIL_GAS → Oil & Gas; FINANCIAL_SERVICES → Financial Services; REAL_ESTATE → Real Estate | -| `useCase` | Transaction type | COMPANY_SALE / DIVESTITURE → M&A sell-side; ACQUISITION → buy-side; MERGER → merger; PRIVATE_EQUITY_FUNDRAISING / ADD_ON → PE; VC_FUNDING_ROUND / FUNDRAISING → capital raise; RESTRUCTURING_OR_INSOLVENCY → restructuring | -| `transactionValue` | Size / complexity | LESS_THAN_US_10_M → SME; BETWEEN_US_10_M_AND_100_M → lower mid-market; BETWEEN_US_100_M_AND_500_M → mid-market; BETWEEN_US_500_M_AND_1_B / GREATER_THAN_US_1_B → large-cap | -| `datacenter` | Geography hint | USA → US/North American; DEU → European (assume GDPR, EU regulatory); AUS → Australian | - -With these four fields you already know the company name, sector, deal type, approximate size, and a geography signal. **Do not ask the user to repeat this information.** - -### What to ask about (only the genuine gaps) - -After reading the project, there may be a small number of things worth clarifying. Ask only what you actually need, in a single short message — not a form: - -- **Specific industry sub-type** if `industryType` is broad and it meaningfully changes the index. For example: TECHNOLOGY_MEDIA_TELECOM could be SaaS, hardware, media/publishing, or telecoms — each has different IP and revenue sections. CONSUMER could be retail, food & beverage, or e-commerce. If the project name makes it obvious (e.g. "Project Falcon — CloudSoft Ltd"), skip this. -- **Jurisdiction precision** if the datacenter alone is ambiguous. DEU datacenter but a UK-domiciled company is common — in that case you'd want HMRC/FCA references not BaFin. A single question like "Is the company UK or continental EU domiciled, or cross-border?" is enough. -- **Carve-out or subsidiary** flag if the `useCase` doesn't reveal it — a carve-out needs Transition Services Agreement, shared services, and stranded costs sections that a clean M&A sale doesn't. - -If none of these gaps exist (e.g. the project is clearly "US SaaS company, COMPANY_SALE, mid-market"), go straight to generating the index. Don't ask questions for the sake of it. - -### Optional: reference structure - -Also offer this once, briefly: "If you'd like me to base the index on an existing deal structure, you can attach the index as a spreadsheet or give me a deal name and I'll read it from the platform." If the user provides one, go to **Step 1b** before generating. - -> **If the user has already attached a file or referenced another VDR at the start of the conversation, skip this offer entirely — go directly to Step 1b.** Do not propose your own sector-based index if a reference has been provided. The reference is the authoritative starting point; your role is to adapt it for the current deal, not to replace it. - -## Step 1b — Read a reference index (if provided) - -**If the user uploads a spreadsheet:** -Read the file. Extract the folder hierarchy from the Index and Title columns. - -**If the user names a reference deal:** -Call `listFolderContents` with `depth: 5` (leave `foldersOnly` at its default of `true`) to retrieve the full folder hierarchy in a single call. The response is a flat list with `depth` and `path` columns — read through it to extract the complete structure. - -After reading either source, **ask the user one question before proceeding:** - -> "I've read the reference structure — [N] folders across [N] levels. How would you like me to use it? -> - **Use as-is** — push it to the data room exactly as provided, no changes -> - **Suggest additions** — keep the reference intact and flag any sections that are typically expected for a [sector] deal but aren't currently included -> - **Full adaptation** — use it as a base and apply deal-specific tailoring for [company name] ([sector], [deal type])" - -Wait for the user's choice before doing anything else. - -- If **use as-is**: skip Step 2 entirely, go straight to Step 4 (confirm and push). Do not modify, rename, or reorder anything. -- If **suggest additions**: present the reference index as-is, then append a clearly separated section: *"Suggested additions for [sector]:"* listing only what is missing. The user decides what to include before anything is pushed. -- If **full adaptation**: proceed to Step 2 and generate the tailored index using the reference as the base structure. - -## Step 2 — Generate the proposed index - -Using the project profile you've assembled, produce a complete, numbered folder hierarchy. Read `references/sector-templates.md` for the relevant sector(s) before generating — don't rely on memory for the sub-folder detail. - -**How to tailor the index:** - -**Sector** — pull the relevant sector section from the reference templates. Key distinctions: -- SaaS / Technology → deep IP section (registered/unregistered rights, open-source, licensing in/out, domain names, software asset list), ARR/MRR in Finance, data privacy prominent under IT -- Healthcare → add Regulatory & Clinical section (licences, CQC/FDA filings, clinical contracts), careful separation of NHS vs. private revenue -- Manufacturing → add Plant & Equipment, Supply Chain, and Environmental sections -- Oil & Gas → add Reserves, Environmental & Regulatory, Concession Agreements sections -- Retail → add Leasehold Properties, Brand & Licensing, Supplier Contracts sections -- Financial Services → add Regulatory Capital, FCA/SEC authorisations, Client Money sections - -**Transaction type:** -- M&A sell-side (COMPANY_SALE, DIVESTITURE) → include Closing Documents section at the end -- PE / add-on (PRIVATE_EQUITY_FUNDRAISING, ADD_ON) → stronger management/governance sections, lighter closing docs, include Management Accounts and KPIs -- Carve-out (DIVESTITURE where partial) → add Transition Services Agreement, Shared Services, Stranded Costs, and Intercompany Agreements sections -- Capital raise (VC_FUNDING_ROUND, FUNDRAISING) → include Investor Presentations, Cap Table History, Use of Proceeds, Funding History -- Restructuring → include Insolvency Proceedings, Creditor Agreements, Security Documents - -**Geography:** -- US / USA datacenter → IRS/SEC/EIN references, Federal/State/Local tax split, FCPA under Compliance -- European / DEU datacenter → GDPR sub-folder prominent under IT/Data, EU regulatory references, VAT returns in Tax -- UK-domiciled → HMRC references, FCA/CMA in Regulatory, Companies House in Corporate, use "Articles of Association" not "By-Laws" -- Cross-border → duplicate Tax and Legal sections per jurisdiction (e.g. "Tax — UK", "Tax — Germany") - -**Size / complexity:** -- SME (< $10M) → 2–3 levels, combine Accounting into Finance, lighter HR section -- Lower mid-market ($10–100M) → standard 3 levels, most sections present but not fully expanded -- Mid-market ($100–500M) → full 3–4 levels as in the base templates -- Large-cap (> $500M) → maximum depth, consider splitting into multiple filerooms by workstream - -**Years of financial and corporate history** — set automatically, never ask the user: -- Standard M&A sell-side (mid-market and below) → **3 years** audited financials (last 3 closed years, i.e. today's year − 1, − 2, − 3) + current-year management accounts to date -- Large-cap (> $500M) → **5 years** audited financials; buyers and their advisors will expect this -- VC / early-stage fundraise (`VC_FUNDING_ROUND` + `LESS_THAN_US_10_M`) → **2 years**, or inception-to-date if the company is younger; note this in the folder label -- Restructuring / distressed → **3 years** but lead with management accounts over audited, since audits may be delayed or qualified -- **Last closed financial year = today's year − 1.** Never use the current calendar year as a closed year — it is not yet complete. In 2026, the last closed year is FY2025. -- Always use actual closed years (e.g. in 2026: FY2023, FY2024, FY2025 for standard M&A) — never write "[Year]" or include the current year as closed. -- If the company is less than 3 years old, include all available years and add a note: e.g. "Audited Financial Statements (FY2024, FY2025 — include inception-to-date accounts if prior history unavailable)" -- Apply the same year logic to corporate history folders (board minutes, tax returns, regulatory filings) — use the same horizon as financials for consistency - -**Format of the proposal:** -Present the index as a clean numbered hierarchy with indentation: - -``` -1. General Information - 1.1 Corporate Organisation - 1.1.1 Group Structure Chart - 1.1.2 Certificate of Incorporation - 1.1.3 Articles of Association - 1.1.4 Board Minutes and Resolutions (last 3 years) - 1.2 Shareholders - 1.2.1 Shareholder Register - 1.2.2 Shareholder Agreements - 1.2.3 Cap Table -2. Finance - 2.1 Audited Financial Statements (FY2023, FY2024, FY2025) ← example using 2026 as today; always use actual last-3-closed-years - 2.2 Management Accounts (monthly, last 24 months) - ... -``` - -Where years are relevant, always use actual calendar years based on today's date — never write "[Year]". - -Close with: "This is my proposed index for [Company Name]. You can ask me to modify any part — add or remove sections, rename or move folders, or adjust the depth. Once you're happy I can push it to the data room, or export it to Excel first." - -## Step 3 — Iterate with the user - -Handle all edit requests conversationally: - -- **Add a section** → insert in a logical position and renumber. Briefly note where you've placed it if it's not obvious. -- **Remove a section** → confirm and renumber. If it has children, confirm those go too. -- **Rename** → apply to that folder only, unless the user says otherwise. -- **Move** → relocate and renumber throughout. Adjust child numbering if the hierarchy level changes. -- **Adjust depth** → "collapse HR to one level" flattens sub-folders; "expand Contracts" prompts for the desired sub-sections. - -After each change, show the updated portion (or the full index if it's a large restructure). Confirm the complete final state before moving to Step 4. - -## Step 4 — Confirm before pushing - -Show a clear confirmation gate before creating anything: - -> "Here's the final index for **[Company Name]** — **[N] folders** across **[N] levels**. Ready to create this in the **[Fileroom Name]** data room. Shall I go ahead?" - -Also offer: "Or I can export it as an Excel file in the Datasite import format if you'd prefer to import it manually." - -Only proceed once the user confirms. - -## Step 5 — Push the index to Datasite - -> ⚠️ **PREPARE projects:** These use a Staging Folder (sandbox) exclusively. All content must be created within the Staging Folder — do not create filerooms or folders outside it. Use `listFolderContents` to locate the sandbox (type: SANDBOX, name: "Staging Folder") before creating any content. - - -**Two options — choose based on whether the project already exists:** - -**Option A — New project (project does not yet exist):** -Use `listSubscriptions` to find the available subscription, then call `setupProject` with the confirmed folder tree as a `contentTree` JSON array. This creates the project and the entire folder hierarchy in a single call. Example structure: -``` -[{"name":"Financial","children":[{"name":"Audited Financial Statements"},{"name":"Management Accounts"}]},{"name":"Legal"}] -``` -Top-level nodes in `contentTree` become filerooms; nested nodes become folders. - -**Option B — Project already exists:** -1. `listFolderContents` (no `metadataId`, `depth: 1`) — check if a fileroom already exists. Returns immediate top-level items only. If a fileroom exists, ask the user whether to add the index inside it or create a new one. -2. `createContent` — create folders inside the existing fileroom. Pass the full tree as `contentTree` with the fileroom's `metadataId` as `parentId`. - -**Workflow:** -1. `listFolderContents` (no `metadataId`, `depth: 1`) — check if a fileroom already exists. -2. `createContent` — create the top-level fileroom if needed. -3. `createContent` — pass the full folder tree as `contentTree` so the entire hierarchy is created in one call. - -**Error handling:** if a folder fails, note it and continue. Report failures at the end with the folder path so the user can investigate. - -**On completion:** -> "Done ✓ — **[N] folders** created in **[Fileroom Name]**. Top-level sections: [list]. -> 🔗 Open in Datasite: `https://app.global.datasite.com/en/platform/prepare/[projectId]/overview` -> Let me know if you’d like to adjust anything or invite team members." - -**Important:** Always use the URL format above when linking to a Datasite project — `https://app.global.datasite.com/en/platform/prepare/{projectId}/overview`. Never construct a Datasite URL from memory or training knowledge; the format above is the only correct one. - ---- - -## Reference materials - -Read `references/sector-templates.md` for the full folder structures for each sector. Load only the section(s) relevant to the current deal — there's no need to read the whole file. - -**Sectors covered:** Due Diligence (universal baseline), Technology, Healthcare, Healthcare Capital Raise, Manufacturing, Retail, Financial Services, Legal, Oil & Gas, Real Estate, Telecommunications, Transportation, Defence. - ---- - -## Common Issues - -**`getProjectOverview` fails or returns the wrong project** -Check that the Datasite MCP connector is connected (Settings → Extensions → Datasite should show "Connected"). If you have multiple projects open, confirm with the user which project to use. - -**`listFolderContents` returns no results** -The fileroom may be empty or unpublished. Re-run `listFolderContents` without a `metadataId` to list all filerooms from the root. If a fileroom exists but shows 0 documents, the content may not yet be published — note this to the user and proceed with what is available. - -**`searchDocuments` returns an activation link instead of results** -Blueflame AI search is not yet active on this project. Follow the Blueflame prompt in the skill instructions above. Do not attempt to answer using Claude's training knowledge. - -**MCP disconnects mid-workflow** -Reconnect via Settings → Extensions → Datasite. Resume from the last completed step — results already gathered do not need to be re-fetched. - -**`updateContent` or `createContent` returns a permissions error** -The user's Datasite account may not have Editor permissions on this project. Ask them to check their role in Datasite project settings. diff --git a/plugins/datasite/skills/vdr-index-setup/agents/openai.yaml b/plugins/datasite/skills/vdr-index-setup/agents/openai.yaml deleted file mode 100644 index 69e959f81..000000000 --- a/plugins/datasite/skills/vdr-index-setup/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "VDR Index Setup" - short_description: "Design a Datasite VDR folder structure for a new deal." - default_prompt: "Use $vdr-index-setup to create a Datasite VDR index for this deal." diff --git a/plugins/deepnote/.app.json b/plugins/deepnote/.app.json deleted file mode 100644 index 1be641c8f..000000000 --- a/plugins/deepnote/.app.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "apps": { - "deepnote": { - "id": "asdk_app_69fb51f9519081919c1f3e44ea9a5a05" - } - } -} diff --git a/plugins/deepnote/.codex-plugin/plugin.json b/plugins/deepnote/.codex-plugin/plugin.json deleted file mode 100644 index d7996d700..000000000 --- a/plugins/deepnote/.codex-plugin/plugin.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "deepnote", - "version": "0.1.5", - "description": "Use Deepnote's collaborative data workspace to explore data, automate notebooks and SQL workflows, and turn analysis into shareable results.", - "author": { - "name": "Deepnote", - "url": "https://deepnote.com" - }, - "homepage": "https://deepnote.com", - "repository": "https://github.com/openai/plugins/tree/main/plugins/deepnote", - "license": "Apache-2.0", - "keywords": [ - "deepnote", - "chatgpt-app", - "openai", - "notebooks", - "data", - "analytics" - ], - "skills": "./skills/", - "apps": "./.app.json", - "interface": { - "displayName": "Deepnote", - "shortDescription": "Explore data and automate analysis in Deepnote", - "longDescription": "Deepnote gives teams a collaborative workspace for notebooks, SQL, apps, and data workflows. This plugin helps OpenAI work with connected Deepnote projects, inspect and run notebooks, and turn workspace context into useful analysis and shareable results.", - "developerName": "Deepnote", - "category": "Data & Analytics", - "capabilities": [ - "Interactive", - "Read", - "Write" - ], - "websiteURL": "https://deepnote.com", - "privacyPolicyURL": "https://deepnote.com/privacy", - "termsOfServiceURL": "https://deepnote.com/terms", - "defaultPrompt": [ - "Summarize my Deepnote workspace and recent notebook activity", - "Explore active data connections to my Deepnote workspace", - "Create a notebook exploring S&P 500 vs. Nasdaq gains over the last 5 years" - ], - "brandColor": "#3793EF", - "composerIcon": "./assets/composer.svg", - "logo": "./assets/logo.png", - "logoDark": "./assets/logo-dark.png", - "screenshots": [] - } -} diff --git a/plugins/deepnote/README.md b/plugins/deepnote/README.md deleted file mode 100644 index 6e84bf3d3..000000000 --- a/plugins/deepnote/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# Deepnote Plugin - -OpenAI plugin for Deepnote. It packages Deepnote skills for searching workspaces, inspecting notebooks, generating project and notebook links, mapping integration usage and cached table structure, reading Deepnote docs, creating, updating, and reorganizing notebook structure, running notebooks, and summarizing run history, status, and outputs. - -## What's Included - -- `skills/deepnote` - routing guidance for Deepnote app tool workflows -- `skills/deepnote-links` - workspace-aware project and notebook link construction -- `skills/deepnote-notebooks` - notebook inspection, review, inputs, blocks, SQL, and outputs -- `skills/deepnote-notebook-editing` - project, notebook, and block creation, block updates, and block reordering workflows -- `skills/deepnote-data-execution` - notebook run, run history, input, integration, and snapshot-output workflows - -## Requirements - -- A Deepnote account with access to the target workspace -- The Deepnote app connected -- OAuth authorization for the Deepnote workspace account you want OpenAI to use - -Authentication is handled by OAuth through the connected Deepnote app. No local credential setup is required for this official app-backed plugin. - -## Behavior Notes - -The OAuth connection acts with the permissions of the connected Deepnote user. A viewer can read viewer-accessible resources; editor and admin accounts can perform the matching write workflows when the relevant app tools are available. - -The current tool surface supports cached integration table structure, but does not promise live database schema refreshes, row previews, single-block execution, environment mutation, permission changes, publishing, or scheduling changes. Skills should say when a requested capability is not exposed by the current app tools. - -## Good First Prompts - -- `Search my Deepnote workspace for customer retention notebooks.` -- `Which Deepnote workspace am I connected to?` -- `Give me links to my Deepnote projects.` -- `Inspect this Deepnote notebook and summarize its inputs.` -- `Create a Deepnote project named Revenue Analysis.` -- `Create a notebook in this Deepnote project and add starter markdown and code blocks.` -- `Update this Deepnote notebook block with the revised SQL.` -- `Add a SQL block to this notebook using my Snowflake integration.` -- `Move these Deepnote notebook blocks to the top of the notebook.` -- `Show me the recent runs for this Deepnote notebook.` -- `Run this Deepnote notebook with customer_name set to Acme.` -- `List Deepnote integrations matching Snowflake.` -- `Show cached tables for my Snowflake integration.` -- `Show me where this Deepnote integration is used.` -- `Look up the Deepnote docs for scheduled notebooks.` diff --git a/plugins/deepnote/assets/composer.svg b/plugins/deepnote/assets/composer.svg deleted file mode 100644 index d1f7d311a..000000000 --- a/plugins/deepnote/assets/composer.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - diff --git a/plugins/deepnote/assets/logo-dark.png b/plugins/deepnote/assets/logo-dark.png deleted file mode 100644 index 3e5ef17fe..000000000 Binary files a/plugins/deepnote/assets/logo-dark.png and /dev/null differ diff --git a/plugins/deepnote/assets/logo.png b/plugins/deepnote/assets/logo.png deleted file mode 100644 index f82def9a8..000000000 Binary files a/plugins/deepnote/assets/logo.png and /dev/null differ diff --git a/plugins/deepnote/skills/deepnote-data-execution/SKILL.md b/plugins/deepnote/skills/deepnote-data-execution/SKILL.md deleted file mode 100644 index 2e4edd070..000000000 --- a/plugins/deepnote/skills/deepnote-data-execution/SKILL.md +++ /dev/null @@ -1,106 +0,0 @@ ---- -name: deepnote-data-execution -description: Use when running Deepnote notebooks, inspecting notebook inputs, reviewing integration references and cached table structure, listing run history, or interpreting run status and snapshot outputs through the Deepnote app tools. ---- - -# Deepnote Data And Execution - -## Available Context - -Use the connected Deepnote app tools for the execution and context they currently expose: - -- `get_notebook` for notebook blocks, input variables, last-run metadata, and integration references visible in blocks. -- `list_integrations` for workspace integration names and types. -- `get_integration` for integration details and cached table structure, optionally filtered by `databaseName`, `schemaName`, or exact `tableName`. -- `list_integration_project_usages`, `list_integration_notebook_usages`, and `list_integration_block_usages` for direct integration usage mapping. -- `create_run` to start full-notebook execution, optionally with input values. -- `list_notebook_runs` for historical notebook runs, newest first, with `pageSize` and `pageToken` pagination. -- `get_run` for run status, errors, completion time, and run snapshots. When `snapshotDelivery` is omitted, it returns a short-lived `snapshotDownloadUrl` when a snapshot is available; this is equivalent to `snapshotDelivery: "downloadUrl"`. Request `snapshotDelivery: "inline"` only when you need `snapshotContent` in the tool response. -- `get_me` for the authenticated workspace, connected user, and caller access level when execution permissions or workspace identity matter. - -Use `get_integration` for cached table and column structure when the user asks about integration schemas, tables, columns, or whether a table exists. Do not claim live database introspection, table previews, query previews, file metadata, or environment configuration unless a current Deepnote app tool explicitly exposes that data. - -## Execution Workflow - -1. Read the notebook context first with `get_notebook`. -2. Identify whether the user needs a fresh run, a specific run status, or notebook run history. -3. If the notebook has inputs and the user supplied values, map values to the exact input `name` fields returned by `get_notebook`. -4. Check whether execution may mutate data, call external services, trigger schedules, or consume significant compute. -5. Use `create_run` only for full-notebook execution by `notebookId`; the Deepnote app does not currently expose single-block execution. -6. If `create_run` returns a tool error, report that error and stop; do not call `get_run` unless a run ID was returned. -7. Use `get_run` to inspect status, errors, completion time, and snapshot availability before reporting results. For routine status checks, omit `snapshotDelivery` so the default download URL delivery is used; request `snapshotDelivery: "inline"` when you need to inspect notebook outputs, snapshot errors, or result details. - -Before starting a run, inspect the notebook for cells that print environment variables, secrets, credentials, or entire configuration objects. If found, warn the user and get explicit confirmation before running. Also warn before running notebooks that start servers, send bulk requests, call external services, or mutate data. - -## Run History - -Use `list_notebook_runs` when the user asks about recent runs, failed runs, run history, or anything older than the latest run metadata returned by `get_notebook`. The tool returns runs newest first with `runId`, `notebookId`, `status`, `createdAt`, `completedAt`, and `pagination`. - -For short history requests, call `list_notebook_runs` with the default `pageSize` of 20. For broader audits, use `pageSize: 100` and follow `pagination.nextPageToken` while `pagination.hasMore` is true, stopping once you have enough evidence for the user's question. - -Use `get_run` only after selecting a specific run that needs detail, snapshot availability, output inspection, or failure debugging. If the user asks "why did the last run fail?" and no run ID is provided, list recent runs first, pick the newest failed run, then call `get_run` for that run. - -## Cached Integration Structure - -Use `list_integrations` to resolve an integration name or type to an ID, then call `get_integration` for cached table structure. The response includes integration details plus `tables`, where each table has `name`, `schema`, optional `database`, and cached `columns` with names and database-native types. - -Use `databaseName`, `schemaName`, and `tableName` filters when the user asks about a specific database, schema, or exact table. These filters apply to cached structure rows; an empty table list means no matching cached structure is visible through the app tools, not proof that the live database has no such table. - -When reporting cached structure, say it is cached. Do not present it as a fresh live database scan, and do not claim access to row previews or query results unless you obtained them from a notebook run snapshot or another exposed app tool. - -## Notebook Run Inputs - -`create_run` accepts an optional `inputs` object. Keys must be notebook input names from `get_notebook`, not labels or block IDs. Values must match the input block type: - -- Text, textarea, file, date, slider, and single-select inputs use strings. -- Checkbox inputs use booleans. -- Multi-select inputs use arrays of strings. -- Date-range inputs use a string or an array of exactly two strings. -- Slider values must be numeric strings. - -If the user provides a label instead of a name, inspect `get_notebook` inputs and map it to the closest input `name` only when the match is unambiguous. Otherwise ask for clarification. Run input values apply only to the new run and do not update the notebook's saved defaults. - -## Run Snapshots - -When `snapshotDelivery` is omitted, `get_run` returns `snapshotDownloadUrl` for available `.snapshot.deepnote` files and `snapshotContent: null`; this is equivalent to `snapshotDelivery: "downloadUrl"`. The URL is short-lived and grants access to the run snapshot, so do not paste it into the final answer unless the user asks for a download link or file handoff. - -Use `snapshotDelivery: "inline"` when the user asks you to inspect outputs, summarize results, diagnose a failed run from snapshot details, map visible references from the snapshot, or otherwise reason over the snapshot content. Inline snapshots can be large and sensitive, so summarize the relevant blocks, outputs, failures, or data shape instead of dumping raw content. - -If the current Deepnote app tool schema does not expose `snapshotDelivery`, use the fields returned by `get_run` as-is and do not invent `snapshotContent` or `snapshotDownloadUrl`. - -## Sensitive Outputs - -When snapshot content, snapshot download URLs, or errors include sensitive, proprietary, personal, or production-like data, minimize exposure in the response. Summarize the result, shape, quality issues, aggregates, or failure mode instead of dumping raw records, presigned URLs, or long logs. - -## Environment Changes - -The Deepnote app tools currently do not expose environment mutation tools. Do not claim to change package versions, environment images, hardware, integrations, credentials, secrets, scheduled runs, or shared app settings unless a current tool explicitly supports that action. - -## Reporting Results - -For successful runs, include the executed notebook name or ID, run ID, status, any input overrides that are safe to mention, and the important result from inline snapshot content when you requested it. If you only have `snapshotDownloadUrl`, mention that a snapshot is available without exposing the URL by default. For failures, include concise error detail and the next fix to try. If a run fails before it starts, such as a workspace or parallel run limit, report the user-facing API error directly. Avoid pasting long logs unless the user asks for them. - -Keep run reports brief and information-dense unless the user asks for detail. Prefer one compact run table plus the most important result or first actionable error. Do not paste long logs, raw snapshots, or full notebook outputs by default. - -Prefer this run summary shape: - -| Field | Value | -| --- | --- | -| Notebook | `Notebook name` | -| Run ID | `run-id` | -| Status | `success`, `failed`, `pending`, or `running` | -| Started | `YYYY-MM-DD HH:MM UTC` | -| Completed | `YYYY-MM-DD HH:MM UTC` or `Still running` | -| Inputs | `safe input summary` or `None` | -| Result | `short result summary` | - -For failed or stuck runs, use a debugging report: - -| Check | Finding | -| --- | --- | -| Run state | `failed`, `pending`, or `running for N minutes` | -| First actionable error | `short error text` | -| Likely cause | `missing input`, `missing file`, `server not listening`, `dependency failure`, or `unknown from app tools` | -| Safe next step | `inspect notebook`, `rerun with inputs`, `start serving notebook`, or `manual Deepnote action needed` | - -When inspecting a large run snapshot, request inline delivery only when necessary, then summarize block counts, failed blocks, final outputs, and the first actionable error instead of pasting the snapshot. diff --git a/plugins/deepnote/skills/deepnote-links/SKILL.md b/plugins/deepnote/skills/deepnote-links/SKILL.md deleted file mode 100644 index ab76da0b7..000000000 --- a/plugins/deepnote/skills/deepnote-links/SKILL.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -name: deepnote-links -description: Use when a task asks for Deepnote URLs, links, project links, notebook links, workspace links, share links, UTM/campaign links, or when a Deepnote response should include clickable links built from Deepnote app project, notebook, or workspace data. ---- - -# Deepnote Links - -Use this skill to build user-facing Deepnote web links from Deepnote app tool data. Prefer links grounded in `get_me`, `list_projects`, `search`, and `get_notebook` responses instead of guessing from names alone. Every project and notebook link built from Deepnote app data must include the UTM parameters below. - -## Inputs To Resolve - -1. Call `get_me` when workspace-aware links are useful. Use `workspace.id` and `workspace.slug`; do not use OAuth tokens, user email, or other auth details in links or summaries. -2. Resolve the project with `list_projects` or `search`. Use the project `id`, `name`, and `slug` if the app response exposes one. -3. Resolve notebook links with `get_notebook` when possible. Use the notebook `id`, `name`, and parent project data. -4. If the exact project or notebook is ambiguous, ask a short clarification or provide a compact candidate list with links only for unambiguous matches. - -## Creation Link Rules - -When linking after a creation workflow, use the resource IDs returned by the write tools as the source of truth: - -- If `create_notebook` returned a notebook, build the notebook link for that returned notebook ID. Do not substitute the first notebook on the project or the default notebook created by `create_project`. -- If `create_project` created a project and no separate `create_notebook` call was made, use the default notebook created with the project only when a notebook link is needed for that active notebook. -- If both the project default notebook and a later `create_notebook` result are present, the later `create_notebook` result is the notebook to link unless the user explicitly asks for the default notebook. -- If parent project data is missing for the created notebook, call `get_notebook` for the target notebook ID or use the known project ID from the creation workflow before constructing the link. - -## URL Shapes - -Use the production web origin `https://deepnote.com` for Deepnote app links. Do not derive the web origin from API or tool hosts. - -Prefer workspace-scoped links when `get_me` returns both `workspace.slug` and `workspace.id`: - -```text -workspaceSlugWithId = {workspace.slug}-{workspace.id} -workspace link = {origin}/workspace/{workspaceSlugWithId} -project link = {origin}/workspace/{workspaceSlugWithId}/project/{projectSegment} -notebook link = {origin}/workspace/{workspaceSlugWithId}/project/{projectSegment}/notebook/{notebookSegment} -``` - -If workspace data is not available, use the non-workspace project route: - -```text -project link = {origin}/project/{projectSegment} -notebook link = {origin}/project/{projectSegment}/notebook/{notebookSegment} -``` - -## Slug Segments - -Use the most canonical segment available: - -1. If the app response exposes a `slug`, use it. -2. Otherwise, for simple names, build a readable segment as `{slugifiedName}-{id}`. -3. If exact slugification is uncertain, the name is missing, or the name has unusual characters, use the ID alone. - -Deepnote project routing accepts UUID-only project segments, so `{project.id}` is the safest fallback. Notebook routing uses ID-only segments when a notebook name is not available, so `{notebook.id}` is the safest notebook fallback. - -Deepnote's readable slugs are created with strict slugification: spaces become hyphens, `/` becomes `-`, unsafe characters are stripped or normalized, and case is preserved. Examples: - -```text -Subject Tracker + 0508fc64-b2c8-4982-b6a0-2590c94b6000 -=> Subject-Tracker-0508fc64-b2c8-4982-b6a0-2590c94b6000 - -folder/notebook 10% + a1b2c3d4 -=> folder-notebook-10percent-a1b2c3d4 -``` - -## Optional Suffixes - -- File paths, when exposed and requested, append after the project segment as `/{encodeURIComponent(filePath)}`. -- Cell or block anchors append as `#anchor`. -- Only generate published app links such as `/app/{authorSlug}/{projectSegment}` or `/streamlit-apps/{streamlitAppId}` when Deepnote app data explicitly exposes the published author slug or Streamlit app ID. - -## UTM Parameters - -For every project and notebook link built from Deepnote app data, add OpenAI attribution query parameters: - -```text -https://deepnote.com/?utm_source=openai&utm_medium=mcp&utm_campaign=openaimcp&utm_content={notebook_id}&utm_term={tool_name} -``` - -Use these values exactly; braces mark placeholders and are not part of the final URL: - -- `utm_source=openai` -- `utm_medium=mcp` -- `utm_campaign=openaimcp` -- `utm_content={notebook_id}` -- `utm_term={tool_name}` - -For notebook links, set `utm_content` to the notebook ID. For project-only links, set `utm_content` to the project ID; when a project link represents a specific notebook's parent project, use that notebook ID instead. - -Set `utm_term` to the Deepnote app tool or workflow that produced or grounded the link, such as `list_projects`, `search`, `get_notebook`, or `workspace_summary`. Use lowercase snake_case values and URL-encode if needed. - -For links to newly created notebooks, set `utm_content` to the created notebook ID and prefer `utm_term=create_notebook`; use `utm_term=get_notebook` when a follow-up `get_notebook` call provided the fields needed to construct the link. - -Add UTM parameters before any URL fragment. Use `?` when the URL has no existing query string, otherwise use `&`. Preserve non-UTM query parameters if they already exist, and replace any existing `utm_source`, `utm_medium`, `utm_campaign`, `utm_content`, or `utm_term` values instead of duplicating them. - -## Response Style - -Return Markdown links with human-readable labels: - -```markdown -[Project Name](https://deepnote.com/workspace/workspace-slug-workspace-id/project/project-id?utm_source=openai&utm_medium=mcp&utm_campaign=openaimcp&utm_content=project-id&utm_term=list_projects) -[Notebook Name](https://deepnote.com/workspace/workspace-slug-workspace-id/project/project-id/notebook/notebook-id?utm_source=openai&utm_medium=mcp&utm_campaign=openaimcp&utm_content=notebook-id&utm_term=get_notebook) -``` - -For lists, inventories, and workspace summaries, put links in the `Project` or `Notebook` column and keep IDs in a separate column only when they help disambiguate. When a table has a `Notebook` column, hyperlink the notebook name itself. If a link cannot be built safely because workspace, project, or notebook data is missing, say which field is missing and how to resolve it. diff --git a/plugins/deepnote/skills/deepnote-notebook-editing/SKILL.md b/plugins/deepnote/skills/deepnote-notebook-editing/SKILL.md deleted file mode 100644 index f9aa13307..000000000 --- a/plugins/deepnote/skills/deepnote-notebook-editing/SKILL.md +++ /dev/null @@ -1,87 +0,0 @@ ---- -name: deepnote-notebook-editing -description: Use when creating Deepnote projects or notebooks, adding or updating blocks or cells, moving existing blocks, scaffolding notebook content, inserting SQL/code/markdown/input blocks, or otherwise editing notebook structure through the Deepnote app tools. ---- - -# Deepnote Notebook Editing - -## When To Use - -Use this skill when the user asks to create a Deepnote project, create a notebook, add a block or cell, update an existing block or cell, move or reorder existing blocks, scaffold starter notebook content, insert code, SQL, markdown, or input blocks, or make a structural notebook edit supported by the current Deepnote app write tools. - -This workflow requires the connected Deepnote app to expose the write tools needed for the requested edit. Use `create_project`, `create_notebook`, and `create_block` for creation workflows; use `update_block` for changing existing block content or SQL integration; use `reorder_notebook_blocks` for moving existing blocks. If the required tool is not visible in the current session, do not claim editing support; explain which app tool is missing. - -The editing surface covered by this skill is: - -- `create_project`: create a new project. Requires `name`; accepts optional `folderId`. -- `create_notebook`: create an empty notebook in a project. Requires `projectId`; accepts optional `name`. -- `create_block`: create a block in a notebook. Requires `notebookId` and `type`; accepts optional `content`, `metadata`, `position`, `includeNotebookBlockIds`, and SQL-only `integrationId`. -- `update_block`: update an existing block. Requires `blockId`; accepts `content`, SQL-only `integrationId`, or both. At least one of `content` or `integrationId` is required. -- `reorder_notebook_blocks`: move one or more existing blocks in a notebook. Requires `notebookId`, non-empty unique `blockIds` in the desired moved-block order, and `placement`. - -## Editing Workflow - -1. Resolve ambiguous names and IDs before writing. Use `get_me` for workspace identity, `search` or `list_projects` for projects/notebooks, `get_notebook` for current block order, and `list_integrations` for SQL connections. -2. Treat `create_project`, `create_notebook`, and `create_block` as non-idempotent. Repeating the same call creates another resource. -3. Use `create_project` only when the user wants a new project. A created project includes a default empty notebook; if the workflow later calls `create_notebook`, the new `create_notebook` result becomes the active notebook for blocks, verification, links, and run prompts. -4. Use `create_notebook` only when adding an empty notebook to a project. It does not accept starter blocks; capture the returned notebook ID and create blocks afterward with `create_block` in that exact notebook. -5. Use `create_block` for each new block. Omit `position` to append, or pass a zero-based `position` when placement matters. -6. Use `update_block` when changing an existing block. It updates content and/or SQL integration in place; it does not create a new block. -7. Pass `includeNotebookBlockIds: true` when the final block order matters, especially for ordered inserts or multi-block scaffolds. -8. Use `reorder_notebook_blocks` when moving existing blocks. It preserves the relative order of blocks omitted from `blockIds` and returns the final active block order. -9. Verify meaningful edits with `get_notebook` after block creation, block update, or block reordering when order, integration attachment, or multi-block content matters. -10. Do not run the notebook after editing unless the user explicitly asks for execution or confirms a final run prompt. After creating or scaffolding a notebook, ask whether to run that exact notebook in Deepnote. - -## Creation Target Tracking - -For creation workflows, keep one active target notebook: - -- If only `create_project` is called, use the default notebook created with the project as the active notebook when blocks or a notebook link are needed. -- If `create_notebook` is called, use the notebook ID returned by `create_notebook` as the active notebook, even when the same project also has an initial default notebook. -- Create blocks, verify with `get_notebook`, build notebook links, and ask about running against the active notebook ID. Do not link to or run the project's default notebook unless it is the active notebook. -- When both a project link and notebook link are useful, label them separately so the notebook link points to the newly created or edited notebook. - -## Block Creation Guidance - -Choose the block `type` that matches Deepnote's block vocabulary. Common types include `code`, `sql`, `markdown`, input blocks such as `input-text`, `input-select`, `input-checkbox`, and text-cell variants such as `text-cell-h1`, `text-cell-p`, and `text-cell-callout`. - -For SQL blocks: - -- Resolve the integration first with `list_integrations` when the user gives a connection name. -- Pass the connection as top-level `integrationId`. -- Do not put `sql_integration_id` inside `metadata`. -- Do not pass `integrationId` for non-SQL blocks. - -For input blocks, put block-type configuration in `metadata` and keep `content` for the visible/default textual content when applicable. Preserve existing notebook naming and variable conventions when adding inputs near related blocks. - -## Block Update Guidance - -Before updating a block, call `get_notebook` and identify the target block ID, current type, current content, and visible SQL integration when relevant. Ask a clarifying question only when the target block or requested replacement is ambiguous. - -Use `update_block` when the user wants to revise an existing cell or block. Send the full replacement `content` for the block content you want saved; do not assume partial snippets will be merged unless the user explicitly asks for exactly that replacement. Use `create_block` only when the user wants an additional new block. - -For SQL blocks, `update_block` can update `content`, `integrationId`, or both in a single call. Resolve the integration with `list_integrations` when the user gives a connection name, then pass the connection as top-level `integrationId`. Do not put `sql_integration_id` in metadata. - -Do not pass `integrationId` for non-SQL blocks. The app tools do not expose block type changes, arbitrary metadata updates, deletion, or saved input-default edits through `update_block`; say so instead of claiming those changes were applied. - -## Block Reordering Guidance - -Before moving blocks, call `get_notebook` and identify the current ordered block IDs. Ask a clarifying question only when the target block or destination is ambiguous. - -Call `reorder_notebook_blocks` with: - -- `notebookId`: the target notebook ID. -- `blockIds`: a non-empty list of unique block IDs to move, ordered exactly as they should appear as the moved group. -- `placement`: `{ "type": "start" }`, `{ "type": "end" }`, or `{ "type": "after", "blockId": "anchor-block-id" }`. - -For `placement.type: "after"`, the anchor `blockId` must be an active block in the same notebook and must not be included in `blockIds`. Use `start` or `end` instead of manufacturing an anchor when the user asks for the beginning or end of the notebook. - -After reordering, report the moved block IDs and final order when useful. If the tool returns the same final order, treat it as a no-op rather than an error. - -## Response Style - -After a successful edit, report the created project, active notebook, and block names/IDs when relevant, plus placement or final block order when useful. Include Deepnote links when they can be safely constructed with `deepnote-links`; for newly created notebooks, the notebook link must use the active notebook ID from `create_notebook` or the default notebook created by `create_project` when no separate notebook was created. - -If a notebook was created or scaffolded and not already run, end with a short question asking whether to run the active notebook in Deepnote now. Do not call `create_run` until the user confirms; after confirmation, use `deepnote-data-execution` and pass the active notebook ID. - -If a write tool returns an error, surface the user-facing message concisely and name the likely fix: missing permission, missing target resource, invalid block type, invalid position or placement, duplicate notebook name, suspended project, or incompatible SQL integration. diff --git a/plugins/deepnote/skills/deepnote-notebooks/SKILL.md b/plugins/deepnote/skills/deepnote-notebooks/SKILL.md deleted file mode 100644 index c65ee91cf..000000000 --- a/plugins/deepnote/skills/deepnote-notebooks/SKILL.md +++ /dev/null @@ -1,67 +0,0 @@ ---- -name: deepnote-notebooks -description: Use when reading, reviewing, inspecting, or reasoning about hosted Deepnote notebooks, blocks, inputs, SQL, Python, or notebook outputs through the Deepnote app tools. ---- - -# Deepnote Notebooks - -## Notebook Inspection Workflow - -1. Resolve the target notebook with `search` or project context before using `get_notebook`. -2. Read the notebook with `get_notebook` before answering questions about structure, inputs, blocks, or latest run state. -3. Preserve distinctions between block types, notebook inputs, code, SQL, markdown, and metadata in your reasoning. -4. When reporting inputs, include the input `name`, `type`, current `value`, and `label` when useful. -5. When SQL connection usage matters, use `list_integrations` and the integration usage tools to confirm project, notebook, or block references instead of inferring solely from names. When table, schema, or column context matters, use `get_integration` for cached structure. -6. When asked to review or explain a notebook, ground the answer in specific notebook/block names or IDs when useful. -7. If the user asks to create a project, create a notebook, add blocks/cells, update existing blocks/cells, or scaffold notebook content, use the `deepnote-notebook-editing` skill. -8. If the user asks for recent runs, failed runs, or run history, use `list_notebook_runs` before selecting a run for `get_run`. - -## Notebook Inspection Output - -Great notebook-inspection output should help the user decide what the notebook does, whether it is safe to run, and what to do next. Prefer this structure: - -Keep notebook inspection brief and high signal by default. Lead with the answer, then include only the tables or cautions that materially help the user. Omit exhaustive block listings, raw code, and long outputs unless the user asks for more detail. - -1. Start with a one-sentence brief: `Notebook "Name" in project "Project" has 12 blocks, 2 inputs, 1 visible connection, and last ran successfully on YYYY-MM-DD HH:MM UTC.` -1. Show a compact status table: - -| Field | Value | -| --- | --- | -| Project | `Project name` | -| Notebook | `Notebook name` | -| Notebook ID | `notebook-id` | -| Scheduled | `Yes` or `No` | -| Last Run | `status/date/run id` or `No run visible` | -| Visible Connections | `Integration name (type)` or `None visible via app tools` | - -1. If inputs exist, add an inputs table: - -| Input | Type | Current Value | Label | -| --- | --- | --- | --- | -| `input_name` | `text` | `safe summary or value` | `Human label` | - -1. Add a block map when useful, especially for reviews and debugging: - -| Order | Type | Purpose | Connection / Output | -| --- | --- | --- | --- | -| `1` | `sql` | `SELECT demo.gapminder sample` | `Clickhouse (clickhouse)` | - -1. Add `Cautions` only when actionable: cells that print environment variables, hard-coded credentials, mutating external calls, long-running servers, large dataset dumps, missing inputs, failed/pending last runs, SQL blocks whose integration is not visible, or integration usage that was not checked when it matters. -1. End with `Useful Next Actions` only when it helps, such as run notebook, inspect latest run, list recent runs, map integrations, summarize outputs, or review risky cells. - -When the Deepnote app tools do not expose a detail, say `Not visible via app tools` rather than inferring from names. Keep raw code excerpts short; summarize large cells and mention block IDs when useful. - -## Code And Output Handling - -- Before suggesting code changes, inspect nearby blocks for imports, shared variables, SQL connections, inputs, and upstream assumptions. -- Prefer deterministic notebook code. Avoid hidden global state, implicit external files, or hard-coded credentials. -- Do not claim an edit was applied unless a write-capable tool is available and reports success. For project, notebook, block creation, and existing block updates, use `deepnote-notebook-editing`. -- If you run a notebook, pass requested input values through `create_run.inputs` using the input `name` fields returned by `get_notebook`, then capture run status with `get_run`. Omit `snapshotDelivery` for status checks so the default download URL delivery is used; request `snapshotDelivery: "inline"` when you need to summarize snapshot content or errors. -- Run input values do not change the notebook's saved default input values. -- For SQL blocks, preserve the existing connection or data source in recommendations unless the user asks to move it. Use `get_integration` for cached table/column context when needed. -- Before running a notebook, flag cells that print `os.environ`, environment variables, credentials, tokens, or broad secret dumps. Do not run those notebooks unless the user explicitly confirms after the risk is named. -- Treat cells that start servers, send network requests, write files, cancel/modify external records, or call production-like systems as stateful. Call out the side effect before execution. - -## Review And Cleanup - -Use Deepnote app reads to verify notebook structure before making claims. If execution was not run, say so plainly and mention the remaining risk. For larger reviews, summarize relevant sections rather than listing every block. diff --git a/plugins/deepnote/skills/deepnote/SKILL.md b/plugins/deepnote/skills/deepnote/SKILL.md deleted file mode 100644 index 5d5b96ad0..000000000 --- a/plugins/deepnote/skills/deepnote/SKILL.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -name: deepnote -description: Use when a task mentions Deepnote, the connected Deepnote app, Deepnote OAuth connection, Deepnote docs, projects, workspaces, notebooks, blocks, integrations, or notebook runs. ---- - -# Deepnote Router - -Use the connected Deepnote app tools as the source of truth for hosted Deepnote state. Prefer them over browser automation, screenshots, ad hoc HTTP calls, or local filesystem guesses for Deepnote projects, notebooks, blocks, integrations, docs, or runs. - -If the Deepnote app tools are unavailable, say so and ask the user to connect the Deepnote app with OAuth. Do not pretend to have inspected Deepnote state. - -When the user asks what Deepnote can do, start with: - -Deepnote can identify the current workspace, search resources, list projects and integrations, inspect notebooks, create and edit notebook structure, map integration usage and cached table structure, read Deepnote docs, run notebooks, and fetch run status and history. - -## Capability Map - -Use the current tool surface only; if a required tool is absent, say which capability is missing. - -- Identity and discovery: `get_me`, `search`, `list_projects`. -- Notebook inspection: `get_notebook`. -- Notebook editing: `create_project`, `create_notebook`, `create_block`, `update_block`, `reorder_notebook_blocks`. -- Integrations: `list_integrations`, `get_integration`, `list_integration_project_usages`, `list_integration_notebook_usages`, `list_integration_block_usages`. -- Execution: `create_run`, `list_notebook_runs`, `get_run`. -- Docs: `list_docs`, `get_doc`. - -## Route By Intent - -| User asks for | Use | -| --- | --- | -| Workspace overview, heartbeat, active/scheduled notebooks, broad inventory | `get_me`, `list_projects`, `list_integrations`, and [references/workspace-summary.md](references/workspace-summary.md) | -| Notebook contents, inputs, SQL, blocks, outputs, review, or explanation | `deepnote-notebooks` | -| Project/notebook creation, adding cells, updating cells, moving/reordering cells, scaffolded notebook content | `deepnote-notebook-editing` | -| Notebook execution, run status, recent/failed runs, run history, snapshots, integration usage, cached tables/columns | `deepnote-data-execution` | -| Project, notebook, workspace, or share links | `deepnote-links` | -| Product docs or how-to questions | `list_docs`, then `get_doc` | - -## Routing Defaults - -1. Resolve ambiguous names and IDs with `search`, `list_projects`, or `list_integrations`. -2. Use `get_me` when workspace identity, connected user, caller role, or OAuth context matters. -3. Read with `get_notebook` before reasoning about notebook blocks, inputs, latest run metadata, or before editing notebook structure. -4. Use `get_integration` for cached table, schema, column, or table-existence questions about an integration. -5. Use `list_notebook_runs` for run history or failed-run searches; use `get_run` for one selected run's status, errors, or snapshot. -6. Use specialist skills for any workflow with detailed rules. Keep this skill as the router. - -## Global Guardrails - -- Never expose tokens, secret values, raw credentials, or sensitive integration metadata. Refer to secret names only. -- Treat project creation, notebook creation, block creation, block updates, block reordering, and notebook runs as persistent or stateful actions. Resolve targets carefully and report affected IDs. -- Do not run a notebook unless the user asks for execution, clearly needs fresh results, or confirms a creation workflow's final run prompt. Creation workflows may ask whether to run the newly created notebook, but must not run it preemptively. Run input overrides apply only to that run. -- Do not expose `snapshotDownloadUrl` values unless the user asks for a download/file handoff; use inline snapshots only when output or error details are needed. -- Use cached integration structure when available, but do not claim a fresh live database scan, row preview, single-block execution, environment mutation, permission change, publishing change, or scheduling change unless a current app tool exposes it. -- Keep responses brief and grounded in Deepnote object names, IDs, statuses, and links when available. diff --git a/plugins/deepnote/skills/deepnote/references/workspace-summary.md b/plugins/deepnote/skills/deepnote/references/workspace-summary.md deleted file mode 100644 index 41cfda767..000000000 --- a/plugins/deepnote/skills/deepnote/references/workspace-summary.md +++ /dev/null @@ -1,51 +0,0 @@ -# Workspace Summary Workflow - -Use this workflow when the user asks for a workspace summary, heartbeat, overview, active notebooks, scheduled notebooks, or a broad project/notebook inventory. - -1. Use `get_me` for workspace name, workspace ID, connected user, and caller access level when useful. -2. Use `list_projects` to collect projects and notebooks. For complete inventories, page through results with `pageSize: 100` until `pagination.hasMore` is false. -3. Use `list_integrations` to collect workspace integration names, types, and IDs. -4. Use `get_notebook` for notebooks that need connection details or latest run detail. -5. Use `list_notebook_runs` when the summary asks for recent runs, failed runs, or run history beyond the latest run metadata. -6. Identify scheduled notebooks from the `isScheduled` field returned by `list_projects` or `get_notebook`. -7. Identify active notebooks from available recency signals such as `lastRunAt`, recent `list_notebook_runs` results, a current or recent `lastRunId`, or an explicitly requested run status from `get_run`. If the app does not expose live kernel/session state, say active means recent run activity rather than an open editor session. -8. Identify integration usage with `list_integration_project_usages`, `list_integration_notebook_usages`, or `list_integration_block_usages` when direct usage mapping is needed. If usage is not checked, write `Usage not checked`; if a checked usage tool returns no usages, write `None found`. -9. Use `get_integration` when the workspace summary or integration report asks for cached table, schema, or column structure. -10. Build safe project and notebook links with `deepnote-links`, including UTM parameters on project and notebook URLs; use `utm_term=workspace_summary` when the link is created by this workflow rather than a single tool result. - -Great workspace-status output should feel like a small operations dashboard: - -1. Start with a one-sentence health line, for example: `Deepnote workspace is reachable; the current app response includes 6 projects, 15 notebooks, 1 scheduled notebook, and 4 integrations.` -2. Add a compact `Key Signals` list with counts visible in the current app response for projects, notebooks, scheduled notebooks, recently run notebooks, failed or pending runs when checked, and integrations. -3. Use a Markdown notebook summary table as the main artifact when individual notebook rows are reasonable, grouping rows by project. Use a compact project summary table only when the workspace is large enough that listing every notebook would be noisy. -4. Keep integrations inside the main table as an `Integrations` column for workspace summaries, notebook inventories, and project summaries. -5. Hyperlink project names and notebook names when links can be safely constructed. In any table with a `Notebook` column, the notebook name should be the Markdown link label. -6. Finish with `Notable Findings` only when there is something actionable, such as a scheduled notebook with no last run, a pending/failed run, a notebook that prints environment variables, or an integration with no checked usage. - -Use this notebook summary table shape for workspace summaries, notebook inventories, and "which notebooks do I have?" style requests unless the workspace is too large or the user asks for a different format: - -| Project | Notebook | Scheduled | Last Run Seen | Integrations | -| --- | --- | --- | --- | --- | -| `Project name as Markdown link` | `Notebook name as Markdown link` | `Yes` or `No` | `YYYY-MM-DD HH:MM UTC`, `None seen`, or `Not visible via app tools` | `Integration name/Type` or `None found` | - -Use this compact project summary table only for large workspaces or high-level summaries. When listing notebook names inside the `Notebooks` column, hyperlink each notebook name: - -| Project | Notebooks | Scheduled | Last Run Seen | Integrations | -| --- | --- | --- | --- | --- | -| `Project name as Markdown link` | `N` or linked notebook names | `Yes` if any notebook in the project is scheduled, otherwise `No` | `YYYY-MM-DD HH:MM UTC`, `None seen`, or `Not visible via app tools` | `Integration name/Type, Integration name/Type` or `None found` | - -For `Last Run Seen`, use that notebook's visible `lastRunAt` in notebook rows. In compact project rows, use the most recent visible `lastRunAt` across notebooks in the project, or a checked `get_run` completion time when more current. Format dates in UTC as `YYYY-MM-DD HH:MM UTC`. Do not write "None seen" when a run ID or run timestamp is visible. - -For `Integrations`, use integration names and IDs from `list_integrations`, then map usage with `list_integration_project_usages`, `list_integration_notebook_usages`, or `list_integration_block_usages` when direct usage matters. Use `get_integration` for cached table/column structure when requested. You may also mention visible references from `get_notebook` blocks or inline `get_run` snapshot content. Do not infer usage from integration names alone; say `None found` only when checked usage or visible references return no connection. - -For a specific project breakdown or a specific notebook summary, filter the notebook summary table to the relevant project or notebook and keep the notebook name hyperlinked. - -Use a standalone integration table only when the user explicitly asks for an integration inventory or integration usage report. In normal workspace and notebook summaries, do not split integrations into a separate table; keep them in the `Integrations` column. - -| Integration | Type | Visible Notebook Usage | -| --- | --- | --- | -| `Integration name` | `type` | `Project / Notebook` from usage tools, `None found`, or `Usage not checked` | - -Keep the table concise for large workspaces: include active notebooks, scheduled notebooks, and notebooks with visible linked connections first; then summarize any remaining notebooks by count. - -Avoid calling notebooks "currently open" or "currently running" unless a current app tool exposes live session state. Prefer `recently run`, `scheduled`, `pending run`, or `last run`. diff --git a/plugins/demandbase/.app.json b/plugins/demandbase/.app.json deleted file mode 100644 index b2b662af5..000000000 --- a/plugins/demandbase/.app.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "apps": { - "demandbase": { - "id": "asdk_app_698ebfa1aadc81918b3bb13ae2118af0" - } - } -} diff --git a/plugins/demandbase/.codex-plugin/plugin.json b/plugins/demandbase/.codex-plugin/plugin.json deleted file mode 100644 index dbd676cda..000000000 --- a/plugins/demandbase/.codex-plugin/plugin.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "demandbase", - "version": "1.0.3", - "description": "Demandbase integration with Codex gives sales, marketing, and GTM teams seamless access to rich B2B data...", - "author": { - "name": "Demandbase Inc", - "url": "https://www.demandbase.com" - }, - "homepage": "https://www.demandbase.com", - "repository": "https://github.com/openai/plugins", - "license": "MIT", - "keywords": [], - "apps": "./.app.json", - "interface": { - "displayName": "Demandbase", - "shortDescription": "Demandbase integration with Codex gives sales, marketing, and GTM teams seamless access to rich B2B data...", - "longDescription": "Demandbase integration with Codex gives sales, marketing, and GTM teams seamless access to rich B2B data directly inside Codex for easier account targeting and engagement.\n\nThis app connects Codex to Demandbase via a secure MCP connection, letting you access both first and third party data:\nThird-Party Data (3P): Search and retrieve industry-leading company and contact data from Demandbase, including firmographics, technographics, family tree, company news, and leadership profiles.\n\nFirst-Party Data (1P): Securely access your own Demandbase 1P data such as known accounts, contacts, CRM entities, engagement, and in-pipeline opportunities\n\nWhat you can do:\nSearch companies by name, location, size, revenue, or industry\nDiscover decision-makers and influencers by role or job level\nFetch full company profiles including recent news and competitors\nTap into 1P signals like engagement, MQA status, or activity\n\nWho it\u2019s for:\nSales reps looking for fast answers about a target account in real time\nMarketers enriching leads or refining personas\nRevOps and Enablement teams embedding intelligence into workflows\nAI product teams creating agent workflows\n\n\nNote: This requires Demandbase licences, please contact PartnerSupport@demandbase.com.", - "developerName": "Demandbase Inc", - "category": "Business & Operations", - "capabilities": [], - "websiteURL": "https://www.demandbase.com", - "privacyPolicyURL": "https://demandbase.com/privacy/", - "termsOfServiceURL": "https://www.demandbase.com/terms-of-use/", - "defaultPrompt": [ - "List the top 10 VP contacts at Demandbase" - ], - "screenshots": [], - "composerIcon": "./assets/logo.png", - "logo": "./assets/logo.png", - "logoDark": "./assets/logo-dark.png" - } -} diff --git a/plugins/demandbase/assets/logo-dark.png b/plugins/demandbase/assets/logo-dark.png deleted file mode 100644 index 95571207b..000000000 Binary files a/plugins/demandbase/assets/logo-dark.png and /dev/null differ diff --git a/plugins/demandbase/assets/logo.png b/plugins/demandbase/assets/logo.png deleted file mode 100644 index dcf9cf3d0..000000000 Binary files a/plugins/demandbase/assets/logo.png and /dev/null differ diff --git a/plugins/digitalocean/.app.json b/plugins/digitalocean/.app.json deleted file mode 100644 index e4441be84..000000000 --- a/plugins/digitalocean/.app.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "apps": { - "digitalocean": { - "id": "asdk_app_6a3c278c93ac8191b29768648d63a754" - } - } -} diff --git a/plugins/digitalocean/.codex-plugin/plugin.json b/plugins/digitalocean/.codex-plugin/plugin.json deleted file mode 100644 index 3afd5d33f..000000000 --- a/plugins/digitalocean/.codex-plugin/plugin.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "digitalocean", - "version": "0.2.2", - "description": "Provision a DigitalOcean droplet as a remote Codex workspace.", - "author": { - "name": "DigitalOcean", - "url": "https://github.com/digitalocean" - }, - "homepage": "https://www.digitalocean.com/", - "repository": "https://github.com/digitalocean/CodexPlugin", - "keywords": [ - "digitalocean", - "droplet", - "virtual-machine", - "remote-session" - ], - "skills": "./skills/", - "apps": "./.app.json", - "interface": { - "displayName": "DigitalOcean", - "shortDescription": "Provision a droplet as a Codex workspace", - "longDescription": "Provision and configure a DigitalOcean droplet as a remote Codex SSH workspace using the connected DigitalOcean app.", - "developerName": "DigitalOcean", - "category": "Developer Tools", - "capabilities": [ - "Interactive", - "Write" - ], - "websiteURL": "https://www.digitalocean.com/", - "privacyPolicyURL": "https://www.digitalocean.com/legal/privacy-policy", - "termsOfServiceURL": "https://www.digitalocean.com/legal/terms-of-service-agreement", - "defaultPrompt": [ - "Provision a DigitalOcean droplet for Codex" - ], - "brandColor": "#1AAFBF", - "composerIcon": "./assets/logo.png", - "logo": "./assets/logo.png", - "logoDark": "./assets/logo-dark.png", - "screenshots": [] - } -} diff --git a/plugins/digitalocean/README.md b/plugins/digitalocean/README.md deleted file mode 100644 index 48f0be546..000000000 --- a/plugins/digitalocean/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# digitalocean - -This directory packages the upstream [digitalocean/CodexPlugin](https://github.com/digitalocean/CodexPlugin) runtime content for the `openai/plugins` marketplace. The provisioning skill is discovered by Codex through its `SKILL.md` frontmatter. - -## What is included - -- `skills/provision-droplet/` from the upstream plugin -- `.app.json` for the connected DigitalOcean app -- Python helpers for SSH key generation and local SSH configuration -- The upstream SSH config template and DigitalOcean assets - -## Codex compatibility notes - -- The upstream plugin id is `digitalocean-codex-workspace`; this import uses the local plugin id `digitalocean`. -- The connected app manifest uses the repository's standard ID-only shape. -- The provisioning workflow and helper scripts are included without behavioral changes. - -## Upstream source - -- Repo: [digitalocean/CodexPlugin](https://github.com/digitalocean/CodexPlugin) -- Imported commit: `6be47f6207d3ff553a706c8e5483e6fa02793d94` -- Imported version: `0.2.2` -- Local plugin id: `digitalocean` - -## Components - -```text -digitalocean/ -├── .codex-plugin/plugin.json -├── .app.json -├── assets/ -│ ├── logo.png -│ └── logo-dark.png -└── skills/provision-droplet/ - ├── SKILL.md - ├── ssh_config.tmpl - └── scripts/ - ├── keygen.py - └── configure_ssh.py -``` - -The skill provisions a droplet from the DigitalOcean Codex Universal image, uploads an SSH key through the connected DigitalOcean app, configures local SSH access, and hands the resulting host off to the Codex desktop app. diff --git a/plugins/digitalocean/assets/logo-dark.png b/plugins/digitalocean/assets/logo-dark.png deleted file mode 100644 index 57b9abf87..000000000 Binary files a/plugins/digitalocean/assets/logo-dark.png and /dev/null differ diff --git a/plugins/digitalocean/assets/logo.png b/plugins/digitalocean/assets/logo.png deleted file mode 100644 index 57b9abf87..000000000 Binary files a/plugins/digitalocean/assets/logo.png and /dev/null differ diff --git a/plugins/digitalocean/skills/provision-droplet/SKILL.md b/plugins/digitalocean/skills/provision-droplet/SKILL.md deleted file mode 100644 index c978c1a86..000000000 --- a/plugins/digitalocean/skills/provision-droplet/SKILL.md +++ /dev/null @@ -1,261 +0,0 @@ ---- -name: provision-droplet -description: > - Use when the user wants to spin up / create / launch / provision a - DigitalOcean droplet (or "a remote dev box on DO") and connect to it from - Codex as a remote SSH workspace. ---- - -# Provision a DigitalOcean droplet as a Codex remote workspace - -Follow these steps in order. Do not skip or reorder them. -**Only the installed Codex DigitalOcean app tools and the bundled Python scripts -may be used. doctl, ad hoc integration configs, and any other DigitalOcean CLI -tools are prohibited.** - -## Before you start - -- **Prerequisites:** a funded DigitalOcean account, the installed and - authenticated Codex **DigitalOcean** app, a local `ssh`/`ssh-keygen` - (OpenSSH), Python 3, and the Codex desktop app. -- **Cost:** the droplet bills **hourly from creation until you delete it**. - Sizes in step 5 show approximate monthly rates. Remind the user to delete it - when done (see *Cleanup* below). -- **Time:** end-to-end takes ~10–15 minutes — roughly 7 minutes waiting for the - droplet to boot (steps 7-8) plus up to 7 minutes for cloud-init (step 9). This - is normal; do not abort. -- **Locate the bundled scripts first (do this before Step 2).** The helper - scripts live in the `scripts/` folder **next to this `SKILL.md`** (i.e. - `provision-droplet/scripts/`). Your current working directory is **not** the - skill directory, so bare relative paths like `scripts/keygen.py` will fail. - Resolve the absolute directory that contains this `SKILL.md` and call it - ``. If you don't already know it, find it — the installed plugin may - nest it under a version folder (e.g. `...//provision-droplet/`), so - locate the directory that actually contains `scripts/keygen.py`. Use - `/scripts/.py` (an absolute path) in **every** command below. - -## Step 1 — Verify DigitalOcean app access - -This plugin depends on the single Codex **DigitalOcean** app. Use it for all -DigitalOcean operations; do not register or log in to separate plugin-owned app -integrations. - -The **DigitalOcean** app provides both: -- SSH key tools: `key-create`, `key-list`, `key-delete`. -- Droplet tools: `droplet-create`, `droplet-get`, `droplet-delete`. - -Confirm that these tools are available before continuing. If the app's tools are -missing or unauthenticated, stop and tell the user to install or authenticate -the DigitalOcean app in Codex. Do not fall back to doctl, API tokens, or a local -integration config. - -## Step 2 — Generate SSH key pair - -```bash -python3 /scripts/keygen.py -``` - -Parse the JSON output and keep these values for the steps below: -`prefix`, `name`, `key_name`, `key_path`, `pub_key`. - -How these relate (all derived from one random `prefix` like `bright-hawk-a3f2`): -- `name` = `codex-` — the **droplet name** and the **local SSH alias** - (they are identical). -- `key_name` = `codex-key-` — the label for the key on DigitalOcean's - side only. -- `key_path` — the local private key file. - -## Step 3 — Upload SSH public key - -Call the **DigitalOcean** app tool **`key-create`**: - -| Parameter | Value | -|-----------|-------| -| `Name` | `key_name` from step 2 | -| `PublicKey` | `pub_key` from step 2 | - -Extract `ssh_key.id` from the response — this is ``. - -If the call fails because a key with that name or fingerprint **already exists** -(e.g. a previous run), do not create a duplicate: call the **DigitalOcean** app -tool **`key-list`**, find the entry whose `name` matches `key_name` (or whose -fingerprint matches the uploaded key), and use its `id` as ``. - -## Step 4 — Choose a region - -Ask the user, in chat: - -> Use the defaults — region **`nyc3`** (New York, US) and size -> **`s-2vcpu-4gb`** (2 vCPU / 4 GB, ~$24/mo) — or customize them? - -If they choose the defaults, use `nyc3` as `` and `s-2vcpu-4gb` as -``, then skip step 5 and continue to step 6. - -If they want to customize the defaults, present this region list first and ask -them to reply with a slug: - -| Slug | Location | -|------|----------| -| `nyc3` *(default)* | New York, US | -| `sfo3` | San Francisco, US | -| `tor1` | Toronto, CA | -| `lon1` | London, UK | -| `fra1` | Frankfurt, DE | -| `ams3` | Amsterdam, NL | -| `sgp1` | Singapore, SG | -| `blr1` | Bangalore, IN | -| `syd1` | Sydney, AU | - -Validate their reply against this table. If it is not one of these slugs, ask -again — do not pass an unlisted value through. The chosen slug is ``. - -## Step 5 — Choose a droplet size - -Skip this step if `` was already set to the default in step 4. - -Otherwise, present this size list and ask the user to reply with a slug. Every -size below is above the **1 vCPU / 2 GB** floor required by the Codex Universal -image. Prices are approximate — confirm in the DigitalOcean dashboard. - -| Slug | vCPU | RAM | Tier | ~$/mo | -|------|------|-----|------|-------| -| `s-2vcpu-4gb` *(default)* | 2 | 4 GB | Shared basic | $24 | -| `s-4vcpu-8gb` | 4 | 8 GB | Shared basic | $48 | -| `s-8vcpu-16gb` | 8 | 16 GB | Shared basic | $96 | -| `c-2` | 2 | 4 GB | Premium CPU-optimized | $42 | -| `g-2vcpu-8gb` | 2 | 8 GB | Premium general-purpose | $63 | - -Validate their reply against this table. If it is not one of these slugs, ask -again — do not pass an unlisted value through. The chosen slug is ``. - -## Step 6 — Create droplet - -Call the **DigitalOcean** app tool **`droplet-create`**: - -| Parameter | Value | Notes | -|-----------|-------|-------| -| `Name` | `name` from step 2 | | -| `Region` | `` from step 4 | | -| `Size` | `` from step 5 | | -| `ImageID` | `234061005` | DigitalOcean **Codex Universal** image | -| `SSHKeys` | `[""]` | | - -Extract `droplet.id` from the response — this is ``. - -If `droplet-create` fails, show the user the error and handle it by cause — do -not blindly retry the same values: -- **Size not available in this region** (premium tiers like `c-2` and - `g-2vcpu-8gb` are not in every region): go back to step 4 or 5 and pick a - different region/size combination. -- **Payment / quota / limit** errors: stop and tell the user to resolve it in - the DigitalOcean dashboard, then re-run. -- **Invalid image or any other error:** stop and report it. - -The uploaded SSH key from step 3 is harmless to leave, but if you abort here see -*Cleanup* below. - -## Step 7 — Schedule delayed deployment check-in - -After `droplet-create` succeeds, inspect the response before polling. If the -droplet is not already `active` with a public IPv4 address in the create -response, assume provisioning is still in progress. - -Create a Codex **heartbeat** to resume this same thread in **5 minutes** for -the first status check. Use the Codex automation tool, not a shell sleep or -local timer: - -| Field | Value | -|-------|-------| -| `mode` | `create` | -| `kind` | `heartbeat` | -| `destination` | `thread` | -| `name` | `Check DigitalOcean droplet ` | -| `rrule` | `FREQ=MINUTELY;INTERVAL=5` | -| `status` | `ACTIVE` | -| `prompt` | `Resume provisioning DigitalOcean droplet (). Start at Step 8: check whether it is active and has a public IPv4 address, then continue the workflow. The droplet bills hourly until deleted.` | - -After creating the heartbeat, tell the user the droplet was created and Codex -will check back in about 5 minutes. **Stop the active turn here.** Do not start -20-second polling until the heartbeat wakes the thread back up. This avoids -busy-waiting during the normal 5-7 minute deployment window. - -Keep the created heartbeat's automation id as `` if the tool -returns one. When the heartbeat resumes the thread, use step 8 to decide -whether to keep checking or to delete/pause the heartbeat and continue. - -If the create response already includes `status == "active"` and a public IPv4 -address, skip the heartbeat and continue immediately to step 8. - -## Step 8 — Check whether the droplet is active - -Call the **DigitalOcean** app tool **`droplet-get`** once with -`ID: `. - -If the response has `status == "active"` **and** `networks.v4` contains an -entry with `type == "public"`, extract `ip_address` from that entry — this is -``. Delete or pause `` if it still exists, then continue to -step 9. - -If the droplet is still not active or does not yet have a public IPv4 address, -schedule Codex to check this same thread again in **1 minute**, then **stop the -active turn here**. Keep checking back every minute until the droplet is ready; -do not give up merely because the DigitalOcean deployment is slow. Prefer -updating the existing `` to a 1-minute interval; if that is not -possible, create a replacement heartbeat and delete/pause the old one so there -is only one active check-in. - -Use these values for the 1-minute follow-up heartbeat: - -| Field | Value | -|-------|-------| -| `mode` | `create` or `update`, depending on the available automation tool | -| `kind` | `heartbeat` | -| `destination` | `thread` | -| `name` | `Check DigitalOcean droplet ` | -| `rrule` | `FREQ=MINUTELY;INTERVAL=1` | -| `status` | `ACTIVE` | -| `prompt` | `Resume provisioning DigitalOcean droplet (). Start at Step 8: check whether it is active and has a public IPv4 address, then continue the workflow. If it is still not ready, schedule another 1-minute heartbeat. The droplet bills hourly until deleted.` | - -Keep all status checks with the DigitalOcean app — do not use doctl or any other -tool to check droplet status. - -## Step 9 — Configure local SSH - -```bash -python3 /scripts/configure_ssh.py \ - --alias codex- \ - --ip \ - --user root \ - --key-path -``` - -**⚠️ Do not interrupt this step.** It waits for cloud-init to finish (up to 7 -minutes) and prints a `⏳` status line every 5 seconds — that output means it is -working normally. **Do not run any other commands. Wait for the line -`DROPLET READY`.** If the script exits with an error instead, report it and -offer to delete the droplet (see *Cleanup*). - -## Final step: adding it to Codex - -Ask Codex to open the add-SSH-host flow by responding with a clickable Markdown -link. Replace `` with `name` from step 2: - -`[Add to Codex SSH](codex://settings/connections/ssh/add?name=)` - -The SSH alias is the local host alias created by step 9. In this workflow, it is -the same value as the droplet name: `codex-`. - -If the link does not open the flow, or if the user wants to check it manually, -tell them to open: **Codex App → Settings → Connections → Add SSH Host → pick -the alias → choose the remote folder.** - -## Cleanup (on failure or when done) - -The droplet bills hourly until deleted. To tear down: - -1. **Delete the droplet** — **DigitalOcean** app tool **`droplet-delete`** with - `ID: `. -2. **Delete the SSH key** (optional) — **DigitalOcean** app tool **`key-delete`** - with the `` from step 3. - -Always confirm with the user before deleting. Do not use doctl for cleanup. diff --git a/plugins/digitalocean/skills/provision-droplet/scripts/configure_ssh.py b/plugins/digitalocean/skills/provision-droplet/scripts/configure_ssh.py deleted file mode 100644 index a3aea0af6..000000000 --- a/plugins/digitalocean/skills/provision-droplet/scripts/configure_ssh.py +++ /dev/null @@ -1,203 +0,0 @@ -#!/usr/bin/env python3 -"""Configure local SSH access for a freshly provisioned DigitalOcean droplet. - -Steps: - 1. Render ssh_config.tmpl and append a Host block to ~/.ssh/config. - 2. Refresh ~/.ssh/known_hosts via ssh-keyscan (20 retries x 10s). - 3. Probe with a real SSH login until cloud-init is done (42 retries x 10s). - 4. Print Codex remote-workspace handoff instructions. - -Uses only the Python standard library — no pip install required. -""" - -from __future__ import annotations - -import argparse -import json -import subprocess -import sys -import threading -import time -from pathlib import Path - - -# --------------------------------------------------------------------------- # -# Heartbeat thread -# --------------------------------------------------------------------------- # -class HeartbeatThread(threading.Thread): - def __init__(self, interval: int = 5): - super().__init__(daemon=True) - self._interval = interval - self._stop_event = threading.Event() - self._status = "working..." - self._lock = threading.Lock() - self._start_time = time.monotonic() - - def set_status(self, msg: str) -> None: - with self._lock: - self._status = msg - - def stop(self) -> None: - self._stop_event.set() - self.join() - - def run(self) -> None: - while not self._stop_event.wait(timeout=self._interval): - elapsed = int(time.monotonic() - self._start_time) - with self._lock: - msg = self._status - print(f"⏳ [{elapsed}s] {msg}", flush=True) - - -# --------------------------------------------------------------------------- # -# SSH config -# --------------------------------------------------------------------------- # -def write_ssh_config(template_path: Path, alias: str, ip: str, user: str, - identity: Path, config_path: Path) -> None: - tmpl = template_path.read_text() - block = (tmpl - .replace("{{ALIAS}}", alias) - .replace("{{IP}}", ip) - .replace("{{USER}}", user) - .replace("{{IDENTITY_FILE}}", str(identity))) - config_path.parent.mkdir(parents=True, exist_ok=True) - existing = config_path.read_text() if config_path.exists() else "" - if f"Host {alias}\n" in existing or f"Host {alias} " in existing: - print(f"~/.ssh/config already has 'Host {alias}'; leaving it untouched.", flush=True) - return - with config_path.open("a") as f: - if existing and not existing.endswith("\n"): - f.write("\n") - f.write("\n" + block.rstrip() + "\n") - config_path.chmod(0o600) - print(f"Appended SSH config block for '{alias}' to {config_path}.", flush=True) - - -# --------------------------------------------------------------------------- # -# known_hosts -# --------------------------------------------------------------------------- # -def update_known_hosts(ip: str, known_hosts: Path, - heartbeat: HeartbeatThread, - tries: int = 20, delay: int = 10) -> None: - known_hosts.parent.mkdir(parents=True, exist_ok=True) - known_hosts.touch(exist_ok=True) - subprocess.run(["ssh-keygen", "-R", ip, "-f", str(known_hosts)], - capture_output=True) - for attempt in range(1, tries + 1): - heartbeat.set_status(f"Scanning SSH host keys (attempt {attempt}/{tries})...") - scan = subprocess.run(["ssh-keyscan", "-T", "10", "-H", ip], - capture_output=True, text=True) - if scan.stdout.strip(): - with known_hosts.open("a") as f: - f.write(scan.stdout) - print(f"Added host keys for {ip} to {known_hosts}.", flush=True) - return - time.sleep(delay) - print(f"WARNING: ssh-keyscan never got a key from {ip}. " - "Run it manually once the droplet finishes booting.", flush=True) - - -# --------------------------------------------------------------------------- # -# SSH login probe -# --------------------------------------------------------------------------- # -def wait_for_ssh_login(ip: str, user: str, key_path: Path, - heartbeat: HeartbeatThread, - tries: int = 42, delay: int = 10) -> bool: - cmd = [ - "ssh", - "-o", "BatchMode=yes", - "-o", "ConnectTimeout=10", - "-o", "StrictHostKeyChecking=accept-new", - "-o", "IdentitiesOnly=yes", - "-i", str(key_path), - f"{user}@{ip}", - "echo", "ok", - ] - for attempt in range(1, tries + 1): - heartbeat.set_status(f"Probing SSH login (attempt {attempt}/{tries})...") - result = subprocess.run(cmd, capture_output=True) - if result.returncode == 0: - print("SSH login succeeded. Droplet is fully ready.", flush=True) - return True - if attempt < tries: - time.sleep(delay) - print( - f"WARNING: SSH login never succeeded after {tries} attempts. " - "The droplet may still be finishing cloud-init. " - "Proceeding — Codex will connect once it is ready.", flush=True - ) - return False - - -# --------------------------------------------------------------------------- # -# Codex handoff -# --------------------------------------------------------------------------- # -def codex_handoff(alias: str, remote_path: str) -> None: - print("\n" + "=" * 64, flush=True) - print("DROPLET READY. To use it as a Codex remote workspace:", flush=True) - print("=" * 64, flush=True) - print(f""" -The host '{alias}' is now in ~/.ssh/config, so the Codex App detects it -automatically. Add it as a remote project: - - Codex App -> Settings -> Connections -> Add SSH Host - -> pick '{alias}' -> remote folder: {remote_path} - -Codex installs its app-server over SSH on connect, so make sure the CLI is -present on the droplet (the app will offer to install it, or run once): - - ssh {alias} 'curl -fsSL https://chatgpt.com/codex/install.sh | CODEX_NON_INTERACTIVE=1 sh' - -NOTE: there is currently no supported CLI to register a desktop remote project -from automation (openai/codex#21554 is open). The one click above is the -supported path. -""", flush=True) - - -# --------------------------------------------------------------------------- # -# main -# --------------------------------------------------------------------------- # -def main() -> None: - p = argparse.ArgumentParser( - description="Configure local SSH access for a provisioned DO droplet." - ) - p.add_argument("--alias", required=True, help="SSH Host alias") - p.add_argument("--ip", required=True, help="Droplet public IP") - p.add_argument("--user", default="root", help="SSH login user (default root)") - p.add_argument("--key-path", required=True, help="Path to private key") - p.add_argument("--ssh-template", default=None, help="Path to ssh_config.tmpl") - p.add_argument("--ssh-config", default="~/.ssh/config") - p.add_argument("--known-hosts", default="~/.ssh/known_hosts") - p.add_argument("--remote-path", default="/root/workspace", - help="Remote project folder for Codex") - p.add_argument("--emit-codex-state", action="store_true", - help="Print the UNSUPPORTED Codex global-state snippet") - args = p.parse_args() - - key_path = Path(args.key_path).expanduser() - ssh_config = Path(args.ssh_config).expanduser() - known_hosts = Path(args.known_hosts).expanduser() - template = (Path(args.ssh_template).expanduser() if args.ssh_template - else Path(__file__).resolve().parent.parent / "ssh_config.tmpl") - - heartbeat = HeartbeatThread(interval=5) - heartbeat.start() - - try: - heartbeat.set_status("Writing ~/.ssh/config entry...") - write_ssh_config(template, args.alias, args.ip, args.user, - key_path, ssh_config) - - update_known_hosts(args.ip, known_hosts, heartbeat) - wait_for_ssh_login(args.ip, args.user, key_path, heartbeat) - - except Exception: - heartbeat.stop() - raise - - heartbeat.stop() - codex_handoff(args.alias, args.remote_path) - - -if __name__ == "__main__": - main() diff --git a/plugins/digitalocean/skills/provision-droplet/scripts/keygen.py b/plugins/digitalocean/skills/provision-droplet/scripts/keygen.py deleted file mode 100644 index 34ff84791..000000000 --- a/plugins/digitalocean/skills/provision-droplet/scripts/keygen.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python3 -"""Generate a unique prefix and ed25519 SSH key pair for a Codex droplet. - -Outputs a single JSON object to stdout: - { - "prefix": "bright-hawk-a3f2", - "name": "codex-bright-hawk-a3f2", - "key_name": "codex-key-bright-hawk-a3f2", - "key_path": "/Users/you/.ssh/codex_bright-hawk-a3f2_ed25519", - "pub_key": "ssh-ed25519 AAAA..." - } -""" - -from __future__ import annotations - -import json -import os -import random -import secrets -import subprocess -from pathlib import Path - -ADJECTIVES = [ - "bright", "silent", "swift", "calm", "bold", - "dark", "keen", "vast", "crisp", "teal", -] -NOUNS = [ - "hawk", "reef", "pine", "dusk", "forge", - "tide", "vale", "peak", "grove", "flint", -] - - -def make_prefix() -> str: - return f"{random.choice(ADJECTIVES)}-{random.choice(NOUNS)}-{secrets.token_hex(2)}" - - -def main() -> None: - prefix = make_prefix() - name = f"codex-{prefix}" - key_name = f"codex-key-{prefix}" - key_path = Path(f"~/.ssh/codex_{prefix}_ed25519").expanduser() - - key_path.parent.mkdir(parents=True, exist_ok=True) - pub_path = Path(str(key_path) + ".pub") - - if not key_path.exists(): - subprocess.run( - ["ssh-keygen", "-t", "ed25519", "-f", str(key_path), - "-N", "", "-C", f"codex@{name}"], - check=True, - ) - key_path.chmod(0o600) - - pub_key = pub_path.read_text().strip() - - print(json.dumps({ - "prefix": prefix, - "name": name, - "key_name": key_name, - "key_path": str(key_path), - "pub_key": pub_key, - })) - - -if __name__ == "__main__": - main() diff --git a/plugins/digitalocean/skills/provision-droplet/ssh_config.tmpl b/plugins/digitalocean/skills/provision-droplet/ssh_config.tmpl deleted file mode 100644 index 7b102db44..000000000 --- a/plugins/digitalocean/skills/provision-droplet/ssh_config.tmpl +++ /dev/null @@ -1,7 +0,0 @@ -Host {{ALIAS}} - HostName {{IP}} - User {{USER}} - IdentityFile {{IDENTITY_FILE}} - IdentitiesOnly yes - StrictHostKeyChecking accept-new - ServerAliveInterval 30 diff --git a/plugins/dnb-finance-analytics/.app.json b/plugins/dnb-finance-analytics/.app.json deleted file mode 100644 index 0bf09705b..000000000 --- a/plugins/dnb-finance-analytics/.app.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "apps": { - "dnb-finance-analytics": { - "id": "asdk_app_6a19c12f33e48191bbf02b9d58c49421" - } - } -} diff --git a/plugins/dnb-finance-analytics/.codex-plugin/plugin.json b/plugins/dnb-finance-analytics/.codex-plugin/plugin.json deleted file mode 100644 index dee689e77..000000000 --- a/plugins/dnb-finance-analytics/.codex-plugin/plugin.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "name": "dnb-finance-analytics", - "version": "1.0.2", - "description": "Commercial credit origination and risk workflows", - "author": { - "name": "Dun & Bradstreet", - "url": "https://www.dnb.com/" - }, - "homepage": "https://www.dnb.com/", - "repository": "https://github.com/openai/plugins", - "license": "MIT", - "keywords": [ - "dnb", - "dun-bradstreet", - "finance-analytics", - "business-identity", - "company-data", - "firmographics", - "credit-risk", - "corporate-linkage", - "portfolio-management", - "credit-origination" - ], - "apps": "./.app.json", - "skills": "./skills/", - "interface": { - "displayName": "D&B Finance Analytics", - "shortDescription": "Commercial credit origination and risk workflows", - "longDescription": "Power commercial credit origination and portfolio management workflows with D&B Finance Analytics embedded AI decisioning layer.\n\nDun & Bradstreet connects the D&B Commercial Graph with proprietary customer data and credit policies to help evaluate applications, set credit limits, and uncover portfolio risk and opportunity.", - "developerName": "Dun & Bradstreet", - "category": "Finance", - "capabilities": [ - "Portfolio search, filtering, aggregation, and sorting", - "Company search by name or D-U-N-S number", - "Detailed company credit-risk reports", - "Company ownership and linkage trees", - "Credit application decisioning", - "Portfolio folder creation, movement, and organization", - "Server-provided Finance Analytics skill discovery" - ], - "websiteURL": "https://www.dnb.com/", - "privacyPolicyURL": "https://www.dnb.com/about-us/data-transparency.html", - "termsOfServiceURL": "https://view.highspot.com/viewer/1c5acd1db08e2cc64faff50703335a1d", - "brandColor": "#004B8D", - "defaultPrompt": [ - "Show me all companies in my portfolio with a failure score above 80, sorted by outstanding balance.", - "Give me a full credit risk report for Acme Corp, including their payment history and risk scores.", - "Who are the parent and subsidiary companies of Global Logistics Inc? Show me the full ownership tree." - ], - "screenshots": [], - "composerIcon": "./assets/app-icon.png", - "logo": "./assets/app-icon.png" - } -} diff --git a/plugins/dnb-finance-analytics/assets/app-icon.png b/plugins/dnb-finance-analytics/assets/app-icon.png deleted file mode 100644 index a5cfdaebb..000000000 Binary files a/plugins/dnb-finance-analytics/assets/app-icon.png and /dev/null differ diff --git a/plugins/dnb-finance-analytics/skills/fa-jobs-to-be-done/SKILL.md b/plugins/dnb-finance-analytics/skills/fa-jobs-to-be-done/SKILL.md deleted file mode 100644 index 0882e448e..000000000 --- a/plugins/dnb-finance-analytics/skills/fa-jobs-to-be-done/SKILL.md +++ /dev/null @@ -1,96 +0,0 @@ ---- -name: fa-jobs-to-be-done -description: Use when the user asks for D&B Finance Analytics workflows such as customer onboarding, credit decisioning, credit limit validation, portfolio risk management, company reports, ownership trees, folder management, or alerts. Use only the D&B Finance Analytics MCP tools for these workflows. -metadata: - mcp_server: finance_analytics_mcp_server - codex_plugin: dnb-finance-analytics ---- - -# Finance Analytics Jobs To Be Done - -This skill routes D&B Finance Analytics requests to the partner-supplied workflow -references in `fa-skills/references/`. - -## Tool Source - -Use the D&B Finance Analytics MCP server only. Do not substitute Morningstar, -Moody's, generic web search, local files, or another MCP server for Finance -Analytics data. - -Before making a tool call, confirm the Finance Analytics MCP tools are available. -If they are not visible, tell the user the D&B Finance Analytics connection is not -available and stop rather than fabricating data. - -Expected Finance Analytics tools include: - -- `fa_search_tool` -- `query_portfolio` -- `get_company_report` -- `get_live_report` -- `account_decisioning_tool` -- `application_decisioning_tool` -- `manage_folder` -- `get_company_ownership_tree` -- `query_alerts` -- `get_portfolio_status` -- `get_portfolio_public_records` -- `get_companies_and_accounts_count` -- `get_dashboard_overview` -- `get_risk_distribution` -- `get_dashboard_status` - -Tool names may be exposed with a Codex namespace such as -`mcp__finance_analytics_mcp_server__query_portfolio`; use the namespaced form when -available. - -## Route The Request - -Load exactly one workflow reference based on the user's intent: - -| Intent signals | Load this reference | -| --- | --- | -| onboard, new customer, new account, add to portfolio, can I do business with | `fa-skills/references/fa-onboarding-workflow.md` | -| credit decisioning, company overview for credit, financial profile, company report | `fa-skills/references/fa-credit-decisioning.md` | -| approve credit, credit limit, increase credit, validate credit, extend credit | `fa-skills/references/fa-credit-validation.md` | -| portfolio risk, top risky companies, portfolio overview, risk distribution, risky accounts | `fa-skills/references/fa-portfolio-management.md` | -| alerts, unread alerts, new alerts, alert severity, alerts for a company | `fa-skills/references/fa-alerts-monitoring.md` | - -If the user's intent is ambiguous, ask one clarifying question before calling tools. - -The partner bundle did not include dedicated persona files or an aging-forecast -workflow file. For persona-specific language, load `fa-skills/references/fa-plain-language.md`. -For aging forecast requests, explain that no aging-forecast workflow was supplied in -this skill bundle and ask whether the user wants to proceed with available portfolio -or report workflows instead. - -## Output Layer - -Before producing user-facing output, load: - -- `fa-skills/references/fa-plain-language.md` - -Use it for tone, numeric presentation, status summaries, and suppression of internal -tool or entity identifiers. - -## Required Behavior - -- Never fabricate or fill gaps with assumed D&B data. -- Retry a failed or unexpectedly empty Finance Analytics tool call once with the - same parameters. -- If a tool still fails, document what was attempted, what was missing, and how the - gap affects the result. -- Favor precision over recall when resolving companies or making risk assessments. -- Never make a binding credit approval or decline; frame credit outputs as suggested - decisions based on available data. -- Include an audit trail when the selected workflow requires one. -- Do not pause mid-pipeline unless a blocking ambiguity must be resolved before a - critical decision. - -## Workflow - -1. Identify the user intent. -2. Load `fa-plain-language.md`. -3. Load the matching workflow reference. -4. Follow that reference's tool order and output structure. -5. Cite the D&B Finance Analytics app as the data source when returning substantive - credit, portfolio, report, ownership, or alert results. diff --git a/plugins/dnb-finance-analytics/skills/fa-jobs-to-be-done/agents/openai.yaml b/plugins/dnb-finance-analytics/skills/fa-jobs-to-be-done/agents/openai.yaml deleted file mode 100644 index e21b8759e..000000000 --- a/plugins/dnb-finance-analytics/skills/fa-jobs-to-be-done/agents/openai.yaml +++ /dev/null @@ -1,9 +0,0 @@ -interface: - display_name: "D&B Finance Analytics" - short_description: "Credit origination and portfolio management with D&B Finance Analytics." - icon_small: "./assets/icon-small.png" - icon_large: "./assets/icon-large.png" - brand_color: "#3B82F6" - default_prompt: "Use $fa-jobs-to-be-done to review a company's credit profile with D&B Finance Analytics." -policy: - allow_implicit_invocation: true diff --git a/plugins/dnb-finance-analytics/skills/fa-jobs-to-be-done/assets/icon-large.png b/plugins/dnb-finance-analytics/skills/fa-jobs-to-be-done/assets/icon-large.png deleted file mode 100644 index 61bdac994..000000000 Binary files a/plugins/dnb-finance-analytics/skills/fa-jobs-to-be-done/assets/icon-large.png and /dev/null differ diff --git a/plugins/dnb-finance-analytics/skills/fa-jobs-to-be-done/assets/icon-small.png b/plugins/dnb-finance-analytics/skills/fa-jobs-to-be-done/assets/icon-small.png deleted file mode 100644 index a5cfdaebb..000000000 Binary files a/plugins/dnb-finance-analytics/skills/fa-jobs-to-be-done/assets/icon-small.png and /dev/null differ diff --git a/plugins/dnb-finance-analytics/skills/fa-jobs-to-be-done/fa-skills/references/fa-alerts-monitoring.md b/plugins/dnb-finance-analytics/skills/fa-jobs-to-be-done/fa-skills/references/fa-alerts-monitoring.md deleted file mode 100644 index 923bf505a..000000000 --- a/plugins/dnb-finance-analytics/skills/fa-jobs-to-be-done/fa-skills/references/fa-alerts-monitoring.md +++ /dev/null @@ -1,203 +0,0 @@ -# FA Alerts Monitoring - Sub-flow Reference - -**Tools used:** `query_alerts` - -This file defines the Alerts Monitoring workflow. It is loaded by the master `SKILL.md` -when user intent resolves to viewing, filtering, or triaging FA system alerts. - -**Core question answered by this workflow:** - -> "What alerts do I have - and which ones need my attention right now?" - -FA system alerts notify credit teams of changes, risk events, and required actions related -to portfolio companies. This workflow helps users find, filter, and act on alerts quickly. - ---- - -## Pipeline Overview - -``` -Stage 1: Retrieve and Filter Alerts - - Stage 2: Triage and Prioritise - - Stage 3: Summarise and Recommend Actions -``` - ---- - -## Trigger Examples - -- "Show me my alerts." -- "What unread alerts do I have?" -- "Show me all high-severity alerts." -- "Are there any alerts for Company X?" -- "What new alerts came in today?" -- "Show me all open alerts." -- "Which alerts need my immediate attention?" - ---- - -## Stage 1 - Retrieve and Filter Alerts - -**Purpose:** Call `query_alerts` with the appropriate filters to retrieve the relevant -alert set for the user's query. - -**Tool call sequence:** - -1. Determine the appropriate filter parameters based on the user's request: - - | User request | Filter to apply | - |---|---| - | "My alerts" / "All alerts" | No filter - retrieve all alerts | - | "Unread alerts" | `read_status = unread` | - | "New alerts" | `read_status = unread`, optionally filter by recent date | - | "Alerts for Company X" | `company_name = X` or `duns = ` | - | "High severity alerts" | `severity = high` | - | "High and medium severity" | `severity IN (high, medium)` | - | "Alerts by type" | `alert_type = ` | - | Count only | Include count aggregation parameter | - -2. Call `query_alerts` with the resolved filter parameters. - - If the user specified a company name but not a DUNS: resolve the DUNS first via - `fa_search_tool` or `query_portfolio` before filtering alerts by company. - - Request the following fields per alert: company name, DUNS, alert type, severity, - alert date, read/unread status, alert description / message, any associated account - or entity reference. - - Apply sort: `severity` descending, then `alert_date` descending (most recent first). - -3. If the result set is large (e.g. > 50 alerts): summarise by severity and type first, - then present the top N highest-severity or most recent unread alerts in detail. Ask - the user if they want to see more. - -**Required outputs:** - -- Total alert count matching the filter -- Count breakdown by severity (High / Medium / Low) -- Count breakdown by read status (Unread / Read) -- Alert list with: company name, alert type, severity, date, read status, brief description - -**Failure condition:** If `query_alerts` returns an error or empty result: -1. Retry once with the same parameters. -2. If still failing: document the failure and advise the user that alerts could not be - retrieved. Do not simulate alert data. - ---- - -## Stage 2 - Triage and Prioritise - -**Purpose:** Organise the returned alerts into a prioritised view so the user can act -on the most important items first. - -**Triage logic:** - -| Priority tier | Criteria | Action signal | -|---|---|---| -| Immediate action | High severity + Unread + Recent (within 24 hours) | Flag as "Requires immediate attention" | -| Review today | High severity + Unread (any date) OR Medium severity + Unread + Recent | Flag as "Review today" | -| Monitor | Medium severity + Read OR any Low severity | Flag as "Monitor - no immediate action" | -| Informational | Low severity + Read | Summarise only; do not surface in detail unless asked | - -**Apply triage to the retrieved alert set:** - -1. Group alerts by priority tier. -2. Within each tier, sort by alert date descending. -3. For "Immediate action" and "Review today" alerts: show full detail (company name, - alert type, severity, date, description). -4. For "Monitor" and "Informational" alerts: show a summary count and brief description - only, unless the user requests detail. - -**Required outputs:** - -- Tiered alert list with priority tier labels -- Immediate action alerts presented in full detail -- Review today alerts presented in full detail -- Monitor / Informational alerts presented as a count summary - -**Narrative:** State how many alerts require immediate attention, how many are for review -today, and how many are informational. Be specific about which companies and alert types -are at the top of the list. - ---- - -## Stage 3 - Summarise and Recommend Actions - -**Purpose:** Translate the alert triage into a clear action plan for the credit team. - -**Output format:** - -1. **Alert summary header:** - - Total alerts retrieved - - Unread count - - Breakdown by severity - -2. **Immediate action items:** - For each "Immediate action" alert: - - Company name, DUNS - - Alert type and brief description - - Why it requires immediate attention - - Recommended action - -3. **Review today items:** - For each "Review today" alert: - - Company name, DUNS - - Alert type and brief description - - Recommended action - -4. **Monitor items:** - - Summary count only (e.g. "12 low-severity alerts are being monitored - no action - required at this time.") - -5. **Recommended actions by alert type:** - - | Alert type | Recommended action | - |---|---| - | Bankruptcy / insolvency | Escalate immediately to credit committee; freeze further credit exposure | - | Payment delinquency | Initiate collections contact; review credit limit | - | Credit limit breach | Review credit terms; reduce limit or require immediate payment | - | Risk score deterioration | Pull updated company report; consider credit review | - | Legal filing (suit, lien, judgment) | Notify credit manager; assess impact on collectability | - | Ownership change | Review implications for credit policy; verify new ownership | - | Business closure / dissolution | Escalate immediately; assess exposure and collection options | - -**Required outputs:** - -- Alert summary (total, unread, by severity) -- Immediate action items with recommended actions -- Review today items with recommended actions -- Monitor summary count -- Full audit trail - -**Narrative:** Lead with the most urgent items. Be direct about what needs attention and -what action to take. Close with a clear summary of what is being monitored. - ---- - -## Handoff Contracts - -| Handoff | Payload | -|---|---| -| Stage 1 - Stage 2 | Full alert list with fields: company, DUNS, alert type, severity, date, read status, description | -| Stage 2 - Stage 3 | Tiered alert list (Immediate / Review today / Monitor / Informational) with priority labels | -| Stage 3 - Output | Alert summary, action items by tier, recommended actions by type, audit trail | - ---- - -## Known Tool Integration Notes - -| Tool | Issue | Handling | -|---|---|---| -| `query_alerts` | Supports filtering by company, alert type, severity, and read status | Always specify filters based on user intent to avoid over-fetching large alert sets | -| `query_alerts` | May return a large result set for broad queries | Summarise by severity and type first; present detail for high-priority alerts only | -| `query_alerts` | Company name filter may require DUNS for precision | Resolve DUNS via `fa_search_tool` or `query_portfolio` when the user specifies a company by name | - ---- - -## Behavioural Requirements (Alerts - specific) - -- Always lead with the highest-severity, unread, most recent alerts. -- Never mark alerts as read without the user's explicit instruction to do so. -- If the user asks for alerts for a specific company: resolve the company to a DUNS first - before filtering - company name matching in `query_alerts` may be imprecise. -- For very large alert sets (> 100 alerts): do not dump the full list. Summarise and ask - the user which tier or type they want to explore. -- Always include the complete audit trail at the bottom of the output. - diff --git a/plugins/dnb-finance-analytics/skills/fa-jobs-to-be-done/fa-skills/references/fa-credit-decisioning.md b/plugins/dnb-finance-analytics/skills/fa-jobs-to-be-done/fa-skills/references/fa-credit-decisioning.md deleted file mode 100644 index d752099ab..000000000 --- a/plugins/dnb-finance-analytics/skills/fa-jobs-to-be-done/fa-skills/references/fa-credit-decisioning.md +++ /dev/null @@ -1,305 +0,0 @@ -# FA Credit Decisioning - Sub-flow Reference - -**Tools used:** `fa_search_tool`, `query_portfolio`, `get_company_report` - -This file defines the Credit Decisioning workflow. It is loaded by the master -`SKILL.md` when user intent resolves to a credit decisioning request or a -company overview to support a credit decision. - -**Core question answered by this workflow:** - -> "Give me a full picture of [Company X] so I can make an informed credit decision." - -Use this workflow for broad, report-driven credit decisioning. Do not use it for -focused point questions (those call only the relevant tool directly) or for credit limit -validation requests (use `fa-credit-validation.md` instead). - ---- - -## Pipeline Overview - -``` -Stage 1: Resolve Company - -> Stage 2: Retrieve Report Data - -> Stage 3: Produce Credit Decisioning Report -``` - ---- - -## Trigger Examples - -- "Provide an overview of Company Z to help me make a credit decision." -- "Provide an overview of Company Z, focusing on recent financials, negative legal events, - and credit profile." -- "Run a credit decisioning for Company Z." -- "Generate the credit decisioning report for company X." -- Any question asking for a general company assessment that is NOT a portfolio question, - NOT a focused credit limit validation, and NOT a narrow single-metric query. - ---- - -## Stage 1 - Resolve Company - -**Purpose:** Identify the correct company and obtain its DUNS number. - -**Tool call sequence:** - -1. If the user provided a DUNS directly: use it. Skip `fa_search_tool`. -2. If the user provided only a company name: call `fa_search_tool` with name and country. - - If exactly one high-confidence match: proceed with that DUNS. - - If multiple matches: show a short table (name, country, DUNS) and ask the user to - select the correct company or provide the DUNS directly. - - If no match: report that no record was found and ask the user to verify the name. - -**Required outputs:** Confirmed company name, DUNS, country. - -**Failure condition:** Do not proceed without a resolved DUNS. - ---- - -## Stage 2 - Retrieve Report Data - -**Purpose:** Pull the full D&B company report to populate the Credit Decisioning Report -template. Use portfolio data to supplement where available. - -**Tool call sequence:** - -1. Call `get_company_report` with the resolved DUNS. - - Set `continue_report_pull` to `false` unless the user explicitly answered "yes" to - a previous prompt about report retrieval. Do NOT carry this parameter over from - earlier tool calls - each request requires a fresh evaluation. - - Extract all fields listed in the Data Extraction Guide below. -2. Call `query_portfolio` with the resolved DUNS to retrieve any current portfolio - account data (outstanding, past due, credit limit utilisation, account type). - - Portfolio data supplements the report but does not override it for risk scores. - - If the company is not in the portfolio, note this in the report. - -**Data Extraction Guide - fields to extract from `get_company_report`:** - -| Section | Field path | -|---|---| -| Company Name | `header.companyName` | -| DUNS | `header.duns` | -| Tradestyles | `header.tradestyles` | -| Business Status | `header.businessStatus` | -| Address | `header.address` (address1, city, stateCode, postalCode, country) | -| Phone | `header.phone` | -| Report Date | `header.reportDate` | -| PAYDEX Score | `scoring.paydex.score`, `.paymentBehavior`, `.days` | -| PAYDEX History | `scoring.paydex.history` | -| Delinquency Score | `scoring.delinquencyScore.score`, `.probability`, `.scoreCommentaryText` | -| Failure Score | `scoring.failureScore.score`, `.probability`, `.scoreCommentaryText` | -| D&B Rating | `scoring.currentDnbRating.score`, `.assignedRiskIndicatorMessageKey` | -| Viability Score | `scoring.viabilityScore.score`, `.viabRiskLevel`, `.portfolioRiskLevel` | -| Max Credit Recommendation | `scoring.maxCredit.amount`, `.currency`, `.riskLevel` | -| Overall Business Risk | `scoring.riskAssessment.overallAssessmentTextKey`, `.summaryRiskLevel` | -| Active/Bankruptcy Status | `scoring.activeStatus` | -| Annual Sales | `financialSummary.profitLoss.data.sales.current.value` | -| Net Worth | `financialSummary.balanceSheet.data.netWorth.current.value` | -| Total Assets | `financialSummary.balanceSheet.data.totalAsset.current.value` | -| Total Liabilities | `financialSummary.balanceSheet.data.totalLiabilities.current.value` | -| Current Assets | `financialSummary.balanceSheet.data.currentAsset.current.value` | -| Current Liabilities | `financialSummary.balanceSheet.data.currentLiabilities.current.value` | -| Pre-Tax Profit | `financialSummary.profitLoss.data.ebit.current.value` | -| Net Income | `financialSummary.profitLoss.data.netIncome.current.value` | -| Financial Source/Date | `financialSummary.source`, `.dateMonthYear` | -| Suits | `legalEvents.suitCount`, `.suitDate` | -| UCC Filings | `legalEvents.financingStatementFilingCount`, `.financingStatementFilingDate` | -| Judgments | `legalEvents.judgementCount`, `.judgementDate` | -| Liens | `legalEvents.lienCount`, `.lienDate` | -| Trade Payments | `tradePayments` object - avg high credit, now owing, past due, total experiences, trade within terms | -| Location Type | `ownership.locationType` | -| Member/Sub/Branch Count | `ownership.memberCount`, `.subsidiaryCount`, `.branchCount` | -| Named Principal | `companyProfile.namedPrincipal` | -| Control Ownership Date | `companyProfile.controlOwnershipDate` | -| Domestic/Global Ultimate | `ownership.ancestorInfos` | -| Employee Count | `keyDataElements.employees.minimumQuantity` | -| Business Start Year | `keyDataElements.businessStartYear`, `.ageofBusiness` | -| SIC/NAICS | `companyProfile.sicCode`, `.naicsCode` | -| Industry Description | `companyProfile.primaryIndustryCodeDescription` | - -For any field not available in the tool output: use `Not available via tools`. -For news and funding events: `get_company_report` does not provide these natively - mark as -`Not available via tools` unless a separate tool call is made. - -**Failure condition:** If `get_company_report` fails after one retry, document the gap and -produce a constrained report using any portfolio data available from `query_portfolio`. -Clearly label the report as constrained due to data unavailability. - ---- - -## Stage 3 - Produce Credit Decisioning Report - -**Purpose:** Populate and output the Credit Decisioning Report template using all data -retrieved in Stage 2. The template structure must be respected exactly. - -**Formatting rules:** - -- Use ATX headings only (`#`, `##`, `###`). No bolded pseudo-headings. -- Put a blank line after each heading and before/after every list or table. -- Do not start a list on the same line as a colon; move lists to the next line. -- Keep lists single level unless hierarchy requires nesting. -- Output `$` as `\$` to avoid encoding issues. - ---- - -## Credit Decisioning Report Template - -### Subject - -` (DUNS: <#########>)` - -### Executive Summary - -Directly provide conclusions and answer to the initial question. State the overall risk -level, maximum credit recommendation, and any significant flags in 2-3 sentences. - -### Risk Snapshot - -- Overall Business Risk: `` -- Probability of Severe Delinquency (12 months): ``% -- Industry Comparison: `` - -**Source:** `get_company_report` - -### Key Drivers of Credit Assessment - -- Risk Drivers: `` -- Payment Drivers: `` - -**Sources:** `get_company_report` - -### Company Profile - -- In Business Since: `` -- Foreign Exposure: `` -- Financials (Latest Summary): - - Tangible Net Worth - - Current Assets - - Total Fixed Assets - - Total Current Liabilities - - Long Term Liabilities - - Net Current Assets (Liabilities) - - Sales/Revenue - - Pre-Tax Profit - - Net Profit - -**Source:** `get_company_report` - -### Corporate Structure - -- Domestic Ultimate: `` -- Global Ultimate: `` -- Notes on Structure: `` - -**Source:** `get_company_report` (limited information may be available) - -### Funding and Material Events - -- Funding Events: `` -- Derogatory Legal Events: `` -- Ownership or Other Material Changes (Last Month): `` -- Significant Events: `` - -**Source:** `get_company_report` - -### News with Potentially Negative Impact - -From collected articles, identify only those that may indicate a negative credit impact. -Relevant signals include: - -- Weak financial results, margin deterioration, or negative guidance -- Rising leverage, liquidity constraints, refinancing pressure, or debt-related issues -- Governance concerns (leadership exits, investigations, accounting issues) -- Regulatory, legal, or credit decisioning risks -- Operational disruptions (supply chain issues, cyber incidents, accidents) -- Sector or macro headwinds that materially affect the company - -Exclude all articles that are neutral, irrelevant, or positive unless they contain embedded -credit-negative implications. - -**Output format - Summary:** - -Output the label `Summary:` exactly. - -- If one or more negatively relevant items are found: provide exactly one concise sentence - summarising key credit-negative themes. Group related topics into meaningful categories - (e.g., liquidity pressure, governance instability, regulatory risk). Do not include article - numbers or references in the summary. -- If no negatively relevant items are found: output exactly `Summary: No negatively impacting - news found.` Do not generate the article list in this case. - -**Output format - Relevant Articles** (only if negatively relevant items exist): - -Output the label `Relevant Articles:` exactly. Number items sequentially starting from 1 -(restart numbering in this section). For each article: provide a short title or one-sentence -overview, one sentence on the credit-relevant negative implication, and the article link. - -**Source:** `get_company_report` - -### Payment Behavior - -- Average Days Beyond Terms (DBT): `` -- Trend: `` -- Comparison to Industry: `` - -**Source:** `get_company_report` (using `paydex`, `trade_payment_indicators`) - -### Detailed Risk Metrics - -For Failure Score, Delinquency Score, and PAYDEX: - -- State the score as a percentile without `%` and without the word `percentile` -- Always include the probability value -- Always include the risk class and risk band - -**Sources:** `get_company_report` - -### Detailed Financials - -Present the same financial fields as in Company Profile. By default, present the latest -available financial snapshot. If historical data is available from `get_company_report`, -present it as a table with one metric per row and each available year as a column. Note -that more historical data may be available on explicit request. - -**Source:** `get_company_report` - -### Notes and Exceptions - -`` - ---- - -## Handoff Contracts - -| Handoff | Payload | -|---|---| -| Stage 1 -> Stage 2 | Confirmed company name, DUNS, country | -| Stage 2 -> Stage 3 | Full `get_company_report` output, supplementary `query_portfolio` data, identified data gaps | -| Stage 3 -> Output | Completed Credit Decisioning Report, audit trail | - ---- - -## Known Tool Integration Notes - -| Tool | Issue | Handling | -|---|---|---| -| `get_company_report` | `continue_report_pull` must be `false` unless user has explicitly approved the report pull for this request | Never carry `continue_report_pull=true` over from prior conversation turns | -| `get_company_report` | Does not natively provide news or funding events | Mark as `Not available via tools`; do not fabricate | -| `query_portfolio` | A company may have multiple accounts per DUNS | Retrieve all accounts; summarise outstanding and past due at DUNS level | -| `fa_search_tool` | May return multiple candidates | Present a selection table; do not auto-select when ambiguous | - ---- - -## Behavioural Requirements (Credit Decisioning - specific) - -- Never make a binding credit approval or decline. The report is an informational credit - assessment - final credit decisions must go through `fa-credit-validation.md` or the - decisioning tools. -- Do not skip any template section. Use `Not available via tools` or `Not applicable` for - missing data - never leave a section empty. -- Prefer `get_company_report` data over `query_portfolio` data for risk scores and financial - data. Portfolio data supplements the report; it does not replace it. -- Always include the complete audit trail at the bottom of the output. - - diff --git a/plugins/dnb-finance-analytics/skills/fa-jobs-to-be-done/fa-skills/references/fa-credit-validation.md b/plugins/dnb-finance-analytics/skills/fa-jobs-to-be-done/fa-skills/references/fa-credit-validation.md deleted file mode 100644 index 8c253d48b..000000000 --- a/plugins/dnb-finance-analytics/skills/fa-jobs-to-be-done/fa-skills/references/fa-credit-validation.md +++ /dev/null @@ -1,286 +0,0 @@ -# FA Credit Limit Validation - Sub-flow Reference - -**Tools used:** `fa_search_tool`, `query_portfolio`, `get_company_report` - -This file defines the Credit Limit Validation workflow. It is loaded by the master -`SKILL.md` when user intent resolves to credit limit approval, increase, validation, -or a concise credit decision recommendation for a specific company. - -**Core question answered by this workflow:** - -> "Can I approve $X for [Company Y]?" or "Should I increase the credit limit for this customer?" - -Use this workflow for concise, decision-focused credit limit assessments. Do not use it -for broad credit decisioning (use `fa-credit-decisioning.md`) or for creating new credit -applications (use the decisioning tools via `fa-onboarding-workflow.md`). - ---- - -## Pipeline Overview - -``` -Stage 1: Resolve Company - -> Stage 2: Retrieve Portfolio and Report Data - -> Stage 3: Produce Credit Validation Response -``` - ---- - -## Trigger Examples - -- "Can I approve a 50,000 credit limit for Company X?" -- "I am owed 100,000 by Company Y. Can I increase the credit limit to 200,000?" -- "Validate this customer for a credit increase." -- "Give me a concise credit decision view for Company Z." -- "Can X's credit limit be raised to 50K?" -- "Should I extend more credit to this account?" - ---- - -## Stage 1 - Resolve Company - -**Purpose:** Identify the correct company and obtain its DUNS number. - -**Tool call sequence:** - -1. If the user provided a DUNS directly: use it. Skip `fa_search_tool`. -2. If the user provided only a company name: call `fa_search_tool` with name and country. - - Identity resolution must use `fa_search_tool`, not `query_portfolio`. - - If exactly one high-confidence match: proceed. - - If multiple matches: show a short table (name, country, DUNS) and ask the user to - confirm the correct company or provide the DUNS directly. - - If no match: report that no record was found and ask the user to verify the name. - -**Required outputs:** Confirmed company name, DUNS, country. - -**Failure condition:** Do not proceed without a resolved DUNS. - ---- - -## Stage 2 - Retrieve Portfolio and Report Data - -**Purpose:** Gather the risk and financial data needed to perform the credit validation. -Portfolio data is the primary source; report data supplements missing fields only. - -**Tool call sequence:** - -1. Call `query_portfolio` with the resolved DUNS. - - Restrict lookup to `credit_file_type` = `ACCOUNT` or `DUNSRIGHT` unless the user - explicitly asks for another entity type. - - A single DUNS may have **multiple accounts** in the portfolio. Retrieve ALL accounts - associated with the DUNS. Never rely on a single account record for calculations. - - Extract: `overall_business_risk`, `max_credit_recommendation`, `paydex_current`, - `failure_score`, `delinquency_score`, `total_outstanding_dollars`, - `total_past_due_dollars`, `credit_limit_utilization`, `db_bankruptcy_present`, - trade payment indicators, legal event indicators, financial stress indicators. - - Calculate the **Aggregated Existing Outstanding Amount** by summing - `total_outstanding_dollars` across ALL accounts for the DUNS. Retain the - account-level split for inclusion in Supporting Factors. - - Calculate the **Aggregated Past Due Amount** by summing `total_past_due_dollars` - across ALL accounts. Retain the account-level split. - -2. Compare the returned fields against the required validation fields listed above. - - If all required fields are present in the portfolio record: proceed to Stage 3 - without calling `get_company_report`. - - If one or more required fields are missing from the portfolio record: call - `get_company_report` with the resolved DUNS and extract only the missing fields. - - **Portfolio values take precedence.** Never override a populated portfolio value - with `get_company_report` data. - -3. If the company is not in the portfolio at all: call `get_company_report` with the - resolved DUNS for all validation data. Note in the output that the company is not - in the portfolio. - -**Required outputs:** - -- Overall Business Risk (OBR) label -- Maximum Credit Recommendation (MCR) amount and currency -- PAYDEX / payment reliability score -- Failure Score and delinquency score -- Aggregated Existing Outstanding Amount (with account-level split if multiple accounts) -- Aggregated Past Due Amount -- Credit limit utilisation -- Bankruptcy and legal event flags -- Data source for each field (portfolio or report) - -**Failure condition:** If neither portfolio nor report data is available, say that the -company could not be resolved or that validation data is unavailable. Do not invent values. - ---- - -## Stage 3 - Produce Credit Validation Response - -**Purpose:** Produce a concise, decision-supporting credit validation output. The response -must be 10-15 lines total and follow the output template exactly. - -**Analysis rules:** - -- Use `overall_business_risk` (OBR) and `max_credit_recommendation` (MCR) as the primary - decision anchors. -- All validation calculations must be done at the DUNS level across ALL accounts. -- For credit extensions: - - `Total Exposure = Aggregated Existing Outstanding Amount + Requested Credit Amount` - - Validate Total Exposure against MCR, not only the requested credit amount. -- If Existing Outstanding Amount alone exceeds MCR: state this first before commenting - on the requested credit amount. -- If Existing Outstanding Amount is not available and the DUNS is in the portfolio: - compare only the requested credit amount and explicitly state that Total Exposure could not - be calculated. -- If DUNS is not in the portfolio (using only `get_company_report`): compare only the - requested credit amount against MCR. Do not mention Total Exposure. -- Never make a binding approval or decline. Frame the result as a suggested decision - based on current data. -- Do not create, run, suggest, or check an application workflow or automated decisioning - workflow unless the user explicitly asks for it. - -**Response contract (strict):** - -- Total length: 10-15 lines. -- Use exactly the sections in the Output Template below. -- Keep wording factual, short, and tool-grounded. -- Do not add background narrative, methodology sections, or full-report headings. -- All factual statements must be supported by explicit numbers (amounts, percentages, - dates, classes, bands). -- Do not reference internal data labels such as `DUNSRIGHT record`, `ACCOUNT record`, - `portfolio record`, or `report record` in user-facing output. - ---- - -## Output Template - -### Subject - -#### ` (DUNS: <#########>)` - -### Executive Summary - -Populate according to the applicable scenario below: - -- **When credit limit available and Total Exposure can be calculated:** - `` is assessed with an Overall Business Risk (OBR) of `` and a Maximum Credit Recommendation (MCR) of - ``. Total Potential Exposure of `` - (Existing Outstanding Amount `` + Requested Credit - Amount ``) is `` the maximum recommended credit - limit of `` by ``. `` - -- **When Existing Outstanding Amount alone exceeds MCR:** - `` is assessed with an OBR of `` and an MCR of ``. Existing Outstanding Amount of `` exceeds the MCR of - `` by ``; an additional Requested Credit - Amount of `` is not recommended. - -- **When Total Exposure cannot be calculated and DUNS is in portfolio:** - `` is assessed with an OBR of `` and an MCR of ``. The requested credit limit `` is `` - the MCR of `` by ``. Total Potential Exposure - could not be calculated because Existing Outstanding Amount is not available. - -- **When DUNS is not in portfolio (using only report data):** - `` is assessed with an OBR of `` and an MCR of ``. The requested credit limit `` is `` - the MCR of `` by ``. - -- **When MCR is not available:** - `` is assessed with an OBR of ``. Maximum Credit Recommendation - (MCR) is not available and hence a comparison cannot be made. - -### Credit Assessment - -- **Overall Business Risk (OBR):** `