Skip to content

Commit 10c5106

Browse files
annab1cursoragent
andcommitted
refactor: Drop product-area detection, just tag the caller's product name
Per-repo file-path -> product-area mapping was more granularity than needed for a Slack pulse feed. The product field is now always the calling repo's name (e.g. "Elementor" or "Elementor Pro"), passed as a plain product-name input instead of a product-areas-file. This removes the whole Product Area Detection section from the shared prompt, drops the AI's product classification (and its validation), and lets callers drop their per-repo areas file entirely. Ref: ED-24831 Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 2ec7364 commit 10c5106

4 files changed

Lines changed: 25 additions & 85 deletions

File tree

actions/product-pulse/README.md

Lines changed: 13 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,9 @@ user-friendly pulse update to Slack. Ported from the `elementor` and `elementor-
55
repos so both can share one implementation.
66

77
The full prompt (goal, decision criteria, writing style, output format, examples) lives
8-
here in `prompt-template.md` and is shared by every caller. Each consuming repo only
9-
keeps a small `product-areas.md` file with the three things that genuinely differ per
10-
repo — `PRODUCT_NAME`, the `PRODUCT_AREAS` file-path mapping, and the `PRODUCT_ENUM`
11-
list — plus a thin wrapper workflow that triggers on `pull_request: closed` and calls
12-
this action. See `product-areas.example.md` for the expected format.
8+
here in `prompt-template.md` and is shared by every caller. Each consuming repo just
9+
passes its `product-name` (e.g. `"Elementor"` or `"Elementor Pro"`) plus a thin wrapper
10+
workflow that triggers on `pull_request: closed` and calls this action.
1311

1412
## Usage
1513

@@ -38,35 +36,22 @@ jobs:
3836
- uses: elementor/elementor-editor-github-actions/actions/product-pulse@main
3937
with:
4038
pr-number: ${{ github.event.pull_request.number }}
41-
product-areas-file: .github/product-pulse-areas.md
39+
product-name: 'Elementor'
4240
cursor-api-key: ${{ secrets.CURSOR_APIKEY }}
4341
slack-token: ${{ secrets.SLACK_TOKEN }}
4442
slack-channel-id: ${{ secrets.SLACK_PULSE_CHANNEL_ID }}
4543
```
4644
47-
`.github/product-pulse-areas.md` in the caller repo:
48-
49-
```
50-
PRODUCT_NAME: Elementor
51-
52-
PRODUCT_AREAS:
53-
- `modules/editor-one/`**"Editor"**
54-
- `modules/ai/`**"Elementor AI"**
55-
- `core/`, `includes/`, and other paths not listed above → **"Elementor"**
56-
57-
PRODUCT_ENUM: "Elementor", "Editor", "Elementor AI"
58-
```
59-
6045
## Inputs
6146
62-
| Input | Required | Default | Description |
63-
| --------------------- | -------- | -------------- | -------------------------------------------------------------------------- |
64-
| `pr-number` | yes | – | Merged PR number to generate the pulse for |
65-
| `product-areas-file` | yes | – | Path to the caller repo's `PRODUCT_NAME`/`PRODUCT_AREAS`/`PRODUCT_ENUM` file |
66-
| `model` | no | `composer-2.5` | Cursor Agent model used for generation |
67-
| `cursor-api-key` | yes | – | Cursor Agent API key |
68-
| `slack-token` | yes | – | Slack bot token with `chat:write` |
69-
| `slack-channel-id` | yes | – | Slack channel ID for pulse notifications |
47+
| Input | Required | Default | Description |
48+
| -------------------- | -------- | -------------- | ---------------------------------------------------------------- |
49+
| `pr-number` | yes | – | Merged PR number to generate the pulse for |
50+
| `product-name` | yes | – | Product name used in the prompt and Slack header |
51+
| `model` | no | `composer-2.5` | Cursor Agent model used for generation |
52+
| `cursor-api-key` | yes | – | Cursor Agent API key |
53+
| `slack-token` | yes | – | Slack bot token with `chat:write` |
54+
| `slack-channel-id` | yes | – | Slack channel ID for pulse notifications |
7055

7156
The full generic prompt lives in this action's `prompt-template.md` and is rendered
72-
together with the caller's `product-areas-file` before being sent to Cursor Agent.
57+
with the caller's `product-name` substituted in before being sent to Cursor Agent.

