@adhdev/daemon-core 0.9.66 → 0.9.68
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/config/mesh-config.d.ts +47 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +525 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +515 -18
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/coordinator-prompt.d.ts +18 -0
- package/dist/mesh/mesh-sync.d.ts +51 -0
- package/dist/repo-mesh-types.d.ts +168 -0
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +1 -1
- package/src/commands/router.ts +156 -0
- package/src/config/mesh-config.ts +228 -0
- package/src/index.ts +33 -0
- package/src/mesh/coordinator-prompt.ts +156 -0
- package/src/mesh/mesh-sync.ts +109 -0
- package/src/repo-mesh-types.ts +212 -0
package/dist/index.mjs
CHANGED
|
@@ -25,6 +25,22 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
25
25
|
};
|
|
26
26
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
27
|
|
|
28
|
+
// src/repo-mesh-types.ts
|
|
29
|
+
var DEFAULT_MESH_POLICY;
|
|
30
|
+
var init_repo_mesh_types = __esm({
|
|
31
|
+
"src/repo-mesh-types.ts"() {
|
|
32
|
+
"use strict";
|
|
33
|
+
DEFAULT_MESH_POLICY = {
|
|
34
|
+
requirePreTaskCheckpoint: false,
|
|
35
|
+
requirePostTaskCheckpoint: true,
|
|
36
|
+
requireApprovalForPush: true,
|
|
37
|
+
requireApprovalForDestructiveGit: true,
|
|
38
|
+
dirtyWorkspaceBehavior: "warn",
|
|
39
|
+
maxParallelTasks: 2
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
28
44
|
// src/config/config.ts
|
|
29
45
|
var config_exports = {};
|
|
30
46
|
__export(config_exports, {
|
|
@@ -263,6 +279,275 @@ var init_config = __esm({
|
|
|
263
279
|
}
|
|
264
280
|
});
|
|
265
281
|
|
|
282
|
+
// src/config/mesh-config.ts
|
|
283
|
+
var mesh_config_exports = {};
|
|
284
|
+
__export(mesh_config_exports, {
|
|
285
|
+
addNode: () => addNode,
|
|
286
|
+
createMesh: () => createMesh,
|
|
287
|
+
deleteMesh: () => deleteMesh,
|
|
288
|
+
getMesh: () => getMesh,
|
|
289
|
+
getMeshByRepo: () => getMeshByRepo,
|
|
290
|
+
listMeshes: () => listMeshes,
|
|
291
|
+
normalizeRepoIdentity: () => normalizeRepoIdentity,
|
|
292
|
+
removeNode: () => removeNode,
|
|
293
|
+
updateMesh: () => updateMesh,
|
|
294
|
+
updateNode: () => updateNode
|
|
295
|
+
});
|
|
296
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
297
|
+
import { join as join3 } from "path";
|
|
298
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
299
|
+
function getMeshConfigPath() {
|
|
300
|
+
return join3(getConfigDir(), "meshes.json");
|
|
301
|
+
}
|
|
302
|
+
function loadMeshConfig() {
|
|
303
|
+
const path26 = getMeshConfigPath();
|
|
304
|
+
if (!existsSync3(path26)) return { meshes: [] };
|
|
305
|
+
try {
|
|
306
|
+
const raw = JSON.parse(readFileSync2(path26, "utf-8"));
|
|
307
|
+
if (!raw || !Array.isArray(raw.meshes)) return { meshes: [] };
|
|
308
|
+
return raw;
|
|
309
|
+
} catch {
|
|
310
|
+
return { meshes: [] };
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
function saveMeshConfig(config) {
|
|
314
|
+
const path26 = getMeshConfigPath();
|
|
315
|
+
writeFileSync2(path26, JSON.stringify(config, null, 2), { encoding: "utf-8", mode: 384 });
|
|
316
|
+
}
|
|
317
|
+
function normalizeRepoIdentity(remoteUrl) {
|
|
318
|
+
let identity = remoteUrl.trim();
|
|
319
|
+
if (identity.startsWith("http://") || identity.startsWith("https://")) {
|
|
320
|
+
try {
|
|
321
|
+
const url = new URL(identity);
|
|
322
|
+
const path26 = url.pathname.replace(/^\//, "").replace(/\.git$/, "");
|
|
323
|
+
return `${url.hostname}/${path26}`;
|
|
324
|
+
} catch {
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
const sshMatch = identity.match(/^(?:ssh:\/\/)?[\w.-]+@([\w.-]+)[:/]([\w.\-/]+?)(?:\.git)?$/);
|
|
328
|
+
if (sshMatch) return `${sshMatch[1]}/${sshMatch[2]}`;
|
|
329
|
+
return identity;
|
|
330
|
+
}
|
|
331
|
+
function listMeshes() {
|
|
332
|
+
return loadMeshConfig().meshes;
|
|
333
|
+
}
|
|
334
|
+
function getMesh(meshId) {
|
|
335
|
+
return loadMeshConfig().meshes.find((m) => m.id === meshId);
|
|
336
|
+
}
|
|
337
|
+
function getMeshByRepo(repoIdentity) {
|
|
338
|
+
return loadMeshConfig().meshes.find((m) => m.repoIdentity === repoIdentity);
|
|
339
|
+
}
|
|
340
|
+
function createMesh(opts) {
|
|
341
|
+
const config = loadMeshConfig();
|
|
342
|
+
if (config.meshes.length >= 20) {
|
|
343
|
+
throw new Error("Maximum 20 meshes allowed");
|
|
344
|
+
}
|
|
345
|
+
const repoIdentity = opts.repoIdentity || (opts.repoRemoteUrl ? normalizeRepoIdentity(opts.repoRemoteUrl) : "");
|
|
346
|
+
if (!repoIdentity) throw new Error("Either repoRemoteUrl or repoIdentity is required");
|
|
347
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
348
|
+
const mesh = {
|
|
349
|
+
id: `mesh_${randomUUID3().replace(/-/g, "")}`,
|
|
350
|
+
name: opts.name.trim().slice(0, 100),
|
|
351
|
+
repoIdentity,
|
|
352
|
+
repoRemoteUrl: opts.repoRemoteUrl,
|
|
353
|
+
defaultBranch: opts.defaultBranch,
|
|
354
|
+
policy: { ...DEFAULT_MESH_POLICY, ...opts.policy },
|
|
355
|
+
coordinator: opts.coordinator || {},
|
|
356
|
+
nodes: [],
|
|
357
|
+
createdAt: now,
|
|
358
|
+
updatedAt: now
|
|
359
|
+
};
|
|
360
|
+
config.meshes.push(mesh);
|
|
361
|
+
saveMeshConfig(config);
|
|
362
|
+
return mesh;
|
|
363
|
+
}
|
|
364
|
+
function updateMesh(meshId, opts) {
|
|
365
|
+
const config = loadMeshConfig();
|
|
366
|
+
const mesh = config.meshes.find((m) => m.id === meshId);
|
|
367
|
+
if (!mesh) return void 0;
|
|
368
|
+
if (opts.name !== void 0) mesh.name = opts.name.trim().slice(0, 100);
|
|
369
|
+
if (opts.defaultBranch !== void 0) mesh.defaultBranch = opts.defaultBranch;
|
|
370
|
+
if (opts.policy) mesh.policy = { ...mesh.policy, ...opts.policy };
|
|
371
|
+
if (opts.coordinator) mesh.coordinator = opts.coordinator;
|
|
372
|
+
mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
373
|
+
saveMeshConfig(config);
|
|
374
|
+
return mesh;
|
|
375
|
+
}
|
|
376
|
+
function deleteMesh(meshId) {
|
|
377
|
+
const config = loadMeshConfig();
|
|
378
|
+
const idx = config.meshes.findIndex((m) => m.id === meshId);
|
|
379
|
+
if (idx === -1) return false;
|
|
380
|
+
config.meshes.splice(idx, 1);
|
|
381
|
+
saveMeshConfig(config);
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
384
|
+
function addNode(meshId, opts) {
|
|
385
|
+
const config = loadMeshConfig();
|
|
386
|
+
const mesh = config.meshes.find((m) => m.id === meshId);
|
|
387
|
+
if (!mesh) return void 0;
|
|
388
|
+
if (mesh.nodes.length >= 10) {
|
|
389
|
+
throw new Error("Maximum 10 nodes per mesh");
|
|
390
|
+
}
|
|
391
|
+
if (mesh.nodes.some((n) => n.workspace === opts.workspace)) {
|
|
392
|
+
throw new Error("This workspace is already in the mesh");
|
|
393
|
+
}
|
|
394
|
+
const node = {
|
|
395
|
+
id: `node_${randomUUID3().replace(/-/g, "")}`,
|
|
396
|
+
workspace: opts.workspace.trim(),
|
|
397
|
+
repoRoot: opts.repoRoot,
|
|
398
|
+
userOverrides: opts.userOverrides || {},
|
|
399
|
+
policy: opts.policy || {},
|
|
400
|
+
isLocalWorktree: opts.isLocalWorktree
|
|
401
|
+
};
|
|
402
|
+
mesh.nodes.push(node);
|
|
403
|
+
mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
404
|
+
saveMeshConfig(config);
|
|
405
|
+
return node;
|
|
406
|
+
}
|
|
407
|
+
function removeNode(meshId, nodeId) {
|
|
408
|
+
const config = loadMeshConfig();
|
|
409
|
+
const mesh = config.meshes.find((m) => m.id === meshId);
|
|
410
|
+
if (!mesh) return false;
|
|
411
|
+
const idx = mesh.nodes.findIndex((n) => n.id === nodeId);
|
|
412
|
+
if (idx === -1) return false;
|
|
413
|
+
mesh.nodes.splice(idx, 1);
|
|
414
|
+
mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
415
|
+
saveMeshConfig(config);
|
|
416
|
+
return true;
|
|
417
|
+
}
|
|
418
|
+
function updateNode(meshId, nodeId, opts) {
|
|
419
|
+
const config = loadMeshConfig();
|
|
420
|
+
const mesh = config.meshes.find((m) => m.id === meshId);
|
|
421
|
+
if (!mesh) return void 0;
|
|
422
|
+
const node = mesh.nodes.find((n) => n.id === nodeId);
|
|
423
|
+
if (!node) return void 0;
|
|
424
|
+
if (opts.userOverrides) node.userOverrides = { ...node.userOverrides, ...opts.userOverrides };
|
|
425
|
+
if (opts.policy) node.policy = { ...node.policy, ...opts.policy };
|
|
426
|
+
mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
427
|
+
saveMeshConfig(config);
|
|
428
|
+
return node;
|
|
429
|
+
}
|
|
430
|
+
var init_mesh_config = __esm({
|
|
431
|
+
"src/config/mesh-config.ts"() {
|
|
432
|
+
"use strict";
|
|
433
|
+
init_config();
|
|
434
|
+
init_repo_mesh_types();
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
// src/mesh/coordinator-prompt.ts
|
|
439
|
+
var coordinator_prompt_exports = {};
|
|
440
|
+
__export(coordinator_prompt_exports, {
|
|
441
|
+
buildCoordinatorSystemPrompt: () => buildCoordinatorSystemPrompt
|
|
442
|
+
});
|
|
443
|
+
function buildCoordinatorSystemPrompt(ctx) {
|
|
444
|
+
const { mesh, status, userInstruction } = ctx;
|
|
445
|
+
const sections = [];
|
|
446
|
+
sections.push(`You are a **Repo Mesh Coordinator** \u2014 a technical team lead who orchestrates work across multiple agent sessions on a shared Git repository.
|
|
447
|
+
|
|
448
|
+
Your mesh: **${mesh.name}**
|
|
449
|
+
Repository: \`${mesh.repoIdentity}\`${mesh.defaultBranch ? `
|
|
450
|
+
Default branch: \`${mesh.defaultBranch}\`` : ""}`);
|
|
451
|
+
if (status?.nodes?.length) {
|
|
452
|
+
sections.push(buildNodeStatusSection(status.nodes));
|
|
453
|
+
} else if (mesh.nodes.length) {
|
|
454
|
+
sections.push(buildNodeConfigSection(mesh));
|
|
455
|
+
} else {
|
|
456
|
+
sections.push("## Nodes\nNo nodes configured yet. Ask the user to add nodes with `adhdev mesh add-node`.");
|
|
457
|
+
}
|
|
458
|
+
sections.push(buildPolicySection(mesh.policy));
|
|
459
|
+
sections.push(TOOLS_SECTION);
|
|
460
|
+
sections.push(WORKFLOW_SECTION);
|
|
461
|
+
sections.push(RULES_SECTION);
|
|
462
|
+
if (userInstruction) {
|
|
463
|
+
sections.push(`## Additional Context
|
|
464
|
+
${userInstruction}`);
|
|
465
|
+
}
|
|
466
|
+
if (mesh.coordinator.systemPromptSuffix) {
|
|
467
|
+
sections.push(mesh.coordinator.systemPromptSuffix);
|
|
468
|
+
}
|
|
469
|
+
return sections.join("\n\n");
|
|
470
|
+
}
|
|
471
|
+
function buildNodeStatusSection(nodes) {
|
|
472
|
+
const lines = ["## Current Node Status", ""];
|
|
473
|
+
for (const n of nodes) {
|
|
474
|
+
const healthIcon = n.health === "online" ? "\u{1F7E2}" : n.health === "dirty" ? "\u{1F7E1}" : n.health === "offline" ? "\u26AB" : "\u{1F534}";
|
|
475
|
+
const sessions = n.activeSessions.length > 0 ? `sessions: ${n.activeSessions.join(", ")}` : "no active sessions";
|
|
476
|
+
const branch = n.git?.branch ? `branch: \`${n.git.branch}\`` : "";
|
|
477
|
+
lines.push(`- ${healthIcon} **${n.machineLabel}** (${n.nodeId})`);
|
|
478
|
+
lines.push(` workspace: \`${n.workspace}\` | ${branch} | ${sessions}`);
|
|
479
|
+
if (n.error) lines.push(` \u26A0\uFE0F ${n.error}`);
|
|
480
|
+
}
|
|
481
|
+
return lines.join("\n");
|
|
482
|
+
}
|
|
483
|
+
function buildNodeConfigSection(mesh) {
|
|
484
|
+
const lines = ["## Configured Nodes", ""];
|
|
485
|
+
for (const n of mesh.nodes) {
|
|
486
|
+
const labels = [];
|
|
487
|
+
if (n.isLocalWorktree) labels.push("worktree");
|
|
488
|
+
if (n.policy.readOnly) labels.push("read-only");
|
|
489
|
+
const suffix = labels.length ? ` [${labels.join(", ")}]` : "";
|
|
490
|
+
lines.push(`- **${n.workspace}** (${n.id})${suffix}`);
|
|
491
|
+
}
|
|
492
|
+
lines.push("", "_Use `mesh_status` to probe live health before delegating work._");
|
|
493
|
+
return lines.join("\n");
|
|
494
|
+
}
|
|
495
|
+
function buildPolicySection(policy) {
|
|
496
|
+
const rules = [];
|
|
497
|
+
if (policy.requirePreTaskCheckpoint) rules.push("- Create a git checkpoint **before** starting each task");
|
|
498
|
+
if (policy.requirePostTaskCheckpoint) rules.push("- Create a git checkpoint **after** each task completes");
|
|
499
|
+
if (policy.requireApprovalForPush) rules.push("- **Ask for user approval** before pushing to remote");
|
|
500
|
+
if (policy.requireApprovalForDestructiveGit) rules.push("- **Ask for user approval** before destructive git operations (force push, reset, etc.)");
|
|
501
|
+
const dirtyBehavior = {
|
|
502
|
+
block: "- **Do not** send tasks to nodes with dirty workspaces",
|
|
503
|
+
warn: "- Warn the user if a node has uncommitted changes before sending a task",
|
|
504
|
+
checkpoint_then_continue: "- Auto-checkpoint dirty nodes before sending tasks"
|
|
505
|
+
}[policy.dirtyWorkspaceBehavior] || "";
|
|
506
|
+
if (dirtyBehavior) rules.push(dirtyBehavior);
|
|
507
|
+
rules.push(`- Maximum **${policy.maxParallelTasks}** tasks running in parallel`);
|
|
508
|
+
return `## Policy
|
|
509
|
+
${rules.join("\n")}`;
|
|
510
|
+
}
|
|
511
|
+
var TOOLS_SECTION, WORKFLOW_SECTION, RULES_SECTION;
|
|
512
|
+
var init_coordinator_prompt = __esm({
|
|
513
|
+
"src/mesh/coordinator-prompt.ts"() {
|
|
514
|
+
"use strict";
|
|
515
|
+
TOOLS_SECTION = `## Available Tools
|
|
516
|
+
|
|
517
|
+
| Tool | Purpose |
|
|
518
|
+
|------|---------|
|
|
519
|
+
| \`mesh_status\` | Check all nodes' health, git state, and active sessions |
|
|
520
|
+
| \`mesh_list_nodes\` | List nodes with workspace paths |
|
|
521
|
+
| \`mesh_launch_session\` | Start a new agent session on a node |
|
|
522
|
+
| \`mesh_send_task\` | Send a task (natural language) to a running agent |
|
|
523
|
+
| \`mesh_read_chat\` | Read an agent's recent messages to check progress |
|
|
524
|
+
| \`mesh_git_status\` | Check git status on a specific node |
|
|
525
|
+
| \`mesh_checkpoint\` | Create a git checkpoint on a node |
|
|
526
|
+
| \`mesh_approve\` | Approve/reject a pending agent action |`;
|
|
527
|
+
WORKFLOW_SECTION = `## Orchestration Workflow
|
|
528
|
+
|
|
529
|
+
1. **Assess** \u2014 Call \`mesh_status\` to see which nodes are healthy and available.
|
|
530
|
+
2. **Plan** \u2014 Decompose the user's request into independent tasks for parallel execution, or sequential tasks when dependencies exist.
|
|
531
|
+
3. **Delegate** \u2014 For each task:
|
|
532
|
+
a. Pick the best node (consider: health, dirty state, current workload).
|
|
533
|
+
b. If no session exists, call \`mesh_launch_session\` to start one.
|
|
534
|
+
c. Call \`mesh_send_task\` with a clear, self-contained natural-language instruction.
|
|
535
|
+
4. **Monitor** \u2014 Periodically call \`mesh_read_chat\` to check progress. Handle approvals via \`mesh_approve\`.
|
|
536
|
+
5. **Verify** \u2014 When a task reports completion, call \`mesh_git_status\` to verify changes were made.
|
|
537
|
+
6. **Checkpoint** \u2014 Call \`mesh_checkpoint\` to save the work.
|
|
538
|
+
7. **Report** \u2014 Summarize what was done, what changed, and any issues.`;
|
|
539
|
+
RULES_SECTION = `## Rules
|
|
540
|
+
|
|
541
|
+
- **Be conversational.** Delegate work the way a tech lead would \u2014 clear, specific instructions in natural language.
|
|
542
|
+
- **Don't inspect code.** Trust the agent's output. Verify via git diff/status, not by reading source files.
|
|
543
|
+
- **Don't over-parallelize.** Start with 1-2 concurrent tasks. Scale up if they succeed.
|
|
544
|
+
- **Handle failures gracefully.** If a task fails, read the chat to understand why, then retry or reassign.
|
|
545
|
+
- **Keep the user informed.** Report progress after each delegation round.
|
|
546
|
+
- **Respect node capabilities.** Don't send build tasks to read-only nodes. Don't push from nodes that aren't allowed to.
|
|
547
|
+
- **Never fabricate tool results.** Always call the actual tool; never pretend you did.`;
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
|
|
266
551
|
// src/logging/logger.ts
|
|
267
552
|
import * as fs2 from "fs";
|
|
268
553
|
import * as path9 from "path";
|
|
@@ -3320,6 +3605,9 @@ var init_provider_cli_adapter = __esm({
|
|
|
3320
3605
|
}
|
|
3321
3606
|
});
|
|
3322
3607
|
|
|
3608
|
+
// src/index.ts
|
|
3609
|
+
init_repo_mesh_types();
|
|
3610
|
+
|
|
3323
3611
|
// src/git/git-executor.ts
|
|
3324
3612
|
import { execFile } from "child_process";
|
|
3325
3613
|
import { constants } from "fs";
|
|
@@ -5117,10 +5405,70 @@ function getSavedProviderSessions(state, filters) {
|
|
|
5117
5405
|
})).sort((a, b) => b.lastUsedAt - a.lastUsedAt);
|
|
5118
5406
|
}
|
|
5119
5407
|
|
|
5408
|
+
// src/index.ts
|
|
5409
|
+
init_mesh_config();
|
|
5410
|
+
init_coordinator_prompt();
|
|
5411
|
+
|
|
5412
|
+
// src/mesh/mesh-sync.ts
|
|
5413
|
+
init_mesh_config();
|
|
5414
|
+
async function syncMeshes(transport) {
|
|
5415
|
+
const result = { pushed: 0, pulled: 0, deleted: 0, errors: [] };
|
|
5416
|
+
let remoteMeshes;
|
|
5417
|
+
try {
|
|
5418
|
+
const res = await transport.listRemoteMeshes();
|
|
5419
|
+
remoteMeshes = res.meshes;
|
|
5420
|
+
} catch (e) {
|
|
5421
|
+
result.errors.push(`Failed to list remote meshes: ${e.message}`);
|
|
5422
|
+
return result;
|
|
5423
|
+
}
|
|
5424
|
+
const localMeshes = listMeshes();
|
|
5425
|
+
const remoteByIdentity = new Map(remoteMeshes.map((m) => [m.repo_identity, m]));
|
|
5426
|
+
const localByIdentity = new Map(localMeshes.map((m) => [m.repoIdentity, m]));
|
|
5427
|
+
for (const local of localMeshes) {
|
|
5428
|
+
if (!remoteByIdentity.has(local.repoIdentity)) {
|
|
5429
|
+
try {
|
|
5430
|
+
await transport.createRemoteMesh({
|
|
5431
|
+
name: local.name,
|
|
5432
|
+
repo_identity: local.repoIdentity,
|
|
5433
|
+
repo_remote_url: local.repoRemoteUrl,
|
|
5434
|
+
default_branch: local.defaultBranch,
|
|
5435
|
+
policy: JSON.stringify(local.policy)
|
|
5436
|
+
});
|
|
5437
|
+
result.pushed++;
|
|
5438
|
+
} catch (e) {
|
|
5439
|
+
result.errors.push(`Push failed for "${local.name}": ${e.message}`);
|
|
5440
|
+
}
|
|
5441
|
+
}
|
|
5442
|
+
}
|
|
5443
|
+
for (const remote of remoteMeshes) {
|
|
5444
|
+
if (!localByIdentity.has(remote.repo_identity)) {
|
|
5445
|
+
try {
|
|
5446
|
+
let policy;
|
|
5447
|
+
try {
|
|
5448
|
+
policy = JSON.parse(remote.policy);
|
|
5449
|
+
} catch {
|
|
5450
|
+
policy = void 0;
|
|
5451
|
+
}
|
|
5452
|
+
createMesh({
|
|
5453
|
+
name: remote.name,
|
|
5454
|
+
repoIdentity: remote.repo_identity,
|
|
5455
|
+
repoRemoteUrl: remote.repo_remote_url || void 0,
|
|
5456
|
+
defaultBranch: remote.default_branch || void 0,
|
|
5457
|
+
policy
|
|
5458
|
+
});
|
|
5459
|
+
result.pulled++;
|
|
5460
|
+
} catch (e) {
|
|
5461
|
+
result.errors.push(`Pull failed for "${remote.name}": ${e.message}`);
|
|
5462
|
+
}
|
|
5463
|
+
}
|
|
5464
|
+
}
|
|
5465
|
+
return result;
|
|
5466
|
+
}
|
|
5467
|
+
|
|
5120
5468
|
// src/config/state-store.ts
|
|
5121
5469
|
init_config();
|
|
5122
|
-
import { existsSync as
|
|
5123
|
-
import { join as
|
|
5470
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
5471
|
+
import { join as join4 } from "path";
|
|
5124
5472
|
var DEFAULT_STATE = {
|
|
5125
5473
|
recentActivity: [],
|
|
5126
5474
|
savedProviderSessions: [],
|
|
@@ -5133,7 +5481,7 @@ function isPlainObject2(value) {
|
|
|
5133
5481
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
5134
5482
|
}
|
|
5135
5483
|
function getStatePath() {
|
|
5136
|
-
return
|
|
5484
|
+
return join4(getConfigDir(), "state.json");
|
|
5137
5485
|
}
|
|
5138
5486
|
function normalizeState(raw) {
|
|
5139
5487
|
const parsed = isPlainObject2(raw) ? raw : {};
|
|
@@ -5169,11 +5517,11 @@ function normalizeState(raw) {
|
|
|
5169
5517
|
}
|
|
5170
5518
|
function loadState() {
|
|
5171
5519
|
const statePath = getStatePath();
|
|
5172
|
-
if (!
|
|
5520
|
+
if (!existsSync4(statePath)) {
|
|
5173
5521
|
return { ...DEFAULT_STATE };
|
|
5174
5522
|
}
|
|
5175
5523
|
try {
|
|
5176
|
-
const raw =
|
|
5524
|
+
const raw = readFileSync3(statePath, "utf-8");
|
|
5177
5525
|
return normalizeState(JSON.parse(raw));
|
|
5178
5526
|
} catch {
|
|
5179
5527
|
return { ...DEFAULT_STATE };
|
|
@@ -5182,7 +5530,7 @@ function loadState() {
|
|
|
5182
5530
|
function saveState(state) {
|
|
5183
5531
|
const statePath = getStatePath();
|
|
5184
5532
|
const normalized = normalizeState(state);
|
|
5185
|
-
|
|
5533
|
+
writeFileSync3(statePath, JSON.stringify(normalized, null, 2), { encoding: "utf-8", mode: 384 });
|
|
5186
5534
|
}
|
|
5187
5535
|
function resetState() {
|
|
5188
5536
|
saveState({ ...DEFAULT_STATE });
|
|
@@ -5190,7 +5538,7 @@ function resetState() {
|
|
|
5190
5538
|
|
|
5191
5539
|
// src/detection/ide-detector.ts
|
|
5192
5540
|
import { execSync } from "child_process";
|
|
5193
|
-
import { existsSync as
|
|
5541
|
+
import { existsSync as existsSync5 } from "fs";
|
|
5194
5542
|
import { platform, homedir as homedir3 } from "os";
|
|
5195
5543
|
import * as path7 from "path";
|
|
5196
5544
|
var BUILTIN_IDE_DEFINITIONS = [];
|
|
@@ -5214,7 +5562,7 @@ function findCliCommand(command) {
|
|
|
5214
5562
|
if (path7.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
|
|
5215
5563
|
const candidate = trimmed.startsWith("~") ? path7.join(homedir3(), trimmed.slice(1)) : trimmed;
|
|
5216
5564
|
const resolved = path7.isAbsolute(candidate) ? candidate : path7.resolve(candidate);
|
|
5217
|
-
return
|
|
5565
|
+
return existsSync5(resolved) ? resolved : null;
|
|
5218
5566
|
}
|
|
5219
5567
|
try {
|
|
5220
5568
|
const result = execSync(
|
|
@@ -5245,9 +5593,9 @@ function checkPathExists(paths) {
|
|
|
5245
5593
|
if (normalized.includes("*")) {
|
|
5246
5594
|
const username = home.split(/[\\/]/).pop() || "";
|
|
5247
5595
|
const resolved = normalized.replace("*", username);
|
|
5248
|
-
if (
|
|
5596
|
+
if (existsSync5(resolved)) return resolved;
|
|
5249
5597
|
} else {
|
|
5250
|
-
if (
|
|
5598
|
+
if (existsSync5(normalized)) return normalized;
|
|
5251
5599
|
}
|
|
5252
5600
|
}
|
|
5253
5601
|
return null;
|
|
@@ -5261,7 +5609,7 @@ async function detectIDEs(providerLoader) {
|
|
|
5261
5609
|
let resolvedCli = cliPath;
|
|
5262
5610
|
if (!resolvedCli && appPath && os21 === "darwin") {
|
|
5263
5611
|
const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
|
|
5264
|
-
if (
|
|
5612
|
+
if (existsSync5(bundledCli)) resolvedCli = bundledCli;
|
|
5265
5613
|
}
|
|
5266
5614
|
if (!resolvedCli && appPath && os21 === "win32") {
|
|
5267
5615
|
const { dirname: dirname7 } = await import("path");
|
|
@@ -5274,7 +5622,7 @@ async function detectIDEs(providerLoader) {
|
|
|
5274
5622
|
`${appDir}\\\\resources\\\\app\\\\bin\\\\${def.cli}.cmd`
|
|
5275
5623
|
];
|
|
5276
5624
|
for (const c of candidates) {
|
|
5277
|
-
if (
|
|
5625
|
+
if (existsSync5(c)) {
|
|
5278
5626
|
resolvedCli = c;
|
|
5279
5627
|
break;
|
|
5280
5628
|
}
|
|
@@ -5300,7 +5648,7 @@ async function detectIDEs(providerLoader) {
|
|
|
5300
5648
|
import { exec } from "child_process";
|
|
5301
5649
|
import * as os2 from "os";
|
|
5302
5650
|
import * as path8 from "path";
|
|
5303
|
-
import { existsSync as
|
|
5651
|
+
import { existsSync as existsSync6 } from "fs";
|
|
5304
5652
|
function parseVersion(raw) {
|
|
5305
5653
|
const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
|
|
5306
5654
|
return match ? match[1] : raw.split("\n")[0].slice(0, 100);
|
|
@@ -5324,7 +5672,7 @@ function resolveCommandPath(command) {
|
|
|
5324
5672
|
if (isExplicitCommandPath(trimmed)) {
|
|
5325
5673
|
const expanded = expandHome(trimmed);
|
|
5326
5674
|
const candidate = path8.isAbsolute(expanded) ? expanded : path8.resolve(expanded);
|
|
5327
|
-
return
|
|
5675
|
+
return existsSync6(candidate) ? candidate : null;
|
|
5328
5676
|
}
|
|
5329
5677
|
return null;
|
|
5330
5678
|
}
|
|
@@ -10952,7 +11300,7 @@ function resolveLegacyProviderScript(fn, scriptName, params) {
|
|
|
10952
11300
|
import * as fs4 from "fs";
|
|
10953
11301
|
import * as os6 from "os";
|
|
10954
11302
|
import * as path11 from "path";
|
|
10955
|
-
import { randomUUID as
|
|
11303
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
10956
11304
|
|
|
10957
11305
|
// src/providers/provider-input-support.ts
|
|
10958
11306
|
var VALID_INPUT_MEDIA_TYPES = /* @__PURE__ */ new Set(["text", "image", "audio", "video", "resource"]);
|
|
@@ -11469,7 +11817,7 @@ function safeBundleIdSegment(value, fallback) {
|
|
|
11469
11817
|
function createChatDebugBundleId(targetSessionId) {
|
|
11470
11818
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:.]/g, "").replace("T", "T").replace("Z", "Z");
|
|
11471
11819
|
const sessionSegment = safeBundleIdSegment(targetSessionId, "unknown-session");
|
|
11472
|
-
return `chat-debug-${timestamp}-${sessionSegment}-${
|
|
11820
|
+
return `chat-debug-${timestamp}-${sessionSegment}-${randomUUID5().slice(0, 8)}`;
|
|
11473
11821
|
}
|
|
11474
11822
|
function buildChatDebugBundleSummary(bundle) {
|
|
11475
11823
|
const target = bundle.target && typeof bundle.target === "object" ? bundle.target : {};
|
|
@@ -14063,7 +14411,7 @@ init_provider_cli_adapter();
|
|
|
14063
14411
|
import * as os13 from "os";
|
|
14064
14412
|
import * as path16 from "path";
|
|
14065
14413
|
import * as crypto4 from "crypto";
|
|
14066
|
-
import { existsSync as
|
|
14414
|
+
import { existsSync as existsSync11 } from "fs";
|
|
14067
14415
|
import { execFileSync } from "child_process";
|
|
14068
14416
|
import chalk from "chalk";
|
|
14069
14417
|
init_config();
|
|
@@ -16205,7 +16553,7 @@ function commandExists(command) {
|
|
|
16205
16553
|
const trimmed = command.trim();
|
|
16206
16554
|
if (!trimmed) return false;
|
|
16207
16555
|
if (isExplicitCommand(trimmed)) {
|
|
16208
|
-
return
|
|
16556
|
+
return existsSync11(expandExecutable(trimmed));
|
|
16209
16557
|
}
|
|
16210
16558
|
try {
|
|
16211
16559
|
execFileSync(process.platform === "win32" ? "where" : "which", [trimmed], {
|
|
@@ -20718,6 +21066,142 @@ var DaemonCommandRouter = class {
|
|
|
20718
21066
|
updateConfig({ machineNickname: nickname || null });
|
|
20719
21067
|
return { success: true };
|
|
20720
21068
|
}
|
|
21069
|
+
// ─── Mesh CRUD (local meshes.json) ───
|
|
21070
|
+
case "list_meshes": {
|
|
21071
|
+
try {
|
|
21072
|
+
const { listMeshes: listMeshes2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
21073
|
+
return { success: true, meshes: listMeshes2() };
|
|
21074
|
+
} catch (e) {
|
|
21075
|
+
return { success: false, error: e.message };
|
|
21076
|
+
}
|
|
21077
|
+
}
|
|
21078
|
+
case "get_mesh": {
|
|
21079
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
21080
|
+
if (!meshId) return { success: false, error: "meshId required" };
|
|
21081
|
+
try {
|
|
21082
|
+
const { getMesh: getMesh3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
21083
|
+
const mesh = getMesh3(meshId);
|
|
21084
|
+
if (!mesh) return { success: false, error: "Mesh not found" };
|
|
21085
|
+
return { success: true, mesh };
|
|
21086
|
+
} catch (e) {
|
|
21087
|
+
return { success: false, error: e.message };
|
|
21088
|
+
}
|
|
21089
|
+
}
|
|
21090
|
+
case "create_mesh": {
|
|
21091
|
+
const name = typeof args?.name === "string" ? args.name.trim() : "";
|
|
21092
|
+
const repoIdentity = typeof args?.repoIdentity === "string" ? args.repoIdentity.trim() : "";
|
|
21093
|
+
const repoRemoteUrl = typeof args?.repoRemoteUrl === "string" ? args.repoRemoteUrl.trim() : void 0;
|
|
21094
|
+
const defaultBranch = typeof args?.defaultBranch === "string" ? args.defaultBranch.trim() : void 0;
|
|
21095
|
+
if (!name) return { success: false, error: "name required" };
|
|
21096
|
+
try {
|
|
21097
|
+
const { createMesh: createMesh2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
21098
|
+
const mesh = createMesh2({ name, repoIdentity, repoRemoteUrl, defaultBranch });
|
|
21099
|
+
return { success: true, mesh };
|
|
21100
|
+
} catch (e) {
|
|
21101
|
+
return { success: false, error: e.message };
|
|
21102
|
+
}
|
|
21103
|
+
}
|
|
21104
|
+
case "delete_mesh": {
|
|
21105
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
21106
|
+
if (!meshId) return { success: false, error: "meshId required" };
|
|
21107
|
+
try {
|
|
21108
|
+
const { deleteMesh: deleteMesh3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
21109
|
+
const deleted = deleteMesh3(meshId);
|
|
21110
|
+
return { success: true, deleted };
|
|
21111
|
+
} catch (e) {
|
|
21112
|
+
return { success: false, error: e.message };
|
|
21113
|
+
}
|
|
21114
|
+
}
|
|
21115
|
+
case "add_mesh_node": {
|
|
21116
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
21117
|
+
const workspace = typeof args?.workspace === "string" ? args.workspace.trim() : "";
|
|
21118
|
+
if (!meshId) return { success: false, error: "meshId required" };
|
|
21119
|
+
if (!workspace) return { success: false, error: "workspace required" };
|
|
21120
|
+
try {
|
|
21121
|
+
const { addNode: addNode3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
21122
|
+
const node = addNode3(meshId, { workspace });
|
|
21123
|
+
if (!node) return { success: false, error: "Mesh not found" };
|
|
21124
|
+
return { success: true, node };
|
|
21125
|
+
} catch (e) {
|
|
21126
|
+
return { success: false, error: e.message };
|
|
21127
|
+
}
|
|
21128
|
+
}
|
|
21129
|
+
case "remove_mesh_node": {
|
|
21130
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
21131
|
+
const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
|
|
21132
|
+
if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
|
|
21133
|
+
try {
|
|
21134
|
+
const { removeNode: removeNode3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
21135
|
+
const removed = removeNode3(meshId, nodeId);
|
|
21136
|
+
return { success: true, removed };
|
|
21137
|
+
} catch (e) {
|
|
21138
|
+
return { success: false, error: e.message };
|
|
21139
|
+
}
|
|
21140
|
+
}
|
|
21141
|
+
// ─── Mesh Coordinator Launch ───
|
|
21142
|
+
case "launch_mesh_coordinator": {
|
|
21143
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
21144
|
+
const cliType = typeof args?.cliType === "string" ? args.cliType.trim() : "claude-cli";
|
|
21145
|
+
if (!meshId) return { success: false, error: "meshId required" };
|
|
21146
|
+
try {
|
|
21147
|
+
const { getMesh: getMesh3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
21148
|
+
const { buildCoordinatorSystemPrompt: buildCoordinatorSystemPrompt2 } = await Promise.resolve().then(() => (init_coordinator_prompt(), coordinator_prompt_exports));
|
|
21149
|
+
const mesh = getMesh3(meshId);
|
|
21150
|
+
if (!mesh) return { success: false, error: "Mesh not found" };
|
|
21151
|
+
if (mesh.nodes.length === 0) return { success: false, error: "No nodes in mesh" };
|
|
21152
|
+
const workspace = mesh.nodes[0].workspace;
|
|
21153
|
+
const { existsSync: existsSync21, readFileSync: readFileSync15, writeFileSync: writeFileSync12, copyFileSync: copyFileSync3 } = await import("fs");
|
|
21154
|
+
const { join: join23 } = await import("path");
|
|
21155
|
+
const mcpConfigPath = join23(workspace, ".mcp.json");
|
|
21156
|
+
const hadExistingMcpConfig = existsSync21(mcpConfigPath);
|
|
21157
|
+
let existingMcpConfig = {};
|
|
21158
|
+
if (hadExistingMcpConfig) {
|
|
21159
|
+
try {
|
|
21160
|
+
existingMcpConfig = JSON.parse(readFileSync15(mcpConfigPath, "utf-8"));
|
|
21161
|
+
copyFileSync3(mcpConfigPath, mcpConfigPath + ".backup");
|
|
21162
|
+
} catch {
|
|
21163
|
+
}
|
|
21164
|
+
}
|
|
21165
|
+
const mcpConfig = {
|
|
21166
|
+
...existingMcpConfig,
|
|
21167
|
+
mcpServers: {
|
|
21168
|
+
...existingMcpConfig.mcpServers || {},
|
|
21169
|
+
"adhdev-mesh": {
|
|
21170
|
+
command: "adhdev-mcp",
|
|
21171
|
+
args: ["--repo-mesh", meshId]
|
|
21172
|
+
}
|
|
21173
|
+
}
|
|
21174
|
+
};
|
|
21175
|
+
writeFileSync12(mcpConfigPath, JSON.stringify(mcpConfig, null, 2), "utf-8");
|
|
21176
|
+
LOG.info("MeshCoordinator", `Wrote .mcp.json to ${workspace} with adhdev-mesh server`);
|
|
21177
|
+
let systemPrompt = "";
|
|
21178
|
+
try {
|
|
21179
|
+
systemPrompt = buildCoordinatorSystemPrompt2({ mesh });
|
|
21180
|
+
} catch {
|
|
21181
|
+
systemPrompt = `You are a Repo Mesh Coordinator for "${mesh.name}". Use the adhdev-mesh MCP tools (mesh_status, mesh_list_nodes, mesh_send_task, mesh_read_chat, mesh_launch_session, etc.) to orchestrate work across ${mesh.nodes.length} node(s).`;
|
|
21182
|
+
}
|
|
21183
|
+
const launchResult = await this.deps.cliManager.handleCliCommand("launch_cli", {
|
|
21184
|
+
cliType,
|
|
21185
|
+
dir: workspace,
|
|
21186
|
+
initialPrompt: systemPrompt
|
|
21187
|
+
});
|
|
21188
|
+
if (!launchResult?.success) {
|
|
21189
|
+
return { success: false, error: launchResult?.error || "Failed to launch CLI session" };
|
|
21190
|
+
}
|
|
21191
|
+
LOG.info("MeshCoordinator", `Launched ${cliType} coordinator for mesh ${meshId} in ${workspace}`);
|
|
21192
|
+
return {
|
|
21193
|
+
success: true,
|
|
21194
|
+
meshId,
|
|
21195
|
+
cliType,
|
|
21196
|
+
workspace,
|
|
21197
|
+
sessionId: launchResult.sessionId || launchResult.id,
|
|
21198
|
+
mcpConfigWritten: true
|
|
21199
|
+
};
|
|
21200
|
+
} catch (e) {
|
|
21201
|
+
LOG.error("MeshCoordinator", `Failed: ${e.message}`);
|
|
21202
|
+
return { success: false, error: e.message };
|
|
21203
|
+
}
|
|
21204
|
+
}
|
|
20721
21205
|
default:
|
|
20722
21206
|
break;
|
|
20723
21207
|
}
|
|
@@ -28683,6 +29167,7 @@ export {
|
|
|
28683
29167
|
DEFAULT_DAEMON_PORT,
|
|
28684
29168
|
DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS,
|
|
28685
29169
|
DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS,
|
|
29170
|
+
DEFAULT_MESH_POLICY,
|
|
28686
29171
|
DEFAULT_SESSION_HOST_APP_NAME,
|
|
28687
29172
|
DEFAULT_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS,
|
|
28688
29173
|
DEFAULT_SESSION_HOST_READY_TIMEOUT_MS,
|
|
@@ -28716,11 +29201,13 @@ export {
|
|
|
28716
29201
|
SessionHostPtyTransportFactory,
|
|
28717
29202
|
TurnSnapshotTracker,
|
|
28718
29203
|
VersionArchive,
|
|
29204
|
+
addNode,
|
|
28719
29205
|
appendRecentActivity,
|
|
28720
29206
|
buildAssistantChatMessage,
|
|
28721
29207
|
buildChatMessage,
|
|
28722
29208
|
buildChatMessageSignature,
|
|
28723
29209
|
buildChatTailDeliverySignature,
|
|
29210
|
+
buildCoordinatorSystemPrompt,
|
|
28724
29211
|
buildMachineInfo,
|
|
28725
29212
|
buildPinnedGlobalInstallCommand,
|
|
28726
29213
|
buildRuntimeSystemChatMessage,
|
|
@@ -28743,6 +29230,8 @@ export {
|
|
|
28743
29230
|
createGitSnapshotStore,
|
|
28744
29231
|
createGitWorkspaceMonitor,
|
|
28745
29232
|
createInteractionId,
|
|
29233
|
+
createMesh,
|
|
29234
|
+
deleteMesh,
|
|
28746
29235
|
detectAllVersions,
|
|
28747
29236
|
detectCLIs,
|
|
28748
29237
|
detectIDEs,
|
|
@@ -28761,6 +29250,8 @@ export {
|
|
|
28761
29250
|
getGitRepoStatus,
|
|
28762
29251
|
getHostMemorySnapshot,
|
|
28763
29252
|
getLogLevel,
|
|
29253
|
+
getMesh,
|
|
29254
|
+
getMeshByRepo,
|
|
28764
29255
|
getNpmExecOptions,
|
|
28765
29256
|
getRecentActivity,
|
|
28766
29257
|
getRecentCommands,
|
|
@@ -28791,6 +29282,7 @@ export {
|
|
|
28791
29282
|
launchIDE,
|
|
28792
29283
|
launchWithCdp,
|
|
28793
29284
|
listHostedCliRuntimes,
|
|
29285
|
+
listMeshes,
|
|
28794
29286
|
loadConfig,
|
|
28795
29287
|
loadState,
|
|
28796
29288
|
logCommand,
|
|
@@ -28806,6 +29298,7 @@ export {
|
|
|
28806
29298
|
normalizeInputEnvelope,
|
|
28807
29299
|
normalizeManagedStatus,
|
|
28808
29300
|
normalizeMessageParts,
|
|
29301
|
+
normalizeRepoIdentity,
|
|
28809
29302
|
normalizeSessionModalFields,
|
|
28810
29303
|
parsePorcelainV2Status,
|
|
28811
29304
|
parseProviderSourceConfigUpdate,
|
|
@@ -28817,6 +29310,7 @@ export {
|
|
|
28817
29310
|
readChatHistory,
|
|
28818
29311
|
recordDebugTrace,
|
|
28819
29312
|
registerExtensionProviders,
|
|
29313
|
+
removeNode,
|
|
28820
29314
|
resetConfig,
|
|
28821
29315
|
resetDebugRuntimeConfig,
|
|
28822
29316
|
resetState,
|
|
@@ -28839,7 +29333,10 @@ export {
|
|
|
28839
29333
|
spawnDetachedDaemonUpgradeHelper,
|
|
28840
29334
|
startDaemonDevSupport,
|
|
28841
29335
|
summarizeGitStatus,
|
|
29336
|
+
syncMeshes,
|
|
28842
29337
|
updateConfig,
|
|
29338
|
+
updateMesh,
|
|
29339
|
+
updateNode,
|
|
28843
29340
|
upsertSavedProviderSession
|
|
28844
29341
|
};
|
|
28845
29342
|
//# sourceMappingURL=index.mjs.map
|