@relipa/ai-flow-kit 0.1.1-beta.0 → 0.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/custom/mcp-presets/README.md +33 -0
- package/custom/mcp-presets/playwright.json +11 -0
- package/custom/rules/test-patterns.md +105 -0
- package/custom/skills/automation-testing/SKILL.md +168 -0
- package/custom/skills/automation-testing/templates/BasePage.ts +30 -0
- package/custom/skills/automation-testing/templates/playwright.config.ts +21 -0
- package/custom/skills/automation-testing/templates/repos.json +8 -0
- package/custom/skills/figma-to-component/SKILL.md +164 -59
- package/docs/common/CHANGELOG.md +16 -0
- package/package.json +2 -2
|
@@ -119,6 +119,39 @@ See: [Figma workflow guide](../../docs/common/workflows/figma.md)
|
|
|
119
119
|
|
|
120
120
|
---
|
|
121
121
|
|
|
122
|
+
### Playwright (`playwright.json`)
|
|
123
|
+
|
|
124
|
+
Drive a real browser to explore selectors, verify flows, and capture evidence. Powers the `automation-testing` skill (Phase 3 — Explore).
|
|
125
|
+
|
|
126
|
+
**When to use:** Generating E2E UI tests. The skill uses MCP to read the live DOM (real selectors, not guessed) and screenshot each step, then emits durable `.spec.ts` files for CI.
|
|
127
|
+
|
|
128
|
+
**Setup:**
|
|
129
|
+
```bash
|
|
130
|
+
aiflow init --adapter playwright
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Requirements:**
|
|
134
|
+
- The web app under test must be running and reachable at `baseUrl`
|
|
135
|
+
- No API token needed
|
|
136
|
+
- First run downloads browsers via `npx playwright install` (one-time)
|
|
137
|
+
|
|
138
|
+
**Tools provided by `@playwright/mcp`:**
|
|
139
|
+
- `browser_navigate(url)` — open a page
|
|
140
|
+
- `browser_snapshot()` — accessibility tree (source of real selectors)
|
|
141
|
+
- `browser_click` / `browser_type` / `browser_fill` — interact with elements
|
|
142
|
+
- `browser_take_screenshot(path)` — capture evidence
|
|
143
|
+
|
|
144
|
+
**Usage:**
|
|
145
|
+
```
|
|
146
|
+
/automation-testing baseUrl: http://localhost:3000 explore: true
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
> **Hybrid principle:** MCP is for *exploration + evidence* only. It does not replace test files — the skill always emits committable `.spec.ts` so CI can re-run.
|
|
150
|
+
|
|
151
|
+
See: [automation-testing design](../../requirements/autotest/AutomationTestWorkflow.md)
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
122
155
|
### Google Sheets (`google-sheets.json`)
|
|
123
156
|
|
|
124
157
|
Connect to Google Sheets for loading task context.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# E2E Test Patterns (Playwright)
|
|
2
|
+
|
|
3
|
+
Reusable patterns the `automation-testing` skill applies when generating Page Objects and specs. Selector priority is always: **`getByRole` → `getByLabel` → `getByTestId`** → (CSS/XPath only as last resort).
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Login / Authentication
|
|
8
|
+
|
|
9
|
+
**Test cases to cover:**
|
|
10
|
+
- Valid credentials → redirect to landing page
|
|
11
|
+
- Wrong password → error message visible
|
|
12
|
+
- Empty required field → validation message
|
|
13
|
+
- (optional) Locked/disabled account → blocked
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
test('TC01 - Login success', async ({ page }) => {
|
|
17
|
+
const login = new LoginPage(page);
|
|
18
|
+
await login.navigate('/login');
|
|
19
|
+
await login.login('user@example.com', 'Password123');
|
|
20
|
+
await expect(page).toHaveURL(/\/dashboard/);
|
|
21
|
+
});
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 2. CRUD (Create / Read / Update / Delete)
|
|
27
|
+
|
|
28
|
+
**Cover:** create → appears in list → edit → reflects change → delete → removed. Always assert on the list/detail after each mutation, not just the success toast.
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
test('Create item appears in list', async ({ page }) => {
|
|
32
|
+
const list = new ItemListPage(page);
|
|
33
|
+
await list.navigate('/items');
|
|
34
|
+
await list.openCreate();
|
|
35
|
+
await list.fillForm({ name: 'Sample' });
|
|
36
|
+
await list.submit();
|
|
37
|
+
await expect(list.rowByName('Sample')).toBeVisible();
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
> **Test data:** prefer a unique suffix (e.g. timestamp passed in via fixture) so reruns don't collide. Clean up created data in `afterEach` when possible.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 3. Form validation
|
|
46
|
+
|
|
47
|
+
- Submit empty → each required field shows its message
|
|
48
|
+
- Invalid format (email, phone) → format message
|
|
49
|
+
- Boundary (min/max length) → length message
|
|
50
|
+
- Valid input → no error, submit succeeds
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
test('Email format validation', async ({ page }) => {
|
|
54
|
+
const form = new SignupPage(page);
|
|
55
|
+
await form.navigate('/signup');
|
|
56
|
+
await form.emailInput.fill('not-an-email');
|
|
57
|
+
await form.submit();
|
|
58
|
+
await expect(page.getByText(/invalid email/i)).toBeVisible();
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## 4. Modal / Dialog
|
|
65
|
+
|
|
66
|
+
- Open trigger → modal visible
|
|
67
|
+
- Confirm → action runs + modal closes
|
|
68
|
+
- Cancel / backdrop / Esc → modal closes, no action
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
await page.getByRole('button', { name: 'Delete' }).click();
|
|
72
|
+
const dialog = page.getByRole('dialog');
|
|
73
|
+
await expect(dialog).toBeVisible();
|
|
74
|
+
await dialog.getByRole('button', { name: 'Confirm' }).click();
|
|
75
|
+
await expect(dialog).toBeHidden();
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 5. Pagination / Table
|
|
81
|
+
|
|
82
|
+
- Default page loads expected count
|
|
83
|
+
- Next/Prev changes rows
|
|
84
|
+
- Page size change updates count
|
|
85
|
+
- Sort by column reorders rows
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
await expect(page.getByRole('row')).toHaveCount(11); // header + 10 rows
|
|
89
|
+
await page.getByRole('button', { name: 'Next' }).click();
|
|
90
|
+
await expect(page.getByText('Page 2')).toBeVisible();
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## 6. General assertions & waits
|
|
96
|
+
|
|
97
|
+
- Prefer **web-first assertions** (`await expect(locator).toBeVisible()`) — they auto-retry. Never `waitForTimeout` to "wait for UI".
|
|
98
|
+
- Assert on **user-visible outcome** (URL, text, element state), not internal state.
|
|
99
|
+
- For async data, assert the loaded element, not a spinner disappearing.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 7. Evidence (MCP explore phase)
|
|
104
|
+
|
|
105
|
+
When exploring via Playwright MCP, capture a screenshot per significant step into `<evidenceDir>/[ticket-id]/TC<NN>-step<N>.png`. Name files so the reviewer can map evidence → test case at a glance.
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: automation-testing
|
|
3
|
+
description: Generate runnable Playwright E2E tests for a web UI from requirement/business-logic docs or a tester's free-form draft. Uses Playwright MCP to explore real selectors and capture evidence on a live browser, then emits durable .spec.ts files plus an HTML report. Tester does not need to know TypeScript.
|
|
4
|
+
keywords: automation, testing, playwright, e2e, mcp, testcase, browser, evidence, report, qa, tester
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Automation Testing (Hybrid MCP)
|
|
8
|
+
|
|
9
|
+
Generate **runnable** Playwright E2E tests for a web UI. Works in a **multi-repo workspace** (a parent folder containing backend + frontend repos).
|
|
10
|
+
|
|
11
|
+
## Core principle — HYBRID
|
|
12
|
+
|
|
13
|
+
> Playwright MCP does **not** replace test files. It **improves** them.
|
|
14
|
+
> Use the live browser to *learn* the app's real selectors and capture evidence, but the deliverable is always committable `.spec.ts` so CI can re-run.
|
|
15
|
+
|
|
16
|
+
## When to use
|
|
17
|
+
|
|
18
|
+
- Tester/Dev wants E2E UI tests but doesn't write TypeScript
|
|
19
|
+
- A requirement (`requirement.md`) or business-logic doc exists and test cases should be derived from it
|
|
20
|
+
- A finished feature needs regression coverage with evidence
|
|
21
|
+
|
|
22
|
+
## How to invoke
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
/automation-testing baseUrl: <url> [explore: true|false] [autoRun: true|false] [headed: true|false] [ticketId: <id>]
|
|
26
|
+
---
|
|
27
|
+
(optional) tester's free-form test case draft
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Example:**
|
|
31
|
+
```
|
|
32
|
+
/automation-testing baseUrl: http://localhost:3000 explore: true autoRun: true
|
|
33
|
+
---
|
|
34
|
+
1. Vào /login, nhập đúng email/password → vào dashboard
|
|
35
|
+
2. Nhập sai password → hiện thông báo lỗi
|
|
36
|
+
3. Để trống email → hiện validation lỗi
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Params
|
|
40
|
+
|
|
41
|
+
| Param | Type | Default | Description |
|
|
42
|
+
|-------|------|---------|-------------|
|
|
43
|
+
| `baseUrl` | string | — | URL of the running web app **(required)** |
|
|
44
|
+
| `explore` | boolean | `true` | Use Playwright MCP to explore real selectors + capture evidence before generating |
|
|
45
|
+
| `autoRun` | boolean | `true` | Run `npx playwright test` after generating |
|
|
46
|
+
| `headed` | boolean | `false` | Show browser window during `autoRun` (CI should keep `false`) |
|
|
47
|
+
| `ticketId` | string | — | Ticket id; if omitted, read from `.aiflow/context/current.json` |
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Phase 1 — Collect
|
|
52
|
+
|
|
53
|
+
1. **Resolve workspace.** Read `.aiflow/repos.json`.
|
|
54
|
+
- If it does NOT exist: copy the template at `.claude/skills/automation-testing/templates/repos.json`, ask the user to confirm the paths (`backend`, `frontend`, `e2e`, `planDir`, `evidenceDir`), then write `.aiflow/repos.json`. **Do not guess** the folder layout.
|
|
55
|
+
2. **Resolve ticket.** Use `ticketId` param, else read `.aiflow/context/current.json`. If none, use a descriptive slug for `[ticket-id]`.
|
|
56
|
+
3. **Read inputs** (any that exist):
|
|
57
|
+
- `<planDir>/[ticket-id]/requirement.md` (Gate 1)
|
|
58
|
+
- `<planDir>/[ticket-id]/plan.md` (Gate 2)
|
|
59
|
+
- business-logic doc (path provided by user or pasted)
|
|
60
|
+
- tester's draft (text after `---`)
|
|
61
|
+
- `custom/rules/test-patterns.md`
|
|
62
|
+
4. **Require `baseUrl`.** If missing, ask before continuing.
|
|
63
|
+
5. If NO testcase draft is provided, that's fine — derive test cases from the requirement + business-logic docs (this is the primary value).
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Phase 2 — Analyze (GATE 1)
|
|
68
|
+
|
|
69
|
+
Produce a structured test case list:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
## Test Cases — [Feature Name]
|
|
73
|
+
|
|
74
|
+
| ID | Test case | Steps | Expected Result |
|
|
75
|
+
|------|--------------------------------|------------------------------------------------|----------------------------------|
|
|
76
|
+
| TC01 | Đăng nhập thành công | 1. /login 2. Nhập email+pass hợp lệ 3. Submit | Redirect /dashboard |
|
|
77
|
+
| TC02 | Sai mật khẩu | 1. /login 2. Sai password 3. Submit | Hiện error message |
|
|
78
|
+
| TC03 | Validation email trống | 1. /login 2. Submit ngay | "Email là bắt buộc" |
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
> ⚠️ **GATE — STOP HERE.** Show the list and ask:
|
|
82
|
+
> "Danh sách test cases trên có đúng không? Thêm/sửa/xóa gì không? Xác nhận để tôi khám phá app và sinh code."
|
|
83
|
+
>
|
|
84
|
+
> **Wait for confirmation before Phase 3.** Do not explore or generate code until confirmed.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Phase 3 — Explore (Playwright MCP, live browser)
|
|
89
|
+
|
|
90
|
+
Only if `explore: true` AND the Playwright MCP server is available AND the app is reachable.
|
|
91
|
+
|
|
92
|
+
For each test case, drive the real app:
|
|
93
|
+
1. `browser_navigate(baseUrl + path)` — open the page
|
|
94
|
+
2. `browser_snapshot()` — read the accessibility tree; **extract real selectors** (prefer `getByRole` → `getByLabel` → `getByTestId`)
|
|
95
|
+
3. Walk the flow (type, click) to confirm it behaves as the test case expects
|
|
96
|
+
4. `browser_take_screenshot()` → save to `<evidenceDir>/[ticket-id]/TC<NN>-step<N>.png`
|
|
97
|
+
5. Record the verified selectors for Phase 4
|
|
98
|
+
|
|
99
|
+
**Output of this phase:** a selector map (element → verified locator) + evidence screenshots.
|
|
100
|
+
|
|
101
|
+
> Do NOT commit the MCP session. The committable artifact is produced in Phase 4.
|
|
102
|
+
|
|
103
|
+
### Fallback
|
|
104
|
+
If `explore: false`, MCP unavailable, or the app is unreachable:
|
|
105
|
+
- Generate from inference (v1 behavior)
|
|
106
|
+
- **Warn clearly** that selectors are unverified and a developer must check them
|
|
107
|
+
- Skip evidence capture
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Phase 4 — Generate (durable scripts)
|
|
112
|
+
|
|
113
|
+
Write files under the `e2e` path from `repos.json`:
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
<e2e>/
|
|
117
|
+
├── pages/
|
|
118
|
+
│ ├── BasePage.ts ← only if missing (template below)
|
|
119
|
+
│ └── [Feature]Page.ts ← Page Object; selectors from Phase 3
|
|
120
|
+
├── specs/[ticket-id]/[feature].spec.ts
|
|
121
|
+
├── fixtures/test.ts ← only if missing
|
|
122
|
+
└── playwright.config.ts ← only if missing (template below)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**Rules:**
|
|
126
|
+
- `BasePage.ts` / `playwright.config.ts` / `fixtures/test.ts` — create only if absent. Templates at `.claude/skills/automation-testing/templates/`.
|
|
127
|
+
- **Page Object** — declare locators using verified selectors from Phase 3 (`getByRole` > `getByLabel` > `getByTestId`; avoid CSS/XPath unless unavoidable). One async method per action.
|
|
128
|
+
- **Spec** — one `test()` per case; group with `test.describe`; test ids MUST match Phase 2 (TC01, TC02…); explicit `expect` assertions.
|
|
129
|
+
- Apply patterns from `custom/rules/test-patterns.md` where relevant.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Phase 5 — Run & Report
|
|
134
|
+
|
|
135
|
+
### If `autoRun: true`
|
|
136
|
+
1. Determine the target repo for E2E (per §Git below) and `cd` there.
|
|
137
|
+
2. Run `npx playwright test <e2e>/specs/[ticket-id]/` (add `--headed` if `headed: true`).
|
|
138
|
+
3. On completion: `npx playwright show-report` (or report the HTML path).
|
|
139
|
+
4. Summarize: pass/fail count, failed-test screenshots, plus the Phase 3 exploration evidence folder.
|
|
140
|
+
|
|
141
|
+
### If `autoRun: false`
|
|
142
|
+
Print manual instructions:
|
|
143
|
+
```
|
|
144
|
+
✅ Scripts generated.
|
|
145
|
+
npx playwright test # all
|
|
146
|
+
npx playwright test <e2e>/specs/[ticket-id]/ # this ticket
|
|
147
|
+
npx playwright test --headed # show browser
|
|
148
|
+
npx playwright show-report # HTML report
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Git & Gate 5 (multi-repo)
|
|
154
|
+
|
|
155
|
+
The parent workspace is usually NOT a git repo. Commit to the **repo that owns the code under test**:
|
|
156
|
+
|
|
157
|
+
| Artifact | Commit target |
|
|
158
|
+
|----------|---------------|
|
|
159
|
+
| E2E tests for UI | the **frontend** repo (`<frontend>/e2e/`) — unless a dedicated `e2e` repo exists |
|
|
160
|
+
| requirement.md / plan.md | `<planDir>` at parent level (shared; not committed into a sub-repo) |
|
|
161
|
+
|
|
162
|
+
> When creating a PR (aiflow Gate 5 / `requesting-code-review`), `cd` into the correct sub-repo first — `git`/PR commands must run inside that repo, not the parent cwd.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Two mandatory gates
|
|
167
|
+
1. After Phase 2 — confirm the test case list before exploring/generating.
|
|
168
|
+
2. After Phase 5 — hand off (evidence + report) and ask the developer to review before commit/PR.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Page, expect } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* BasePage — shared helpers for all Page Objects.
|
|
5
|
+
* Generated by the aiflow `automation-testing` skill. Create once; extend per feature.
|
|
6
|
+
*/
|
|
7
|
+
export abstract class BasePage {
|
|
8
|
+
constructor(protected readonly page: Page) {}
|
|
9
|
+
|
|
10
|
+
/** Navigate to a path relative to playwright.config `baseURL`. */
|
|
11
|
+
async navigate(path = '/'): Promise<void> {
|
|
12
|
+
await this.page.goto(path);
|
|
13
|
+
await this.waitForLoad();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Wait until network is idle (page settled). */
|
|
17
|
+
async waitForLoad(): Promise<void> {
|
|
18
|
+
await this.page.waitForLoadState('networkidle');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Capture a full-page screenshot as evidence. */
|
|
22
|
+
async screenshot(name: string): Promise<void> {
|
|
23
|
+
await this.page.screenshot({ path: `evidence/${name}.png`, fullPage: true });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Assert the current URL matches (string or RegExp). */
|
|
27
|
+
async expectUrl(url: string | RegExp): Promise<void> {
|
|
28
|
+
await expect(this.page).toHaveURL(url);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { defineConfig, devices } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Playwright config generated by the aiflow `automation-testing` skill.
|
|
5
|
+
* Create once per target repo; edit to fit your environment.
|
|
6
|
+
*/
|
|
7
|
+
export default defineConfig({
|
|
8
|
+
testDir: './specs',
|
|
9
|
+
fullyParallel: false,
|
|
10
|
+
retries: process.env.CI ? 1 : 0,
|
|
11
|
+
reporter: [['html', { open: 'never' }], ['list']],
|
|
12
|
+
use: {
|
|
13
|
+
baseURL: process.env.BASE_URL || 'http://localhost:3000',
|
|
14
|
+
trace: 'on-first-retry',
|
|
15
|
+
screenshot: 'only-on-failure',
|
|
16
|
+
video: 'retain-on-failure',
|
|
17
|
+
},
|
|
18
|
+
projects: [
|
|
19
|
+
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
|
|
20
|
+
],
|
|
21
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "Workspace map for the automation-testing skill. Copied to .aiflow/repos.json. Paths are relative to the parent workspace folder (aiflow cwd). Adjust to match your layout.",
|
|
3
|
+
"backend": "./backend",
|
|
4
|
+
"frontend": "./frontend",
|
|
5
|
+
"e2e": "./frontend/e2e",
|
|
6
|
+
"planDir": "./plan",
|
|
7
|
+
"evidenceDir": "./frontend/e2e/evidence"
|
|
8
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: figma-to-component
|
|
3
|
-
description: Read designs from Figma and generate React/Next.js/Vue components following project conventions. Supports specific frame URLs and entire pages.
|
|
4
|
-
keywords: figma, design, component, ui, generate, react, nextjs, vue, frontend
|
|
3
|
+
description: Read designs from Figma and generate React/Next.js App Router/Vue/Angular components following project conventions. Supports specific frame URLs and entire pages.
|
|
4
|
+
keywords: figma, design, component, ui, generate, react, nextjs, nextjs-app-router, vue, angular, frontend
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Figma to Component
|
|
@@ -14,6 +14,19 @@ keywords: figma, design, component, ui, generate, react, nextjs, vue, frontend
|
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
17
|
+
## Step 0: Verify Figma MCP is available
|
|
18
|
+
|
|
19
|
+
Before doing anything, confirm the Figma MCP server is connected in this session:
|
|
20
|
+
|
|
21
|
+
- The tool `mcp__figma__get_figma_data` (and `mcp__figma__download_figma_images`) must be available.
|
|
22
|
+
- If it is **not** available, STOP and tell the developer:
|
|
23
|
+
> Figma MCP not connected. Run `aiflow init -a figma` (REST API token) or
|
|
24
|
+
> `aiflow init -a figma-desktop` (Figma Desktop), then restart the session.
|
|
25
|
+
|
|
26
|
+
Do not guess design values from the URL — without the MCP server the skill cannot read the design.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
17
30
|
## Process
|
|
18
31
|
|
|
19
32
|
### Step 1: Identify input
|
|
@@ -30,20 +43,24 @@ Extract `fileKey` and `nodeId` from the URL.
|
|
|
30
43
|
Use Figma MCP tools to fetch design information:
|
|
31
44
|
|
|
32
45
|
```
|
|
33
|
-
1. Get node info
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
46
|
+
1. Get node info, styles, and components in one call:
|
|
47
|
+
get_figma_data(fileKey, nodeId, depth=2)
|
|
48
|
+
|
|
49
|
+
2. Export image if visual reference needed:
|
|
50
|
+
download_figma_images(fileKey, nodes=[{ nodeId, fileName }], localPath)
|
|
37
51
|
```
|
|
38
52
|
|
|
53
|
+
Note: `get_figma_data` returns layout, fills, strokes, typography, and component
|
|
54
|
+
definitions in a single response. No separate style or component fetch is needed.
|
|
55
|
+
|
|
39
56
|
Analyze the output to extract:
|
|
40
57
|
- **Layout**: flexbox direction, gap, padding, alignment
|
|
41
58
|
- **Sizing**: width/height (fixed vs fill vs hug)
|
|
42
|
-
- **Colors**: fills, strokes → map to Tailwind colors if available
|
|
43
|
-
- **Typography**: font-size, font-weight, line-height, letter-spacing
|
|
44
|
-
- **Spacing**: padding, margin, gap
|
|
45
|
-
- **Border**: radius, width, color
|
|
46
|
-
- **Shadow**: box-shadow
|
|
59
|
+
- **Colors**: fills, strokes → map to project CSS tokens / Tailwind colors if available
|
|
60
|
+
- **Typography**: font-size, font-weight, line-height, letter-spacing
|
|
61
|
+
- **Spacing**: padding, margin, gap
|
|
62
|
+
- **Border**: radius, width, color
|
|
63
|
+
- **Shadow**: box-shadow
|
|
47
64
|
- **States**: hover, focus, disabled, active (if variants present)
|
|
48
65
|
- **Responsive**: breakpoints if multiple frames present
|
|
49
66
|
|
|
@@ -73,19 +90,12 @@ download_figma_images({
|
|
|
73
90
|
- `localPath` defaults to `public/assets/figma/` (relative to the project root)
|
|
74
91
|
- `fileName` follows the pattern: `<layer name in lowercase, spaces replaced with ->-<nodeId>.png`
|
|
75
92
|
- Example: layer "Banner Top" with nodeId "123-456" → `banner-top-123-456.png`
|
|
76
|
-
- If MCP does not support `download_figma_images`,
|
|
93
|
+
- If MCP does not support `download_figma_images`, get the image URL via `get_figma_data`, then notify DEV to download manually and place in `public/assets/figma/`
|
|
77
94
|
|
|
78
95
|
**Record the result:**
|
|
79
96
|
|
|
80
97
|
After export, save the mapping for use in Step 4:
|
|
81
98
|
|
|
82
|
-
```
|
|
83
|
-
imageMap = {
|
|
84
|
-
"<nodeId>": "public/assets/figma/<fileName>.png"
|
|
85
|
-
}
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
Example:
|
|
89
99
|
```
|
|
90
100
|
imageMap = {
|
|
91
101
|
"123-456": "public/assets/figma/banner-top-123-456.png",
|
|
@@ -93,53 +103,50 @@ imageMap = {
|
|
|
93
103
|
}
|
|
94
104
|
```
|
|
95
105
|
|
|
96
|
-
### Step 3:
|
|
106
|
+
### Step 3: Detect CSS tooling, then map Figma tokens
|
|
97
107
|
|
|
98
|
-
**
|
|
99
|
-
```
|
|
100
|
-
Figma hex #3B82F6 → Tailwind blue-500
|
|
101
|
-
Figma hex #EF4444 → Tailwind red-500
|
|
102
|
-
Figma rgba(0,0,0,0.5) → Tailwind black/50
|
|
103
|
-
No Tailwind match → use arbitrary value [#hexcode]
|
|
104
|
-
```
|
|
108
|
+
**Detect the project's styling approach first** (do not assume Tailwind):
|
|
105
109
|
|
|
106
|
-
**
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
110
|
+
1. `tailwind.config.*` present → **Tailwind** — map to utility classes; extend `tailwind.config` with design tokens when a value has no built-in match
|
|
111
|
+
2. `*.module.css` / `*.module.scss` files present → **CSS Modules** — emit a co-located `.module.css` and reference `styles.x`
|
|
112
|
+
3. `styled-components` / `@emotion` in `package.json` → **CSS-in-JS** — emit styled components
|
|
113
|
+
4. None of the above → **vanilla CSS** — emit a co-located `.css` file with CSS custom properties
|
|
114
|
+
|
|
115
|
+
**Design tokens:** prefer mapping Figma Styles/Variables to existing project tokens
|
|
116
|
+
(Tailwind theme keys or CSS custom properties) instead of hardcoding hex/px values.
|
|
117
|
+
Only fall back to an arbitrary value when no token matches.
|
|
118
|
+
|
|
119
|
+
**Tailwind reference mappings** (when Tailwind is the detected tooling):
|
|
116
120
|
|
|
117
|
-
**Typography mapping:**
|
|
118
121
|
```
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
text-xl
|
|
124
|
-
|
|
125
|
-
text-3xl: 30px
|
|
126
|
-
font-normal: 400
|
|
127
|
-
font-medium: 500
|
|
128
|
-
font-semibold: 600
|
|
129
|
-
font-bold: 700
|
|
122
|
+
Color: #3B82F6 → blue-500 · #EF4444 → red-500 · rgba(0,0,0,0.5) → black/50
|
|
123
|
+
no match → arbitrary value [#hexcode]
|
|
124
|
+
Spacing: 4→p-1/gap-1 · 8→p-2 · 12→p-3 · 16→p-4 · 24→p-6 · 32→p-8 (8px grid)
|
|
125
|
+
no match → arbitrary value [20px]
|
|
126
|
+
Type: 12→text-xs · 14→text-sm · 16→text-base · 18→text-lg · 20→text-xl · 24→text-2xl · 30→text-3xl
|
|
127
|
+
400→font-normal · 500→font-medium · 600→font-semibold · 700→font-bold
|
|
130
128
|
```
|
|
131
129
|
|
|
132
130
|
### Step 4: Generate component
|
|
133
131
|
|
|
134
|
-
**Framework detection** — check
|
|
135
|
-
|
|
136
|
-
|
|
132
|
+
**Framework detection** — check in this order:
|
|
133
|
+
|
|
134
|
+
1. Read project's `CLAUDE.md` for framework identifier:
|
|
135
|
+
- `nextjs-app-router` → Next.js App Router (Server/Client Components)
|
|
136
|
+
- `reactjs` / `nextjs` → React / Next.js Pages Router `.tsx`
|
|
137
|
+
- `vue-nuxt` → Vue 3 `.vue` with Composition API
|
|
138
|
+
- `angular` → Angular `@Component` class
|
|
139
|
+
2. Scan project files if CLAUDE.md is ambiguous:
|
|
140
|
+
- `app/` directory present → `nextjs-app-router`
|
|
141
|
+
- `angular.json` present → `angular`
|
|
142
|
+
- `nuxt.config.*` present → `vue-nuxt`
|
|
143
|
+
3. Fallback → `nextjs` (React Pages Router)
|
|
137
144
|
|
|
138
145
|
**Image node rendering rule:**
|
|
139
146
|
|
|
140
147
|
Before rendering each node, check `imageMap` from Step 2.5:
|
|
141
148
|
- If `nodeId` is in `imageMap` → render using `<img>` (React/Vue) or `<Image>` (Next.js), do NOT use CSS `background-image`
|
|
142
|
-
- If not in `imageMap` → render normally using div +
|
|
149
|
+
- If not in `imageMap` → render normally using div + styling classes
|
|
143
150
|
|
|
144
151
|
React/Next.js template for image node:
|
|
145
152
|
```tsx
|
|
@@ -197,6 +204,57 @@ export const [ComponentName] = ({ className }: [ComponentName]Props) => {
|
|
|
197
204
|
};
|
|
198
205
|
```
|
|
199
206
|
|
|
207
|
+
#### Next.js App Router component template:
|
|
208
|
+
|
|
209
|
+
Default to **Server Component**. Add `'use client'` only when the design has
|
|
210
|
+
interactive states (hover effects, click handlers, form inputs, modals).
|
|
211
|
+
|
|
212
|
+
```tsx
|
|
213
|
+
// Server Component — no interactivity (default)
|
|
214
|
+
interface [ComponentName]Props {
|
|
215
|
+
className?: string;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export default async function [ComponentName]({ className }: [ComponentName]Props) {
|
|
219
|
+
return (
|
|
220
|
+
<div className={cn(
|
|
221
|
+
// Layout classes
|
|
222
|
+
// Sizing classes
|
|
223
|
+
// Color classes
|
|
224
|
+
className
|
|
225
|
+
)}>
|
|
226
|
+
{/* Child nodes */}
|
|
227
|
+
</div>
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
```tsx
|
|
233
|
+
// Client Component — has onClick / useState / useEffect
|
|
234
|
+
'use client'
|
|
235
|
+
|
|
236
|
+
import { useState } from 'react'
|
|
237
|
+
|
|
238
|
+
interface [ComponentName]Props {
|
|
239
|
+
className?: string;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export function [ComponentName]({ className }: [ComponentName]Props) {
|
|
243
|
+
return (
|
|
244
|
+
<div className={cn(
|
|
245
|
+
// Layout classes
|
|
246
|
+
// Sizing classes
|
|
247
|
+
// Color classes
|
|
248
|
+
className
|
|
249
|
+
)}>
|
|
250
|
+
{/* Child nodes */}
|
|
251
|
+
</div>
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
File path: `app/components/[ComponentName].tsx` or `components/[ComponentName].tsx`
|
|
257
|
+
|
|
200
258
|
#### Vue 3 component template:
|
|
201
259
|
|
|
202
260
|
```vue
|
|
@@ -221,6 +279,40 @@ const props = withDefaults(defineProps<Props>(), {});
|
|
|
221
279
|
</template>
|
|
222
280
|
```
|
|
223
281
|
|
|
282
|
+
#### Angular component template:
|
|
283
|
+
|
|
284
|
+
Use Tailwind classes if `tailwind.config.*` exists in the project root.
|
|
285
|
+
Otherwise use `styles` array with CSS-in-component.
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
import { Component, Input } from '@angular/core';
|
|
289
|
+
import { CommonModule } from '@angular/common';
|
|
290
|
+
|
|
291
|
+
@Component({
|
|
292
|
+
selector: 'app-[component-name]',
|
|
293
|
+
standalone: true,
|
|
294
|
+
imports: [CommonModule],
|
|
295
|
+
template: `
|
|
296
|
+
<div [class]="hostClasses">
|
|
297
|
+
<!-- Child nodes -->
|
|
298
|
+
</div>
|
|
299
|
+
`,
|
|
300
|
+
styles: []
|
|
301
|
+
})
|
|
302
|
+
export class [ComponentName]Component {
|
|
303
|
+
@Input() className = '';
|
|
304
|
+
|
|
305
|
+
get hostClasses(): string {
|
|
306
|
+
return [
|
|
307
|
+
// Layout classes
|
|
308
|
+
// Sizing classes
|
|
309
|
+
// Color classes
|
|
310
|
+
this.className
|
|
311
|
+
].filter(Boolean).join(' ');
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
224
316
|
### Step 5: Handle interactive states
|
|
225
317
|
|
|
226
318
|
If Figma has variants (Default, Hover, Disabled, Active):
|
|
@@ -245,14 +337,14 @@ const variantClasses = {
|
|
|
245
337
|
After generation, verify:
|
|
246
338
|
|
|
247
339
|
- [ ] Layout matches Figma (flex direction, alignment, gap)
|
|
248
|
-
- [ ] Colors mapping correct (or
|
|
340
|
+
- [ ] Colors mapping correct (project tokens, or arbitrary values if no match)
|
|
249
341
|
- [ ] Typography correct (size, weight, line-height)
|
|
250
342
|
- [ ] Spacing correct (padding, margin, gap)
|
|
251
343
|
- [ ] Component has `className` prop for external overrides
|
|
252
|
-
- [ ]
|
|
344
|
+
- [ ] Conditional/mergeable classes use the project's helper (`cn()` for Tailwind)
|
|
253
345
|
- [ ] Responsive if Figma has mobile frames
|
|
254
346
|
- [ ] Props typed with TypeScript interface
|
|
255
|
-
- [ ] Image nodes use `<img>`
|
|
347
|
+
- [ ] Image nodes use `<img>` / `<Image>` (do not use CSS `background-image`)
|
|
256
348
|
- [ ] Image files exist in `public/assets/figma/` before submitting code
|
|
257
349
|
|
|
258
350
|
---
|
|
@@ -273,9 +365,9 @@ Always output in order:
|
|
|
273
365
|
|
|
274
366
|
## Rules
|
|
275
367
|
|
|
276
|
-
- **
|
|
277
|
-
- **
|
|
278
|
-
- **
|
|
368
|
+
- **Detect styling tooling first** — do not assume Tailwind; honour CSS Modules / styled-components / vanilla CSS when detected
|
|
369
|
+
- **Prefer design tokens** — map Figma Styles/Variables to project tokens; only hardcode a value when no token matches
|
|
370
|
+
- **Always use** the project's class-merge helper (`cn()` = clsx + twMerge for Tailwind)
|
|
279
371
|
- **Responsive** — if Figma only has 1 breakpoint, default to mobile-first design
|
|
280
372
|
- **Accessibility** — add `aria-label`, `role`, `alt` for interactive and image elements
|
|
281
373
|
- **Image** — detect image fills in Figma node data, export via `download_figma_images` MCP, save to `public/assets/figma/`. Use `<Image>` (Next.js) or `<img>` (React/Vue) with `src` pointing to the exported file. Do NOT use CSS `background-image` for image nodes.
|
|
@@ -283,6 +375,19 @@ Always output in order:
|
|
|
283
375
|
|
|
284
376
|
---
|
|
285
377
|
|
|
378
|
+
## Troubleshooting
|
|
379
|
+
|
|
380
|
+
| Symptom | Cause | Fix |
|
|
381
|
+
|---------|-------|-----|
|
|
382
|
+
| `mcp__figma__get_figma_data` not available | Figma MCP server not connected | Run `aiflow init -a figma` or `-a figma-desktop`, restart session |
|
|
383
|
+
| `403 Forbidden` / token rejected | PAT invalid or wrong header | Re-issue a `figd_...` token; `init` sends `X-Figma-Token` (not `Bearer`) |
|
|
384
|
+
| Tool call hangs / times out | Large node tree (`depth` too high) | Lower `depth` (e.g. `depth=1`), target a specific `nodeId` not the whole file |
|
|
385
|
+
| `404` on node | Wrong `fileKey`/`nodeId` | Re-copy the frame URL; node-id uses `-` in URL but `:` in API |
|
|
386
|
+
| Empty fills / no styles | Node is a component instance | Fetch the main component, or increase `depth` |
|
|
387
|
+
| `download_figma_images` unsupported | Older MCP preset | Get image URL via `get_figma_data`, download manually to `public/assets/figma/` |
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
286
391
|
## Trigger Example Commands
|
|
287
392
|
|
|
288
393
|
```
|
package/docs/common/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,22 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [0.1.1] - 2026-06-08
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Ubuntu / Linux clipboard support in `ak prompt`** — `ak prompt` now copies to the clipboard on native Linux. It auto-detects the display server (Wayland → `wl-copy`, X11 → `xclip` → `xsel`) and the package manager (`apt-get` / `dnf` / `pacman` / `zypper`). When no clipboard tool is installed it prints a clear warning (`⚠ Clipboard tool not found (need: xclip)`), shows the exact install command, and offers to install it via `sudo` then retries the copy. If the user declines or no package manager is found, it falls back to printing the full prompt for manual copy. Previously the command silently failed on Ubuntu when neither `xclip` nor `xsel` was present.
|
|
15
|
+
- **`figma-to-component`: image detection & export** — The skill now scans the Figma node tree for image nodes (`fills[].type === "IMAGE"`, `VECTOR`/`BOOLEAN_OPERATION`, complex non-CSS groups), exports them via `download_figma_images` into `public/assets/figma/`, and records an `imageMap`. Generated components render those nodes with `<Image>` (Next.js) / `<img>` (React/Vue) instead of CSS `background-image`.
|
|
16
|
+
- **`figma-to-component`: MCP availability pre-check (Step 0)** — The skill now verifies `mcp__figma__get_figma_data` / `mcp__figma__download_figma_images` are connected before running, and stops with setup instructions (`aiflow init -a figma` / `-a figma-desktop`) instead of guessing design values from the URL.
|
|
17
|
+
- **`figma-to-component`: CSS tooling detection + design tokens** — Detection now branches on the project's styling approach (Tailwind / CSS Modules / styled-components / vanilla CSS) instead of hardcoding Tailwind, and prefers mapping Figma Styles/Variables to existing project tokens (Tailwind theme keys or CSS custom properties), falling back to arbitrary values only when no token matches.
|
|
18
|
+
- **`figma-to-component`: troubleshooting table** — Added a troubleshooting section covering MCP-not-connected, `403`/token rejected, request timeout (lower `depth`), `404` node-id, empty fills, and unsupported `download_figma_images`.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- **Critical: `figma-to-component` source/installed divergence** — The source skill (`custom/skills/figma-to-component/SKILL.md`) still referenced the non-existent tool names `figma_get_file_nodes` / `figma_get_file_styles` / `figma_get_image` and only had 2-tier framework detection, while the installed `.claude/` copy had been fixed in 0.0.9. The next `aiflow init` / `sync-skills` would have overwritten the working copy with the broken source — re-introducing the 0.0.9 bug. Both copies are now synced to a single canonical version: correct tool names (`get_figma_data` / `download_figma_images`), 4-tier framework detection (App Router / React / Vue / Angular), and the image-export step.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
10
26
|
## [0.1.0] - 2026-05-28
|
|
11
27
|
|
|
12
28
|
### Added
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@relipa/ai-flow-kit",
|
|
3
|
-
"version": "0.1.1
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "All-in-one AI Flow Kit for team development with Claude AI - skills, templates, and MCP adapters",
|
|
5
5
|
"author": "Example Team",
|
|
6
6
|
"publishConfig": {
|
|
@@ -65,4 +65,4 @@
|
|
|
65
65
|
"<rootDir>/tests/**/*.test.js"
|
|
66
66
|
]
|
|
67
67
|
}
|
|
68
|
-
}
|
|
68
|
+
}
|