@mcpher/gas-fakes 2.3.13 → 2.3.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +11 -5
  2. package/gf_agent/README.md +101 -0
  3. package/gf_agent/SKILL.md +396 -0
  4. package/gf_agent/documentation.md +105 -0
  5. package/gf_agent/gf-agent-contributor/SKILL.md +56 -0
  6. package/gf_agent/index.md +21 -0
  7. package/gf_agent/knowledge/00-execution-context.md +4 -0
  8. package/gf_agent/knowledge/01-drive.md +12 -0
  9. package/gf_agent/knowledge/02-syntax.md +13 -0
  10. package/gf_agent/knowledge/03-auth.md +15 -0
  11. package/gf_agent/knowledge/04-advanced.md +24 -0
  12. package/gf_agent/knowledge/05-sheets-forms.md +25 -0
  13. package/gf_agent/knowledge/06-jdbc-cloudsql.md +21 -0
  14. package/gf_agent/knowledge/07-jdbc-auth-details.md +30 -0
  15. package/gf_agent/knowledge/08-docs-limitations.md +4 -0
  16. package/gf_agent/knowledge/09-orchestrator-pattern.md +54 -0
  17. package/gf_agent/knowledge/10-sandbox-security.md +61 -0
  18. package/gf_agent/knowledge/11-chart-builder-limitations.md +15 -0
  19. package/gf_agent/knowledge/12-gmail-eventual-consistency.md +13 -0
  20. package/gf_agent/knowledge/README.md +16 -0
  21. package/gf_agent/scripts/SKILL.template.md +65 -0
  22. package/gf_agent/scripts/builder.js +78 -47
  23. package/gf_agent/skills/base.md +156 -0
  24. package/gf_agent/skills/cache.md +20 -0
  25. package/gf_agent/skills/calendar.md +780 -0
  26. package/gf_agent/skills/charts.md +127 -0
  27. package/gf_agent/skills/document.md +6626 -0
  28. package/gf_agent/skills/drive.md +423 -0
  29. package/gf_agent/skills/forms.md +4036 -0
  30. package/gf_agent/skills/gmail.md +576 -0
  31. package/gf_agent/skills/jdbc.md +3101 -0
  32. package/gf_agent/skills/lock.md +20 -0
  33. package/gf_agent/skills/properties.md +19 -0
  34. package/gf_agent/skills/script.md +50 -0
  35. package/gf_agent/skills/slides.md +5054 -0
  36. package/gf_agent/skills/spreadsheet.md +56075 -0
  37. package/gf_agent/skills/urlfetch.md +28 -0
  38. package/gf_agent/skills/utilities.md +33 -0
  39. package/gf_agent/skills/xml.md +270 -0
  40. package/package.json +1 -1
  41. package/src/cli/mcp.js +82 -67
  42. package/src/cli/setup.js +87 -9
  43. package/src/services/advgmail/fakeadvgmailmessages.js +85 -3
  44. package/src/services/driveapp/fakedrivemeta.js +1 -1
  45. package/src/services/gmailapp/fakegmailapp.js +217 -1
  46. package/src/services/gmailapp/fakegmailattachment.js +5 -0
  47. package/src/services/gmailapp/fakegmaildraft.js +32 -4
  48. package/src/services/gmailapp/fakegmaillabel.js +45 -0
  49. package/src/services/gmailapp/fakegmailmessage.js +212 -9
  50. package/src/services/gmailapp/fakegmailthread.js +151 -1
  51. package/src/services/spreadsheetapp/fakeembeddedchartbuilder.js +113 -28
  52. package/src/support/sxgmail.js +22 -2
  53. package/docs_discovery.json +0 -4939
  54. package/drive_tools.js +0 -20
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # <img src="./logo.png" alt="gas-fakes logo" width="50" align="top"> Run Native Apps Script code anywhere with gas-fakes
1
+ # <img src="./pngs/logo.png" alt="gas-fakes logo" width="50" align="top"> Run Native Apps Script code anywhere with gas-fakes
2
2
 
3
3
  ## Google Apps Script, meet Local Development.
4
4
 
@@ -175,18 +175,23 @@ For inspiration on pushing modified files to the IDE, see the togas.sh bash scri
175
175
 
176
176
  As I mentioned earlier, to take this further, I'm going to need a lot of help to extend the methods and services supported - so if you feel this would be useful to you, and would like to collaborate, please ping me on bruce@mcpher.com and we'll talk.
177
177
 
178
- ## <img src="./logo.png" alt="gas-fakes logo" width="50" align="top"> Further Reading
178
+ ## <img src="./pngs/logo.png" alt="gas-fakes logo" width="50" align="top"> Further Reading
179
179
 
180
- ## Watch the video
180
+ ## Watch the gas-fakes intro video
181
181
 
