@relipa/ai-flow-kit 0.1.6 → 0.1.7-beta.0

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.
Files changed (30) hide show
  1. package/bin/aiflow.js +38 -34
  2. package/custom/skills/review-plan/SKILL.md +19 -0
  3. package/custom/templates/memory/CODEOWNERS +8 -0
  4. package/custom/templates/memory/ci/memory-finalize.yml +9 -0
  5. package/custom/templates/memory/ci/memory-lint.yml +10 -0
  6. package/custom/templates/memory/gitlab/merge_request_templates/memory.md +18 -0
  7. package/custom/templates/memory/memory-item.md +25 -0
  8. package/custom/templates/memory/skeleton/00.Shared/architecture/_global/.gitkeep +0 -0
  9. package/custom/templates/memory/skeleton/00.Shared/decisions/.gitkeep +0 -0
  10. package/custom/templates/memory/skeleton/00.Shared/domain/_global/.gitkeep +0 -0
  11. package/custom/templates/memory/skeleton/00.Shared/glossary/.gitkeep +0 -0
  12. package/custom/templates/memory/skeleton/01.Lessons/ba/_global/.gitkeep +0 -0
  13. package/custom/templates/memory/skeleton/01.Lessons/dev/_global/.gitkeep +0 -0
  14. package/custom/templates/memory/skeleton/01.Lessons/pm/_global/.gitkeep +0 -0
  15. package/custom/templates/memory/skeleton/01.Lessons/qa/_global/.gitkeep +0 -0
  16. package/custom/templates/memory/skeleton/02.Instincts/approved/_global/.gitkeep +0 -0
  17. package/custom/templates/memory/skeleton/03.Retro/.gitkeep +0 -0
  18. package/custom/templates/memory/skeleton/MEMORY.md +7 -0
  19. package/custom/templates/memory/skeleton/_deprecated/.gitkeep +0 -0
  20. package/custom/templates/shared/create-spec-workflow.md +13 -1
  21. package/custom/templates/shared/create-testcase-workflow.md +12 -0
  22. package/custom/templates/shared/gate-workflow.md +7 -0
  23. package/package.json +1 -1
  24. package/scripts/create-score-excel.js +1 -1
  25. package/scripts/hooks/session-start.js +105 -2
  26. package/scripts/init.js +24 -0
  27. package/scripts/memory-store.js +391 -0
  28. package/scripts/memory.js +176 -247
  29. package/scripts/update.js +12 -0
  30. package/scripts/use.js +2 -1
package/bin/aiflow.js CHANGED
@@ -85,12 +85,10 @@ program
85
85
  let sub = sec;
86
86
  if (sec && !sec.startsWith('-')) {
87
87
  const memAliases = {
88
- 's': 'save',
89
- 'g': 'get',
88
+ 'd': 'draft',
90
89
  'ls': 'list',
91
- 'sr': 'search',
92
- 'd': 'delete',
93
- 'cl': 'clear'
90
+ 'sb': 'submit',
91
+ 'rm': 'remove',
94
92
  };
95
93
  if (memAliases[sec]) sub = memAliases[sec];
96
94
  cmd = `memory.${sub}`;
@@ -351,45 +349,51 @@ program
351
349
  });
352
350
  });
353
351
 
354
- // ── memory ────────────────────────────────────────────────────
355
- // Sub-commands to avoid the ambiguous "memory <action> --save key" pattern
356
- const memCmd = program.command('memory').alias('mem').description('Manage team knowledge and memory');
357
-
358
- memCmd
359
- .command('save <key> <value>')
360
- .alias('s')
361
- .description('Save a memory entry')
362
- .action((key, value) => { memoryCommand('save', { key, value }); });
352
+ // ── memory (99.Memory/ Project Brain — see docs/internal/Memory-Architecture-v1.0.md) ──
353
+ // Same "AI drafts human approves via MR" pattern as `docs` below: drafts are local-only
354
+ // (_pending/, gitignored) until `submit` opens a Merge Request for PM review.
355
+ const memCmd = program.command('memory').alias('mem').description('Manage 99.Memory/ project knowledge (draft, list, submit, remove)');
363
356
 
364
357
  memCmd
