burhan-mop 0.1.1 → 0.1.2
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/.MOP/PROTOCOL.md +4 -1
- package/.MOP/scripts/burhan-mop.mjs +180 -47
- package/README.bm.md +152 -0
- package/README.md +65 -1057
- package/package.json +2 -1
package/.MOP/PROTOCOL.md
CHANGED
|
@@ -287,9 +287,12 @@ apply.
|
|
|
287
287
|
The package installer command is:
|
|
288
288
|
|
|
289
289
|
```bash
|
|
290
|
-
npx burhan-mop install
|
|
290
|
+
npx burhan-mop install [--target <project-folder>] [--force] [--json]
|
|
291
291
|
```
|
|
292
292
|
|
|
293
|
+
The default output is a clean terminal UI. Use `--json` for CI, scripts, and
|
|
294
|
+
other automation that needs machine-readable output.
|
|
295
|
+
|
|
293
296
|
The short package command works after the package is published to npm. Before
|
|
294
297
|
an npm publish, install directly from GitHub:
|
|
295
298
|
|
|
@@ -5,6 +5,39 @@ import { fileURLToPath } from 'node:url';
|
|
|
5
5
|
|
|
6
6
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
7
7
|
const packageRoot = resolve(here, '..', '..');
|
|
8
|
+
const colorEnabled = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
9
|
+
const colors = {
|
|
10
|
+
reset: '\x1b[0m',
|
|
11
|
+
bold: '\x1b[1m',
|
|
12
|
+
dim: '\x1b[2m',
|
|
13
|
+
green: '\x1b[32m',
|
|
14
|
+
yellow: '\x1b[33m',
|
|
15
|
+
red: '\x1b[31m',
|
|
16
|
+
cyan: '\x1b[36m',
|
|
17
|
+
gray: '\x1b[90m'
|
|
18
|
+
};
|
|
19
|
+
const installEntries = [
|
|
20
|
+
'AGENTS.md',
|
|
21
|
+
'CLAUDE.md',
|
|
22
|
+
'GEMINI.md',
|
|
23
|
+
'.MOP',
|
|
24
|
+
'.agents',
|
|
25
|
+
'.claude',
|
|
26
|
+
'.claude-flow',
|
|
27
|
+
'.codex',
|
|
28
|
+
'.gemini',
|
|
29
|
+
'.mcp.json'
|
|
30
|
+
];
|
|
31
|
+
const doctorEntries = [
|
|
32
|
+
'AGENTS.md',
|
|
33
|
+
'CLAUDE.md',
|
|
34
|
+
'GEMINI.md',
|
|
35
|
+
'.MOP/STATE.json',
|
|
36
|
+
'.MOP/PROTOCOL.md',
|
|
37
|
+
'.MOP/scripts/mop-core.mjs',
|
|
38
|
+
'.MOP/scripts/mop-workflow.mjs',
|
|
39
|
+
'.agents/skills/mop-help/SKILL.md'
|
|
40
|
+
];
|
|
8
41
|
|
|
9
42
|
function parseArgs(argv) {
|
|
10
43
|
const out = { _: [] };
|
|
@@ -14,7 +47,11 @@ function parseArgs(argv) {
|
|
|
14
47
|
out._.push(item);
|
|
15
48
|
continue;
|
|
16
49
|
}
|
|
17
|
-
const key = item.slice(2);
|
|
50
|
+
const [key, inlineValue] = item.slice(2).split('=', 2);
|
|
51
|
+
if (inlineValue !== undefined) {
|
|
52
|
+
out[key] = inlineValue;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
18
55
|
const next = argv[i + 1];
|
|
19
56
|
if (!next || next.startsWith('--')) {
|
|
20
57
|
out[key] = true;
|
|
@@ -26,82 +63,178 @@ function parseArgs(argv) {
|
|
|
26
63
|
return out;
|
|
27
64
|
}
|
|
28
65
|
|
|
29
|
-
function
|
|
30
|
-
if (!
|
|
31
|
-
|
|
66
|
+
function paint(name, value) {
|
|
67
|
+
if (!colorEnabled) return value;
|
|
68
|
+
return `${colors[name]}${value}${colors.reset}`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function rule() {
|
|
72
|
+
console.log(paint('gray', '-'.repeat(66)));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function header(title, subtitle) {
|
|
76
|
+
console.log('');
|
|
77
|
+
rule();
|
|
78
|
+
console.log(paint('bold', title));
|
|
79
|
+
console.log(paint('cyan', subtitle));
|
|
80
|
+
rule();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function asJson(args) {
|
|
84
|
+
return args.json === true || args.format === 'json';
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function statusBadge(status) {
|
|
88
|
+
if (status === 'installed') return paint('green', '[OK]');
|
|
89
|
+
if (status === 'skipped-existing') return paint('yellow', '[SKIP]');
|
|
90
|
+
if (status === 'missing-source') return paint('red', '[MISS]');
|
|
91
|
+
return paint('gray', '[INFO]');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function statusLabel(status) {
|
|
95
|
+
if (status === 'installed') return 'installed';
|
|
96
|
+
if (status === 'skipped-existing') return 'already exists';
|
|
97
|
+
if (status === 'missing-source') return 'template missing';
|
|
98
|
+
return status;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function summarize(results) {
|
|
102
|
+
return results.reduce((summary, item) => {
|
|
103
|
+
if (item.status === 'installed') summary.installed += 1;
|
|
104
|
+
else if (item.status === 'skipped-existing') summary.skipped += 1;
|
|
105
|
+
else if (item.status === 'missing-source') summary.missing += 1;
|
|
106
|
+
else summary.other += 1;
|
|
107
|
+
return summary;
|
|
108
|
+
}, { installed: 0, skipped: 0, missing: 0, other: 0 });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function copyPath(entry, source, target, force = false) {
|
|
112
|
+
if (!existsSync(source)) return { entry, source, target, status: 'missing-source' };
|
|
113
|
+
if (existsSync(target) && !force) return { entry, source, target, status: 'skipped-existing' };
|
|
32
114
|
mkdirSync(dirname(target), { recursive: true });
|
|
33
115
|
cpSync(source, target, { recursive: true, force: true });
|
|
34
|
-
return { source, target, status: 'installed' };
|
|
116
|
+
return { entry, source, target, status: 'installed' };
|
|
35
117
|
}
|
|
36
118
|
|
|
37
|
-
function
|
|
119
|
+
function buildInstallReport(args) {
|
|
38
120
|
const targetRoot = resolve(String(args.target || process.cwd()));
|
|
39
121
|
const force = args.force === true;
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
'CLAUDE.md',
|
|
43
|
-
'GEMINI.md',
|
|
44
|
-
'.MOP',
|
|
45
|
-
'.agents',
|
|
46
|
-
'.claude',
|
|
47
|
-
'.claude-flow',
|
|
48
|
-
'.codex',
|
|
49
|
-
'.gemini',
|
|
50
|
-
'.mcp.json'
|
|
51
|
-
];
|
|
52
|
-
const results = entries.map((entry) => copyPath(
|
|
122
|
+
const results = installEntries.map((entry) => copyPath(
|
|
123
|
+
entry,
|
|
53
124
|
join(packageRoot, entry),
|
|
54
125
|
join(targetRoot, entry),
|
|
55
126
|
force
|
|
56
127
|
));
|
|
57
|
-
|
|
58
|
-
|
|
128
|
+
const summary = summarize(results);
|
|
129
|
+
return {
|
|
130
|
+
ok: summary.missing === 0,
|
|
59
131
|
command: 'npx burhan-mop install',
|
|
60
132
|
target: targetRoot,
|
|
61
133
|
force,
|
|
62
134
|
results,
|
|
135
|
+
summary,
|
|
63
136
|
next: [
|
|
64
137
|
'Run /mop-setup in the target project.',
|
|
65
138
|
'For team mode, initialize autosycn after setup.',
|
|
66
139
|
'Use: node .MOP/scripts/mop-workflow.mjs help --actor <codename> --task "lepas ni buat apa?"'
|
|
67
140
|
]
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function renderInstall(report) {
|
|
145
|
+
header('BURHAN-MOP installer', 'Portable AI MemoryCore for Claude, Codex / ChatGPT, Gemini, and Antigravity');
|
|
146
|
+
console.log(`${paint('bold', 'Target')} : ${report.target}`);
|
|
147
|
+
console.log(`${paint('bold', 'Mode')} : ${report.force ? 'force overwrite' : 'safe install'}`);
|
|
148
|
+
rule();
|
|
149
|
+
for (const item of report.results) {
|
|
150
|
+
console.log(`${statusBadge(item.status)} ${item.entry.padEnd(28)} ${statusLabel(item.status)}`);
|
|
151
|
+
}
|
|
152
|
+
rule();
|
|
153
|
+
console.log(`${paint('bold', 'Summary')}: ${report.summary.installed} installed, ${report.summary.skipped} skipped, ${report.summary.missing} missing`);
|
|
154
|
+
if (report.summary.skipped > 0) {
|
|
155
|
+
console.log(paint('yellow', 'Tip: existing files were kept. Use --force only when you want to overwrite them.'));
|
|
156
|
+
}
|
|
157
|
+
if (report.summary.missing > 0) {
|
|
158
|
+
console.log(paint('red', 'Some package templates are missing. Reinstall or report this package build.'));
|
|
159
|
+
}
|
|
160
|
+
console.log('');
|
|
161
|
+
console.log(paint('bold', 'Next'));
|
|
162
|
+
report.next.forEach((item, index) => {
|
|
163
|
+
console.log(` ${index + 1}. ${item}`);
|
|
164
|
+
});
|
|
165
|
+
console.log('');
|
|
166
|
+
console.log(paint('dim', 'Automation JSON: npx burhan-mop install --json'));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function install(args) {
|
|
170
|
+
const report = buildInstallReport(args);
|
|
171
|
+
if (asJson(args)) {
|
|
172
|
+
console.log(JSON.stringify(report, null, 2));
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
renderInstall(report);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function buildDoctorReport() {
|
|
179
|
+
const results = doctorEntries.map((entry) => {
|
|
83
180
|
const path = join(process.cwd(), entry);
|
|
181
|
+
const exists = existsSync(path);
|
|
84
182
|
return {
|
|
85
183
|
entry,
|
|
86
|
-
exists
|
|
87
|
-
type:
|
|
184
|
+
exists,
|
|
185
|
+
type: exists ? (statSync(path).isDirectory() ? 'dir' : 'file') : 'missing'
|
|
88
186
|
};
|
|
89
187
|
});
|
|
90
|
-
|
|
188
|
+
return {
|
|
91
189
|
ok: results.every((item) => item.exists),
|
|
92
190
|
cwd: process.cwd(),
|
|
93
191
|
results
|
|
94
|
-
}
|
|
192
|
+
};
|
|
95
193
|
}
|
|
96
194
|
|
|
97
|
-
function
|
|
98
|
-
|
|
195
|
+
function renderDoctor(report) {
|
|
196
|
+
header('BURHAN-MOP doctor', 'Workspace health check');
|
|
197
|
+
console.log(`${paint('bold', 'Project')}: ${report.cwd}`);
|
|
198
|
+
rule();
|
|
199
|
+
for (const item of report.results) {
|
|
200
|
+
const badge = item.exists ? paint('green', '[OK]') : paint('red', '[MISS]');
|
|
201
|
+
console.log(`${badge} ${item.entry.padEnd(38)} ${item.type}`);
|
|
202
|
+
}
|
|
203
|
+
rule();
|
|
204
|
+
console.log(`${paint('bold', 'Status')}: ${report.ok ? paint('green', 'ready') : paint('red', 'missing files')}`);
|
|
205
|
+
console.log('');
|
|
206
|
+
console.log(paint('dim', 'Automation JSON: npx burhan-mop doctor --json'));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function doctor(args) {
|
|
210
|
+
const report = buildDoctorReport();
|
|
211
|
+
if (asJson(args)) {
|
|
212
|
+
console.log(JSON.stringify(report, null, 2));
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
renderDoctor(report);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function listPackage(args) {
|
|
219
|
+
const report = {
|
|
99
220
|
packageRoot,
|
|
100
221
|
entries: readdirSync(packageRoot, { withFileTypes: true }).map((item) => ({
|
|
101
222
|
name: item.name,
|
|
102
223
|
type: item.isDirectory() ? 'dir' : 'file'
|
|
103
224
|
}))
|
|
104
|
-
}
|
|
225
|
+
};
|
|
226
|
+
if (asJson(args)) {
|
|
227
|
+
console.log(JSON.stringify(report, null, 2));
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
header('BURHAN-MOP package', 'Published package contents');
|
|
231
|
+
console.log(`${paint('bold', 'Root')}: ${report.packageRoot}`);
|
|
232
|
+
rule();
|
|
233
|
+
for (const item of report.entries) {
|
|
234
|
+
console.log(`${item.type.padEnd(4)} ${item.name}`);
|
|
235
|
+
}
|
|
236
|
+
console.log('');
|
|
237
|
+
console.log(paint('dim', 'Automation JSON: npx burhan-mop package --json'));
|
|
105
238
|
}
|
|
106
239
|
|
|
107
240
|
function main() {
|
|
@@ -109,11 +242,11 @@ function main() {
|
|
|
109
242
|
const args = parseArgs(rest);
|
|
110
243
|
if (command === 'install') return install(args);
|
|
111
244
|
if (command === 'doctor') return doctor(args);
|
|
112
|
-
if (command === 'package') return listPackage();
|
|
245
|
+
if (command === 'package') return listPackage(args);
|
|
113
246
|
console.log(`Usage:
|
|
114
|
-
npx burhan-mop install [--target PATH] [--force]
|
|
115
|
-
npx burhan-mop doctor
|
|
116
|
-
npx burhan-mop package`);
|
|
247
|
+
npx burhan-mop install [--target PATH] [--force] [--json]
|
|
248
|
+
npx burhan-mop doctor [--json]
|
|
249
|
+
npx burhan-mop package [--json]`);
|
|
117
250
|
}
|
|
118
251
|
|
|
119
252
|
try {
|
package/README.bm.md
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
<h1 align="center">BURHAN-MOP</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<strong>MOP portable AI MemoryCore untuk Claude, Codex / ChatGPT, Gemini, dan Antigravity.</strong>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://www.npmjs.com/package/burhan-mop">
|
|
9
|
+
<img src="https://img.shields.io/npm/v/burhan-mop?style=for-the-badge&label=NPM" alt="npm version">
|
|
10
|
+
</a>
|
|
11
|
+
<a href="https://github.com/BURHANDEV-ENTERPRISE/BURHAN-MOP">
|
|
12
|
+
<img src="https://img.shields.io/badge/GitHub-BURHAN--MOP-181717?style=for-the-badge&logo=github" alt="GitHub repository">
|
|
13
|
+
</a>
|
|
14
|
+
<img src="https://img.shields.io/badge/Node-%3E%3D20-1FAE4B?style=for-the-badge&logo=node.js&logoColor=white" alt="Node 20+">
|
|
15
|
+
<img src="https://img.shields.io/badge/License-UNLICENSED-9CA3AF?style=for-the-badge" alt="License">
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
<p align="center">
|
|
19
|
+
<img src="https://img.shields.io/badge/Claude-ready-5A45FF?style=flat-square" alt="Claude ready">
|
|
20
|
+
<img src="https://img.shields.io/badge/Codex-ready-111111?style=flat-square" alt="Codex ready">
|
|
21
|
+
<img src="https://img.shields.io/badge/Gemini-ready-4285F4?style=flat-square" alt="Gemini ready">
|
|
22
|
+
<img src="https://img.shields.io/badge/Antigravity-ready-00A67E?style=flat-square" alt="Antigravity ready">
|
|
23
|
+
<img src="https://img.shields.io/badge/MOP_Workflow-BMAD_inspired-FFB000?style=flat-square" alt="MOP workflow">
|
|
24
|
+
</p>
|
|
25
|
+
|
|
26
|
+
<p align="center">
|
|
27
|
+
<a href="./README.md">English</a>
|
|
28
|
+
|
|
|
29
|
+
<strong>Bahasa Melayu</strong>
|
|
30
|
+
</p>
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Apa Itu BURHAN-MOP?
|
|
35
|
+
|
|
36
|
+
BURHAN-MOP ialah **MOP (Memory of Planet) core** yang portable untuk workspace
|
|
37
|
+
coding AI. Ia bagi semua AI provider sumber kebenaran yang sama: memory project,
|
|
38
|
+
peraturan agent, workflow gate, folder artifact, autosycn, dan setup deploy.
|
|
39
|
+
|
|
40
|
+
Maksudnya mudah: pasang sekali, kemudian Claude, Codex / ChatGPT, Gemini, dan
|
|
41
|
+
Antigravity boleh masuk project yang sama dengan context yang sama.
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
Jalankan dalam root project:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npx burhan-mop install
|
|
49
|
+
npx burhan-mop doctor
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Install ke folder lain:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npx burhan-mop install --target "C:\path\to\project"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Paksa overwrite install sedia ada:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npx burhan-mop install --force
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Installer akan tunjuk terminal UI yang kemas secara default. Untuk automation,
|
|
65
|
+
guna JSON:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npx burhan-mop install --json
|
|
69
|
+
npx burhan-mop doctor --json
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Selepas install, buka AI coding chat dalam project itu dan jalankan:
|
|
73
|
+
|
|
74
|
+
```text
|
|
75
|
+
/mop-setup
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Apa Yang Dipasang
|
|
79
|
+
|
|
80
|
+
| Path | Fungsi |
|
|
81
|
+
| --- | --- |
|
|
82
|
+
| `.MOP/` | State, protocol, script, workflow config, dan template artifact MOP. |
|
|
83
|
+
| `AGENTS.md` | Arahan neutral untuk Codex / ChatGPT coding agents. |
|
|
84
|
+
| `CLAUDE.md` | Entry point dan rules untuk Claude Code. |
|
|
85
|
+
| `GEMINI.md` | Entry point untuk Gemini CLI. |
|
|
86
|
+
| `.agents/` | Agent dan skill yang sesuai untuk Antigravity. |
|
|
87
|
+
| `.codex/`, `.gemini/`, `.claude/` | Config dan skill surface ikut provider. |
|
|
88
|
+
|
|
89
|
+
## Fungsi Utama
|
|
90
|
+
|
|
91
|
+
| Fungsi | Apa dia buat |
|
|
92
|
+
| --- | --- |
|
|
93
|
+
| Auth Gate | First action mesti setup/login. AI tidak terus bekerja sebelum gate lulus. |
|
|
94
|
+
| Agent Router | Pilih satu primary agent dan tambah support agent bila perlu. |
|
|
95
|
+
| Party Mode | Tunjuk perbincangan agent-to-agent untuk keputusan multi-role. |
|
|
96
|
+
| MOP Workflow | Flow inspirasi BMAD dari idea sampai release dengan readiness gate. |
|
|
97
|
+
| Artifacts | Simpan plan, spec, review, dan release notes dalam `.MOP/artifacts/`. |
|
|
98
|
+
| Autosycn | Commit dan push guna identiti user sebenar, bukan identiti AI tool. |
|
|
99
|
+
| Auto Deploy | Setup optional untuk GitHub, Docker, dan Vercel. |
|
|
100
|
+
|
|
101
|
+
## Flow Sesi Pertama
|
|
102
|
+
|
|
103
|
+
```text
|
|
104
|
+
1. AI baca .MOP/STATE.json
|
|
105
|
+
2. Jika belum setup, AI suruh jalankan /mop-setup
|
|
106
|
+
3. Setup tanya nama project, owner, codename, password, mode, bahasa, dan GitHub
|
|
107
|
+
4. Selepas login, setiap task lalu Agent Router
|
|
108
|
+
5. Task kompleks guna MOP Workflow dan readiness gate sebelum coding
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Command Berguna
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
npx burhan-mop install
|
|
115
|
+
npx burhan-mop doctor
|
|
116
|
+
npx burhan-mop package
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Helper dalam project:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
node .MOP/scripts/mop-core.mjs status
|
|
123
|
+
node .MOP/scripts/mop-core.mjs validate
|
|
124
|
+
node .MOP/scripts/mop-workflow.mjs help --actor <codename> --task "<task>"
|
|
125
|
+
node .MOP/scripts/mop-autosycn.mjs run --actor <codename> --reason "<apa berubah>"
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Workflow Team
|
|
129
|
+
|
|
130
|
+
Dalam team mode, kerja akan push ke branch user dulu:
|
|
131
|
+
|
|
132
|
+
```text
|
|
133
|
+
dev/<codename> -> BURHAN-MOP review -> main
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
BURHAN-MOP bertindak sebagai merge guardian. Ia semak branch, validate state,
|
|
137
|
+
dan merge hanya bila workflow selamat.
|
|
138
|
+
|
|
139
|
+
## Release
|
|
140
|
+
|
|
141
|
+
| Item | Nilai |
|
|
142
|
+
| --- | --- |
|
|
143
|
+
| npm package | [`burhan-mop`](https://www.npmjs.com/package/burhan-mop) |
|
|
144
|
+
| command latest | `npx burhan-mop install` |
|
|
145
|
+
| GitHub release | [`v0.1.2`](https://github.com/BURHANDEV-ENTERPRISE/BURHAN-MOP/releases/tag/v0.1.2) |
|
|
146
|
+
| Node | `>=20` |
|
|
147
|
+
|
|
148
|
+
## Links
|
|
149
|
+
|
|
150
|
+
- npm: https://www.npmjs.com/package/burhan-mop
|
|
151
|
+
- GitHub: https://github.com/BURHANDEV-ENTERPRISE/BURHAN-MOP
|
|
152
|
+
- English README: [README.md](./README.md)
|