openmoneta-dev-kit 2.2.1 → 2.3.1

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.
@@ -5,23 +5,31 @@
5
5
  # AGENTS.md ở project root vẫn có đầy đủ chi tiết — AI đọc khi cần.
6
6
  #
7
7
  # Cũng check update từ remote git repo (cache 24h).
8
+ #
9
+ # Multi-platform (v2.3.0): mặc định install home là ~/.cursor (Cursor).
10
+ # Adapter Claude Code gọi script này với env:
11
+ # OPENMONETA_HOME=~/.claude — đổi nơi đọc version/registry/cache
12
+ # OPENMONETA_SKIP_SUMMARY=1 — bỏ inject summary 6 bước (tránh nạp đôi
13
+ # khi platform đã load quy trình qua CLAUDE.md → @AGENTS.md)
8
14
 
9
15
  set -euo pipefail
10
16
 
11
17
  INPUT_JSON=$(cat 2>/dev/null || echo '{}')
12
18
 
13
- # Auto-register project vào Cursor registry (seed cho `update --all-projects`).
14
- # Im lặng. Chỉ append khi: workspace có docs/INDEX.md, Cursor install home đã cài,
19
+ OM_HOME="${OPENMONETA_HOME:-$HOME/.cursor}"
20
+
21
+ # Auto-register project vào registry của install home (seed cho `update --all-projects`).
22
+ # Im lặng. Chỉ append khi: workspace có docs/INDEX.md, install home đã cài,
15
23
  # và path chưa có trong registry (dedup, chuẩn hoá pwd -P).
