burhan-mop 0.1.6 → 0.1.8

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
@@ -122,6 +123,11 @@ Routing rules:
122
123
  genuinely requires several areas of expertise.
123
124
  - If the route reports `partyMode.active: true`, run Party Mode before the final
124
125
  answer. Name any missing participant agents first.
126
+ - If the route reports `nextAction: "name-required-party-agents"`, stop and ask
127
+ every question in `missingAgentQuestions`. Do not continue with the task until
128
+ all required party agents have names.
129
+ - Explicit requests such as `party mode`, `multi-agent`, `semua agent`, or
130
+ `agent bincang` must activate Party Mode automatically.
125
131
  - The route JSON includes an `answerContract`. The assistant must restore
126
132
  monthly memory, start the visible answer with `answerContract.firstLine`, and
127
133
  save a one-line memory after meaningful work.
@@ -162,6 +168,8 @@ PARTY MODE
162
168
  - Do not include irrelevant agents just because they exist.
163
169
  - If a needed party role has no named agent, ask the user to name that agent
164
170
  before using it.
171
+ - If several party agents are missing, ask for all missing names in one reply:
172
+ `Kita ada <N> agent belum ada nama. Beri nama untuk ...`
165
173
 
166
174
  Visible dialogue format:
167
175
 
@@ -187,6 +195,32 @@ agent: <from-name> (<from-role>)
187
195
  <explanation>
188
196
  ```
189
197
 
198
+ ## Browser And Scraping Gate
199
+
200
+ Browser, scraping, rendered extraction, clicking, login flow, bot-detection, and
201
+ form-filling tasks must pass browser preflight before any browser automation.
202
+
203
+ Run:
204
+
205
+ ```bash
206
+ node .MOP/scripts/mop-core.mjs browser preflight
207
+ ```
208
+
209
+ Rules:
210
+
211
+ - First scan the default browser automatically. On Linux this uses
212
+ `xdg-settings get default-web-browser`; fallback is `$BROWSER`.
213
+ - If Chrome or Chromium is detected, browser automation may proceed with normal
214
+ Chrome-compatible mode.
215
+ - If Edge, Brave, or Opera is detected, use browser-act `chrome-direct` mode and
216
+ guide the user to start that browser with `--remote-debugging-port`.
217
+ - If no supported browser is detected, ask the user which browser they use
218
+ before scraping or browser automation.
219
+ - Do not start a default Chrome session first when the user uses Edge, Brave, or
220
+ Opera.
221
+ - If `agent route` includes `browserPreflight.needsQuestion: true`, ask
222
+ `browserPreflight.question` and stop.
223
+
190
224
  ## MOP Workflow
191
225
 
192
226
  MOP Workflow is BMAD-inspired and MOP-native. It is the default
@@ -431,10 +465,13 @@ In team mode, an agent name is the identity.
431
465
  Autosycn is always available and should be used after meaningful state or file
432
466
  changes. It is intentionally identity-safe.
433
467
 
434
- Before first push for a member, configure the real Git identity:
468
+ Before first push for a member, configure the real GitHub identity. Default to
469
+ GitHub noreply so commits attach to the active user account without exposing
470
+ private email:
435
471
 
436
472
  ```bash