365
- .command('get <key>')
366
- .alias('g')
367
- .description('Retrieve a memory entry')
368
- .action((key) => { memoryCommand('get', { key }); });
358
+ .command('draft')
359
+ .alias('d')
360
+ .description('Create a local memory draft in _pending/ (not yet shared with the team)')
361
+ .requiredOption('-c, --category <category>', 'e.g. 00.Shared/architecture, 01.Lessons/dev, 02.Instincts/approved')
362
+ .requiredOption('-s, --slug <slug>', 'short kebab-case name, e.g. prevent-429-error')
363
+ .requiredOption('--content <text>', 'memory body (≤150 words, 1 fact)')
364
+ .option('-f, --function-id <id>', 'functionId this memory is scoped to (omit/= global for cross-project facts)')
365
+ .option('--scope <scope>', 'alternative to --function-id for flat categories (glossary/decisions)')
366
+ .option('-t, --tags <list>', 'comma-separated tags, scorer matches on these')
367
+ .option('-w, --workflows <list>', 'comma-separated workflows (coding, create-spec, create-testcase, gen-doc, all)')
368
+ .option('--source <text>', 'trace: ticket / Gate this was learned from')
369
+ .option('--confidence <n>', 'AI self-rated 0.3–0.7', '0.5')
370
+ .action((options) => { memoryCommand('draft', options); });
369
371
 
370
372
  memCmd
371
373
  .command('list')
372
374
  .alias('ls')
373
- .description('List all memories')
374
- .action(() => { memoryCommand('list'); });
375
-
376
- memCmd
377
- .command('search <query>')
378
- .alias('sr')
379
- .description('Search memories by keyword')
380
- .action((query) => { memoryCommand('search', { query }); });
375
+ .option('-a, --approved', 'list approved memories instead of local pending drafts')
376
+ .action((options) => { memoryCommand('list', options); });
381
377
 
382
378
  memCmd
383
- .command('delete <key>')
384
- .alias('d')
385
- .description('Delete a memory entry')
386
- .action((key) => { memoryCommand('delete', { key }); });
379
+ .command('submit <path>')
380
+ .alias('sb')
381
+ .description('Move a pending draft (path from `ak memory list`) to its destination and open a Merge Request')
382
+ .requiredOption('-t, --title <title>', 'commit message / MR title')
383
+ .option('-d, --description <text>', 'MR description', '')
384
+ .option('-y, --yes', 'skip the interactive confirm — only pass this after the user has approved in chat')
385
+ .action((path, options) => { memoryCommand('submit', { ...options, _positional: [path] }); });
387
386
 
388
387
  memCmd
389
- .command('clear')
390
- .alias('cl')
391
- .description('Clear all memories')
392
- .action(() => { memoryCommand('clear'); });
388
+ .command('remove <path>')
389
+ .alias('rm')
390
+ .description('Propose removing an approved memory (path from `ak memory list --approved`)')
391
+ .requiredOption('-t, --title <title>', 'commit message / MR title')
392
+ .option('-d, --description <text>', 'MR description', '')
393
+ .option('--reason <text>', 'why this memory is being removed')
394
+ .option('--hard', 'delete outright instead of moving to _deprecated/ (rarely — see doc §Luồng 4)')
395
+ .option('-y, --yes', 'skip the interactive confirm — only pass this after the user has approved in chat')
396
+ .action((path, options) => { memoryCommand('remove', { ...options, _positional: [path] }); });
393
397
 
394
398
  // ── docs (branch + Merge Request workflow for AK-Docs/Shared-Docs) ─────
395
399
  // See docs/internal/Docs-Management-Flow.md — PM reviews & merges `main`;
@@ -164,6 +164,22 @@ Create file `AK-Docs/04.Coding/04.Reviews/[functionId]/[ticketId].md`:
164
164
  - [ ] UI matches `design-context.md` (layout, color, typography, spacing) — UI tickets only
165
165
  ```
166
166
 
167
+ ### Step 2.5: Retrospect + propose memory drafts
168
+
169
+ Synthesize what was learned this task. **Priority order — human corrections first:** if this is a repeat pass through Gate 4 (the developer already sent "BUG: ..." at least once this task), the developer's own correction is the single highest-value lesson — scan back through this session for it and draft it even if you already drafted something for the earlier BUG at Step 4 (dedup will catch an exact repeat; don't skip capturing it out of caution). Only after that, add anything else genuinely new: architecture facts discovered, decisions made and why. For each candidate, create a **local draft** (no approval needed yet — `_pending/` is local-only, doc `docs/internal/Memory-Architecture-v1.0.md`):
170
+
171
+ ```
172
+ ak memory draft --category 01.Lessons/dev --function-id [functionId] \
173
+ --slug <short-kebab-slug> --content "<≤150 từ, 1 fact>" \
174
+ --tags <tag1,tag2> --workflows coding --source "[ticket-id] / Gate 4"
175
+ ```
176
+
177
+ Other useful categories here: `00.Shared/architecture` (facts about the system discovered while coding), `00.Shared/decisions` (a technical choice made and why). Skip this step if nothing genuinely new was learned — don't manufacture a memory just to have one.
178
+
179
+ If `ak memory draft` reports a conflict (a similar memory already exists), don't create a duplicate — mention it to the developer instead so they can decide whether to edit the existing one.
180
+
181
+ List the created draft(s) in the Step 3 message below so the developer knows what's sitting in `_pending/` — they (or anyone on the team) can inspect and `ak memory submit` it later when ready to share with the team.
182
+
167
183
  ### Step 3: GATE 4 — Present to Developer
168
184
 
169
185
  ```markdown