182
- [![Watch the video](introvideo.png)](https://youtu.be/oEjpIrkYpEM)
182
+ [![Watch the intro video](./pngs/introvideo.png)](https://youtu.be/oEjpIrkYpEM)
183
+
184
+ ## Watch the gf_agent video on natural language automation
185
+
186
+ [![Use natural language with gf_agent](./pngs/gfagent.png)](https://youtu.be/lujByoX71HU)
183
187
 
184
188
  ## Read more docs
185
189
 
186
190
  - [gas fakes intro video](https://youtu.be/oEjpIrkYpEM)
187
191
  - [getting started](GETTING_STARTED.md) - how to handle authentication for Workspace scopes.
188
192
  - [readme](README.md)
189
- - [Natural Language Automation with Gemini Skills & MCP Server](gemini-skills-mcp.md) - new skills-based agent approach.
193
+ - [Natural Language Automation with Gemini Skills & MCP Server](notes/gemini-skills-mcp.md) - new skills-based agent approach.
194
+ - [Add agent skills to gf_agent](https://ramblings.mcpher.com/add-skills-gf_agent/)
190
195
  - [gf_agent documentation](../gf_agent/README.md) - instructions for the Gemini CLI automation agent and MCP server.
191
196
  - [gas fakes cli](notes/gas-fakes-cli.md)
192
197
  - [github actions using adc](https://github.com/brucemcpherson/gas-fakes-actions-adc)
@@ -239,3 +244,4 @@ As I mentioned earlier, to take this further, I'm going to need a lot of help to
239
244
  - [Secure and Streamlined Google Apps Script Development with gas-fakes CLI and Gemini CLI Extension](https://medium.com/google-cloud/secure-and-streamlined-google-apps-script-development-with-gas-fakes-cli-and-gemini-cli-extension-67bbce80e2c8)
240
245
  - [Secure and Conversational Google Workspace Automation: Integrating Gemini CLI with a gas-fakes MCP Server](https://medium.com/google-cloud/secure-and-conversational-google-workspace-automation-integrating-gemini-cli-with-a-gas-fakes-mcp-0a5341559865)
241
246
  - [A Fake-Sandbox for Google Apps Script: A Feasibility Study on Securely Executing Code Generated by Gemini CL](https://medium.com/google-cloud/a-fake-sandbox-for-google-apps-script-a-feasibility-study-on-securely-executing-code-generated-by-cc985ce5dae3)
247
+
@@ -0,0 +1,101 @@
1
+ # gf_agent - Google Apps Script Local Automation Agent
2
+
3
+ `gf_agent` is an interactive agent for Gemini CLI that allows you to automate Google Workspace tasks locally using [gas-fakes](https://github.com/brucemcpherson/gas-fakes).
4
+
5
+ *For full project details, see the [main gas-fakes README](../README.md).*
6
+
7
+ ## Requirements & Setup
8
+
9
+ Before using the agent or the MCP tools, you **must** configure your local environment to allow access to your Google Workspace account:
10
+
11
+ 1. **Manifest File**: You need a local `appsscript.json` file in your directory containing the necessary `oauthScopes` you intend to use.
12
+ 2. **Initialization**: Run `gas-fakes init` to generate a `.env` file and configure your auth parameters.
13
+ 3. **Authentication**: Run `gas-fakes auth` to log in and generate the tokens required to run the scripts.
14
+
15
+ ## Installation & Integration
16
+
17
+ *Note: You can skip these manual steps if you already chose to install the Gemini skills during the `gas-fakes init` process. The `init` command will automatically detect if you are in a local clone and link the skills appropriately.*
18
+
19
+ ### 1. Install the Skill Agent
20
+
21
+ **For General Users:**
22
+ You can install the `gf_agent` directly from this repository. To install **only** this skill:
23
+ ```bash
24
+ gemini skills install https://github.com/brucemcpherson/gas-fakes.git --path gf_agent
25
+ ```
26
+ *Note: This repository contains multiple specialized skills (for development, maintenance, etc.). Omitting the `--path` flag will install all of them.*
27
+
28
+ **For Contributors & Developers:**
29
+ Do **not** use the `install` command with the remote URL, as it will download a cached copy that is difficult to edit. Because `gf_agent` is a subfolder of the main `gas-fakes` repository, use Git Sparse Checkout to isolate it:
30
+
31
+ ```bash
32
+ mkdir gf_agent_standalone && cd gf_agent_standalone
33
+ git init
34
+ git remote add origin https://github.com/brucemcpherson/gas-fakes.git
35
+ git config core.sparseCheckout true
36
+ echo "gf_agent/*" >> .git/info/sparse-checkout
37
+ git pull origin main
38
+ gemini skills link ./gf_agent
39
+ ```
40
+
41
+ ### 2. Configure the MCP Server
42
+ To use Google Workspace services as tools within Gemini CLI, add the MCP server to your settings.
43
+
44
+ The easiest way is to use the built-in command (assuming you installed `gas-fakes` globally):
45
+ ```bash
46
+ gemini mcp add --scope project gas-fakes-mcp gas-fakes mcp
47
+ ```
48
+
49
+ *(Advanced: Alternatively, you can manually add the configuration to your `.gemini/settings.json` file instead of using the command above. See [documentation.md](documentation.md) for the manual JSON structure).*
50
+
51
+ ### 3. Requirements
52
+ Ensure dependencies are installed in your workspace:
53
+ ```bash
54
+ npm install
55
+ ```
56
+
57
+ ## Usage
58
+
59
+ Once the MCP server is configured, you don't need to use any special prefixes or agent names. Simply describe what you want to do in plain English:
60
+
61
+ - "Create a sheet called 'Latest Drive Files' and add my recently created Drive files to it."
62
+ - "Find latest 5 emails from 'Martin Hawksey' and summarize them in a Google Doc."
63
+ - "Add a meeting with Martin Hawksey to my calendar for tomorrow at 10am. Subject is gas-fakes agent"
64
+ - "Find my airports spreadsheet, and using the sheet with the most data, list to the console the 5 highest airports sorted by elevation high to low, and convert elevation to meters.
65
+
66
+ The LLM will automatically:
67
+ 1. Identify the correct Google Workspace services needed.
68
+ 2. Generate the appropriate Apps Script code.
69
+ 3. Execute it locally via `gas-fakes`.
70
+ 4. Confirm the results to you.
71
+
72
+ ## Antigravity & MCP Integration
73
+
74
+ `gas-fakes` can also act as an MCP (Model Context Protocol) server for tools like Antigravity. To launch the MCP server:
75
+
76
+ ```bash
77
+ gas-fakes mcp
78
+ ```
79
+
80
+ This allows Antigravity or other MCP-compatible clients to call Google Workspace tools directly through `gas-fakes`.
81
+
82
+
83
+ ## Why use gf_agent?
84
+
85
+ - **Fast Iteration**: Test your automation logic without waiting for the Apps Script IDE or deployment.
86
+ - **Security**: Run tasks locally using your own Google credentials (via ADC) without uploading code to a third-party service.
87
+ - **Powerful Integration**: Use Node.js libraries alongside Google Apps Script APIs.
88
+
89
+ ## Contributing to gf_agent
90
+
91
+ If you'd like to improve the agent by teaching it new tricks, outlining Apps Script API parity warnings, or documenting best practices, we welcome your contributions!
92
+
93
+ To avoid Git merge conflicts on the large monolithic `SKILL.md` file, the `gf_agent` uses a **modular knowledge base**.
94
+
95
+ 1. Clone or fork this repository (or just the `gf_agent` directory).
96
+ 2. Create a new Markdown file inside the `gf_agent/knowledge/` directory (e.g., `06-new-feature.md`).
97
+ 3. Write your specific instructions or lessons learned in that file.
98
+ 4. **Do NOT** attempt to compile `SKILL.md` yourself.
99
+ 5. Commit and submit a Pull Request with your new Markdown file.
100
+
101
+ Once merged, the repository maintainers will run the automated pipeline to compile your new knowledge into the master `gf_agent/SKILL.md` for all users! See the [Knowledge Base README](knowledge/README.md) or ask the `gf-agent-contributor` skill for more details.
@@ -0,0 +1,396 @@
1
+ ---
2
+ name: gf_agent
3
+ description: >
4
+ Specialized agent for automating Google Workspace tasks locally using gas-fakes.
5
+ Generates and executes Google Apps Script code on Node.js.
6
+ ---
7
+ # Skill: gf_agent
8
+
9
+ ## Overview
10
+ `gf_agent` is a specialized agent for automating Google Workspace tasks using the `gas-fakes` local emulation environment. It can generate and execute Google Apps Script (GAS) code locally on Node.js.
11
+
12
+ ## Capabilities
13
+ - **Automate Workspace**: Create, read, and modify Google Docs, Sheets, Slides, Forms, Drive files, and Calendars.
14
+ - **Local Execution**: Run scripts using `@mcpher/gas-fakes` to avoid needing a live Apps Script environment for every task.
15
+ - **Service Integration**: Seamlessly combine multiple Google services (e.g., fetch data from a Sheet and create a Doc).
16
+ - **Mock/Real Parity**: Write code that works both locally (using fakes) and on the real Google Apps Script platform.
17
+
18
+ ## Instructions
19
+ 1. **Understand and Orchestrate**:
20
+ - Identify required Google Apps Script services (e.g., SpreadsheetApp, DriveApp).
21
+ - For multi-service or complex tasks, adopt the **Orchestrator Pattern**: Plan the task as a sequence of service-specific sub-tasks.
22
+ 2. **Research and Verify (Service Agent Phase)**:
23
+ - For each service, first verify method existence by reading the local `skills/{service}.md` files in the skill directory.
24
+ - If detailed method signatures or descriptions are needed, fetch the latest implementation details from the remote `gas-fakes` repository.
25
+ - **Remote URL**: `https://raw.githubusercontent.com/brucemcpherson/gas-fakes/main/progress/{service}.md` (Note: `{service}` is lowercase, e.g., `spreadsheet.md`).
26
+ - Use these details to construct precise, parity-compliant code without relying on external search engines unless documentation is missing.
27
+ 3. **Generate Script**: Create a Node.js script that:
28
+ - Imports `@mcpher/gas-fakes`.
29
+ - Uses standard GAS syntax.
30
+ - (Optional) Uses `ScriptApp.isFake` for local-only logic like logging or cleanup.
31
+ 4. **Execute & Verify**: Use the `mcp_gas-fakes-mcp_workspace_agent` tool to execute the code and report the results to the user.
32
+
33
+ ## Example Workflow
34
+ User: "Summarize the last 5 unread emails and save the summary to a new Google Doc."
35
+ Agent:
36
+ 1. **Identify Services**: `GmailApp`, `DocumentApp`.
37
+ 2. **Verify Methods**:
38
+ - Check `skills/gmail.md` and `skills/document.md`.
39
+ - (Optional) Fetch `progress/gmail.md` and `progress/document.md` from GitHub for detailed signatures.
40
+ 3. **Generate Script**:
41
+ ```javascript
42
+ import '@mcpher/gas-fakes';
43
+ const threads = GmailApp.getInboxThreads(0, 5);
44
+ let summary = 'Email Summary:\n\n';
45
+ threads.forEach(t => {
46
+ const msg = t.getMessages()[0];
47
+ summary += `From: ${msg.getFrom()}\nSubject: ${msg.getSubject()}\n---\n`;
48
+ });
49
+ const doc = DocumentApp.create('Email Summaries');
50
+ doc.getBody().appendParagraph(summary);
51
+ console.log('Summary saved to Doc ID:', doc.getId());
52
+ ```
53
+ 4. **Execute**: Call `mcp_gas-fakes-mcp_workspace_agent({ script: '...' })`.
54
+ 5. **Report**: Confirm completion and provide the Doc ID.
55
+
56
+ ## Notes
57
+ - Always use ES modules (`import`).
58
+ - Note that the Apps Script Services are all automatically available- for Example DriveApp, SpreadsheetApp, etc. are all available in the global namespace - no need to import them.
59
+ - the manifest file is used to conteol which scopes are required. dwd is the preferred authentication method but it needs the user to enable it from the domain admain console during the authentication stage.
60
+ - Advanced Service versions of the services are available - and map to their apps script equivalents. These are also available via the global namespace for example Drive, Sheets , etc.
61
+ - Note that Apps Script is synchronous. gas-fake emulates this so all calls to services will be synchronous
62
+ - Where possible, use the native Apps Script service (for example DriveApp) in preference to the advanced services (Drive, Sheets, etc.)
63
+
64
+ ## Lessons Learned & Best Practices (from Test Patterns)
65
+
66
+
67
+ ### Execution Context & Artifacts (CRITICAL)
68
+ - **Role Boundary**: You are the `gf_agent` operating on behalf of an end-user to automate Google Workspace tasks. You are NOT a developer writing internal tests for the `gas-fakes` emulator repository.
69
+ - **Transient Execution**: When fulfilling automation requests (e.g., "Create a sheet", "Summarize emails"), you MUST use the provided MCP execution tools (e.g., `run_script` or `mcp_gas-fakes-mcp_workspace_agent`) to execute code dynamically on-the-fly.
70
+ - **No Permanent Artifacts**: DO NOT write script files to disk (e.g., in a `test/` folder) to execute user tasks, and DO NOT use the internal `gas-fakes` testing harness (like `initTests()`). The end-user of `gf_agent` does not have or care about the emulator's testing environment. Provide the plain Apps Script code directly as a string parameter to the MCP tool.
71
+
72
+ ### Efficient Drive Searching (Best Practice)
73
+ - **Prefer `searchFiles()` over manual iteration**: When looking for specific files (e.g., by name, date, or parent), always use `DriveApp.searchFiles(query)` instead of `DriveApp.getFiles()` with manual filtering. Searching happens on the server and is significantly faster.
74
+ - **Date Formatting for Queries**: When searching by date (e.g., `modifiedTime` or `createdTime`), you MUST use the RFC3339 format (e.g., `YYYY-MM-DDThh:mm:ss`).
75
+ - **Crucial**: Use `modifiedTime` instead of `modifiedDate` when querying Drive via `gas-fakes`, as it maps to the Drive API v3 field.
76
+ - *Example*: `DriveApp.searchFiles("modifiedTime >= '2024-04-24T00:00:00'")`.
77
+
78
+ ### Advanced Service Versioning (v3 Preference)
79
+ - **Drive API**: `gas-fakes` follows the **Drive API v3** naming convention.
80
+ - Use `Drive.Files.create()` instead of `insert()`.
81
+ - Use `name` instead of `title` in resource objects.
82
+ - If a method from a live Apps Script snippet fails, check for its v3 equivalent before assuming it is missing.
83
+ - **Service Discovery**: If unsure about available methods, run a short `workspace_agent` script to log `Object.keys(Service.SubService)` to confirm implemented endpoints.
84
+
85
+
86
+ ### Common Apps Script Syntax Gotchas (First-Time Accuracy)
87
+ - **File Conversion (Exporting to PDF)**: While live Apps Script can seamlessly convert text files (`text/plain`) to PDF using `file.getAs('application/pdf')`, the underlying Google Drive API **only supports exporting Docs Editor files** (Docs, Sheets, Slides).
88
+ - **Automated Workaround in gas-fakes**: `gas-fakes` handles this transparently! If you attempt to convert a non-editor file to PDF locally via `.getAs()`, it automatically performs a temporary two-step conversion (copying it to a Google Doc, exporting it, and trashing the temp file). This ensures parity with live Apps Script without manual intervention.
89
+ - **Google Docs Formatting**: You CANNOT apply formatting (bold, italic, etc.) directly to a `Paragraph` or `ListItem`. You MUST use `editAsText()` first.
90
+ - *Incorrect*: `paragraph.setItalic(true)`
91
+ - *Correct*: `paragraph.editAsText().setItalic(true)`
92
+ - **Google Docs Element Detachment**: To move or duplicate an element (like an image, paragraph, or table) within a document, you **cannot** just append it if it's already attached to the document tree. You must either detach it (`element.removeFromParent()`) or copy it (`element.copy()`) first, otherwise an "Element must be detached" error is thrown.
93
+ - **Google Docs Headers/Footers**: You can only add one header/footer to a document tab. Calling `doc.addHeader()` twice will throw an error.
94
+ - **Google Sheets Developer Metadata**: You **cannot** add `DeveloperMetadata` to an arbitrary range (e.g., `A1:B2`) or a partial row/column. It **only** works on full sheets, full rows (`9:9`), full columns (`J:J`), or the spreadsheet itself.
95
+ - **Google Sheets Filters**: A sheet can only have one filter at a time. Attempting to call `range.createFilter()` on a sheet that already has a filter will throw an error.
96
+ - **Google Drive Blob Naming**: When creating a file from a Blob (`DriveApp.createFile(blob)`), the Blob **must** have a name. `Utilities.newBlob("content")` does not set a name by default and will throw "Blob object must have non-null name". Always use `Utilities.newBlob("content", "text/plain", "filename.txt")`.
97
+ - **Flush Requirements**: If your script creates a resource and immediately tries to perform a complex search or metadata operation on it, call `SpreadsheetApp.flush()` or `doc.saveAndClose()` to ensure the state is synchronized.
98
+ - **Date Comparison**: When comparing dates from `getLastUpdated()` or `getDateCreated()`, remember they are JavaScript `Date` objects. Use `.getTime()` for reliable numerical comparison.
99
+
100
+
101
+ ### Authentication & Troubleshooting
102
+ - **Permission Denied/Auth Failures**: Most authentication errors stem from a mismatch between the script's required scopes and the authorized environment.
103
+ - **Manifest & Scopes**: `gas-fakes` reads `appsscript.json` to discover required scopes. If these scopes weren't authorized during `gas-fakes auth`, the script will fail.
104
+ - **Advice for User (Auth Methods)**: When a user needs help setting up authentication for `gas-fakes`, advise them based on their specific situation:
105
+ - **Service Account (Default)**: Recommend this for most automated tasks or server environments. They need a GCP project, a Service Account, and its JSON key file.
106
+ - **DWD (Domain Wide Delegation)**: Recommend this if the script needs to act on behalf of other users in a Google Workspace domain. Remind the user that the Service Account must be explicitly authorized in the Google Workspace Admin Console (Security > API Controls > Domain-wide Delegation) for the specific scopes being used, and they must provide the `Subject` email address during the `gas-fakes auth` flow.
107
+ - **Desktop/OAuth (CLI)**: Recommend this for personal scripting or if they cannot use a Service Account. It requires `OAuth Client ID` credentials from GCP (Application type: Desktop) and will prompt them to authenticate via a browser window.
108
+ - **Initialization**: Ensure the project has been initialized using `gas-fakes init`. The `.env` file must contain the correct `GF_PLATFORM_AUTH` and associated credentials.
109
+
110
+ ### Parity & Platform Logic
111
+ - **`ScriptApp.isFake`**: Use this boolean to detect the `gas-fakes` environment.
112
+ - **Best Practice**: Guard **only** infrastructure logic (logging, cache checks, special backends) with this flag.
113
+ - **Warning**: Do **not** use it to change the core business logic of a script, as this defeats the purpose of parity.
114
+ - **Backend Selection**: `ScriptApp.__platform` can be dynamically switched to target `google`, `ksuite`, or `msgraph`.
115
+ - **Self-Correcting**: `gas-fakes` resources (Files, Sheets) "remember" their platform at creation. Subsequent calls on that object will automatically use the correct backend even if `ScriptApp.__platform` has changed globally.
116
+
117
+
118
+ ### Advanced Service Usage & Interop
119
+ - **Metadata Access**: If a standard service object (e.g., `Spreadsheet`) doesn't expose a specific property, use `Service.__getMetaProps(fields)` or `Service.__getMeta()` to fetch the underlying JSON resource from the Google API.
120
+ - **Batching and Flush**:
121
+ - **Spreadsheets**: Use `SpreadsheetApp.flush()` to force calculation and commitment of values.
122
+ - **Documents**: Use `doc.saveAndClose()` followed by `DocumentApp.openById(id)` to synchronize the "Shadow Document" state with the Google servers.
123
+ - **Advanced Drive (v3)**:
124
+ - **List Files**: `Drive.Files.list({ q: "...", fields: "files(id, name)" })` is the most efficient way to batch fetch file metadata.
125
+ - **Permissions**: `Drive.Permissions.create({ role, type, emailAddress }, fileId)` is preferred for programmatic sharing.
126
+ - **Iterators**: `gas-fakes` iterators (like `FileIterator`) implement the native Apps Script `hasNext()` and `next()` methods, which differ from standard JavaScript iterators.
127
+
128
+ ### Google Docs & Images
129
+ - **Inline Image Resizing**: Native methods like `setWidth()`/`setHeight()` are **NOT** implemented.
130
+ - **Conversion**: To create a Google Doc from HTML, use `Drive.Files.create()` with the correct v3 parameters:
131
+ ```javascript
132
+ const resource = { name: "Doc Name", mimeType: "application/vnd.google-apps.document" };
133
+ Drive.Files.create(resource, htmlBlob);
134
+ ```
135
+ - **Resizing Workaround**: The Docs API does not support updating image properties. You must **delete and re-insert** the image with the new dimensions.
136
+ - **Crucial**: Always sort your delete/insert requests by `startIndex` in **descending order** when performing multiple operations in a single `batchUpdate`. This prevents index shifting from invalidating subsequent operations in the same batch.
137
+ - **Shadow Document & Named Ranges**: `gas-fakes` uses a "Shadow Document" approach. Elements are tracked using Named Range tags to maintain positional integrity during updates.
138
+ - **Table Creation**: `appendTable()` without arguments creates a 1x1 table in `gas-fakes`, whereas live Apps Script creates an empty table stub.
139
+ - **Rate Limiting (429 Errors)**: Because `gas-fakes` translates local calls into real-time API requests, making rapid, successive calls like `appendParagraph()` in a loop will trigger Google's rate limit.
140
+ - **Best Practice**: Concatenate strings locally and make a single `appendParagraph()` call, rather than appending multiple short lines separately.
141
+ - **Real-time Feedback**: If you see retryable 429 errors in the console output during script execution, you MUST inform the user that the process is experiencing rate limiting but that `gas-fakes` is automatically handling retries.
142
+
143
+
144
+ ### Google Sheets (SpreadsheetApp)
145
+ - **Chart Creation & Ranges**: When using `EmbeddedChartBuilder.addRange()`, the Sheets API requires `ChartSourceRange` domains and series to have a length of 1 for either rows or columns.
146
+ - **Crucial**: Do **not** pass a multi-column range to `addRange()`. Add domains and series as separate single-column ranges.
147
+ - **Values vs. Display Values**:
148
+ - `getValues()` returns unformatted data.
149
+ - `getDisplayValues()` returns formatted strings.
150
+ - `setValues()` uses "USER_ENTERED" mode.
151
+ - **Bulk Operations (RangeList)**: Use `sheet.getRangeList(['A1', 'C1', 'E1'])` for multi-range formatting.
152
+
153
+ ### Google Forms (FormApp)
154
+ - **Programmatic Submission**: The public Forms API does **not** support submitting responses. `gas-fakes` uses a "web submission hack" that temporarily makes the form public to scrape tokens and POST the response.
155
+ - **Choice IDs**: The Forms API uses hex string IDs, while Apps Script uses numbers. `gas-fakes` handles this conversion automatically.
156
+
157
+ ### JDBC
158
+ - **MySQL 8+ Authentication**: You must downgrade users to `mysql_native_password` on the server for successful connection.
159
+ - **BigDecimal**: Always wrap `getBigDecimal()` result in `Number()` or `parseFloat()` for cross-platform compatibility.
160
+
161
+ ### Chart Generation Parity (SpreadsheetApp.newChart)
162
+ When implementing Google Sheets Embedded Charts, be aware of the following Live Apps Script vs. REST API oddities:
163
+ - **Enum Strictness**: Live GAS actively rejects string literals for `Charts` Enums (e.g., passing `"SHOW_ALL"` instead of `Charts.ChartHiddenDimensionStrategy.SHOW_ALL` throws `The parameters (String) don't match the method signature`). Ensure your generated test scripts strictly use the Enum objects.
164
+ - **Method Availability**: Live GAS does not support visual formatting methods (`setTitle`, `setBackgroundColor`) on the generic `EmbeddedChartBuilder` returned by `sheet.newChart()`. They are only available *after* casting to a specific builder (e.g., `.asPieChart().setTitle(...)`).
165
+ - **Hidden Dimensions Crash**: `setHiddenDimensionStrategy` will throw a backend `Unexpected error` if called before assigning a chart type, or if called on an incompatible type (like a Pie chart or Table chart). Only call it on compatible builders (like a Bar or Column chart).
166
+
167
+ ### Environment-Agnostic Test Design
168
+ - When generating scripts or test assertions designed to run interoperably (both locally on Node.js and on Live Apps Script), **never assert against internal private properties** (e.g., properties prefixed with `__`, like `__apiChart`). These properties do not exist on the Live Apps Script Java classes and will cause the script to crash in the cloud environment. Only assert against public, documented getter/setter methods.
169
+
170
+
171
+ ### JDBC & Cloud SQL Troubleshooting
172
+
173
+ #### 1. MySQL 8.x/8.4 Authentication Plugin
174
+ **The Problem:** Apps Script's JDBC driver is incompatible with the default `caching_sha2_password` security in MySQL 8.0+.
175
+ **The Solution:** Use the legacy `mysql_native_password` plugin.
176
+ 1. **Enable Database Flag:** In Cloud SQL Console, add the flag `mysql_native_password` and set it to `on`.
177
+ 2. **Downgrade User:** Run `ALTER USER 'your-user'@'%' IDENTIFIED WITH mysql_native_password BY 'your-password';`.
178
+
179
+ #### 2. GCP Project Configuration
180
+ **The Problem:** Connection fails even with correct DB credentials because the script isn't authorized to "tunnel" to the instance.
181
+ **The Solution:**
182
+ 1. **Enable API:** Ensure the **Cloud SQL Admin API** is enabled in the Google Cloud project.
183
+ 2. **IAM Role:** The user running the script (or the service account) must have the **Cloud SQL Client** (`roles/cloudsql.client`) role.
184
+ 3. **OAuth Scopes:** The script must include the `https://www.googleapis.com/auth/sqlservice` scope. Add this to your `appsscript.json` manifest if not automatically prompted.
185
+
186
+ #### 3. Connection Syntax
187
+ **The Problem:** Using IP addresses with `getCloudSqlConnection`.
188
+ **The Solution:** Use the **Instance Connection Name** (`project:region:instance`).
189
+ - **MySQL:** `jdbc:google:mysql://INSTANCE_CONNECTION_NAME/DATABASE_NAME`
190
+ - **PostgreSQL:** `jdbc:google:postgres://INSTANCE_CONNECTION_NAME/DATABASE_NAME`
191
+ - **SQL Server:** `jdbc:google:sqlserver://INSTANCE_CONNECTION_NAME/DATABASE_NAME`
192
+
193
+ ### JDBC & Cloud SQL Authentication Guide
194
+
195
+ #### 1. "Failed to establish a database connection" (Generic Error)
196
+ This is the most common error. Systematically check:
197
+ - **Cloud SQL Admin API**: MUST be enabled in the Google Cloud Console for the project.
198
+ - **IAM Permissions**: The authenticated user (or service account) must have the **Cloud SQL Client** role (`roles/cloudsql.client`).
199
+ - **OAuth Scopes**: Your `appsscript.json` manifest must include the `"https://www.googleapis.com/auth/sqlservice"` scope.
200
+ - **Instance Connection Name**: Ensure you are using the full name (`project-id:region:instance-id`) and not just the instance ID or IP address when using `getCloudSqlConnection`.
201
+
202
+ #### 2. "Access denied for user" (Database Level)
203
+ Even if the secure tunnel is established, the database engine may reject the user:
204
+ - **Host Wildcard**: In MySQL/PostgreSQL, users are often restricted by host (e.g., `'user'@'localhost'`). Connections via `getCloudSqlConnection` appear as coming from an internal Google network. Ensure your user is created with a wildcard host or a host that allows Google's internal ranges (e.g., `'user'@'%'`).
205
+ - **Password Complexity**: While `Jdbc.getCloudSqlConnection` handles passwords as separate arguments (avoiding URI encoding issues), double-check for typos or expired passwords.
206
+
207
+ #### 3. MySQL 8.0+ Authentication Plugin
208
+ MySQL 8.0+ uses `caching_sha2_password` by default, which is incompatible with the Apps Script JDBC driver.
209
+ - **Symptom**: `Handshake failed` or `Access denied` even with correct credentials.
210
+ - **Fix**:
211
+ 1. In the Cloud SQL Console, add the database flag `mysql_native_password` and set it to `on`.
212
+ 2. Run the following SQL to downgrade the user: `ALTER USER 'your-user'@'%' IDENTIFIED WITH mysql_native_password BY 'your-password';`.
213
+
214
+ #### 4. Connection Methods: Tunneling vs. Public IP
215
+ - **Method A: `Jdbc.getCloudSqlConnection(url, user, password)` (Recommended)**
216
+ - Works for MySQL and PostgreSQL.
217
+ - Uses an internal secure tunnel; **no IP whitelisting required**.
218
+ - URL format: `jdbc:google:mysql://INSTANCE_CONNECTION_NAME/DATABASE_NAME`
219
+ - **Method B: `Jdbc.getConnection(url, user, password)`**
220
+ - Required for **SQL Server** or non-Google hosted databases.
221
+ - **IP Whitelisting**: You MUST whitelist Apps Script's public IP ranges in your database firewall/networking settings.
222
+ - URL format: `jdbc:sqlserver://PUBLIC_IP:1433;databaseName=DB_NAME`
223
+
224
+
225
+ ### Google Docs API Limitations
226
+
227
+ - **Horizontal Rules**: The underlying Google Docs API does not support creating, updating, or managing `HorizontalRule` elements. Because `gas-fakes` maps local calls to the REST API, attempting to use methods like `body.appendHorizontalRule()` or `body.insertHorizontalRule()` will crash the script with a `GoogleJsonResponseException` (e.g., `Invalid requests...`).
228
+ - **Workaround**: If you need to visually separate content in a generated Document, use simple text-based separators like `body.appendParagraph('--------------------------------------------------')` instead.
229
+
230
+
231
+ # Orchestrator and Service Agent Pattern
232
+
233
+ To improve the reliability and accuracy of `gf_agent` when handling complex Google Workspace tasks, the agent should follow an **Orchestrator/Service Agent** architecture. This pattern minimizes "tool space interference" and ensures that the agent always uses the most accurate and up-to-date implementation details for each service.
234
+
235
+ ## The Problem: Tool Space Interference
236
+ When an agent attempts to handle multiple services (e.g., Spreadsheet, Drive, and Gmail) in a single turn, the context can become overwhelmed with conflicting method signatures or outdated knowledge from external search results. This often leads to "hallucinated" methods or incorrect parameter usage.
237
+
238
+ ## The Solution: Modular Delegation
239
+
240
+ ### 1. The Orchestrator Phase
241
+ Upon receiving a user request, the agent acts as an **Orchestrator**.
242
+ - **Identify Services**: Determine exactly which Apps Script services are required (e.g., `SpreadsheetApp`, `DriveApp`).
243
+ - **Decomposition**: Break the request into service-specific sub-tasks.
244
+ - **Service Verification**: Verify that the required services and classes exist by checking the local `skills/` directory within the `gf_agent` skill.
245
+
246
+ ### 2. The Service Agent Phase (Context Compression)
247
+ For each identified service, the main agent MUST invoke a **sub-agent** (e.g., `generalist`) to perform the deep research.
248
+ - **Why?**: Large documentation files (like `spreadsheet.md`) can bloat the main context window. Sub-agents run in isolated environments and return only a distilled summary.
249
+ - **Process**:
250
+ 1. Main Agent calls `invoke_agent` with the instruction: "Research Class X in Service Y from remote docs. Return ONLY the method signatures for A, B, and C."
251
+ 2. Sub-Agent uses `curl` + `awk` (or `web_fetch`) to find the information.
252
+ 3. Sub-Agent returns a precise report.
253
+ 4. The main context stays lean.
254
+
255
+ - **Remote Document Retrieval (Sub-Agent Technique)**:
256
+ - Sub-agents should use `run_shell_command` with `curl` and `awk` for surgical class extraction.
257
+ - **Branch Routing (CRITICAL)**:
258
+ - If the user is operating within the `gas-fakes` repository (developer mode), the sub-agent MUST run `git branch --show-current` to determine the active branch, and use THAT branch name in the URL. This ensures they get work-in-progress signatures.
259
+ - If the user is operating outside the repository (end-user mode), the sub-agent MUST ALWAYS use the `main` branch.
260
+ - **Command Template**:
261
+ - `curl -s https://raw.githubusercontent.com/brucemcpherson/gas-fakes/{BRANCH_NAME}/progress/{Service}.md | awk '/^## Class: \[{ClassName}\]/{flag=1; print; next} /^## Class:/{if(flag) {flag=0; exit}} flag'`
262
+ - *(Note: `{Service}.md` is case-sensitive. Check `gf_agent/skills/` for the exact casing, e.g., `Spreadsheet.md`)*
263
+
264
+ ### 3. Synthesis and Execution
265
+ Once all required service-specific knowledge is gathered:
266
+ - **Unified Implementation**: Combine the gathered patterns into a single, cohesive Apps Script block.
267
+ - **Execution**: Use the `mcp_gas-fakes-mcp_workspace_agent` to execute the script.
268
+ - **Validation**: If the script fails, the agent returns to the Service Agent phase for the failing service to re-verify the implementation details.
269
+
270
+ ## Benefits
271
+ - **Zero-Search Dependency**: By using the remote `progress` files as the primary source of truth, the agent avoids the risk of using outdated or non-parity GAS snippets found on the web.
272
+ - **Context Efficiency**: Researching one service at a time prevents the "interference" of unrelated documentation.
273
+ - **Parity Guarantee**: The remote documentation is generated directly from the `gas-fakes` source code, ensuring 100% parity with the local environment.
274
+
275
+ ## Delegation Anti-Patterns (CRITICAL)
276
+ - **NEVER Delegate Execution**: The main orchestrator agent MUST retain control of script generation and the execution tool (e.g., `mcp_gas-fakes-mcp_workspace_agent`).
277
+ - **Subagent Context Loss**: Subagents do **not** inherit the `gf_agent` knowledge base, parity rules, or active skills. If you tell a subagent to "execute these 5 tasks," it will generate standard Google Apps Script code that ignores `gas-fakes` specific workarounds (like avoiding `HorizontalRule` or `modifiedDate`), leading to widespread failures.
278
+ - **Strict Role Boundary**: Subagents are strictly for **isolated documentation retrieval** (the Service Agent Phase). The main agent writes and runs the code.
279
+
280
+ ## Safe Parallelism (Performance)
281
+ While delegating *orchestration* is dangerous, performing tasks in parallel is highly encouraged for efficiency. The orchestrator should achieve this in two ways:
282
+ 1. **Tool Call Parallelism**: The Main Agent generates multiple, parity-compliant scripts and issues them as independent, simultaneous calls to `mcp_gas-fakes-mcp_workspace_agent` within a single turn.
283
+ 2. **Execution-Level Async**: Since `gas-fakes` runs on Node.js, the Main Agent can generate a single script that uses `Promise.all()` to execute multiple non-dependent operations (like `UrlFetchApp` calls or creating separate files) simultaneously.
284
+ 3. **Sequence when Dependent**: Only use `wait_for_previous: true` or sequential turns when a task depends on the side-effect of a previous one (e.g., reading a sheet that was just created).
285
+
286
+
287
+ # Sandbox and Security Controls
288
+
289
+ `gf_agent` can operate in a **Sandbox Mode** to ensure that automated tasks are restricted to specific files, recipients, or usage quotas. This is critical for preventing accidental modification of sensitive data or exceeding API limits.
290
+
291
+ ## Core Concepts
292
+
293
+ ### 1. Enabling the Sandbox
294
+ The sandbox is controlled via `ScriptApp.__behavior`. Enabling it locks down the environment.
295
+ ```javascript
296
+ ScriptApp.__behavior.sandboxMode = true;
297
+ ScriptApp.__behavior.strictSandbox = true; // Only allow files created in this session
298
+ ```
299
+
300
+ ### 2. ID Whitelisting (Files & Folders)
301
+ In `strictSandbox` mode, all external files are blocked by default. Use whitelisting to grant granular access.
302
+ - **Pattern**: `addIdWhitelist(item)`
303
+ - **Permissions**: `.setRead(true)`, `.setWrite(true)`, `.setTrash(true)`
304
+ ```javascript
305
+ const behavior = ScriptApp.__behavior;
306
+ const item = behavior.newIdWhitelistItem('FILE_ID')
307
+ .setRead(true)
308
+ .setWrite(false) // Read-only
309
+ .setTrash(false);
310
+ behavior.addIdWhitelist(item);
311
+ ```
312
+
313
+ ### 3. Gmail Security
314
+ The Gmail service has specialized sandbox settings under `sandboxService.GmailApp`.
315
+ - **Email Whitelist**: Restricts `sendEmail` to specific addresses.
316
+ - **Label Whitelist**: Restricts which labels can be read, applied, or deleted.
317
+ - **Usage Limits**: Sets quotas for `read`, `write`, `trash`, and `send` operations.
318
+ ```javascript
319
+ const gmail = ScriptApp.__behavior.sandboxService.GmailApp;
320
+ gmail.emailWhitelist = ['allowed@example.com'];
321
+ gmail.usageLimit = { send: 5, read: 10 }; // Granular limits
322
+ // OR
323
+ gmail.usageLimit = 50; // Total operations limit
324
+ ```
325
+
326
+ ### 4. Service & Method Restrictions
327
+ You can disable entire services or restrict scripts to a subset of allowed methods.
328
+ ```javascript
329
+ // Disable a service entirely
330
+ ScriptApp.__behavior.sandboxService.SlidesApp.enabled = false;
331
+
332
+ // Whitelist specific methods for a service
333
+ ScriptApp.__behavior.sandboxService.DriveApp.setMethodWhitelist(['getFileById', 'getBlob']);
334
+ ```
335
+
336
+ ### 5. Automated Cleanup
337
+ The sandbox tracks every resource created during a session. When `behavior.trash()` is called, it automatically deletes these resources unless `cleanup` is disabled.
338
+ ```javascript
339
+ ScriptApp.__behavior.cleanup = true; // Default: true
340
+ // Set per-service
341
+ ScriptApp.__behavior.sandboxService.GmailApp.cleanup = false;
342
+ ```
343
+
344
+ ## Best Practices for `gf_agent`
345
+ - **Session Isolation**: When a user provides a list of files or emails, always initialize the sandbox with those specific whitelists at the top of the script.
346
+ - **Explicit Whitelisting**: Use `behavior.addIdWhitelist` for file access. DO NOT assume `sandboxService.SpreadsheetApp` has an `addFileWhitelist` method (it is handled globally by the behavior ID whitelist).
347
+ - **Safe Execution**: In the Orchestrator Phase, identify if the task requires external access and include the necessary sandbox boilerplate in the generated script.
348
+
349
+
350
+ ### Chart Builder Method Limitations (API Parity)
351
+
352
+ When generating code that builds Embedded Charts in Google Sheets (`SpreadsheetApp.newChart()`), you must adhere to the following restrictions, as `gas-fakes` maps directly to the Google Sheets REST API v4 which has limitations compared to Live Apps Script:
353
+
354
+ 1. **Method Fragmentation (Range Settings)**:
355
+ - You MUST NOT use `setXAxisRange()` or `setYAxisRange()` unless you are specifically building a `ScatterChart`. In Live Apps Script, these methods are exclusive to `EmbeddedScatterChartBuilder` and will throw a `TypeError` on other chart types.
356
+ - For all other chart types (Column, Bar, Line, Area), you MUST use the generic `setRange(min, max)` method.
357
+
358
+ 2. **Unimplemented Formatting Methods**:
359
+ - The REST API v4 lacks direct properties for many granular text-styling and sub-scale formatting options.
360
+ - You MUST NOT use the following methods as they will throw a `notYetImplemented` error in `gas-fakes`: `useLogScale()`, `setXAxisLogScale()`, `setYAxisLogScale()`, `reverseCategories()`, `reverseDirection()`, `setPointStyle()`, `enablePaging()`, `enableSorting()`, or any method ending in `*TextStyle()` (e.g., `setTitleTextStyle()`).
361
+ - Stick to core configurations: `setColors()`, `setXAxisTitle()`, `setYAxisTitle()`, `setRange()`, `setStacked()`, `setBackgroundColor()`, `setLegendPosition()`, and `set3D()`.
362
+
363
+ 3. **Pie Chart Custom Colors**:
364
+ - The REST API v4 does not support setting custom slice colors for Pie Charts. Calling `setColors()` on a Pie Chart builder will be silently ignored. Do not attempt to style Pie Chart slices.
365
+
366
+ ### Gmail Modifiers and Eventual Consistency
367
+
368
+ When writing scripts that modify Gmail objects (e.g., `GmailMessage.markRead()`, `GmailMessage.star()`, `GmailThread.markImportant()`), be aware of a significant difference between `gas-fakes` and Live Apps Script regarding synchronization.
369
+
370
+ - **Live Apps Script (Eventual Consistency)**: Despite documentation claiming these methods are synchronous and "force a refresh", the backend operations are eventually consistent. If you check the state immediately after modifying it (e.g., `message.markRead(); console.log(message.isUnread());`), it will likely return the old state.
371
+ - **Portable Code Pattern**: If you are writing tests or robust code that must run reliably in Live Apps Script as well as `gas-fakes`, you must introduce an artificial delay and manually refresh the object state:
372
+ ```javascript
373
+ message.markRead();
374
+ if (!ScriptApp.isFake) Utilities.sleep(1000); // Wait for Live GAS backend
375
+ message.refresh(); // Manually force a re-fetch of the state
376
+ console.log(message.isUnread()); // Now safe to assert
377
+ ```
378
+ - **gas-fakes Execution**: When executing transient scripts locally via `gas-fakes` that don't need immediate assertions, this pattern is not strictly necessary as `gas-fakes` handles the REST API synchronization reliably, but it is best practice for cross-platform parity.
379
+
380
+ # gf_agent Knowledge Base
381
+
382
+ This directory contains modular markdown files representing the "Lessons Learned & Best Practices" for the `gf_agent` skill.
383
+
384
+ ## Contributing
385
+
386
+ To prevent Git merge conflicts on the monolithic `gf_agent/SKILL.md` file, collaborators should **never edit `gf_agent/SKILL.md` directly**.
387
+
388
+ Instead, to add new knowledge or instructions to the agent:
389
+ 1. Create a new markdown file in this directory (e.g., `06-new-feature.md`).
390
+ 2. Prefix it with a number to control the insertion order.
391
+ 3. Write your instructions, tips, or parity warnings.
392
+ 4. Commit ONLY your new markdown file and submit a Pull Request.
393
+
394
+ **Do not attempt to compile the SKILL.md file yourself.**
395
+ When your Pull Request is merged into the core `gas-fakes` repository, the maintainer will run the overarching `npm run docs` pipeline. The `builder.js` script will automatically read all files in this directory, sort them, and cleanly generate the final `gf_agent/SKILL.md` artifact for all users.
396
+
@@ -0,0 +1,105 @@
1
+ # gf_agent Documentation
2
+
3
+ This document provides patterns and examples for using `gf_agent` and `gas-fakes` to automate Google Workspace tasks.
4
+
5
+ ## Integration Guides
6
+
7
+ ### Gemini CLI & MCP Setup
8
+ To integrate `gf_agent` and its Workspace tools into Gemini CLI:
9
+
10
+ 1. **Link the Skill**:
11
+ ```bash
12
+ gemini skills link /Users/brucemcpherson/Documents/repos/gas-fakes/gf_agent
13
+ ```
14
+ 2. **Add MCP Tools**:
15
+ Run this to register the service-specific tools (Spreadsheet, Doc, Drive, etc.):
16
+ ```bash
17
+ gemini mcp add --scope project gas-fakes-mcp node ./gas-fakes.js mcp
18
+ ```
19
+ 3. **Verify**: Run `gemini mcp list` to see the registered `gas-fakes-mcp` tools.
20
+ 4. **Use it**:
21
+ - **As an Agent**: `@gf_agent create a sheet...`
22
+ - **As Tools**: You don't need to specify the tool name. The LLM automatically infers whether to use `spreadsheet_service`, `drive_service`, or the cross-service `workspace_agent`. Just say: *"Read my latest Drive document and email it to john@example.com."*
23
+
24
+ ### Generic MCP Setup (Antigravity, Claude, etc.)
25
+ `gas-fakes` includes a built-in MCP server that can be used by any MCP-compatible client.
26
+
27
+ ## Core Patterns
28
+
29
+ ### Initialization
30
+ Every script must import `@mcpher/gas-fakes` to enable the local emulation environment.
31
+ ```javascript
32
+ import '@mcpher/gas-fakes';
33
+ ```
34
+
35
+ ### Google Sheets (SpreadsheetApp)
36
+ #### Create and modify a sheet
37
+ ```javascript
38
+ const ss = SpreadsheetApp.create('My New Sheet');
39
+ const sheet = ss.getActiveSheet();
40
+ sheet.getRange('A1').setValue('Item');
41
+ sheet.getRange('B1').setValue('Count');
42
+ sheet.getRange('A2:B2').setValues([['Apples', 10]]);
43
+ ```
44
+
45
+ #### Read data from a sheet
46
+ ```javascript
47
+ const ss = SpreadsheetApp.openById('SPREADSHEET_ID');
48
+ const data = ss.getDataRange().getValues();
49
+ console.log(data);
50
+ ```
51
+
52
+ ### Google Docs (DocumentApp)
53
+ #### Create and append text
54
+ ```javascript
55
+ const doc = DocumentApp.create('My Doc');
56
+ const body = doc.getBody();
57
+ body.appendParagraph('Hello World');
58
+ body.appendListItem('First Item');
59
+ body.appendTable([['Header 1', 'Header 2'], ['Val 1', 'Val 2']]);
60
+ ```
61
+
62
+ ### Google Drive (DriveApp)
63
+ #### List files in a folder
64
+ ```javascript
65
+ const folder = DriveApp.getFolderById('FOLDER_ID');
66
+ const files = folder.getFiles();
67
+ while (files.hasNext()) {
68
+ const file = files.next();
69
+ console.log(file.getName(), file.getId());
70
+ }
71
+ ```
72
+
73
+ #### Create a folder
74
+ ```javascript
75
+ const newFolder = DriveApp.createFolder('Project Assets');
76
+ ```
77
+
78
+ ### Gmail (GmailApp)
79
+ #### Send an email
80
+ ```javascript
81
+ GmailApp.sendEmail('recipient@example.com', 'Subject', 'Body content');
82
+ ```
83
+
84
+ ## Advanced Usage
85
+
86
+ ### Using the Sandbox
87
+ By default, `gas-fakes` operates in a sandbox. You can check if you are in fake mode and configure behavior:
88
+ ```javascript
89
+ if (ScriptApp.isFake) {
90
+ const behavior = ScriptApp.__behavior;
91
+ behavior.sandboxMode = true; // only see files created in this session
92
+ }
93
+ ```
94
+
95
+ ### Cleanup
96
+ If you create many test files, you can trash them at the end of the session:
97
+ ```javascript
98
+ if (ScriptApp.isFake) {
99
+ ScriptApp.__behavior.trash();
100
+ }
101
+ ```
102
+
103
+ ## Troubleshooting
104
+ - **Method not found**: Check the [Index](index.md) to see if the method is implemented.
105
+ - **Permission denied**: Ensure you are using the correct IDs or that the sandbox is configured to allow access to those IDs.