openmoneta-dev-kit 2.0.1 → 2.1.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/VERSION +1 -1
- package/hooks/inject-process-context.sh +4 -0
- package/opencode/plugins/openmoneta-guard.ts +8 -1
- package/package.json +1 -1
- package/scripts/docs-audit.sh +125 -1
- package/skills/docs-maintenance/SKILL.md +63 -3
- package/src/cli.js +1 -0
- package/src/commands/docs.js +5 -1
- package/src/commands/update.js +24 -8
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.0
|
|
1
|
+
2.1.0
|
|
@@ -16,6 +16,10 @@ INPUT_JSON=$(cat 2>/dev/null || echo '{}')
|
|
|
16
16
|
register_project_cursor() {
|
|
17
17
|
local ws="$1"
|
|
18
18
|
[[ -n "$ws" && -f "$ws/docs/INDEX.md" ]] || return 0
|
|
19
|
+
# Bỏ qua repo nguồn của chính kit (tránh update --all-projects đè docs bespoke).
|
|
20
|
+
if [[ -f "$ws/package.json" ]] && grep -q '"name": *"openmoneta-dev-kit"' "$ws/package.json" 2>/dev/null; then
|
|
21
|
+
return 0
|
|
22
|
+
fi
|
|
19
23
|
[[ -f "$HOME/.cursor/.openmoneta-version" ]] || return 0
|
|
20
24
|
local reg="$HOME/.cursor/.openmoneta-projects"
|
|
21
25
|
local abs
|
|
@@ -454,6 +454,13 @@ function clearVerifyLoop(root: string) {
|
|
|
454
454
|
function registerProjectOpenCode(root: string) {
|
|
455
455
|
try {
|
|
456
456
|
if (!fs.existsSync(path.join(root, "docs", "INDEX.md"))) return
|
|
457
|
+
// Bỏ qua repo nguồn của chính kit (tránh update --all-projects đè docs bespoke).
|
|
458
|
+
try {
|
|
459
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"))
|
|
460
|
+
if (pkg && pkg.name === "openmoneta-dev-kit") return
|
|
461
|
+
} catch {
|
|
462
|
+
// không có package.json hợp lệ → không phải repo nguồn, tiếp tục
|
|
463
|
+
}
|
|
457
464
|
const home = process.env.HOME || process.env.USERPROFILE || ""
|
|
458
465
|
const xdg = process.env.XDG_CONFIG_HOME || path.join(home, ".config")
|
|
459
466
|
const ocHome = path.join(xdg, "opencode")
|
|
@@ -511,7 +518,7 @@ export const OpenMonetaGuard = async (ctx: GuardContext) => {
|
|
|
511
518
|
{
|
|
512
519
|
loaded_at: new Date().toISOString(),
|
|
513
520
|
root,
|
|
514
|
-
version: "2.0.
|
|
521
|
+
version: "2.0.2",
|
|
515
522
|
load_count: globalState[globalKey],
|
|
516
523
|
},
|
|
517
524
|
null,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openmoneta-dev-kit",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "OpenMoneta Dev Kit — Biến Cursor IDE / OpenCode thành team developer hoàn chỉnh với quy trình 6 bước, adaptive planning, hooks enforcement, và token-aware doc routing",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cursor",
|
package/scripts/docs-audit.sh
CHANGED
|
@@ -9,8 +9,11 @@
|
|
|
9
9
|
# bash docs-audit.sh --report # như trên
|
|
10
10
|
# bash docs-audit.sh --worklist # TSV: <slug>\t<docsPath>\t<sourcePath>\t<missing|stale|ok>
|
|
11
11
|
# bash docs-audit.sh --validate # liệt kê GHOST (doc trỏ source/module không tồn tại)
|
|
12
|
+
# bash docs-audit.sh --decisions # audit ADR (docs/decisions/): legacy / index-sync / supersede / module-link
|
|
13
|
+
# bash docs-audit.sh --decisions-worklist # TSV: <issue>\t<path> cho skill xử lý
|
|
12
14
|
#
|
|
13
15
|
# Stale detection: so commit mới nhất của source vs README (git); fallback mtime filesystem.
|
|
16
|
+
# ADR audit chỉ MECHANICAL (không bịa nội dung): phát hiện nợ ADR, không tự sửa.
|
|
14
17
|
|
|
15
18
|
set -uo pipefail
|
|
16
19
|
|
|
@@ -18,8 +21,10 @@ MODE="report"
|
|
|
18
21
|
case "${1:-}" in
|
|
19
22
|
--worklist) MODE="worklist" ;;
|
|
20
23
|
--validate) MODE="validate" ;;
|
|
24
|
+
--decisions) MODE="decisions" ;;
|
|
25
|
+
--decisions-worklist) MODE="decisions-worklist" ;;
|
|
21
26
|
--report|"") MODE="report" ;;
|
|
22
|
-
*) echo "[!] Mode không hợp lệ: $1 (dùng --report|--worklist|--validate)" >&2; exit 2 ;;
|
|
27
|
+
*) echo "[!] Mode không hợp lệ: $1 (dùng --report|--worklist|--validate|--decisions|--decisions-worklist)" >&2; exit 2 ;;
|
|
23
28
|
esac
|
|
24
29
|
|
|
25
30
|
GIT_OK=0
|
|
@@ -100,6 +105,125 @@ slug_to_source() {
|
|
|
100
105
|
fi
|
|
101
106
|
}
|
|
102
107
|
|
|
108
|
+
# ---------- MODE: decisions / decisions-worklist (ADR audit) ----------
|
|
109
|
+
if [[ "$MODE" == "decisions" || "$MODE" == "decisions-worklist" ]]; then
|
|
110
|
+
WL=0; [[ "$MODE" == "decisions-worklist" ]] && WL=1
|
|
111
|
+
DDIR="docs/decisions"
|
|
112
|
+
|
|
113
|
+
emit() { # <issue> <path-or-msg>
|
|
114
|
+
if [[ "$WL" -eq 1 ]]; then printf '%s\t%s\n' "$1" "$2"; else printf ' %-16s %s\n' "$1" "$2"; fi
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
# ADR đúng format = NNNN-slug.md (NNNN sequential). Loại trừ file date YYYY-MM-DD-slug.md (legacy).
|
|
118
|
+
is_adr_file() {
|
|
119
|
+
local b="$1"
|
|
120
|
+
[[ "$b" =~ ^[0-9][0-9][0-9][0-9]-.*\.md$ ]] || return 1
|
|
121
|
+
[[ "$b" =~ ^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]- ]] && return 1
|
|
122
|
+
return 0
|
|
123
|
+
}
|
|
124
|
+
adr_files() { # in ra path các ADR đúng format
|
|
125
|
+
local f b
|
|
126
|
+
for f in "$DDIR"/[0-9][0-9][0-9][0-9]-*.md; do
|
|
127
|
+
[[ -f "$f" ]] || continue
|
|
128
|
+
b=$(basename "$f")
|
|
129
|
+
is_adr_file "$b" && echo "$f"
|
|
130
|
+
done
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if [[ ! -d "$DDIR" ]]; then
|
|
134
|
+
[[ "$WL" -eq 0 ]] && echo " Project chưa có layer Decision Memory (docs/decisions/). Bỏ qua ADR audit."
|
|
135
|
+
exit 0
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
n_legacy=0; n_orphan=0; n_ghost=0; n_supersede=0; n_modlink=0
|
|
139
|
+
|
|
140
|
+
# (1) Legacy: file trong docs/decisions/ không theo NNNN-slug.md (ngoài INDEX.md)
|
|
141
|
+
for f in "$DDIR"/*; do
|
|
142
|
+
[[ -f "$f" ]] || continue
|
|
143
|
+
b=$(basename "$f")
|
|
144
|
+
[[ "$b" == "INDEX.md" ]] && continue
|
|
145
|
+
if ! is_adr_file "$b"; then
|
|
146
|
+
emit "LEGACY" "$f"
|
|
147
|
+
n_legacy=$((n_legacy+1))
|
|
148
|
+
fi
|
|
149
|
+
done
|
|
150
|
+
|
|
151
|
+
# (2a) ADR file đúng format nhưng KHÔNG có trong bảng INDEX
|
|
152
|
+
while IFS= read -r f; do
|
|
153
|
+
[[ -z "$f" ]] && continue
|
|
154
|
+
b=$(basename "$f")
|
|
155
|
+
if [[ -f "$DDIR/INDEX.md" ]] && grep -qF "$b" "$DDIR/INDEX.md" 2>/dev/null; then :; else
|
|
156
|
+
emit "NOT-IN-INDEX" "$f"
|
|
157
|
+
n_orphan=$((n_orphan+1))
|
|
158
|
+
fi
|
|
159
|
+
done < <(adr_files)
|
|
160
|
+
|
|
161
|
+
# (2b) INDEX trỏ ADR file đã mất
|
|
162
|
+
if [[ -f "$DDIR/INDEX.md" ]]; then
|
|
163
|
+
while IFS= read -r ref; do
|
|
164
|
+
[[ -z "$ref" ]] && continue
|
|
165
|
+
[[ -f "$DDIR/$ref" ]] || { emit "INDEX-GHOST" "$DDIR/INDEX.md → $ref"; n_ghost=$((n_ghost+1)); }
|
|
166
|
+
done < <(grep -oE '[0-9]{4}-[A-Za-z0-9._-]+\.md' "$DDIR/INDEX.md" 2>/dev/null | sort -u)
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
# (3) Supersede integrity
|
|
170
|
+
while IFS= read -r f; do
|
|
171
|
+
[[ -z "$f" ]] && continue
|
|
172
|
+
sup=$(grep -oE '^\*\*Supersedes\*\*:[[:space:]]*ADR-[0-9]{4}' "$f" 2>/dev/null | grep -oE '[0-9]{4}' | head -1)
|
|
173
|
+
[[ -z "$sup" ]] && continue
|
|
174
|
+
target=$(ls "$DDIR/${sup}-"*.md 2>/dev/null | head -1)
|
|
175
|
+
if [[ -z "$target" ]]; then
|
|
176
|
+
emit "SUPERSEDE-MISS" "$(basename "$f") → ADR-$sup không tồn tại"
|
|
177
|
+
n_supersede=$((n_supersede+1))
|
|
178
|
+
elif ! grep -qE '^\*\*Status\*\*:.*Superseded by' "$target" 2>/dev/null; then
|
|
179
|
+
emit "SUPERSEDE-OPEN" "$(basename "$target") chưa đổi Status sang 'Superseded by' (bị $(basename "$f") thay)"
|
|
180
|
+
n_supersede=$((n_supersede+1))
|
|
181
|
+
fi
|
|
182
|
+
done < <(adr_files)
|
|
183
|
+
|
|
184
|
+
# (4) Module link (advisory): ADR khai Module(s) có docs/modules/<slug>/README.md nhưng chưa link ADR
|
|
185
|
+
while IFS= read -r f; do
|
|
186
|
+
[[ -z "$f" ]] && continue
|
|
187
|
+
b=$(basename "$f")
|
|
188
|
+
mods=$(grep -oE '^\*\*Module\(s\)\*\*:.*' "$f" 2>/dev/null | sed -E 's/^\*\*Module\(s\)\*\*:[[:space:]]*//')
|
|
189
|
+
[[ -z "$mods" ]] && continue
|
|
190
|
+
IFS=',' read -ra arr <<< "$mods"
|
|
191
|
+
for m in "${arr[@]}"; do
|
|
192
|
+
slug=$(echo "$m" | sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//')
|
|
193
|
+
[[ -z "$slug" || "$slug" == "—" ]] && continue
|
|
194
|
+
readme="docs/modules/$slug/README.md"
|
|
195
|
+
[[ -f "$readme" ]] || continue
|
|
196
|
+
if ! grep -qF "$b" "$readme" 2>/dev/null; then
|
|
197
|
+
emit "MODLINK" "$readme chưa link $b (Module(s) của ADR)"
|
|
198
|
+
n_modlink=$((n_modlink+1))
|
|
199
|
+
fi
|
|
200
|
+
done
|
|
201
|
+
done < <(adr_files)
|
|
202
|
+
|
|
203
|
+
if [[ "$WL" -eq 1 ]]; then exit 0; fi
|
|
204
|
+
|
|
205
|
+
total=$((n_legacy+n_orphan+n_ghost+n_supersede))
|
|
206
|
+
echo ""
|
|
207
|
+
echo " ADR audit — $(pwd)/$DDIR"
|
|
208
|
+
echo " ──────────────────────────────────────────────────────────────"
|
|
209
|
+
if [[ "$total" -eq 0 && "$n_modlink" -eq 0 ]]; then
|
|
210
|
+
echo " ✓ Sạch: ADR đăng ký đầy đủ trong INDEX, không legacy/ghost, supersede toàn vẹn."
|
|
211
|
+
else
|
|
212
|
+
echo " Tổng: $total vấn đề cần xử lý"
|
|
213
|
+
echo " · $n_legacy legacy (file không theo NNNN-slug.md, chưa đăng ký)"
|
|
214
|
+
echo " · $n_orphan ADR chưa có trong INDEX"
|
|
215
|
+
echo " · $n_ghost INDEX trỏ ADR đã mất"
|
|
216
|
+
echo " · $n_supersede supersede không toàn vẹn"
|
|
217
|
+
[[ "$n_modlink" -gt 0 ]] && echo " · $n_modlink module README chưa link ADR (advisory)"
|
|
218
|
+
echo ""
|
|
219
|
+
echo " → Để MIGRATE legacy (reformat file có sẵn nội dung) + đăng ký INDEX:"
|
|
220
|
+
echo " mở Cursor/OpenCode và yêu cầu chạy skill 'docs-maintenance' (phạm vi ADR)."
|
|
221
|
+
echo " → ADR còn THIẾU cho quyết định CHƯA ghi: dùng skill 'decision-recorder' (cần ngữ cảnh thật, KHÔNG bịa từ code)."
|
|
222
|
+
fi
|
|
223
|
+
echo ""
|
|
224
|
+
exit 0
|
|
225
|
+
fi
|
|
226
|
+
|
|
103
227
|
MODULES=$(collect_modules | sort -u)
|
|
104
228
|
|
|
105
229
|
# ---------- MODE: validate (ghost) ----------
|
|
@@ -1,22 +1,33 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: docs-maintenance
|
|
3
|
-
description: "ON-DEMAND ONLY: dùng khi user yêu cầu tạo docs còn thiếu / backfill module README / refresh docs lỗi thời / audit docs / bảo trì docs dự án
|
|
3
|
+
description: "ON-DEMAND ONLY: dùng khi user yêu cầu tạo docs còn thiếu / backfill module README / refresh docs lỗi thời / audit docs / bảo trì docs dự án cũ / audit & migrate ADR legacy. KHÔNG tự trigger trong quy trình bình thường."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Docs Maintenance
|
|
7
7
|
|
|
8
|
-
Quét cả repo một lượt để **backfill module README còn thiếu** và **refresh README đã lỗi thời** so với code, rồi **đồng bộ `docs/INDEX.md`** (Modules hiện có + Token Routing).
|
|
8
|
+
Quét cả repo một lượt để **backfill module README còn thiếu** và **refresh README đã lỗi thời** so với code, rồi **đồng bộ `docs/INDEX.md`** (Modules hiện có + Token Routing). Ngoài ra **audit + migrate ADR legacy** trong `docs/decisions/` (phần mechanical, KHÔNG bịa rationale).
|
|
9
9
|
|
|
10
10
|
Dùng khi onboard/bảo trì **dự án cũ** đã tích lũy nợ doc (code đụng nhiều nhưng README không được cập nhật), hoặc khi user yêu cầu rõ. Đây là phiên bản "chạy hàng loạt cho cả repo" của skill `module-architect` (vốn chạy từng module/session).
|
|
11
11
|
|
|
12
|
+
## Hai phạm vi tách bạch
|
|
13
|
+
|
|
14
|
+
| Phạm vi | Nguồn chân lý | Skill này làm | KHÔNG làm |
|
|
15
|
+
|---|---|---|---|
|
|
16
|
+
| **Module README** (WHAT) | Source code (verify bằng grep) | Backfill MISSING + refresh STALE + sync INDEX | — |
|
|
17
|
+
| **ADR** (WHY/WHY-NOT) | Phán đoán kiến trúc của con người | Audit mechanical (legacy/index-sync/supersede/module-link) + **migrate file legacy ĐÃ CÓ nội dung** | **KHÔNG bịa rationale ADR từ code**; tạo ADR cho quyết định CHƯA ghi → `decision-recorder` |
|
|
18
|
+
|
|
12
19
|
## Iron Law
|
|
13
20
|
|
|
14
21
|
```
|
|
15
|
-
CHỈ VIẾT NHỮNG GÌ VERIFY ĐƯỢC TRONG
|
|
22
|
+
CHỈ VIẾT NHỮNG GÌ VERIFY ĐƯỢC TRONG NGUỒN. KHÔNG BỊA.
|
|
23
|
+
- Module README: mỗi dòng truy về symbol/import có thật trong source.
|
|
24
|
+
- ADR: mỗi rationale truy về nội dung file legacy ĐÃ CÓ. KHÔNG suy diễn WHY/root-cause từ code hiện tại.
|
|
16
25
|
```
|
|
17
26
|
|
|
18
27
|
Stale docs còn tệ hơn không có docs — session sau AI đọc sai, làm sai. Mỗi dòng trong README phải truy về một symbol/import có thật trong source. Không chắc → mô tả mức ý định cao, KHÔNG bịa signature.
|
|
19
28
|
|
|
29
|
+
**ADR bịa còn nguy hiểm hơn**: ADR giả tạo ra "trí nhớ quyết định" sai → session sau tin theo và đảo kiến trúc sai hướng — phá đúng cái Decision Memory muốn chống. KHÔNG bao giờ chế WHY/rejected-alternatives/root-cause từ code. File legacy không đủ rationale → BÁO user, KHÔNG tự điền.
|
|
30
|
+
|
|
20
31
|
## Ranh giới (Lean — KHÔNG làm gì)
|
|
21
32
|
|
|
22
33
|
- **KHÔNG** sinh bộ doc tổng quan kiểu khác (`system-architecture.md`, `project-roadmap.md`, `codebase-summary.md`...). Mô hình OM là **per-module README 3 sections + `docs/INDEX.md`**, đủ để navigate token-aware.
|
|
@@ -81,6 +92,48 @@ Cho mỗi module status `stale`:
|
|
|
81
92
|
|
|
82
93
|
In tóm tắt: `created / updated / skipped` + cảnh báo (module không verify được, ghost). Gợi ý chạy lại `openmoneta docs --validate` để xác nhận sạch.
|
|
83
94
|
|
|
95
|
+
## ADR audit + migrate legacy (docs/decisions/)
|
|
96
|
+
|
|
97
|
+
Chạy khi user yêu cầu "audit ADR", "bảo trì decisions", "migrate quyết định cũ", hoặc khi onboard dự án có `docs/decisions/` lộn xộn. Bỏ qua nếu project chưa có `docs/decisions/`.
|
|
98
|
+
|
|
99
|
+
### 1. Audit (deterministic)
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
bash ~/.cursor/scripts/docs-audit.sh --decisions # report 4 loại vấn đề
|
|
103
|
+
bash ~/.cursor/scripts/docs-audit.sh --decisions-worklist # TSV: <issue>\t<path>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
(OpenCode: `~/.config/opencode/scripts/docs-audit.sh`.) 4 loại issue:
|
|
107
|
+
|
|
108
|
+
- **LEGACY**: file trong `docs/decisions/` không theo `NNNN-slug.md` (vd `YYYY-MM-DD-*.md`) → ứng viên migrate.
|
|
109
|
+
- **NOT-IN-INDEX**: ADR đúng format nhưng thiếu dòng trong `INDEX.md`.
|
|
110
|
+
- **INDEX-GHOST**: `INDEX.md` trỏ ADR file đã mất.
|
|
111
|
+
- **SUPERSEDE-MISS / SUPERSEDE-OPEN**: `Supersedes: ADR-XXXX` trỏ ADR không tồn tại, hoặc ADR cũ chưa đổi Status sang `Superseded by`.
|
|
112
|
+
- **MODLINK** (advisory): module README chưa link ADR khai trong `Module(s)`.
|
|
113
|
+
|
|
114
|
+
### 2. Migrate LEGACY → ADR chuẩn (CHỈ reformat, KHÔNG bịa)
|
|
115
|
+
|
|
116
|
+
Cho mỗi file LEGACY:
|
|
117
|
+
|
|
118
|
+
1. **Đọc file legacy** — nó ĐÃ CÓ rationale thật. KHÔNG suy diễn từ code.
|
|
119
|
+
2. Xác định `NNNN` mới = max ADR hiện có trong INDEX + 1 (4 chữ số).
|
|
120
|
+
3. Tạo `docs/decisions/NNNN-<slug>.md` theo `~/.cursor/templates/adr.md.tpl`, **map nội dung có sẵn** vào 5 section (Context/Decision/Rejected alternatives/Consequences/Failed approaches log). Phần nào file cũ KHÔNG có → để trống + ghi `(legacy: không ghi rõ)`, KHÔNG chế ra.
|
|
121
|
+
4. Field: `Status` (giữ theo file cũ nếu có, mặc định `Accepted (LOCKED)`), `Date` (lấy từ tên file/nội dung cũ), `Module(s)` (nếu suy được từ nội dung), `Plan nguồn: —`.
|
|
122
|
+
5. Xóa file legacy cũ (đã chuyển nội dung) — hoặc nếu format cũ là chuẩn nội bộ của dự án, hỏi user trước khi xóa.
|
|
123
|
+
6. Thêm dòng vào bảng `INDEX.md`.
|
|
124
|
+
|
|
125
|
+
### 3. Sửa NOT-IN-INDEX / INDEX-GHOST / SUPERSEDE / MODLINK
|
|
126
|
+
|
|
127
|
+
- **NOT-IN-INDEX**: đọc ADR → thêm dòng vào bảng INDEX (id, tiêu đề, status, module, date).
|
|
128
|
+
- **INDEX-GHOST**: dòng INDEX trỏ file mất → hỏi user (xóa dòng hay khôi phục file). KHÔNG tự xóa.
|
|
129
|
+
- **SUPERSEDE-OPEN**: đổi ADR cũ sang `Status: Superseded by ADR-NNNN` (mechanical, an toàn).
|
|
130
|
+
- **SUPERSEDE-MISS**: target không tồn tại → BÁO user (có thể typo hoặc file mất), KHÔNG đoán.
|
|
131
|
+
- **MODLINK**: thêm link ADR vào section "## Quyết định kiến trúc (ADR)" của module README.
|
|
132
|
+
|
|
133
|
+
### 4. Báo nếu thiếu rationale
|
|
134
|
+
|
|
135
|
+
File legacy chỉ có tiêu đề/1 dòng, không đủ WHY → migrate phần có được, **liệt kê rõ cho user** ADR nào cần bổ sung rationale qua `decision-recorder`. KHÔNG tự bịa cho đủ section.
|
|
136
|
+
|
|
84
137
|
## Bảng Rationalization (cái cớ → sự thật)
|
|
85
138
|
|
|
86
139
|
| Cái cớ | Sự thật |
|
|
@@ -90,6 +143,8 @@ In tóm tắt: `created / updated / skipped` + cảnh báo (module không verify
|
|
|
90
143
|
| "Viết lại cả README cho gọn" | Phá nội dung đang đúng + tốn token. Chỉ sửa phần lệch. |
|
|
91
144
|
| "Thêm doc tổng quan cho đầy đủ" | Trái Lean. OM chỉ module README + INDEX. |
|
|
92
145
|
| "Module mới khỏi thêm Token Routing" | Không có keyword → AI không tìm thấy module → đọc loạn. Bắt buộc thêm. |
|
|
146
|
+
| "File legacy thiếu WHY, mình suy từ code cho đủ ADR" | Bịa rationale = trí nhớ quyết định giả = tệ hơn không có. Để trống + báo user. |
|
|
147
|
+
| "Tạo luôn ADR cho quyết định chưa ai ghi" | Sai skill. Đó là việc của `decision-recorder` (cần ngữ cảnh thật). |
|
|
93
148
|
|
|
94
149
|
## Red Flags — DỪNG
|
|
95
150
|
|
|
@@ -98,9 +153,14 @@ In tóm tắt: `created / updated / skipped` + cảnh báo (module không verify
|
|
|
98
153
|
- Đang định sinh `system-architecture.md` / `codebase-summary.md`.
|
|
99
154
|
- Đang định xóa GHOST mà chưa hỏi user.
|
|
100
155
|
- Tạo module mới trong INDEX nhưng quên Token Routing keyword.
|
|
156
|
+
- Đang điền section Rejected-alternatives/root-cause của ADR mà chưa thấy nó trong file legacy.
|
|
157
|
+
- Đang tạo ADR mới cho quyết định chưa từng được ghi (→ việc của `decision-recorder`).
|
|
158
|
+
- Đang xóa file legacy/INDEX-GHOST mà chưa hỏi user.
|
|
101
159
|
|
|
102
160
|
## Examples
|
|
103
161
|
|
|
104
162
|
- User: "Dự án này nhiều module thiếu docs, tạo giúp" → `init`: chạy worklist → tạo mọi MISSING + refresh STALE → sync INDEX → report.
|
|
105
163
|
- User: "Doc còn khớp code không?" → `audit`: chỉ report.
|
|
106
164
|
- User: "Cập nhật doc 3 module vừa refactor" → `sync` (giới hạn các slug đó).
|
|
165
|
+
- User: "Audit ADR / decisions lộn xộn" → `docs-audit.sh --decisions` → report 4 loại issue.
|
|
166
|
+
- User: "Migrate mấy file quyết định cũ vào ADR chuẩn" → đọc từng file legacy → reformat sang `NNNN-slug.md` + đăng ký INDEX (không bịa phần thiếu, báo user).
|
package/src/cli.js
CHANGED
|
@@ -56,6 +56,7 @@ Biến Cursor IDE / OpenCode thành team developer hoàn chỉnh.
|
|
|
56
56
|
|
|
57
57
|
openmoneta docs Audit module README (thiếu/lỗi thời) + sync INDEX
|
|
58
58
|
openmoneta docs --validate Phát hiện doc trỏ module/source không tồn tại (ghost)
|
|
59
|
+
openmoneta docs --decisions Audit ADR (docs/decisions/): legacy/index-sync/supersede/module-link
|
|
59
60
|
|
|
60
61
|
openmoneta uninstall Gỡ cài đặt
|
|
61
62
|
|
package/src/commands/docs.js
CHANGED
|
@@ -6,6 +6,8 @@ const { getPkgRoot, isWindows } = require("../lib/paths")
|
|
|
6
6
|
async function run(args) {
|
|
7
7
|
const validate = args.includes("--validate")
|
|
8
8
|
const worklist = args.includes("--worklist")
|
|
9
|
+
const decisions = args.includes("--decisions")
|
|
10
|
+
const decisionsWorklist = args.includes("--decisions-worklist")
|
|
9
11
|
|
|
10
12
|
const pkgRoot = getPkgRoot()
|
|
11
13
|
const script = path.join(pkgRoot, "scripts", "docs-audit.sh")
|
|
@@ -17,7 +19,9 @@ async function run(args) {
|
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
let mode = "--report"
|
|
20
|
-
if (
|
|
22
|
+
if (decisionsWorklist) mode = "--decisions-worklist"
|
|
23
|
+
else if (decisions) mode = "--decisions"
|
|
24
|
+
else if (validate) mode = "--validate"
|
|
21
25
|
else if (worklist) mode = "--worklist"
|
|
22
26
|
|
|
23
27
|
try {
|
package/src/commands/update.js
CHANGED
|
@@ -13,6 +13,18 @@ function isOpenmonetaProject(dir) {
|
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
// Repo nguồn của chính kit (package.json name == openmoneta-dev-kit). Consumer
|
|
17
|
+
// project không có chữ ký này. Dùng để KHÔNG re-render init lên repo nguồn,
|
|
18
|
+
// tránh đè docs bespoke của kit (docs/INDEX.md, plans/INDEX.md, decisions/).
|
|
19
|
+
function isKitRepo(dir) {
|
|
20
|
+
try {
|
|
21
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(dir, "package.json"), "utf8"))
|
|
22
|
+
return pkg && pkg.name === "openmoneta-dev-kit"
|
|
23
|
+
} catch {
|
|
24
|
+
return false
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
16
28
|
function readRegistry() {
|
|
17
29
|
const seen = new Set()
|
|
18
30
|
for (const target of ["cursor", "opencode"]) {
|
|
@@ -49,7 +61,7 @@ function pruneRegistry() {
|
|
|
49
61
|
for (const line of content.split("\n")) {
|
|
50
62
|
const p = line.trim()
|
|
51
63
|
if (!p) continue
|
|
52
|
-
if (isOpenmonetaProject(p)) kept.push(p)
|
|
64
|
+
if (isOpenmonetaProject(p) && !isKitRepo(p)) kept.push(p)
|
|
53
65
|
else pruned++
|
|
54
66
|
}
|
|
55
67
|
fs.writeFileSync(reg, kept.length ? kept.join("\n") + "\n" : "")
|
|
@@ -254,8 +266,8 @@ async function run(args) {
|
|
|
254
266
|
const pruned = pruneRegistry()
|
|
255
267
|
if (pruned > 0) console.log(`\n ℹ Registry: prune ${pruned} entry chết.`)
|
|
256
268
|
|
|
257
|
-
const projects = readRegistry()
|
|
258
|
-
if (isOpenmonetaProject(cwd) && !projects.includes(fs.realpathSync(cwd))) {
|
|
269
|
+
const projects = readRegistry().filter((p) => !isKitRepo(p))
|
|
270
|
+
if (isOpenmonetaProject(cwd) && !isKitRepo(cwd) && !projects.includes(fs.realpathSync(cwd))) {
|
|
259
271
|
projects.push(fs.realpathSync(cwd))
|
|
260
272
|
}
|
|
261
273
|
|
|
@@ -280,9 +292,13 @@ async function run(args) {
|
|
|
280
292
|
} else {
|
|
281
293
|
try {
|
|
282
294
|
fs.accessSync(path.join(cwd, "docs", "INDEX.md"))
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
295
|
+
if (isKitRepo(cwd)) {
|
|
296
|
+
console.log(`\n ℹ Bỏ qua sync: đây là repo nguồn OpenMoneta Dev Kit (không re-render lên chính nó).`)
|
|
297
|
+
} else {
|
|
298
|
+
console.log(`\n ▶ Syncing project: ${path.basename(cwd)}`)
|
|
299
|
+
syncProject(initScript, cwd)
|
|
300
|
+
console.log(`\n ✅ Project synced.`)
|
|
301
|
+
}
|
|
286
302
|
} catch (err) {
|
|
287
303
|
if (isWindows()) {
|
|
288
304
|
console.log(`\n ⚠ Không thể sync project (cần Git Bash). Cài tại: https://git-scm.com/download/win`)
|
|
@@ -294,7 +310,7 @@ async function run(args) {
|
|
|
294
310
|
}
|
|
295
311
|
|
|
296
312
|
const others = readRegistry().filter(
|
|
297
|
-
(p) => isOpenmonetaProject(p) && !(isOpenmonetaProject(cwd) && p === fs.realpathSync(cwd))
|
|
313
|
+
(p) => isOpenmonetaProject(p) && !isKitRepo(p) && !(isOpenmonetaProject(cwd) && p === fs.realpathSync(cwd))
|
|
298
314
|
)
|
|
299
315
|
if (others.length > 0) {
|
|
300
316
|
console.log(`\n ℹ Có ${others.length} project khác trong registry chưa sync. Sync tất cả:`)
|
|
@@ -308,5 +324,5 @@ async function run(args) {
|
|
|
308
324
|
|
|
309
325
|
module.exports = {
|
|
310
326
|
run,
|
|
311
|
-
_internal: { isOpenmonetaProject, readRegistry, pruneRegistry, findProjects, scanSeed },
|
|
327
|
+
_internal: { isOpenmonetaProject, isKitRepo, readRegistry, pruneRegistry, findProjects, scanSeed },
|
|
312
328
|
}
|