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
5 changes: 5 additions & 0 deletions .changeset/account-sign-in.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"fulldev": minor
---

Sign in through Fulldev's own account pages at `https://app.full.dev`; existing sign-ins must sign in again.
5 changes: 5 additions & 0 deletions .changeset/apps.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"fulldev": minor
---

Fulldev's products are now called apps. The help lists them under "Apps" and reads `fulldev <app> <command>`, and the JSON of `fulldev status`, `fulldev logout` and a sign-in error names each one as `app` (a list under `apps`) instead of `product` (`products`). Commands, names and servers stay the same.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
5 changes: 5 additions & 0 deletions .changeset/contacts-app.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"fulldev": minor
---

Add Fulldev Contacts as the app `contacts`: check, pause and resume the daily copy of your Google contacts, and answer its look-alike questions.
5 changes: 5 additions & 0 deletions .changeset/one-server.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"fulldev": minor
---

Every app through one Fulldev MCP server, at `https://app.full.dev/mcp`, with one sign-in. `fulldev login`, `fulldev status` and `fulldev logout` no longer take an app, and `status` and `logout` print that one sign-in as one object instead of a list under `apps`; a sign-in error names `fulldev login`. Each app's tools start with its name, such as `cms_list_repositories`. `fulldev instructions`, `fulldev tools` and `fulldev call` work across every app you may use; `fulldev <app> instructions|tools|call` show and call that app's part, with tool names with or without its prefix. `FULLDEV_URL` replaces `FULLDEV_<APP>_URL`.
5 changes: 5 additions & 0 deletions .changeset/pages-app.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"fulldev": minor
---

Add Fulldev Pages as the app `pages`.
5 changes: 5 additions & 0 deletions .changeset/paste-sign-in.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"fulldev": minor
---

Sign in over SSH: with `--no-browser`, paste the address of the page after sign-in into the terminal when the browser is on another computer. fulldev no longer registers an OAuth client itself and fails with `CLIENT_METADATA_UNSUPPORTED` on a server without Client ID Metadata Documents. It no longer removes the sign-in file of version 0.1.0; delete `~/.config/fulldev/credentials.json` if it is still there.
5 changes: 5 additions & 0 deletions .changeset/token-lifetime.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"fulldev": patch
---

Access tokens now last 15 minutes; the help of `fulldev logout` says so.
69 changes: 37 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# Fulldev CLI

Use Fulldev products from a terminal or an AI agent. The CLI is a thin client of each product's MCP server: it reads the tools, their schemas, and the instructions from the server each time, so it stays in step with the product.
Use Fulldev apps from a terminal or an AI agent. The CLI is a thin client of the Fulldev MCP server at `https://app.full.dev/mcp`, which has the tools of every app your organization gives you: it reads the tools, their schemas, and the instructions from the server each time, so it stays in step with the apps. Each app's tools start with its name, such as `cms_`.

