@relipa/ai-flow-kit 0.1.7 → 0.1.8

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.
@@ -0,0 +1,133 @@
1
+ # Project Folder Structure
2
+
3
+ > Cấu trúc thư mục chuẩn cho toàn dự án PILOT — áp dụng cho tất cả role: BA, Dev, Tester, PM/BrSE.
4
+
5
+ ---
6
+
7
+ ## Tổng quan
8
+
9
+ ```
10
+ {Root}/ ← Thư mục mở bằng VSCode, có thể chứa nhiều source repo
11
+ ├── {Source-Repo}/ ← Source code (GitLab repo)
12
+
13
+ ├── AK-Docs/ ← Tài liệu dự án (GitLab repo riêng)
14
+ │ ├── 00.Project-Overview/
15
+ │ ├── 01.QnA/
16
+ │ ├── 02.BA-Specs/
17
+ │ ├── 03.Testing/
18
+ │ └── 04.Coding/
19
+
20
+ └── Shared-Docs/ ← Template dùng chung cho TẤT CẢ dự án (GitLab repo riêng)
21
+ ├── Spec-Templates/
22
+ ├── QA-Templates/
23
+ ├── Testcase-Templates/
24
+ └── Test-Report-Templates/
25
+ ```
26
+
27
+ > **Tự động đồng bộ:** Mỗi lần chạy `ak init` hoặc `ak update`, tool sẽ tự kiểm tra `AK-Docs/` và `Shared-Docs/` cạnh source repo — nếu đã tồn tại (và là git repo) sẽ tự `git pull` để lấy bản mới nhất, nếu chưa có sẽ cảnh báo (warning) để bạn clone thủ công.
28
+
29
+ ---
30
+
31
+ ## Chi tiết — `AK-Docs/`
32
+
33
+ ```
34
+ AK-Docs/
35
+
36
+ ├── 00.Project-Overview/ ← PM / BrSE quản lý
37
+ │ ├── Project-Summary.md # Goals, scope, background, stack
38
+ │ ├── Function-List.md # F-ID | Feature | BA | Dev | Tester | Status
39
+ │ └── Team.md # Member, role, contact
40
+
41
+ ├── 01.QnA/ ← Trao đổi với khách hàng (có comtor)
42
+ │ └── QnA-Log.md # 1 file duy nhất cho toàn dự án
43
+
44
+ ├── 02.BA-Specs/ ← BA quản lý
45
+ │ ├── 00.Requirements/ # Requirements Gathering
46
+ │ ├── 01.Analysis/ # Gate 1: Business Analysis
47
+ │ ├── 02.QnA/ # Gate 2: Q&A nội bộ với stakeholder
48
+ │ ├── 03.UI-Prototypes/ # Gate 3: UI/UX Design
49
+ │ └── 04.UC-Specs/ # Gate 4: Final UC Specification
50
+
51
+ ├── 03.Testing/ ← Tester quản lý
52
+ │ ├── 00.Strategies/ # Test strategy & approach
53
+ │ ├── 01.Testcases/ # Gate 2: Test cases per feature
54
+ │ ├── 02.Reports/ # Gate 4: Execution & regression reports
55
+ │ ├── 03.Test-Data/ # Test data: CSV, JSON, SQL fixtures
56
+ │ ├── 04.Evidence/ # Gate 3: Screenshots, videos, logs
57
+ │ ├── 05.Scripts/ # Playwright automation scripts
58
+ │ ├── 06.Bugs/ # Gate 3: Bug reports
59
+ │ └── 07.AI-Artifacts/ # AI working docs: Gate 1-2 outputs
60
+
61
+ └── 04.Coding/ ← Dev quản lý
62
+ ├── 00.Overview/ # Tracker tổng: F-ID | Ticket | Dev | Gate | PR
63
+ ├── 01.Requirements/ # Gate 1: AI phân tích ticket + source code
64
+ ├── 02.Plans/ # Gate 2: TDD implementation plan
65
+ ├── 03.TDD-Notes/ # Gate 3: Test list viết trước + notes
66
+ ├── 04.Reviews/ # Gate 4: Self-review + impact analysis
67
+ └── 05.Pull-Requests/ # Gate 5: PR content + review checklist
68
+ ```
69
+
70
+ ---
71
+
72
+ ## Chi tiết — `Shared-Docs/`
73
+
74
+ > Repo dùng chung cho **TẤT CẢ dự án** — chỉ chứa template gốc, không chứa tài liệu của riêng dự án nào. Chỉ AI Testing Team mới có quyền chỉnh sửa.
75
+
76
+ | Thư mục | Nội dung |
77
+ |---|---|
78
+ | `Spec-Templates/` | Template UC Spec cho BA (Gate 1→4 của `create-spec` workflow) |
79
+ | `QA-Templates/` | Template chiến lược test, checklist review cho Tester |
80
+ | `Testcase-Templates/` | Template bộ Test Case (Gate 1→4 của `create-testcase` workflow) |
81
+ | `Test-Report-Templates/` | Template báo cáo thực thi test (execution report, regression report) |
82
+
83
+ Mỗi dự án khi cần tạo tài liệu mới (UC Spec, Test Case, Test Report...) sẽ copy template tương ứng từ `Shared-Docs/` về đúng thư mục trong `AK-Docs/` của dự án đó rồi điền nội dung.
84
+
85
+ ---
86
+
87
+ ## Quy tắc đặt tên
88
+
89
+ | Element | Convention | Ví dụ |
90
+ |---|---|---|
91
+ | Section folder | `{N}.{Pascal-Case}/` | `00.Requirements/`, `03.TDD-Notes/` |
92
+ | Feature folder | `F-{3-digit}_{Pascal-Case}/` | `F-001_User-Login/` |
93
+ | File có version | `{Pascal-Name}_v{N}.md` | `UC-Spec_v2.md` |
94
+ | File theo ticket | `TICKET-{ID}.md` | `TICKET-100.md` |
95
+ | File archive | `_archive/` subfolder | `04.UC-Specs/F-001/_archive/` |
96
+
97
+ ---
98
+
99
+ ## Ai lưu tài liệu ở đâu, khi nào
100
+
101
+ | Vai trò | Lưu vào | Thời điểm |
102
+ |---|---|---|
103
+ | PM / BrSE | `00.Project-Overview/` | Đầu dự án + mỗi sprint |
104
+ | BA / PM / Comtor | `01.QnA/QnA-Log.md` | Khi có câu hỏi từ khách hàng |
105
+ | BA | `02.BA-Specs/` | Sau khi Gate 4 APPROVED |
106
+ | Tester | `03.Testing/` | Sau khi Gate 4 APPROVED |
107
+ | Dev | `04.Coding/` | Trước khi tạo Pull Request |
108
+
109
+ ---
110
+
111
+ ## Luồng tài liệu qua từng vai trò
112
+
113
+ ```
114
+ PM tạo ticket (Backlog/Jira)
115
+
116
+
117
+ [BA] ak use TICKET-123
118
+ → 02.BA-Specs/ (Gate 1→4)
119
+ → Thông báo Dev + Tester sau Gate 4 APPROVED
120
+
121
+ ├──────────────────────────────────┐
122
+ ▼ ▼
123
+ [Tester] Extension sidebar [Dev] ak use TICKET-123
124
+ → 03.Testing/ (Gate 1→4) → 04.Coding/ (Gate 1→5)
125
+ │ │
126
+ └──────────────┬───────────────────┘
127
+
128
+ Pull Request
129
+ link: UC Spec + Test Case + Dev Plan
130
+
131
+
132
+ [PM] Gate 4 Tester → Go / No-Go
133
+ ```
@@ -0,0 +1,124 @@
1
+ # Testing — Folder Structure
2
+
3
+ > **Managed by:** Tester
4
+ > **Organized by:** Output Type → Function-ID (Scripts: Repo → Function-ID)
5
+ > **Naming:** Function-ID format `F-001_User-Login`, Evidence by `TICKET-ID_Date`
6
+ > **Note:** Templates are managed in a separate shared repo.
7
+
8
+ ---
9
+
10
+ ## Folder Tree
11
+
12
+ ```
13
+ 03.Testing/
14
+
15
+ ├── 00.Strategies/ # Test strategy & approach (project-level)
16
+ │ ├── Overview.md
17
+ │ ├── Regression-Strategy.md
18
+ │ └── Execution-Strategy.md
19
+
20
+ ├── 01.Testcases/ # Gate 2: Test cases per feature
21
+ │ └── F-001_User-Login/
22
+ │ ├── Testcase_v2.md # Current version
23
+ │ └── _archive/
24
+ │ └── Testcase_v1.md
25
+
26
+ ├── 02.Reports/ # Gate 4: Execution & regression reports
27
+ │ ├── F-001_User-Login/
28
+ │ │ ├── TICKET-100/
29
+ │ │ │ ├── Execution-Report.md
30
+ │ │ │ └── Bug-Summary.md
31
+ │ │ └── TICKET-100_Retest/
32
+ │ │ └── Execution-Report.md
33
+ │ └── Regression/ # Regression runs per sprint
34
+ │ └── Sprint-01_Regression.md
35
+
36
+ ├── 03.Test-Data/ # Test data: CSV, JSON, SQL fixtures
37
+ │ └── F-001_User-Login/
38
+ │ ├── Users.csv
39
+ │ └── Fixtures.sql
40
+
41
+ ├── 04.Evidence/ # Gate 3: Screenshots, videos, logs per execution
42
+ │ └── F-001_User-Login/
43
+ │ ├── TICKET-100_2026-06-15/ # Ticket + date to identify execution round
44
+ │ │ ├── TC_001_PASS/
45
+ │ │ │ └── Screenshot.png
46
+ │ │ └── TC_002_FAIL/
47
+ │ │ ├── Screenshot.png
48
+ │ │ └── Video.mp4
49
+ │ └── TICKET-100_Retest_2026-06-18/
50
+ │ └── TC_002_PASS/
51
+ │ └── Screenshot.png
52
+
53
+ ├── 05.Scripts/ # Playwright automation (by Repo → Feature)
54
+ │ ├── Repo1/ # e.g. frontend app
55
+ │ │ ├── playwright.config.ts # baseURL, browser config for this repo
56
+ │ │ └── F-001_User-Login/
57
+ │ │ └── Login.spec.ts
58
+ │ ├── Repo2/ # e.g. admin app
59
+ │ │ ├── playwright.config.ts
60
+ │ │ └── F-003_Admin-Panel/
61
+ │ │ └── Admin.spec.ts
62
+ │ └── Shared/ # Fixtures, helpers used across repos
63
+ │ ├── Fixtures.ts
64
+ │ └── Helpers.ts
65
+
66
+ ├── 06.Bugs/ # Gate 3: Bug reports (AI draft on TC fail)
67
+ │ └── F-001_User-Login/
68
+ │ └── BUG-001_Login-Timeout.md
69
+
70
+ └── 07.AI-Artifacts/ # AI working docs: Gate 1-2 outputs
71
+ └── F-001_User-Login/
72
+ ├── Gate1_Test-Analysis.md # Gate 1: scope, flows, risks
73
+ ├── Gate2_QnA.md # Gate 2: Q&A clarification (internal)
74
+ └── Gate2_Coverage-Matrix.md # Gate 2: coverage matrix
75
+ ```
76
+
77
+ ---
78
+
79
+ ## File Naming Convention
80
+
81
+ | Element | Convention | Example |
82
+ |---|---|---|
83
+ | Section folder | `{N}.{Pascal-Case}/` | `00.Strategies/`, `04.Evidence/` |
84
+ | Feature folder | `F-{3-digit}_{Pascal-Case}/` | `F-001_User-Login/` |
85
+ | Testcase file | `Testcase_v{N}.md` | `Testcase_v2.md` |
86
+ | Evidence folder | `TICKET-{ID}_{YYYY-MM-DD}/` | `TICKET-100_2026-06-15/` |
87
+ | Retest folder | `TICKET-{ID}_Retest_{YYYY-MM-DD}/` | `TICKET-100_Retest_2026-06-18/` |
88
+ | TC result folder | `TC_{ID}_{PASS\|FAIL}/` | `TC_001_PASS/` |
89
+ | Bug file | `BUG-{ID}_{Pascal-Description}.md` | `BUG-001_Login-Timeout.md` |
90
+ | Script config | `playwright.config.ts` | per Repo folder |
91
+ | Script file | `{Feature}.spec.ts` | `Login.spec.ts` |
92
+ | Archive folder | `_archive/` | inside feature folder |
93
+
94
+ ---
95
+
96
+ ## Gate → Folder Mapping
97
+
98
+ ### 1. Quy trình Tạo Test Case (`create-testcase`)
99
+
100
+ | Gate | Output | Folder |
101
+ |---|---|---|
102
+ | Gate 1 | Phân tích yêu cầu & Đánh giá rủi ro | `07.AI-Artifacts/[functionId]_01_Requirement_Analysis_Result.md` |
103
+ | Gate 2 | Xây dựng kịch bản (Scenarios) | `07.AI-Artifacts/[functionId]_02_Test_Scenarios_Result.md` |
104
+ | Gate 3 | Thiết kế Test Case chi tiết (Draft) | `07.AI-Artifacts/[functionId]_03_Test_Cases_Draft_Result.md` |
105
+ | Gate 4 | Review & Tối ưu hóa (TestCase chính thức) | `01.Testcases/[Feature]/[functionId]_TestCase.md` & `07.AI-Artifacts/[functionId]_04_Test_Cases_Final_Result.md` |
106
+
107
+ ### 2. Quy trình Thực thi Test (`execute-test`)
108
+
109
+ | Gate | Output | Folder |
110
+ |---|---|---|
111
+ | Gate 1 | Chuẩn bị & Lập kế hoạch thực thi | (Console output / Work Plan) |
112
+ | Gate 2 | Đồng bộ kịch bản kiểm thử (Script Sync) | `05.Scripts/[Repo]/[Feature]/[Feature].spec.ts` |
113
+ | Gate 3 | Thực thi test & Thu thập evidence | `04.Evidence/[Feature]/[Ticket]_[Date]/` & `06.Bugs/[Feature]/` |
114
+ | Gate 4 | Báo cáo test & Log bug | `02.Reports/[Feature]/[Ticket]/Execution-Report.md` |
115
+
116
+ ---
117
+
118
+ ## Rules
119
+
120
+ - Không tự sửa template — template nằm trong shared repo riêng
121
+ - Lưu testcase và kết quả lên GitLab **sau khi Gate 4 được APPROVED**
122
+ - Khi retest: tạo subfolder `TICKET-XXX_Retest_Date/` mới trong Evidence — không ghi đè cũ
123
+ - `05.Scripts/` chỉ chứa `.spec.ts` và `playwright.config.ts` — không chứa testcase `.md`
124
+ - `07.AI-Artifacts/` là working docs của AI — không phải tài liệu chính thức
@@ -24,15 +24,16 @@ ak <alias> --help # Also works with aliases
24
24
  | `ak guide` | `ak g` | `ak remove` | `ak rm` |
