devchain-cli 0.1.0
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/LICENSE +53 -0
- package/README.md +74 -0
- package/dist/cli.js +782 -0
- package/dist/drizzle/0000_flippant_the_spike.sql +220 -0
- package/dist/drizzle/0001_shiny_snowbird.sql +7 -0
- package/dist/drizzle/0002_high_zemo.sql +19 -0
- package/dist/drizzle/0003_noisy_scarecrow.sql +21 -0
- package/dist/drizzle/0004_mcp_metadata.sql +5 -0
- package/dist/drizzle/0005_agent_profile_instructions.sql +3 -0
- package/dist/drizzle/0006_wakeful_marvex.sql +15 -0
- package/dist/drizzle/0007_agent_profile_options.sql +7 -0
- package/dist/drizzle/0008_event_logs.sql +31 -0
- package/dist/drizzle/0009_chat_tables.sql +42 -0
- package/dist/drizzle/0010_amazing_the_initiative.sql +11 -0
- package/dist/drizzle/0011_curved_payback.sql +16 -0
- package/dist/drizzle/0012_terminal_activity.sql +8 -0
- package/dist/drizzle/0013_chat_activities.sql +18 -0
- package/dist/drizzle/0014_chat_activities_start_msg_nullable.sql +5 -0
- package/dist/drizzle/0015_projects_is_template.sql +4 -0
- package/dist/drizzle/0016_agent_profiles_project_scoped.sql +4 -0
- package/dist/drizzle/meta/0000_snapshot.json +1388 -0
- package/dist/drizzle/meta/0001_snapshot.json +1434 -0
- package/dist/drizzle/meta/0002_snapshot.json +1434 -0
- package/dist/drizzle/meta/0003_snapshot.json +1583 -0
- package/dist/drizzle/meta/0004_snapshot.json +1605 -0
- package/dist/drizzle/meta/0005_snapshot.json +1612 -0
- package/dist/drizzle/meta/0006_snapshot.json +1742 -0
- package/dist/drizzle/meta/0007_snapshot.json +1742 -0
- package/dist/drizzle/meta/0008_snapshot.json +1897 -0
- package/dist/drizzle/meta/0009_snapshot.json +2202 -0
- package/dist/drizzle/meta/0010_snapshot.json +2283 -0
- package/dist/drizzle/meta/0011_snapshot.json +2407 -0
- package/dist/drizzle/meta/_journal.json +125 -0
- package/dist/lib/interactive-cli.js +279 -0
- package/dist/server/app.module.d.ts +4 -0
- package/dist/server/app.module.js +64 -0
- package/dist/server/app.module.js.map +1 -0
- package/dist/server/common/config/env.config.d.ts +23 -0
- package/dist/server/common/config/env.config.js +60 -0
- package/dist/server/common/config/env.config.js.map +1 -0
- package/dist/server/common/config/feature-flags.d.ts +5 -0
- package/dist/server/common/config/feature-flags.js +8 -0
- package/dist/server/common/config/feature-flags.js.map +1 -0
- package/dist/server/common/errors/error-types.d.ts +30 -0
- package/dist/server/common/errors/error-types.js +63 -0
- package/dist/server/common/errors/error-types.js.map +1 -0
- package/dist/server/common/filters/http-exception.filter.d.ts +4 -0
- package/dist/server/common/filters/http-exception.filter.js +72 -0
- package/dist/server/common/filters/http-exception.filter.js.map +1 -0
- package/dist/server/common/filters/ws-exception.filter.d.ts +5 -0
- package/dist/server/common/filters/ws-exception.filter.js +102 -0
- package/dist/server/common/filters/ws-exception.filter.js.map +1 -0
- package/dist/server/common/logging/logger.d.ts +3 -0
- package/dist/server/common/logging/logger.js +29 -0
- package/dist/server/common/logging/logger.js.map +1 -0
- package/dist/server/common/middleware/request-id.middleware.d.ts +12 -0
- package/dist/server/common/middleware/request-id.middleware.js +24 -0
- package/dist/server/common/middleware/request-id.middleware.js.map +1 -0
- package/dist/server/main.d.ts +1 -0
- package/dist/server/main.js +94 -0
- package/dist/server/main.js.map +1 -0
- package/dist/server/modules/agents/agents.module.d.ts +2 -0
- package/dist/server/modules/agents/agents.module.js +22 -0
- package/dist/server/modules/agents/agents.module.js.map +1 -0
- package/dist/server/modules/agents/controllers/agents.controller.d.ts +12 -0
- package/dist/server/modules/agents/controllers/agents.controller.js +115 -0
- package/dist/server/modules/agents/controllers/agents.controller.js.map +1 -0
- package/dist/server/modules/chat/chat.module.d.ts +2 -0
- package/dist/server/modules/chat/chat.module.js +28 -0
- package/dist/server/modules/chat/chat.module.js.map +1 -0
- package/dist/server/modules/chat/controllers/chat-settings.controller.d.ts +8 -0
- package/dist/server/modules/chat/controllers/chat-settings.controller.js +66 -0
- package/dist/server/modules/chat/controllers/chat-settings.controller.js.map +1 -0
- package/dist/server/modules/chat/controllers/chat.controller.d.ts +15 -0
- package/dist/server/modules/chat/controllers/chat.controller.js +152 -0
- package/dist/server/modules/chat/controllers/chat.controller.js.map +1 -0
- package/dist/server/modules/chat/dtos/chat-settings.dto.d.ts +31 -0
- package/dist/server/modules/chat/dtos/chat-settings.dto.js +17 -0
- package/dist/server/modules/chat/dtos/chat-settings.dto.js.map +1 -0
- package/dist/server/modules/chat/dtos/chat.dto.d.ts +309 -0
- package/dist/server/modules/chat/dtos/chat.dto.js +80 -0
- package/dist/server/modules/chat/dtos/chat.dto.js.map +1 -0
- package/dist/server/modules/chat/services/chat-settings.service.d.ts +12 -0
- package/dist/server/modules/chat/services/chat-settings.service.js +120 -0
- package/dist/server/modules/chat/services/chat-settings.service.js.map +1 -0
- package/dist/server/modules/chat/services/chat.service.d.ts +45 -0
- package/dist/server/modules/chat/services/chat.service.js +730 -0
- package/dist/server/modules/chat/services/chat.service.js.map +1 -0
- package/dist/server/modules/chat/services/invite-template.util.d.ts +17 -0
- package/dist/server/modules/chat/services/invite-template.util.js +70 -0
- package/dist/server/modules/chat/services/invite-template.util.js.map +1 -0
- package/dist/server/modules/core/controllers/health.controller.d.ts +7 -0
- package/dist/server/modules/core/controllers/health.controller.js +39 -0
- package/dist/server/modules/core/controllers/health.controller.js.map +1 -0
- package/dist/server/modules/core/controllers/preflight.controller.d.ts +10 -0
- package/dist/server/modules/core/controllers/preflight.controller.js +58 -0
- package/dist/server/modules/core/controllers/preflight.controller.js.map +1 -0
- package/dist/server/modules/core/core.module.d.ts +2 -0
- package/dist/server/modules/core/core.module.js +27 -0
- package/dist/server/modules/core/core.module.js.map +1 -0
- package/dist/server/modules/core/services/preflight.service.d.ts +44 -0
- package/dist/server/modules/core/services/preflight.service.js +400 -0
- package/dist/server/modules/core/services/preflight.service.js.map +1 -0
- package/dist/server/modules/documents/controllers/documents.controller.d.ts +12 -0
- package/dist/server/modules/documents/controllers/documents.controller.js +173 -0
- package/dist/server/modules/documents/controllers/documents.controller.js.map +1 -0
- package/dist/server/modules/documents/documents.module.d.ts +2 -0
- package/dist/server/modules/documents/documents.module.js +22 -0
- package/dist/server/modules/documents/documents.module.js.map +1 -0
- package/dist/server/modules/epics/controllers/epic-comments.controller.d.ts +10 -0
- package/dist/server/modules/epics/controllers/epic-comments.controller.js +94 -0
- package/dist/server/modules/epics/controllers/epic-comments.controller.js.map +1 -0
- package/dist/server/modules/epics/controllers/epics.controller.d.ts +16 -0
- package/dist/server/modules/epics/controllers/epics.controller.js +201 -0
- package/dist/server/modules/epics/controllers/epics.controller.js.map +1 -0
- package/dist/server/modules/epics/epics.module.d.ts +2 -0
- package/dist/server/modules/epics/epics.module.js +28 -0
- package/dist/server/modules/epics/epics.module.js.map +1 -0
- package/dist/server/modules/epics/services/epics.service.d.ts +34 -0
- package/dist/server/modules/epics/services/epics.service.js +251 -0
- package/dist/server/modules/epics/services/epics.service.js.map +1 -0
- package/dist/server/modules/events/catalog/epic.assigned.d.ts +36 -0
- package/dist/server/modules/events/catalog/epic.assigned.js +19 -0
- package/dist/server/modules/events/catalog/epic.assigned.js.map +1 -0
- package/dist/server/modules/events/catalog/index.d.ts +71 -0
- package/dist/server/modules/events/catalog/index.js +15 -0
- package/dist/server/modules/events/catalog/index.js.map +1 -0
- package/dist/server/modules/events/catalog/session.crashed.d.ts +15 -0
- package/dist/server/modules/events/catalog/session.crashed.js +12 -0
- package/dist/server/modules/events/catalog/session.crashed.js.map +1 -0
- package/dist/server/modules/events/catalog/session.started.d.ts +21 -0
- package/dist/server/modules/events/catalog/session.started.js +14 -0
- package/dist/server/modules/events/catalog/session.started.js.map +1 -0
- package/dist/server/modules/events/catalog/session.stopped.d.ts +12 -0
- package/dist/server/modules/events/catalog/session.stopped.js +11 -0
- package/dist/server/modules/events/catalog/session.stopped.js.map +1 -0
- package/dist/server/modules/events/controllers/event-log.controller.d.ts +7 -0
- package/dist/server/modules/events/controllers/event-log.controller.js +75 -0
- package/dist/server/modules/events/controllers/event-log.controller.js.map +1 -0
- package/dist/server/modules/events/dtos/event-log.dto.d.ts +33 -0
- package/dist/server/modules/events/dtos/event-log.dto.js +3 -0
- package/dist/server/modules/events/dtos/event-log.dto.js.map +1 -0
- package/dist/server/modules/events/events.module.d.ts +2 -0
- package/dist/server/modules/events/events.module.js +45 -0
- package/dist/server/modules/events/events.module.js.map +1 -0
- package/dist/server/modules/events/index.d.ts +6 -0
- package/dist/server/modules/events/index.js +23 -0
- package/dist/server/modules/events/index.js.map +1 -0
- package/dist/server/modules/events/services/event-log.service.d.ts +38 -0
- package/dist/server/modules/events/services/event-log.service.js +272 -0
- package/dist/server/modules/events/services/event-log.service.js.map +1 -0
- package/dist/server/modules/events/services/events-stream.service.d.ts +23 -0
- package/dist/server/modules/events/services/events-stream.service.js +45 -0
- package/dist/server/modules/events/services/events-stream.service.js.map +1 -0
- package/dist/server/modules/events/services/events.service.d.ts +14 -0
- package/dist/server/modules/events/services/events.service.js +65 -0
- package/dist/server/modules/events/services/events.service.js.map +1 -0
- package/dist/server/modules/events/subscribers/chat-message-broadcaster.subscriber.d.ts +20 -0
- package/dist/server/modules/events/subscribers/chat-message-broadcaster.subscriber.js +47 -0
- package/dist/server/modules/events/subscribers/chat-message-broadcaster.subscriber.js.map +1 -0
- package/dist/server/modules/events/subscribers/chat-message-delivery.subscriber.d.ts +24 -0
- package/dist/server/modules/events/subscribers/chat-message-delivery.subscriber.js +81 -0
- package/dist/server/modules/events/subscribers/chat-message-delivery.subscriber.js.map +1 -0
- package/dist/server/modules/events/subscribers/epic-assignment-notifier.subscriber.d.ts +25 -0
- package/dist/server/modules/events/subscribers/epic-assignment-notifier.subscriber.js +223 -0
- package/dist/server/modules/events/subscribers/epic-assignment-notifier.subscriber.js.map +1 -0
- package/dist/server/modules/events/subscribers/index.d.ts +2 -0
- package/dist/server/modules/events/subscribers/index.js +12 -0
- package/dist/server/modules/events/subscribers/index.js.map +1 -0
- package/dist/server/modules/fs/fs.controller.d.ts +18 -0
- package/dist/server/modules/fs/fs.controller.js +56 -0
- package/dist/server/modules/fs/fs.controller.js.map +1 -0
- package/dist/server/modules/fs/fs.module.d.ts +2 -0
- package/dist/server/modules/fs/fs.module.js +20 -0
- package/dist/server/modules/fs/fs.module.js.map +1 -0
- package/dist/server/modules/mcp/constants.d.ts +9 -0
- package/dist/server/modules/mcp/constants.js +38 -0
- package/dist/server/modules/mcp/constants.js.map +1 -0
- package/dist/server/modules/mcp/controllers/mcp-http.controller.d.ts +16 -0
- package/dist/server/modules/mcp/controllers/mcp-http.controller.js +790 -0
- package/dist/server/modules/mcp/controllers/mcp-http.controller.js.map +1 -0
- package/dist/server/modules/mcp/controllers/mcp-sdk.controller.d.ts +12 -0
- package/dist/server/modules/mcp/controllers/mcp-sdk.controller.js +676 -0
- package/dist/server/modules/mcp/controllers/mcp-sdk.controller.js.map +1 -0
- package/dist/server/modules/mcp/controllers/mcp-test.controller.d.ts +50 -0
- package/dist/server/modules/mcp/controllers/mcp-test.controller.js +115 -0
- package/dist/server/modules/mcp/controllers/mcp-test.controller.js.map +1 -0
- package/dist/server/modules/mcp/dtos/mcp.dto.d.ts +1094 -0
- package/dist/server/modules/mcp/dtos/mcp.dto.js +257 -0
- package/dist/server/modules/mcp/dtos/mcp.dto.js.map +1 -0
- package/dist/server/modules/mcp/gateways/mcp.gateway.d.ts +21 -0
- package/dist/server/modules/mcp/gateways/mcp.gateway.js +141 -0
- package/dist/server/modules/mcp/gateways/mcp.gateway.js.map +1 -0
- package/dist/server/modules/mcp/mcp.module.d.ts +2 -0
- package/dist/server/modules/mcp/mcp.module.js +83 -0
- package/dist/server/modules/mcp/mcp.module.js.map +1 -0
- package/dist/server/modules/mcp/services/instructions-resolver.d.ts +24 -0
- package/dist/server/modules/mcp/services/instructions-resolver.js +203 -0
- package/dist/server/modules/mcp/services/instructions-resolver.js.map +1 -0
- package/dist/server/modules/mcp/services/mcp-provider-registration.service.d.ts +52 -0
- package/dist/server/modules/mcp/services/mcp-provider-registration.service.js +327 -0
- package/dist/server/modules/mcp/services/mcp-provider-registration.service.js.map +1 -0
- package/dist/server/modules/mcp/services/mcp-server.service.d.ts +6 -0
- package/dist/server/modules/mcp/services/mcp-server.service.js +26 -0
- package/dist/server/modules/mcp/services/mcp-server.service.js.map +1 -0
- package/dist/server/modules/mcp/services/mcp.service.d.ts +69 -0
- package/dist/server/modules/mcp/services/mcp.service.js +2056 -0
- package/dist/server/modules/mcp/services/mcp.service.js.map +1 -0
- package/dist/server/modules/mcp/services/terminal-activity.service.d.ts +9 -0
- package/dist/server/modules/mcp/services/terminal-activity.service.js +51 -0
- package/dist/server/modules/mcp/services/terminal-activity.service.js.map +1 -0
- package/dist/server/modules/profiles/controllers/profiles.controller.d.ts +45 -0
- package/dist/server/modules/profiles/controllers/profiles.controller.js +188 -0
- package/dist/server/modules/profiles/controllers/profiles.controller.js.map +1 -0
- package/dist/server/modules/profiles/dto.d.ts +75 -0
- package/dist/server/modules/profiles/dto.js +24 -0
- package/dist/server/modules/profiles/dto.js.map +1 -0
- package/dist/server/modules/profiles/profiles.module.d.ts +2 -0
- package/dist/server/modules/profiles/profiles.module.js +22 -0
- package/dist/server/modules/profiles/profiles.module.js.map +1 -0
- package/dist/server/modules/projects/controllers/projects.controller.d.ts +131 -0
- package/dist/server/modules/projects/controllers/projects.controller.js +196 -0
- package/dist/server/modules/projects/controllers/projects.controller.js.map +1 -0
- package/dist/server/modules/projects/projects.module.d.ts +2 -0
- package/dist/server/modules/projects/projects.module.js +26 -0
- package/dist/server/modules/projects/projects.module.js.map +1 -0
- package/dist/server/modules/projects/services/projects.service.d.ts +138 -0
- package/dist/server/modules/projects/services/projects.service.js +564 -0
- package/dist/server/modules/projects/services/projects.service.js.map +1 -0
- package/dist/server/modules/prompts/controllers/prompts.controller.d.ts +11 -0
- package/dist/server/modules/prompts/controllers/prompts.controller.js +112 -0
- package/dist/server/modules/prompts/controllers/prompts.controller.js.map +1 -0
- package/dist/server/modules/prompts/prompts.module.d.ts +2 -0
- package/dist/server/modules/prompts/prompts.module.js +22 -0
- package/dist/server/modules/prompts/prompts.module.js.map +1 -0
- package/dist/server/modules/providers/adapters/claude.adapter.d.ts +9 -0
- package/dist/server/modules/providers/adapters/claude.adapter.js +46 -0
- package/dist/server/modules/providers/adapters/claude.adapter.js.map +1 -0
- package/dist/server/modules/providers/adapters/codex.adapter.d.ts +9 -0
- package/dist/server/modules/providers/adapters/codex.adapter.js +46 -0
- package/dist/server/modules/providers/adapters/codex.adapter.js.map +1 -0
- package/dist/server/modules/providers/adapters/index.d.ts +4 -0
- package/dist/server/modules/providers/adapters/index.js +21 -0
- package/dist/server/modules/providers/adapters/index.js.map +1 -0
- package/dist/server/modules/providers/adapters/provider-adapter.factory.d.ts +8 -0
- package/dist/server/modules/providers/adapters/provider-adapter.factory.js +42 -0
- package/dist/server/modules/providers/adapters/provider-adapter.factory.js.map +1 -0
- package/dist/server/modules/providers/adapters/provider-adapter.interface.d.ts +18 -0
- package/dist/server/modules/providers/adapters/provider-adapter.interface.js +3 -0
- package/dist/server/modules/providers/adapters/provider-adapter.interface.js.map +1 -0
- package/dist/server/modules/providers/controllers/providers.controller.d.ts +33 -0
- package/dist/server/modules/providers/controllers/providers.controller.js +399 -0
- package/dist/server/modules/providers/controllers/providers.controller.js.map +1 -0
- package/dist/server/modules/providers/providers.module.d.ts +2 -0
- package/dist/server/modules/providers/providers.module.js +27 -0
- package/dist/server/modules/providers/providers.module.js.map +1 -0
- package/dist/server/modules/records/controllers/records.controller.d.ts +13 -0
- package/dist/server/modules/records/controllers/records.controller.js +83 -0
- package/dist/server/modules/records/controllers/records.controller.js.map +1 -0
- package/dist/server/modules/records/records.module.d.ts +2 -0
- package/dist/server/modules/records/records.module.js +22 -0
- package/dist/server/modules/records/records.module.js.map +1 -0
- package/dist/server/modules/sessions/controllers/sessions.controller.d.ts +13 -0
- package/dist/server/modules/sessions/controllers/sessions.controller.js +93 -0
- package/dist/server/modules/sessions/controllers/sessions.controller.js.map +1 -0
- package/dist/server/modules/sessions/dtos/sessions.dto.d.ts +67 -0
- package/dist/server/modules/sessions/dtos/sessions.dto.js +18 -0
- package/dist/server/modules/sessions/dtos/sessions.dto.js.map +1 -0
- package/dist/server/modules/sessions/services/activity-tracker.service.d.ts +15 -0
- package/dist/server/modules/sessions/services/activity-tracker.service.js +103 -0
- package/dist/server/modules/sessions/services/activity-tracker.service.js.map +1 -0
- package/dist/server/modules/sessions/services/sessions.service.d.ts +49 -0
- package/dist/server/modules/sessions/services/sessions.service.js +511 -0
- package/dist/server/modules/sessions/services/sessions.service.js.map +1 -0
- package/dist/server/modules/sessions/sessions.module.d.ts +2 -0
- package/dist/server/modules/sessions/sessions.module.js +36 -0
- package/dist/server/modules/sessions/sessions.module.js.map +1 -0
- package/dist/server/modules/sessions/utils/profile-options.d.ts +4 -0
- package/dist/server/modules/sessions/utils/profile-options.js +70 -0
- package/dist/server/modules/sessions/utils/profile-options.js.map +1 -0
- package/dist/server/modules/sessions/utils/template-renderer.d.ts +27 -0
- package/dist/server/modules/sessions/utils/template-renderer.js +38 -0
- package/dist/server/modules/sessions/utils/template-renderer.js.map +1 -0
- package/dist/server/modules/settings/controllers/settings.controller.d.ts +10 -0
- package/dist/server/modules/settings/controllers/settings.controller.js +77 -0
- package/dist/server/modules/settings/controllers/settings.controller.js.map +1 -0
- package/dist/server/modules/settings/dtos/settings.dto.d.ts +104 -0
- package/dist/server/modules/settings/dtos/settings.dto.js +44 -0
- package/dist/server/modules/settings/dtos/settings.dto.js.map +1 -0
- package/dist/server/modules/settings/services/settings.service.d.ts +20 -0
- package/dist/server/modules/settings/services/settings.service.js +310 -0
- package/dist/server/modules/settings/services/settings.service.js.map +1 -0
- package/dist/server/modules/settings/settings.module.d.ts +2 -0
- package/dist/server/modules/settings/settings.module.js +26 -0
- package/dist/server/modules/settings/settings.module.js.map +1 -0
- package/dist/server/modules/statuses/controllers/statuses.controller.d.ts +17 -0
- package/dist/server/modules/statuses/controllers/statuses.controller.js +124 -0
- package/dist/server/modules/statuses/controllers/statuses.controller.js.map +1 -0
- package/dist/server/modules/statuses/statuses.module.d.ts +2 -0
- package/dist/server/modules/statuses/statuses.module.js +22 -0
- package/dist/server/modules/statuses/statuses.module.js.map +1 -0
- package/dist/server/modules/storage/db/db.config.d.ts +5 -0
- package/dist/server/modules/storage/db/db.config.js +19 -0
- package/dist/server/modules/storage/db/db.config.js.map +1 -0
- package/dist/server/modules/storage/db/db.module.d.ts +2 -0
- package/dist/server/modules/storage/db/db.module.js +22 -0
- package/dist/server/modules/storage/db/db.module.js.map +1 -0
- package/dist/server/modules/storage/db/db.provider.d.ts +3 -0
- package/dist/server/modules/storage/db/db.provider.js +61 -0
- package/dist/server/modules/storage/db/db.provider.js.map +1 -0
- package/dist/server/modules/storage/db/migrate.d.ts +1 -0
- package/dist/server/modules/storage/db/migrate.js +32 -0
- package/dist/server/modules/storage/db/migrate.js.map +1 -0
- package/dist/server/modules/storage/db/schema.d.ts +4058 -0
- package/dist/server/modules/storage/db/schema.js +393 -0
- package/dist/server/modules/storage/db/schema.js.map +1 -0
- package/dist/server/modules/storage/db/sqlite-raw.d.ts +3 -0
- package/dist/server/modules/storage/db/sqlite-raw.js +8 -0
- package/dist/server/modules/storage/db/sqlite-raw.js.map +1 -0
- package/dist/server/modules/storage/interfaces/storage.interface.d.ts +193 -0
- package/dist/server/modules/storage/interfaces/storage.interface.js +5 -0
- package/dist/server/modules/storage/interfaces/storage.interface.js.map +1 -0
- package/dist/server/modules/storage/local/local-storage.service.d.ts +105 -0
- package/dist/server/modules/storage/local/local-storage.service.js +2069 -0
- package/dist/server/modules/storage/local/local-storage.service.js.map +1 -0
- package/dist/server/modules/storage/models/domain.models.d.ts +152 -0
- package/dist/server/modules/storage/models/domain.models.js +3 -0
- package/dist/server/modules/storage/models/domain.models.js.map +1 -0
- package/dist/server/modules/storage/storage.module.d.ts +2 -0
- package/dist/server/modules/storage/storage.module.js +29 -0
- package/dist/server/modules/storage/storage.module.js.map +1 -0
- package/dist/server/modules/terminal/dtos/ws-envelope.dto.d.ts +103 -0
- package/dist/server/modules/terminal/dtos/ws-envelope.dto.js +50 -0
- package/dist/server/modules/terminal/dtos/ws-envelope.dto.js.map +1 -0
- package/dist/server/modules/terminal/gateways/terminal.gateway.d.ts +83 -0
- package/dist/server/modules/terminal/gateways/terminal.gateway.js +622 -0
- package/dist/server/modules/terminal/gateways/terminal.gateway.js.map +1 -0
- package/dist/server/modules/terminal/services/pty.service.d.ts +29 -0
- package/dist/server/modules/terminal/services/pty.service.js +236 -0
- package/dist/server/modules/terminal/services/pty.service.js.map +1 -0
- package/dist/server/modules/terminal/services/terminal-emulator.service.d.ts +40 -0
- package/dist/server/modules/terminal/services/terminal-emulator.service.js +356 -0
- package/dist/server/modules/terminal/services/terminal-emulator.service.js.map +1 -0
- package/dist/server/modules/terminal/services/terminal-seed.service.d.ts +29 -0
- package/dist/server/modules/terminal/services/terminal-seed.service.js +233 -0
- package/dist/server/modules/terminal/services/terminal-seed.service.js.map +1 -0
- package/dist/server/modules/terminal/services/terminal-send-coordinator.service.d.ts +5 -0
- package/dist/server/modules/terminal/services/terminal-send-coordinator.service.js +36 -0
- package/dist/server/modules/terminal/services/terminal-send-coordinator.service.js.map +1 -0
- package/dist/server/modules/terminal/services/terminal-stream.service.d.ts +17 -0
- package/dist/server/modules/terminal/services/terminal-stream.service.js +88 -0
- package/dist/server/modules/terminal/services/terminal-stream.service.js.map +1 -0
- package/dist/server/modules/terminal/services/tmux.service.d.ts +42 -0
- package/dist/server/modules/terminal/services/tmux.service.js +318 -0
- package/dist/server/modules/terminal/services/tmux.service.js.map +1 -0
- package/dist/server/modules/terminal/terminal.module.d.ts +2 -0
- package/dist/server/modules/terminal/terminal.module.js +47 -0
- package/dist/server/modules/terminal/terminal.module.js.map +1 -0
- package/dist/server/modules/terminal/utils/ansi-sanitizer.d.ts +8 -0
- package/dist/server/modules/terminal/utils/ansi-sanitizer.js +48 -0
- package/dist/server/modules/terminal/utils/ansi-sanitizer.js.map +1 -0
- package/dist/server/modules/terminal/utils/control-keys.d.ts +3 -0
- package/dist/server/modules/terminal/utils/control-keys.js +30 -0
- package/dist/server/modules/terminal/utils/control-keys.js.map +1 -0
- package/dist/server/modules/ui/ui.controller.d.ts +4 -0
- package/dist/server/modules/ui/ui.controller.js +55 -0
- package/dist/server/modules/ui/ui.controller.js.map +1 -0
- package/dist/server/modules/ui/ui.module.d.ts +2 -0
- package/dist/server/modules/ui/ui.module.js +22 -0
- package/dist/server/modules/ui/ui.module.js.map +1 -0
- package/dist/server/test-setup.d.ts +1 -0
- package/dist/server/test-setup.js +14 -0
- package/dist/server/test-setup.js.map +1 -0
- package/dist/server/tsconfig.tsbuildinfo +1 -0
- package/dist/server/ui/assets/index-DFChYYFN.css +32 -0
- package/dist/server/ui/assets/index-DpXRypHy.js +636 -0
- package/dist/server/ui/index.html +26 -0
- package/dist/templates/simple-codex.json +134 -0
- package/package.json +105 -0
- package/prebuilds/README.md +7 -0
|
@@ -0,0 +1,2056 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.McpService = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const storage_interface_1 = require("../../storage/interfaces/storage.interface");
|
|
18
|
+
const chat_service_1 = require("../../chat/services/chat.service");
|
|
19
|
+
const sessions_service_1 = require("../../sessions/services/sessions.service");
|
|
20
|
+
const terminal_gateway_1 = require("../../terminal/gateways/terminal.gateway");
|
|
21
|
+
const epics_service_1 = require("../../epics/services/epics.service");
|
|
22
|
+
const logger_1 = require("../../../common/logging/logger");
|
|
23
|
+
const path_1 = require("path");
|
|
24
|
+
const mcp_dto_1 = require("../dtos/mcp.dto");
|
|
25
|
+
const instructions_resolver_1 = require("./instructions-resolver");
|
|
26
|
+
const error_types_1 = require("../../../common/errors/error-types");
|
|
27
|
+
const common_2 = require("@nestjs/common");
|
|
28
|
+
const zod_1 = require("zod");
|
|
29
|
+
const logger = (0, logger_1.createLogger)('McpService');
|
|
30
|
+
let McpService = class McpService {
|
|
31
|
+
constructor(storage, chatService, sessionsService, terminalGateway, epicsService) {
|
|
32
|
+
this.storage = storage;
|
|
33
|
+
this.chatService = chatService;
|
|
34
|
+
this.sessionsService = sessionsService;
|
|
35
|
+
this.terminalGateway = terminalGateway;
|
|
36
|
+
this.epicsService = epicsService;
|
|
37
|
+
this.DEFAULT_INLINE_MAX_BYTES = 64 * 1024;
|
|
38
|
+
logger.info('McpService initialized');
|
|
39
|
+
this.featureFlags = this.storage.getFeatureFlags();
|
|
40
|
+
this.instructionsResolver = new instructions_resolver_1.InstructionsResolver(this.storage, (document, cache, maxDepth, maxBytes) => this.buildInlineResolution(document, cache, maxDepth, maxBytes), this.featureFlags);
|
|
41
|
+
}
|
|
42
|
+
async resolveAgentByNameUnique(projectId, name) {
|
|
43
|
+
const normalized = name.trim().toLowerCase();
|
|
44
|
+
const list = await this.storage.listAgents(projectId, { limit: 1000, offset: 0 });
|
|
45
|
+
const matches = list.items.filter((a) => a.name.toLowerCase() === normalized);
|
|
46
|
+
if (matches.length === 0) {
|
|
47
|
+
return {
|
|
48
|
+
success: false,
|
|
49
|
+
error: {
|
|
50
|
+
code: 'AGENT_NOT_FOUND',
|
|
51
|
+
message: `Agent "${name}" not found in project`,
|
|
52
|
+
data: { availableNames: list.items.map((a) => a.name) },
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
if (matches.length > 1) {
|
|
57
|
+
return {
|
|
58
|
+
success: false,
|
|
59
|
+
error: {
|
|
60
|
+
code: 'AMBIGUOUS_AGENT_NAME',
|
|
61
|
+
message: `Multiple agents named "${name}" found in project; please disambiguate`,
|
|
62
|
+
data: { matches: matches.map((a) => ({ id: a.id, name: a.name })) },
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return { id: matches[0].id, name: matches[0].name };
|
|
67
|
+
}
|
|
68
|
+
validateAbsolutePath(pathValue) {
|
|
69
|
+
if (!pathValue) {
|
|
70
|
+
return {
|
|
71
|
+
success: false,
|
|
72
|
+
error: {
|
|
73
|
+
code: 'INVALID_PATH',
|
|
74
|
+
message: 'Path parameter is required and cannot be empty.',
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
if (!(0, path_1.isAbsolute)(pathValue)) {
|
|
79
|
+
return {
|
|
80
|
+
success: false,
|
|
81
|
+
error: {
|
|
82
|
+
code: 'INVALID_PATH',
|
|
83
|
+
message: 'Path must be an absolute filesystem path. Please provide the full project root path (e.g., /home/user/project).',
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
async resolveProject(pathValue) {
|
|
90
|
+
const pathValidation = this.validateAbsolutePath(pathValue);
|
|
91
|
+
if (pathValidation) {
|
|
92
|
+
return { error: pathValidation };
|
|
93
|
+
}
|
|
94
|
+
const project = await this.storage.findProjectByPath(pathValue);
|
|
95
|
+
if (!project) {
|
|
96
|
+
logger.debug('No project found for provided path');
|
|
97
|
+
return {
|
|
98
|
+
error: {
|
|
99
|
+
success: false,
|
|
100
|
+
error: {
|
|
101
|
+
code: 'PROJECT_NOT_FOUND',
|
|
102
|
+
message: 'No project found for provided path',
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
logger.debug({ projectId: project.id }, 'Resolved path to project');
|
|
108
|
+
return { project };
|
|
109
|
+
}
|
|
110
|
+
async handleToolCall(tool, params) {
|
|
111
|
+
try {
|
|
112
|
+
const normalizedTool = tool.replace(/[.\-/]/g, '_');
|
|
113
|
+
logger.info({ tool: normalizedTool, originalTool: tool, params }, 'Handling MCP tool call');
|
|
114
|
+
switch (normalizedTool) {
|
|
115
|
+
case 'devchain_create_record':
|
|
116
|
+
return await this.createRecord(params);
|
|
117
|
+
case 'devchain_update_record':
|
|
118
|
+
return await this.updateRecord(params);
|
|
119
|
+
case 'devchain_get_record':
|
|
120
|
+
return await this.getRecord(params);
|
|
121
|
+
case 'devchain_list_records':
|
|
122
|
+
return await this.listRecords(params);
|
|
123
|
+
case 'devchain_add_tags':
|
|
124
|
+
return await this.addTags(params);
|
|
125
|
+
case 'devchain_remove_tags':
|
|
126
|
+
return await this.removeTags(params);
|
|
127
|
+
case 'devchain_list_documents':
|
|
128
|
+
return await this.listDocuments(params);
|
|
129
|
+
case 'devchain_get_document':
|
|
130
|
+
return await this.getDocument(params);
|
|
131
|
+
case 'devchain_create_document':
|
|
132
|
+
return await this.createDocument(params);
|
|
133
|
+
case 'devchain_update_document':
|
|
134
|
+
return await this.updateDocument(params);
|
|
135
|
+
case 'devchain_list_prompts':
|
|
136
|
+
return await this.listPrompts(params);
|
|
137
|
+
case 'devchain_get_prompt':
|
|
138
|
+
return await this.getPrompt(params);
|
|
139
|
+
case 'devchain_list_agents':
|
|
140
|
+
return await this.listAgents(params);
|
|
141
|
+
case 'devchain_get_agent_by_name':
|
|
142
|
+
return await this.getAgentByName(params);
|
|
143
|
+
case 'devchain_list_statuses':
|
|
144
|
+
return await this.listStatuses(params);
|
|
145
|
+
case 'devchain_list_epics':
|
|
146
|
+
return await this.listEpics(params);
|
|
147
|
+
case 'devchain_list_assigned_epics_tasks':
|
|
148
|
+
return await this.listAssignedEpicsTasks(params);
|
|
149
|
+
case 'devchain_create_epic':
|
|
150
|
+
return await this.createEpic(params);
|
|
151
|
+
case 'devchain_get_epic_by_id':
|
|
152
|
+
return await this.getEpicById(params);
|
|
153
|
+
case 'devchain_add_epic_comment':
|
|
154
|
+
return await this.addEpicComment(params);
|
|
155
|
+
case 'devchain_update_epic':
|
|
156
|
+
return await this.updateEpic(params);
|
|
157
|
+
case 'notifications_initialized':
|
|
158
|
+
return { success: true, data: { acknowledged: true } };
|
|
159
|
+
case 'devchain_send_message':
|
|
160
|
+
return await this.sendMessage(params);
|
|
161
|
+
case 'devchain_chat_ack':
|
|
162
|
+
return await this.chatAck(params);
|
|
163
|
+
case 'devchain_chat_list_members':
|
|
164
|
+
return await this.chatListMembers(params);
|
|
165
|
+
case 'devchain_chat_read_history':
|
|
166
|
+
return await this.chatReadHistory(params);
|
|
167
|
+
case 'devchain_activity_start':
|
|
168
|
+
return await this.activityStart(params);
|
|
169
|
+
case 'devchain_activity_finish':
|
|
170
|
+
return await this.activityFinish(params);
|
|
171
|
+
default:
|
|
172
|
+
logger.warn({ tool: normalizedTool }, 'Unknown MCP tool');
|
|
173
|
+
return {
|
|
174
|
+
success: false,
|
|
175
|
+
error: {
|
|
176
|
+
code: 'UNKNOWN_TOOL',
|
|
177
|
+
message: `Unknown tool: ${tool}`,
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
logger.error({ tool, error }, 'MCP tool call failed');
|
|
184
|
+
if (error instanceof zod_1.ZodError) {
|
|
185
|
+
return {
|
|
186
|
+
success: false,
|
|
187
|
+
error: {
|
|
188
|
+
code: 'VALIDATION_ERROR',
|
|
189
|
+
message: 'Invalid parameters supplied to MCP tool.',
|
|
190
|
+
data: { issues: error.issues },
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
success: false,
|
|
196
|
+
error: {
|
|
197
|
+
code: 'INTERNAL_ERROR',
|
|
198
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async handleResourceRequest(uri) {
|
|
204
|
+
try {
|
|
205
|
+
logger.info({ uri }, 'Handling MCP resource request');
|
|
206
|
+
if (uri.startsWith('doc://')) {
|
|
207
|
+
return await this.resolveDocumentResource(uri);
|
|
208
|
+
}
|
|
209
|
+
if (uri.startsWith('prompt://')) {
|
|
210
|
+
return await this.resolvePromptResource(uri);
|
|
211
|
+
}
|
|
212
|
+
return {
|
|
213
|
+
success: false,
|
|
214
|
+
error: {
|
|
215
|
+
code: 'UNKNOWN_RESOURCE',
|
|
216
|
+
message: `Unknown resource: ${uri}`,
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
logger.error({ uri, error }, 'MCP resource handler failed');
|
|
222
|
+
return {
|
|
223
|
+
success: false,
|
|
224
|
+
error: {
|
|
225
|
+
code: 'INTERNAL_ERROR',
|
|
226
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
async createRecord(params) {
|
|
232
|
+
const validated = mcp_dto_1.CreateRecordParamsSchema.parse(params);
|
|
233
|
+
const record = await this.storage.createRecord({
|
|
234
|
+
epicId: validated.epicId,
|
|
235
|
+
type: validated.type,
|
|
236
|
+
data: validated.data,
|
|
237
|
+
tags: validated.tags || [],
|
|
238
|
+
});
|
|
239
|
+
const response = {
|
|
240
|
+
id: record.id,
|
|
241
|
+
epicId: record.epicId,
|
|
242
|
+
type: record.type,
|
|
243
|
+
data: record.data,
|
|
244
|
+
tags: record.tags,
|
|
245
|
+
version: record.version,
|
|
246
|
+
createdAt: record.createdAt,
|
|
247
|
+
updatedAt: record.updatedAt,
|
|
248
|
+
};
|
|
249
|
+
return { success: true, data: response };
|
|
250
|
+
}
|
|
251
|
+
async updateRecord(params) {
|
|
252
|
+
const validated = mcp_dto_1.UpdateRecordParamsSchema.parse(params);
|
|
253
|
+
const record = await this.storage.updateRecord(validated.id, {
|
|
254
|
+
data: validated.data,
|
|
255
|
+
type: validated.type,
|
|
256
|
+
tags: validated.tags,
|
|
257
|
+
}, validated.version);
|
|
258
|
+
const response = {
|
|
259
|
+
id: record.id,
|
|
260
|
+
epicId: record.epicId,
|
|
261
|
+
type: record.type,
|
|
262
|
+
data: record.data,
|
|
263
|
+
tags: record.tags,
|
|
264
|
+
version: record.version,
|
|
265
|
+
createdAt: record.createdAt,
|
|
266
|
+
updatedAt: record.updatedAt,
|
|
267
|
+
};
|
|
268
|
+
return { success: true, data: response };
|
|
269
|
+
}
|
|
270
|
+
async getRecord(params) {
|
|
271
|
+
const validated = mcp_dto_1.GetRecordParamsSchema.parse(params);
|
|
272
|
+
const record = await this.storage.getRecord(validated.id);
|
|
273
|
+
const response = {
|
|
274
|
+
id: record.id,
|
|
275
|
+
epicId: record.epicId,
|
|
276
|
+
type: record.type,
|
|
277
|
+
data: record.data,
|
|
278
|
+
tags: record.tags,
|
|
279
|
+
version: record.version,
|
|
280
|
+
createdAt: record.createdAt,
|
|
281
|
+
updatedAt: record.updatedAt,
|
|
282
|
+
};
|
|
283
|
+
return { success: true, data: response };
|
|
284
|
+
}
|
|
285
|
+
async listRecords(params) {
|
|
286
|
+
const validated = mcp_dto_1.ListRecordsParamsSchema.parse(params);
|
|
287
|
+
const result = await this.storage.listRecords(validated.epicId, {
|
|
288
|
+
limit: validated.limit,
|
|
289
|
+
offset: validated.offset,
|
|
290
|
+
});
|
|
291
|
+
let filtered = result.items;
|
|
292
|
+
if (validated.type) {
|
|
293
|
+
filtered = filtered.filter((r) => r.type === validated.type);
|
|
294
|
+
}
|
|
295
|
+
if (validated.tags && validated.tags.length > 0) {
|
|
296
|
+
filtered = filtered.filter((r) => {
|
|
297
|
+
return validated.tags.every((tag) => r.tags.includes(tag));
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
const response = {
|
|
301
|
+
records: filtered.map((r) => ({
|
|
302
|
+
id: r.id,
|
|
303
|
+
epicId: r.epicId,
|
|
304
|
+
type: r.type,
|
|
305
|
+
data: r.data,
|
|
306
|
+
tags: r.tags,
|
|
307
|
+
version: r.version,
|
|
308
|
+
createdAt: r.createdAt,
|
|
309
|
+
updatedAt: r.updatedAt,
|
|
310
|
+
})),
|
|
311
|
+
total: filtered.length,
|
|
312
|
+
};
|
|
313
|
+
return { success: true, data: response };
|
|
314
|
+
}
|
|
315
|
+
async addTags(params) {
|
|
316
|
+
const validated = mcp_dto_1.AddTagsParamsSchema.parse(params);
|
|
317
|
+
const record = await this.storage.getRecord(validated.id);
|
|
318
|
+
const newTags = Array.from(new Set([...record.tags, ...validated.tags]));
|
|
319
|
+
const updated = await this.storage.updateRecord(validated.id, { tags: newTags }, record.version);
|
|
320
|
+
const response = {
|
|
321
|
+
id: updated.id,
|
|
322
|
+
tags: updated.tags,
|
|
323
|
+
};
|
|
324
|
+
return { success: true, data: response };
|
|
325
|
+
}
|
|
326
|
+
async removeTags(params) {
|
|
327
|
+
const validated = mcp_dto_1.RemoveTagsParamsSchema.parse(params);
|
|
328
|
+
const record = await this.storage.getRecord(validated.id);
|
|
329
|
+
const newTags = record.tags.filter((tag) => !validated.tags.includes(tag));
|
|
330
|
+
const updated = await this.storage.updateRecord(validated.id, { tags: newTags }, record.version);
|
|
331
|
+
const response = {
|
|
332
|
+
id: updated.id,
|
|
333
|
+
tags: updated.tags,
|
|
334
|
+
};
|
|
335
|
+
return { success: true, data: response };
|
|
336
|
+
}
|
|
337
|
+
async listDocuments(params) {
|
|
338
|
+
const validated = mcp_dto_1.ListDocumentsParamsSchema.parse(params);
|
|
339
|
+
const pathValidation = this.validateAbsolutePath(validated.path);
|
|
340
|
+
if (pathValidation) {
|
|
341
|
+
return pathValidation;
|
|
342
|
+
}
|
|
343
|
+
const project = await this.storage.findProjectByPath(validated.path);
|
|
344
|
+
if (!project) {
|
|
345
|
+
logger.debug({ path: validated.path }, 'No project found for path, returning empty results');
|
|
346
|
+
return {
|
|
347
|
+
success: true,
|
|
348
|
+
data: {
|
|
349
|
+
documents: [],
|
|
350
|
+
total: 0,
|
|
351
|
+
limit: validated.limit ?? 100,
|
|
352
|
+
offset: validated.offset ?? 0,
|
|
353
|
+
},
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
logger.debug({ path: validated.path, projectId: project.id }, 'Resolved path to project');
|
|
357
|
+
const filters = {
|
|
358
|
+
projectId: project.id,
|
|
359
|
+
};
|
|
360
|
+
if (validated.tags?.length) {
|
|
361
|
+
filters.tags = validated.tags;
|
|
362
|
+
}
|
|
363
|
+
if (validated.q) {
|
|
364
|
+
filters.q = validated.q;
|
|
365
|
+
}
|
|
366
|
+
if (validated.limit !== undefined) {
|
|
367
|
+
filters.limit = validated.limit;
|
|
368
|
+
}
|
|
369
|
+
if (validated.offset !== undefined) {
|
|
370
|
+
filters.offset = validated.offset;
|
|
371
|
+
}
|
|
372
|
+
const result = await this.storage.listDocuments(filters);
|
|
373
|
+
const response = {
|
|
374
|
+
documents: result.items.map((doc) => this.mapDocumentSummary(doc)),
|
|
375
|
+
total: result.total,
|
|
376
|
+
limit: result.limit,
|
|
377
|
+
offset: result.offset,
|
|
378
|
+
};
|
|
379
|
+
return { success: true, data: response };
|
|
380
|
+
}
|
|
381
|
+
async getDocument(params) {
|
|
382
|
+
const validated = mcp_dto_1.GetDocumentParamsSchema.parse(params);
|
|
383
|
+
const includeLinks = validated.includeLinks ?? 'meta';
|
|
384
|
+
let document;
|
|
385
|
+
if (validated.id) {
|
|
386
|
+
document = await this.storage.getDocument({ id: validated.id });
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
const projectId = validated.projectId === '' ? null : validated.projectId;
|
|
390
|
+
document = await this.storage.getDocument({ slug: validated.slug, projectId });
|
|
391
|
+
}
|
|
392
|
+
const response = {
|
|
393
|
+
document: this.mapDocumentDetail(document),
|
|
394
|
+
links: [],
|
|
395
|
+
};
|
|
396
|
+
let cache = new Map();
|
|
397
|
+
if (includeLinks !== 'none') {
|
|
398
|
+
const collected = await this.collectDocumentLinks(document);
|
|
399
|
+
response.links = collected.links;
|
|
400
|
+
cache = collected.cache;
|
|
401
|
+
if (includeLinks === 'inline') {
|
|
402
|
+
const inline = await this.buildInlineResolution(document, cache, validated.maxDepth ?? 1, validated.maxBytes ?? this.DEFAULT_INLINE_MAX_BYTES);
|
|
403
|
+
response.resolved = inline;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return { success: true, data: response };
|
|
407
|
+
}
|
|
408
|
+
async createDocument(params) {
|
|
409
|
+
const validated = mcp_dto_1.CreateDocumentParamsSchema.parse(params);
|
|
410
|
+
const pathValidation = this.validateAbsolutePath(validated.path);
|
|
411
|
+
if (pathValidation) {
|
|
412
|
+
return pathValidation;
|
|
413
|
+
}
|
|
414
|
+
const project = await this.storage.findProjectByPath(validated.path);
|
|
415
|
+
if (!project) {
|
|
416
|
+
return {
|
|
417
|
+
success: false,
|
|
418
|
+
error: {
|
|
419
|
+
code: 'PROJECT_NOT_FOUND',
|
|
420
|
+
message: `No project found for path: ${validated.path}`,
|
|
421
|
+
},
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
logger.debug({ path: validated.path, projectId: project.id }, 'Resolved path to project for document creation');
|
|
425
|
+
const document = await this.storage.createDocument({
|
|
426
|
+
projectId: project.id,
|
|
427
|
+
title: validated.title,
|
|
428
|
+
contentMd: validated.contentMd,
|
|
429
|
+
tags: validated.tags,
|
|
430
|
+
});
|
|
431
|
+
const response = {
|
|
432
|
+
document: this.mapDocumentDetail(document),
|
|
433
|
+
};
|
|
434
|
+
return { success: true, data: response };
|
|
435
|
+
}
|
|
436
|
+
async updateDocument(params) {
|
|
437
|
+
const validated = mcp_dto_1.UpdateDocumentParamsSchema.parse(params);
|
|
438
|
+
const document = await this.storage.updateDocument(validated.id, {
|
|
439
|
+
title: validated.title,
|
|
440
|
+
slug: validated.slug,
|
|
441
|
+
contentMd: validated.contentMd,
|
|
442
|
+
tags: validated.tags,
|
|
443
|
+
archived: validated.archived,
|
|
444
|
+
version: validated.version,
|
|
445
|
+
});
|
|
446
|
+
const response = {
|
|
447
|
+
document: this.mapDocumentDetail(document),
|
|
448
|
+
};
|
|
449
|
+
return { success: true, data: response };
|
|
450
|
+
}
|
|
451
|
+
async listPrompts(params) {
|
|
452
|
+
const validated = mcp_dto_1.ListPromptsParamsSchema.parse(params);
|
|
453
|
+
let projectId;
|
|
454
|
+
if (validated.path) {
|
|
455
|
+
const pathCheck = this.validateAbsolutePath(validated.path);
|
|
456
|
+
if (pathCheck) {
|
|
457
|
+
return pathCheck;
|
|
458
|
+
}
|
|
459
|
+
const project = await this.storage.findProjectByPath(validated.path);
|
|
460
|
+
if (!project) {
|
|
461
|
+
logger.debug({ path: validated.path }, 'No project found for path; returning empty prompts');
|
|
462
|
+
return { success: true, data: { prompts: [], total: 0 } };
|
|
463
|
+
}
|
|
464
|
+
projectId = project.id;
|
|
465
|
+
}
|
|
466
|
+
else if (validated.projectId) {
|
|
467
|
+
projectId = validated.projectId;
|
|
468
|
+
logger.warn('ListPrompts called with deprecated projectId parameter; prefer path');
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
return {
|
|
472
|
+
success: false,
|
|
473
|
+
error: { code: 'INVALID_PARAMS', message: 'path is required for devchain_list_prompts' },
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
const result = await this.storage.listPrompts(projectId ?? null, undefined);
|
|
477
|
+
let items = result.items;
|
|
478
|
+
if (validated.tags?.length) {
|
|
479
|
+
items = items.filter((prompt) => validated.tags.every((tag) => prompt.tags.includes(tag)));
|
|
480
|
+
}
|
|
481
|
+
if (validated.q) {
|
|
482
|
+
const query = validated.q.toLowerCase();
|
|
483
|
+
items = items.filter((prompt) => prompt.title.toLowerCase().includes(query) ||
|
|
484
|
+
prompt.content.toLowerCase().includes(query));
|
|
485
|
+
}
|
|
486
|
+
const response = {
|
|
487
|
+
prompts: items.map((prompt) => this.mapPromptSummary(prompt)),
|
|
488
|
+
total: items.length,
|
|
489
|
+
};
|
|
490
|
+
return { success: true, data: response };
|
|
491
|
+
}
|
|
492
|
+
async getPrompt(params) {
|
|
493
|
+
const validated = mcp_dto_1.GetPromptParamsSchema.parse(params);
|
|
494
|
+
let prompt;
|
|
495
|
+
if (validated.id) {
|
|
496
|
+
prompt = await this.storage.getPrompt(validated.id);
|
|
497
|
+
}
|
|
498
|
+
else if (validated.name) {
|
|
499
|
+
let projectId;
|
|
500
|
+
if (validated.path) {
|
|
501
|
+
const pathCheck = this.validateAbsolutePath(validated.path);
|
|
502
|
+
if (pathCheck)
|
|
503
|
+
return pathCheck;
|
|
504
|
+
const project = await this.storage.findProjectByPath(validated.path);
|
|
505
|
+
if (!project) {
|
|
506
|
+
return {
|
|
507
|
+
success: false,
|
|
508
|
+
error: { code: 'PROJECT_NOT_FOUND', message: 'No project found for provided path' },
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
projectId = project.id;
|
|
512
|
+
}
|
|
513
|
+
else if (validated.projectId) {
|
|
514
|
+
projectId = validated.projectId;
|
|
515
|
+
logger.warn('GetPrompt called with deprecated projectId parameter; prefer path');
|
|
516
|
+
}
|
|
517
|
+
else {
|
|
518
|
+
return {
|
|
519
|
+
success: false,
|
|
520
|
+
error: {
|
|
521
|
+
code: 'INVALID_PARAMS',
|
|
522
|
+
message: 'path is required when querying prompt by name',
|
|
523
|
+
},
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
const list = await this.storage.listPrompts(projectId ?? null, undefined);
|
|
527
|
+
prompt = list.items.find((item) => {
|
|
528
|
+
if (item.title !== validated.name) {
|
|
529
|
+
return false;
|
|
530
|
+
}
|
|
531
|
+
if (validated.version !== undefined) {
|
|
532
|
+
return item.version === validated.version;
|
|
533
|
+
}
|
|
534
|
+
return true;
|
|
535
|
+
});
|
|
536
|
+
if (prompt) {
|
|
537
|
+
prompt = await this.storage.getPrompt(prompt.id);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
if (!prompt) {
|
|
541
|
+
throw new Error('Prompt not found');
|
|
542
|
+
}
|
|
543
|
+
const response = {
|
|
544
|
+
prompt: this.mapPromptSummary(prompt),
|
|
545
|
+
};
|
|
546
|
+
return { success: true, data: response };
|
|
547
|
+
}
|
|
548
|
+
async listAgents(params) {
|
|
549
|
+
const validated = mcp_dto_1.ListAgentsParamsSchema.parse(params);
|
|
550
|
+
const pathValidation = this.validateAbsolutePath(validated.path);
|
|
551
|
+
if (pathValidation) {
|
|
552
|
+
return pathValidation;
|
|
553
|
+
}
|
|
554
|
+
const project = await this.storage.findProjectByPath(validated.path);
|
|
555
|
+
if (!project) {
|
|
556
|
+
logger.debug({ path: validated.path }, 'No project found for path, returning empty agent list');
|
|
557
|
+
return {
|
|
558
|
+
success: true,
|
|
559
|
+
data: {
|
|
560
|
+
agents: [],
|
|
561
|
+
total: 0,
|
|
562
|
+
limit: validated.limit ?? 100,
|
|
563
|
+
offset: validated.offset ?? 0,
|
|
564
|
+
},
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
const limit = validated.limit ?? 100;
|
|
568
|
+
const offset = validated.offset ?? 0;
|
|
569
|
+
const normalizedQuery = validated.q?.toLowerCase();
|
|
570
|
+
const fetchOptions = normalizedQuery ? { limit: limit + offset, offset: 0 } : { limit, offset };
|
|
571
|
+
const result = await this.storage.listAgents(project.id, fetchOptions);
|
|
572
|
+
let agents = result.items;
|
|
573
|
+
if (normalizedQuery) {
|
|
574
|
+
agents = agents.filter((agent) => agent.name.toLowerCase().includes(normalizedQuery));
|
|
575
|
+
}
|
|
576
|
+
const paginatedAgents = normalizedQuery ? agents.slice(offset, offset + limit) : agents;
|
|
577
|
+
const response = {
|
|
578
|
+
agents: paginatedAgents.map((agent) => ({
|
|
579
|
+
id: agent.id,
|
|
580
|
+
name: agent.name,
|
|
581
|
+
profileId: agent.profileId,
|
|
582
|
+
})),
|
|
583
|
+
total: normalizedQuery ? agents.length : result.total,
|
|
584
|
+
limit,
|
|
585
|
+
offset,
|
|
586
|
+
};
|
|
587
|
+
return { success: true, data: response };
|
|
588
|
+
}
|
|
589
|
+
async getAgentByName(params) {
|
|
590
|
+
const validated = mcp_dto_1.GetAgentByNameParamsSchema.parse(params);
|
|
591
|
+
const pathValidation = this.validateAbsolutePath(validated.path);
|
|
592
|
+
if (pathValidation) {
|
|
593
|
+
return pathValidation;
|
|
594
|
+
}
|
|
595
|
+
const project = await this.storage.findProjectByPath(validated.path);
|
|
596
|
+
if (!project) {
|
|
597
|
+
return {
|
|
598
|
+
success: false,
|
|
599
|
+
error: {
|
|
600
|
+
code: 'PROJECT_NOT_FOUND',
|
|
601
|
+
message: `No project found for path: ${validated.path}`,
|
|
602
|
+
},
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
const normalizedName = validated.name.trim().toLowerCase();
|
|
606
|
+
const agentsList = await this.storage.listAgents(project.id, { limit: 1000, offset: 0 });
|
|
607
|
+
const candidate = agentsList.items.find((agent) => agent.name.toLowerCase() === normalizedName);
|
|
608
|
+
if (!candidate) {
|
|
609
|
+
return {
|
|
610
|
+
success: false,
|
|
611
|
+
error: {
|
|
612
|
+
code: 'AGENT_NOT_FOUND',
|
|
613
|
+
message: `Agent "${validated.name}" not found in project`,
|
|
614
|
+
data: {
|
|
615
|
+
availableNames: agentsList.items.map((agent) => agent.name),
|
|
616
|
+
},
|
|
617
|
+
},
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
let agentWithProfile;
|
|
621
|
+
try {
|
|
622
|
+
agentWithProfile = await this.storage.getAgentByName(project.id, candidate.name);
|
|
623
|
+
}
|
|
624
|
+
catch (error) {
|
|
625
|
+
if (error instanceof error_types_1.NotFoundError) {
|
|
626
|
+
return {
|
|
627
|
+
success: false,
|
|
628
|
+
error: {
|
|
629
|
+
code: 'AGENT_NOT_FOUND',
|
|
630
|
+
message: `Agent "${validated.name}" not found in project`,
|
|
631
|
+
data: {
|
|
632
|
+
availableNames: agentsList.items.map((agent) => agent.name),
|
|
633
|
+
},
|
|
634
|
+
},
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
logger.warn({ projectId: project.id, name: candidate.name, error }, 'Agent lookup failed after matching by name');
|
|
638
|
+
throw error;
|
|
639
|
+
}
|
|
640
|
+
const profile = agentWithProfile.profile;
|
|
641
|
+
const resolvedInstructions = profile
|
|
642
|
+
? await this.instructionsResolver.resolve(project.id, profile.instructions ?? null, {
|
|
643
|
+
maxBytes: this.DEFAULT_INLINE_MAX_BYTES,
|
|
644
|
+
})
|
|
645
|
+
: null;
|
|
646
|
+
if (profile && this.featureFlags.enableProfileInstructionTemplates) {
|
|
647
|
+
}
|
|
648
|
+
const response = {
|
|
649
|
+
agent: {
|
|
650
|
+
id: agentWithProfile.id,
|
|
651
|
+
name: agentWithProfile.name,
|
|
652
|
+
profileId: agentWithProfile.profileId,
|
|
653
|
+
profile: profile
|
|
654
|
+
? {
|
|
655
|
+
id: profile.id,
|
|
656
|
+
name: profile.name,
|
|
657
|
+
instructions: profile.instructions ?? null,
|
|
658
|
+
instructionsResolved: resolvedInstructions ?? undefined,
|
|
659
|
+
}
|
|
660
|
+
: undefined,
|
|
661
|
+
},
|
|
662
|
+
};
|
|
663
|
+
return { success: true, data: response };
|
|
664
|
+
}
|
|
665
|
+
async listStatuses(params) {
|
|
666
|
+
const validated = mcp_dto_1.ListStatusesParamsSchema.parse(params);
|
|
667
|
+
const resolution = await this.resolveProject(validated.path);
|
|
668
|
+
if (!resolution.project) {
|
|
669
|
+
return resolution.error;
|
|
670
|
+
}
|
|
671
|
+
const result = await this.storage.listStatuses(resolution.project.id, {
|
|
672
|
+
limit: 1000,
|
|
673
|
+
offset: 0,
|
|
674
|
+
});
|
|
675
|
+
const response = {
|
|
676
|
+
statuses: result.items.map((status) => this.mapStatusSummary(status)),
|
|
677
|
+
};
|
|
678
|
+
return { success: true, data: response };
|
|
679
|
+
}
|
|
680
|
+
async listEpics(params) {
|
|
681
|
+
const validated = mcp_dto_1.ListEpicsParamsSchema.parse(params);
|
|
682
|
+
const resolution = await this.resolveProject(validated.path);
|
|
683
|
+
if (!resolution.project) {
|
|
684
|
+
return resolution.error;
|
|
685
|
+
}
|
|
686
|
+
const project = resolution.project;
|
|
687
|
+
let statusId;
|
|
688
|
+
if (validated.statusName) {
|
|
689
|
+
const status = await this.storage.findStatusByName(project.id, validated.statusName);
|
|
690
|
+
if (!status) {
|
|
691
|
+
return {
|
|
692
|
+
success: false,
|
|
693
|
+
error: {
|
|
694
|
+
code: 'STATUS_NOT_FOUND',
|
|
695
|
+
message: `Status "${validated.statusName}" was not found for project ${project.id}.`,
|
|
696
|
+
},
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
statusId = status.id;
|
|
700
|
+
}
|
|
701
|
+
const limit = validated.limit ?? 100;
|
|
702
|
+
const offset = validated.offset ?? 0;
|
|
703
|
+
const query = validated.q?.trim();
|
|
704
|
+
const result = await this.storage.listProjectEpics(project.id, {
|
|
705
|
+
statusId,
|
|
706
|
+
q: query && query.length ? query : undefined,
|
|
707
|
+
limit,
|
|
708
|
+
offset,
|
|
709
|
+
});
|
|
710
|
+
const statusesResult = await this.storage.listStatuses(project.id, {
|
|
711
|
+
limit: 1000,
|
|
712
|
+
offset: 0,
|
|
713
|
+
});
|
|
714
|
+
const statusById = new Map();
|
|
715
|
+
for (const s of statusesResult.items)
|
|
716
|
+
statusById.set(s.id, s);
|
|
717
|
+
const agentIds = new Set();
|
|
718
|
+
for (const epic of result.items) {
|
|
719
|
+
if (epic.agentId)
|
|
720
|
+
agentIds.add(epic.agentId);
|
|
721
|
+
}
|
|
722
|
+
const agentNameById = new Map();
|
|
723
|
+
for (const agentId of agentIds) {
|
|
724
|
+
try {
|
|
725
|
+
const agent = await this.storage.getAgent(agentId);
|
|
726
|
+
agentNameById.set(agentId, agent.name);
|
|
727
|
+
}
|
|
728
|
+
catch (error) {
|
|
729
|
+
logger.warn({ agentId }, 'Failed to resolve agent name');
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
const epicsWithStatus = result.items.map((epic) => {
|
|
733
|
+
const summary = this.mapEpicSummary(epic, agentNameById);
|
|
734
|
+
const s = statusById.get(epic.statusId);
|
|
735
|
+
if (s) {
|
|
736
|
+
summary.status = this.mapStatusSummary(s);
|
|
737
|
+
}
|
|
738
|
+
return summary;
|
|
739
|
+
});
|
|
740
|
+
const response = {
|
|
741
|
+
epics: epicsWithStatus,
|
|
742
|
+
total: result.total,
|
|
743
|
+
limit: result.limit,
|
|
744
|
+
offset: result.offset,
|
|
745
|
+
};
|
|
746
|
+
return { success: true, data: response };
|
|
747
|
+
}
|
|
748
|
+
async listAssignedEpicsTasks(params) {
|
|
749
|
+
const validated = mcp_dto_1.ListAssignedEpicsTasksParamsSchema.parse(params);
|
|
750
|
+
const resolution = await this.resolveProject(validated.path);
|
|
751
|
+
if (!resolution.project) {
|
|
752
|
+
return resolution.error;
|
|
753
|
+
}
|
|
754
|
+
const project = resolution.project;
|
|
755
|
+
const limit = validated.limit ?? 100;
|
|
756
|
+
const offset = validated.offset ?? 0;
|
|
757
|
+
try {
|
|
758
|
+
const result = await this.storage.listAssignedEpics(project.id, {
|
|
759
|
+
agentName: validated.agentName,
|
|
760
|
+
limit,
|
|
761
|
+
offset,
|
|
762
|
+
});
|
|
763
|
+
const statusesResult = await this.storage.listStatuses(project.id, {
|
|
764
|
+
limit: 1000,
|
|
765
|
+
offset: 0,
|
|
766
|
+
});
|
|
767
|
+
const statusById = new Map();
|
|
768
|
+
for (const s of statusesResult.items)
|
|
769
|
+
statusById.set(s.id, s);
|
|
770
|
+
const agentIds = new Set();
|
|
771
|
+
for (const epic of result.items) {
|
|
772
|
+
if (epic.agentId)
|
|
773
|
+
agentIds.add(epic.agentId);
|
|
774
|
+
}
|
|
775
|
+
const agentNameById = new Map();
|
|
776
|
+
for (const agentId of agentIds) {
|
|
777
|
+
try {
|
|
778
|
+
const agent = await this.storage.getAgent(agentId);
|
|
779
|
+
agentNameById.set(agentId, agent.name);
|
|
780
|
+
}
|
|
781
|
+
catch (error) {
|
|
782
|
+
logger.warn({ agentId }, 'Failed to resolve agent name');
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
const epicsWithStatus = result.items.map((epic) => {
|
|
786
|
+
const summary = this.mapEpicSummary(epic, agentNameById);
|
|
787
|
+
const s = statusById.get(epic.statusId);
|
|
788
|
+
if (s) {
|
|
789
|
+
summary.status = this.mapStatusSummary(s);
|
|
790
|
+
}
|
|
791
|
+
return summary;
|
|
792
|
+
});
|
|
793
|
+
const response = {
|
|
794
|
+
epics: epicsWithStatus,
|
|
795
|
+
total: result.total,
|
|
796
|
+
limit: result.limit,
|
|
797
|
+
offset: result.offset,
|
|
798
|
+
};
|
|
799
|
+
return { success: true, data: response };
|
|
800
|
+
}
|
|
801
|
+
catch (error) {
|
|
802
|
+
if (error instanceof error_types_1.NotFoundError) {
|
|
803
|
+
return {
|
|
804
|
+
success: false,
|
|
805
|
+
error: {
|
|
806
|
+
code: 'AGENT_NOT_FOUND',
|
|
807
|
+
message: `Agent "${validated.agentName}" was not found for project ${project.id}.`,
|
|
808
|
+
},
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
if (error instanceof error_types_1.ValidationError) {
|
|
812
|
+
return {
|
|
813
|
+
success: false,
|
|
814
|
+
error: {
|
|
815
|
+
code: 'VALIDATION_ERROR',
|
|
816
|
+
message: error.message,
|
|
817
|
+
data: error.details,
|
|
818
|
+
},
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
throw error;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
async createEpic(params) {
|
|
825
|
+
if (!this.epicsService) {
|
|
826
|
+
return {
|
|
827
|
+
success: false,
|
|
828
|
+
error: {
|
|
829
|
+
code: 'SERVICE_UNAVAILABLE',
|
|
830
|
+
message: 'Epic creation requires full app context (not available in standalone MCP mode)',
|
|
831
|
+
},
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
const validated = mcp_dto_1.CreateEpicParamsSchema.parse(params);
|
|
835
|
+
const resolution = await this.resolveProject(validated.path);
|
|
836
|
+
if (!resolution.project) {
|
|
837
|
+
return resolution.error;
|
|
838
|
+
}
|
|
839
|
+
const project = resolution.project;
|
|
840
|
+
let statusId;
|
|
841
|
+
if (validated.statusName) {
|
|
842
|
+
const status = await this.storage.findStatusByName(project.id, validated.statusName);
|
|
843
|
+
if (!status) {
|
|
844
|
+
const statusesResult = await this.storage.listStatuses(project.id, {
|
|
845
|
+
limit: 1000,
|
|
846
|
+
offset: 0,
|
|
847
|
+
});
|
|
848
|
+
return {
|
|
849
|
+
success: false,
|
|
850
|
+
error: {
|
|
851
|
+
code: 'STATUS_NOT_FOUND',
|
|
852
|
+
message: `Status "${validated.statusName}" was not found for project.`,
|
|
853
|
+
data: {
|
|
854
|
+
availableStatuses: statusesResult.items.map((s) => ({ id: s.id, name: s.label })),
|
|
855
|
+
},
|
|
856
|
+
},
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
statusId = status.id;
|
|
860
|
+
}
|
|
861
|
+
try {
|
|
862
|
+
const epic = await this.epicsService.createEpicForProject(project.id, {
|
|
863
|
+
title: validated.title,
|
|
864
|
+
description: validated.description ?? null,
|
|
865
|
+
statusId,
|
|
866
|
+
tags: validated.tags ?? [],
|
|
867
|
+
agentName: validated.agentName,
|
|
868
|
+
parentId: validated.parentId ?? null,
|
|
869
|
+
});
|
|
870
|
+
let agentNameById;
|
|
871
|
+
if (epic.agentId) {
|
|
872
|
+
agentNameById = new Map();
|
|
873
|
+
try {
|
|
874
|
+
const agent = await this.storage.getAgent(epic.agentId);
|
|
875
|
+
agentNameById.set(epic.agentId, agent.name);
|
|
876
|
+
}
|
|
877
|
+
catch (error) {
|
|
878
|
+
logger.warn({ agentId: epic.agentId }, 'Failed to resolve agent name');
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
const response = {
|
|
882
|
+
epic: this.mapEpicSummary(epic, agentNameById),
|
|
883
|
+
};
|
|
884
|
+
return { success: true, data: response };
|
|
885
|
+
}
|
|
886
|
+
catch (error) {
|
|
887
|
+
if (error instanceof error_types_1.NotFoundError) {
|
|
888
|
+
return {
|
|
889
|
+
success: false,
|
|
890
|
+
error: {
|
|
891
|
+
code: 'AGENT_NOT_FOUND',
|
|
892
|
+
message: `Agent "${validated.agentName}" was not found for project ${project.id}.`,
|
|
893
|
+
},
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
if (error instanceof error_types_1.ValidationError) {
|
|
897
|
+
return {
|
|
898
|
+
success: false,
|
|
899
|
+
error: {
|
|
900
|
+
code: 'VALIDATION_ERROR',
|
|
901
|
+
message: error.message,
|
|
902
|
+
data: error.details,
|
|
903
|
+
},
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
throw error;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
async getEpicById(params) {
|
|
910
|
+
const validated = mcp_dto_1.GetEpicByIdParamsSchema.parse(params);
|
|
911
|
+
const resolution = await this.resolveProject(validated.path);
|
|
912
|
+
if (!resolution.project) {
|
|
913
|
+
return resolution.error;
|
|
914
|
+
}
|
|
915
|
+
const project = resolution.project;
|
|
916
|
+
let epic;
|
|
917
|
+
try {
|
|
918
|
+
epic = await this.storage.getEpic(validated.id);
|
|
919
|
+
}
|
|
920
|
+
catch (error) {
|
|
921
|
+
if (error instanceof error_types_1.NotFoundError) {
|
|
922
|
+
return {
|
|
923
|
+
success: false,
|
|
924
|
+
error: {
|
|
925
|
+
code: 'EPIC_NOT_FOUND',
|
|
926
|
+
message: `Epic ${validated.id} was not found.`,
|
|
927
|
+
},
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
throw error;
|
|
931
|
+
}
|
|
932
|
+
if (epic.projectId !== project.id) {
|
|
933
|
+
return {
|
|
934
|
+
success: false,
|
|
935
|
+
error: {
|
|
936
|
+
code: 'EPIC_NOT_FOUND',
|
|
937
|
+
message: `Epic ${validated.id} does not belong to the resolved project.`,
|
|
938
|
+
},
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
const commentsResult = await this.storage.listEpicComments(epic.id, {
|
|
942
|
+
limit: 250,
|
|
943
|
+
offset: 0,
|
|
944
|
+
});
|
|
945
|
+
const subEpicsResult = await this.storage.listSubEpics(epic.id, { limit: 250, offset: 0 });
|
|
946
|
+
const statusesResult = await this.storage.listStatuses(project.id, {
|
|
947
|
+
limit: 1000,
|
|
948
|
+
offset: 0,
|
|
949
|
+
});
|
|
950
|
+
const statusById = new Map();
|
|
951
|
+
for (const s of statusesResult.items)
|
|
952
|
+
statusById.set(s.id, s);
|
|
953
|
+
const agentIds = new Set();
|
|
954
|
+
if (epic.agentId)
|
|
955
|
+
agentIds.add(epic.agentId);
|
|
956
|
+
for (const child of subEpicsResult.items) {
|
|
957
|
+
if (child.agentId)
|
|
958
|
+
agentIds.add(child.agentId);
|
|
959
|
+
}
|
|
960
|
+
const agentNameById = new Map();
|
|
961
|
+
for (const agentId of agentIds) {
|
|
962
|
+
try {
|
|
963
|
+
const agent = await this.storage.getAgent(agentId);
|
|
964
|
+
agentNameById.set(agentId, agent.name);
|
|
965
|
+
}
|
|
966
|
+
catch (error) {
|
|
967
|
+
logger.warn({ agentId }, 'Failed to resolve agent name');
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
let parentSummary;
|
|
971
|
+
if (epic.parentId) {
|
|
972
|
+
try {
|
|
973
|
+
const parent = await this.storage.getEpic(epic.parentId);
|
|
974
|
+
if (parent.projectId === project.id) {
|
|
975
|
+
parentSummary = this.mapEpicParent(parent);
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
catch (error) {
|
|
979
|
+
if (error instanceof error_types_1.NotFoundError) {
|
|
980
|
+
logger.warn({ epicId: epic.id, parentId: epic.parentId }, 'Parent epic missing');
|
|
981
|
+
}
|
|
982
|
+
else {
|
|
983
|
+
throw error;
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
const epicSummary = this.mapEpicSummary(epic, agentNameById);
|
|
988
|
+
const epicStatus = statusById.get(epic.statusId);
|
|
989
|
+
if (epicStatus) {
|
|
990
|
+
epicSummary.status = this.mapStatusSummary(epicStatus);
|
|
991
|
+
}
|
|
992
|
+
const subEpicsWithStatus = subEpicsResult.items.map((child) => {
|
|
993
|
+
const childSummary = this.mapEpicChild(child);
|
|
994
|
+
const childStatus = statusById.get(child.statusId);
|
|
995
|
+
if (childStatus) {
|
|
996
|
+
childSummary.status = this.mapStatusSummary(childStatus);
|
|
997
|
+
}
|
|
998
|
+
return childSummary;
|
|
999
|
+
});
|
|
1000
|
+
const response = {
|
|
1001
|
+
epic: epicSummary,
|
|
1002
|
+
comments: [...commentsResult.items]
|
|
1003
|
+
.reverse()
|
|
1004
|
+
.map((comment, idx) => ({ ...this.mapEpicComment(comment), commentNumber: idx + 1 })),
|
|
1005
|
+
subEpics: subEpicsWithStatus,
|
|
1006
|
+
};
|
|
1007
|
+
if (parentSummary) {
|
|
1008
|
+
response.parent = parentSummary;
|
|
1009
|
+
}
|
|
1010
|
+
return { success: true, data: response };
|
|
1011
|
+
}
|
|
1012
|
+
async addEpicComment(params) {
|
|
1013
|
+
const validated = mcp_dto_1.AddEpicCommentParamsSchema.parse(params);
|
|
1014
|
+
const resolution = await this.resolveProject(validated.path);
|
|
1015
|
+
if (!resolution.project) {
|
|
1016
|
+
return resolution.error;
|
|
1017
|
+
}
|
|
1018
|
+
const project = resolution.project;
|
|
1019
|
+
let epic;
|
|
1020
|
+
try {
|
|
1021
|
+
epic = await this.storage.getEpic(validated.epicId);
|
|
1022
|
+
}
|
|
1023
|
+
catch (error) {
|
|
1024
|
+
if (error instanceof error_types_1.NotFoundError) {
|
|
1025
|
+
return {
|
|
1026
|
+
success: false,
|
|
1027
|
+
error: {
|
|
1028
|
+
code: 'EPIC_NOT_FOUND',
|
|
1029
|
+
message: `Epic ${validated.epicId} was not found.`,
|
|
1030
|
+
},
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
throw error;
|
|
1034
|
+
}
|
|
1035
|
+
if (epic.projectId !== project.id) {
|
|
1036
|
+
return {
|
|
1037
|
+
success: false,
|
|
1038
|
+
error: {
|
|
1039
|
+
code: 'EPIC_NOT_FOUND',
|
|
1040
|
+
message: `Epic ${validated.epicId} does not belong to the resolved project.`,
|
|
1041
|
+
},
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
const comment = await this.storage.createEpicComment({
|
|
1045
|
+
epicId: validated.epicId,
|
|
1046
|
+
authorName: validated.authorName,
|
|
1047
|
+
content: validated.content,
|
|
1048
|
+
});
|
|
1049
|
+
const response = {
|
|
1050
|
+
comment: this.mapEpicComment(comment),
|
|
1051
|
+
};
|
|
1052
|
+
return { success: true, data: response };
|
|
1053
|
+
}
|
|
1054
|
+
async updateEpic(params) {
|
|
1055
|
+
if (!this.epicsService) {
|
|
1056
|
+
return {
|
|
1057
|
+
success: false,
|
|
1058
|
+
error: {
|
|
1059
|
+
code: 'SERVICE_UNAVAILABLE',
|
|
1060
|
+
message: 'Epic updates require full app context (not available in standalone MCP mode)',
|
|
1061
|
+
},
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
1064
|
+
let preprocessedParams = params;
|
|
1065
|
+
if (params && typeof params === 'object' && 'assignment' in params) {
|
|
1066
|
+
const p = params;
|
|
1067
|
+
if (typeof p.assignment === 'string') {
|
|
1068
|
+
try {
|
|
1069
|
+
preprocessedParams = { ...p, assignment: JSON.parse(p.assignment) };
|
|
1070
|
+
}
|
|
1071
|
+
catch {
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
const validated = mcp_dto_1.UpdateEpicParamsSchema.parse(preprocessedParams);
|
|
1076
|
+
const resolution = await this.resolveProject(validated.path);
|
|
1077
|
+
if (!resolution.project) {
|
|
1078
|
+
return resolution.error;
|
|
1079
|
+
}
|
|
1080
|
+
const project = resolution.project;
|
|
1081
|
+
let epic;
|
|
1082
|
+
try {
|
|
1083
|
+
epic = await this.storage.getEpic(validated.id);
|
|
1084
|
+
}
|
|
1085
|
+
catch (error) {
|
|
1086
|
+
if (error instanceof error_types_1.NotFoundError) {
|
|
1087
|
+
return {
|
|
1088
|
+
success: false,
|
|
1089
|
+
error: {
|
|
1090
|
+
code: 'EPIC_NOT_FOUND',
|
|
1091
|
+
message: `Epic ${validated.id} was not found.`,
|
|
1092
|
+
},
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1095
|
+
throw error;
|
|
1096
|
+
}
|
|
1097
|
+
if (epic.projectId !== project.id) {
|
|
1098
|
+
return {
|
|
1099
|
+
success: false,
|
|
1100
|
+
error: {
|
|
1101
|
+
code: 'EPIC_NOT_FOUND',
|
|
1102
|
+
message: `Epic ${validated.id} does not belong to the resolved project.`,
|
|
1103
|
+
},
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
const updateData = {};
|
|
1107
|
+
if (validated.title !== undefined) {
|
|
1108
|
+
updateData.title = validated.title;
|
|
1109
|
+
}
|
|
1110
|
+
if (validated.description !== undefined) {
|
|
1111
|
+
updateData.description = validated.description;
|
|
1112
|
+
}
|
|
1113
|
+
if (validated.statusName) {
|
|
1114
|
+
const status = await this.storage.findStatusByName(project.id, validated.statusName);
|
|
1115
|
+
if (!status) {
|
|
1116
|
+
const statusesResult = await this.storage.listStatuses(project.id, {
|
|
1117
|
+
limit: 1000,
|
|
1118
|
+
offset: 0,
|
|
1119
|
+
});
|
|
1120
|
+
return {
|
|
1121
|
+
success: false,
|
|
1122
|
+
error: {
|
|
1123
|
+
code: 'STATUS_NOT_FOUND',
|
|
1124
|
+
message: `Status "${validated.statusName}" was not found for project.`,
|
|
1125
|
+
data: {
|
|
1126
|
+
availableStatuses: statusesResult.items.map((s) => ({ id: s.id, name: s.label })),
|
|
1127
|
+
},
|
|
1128
|
+
},
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
updateData.statusId = status.id;
|
|
1132
|
+
}
|
|
1133
|
+
if (validated.assignment) {
|
|
1134
|
+
if ('clear' in validated.assignment && validated.assignment.clear) {
|
|
1135
|
+
updateData.agentId = null;
|
|
1136
|
+
}
|
|
1137
|
+
else if ('agentName' in validated.assignment) {
|
|
1138
|
+
try {
|
|
1139
|
+
const agent = await this.storage.getAgentByName(project.id, validated.assignment.agentName);
|
|
1140
|
+
updateData.agentId = agent.id;
|
|
1141
|
+
}
|
|
1142
|
+
catch (error) {
|
|
1143
|
+
if (error instanceof error_types_1.NotFoundError) {
|
|
1144
|
+
const agentsList = await this.storage.listAgents(project.id, {
|
|
1145
|
+
limit: 1000,
|
|
1146
|
+
offset: 0,
|
|
1147
|
+
});
|
|
1148
|
+
return {
|
|
1149
|
+
success: false,
|
|
1150
|
+
error: {
|
|
1151
|
+
code: 'AGENT_NOT_FOUND',
|
|
1152
|
+
message: `Agent "${validated.assignment.agentName}" was not found for project.`,
|
|
1153
|
+
data: {
|
|
1154
|
+
availableAgents: agentsList.items.map((a) => ({ id: a.id, name: a.name })),
|
|
1155
|
+
},
|
|
1156
|
+
},
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
1159
|
+
throw error;
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
if (validated.clearParent) {
|
|
1164
|
+
updateData.parentId = null;
|
|
1165
|
+
}
|
|
1166
|
+
else if (validated.parentId !== undefined) {
|
|
1167
|
+
if (validated.parentId === validated.id) {
|
|
1168
|
+
return {
|
|
1169
|
+
success: false,
|
|
1170
|
+
error: {
|
|
1171
|
+
code: 'PARENT_INVALID',
|
|
1172
|
+
message: 'An epic cannot be its own parent.',
|
|
1173
|
+
},
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
let parentEpic;
|
|
1177
|
+
try {
|
|
1178
|
+
parentEpic = await this.storage.getEpic(validated.parentId);
|
|
1179
|
+
}
|
|
1180
|
+
catch (error) {
|
|
1181
|
+
if (error instanceof error_types_1.NotFoundError) {
|
|
1182
|
+
return {
|
|
1183
|
+
success: false,
|
|
1184
|
+
error: {
|
|
1185
|
+
code: 'PARENT_INVALID',
|
|
1186
|
+
message: `Parent epic ${validated.parentId} was not found.`,
|
|
1187
|
+
},
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1190
|
+
throw error;
|
|
1191
|
+
}
|
|
1192
|
+
if (parentEpic.projectId !== project.id) {
|
|
1193
|
+
return {
|
|
1194
|
+
success: false,
|
|
1195
|
+
error: {
|
|
1196
|
+
code: 'PARENT_INVALID',
|
|
1197
|
+
message: 'Parent epic must belong to the same project.',
|
|
1198
|
+
},
|
|
1199
|
+
};
|
|
1200
|
+
}
|
|
1201
|
+
if (parentEpic.parentId !== null) {
|
|
1202
|
+
return {
|
|
1203
|
+
success: false,
|
|
1204
|
+
error: {
|
|
1205
|
+
code: 'HIERARCHY_CONFLICT',
|
|
1206
|
+
message: 'Only one level of epic hierarchy is allowed. The specified parent already has a parent.',
|
|
1207
|
+
},
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1210
|
+
updateData.parentId = validated.parentId;
|
|
1211
|
+
}
|
|
1212
|
+
if (validated.setTags !== undefined) {
|
|
1213
|
+
updateData.tags = validated.setTags;
|
|
1214
|
+
}
|
|
1215
|
+
else if (validated.addTags || validated.removeTags) {
|
|
1216
|
+
const currentTags = new Set(epic.tags);
|
|
1217
|
+
if (validated.addTags) {
|
|
1218
|
+
validated.addTags.forEach((tag) => currentTags.add(tag));
|
|
1219
|
+
}
|
|
1220
|
+
if (validated.removeTags) {
|
|
1221
|
+
validated.removeTags.forEach((tag) => currentTags.delete(tag));
|
|
1222
|
+
}
|
|
1223
|
+
updateData.tags = Array.from(currentTags);
|
|
1224
|
+
}
|
|
1225
|
+
let updatedEpic;
|
|
1226
|
+
try {
|
|
1227
|
+
updatedEpic = await this.epicsService.updateEpic(validated.id, updateData, validated.version);
|
|
1228
|
+
}
|
|
1229
|
+
catch (error) {
|
|
1230
|
+
if (error instanceof Error && error.message.includes('was modified by another operation')) {
|
|
1231
|
+
const currentEpic = await this.storage.getEpic(validated.id);
|
|
1232
|
+
return {
|
|
1233
|
+
success: false,
|
|
1234
|
+
error: {
|
|
1235
|
+
code: 'VERSION_CONFLICT',
|
|
1236
|
+
message: `Epic version conflict. Expected version ${validated.version}, but current version is ${currentEpic.version}.`,
|
|
1237
|
+
data: {
|
|
1238
|
+
currentVersion: currentEpic.version,
|
|
1239
|
+
},
|
|
1240
|
+
},
|
|
1241
|
+
};
|
|
1242
|
+
}
|
|
1243
|
+
throw error;
|
|
1244
|
+
}
|
|
1245
|
+
let agentNameById;
|
|
1246
|
+
if (updatedEpic.agentId) {
|
|
1247
|
+
agentNameById = new Map();
|
|
1248
|
+
try {
|
|
1249
|
+
const agent = await this.storage.getAgent(updatedEpic.agentId);
|
|
1250
|
+
agentNameById.set(updatedEpic.agentId, agent.name);
|
|
1251
|
+
}
|
|
1252
|
+
catch (error) {
|
|
1253
|
+
logger.warn({ agentId: updatedEpic.agentId }, 'Failed to resolve agent name');
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
const response = {
|
|
1257
|
+
epic: this.mapEpicSummary(updatedEpic, agentNameById),
|
|
1258
|
+
};
|
|
1259
|
+
return { success: true, data: response };
|
|
1260
|
+
}
|
|
1261
|
+
async sendMessage(params) {
|
|
1262
|
+
if (!this.chatService || !this.sessionsService) {
|
|
1263
|
+
return {
|
|
1264
|
+
success: false,
|
|
1265
|
+
error: {
|
|
1266
|
+
code: 'SERVICE_UNAVAILABLE',
|
|
1267
|
+
message: 'Chat functionality requires full app context (not available in standalone MCP mode)',
|
|
1268
|
+
},
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1271
|
+
const validated = mcp_dto_1.SendMessageParamsSchema.parse(params);
|
|
1272
|
+
const resolution = await this.resolveProject(validated.path);
|
|
1273
|
+
if (!resolution.project) {
|
|
1274
|
+
return resolution.error;
|
|
1275
|
+
}
|
|
1276
|
+
const project = resolution.project;
|
|
1277
|
+
try {
|
|
1278
|
+
let senderAgentId = null;
|
|
1279
|
+
const validatedWithRecipient = validated;
|
|
1280
|
+
const recipientType = validatedWithRecipient.recipient ?? 'agents';
|
|
1281
|
+
const senderNameInput = validated.senderName ?? validatedWithRecipient.agentName;
|
|
1282
|
+
if (senderNameInput) {
|
|
1283
|
+
const resolved = await this.resolveAgentByNameUnique(project.id, senderNameInput);
|
|
1284
|
+
if (!('id' in resolved)) {
|
|
1285
|
+
return resolved;
|
|
1286
|
+
}
|
|
1287
|
+
senderAgentId = resolved.id;
|
|
1288
|
+
}
|
|
1289
|
+
let normalizedRecipientIds = [];
|
|
1290
|
+
if (validated.recipientAgentNames && validated.recipientAgentNames.length > 0) {
|
|
1291
|
+
for (const name of validated.recipientAgentNames) {
|
|
1292
|
+
const agent = await this.storage.getAgentByName(project.id, name);
|
|
1293
|
+
normalizedRecipientIds.push(agent.id);
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
normalizedRecipientIds = Array.from(new Set(normalizedRecipientIds.filter((id) => id && id !== senderAgentId)));
|
|
1297
|
+
let threadId = validated.threadId;
|
|
1298
|
+
if (!threadId && senderAgentId) {
|
|
1299
|
+
if (recipientType === 'user') {
|
|
1300
|
+
const direct = await this.chatService.createDirectThread({
|
|
1301
|
+
projectId: project.id,
|
|
1302
|
+
agentId: senderAgentId,
|
|
1303
|
+
});
|
|
1304
|
+
threadId = direct.id;
|
|
1305
|
+
}
|
|
1306
|
+
else {
|
|
1307
|
+
const recips = normalizedRecipientIds;
|
|
1308
|
+
if (recips.length === 0) {
|
|
1309
|
+
return {
|
|
1310
|
+
success: false,
|
|
1311
|
+
error: {
|
|
1312
|
+
code: 'RECIPIENTS_REQUIRED',
|
|
1313
|
+
message: 'Recipient agents must be provided when creating a new thread without threadId.',
|
|
1314
|
+
},
|
|
1315
|
+
};
|
|
1316
|
+
}
|
|
1317
|
+
const groupThread = await this.chatService.createGroupThread({
|
|
1318
|
+
projectId: project.id,
|
|
1319
|
+
agentIds: Array.from(new Set([senderAgentId, ...recips])),
|
|
1320
|
+
createdByType: 'agent',
|
|
1321
|
+
createdByAgentId: senderAgentId,
|
|
1322
|
+
});
|
|
1323
|
+
threadId = groupThread.id;
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
if (!threadId) {
|
|
1327
|
+
return {
|
|
1328
|
+
success: false,
|
|
1329
|
+
error: {
|
|
1330
|
+
code: 'THREAD_REQUIRED',
|
|
1331
|
+
message: 'threadId is required when senderName is not provided',
|
|
1332
|
+
},
|
|
1333
|
+
};
|
|
1334
|
+
}
|
|
1335
|
+
const thread = await this.chatService.getThread(threadId);
|
|
1336
|
+
const message = await this.chatService.createMessage(threadId, {
|
|
1337
|
+
authorType: senderAgentId ? 'agent' : 'user',
|
|
1338
|
+
authorAgentId: senderAgentId ?? undefined,
|
|
1339
|
+
content: validated.message,
|
|
1340
|
+
});
|
|
1341
|
+
let targetAgentIds = normalizedRecipientIds;
|
|
1342
|
+
if (senderAgentId &&
|
|
1343
|
+
thread.members &&
|
|
1344
|
+
thread.members.length > 1 &&
|
|
1345
|
+
targetAgentIds.length === 0) {
|
|
1346
|
+
targetAgentIds = thread.members.filter((id) => id !== senderAgentId);
|
|
1347
|
+
}
|
|
1348
|
+
const activeSessions = await this.sessionsService.listActiveSessions();
|
|
1349
|
+
const delivered = [];
|
|
1350
|
+
for (const agentId of targetAgentIds) {
|
|
1351
|
+
const agent = await this.storage.getAgent(agentId);
|
|
1352
|
+
const session = activeSessions.find((s) => s.agentId === agentId);
|
|
1353
|
+
if (session) {
|
|
1354
|
+
const senderName = senderAgentId
|
|
1355
|
+
? (await this.storage.getAgent(senderAgentId)).name
|
|
1356
|
+
: 'User';
|
|
1357
|
+
const injectionText = `\n[CHAT] From: ${senderName} • Thread: ${threadId}\n${validated.message}\n[ACK] tools/call { name: "devchain_chat_ack", arguments: { thread_id: "${threadId}", message_id: "${message.id}", agent_name: "${agent.name}" } }\n`;
|
|
1358
|
+
await this.sessionsService.injectTextIntoSession(session.id, injectionText);
|
|
1359
|
+
delivered.push({
|
|
1360
|
+
agentId,
|
|
1361
|
+
agentName: agent.name,
|
|
1362
|
+
sessionId: session.id,
|
|
1363
|
+
status: 'delivered',
|
|
1364
|
+
});
|
|
1365
|
+
}
|
|
1366
|
+
else {
|
|
1367
|
+
delivered.push({
|
|
1368
|
+
agentId,
|
|
1369
|
+
agentName: agent.name,
|
|
1370
|
+
sessionId: '',
|
|
1371
|
+
status: 'queued',
|
|
1372
|
+
});
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
const response = {
|
|
1376
|
+
threadId,
|
|
1377
|
+
messageId: message.id,
|
|
1378
|
+
deliveryCount: delivered.filter((d) => d.status === 'delivered').length,
|
|
1379
|
+
delivered,
|
|
1380
|
+
};
|
|
1381
|
+
return { success: true, data: response };
|
|
1382
|
+
}
|
|
1383
|
+
catch (error) {
|
|
1384
|
+
logger.error({ error, params: validated }, 'sendMessage failed');
|
|
1385
|
+
return {
|
|
1386
|
+
success: false,
|
|
1387
|
+
error: {
|
|
1388
|
+
code: 'SEND_MESSAGE_FAILED',
|
|
1389
|
+
message: error instanceof Error ? error.message : 'Failed to send message',
|
|
1390
|
+
},
|
|
1391
|
+
};
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
async chatAck(params) {
|
|
1395
|
+
if (!this.chatService || !this.terminalGateway) {
|
|
1396
|
+
return {
|
|
1397
|
+
success: false,
|
|
1398
|
+
error: {
|
|
1399
|
+
code: 'SERVICE_UNAVAILABLE',
|
|
1400
|
+
message: 'Chat acknowledgment requires full app context (not available in standalone MCP mode)',
|
|
1401
|
+
},
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1404
|
+
const validated = mcp_dto_1.ChatAckParamsSchema.parse(params);
|
|
1405
|
+
const { thread_id: threadId, message_id: messageId } = validated;
|
|
1406
|
+
let agentId = null;
|
|
1407
|
+
try {
|
|
1408
|
+
const thread = await this.chatService.getThread(threadId);
|
|
1409
|
+
const memberIds = thread.members ?? [];
|
|
1410
|
+
let resolved = null;
|
|
1411
|
+
for (const mid of memberIds) {
|
|
1412
|
+
const a = await this.storage.getAgent(mid);
|
|
1413
|
+
if (a.name.toLowerCase() === validated.agent_name.toLowerCase()) {
|
|
1414
|
+
resolved = a.id;
|
|
1415
|
+
break;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
if (!resolved) {
|
|
1419
|
+
return {
|
|
1420
|
+
success: false,
|
|
1421
|
+
error: {
|
|
1422
|
+
code: 'AGENT_NOT_IN_THREAD',
|
|
1423
|
+
message: `Agent ${validated.agent_name} is not a member of thread ${threadId}`,
|
|
1424
|
+
},
|
|
1425
|
+
};
|
|
1426
|
+
}
|
|
1427
|
+
agentId = resolved;
|
|
1428
|
+
const now = new Date().toISOString();
|
|
1429
|
+
await this.storage.markMessageAsRead(messageId, agentId, now);
|
|
1430
|
+
if (this.sessionsService) {
|
|
1431
|
+
const activeSessions = await this.sessionsService.listActiveSessions();
|
|
1432
|
+
const agentSession = activeSessions.find((s) => s.agentId === agentId);
|
|
1433
|
+
if (agentSession && agentSession.tmuxSessionId) {
|
|
1434
|
+
await this.chatService.acknowledgeInvite(threadId, messageId, agentId, agentSession.tmuxSessionId);
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
this.terminalGateway.broadcastEvent(`chat/${threadId}`, 'message.read', {
|
|
1438
|
+
messageId,
|
|
1439
|
+
agentId: agentId,
|
|
1440
|
+
readAt: now,
|
|
1441
|
+
});
|
|
1442
|
+
let agentName = validated.agent_name ?? '';
|
|
1443
|
+
if (!agentName) {
|
|
1444
|
+
try {
|
|
1445
|
+
const a = await this.storage.getAgent(agentId);
|
|
1446
|
+
agentName = a.name;
|
|
1447
|
+
}
|
|
1448
|
+
catch { }
|
|
1449
|
+
}
|
|
1450
|
+
const response = {
|
|
1451
|
+
threadId,
|
|
1452
|
+
messageId,
|
|
1453
|
+
agentId: agentId,
|
|
1454
|
+
agentName,
|
|
1455
|
+
acknowledged: true,
|
|
1456
|
+
};
|
|
1457
|
+
return { success: true, data: response };
|
|
1458
|
+
}
|
|
1459
|
+
catch (error) {
|
|
1460
|
+
logger.error({ error, params: validated }, 'chatAck failed');
|
|
1461
|
+
return {
|
|
1462
|
+
success: false,
|
|
1463
|
+
error: {
|
|
1464
|
+
code: 'CHAT_ACK_FAILED',
|
|
1465
|
+
message: error instanceof Error ? error.message : 'Failed to acknowledge message',
|
|
1466
|
+
},
|
|
1467
|
+
};
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
async chatListMembers(params) {
|
|
1471
|
+
if (!this.chatService || !this.sessionsService) {
|
|
1472
|
+
return {
|
|
1473
|
+
success: false,
|
|
1474
|
+
error: {
|
|
1475
|
+
code: 'SERVICE_UNAVAILABLE',
|
|
1476
|
+
message: 'Chat members listing requires full app context (not available in standalone MCP mode)',
|
|
1477
|
+
},
|
|
1478
|
+
};
|
|
1479
|
+
}
|
|
1480
|
+
const validated = mcp_dto_1.ChatListMembersParamsSchema.parse(params);
|
|
1481
|
+
try {
|
|
1482
|
+
const thread = await this.chatService.getThread(validated.thread_id);
|
|
1483
|
+
const memberIds = thread.members ?? [];
|
|
1484
|
+
if (memberIds.length === 0) {
|
|
1485
|
+
const emptyResponse = {
|
|
1486
|
+
thread: {
|
|
1487
|
+
id: thread.id,
|
|
1488
|
+
title: thread.title,
|
|
1489
|
+
},
|
|
1490
|
+
members: [],
|
|
1491
|
+
total: 0,
|
|
1492
|
+
};
|
|
1493
|
+
return { success: true, data: emptyResponse };
|
|
1494
|
+
}
|
|
1495
|
+
const agents = await Promise.all(memberIds.map(async (agentId) => {
|
|
1496
|
+
try {
|
|
1497
|
+
return await this.storage.getAgent(agentId);
|
|
1498
|
+
}
|
|
1499
|
+
catch (error) {
|
|
1500
|
+
logger.error({ error, agentId, threadId: thread.id }, 'Failed to resolve agent for chat members');
|
|
1501
|
+
throw error;
|
|
1502
|
+
}
|
|
1503
|
+
}));
|
|
1504
|
+
const activeSessions = await this.sessionsService.listActiveSessions();
|
|
1505
|
+
const onlineAgents = new Set(activeSessions.map((session) => session.agentId));
|
|
1506
|
+
const members = agents.map((agent) => ({
|
|
1507
|
+
agent_id: agent.id,
|
|
1508
|
+
agent_name: agent.name,
|
|
1509
|
+
online: onlineAgents.has(agent.id),
|
|
1510
|
+
}));
|
|
1511
|
+
const response = {
|
|
1512
|
+
thread: {
|
|
1513
|
+
id: thread.id,
|
|
1514
|
+
title: thread.title,
|
|
1515
|
+
},
|
|
1516
|
+
members,
|
|
1517
|
+
total: members.length,
|
|
1518
|
+
};
|
|
1519
|
+
return { success: true, data: response };
|
|
1520
|
+
}
|
|
1521
|
+
catch (error) {
|
|
1522
|
+
if (error instanceof common_1.NotFoundException || error instanceof error_types_1.NotFoundError) {
|
|
1523
|
+
return {
|
|
1524
|
+
success: false,
|
|
1525
|
+
error: {
|
|
1526
|
+
code: 'NOT_FOUND',
|
|
1527
|
+
message: `Thread ${validated.thread_id} was not found.`,
|
|
1528
|
+
},
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1531
|
+
logger.error({ error, params: validated }, 'chatListMembers failed');
|
|
1532
|
+
return {
|
|
1533
|
+
success: false,
|
|
1534
|
+
error: {
|
|
1535
|
+
code: 'CHAT_LIST_MEMBERS_FAILED',
|
|
1536
|
+
message: error instanceof Error ? error.message : 'Failed to list chat members',
|
|
1537
|
+
},
|
|
1538
|
+
};
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
async chatReadHistory(params) {
|
|
1542
|
+
if (!this.chatService) {
|
|
1543
|
+
return {
|
|
1544
|
+
success: false,
|
|
1545
|
+
error: {
|
|
1546
|
+
code: 'SERVICE_UNAVAILABLE',
|
|
1547
|
+
message: 'Chat history requires full app context (not available in standalone MCP mode)',
|
|
1548
|
+
},
|
|
1549
|
+
};
|
|
1550
|
+
}
|
|
1551
|
+
const validated = mcp_dto_1.ChatReadHistoryParamsSchema.parse(params);
|
|
1552
|
+
try {
|
|
1553
|
+
const thread = await this.chatService.getThread(validated.thread_id);
|
|
1554
|
+
const limit = validated.limit ?? 50;
|
|
1555
|
+
const validatedWithExcludeSystem = validated;
|
|
1556
|
+
const excludeSystem = typeof validatedWithExcludeSystem.exclude_system === 'boolean'
|
|
1557
|
+
? validatedWithExcludeSystem.exclude_system
|
|
1558
|
+
: true;
|
|
1559
|
+
const messagesList = await this.chatService.listMessages(validated.thread_id, {
|
|
1560
|
+
since: validated.since,
|
|
1561
|
+
limit,
|
|
1562
|
+
offset: 0,
|
|
1563
|
+
});
|
|
1564
|
+
const authorIds = new Set();
|
|
1565
|
+
const targetIds = new Set();
|
|
1566
|
+
for (const m of messagesList.items) {
|
|
1567
|
+
if (m.authorAgentId)
|
|
1568
|
+
authorIds.add(m.authorAgentId);
|
|
1569
|
+
if (m.targets)
|
|
1570
|
+
for (const t of m.targets)
|
|
1571
|
+
targetIds.add(t);
|
|
1572
|
+
}
|
|
1573
|
+
const idToName = new Map();
|
|
1574
|
+
const toLoad = Array.from(new Set([...authorIds, ...targetIds]));
|
|
1575
|
+
for (const id of toLoad) {
|
|
1576
|
+
try {
|
|
1577
|
+
const a = await this.storage.getAgent(id);
|
|
1578
|
+
idToName.set(id, a.name);
|
|
1579
|
+
}
|
|
1580
|
+
catch {
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
const filteredItems = excludeSystem
|
|
1584
|
+
? messagesList.items.filter((m) => m.authorType !== 'system')
|
|
1585
|
+
: messagesList.items;
|
|
1586
|
+
const messages = filteredItems.map((m) => {
|
|
1587
|
+
const base = {
|
|
1588
|
+
id: m.id,
|
|
1589
|
+
author_type: m.authorType,
|
|
1590
|
+
author_agent_id: m.authorAgentId ?? null,
|
|
1591
|
+
author_agent_name: m.authorAgentId ? (idToName.get(m.authorAgentId) ?? null) : null,
|
|
1592
|
+
content: m.content,
|
|
1593
|
+
created_at: m.createdAt,
|
|
1594
|
+
targets: m.targets,
|
|
1595
|
+
};
|
|
1596
|
+
if (m.targets && m.targets.length > 0) {
|
|
1597
|
+
const names = m.targets
|
|
1598
|
+
.map((tid) => idToName.get(tid))
|
|
1599
|
+
.filter((n) => typeof n === 'string' && n.length > 0);
|
|
1600
|
+
if (names.length > 0) {
|
|
1601
|
+
base.target_agent_names = names;
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
return base;
|
|
1605
|
+
});
|
|
1606
|
+
const response = {
|
|
1607
|
+
thread: {
|
|
1608
|
+
id: thread.id,
|
|
1609
|
+
title: thread.title,
|
|
1610
|
+
},
|
|
1611
|
+
messages,
|
|
1612
|
+
has_more: messages.length === limit,
|
|
1613
|
+
};
|
|
1614
|
+
return { success: true, data: response };
|
|
1615
|
+
}
|
|
1616
|
+
catch (error) {
|
|
1617
|
+
if (error instanceof common_1.NotFoundException || error instanceof error_types_1.NotFoundError) {
|
|
1618
|
+
return {
|
|
1619
|
+
success: false,
|
|
1620
|
+
error: {
|
|
1621
|
+
code: 'NOT_FOUND',
|
|
1622
|
+
message: `Thread ${validated.thread_id} was not found.`,
|
|
1623
|
+
},
|
|
1624
|
+
};
|
|
1625
|
+
}
|
|
1626
|
+
logger.error({ error, params: validated }, 'chatReadHistory failed');
|
|
1627
|
+
return {
|
|
1628
|
+
success: false,
|
|
1629
|
+
error: {
|
|
1630
|
+
code: 'CHAT_READ_HISTORY_FAILED',
|
|
1631
|
+
message: error instanceof Error ? error.message : 'Failed to read chat history',
|
|
1632
|
+
},
|
|
1633
|
+
};
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
async activityStart(params) {
|
|
1637
|
+
if (!this.chatService) {
|
|
1638
|
+
return {
|
|
1639
|
+
success: false,
|
|
1640
|
+
error: { code: 'SERVICE_UNAVAILABLE', message: 'Chat service unavailable' },
|
|
1641
|
+
};
|
|
1642
|
+
}
|
|
1643
|
+
const validated = mcp_dto_1.ActivityStartParamsSchema.parse(params);
|
|
1644
|
+
const resolution = await this.resolveProject(validated.path);
|
|
1645
|
+
if (!resolution.project)
|
|
1646
|
+
return resolution.error;
|
|
1647
|
+
const project = resolution.project;
|
|
1648
|
+
const agentRes = await this.resolveAgentByNameUnique(project.id, validated.agentName);
|
|
1649
|
+
if (!('id' in agentRes))
|
|
1650
|
+
return agentRes;
|
|
1651
|
+
const agentId = agentRes.id;
|
|
1652
|
+
let threadId = validated.threadId;
|
|
1653
|
+
if (threadId) {
|
|
1654
|
+
const thread = await this.chatService.getThread(threadId);
|
|
1655
|
+
const members = thread.members ?? [];
|
|
1656
|
+
if (!members.includes(agentId)) {
|
|
1657
|
+
return {
|
|
1658
|
+
success: false,
|
|
1659
|
+
error: {
|
|
1660
|
+
code: 'AGENT_NOT_IN_THREAD',
|
|
1661
|
+
message: `Agent ${validated.agentName} is not a member of thread ${threadId}`,
|
|
1662
|
+
},
|
|
1663
|
+
};
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
else {
|
|
1667
|
+
const direct = await this.chatService.createDirectThread({ projectId: project.id, agentId });
|
|
1668
|
+
threadId = direct.id;
|
|
1669
|
+
}
|
|
1670
|
+
const result = await this.chatService.startActivity(threadId, agentId, validated.title, {
|
|
1671
|
+
announce: validated.announce,
|
|
1672
|
+
});
|
|
1673
|
+
const response = {
|
|
1674
|
+
activity_id: result.activityId,
|
|
1675
|
+
thread_id: threadId,
|
|
1676
|
+
start_message_id: result.startMessageId,
|
|
1677
|
+
started_at: result.startedAt,
|
|
1678
|
+
auto_finished_prior: result.autoFinishedPrior,
|
|
1679
|
+
};
|
|
1680
|
+
return { success: true, data: response };
|
|
1681
|
+
}
|
|
1682
|
+
async activityFinish(params) {
|
|
1683
|
+
if (!this.chatService) {
|
|
1684
|
+
return {
|
|
1685
|
+
success: false,
|
|
1686
|
+
error: { code: 'SERVICE_UNAVAILABLE', message: 'Chat service unavailable' },
|
|
1687
|
+
};
|
|
1688
|
+
}
|
|
1689
|
+
const validated = mcp_dto_1.ActivityFinishParamsSchema.parse(params);
|
|
1690
|
+
const resolution = await this.resolveProject(validated.path);
|
|
1691
|
+
if (!resolution.project)
|
|
1692
|
+
return resolution.error;
|
|
1693
|
+
const project = resolution.project;
|
|
1694
|
+
const agentRes = await this.resolveAgentByNameUnique(project.id, validated.agentName);
|
|
1695
|
+
if (!('id' in agentRes))
|
|
1696
|
+
return agentRes;
|
|
1697
|
+
const agentId = agentRes.id;
|
|
1698
|
+
let threadId = validated.threadId;
|
|
1699
|
+
if (threadId) {
|
|
1700
|
+
const thread = await this.chatService.getThread(threadId);
|
|
1701
|
+
const members = thread.members ?? [];
|
|
1702
|
+
if (!members.includes(agentId)) {
|
|
1703
|
+
return {
|
|
1704
|
+
success: false,
|
|
1705
|
+
error: {
|
|
1706
|
+
code: 'AGENT_NOT_IN_THREAD',
|
|
1707
|
+
message: `Agent ${validated.agentName} is not a member of thread ${threadId}`,
|
|
1708
|
+
},
|
|
1709
|
+
};
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
else {
|
|
1713
|
+
const direct = await this.chatService.createDirectThread({ projectId: project.id, agentId });
|
|
1714
|
+
threadId = direct.id;
|
|
1715
|
+
}
|
|
1716
|
+
try {
|
|
1717
|
+
const result = await this.chatService.finishActivity(threadId, agentId, {
|
|
1718
|
+
message: validated.message,
|
|
1719
|
+
status: validated.status,
|
|
1720
|
+
});
|
|
1721
|
+
const response = {
|
|
1722
|
+
activity_id: result.activityId,
|
|
1723
|
+
thread_id: threadId,
|
|
1724
|
+
finish_message_id: result.finishMessageId,
|
|
1725
|
+
started_at: result.startedAt,
|
|
1726
|
+
finished_at: result.finishedAt,
|
|
1727
|
+
status: result.status,
|
|
1728
|
+
};
|
|
1729
|
+
return { success: true, data: response };
|
|
1730
|
+
}
|
|
1731
|
+
catch (error) {
|
|
1732
|
+
if (error instanceof error_types_1.ValidationError || error instanceof common_2.BadRequestException) {
|
|
1733
|
+
return {
|
|
1734
|
+
success: false,
|
|
1735
|
+
error: { code: 'NO_RUNNING_ACTIVITY', message: 'No running activity to finish' },
|
|
1736
|
+
};
|
|
1737
|
+
}
|
|
1738
|
+
throw error;
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
mapStatusSummary(status) {
|
|
1742
|
+
return {
|
|
1743
|
+
id: status.id,
|
|
1744
|
+
name: status.label,
|
|
1745
|
+
position: status.position,
|
|
1746
|
+
color: status.color,
|
|
1747
|
+
};
|
|
1748
|
+
}
|
|
1749
|
+
mapEpicSummary(epic, agentNameById) {
|
|
1750
|
+
const summary = {
|
|
1751
|
+
id: epic.id,
|
|
1752
|
+
title: epic.title,
|
|
1753
|
+
description: epic.description ?? null,
|
|
1754
|
+
statusId: epic.statusId,
|
|
1755
|
+
version: epic.version,
|
|
1756
|
+
};
|
|
1757
|
+
if (epic.agentId && agentNameById) {
|
|
1758
|
+
const agentName = agentNameById.get(epic.agentId);
|
|
1759
|
+
if (agentName) {
|
|
1760
|
+
summary.agentName = agentName;
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
if (epic.parentId) {
|
|
1764
|
+
summary.parentId = epic.parentId;
|
|
1765
|
+
}
|
|
1766
|
+
return summary;
|
|
1767
|
+
}
|
|
1768
|
+
mapEpicChild(epic) {
|
|
1769
|
+
return {
|
|
1770
|
+
id: epic.id,
|
|
1771
|
+
title: epic.title,
|
|
1772
|
+
statusId: epic.statusId,
|
|
1773
|
+
};
|
|
1774
|
+
}
|
|
1775
|
+
mapEpicParent(epic) {
|
|
1776
|
+
return {
|
|
1777
|
+
id: epic.id,
|
|
1778
|
+
title: epic.title,
|
|
1779
|
+
description: epic.description ?? null,
|
|
1780
|
+
};
|
|
1781
|
+
}
|
|
1782
|
+
mapEpicComment(comment) {
|
|
1783
|
+
return {
|
|
1784
|
+
id: comment.id,
|
|
1785
|
+
authorName: comment.authorName,
|
|
1786
|
+
content: comment.content,
|
|
1787
|
+
createdAt: comment.createdAt,
|
|
1788
|
+
};
|
|
1789
|
+
}
|
|
1790
|
+
mapDocumentSummary(document) {
|
|
1791
|
+
return {
|
|
1792
|
+
id: document.id,
|
|
1793
|
+
projectId: document.projectId,
|
|
1794
|
+
title: document.title,
|
|
1795
|
+
slug: document.slug,
|
|
1796
|
+
tags: document.tags,
|
|
1797
|
+
archived: document.archived,
|
|
1798
|
+
version: document.version,
|
|
1799
|
+
updatedAt: document.updatedAt,
|
|
1800
|
+
};
|
|
1801
|
+
}
|
|
1802
|
+
mapDocumentDetail(document) {
|
|
1803
|
+
const summary = this.mapDocumentSummary(document);
|
|
1804
|
+
return {
|
|
1805
|
+
...summary,
|
|
1806
|
+
contentMd: document.contentMd,
|
|
1807
|
+
createdAt: document.createdAt,
|
|
1808
|
+
};
|
|
1809
|
+
}
|
|
1810
|
+
mapPromptSummary(prompt) {
|
|
1811
|
+
return {
|
|
1812
|
+
id: prompt.id,
|
|
1813
|
+
projectId: prompt.projectId,
|
|
1814
|
+
title: prompt.title,
|
|
1815
|
+
content: prompt.content,
|
|
1816
|
+
tags: prompt.tags,
|
|
1817
|
+
version: prompt.version,
|
|
1818
|
+
createdAt: prompt.createdAt,
|
|
1819
|
+
updatedAt: prompt.updatedAt,
|
|
1820
|
+
};
|
|
1821
|
+
}
|
|
1822
|
+
extractLinkSlugs(content) {
|
|
1823
|
+
const regex = /\[\[([A-Za-z0-9_\-./]+)\]\]/g;
|
|
1824
|
+
const seen = new Set();
|
|
1825
|
+
const slugs = [];
|
|
1826
|
+
let match;
|
|
1827
|
+
while ((match = regex.exec(content)) !== null) {
|
|
1828
|
+
const slug = match[1].trim();
|
|
1829
|
+
if (slug && !seen.has(slug)) {
|
|
1830
|
+
seen.add(slug);
|
|
1831
|
+
slugs.push(slug);
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
return slugs;
|
|
1835
|
+
}
|
|
1836
|
+
escapeForRegex(value) {
|
|
1837
|
+
return value.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
1838
|
+
}
|
|
1839
|
+
async collectDocumentLinks(document) {
|
|
1840
|
+
const cache = new Map();
|
|
1841
|
+
cache.set(document.slug, document);
|
|
1842
|
+
const slugs = this.extractLinkSlugs(document.contentMd);
|
|
1843
|
+
const projectId = document.projectId ?? null;
|
|
1844
|
+
const links = [];
|
|
1845
|
+
for (const slug of slugs) {
|
|
1846
|
+
const linked = await this.loadDocumentBySlug(projectId, slug, cache);
|
|
1847
|
+
if (linked) {
|
|
1848
|
+
links.push({
|
|
1849
|
+
slug,
|
|
1850
|
+
title: linked.title,
|
|
1851
|
+
id: linked.id,
|
|
1852
|
+
projectId: linked.projectId,
|
|
1853
|
+
exists: true,
|
|
1854
|
+
});
|
|
1855
|
+
}
|
|
1856
|
+
else {
|
|
1857
|
+
links.push({ slug, exists: false });
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
return { links, cache };
|
|
1861
|
+
}
|
|
1862
|
+
async buildInlineResolution(document, cache, maxDepth, maxBytes) {
|
|
1863
|
+
const effectiveDepth = Math.max(0, maxDepth);
|
|
1864
|
+
const path = new Set([document.slug]);
|
|
1865
|
+
const result = await this.inlineDocumentContent(document.contentMd, document.projectId ?? null, 0, { maxDepth: effectiveDepth, maxBytes }, cache, path);
|
|
1866
|
+
const limited = this.applyByteLimit(result.content, maxBytes);
|
|
1867
|
+
return {
|
|
1868
|
+
contentMd: limited.content,
|
|
1869
|
+
depthUsed: Math.min(result.depthUsed, effectiveDepth),
|
|
1870
|
+
bytes: limited.bytes,
|
|
1871
|
+
truncated: limited.truncated || result.truncated,
|
|
1872
|
+
};
|
|
1873
|
+
}
|
|
1874
|
+
async inlineDocumentContent(content, projectId, depth, options, cache, path) {
|
|
1875
|
+
if (options.maxDepth === 0 || depth >= options.maxDepth) {
|
|
1876
|
+
const bytes = Buffer.byteLength(content, 'utf8');
|
|
1877
|
+
return { content, depthUsed: depth, bytes, truncated: false };
|
|
1878
|
+
}
|
|
1879
|
+
let workingContent = content;
|
|
1880
|
+
let depthUsed = depth;
|
|
1881
|
+
const slugs = this.extractLinkSlugs(content);
|
|
1882
|
+
for (const slug of slugs) {
|
|
1883
|
+
if (depth >= options.maxDepth) {
|
|
1884
|
+
break;
|
|
1885
|
+
}
|
|
1886
|
+
if (path.has(slug)) {
|
|
1887
|
+
continue;
|
|
1888
|
+
}
|
|
1889
|
+
const linked = await this.loadDocumentBySlug(projectId, slug, cache);
|
|
1890
|
+
if (!linked) {
|
|
1891
|
+
continue;
|
|
1892
|
+
}
|
|
1893
|
+
path.add(slug);
|
|
1894
|
+
const childResult = await this.inlineDocumentContent(linked.contentMd, linked.projectId ?? projectId, depth + 1, options, cache, path);
|
|
1895
|
+
path.delete(slug);
|
|
1896
|
+
depthUsed = Math.max(depthUsed, childResult.depthUsed);
|
|
1897
|
+
const snippet = this.buildInlineSnippet(linked, childResult.content, depth + 1);
|
|
1898
|
+
const pattern = new RegExp(`\\[\\[${this.escapeForRegex(slug)}\\]\\]`, 'g');
|
|
1899
|
+
workingContent = workingContent.replace(pattern, snippet);
|
|
1900
|
+
}
|
|
1901
|
+
const bytes = Buffer.byteLength(workingContent, 'utf8');
|
|
1902
|
+
return {
|
|
1903
|
+
content: workingContent,
|
|
1904
|
+
depthUsed: Math.max(depthUsed, depth),
|
|
1905
|
+
bytes,
|
|
1906
|
+
truncated: false,
|
|
1907
|
+
};
|
|
1908
|
+
}
|
|
1909
|
+
buildInlineSnippet(document, content, depth) {
|
|
1910
|
+
const headingLevel = Math.min(6, 2 + depth);
|
|
1911
|
+
const heading = `${'#'.repeat(headingLevel)} ${document.title || document.slug}`;
|
|
1912
|
+
return `\n\n---\n${heading}\n\n${content}\n---\n\n`;
|
|
1913
|
+
}
|
|
1914
|
+
async loadDocumentBySlug(projectId, slug, cache) {
|
|
1915
|
+
if (cache.has(slug)) {
|
|
1916
|
+
return cache.get(slug) ?? null;
|
|
1917
|
+
}
|
|
1918
|
+
try {
|
|
1919
|
+
const linked = await this.storage.getDocument({ projectId, slug });
|
|
1920
|
+
cache.set(slug, linked);
|
|
1921
|
+
return linked;
|
|
1922
|
+
}
|
|
1923
|
+
catch (error) {
|
|
1924
|
+
cache.set(slug, null);
|
|
1925
|
+
return null;
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
applyByteLimit(content, maxBytes) {
|
|
1929
|
+
const bytes = Buffer.byteLength(content, 'utf8');
|
|
1930
|
+
if (bytes <= maxBytes) {
|
|
1931
|
+
return { content, bytes, truncated: false };
|
|
1932
|
+
}
|
|
1933
|
+
const buffer = Buffer.from(content, 'utf8');
|
|
1934
|
+
const truncatedBuffer = buffer.subarray(0, maxBytes);
|
|
1935
|
+
return {
|
|
1936
|
+
content: truncatedBuffer.toString('utf8'),
|
|
1937
|
+
bytes: maxBytes,
|
|
1938
|
+
truncated: true,
|
|
1939
|
+
};
|
|
1940
|
+
}
|
|
1941
|
+
async resolveDocumentResource(uri) {
|
|
1942
|
+
const spec = uri.slice('doc://'.length);
|
|
1943
|
+
const slashIndex = spec.indexOf('/');
|
|
1944
|
+
if (slashIndex === -1) {
|
|
1945
|
+
throw new Error(`Invalid document resource URI: ${uri}`);
|
|
1946
|
+
}
|
|
1947
|
+
const projectPart = spec.slice(0, slashIndex);
|
|
1948
|
+
const slugPart = spec.slice(slashIndex + 1);
|
|
1949
|
+
const projectSlug = decodeURIComponent(projectPart);
|
|
1950
|
+
const documentSlug = decodeURIComponent(slugPart);
|
|
1951
|
+
const projectIdCandidate = await this.findProjectIdBySlug(projectSlug);
|
|
1952
|
+
if (projectIdCandidate === undefined) {
|
|
1953
|
+
return {
|
|
1954
|
+
success: false,
|
|
1955
|
+
error: {
|
|
1956
|
+
code: 'PROJECT_NOT_FOUND',
|
|
1957
|
+
message: `Unknown project slug: ${projectSlug}`,
|
|
1958
|
+
},
|
|
1959
|
+
};
|
|
1960
|
+
}
|
|
1961
|
+
try {
|
|
1962
|
+
const document = await this.storage.getDocument({
|
|
1963
|
+
projectId: projectIdCandidate ?? null,
|
|
1964
|
+
slug: documentSlug,
|
|
1965
|
+
});
|
|
1966
|
+
return {
|
|
1967
|
+
success: true,
|
|
1968
|
+
data: {
|
|
1969
|
+
uri,
|
|
1970
|
+
mimeType: 'text/markdown',
|
|
1971
|
+
content: document.contentMd,
|
|
1972
|
+
document: this.mapDocumentDetail(document),
|
|
1973
|
+
},
|
|
1974
|
+
};
|
|
1975
|
+
}
|
|
1976
|
+
catch (error) {
|
|
1977
|
+
return {
|
|
1978
|
+
success: false,
|
|
1979
|
+
error: {
|
|
1980
|
+
code: 'DOCUMENT_NOT_FOUND',
|
|
1981
|
+
message: `Document not found: ${documentSlug}`,
|
|
1982
|
+
},
|
|
1983
|
+
};
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
async resolvePromptResource(uri) {
|
|
1987
|
+
const spec = uri.slice('prompt://'.length);
|
|
1988
|
+
if (!spec) {
|
|
1989
|
+
throw new Error(`Invalid prompt resource URI: ${uri}`);
|
|
1990
|
+
}
|
|
1991
|
+
const atIndex = spec.lastIndexOf('@');
|
|
1992
|
+
const namePart = atIndex === -1 ? spec : spec.slice(0, atIndex);
|
|
1993
|
+
const versionPart = atIndex === -1 ? undefined : spec.slice(atIndex + 1);
|
|
1994
|
+
const name = decodeURIComponent(namePart).trim();
|
|
1995
|
+
if (!name) {
|
|
1996
|
+
throw new Error(`Prompt name missing in URI: ${uri}`);
|
|
1997
|
+
}
|
|
1998
|
+
let version;
|
|
1999
|
+
if (versionPart && versionPart.length > 0) {
|
|
2000
|
+
version = Number(versionPart);
|
|
2001
|
+
if (!Number.isFinite(version) || version <= 0) {
|
|
2002
|
+
throw new Error(`Invalid prompt version in URI: ${uri}`);
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
const list = await this.storage.listPrompts(null, undefined);
|
|
2006
|
+
const candidates = list.items.filter((prompt) => prompt.title === name && (version === undefined || prompt.version === version));
|
|
2007
|
+
const selected = candidates.find((prompt) => prompt.projectId === null) ?? candidates[0];
|
|
2008
|
+
if (!selected) {
|
|
2009
|
+
return {
|
|
2010
|
+
success: false,
|
|
2011
|
+
error: {
|
|
2012
|
+
code: 'PROMPT_NOT_FOUND',
|
|
2013
|
+
message: `Prompt not found: ${name}${version ? `@${version}` : ''}`,
|
|
2014
|
+
},
|
|
2015
|
+
};
|
|
2016
|
+
}
|
|
2017
|
+
const prompt = await this.storage.getPrompt(selected.id);
|
|
2018
|
+
return {
|
|
2019
|
+
success: true,
|
|
2020
|
+
data: {
|
|
2021
|
+
uri,
|
|
2022
|
+
mimeType: 'text/markdown',
|
|
2023
|
+
content: prompt.content,
|
|
2024
|
+
prompt: this.mapPromptSummary(prompt),
|
|
2025
|
+
},
|
|
2026
|
+
};
|
|
2027
|
+
}
|
|
2028
|
+
async findProjectIdBySlug(projectSlug) {
|
|
2029
|
+
if (!projectSlug || projectSlug === 'global') {
|
|
2030
|
+
return null;
|
|
2031
|
+
}
|
|
2032
|
+
const projects = await this.storage.listProjects({ limit: 1000, offset: 0 });
|
|
2033
|
+
const match = projects.items.find((project) => this.slugifyProjectName(project.name) === projectSlug);
|
|
2034
|
+
return match?.id;
|
|
2035
|
+
}
|
|
2036
|
+
slugifyProjectName(name) {
|
|
2037
|
+
return name
|
|
2038
|
+
.toLowerCase()
|
|
2039
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
2040
|
+
.replace(/(^-|-$)/g, '');
|
|
2041
|
+
}
|
|
2042
|
+
};
|
|
2043
|
+
exports.McpService = McpService;
|
|
2044
|
+
exports.McpService = McpService = __decorate([
|
|
2045
|
+
(0, common_1.Injectable)(),
|
|
2046
|
+
__param(0, (0, common_1.Inject)(storage_interface_1.STORAGE_SERVICE)),
|
|
2047
|
+
__param(1, (0, common_1.Inject)((0, common_1.forwardRef)(() => chat_service_1.ChatService))),
|
|
2048
|
+
__param(2, (0, common_1.Inject)((0, common_1.forwardRef)(() => sessions_service_1.SessionsService))),
|
|
2049
|
+
__param(3, (0, common_1.Inject)((0, common_1.forwardRef)(() => terminal_gateway_1.TerminalGateway))),
|
|
2050
|
+
__param(4, (0, common_1.Inject)((0, common_1.forwardRef)(() => epics_service_1.EpicsService))),
|
|
2051
|
+
__metadata("design:paramtypes", [Object, chat_service_1.ChatService,
|
|
2052
|
+
sessions_service_1.SessionsService,
|
|
2053
|
+
terminal_gateway_1.TerminalGateway,
|
|
2054
|
+
epics_service_1.EpicsService])
|
|
2055
|
+
], McpService);
|
|
2056
|
+
//# sourceMappingURL=mcp.service.js.map
|