openmoneta-dev-kit 2.2.0 → 2.3.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 +7 -4
- package/VERSION +1 -1
- package/claude/agents/qa-autonomous.md +39 -0
- package/claude/agents/requirement-analyst.md +51 -0
- package/claude/agents/security-auditor.md +44 -0
- package/claude/agents/ui-tester.md +40 -0
- package/claude/hooks/bash-guard.sh +112 -0
- package/claude/hooks/posttooluse-adapter.sh +38 -0
- package/claude/hooks/pretooluse-adapter.sh +69 -0
- package/claude/hooks/sessionstart-adapter.sh +44 -0
- package/claude/hooks/stop-adapter.sh +60 -0
- package/claude/settings-hooks.json +55 -0
- package/hooks/inject-process-context.sh +24 -12
- package/hooks/verify-completion.sh +2 -1
- package/install-claude-code.sh +262 -0
- package/package.json +5 -2
- package/scripts/init-project.sh +25 -0
- package/skills/automated-testing/SKILL.md +2 -0
- package/skills/automated-testing/scripts/install-playwright.sh +3 -0
- package/skills/decision-recorder/SKILL.md +1 -1
- package/skills/docs-maintenance/SKILL.md +3 -1
- package/skills/module-architect/SKILL.md +2 -2
- package/src/commands/init.js +7 -1
- package/src/commands/install.js +21 -2
- package/src/commands/uninstall.js +54 -3
- package/src/commands/update.js +32 -9
- package/src/lib/paths.js +4 -0
- package/templates/AGENTS.md.tpl +5 -5
- package/uninstall.sh +62 -1
- package/update.sh +16 -1
|
@@ -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
|
-
|
|
14
|
-
|
|
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
|
-
|
|
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 "$
|
|
24
|
-
local reg="$
|
|
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
|
-
|
|
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="$
|
|
82
|
-
REPO_FILE="$
|
|
83
|
-
CHECK_CACHE="$
|
|
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="$
|
|
86
|
-
[[ -f "$REPO_FILE" ]] || REPO_FILE="$
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.0",
|
|
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",
|
package/scripts/init-project.sh
CHANGED
|
@@ -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
|
|
@@ -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` (
|
|
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
|
|
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
|
|
package/src/commands/init.js
CHANGED
|
@@ -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()
|
package/src/commands/install.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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 }
|