openmoneta-dev-kit 1.12.0 → 1.13.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 CHANGED
@@ -1 +1 @@
1
- 1.12.0
1
+ 1.13.0
@@ -10,12 +10,30 @@ set -euo pipefail
10
10
 
11
11
  INPUT_JSON=$(cat 2>/dev/null || echo '{}')
12
12
 
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,
15
+ # và path chưa có trong registry (dedup, chuẩn hoá pwd -P).
16
+ register_project_cursor() {
17
+ local ws="$1"
18
+ [[ -n "$ws" && -f "$ws/docs/INDEX.md" ]] || return 0
19
+ [[ -f "$HOME/.cursor/.openmoneta-version" ]] || return 0
20
+ local reg="$HOME/.cursor/.openmoneta-projects"
21
+ local abs
22
+ abs="$(cd "$ws" 2>/dev/null && pwd -P)" || return 0
23
+ [[ -n "$abs" ]] || return 0
24
+ if [[ -f "$reg" ]] && grep -qFx "$abs" "$reg" 2>/dev/null; then
25
+ return 0
26
+ fi
27
+ echo "$abs" >> "$reg" 2>/dev/null || true
28
+ }
29
+
13
30
  # Reset docs-index marker mỗi session (force re-read INDEX.md)
14
31
  if command -v jq >/dev/null 2>&1; then
15
32
  WORKSPACE_FROM_INPUT=$(echo "$INPUT_JSON" | jq -r '.workspace_roots[0] // ""' 2>/dev/null || echo "")
16
33
  if [[ -n "$WORKSPACE_FROM_INPUT" && -d "$WORKSPACE_FROM_INPUT/.cursor" ]]; then
17
34
  rm -f "$WORKSPACE_FROM_INPUT/.cursor/.docs-index-read" 2>/dev/null || true
18
35
  fi
36
+ register_project_cursor "$WORKSPACE_FROM_INPUT" 2>/dev/null || true
19
37
  fi
20
38
 
21
39
  # === Summary 6 bước (~45 dòng, ~650 tokens) ===
@@ -400,6 +400,33 @@ function clearVerifyLoop(root: string) {
400
400
  fs.rmSync(path.join(root, ".cursor", ".openmoneta-verify-loop.json"), { force: true })
401
401
  }
402
402
 
