openmoneta-dev-kit 1.10.5 → 1.11.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.
- package/VERSION +1 -1
- package/opencode/AGENTS.md.tpl +15 -2
- package/opencode/plugins/openmoneta-guard.ts +221 -2
- package/package.json +1 -1
- package/templates/AGENTS.md.tpl +4 -2
- package/update.ps1 +16 -2
- package/update.sh +15 -2
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.
|
|
1
|
+
1.11.1
|
package/opencode/AGENTS.md.tpl
CHANGED
|
@@ -6,18 +6,31 @@
|
|
|
6
6
|
|
|
7
7
|
1. Luôn trả lời bằng tiếng Việt.
|
|
8
8
|
2. Khi project có `docs/INDEX.md`, đọc file đó trước khi đọc source code để dùng Token Routing.
|
|
9
|
-
3. Dùng OpenMoneta workflow 6 bước
|
|
9
|
+
3. Dùng OpenMoneta workflow 6 bước (xem bên dưới).
|
|
10
10
|
4. Task nhỏ/rõ ràng có thể không cần plan. Task lớn/rủi ro/mơ hồ phải tạo repo plan `Status: Draft`, trình user review, chỉ triển khai sau khi user approve và đổi `Status: In Progress`.
|
|
11
11
|
5. Nếu có plan `In Progress`, chỉ sửa file trong `## Files ảnh hưởng` / `## Files thay đổi`.
|
|
12
12
|
6. Khi user yêu cầu push, phải fetch/rebase remote ngay trước push; không dùng `git push --force` trên shared branch.
|
|
13
13
|
|
|
14
|
+
## Quy trình 6 bước
|
|
15
|
+
|
|
16
|
+
- **B1 Phân tích** (skill `requirement-analysis`): Read `docs/INDEX.md` → match Token Routing → đọc README module liên quan → đọc source → hỏi clarify nếu yêu cầu chưa rõ (ghi vào `## Hiểu yêu cầu` của plan).
|
|
17
|
+
- **B2 Thiết kế** (skill `module-architect`): SRP 1 module 1 trách nhiệm. Feature có concept riêng → module mới (KHÔNG nhét utils/common/shared). **Module có source nhưng chưa có doc → BẮT BUỘC backfill `docs/modules/<slug>/README.md` 3 sections (Trách nhiệm + Public API + Dependencies)** trước khi sang B4. Module mới → thêm keyword vào Token Routing của `docs/INDEX.md`.
|
|
18
|
+
- **B3 Adaptive Plan** (skill `plan-writer`): task nhỏ/rõ → không cần plan. Task lớn/rủi ro/mơ hồ → tạo repo plan `Status: Draft`, trình user review, chỉ code sau khi user approve và đổi `Status: In Progress`.
|
|
19
|
+
- **B4 Triển khai**: có plan active → chỉ edit file trong scope plan; không plan → chỉ sửa đúng yêu cầu. Mỗi sub-task xong → tick `- [x]`.
|
|
20
|
+
- **B5 Update doc + close plan** — **mandatory closing checklist** (plugin guard `event:session.idle` sẽ re-prompt nếu thiếu):
|
|
21
|
+
- [ ] Sync `docs/modules/<slug>/README.md` cho mọi module bị sửa: **Trách nhiệm + Public API + Dependencies**.
|
|
22
|
+
- [ ] Module mới đã có trong bảng "Modules hiện có" + "Token Routing" của `docs/INDEX.md`.
|
|
23
|
+
- [ ] Plan → `Status: Done` + tick mọi checkbox.
|
|
24
|
+
- [ ] **AUTO-ARCHIVE**: `git mv plans/<file>.md plans/archive/<file>.md` + update `plans/INDEX.md` (Active → Archived).
|
|
25
|
+
- **B6 Pre-push Sync + Safe Push** (skill `safe-push`, CONDITIONAL): chỉ khi user yêu cầu push. `git fetch` + rebase trước push, conflict logic → hỏi user, `git push` thường, KHÔNG `--force` shared branch.
|
|
26
|
+
|
|
14
27
|
## OpenCode Notes
|
|
15
28
|
|
|
16
29
|
- OpenCode đọc global rules từ `~/.config/opencode/AGENTS.md`.
|
|
17
30
|
- OpenCode đọc project rules từ `AGENTS.md` ở project root. Project rules là source of truth cho từng repo.
|
|
18
31
|
- OpenMoneta skills được cài vào `~/.config/opencode/skills/*/SKILL.md` và agent nên load bằng skill tool khi cần.
|
|
19
32
|
- OpenMoneta subagents được cài vào `~/.config/opencode/agents/`.
|
|
20
|
-
- OpenCode plugin guard trong `~/.config/opencode/plugins/openmoneta-guard.ts` enforce
|
|
33
|
+
- OpenCode plugin guard trong `~/.config/opencode/plugins/openmoneta-guard.ts` enforce workflow tương đương Cursor hooks: `tool.execute.before` (docs-first + plan gate), `tool.execute.after` (track changes), và `event:session.idle` (verify B2/B5: module README + close plan, re-prompt nhắc hoàn tất với loop guard ≤4 lần).
|
|
21
34
|
|
|
22
35
|
## Khi Bắt Đầu Task
|
|
23
36
|
|
|
@@ -253,11 +253,174 @@ function fail(message: string): never {
|
|
|
253
253
|
throw new Error(`[OpenMoneta Dev Kit]\n${message}`)
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
-
|
|
256
|
+
// === verify-completion parity (port từ Cursor hook verify-completion.sh) ===
|
|
257
|
+
|
|
258
|
+
const VERIFY_LOOP_LIMIT = 4
|
|
259
|
+
const NON_CODE_CHANGE = /^(docs\/|plans\/|README|\.gitignore|\.env\.example|AGENTS\.md|\.cursor\/)/
|
|
260
|
+
|
|
261
|
+
function readSessionChanges(root: string): string[] {
|
|
262
|
+
const file = path.join(root, ".cursor", ".session-changes.json")
|
|
263
|
+
if (!fs.existsSync(file)) return []
|
|
264
|
+
try {
|
|
265
|
+
const data = JSON.parse(fs.readFileSync(file, "utf8")) as { changes?: { path?: string }[] }
|
|
266
|
+
return (data.changes || []).map((c) => asString(c.path)).filter(Boolean)
|
|
267
|
+
} catch {
|
|
268
|
+
return []
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function clearSessionChanges(root: string) {
|
|
273
|
+
fs.rmSync(path.join(root, ".cursor", ".session-changes.json"), { force: true })
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Port logic infer module slug từ scripts/list-affected-modules.sh
|
|
277
|
+
function inferModules(paths: string[]): { slug: string; docsPath: string }[] {
|
|
278
|
+
const out = new Map<string, string>()
|
|
279
|
+
for (const p of paths) {
|
|
280
|
+
if (!p) continue
|
|
281
|
+
if (/^(docs|plans|tests|scripts|infra|logs|\.cursor|node_modules)\//.test(p)) continue
|
|
282
|
+
if (["AGENTS.md", "CHANGELOG.md", "README.md", ".gitignore", ".env.example"].includes(p)) continue
|
|
283
|
+
const seg = p.split("/")
|
|
284
|
+
if (p.startsWith("apps/") && seg.length >= 2) {
|
|
285
|
+
out.set(`apps-${seg[1]}`, `docs/modules/apps-${seg[1]}`)
|
|
286
|
+
} else if (p.startsWith("packages/") && seg.length >= 2) {
|
|
287
|
+
out.set(`packages-${seg[1]}`, `docs/modules/packages-${seg[1]}`)
|
|
288
|
+
} else if (p.startsWith("src/modules/") && seg.length >= 3) {
|
|
289
|
+
out.set(seg[2], `docs/modules/${seg[2]}`)
|
|
290
|
+
} else if (p.startsWith("src/") && seg.length >= 2 && seg[1] !== "modules") {
|
|
291
|
+
out.set(seg[1], `docs/modules/${seg[1]}`)
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return [...out.entries()].map(([slug, docsPath]) => ({ slug, docsPath }))
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function planUntickedCount(planPath: string): number {
|
|
298
|
+
const match = fs.readFileSync(planPath, "utf8").match(/^- \[ \]/gm)
|
|
299
|
+
return match ? match.length : 0
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Check 9: có heading "## Hiểu yêu cầu" + nội dung không trống (bỏ dòng blockquote `>`)
|
|
303
|
+
function planHasUnderstanding(planPath: string): boolean {
|
|
304
|
+
const lines = fs.readFileSync(planPath, "utf8").split(/\r?\n/)
|
|
305
|
+
let inSection = false
|
|
306
|
+
let foundHeading = false
|
|
307
|
+
let hasContent = false
|
|
308
|
+
for (const line of lines) {
|
|
309
|
+
if (/^##\s+Hiểu yêu cầu/.test(line)) {
|
|
310
|
+
foundHeading = true
|
|
311
|
+
inSection = true
|
|
312
|
+
continue
|
|
313
|
+
}
|
|
314
|
+
if (inSection && /^##\s+/.test(line)) break
|
|
315
|
+
if (inSection) {
|
|
316
|
+
const trimmed = line.trim()
|
|
317
|
+
if (trimmed && !trimmed.startsWith(">")) hasContent = true
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return foundHeading && hasContent
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Build danh sách issue tương đương verify-completion Check 5/6/9.
|
|
324
|
+
function buildVerifyIssues(root: string): string[] {
|
|
325
|
+
const paths = readSessionChanges(root)
|
|
326
|
+
if (paths.length === 0) return []
|
|
327
|
+
if (!paths.some((p) => !NON_CODE_CHANGE.test(p))) return []
|
|
328
|
+
|
|
329
|
+
const issues: string[] = []
|
|
330
|
+
|
|
331
|
+
const draftPlans = listPlans(root, "Draft")
|
|
332
|
+
const inProgressPlans = listPlans(root, "In Progress")
|
|
333
|
+
const activePlans = [...draftPlans, ...inProgressPlans]
|
|
334
|
+
|
|
335
|
+
// Check 5: plan active phải Done + hết checkbox
|
|
336
|
+
for (const plan of activePlans) {
|
|
337
|
+
const name = path.basename(plan)
|
|
338
|
+
const unticked = planUntickedCount(plan)
|
|
339
|
+
if (unticked > 0) {
|
|
340
|
+
issues.push(`❌ Plan '${name}' còn ${unticked} checkbox '- [ ]' chưa tick. Hoàn thành các task (Bước 5).`)
|
|
341
|
+
}
|
|
342
|
+
if (draftPlans.includes(plan)) {
|
|
343
|
+
issues.push(
|
|
344
|
+
`❌ Plan '${name}' vẫn ở trạng thái Draft (chưa được user approve) nhưng có code change gắn vào.`,
|
|
345
|
+
)
|
|
346
|
+
} else {
|
|
347
|
+
issues.push(
|
|
348
|
+
`❌ Plan '${name}' vẫn In Progress. Đổi Status: Done + git mv plans/${name} sang plans/archive/ (Bước 5).`,
|
|
349
|
+
)
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Check 9: plan active + plan sửa trong session phải có "## Hiểu yêu cầu" không trống
|
|
354
|
+
const planSet = new Set<string>(activePlans)
|
|
355
|
+
for (const rel of paths) {
|
|
356
|
+
if (/^plans\/.*\.md$/.test(rel) && rel !== "plans/INDEX.md") {
|
|
357
|
+
const abs = path.join(root, rel)
|
|
358
|
+
if (fs.existsSync(abs)) planSet.add(abs)
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
for (const plan of planSet) {
|
|
362
|
+
if (!planHasUnderstanding(plan)) {
|
|
363
|
+
issues.push(
|
|
364
|
+
`❌ Plan '${path.basename(plan)}' thiếu/trống section '## Hiểu yêu cầu' (audit clarify Bước 1).`,
|
|
365
|
+
)
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Check 6: module bị sửa phải có docs/modules/<slug>/README.md
|
|
370
|
+
for (const { slug, docsPath } of inferModules(paths)) {
|
|
371
|
+
if (!fs.existsSync(path.join(root, docsPath, "README.md"))) {
|
|
372
|
+
issues.push(
|
|
373
|
+
`❌ Module '${slug}' bị sửa nhưng thiếu '${docsPath}/README.md'. Tạo README 3 sections (Trách nhiệm + Public API + Dependencies) + cập nhật docs/INDEX.md (Bước 2/5).`,
|
|
374
|
+
)
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return issues
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
type VerifyLoopState = { sessionID: string; count: number }
|
|
382
|
+
|
|
383
|
+
function readVerifyLoop(root: string): VerifyLoopState {
|
|
384
|
+
const file = path.join(root, ".cursor", ".openmoneta-verify-loop.json")
|
|
385
|
+
if (!fs.existsSync(file)) return { sessionID: "", count: 0 }
|
|
386
|
+
try {
|
|
387
|
+
return JSON.parse(fs.readFileSync(file, "utf8")) as VerifyLoopState
|
|
388
|
+
} catch {
|
|
389
|
+
return { sessionID: "", count: 0 }
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function writeVerifyLoop(root: string, state: VerifyLoopState) {
|
|
394
|
+
const file = path.join(root, ".cursor", ".openmoneta-verify-loop.json")
|
|
395
|
+
fs.mkdirSync(path.dirname(file), { recursive: true })
|
|
396
|
+
fs.writeFileSync(file, `${JSON.stringify(state, null, 2)}\n`)
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function clearVerifyLoop(root: string) {
|
|
400
|
+
fs.rmSync(path.join(root, ".cursor", ".openmoneta-verify-loop.json"), { force: true })
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
type SessionPromptClient = {
|
|
404
|
+
session?: {
|
|
405
|
+
prompt?: (input: {
|
|
406
|
+
path: { id: string }
|
|
407
|
+
body: { parts: { type: string; text: string }[] }
|
|
408
|
+
}) => Promise<unknown>
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
type GuardContext = {
|
|
413
|
+
worktree?: string
|
|
414
|
+
directory?: string
|
|
415
|
+
client?: SessionPromptClient
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
export const OpenMonetaGuard = async (ctx: GuardContext) => {
|
|
257
419
|
const globalKey = Symbol.for("openmoneta.devkit.guard.loaded")
|
|
258
420
|
const globalState = globalThis as Record<symbol, number>
|
|
259
421
|
globalState[globalKey] = (globalState[globalKey] || 0) + 1
|
|
260
422
|
|
|
423
|
+
const client = ctx.client
|
|
261
424
|
const root = projectRoot(ctx)
|
|
262
425
|
const marker = path.join(root, ".cursor", ".docs-index-read")
|
|
263
426
|
const guardLoadedMarker = path.join(root, ".cursor", ".openmoneta-guard-loaded.json")
|
|
@@ -271,7 +434,7 @@ export const OpenMonetaGuard = async (ctx: { worktree?: string; directory?: stri
|
|
|
271
434
|
{
|
|
272
435
|
loaded_at: new Date().toISOString(),
|
|
273
436
|
root,
|
|
274
|
-
version: "1.
|
|
437
|
+
version: "1.11.0",
|
|
275
438
|
load_count: globalState[globalKey],
|
|
276
439
|
},
|
|
277
440
|
null,
|
|
@@ -383,6 +546,62 @@ export const OpenMonetaGuard = async (ctx: { worktree?: string; directory?: stri
|
|
|
383
546
|
trackChange(root, relativePath, input.tool)
|
|
384
547
|
}
|
|
385
548
|
},
|
|
549
|
+
|
|
550
|
+
// Parity với Cursor hook `stop`/verify-completion. OpenCode `event` không block
|
|
551
|
+
// được như Cursor, nên khi phiên idle mà còn thiếu B2/B5 ta re-prompt lại session
|
|
552
|
+
// để buộc Agent hoàn tất, kèm loop guard chống lặp vô hạn.
|
|
553
|
+
event: async ({
|
|
554
|
+
event,
|
|
555
|
+
}: {
|
|
556
|
+
event: { type: string; properties?: Record<string, unknown> }
|
|
557
|
+
}) => {
|
|
558
|
+
if (event.type !== "session.idle") return
|
|
559
|
+
try {
|
|
560
|
+
const issues = buildVerifyIssues(root)
|
|
561
|
+
const sessionID = asString(event.properties?.sessionID)
|
|
562
|
+
|
|
563
|
+
if (issues.length === 0) {
|
|
564
|
+
clearSessionChanges(root)
|
|
565
|
+
clearVerifyLoop(root)
|
|
566
|
+
return
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
const loop = readVerifyLoop(root)
|
|
570
|
+
const count = loop.sessionID === sessionID ? loop.count : 0
|
|
571
|
+
|
|
572
|
+
if (count >= VERIFY_LOOP_LIMIT) {
|
|
573
|
+
console.warn(
|
|
574
|
+
`[OpenMoneta Dev Kit] verify-completion: đã nhắc ${count} lần, graceful exit. ` +
|
|
575
|
+
`Hãy review thủ công:\n${issues.join("\n")}`,
|
|
576
|
+
)
|
|
577
|
+
return
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (!client?.session?.prompt || !sessionID) {
|
|
581
|
+
console.warn(
|
|
582
|
+
`[OpenMoneta Dev Kit] verify-completion còn ${issues.length} việc nhưng không re-prompt được ` +
|
|
583
|
+
`(thiếu client/sessionID):\n${issues.join("\n")}`,
|
|
584
|
+
)
|
|
585
|
+
return
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
const message = [
|
|
589
|
+
"[OpenMoneta Dev Kit — verify-completion] Phiên chưa nên kết thúc, còn thiếu Bước 2/5:",
|
|
590
|
+
"",
|
|
591
|
+
...issues,
|
|
592
|
+
"",
|
|
593
|
+
`Hoàn tất các mục trên rồi mới kết thúc. (Còn ${VERIFY_LOOP_LIMIT - count - 1} lần nhắc nữa.)`,
|
|
594
|
+
].join("\n")
|
|
595
|
+
|
|
596
|
+
await client.session.prompt({
|
|
597
|
+
path: { id: sessionID },
|
|
598
|
+
body: { parts: [{ type: "text", text: message }] },
|
|
599
|
+
})
|
|
600
|
+
writeVerifyLoop(root, { sessionID, count: count + 1 })
|
|
601
|
+
} catch (err) {
|
|
602
|
+
console.warn(`[OpenMoneta Dev Kit] verify-completion event error: ${String(err)}`)
|
|
603
|
+
}
|
|
604
|
+
},
|
|
386
605
|
}
|
|
387
606
|
}
|
|
388
607
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openmoneta-dev-kit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.1",
|
|
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/templates/AGENTS.md.tpl
CHANGED
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
|
|
55
55
|
### Bước 5 — Update doc + close plan
|
|
56
56
|
|
|
57
|
-
> Skill: `module-architect` (đã merge `doc-maintainer`).
|
|
57
|
+
> Skill: `module-architect` (đã merge `doc-maintainer`). Cursor: hook `verify-completion` Check 6 chặn kết thúc nếu module README thiếu. OpenCode: plugin guard `event:session.idle` re-prompt nhắc hoàn tất (loop guard ≤4 lần).
|
|
58
58
|
|
|
59
59
|
- Sync `docs/modules/<slug>/README.md` (Public API + Dependencies) nếu API đổi.
|
|
60
60
|
- Module mới → đảm bảo đã có trong bảng "Modules hiện có" + "Token Routing" của `docs/INDEX.md`.
|
|
@@ -91,11 +91,13 @@
|
|
|
91
91
|
|
|
92
92
|
## Hooks enforce
|
|
93
93
|
|
|
94
|
+
> Cursor dùng bash hooks (`~/.cursor/hooks/`). OpenCode dùng plugin guard `openmoneta-guard.ts` — `tool.execute.before/after` tương đương 2 hook đầu, `event:session.idle` tương đương `verify-completion` (re-prompt thay vì block cứng).
|
|
95
|
+
|
|
94
96
|
| Hook | Khi nào | Hậu quả |
|
|
95
97
|
|---|---|---|
|
|
96
98
|
| `enforce-docs-first` | preToolUse Read/Glob/Grep | BLOCK source code đọc nếu chưa Read `docs/INDEX.md` |
|
|
97
99
|
| `check-plan-exists` | preToolUse Write/StrReplace/EditNotebook/Delete | ALLOW task thường không plan; BLOCK file nhạy cảm không plan, plan Draft, hoặc file ngoài scope plan |
|
|
98
|
-
| `verify-completion` | stop | BLOCK kết thúc nếu Check 5/6/9 fail (plan Done + checkbox, module README, "Hiểu yêu cầu") |
|
|
100
|
+
| `verify-completion` | stop (Cursor) / `session.idle` (OpenCode) | Cursor BLOCK kết thúc nếu Check 5/6/9 fail; OpenCode re-prompt nhắc hoàn tất (plan Done + checkbox, module README, "Hiểu yêu cầu"), loop guard ≤4 lần |
|
|
99
101
|
|
|
100
102
|
## Override cho dự án này
|
|
101
103
|
|
package/update.ps1
CHANGED
|
@@ -160,11 +160,25 @@ if ($CurrentVersion -eq $NewVersion) {
|
|
|
160
160
|
Write-Host "Đã ở phiên bản mới nhất (v$CurrentVersion). Vẫn cài lại để đảm bảo đồng bộ."
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
# === Step 2: Global install ===
|
|
163
|
+
# === Step 2: Global install (Cursor) ===
|
|
164
164
|
Write-Host ""
|
|
165
|
-
Write-Host "==> Chạy install.ps1..."
|
|
165
|
+
Write-Host "==> Chạy install.ps1 (Cursor)..."
|
|
166
166
|
& "$RepoDir\install.ps1" -Yes
|
|
167
167
|
|
|
168
|
+
# === Step 2B: Global install (OpenCode) ===
|
|
169
|
+
# Chỉ chạy nếu máy đã cài OpenMoneta cho OpenCode hoặc có binary `opencode`.
|
|
170
|
+
$OpenCodeDir = if ($env:XDG_CONFIG_HOME) { "$env:XDG_CONFIG_HOME\opencode" } else { "$env:USERPROFILE\.config\opencode" }
|
|
171
|
+
if ((Test-Path "$OpenCodeDir\.openmoneta-version") -or (Get-Command opencode -ErrorAction SilentlyContinue)) {
|
|
172
|
+
Write-Host ""
|
|
173
|
+
Write-Host "==> Phát hiện OpenCode -> chạy install-opencode.ps1..."
|
|
174
|
+
& "$RepoDir\install-opencode.ps1" -Yes
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
Write-Host ""
|
|
178
|
+
Write-Host "ℹ️ Không phát hiện OpenCode, bỏ qua install-opencode.ps1."
|
|
179
|
+
Write-Host " Cài tay nếu cần: pwsh $RepoDir\install-opencode.ps1"
|
|
180
|
+
}
|
|
181
|
+
|
|
168
182
|
Write-Host ""
|
|
169
183
|
Write-Host "✅ Global update hoàn tất: v$CurrentVersion -> v$NewVersion" -ForegroundColor Green
|
|
170
184
|
if ($CurrentVersion -ne $NewVersion) {
|
package/update.sh
CHANGED
|
@@ -185,11 +185,24 @@ if [[ "$CURRENT_VERSION" == "$NEW_VERSION" ]]; then
|
|
|
185
185
|
echo "Đã ở phiên bản mới nhất (v$CURRENT_VERSION). Vẫn cài lại để đảm bảo đồng bộ."
|
|
186
186
|
fi
|
|
187
187
|
|
|
188
|
-
# === Step 2: Global install ===
|
|
188
|
+
# === Step 2: Global install (Cursor) ===
|
|
189
189
|
echo ""
|
|
190
|
-
echo "==> Chạy install.sh..."
|
|
190
|
+
echo "==> Chạy install.sh (Cursor)..."
|
|
191
191
|
bash "$REPO_DIR/install.sh" --yes
|
|
192
192
|
|
|
193
|
+
# === Step 2B: Global install (OpenCode) ===
|
|
194
|
+
# Chỉ chạy nếu máy đã cài OpenMoneta cho OpenCode hoặc có binary `opencode`.
|
|
195
|
+
OPENCODE_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/opencode"
|
|
196
|
+
if [[ -f "$OPENCODE_DIR/.openmoneta-version" ]] || command -v opencode >/dev/null 2>&1; then
|
|
197
|
+
echo ""
|
|
198
|
+
echo "==> Phát hiện OpenCode → chạy install-opencode.sh..."
|
|
199
|
+
bash "$REPO_DIR/install-opencode.sh" --yes
|
|
200
|
+
else
|
|
201
|
+
echo ""
|
|
202
|
+
echo "ℹ️ Không phát hiện OpenCode, bỏ qua install-opencode.sh."
|
|
203
|
+
echo " Cài tay nếu cần: bash $REPO_DIR/install-opencode.sh"
|
|
204
|
+
fi
|
|
205
|
+
|
|
193
206
|
echo ""
|
|
194
207
|
echo "✅ Global update hoàn tất: v$CURRENT_VERSION → v$NEW_VERSION"
|
|
195
208
|
echo ""
|