@@ -177,6 +193,7 @@ Summary: AK-Docs/04.Coding/04.Reviews/[functionId]/[ticketId].md
177
193
  - Tests: ✅ [N] passed / ❌ [N] failed
178
194
  - Impact: [Low/Medium/High]
179
195
  - Checklist: [N/N] items ✅
196
+ - Memory drafts created: [N] (local, `_pending/` — `ak memory submit` when ready to share)
180
197
 
181
198
  **You need to:**
182
199
  1. Review the summary above
@@ -199,10 +216,12 @@ Summary: AK-Docs/04.Coding/04.Reviews/[functionId]/[ticketId].md
199
216
  → Guide on creating a Pull Request
200
217
 
201
218
  **If the developer types "BUG: [description]" — coding bug:**
219
+ → **Immediately draft a memory** capturing exactly what the developer flagged and why it was wrong (`ak memory draft --category 01.Lessons/dev --function-id [functionId] --slug <slug> --content "<the bug + the fix>" --workflows coding --source "[ticket-id] / Gate 4 dev feedback"`) — do this before fixing, while the correction is fresh; don't wait for Step 2.5 on the next pass to catch it in hindsight.
202
220
  → Analyze bug, fix, rerun verification
203
221
  → Repeat from Step 1 of Gate 4
204
222
 
205
223
  **If the developer types "BUG: [description]" — requirement bug:**
224
+ → **Immediately draft a memory** (same category/command as above) capturing what was misunderstood about the requirement and what it actually should have been — this is a requirement-comprehension lesson, valuable even though Gate 1 itself has no retrospect step of its own.
206
225
  → Notify: "This is a requirement bug, requirement document needs update"
207
226
  → Return to Gate 1, update requirement.md, wait for APPROVED again
208
227
 
