@inizioevoke/veeva-astroclm-core 1.0.8 → 1.1.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/agents/claude/analytics-auditor/SKILL.md +274 -0
- package/agents/claude/page-manager/SKILL.md +2 -2
- package/agents/claude/veeva-config-manager/SKILL.md +1 -1
- package/dist/apps/veeva-config-manager/create.js +2 -2
- package/dist/apps/veeva-config-manager/templates/config.ts.txt +0 -2
- package/package.json +1 -1
- package/postinstall.mjs +10 -8
- package/src/apps/veeva-config-manager/create.ts +2 -2
- package/src/apps/veeva-config-manager/templates/config.ts.txt +0 -2
- package/agents/claude/SKILL.md +0 -15
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: analytics-auditor
|
|
3
|
+
description: >
|
|
4
|
+
Starts the Astro dev server, visits every page, clicks configurable CSS selectors,
|
|
5
|
+
captures all browser console messages, and writes a deduplication report of unique
|
|
6
|
+
log messages grouped by page. Use this skill whenever the user asks to audit,
|
|
7
|
+
record, or report on analytics, metrics, or events.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Analytics Auditor Skill
|
|
11
|
+
|
|
12
|
+
## Purpose
|
|
13
|
+
|
|
14
|
+
Start the dev server, click interactive elements, harvest every `console.*` message,
|
|
15
|
+
and produce a report of **unique messages** (grouped by page) written to
|
|
16
|
+
`analytics-audit-report.md` in the project root.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Instructions
|
|
21
|
+
|
|
22
|
+
### Step 0 — Load the Playwright MCP tool schemas
|
|
23
|
+
|
|
24
|
+
Before doing anything else, call:
|
|
25
|
+
```
|
|
26
|
+
ToolSearch({ query: "select:mcp__plugin_playwright_playwright__browser_navigate,mcp__plugin_playwright_playwright__browser_click,mcp__plugin_playwright_playwright__browser_console_messages,mcp__plugin_playwright_playwright__browser_snapshot,mcp__plugin_playwright_playwright__browser_wait_for,mcp__plugin_playwright_playwright__browser_evaluate,mcp__plugin_playwright_playwright__browser_handle_dialog,mcp__plugin_playwright_playwright__browser_close" })
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
You will need all of those tools to be loaded and callable.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
### Step 1 — Start the dev server and detect its port
|
|
34
|
+
|
|
35
|
+
Astro auto-increments the port when the default (4321) is already in use, so never
|
|
36
|
+
assume a fixed port. Instead, capture the server's own output to read the URL it
|
|
37
|
+
actually binds to.
|
|
38
|
+
|
|
39
|
+
Run this command to start the server and tee its output to a temp file:
|
|
40
|
+
```bash
|
|
41
|
+
npm run dev > /tmp/astro-dev.log 2>&1 &
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Then poll the log file until Astro prints its ready URL. **Do not use `grep -P`** —
|
|
45
|
+
it is not supported on Windows. Use this portable polling loop instead:
|
|
46
|
+
```bash
|
|
47
|
+
for i in $(seq 1 20); do
|
|
48
|
+
port=$(grep -o 'localhost:[0-9]*' /tmp/astro-dev.log | head -1 | grep -o '[0-9]*$')
|
|
49
|
+
if [ -n "$port" ]; then echo $port; break; fi
|
|
50
|
+
sleep 1
|
|
51
|
+
done
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Extract the port from that output and use it as `DEV_PORT` for all subsequent URLs
|
|
55
|
+
(e.g. `http://localhost:${DEV_PORT}/home/`).
|
|
56
|
+
|
|
57
|
+
If the log file already contains a port before you start the server, the server was
|
|
58
|
+
already running — use that port and skip the `npm run dev` step.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
### Step 2 — Discover pages and define click selectors
|
|
63
|
+
|
|
64
|
+
#### Page URLs
|
|
65
|
+
|
|
66
|
+
Do **not** hardcode the page list. Instead, read the `src/pages` directory to discover
|
|
67
|
+
all `.astro` files:
|
|
68
|
+
```bash
|
|
69
|
+
find src/pages -name "*.astro" | sort
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Convert each file path to a URL route using these rules:
|
|
73
|
+
- Strip the `src/pages/` prefix and `.astro` extension
|
|
74
|
+
- A file named `index.astro` maps to `/`
|
|
75
|
+
- All other files map to `/<name>/` (trailing slash)
|
|
76
|
+
- Nested files (e.g. `src/pages/about/moa.astro`) map to `/about/moa/`
|
|
77
|
+
|
|
78
|
+
Prepend `http://localhost:${DEV_PORT}` from Step 1 to form the full URL.
|
|
79
|
+
|
|
80
|
+
Example — `src/pages/home.astro` → `http://localhost:${DEV_PORT}/home/`
|
|
81
|
+
|
|
82
|
+
#### Click selectors
|
|
83
|
+
|
|
84
|
+
On every page, query for all elements matching the following selectors and click each
|
|
85
|
+
one that is visible and not disabled:
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
a
|
|
89
|
+
button
|
|
90
|
+
[role="button"]
|
|
91
|
+
[role="link"]
|
|
92
|
+
[role="tab"]
|
|
93
|
+
input[type="checkbox"]
|
|
94
|
+
input[type="radio"]
|
|
95
|
+
[role="checkbox"]
|
|
96
|
+
[role="radio"]
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Use a single combined CSS selector string joined with commas when querying the page.
|
|
100
|
+
The user may supply additional or replacement selectors in their request — merge them
|
|
101
|
+
into this list rather than replacing it entirely unless the user says otherwise.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
### Step 3 — Per-page procedure
|
|
106
|
+
|
|
107
|
+
For **each page URL** discovered in Step 2:
|
|
108
|
+
|
|
109
|
+
1. Navigate to the page URL.
|
|
110
|
+
2. Wait for the page to load (use `browser_wait_for` with a known selector like `h1`).
|
|
111
|
+
3. **Inject a navigation guard** using `browser_evaluate` to prevent link clicks from
|
|
112
|
+
navigating away while still allowing the app's own click handlers (including
|
|
113
|
+
tracking) to run. Use a **bubble-phase** listener so the app's capture-phase and
|
|
114
|
+
bubble-phase handlers fire first:
|
|
115
|
+
```js
|
|
116
|
+
() => {
|
|
117
|
+
document.addEventListener('click', (e) => {
|
|
118
|
+
const a = e.target.closest('a');
|
|
119
|
+
if (a) e.preventDefault();
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
This approach ensures tracking events fire before navigation is cancelled.
|
|
124
|
+
**Do NOT add a `beforeunload` listener** — it causes a "Leave site?" browser dialog
|
|
125
|
+
that blocks every subsequent Playwright navigation.
|
|
126
|
+
|
|
127
|
+
4. Take a `browser_snapshot` to get the current accessibility tree. Use the refs from
|
|
128
|
+
this snapshot for all subsequent clicks on this page.
|
|
129
|
+
|
|
130
|
+
5. For **each matched element** (visible, not disabled):
|
|
131
|
+
a. Click it using `browser_click`.
|
|
132
|
+
b. Wait 500ms for async effects.
|
|
133
|
+
c. If the click opened a modal or significantly changed the DOM, take a new
|
|
134
|
+
`browser_snapshot` before proceeding — snapshot refs become stale after DOM
|
|
135
|
+
changes and will cause errors if reused.
|
|
136
|
+
d. If a dialog appears (check the snapshot for `Modal state`), dismiss it with
|
|
137
|
+
`browser_handle_dialog` before continuing.
|
|
138
|
+
|
|
139
|
+
6. After all clicks on the page, call `browser_console_messages` **once** with
|
|
140
|
+
`level: "debug"`. Since `all` defaults to `false`, this returns only the messages
|
|
141
|
+
from the current page navigation — no need to diff between clicks.
|
|
142
|
+
|
|
143
|
+
7. Store all messages with the page route (e.g. `/home/`) as their source.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
### Step 4 — Parse, deduplicate, and structure the data
|
|
148
|
+
|
|
149
|
+
#### Identify tracking events
|
|
150
|
+
|
|
151
|
+
When an element is clicked the app emits a tracking log in this format:
|
|
152
|
+
```
|
|
153
|
+
[HH:MM:SS.mmm] DEBUG: track {slide: 'home', type: 'button', name: 'button_evo_flip_card_trigger'}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
For every console message, check whether its text matches this pattern:
|
|
157
|
+
```
|
|
158
|
+
DEBUG: track {…}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
If it matches, parse out the object payload `{ slide, type, name }` — these are the
|
|
162
|
+
primary data points of interest. Store them as structured tracking events separate from
|
|
163
|
+
general log messages.
|
|
164
|
+
|
|
165
|
+
#### Collect all messages
|
|
166
|
+
|
|
167
|
+
Gather every console message captured across all pages into a flat list:
|
|
168
|
+
```
|
|
169
|
+
{
|
|
170
|
+
page: string, // route, e.g. "/home/"
|
|
171
|
+
level: "log"|"warn"|"error"|"info"|"debug",
|
|
172
|
+
text: string, // full raw message text
|
|
173
|
+
tracking: { // only present when the message is a tracking event
|
|
174
|
+
slide: string,
|
|
175
|
+
type: string,
|
|
176
|
+
name: string
|
|
177
|
+
} | null
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
#### Deduplicate
|
|
182
|
+
|
|
183
|
+
Two messages are duplicates if they share the same `text`. When the same message
|
|
184
|
+
appears on multiple pages, record all pages it appeared on.
|
|
185
|
+
|
|
186
|
+
#### Tiers
|
|
187
|
+
|
|
188
|
+
Organize into four tiers:
|
|
189
|
+
1. **Tracking Events** — messages matching the `DEBUG: track` pattern
|
|
190
|
+
2. **Errors** — `console.error`
|
|
191
|
+
3. **Warnings** — `console.warn`
|
|
192
|
+
4. **Info / Log** — `console.log`, `console.info`, `console.debug` (excluding tracking events)
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
### Step 5 — Write the report
|
|
197
|
+
|
|
198
|
+
Write a Markdown file to `analytics-audit-report.md` in the project root with this structure:
|
|
199
|
+
|
|
200
|
+
```markdown
|
|
201
|
+
# Browser Console Log Audit
|
|
202
|
+
|
|
203
|
+
**Date:** <today's date>
|
|
204
|
+
**Pages audited:** /home/, /about/, /about/moa/
|
|
205
|
+
**Total unique messages:** <N>
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Tracking Events (<count>)
|
|
210
|
+
|
|
211
|
+
| Slide | Type | Name | Pages |
|
|
212
|
+
|-------|------|------|-------|
|
|
213
|
+
| `home` | `button` | `button_evo_flip_card_trigger` | /home/ |
|
|
214
|
+
|
|
215
|
+
## Errors (<count>)
|
|
216
|
+
|
|
217
|
+
| Message | Pages |
|
|
218
|
+
|---------|-------|
|
|
219
|
+
| `<message text>` | /home/, /about/ |
|
|
220
|
+
|
|
221
|
+
## Warnings (<count>)
|
|
222
|
+
|
|
223
|
+
| Message | Pages |
|
|
224
|
+
|---------|-------|
|
|
225
|
+
| `<message text>` | /home/ |
|
|
226
|
+
|
|
227
|
+
## Info / Log (<count>)
|
|
228
|
+
|
|
229
|
+
| Message | Pages |
|
|
230
|
+
|---------|-------|
|
|
231
|
+
| `<message text>` | /about/moa/ |
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
*Generated by the analytics-auditor skill.*
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
If a tier has zero messages, write `_None_` instead of a table.
|
|
239
|
+
|
|
240
|
+
After writing the file, tell the user where the report was saved and give a brief
|
|
241
|
+
summary (e.g. "Found 8 tracking events, 2 errors, 1 warning, 5 info messages across 3 pages").
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
### Step 6 — Close the browser and clean up
|
|
246
|
+
|
|
247
|
+
Call `browser_close` when done so the Playwright session is cleaned up.
|
|
248
|
+
|
|
249
|
+
Then delete the `.playwright-mcp` directory that Playwright creates for snapshots and
|
|
250
|
+
console logs:
|
|
251
|
+
```bash
|
|
252
|
+
rm -rf .playwright-mcp
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Do **not** stop the dev server — the user likely wants to keep it running.
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## Notes
|
|
260
|
+
|
|
261
|
+
- The Astro dev server defaults to port **4321** but increments on collision — always
|
|
262
|
+
read the actual port from the server output as described in Step 1.
|
|
263
|
+
- **`grep -P` is not supported on Windows** — use `grep -o` with basic regex patterns.
|
|
264
|
+
- **Snapshot refs go stale** after DOM mutations (modal open/close, tab switches). Always
|
|
265
|
+
re-snapshot after any click that visibly changes the page structure.
|
|
266
|
+
- **`browser_console_messages` fails if a dialog is showing** — check the snapshot for
|
|
267
|
+
`Modal state` and dismiss with `browser_handle_dialog` before calling it.
|
|
268
|
+
- **Call `browser_console_messages` once per page** (at the end), not after every click.
|
|
269
|
+
The default `all: false` scopes results to the current navigation, giving a clean
|
|
270
|
+
per-page log with no diffing needed.
|
|
271
|
+
- If the user provides their own list of selectors or pages, replace the defaults in
|
|
272
|
+
Step 2 before proceeding.
|
|
273
|
+
- If a selector does not match any element on a page, log a warning in the report's
|
|
274
|
+
header section rather than failing.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: page-manager
|
|
3
3
|
description: >
|
|
4
|
-
Scaffolds a new page in this Veeva CLM
|
|
4
|
+
Scaffolds a new page in this Veeva Astro CLM project. Use this skill whenever the user asks to
|
|
5
5
|
create a new page, add a page, scaffold a slide, generate a new route, or set up a new CLM slide —
|
|
6
6
|
even if they don't say "page manager" explicitly. If the user is working in this project and wants
|
|
7
7
|
a new page, this skill should always trigger.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: veeva-config-manager
|
|
3
3
|
description: >
|
|
4
4
|
Manages Veeva CLM configuration for this project. Use this skill whenever the user asks to
|
|
5
5
|
create, generate, or set up a Veeva config, initialize a veeva-config.ts, scaffold a new
|
|
@@ -127,8 +127,8 @@ export default async function (rootDir) {
|
|
|
127
127
|
.replace(/##IOS_RESOLUTION##/g, iosResolution)
|
|
128
128
|
.replace(/##DISABLE_ACTIONS##/g, `[${disableActions.map((a) => { return `'${a}'`; }).join(', ')}]`)
|
|
129
129
|
.replace(/##CRM_TARGET##/g, crmTarget)
|
|
130
|
-
.replace(/##SLIDES##/g, `[${slides.join(', ')}]`)
|
|
131
|
-
|
|
130
|
+
.replace(/##SLIDES##/g, `[${slides.join(', ')}]`);
|
|
131
|
+
// .replace(/##SLIDE_IDS##/g, [...slideIds].join());
|
|
132
132
|
await writeFile(resolvePath(rootDir, 'veeva-config.ts'), template);
|
|
133
133
|
await sleep(); // make it seem like we're doing something
|
|
134
134
|
spin.stop('Config file generated!');
|
package/package.json
CHANGED
package/postinstall.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { cpSync, readdirSync, rmSync } from 'fs';
|
|
2
2
|
import { join, dirname } from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
|
|
@@ -6,12 +6,14 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
6
6
|
|
|
7
7
|
// Resolve the consuming project's root (three levels up from node_modules/@inizioevoke/astro-core)
|
|
8
8
|
const projectRoot = join(__dirname, '..', '..', '..');
|
|
9
|
-
const destDir = join(projectRoot, '.claude', 'skills'
|
|
10
|
-
const src = join(__dirname, 'agents', 'claude'
|
|
11
|
-
const dest = join(destDir, 'SKILL.md');
|
|
9
|
+
const destDir = join(projectRoot, '.claude', 'skills');
|
|
10
|
+
const src = join(__dirname, 'agents', 'claude');
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
const skillDirs = readdirSync(src);
|
|
13
|
+
for (const dir of skillDirs) {
|
|
14
|
+
const skillDestDir = join(destDir, dir);
|
|
15
|
+
rmSync(skillDestDir, { recursive: true, force: true });
|
|
16
|
+
cpSync(join(src, dir), skillDestDir, { recursive: true });
|
|
17
|
+
}
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
console.log(`[@inizioevoke/veeva-astroclm-core] Copied skill to ${dest}`);
|
|
19
|
+
console.log(`[@inizioevoke/veeva-astroclm-core] Copied skills to ${destDir}`);
|
|
@@ -141,8 +141,8 @@ export default async function(rootDir: string) {
|
|
|
141
141
|
.replace(/##IOS_RESOLUTION##/g, iosResolution)
|
|
142
142
|
.replace(/##DISABLE_ACTIONS##/g, `[${disableActions.map((a) => { return `'${a}'`}).join(', ')}]`)
|
|
143
143
|
.replace(/##CRM_TARGET##/g, crmTarget)
|
|
144
|
-
.replace(/##SLIDES##/g, `[${slides.join(', ')}]`)
|
|
145
|
-
.replace(/##SLIDE_IDS##/g, [...slideIds].join());
|
|
144
|
+
.replace(/##SLIDES##/g, `[${slides.join(', ')}]`);
|
|
145
|
+
// .replace(/##SLIDE_IDS##/g, [...slideIds].join());
|
|
146
146
|
await writeFile(resolvePath(rootDir, 'veeva-config.ts'), template);
|
|
147
147
|
|
|
148
148
|
await sleep(); // make it seem like we're doing something
|
package/agents/claude/SKILL.md
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: inizioevoke-veeva-astroclm
|
|
3
|
-
description: >
|
|
4
|
-
Skills and tools for working in this Veeva CLM Astro project (inizioevoke-veeva-astroclm).
|
|
5
|
-
Use this skill for any project-specific tasks such as creating pages, managing content, or
|
|
6
|
-
running project utilities. Triggers on any request that involves scaffolding, generating,
|
|
7
|
-
or managing project files in this Veeva CLM project.
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# inizioevoke-veeva-astroclm Skills
|
|
11
|
-
|
|
12
|
-
## Available skills
|
|
13
|
-
|
|
14
|
-
- **Page Manager** — Create a new page in the project → read `../../../node_modules/@inizioevoke/veeva-astroclm-core/agents/claude/page-manager/SKILL.md`
|
|
15
|
-
- **Veeva Config Manager** — Manage Veeva CLM configuration → read `../../../node_modules/@inizioevoke/veeva-astroclm-core/agents/claude/veeva-config-manager/SKILL.md`
|