Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- Docs: add `docs add-tab`, `docs rename-tab`, and `docs delete-tab` for managing Google Docs tabs. (#547) — thanks @chopenhauer.
- Docs: support tab-scoped Markdown append and find-replace flows. (#541) — thanks @donbowman.
- Sheets: add `sheets table append` for appending rows to structured Sheets tables without targeting headers directly.
- Sheets: add header-safe `sheets table clear` for clearing table data rows without touching headers or footers.

### Fixed
- Agent safety: compile baked safety profile policies into generated hash switches so raw allow/deny rule strings are not embedded as patchable YAML. (#540) — thanks @drewburchfield.
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1392,6 +1392,7 @@ gog sheets named-ranges delete <spreadsheetId> MyNamedRange2
gog sheets table list <spreadsheetId>
gog sheets table create <spreadsheetId> 'Sheet1!A1:C4' --name Tasks --columns-json '[{"columnName":"Task","columnType":"TEXT"},{"columnName":"Amount","columnType":"DOUBLE"},{"columnName":"Done","columnType":"BOOLEAN"}]'
gog sheets table append <spreadsheetId> <tableId> --values-json '[["Write docs",2,true]]'
gog sheets table clear <spreadsheetId> <tableId> --force
gog sheets table get <spreadsheetId> <tableId>
gog sheets table delete <spreadsheetId> <tableId> --force
# See docs/sheets-tables.md for valid column types and current command scope.
Expand Down
1 change: 1 addition & 0 deletions docs/commands.generated.md
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ Generated from `gog schema --json`.
- [`gog sheets (sheet) resize-rows <spreadsheetId> <rows> [flags]`](commands/gog-sheets-resize-rows.md) - Resize sheet rows
- [`gog sheets (sheet) table (tables) <command>`](commands/gog-sheets-table.md) - Manage Google Sheets tables
- [`gog sheets (sheet) table (tables) append (add-row,add-rows) <spreadsheetId> <tableId> [<values> ...] [flags]`](commands/gog-sheets-table-append.md) - Append rows to a table
- [`gog sheets (sheet) table (tables) clear (clear-rows) <spreadsheetId> <tableId>`](commands/gog-sheets-table-clear.md) - Clear table data rows
- [`gog sheets (sheet) table (tables) create (add,new) --name=STRING --columns-json=STRING <spreadsheetId> <range>`](commands/gog-sheets-table-create.md) - Create a table
- [`gog sheets (sheet) table (tables) delete (rm,remove,del) <spreadsheetId> <tableId>`](commands/gog-sheets-table-delete.md) - Delete a table
- [`gog sheets (sheet) table (tables) get (show,info) <spreadsheetId> <tableId>`](commands/gog-sheets-table-get.md) - Get a table
Expand Down
3 changes: 2 additions & 1 deletion docs/commands/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Every `gog` command has a generated docs page. The source of truth is the live CLI schema; run `make docs-commands` after changing command names, flags, help text, aliases, or arguments.

Generated pages: 457.
Generated pages: 458.

## Top-level Commands

Expand Down Expand Up @@ -459,6 +459,7 @@ Generated pages: 457.
- [gog sheets resize-rows](gog-sheets-resize-rows.md) - Resize sheet rows
- [gog sheets table](gog-sheets-table.md) - Manage Google Sheets tables
- [gog sheets table append](gog-sheets-table-append.md) - Append rows to a table
- [gog sheets table clear](gog-sheets-table-clear.md) - Clear table data rows
- [gog sheets table create](gog-sheets-table-create.md) - Create a table
- [gog sheets table delete](gog-sheets-table-delete.md) - Delete a table
- [gog sheets table get](gog-sheets-table-get.md) - Get a table
Expand Down
42 changes: 42 additions & 0 deletions docs/commands/gog-sheets-table-clear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# `gog sheets table clear`

> Generated from `gog schema --json`. Do not edit this page by hand; run `make docs-commands`.

Clear table data rows

## Usage

```bash
gog sheets (sheet) table (tables) clear (clear-rows) <spreadsheetId> <tableId>
```

## Parent

- [gog sheets table](gog-sheets-table.md)

## Flags

| Flag | Type | Default | Help |
| --- | --- | --- | --- |
| `--access-token` | `string` | | Use provided access token directly (bypasses stored refresh tokens; token expires in ~1h) |
| `-a`<br>`--account`<br>`--acct` | `string` | | Account email for API commands (gmail/calendar/chat/classroom/drive/docs/slides/contacts/tasks/people/sheets/forms/appscript/ads) |
| `--client` | `string` | | OAuth client name (selects stored credentials + token bucket) |
| `--color` | `string` | auto | Color output: auto\|always\|never |
| `--disable-commands` | `string` | | Comma-separated list of disabled commands; dot paths allowed |
| `-n`<br>`--dry-run`<br>`--dryrun`<br>`--noop`<br>`--preview` | `bool` | | Do not make changes; print intended actions and exit successfully |
| `--enable-commands` | `string` | | Comma-separated list of enabled commands; dot paths allowed (restricts CLI) |
| `-y`<br>`--force`<br>`--assume-yes`<br>`--yes` | `bool` | | Skip confirmations for destructive commands |
| `--gmail-no-send` | `bool` | false | Block Gmail send operations (agent safety) |
| `-h`<br>`--help` | `kong.helpFlag` | | Show context-sensitive help. |
| `-j`<br>`--json`<br>`--machine` | `bool` | false | Output JSON to stdout (best for scripting) |
| `--no-input`<br>`--non-interactive`<br>`--noninteractive` | `bool` | | Never prompt; fail instead (useful for CI) |
| `-p`<br>`--plain`<br>`--tsv` | `bool` | false | Output stable, parseable text to stdout (TSV; no colors) |
| `--results-only` | `bool` | | In JSON mode, emit only the primary result (drops envelope fields like nextPageToken) |
| `--select`<br>`--pick`<br>`--project` | `string` | | In JSON mode, select comma-separated fields (best-effort; supports dot paths). Desire path: use --fields for most commands. |
| `-v`<br>`--verbose` | `bool` | | Enable verbose logging |
| `--version` | `kong.VersionFlag` | | Print version and exit |

## See Also

- [gog sheets table](gog-sheets-table.md)
- [Command index](README.md)
1 change: 1 addition & 0 deletions docs/commands/gog-sheets-table.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ gog sheets (sheet) table (tables) <command>
## Subcommands

- [gog sheets table append](gog-sheets-table-append.md) - Append rows to a table
- [gog sheets table clear](gog-sheets-table-clear.md) - Clear table data rows
- [gog sheets table create](gog-sheets-table-create.md) - Create a table
- [gog sheets table delete](gog-sheets-table-delete.md) - Delete a table
- [gog sheets table get](gog-sheets-table-get.md) - Get a table
Expand Down
28 changes: 24 additions & 4 deletions docs/sheets-tables.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,25 @@ new rows after the current table data and expand the table, without targeting
the header row directly. Rows wider than the table's column count are rejected
before the mutation is sent.

## Clear Data Rows

Clear table data by table ID or name:

```bash
gog sheets table clear "$spreadsheet_id" "$table_id" --force
```

This clears only the table data body. It never includes the header row in the
clear range. If Sheets reports a footer row, `gog` skips the footer row too and
clears only the rows between header and footer.

Header-only tables fail with a clear message instead of sending an empty or
header-touching mutation. Use `--dry-run --json` to preview the exact data range:

```bash
gog sheets table clear "$spreadsheet_id" Tasks --dry-run --json
```

## Delete A Table

Deleting removes the table object. Use `--force` for non-interactive runs:
Expand All @@ -117,10 +136,10 @@ gog sheets table delete "$spreadsheet_id" "$table_id" --dry-run --json

## Current Scope

This table command set intentionally covers list, get, create, append, and
delete. Table update, footer editing, and table-aware clear behavior need
separate semantics because the plain Sheets range APIs can touch table headers
or footer rows if used blindly.
This table command set intentionally covers list, get, create, append, clear
data rows, and delete. Table update and footer editing need separate semantics
because the plain Sheets range APIs can touch table headers or footer rows if
used blindly.

## Command Pages

Expand All @@ -129,4 +148,5 @@ or footer rows if used blindly.
- [`gog sheets table get`](commands/gog-sheets-table-get.md)
- [`gog sheets table create`](commands/gog-sheets-table-create.md)
- [`gog sheets table append`](commands/gog-sheets-table-append.md)
- [`gog sheets table clear`](commands/gog-sheets-table-clear.md)
- [`gog sheets table delete`](commands/gog-sheets-table-delete.md)
9 changes: 8 additions & 1 deletion internal/cmd/sheets_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type SheetsTableCmd struct {
Get SheetsTableGetCmd `cmd:"" name:"get" aliases:"show,info" help:"Get a table"`
Create SheetsTableCreateCmd `cmd:"" name:"create" aliases:"add,new" help:"Create a table"`
Append SheetsTableAppendCmd `cmd:"" name:"append" aliases:"add-row,add-rows" help:"Append rows to a table"`
Clear SheetsTableClearCmd `cmd:"" name:"clear" aliases:"clear-rows" help:"Clear table data rows"`
Delete SheetsTableDeleteCmd `cmd:"" name:"delete" aliases:"rm,remove,del" help:"Delete a table"`
}

Expand Down Expand Up @@ -298,7 +299,9 @@ type sheetsTableItem struct {
SheetID int64 `json:"sheetId"`
SheetTitle string `json:"sheetTitle"`
A1 string `json:"a1"`
DataA1 string `json:"dataA1,omitempty"`
Range *sheets.GridRange `json:"range,omitempty"`
HasFooter bool `json:"hasFooter,omitempty"`
Columns []sheetsTableColumnItem `json:"columns,omitempty"`
}

Expand All @@ -310,7 +313,7 @@ type sheetsTableColumnItem struct {

func fetchSpreadsheetTables(ctx context.Context, svc *sheets.Service, spreadsheetID string) ([]sheetsTableItem, error) {
call := svc.Spreadsheets.Get(spreadsheetID).
Fields("sheets(properties(sheetId,title),tables(tableId,name,range,columnProperties(columnIndex,columnName,columnType,dataValidationRule)))")
Fields("sheets(properties(sheetId,title),tables(tableId,name,range,rowsProperties(footerColorStyle),columnProperties(columnIndex,columnName,columnType,dataValidationRule)))")
if ctx != nil {
call = call.Context(ctx)
}
Expand Down Expand Up @@ -358,8 +361,12 @@ func sheetsTableToItem(table *sheets.Table, catalog *spreadsheetRangeCatalog) sh
}
if item.SheetTitle != "" {
item.A1 = gridRangeToA1(item.SheetTitle, table.Range)
if dataA1, ok := sheetsTableDataRangeA1(item.SheetTitle, table); ok {
item.DataA1 = dataA1
}
}
}
item.HasFooter = sheetsTableHasFooter(table)
for _, col := range table.ColumnProperties {
if col == nil {
continue
Expand Down
109 changes: 109 additions & 0 deletions internal/cmd/sheets_table_clear.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package cmd

import (
"context"
"fmt"
"os"
"strings"

"google.golang.org/api/sheets/v4"

"github.com/steipete/gogcli/internal/outfmt"
"github.com/steipete/gogcli/internal/ui"
)

type SheetsTableClearCmd struct {
SpreadsheetID string `arg:"" name:"spreadsheetId" help:"Spreadsheet ID"`
TableID string `arg:"" name:"tableId" help:"Table ID or table name"`
}

func (c *SheetsTableClearCmd) Run(ctx context.Context, flags *RootFlags) error {
u := ui.FromContext(ctx)
account, err := requireAccount(flags)
if err != nil {
return err
}

spreadsheetID := normalizeGoogleID(strings.TrimSpace(c.SpreadsheetID))
in := strings.TrimSpace(c.TableID)
if spreadsheetID == "" {
return usage("empty spreadsheetId")
}
if in == "" {
return usage("empty tableId")
}

svc, err := newSheetsService(ctx, account)
if err != nil {
return err
}

tables, err := fetchSpreadsheetTables(ctx, svc, spreadsheetID)
if err != nil {
return err
}
table, found, err := resolveSheetsTable(in, tables)
if err != nil {
return err
}
if !found {
return usagef("unknown table %q", in)
}
dataRange := strings.TrimSpace(table.DataA1)
if dataRange == "" {
return fmt.Errorf("table %q has no data rows to clear", table.TableID)
}

if dryRunErr := dryRunExit(ctx, flags, "sheets.table.clear", map[string]any{
"spreadsheet_id": spreadsheetID,
"table_id": table.TableID,
"name": table.Name,
"data_range": dataRange,
"has_footer": table.HasFooter,
}); dryRunErr != nil {
return dryRunErr
}
if flags == nil || !flags.Force {
return usage("sheets table clear requires --force")
}
if confirmErr := confirmDestructiveChecked(ctx, flagsWithoutDryRun(flags), "clear data rows in table "+table.Name); confirmErr != nil {
return confirmErr
}

resp, err := svc.Spreadsheets.Values.Clear(spreadsheetID, dataRange, &sheets.ClearValuesRequest{}).Do()
if err != nil {
return err
}

if outfmt.IsJSON(ctx) {
return outfmt.WriteJSON(ctx, os.Stdout, map[string]any{
"tableId": table.TableID,
"name": table.Name,
"tableRange": table.A1,
"clearedRange": resp.ClearedRange,
"hasFooter": table.HasFooter,
})
}

u.Out().Printf("Cleared data rows in %s", resp.ClearedRange)
return nil
}

func sheetsTableHasFooter(table *sheets.Table) bool {
return table != nil && table.RowsProperties != nil && table.RowsProperties.FooterColorStyle != nil
}

func sheetsTableDataRangeA1(sheetTitle string, table *sheets.Table) (string, bool) {
if table == nil || table.Range == nil {
return "", false
}
dataRange := *table.Range
dataRange.StartRowIndex++
if sheetsTableHasFooter(table) {
dataRange.EndRowIndex--
}
if dataRange.EndRowIndex <= dataRange.StartRowIndex {
return "", false
}
return gridRangeToA1(sheetTitle, &dataRange), true
}
Loading
Loading