@topy-ai/maggie 0.8.0 → 0.8.1

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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [English README](README.md) · [完整繁體中文 README](https://github.com/TOPY-AI-LTD/ai-cmo-skills/blob/main/README.zh-TW.md)
4
4
 
5
- `@topy-ai/maggie` 提供 20 個可安裝的 AI website workflow skills,支援
5
+ `@topy-ai/maggie` 提供 24 個可安裝的 AI website workflow skills,支援
6
6
  Codex、Claude Code 與相容的 coding agents。
7
7
 
8
8
  ## 安裝
@@ -143,7 +143,7 @@ structure drift 或重複 treatment product 時 fail closed。
143
143
  deployment、memory、feedback 和 MaggieDash。內容先 draft/review,外部寫入、
144
144
  publish 與 production deployment 需要明確確認。
145
145
 
146
- 目前 npm 版本為 `0.8.0`,安全優化 roadmap(scan、image-patch、標頭評分、SSVC、GDPR 日誌規則、新 MCP 工具),含 fix-plan 修補風險分級 T1–T4(判斷補丁是否安全套用)與 host-impact 補丁影響面量測(哪些服務需重啟、是否需重開機)、public-check、Maggie Security、batch 31 排程 agent routines、報告收件匣與無人值守 agent 限制、PHP runtime 盤點與主機鎖、pull 網站改寫、query 點子與社群佇列檢查,batch 30 MaggieDash 先裝核心、release privacy gate、feedback 備援、社群連結與圖片檢查、大量頁面產生與 app 檢查,batch 29 預約規則、部署、app QA、助理與社群功能,以及 batch 28 產品 app、assistant tools、mobile、product release、skills catalog 與 bridge host 功能、batch 26、27 feedback 修正與 idempotent `maggie update`與 Booking email-template 合約/CLI validator 同步。本版完成 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
146
+ 目前 npm 版本為 `0.8.1`,新增 maggie-spirit(為 AI 品牌生成平面像素 spirit/sprite 系統:logo mark 與各 agent avatar 皆以共用的 32 狀態基礎集播放動畫)與 goal-led AI agent 設計參考,並補上 ONEMAN.APP in-place 開發的部署與設計筆記(靜態站先 build 再部署、Astro 執行期注入節點樣式、in-place DESIGN.md 擷取)。先前 0.8.0 為安全優化 roadmap(scan、image-patch、標頭評分、SSVC、GDPR 日誌規則、新 MCP 工具),含 fix-plan 修補風險分級 T1–T4(判斷補丁是否安全套用)與 host-impact 補丁影響面量測(哪些服務需重啟、是否需重開機)、public-check、Maggie Security、batch 31 排程 agent routines、報告收件匣與無人值守 agent 限制、PHP runtime 盤點與主機鎖、pull 網站改寫、query 點子與社群佇列檢查,batch 30 MaggieDash 先裝核心、release privacy gate、feedback 備援、社群連結與圖片檢查、大量頁面產生與 app 檢查,batch 29 預約規則、部署、app QA、助理與社群功能,以及 batch 28 產品 app、assistant tools、mobile、product release、skills catalog 與 bridge host 功能、batch 26、27 feedback 修正與 idempotent `maggie update`與 Booking email-template 合約/CLI validator 同步。本版完成 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
147
147
  dispatch、`maggie --version`、feedback batch review 聚合與重複偵測、marketplace enrichment evidence、booking
148
148
  worker/resolver evidence,以及 read-only migration preflight。它也包含 host-owned mobile app surface contract、signed-in
149
149
  camera-state QA、直接 Astro route resolution、correlated feedback batch,以及
package/README.md CHANGED
@@ -87,9 +87,9 @@ Then invoke the installed skills from your coding agent, for example:
87
87
 
88
88
  Maggie keeps the existing project foundation and asks for decisions before
89
89
  shared routes, analytics, or publishing boundaries change. The current
90
- package ships 23 installable skills and a local-first MaggieDash foundation.
90
+ package ships 24 installable skills and a local-first MaggieDash foundation.
91
91
 
92
- This package is `0.8.0` — the security optimization roadmap: one-shot `scan`, `image-patch` (copa) container remediation, A+…F header grade, SSVC rationale in reports, a GDPR log-privacy rule in code-scan, five more MCP tools (35), and pinned scanners, on top of safe patching (fix-risk tiers + host-impact).
92
+ This package is `0.8.1` — adds `maggie-spirit` (generate an AI brand's flat-pixel spirit/sprite system: a logo mark plus per-agent avatars animated through a shared 32-state base set) and a goal-led AI agent design reference, plus deployment and design notes from the ONEMAN.APP in-place build (static-site build-before-deploy, Astro runtime-injected DOM styling, in-place DESIGN.md extraction).
93
93
 
94
94
  The 0.7.59 release made `maggie security host-audit` (the deep
95
95
  lynis audit over SSH) robust on hosts where a storage test can stall: lynis runs
@@ -0,0 +1,92 @@
1
+ # Goal-led AI agent design
2
+
3
+ A reusable method for defining the agents in a multi-agent product so a single
4
+ human can run a company by stating intent, not by operating tools. Distilled
5
+ from the ONEMAN.APP cofounder build; pairs with the `maggie-spirit` skill (give
6
+ each designed agent a spirit/avatar) and `maggie-assistant-tools` (expose an
7
+ agent's capabilities safely).
8
+
9
+ ## Principle
10
+
11
+ The human sets a **goal**; the agents turn it into work and keep the human in
12
+ the loop only for judgement and approval. Each agent owns one lane, declares the
13
+ **capability classes** it may use, and runs under a **risk ceiling** with
14
+ explicit approval gates. Nothing that is risky, public, or spends money happens
15
+ without a human yes.
16
+
17
+ ## Define each agent with a role contract
18
+
19
+ One `role.yaml` per agent. Recommended fields:
20
+
21
+ ```yaml
22
+ role: CTO # the lane
23
+ name: AI CTO # display name
24
+ persona: Turing # inspiration, not impersonation
25
+ spirit: Engineering # one-word identity (drives the spirit accent/motif)
26
+ summary: > # what it owns, in one or two sentences
27
+ Technical design, build, integrate, test and deploy. Production deployment is
28
+ gated behind human approval.
29
+ responsibilities: [design, build, integrate, test, deploy, observe]
30
+ constraints: [no prod deploy without approval, no secrets in source]
31
+ allowed_capability_classes: [app.scaffold, app.build, app.test, code.review,
32
+ deploy.release, integration.connect, ops.observe, job.execute]
33
+ default_risk_ceiling: L1 # L0 read -> L3 irreversible/public/paid
34
+ business_system: Execution # which paired system (e.g. with COO)
35
+ raci: { new_service_app: "A", security_verdict: I }
36
+ interfaces: { mcp: planned, cli: planned, api: planned }
37
+ ```
38
+
39
+ - **Capability classes**, not freeform skills, are the unit of authority. They
40
+ are stable `area.verb` identifiers (`content.publish`, `revenue.close`,
41
+ `security.scan`) that a UI can list and a gate can check. These are the
42
+ "skills" a product surfaces per agent.
43
+ - **One CEO orchestrates.** The CEO owns `goal.decompose`, `mission.manage`,
44
+ `plan.compose`, `priority.rank`, `outcome.accept`: it turns intent into
45
+ missions with acceptance criteria, delegates each to the right agent, and
46
+ signs off - it never bypasses security or budget approval.
47
+ - **Security holds a non-overridable veto.** A high-risk security verdict cannot
48
+ be overruled by another agent.
49
+ - **Finance is explain-only.** Financial facts come from deterministic data
50
+ (billing/usage ledger, Stripe); the CFO reconciles and advises, it is not the
51
+ source of truth.
52
+
53
+ ## Risk ceilings and approval gates
54
+
55
+ | Level | Meaning | Gate |
56
+ |---|---|---|
57
+ | L0 | read-only | none |
58
+ | L1 | internal writes (drafts, plans, workflows) | logged |
59
+ | L2 | customer-facing or public (publish, close a deal) | human approval |
60
+ | L3 | irreversible / paid / production (deploy, transfer) | human approval + audit |
61
+
62
+ Every agent declares a `default_risk_ceiling`; an action above it stops for an
63
+ explicit human yes. Log every action; make L2/L3 produce an approval card.
64
+
65
+ ## The goal loop
66
+
67
+ 1. The human states what the business should do.
68
+ 2. The CEO writes missions with acceptance criteria.
69
+ 3. Each mission is delegated to the agent whose lane and capability classes fit.
70
+ 4. Agents work their lane; risky/public/paid steps wait for approval.
71
+ 5. The CEO reviews outcomes against the acceptance criteria and signs off, or
72
+ sends the mission back.
73
+ 6. The human stays the only human in the loop - approve, steer, ship.
74
+
75
+ ## Surfacing agents to users
76
+
77
+ Each agent reads cleanly as a profile: persona name, spirit, its capability
78
+ classes (its "skills"), and the service apps it manages. Give it a spirit/avatar
79
+ with `maggie-spirit` (shared 32-state motion vocabulary, per-agent accent) so
80
+ status (idle, working, deploying, success, blocked, secure) reads the same
81
+ across the whole team. Mark an agent "shipped" only when its product is actually
82
+ live.
83
+
84
+ ## Checklist
85
+
86
+ - [ ] One lane per agent; no two agents own the same decision.
87
+ - [ ] Capability classes are `area.verb`, stable, and enumerable.
88
+ - [ ] A single orchestrator (CEO) owns decomposition and sign-off.
89
+ - [ ] Security veto and finance-is-explain-only are encoded, not assumed.
90
+ - [ ] Every agent has a risk ceiling; L2/L3 actions gate on human approval.
91
+ - [ ] Persona is inspiration, not impersonation of a real person.
92
+ - [ ] Each agent has a spirit/avatar from the shared 32-state base set.
@@ -90,6 +90,10 @@
90
90
  "name": "maggie-social-share",
91
91
  "description": "Turn approved AI CMO articles and content assets into platform-native social drafts, scheduled posts, and measured distribution. Use for Social Share planning, LinkedIn/Facebook copy, CTA mapping, calendars, approvals, publish retries, or social performance readback."
92
92
  },
93
+ {
94
+ "name": "maggie-spirit",
95
+ "description": "Generate an AI brand's flat-pixel spirit (sprite) system - a logo mark that animates through a fixed 32-state base set, and per-agent avatar faces animated through the same states. Use for agent/brand avatars, logo marks, sprite-sheet and sprite-animation assets, and a shared motion vocabulary across Core and Service agents."
96
+ },
93
97
  {
94
98
  "name": "maggie-template",
95
99
  "description": "Discover, recommend, inspect, and apply verified Astro homepage templates from the Maggie marketplace. Use when a user wants to start a site from a selected template, compare available designs, or choose a visual direction before maggie-clone or maggie-design."
@@ -2,7 +2,7 @@
2
2
  name: maggie-deployment
3
3
  description: Deploy and operate Maggie blog projects with Cloudflare Workers as the default target, while preserving an adapter boundary for VPS, GCP, and AWS.
4
4
  metadata:
5
- version: 1.6.0
5
+ version: 1.6.1
6
6
  ---
7
7
 
8
8
  # Maggie Deployment
@@ -749,3 +749,23 @@ discovery, install-manifest writes, or file synchronization. For fixture-backed
749
749
  browser QA, the host owns the isolated dev server and data source; document a
750
750
  second port/process and fixture mode in the host runbook instead of making the
751
751
  shared updater or deployment CLI start an unmanaged daemon.
752
+
753
+ ## Static sites: build first, ship the build output only
754
+
755
+ A static-site deploy must run the framework build and ship **only** the build
756
+ output, never the source tree. A pipeline that copies the project directory and
757
+ serves it (for example `rsync webapp/ ...` then `python -m http.server`) exposes
758
+ the source and, with no `index.html` at the root, serves a directory listing
759
+ instead of the site.
760
+
761
+ The deploy step must therefore:
762
+
763
+ 1. install and build in the project (`npm ci && npm run build`), then
764
+ 2. ship only the build artifact (`dist/`, `build/`, `.output/public`, ...) to the
765
+ web root, and
766
+ 3. verify the live root returns the rendered page (a `200` whose body contains
767
+ the expected `<title>`/`<h1>`), not an autoindex.
768
+
769
+ Treat "the root is a directory listing" or "source files (`package.json`,
770
+ `src/`) are reachable over HTTP" as a failed deploy. Prefer a build artifact
771
+ check in CI over trusting that a copy step shipped the right directory.
@@ -2,7 +2,7 @@
2
2
  name: maggie-design
3
3
  description: Design authorized interior pages, review rendered responsive layouts, or explicitly rebrand a packaged homepage/template. Use rebrand only with a named source brand and target brand.
4
4
  metadata:
5
- version: 1.6.0
5
+ version: 1.6.1
6
6
  ---
7
7
 
8
8
  # Maggie Design
@@ -853,3 +853,31 @@ without an explicit host contract. Decode HTML entities exactly once; unknown
853
853
  or double-escaped entities stop the check. Optional empty attributes such as
854
854
  `style=""` must be omitted. Codemods using compiler positions must anchor at
855
855
  the opening `<tag` before slicing source text.
856
+
857
+ ## Front-end pitfalls from in-place builds
858
+
859
+ ### Runtime-injected DOM does not get scoped styles
860
+
861
+ Astro (and other scoped-style systems) add a per-component attribute
862
+ (`data-astro-cid-*`) only to nodes present in the template at build time. Nodes
863
+ you create at runtime with `innerHTML`/`createElement` never receive it, so
864
+ scoped `<style>` rules silently do not match them - a tab, card, or image built
865
+ in script renders unstyled while the identical server-rendered markup looks
866
+ correct. Style runtime-injected content with `<style is:global>` (scoped by an
867
+ id/class you control, e.g. `#my-modal .card { ... }`) or with inline styles.
868
+ Verify with rendered-browser evidence, not a static class scan: the static
869
+ selector exists, but the element it should match does not carry the scope.
870
+
871
+ ### Extract DESIGN.md in-place from the project's own homepage
872
+
873
+ `maggie_design_system.py generate` builds a DESIGN.md from clone evidence, which
874
+ an in-place redesign does not have. When the design source of truth is the
875
+ project's existing homepage, author `DESIGN.md` directly from it: read the
876
+ `:root` tokens, fonts, composition, responsive and interaction rules from the
877
+ live page, write the eight required headings (`## Source and evidence`,
878
+ `## Design tokens`, `## Composition rules`, `## Responsive rules`,
879
+ `## Interaction rules`, `## Shared shell contract`, `## New page contract`,
880
+ `## Unknowns and approvals`), and include the `Generated by Maggie` marker line
881
+ so `maggie tool maggie_design_system.py validate DESIGN.md` passes. State in
882
+ `## Source and evidence` that it was extracted in-place with no external clone.
883
+ This unlocks `maggie design in-place` for a route without a clone run.
@@ -0,0 +1,128 @@
1
+ ---
2
+ name: maggie-spirit
3
+ description: Generate an AI brand's flat-pixel spirit (sprite) system - a logo mark that animates through a fixed 32-state base set, and per-agent avatar faces animated through the same states. Use for agent/brand avatars, logo marks, sprite-sheet and sprite-animation assets, and a shared motion vocabulary across Core and Service agents.
4
+ metadata:
5
+ version: 0.1.0
6
+ ---
7
+
8
+ # Maggie Spirit
9
+
10
+ Give an agent brand one flat-pixel visual language: a **logo mark** and a set of
11
+ **agent avatars** that all animate through the **same fixed catalog of 32
12
+ states** (power, work, comms, outcome, gov). Agents differ only by their
13
+ **motif/face** and their **accent** hue; the motion vocabulary, semantic palette
14
+ and status badges stay identical so a state reads the same everywhere.
15
+
16
+ Everything is deterministic (seeded by accent + persona) and self-contained
17
+ (Pillow only - no model calls, no per-image cost). It pairs with
18
+ [agent goal-led design](../../references/agent-goal-design.md): design the agent,
19
+ then give it a spirit.
20
+
21
+ Follow the [Maggie Memory Hook](../../references/memory-hook.md) at invocation
22
+ and completion.
23
+
24
+ ## When to use
25
+
26
+ - You are standing up a multi-agent product (a C-suite of AI cofounders, a set
27
+ of Service Apps) and want a consistent avatar + logo + status-animation set.
28
+ - You need a logo mark that can show live state (idle, working, deploying,
29
+ success, warning, secure, ...) rather than a single static glyph.
30
+ - You need sprite sheets / animation strips + a `states.json` manifest to drive
31
+ a web UI (the CSS `steps()` technique), not a one-off illustration.
32
+
33
+ For page/layout design use `maggie-design`; for marketplace templates use
34
+ `maggie-marketplace`; this skill produces the brand's character assets only.
35
+
36
+ ## The 32-state base set
37
+
38
+ Five groups, fixed across every agent so severity and status read the same:
39
+
40
+ - **power:** off, booting, idle, active, paused, offline
41
+ - **work:** thinking, loading, syncing, writing, streaming, queued, scanning, deploying
42
+ - **comms:** speaking, listening, notification, mention, recording
43
+ - **outcome:** success, warning, critical, blocked, error, retrying
44
+ - **gov:** awaiting, approved, rejected, secure, vulnerable, maintenance, celebrating
45
+
46
+ Each state carries its own frame count, fps, motion and optional badge; every
47
+ state animates (none is static). The per-state `[frames, fps]` are written to
48
+ `states.json` beside the images so a UI can play each strip exactly.
49
+
50
+ ## CLI
51
+
52
+ Run through the bundled tool so it uses the current project as the working
53
+ directory:
54
+
55
+ ```bash
56
+ # logo mark + 32-state animated base set (anim-*.png, mark-*.svg, sprite-sheet.png, states.json)
57
+ maggie tool maggie_spirit.py mark --out public/assets/brand --name oneman \
58
+ --accent "#ff1212" --title ONEMAN
59
+
60
+ # one agent avatar animated through all 32 states (+ stills + sheet + states.json)
61
+ maggie tool maggie_spirit.py agent --out public/assets/brand --name cto \
62
+ --accent "#3b82f6" --spec cto.persona.json
63
+
64
+ # a single still avatar face
65
+ maggie tool maggie_spirit.py avatar --out public/assets/brand/cto/face.png \
66
+ --accent "#3b82f6" --spec cto.persona.json
67
+
68
+ # the reference base set to copy and recolour: default mark + example cast + founder
69
+ maggie tool maggie_spirit.py demo --out public/assets/brand
70
+ ```
71
+
72
+ `persona.json` (all keys optional except implied by your design):
73
+
74
+ ```json
75
+ {
76
+ "gender": "m",
77
+ "hair": "#2a2018",
78
+ "skin": "light | mid | tan | deep",
79
+ "hairstyle": "short|slick|spiky|buzz|longwavy|longstraight|longbob|ponytail",
80
+ "eyes": "round|wide|lash|happy",
81
+ "mouth": "flat|smile|smirk|lips|open",
82
+ "prop": "glasses|shades|cigar|headset|earbuds|monocle|pen|bandana",
83
+ "propcol": "#f2c75a",
84
+ "hoodie": true, "band": "#e23b3b", "mask": "#12201a", "specs": true
85
+ }
86
+ ```
87
+
88
+ ## Output contract
89
+
90
+ For each target (`<out>/<name>/`):
91
+
92
+ - `anim-<state>.png` - one horizontal strip per state (frames laid left to right).
93
+ - `still-<state>.png` - a representative still per state (agents only).
94
+ - `mark-<state>.svg` - a crisp SVG still per state (mark only).
95
+ - `sprite-sheet.png` - an overview contact sheet.
96
+ - `states.json` - `{ title, accent, N, scale, groups, states: { <state>: {group, frames, fps, motion, badge} } }`.
97
+
98
+ ## Driving the animation in a UI
99
+
100
+ Flat-pixel, pixel-exact. Use CSS `steps()` with the per-state frame count, never
101
+ percentage-position stepping:
102
+
103
+ ```css
104
+ .sprite{
105
+ --fw: 48px; /* one frame = one cell */
106
+ width: var(--fw); height: var(--fw);
107
+ background: url(/assets/brand/cto/anim-active.png) no-repeat;
108
+ background-size: calc(var(--n) * var(--fw)) var(--fw);
109
+ image-rendering: pixelated;
110
+ animation: play calc(var(--n) / var(--fps) * 1s) steps(var(--n)) infinite;
111
+ }
112
+ @keyframes play { to { background-position-x: calc(-1 * var(--n) * var(--fw)); } }
113
+ ```
114
+
115
+ Set `--n` and `--fps` per state from `states.json`. If you build the sprite
116
+ element with `innerHTML` in an Astro page, style it with `<style is:global>` -
117
+ Astro's scoped styles do not reach runtime-injected nodes (see `maggie-design`).
118
+
119
+ ## Boundaries
120
+
121
+ - Deterministic and local: no LLM/image-model calls and no per-image billing.
122
+ Pillow is the only dependency.
123
+ - The mark is the brand's own; recolour via `--accent`, keep the semantic
124
+ palette (severity colours) fixed so status stays legible across agents.
125
+ - Do not hand-edit generated files; change the persona/accent and regenerate so
126
+ the whole set stays consistent.
127
+ - Store generated art under the host project's assets; keep source personas
128
+ (the `*.persona.json`) in the repo so the set is reproducible.
@@ -38,6 +38,11 @@ def project_root(args: argparse.Namespace) -> Path:
38
38
 
39
39
 
40
40
  def store_for(root: Path) -> MaggieDashStore:
41
+ # ONEMAN forbids SQLite: when MAGGIEDASH_DATABASE_URL is set, back the store
42
+ # with Postgres. Otherwise fall back to the local SQLite dev store.
43
+ dsn = os.environ.get("MAGGIEDASH_DATABASE_URL")
44
+ if dsn:
45
+ return MaggieDashStore(dsn=dsn)
41
46
  return MaggieDashStore(root / ".maggie" / "maggiedash.sqlite")
42
47
 
43
48
 
@@ -642,6 +647,9 @@ def command_recommend(args: argparse.Namespace) -> int:
642
647
  def command_init(args: argparse.Namespace) -> int:
643
648
  require_confirm(args)
644
649
  root = project_root(args)
650
+ if str(getattr(args, "adapter", "")).endswith("postgres") and not os.environ.get("MAGGIEDASH_DATABASE_URL"):
651
+ print("POSTGRES_DSN_REQUIRED: adapter is postgres but MAGGIEDASH_DATABASE_URL is not set.", file=sys.stderr)
652
+ return 2
645
653
  store = store_for(root)
646
654
  try:
647
655
  project = store.init_project(args.project_id, args.name, args.adapter, args.locale)
@@ -652,9 +660,9 @@ def command_init(args: argparse.Namespace) -> int:
652
660
  "name": args.name,
653
661
  "adapter": args.adapter,
654
662
  "locale": args.locale,
655
- "storage": str(store.path),
663
+ "storage": store.storage_label,
656
664
  }, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
657
- emit({"status": "initialized", "project": project, "contract": str(contract), "storage": str(store.path)})
665
+ emit({"status": "initialized", "project": project, "contract": str(contract), "storage": store.storage_label})
658
666
  return 0
659
667
  finally:
660
668
  store.close()
@@ -667,7 +675,7 @@ def command_status(args: argparse.Namespace) -> int:
667
675
  try:
668
676
  project = store.connection.execute("SELECT * FROM maggiedash_projects WHERE id=?", (project_id,)).fetchone()
669
677
  documents = store.connection.execute("SELECT id, kind, title, slug, status, updated_at FROM maggiedash_documents WHERE project_id=? ORDER BY updated_at DESC", (project_id,)).fetchall()
670
- emit({"status": "ready" if project else "not_initialized", "project": dict(project) if project else None, "documents": [dict(row) for row in documents], "storage": str(store.path)})
678
+ emit({"status": "ready" if project else "not_initialized", "project": dict(project) if project else None, "documents": [dict(row) for row in documents], "storage": store.storage_label})
671
679
  return 0 if project else 1
672
680
  finally:
673
681
  store.close()
@@ -680,7 +688,7 @@ def command_migrate(args: argparse.Namespace) -> int:
680
688
  try:
681
689
  store.connection.execute("PRAGMA user_version = 1")
682
690
  store.connection.commit()
683
- emit({"status": "migrated", "schemaVersion": "maggiedash-storage.v1", "storage": str(store.path)})
691
+ emit({"status": "migrated", "schemaVersion": "maggiedash-storage.v1", "storage": store.storage_label})
684
692
  return 0
685
693
  finally:
686
694
  store.close()
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env python3
2
+ """maggie_spirit — generate an AI brand's pixel spirit (sprite) system.
3
+
4
+ One flat-pixel visual language for an agent brand:
5
+ * a logo "mark" that animates through a fixed 32-state base set, and
6
+ * per-agent avatar faces that animate through the same 32 states.
7
+
8
+ Everything is deterministic (seeded by accent + persona) and self-contained
9
+ (Pillow only). See skills/maggie-spirit/SKILL.md for the workflow.
10
+
11
+ Subcommands:
12
+ mark --out DIR [--name mark] [--accent #ff1212] [--title ONEMAN] [--scale 9]
13
+ Logo mark + 32-state animated base set + sprite sheet + states.json.
14
+ agent --out DIR --name cto --accent #3b82f6 --spec persona.json
15
+ One agent avatar animated through all 32 states (+ sheet + states.json).
16
+ avatar --out FILE.png --accent #3b82f6 --spec persona.json [--scale 10]
17
+ A single still avatar face (no animation).
18
+ demo --out DIR
19
+ The reference spirit base set: the default mark + 8 example agents +
20
+ founder — a starter kit to copy and recolour.
21
+
22
+ persona.json (agent/avatar), all keys optional except noted:
23
+ {"gender":"m|f","hair":"#2a2018","skin":"light|mid|tan|deep",
24
+ "hairstyle":"short|slick|spiky|buzz|longwavy|longstraight|longbob|ponytail",
25
+ "eyes":"round|wide|lash|happy","mouth":"flat|smile|smirk|lips|open",
26
+ "prop":"glasses|shades|cigar|headset|earbuds|monocle|pen|bandana",
27
+ "propcol":"#f2c75a","hoodie":true,"band":"#e23b3b","mask":"#12201a","specs":true}
28
+ """
29
+ import argparse, json, os, sys
30
+
31
+ HERE = os.path.dirname(os.path.abspath(__file__))
32
+ RUNTIME = os.path.normpath(os.path.join(HERE, "..", "runtime"))
33
+ if RUNTIME not in sys.path:
34
+ sys.path.insert(0, RUNTIME)
35
+
36
+ try:
37
+ import spirit_base as B
38
+ import spirit_face as F
39
+ import spirit_agent as A
40
+ except ImportError as e: # pragma: no cover
41
+ sys.stderr.write(
42
+ "maggie_spirit needs Pillow and the spirit_* runtime modules.\n"
43
+ f" {e}\n pip install pillow\n")
44
+ raise SystemExit(2)
45
+
46
+
47
+ def _load_spec(path):
48
+ if not path:
49
+ return {}
50
+ with open(path, "r", encoding="utf-8") as fh:
51
+ spec = json.load(fh)
52
+ if not isinstance(spec, dict):
53
+ raise SystemExit("persona spec must be a JSON object")
54
+ return spec
55
+
56
+
57
+ def cmd_mark(a):
58
+ B.OUT_BASE = os.path.abspath(a.out)
59
+ os.makedirs(B.OUT_BASE, exist_ok=True)
60
+ meta = B.generate(B.Motif.oneman(), accent=a.accent, out=a.name,
61
+ title=a.title, scale=a.scale)
62
+ print(f"{a.title}: {len(meta['states'])} states -> {os.path.join(a.out, a.name)}/")
63
+ print("groups:", ", ".join(meta.get("groups", [])))
64
+
65
+
66
+ def _persona(spec):
67
+ gender = spec.get("gender", "m")
68
+ params = {k: v for k, v in spec.items() if k != "gender"}
69
+ params.setdefault("hair", "#1c1c22")
70
+ params.setdefault("skin", "mid")
71
+ return gender, params
72
+
73
+
74
+ def cmd_agent(a):
75
+ A.OUT_BASE = os.path.abspath(a.out)
76
+ os.makedirs(A.OUT_BASE, exist_ok=True)
77
+ gender, params = _persona(_load_spec(a.spec))
78
+ meta = A.gen_agent(a.name.lower(), a.accent, gender, params, scale=a.scale)
79
+ print(f"{a.name} {a.accent} {gender}: {len(meta['states'])} states -> "
80
+ f"{os.path.join(a.out, a.name.lower())}/")
81
+
82
+
83
+ def cmd_avatar(a):
84
+ gender, params = _persona(_load_spec(a.spec))
85
+ img = F.render(F.face(a.accent, gender=gender, **params), a.scale)
86
+ out = os.path.abspath(a.out)
87
+ os.makedirs(os.path.dirname(out) or ".", exist_ok=True)
88
+ img.save(out)
89
+ print(f"avatar -> {a.out} ({img.size[0]}x{img.size[1]})")
90
+
91
+
92
+ def cmd_demo(a):
93
+ base = os.path.abspath(a.out)
94
+ os.makedirs(base, exist_ok=True)
95
+ B.OUT_BASE = base
96
+ A.OUT_BASE = base
97
+ B.generate(B.Motif.oneman(), accent="#ff1212", out="oneman", title="ONEMAN")
98
+ made = ["oneman (mark)"]
99
+ for role, acc, g, p in list(F.CAST) + [F.FOUNDER]:
100
+ A.gen_agent(role.lower(), acc, g, p)
101
+ made.append(role.lower())
102
+ print(f"spirit base set -> {a.out}/")
103
+ print("generated:", ", ".join(made))
104
+
105
+
106
+ def main(argv=None):
107
+ ap = argparse.ArgumentParser(prog="maggie_spirit", description=__doc__,
108
+ formatter_class=argparse.RawDescriptionHelpFormatter)
109
+ sub = ap.add_subparsers(dest="cmd", required=True)
110
+
111
+ m = sub.add_parser("mark", help="logo mark + 32-state animated base set")
112
+ m.add_argument("--out", required=True)
113
+ m.add_argument("--name", default="mark")
114
+ m.add_argument("--accent", default="#ff1212")
115
+ m.add_argument("--title", default="ONEMAN")
116
+ m.add_argument("--scale", type=int, default=9)
117
+ m.set_defaults(fn=cmd_mark)
118
+
119
+ ag = sub.add_parser("agent", help="one agent avatar through all 32 states")
120
+ ag.add_argument("--out", required=True)
121
+ ag.add_argument("--name", required=True)
122
+ ag.add_argument("--accent", required=True)
123
+ ag.add_argument("--spec", help="persona.json")
124
+ ag.add_argument("--scale", type=int, default=8)
125
+ ag.set_defaults(fn=cmd_agent)
126
+
127
+ av = sub.add_parser("avatar", help="a single still avatar face")
128
+ av.add_argument("--out", required=True)
129
+ av.add_argument("--accent", required=True)
130
+ av.add_argument("--spec", help="persona.json")
131
+ av.add_argument("--scale", type=int, default=10)
132
+ av.set_defaults(fn=cmd_avatar)
133
+
134
+ d = sub.add_parser("demo", help="reference spirit base set (mark + examples)")
135
+ d.add_argument("--out", required=True)
136
+ d.set_defaults(fn=cmd_demo)
137
+
138
+ a = ap.parse_args(argv)
139
+ a.fn(a)
140
+
141
+
142
+ if __name__ == "__main__":
143
+ main()