claude-plugin-wordpress-manager 2.12.2 → 2.14.0
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/.claude-plugin/plugin.json +8 -3
- package/CHANGELOG.md +94 -2
- package/agents/wp-accessibility-auditor.md +1 -1
- package/agents/wp-content-strategist.md +2 -2
- package/agents/wp-deployment-engineer.md +1 -1
- package/agents/wp-distribution-manager.md +1 -1
- package/agents/wp-monitoring-agent.md +1 -1
- package/agents/wp-performance-optimizer.md +1 -1
- package/agents/wp-security-auditor.md +1 -1
- package/agents/wp-site-manager.md +3 -3
- package/commands/wp-setup.md +2 -2
- package/docs/GUIDE.md +260 -21
- package/docs/VALIDATION.md +341 -0
- package/docs/guides/wp-ecommerce.md +4 -4
- package/docs/plans/2026-03-01-tier3-wcop-implementation.md +1 -1
- package/docs/plans/2026-03-01-tier4-5-implementation.md +1 -1
- package/docs/plans/2026-03-02-content-framework-architecture.md +612 -0
- package/docs/plans/2026-03-02-content-framework-strategic-reflections.md +228 -0
- package/docs/plans/2026-03-02-content-intelligence-phase2.md +560 -0
- package/docs/plans/2026-03-02-content-pipeline-phase1.md +456 -0
- package/docs/plans/2026-03-02-dashboard-kanban-design.md +761 -0
- package/docs/plans/2026-03-02-dashboard-kanban-implementation.md +598 -0
- package/docs/plans/2026-03-02-dashboard-strategy.md +363 -0
- package/docs/plans/2026-03-02-editorial-calendar-phase3.md +490 -0
- package/docs/validation/.gitkeep +0 -0
- package/docs/validation/dashboard.html +286 -0
- package/docs/validation/results.json +1705 -0
- package/package.json +16 -3
- package/scripts/context-scanner.mjs +446 -0
- package/scripts/dashboard-renderer.mjs +553 -0
- package/scripts/run-validation.mjs +1132 -0
- package/servers/wp-rest-bridge/build/server.js +17 -6
- package/servers/wp-rest-bridge/build/tools/index.js +0 -9
- package/servers/wp-rest-bridge/build/tools/plugin-repository.js +23 -31
- package/servers/wp-rest-bridge/build/tools/schema.js +10 -2
- package/servers/wp-rest-bridge/build/tools/unified-content.js +10 -2
- package/servers/wp-rest-bridge/build/wordpress.d.ts +0 -3
- package/servers/wp-rest-bridge/build/wordpress.js +16 -98
- package/servers/wp-rest-bridge/package.json +1 -0
- package/skills/wp-analytics/SKILL.md +153 -0
- package/skills/wp-analytics/references/signals-feed-schema.md +417 -0
- package/skills/wp-content/references/content-templates.md +1 -1
- package/skills/wp-content/references/seo-optimization.md +8 -8
- package/skills/wp-content-attribution/references/roi-calculation.md +1 -1
- package/skills/wp-content-attribution/references/utm-tracking-setup.md +5 -5
- package/skills/wp-content-generation/references/generation-workflow.md +2 -2
- package/skills/wp-content-pipeline/SKILL.md +461 -0
- package/skills/wp-content-pipeline/references/content-brief-schema.md +377 -0
- package/skills/wp-content-pipeline/references/site-config-schema.md +431 -0
- package/skills/wp-content-repurposing/references/auto-transform-pipeline.md +1 -1
- package/skills/wp-content-repurposing/references/email-newsletter.md +1 -1
- package/skills/wp-content-repurposing/references/platform-specs.md +2 -2
- package/skills/wp-content-repurposing/references/transform-templates.md +27 -27
- package/skills/wp-dashboard/SKILL.md +121 -0
- package/skills/wp-deploy/references/ssh-deploy.md +2 -2
- package/skills/wp-editorial-planner/SKILL.md +262 -0
- package/skills/wp-editorial-planner/references/editorial-schema.md +268 -0
- package/skills/wp-multilang-network/references/content-sync.md +3 -3
- package/skills/wp-multilang-network/references/network-architecture.md +1 -1
- package/skills/wp-multilang-network/references/seo-international.md +7 -7
- package/skills/wp-structured-data/references/schema-types.md +4 -4
- package/skills/wp-webhooks/references/payload-formats.md +3 -3
|
@@ -0,0 +1,761 @@
|
|
|
1
|
+
# Editorial Kanban Dashboard — Design Document
|
|
2
|
+
|
|
3
|
+
**Data**: 2026-03-02
|
|
4
|
+
**Versione**: 1.0.0
|
|
5
|
+
**Stato**: In design
|
|
6
|
+
**Parent**: [Dashboard Strategy](2026-03-02-dashboard-strategy.md)
|
|
7
|
+
**Deliverable**: Skill `wp-dashboard` + script `dashboard-renderer.mjs` + modulo condiviso `context-scanner.mjs`
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 1. Obiettivo
|
|
12
|
+
|
|
13
|
+
Generare un file HTML statico self-contained che visualizza lo stato editoriale di un sito WordPress come Kanban board. L'operatore invoca la skill `wp-dashboard`, lo script legge `.content-state/`, produce l'HTML e lo apre nel browser.
|
|
14
|
+
|
|
15
|
+
**Non-obiettivi**: interattività (drag & drop), aggiornamento live, persistenza stato proprio, dipendenze esterne.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 2. Layout Visivo
|
|
20
|
+
|
|
21
|
+
### 2.1 Struttura Generale
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
┌──────────────────────────────────────────────────────────────────────────────┐
|
|
25
|
+
│ HEADER: Editorial Dashboard — mysite.example.com — Marzo 2026 │
|
|
26
|
+
│ Generato: 2026-03-02 14:30 | Posts: 2/8 pubblicati | Pipeline: 1 ready │
|
|
27
|
+
├──────────────────────────────────────────────────────────────────────────────┤
|
|
28
|
+
│ │
|
|
29
|
+
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
|
30
|
+
│ │ PLANNED │ │ DRAFT │ │ READY │ │SCHEDULED │ │PUBLISHED │ │
|
|
31
|
+
│ │ (3) │ │ (1) │ │ (1) │ │ (0) │ │ (2) │ │
|
|
32
|
+
│ ├──────────┤ ├──────────┤ ├──────────┤ ├──────────┤ ├──────────┤ │
|
|
33
|
+
│ │ │ │ │ │ │ │ │ │ │ │
|
|
34
|
+
│ │ ┌──────┐ │ │ ┌──────┐ │ │ ┌──────┐ │ │ │ │ ┌──────┐ │ │
|
|
35
|
+
│ │ │ CARD │ │ │ │ CARD │ │ │ │ CARD │ │ │ (vuoto) │ │ │ CARD │ │ │
|
|
36
|
+
│ │ └──────┘ │ │ └──────┘ │ │ └──────┘ │ │ │ │ └──────┘ │ │
|
|
37
|
+
│ │ ┌──────┐ │ │ │ │ │ │ │ │ ┌──────┐ │ │
|
|
38
|
+
│ │ │ CARD │ │ │ │ │ │ │ │ │ │ CARD │ │ │
|
|
39
|
+
│ │ └──────┘ │ │ │ │ │ │ │ │ └──────┘ │ │
|
|
40
|
+
│ │ ┌──────┐ │ │ │ │ │ │ │ │ │ │
|
|
41
|
+
│ │ │ CARD │ │ │ │ │ │ │ │ │ │ │
|
|
42
|
+
│ │ └──────┘ │ │ │ │ │ │ │ │ │ │
|
|
43
|
+
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
|
|
44
|
+
│ │
|
|
45
|
+
├──────────────────────────────────────────────────────────────────────────────┤
|
|
46
|
+
│ SIGNALS STRIP (se anomalie presenti) │
|
|
47
|
+
│ ▲ +120% "acqua premium" impressions | ▲ +85% LinkedIn referrals │
|
|
48
|
+
├──────────────────────────────────────────────────────────────────────────────┤
|
|
49
|
+
│ FOOTER: WordPress Manager v2.14.0 | wp-dashboard skill │
|
|
50
|
+
└──────────────────────────────────────────────────────────────────────────────┘
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 2.2 Anatomia della Card
|
|
54
|
+
|
|
55
|
+
Ogni card rappresenta un contenuto nel ciclo editoriale.
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
┌─────────────────────────────┐
|
|
59
|
+
│ Mar 18 📝 │ ← data + icona tipo (📝 post, 📄 page)
|
|
60
|
+
│ │
|
|
61
|
+
│ Acqua premium: perché le │ ← titolo (troncato a 60 char)
|
|
62
|
+
│ ricerche sono esplose... │
|
|
63
|
+
│ │
|
|
64
|
+
│ BRF-2026-005 │ ← brief ID (se esiste)
|
|
65
|
+
│ #wellness #premium-water │ ← categorie/tag principali
|
|
66
|
+
│ │
|
|
67
|
+
│ 🔗 in 📧 nl │ ← icone canali distribuzione
|
|
68
|
+
└─────────────────────────────┘
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Varianti card per stato**:
|
|
72
|
+
|
|
73
|
+
| Stato | Colore bordo sinistro | Note |
|
|
74
|
+
|-------|----------------------|------|
|
|
75
|
+
| `planned` | grigio `#94a3b8` | Titolo può essere `[da assegnare]` |
|
|
76
|
+
| `draft` | giallo `#eab308` | Ha Brief ID |
|
|
77
|
+
| `ready` | blu `#3b82f6` | Pronto per scheduling |
|
|
78
|
+
| `scheduled` | viola `#8b5cf6` | Ha Post ID, mostra data scheduling |
|
|
79
|
+
| `published` | verde `#22c55e` | Ha Post ID + URL |
|
|
80
|
+
|
|
81
|
+
**Card "da assegnare"** (planned, senza titolo):
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
┌─────────────────────────────┐
|
|
85
|
+
│ Mar 20 📝 │
|
|
86
|
+
│ │
|
|
87
|
+
│ [da assegnare] │ ← testo grigio, italic
|
|
88
|
+
│ │
|
|
89
|
+
│ — │ ← nessun brief ID
|
|
90
|
+
└─────────────────────────────┘
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 2.3 Header — Metriche Aggregate
|
|
94
|
+
|
|
95
|
+
Il header contiene una riga di metriche riassuntive:
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
Editorial Dashboard — mysite.example.com — Marzo 2026
|
|
99
|
+
Generato: 2026-03-02 14:30 | Posts: 2/8 pubblicati | Pipeline: 1 ready, 1 draft | Next: Mar 11
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
| Metrica | Calcolo |
|
|
103
|
+
|---------|---------|
|
|
104
|
+
| Posts X/Y pubblicati | `count(status=published)` / `goals.posts_target` |
|
|
105
|
+
| Pipeline: N ready, N draft | count per status nei brief attivi |
|
|
106
|
+
| Next: data | prima data con `status` non `published` e non `planned-vuoto` |
|
|
107
|
+
|
|
108
|
+
### 2.4 Signals Strip
|
|
109
|
+
|
|
110
|
+
Striscia opzionale sotto il Kanban, presente solo se `signals-feed.md` contiene anomalie:
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
┌──────────────────────────────────────────────────────────────────────────────┐
|
|
114
|
+
│ ⚡ Signals ▲ +120% "acqua premium" impressions → content cluster │
|
|
115
|
+
│ ▲ +85% LinkedIn referrals → scale posting frequency │
|
|
116
|
+
│ ▲ +47% /premium-water-benefici pageviews → investigate │
|
|
117
|
+
└──────────────────────────────────────────────────────────────────────────────┘
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Ogni anomalia mostra: direzione (`▲`/`▼`), delta percentuale, entità, azione suggerita.
|
|
121
|
+
|
|
122
|
+
### 2.5 Palette Colori
|
|
123
|
+
|
|
124
|
+
Coerenza con il brand system AcmeBrand/MySite ma neutrale (il dashboard è uno strumento operativo, non un artefatto brand):
|
|
125
|
+
|
|
126
|
+
```css
|
|
127
|
+
/* Background & Structure */
|
|
128
|
+
--bg-page: #f8fafc; /* slate-50 */
|
|
129
|
+
--bg-column: #f1f5f9; /* slate-100 */
|
|
130
|
+
--bg-card: #ffffff;
|
|
131
|
+
--border: #e2e8f0; /* slate-200 */
|
|
132
|
+
--text-primary: #1e293b; /* slate-800 */
|
|
133
|
+
--text-secondary:#64748b; /* slate-500 */
|
|
134
|
+
--text-muted: #94a3b8; /* slate-400 */
|
|
135
|
+
|
|
136
|
+
/* Status Colors */
|
|
137
|
+
--status-planned: #94a3b8; /* slate-400 */
|
|
138
|
+
--status-draft: #eab308; /* yellow-500 */
|
|
139
|
+
--status-ready: #3b82f6; /* blue-500 */
|
|
140
|
+
--status-scheduled: #8b5cf6; /* violet-500 */
|
|
141
|
+
--status-published: #22c55e; /* green-500 */
|
|
142
|
+
|
|
143
|
+
/* Signals */
|
|
144
|
+
--signal-up: #22c55e; /* green */
|
|
145
|
+
--signal-down: #ef4444; /* red */
|
|
146
|
+
|
|
147
|
+
/* Typography */
|
|
148
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## 3. Data Flow: SCAN → AGGREGATE → RENDER
|
|
154
|
+
|
|
155
|
+
### 3.1 SCAN — Lettura File
|
|
156
|
+
|
|
157
|
+
Lo scanner legge i file `.content-state/` e produce un oggetto JavaScript strutturato.
|
|
158
|
+
|
|
159
|
+
**Input files**:
|
|
160
|
+
|
|
161
|
+
| File | Parsing | Output |
|
|
162
|
+
|------|---------|--------|
|
|
163
|
+
| `{site_id}.config.md` | Frontmatter YAML | `{ site_id, site_url, brand, defaults, channels, seo, cadence }` |
|
|
164
|
+
| `YYYY-MM-editorial.state.md` | Frontmatter YAML + tabelle Markdown | `{ calendar_id, period, goals, entries[] }` |
|
|
165
|
+
| `pipeline-active/*.brief.md` | Frontmatter YAML per ogni file | `{ briefs_active[] }` |
|
|
166
|
+
| `pipeline-archive/*.brief.md` | Frontmatter YAML per ogni file | `{ briefs_archived[] }` |
|
|
167
|
+
| `signals-feed.md` | Frontmatter YAML + tabella anomalie | `{ signals[] }` |
|
|
168
|
+
|
|
169
|
+
**Parsing delle tabelle Markdown**:
|
|
170
|
+
|
|
171
|
+
Il calendario editoriale usa tabelle con colonne fisse:
|
|
172
|
+
|
|
173
|
+
```markdown
|
|
174
|
+
| Data | Titolo | Tipo | Status | Brief ID | Post ID | Canali |
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Lo scanner converte ogni riga in un oggetto:
|
|
178
|
+
|
|
179
|
+
```javascript
|
|
180
|
+
{
|
|
181
|
+
date: "2026-03-18",
|
|
182
|
+
title: "Acqua premium: perché le ricerche sono esplose del 120%",
|
|
183
|
+
type: "post",
|
|
184
|
+
status: "ready",
|
|
185
|
+
briefId: "BRF-2026-005",
|
|
186
|
+
postId: null,
|
|
187
|
+
channels: ["linkedin", "newsletter"]
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Regole di parsing**:
|
|
192
|
+
- Le righe con titolo `[da assegnare]` hanno `title: null`
|
|
193
|
+
- Post ID `—` è convertito a `null`
|
|
194
|
+
- Canali sono splittati per `, ` (virgola + spazio)
|
|
195
|
+
- Le date sono nel formato `Mon DD` (es. `Mar 18`) e vengono risolte con anno/mese dal `period` del calendar
|
|
196
|
+
|
|
197
|
+
**Risultato SCAN**:
|
|
198
|
+
|
|
199
|
+
```javascript
|
|
200
|
+
{
|
|
201
|
+
site: {
|
|
202
|
+
id: "mysite",
|
|
203
|
+
url: "https://mysite.example.com",
|
|
204
|
+
brand: { tone: "...", language: "it", ... },
|
|
205
|
+
cadence: { posts_per_week: 3, preferred_days: [...], publish_time: "09:00" },
|
|
206
|
+
channels: { linkedin: { enabled: true, ... }, ... }
|
|
207
|
+
},
|
|
208
|
+
calendar: {
|
|
209
|
+
id: "CAL-2026-03",
|
|
210
|
+
period: "2026-03-01..2026-03-31",
|
|
211
|
+
goals: { posts_target: 8, posts_published: 2, ... },
|
|
212
|
+
entries: [
|
|
213
|
+
{ date: "2026-03-04", title: "Acqua premium: 5 benefici...", type: "post", status: "published", briefId: "BRF-2026-001", postId: 1234, channels: ["linkedin", "newsletter"] },
|
|
214
|
+
{ date: "2026-03-06", title: "Come il frutto mediterraneo diventa bevanda", type: "post", status: "published", briefId: "BRF-2026-002", postId: 1235, channels: ["linkedin", "twitter"] },
|
|
215
|
+
{ date: "2026-03-11", title: "Zero calorie, tutto gusto: la scienza", type: "post", status: "ready", briefId: "BRF-2026-003", postId: null, channels: ["linkedin", "newsletter"] },
|
|
216
|
+
{ date: "2026-03-13", title: "Mediterraneo e sostenibilità: la filiera", type: "post", status: "draft", briefId: "BRF-2026-004", postId: null, channels: ["linkedin"] },
|
|
217
|
+
{ date: "2026-03-18", title: "Acqua premium: perché le ricerche...", type: "post", status: "ready", briefId: "BRF-2026-005", postId: null, channels: ["linkedin", "newsletter"] },
|
|
218
|
+
{ date: "2026-03-20", title: null, type: "post", status: "planned", briefId: null, postId: null, channels: [] },
|
|
219
|
+
{ date: "2026-03-25", title: null, type: "post", status: "planned", briefId: null, postId: null, channels: [] },
|
|
220
|
+
{ date: "2026-03-27", title: null, type: "post", status: "planned", briefId: null, postId: null, channels: [] }
|
|
221
|
+
]
|
|
222
|
+
},
|
|
223
|
+
briefs: {
|
|
224
|
+
active: [
|
|
225
|
+
{ briefId: "BRF-2026-005", status: "ready", title: "Acqua premium: perché...", siteId: "mysite", channels: ["linkedin", "newsletter"], signalRef: "FEED-mysite-2026-02 → Keyword:acqua premium +120%" }
|
|
226
|
+
],
|
|
227
|
+
archived: [
|
|
228
|
+
{ briefId: "BRF-2026-001", status: "published", title: "I Benefici dell'Acqua Premium...", postId: 2456, postUrl: "https://mysite.example.com/benefici-..." }
|
|
229
|
+
]
|
|
230
|
+
},
|
|
231
|
+
signals: {
|
|
232
|
+
feedId: "FEED-mysite-2026-02",
|
|
233
|
+
period: "2026-02-01..2026-02-28",
|
|
234
|
+
anomalies: [
|
|
235
|
+
{ entity: "Keyword:acqua premium", metric: "search_impressions", delta: "+120%", pattern: "Search Intent Shift", action: "Investigate: content cluster opportunity" },
|
|
236
|
+
{ entity: "Source:linkedin", metric: "referral_sessions", delta: "+85%", pattern: "Early-Adopter Surge", action: "Scale: increase posting frequency on linkedin" },
|
|
237
|
+
{ entity: "Page:/premium-water-benefici", metric: "pageviews", delta: "+47%", pattern: "Unclassified anomaly", action: "Review: investigate cause of +47% change in pageviews" }
|
|
238
|
+
]
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### 3.2 AGGREGATE — Metriche Derivate
|
|
244
|
+
|
|
245
|
+
Dall'output SCAN, calcola metriche aggregate per il rendering:
|
|
246
|
+
|
|
247
|
+
```javascript
|
|
248
|
+
{
|
|
249
|
+
// Progress metrics
|
|
250
|
+
postsPublished: 2,
|
|
251
|
+
postsTarget: 8,
|
|
252
|
+
progressPercent: 25, // 2/8 * 100
|
|
253
|
+
|
|
254
|
+
// Pipeline counts (per column)
|
|
255
|
+
columns: {
|
|
256
|
+
planned: 3, // entries con status=planned
|
|
257
|
+
draft: 1, // entries con status=draft
|
|
258
|
+
ready: 2, // entries con status=ready (incluso da brief attivi)
|
|
259
|
+
scheduled: 0, // entries con status=scheduled
|
|
260
|
+
published: 2 // entries con status=published
|
|
261
|
+
},
|
|
262
|
+
|
|
263
|
+
// Timeline
|
|
264
|
+
nextDeadline: {
|
|
265
|
+
date: "2026-03-11",
|
|
266
|
+
title: "Zero calorie, tutto gusto: la scienza",
|
|
267
|
+
status: "ready",
|
|
268
|
+
daysFromNow: 9
|
|
269
|
+
},
|
|
270
|
+
|
|
271
|
+
// Channel distribution
|
|
272
|
+
channelUsage: {
|
|
273
|
+
linkedin: 5, // entries che includono linkedin
|
|
274
|
+
newsletter: 3,
|
|
275
|
+
twitter: 1
|
|
276
|
+
},
|
|
277
|
+
|
|
278
|
+
// Signals summary
|
|
279
|
+
signalsCount: 3,
|
|
280
|
+
signalsHighest: { entity: "acqua premium", delta: "+120%", pattern: "Search Intent Shift" },
|
|
281
|
+
|
|
282
|
+
// Calendar fill rate
|
|
283
|
+
fillRate: 62.5, // 5/8 entries con titolo assegnato * 100
|
|
284
|
+
|
|
285
|
+
// Generation metadata
|
|
286
|
+
generatedAt: "2026-03-02T14:30:00+01:00",
|
|
287
|
+
generatorVersion: "1.0.0"
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### 3.3 RENDER — Generazione HTML
|
|
292
|
+
|
|
293
|
+
Il renderer prende `entries[]` (raggruppate per status) e `metrics`, produce un HTML self-contained.
|
|
294
|
+
|
|
295
|
+
**Template strategy**: template literal ES6. Nessun template engine esterno.
|
|
296
|
+
|
|
297
|
+
**Struttura HTML**:
|
|
298
|
+
|
|
299
|
+
```html
|
|
300
|
+
<!DOCTYPE html>
|
|
301
|
+
<html lang="it">
|
|
302
|
+
<head>
|
|
303
|
+
<meta charset="UTF-8">
|
|
304
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
305
|
+
<title>Editorial Dashboard — {site_id} — {month} {year}</title>
|
|
306
|
+
<style>
|
|
307
|
+
/* Tutti gli stili inline — vedi sezione 2.5 per la palette */
|
|
308
|
+
/* ~150 righe CSS: reset, layout grid, card, header, signals strip */
|
|
309
|
+
</style>
|
|
310
|
+
</head>
|
|
311
|
+
<body>
|
|
312
|
+
<header>
|
|
313
|
+
<!-- Metriche aggregate: titolo, data generazione, progress bar, pipeline counts -->
|
|
314
|
+
</header>
|
|
315
|
+
<main class="kanban">
|
|
316
|
+
<!-- 5 colonne: planned | draft | ready | scheduled | published -->
|
|
317
|
+
<!-- Ogni colonna contiene N card dalla entries[] filtrata per status -->
|
|
318
|
+
</main>
|
|
319
|
+
<section class="signals-strip">
|
|
320
|
+
<!-- Anomalie da signals-feed, se presenti -->
|
|
321
|
+
</section>
|
|
322
|
+
<footer>
|
|
323
|
+
<!-- Versione plugin, skill, timestamp -->
|
|
324
|
+
</footer>
|
|
325
|
+
</body>
|
|
326
|
+
</html>
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
**Dimensioni target**: < 30 KB per un mese con 8-12 entries e 3-5 anomalie.
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## 4. Architettura Script
|
|
334
|
+
|
|
335
|
+
### 4.1 Moduli
|
|
336
|
+
|
|
337
|
+
```
|
|
338
|
+
scripts/
|
|
339
|
+
├── context-scanner.mjs ← SCAN + AGGREGATE (modulo condiviso)
|
|
340
|
+
│ export: scanContentState(contentStatePath, siteId)
|
|
341
|
+
│ export: aggregateMetrics(rawData, viewType)
|
|
342
|
+
│ export: renderContextSnippet(metrics, sliceType) ← per Fase B (step 0)
|
|
343
|
+
│
|
|
344
|
+
└── dashboard-renderer.mjs ← RENDER HTML + open browser
|
|
345
|
+
import: { scanContentState, aggregateMetrics } from './context-scanner.mjs'
|
|
346
|
+
export: renderKanbanHTML(rawData, metrics)
|
|
347
|
+
main: scan → aggregate → render → write file → open browser
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### 4.2 `context-scanner.mjs` — API
|
|
351
|
+
|
|
352
|
+
```javascript
|
|
353
|
+
/**
|
|
354
|
+
* Scans .content-state/ directory for a specific site.
|
|
355
|
+
*
|
|
356
|
+
* @param {string} contentStatePath - Path to .content-state/ directory
|
|
357
|
+
* @param {string} siteId - Site identifier (e.g., "mysite")
|
|
358
|
+
* @returns {object} Raw data: { site, calendar, briefs, signals }
|
|
359
|
+
*/
|
|
360
|
+
export function scanContentState(contentStatePath, siteId) { ... }
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Computes aggregate metrics from raw scan data.
|
|
364
|
+
*
|
|
365
|
+
* @param {object} rawData - Output of scanContentState()
|
|
366
|
+
* @param {string} viewType - "kanban" | "signals" | "distribution" | "health"
|
|
367
|
+
* @returns {object} Aggregated metrics for the requested view
|
|
368
|
+
*/
|
|
369
|
+
export function aggregateMetrics(rawData, viewType) { ... }
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Renders a compact terminal snippet for step 0 context.
|
|
373
|
+
* (Fase B — implementazione differita)
|
|
374
|
+
*
|
|
375
|
+
* @param {object} metrics - Output of aggregateMetrics()
|
|
376
|
+
* @param {string} sliceType - "pipeline" | "calendar" | "signals"
|
|
377
|
+
* @returns {string} Formatted terminal text (3-5 lines)
|
|
378
|
+
*/
|
|
379
|
+
export function renderContextSnippet(metrics, sliceType) { ... }
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### 4.3 `dashboard-renderer.mjs` — CLI
|
|
383
|
+
|
|
384
|
+
```bash
|
|
385
|
+
# Uso:
|
|
386
|
+
node scripts/dashboard-renderer.mjs # sito da config, mese corrente
|
|
387
|
+
node scripts/dashboard-renderer.mjs --site=mysite # sito specifico
|
|
388
|
+
node scripts/dashboard-renderer.mjs --month=2026-04 # mese specifico
|
|
389
|
+
node scripts/dashboard-renderer.mjs --output=/tmp/dash.html # output specifico
|
|
390
|
+
node scripts/dashboard-renderer.mjs --no-open # non aprire browser
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
**Flusso main()**:
|
|
394
|
+
|
|
395
|
+
```
|
|
396
|
+
1. Parse CLI args (--site, --month, --output, --no-open)
|
|
397
|
+
2. Resolve contentStatePath da plugin root
|
|
398
|
+
3. Se --site non fornito:
|
|
399
|
+
a. Cerca tutti i *.config.md in .content-state/
|
|
400
|
+
b. Se uno solo → usa quello
|
|
401
|
+
c. Se multipli → errore con lista siti disponibili
|
|
402
|
+
4. Se --month non fornito → usa mese corrente
|
|
403
|
+
5. scanContentState(contentStatePath, siteId)
|
|
404
|
+
6. aggregateMetrics(rawData, "kanban")
|
|
405
|
+
7. renderKanbanHTML(rawData, metrics) → htmlString
|
|
406
|
+
8. Scrivi htmlString in file temporaneo (o --output)
|
|
407
|
+
Default: .content-state/.dashboard-{siteId}-{month}.html
|
|
408
|
+
9. Se non --no-open: exec("xdg-open {filepath}") su Linux
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### 4.4 YAML Frontmatter Parsing
|
|
412
|
+
|
|
413
|
+
Lo scanner parsa il frontmatter YAML manualmente (no dipendenze):
|
|
414
|
+
|
|
415
|
+
```javascript
|
|
416
|
+
function parseFrontmatter(content) {
|
|
417
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
418
|
+
if (!match) return { frontmatter: {}, body: content };
|
|
419
|
+
// Simple YAML parser for flat/nested structures
|
|
420
|
+
// Handles: strings, numbers, arrays (inline [a, b] and multi-line - a\n- b), nested objects
|
|
421
|
+
// Does NOT need: anchors, aliases, multi-document, complex types
|
|
422
|
+
}
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
**Alternativa**: usare il pacchetto `yaml` già presente come dipendenza transitiva del SDK MCP in `servers/wp-rest-bridge/node_modules/`. Importarlo via `createRequire`.
|
|
426
|
+
|
|
427
|
+
**Decisione**: usare `yaml` da node_modules se disponibile (più robusto), fallback a parser manuale semplice. Verificare disponibilità a implementazione.
|
|
428
|
+
|
|
429
|
+
### 4.5 Markdown Table Parsing
|
|
430
|
+
|
|
431
|
+
Le tabelle editoriali hanno formato fisso:
|
|
432
|
+
|
|
433
|
+
```markdown
|
|
434
|
+
| Data | Titolo | Tipo | Status | Brief ID | Post ID | Canali |
|
|
435
|
+
|------|--------|------|--------|----------|---------|--------|
|
|
436
|
+
| Mar 4 | Acqua premium: 5 benefici scientifici | post | published | BRF-2026-001 | 1234 | linkedin, newsletter |
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
Parser dedicato:
|
|
440
|
+
|
|
441
|
+
```javascript
|
|
442
|
+
function parseEditorialTable(markdownBody) {
|
|
443
|
+
// 1. Split body in sezioni per "## Settimana N"
|
|
444
|
+
// 2. Per ogni sezione, trova la tabella (righe che iniziano con |)
|
|
445
|
+
// 3. Skip header row (| Data | ...) e separator row (|------|...)
|
|
446
|
+
// 4. Per ogni data row: split per |, trim, map a oggetto
|
|
447
|
+
// 5. Gestisci valori speciali: "—" → null, "[da assegnare]" → null per title
|
|
448
|
+
// Return: array di entry objects
|
|
449
|
+
}
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
### 4.6 Dipendenze
|
|
453
|
+
|
|
454
|
+
| Dipendenza | Tipo | Note |
|
|
455
|
+
|------------|------|------|
|
|
456
|
+
| `node:fs/promises` | Built-in | Lettura file |
|
|
457
|
+
| `node:path` | Built-in | Path resolution |
|
|
458
|
+
| `node:child_process` | Built-in | `exec` per `xdg-open` |
|
|
459
|
+
| `node:url` | Built-in | `fileURLToPath` per ESM |
|
|
460
|
+
| `yaml` (opzionale) | From node_modules | Parsing YAML robusto, via `createRequire` |
|
|
461
|
+
|
|
462
|
+
**Zero nuove dipendenze npm da installare.**
|
|
463
|
+
|
|
464
|
+
---
|
|
465
|
+
|
|
466
|
+
## 5. Skill Definition
|
|
467
|
+
|
|
468
|
+
### 5.1 `skills/wp-dashboard/SKILL.md`
|
|
469
|
+
|
|
470
|
+
```yaml
|
|
471
|
+
---
|
|
472
|
+
name: wp-dashboard
|
|
473
|
+
description: This skill should be used when the user asks to "show dashboard",
|
|
474
|
+
"show editorial status", "open kanban", "visualize content state",
|
|
475
|
+
"show content overview", "mostra dashboard", "apri kanban", or wants
|
|
476
|
+
a visual overview of the editorial pipeline. Generates a self-contained
|
|
477
|
+
HTML Kanban board from .content-state/ files and opens it in the browser.
|
|
478
|
+
version: 1.0.0
|
|
479
|
+
---
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
**Workflow della skill**:
|
|
483
|
+
|
|
484
|
+
1. Determinare il sito target (da contesto conversazione o chiedere all'utente)
|
|
485
|
+
2. Determinare il mese (default: corrente)
|
|
486
|
+
3. Eseguire lo script:
|
|
487
|
+
```bash
|
|
488
|
+
node scripts/dashboard-renderer.mjs --site={siteId} --month={YYYY-MM}
|
|
489
|
+
```
|
|
490
|
+
4. Confermare all'utente che il dashboard è stato aperto nel browser
|
|
491
|
+
5. Se l'utente chiede modifiche al contenuto basandosi sul dashboard → suggerire la skill appropriata (`wp-content-pipeline`, `wp-editorial-planner`)
|
|
492
|
+
|
|
493
|
+
### 5.2 Trigger e Routing
|
|
494
|
+
|
|
495
|
+
La skill è invocata esplicitamente dall'utente. Non è un step automatico di altre skill (quello è Fase B — context snippet).
|
|
496
|
+
|
|
497
|
+
**Frasi trigger**:
|
|
498
|
+
- "mostra il dashboard" / "show dashboard"
|
|
499
|
+
- "apri il kanban" / "open kanban"
|
|
500
|
+
- "stato editoriale" / "editorial status"
|
|
501
|
+
- "panoramica contenuti" / "content overview"
|
|
502
|
+
- "dove siamo con i post?" / "where are we with posts?"
|
|
503
|
+
|
|
504
|
+
---
|
|
505
|
+
|
|
506
|
+
## 6. HTML Template — Specifiche di Dettaglio
|
|
507
|
+
|
|
508
|
+
### 6.1 CSS Layout
|
|
509
|
+
|
|
510
|
+
**Kanban grid**: CSS Grid con 5 colonne di larghezza uguale.
|
|
511
|
+
|
|
512
|
+
```css
|
|
513
|
+
.kanban {
|
|
514
|
+
display: grid;
|
|
515
|
+
grid-template-columns: repeat(5, 1fr);
|
|
516
|
+
gap: 16px;
|
|
517
|
+
padding: 24px;
|
|
518
|
+
min-height: 400px;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
.column {
|
|
522
|
+
background: var(--bg-column);
|
|
523
|
+
border-radius: 8px;
|
|
524
|
+
padding: 12px;
|
|
525
|
+
display: flex;
|
|
526
|
+
flex-direction: column;
|
|
527
|
+
gap: 8px;
|
|
528
|
+
}
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
**Card**: Box bianco con bordo sinistro colorato.
|
|
532
|
+
|
|
533
|
+
```css
|
|
534
|
+
.card {
|
|
535
|
+
background: var(--bg-card);
|
|
536
|
+
border-radius: 6px;
|
|
537
|
+
border-left: 4px solid var(--status-color);
|
|
538
|
+
padding: 12px;
|
|
539
|
+
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
540
|
+
}
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
**Responsive**: A viewport < 768px le colonne diventano verticali (stack).
|
|
544
|
+
|
|
545
|
+
```css
|
|
546
|
+
@media (max-width: 768px) {
|
|
547
|
+
.kanban {
|
|
548
|
+
grid-template-columns: 1fr;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
### 6.2 Header con Progress Bar
|
|
554
|
+
|
|
555
|
+
```html
|
|
556
|
+
<header>
|
|
557
|
+
<h1>Editorial Dashboard — mysite.example.com — Marzo 2026</h1>
|
|
558
|
+
<div class="meta">
|
|
559
|
+
Generato: 2026-03-02 14:30 | Next: Mar 11 — "Zero calorie, tutto gusto"
|
|
560
|
+
</div>
|
|
561
|
+
<div class="progress-bar">
|
|
562
|
+
<div class="progress-fill" style="width: 25%"></div>
|
|
563
|
+
<span>2/8 pubblicati</span>
|
|
564
|
+
</div>
|
|
565
|
+
<div class="pipeline-counts">
|
|
566
|
+
<span class="badge planned">3 planned</span>
|
|
567
|
+
<span class="badge draft">1 draft</span>
|
|
568
|
+
<span class="badge ready">2 ready</span>
|
|
569
|
+
<span class="badge scheduled">0 scheduled</span>
|
|
570
|
+
<span class="badge published">2 published</span>
|
|
571
|
+
</div>
|
|
572
|
+
</header>
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
### 6.3 Card HTML
|
|
576
|
+
|
|
577
|
+
```html
|
|
578
|
+
<div class="card" style="--status-color: var(--status-ready)">
|
|
579
|
+
<div class="card-header">
|
|
580
|
+
<span class="card-date">Mar 18</span>
|
|
581
|
+
<span class="card-type" title="post">📝</span>
|
|
582
|
+
</div>
|
|
583
|
+
<div class="card-title">Acqua premium: perché le ricerche sono esplose del 120%</div>
|
|
584
|
+
<div class="card-meta">
|
|
585
|
+
<span class="brief-id">BRF-2026-005</span>
|
|
586
|
+
</div>
|
|
587
|
+
<div class="card-tags">
|
|
588
|
+
<span class="tag">#wellness</span>
|
|
589
|
+
<span class="tag">#premium-water</span>
|
|
590
|
+
</div>
|
|
591
|
+
<div class="card-channels">
|
|
592
|
+
<span class="channel" title="LinkedIn">in</span>
|
|
593
|
+
<span class="channel" title="Newsletter">nl</span>
|
|
594
|
+
</div>
|
|
595
|
+
</div>
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
**Card "da assegnare"**:
|
|
599
|
+
|
|
600
|
+
```html
|
|
601
|
+
<div class="card card--empty" style="--status-color: var(--status-planned)">
|
|
602
|
+
<div class="card-header">
|
|
603
|
+
<span class="card-date">Mar 20</span>
|
|
604
|
+
<span class="card-type" title="post">📝</span>
|
|
605
|
+
</div>
|
|
606
|
+
<div class="card-title card-title--placeholder">[da assegnare]</div>
|
|
607
|
+
</div>
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
### 6.4 Signals Strip HTML
|
|
611
|
+
|
|
612
|
+
```html
|
|
613
|
+
<section class="signals-strip">
|
|
614
|
+
<h2>Signals</h2>
|
|
615
|
+
<div class="signal-list">
|
|
616
|
+
<div class="signal signal--up">
|
|
617
|
+
<span class="signal-arrow">▲</span>
|
|
618
|
+
<span class="signal-delta">+120%</span>
|
|
619
|
+
<span class="signal-entity">"acqua premium" impressions</span>
|
|
620
|
+
<span class="signal-action">content cluster opportunity</span>
|
|
621
|
+
</div>
|
|
622
|
+
<!-- ... altre anomalie ... -->
|
|
623
|
+
</div>
|
|
624
|
+
</section>
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
### 6.5 Icone Canali
|
|
628
|
+
|
|
629
|
+
Abbreviazioni testuali (no icon font, no SVG inline per semplicità):
|
|
630
|
+
|
|
631
|
+
| Canale | Abbrev | Title attribute |
|
|
632
|
+
|--------|--------|-----------------|
|
|
633
|
+
| linkedin | `in` | LinkedIn |
|
|
634
|
+
| twitter | `tw` | Twitter/X |
|
|
635
|
+
| newsletter / mailchimp | `nl` | Newsletter |
|
|
636
|
+
| buffer | `bf` | Buffer |
|
|
637
|
+
|
|
638
|
+
Stile: pill badge con background colorato.
|
|
639
|
+
|
|
640
|
+
```css
|
|
641
|
+
.channel {
|
|
642
|
+
display: inline-block;
|
|
643
|
+
padding: 2px 6px;
|
|
644
|
+
border-radius: 4px;
|
|
645
|
+
font-size: 11px;
|
|
646
|
+
font-weight: 600;
|
|
647
|
+
text-transform: uppercase;
|
|
648
|
+
}
|
|
649
|
+
.channel[title="LinkedIn"] { background: #0077b5; color: white; }
|
|
650
|
+
.channel[title="Twitter/X"] { background: #1da1f2; color: white; }
|
|
651
|
+
.channel[title="Newsletter"]{ background: #f59e0b; color: white; }
|
|
652
|
+
.channel[title="Buffer"] { background: #168eea; color: white; }
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
---
|
|
656
|
+
|
|
657
|
+
## 7. Gestione Edge Case
|
|
658
|
+
|
|
659
|
+
### 7.1 File Mancanti
|
|
660
|
+
|
|
661
|
+
| Scenario | Comportamento |
|
|
662
|
+
|----------|--------------|
|
|
663
|
+
| `{site_id}.config.md` non esiste | Errore: "Site config not found for '{siteId}'. Run `wp-editorial-planner` first." |
|
|
664
|
+
| `YYYY-MM-editorial.state.md` non esiste | Warning: "No calendar for {month}. Dashboard shows empty Kanban." Genera HTML con tutte le colonne vuote. |
|
|
665
|
+
| `signals-feed.md` non esiste | Signals strip non renderizzata. |
|
|
666
|
+
| `pipeline-active/` vuoto | Sezione brief nella card non mostra dati aggiuntivi. |
|
|
667
|
+
| `pipeline-archive/` vuoto | Nessun impatto (i brief archiviati non appaiono nel Kanban). |
|
|
668
|
+
|
|
669
|
+
### 7.2 Dati Inconsistenti
|
|
670
|
+
|
|
671
|
+
| Scenario | Comportamento |
|
|
672
|
+
|----------|--------------|
|
|
673
|
+
| Calendar entry ha Brief ID ma il file brief non esiste | Card mostra il Brief ID con badge `⚠ missing` |
|
|
674
|
+
| Brief ha status diverso da calendar entry | Usa lo status del calendar (il calendar è la source of truth per il Kanban) |
|
|
675
|
+
| Entry senza data | Skip entry, non appare nel Kanban |
|
|
676
|
+
| Titolo > 60 caratteri | Tronca con `...` nel card title, titolo completo nel `title` attribute (hover) |
|
|
677
|
+
|
|
678
|
+
### 7.3 Multi-Sito (Futuro)
|
|
679
|
+
|
|
680
|
+
Quando ci sono più siti configurati:
|
|
681
|
+
|
|
682
|
+
- `--site` è obbligatorio
|
|
683
|
+
- Se omesso, lo script lista i siti disponibili e esce
|
|
684
|
+
- Ogni sito ha il suo HTML separato
|
|
685
|
+
- Non esiste (per ora) un dashboard multi-sito aggregato
|
|
686
|
+
|
|
687
|
+
---
|
|
688
|
+
|
|
689
|
+
## 8. File Output
|
|
690
|
+
|
|
691
|
+
### 8.1 Naming Convention
|
|
692
|
+
|
|
693
|
+
```
|
|
694
|
+
.content-state/.dashboard-{siteId}-{YYYY-MM}.html
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
Esempio: `.content-state/.dashboard-mysite-2026-03.html`
|
|
698
|
+
|
|
699
|
+
- Prefisso `.` (hidden file) perché è un artefatto generato, non dati
|
|
700
|
+
- Nella directory `.content-state/` per coerenza (è uno "stato" derivato)
|
|
701
|
+
- **Gitignored**: aggiungere `.content-state/.dashboard-*.html` al `.gitignore`
|
|
702
|
+
|
|
703
|
+
### 8.2 Apertura Browser
|
|
704
|
+
|
|
705
|
+
```javascript
|
|
706
|
+
import { exec } from 'node:child_process';
|
|
707
|
+
import { platform } from 'node:os';
|
|
708
|
+
|
|
709
|
+
function openInBrowser(filepath) {
|
|
710
|
+
const cmd = platform() === 'darwin' ? 'open' :
|
|
711
|
+
platform() === 'win32' ? 'start' :
|
|
712
|
+
'xdg-open';
|
|
713
|
+
exec(`${cmd} "${filepath}"`);
|
|
714
|
+
}
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
---
|
|
718
|
+
|
|
719
|
+
## 9. Verifica Design
|
|
720
|
+
|
|
721
|
+
### 9.1 Checklist Pre-Implementazione
|
|
722
|
+
|
|
723
|
+
- [ ] La palette colori è leggibile su sfondo chiaro?
|
|
724
|
+
- [ ] Le abbreviazioni canali sono intuitive?
|
|
725
|
+
- [ ] L'HTML è < 30 KB con i dati di esempio (8 entries, 3 anomalie)?
|
|
726
|
+
- [ ] Il layout responsivo funziona a 768px?
|
|
727
|
+
- [ ] Il parser frontmatter gestisce tutti i campi dei file reali?
|
|
728
|
+
- [ ] Il parser tabelle gestisce `—`, `[da assegnare]`, virgole nei canali?
|
|
729
|
+
- [ ] `xdg-open` funziona su WSL2?
|
|
730
|
+
|
|
731
|
+
### 9.2 Test con Dati Reali
|
|
732
|
+
|
|
733
|
+
Il dataset di test è lo stato corrente di `.content-state/` per mysite (Marzo 2026):
|
|
734
|
+
- 8 entries nel calendario (2 published, 1 draft, 2 ready, 3 planned)
|
|
735
|
+
- 1 brief attivo (BRF-2026-005)
|
|
736
|
+
- 1 brief archiviato (BRF-2026-001)
|
|
737
|
+
- 3 anomalie nel signals feed
|
|
738
|
+
|
|
739
|
+
Questo dataset è sufficiente per validare tutti i casi: card con titolo, card vuota, card con brief, card senza brief, card pubblicata con post ID.
|
|
740
|
+
|
|
741
|
+
---
|
|
742
|
+
|
|
743
|
+
## 10. Documenti Derivati
|
|
744
|
+
|
|
745
|
+
Da questo design document deriva:
|
|
746
|
+
|
|
747
|
+
```
|
|
748
|
+
docs/plans/2026-03-02-dashboard-kanban-design.md ← QUESTO DOCUMENTO
|
|
749
|
+
└── docs/plans/2026-03-02-dashboard-kanban-implementation.md (prossimo passo)
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
Il piano di implementazione conterrà i task operativi TDD per:
|
|
753
|
+
1. `scripts/context-scanner.mjs` (SCAN + AGGREGATE)
|
|
754
|
+
2. `scripts/dashboard-renderer.mjs` (RENDER + CLI)
|
|
755
|
+
3. `skills/wp-dashboard/SKILL.md`
|
|
756
|
+
4. Test con dati reali
|
|
757
|
+
5. Version bump e changelog
|
|
758
|
+
|
|
759
|
+
---
|
|
760
|
+
|
|
761
|
+
*Design document per il Kanban editoriale del WordPress Manager Plugin. Fase A della Dashboard Strategy.*
|