16
- register_project_cursor() {
24
+ register_project() {
17
25
  local ws="$1"
18
26
  [[ -n "$ws" && -f "$ws/docs/INDEX.md" ]] || return 0
19
27
  # Bỏ qua repo nguồn của chính kit (tránh update --all-projects đè docs bespoke).
20
28
  if [[ -f "$ws/package.json" ]] && grep -q '"name": *"openmoneta-dev-kit"' "$ws/package.json" 2>/dev/null; then
21
29
  return 0
22
30
  fi
23
- [[ -f "$HOME/.cursor/.openmoneta-version" ]] || return 0
24
- local reg="$HOME/.cursor/.openmoneta-projects"
31
+ [[ -f "$OM_HOME/.openmoneta-version" ]] || return 0
32
+ local reg="$OM_HOME/.openmoneta-projects"
25
33
  local abs
26
34
  abs="$(cd "$ws" 2>/dev/null && pwd -P)" || return 0
27
35
  [[ -n "$abs" ]] || return 0
@@ -38,7 +46,7 @@ if command -v jq >/dev/null 2>&1; then
38
46
  rm -f "$WORKSPACE_FROM_INPUT/.cursor/.docs-index-read" 2>/dev/null || true
39
47
  rm -f "$WORKSPACE_FROM_INPUT/.cursor/.decisions-read" 2>/dev/null || true
40
48
  fi
41
- register_project_cursor "$WORKSPACE_FROM_INPUT" 2>/dev/null || true
49
+ register_project "$WORKSPACE_FROM_INPUT" 2>/dev/null || true
42
50
  fi
43
51
 
44
52
  # === Summary 6 bước (~45 dòng, ~650 tokens) ===
@@ -78,12 +86,12 @@ SUMMARY='# OpenMoneta Dev Kit v1.7.0 — Quy trình 6 bước (Adaptive Planning
78
86
 
79
87
  # === Auto-check update (cache 24h) ===
80
88
  UPDATE_NOTICE=""
81
- VERSION_FILE="$HOME/.cursor/.openmoneta-version"
82
- REPO_FILE="$HOME/.cursor/.openmoneta-repo"
83
- CHECK_CACHE="$HOME/.cursor/.openmoneta-update-check"
89
+ VERSION_FILE="$OM_HOME/.openmoneta-version"
90
+ REPO_FILE="$OM_HOME/.openmoneta-repo"
91
+ CHECK_CACHE="$OM_HOME/.openmoneta-update-check"
84
92
 
85
- [[ -f "$VERSION_FILE" ]] || VERSION_FILE="$HOME/.cursor/.cursor-team-dev-version"
86
- [[ -f "$REPO_FILE" ]] || REPO_FILE="$HOME/.cursor/.cursor-team-dev-repo"
93
+ [[ -f "$VERSION_FILE" ]] || VERSION_FILE="$OM_HOME/.cursor-team-dev-version"
94
+ [[ -f "$REPO_FILE" ]] || REPO_FILE="$OM_HOME/.cursor-team-dev-repo"
87
95
 
88
96
  check_update() {
89
97
  [[ -f "$VERSION_FILE" && -f "$REPO_FILE" ]] || return 0
@@ -130,7 +138,11 @@ check_update() {
130
138
 
131
139
  check_update 2>/dev/null || true
132
140
 
133
- FULL_CONTEXT="${SUMMARY}${UPDATE_NOTICE}"
141
+ if [[ -n "${OPENMONETA_SKIP_SUMMARY:-}" ]]; then
142
+ FULL_CONTEXT="${UPDATE_NOTICE}"
143
+ else
144
+ FULL_CONTEXT="${SUMMARY}${UPDATE_NOTICE}"
145
+ fi
134
146
 
135
147
  if command -v jq >/dev/null 2>&1; then
136
148
  jq -n --arg ctx "$FULL_CONTEXT" '{
@@ -107,7 +107,8 @@ for plan in $ALL_RECENT_PLANS; do
107
107
  done
108
108
 
109
109
  # === Check 6: Module README synced (BLOCK strict) ===
110
- LIST_MODULES_SCRIPT="$HOME/.cursor/scripts/list-affected-modules.sh"
110
+ # OPENMONETA_HOME: adapter Claude Code set về ~/.claude; default giữ Cursor.
111
+ LIST_MODULES_SCRIPT="${OPENMONETA_HOME:-$HOME/.cursor}/scripts/list-affected-modules.sh"
111
112
  AFFECTED=""
112
113
  if [[ -x "$LIST_MODULES_SCRIPT" || -f "$LIST_MODULES_SCRIPT" ]]; then
113
114
  AFFECTED=$(bash "$LIST_MODULES_SCRIPT" "$CHANGES_FILE" 2>/dev/null || true)
@@ -0,0 +1,262 @@
1
+ #!/usr/bin/env bash
2
+ # install-claude-code.sh - Cài OpenMoneta Dev Kit vào ~/.claude/ (Claude Code).
3
+ #
4
+ # KHÁC install-opencode.sh một điểm CHỦ ĐÍCH: ~/.claude/ là thư mục sống của user
5
+ # (settings, sessions, skills/agents riêng) → installer copy THEO MANIFEST từng item,
6
+ # TUYỆT ĐỐI không rm -rf thư mục chuẩn (skills/, agents/) của Claude Code.
7
+ #
8
+ # Usage:
9
+ # bash install-claude-code.sh # Cài bình thường (interactive confirm)
10
+ # bash install-claude-code.sh --yes # Cài không hỏi xác nhận
11
+ # bash install-claude-code.sh --no-backup # Skip backup (không khuyến nghị)
12
+
13
+ set -euo pipefail
14
+
15
+ AUTO_YES=0
16
+ DO_BACKUP=1
17
+ for arg in "$@"; do
18
+ case "$arg" in
19
+ --yes|-y) AUTO_YES=1 ;;
20
+ --no-backup) DO_BACKUP=0 ;;
21
+ --help|-h)
22
+ grep '^#' "$0" | sed 's/^# \?//' | head -12
23
+ exit 0
24
+ ;;
25
+ esac
26
+ done
27
+
28
+ REPO_DIR="$(cd "$(dirname "$0")" && pwd)"
29
+ CLAUDE_DIR="$HOME/.claude"
30
+ TIMESTAMP=$(date +%Y%m%d-%H%M%S)
31
+ BACKUP_DIR="$CLAUDE_DIR/.backup-openmoneta-$TIMESTAMP"
32
+ MANIFEST="$CLAUDE_DIR/.openmoneta-manifest"
33
+
34
+ echo "==> OpenMoneta Dev Kit Installer for Claude Code"
35
+ echo " Source: $REPO_DIR"
36
+ echo " Target: $CLAUDE_DIR"
37
+ echo ""
38
+
39
+ if [[ ! -f "$REPO_DIR/VERSION" ]]; then
40
+ echo "[!] Không phải repo OpenMoneta Dev Kit (thiếu VERSION). Abort." >&2
41
+ exit 1
42
+ fi
43
+ NEW_VERSION=$(cat "$REPO_DIR/VERSION")
44
+
45
+ case "$(uname -s)" in
46
+ Darwin*|Linux*) ;;
47
+ *)
48
+ echo "[!] OS $(uname -s) chưa hỗ trợ cho Claude Code installer (Windows: planned)." >&2
49
+ exit 1
50
+ ;;
51
+ esac
52
+
53
+ for required in bash cp mkdir rm python3; do
54
+ command -v "$required" >/dev/null 2>&1 || {
55
+ echo "[!] Thiếu dependency: $required" >&2
56
+ exit 1
57
+ }
58
+ done
59
+
60
+ if ! command -v jq >/dev/null 2>&1; then
61
+ echo "[!] CẢNH BÁO: máy thiếu 'jq'. Hooks enforcement sẽ FAIL-OPEN (không chặn gì)."
62
+ echo " Cài jq để enforcement hoạt động: brew install jq (macOS) / apt install jq (Linux)."
63
+ echo ""
64
+ fi
65
+
66
+ # Pre-flight: settings.json phải là JSON hợp lệ TRƯỚC khi installer đụng bất cứ gì
67
+ # (nếu để đến bước merge mới abort thì manifest cũ đã bị dọn → artifacts mồ côi).
68
+ if [[ -f "$CLAUDE_DIR/settings.json" ]]; then
69
+ if ! python3 -c 'import json,sys; json.load(open(sys.argv[1]))' "$CLAUDE_DIR/settings.json" 2>/dev/null; then
70
+ echo "[!] ABORT: $CLAUDE_DIR/settings.json không phải JSON hợp lệ." >&2
71
+ echo " Sửa file rồi chạy lại installer. Chưa có gì bị thay đổi." >&2
72
+ exit 1
73
+ fi
74
+ fi
75
+
76
+ VERSION_FILE="$CLAUDE_DIR/.openmoneta-version"
77
+ REPO_FILE="$CLAUDE_DIR/.openmoneta-repo"
78
+ INSTALL_FILE="$CLAUDE_DIR/.openmoneta-installed-at"
79
+
80
+ EXISTING_VERSION=""
81
+ if [[ -f "$VERSION_FILE" ]]; then
82
+ EXISTING_VERSION=$(cat "$VERSION_FILE" 2>/dev/null || echo "unknown")
83
+ fi
84
+
85
+ if [[ -n "$EXISTING_VERSION" ]]; then
86
+ echo "Phát hiện OpenMoneta Dev Kit for Claude Code hiện tại: v$EXISTING_VERSION → sẽ cập nhật lên v$NEW_VERSION"
87
+ else
88
+ echo "Cài đặt mới OpenMoneta Dev Kit for Claude Code: v$NEW_VERSION"
89
+ fi
90
+
91
+ if [[ -d "$CLAUDE_DIR" && $DO_BACKUP -eq 1 ]]; then
92
+ echo "Backup các item bị đụng vào: $BACKUP_DIR"
93
+ fi
94
+
95
+ if [[ $AUTO_YES -eq 0 ]]; then
96
+ read -r -p "Tiếp tục? [y/N] " ans
97
+ if [[ "$ans" != "y" && "$ans" != "Y" ]]; then
98
+ echo "Hủy."
99
+ exit 0
100
+ fi
101
+ fi
102
+
103
+ mkdir -p "$CLAUDE_DIR"
104
+
105
+ # === Backup: chỉ backup những item installer sẽ đụng ===
106
+ if [[ $DO_BACKUP -eq 1 ]]; then
107
+ echo ""
108
+ echo "==> Backup..."
109
+ mkdir -p "$BACKUP_DIR"
110
+ for item in settings.json templates scripts .openmoneta-version .openmoneta-manifest; do
111
+ src="$CLAUDE_DIR/$item"
112
+ if [[ -e "$src" ]]; then
113
+ cp -R "$src" "$BACKUP_DIR/"
114
+ echo " + $item"
115
+ fi
116
+ done
117
+ if [[ -d "$CLAUDE_DIR/hooks/openmoneta" ]]; then
118
+ mkdir -p "$BACKUP_DIR/hooks"
119
+ cp -R "$CLAUDE_DIR/hooks/openmoneta" "$BACKUP_DIR/hooks/"
120
+ echo " + hooks/openmoneta"
121
+ fi
122
+ if [[ -z "$(ls -A "$BACKUP_DIR" 2>/dev/null)" ]]; then
123
+ rmdir "$BACKUP_DIR" 2>/dev/null || true
124
+ echo " (không có gì để backup)"
125
+ fi
126
+ fi
127
+
128
+ # === Dọn bản cài cũ theo manifest (clean update — chỉ xóa item kit đã cài) ===
129
+ if [[ -f "$MANIFEST" ]]; then
130
+ echo ""
131
+ echo "==> Dọn bản cài cũ (theo manifest)..."
132
+ while IFS= read -r item; do
133
+ [[ -z "$item" ]] && continue
134
+ case "$item" in
135
+ /*|*..*) continue ;; # chỉ chấp nhận path tương đối an toàn
136
+ esac
137
+ if [[ -e "$CLAUDE_DIR/$item" ]]; then
138
+ rm -rf "${CLAUDE_DIR:?}/$item"
139
+ fi
140
+ done < "$MANIFEST"
141
+ rm -f "$MANIFEST"
142
+ fi
143
+
144
+ echo ""
145
+ echo "==> Cài OpenMoneta config cho Claude Code..."
146
+
147
+ NEW_MANIFEST_ITEMS=()
148
+
149
+ # Skills: copy từng skill của kit vào ~/.claude/skills/<name>/ (giữ skill riêng của user).
150
+ mkdir -p "$CLAUDE_DIR/skills"
151
+ for skill_dir in "$REPO_DIR/skills/"*/; do
152
+ name=$(basename "$skill_dir")
153
+ rm -rf "${CLAUDE_DIR:?}/skills/$name"
154
+ cp -R "$skill_dir" "$CLAUDE_DIR/skills/$name"
155
+ NEW_MANIFEST_ITEMS+=("skills/$name")
156
+ done
157
+ echo " + skills/ ($(ls "$REPO_DIR/skills" | wc -l | tr -d ' ') skill của kit; skill riêng của bạn giữ nguyên)"
158
+
159
+ # Agents: copy từng file (giữ agent riêng của user).
160
+ mkdir -p "$CLAUDE_DIR/agents"
161
+ for agent_file in "$REPO_DIR/claude/agents/"*.md; do
162
+ name=$(basename "$agent_file")
163
+ cp "$agent_file" "$CLAUDE_DIR/agents/$name"
164
+ NEW_MANIFEST_ITEMS+=("agents/$name")
165
+ done
166
+ echo " + agents/ (4 subagent của kit; agent riêng của bạn giữ nguyên)"
167
+
168
+ # Hooks: adapters + core (bash hooks gốc dùng chung với Cursor) — thư mục kit-owned.
169
+ rm -rf "$CLAUDE_DIR/hooks/openmoneta"
170
+ mkdir -p "$CLAUDE_DIR/hooks/openmoneta/core"
171
+ cp "$REPO_DIR/claude/hooks/"*.sh "$CLAUDE_DIR/hooks/openmoneta/"
172
+ cp "$REPO_DIR/hooks/"*.sh "$CLAUDE_DIR/hooks/openmoneta/core/"
173
+ chmod +x "$CLAUDE_DIR/hooks/openmoneta/"*.sh "$CLAUDE_DIR/hooks/openmoneta/core/"*.sh
174
+ NEW_MANIFEST_ITEMS+=("hooks/openmoneta")
175
+ echo " + hooks/openmoneta/ (5 adapter + 5 core hook)"
176
+
177
+ # Templates + scripts (kit-owned, cần cho init-project.sh chạy độc lập không cần Cursor).
178
+ rm -rf "$CLAUDE_DIR/templates"
179
+ cp -R "$REPO_DIR/templates" "$CLAUDE_DIR/templates"
180
+ NEW_MANIFEST_ITEMS+=("templates")
181
+ echo " + templates/"
182
+
183
+ rm -rf "$CLAUDE_DIR/scripts"
184
+ cp -R "$REPO_DIR/scripts" "$CLAUDE_DIR/scripts"
185
+ chmod +x "$CLAUDE_DIR/scripts/"*.sh 2>/dev/null || true
186
+ NEW_MANIFEST_ITEMS+=("scripts")
187
+ echo " + scripts/"
188
+
189
+ # === Merge hooks config vào settings.json (idempotent, không đè key user) ===
190
+ SETTINGS="$CLAUDE_DIR/settings.json"
191
+ FRAGMENT="$REPO_DIR/claude/settings-hooks.json"
192
+
193
+ python3 - "$SETTINGS" "$FRAGMENT" <<'PYEOF'
194
+ import json, sys, os
195
+
196
+ settings_path, fragment_path = sys.argv[1], sys.argv[2]
197
+
198
+ with open(fragment_path) as f:
199
+ fragment = json.load(f)
200
+
201
+ settings = {}
202
+ if os.path.exists(settings_path):
203
+ try:
204
+ with open(settings_path) as f:
205
+ settings = json.load(f)
206
+ except (json.JSONDecodeError, ValueError) as e:
207
+ # KHÔNG đè file hỏng/không parse được — user tự sửa rồi chạy lại.
208
+ print(f" [!] ABORT: không parse được {settings_path}: {e}", file=sys.stderr)
209
+ print(" Sửa file (JSON hợp lệ) rồi chạy lại installer. Chưa có gì bị ghi đè.", file=sys.stderr)
210
+ sys.exit(1)
211
+
212
+ hooks = settings.setdefault("hooks", {})
213
+
214
+ def entry_commands(entry):
215
+ return {h.get("command") for h in entry.get("hooks", []) if isinstance(h, dict)}
216
+
217
+ added = 0
218
+ for event, frag_entries in fragment.get("hooks", {}).items():
219
+ existing = hooks.setdefault(event, [])
220
+ if not isinstance(existing, list):
221
+ print(f" [!] ABORT: settings.json hooks.{event} không phải list — không merge được.", file=sys.stderr)
222
+ sys.exit(1)
223
+ existing_cmds = set()
224
+ for e in existing:
225
+ if isinstance(e, dict):
226
+ existing_cmds |= entry_commands(e)
227
+ for fe in frag_entries:
228
+ if entry_commands(fe) & existing_cmds:
229
+ continue # đã có (idempotent)
230
+ existing.append(fe)
231
+ added += 1
232
+
233
+ with open(settings_path, "w") as f:
234
+ json.dump(settings, f, indent=2, ensure_ascii=False)
235
+ f.write("\n")
236
+
237
+ print(f" + settings.json (merge hooks: {added} entry mới, key user giữ nguyên)")
238
+ PYEOF
239
+
240
+ # === Ghi manifest + version files ===
241
+ printf '%s\n' "${NEW_MANIFEST_ITEMS[@]}" > "$MANIFEST"
242
+ echo "$NEW_VERSION" > "$VERSION_FILE"
243
+ echo "$REPO_DIR" > "$REPO_FILE"
244
+ date -Iseconds > "$INSTALL_FILE"
245
+
246
+ echo ""
247
+ echo "================================================"
248
+ echo "✅ Cài đặt thành công OpenMoneta Dev Kit for Claude Code v$NEW_VERSION"
249
+ echo "================================================"
250
+ echo ""
251
+ echo "Đã cài vào: $CLAUDE_DIR/ (theo manifest: $MANIFEST)"
252
+ if [[ $DO_BACKUP -eq 1 && -d "$BACKUP_DIR" ]]; then
253
+ echo "Backup tại: $BACKUP_DIR/"
254
+ fi
255
+ echo ""
256
+ echo "Bước tiếp theo:"
257
+ echo " 1. Mở project cần dùng Claude Code."
258
+ echo " 2. Chạy: OPENMONETA_HOME=$CLAUDE_DIR bash $CLAUDE_DIR/scripts/init-project.sh ."
259
+ echo " (hoặc: openmoneta init --claude)"
260
+ echo " 3. RESTART Claude Code (hooks chỉ được nạp lúc session start; kiểm tra bằng /hooks)."
261
+ echo ""
262
+ echo "Lưu ý: project-level CLAUDE.md import @AGENTS.md — AGENTS.md là source of truth."
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "openmoneta-dev-kit",
3
- "version": "2.2.1",
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",
3
+ "version": "2.3.1",
4
+ "description": "OpenMoneta Dev Kit — Biến Cursor IDE / OpenCode / Claude Code 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",
7
7
  "opencode",
