openmoneta-dev-kit 1.9.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.
- package/README.md +103 -0
- package/agents/qa-autonomous.md +131 -0
- package/agents/requirement-analyst.md +98 -0
- package/agents/security-auditor.md +120 -0
- package/agents/ui-tester.md +186 -0
- package/bin/openmoneta.js +11 -0
- package/hooks/check-plan-exists.sh +154 -0
- package/hooks/enforce-docs-first.sh +169 -0
- package/hooks/inject-process-context.sh +117 -0
- package/hooks/track-changes.sh +46 -0
- package/hooks/verify-completion.sh +165 -0
- package/hooks.json +30 -0
- package/opencode/AGENTS.md.tpl +38 -0
- package/opencode/agents/qa-autonomous.md +42 -0
- package/opencode/agents/requirement-analyst.md +51 -0
- package/opencode/agents/security-auditor.md +46 -0
- package/opencode/agents/ui-tester.md +43 -0
- package/opencode/plugins/openmoneta-guard.ts +389 -0
- package/package.json +41 -0
- package/scripts/debug-hooks.sh +54 -0
- package/scripts/init-project.sh +438 -0
- package/scripts/list-affected-modules.sh +74 -0
- package/skills/auth-bypass-testing/SKILL.md +236 -0
- package/skills/automated-testing/SKILL.md +162 -0
- package/skills/automated-testing/scripts/install-playwright.sh +134 -0
- package/skills/module-architect/SKILL.md +256 -0
- package/skills/plan-writer/SKILL.md +229 -0
- package/skills/requirement-analysis/SKILL.md +163 -0
- package/skills/safe-push/SKILL.md +182 -0
- package/skills/security-checklist/SKILL.md +116 -0
- package/skills/test-strategy/SKILL.md +135 -0
- package/skills/ui-test-loop/SKILL.md +161 -0
- package/src/cli.js +63 -0
- package/src/commands/check.js +30 -0
- package/src/commands/init.js +43 -0
- package/src/commands/install.js +50 -0
- package/src/commands/uninstall.js +74 -0
- package/src/commands/update.js +81 -0
- package/src/lib/paths.js +46 -0
- package/src/lib/version.js +45 -0
- package/templates/AGENTS.md.tpl +106 -0
- package/templates/docs-INDEX.md.tpl +62 -0
- package/templates/env.test.tpl +16 -0
- package/templates/karpathy-reference.md +49 -0
- package/templates/plans-INDEX.md.tpl +38 -0
- package/templates/playwright.config.ts.tpl +44 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: safe-push
|
|
3
|
+
description: "CORE CONDITIONAL: kích hoạt khi user yêu cầu commit + push / push code / đẩy code / merge lên remote / lên main. Đảm bảo pre-push sync + conflict resolution + safe fast-forward push, không đè code người khác. KHÔNG dùng --force trên shared branch."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Safe Push (CORE CONDITIONAL)
|
|
7
|
+
|
|
8
|
+
Skill này chạy ở **Bước 6 — Pre-push Sync + Safe Push**.
|
|
9
|
+
|
|
10
|
+
Chỉ kích hoạt khi user yêu cầu rõ:
|
|
11
|
+
- "commit + push"
|
|
12
|
+
- "push code"
|
|
13
|
+
- "đẩy code"
|
|
14
|
+
- "đẩy lên remote"
|
|
15
|
+
- "merge lên main"
|
|
16
|
+
- "push bản này"
|
|
17
|
+
|
|
18
|
+
Không kích hoạt cho task code bình thường chưa có yêu cầu push.
|
|
19
|
+
|
|
20
|
+
## Mục tiêu
|
|
21
|
+
|
|
22
|
+
Đảm bảo trước khi `git push`, local branch đã đồng bộ với remote mới nhất để không đè code người khác trong repo có nhiều contributor.
|
|
23
|
+
|
|
24
|
+
Vấn đề cần chặn:
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
T0: AI bắt đầu sửa file a từ remote v1
|
|
28
|
+
T1: Người khác sửa file a và push remote v2
|
|
29
|
+
T2: AI push code dựa trên v1
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Bước 6 bắt buộc `fetch + rebase` ngay trước push. Nếu remote có commit mới, local phải rebase lên commit đó trước. Nếu có conflict logic, hỏi user, không tự đoán.
|
|
33
|
+
|
|
34
|
+
## Nguyên tắc bắt buộc
|
|
35
|
+
|
|
36
|
+
1. **Không force push shared branch**: tuyệt đối không chạy `git push --force` hoặc `git push -f` lên `main`, `master`, `develop`, `staging`, `production`.
|
|
37
|
+
2. **Không auto-resolve conflict code logic**: file code business (`*.ts`, `*.tsx`, `*.py`, `*.go`, `*.java`, `*.rb`, `*.php`, `*.rs`) phải hỏi user nếu conflict cùng logic/dòng.
|
|
38
|
+
3. **Re-test sau rebase/conflict**: code đã đổi ngữ cảnh sau rebase, phải chạy lại verify tối thiểu phù hợp repo trước khi push.
|
|
39
|
+
4. **Push thường trước**: dùng `git push` fast-forward. Nếu rejected do remote mới, quay lại fetch/rebase. Loop tối đa 3 lần.
|
|
40
|
+
5. **Không deploy trong skill này**: CI/CD/deploy là pipeline ngoài scope trừ khi user yêu cầu skill riêng.
|
|
41
|
+
|
|
42
|
+
## Workflow 7 bước
|
|
43
|
+
|
|
44
|
+
### 1. Pre-flight branch
|
|
45
|
+
|
|
46
|
+
Chạy:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
git status --short
|
|
50
|
+
git branch --show-current
|
|
51
|
+
git remote -v
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Nếu branch là `main`, `master`, `develop`, `staging`, `production`:
|
|
55
|
+
- Cảnh báo user đây là shared branch.
|
|
56
|
+
- Chỉ tiếp tục nếu user đã yêu cầu push rõ.
|
|
57
|
+
- Không dùng force push trong mọi trường hợp.
|
|
58
|
+
|
|
59
|
+
Nếu working tree chưa clean:
|
|
60
|
+
- Nếu user yêu cầu commit + push: commit trước theo Git Safety Protocol.
|
|
61
|
+
- Nếu còn file conflict/untracked không rõ scope: hỏi user.
|
|
62
|
+
|
|
63
|
+
### 2. Fetch remote ngay trước push
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
BRANCH=$(git branch --show-current)
|
|
67
|
+
git fetch origin "$BRANCH"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Nếu branch chưa track remote:
|
|
71
|
+
- Dùng `git push -u origin HEAD` sau khi các bước sync/test đã pass.
|
|
72
|
+
|
|
73
|
+
### 3. So sánh local với remote
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
git status -sb
|
|
77
|
+
git rev-list --left-right --count HEAD...origin/$BRANCH
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Diễn giải:
|
|
81
|
+
- `0 0`: local == remote, có thể push nếu đã có commit mới local.
|
|
82
|
+
- `N 0`: local ahead remote, có thể push sau test.
|
|
83
|
+
- `0 N`: remote ahead, phải rebase.
|
|
84
|
+
- `N M`: diverged, phải rebase.
|
|
85
|
+
|
|
86
|
+
### 4. Rebase với remote nếu cần
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
git pull --rebase origin "$BRANCH"
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Nếu rebase sạch:
|
|
93
|
+
- Chuyển sang bước test.
|
|
94
|
+
|
|
95
|
+
Nếu conflict:
|
|
96
|
+
- Chuyển sang bước 5.
|
|
97
|
+
|
|
98
|
+
### 5. Conflict resolution policy
|
|
99
|
+
|
|
100
|
+
Trước tiên liệt kê conflict:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
git status --short
|
|
104
|
+
git diff --name-only --diff-filter=U
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
| Loại file conflict | Strategy |
|
|
108
|
+
|---|---|
|
|
109
|
+
| `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml` | Không sửa tay nếu tránh được. Resolve package files rồi regenerate bằng package manager phù hợp (`npm install`, `yarn install`, `pnpm install`). |
|
|
110
|
+
| Generated files (`dist/`, `build/`, `.next/`, `coverage/`) | Cảnh báo user. Thường không nên commit generated output. |
|
|
111
|
+
| Code logic (`*.ts`, `*.tsx`, `*.py`, `*.go`, ...) | Hỏi user bằng `AskQuestion`. Không tự chọn ours/theirs nếu thay đổi có ý nghĩa business. |
|
|
112
|
+
| Migration/schema SQL | Escalate user. Có thể cần đổi thứ tự/tên migration để giữ lịch sử đúng. |
|
|
113
|
+
| Docs/plans (`*.md`) | Có thể merge thủ công nếu rõ ràng; nếu nội dung user/team conflict, hỏi user. |
|
|
114
|
+
|
|
115
|
+
Sau khi user quyết định:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
git add <resolved-files>
|
|
119
|
+
git rebase --continue
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Nếu không thể resolve an toàn:
|
|
123
|
+
- `git rebase --abort`
|
|
124
|
+
- Báo user rõ file conflict và lý do dừng.
|
|
125
|
+
|
|
126
|
+
### 6. Re-run verify tối thiểu
|
|
127
|
+
|
|
128
|
+
Chọn verify theo repo:
|
|
129
|
+
- Nếu có `package.json`: chạy lint/typecheck/test script liên quan nếu tồn tại.
|
|
130
|
+
- Nếu không có script test: chạy check nhẹ nhất có sẵn (`npm run lint`, `npm run typecheck`, `go test ./...`, `pytest`, v.v.).
|
|
131
|
+
- Nếu user không yêu cầu test và repo lớn: chạy smoke verify đã ghi trong plan.
|
|
132
|
+
|
|
133
|
+
Nếu verify fail:
|
|
134
|
+
- Fix nếu lỗi đơn giản và nằm trong scope.
|
|
135
|
+
- Nếu lỗi do conflict logic hoặc ngoài scope: dừng và hỏi user.
|
|
136
|
+
|
|
137
|
+
### 7. Safe push
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
git push
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Nếu push rejected vì remote có commit mới:
|
|
144
|
+
1. Quay lại bước 2.
|
|
145
|
+
2. Loop tối đa 3 lần.
|
|
146
|
+
3. Nếu vẫn rejected/conflict liên tục, dừng và báo user.
|
|
147
|
+
|
|
148
|
+
Không dùng:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
git push --force
|
|
152
|
+
git push -f
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Chỉ cân nhắc `--force-with-lease` trên feature branch cá nhân khi user yêu cầu rõ và branch không phải shared branch.
|
|
156
|
+
|
|
157
|
+
## Output bắt buộc
|
|
158
|
+
|
|
159
|
+
Sau khi push thành công, báo ngắn:
|
|
160
|
+
- Branch đã push.
|
|
161
|
+
- Commit SHA mới nhất.
|
|
162
|
+
- Remote status.
|
|
163
|
+
- Có rebase/conflict không.
|
|
164
|
+
- Verify đã chạy gì.
|
|
165
|
+
|
|
166
|
+
Ví dụ:
|
|
167
|
+
|
|
168
|
+
```text
|
|
169
|
+
Đã push branch feature/search-form.
|
|
170
|
+
- SHA: abc1234
|
|
171
|
+
- Pre-push sync: remote ahead 1 commit, rebase sạch
|
|
172
|
+
- Verify: npm run lint pass
|
|
173
|
+
- Push: git push pass
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Nếu dừng vì conflict:
|
|
177
|
+
|
|
178
|
+
```text
|
|
179
|
+
Đã dừng trước push vì conflict code logic ở src/a.ts.
|
|
180
|
+
Remote có thay đổi mới của người khác trên cùng block logic.
|
|
181
|
+
Cần bạn chọn hướng merge trước khi tiếp tục.
|
|
182
|
+
```
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: security-checklist
|
|
3
|
+
description: "ON-DEMAND ONLY: chỉ kích hoạt khi user explicitly yêu cầu security audit/review (vd 'audit security login flow', 'check OWASP cho API /payment'), HOẶC task có chạm auth/payment/PII và bạn muốn tự kiểm tra trước commit. KHÔNG tự trigger trong quy trình bình thường (v1.5.0 đã bỏ Bước 5 Security khỏi core process)."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Security Checklist (ON-DEMAND)
|
|
7
|
+
|
|
8
|
+
Skill này **KHÔNG** nằm trong quy trình core (v1.5.0+). Chỉ dùng khi:
|
|
9
|
+
|
|
10
|
+
- User explicit yêu cầu: "audit security X", "review OWASP cho Y", "check secrets".
|
|
11
|
+
- Bạn (AI) chủ động muốn check trước commit nếu task chạm auth/payment/PII/admin/permission.
|
|
12
|
+
- Trước major release.
|
|
13
|
+
|
|
14
|
+
Có thể delegate sang sub-agent `security-auditor` để có audit độc lập.
|
|
15
|
+
|
|
16
|
+
## Checklist 10 mục (OWASP Top 10 mapping)
|
|
17
|
+
|
|
18
|
+
Áp dụng các mục liên quan đến task hiện tại. Tick từng mục:
|
|
19
|
+
|
|
20
|
+
### A01 — Broken Access Control
|
|
21
|
+
- [ ] Mọi endpoint có middleware authn check
|
|
22
|
+
- [ ] Authz check (role/permission) đúng resource owner
|
|
23
|
+
- [ ] Không trả về data của user khác trong response
|
|
24
|
+
- [ ] IDOR: sequential ID không cho enumerate
|
|
25
|
+
|
|
26
|
+
### A02 — Cryptographic Failures
|
|
27
|
+
- [ ] HTTPS only (HSTS header)
|
|
28
|
+
- [ ] Password hash bằng bcrypt/argon2 (KHÔNG md5/sha1)
|
|
29
|
+
- [ ] JWT dùng RS256/ES256 (HS256 chỉ khi shared secret đảm bảo)
|
|
30
|
+
- [ ] Sensitive data không log
|
|
31
|
+
|
|
32
|
+
### A03 — Injection
|
|
33
|
+
- [ ] SQL: dùng parameterized query / ORM (không string concat)
|
|
34
|
+
- [ ] NoSQL: validate operator (`$where`, `$ne` blacklist)
|
|
35
|
+
- [ ] OS command: dùng `execFile` thay `exec`, escape arg
|
|
36
|
+
- [ ] HTML output: escape qua framework (React, Vue auto-escape)
|
|
37
|
+
|
|
38
|
+
### A04 — Insecure Design
|
|
39
|
+
- [ ] Rate limit ở endpoint sensitive (login, password reset)
|
|
40
|
+
- [ ] Account lockout sau N lần fail
|
|
41
|
+
- [ ] Workflow business logic không bypass được
|
|
42
|
+
|
|
43
|
+
### A05 — Security Misconfiguration
|
|
44
|
+
- [ ] Default credentials đã đổi
|
|
45
|
+
- [ ] Stack trace KHÔNG lộ trong production
|
|
46
|
+
- [ ] CORS không `*` cho endpoint có credentials
|
|
47
|
+
- [ ] Security headers: CSP, X-Frame-Options, X-Content-Type-Options
|
|
48
|
+
|
|
49
|
+
### A06 — Vulnerable Components
|
|
50
|
+
- [ ] `npm audit` / `pip-audit` / `cargo audit` không có HIGH/CRITICAL
|
|
51
|
+
- [ ] Dependencies cập nhật trong 6 tháng
|
|
52
|
+
|
|
53
|
+
### A07 — Identification and Authentication Failures
|
|
54
|
+
- [ ] MFA cho admin
|
|
55
|
+
- [ ] Session timeout hợp lý
|
|
56
|
+
- [ ] Logout invalidate token thật sự (server-side)
|
|
57
|
+
- [ ] Password policy: min 8 char, không common password
|
|
58
|
+
|
|
59
|
+
### A08 — Software and Data Integrity Failures
|
|
60
|
+
- [ ] CI/CD pipeline pin version dependency
|
|
61
|
+
- [ ] Subresource Integrity cho external scripts
|
|
62
|
+
- [ ] Verify webhook signature (Stripe, GitHub, etc.)
|
|
63
|
+
|
|
64
|
+
### A09 — Security Logging and Monitoring Failures
|
|
65
|
+
- [ ] Log auth events (login success/fail)
|
|
66
|
+
- [ ] Log access control fail
|
|
67
|
+
- [ ] Log không chứa secrets/PII
|
|
68
|
+
|
|
69
|
+
### A10 — Server-Side Request Forgery (SSRF)
|
|
70
|
+
- [ ] Không cho user input URL để fetch không validate
|
|
71
|
+
- [ ] Block private IP range (10.x, 192.168.x, 169.254.x)
|
|
72
|
+
|
|
73
|
+
## Secrets scan command
|
|
74
|
+
|
|
75
|
+
Chạy ở root project trước commit:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Tìm các pattern khả nghi
|
|
79
|
+
rg -i "(api[_-]?key|secret|token|password|private[_-]?key)\s*[:=]\s*['\"][^'\"]{8,}" \
|
|
80
|
+
--glob '!node_modules' --glob '!*.lock' --glob '!.env*' --glob '!CHANGELOG.md'
|
|
81
|
+
|
|
82
|
+
# Check .env không vô tình commit
|
|
83
|
+
git ls-files | grep -E '\.env$|\.env\.local$|\.env\.production$' && echo "WARNING: .env committed!"
|
|
84
|
+
|
|
85
|
+
# Test bypass token check
|
|
86
|
+
rg "TEST_BYPASS_TOKEN\s*=" --glob '!.env.test' --glob '!*.tpl'
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Khi nào delegate sang `security-auditor`
|
|
90
|
+
|
|
91
|
+
- Task ảnh hưởng auth/authz
|
|
92
|
+
- Task xử lý payment/PII
|
|
93
|
+
- Task expose endpoint mới ra internet
|
|
94
|
+
- Trước mỗi major release
|
|
95
|
+
|
|
96
|
+
Sub-agent sẽ chạy checklist độc lập + scan secrets, output report ngắn để parent review.
|
|
97
|
+
|
|
98
|
+
## Output bắt buộc
|
|
99
|
+
|
|
100
|
+
Vào section `## Security Check` của plan:
|
|
101
|
+
|
|
102
|
+
```markdown
|
|
103
|
+
## Security Check
|
|
104
|
+
|
|
105
|
+
**Date**: YYYY-MM-DD HH:mm
|
|
106
|
+
**Checked by**: parent | security-auditor
|
|
107
|
+
|
|
108
|
+
- [x] A01: ... (notes)
|
|
109
|
+
- [x] A03: ... (notes)
|
|
110
|
+
- [N/A] A10: không có user-input URL
|
|
111
|
+
|
|
112
|
+
**Issues found**:
|
|
113
|
+
- (none) | <description + plan to fix>
|
|
114
|
+
|
|
115
|
+
**Secrets scan**: PASS | FAIL <details>
|
|
116
|
+
```
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: test-strategy
|
|
3
|
+
description: "ON-DEMAND ONLY: chỉ kích hoạt khi user explicitly yêu cầu viết test (vd 'viết unit test cho X', 'add E2E cho login flow'), HOẶC khi bạn (AI) cần TDD-first cho bug fix khó. KHÔNG tự trigger trong quy trình bình thường (v1.5.0 đã bỏ Bước 6 Test khỏi core process). Phân tầng test: Unit ~70%, Integration ~20%, E2E ~10% với tools mặc định theo stack (Vitest/Jest/Pytest/Go test)."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Test Strategy
|
|
7
|
+
|
|
8
|
+
## Test Pyramid
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
/\
|
|
12
|
+
/E2\ ~10% — chậm, đắt, kiểm tra user flow thật
|
|
13
|
+
/----\
|
|
14
|
+
/ Inte \ ~20% — kiểm tra modules giao tiếp với nhau
|
|
15
|
+
/--------\
|
|
16
|
+
/ Unit \ ~70% — nhanh, kiểm tra function/class riêng lẻ
|
|
17
|
+
/------------\
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Chọn tool theo stack
|
|
21
|
+
|
|
22
|
+
| Stack | Unit | Integration | E2E | Coverage |
|
|
23
|
+
|---|---|---|---|---|
|
|
24
|
+
| Node/TS | Vitest (default) hoặc Jest | Vitest + supertest/msw | Playwright | c8 (vitest), jest --coverage |
|
|
25
|
+
| Python | Pytest | Pytest + httpx/responses | Playwright (python) | pytest-cov |
|
|
26
|
+
| Go | go test | go test (build tag integration) | Playwright (gọi từ Go binary) | go test -cover |
|
|
27
|
+
| Rust | cargo test | cargo test (integration_tests/) | Playwright | tarpaulin |
|
|
28
|
+
|
|
29
|
+
Phát hiện stack qua: `package.json`, `pyproject.toml`/`requirements.txt`, `go.mod`, `Cargo.toml`.
|
|
30
|
+
|
|
31
|
+
## Khi nào dùng loại nào
|
|
32
|
+
|
|
33
|
+
### Unit test (~70%)
|
|
34
|
+
|
|
35
|
+
- ✅ Logic thuần (validation, transform, computation)
|
|
36
|
+
- ✅ Edge cases của 1 function
|
|
37
|
+
- ✅ Pure functions, hooks không side-effect
|
|
38
|
+
- ❌ Test framework code (React component render → integration thì hơn)
|
|
39
|
+
|
|
40
|
+
### Integration test (~20%)
|
|
41
|
+
|
|
42
|
+
- ✅ Module A gọi module B với data thật
|
|
43
|
+
- ✅ API endpoint với mock DB hoặc test DB
|
|
44
|
+
- ✅ React component với providers (router, store)
|
|
45
|
+
- ❌ Test browser behavior thật → E2E
|
|
46
|
+
|
|
47
|
+
### E2E test (~10%)
|
|
48
|
+
|
|
49
|
+
- ✅ User flow critical (đăng ký, đăng nhập, checkout)
|
|
50
|
+
- ✅ Cross-browser/multi-viewport behavior
|
|
51
|
+
- ✅ Smoke test sau deploy
|
|
52
|
+
- ❌ Test mọi edge case → quá chậm, dùng unit
|
|
53
|
+
|
|
54
|
+
## Quy tắc viết test
|
|
55
|
+
|
|
56
|
+
### 1. AAA Pattern (Arrange / Act / Assert)
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
test('parseDate trả null khi input rỗng', () => {
|
|
60
|
+
// Arrange
|
|
61
|
+
const input = '';
|
|
62
|
+
// Act
|
|
63
|
+
const result = parseDate(input);
|
|
64
|
+
// Assert
|
|
65
|
+
expect(result).toBeNull();
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 2. Test name = behavior
|
|
70
|
+
|
|
71
|
+
- ✅ "trả null khi input rỗng"
|
|
72
|
+
- ✅ "throw khi user không có quyền admin"
|
|
73
|
+
- ❌ "test parseDate" (vô nghĩa)
|
|
74
|
+
- ❌ "should work" (không nói được gì)
|
|
75
|
+
|
|
76
|
+
### 3. Một test → một behavior
|
|
77
|
+
|
|
78
|
+
Nhiều `expect` OK nếu cùng một behavior. Nhưng nhiều behavior → tách test.
|
|
79
|
+
|
|
80
|
+
### 4. Coverage không phải metric duy nhất
|
|
81
|
+
|
|
82
|
+
- Aim: branch coverage **≥80%** cho code business
|
|
83
|
+
- Aim: branch coverage **≥95%** cho code security/payment
|
|
84
|
+
- KHÔNG aim 100% mù quáng — test trivial getters tốn công vô ích.
|
|
85
|
+
|
|
86
|
+
## Bug fix workflow — TDD bắt buộc (Karpathy #4)
|
|
87
|
+
|
|
88
|
+
> Tham khảo: [`templates/karpathy-reference.md`](../../templates/karpathy-reference.md) — nguyên tắc "Goal-Driven Execution".
|
|
89
|
+
|
|
90
|
+
Cho **bug fix** (KHÔNG áp dụng cho feature mới — feature mới linh hoạt theo workflow chuẩn bên dưới):
|
|
91
|
+
|
|
92
|
+
1. **Viết test reproducing bug TRƯỚC** — test phải FAIL.
|
|
93
|
+
- Test name = mô tả bug. Vd: `test('parseDate trả null khi input là "0"', ...)`.
|
|
94
|
+
- Chạy → confirm RED (fail đúng symptom user báo).
|
|
95
|
+
2. **Commit test** riêng với message `test: reproduce <bug-summary>`.
|
|
96
|
+
3. **Fix code**.
|
|
97
|
+
4. Chạy lại → confirm GREEN.
|
|
98
|
+
5. **Commit fix** riêng với message `fix: <bug-summary>`.
|
|
99
|
+
6. (Optional) Refactor → vẫn GREEN.
|
|
100
|
+
|
|
101
|
+
### Lý do TDD-first cho bug
|
|
102
|
+
|
|
103
|
+
- Bug pass test sau fix nhưng KHÔNG có test reproducing → không chứng minh được bug exact đã fix → dễ regression.
|
|
104
|
+
- 2 commit riêng (test + fix) giúp `git log` tự document được bug + fix tương ứng.
|
|
105
|
+
- Reviewer thấy ngay test đã fail trước khi fix.
|
|
106
|
+
|
|
107
|
+
### Output bắt buộc trong plan (cho bug fix)
|
|
108
|
+
|
|
109
|
+
- [ ] Test reproducing bug đã viết và confirm FAIL trước khi fix → **Verify**: `git log --oneline | grep '^test: reproduce'`
|
|
110
|
+
- [ ] Test PASS sau khi fix → **Verify**: command chạy test cụ thể trả 0 fail
|
|
111
|
+
- [ ] 2 commit riêng (test + fix), không squash trước review
|
|
112
|
+
|
|
113
|
+
## Workflow Bước 6a (feature mới)
|
|
114
|
+
|
|
115
|
+
1. Đọc plan → xác định AC cần verify.
|
|
116
|
+
2. Mỗi AC ≥ 1 test (unit/integration/E2E tùy nature).
|
|
117
|
+
3. Viết test trước hoặc sau code đều OK (cho **feature mới**), nhưng phải xanh trước khi sang Bước 7.
|
|
118
|
+
4. Chạy:
|
|
119
|
+
- JS/TS: `npx vitest run --coverage` (hoặc `jest --coverage`)
|
|
120
|
+
- Python: `pytest --cov`
|
|
121
|
+
- Go: `go test ./... -cover`
|
|
122
|
+
5. Ghi coverage % vào plan section "Test Plan".
|
|
123
|
+
6. Lưu kết quả vào `.cursor/.last-test-result` (hook `verify-completion` đọc):
|
|
124
|
+
```json
|
|
125
|
+
{"status": "pass", "timestamp": "2026-04-23T14:30:00Z", "coverage": 87.5}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Khi delegate sang `qa-autonomous`
|
|
129
|
+
|
|
130
|
+
Nên delegate khi:
|
|
131
|
+
- Test loop có thể nhiều iteration (>3 lần fix)
|
|
132
|
+
- Test log dài, sẽ nhồi context parent
|
|
133
|
+
- Cần debug deeper với nhiều variations
|
|
134
|
+
|
|
135
|
+
Sub-agent tự loop test-fix-retest, chỉ báo cáo khi pass hoặc fail >3 lần.
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ui-test-loop
|
|
3
|
+
description: "ON-DEMAND ONLY: chỉ kích hoạt khi user explicitly yêu cầu UI test loop (vd 'test UI mobile + tablet + desktop, fix lỗi rồi retest', 'visual regression test cho component X'). KHÔNG tự trigger trong quy trình bình thường (v1.5.0 đã bỏ hook ui-checkpoint). Workflow loop test-fix-retest cho UI/UX bằng Playwright. Hard limit 5 vòng trước khi escalate user."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# UI Test Loop với Checkpoint
|
|
7
|
+
|
|
8
|
+
Workflow chuẩn để **AI tự test UI, tự fix, tự retest** đến khi đạt yêu cầu — nhưng có **safety checkpoint** mỗi 2 vòng để user duyệt và tránh đi sai hướng quá lâu.
|
|
9
|
+
|
|
10
|
+
## Workflow
|
|
11
|
+
|
|
12
|
+
```mermaid
|
|
13
|
+
flowchart LR
|
|
14
|
+
Start[Đọc AC từ plan] --> Sess[Tạo .ui-session.json]
|
|
15
|
+
Sess --> Write[Viết Playwright test theo AC]
|
|
16
|
+
Write --> Run[Chạy multi-viewport]
|
|
17
|
+
Run --> Pass{Pass?}
|
|
18
|
+
Pass -->|Có| Done[Cleanup .ui-session.json + báo cáo]
|
|
19
|
+
Pass -->|Không| Fix[Đọc lỗi, sửa code]
|
|
20
|
+
Fix --> Inc[iter++]
|
|
21
|
+
Inc --> Limit{iter >= 10?}
|
|
22
|
+
Limit -->|Có| Escalate[Escalate user]
|
|
23
|
+
Limit -->|Không| Run
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Hook `subagentStop` (`ui-checkpoint.sh`) tự động kích hoạt **mỗi 2 vòng subagent stop** — buộc subagent show screenshot trước khi tiếp tục.
|
|
27
|
+
|
|
28
|
+
## Bước thực hiện (cho `ui-tester` subagent)
|
|
29
|
+
|
|
30
|
+
### 1. Khởi tạo session
|
|
31
|
+
|
|
32
|
+
Tạo file `.cursor/.ui-session.json` ở workspace để hook biết kích hoạt:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
mkdir -p .cursor
|
|
36
|
+
cat > .cursor/.ui-session.json <<EOF
|
|
37
|
+
{
|
|
38
|
+
"plan_slug": "$(basename "$PLAN_FILE" .md)",
|
|
39
|
+
"started_at": "$(date -Iseconds)",
|
|
40
|
+
"iter": 0
|
|
41
|
+
}
|
|
42
|
+
EOF
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Set env:
|
|
46
|
+
```bash
|
|
47
|
+
export PLAN_SLUG="2026-04-23-add-google-login"
|
|
48
|
+
export UI_ITER=0
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 2. Đọc Acceptance Criteria
|
|
52
|
+
|
|
53
|
+
Mở plan file → tìm section `## Acceptance Criteria`. Mỗi AC = 1 hoặc nhiều test case.
|
|
54
|
+
|
|
55
|
+
### 3. Viết test
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { test, expect } from '@playwright/test';
|
|
59
|
+
|
|
60
|
+
// AC1: Button "Đăng nhập Google" hiển thị icon + text đúng
|
|
61
|
+
test('AC1: login button visible with correct content', async ({ page }, testInfo) => {
|
|
62
|
+
await page.goto('/login');
|
|
63
|
+
|
|
64
|
+
const btn = page.getByRole('button', { name: /Đăng nhập Google/i });
|
|
65
|
+
await expect(btn).toBeVisible();
|
|
66
|
+
await expect(btn.locator('img[alt*="Google"]')).toBeVisible();
|
|
67
|
+
|
|
68
|
+
await page.screenshot({
|
|
69
|
+
path: `tests/screenshots/${process.env.PLAN_SLUG}/iter-${process.env.UI_ITER}/${testInfo.project.name}-ac1.png`,
|
|
70
|
+
fullPage: true,
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// AC2: Click button mở popup OAuth
|
|
75
|
+
test('AC2: click opens OAuth popup', async ({ page, context }) => {
|
|
76
|
+
await page.goto('/login');
|
|
77
|
+
const popupPromise = context.waitForEvent('page');
|
|
78
|
+
await page.getByRole('button', { name: /Đăng nhập Google/i }).click();
|
|
79
|
+
const popup = await popupPromise;
|
|
80
|
+
await expect(popup).toHaveURL(/accounts\.google\.com/);
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 4. Chạy test multi-viewport
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
UI_ITER=$((UI_ITER + 1)) npx playwright test login.spec.ts
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Lưu kết quả:
|
|
91
|
+
```bash
|
|
92
|
+
STATUS=$([[ $? == 0 ]] && echo "pass" || echo "fail")
|
|
93
|
+
echo "{\"status\": \"$STATUS\", \"timestamp\": \"$(date -Iseconds)\", \"tool\": \"playwright\", \"iter\": $UI_ITER}" > .cursor/.last-test-result
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 5. Loop quyết định
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
if test pass:
|
|
100
|
+
→ cleanup .ui-session.json
|
|
101
|
+
→ báo cáo "✅ UI tests pass sau N vòng. Screenshots tại tests/screenshots/<slug>/iter-N/"
|
|
102
|
+
→ end
|
|
103
|
+
|
|
104
|
+
if test fail:
|
|
105
|
+
→ đọc lỗi từ tests/playwright-report/
|
|
106
|
+
→ debug: ưu tiên fix CSS/component, KHÔNG sửa test cho match code sai
|
|
107
|
+
→ UI_ITER++
|
|
108
|
+
→ loop lại bước 4
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### 6. Checkpoint (tự động qua hook)
|
|
112
|
+
|
|
113
|
+
Hook `ui-checkpoint.sh` chạy ở `subagentStop`. Khi `loop_count` chia hết cho 2 → trả `followup_message` buộc subagent:
|
|
114
|
+
- Show screenshot mới nhất ở `tests/screenshots/<slug>/iter-<n>/`
|
|
115
|
+
- Mô tả ngắn những gì đã thử và kết quả
|
|
116
|
+
- **Dừng và chờ user duyệt** trước khi loop tiếp
|
|
117
|
+
|
|
118
|
+
User trả lời:
|
|
119
|
+
- "OK tiếp tục" → loop tiếp
|
|
120
|
+
- "Sửa thêm X" → adjust và loop
|
|
121
|
+
- "Stop, tôi tự xử" → cleanup `.ui-session.json` và end
|
|
122
|
+
|
|
123
|
+
### 7. Hard limit & Escalate
|
|
124
|
+
|
|
125
|
+
Sau **5 checkpoint** (≈ 10 iteration), hook không gửi followup nữa, subagent phải:
|
|
126
|
+
- Tóm tắt tất cả những gì đã thử (mỗi iter ngắn 1 dòng)
|
|
127
|
+
- List screenshots quan trọng để user xem
|
|
128
|
+
- Hypothesis: vì sao chưa pass
|
|
129
|
+
- Đề xuất: cần user can thiệp gì
|
|
130
|
+
|
|
131
|
+
## Quy tắc fix
|
|
132
|
+
|
|
133
|
+
1. **Ưu tiên fix code, không fix test** — trừ khi test sai assertion.
|
|
134
|
+
2. **Fix cause, không fix symptom** — nếu CSS overflow, sửa root cause (flex layout) thay vì hack `overflow: hidden`.
|
|
135
|
+
3. **Một thay đổi 1 lần** — không fix nhiều thứ cùng lúc, khó debug.
|
|
136
|
+
4. **Verify với 1 viewport trước**, OK mới chạy full multi-viewport.
|
|
137
|
+
|
|
138
|
+
## Cleanup khi xong
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
rm -f .cursor/.ui-session.json
|
|
142
|
+
unset PLAN_SLUG UI_ITER
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Output bắt buộc
|
|
146
|
+
|
|
147
|
+
Trong báo cáo cuối:
|
|
148
|
+
|
|
149
|
+
```markdown
|
|
150
|
+
## UI Test Result
|
|
151
|
+
|
|
152
|
+
- Plan: `plans/<plan-slug>.md`
|
|
153
|
+
- Iterations: N
|
|
154
|
+
- Checkpoints: M (user duyệt qua các vòng 2, 4, ...)
|
|
155
|
+
- Final status: PASS | FAIL (escalated)
|
|
156
|
+
- Screenshots: `tests/screenshots/<slug>/iter-<final>/`
|
|
157
|
+
- Coverage AC:
|
|
158
|
+
- [x] AC1: ...
|
|
159
|
+
- [x] AC2: ...
|
|
160
|
+
- [ ] AC3: ... (escalate vì <reason>)
|
|
161
|
+
```
|
package/src/cli.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const { setPkgRoot, isInstalled } = require("./lib/paths")
|
|
2
|
+
const { getLocalVersion } = require("./lib/version")
|
|
3
|
+
|
|
4
|
+
async function cli(pkgRoot) {
|
|
5
|
+
setPkgRoot(pkgRoot)
|
|
6
|
+
|
|
7
|
+
const cmd = process.argv[2]
|
|
8
|
+
const args = process.argv.slice(3)
|
|
9
|
+
|
|
10
|
+
switch (cmd) {
|
|
11
|
+
case "install":
|
|
12
|
+
return require("./commands/install").run(args)
|
|
13
|
+
case "init":
|
|
14
|
+
return require("./commands/init").run(args)
|
|
15
|
+
case "update":
|
|
16
|
+
return require("./commands/update").run(args)
|
|
17
|
+
case "uninstall":
|
|
18
|
+
return require("./commands/uninstall").run(args)
|
|
19
|
+
case "check":
|
|
20
|
+
case "--check":
|
|
21
|
+
return require("./commands/check").run(args)
|
|
22
|
+
case "--version":
|
|
23
|
+
case "-v":
|
|
24
|
+
console.log(`v${getLocalVersion() || "unknown"}`)
|
|
25
|
+
return
|
|
26
|
+
case "--help":
|
|
27
|
+
case "-h":
|
|
28
|
+
default:
|
|
29
|
+
return showHelp()
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function showHelp() {
|
|
34
|
+
const v = getLocalVersion() || "unknown"
|
|
35
|
+
|
|
36
|
+
console.log(`
|
|
37
|
+
OpenMoneta Dev Kit v${v}
|
|
38
|
+
──────────────────────────────────────────────
|
|
39
|
+
Biến Cursor IDE / OpenCode thành team developer hoàn chỉnh.
|
|
40
|
+
|
|
41
|
+
openmoneta install Cài vào Cursor (~/.cursor/)
|
|
42
|
+
openmoneta install --opencode Cài vào OpenCode (~/.config/opencode/)
|
|
43
|
+
openmoneta install --yes Cài không hỏi xác nhận
|
|
44
|
+
|
|
45
|
+
openmoneta init Khởi tạo project (tạo AGENTS.md, docs/INDEX.md, plans/INDEX.md)
|
|
46
|
+
openmoneta init --opencode Init cho OpenCode
|
|
47
|
+
openmoneta init /path/to/proj Init project ở path chỉ định
|
|
48
|
+
|
|
49
|
+
openmoneta update Update global + sync project hiện tại
|
|
50
|
+
openmoneta update --yes Auto-sync, skip nếu đã latest
|
|
51
|
+
openmoneta update --check Chỉ kiểm tra có version mới không
|
|
52
|
+
|
|
53
|
+
openmoneta check Kiểm tra version mới nhất từ npm
|
|
54
|
+
|
|
55
|
+
openmoneta uninstall Gỡ cài đặt
|
|
56
|
+
|
|
57
|
+
Trạng thái: Cursor: ${isInstalled("cursor") ? "đã cài" : "chưa cài"} | OpenCode: ${isInstalled("opencode") ? "đã cài" : "chưa cài"}
|
|
58
|
+
|
|
59
|
+
Docs: https://github.com/rapperkey/OpenMoneta-Dev-Kit
|
|
60
|
+
`)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
module.exports = { cli }
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const { getLocalVersion, getLatestVersion } = require("../lib/version")
|
|
2
|
+
|
|
3
|
+
async function run(args) {
|
|
4
|
+
const local = getLocalVersion()
|
|
5
|
+
const latest = getLatestVersion()
|
|
6
|
+
|
|
7
|
+
console.log(`\n OpenMoneta Dev Kit`)
|
|
8
|
+
console.log(` ─────────────────`)
|
|
9
|
+
console.log(` Local : v${local || "unknown"}`)
|
|
10
|
+
console.log(` Latest: v${latest || "không xác định (không có mạng?)"}`)
|
|
11
|
+
|
|
12
|
+
if (!latest) {
|
|
13
|
+
console.log(`\n ❓ Không thể kiểm tra version mới nhất. Kiểm tra kết nối mạng.`)
|
|
14
|
+
return
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (!local) {
|
|
18
|
+
console.log(`\n 📦 Chưa cài đặt. Chạy: openmoneta install`)
|
|
19
|
+
return
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (local === latest) {
|
|
23
|
+
console.log(`\n ✅ Đã ở phiên bản mới nhất.`)
|
|
24
|
+
} else {
|
|
25
|
+
console.log(`\n 🔔 Có phiên bản mới: v${local} → v${latest}`)
|
|
26
|
+
console.log(` Chạy: openmoneta update`)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = { run }
|