@relipa/ai-flow-kit 0.1.5 → 0.1.6
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/bin/aiflow.js +34 -0
- package/custom/rules/ml-conventions.md +11 -8
- package/custom/rules/project-conventions.md +18 -2
- package/custom/skills/design-experiment/SKILL.md +2 -2
- package/custom/skills/evaluate-model/SKILL.md +2 -2
- package/custom/skills/explore-data/SKILL.md +1 -1
- package/custom/skills/figma-to-component/SKILL.md +222 -20
- package/custom/skills/frame-ml-problem/SKILL.md +1 -1
- package/custom/skills/generate-spec/SKILL.md +19 -0
- package/custom/skills/read-study-requirement/SKILL.md +69 -1
- package/custom/skills/review-plan/SKILL.md +23 -0
- package/custom/templates/shared/create-spec-workflow.md +55 -0
- package/custom/templates/shared/create-testcase-workflow.md +55 -0
- package/custom/templates/shared/gate-workflow.md +115 -3
- package/custom/templates/shared/ml-gate-workflow.md +16 -9
- package/docs/common/AIFLOW.md +11 -1
- package/docs/common/CHANGELOG.md +32 -0
- package/docs/common/cli-reference.md +3 -1
- package/docs/common/workflows/figma.md +176 -105
- package/package.json +2 -2
- package/scripts/create-score-excel.js +135 -14
- package/scripts/detect.js +11 -0
- package/scripts/docs-branch.js +264 -0
- package/scripts/hooks/figma-rate-limit.js +83 -0
- package/scripts/hooks/session-start.js +41 -6
- package/scripts/init.js +29 -1
- package/scripts/prompt.js +45 -0
- package/scripts/task.js +30 -15
- package/scripts/use.js +6 -5
package/docs/common/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,35 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [0.1.6] - 2026-07-13
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **Mandatory Source & Docs sync at the start of every Gate — across DEV, TESTER, EXECUTE, BA, and QA workflows.** Previously, `git pull` for `AK-Docs`/`Shared-Docs` only ran via the `ak init` / `ak update` CLI commands (`scripts/docs-repo.js`), and the source repo itself was only synced once — inside the DEV workflow's Gate 1 pre-flight bullet. Now every Gate of every workflow syncs both the source repo and the docs repos before doing anything else:
|
|
17
|
+
- **New shared procedure — "Pre-flight — Đồng bộ Source & Docs":** (1) `cd` into the source repo itself (not just the outer workspace) → `git status --porcelain`, then `git pull --ff-only` if the working tree is clean (skipped silently if dirty, diverged, or no remote tracking branch); (2) `cd` into `AK-Docs/` (sibling folder at workspace root) → `git pull`; (3) `cd` into `Shared-Docs/` (sibling folder at workspace root) → `git pull`; (4) `cd` back to the original working directory before continuing the gate.
|
|
18
|
+
- **Failure handling:** if any `git pull` in the procedure fails (conflict, no remote, network, etc.), the AI does **not** block or stop the workflow — it shows a `⚠️ CẢNH BÁO` warning to the user and continues the gate with the current local data.
|
|
19
|
+
- **`custom/templates/shared/gate-workflow.md`** — added the shared procedure once under the mandatory intro; wired a short pointer to it into every gate of all 3 workflows it defines: `[DEV]` 5-Gate (Gates 1–5), `[TESTER]` 4-Gate (Gate 1, Phases 2a/2b/2c/2d, Gates 3–4), and `[EXECUTE]` 4-Gate (Gates 1–4). The old DEV Gate 1 bullet that only synced the source repo now points at the shared procedure instead of duplicating a narrower version of it.
|
|
20
|
+
- **`custom/templates/shared/create-spec-workflow.md`** (`[BA]` 4-Gate Spec Creation) — added the shared procedure near the top and a pointer to it at the start of Gates 1–4.
|
|
21
|
+
- **`custom/templates/shared/create-testcase-workflow.md`** (`[QA]` 4-Gate TestCase Creation) — added the shared procedure near the top and a pointer to it at the start of Gates 1–4.
|
|
22
|
+
- If `AK-Docs/` or `Shared-Docs/` doesn't exist at the workspace root, or isn't a git repo, that step is skipped silently (no warning) — the project may not use a separate docs repo.
|
|
23
|
+
- **New task type `gen-doc` — "Generate Document" 2-Gate flow** (`!34 feature/new_task_type`). A lighter flow for ad-hoc documentation tasks that don't need TDD/coding gates: Gate 1 (AI reads the request, plans the document — outline, scope, output format, sources) → APPROVED → Gate 2 runs immediately (generates the document, self-reviews, writes a task summary, then auto-closes the task — no Gate 3/4/5).
|
|
24
|
+
- `scripts/detect.js` — new keyword set (`gen-doc`, `generate doc`, `tạo tài liệu`, `flow document`, …) so free-text task descriptions can auto-detect this type.
|
|
25
|
+
- `scripts/use.js` — new picker entry "📝 Generate Doc 2 Gate" in the manual task-type selector.
|
|
26
|
+
- `scripts/task.js` — `gen-doc` capped at `maxGate = 2`; gate labels ("Generate Document" / "Done") and gate-history summary generation updated accordingly.
|
|
27
|
+
- `scripts/prompt.js` (`ak prompt gen-doc`) and `scripts/hooks/session-start.js` (auto-start + fast-mode messages) — new gen-doc-specific instructions.
|
|
28
|
+
- `custom/templates/shared/gate-workflow.md` — new `## gen-doc Task Type — 2-Gate Flow` section; `custom/rules/project-conventions.md` — new gen-doc output-path table; `docs/common/AIFLOW.md` / `cli-reference.md` — documented the flow and the new `ak p gen-doc` prompt type.
|
|
29
|
+
- Initial output paths were `plan/[ticket-id]/requirement.md` / `output.md` / `task-summary.md` — since relocated to `AK-Docs/04.Coding/`, see **Changed** below.
|
|
30
|
+
- **Figma design integrated into the DEV Gate workflow (Gate 1→4)** (`!32 feature/figma-gate-workflow`). UI tickets now get Figma wired in automatically, no manual skill invocation:
|
|
31
|
+
- **Gate 1** (`read-study-requirement`) detects a Figma URL (ticket description / `supplementaryContext[]` / asks DEV once), fetches the design **exactly once** via Figma MCP, and caches the shared artifact `design/` (`design-context.md` with layout/tokens/component list/image map, `nodes.json` raw cache, `images/`, `figma-manifest.json`).
|
|
32
|
+
- **Gate 2** (`generate-spec`) reads the cached `design-context.md` (no second MCP call) and folds the component tree, design-token mapping, and an image-copy task into the TDD plan.
|
|
33
|
+
- **Gate 3** (`figma-to-component`, new **Gate mode**) reuses the cached `nodes.json`, copies images to `public/assets/figma/`, and generates the components — again without re-calling MCP.
|
|
34
|
+
- **Gate 4** (`review-plan`) adds a **Design Conformance Check**: compares the built UI against `design-context.md` region-by-region, against the actual rendered reference image, not just a text checklist (no-fabricate / no-omit / layout / color / typography / every component and image present).
|
|
35
|
+
- Refresh: typing "reload figma" re-fetches at Gate 1 and overwrites the cached `design/` artifact.
|
|
36
|
+
- **New anti-429 guard** — `scripts/hooks/figma-rate-limit.js`, a `PreToolUse` hook (installed by `scripts/init.js`) that throttles every `mcp__figma__*` call to 6 REST-call-units/60s (tunable via `FIGMA_RATE_LIMIT_UNITS`; `download_figma_images` counts as 3 units). It **sleeps rather than fails**, so calls are delayed, never dropped — needed because Figma rate-limits `get_figma_data`/image renders by **seat tier**, and a View/Collab-seat token gets only ~6 Tier-1 calls/**month** (effectively unusable) vs 10–20/min for a Dev/Full seat.
|
|
37
|
+
- `docs/common/workflows/figma.md` — documents the Gate-by-Gate table above, the shared `design/` artifact layout, and a new "Rate limits (429)" section (seat-tier budget table, `Retry-After` triage, troubleshooting entry for immediate 429s).
|
|
38
|
+
- **`ak docs branch` / `ak docs submit`** — new CLI commands (`scripts/docs-branch.js`) so the AI can help create a `feature/<functionId>/<taskId>` branch in `AK-Docs`/`Shared-Docs` from `main` (`ak docs branch`) and commit + push + open a Merge Request (`ak docs submit --title ... --description ...`), auto-detecting GitLab/GitHub via the remote URL to run `glab mr create` / `gh pr create`, or falling back to a pre-filled manual MR link if neither CLI is installed. Both commands print the full plan and only perform any git write action when passed `--yes` — which the AI may only add after the developer has explicitly confirmed the plan in chat (otherwise, or with no interactive TTY, it's a no-op dry run). This keeps the same "AI never commits/pushes unasked" guarantee as the existing `block-git-write` hook, extended to a capability that hook can't cover.
|
|
39
|
+
- **`docs/internal/Docs-Management-Flow.md`** — new internal doc formalizing the `AK-Docs` branch/MR workflow for every role: `main` is protected, PM-reviewed-and-merged only; every other role (BA/Dev/QA/TL) updates docs on a `feature/<functionId>/<taskId>` branch created from `main`; a step-by-step table makes explicit which steps are self-review (the authoring role) vs. the single mandatory PM-review-and-merge gate. Generalizes the branch/MR model `Memory-Architecture-v1.0.md` §5.1 designed for `99.Memory/` to all of `AK-Docs`.
|
|
40
|
+
|
|
12
41
|
### Fixed
|
|
13
42
|
|
|
14
43
|
- **`aiflow prompt <type>` still emitted `plan/[ticket-id]/...` paths** — The 0.1.5 migration to `04.Coding/` (see below) updated `gate-workflow.md`, the skills, all 5 tool templates, and `session-start.js`, but missed `scripts/prompt.js`. Its `PROMPT_TEMPLATES` (used by `aiflow prompt feature|bug-fix|refactor|investigation|impact-analysis|testing|documentation` to build the copy-paste prompt for Cursor/Gemini/manual use) still hard-coded `Output plan/[ticket-id]/requirement.md` / `plan.md` / `summary.md`, contradicting the `custom/rules/project-conventions.md` override appended later in the same prompt — this was the root cause of `/plan` still appearing at the project root even on 0.1.5-beta.1. All 11 occurrences now point at the current `04.Coding/<section>/[functionId]/[ticketId].md` convention.
|
|
@@ -16,6 +45,9 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
16
45
|
|
|
17
46
|
### Changed
|
|
18
47
|
|
|
48
|
+
- **`gen-doc` and ML workflow output relocated from `plan/[ticket-id]/` to `AK-Docs/04.Coding/`.** Both `gen-doc` (2-gate, added this version — see **Added** above) and the ML 5-gate workflow (`ml-gate-workflow.md`, pre-existing) still wrote to the legacy `plan/[ticket-id]/requirement.md` / `output.md` / `task-summary.md` / `ml-problem.md` / `experiment-plan.md` / `eval-report.md` paths that the 0.1.5 Dev-workflow migration deliberately left untouched. Per user decision, neither gets its own AK-Docs section — both now reuse the **same** `01.Requirements/02.Plans/03.TDD-Notes/04.Reviews` folders as the standard Dev workflow (gen-doc only ever populates the first two; ML uses all it needs through Gate 4, folding the model card into the Gate 4 review doc). Both workflows now also require the same mandatory `functionId` confirmation at Gate 1 that DEV/BA/QA already had, which neither had before. Updated: `custom/templates/shared/gate-workflow.md` (gen-doc section) and `ml-gate-workflow.md`, `custom/rules/project-conventions.md` and `ml-conventions.md` (output-path tables + legacy-path deprecation notice extended to cover these), `scripts/prompt.js` (gen-doc template) and `scripts/hooks/session-start.js` (gen-doc fast-mode text), and 4 ML skills (`design-experiment`, `evaluate-model`, `explore-data`, `frame-ml-problem`) whose completion checklists still hard-coded the old paths independent of the rules-file override.
|
|
49
|
+
- **Branch/MR hooks wired into every workflow that writes to `AK-Docs`.** `create-spec-workflow.md` (BA), `create-testcase-workflow.md` (QA), `gate-workflow.md` (DEV + gen-doc), and `ml-gate-workflow.md` (ML) each now: (a) confirm `functionId`/`taskId` and create/checkout the `AK-Docs` branch `feature/[functionId]/[taskId]` **before** writing the first gate output (new Bước 0.5, right after the existing functionId pre-flight), and (b) at the final gate, submit that branch via `ak docs submit` — with the commit/MR title+description shown and explicitly confirmed by the user first — instead of the previous vague "lưu tài liệu lên remote (GitLab)" instruction. Every step is explicit that PM reviews and merges the MR; the authoring role never merges it themselves.
|
|
50
|
+
|
|
19
51
|
- **DEV workflow output relocated to `AK-Docs/04.Coding/`** — Per `docs/internal/Project-Structure.md` / `Coding-Structure.md`, `04.Coding/` (and its BA/QA siblings `02.BA-Specs/`, `03.Testing/`, `00.Project-Overview/`) belong under the `AK-Docs/` docs repo (a sibling of the source repo, synced via `scripts/docs-repo.js`), not directly at the source repo root. Updated all references across `CLAUDE.md`, `custom/rules/project-conventions.md`, `custom/templates/shared/gate-workflow.md`, the 5 tool templates, the DEV skills (`read-study-requirement`, `generate-spec`, `review-plan`, `gate-review`), `docs/common/AIFLOW.md` / `ai-integration.md` / `workflows/{bug-fix,feature}.md`, `scripts/hooks/session-start.js`, and `scripts/prompt.js`. `scripts/task.js` now resolves the coding dir via `resolveDocsRepoPath(PROJECT_DIR, 'AK-Docs')` (from `docs-repo.js`) instead of hard-coding the path, so `findTaskDocs()` / `detectCurrentGate()` look under `AK-Docs/04.Coding/` first, with the legacy `plan/<taskId>/` folder (at the source repo root) still checked as a fallback.
|
|
20
52
|
|
|
21
53
|
## [0.1.5] - 2026-07-01
|
|
@@ -262,6 +262,7 @@ ak t n -t PROJ-33 # specify ticket (--ticket)
|
|
|
262
262
|
| `plan/<id>/requirement.md` | Gate 2 |
|
|
263
263
|
| `plan/<id>/plan.md` | Gate 3 |
|
|
264
264
|
| `plan/<id>/summary.md` | Gate 5 |
|
|
265
|
+
| `plan/<id>/output.md` _(gen-doc)_ | Done (Gate 2 complete) |
|
|
265
266
|
|
|
266
267
|
**Example workflow:**
|
|
267
268
|
```bash
|
|
@@ -316,6 +317,7 @@ ak p feature # generate feature prompt
|
|
|
316
317
|
ak p investigation # generate investigation prompt
|
|
317
318
|
ak p refactor # generate refactor prompt
|
|
318
319
|
ak p impact # generate impact analysis prompt
|
|
320
|
+
ak p gen-doc # generate document generation prompt (2-gate)
|
|
319
321
|
ak p -l # list all prompt types (--list)
|
|
320
322
|
ak p bug-fix -o p.md # save to file (--output)
|
|
321
323
|
ak p bug-fix -L vietnamese # Vietnamese language (--lang)
|
|
@@ -323,7 +325,7 @@ ak p bug-fix -d comprehensive # comprehensive detail (--detail)
|
|
|
323
325
|
```
|
|
324
326
|
|
|
325
327
|
**Options:**
|
|
326
|
-
- `<type>` — Prompt type (bug-fix, feature, investigation, refactor, impact, docs)
|
|
328
|
+
- `<type>` — Prompt type (bug-fix, feature, investigation, refactor, impact, docs, gen-doc)
|
|
327
329
|
- `-l, --list` — List available prompt types
|
|
328
330
|
- `-o, --output <file>` — Save to file instead of printing
|
|
329
331
|
- `-L, --lang <lang>` — Language (english, vietnamese)
|
|
@@ -1,105 +1,176 @@
|
|
|
1
|
-
# Figma → Component Workflow
|
|
2
|
-
|
|
3
|
-
Generate UI components directly from Figma designs using the `figma-to-component` skill.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Prerequisites
|
|
8
|
-
|
|
9
|
-
Choose one adapter based on your setup:
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
1.
|
|
78
|
-
2.
|
|
79
|
-
3.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
1
|
+
# Figma → Component Workflow
|
|
2
|
+
|
|
3
|
+
Generate UI components directly from Figma designs using the `figma-to-component` skill.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
Choose one adapter based on your setup:
|
|
10
|
+
|
|
11
|
+
> **Which adapter?** The REST API is rate-limited **per seat** (see [Rate limits](#rate-limits-429)
|
|
12
|
+
> below): a View/Collab seat gets only ~6 heavy calls per MONTH — effectively unusable.
|
|
13
|
+
> - Token from a **Dev/Full seat** → Option A (REST) works fine with the batched strategy.
|
|
14
|
+
> - Only a View/Collab seat, or design-heavy work (many frames, art-heavy exports) →
|
|
15
|
+
> prefer **Option B (Figma Desktop)** — it uses the local app session and consumes no REST quota.
|
|
16
|
+
|
|
17
|
+
### Option A — REST API (no Desktop required; needs a Dev/Full-seat token)
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
ak init -a figma
|
|
21
|
+
# Prompts for FIGMA_API_TOKEN
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Get your token: [figma.com/settings](https://www.figma.com/settings) → Security → Personal access tokens → Create new token (File content: read). Create it from an account with a **Dev or Full seat** — a View/Collab seat's token is capped at ~6 Tier 1 calls/month.
|
|
25
|
+
|
|
26
|
+
### Option B — Official Figma MCP (requires Figma Desktop app)
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
ak init -a figma-desktop
|
|
30
|
+
# No token needed — uses Desktop app session
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Requirements: Figma Desktop app installed and the target file open before invoking the skill.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Getting a Figma Node URL
|
|
38
|
+
|
|
39
|
+
1. Open your file in Figma (browser or Desktop)
|
|
40
|
+
2. Right-click on a frame or component → **Copy link to selection**
|
|
41
|
+
3. The URL looks like:
|
|
42
|
+
```
|
|
43
|
+
https://www.figma.com/design/FILEKEY/FileName?node-id=123-456
|
|
44
|
+
```
|
|
45
|
+
- `FILEKEY` — the file identifier (between `/design/` and the next `/`)
|
|
46
|
+
- `node-id=123-456` — the specific frame or component to generate
|
|
47
|
+
|
|
48
|
+
You can also link to the entire file (no `node-id`) to let the skill list available frames.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Trigger the Skill
|
|
53
|
+
|
|
54
|
+
Paste the Figma URL into Claude Code with a generation request:
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
Generate component from this Figma frame:
|
|
58
|
+
https://www.figma.com/design/XXXXX/App?node-id=123-456
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
Implement the UserCard UI from Figma:
|
|
63
|
+
https://www.figma.com/design/XXXXX/App?node-id=78-910
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
Read Figma and generate component following my project conventions:
|
|
68
|
+
https://www.figma.com/design/XXXXX/App?node-id=42-0
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## What to Expect
|
|
74
|
+
|
|
75
|
+
The skill outputs in this order:
|
|
76
|
+
|
|
77
|
+
1. **Design Summary** — layout structure, color palette, typography scale used in the design
|
|
78
|
+
2. **Component file** — complete code for your detected framework (React, Next.js App Router, Vue 3, or Angular)
|
|
79
|
+
3. **Usage example** — how to import and use the component
|
|
80
|
+
4. **Notes** — anything that couldn't be mapped 1:1 from Figma (custom fonts, unsupported effects, etc.)
|
|
81
|
+
|
|
82
|
+
**Framework auto-detection order:**
|
|
83
|
+
1. Read `CLAUDE.md` for framework identifier (`nextjs-app-router`, `reactjs`, `vue-nuxt`, `angular`)
|
|
84
|
+
2. Scan project files (`app/` dir → App Router, `angular.json` → Angular, `nuxt.config.*` → Vue)
|
|
85
|
+
3. Fallback → Next.js / React
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Figma trong Gate workflow
|
|
90
|
+
|
|
91
|
+
Khi làm việc theo quy trình 5 Gate (xem `CLAUDE.md`) với ticket liên quan UI, Figma được
|
|
92
|
+
nối tự động — không cần gọi skill thủ công:
|
|
93
|
+
|
|
94
|
+
| Gate | Vai trò Figma |
|
|
95
|
+
|------|----------------|
|
|
96
|
+
| **1** | `read-study-requirement` dò Figma URL trong ticket (hoặc hỏi DEV một lần), đọc design **một lần** qua MCP, lưu artifact `plan/[ticket-id]/design/` (design-context.md + images/ + nodes.json). |
|
|
97
|
+
| **2** | `generate-spec` đọc `design-context.md` → đưa component tree, design tokens, danh sách ảnh vào plan TDD. |
|
|
98
|
+
| **3** | `figma-to-component` chạy ở **Gate mode**: dùng lại cache `nodes.json`, copy ảnh sang `public/assets/figma/`, sinh component (không gọi lại MCP). |
|
|
99
|
+
| **4** | `review-plan` đối chiếu UI đã code với `design-context.md`. |
|
|
100
|
+
|
|
101
|
+
**Artifact dùng chung** — nguồn sự thật cho mọi Gate sau Gate 1:
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
plan/[ticket-id]/design/
|
|
105
|
+
├── design-context.md # layout, tokens, component list, image map
|
|
106
|
+
├── images/ # ảnh export từ Figma
|
|
107
|
+
└── nodes.json # raw get_figma_data (cache)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Refresh khi design đổi:** gõ "reload figma" → Gate 1 fetch lại, ghi đè `design/`, cập nhật `Fetched`.
|
|
111
|
+
|
|
112
|
+
**Ticket không liên quan UI** hoặc không có Figma URL → toàn bộ nhánh Figma được bỏ qua.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Review Checklist
|
|
117
|
+
|
|
118
|
+
After the component is generated, verify:
|
|
119
|
+
|
|
120
|
+
- [ ] Layout matches Figma (flex direction, alignment, gap)
|
|
121
|
+
- [ ] Colors are correct (Tailwind class or arbitrary value)
|
|
122
|
+
- [ ] Typography matches (size, weight, line-height)
|
|
123
|
+
- [ ] Spacing correct (padding, margin, gap)
|
|
124
|
+
- [ ] Interactive states handled (`'use client'` in App Router, variants in React/Vue/Angular)
|
|
125
|
+
- [ ] Images use correct component (`next/image` for Next.js, `<img>` for others)
|
|
126
|
+
- [ ] `className` / `class` prop exposed for external overrides
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Rate limits (429)
|
|
131
|
+
|
|
132
|
+
Figma rate-limits the REST API by **endpoint tier × plan × seat** (since 2025-11-17).
|
|
133
|
+
`get_figma_data` (file/nodes) and image renders share the same **Tier 1** bucket:
|
|
134
|
+
|
|
135
|
+
| Seat of the token owner | Tier 1 budget (file / nodes / render images) |
|
|
136
|
+
|---|---|
|
|
137
|
+
| View / Collab | **~6 calls / MONTH** (any plan) |
|
|
138
|
+
| Dev / Full — Starter | 10 / min |
|
|
139
|
+
| Dev / Full — Professional | 15 / min |
|
|
140
|
+
| Dev / Full — Organization+ | 20 / min |
|
|
141
|
+
|
|
142
|
+
The fills-map endpoint (`/v1/files/:key/images`) is Tier 2 (25-100/min) — the skill prefers it.
|
|
143
|
+
Downloading image bytes from the returned S3 URLs costs nothing.
|
|
144
|
+
|
|
145
|
+
> **Measured on a free personal (Starter) account, 2026-07:** the API returned
|
|
146
|
+
> `x-figma-rate-limit-type: low` with `retry-after ≈ 4 days` after only ~4 Tier 1 calls —
|
|
147
|
+
> i.e. free accounts behave like the View/Collab monthly bucket, NOT 10/min. On a free
|
|
148
|
+
> account, budget ONE batched fetch per design and cache everything; for anything heavier
|
|
149
|
+
> use `figma-desktop` or a paid Dev/Full seat.
|
|
150
|
+
|
|
151
|
+
**Client-side guard:** `ak init` installs a PreToolUse hook
|
|
152
|
+
(`.claude/hooks/figma-rate-limit.js`) that throttles every `mcp__figma__*` call to
|
|
153
|
+
6 REST-call units per 60s (safe under the 10/min Starter budget; `download_figma_images`
|
|
154
|
+
counts as 3 units — it makes up to 3 REST calls internally). It sleeps instead of
|
|
155
|
+
failing, so calls are delayed — never dropped. Tune with `FIGMA_RATE_LIMIT_UNITS`
|
|
156
|
+
(e.g. `15` on a Professional plan).
|
|
157
|
+
|
|
158
|
+
**When you hit a 429, read the `Retry-After` header:**
|
|
159
|
+
|
|
160
|
+
- **≤ 60s** → per-minute bucket. Wait and retry once — this is normal on art-heavy frames.
|
|
161
|
+
- **Hours/days, or `X-Figma-Rate-Limit-Type: low`** → per-seat quota. A new token on the same
|
|
162
|
+
account will NOT help. Fix: use a token from a Dev/Full seat, or switch to
|
|
163
|
+
`ak init -a figma-desktop`.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Troubleshooting
|
|
168
|
+
|
|
169
|
+
| Problem | Fix |
|
|
170
|
+
|---|---|
|
|
171
|
+
| `get_figma_data` tool not found | Run `ak init -a figma` to configure the MCP |
|
|
172
|
+
| "Invalid API token" | Token expired — create a new one at figma.com/settings |
|
|
173
|
+
| `429` immediately / every session | Token from a View/Collab seat (~6 calls/month) — see [Rate limits](#rate-limits-429) |
|
|
174
|
+
| Wrong node generated | Check the `node-id` in the URL matches your selection in Figma |
|
|
175
|
+
| Figma Desktop MCP not responding | Ensure Figma Desktop app is open with the file loaded |
|
|
176
|
+
| Component uses wrong framework | Add framework to `CLAUDE.md` (e.g. `nextjs-app-router`) |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@relipa/ai-flow-kit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
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
|
+
}
|
|
@@ -24,6 +24,25 @@ function norm(cmd) {
|
|
|
24
24
|
return ALIAS_MAP[c] || c;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
// ── Version helpers ─────────────────────────────────────────────────────────
|
|
28
|
+
const CURRENT_VERSION = '0.1.4';
|
|
29
|
+
|
|
30
|
+
function parseVersion(v) {
|
|
31
|
+
if (!v || v === '-' || v === '' || v.toLowerCase() === 'unknown') return null;
|
|
32
|
+
const core = v.split('-')[0]; // strip prerelease suffix, e.g. 0.1.5-beta.1 -> 0.1.5
|
|
33
|
+
const parts = core.split('.').map(n => parseInt(n, 10));
|
|
34
|
+
if (parts.some(n => Number.isNaN(n))) return null;
|
|
35
|
+
return parts;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function compareVersion(a, b) {
|
|
39
|
+
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
40
|
+
const diff = (a[i] || 0) - (b[i] || 0);
|
|
41
|
+
if (diff !== 0) return diff;
|
|
42
|
+
}
|
|
43
|
+
return 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
27
46
|
// ── Parse log file ─────────────────────────────────────────────────────────
|
|
28
47
|
function parseLog(filePath) {
|
|
29
48
|
const lines = fs.readFileSync(filePath, 'utf-8').split(/\r?\n/);
|
|
@@ -39,17 +58,19 @@ function parseLog(filePath) {
|
|
|
39
58
|
if (!user || user === '-' || user === '') continue;
|
|
40
59
|
|
|
41
60
|
const timestamp = cols[0] || '';
|
|
61
|
+
const version = cols[5] || '';
|
|
42
62
|
const command = cols[6] || '';
|
|
43
63
|
const ticket = cols[9] || '';
|
|
44
64
|
|
|
45
65
|
const date = timestamp.split(' ')[0]; // dd/MM/yyyy
|
|
46
66
|
|
|
47
67
|
if (!users[user]) {
|
|
48
|
-
users[user] = { email: user, days: new Set(), events: 0, rawCmds: new Set(), gateCmds: [] };
|
|
68
|
+
users[user] = { email: user, days: new Set(), events: 0, rawCmds: new Set(), gateCmds: [], versions: new Set() };
|
|
49
69
|
}
|
|
50
70
|
const u = users[user];
|
|
51
71
|
u.events++;
|
|
52
72
|
if (date) u.days.add(date);
|
|
73
|
+
if (version.trim()) u.versions.add(version.trim());
|
|
53
74
|
|
|
54
75
|
const cmd = norm(command);
|
|
55
76
|
if (cmd) u.rawCmds.add(cmd);
|
|
@@ -130,6 +151,19 @@ const SCORE_LABELS = [
|
|
|
130
151
|
'Sử dụng thường xuyên workflow',
|
|
131
152
|
];
|
|
132
153
|
|
|
154
|
+
// ── Version status for a user ──────────────────────────────────────────────
|
|
155
|
+
function getVersionStatus(u) {
|
|
156
|
+
const all = [...u.versions];
|
|
157
|
+
const parsed = all.map(v => ({ raw: v, parts: parseVersion(v) })).filter(v => v.parts);
|
|
158
|
+
if (parsed.length === 0) {
|
|
159
|
+
return { status: all.length ? 'unknown' : 'none', latest: null, all };
|
|
160
|
+
}
|
|
161
|
+
parsed.sort((a, b) => compareVersion(a.parts, b.parts));
|
|
162
|
+
const latest = parsed[parsed.length - 1];
|
|
163
|
+
const isOutdated = compareVersion(latest.parts, parseVersion(CURRENT_VERSION)) < 0;
|
|
164
|
+
return { status: isOutdated ? 'outdated' : 'ok', latest: latest.raw, all };
|
|
165
|
+
}
|
|
166
|
+
|
|
133
167
|
// ── Build summary row for each user ───────────────────────────────────────
|
|
134
168
|
function buildRow(email, u) {
|
|
135
169
|
const { events, days, rawCmds, gateCmds } = u;
|
|
@@ -198,12 +232,12 @@ function buildRow(email, u) {
|
|
|
198
232
|
impactLabel: '',
|
|
199
233
|
totalScore: score,
|
|
200
234
|
notes,
|
|
235
|
+
versionStatus: getVersionStatus(u),
|
|
201
236
|
};
|
|
202
237
|
}
|
|
203
238
|
|
|
204
239
|
// ── Excel writer ───────────────────────────────────────────────────────────
|
|
205
|
-
|
|
206
|
-
const workbook = new ExcelJS.Workbook();
|
|
240
|
+
function writeExcel(rows, workbook, sheetLabel, fromLabel, toLabel) {
|
|
207
241
|
const sheet = workbook.addWorksheet(`Score ${sheetLabel}`);
|
|
208
242
|
|
|
209
243
|
sheet.columns = [
|
|
@@ -247,20 +281,23 @@ async function writeExcel(rows, outputPath, sheetLabel, fromLabel, toLabel) {
|
|
|
247
281
|
const hRow = sheet.addRow(HEADERS);
|
|
248
282
|
hRow.height = 36;
|
|
249
283
|
hRow.eachCell(cell => {
|
|
250
|
-
cell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: '
|
|
284
|
+
cell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF4F81BD' } };
|
|
251
285
|
cell.font = { bold: true, color: { argb: 'FFFFFFFF' }, size: 11 };
|
|
252
286
|
cell.alignment = { vertical: 'middle', horizontal: 'center', wrapText: true };
|
|
253
287
|
cell.border = border;
|
|
254
288
|
});
|
|
255
289
|
|
|
256
|
-
// Score → row color
|
|
290
|
+
// Score → row color (matches requirements/GetPoints-format.png)
|
|
257
291
|
const COLORS = {
|
|
258
|
-
0: '
|
|
259
|
-
1: '
|
|
260
|
-
2: '
|
|
261
|
-
3: '
|
|
262
|
-
4: '
|
|
263
|
-
5: '
|
|
292
|
+
0: 'FFFFFFFF',
|
|
293
|
+
1: 'FFF5F8EE',
|
|
294
|
+
2: 'FFEBF1DE',
|
|
295
|
+
3: 'FFD8E4BC',
|
|
296
|
+
4: 'FFC4D79B',
|
|
297
|
+
5: 'FF76933C',
|
|
298
|
+
};
|
|
299
|
+
const FONT_COLORS = {
|
|
300
|
+
0: 'FFFF0000',
|
|
264
301
|
};
|
|
265
302
|
|
|
266
303
|
rows.forEach((row, idx) => {
|
|
@@ -274,9 +311,11 @@ async function writeExcel(rows, outputPath, sheetLabel, fromLabel, toLabel) {
|
|
|
274
311
|
r.height = 20;
|
|
275
312
|
|
|
276
313
|
const fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: COLORS[row.depthScore] || 'FFFFFFFF' } };
|
|
314
|
+
const fontColor = FONT_COLORS[row.depthScore] || 'FF000000';
|
|
277
315
|
r.eachCell({ includeEmpty: true }, (cell, col) => {
|
|
278
316
|
cell.border = border;
|
|
279
317
|
cell.fill = fill;
|
|
318
|
+
cell.font = { color: { argb: fontColor } };
|
|
280
319
|
cell.alignment = (col <= 4 || (col >= 9 && col <= 13))
|
|
281
320
|
? { vertical: 'middle', horizontal: 'center' }
|
|
282
321
|
: { vertical: 'middle', horizontal: 'left', wrapText: false };
|
|
@@ -289,8 +328,81 @@ async function writeExcel(rows, outputPath, sheetLabel, fromLabel, toLabel) {
|
|
|
289
328
|
sheet.views = [{ state: 'frozen', ySplit: 2 }];
|
|
290
329
|
sheet.autoFilter = { from: 'A2', to: 'N2' };
|
|
291
330
|
|
|
331
|
+
return sheet;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// ── Version warning sheet ───────────────────────────────────────────────────
|
|
335
|
+
function writeVersionSheet(workbook, rows) {
|
|
336
|
+
const sheet = workbook.addWorksheet('Version Warning');
|
|
337
|
+
sheet.columns = [
|
|
338
|
+
{ key: 'no', width: 5 },
|
|
339
|
+
{ key: 'email', width: 30 },
|
|
340
|
+
{ key: 'status', width: 16 },
|
|
341
|
+
{ key: 'latest', width: 16 },
|
|
342
|
+
{ key: 'allVersions', width: 30 },
|
|
343
|
+
{ key: 'note', width: 50 },
|
|
344
|
+
];
|
|
345
|
+
|
|
346
|
+
const border = {
|
|
347
|
+
top: { style: 'thin' }, left: { style: 'thin' },
|
|
348
|
+
bottom: { style: 'thin' }, right: { style: 'thin' },
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
sheet.addRow([`Cảnh báo phiên bản — Yêu cầu phiên bản hiện tại: ${CURRENT_VERSION}`]);
|
|
352
|
+
sheet.mergeCells('A1:F1');
|
|
353
|
+
const title = sheet.getCell('A1');
|
|
354
|
+
title.font = { bold: true, size: 13, color: { argb: 'FF1F3864' } };
|
|
355
|
+
title.alignment = { vertical: 'middle', horizontal: 'center' };
|
|
356
|
+
title.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFD6DCE4' } };
|
|
357
|
+
sheet.getRow(1).height = 28;
|
|
358
|
+
|
|
359
|
+
const HEADERS = ['#', 'Email', 'Trạng thái', 'Phiên bản mới nhất', 'Các phiên bản dùng trong tuần', 'Ghi chú'];
|
|
360
|
+
const hRow = sheet.addRow(HEADERS);
|
|
361
|
+
hRow.height = 20;
|
|
362
|
+
hRow.eachCell(cell => {
|
|
363
|
+
cell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF4F81BD' } };
|
|
364
|
+
cell.font = { bold: true, color: { argb: 'FFFFFFFF' }, size: 11 };
|
|
365
|
+
cell.alignment = { vertical: 'middle', horizontal: 'center', wrapText: true };
|
|
366
|
+
cell.border = border;
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
const warnings = rows.filter(row => row.versionStatus.status === 'outdated' || row.versionStatus.status === 'unknown');
|
|
370
|
+
|
|
371
|
+
const STATUS_LABEL = { outdated: 'CŨ HƠN', unknown: 'KHÔNG XÁC ĐỊNH', ok: 'OK', none: 'Không có dữ liệu' };
|
|
372
|
+
const STATUS_COLOR = { outdated: 'FFFFC7CE', unknown: 'FFFFEB9C' };
|
|
373
|
+
|
|
374
|
+
warnings.forEach((row, idx) => {
|
|
375
|
+
const vs = row.versionStatus;
|
|
376
|
+
const note = vs.status === 'outdated'
|
|
377
|
+
? `Đang dùng ${vs.latest}, chưa cập nhật lên ${CURRENT_VERSION}`
|
|
378
|
+
: 'Không đọc được phiên bản (giá trị "-" hoặc "unknown") — cần kiểm tra lại';
|
|
379
|
+
const r = sheet.addRow([
|
|
380
|
+
idx + 1, row.email, STATUS_LABEL[vs.status], vs.latest || '-', vs.all.join(', ') || '-', note,
|
|
381
|
+
]);
|
|
382
|
+
r.height = 20;
|
|
383
|
+
const fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: STATUS_COLOR[vs.status] || 'FFFFFFFF' } };
|
|
384
|
+
r.eachCell({ includeEmpty: true }, (cell, col) => {
|
|
385
|
+
cell.border = border;
|
|
386
|
+
cell.fill = fill;
|
|
387
|
+
cell.alignment = (col <= 4)
|
|
388
|
+
? { vertical: 'middle', horizontal: 'center' }
|
|
389
|
+
: { vertical: 'middle', horizontal: 'left', wrapText: false };
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
sheet.views = [{ state: 'frozen', ySplit: 2 }];
|
|
394
|
+
sheet.autoFilter = { from: 'A2', to: 'F2' };
|
|
395
|
+
|
|
396
|
+
return warnings;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
async function writeExcelFile(rows, outputPath, sheetLabel, fromLabel, toLabel) {
|
|
400
|
+
const workbook = new ExcelJS.Workbook();
|
|
401
|
+
writeExcel(rows, workbook, sheetLabel, fromLabel, toLabel);
|
|
402
|
+
const warnings = writeVersionSheet(workbook, rows);
|
|
292
403
|
await workbook.xlsx.writeFile(outputPath);
|
|
293
|
-
console.log(`Written: ${outputPath} (${rows.length} users)`);
|
|
404
|
+
console.log(`Written: ${outputPath} (${rows.length} users, ${warnings.length} version warning(s))`);
|
|
405
|
+
return warnings;
|
|
294
406
|
}
|
|
295
407
|
|
|
296
408
|
// ── Canonical user list (fixed order) ─────────────────────────────────────
|
|
@@ -315,6 +427,10 @@ const CANONICAL_USERS = [
|
|
|
315
427
|
'anhnt@relipasoft.com',
|
|
316
428
|
'khanhdd@relipasoft.com',
|
|
317
429
|
'hieunv@relipasoft.com',
|
|
430
|
+
'cucdt@relipasoft.com',
|
|
431
|
+
'khanhnv@relipasoft.com',
|
|
432
|
+
'namhh@relipasoft.com',
|
|
433
|
+
'tannt@relipasoft.com',
|
|
318
434
|
];
|
|
319
435
|
|
|
320
436
|
// Log email → canonical email (for slight mismatches)
|
|
@@ -323,7 +439,7 @@ const LOG_EMAIL_ALIAS = {
|
|
|
323
439
|
};
|
|
324
440
|
|
|
325
441
|
const EMPTY_USER = (email) => ({
|
|
326
|
-
email, events: 0, days: new Set(), rawCmds: new Set(), gateCmds: [],
|
|
442
|
+
email, events: 0, days: new Set(), rawCmds: new Set(), gateCmds: [], versions: new Set(),
|
|
327
443
|
});
|
|
328
444
|
|
|
329
445
|
// ── Main ───────────────────────────────────────────────────────────────────
|
|
@@ -357,6 +473,7 @@ const EMPTY_USER = (email) => ({
|
|
|
357
473
|
u.events += data.events;
|
|
358
474
|
data.days.forEach(d => u.days.add(d));
|
|
359
475
|
data.rawCmds.forEach(c => u.rawCmds.add(c));
|
|
476
|
+
data.versions.forEach(v => u.versions.add(v));
|
|
360
477
|
u.gateCmds.push(...data.gateCmds);
|
|
361
478
|
}
|
|
362
479
|
}
|
|
@@ -367,5 +484,9 @@ const EMPTY_USER = (email) => ({
|
|
|
367
484
|
return buildRow(email, u);
|
|
368
485
|
});
|
|
369
486
|
|
|
370
|
-
await
|
|
487
|
+
const warnings = await writeExcelFile(rows, outputFile, sheetLabel, fromLabel, toLabel);
|
|
488
|
+
if (warnings.length) {
|
|
489
|
+
console.log(`\n⚠️ Version warnings (< ${CURRENT_VERSION} or unknown):`);
|
|
490
|
+
warnings.forEach(w => console.log(` - ${w.email}: ${w.versionStatus.latest || 'unknown'} (${w.versionStatus.status})`));
|
|
491
|
+
}
|
|
371
492
|
})();
|
package/scripts/detect.js
CHANGED
|
@@ -74,6 +74,17 @@ class TaskDetector {
|
|
|
74
74
|
antiKeywords: [],
|
|
75
75
|
icon: '🧪',
|
|
76
76
|
description: 'Writing tests and QA'
|
|
77
|
+
},
|
|
78
|
+
'gen-doc': {
|
|
79
|
+
keywords: [
|
|
80
|
+
'gen-doc', 'generate doc', 'generate document', 'tạo tài liệu',
|
|
81
|
+
'tài liệu flow', 'tài liệu logic', 'tài liệu điều tra',
|
|
82
|
+
'flow document', 'logic document', 'analysis document',
|
|
83
|
+
'source analysis', 'feature document', 'system document'
|
|
84
|
+
],
|
|
85
|
+
antiKeywords: ['code', 'fix', 'bug'],
|
|
86
|
+
icon: '📝',
|
|
87
|
+
description: 'Generate document from source code / feature analysis'
|
|
77
88
|
}
|
|
78
89
|
};
|
|
79
90
|
}
|