actions/product-pulse/action.yml

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ inputs:
55
pr-number:
66
description: 'Pull request number to generate the pulse for'
77
required: true
8-
product-areas-file:
9-
description: 'Path (relative to the checked-out caller repo) to the repo-specific product-areas markdown file (PRODUCT_NAME/PRODUCT_AREAS/PRODUCT_ENUM). Combined with this action''s shared prompt-template.md to build the full prompt.'
8+
product-name:
9+
description: 'Product name to use in the prompt and Slack header (e.g. "Elementor" or "Elementor Pro")'
1010
required: true
1111
model:
1212
description: 'Cursor Agent model to use for pulse generation'
@@ -47,13 +47,12 @@ runs:
4747
shell: bash
4848
env:
4949
ACTION_PATH: ${{ github.action_path }}
50-
PRODUCT_AREAS_FILE: ${{ inputs.product-areas-file }}
50+
PRODUCT_NAME: ${{ inputs.product-name }}
5151
run: |
5252
python3 "$ACTION_PATH/scripts/render-prompt.py" \
5353
"$ACTION_PATH/prompt-template.md" \
54-
"$PRODUCT_AREAS_FILE" \
55-
/tmp/rendered_prompt.md \
56-
/tmp/product_name.txt
54+
"$PRODUCT_NAME" \
55+
/tmp/rendered_prompt.md
5756
5857
- name: Generate product pulse
5958
id: generate
@@ -102,7 +101,6 @@ runs:
102101
env:
103102
ACTION_PATH: ${{ github.action_path }}
104103
run: |
105-
DEFAULT_PRODUCT=$(cat /tmp/product_name.txt)
106104
RAW_OUTPUT=$(cat /tmp/pulse_output.json)
107105
108106
RESULT_CONTENT=$(echo "$RAW_OUTPUT" | jq -r '.result // empty')
@@ -143,7 +141,6 @@ runs:
143141
144142
TITLE=$(echo "$OUTPUT" | jq -r '.title')
145143
DESCRIPTION=$(echo "$OUTPUT" | jq -r '.description')
146-
PRODUCT=$(echo "$OUTPUT" | jq -r --arg default "$DEFAULT_PRODUCT" '.product // $default')
147144
TYPE=$(echo "$OUTPUT" | jq -r '.type // "feature"')
148145
149146
if [ -z "$TITLE" ] || [ "$TITLE" = "null" ]; then
@@ -167,7 +164,7 @@ runs:
167164
;;
168165
esac
169166
170-
echo "✅ Generated pulse: $TITLE (product: $PRODUCT, type: $TYPE)"
167+
echo "✅ Generated pulse: $TITLE (type: $TYPE)"
171168
172169
PR_BODY=$(cat /tmp/pr.json | jq -r '.body // ""')
173170
LOOM_URL=$(echo "$PR_BODY" | grep -oE 'https://(www\.)?loom\.com/share/[a-zA-Z0-9]+' | head -1 || echo "")
@@ -190,7 +187,6 @@ runs:
190187
echo "description<<EOF"
191188
echo "$DESCRIPTION"
192189
echo "EOF"
193-
echo "product=$PRODUCT"
194190
echo "type=$TYPE"
195191
} >> $GITHUB_OUTPUT
196192
@@ -203,7 +199,7 @@ runs:
203199
SLACK_CHANNEL_ID: ${{ inputs.slack-channel-id }}
204200
TITLE: ${{ steps.update.outputs.title }}
205201
DESCRIPTION: ${{ steps.update.outputs.description }}
206-
PRODUCT: ${{ steps.update.outputs.product }}
202+
PRODUCT: ${{ inputs.product-name }}
207203
TYPE: ${{ steps.update.outputs.type }}
208204
LOOM_URL: ${{ steps.update.outputs.loom_url }}
209205
JIRA_URL: ${{ steps.update.outputs.jira_url }}

actions/product-pulse/prompt-template.md

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,6 @@ Every included PR must also be classified with a `type`:
3636
- `"improvement"` — makes an existing feature faster, smoother, or easier to use, without adding new capability
3737
- `"internal"` — a notable change worth logging but with no direct end-user impact (e.g. new admin-only tooling)
3838

39-
## Product Area Detection
40-
41-
Based on which files the PR touches, determine the product area:
42-
43-
{{PRODUCT_AREAS}}
44-
- If the PR touches multiple areas, pick the primary one (where the main feature lives).
45-
- Changes in `packages/` belong to whichever product area consumes them — check the PR context.
46-
4739
## Output Format
4840

