Skip to content

feat: notion interactive wizard command#272

Open
rubenmarcus wants to merge 5 commits intomainfrom
feat/notion-integration-wizard
Open

feat: notion interactive wizard command#272
rubenmarcus wants to merge 5 commits intomainfrom
feat/notion-integration-wizard

Conversation

@rubenmarcus
Copy link
Member

Summary

  • Add ralph-starter notion top-level command for interactive Notion page selection
  • Wizard flow: authenticate → search pages by name → select a page → run
  • Reuses shared wizard utilities from src/integrations/wizards/shared.ts
  • Falls back to wizard when ralph-starter run --from notion is used without --project
  • Updated Notion source docs and README commands table

Changes

  • New: src/commands/notion.ts — Notion wizard with search API integration
  • Modified: src/cli.ts — Register ralph-starter notion command
  • Modified: src/commands/run.ts — Wizard fallback for --from notion
  • Modified: docs/docs/sources/notion.md — Wizard section + examples
  • Modified: README.md — Commands table entry

Test plan

  • ralph-starter notion launches wizard and completes flow
  • ralph-starter run --from notion (no --project) redirects to wizard
  • ralph-starter run --from notion --project "Product Specs" still works (existing behavior)
  • URL paste mode accepts notion.so and notion.site URLs
  • Search mode finds pages and "Search again" loop works
  • Missing credentials prompts for token and saves
  • pnpm build — no new errors (pre-existing pixelmatch/sharp errors only)

🤖 Generated with Claude Code

rubenmarcus and others added 4 commits March 8, 2026 11:17
…or-url)

Shared utilities for integration wizards:
- ensureCredentials(): check for existing auth, prompt for API key if missing
- askBrowseOrUrl(): common "browse or paste URL" prompt
- askForUrl(): URL input with domain validation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Interactive wizard for working with Notion pages:
- Auth check (Notion API token)
- Search pages by query with "search again" loop, or paste URL
- Extracts page titles from Notion's property structure
- Delegates to runCommand() for execution

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add notion wizard to CLI as top-level command
- Add wizard fallback in run.ts for --from notion without project

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add interactive wizard section to Notion source docs
- Add ralph-starter notion to README commands table

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@chatgpt-codex-connector
Copy link

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 8, 2026

Issue Linking Reminder

This PR doesn't appear to have a linked issue. Consider linking to:

  • This repo: Closes #123
  • ralph-ideas: Closes multivmlabs/ralph-ideas#123

Using Closes, Fixes, or Resolves will auto-close the issue when this PR is merged.


If this PR doesn't need an issue, you can ignore this message.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 8, 2026

✔️ Bundle Size Analysis

Metric Value
Base 2425.03 KB
PR 2448.02 KB
Diff 22.98 KB (0%)
Bundle breakdown
156K	dist/auth
32K	dist/automation
4.0K	dist/cli.d.ts
4.0K	dist/cli.d.ts.map
20K	dist/cli.js
12K	dist/cli.js.map
596K	dist/commands
28K	dist/config
4.0K	dist/index.d.ts
4.0K	dist/index.d.ts.map
4.0K	dist/index.js
4.0K	dist/index.js.map
916K	dist/integrations
84K	dist/llm
900K	dist/loop
188K	dist/mcp
32K	dist/presets
92K	dist/setup
40K	dist/skills
392K	dist/sources
76K	dist/ui
144K	dist/utils
336K	dist/wizard

@github-actions
Copy link
Contributor

github-actions bot commented Mar 8, 2026

🔗 Docs Preview

Preview URL: https://feat-notion-integration-wiza.ralph-starter-docs.pages.dev

This preview was deployed from the latest commit on this PR.

Comment on lines +50 to +54
headers: {
Authorization: `Bearer ${token}`,
'Notion-Version': NOTION_API_VERSION,
'Content-Type': 'application/json',
},

Check warning

Code scanning / CodeQL

File data in outbound network request Medium

Outbound network request depends on
file data
.
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 8, 2026

Greptile Summary

This PR introduces an interactive Notion wizard command (ralph-starter notion) that guides users through authenticating, searching pages by name, selecting a page, and delegating execution to runCommand. It also adds a fallback so that ralph-starter run --from notion (without --project) automatically launches the wizard.

Key changes and findings:

  • src/commands/notion.ts — New wizard. Contains a real logic bug: the token retrieval at line 146 falls back on creds?.token only, missing creds?.apiKey. Because sources/config.ts maps the Notion env-var to the apiKey credential key, a credential saved via a non-wizard path (or any path that stores under apiKey) will silently fail the token guard. Also participates in a circular import with run.ts; the dynamic import in run.ts breaks the load-time cycle but the architecture is fragile.
  • src/integrations/wizards/shared.ts — New shared utilities. Logic is correct, but interface is used for CredentialOptions where type is preferred per project rules.
  • src/commands/run.ts — Wizard fallback inserted at an appropriate position (after git-repo checks, before source fetch). Dynamic import correctly prevents a circular-dependency crash at module load.
  • src/cli.ts, README.md, docs/ — Registration, documentation, and README table updates are all clean.

