@typeroll/mcp-server 0.45.79 → 0.45.81
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/dist/bundled-content.js +13 -10
- package/dist/server.js +4 -2
- package/dist/tools/domain.js +24 -3
- package/dist/tools/settings.js +9 -2
- package/dist/version.js +1 -1
- package/dist/workspace.js +4 -2
- package/package.json +2 -2
- package/skills/tr-brand.md +43 -7
- package/skills/tr-migrate-wp.md +55 -0
- package/skills/tr-migration-evidence.md +27 -1
- package/skills/tr-new-site.md +37 -1
- package/skills/tr-starter-design.md +36 -0
- package/workspace-templates/qa/checklist.md +17 -0
- package/workspace-templates/scripts/README.md +26 -0
- package/workspace-templates/scripts/typeroll/color_tokens.py +81 -0
- package/workspace-templates/scripts/typeroll/prepare_image.py +64 -0
- package/workspace-templates/workflows/design.md +35 -0
- package/workspace-templates/workflows/migration.md +45 -0
- package/workspace-templates/workflows/new-site.md +7 -0
- package/workspace-templates/workflows/source-media.md +50 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Convert a reviewed local original to an sRGB JPEG; never upload or change CMS content.
|
|
2
|
+
Requires Pillow with ImageCms. Preserve the untouched original in the source archive.
|
|
3
|
+
CMYK needs its actual embedded or supplied ICC profile. Never guess a CMYK profile.
|
|
4
|
+
Unprofiled RGB/gray requires --assume-srgb after review. Output must not exist.
|
|
5
|
+
"""
|
|
6
|
+
import argparse
|
|
7
|
+
import hashlib
|
|
8
|
+
import io
|
|
9
|
+
import json
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def prepare(source, output, source_profile=None, assume_srgb=False, quality=95):
|
|
14
|
+
from PIL import Image, ImageCms, ImageOps
|
|
15
|
+
source, output = Path(source), Path(output)
|
|
16
|
+
if output.exists() or source.resolve() == output.resolve():
|
|
17
|
+
raise ValueError('Output already exists; choose a new derivative path.')
|
|
18
|
+
if output.suffix.lower() not in ('.jpg', '.jpeg'):
|
|
19
|
+
raise ValueError('Choose a .jpg or .jpeg output.')
|
|
20
|
+
if not 1 <= quality <= 100:
|
|
21
|
+
raise ValueError('JPEG quality must be from 1 to 100.')
|
|
22
|
+
with Image.open(source) as original:
|
|
23
|
+
if getattr(original, 'n_frames', 1) != 1:
|
|
24
|
+
raise ValueError('Multi-page/animated originals require an explicit frame decision.')
|
|
25
|
+
if 'A' in original.getbands() or 'transparency' in original.info:
|
|
26
|
+
raise ValueError('Transparent originals need a reviewed background or a PNG workflow.')
|
|
27
|
+
profile_bytes = Path(source_profile).read_bytes() if source_profile else original.info.get('icc_profile')
|
|
28
|
+
image = ImageOps.exif_transpose(original)
|
|
29
|
+
target = ImageCms.ImageCmsProfile(ImageCms.createProfile('sRGB'))
|
|
30
|
+
if profile_bytes:
|
|
31
|
+
profile = ImageCms.ImageCmsProfile(io.BytesIO(profile_bytes))
|
|
32
|
+
converted = ImageCms.profileToProfile(image, profile, target, outputMode='RGB')
|
|
33
|
+
elif image.mode in ('RGB', 'L') and assume_srgb:
|
|
34
|
+
converted = image.convert('RGB')
|
|
35
|
+
else:
|
|
36
|
+
raise ValueError('Missing source ICC profile. CMYK cannot be safely guessed; supply the correct --source-profile. For reviewed unprofiled RGB/gray only, use --assume-srgb.')
|
|
37
|
+
# No source EXIF/GPS/caption metadata is copied into the public derivative.
|
|
38
|
+
with output.open('xb') as destination:
|
|
39
|
+
converted.save(destination, format='JPEG', quality=quality, subsampling=0, icc_profile=target.tobytes())
|
|
40
|
+
with Image.open(output) as check:
|
|
41
|
+
check.load()
|
|
42
|
+
if check.mode != 'RGB' or not check.info.get('icc_profile'):
|
|
43
|
+
raise ValueError('Output verification failed; do not upload the derivative.')
|
|
44
|
+
dimensions = list(check.size)
|
|
45
|
+
return {'source_sha256': hashlib.sha256(source.read_bytes()).hexdigest(),
|
|
46
|
+
'output_sha256': hashlib.sha256(output.read_bytes()).hexdigest(),
|
|
47
|
+
'width': dimensions[0], 'height': dimensions[1], 'colour_space': 'sRGB',
|
|
48
|
+
'source_profile': 'supplied' if source_profile else 'embedded' if profile_bytes else 'assumed sRGB',
|
|
49
|
+
'uploaded': False}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def main():
|
|
53
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
54
|
+
parser.add_argument('source', type=Path)
|
|
55
|
+
parser.add_argument('output', type=Path)
|
|
56
|
+
parser.add_argument('--source-profile', type=Path)
|
|
57
|
+
parser.add_argument('--assume-srgb', action='store_true')
|
|
58
|
+
parser.add_argument('--quality', type=int, default=95)
|
|
59
|
+
args = parser.parse_args()
|
|
60
|
+
print(json.dumps(prepare(args.source, args.output, args.source_profile, args.assume_srgb, args.quality), indent=2))
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
if __name__ == '__main__':
|
|
64
|
+
main()
|
|
@@ -28,3 +28,38 @@ Record deliberate departures in decisions.md with scope and owner approval.
|
|
|
28
28
|
Use a QA round per review: method, pages/viewports, expected differences, results,
|
|
29
29
|
unverified items and remaining defects. Record platform findings in
|
|
30
30
|
qa/typeroll-platform-feedback.md; do not bury them in workaround CSS.
|
|
31
|
+
|
|
32
|
+
## Site tokens before block overrides
|
|
33
|
+
|
|
34
|
+
Core 0.2.106 / template capabilities 0.54.0 adds palette names in all colour
|
|
35
|
+
fields and named typography/spacing steps. Check the connected capability
|
|
36
|
+
version; on older renderers use `var(--color-surface)` rather than a bare name.
|
|
37
|
+
|
|
38
|
+
Set colours and font families in site settings, then use named styles and
|
|
39
|
+
palette names in blocks. Do not write hex, rgb or font-family literals in block
|
|
40
|
+
content. Prefer `primary`, `secondary`, `accent`, `background`, `surface`, `text`,
|
|
41
|
+
`text_light`, and `on_primary`/`on_secondary`/`on_accent` for text on brand colours.
|
|
42
|
+
Use `surface_hover` for a darker interactive surface (derived automatically
|
|
43
|
+
unless explicitly configured). Existing semantic roles should be reused; a
|
|
44
|
+
new visual shade is not automatically a new semantic role. For a derived shade
|
|
45
|
+
use `color-mix(in srgb, var(--color-surface) 92%, var(--color-primary))`.
|
|
46
|
+
For an additional role not in the native palette, define a named site style
|
|
47
|
+
or a site CSS variable derived from existing palette variables, and document it.
|
|
48
|
+
|
|
49
|
+
Choose `heading`, `body` or `mono` for fonts. Font families belong only in the
|
|
50
|
+
site's `fonts` settings. Use type-size steps `xs`, `sm`, `md`, `lg`, `xl`, `2xl`,
|
|
51
|
+
`3xl`; spacing `none`, `xs`, `sm`, `md`, `lg`, `xl`; line height `tight`, `snug`,
|
|
52
|
+
`normal`, `relaxed`, `loose`. Presentation fields accept responsive maps of these
|
|
53
|
+
names, for example `font_size_px: {mobile: "md", desktop: "lg"}`. Read the field
|
|
54
|
+
schema: counts, widths and breakpoints are not spacing steps. Pixel values are
|
|
55
|
+
explicit exceptions, not the default authoring method. Configure scales in
|
|
56
|
+
`design_scales`, then reuse them in role styles and blocks.
|
|
57
|
+
|
|
58
|
+
Record deliberate exceptions in `decisions.md`, with source/block, reason and
|
|
59
|
+
review criteria (for example white text on a photograph). Run `lint_design_tokens`
|
|
60
|
+
before launch and after major design changes. It warns, never blocks launch.
|
|
61
|
+
Use the starter's `scripts/typeroll/color_tokens.py` for an exact-match dry run;
|
|
62
|
+
review the plan before applying it in a dedicated Version. Equal hex values can
|
|
63
|
+
mean different roles: never automatically choose between background and text.
|
|
64
|
+
Verify a primary/surface change in a test Version across header, footer, cards,
|
|
65
|
+
menus and buttons, then restore the test settings. Do not republish automatically.
|
|
@@ -82,3 +82,48 @@ Page/partial block writes update working copies; check `saved` and use
|
|
|
82
82
|
`commit_working_copy` with `target: {kind: "page"|"partial", id: "..."}`.
|
|
83
83
|
Page templates save immediately and do not have working copies. REST read envelopes and
|
|
84
84
|
MCP tool text are different layers; `call.py` returns the decoded tool result.
|
|
85
|
+
|
|
86
|
+
## Design token check
|
|
87
|
+
|
|
88
|
+
Follow workflows/design.md for palette/font roles and named scales. Run
|
|
89
|
+
`lint_design_tokens` before launch and after major design changes. Review exact
|
|
90
|
+
colour replacements with scripts/typeroll/color_tokens.py; record deliberate
|
|
91
|
+
exceptions in decisions.md. Preserve text independently from design changes.
|
|
92
|
+
|
|
93
|
+
## Source retirement, analytics and domain preparation
|
|
94
|
+
|
|
95
|
+
Before old hosting is retired, follow workflows/source-media.md: preserve unused
|
|
96
|
+
and print originals in a private archive with independently verified checksums.
|
|
97
|
+
Only approved web assets enter the CMS. Use ICC-aware conversion for CMYK/TIFF.
|
|
98
|
+
|
|
99
|
+
Enable Analytics through the authorized Typeroll API/MCP (`update_app` or the
|
|
100
|
+
Organization analytics tools), then read back settings and verify the beacon and
|
|
101
|
+
cookies on the actual artifact. Resolve missing provider scopes through the
|
|
102
|
+
normal approval flow. A separately authorized provider-side setup with a supplied
|
|
103
|
+
beacon token is an alternative, not a way to bypass Organization permissions.
|
|
104
|
+
Never expose a provider API token as a public beacon token.
|
|
105
|
+
|
|
106
|
+
From Core 0.2.106, saving future hosts does not redirect normal publication.
|
|
107
|
+
Keep using `trigger_deploy` to update and review the current/temporary address.
|
|
108
|
+
Read `next_publication` in `read_publishing_domains` for the destination.
|
|
109
|
+
Shortly before DNS changes, deliberately call `prepare_publishing_domain_change`
|
|
110
|
+
with the current revision. It prepares the last successful public snapshot,
|
|
111
|
+
not later unpublished CMS edits. Review its frozen indexing settings and DNS
|
|
112
|
+
requirements, then approve traffic separately. If another normal publication
|
|
113
|
+
succeeds while the candidate waits, prepare again: an outdated snapshot cannot
|
|
114
|
+
replace newer published content. There is no need to clear saved future hosts.
|
|
115
|
+
To correct candidate indexing, save the intended setting, publish normally, and
|
|
116
|
+
prepare again. Temporary-host protection keeps the review address noindex even
|
|
117
|
+
when the published authored setting permits indexing on the future host.
|
|
118
|
+
Keep MX/SPF and unrelated DNS unchanged; review any validation window and obtain
|
|
119
|
+
the already-defined traffic-switch authorization.
|
|
120
|
+
|
|
121
|
+
## Owner-supplied facts and photos
|
|
122
|
+
|
|
123
|
+
Record owner statements, source and confirmation date. Preserve approved copy
|
|
124
|
+
under `text: preserve`; do not silently delete dates/prices or rewrite it into
|
|
125
|
+
"timeless" language. Under `text: renew`, propose removing stale details or using
|
|
126
|
+
recurring-event wording only when a source supports recurrence. Flag uncertain
|
|
127
|
+
facts for review or keep new Pages in draft; missing evidence does not prove that
|
|
128
|
+
an event stopped. Link to the actual organiser when relevant. Upload approved
|
|
129
|
+
photos with SHA-256 verification and review focal points at mobile/desktop widths.
|
|
@@ -15,3 +15,10 @@ Do not publish, change DNS or send messages without explicit approval.
|
|
|
15
15
|
|
|
16
16
|
The CMS owns current content and settings. The repository owns the brief, rules,
|
|
17
17
|
decisions and evidence. Load recipes when needed, not the whole manual.
|
|
18
|
+
|
|
19
|
+
## Design token check
|
|
20
|
+
|
|
21
|
+
Follow workflows/design.md for palette/font roles and named scales. Run
|
|
22
|
+
`lint_design_tokens` before launch and after major design changes. Review exact
|
|
23
|
+
colour replacements with scripts/typeroll/color_tokens.py; record deliberate
|
|
24
|
+
exceptions in decisions.md. Preserve text independently from design changes.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Preserve source media before retirement
|
|
2
|
+
|
|
3
|
+
This is a separate owner-approved archive, not an import of every old file into
|
|
4
|
+
the CMS. Do not delete source files or cancel old hosting as part of this routine.
|
|
5
|
+
|
|
6
|
+
1. Inventory originals and downloads from the source API/export, including files
|
|
7
|
+
absent from published pages. Include drafts, private records and historical
|
|
8
|
+
attachments when authorized. A public sitemap or REST response cannot prove
|
|
9
|
+
the library is complete. Keep the export under ignored `sources/private/`.
|
|
10
|
+
2. Compare with the new site's references, including templates, partials and
|
|
11
|
+
retained URLs. Record used, unused and uncertain files. "Unused" is not an
|
|
12
|
+
instruction to delete. Preserve print originals even when a web derivative exists.
|
|
13
|
+
3. Agree a private archive destination, its owning account, retention and access.
|
|
14
|
+
Verify account/bucket identity and that public bucket/custom-domain access is
|
|
15
|
+
disabled. Use the owner's credentials through their secret manager; never put
|
|
16
|
+
credentials in scripts or manifests. Do not provision or expose a bucket silently.
|
|
17
|
+
4. Copy with bounded concurrency and bounded reads, stream large files and use
|
|
18
|
+
resumable transfers when supported. Preserve a collision-safe key derived from
|
|
19
|
+
the full source host/path, not just the filename. Never overwrite a different
|
|
20
|
+
object merely because the basename matches.
|
|
21
|
+
5. Record source URL, destination key, source ID, byte count, SHA-256, media type,
|
|
22
|
+
title/alt/caption, copy time and verification state in a private manifest.
|
|
23
|
+
ETag is not universally MD5 (multipart objects and providers differ). Use a
|
|
24
|
+
documented checksum API or stream the destination back and compare SHA-256
|
|
25
|
+
and size. Metadata claiming a hash is not proof of the uploaded bytes.
|
|
26
|
+
6. Resume only verified matches; report every unavailable source and hash conflict.
|
|
27
|
+
Verify a restore from the archive, including a large file. Keep an independent
|
|
28
|
+
private copy of the manifest. The owner reviews completeness and grants a
|
|
29
|
+
separate old-host retirement decision. This archive does not preserve old
|
|
30
|
+
public URLs by itself: keep URL parity/redirect acceptance separate.
|
|
31
|
+
|
|
32
|
+
## Prepare print images for the web
|
|
33
|
+
|
|
34
|
+
Use `scripts/typeroll/prepare_image.py` for a reviewed local TIFF or other original.
|
|
35
|
+
Install Pillow with ImageCms in an isolated environment. It applies EXIF
|
|
36
|
+
orientation, converts through the embedded (or explicitly supplied) ICC profile
|
|
37
|
+
to sRGB, embeds the sRGB profile, strips source EXIF metadata and writes a new
|
|
38
|
+
JPEG. It never changes the original, uploads files or edits a Page.
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
python3 scripts/typeroll/prepare_image.py sources/private/original.tif sources/private/web.jpg
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
CMYK without the correct profile fails; obtain the actual source profile instead
|
|
45
|
+
of guessing. `--source-profile sources/private/original.icc` supplies it.
|
|
46
|
+
Unprofiled RGB/gray requires an explicit `--assume-srgb` decision. Multi-page,
|
|
47
|
+
animated and transparent images require a separate reviewed workflow. Compare
|
|
48
|
+
colour, subject framing and sharpness visually before upload. Upload through the
|
|
49
|
+
normal integrity-checked media flow and choose alt/caption text independently.
|
|
50
|
+
Store the tool's hash report privately; never commit customer images or archives.
|