@moxxy/cli 0.14.7 → 0.14.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/dist/bin.js CHANGED
@@ -139109,7 +139109,7 @@ function resolveCollabConfig(persisted, overrides) {
139109
139109
  return {
139110
139110
  ...DEFAULT_COLLAB_CONFIG,
139111
139111
  ...stripUndefined(fromPrefs),
139112
- ...stripUndefined({})
139112
+ ...stripUndefined(overrides ?? {})
139113
139113
  };
139114
139114
  }
139115
139115
  function stripUndefined(obj) {
@@ -139292,6 +139292,16 @@ async function detectGit(cwd2) {
139292
139292
  const repo = installed ? await isGitRepo(cwd2) : false;
139293
139293
  return { installed, repo };
139294
139294
  }
139295
+ async function tryInitGitRepo(cwd2) {
139296
+ let r2 = await git(cwd2, ["init", "-b", "main"]);
139297
+ if (r2.code !== 0)
139298
+ r2 = await git(cwd2, ["init"]);
139299
+ if (r2.code !== 0 || !await isGitRepo(cwd2))
139300
+ return false;
139301
+ await git(cwd2, ["add", "-A"]);
139302
+ const c2 = await git(cwd2, [...IDENTITY, "commit", "--allow-empty", "-m", "moxxy-collab: initial snapshot", "--no-verify"]);
139303
+ return c2.code === 0 && (await headSha(cwd2)).length > 0;
139304
+ }
139295
139305
  async function headSha(cwd2) {
139296
139306
  return (await git(cwd2, ["rev-parse", "HEAD"])).stdout.trim();
139297
139307
  }
@@ -139397,6 +139407,19 @@ function peerReaderFor(worktrees, baseSha) {
139397
139407
  }
139398
139408
  };
139399
139409
  }
139410
+ function cwdPeerReader(cwd2) {
139411
+ return {
139412
+ async files() {
139413
+ return await isGitRepo(cwd2) ? changedFiles(cwd2) : [];
139414
+ },
139415
+ async read(_agentId, path62) {
139416
+ return readFile(resolveWithin(cwd2, path62), "utf8");
139417
+ },
139418
+ async diff() {
139419
+ return "";
139420
+ }
139421
+ };
139422
+ }
139400
139423
  var FORCE_KILL_GRACE_MS = 4e3;
139401
139424
  var STDERR_RING = 40;