25
25
  | `ak update` | `ak up` | `ak sync-skills` | `ak sync` |
26
26
  | `ak doctor` | `ak dr` | `ak telemetry` | `ak tel` |
27
+ | `ak ask` | `ak ask` | | |
27
28
 
28
29
  | `task` sub-command | Short | `memory` sub-command | Short |
29
30
  |---|---|---|---|
30
- | `ak t status` | `ak t st` | `ak mem save` | `ak mem s` |
31
- | `ak t list` | `ak t ls` | `ak mem get` | `ak mem g` |
32
- | `ak t pause` | `ak t p` | `ak mem list` | `ak mem ls` |
33
- | `ak t switch` | `ak t sw` | `ak mem search` | `ak mem sr` |
34
- | `ak t resume` | `ak t r` | `ak mem delete` | `ak mem d` |
35
- | `ak t reset` | `ak t rst` | `ak mem clear` | `ak mem cl` |
31
+ | `ak t status` | `ak t st` | `ak mem draft` | `ak mem d` |
32
+ | `ak t list` | `ak t ls` | `ak mem list` | `ak mem ls` |
33
+ | `ak t pause` | `ak t p` | `ak mem submit` | `ak mem sb` |
34
+ | `ak t switch` | `ak t sw` | `ak mem remove` | `ak mem rm` |
35
+ | `ak t resume` | `ak t r` | | |
36
+ | `ak t reset` | `ak t rst` | | |
36
37
  | `ak t remove` | `ak t rm` | | |