| Product | Server | For |
| --------- | ------------------------------ | ----------------------------------------------------------------------------------------- |
| `cms` | `https://cms.full.dev/mcp` | Editing your website through the [Fulldev CMS](https://cms.full.dev) |
| `connect` | `https://connect.full.dev/mcp` | Your business tools, such as Shopify, through [Fulldev Connect](https://connect.full.dev) |
| `scan` | `https://scan.full.dev/mcp` | Scanning websites with [Fulldev Scan](https://scan.full.dev), for administrators only |
| `sites` | `https://sites.full.dev/mcp` | A finished website from [Fulldev Sites](https://sites.full.dev), for administrators only |
| App | For |
| ---------- | ---------------------------------------------------------------------------------------------------- |
| `cms` | Editing your website through the [Fulldev CMS](https://cms.full.dev) |
| `connect` | Your business tools, such as Shopify, through [Fulldev Connect](https://connect.full.dev) |
| `scan` | Scanning websites with [Fulldev Scan](https://scan.full.dev), for administrators only |
| `sites` | A finished website from [Fulldev Sites](https://sites.full.dev), for administrators only |
| `pages` | Reports and plans as web pages with [Fulldev Pages](https://pages.full.dev), for administrators only |
| `contacts` | The daily copy of your Google contacts with [Fulldev Contacts](https://contacts.full.dev) |

## Install

Expand All @@ -34,47 +36,50 @@ npx skills add fulldotdev/cli
## Sign in

```sh
fulldev login # every product, one after the other
fulldev login cms # one product
fulldev status # per product: signed in, valid, email, organization, expiry
fulldev logout # every product
fulldev logout cms # one product
fulldev login # once, for every app
fulldev status # signed in, valid, email, organization, expiry
fulldev logout
```

`fulldev login` opens your browser to sign in with your Fulldev account and choose your organization. Add `--no-browser` to only print the link.
`fulldev login` opens your browser at [app.full.dev](https://app.full.dev/sign-in), where you sign in with your Fulldev account, with a code sent to your email or with Google, and choose your organization when you are in more than one. Add `--no-browser` to only print the link.

There is one Fulldev account for all products, but each product gets its own tokens, which only that product accepts. The browser remembers your account, so signing in to the next product is quick. The organization is chosen at sign-in; to switch organization, run `fulldev login <product>` again.
On a computer without a browser, such as over SSH, run `fulldev login --no-browser` and open the link on your own computer. The page after sign-in does not load there, because it is meant for the computer that runs `fulldev`: copy its address from the address bar and paste it into the terminal.

`fulldev logout` revokes the product's refresh token at the authorization server (access tokens cannot be revoked and expire within a day) and deletes them from this computer. When revoking fails, for example offline, it still deletes them and says so.
One sign-in covers every app the organization gives you: the tokens are for the Fulldev MCP server, which checks on every call which apps you may use. When you are in more than one organization, you choose one at every sign-in; to switch organization, run `fulldev login` again.

`fulldev logout` revokes the refresh token at the authorization server (access tokens cannot be revoked and expire within 15 minutes) and deletes the tokens from this computer. When revoking fails, for example offline, it still deletes them and says so.

How it works:

- Browser sign-in uses OAuth with PKCE (S256) and a redirect to `http://127.0.0.1:<random port>/callback`. It asks for the product's MCP URL as the resource, so the token is only valid for that product.
- The CLI is a pre-registered public OAuth client of [Clerk](https://clerk.full.dev). For another authorization server, such as the Clerk development instance behind a deploy preview, it registers a client itself the first time.
- Tokens are stored in the OS keychain (macOS Keychain, Windows Credential Manager, or the Secret Service on Linux) under the service `fulldev`, one entry per product server. Where no keychain is available, such as in many containers and CI runners, they go to `~/.config/fulldev/auth.json` (or `$XDG_CONFIG_HOME/fulldev/auth.json`), readable only by you; `fulldev status` says which one is used.
- Browser sign-in uses OAuth with PKCE (S256) and a redirect to `http://127.0.0.1:<random port>/callback`, or the pasted address of that redirect, after checking that it belongs to this sign-in. It asks for the server's MCP URL as the resource, so the token is only valid there.
- The CLI is a public OAuth client that needs no registration: its client id is the address of its [Client ID Metadata Document](https://full.dev/oauth/cli.json), which the authorization server at `https://app.full.dev/api/auth` reads. It never registers a client, so sign-in to a server that does not support such documents fails with `CLIENT_METADATA_UNSUPPORTED`.
- Tokens are stored in the OS keychain (macOS Keychain, Windows Credential Manager, or the Secret Service on Linux) under the service `fulldev`, one entry per server. Where no keychain is available, such as in many containers and CI runners, they go to `~/.config/fulldev/auth.json` (or `$XDG_CONFIG_HOME/fulldev/auth.json`), readable only by you; `fulldev status` says which one is used.
- Access tokens are refreshed when they expire. Parallel commands take turns through a lock file next to it, so they never refresh the same token twice.
- Version 0.1.0 kept a sign-in for the CMS in `~/.config/fulldev/credentials.json`. The first command of this version removes that file and asks you to sign in again with `fulldev login cms`.
- Sign-ins from fulldev 0.3 and earlier were per app and made with Clerk, and no longer work: run `fulldev login` again.

Product commands never start a sign-in without a terminal (when stdin or stdout is not a TTY): they fail at once with exit code 3 and say which `fulldev login <product>` to run. At a terminal they open the browser sign-in themselves; `--no-login` turns that off.
Commands never start a sign-in without a terminal (when stdin or stdout is not a TTY): they fail at once with exit code 3 and say to run `fulldev login`. At a terminal they open the browser sign-in themselves; `--no-login` turns that off.

## Use

```sh
fulldev cms instructions # how to use the tools, from the server; read first
fulldev cms tools # name, title and description of each tool
fulldev instructions # how to use every app you may use, from the server; read first
fulldev tools # name, title and description of every tool
fulldev call cms_list_repositories
fulldev cms instructions # the overview and the CMS's own part
fulldev cms tools # the CMS's tools only
fulldev cms tools commit_files # description, input schema, output schema, annotations
fulldev cms call list_projects
fulldev cms call read_file '{"branchId":"<id>","path":"src/content/home.md"}'
fulldev cms call commit_files --file changes.json
echo '{"branchId":"<id>"}' | fulldev cms call get_preview -
fulldev cms form wait <branchId> <formId> --timeout 60
fulldev connect tools
fulldev help cms call # help for any command
```

After `fulldev <app>`, a tool's name may leave out the app's prefix: `fulldev cms call read_file` calls `cms_read_file`.

`call` prints the tool's result as JSON on stdout. When the tool fails, it prints the error on stderr and exits with 1.

`fulldev cms form wait` keeps calling `wait_for_form` until the person sends the form, with a progress line on stderr each round, then prints the result. After `--timeout` minutes (default 30) it stops, also in the middle of a request, prints the form id, and exits with 2, so you can run it again.
`fulldev cms form wait` keeps calling `cms_wait_for_form` until the person sends the form, with a progress line on stderr each round, then prints the result. After `--timeout` minutes (default 30) it stops, also in the middle of a request, prints the form id, and exits with 2, so you can run it again.

## Output and exit codes

Expand All @@ -84,8 +89,8 @@ Data is JSON on stdout. `instructions` and help are plain text. Progress lines g
{
"error": {
"code": "SIGN_IN_REQUIRED",
"message": "Not signed in to Fulldev CMS. Run: fulldev login cms",
"command": "fulldev login cms"
"message": "Not signed in to Fulldev. Run: fulldev login",
"command": "fulldev login"
}
}
```
Expand All @@ -100,11 +105,11 @@ Data is JSON on stdout. `instructions` and help are plain text. Progress lines g

## Deploy previews

`--url <mcp url>` or `FULLDEV_<PRODUCT>_URL` (such as `FULLDEV_CMS_URL` or `FULLDEV_CONNECT_URL`) selects another server for one product. Each server has its own sign-in.
`--url <mcp url>` or `FULLDEV_URL` selects another server, such as a deploy preview's, which serves it at `/mcp`. Each server has its own sign-in.

```sh
FULLDEV_CMS_URL=https://deploy-preview-12--fulldev-cms.netlify.app/mcp fulldev login cms
fulldev cms tools --url https://deploy-preview-12--fulldev-cms.netlify.app/mcp
FULLDEV_URL=https://deploy-preview-12--fulldev-app.netlify.app/mcp fulldev login
fulldev cms tools --url https://deploy-preview-12--fulldev-app.netlify.app/mcp
```

## Develop
Expand All @@ -115,13 +120,13 @@ pnpm check # format, lint, type check, tests, and that dist/ is up to date
pnpm build # writes dist/cli.mjs
```

A product is one entry in `src/products.ts`. The pre-registered OAuth client ids are in `clientIds` in `src/oauth.ts`.
An app is one entry in `src/apps.ts`; the server's address is `server` there. The OAuth client id is `clientMetadataUrl` in `src/oauth.ts`; the document at that address is published with the full.dev website.

`dist/` is committed, because `npx github:fulldotdev/cli` runs it without a build step. Run `pnpm build` and commit `dist/` with every source change.

## Releases

Every pull request that changes what users get adds a changeset with `pnpm changeset`. Merging to `main` opens a release pull request; merging that publishes the [`fulldev` package](https://www.npmjs.com/package/fulldev) to npm from GitHub Actions with trusted publishing and provenance, without an npm token. `dist/` stays committed, so `npx github:fulldotdev/cli` keeps working; `pnpm check` fails when it is out of date.
Every change that users get adds a changeset with `pnpm changeset`. The [`fulldev` package](https://www.npmjs.com/package/fulldev) is published only from [fulldotdev/cli](https://github.com/fulldotdev/cli): merging a change with its changeset to `main` opens a release pull request, and merging that publishes to npm from GitHub Actions with trusted publishing and provenance, without an npm token.

## License

Expand Down
Loading
Loading