@adhdev/daemon-core 0.9.76-rc.9 → 0.9.76

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.
Files changed (65) hide show
  1. package/dist/cli-adapters/provider-cli-adapter.d.ts +5 -2
  2. package/dist/cli-adapters/provider-cli-runtime.d.ts +1 -0
  3. package/dist/cli-adapters/provider-cli-shared.d.ts +24 -0
  4. package/dist/commands/chat-commands.d.ts +2 -0
  5. package/dist/commands/cli-manager.d.ts +17 -4
  6. package/dist/commands/mesh-coordinator.d.ts +2 -0
  7. package/dist/commands/router.d.ts +11 -0
  8. package/dist/config/mesh-config.d.ts +3 -0
  9. package/dist/git/git-types.d.ts +1 -1
  10. package/dist/git/git-worktree.d.ts +64 -0
  11. package/dist/git/index.d.ts +2 -0
  12. package/dist/index.d.ts +4 -4
  13. package/dist/index.js +2427 -561
  14. package/dist/index.js.map +1 -1
  15. package/dist/index.mjs +2432 -584
  16. package/dist/index.mjs.map +1 -1
  17. package/dist/mesh/coordinator-prompt.d.ts +1 -0
  18. package/dist/mesh/mesh-events.d.ts +18 -0
  19. package/dist/providers/chat-message-normalization.d.ts +40 -0
  20. package/dist/providers/cli-provider-instance.d.ts +7 -1
  21. package/dist/providers/contracts.d.ts +20 -1
  22. package/dist/providers/io-contracts.d.ts +17 -1
  23. package/dist/providers/provider-input-support.d.ts +18 -2
  24. package/dist/providers/provider-instance-manager.d.ts +1 -0
  25. package/dist/providers/provider-instance.d.ts +4 -0
  26. package/dist/repo-mesh-types.d.ts +34 -0
  27. package/dist/session-host/runtime-support.d.ts +2 -1
  28. package/dist/shared-types.d.ts +8 -0
  29. package/dist/types.d.ts +9 -0
  30. package/package.json +4 -5
  31. package/src/chat/subscription-updates.ts +3 -1
  32. package/src/cli-adapters/provider-cli-adapter.ts +44 -11
  33. package/src/cli-adapters/provider-cli-runtime.ts +3 -2
  34. package/src/cli-adapters/provider-cli-shared.ts +201 -15
  35. package/src/commands/chat-commands.ts +166 -16
  36. package/src/commands/cli-manager.ts +78 -5
  37. package/src/commands/handler.ts +13 -4
  38. package/src/commands/mesh-coordinator.ts +155 -5
  39. package/src/commands/router.d.ts +1 -0
  40. package/src/commands/router.ts +606 -32
  41. package/src/config/mesh-config.ts +27 -2
  42. package/src/git/git-commands.ts +5 -1
  43. package/src/git/git-types.ts +1 -0
  44. package/src/git/git-worktree.ts +214 -0
  45. package/src/git/index.ts +14 -0
  46. package/src/index.ts +20 -1
  47. package/src/mesh/coordinator-prompt.ts +36 -14
  48. package/src/mesh/mesh-events.ts +173 -42
  49. package/src/providers/acp-provider-instance.ts +118 -30
  50. package/src/providers/chat-message-normalization.ts +241 -0
  51. package/src/providers/cli-provider-instance.d.ts +2 -0
  52. package/src/providers/cli-provider-instance.ts +219 -13
  53. package/src/providers/contracts.ts +25 -1
  54. package/src/providers/io-contracts.ts +63 -5
  55. package/src/providers/provider-input-support.ts +125 -1
  56. package/src/providers/provider-instance-manager.ts +20 -1
  57. package/src/providers/provider-instance.ts +4 -0
  58. package/src/providers/provider-schema.ts +38 -8
  59. package/src/providers/read-chat-contract.ts +8 -0
  60. package/src/repo-mesh-types.ts +38 -0
  61. package/src/session-host/runtime-support.ts +55 -7
  62. package/src/shared-types.ts +8 -0
  63. package/src/status/builders.ts +5 -3
  64. package/src/status/reporter.ts +6 -0
  65. package/src/types.ts +9 -0
