evnict-kit 0.2.2 → 0.2.4
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 +39 -2
- package/bin/cli.js +7 -1
- package/package.json +1 -1
- package/scripts/patch-workflows.js +142 -0
- package/scripts/postinstall.js +1 -1
- package/src/commands/init.js +18 -9
- package/src/commands/sync.js +317 -0
- package/src/commands/upgrade.js +7 -1
- package/src/utils/config.js +2 -2
- package/templates/GETTING-STARTED.md +23 -2
- package/templates/content/skills/evnict-kit-bug-fix/SKILL.md +1 -1
- package/templates/content/skills/evnict-kit-git-worktrees/SKILL.md +0 -2
- package/templates/content/skills/evnict-kit-onboard/SKILL.md +0 -1
- package/templates/content/skills/evnict-kit-spec/SKILL.md +1 -1
- package/templates/content/skills/evnict-kit-wiki/SKILL.md +16 -54
- package/templates/content/workflows/evnict-kit-archive-wiki.md +4 -5
- package/templates/content/workflows/evnict-kit-attt.md +1 -1
- package/templates/content/workflows/evnict-kit-bug-fix.md +1 -1
- package/templates/content/workflows/evnict-kit-feature-large.md +2 -2
- package/templates/content/workflows/evnict-kit-feature-small.md +1 -1
- package/templates/content/workflows/evnict-kit-init-wiki.md +18 -47
- package/templates/content/workflows/evnict-kit-wiki-archive-feature.md +2 -3
- package/templates/content/workflows/evnict-kit-wiki-query.md +3 -3
- package/templates/content/workflows/evnict-kit-wiki-scan-project.md +3 -4
- package/templates/skills/evnict-kit-bug-fix/SKILL.md +1 -1
- package/templates/skills/evnict-kit-create-component/SKILL.md +23 -0
- package/templates/skills/evnict-kit-create-page/SKILL.md +23 -0
- package/templates/skills/evnict-kit-frontend-design/SKILL.md +161 -0
- package/templates/skills/evnict-kit-spec/SKILL.md +1 -1
- package/templates/skills/evnict-kit-wiki/SKILL.md +16 -54
- package/templates/wiki/AGENTS.md +280 -0
- package/templates/wiki/CLAUDE.md +280 -0
- package/templates/wiki/FAQ.md +168 -0
- package/templates/wiki/README.md +272 -35
- package/templates/wiki/config.example.yaml +145 -17
- package/templates/wiki/scripts/run-wiki.sh +17 -0
- package/templates/wiki/scripts/setup-scheduler.ps1 +29 -0
- package/templates/wiki/skills/llm-wiki/SKILL.md +341 -0
- package/templates/wiki/wiki/INDEX.md +9 -0
- package/templates/wiki/wiki/INDEX.template.md +29 -0
- package/templates/wiki/wiki/LOG.md +3 -0
- package/templates/wiki/wiki/LOG.template.md +7 -0
- package/templates/wiki/wiki-viewer.html +408 -0
- package/templates/workflows/antigravity/evnict-kit-archive-wiki.md +24 -5
- package/templates/workflows/antigravity/evnict-kit-attt.md +21 -1
- package/templates/workflows/antigravity/evnict-kit-bug-fix.md +24 -1
- package/templates/workflows/antigravity/evnict-kit-feature-large.md +26 -2
- package/templates/workflows/antigravity/evnict-kit-feature-small.md +24 -1
- package/templates/workflows/antigravity/evnict-kit-handoff.md +23 -0
- package/templates/workflows/antigravity/evnict-kit-implement.md +23 -0
- package/templates/workflows/antigravity/evnict-kit-init-check.md +20 -0
- package/templates/workflows/antigravity/evnict-kit-init-context.md +20 -0
- package/templates/workflows/antigravity/evnict-kit-init-rules.md +20 -0
- package/templates/workflows/antigravity/evnict-kit-init-wiki.md +26 -8
- package/templates/workflows/antigravity/evnict-kit-plan.md +24 -0
- package/templates/workflows/antigravity/evnict-kit-review.md +20 -0
- package/templates/workflows/antigravity/evnict-kit-spec-archive.md +20 -0
- package/templates/workflows/antigravity/evnict-kit-wiki-archive-feature.md +22 -3
- package/templates/workflows/antigravity/evnict-kit-wiki-query.md +23 -3
- package/templates/workflows/antigravity/evnict-kit-wiki-scan-project.md +23 -4
- package/templates/workflows/claude/evnict-kit-archive-wiki.md +24 -5
- package/templates/workflows/claude/evnict-kit-attt.md +21 -1
- package/templates/workflows/claude/evnict-kit-bug-fix.md +24 -1
- package/templates/workflows/claude/evnict-kit-feature-large.md +26 -2
- package/templates/workflows/claude/evnict-kit-feature-small.md +24 -1
- package/templates/workflows/claude/evnict-kit-handoff.md +23 -0
- package/templates/workflows/claude/evnict-kit-implement.md +23 -0
- package/templates/workflows/claude/evnict-kit-init-check.md +20 -0
- package/templates/workflows/claude/evnict-kit-init-context.md +20 -0
- package/templates/workflows/claude/evnict-kit-init-rules.md +20 -0
- package/templates/workflows/claude/evnict-kit-init-wiki.md +25 -7
- package/templates/workflows/claude/evnict-kit-plan.md +24 -0
- package/templates/workflows/claude/evnict-kit-review.md +20 -0
- package/templates/workflows/claude/evnict-kit-spec-archive.md +20 -0
- package/templates/workflows/claude/evnict-kit-wiki-archive-feature.md +22 -3
- package/templates/workflows/claude/evnict-kit-wiki-query.md +23 -3
- package/templates/workflows/claude/evnict-kit-wiki-scan-project.md +23 -4
- package/templates/wiki/package.json +0 -17
- package/templates/wiki/raw/notes/.gitkeep +0 -1
- package/templates/wiki/scripts/ingest.js +0 -66
package/README.md
CHANGED
|
@@ -69,6 +69,29 @@ Mở AI Agent (Antigravity / Claude / Cursor / ...) trong thư mục project, sa
|
|
|
69
69
|
|
|
70
70
|
---
|
|
71
71
|
|
|
72
|
+
## 🔄 Cập nhật phiên bản mới (Upgrade)
|
|
73
|
+
|
|
74
|
+
Khi có bản cập nhật mới của `evnict-kit`, bạn làm theo 2 bước sau để nâng cấp hoàn chỉnh:
|
|
75
|
+
|
|
76
|
+
### 1. Nâng cấp công cụ trên máy (Global)
|
|
77
|
+
Cập nhật package cài đặt chung cho môi trường làm việc của bạn:
|
|
78
|
+
```bash
|
|
79
|
+
npm update -g evnict-kit
|
|
80
|
+
# Hoặc an toàn hơn, dùng lệnh có sẵn:
|
|
81
|
+
evnict-kit upgrade
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 2. Đồng bộ file vào Project hiện tại (Sync)
|
|
85
|
+
Lưu ý: Các dự án của bạn (đã được `init` từ bản cũ) **sẽ không tự động nhận** các Workflows, Skills hay Rules mới.
|
|
86
|
+
Bạn cần di chuyển vào thư mục dự án và chạy lệnh:
|
|
87
|
+
```bash
|
|
88
|
+
cd /path/to/workspace/your-project
|
|
89
|
+
evnict-kit sync
|
|
90
|
+
```
|
|
91
|
+
> **💡 Tính năng lệnh Sync:** Hệ thống sẽ tự động phát hiện xem project đang dùng AI Tool nào (Antigravity/Claude/Cursor...) và cập nhật/ghi đè các file chuẩn của `evnict-kit`, trong khi vẫn **GIỮ NGUYÊN HOÀN TOÀN** các đoạn code hoặc Project Rules riêng mà bạn tự định nghĩa.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
72
95
|
## 🎯 Tính năng
|
|
73
96
|
|
|
74
97
|
### 🧠 Rules Engine — 5 Rule Sets
|
|
@@ -158,6 +181,7 @@ evnict-kit init-rules --tool=cursor
|
|
|
158
181
|
| `evnict-kit doctor` | 🩺 Kiểm tra môi trường, phiên bản, cập nhật |
|
|
159
182
|
| `evnict-kit info` | 📊 Thống kê chi tiết toolkit |
|
|
160
183
|
| `evnict-kit upgrade` | 🔄 Kiểm tra & cập nhật phiên bản mới nhất |
|
|
184
|
+
| `evnict-kit sync` | 🔄 Re-deploy templates vào các project — ghi đè files evnict-kit, giữ nguyên files user |
|
|
161
185
|
|
|
162
186
|
### Lệnh Agent (gõ trong AI Agent chat)
|
|
163
187
|
|
|
@@ -281,7 +305,7 @@ FE: Đọc handoff.md → implement → update 🟢 Đã xử lý
|
|
|
281
305
|
| Metric | Count |
|
|
282
306
|
|--------|-------|
|
|
283
307
|
| Rule Sets | 5 |
|
|
284
|
-
| Skills |
|
|
308
|
+
| Skills | 23 |
|
|
285
309
|
| Workflows | 17 |
|
|
286
310
|
| Supported AI Tools | 5 |
|
|
287
311
|
| Tech Stacks (BE) | SpringBoot · ASP.NET · Java EE |
|
|
@@ -305,7 +329,20 @@ FE: Đọc handoff.md → implement → update 🟢 Đã xử lý
|
|
|
305
329
|
|
|
306
330
|
## 📜 Changelog
|
|
307
331
|
|
|
308
|
-
### v0.2.
|
|
332
|
+
### v0.2.4 (Current)
|
|
333
|
+
- 📚 **Native llm-wiki Integration**: Chuyển đổi toàn bộ quy trình Wiki sang chuẩn `llm-wiki` với Agent-driven ingestion — xoá bỏ sự phụ thuộc script Node.js (`scripts/ingest.js`)
|
|
334
|
+
- 🧹 **Cấu trúc Wiki chuẩn hóa**: Wiki project giờ đây được sắp xếp sạch sẽ trong `wiki/entities/`, `wiki/concepts/`, `wiki/sources/`, `wiki/syntheses/` (thay vì folder `processed/` cũ)
|
|
335
|
+
- 🤖 **Auto-Skills**: Agent tự động dùng skill `llm-wiki` (`SKILL.md`) để đọc code, sinh tri thức và trích xuất thực thể chuyên nghiệp
|
|
336
|
+
- 🗑️ **Refactoring**: Cập nhật toàn bộ Agent workflow (scan, query, archive) bám sát kiến trúc mới
|
|
337
|
+
|
|
338
|
+
### v0.2.3
|
|
339
|
+
- 🎨 **Frontend Design Skill**: Skill mới `evnict-kit-frontend-design` — hướng dẫn tư duy thiết kế UI chất lượng cao cho Angular, tránh "AI slop" aesthetics
|
|
340
|
+
- 🧠 **Context Refresh**: Thêm section "Tuân thủ Rules & Context" vào TẤT CẢ 36 workflows/skills — cơ chế smart check (chỉ đọc lại nếu chưa đọc, tiết kiệm token)
|
|
341
|
+
- 🎯 **FE Design Hint**: 6 workflows có code FE được bổ sung nhắc tham chiếu skill frontend-design
|
|
342
|
+
- 🔄 **Sync Command**: Lệnh `evnict-kit sync` — re-deploy templates khi upgrade version, không xóa files user tự thêm
|
|
343
|
+
- 📦 Hỗ trợ cả Antigravity + Claude workflows
|
|
344
|
+
|
|
345
|
+
### v0.2.2
|
|
309
346
|
- 🧹 Removed obsolete standalone commands (`init-rules`, `init-context`, `init-workflow`, `init-check`)
|
|
310
347
|
- 🩺 `doctor` command: health check, version check, workspace status
|
|
311
348
|
- 📊 `info` command: toolkit statistics dashboard
|
package/bin/cli.js
CHANGED
|
@@ -5,9 +5,10 @@ import { addCommand } from '../src/commands/add.js';
|
|
|
5
5
|
import { doctorCommand } from '../src/commands/doctor.js';
|
|
6
6
|
import { infoCommand } from '../src/commands/info.js';
|
|
7
7
|
import { upgradeCommand } from '../src/commands/upgrade.js';
|
|
8
|
+
import { syncCommand } from '../src/commands/sync.js';
|
|
8
9
|
|
|
9
10
|
const program = new Command();
|
|
10
|
-
program.name('evnict-kit').description('EVNICT AI-Assisted Development Toolkit v0.2.
|
|
11
|
+
program.name('evnict-kit').description('EVNICT AI-Assisted Development Toolkit v0.2.3').version('0.2.3');
|
|
11
12
|
|
|
12
13
|
program.command('init')
|
|
13
14
|
.description('Khoi tao workspace + deploy rules/skills/workflows vao tung project')
|
|
@@ -43,4 +44,9 @@ program.command('upgrade')
|
|
|
43
44
|
.option('-y, --yes', 'Auto-confirm upgrade')
|
|
44
45
|
.action(upgradeCommand);
|
|
45
46
|
|
|
47
|
+
program.command('sync')
|
|
48
|
+
.description('Re-deploy templates (workflows/skills/rules) vao cac project da init — ghi de files evnict-kit, giu nguyen files user')
|
|
49
|
+
.option('-y, --yes', 'Auto-confirm sync (khong hoi)')
|
|
50
|
+
.action(syncCommand);
|
|
51
|
+
|
|
46
52
|
program.parse();
|
package/package.json
CHANGED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Patch script: Add Context Refresh + FE Design hint to all workflows
|
|
3
|
+
* Run: node scripts/patch-workflows.js
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync, writeFileSync, readdirSync, existsSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
|
|
8
|
+
const TEMPLATES_DIR = join(import.meta.dirname, '..', 'templates');
|
|
9
|
+
|
|
10
|
+
// ═══════════════════════════════════════════
|
|
11
|
+
// Context Refresh block (smart check mechanism)
|
|
12
|
+
// ═══════════════════════════════════════════
|
|
13
|
+
const CONTEXT_REFRESH = `
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## ⚠️ NHẮC NHỞ: Tuân thủ Rules & Context
|
|
17
|
+
|
|
18
|
+
> **Agent dùng lâu có thể quên rules.** Trước khi bắt đầu code, tự kiểm tra:
|
|
19
|
+
|
|
20
|
+
### Quick Check (agent tự đánh giá)
|
|
21
|
+
1. ✅ Tôi đã đọc \`.agent/rules/\` trong phiên làm việc này chưa?
|
|
22
|
+
2. ✅ Tôi đã đọc AGENTS.md (hoặc context file) chưa?
|
|
23
|
+
3. ✅ Tôi nhớ rõ coding conventions của dự án này không?
|
|
24
|
+
|
|
25
|
+
**→ Nếu BẤT KỲ câu nào = "CHƯA" hoặc "KHÔNG CHẮC":**
|
|
26
|
+
- Đọc lại tất cả files trong \`.agent/rules/\` (hoặc thư mục rules tương ứng)
|
|
27
|
+
- Đọc lại AGENTS.md (hoặc context file tương ứng)
|
|
28
|
+
- Query wiki nếu có: \`grep -rl "{keyword}" {wiki_path}/processed/ --include="*.md"\`
|
|
29
|
+
|
|
30
|
+
**→ Nếu TẤT CẢ = "RỒI" → tiếp tục workflow, KHÔNG cần đọc lại.**
|
|
31
|
+
|
|
32
|
+
> **NGUYÊN TẮC:** Không chắc convention → ĐỌC LẠI rule file. KHÔNG đoán.
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
// ═══════════════════════════════════════════
|
|
36
|
+
// FE Design hint block
|
|
37
|
+
// ═══════════════════════════════════════════
|
|
38
|
+
const FE_DESIGN_HINT = `
|
|
39
|
+
> 🎨 **FE UI Quality:** Khi tạo/sửa UI component, tham khảo skill \`evnict-kit-frontend-design\`
|
|
40
|
+
> để đảm bảo chất lượng thiết kế cao. Áp dụng Design Thinking (Purpose → Tone → Constraints → Differentiation) trước khi code UI.
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
// ═══════════════════════════════════════════
|
|
44
|
+
// Files that need FE Design hint (in addition to Context Refresh)
|
|
45
|
+
// ═══════════════════════════════════════════
|
|
46
|
+
const FE_HINT_FILES = [
|
|
47
|
+
'evnict-kit-feature-small.md',
|
|
48
|
+
'evnict-kit-feature-large.md',
|
|
49
|
+
'evnict-kit-implement.md',
|
|
50
|
+
'evnict-kit-plan.md',
|
|
51
|
+
'evnict-kit-handoff.md',
|
|
52
|
+
'evnict-kit-bug-fix.md',
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
// ═══════════════════════════════════════════
|
|
56
|
+
// Process workflow files in a tool directory
|
|
57
|
+
// ═══════════════════════════════════════════
|
|
58
|
+
function patchFile(filePath, addFeHint) {
|
|
59
|
+
let content = readFileSync(filePath, 'utf8');
|
|
60
|
+
|
|
61
|
+
// Skip if already patched
|
|
62
|
+
if (content.includes('NHẮC NHỞ: Tuân thủ Rules & Context')) {
|
|
63
|
+
console.log(` ⏭️ Already patched: ${filePath}`);
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Find the insertion point — before "## Checklist" or "## Tiêu chí" at the end
|
|
68
|
+
const checklistPatterns = [
|
|
69
|
+
/\n## Checklist[^\n]*\n/,
|
|
70
|
+
/\n## Checklist hoàn thành[^\n]*\n/,
|
|
71
|
+
/\n## Tiêu chí hoàn thành[^\n]*\n/,
|
|
72
|
+
/\n## Tiêu chí thành công[^\n]*\n/,
|
|
73
|
+
/\n## Output[^\n]*\n/,
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
let insertPos = -1;
|
|
77
|
+
for (const pattern of checklistPatterns) {
|
|
78
|
+
const match = content.match(pattern);
|
|
79
|
+
if (match) {
|
|
80
|
+
insertPos = content.lastIndexOf(match[0]);
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let insertBlock = '';
|
|
86
|
+
|
|
87
|
+
// Add FE Design hint if applicable
|
|
88
|
+
if (addFeHint) {
|
|
89
|
+
insertBlock += FE_DESIGN_HINT;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Add Context Refresh
|
|
93
|
+
insertBlock += CONTEXT_REFRESH;
|
|
94
|
+
|
|
95
|
+
if (insertPos >= 0) {
|
|
96
|
+
// Insert before checklist
|
|
97
|
+
content = content.slice(0, insertPos) + insertBlock + content.slice(insertPos);
|
|
98
|
+
} else {
|
|
99
|
+
// No checklist found — append at end
|
|
100
|
+
content = content.trimEnd() + '\n' + insertBlock + '\n';
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
writeFileSync(filePath, content, 'utf8');
|
|
104
|
+
console.log(` ✅ Patched: ${filePath}${addFeHint ? ' (+FE Design)' : ''}`);
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ═══════════════════════════════════════════
|
|
109
|
+
// Main
|
|
110
|
+
// ═══════════════════════════════════════════
|
|
111
|
+
const toolDirs = ['antigravity', 'claude'];
|
|
112
|
+
let patchedCount = 0;
|
|
113
|
+
|
|
114
|
+
for (const tool of toolDirs) {
|
|
115
|
+
const wfDir = join(TEMPLATES_DIR, 'workflows', tool);
|
|
116
|
+
if (!existsSync(wfDir)) {
|
|
117
|
+
console.log(`⚠️ Skipping ${tool} — directory not found`);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
console.log(`\n══ Patching ${tool} workflows ══`);
|
|
122
|
+
const files = readdirSync(wfDir).filter(f => f.endsWith('.md') && f !== 'README.md');
|
|
123
|
+
|
|
124
|
+
for (const file of files) {
|
|
125
|
+
const addFeHint = FE_HINT_FILES.includes(file);
|
|
126
|
+
const patched = patchFile(join(wfDir, file), addFeHint);
|
|
127
|
+
if (patched) patchedCount++;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Also patch skills
|
|
132
|
+
const skillDirs = ['evnict-kit-create-component', 'evnict-kit-create-page'];
|
|
133
|
+
console.log(`\n══ Patching skills ══`);
|
|
134
|
+
for (const skillDir of skillDirs) {
|
|
135
|
+
const skillPath = join(TEMPLATES_DIR, 'skills', skillDir, 'SKILL.md');
|
|
136
|
+
if (existsSync(skillPath)) {
|
|
137
|
+
const patched = patchFile(skillPath, true);
|
|
138
|
+
if (patched) patchedCount++;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
console.log(`\n✅ Done! Patched ${patchedCount} files.`);
|
package/scripts/postinstall.js
CHANGED
package/src/commands/init.js
CHANGED
|
@@ -54,7 +54,7 @@ async function initInteractive(options) {
|
|
|
54
54
|
|
|
55
55
|
console.log(`
|
|
56
56
|
╔═══════════════════════════════════════════╗
|
|
57
|
-
║ 🚀 EVNICT-KIT v0.2.
|
|
57
|
+
║ 🚀 EVNICT-KIT v0.2.4: Init Setup ║
|
|
58
58
|
╚═══════════════════════════════════════════╝
|
|
59
59
|
`);
|
|
60
60
|
|
|
@@ -223,7 +223,7 @@ async function deployWorkspace({ name, tool, repoConfigs, db, wikiEnabled, cwd }
|
|
|
223
223
|
|
|
224
224
|
console.log(`
|
|
225
225
|
╔═══════════════════════════════════════════════════════╗
|
|
226
|
-
║ 🚀 EVNICT-KIT v0.2.
|
|
226
|
+
║ 🚀 EVNICT-KIT v0.2.4: Init Workspace ║
|
|
227
227
|
╚═══════════════════════════════════════════════════════╝
|
|
228
228
|
|
|
229
229
|
Project: ${name}
|
|
@@ -248,7 +248,7 @@ async function deployWorkspace({ name, tool, repoConfigs, db, wikiEnabled, cwd }
|
|
|
248
248
|
writeFile(join(evnictDir, 'handoff/be-status.md'), `# BE Status\nstatus: idle\n`, {cwd,silent:true});
|
|
249
249
|
writeFile(join(evnictDir, 'handoff/fe-status.md'), `# FE Status\nstatus: idle\n`, {cwd,silent:true});
|
|
250
250
|
|
|
251
|
-
// v0.2.
|
|
251
|
+
// v0.2.3: Tạo handoff.md template
|
|
252
252
|
const handoffTemplate = `# Agent Handoff Log
|
|
253
253
|
> File này dùng để trao đổi giữa BE Agent và FE Agent.
|
|
254
254
|
> Mỗi issue ghi theo format bên dưới.
|
|
@@ -304,15 +304,24 @@ async function deployWorkspace({ name, tool, repoConfigs, db, wikiEnabled, cwd }
|
|
|
304
304
|
if (existsSync(wikiSrc)) {
|
|
305
305
|
ensureDir(wikiPath, cwd);
|
|
306
306
|
const count = copyTemplateDir(wikiSrc, wikiPath, cwd);
|
|
307
|
+
|
|
308
|
+
// Tạo thêm folder structure cho Agent
|
|
309
|
+
ensureDir(join(wikiPath, 'wiki/entities'), cwd);
|
|
310
|
+
ensureDir(join(wikiPath, 'wiki/concepts'), cwd);
|
|
311
|
+
ensureDir(join(wikiPath, 'wiki/sources'), cwd);
|
|
312
|
+
ensureDir(join(wikiPath, 'wiki/syntheses'), cwd);
|
|
313
|
+
ensureDir(join(wikiPath, 'outputs'), cwd);
|
|
314
|
+
ensureDir(join(wikiPath, '.discoveries'), cwd);
|
|
315
|
+
|
|
307
316
|
const configPath = join(wikiPath, 'config.example.yaml');
|
|
308
317
|
if (existsSync(configPath)) {
|
|
309
318
|
let content = readFileSync(configPath, 'utf8');
|
|
310
|
-
content = content.replaceAll('
|
|
319
|
+
content = content.replaceAll('My LLM Wiki', name);
|
|
311
320
|
writeFileSync(join(wikiPath, 'config.yaml'), content, 'utf8');
|
|
312
321
|
console.log(` ✅ config.yaml created`);
|
|
313
322
|
}
|
|
314
323
|
console.log(` ✅ Wiki template deployed (${count} files)`);
|
|
315
|
-
console.log(` 💡
|
|
324
|
+
console.log(` 💡 Mở Agent trong ${name}-wiki/, chạy: /llm-wiki init "${name}"`);
|
|
316
325
|
} else {
|
|
317
326
|
console.log(` ⚠️ Wiki template not found — fallback to manual setup`);
|
|
318
327
|
ensureDir(wikiPath, cwd);
|
|
@@ -363,7 +372,7 @@ async function deployWorkspace({ name, tool, repoConfigs, db, wikiEnabled, cwd }
|
|
|
363
372
|
|
|
364
373
|
console.log(`
|
|
365
374
|
╔═══════════════════════════════════════════════════════╗
|
|
366
|
-
║ ✅ Workspace "${name}" v0.2.
|
|
375
|
+
║ ✅ Workspace "${name}" v0.2.4 initialized! ║
|
|
367
376
|
╠═══════════════════════════════════════════════════════╣
|
|
368
377
|
║ ║
|
|
369
378
|
║ Projects: ║
|
|
@@ -468,7 +477,7 @@ function updateGitignore(projectPath, projectName) {
|
|
|
468
477
|
}
|
|
469
478
|
|
|
470
479
|
// ════════════════════════════════════════════════════════════════════
|
|
471
|
-
// Deploy to individual project — v0.2.
|
|
480
|
+
// Deploy to individual project — v0.2.3 Multi-Tool Adapter Pattern
|
|
472
481
|
// ════════════════════════════════════════════════════════════════════
|
|
473
482
|
|
|
474
483
|
function deployToProject(projectPath, cwd, opts) {
|
|
@@ -678,7 +687,7 @@ function deployShared(projectPath, cwd, opts) {
|
|
|
678
687
|
writeFile(join(projectPath, 'Instruct-Agent-AI.md'), content, {cwd});
|
|
679
688
|
}
|
|
680
689
|
|
|
681
|
-
// ═══ GETTING-STARTED.md (v0.2.
|
|
690
|
+
// ═══ GETTING-STARTED.md (v0.2.3) ═══
|
|
682
691
|
const guideTemplate = join(TEMPLATES_DIR, 'GETTING-STARTED.md');
|
|
683
692
|
if (existsSync(guideTemplate)) {
|
|
684
693
|
let content = readFileSync(guideTemplate, 'utf8');
|
|
@@ -699,7 +708,7 @@ function genConfig(name, repoConfigs, db, tool, wiki) {
|
|
|
699
708
|
` - { folder: "${r.folder}", type: "${r.type}", tech: "${r.tech}" }`
|
|
700
709
|
).join('\n');
|
|
701
710
|
|
|
702
|
-
return `# EVNICT-KIT v0.2.
|
|
711
|
+
return `# EVNICT-KIT v0.2.4 Config
|
|
703
712
|
project:
|
|
704
713
|
name: "${name}"
|
|
705
714
|
repos:
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, unlinkSync } from 'node:fs';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import readline from 'node:readline';
|
|
5
|
+
import { loadConfig, getToolMap, TECH_LABELS, SUPPORTED_TOOLS } from '../utils/config.js';
|
|
6
|
+
import { ensureDir, writeFile, TEMPLATES_DIR } from '../utils/file.js';
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = dirname(__filename);
|
|
10
|
+
|
|
11
|
+
// ── Colors ──
|
|
12
|
+
const c = {
|
|
13
|
+
reset: '\x1b[0m',
|
|
14
|
+
bold: '\x1b[1m',
|
|
15
|
+
dim: '\x1b[2m',
|
|
16
|
+
green: '\x1b[32m',
|
|
17
|
+
yellow: '\x1b[33m',
|
|
18
|
+
red: '\x1b[31m',
|
|
19
|
+
cyan: '\x1b[36m',
|
|
20
|
+
magenta: '\x1b[35m',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// ════════════════════════════════════════════════════════════════════
|
|
24
|
+
// Detect evnict-kit managed files vs user files
|
|
25
|
+
// evnict-kit files always start with "evnict-kit-" prefix
|
|
26
|
+
// or numbered prefix like "01-evnict-kit-", "02-evnict-kit-"
|
|
27
|
+
// ════════════════════════════════════════════════════════════════════
|
|
28
|
+
|
|
29
|
+
function isEvnictKitFile(filename) {
|
|
30
|
+
return filename.startsWith('evnict-kit-') ||
|
|
31
|
+
/^\d{2}-evnict-kit-/.test(filename);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function isEvnictKitSkillDir(dirname) {
|
|
35
|
+
return dirname.startsWith('evnict-kit-');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ════════════════════════════════════════════════════════════════════
|
|
39
|
+
// Sync templates into a project — overwrite evnict-kit files only
|
|
40
|
+
// ════════════════════════════════════════════════════════════════════
|
|
41
|
+
|
|
42
|
+
function syncRules(projectPath, cwd, tool, toolMap) {
|
|
43
|
+
const rulesSrc = join(TEMPLATES_DIR, 'rules', tool);
|
|
44
|
+
const rulesDest = join(projectPath, toolMap.rulesDir);
|
|
45
|
+
if (!existsSync(rulesSrc) || !toolMap.rulesDir) return { updated: 0, skipped: 0, userFiles: [] };
|
|
46
|
+
|
|
47
|
+
ensureDir(rulesDest, cwd);
|
|
48
|
+
const srcFiles = readdirSync(rulesSrc).filter(f => f.endsWith('.md') || f.endsWith('.mdc'));
|
|
49
|
+
let updated = 0, skipped = 0;
|
|
50
|
+
const userFiles = [];
|
|
51
|
+
|
|
52
|
+
// Detect user files in dest
|
|
53
|
+
if (existsSync(rulesDest)) {
|
|
54
|
+
const destFiles = readdirSync(rulesDest).filter(f => !f.startsWith('.'));
|
|
55
|
+
for (const f of destFiles) {
|
|
56
|
+
if (!isEvnictKitFile(f) && !srcFiles.includes(f)) {
|
|
57
|
+
userFiles.push(f);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Overwrite evnict-kit rules
|
|
63
|
+
for (const file of srcFiles) {
|
|
64
|
+
const content = readFileSync(join(rulesSrc, file), 'utf8');
|
|
65
|
+
const destPath = join(rulesDest, file);
|
|
66
|
+
const existed = existsSync(destPath);
|
|
67
|
+
writeFile(destPath, content, { overwrite: true, cwd, silent: true });
|
|
68
|
+
if (existed) {
|
|
69
|
+
updated++;
|
|
70
|
+
console.log(` 🔄 ${file}`);
|
|
71
|
+
} else {
|
|
72
|
+
updated++;
|
|
73
|
+
console.log(` ✨ ${file} (new)`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return { updated, skipped, userFiles };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function syncWorkflows(projectPath, cwd, tool, toolMap) {
|
|
81
|
+
const wfSrc = join(TEMPLATES_DIR, 'workflows', tool);
|
|
82
|
+
const wfDest = join(projectPath, toolMap.workflowsDir);
|
|
83
|
+
if (!existsSync(wfSrc) || !toolMap.workflowsDir) return { updated: 0, skipped: 0, userFiles: [] };
|
|
84
|
+
|
|
85
|
+
ensureDir(wfDest, cwd);
|
|
86
|
+
const srcFiles = readdirSync(wfSrc).filter(f => f.endsWith('.md'));
|
|
87
|
+
let updated = 0;
|
|
88
|
+
const userFiles = [];
|
|
89
|
+
|
|
90
|
+
// Detect user files in dest
|
|
91
|
+
if (existsSync(wfDest)) {
|
|
92
|
+
const destFiles = readdirSync(wfDest).filter(f => f.endsWith('.md'));
|
|
93
|
+
for (const f of destFiles) {
|
|
94
|
+
if (!isEvnictKitFile(f) && !srcFiles.includes(f) && f !== 'README.md') {
|
|
95
|
+
userFiles.push(f);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
for (const file of srcFiles) {
|
|
101
|
+
const content = readFileSync(join(wfSrc, file), 'utf8');
|
|
102
|
+
const destPath = join(wfDest, file);
|
|
103
|
+
const existed = existsSync(destPath);
|
|
104
|
+
writeFile(destPath, content, { overwrite: true, cwd, silent: true });
|
|
105
|
+
if (existed) {
|
|
106
|
+
updated++;
|
|
107
|
+
console.log(` 🔄 ${file}`);
|
|
108
|
+
} else {
|
|
109
|
+
updated++;
|
|
110
|
+
console.log(` ✨ ${file} (new)`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return { updated, userFiles };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function syncSkills(projectPath, cwd, toolMap) {
|
|
118
|
+
const skillsSrc = join(TEMPLATES_DIR, 'skills');
|
|
119
|
+
if (!existsSync(skillsSrc) || !toolMap.skillsDir) return { updated: 0, newSkills: 0, userSkills: [] };
|
|
120
|
+
|
|
121
|
+
const srcDirs = readdirSync(skillsSrc, { withFileTypes: true })
|
|
122
|
+
.filter(d => d.isDirectory());
|
|
123
|
+
let updated = 0, newSkills = 0;
|
|
124
|
+
const userSkills = [];
|
|
125
|
+
|
|
126
|
+
// Detect user skills in dest
|
|
127
|
+
const skillsDest = join(projectPath, toolMap.skillsDir);
|
|
128
|
+
if (existsSync(skillsDest)) {
|
|
129
|
+
const destDirs = readdirSync(skillsDest, { withFileTypes: true })
|
|
130
|
+
.filter(d => d.isDirectory())
|
|
131
|
+
.map(d => d.name);
|
|
132
|
+
const srcNames = srcDirs.map(d => d.name);
|
|
133
|
+
for (const d of destDirs) {
|
|
134
|
+
if (!srcNames.includes(d) && !isEvnictKitSkillDir(d)) {
|
|
135
|
+
userSkills.push(d);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
for (const d of srcDirs) {
|
|
141
|
+
const dest = join(projectPath, toolMap.skillsDir, d.name);
|
|
142
|
+
const existed = existsSync(dest);
|
|
143
|
+
ensureDir(dest, cwd);
|
|
144
|
+
|
|
145
|
+
// Overwrite all files in skill dir
|
|
146
|
+
const skillFiles = readdirSync(join(skillsSrc, d.name), { withFileTypes: true });
|
|
147
|
+
for (const entry of skillFiles) {
|
|
148
|
+
if (entry.isFile()) {
|
|
149
|
+
const content = readFileSync(join(skillsSrc, d.name, entry.name), 'utf8');
|
|
150
|
+
writeFile(join(dest, entry.name), content, { overwrite: true, cwd, silent: true });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (existed) {
|
|
155
|
+
updated++;
|
|
156
|
+
console.log(` 🔄 ${d.name}/`);
|
|
157
|
+
} else {
|
|
158
|
+
newSkills++;
|
|
159
|
+
console.log(` ✨ ${d.name}/ (new)`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return { updated, newSkills, userSkills };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function syncSharedFiles(projectPath, cwd, toolMap) {
|
|
167
|
+
let updated = 0;
|
|
168
|
+
|
|
169
|
+
// GETTING-STARTED.md
|
|
170
|
+
const guideTemplate = join(TEMPLATES_DIR, 'GETTING-STARTED.md');
|
|
171
|
+
if (existsSync(guideTemplate)) {
|
|
172
|
+
const guideDest = join(projectPath, 'GETTING-STARTED.md');
|
|
173
|
+
if (existsSync(guideDest)) {
|
|
174
|
+
const content = readFileSync(guideTemplate, 'utf8');
|
|
175
|
+
// Preserve {{}} placeholders — user may have customized
|
|
176
|
+
writeFile(guideDest, content, { overwrite: true, cwd, silent: true });
|
|
177
|
+
console.log(` 🔄 GETTING-STARTED.md`);
|
|
178
|
+
updated++;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return { updated };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ════════════════════════════════════════════════════════════════════
|
|
186
|
+
// Main sync command
|
|
187
|
+
// ════════════════════════════════════════════════════════════════════
|
|
188
|
+
|
|
189
|
+
export async function syncCommand(options) {
|
|
190
|
+
const cwd = process.cwd();
|
|
191
|
+
|
|
192
|
+
// Load config
|
|
193
|
+
const config = loadConfig(cwd);
|
|
194
|
+
if (!config) {
|
|
195
|
+
console.error('❌ Không tìm thấy .evnict/config.yaml. Chạy evnict-kit init trước.');
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const name = config.project?.name;
|
|
200
|
+
const tool = config.ai_tool || 'antigravity';
|
|
201
|
+
const repos = config.repos || [];
|
|
202
|
+
|
|
203
|
+
if (!SUPPORTED_TOOLS.includes(tool)) {
|
|
204
|
+
console.error(`❌ Tool "${tool}" chưa hỗ trợ.`);
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const toolMap = getToolMap(tool);
|
|
209
|
+
const pkgPath = join(__dirname, '..', '..', 'package.json');
|
|
210
|
+
const version = JSON.parse(readFileSync(pkgPath, 'utf8')).version;
|
|
211
|
+
|
|
212
|
+
console.log(`
|
|
213
|
+
${c.bold}${c.cyan}╔═══════════════════════════════════════════════════════╗
|
|
214
|
+
║ 🔄 EVNICT-KIT v${version}: Sync Templates ║
|
|
215
|
+
╚═══════════════════════════════════════════════════════╝${c.reset}
|
|
216
|
+
|
|
217
|
+
Project: ${c.bold}${name}${c.reset}
|
|
218
|
+
Tool: ${tool} → ${toolMap.agentDir}/
|
|
219
|
+
Repos: ${repos.map(r => r.folder).join(', ')}
|
|
220
|
+
`);
|
|
221
|
+
|
|
222
|
+
// Confirm unless --yes
|
|
223
|
+
if (!options.yes) {
|
|
224
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
225
|
+
const answer = await new Promise(r => rl.question(
|
|
226
|
+
`${c.yellow}⚠️ Sync sẽ GHI ĐÈ tất cả files evnict-kit (workflows, skills, rules).${c.reset}
|
|
227
|
+
Files do user tự thêm sẽ KHÔNG bị xóa.
|
|
228
|
+
Tiếp tục? (y/N): `, r
|
|
229
|
+
));
|
|
230
|
+
rl.close();
|
|
231
|
+
|
|
232
|
+
if (answer.trim().toLowerCase() !== 'y' && answer.trim().toLowerCase() !== 'yes') {
|
|
233
|
+
console.log('\n ⏭️ Đã hủy sync.');
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
console.log('');
|
|
239
|
+
|
|
240
|
+
// ── Sync each project ──
|
|
241
|
+
const summary = { totalUpdated: 0, totalNew: 0, userFiles: [] };
|
|
242
|
+
|
|
243
|
+
for (const repo of repos) {
|
|
244
|
+
const projectPath = join(cwd, repo.folder);
|
|
245
|
+
if (!existsSync(projectPath)) {
|
|
246
|
+
console.log(` ⚠️ ${repo.folder}/ không tồn tại — skip`);
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
console.log(`${c.bold}── ${repo.type.toUpperCase()}: ${repo.folder} ──${c.reset}`);
|
|
251
|
+
|
|
252
|
+
// Rules
|
|
253
|
+
if (toolMap.rulesDir) {
|
|
254
|
+
console.log(` 📏 Rules → ${toolMap.rulesDir}/`);
|
|
255
|
+
const r = syncRules(projectPath, cwd, tool, toolMap);
|
|
256
|
+
summary.totalUpdated += r.updated;
|
|
257
|
+
if (r.userFiles.length > 0) {
|
|
258
|
+
summary.userFiles.push(...r.userFiles.map(f => `${repo.folder}/${toolMap.rulesDir}/${f}`));
|
|
259
|
+
console.log(` ${c.green}✅ ${r.userFiles.length} user files preserved${c.reset}`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Workflows
|
|
264
|
+
if (toolMap.workflowsDir) {
|
|
265
|
+
console.log(` 📋 Workflows → ${toolMap.workflowsDir}/`);
|
|
266
|
+
const w = syncWorkflows(projectPath, cwd, tool, toolMap);
|
|
267
|
+
summary.totalUpdated += w.updated;
|
|
268
|
+
if (w.userFiles.length > 0) {
|
|
269
|
+
summary.userFiles.push(...w.userFiles.map(f => `${repo.folder}/${toolMap.workflowsDir}/${f}`));
|
|
270
|
+
console.log(` ${c.green}✅ ${w.userFiles.length} user files preserved${c.reset}`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Skills
|
|
275
|
+
if (toolMap.skillsDir) {
|
|
276
|
+
console.log(` 🎯 Skills → ${toolMap.skillsDir}/`);
|
|
277
|
+
const s = syncSkills(projectPath, cwd, toolMap);
|
|
278
|
+
summary.totalUpdated += s.updated;
|
|
279
|
+
summary.totalNew += s.newSkills;
|
|
280
|
+
if (s.userSkills.length > 0) {
|
|
281
|
+
summary.userFiles.push(...s.userSkills.map(d => `${repo.folder}/${toolMap.skillsDir}/${d}/`));
|
|
282
|
+
console.log(` ${c.green}✅ ${s.userSkills.length} user skills preserved${c.reset}`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Shared files (GETTING-STARTED.md)
|
|
287
|
+
console.log(` 📖 Shared files`);
|
|
288
|
+
const sh = syncSharedFiles(projectPath, cwd, toolMap);
|
|
289
|
+
summary.totalUpdated += sh.updated;
|
|
290
|
+
|
|
291
|
+
console.log('');
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// ── Summary ──
|
|
295
|
+
console.log(`${c.bold}${c.cyan}╔═══════════════════════════════════════════════════════╗
|
|
296
|
+
║ ✅ Sync complete! ║
|
|
297
|
+
╠═══════════════════════════════════════════════════════╣${c.reset}
|
|
298
|
+
║
|
|
299
|
+
║ 📊 ${c.bold}${summary.totalUpdated} files updated${c.reset}, ${c.bold}${summary.totalNew} new files${c.reset}
|
|
300
|
+
║ ${c.green}🔒 ${summary.userFiles.length} user files preserved (không bị xóa)${c.reset}`);
|
|
301
|
+
|
|
302
|
+
if (summary.userFiles.length > 0) {
|
|
303
|
+
console.log(`║`);
|
|
304
|
+
console.log(`║ ${c.dim}User files kept:${c.reset}`);
|
|
305
|
+
for (const f of summary.userFiles) {
|
|
306
|
+
console.log(`║ ${c.dim}· ${f}${c.reset}`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
console.log(`║
|
|
311
|
+
║ ${c.dim}Lưu ý: Context file (AGENTS.md/CLAUDE.md/.cursorrules)${c.reset}
|
|
312
|
+
║ ${c.dim}và rules RP05 (project conventions) KHÔNG bị ghi đè —${c.reset}
|
|
313
|
+
║ ${c.dim}vì chứa nội dung riêng của từng dự án.${c.reset}
|
|
314
|
+
║
|
|
315
|
+
${c.bold}${c.cyan}╚═══════════════════════════════════════════════════════╝${c.reset}
|
|
316
|
+
`);
|
|
317
|
+
}
|
package/src/commands/upgrade.js
CHANGED
|
@@ -114,7 +114,13 @@ function doUpgrade(targetVersion) {
|
|
|
114
114
|
});
|
|
115
115
|
console.log('');
|
|
116
116
|
console.log(` ${c.green}+${c.reset} ${c.bold}Upgraded successfully -> v${targetVersion}${c.reset}`);
|
|
117
|
-
console.log(
|
|
117
|
+
console.log('');
|
|
118
|
+
console.log(` ${c.yellow}!${c.reset} ${c.bold}Quan trọng:${c.reset} Chạy lệnh sau để cập nhật templates vào các project:`);
|
|
119
|
+
console.log(` ${c.cyan} cd <workspace-folder>${c.reset}`);
|
|
120
|
+
console.log(` ${c.cyan} evnict-kit sync${c.reset}`);
|
|
121
|
+
console.log('');
|
|
122
|
+
console.log(` ${c.dim}Lệnh sync sẽ ghi đè workflows/skills/rules mới,${c.reset}`);
|
|
123
|
+
console.log(` ${c.dim}nhưng giữ nguyên files bạn tự thêm.${c.reset}`);
|
|
118
124
|
console.log('');
|
|
119
125
|
} catch (e) {
|
|
120
126
|
console.log('');
|