@aotter/mantle 0.0.11-alpha.35 → 0.0.11-alpha.37
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 +40 -1
- package/docs/adr/0013-agent-provisioned-consumer-projects.md +8 -1
- package/docs/adr/0014-auth-better-auth-and-multi-tenant-mcp.md +39 -0
- package/docs/adr/0016-site-semantic-layer.md +11 -7
- package/docs/adr/README.md +2 -2
- package/docs/auth-hosting-model.md +139 -0
- package/docs/media-uploads.md +179 -0
- package/docs/release-process.md +14 -16
- package/package.json +7 -7
- package/skills/README.md +23 -3
- package/skills/develop/SKILL.md +72 -0
- package/skills/install/SKILL.md +24 -8
- package/skills/plugin/SKILL.md +120 -0
- package/skills/provision/SKILL.md +12 -0
- package/skills/theme/SKILL.md +41 -0
- package/skills/update/SKILL.md +42 -0
package/README.md
CHANGED
|
@@ -38,7 +38,41 @@ deployable Mantle site, and connects the first Cloudflare deploy. Then
|
|
|
38
38
|
point your coding agent (Claude Code / Cursor / Codex) at the provisioned
|
|
39
39
|
repo to continue.
|
|
40
40
|
|
|
41
|
-
Starter source and provision bundles live in [`aotter/mantle-starters`](https://github.com/aotter/mantle-starters); launch starts from the blank base and the agent applies the selected type as a post-launch overlay. See `skills/install` in the [Mantle repo](https://github.com/aotter/mantle/tree/develop/skills/install) for how the agent continues a provisioned repo.
|
|
41
|
+
Starter source and provision bundles live in [`aotter/mantle-starters`](https://github.com/aotter/mantle-starters); launch starts from the blank base and the agent applies the selected type as a post-launch overlay. Generated repos vendor Core-owned `mantle:*` skills from this package for repo-local use. See `skills/install` in the [Mantle repo](https://github.com/aotter/mantle/tree/develop/skills/install) for how the agent continues a provisioned repo.
|
|
42
|
+
|
|
43
|
+
## Agent marketplace install
|
|
44
|
+
|
|
45
|
+
Install the Mantle Core skill bundle before working on generated repos:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Claude Code
|
|
49
|
+
/plugin marketplace add aotter/mantle
|
|
50
|
+
/plugin install mantle
|
|
51
|
+
|
|
52
|
+
# Codex
|
|
53
|
+
codex plugin marketplace add aotter/mantle --ref develop
|
|
54
|
+
codex plugin add mantle@mantle
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Cursor and VS Code Copilot can auto-discover the GitHub repo through
|
|
58
|
+
`.cursor-plugin/plugin.json` and `.copilot-plugin/plugin.json` after the repo
|
|
59
|
+
is cloned or opened.
|
|
60
|
+
|
|
61
|
+
## Marketplace capability installs
|
|
62
|
+
|
|
63
|
+
In a generated repo, tell your coding agent:
|
|
64
|
+
|
|
65
|
+
```txt
|
|
66
|
+
Use repo-local mantle:plugin to install <plugin slug or recipe URL> in this repo.
|
|
67
|
+
Use repo-local mantle:plugin to update <plugin id> in this repo.
|
|
68
|
+
Use repo-local mantle:plugin to remove <plugin id> from this repo.
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Mantle marketplace entries are agent-installable recipes. They declare the
|
|
72
|
+
plugin source, Mantle version range, files/atoms/routes/tools, adapter
|
|
73
|
+
requirements, secrets, and checks. The agent applies the recipe through the
|
|
74
|
+
Core-owned `mantle:plugin` skill and records it in `.mantle/plugins.json` plus
|
|
75
|
+
`.mantle/plugins.lock.json`. There is no `mantle plugin add` CLI yet.
|
|
42
76
|
|
|
43
77
|
## Adapter targets
|
|
44
78
|
|
|
@@ -54,7 +88,12 @@ The `mantle-runtime` package never imports Cloudflare-specific types — adapter
|
|
|
54
88
|
- Embedded docs and agent skills ship inside this npm package for
|
|
55
89
|
generated-site agents:
|
|
56
90
|
- `node_modules/@aotter/mantle/docs/design-atoms.md`
|
|
91
|
+
- `node_modules/@aotter/mantle/docs/media-uploads.md` (Cloudflare R2 adapter recipe)
|
|
57
92
|
- `node_modules/@aotter/mantle/docs/adr/`
|
|
93
|
+
- `node_modules/@aotter/mantle/skills/develop/SKILL.md`
|
|
94
|
+
- `node_modules/@aotter/mantle/skills/plugin/SKILL.md`
|
|
95
|
+
- `node_modules/@aotter/mantle/skills/theme/SKILL.md`
|
|
96
|
+
- `node_modules/@aotter/mantle/skills/update/SKILL.md`
|
|
58
97
|
- `node_modules/@aotter/mantle/skills/install/SKILL.md`
|
|
59
98
|
- `node_modules/@aotter/mantle/skills/provision/SKILL.md`
|
|
60
99
|
- [Repo](https://github.com/aotter/mantle)
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
## Status
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Superseded for first launch by provisioning v2 (2026-06-27). Historical
|
|
6
|
+
record only.
|
|
6
7
|
|
|
7
8
|
## Date
|
|
8
9
|
|
|
@@ -10,6 +11,12 @@ Accepted (new)
|
|
|
10
11
|
|
|
11
12
|
## Context
|
|
12
13
|
|
|
14
|
+
> **Current direction:** Mantle landing creates the GitHub repo from
|
|
15
|
+
> `mantle-starters` `provision-bundles/<type>.json`, commits the blank
|
|
16
|
+
> or type bundle, and connects Cloudflare Workers CI when possible.
|
|
17
|
+
> Do not extend the old `create-mantle launch --session` or
|
|
18
|
+
> `provision:up` path.
|
|
19
|
+
|
|
13
20
|
mantle is not optimized for a human developer reading a long
|
|
14
21
|
installation guide and hand-editing boilerplate. The intended v0.1.0
|
|
15
22
|
entry path is:
|
|
@@ -419,3 +419,42 @@ The original ADR-0014 §"Auth as contract, Better Auth as default" framing stays
|
|
|
419
419
|
- A `@cloudflare/vitest-pool-workers`-based integration test covering the full OAuth flow (DCR → consent → token → MCP RPC). Node-vitest can't load `@cloudflare/workers-oauth-provider` because it imports from `cloudflare:workers`.
|
|
420
420
|
- Starters (`aotter/mantle-starters`) migration to the same top-level OAuthProvider shape. All 8 archetypes currently use the pre-carve-out `mountMcp` API and need updating before the next starter tag.
|
|
421
421
|
- Track whether Anthropic relaxes (1) the `/mcp` resource-path-prefix requirement and (2) the no-colon-in-scope requirement. Both are de-facto MCP client behaviors, not RFC requirements; if upstream relaxes them, the SDK can re-introduce `mcp:read` / `mcp:staff` scopes for finer-grained delegation.
|
|
422
|
+
|
|
423
|
+
## Amendment — 2026-06-30: Hosted-auth boundary and first-party cookie fields
|
|
424
|
+
|
|
425
|
+
Mantle Platform introduces a first-party hosted-auth use case:
|
|
426
|
+
`platform.mantle.tools` owns site-owner account, billing, hosted
|
|
427
|
+
identity, and platform email, while generated sites still own local
|
|
428
|
+
grants, member records, content, commerce, forms, and legal copy. The
|
|
429
|
+
product boundary is documented in
|
|
430
|
+
[`docs/auth-hosting-model.md`](../auth-hosting-model.md).
|
|
431
|
+
|
|
432
|
+
This does not change ADR-0014's core rule: Mantle does not expose an
|
|
433
|
+
un-curated `betterAuthOptions` or `advanced` passthrough. Missing Better
|
|
434
|
+
Auth knobs become first-class SDK fields only when a real Mantle use
|
|
435
|
+
case needs them.
|
|
436
|
+
|
|
437
|
+
The first-party SSO use case needs exactly three Better Auth fields:
|
|
438
|
+
|
|
439
|
+
- `trustedOrigins` — app-owned origins that Better Auth should trust for
|
|
440
|
+
auth flows. SDK-managed provider origins, such as Apple, are still
|
|
441
|
+
injected automatically.
|
|
442
|
+
- `crossSubDomainCookies` — Better Auth's same-parent-domain cookie
|
|
443
|
+
support for a trusted first-party app family.
|
|
444
|
+
- `cookiePrefix` — required when two Better Auth apps can write cookies
|
|
445
|
+
under the same parent domain, so Platform cookies do not collide with
|
|
446
|
+
Landing or generated-site cookies.
|
|
447
|
+
|
|
448
|
+
These fields are additive. Existing consumers that do not pass them keep
|
|
449
|
+
the previous cookie/session behavior.
|
|
450
|
+
|
|
451
|
+
The boundary is deliberately narrower than "hosted auth everywhere":
|
|
452
|
+
|
|
453
|
+
- Free users can still self-host every login method Mantle exposes
|
|
454
|
+
through `createAuth()`.
|
|
455
|
+
- Same-parent-domain SSO can use `crossSubDomainCookies` when the same
|
|
456
|
+
party controls all participating subdomains.
|
|
457
|
+
- Customer-owned domains cannot receive `mantle.tools` cookies. Paid
|
|
458
|
+
hosted auth for `customer.com` must use an OAuth/OIDC broker flow:
|
|
459
|
+
Platform authenticates and returns identity; the customer site creates
|
|
460
|
+
its own local session and maps identity into local grants.
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
## Status
|
|
4
4
|
|
|
5
5
|
Accepted (slimmed 2026-05-12 per Epic #116; `mantle/site.md` letter
|
|
6
|
-
surface suspended 2026-06-19 and removed from first-run scaffolds
|
|
6
|
+
surface suspended 2026-06-19 and removed from first-run scaffolds;
|
|
7
|
+
amended 2026-06-27 for landing provision bundles).
|
|
7
8
|
|
|
8
9
|
## Decision
|
|
9
10
|
|
|
@@ -27,7 +28,8 @@ from deterministic scaffold state.
|
|
|
27
28
|
|
|
28
29
|
## Placeholder macros
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
Mantle landing substitutes these across provision-bundle `*.template`
|
|
32
|
+
files in a single pass:
|
|
31
33
|
|
|
32
34
|
| Macro | Source | Example |
|
|
33
35
|
|---|---|---|
|
|
@@ -41,7 +43,8 @@ from deterministic scaffold state.
|
|
|
41
43
|
| `{{INSTALL_TIMESTAMP}}` | ISO 8601 of install run | `2026-05-12T14:03:00Z` |
|
|
42
44
|
| `{{INSTALL_SUMMARY}}` | CLI flag | `bootstrapped publication site for Lab Cafe in zh-TW/en` |
|
|
43
45
|
|
|
44
|
-
New macros must be added here, to `
|
|
46
|
+
New macros must be added here, to `mantle-starters` bundle templates,
|
|
47
|
+
and to the landing substitution pass.
|
|
45
48
|
|
|
46
49
|
## Update rules
|
|
47
50
|
|
|
@@ -58,8 +61,9 @@ New macros must be added here, to `_common/*.template`, and to the substitution
|
|
|
58
61
|
|
|
59
62
|
## Implementation
|
|
60
63
|
|
|
61
|
-
- Templates: `mantle-starters/
|
|
62
|
-
|
|
63
|
-
-
|
|
64
|
+
- Templates: `mantle-starters/blank/AGENTS.md.template` and
|
|
65
|
+
generated `.mantle/*.template` bundle files.
|
|
66
|
+
- Substitution: Mantle landing provision-bundle substitution.
|
|
67
|
+
- Install handoff: `skills/install/SKILL.md` describes how agents continue from the landing-provisioned repo.
|
|
64
68
|
- Provision update: `skills/provision/SKILL.md` describes the `AGENTS.md` public-site rewrite after deploy.
|
|
65
|
-
-
|
|
69
|
+
- Type overlays are applied while building `provision-bundles/<type>.json`; generated repos should not need a second overlay step.
|
package/docs/adr/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Records of *why* mantle ended up shaped this way. The numbering preserves POC AD
|
|
|
14
14
|
| [0010](0010-locale-and-translates.md) | Locale 3-layer (manifest / D1 site_config / data field) + translates pattern. Boot decoupled from `site_config` (issue #60 fix). | Accepted (refreshed) |
|
|
15
15
|
| [0011](0011-adapter-port-spec.md) | Adapter port spec. Required runtime ports plus optional feature ports. CF impl + Netlify stub. | Accepted (new) |
|
|
16
16
|
| [0012](0012-views-as-public-rest.md) | Views auto-expose `GET /api/views/<name>` as the public REST read surface. Schemas never get a public REST endpoint. Filter comparison values accept `{ $param: <name> }`; `?page=&show=` reserved for pagination. | Accepted (new) |
|
|
17
|
-
| [0013](0013-agent-provisioned-consumer-projects.md) |
|
|
17
|
+
| [0013](0013-agent-provisioned-consumer-projects.md) | Historical agent-provisioned consumer projects path. Superseded for first launch by landing provision bundles. | Superseded |
|
|
18
18
|
| [0014](0014-auth-better-auth-and-multi-tenant-mcp.md) | Better Auth for staff sign-in (D1 session); the MCP OAuth surface carves out to `@cloudflare/workers-oauth-provider` (KV grant store) at top level. The two meet at `/oauth/authorize` where the consent handler reads the Better Auth session. MCP splits into `/mcp/staff` (write, admin-role) and `/mcp` (read, any signed-in). Scope advertised as `["mcp"]` (single non-colon) because claude.ai rejects colon-shaped scopes. Auth port disappears; runtime takes Better Auth instance directly. See § "Amendment 2026-05-15". | Accepted + amended |
|
|
19
19
|
| [0016](0016-site-semantic-layer.md) | Site semantic layer: `AGENTS.md` (cross-tool entry) + `.mantle/launch-state.json` (deterministic install context). The older `mantle/site.md` letter surface is suspended from first-run scaffolds. | Accepted (slimmed) |
|
|
20
20
|
|
|
@@ -27,7 +27,7 @@ If you're new to the codebase:
|
|
|
27
27
|
3. **0007** — what running the SDK feels like as an AI author (and as the operator agent).
|
|
28
28
|
4. **0011** — the boundary between the runtime and the adapter (most load-bearing for the rebuild).
|
|
29
29
|
5. **0010** — how locale flows through the system.
|
|
30
|
-
6. **0013** —
|
|
30
|
+
6. **0013** — historical install-session context; current first launch is landing provision bundles plus repo-local handoff.
|
|
31
31
|
7. **0002, 0008** — the two ADRs that touch every diagnostic and every binding.
|
|
32
32
|
|
|
33
33
|
## What's NOT here (and why)
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# Auth Hosting Model
|
|
2
|
+
|
|
3
|
+
Mantle treats authentication as a runtime capability, not a paywall on
|
|
4
|
+
basic login. The split is:
|
|
5
|
+
|
|
6
|
+
- **Mantle SDK** gives every generated site the primitives needed to
|
|
7
|
+
run its own auth.
|
|
8
|
+
- **Mantle starters** decide whether a generated site is wired for
|
|
9
|
+
self-hosted auth or as a client of Mantle Platform hosted auth.
|
|
10
|
+
- **Mantle Platform** can sell hosted identity, provider setup, email,
|
|
11
|
+
and billing convenience for site owners who do not want to operate
|
|
12
|
+
those pieces.
|
|
13
|
+
|
|
14
|
+
This document is the source of truth for the product and SDK boundary.
|
|
15
|
+
Do not duplicate this model in starter handoff copy or landing prose;
|
|
16
|
+
link here and keep downstream docs short.
|
|
17
|
+
|
|
18
|
+
## Free Self-Hosted Auth
|
|
19
|
+
|
|
20
|
+
A free Mantle site can run every login method the SDK exposes through
|
|
21
|
+
`createAuth()`:
|
|
22
|
+
|
|
23
|
+
- social OAuth providers supported by Better Auth;
|
|
24
|
+
- generic provider-specific options through the `social.extras` shape;
|
|
25
|
+
- email OTP;
|
|
26
|
+
- magic link;
|
|
27
|
+
- passkey or additional Better Auth method support when the SDK adds a
|
|
28
|
+
curated first-class field for it;
|
|
29
|
+
- first-party same-domain or same-parent-domain SSO when the site owner
|
|
30
|
+
controls every participating subdomain.
|
|
31
|
+
|
|
32
|
+
The site owner supplies their own provider client ids/secrets,
|
|
33
|
+
transactional email sender, DNS, cookie policy, and operational
|
|
34
|
+
maintenance. Mantle must not block self-hosted login behind a paid
|
|
35
|
+
feature flag.
|
|
36
|
+
|
|
37
|
+
## Hosted Platform Auth
|
|
38
|
+
|
|
39
|
+
Paid hosted auth is a convenience and operations product. The value is
|
|
40
|
+
that Mantle Platform can:
|
|
41
|
+
|
|
42
|
+
- hold OAuth provider configuration;
|
|
43
|
+
- hold email provider credentials;
|
|
44
|
+
- send OTP and magic-link email;
|
|
45
|
+
- manage deliverability and provider rotation;
|
|
46
|
+
- register generated sites as clients;
|
|
47
|
+
- check billing/license entitlements before hosted capabilities are
|
|
48
|
+
enabled;
|
|
49
|
+
- expose a stable hosted identity broker for generated sites.
|
|
50
|
+
|
|
51
|
+
The generated site still owns local grants, member records, content,
|
|
52
|
+
orders, forms, and legal copy. Platform returns identity. The site maps
|
|
53
|
+
that identity into `ctx.user`, `ctx.staff`, and its own grant model.
|
|
54
|
+
|
|
55
|
+
## Same Parent Domain SSO
|
|
56
|
+
|
|
57
|
+
When the apps share a parent domain that the same party controls, SSO
|
|
58
|
+
can use browser cookies:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
createAuth({
|
|
62
|
+
// ...
|
|
63
|
+
trustedOrigins: ["https://mantle.tools", "https://www.mantle.tools"],
|
|
64
|
+
cookiePrefix: "mantle-platform",
|
|
65
|
+
crossSubDomainCookies: {
|
|
66
|
+
enabled: true,
|
|
67
|
+
domain: "mantle.tools",
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Use this only for a trusted first-party app family, for example
|
|
73
|
+
`platform.mantle.tools` and `mantle.tools`.
|
|
74
|
+
|
|
75
|
+
`cookiePrefix` is required whenever more than one Better Auth app can
|
|
76
|
+
write cookies under the same parent domain. Without it, Platform and
|
|
77
|
+
Landing can overwrite each other's default Better Auth cookie names.
|
|
78
|
+
|
|
79
|
+
`trustedOrigins` is a Better Auth auth-flow trust list. It is not a
|
|
80
|
+
general API CORS policy and must not be documented as one.
|
|
81
|
+
|
|
82
|
+
## Customer Domain SSO
|
|
83
|
+
|
|
84
|
+
Shared cookies do not work across different registrable domains:
|
|
85
|
+
|
|
86
|
+
```text
|
|
87
|
+
platform.mantle.tools -> customer.com
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Browsers will not send a `mantle.tools` cookie to `customer.com`. For a
|
|
91
|
+
customer-owned domain, hosted auth must use OAuth/OIDC:
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
customer.com/login
|
|
95
|
+
-> platform.mantle.tools/oauth/authorize
|
|
96
|
+
-> user signs in with Platform-supported methods
|
|
97
|
+
-> customer.com/api/auth/callback/mantle
|
|
98
|
+
-> customer.com verifies the authorization response
|
|
99
|
+
-> customer.com creates its own local session
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
That local session is the customer's site session. Platform remains the
|
|
103
|
+
identity broker and entitlement authority; it does not become the
|
|
104
|
+
customer site's member database or authorization system.
|
|
105
|
+
|
|
106
|
+
## Landing Provisioning Split
|
|
107
|
+
|
|
108
|
+
Landing should branch before GitHub or Cloudflare side effects:
|
|
109
|
+
|
|
110
|
+
- **Continue free**: commit a self-hosted auth-ready site with docs,
|
|
111
|
+
env placeholders, and the user's own provider setup path.
|
|
112
|
+
- **Use hosted auth**: check Platform entitlement, register the site as
|
|
113
|
+
a Platform auth client, and commit the generated site's Platform auth
|
|
114
|
+
client config.
|
|
115
|
+
|
|
116
|
+
Landing can probe Platform staff/session state, but provisioning's
|
|
117
|
+
GitHub OAuth token is still Landing-owned unless a separate token
|
|
118
|
+
handoff design is introduced.
|
|
119
|
+
|
|
120
|
+
The generated site's hosted-auth client code belongs in the starter or
|
|
121
|
+
a starter overlay. Core owns only the auth contract, manifest/runtime
|
|
122
|
+
identity vocabulary (`ctx.user`, `ctx.staff`), and curated Better Auth
|
|
123
|
+
server primitives.
|
|
124
|
+
|
|
125
|
+
## SDK Surface Rule
|
|
126
|
+
|
|
127
|
+
Mantle should expose Better Auth knobs only as curated first-class
|
|
128
|
+
fields when there is a real Mantle use case. Do not add a generic
|
|
129
|
+
`betterAuthOptions` or `advanced` passthrough.
|
|
130
|
+
|
|
131
|
+
The current first-party SSO use case justifies these optional fields on
|
|
132
|
+
`CreateAuthConfig`:
|
|
133
|
+
|
|
134
|
+
- `trustedOrigins`
|
|
135
|
+
- `crossSubDomainCookies`
|
|
136
|
+
- `cookiePrefix`
|
|
137
|
+
|
|
138
|
+
All three are additive. Existing generated sites that do not pass them
|
|
139
|
+
keep their previous cookie and session behavior.
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# Cloudflare R2 Media Uploads
|
|
2
|
+
|
|
3
|
+
Mantle supports staff media uploads through Staff MCP tools:
|
|
4
|
+
`create_media_upload` and `commit_media_upload`. This is an optional
|
|
5
|
+
post-launch feature for sites that use the Cloudflare adapter and need
|
|
6
|
+
staff-managed images or files.
|
|
7
|
+
|
|
8
|
+
Do not make R2 part of the Day 1 launch path. Cloudflare R2 setup may
|
|
9
|
+
require billing or a credit card, so landing and the blank starter do not
|
|
10
|
+
provision it by default.
|
|
11
|
+
|
|
12
|
+
## When To Enable
|
|
13
|
+
|
|
14
|
+
Enable this after the site already has:
|
|
15
|
+
|
|
16
|
+
- a working Cloudflare Worker deploy;
|
|
17
|
+
- staff auth configured;
|
|
18
|
+
- a real need for staff or an agent to upload images/files.
|
|
19
|
+
|
|
20
|
+
Use Claude Code, Codex, Cursor, or another local/non-sandboxed coding
|
|
21
|
+
agent for media maintenance. Claude Cowork often cannot complete this
|
|
22
|
+
flow because the final upload is a direct HTTP PUT to
|
|
23
|
+
`*.r2.cloudflarestorage.com`.
|
|
24
|
+
|
|
25
|
+
## How It Works
|
|
26
|
+
|
|
27
|
+
The upload flow is deliberately split:
|
|
28
|
+
|
|
29
|
+
1. The agent calls Staff MCP `create_media_upload` with purpose, variant
|
|
30
|
+
metadata, byte sizes, and mime types.
|
|
31
|
+
2. Mantle returns signed upload URLs and required headers.
|
|
32
|
+
3. The agent reads the local/chat attachment bytes and PUTs them directly
|
|
33
|
+
to R2.
|
|
34
|
+
4. The agent calls `commit_media_upload`.
|
|
35
|
+
|
|
36
|
+
Do not pass image bytes or base64 payloads through MCP tool arguments.
|
|
37
|
+
The Worker validates policy; the agent runtime performs file processing
|
|
38
|
+
and upload.
|
|
39
|
+
|
|
40
|
+
## Cloudflare Setup
|
|
41
|
+
|
|
42
|
+
Create a bucket and public read URL:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
wrangler r2 bucket create <project>-media
|
|
46
|
+
wrangler r2 bucket dev-url enable <project>-media
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
In the Cloudflare dashboard, create an R2 S3 API token:
|
|
50
|
+
|
|
51
|
+
1. Open **R2**.
|
|
52
|
+
2. Open **Manage R2 API Tokens**.
|
|
53
|
+
3. Create an Object Read & Write token.
|
|
54
|
+
4. Copy the Access Key ID and Secret Access Key.
|
|
55
|
+
|
|
56
|
+
The R2 binding alone cannot issue presigned PUT URLs; Mantle also needs
|
|
57
|
+
these S3-compatible credentials.
|
|
58
|
+
|
|
59
|
+
## `wrangler.toml`
|
|
60
|
+
|
|
61
|
+
```toml
|
|
62
|
+
[vars]
|
|
63
|
+
R2_ACCOUNT_ID = "<account-id>"
|
|
64
|
+
MEDIA_PUBLIC_URL_BASE = "https://pub-<hash>.r2.dev"
|
|
65
|
+
|
|
66
|
+
[[r2_buckets]]
|
|
67
|
+
binding = "MEDIA_BUCKET"
|
|
68
|
+
bucket_name = "<project>-media"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Set secrets:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
wrangler secret put R2_ACCESS_KEY_ID
|
|
75
|
+
wrangler secret put R2_SECRET_ACCESS_KEY
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## `src/mantleConfig.ts`
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { R2MediaStorage, type CmsConfig } from "@aotter/mantle/cloudflare";
|
|
82
|
+
import { AwsClient } from "aws4fetch";
|
|
83
|
+
|
|
84
|
+
export interface Env {
|
|
85
|
+
readonly MEDIA_BUCKET?: R2Bucket;
|
|
86
|
+
readonly R2_ACCOUNT_ID?: string;
|
|
87
|
+
readonly R2_ACCESS_KEY_ID?: string;
|
|
88
|
+
readonly R2_SECRET_ACCESS_KEY?: string;
|
|
89
|
+
readonly MEDIA_PUBLIC_URL_BASE?: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function buildMediaStorage(env: Env): CmsConfig["bindings"]["mediaStorage"] {
|
|
93
|
+
if (
|
|
94
|
+
!env.MEDIA_BUCKET ||
|
|
95
|
+
!env.R2_ACCOUNT_ID ||
|
|
96
|
+
!env.R2_ACCESS_KEY_ID ||
|
|
97
|
+
!env.R2_SECRET_ACCESS_KEY ||
|
|
98
|
+
!env.MEDIA_PUBLIC_URL_BASE
|
|
99
|
+
) {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const s3 = new AwsClient({
|
|
104
|
+
accessKeyId: env.R2_ACCESS_KEY_ID,
|
|
105
|
+
secretAccessKey: env.R2_SECRET_ACCESS_KEY,
|
|
106
|
+
region: "auto",
|
|
107
|
+
service: "s3",
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
return new R2MediaStorage(
|
|
111
|
+
env.MEDIA_BUCKET,
|
|
112
|
+
s3,
|
|
113
|
+
`https://<project>-media.${env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,
|
|
114
|
+
env.MEDIA_PUBLIC_URL_BASE,
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Then wire it into the CMS config:
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
siteDefaults: {
|
|
123
|
+
media: {
|
|
124
|
+
purposes: [
|
|
125
|
+
{
|
|
126
|
+
name: "page-image",
|
|
127
|
+
required: ["image/jpeg,image/png", "image/webp", "image/avif"],
|
|
128
|
+
maxBytes: {
|
|
129
|
+
"image/jpeg": 5_000_000,
|
|
130
|
+
"image/png": 5_000_000,
|
|
131
|
+
"image/webp": 3_000_000,
|
|
132
|
+
"image/avif": 2_000_000,
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
bindings: {
|
|
139
|
+
mediaStorage: buildMediaStorage(env),
|
|
140
|
+
},
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
`required` is slot-based. The example above declares three slots:
|
|
144
|
+
|
|
145
|
+
- slot 0: choose exactly one fallback mime, preserving the source
|
|
146
|
+
semantics (`image/jpeg` for opaque photos, `image/png` when alpha must
|
|
147
|
+
be preserved);
|
|
148
|
+
- slot 1: include a WebP variant;
|
|
149
|
+
- slot 2: include an AVIF variant.
|
|
150
|
+
|
|
151
|
+
This models the ADR-0017 multi-variant `<picture>` shape. If a site wants
|
|
152
|
+
a simpler single-variant upload for a narrow purpose, it may declare one
|
|
153
|
+
slot such as `["image/jpeg,image/png,image/webp,image/gif"]`; in that
|
|
154
|
+
case, the agent chooses exactly one mime from the comma-separated list and
|
|
155
|
+
must not upload one variant per listed mime.
|
|
156
|
+
|
|
157
|
+
## Tool Visibility
|
|
158
|
+
|
|
159
|
+
The Staff MCP media tools are registered only when both are true:
|
|
160
|
+
|
|
161
|
+
- `bindings.mediaStorage` is set;
|
|
162
|
+
- `siteDefaults.media.purposes` contains at least one purpose.
|
|
163
|
+
|
|
164
|
+
If either side is missing, `create_media_upload` and
|
|
165
|
+
`commit_media_upload` will not appear in `tools/list`.
|
|
166
|
+
|
|
167
|
+
## Agent Guidance
|
|
168
|
+
|
|
169
|
+
For image maintenance, prefer Claude Code or another coding agent that can
|
|
170
|
+
read local files, process images, and make outbound PUT requests to R2.
|
|
171
|
+
Avoid Claude Cowork for this workflow; use a non-sandboxed agent instead.
|
|
172
|
+
|
|
173
|
+
Preserve source semantics:
|
|
174
|
+
|
|
175
|
+
- photos may use JPEG/WebP variants;
|
|
176
|
+
- transparent logos must keep alpha;
|
|
177
|
+
- animated GIFs must stay animated;
|
|
178
|
+
- do not silently flatten, resize, or recompress user assets without
|
|
179
|
+
asking.
|
package/docs/release-process.md
CHANGED
|
@@ -139,7 +139,7 @@ The audit takes ~30 seconds and rules out the most common fanout failure. Do it
|
|
|
139
139
|
`bump-from-sdk.yml` / `bump-from-starters.yml` failed at the validate / typecheck gate because the SDK release introduced a code-shape break? Re-firing the workflow won't help — it'll fail the same gate on the same source. The fix-forward path:
|
|
140
140
|
|
|
141
141
|
1. Branch off `develop` (starters) or `main` (landing): `release/vX.Y.Z`.
|
|
142
|
-
2. Replicate what the bump workflow would have done — bump every `@aotter/mantle*` dep + own `version` in package.json files, refresh
|
|
142
|
+
2. Replicate what the bump workflow would have done — bump every `@aotter/mantle*` dep + own `version` in package.json files, refresh lockfiles via `pnpm install --no-frozen-lockfile`, and rebuild starter provision bundles when starters source changed.
|
|
143
143
|
3. Add whatever source-code fixes satisfy the new SDK shape.
|
|
144
144
|
4. Commit subject MUST be `release: bump @aotter/mantle* to vX.Y.Z` (starters) or `release: bump @aotter/mantle to vX.Y.Z` (landing) — `tag-and-dispatch-landing.yml` filters on this in starters; landing has no equivalent filter but the convention keeps history consistent.
|
|
145
145
|
5. Open PR base=`main` (starters and landing), CI passes now that lockfile + source are in sync, rebase-merge.
|
|
@@ -148,11 +148,11 @@ The audit takes ~30 seconds and rules out the most common fanout failure. Do it
|
|
|
148
148
|
|
|
149
149
|
### Re-spin release for a downstream-content-only fix
|
|
150
150
|
|
|
151
|
-
Sometimes the SDK npm artifact is fine but the
|
|
151
|
+
Sometimes the SDK npm artifact is fine but the generated starter provision bundle is broken (e.g. starter content didn't include a freshly-required field at release time). Per `§ Rollback / yanking policy`, the right path is to publish the next alpha as a no-op SDK bump that re-spins the fanout:
|
|
152
152
|
|
|
153
153
|
1. Cut alpha.N+1 in `mantle/` with empty SDK diff (versions + CHANGELOG only).
|
|
154
154
|
2. CHANGELOG entry MUST say explicitly: `No SDK code changes. alpha.N+1 re-spins the release fanout to ship starter content that should have been part of alpha.N (see #XXX).`
|
|
155
|
-
3. Tag + push
|
|
155
|
+
3. Tag + push -> full fanout produces fresh `mantle-starters` provision bundles with the corrected content.
|
|
156
156
|
|
|
157
157
|
Don't force-retag the broken alpha. Don't introduce a starter-only sub-tag like `vX.Y.Z-starter.N`. Either breaks the convention that starter version === SDK version.
|
|
158
158
|
|
|
@@ -173,7 +173,7 @@ release.yml: pnpm install → build → test (gate) → verify package.json
|
|
|
173
173
|
│
|
|
174
174
|
▼
|
|
175
175
|
mantle-starters/bump-from-sdk.yml: bump @aotter/mantle* deps
|
|
176
|
-
+ own version
|
|
176
|
+
+ own version → pnpm install + rebuild bundles →
|
|
177
177
|
validate × 5 starters (gate) → typecheck × 5 (gate) →
|
|
178
178
|
PR onto main → auto-approve + auto-merge
|
|
179
179
|
│
|
|
@@ -271,22 +271,20 @@ stay individually installable for tooling / alt-adapter authors.
|
|
|
271
271
|
|
|
272
272
|
Do **not** publish starter packages during alpha unless a separate PR
|
|
273
273
|
explicitly prepares their package allowlists and verifies the tarballs.
|
|
274
|
-
Current starter
|
|
275
|
-
|
|
276
|
-
repo
|
|
274
|
+
Current starter launch flow is landing-driven: landing fetches generated
|
|
275
|
+
`provision-bundles/<type>.json` artifacts from `aotter/mantle-starters`,
|
|
276
|
+
commits the user's GitHub repo, and uses npm as the runtime dependency
|
|
277
|
+
source.
|
|
277
278
|
|
|
278
279
|
Do **not** publish `@aotter/mantle-netlify` while it is a stub.
|
|
279
280
|
|
|
280
|
-
`create-mantle`
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
from the starters repo as a GitHub release tarball. Releases on this SDK
|
|
284
|
-
repo do not attach a create-mantle tarball and must not publish
|
|
285
|
-
`@aotter/create-mantle`.
|
|
281
|
+
The retired `create-mantle` scaffolder is not shipped by this SDK repo.
|
|
282
|
+
Releases on this SDK repo must not attach a create-mantle tarball and
|
|
283
|
+
must not publish `@aotter/create-mantle`.
|
|
286
284
|
|
|
287
|
-
`skills/install/SKILL.md` consumes the
|
|
288
|
-
page. Human-facing
|
|
289
|
-
not this SDK repo.
|
|
285
|
+
`skills/install/SKILL.md` consumes the repo and handoff created by the
|
|
286
|
+
landing page. Human-facing starter bundle details belong in the
|
|
287
|
+
`mantle-starters` README, not this SDK repo.
|
|
290
288
|
|
|
291
289
|
### Pre-publish checks
|
|
292
290
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aotter/mantle",
|
|
3
|
-
"version": "0.0.11-alpha.
|
|
3
|
+
"version": "0.0.11-alpha.37",
|
|
4
4
|
"description": "Umbrella entry for @aotter/mantle. Adopters install this one package and import from subpaths: /spec, /runtime, /cloudflare, /admin-ui. Sub-packages remain individually installable on npm for tooling / alt-adapter authors. The Netlify adapter ships as a private workspace stub in v0.1 — its subpath will be added when the impl lands in v0.2.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://mantle.tools/",
|
|
@@ -47,15 +47,15 @@
|
|
|
47
47
|
"README.md"
|
|
48
48
|
],
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@aotter/mantle-
|
|
51
|
-
"@aotter/mantle-runtime": "0.0.11-alpha.
|
|
52
|
-
"@aotter/mantle-spec": "0.0.11-alpha.
|
|
53
|
-
"@aotter/mantle-
|
|
50
|
+
"@aotter/mantle-admin-ui": "0.0.11-alpha.37",
|
|
51
|
+
"@aotter/mantle-runtime": "0.0.11-alpha.37",
|
|
52
|
+
"@aotter/mantle-spec": "0.0.11-alpha.37",
|
|
53
|
+
"@aotter/mantle-cloudflare": "0.0.11-alpha.37"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"@cloudflare/workers-oauth-provider": "^0.7.0",
|
|
57
57
|
"aws4fetch": "^1.0.20",
|
|
58
|
-
"better-auth": "^1.6.
|
|
58
|
+
"better-auth": "^1.6.23",
|
|
59
59
|
"hono": "^4.12.0",
|
|
60
60
|
"zod": "^4.0.0"
|
|
61
61
|
},
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"@cloudflare/workers-oauth-provider": "^0.7.0",
|
|
64
64
|
"@types/node": "^25",
|
|
65
65
|
"aws4fetch": "^1.0.20",
|
|
66
|
-
"better-auth": "^1.6.
|
|
66
|
+
"better-auth": "^1.6.23",
|
|
67
67
|
"hono": "^4.12.23",
|
|
68
68
|
"typescript": "^6.0.3",
|
|
69
69
|
"zod": "^4.4.2"
|
package/skills/README.md
CHANGED
|
@@ -4,13 +4,33 @@ Agent-readable skill briefs for consumers of `@aotter/mantle-*`. Discoverable by
|
|
|
4
4
|
|
|
5
5
|
| Skill | When to invoke |
|
|
6
6
|
|---|---|
|
|
7
|
+
| [`develop`](develop/SKILL.md) | `mantle:develop`: Core-owned workflow for manifest, runtime, handler, adapter, validation, and MCP work in any Mantle project. |
|
|
8
|
+
| [`plugin`](plugin/SKILL.md) | `mantle:plugin`: Core-owned marketplace workflow for plan-first capability installs across starters and adapters. |
|
|
9
|
+
| [`theme`](theme/SKILL.md) | `mantle:theme`: Core-owned visual workflow. Reads project context but does not depend on starter-owned skill semantics. |
|
|
10
|
+
| [`update`](update/SKILL.md) | `mantle:update`: Core-owned drift check workflow for SDK, starter snapshots, and plugin lockfiles. |
|
|
7
11
|
| [`install`](install/SKILL.md) | User wants to start or continue a Mantle site. Sites launch on [Mantle landing](https://mantle.tools), which provisions the GitHub repo and first Cloudflare deploy; this brief orients the agent to take over and continue the provisioned repo. |
|
|
8
|
-
| [`customize-design`](customize-design/SKILL.md) |
|
|
9
|
-
| [`extend`](extend/SKILL.md) |
|
|
10
|
-
| [`provision`](provision/SKILL.md) | User wants production fully usable after a landing launch. Verify the
|
|
12
|
+
| [`customize-design`](customize-design/SKILL.md) | Legacy publication-specific design guide. Prefer `mantle:theme` for generated repos. |
|
|
13
|
+
| [`extend`](extend/SKILL.md) | Legacy atom-authoring guide. Prefer `mantle:develop` or `mantle:plugin` depending on whether the work is one-off or installable. |
|
|
14
|
+
| [`provision`](provision/SKILL.md) | User wants production fully usable after a landing launch. Verify the current landing deploy, wire per-site staff auth, smoke test, and hand off the operator setup URL. |
|
|
11
15
|
|
|
12
16
|
The skills target `mantle@v0.1.0`. Each one names its assumed grammar version in the front-matter `applies_to:` field; future versions add a sibling SKILL.md or update the existing one.
|
|
13
17
|
|
|
18
|
+
## Skill authority
|
|
19
|
+
|
|
20
|
+
The `mantle:*` namespace is owned by `@aotter/mantle`. Starter template
|
|
21
|
+
repos may vendor exact copies for offline/repo-local use, but they must not
|
|
22
|
+
fork the meaning of a `mantle:*` skill. Starter launch files and plugin recipes
|
|
23
|
+
are context that Core skills read, not competing skill contracts.
|
|
24
|
+
|
|
25
|
+
## Marketplace install
|
|
26
|
+
|
|
27
|
+
The repo is also an agent plugin bundle:
|
|
28
|
+
|
|
29
|
+
- Claude Code: `.claude-plugin/plugin.json` plus `.claude-plugin/marketplace.json`.
|
|
30
|
+
- Codex: `.codex-plugin/plugin.json` plus `.agents/plugins/marketplace.json`.
|
|
31
|
+
- Cursor: `.cursor-plugin/plugin.json`.
|
|
32
|
+
- VS Code + GitHub Copilot: `.copilot-plugin/plugin.json`.
|
|
33
|
+
|
|
14
34
|
## Audience
|
|
15
35
|
|
|
16
36
|
These are written for **AI agents acting on behalf of consumers of mantle**, not for agents maintaining the mantle SDK itself. SDK-internal guidance lives in [`/CLAUDE.md`](../CLAUDE.md). Two audiences, two artifacts.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mantle:develop
|
|
3
|
+
description: Work on any Mantle project using the Core SDK contract. Use for manifest, runtime, content model, handler, adapter, validation, and MCP work after a project already exists.
|
|
4
|
+
source: "@aotter/mantle"
|
|
5
|
+
sourcePath: skills/develop/SKILL.md
|
|
6
|
+
when_to_invoke: |
|
|
7
|
+
Working dir contains a Mantle project: package.json depends on @aotter/mantle or @aotter/mantle-*, and manifests/ or src/mantleConfig.ts exists.
|
|
8
|
+
applies_to: mantle@v0.1.0
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Mantle Develop
|
|
12
|
+
|
|
13
|
+
This is the Core SDK skill for working inside an existing Mantle project.
|
|
14
|
+
Starter files, launch handoffs, and plugin recipes are context; this skill
|
|
15
|
+
owns the workflow vocabulary.
|
|
16
|
+
|
|
17
|
+
## First Read
|
|
18
|
+
|
|
19
|
+
1. `package.json` for the installed `@aotter/mantle*` versions.
|
|
20
|
+
2. `manifests/` and `src/mantleConfig.ts` for the active atoms and adapter wiring.
|
|
21
|
+
3. Optional local context: `.mantle/launch-state.json`, `.mantle/handoff.md`,
|
|
22
|
+
`.mantle/plugins.json`, `.mantle/plugins.lock.json`, and `.mantle/recipes/`.
|
|
23
|
+
4. Installed Core docs in `node_modules/@aotter/mantle/docs/`.
|
|
24
|
+
|
|
25
|
+
If `node_modules/` is missing, run `pnpm install --frozen-lockfile` before
|
|
26
|
+
falling back to remote docs.
|
|
27
|
+
|
|
28
|
+
## Core Model
|
|
29
|
+
|
|
30
|
+
Mantle exposes exactly four declarative atoms:
|
|
31
|
+
|
|
32
|
+
| Atom | Purpose |
|
|
33
|
+
|---|---|
|
|
34
|
+
| `Schema` | Stored entity/table shape. |
|
|
35
|
+
| `View` | Read/query surface. |
|
|
36
|
+
| `Procedure` | Typed mutation or operation. |
|
|
37
|
+
| `Trigger` | HTTP/lifecycle/MCP invocation binding. |
|
|
38
|
+
|
|
39
|
+
Do not invent manifest kinds such as `Form`, `Feature`, `Workflow`, or
|
|
40
|
+
`Membership`. Compose those from the four atoms plus TypeScript only where
|
|
41
|
+
the atoms cannot express the behavior.
|
|
42
|
+
|
|
43
|
+
## Adapter Boundary
|
|
44
|
+
|
|
45
|
+
The runtime is adapter-neutral. Required runtime ports are `DatabaseDriver`,
|
|
46
|
+
`KvCache`, and `AssetServer`. Optional feature ports, such as `MediaStorage`
|
|
47
|
+
or `DeferredHookDispatcher`, are enabled only when the current adapter wires
|
|
48
|
+
them.
|
|
49
|
+
|
|
50
|
+
Do not assume Cloudflare unless the project imports `@aotter/mantle/cloudflare`
|
|
51
|
+
or its adapter config is visible. A future Netlify adapter should satisfy the
|
|
52
|
+
same Core workflow through its own ports and provider setup.
|
|
53
|
+
|
|
54
|
+
## Loop
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pnpm install --frozen-lockfile
|
|
58
|
+
pnpm validate
|
|
59
|
+
pnpm typecheck
|
|
60
|
+
pnpm check
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Use `pnpm dev` for local preview when the project provides it.
|
|
64
|
+
|
|
65
|
+
## Rules
|
|
66
|
+
|
|
67
|
+
- Prefer manifest YAML for content model changes.
|
|
68
|
+
- Add TypeScript only for handlers, rendering, adapter wiring, or real behavior.
|
|
69
|
+
- Do not write directly to D1, KV, Postgres, or object storage for content
|
|
70
|
+
authoring. Use runtime use cases, admin APIs, or Staff MCP.
|
|
71
|
+
- Do not commit provider secrets.
|
|
72
|
+
- If the work is an installable capability, switch to `mantle:plugin`.
|
package/skills/install/SKILL.md
CHANGED
|
@@ -42,13 +42,13 @@ A landing-provisioned repo carries its launch context and ground truth in:
|
|
|
42
42
|
| Path | Contents |
|
|
43
43
|
|---|---|
|
|
44
44
|
| `.mantle/launch-state.json` | Non-secret launch choices: type, purpose, locales, repo, owner, suggested overlay |
|
|
45
|
-
| `.mantle/features.json` |
|
|
45
|
+
| `.mantle/features.json` | Starter launch context and applied starter state |
|
|
46
46
|
| `.mantle/handoff.md` | The launch handoff note written for you |
|
|
47
47
|
| `manifests/*.yaml` | Schemas / Views / Procedures / Triggers |
|
|
48
48
|
| `src/mantleConfig.ts` | Site defaults, handler registration, runtime bindings |
|
|
49
49
|
| `src/handlers/` | Handler implementations |
|
|
50
50
|
| `AGENTS.md` | Cross-tool agent entry |
|
|
51
|
-
| `.agent/skills/` + `.claude/skills/` |
|
|
51
|
+
| `.agent/skills/` + `.claude/skills/` | Vendored Core skills from `@aotter/mantle`: `mantle:develop`, `mantle:plugin`, `mantle:theme`, `mantle:update` |
|
|
52
52
|
|
|
53
53
|
## Take Over a Provisioned Repo
|
|
54
54
|
|
|
@@ -73,23 +73,37 @@ Surface both verbatim when validation fails.
|
|
|
73
73
|
|
|
74
74
|
## Continue With Repo-Local Skills
|
|
75
75
|
|
|
76
|
-
The generated repo
|
|
77
|
-
`.claude/skills/`.
|
|
76
|
+
The generated repo vendors Core-owned skills under `.agent/skills/` and
|
|
77
|
+
`.claude/skills/`. The starter carries local copies for offline/pinned use; it
|
|
78
|
+
does not own a separate skill contract.
|
|
78
79
|
|
|
79
|
-
- **`mantle:overlay`** — apply the selected launch type as a small
|
|
80
|
-
post-launch overlay (first useful pages plus tiny seed data in the
|
|
81
|
-
user's language). This is the usual first step on a fresh blank launch.
|
|
82
80
|
- **`mantle:develop`** — manifest, runtime, content-model, layout,
|
|
83
81
|
bug-fix, and validation work on the site.
|
|
82
|
+
- **`mantle:plugin`** — install repeatable marketplace capabilities using
|
|
83
|
+
a plan-first Core workflow.
|
|
84
84
|
- **`mantle:theme`** — brand and visual direction via Mantle / Kiwa tokens
|
|
85
85
|
and source. There is no premade theme picker; visual direction is agent
|
|
86
86
|
work after launch.
|
|
87
87
|
- **`mantle:update`** — compare the repo against its upstream provision
|
|
88
|
-
bundle.
|
|
88
|
+
bundle or installed plugin locks.
|
|
89
89
|
|
|
90
90
|
Remove the placeholder blank homepage once the selected type ships a real
|
|
91
91
|
homepage.
|
|
92
92
|
|
|
93
|
+
## Optional Media Uploads
|
|
94
|
+
|
|
95
|
+
Media uploads are a post-launch opt-in, not part of Day 1 provisioning. Do
|
|
96
|
+
not block the first deploy or first useful page on storage setup.
|
|
97
|
+
|
|
98
|
+
When the current repo uses the Cloudflare adapter and really needs
|
|
99
|
+
staff-managed images or files, follow the Cloudflare R2 recipe:
|
|
100
|
+
<https://raw.githubusercontent.com/aotter/mantle/develop/docs/media-uploads.md>.
|
|
101
|
+
|
|
102
|
+
For media maintenance, prefer Claude Code, Codex, Cursor, or another
|
|
103
|
+
local/non-sandboxed coding agent that can read files and make outbound PUT
|
|
104
|
+
requests. Do not rely on Claude Cowork for R2 uploads; use a
|
|
105
|
+
non-sandboxed agent instead.
|
|
106
|
+
|
|
93
107
|
## Local Preview
|
|
94
108
|
|
|
95
109
|
To see the site locally before pushing:
|
|
@@ -122,6 +136,8 @@ skill:
|
|
|
122
136
|
- Don't scaffold a project from scratch locally; launch happens on landing.
|
|
123
137
|
- Don't block the first useful page on polishing prose or writing a site letter.
|
|
124
138
|
- Don't ask for a Cloudflare API token in the base flow.
|
|
139
|
+
- Don't require R2 media setup in the base flow; it is optional
|
|
140
|
+
post-launch work.
|
|
125
141
|
- Don't commit provider secrets.
|
|
126
142
|
- Don't use `127.0.0.1` in OAuth callback examples; use `localhost` locally
|
|
127
143
|
and the real Worker URL in production.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mantle:plugin
|
|
3
|
+
description: Discover, plan, apply, and verify Mantle marketplace plugins through the Core SDK contract. Use when the user wants to add an installable capability without hand-planning provisioning steps.
|
|
4
|
+
source: "@aotter/mantle"
|
|
5
|
+
sourcePath: skills/plugin/SKILL.md
|
|
6
|
+
when_to_invoke: |
|
|
7
|
+
The user asks for a marketplace/plugin/capability install, or wants a domain feature that should be repeatable across starters and adapters.
|
|
8
|
+
applies_to: mantle@v0.1.0
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Mantle Plugin
|
|
12
|
+
|
|
13
|
+
Mantle plugins are Core SDK capability packages. They are not starter overlays
|
|
14
|
+
and they are not provider provisioning scripts.
|
|
15
|
+
|
|
16
|
+
A plugin may contribute:
|
|
17
|
+
|
|
18
|
+
- manifests: `Schema`, `View`, `Procedure`, `Trigger`;
|
|
19
|
+
- handler source or handler registration notes;
|
|
20
|
+
- site defaults or media policy additions;
|
|
21
|
+
- expected HTTP, admin, and MCP surfaces;
|
|
22
|
+
- adapter capability requirements and provider setup notes.
|
|
23
|
+
|
|
24
|
+
## User Install Entry
|
|
25
|
+
|
|
26
|
+
The user-facing install path is:
|
|
27
|
+
|
|
28
|
+
```txt
|
|
29
|
+
Use repo-local mantle:plugin to install <plugin slug or recipe URL> in this repo.
|
|
30
|
+
Use repo-local mantle:plugin to update <plugin id> in this repo.
|
|
31
|
+
Use repo-local mantle:plugin to remove <plugin id> from this repo.
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
There is no `mantle plugin add` CLI yet. Do not invent one. Install from a
|
|
35
|
+
marketplace entry, plugin package, or recipe URL that declares enough data for
|
|
36
|
+
an agent to apply the capability deterministically.
|
|
37
|
+
|
|
38
|
+
A valid marketplace entry must include:
|
|
39
|
+
|
|
40
|
+
- plugin id, title, source, and version;
|
|
41
|
+
- supported Mantle version range;
|
|
42
|
+
- files, manifests, handlers, routes, MCP tools, and admin surfaces it adds;
|
|
43
|
+
- adapter capabilities and provider resources it requires;
|
|
44
|
+
- required env vars and secrets, without secret values;
|
|
45
|
+
- verification commands and expected surfaces.
|
|
46
|
+
|
|
47
|
+
If the marketplace page is only marketing copy or lacks an install recipe,
|
|
48
|
+
stop and ask for the recipe instead of guessing.
|
|
49
|
+
|
|
50
|
+
## First Read
|
|
51
|
+
|
|
52
|
+
1. `package.json` for Mantle version and adapter package.
|
|
53
|
+
2. `manifests/` for current atom names and route/tool collisions.
|
|
54
|
+
3. `src/mantleConfig.ts` for registered handlers, templates, and optional ports.
|
|
55
|
+
4. `.mantle/plugins.json` and `.mantle/plugins.lock.json` if present.
|
|
56
|
+
5. `.mantle/launch-state.json` only as context, not as plugin authority.
|
|
57
|
+
|
|
58
|
+
## Plan First
|
|
59
|
+
|
|
60
|
+
Before applying any plugin, produce a plan:
|
|
61
|
+
|
|
62
|
+
- files to add or change;
|
|
63
|
+
- atoms to add and their names;
|
|
64
|
+
- HTTP routes and MCP tools that will appear;
|
|
65
|
+
- required runtime ports;
|
|
66
|
+
- adapter-specific resources, env vars, and secrets;
|
|
67
|
+
- checks to run.
|
|
68
|
+
|
|
69
|
+
If the plugin needs a capability the current adapter does not expose, stop
|
|
70
|
+
with the missing capability instead of inventing provider steps.
|
|
71
|
+
|
|
72
|
+
## Apply
|
|
73
|
+
|
|
74
|
+
Apply the smallest deterministic diff. Do not run arbitrary install scripts
|
|
75
|
+
from a plugin package. Copy declared files, wire declared handlers, update the
|
|
76
|
+
plugin ledger, then validate.
|
|
77
|
+
|
|
78
|
+
Suggested ledger paths:
|
|
79
|
+
|
|
80
|
+
```txt
|
|
81
|
+
.mantle/plugins.json
|
|
82
|
+
.mantle/plugins.lock.json
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Keep starter launch state separate from plugin state. `.mantle/features.json`
|
|
86
|
+
is launch/starter context, not the Core plugin ledger.
|
|
87
|
+
|
|
88
|
+
## Update
|
|
89
|
+
|
|
90
|
+
Compare the installed lock entry against the marketplace entry or recipe URL.
|
|
91
|
+
Apply only the declared version diff, update `.mantle/plugins.lock.json`, then
|
|
92
|
+
run the same verification checks.
|
|
93
|
+
|
|
94
|
+
## Remove
|
|
95
|
+
|
|
96
|
+
Use the lock entry as the removal manifest. Delete only files and atoms owned
|
|
97
|
+
by that plugin, unwind handler registrations it added, remove its ledger entry,
|
|
98
|
+
then validate. If another plugin or local code depends on a removed atom, stop
|
|
99
|
+
and report the dependency instead of deleting through it.
|
|
100
|
+
|
|
101
|
+
## Verify
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
pnpm validate
|
|
105
|
+
pnpm typecheck
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Then verify the plugin's declared surfaces:
|
|
109
|
+
|
|
110
|
+
- `GET /api/views/<name>` for View reads;
|
|
111
|
+
- HTTP Trigger path for public writes;
|
|
112
|
+
- Staff/Public MCP `tools/list` for MCP Trigger or Schema-derived tools;
|
|
113
|
+
- adapter resource presence when the plugin requires optional ports.
|
|
114
|
+
|
|
115
|
+
## Don't
|
|
116
|
+
|
|
117
|
+
- Don't treat a starter archetype as a plugin.
|
|
118
|
+
- Don't assume Cloudflare; inspect the active adapter and capability ports.
|
|
119
|
+
- Don't create a second skill namespace for starter-specific plugins.
|
|
120
|
+
- Don't commit secrets. Provider secrets stay in the platform secret store.
|
|
@@ -39,6 +39,15 @@ with v2.
|
|
|
39
39
|
Provision does not seed production content. First real content is created
|
|
40
40
|
after owner sign-in through Staff MCP / admin authoring.
|
|
41
41
|
|
|
42
|
+
Media uploads are optional post-launch work. Do not require storage or media
|
|
43
|
+
upload credentials to finish first production provisioning. If the current
|
|
44
|
+
repo uses the Cloudflare adapter and the owner asks for staff image/file
|
|
45
|
+
uploads later, follow the Cloudflare R2 recipe:
|
|
46
|
+
<https://raw.githubusercontent.com/aotter/mantle/develop/docs/media-uploads.md>
|
|
47
|
+
and use Claude Code or another local/non-sandboxed coding agent for the
|
|
48
|
+
upload workflow. Do not use Claude Cowork for R2 uploads; use a
|
|
49
|
+
non-sandboxed agent instead.
|
|
50
|
+
|
|
42
51
|
## Principles
|
|
43
52
|
|
|
44
53
|
1. Use the user's accounts. The repo and Worker belong to the user's
|
|
@@ -149,10 +158,13 @@ repo-local `.agent/skills/` directory (`mantle:develop`, `mantle:overlay`,
|
|
|
149
158
|
| GitHub OAuth callback mismatch | OAuth App callback URL is wrong | Set it exactly to `<worker-url>/api/auth/callback/github`. |
|
|
150
159
|
| Owner signs in but admin / MCP returns 403 | `ADMIN_GITHUB_LOGIN` does not match the signed-in GitHub login | Fix the `ADMIN_GITHUB_LOGIN` value and redeploy. |
|
|
151
160
|
| Worker boots but sessions fail after a rerun | `BETTER_AUTH_SECRET` changed or was deleted | Restore the old secret if available; otherwise users must sign in again. |
|
|
161
|
+
| `create_media_upload` is missing from Staff MCP | Optional R2 media is not configured, or `media.purposes` is empty | Only fix this if the owner explicitly wants media uploads; follow `docs/media-uploads.md`. |
|
|
162
|
+
| Upload session works but the PUT to R2 fails from Claude Cowork | Cowork sandbox egress blocks direct R2 uploads | Retry from Claude Code / another non-sandboxed agent. |
|
|
152
163
|
|
|
153
164
|
## Don't
|
|
154
165
|
|
|
155
166
|
- Don't ask for a Cloudflare API token in the base first-run path.
|
|
167
|
+
- Don't require R2 media setup in the base first-run path.
|
|
156
168
|
- Don't re-create the repo or re-run the first deploy from the agent;
|
|
157
169
|
landing owns first provisioning.
|
|
158
170
|
- Don't resurrect `provision:up` / `provision:plan` as a second
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mantle:theme
|
|
3
|
+
description: Apply brand and visual direction in a Mantle project without depending on starter-owned skill semantics.
|
|
4
|
+
source: "@aotter/mantle"
|
|
5
|
+
sourcePath: skills/theme/SKILL.md
|
|
6
|
+
when_to_invoke: |
|
|
7
|
+
User wants visual, brand, layout, copy, or UI polish in an existing Mantle project.
|
|
8
|
+
applies_to: mantle@v0.1.0
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Mantle Theme
|
|
12
|
+
|
|
13
|
+
Theme work is project-owned source editing. Starters may ship Kiwa files,
|
|
14
|
+
tokens, or recipes, but the skill contract is Core-owned.
|
|
15
|
+
|
|
16
|
+
## First Read
|
|
17
|
+
|
|
18
|
+
1. `.mantle/handoff.md` and `.mantle/recipes/` if present.
|
|
19
|
+
2. `styles/`, `components/`, `src/home.tsx`, `src/theme*`, and `kiwa-ui.json`
|
|
20
|
+
if present.
|
|
21
|
+
3. `manifests/` to understand which content shape drives the public UI.
|
|
22
|
+
|
|
23
|
+
## Work
|
|
24
|
+
|
|
25
|
+
- Edit source the repo owns.
|
|
26
|
+
- Use existing tokens, CSS, components, and installed dependencies first.
|
|
27
|
+
- Keep accessibility basics: semantic HTML, focus states, contrast, and
|
|
28
|
+
keyboard reachability.
|
|
29
|
+
- Do not require registry access for a project that already vendors UI source.
|
|
30
|
+
- Add UI dependencies only when existing source cannot cover the requested
|
|
31
|
+
change.
|
|
32
|
+
|
|
33
|
+
## Check
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pnpm validate
|
|
37
|
+
pnpm typecheck
|
|
38
|
+
pnpm dev
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Visually verify UI changes before calling them done.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mantle:update
|
|
3
|
+
description: Check a Mantle project for drift against its Core SDK, starter source, or installed plugin lockfiles.
|
|
4
|
+
source: "@aotter/mantle"
|
|
5
|
+
sourcePath: skills/update/SKILL.md
|
|
6
|
+
when_to_invoke: |
|
|
7
|
+
User asks to update Mantle, compare generated files, refresh starter/vendor output, or inspect marketplace plugin drift.
|
|
8
|
+
applies_to: mantle@v0.1.0
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Mantle Update
|
|
12
|
+
|
|
13
|
+
Use this for drift checks. Do not blindly overwrite user-owned code.
|
|
14
|
+
|
|
15
|
+
## First Read
|
|
16
|
+
|
|
17
|
+
1. `package.json` and lockfile for installed `@aotter/mantle*` versions.
|
|
18
|
+
2. `.mantle/launch-state.json` and `.mantle/features.json` when the project
|
|
19
|
+
came from Mantle landing.
|
|
20
|
+
3. `.mantle/plugins.json` and `.mantle/plugins.lock.json` when plugins are
|
|
21
|
+
installed.
|
|
22
|
+
4. Existing project scripts such as `mantle:update`, `validate`, and
|
|
23
|
+
`typecheck`.
|
|
24
|
+
|
|
25
|
+
## Workflow
|
|
26
|
+
|
|
27
|
+
1. Start from a clean git worktree.
|
|
28
|
+
2. Run the project's existing update or compare script if one exists.
|
|
29
|
+
3. Read the generated report before editing.
|
|
30
|
+
4. Apply useful differences manually.
|
|
31
|
+
5. Re-run:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pnpm validate
|
|
35
|
+
pnpm typecheck
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Boundary
|
|
39
|
+
|
|
40
|
+
Starter bundles and plugin packages can provide source snapshots, but Core owns
|
|
41
|
+
the update vocabulary. A stale starter recipe or plugin note is context; it does
|
|
42
|
+
not override the installed Core SDK contract.
|