Confidence Score: 3/5

  • Safe to merge with caveats — the token lookup bug can cause silent auth failures for users whose credentials were stored under the apiKey key.
  • Score reduced from 5 because of the real logic bug in the Notion token lookup that can cause a "Could not obtain token" error even when valid credentials exist, plus a circular import dependency between notion.ts and run.ts and several interface-vs-type style violations per project rules.
  • Pay close attention to src/commands/notion.ts (token lookup bug at line 146 and circular dependency with run.ts) and src/integrations/wizards/shared.ts (interface vs type).

Important Files Changed

Filename Overview
src/commands/notion.ts New Notion wizard command — implements auth, browse/URL modes, and search-select loop. Contains a token lookup bug (missing creds?.apiKey fallback), interface instead of type declarations, and participates in a circular import with run.ts.
src/integrations/wizards/shared.ts New shared wizard utilities for credential prompting and URL input. Logic is solid; minor style issue: CredentialOptions should be type not interface per project rules.
src/commands/run.ts Adds wizard fallback for --from notion without --project. Dynamic import correctly breaks the circular dependency at load time. Placement after git-repo checks but before spinner/fetch is appropriate.
src/cli.ts Registers the ralph-starter notion command with all expected flags. Mirrors existing command registration patterns cleanly.
docs/docs/sources/notion.md Adds wizard section with usage examples and available flags. Documentation is accurate and well-structured.
README.md Adds ralph-starter notion row to the commands table. Simple, correct change.

Sequence Diagram

sequenceDiagram
    participant User
    participant CLI as cli.ts
    participant Run as run.ts
    participant Notion as notion.ts (wizard)
    participant Shared as shared.ts
    participant Config as sources/config.ts
    participant API as Notion API

    User->>CLI: ralph-starter notion [opts]
    CLI->>Notion: notionCommand(opts)
    Notion->>Shared: ensureCredentials('notion', ...)
    Shared->>Config: getSourceCredentials('notion')
    Config-->>Shared: creds or null
    alt No credentials found
        Shared->>User: Prompt for token
        User-->>Shared: token input
        Shared->>Config: setSourceCredential('notion', 'token', value)
    end
    Shared-->>Notion: token string
    Notion->>Shared: askBrowseOrUrl('Notion')
    Shared-->>Notion: 'browse' | 'url'
    alt URL mode
        Notion->>Shared: askForUrl('Notion', pattern)
        Shared-->>Notion: url
        Notion->>Run: runCommand(undefined, {from:'notion', project:url, ...})
    else Browse mode
        Notion->>Config: getSourceCredentials('notion')
        Config-->>Notion: creds
        loop Search loop
            Notion->>User: Prompt search query
            User-->>Notion: query
            Notion->>API: POST /search {query}
            API-->>Notion: results[]
            Notion->>User: Select page or Search again
            User-->>Notion: selectedUrl or SEARCH_AGAIN
        end
        Notion->>Run: runCommand(undefined, {from:'notion', project:selectedUrl, ...})
    end

    Note over User,Run: Fallback path
    User->>CLI: ralph-starter run --from notion
    CLI->>Run: runCommand(task, {from:'notion', ...})
    Run->>Notion: notionCommand(opts) [dynamic import]
Loading

Last reviewed commit: 249a6d2

Comment on lines +16 to +28
export interface NotionWizardOptions {
commit?: boolean;
push?: boolean;
pr?: boolean;
validate?: boolean;
maxIterations?: number;
agent?: string;
}

const NOTION_API_BASE = 'https://api.notion.com/v1';
const NOTION_API_VERSION = '2022-06-28';

