@topy-ai/maggie 0.7.44 → 0.7.46
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 +29 -6
- package/README.md +86 -12
- package/bin/maggie.js +207 -58
- package/bundled-contracts/google-integrations/capability-report-v2.schema.json +76 -0
- package/bundled-contracts/google-integrations/external-write-readback-v1.schema.json +36 -0
- package/bundled-contracts/maggie-deployment/deployer-delegation-v1.schema.json +20 -0
- package/bundled-contracts/maggie-deployment/release-profile-v1.schema.json +34 -0
- package/bundled-contracts/maggie-design/browser-capability-v1.schema.json +33 -0
- package/bundled-contracts/maggie-feedback/evidence-bundle-v1.schema.json +38 -0
- package/bundled-references/browser-inspection.md +17 -0
- package/bundled-references/google-integrations-runbook.md +8 -1
- package/bundled-skills/maggie-blog-bootstrap/SKILL.md +7 -7
- package/bundled-skills/maggie-clone/SKILL.md +21 -14
- package/bundled-skills/maggie-clone-to-template/SKILL.md +11 -11
- package/bundled-skills/maggie-clone-to-template/references/workflow.md +2 -2
- package/bundled-skills/maggie-content-localization/SKILL.md +10 -10
- package/bundled-skills/maggie-deployment/SKILL.md +57 -23
- package/bundled-skills/maggie-deployment/references/vps.md +4 -4
- package/bundled-skills/maggie-design/SKILL.md +12 -12
- package/bundled-skills/maggie-feedback/SKILL.md +23 -0
- package/bundled-skills/maggie-marketplace/SKILL.md +12 -12
- package/bundled-skills/maggie-ops/SKILL.md +28 -6
- package/bundled-skills/maggie-project-context/SKILL.md +1 -1
- package/bundled-skills/maggie-seo-geo/SKILL.md +6 -3
- package/bundled-skills/maggie-service-booking/SKILL.md +24 -24
- package/bundled-skills/maggie-social-share/SKILL.md +2 -2
- package/bundled-skills/maggie-template/SKILL.md +5 -5
- package/bundled-tools/clis/maggie_analytics.py +8 -0
- package/bundled-tools/clis/maggie_browser_audit.py +13 -2
- package/bundled-tools/clis/maggie_deployment.py +167 -6
- package/bundled-tools/clis/maggie_feedback.py +115 -1
- package/bundled-tools/clis/maggie_ops.py +33 -0
- package/bundled-tools/clis/maggie_release.py +124 -19
- package/bundled-tools/runtime/browser_capability.py +143 -0
- package/bundled-tools/runtime/external_write.py +122 -0
- package/bundled-tools/runtime/google_capabilities.py +37 -5
- package/package.json +1 -1
- package/references/browser-inspection.md +17 -0
- package/references/google-integrations-runbook.md +8 -1
package/bin/maggie.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
5
5
|
import { spawnSync } from "node:child_process";
|
|
6
|
+
import { homedir } from "node:os";
|
|
6
7
|
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
7
8
|
import { fileURLToPath } from "node:url";
|
|
8
9
|
|
|
@@ -16,6 +17,8 @@ const MARKETPLACE_ROOT = join(PACKAGE_ROOT, "bundled-marketplace");
|
|
|
16
17
|
const CONTRACTS_ROOT = join(PACKAGE_ROOT, "bundled-contracts");
|
|
17
18
|
const TEMPLATES_ROOT = join(PACKAGE_ROOT, "bundled-templates");
|
|
18
19
|
const STATE_DIR = ".maggie";
|
|
20
|
+
const USER_SCOPE = "user";
|
|
21
|
+
const PROJECT_SCOPE = "project";
|
|
19
22
|
function loadSkillNames() {
|
|
20
23
|
try {
|
|
21
24
|
return JSON.parse(readFileSync(join(SKILLS_ROOT, "catalog.json"), "utf8")).skills.map((skill) => skill.name);
|
|
@@ -45,17 +48,23 @@ const RETIRED_PATHS = [
|
|
|
45
48
|
"docs/ai-cmo-emdash",
|
|
46
49
|
];
|
|
47
50
|
const RETIRED_REFERENCE_FILES = ["CLAUDE.md", ".claude/CLAUDE.md", ".agents/AGENTS.md", ".agents/README.md"];
|
|
51
|
+
const USER_RETIRED_PATHS = [
|
|
52
|
+
"skills/maggie-emdash",
|
|
53
|
+
"skills/building-emdash-site",
|
|
54
|
+
"skills/creating-plugins",
|
|
55
|
+
"skills/emdash-cli",
|
|
56
|
+
"references/emdash-integration.md",
|
|
57
|
+
];
|
|
48
58
|
|
|
49
59
|
function usage() {
|
|
50
60
|
console.log(`Maggie Skills installer
|
|
51
61
|
|
|
52
62
|
Usage:
|
|
53
63
|
maggie --version | version
|
|
54
|
-
maggie init [--project PATH] [--agent auto|codex|claude|all] [--skills LIST]
|
|
55
|
-
maggie
|
|
56
|
-
maggie update [SKILL ...] [--project PATH] [--agent auto|codex|claude|all] [--force]
|
|
64
|
+
maggie init|install [--scope project|user|--global] [--project PATH] [--agent auto|codex|claude|all] [--skills LIST]
|
|
65
|
+
maggie update [SKILL ...] [--scope project|user|--global] [--project PATH] [--agent auto|codex|claude|all] [--force]
|
|
57
66
|
maggie list
|
|
58
|
-
maggie doctor [--project PATH]
|
|
67
|
+
maggie doctor [--scope project|user|--global] [--project PATH]
|
|
59
68
|
maggie bootstrap interview [project]
|
|
60
69
|
maggie dash init|install|status|migrate|transition|variant|cms --project PATH [options]
|
|
61
70
|
maggie booking install|worker-schedule|inspect|setup|contract|runtime-validate|ops-validate|release-gate|stripe-audit|tasks --project PATH [options]
|
|
@@ -77,8 +86,9 @@ Usage:
|
|
|
77
86
|
maggie content FILE --source PROVIDER --project PATH --confirm
|
|
78
87
|
maggie agent-content write --url URL --allowed-origin ORIGIN --token-env ENV --payload FILE
|
|
79
88
|
maggie bootstrap phase <start|pass|fail|status> <phase> [project]
|
|
80
|
-
maggie remove [SKILL ...] [--project PATH] [--agent codex|claude|all]
|
|
81
|
-
maggie cleanup --project PATH [--confirm]
|
|
89
|
+
maggie remove [SKILL ...] [--scope project|user|--global] [--project PATH] [--agent codex|claude|all]
|
|
90
|
+
maggie cleanup [--scope project|user|--global] [--project PATH] [--confirm]
|
|
91
|
+
maggie tool <bundled-cli.py> [arguments]
|
|
82
92
|
maggie marketplace add <template-url> clone <preview-url> --rename <id>
|
|
83
93
|
maggie marketplace plan --workspace-id ID --merchant-id ID --site-slug SLUG --cal-url URL [--output FILE]
|
|
84
94
|
maggie marketplace list
|
|
@@ -143,7 +153,7 @@ Usage:
|
|
|
143
153
|
maggie brand-kit --manifest FILE | --project PATH --design DESIGN.md [--write --confirm]
|
|
144
154
|
maggie npm11 --project PATH [--smoke --confirm]
|
|
145
155
|
maggie booking-delivery [--policy FILE]
|
|
146
|
-
maggie release PATH --environment staging --target vps-with-cloudflare-dns
|
|
156
|
+
maggie release PATH --profile .maggie/release-profile.json --environment staging --target vps-with-cloudflare-dns
|
|
147
157
|
maggie api lifecycle --project PATH [--execute --allow-quota]
|
|
148
158
|
maggie memory <init|list|search|context|add|record-error|transition|export> --project PATH
|
|
149
159
|
maggie localization <extract|plan|generate|preview|validate|review|publish|stale|glossary> [options]
|
|
@@ -153,7 +163,7 @@ Usage:
|
|
|
153
163
|
maggie site-audit URL [--crawl] [--access-log FILE] [--require-sitemap-request] [--languages en-GB,es-MX,ja-JP] [--check-hreflang] [--check-internal-links]
|
|
154
164
|
maggie site-audit URL --crawl --save-baseline FILE --reviewer NAME
|
|
155
165
|
maggie site-audit URL --crawl --baseline FILE [--reason-all REASON]
|
|
156
|
-
maggie browser-audit URL --browse PATH --output DIR --required SELECTOR [--sticky SELECTOR] [--interactions FILE]
|
|
166
|
+
maggie browser-audit URL --browse PATH --output DIR [--required SELECTOR] [--sticky SELECTOR] [--interactions FILE] [--check-browser]
|
|
157
167
|
maggie verification coverage --contract FILE --evidence FILE
|
|
158
168
|
maggie ops audit|preflight|verify|lockfiles|seed-manifest|google-capabilities|favicon-check --project PATH
|
|
159
169
|
maggie ops google-capabilities --project PATH --report FILE [--output FILE]
|
|
@@ -180,6 +190,29 @@ function projectRoot(args) {
|
|
|
180
190
|
return resolve(option(args, "--project", process.cwd()));
|
|
181
191
|
}
|
|
182
192
|
|
|
193
|
+
function installScope(args) {
|
|
194
|
+
const explicit = option(args, "--scope");
|
|
195
|
+
const globalFlag = args.includes("--global");
|
|
196
|
+
if (globalFlag && explicit && explicit !== USER_SCOPE) throw new Error("--global cannot be combined with --scope project");
|
|
197
|
+
const scope = globalFlag ? USER_SCOPE : (explicit || PROJECT_SCOPE);
|
|
198
|
+
if (![PROJECT_SCOPE, USER_SCOPE].includes(scope)) throw new Error(`unknown install scope: ${scope}`);
|
|
199
|
+
return scope;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function userAgentRoot(agent) {
|
|
203
|
+
if (agent === "codex") return resolve(process.env.CODEX_HOME || join(homedir(), ".codex"));
|
|
204
|
+
if (agent === "claude") return resolve(process.env.CLAUDE_CONFIG_DIR || join(homedir(), ".claude"));
|
|
205
|
+
throw new Error(`unknown agent: ${agent}`);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function userStateRoot() {
|
|
209
|
+
return resolve(process.env.MAGGIE_HOME || join(homedir(), ".config", "maggie"));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function installManifestPath(root, scope) {
|
|
213
|
+
return scope === USER_SCOPE ? join(userStateRoot(), "install.json") : join(root, STATE_DIR, "install.json");
|
|
214
|
+
}
|
|
215
|
+
|
|
183
216
|
function selectedSkills(args) {
|
|
184
217
|
const requested = values(args, "--skills").concat(args.filter((item) => SKILL_NAMES.includes(item)));
|
|
185
218
|
const unique = [...new Set(requested)];
|
|
@@ -190,8 +223,12 @@ function selectedSkills(args) {
|
|
|
190
223
|
}
|
|
191
224
|
|
|
192
225
|
function diskSkillNames(root) {
|
|
226
|
+
return diskSkillNamesFromRoots([join(root, ".agents"), join(root, ".claude")]);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function diskSkillNamesFromRoots(agentRoots) {
|
|
193
230
|
const installed = new Set();
|
|
194
|
-
for (const agentRoot of
|
|
231
|
+
for (const agentRoot of agentRoots) {
|
|
195
232
|
const skillsRoot = join(agentRoot, "skills");
|
|
196
233
|
if (!existsSync(skillsRoot)) continue;
|
|
197
234
|
for (const entry of readdirSync(skillsRoot, { withFileTypes: true })) {
|
|
@@ -216,9 +253,29 @@ function mergedSkillNames(root, selected) {
|
|
|
216
253
|
.filter((skill) => SKILL_NAMES.includes(skill)).sort();
|
|
217
254
|
}
|
|
218
255
|
|
|
219
|
-
function
|
|
256
|
+
function mergedUserSkillNames(roots) {
|
|
257
|
+
return [...new Set(Object.values(userSkillInventory(roots)).flat())].sort();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function userSkillInventory(roots) {
|
|
261
|
+
return Object.fromEntries(roots.map((agentRoot) => [
|
|
262
|
+
agentRoot,
|
|
263
|
+
[...diskSkillNamesFromRoots([agentRoot])].filter((skill) => SKILL_NAMES.includes(skill)).sort(),
|
|
264
|
+
]));
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function allUserAgentRoots() {
|
|
268
|
+
return [userAgentRoot("codex"), userAgentRoot("claude")];
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function agentRoots(args, root, scope = PROJECT_SCOPE) {
|
|
220
272
|
const requested = option(args, "--agent", "auto");
|
|
221
273
|
if (!["auto", "codex", "claude", "all"].includes(requested)) throw new Error(`unknown agent: ${requested}`);
|
|
274
|
+
if (scope === USER_SCOPE) {
|
|
275
|
+
if (requested === "codex") return [userAgentRoot("codex")];
|
|
276
|
+
if (requested === "claude") return [userAgentRoot("claude")];
|
|
277
|
+
return [userAgentRoot("codex"), userAgentRoot("claude")];
|
|
278
|
+
}
|
|
222
279
|
if (requested === "codex") return [join(root, ".agents")];
|
|
223
280
|
if (requested === "claude") return [join(root, ".claude")];
|
|
224
281
|
if (requested === "all") return [join(root, ".agents"), join(root, ".claude")];
|
|
@@ -367,26 +424,45 @@ function managedFileDigests(root, roots, skills, previousManagedFiles = {}) {
|
|
|
367
424
|
return Object.fromEntries(Object.entries(managedFiles).sort(([a], [b]) => a.localeCompare(b)));
|
|
368
425
|
}
|
|
369
426
|
|
|
370
|
-
function
|
|
427
|
+
function userManagedPairs(roots, skills) {
|
|
428
|
+
const pairs = [];
|
|
429
|
+
const add = (source, target) => { if (existsSync(source) && existsSync(target)) pairs.push([source, target]); };
|
|
430
|
+
for (const agentRoot of roots) {
|
|
431
|
+
for (const skill of skills) add(join(SKILLS_ROOT, skill), join(agentRoot, "skills", skill));
|
|
432
|
+
add(REFERENCES_ROOT, join(agentRoot, "references"));
|
|
433
|
+
}
|
|
434
|
+
return pairs;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function userManagedFileDigests(roots, skills, previousManagedFiles = {}) {
|
|
438
|
+
const managedFiles = {};
|
|
439
|
+
for (const [source, target] of userManagedPairs(roots, skills)) collectManagedFiles(source, target, "/", managedFiles, previousManagedFiles);
|
|
440
|
+
return Object.fromEntries(Object.entries(managedFiles).sort(([a], [b]) => a.localeCompare(b)));
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function readInstallManifest(root, scope = PROJECT_SCOPE) {
|
|
371
444
|
try {
|
|
372
|
-
const path =
|
|
445
|
+
const path = installManifestPath(root, scope);
|
|
373
446
|
return existsSync(path) ? JSON.parse(readFileSync(path, "utf8")) : {};
|
|
374
447
|
} catch {
|
|
375
448
|
return {};
|
|
376
449
|
}
|
|
377
450
|
}
|
|
378
451
|
|
|
379
|
-
function writeInstallManifest(root, data) {
|
|
380
|
-
const stateDir = join(root, STATE_DIR);
|
|
452
|
+
function writeInstallManifest(root, data, scope = PROJECT_SCOPE) {
|
|
453
|
+
const stateDir = scope === USER_SCOPE ? userStateRoot() : join(root, STATE_DIR);
|
|
381
454
|
mkdirSync(stateDir, { recursive: true });
|
|
382
455
|
writeFileSync(join(stateDir, "install.json"), JSON.stringify(data, null, 2) + "\n");
|
|
383
456
|
}
|
|
384
457
|
|
|
385
458
|
function install(args) {
|
|
459
|
+
const scope = installScope(args);
|
|
386
460
|
const root = projectRoot(args);
|
|
387
461
|
const skills = selectedSkills(args);
|
|
388
462
|
const force = args.includes("--force");
|
|
389
|
-
const roots = agentRoots(args, root);
|
|
463
|
+
const roots = agentRoots(args, root, scope);
|
|
464
|
+
const previousManifest = scope === USER_SCOPE ? readInstallManifest(root, scope) : {};
|
|
465
|
+
const previousManagedFiles = previousManifest.managedFiles && typeof previousManifest.managedFiles === "object" ? previousManifest.managedFiles : {};
|
|
390
466
|
if (!existsSync(SKILLS_ROOT)) throw new Error("bundled skills are missing; run npm pack from the package source");
|
|
391
467
|
for (const agentRoot of roots) {
|
|
392
468
|
for (const skill of skills) {
|
|
@@ -395,25 +471,32 @@ function install(args) {
|
|
|
395
471
|
}
|
|
396
472
|
if (existsSync(REFERENCES_ROOT)) copyIfMissing(REFERENCES_ROOT, join(agentRoot, "references"), force);
|
|
397
473
|
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
474
|
+
if (scope === PROJECT_SCOPE) {
|
|
475
|
+
const tools = join(root, "tools");
|
|
476
|
+
if (existsSync(TOOLS_ROOT)) {
|
|
477
|
+
for (const group of ["clis", "runtime", "integrations"]) {
|
|
478
|
+
if (existsSync(join(TOOLS_ROOT, group))) {
|
|
479
|
+
const target = join(tools, group);
|
|
480
|
+
if (existsSync(target)) syncTree(join(TOOLS_ROOT, group), target, force);
|
|
481
|
+
else copyIfMissing(join(TOOLS_ROOT, group), target, force);
|
|
482
|
+
}
|
|
405
483
|
}
|
|
406
484
|
}
|
|
485
|
+
if (existsSync(join(DESIGN_ROOT, "SPA-DESIGN.md"))) {
|
|
486
|
+
copyIfMissing(join(DESIGN_ROOT, "SPA-DESIGN.md"), join(root, ".maggie", "design-reference", "SPA-DESIGN.md"));
|
|
487
|
+
}
|
|
488
|
+
if (existsSync(MARKETPLACE_ROOT)) copyIfMissing(MARKETPLACE_ROOT, join(root, "marketplace"));
|
|
489
|
+
if (existsSync(CONTRACTS_ROOT)) copyIfMissing(CONTRACTS_ROOT, join(root, "contracts"));
|
|
490
|
+
if (existsSync(join(TEMPLATES_ROOT, "maggiedash"))) copyIfMissing(join(TEMPLATES_ROOT, "maggiedash"), join(root, "templates", "maggiedash"));
|
|
407
491
|
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
console.log(`
|
|
416
|
-
console.log("Run `maggie doctor --project .` before using mutating workflows.");
|
|
492
|
+
const manifestRoots = scope === USER_SCOPE ? allUserAgentRoots() : roots;
|
|
493
|
+
const installedUserSkills = scope === USER_SCOPE ? mergedUserSkillNames(manifestRoots) : [];
|
|
494
|
+
const manifest = scope === USER_SCOPE
|
|
495
|
+
? { scope, version: PACKAGE_VERSION, agents: manifestRoots, skills: installedUserSkills, skillsByAgent: userSkillInventory(manifestRoots), managedFiles: userManagedFileDigests(manifestRoots, installedUserSkills, previousManagedFiles), installed_at: new Date().toISOString() }
|
|
496
|
+
: { scope, version: PACKAGE_VERSION, agents: roots.map((item) => item.slice(root.length + 1)), skills: mergedSkillNames(root, skills), managedFiles: managedFileDigests(root, roots, skills), installed_at: new Date().toISOString() };
|
|
497
|
+
writeInstallManifest(root, manifest, scope);
|
|
498
|
+
console.log(scope === USER_SCOPE ? `Maggie installed for this user in ${userStateRoot()}` : `Maggie installed in ${root}`);
|
|
499
|
+
console.log(scope === USER_SCOPE ? "Project state remains local; run `maggie doctor --scope user` to verify the user install." : "Run `maggie doctor --project .` before using mutating workflows.");
|
|
417
500
|
}
|
|
418
501
|
|
|
419
502
|
function update(args) {
|
|
@@ -421,14 +504,16 @@ function update(args) {
|
|
|
421
504
|
usage();
|
|
422
505
|
return;
|
|
423
506
|
}
|
|
507
|
+
const scope = installScope(args);
|
|
424
508
|
const root = projectRoot(args);
|
|
425
509
|
const skills = selectedSkills(args);
|
|
426
510
|
const force = args.includes("--force");
|
|
427
|
-
const roots = agentRoots(args, root);
|
|
428
|
-
const previousManifest = readInstallManifest(root);
|
|
511
|
+
const roots = agentRoots(args, root, scope);
|
|
512
|
+
const previousManifest = readInstallManifest(root, scope);
|
|
429
513
|
const previousManagedFiles = previousManifest.managedFiles && typeof previousManifest.managedFiles === "object" ? previousManifest.managedFiles : {};
|
|
430
514
|
if (!existsSync(SKILLS_ROOT)) throw new Error("bundled skills are missing; run npm pack from the package source");
|
|
431
515
|
let updated = 0;
|
|
516
|
+
const managedRoot = scope === USER_SCOPE ? "/" : root;
|
|
432
517
|
for (const agentRoot of roots) {
|
|
433
518
|
for (const skill of skills) {
|
|
434
519
|
const source = join(SKILLS_ROOT, skill);
|
|
@@ -437,23 +522,30 @@ function update(args) {
|
|
|
437
522
|
console.log(`absent ${target} (run install to add it)`);
|
|
438
523
|
continue;
|
|
439
524
|
}
|
|
440
|
-
updated += syncTree(source, target, force,
|
|
525
|
+
updated += syncTree(source, target, force, managedRoot, previousManagedFiles);
|
|
441
526
|
}
|
|
442
|
-
if (existsSync(REFERENCES_ROOT) && existsSync(join(agentRoot, "references"))) updated += syncTree(REFERENCES_ROOT, join(agentRoot, "references"), force,
|
|
527
|
+
if (existsSync(REFERENCES_ROOT) && existsSync(join(agentRoot, "references"))) updated += syncTree(REFERENCES_ROOT, join(agentRoot, "references"), force, managedRoot, previousManagedFiles);
|
|
443
528
|
}
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
const
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
529
|
+
if (scope === PROJECT_SCOPE) {
|
|
530
|
+
const tools = join(root, "tools");
|
|
531
|
+
if (existsSync(TOOLS_ROOT)) for (const group of ["clis", "runtime", "integrations"]) if (existsSync(join(TOOLS_ROOT, group))) {
|
|
532
|
+
const target = join(tools, group);
|
|
533
|
+
if (existsSync(target)) updated += syncTree(join(TOOLS_ROOT, group), target, force, root, previousManagedFiles);
|
|
534
|
+
}
|
|
535
|
+
if (existsSync(join(DESIGN_ROOT, "SPA-DESIGN.md")) && existsSync(join(root, ".maggie", "design-reference"))) {
|
|
536
|
+
updated += syncTree(DESIGN_ROOT, join(root, ".maggie", "design-reference"), force, root, previousManagedFiles);
|
|
537
|
+
}
|
|
538
|
+
if (existsSync(MARKETPLACE_ROOT) && existsSync(join(root, "marketplace"))) updated += syncTree(MARKETPLACE_ROOT, join(root, "marketplace"), force, root, previousManagedFiles);
|
|
539
|
+
if (existsSync(CONTRACTS_ROOT) && existsSync(join(root, "contracts"))) updated += syncTree(CONTRACTS_ROOT, join(root, "contracts"), force, root, previousManagedFiles);
|
|
540
|
+
if (existsSync(join(TEMPLATES_ROOT, "maggiedash")) && existsSync(join(root, "templates", "maggiedash"))) updated += syncTree(join(TEMPLATES_ROOT, "maggiedash"), join(root, "templates", "maggiedash"), force, root, previousManagedFiles);
|
|
451
541
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
542
|
+
const manifestRoots = scope === USER_SCOPE ? allUserAgentRoots() : roots;
|
|
543
|
+
const updatedUserSkills = scope === USER_SCOPE ? mergedUserSkillNames(manifestRoots) : [];
|
|
544
|
+
const manifest = scope === USER_SCOPE
|
|
545
|
+
? { scope, version: PACKAGE_VERSION, agents: manifestRoots, skills: updatedUserSkills, skillsByAgent: userSkillInventory(manifestRoots), managedFiles: userManagedFileDigests(manifestRoots, updatedUserSkills, previousManagedFiles), updated_at: new Date().toISOString() }
|
|
546
|
+
: { scope, version: PACKAGE_VERSION, agents: roots.map((item) => item.slice(root.length + 1)), skills: mergedSkillNames(root, skills), managedFiles: managedFileDigests(root, roots, skills, previousManagedFiles), updated_at: new Date().toISOString() };
|
|
547
|
+
writeInstallManifest(root, manifest, scope);
|
|
548
|
+
console.log(`Maggie ${scope} update complete: ${updated} files changed`);
|
|
457
549
|
if (!force) console.log("Local files with changes were preserved. Review the output and rerun with --force only when replacement is intended.");
|
|
458
550
|
}
|
|
459
551
|
|
|
@@ -505,6 +597,23 @@ function workflowCli(name, args) {
|
|
|
505
597
|
process.exitCode = result.status ?? 1;
|
|
506
598
|
}
|
|
507
599
|
|
|
600
|
+
function tool(args) {
|
|
601
|
+
const [name, ...forwarded] = args;
|
|
602
|
+
if (!name || name.includes("/") || name.includes("\\") || name.includes("..") || !name.endsWith(".py")) {
|
|
603
|
+
throw new Error("tool requires one bundled CLI filename such as maggie_design.py");
|
|
604
|
+
}
|
|
605
|
+
const root = projectRoot(forwarded);
|
|
606
|
+
const script = toolScript(name, root);
|
|
607
|
+
if (!existsSync(script)) throw new Error(`bundled CLI is missing: ${name}`);
|
|
608
|
+
const result = spawnSync("python3", [script, ...forwarded], {
|
|
609
|
+
stdio: "inherit",
|
|
610
|
+
cwd: root,
|
|
611
|
+
env: { ...process.env, MAGGIE_VERSION: PACKAGE_VERSION },
|
|
612
|
+
});
|
|
613
|
+
if (result.error) throw result.error;
|
|
614
|
+
process.exitCode = result.status ?? 1;
|
|
615
|
+
}
|
|
616
|
+
|
|
508
617
|
function toolScript(name, root) {
|
|
509
618
|
const bundled = join(TOOLS_ROOT, "clis", name);
|
|
510
619
|
return existsSync(bundled) ? bundled : join(root, "tools", "clis", name);
|
|
@@ -515,7 +624,25 @@ function version() {
|
|
|
515
624
|
}
|
|
516
625
|
|
|
517
626
|
function doctor(args) {
|
|
627
|
+
const scope = installScope(args);
|
|
518
628
|
const root = projectRoot(args);
|
|
629
|
+
if (scope === USER_SCOPE) {
|
|
630
|
+
const roots = agentRoots(args, root, scope);
|
|
631
|
+
const checks = roots.map((agentRoot) => [agentRoot, existsSync(join(agentRoot, "skills"))]);
|
|
632
|
+
for (const [agentRoot, passed] of checks) console.log(`${passed ? "PASS" : "INFO"} ${agentRoot}/skills`);
|
|
633
|
+
const manifest = readInstallManifest(root, scope);
|
|
634
|
+
const expected = manifest?.skillsByAgent && typeof manifest.skillsByAgent === "object"
|
|
635
|
+
? [...new Set(roots.flatMap((agentRoot) => Array.isArray(manifest.skillsByAgent[agentRoot]) ? manifest.skillsByAgent[agentRoot] : []))].filter((skill) => SKILL_NAMES.includes(skill)).sort()
|
|
636
|
+
: Array.isArray(manifest?.skills) ? [...new Set(manifest.skills)].filter((skill) => SKILL_NAMES.includes(skill)) : null;
|
|
637
|
+
const installedNames = new Set([...diskSkillNamesFromRoots(roots)].filter((skill) => SKILL_NAMES.includes(skill)));
|
|
638
|
+
const missing = expected ? expected.filter((skill) => !installedNames.has(skill)) : [];
|
|
639
|
+
const inventoryState = expected && missing.length ? "INCOMPLETE" : "PASS";
|
|
640
|
+
console.log(`${inventoryState} user-skill-inventory manifest=${expected?.length ?? "unknown"} on-disk=${installedNames.size}${missing.length ? ` missing=${missing.join(",")}` : ""}`);
|
|
641
|
+
console.log(`INFO user-install-manifest=${installManifestPath(root, scope)}`);
|
|
642
|
+
console.log(`INFO installed-skills=${installedNames.size}`);
|
|
643
|
+
if (inventoryState === "INCOMPLETE") process.exitCode = 1;
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
519
646
|
const checks = [
|
|
520
647
|
["project", existsSync(root)],
|
|
521
648
|
["bootstrap-state", existsSync(join(root, ".maggie", "bootstrap-state.json"))],
|
|
@@ -538,9 +665,11 @@ function doctor(args) {
|
|
|
538
665
|
}
|
|
539
666
|
|
|
540
667
|
function remove(args) {
|
|
668
|
+
const scope = installScope(args);
|
|
541
669
|
const root = projectRoot(args);
|
|
542
670
|
const skills = selectedSkills(args);
|
|
543
|
-
|
|
671
|
+
const roots = agentRoots(args, root, scope);
|
|
672
|
+
for (const agentRoot of roots) {
|
|
544
673
|
for (const skill of skills) {
|
|
545
674
|
const target = join(agentRoot, "skills", skill);
|
|
546
675
|
if (existsSync(target)) {
|
|
@@ -549,31 +678,50 @@ function remove(args) {
|
|
|
549
678
|
} else console.log(`absent ${target}`);
|
|
550
679
|
}
|
|
551
680
|
}
|
|
681
|
+
if (scope === USER_SCOPE) {
|
|
682
|
+
const manifestRoots = allUserAgentRoots();
|
|
683
|
+
const remainingSkills = mergedUserSkillNames(manifestRoots);
|
|
684
|
+
writeInstallManifest(root, {
|
|
685
|
+
scope,
|
|
686
|
+
version: PACKAGE_VERSION,
|
|
687
|
+
agents: manifestRoots,
|
|
688
|
+
skills: remainingSkills,
|
|
689
|
+
skillsByAgent: userSkillInventory(manifestRoots),
|
|
690
|
+
managedFiles: userManagedFileDigests(manifestRoots, remainingSkills),
|
|
691
|
+
updated_at: new Date().toISOString(),
|
|
692
|
+
}, scope);
|
|
693
|
+
}
|
|
552
694
|
}
|
|
553
695
|
|
|
554
696
|
function cleanup(args) {
|
|
697
|
+
const scope = installScope(args);
|
|
555
698
|
const root = projectRoot(args);
|
|
556
|
-
const
|
|
557
|
-
|
|
558
|
-
|
|
699
|
+
const roots = scope === USER_SCOPE ? agentRoots(args, root, scope) : [root];
|
|
700
|
+
const retiredPaths = scope === USER_SCOPE ? USER_RETIRED_PATHS : RETIRED_PATHS;
|
|
701
|
+
const existing = roots.flatMap((base) => retiredPaths
|
|
702
|
+
.map((relativePath) => ({ relativePath, target: join(base, relativePath) }))
|
|
703
|
+
.filter(({ target }) => existsSync(target)));
|
|
704
|
+
|
|
559
705
|
if (!existing.length) {
|
|
560
|
-
console.log(
|
|
706
|
+
console.log(`Maggie cleanup (${scope}): no retired artifacts found`);
|
|
561
707
|
} else {
|
|
562
708
|
for (const { relativePath, target } of existing) console.log(`${args.includes("--confirm") ? "removing" : "would remove"} ${relativePath}`);
|
|
563
709
|
if (args.includes("--confirm")) {
|
|
564
710
|
for (const { target } of existing) rmSync(target, { recursive: true, force: false });
|
|
565
|
-
console.log(`Maggie cleanup complete: removed ${existing.length} retired artifact(s)`);
|
|
711
|
+
console.log(`Maggie cleanup (${scope}) complete: removed ${existing.length} retired artifact(s)`);
|
|
566
712
|
} else {
|
|
567
713
|
console.log("Nothing was deleted. Rerun with --confirm to remove these retired artifacts.");
|
|
568
714
|
}
|
|
569
715
|
}
|
|
570
|
-
|
|
571
|
-
const
|
|
572
|
-
|
|
573
|
-
|
|
716
|
+
if (scope === PROJECT_SCOPE) {
|
|
717
|
+
for (const relativePath of RETIRED_REFERENCE_FILES) {
|
|
718
|
+
const target = join(root, relativePath);
|
|
719
|
+
if (existsSync(target) && /emdash|em-dash|building-emdash-site|creating-plugins/i.test(readFileSync(target, "utf8"))) {
|
|
720
|
+
console.log(`review required ${relativePath}: contains a retired EmDash reference`);
|
|
721
|
+
}
|
|
574
722
|
}
|
|
723
|
+
console.log("Migration history under docs/de-emdash-* and marketplace/.maggie were preserved.");
|
|
575
724
|
}
|
|
576
|
-
console.log("Migration history under docs/de-emdash-* and marketplace/.maggie were preserved.");
|
|
577
725
|
}
|
|
578
726
|
|
|
579
727
|
const [command = "help", ...args] = process.argv.slice(2);
|
|
@@ -623,6 +771,7 @@ try {
|
|
|
623
771
|
else if (command === "booking") workflowCli("maggie_booking.py", args);
|
|
624
772
|
else if (command === "agent-content") workflowCli("maggie_agent_content.py", args);
|
|
625
773
|
else if (command === "content") workflowCli("maggie_content.py", args);
|
|
774
|
+
else if (command === "tool") tool(args);
|
|
626
775
|
else if (command === "init" || command === "install") install(args);
|
|
627
776
|
else if (command === "update") update(args);
|
|
628
777
|
else if (command === "doctor") doctor(args);
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://maggie.noblox.app/contracts/google-integrations/capability-report-v2.json",
|
|
4
|
+
"title": "Maggie Google integrations capability report v2",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["schemaVersion", "generatedAt", "providerResults", "passed", "mutationsAllowed"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"schemaVersion": {"const": "maggie-google-capability-report.v2"},
|
|
10
|
+
"generatedAt": {"type": "string", "minLength": 1},
|
|
11
|
+
"providerResults": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/providerResult"}},
|
|
12
|
+
"passed": {"type": "boolean"},
|
|
13
|
+
"mutationsAllowed": {"const": false}
|
|
14
|
+
},
|
|
15
|
+
"$defs": {
|
|
16
|
+
"providerResult": {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"additionalProperties": false,
|
|
19
|
+
"required": ["provider", "resource", "activeAccount", "target", "authMode", "scopes", "productRole", "evidence", "capabilities", "nextAction"],
|
|
20
|
+
"properties": {
|
|
21
|
+
"provider": {"enum": ["gsc", "ga4", "gtm", "google-ads", "firebase"]},
|
|
22
|
+
"resource": {"type": "string", "minLength": 1, "maxLength": 200},
|
|
23
|
+
"activeAccount": {"$ref": "#/$defs/verifiedRef"},
|
|
24
|
+
"target": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"additionalProperties": false,
|
|
27
|
+
"required": ["id", "kind", "verified"],
|
|
28
|
+
"properties": {
|
|
29
|
+
"id": {"type": "string", "minLength": 1, "maxLength": 200},
|
|
30
|
+
"kind": {"enum": ["property", "container", "customer", "project", "site", "account"]},
|
|
31
|
+
"verified": {"const": true}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"authMode": {"enum": ["desktop-oauth", "service-account-impersonation", "none"]},
|
|
35
|
+
"scopes": {"type": "array", "items": {"type": "string", "minLength": 1}, "uniqueItems": true},
|
|
36
|
+
"productRole": {"type": "string", "minLength": 1, "maxLength": 200},
|
|
37
|
+
"evidence": {"$ref": "#/$defs/evidence"},
|
|
38
|
+
"capabilities": {"$ref": "#/$defs/capabilities"},
|
|
39
|
+
"nextAction": {"type": "string", "minLength": 1, "maxLength": 500}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"verifiedRef": {
|
|
43
|
+
"type": "object",
|
|
44
|
+
"additionalProperties": false,
|
|
45
|
+
"required": ["id", "verified"],
|
|
46
|
+
"properties": {
|
|
47
|
+
"id": {"type": "string", "pattern": "^[^@\\s]{1,200}$"},
|
|
48
|
+
"verified": {"const": true}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"evidence": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"additionalProperties": false,
|
|
54
|
+
"required": ["verified", "endpoint", "statusCode", "readOnly"],
|
|
55
|
+
"properties": {
|
|
56
|
+
"verified": {"type": "boolean"},
|
|
57
|
+
"endpoint": {"type": "string", "minLength": 1, "maxLength": 500},
|
|
58
|
+
"statusCode": {"type": "integer", "minimum": 100, "maximum": 599},
|
|
59
|
+
"readOnly": {"type": "boolean"},
|
|
60
|
+
"writeTest": {"type": "object"},
|
|
61
|
+
"adsPrerequisites": {"type": "object"}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"capabilities": {
|
|
65
|
+
"type": "object",
|
|
66
|
+
"additionalProperties": false,
|
|
67
|
+
"required": ["read", "report", "edit", "publish"],
|
|
68
|
+
"properties": {
|
|
69
|
+
"read": {"enum": ["verified", "not_tested", "not_available", "blocked"]},
|
|
70
|
+
"report": {"enum": ["verified", "not_tested", "not_available", "blocked"]},
|
|
71
|
+
"edit": {"enum": ["verified", "not_tested", "not_available", "blocked"]},
|
|
72
|
+
"publish": {"enum": ["verified", "not_tested", "not_available", "blocked"]}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://maggie.noblox.app/contracts/google-integrations/external-write-readback-v1.json",
|
|
4
|
+
"title": "Maggie external provider write readback evidence v1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["schemaVersion", "generatedAt", "provider", "resource", "operation", "idempotencyKey", "retry", "mutation", "readback", "duplicates", "orphaned", "cleanup", "mutationExecuted"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"schemaVersion": {"const": "maggie-external-write-readback.v1"},
|
|
10
|
+
"generatedAt": {"type": "string", "minLength": 1},
|
|
11
|
+
"provider": {"enum": ["ga4", "gtm", "gsc", "google-ads", "firebase"]},
|
|
12
|
+
"resource": {"type": "string", "minLength": 1, "maxLength": 200},
|
|
13
|
+
"operation": {"type": "string", "pattern": "^[a-z][a-z0-9_.-]{1,63}$"},
|
|
14
|
+
"idempotencyKey": {"type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{2,127}$"},
|
|
15
|
+
"retry": {
|
|
16
|
+
"type": "object", "additionalProperties": false, "required": ["attempt", "maxAttempts", "sameKeyOnRetry"],
|
|
17
|
+
"properties": {"attempt": {"type": "integer", "minimum": 1, "maximum": 3}, "maxAttempts": {"type": "integer", "minimum": 1, "maximum": 3}, "sameKeyOnRetry": {"const": true}}
|
|
18
|
+
},
|
|
19
|
+
"mutation": {
|
|
20
|
+
"type": "object", "additionalProperties": false, "required": ["status", "statusCode", "explicitConfirmation"],
|
|
21
|
+
"properties": {"status": {"enum": ["created", "updated", "no-op"]}, "statusCode": {"type": "integer", "minimum": 200, "maximum": 299}, "explicitConfirmation": {"const": true}}
|
|
22
|
+
},
|
|
23
|
+
"readback": {
|
|
24
|
+
"type": "object", "additionalProperties": false, "required": ["passed", "endpoint", "checks"],
|
|
25
|
+
"properties": {
|
|
26
|
+
"passed": {"const": true},
|
|
27
|
+
"endpoint": {"type": "string", "pattern": "^https://[^\\s?#]+$"},
|
|
28
|
+
"checks": {"type": "array", "minItems": 1, "items": {"type": "object"}}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"duplicates": {"type": "array", "maxItems": 0},
|
|
32
|
+
"orphaned": {"type": "array", "maxItems": 0},
|
|
33
|
+
"cleanup": {"type": "object", "additionalProperties": false, "required": ["status", "checked"], "properties": {"status": {"enum": ["not-needed", "passed"]}, "checked": {"const": true}}},
|
|
34
|
+
"mutationExecuted": {"const": true}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://maggiedash.noblox.app/contracts/deployer-delegation-v1.schema.json",
|
|
4
|
+
"title": "Maggie deployment deployer delegation evidence v1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["schemaVersion", "host", "user", "releaseRoot", "service", "state", "passed", "checks", "mutation"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"schemaVersion": {"const": "maggie-deployment-delegation.v1"},
|
|
10
|
+
"host": {"type": "string", "minLength": 1},
|
|
11
|
+
"user": {"type": "string", "pattern": "^[a-z_][a-z0-9_-]{0,31}\\$?$"},
|
|
12
|
+
"releaseRoot": {"type": "string", "pattern": "^/[^\\r\\n]*$"},
|
|
13
|
+
"service": {"type": "string", "pattern": "^[A-Za-z0-9_.@-]+$"},
|
|
14
|
+
"state": {"enum": ["passed", "failed", "transient", "inconclusive"]},
|
|
15
|
+
"passed": {"type": "boolean"},
|
|
16
|
+
"checks": {"type": "object"},
|
|
17
|
+
"errors": {"type": "array", "items": {"type": "string"}},
|
|
18
|
+
"mutation": {"const": "not executed"}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://maggiedash.noblox.app/contracts/release-profile-v1.schema.json",
|
|
4
|
+
"title": "Maggie release profile v1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["schemaVersion", "profile", "capabilities"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"schemaVersion": {"const": "maggie-release-profile.v1"},
|
|
10
|
+
"profile": {"type": "string", "pattern": "^[a-z][a-z0-9-]{1,63}$"},
|
|
11
|
+
"projectType": {"type": "string", "enum": ["astro", "nextjs", "sveltekit", "nuxt", "vite-react", "vite-vue", "other"]},
|
|
12
|
+
"capabilities": {
|
|
13
|
+
"type": "array",
|
|
14
|
+
"minItems": 1,
|
|
15
|
+
"uniqueItems": true,
|
|
16
|
+
"items": {
|
|
17
|
+
"enum": [
|
|
18
|
+
"deployment",
|
|
19
|
+
"scenario-qa",
|
|
20
|
+
"social-card-audit",
|
|
21
|
+
"migration",
|
|
22
|
+
"maggiedash-health",
|
|
23
|
+
"schedule",
|
|
24
|
+
"analytics-contract",
|
|
25
|
+
"analytics-release-gate",
|
|
26
|
+
"service-facts",
|
|
27
|
+
"maggiedash-schema",
|
|
28
|
+
"editorial-review",
|
|
29
|
+
"durable-site-evidence"
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|