@topy-ai/maggie 0.7.35 → 0.7.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-zh-TW.md +45 -2
- package/README.md +108 -4
- package/bin/maggie.js +76 -19
- package/bundled-contracts/maggie-seo/model-policy-v2.schema.json +29 -0
- package/bundled-contracts/maggie-service-booking/notification-lifecycle-v1.schema.json +29 -0
- package/bundled-contracts/maggiedash/README.md +3 -0
- package/bundled-contracts/maggiedash/booking-access-v1.json +29 -0
- package/bundled-contracts/maggiedash/booking-customer-surface-v1.json +46 -0
- package/bundled-contracts/maggiedash/booking-email-templates-v1.json +38 -0
- package/bundled-contracts/maggiedash/booking-host-adapter-v1.json +76 -0
- package/bundled-contracts/maggiedash/booking-ops-evidence-v1.json +19 -0
- package/bundled-contracts/maggiedash/execution-board.json +1886 -0
- package/bundled-contracts/maggiedash/stripe-booking-capabilities-v1.json +68 -0
- package/bundled-skills/README.md +1 -0
- package/bundled-skills/catalog.json +4 -0
- package/bundled-skills/maggie-blog-bootstrap/SKILL.md +6 -0
- package/bundled-skills/maggie-booking/SKILL.md +462 -0
- package/bundled-skills/maggie-design/SKILL.md +15 -0
- package/bundled-skills/maggie-seo-geo/SKILL.md +24 -2
- package/bundled-skills/maggie-service-booking/SKILL.md +19 -0
- package/bundled-templates/astro-blog/starter/README.md +4 -0
- package/bundled-templates/astro-blog/starter/src/styles/global.css +6 -0
- package/bundled-tools/clis/maggie.py +36 -2
- package/bundled-tools/clis/maggie_booking.py +1080 -0
- package/bundled-tools/clis/maggie_dash.py +181 -5
- package/bundled-tools/clis/maggie_release.py +24 -0
- package/bundled-tools/clis/maggie_service_booking.py +38 -2
- package/bundled-tools/clis/site_audit.py +114 -15
- package/bundled-tools/runtime/site_baseline.py +11 -1
- package/package.json +1 -1
|
@@ -10,7 +10,9 @@ metadata:
|
|
|
10
10
|
## Versioned Gemini policy
|
|
11
11
|
|
|
12
12
|
AI-assisted SEO, localization, and content generation must resolve models
|
|
13
|
-
through one explicit policy: `maggie-gemini-model-policy.v1
|
|
13
|
+
through one explicit policy: `maggie-gemini-model-policy.v1` for legacy local
|
|
14
|
+
routes or `contracts/maggie-seo/model-policy-v2.schema.json` for provider-routed
|
|
15
|
+
work. Pin the primary
|
|
14
16
|
model, declare a separate fallback, and record the policy version, exact
|
|
15
17
|
prompt/schema versions, locale, and source revision with generated evidence.
|
|
16
18
|
Only model availability failures (retired model, quota, timeout, or provider
|
|
@@ -18,6 +20,13 @@ Only model availability failures (retired model, quota, timeout, or provider
|
|
|
18
20
|
invalid output remain errors. Do not scatter retired model defaults across
|
|
19
21
|
routes.
|
|
20
22
|
|
|
23
|
+
Provider-routed work must use policy v2 and declare the provider, endpoint
|
|
24
|
+
environment variable, API-key environment variable, billing mode (`byok` or
|
|
25
|
+
`managed`), and a finite `maxTokens` limit. A route is not considered BYOK
|
|
26
|
+
merely because a key exists in the process; the selected endpoint and billing
|
|
27
|
+
path must be recorded in the policy evidence. Never print key values or send a
|
|
28
|
+
provider secret in a report.
|
|
29
|
+
|
|
21
30
|
## Static audit and sitemap evidence
|
|
22
31
|
|
|
23
32
|
Run `maggie site-audit https://example.com --crawl --json` for sitemap-listed
|
|
@@ -129,6 +138,13 @@ maggie site-audit https://example.com --crawl --json \
|
|
|
129
138
|
--reason 'https://example.com/pricing=approved pricing refresh'
|
|
130
139
|
```
|
|
131
140
|
|
|
141
|
+
When the same approved reason applies to every drift, use `--reason-all` or
|
|
142
|
+
`--reason '*=approved site-wide stylesheet rebuild'`. A URL-specific reason can
|
|
143
|
+
still override the site-wide reason. If a page changes between its first and
|
|
144
|
+
second fetch, the comparison reports it as `unstable` rather than silently
|
|
145
|
+
calling it a content or markup change; review that page and supply a reason
|
|
146
|
+
before recapture.
|
|
147
|
+
|
|
132
148
|
The command fails closed when the current crawl is incomplete, a drift has no
|
|
133
149
|
reason, a reason names a URL without a reported drift, or the destination file
|
|
134
150
|
already exists. The new contract records its `baselineId`, `supersedes` value,
|
|
@@ -249,7 +265,9 @@ accepts JPEG, PNG, GIF, and ICO responses. It reports missing, unreachable,
|
|
|
249
265
|
undersized, unreadable, or unsupported images and warns when one source is
|
|
250
266
|
used for at least 75% of a sample of three or more pages. Page-specific
|
|
251
267
|
first-band images remain an editorial choice and should be declared by the
|
|
252
|
-
host manifest.
|
|
268
|
+
host manifest. When `docs/social-cards-pages.json` or
|
|
269
|
+
`docs/public-urls.json` exists, `maggie release` includes the social-card audit
|
|
270
|
+
in its read-only release path.
|
|
253
271
|
|
|
254
272
|
Head-tag auditing consumes a host-produced JSON manifest with `url`, `shell`,
|
|
255
273
|
and normalized `tags`. It compares structural declarations for icons,
|
|
@@ -281,6 +299,10 @@ is intended; the report then says `"declared-origin"`.
|
|
|
281
299
|
Use `--output <project>/docs/site-audit.json` to persist the evidence used by
|
|
282
300
|
the release review.
|
|
283
301
|
|
|
302
|
+
For a full crawl, add `--check-internal-links` to follow same-origin HTML links
|
|
303
|
+
and check HTTP 200 targets plus `#fragment` IDs. External, mailto, and other
|
|
304
|
+
non-site links are intentionally out of scope.
|
|
305
|
+
|
|
284
306
|
- **Plan:** combine Project Context, GSC/search signals, competitor gaps, and
|
|
285
307
|
AI visibility prompts into topic clusters and briefs.
|
|
286
308
|
- **Audit:** inspect robots, sitemap, canonical URLs, metadata, JSON-LD,
|
|
@@ -186,6 +186,25 @@ evidence artifact and validates the selected ending (`pending`, `redirect`,
|
|
|
186
186
|
signals, and sitemap exclusion. It never stores response bodies or decides a
|
|
187
187
|
redirect destination for the host.
|
|
188
188
|
|
|
189
|
+
## Appointment time contract
|
|
190
|
+
|
|
191
|
+
Any booking or seasonal variant that receives a timezone-naive wall-clock
|
|
192
|
+
timestamp must carry an IANA `venueTimezone` such as `Europe/London`. The
|
|
193
|
+
shared parser in `tools/clis/maggie_service_booking.py` applies venue DST rules
|
|
194
|
+
instead of the server timezone. An ambiguous DST time must also declare
|
|
195
|
+
`venueTimeFold: 0` or `1`; nonexistent local times are rejected. Prefer an
|
|
196
|
+
offset-bearing timestamp when the provider already supplies one. This is a
|
|
197
|
+
validation contract only: it does not claim that a provider supports booking
|
|
198
|
+
creation or confirmation.
|
|
199
|
+
|
|
200
|
+
When a host implements its own notification adapter, use
|
|
201
|
+
[`notification-lifecycle-v1.schema.json`](../../bundled-contracts/maggie-service-booking/notification-lifecycle-v1.schema.json)
|
|
202
|
+
to represent acknowledgement, studio notification, confirmation, alternative,
|
|
203
|
+
and cancellation events. Every event needs an idempotency key and explicit
|
|
204
|
+
status. A request must remain `pending-confirmation` until the host/provider
|
|
205
|
+
actually confirms it; an email or UI receipt must never imply an appointment
|
|
206
|
+
was booked. The contract is provider-neutral and does not send mail by itself.
|
|
207
|
+
|
|
189
208
|
## Asynchronous onboarding contract
|
|
190
209
|
|
|
191
210
|
Provider onboarding is three independent state machines: `import` owns the
|
|
@@ -88,6 +88,10 @@ with `WORDPRESS_API_TOKEN` or a server-only application-password pair.
|
|
|
88
88
|
The starter includes a Tailwind v4 Vite entry point for projects that choose
|
|
89
89
|
Tailwind during bootstrap. It remains compatible with the existing contract
|
|
90
90
|
surface and does not require replacing the host's design system.
|
|
91
|
+
The entry point explicitly scopes Tailwind v4 scanning to renderable Astro
|
|
92
|
+
pages, layouts, and components. API/lib fixtures and prose in server-only files
|
|
93
|
+
are not treated as utility-class sources; keep that scope when adding a host
|
|
94
|
+
component.
|
|
91
95
|
|
|
92
96
|
`npm run maggie:*` uses the installed `maggie` binary. It is portable after the
|
|
93
97
|
starter is copied out of this monorepo; it no longer depends on repository-
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
@import "tailwindcss";
|
|
2
2
|
|
|
3
|
+
/* Scan renderable Astro surfaces only. Server fixtures and prose in API/lib
|
|
4
|
+
files must not become accidental utility classes in the shipped bundle. */
|
|
5
|
+
@source "../pages/**/*.astro";
|
|
6
|
+
@source "../layouts/**/*.astro";
|
|
7
|
+
@source "../components/**/*.{astro,jsx,tsx}";
|
|
8
|
+
|
|
3
9
|
/* The starter keeps its contract UI framework-neutral while providing a
|
|
4
10
|
working Tailwind v4 entry point for projects that select Tailwind. */
|
|
@@ -23,6 +23,7 @@ STATE_FILE = "bootstrap-state.json"
|
|
|
23
23
|
IGNORED_DIRS = {".git", ".next", "node_modules", "dist", "build", "coverage", "generated"}
|
|
24
24
|
TEXT_SUFFIXES = {".css", ".html", ".js", ".jsx", ".md", ".mdx", ".svelte", ".ts", ".tsx", ".vue", ".astro", ".njk", ".liquid", ".json", ".yaml", ".yml"}
|
|
25
25
|
DOCTOR_NON_SITE_DIRS = {".agents", ".claude", "__tests__", "contracts", "docs", "examples", "fixtures", "marketplace", "references", "scripts", "test", "tests", "tools"}
|
|
26
|
+
DOCTOR_SERVER_DIRS = {"api", "data", "lib", "server", "services"}
|
|
26
27
|
WORKFLOW_PHASES = (
|
|
27
28
|
"maggiedash-foundation",
|
|
28
29
|
"project-research",
|
|
@@ -81,6 +82,26 @@ def doctor_source_files(root: Path, files: list[Path]) -> list[Path]:
|
|
|
81
82
|
return result
|
|
82
83
|
|
|
83
84
|
|
|
85
|
+
def doctor_placeholder_files(root: Path, source_files: list[Path], rendered_dir: str | None = None) -> tuple[list[Path], str]:
|
|
86
|
+
"""Choose rendered output first, with a narrow public-source fallback.
|
|
87
|
+
|
|
88
|
+
Server fixtures and CLI examples are not shipped page metadata and must not
|
|
89
|
+
fail a public-site placeholder check. A build output directory is the
|
|
90
|
+
strongest evidence; ``--rendered-dir`` makes that choice explicit for CI.
|
|
91
|
+
"""
|
|
92
|
+
candidates = [Path(rendered_dir).expanduser().resolve()] if rendered_dir else [root / name for name in ("dist", "build", "out", "public")]
|
|
93
|
+
for candidate in candidates:
|
|
94
|
+
if not candidate.is_dir():
|
|
95
|
+
continue
|
|
96
|
+
files = sorted(path for path in candidate.rglob("*") if path.is_file() and path.suffix.lower() in TEXT_SUFFIXES)
|
|
97
|
+
return files, "rendered-output"
|
|
98
|
+
fallback = [
|
|
99
|
+
path for path in source_files
|
|
100
|
+
if not any(part.lower() in DOCTOR_SERVER_DIRS for part in path.relative_to(root).parts[:-1])
|
|
101
|
+
]
|
|
102
|
+
return fallback, "public-site-source"
|
|
103
|
+
|
|
104
|
+
|
|
84
105
|
def package_info(root: Path) -> tuple[dict, Path | None]:
|
|
85
106
|
candidates = [root / "package.json"] + sorted(path for path in root.glob("*/package.json") if path.parent.name not in IGNORED_DIRS)
|
|
86
107
|
parsed: list[tuple[int, dict, Path]] = []
|
|
@@ -819,6 +840,8 @@ def command_doctor(args: argparse.Namespace) -> int:
|
|
|
819
840
|
files = list(files_for(root))
|
|
820
841
|
source_files = doctor_source_files(root, files)
|
|
821
842
|
text = "\n".join(read_text(path) for path in source_files)
|
|
843
|
+
placeholder_files, placeholder_scope = doctor_placeholder_files(root, source_files, getattr(args, "rendered_dir", None))
|
|
844
|
+
placeholder_text = "\n".join(read_text(path) for path in placeholder_files)
|
|
822
845
|
paths = [evidence(path, root).lower() for path in source_files]
|
|
823
846
|
|
|
824
847
|
checks = {
|
|
@@ -838,7 +861,7 @@ def command_doctor(args: argparse.Namespace) -> int:
|
|
|
838
861
|
"jsonld_dates": "datePublished" in text and ("dateModified" in text or "updatedAt" in text),
|
|
839
862
|
"published_only_guard": "published" in text.lower() and any(token in text.lower() for token in {"sitemap", "post"}),
|
|
840
863
|
"no_public_api_key": not bool(re.search(r"(?:PUBLIC|NEXT_PUBLIC)[A-Z0-9_]*(?:API|KEY|TOKEN)", text, re.IGNORECASE)),
|
|
841
|
-
"no_placeholder_metadata": not any(token in
|
|
864
|
+
"no_placeholder_metadata": not any(token in placeholder_text.lower() for token in {"your-domain.com", "example.com", "lorem ipsum", "replace-me"}),
|
|
842
865
|
}
|
|
843
866
|
if args.strict:
|
|
844
867
|
checks.update({
|
|
@@ -886,7 +909,7 @@ def command_doctor(args: argparse.Namespace) -> int:
|
|
|
886
909
|
safe_skips.append("robots route requires framework confirmation")
|
|
887
910
|
for check, reason in (("canonical_metadata", "canonical metadata needs a site-specific URL"), ("meta_description", "metadata content needs editorial input"), ("article_jsonld", "JSON-LD shape needs content-specific fields")):
|
|
888
911
|
if not checks[check]: safe_skips.append(reason)
|
|
889
|
-
result = {"project_root": str(root), "placeholder_scan": {"scope":
|
|
912
|
+
result = {"project_root": str(root), "placeholder_scan": {"scope": placeholder_scope, "files": len(placeholder_files)}, "checks": {key: {"ok": value} for key, value in checks.items()}, "passed": all(checks.values())}
|
|
890
913
|
if getattr(args, "fix_safe", False): result["safe_fixes"] = {"applied": safe_fixes, "skipped_requires_confirmation": safe_skips}
|
|
891
914
|
print(json.dumps(result, indent=2))
|
|
892
915
|
return 0 if result["passed"] else 1
|
|
@@ -940,6 +963,13 @@ def command_feedback(args: argparse.Namespace) -> int:
|
|
|
940
963
|
return completed.returncode
|
|
941
964
|
|
|
942
965
|
|
|
966
|
+
def command_booking(args: argparse.Namespace) -> int:
|
|
967
|
+
"""Dispatch Booking contract checks through the canonical Booking CLI."""
|
|
968
|
+
cli = Path(__file__).with_name("maggie_booking.py")
|
|
969
|
+
completed = subprocess.run([sys.executable, str(cli), *args.booking_args], cwd=Path.cwd(), check=False)
|
|
970
|
+
return completed.returncode
|
|
971
|
+
|
|
972
|
+
|
|
943
973
|
def parser() -> argparse.ArgumentParser:
|
|
944
974
|
p = argparse.ArgumentParser(prog="maggie", description=__doc__)
|
|
945
975
|
sub = p.add_subparsers(dest="command", required=True)
|
|
@@ -994,12 +1024,16 @@ def parser() -> argparse.ArgumentParser:
|
|
|
994
1024
|
feedback = sub.add_parser("feedback", help="collect, review, and submit privacy-safe feedback")
|
|
995
1025
|
feedback.add_argument("feedback_args", nargs=argparse.REMAINDER, help="arguments for maggie_feedback.py")
|
|
996
1026
|
feedback.set_defaults(func=command_feedback)
|
|
1027
|
+
booking = sub.add_parser("booking", help="inspect and validate the MaggieDash Booking workspace")
|
|
1028
|
+
booking.add_argument("booking_args", nargs=argparse.REMAINDER, help="arguments for maggie_booking.py")
|
|
1029
|
+
booking.set_defaults(func=command_booking)
|
|
997
1030
|
doctor = sub.add_parser("doctor", help="check blog routes and SEO output invariants")
|
|
998
1031
|
doctor.add_argument("project", nargs="?", default=".")
|
|
999
1032
|
doctor.add_argument("--require-bootstrap", action="store_true")
|
|
1000
1033
|
doctor.add_argument("--strict", action="store_true", help="also require the complete AI-native blog and Ops surface")
|
|
1001
1034
|
doctor.add_argument("--ci", action="store_true", help="CI alias for strict checks")
|
|
1002
1035
|
doctor.add_argument("--fix-safe", action="store_true", help="apply only low-risk route fixes; never changes schema, slugs, canonical URLs or publishing state")
|
|
1036
|
+
doctor.add_argument("--rendered-dir", help="scan this build output for placeholder metadata instead of source fixtures")
|
|
1003
1037
|
doctor.set_defaults(func=command_doctor)
|
|
1004
1038
|
for name in ("routes", "schema", "seo", "migration", "env"):
|
|
1005
1039
|
check = sub.add_parser(name, help=f"run Maggie {name} contract checks")
|