403
+ // Auto-register project vào OpenCode registry (seed cho `update --all-projects`).
404
+ // Im lặng + try/catch — seed không được phép làm fail guard. Chỉ append khi:
405
+ // project có docs/INDEX.md, OpenCode install home đã cài, path chưa có trong registry.
406
+ function registerProjectOpenCode(root: string) {
407
+ try {
408
+ if (!fs.existsSync(path.join(root, "docs", "INDEX.md"))) return
409
+ const home = process.env.HOME || process.env.USERPROFILE || ""
410
+ const xdg = process.env.XDG_CONFIG_HOME || path.join(home, ".config")
411
+ const ocHome = path.join(xdg, "opencode")
412
+ if (!fs.existsSync(path.join(ocHome, ".openmoneta-version"))) return
413
+ const reg = path.join(ocHome, ".openmoneta-projects")
414
+ const abs = fs.realpathSync(root)
415
+ let lines: string[] = []
416
+ if (fs.existsSync(reg)) {
417
+ lines = fs
418
+ .readFileSync(reg, "utf8")
419
+ .split("\n")
420
+ .map((l) => l.trim())
421
+ .filter(Boolean)
422
+ }
423
+ if (lines.includes(abs)) return
424
+ fs.appendFileSync(reg, `${abs}\n`)
425
+ } catch {
426
+ // im lặng
427
+ }
428
+ }
429
+
403
430
  type SessionPromptClient = {
404
431
  session?: {
405
432
  prompt?: (input: {
@@ -434,7 +461,7 @@ export const OpenMonetaGuard = async (ctx: GuardContext) => {
434
461
  {
435
462
  loaded_at: new Date().toISOString(),
436
463
  root,
437
- version: "1.11.0",
464
+ version: "1.13.0",
438
465
  load_count: globalState[globalKey],
439
466
  },
440
467
  null,
@@ -442,6 +469,9 @@ export const OpenMonetaGuard = async (ctx: GuardContext) => {
442
469
  )}\n`,
443
470
  )
444
471
 
472
+ // Seed project vào OpenCode registry để `update --all-projects` thấy project này.
473
+ registerProjectOpenCode(root)
474
+
445
475
  // OpenCode does not have Cursor's sessionStart hook, so reset the docs-first
446
476
  // marker when the plugin is loaded for a new OpenCode process/session.
447
477
  if (fs.existsSync(marker)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openmoneta-dev-kit",
3
- "version": "1.12.0",
3
+ "version": "1.13.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",
@@ -42,14 +42,14 @@ Stale docs còn tệ hơn không có docs — session sau AI đọc sai, làm sa
42
42
 
43
43
  ## Workflow
44
44
 
45
- ### 1. Lấy danh sách (deterministic)
45
+ ### 1. Lấy danh sách + soi ghost (deterministic)
46
46
 
47
47
  ```bash
48
- bash ~/.cursor/scripts/docs-audit.sh --worklist
49
- # Output TSV: <slug>\t<docsPath>\t<sourcePath>\t<missing|stale|ok>
48
+ bash ~/.cursor/scripts/docs-audit.sh --worklist # MISSING/STALE/ok (TSV: slug\tdocs\tsource\tstatus)
49
+ bash ~/.cursor/scripts/docs-audit.sh --validate # liệt GHOST (doc/INDEX trỏ source/module đã mất)
50
50
  ```
51
51
 
52
- (OpenCode: `~/.config/opencode/scripts/docs-audit.sh`.) Mode `audit` chỉ in report rồi dừng, không qua các bước dưới.
52
+ (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.
53
53
 
54
54
  ### 2. (Repo lớn) đọc song song
55
55
 
@@ -75,7 +75,7 @@ Cho mỗi module status `stale`:
75
75
  ### 5. Sync `docs/INDEX.md`
76
76
 
77
77
  - Module mới → thêm dòng vào bảng "Modules hiện có" + **3-5 keyword (VN+EN)** vào bảng "Token Routing" (bắt buộc, để hook docs-first + token-aware reading hoạt động).
78
- - GHOST → báo user, đề xuất xóa dòng/thư mục (KHÔNG tự xóa nếu chưa chắc).
78
+ - **GHOST** (từ `--validate` ở Bước 1) LIỆT KÊ rõ từng ghost cho user. **KHÔNG tự xóa** — map slug→source là heuristic, có thể là module vừa đổi tên/di chuyển. Hỏi user bằng `AskQuestion` (xóa doc/dòng INDEX, hay map lại sang source mới); chỉ thao tác sau khi user xác nhận từng cái.
79
79
 
80
80
  ### 6. Report cuối
81
81
 
@@ -57,6 +57,104 @@ function pruneRegistry() {
57
57
  return pruned
58
58
  }
59
59
 
60
+ const SCAN_PRUNE_DIRS = new Set([
61
+ "node_modules",
62
+ ".git",
63
+ "Library",
64
+ ".Trash",
65
+ ".cache",
66
+ ".npm",
67
+ ".cargo",
68
+ ".gradle",
69
+ ".venv",
70
+ "dist",
71
+ "build",
72
+ ])
73
+
74
+ function findProjects(root, maxDepth) {
75
+ const out = []
76
+ function walk(dir, depth) {
77
+ if (depth > maxDepth) return
78
+ if (isOpenmonetaProject(dir)) {
79
+ try {
80
+ fs.accessSync(path.join(dir, "AGENTS.md"))
81
+ out.push(dir)
82
+ } catch {
83
+ // thiếu AGENTS.md → không tính là OM project khi scan
84
+ }
85
+ }
86
+ let entries
87
+ try {
88
+ entries = fs.readdirSync(dir, { withFileTypes: true })
89
+ } catch {
90
+ return
91
+ }
92
+ for (const e of entries) {
93
+ if (!e.isDirectory()) continue
94
+ if (SCAN_PRUNE_DIRS.has(e.name) || e.name.startsWith(".")) continue
95
+ walk(path.join(dir, e.name), depth + 1)
96
+ }
97
+ }
98
+ walk(root, 0)
99
+ return out
100
+ }
101
+
102
+ function scanSeed(root, pkgRoot) {
103
+ let absRoot
104
+ try {
105
+ absRoot = fs.realpathSync(root)
106
+ } catch {
107
+ console.log(` [!] Scan root không tồn tại: ${root}`)
108
+ return
109
+ }
110
+ const targets = ["cursor", "opencode"].filter((t) => isInstalled(t))
111
+ if (targets.length === 0) {
112
+ console.log(` [!] Chưa có install home nào để seed registry.`)
113
+ return
114
+ }
115
+ let kitRoot
116
+ try {
117
+ kitRoot = fs.realpathSync(pkgRoot)
118
+ } catch {
119
+ kitRoot = pkgRoot
120
+ }
121
+ const uniq = [
122
+ ...new Set(
123
+ findProjects(absRoot, 5)
124
+ .map((p) => {
125
+ try {
126
+ return fs.realpathSync(p)
127
+ } catch {
128
+ return p
129
+ }
130
+ })
131
+ .filter((p) => p !== kitRoot)
132
+ ),
133
+ ]
134
+ let seeded = 0
135
+ for (const t of targets) {
136
+ const reg = getProjectRegistry(t)
137
+ let existing = new Set()
138
+ try {
139
+ existing = new Set(
140
+ fs
141
+ .readFileSync(reg, "utf8")
142
+ .split("\n")
143
+ .map((l) => l.trim())
144
+ .filter(Boolean)
145
+ )
146
+ } catch {
147
+ // registry chưa tồn tại → tạo mới khi append
148
+ }
149
+ const toAdd = uniq.filter((p) => !existing.has(p))
150
+ if (toAdd.length) {
151
+ fs.appendFileSync(reg, toAdd.map((p) => `${p}\n`).join(""))
152
+ seeded += toAdd.length
153
+ }
154
+ }
155
+ console.log(` Scan: thấy ${uniq.length} OM project, seed ${seeded} entry mới.`)
156
+ }
157
+
60
158
  function syncProject(initScript, dir) {
61
159
  if (isWindows()) {
62
160
  execSync(`bash "${initScript}" "${dir}"`, { stdio: "inherit", shell: true })
@@ -69,7 +167,14 @@ async function run(args) {
69
167
  const checkOnly = args.includes("--check")
70
168
  const autoYes = args.includes("--yes") || args.includes("-y")
71
169
  const skipProjects = args.includes("--skip-projects")
72
- const allProjects = args.includes("--all-projects")
170
+ const scanIdx = args.indexOf("--scan")
171
+ const scan = scanIdx !== -1
172
+ let scanRoot = process.env.HOME || process.env.USERPROFILE || "."
173
+ if (scan) {
174
+ const next = args[scanIdx + 1]
175
+ if (next && !next.startsWith("-")) scanRoot = next
176
+ }
177
+ const allProjects = args.includes("--all-projects") || scan
73
178
  const force = args.includes("--force")
74
179
 
75
180
  const local = getLocalVersion()
@@ -140,6 +245,11 @@ async function run(args) {
140
245
  const initScript = path.join(pkgRoot, "scripts", "init-project.sh")
141
246
  const cwd = process.cwd()
142
247
 
248
+ if (scan) {
249
+ console.log(`\n ▶ --scan: dò OM project để seed registry...`)
250
+ scanSeed(scanRoot, pkgRoot)
251
+ }
252
+
143
253
  if (allProjects) {
144
254
  const pruned = pruneRegistry()
145
255
  if (pruned > 0) console.log(`\n ℹ Registry: prune ${pruned} entry chết.`)
@@ -196,4 +306,7 @@ async function run(args) {
196
306
  console.log(`\n 🎉 Hoàn tất. Restart Cursor/OpenCode để áp dụng.`)
197
307
  }
198
308
 
199
- module.exports = { run, _internal: { isOpenmonetaProject, readRegistry, pruneRegistry } }
309
+ module.exports = {
310
+ run,
311
+ _internal: { isOpenmonetaProject, readRegistry, pruneRegistry, findProjects, scanSeed },
312
+ }
package/update.ps1 CHANGED
@@ -7,6 +7,7 @@
7
7
  # .\update.ps1 -Check # chỉ check, không cài
8
8
  # .\update.ps1 -Project C:\path # global + sync thêm project chỉ định
9
9
  # .\update.ps1 -AllProjects # global + sync MỌI project trong registry
10
+ # .\update.ps1 -Scan [-ScanRoot path] # dò OM project (mặc định $HOME) → seed registry → sync all
10
11
  # .\update.ps1 -SkipProjects # global only (old behavior)
11
12
 
12
13
  param(
@@ -15,6 +16,8 @@ param(
15
16
  [switch]$Check,
16
17
  [switch]$SkipProjects,
17
18
  [switch]$AllProjects,
19
+ [switch]$Scan,
20
+ [string]$ScanRoot,
18
21
  [string[]]$Project
19
22
  )
20
23
 
@@ -124,6 +127,49 @@ function Invoke-PruneRegistry {
124
127
  return $pruned
125
128
  }
126
129
 
130
+ # Helper: scan tìm OM project (docs\INDEX.md + AGENTS.md) → seed registry mỗi home tồn tại
131
+ function Invoke-ScanSeed {
132
+ $root = if ($ScanRoot) { $ScanRoot } else { $env:USERPROFILE }
133
+ $absRoot = (Resolve-Path $root -ErrorAction SilentlyContinue).Path
134
+ if (-not $absRoot) { Write-Host " [!] Scan root không tồn tại: $root"; return }
135
+
136
+ $homes = @()
137
+ if (Test-Path (Join-Path $env:USERPROFILE ".cursor\.openmoneta-version")) {
138
+ $homes += (Join-Path $env:USERPROFILE ".cursor\.openmoneta-projects")
139
+ }
140
+ $ocBase = if ($env:XDG_CONFIG_HOME) { "$env:XDG_CONFIG_HOME\opencode" } else { "$env:USERPROFILE\.config\opencode" }
141
+ if (Test-Path (Join-Path $ocBase ".openmoneta-version")) {
142
+ $homes += (Join-Path $ocBase ".openmoneta-projects")
143
+ }
144
+ if ($homes.Count -eq 0) { Write-Host " [!] Chưa có install home nào để seed registry."; return }
145
+
146
+ $prune = @('node_modules', '.git', 'Library', '.Trash', '.cache', '.npm', '.cargo', '.gradle', '.venv', 'dist', 'build')
147
+ Write-Host " Scanning '$absRoot' (depth ~6)..."
148
+ $indexes = Get-ChildItem -Path $absRoot -Recurse -Depth 6 -Filter "INDEX.md" -File -ErrorAction SilentlyContinue |
149
+ Where-Object { (Split-Path $_.DirectoryName -Leaf) -eq "docs" }
150
+
151
+ $found = 0; $seeded = 0
152
+ $absKit = (Resolve-Path $RepoDir -ErrorAction SilentlyContinue).Path
153
+ foreach ($idx in $indexes) {
154
+ $proj = Split-Path (Split-Path $idx.FullName -Parent) -Parent
155
+ $skip = $false
156
+ foreach ($d in $prune) { if ($proj -like "*\$d\*" -or $proj -like "*\$d") { $skip = $true; break } }
157
+ if ($skip) { continue }
158
+ if (-not (Test-Path (Join-Path $proj "AGENTS.md"))) { continue }
159
+ $abs = (Resolve-Path $proj -ErrorAction SilentlyContinue).Path
160
+ if (-not $abs -or $abs -eq $absKit) { continue }
161
+ $found++
162
+ foreach ($reg in $homes) {
163
+ $existing = @()
164
+ if (Test-Path $reg) { $existing = Get-Content $reg | ForEach-Object { $_.Trim() } | Where-Object { $_ } }
165
+ if ($existing -contains $abs) { continue }
166
+ Add-Content -Path $reg -Value $abs
167
+ $seeded++
168
+ }
169
+ }
170
+ Write-Host " Scan: thấy $found OM project, seed $seeded entry mới vào registry."
171
+ }
172
+
127
173
  # Helper: collect projects to sync
128
174
  function Get-OpenMonetaProjects {
129
175
  $found = @()
@@ -262,6 +308,13 @@ if ($SkipProjects) {
262
308
  exit 0
263
309
  }
264
310
 
311
+ if ($Scan) {
312
+ $AllProjects = $true
313
+ Write-Host ""
314
+ Write-Host "==> -Scan: dò OM project để seed registry..."
315
+ Invoke-ScanSeed
316
+ }
317
+
265
318
  if ($AllProjects) {
266
319
  Write-Host ""
267
320
  Write-Host "==> -AllProjects: dọn registry + gom mọi project đã đăng ký..."
package/update.sh CHANGED
@@ -9,6 +9,7 @@
9
9
  # bash update.sh --check # chỉ check, không cài
10
10
  # bash update.sh --project /path # global + sync thêm project chỉ định
11
11
  # bash update.sh --all-projects # global + sync MỌI project trong registry
12
+ # bash update.sh --scan [root] # dò OM project (mặc định $HOME) → seed registry → sync all
12
13
  # bash update.sh --skip-projects # global only (old behavior)
13
14
 
14
15
  set -euo pipefail
@@ -22,31 +23,42 @@ SKIP_PROJECTS=0
22
23
  FORCE=0
23
24
  CHECK_ONLY=0
24
25
  ALL_PROJECTS=0
26
+ SCAN=0
27
+ SCAN_ROOT=""
25
28
  PROJECT_PATHS=()
26
29
 
27
- for arg in "$@"; do
28
- case "$arg" in
30
+ while [[ $# -gt 0 ]]; do
31
+ case "$1" in
29
32
  --yes|-y) AUTO_YES=1 ;;
30
33
  --force) FORCE=1 ;;
31
34
  --check) CHECK_ONLY=1 ;;
32
35
  --all-projects) ALL_PROJECTS=1 ;;
33
36
  --skip-projects) SKIP_PROJECTS=1 ;;
37
+ --scan)
38
+ SCAN=1
39
+ ALL_PROJECTS=1
40
+ if [[ -n "${2:-}" && "${2:-}" != -* ]]; then
41
+ SCAN_ROOT="$2"
42
+ shift
43
+ fi
44
+ ;;
34
45
  --help|-h)
35
- grep '^#' "$0" | sed 's/^# \?//' | head -17
46
+ grep '^#' "$0" | sed 's/^# \?//' | head -18
36
47
  exit 0
37
48
  ;;
38
49
  --project)
39
- shift
40
- if [[ -n "${1:-}" && "${1:-}" != -* ]]; then
41
- PROJECT_PATHS+=("$1")
50
+ if [[ -n "${2:-}" && "${2:-}" != -* ]]; then
51
+ PROJECT_PATHS+=("$2")
52
+ shift
42
53
  fi
43
54
  ;;
44
55
  *)
45
- if [[ "$arg" != -* ]]; then
46
- PROJECT_PATHS+=("$arg")
56
+ if [[ "$1" != -* ]]; then
57
+ PROJECT_PATHS+=("$1")
47
58
  fi
48
59
  ;;
49
60
  esac
61
+ shift
50
62
  done
51
63
 
52
64
  cd "$REPO_DIR"
@@ -146,6 +158,45 @@ prune_registry() {
146
158
  return 0
147
159
  }
148
160
 
161
+ # === Helper: scan tìm OM project (docs/INDEX.md + AGENTS.md) → seed registry mỗi home tồn tại ===
162
+ scan_seed() {
163
+ local root="${SCAN_ROOT:-$HOME}"
164
+ local abs_root
165
+ abs_root="$(cd "$root" 2>/dev/null && pwd -P)" || { echo " [!] Scan root không tồn tại: $root"; return 0; }
166
+
167
+ local homes=()
168
+ [[ -f "$HOME/.cursor/.openmoneta-version" ]] && homes+=("$HOME/.cursor/.openmoneta-projects")
169
+ [[ -f "${XDG_CONFIG_HOME:-$HOME/.config}/opencode/.openmoneta-version" ]] && \
170
+ homes+=("${XDG_CONFIG_HOME:-$HOME/.config}/opencode/.openmoneta-projects")
171
+ if [[ ${#homes[@]} -eq 0 ]]; then
172
+ echo " [!] Chưa có install home nào để seed registry."
173
+ return 0
174
+ fi
175
+
176
+ echo " Scanning '$abs_root' (maxdepth project ~5)..."
177
+ local found=0 seeded=0 idx proj abs reg
178
+ while IFS= read -r idx; do
179
+ proj="$(dirname "$(dirname "$idx")")"
180
+ [[ -f "$proj/AGENTS.md" ]] || continue
181
+ abs="$(cd "$proj" 2>/dev/null && pwd -P)" || continue
182
+ [[ -z "$abs" || "$abs" == "$REPO_DIR" ]] && continue
183
+ found=$((found + 1))
184
+ for reg in "${homes[@]}"; do
185
+ if [[ -f "$reg" ]] && grep -qFx "$abs" "$reg" 2>/dev/null; then
186
+ continue
187
+ fi
188
+ echo "$abs" >> "$reg"
189
+ seeded=$((seeded + 1))
190
+ done
191
+ done < <(find "$abs_root" -maxdepth 7 \
192
+ \( -name node_modules -o -name .git -o -name Library -o -name .Trash \
193
+ -o -name .cache -o -name .npm -o -name .cargo -o -name .gradle \
194
+ -o -name .venv -o -name dist -o -name build \) -prune -o \
195
+ -type f -path '*/docs/INDEX.md' -print 2>/dev/null)
196
+
197
+ echo " Scan: thấy $found OM project, seed $seeded entry mới vào registry."
198
+ }
199
+
149
200
  # === Helper: collect projects to sync ===
150
201
  collect_projects() {
151
202
  local found=()
@@ -293,6 +344,12 @@ print_registry_hint() {
293
344
  return 0
294
345
  }
295
346
 
347
+ if [[ $SCAN -eq 1 ]]; then
348
+ echo ""
349
+ echo "==> --scan: dò OM project để seed registry..."
350
+ scan_seed
351
+ fi
352
+
296
353
  if [[ $ALL_PROJECTS -eq 1 ]]; then
297
354
  echo ""
298
355
  echo "==> --all-projects: dọn registry + gom mọi project đã đăng ký..."