8
+ "claude-code",
8
9
  "ai",
9
10
  "developer-tools",
10
11
  "workflow",
@@ -29,8 +30,10 @@
29
30
  "hooks/",
30
31
  "scripts/",
31
32
  "opencode/",
33
+ "claude/",
32
34
  "install.sh",
33
35
  "install-opencode.sh",
36
+ "install-claude-code.sh",
34
37
  "install.ps1",
35
38
  "install-opencode.ps1",
36
39
  "uninstall.sh",
@@ -377,6 +377,29 @@ sync_opencode_project_guard() {
377
377
  rm -rf ".opencode/plugins" ".opencode/plugin" 2>/dev/null || true
378
378
  }
379
379
 
380
+ sync_claude_md() {
381
+ # Claude Code KHÔNG đọc AGENTS.md natively → cần shim CLAUDE.md import @AGENTS.md
382
+ # (khuyến nghị chính thức Anthropic; AGENTS.md vẫn là source of truth chung 3 platform).
383
+ # CHỈ tạo khi init chạy cho Claude Code home (openmoneta init --claude) — tránh sinh
384
+ # file lạ trong repo của team không dùng Claude Code.
385
+ if [[ "$OPENMONETA_HOME" != "$HOME/.claude" ]]; then
386
+ return 0
387
+ fi
388
+ local dst="CLAUDE.md"
389
+ if [[ -f "$dst" ]]; then
390
+ if grep -qE '^@AGENTS\.md' "$dst" 2>/dev/null; then
391
+ SKIPPED+=("file: CLAUDE.md (đã import @AGENTS.md)")
392
+ else
393
+ SKIPPED+=("file: CLAUDE.md (đã có sẵn — KHÔNG đè; thêm dòng '@AGENTS.md' thủ công để dùng chung quy trình OpenMoneta)")
394
+ fi
395
+ return 0
396
+ fi
397
+ cat > "$dst" <<'EOF'
398
+ @AGENTS.md
399
+ EOF
400
+ CREATED+=("file: CLAUDE.md (import @AGENTS.md)")
401
+ }
402
+
380
403
  # === Bước 1: Tạo cấu trúc thư mục tối thiểu ===