437
- node .MOP/scripts/mop-core.mjs member git-identity --actor <codename> --name "<display name>" --email "<github-verified-email>" --github-username "<github-login>"
473
+ gh auth login
474
+ node .MOP/scripts/mop-core.mjs member git-identity --actor <codename> --name "<display name>" --email github-noreply --github-username "<github-login>"
438
475
  ```
439
476
 
440
477
  Then run:
@@ -455,6 +492,13 @@ Autosycn must:
455
492
  - Commit with `GIT_AUTHOR_NAME`, `GIT_AUTHOR_EMAIL`, `GIT_COMMITTER_NAME`, and
456
493
  `GIT_COMMITTER_EMAIL` set from member state.
457
494
  - Set local `git config user.name` and `user.email` before commit/merge.
495
+ - For member commits, derive `user.email` from the active GitHub CLI account as
496
+ `ID+USERNAME@users.noreply.github.com` when
497
+ `autosync.githubIdentity.useNoreplyForMemberCommits` is enabled.
498
+ - Refuse to commit or push if `gh api user` is authenticated as a different
499
+ GitHub username than the active member's configured `githubUsername`.
500
+ - Use the member GitHub identity for setup, preflight, memory, and work branch
501
+ commits. Use `BURHAN-MOP` only for merge guardian commits.
458
502
  - In team mode, `main` is the trunk and each user works on `dev/<codename>`.
459
503
  - Push to `dev/<codename>` in team mode and `main` in solo mode.
460
504
  - After a team push, BURHAN-MOP reviews `dev/<codename>` and merges it into
@@ -469,10 +513,11 @@ Autosycn must:
469
513
  - If `githubUsername` is configured, refuse to push unless `gh api user`
470
514
  verifies the same account.
471
515
 
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.
516
+ Important: GitHub commit attribution comes from commit email. MOP uses the
517
+ active GitHub account noreply email for member commits by default. GitHub push
518
+ actor comes from the credential or SSH key used by `git push`; no script can
519
+ fake that. If GitHub shows the wrong pusher, fix `gh auth login`, Git
520
+ Credential Manager, or the SSH key account.
476
521
 
477
522
  ## Default Skill: auto-deploy
478
523
 
package/.MOP/STATE.json CHANGED
@@ -77,7 +77,34 @@
77
77
  "Normal Party mode should use at least 3 participants and prefer 4 when the task has enough relevant roles.",
78
78
  "The primary agent coordinates the party and keeps the user intent visible.",
79
79
  "Every visible agent message must use the configured dialogue format.",
80
- "Agents should ask each other focused questions, answer directly, then hand a clear result to the user."
80
+ "Agents should ask each other focused questions, answer directly, then hand a clear result to the user.",
81
+ "Explicit requests such as party mode, multi-agent, semua agent, or agent discussion must activate Party Mode.",
82
+ "If Party Mode needs unnamed agents, ask for all missing agent names before doing the task."
83
+ ]
84
+ },
85
+ "browserPolicy": {
86
+ "requirePreflightBeforeBrowserWork": true,
87
+ "requireDefaultBrowserCheck": true,
88
+ "supportedChoices": [
89
+ "Chrome",
90
+ "Edge",
91
+ "Brave",
92
+ "Opera"
93
+ ],
94
+ "builtinChromeBrowsers": [
95
+ "chrome",
96
+ "chromium"
97
+ ],
98
+ "directModeBrowsers": [
99
+ "brave",
100
+ "edge",
101
+ "opera"
102
+ ],
103
+ "rules": [
104
+ "Before browser, scraping, extraction, click automation, login flow, or form filling, run browser preflight.",
105
+ "Check the user's default browser automatically when possible.",
106
+ "If the default browser is Edge, Brave, or Opera, use chrome-direct mode and guide the user to start remote debugging.",
107
+ "If no supported browser is detected, ask the user which browser they use before continuing."
81
108
  ]
82
109
  },
83
110
  "workflow": {
@@ -399,7 +426,18 @@
399
426
  }
400
427
  },
401
428
  "requireUserGitEmail": true,
402
- "verifyGhUserWhenConfigured": true
429
+ "verifyGhUserWhenConfigured": true,
430
+ "githubIdentity": {
431
+ "requireMatchedGhUser": true,
432
+ "useNoreplyForMemberCommits": true,
433
+ "noreplyFormat": "id-plus-login",
434
+ "rules": [
435
+ "Member commits must use the active member GitHub account, not an AI or guardian identity.",
436
+ "When gh is authenticated, derive member commit email as ID+USERNAME@users.noreply.github.com.",
437
+ "Refuse autosycn if the active gh account does not match the member githubUsername.",
438
+ "BURHAN-MOP identity is reserved for merge guardian commits only."
439
+ ]
440
+ }
403
441
  },
404
442
  "members": {},
405
443
  "installedSkills": [
@@ -520,6 +558,12 @@
520
558
  "kind": "operations",
521
559
  "purpose": "GitHub repo, branch, PR, issue, release, and merge workflow."
522
560
  },
561
+ {
562
+ "role": "browser",
563
+ "title": "Browser Automation Agent",
564
+ "kind": "operations",
565
+ "purpose": "Browser preflight, scraping, rendered extraction, clicking, form filling, login flows, and browser-act coordination."
566
+ },
523
567
  {
524
568
  "role": "devops",
525
569
  "title": "DevOps Engineer",
@@ -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;
@@ -184,6 +232,66 @@ function answerContractFor(state, actor, agent = activeAgentFor(state, actor)) {
184
232
  };
185
233
  }
186
234
 
235
+ function browserPolicy(state) {
236
+ return state.browserPolicy || {
237
+ requirePreflightBeforeBrowserWork: true,
238
+ requireDefaultBrowserCheck: true,
239
+ directModeBrowsers: ['brave', 'edge', 'opera'],
240
+ builtinChromeBrowsers: ['chrome', 'chromium'],
241
+ supportedChoices: ['Chrome', 'Edge', 'Brave', 'Opera']
242
+ };
243
+ }
244
+
245
+ function detectDefaultBrowser() {
246
+ const xdg = spawnSync('xdg-settings', ['get', 'default-web-browser'], {
247
+ cwd: rootDir,
248
+ encoding: 'utf8'
249
+ });
250
+ const raw = (xdg.status === 0 ? xdg.stdout : '').trim() || process.env.BROWSER || '';
251
+ const value = raw.toLowerCase();
252
+ let family = 'unknown';
253
+ if (/(google-chrome|chrome)/.test(value) && !/chromium/.test(value)) family = 'chrome';
254
+ else if (/chromium/.test(value)) family = 'chromium';
255
+ else if (/brave/.test(value)) family = 'brave';
256
+ else if (/(microsoft-edge|edge)/.test(value)) family = 'edge';
257
+ else if (/opera/.test(value)) family = 'opera';
258
+ else if (/firefox/.test(value)) family = 'firefox';
259
+ return {
260
+ raw: raw || null,
261
+ family,
262
+ source: raw ? (xdg.status === 0 ? 'xdg-settings' : 'BROWSER') : 'not-detected'
263
+ };
264
+ }
265
+
266
+ function browserPreflightFor(state) {
267
+ const policy = browserPolicy(state);
268
+ const detected = detectDefaultBrowser();
269
+ const direct = (policy.directModeBrowsers || []).includes(detected.family);
270
+ const builtin = (policy.builtinChromeBrowsers || []).includes(detected.family);
271
+ const supported = direct || builtin;
272
+ const mode = builtin ? 'chrome' : direct ? 'chrome-direct' : 'ask-user-browser';
273
+ const needsQuestion = !supported;
274
+ return {
275
+ required: policy.requirePreflightBeforeBrowserWork !== false,
276
+ defaultBrowser: detected,
277
+ mode,
278
+ ready: supported,
279
+ needsQuestion,
280
+ question: needsQuestion
281
+ ? `Saya tak dapat kesan Chrome/Edge/Brave/Opera sebagai browser default. Awak guna browser apa? Pilih: ${(policy.supportedChoices || ['Chrome', 'Edge', 'Brave', 'Opera']).join(', ')}.`
282
+ : '',
283
+ instructions: direct
284
+ ? [
285
+ `Use browser-act chrome-direct for ${detected.family}.`,
286
+ 'Guide the user to start that browser with --remote-debugging-port before scraping or form automation.',
287
+ 'Do not create a default chrome session first.'
288
+ ]
289
+ : builtin
290
+ ? ['Use normal Chrome-compatible browser automation.', 'Do not ask the user again unless automation fails.']
291
+ : ['Ask the user which browser they use before scraping or browser automation.']
292
+ };
293
+ }
294
+
187
295
  function requireActiveAgent(state, actor, role = 'core', title = 'Core Agent') {
188
296
  const agent = activeAgentFor(state, actor);
189
297
  if (agent) return agent;
@@ -335,7 +443,7 @@ const routeRules = [
335
443
  {
336
444
  role: 'browser',
337
445
  support: ['researcher', 'tester'],
338
- keywords: ['browser', 'browse', 'scrape', 'scraping', 'extract', 'click', 'login flow', 'fill form', 'captcha', 'bot detection', 'website', 'url', 'webpage']
446
+ keywords: ['agent browser', 'browser agent', 'browser automation', 'browser', 'browse', 'scrape', 'scraping', 'web scraping', 'extract', 'click', 'login flow', 'fill form', 'captcha', 'bot detection', 'website', 'url', 'webpage']
339
447
  },
340
448
  {
341
449
  role: 'researcher',
@@ -359,6 +467,10 @@ function routeScore(task, rule) {
359
467
  return rule.keywords.reduce((score, keyword) => task.includes(keyword) ? score + Math.max(1, keyword.split(/\s+/).length) : score, 0);
360
468
  }
361
469
 
470
+ function hasBrowserWorkIntent(task) {
471
+ return /\b(agent browser|browser agent|browser automation|browse|scrape|scraping|web scraping|extract|click|login flow|fill form|captcha|bot detection|webpage|url)\b/.test(task);
472
+ }
473
+
362
474
  function uniqueValues(values) {
363
475
  return [...new Set(values.filter(Boolean))];
364
476
  }
@@ -372,6 +484,7 @@ function maybeLimit(values, limit) {
372
484
 
373
485
  function shouldActivatePartyMode(state, task, primaryRole, supportRoles, newSystemIntent) {
374
486
  if (state.partyMode?.enabled === false || state.partyMode?.autoActivateWhenNeeded === false) return false;
487
+ const explicitPartyIntent = /\b(party mode|party|multi[- ]?agent|swarm|semua agent|banyak agent|agent.*bincang|bincang.*agent|agent.*discuss|discuss.*agent)\b/.test(task);
375
488
  const multiDomainIntent = [
376
489
  ['ui', 'backend'],
377
490
  ['frontend', 'backend'],
@@ -381,10 +494,11 @@ function shouldActivatePartyMode(state, task, primaryRole, supportRoles, newSyst
381
494
  ['security', 'auth'],
382
495
  ['prompt', 'system']
383
496
  ].some(([first, second]) => task.includes(first) && task.includes(second));
497
+ const browserRiskIntent = hasBrowserWorkIntent(task);
384
498
  const connectiveIntent = /\b(connect|connected|integrate|integration|bersambung|sambung|hubung|flow|workflow)\b/.test(task);
385
499
  const broadBuild = newSystemIntent && supportRoles.length >= 2;
386
500
  const specialistStack = supportRoles.length >= 3 && ['architect', 'planner', 'core'].includes(primaryRole);
387
- return multiDomainIntent || connectiveIntent || broadBuild || specialistStack;
501
+ return explicitPartyIntent || multiDomainIntent || browserRiskIntent || connectiveIntent || broadBuild || specialistStack;
388
502
  }
389
503
 
390
504
  function partyFormat(state) {
@@ -405,12 +519,14 @@ function partyParticipantsFor(state, primaryRole, supportRoles, scored, partyAct
405
519
  const minimum = Number(state.partyMode?.minimumParticipants || 3);
406
520
  const floor = Number.isFinite(preferredMinimum) ? Math.max(minimum, preferredMinimum) : minimum;
407
521
  const fallbackRoles = ['planner', 'researcher', 'reviewer', 'coder', 'architect', 'prompt', 'tester'];
408
- const participants = uniqueValues([
522
+ const relevant = uniqueValues([
409
523
  primaryRole,
410
524
  ...supportRoles,
411
- ...scored.map((rule) => rule.role),
412
- ...fallbackRoles
525
+ ...scored.map((rule) => rule.role)
413
526
  ]);
527
+ const participants = relevant.length >= floor
528
+ ? relevant
529
+ : uniqueValues([...relevant, ...fallbackRoles]).slice(0, floor);
414
530
  const catalogRoles = new Set((state.agentCatalog || []).map((item) => item.role));
415
531
  const filtered = participants.filter((role) => catalogRoles.has(role));
416
532
  const enough = filtered.length >= floor ? filtered : participants;
@@ -428,15 +544,16 @@ function inferAgentRoute(state, taskText) {
428
544
  const newSystemIntent = /\b(system|sistem|app|tool|platform|website|dashboard|engine|core)\b/.test(task)
429
545
  || /buat sebuah|bina sebuah|create a|build a/.test(task);
430
546
  const implementationIntent = /\b(code|coding|implement|fix|ubah file|buat file)\b/.test(task);
547
+ const browserWorkIntent = hasBrowserWorkIntent(task);
431
548
  const top = scored[0];
432
- let primaryRole = top?.role || state.agentPolicy?.defaultRole || 'core';
549
+ let primaryRole = browserWorkIntent ? 'browser' : (top?.role || state.agentPolicy?.defaultRole || 'core');
433
550
 
434
- if (newSystemIntent && !implementationIntent && state.agentRouter?.preferHighReasoningForNewSystems !== false) {
551
+ if (newSystemIntent && !implementationIntent && !browserWorkIntent && state.agentRouter?.preferHighReasoningForNewSystems !== false) {
435
552
  primaryRole = state.agentRouter?.defaultHighReasoningRole || 'architect';
436
553
  }
437
554
 
438
555
  const primary = catalogForRole(state, primaryRole);
439
- const baseSupport = newSystemIntent
556
+ const baseSupport = newSystemIntent && !browserWorkIntent
440
557
  ? ['planner', 'researcher', 'prompt', 'coder', 'reviewer']
441
558
  : (top?.support || []);
442
559
  const supportRoles = maybeLimit(uniqueValues([
@@ -447,7 +564,7 @@ function inferAgentRoute(state, taskText) {
447
564
  const partyActive = shouldActivatePartyMode(state, task, primaryRole, supportRoles, newSystemIntent);
448
565
  const partyParticipants = partyParticipantsFor(state, primaryRole, supportRoles, scored, partyActive);
449
566
 
450
- const ambiguousNewSystem = newSystemIntent && words.length < 18;
567
+ const ambiguousNewSystem = newSystemIntent && !browserWorkIntent && words.length < 18;
451
568
  const noClearMatch = scored.length === 0 && words.length > 3;
452
569
  const needsClarification = state.agentRouter?.clarifyBeforeActionWhenAmbiguous !== false
453
570
  && (ambiguousNewSystem || noClearMatch || /\b(maybe|mungkin|lebih kurang|macam)\b/.test(task));
@@ -478,7 +595,7 @@ function inferAgentRoute(state, taskText) {
478
595
  confidence: top ? Math.min(0.95, 0.45 + (top.score * 0.08)) : 0.35,
479
596
  needsClarification,
480
597
  questions,
481
- reason: newSystemIntent
598
+ reason: newSystemIntent && !browserWorkIntent
482
599
  ? `New or broad system task routed to ${primary.title} for high-reasoning planning before implementation.`
483
600
  : top
484
601
  ? `Matched keywords for ${primary.title}: ${top.keywords.filter((keyword) => task.includes(keyword)).join(', ')}.`
@@ -502,7 +619,7 @@ function setup(args) {
502
619
  const codingLanguage = String(args['coding-language'] || 'English');
503
620
  const githubUrl = String(args['github-url'] || '');
504
621
  const gitName = String(args['git-name'] || displayName);
505
- const gitEmail = String(args['git-email'] || '');
622
+ const gitEmail = String(args['git-email'] || 'github-noreply');
506
623
  const githubUsername = String(args['github-username'] || '');
507
624
  const joinMode = String(args['join-mode'] || 'owner-approved');
508
625
 
@@ -510,12 +627,10 @@ function setup(args) {
510
627
  if (password.length < 8) throw new Error('Password must be at least 8 characters.');
511
628
  if (!['solo', 'team'].includes(mode)) throw new Error('Mode must be solo or team.');
512
629
  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
630
  if (!['open', 'owner-approved', 'invite'].includes(joinMode)) {
517
631
  throw new Error('Join mode must be open, owner-approved, or invite.');
518
632
  }
633
+ const gitIdentity = resolveGitIdentityInput(state, codename, gitName, gitEmail, githubUsername);
519
634
 
520
635
  const { passwordHash, passwordSalt } = hashPassword(password);
521
636
  state.initialized = true;
@@ -545,11 +660,7 @@ function setup(args) {
545
660
  conversation: conversationLanguage,
546
661
  coding: codingLanguage
547
662
  },
548
- gitIdentity: {
549
- name: gitName,
550
- email: gitEmail,
551
- githubUsername
552
- },
663
+ gitIdentity,
553
664
  joinedAt: now()
554
665
  }
555
666
  };
@@ -692,13 +803,27 @@ function agentRoute(args) {
692
803
  };
693
804
  }) : [];
694
805
  const missingPartyAgents = partyAgents.filter((item) => item.missing);
806
+ const missingAgentCommands = missingPartyAgents.map((item) => (
807
+ `node .MOP/scripts/mop-core.mjs agent activate --actor ${actor} --role ${item.role} --title "${item.title}" --name "<agent-name>"`
808
+ ));
809
+ const missingAgentQuestions = missingPartyAgents.map((item) => (
810
+ `Beri nama untuk ${item.title} (${item.role}) kamu:`
811
+ ));
812
+ const browserPreflight = route.primaryRole === 'browser'
813
+ || route.supportRoles.includes('browser')
814
+ || route.partyMode?.participants?.includes('browser')
815
+ || hasBrowserWorkIntent(route.task || '')
816
+ ? browserPreflightFor(state)
817
+ : null;
695
818
  const response = {
696
819
  ok: Boolean(agent),
697
820
  actor,
698
821
  route,
699
822
  partyAgents,
823
+ missingAgents: missingPartyAgents,
700
824
  activeAgent: null,
701
825
  answerContract: null,
826
+ browserPreflight,
702
827
  monthlyMemory: {
703
828
  restoreCommand: `node .MOP/scripts/mop-core.mjs memory brief --actor ${actor}`,
704
829
  saveCommand: `node .MOP/scripts/mop-core.mjs memory add --actor ${actor} --kind conversation --summary "<one-line outcome>"`
@@ -721,18 +846,33 @@ function agentRoute(args) {
721
846
  if (route.partyMode.active && missingPartyAgents.length) {
722
847
  response.ok = false;
723
848
  response.nextAction = 'name-required-party-agents';
724
- response.message = 'Party mode diperlukan, tetapi ada agent terlibat yang belum dinamakan.';
725
- response.missingAgentCommands = missingPartyAgents.map((item) => (
726
- `node .MOP/scripts/mop-core.mjs agent activate --actor ${actor} --role ${item.role} --title "${item.title}" --name "<agent-name>"`
727
- ));
849
+ response.message = `Party Mode perlukan ${missingPartyAgents.length} agent yang belum ada nama. Minta nama semua agent ini dahulu sebelum sambung kerja.`;
850
+ response.ask = `Kita ada ${missingPartyAgents.length} agent belum ada nama. ${missingAgentQuestions.join(' ')}`;
851
+ response.missingAgentQuestions = missingAgentQuestions;
852
+ response.missingAgentCommands = missingAgentCommands;
853
+ } else if (browserPreflight?.required && browserPreflight.needsQuestion) {
854
+ response.ok = false;
855
+ response.nextAction = 'ask-browser-before-browser-work';
856
+ response.message = 'Browser preflight belum ready. Tanya browser user dahulu sebelum scraping/browser automation.';
857
+ response.ask = browserPreflight.question;
728
858
  } else {
729
859
  response.nextAction = route.needsClarification ? 'ask-clarifying-questions' : 'proceed-with-agent';
730
860
  }
731
861
  } else {
732
- response.nextAction = 'name-required-agent';
733
- response.message = `Task ini perlukan ${route.primaryTitle}. Agent ini belum ada nama lagi atau belum dipilih.`;
734
- response.ask = `Beri nama untuk ${route.primaryTitle} kamu:`;
862
+ response.nextAction = route.partyMode.active && missingPartyAgents.length
863
+ ? 'name-required-party-agents'
864
+ : 'name-required-agent';
865
+ response.message = route.partyMode.active && missingPartyAgents.length
866
+ ? `Party Mode perlukan ${missingPartyAgents.length} agent yang belum ada nama. Minta nama semua agent ini dahulu sebelum sambung kerja.`
867
+ : `Task ini perlukan ${route.primaryTitle}. Agent ini belum ada nama lagi atau belum dipilih.`;
868
+ response.ask = route.partyMode.active && missingPartyAgents.length
869
+ ? `Kita ada ${missingPartyAgents.length} agent belum ada nama. ${missingAgentQuestions.join(' ')}`
870
+ : `Beri nama untuk ${route.primaryTitle} kamu:`;
735
871
  response.command = `node .MOP/scripts/mop-core.mjs agent activate --actor ${actor} --role ${route.primaryRole} --title "${route.primaryTitle}" --name "<agent-name>"`;
872
+ if (route.partyMode.active && missingPartyAgents.length) {
873
+ response.missingAgentQuestions = missingAgentQuestions;
874
+ response.missingAgentCommands = missingAgentCommands;
875
+ }
736
876
  if (route.needsClarification) response.afterNaming = route.questions;
737
877
  }
738
878
 
@@ -747,6 +887,11 @@ function agentList() {
747
887
  }, null, 2));
748
888
  }
749
889
 
890
+ function browserPreflight() {
891
+ const state = readState();
892
+ console.log(JSON.stringify(browserPreflightFor(state), null, 2));
893
+ }
894
+
750
895
  function memoryAdd(args) {
751
896
  const state = readState();
752
897
  if (!state.initialized) throw new Error('MOP is not initialized.');
@@ -822,12 +967,12 @@ function memberGitIdentity(args) {
822
967
  if (!member) throw new Error('Unknown actor.');
823
968
  const agent = requireActiveAgent(state, actor);
824
969
  const name = String(args.name || member.displayName || actor);
825
- const email = requireArg(args, 'email');
970
+ const email = String(args.email || 'github-noreply');
826
971
  const githubUsername = String(args['github-username'] || member.gitIdentity?.githubUsername || '');
827
- member.gitIdentity = { name, email, githubUsername };
972
+ member.gitIdentity = resolveGitIdentityInput(state, actor, name, email, githubUsername);
828
973
  appendLedger(state, actor, 'git-identity', `Updated git identity for ${actor}.`, agent);
829
974
  writeState(state);
830
- console.log(`Git identity set for ${actor}: ${name} <${email}>${githubUsername ? ` github=${githubUsername}` : ''}`);
975
+ console.log(`Git identity set for ${actor}: ${member.gitIdentity.name} <${member.gitIdentity.email}>${member.gitIdentity.githubUsername ? ` github=${member.gitIdentity.githubUsername}` : ''}`);
831
976
  }
832
977
 
833
978
  function validate() {
@@ -841,8 +986,12 @@ function validate() {
841
986
  if (state.agentPolicy && typeof state.agentPolicy !== 'object') errors.push('agentPolicy must be object');
842
987
  if (state.answerPolicy && typeof state.answerPolicy !== 'object') errors.push('answerPolicy must be object');
843
988
  if (state.memoryPolicy && typeof state.memoryPolicy !== 'object') errors.push('memoryPolicy must be object');
989
+ if (state.browserPolicy && typeof state.browserPolicy !== 'object') errors.push('browserPolicy must be object');
844
990
  if (state.agentRouter && typeof state.agentRouter !== 'object') errors.push('agentRouter must be object');
845
991
  if (state.partyMode && typeof state.partyMode !== 'object') errors.push('partyMode must be object');
992
+ if (state.autosync?.githubIdentity && typeof state.autosync.githubIdentity !== 'object') {
993
+ errors.push('autosync.githubIdentity must be object');
994
+ }
846
995
  if (state.projectRootPolicy && typeof state.projectRootPolicy !== 'object') errors.push('projectRootPolicy must be object');
847
996
  if (state.projectRootPolicy?.rules && !Array.isArray(state.projectRootPolicy.rules)) {
848
997
  errors.push('projectRootPolicy.rules must be array');
@@ -925,6 +1074,7 @@ function status() {
925
1074
  agentPolicy: state.agentPolicy || {},
926
1075
  answerPolicy: answerPolicy(state),
927
1076
  memoryPolicy: memoryPolicy(state),
1077
+ browserPolicy: browserPolicy(state),
928
1078
  agentRouter: state.agentRouter || {},
929
1079
  partyMode: state.partyMode || {},
930
1080
  workflow: {
@@ -978,6 +1128,7 @@ function main() {
978
1128
  if (command === 'agent' && subcommand === 'require') return agentRequire(args);
979
1129
  if (command === 'agent' && subcommand === 'route') return agentRoute(args);
980
1130
  if (command === 'agent' && subcommand === 'list') return agentList();
1131
+ if (command === 'browser' && subcommand === 'preflight') return browserPreflight();
981
1132
  if (command === 'memory' && subcommand === 'add') return memoryAdd(args);
982
1133
  if (command === 'memory' && subcommand === 'brief') return memoryBrief(args);
983
1134
  if (command === 'memory' && subcommand === 'restore') return memoryRestore(args);
@@ -985,15 +1136,16 @@ function main() {
985
1136
  console.log(`Usage:
