@scribe-platform/cli 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -3
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +15 -1
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/prospect.d.ts +27 -0
- package/dist/commands/prospect.d.ts.map +1 -0
- package/dist/commands/prospect.js +213 -0
- package/dist/commands/prospect.js.map +1 -0
- package/dist/commands/request.d.ts +38 -0
- package/dist/commands/request.d.ts.map +1 -0
- package/dist/commands/request.js +214 -0
- package/dist/commands/request.js.map +1 -0
- package/dist/commands/site.d.ts +11 -0
- package/dist/commands/site.d.ts.map +1 -1
- package/dist/commands/site.js +85 -6
- package/dist/commands/site.js.map +1 -1
- package/dist/commands/update.d.ts +10 -0
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +128 -31
- package/dist/commands/update.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/claude-hook.d.ts +74 -0
- package/dist/lib/claude-hook.d.ts.map +1 -0
- package/dist/lib/claude-hook.js +108 -0
- package/dist/lib/claude-hook.js.map +1 -0
- package/dist/lib/client.d.ts +199 -1
- package/dist/lib/client.d.ts.map +1 -1
- package/dist/lib/client.js +91 -2
- package/dist/lib/client.js.map +1 -1
- package/dist/lib/skill.d.ts +13 -0
- package/dist/lib/skill.d.ts.map +1 -1
- package/dist/lib/skill.js +33 -2
- package/dist/lib/skill.js.map +1 -1
- package/dist/lib/update-check.d.ts +46 -0
- package/dist/lib/update-check.d.ts.map +1 -0
- package/dist/lib/update-check.js +103 -0
- package/dist/lib/update-check.js.map +1 -0
- package/package.json +1 -1
- package/skills/scribe-site-builder/SKILL.md +207 -52
- package/skills/scribe-site-builder/recipes/README.md +10 -3
- package/skills/scribe-site-builder/recipes/domains.md +151 -0
- package/skills/scribe-site-builder/recipes/forms.md +5 -2
- package/skills/scribe-site-builder/recipes/prospects.md +192 -0
- package/skills/scribe-site-builder/recipes/requests.md +145 -0
- package/skills/scribe-site-builder/recipes/schema.md +24 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export declare const PACKAGE_NAME = "@scribe-platform/cli";
|
|
2
|
+
/**
|
|
3
|
+
* Compares two dotted numeric versions. Returns true when `candidate` is newer
|
|
4
|
+
* than `current`.
|
|
5
|
+
*
|
|
6
|
+
* Deliberately not a semver dependency: this package publishes plain
|
|
7
|
+
* `major.minor.patch` and the one judgement call — what to do with a
|
|
8
|
+
* prerelease suffix — is answered by ignoring it. A `0.8.0-beta.1` compares
|
|
9
|
+
* equal to `0.8.0`, so someone already on the beta is not told to "update" to
|
|
10
|
+
* the release they effectively have, and someone on `0.7.0` is offered it.
|
|
11
|
+
* Shipping a prerelease to `latest` is not something this project does.
|
|
12
|
+
*/
|
|
13
|
+
export declare function isNewer(candidate: string, current: string): boolean;
|
|
14
|
+
export interface UpdateCheck {
|
|
15
|
+
current: string;
|
|
16
|
+
/** Null when the registry couldn't be reached. */
|
|
17
|
+
latest: string | null;
|
|
18
|
+
updateAvailable: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* False when the registry could not be reached.
|
|
21
|
+
*
|
|
22
|
+
* Reported separately from `latest` so a caller can't say "up to date" when
|
|
23
|
+
* it means "couldn't ask" — the two are the same shape and only one of them
|
|
24
|
+
* is a fact.
|
|
25
|
+
*/
|
|
26
|
+
reachable: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Whether a newer CLI has been published.
|
|
30
|
+
*
|
|
31
|
+
* Runs on **every** session start, with no cache. An earlier version cached
|
|
32
|
+
* the answer for a day to keep a round trip off the front of each
|
|
33
|
+
* conversation; measuring it made that look like the wrong trade — 210ms
|
|
34
|
+
* against being up to a day late on a fix, plus a cache that silently
|
|
35
|
+
* swallowed the environment override this is tested with. Checking every time
|
|
36
|
+
* is both faster to the user and less code.
|
|
37
|
+
*/
|
|
38
|
+
export declare function checkForUpdate(current: string): Promise<UpdateCheck>;
|
|
39
|
+
/**
|
|
40
|
+
* What `npm install -g` is pointed at.
|
|
41
|
+
*
|
|
42
|
+
* Overridable so the whole update path can be exercised against a local
|
|
43
|
+
* `npm pack` tarball before anything is published — see TESTING-UPDATES.md.
|
|
44
|
+
*/
|
|
45
|
+
export declare function installSpec(): string;
|
|
46
|
+
//# sourceMappingURL=update-check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-check.d.ts","sourceRoot":"","sources":["../../src/lib/update-check.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,yBAAyB,CAAC;AAsBnD;;;;;;;;;;GAUG;AACH,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAiBnE;AAuBD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;IACzB;;;;;;OAMG;IACH,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAe1E;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAEpC"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
export const PACKAGE_NAME = '@scribe-platform/cli';
|
|
2
|
+
/**
|
|
3
|
+
* The published package is **public**, so this is a plain unauthenticated GET
|
|
4
|
+
* — no `npm view` subprocess, no auth, no dependency on the admin app knowing
|
|
5
|
+
* which CLI version is current.
|
|
6
|
+
*
|
|
7
|
+
* The `-/package/<name>/dist-tags` route rather than the package document:
|
|
8
|
+
* it answers in ~200ms with 18 bytes, where the full packument is 30KB of
|
|
9
|
+
* every version ever published. At that price the check can run at every
|
|
10
|
+
* session start instead of being cached, which is the difference between
|
|
11
|
+
* Ashley getting a fix now and getting it tomorrow.
|
|
12
|
+
*/
|
|
13
|
+
const DEFAULT_REGISTRY = 'https://registry.npmjs.org';
|
|
14
|
+
/**
|
|
15
|
+
* Short on purpose. This runs at session start, in front of someone waiting to
|
|
16
|
+
* type, and a registry that is slow is indistinguishable from one that is down
|
|
17
|
+
* — in both cases the right answer is to stop waiting and carry on unupdated.
|
|
18
|
+
*/
|
|
19
|
+
const FETCH_TIMEOUT_MS = 3_000;
|
|
20
|
+
/**
|
|
21
|
+
* Compares two dotted numeric versions. Returns true when `candidate` is newer
|
|
22
|
+
* than `current`.
|
|
23
|
+
*
|
|
24
|
+
* Deliberately not a semver dependency: this package publishes plain
|
|
25
|
+
* `major.minor.patch` and the one judgement call — what to do with a
|
|
26
|
+
* prerelease suffix — is answered by ignoring it. A `0.8.0-beta.1` compares
|
|
27
|
+
* equal to `0.8.0`, so someone already on the beta is not told to "update" to
|
|
28
|
+
* the release they effectively have, and someone on `0.7.0` is offered it.
|
|
29
|
+
* Shipping a prerelease to `latest` is not something this project does.
|
|
30
|
+
*/
|
|
31
|
+
export function isNewer(candidate, current) {
|
|
32
|
+
const parts = (v) => v
|
|
33
|
+
.split('-')[0]
|
|
34
|
+
.split('.')
|
|
35
|
+
.map((n) => Number.parseInt(n, 10))
|
|
36
|
+
.map((n) => (Number.isNaN(n) ? 0 : n));
|
|
37
|
+
const a = parts(candidate);
|
|
38
|
+
const b = parts(current);
|
|
39
|
+
for (let i = 0; i < Math.max(a.length, b.length); i += 1) {
|
|
40
|
+
const left = a[i] ?? 0;
|
|
41
|
+
const right = b[i] ?? 0;
|
|
42
|
+
if (left !== right)
|
|
43
|
+
return left > right;
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
/** The registry's `latest` dist-tag, or null if it can't be reached in time. */
|
|
48
|
+
async function fetchLatest() {
|
|
49
|
+
const registry = process.env.SCRIBE_CLI_REGISTRY ?? DEFAULT_REGISTRY;
|
|
50
|
+
const url = `${registry}/-/package/${PACKAGE_NAME.replace('/', '%2F')}/dist-tags`;
|
|
51
|
+
const controller = new AbortController();
|
|
52
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
53
|
+
try {
|
|
54
|
+
const res = await fetch(url, { signal: controller.signal });
|
|
55
|
+
if (!res.ok)
|
|
56
|
+
return null;
|
|
57
|
+
const body = (await res.json());
|
|
58
|
+
return body.latest ?? null;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Offline, slow, rate-limited, or a registry that answered with something
|
|
62
|
+
// unexpected. All the same from here: no opinion about updates.
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
clearTimeout(timer);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Whether a newer CLI has been published.
|
|
71
|
+
*
|
|
72
|
+
* Runs on **every** session start, with no cache. An earlier version cached
|
|
73
|
+
* the answer for a day to keep a round trip off the front of each
|
|
74
|
+
* conversation; measuring it made that look like the wrong trade — 210ms
|
|
75
|
+
* against being up to a day late on a fix, plus a cache that silently
|
|
76
|
+
* swallowed the environment override this is tested with. Checking every time
|
|
77
|
+
* is both faster to the user and less code.
|
|
78
|
+
*/
|
|
79
|
+
export async function checkForUpdate(current) {
|
|
80
|
+
// Ahead of the fetch and never persisted. This is the knob the local test
|
|
81
|
+
// recipe turns — see TESTING-UPDATES.md.
|
|
82
|
+
const forced = process.env.SCRIBE_CLI_LATEST;
|
|
83
|
+
if (forced) {
|
|
84
|
+
return { current, latest: forced, updateAvailable: isNewer(forced, current), reachable: true };
|
|
85
|
+
}
|
|
86
|
+
const latest = await fetchLatest();
|
|
87
|
+
return {
|
|
88
|
+
current,
|
|
89
|
+
latest,
|
|
90
|
+
updateAvailable: latest !== null && isNewer(latest, current),
|
|
91
|
+
reachable: latest !== null,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* What `npm install -g` is pointed at.
|
|
96
|
+
*
|
|
97
|
+
* Overridable so the whole update path can be exercised against a local
|
|
98
|
+
* `npm pack` tarball before anything is published — see TESTING-UPDATES.md.
|
|
99
|
+
*/
|
|
100
|
+
export function installSpec() {
|
|
101
|
+
return process.env.SCRIBE_CLI_PACKAGE ?? `${PACKAGE_NAME}@latest`;
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=update-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-check.js","sourceRoot":"","sources":["../../src/lib/update-check.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: scribe-site-builder
|
|
3
|
-
description: Guides building a website on Scribe CMS end to end — designing the Astro site freely against Scribe as its backend, editing the `.scribe.yml` schema, adding one or many forms, the local dev loop, pushing content, and deploying — using the `scribe` CLI and the recipes in `recipes/`. Also the reference for working an existing Scribe site (adding fields/collections, publishing content, triaging form submissions, deleting a site). Use whenever the user wants to build/create/start a new website or site with Scribe, add a form to a site, work on an existing Scribe site, or asks about the `scribe` CLI.
|
|
3
|
+
description: Guides building a website on Scribe CMS end to end — designing the Astro site freely against Scribe as its backend, editing the `.scribe.yml` schema, adding one or many forms, the local dev loop, pushing content, and deploying — using the `scribe` CLI and the recipes in `recipes/`. Also the reference for working an existing Scribe site (adding fields/collections, publishing content, triaging form submissions and inquiries, renaming, archiving, restoring or deleting a site), for taking a site live on a custom domain — checking whether a client has a domain, buying one, DNS/CNAME setup, GitHub Pages domain config, HTTPS — for the queue of requests clients have sent the agency (triaging, claiming, replying, closing), and for the agency's prospect pipeline (logging calls, follow-ups, won/lost). Use whenever the user wants to build/create/start a new website or site with Scribe, add a form to a site, work on an existing Scribe site, publish/launch/go live with a site, point a site at a domain, set up or fix a custom domain or DNS for a Scribe site, work the inquiries a site has received, triage or reply to a client's request for work, log a call or follow-up with a prospect or client, or asks about the `scribe` CLI.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Building a site with Scribe
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
**This file and the `recipes/` beside it are the whole reference.** They ship
|
|
9
|
+
inside the `@scribe-platform/cli` npm package and are installed with it, so
|
|
10
|
+
everything needed to drive Scribe is here or in `scribe <command> --help`.
|
|
11
|
+
There is no repo to consult and no wiki to look up — if something seems to be
|
|
12
|
+
missing, it is a gap worth telling Dave about, not a file to go hunting for.
|
|
13
|
+
|
|
14
|
+
(It is generated from `docs/BUILDER-GUIDE.md` and `docs/AGENT-GUIDE.md` in the
|
|
15
|
+
Scribe repo. That matters only to whoever edits it there; those paths do not
|
|
16
|
+
exist on a machine that installed the CLI from npm.)
|
|
12
17
|
|
|
13
18
|
## Recipes — read the one you need
|
|
14
19
|
|
|
@@ -22,6 +27,9 @@ instead of reading all of them:
|
|
|
22
27
|
| [`recipes/collections.md`](./recipes/collections.md) | rendering editor-managed content |
|
|
23
28
|
| [`recipes/forms.md`](./recipes/forms.md) | adding one or more forms |
|
|
24
29
|
| [`recipes/deploy.md`](./recipes/deploy.md) | build/deploy setup or debugging |
|
|
30
|
+
| [`recipes/domains.md`](./recipes/domains.md) | anything about a domain — whether the client has one, getting them one, the DNS, taking a site live on it, fixing one |
|
|
31
|
+
| [`recipes/requests.md`](./recipes/requests.md) | working the queue of jobs clients have asked the agency for |
|
|
32
|
+
| [`recipes/prospects.md`](./recipes/prospects.md) | logging calls and follow-ups on prospects (not site-building) |
|
|
25
33
|
|
|
26
34
|
[`recipes/README.md`](./recipes/README.md) lists the five things that are a hard
|
|
27
35
|
contract (because Scribe writes into the repo) as opposed to advice.
|
|
@@ -32,6 +40,16 @@ contract (because Scribe writes into the repo) as opposed to advice.
|
|
|
32
40
|
repo. The admin app and the `scribe` CLI are both just clients that read/write
|
|
33
41
|
files in it via the GitHub API; a local `git clone` is a third client reading/writing
|
|
34
42
|
the same files via git.
|
|
43
|
+
- **Scribe owns the repo's GitHub Pages settings — never set them by hand.** The
|
|
44
|
+
custom domain, `public/CNAME`, HTTPS enforcement and the build's base path are
|
|
45
|
+
all written by `scribe site promote`, in one idempotent run. Do **not** reach for
|
|
46
|
+
`gh api .../pages`, the GitHub web UI, or a hand-written `public/CNAME`: setting
|
|
47
|
+
one without the others leaves the site 404ing, and the Pages call needs repo
|
|
48
|
+
admin that a site user doesn't have. The CLI doesn't need it — the admin app
|
|
49
|
+
makes that call with Scribe's own org token, so a write-scoped API key is enough.
|
|
50
|
+
If something about a domain looks like it needs a GitHub permission you lack,
|
|
51
|
+
that's the signal you're on the wrong path, not that you need Dave.
|
|
52
|
+
See [`recipes/domains.md`](./recipes/domains.md).
|
|
35
53
|
- **Form submissions are the exception — they live in MongoDB, not the repo** (RFC
|
|
36
54
|
0004). A Visitor's name, email and message used to be committed as a YAML file into
|
|
37
55
|
the site's public repo, which published them to the open internet. Nothing writes
|
|
@@ -60,41 +78,61 @@ contract (because Scribe writes into the repo) as opposed to advice.
|
|
|
60
78
|
|
|
61
79
|
## Setup (once)
|
|
62
80
|
|
|
63
|
-
- **CLI**: confirm `scribe --help` works. If it's not found
|
|
64
|
-
`
|
|
81
|
+
- **CLI**: confirm `scribe --help` works. If it's not found:
|
|
82
|
+
`npm install -g @scribe-platform/cli`. (Working inside a clone of the Scribe
|
|
83
|
+
repo itself? `npm link` from `packages/cli` instead — but that is a
|
|
84
|
+
maintainer's setup, not a user's.)
|
|
65
85
|
- **Login**: `scribe login --token <key>` — mint the key from the
|
|
66
|
-
admin UI (sign in → account menu → **API Tokens** → New token).
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
86
|
+
admin UI (sign in → account menu → **API Tokens** → New token). This is the
|
|
87
|
+
whole machine setup: it writes `~/.scribe/config.json`, installs this skill,
|
|
88
|
+
and turns on auto-updates. Defaults to the hosted admin at
|
|
89
|
+
`https://scribe.weaverdigital.io`; add `--api-url <url>` only to point at a
|
|
90
|
+
different instance (a local dev/Docker admin). Every other command reads that
|
|
91
|
+
config, no per-invocation `--token` flag. If a command fails with a raw `fetch failed`, that's not an API
|
|
71
92
|
error — it means `apiUrl` in that config doesn't point at anything listening;
|
|
72
93
|
re-run `scribe login` with the right URL.
|
|
73
94
|
|
|
74
95
|
## Keeping Scribe up to date
|
|
75
96
|
|
|
76
|
-
|
|
77
|
-
|
|
97
|
+
**This normally happens by itself and needs no attention.** `scribe login`
|
|
98
|
+
installs this skill and registers a Claude Code `SessionStart` hook that checks npm for
|
|
99
|
+
a newer `@scribe-platform/cli` at the start of **every** session and installs
|
|
100
|
+
it — CLI and this skill together — *before* the session's skills are scanned.
|
|
101
|
+
So a session either started on the current version or was brought to it before
|
|
102
|
+
anyone typed. That is the whole reason the check runs at session start rather
|
|
103
|
+
than mid-conversation: there is never a new skill sitting on disk that the
|
|
104
|
+
running session can't see.
|
|
105
|
+
|
|
106
|
+
If an update did install, the hook says so in this session's context. Don't
|
|
107
|
+
relay it; it's already done.
|
|
108
|
+
|
|
109
|
+
If the hook reports that an update **failed** to install, that is for Dave, not
|
|
110
|
+
for the user — it will be an npm permissions or registry problem. Say so plainly
|
|
111
|
+
and carry on; the old version still works.
|
|
112
|
+
|
|
113
|
+
`scribe update` also runs the whole thing by hand:
|
|
78
114
|
|
|
79
115
|
```
|
|
80
|
-
scribe update
|
|
116
|
+
scribe update # update the CLI + this skill now
|
|
117
|
+
scribe update --check # just report whether a newer version exists
|
|
118
|
+
scribe update --install-hook # repair the auto-update hook if it went missing
|
|
81
119
|
```
|
|
82
120
|
|
|
83
|
-
|
|
121
|
+
One caveat: this **only works for a global npm install** (`npm install -g
|
|
122
|
+
@scribe-platform/cli`). In a dev checkout of the scribe repo it refuses, and if
|
|
123
|
+
`~/.claude/skills/scribe-site-builder` is a symlink into a checkout it leaves it
|
|
124
|
+
alone — that's an author editing the skill in place.
|
|
84
125
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
for a restart.
|
|
90
|
-
- **It only works for a global npm install** (`npm install -g
|
|
91
|
-
@scribe-platform/cli`). In a dev checkout of the scribe repo it refuses, and if
|
|
92
|
-
`~/.claude/skills/scribe-site-builder` is a symlink into a checkout it leaves it
|
|
93
|
-
alone — that's an author editing the skill in place.
|
|
126
|
+
Run it by hand when the user says there's a new version, or when a CLI command
|
|
127
|
+
fails in a way that suggests this skill is behind the API (an option the CLI
|
|
128
|
+
doesn't recognise, a command that no longer exists) — the session-start check
|
|
129
|
+
runs before the session, so anything published since then is not here yet.
|
|
94
130
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
131
|
+
**If a hand-run update installs a new version mid-session**, tell the user in one
|
|
132
|
+
sentence, in their words: *"I've updated Scribe — close this chat and start a new
|
|
133
|
+
one so I pick up the new instructions."* Then stop relying on anything in this
|
|
134
|
+
file that the update might have changed; if you need the new text now, read
|
|
135
|
+
`~/.claude/skills/scribe-site-builder/SKILL.md` from disk.
|
|
98
136
|
|
|
99
137
|
## Workflow: building a new site
|
|
100
138
|
|
|
@@ -168,6 +206,11 @@ recognise, a command that no longer exists).
|
|
|
168
206
|
8. **Domain cutover — only when a customer's own hostname is ready.** Skip this
|
|
169
207
|
entirely for demo sites; the GitHub Pages default already works.
|
|
170
208
|
|
|
209
|
+
If it *isn't* ready — the client may not own a domain, may not know who holds
|
|
210
|
+
it, or may have mail on it — that part is the bulk of the work and is in
|
|
211
|
+
[`recipes/domains.md`](./recipes/domains.md) under "Before the cutover". The
|
|
212
|
+
client buys their own domain, at their own registrar, in their own name.
|
|
213
|
+
|
|
171
214
|
Check what DNS the domain needs first. This changes nothing:
|
|
172
215
|
```
|
|
173
216
|
scribe site promote my-site --domain example.com --dns-only
|
|
@@ -201,12 +244,22 @@ recognise, a command that no longer exists).
|
|
|
201
244
|
sync — fix it in the Cloudflare dashboard, or the contact form silently rejects
|
|
202
245
|
real visitors on that domain.
|
|
203
246
|
|
|
247
|
+
**You do not need GitHub repo admin rights for any of this.** The Pages API call
|
|
248
|
+
is made by the admin app using Scribe's org token, so a write-scoped API key is
|
|
249
|
+
enough. If you find yourself checking whether the user can administer the repo,
|
|
250
|
+
you've left the supported path.
|
|
251
|
+
|
|
204
252
|
`scribe site update my-site --domain example.com` only edits the stored record
|
|
205
|
-
and the Turnstile widget. Use it to correct a domain, never to cut a site over
|
|
253
|
+
and the Turnstile widget. Use it to correct a domain, never to cut a site over —
|
|
254
|
+
the CLI now refuses that and points at `promote`. Deeper reference, including how
|
|
255
|
+
to repair a half-finished cutover: [`recipes/domains.md`](./recipes/domains.md).
|
|
206
256
|
|
|
207
257
|
## Working an existing site
|
|
208
258
|
|
|
209
259
|
```
|
|
260
|
+
scribe site promote <site> --domain <domain> [--dns-only] # take it live on a real domain
|
|
261
|
+
scribe site update <site> --turnstile-key <key> # correct stored settings only
|
|
262
|
+
|
|
210
263
|
scribe content list <site> [--collection <name>] [--json]
|
|
211
264
|
scribe content read <site> <path> [--json]
|
|
212
265
|
scribe content publish <site> <file> # validates against schema
|
|
@@ -223,8 +276,19 @@ scribe submission list <site> <collection> [--status <status>] [--page <n>] [--j
|
|
|
223
276
|
scribe submission update <site> <collection> <id> [--status <s>] [--note <n>]
|
|
224
277
|
scribe submission export <site> <collection> [--format csv|json] [--status <s>] [--out <file>]
|
|
225
278
|
scribe submission delete <site> <collection> <id> [--yes] # staff only
|
|
279
|
+
|
|
280
|
+
scribe request list [--site <slug>] [--status <s>] [--type <t>] [--mine|--unassigned] [--all] [--page <n>]
|
|
281
|
+
scribe request show <id> # the ask + the whole thread, internal notes included
|
|
282
|
+
scribe request claim <id> [--release]
|
|
283
|
+
scribe request reply <id> --message <text> [--internal]
|
|
284
|
+
scribe request status <id> <open|in-progress|waiting-on-you|done|cancelled>
|
|
226
285
|
```
|
|
227
286
|
|
|
287
|
+
- **"Publish/launch the site at example.com" means `scribe site promote`** — the full
|
|
288
|
+
domain cutover in one command — step 8 of the build workflow above, and in full in
|
|
289
|
+
[`recipes/domains.md`](./recipes/domains.md). It is not `content publish`, and
|
|
290
|
+
it is not `site update --domain` (which only rewrites the stored record and would
|
|
291
|
+
leave the site 404ing on the new host — the CLI refuses that now and tells you so).
|
|
228
292
|
- `scribe schema apply` is the way to add a form (or any collection with fields): it
|
|
229
293
|
takes a YAML/JSON file describing **one whole collection**, upserts it by `name`,
|
|
230
294
|
and validates through the admin's own parser first — so a reserved `status` field,
|
|
@@ -254,6 +318,53 @@ scribe submission delete <site> <collection> <id> [--yes] # staff only
|
|
|
254
318
|
**staff** role — a site user's API key gets a 403. It prompts for the id unless
|
|
255
319
|
`--yes` is passed. There is deliberately no equivalent in the admin UI, so don't go
|
|
256
320
|
looking for a delete button on the inbox.
|
|
321
|
+
- **`scribe request` is the other queue, and the opposite direction.** A submission is
|
|
322
|
+
a Visitor contacting the *client*; a Request is the **client contacting the agency**
|
|
323
|
+
to ask for work. One cross-site queue, staff-only, nothing in any repo. Three things
|
|
324
|
+
to know before touching it, with the rest in
|
|
325
|
+
[`recipes/requests.md`](./recipes/requests.md):
|
|
326
|
+
- **`waiting-on-you` means waiting on the _client_** — every status is phrased for
|
|
327
|
+
the Client's own dashboard. Read as an instruction to the agency it means the
|
|
328
|
+
opposite of what it says.
|
|
329
|
+
- **A visible `reply` is an email to the client**, sent immediately and permanently.
|
|
330
|
+
Show the user the text before sending unless they've said not to bother.
|
|
331
|
+
`--internal` is the Agency-only note: no email, invisible to the client, moves no
|
|
332
|
+
status.
|
|
333
|
+
- **There is no `create`.** A Request comes from the Client; filing one for them
|
|
334
|
+
would put words in their mouth. Say so rather than hunting for a flag.
|
|
335
|
+
|
|
336
|
+
## A site's two names, and the jobs only the admin UI can do
|
|
337
|
+
|
|
338
|
+
A site has a **slug** and a **display name**, and only one of them moves.
|
|
339
|
+
|
|
340
|
+
- The **slug** (`riverside-dental`) is the site's identity: the repo name, the
|
|
341
|
+
Pages URL, `?site=` on the submissions endpoint, and the argument to every
|
|
342
|
+
`scribe` command. **There is no rename** — not in the CLI, not in the admin.
|
|
343
|
+
Moving a site to a different slug means provisioning a new site. So when
|
|
344
|
+
someone is choosing a slug, it is worth one sentence of care up front.
|
|
345
|
+
- The **display name** is a label, and is editable any time: in the Staff console,
|
|
346
|
+
**Sites → the site → the Name field** at the top of the Details card, saved on
|
|
347
|
+
blur. It is allowed on archived sites too. `scribe site add --name` sets it at
|
|
348
|
+
creation; nothing in the CLI changes it afterwards.
|
|
349
|
+
|
|
350
|
+
A rename reaches the consoles and notification subjects and **nothing a Visitor
|
|
351
|
+
sees** — not the URL, and not `.scribe.yml`'s `siteName`, which is the website's
|
|
352
|
+
own copy of its name in a repo the rename doesn't write to. If the name is wrong
|
|
353
|
+
on the published pages too, that's a separate content edit in the repo.
|
|
354
|
+
|
|
355
|
+
Three other things live only in the Staff console, so route the user there rather
|
|
356
|
+
than hunting for a flag:
|
|
357
|
+
|
|
358
|
+
| Job | Where |
|
|
359
|
+
|---|---|
|
|
360
|
+
| Rename (display name) | Sites → the site → Name |
|
|
361
|
+
| Attach a site to a Client, or detach it | Sites → the site → **Client** |
|
|
362
|
+
| Star a site (sorts it to the top of the Sites list, and is what the staff Home page's Sites card shows) | Sites → the star |
|
|
363
|
+
|
|
364
|
+
And one that isn't available at all: **creating a login for a client's own
|
|
365
|
+
people**. There is no `scribe` command for it and no self-serve invite — user
|
|
366
|
+
management is super-admin only. "Give the client access to edit their content" is
|
|
367
|
+
a request to hand to Dave, with the site slug and who needs the login.
|
|
257
368
|
|
|
258
369
|
## Images — never hotlink, always commit the file
|
|
259
370
|
|
|
@@ -291,29 +402,62 @@ placeholder / link out to somewhere else on purpose. Don't default to that — t
|
|
|
291
402
|
"add a photo of X" as "go get a real image and commit it," not "find any URL that
|
|
292
403
|
renders."
|
|
293
404
|
|
|
294
|
-
##
|
|
405
|
+
## Taking a site down: archive, restore, delete
|
|
295
406
|
|
|
296
407
|
```
|
|
297
|
-
scribe site
|
|
408
|
+
scribe site archive <slug> # offline + read-only; deletes nothing
|
|
409
|
+
scribe site restore <slug> # back online, exactly as it was
|
|
410
|
+
scribe site list --archived # the archived ones (plain `list` hides them)
|
|
411
|
+
scribe site delete <slug> [--yes] # permanent; only works on an archived site
|
|
298
412
|
```
|
|
299
413
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
414
|
+
**Archive is how a site comes down** (RFC 0026). It turns GitHub Pages off, so the
|
|
415
|
+
website stops loading. It archives the repo on GitHub, so it is read-only but still
|
|
416
|
+
there. It stops the site's forms accepting submissions and removes the site from
|
|
417
|
+
every list people work from. **Nothing is deleted**: content, schema, submissions and
|
|
418
|
+
their history, requests, metrics and who had access all stay. `scribe site restore`
|
|
419
|
+
brings it back, turning Pages on and starting a deploy, so the site is live again
|
|
420
|
+
in a minute or two. Both need a read-write key belonging to Agency staff. A site
|
|
421
|
+
user's key is refused, even for their own site.
|
|
422
|
+
|
|
423
|
+
**When someone asks you to delete, remove or take down a site, archive it.** It
|
|
424
|
+
can be undone, so this is an ordinary request for you to carry out, not one to hand
|
|
425
|
+
back:
|
|
426
|
+
|
|
427
|
+
1. Run `scribe site list` and name the exact slug and its live URL back to them,
|
|
428
|
+
along with anything that sounds like a sibling they want kept (the `-v2` they're
|
|
429
|
+
removing versus the `v1` that's live).
|
|
430
|
+
2. Tell them what archiving does: the site goes offline and nothing is deleted. It
|
|
431
|
+
only touches that one site, not Scribe, their other sites or their account.
|
|
432
|
+
3. When they confirm the slug, run `scribe site archive <slug>` and report the output,
|
|
433
|
+
including how to bring it back.
|
|
434
|
+
|
|
435
|
+
`scribe site delete` is a different thing, and it is permanent. It deletes the GitHub
|
|
436
|
+
repo (Astro source, schema, content) and every record Scribe holds for the site,
|
|
437
|
+
including its submissions and their history. There is no undo. It refuses a live site
|
|
438
|
+
with a 409 telling you to archive first, so a single command can never destroy a live
|
|
439
|
+
site. Only run it when the person has explicitly asked for permanent deletion of a
|
|
440
|
+
site that is already archived. "Delete" on its own almost always means "take it
|
|
441
|
+
down", which is what archive does. Without `--yes` it prompts for the slug to be
|
|
442
|
+
retyped. From a shell with no terminal (yours) it exits instead of waiting, so after
|
|
443
|
+
that explicit request it's `--yes`. It needs an **unrestricted** API key (no
|
|
444
|
+
`siteSlugs` restriction), the same rule as `site add`. In the admin, archiving is
|
|
445
|
+
under the site's **Settings → Danger zone**. Restore and **Delete permanently** are on
|
|
446
|
+
the archived site's page in the Staff console: **Sites → Archived**.
|
|
447
|
+
|
|
448
|
+
An archived site's other commands (`content`, `schema`, `submission`, `build`, …)
|
|
449
|
+
return a 409 saying it's archived, because its repo is read-only. Restore it first.
|
|
450
|
+
Its slug stays taken while it's archived, so `site add` with the same slug says so.
|
|
451
|
+
|
|
452
|
+
Archiving and deleting both drop the site's domain from the Turnstile widget, but
|
|
453
|
+
only if no other *live* site still uses it, so removing one demo never breaks the
|
|
454
|
+
contact form on the rest. Restoring adds it back. A `Warning:` in the output means
|
|
455
|
+
that cleanup didn't happen. It's harmless, but tidy it up in Cloudflare.
|
|
456
|
+
|
|
457
|
+
Side effect of the permanent delete worth knowing: if an API key's `siteSlugs` was
|
|
458
|
+
only that one site, deleting it leaves that key with an empty `siteSlugs` array,
|
|
459
|
+
which the API treats as **unrestricted** (every site), not inert. Archiving doesn't
|
|
460
|
+
touch keys at all.
|
|
317
461
|
|
|
318
462
|
## `.scribe.yml` field reference
|
|
319
463
|
|
|
@@ -350,10 +494,21 @@ On a **form collection** specifically, since RFC 0004 moved submissions into Mon
|
|
|
350
494
|
- **An `image` field is rejected** on a form collection. The old path uploaded a
|
|
351
495
|
Visitor's file straight into the public repo, and a Mongo document is the wrong place
|
|
352
496
|
for a blob. Deferred to RFC 0007 (object storage).
|
|
353
|
-
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
497
|
+
- **New submissions are emailed** (RFC 0009, on RFC 0021's mailer), and
|
|
498
|
+
`notify:` is no longer how a *person* subscribes. Who gets one:
|
|
499
|
+
- **Site users on that Site are subscribed by default** — a lead nobody hears
|
|
500
|
+
about is the bug this closes, so the people whose business it is are opted in
|
|
501
|
+
unless they opt out.
|
|
502
|
+
- **Agency admins are not**, because they can reach every Site. They opt in per
|
|
503
|
+
Site per collection, under **Your Profile → Email notifications** in the admin.
|
|
504
|
+
- **`notify: [...]` now means addresses with no Scribe account** — a shared
|
|
505
|
+
`office@` inbox. Don't put a Scribe user's address in it; they manage their own.
|
|
506
|
+
|
|
507
|
+
The mail is best-effort and never fails the Visitor's POST, and `Reply-To` is
|
|
508
|
+
the Visitor's own address when the form has a field named `email` — that exact
|
|
509
|
+
name is the only signal, so a form whose address field is called something else
|
|
510
|
+
gets no `Reply-To`. So a client *can* be promised email on a new submission. The
|
|
511
|
+
inbox and its `needs-you` badge remain the thing of record.
|
|
357
512
|
|
|
358
513
|
Per field: `name`, `type`, `label`, `required`, `default`, `pattern`, `hidden`,
|
|
359
514
|
`readonly`, `isBody` (this field is the markdown body, not frontmatter), `list`
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# Recipes
|
|
2
2
|
|
|
3
|
-
Patterns for building an Astro site against Scribe as the backend. Per
|
|
4
|
-
|
|
5
|
-
**advice, not enforcement** — read the one you need, adapt it to the site you're
|
|
3
|
+
Patterns for building an Astro site against Scribe as the backend. Per RFC
|
|
4
|
+
0010 these are **advice, not enforcement** — read the one you need, adapt it to the site you're
|
|
6
5
|
building, and ignore the rest. There is no template to conform to and no
|
|
7
6
|
generated config to keep in sync.
|
|
8
7
|
|
|
@@ -12,6 +11,14 @@ generated config to keep in sync.
|
|
|
12
11
|
| [`collections.md`](./collections.md) | rendering content (posts, products, events) that editors manage in Scribe |
|
|
13
12
|
| [`forms.md`](./forms.md) | putting one or more forms on the Site |
|
|
14
13
|
| [`deploy.md`](./deploy.md) | setting up or debugging the build and deploy |
|
|
14
|
+
| [`domains.md`](./domains.md) | finding out whether a client has a domain, getting them one, the DNS, taking a Site live on it, or repairing one |
|
|
15
|
+
| [`requests.md`](./requests.md) | working the queue of jobs clients have asked the agency for |
|
|
16
|
+
| [`prospects.md`](./prospects.md) | recording and nurturing prospects in the calling pipeline |
|
|
17
|
+
|
|
18
|
+
`requests.md` and `prospects.md` are the odd ones out — they're about the
|
|
19
|
+
agency's own work (its queue and its sales pipeline) rather than anything in a
|
|
20
|
+
Site's repo, and they live here because the `scribe` CLI is the interface for
|
|
21
|
+
all of it.
|
|
15
22
|
|
|
16
23
|
## The part that isn't advice
|
|
17
24
|
|