139402
139425
  var PeerSupervisor = class {
@@ -139636,7 +139659,7 @@ async function* runCollaborative(ctx, deps) {
139636
139659
  yield await ctx.emit(emitAbort(ctx, "aborted before collaboration start"));
139637
139660
  return;
139638
139661
  }
139639
- const cfg = deps.config ?? resolveCollabConfig();
139662
+ const cfg = deps.config ?? resolveCollabConfig(void 0, deps.concurrencyOverride ? { concurrency: deps.concurrencyOverride } : void 0);
139640
139663
  const cwd2 = deps.cwd ?? process.cwd();
139641
139664
  const task = lastUserPromptText(ctx) ?? "";
139642
139665
  if (!task.trim()) {
@@ -139662,17 +139685,22 @@ async function* runCollaborative(ctx, deps) {
139662
139685
  let completed = false;
139663
139686
  try {
139664
139687
  mkdirSync(collabRunDir(runId), { recursive: true });
139665
- const { installed: gitInstalled2, repo: gitRepo } = await detectGit(cwd2);
139666
- const parallel = cfg.concurrency === "parallel" && gitRepo;
139667
- archiveParallel = parallel;
139688
+ const { installed: gitInstalled2, repo: alreadyGitRepo } = await (deps.detectGit ?? detectGit)(cwd2);
139689
+ let gitRepo = alreadyGitRepo;
139690
+ if (!gitRepo && gitInstalled2 && cfg.concurrency !== "sequential") {
139691
+ gitRepo = await tryInitGitRepo(cwd2).catch(() => false);
139692
+ }
139693
+ const execMode = resolveExecMode(cfg, gitRepo);
139694
+ const usesGit = execMode === "git-parallel";
139695
+ const runsParallel = execMode !== "sequential";
139696
+ archiveParallel = runsParallel;
139668
139697
  archiveGitRepo = gitRepo;
139669
- if (!parallel) {
139670
- yield await ctx.emit(plugin4(ctx, "collab_fallback_sequential", {
139671
- reason: !gitInstalled2 ? "git is not installed \u2014 running agents sequentially in your workspace" : !gitRepo ? "this folder is not a git repository \u2014 running agents sequentially in your workspace" : "sequential mode selected"
139672
- }));
139698
+ yield await ctx.emit(plugin4(ctx, "collab_exec_mode", { mode: execMode, gitInstalled: gitInstalled2, gitRepo }));
139699
+ if (execMode === "sequential") {
139700
+ yield await ctx.emit(plugin4(ctx, "collab_fallback_sequential", { reason: "sequential mode \u2014 one agent at a time" }));
139673
139701
  }
139674
139702
  let baseSha = "";
139675
- if (parallel) {
139703
+ if (usesGit) {
139676
139704
  const base2 = await resolveBase(cwd2, { snapshotDirty: true });
139677
139705
  baseSha = base2.baseSha;
139678
139706
  }
@@ -139686,7 +139714,9 @@ async function* runCollaborative(ctx, deps) {
139686
139714
  socketPath: hubSocketPath(runId),
139687
139715
  task,
139688
139716
  roster: [architectEntry],
139689
- peerReader: peerReaderFor(worktrees, baseSha)
139717
+ // git: read each agent's worktree. non-git: every agent shares one tree, so
139718
+ // peer-read is just "read the live shared workspace".
139719
+ peerReader: usesGit ? peerReaderFor(worktrees, baseSha) : cwdPeerReader(cwd2)
139690
139720
  });
139691
139721
  registerActiveHub(String(ctx.sessionId), hub);
139692
139722
  unsubscribe = hub.subscribe((e3) => {
@@ -139701,7 +139731,7 @@ async function* runCollaborative(ctx, deps) {
139701
139731
  signal: ctx.signal
139702
139732
  };
139703
139733
  supervisor = (deps.createSupervisor ?? ((o2) => new PeerSupervisor(o2)))(supervisorOpts, hub);
139704
- yield await ctx.emit(plugin4(ctx, "collab_started", { task, parallel, gitInstalled: gitInstalled2, gitRepo }));
139734
+ yield await ctx.emit(plugin4(ctx, "collab_started", { task, parallel: runsParallel, execMode, gitInstalled: gitInstalled2, gitRepo }));
139705
139735
  try {
139706
139736
  const events = ctx.log.slice();
139707
139737
  mkdirSync(join(cwd2, COLLAB_SCAFFOLD_DIR), { recursive: true });
@@ -139766,13 +139796,13 @@ ${why}` : ""}`));
139766
139796
  }
139767
139797
  }
139768
139798
  yield await ctx.emit(plugin4(ctx, "collab_roster_confirmed", { roster }));
139769
- if (parallel) {
139799
+ if (usesGit) {
139770
139800
  await commitAll(cwd2, "moxxy-collab: scaffold contracts");
139771
139801
  baseSha = await headSha(cwd2);
139772
139802
  mkdirSync(worktreeRoot(runId), { recursive: true });
139773
139803
  }
139774
139804
  const doneIds = [];
139775
- if (parallel) {
139805
+ if (execMode === "git-parallel") {
139776
139806
  for (const entry of roster) {
139777
139807
  hub.state.addAgent(entry);
139778
139808
  const wt3 = worktreePath(runId, entry.id);
@@ -139787,6 +139817,24 @@ ${why}` : ""}`));
139787
139817
  for (const r2 of roster)
139788
139818
  if (statusOf(hub, r2.id) === "done")
139789
139819
  doneIds.push(r2.id);
139820
+ } else if (execMode === "cwd-parallel") {
139821
+ for (const entry of roster) {
139822
+ hub.state.addAgent(entry);
139823
+ if (entry.ownedPaths?.length) {
139824
+ const res = hub.state.boardClaim(entry.id, entry.ownedPaths);
139825
+ if (!res.ok) {
139826
+ yield await ctx.emit(plugin4(ctx, "collab_ownership_overlap", { id: entry.id, paths: entry.ownedPaths, ownedBy: res.ownedBy }));
139827
+ }
139828
+ }
139829
+ const charterFile = writeCharterFile(runId, entry);
139830
+ supervisor.spawn({ entry, cwd: cwd2, mode: COLLAB_PEER_MODE_NAME, ...charterFile ? { charterFile } : {} });
139831
+ yield await ctx.emit(plugin4(ctx, "collab_agent_spawned", { id: entry.id, role: entry.role }));
139832
+ }
139833
+ await waitForAgents(hub, supervisor, roster.map((r2) => r2.id), ctx.signal, cfg.wallClockMs);
139834
+ yield* surfaceFailures(ctx, hub, supervisor, roster.map((r2) => r2.id));
139835
+ for (const r2 of roster)
139836
+ if (statusOf(hub, r2.id) === "done")
139837
+ doneIds.push(r2.id);
139790
139838
  } else {
139791
139839
  for (const entry of roster) {
139792
139840
  if (ctx.signal.aborted)
@@ -139808,7 +139856,7 @@ ${why}` : ""}`));
139808
139856
  return;
139809
139857
  }
139810
139858
  let mergeNote = "";
139811
- if (parallel && doneIds.length > 0) {
139859
+ if (usesGit && doneIds.length > 0) {
139812
139860
  const result = await integrate({
139813
139861
  repoCwd: cwd2,
139814
139862
  runId,
@@ -139832,11 +139880,13 @@ ${why}` : ""}`));
139832
139880
  }
139833
139881
  const summaries = hub.state.doneSummaries();
139834
139882
  const summaryBlock = summaries.length ? summaries.map((s2) => `- **${s2.agentId}**: ${s2.summary}`).join("\n") : "(no agent reported a completion summary)";
139883
+ const cwdNote = execMode === "cwd-parallel" ? "The team worked together directly in your workspace; please review the result." : "";
139884
+ const tail = [mergeNote, cwdNote].filter(Boolean).join("\n\n");
139835
139885
  yield await ctx.emit(assistant(ctx, `Collaboration complete \u2014 ${doneIds.length}/${roster.length} agents finished.
139836
139886
 
139837
- ${summaryBlock}${mergeNote ? `
139887
+ ${summaryBlock}${tail ? `
139838
139888
 
139839
- ${mergeNote}` : ""}`));
139889
+ ${tail}` : ""}`));
139840
139890
  completed = true;
139841
139891
  yield await ctx.emit(plugin4(ctx, "collab_completed", { done: doneIds, total: roster.length }));
139842
139892
  } finally {
@@ -139951,6 +140001,11 @@ function readRoster(path62, maxAgents) {
139951
140001
  function slug(s2) {
139952
140002
  return s2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 32);
139953
140003
  }
140004
+ function resolveExecMode(cfg, gitRepo) {
140005
+ if (cfg.concurrency === "sequential")
140006
+ return "sequential";
140007
+ return gitRepo ? "git-parallel" : "cwd-parallel";
140008
+ }
139954
140009
  function cleanRole(raw) {
139955
140010
  if (typeof raw !== "string")
139956
140011
  return "implementer";
@@ -140108,12 +140163,13 @@ The team coordinates through a shared hub (use these tools):
140108
140163
  - collab_send / collab_broadcast \u2014 message a teammate by id, or the whole team.
140109
140164
  - collab_board / collab_add_task / collab_update \u2014 the shared task board (what's done / in progress / blocked).
140110
140165
  - collab_contracts \u2014 the agreed interfaces/boundaries you must build to.
140111
- - collab_claim(paths) \u2014 claim files BEFORE editing them. If the claim is rejected, another agent owns them: message that owner and coordinate; do NOT edit files you don't own.
140112
- - collab_release \u2014 release a claim when you're done with those files.
140113
- - collab_peer_read / collab_peer_files / collab_peer_diff \u2014 read a teammate's ACTUAL in-progress work (get their real interface instead of guessing).
140166
+ - collab_claim(paths) \u2014 you may SHARE ONE LIVE WORKSPACE with the other agents (no isolation), so you MUST collab_claim a file BEFORE every edit and only edit files you currently own. If a claim is REJECTED another agent owns it: collab_send that owner, agree who edits, and do NOT touch it until you own it. Claim the NARROWEST set you need (single files, not whole dirs) so you don't block teammates. If you RENAME or MOVE a file, claim BOTH the old and the new path first.
140167
+ - collab_release \u2014 release a claim the moment you finish a file so others can proceed.
140168
+ - collab_peer_read / collab_peer_files / collab_peer_diff \u2014 read a teammate's ACTUAL in-progress work (get their real interface instead of guessing). Only rely on work a teammate has said is done \u2014 a file may be mid-write.
140114
140169
 
140115
140170
  Cooperation rules:
140116
140171
  - Build strictly to the shared contracts. If you must change a shared boundary, use collab_contract_propose_change and wait for acks \u2014 never break a contract unilaterally.
140172
+ - Shared/aggregator files (package.json, a lockfile, a barrel index, a shared types file) have ONE owner \u2014 never edit one you don't own; ask its owner via collab_send to make the change for you.
140117
140173
  - The human may step in at any time. When you receive a HUMAN directive or a message from "human", treat it as authoritative (it overrides your current plan), and REPLY to them with collab_send to "human" \u2014 acknowledge it and say what you'll do (or ask a brief clarifying question). Don't go silent on the human. If the team is paused, finish your current edit and wait.
140118
140174
  - Keep teammates informed: broadcast meaningful progress and blockers.
140119
140175
  - When YOUR sub-task is fully complete and verified, call collab_done with a short summary. The run finishes when everyone is done.`;
@@ -140125,7 +140181,7 @@ var COLLAB_ARCHITECT_PROMPT = `${COLLAB_COMMON}
140125
140181
  You are the ARCHITECT \u2014 you run FIRST and set the team up for success. Your job, in order:
140126
140182
  0. Read ${COLLAB_SCAFFOLD_DIR}/${BRIEF_FILENAME} \u2014 the goal + key-requirements summary \u2014 so you decompose toward what the user actually wants. If you need a detail it omits, grep ${COLLAB_SCAFFOLD_DIR}/${CONVERSATION_FILENAME}.
140127
140183
  1. Explore the workspace to understand the task and its boundaries.
140128
- 2. Assemble the RIGHT TEAM for THIS deliverable and decompose into INDEPENDENT sub-tasks with DISJOINT ownership (minimize overlap). Pick the roles the work actually needs \u2014 a coding task wants developers + a QA reviewer; a document/plan/design deliverable wants a writer, a researcher, a designer, an editor; a product effort may want a PM to sequence + verify. Don't default everyone to a generic "implementer".
140184
+ 2. Assemble the RIGHT TEAM for THIS deliverable and decompose into INDEPENDENT sub-tasks with DISJOINT ownership (minimize overlap). Pick the roles the work actually needs \u2014 a coding task wants developers + a QA reviewer; a document/plan/design deliverable wants a writer, a researcher, a designer, an editor; a product effort may want a PM to sequence + verify. Don't default everyone to a generic "implementer". The team may run together in ONE shared workspace with only advisory file locks, so ownedPaths MUST be DISJOINT \u2014 give each agent its own specific files/leaf dirs with NO overlap. If two roles would both need one shared file (a router, a barrel index, package.json), give it to ONE owner and have the other coordinate via collab_send or a contract; never assign the same path to two agents. Every roster entry MUST declare ownedPaths.
140129
140185
  3. Define the shared CONTRACTS \u2014 the interfaces, types, API shapes, section outlines, or boundaries where the team's work meets. Publish each with collab_contract_publish (give an owner + consumers).
140130
140186
  4. Write two files into the repo:
140131
140187
  - ${COLLAB_SCAFFOLD_DIR}/${CONTRACTS_FILENAME} \u2014 human-readable contracts/boundaries the team must follow.
@@ -156299,7 +156355,7 @@ async function runAgentCommand(argv) {
156299
156355
  }
156300
156356
  function buildSeedTurn(args) {
156301
156357
  const { role, parentTask, subtask } = args;
156302
- const pointer = "Shared team context is in `.moxxy-collab/BRIEF.md` (a concise summary of the user's goal + key requirements) and `.moxxy-collab/CONTRACTS.md` (the agreed interfaces). Read them before you start so your work fits the real goal. If you need a detail the brief omits, read or grep `.moxxy-collab/CONVERSATION.md` (the full transcript) \u2014 do not load it wholesale.";
156358
+ const pointer = "Shared team context is in `.moxxy-collab/BRIEF.md` (a concise summary of the user's goal + key requirements) and `.moxxy-collab/CONTRACTS.md` (the agreed interfaces). Read them before you start so your work fits the real goal. If you need a detail the brief omits, read or grep `.moxxy-collab/CONVERSATION.md` (the full transcript) \u2014 do not load it wholesale. You may share ONE live workspace with the other agents (no isolation) \u2014 collab_claim before every edit, edit only what you own, and release when done.";
156303
156359
  if (role === "architect" || !parentTask || parentTask === subtask) {
156304
156360
  return subtask ? `${subtask}
156305
156361