swarmdo 1.58.15 → 1.58.23
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/.claude/agents/base-template-generator.md +1 -1
- package/.claude/agents/goal/code-goal-planner.md +1 -1
- package/.claude/helpers/agent-bridge-hook.cjs +174 -0
- package/.claude/helpers/hook-handler.cjs +34 -8
- package/.claude/settings.json +16 -0
- package/.claude/settings.local.json +7 -0
- package/README.md +2 -2
- package/package.json +1 -1
- package/v3/@swarmdo/cli/.claude/helpers/agent-bridge-hook.cjs +174 -0
- package/v3/@swarmdo/cli/.claude/helpers/auto-memory-hook.mjs +274 -27
- package/v3/@swarmdo/cli/.claude/helpers/hook-handler.cjs +150 -107
- package/v3/@swarmdo/cli/.claude/helpers/intelligence.cjs +196 -989
- package/v3/@swarmdo/cli/.claude/helpers/{memory.js → memory.cjs} +10 -9
- package/v3/@swarmdo/cli/.claude/helpers/router.cjs +88 -0
- package/v3/@swarmdo/cli/.claude/helpers/session.cjs +125 -0
- package/v3/@swarmdo/cli/.claude/settings.json +22 -10
- package/v3/@swarmdo/cli/dist/src/commands/config.js +5 -4
- package/v3/@swarmdo/cli/dist/src/init/executor.d.ts +15 -0
- package/v3/@swarmdo/cli/dist/src/init/executor.js +24 -1
- package/v3/@swarmdo/cli/dist/src/init/settings-generator.js +33 -5
- package/v3/@swarmdo/cli/dist/src/mcp-tools/agent-tools.js +1 -1
- package/v3/@swarmdo/cli/dist/src/mcp-tools/neural-tools.d.ts +1 -0
- package/v3/@swarmdo/cli/dist/src/mcp-tools/neural-tools.js +132 -78
- package/v3/@swarmdo/cli/package.json +1 -1
- package/v3/@swarmdo/cli-core/dist/src/index.js +1 -1
- package/v3/@swarmdo/codex/dist/cli.js +0 -0
- package/v3/@swarmdo/hooks/dist/cli/guidance-cli.js +0 -0
- package/v3/@swarmdo/hooks/dist/statusline/index.d.ts +3 -3
- package/v3/@swarmdo/hooks/dist/statusline/index.js +3 -3
- package/v3/@swarmdo/integration/dist/__tests__/agentic-flow-agent.test.js +1 -4
- package/v3/@swarmdo/neural/dist/flash-attention.d.ts +1 -1
- package/v3/@swarmdo/neural/dist/flash-attention.js +1 -1
- package/v3/@swarmdo/plugin-agent-federation/dist/plugin.js +1 -1
- package/v3/@swarmdo/plugin-agent-federation/dist/transport/midstream-aware-loader.js +2 -2
- package/v3/@swarmdo/plugin-iot-cognitum/dist/application/index.d.ts +2 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/application/index.js +2 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/application/iot-coordinator.d.ts +138 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/application/iot-coordinator.js +418 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/bin.d.ts +15 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/bin.js +240 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/cli-commands.d.ts +7 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/cli-commands.js +508 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-agent.d.ts +54 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-agent.js +2 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-fleet.d.ts +46 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-fleet.js +2 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-trust-level.d.ts +18 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/device-trust-level.js +28 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/index.d.ts +5 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/index.js +2 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/telemetry.d.ts +35 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/entities/telemetry.js +2 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/device-repository.d.ts +11 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/device-repository.js +2 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/fleet-repository.d.ts +9 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/fleet-repository.js +2 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/index.d.ts +5 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/index.js +2 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/telemetry-repository.d.ts +12 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/telemetry-repository.js +2 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/trust-history-repository.d.ts +19 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/repositories/trust-history-repository.js +2 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/anomaly-detection-service.d.ts +44 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/anomaly-detection-service.js +117 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/device-lifecycle-service.d.ts +70 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/device-lifecycle-service.js +194 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/firmware-orchestration-service.d.ts +69 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/firmware-orchestration-service.js +139 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/fleet-topology-service.d.ts +34 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/fleet-topology-service.js +101 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/index.d.ts +10 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/index.js +10 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/mesh-service.d.ts +64 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/mesh-service.js +70 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/sona-integration-service.d.ts +44 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/sona-integration-service.js +79 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/telemetry-ingestion-service.d.ts +37 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/telemetry-ingestion-service.js +39 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/telemetry-service.d.ts +67 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/telemetry-service.js +58 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/witness-verification-service.d.ts +45 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/domain/services/witness-verification-service.js +63 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/index.d.ts +26 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/index.js +17 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/agentdb-telemetry-repository.d.ts +51 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/agentdb-telemetry-repository.js +110 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-device-repository.d.ts +13 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-device-repository.js +25 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-fleet-repository.d.ts +11 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-fleet-repository.js +19 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-trust-history-repository.d.ts +11 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/in-memory-trust-history-repository.js +27 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/index.d.ts +6 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/index.js +6 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/seed-client-factory.d.ts +41 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/infrastructure/seed-client-factory.js +64 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/mcp-tools.d.ts +8 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/mcp-tools.js +703 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/plugin.d.ts +23 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/plugin.js +239 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/anomaly-scan-worker.d.ts +17 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/anomaly-scan-worker.js +42 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/firmware-watch-worker.d.ts +18 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/firmware-watch-worker.js +45 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/health-probe-worker.d.ts +19 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/health-probe-worker.js +50 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/mesh-sync-worker.d.ts +19 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/mesh-sync-worker.js +47 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/telemetry-ingest-worker.d.ts +17 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/telemetry-ingest-worker.js +40 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/witness-audit-worker.d.ts +18 -0
- package/v3/@swarmdo/plugin-iot-cognitum/dist/workers/witness-audit-worker.js +51 -0
- package/v3/@swarmdo/providers/dist/swarmvector-provider.d.ts +1 -1
- package/v3/@swarmdo/providers/dist/swarmvector-provider.js +1 -1
- package/v3/@swarmdo/security/dist/input-validator.d.ts +6 -6
- package/v3/@swarmdo/shared/dist/hooks/safety/git-commit.js +1 -1
- package/v3/@swarmdo/shared/dist/mcp/transport/http.js +1 -8
- package/v3/@swarmdo/testing/dist/fixtures/agent-fixtures.d.ts +214 -0
- package/v3/@swarmdo/testing/dist/fixtures/agent-fixtures.js +595 -0
- package/v3/@swarmdo/testing/dist/fixtures/agents.d.ts +76 -0
- package/v3/@swarmdo/testing/dist/fixtures/agents.js +158 -0
- package/v3/@swarmdo/testing/dist/fixtures/configurations.d.ts +157 -0
- package/v3/@swarmdo/testing/dist/fixtures/configurations.js +337 -0
- package/v3/@swarmdo/testing/dist/fixtures/index.d.ts +10 -0
- package/v3/@swarmdo/testing/dist/fixtures/index.js +17 -0
- package/v3/@swarmdo/testing/dist/fixtures/mcp-fixtures.d.ts +329 -0
- package/v3/@swarmdo/testing/dist/fixtures/mcp-fixtures.js +714 -0
- package/v3/@swarmdo/testing/dist/fixtures/memory-entries.d.ts +159 -0
- package/v3/@swarmdo/testing/dist/fixtures/memory-entries.js +262 -0
- package/v3/@swarmdo/testing/dist/fixtures/memory-fixtures.d.ts +290 -0
- package/v3/@swarmdo/testing/dist/fixtures/memory-fixtures.js +547 -0
- package/v3/@swarmdo/testing/dist/fixtures/swarm-fixtures.d.ts +289 -0
- package/v3/@swarmdo/testing/dist/fixtures/swarm-fixtures.js +534 -0
- package/v3/@swarmdo/testing/dist/fixtures/tasks.d.ts +106 -0
- package/v3/@swarmdo/testing/dist/fixtures/tasks.js +229 -0
- package/v3/@swarmdo/testing/dist/helpers/assertion-helpers.d.ts +213 -0
- package/v3/@swarmdo/testing/dist/helpers/assertion-helpers.js +435 -0
- package/v3/@swarmdo/testing/dist/helpers/assertions.d.ts +135 -0
- package/v3/@swarmdo/testing/dist/helpers/assertions.js +217 -0
- package/v3/@swarmdo/testing/dist/helpers/create-mock.d.ts +96 -0
- package/v3/@swarmdo/testing/dist/helpers/create-mock.js +164 -0
- package/v3/@swarmdo/testing/dist/helpers/index.d.ts +14 -0
- package/v3/@swarmdo/testing/dist/helpers/index.js +22 -0
- package/v3/@swarmdo/testing/dist/helpers/mock-factory.d.ts +323 -0
- package/v3/@swarmdo/testing/dist/helpers/mock-factory.js +350 -0
- package/v3/@swarmdo/testing/dist/helpers/setup-teardown.d.ts +271 -0
- package/v3/@swarmdo/testing/dist/helpers/setup-teardown.js +446 -0
- package/v3/@swarmdo/testing/dist/helpers/swarm-instance.d.ts +142 -0
- package/v3/@swarmdo/testing/dist/helpers/swarm-instance.js +205 -0
- package/v3/@swarmdo/testing/dist/helpers/test-application.d.ts +190 -0
- package/v3/@swarmdo/testing/dist/helpers/test-application.js +138 -0
- package/v3/@swarmdo/testing/dist/helpers/test-utils.d.ts +274 -0
- package/v3/@swarmdo/testing/dist/helpers/test-utils.js +456 -0
- package/v3/@swarmdo/testing/dist/index.d.ts +42 -0
- package/v3/@swarmdo/testing/dist/index.js +66 -0
- package/v3/@swarmdo/testing/dist/mocks/index.d.ts +8 -0
- package/v3/@swarmdo/testing/dist/mocks/index.js +10 -0
- package/v3/@swarmdo/testing/dist/mocks/mock-mcp-client.d.ts +206 -0
- package/v3/@swarmdo/testing/dist/mocks/mock-mcp-client.js +575 -0
- package/v3/@swarmdo/testing/dist/mocks/mock-services.d.ts +287 -0
- package/v3/@swarmdo/testing/dist/mocks/mock-services.js +563 -0
- package/v3/@swarmdo/testing/dist/regression/api-contract.d.ts +109 -0
- package/v3/@swarmdo/testing/dist/regression/api-contract.js +374 -0
- package/v3/@swarmdo/testing/dist/regression/index.d.ts +18 -0
- package/v3/@swarmdo/testing/dist/regression/index.js +18 -0
- package/v3/@swarmdo/testing/dist/regression/integration-regression.d.ts +67 -0
- package/v3/@swarmdo/testing/dist/regression/integration-regression.js +334 -0
- package/v3/@swarmdo/testing/dist/regression/performance-baseline.d.ts +106 -0
- package/v3/@swarmdo/testing/dist/regression/performance-baseline.js +268 -0
- package/v3/@swarmdo/testing/dist/regression/regression-runner.d.ts +124 -0
- package/v3/@swarmdo/testing/dist/regression/regression-runner.js +227 -0
- package/v3/@swarmdo/testing/dist/regression/security-regression.d.ts +73 -0
- package/v3/@swarmdo/testing/dist/regression/security-regression.js +258 -0
- package/v3/@swarmdo/testing/dist/setup.d.ts +43 -0
- package/v3/@swarmdo/testing/dist/setup.js +91 -0
- package/v3/@swarmdo/testing/dist/v2-compat/api-compat.test.d.ts +10 -0
- package/v3/@swarmdo/testing/dist/v2-compat/api-compat.test.js +433 -0
- package/v3/@swarmdo/testing/dist/v2-compat/cli-compat.test.d.ts +10 -0
- package/v3/@swarmdo/testing/dist/v2-compat/cli-compat.test.js +352 -0
- package/v3/@swarmdo/testing/dist/v2-compat/compatibility-validator.d.ts +206 -0
- package/v3/@swarmdo/testing/dist/v2-compat/compatibility-validator.js +839 -0
- package/v3/@swarmdo/testing/dist/v2-compat/hooks-compat.test.d.ts +10 -0
- package/v3/@swarmdo/testing/dist/v2-compat/hooks-compat.test.js +428 -0
- package/v3/@swarmdo/testing/dist/v2-compat/index.d.ts +38 -0
- package/v3/@swarmdo/testing/dist/v2-compat/index.js +41 -0
- package/v3/@swarmdo/testing/dist/v2-compat/mcp-compat.test.d.ts +10 -0
- package/v3/@swarmdo/testing/dist/v2-compat/mcp-compat.test.js +419 -0
- package/v3/@swarmdo/testing/dist/v2-compat/report-generator.d.ts +34 -0
- package/v3/@swarmdo/testing/dist/v2-compat/report-generator.js +372 -0
- package/v3/@swarmdo/cli/.claude/helpers/router.js +0 -105
- package/v3/@swarmdo/cli/.claude/helpers/session.js +0 -157
- package/v3/@swarmdo/cli/.claude/helpers/statusline.js +0 -352
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: base-template-generator
|
|
3
|
-
description: Use this agent when you need to create foundational templates, boilerplate code, or starter configurations for new projects, components, or features. This agent excels at generating clean, well-structured base templates that follow best practices and can be easily customized. Examples: <example>Context: User needs to start a new React component and wants a solid foundation. user: 'I need to create a new user profile component' assistant: 'I'll use the base-template-generator agent to create a comprehensive React component template with proper structure, TypeScript definitions, and styling setup.' <commentary>Since the user needs a foundational template for a new component, use the base-template-generator agent to create a well-structured starting point.</commentary></example> <example>Context: User is setting up a new API endpoint and needs a template. user: 'Can you help me set up a new REST API endpoint for user management?' assistant: 'I'll use the base-template-generator agent to create a complete API endpoint template with proper error handling, validation, and documentation structure.' <commentary>The user needs a foundational template for an API endpoint, so use the base-template-generator agent to provide a comprehensive starting point.</commentary></example>
|
|
3
|
+
description: "Use this agent when you need to create foundational templates, boilerplate code, or starter configurations for new projects, components, or features. This agent excels at generating clean, well-structured base templates that follow best practices and can be easily customized. Examples: <example>Context: User needs to start a new React component and wants a solid foundation. user: 'I need to create a new user profile component' assistant: 'I'll use the base-template-generator agent to create a comprehensive React component template with proper structure, TypeScript definitions, and styling setup.' <commentary>Since the user needs a foundational template for a new component, use the base-template-generator agent to create a well-structured starting point.</commentary></example> <example>Context: User is setting up a new API endpoint and needs a template. user: 'Can you help me set up a new REST API endpoint for user management?' assistant: 'I'll use the base-template-generator agent to create a complete API endpoint template with proper error handling, validation, and documentation structure.' <commentary>The user needs a foundational template for an API endpoint, so use the base-template-generator agent to provide a comprehensive starting point.</commentary></example>"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
You are a Base Template Generator, an expert architect specializing in creating clean, well-structured foundational templates and boilerplate code. Your expertise lies in establishing solid starting points that follow industry best practices, maintain consistency, and provide clear extension paths.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: code-goal-planner
|
|
3
|
-
description: Code-centric Goal-Oriented Action Planning specialist that creates intelligent plans for software development objectives. Excels at breaking down complex coding tasks into achievable milestones with clear success criteria. Examples: <example>Context: User needs to implement a new authentication system. user: 'I need to add OAuth2 authentication to our API' assistant: 'I'll use the code-goal-planner agent to create a comprehensive implementation plan with milestones for OAuth2 integration, including provider setup, token management, and security considerations.' <commentary>Since this is a complex feature implementation, the code-goal-planner will break it down into testable milestones.</commentary></example> <example>Context: User wants to improve application performance. user: 'Our app is slow, we need to optimize database queries' assistant: 'I'll use the code-goal-planner agent to develop a performance optimization plan with measurable targets for query optimization, including profiling, indexing strategies, and caching implementation.' <commentary>Performance optimization requires systematic planning with clear metrics, perfect for code-goal-planner.</commentary></example>
|
|
3
|
+
description: "Code-centric Goal-Oriented Action Planning specialist that creates intelligent plans for software development objectives. Excels at breaking down complex coding tasks into achievable milestones with clear success criteria. Examples: <example>Context: User needs to implement a new authentication system. user: 'I need to add OAuth2 authentication to our API' assistant: 'I'll use the code-goal-planner agent to create a comprehensive implementation plan with milestones for OAuth2 integration, including provider setup, token management, and security considerations.' <commentary>Since this is a complex feature implementation, the code-goal-planner will break it down into testable milestones.</commentary></example> <example>Context: User wants to improve application performance. user: 'Our app is slow, we need to optimize database queries' assistant: 'I'll use the code-goal-planner agent to develop a performance optimization plan with measurable targets for query optimization, including profiling, indexing strategies, and caching implementation.' <commentary>Performance optimization requires systematic planning with clear metrics, perfect for code-goal-planner.</commentary></example>"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
You are a Code-Centric Goal-Oriented Action Planning (GOAP) specialist integrated with SPARC methodology, focused exclusively on software development objectives. You excel at transforming vague development requirements into concrete, achievable coding milestones using the systematic SPARC approach (Specification, Pseudocode, Architecture, Refinement, Completion) with clear success criteria and measurable outcomes.
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agent-bridge-hook.cjs — bind real Claude Code subagents into Swarmdo's
|
|
3
|
+
* canonical agent registry, automatically.
|
|
4
|
+
*
|
|
5
|
+
* #108. `src/agent-bridge/bridge.ts` was written to solve "a session can run
|
|
6
|
+
* four Claude Code agents while `swarmdo agent list` stays empty", and is pure
|
|
7
|
+
* by design because "All fs / MCP / spawning lives in the command + hook
|
|
8
|
+
* layers". The command layer got built (`swarmdo agent bridge register`); the
|
|
9
|
+
* hook layer never did. So nothing ever registered a Task/Agent-tool subagent,
|
|
10
|
+
* `.swarmdo/agents/store.json` stayed `{"agents":{}}`, and the statusline's
|
|
11
|
+
* `🐝 Swarms N 🤖 Agents M` sat at 0/0 forever. This file is that missing
|
|
12
|
+
* hook layer.
|
|
13
|
+
*
|
|
14
|
+
* Why shell out to the CLI instead of writing the store here: registering is
|
|
15
|
+
* not just a store write — `agent_bridge_register` also auto-forms a swarm from
|
|
16
|
+
* config and enrolls the agent (agent-tools.ts:462), which is what makes the
|
|
17
|
+
* Swarms counter move too. bridge.ts's own rule is to go through the canonical
|
|
18
|
+
* registerAgent path — "NO parallel store". Reimplementing that in CJS would
|
|
19
|
+
* fork the registry. The CLI costs ~850ms to boot (it loads ONNX), far too slow
|
|
20
|
+
* to block every subagent spawn, so calls are fired detached and unref'd: the
|
|
21
|
+
* hook returns immediately and the registry lands ~1s later. The statusline
|
|
22
|
+
* polls every 5s, so eventual consistency is invisible here.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const fs = require('fs');
|
|
26
|
+
const os = require('os');
|
|
27
|
+
const path = require('path');
|
|
28
|
+
const { spawn } = require('child_process');
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Mirror of src/agent-bridge/bridge.ts `bridgeAgentId` — kept in sync by hand,
|
|
32
|
+
* because this hook is standalone CommonJS and cannot import the ESM engine
|
|
33
|
+
* (the same constraint router.cjs already works under for classifyPrompt).
|
|
34
|
+
* __tests__/agent-bridge-hook.test.ts asserts the two implementations agree.
|
|
35
|
+
*
|
|
36
|
+
* Deterministic, so SubagentStart and SubagentStop derive the SAME id from the
|
|
37
|
+
* same agent_id and the stop reliably terminates the record the start created.
|
|
38
|
+
*/
|
|
39
|
+
function bridgeAgentId(d) {
|
|
40
|
+
const sess = d.sessionId
|
|
41
|
+
? d.sessionId.replace(/[^A-Za-z0-9]/g, '').slice(0, 8) || 'nosess'
|
|
42
|
+
: 'nosess';
|
|
43
|
+
const safe = String(d.name || '').replace(/[^A-Za-z0-9_-]+/g, '-').replace(/^-+|-+$/g, '') || 'agent';
|
|
44
|
+
return `cc-${sess}-${safe}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Locate an installed CLI bin. Mirror of statusline.cjs resolveCliBin() — same
|
|
49
|
+
* candidate order, same reason (#2337): never `npx @latest` from a hook.
|
|
50
|
+
* Returns null when nothing is installed, in which case we no-op rather than
|
|
51
|
+
* pay a registry round-trip on every subagent spawn.
|
|
52
|
+
*/
|
|
53
|
+
function resolveCliBin(cwd) {
|
|
54
|
+
try {
|
|
55
|
+
const home = os.homedir();
|
|
56
|
+
const base = cwd || process.cwd();
|
|
57
|
+
const candidates = [
|
|
58
|
+
path.join(home, '.claude', 'plugins', 'marketplaces', 'swarmdo', 'bin', 'cli.js'),
|
|
59
|
+
path.join(base, 'node_modules', '@swarmdo', 'cli', 'bin', 'cli.js'),
|
|
60
|
+
path.join(base, 'node_modules', 'swarmdo', 'bin', 'cli.js'),
|
|
61
|
+
path.join(base, 'v3', '@swarmdo', 'cli', 'bin', 'cli.js'),
|
|
62
|
+
];
|
|
63
|
+
try {
|
|
64
|
+
const binDir = path.dirname(process.execPath);
|
|
65
|
+
const globalModuleDirs = [
|
|
66
|
+
path.join(binDir, '..', 'lib', 'node_modules'),
|
|
67
|
+
path.join(binDir, 'node_modules'),
|
|
68
|
+
'/opt/homebrew/lib/node_modules',
|
|
69
|
+
'/usr/local/lib/node_modules',
|
|
70
|
+
];
|
|
71
|
+
for (const prefix of [process.env.npm_config_prefix, process.env.PREFIX, path.join(home, '.npm-global')]) {
|
|
72
|
+
if (prefix) globalModuleDirs.push(path.join(prefix, 'lib', 'node_modules'));
|
|
73
|
+
}
|
|
74
|
+
for (const gm of globalModuleDirs) {
|
|
75
|
+
candidates.push(
|
|
76
|
+
path.join(gm, 'swarmdo', 'bin', 'cli.js'),
|
|
77
|
+
path.join(gm, '@swarmdo', 'cli', 'bin', 'cli.js'),
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
} catch { /* ignore */ }
|
|
81
|
+
for (const p of candidates) {
|
|
82
|
+
if (fs.existsSync(p)) return p;
|
|
83
|
+
}
|
|
84
|
+
} catch { /* ignore */ }
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Is this hook payload a real subagent event we should act on? `agent_id` is
|
|
90
|
+
* present ONLY when the hook fires for a subagent, so it doubles as the guard
|
|
91
|
+
* against main-thread invocations. Pure.
|
|
92
|
+
*/
|
|
93
|
+
function isSubagentEvent(hookInput) {
|
|
94
|
+
const id = hookInput && (hookInput.agent_id || hookInput.agentId);
|
|
95
|
+
return typeof id === 'string' && id.length > 0;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Build the `agent bridge register` argv for a SubagentStart payload, or null
|
|
100
|
+
* when the payload is not a subagent event. Pure — the spawn happens in
|
|
101
|
+
* register(). Registering is idempotent on name+session, so a duplicate
|
|
102
|
+
* SubagentStart updates the bound record rather than forking a second one.
|
|
103
|
+
*/
|
|
104
|
+
function buildRegisterArgs(hookInput) {
|
|
105
|
+
if (!isSubagentEvent(hookInput)) return null;
|
|
106
|
+
const agentId = hookInput.agent_id || hookInput.agentId;
|
|
107
|
+
const sessionId = hookInput.session_id || hookInput.sessionId || '';
|
|
108
|
+
const agentType = hookInput.agent_type || hookInput.agentType || 'general-purpose';
|
|
109
|
+
const args = ['agent', 'bridge', 'register', '-n', agentId, '-t', agentType];
|
|
110
|
+
if (sessionId) args.push('-s', sessionId);
|
|
111
|
+
return args;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Build the `agent stop` argv for a SubagentStop payload, or null when the
|
|
116
|
+
* payload is not a subagent event. `agent stop` sets status='terminated'
|
|
117
|
+
* (agent-tools.ts:721), which is precisely what computeSwarmStatus filters on,
|
|
118
|
+
* so the Agents count decrements when a subagent finishes. `-f` because a hook
|
|
119
|
+
* has no TTY and must not hit the interactive confirm. Pure.
|
|
120
|
+
*/
|
|
121
|
+
function buildTerminateArgs(hookInput) {
|
|
122
|
+
if (!isSubagentEvent(hookInput)) return null;
|
|
123
|
+
const id = bridgeAgentId({
|
|
124
|
+
name: hookInput.agent_id || hookInput.agentId,
|
|
125
|
+
sessionId: hookInput.session_id || hookInput.sessionId,
|
|
126
|
+
});
|
|
127
|
+
return ['agent', 'stop', id, '-f'];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Fire a CLI subcommand and forget it. Detached + unref'd + stdio ignored so
|
|
132
|
+
* the hook process can exit immediately without waiting on the CLI's ~850ms
|
|
133
|
+
* boot, and without the child dying alongside it.
|
|
134
|
+
*/
|
|
135
|
+
function spawnDetached(args, cwd) {
|
|
136
|
+
const cliBin = resolveCliBin(cwd);
|
|
137
|
+
if (!cliBin) return false;
|
|
138
|
+
try {
|
|
139
|
+
const child = spawn(process.execPath, [cliBin, ...args], {
|
|
140
|
+
cwd: cwd || process.cwd(),
|
|
141
|
+
detached: true,
|
|
142
|
+
stdio: 'ignore',
|
|
143
|
+
});
|
|
144
|
+
child.unref();
|
|
145
|
+
return true;
|
|
146
|
+
} catch {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** SubagentStart → bind the subagent (and auto-form its swarm). */
|
|
152
|
+
function register(hookInput, cwd) {
|
|
153
|
+
const args = buildRegisterArgs(hookInput);
|
|
154
|
+
if (!args) return false;
|
|
155
|
+
return spawnDetached(args, cwd || (hookInput && hookInput.cwd));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** SubagentStop → mark the bound record terminated. */
|
|
159
|
+
function terminate(hookInput, cwd) {
|
|
160
|
+
const args = buildTerminateArgs(hookInput);
|
|
161
|
+
if (!args) return false;
|
|
162
|
+
return spawnDetached(args, cwd || (hookInput && hookInput.cwd));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
module.exports = {
|
|
166
|
+
bridgeAgentId,
|
|
167
|
+
resolveCliBin,
|
|
168
|
+
isSubagentEvent,
|
|
169
|
+
buildRegisterArgs,
|
|
170
|
+
buildTerminateArgs,
|
|
171
|
+
spawnDetached,
|
|
172
|
+
register,
|
|
173
|
+
terminate,
|
|
174
|
+
};
|
|
@@ -34,6 +34,7 @@ const router = safeRequire(path.join(helpersDir, 'router.cjs'));
|
|
|
34
34
|
const session = safeRequire(path.join(helpersDir, 'session.cjs'));
|
|
35
35
|
const memory = safeRequire(path.join(helpersDir, 'memory.cjs'));
|
|
36
36
|
const intelligence = safeRequire(path.join(helpersDir, 'intelligence.cjs'));
|
|
37
|
+
const agentBridge = safeRequire(path.join(helpersDir, 'agent-bridge-hook.cjs'));
|
|
37
38
|
|
|
38
39
|
// ── Intelligence timeout protection (fixes #1530, #1531) ───────────────────
|
|
39
40
|
var INTELLIGENCE_TIMEOUT_MS = 3000;
|
|
@@ -143,19 +144,23 @@ const handlers = {
|
|
|
143
144
|
} else {
|
|
144
145
|
console.log('[INFO] Router not available, using default routing');
|
|
145
146
|
}
|
|
146
|
-
// Agentic prompts →
|
|
147
|
-
// Claude Code
|
|
148
|
-
//
|
|
147
|
+
// Agentic prompts → suggest spawning a swarm, and which roles. Swarmdo
|
|
148
|
+
// cannot call Claude Code's Agent tool itself, so this advisory is the only
|
|
149
|
+
// way it can ask. Fully guarded — never breaks the prompt.
|
|
150
|
+
//
|
|
151
|
+
// Deliberately does NOT mention `agent bridge register` (#108): the
|
|
152
|
+
// SubagentStart hook registers every subagent on spawn, so telling the main
|
|
153
|
+
// agent to register by hand is both wrong and redundant work. This fires on
|
|
154
|
+
// every agentic prompt, so it is kept to one line — the three lines of
|
|
155
|
+
// manual-registration instructions it replaced were pure token cost.
|
|
149
156
|
try {
|
|
150
157
|
if (router && router.classifyAgentic) {
|
|
151
158
|
const intent = router.classifyAgentic(prompt);
|
|
152
159
|
if (intent.requiresAgents) {
|
|
153
160
|
const roles = intent.roles.join(', ');
|
|
154
161
|
console.log('');
|
|
155
|
-
console.log('[SWARMDO] Agentic task —
|
|
156
|
-
|
|
157
|
-
console.log(' swarmdo agent bridge register -n <agent-name> -t <role> -s <session>');
|
|
158
|
-
console.log(' suggested roles: ' + roles + ' · then: swarmdo agent bridge list / swarm status');
|
|
162
|
+
console.log('[SWARMDO] Agentic task — consider spawning agents: ' + roles
|
|
163
|
+
+ ' (auto-registered on spawn; see `swarmdo swarm status`)');
|
|
159
164
|
}
|
|
160
165
|
}
|
|
161
166
|
} catch (e) { /* advisory only — never break the prompt */ }
|
|
@@ -270,6 +275,27 @@ const handlers = {
|
|
|
270
275
|
console.log('[OK] Status check');
|
|
271
276
|
},
|
|
272
277
|
|
|
278
|
+
// SubagentStart — bind the spawned Claude Code subagent into Swarmdo's
|
|
279
|
+
// canonical registry so `swarmdo agent list` and the statusline's
|
|
280
|
+
// `🐝 Swarms N 🤖 Agents M` reflect agents that actually exist (#108).
|
|
281
|
+
// Registering also auto-forms the swarm, so both counters move.
|
|
282
|
+
'agent-register': () => {
|
|
283
|
+
if (!agentBridge) return;
|
|
284
|
+
if (!agentBridge.isSubagentEvent(hookInput)) return; // main-thread call
|
|
285
|
+
var ok = agentBridge.register(hookInput);
|
|
286
|
+
console.log(ok
|
|
287
|
+
? '[OK] Bridged subagent ' + (hookInput.agent_type || 'agent') + ' into Swarmdo'
|
|
288
|
+
: '[WARN] Could not bridge subagent (no installed CLI found)');
|
|
289
|
+
},
|
|
290
|
+
|
|
291
|
+
// SubagentStop — mark the bound record terminated so the Agents count
|
|
292
|
+
// decrements. Same deterministic id the register side derived (#108).
|
|
293
|
+
'agent-terminate': () => {
|
|
294
|
+
if (!agentBridge) return;
|
|
295
|
+
if (!agentBridge.isSubagentEvent(hookInput)) return;
|
|
296
|
+
agentBridge.terminate(hookInput);
|
|
297
|
+
},
|
|
298
|
+
|
|
273
299
|
'stats': () => {
|
|
274
300
|
if (intelligence && intelligence.stats) {
|
|
275
301
|
intelligence.stats(args.includes('--json'));
|
|
@@ -288,7 +314,7 @@ if (command && handlers[command]) {
|
|
|
288
314
|
} else if (command) {
|
|
289
315
|
console.log('[OK] Hook: ' + command);
|
|
290
316
|
} else {
|
|
291
|
-
console.log('Usage: hook-handler.cjs <route|pre-bash|post-edit|session-restore|session-end|pre-task|post-task|compact-manual|compact-auto|status|stats>');
|
|
317
|
+
console.log('Usage: hook-handler.cjs <route|pre-bash|post-edit|session-restore|session-end|pre-task|post-task|agent-register|agent-terminate|compact-manual|compact-auto|status|stats>');
|
|
292
318
|
}
|
|
293
319
|
}
|
|
294
320
|
|
package/.claude/settings.json
CHANGED
|
@@ -144,6 +144,17 @@
|
|
|
144
144
|
]
|
|
145
145
|
}
|
|
146
146
|
],
|
|
147
|
+
"SubagentStart": [
|
|
148
|
+
{
|
|
149
|
+
"hooks": [
|
|
150
|
+
{
|
|
151
|
+
"type": "command",
|
|
152
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/helpers/hook-handler.cjs\" agent-register",
|
|
153
|
+
"timeout": 5000
|
|
154
|
+
}
|
|
155
|
+
]
|
|
156
|
+
}
|
|
157
|
+
],
|
|
147
158
|
"SubagentStop": [
|
|
148
159
|
{
|
|
149
160
|
"hooks": [
|
|
@@ -151,6 +162,11 @@
|
|
|
151
162
|
"type": "command",
|
|
152
163
|
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/helpers/hook-handler.cjs\" post-task",
|
|
153
164
|
"timeout": 5000
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"type": "command",
|
|
168
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/helpers/hook-handler.cjs\" agent-terminate",
|
|
169
|
+
"timeout": 5000
|
|
154
170
|
}
|
|
155
171
|
]
|
|
156
172
|
}
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://swarmdo.com)
|
|
4
4
|
|
|
5
|
-
[](https://img.shields.io/badge/npx%20swarmdo-v1.58.23-cb3837?style=for-the-badge&logo=npm&logoColor=white)](https://github.com/SwarmDo/swarmdo/releases)
|
|
6
6
|
[](https://github.com/SwarmDo/swarmdo/blob/main/LICENSE)
|
|
7
7
|
[](https://swarmdo.com)
|
|
8
8
|
[](https://github.com/SwarmDo/swarmdo)
|
|
@@ -294,7 +294,7 @@ The recent release train added a full day-to-day operations layer around the swa
|
|
|
294
294
|
| `swarmdo hidden-coupling` | **Co-change with no import edge** — join the two graphs swarmdo already owns (temporal `coupling` from git + `codegraph`'s import graph) and emit the set difference: file pairs that keep changing together yet nothing in the code links them ("logical minus structural coupling" — a config and its consumers, a schema and its mirror type). The co-edit `affected` can't see. `--since`, `--min-shared`, `--csv`, `--format json`. Grounded in Gall et al. (ICSM 1998). Deterministic |
|
|
295
295
|
| `swarmdo standup` (alias `mine`) | **What did I do?** — your commits since your last working day, grouped by day with a diffstat. **Weekend-aware** (git-standup parity): on Monday it reaches back to Friday, on Sunday to Friday, otherwise to yesterday — one-command recall for a standup or for re-orienting at the start of a session. Defaults to the current git user; `--author <name>`/`--all` for teammates, `--days N` or `--since 2w` to override the window, `--format json`. Pure engine (weekend math + per-day fold) → fixture-tested. Deterministic |
|
|
296
296
|
| `swarmdo commands` (alias `slash`) | **Hot/cold/orphan usage report** for your authored `.claude/` surface — which custom slash-commands (`.claude/commands/**`) and subagents (`.claude/agents/**`) you actually invoke (**hot**, with counts), which are **cold** (defined but never used → prune candidates), and which are **orphan** (invoked but undefined → a typo or a builtin). `config lint` says "are these valid?"; this says "are these used?" — joining the defined files against invocation counts mined from local transcripts (`<command-name>` markers + `subagent_type` fields). `--unused` prints just the prune list, `--unused --strict` is a CI gate (exit 1 if anything's dead), `--all` counts across every project, `--json`. Pure parsers + join → fixture-tested |
|
|
297
|
-
| `swarmdo agent bridge` | **Link Claude Code agents ⇄ Swarmdo** — the fix for "Swarmdo is installed but never actually used." Swarmdo can't call Claude Code's `Agent` tool (only the main loop can), so `agent bridge register -n <name>` binds a real Claude Code agent into Swarmdo's registry (`swarmdo agent list` stops being empty) and **auto-spins-up a swarm from your config, enrolling the agent** — the next bind joins the same swarm. `bridge list` (bound vs native), `bridge sync --live a,b` (reconcile drift), `bridge advise "<prompt>"` (should this warrant a swarm? which roles?). Also MCP tools `agent_bridge_register`/`agent_bridge_list`. Pure engine → 16 tests |
|
|
297
|
+
| `swarmdo agent bridge` | **Link Claude Code agents ⇄ Swarmdo** — the fix for "Swarmdo is installed but never actually used." Swarmdo can't call Claude Code's `Agent` tool (only the main loop can), so `agent bridge register -n <name>` binds a real Claude Code agent into Swarmdo's registry (`swarmdo agent list` stops being empty) and **auto-spins-up a swarm from your config, enrolling the agent** — the next bind joins the same swarm. **Automatic since 1.58.18:** `swarmdo init` wires a `SubagentStart` hook that registers every Claude Code subagent as it spawns, and a `SubagentStop` hook that marks it terminated — so the statusline's `🐝 Swarms N 🤖 Agents M` tracks real agents with no manual step (#108). `bridge list` (bound vs native), `bridge sync --live a,b` (reconcile drift), `bridge advise "<prompt>"` (should this warrant a swarm? which roles?). Also MCP tools `agent_bridge_register`/`agent_bridge_list`. Pure engine → 16 tests |
|
|
298
298
|
| `swarmdo testreport` | **JUnit/TAP → failure digest** — turn raw test-result files into the exact failing test names + `file:line` + assertion messages, instead of scanning hundreds of log lines. The front-half of the test→fix loop: feed the failures straight into `repair`. Reads a file, a directory, or stdin; `--ci` exits 1 on any failure, `--format json`. Also an MCP tool (`testreport`). Deterministic |
|
|
299
299
|
| `swarmdo integrations` (alias `integrate`) | **Use swarmdo from Codex CLI, GitHub Copilot CLI, and pi** — one command wires AGENTS.md + each CLI's MCP config (idempotent, dry-run first, never touches your Claude Code setup) |
|
|
300
300
|
| OpenRouter model pool | **Let swarms pick from any models you configure** — declare tier-mapped OpenRouter models in `swarmdo.config.json`; the router Thompson-samples among them per task and the execution layer dispatches the winner |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "swarmdo",
|
|
3
|
-
"version": "1.58.
|
|
3
|
+
"version": "1.58.23",
|
|
4
4
|
"description": "Swarmdo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agent-bridge-hook.cjs — bind real Claude Code subagents into Swarmdo's
|
|
3
|
+
* canonical agent registry, automatically.
|
|
4
|
+
*
|
|
5
|
+
* #108. `src/agent-bridge/bridge.ts` was written to solve "a session can run
|
|
6
|
+
* four Claude Code agents while `swarmdo agent list` stays empty", and is pure
|
|
7
|
+
* by design because "All fs / MCP / spawning lives in the command + hook
|
|
8
|
+
* layers". The command layer got built (`swarmdo agent bridge register`); the
|
|
9
|
+
* hook layer never did. So nothing ever registered a Task/Agent-tool subagent,
|
|
10
|
+
* `.swarmdo/agents/store.json` stayed `{"agents":{}}`, and the statusline's
|
|
11
|
+
* `🐝 Swarms N 🤖 Agents M` sat at 0/0 forever. This file is that missing
|
|
12
|
+
* hook layer.
|
|
13
|
+
*
|
|
14
|
+
* Why shell out to the CLI instead of writing the store here: registering is
|
|
15
|
+
* not just a store write — `agent_bridge_register` also auto-forms a swarm from
|
|
16
|
+
* config and enrolls the agent (agent-tools.ts:462), which is what makes the
|
|
17
|
+
* Swarms counter move too. bridge.ts's own rule is to go through the canonical
|
|
18
|
+
* registerAgent path — "NO parallel store". Reimplementing that in CJS would
|
|
19
|
+
* fork the registry. The CLI costs ~850ms to boot (it loads ONNX), far too slow
|
|
20
|
+
* to block every subagent spawn, so calls are fired detached and unref'd: the
|
|
21
|
+
* hook returns immediately and the registry lands ~1s later. The statusline
|
|
22
|
+
* polls every 5s, so eventual consistency is invisible here.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const fs = require('fs');
|
|
26
|
+
const os = require('os');
|
|
27
|
+
const path = require('path');
|
|
28
|
+
const { spawn } = require('child_process');
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Mirror of src/agent-bridge/bridge.ts `bridgeAgentId` — kept in sync by hand,
|
|
32
|
+
* because this hook is standalone CommonJS and cannot import the ESM engine
|
|
33
|
+
* (the same constraint router.cjs already works under for classifyPrompt).
|
|
34
|
+
* __tests__/agent-bridge-hook.test.ts asserts the two implementations agree.
|
|
35
|
+
*
|
|
36
|
+
* Deterministic, so SubagentStart and SubagentStop derive the SAME id from the
|
|
37
|
+
* same agent_id and the stop reliably terminates the record the start created.
|
|
38
|
+
*/
|
|
39
|
+
function bridgeAgentId(d) {
|
|
40
|
+
const sess = d.sessionId
|
|
41
|
+
? d.sessionId.replace(/[^A-Za-z0-9]/g, '').slice(0, 8) || 'nosess'
|
|
42
|
+
: 'nosess';
|
|
43
|
+
const safe = String(d.name || '').replace(/[^A-Za-z0-9_-]+/g, '-').replace(/^-+|-+$/g, '') || 'agent';
|
|
44
|
+
return `cc-${sess}-${safe}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Locate an installed CLI bin. Mirror of statusline.cjs resolveCliBin() — same
|
|
49
|
+
* candidate order, same reason (#2337): never `npx @latest` from a hook.
|
|
50
|
+
* Returns null when nothing is installed, in which case we no-op rather than
|
|
51
|
+
* pay a registry round-trip on every subagent spawn.
|
|
52
|
+
*/
|
|
53
|
+
function resolveCliBin(cwd) {
|
|
54
|
+
try {
|
|
55
|
+
const home = os.homedir();
|
|
56
|
+
const base = cwd || process.cwd();
|
|
57
|
+
const candidates = [
|
|
58
|
+
path.join(home, '.claude', 'plugins', 'marketplaces', 'swarmdo', 'bin', 'cli.js'),
|
|
59
|
+
path.join(base, 'node_modules', '@swarmdo', 'cli', 'bin', 'cli.js'),
|
|
60
|
+
path.join(base, 'node_modules', 'swarmdo', 'bin', 'cli.js'),
|
|
61
|
+
path.join(base, 'v3', '@swarmdo', 'cli', 'bin', 'cli.js'),
|
|
62
|
+
];
|
|
63
|
+
try {
|
|
64
|
+
const binDir = path.dirname(process.execPath);
|
|
65
|
+
const globalModuleDirs = [
|
|
66
|
+
path.join(binDir, '..', 'lib', 'node_modules'),
|
|
67
|
+
path.join(binDir, 'node_modules'),
|
|
68
|
+
'/opt/homebrew/lib/node_modules',
|
|
69
|
+
'/usr/local/lib/node_modules',
|
|
70
|
+
];
|
|
71
|
+
for (const prefix of [process.env.npm_config_prefix, process.env.PREFIX, path.join(home, '.npm-global')]) {
|
|
72
|
+
if (prefix) globalModuleDirs.push(path.join(prefix, 'lib', 'node_modules'));
|
|
73
|
+
}
|
|
74
|
+
for (const gm of globalModuleDirs) {
|
|
75
|
+
candidates.push(
|
|
76
|
+
path.join(gm, 'swarmdo', 'bin', 'cli.js'),
|
|
77
|
+
path.join(gm, '@swarmdo', 'cli', 'bin', 'cli.js'),
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
} catch { /* ignore */ }
|
|
81
|
+
for (const p of candidates) {
|
|
82
|
+
if (fs.existsSync(p)) return p;
|
|
83
|
+
}
|
|
84
|
+
} catch { /* ignore */ }
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Is this hook payload a real subagent event we should act on? `agent_id` is
|
|
90
|
+
* present ONLY when the hook fires for a subagent, so it doubles as the guard
|
|
91
|
+
* against main-thread invocations. Pure.
|
|
92
|
+
*/
|
|
93
|
+
function isSubagentEvent(hookInput) {
|
|
94
|
+
const id = hookInput && (hookInput.agent_id || hookInput.agentId);
|
|
95
|
+
return typeof id === 'string' && id.length > 0;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Build the `agent bridge register` argv for a SubagentStart payload, or null
|
|
100
|
+
* when the payload is not a subagent event. Pure — the spawn happens in
|
|
101
|
+
* register(). Registering is idempotent on name+session, so a duplicate
|
|
102
|
+
* SubagentStart updates the bound record rather than forking a second one.
|
|
103
|
+
*/
|
|
104
|
+
function buildRegisterArgs(hookInput) {
|
|
105
|
+
if (!isSubagentEvent(hookInput)) return null;
|
|
106
|
+
const agentId = hookInput.agent_id || hookInput.agentId;
|
|
107
|
+
const sessionId = hookInput.session_id || hookInput.sessionId || '';
|
|
108
|
+
const agentType = hookInput.agent_type || hookInput.agentType || 'general-purpose';
|
|
109
|
+
const args = ['agent', 'bridge', 'register', '-n', agentId, '-t', agentType];
|
|
110
|
+
if (sessionId) args.push('-s', sessionId);
|
|
111
|
+
return args;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Build the `agent stop` argv for a SubagentStop payload, or null when the
|
|
116
|
+
* payload is not a subagent event. `agent stop` sets status='terminated'
|
|
117
|
+
* (agent-tools.ts:721), which is precisely what computeSwarmStatus filters on,
|
|
118
|
+
* so the Agents count decrements when a subagent finishes. `-f` because a hook
|
|
119
|
+
* has no TTY and must not hit the interactive confirm. Pure.
|
|
120
|
+
*/
|
|
121
|
+
function buildTerminateArgs(hookInput) {
|
|
122
|
+
if (!isSubagentEvent(hookInput)) return null;
|
|
123
|
+
const id = bridgeAgentId({
|
|
124
|
+
name: hookInput.agent_id || hookInput.agentId,
|
|
125
|
+
sessionId: hookInput.session_id || hookInput.sessionId,
|
|
126
|
+
});
|
|
127
|
+
return ['agent', 'stop', id, '-f'];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Fire a CLI subcommand and forget it. Detached + unref'd + stdio ignored so
|
|
132
|
+
* the hook process can exit immediately without waiting on the CLI's ~850ms
|
|
133
|
+
* boot, and without the child dying alongside it.
|
|
134
|
+
*/
|
|
135
|
+
function spawnDetached(args, cwd) {
|
|
136
|
+
const cliBin = resolveCliBin(cwd);
|
|
137
|
+
if (!cliBin) return false;
|
|
138
|
+
try {
|
|
139
|
+
const child = spawn(process.execPath, [cliBin, ...args], {
|
|
140
|
+
cwd: cwd || process.cwd(),
|
|
141
|
+
detached: true,
|
|
142
|
+
stdio: 'ignore',
|
|
143
|
+
});
|
|
144
|
+
child.unref();
|
|
145
|
+
return true;
|
|
146
|
+
} catch {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** SubagentStart → bind the subagent (and auto-form its swarm). */
|
|
152
|
+
function register(hookInput, cwd) {
|
|
153
|
+
const args = buildRegisterArgs(hookInput);
|
|
154
|
+
if (!args) return false;
|
|
155
|
+
return spawnDetached(args, cwd || (hookInput && hookInput.cwd));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** SubagentStop → mark the bound record terminated. */
|
|
159
|
+
function terminate(hookInput, cwd) {
|
|
160
|
+
const args = buildTerminateArgs(hookInput);
|
|
161
|
+
if (!args) return false;
|
|
162
|
+
return spawnDetached(args, cwd || (hookInput && hookInput.cwd));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
module.exports = {
|
|
166
|
+
bridgeAgentId,
|
|
167
|
+
resolveCliBin,
|
|
168
|
+
isSubagentEvent,
|
|
169
|
+
buildRegisterArgs,
|
|
170
|
+
buildTerminateArgs,
|
|
171
|
+
spawnDetached,
|
|
172
|
+
register,
|
|
173
|
+
terminate,
|
|
174
|
+
};
|