@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.
@@ -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
- ### Option A REST API (recommended, no Desktop required)
12
-
13
- ```bash
14
- ak init -a figma
15
- # Prompts for FIGMA_API_TOKEN
16
- ```
17
-
18
- Get your token: [figma.com/settings](https://www.figma.com/settings) → Security → Personal access tokens → Create new token (File content: read).
19
-
20
- ### Option B — Official Figma MCP (requires Figma Desktop app)
21
-
22
- ```bash
23
- ak init -a figma-desktop
24
- # No token neededuses Desktop app session
25
- ```
26
-
27
- Requirements: Figma Desktop app installed and the target file open before invoking the skill.
28
-
29
- ---
30
-
31
- ## Getting a Figma Node URL
32
-
33
- 1. Open your file in Figma (browser or Desktop)
34
- 2. Right-click on a frame or component → **Copy link to selection**
35
- 3. The URL looks like:
36
- ```
37
- https://www.figma.com/design/FILEKEY/FileName?node-id=123-456
38
- ```
39
- - `FILEKEY` the file identifier (between `/design/` and the next `/`)
40
- - `node-id=123-456` the specific frame or component to generate
41
-
42
- You can also link to the entire file (no `node-id`) to let the skill list available frames.
43
-
44
- ---
45
-
46
- ## Trigger the Skill
47
-
48
- Paste the Figma URL into Claude Code with a generation request:
49
-
50
- ```
51
- Generate component from this Figma frame:
52
- https://www.figma.com/design/XXXXX/App?node-id=123-456
53
- ```
54
-
55
- ```
56
- Implement the UserCard UI from Figma:
57
- https://www.figma.com/design/XXXXX/App?node-id=78-910
58
- ```
59
-
60
- ```
61
- Read Figma and generate component following my project conventions:
62
- https://www.figma.com/design/XXXXX/App?node-id=42-0
63
- ```
64
-
65
- ---
66
-
67
- ## What to Expect
68
-
69
- The skill outputs in this order:
70
-
71
- 1. **Design Summary** — layout structure, color palette, typography scale used in the design
72
- 2. **Component file** — complete code for your detected framework (React, Next.js App Router, Vue 3, or Angular)
73
- 3. **Usage example** — how to import and use the component
74
- 4. **Notes** — anything that couldn't be mapped 1:1 from Figma (custom fonts, unsupported effects, etc.)
75
-
76
- **Framework auto-detection order:**
77
- 1. Read `CLAUDE.md` for framework identifier (`nextjs-app-router`, `reactjs`, `vue-nuxt`, `angular`)
78
- 2. Scan project files (`app/` dir App Router, `angular.json` Angular, `nuxt.config.*` Vue)
79
- 3. Fallback Next.js / React
80
-
81
- ---
82
-
83
- ## Review Checklist
84
-
85
- After the component is generated, verify:
86
-
87
- - [ ] Layout matches Figma (flex direction, alignment, gap)
88
- - [ ] Colors are correct (Tailwind class or arbitrary value)
89
- - [ ] Typography matches (size, weight, line-height)
90
- - [ ] Spacing correct (padding, margin, gap)
91
- - [ ] Interactive states handled (`'use client'` in App Router, variants in React/Vue/Angular)
92
- - [ ] Images use correct component (`next/image` for Next.js, `<img>` for others)
93
- - [ ] `className` / `class` prop exposed for external overrides
94
-
95
- ---
96
-
97
- ## Troubleshooting
98
-
99
- | Problem | Fix |
100
- |---|---|
101
- | `get_figma_data` tool not found | Run `ak init -a figma` to configure the MCP |
102
- | "Invalid API token" | Token expired — create a new one at figma.com/settings |
103
- | Wrong node generated | Check the `node-id` in the URL matches your selection in Figma |
104
- | Figma Desktop MCP not responding | Ensure Figma Desktop app is open with the file loaded |
105
- | Component uses wrong framework | Add framework to `CLAUDE.md` (e.g. `nextjs-app-router`) |
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.5",
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
- async function writeExcel(rows, outputPath, sheetLabel, fromLabel, toLabel) {
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: 'FF4472C4' } };
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: 'FFFFC7CE',
259
- 1: 'FFFFEB9C',
260
- 2: 'FFFFFFCC',
261
- 3: 'FFD9EAD3',
262
- 4: 'FF93C47D',
263
- 5: 'FF6AA84F',
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 writeExcel(rows, outputFile, sheetLabel, fromLabel, toLabel);
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
  }