Skip to main content
ClaudeWave
Skill341 repo starsupdated today

google-forms

This Claude Code skill wraps Google Forms via the gws command-line tool, enabling creation of forms, addition of questions, modification of publish settings, and retrieval of submitted responses. Use it when you need to programmatically build surveys or questionnaires, manage form configuration, or aggregate and analyze form responses through the Google Forms API.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/Lilac-Labs/gini-agent /tmp/google-forms && cp -r /tmp/google-forms/skills/google/google-forms ~/.claude/skills/google-forms
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

# Google Forms

Use `gws forms` to create new forms, add questions to them, change publish settings, and read submitted responses. Wraps the Forms v1 API.

## Prerequisites

- `gws` installed and authenticated. If `gws` is not on PATH OR `gws auth status` reports no authenticated user, do NOT silently call setup. Instead, in a single short reply to the user:
  1. State plainly what's missing — e.g. "Google Workspace access isn't set up on this machine yet" or "your Google sign-in has expired."
  2. Ask one sentence: "Want me to walk you through setting it up?" Wait for the user's answer.
  3. If they say yes, call `read_skill` with name `google-workspace-setup` and run that skill's onboarding flow turn-by-turn. If they say no or ask to defer, acknowledge briefly and stop — do not retry the original request.
- Apply the same flow when any `gws forms ...` call fails mid-task with `command not found` / ENOENT, HTTP 401, "no credentials", or "scope required". Don't report the failure as a dead end — surface the missing prerequisite and ask if the user wants to set it up before moving on.
- OAuth scopes the user picked at login must cover the verbs the agent will use:
  - Create / edit forms (read + write structure): `forms.body`
  - Read form structure only (e.g. mapping `questionId → title` for summaries): `forms.body.readonly`
  - Read submitted responses: `forms.responses.readonly`
  - Summarize responses by question text: `forms.responses.readonly` AND `forms.body.readonly` (or `forms.body`). `forms.responses.readonly` is NOT on `forms.get`'s authorized scope list — you need a body scope to fetch the structure separately.

## Selecting a Google account

The connected Google accounts (each with its tag, email, and config dir) are listed in your system context under **"Connected Google accounts"**. To target a specific account, prefix the command with its config dir:

```bash
GOOGLE_WORKSPACE_CLI_CONFIG_DIR="<configDir>" gws forms forms create --json '{"info":{"title":"Survey"}}'
```

Selection rule: one account connected → just use it. Two or more:

- The user named or clearly implied one account (a tag, an email, or unambiguous context) → use only that account.
- A read/lookup/search the user didn't tie to an account (e.g. listing events, searching mail, finding a doc) → run it against **every** connected account (one `gws` call per config dir) and aggregate, labeling each result by its tag and email. Don't pick just one, and don't ask — the user wants the whole picture across accounts.
- A write (send, create, edit, delete) with no account named → ASK which account first; never guess.

If no accounts are connected yet, fall back to the setup flow in Prerequisites (`read_skill` with `google-workspace-setup`).

## When to Use

- The user asks Gini to create a Google Form (survey, signup, feedback, RSVP).
- Reading responses to an existing form and summarizing them.
- Updating publish settings on an existing form (accepting responses on/off, etc.).
- Watching for new responses on an existing form.

## When NOT to Use

- Structured data the user maintains themselves — use Sheets (`gws sheets ...`), not Forms.
- Free-form notes — use `apple-notes`, `obsidian`, or `google-docs` instead.
- Agent-internal state — use the `memory` tool.
- Long, branching surveys with complex logic — Forms supports basic section navigation but real survey tooling (Typeform, Qualtrics) is more appropriate.

## Quick Reference

The Forms surface is small: `forms.create`, `forms.get`, `forms.batchUpdate`, `forms.setPublishSettings`, and the `forms.responses` subresource for reading submissions.

### Create a form (two-step)

`forms.create` only honors `info.title` and `info.documentTitle`. Body items must be added with a follow-up `batchUpdate`. This is by design — Google rejects body content in the create call.

```bash
# Step 1: create the empty form
gws forms forms create --json '{
  "info": {
    "title": "Team feedback",
    "documentTitle": "Team feedback (internal)"
  }
}'

# Capture the response's "formId" — you'll need it for step 2.

# Step 2: add items (questions) with batchUpdate
gws forms forms batchUpdate \
  --params '{"formId":"<FORM_ID>"}' \
  --json '{
    "requests": [
      {
        "createItem": {
          "item": {
            "title": "How are things going?",
            "questionItem": {
              "question": {
                "required": true,
                "textQuestion": {"paragraph": true}
              }
            }
          },
          "location": {"index": 0}
        }
      },
      {
        "createItem": {
          "item": {
            "title": "How satisfied are you?",
            "questionItem": {
              "question": {
                "required": true,
                "scaleQuestion": {
                  "low": 1, "high": 5,
                  "lowLabel": "Not at all", "highLabel": "Extremely"
                }
              }
            }
          },
          "location": {"index": 1}
        }
      }
    ]
  }'
```

### Read a form's structure

```bash
gws forms forms get --params '{"formId":"<FORM_ID>"}'
```

The response contains the title, items, and the `responderUri` (the public URL to share with respondents).

### Read responses

```bash
# All responses on a form
gws forms forms responses list --params '{"formId":"<FORM_ID>"}' --page-all

# A specific response
gws forms forms responses get \
  --params '{"formId":"<FORM_ID>","responseId":"<RESPONSE_ID>"}'

# Filter by submission time (server-side filter)
gws forms forms responses list \
  --params '{"formId":"<FORM_ID>","filter":"timestamp > 2026-06-01T00:00:00Z"}' \
  --page-all
```

Each response has an `answers` map keyed by `questionId`. Cross-reference against `forms.get` to map question IDs back to titles when summarizing. `forms.get` reads form structure and requires `forms.body.readonly` (or `forms.body`) — `forms.responses.readonly` alone is not on its authorized scope list, so summary workf