@moxxy/cli 0.14.4 → 0.14.6

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
@@ -2,7 +2,7 @@
2
2
  import { createRequire } from 'node:module';
3
3
  import { z as z$1, createMutex, defineTunnelProvider, definePlugin, defineProvider, defineTool, MoxxyError, asTurnId, defineMode, asPluginId, defineCommand, defineChannel, defineWorkflowExecutor, toFriendlyError, estimateTextTokens, classifyHttpStatus, createStuckLoopDetector, runCompactionIfNeeded, runElisionIfNeeded, collectProviderStream, usageEventFields, isContextOverflowError, emitRequestsAndDetectStuck, executeToolUses, buildSystemPromptWithSkills, projectMessages, defineCompactor, defineCacheStrategy, denyByDefaultResolver, createAllowListResolver, zodToJsonSchema, fileDiffSummary, runSingleShotTurn, defineSurface, runManualCompaction, isFileDiffDisplay, renderFrontmatter, defineEmbedder, migrateModeName, skillFrontmatterSchema, asSkillId, startChannelWith, parseFrontmatterFile, createDeferredPermissionResolver, getInstallHint, defineTranscriber, summarizeTokensByModel, countNodes, moxxyPackageSchema, encodeLoginPrompt, classifyNetworkError, addModelTotals, createJsonFileStore, ISOLATION_RANK, MOXXY_PCM16_24KHZ_MIME, fileDiffVerb, parseFrontmatter, createCallbackResolver, autoAllowResolver, asSessionId, asToolCallId, defineViewRenderer, DEFAULT_VIEW_TAGS, isSafeViewUrl, evaluateToolRule, summarizeSessionTokensFromEvents, toDiffRows, diffGutterNo, computeElisionState, toolResultStubbed, toolResultStub, toolResultBytes, conversationalStubbed, conversationalStub, asEventId } from '@moxxy/sdk';
4
4
  import * as fs32 from 'fs';
5
- import fs32__default, { existsSync, promises, ReadStream, mkdirSync, rmSync, statSync, readdirSync, writeFileSync, readFileSync, unlinkSync, chmodSync, watch, createReadStream } from 'fs';
5
+ import fs32__default, { existsSync, promises, ReadStream, mkdirSync, writeFileSync, rmSync, statSync, readdirSync, readFileSync, unlinkSync, chmodSync, watch, createReadStream } from 'fs';
6
6
  import * as path3 from 'path';
7
7
  import path3__default, { join, dirname, resolve, relative, isAbsolute, basename } from 'path';
8
8
  import { isCliTunnelAvailable, writeFileAtomic, spawnCliTunnel, moxxyPath, moxxyHome, bearerTokenMatches, resolveChannelToken, rotateChannelToken, readRequestBody, MOXXY_WS_SUBPROTOCOL, bearerGuard, tokenFromWsProtocolHeader, writeFileAtomicSync } from '@moxxy/sdk/server';
@@ -81553,10 +81553,10 @@ var init_output = __esm({
81553
81553
  transformers
81554
81554
  });
81555
81555
  }