37
38
  | `ak t next` | `ak t n` | | |
38
39
 
@@ -588,39 +589,53 @@ ak tel flush # force-send buffered events now
588
589
 
589
590
  ## memory (alias: `mem`)
590
591
 
591
- Manage team knowledge and context memory.
592
+ Manage the `99.Memory/` Project Brain — a git-based, human-approved team knowledge base living inside `AK-Docs/99.Memory/`. AI (or you) drafts a memory locally, then submits it as a Merge Request for a teammate/PM to approve. See `docs/common/Memory-Architecture-v1.0.md` for the full design.
593
+
594
+ > **Note:** This replaces the older `.aiflow/memory/` JSON store (`save/get/search/delete/clear`) retired in v0.1.7 — that command set no longer exists.
592
595
 
593
596
  ```bash
594
- ak mem s "payment-flow" "Process: 1. Validate, 2. Call Stripe, 3. Update DB" # save
595
- ak mem g "payment-flow" # retrieve (memory get)
596
- ak mem ls # list all (memory list)
597
- ak mem sr "validation" # search (memory search)
598
- ak mem d "old-memory" # delete (memory delete)
599
- ak mem cl # clear all (memory clear)
597
+ ak mem d -c 01.Lessons/dev -s prevent-429-error --content "Retry with backoff on 429 from payment gateway" -t payment,retry
598
+ ak mem ls # list local pending drafts (memory list)
599
+ ak mem ls -a # list team-approved memories (--approved)
600
+ ak mem sb 01.Lessons/dev/prevent-429-error.md -t "Add memory: prevent-429-error" # submit a draft (memory submit)
601
+ ak mem rm 01.Lessons/dev/prevent-429-error.md -t "Remove stale memory" --reason "no longer applies" # (memory remove)
600
602
  ```
