burhan-mop 0.1.6 → 0.1.7

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 CHANGED
@@ -31,7 +31,8 @@ Use this order:
31
31
  7. Coding/adventure language.
32
32
  8. GitHub project link. Required for `team`, optional for `solo`.
33
33
  9. GitHub username.
34
- 10. Git commit email. Use a GitHub-verified email or GitHub noreply email.
34
+ 10. Git commit email. Default to `github-noreply` so MOP derives the active
35
+ GitHub account email as `ID+USERNAME@users.noreply.github.com`.
35
36
  11. If `team`, ask join mode: `open`, `owner-approved`, or `invite`.
36
37
  12. Ask auto deploy: `Nak aktifkan auto deploy sekarang? Pilih provider:
37
38
  GitHub, Docker, Vercel.` If the user says later/no, answer with the defer
@@ -40,7 +41,7 @@ Use this order:
40
41
  After confirmation, run:
41
42
 
42
43
  ```bash
43
- node .MOP/scripts/mop-core.mjs setup --project-name "<name>" --name "<display>" --codename <codename> --password "<password>" --mode <solo|team> --conversation-language "<lang>" --coding-language "<lang>" --git-email "<github-verified-email>" [--git-name "<display>"] [--github-username "<github-login>"] [--github-url "<url>"] [--join-mode <mode>]
44
+ node .MOP/scripts/mop-core.mjs setup --project-name "<name>" --name "<display>" --codename <codename> --password "<password>" --mode <solo|team> --conversation-language "<lang>" --coding-language "<lang>" --git-email github-noreply [--git-name "<display>"] [--github-username "<github-login>"] [--github-url "<url>"] [--join-mode <mode>]
44
45
  ```
45
46
 
46
47
  ## Agent Naming Ceremony
@@ -431,10 +432,13 @@ In team mode, an agent name is the identity.
431
432
  Autosycn is always available and should be used after meaningful state or file
432
433
  changes. It is intentionally identity-safe.
433
434
 
434
- Before first push for a member, configure the real Git identity:
435
+ Before first push for a member, configure the real GitHub identity. Default to
436
+ GitHub noreply so commits attach to the active user account without exposing
437
+ private email:
435
438
 
436
439
  ```bash
437
- node .MOP/scripts/mop-core.mjs member git-identity --actor <codename> --name "<display name>" --email "<github-verified-email>" --github-username "<github-login>"
440
+ gh auth login
441
+ node .MOP/scripts/mop-core.mjs member git-identity --actor <codename> --name "<display name>" --email github-noreply --github-username "<github-login>"
438
442
  ```
439
443
 
440
444
  Then run:
@@ -455,6 +459,13 @@ Autosycn must:
455
459
  - Commit with `GIT_AUTHOR_NAME`, `GIT_AUTHOR_EMAIL`, `GIT_COMMITTER_NAME`, and
456
460
  `GIT_COMMITTER_EMAIL` set from member state.
457
461
  - Set local `git config user.name` and `user.email` before commit/merge.
462
+ - For member commits, derive `user.email` from the active GitHub CLI account as
463
+ `ID+USERNAME@users.noreply.github.com` when
464
+ `autosync.githubIdentity.useNoreplyForMemberCommits` is enabled.
465
+ - Refuse to commit or push if `gh api user` is authenticated as a different
466
+ GitHub username than the active member's configured `githubUsername`.
467
+ - Use the member GitHub identity for setup, preflight, memory, and work branch
468
+ commits. Use `BURHAN-MOP` only for merge guardian commits.
458
469
  - In team mode, `main` is the trunk and each user works on `dev/<codename>`.
459
470
  - Push to `dev/<codename>` in team mode and `main` in solo mode.
460
471
  - After a team push, BURHAN-MOP reviews `dev/<codename>` and merges it into
@@ -469,10 +480,11 @@ Autosycn must:
469
480
  - If `githubUsername` is configured, refuse to push unless `gh api user`
470
481
  verifies the same account.
471
482
 
472
- Important: GitHub commit attribution comes from commit email. GitHub push actor
473
- comes from the credential or SSH key used by `git push`; no script can fake that.
474
- If GitHub shows the AI/bot as pusher, fix `gh auth login`, Git Credential
475
- Manager, or the SSH key account.
483
+ Important: GitHub commit attribution comes from commit email. MOP uses the
484
+ active GitHub account noreply email for member commits by default. GitHub push
485
+ actor comes from the credential or SSH key used by `git push`; no script can
486
+ fake that. If GitHub shows the wrong pusher, fix `gh auth login`, Git
487
+ Credential Manager, or the SSH key account.
476
488
 