381
404
  echo "==> Tạo thư mục tối thiểu..."
382
405
  ensure_dir "docs"
@@ -394,6 +417,7 @@ sync_docs_index
394
417
  sync_decisions_index
395
418
  sync_plans_index
396
419
  sync_opencode_project_guard
420
+ sync_claude_md
397
421
 
398
422
  # === Bước 3: .gitignore với patterns chuẩn ===
399
423
  echo "==> Update .gitignore..."
@@ -407,6 +431,7 @@ PATTERNS=(
407
431
  ".cursor/.changelog-baseline"
408
432
  ".cursor/.last-test-result"
409
433
  ".cursor/.ui-session.json"
434
+ ".cursor/.claude-stop-loop-*.json"
410
435
  )
411
436
 
412
437
  if [[ ! -f "$GITIGNORE" ]]; then
@@ -454,6 +479,7 @@ register_in_home() {
454
479
 
455
480
  register_in_home "$HOME/.cursor"
456
481
  register_in_home "${XDG_CONFIG_HOME:-$HOME/.config}/opencode"
482
+ register_in_home "$HOME/.claude"
457
483
 
458
484
  # === Báo cáo ===
459
485
  echo ""
@@ -15,6 +15,8 @@ Chạy script tự động (idempotent):
15
15
  bash ~/.cursor/skills/automated-testing/scripts/install-playwright.sh
16
16
  ```
17
17
 
18
+ > OpenCode/Claude Code: script cùng path trong `~/.config/opencode/skills/` / `~/.claude/skills/`.
19
+
18
20
  Script sẽ:
19
21
  1. Detect package manager (`npm`/`pnpm`/`yarn`/`bun`).
20
22
  2. Cài `@playwright/test` + `dotenv`.
@@ -42,7 +42,10 @@ echo "==> Cài Chromium browser cho Playwright..."
42
42
  npx playwright install chromium --with-deps 2>/dev/null || npx playwright install chromium
43
43
 
44
44
  # Copy playwright.config.ts từ template nếu chưa có
45
+ # Tìm template theo platform đã cài (Cursor → OpenCode → Claude Code).
45
46
  TEMPLATE="$HOME/.cursor/templates/playwright.config.ts.tpl"
47
+ [[ -f "$TEMPLATE" ]] || TEMPLATE="${XDG_CONFIG_HOME:-$HOME/.config}/opencode/templates/playwright.config.ts.tpl"
48
+ [[ -f "$TEMPLATE" ]] || TEMPLATE="$HOME/.claude/templates/playwright.config.ts.tpl"
46
49
  TARGET="playwright.config.ts"
47
50
  if [[ -f "$TARGET" ]]; then
48
51
  echo "==> $TARGET đã tồn tại, skip."
@@ -36,7 +36,7 @@ KHÔNG ĐẢO NGƯỢC một kiến trúc đã Accepted (LOCKED) khi CHƯA:
36
36
  ## Cách tạo ADR mới
37
37
 
38
38
  1. Đọc `docs/decisions/INDEX.md` để biết số ADR lớn nhất hiện có → `NNNN` mới = max + 1 (4 chữ số).
39
- 2. Copy template `~/.cursor/templates/adr.md.tpl` (hoặc `~/.config/opencode/templates/adr.md.tpl`) → `docs/decisions/NNNN-<slug>.md`.
39
+ 2. Copy template `~/.cursor/templates/adr.md.tpl` (OpenCode: `~/.config/opencode/templates/adr.md.tpl`; Claude Code: `~/.claude/templates/adr.md.tpl`) → `docs/decisions/NNNN-<slug>.md`.
40
40
  3. Điền 5 section: Context, Decision, **Rejected alternatives (kèm root cause)**, Consequences (known limitations), Failed approaches log.
41
41
  4. Điền field: `Status: Accepted (LOCKED)`, `Date`, `Module(s)`, `Plan nguồn` (link plan archive nếu có — đây là **tầng chi tiết**), `Supersedes` (nếu thay ADR cũ).
42
42
  5. Cập nhật bảng trong `docs/decisions/INDEX.md`.
@@ -60,6 +60,8 @@ bash ~/.cursor/scripts/docs-audit.sh --worklist # MISSING/STALE/ok (TSV: slug
60
60
  bash ~/.cursor/scripts/docs-audit.sh --validate # liệt kê GHOST (doc/INDEX trỏ source/module đã mất)
61
61
  ```
62
62
 
63
+ > Đường dẫn script theo platform: Cursor `~/.cursor/scripts/`, OpenCode `~/.config/opencode/scripts/`, Claude Code `~/.claude/scripts/`.
64
+
63
65
  (OpenCode: `~/.config/opencode/scripts/docs-audit.sh`.) **BẮT BUỘC chạy cả `--validate`** ở mọi mode để không bỏ sót ghost. Mode `audit` in cả 2 kết quả rồi dừng, không qua các bước tạo/sửa dưới.
64
66
 
65
67
  ### 2. (Repo lớn) đọc song song
@@ -117,7 +119,7 @@ Cho mỗi file LEGACY:
117
119
 
118
120
  1. **Đọc file legacy** — nó ĐÃ CÓ rationale thật. KHÔNG suy diễn từ code.
119
121
  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.
122
+ 3. Tạo `docs/decisions/NNNN-<slug>.md` theo `~/.cursor/templates/adr.md.tpl` (OpenCode/Claude Code: `templates/adr.md.tpl` trong home tương ứng), **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
123
  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
124
  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
125
  6. Thêm dòng vào bảng `INDEX.md`.
@@ -190,7 +190,7 @@ Quy ước slug:
190
190
  | `src/modules/auth/...` | `auth` | `docs/modules/auth/` |
191
191
  | `src/auth/...` (single-repo, không có `modules/`) | `auth` | `docs/modules/auth/` |
192
192
 
193
- > Helper: `bash ~/.cursor/scripts/list-affected-modules.sh .cursor/.session-changes.json` infer slug từ file đã sửa.
193
+ > Helper: `bash ~/.cursor/scripts/list-affected-modules.sh .cursor/.session-changes.json` infer slug từ file đã sửa (OpenCode/Claude Code: script cùng tên trong `~/.config/opencode/scripts/` / `~/.claude/scripts/`).
194
194
 
195
195
  ### Bước 2.2 — Backfill khi chạm vào module chưa có docs
196
196
 
@@ -229,7 +229,7 @@ Quy tắc maintenance:
229
229
  | `src/modules/auth/z.ts` | `auth` | `docs/modules/auth/` |
230
230
  | `src/auth/z.ts` (no `modules/`) | `auth` | `docs/modules/auth/` |
231
231
 
232
- > Helper script: `bash ~/.cursor/scripts/list-affected-modules.sh .cursor/.session-changes.json`.
232
+ > Helper script: `bash ~/.cursor/scripts/list-affected-modules.sh .cursor/.session-changes.json` (OpenCode/Claude Code: script cùng tên trong home tương ứng).
233
233
 
234
234
  **Workflow cho mỗi module slug**:
235
235
 
@@ -1,15 +1,18 @@
1
1
  const { execSync } = require("node:child_process")
2
2
  const path = require("node:path")
3
- const { getPkgRoot, OPENCODE_DIR, isWindows } = require("../lib/paths")
3
+ const { getPkgRoot, OPENCODE_DIR, CLAUDE_DIR, isWindows } = require("../lib/paths")
4
4
 
5
5
  async function run(args) {
6
6
  let projectDir = process.cwd()
7
7
  let isOpenCode = false
8
+ let isClaude = false
8
9
  const remaining = []
9
10
 
10
11
  for (const arg of args) {
11
12
  if (arg === "--opencode") {
12
13
  isOpenCode = true
14
+ } else if (arg === "--claude") {
15
+ isClaude = true
13
16
  } else if (arg.startsWith("-")) {
14
17
  remaining.push(arg)
15
18
  } else {
@@ -27,6 +30,9 @@ async function run(args) {
27
30
  if (isOpenCode) {
28
31
  env.OPENMONETA_HOME = OPENCODE_DIR
29
32
  console.log(` Mode: OpenCode`)
33
+ } else if (isClaude) {
34
+ env.OPENMONETA_HOME = CLAUDE_DIR
35
+ console.log(` Mode: Claude Code`)
30
36
  }
31
37
 
32
38
  const shellCmd = isWindows()
@@ -7,15 +7,21 @@ async function run(args) {
7
7
  const autoYes = args.includes("--yes") || args.includes("-y")
8
8
  const cursorOnly = args.includes("--cursor")
9
9
  const opencodeOnly = args.includes("--opencode")
10
- const both = !cursorOnly && !opencodeOnly
10
+ const claudeOnly = args.includes("--claude")
11
+ // Default (không flag) giữ Cursor + OpenCode như trước. Claude Code là opt-in
12
+ // qua --claude để không tạo ~/.claude trên máy không dùng Claude Code.
13
+ const both = !cursorOnly && !opencodeOnly && !claudeOnly
11
14
 
12
15
  const v = getLocalVersion() || "unknown"
13
16
  const pkgRoot = getPkgRoot()
14
17
 
15
18
  if (both) {
16
19
  console.log(`\n 🔧 Cài OpenMoneta Dev Kit v${v} cho Cursor + OpenCode`)
20
+ console.log(` (Claude Code: opt-in, chạy thêm: openmoneta install --claude)`)
17
21
  } else if (cursorOnly) {
18
22
  console.log(`\n 🔧 Cài OpenMoneta Dev Kit v${v} cho Cursor`)
23
+ } else if (claudeOnly) {
24
+ console.log(`\n 🔧 Cài OpenMoneta Dev Kit v${v} cho Claude Code`)
19
25
  } else {
20
26
  console.log(`\n 🔧 Cài OpenMoneta Dev Kit v${v} cho OpenCode`)
21
27
  }
@@ -44,7 +50,20 @@ async function run(args) {
44
50
  }
45
51
  }
46
52
 
47
- console.log(`\n 🎉 Hoàn tất. Restart Cursor/OpenCode để áp dụng.`)
53
+ if (claudeOnly) {
54
+ try {
55
+ const flags = autoYes ? "--yes" : ""
56
+ // Chưa có install-claude-code.ps1 — Windows defer (xem plan 2026-06-11).
57
+ const cmd = `bash "${path.join(pkgRoot, "install-claude-code.sh")}" ${flags}`.trim()
58
+ console.log(`\n ▶ Claude Code...`)
59
+ execSync(cmd, { stdio: "inherit", cwd: pkgRoot })
60
+ console.log(` ✅ Claude Code done.`)
61
+ } catch (err) {
62
+ console.error(` ❌ Claude Code failed: ${err.message}`)
63
+ }
64
+ }
65
+
66
+ console.log(`\n 🎉 Hoàn tất. Restart Cursor/OpenCode/Claude Code để áp dụng.`)
48
67
  }
49
68
 
50
69
  module.exports = { run }
@@ -1,7 +1,7 @@
1
- const { unlinkSync, rmdirSync, existsSync, readdirSync, lstatSync } = require("node:fs")
1
+ const { unlinkSync, rmdirSync, existsSync, readdirSync, lstatSync, readFileSync, writeFileSync } = require("node:fs")
2
2
  const path = require("node:path")
3
3
  const { execSync } = require("node:child_process")
4
- const { CURSOR_DIR, OPENCODE_DIR, isInstalled, getPkgRoot, isWindows, runScript } = require("../lib/paths")
4
+ const { CURSOR_DIR, OPENCODE_DIR, CLAUDE_DIR, isInstalled, getPkgRoot, isWindows, runScript } = require("../lib/paths")
5
5
 
6
6
  function rmDir(dir) {
7
7
  if (!existsSync(dir)) return
@@ -20,6 +20,55 @@ function rmFile(p) {
20
20
  if (existsSync(p)) unlinkSync(p)
21
21
  }
22
22
 
23
+ // ~/.claude chứa data + skills/agents riêng của user → KHÔNG xóa nguyên thư mục.
24
+ // Chỉ xóa đúng item kit đã cài (theo .openmoneta-manifest) + gỡ hooks entries
25
+ // openmoneta khỏi settings.json.
26
+ function uninstallClaude() {
27
+ if (!existsSync(CLAUDE_DIR)) return
28
+
29
+ const manifest = path.join(CLAUDE_DIR, ".openmoneta-manifest")
30
+ if (existsSync(manifest)) {
31
+ const items = readFileSync(manifest, "utf8")
32
+ .split("\n")
33
+ .map((l) => l.trim())
34
+ .filter(Boolean)
35
+ for (const item of items) {
36
+ const p = path.join(CLAUDE_DIR, item)
37
+ // Manifest chỉ chứa path tương đối bên trong ~/.claude.
38
+ if (!p.startsWith(CLAUDE_DIR + path.sep)) continue
39
+ if (existsSync(p)) {
40
+ if (lstatSync(p).isDirectory()) rmDir(p)
41
+ else unlinkSync(p)
42
+ }
43
+ }
44
+ unlinkSync(manifest)
45
+ }
46
+
47
+ const settingsFile = path.join(CLAUDE_DIR, "settings.json")
48
+ if (existsSync(settingsFile)) {
49
+ try {
50
+ const settings = JSON.parse(readFileSync(settingsFile, "utf8"))
51
+ if (settings.hooks && typeof settings.hooks === "object") {
52
+ const isOpenmonetaEntry = (entry) =>
53
+ Array.isArray(entry.hooks) && entry.hooks.some((h) => typeof h.command === "string" && h.command.includes("hooks/openmoneta/"))
54
+ for (const event of Object.keys(settings.hooks)) {
55
+ if (!Array.isArray(settings.hooks[event])) continue
56
+ settings.hooks[event] = settings.hooks[event].filter((e) => !isOpenmonetaEntry(e))
57
+ if (settings.hooks[event].length === 0) delete settings.hooks[event]
58
+ }
59
+ if (Object.keys(settings.hooks).length === 0) delete settings.hooks
60
+ }
61
+ writeFileSync(settingsFile, JSON.stringify(settings, null, 2) + "\n")
62
+ } catch {
63
+ console.warn(` ⚠ Không parse được ${settingsFile} — giữ nguyên, gỡ hooks openmoneta thủ công nếu cần.`)
64
+ }
65
+ }
66
+
67
+ for (const f of [".openmoneta-version", ".openmoneta-repo", ".openmoneta-installed-at", ".openmoneta-update-check"]) {
68
+ rmFile(path.join(CLAUDE_DIR, f))
69
+ }
70
+ }
71
+
23
72
  async function run(args) {
24
73
  const purge = args.includes("--purge")
25
74
  const autoYes = args.includes("--yes") || args.includes("-y")
@@ -75,8 +124,10 @@ async function run(args) {
75
124
  rmFile(path.join(dir, "AGENTS.md"))
76
125
  }
77
126
 
127
+ uninstallClaude()
128
+
78
129
  console.log(`\n ✅ Đã gỡ cài đặt.`)
79
- console.log(` Restart Cursor/OpenCode để hoàn tất.`)
130
+ console.log(` Restart Cursor/OpenCode/Claude Code để hoàn tất.`)
80
131
  }
81
132
 
82
133
  module.exports = { run }