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 @@ -7,6 +7,7 @@
- Docs: add `VISION.md` with project fit, discussion, and live-test merge guidance.
- Calendar: add --with-zoom / --regenerate-zoom / --remove-zoom that create, regenerate, and remove Zoom meetings and attach the join URL + meeting ID + passcode to the Calendar event description. Google's Calendar API rejects conferenceData writes asserting `conferenceSolution.key.type="addOn"` from non-Workspace-Marketplace OAuth clients, so the description-mode integration is the path that round-trips through Google's storage; trade-off is no native "Join with Zoom" conference card. (#589, #590) — thanks @alexisperumal and @mvanhorn.
- Auth: add gog zoom auth setup / doctor for Zoom S2S OAuth credential storage. (#590) — thanks @mvanhorn.
- Docs: add `gog docs insert-page-break <docId> [--index N | --at-end] [--tab=STRING]` to insert a Google Docs page break directly via `InsertPageBreakRequest` — markdown has no native page-break construct, so this is the only path for multi-page deliverables. Aliases: `page-break`, `pb`. (#604)

### Fixed

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ Docs: [Google Docs editing](docs/docs-editing.md),
```bash
gog docs write <docId> --append --markdown --text '## Status'
gog docs format <docId> --match Status --bold --font-size 18
gog docs insert-page-break <docId> --at-end
gog docs add-tab <docId> --title "Notes"
gog docs tabs add <docId> --title "Notes"
gog docs find-replace <docId> old new --tab "Notes" --dry-run
Expand Down
1 change: 1 addition & 0 deletions docs/commands.generated.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ Generated from `gog schema --json`.
- [`gog docs (doc) format <docId> [flags]`](commands/gog-docs-format.md) - Apply text or paragraph formatting to a Google Doc
- [`gog docs (doc) info (get,show) <docId>`](commands/gog-docs-info.md) - Get Google Doc metadata
- [`gog docs (doc) insert <docId> [<content>] [flags]`](commands/gog-docs-insert.md) - Insert text at a specific position
- [`gog docs (doc) insert-page-break (page-break,pb) <docId> [flags]`](commands/gog-docs-insert-page-break.md) - Insert a page break at a specific position (or end-of-doc with --at-end)
- [`gog docs (doc) list-tabs <docId>`](commands/gog-docs-list-tabs.md) - List all tabs in a Google Doc
- [`gog docs (doc) raw <docId> [flags]`](commands/gog-docs-raw.md) - Dump raw Google Docs API response as JSON (Documents.Get; lossless; for scripting and LLM consumption)
- [`gog docs (doc) rename-tab <docId> [flags]`](commands/gog-docs-rename-tab.md) - Rename a tab in a Google Doc
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: 564.
Generated pages: 565.

## Top-level Commands

Expand Down Expand Up @@ -289,6 +289,7 @@ Generated pages: 564.
- [gog docs format](gog-docs-format.md) - Apply text or paragraph formatting to a Google Doc
- [gog docs info](gog-docs-info.md) - Get Google Doc metadata
- [gog docs insert](gog-docs-insert.md) - Insert text at a specific position
- [gog docs insert-page-break](gog-docs-insert-page-break.md) - Insert a page break at a specific position (or end-of-doc with --at-end)
- [gog docs list-tabs](gog-docs-list-tabs.md) - List all tabs in a Google Doc
- [gog docs raw](gog-docs-raw.md) - Dump raw Google Docs API response as JSON (Documents.Get; lossless; for scripting and LLM consumption)
- [gog docs rename-tab](gog-docs-rename-tab.md) - Rename a tab in a Google Doc
Expand Down
46 changes: 46 additions & 0 deletions docs/commands/gog-docs-insert-page-break.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# `gog docs insert-page-break`

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

Insert a page break at a specific position (or end-of-doc with --at-end)

## Usage

```bash
gog docs (doc) insert-page-break (page-break,pb) <docId> [flags]
```

## Parent

- [gog docs](gog-docs.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/drivelabels/docs/slides/contacts/tasks/people/sheets/forms/sites/appscript/analytics/searchconsole/ads/photos) |
| `--at-end` | `bool` | | Insert at end-of-doc/tab (mutually exclusive with --index) |
| `--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. |
| `--index` | `*int64` | | Character index to insert at (1 = beginning). Omit or use --at-end for end-of-doc. |
| `-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. |
| `--tab` | `string` | | Target a specific tab by title or ID (see docs list-tabs) |
| `-v`<br>`--verbose` | `bool` | | Enable verbose logging |
| `--version` | `kong.VersionFlag` | | Print version and exit |
| `--wrap-untrusted` | `bool` | false | In JSON/raw output, wrap fetched text fields in external untrusted-content markers |

## See Also

- [gog docs](gog-docs.md)
- [Command index](README.md)
1 change: 1 addition & 0 deletions docs/commands/gog-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ gog docs (doc) <command> [flags]
- [gog docs format](gog-docs-format.md) - Apply text or paragraph formatting to a Google Doc
- [gog docs info](gog-docs-info.md) - Get Google Doc metadata
- [gog docs insert](gog-docs-insert.md) - Insert text at a specific position
- [gog docs insert-page-break](gog-docs-insert-page-break.md) - Insert a page break at a specific position (or end-of-doc with --at-end)
- [gog docs list-tabs](gog-docs-list-tabs.md) - List all tabs in a Google Doc
- [gog docs raw](gog-docs-raw.md) - Dump raw Google Docs API response as JSON (Documents.Get; lossless; for scripting and LLM consumption)
- [gog docs rename-tab](gog-docs-rename-tab.md) - Rename a tab in a Google Doc
Expand Down
18 changes: 18 additions & 0 deletions docs/docs-editing.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,24 @@ Command page:

- [`gog docs format`](commands/gog-docs-format.md)

## Page Breaks

Markdown has no native page-break construct, so multi-page deliverables need a
direct Docs API call. Insert a page break at a specific index or append one at
end-of-doc:

```bash
gog docs insert-page-break <docId> --at-end
gog docs insert-page-break <docId> --index 250 --tab "Notes"
```

`--index` and `--at-end` are mutually exclusive; omit both to default to
end-of-doc. Aliases: `page-break`, `pb`.

Command page:

- [`gog docs insert-page-break`](commands/gog-docs-insert-page-break.md)

## Tabs

Manage Google Docs tabs:
Expand Down
45 changes: 23 additions & 22 deletions internal/cmd/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,29 @@ import (
var newDocsService = googleapi.NewDocs

type DocsCmd struct {
Export DocsExportCmd `cmd:"" name:"export" aliases:"download,dl" help:"Export a Google Doc (pdf|docx|txt|md|html)"`
Info DocsInfoCmd `cmd:"" name:"info" aliases:"get,show" help:"Get Google Doc metadata"`
Create DocsCreateCmd `cmd:"" name:"create" aliases:"add,new" help:"Create a Google Doc"`
Copy DocsCopyCmd `cmd:"" name:"copy" aliases:"cp,duplicate" help:"Copy a Google Doc"`
Cat DocsCatCmd `cmd:"" name:"cat" aliases:"text,read" help:"Print a Google Doc as plain text"`
Comments DocsCommentsCmd `cmd:"" name:"comments" help:"Manage comments on files"`
Tabs DocsTabsCmd `cmd:"" name:"tabs" help:"Manage Google Doc tabs"`
AddTab DocsAddTabCmd `cmd:"" name:"add-tab" help:"Add a tab to a Google Doc"`
RenameTab DocsRenameTabCmd `cmd:"" name:"rename-tab" help:"Rename a tab in a Google Doc"`
DeleteTab DocsDeleteTabCmd `cmd:"" name:"delete-tab" help:"Delete a tab from a Google Doc"`
ListTabs DocsListTabsCmd `cmd:"" name:"list-tabs" help:"List all tabs in a Google Doc"`
Write DocsWriteCmd `cmd:"" name:"write" help:"Write content to a Google Doc"`
Insert DocsInsertCmd `cmd:"" name:"insert" help:"Insert text at a specific position"`
Delete DocsDeleteCmd `cmd:"" name:"delete" help:"Delete text range from document"`
FindReplace DocsFindReplaceCmd `cmd:"" name:"find-replace" help:"Find and replace text. Supports plain text or markdown with images; use --first for a single occurrence."`
Update DocsUpdateCmd `cmd:"" name:"update" help:"Insert text at a specific index in a Google Doc"`
Edit DocsEditCmd `cmd:"" name:"edit" help:"Find and replace text in a Google Doc"`
Format DocsFormatCmd `cmd:"" name:"format" help:"Apply text or paragraph formatting to a Google Doc"`
Sed DocsSedCmd `cmd:"" name:"sed" help:"Regex find/replace (sed-style: s/pattern/replacement/g)"`
Clear DocsClearCmd `cmd:"" name:"clear" help:"Clear all content from a Google Doc"`
Structure DocsStructureCmd `cmd:"" name:"structure" aliases:"struct" help:"Show document structure with numbered paragraphs"`
Raw DocsRawCmd `cmd:"" name:"raw" help:"Dump raw Google Docs API response as JSON (Documents.Get; lossless; for scripting and LLM consumption)"`
Export DocsExportCmd `cmd:"" name:"export" aliases:"download,dl" help:"Export a Google Doc (pdf|docx|txt|md|html)"`
Info DocsInfoCmd `cmd:"" name:"info" aliases:"get,show" help:"Get Google Doc metadata"`
Create DocsCreateCmd `cmd:"" name:"create" aliases:"add,new" help:"Create a Google Doc"`
Copy DocsCopyCmd `cmd:"" name:"copy" aliases:"cp,duplicate" help:"Copy a Google Doc"`
Cat DocsCatCmd `cmd:"" name:"cat" aliases:"text,read" help:"Print a Google Doc as plain text"`
Comments DocsCommentsCmd `cmd:"" name:"comments" help:"Manage comments on files"`
Tabs DocsTabsCmd `cmd:"" name:"tabs" help:"Manage Google Doc tabs"`
AddTab DocsAddTabCmd `cmd:"" name:"add-tab" help:"Add a tab to a Google Doc"`
RenameTab DocsRenameTabCmd `cmd:"" name:"rename-tab" help:"Rename a tab in a Google Doc"`
DeleteTab DocsDeleteTabCmd `cmd:"" name:"delete-tab" help:"Delete a tab from a Google Doc"`
ListTabs DocsListTabsCmd `cmd:"" name:"list-tabs" help:"List all tabs in a Google Doc"`
Write DocsWriteCmd `cmd:"" name:"write" help:"Write content to a Google Doc"`
Insert DocsInsertCmd `cmd:"" name:"insert" help:"Insert text at a specific position"`
InsertPageBreak DocsInsertPageBreakCmd `cmd:"" name:"insert-page-break" aliases:"page-break,pb" help:"Insert a page break at a specific position (or end-of-doc with --at-end)"`
Delete DocsDeleteCmd `cmd:"" name:"delete" help:"Delete text range from document"`
FindReplace DocsFindReplaceCmd `cmd:"" name:"find-replace" help:"Find and replace text. Supports plain text or markdown with images; use --first for a single occurrence."`
Update DocsUpdateCmd `cmd:"" name:"update" help:"Insert text at a specific index in a Google Doc"`
Edit DocsEditCmd `cmd:"" name:"edit" help:"Find and replace text in a Google Doc"`
Format DocsFormatCmd `cmd:"" name:"format" help:"Apply text or paragraph formatting to a Google Doc"`
Sed DocsSedCmd `cmd:"" name:"sed" help:"Regex find/replace (sed-style: s/pattern/replacement/g)"`
Clear DocsClearCmd `cmd:"" name:"clear" help:"Clear all content from a Google Doc"`
Structure DocsStructureCmd `cmd:"" name:"structure" aliases:"struct" help:"Show document structure with numbered paragraphs"`
Raw DocsRawCmd `cmd:"" name:"raw" help:"Dump raw Google Docs API response as JSON (Documents.Get; lossless; for scripting and LLM consumption)"`
}

type DocsTabsCmd struct {
Expand Down
117 changes: 117 additions & 0 deletions internal/cmd/docs_insert_page_break.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package cmd

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

"google.golang.org/api/docs/v1"

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

// DocsInsertPageBreakCmd inserts a page break at a specific character index in
// a Google Doc (or at the end of the body/tab when --at-end is supplied, or
// --index is omitted). Surfaces the Docs API InsertPageBreakRequest directly,
// since markdown has no native page-break construct that the markdown writer
// could translate.
type DocsInsertPageBreakCmd struct {
DocID string `arg:"" name:"docId" help:"Doc ID"`
Index *int64 `name:"index" help:"Character index to insert at (1 = beginning). Omit or use --at-end for end-of-doc."`
AtEnd bool `name:"at-end" help:"Insert at end-of-doc/tab (mutually exclusive with --index)"`
Tab string `name:"tab" help:"Target a specific tab by title or ID (see docs list-tabs)"`
TabID string `name:"tab-id" hidden:"" help:"(deprecated) Use --tab"`
}

func (c *DocsInsertPageBreakCmd) Run(ctx context.Context, flags *RootFlags) error {
u := ui.FromContext(ctx)
docID := strings.TrimSpace(c.DocID)
if docID == "" {
return usage("empty docId")
}
if c.AtEnd && c.Index != nil {
return usage("--at-end and --index are mutually exclusive")
}
if c.Index != nil && *c.Index < 1 {
return usage("--index must be >= 1 (index 0 is reserved)")
}

tab, tabErr := resolveTabArg(ctx, c.Tab, c.TabID)
if tabErr != nil {
return tabErr
}
c.Tab = tab

resolveEnd := c.AtEnd || c.Index == nil

dryRunPayload := map[string]any{
"documentId": docID,
"tab": c.Tab,
}
if resolveEnd {
dryRunPayload["atIndex"] = "end"
} else {
dryRunPayload["atIndex"] = *c.Index
}
if dryRunErr := dryRunExit(ctx, flags, "docs.insert-page-break", dryRunPayload); dryRunErr != nil {
return dryRunErr
}

svc, err := requireDocsService(ctx, flags)
if err != nil {
return err
}

var insertIndex int64
if resolveEnd {
endIndex, tabID, endErr := docsTargetEndIndexAndTabID(ctx, svc, docID, c.Tab)
if endErr != nil {
return endErr
}
c.Tab = tabID
insertIndex = docsAppendIndex(endIndex)
} else {
insertIndex = *c.Index
if c.Tab != "" {
tabID, tabErr := resolveDocsTabID(ctx, svc, docID, c.Tab)
if tabErr != nil {
return tabErr
}
c.Tab = tabID
}
}

result, err := svc.Documents.BatchUpdate(docID, &docs.BatchUpdateDocumentRequest{
Requests: []*docs.Request{{
InsertPageBreak: &docs.InsertPageBreakRequest{
Location: &docs.Location{
Index: insertIndex,
TabId: c.Tab,
},
},
}},
}).Context(ctx).Do()
if err != nil {
return fmt.Errorf("inserting page break: %w", err)
}

if outfmt.IsJSON(ctx) {
payload := map[string]any{
"documentId": result.DocumentId,
"atIndex": insertIndex,
}
if c.Tab != "" {
payload["tabId"] = c.Tab
}
return outfmt.WriteJSON(ctx, os.Stdout, payload)
}

u.Out().Linef("documentId\t%s", result.DocumentId)
u.Out().Linef("atIndex\t%d", insertIndex)
if c.Tab != "" {
u.Out().Linef("tabId\t%s", c.Tab)
}
return nil
}
Loading
Loading