4941
Output ONLY valid JSON in this exact format:
@@ -52,7 +44,6 @@ Output ONLY valid JSON in this exact format:
5244
{
5345
"skip": false,
5446
"type": "feature",
55-
"product": "Widgets",
5647
"title": "Loop Through WooCommerce Products",
5748
"description": "You can now build dynamic product grids that automatically loop through your WooCommerce catalog. No more manually adding each product one by one."
5849
}
@@ -67,8 +58,6 @@ Or if skipping:
6758
}
6859
```
6960

70-
The `product` field must be one of: {{PRODUCT_ENUM}}.
71-
7261
The `type` field must be one of: `"feature"`, `"fix"`, `"improvement"`, `"internal"`.
7362

7463
## Writing Style
@@ -97,7 +86,6 @@ Follow Lovable's product update style:
9786
{
9887
"skip": false,
9988
"type": "feature",
100-
"product": "Editor",
10189
"title": "Drag Widgets Between Columns",
10290
"description": "You can now drag widgets directly from one column to another in the editor. No more copy-paste or delete-and-recreate when rearranging your layout."
10391
}
@@ -125,7 +113,6 @@ Why it's bad: The title doesn't tell users WHAT the feature does.
125113
{
126114
"skip": false,
127115
"type": "feature",
128-
"product": "Theme Builder",
129116
"title": "Preview Templates Before Publishing",
130117
"description": "You can now preview how a theme template looks against real content before making it live. Catch layout issues before your visitors do."
131118
}
@@ -136,7 +123,6 @@ Why it's bad: The title doesn't tell users WHAT the feature does.
136123
{
137124
"skip": false,
138125
"type": "fix",
139-
"product": "Widgets",
140126
"title": "Fixed Broken Icons in Nav Menu",
141127
"description": "Custom icons in the Nav Menu widget no longer disappear when the Inline Font Icons experiment is off."
142128
}
@@ -156,11 +142,11 @@ Why it's bad: The title doesn't tell users WHAT the feature does.
156142
A PR titled `chore: update Playwright config` that only touches `.github/workflows/playwright.yml` and `tests/playwright/` → skip. CI and test infrastructure changes are never product-facing.
157143

158144
### Feature PR (include):
159-
A PR that adds a brand-new user-visible widget or capability, touching paths matched in the Product Area Detection list above → include with the corresponding product. New user-visible widgets always qualify.
145+
A PR that adds a brand-new user-visible widget or capability → include. New user-visible widgets always qualify.
160146

161147
### Partial `packages/` changes (evaluate carefully):
162148
A PR that only bumps versions or updates a CHANGELOG.md under `packages/` → skip (release housekeeping).
163-
A PR that changes behavior inside a `packages/` source directory with corresponding UI impact → include with the product area that consumes that package. Read the diff and PR body to determine whether the package change reaches users.
149+
A PR that changes behavior inside a `packages/` source directory with corresponding UI impact → include. Read the diff and PR body to determine whether the package change reaches users.
164150

165151
## Context You'll Receive
166152

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,10 @@
11
#!/usr/bin/env python3
2-
import re
32
import sys
43

5-
template_path, areas_path, output_prompt_path, output_product_name_path = sys.argv[1:5]
6-
7-
with open(areas_path, encoding="utf-8") as f:
8-
areas_text = f.read()
9-
10-
name_match = re.search(r"^PRODUCT_NAME:\s*(.+)$", areas_text, re.MULTILINE)
11-
enum_match = re.search(r"^PRODUCT_ENUM:\s*(.+)$", areas_text, re.MULTILINE)
12-
areas_match = re.search(r"^PRODUCT_AREAS:\s*\n(.*?)\n+PRODUCT_ENUM:", areas_text, re.DOTALL | re.MULTILINE)
13-
14-
if not (name_match and enum_match and areas_match):
15-
sys.exit(
16-
f"{areas_path} must define PRODUCT_NAME, PRODUCT_AREAS, and PRODUCT_ENUM"
17-
)
18-
19-
product_name = name_match.group(1).strip()
20-
product_areas = areas_match.group(1).strip()
21-
product_enum = enum_match.group(1).strip()
4+
template_path, product_name, output_path = sys.argv[1:4]
225

236
with open(template_path, encoding="utf-8") as f:
24-
rendered = f.read()
25-
26-
rendered = (
27-
rendered
28-
.replace("{{PRODUCT_NAME}}", product_name)
29-
.replace("{{PRODUCT_AREAS}}", product_areas)
30-
.replace("{{PRODUCT_ENUM}}", product_enum)
31-
)
7+
rendered = f.read().replace("{{PRODUCT_NAME}}", product_name)
328

33-
with open(output_prompt_path, "w", encoding="utf-8") as f:
9+
with open(output_path, "w", encoding="utf-8") as f:
3410
f.write(rendered)
35-
36-
with open(output_product_name_path, "w", encoding="utf-8") as f:
37-
f.write(product_name)

0 commit comments

Comments
 (0)