@topy-ai/maggie 0.7.44 → 0.7.45
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 +24 -6
- package/README.md +44 -12
- package/bin/maggie.js +205 -56
- package/bundled-skills/maggie-blog-bootstrap/SKILL.md +7 -7
- package/bundled-skills/maggie-clone/SKILL.md +14 -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 +8 -8
- package/bundled-skills/maggie-deployment/references/vps.md +4 -4
- package/bundled-skills/maggie-design/SKILL.md +12 -12
- package/bundled-skills/maggie-marketplace/SKILL.md +12 -12
- package/bundled-skills/maggie-ops/SKILL.md +6 -6
- package/bundled-skills/maggie-project-context/SKILL.md +1 -1
- package/bundled-skills/maggie-seo-geo/SKILL.md +2 -2
- 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/package.json +1 -1
package/README-zh-TW.md
CHANGED
|
@@ -8,11 +8,29 @@ Codex、Claude Code 與相容的 coding agents。
|
|
|
8
8
|
## 安裝
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
npm install --global @topy-ai/maggie
|
|
12
|
+
maggie init --scope user --agent all
|
|
13
|
+
maggie doctor --scope user --agent all
|
|
13
14
|
```
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
這只會把可重用 skills 和 shared references 安裝到 user-level Claude Code/Codex
|
|
17
|
+
目錄;`.maggie/`、evidence、contracts、templates、marketplace 和 host files
|
|
18
|
+
仍然由各 project 自己管理。更新全域安裝:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install --global @topy-ai/maggie@latest
|
|
22
|
+
maggie update --scope user --agent all
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
若要讓 repository 固定自己的 skill 版本,才使用 project-local 安裝:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx @topy-ai/maggie init --scope project --agent all
|
|
29
|
+
npx @topy-ai/maggie doctor --scope project --project .
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
目前 release 是 `0.7.45`。新增 Codex/Claude Code 的一次性 user-scope 安裝、bundled
|
|
33
|
+
workflow runner 與 user-scope maintenance commands;並完成 2026-09-24 booking feedback batch:MaggieDash
|
|
16
34
|
Calendar availability grid/free-slot entry,以及 ORA modal focus restoration
|
|
17
35
|
的驗證證據。另新增 secret-free Starter marketplace provisioning
|
|
18
36
|
plan、versioned GitHub/Cloudflare/Starter contracts,以及 deterministic
|
|
@@ -57,8 +75,8 @@ maggie booking-delivery --policy .maggie/booking-delivery-policy.json
|
|
|
57
75
|
常用流程:
|
|
58
76
|
|
|
59
77
|
```bash
|
|
60
|
-
|
|
61
|
-
|
|
78
|
+
maggie tool maggie.py analyze . --json --save
|
|
79
|
+
maggie tool maggie.py bootstrap interview .
|
|
62
80
|
maggie memory context --project . --skill <skill-name>
|
|
63
81
|
maggie doctor --project . --require-bootstrap --strict
|
|
64
82
|
```
|
|
@@ -82,7 +100,7 @@ structure drift 或重複 treatment product 時 fail closed。
|
|
|
82
100
|
deployment、memory、feedback 和 MaggieDash。內容先 draft/review,外部寫入、
|
|
83
101
|
publish 與 production deployment 需要明確確認。
|
|
84
102
|
|
|
85
|
-
目前 source tree 的正式 release 是 `0.7.
|
|
103
|
+
目前 source tree 的正式 release 是 `0.7.45`。本版完成 2026-09-24 booking feedback batch:Calendar grid/free-slot entry 與 host modal focus-restoration evidence。本版新增 Starter marketplace provisioning plan;本版完成 feedback batches 22、23:加入 declarative browser interaction evidence、design markup/style/content-key gate、compiler source anchoring、entity double-escape protection、`update --help` read-only、noindex hypothetical indexability、可過期 baseline acknowledgement、DOM-only link extraction regression,以及 MaggieDash 0.2.10 的 content override、短期 browser session、CDN-safe public-session cache contracts。authentication 和 isolated dev server 仍由 host 負責。`maggie-booking` workflow、傳統 email/password、Stripe、Resend、public booking flow 及既有安全邊界維持不變;provider-bound video generation 與 transactional delivery 仍記錄在 issue #39,前一批延後功能仍在 issue #38,不會直接成為 active memory。0.7.33 記錄並審查一筆更正回饋;確認 0.7.32 已正確處理 batch index,沒有修改已驗證的 shared behavior。0.7.32 加入 feedback 一基索引支援、MaggieDash panel 的 source/freshness/error evidence、Consent Mode 與實際 write/no-op reconciliation、SEO baseline recapture、sitemap origin rebasing、content-diff scope、migration ledger reconciliation、deployment credential preflight、release provenance/runtime preflight、icon release gate、opt-in blog auto-publish gate,以及 least-privilege VPS deployer。也修復 top-level `maggie feedback` dispatcher parity。0.7.31 加入 update 差異摘要、atomic installer copy、subset install manifest merge,以及 inventory drift 時 doctor non-zero。0.7.30 補上 feedback tracking issue closeout gate 與 batch 14/15 audit ledger。0.7.29 修正 npm package landing page 使用英文 README;0.7.28 加入 versioned Gemini model policy、明確 fallback 與 provenance、bundled-first CLI
|
|
86
104
|
dispatch、`maggie --version`、feedback batch review 聚合與重複偵測、marketplace enrichment evidence、booking
|
|
87
105
|
worker/resolver evidence,以及 read-only migration preflight。它也包含 host-owned mobile app surface contract、signed-in
|
|
88
106
|
camera-state QA、直接 Astro route resolution、correlated feedback batch,以及
|
package/README.md
CHANGED
|
@@ -12,7 +12,36 @@ composable skills for project bootstrap, authorized website cloning, design
|
|
|
12
12
|
systems, SEO/GEO, content operations, marketplace templates, deployment, and
|
|
13
13
|
persistent project memory.
|
|
14
14
|
|
|
15
|
-
## Install
|
|
15
|
+
## Install once for every project
|
|
16
|
+
|
|
17
|
+
Install the CLI globally and place the reusable skills in the user-level
|
|
18
|
+
directories used by Codex and Claude Code:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install --global @topy-ai/maggie
|
|
22
|
+
maggie init --scope user --agent all
|
|
23
|
+
maggie doctor --scope user --agent all
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
This writes only agent skills, shared references, and a user install manifest.
|
|
27
|
+
Project `.maggie/` state, evidence, contracts, templates, marketplace data,
|
|
28
|
+
and host files remain local to each project. Use `maggie tool` when a workflow
|
|
29
|
+
needs a bundled Python CLI; it runs the package copy with the current project
|
|
30
|
+
as its working directory.
|
|
31
|
+
|
|
32
|
+
Upgrade the CLI and user skills together:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install --global @topy-ai/maggie@latest
|
|
36
|
+
maggie update --scope user --agent all
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`--global` is an alias for `--scope user`. Use `--force` only to replace an
|
|
40
|
+
intentionally customized user skill.
|
|
41
|
+
|
|
42
|
+
## Project-local install
|
|
43
|
+
|
|
44
|
+
Use project scope when a repository must pin or customize its skill files:
|
|
16
45
|
|
|
17
46
|
`site-audit --crawl --save-baseline FILE --reviewer NAME` records a reviewed
|
|
18
47
|
site contract; `site-audit --crawl --baseline FILE` fails on URL, metadata,
|
|
@@ -25,14 +54,14 @@ with explicit `--confirm`, resumable draft batches and no automatic publishing.
|
|
|
25
54
|
See the installed localization skill for the adapter protocol and examples.
|
|
26
55
|
|
|
27
56
|
```bash
|
|
28
|
-
npx @topy-ai/maggie init --agent codex
|
|
57
|
+
npx @topy-ai/maggie init --scope project --agent codex
|
|
29
58
|
```
|
|
30
59
|
|
|
31
60
|
Install for Claude Code or both supported agent directories:
|
|
32
61
|
|
|
33
62
|
```bash
|
|
34
|
-
npx @topy-ai/maggie init --agent claude
|
|
35
|
-
npx @topy-ai/maggie init --agent all
|
|
63
|
+
npx @topy-ai/maggie init --scope project --agent claude
|
|
64
|
+
npx @topy-ai/maggie init --scope project --agent all
|
|
36
65
|
```
|
|
37
66
|
|
|
38
67
|
Update an existing project while preserving local customisations by default:
|
|
@@ -60,7 +89,9 @@ Maggie keeps the existing project foundation and asks for decisions before
|
|
|
60
89
|
shared routes, analytics, or publishing boundaries change. The current
|
|
61
90
|
package ships 20 installable skills and a local-first MaggieDash foundation.
|
|
62
91
|
|
|
63
|
-
The current release is `0.7.
|
|
92
|
+
The current release is `0.7.45`. It adds one-time user-scope installation for
|
|
93
|
+
Codex and Claude Code, a bundled workflow runner, and scoped maintenance
|
|
94
|
+
commands. It also closes the 2026-09-24 booking feedback
|
|
64
95
|
batch with the canonical MaggieDash Calendar availability grid/free-slot
|
|
65
96
|
entry and documented ORA modal focus-restoration evidence. It also adds the
|
|
66
97
|
secret-free Starter marketplace
|
|
@@ -313,8 +344,8 @@ For a genuinely empty project, create the host framework first, then bootstrap
|
|
|
313
344
|
Maggie in this order:
|
|
314
345
|
|
|
315
346
|
```bash
|
|
316
|
-
|
|
317
|
-
|
|
347
|
+
maggie tool maggie.py analyze . --json --save
|
|
348
|
+
maggie tool maggie.py bootstrap interview .
|
|
318
349
|
maggie dash install --project . --confirm
|
|
319
350
|
maggie dash init --project . --confirm
|
|
320
351
|
maggie dash migrate --project . --confirm
|
|
@@ -336,8 +367,9 @@ The CLI provides the installer plus durable workflow commands:
|
|
|
336
367
|
|
|
337
368
|
```text
|
|
338
369
|
maggie --version | version
|
|
339
|
-
maggie init | install | update | remove | list | doctor
|
|
340
|
-
maggie cleanup --project . [--confirm]
|
|
370
|
+
maggie init | install | update | remove | list | doctor [--scope project|user]
|
|
371
|
+
maggie cleanup [--scope project|user] --project . [--confirm]
|
|
372
|
+
maggie tool <bundled-cli.py> [arguments]
|
|
341
373
|
maggie bootstrap interview | phase ...
|
|
342
374
|
maggie dash install | init | status | migrate | cms ...
|
|
343
375
|
maggie dash transition ... # explicit content approval transition
|
|
@@ -599,8 +631,8 @@ artifact schemas.
|
|
|
599
631
|
Recommended upgrade sequence for the current release:
|
|
600
632
|
|
|
601
633
|
```bash
|
|
602
|
-
npx @topy-ai/maggie@0.7.
|
|
603
|
-
npx @topy-ai/maggie@0.7.
|
|
634
|
+
npx @topy-ai/maggie@0.7.45 update --scope project --project . --force
|
|
635
|
+
npx @topy-ai/maggie@0.7.45 cleanup --scope project --project .
|
|
604
636
|
```
|
|
605
637
|
|
|
606
638
|
Maintainers should pass npm credentials through the repository helper, never
|
|
@@ -1028,7 +1060,7 @@ See the [NoBlox preview gallery](https://github.com/TOPY-AI-LTD/NoBlox-App-Templ
|
|
|
1028
1060
|
For an explicit brand change, run Maggie Design rebrand before `review`:
|
|
1029
1061
|
|
|
1030
1062
|
```bash
|
|
1031
|
-
|
|
1063
|
+
maggie tool maggie_design.py rebrand \
|
|
1032
1064
|
--template marketplace/templates/<id> \
|
|
1033
1065
|
--source-brand "Source Brand" --brand "Target Brand"
|
|
1034
1066
|
```
|
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
|
|
@@ -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);
|
|
@@ -14,14 +14,14 @@ project-specific design, service, SEO, operations, and deployment adapters.
|
|
|
14
14
|
|
|
15
15
|
## Workflow
|
|
16
16
|
|
|
17
|
-
1. Run `
|
|
17
|
+
1. Run `maggie tool maggie.py analyze PROJECT --json --save` and record
|
|
18
18
|
evidence for framework, language, UI, icons, data, content, routes, and
|
|
19
19
|
deployment.
|
|
20
|
-
2. Run `
|
|
20
|
+
2. Run `maggie tool maggie.py bootstrap interview PROJECT`; review the
|
|
21
21
|
checkpoint and confirm foundation, experience, data, and publishing choices.
|
|
22
22
|
3. For a new MaggieDash project, run:
|
|
23
|
-
`
|
|
24
|
-
`
|
|
23
|
+
`maggie tool maggie_dash.py init --project PROJECT --confirm` and
|
|
24
|
+
`maggie tool maggie_dash.py migrate --project PROJECT --confirm`.
|
|
25
25
|
If the first-party dashboard is installed, also run
|
|
26
26
|
`maggie dash install --project PROJECT --confirm`; this copies the
|
|
27
27
|
provider-neutral backend type boundary and idempotent starter schema beside
|
|
@@ -30,8 +30,8 @@ project-specific design, service, SEO, operations, and deployment adapters.
|
|
|
30
30
|
4. Advance the ordered foundation gate:
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
maggie tool maggie.py bootstrap phase start maggiedash-foundation PROJECT
|
|
34
|
+
maggie tool maggie.py bootstrap phase pass maggiedash-foundation PROJECT \
|
|
35
35
|
--validation "maggie analyze PROJECT; npm run build"
|
|
36
36
|
```
|
|
37
37
|
|
|
@@ -41,7 +41,7 @@ project-specific design, service, SEO, operations, and deployment adapters.
|
|
|
41
41
|
7. Import external content through the provider-neutral draft workflow:
|
|
42
42
|
|
|
43
43
|
```bash
|
|
44
|
-
|
|
44
|
+
maggie tool maggie_content.py records.json --project PROJECT \
|
|
45
45
|
--source PROVIDER --confirm
|
|
46
46
|
```
|
|
47
47
|
|