diff --git a/FIRESTORE-NATIVE.md b/FIRESTORE-NATIVE.md index 7034f0e..ac36eb5 100644 --- a/FIRESTORE-NATIVE.md +++ b/FIRESTORE-NATIVE.md @@ -21,7 +21,7 @@ This section covers connecting to a Firestore instance. 2. **Handle Missing Variables**: If a command fails with an error message containing a placeholder like `${FIRESTORE_PROJECT}`, it signifies a missing environment variable. Inform the user which variable is missing and instruct them to set it. -3. **Handle Permission Errors**: If you encounter permission errors, ensure the user has the correct Firestore permissions (e.g., `datastore.entities.list`, `datastore.entities.create`). The user likely lacks the roles Cloud Datastore User (`roles/datastore.user`) and Firebase Rules Viewer (`roles/firebaserules.viewer`). You can provide these links for assistance: +3. **Handle Permission Errors**: If you encounter permission errors, ensure the user has the correct Firestore permissions (e.g., `datastore.entities.list`, `datastore.entities.create`). The user likely lacks the role Cloud Datastore User (`roles/datastore.user`). You can provide these links for assistance: * Granting Roles: https://cloud.google.com/iam/docs/grant-role-console * Firestore Permissions: https://cloud.google.com/iam/docs/roles-permissions/firestore diff --git a/README.md b/README.md index c908b9b..6c6072c 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ This repository provides a set of agent skills to interact with [Firestore](http - **Seamless Workflow:** Integrates seamlessly into your AI agent's environment. No need to constantly switch contexts for common database tasks. - **Natural Language Queries:** Stop wrestling with complex commands. Explore schemas and query data by describing what you want in plain English. -- **Data & Security Management:** Manage your Firestore data and security rules directly from your agent. +- **Data Management:** Manage your Firestore data directly from your agent. - **Code Generation:** Accelerate development by asking your agent to generate data classes and other code snippets based on your document structures. ## 📺 Video Walkthrough @@ -228,16 +228,12 @@ Interact with Firestore using natural language: - **Document Updates and Cleanup**: - "For all 20 test users you just found, please remove product-glasses(inactive) from their wishlist." - "Update the document with ID order-987 in the orders collection to set the status to 'Shipped'." -- **Security Rules Management**: - - "new_rules.txt is a new Firestore Security Rule I'm working on for staging. Can you validate it for me?" - - "Show me the active Firestore security rules for this project." ## Supported Skills The following skills are available in this repository: - [Firestore Data](./skills/firestore-data/SKILL.md) - Handles NoSQL document operations and collection hierarchy exploration. Use for CRUD tasks and data retrieval. Provides flexible document manipulation and structured querying. -- [Firestore Security](./skills/firestore-security/SKILL.md) - Manages access control and security compliance. Use when auditing permissions or deploying new security logic. Provides rule retrieval and syntax validation. ## Additional Agent Skills diff --git a/skills/firestore-security/SKILL.md b/skills/firestore-security/SKILL.md deleted file mode 100644 index d68be30..0000000 --- a/skills/firestore-security/SKILL.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -name: firestore-security -description: Manages access control and security compliance. Use when auditing permissions or deploying new security logic. Provides rule retrieval and syntax validation. ---- - -## Usage - -All scripts can be executed using Node.js. Replace `` and `` with actual values. - -**Bash:** -`node /scripts/.js '{"": ""}'` - -**PowerShell:** -`node /scripts/.js '{\"\": \"\"}'` - -Note: The scripts automatically load the environment variables from various .env files. Do not ask the user to set vars unless skill executions fails due to env var absence. - - -## Scripts - - -### get_rules - -Retrieves the active Firestore security rules for the current project - - - ---- - -### validate_rules - -Checks the provided Firestore Rules source for syntax and validation errors. Provide the source code to validate. - -#### Parameters - -| Name | Type | Description | Required | Default | -| :--- | :--- | :--- | :--- | :--- | -| source | string | The Firestore Rules source code to validate | Yes | | - - ---- - diff --git a/skills/firestore-security/scripts/get_rules.js b/skills/firestore-security/scripts/get_rules.js deleted file mode 100755 index 535bfc3..0000000 --- a/skills/firestore-security/scripts/get_rules.js +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env node - -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -const { spawn, execSync } = require('child_process'); -const path = require('path'); -const fs = require('fs'); -const os = require('os'); - -const toolName = "get_rules"; -const configArgs = ["--prebuilt", "firestore"]; - -const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ - 'FIRESTORE_DATABASE', -]; - - -function mergeEnvVars(env) { - if (process.env.GEMINI_CLI === '1') { - const envPath = path.resolve(__dirname, '../../../.env'); - if (fs.existsSync(envPath)) { - const envContent = fs.readFileSync(envPath, 'utf-8'); - envContent.split('\n').forEach(line => { - const trimmed = line.trim(); - if (trimmed && !trimmed.startsWith('#')) { - const splitIdx = trimmed.indexOf('='); - if (splitIdx !== -1) { - const key = trimmed.slice(0, splitIdx).trim(); - let value = trimmed.slice(splitIdx + 1).trim(); - value = value.replace(/(^['"]|['"]$)/g, ''); - if (env[key] === undefined) { - env[key] = value; - } - } - } - }); - } - } else if (process.env.CLAUDECODE === '1') { - const prefix = 'CLAUDE_PLUGIN_OPTION_'; - for (const key in process.env) { - if (key.startsWith(prefix)) { - env[key.substring(prefix.length)] = process.env[key]; - } - } - } -} - -function prepareEnvironment() { - let env = { ...process.env }; - let userAgent = "skills"; - if (process.env.GEMINI_CLI === '1') { - userAgent = "skills-geminicli"; - } else if (process.env.CLAUDECODE === '1') { - userAgent = "skills-claudecode"; - } else if (process.env.CODEX_CI === '1') { - userAgent = "skills-codex"; - } - mergeEnvVars(env); - - OPTIONAL_VARS_TO_OMIT_IF_EMPTY.forEach(varName => { - if (env[varName] === '') { - delete env[varName]; - } - }); - - - return { env, userAgent }; -} - -function main() { - const { env, userAgent } = prepareEnvironment(); - const args = process.argv.slice(2); - - const command = os.platform() === 'win32' ? 'npx.cmd' : 'npx'; - const processedArgs = os.platform() === 'win32' ? args.map(arg => arg.includes('"') ? '"' + arg.replace(/"/g, '""') + '"' : arg) : args; - const npxArgs = ["--yes", "@toolbox-sdk/server@1.1.0", "--log-level", "error", ...configArgs, "invoke", toolName, "--user-agent-metadata", userAgent, ...processedArgs]; - - const child = spawn(command, npxArgs, { shell: os.platform() === 'win32', stdio: 'inherit', env }); - - - child.on('close', (code) => { - process.exit(code); - }); - - child.on('error', (err) => { - console.error("Error executing toolbox:", err); - process.exit(1); - }); -} - -main(); diff --git a/skills/firestore-security/scripts/validate_rules.js b/skills/firestore-security/scripts/validate_rules.js deleted file mode 100755 index e9398d5..0000000 --- a/skills/firestore-security/scripts/validate_rules.js +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env node - -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -const { spawn, execSync } = require('child_process'); -const path = require('path'); -const fs = require('fs'); -const os = require('os'); - -const toolName = "validate_rules"; -const configArgs = ["--prebuilt", "firestore"]; - -const OPTIONAL_VARS_TO_OMIT_IF_EMPTY = [ - 'FIRESTORE_DATABASE', -]; - - -function mergeEnvVars(env) { - if (process.env.GEMINI_CLI === '1') { - const envPath = path.resolve(__dirname, '../../../.env'); - if (fs.existsSync(envPath)) { - const envContent = fs.readFileSync(envPath, 'utf-8'); - envContent.split('\n').forEach(line => { - const trimmed = line.trim(); - if (trimmed && !trimmed.startsWith('#')) { - const splitIdx = trimmed.indexOf('='); - if (splitIdx !== -1) { - const key = trimmed.slice(0, splitIdx).trim(); - let value = trimmed.slice(splitIdx + 1).trim(); - value = value.replace(/(^['"]|['"]$)/g, ''); - if (env[key] === undefined) { - env[key] = value; - } - } - } - }); - } - } else if (process.env.CLAUDECODE === '1') { - const prefix = 'CLAUDE_PLUGIN_OPTION_'; - for (const key in process.env) { - if (key.startsWith(prefix)) { - env[key.substring(prefix.length)] = process.env[key]; - } - } - } -} - -function prepareEnvironment() { - let env = { ...process.env }; - let userAgent = "skills"; - if (process.env.GEMINI_CLI === '1') { - userAgent = "skills-geminicli"; - } else if (process.env.CLAUDECODE === '1') { - userAgent = "skills-claudecode"; - } else if (process.env.CODEX_CI === '1') { - userAgent = "skills-codex"; - } - mergeEnvVars(env); - - OPTIONAL_VARS_TO_OMIT_IF_EMPTY.forEach(varName => { - if (env[varName] === '') { - delete env[varName]; - } - }); - - - return { env, userAgent }; -} - -function main() { - const { env, userAgent } = prepareEnvironment(); - const args = process.argv.slice(2); - - const command = os.platform() === 'win32' ? 'npx.cmd' : 'npx'; - const processedArgs = os.platform() === 'win32' ? args.map(arg => arg.includes('"') ? '"' + arg.replace(/"/g, '""') + '"' : arg) : args; - const npxArgs = ["--yes", "@toolbox-sdk/server@1.1.0", "--log-level", "error", ...configArgs, "invoke", toolName, "--user-agent-metadata", userAgent, ...processedArgs]; - - const child = spawn(command, npxArgs, { shell: os.platform() === 'win32', stdio: 'inherit', env }); - - - child.on('close', (code) => { - process.exit(code); - }); - - child.on('error', (err) => { - console.error("Error executing toolbox:", err); - process.exit(1); - }); -} - -main();