Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
d079a72
fix(cloud): harden run snapshots and request deadlines
jamesbhobbs Aug 5, 2026
b337a4c
fix(cloud): address snapshot review feedback
jamesbhobbs Aug 5, 2026
eee22bc
style: format README table after rebase conflict resolution
jamesbhobbs Aug 13, 2026
5f877b5
feat(cli): configure detached cloud-run storage
jamesbhobbs Aug 5, 2026
16e560a
fix(cli): complete storage-mode values in Bash
jamesbhobbs Aug 5, 2026
08a1db2
fix(cli): narrow JSON.parse result to CloudRunResult in tests
jamesbhobbs Aug 13, 2026
40b3273
feat(cloud): add block-level API client and push sync engine
jamesbhobbs Aug 13, 2026
f08dc90
Merge branch 'main' into fix/cloud-run-reliability
jamesbhobbs Aug 13, 2026
02ceeb5
fix(cli): narrow optional snapshotPath before fs.readFile in test
jamesbhobbs Aug 13, 2026
03f7c5b
test(cli): add Zsh and Fish assertions for --storage-mode completions
jamesbhobbs Aug 13, 2026
dd9139d
Merge branch 'fix/cloud-run-reliability' into feat/cli-push-blocks
jamesbhobbs Aug 13, 2026
2d07cf3
fix(sync): apply the approved plan and stop phantom integration updates
jamesbhobbs Aug 17, 2026
14c0dc4
feat(cloud): add updateNotebook client for PATCH /v2/notebooks/{noteb…
jankoritak Aug 18, 2026
690d318
feat(cli): add `deepnote notebooks rename` command
jankoritak Aug 18, 2026
aa82701
fix(cli): address notebook rename review feedback
jankoritak Aug 18, 2026
d4c6300
fix(cli): load .env before reading token in notebooks rename
jankoritak Aug 18, 2026
e25eeeb
fix: apply CodeRabbit auto-fixes
coderabbitai[bot] Aug 18, 2026
7dd84f3
style: re-run prettier after CodeRabbit auto-fixes
jankoritak Aug 18, 2026
e7ece48
Merge origin/main into feat/cli-rename-notebook
jankoritak Sep 9, 2026
928168d
Merge branch 'main' into feat/cli-rename-notebook
jankoritak Sep 29, 2026
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
35 changes: 35 additions & 0 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,41 @@ deepnote integrations edit <integration-id>

Like `integrations pull`, both commands fail with exit code 2 and leave all files untouched if the integrations file contains invalid YAML.

### `notebooks rename <notebook-id> <new-name>`

Rename a notebook in Deepnote Cloud.

```bash
deepnote notebooks rename <notebook-id> "New name"
```

**Options:**

| Option | Description | Default |
| ----------------------- | ------------------------------------------------------------------------------- | ------------ |
| `--url <url>` | Deepnote API base URL | Deepnote API |
| `--token <token>` | API token (or use `DEEPNOTE_TOKEN` env var or `.env` file in current directory) | |
| `-o, --output <format>` | Output format: `json` | |

**Examples:**

```bash
# Rename a notebook by its ID
deepnote notebooks rename 7061f86dec6e4e11893288f295a82017 "Quarterly report"

# Machine-readable output
deepnote notebooks rename 7061f86dec6e4e11893288f295a82017 "Quarterly report" --output json
```

JSON output returns `{ "success": true, "notebook": { "id", "projectId", "name", "isInit" } }`; `isInit`
is `true` when the notebook is the project's init notebook after the rename.

Renaming fails with a conflict when another notebook in the project already uses the name, when the project is suspended, and for single-notebook or Agent projects, where the project owns its notebook's name.

Naming a notebook `Init` designates it as the project's init notebook in Deepnote. This is a
functional change, not only a cosmetic rename: the init notebook can run as a prelude to other
notebooks in the project.

### `completion <shell>`

Generate shell completion scripts for tab completion.
Expand Down
50 changes: 50 additions & 0 deletions packages/cli/src/cli.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
import { execFileSync } from 'node:child_process'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { createProgram, run } from './cli'
import { resetOutputConfig } from './output'
import { version } from './version'

function runBashCompletion(script: string, words: string[]): string[] {
const invocation = [
script,
'COMP_WORDS=("$@")',
`COMP_CWORD=$((\${#COMP_WORDS[@]} - 1))`,
'_deepnote_completions',
`printf '%s\\n' "\${COMPREPLY[@]}"`,
].join('\n')
const output = execFileSync('bash', ['-c', invocation, 'bash', ...words], { encoding: 'utf8' })

return output.trim().split('\n').filter(Boolean)
}

describe('CLI', () => {
beforeEach(() => {
resetOutputConfig()
Expand Down Expand Up @@ -83,6 +97,16 @@ describe('CLI', () => {
expect(completionCmd?.description()).toBe('Generate shell completion scripts')
})

it('notebooks rename command is properly configured', () => {
const program = createProgram()
const notebooksCmd = program.commands.find(cmd => cmd.name() === 'notebooks')
const renameCmd = notebooksCmd?.commands.find(cmd => cmd.name() === 'rename')

expect(renameCmd).toBeDefined()
expect(renameCmd?.description()).toBe('Rename a notebook in Deepnote Cloud')
expect(renameCmd?.options.map(option => option.flags)).toContain('-o, --output <format>')
})

it('schedule command is properly configured', () => {
const program = createProgram()
const scheduleCmd = program.commands.find(cmd => cmd.name() === 'schedule')
Expand Down Expand Up @@ -176,6 +200,23 @@ describe('CLI', () => {
return 0
fi`)
expect(output).toContain('--storage-mode --timeout --push --yes --url')
expect(runBashCompletion(output, ['deepnote', 'notebooks', 'rename', 'notebook-id', 'New name', '--'])).toEqual([
'--url',
'--token',
'--output',
])
expect(runBashCompletion(output, ['deepnote', 'notebooks', 'rename', 'notebook-id', 'New name', '-'])).toEqual([
'-o',
'--url',
'--token',
'--output',
])
expect(
runBashCompletion(output, ['deepnote', 'notebooks', 'rename', 'notebook-id', 'New name', '--output', ''])
).toEqual(['json'])
expect(
runBashCompletion(output, ['deepnote', 'notebooks', 'rename', 'notebook-id', 'New name', '-o', ''])
).toEqual(['json'])
consoleSpy.mockRestore()
})

Expand Down Expand Up @@ -298,6 +339,15 @@ describe('CLI', () => {
'Invalid output format'
)
})

it('errors when invalid output format is used with notebooks rename', async () => {
const program = createProgram()
program.exitOverride()

await expect(
program.parseAsync(['notebooks', 'rename', 'notebook-id', 'New name', '-o', 'yaml'], { from: 'user' })
).rejects.toThrow('Invalid output format')
})
})

describe('invalid option values', () => {
Expand Down
33 changes: 33 additions & 0 deletions packages/cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { createIntegrationsPullAction } from './commands/integrations'
import { createIntegrationsAddAction } from './commands/integrations/add-integration'
import { createIntegrationsEditAction } from './commands/integrations/edit-integration'
import { createLintAction } from './commands/lint'
import { createNotebooksRenameAction } from './commands/notebooks/rename-notebook'
import { createOpenAction } from './commands/open'
import { createPublishAction } from './commands/publish'
import { createRunAction } from './commands/run'
Expand Down Expand Up @@ -1155,6 +1156,38 @@ ${c.bold('Examples:')}
.option('--file <path>', 'Path to integrations file', DEFAULT_INTEGRATIONS_FILE)
.option('--env-file <path>', 'Path to .env file for storing secrets', DEFAULT_ENV_FILE)
.action(createIntegrationsEditAction(program))

// Notebooks command group - manage notebooks in Deepnote Cloud
const notebooksCmd = program
.command('notebooks')
.description('Manage notebooks in Deepnote Cloud')
.addHelpText('after', () => {
const c = getChalk()
return `
${c.bold('Subcommands:')}
rename Rename a notebook in Deepnote Cloud

${c.bold('Examples:')}
${c.dim('# Rename a notebook by its ID')}
$ deepnote notebooks rename <notebook-id> "New name"

${c.dim('# Rename with a specific token')}
$ deepnote notebooks rename <notebook-id> "New name" --token <token>

${c.dim('# Machine-readable output')}
$ deepnote notebooks rename <notebook-id> "New name" --output json
`
})

notebooksCmd
.command('rename')
.description('Rename a notebook in Deepnote Cloud')
.argument('<notebook-id>', 'ID of the notebook in Deepnote Cloud')
.argument('<new-name>', 'New notebook name')
.option('--token <token>', `Deepnote API token (defaults to ${DEEPNOTE_TOKEN_ENV})`)
.option('--url <url>', 'Deepnote API base URL', DEFAULT_API_URL)
.option('-o, --output <format>', 'Output format: json', createFormatValidator(['json']))
.action(createNotebooksRenameAction(program))
}

/**
Expand Down
Loading
Loading