interface NotionSearchResult {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use type instead of interface for simple data structures

Per the project style guide, type should be used by default unless you specifically need interface features (extension / implementation). Both NotionWizardOptions and NotionSearchResult are plain data structures with no inheritance needs.

Suggested change
export interface NotionWizardOptions {
commit?: boolean;
push?: boolean;
pr?: boolean;
validate?: boolean;
maxIterations?: number;
agent?: string;
}
const NOTION_API_BASE = 'https://api.notion.com/v1';
const NOTION_API_VERSION = '2022-06-28';
interface NotionSearchResult {
export type NotionWizardOptions = {
commit?: boolean;
push?: boolean;
pr?: boolean;
validate?: boolean;
maxIterations?: number;
agent?: string;
};

The same applies to interface NotionSearchResult at line 28 — it should be declared as type NotionSearchResult = { ... }.

This pattern is also violated in src/integrations/wizards/shared.ts:12 where export interface CredentialOptions should be export type CredentialOptions = { ... }.

Rule Used: Use type by default in TypeScript unless you spe... (source)

Learnt From
cytonic-network/ai-frontend#48

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/commands/notion.ts
Line: 16-28

Comment:
**Use `type` instead of `interface` for simple data structures**

Per the project style guide, `type` should be used by default unless you specifically need `interface` features (extension / implementation). Both `NotionWizardOptions` and `NotionSearchResult` are plain data structures with no inheritance needs.

```suggestion
export type NotionWizardOptions = {
  commit?: boolean;
  push?: boolean;
  pr?: boolean;
  validate?: boolean;
  maxIterations?: number;
  agent?: string;
};
```

The same applies to `interface NotionSearchResult` at line 28 — it should be declared as `type NotionSearchResult = { ... }`.

This pattern is also violated in `src/integrations/wizards/shared.ts:12` where `export interface CredentialOptions` should be `export type CredentialOptions = { ... }`.

**Rule Used:** Use `type` by default in TypeScript unless you spe... ([source](https://app.greptile.com/review/custom-context?memory=c862f053-5655-4b41-be69-c840e3c9f280))

**Learnt From**
[cytonic-network/ai-frontend#48](https://github.com/cytonic-network/ai-frontend/pull/48)

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +143 to +146
// Browse mode — search for pages
// Get the actual token for API calls (ensureCredentials may have returned '__cli_auth__')
const creds = await import('../sources/config.js').then((m) => m.getSourceCredentials('notion'));
const token = process.env.NOTION_API_KEY || creds?.token;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dynamic import used to break circular dependency

notion.ts statically imports runCommand from run.ts (line 14), while run.ts dynamically imports notionCommand from notion.ts (line 347). This is a circular dependency handled at runtime via the dynamic import. While it works in Node.js/ESM, it is a recognised architecture smell and can cause hard-to-debug issues as the codebase grows (e.g., if the static import graph is analysed by bundlers or test runners).

Consider extracting the shared NotionWizardOptions type and/or a thin launcher helper into a separate module (e.g., src/commands/notion-wizard.ts) so that run.ts can import the wizard without creating a cycle. At a minimum, a short comment explaining the intentional cycle would help future maintainers.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/commands/notion.ts
Line: 143-146

Comment:
**Dynamic import used to break circular dependency**

`notion.ts` statically imports `runCommand` from `run.ts` (line 14), while `run.ts` dynamically imports `notionCommand` from `notion.ts` (line 347). This is a circular dependency handled at runtime via the dynamic import. While it works in Node.js/ESM, it is a recognised architecture smell and can cause hard-to-debug issues as the codebase grows (e.g., if the static import graph is analysed by bundlers or test runners).

Consider extracting the shared `NotionWizardOptions` type and/or a thin launcher helper into a separate module (e.g., `src/commands/notion-wizard.ts`) so that `run.ts` can import the wizard without creating a cycle. At a minimum, a short comment explaining the intentional cycle would help future maintainers.

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

// Browse mode — search for pages
// Get the actual token for API calls (ensureCredentials may have returned '__cli_auth__')
const creds = await import('../sources/config.js').then((m) => m.getSourceCredentials('notion'));
const token = process.env.NOTION_API_KEY || creds?.token;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing apiKey fallback when retrieving Notion token

getSourceCredentials('notion') can return credentials stored under either the token key (saved by ensureCredentials) or the apiKey key (populated from the environment-variable mapping defined in sources/config.ts). The current line only checks creds?.token, so if the credential object only carries an apiKey property — for example, when it was previously set through a non-wizard config path — the token will be undefined and the check at line 148 will show the "Could not obtain Notion API token" error even though a valid credential exists.

Add a final || creds?.apiKey fallback to cover this case, matching the same fallback chain already used in shared.ts (existing?.token || existing?.apiKey).

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/commands/notion.ts
Line: 146

Comment:
**Missing `apiKey` fallback when retrieving Notion token**

`getSourceCredentials('notion')` can return credentials stored under either the `token` key (saved by `ensureCredentials`) or the `apiKey` key (populated from the environment-variable mapping defined in `sources/config.ts`). The current line only checks `creds?.token`, so if the credential object only carries an `apiKey` property — for example, when it was previously set through a non-wizard config path — the token will be `undefined` and the check at line 148 will show the "Could not obtain Notion API token" error even though a valid credential exists.

Add a final `|| creds?.apiKey` fallback to cover this case, matching the same fallback chain already used in `shared.ts` (`existing?.token || existing?.apiKey`).

How can I resolve this? If you propose a fix, please make it concise.

@rubenmarcus rubenmarcus changed the title feat: Notion interactive wizard command feat: notion interactive wizard command Mar 8, 2026
- Change interface to type for plain data structures (Greptile)
- Anchor Notion URL regex pattern (CodeQL fix)
- Add apiKey fallback when retrieving Notion token (Greptile)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant