@topy-ai/maggie 0.7.61 → 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.
Files changed (29) hide show
  1. package/README-zh-TW.md +2 -2
  2. package/README.md +5 -5
  3. package/bundled-references/agent-goal-design.md +92 -0
  4. package/bundled-skills/catalog.json +4 -0
  5. package/bundled-skills/maggie-deployment/SKILL.md +21 -1
  6. package/bundled-skills/maggie-design/SKILL.md +29 -1
  7. package/bundled-skills/maggie-security/SKILL.md +10 -2
  8. package/bundled-skills/maggie-security/references/commands.md +10 -3
  9. package/bundled-skills/maggie-security/references/explaining-findings.md +7 -0
  10. package/bundled-skills/maggie-security/references/tools-and-licences.md +8 -0
  11. package/bundled-skills/maggie-security/registry/security-tools.registry.json +748 -652
  12. package/bundled-skills/maggie-spirit/SKILL.md +128 -0
  13. package/bundled-tools/clis/maggie_dash.py +12 -4
  14. package/bundled-tools/clis/maggie_security.py +1 -1
  15. package/bundled-tools/clis/maggie_security_code.py +49 -1
  16. package/bundled-tools/clis/maggie_security_deps.py +95 -0
  17. package/bundled-tools/clis/maggie_security_mcp.py +67 -1
  18. package/bundled-tools/clis/maggie_security_report.py +31 -2
  19. package/bundled-tools/clis/maggie_security_scan.py +168 -0
  20. package/bundled-tools/clis/maggie_security_web.py +45 -1
  21. package/bundled-tools/clis/maggie_spirit.py +143 -0
  22. package/bundled-tools/clis/security_data/tools.lock.json +36 -7
  23. package/bundled-tools/runtime/maggie_dash_pg.py +126 -0
  24. package/bundled-tools/runtime/maggie_dash_store.py +17 -5
  25. package/bundled-tools/runtime/spirit_agent.py +262 -0
  26. package/bundled-tools/runtime/spirit_base.py +256 -0
  27. package/bundled-tools/runtime/spirit_face.py +222 -0
  28. package/package.json +1 -1
  29. package/references/agent-goal-design.md +92 -0