986
1137
  node .MOP/scripts/mop-core.mjs status
987
1138
  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]
1139
+ 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
1140
  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]
1141
+ node .MOP/scripts/mop-core.mjs member git-identity --actor CODE --name NAME [--email github-noreply|EMAIL] [--github-username USER]
991
1142
  node .MOP/scripts/mop-core.mjs agent activate --actor CODE --role ROLE --title TITLE --name NAME
992
1143
  node .MOP/scripts/mop-core.mjs agent use --actor CODE --name NAME
993
1144
  node .MOP/scripts/mop-core.mjs agent current --actor CODE
994
1145
  node .MOP/scripts/mop-core.mjs agent require --actor CODE [--role ROLE] [--title TITLE]
995
1146
  node .MOP/scripts/mop-core.mjs agent route --actor CODE --task "task text"
996
1147
  node .MOP/scripts/mop-core.mjs agent list
1148
+ node .MOP/scripts/mop-core.mjs browser preflight
997
1149
  node .MOP/scripts/mop-core.mjs memory brief --actor CODE [--month YYYY-MM]
998
1150
  node .MOP/scripts/mop-core.mjs memory add --actor CODE --kind conversation --summary "what happened"
999
1151
  node .MOP/scripts/mop-core.mjs memory restore --actor CODE`);
package/.agents/AGENTS.md CHANGED
@@ -23,6 +23,14 @@ Before doing anything, read `.MOP/STATE.json` and follow
23
23
  `mop-core.mjs memory add --actor <codename> --kind conversation --summary "<outcome>"`.
24
24
  - If the router says clarification is needed, ask the clarifying questions
25
25
  before implementation.
26
+ - If the router returns `nextAction: "name-required-party-agents"`, ask every
27
+ question in `missingAgentQuestions` and stop until those agents are named.
28
+ - Before browser, scraping, extraction, click automation, login flow,
29
+ bot-detection, or form-filling work, run `mop-core.mjs browser preflight`. If
30
+ it reports Edge, Brave, or Opera, use browser-act `chrome-direct` mode and
31
+ guide the user to start remote debugging (`--remote-debugging-port`). If it
32
+ cannot detect a supported browser, ask which browser they use before doing
33
+ browser work.
26
34
  - If the router activates Party Mode, show visible agent-to-agent dialogue using
27
35
  the exact format in `.MOP/PROTOCOL.md`, with `PARTY MODE` shown in
28
36
  large uppercase before the dialogue. Party Mode normally uses at least 3
@@ -34,6 +42,10 @@ Before doing anything, read `.MOP/STATE.json` and follow
34
42
  - For risky changes, run adversarial review before implementation or merge.
35
43
  - When a role agent first appears, ask the user to name it before using it as a
36
44
  personal agent.
45
+ - Autosycn member commits must use the active member GitHub account. By default,
46
+ MOP derives `ID+USERNAME@users.noreply.github.com` from `gh api user` and
47
+ refuses mismatched GitHub accounts. `BURHAN-MOP` identity is reserved for
48
+ merge guardian commits only.
37
49
 
38
50
  ## Operating Rules
39
51
 
package/AGENTS.md CHANGED
@@ -31,7 +31,16 @@ follow `.MOP/PROTOCOL.md`.
31
31
 
32
32
  - If the router marks the task as ambiguous, the named primary agent must ask
33
33
  clarifying questions before implementation.
34
- - **[browser] Agent Rule**: Before creating a new browser session with `browser-act`, you must check the user's default browser (e.g., using `xdg-settings get default-web-browser` on Linux) or ask them what browser they use (Chrome, Edge, Brave, Opera). If they use something other than the built-in Chrome, use `browser-act`'s `chrome-direct` mode and guide them to start their browser with remote debugging (`--remote-debugging-port`), rather than just creating a default `chrome` browser.
34
+ - If the router returns `nextAction: "name-required-party-agents"`, ask every
35
+ question in `missingAgentQuestions` and stop until those agents are named.
36
+ - **[browser] Agent Rule**: Before browser, scraping, extraction, click
37
+ automation, login flow, bot-detection, or form-filling work, run
38
+ `mop-core.mjs browser preflight`. It scans the default browser when possible.
39
+ If it reports Edge, Brave, or Opera, use browser-act `chrome-direct` mode and
40
+ guide the user to start remote debugging (`--remote-debugging-port`). If it
41
+ cannot detect a supported browser, ask which browser they use before doing
42
+ browser work. Never create a default Chrome session first when the user uses
43
+ another supported browser.
35
44
  - If the router returns `partyMode.active: true`, use Party Mode. Show
36
45
  `PARTY MODE` in large uppercase before the dialogue, then generate the
37
46
  agent-to-agent and agent-to-user dialogue explicitly in your response using this exact format:
@@ -46,8 +55,8 @@ follow `.MOP/PROTOCOL.md`.
46
55
 
47
56
  For `/mop-setup`, ask in order: project name (default folder name), owner display
48
57
  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.
58
+ language, GitHub link, GitHub username, Git commit email (`github-noreply` by
59
+ default), join mode if team, then whether to activate auto-deploy now or later.
51
60
 
52
61
  Agent role/template names are not personal AI names. When a role agent is first
53
62
  needed, ask the user to name it and save it with `mop-core.mjs agent activate`.
@@ -62,6 +71,9 @@ agent route instead of answering invisibly.
62
71
  Default skill: `autosycn`. After meaningful changes, use
63
72
  `.MOP/scripts/mop-autosycn.mjs`. It must commit and merge with the
64
73
  real member Git identity from state, never with the AI tool identity.
74
+ Member commits must use the active member GitHub account; by default MOP derives
75
+ `ID+USERNAME@users.noreply.github.com` from `gh api user` and refuses mismatched
76
+ GitHub accounts. `BURHAN-MOP` identity is only for merge guardian commits.
65
77
  In team mode, run `preflight --actor <codename>` before starting work; work goes
66
78
  to `dev/<codename>`. Every small or large change is pushed there first, then
67
79
  BURHAN-MOP reviews and merges to `main` only when checks pass.
package/CLAUDE.md CHANGED
@@ -23,6 +23,14 @@ follow `.MOP/PROTOCOL.md`.
23
23
  `mop-core.mjs memory add --actor <codename> --kind conversation --summary "<one-line outcome>"`.
24
24
  - If the router marks the task as ambiguous, the named primary agent must ask
25
25
  clarifying questions before implementation.
26
+ - If the router returns `nextAction: "name-required-party-agents"`, ask every
27
+ question in `missingAgentQuestions` and stop until those agents are named.
28
+ - Before browser, scraping, extraction, click automation, login flow,
29
+ bot-detection, or form-filling work, run `mop-core.mjs browser preflight`. If
30
+ it reports Edge, Brave, or Opera, use browser-act `chrome-direct` mode and
31
+ guide the user to start remote debugging (`--remote-debugging-port`). If it
32
+ cannot detect a supported browser, ask which browser they use before doing
33
+ browser work.
26
34
  - If the router returns `partyMode.active: true`, use Party Mode. Show
27
35
  `PARTY MODE` in large uppercase before the dialogue, then show
28
36
  agent-to-agent and agent-to-user dialogue with the exact format from
@@ -47,6 +55,9 @@ agent route instead of answering invisibly.
47
55
  Default skill: `autosycn`. After meaningful changes, use
48
56
  `.MOP/scripts/mop-autosycn.mjs`. It must commit and merge with the
49
57
  real member Git identity from state, never with the AI tool identity.
58
+ Member commits must use the active member GitHub account; by default MOP derives
59
+ `ID+USERNAME@users.noreply.github.com` from `gh api user` and refuses mismatched
60
+ GitHub accounts. `BURHAN-MOP` identity is only for merge guardian commits.
50
61
  In team mode, run `preflight --actor <codename>` before starting work; work goes
51
62
  to `dev/<codename>`. Every small or large change is pushed there first, then
52
63
  BURHAN-MOP reviews and merges to `main` only when checks pass.
package/GEMINI.md CHANGED
@@ -16,6 +16,14 @@ 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
+ - If `agent route` returns `nextAction: "name-required-party-agents"`, ask every
20
+ question in `missingAgentQuestions` and stop until those agents are named.
21
+ - Before browser, scraping, extraction, click automation, login flow,
22
+ bot-detection, or form-filling work, run `mop-core.mjs browser preflight` and
23
+ follow its mode/question before doing browser work.
24
+ - Autosycn member commits must use the active member GitHub account. MOP derives
25
+ `ID+USERNAME@users.noreply.github.com` from `gh api user` by default and
26
+ refuses mismatched GitHub accounts.
19
27
  - Treat the current workspace root as the project root; do not create a nested
20
28
  project wrapper folder for app work unless the user explicitly asks for it.
21
29
  - 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.8`](https://github.com/BURHANDEV-ENTERPRISE/BURHAN-MOP/releases/tag/v0.1.8) |
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.8`](https://github.com/BURHANDEV-ENTERPRISE/BURHAN-MOP/releases/tag/v0.1.8) |
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.8",
4
4
  "description": "MOP portable agent memory core installer and CLI.",
5
5
  "type": "module",
6
6
  "author": "BURHANDEV ENTERPRISE",