601
603
 
602
604
  **Sub-commands (with aliases):**
603
- - `save|s <key> <value>`Save a note
604
- - `get|g <key>` — Read a note
605
- - `list|ls`List all notes
606
- - `search|sr <query>` — Search notes
607
- - `delete|d <key>` — Delete a note
608
- - `clear|cl`Clear all notes
605
+ - `draft|d --category <cat> --slug <slug> --content <text> [options]` Create a local memory draft in `_pending/` (not yet shared with the team)
606
+ - `-c, --category <cat>` — e.g. `00.Shared/architecture`, `01.Lessons/dev`, `02.Instincts/approved`
607
+ - `-s, --slug <slug>` short kebab-case name, e.g. `prevent-429-error`
608
+ - `--content <text>` — memory body (≤150 words, 1 fact)
609
+ - `-f, --function-id <id>` — functionId this memory is scoped to (omit for a global/cross-project fact)
610
+ - `--scope <scope>` alternative to `--function-id` for flat categories (glossary/decisions)
611
+ - `-t, --tags <list>` — comma-separated tags the scorer matches on
612
+ - `-w, --workflows <list>` — comma-separated workflows (coding, create-spec, create-testcase, gen-doc, all)
613
+ - `--source <text>` — ticket/Gate this was learned from
614
+ - `--confidence <n>` — AI self-rated 0.3–0.7 (default 0.5)
615
+ - `list|ls [-a, --approved]` — List local pending drafts, or team-approved memories with `--approved`
616
+ - `submit|sb <path> --title <title> [-d <desc>] [-y]` — Move a pending draft (path from `ak mem list`) to its destination in `AK-Docs/99.Memory/` and open a Merge Request
617
+ - `remove|rm <path> --title <title> [--reason <text>] [--hard] [-y]` — Propose removing an approved memory (path from `ak mem list --approved`); soft-remove moves it to `_deprecated/`, `--hard` deletes it outright
609
618
 