@@ -0,0 +1,222 @@
1
+ #!/usr/bin/env python3
2
+ """ONEMAN Core-Agent faces — the O mark IS the face.
3
+ Thin accent rim (bigger face) -> larger, varied eyes. Mixed gender; women wear
4
+ long hair. Distinct hair silhouette + one personality prop per agent."""
5
+ import math
6
+ from PIL import Image, ImageDraw
7
+ W=H=28
8
+ def _rgb(h):h=h.lstrip('#');return(int(h[0:2],16),int(h[2:4],16),int(h[4:6],16))
9
+ def shade(c,a):
10
+ f=lambda v:max(0,min(255,round(v+(a/100)*((255-v)if a>0 else v))));return(f(c[0]),f(c[1]),f(c[2]))
11
+ OUT=_rgb('14141e');WHT=_rgb('f7f7fb');EYE=_rgb('1b1b26')
12
+ cx,cy=14,13;R_OUT=11.0;R_IN=8.6 # thin rim -> big face
13
+ SKINS={"light":_rgb('f0c49a'),"mid":_rgb('d89c70'),"tan":_rgb('c6895a'),"deep":_rgb('9c6a44')}
14
+ def rect(px,x0,y0,x1,y1,c):
15
+ for y in range(max(0,y0),min(H,y1+1)):
16
+ for x in range(max(0,x0),min(W,x1+1)):px[(x,y)]=c
17
+ def ytop(x):
18
+ dx=x-cx;return (cy-math.sqrt(R_OUT**2-dx*dx)) if abs(dx)<=R_OUT else None
19
+
20
+ def draw_eyes(px,style,SK):
21
+ """Bigger eyes, varied. Left cols cx-5..-3, right cx+3..+5."""
22
+ def one(side,lash=False,shut=False,small=False,wide=False):
23
+ s=side
24
+ L=[cx+ s*3,cx+s*4,cx+s*5]
25
+ if shut:
26
+ for x in L:px[(x,cy-1)]=OUT
27
+ if lash:px[(cx+s*6,cy-2)]=OUT
28
+ return
29
+ for x in L: # white block 3x2
30
+ px[(x,cy-2)]=WHT;px[(x,cy-1)]=WHT
31
+ pupx=cx+s*4
32
+ if wide:
33
+ px[(pupx,cy-1)]=EYE # small pupil, lots of white
34
+ elif small:
35
+ px[(pupx,cy-1)]=EYE
36
+ else:
37
+ px[(pupx,cy-1)]=EYE;px[(pupx,cy-2)]=EYE # big pupil
38
+ px[(cx+s*3 if s<0 else cx+s*5,cy-2)]=WHT # keep a white glint corner
39
+ if lash:
40
+ px[(cx+s*6,cy-2)]=OUT;px[(cx+s*5,cy-3)]=OUT;px[(cx+s*3,cy-2)]=OUT
41
+ if style=="wink":
42
+ one(-1);one(1,shut=True)
43
+ elif style=="lash":
44
+ one(-1,lash=True);one(1,lash=True)
45
+ elif style=="wide":
46
+ one(-1,wide=True);one(1,wide=True)
47
+ elif style=="small":
48
+ one(-1,small=True);one(1,small=True)
49
+ elif style=="happy":
50
+ for s in(-1,1): px[(cx+s*5,cy-1)]=OUT;px[(cx+s*4,cy-2)]=OUT;px[(cx+s*3,cy-1)]=OUT
51
+ else: # round
52
+ one(-1);one(1)
53
+
54
+ def hair_crown(px,HA,HAs,HAh,style):
55
+ def up(dx):
56
+ a=abs(dx)
57
+ return {"buzz":0,"short":1,"slick":1,"spiky":(3 if dx%2==0 else 1),
58
+ "longstraight":1,"longwavy":1,"ponytail":1,"longbob":1}.get(style,1)
59
+ def down(dx):
60
+ return {"buzz":1,"short":2,"slick":(3 if dx>0 else 1)}.get(style,2)
61
+ for x in range(cx-11,cx+12):
62
+ yt=ytop(x)
63
+ if yt is None:continue
64
+ base=round(yt)
65
+ if base>cy-2:continue
66
+ u=up(x-cx)
67
+ topy=base-u;boty=min(base+down(x-cx),cy-3)
68
+ for y in range(topy,boty+1):
69
+ if 0<=y<H:px[(x,y)]=HA
70
+ if 0<=topy<H:px[(x,topy)]=HAh
71
+ if 0<=boty<H and px.get((x,boty))==HA:px[(x,boty)]=HAs
72
+
73
+ def hair_sides(px,HA,HAs,style):
74
+ # women's long hair: side panels of different length/shape
75
+ for x in range(cx-11,cx+12):
76
+ dx=x-cx;a=abs(dx)
77
+ if a<6 or a>10:continue
78
+ yt=ytop(x)
79
+ if yt is None:continue
80
+ top=round(yt)
81
+ if style=="longstraight":L=cy+7
82
+ elif style=="longwavy":L=cy+6+(1 if (x%2==0) else -1)
83
+ elif style=="longbob":L=cy+2
84
+ elif style=="ponytail":L=cy
85
+ else:L=cy-1
86
+ for y in range(top,min(H,L+1)):px[(x,y)]=HA
87
+ if L<H:px[(x,min(H-1,L))]=HAs
88
+ if style=="ponytail":
89
+ for yy in range(cy-4,cy+5): # tail on the right
90
+ for xx in range(cx+9,cx+12):
91
+ if abs((yy-cy))<=4-(xx-(cx+9)):px[(xx,yy)]=HA
92
+ px[(cx+9,cy+4)]=HAs
93
+ px[(cx+8,cy-5)]=HA # tie base
94
+
95
+ def face(accent,hair,skin="mid",*,gender="m",hairstyle="short",eyes="round",
96
+ mouth="smile",prop=None,propcol="#caa472",hoodie=False,band=None,mask=None,specs=False):
97
+ AC=_rgb(accent);ACd=shade(AC,-30);SK=SKINS[skin];SKs=shade(SK,-20)
98
+ HA=_rgb(hair);HAs=shade(HA,-28);HAh=shade(HA,22)
99
+ px={}
100
+ for y in range(H):
101
+ for x in range(W):
102
+ d=math.hypot(x-cx,y-cy)
103
+ if d<=R_OUT:px[(x,y)]=(AC if d<R_OUT-0.9 else ACd) if d>=R_IN else SK
104
+ for y in range(0,cy-3):
105
+ for x in range(cx-2,cx+3):
106
+ if px.get((x,y)) in(AC,ACd):del px[(x,y)]
107
+ for x in range(cx-5,cx+6):
108
+ if px.get((x,cy+5))==SK:px[(x,cy+5)]=SKs
109
+ if gender=="f":hair_sides(px,HA,HAs,hairstyle) # draw long hair behind/around first
110
+ if hairstyle!="bald":hair_crown(px,HA,HAs,HAh,hairstyle)
111
+ draw_eyes(px,eyes,SK)
112
+ px[(cx,cy+2)]=SKs # nose
113
+ if mouth=="smile":
114
+ px[(cx-2,cy+4)]=OUT;px[(cx-1,cy+5)]=OUT;px[(cx,cy+5)]=OUT;px[(cx+1,cy+5)]=OUT;px[(cx+2,cy+4)]=OUT
115
+ elif mouth=="flat":rect(px,cx-2,cy+4,cx+1,cy+4,OUT)
116
+ elif mouth=="smirk":
117
+ px[(cx-1,cy+5)]=OUT;px[(cx,cy+5)]=OUT;px[(cx+1,cy+4)]=OUT;px[(cx+2,cy+3)]=OUT
118
+ elif mouth=="open":rect(px,cx-2,cy+4,cx+1,cy+5,OUT);px[(cx-1,cy+5)]=_rgb('7a1a12')
119
+ elif mouth=="lips":rect(px,cx-2,cy+4,cx+1,cy+4,_rgb('c0392b')) # lipstick
120
+ # props
121
+ if prop=="cigar":
122
+ PC=_rgb(propcol);rect(px,cx+2,cy+4,cx+8,cy+4,PC);px[(cx+2,cy+4)]=shade(PC,-25)
123
+ px[(cx+8,cy+4)]=_rgb('ff4422');px[(cx+9,cy+4)]=_rgb('ffb08a')
124
+ px[(cx+9,cy+2)]=_rgb('8a8a9a');px[(cx+10,cy+1)]=_rgb('6a6a7a')
125
+ elif prop=="shades":
126
+ rect(px,cx-6,cy-2,cx-2,cy,OUT);rect(px,cx+2,cy-2,cx+6,cy,OUT)
127
+ rect(px,cx-1,cy-2,cx+1,cy-2,OUT);px[(cx-7,cy-2)]=OUT;px[(cx+7,cy-2)]=OUT
128
+ px[(cx-5,cy-1)]=_rgb('3a3a4a');px[(cx+3,cy-1)]=_rgb('3a3a4a')
129
+ elif prop=="glasses":
130
+ for x in range(cx-6,cx-1):px[(x,cy-2)]=OUT;px[(x,cy)]=OUT
131
+ px[(cx-6,cy-1)]=OUT;px[(cx-2,cy-1)]=OUT
132
+ for x in range(cx+2,cx+7):px[(x,cy-2)]=OUT;px[(x,cy)]=OUT
133
+ px[(cx+1,cy-1)]=OUT;px[(cx+6,cy-1)]=OUT;px[(cx,cy-1)]=OUT
134
+ elif prop=="monocle":
135
+ for x in range(cx+2,cx+7):px[(x,cy-2)]=OUT;px[(x,cy)]=OUT
136
+ px[(cx+1,cy-1)]=OUT;px[(cx+6,cy-1)]=OUT
137
+ px[(cx+4,cy+1)]=_rgb('d4af37');px[(cx+4,cy+2)]=_rgb('d4af37')
138
+ elif prop=="pen":
139
+ PC=_rgb(propcol)
140
+ for i in range(5):
141
+ x=cx+8+i;y=cy-2-i
142
+ if 0<=x<W and 0<=y<H:px[(x,y)]=PC
143
+ px[(cx+8,cy-2)]=_rgb('2a2a34');px[(cx+12,cy-6)]=shade(PC,30)
144
+ elif prop=="headset":
145
+ rect(px,cx-12,cy-6,cx-12,cy+1,OUT);px[(cx-12,cy+2)]=OUT
146
+ rect(px,cx-13,cy+1,cx-12,cy+3,EYE);rect(px,cx-12,cy+3,cx-6,cy+4,OUT);px[(cx-6,cy+4)]=_rgb('ff4422')
147
+ elif prop=="earbuds":
148
+ px[(cx-9,cy)]=WHT;px[(cx-9,cy+1)]=WHT;px[(cx+9,cy)]=WHT;px[(cx+9,cy+1)]=WHT
149
+ elif prop=="bandana":
150
+ BC=_rgb(propcol)
151
+ for x in range(cx-11,cx+12):
152
+ yt=ytop(x)
153
+ if yt is None:continue
154
+ base=round(yt)
155
+ for y in range(base-1,base+3):
156
+ if 0<=y<H and y<=cy-3 and px.get((x,y)) not in(None,SK,SKs):px[(x,y)]=BC
157
+ px[(cx-10,cy-2)]=BC;px[(cx-11,cy-1)]=BC;px[(cx-11,cy)]=shade(BC,-22)
158
+ for x in range(cx-6,cx+7,3):px[(x,cy-5)]=shade(BC,-22)
159
+ # ---- combinable wearables (Founder) ----
160
+ if hoodie:
161
+ HC=_rgb('161b22')
162
+ for y in range(cy-2,H):
163
+ for x in range(W):
164
+ d=math.hypot(x-cx,y-cy)
165
+ if R_OUT<d<=R_OUT+2.4 and px.get((x,y)) is None: px[(x,y)]=HC
166
+ if band:
167
+ BC=_rgb(band); BCd=shade(BC,-24)
168
+ for y in range(0,cy-6): # hair crown above the (raised) band
169
+ for x in range(cx-9,cx+10):
170
+ if px.get((x,y))==SK: px[(x,y)]=HA
171
+ for y in (cy-6,cy-5,cy-4): # headband, raised a touch
172
+ for x in range(cx-10,cx+11):
173
+ yt=ytop(x)
174
+ if yt is None: continue
175
+ if y>=round(yt)-1 and px.get((x,y)) is not None: px[(x,y)]=BC
176
+ for strand in (0,1): # two tails, both streaming up-right (wind-blown)
177
+ for k in range(6):
178
+ xx=cx+8+k; yy=(cy-2-strand*2)-(k*2)//3
179
+ if 0<=xx<W and 0<=yy<H: px[(xx,yy)]=BC if k<3 else BCd
180
+ if mask:
181
+ MC=_rgb(mask);MCs=shade(MC,34);GL=_rgb('39d353')
182
+ for y in range(cy+1,cy+10): # dark mask over nose+mouth+chin (follows the face)
183
+ for x in range(cx-8,cx+9):
184
+ if px.get((x,y)) in(SK,SKs): px[(x,y)]=MC
185
+ for x in range(cx-5,cx+6):
186
+ if px.get((x,cy+1))==MC: px[(x,cy+1)]=MCs # ridge over the nose
187
+ for y in range(cy+2,cy+8):
188
+ if px.get((cx,y))==MC: px[(cx,y)]=MCs # centre seam
189
+ for k in range(3): px[(cx+3,cy+4+k)]=GL;px[(cx+4,cy+4+k)]=GL # green vent stripes
190
+ if specs:
191
+ LENS=_rgb('1f8a3a')
192
+ rect(px,cx-6,cy-2,cx-2,cy,OUT); rect(px,cx-5,cy-2,cx-3,cy-1,LENS); px[(cx-4,cy-1)]=_rgb('0c1a10')
193
+ rect(px,cx+2,cy-2,cx+6,cy,OUT); rect(px,cx+3,cy-2,cx+5,cy-1,LENS); px[(cx+4,cy-1)]=_rgb('0c1a10')
194
+ px[(cx-1,cy-1)]=OUT;px[(cx,cy-1)]=OUT;px[(cx+1,cy-1)]=OUT # bridge
195
+ return px
196
+
197
+ FOUNDER=("FOUNDER","#2fbf52","m",dict(hair="#141419",skin="light",hairstyle="short",eyes="happy",mouth="flat",band="#e23b3b",mask="#12201a",hoodie=True,specs=True))
198
+
199
+ def render(px,scale,bg=None):
200
+ img=Image.new("RGBA",(W*scale,H*scale),(bg or(0,0,0,0)));d=ImageDraw.Draw(img)
201
+ for (x,y),c in px.items():
202
+ if c is not None:d.rectangle([x*scale,y*scale,(x+1)*scale-1,(y+1)*scale-1],fill=c+(255,))
203
+ return img
204
+
205
+ CAST=[
206
+ ("CEO","#f59e0b","m",dict(hair="#2a2018",skin="mid", hairstyle="slick",eyes="round",mouth="smirk",prop="cigar")),
207
+ ("CPO","#8b5cf6","f",dict(hair="#3a2418",skin="tan", hairstyle="longwavy",eyes="lash",mouth="smile",prop="pen",propcol="#f2c75a")),
208
+ ("CMO","#ec4899","f",dict(hair="#4a2a18",skin="light",hairstyle="longstraight",eyes="lash",mouth="lips",prop="shades")),
209
+ ("CRO","#84cc16","m",dict(hair="#1c1c22",skin="mid", hairstyle="spiky",eyes="wide",mouth="open",prop="headset")),
210
+ ("CTO","#3b82f6","m",dict(hair="#1c1c22",skin="tan", hairstyle="buzz",eyes="round",mouth="flat",prop="glasses")),
211
+ ("COO","#06b6d4","f",dict(hair="#15151c",skin="deep", hairstyle="ponytail",eyes="lash",mouth="smile",prop="earbuds")),
212
+ ("CFO","#2dd4bf","f",dict(hair="#6a4a2a",skin="light",hairstyle="longbob",eyes="lash",mouth="flat",prop="monocle")),
213
+ ("CSO","#6366f1","m",dict(hair="#1c1c22",skin="mid", hairstyle="short",eyes="wide",mouth="flat",prop="bandana",propcol="#ef4444")),
214
+ ]
215
+ if __name__=="__main__":
216
+ scale=10;gap=14;lblh=22;cols=4;rows=2;cw=W*scale;ch=H*scale
217
+ sheet=Image.new("RGBA",(cols*cw+(cols+1)*gap, rows*(ch+lblh)+gap),(13,13,20,255));d=ImageDraw.Draw(sheet)
218
+ for i,(nm,acc,g,p) in enumerate(CAST):
219
+ im=render(face(acc,gender=g,**p),scale)
220
+ x=gap+(i%cols)*(cw+gap);y=lblh+(i//cols)*(ch+lblh)
221
+ sheet.paste(im,(x,y),im);d.text((x+4,y-16),f"{nm} {'F' if g=='f' else 'M'} {p['hairstyle']}/{p['prop']}",fill=(220,220,235))
222
+ sheet.save("/tmp/face_preview.png");print("wrote",sheet.size)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topy-ai/maggie",
3
- "version": "0.7.61",
3
+ "version": "0.8.1",
4
4
  "description": "Install and manage Maggie Skills for AI coding agents",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -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.