81556
- clip(clip) {
81556
+ clip(clip2) {
81557
81557
  this.operations.push({
81558
81558
  type: "clip",
81559
- clip
81559
+ clip: clip2
81560
81560
  });
81561
81561
  }
81562
81562
  unclip() {
@@ -81590,40 +81590,40 @@ var init_output = __esm({
81590
81590
  const { text, transformers } = operation;
81591
81591
  let { x: x4, y: y2 } = operation;
81592
81592
  let lines = text.split("\n");
81593
- const clip = clips.at(-1);
81594
- if (clip) {
81595
- const clipHorizontally = typeof clip?.x1 === "number" && typeof clip?.x2 === "number";
81596
- const clipVertically = typeof clip?.y1 === "number" && typeof clip?.y2 === "number";
81593
+ const clip2 = clips.at(-1);
81594
+ if (clip2) {
81595
+ const clipHorizontally = typeof clip2?.x1 === "number" && typeof clip2?.x2 === "number";
81596
+ const clipVertically = typeof clip2?.y1 === "number" && typeof clip2?.y2 === "number";
81597
81597
  if (clipHorizontally) {
81598
81598
  const width = widestLine(text);
81599
- if (x4 + width < clip.x1 || x4 > clip.x2) {
81599
+ if (x4 + width < clip2.x1 || x4 > clip2.x2) {
81600
81600
  continue;
81601
81601
  }
81602
81602
  }
81603
81603
  if (clipVertically) {
81604
81604
  const height = lines.length;
81605
- if (y2 + height < clip.y1 || y2 > clip.y2) {
81605
+ if (y2 + height < clip2.y1 || y2 > clip2.y2) {
81606
81606
  continue;
81607
81607
  }
81608
81608
  }
81609
81609
  if (clipHorizontally) {
81610
81610
  lines = lines.map((line) => {
81611
- const from = x4 < clip.x1 ? clip.x1 - x4 : 0;
81611
+ const from = x4 < clip2.x1 ? clip2.x1 - x4 : 0;
81612
81612
  const width = stringWidth(line);
81613
- const to = x4 + width > clip.x2 ? clip.x2 - x4 : width;
81613
+ const to = x4 + width > clip2.x2 ? clip2.x2 - x4 : width;
81614
81614
  return sliceAnsi2(line, from, to);
81615
81615
  });
81616
- if (x4 < clip.x1) {
81617
- x4 = clip.x1;
81616
+ if (x4 < clip2.x1) {
81617
+ x4 = clip2.x1;
81618
81618
  }
81619
81619
  }
81620
81620
  if (clipVertically) {
81621
- const from = y2 < clip.y1 ? clip.y1 - y2 : 0;
81621
+ const from = y2 < clip2.y1 ? clip2.y1 - y2 : 0;
81622
81622
  const height = lines.length;
81623
- const to = y2 + height > clip.y2 ? clip.y2 - y2 : height;
81623
+ const to = y2 + height > clip2.y2 ? clip2.y2 - y2 : height;
81624
81624
  lines = lines.slice(from, to);
81625
- if (y2 < clip.y1) {
81626
- y2 = clip.y1;
81625
+ if (y2 < clip2.y1) {
81626
+ y2 = clip2.y1;
81627
81627
  }
81628
81628
  }
81629
81629
  }
@@ -85713,17 +85713,25 @@ function handleCollabEvent(e3, ref, root) {
85713
85713
  if (typeof item.id === "string") {
85714
85714
  const existing = block.tasks.find((x4) => x4.id === item.id);
85715
85715
  const owner = typeof item.owner === "string" ? item.owner : null;
85716
+ const paths = Array.isArray(item.paths) ? item.paths.filter((x4) => typeof x4 === "string") : void 0;
85717
+ const detail = typeof item.detail === "string" ? item.detail : void 0;
85716
85718
  if (!existing) {
85717
85719
  block.tasks.push({
85718
85720
  id: item.id,
85719
85721
  title: String(item.title ?? ""),
85720
85722
  status: String(item.status ?? "open"),
85721
- owner
85723
+ owner,
85724
+ ...paths && paths.length > 0 ? { paths } : {},
85725
+ ...detail ? { detail } : {}
85722
85726
  });
85723
85727
  } else {
85724
85728
  existing.title = String(item.title ?? existing.title);
85725
85729
  existing.status = String(item.status ?? existing.status);
85726
85730
  existing.owner = owner ?? existing.owner;
85731
+ if (paths && paths.length > 0)
85732
+ existing.paths = paths;
85733
+ if (detail)
85734
+ existing.detail = detail;
85727
85735
  }
85728
85736
  }
85729
85737
  break;
@@ -136051,6 +136059,7 @@ var ARCHITECT_AGENT_ID = "architect";
136051
136059
  var COLLAB_SCAFFOLD_DIR = ".moxxy-collab";
136052
136060
  var CONTRACTS_FILENAME = "CONTRACTS.md";
136053
136061
  var ROSTER_FILENAME = "roster.json";
136062
+ var BRIEF_FILENAME = "BRIEF.md";
136054
136063
  function collabRunId(sessionId, turnId) {
136055
136064
  const tail = (s2) => s2.replace(/[^a-zA-Z0-9]/g, "").slice(-6) || "x";
136056
136065
  return `${tail(sessionId)}-${tail(turnId)}`;
@@ -139100,6 +139109,68 @@ function resolveCollabConfig(persisted, overrides) {
139100
139109
  function stripUndefined(obj) {
139101
139110
  return Object.fromEntries(Object.entries(obj).filter(([, v3]) => v3 !== void 0));
139102
139111
  }
139112
+ var MAX_MSG_CHARS = 600;
139113
+ var MAX_TOTAL_CHARS = 6e3;
139114
+ function clip(s2, n2) {
139115
+ const t2 = s2.trim().replace(/\s+/g, " ");
139116
+ return t2.length > n2 ? `${t2.slice(0, n2 - 1)}\u2026` : t2;
139117
+ }
139118
+ function digestTurns(events) {
139119
+ const out = [];
139120
+ for (const raw of events) {
139121
+ const e3 = raw;
139122
+ if (e3.type === "user_prompt" && typeof e3.text === "string" && e3.text.trim()) {
139123
+ out.push({ role: "user", text: e3.text });
139124
+ } else if (e3.type === "assistant_message" && e3.source === "model" && typeof e3.content === "string" && e3.content.trim()) {
139125
+ out.push({ role: "assistant", text: e3.content });
139126
+ }
139127
+ }
139128
+ return out;
139129
+ }
139130
+ function buildBrief(task, events) {
139131
+ const turns = digestTurns(events);
139132
+ const trimmed = turns.length > 0 && turns[turns.length - 1].role === "user" && turns[turns.length - 1].text.trim() === task.trim() ? turns.slice(0, -1) : turns;
139133
+ const recent = trimmed.slice(-12);
139134
+ const lines = [
139135
+ "# Collaboration brief",
139136
+ "",
139137
+ "This is the shared context for the whole team. It is the user's goal and the",
139138
+ "conversation that led to it \u2014 read it before planning so your work fits the",
139139
+ "real intent, not just your narrow sub-task.",
139140
+ "",
139141
+ "## Goal",
139142
+ "",
139143
+ clip(task, 1500) || "(no goal text)"
139144
+ ];
139145
+ if (recent.length > 0) {
139146
+ lines.push("", "## Conversation so far", "");
139147
+ for (const t2 of recent) {
139148
+ const who = t2.role === "user" ? "User" : "Assistant";
139149
+ lines.push(`- **${who}:** ${clip(t2.text, MAX_MSG_CHARS)}`);
139150
+ }
139151
+ }
139152
+ let brief = lines.join("\n");
139153
+ if (brief.length > MAX_TOTAL_CHARS) {
139154
+ brief = `${brief.slice(0, MAX_TOTAL_CHARS - 1)}\u2026`;
139155
+ }
139156
+ return `${brief}
139157
+ `;
139158
+ }
139159
+ function moxxyHome2() {
139160
+ return process.env.MOXXY_HOME ?? join(homedir(), ".moxxy");
139161
+ }
139162
+ function collabRunsDir() {
139163
+ return join(moxxyHome2(), "collab", "runs");
139164
+ }
139165
+ function writeRunRecord(rec) {
139166
+ try {
139167
+ const dir = collabRunsDir();
139168
+ mkdirSync(dir, { recursive: true });
139169
+ writeFileSync(join(dir, `${rec.runId}.json`), `${JSON.stringify(rec, null, 2)}
139170
+ `);
139171
+ } catch {
139172
+ }
139173
+ }
139103
139174
  var IDENTITY = ["-c", "user.name=moxxy-collab", "-c", "user.email=collab@moxxy.local"];
139104
139175
  function git(cwd2, args) {
139105
139176
  return new Promise((resolve13) => {
@@ -139478,14 +139549,22 @@ async function* runCollaborative(ctx, deps) {
139478
139549
  return;
139479
139550
  }
139480
139551
  const runId = collabRunId(String(ctx.sessionId), String(ctx.turnId));
139552
+ const startedAtMs = Date.now();
139481
139553
  const worktrees = /* @__PURE__ */ new Map();
139482
139554
  let hub = null;
139483
139555
  let supervisor = null;
139484
139556
  let unsubscribe = null;
139557
+ let archiveParallel = false;
139558
+ let archiveGitRepo = false;
139559
+ let briefText = "";
139560
+ let mergeForArchive;
139561
+ let completed = false;
139485
139562
  try {
139486
139563
  mkdirSync(collabRunDir(runId), { recursive: true });
139487
139564
  const { installed: gitInstalled2, repo: gitRepo } = await detectGit(cwd2);
139488
139565
  const parallel = cfg.concurrency === "parallel" && gitRepo;
139566
+ archiveParallel = parallel;
139567
+ archiveGitRepo = gitRepo;
139489
139568
  if (!parallel) {
139490
139569
  yield await ctx.emit(plugin4(ctx, "collab_fallback_sequential", {
139491
139570
  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"
@@ -139522,6 +139601,13 @@ async function* runCollaborative(ctx, deps) {
139522
139601
  };
139523
139602
  supervisor = (deps.createSupervisor ?? ((o2) => new PeerSupervisor(o2)))(supervisorOpts, hub);
139524
139603
  yield await ctx.emit(plugin4(ctx, "collab_started", { task, parallel, gitInstalled: gitInstalled2, gitRepo }));
139604
+ try {
139605
+ briefText = buildBrief(task, ctx.log.slice());
139606
+ mkdirSync(join(cwd2, COLLAB_SCAFFOLD_DIR), { recursive: true });
139607
+ writeFileSync(join(cwd2, COLLAB_SCAFFOLD_DIR, BRIEF_FILENAME), briefText);
139608
+ yield await ctx.emit(plugin4(ctx, "collab_brief_written", { path: join(COLLAB_SCAFFOLD_DIR, BRIEF_FILENAME) }));
139609
+ } catch {
139610
+ }
139525
139611
  supervisor.spawn({ entry: architectEntry, cwd: cwd2, mode: COLLAB_ARCHITECT_MODE_NAME });
139526
139612
  yield await ctx.emit(plugin4(ctx, "collab_agent_spawned", { id: ARCHITECT_AGENT_ID, role: "architect" }));
139527
139613
  const architectOk = await waitForAgent(hub, supervisor, ARCHITECT_AGENT_ID, ctx.signal, cfg.wallClockMs);
@@ -139611,6 +139697,12 @@ ${why}` : ""}`));
139611
139697
  board: hub.state.boardItems(),
139612
139698
  mergePolicy: cfg.mergePolicy
139613
139699
  });
139700
+ mergeForArchive = {
139701
+ merged: result.merged,
139702
+ promoted: result.promoted,
139703
+ conflicts: result.conflicts.length,
139704
+ ...result.stagingBranch ? { stagingBranch: result.stagingBranch } : {}
139705
+ };
139614
139706
  yield await ctx.emit(plugin4(ctx, "collab_merge", result));
139615
139707
  for (const c2 of result.conflicts) {
139616
139708
  yield await ctx.emit(plugin4(ctx, "collab_conflict", c2));
@@ -139624,10 +139716,54 @@ ${why}` : ""}`));
139624
139716
  ${summaryBlock}${mergeNote ? `
139625
139717
 
139626
139718
  ${mergeNote}` : ""}`));
139719
+ completed = true;
139627
139720
  yield await ctx.emit(plugin4(ctx, "collab_completed", { done: doneIds, total: roster.length }));
139628
139721
  } finally {
139629
139722
  if (supervisor)
139630
139723
  await supervisor.shutdownAll("collaboration complete");
139724
+ if (hub) {
139725
+ try {
139726
+ const agents = hub.state.rosterView().agents;
139727
+ const implementers = agents.filter((a2) => a2.role !== "architect");
139728
+ writeRunRecord({
139729
+ runId,
139730
+ task,
139731
+ startedAtMs,
139732
+ finishedAtMs: Date.now(),
139733
+ outcome: completed ? "completed" : ctx.signal.aborted ? "aborted" : "failed",
139734
+ parallel: archiveParallel,
139735
+ gitRepo: archiveGitRepo,
139736
+ agents: agents.map((a2) => ({
139737
+ id: a2.id,
139738
+ name: a2.name,
139739
+ role: a2.role,
139740
+ status: a2.status,
139741
+ subtask: a2.subtask,
139742
+ ...a2.doneSummary ? { doneSummary: a2.doneSummary } : {}
139743
+ })),
139744
+ doneCount: implementers.filter((a2) => a2.status === "done").length,
139745
+ totalCount: implementers.length,
139746
+ board: hub.state.boardItems().map((b3) => ({
139747
+ id: b3.id,
139748
+ title: b3.title,
139749
+ status: b3.status,
139750
+ ...b3.owner ? { owner: b3.owner } : {},
139751
+ ...b3.paths ? { paths: b3.paths } : {}
139752
+ })),
139753
+ contracts: hub.state.contractList().map((c2) => ({
139754
+ id: c2.id,
139755
+ title: c2.title,
139756
+ owner: c2.owner,
139757
+ status: c2.status,
139758
+ version: c2.version
139759
+ })),
139760
+ messageCount: hub.state.allMessages().length,
139761
+ ...mergeForArchive ? { merge: mergeForArchive } : {},
139762
+ ...briefText ? { brief: briefText } : {}
139763
+ });
139764
+ } catch {
139765
+ }
139766
+ }
139631
139767
  if (unsubscribe)
139632
139768
  unsubscribe();
139633
139769
  unregisterActiveHub(String(ctx.sessionId));
@@ -139675,7 +139811,9 @@ function readRoster(path62, maxAgents) {
139675
139811
  out.push({
139676
139812
  id,
139677
139813
  name: typeof r2.name === "string" ? r2.name : id,
139678
- role: "implementer",
139814
+ // Carry the architect's proposed role (pm/designer/developer/qa/writer/…)
139815
+ // so the team is cross-functional, not a pool of identical implementers.
139816
+ role: cleanRole(r2.role),
139679
139817
  subtask: r2.subtask,
139680
139818
  ...Array.isArray(r2.ownedPaths) ? { ownedPaths: r2.ownedPaths.filter((p3) => typeof p3 === "string") } : {},
139681
139819
  ...typeof r2.model === "string" ? { model: r2.model } : {}
@@ -139691,6 +139829,14 @@ function readRoster(path62, maxAgents) {
139691
139829
  function slug(s2) {
139692
139830
  return s2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 32);
139693
139831
  }
139832
+ function cleanRole(raw) {
139833
+ if (typeof raw !== "string")
139834
+ return "implementer";
139835
+ const r2 = raw.toLowerCase().replace(/[^a-z0-9 -]/g, "").replace(/\s+/g, " ").trim().slice(0, 24);
139836
+ if (!r2 || r2 === ARCHITECT_AGENT_ID)
139837
+ return "implementer";
139838
+ return r2;
139839
+ }
139694
139840
  function statusOf(hub, id) {
139695
139841
  return hub.state.rosterView().agents.find((a2) => a2.id === id)?.status;
139696
139842
  }
@@ -139813,7 +139959,11 @@ function emitAbort(ctx, reason) {
139813
139959
  }
139814
139960
 
139815
139961
  // ../mode-collaborative/dist/prompts.js
139816
- var COLLAB_COMMON = `You are one agent on a TEAM of separate agents collaborating on one task in a shared codebase. You are a peer, not in charge \u2014 you cooperate.
139962
+ var COLLAB_COMMON = `You are one agent on a TEAM of separate agents collaborating on one task in a shared workspace. You are a peer, not in charge \u2014 you cooperate.
139963
+
139964
+ Know the WHOLE picture before you act:
139965
+ - ${COLLAB_SCAFFOLD_DIR}/${BRIEF_FILENAME} is the shared brief \u2014 the user's overall goal and the conversation/intent behind it. Read it FIRST so your work serves the real goal, not just the literal words of your sub-task.
139966
+ - Before planning, recall() any relevant prior knowledge about this workspace/task. When you discover a durable fact (a decision, a gotcha, an interface, a convention), memory_save it so the team \u2014 and future work \u2014 keeps it.
139817
139967
 
139818
139968
  The team coordinates through a shared hub (use these tools):
139819
139969
  - collab_roster \u2014 who is on the team, their roles, sub-tasks, and status.
@@ -139827,21 +139977,22 @@ The team coordinates through a shared hub (use these tools):
139827
139977
 
139828
139978
  Cooperation rules:
139829
139979
  - 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.
139830
- - The human may step in at any time: honor any directive you receive (it overrides your current plan), and if the team is paused, finish your current edit and wait.
139980
+ - 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.
139831
139981
  - Keep teammates informed: broadcast meaningful progress and blockers.
139832
139982
  - When YOUR sub-task is fully complete and verified, call collab_done with a short summary. The run finishes when everyone is done.`;
139833
139983
  var COLLAB_PEER_PROMPT = `${COLLAB_COMMON}
139834
139984
 
139835
- You are an IMPLEMENTER. Your sub-task is provided. Start by reading ${COLLAB_SCAFFOLD_DIR}/${CONTRACTS_FILENAME} and calling collab_contracts, collab_roster, and collab_board so you know the plan and who owns what. Claim your files, implement against the contracts, coordinate on intersections, then call collab_done.`;
139985
+ You are a TEAM MEMBER with a specific role (given below) and sub-task. Work as that role \u2014 a writer writes, a designer designs, a developer builds, a QA reviews, a PM sequences + verifies. Start by reading ${COLLAB_SCAFFOLD_DIR}/${BRIEF_FILENAME} (the goal + intent) and ${COLLAB_SCAFFOLD_DIR}/${CONTRACTS_FILENAME}, and calling collab_contracts, collab_roster, and collab_board so you know the plan and who owns what. Claim your files, deliver your part against the contracts, coordinate on intersections, then call collab_done.`;
139836
139986
  var COLLAB_ARCHITECT_PROMPT = `${COLLAB_COMMON}
139837
139987
 
139838
139988
  You are the ARCHITECT \u2014 you run FIRST and set the team up for success. Your job, in order:
139839
- 1. Explore the codebase to understand the task and its boundaries.
139840
- 2. Decompose the task into INDEPENDENT sub-tasks with DISJOINT file ownership (minimize overlap so agents rarely touch the same files).
139841
- 3. Define the shared CONTRACTS \u2014 the interfaces, types, API shapes, and module boundaries where the implementers' work meets. Publish each with collab_contract_publish (give an owner + consumers).
139989
+ 0. Read ${COLLAB_SCAFFOLD_DIR}/${BRIEF_FILENAME} \u2014 the user's goal and the conversation behind it \u2014 so you decompose toward what they actually want.
139990
+ 1. Explore the workspace to understand the task and its boundaries.
139991
+ 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".
139992
+ 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).
139842
139993
  4. Write two files into the repo:
139843
- - ${COLLAB_SCAFFOLD_DIR}/${CONTRACTS_FILENAME} \u2014 human-readable contracts/boundaries the implementers must follow.
139844
- - ${COLLAB_SCAFFOLD_DIR}/${ROSTER_FILENAME} \u2014 a JSON array proposing the implementer roster. Each entry: { "id": "kebab-slug", "name": "Role Name", "role": "implementer", "subtask": "what this agent builds", "ownedPaths": ["dir/", "file.ts"] }. Do NOT include yourself.
139994
+ - ${COLLAB_SCAFFOLD_DIR}/${CONTRACTS_FILENAME} \u2014 human-readable contracts/boundaries the team must follow.
139995
+ - ${COLLAB_SCAFFOLD_DIR}/${ROSTER_FILENAME} \u2014 a JSON array proposing the team. Each entry: { "id": "kebab-slug", "name": "Display Name", "role": "<function>", "subtask": "what this agent delivers", "ownedPaths": ["dir/", "file.md"] }. "role" is the agent's FUNCTION \u2014 e.g. "developer", "designer", "pm", "qa", "writer", "researcher", "editor" \u2014 choose what fits. Do NOT include yourself, and do NOT use "architect" (that's you).
139845
139996
  5. Broadcast a short kickoff summary, then call collab_done.
139846
139997
 
139847
139998
  After the implementers start, you stay available as the BROKER: answer interface questions, and when an implementer proposes a contract change, review it and (if sound) commit it with collab_contract_update so everyone re-syncs.`;
@@ -149515,13 +149666,13 @@ async function runWorkflow(workflow, deps, opts = {}) {
149515
149666
  const startedAt = (deps.now ?? Date.now)();
149516
149667
  const result = await executor.run(workflow, deps);
149517
149668
  if (opts.recordDir !== null) {
149518
- await writeRunRecord(workflow, result, startedAt, executor.name, deps, opts.recordDir ?? defaultRunRecordDir()).catch((err) => deps.logger?.warn?.("workflow: failed to write run record", {
149669
+ await writeRunRecord2(workflow, result, startedAt, executor.name, deps, opts.recordDir ?? defaultRunRecordDir()).catch((err) => deps.logger?.warn?.("workflow: failed to write run record", {
149519
149670
  error: err instanceof Error ? err.message : String(err)
149520
149671
  }));
149521
149672
  }
149522
149673
  return result;
149523
149674
  }
149524
- async function writeRunRecord(workflow, result, startedAt, executorName, deps, dir) {
149675
+ async function writeRunRecord2(workflow, result, startedAt, executorName, deps, dir) {
149525
149676
  await promises.mkdir(dir, { recursive: true });
149526
149677
  const stamp = new Date(startedAt).toISOString().replace(/[:.]/g, "-");
149527
149678
  const file = path3.join(dir, `${stamp}-${workflow.name}-${ulid().slice(-6)}.jsonl`);
@@ -153902,11 +154053,11 @@ function servicePlatform() {
153902
154053
  if (process.platform === "linux") return "linux";
153903
154054
  return "unsupported";
153904
154055
  }
153905
- function moxxyHome2() {
154056
+ function moxxyHome3() {
153906
154057
  return process.env.MOXXY_HOME ?? path3__default.join(homedir(), ".moxxy");
153907
154058
  }
153908
154059
  function serviceLogPath(spec) {
153909
- return path3__default.join(moxxyHome2(), "services", `${spec.id}.log`);
154060
+ return path3__default.join(moxxyHome3(), "services", `${spec.id}.log`);
153910
154061
  }
153911
154062
  function nodeBin() {
153912
154063
  return process.execPath;
@@ -155948,7 +156099,10 @@ async function runAgentCommand(argv) {
155948
156099
  const sessionId = process.env.MOXXY_SESSION_ID?.trim();
155949
156100
  const mode = process.env.MOXXY_MODE?.trim() || "collab-peer";
155950
156101
  const subtask = process.env.MOXXY_COLLAB_SUBTASK ?? "";
156102
+ const parentTask = process.env.MOXXY_COLLAB_PARENT_TASK?.trim() ?? "";
156103
+ const role = process.env.MOXXY_COLLAB_ROLE?.trim() ?? "";
155951
156104
  const model = process.env.MOXXY_MODEL?.trim();
156105
+ const seededTurn = buildSeedTurn({ role, parentTask, subtask });
155952
156106
  const setup = await bootSessionWithConfig(argv, {
155953
156107
  skipKeyPrompt: true,
155954
156108
  tolerateNoProvider: true,
@@ -155970,7 +156124,7 @@ async function runAgentCommand(argv) {
155970
156124
  const runnerServer = await startRunnerServer(session);
155971
156125
  const turnDone = (async () => {
155972
156126
  try {
155973
- for await (const _2 of session.runTurn(subtask)) void _2;
156127
+ for await (const _2 of session.runTurn(seededTurn)) void _2;
155974
156128
  } catch {
155975
156129
  }
155976
156130
  try {
@@ -155987,6 +156141,22 @@ async function runAgentCommand(argv) {
155987
156141
  await runUntilSignal2(runnerServer, session, turnDone);
155988
156142
  return 0;
155989
156143
  }
156144
+ function buildSeedTurn(args) {
156145
+ const { role, parentTask, subtask } = args;
156146
+ const pointer = "Shared team context is in `.moxxy-collab/BRIEF.md` (the user's overall goal + the conversation/intent) and `.moxxy-collab/CONTRACTS.md` (the agreed interfaces). Read them before you start so your work fits the real goal.";
156147
+ if (role === "architect" || !parentTask || parentTask === subtask) {
156148
+ return subtask ? `${subtask}
156149
+
156150
+ ${pointer}` : pointer;
156151
+ }
156152
+ const roleLine = role && role !== "implementer" ? `Your role on the team: ${role}.
156153
+ ` : "";
156154
+ return `${roleLine}Overall team goal: ${parentTask}
156155
+
156156
+ Your sub-task: ${subtask}
156157
+
156158
+ ${pointer}`;
156159
+ }
155990
156160
  async function runUntilSignal2(runnerServer, session, turnDone) {
155991
156161
  let stopping = false;
155992
156162
  const shutdown = async (signal) => {