610
619
  **What it does:**
611
- 1. Stores team knowledge in `.aiflow/memory/`
612
- 2. Makes knowledge discoverable across the team
620
+ 1. `draft` writes a frontmatter'd Markdown file to `AK-Docs/99.Memory/_pending/` on your machine only — recallable locally, but not yet shared with the team
621
+ 2. `submit` creates a branch in `AK-Docs`, moves the file out of `_pending/` to its real category path, commits, pushes, and opens a Merge Request (same branch+MR flow as `ak docs branch`/`ak docs submit`)
622
+ 3. A teammate (PM by convention) reviews and merges the MR — that's the point it becomes recallable for everyone else
623
+ 4. `session-start` hook auto-injects the `MEMORY.md` index plus top-N relevant approved memories into every new session — no manual recall needed once merged
613
624
 
614
625
  **Example:**
615
626
  ```bash
616
- $ aiflow memory save "payment-flow" "Process: 1. Validate amount, 2. Call Stripe API, 3. Update DB"
617
- Saved: payment-flow
627
+ $ ak mem d -c 01.Lessons/dev -s prevent-429-error --content "Retry with backoff on 429 from payment gateway" -t payment,retry
628
+ Draft đã tạo: _pending/01.Lessons/dev/prevent-429-error.md
629
+ id: mem-payment-prevent-429-error (local — chỉ máy này recall được cho tới khi submit)
630
+
631
+ $ ak mem ls
632
+ Pending drafts (local):
633
+ mem-payment-prevent-429-error _pending/01.Lessons/dev/prevent-429-error.md
634
+ type=lesson scope=payment workflows=all
618
635
 
619
- $ aiflow memory search "payment"
620
- Found 3 memories matching "payment":
621
- 1. payment-flow
622
- 2. payment-error-handling
623
- 3. payment-tests
636
+ $ ak mem sb 01.Lessons/dev/prevent-429-error.md -t "Add memory: prevent-429-error"
637
+ Branch memory/prevent-429-error created in AK-Docs
638
+ Merge Request opened — waiting for approval
624
639
  ```
625
640
 
626
641
  ## guide (alias: `g`)
@@ -639,6 +654,45 @@ ak g -c # command reference only (--comman
639
654
 
640
655
  ---
641
656
 
657
+ ## ask
658
+
659
+ Search ai-flow-kit's own docs for an answer about the kit itself — install, roles, folder structure, CLI commands, memory draft/submit, troubleshooting. This is the deterministic fallback the `aiflow-help` skill uses (and that works with no LLM at all): local keyword search over `docs/common/*.md`, no network call.
660
+
661
+ ```bash
662
+ ak ask "how do I install ai-flow-kit"
663
+ ak ask "submit memory"
664
+ ak ask "switch between cursor and claude code"
665
+ ak ask "some obscure question" -n 5 # show up to 5 results (--limit)
666
+ ```
667
+
668
+ **Arguments:**
669
+ - `<question>` — Free-text question (English or Vietnamese)
670
+
671
+ **Options:**
672
+ - `-n, --limit <n>` — Max number of results to show (default 3)
673
+
674
+ **What it does:**
675
+ 1. Scans every `docs/common/*.md` file (including `workflows/`)
676
+ 2. Scores each file by how many query terms it contains, weighting heading matches higher
677
+ 3. Prints the top matches with the nearest heading and a snippet, ranked by score
678
+
679
+ See also `docs/common/INDEX.md` for the curated topic map this search complements, and the `/ak-ask "<question>"` slash command for asking the same kind of question directly inside a Claude Code chat (explicit trigger for the `aiflow-help` skill, no terminal needed).
680
+
681
+ **Example:**
682
+ ```bash
683
+ $ ak ask "submit memory"
684
+
685
+ Results for "submit memory":
686
+
687
+ docs/common/cli-reference.md (score 14)
688
+ → memory (alias: `mem`)
689
+ ...`submit|sb <path> --title <title> [-d <desc>] [-y]` — Move a pending draft...
690
+
691
+ See docs/common/INDEX.md for the full topic map.
692
+ ```
693
+
694
+ ---
695
+
642
696
  ## gate (AI workflow logging)