477
489
  ## Default Skill: auto-deploy
478
490
 
package/.MOP/STATE.json CHANGED
@@ -399,7 +399,18 @@
399
399
  }
400
400
  },
401
401
  "requireUserGitEmail": true,
402
- "verifyGhUserWhenConfigured": true
402
+ "verifyGhUserWhenConfigured": true,
403
+ "githubIdentity": {
404
+ "requireMatchedGhUser": true,
405
+ "useNoreplyForMemberCommits": true,
406
+ "noreplyFormat": "id-plus-login",
407
+ "rules": [
408
+ "Member commits must use the active member GitHub account, not an AI or guardian identity.",
409
+ "When gh is authenticated, derive member commit email as ID+USERNAME@users.noreply.github.com.",
410
+ "Refuse autosycn if the active gh account does not match the member githubUsername.",
411
+ "BURHAN-MOP identity is reserved for merge guardian commits only."
412
+ ]
413
+ }
403
414
  },
404
415
  "members": {},
405
416
  "installedSkills": [
@@ -247,22 +247,59 @@ function getMember(state, actor) {
247
247
  return member;
248
248
  }
249
249
 
250
+ function githubIdentityPolicy(state) {
251
+ return state.autosync?.githubIdentity || {
252
+ requireMatchedGhUser: true,
253
+ useNoreplyForMemberCommits: true,
254
+ noreplyFormat: 'id-plus-login'
255
+ };
256
+ }
257
+
258
+ function currentGhUser() {
259
+ const result = runOptional('gh', ['api', 'user', '--jq', '{login:.login,id:.id,email:.email}']);
260
+ if (!result.ok) return null;
261
+ try {
262
+ return JSON.parse(result.stdout || '{}');
263
+ } catch {
264
+ return null;
265
+ }
266
+ }
267
+
268
+ function githubNoreplyEmail(user) {
269
+ if (!user?.login || !user?.id) return '';
270
+ return `${user.id}+${user.login}@users.noreply.github.com`;
271
+ }
272
+
250
273
  function identityFor(state, actor) {
251
274
  const member = getMember(state, actor);
252
275
  const identity = member.gitIdentity || {};
276
+ const policy = githubIdentityPolicy(state);
277
+ const gh = currentGhUser();
253
278
  const name = identity.name || member.displayName || actor;
254
- const email = identity.email || member.github?.noreplyEmail || '';
279
+ const githubUsername = identity.githubUsername || member.github?.username || gh?.login || '';
280
+ if (gh?.login && githubUsername && policy.requireMatchedGhUser !== false && gh.login.toLowerCase() !== githubUsername.toLowerCase()) {
281
+ throw new Error(`GitHub CLI authenticated as ${gh.login}, expected ${githubUsername}. Refusing to commit or push as the wrong user.`);
282
+ }
283
+ let email = identity.email || member.github?.noreplyEmail || '';
284
+ if (policy.useNoreplyForMemberCommits !== false) {
285
+ email = githubNoreplyEmail(gh);
286
+ if (!email) {
287
+ throw new Error('GitHub noreply identity is required for member commits. Run gh auth login as the real user, or set autosync.githubIdentity.useNoreplyForMemberCommits=false.');
288
+ }
289
+ }
255
290
  if (!email && state.autosync?.requireUserGitEmail !== false) {
256
291
  throw new Error([
257
292
  `Missing git email for ${actor}.`,
258
- 'Set a GitHub-verified email or noreply email first:',
259
- `node .MOP/scripts/mop-core.mjs member git-identity --actor ${actor} --name "${name}" --email "<github-verified-email>" [--github-username "<username>"]`
293
+ 'Set a GitHub-verified email or let MOP derive GitHub noreply from gh:',
294
+ `node .MOP/scripts/mop-core.mjs member git-identity --actor ${actor} --name "${name}" --email github-noreply [--github-username "<username>"]`
260
295
  ].join(' '));
261
296
  }
262
297
  return {
263
298
  name,
264
299
  email,
265
- githubUsername: identity.githubUsername || member.github?.username || ''
300
+ githubUsername,
301
+ githubUserId: gh?.login?.toLowerCase() === githubUsername.toLowerCase() ? gh.id : identity.githubUserId,
302
+ emailSource: policy.useNoreplyForMemberCommits !== false ? 'github-noreply' : (identity.emailSource || 'manual')
266
303
  };
267
304
  }
268
305
 
@@ -335,15 +372,16 @@ function configureRemote(url, replaceRemote = false) {
335
372
  }
336
373
 
337
374
  function verifyGhUser(identity, state) {
338
- if (!identity.githubUsername || state.autosync?.verifyGhUserWhenConfigured === false) return 'skipped';
339
- const gh = runOptional('gh', ['api', 'user', '--jq', '.login']);
340
- if (!gh.ok) {
375
+ const policy = githubIdentityPolicy(state);
376
+ if (state.autosync?.verifyGhUserWhenConfigured === false && policy.requireMatchedGhUser === false) return 'skipped';
377
+ const gh = currentGhUser();
378
+ if (!gh?.login) {
341
379
  throw new Error('GitHub username is configured, but gh could not verify the active account. Run gh auth login as the real user or set autosync.verifyGhUserWhenConfigured=false for SSH-only workflows.');
342
380
  }
343
- if (gh.stdout.toLowerCase() !== identity.githubUsername.toLowerCase()) {
344
- throw new Error(`GitHub CLI authenticated as ${gh.stdout}, expected ${identity.githubUsername}. Refusing to push as the wrong account.`);
381
+ if (identity.githubUsername && gh.login.toLowerCase() !== identity.githubUsername.toLowerCase()) {
382
+ throw new Error(`GitHub CLI authenticated as ${gh.login}, expected ${identity.githubUsername}. Refusing to push as the wrong account.`);
345
383
  }
346
- return `verified:${gh.stdout}`;
384
+ return `verified:${gh.login}:${githubNoreplyEmail(gh) || 'email-unavailable'}`;
347
385
  }
348
386
 
349
387
  function runProjectCommand(command, env) {
@@ -677,6 +715,7 @@ function status() {
677
715
  workBranchPrefix: state.autosync?.workBranchPrefix || 'dev',
678
716
  autoMergeToMain: state.autosync?.autoMergeToMain !== false,
679
717
  mergeGuardian: guardianConfig(state),
718
+ githubIdentity: githubIdentityPolicy(state),
680
719
  preflightBeforeWork: state.autosync?.preflightBeforeWork !== false,
681
720
  requireUserGitEmail: state.autosync?.requireUserGitEmail !== false,
682
721
  initialized: state.initialized,
@@ -691,7 +730,8 @@ function status() {
691
730
  displayName: member.displayName,
692
731
  gitIdentityConfigured: Boolean(member.gitIdentity?.email || member.github?.noreplyEmail),
693
732
  gitName: member.gitIdentity?.name || member.displayName || key,
694
- gitEmail: member.gitIdentity?.email || member.github?.noreplyEmail || ''
733
+ gitEmail: member.gitIdentity?.email || member.github?.noreplyEmail || '',
734
+ githubUsername: member.gitIdentity?.githubUsername || member.github?.username || ''
695
735
  }
696
736
  ]))
697
737
  }, null, 2));
@@ -3,6 +3,7 @@ import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, writeFile
3
3
  import { dirname, join, resolve } from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import { randomBytes, scryptSync, timingSafeEqual } from 'node:crypto';
6
+ import { spawnSync } from 'node:child_process';
6
7
 
7
8
  const here = dirname(fileURLToPath(import.meta.url));
8
9
  const coreDir = resolve(here, '..');
@@ -71,6 +72,53 @@ function verifyPassword(password, salt, expectedHex) {
71
72
  return expected.length === actual.length && timingSafeEqual(actual, expected);
72
73
  }
73
74
 
75
+ function currentGhUser() {
76
+ const result = spawnSync('gh', ['api', 'user', '--jq', '{login:.login,id:.id,email:.email}'], {
77
+ cwd: rootDir,
78
+ encoding: 'utf8'
79
+ });
80
+ if (result.status !== 0) return null;
81
+ try {
82
+ return JSON.parse(result.stdout || '{}');
83
+ } catch {
84
+ return null;
85
+ }
86
+ }
87
+
88
+ function githubNoreplyEmail(user) {
89
+ if (!user?.login || !user?.id) return '';
90
+ return `${user.id}+${user.login}@users.noreply.github.com`;
91
+ }
92
+
93
+ function resolveGitIdentityInput(state, actor, name, emailInput, githubUsernameInput) {
94
+ const policy = state.autosync?.githubIdentity || {};
95
+ const preferNoreply = policy.useNoreplyForMemberCommits !== false;
96
+ const gh = currentGhUser();
97
+ const githubUsername = githubUsernameInput || gh?.login || '';
98
+ if (gh?.login && githubUsername && policy.requireMatchedGhUser !== false && gh.login.toLowerCase() !== githubUsername.toLowerCase()) {
99
+ throw new Error(`GitHub CLI authenticated as ${gh.login}, expected ${githubUsername}. Run gh auth login as the real user.`);
100
+ }
101
+
102
+ let email = String(emailInput || '').trim();
103
+ const wantsNoreply = !email || ['auto', 'github', 'github-noreply', 'noreply'].includes(email.toLowerCase());
104
+ if (preferNoreply && wantsNoreply) {
105
+ email = githubNoreplyEmail(gh);
106
+ if (!email) {
107
+ throw new Error('Cannot derive GitHub noreply email. Run gh auth login or provide --git-email "<github-verified-email>".');
108
+ }
109
+ }
110
+ if (!email && state.autosync?.requireUserGitEmail !== false) {
111
+ throw new Error('Git email is required. Use --git-email github-noreply after gh auth login, or provide a GitHub-verified email.');
112
+ }
113
+ return {
114
+ name,
115
+ email,
116
+ githubUsername,
117
+ githubUserId: gh?.login?.toLowerCase() === githubUsername.toLowerCase() ? gh.id : undefined,
118
+ emailSource: wantsNoreply && email ? 'github-noreply' : 'manual'
119
+ };
120
+ }
121
+
74
122
  function activeAgentFor(state, actor) {
75
123
  const activeId = state.activeAgents?.[actor];
76
124
  if (!activeId) return null;
@@ -502,7 +550,7 @@ function setup(args) {
502
550
  const codingLanguage = String(args['coding-language'] || 'English');
503
551
  const githubUrl = String(args['github-url'] || '');
504
552
  const gitName = String(args['git-name'] || displayName);
505
- const gitEmail = String(args['git-email'] || '');
553
+ const gitEmail = String(args['git-email'] || 'github-noreply');
506
554
  const githubUsername = String(args['github-username'] || '');
507
555
  const joinMode = String(args['join-mode'] || 'owner-approved');
508
556
 
@@ -510,12 +558,10 @@ function setup(args) {
510
558
  if (password.length < 8) throw new Error('Password must be at least 8 characters.');
511
559
  if (!['solo', 'team'].includes(mode)) throw new Error('Mode must be solo or team.');
512
560
  if (mode === 'team' && !githubUrl) throw new Error('Team mode requires --github-url.');
513
- if (state.autosync?.requireUserGitEmail !== false && !gitEmail) {
514
- throw new Error('Git email is required so commits are attributed to the real user, not the AI tool.');
515
- }
516
561
  if (!['open', 'owner-approved', 'invite'].includes(joinMode)) {
517
562
  throw new Error('Join mode must be open, owner-approved, or invite.');
518
563
  }
564
+ const gitIdentity = resolveGitIdentityInput(state, codename, gitName, gitEmail, githubUsername);
519
565
 
520
566
  const { passwordHash, passwordSalt } = hashPassword(password);
521
567
  state.initialized = true;
@@ -545,11 +591,7 @@ function setup(args) {
545
591
  conversation: conversationLanguage,
546
592
  coding: codingLanguage
547
593
  },
548
- gitIdentity: {
549
- name: gitName,
550
- email: gitEmail,
551
- githubUsername
552
- },
594
+ gitIdentity,
553
595
  joinedAt: now()
554
596
  }
555
597
  };
@@ -822,12 +864,12 @@ function memberGitIdentity(args) {
822
864
  if (!member) throw new Error('Unknown actor.');
823
865
  const agent = requireActiveAgent(state, actor);
824
866
  const name = String(args.name || member.displayName || actor);
825
- const email = requireArg(args, 'email');
867
+ const email = String(args.email || 'github-noreply');
826
868
  const githubUsername = String(args['github-username'] || member.gitIdentity?.githubUsername || '');
827
- member.gitIdentity = { name, email, githubUsername };
869
+ member.gitIdentity = resolveGitIdentityInput(state, actor, name, email, githubUsername);
828
870
  appendLedger(state, actor, 'git-identity', `Updated git identity for ${actor}.`, agent);
829
871
  writeState(state);
830
- console.log(`Git identity set for ${actor}: ${name} <${email}>${githubUsername ? ` github=${githubUsername}` : ''}`);
872
+ console.log(`Git identity set for ${actor}: ${member.gitIdentity.name} <${member.gitIdentity.email}>${member.gitIdentity.githubUsername ? ` github=${member.gitIdentity.githubUsername}` : ''}`);
831
873
  }
832
874
 
833
875
  function validate() {
@@ -843,6 +885,9 @@ function validate() {
843
885
  if (state.memoryPolicy && typeof state.memoryPolicy !== 'object') errors.push('memoryPolicy must be object');
844
886
  if (state.agentRouter && typeof state.agentRouter !== 'object') errors.push('agentRouter must be object');
845
887
  if (state.partyMode && typeof state.partyMode !== 'object') errors.push('partyMode must be object');
888
+ if (state.autosync?.githubIdentity && typeof state.autosync.githubIdentity !== 'object') {
889
+ errors.push('autosync.githubIdentity must be object');
890
+ }
846
891
  if (state.projectRootPolicy && typeof state.projectRootPolicy !== 'object') errors.push('projectRootPolicy must be object');
847
892
  if (state.projectRootPolicy?.rules && !Array.isArray(state.projectRootPolicy.rules)) {
848
893
  errors.push('projectRootPolicy.rules must be array');
@@ -985,9 +1030,9 @@ function main() {
985
1030
  console.log(`Usage:
986
1031
  node .MOP/scripts/mop-core.mjs status
987
1032
  node .MOP/scripts/mop-core.mjs validate
988
- node .MOP/scripts/mop-core.mjs setup --project-name NAME --name DISPLAY --codename CODE --password PASS --mode solo|team --conversation-language LANG --coding-language LANG --git-email EMAIL [--git-name NAME] [--github-username USER] [--github-url URL]
1033
+ node .MOP/scripts/mop-core.mjs setup --project-name NAME --name DISPLAY --codename CODE --password PASS --mode solo|team --conversation-language LANG --coding-language LANG [--git-email github-noreply|EMAIL] [--git-name NAME] [--github-username USER] [--github-url URL]
989
1034
  node .MOP/scripts/mop-core.mjs login --codename CODE --password PASS
990
- node .MOP/scripts/mop-core.mjs member git-identity --actor CODE --name NAME --email EMAIL [--github-username USER]
1035
+ node .MOP/scripts/mop-core.mjs member git-identity --actor CODE --name NAME [--email github-noreply|EMAIL] [--github-username USER]
991
1036
  node .MOP/scripts/mop-core.mjs agent activate --actor CODE --role ROLE --title TITLE --name NAME
992
1037
  node .MOP/scripts/mop-core.mjs agent use --actor CODE --name NAME
993
1038
  node .MOP/scripts/mop-core.mjs agent current --actor CODE
package/.agents/AGENTS.md CHANGED
@@ -34,6 +34,10 @@ Before doing anything, read `.MOP/STATE.json` and follow
34
34
  - For risky changes, run adversarial review before implementation or merge.
35
35
  - When a role agent first appears, ask the user to name it before using it as a
36
36
  personal agent.
37
+ - Autosycn member commits must use the active member GitHub account. By default,
38
+ MOP derives `ID+USERNAME@users.noreply.github.com` from `gh api user` and
39
+ refuses mismatched GitHub accounts. `BURHAN-MOP` identity is reserved for
40
+ merge guardian commits only.
37
41
 
38
42
  ## Operating Rules
39
43
 
package/AGENTS.md CHANGED
@@ -46,8 +46,8 @@ follow `.MOP/PROTOCOL.md`.
46
46
 
47
47
  For `/mop-setup`, ask in order: project name (default folder name), owner display
48
48
  name, owner codename, password, solo/team, conversation language, coding
49
- language, GitHub link, GitHub username, Git commit email, join mode if team,
50
- then whether to activate auto-deploy now or later.
49
+ language, GitHub link, GitHub username, Git commit email (`github-noreply` by
50
+ default), join mode if team, then whether to activate auto-deploy now or later.
51
51
 
52
52
  Agent role/template names are not personal AI names. When a role agent is first
53
53
  needed, ask the user to name it and save it with `mop-core.mjs agent activate`.
@@ -62,6 +62,9 @@ agent route instead of answering invisibly.
62
62
  Default skill: `autosycn`. After meaningful changes, use
63
63
  `.MOP/scripts/mop-autosycn.mjs`. It must commit and merge with the
64
64
  real member Git identity from state, never with the AI tool identity.
65
+ Member commits must use the active member GitHub account; by default MOP derives
66
+ `ID+USERNAME@users.noreply.github.com` from `gh api user` and refuses mismatched
67
+ GitHub accounts. `BURHAN-MOP` identity is only for merge guardian commits.
65
68
  In team mode, run `preflight --actor <codename>` before starting work; work goes
66
69
  to `dev/<codename>`. Every small or large change is pushed there first, then
67
70
  BURHAN-MOP reviews and merges to `main` only when checks pass.
package/CLAUDE.md CHANGED
@@ -47,6 +47,9 @@ agent route instead of answering invisibly.
47
47
  Default skill: `autosycn`. After meaningful changes, use
48
48
  `.MOP/scripts/mop-autosycn.mjs`. It must commit and merge with the
49
49
  real member Git identity from state, never with the AI tool identity.
50
+ Member commits must use the active member GitHub account; by default MOP derives
51
+ `ID+USERNAME@users.noreply.github.com` from `gh api user` and refuses mismatched
52
+ GitHub accounts. `BURHAN-MOP` identity is only for merge guardian commits.
50
53
  In team mode, run `preflight --actor <codename>` before starting work; work goes
51
54
  to `dev/<codename>`. Every small or large change is pushed there first, then
52
55
  BURHAN-MOP reviews and merges to `main` only when checks pass.
package/GEMINI.md CHANGED
@@ -16,6 +16,9 @@ rules are imported below.
16
16
  example: `agent: <agent-name> (<agent-role>) to <user>`.
17
17
  - Save a one-line memory after meaningful work with
18
18
  `mop-core.mjs memory add --actor <codename> --kind conversation --summary "<outcome>"`.
19
+ - Autosycn member commits must use the active member GitHub account. MOP derives
20
+ `ID+USERNAME@users.noreply.github.com` from `gh api user` by default and
21
+ refuses mismatched GitHub accounts.
19
22
  - Treat the current workspace root as the project root; do not create a nested
20
23
  project wrapper folder for app work unless the user explicitly asks for it.
21
24
  - Use `.gemini/settings.json` for context filenames and MCP server setup.
package/README.bm.md CHANGED
@@ -142,7 +142,7 @@ dan merge hanya bila workflow selamat.
142
142
  | --- | --- |
143
143
  | npm package | [`burhan-mop`](https://www.npmjs.com/package/burhan-mop) |
144
144
  | command latest | `npx burhan-mop install` |
145
- | GitHub release | [`v0.1.6`](https://github.com/BURHANDEV-ENTERPRISE/BURHAN-MOP/releases/tag/v0.1.6) |
145
+ | GitHub release | [`v0.1.7`](https://github.com/BURHANDEV-ENTERPRISE/BURHAN-MOP/releases/tag/v0.1.7) |
146
146
  | Node | `>=20` |
147
147
 
148
148
  ## Links
package/README.md CHANGED
@@ -141,7 +141,7 @@ and merges only when the workflow is safe.
141
141
  | --- | --- |
142
142
  | npm package | [`burhan-mop`](https://www.npmjs.com/package/burhan-mop) |
143
143
  | latest command | `npx burhan-mop install` |
144
- | GitHub release | [`v0.1.6`](https://github.com/BURHANDEV-ENTERPRISE/BURHAN-MOP/releases/tag/v0.1.6) |
144
+ | GitHub release | [`v0.1.7`](https://github.com/BURHANDEV-ENTERPRISE/BURHAN-MOP/releases/tag/v0.1.7) |
145
145
  | Node | `>=20` |
146
146
 
147
147
  ## Links
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "burhan-mop",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "MOP portable agent memory core installer and CLI.",
5
5
  "type": "module",
6
6
  "author": "BURHANDEV ENTERPRISE",