@topy-ai/maggie 0.7.30 → 0.7.32
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 +2 -2
- package/README.md +99 -17
- package/bin/maggie.js +105 -17
- package/bundled-references/google-integrations-runbook.md +26 -0
- package/bundled-skills/maggie-blog/SKILL.md +18 -1
- package/bundled-skills/maggie-dash/SKILL.md +18 -3
- package/bundled-skills/maggie-deployment/SKILL.md +72 -0
- package/bundled-skills/maggie-deployment/references/vps.md +26 -0
- package/bundled-skills/maggie-design/SKILL.md +16 -0
- package/bundled-skills/maggie-feedback/SKILL.md +11 -2
- package/bundled-skills/maggie-ops/SKILL.md +11 -2
- package/bundled-skills/maggie-seo-geo/SKILL.md +22 -1
- package/bundled-tools/clis/maggie.py +12 -0
- package/bundled-tools/clis/maggie_analytics.py +34 -0
- package/bundled-tools/clis/maggie_blog.py +13 -0
- package/bundled-tools/clis/maggie_dash.py +17 -0
- package/bundled-tools/clis/maggie_deployment.py +91 -7
- package/bundled-tools/clis/maggie_feedback.py +18 -4
- package/bundled-tools/clis/maggie_icon_release_gate.py +79 -0
- package/bundled-tools/clis/maggie_migration.py +71 -0
- package/bundled-tools/clis/maggie_release_manifest.py +81 -0
- package/bundled-tools/clis/maggie_runtime_preflight.py +76 -0
- package/bundled-tools/clis/site_audit.py +78 -5
- package/bundled-tools/integrations/analytics.md +20 -0
- package/bundled-tools/runtime/maggie_blog_publish.py +38 -0
- package/bundled-tools/runtime/maggie_dash_panels.py +111 -0
- package/bundled-tools/runtime/maggie_quality.py +1 -1
- package/bundled-tools/runtime/maggie_sections.py +39 -1
- package/bundled-tools/runtime/site_baseline.py +23 -2
- package/package.json +1 -1
- package/references/google-integrations-runbook.md +26 -0
package/README-zh-TW.md
CHANGED
|
@@ -8,7 +8,7 @@ Codex、Claude Code 與相容的 coding agents。
|
|
|
8
8
|
## 安裝
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
|
-
npx @topy-ai/maggie@0.7.
|
|
11
|
+
npx @topy-ai/maggie@0.7.32 init --agent all
|
|
12
12
|
npx @topy-ai/maggie doctor --project .
|
|
13
13
|
```
|
|
14
14
|
|
|
@@ -25,7 +25,7 @@ maggie doctor --project . --require-bootstrap --strict
|
|
|
25
25
|
deployment、memory、feedback 和 MaggieDash。內容先 draft/review,外部寫入、
|
|
26
26
|
publish 與 production deployment 需要明確確認。
|
|
27
27
|
|
|
28
|
-
目前 release 是 `0.7.30
|
|
28
|
+
目前 release 是 `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
|
|
29
29
|
dispatch、`maggie --version`、feedback batch review 聚合與重複偵測、marketplace enrichment evidence、booking
|
|
30
30
|
worker/resolver evidence,以及 read-only migration preflight。它也包含 host-owned mobile app surface contract、signed-in
|
|
31
31
|
camera-state QA、直接 Astro route resolution、correlated feedback batch,以及
|
package/README.md
CHANGED
|
@@ -60,19 +60,29 @@ Maggie keeps the existing project foundation and asks for decisions before
|
|
|
60
60
|
shared routes, analytics, or publishing boundaries change. The current
|
|
61
61
|
package ships 19 installable skills and a local-first MaggieDash foundation.
|
|
62
62
|
|
|
63
|
-
The current release is `0.7.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
63
|
+
The current release is `0.7.32`. It adds explicit one-based feedback batch
|
|
64
|
+
index support, source/freshness/error evidence for MaggieDash measurement
|
|
65
|
+
panels, Consent Mode and actual-write reconciliation contracts, reviewed SEO
|
|
66
|
+
baseline recapture, requested-origin sitemap rebasing, explicit content-diff
|
|
67
|
+
scope, migration-ledger reconciliation, canonical deployment credential
|
|
68
|
+
preflight, unified release provenance, runtime preflight classification,
|
|
69
|
+
design icon-release gating, opt-in blog auto-publish gating, and a
|
|
70
|
+
least-privilege VPS deployer plan. It also restores top-level `maggie
|
|
71
|
+
feedback` dispatch parity. The 0.7.31 release added bounded update diff
|
|
72
|
+
evidence, atomic installer copies, subset-install manifest merge, and
|
|
73
|
+
non-zero doctor failures for inventory drift. The 0.7.30 release added the
|
|
74
|
+
internal feedback issue closeout gate. The 0.7.29 release fixed npm metadata
|
|
75
|
+
to render this English README as the package landing page. The 0.7.28
|
|
76
|
+
release added a versioned Gemini model policy with an explicit fallback and
|
|
77
|
+
provenance, bundled-CLI version discovery, a bundled-first dispatcher that
|
|
78
|
+
avoids stale project wrappers, privacy-safe feedback batch review/duplicate
|
|
79
|
+
detection, marketplace enrichment evidence, bounded booking worker/resolver
|
|
80
|
+
reports, and read-only migration preflight contracts. The release retains the
|
|
81
|
+
provider-neutral MaggieDash host adapter contract, idempotent PostgreSQL
|
|
82
|
+
starter schema, type-only backend boundary, runtime endpoint conformance
|
|
83
|
+
evidence, safe quota/error summaries, stale marketplace readiness
|
|
84
|
+
revalidation, explicit VPS port/origin planning, first-release rollback
|
|
85
|
+
evidence, and clean non-interactive bootstrap errors.
|
|
76
86
|
It retains the host-owned mobile app surface
|
|
77
87
|
contract (`maggie design app-init`/`app-validate`), direct Astro route
|
|
78
88
|
resolution, correlated feedback batches, and runtime package-version capture.
|
|
@@ -129,6 +139,16 @@ evidence proves that a dashboard screen mounted and executed its checks, so a
|
|
|
129
139
|
static component scan cannot pass by itself. MaggieDash hosts may also expose
|
|
130
140
|
the provider-neutral activity-log and managed-navigation contracts.
|
|
131
141
|
|
|
142
|
+
Measurement panels must report their source, capture time/freshness, and
|
|
143
|
+
independent warning/error state. Validate the host-produced report before
|
|
144
|
+
using it in a release decision:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
maggie dash panels-validate \
|
|
148
|
+
--report .maggie/measurement-panels.json \
|
|
149
|
+
--output docs/measurement-panels-validation.json
|
|
150
|
+
```
|
|
151
|
+
|
|
132
152
|
The current package also includes reusable safeguards from the latest feedback
|
|
133
153
|
review: `maggie dash api-contract` checks declared request and 2xx response
|
|
134
154
|
shapes; `maggie blog check-gate`/`approve` enforces review before publish;
|
|
@@ -249,10 +269,17 @@ maggie ops favicon-check ... # served favicon behaviour check
|
|
|
249
269
|
maggie deployment | migration | release | analytics | schedule
|
|
250
270
|
maggie deployment readiness --project PATH
|
|
251
271
|
maggie deployment parity --project PATH --evidence FILE --output FILE
|
|
272
|
+
maggie deployment runtime-preflight --evidence FILE --output FILE
|
|
273
|
+
maggie deployment release-manifest --evidence FILE --output FILE
|
|
274
|
+
maggie deployment credential-preflight [--expected-host HOST]
|
|
252
275
|
maggie migration identity --identity-file FILE [--expected-file FILE]
|
|
253
276
|
maggie migration preflight --evidence FILE [--output FILE]
|
|
277
|
+
maggie migration reconcile --evidence FILE --migration-dir DIR [--output FILE]
|
|
254
278
|
maggie deployment canary --asset URL=SHA256 --render-report report.json
|
|
255
279
|
maggie design icon-inventory --source-dir src --runtime assets/icons.css
|
|
280
|
+
maggie design icon-release-gate --icon-report FILE --browser-report FILE --output FILE
|
|
281
|
+
maggie dash panels-validate --report FILE [--output FILE]
|
|
282
|
+
maggie blog auto-publish-gate --evidence FILE --output FILE
|
|
256
283
|
maggie api lifecycle | site-audit | ops audit
|
|
257
284
|
maggie marketplace enrichment-report --project PATH --input FILE [--output FILE]
|
|
258
285
|
maggie service worker-health --project PATH --evidence FILE [--output FILE]
|
|
@@ -318,6 +345,16 @@ The adapter is an argv array, receives a versioned request on stdin, and must
|
|
|
318
345
|
print only the three review-policy fields. Provider stderr is suppressed and a
|
|
319
346
|
missing local store without an adapter fails closed.
|
|
320
347
|
|
|
348
|
+
Automated blog publishing stays human-reviewed by default. If a host opts in,
|
|
349
|
+
the gate requires explicit review bypass evidence plus locale, image-alt, SEO,
|
|
350
|
+
sitemap, indexing-notification, and audit checks:
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
maggie blog auto-publish-gate \
|
|
354
|
+
--evidence .maggie/blog/auto-publish-evidence.json \
|
|
355
|
+
--output docs/blog-auto-publish-gate.json
|
|
356
|
+
```
|
|
357
|
+
|
|
321
358
|
### Design and deployment release gates
|
|
322
359
|
|
|
323
360
|
Check source-to-runtime icon coverage before shipping a design:
|
|
@@ -332,6 +369,17 @@ and is only static name coverage. Pair changed icon controls with a
|
|
|
332
369
|
browser-runtime `icon-rendered` assertion so the host adapter proves that the
|
|
333
370
|
glyph is visible, has positive dimensions, and has an accessible label.
|
|
334
371
|
|
|
372
|
+
For a design release, compose the static inventory with browser-painted glyph
|
|
373
|
+
evidence. Every tested icon must be visible, have positive dimensions, expose
|
|
374
|
+
an accessible name, and load successfully:
|
|
375
|
+
|
|
376
|
+
```bash
|
|
377
|
+
maggie design icon-release-gate \
|
|
378
|
+
--icon-report docs/icon-inventory.json \
|
|
379
|
+
--browser-report .maggie/icon-browser-evidence.json \
|
|
380
|
+
--output docs/icon-release-gate.json
|
|
381
|
+
```
|
|
382
|
+
|
|
335
383
|
After deployment, compare an immutable asset fingerprint and validate the
|
|
336
384
|
browser adapter's rendered report:
|
|
337
385
|
|
|
@@ -360,6 +408,37 @@ privileges, route API status, stale resident processes, source-job IDs, and
|
|
|
360
408
|
browser/driver compatibility. Add the resulting report to readiness with
|
|
361
409
|
`--runtime-parity`; a failed report blocks the gate.
|
|
362
410
|
|
|
411
|
+
Use the runtime preflight to classify dependency, browser, worker, database,
|
|
412
|
+
and route failures independently. A non-pass check must include remediation:
|
|
413
|
+
|
|
414
|
+
```bash
|
|
415
|
+
maggie deployment runtime-preflight \
|
|
416
|
+
--evidence .maggie/verification/runtime-preflight-input.json \
|
|
417
|
+
--output .maggie/verification/runtime-preflight.json
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
Before activation, reconcile the host's `maggie_schema_migrations` ledger with
|
|
421
|
+
local SQL files. This is read-only and produces an ordered missing-migration
|
|
422
|
+
plan. Deployment credential checks similarly verify only the presence of the
|
|
423
|
+
canonical names, never their values:
|
|
424
|
+
|
|
425
|
+
```bash
|
|
426
|
+
maggie migration reconcile \
|
|
427
|
+
--evidence .maggie/migration/ledger.json \
|
|
428
|
+
--migration-dir migrations \
|
|
429
|
+
--output docs/migration-reconcile.json
|
|
430
|
+
maggie deployment credential-preflight --expected-host example.com
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
Finally, combine route, migration, runtime, asset/icon, edge-cache,
|
|
434
|
+
browser/render, and rollback evidence into one release provenance manifest:
|
|
435
|
+
|
|
436
|
+
```bash
|
|
437
|
+
maggie deployment release-manifest \
|
|
438
|
+
--evidence .maggie/verification/release-evidence.json \
|
|
439
|
+
--output docs/release-manifest.json
|
|
440
|
+
```
|
|
441
|
+
|
|
363
442
|
Unit regression does not establish runtime release readiness. Produce unit
|
|
364
443
|
evidence and then validate all required release evidence slots; add
|
|
365
444
|
`--runtime-parity` when the deployment uses the worker/browser surfaces above:
|
|
@@ -419,8 +498,8 @@ artifact schemas.
|
|
|
419
498
|
Recommended upgrade sequence for the current release:
|
|
420
499
|
|
|
421
500
|
```bash
|
|
422
|
-
npx @topy-ai/maggie@0.7.
|
|
423
|
-
npx @topy-ai/maggie@0.7.
|
|
501
|
+
npx @topy-ai/maggie@0.7.32 update --project . --force
|
|
502
|
+
npx @topy-ai/maggie@0.7.32 cleanup --project .
|
|
424
503
|
```
|
|
425
504
|
|
|
426
505
|
Maintainers should pass npm credentials through the repository helper, never
|
|
@@ -586,10 +665,13 @@ For related observations, create 1–50 local drafts with shared correlation
|
|
|
586
665
|
metadata and review them before submitting:
|
|
587
666
|
|
|
588
667
|
```bash
|
|
589
|
-
maggie feedback batch --project . --batch-file ./feedback-batch.json
|
|
668
|
+
maggie feedback batch --project . --batch-file ./feedback-batch.json \
|
|
669
|
+
--batch-base 0
|
|
590
670
|
```
|
|
591
671
|
|
|
592
|
-
The batch command never submits automatically or writes active memory.
|
|
672
|
+
The batch command never submits automatically or writes active memory. External
|
|
673
|
+
one-based runners can pass `--batch-base 1`; Maggie stores canonical indexes
|
|
674
|
+
zero-based and validates the final item boundary explicitly.
|
|
593
675
|
|
|
594
676
|
Aggregate a correlated batch before reviewing individual drafts:
|
|
595
677
|
|
package/bin/maggie.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
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 {
|
|
7
|
-
import { dirname, join, resolve } from "node:path";
|
|
6
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
8
7
|
import { fileURLToPath } from "node:url";
|
|
9
8
|
|
|
10
9
|
const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
@@ -92,6 +91,7 @@ Usage:
|
|
|
92
91
|
maggie design app-init --project PATH --route /app/valuation --auth-mode email-password --confirm
|
|
93
92
|
maggie design app-validate --project PATH --plan PATH --runtime-evidence FILE --rendered-dir PATH --confirm
|
|
94
93
|
maggie design icon-inventory --project PATH --source-dir src --runtime assets/icons.css --output docs/icon-inventory.json
|
|
94
|
+
maggie design icon-release-gate --icon-report FILE --browser-report FILE --output FILE
|
|
95
95
|
maggie design author --project PATH --route /about --purpose TEXT --audience TEXT --confirm
|
|
96
96
|
maggie design in-place --project PATH --route /pricing [--content-source PATH]
|
|
97
97
|
maggie design section --project PATH --route /pricing --section-id ID
|
|
@@ -118,6 +118,8 @@ Usage:
|
|
|
118
118
|
maggie deployment readiness --project PATH
|
|
119
119
|
maggie deployment parity --project PATH --evidence FILE --output FILE
|
|
120
120
|
maggie deployment canary --project PATH --asset URL=SHA256 --render-report report.json --output docs/deployment-canary.json
|
|
121
|
+
maggie deployment release-manifest --evidence FILE --output FILE
|
|
122
|
+
maggie deployment runtime-preflight --evidence FILE --output FILE
|
|
121
123
|
maggie migration preflight --evidence FILE --output FILE
|
|
122
124
|
maggie migration --project PATH --environment staging
|
|
123
125
|
maggie migration identity --identity-file FILE [--expected-file FILE]
|
|
@@ -169,6 +171,33 @@ function selectedSkills(args) {
|
|
|
169
171
|
return unique;
|
|
170
172
|
}
|
|
171
173
|
|
|
174
|
+
function diskSkillNames(root) {
|
|
175
|
+
const installed = new Set();
|
|
176
|
+
for (const agentRoot of [join(root, ".agents"), join(root, ".claude")]) {
|
|
177
|
+
const skillsRoot = join(agentRoot, "skills");
|
|
178
|
+
if (!existsSync(skillsRoot)) continue;
|
|
179
|
+
for (const entry of readdirSync(skillsRoot, { withFileTypes: true })) {
|
|
180
|
+
if (entry.isDirectory() && existsSync(join(skillsRoot, entry.name, "SKILL.md"))) installed.add(entry.name);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return installed;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function mergedSkillNames(root, selected) {
|
|
187
|
+
const manifestPath = join(root, STATE_DIR, "install.json");
|
|
188
|
+
let manifestSkills = [];
|
|
189
|
+
try {
|
|
190
|
+
if (existsSync(manifestPath)) {
|
|
191
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
192
|
+
manifestSkills = Array.isArray(manifest.skills) ? manifest.skills : [];
|
|
193
|
+
}
|
|
194
|
+
} catch {
|
|
195
|
+
manifestSkills = [];
|
|
196
|
+
}
|
|
197
|
+
return [...new Set([...manifestSkills, ...diskSkillNames(root), ...selected])]
|
|
198
|
+
.filter((skill) => SKILL_NAMES.includes(skill)).sort();
|
|
199
|
+
}
|
|
200
|
+
|
|
172
201
|
function agentRoots(args, root) {
|
|
173
202
|
const requested = option(args, "--agent", "auto");
|
|
174
203
|
if (!["auto", "codex", "claude", "all"].includes(requested)) throw new Error(`unknown agent: ${requested}`);
|
|
@@ -184,7 +213,18 @@ function agentRoots(args, root) {
|
|
|
184
213
|
function copyIfMissing(source, target, force = false) {
|
|
185
214
|
if (existsSync(target) && !force) return "skipped";
|
|
186
215
|
mkdirSync(dirname(target), { recursive: true });
|
|
187
|
-
|
|
216
|
+
if (existsSync(target)) {
|
|
217
|
+
cpSync(source, target, { recursive: true, force });
|
|
218
|
+
return "installed";
|
|
219
|
+
}
|
|
220
|
+
const staging = join(dirname(target), `.${basename(target)}.maggie-copy-${process.pid}-${Date.now()}`);
|
|
221
|
+
try {
|
|
222
|
+
cpSync(source, staging, { recursive: true, force: true });
|
|
223
|
+
renameSync(staging, target);
|
|
224
|
+
} catch (error) {
|
|
225
|
+
rmSync(staging, { recursive: true, force: true });
|
|
226
|
+
throw error;
|
|
227
|
+
}
|
|
188
228
|
return "installed";
|
|
189
229
|
}
|
|
190
230
|
|
|
@@ -192,6 +232,56 @@ function digest(path) {
|
|
|
192
232
|
return createHash("sha256").update(readFileSync(path)).digest("hex");
|
|
193
233
|
}
|
|
194
234
|
|
|
235
|
+
function diffSummary(source, target) {
|
|
236
|
+
const packageBuffer = readFileSync(source);
|
|
237
|
+
const localBuffer = readFileSync(target);
|
|
238
|
+
if (packageBuffer.includes(0) || localBuffer.includes(0)) return "binary content differs";
|
|
239
|
+
const packageLines = packageBuffer.toString("utf8").split(/\r?\n/);
|
|
240
|
+
const localLines = localBuffer.toString("utf8").split(/\r?\n/);
|
|
241
|
+
const limit = 2000;
|
|
242
|
+
if (packageLines.length > limit || localLines.length > limit) {
|
|
243
|
+
return `text differs (${localLines.length} local lines vs ${packageLines.length} package lines; diff bounded at ${limit} lines)`;
|
|
244
|
+
}
|
|
245
|
+
let packageIndex = 0;
|
|
246
|
+
let localIndex = 0;
|
|
247
|
+
let added = 0;
|
|
248
|
+
let removed = 0;
|
|
249
|
+
let blocks = 0;
|
|
250
|
+
while (packageIndex < packageLines.length || localIndex < localLines.length) {
|
|
251
|
+
if (packageIndex >= packageLines.length) {
|
|
252
|
+
added += localLines.length - localIndex;
|
|
253
|
+
blocks++;
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
if (localIndex >= localLines.length) {
|
|
257
|
+
removed += packageLines.length - packageIndex;
|
|
258
|
+
blocks++;
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
if (packageLines[packageIndex] === localLines[localIndex]) {
|
|
262
|
+
packageIndex++;
|
|
263
|
+
localIndex++;
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
const packageNext = packageLines.slice(packageIndex, packageIndex + 40).findIndex((line, offset) => line === localLines[localIndex]);
|
|
267
|
+
const localNext = localLines.slice(localIndex, localIndex + 40).findIndex((line, offset) => line === packageLines[packageIndex]);
|
|
268
|
+
if (packageNext >= 0 && (localNext < 0 || packageNext <= localNext)) {
|
|
269
|
+
removed += packageNext || 1;
|
|
270
|
+
packageIndex += packageNext || 1;
|
|
271
|
+
} else if (localNext >= 0) {
|
|
272
|
+
added += localNext || 1;
|
|
273
|
+
localIndex += localNext || 1;
|
|
274
|
+
} else {
|
|
275
|
+
removed++;
|
|
276
|
+
added++;
|
|
277
|
+
packageIndex++;
|
|
278
|
+
localIndex++;
|
|
279
|
+
}
|
|
280
|
+
blocks++;
|
|
281
|
+
}
|
|
282
|
+
return `${added} local additions, ${removed} package lines removed across ${blocks} changed blocks (${localLines.length} local lines vs ${packageLines.length} package lines)`;
|
|
283
|
+
}
|
|
284
|
+
|
|
195
285
|
function syncTree(source, target, force) {
|
|
196
286
|
let changed = 0;
|
|
197
287
|
for (const entry of readdirSync(source, { withFileTypes: true })) {
|
|
@@ -211,7 +301,7 @@ function syncTree(source, target, force) {
|
|
|
211
301
|
console.log(`updated ${targetPath}`);
|
|
212
302
|
changed++;
|
|
213
303
|
} else {
|
|
214
|
-
console.log(`preserved ${targetPath} (local changes; use --force to replace)`);
|
|
304
|
+
console.log(`preserved ${targetPath} (local changes; ${diffSummary(sourcePath, targetPath)}; use --force to replace)`);
|
|
215
305
|
}
|
|
216
306
|
}
|
|
217
307
|
return changed;
|
|
@@ -248,7 +338,7 @@ function install(args) {
|
|
|
248
338
|
if (existsSync(join(TEMPLATES_ROOT, "maggiedash"))) copyIfMissing(join(TEMPLATES_ROOT, "maggiedash"), join(root, "templates", "maggiedash"));
|
|
249
339
|
const stateDir = join(root, ".maggie");
|
|
250
340
|
mkdirSync(stateDir, { recursive: true });
|
|
251
|
-
writeFileSync(join(stateDir, "install.json"), JSON.stringify({ version: PACKAGE_VERSION, agents: roots.map((item) => item.slice(root.length + 1)), skills, installed_at: new Date().toISOString() }, null, 2) + "\n");
|
|
341
|
+
writeFileSync(join(stateDir, "install.json"), JSON.stringify({ version: PACKAGE_VERSION, agents: roots.map((item) => item.slice(root.length + 1)), skills: mergedSkillNames(root, skills), installed_at: new Date().toISOString() }, null, 2) + "\n");
|
|
252
342
|
console.log(`Maggie installed in ${root}`);
|
|
253
343
|
console.log("Run `maggie doctor --project .` before using mutating workflows.");
|
|
254
344
|
}
|
|
@@ -285,7 +375,7 @@ function update(args) {
|
|
|
285
375
|
if (existsSync(join(TEMPLATES_ROOT, "maggiedash")) && existsSync(join(root, "templates", "maggiedash"))) updated += syncTree(join(TEMPLATES_ROOT, "maggiedash"), join(root, "templates", "maggiedash"), force);
|
|
286
376
|
const stateDir = join(root, STATE_DIR);
|
|
287
377
|
mkdirSync(stateDir, { recursive: true });
|
|
288
|
-
writeFileSync(join(stateDir, "install.json"), JSON.stringify({ version: PACKAGE_VERSION, agents: roots.map((item) => item.slice(root.length + 1)), skills, updated_at: new Date().toISOString() }, null, 2) + "\n");
|
|
378
|
+
writeFileSync(join(stateDir, "install.json"), JSON.stringify({ version: PACKAGE_VERSION, agents: roots.map((item) => item.slice(root.length + 1)), skills: mergedSkillNames(root, skills), updated_at: new Date().toISOString() }, null, 2) + "\n");
|
|
289
379
|
console.log(`Maggie update complete: ${updated} files changed`);
|
|
290
380
|
if (!force) console.log("Local files with changes were preserved. Review the output and rerun with --force only when replacement is intended.");
|
|
291
381
|
}
|
|
@@ -359,19 +449,14 @@ function doctor(args) {
|
|
|
359
449
|
let manifest = null;
|
|
360
450
|
try { if (existsSync(manifestPath)) manifest = JSON.parse(readFileSync(manifestPath, "utf8")); } catch { manifest = null; }
|
|
361
451
|
const expected = Array.isArray(manifest?.skills) ? [...new Set(manifest.skills)] : null;
|
|
362
|
-
const
|
|
363
|
-
const installedNames = new Set();
|
|
364
|
-
for (const agentRoot of [join(root, ".agents"), join(root, ".claude")]) {
|
|
365
|
-
for (const skill of SKILL_NAMES) if (existsSync(join(agentRoot, "skills", skill, "SKILL.md"))) {
|
|
366
|
-
installed.push(`${agentRoot}/${skill}`);
|
|
367
|
-
installedNames.add(skill);
|
|
368
|
-
}
|
|
369
|
-
}
|
|
452
|
+
const installedNames = diskSkillNames(root);
|
|
370
453
|
const missing = expected ? expected.filter((skill) => !installedNames.has(skill)) : [];
|
|
371
|
-
const
|
|
372
|
-
|
|
454
|
+
const extra = expected ? [...installedNames].filter((skill) => !expected.includes(skill)).sort() : [];
|
|
455
|
+
const inventoryState = expected && (missing.length || extra.length) ? "INCOMPLETE" : "PASS";
|
|
456
|
+
console.log(`${inventoryState} skill-inventory manifest=${expected?.length ?? "unknown"} on-disk=${installedNames.size}${missing.length ? ` missing=${missing.join(",")}` : ""}${extra.length ? ` extra=${extra.join(",")}` : ""}`);
|
|
373
457
|
console.log(`INFO installed-skills=${installedNames.size}`);
|
|
374
458
|
console.log("INFO doctor is diagnostic; bootstrap completion remains a project decision gate.");
|
|
459
|
+
if (inventoryState === "INCOMPLETE") process.exitCode = 1;
|
|
375
460
|
}
|
|
376
461
|
|
|
377
462
|
function remove(args) {
|
|
@@ -422,6 +507,7 @@ try {
|
|
|
422
507
|
else if (command === "clone") workflowCli("maggie_clone.py", args);
|
|
423
508
|
else if (command === "clone-to-template") workflowCli("maggie_clone_to_template.py", args);
|
|
424
509
|
else if (command === "design" && args[0] === "icon-inventory") workflowCli("maggie_icon_inventory.py", args.slice(1));
|
|
510
|
+
else if (command === "design" && args[0] === "icon-release-gate") workflowCli("maggie_icon_release_gate.py", args.slice(1));
|
|
425
511
|
else if (command === "design") workflowCli("maggie_design.py", args);
|
|
426
512
|
else if (command === "auth") workflowCli("maggie_auth.py", args);
|
|
427
513
|
else if (command === "blog") workflowCli("maggie_blog.py", args);
|
|
@@ -432,6 +518,8 @@ try {
|
|
|
432
518
|
else if (command === "deployment" && args[0] === "canary") workflowCli("maggie_deployment_canary.py", args.slice(1));
|
|
433
519
|
else if (command === "deployment" && args[0] === "readiness") workflowCli("maggie_deployment_readiness.py", args.slice(1));
|
|
434
520
|
else if (command === "deployment" && args[0] === "parity") workflowCli("maggie_deployment_parity.py", args.slice(1));
|
|
521
|
+
else if (command === "deployment" && args[0] === "release-manifest") workflowCli("maggie_release_manifest.py", args.slice(1));
|
|
522
|
+
else if (command === "deployment" && args[0] === "runtime-preflight") workflowCli("maggie_runtime_preflight.py", args.slice(1));
|
|
435
523
|
else if (command === "deployment") workflowCli("maggie_deployment.py", args);
|
|
436
524
|
else if (command === "migration") workflowCli("maggie_migration.py", args);
|
|
437
525
|
else if (command === "schedule") workflowCli("maggie_schedule.py", args);
|
|
@@ -52,6 +52,32 @@ The validator accepts the complete provider allowlist, but a capability is
|
|
|
52
52
|
verified only when its corresponding required scope is present. Scope presence
|
|
53
53
|
alone never proves product access.
|
|
54
54
|
|
|
55
|
+
### GTM operation scope matrix
|
|
56
|
+
|
|
57
|
+
Request the union of the rows needed by one operation; do not use a broad
|
|
58
|
+
scope merely because a later operation might need it:
|
|
59
|
+
|
|
60
|
+
| GTM operation | Minimum OAuth scope |
|
|
61
|
+
|---|---|
|
|
62
|
+
| List/read account, container, tag, trigger, or variable | `https://www.googleapis.com/auth/tagmanager.readonly` |
|
|
63
|
+
| Edit container configuration | `https://www.googleapis.com/auth/tagmanager.edit.containers` |
|
|
64
|
+
| Create or update a container version | `https://www.googleapis.com/auth/tagmanager.edit.containerversions` |
|
|
65
|
+
| Publish a container version | `https://www.googleapis.com/auth/tagmanager.publish` |
|
|
66
|
+
|
|
67
|
+
An operation that edits a container and creates a version must request both
|
|
68
|
+
edit scopes; publishing additionally requires the publish scope. Record the
|
|
69
|
+
operation name and exact scope union in the capability evidence so a successful
|
|
70
|
+
read cannot be mistaken for edit or publish access.
|
|
71
|
+
|
|
72
|
+
### Missing fields are unknown
|
|
73
|
+
|
|
74
|
+
Google APIs can omit fields, especially when a proto3 field is absent from the
|
|
75
|
+
response or was not selected by the request. An omitted boolean is not
|
|
76
|
+
`false`, and an absent schema field is not evidence that the capability is
|
|
77
|
+
disabled. Reports must use `unknown`/`not_observed` in the provider adapter's
|
|
78
|
+
evidence and obtain a corroborating read or time-series observation before
|
|
79
|
+
making a negative claim. Never silently coerce a missing field to `false`.
|
|
80
|
+
|
|
55
81
|
## Provider setup and read-only preflight
|
|
56
82
|
|
|
57
83
|
1. Select one provider and one resource. Enable only that provider API in the
|
|
@@ -54,7 +54,24 @@ explicit `approve` transition before `publish`. Stable `contentId` is the
|
|
|
54
54
|
ingest identity and a published slug must not change during a rewrite.
|
|
55
55
|
Search/sort views are not indexable; drafts never appear in public routes, RSS,
|
|
56
56
|
or sitemap output. Provider keys remain server-side. Public publication and
|
|
57
|
-
|
|
57
|
+
migrations always require explicit confirmation.
|
|
58
|
+
|
|
59
|
+
Automatic publication is an opt-in host policy, not the default. Keep human
|
|
60
|
+
review enabled unless the owner explicitly chooses otherwise. If a host has
|
|
61
|
+
that approval, validate its evidence before its scheduler calls the host's
|
|
62
|
+
publish path:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
maggie blog auto-publish-gate \
|
|
66
|
+
--evidence .maggie/blog-auto-publish-evidence.json \
|
|
67
|
+
--output .maggie/blog-auto-publish-gate.json
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The `maggie-blog-auto-publish.v1` evidence must explicitly set `optIn: true`
|
|
71
|
+
and `reviewDisabledExplicitly: true`, then pass locale variants, feature image
|
|
72
|
+
and alt text, SEO fields, sitemap generation, indexing notification, and audit
|
|
73
|
+
checks. The gate never publishes; missing evidence keeps content in human
|
|
74
|
+
review and remains non-indexable.
|
|
58
75
|
|
|
59
76
|
The adapter runs as an argv array without a shell, receives a versioned request
|
|
60
77
|
on stdin, and must print only an object containing
|
|
@@ -80,6 +80,14 @@ This is a runtime contract gate, not a source-only check; it fails when a
|
|
|
80
80
|
declared endpoint is missing, uses the wrong route/method, returns a non-2xx
|
|
81
81
|
status or omits required fields.
|
|
82
82
|
|
|
83
|
+
For analytics panels and Consent Mode, keep the same evidence boundary: the
|
|
84
|
+
default consent state must be written before the container loads, first-party
|
|
85
|
+
storage behavior must be explicitly `stop` or `degrade`, and intentional form
|
|
86
|
+
events must be declared separately as `record` or `omit`. A browser report must
|
|
87
|
+
prove each decision; an absent field is unknown and must not be interpreted as
|
|
88
|
+
false. Run the analytics release gate when the dashboard or host measurement
|
|
89
|
+
contract changes.
|
|
90
|
+
|
|
83
91
|
MaggieDash 0.2.4 adds two reusable operator workspaces. Activity is a read-only
|
|
84
92
|
view backed by `/api/maggie/activity.json`; it supports actor/action filters,
|
|
85
93
|
refresh, and a safe unavailable state. Navigation is an ordered,
|
|
@@ -255,6 +263,10 @@ maggie dash schema-audit --inventory .maggie/schema-inventory.json \
|
|
|
255
263
|
# Prove a browser check mounted the screen and executed its data calls:
|
|
256
264
|
maggie dash ui runtime-validate --evidence .maggie/dashboard-runtime.json
|
|
257
265
|
|
|
266
|
+
# Validate independent dashboard measurement panels and freshness evidence:
|
|
267
|
+
maggie dash panels-validate --report .maggie/measurement-panels.json \
|
|
268
|
+
--output .maggie/measurement-panels-result.json
|
|
269
|
+
|
|
258
270
|
# Classify docs, show inbound references and completed progress trackers:
|
|
259
271
|
maggie docs audit --project . --docs-dir docs \
|
|
260
272
|
--output docs/documentation-audit.json
|
|
@@ -339,9 +351,12 @@ category, collection, or service reference; an unresolved binding is a publish
|
|
|
339
351
|
failure, not an empty state.
|
|
340
352
|
|
|
341
353
|
Repair scripts must select all candidates, compare every target field, write
|
|
342
|
-
only changed rows, and report both changed and unchanged rows.
|
|
343
|
-
|
|
344
|
-
|
|
354
|
+
only changed rows, and report both changed and unchanged rows. Each step must
|
|
355
|
+
also return a boolean `wrote`; the summary's `writes` count is the number of
|
|
356
|
+
true outcomes, not the number of executed steps. The shared section reconcile
|
|
357
|
+
command exposes `writeCount`, `unchangedCount`, and `writeOutcome` for this
|
|
358
|
+
purpose. Validate the repair contract with `idempotency-validate`; a query
|
|
359
|
+
that selects only rows that look unconverted cannot repair its own bad output.
|
|
345
360
|
|
|
346
361
|
After any script or direct adapter write to translation data, invalidate the
|
|
347
362
|
running process before verification. Record the restart and run the rendering
|
|
@@ -88,6 +88,36 @@ not fabricate browser, rendered, or deployment evidence and does not deploy
|
|
|
88
88
|
from this command. See
|
|
89
89
|
[`readiness-v1.schema.json`](../../bundled-contracts/maggie-deployment/readiness-v1.schema.json).
|
|
90
90
|
|
|
91
|
+
Classify host/runtime failures before a release can move:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
maggie deployment runtime-preflight \
|
|
95
|
+
--evidence .maggie/verification/runtime-preflight-input.json \
|
|
96
|
+
--output .maggie/verification/runtime-preflight.json
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
The `maggie-runtime-preflight.v1` input has independent checks for
|
|
100
|
+
dependencies, browser/driver compatibility, worker configuration, database
|
|
101
|
+
privileges, and routes/API. Each non-pass status must include an actionable
|
|
102
|
+
remediation. Missing checks are `unknown` and fail closed; the command never
|
|
103
|
+
connects to a host or mutates runtime state.
|
|
104
|
+
|
|
105
|
+
Before production approval, compose one cross-workflow manifest from the
|
|
106
|
+
sanitized outputs of the route, migration, runtime, asset/icon, edge-cache,
|
|
107
|
+
browser/render, and rollback checks:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
maggie deployment release-manifest \
|
|
111
|
+
--evidence .maggie/release-evidence.json \
|
|
112
|
+
--output .maggie/release-manifest.json
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The `maggie-release-evidence.v1` input links the release commit and Maggie
|
|
116
|
+
version to every required evidence slot and release fingerprint. The validator
|
|
117
|
+
fails if any slot is missing or not passed, keeps only normalized metadata, and
|
|
118
|
+
records `mutation: "not executed"`; it does not deploy or claim that a check
|
|
119
|
+
was run.
|
|
120
|
+
|
|
91
121
|
## Automatic memory hook
|
|
92
122
|
|
|
93
123
|
Follow [Maggie Memory Hook](../../references/memory-hook.md) at invocation and completion.
|
|
@@ -115,6 +145,22 @@ This command never deploys. Production execution still requires the shared
|
|
|
115
145
|
decision loop, explicit confirmation, migration/rollback details, and the
|
|
116
146
|
provider-specific deploy command.
|
|
117
147
|
|
|
148
|
+
For a VPS adapter, check the project `.env` by variable name before reporting
|
|
149
|
+
credentials as unavailable:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
maggie deployment credential-preflight \
|
|
153
|
+
--env-file .env --expected-host example-host \
|
|
154
|
+
--output .maggie/deployment/credentials.json
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The preflight checks `DEPLOY_SERVER_IP`, `DEPLOY_SERVER_SSH_USER`,
|
|
158
|
+
`DEPLOY_SERVER_SSH_PASSWORD`, and `DEPLOY_SERVER_SSH_SUDOER` by presence only.
|
|
159
|
+
It never prints or stores values. `--expected-host` is optional, but should be
|
|
160
|
+
used for production to prevent deploying to an unintended server. SSH-key
|
|
161
|
+
adapters may use their own named secret contract; map it explicitly in their
|
|
162
|
+
local plan.
|
|
163
|
+
|
|
118
164
|
When deployment verification depends on Google properties or accounts, use
|
|
119
165
|
the shared [Google integrations runbook](../../references/google-integrations-runbook.md).
|
|
120
166
|
Keep provider auth and capability evidence separate from deployment health;
|
|
@@ -146,6 +192,15 @@ carries `.agents`/`.claude` state, restarts before verification, and prunes to
|
|
|
146
192
|
the current release plus one rollback candidate. It never
|
|
147
193
|
contains credentials and never SSHs, changes DNS, restarts systemd or deploys.
|
|
148
194
|
|
|
195
|
+
VPS plans use a dedicated deployer account by default:
|
|
196
|
+
`--deployer-user maggie-deploy`. The generated sudoers contract permits only
|
|
197
|
+
the exact `/bin/systemctl restart <service>` and
|
|
198
|
+
`/bin/systemctl is-active <service>` commands for the named service. Release
|
|
199
|
+
files and the application remain separated (`maggie-deploy` versus
|
|
200
|
+
`www-data`); no wildcard, shell, `sudo su`, or broad root command is generated.
|
|
201
|
+
Install and review that narrow sudoers policy on the host before executing a
|
|
202
|
+
runner.
|
|
203
|
+
|
|
149
204
|
VPS plans include a bounded retention policy: keep two immutable releases,
|
|
150
205
|
preserve the `current` target and rollback target, and review prune candidates
|
|
151
206
|
before any operator executes cleanup. Generate a read-only candidate report:
|
|
@@ -186,6 +241,23 @@ The gate fails on a missing or mismatched target identity and never executes a
|
|
|
186
241
|
migration. A successful connection alone is not proof that the intended
|
|
187
242
|
database was selected.
|
|
188
243
|
|
|
244
|
+
Before activation, reconcile the host's migration ledger rather than relying
|
|
245
|
+
on a remembered local migration list:
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
maggie migration reconcile \
|
|
249
|
+
--evidence .maggie/migration/server-ledger.json \
|
|
250
|
+
--migration-dir migrations \
|
|
251
|
+
--output .maggie/migration/ledger-reconcile.json
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
The host adapter must query `maggie_schema_migrations` and emit only the
|
|
255
|
+
version identifiers and environment. The read-only command orders local
|
|
256
|
+
numbered SQL files, reports `missing`, and remains `mutation: "not executed"`.
|
|
257
|
+
Apply every missing file through the host migration runner, re-read the ledger,
|
|
258
|
+
and only then continue to the release switch; a missing version is a failed
|
|
259
|
+
deployment preflight, not evidence that activation can proceed.
|
|
260
|
+
|
|
189
261
|
If a release depends on existing rows or seeded data, set
|
|
190
262
|
`dataDependencies: true` in `.maggie/migration-manifest.json` and provide
|
|
191
263
|
`.maggie/deployment/data-release.json` before deployment. The checkpoint must
|
|
@@ -39,6 +39,19 @@ owner before any remote mutation.
|
|
|
39
39
|
|
|
40
40
|
### SSH and secret-source contract
|
|
41
41
|
|
|
42
|
+
First run the shared credential presence check against the project `.env`:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
maggie deployment credential-preflight --env-file .env \
|
|
46
|
+
--expected-host example-host
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The canonical password-based names are `DEPLOY_SERVER_IP`,
|
|
50
|
+
`DEPLOY_SERVER_SSH_USER`, `DEPLOY_SERVER_SSH_PASSWORD`, and
|
|
51
|
+
`DEPLOY_SERVER_SSH_SUDOER`. The command reports only presence and an optional
|
|
52
|
+
host match; it never prints a value. A provider may instead use an SSH key, but
|
|
53
|
+
must state that alternate name and source in its plan.
|
|
54
|
+
|
|
42
55
|
Use these canonical project variables when a VPS adapter is configured:
|
|
43
56
|
|
|
44
57
|
| Variable | Meaning | Precedence |
|
|
@@ -86,6 +99,19 @@ The generated Nginx/Astro boundary keeps `checkOrigin` enabled and requires
|
|
|
86
99
|
canonical HTTPS `allowedDomains` plus trusted `X-Forwarded-Host`/
|
|
87
100
|
`X-Forwarded-Proto` handling when TLS terminates at the proxy.
|
|
88
101
|
|
|
102
|
+
Use a dedicated least-privilege deployer when generating the plan:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
python3 tools/clis/maggie_deployment.py . --vps-plan \
|
|
106
|
+
--domain example.co.uk --service example --node-port 4321 \
|
|
107
|
+
--deployer-user maggie-deploy
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
The plan separates the SSH/deploy account from the `www-data` application
|
|
111
|
+
account and allowlists only the exact service restart and status commands.
|
|
112
|
+
The runner uses non-interactive `sudo -n`; it never requires broad sudo or a
|
|
113
|
+
shell escalation. Review/install the host sudoers entry separately.
|
|
114
|
+
|
|
89
115
|
Review the generated systemd and Nginx files, then obtain explicit approval
|
|
90
116
|
before installing them on a host.
|
|
91
117
|
|