643
697
 
644
698
  Called automatically by AI during gate workflow — not intended for manual use.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@relipa/ai-flow-kit",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
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": {
package/scripts/ask.js ADDED
@@ -0,0 +1,141 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const chalk = require('chalk');
4
+
5
+ // Local, deterministic fallback for "how do I use ai-flow-kit" questions —
6
+ // works without any LLM, and gives the aiflow-help skill a ground-truth
7
+ // search path instead of guessing. Scores docs/common/*.md by simple
8
+ // keyword overlap; no stemming/diacritics-folding (see upgrade-plan #45).
9
+
10
+ const PKG_DIR = path.join(__dirname, '..');
11
+ const DOCS_DIR = path.join(PKG_DIR, 'docs', 'common');
12
+ const SNIPPET_RADIUS = 160;
13
+
14
+ module.exports = function ask(query, options = {}) {
15
+ if (!query || !query.trim()) {
16
+ console.log(chalk.yellow('Usage: ak ask "<question>"'));
17
+ console.log(chalk.gray(' ak ask "how do I install ai-flow-kit"'));
18
+ console.log(chalk.gray(' ak ask "submit memory"'));
19
+ return;
20
+ }
21
+
22
+ const limit = options.limit ? parseInt(options.limit, 10) : 3;
23
+ const files = walk(DOCS_DIR);
24
+ const terms = tokenize(query);
25
+
26
+ const scored = files
27
+ .map((file) => scoreFile(file, terms))
28
+ .filter((r) => r.score > 0)
29
+ .sort((a, b) => b.score - a.score)
30
+ .slice(0, limit);
31
+
32
+ if (scored.length === 0) {
33
+ console.log(chalk.yellow(`No local match for "${query}" in docs/common/.`));
34
+ console.log(chalk.gray(' Try `ak guide` for the quickstart, or check docs/common/INDEX.md for the full topic map.'));
35
+ return scored;
36
+ }
37
+
38
+ console.log(chalk.cyan(`\nResults for "${query}":\n`));
39
+ for (const r of scored) {
40
+ console.log(chalk.bold.white(` ${r.relPath}`) + chalk.gray(` (score ${r.score})`));
41
+ if (r.heading) console.log(chalk.gray(` → ${r.heading}`));
42
+ console.log(` ${r.snippet}\n`);
43
+ }
44
+ console.log(chalk.gray(`See docs/common/INDEX.md for the full topic map.`));
45
+ return scored;
46
+ };
47
+
48
+ function walk(dir) {
49
+ let results = [];
50
+ if (!fs.existsSync(dir)) return results;
51
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
52
+ const full = path.join(dir, entry.name);
53
+ if (entry.isDirectory()) {
54
+ results = results.concat(walk(full));
55
+ } else if (entry.isFile() && entry.name.endsWith('.md')) {
56
+ results.push(full);
57
+ }
58
+ }
59
+ return results;
60
+ }
61
+
62
+ // Generic connector words — kept out of scoring so a short common word (esp. "ai",
63
+ // which appears in almost every doc via "ai-flow-kit") doesn't drown out real topic terms.
64
+ const STOPWORDS = new Set([
65
+ 'a', 'an', 'the', 'this', 'that', 'these', 'those', 'is', 'are', 'was', 'were', 'be', 'been',
66
+ 'do', 'does', 'did', 'to', 'of', 'in', 'on', 'at', 'for', 'and', 'or', 'with', 'how', 'what',
67
+ 'why', 'when', 'where', 'who', 'which', 'it', 'its', 'my', 'me', 'i', 'you', 'your', 'can',
68
+ 'ai',
69
+ 'la', 'va', 'cua', 'the', 'nay', 'de', 'thi', 'toi', 'la gi', 'nhu', 'the nao',
70
+ ]);
71
+
72
+ function tokenize(text) {
73
+ return text
74
+ .toLowerCase()
75
+ .split(/[^a-z0-9à-ỹ]+/i)
76
+ .filter((t) => t.length > 1 && !STOPWORDS.has(t));
77
+ }
78
+
79
+ // Two failure modes to guard against: (1) a raw hit count biases toward the longest
80
+ // file regardless of relevance (CHANGELOG.md is 5x getting-started.md) — fixed by
81
+ // normalizing body hits by document length; (2) substring matching false-positives on
82
+ // short queries (query term "flow" matching inside "Workflow") — fixed by comparing
83
+ // whole tokens instead of doing lower.includes(term).
84
+ function scoreFile(file, terms) {
85
+ const content = fs.readFileSync(file, 'utf-8');
86
+ const relPath = path.relative(PKG_DIR, file).replace(/\\/g, '/');
87
+
88
+ const docTokens = tokenizeRaw(content);
89
+ const wordCount = Math.max(1, docTokens.length);
90
+ const freq = {};
91
+ for (const t of docTokens) freq[t] = (freq[t] || 0) + 1;
92
+
93
+ let bodyHits = 0;
94
+ for (const term of terms) bodyHits += freq[term] || 0;
95
+
96
+ let headingHits = 0;
97
+ const headingLines = content.split('\n').filter((l) => l.trim().startsWith('#'));
98
+ for (const line of headingLines) {
99
+ const lineTokens = new Set(tokenizeRaw(line));
100
+ for (const term of terms) {
101
+ if (lineTokens.has(term)) headingHits += 1;
102
+ }
103
+ }
104
+
105
+ const score = Math.round((bodyHits / wordCount) * 1000 + headingHits * 5);
106
+
107
+ // Snippet position is cosmetic only (not used for scoring), so a plain substring
108
+ // search is fine here even though it may land inside a larger word.
109
+ const lower = content.toLowerCase();
110
+ let firstMatchIndex = -1;
111
+ for (const term of terms) {
112
+ const idx = lower.indexOf(term);
113
+ if (idx !== -1 && (firstMatchIndex === -1 || idx < firstMatchIndex)) firstMatchIndex = idx;
114
+ }
115
+
116
+ const heading = nearestHeading(content, firstMatchIndex);
117
+ const snippet = firstMatchIndex === -1
118
+ ? content.trim().slice(0, SNIPPET_RADIUS).replace(/\n+/g, ' ')
119
+ : buildSnippet(content, firstMatchIndex);
120
+
121
+ return { file, relPath, score, heading, snippet };
122
+ }
123
+
124
+ // Same splitting rule as tokenize(), but without stopword filtering — used to index
125
+ // document content, where we want an accurate word count and per-word frequency map.
126
+ function tokenizeRaw(text) {
127
+ return text.toLowerCase().split(/[^a-z0-9à-ỹ]+/i).filter((t) => t.length > 1);
128
+ }
129
+
130
+ function nearestHeading(content, index) {
131
+ if (index === -1) return null;
132
+ const before = content.slice(0, index).split('\n').filter((l) => l.trim().startsWith('#'));
133
+ return before.length ? before[before.length - 1].replace(/^#+\s*/, '').trim() : null;
134
+ }
135
+
136
+ function buildSnippet(content, index) {
137
+ const start = Math.max(0, index - SNIPPET_RADIUS / 2);
138
+ const end = Math.min(content.length, index + SNIPPET_RADIUS / 2);
139
+ const raw = content.slice(start, end).replace(/\n+/g, ' ').trim();
140
+ return (start > 0 ? '…' : '') + raw + (end < content.length ? '…' : '');
141
+ }
@@ -25,7 +25,7 @@ function norm(cmd) {
25
25
  }
26
26
 
27
27
  // ── Version helpers ─────────────────────────────────────────────────────────
28
- const CURRENT_VERSION = '0.1.5';
28
+ const CURRENT_VERSION = '0.1.6';
29
29
 
30
30
  function parseVersion(v) {
31
31
  if (!v || v === '-' || v === '' || v.toLowerCase() === 'unknown') return null;
@@ -407,13 +407,10 @@ async function writeExcelFile(rows, outputPath, sheetLabel, fromLabel, toLabel)
407
407
 
408
408
  // ── Canonical user list (fixed order) ─────────────────────────────────────
409
409
  const CANONICAL_USERS = [
410
- 'hoangdv@relipasoft.com',
411
410
  'chinhtt@relipasoft.com',
412
411
  'phongnx@relipasoft.com',
413
412
  'dungha@relipasoft.com',
414
- 'thangnv@relipasoft.com',
415
413
  'nguyenlt@relipasoft.com',
416
- 'thuongvv@relipasoft.com',
417
414
  'yenvtb@relipasoft.com',
418
415
  'thainq@relipasoft.com',
419
416
  'tuoittx@relipasoft.com',
@@ -431,6 +428,7 @@ const CANONICAL_USERS = [
431
428
  'khanhnv@relipasoft.com',
432
429
  'namhh@relipasoft.com',
433
430
  'tannt@relipasoft.com',
431
+ 'khangnv@relipasoft.com',
434
432
  ];
435
433
 
436
434
  // Log email → canonical email (for slight mismatches)
@@ -5,7 +5,7 @@ const { execSync } = require('child_process');
5
5
  const { confirm } = require('@inquirer/prompts');
6
6
  const { resolveDocsRepoPath, DOC_REPOS } = require('./docs-repo');
7
7
 
8
- // Implements the branch + Merge Request half of docs/internal/Docs-Management-Flow.md:
8
+ // Implements the branch + Merge Request half of docs/common/Docs-Management-Flow.md:
9
9
  // PM reviews & merges into `main`; every other role updates docs on a
10
10
  // `feature/<functionId>/<taskId>` branch created from `main`, then submits a MR.
11
11
  //
@@ -7,7 +7,7 @@ const { execSync } = require('child_process');
7
7
  // fixed-name folders living directly under the workspace root — the folder
8
8
  // opened in the editor, which contains one or more source repos plus these
9
9
  // two docs repos as siblings. `ak` is run from that root, so projectDir
10
- // (process.cwd()) already IS the root — see docs/internal/Project-Structure.md.
10
+ // (process.cwd()) already IS the root — see docs/common/Project-Structure.md.
11
11
  const DOC_REPOS = [
12
12
  { name: 'AK-Docs', purpose: 'tài liệu dự án (Requirements, Specs, Test Cases, Coding docs...)' },
13
13
  { name: 'Shared-Docs', purpose: 'template dùng chung cho tất cả dự án (Spec, QA, Test Case, Test Report...)' },
@@ -100,7 +100,7 @@ process.stdin.on('end', async () => {
100
100
  } catch (_) { }
101
101
 
102
102
  // ── 3.5 Load relevant memory (99.Memory/ Project Brain) ────────
103
- // See docs/internal/Memory-Architecture-v1.0.md — Luồng 3 (Nạp/Sử dụng).
103
+ // See docs/common/Memory-Architecture-v1.0.md — Luồng 3 (Nạp/Sử dụng).
104
104
  let memoryBlock = '';
105
105
  try {
106
106
  const workspaceRoot = resolveWorkspaceRoot(projectRoot);
package/scripts/init.js CHANGED
@@ -350,6 +350,11 @@ async function setupClaudeCommands(projectDir) {
350
350
  dest: 'coding.md',
351
351
  description: 'Manual trigger for the DEV 5-Gate Coding Workflow (bug-fix / feature / refactor / investigation / documentation). Use this when the SessionStart auto-start message was missed — e.g. a ticket was loaded with `ak use` while this chat session was already open.',
352
352
  },
353
+ {
354
+ src: path.join(PKG_DIR, 'custom', 'templates', 'shared', 'ak-ask-workflow.md'),
355
+ dest: 'ak-ask.md',
356
+ description: 'Ask a question about ai-flow-kit itself (install, roles, folder structure, CLI, memory workflow) — explicit trigger for the aiflow-help self-help skill, for when the question might otherwise be mistaken for ticket work. Usage: /ak-ask "how do I submit a memory".',
357
+ },
353
358
  ];
354
359
 
355
360
  for (const cmd of commands) {
@@ -1092,7 +1097,7 @@ async function init(options) {
1092
1097
 
1093
1098
  // ── Bootstrap 99.Memory/ skeleton (Luồng 1 — Khởi tạo) ────────
1094
1099
  // Free: no reads, no token cost — just an empty folder skeleton if AK-Docs
1095
- // exists and doesn't have one yet (docs/internal/Memory-Architecture-v1.0.md §4).
1100
+ // exists and doesn't have one yet (docs/common/Memory-Architecture-v1.0.md §4).
1096
1101
  const memorySkeleton = await memoryStore.ensureSkeleton(projectDir);
1097
1102
  if (memorySkeleton.created) {
1098
1103
  console.log(chalk.green('✓ Đã tạo khung 99.Memory/ trong AK-Docs (chưa có ghi nhớ nào).'));
@@ -1210,6 +1215,7 @@ module.exports.detectRtk = detectRtk;
1210
1215
  module.exports.isRtkHookConfigured = isRtkHookConfigured;
1211
1216
  module.exports.isGitNexusConfigured = isGitNexusConfigured;
1212
1217
  module.exports.setupClaudeCommands = setupClaudeCommands;
1218
+ module.exports.copyDocsToProject = copyDocsToProject;
1213
1219
  module.exports.setupFramework = setupFramework;
1214
1220
  module.exports.ensureAiflowGitignored = ensureAiflowGitignored;
1215
1221
  module.exports.resolveWorkflowFile = resolveWorkflowFile;
@@ -9,7 +9,7 @@ function resolveDocsRepoPath(projectDir, repoName) {
9
9
  return path.join(projectDir, repoName);
10
10
  }
11
11
 
12
- // Engine for the "99.Memory/" Project Brain — see docs/internal/Memory-Architecture-v1.0.md.
12
+ // Engine for the "99.Memory/" Project Brain — see docs/common/Memory-Architecture-v1.0.md.
13
13
  // Pure functions only (no chalk/console output, no CLI parsing) — scripts/memory.js and
14
14
  // scripts/hooks/session-start.js are the callers.
15
15
  //