@topy-ai/maggie 0.7.9 → 0.7.11
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 +37 -11
- package/bin/maggie.js +18 -21
- package/bundled-skills/README.md +0 -1
- package/bundled-skills/catalog.json +78 -0
- package/bundled-skills/maggie-dash/SKILL.md +39 -4
- package/bundled-skills/maggie-deployment/SKILL.md +6 -0
- package/bundled-skills/maggie-deployment/references/vps.md +33 -0
- package/bundled-skills/maggie-design/SKILL.md +30 -8
- package/bundled-skills/maggie-feedback/SKILL.md +2 -1
- package/bundled-skills/maggie-seo-geo/SKILL.md +7 -2
- package/bundled-templates/maggiedash/section-fanout.json +11 -0
- package/bundled-templates/maggiedash/section-registry.json +15 -0
- package/bundled-tools/clis/maggie_dash.py +80 -3
- package/bundled-tools/clis/maggie_design.py +134 -5
- package/bundled-tools/clis/maggie_feedback.py +3 -0
- package/bundled-tools/clis/maggie_icon_inventory.py +18 -6
- package/bundled-tools/clis/maggie_release.py +55 -0
- package/bundled-tools/clis/site_audit.py +34 -2
- package/bundled-tools/runtime/maggie_sections.py +140 -0
- package/bundled-tools/runtime/route_imports.py +23 -0
- package/bundled-tools/runtime/site_baseline.py +37 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -218,8 +218,8 @@ artifact schemas.
|
|
|
218
218
|
Recommended upgrade sequence for the current release:
|
|
219
219
|
|
|
220
220
|
```bash
|
|
221
|
-
npx @topy-ai/maggie@0.7.
|
|
222
|
-
npx @topy-ai/maggie@0.7.
|
|
221
|
+
npx @topy-ai/maggie@0.7.11 update --project . --force
|
|
222
|
+
npx @topy-ai/maggie@0.7.11 cleanup --project .
|
|
223
223
|
```
|
|
224
224
|
|
|
225
225
|
Maintainers should pass npm credentials through the repository helper, never
|
|
@@ -229,8 +229,11 @@ as a command-line argument:
|
|
|
229
229
|
node scripts/publish-npm.mjs --maggie-env-file ../.env
|
|
230
230
|
```
|
|
231
231
|
|
|
232
|
-
The 0.7.
|
|
233
|
-
|
|
232
|
+
The 0.7.11 workflow adds served-content equivalence checks, query-route
|
|
233
|
+
baseline exclusions, changed-surface render evidence gates, generated skill
|
|
234
|
+
catalogs, component-binding audits, and icon-family noise filtering. It also
|
|
235
|
+
includes the 0.7.9 nested section-field contracts, renderer-backed examples,
|
|
236
|
+
stable-ID preservation during migration, and disjoint page
|
|
234
237
|
inventory guidance. It retains the shared Google integrations runbook and
|
|
235
238
|
fail-closed provider capability matrix, alongside the installable MaggieDash admin distribution and
|
|
236
239
|
audited CMS operations (`cms revisions`,
|
|
@@ -248,6 +251,11 @@ observations; every route must include status, canonical, indexability, and
|
|
|
248
251
|
required metadata checks. Agent content writes require an explicit approved
|
|
249
252
|
origin, same-origin redirects, and recursive credential validation. Feedback
|
|
250
253
|
submissions persist and print only an allowlisted acknowledgement. The release
|
|
254
|
+
also adds image-aware content equivalence with explicit legacy-baseline
|
|
255
|
+
limitations, locale checks for stored image alt text, a validated label/value
|
|
256
|
+
`pairs` section, registry fan-out checks, idempotent reconciliation, per-step
|
|
257
|
+
and section-scoped design evidence, catch-all source confidence, shell-safe VPS
|
|
258
|
+
SSH guidance, and compatible feedback CLI examples. The release
|
|
251
259
|
retains the existing service matching,
|
|
252
260
|
localization, seed-manifest, lockfile/analytics, sitemap, deployment and
|
|
253
261
|
rollback workflows.
|
|
@@ -470,7 +478,6 @@ python3 tools/clis/maggie_design.py rebrand \
|
|
|
470
478
|
| `maggie-feedback` | Collect redacted feedback drafts and explicitly submit them to the NoBlox feedback endpoint |
|
|
471
479
|
| `maggie-auth-reference` | Generate and validate traditional email/password auth with secure server-side sessions |
|
|
472
480
|
| `maggie-blog` | Run a provider-neutral blog lifecycle with stable identity, topics, feeds, settings, and rollback |
|
|
473
|
-
| `maggie-google-capabilities` | Validate redacted Google provider evidence and separate read/report/edit/publish capability states |
|
|
474
481
|
|
|
475
482
|
`maggie-design` can initialize native blog and service UI plans from a local,
|
|
476
483
|
read-only structural reference:
|
|
@@ -490,13 +497,32 @@ maggie design validate-ui --project . \
|
|
|
490
497
|
This reuses the host project's homepage shell and `DESIGN.md`; it does not
|
|
491
498
|
copy reference branding, source code, private data, or provider facts.
|
|
492
499
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
500
|
+
For existing routes, in-place design jobs are resumable and can be narrowed to
|
|
501
|
+
one section. Catch-all routes remain `unproven` until their content source is
|
|
502
|
+
declared, and each completed step records evidence:
|
|
503
|
+
|
|
504
|
+
```bash
|
|
505
|
+
maggie design in-place --project . --route /pricing \
|
|
506
|
+
--content-source src/lib/pages/db.ts
|
|
507
|
+
maggie design step in-place-<id> --project . \
|
|
508
|
+
--step inspect-route --evidence .maggie/evidence/route.json
|
|
509
|
+
maggie design section --project . --route /pricing --section-id hours
|
|
510
|
+
maggie design section-validate --before before.json --after after.json \
|
|
511
|
+
--section-id hours
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
MaggieDash section contracts include a compact `pairs` band for factual
|
|
515
|
+
label/value rows. Validate required values, registry fan-out, locale sidecars,
|
|
516
|
+
and idempotent repairs with `maggie dash sections validate`,
|
|
517
|
+
`fanout-validate`, `locale-validate`, and `reconcile`.
|
|
497
518
|
|
|
498
|
-
The package
|
|
499
|
-
|
|
519
|
+
The package includes all 18 installable skills: `maggie-blog-bootstrap`,
|
|
520
|
+
`maggie-dash`, `maggie-clone`, `maggie-clone-to-template`, `maggie-marketplace`,
|
|
521
|
+
`maggie-template`, `maggie-design`, `maggie-ops`, `maggie-deployment`,
|
|
522
|
+
`maggie-project-context`, `maggie-seo-geo`, `maggie-social-share`,
|
|
523
|
+
`maggie-service-booking`, `maggie-memory`, `maggie-content-localization`,
|
|
524
|
+
`maggie-feedback`, `maggie-auth-reference`, and `maggie-blog`. Use the stable
|
|
525
|
+
commands below after installation:
|
|
500
526
|
|
|
501
527
|
```bash
|
|
502
528
|
maggie auth reference --project . --confirm
|
package/bin/maggie.js
CHANGED
|
@@ -17,26 +17,18 @@ const MARKETPLACE_ROOT = join(PACKAGE_ROOT, "bundled-marketplace");
|
|
|
17
17
|
const CONTRACTS_ROOT = join(PACKAGE_ROOT, "bundled-contracts");
|
|
18
18
|
const TEMPLATES_ROOT = join(PACKAGE_ROOT, "bundled-templates");
|
|
19
19
|
const STATE_DIR = ".maggie";
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"maggie-social-share",
|
|
33
|
-
"maggie-service-booking",
|
|
34
|
-
"maggie-memory",
|
|
35
|
-
"maggie-content-localization",
|
|
36
|
-
"maggie-feedback",
|
|
37
|
-
"maggie-auth-reference",
|
|
38
|
-
"maggie-blog",
|
|
39
|
-
];
|
|
20
|
+
function loadSkillNames() {
|
|
21
|
+
try {
|
|
22
|
+
return JSON.parse(readFileSync(join(SKILLS_ROOT, "catalog.json"), "utf8")).skills.map((skill) => skill.name);
|
|
23
|
+
} catch {
|
|
24
|
+
// Local regression tests may invoke the wrapper before package assembly.
|
|
25
|
+
// Discover real frontmatter directories and never resurrect retired content.
|
|
26
|
+
return readdirSync(SKILLS_ROOT, { withFileTypes: true })
|
|
27
|
+
.filter((entry) => entry.isDirectory() && entry.name !== "maggie-emdash" && existsSync(join(SKILLS_ROOT, entry.name, "SKILL.md")))
|
|
28
|
+
.map((entry) => entry.name).sort();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const SKILL_NAMES = loadSkillNames();
|
|
40
32
|
const RETIRED_PATHS = [
|
|
41
33
|
".agents/skills/maggie-emdash",
|
|
42
34
|
".claude/skills/maggie-emdash",
|
|
@@ -66,7 +58,8 @@ Usage:
|
|
|
66
58
|
maggie doctor [--project PATH]
|
|
67
59
|
maggie bootstrap interview [project]
|
|
68
60
|
maggie dash init|install|status|migrate|transition|variant|cms --project PATH [options]
|
|
69
|
-
maggie dash sections <validate|prompt|keys> [options]
|
|
61
|
+
maggie dash sections <validate|prompt|keys|remap-translations|fanout-validate|locale-validate|reconcile> [options]
|
|
62
|
+
maggie dash components-audit --bindings-file FILE --sections-file FILE --pages-file FILE
|
|
70
63
|
maggie dash status --project PATH
|
|
71
64
|
maggie dash migrate --project PATH --confirm
|
|
72
65
|
maggie content FILE --source PROVIDER --project PATH --confirm
|
|
@@ -92,6 +85,10 @@ Usage:
|
|
|
92
85
|
maggie design validate-ui --project PATH --plan PATH --rendered-dir PATH --confirm
|
|
93
86
|
maggie design icon-inventory --project PATH --source-dir src --runtime assets/icons.css --output docs/icon-inventory.json
|
|
94
87
|
maggie design author --project PATH --route /about --purpose TEXT --audience TEXT --confirm
|
|
88
|
+
maggie design in-place --project PATH --route /pricing [--content-source PATH]
|
|
89
|
+
maggie design section --project PATH --route /pricing --section-id ID
|
|
90
|
+
maggie design section-validate --before FILE --after FILE --section-id ID
|
|
91
|
+
maggie design step <job-id> --step NAME --evidence FILE
|
|
95
92
|
maggie auth reference --project PATH --confirm
|
|
96
93
|
maggie auth check --project PATH [--production]
|
|
97
94
|
maggie blog init|inspect|ingest|validate|publish|sitemap|settings|rollback|integration-state
|
package/bundled-skills/README.md
CHANGED
|
@@ -13,7 +13,6 @@ Skills are the agent-facing workflows. They compose with the tools in
|
|
|
13
13
|
| `maggie-template` | Discover, recommend, inspect, and apply a selected Astro homepage template | marketplace catalog, bootstrap state, template `DESIGN.md` |
|
|
14
14
|
| `maggie-design` | Fully clone authorized interior pages, then reconcile them to the homepage header/footer shell | browser MCP, clone planner CLI, completed Maggie homepage |
|
|
15
15
|
| `maggie-ops` | Build, connect, operate, upgrade, and verify the private blog operations dashboard | Ops dashboard contract, authenticated API bridge, host tests |
|
|
16
|
-
| `maggie-google-capabilities` | Validate a redacted Google provider capability matrix without inferring write access | Google integrations runbook, provider evidence |
|
|
17
16
|
| `maggie-deployment` | Deploy and verify a dynamic Maggie blog, Cloudflare-first | Cloudflare Workers, D1, R2, KV, Wrangler |
|
|
18
17
|
| `maggie-project-context` | Sync Project, voice, site and CTA context | project-context CLI/API |
|
|
19
18
|
| `maggie-seo-geo` | Plan, audit, create/rewrite and measure SEO/GEO | visibility, SEO audit, GSC/GA4, content quality |
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "maggie-skill-catalog.v1",
|
|
3
|
+
"generatedFrom": "skills/*/SKILL.md",
|
|
4
|
+
"skills": [
|
|
5
|
+
{
|
|
6
|
+
"name": "maggie-auth-reference",
|
|
7
|
+
"description": "Generate and validate a provider-neutral traditional email/password auth reference with secure server sessions and production security gates."
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"name": "maggie-blog",
|
|
11
|
+
"description": "Create and operate a provider-neutral blog with stable post identity, topics, draft approval, archive/post routes, RSS, sitemap, settings, and idempotent local ingest."
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"name": "maggie-blog-bootstrap",
|
|
15
|
+
"description": "Establish a MaggieDash-backed Astro blog or complete a small SEO/GEO-ready blog in an existing project."
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "maggie-clone",
|
|
19
|
+
"description": "Reverse-engineer an authorized website homepage and create the Maggie homepage foundation inside an existing blog project. Use with /maggie-clone plus a homepage URL when the user wants to replicate the homepage structure, header, footer, assets, responsive behavior, and interactions."
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"name": "maggie-clone-to-template",
|
|
23
|
+
"description": "Turn an authorized website URL and a design or copy prompt into a validated marketplace template through clone, asset, screenshot, comparison, revision, and packaging gates."
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"name": "maggie-content-localization",
|
|
27
|
+
"description": "Manage translation, polish, rewrite, market localization, review, stale detection, and publishing for pages, guides, posts, services, products, and categories."
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"name": "maggie-dash",
|
|
31
|
+
"description": "Manage the MaggieDash project foundation, local content store, and approval lifecycle."
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"name": "maggie-deployment",
|
|
35
|
+
"description": "Deploy and operate Maggie blog projects with Cloudflare Workers as the default target, while preserving an adapter boundary for VPS, GCP, and AWS."
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "maggie-design",
|
|
39
|
+
"description": "Design authorized interior pages, review rendered responsive layouts, or explicitly rebrand a packaged homepage/template. Use rebrand only with a named source brand and target brand."
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"name": "maggie-feedback",
|
|
43
|
+
"description": "Collect, review, and explicitly submit privacy-safe feedback about a Maggie skill run, workflow result, bug, or feature request."
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"name": "maggie-marketplace",
|
|
47
|
+
"description": "Build, rebrand, localise, validate, version, and apply reusable Astro marketplace templates from an authorised preview URL or local HTML export. Use when adding a design template, creating a neutral demo, or preparing a stable homepage starter for an Astro project."
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"name": "maggie-memory",
|
|
51
|
+
"description": "Persist and retrieve confirmed user preferences, project conventions, lessons from repaired mistakes, and structured error history across Maggie skill runs."
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"name": "maggie-ops",
|
|
55
|
+
"description": "Build, connect, operate, upgrade, and verify a private Maggie Ops dashboard for blog content, API Pull, sitemap matching, rewrite approvals, reports, and integrations. Use when the user asks for Ops/admin functionality or lifecycle operations rather than public blog pages."
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"name": "maggie-project-context",
|
|
59
|
+
"description": "Sync a site's safe AI CMO Project, selected brand voice, site settings, and CTA context into a local generated context file. Use when connecting a vibe-coded blog or existing website to AI CMO, refreshing brand context, or diagnosing missing CTA/project data."
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"name": "maggie-seo-geo",
|
|
63
|
+
"description": "Plan, audit, create, rewrite, and measure content for AI CMO's paid SEO and GEO workflow. Use for topic opportunities, AI visibility, technical SEO, extractable article structure, sitemap-based rewrites, GSC readback, or SEO/GEO client reports."
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"name": "maggie-service-booking",
|
|
67
|
+
"description": "Import, synchronise, validate, and design SPA service pages from a booking provider such as Fresha. Use for service catalogues, treatment variants, prices, durations, booking links, payment links, and booking-aware page generation."
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"name": "maggie-social-share",
|
|
71
|
+
"description": "Turn approved AI CMO articles and content assets into platform-native social drafts, scheduled posts, and measured distribution. Use for Social Share planning, LinkedIn/Facebook copy, CTA mapping, calendars, approvals, publish retries, or social performance readback."
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"name": "maggie-template",
|
|
75
|
+
"description": "Discover, recommend, inspect, and apply verified Astro homepage templates from the Maggie marketplace. Use when a user wants to start a site from a selected template, compare available designs, or choose a visual direction before maggie-clone or maggie-design."
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
}
|
|
@@ -177,6 +177,20 @@ maggie dash sections validate --registry templates/maggiedash/section-registry.j
|
|
|
177
177
|
maggie dash sections prompt --registry templates/maggiedash/section-registry.json
|
|
178
178
|
maggie dash sections keys --registry templates/maggiedash/section-registry.json \
|
|
179
179
|
--page-id <page-id> --sections-file .maggie/sections.json
|
|
180
|
+
maggie dash sections remap-translations \
|
|
181
|
+
--translations-file .maggie/translations-legacy.json \
|
|
182
|
+
--mapping-file .maggie/section-translation-map.json \
|
|
183
|
+
--output .maggie/translations-v2.json --confirm
|
|
184
|
+
|
|
185
|
+
# Validate locale sidecars for stored image alt text before publishing:
|
|
186
|
+
maggie dash sections locale-validate \
|
|
187
|
+
--page-id <page-id> --sections-file .maggie/sections.json \
|
|
188
|
+
--translations-file .maggie/translations-by-locale.json --locale zh-Hant
|
|
189
|
+
|
|
190
|
+
# Check that a host wired every registry type through all implementation surfaces:
|
|
191
|
+
maggie dash sections fanout-validate \
|
|
192
|
+
--registry templates/maggiedash/section-registry.json \
|
|
193
|
+
--fanout-file templates/maggiedash/section-fanout.json
|
|
180
194
|
```
|
|
181
195
|
|
|
182
196
|
The catalogue declares purpose, usage, placement, repeatability and layout
|
|
@@ -184,10 +198,24 @@ limits, renderer-owned examples, and the shape of repeated entries. A repeat
|
|
|
184
198
|
may contain an object (`title`, `body`, `href`, and so on), not just a count;
|
|
185
199
|
the nested limits are displayed to the planner and checked by `sections
|
|
186
200
|
validate`. Over-limit copy is an editor note; unknown types fail validation.
|
|
187
|
-
The
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
201
|
+
The registry is intentionally marked `catalogStatus: starter`, not presented
|
|
202
|
+
as a production-complete vocabulary. Hosts commonly add renderer-backed bands
|
|
203
|
+
such as `form`, `map`, `steps`, `checklist`, `compare`, `quote`, `timeline`,
|
|
204
|
+
`gallery`, `stats`, `split`, `glossary`, `credentials`, `team`, `before-after`,
|
|
205
|
+
`statement`, `features`, `scope`, `animated`, `opening`, `breadcrumb`, and
|
|
206
|
+
`bound-collection`; the machine-readable registry lists these examples. Every
|
|
207
|
+
added band must have a real-renderer preview or an explicitly labelled example
|
|
208
|
+
fallback at the supported breakpoints. Do not describe the starter vocabulary
|
|
209
|
+
as a fixed number of bands.
|
|
210
|
+
|
|
211
|
+
Required fields and repeated `pairs` values cannot be blank. The `pairs`
|
|
212
|
+
starter band is intended for compact label/value facts such as opening hours;
|
|
213
|
+
it is not a prose fallback. The registry fan-out manifest is a host integration
|
|
214
|
+
contract: when a host adds a type, update its type union/schema, blank state,
|
|
215
|
+
validation, readable-content extraction, renderer, and editor entry together,
|
|
216
|
+
then run `fanout-validate`. `locale-validate` checks stored image-alt sidecars;
|
|
217
|
+
the host adapter must write the section and all non-default locale rows in one
|
|
218
|
+
transaction. The JSON report is evidence, not a database write.
|
|
191
219
|
|
|
192
220
|
Translation keys use stable section IDs, with an array-index fallback only
|
|
193
221
|
until the ordered migration is complete. Migration preserves unique IDs that
|
|
@@ -195,6 +223,13 @@ already belong to the source section list; it generates a new ID only for a
|
|
|
195
223
|
missing, duplicate, or target-conflicting ID. Never rebuild IDs from array
|
|
196
224
|
position or overwrite existing translation keys.
|
|
197
225
|
|
|
226
|
+
When copy already lives under another keyspace, generate an explicit prefix
|
|
227
|
+
mapping from the converter and run `remap-translations`. The helper applies
|
|
228
|
+
longest prefixes first, preserves unmapped keys for review, and fails on
|
|
229
|
+
conflicting destinations. For a database adapter, apply the section rows and
|
|
230
|
+
translation remap in one transaction with the same migration ID; the JSON CLI
|
|
231
|
+
is the reviewable output form, not a substitute for the adapter's transaction.
|
|
232
|
+
|
|
198
233
|
Keep data-owned values out of page copy. For example, a pricing band should
|
|
199
234
|
store a service identifier or slug and let the live service/booking adapter
|
|
200
235
|
render current options and prices. Inventory reports must classify each
|
|
@@ -30,6 +30,12 @@ errors, missing assets, or visual placeholders. The result follows
|
|
|
30
30
|
`maggie-deployment-canary.v1`; never include cookies, authorization headers, or
|
|
31
31
|
response bodies in it.
|
|
32
32
|
|
|
33
|
+
When a release changes a visitor-facing HTML, CSS, component, or asset surface,
|
|
34
|
+
the release preflight automatically requires both a passing icon inventory and
|
|
35
|
+
rendered canary evidence. The canary must include screenshots, zero console or
|
|
36
|
+
network errors, and zero placeholder matches. Query-driven routes belong in
|
|
37
|
+
behavior/API checks, not static byte baselines.
|
|
38
|
+
|
|
33
39
|
## Automatic memory hook
|
|
34
40
|
|
|
35
41
|
Follow [Maggie Memory Hook](../../references/memory-hook.md) at invocation and completion.
|
|
@@ -37,6 +37,39 @@ systemd service, environment file, Nginx/TLS configuration, release root,
|
|
|
37
37
|
database/media migration and backup strategy, health checks, and rollback
|
|
38
38
|
owner before any remote mutation.
|
|
39
39
|
|
|
40
|
+
### SSH and secret-source contract
|
|
41
|
+
|
|
42
|
+
Use these canonical project variables when a VPS adapter is configured:
|
|
43
|
+
|
|
44
|
+
| Variable | Meaning | Precedence |
|
|
45
|
+
| --- | --- | --- |
|
|
46
|
+
| `NOBLOX_DEPLOY_HOST` | VPS hostname or IP | explicit CLI flag, then project `.env`, then provider secret manager |
|
|
47
|
+
| `NOBLOX_DEPLOY_SSH_USER` | SSH login user | explicit CLI flag, then project `.env`, then provider secret manager |
|
|
48
|
+
| `NOBLOX_DEPLOY_SSH_PORT` | SSH port | explicit CLI flag, then project `.env`, then provider secret manager |
|
|
49
|
+
| `NOBLOX_DEPLOY_SSH_KEY` | path or secret reference for the private key | explicit CLI flag, then project `.env`, then provider secret manager |
|
|
50
|
+
| `NOBLOX_DEPLOY_ENV_FILE` | remote runtime env-file reference | provider secret manager only for the secret values |
|
|
51
|
+
|
|
52
|
+
Legacy provider-specific names may be supported by an adapter, but must be
|
|
53
|
+
mapped to these names in the local plan. The SSH key is preferred. Do not put
|
|
54
|
+
an SSH password in a plan or command line; if password authentication is
|
|
55
|
+
unavoidable, resolve `NOBLOX_DEPLOY_SSH_PASSWORD` at execution time from the
|
|
56
|
+
approved secret manager and never echo it. Explicit CLI values override `.env`;
|
|
57
|
+
`.env` overrides the provider adapter's default names. Values are validated by
|
|
58
|
+
name and presence only, never printed.
|
|
59
|
+
|
|
60
|
+
When composing SSH options in Bash or zsh, use an array so whitespace is not
|
|
61
|
+
reinterpreted as extra arguments:
|
|
62
|
+
|
|
63
|
+
```zsh
|
|
64
|
+
ssh_opts=(-o StrictHostKeyChecking=accept-new -o BatchMode=yes)
|
|
65
|
+
ssh "${ssh_opts[@]}" -p "$NOBLOX_DEPLOY_SSH_PORT" \
|
|
66
|
+
"$NOBLOX_DEPLOY_SSH_USER@$NOBLOX_DEPLOY_HOST" "systemctl is-active example"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Do not use `ssh_opts='-o StrictHostKeyChecking=accept-new -o BatchMode=yes'`
|
|
70
|
+
followed by `ssh $ssh_opts ...`; zsh does not perform the word splitting that
|
|
71
|
+
this pattern assumes.
|
|
72
|
+
|
|
40
73
|
Create a local, secret-free plan before remote execution:
|
|
41
74
|
|
|
42
75
|
```bash
|
|
@@ -10,13 +10,33 @@ metadata:
|
|
|
10
10
|
## In-place route evidence
|
|
11
11
|
|
|
12
12
|
`maggie design in-place --project . --route /example` resolves local source
|
|
13
|
-
files and falls back to a discovered `[...slug]`/`[[...slug]]` file.
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
files and falls back to a discovered `[...slug]`/`[[...slug]]` file. A catch-all
|
|
14
|
+
is recorded as `resolution: unproven` unless `--content-source` names the
|
|
15
|
+
actual DB/content resolver. A source candidate is not proof that the concrete
|
|
16
|
+
URL is served: verify the HTTP response, content identity, and framework route
|
|
16
17
|
mapping before editing a shared catch-all. Next app-router catch-alls whose
|
|
17
|
-
file is named `page.tsx` are not covered by this filename fallback.
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
file is named `page.tsx` are not covered by this filename fallback.
|
|
19
|
+
|
|
20
|
+
In-place work is resumable. Record each completed step and its evidence:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
maggie design step in-place-<id> --project . \
|
|
24
|
+
--step inspect-route --evidence .maggie/evidence/route.json
|
|
25
|
+
maggie design status in-place-<id> --project .
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The job becomes `done` only after every required step has evidence; `ready`
|
|
29
|
+
means the contract exists, not that browser QA passed. For a one-band change,
|
|
30
|
+
use section-scoped mode and validate before/after evidence:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
maggie design section --project . --route /pricing --section-id hours
|
|
34
|
+
maggie design section-validate --before before.json --after after.json \
|
|
35
|
+
--section-id hours
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The validator requires the target section to change and proves that every
|
|
39
|
+
other section remains unchanged.
|
|
20
40
|
|
|
21
41
|
## Source-to-runtime icon inventory
|
|
22
42
|
|
|
@@ -34,8 +54,10 @@ Use `missing`, `unknown`, and `coverage` from the versioned
|
|
|
34
54
|
`maggie-icon-inventory.v1` report as the release gate. A binary font without a
|
|
35
55
|
name map is reported as evidence only; it must not be treated as proof that a
|
|
36
56
|
glyph exists. Fix the source token or add the runtime definition before
|
|
37
|
-
shipping.
|
|
38
|
-
|
|
57
|
+
shipping. The inventory compares a scoped runtime icon family (`ph-*`, `fa-*`,
|
|
58
|
+
or `lucide-*`), normalizes family prefixes, and ignores inline SVG IDs and font
|
|
59
|
+
filename noise. Do not paste private source, URLs with credentials, or user
|
|
60
|
+
data into the report.
|
|
39
61
|
|
|
40
62
|
## Automatic memory hook
|
|
41
63
|
|
|
@@ -16,7 +16,7 @@ general lesson or regression test.
|
|
|
16
16
|
## Collect locally
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
maggie feedback
|
|
19
|
+
maggie feedback --project . collect \
|
|
20
20
|
--skill maggie-clone-to-template --run-id clone-001 \
|
|
21
21
|
--type bug --phase visual-qa \
|
|
22
22
|
--summary "Mobile hero overflows the viewport" \
|
|
@@ -26,6 +26,7 @@ maggie feedback collect --project . \
|
|
|
26
26
|
--reproduce "Run the clone workflow" \
|
|
27
27
|
--reproduce "Open the mobile screenshot" \
|
|
28
28
|
--screenshot ./screenshots/mobile.png
|
|
29
|
+
# The equivalent `maggie feedback collect --project . ...` spelling is also supported.
|
|
29
30
|
maggie feedback preview .maggie/feedback/<feedback-id>.json --format markdown
|
|
30
31
|
```
|
|
31
32
|
|
|
@@ -83,8 +83,13 @@ automatically replace the baseline following failure. Dynamic dates, class
|
|
|
83
83
|
names and copy can produce legitimate differences requiring review.
|
|
84
84
|
|
|
85
85
|
This covers server-rendered sitemap pages, not CSS rendering, JavaScript-only
|
|
86
|
-
content, database translation keys or browser interactions.
|
|
87
|
-
|
|
86
|
+
content, database translation keys or browser interactions. Query-string URLs
|
|
87
|
+
are recorded as excluded from byte baselines because they often represent
|
|
88
|
+
search/filter state; use browser or API-specific tests for those states. The
|
|
89
|
+
baseline now compares a served-content contract (headings, paragraphs and
|
|
90
|
+
links) alongside metadata and structure, so a section migration cannot pass
|
|
91
|
+
merely because the HTML byte shape stayed similar. A source key change is
|
|
92
|
+
detected here only when it changes served content. Baselines contain site
|
|
88
93
|
metadata; do not publish private project contracts without permission.
|
|
89
94
|
|
|
90
95
|
## Browser behavior evidence
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "maggiedash-section-fanout.v1",
|
|
3
|
+
"description": "Starter host contract: every registry type must be wired through each implementation surface. Hosts extending the registry must regenerate this file.",
|
|
4
|
+
"typeUnion": ["cards", "cta", "faq", "hero", "links", "pairs", "prose"],
|
|
5
|
+
"sectionSchema": ["cards", "cta", "faq", "hero", "links", "pairs", "prose"],
|
|
6
|
+
"blank": ["cards", "cta", "faq", "hero", "links", "pairs", "prose"],
|
|
7
|
+
"validation": ["cards", "cta", "faq", "hero", "links", "pairs", "prose"],
|
|
8
|
+
"textExtraction": ["cards", "cta", "faq", "hero", "links", "pairs", "prose"],
|
|
9
|
+
"renderer": ["cards", "cta", "faq", "hero", "links", "pairs", "prose"],
|
|
10
|
+
"editor": ["cards", "cta", "faq", "hero", "links", "pairs", "prose"]
|
|
11
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "maggiedash-section-registry.v1",
|
|
3
3
|
"description": "The provider-neutral starter vocabulary used by page planners and editors. Hosts may extend it with renderer-backed section types.",
|
|
4
|
+
"catalogStatus": "starter",
|
|
5
|
+
"hostExtensionExamples": ["form", "map", "steps", "checklist", "compare", "quote", "timeline", "gallery", "stats", "split", "glossary", "credentials", "team", "before-after", "statement", "features", "scope", "animated", "opening", "breadcrumb", "bound-collection"],
|
|
4
6
|
"sections": [
|
|
5
7
|
{
|
|
6
8
|
"type": "hero",
|
|
@@ -31,6 +33,19 @@
|
|
|
31
33
|
],
|
|
32
34
|
"example": {"type": "prose", "heading": "Make the important part easier to understand", "paragraphs": ["Start with the decision your reader is trying to make.", "Then give them the context, evidence and next step in that order."]}
|
|
33
35
|
},
|
|
36
|
+
{
|
|
37
|
+
"type": "pairs",
|
|
38
|
+
"purpose": "Shows named facts as a compact label-and-value band.",
|
|
39
|
+
"usage": "Use for opening hours, specifications, eligibility, or other factual pairs; never for long prose.",
|
|
40
|
+
"position": "body",
|
|
41
|
+
"repeatable": true,
|
|
42
|
+
"fields": [
|
|
43
|
+
{"name": "heading", "usage": "What the facts describe.", "limit": 90, "required": true},
|
|
44
|
+
{"name": "rows", "usage": "Non-empty factual label/value pairs.", "limit": 0, "repeats": {"min": 1, "max": 12, "of": [{"name": "label", "usage": "The fact name.", "limit": 70, "required": true}, {"name": "value", "usage": "The concise fact value; never blank.", "limit": 180, "required": true}]}},
|
|
45
|
+
{"name": "note", "usage": "Optional clarification below the facts.", "limit": 220}
|
|
46
|
+
],
|
|
47
|
+
"example": {"type": "pairs", "heading": "At a glance", "rows": [{"label": "Typical response", "value": "Within one working day"}, {"label": "Available", "value": "Monday to Friday"}], "note": "Times may change on public holidays."}
|
|
48
|
+
},
|
|
34
49
|
{
|
|
35
50
|
"type": "cards",
|
|
36
51
|
"purpose": "Presents parallel points side by side.",
|
|
@@ -23,7 +23,8 @@ sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "runtime"))
|
|
|
23
23
|
from maggie_dash_store import MaggieDashStore # noqa: E402
|
|
24
24
|
from service_variants import ServiceVariantStore # noqa: E402
|
|
25
25
|
from maggie_dash_ui import load_and_validate # noqa: E402
|
|
26
|
-
from maggie_sections import catalogue, section_id_migration, validate_registry, copy_notes # noqa: E402
|
|
26
|
+
from maggie_sections import catalogue, remap_translations, section_id_migration, validate_registry, copy_notes, validate_values, validate_fanout, reconcile_fields, validate_locale_coverage # noqa: E402
|
|
27
|
+
from route_imports import classify_bindings # noqa: E402
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
def project_root(args: argparse.Namespace) -> Path:
|
|
@@ -319,6 +320,47 @@ def command_ui(args: argparse.Namespace) -> int:
|
|
|
319
320
|
|
|
320
321
|
|
|
321
322
|
def command_sections(args: argparse.Namespace) -> int:
|
|
323
|
+
if args.sections_command == "remap-translations":
|
|
324
|
+
require_confirm(args)
|
|
325
|
+
translations = json.loads(Path(args.translations_file).resolve().read_text(encoding="utf-8"))
|
|
326
|
+
mappings = json.loads(Path(args.mapping_file).resolve().read_text(encoding="utf-8"))
|
|
327
|
+
result = remap_translations(translations, mappings)
|
|
328
|
+
if result["passed"]:
|
|
329
|
+
output = Path(args.output).resolve()
|
|
330
|
+
if output.exists() and not args.force:
|
|
331
|
+
raise ValueError(f"output exists; use --force to replace: {output}")
|
|
332
|
+
output.parent.mkdir(parents=True, exist_ok=True)
|
|
333
|
+
output.write_text(json.dumps(result["translations"], indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
|
334
|
+
result["output"] = str(output)
|
|
335
|
+
emit(result)
|
|
336
|
+
return 0 if result["passed"] else 1
|
|
337
|
+
if args.sections_command == "fanout-validate":
|
|
338
|
+
registry = json.loads(Path(args.registry).resolve().read_text(encoding="utf-8"))
|
|
339
|
+
fanout = json.loads(Path(args.fanout_file).resolve().read_text(encoding="utf-8"))
|
|
340
|
+
checked = validate_registry(registry)
|
|
341
|
+
result = {**validate_fanout(registry, fanout), "registry": checked}
|
|
342
|
+
emit(result)
|
|
343
|
+
return 0 if result["passed"] and checked["passed"] else 1
|
|
344
|
+
if args.sections_command == "reconcile":
|
|
345
|
+
current = json.loads(Path(args.current_file).resolve().read_text(encoding="utf-8"))
|
|
346
|
+
desired = json.loads(Path(args.desired_file).resolve().read_text(encoding="utf-8"))
|
|
347
|
+
result = reconcile_fields(current, desired)
|
|
348
|
+
if result["passed"]:
|
|
349
|
+
require_confirm(args)
|
|
350
|
+
output = Path(args.output).resolve()
|
|
351
|
+
if output.exists() and not args.force:
|
|
352
|
+
raise ValueError(f"output exists; use --force to replace: {output}")
|
|
353
|
+
output.parent.mkdir(parents=True, exist_ok=True)
|
|
354
|
+
output.write_text(json.dumps(result["result"], indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
|
355
|
+
result["output"] = str(output)
|
|
356
|
+
emit(result)
|
|
357
|
+
return 0 if result["passed"] else 1
|
|
358
|
+
if args.sections_command == "locale-validate":
|
|
359
|
+
sections = json.loads(Path(args.sections_file).resolve().read_text(encoding="utf-8"))
|
|
360
|
+
translations = json.loads(Path(args.translations_file).resolve().read_text(encoding="utf-8"))
|
|
361
|
+
result = validate_locale_coverage(args.page_id, sections, translations, args.locale)
|
|
362
|
+
emit(result)
|
|
363
|
+
return 0 if result["passed"] else 1
|
|
322
364
|
registry = json.loads(Path(args.registry).resolve().read_text(encoding="utf-8"))
|
|
323
365
|
checked = validate_registry(registry)
|
|
324
366
|
if not checked["passed"]:
|
|
@@ -329,14 +371,31 @@ def command_sections(args: argparse.Namespace) -> int:
|
|
|
329
371
|
return 0
|
|
330
372
|
if args.sections_command == "validate":
|
|
331
373
|
sections = json.loads(Path(args.sections_file).resolve().read_text(encoding="utf-8"))
|
|
332
|
-
result = {**checked, "copy": copy_notes(registry, sections)}
|
|
374
|
+
result = {**checked, "copy": copy_notes(registry, sections), "values": validate_values(registry, sections)}
|
|
333
375
|
emit(result)
|
|
334
|
-
return 0 if result["copy"]["passed"] else 1
|
|
376
|
+
return 0 if result["copy"]["passed"] and result["values"]["passed"] else 1
|
|
335
377
|
sections = json.loads(Path(args.sections_file).resolve().read_text(encoding="utf-8"))
|
|
336
378
|
emit(section_id_migration(args.page_id, sections))
|
|
337
379
|
return 0
|
|
338
380
|
|
|
339
381
|
|
|
382
|
+
def command_components_audit(args: argparse.Namespace) -> int:
|
|
383
|
+
bindings = json.loads(Path(args.bindings_file).resolve().read_text(encoding="utf-8"))
|
|
384
|
+
sections = json.loads(Path(args.sections_file).resolve().read_text(encoding="utf-8"))
|
|
385
|
+
pages = json.loads(Path(args.pages_file).resolve().read_text(encoding="utf-8"))
|
|
386
|
+
result = classify_bindings(
|
|
387
|
+
bindings if isinstance(bindings, list) else bindings.get("bindings", []),
|
|
388
|
+
set(sections if isinstance(sections, list) else sections.get("paths", [])),
|
|
389
|
+
set(pages if isinstance(pages, list) else pages.get("paths", [])),
|
|
390
|
+
)
|
|
391
|
+
if args.output:
|
|
392
|
+
output = Path(args.output).resolve(); output.parent.mkdir(parents=True, exist_ok=True)
|
|
393
|
+
output.write_text(json.dumps(result, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
|
394
|
+
result["output"] = str(output)
|
|
395
|
+
emit(result)
|
|
396
|
+
return 0 if result["passed"] else 1
|
|
397
|
+
|
|
398
|
+
|
|
340
399
|
def variant_store(args: argparse.Namespace) -> ServiceVariantStore:
|
|
341
400
|
return ServiceVariantStore(project_root(args) / ".maggie" / "service-variants.json")
|
|
342
401
|
|
|
@@ -449,7 +508,25 @@ def parser() -> argparse.ArgumentParser:
|
|
|
449
508
|
sections_prompt.set_defaults(func=command_sections)
|
|
450
509
|
sections_keys = sections_sub.add_parser("keys")
|
|
451
510
|
sections_keys.add_argument("--registry", required=True); sections_keys.add_argument("--sections-file", required=True); sections_keys.add_argument("--page-id", required=True)
|
|
511
|
+
sections_remap = sections_sub.add_parser("remap-translations", help="move an existing translation keyspace into stable section keys")
|
|
512
|
+
sections_remap.add_argument("--translations-file", required=True); sections_remap.add_argument("--mapping-file", required=True); sections_remap.add_argument("--output", required=True); sections_remap.add_argument("--force", action="store_true"); sections_remap.add_argument("--confirm", action="store_true")
|
|
513
|
+
sections_remap.set_defaults(func=command_sections)
|
|
514
|
+
sections_fanout = sections_sub.add_parser("fanout-validate", help="check registry type fan-out across host implementation surfaces")
|
|
515
|
+
sections_fanout.add_argument("--registry", required=True); sections_fanout.add_argument("--fanout-file", required=True)
|
|
516
|
+
sections_fanout.set_defaults(func=command_sections)
|
|
517
|
+
sections_reconcile = sections_sub.add_parser("reconcile", help="apply an idempotent compare-and-set JSON repair")
|
|
518
|
+
sections_reconcile.add_argument("--current-file", required=True); sections_reconcile.add_argument("--desired-file", required=True); sections_reconcile.add_argument("--output", required=True); sections_reconcile.add_argument("--force", action="store_true"); sections_reconcile.add_argument("--confirm", action="store_true")
|
|
519
|
+
sections_reconcile.set_defaults(func=command_sections)
|
|
520
|
+
sections_locale = sections_sub.add_parser("locale-validate", help="check locale sidecar coverage for stored image alt fields")
|
|
521
|
+
sections_locale.add_argument("--page-id", required=True); sections_locale.add_argument("--sections-file", required=True); sections_locale.add_argument("--translations-file", required=True); sections_locale.add_argument("--locale", action="append", required=True)
|
|
522
|
+
sections_locale.set_defaults(func=command_sections)
|
|
452
523
|
sections_keys.set_defaults(func=command_sections)
|
|
524
|
+
components = sub.add_parser("components-audit", help="classify route component bindings against live section/page inventories")
|
|
525
|
+
components.add_argument("--bindings-file", required=True)
|
|
526
|
+
components.add_argument("--sections-file", required=True, help="JSON array/object of section-managed paths")
|
|
527
|
+
components.add_argument("--pages-file", required=True, help="JSON array/object of live page paths")
|
|
528
|
+
components.add_argument("--output")
|
|
529
|
+
components.set_defaults(func=command_components_audit)
|
|
453
530
|
variant = sub.add_parser("variant", help="manage service variant lifecycle")
|
|
454
531
|
variant_sub = variant.add_subparsers(dest="variant_command", required=True)
|
|
455
532
|
create = variant_sub.add_parser("create"); create.add_argument("--project", default="."); create.add_argument("--service-id", required=True); create.add_argument("--variant-id", required=True); create.add_argument("--variant-type", required=True); create.add_argument("--locale", required=True); create.add_argument("--market", required=True); create.add_argument("--slug", required=True); create.add_argument("--title", required=True); create.add_argument("--facts", required=True); create.add_argument("--source-revision", required=True); create.add_argument("--canonical-variant-id"); create.add_argument("--cluster-link", action="append", default=[]); create.add_argument("--layout-family", default="service-default"); create.add_argument("--confirm", action="store_true")
|