@@ -74,6 +74,25 @@ export function normalizeRepoIdentity(remoteUrl: string): string {
74
74
 
75
75
  // ─── CRUD Operations ────────────────────────────
76
76
 
77
+ const SESSION_CLEANUP_MODES = new Set(['preserve', 'stop', 'delete_stopped', 'stop_and_delete']);
78
+ const SPAWNED_SESSION_VISIBILITY_MODES = new Set(['visible', 'hidden']);
79
+
80
+ function mergeMeshPolicy(base: RepoMeshPolicy | undefined, patch: Partial<RepoMeshPolicy> | undefined): RepoMeshPolicy {
81
+ const policy: RepoMeshPolicy = { ...DEFAULT_MESH_POLICY, ...(base || {}), ...(patch || {}) };
82
+ if (!['block', 'warn', 'checkpoint_then_continue'].includes(policy.dirtyWorkspaceBehavior)) {
83
+ policy.dirtyWorkspaceBehavior = 'warn';
84
+ }
85
+ const maxParallelTasks = Number(policy.maxParallelTasks);
86
+ policy.maxParallelTasks = Number.isFinite(maxParallelTasks) ? Math.max(1, Math.min(8, Math.floor(maxParallelTasks))) : 2;
87
+ if (!SESSION_CLEANUP_MODES.has(String(policy.sessionCleanupOnNodeRemove))) {
88
+ policy.sessionCleanupOnNodeRemove = 'preserve';
89
+ }
90
+ if (!SPAWNED_SESSION_VISIBILITY_MODES.has(String(policy.spawnedSessionVisibility))) {
91
+ policy.spawnedSessionVisibility = 'visible';
92
+ }
93
+ return policy;
94
+ }
95
+
77
96
  export function listMeshes(): LocalMeshEntry[] {
78
97
  return loadMeshConfig().meshes;
79
98
  }
@@ -112,7 +131,7 @@ export function createMesh(opts: CreateMeshOptions): LocalMeshEntry {
112
131
  repoIdentity,
113
132
  repoRemoteUrl: opts.repoRemoteUrl,
114
133
  defaultBranch: opts.defaultBranch,
115
- policy: { ...DEFAULT_MESH_POLICY, ...opts.policy },
134
+ policy: mergeMeshPolicy(undefined, opts.policy),
116
135
  coordinator: opts.coordinator || {},
117
136
  nodes: [],
118
137
  createdAt: now,
@@ -138,7 +157,7 @@ export function updateMesh(meshId: string, opts: UpdateMeshOptions): LocalMeshEn
138
157
 
139
158
  if (opts.name !== undefined) mesh.name = opts.name.trim().slice(0, 100);
140
159
  if (opts.defaultBranch !== undefined) mesh.defaultBranch = opts.defaultBranch;
141
- if (opts.policy) mesh.policy = { ...mesh.policy, ...opts.policy };
160
+ if (opts.policy) mesh.policy = mergeMeshPolicy(mesh.policy, opts.policy);
142
161
  if (opts.coordinator) mesh.coordinator = opts.coordinator;
143
162
  mesh.updatedAt = new Date().toISOString();
144
163
 
@@ -160,9 +179,12 @@ export function deleteMesh(meshId: string): boolean {
160
179
  export interface AddNodeOptions {
161
180
  workspace: string;
162
181
  repoRoot?: string;
182
+ daemonId?: string;
163
183
  userOverrides?: Partial<RepoMeshNodeCapabilities>;
164
184
  policy?: RepoMeshNodePolicy;
165
185
  isLocalWorktree?: boolean;
186
+ worktreeBranch?: string;
187
+ clonedFromNodeId?: string;
166
188
  }
167
189
 
168
190
  export function addNode(meshId: string, opts: AddNodeOptions): LocalMeshNodeEntry | undefined {
@@ -183,9 +205,12 @@ export function addNode(meshId: string, opts: AddNodeOptions): LocalMeshNodeEntr
183
205
  id: `node_${randomUUID().replace(/-/g, '')}`,
184
206
  workspace: opts.workspace.trim(),
185
207
  repoRoot: opts.repoRoot,
208
+ daemonId: opts.daemonId,
186
209
  userOverrides: opts.userOverrides || {},
187
210
  policy: opts.policy || {},
188
211
  isLocalWorktree: opts.isLocalWorktree,
212
+ worktreeBranch: opts.worktreeBranch,
213
+ clonedFromNodeId: opts.clonedFromNodeId,
189
214
  };
190
215
 
191
216
  mesh.nodes.push(node);
@@ -152,6 +152,7 @@ const FAILURE_REASONS = new Set<GitFailureReason>([
152
152
  'dirty_index_required',
153
153
  'conflict',
154
154
  'invalid_args',
155
+ 'nothing_to_commit',
155
156
  'git_command_failed',
156
157
  ]);
157
158
 
@@ -454,7 +455,10 @@ async function gitCheckpoint(
454
455
  } catch (err: any) {
455
456
  const output = (err?.stdout || '') + (err?.stderr || '');
456
457
  if (/nothing to commit/i.test(output)) {
457
- throw new GitCommandError('git_command_failed', 'Nothing to commit');
458
+ throw new GitCommandError('nothing_to_commit', 'Nothing to commit — working tree is clean.', {
459
+ stdout: err?.stdout,
460
+ stderr: err?.stderr,
461
+ });
458
462
  }
459
463
  throw err;
460
464
  }
@@ -14,6 +14,7 @@ export type GitFailureReason =
14
14
  | 'dirty_index_required'
15
15
  | 'conflict'
16
16
  | 'invalid_args'
17
+ | 'nothing_to_commit'
17
18
  | 'git_command_failed';
18
19
 
19
20
  export interface GitRepoIdentity {
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Git Worktree — Create/remove/list worktrees for Repo Mesh node cloning
3
+ *
4
+ * Used by the `clone_mesh_node` daemon command to create isolated
5
+ * worktree-based nodes for parallel branch work within a mesh.
6
+ *
7
+ * Worktrees are placed outside the source repo to avoid .gitignore
8
+ * pollution and submodule conflicts:
9
+ * <repoParent>/.adhdev-worktrees/<meshName>/<branch>/
10
+ */
11
+
12
+ import * as path from 'node:path';
13
+ import { mkdir } from 'node:fs/promises';
14
+ import { existsSync } from 'node:fs';
15
+ import { execFile } from 'node:child_process';
16
+ import { promisify } from 'node:util';
17
+
18
+ const execFileAsync = promisify(execFile);
19
+
20
+ const WORKTREE_DIR_NAME = '.adhdev-worktrees';
21
+ const GIT_TIMEOUT_MS = 30_000;
22
+ const GIT_MAX_BUFFER = 4 * 1024 * 1024;
23
+
24
+ // ─── Types ──────────────────────────────────────
25
+
26
+ export interface WorktreeCreateOptions {
27
+ /** Absolute path to the source repo's git root */
28
+ repoRoot: string;
29
+ /** Branch name for the new worktree */
30
+ branch: string;
31
+ /** Starting point for the branch (default: HEAD) */
32
+ baseBranch?: string;
33
+ /** Mesh name, used for organizing worktree directories */
34
+ meshName: string;
35
+ /** Override the auto-resolved target directory */
36
+ targetDir?: string;
37
+ }
38
+
39
+ export interface WorktreeCreateResult {
40
+ success: true;
41
+ worktreePath: string;
42
+ branch: string;
43
+ }
44
+
45
+ export interface WorktreeEntry {
46
+ path: string;
47
+ head: string;
48
+ branch: string | null;
49
+ bare: boolean;
50
+ }
51
+
52
+ export interface WorktreeRemoveResult {
53
+ success: true;
54
+ removedPath: string;
55
+ }
56
+
57
+ // ─── Path Resolution ────────────────────────────
58
+
59
+ /**
60
+ * Resolve the target directory for a new worktree.
61
+ * Places worktrees at: <repoParent>/.adhdev-worktrees/<meshName>/<branch>/
62
+ */
63
+ export function resolveWorktreePath(repoRoot: string, meshName: string, branch: string): string {
64
+ // Sanitize branch name for filesystem (e.g. feat/auth → feat-auth)
65
+ const safeBranch = branch.replace(/[/\\:*?"<>|]/g, '-').replace(/^\.+|\.+$/g, '');
66
+ const safeMeshName = meshName.replace(/[/\\:*?"<>|]/g, '-').replace(/^\.+|\.+$/g, '');
67
+ const parentDir = path.dirname(repoRoot);
68
+ return path.join(parentDir, WORKTREE_DIR_NAME, safeMeshName, safeBranch);
69
+ }
70
+
71
+ // ─── Create ─────────────────────────────────────
72
+
73
+ /**
74
+ * Create a new git worktree with a fresh branch.
75
+ *
76
+ * Runs: git worktree add <targetDir> -b <branch> [baseBranch]
77
+ */
78
+ export async function createWorktree(opts: WorktreeCreateOptions): Promise<WorktreeCreateResult> {
79
+ const { repoRoot, branch, baseBranch, meshName } = opts;
80
+ const targetDir = opts.targetDir || resolveWorktreePath(repoRoot, meshName, branch);
81
+
82
+ if (existsSync(targetDir)) {
83
+ throw new Error(`Worktree target directory already exists: ${targetDir}`);
84
+ }
85
+
86
+ // Ensure parent directory exists
87
+ await mkdir(path.dirname(targetDir), { recursive: true });
88
+
89
+ const args = ['worktree', 'add', targetDir, '-b', branch];
90
+ if (baseBranch) {
91
+ args.push(baseBranch);
92
+ }
93
+
94
+ try {
95
+ await execFileAsync('git', args, {
96
+ cwd: repoRoot,
97
+ encoding: 'utf8',
98
+ timeout: GIT_TIMEOUT_MS,
99
+ maxBuffer: GIT_MAX_BUFFER,
100
+ windowsHide: true,
101
+ });
102
+ } catch (error: any) {
103
+ const stderr = typeof error.stderr === 'string' ? error.stderr : '';
104
+ // Clean error messages for common failures
105
+ if (/already exists/i.test(stderr)) {
106
+ throw new Error(`Branch '${branch}' already exists or is checked out in another worktree`);
107
+ }
108
+ throw new Error(`git worktree add failed: ${stderr.trim() || error.message}`);
109
+ }
110
+
111
+ return {
112
+ success: true,
113
+ worktreePath: targetDir,
114
+ branch,
115
+ };
116
+ }
117
+
118
+ // ─── Remove ─────────────────────────────────────
119
+
120
+ /**
121
+ * Remove a git worktree and clean up the directory.
122
+ *
123
+ * Runs: git worktree remove <worktreePath> --force
124
+ */
125
+ export async function removeWorktree(repoRoot: string, worktreePath: string): Promise<WorktreeRemoveResult> {
126
+ if (!existsSync(worktreePath)) {
127
+ // Already gone — just prune
128
+ await pruneWorktrees(repoRoot);
129
+ return { success: true, removedPath: worktreePath };
130
+ }
131
+
132
+ try {
133
+ await execFileAsync('git', ['worktree', 'remove', worktreePath, '--force'], {
134
+ cwd: repoRoot,
135
+ encoding: 'utf8',
136
+ timeout: GIT_TIMEOUT_MS,
137
+ maxBuffer: GIT_MAX_BUFFER,
138
+ windowsHide: true,
139
+ });
140
+ } catch (error: any) {
141
+ const stderr = typeof error.stderr === 'string' ? error.stderr : '';
142
+ throw new Error(`git worktree remove failed: ${stderr.trim() || error.message}`);
143
+ }
144
+
145
+ return { success: true, removedPath: worktreePath };
146
+ }
147
+
148
+ // ─── List ───────────────────────────────────────
149
+
150
+ /**
151
+ * List all worktrees for a repository.
152
+ *
153
+ * Runs: git worktree list --porcelain
154
+ */
155
+ export async function listWorktrees(repoRoot: string): Promise<WorktreeEntry[]> {
156
+ const { stdout } = await execFileAsync('git', ['worktree', 'list', '--porcelain'], {
157
+ cwd: repoRoot,
158
+ encoding: 'utf8',
159
+ timeout: GIT_TIMEOUT_MS,
160
+ maxBuffer: GIT_MAX_BUFFER,
161
+ windowsHide: true,
162
+ });
163
+
164
+ return parseWorktreeListOutput(stdout);
165
+ }
166
+
167
+ /**
168
+ * Parse `git worktree list --porcelain` output into structured entries.
169
+ */
170
+ export function parseWorktreeListOutput(output: string): WorktreeEntry[] {
171
+ const entries: WorktreeEntry[] = [];
172
+ const blocks = output.trim().split(/\n\n+/);
173
+
174
+ for (const block of blocks) {
175
+ if (!block.trim()) continue;
176
+ const lines = block.trim().split('\n');
177
+ const entry: WorktreeEntry = { path: '', head: '', branch: null, bare: false };
178
+
179
+ for (const line of lines) {
180
+ if (line.startsWith('worktree ')) {
181
+ entry.path = line.slice('worktree '.length).trim();
182
+ } else if (line.startsWith('HEAD ')) {
183
+ entry.head = line.slice('HEAD '.length).trim();
184
+ } else if (line.startsWith('branch ')) {
185
+ const ref = line.slice('branch '.length).trim();
186
+ // refs/heads/feat/auth → feat/auth
187
+ entry.branch = ref.replace(/^refs\/heads\//, '');
188
+ } else if (line === 'bare') {
189
+ entry.bare = true;
190
+ }
191
+ }
192
+
193
+ if (entry.path) {
194
+ entries.push(entry);
195
+ }
196
+ }
197
+
198
+ return entries;
199
+ }
200
+
201
+ // ─── Prune ──────────────────────────────────────
202
+
203
+ async function pruneWorktrees(repoRoot: string): Promise<void> {
204
+ try {
205
+ await execFileAsync('git', ['worktree', 'prune'], {
206
+ cwd: repoRoot,
207
+ encoding: 'utf8',
208
+ timeout: GIT_TIMEOUT_MS,
209
+ windowsHide: true,
210
+ });
211
+ } catch {
212
+ // Prune is best-effort
213
+ }
214
+ }
package/src/git/index.ts CHANGED
@@ -73,3 +73,17 @@ export type {
73
73
 
74
74
  export { TurnSnapshotTracker } from './turn-snapshot-tracker.js';
75
75
  export type { TurnCompletedCallback } from './turn-snapshot-tracker.js';
76
+
77
+ export {
78
+ createWorktree,
79
+ listWorktrees,
80
+ parseWorktreeListOutput,
81
+ removeWorktree,
82
+ resolveWorktreePath,
83
+ } from './git-worktree.js';
84
+ export type {
85
+ WorktreeCreateOptions,
86
+ WorktreeCreateResult,
87
+ WorktreeEntry,
88
+ WorktreeRemoveResult,
89
+ } from './git-worktree.js';
package/src/index.ts CHANGED
@@ -79,6 +79,10 @@ export type {
79
79
  CliProviderState,
80
80
  AcpProviderState,
81
81
  ExtensionProviderState,
82
+ MessageInputSupport,
83
+ InputMediaStrategyDescriptor,
84
+ InputAttachmentStrategy,
85
+ InputMediaType,
82
86
  } from './shared-types.js';
83
87
 
84
88
  // ── Repo Mesh Types (cross-package) ──
@@ -88,6 +92,7 @@ export type {
88
92
  RepoMeshNodeHealth,
89
93
  RepoMeshPolicy,
90
94
  RepoMeshNodePolicy,
95
+ RepoMeshRelatedRepo,
91
96
  RepoMeshNodeCapabilities,
92
97
  DetectedCommand,
93
98
  ProjectContextSnapshot,
@@ -311,8 +316,22 @@ export {
311
316
  buildUserChatMessage,
312
317
  normalizeChatMessage,
313
318
  normalizeChatMessages,
319
+ CHAT_MESSAGE_VISIBILITIES,
320
+ CHAT_MESSAGE_TRANSCRIPT_VISIBILITIES,
321
+ CHAT_MESSAGE_AUDIENCES,
322
+ CHAT_MESSAGE_SOURCES,
323
+ CHAT_MESSAGE_ACTIVITY_SOURCES,
324
+ CHAT_MESSAGE_INTERNAL_SOURCES,
325
+ classifyChatMessageVisibility,
326
+ isUserFacingChatMessage,
327
+ isActivityChatMessage,
328
+ isInternalChatMessage,
329
+ filterUserFacingChatMessages,
330
+ filterActivityChatMessages,
331
+ filterInternalChatMessages,
332
+ filterChatMessagesByVisibility,
314
333
  } from './providers/chat-message-normalization.js';
315
- export type { BuiltinChatMessageKind, ChatMessageKind } from './providers/chat-message-normalization.js';
334
+ export type { BuiltinChatMessageKind, ChatMessageKind, ChatMessageVisibility, ChatMessageTranscriptVisibility, ChatMessageAudience, ChatMessageSource, ChatMessageTranscriptSurface, ChatMessageVisibilityClassification } from './providers/chat-message-normalization.js';
316
335
  export { VersionArchive, detectAllVersions } from './providers/version-archive.js';
317
336
  export type { ProviderVersionInfo, VersionHistory } from './providers/version-archive.js';
318
337
 
@@ -16,6 +16,7 @@ import type {
16
16
  RepoMeshStatus,
17
17
  RepoMeshNodeStatus,
18
18
  } from '../repo-mesh-types.js';
19
+ import { DEFAULT_MESH_POLICY } from '../repo-mesh-types.js';
19
20
 
20
21
  // ─── Prompt Builder ─────────────────────────────
21
22
 
@@ -23,10 +24,11 @@ export interface CoordinatorPromptContext {
23
24
  mesh: LocalMeshEntry;
24
25
  status?: RepoMeshStatus;
25
26
  userInstruction?: string;
27
+ coordinatorCliType?: string;
26
28
  }
27
29
 
28
30
  export function buildCoordinatorSystemPrompt(ctx: CoordinatorPromptContext): string {
29
- const { mesh, status, userInstruction } = ctx;
31
+ const { mesh, status, userInstruction, coordinatorCliType } = ctx;
30
32
  const sections: string[] = [];
31
33
 
32
34
  // ── Identity ──
@@ -45,23 +47,26 @@ Repository: \`${mesh.repoIdentity}\`${mesh.defaultBranch ? `\nDefault branch: \`
45
47
  }
46
48
 
47
49
  // ── Policy ──
48
- sections.push(buildPolicySection(mesh.policy));
50
+ sections.push(buildPolicySection({ ...DEFAULT_MESH_POLICY, ...(mesh.policy || {}) }));
49
51
 
50
52
  // ── Tools ──
51
53
  sections.push(TOOLS_SECTION);
52
54
 
55
+ // ── Tool Exposure Preflight ──
56
+ sections.push(TOOL_EXPOSURE_PREFLIGHT_SECTION);
57
+
53
58
  // ── Workflow ──
54
59
  sections.push(WORKFLOW_SECTION);
55
60
 
56
61
  // ── Rules ──
57
- sections.push(RULES_SECTION);
62
+ sections.push(buildRulesSection(coordinatorCliType));
58
63
 
59
64
  // ── User instruction ──
60
65
  if (userInstruction) {
61
66
  sections.push(`## Additional Context\n${userInstruction}`);
62
67
  }
63
68
 
64
- if (mesh.coordinator.systemPromptSuffix) {
69
+ if (mesh.coordinator?.systemPromptSuffix) {
65
70
  sections.push(mesh.coordinator.systemPromptSuffix);
66
71
  }
67
72
 
@@ -130,7 +135,13 @@ const TOOLS_SECTION = `## Available Tools
130
135
  | \`mesh_read_chat\` | Read an agent's recent messages to check progress |
131
136
  | \`mesh_git_status\` | Check git status on a specific node |
132
137
  | \`mesh_checkpoint\` | Create a git checkpoint on a node |
133
- | \`mesh_approve\` | Approve/reject a pending agent action |`;
138
+ | \`mesh_approve\` | Approve/reject a pending agent action |
139
+ | \`mesh_clone_node\` | Create a worktree node for isolated parallel branch work |
140
+ | \`mesh_remove_node\` | Remove a node (cleans up worktree if applicable) |`;
141
+
142
+ const TOOL_EXPOSURE_PREFLIGHT_SECTION = `## Tool Exposure Preflight
143
+
144
+ Before doing any coordinator work, confirm that the actual callable tool list includes \`mesh_status\` and the other \`mesh_*\` tools from the table above. If this Repo Mesh coordinator prompt is present but the callable \`mesh_*\` tools are missing, the MCP server/tool manifest is stale or not injected yet. Do not substitute terminal/file/git tools, do not inspect or edit the repository directly, and do not continue as a non-mesh local coding agent. Stop immediately and tell the user to run \`/reload-mcp\` or start a fresh coordinator session so ADHDev can reconnect \`adhdev-mesh\`.`;
134
145
 
135
146
  const WORKFLOW_SECTION = `## Orchestration Workflow
136
147
 
@@ -138,21 +149,32 @@ const WORKFLOW_SECTION = `## Orchestration Workflow
138
149
  2. **Plan** — Decompose the user's request into independent tasks for parallel execution, or sequential tasks when dependencies exist.
139
150
  3. **Delegate** — For each task:
140
151
  a. Pick the best node (consider: health, dirty state, current workload).
141
- b. If no session exists, call \`mesh_launch_session\` to start one.
142
- c. Call \`mesh_send_task\` with a **complete, self-contained** instruction that includes all context the agent needs (file paths, line numbers, what to change, why). Do not send partial instructions expecting future follow-up.
143
- 4. **Monitor** Periodically call \`mesh_read_chat\` to check progress. Handle approvals via \`mesh_approve\`.
144
- 5. **Verify** — When a task reports completion, call \`mesh_git_status\` to verify changes were made.
152
+ b. If you need branch isolation for parallel work, call \`mesh_clone_node\` to create a worktree node first.
153
+ c. If no session exists, call \`mesh_launch_session\` to start one.
154
+ d. Call \`mesh_send_task\` with a **complete, self-contained** instruction that includes all context the agent needs (file paths, line numbers, what to change, why). Do not send partial instructions expecting future follow-up.
155
+ 4. **Monitor** — Prefer event-driven completion/status notifications. Do **not** poll \`mesh_read_chat\` repeatedly just because the delegated session has not produced a final assistant message yet; tool/terminal activity means work may still be in progress. Do not call \`mesh_read_chat\` again within a few seconds for the same generating session; wait for the completion callback/status event instead unless you are debugging a real stall. Use at most one compact \`mesh_read_chat\` check after a completion/approval signal, an explicit user status request, or a real timeout/stall. Handle approvals via \`mesh_approve\`.
156
+ 5. **Verify** — When a task reports completion or git work is visible, call \`mesh_git_status\` to verify changes were made.
145
157
  6. **Checkpoint** — Call \`mesh_checkpoint\` to save the work.
146
- 7. **Report** — Summarize what was done, what changed, and any issues.`;
158
+ 7. **Clean up** — Remove worktree nodes via \`mesh_remove_node\` after their work is merged or no longer needed.
159
+ 8. **Report** — Summarize what was done, what changed, and any issues.`;
147
160
 
148
- const RULES_SECTION = `## Rules
161
+ function buildRulesSection(coordinatorCliType?: string): string {
162
+ const coordinatorNote = coordinatorCliType
163
+ ? `\n- **Coordinator runtime is not a delegation default.** This coordinator is running as \`${coordinatorCliType}\`, but delegated node sessions must follow the user's requested provider, not the coordinator's own runtime.`
164
+ : '';
165
+
166
+ return `## Rules
149
167
 
150
168
  - **Minimize coordinator context.** The coordinator's job is routing, not implementing. Do not read source files, run commands, or analyze code directly — delegate all of that to node agents. Your context should stay lean.
151
169
  - **Delegate analysis too.** If you need to understand a bug or explore the codebase, send that investigation as a task to a node. Do not do it yourself.
170
+ - **Respect explicit provider requests.** If the user names an agent/provider, pass the matching provider type to \`mesh_launch_session\`: Hermes → \`hermes-cli\`, Claude Code/Claude → \`claude-cli\`, Codex → \`codex-cli\`, Gemini → \`gemini-cli\`. Never substitute \`claude-cli\` just because the coordinator itself is Claude Code.
152
171
  - **Front-load the task message.** When calling \`mesh_send_task\`, include everything the agent needs: what files to touch, what the problem is, what the fix should look like. The agent won't ask follow-up questions.
153
- - **Don't inspect code.** Trust the agent's output. Verify via \`mesh_git_status\`, not by reading source files.
154
- - **Don't over-parallelize.** Start with 1-2 concurrent tasks. Scale up if they succeed.
172
+ - **Don't inspect code.** Treat delegated agent summaries as self-reports, not verification. Verify side effects via \`mesh_git_status\` (including related repo freshness when configured), not by reading source files.
173
+ - **Don't over-parallelize.** Start with 1-2 concurrent tasks. Scale up if they succeed. Never launch a duplicate session or second worker solely because \`mesh_read_chat\` has no final assistant message while the delegated session is still showing tool/terminal activity.
155
174
  - **Handle failures gracefully.** If a task fails, read the chat to understand why, then retry or reassign.
156
175
  - **Keep the user informed.** Report progress after each delegation round — one or two sentences, not a narration.
157
176
  - **Respect node capabilities.** Don't send build tasks to read-only nodes. Don't push from nodes that aren't allowed to.
158
- - **Never fabricate tool results.** Always call the actual tool; never pretend you did.`;
177
+ - **Never fabricate tool results.** Always call the actual tool; never pretend you did.
178
+ - **Clean up worktree nodes.** After a worktree task completes and its changes are merged or checkpointed, call \`mesh_remove_node\` to free resources.
179
+ - **Name worktree branches meaningfully.** Use descriptive names like \`feat/auth-refactor\` or \`fix/build-123\`.${coordinatorNote}`;
180
+ }