@@ -0,0 +1,8 @@
1
+ # Áp dụng thủ công vào file CODEOWNERS của repo AK-Docs (mục 5.1.2 Memory-Architecture-v1.0.md).
2
+ # GitLab tự gợi ý các reviewer này làm Consult khi diff đụng đúng thư mục — không phải approver
3
+ # (approver luôn là PM, cấu hình riêng ở MR approval rule, không qua CODEOWNERS).
4
+
5
+ 99.Memory/00.Shared/architecture/ @TL
6
+ 99.Memory/00.Shared/domain/ @BA
7
+ 99.Memory/00.Shared/glossary/ @BA
8
+ 99.Memory/02.Instincts/ @TL
@@ -0,0 +1,9 @@
1
+ # Áp dụng thủ công vào .gitlab-ci.yml của repo AK-Docs (mục 5.1.2 Memory-Architecture-v1.0.md).
2
+ # Chạy khi MR label `memory` đã đủ approval từ PM — flip status, rebuild MEMORY.md, commit lên branch.
3
+ memory-finalize:
4
+ stage: finalize
5
+ rules:
6
+ - if: '$CI_MERGE_REQUEST_LABELS =~ /memory/ && $CI_MERGE_REQUEST_APPROVED == "true"'
7
+ script:
8
+ - node scripts/ci/memory-finalize.js # flip status: approved, điền reviewed_by, rebuild MEMORY.md
9
+ # (script CI thực tế nằm trong repo AK-Docs, không phải kit)
@@ -0,0 +1,10 @@
1
+ # Áp dụng thủ công vào .gitlab-ci.yml của repo AK-Docs (mục 5.1.2 Memory-Architecture-v1.0.md).
2
+ # Chạy trên mọi MR có label `memory`, TRƯỚC khi PM approve.
3
+ memory-lint:
4
+ stage: lint
5
+ rules:
6
+ - if: '$CI_MERGE_REQUEST_LABELS =~ /memory/'
7
+ script:
8
+ - node scripts/ci/memory-lint.js # validate frontmatter đủ trường, ≤150 từ, 1 fact/file,
9
+ # id không trùng, id khớp đường dẫn file, quét secret/PII
10
+ # (script CI thực tế nằm trong repo AK-Docs, không phải kit)
@@ -0,0 +1,18 @@
1
+ ## Memory MR — Ticket: <!-- TICKET-123 -->
2
+
3
+ **Loại:** Thêm mới / Gỡ bỏ (soft-remove / hard-remove)
4
+ **Mem-id(s):** <!-- mem-F003-refund-flow, mem-F003-prevent-429-error -->
5
+
6
+ ### Checklist PM (bắt buộc trước khi approve)
7
+
8
+ - [ ] Đúng sự thật và còn hiệu lực?
9
+ - [ ] Trace được nguồn gốc (`source:` trỏ đúng ticket/Gate)?
10
+ - [ ] Nội dung ngắn gọn (≤150 từ), đúng khuôn mẫu 1 file = 1 fact?
11
+ - [ ] Có hành động cụ thể áp dụng được (mục "Áp dụng")?
12
+ - [ ] Phạm vi (`scope:`) hẹp nhất có thể — không lạm dụng `global`?
13
+ - [ ] Không mâu thuẫn với `.rules/`?
14
+ - [ ] Đã tham vấn đúng người cho nội dung nhạy cảm (TL cho kỹ thuật, BA/Comtor cho glossary/domain)?
15
+
16
+ ### Consult (tự động theo CODEOWNERS)
17
+
18
+ TL/BA được tag tự động theo thư mục bị đụng — đây là góp ý, quyền quyết vẫn ở PM.
@@ -0,0 +1,25 @@
1
+ ---
2
+ id: mem-<functionId>-<slug> # CLI tự sinh từ vị trí file: mem-<functionId>-<slug>
3
+ # slug do AI đặt — ngắn, đọc là hiểu memory lưu gì
4
+ # (vd: basic-payment-flow, refund-flow, prevent-429-error).
5
+ # KHÔNG có timestamp: trùng slug trong cùng folder chính là
6
+ # tín hiệu chống trùng lặp — CLI hỏi "memory tương tự đã có,
7
+ # cập nhật bản cũ thay vì tạo mới?"
8
+ type: lesson | fact | decision | glossary | instinct
9
+ workflows: [coding, create-testcase] # workflow nào cần recall; [all] nếu mọi workflow
10
+ tags: [payment, refund, http-429] # ascii-lowercase, AI tự sinh khi tạo draft —
11
+ # scorer CHỈ so khớp trên trường này (không so body — JP/VN)
12
+ scope: F-003_Payment | global # trùng với folder chứa file; global → file đặt trong _global/
13
+ confidence: 0.6 # AI tự chấm 0.3–0.7; reviewer chỉnh khi duyệt
14
+ source: TICKET-123 / Gate 4 # trace: học được từ đâu
15
+ status: pending # pending | approved | deprecated
16
+ hits: 0 # consolidate cập nhật từ telemetry local — không sửa tay
17
+ created: 2026-07-09
18
+ reviewed_by: # điền khi approve
19
+ refs: [] # (tùy chọn) file/dir code hoặc docs mà memory phụ thuộc
20
+ verified_commit: # commit đã verify refs lần cuối (CLI tự điền)
21
+ stale: false # hook tự set true khi refs thay đổi sau verified_commit
22
+ ---
23
+ <Nội dung: TỐI ĐA 150 từ, 1 file = 1 fact duy nhất.>
24
+
25
+ **Áp dụng:** <hành động cụ thể AI/người cần làm khi gặp tình huống này.>
@@ -0,0 +1,7 @@
1
+ # 99.Memory — Project Brain Index
2
+
3
+ > Auto-generated by `ak memory submit` / `ak memory remove` when a Merge Request is merged into `main`.
4
+ > **Do not edit this file by hand** — it is rebuilt from the frontmatter of every `status: approved` file under `99.Memory/`.
5
+ > One line per memory: `- [mem-id](path/to/file.md) — <hook ≤15 từ> (type, scope, workflows)`
6
+
7
+ See `docs/internal/Memory-Architecture-v1.0.md` in ai-flow-kit for the full design.
@@ -429,7 +429,7 @@ File phân tích: [02.BA-Specs/01.Analysis/[functionId]/Analysis_v(n+1).md](02
429
429
  → Nếu blocked: làm theo gate-review skill response protocol
430
430
 
431
431
  **Phản hồi đặc biệt:**
432
- - BA gõ `REVISION: [nội dung]` → cập nhật section liên quan → re-generate review file → hiển thị lại gate pause
432
+ - BA gõ `REVISION: [nội dung]` → **trước khi sửa**, tạo ngay 1 memory draft ghi lại chính xác điều BA vừa chỉnh (`ak memory draft --category 01.Lessons/ba --function-id [functionId] --slug <slug> --content "<điều BA sửa + vì sao>" --workflows create-spec --source "[functionId] / Gate 4 BA feedback"`) — đây là tín hiệu giá trị nhất, đừng chờ Bước 7.5 mới bắt lại theo trí nhớ → cập nhật section liên quan → re-generate review file → hiển thị lại gate pause
433
433
 
434
434
  **Definition of Done:**
435
435
  - [ ] File `UC-Spec_v1.md` đúng vị trí và cấu trúc template
@@ -441,6 +441,18 @@ File phân tích: [02.BA-Specs/01.Analysis/[functionId]/Analysis_v(n+1).md](02
441
441
  > **Telemetry:** Run `ak gate 4 start --ticket [functionId]` khi bắt đầu gate này.
442
442
  > Run `ak gate 4 approved --ticket [functionId]` sau khi gate-review verify passed. Run as-is — không thêm shell redirects.
443
443
 
444
+ #### Bước 7.5: Retrospect + đề xuất memory draft
445
+
446
+ Sau khi Gate 4 APPROVED, đúc kết những gì học được. **Ưu tiên feedback của BA trước:** nếu có vòng `REVISION` nào đã xảy ra, đảm bảo điều BA sửa đã có draft (thường đã tạo ngay lúc REVISION ở trên — kiểm tra lại, đừng bỏ sót). Sau đó mới thêm những gì mới khác: business rule mới confirm, thuật ngữ cần làm rõ, quyết định đặc tả và lý do. Tạo **draft local** cho từng candidate (chưa cần duyệt — `_pending/` chỉ ở local, xem `docs/internal/Memory-Architecture-v1.0.md`):
447
+
448
+ ```
449
+ ak memory draft --category 00.Shared/domain --function-id [functionId] \
450
+ --slug <slug-ngắn> --content "<≤150 từ, 1 fact>" \
451
+ --tags <tag1,tag2> --workflows create-spec --source "[functionId] / Gate 4"
452
+ ```
453
+
454
+ Category khác hữu ích ở đây: `00.Shared/glossary` (thuật ngữ JP↔VN↔EN mới xác nhận — flat, không cần `--function-id`), `01.Lessons/ba` (bài học khi làm spec), `00.Shared/decisions` (quyết định đặc tả). Bỏ qua bước này nếu không có gì thực sự mới. Nếu `ak memory draft` báo trùng, đừng tạo bản mới — báo cho BA để họ quyết định sửa bản cũ.
455
+
444
456
  #### Bước 8: Submit AK-Docs lên remote qua Merge Request
445
457
 
446
458
  Sau khi Gate 4 đã APPROVED (UC Spec hoàn thành):
@@ -485,6 +485,18 @@ Trước khi ghi bất kỳ file nào vào `03.Testing/`, đảm bảo AK-Docs
485
485
  > **Telemetry:** Run `ak gate 4 start --ticket [functionId]` khi bắt đầu gate này.
486
486
  > Run `ak gate 4 approved --ticket [functionId]` sau khi gate-review verify passed. Run as-is — không thêm shell redirects.
487
487
 
488
+ #### Bước 7.5: Retrospect + đề xuất memory draft
489
+
490
+ Sau khi Gate 4 APPROVED, đúc kết những gì học được. **Ưu tiên trước:** nếu review ở Bước 7 từng phát hiện Major/Critical issue phải sửa lại, đó là bài học giá trị nhất — đảm bảo có draft cho đúng issue đó. Sau đó mới thêm lỗi/bẫy môi trường gặp khi test, business rule cần làm rõ thêm. Tạo **draft local** cho từng candidate (chưa cần duyệt — `_pending/` chỉ ở local, xem `docs/internal/Memory-Architecture-v1.0.md`):
491
+
492
+ ```
493
+ ak memory draft --category 01.Lessons/qa --function-id [functionId] \
494
+ --slug <slug-ngắn> --content "<≤150 từ, 1 fact>" \
495
+ --tags <tag1,tag2> --workflows create-testcase --source "[functionId] / Gate 4"
496
+ ```
497
+
498
+ Category khác hữu ích ở đây: `00.Shared/domain` (business rule phát hiện khi thiết kế test), `00.Shared/glossary` (thuật ngữ — flat, không cần `--function-id`). Bỏ qua bước này nếu không có gì thực sự mới. Nếu `ak memory draft` báo trùng, đừng tạo bản mới — báo cho QA để họ quyết định sửa bản cũ.
499
+
488
500
  #### Bước 8: Submit AK-Docs lên remote qua Merge Request
489
501
 
490
502
  Sau khi Gate 4 đã APPROVED (bộ Test Case hoàn thành):
@@ -655,6 +655,13 @@ Bugs logged: [N] | Skipped: [N]
655
655
  - **Markdown:** Save to `AK-Docs/04.Coding/02.Plans/[functionId]/[ticketId].md` (or the custom path/format noted in the requirement doc — e.g. Excel; still write a short pointer + summary into this file so the AK-Docs history stays complete)
656
656
  4. Self-review: verify content completeness against the approved requirement outline
657
657
  5. Create `AK-Docs/04.Coding/02.Plans/[functionId]/[ticketId]-summary.md` with a brief summary of what was generated
658
+ 5.5. **Retrospect + propose memory drafts:** synthesize anything genuinely new learned while producing this document (architecture fact, business rule, decision) and create a **local draft** for each (no approval needed yet — `_pending/` is local-only, see `docs/internal/Memory-Architecture-v1.0.md`):
659
+ ```
660
+ ak memory draft --category 00.Shared/architecture --function-id [functionId] \
661
+ --slug <short-kebab-slug> --content "<≤150 words, 1 fact>" \
662
+ --tags <tag1,tag2> --workflows gen-doc --source "[ticket-id] / Gate 2"
663
+ ```
664
+ Skip if nothing new was learned. If `ak memory draft` reports a conflict, don't duplicate — mention the existing one instead.
658
665
  6. **Submit AK-Docs lên remote qua Merge Request:**
659
666
  - Soạn title + description cho MR (tóm tắt tài liệu vừa tạo, link ticket gốc), hiển thị cho người dùng xem trước.
660
667
  - Hỏi: "Nội dung commit/MR như trên — đồng ý submit AK-Docs không?" → đồng ý thì chạy `ak docs submit --title "..." --description "..." --yes`; từ chối thì dừng, để người dùng tự commit/tạo MR khi sẵn sàng.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@relipa/ai-flow-kit",
3
- "version": "0.1.6",
3
+ "version": "0.1.7-beta.0",
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": {
@@ -25,7 +25,7 @@ function norm(cmd) {
25
25
  }
26
26
 
27
27
  // ── Version helpers ─────────────────────────────────────────────────────────
28
- const CURRENT_VERSION = '0.1.4';
28
+ const CURRENT_VERSION = '0.1.5';
29
29
 
30
30
  function parseVersion(v) {
31
31
  if (!v || v === '-' || v === '' || v.toLowerCase() === 'unknown') return null;
@@ -9,6 +9,7 @@
9
9
  const fs = require('fs');
10
10
  const path = require('path');
11
11
  const os = require('os');
12
+ const { execSync } = require('child_process');
12
13
 
13
14
  function loadTelemetry() {
14
15
  try { return require('../telemetry/record'); } catch (_) { }
@@ -29,7 +30,7 @@ let tasksDir = '';
29
30
  let raw = '';
30
31
  process.stdin.setEncoding('utf-8');
31
32
  process.stdin.on('data', chunk => { raw += chunk; });
32
- process.stdin.on('end', () => {
33
+ process.stdin.on('end', async () => {
33
34
  let hookData = {};
34
35
  try { hookData = JSON.parse(raw || '{}'); } catch (_) { }
35
36
 
@@ -59,9 +60,10 @@ process.stdin.on('end', () => {
59
60
 
60
61
  // ── 2. Load active ticket context ──────────────────────────────
61
62
  let contextBlock = '';
63
+ let ctx = null;
62
64
  try {
63
65
  if (fs.existsSync(contextPath)) {
64
- const ctx = JSON.parse(fs.readFileSync(contextPath, 'utf-8'));
66
+ ctx = JSON.parse(fs.readFileSync(contextPath, 'utf-8'));
65
67
  if (ctx.taskId && ctx.title) {
66
68
  const taskState = loadTaskState(ctx.taskId);
67
69
  contextBlock = buildContextPrompt(ctx, taskState);
@@ -97,6 +99,34 @@ process.stdin.on('end', () => {
97
99
  }
98
100
  } catch (_) { }
99
101
 
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).
104
+ let memoryBlock = '';
105
+ try {
106
+ const workspaceRoot = resolveWorkspaceRoot(projectRoot);
107
+ const akDocsPath = path.join(workspaceRoot, 'AK-Docs');
108
+ if (fs.existsSync(akDocsPath)) {
109
+ if (fs.existsSync(path.join(akDocsPath, '.git'))) {
110
+ pullAkDocsWithTimeout(akDocsPath);
111
+ }
112
+ const memoryStore = require('../lib/memory-store');
113
+ const functionId = ctx ? (ctx.functionId || ctx.screenId) : null;
114
+ const workflow = inferWorkflow(ctx);
115
+ const tags = ctx ? extractQueryTags(ctx) : [];
116
+
117
+ const [indexContent, relevant] = await Promise.all([
118
+ memoryStore.readIndex(workspaceRoot),
119
+ memoryStore.loadRelevant(workspaceRoot, { functionId, workflow, tags }),
120
+ ]);
121
+
122
+ memoryBlock = buildMemoryBlock(indexContent, relevant);
123
+ // recordHits' ledger lives under THIS project's own .aiflow/ (projectRoot),
124
+ // not the workspace root — matches where context/tasks/ already live. Batched
125
+ // into one read-modify-write (see memory-store.js) instead of one call per id.
126
+ await memoryStore.recordHits(projectRoot, relevant.map(m => m.id));
127
+ }
128
+ } catch (_) { }
129
+
100
130
  // ── 4. Combine and output ──────────────────────────────────────
101
131
  const parts = [];
102
132
  if (skillContent) {
@@ -105,6 +135,9 @@ process.stdin.on('end', () => {
105
135
  if (contextBlock) {
106
136
  parts.push(contextBlock);
107
137
  }
138
+ if (memoryBlock) {
139
+ parts.push(memoryBlock);
140
+ }
108
141
 
109
142
  const combined = parts.join('\n\n');
110
143
  const escaped = combined
@@ -126,6 +159,76 @@ process.stdin.on('end', () => {
126
159
 
127
160
  // ── Helpers ────────────────────────────────────────────────────
128
161
 
162
+ // AK-Docs lives as a sibling of the project repo at the workspace root (see
163
+ // scripts/docs-repo.js), but this hook can't rely on process.cwd() — Claude Code's
164
+ // hook invocation cwd isn't guaranteed to be the workspace root. Try the project
165
+ // root itself first (project opened standalone with AK-Docs cloned alongside it
166
+ // at that same level), then its parent (the documented sibling-of-the-repo layout).
167
+ function resolveWorkspaceRoot(projectRoot) {
168
+ if (fs.existsSync(path.join(projectRoot, 'AK-Docs'))) return projectRoot;
169
+ const parent = path.resolve(projectRoot, '..');
170
+ if (fs.existsSync(path.join(parent, 'AK-Docs'))) return parent;
171
+ return projectRoot;
172
+ }
173
+
174
+ function pullAkDocsWithTimeout(akDocsPath, timeoutMs = 5000) {
175
+ try {
176
+ execSync('git pull', { cwd: akDocsPath, stdio: 'ignore', timeout: timeoutMs });
177
+ } catch (err) {
178
+ process.stderr.write(`[aiflow] ⚠ Không pull được AK-Docs (${String(err.message || err).split('\n')[0]}) — dùng bản memory hiện có trên máy.\n`);
179
+ }
180
+ }
181
+
182
+ // Map the active task's taskType to the workflow profile used by the memory scorer
183
+ // (doc §5.2 profile table). Unresolvable → null, which falls back to the doc's
184
+ // "gen-doc / không xác định" default profile inside memory-store's scoring.
185
+ function inferWorkflow(ctx) {
186
+ if (!ctx) return null;
187
+ const t = ctx.taskType;
188
+ if (t === 'gen-doc') return 'gen-doc';
189
+ if (t === 'testing') return 'create-testcase';
190
+ if (t === 'spec') return 'create-spec';
191
+ if (!t || ['feature', 'bug-fix', 'refactor', 'documentation', 'investigation'].includes(t)) return 'coding';
192
+ return null;
193
+ }
194
+
195
+ // Rough query-side tag extraction from the ticket title — the scorer only needs a
196
+ // handful of significant words to overlap against memory `tags:` (doc §5.2 formula).
197
+ function extractQueryTags(ctx) {
198
+ const text = `${ctx.title || ''} ${ctx.description || ''}`;
199
+ return [...new Set(
200
+ text.toLowerCase()
201
+ .replace(/[^a-z0-9\s-]/g, ' ')
202
+ .split(/\s+/)
203
+ .filter(w => w.length > 3)
204
+ )].slice(0, 8);
205
+ }
206
+
207
+ function buildMemoryBlock(indexContent, relevant) {
208
+ if (!indexContent && (!relevant || relevant.length === 0)) return '';
209
+ const lines = ['<PROJECT_MEMORY>'];
210
+ lines.push('**99.Memory/ index (Lớp 1 — luôn có; xem MEMORY.md để biết toàn bộ danh mục):**');
211
+ lines.push('```markdown');
212
+ lines.push((indexContent || '(chưa có memory nào được approve)').trim());
213
+ lines.push('```');
214
+ if (relevant && relevant.length > 0) {
215
+ lines.push('');
216
+ lines.push(`**Memory liên quan nhất tới task này (Lớp 2, ${relevant.length} mục):**`);
217
+ for (const m of relevant) {
218
+ const pendingTag = m.status === 'pending'
219
+ ? ' ⚠ CHƯA DUYỆT — chỉ máy này thấy, kiểm tra lại trước khi tin'
220
+ : '';
221
+ const firstLine = (m.content || '').trim().split('\n')[0];
222
+ lines.push(`- **${m.id}** (${m.type}, scope=${m.scope})${pendingTag}`);
223
+ lines.push(` ${firstLine}`);
224
+ }
225
+ }
226
+ lines.push('');
227
+ lines.push('Nếu memory mâu thuẫn với `.rules/`, `.rules/` thắng — báo cho developer biết.');
228
+ lines.push('</PROJECT_MEMORY>');
229
+ return lines.join('\n');
230
+ }
231
+
129
232
  function loadTaskState(taskId) {
130
233
  try {
131
234
  const statePath = path.join(tasksDir, taskId, 'task-state.json');
package/scripts/init.js CHANGED
@@ -4,6 +4,7 @@ const os = require('os');
4
4
  const chalk = require('chalk');
5
5
  const { input, checkbox, confirm } = require('@inquirer/prompts');
6
6
  const { syncDocsRepos } = require('./docs-repo');
7
+ const memoryStore = require('./memory-store');
7
8
 
8
9
  const PKG_DIR = path.join(__dirname, '..');
9
10
  const GLOBAL_AIFLOW_DIR = path.join(os.homedir(), '.aiflow');
@@ -229,6 +230,16 @@ async function setupSuperpowersHook(_projectDir, claudeDir) {
229
230
  await fs.copy(telemetrySrc, telemetryDest, { overwrite: true });
230
231
  await fs.writeJson(path.join(telemetryDest, 'package.json'), { type: 'commonjs' }, { spaces: 2 });
231
232
 
233
+ // Copy memory-store.js so session-start.js can require('../lib/memory-store') for the
234
+ // 99.Memory/ Layer 1+2 load — same reason as telemetry above: only the hook file itself
235
+ // is deployed, not the rest of scripts/. memory-store.js is dependency-free by design
236
+ // (no fs-extra/chalk — see its own header comment) specifically so it can be copied alone.
237
+ // Own subdir + nested package.json for the same CJS-pinning reason as hooks/telemetry above.
238
+ const libDest = path.join(claudeDir, 'lib');
239
+ await fs.ensureDir(libDest);
240
+ await fs.copy(path.join(PKG_DIR, 'scripts', 'memory-store.js'), path.join(libDest, 'memory-store.js'), { overwrite: true });
241
+ await fs.writeJson(path.join(libDest, 'package.json'), { type: 'commonjs' }, { spaces: 2 });
242
+
232
243
  const settingsFile = path.join(claudeDir, 'settings.json');
233
244
  let settings = {};
234
245
  if (await fs.pathExists(settingsFile)) {
@@ -1079,6 +1090,19 @@ async function init(options) {
1079
1090
  // ── Sync AK-Docs / Shared-Docs sibling repos ──────────────────
1080
1091
  await syncDocsRepos(projectDir);
1081
1092
 
1093
+ // ── Bootstrap 99.Memory/ skeleton (Luồng 1 — Khởi tạo) ────────
1094
+ // 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).
1096
+ const memorySkeleton = await memoryStore.ensureSkeleton(projectDir);
1097
+ if (memorySkeleton.created) {
1098
+ console.log(chalk.green('✓ Đã tạo khung 99.Memory/ trong AK-Docs (chưa có ghi nhớ nào).'));
1099
+ const gitignoreResult = await memoryStore.ensureMemoryGitignored(projectDir);
1100
+ if (memorySkeleton.committed || gitignoreResult.committed) {
1101
+ console.log(chalk.yellow(' ⚠ Đã commit local trên nhánh hiện tại của AK-Docs — `main` là protected branch,'));
1102
+ console.log(chalk.yellow(' hãy tự `git push` khi sẵn sàng (không tự động push).'));
1103
+ }
1104
+ }
1105
+
1082
1106
  // ── Auto-detect or prompt for framework if not supplied ──────
1083
1107
  if (frameworks.length === 0) {
1084
1108
  const stateFilePath = path.join(aiflowDir, 'state.json');