@bevel-software/platform-core-backend 0.25.2 → 0.27.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/agent-guide/access-control.md +251 -0
- package/agent-guide/conventions.md +35 -0
- package/agent-guide/directory-structure.md +145 -0
- package/agent-guide/finding-things.md +7 -0
- package/agent-guide/html-views.md +83 -0
- package/agent-guide/introduction.md +27 -0
- package/agent-guide/new-knowledge-base.md +24 -0
- package/agent-guide/skills.md +47 -0
- package/agent-guide/tool-manuals.md +227 -0
- package/agent-guide/where-a-new-file-goes.md +36 -0
- package/dist/assets.d.ts +26 -0
- package/dist/assets.d.ts.map +1 -1
- package/dist/assets.js +35 -2
- package/dist/assets.js.map +1 -1
- package/dist/core/catalog-revision.d.ts +19 -1
- package/dist/core/catalog-revision.d.ts.map +1 -1
- package/dist/core/catalog-revision.js +14 -3
- package/dist/core/catalog-revision.js.map +1 -1
- package/dist/core/core-ports.d.ts +11 -0
- package/dist/core/core-ports.d.ts.map +1 -1
- package/dist/core/core-ports.js.map +1 -1
- package/dist/core/create-core-server.d.ts +5 -0
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +65 -3
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +35 -0
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +114 -12
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/core/lifecycle.d.ts +16 -1
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +18 -1
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/core-config.d.ts +21 -0
- package/dist/core-config.d.ts.map +1 -1
- package/dist/core-config.js +22 -1
- package/dist/core-config.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/modules/access/access-control.interface.d.ts +17 -0
- package/dist/modules/access/access-control.interface.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +2 -0
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +27 -0
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/access/directory-sync-bot.js +1 -1
- package/dist/modules/access/directory-sync-bot.js.map +1 -1
- package/dist/modules/agent-guide/agent-guide.d.ts +145 -0
- package/dist/modules/agent-guide/agent-guide.d.ts.map +1 -0
- package/dist/modules/agent-guide/agent-guide.js +199 -0
- package/dist/modules/agent-guide/agent-guide.js.map +1 -0
- package/dist/modules/agent-guide/agent-guide.tools.d.ts +24 -0
- package/dist/modules/agent-guide/agent-guide.tools.d.ts.map +1 -0
- package/dist/modules/agent-guide/agent-guide.tools.js +100 -0
- package/dist/modules/agent-guide/agent-guide.tools.js.map +1 -0
- package/dist/modules/agent-guide/index.d.ts +4 -0
- package/dist/modules/agent-guide/index.d.ts.map +1 -0
- package/dist/modules/agent-guide/index.js +4 -0
- package/dist/modules/agent-guide/index.js.map +1 -0
- package/dist/modules/agent-instructions/agent-instructions.routes.d.ts +3 -2
- package/dist/modules/agent-instructions/agent-instructions.routes.d.ts.map +1 -1
- package/dist/modules/agent-instructions/agent-instructions.routes.js +3 -2
- package/dist/modules/agent-instructions/agent-instructions.routes.js.map +1 -1
- package/dist/modules/agent-instructions/compose.d.ts +14 -6
- package/dist/modules/agent-instructions/compose.d.ts.map +1 -1
- package/dist/modules/agent-instructions/compose.js +28 -8
- package/dist/modules/agent-instructions/compose.js.map +1 -1
- package/dist/modules/agent-instructions/index.d.ts +1 -1
- package/dist/modules/agent-instructions/index.d.ts.map +1 -1
- package/dist/modules/agent-instructions/index.js +1 -1
- package/dist/modules/agent-instructions/index.js.map +1 -1
- package/dist/modules/agent-instructions/shared-file-rules.d.ts +10 -50
- package/dist/modules/agent-instructions/shared-file-rules.d.ts.map +1 -1
- package/dist/modules/agent-instructions/shared-file-rules.js +51 -104
- package/dist/modules/agent-instructions/shared-file-rules.js.map +1 -1
- package/dist/modules/audit/agent-audit.service.d.ts +21 -2
- package/dist/modules/audit/agent-audit.service.d.ts.map +1 -1
- package/dist/modules/audit/agent-audit.service.js +43 -2
- package/dist/modules/audit/agent-audit.service.js.map +1 -1
- package/dist/modules/audit/agent-connection.routes.d.ts +34 -0
- package/dist/modules/audit/agent-connection.routes.d.ts.map +1 -0
- package/dist/modules/audit/agent-connection.routes.js +38 -0
- package/dist/modules/audit/agent-connection.routes.js.map +1 -0
- package/dist/modules/audit/audit.contract.d.ts +24 -0
- package/dist/modules/audit/audit.contract.d.ts.map +1 -1
- package/dist/modules/audit/audit.contract.js.map +1 -1
- package/dist/modules/audit/index.d.ts +1 -0
- package/dist/modules/audit/index.d.ts.map +1 -1
- package/dist/modules/audit/index.js +1 -0
- package/dist/modules/audit/index.js.map +1 -1
- package/dist/modules/auth/account.routes.d.ts.map +1 -1
- package/dist/modules/auth/account.routes.js +10 -3
- package/dist/modules/auth/account.routes.js.map +1 -1
- package/dist/modules/auth/auth.middleware.d.ts +7 -0
- package/dist/modules/auth/auth.middleware.d.ts.map +1 -1
- package/dist/modules/auth/auth.middleware.js +1 -1
- package/dist/modules/auth/auth.middleware.js.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.d.ts.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.js +11 -3
- package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
- package/dist/modules/database/core-schema.d.ts +96 -0
- package/dist/modules/database/core-schema.d.ts.map +1 -1
- package/dist/modules/database/core-schema.js +32 -3
- package/dist/modules/database/core-schema.js.map +1 -1
- package/dist/modules/embed/account-link.service.d.ts +42 -0
- package/dist/modules/embed/account-link.service.d.ts.map +1 -0
- package/dist/modules/embed/account-link.service.js +50 -0
- package/dist/modules/embed/account-link.service.js.map +1 -0
- package/dist/modules/embed/embed-link.d.ts +58 -0
- package/dist/modules/embed/embed-link.d.ts.map +1 -0
- package/dist/modules/embed/embed-link.js +160 -0
- package/dist/modules/embed/embed-link.js.map +1 -0
- package/dist/modules/embed/embed.errors.d.ts +30 -0
- package/dist/modules/embed/embed.errors.d.ts.map +1 -0
- package/dist/modules/embed/embed.errors.js +42 -0
- package/dist/modules/embed/embed.errors.js.map +1 -0
- package/dist/modules/embed/embed.interface.d.ts +204 -0
- package/dist/modules/embed/embed.interface.d.ts.map +1 -0
- package/dist/modules/embed/embed.interface.js +2 -0
- package/dist/modules/embed/embed.interface.js.map +1 -0
- package/dist/modules/embed/embed.routes.d.ts +59 -0
- package/dist/modules/embed/embed.routes.d.ts.map +1 -0
- package/dist/modules/embed/embed.routes.js +343 -0
- package/dist/modules/embed/embed.routes.js.map +1 -0
- package/dist/modules/embed/embed.service.d.ts +169 -0
- package/dist/modules/embed/embed.service.d.ts.map +1 -0
- package/dist/modules/embed/embed.service.js +627 -0
- package/dist/modules/embed/embed.service.js.map +1 -0
- package/dist/modules/embed/embed.tools.d.ts +51 -0
- package/dist/modules/embed/embed.tools.d.ts.map +1 -0
- package/dist/modules/embed/embed.tools.js +170 -0
- package/dist/modules/embed/embed.tools.js.map +1 -0
- package/dist/modules/embed/mcp-app.d.ts +64 -0
- package/dist/modules/embed/mcp-app.d.ts.map +1 -0
- package/dist/modules/embed/mcp-app.js +147 -0
- package/dist/modules/embed/mcp-app.js.map +1 -0
- package/dist/modules/embed/mcp-app.routes.d.ts +21 -0
- package/dist/modules/embed/mcp-app.routes.d.ts.map +1 -0
- package/dist/modules/embed/mcp-app.routes.js +36 -0
- package/dist/modules/embed/mcp-app.routes.js.map +1 -0
- package/dist/modules/github-app/github-app.connection.d.ts +15 -0
- package/dist/modules/github-app/github-app.connection.d.ts.map +1 -1
- package/dist/modules/github-app/github-app.connection.js +34 -3
- package/dist/modules/github-app/github-app.connection.js.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.d.ts +65 -0
- package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.js +300 -0
- package/dist/modules/kb-fs/locking-filesystem.js.map +1 -1
- package/dist/modules/kb-sync/kb-sync.interface.d.ts +5 -0
- package/dist/modules/kb-sync/kb-sync.interface.d.ts.map +1 -1
- package/dist/modules/kb-sync/kb-sync.service.d.ts.map +1 -1
- package/dist/modules/kb-sync/kb-sync.service.js +6 -0
- package/dist/modules/kb-sync/kb-sync.service.js.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.js +2 -1
- package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
- package/dist/modules/mcp/mcp.service.d.ts +57 -3
- package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.service.js +177 -24
- package/dist/modules/mcp/mcp.service.js.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts +24 -0
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js +32 -4
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
- package/dist/modules/mcp/tool-schema-guard.d.ts +105 -0
- package/dist/modules/mcp/tool-schema-guard.d.ts.map +1 -0
- package/dist/modules/mcp/tool-schema-guard.js +171 -0
- package/dist/modules/mcp/tool-schema-guard.js.map +1 -0
- package/dist/modules/onboarding/onboarding.contract.d.ts +80 -0
- package/dist/modules/onboarding/onboarding.contract.d.ts.map +1 -0
- package/dist/modules/onboarding/onboarding.contract.js +7 -0
- package/dist/modules/onboarding/onboarding.contract.js.map +1 -0
- package/dist/modules/onboarding/onboarding.routes.d.ts +18 -0
- package/dist/modules/onboarding/onboarding.routes.d.ts.map +1 -0
- package/dist/modules/onboarding/onboarding.routes.js +81 -0
- package/dist/modules/onboarding/onboarding.routes.js.map +1 -0
- package/dist/modules/onboarding/starter-pack.service.d.ts +213 -0
- package/dist/modules/onboarding/starter-pack.service.d.ts.map +1 -0
- package/dist/modules/onboarding/starter-pack.service.js +492 -0
- package/dist/modules/onboarding/starter-pack.service.js.map +1 -0
- package/dist/modules/onboarding/starter-packs.d.ts +78 -0
- package/dist/modules/onboarding/starter-packs.d.ts.map +1 -0
- package/dist/modules/onboarding/starter-packs.js +156 -0
- package/dist/modules/onboarding/starter-packs.js.map +1 -0
- package/dist/modules/plugins/index.d.ts +1 -1
- package/dist/modules/plugins/index.d.ts.map +1 -1
- package/dist/modules/plugins/index.js +1 -1
- package/dist/modules/plugins/index.js.map +1 -1
- package/dist/modules/plugins/plugin-provision.service.d.ts +19 -0
- package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugin-provision.service.js +18 -1
- package/dist/modules/plugins/plugin-provision.service.js.map +1 -1
- package/dist/modules/plugins/plugins.routes.d.ts +0 -36
- package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.routes.js +31 -0
- package/dist/modules/plugins/plugins.routes.js.map +1 -1
- package/dist/modules/plugins/plugins.tools.d.ts +36 -2
- package/dist/modules/plugins/plugins.tools.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.tools.js +71 -14
- package/dist/modules/plugins/plugins.tools.js.map +1 -1
- package/dist/modules/settings/deployment-settings.service.d.ts +34 -7
- package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
- package/dist/modules/settings/deployment-settings.service.js +148 -54
- package/dist/modules/settings/deployment-settings.service.js.map +1 -1
- package/dist/modules/settings/repository-source.d.ts +1 -0
- package/dist/modules/settings/repository-source.d.ts.map +1 -1
- package/dist/modules/settings/repository-source.js +3 -3
- package/dist/modules/settings/repository-source.js.map +1 -1
- package/dist/modules/settings/setup.routes.d.ts.map +1 -1
- package/dist/modules/settings/setup.routes.js +3 -6
- package/dist/modules/settings/setup.routes.js.map +1 -1
- package/dist/modules/skills/skills.tools.d.ts.map +1 -1
- package/dist/modules/skills/skills.tools.js +103 -43
- package/dist/modules/skills/skills.tools.js.map +1 -1
- package/dist/modules/tool-auth/connection-key-rejection.d.ts +10 -1
- package/dist/modules/tool-auth/connection-key-rejection.d.ts.map +1 -1
- package/dist/modules/tool-auth/connection-key-rejection.js +11 -3
- package/dist/modules/tool-auth/connection-key-rejection.js.map +1 -1
- package/dist/modules/tool-auth/external-api-key.interface.d.ts +8 -0
- package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.d.ts +24 -2
- package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.js +59 -2
- package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
- package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
- package/dist/modules/tool-auth/tool-auth.middleware.js +9 -4
- package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
- package/dist/modules/tool-helpers/index.d.ts +28 -1
- package/dist/modules/tool-helpers/index.d.ts.map +1 -1
- package/dist/modules/tool-helpers/index.js +28 -1
- package/dist/modules/tool-helpers/index.js.map +1 -1
- package/dist/modules/tool-helpers/route-argument-check.d.ts +10 -0
- package/dist/modules/tool-helpers/route-argument-check.d.ts.map +1 -0
- package/dist/modules/tool-helpers/route-argument-check.js +132 -0
- package/dist/modules/tool-helpers/route-argument-check.js.map +1 -0
- package/dist/modules/tool-helpers/route-tool-schemas.d.ts +89 -0
- package/dist/modules/tool-helpers/route-tool-schemas.d.ts.map +1 -0
- package/dist/modules/tool-helpers/route-tool-schemas.js +113 -0
- package/dist/modules/tool-helpers/route-tool-schemas.js.map +1 -0
- package/dist/modules/tool-helpers/tool-def.d.ts +71 -0
- package/dist/modules/tool-helpers/tool-def.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-def.js +70 -6
- package/dist/modules/tool-helpers/tool-def.js.map +1 -1
- package/dist/modules/tool-helpers/tool-handler.d.ts +46 -6
- package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-handler.js +157 -8
- package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
- package/dist/modules/tool-helpers/tool.contract.d.ts +9 -0
- package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool.contract.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +23 -4
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.contract.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts +4 -0
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.js +14 -0
- package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.d.ts +7 -0
- package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.js +77 -38
- package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
- package/dist/modules/tool-registry/description-length.d.ts +20 -16
- package/dist/modules/tool-registry/description-length.d.ts.map +1 -1
- package/dist/modules/tool-registry/description-length.js +50 -30
- package/dist/modules/tool-registry/description-length.js.map +1 -1
- package/dist/modules/tool-registry/guide-first.d.ts +27 -0
- package/dist/modules/tool-registry/guide-first.d.ts.map +1 -0
- package/dist/modules/tool-registry/guide-first.js +40 -0
- package/dist/modules/tool-registry/guide-first.js.map +1 -0
- package/dist/modules/tool-registry/tool-registry.d.ts +6 -0
- package/dist/modules/tool-registry/tool-registry.d.ts.map +1 -1
- package/dist/modules/tool-registry/tool-registry.js +9 -2
- package/dist/modules/tool-registry/tool-registry.js.map +1 -1
- package/dist/modules/workflow/agent-tools/change-request-read-shape.d.ts +449 -0
- package/dist/modules/workflow/agent-tools/change-request-read-shape.d.ts.map +1 -0
- package/dist/modules/workflow/agent-tools/change-request-read-shape.js +481 -0
- package/dist/modules/workflow/agent-tools/change-request-read-shape.js.map +1 -0
- package/dist/modules/workflow/agent-tools/change-request-read.tools.d.ts +126 -0
- package/dist/modules/workflow/agent-tools/change-request-read.tools.d.ts.map +1 -0
- package/dist/modules/workflow/agent-tools/change-request-read.tools.js +611 -0
- package/dist/modules/workflow/agent-tools/change-request-read.tools.js.map +1 -0
- package/dist/modules/workflow/agent-tools/change-request-summary.d.ts +12 -1
- package/dist/modules/workflow/agent-tools/change-request-summary.d.ts.map +1 -1
- package/dist/modules/workflow/agent-tools/change-request-summary.js +5 -1
- package/dist/modules/workflow/agent-tools/change-request-summary.js.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts +8 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.js +133 -37
- package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
- package/dist/modules/workflow/file-lock.service.d.ts +29 -5
- package/dist/modules/workflow/file-lock.service.d.ts.map +1 -1
- package/dist/modules/workflow/file-lock.service.js +62 -5
- package/dist/modules/workflow/file-lock.service.js.map +1 -1
- package/dist/modules/workflow/git/git.service.d.ts +282 -14
- package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/git.service.js +627 -126
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workflow/git/merge-commit.d.ts +73 -0
- package/dist/modules/workflow/git/merge-commit.d.ts.map +1 -0
- package/dist/modules/workflow/git/merge-commit.js +89 -0
- package/dist/modules/workflow/git/merge-commit.js.map +1 -0
- package/dist/modules/workflow/git/node-git-runner.d.ts +15 -0
- package/dist/modules/workflow/git/node-git-runner.d.ts.map +1 -1
- package/dist/modules/workflow/git/node-git-runner.js +78 -6
- package/dist/modules/workflow/git/node-git-runner.js.map +1 -1
- package/dist/modules/workflow/git/pull-request.service.d.ts +94 -1
- package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/pull-request.service.js +332 -37
- package/dist/modules/workflow/git/pull-request.service.js.map +1 -1
- package/dist/modules/workflow/pending-commits.service.d.ts +15 -0
- package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -1
- package/dist/modules/workflow/pending-commits.service.js +25 -1
- package/dist/modules/workflow/pending-commits.service.js.map +1 -1
- package/dist/modules/workflow/pending-commits.worker.d.ts +6 -1
- package/dist/modules/workflow/pending-commits.worker.d.ts.map +1 -1
- package/dist/modules/workflow/pending-commits.worker.js +23 -6
- package/dist/modules/workflow/pending-commits.worker.js.map +1 -1
- package/dist/modules/workflow/recovery-bot.js +1 -1
- package/dist/modules/workflow/recovery-bot.js.map +1 -1
- package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts +35 -0
- package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/review-workflow/review-workflow.service.js +180 -13
- package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
- package/dist/modules/workflow/sanitize-error.d.ts +16 -0
- package/dist/modules/workflow/sanitize-error.d.ts.map +1 -1
- package/dist/modules/workflow/sanitize-error.js +45 -0
- package/dist/modules/workflow/sanitize-error.js.map +1 -1
- package/dist/modules/workflow/workflow.routes.d.ts +6 -2
- package/dist/modules/workflow/workflow.routes.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.routes.js +7 -2
- package/dist/modules/workflow/workflow.routes.js.map +1 -1
- package/dist/modules/workflow/workflow.service.d.ts +187 -15
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.service.js +808 -106
- package/dist/modules/workflow/workflow.service.js.map +1 -1
- package/dist/modules/workspace/agent-download.builder.d.ts +105 -0
- package/dist/modules/workspace/agent-download.builder.d.ts.map +1 -0
- package/dist/modules/workspace/agent-download.builder.js +238 -0
- package/dist/modules/workspace/agent-download.builder.js.map +1 -0
- package/dist/modules/workspace/agent-download.routes.d.ts +77 -0
- package/dist/modules/workspace/agent-download.routes.d.ts.map +1 -0
- package/dist/modules/workspace/agent-download.routes.js +156 -0
- package/dist/modules/workspace/agent-download.routes.js.map +1 -0
- package/dist/modules/workspace/agent-download.store.d.ts +225 -0
- package/dist/modules/workspace/agent-download.store.d.ts.map +1 -0
- package/dist/modules/workspace/agent-download.store.js +423 -0
- package/dist/modules/workspace/agent-download.store.js.map +1 -0
- package/dist/modules/workspace/agent-upload.store.d.ts +10 -1
- package/dist/modules/workspace/agent-upload.store.d.ts.map +1 -1
- package/dist/modules/workspace/agent-upload.store.js +16 -6
- package/dist/modules/workspace/agent-upload.store.js.map +1 -1
- package/dist/modules/workspace/file-readers/raw-mime.d.ts +6 -0
- package/dist/modules/workspace/file-readers/raw-mime.d.ts.map +1 -0
- package/dist/modules/workspace/file-readers/raw-mime.js +38 -0
- package/dist/modules/workspace/file-readers/raw-mime.js.map +1 -0
- package/dist/modules/workspace/file-readers/text-reader.d.ts +5 -5
- package/dist/modules/workspace/file-readers/text-reader.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/text-reader.js +5 -8
- package/dist/modules/workspace/file-readers/text-reader.js.map +1 -1
- package/dist/modules/workspace/first-run.d.ts +111 -0
- package/dist/modules/workspace/first-run.d.ts.map +1 -0
- package/dist/modules/workspace/first-run.js +181 -0
- package/dist/modules/workspace/first-run.js.map +1 -0
- package/dist/modules/workspace/move-links.d.ts +92 -0
- package/dist/modules/workspace/move-links.d.ts.map +1 -0
- package/dist/modules/workspace/move-links.js +173 -0
- package/dist/modules/workspace/move-links.js.map +1 -0
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.js +24 -39
- package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
- package/dist/modules/workspace/startup/steps/template-files.step.d.ts +58 -52
- package/dist/modules/workspace/startup/steps/template-files.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/template-files.step.js +209 -223
- package/dist/modules/workspace/startup/steps/template-files.step.js.map +1 -1
- package/dist/modules/workspace/startup/steps/template-source.d.ts +5 -3
- package/dist/modules/workspace/startup/steps/template-source.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/template-source.js +5 -3
- package/dist/modules/workspace/startup/steps/template-source.js.map +1 -1
- package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.routes.js +63 -22
- package/dist/modules/workspace/workspace.routes.js.map +1 -1
- package/dist/modules/workspace/workspace.service.d.ts +75 -2
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.service.js +197 -24
- package/dist/modules/workspace/workspace.service.js.map +1 -1
- package/dist/modules/workspace/workspace.tools.d.ts +68 -1
- package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.tools.js +642 -71
- package/dist/modules/workspace/workspace.tools.js.map +1 -1
- package/dist/modules/write-access/write-access.d.ts.map +1 -1
- package/dist/modules/write-access/write-access.js +9 -0
- package/dist/modules/write-access/write-access.js.map +1 -1
- package/dist/shared/domain-errors.d.ts +84 -1
- package/dist/shared/domain-errors.d.ts.map +1 -1
- package/dist/shared/domain-errors.js +100 -12
- package/dist/shared/domain-errors.js.map +1 -1
- package/dist/shared/git.contract.d.ts +10 -1
- package/dist/shared/git.contract.d.ts.map +1 -1
- package/dist/shared/git.contract.js.map +1 -1
- package/dist/shared/hidden-tools.d.ts +44 -0
- package/dist/shared/hidden-tools.d.ts.map +1 -0
- package/dist/shared/hidden-tools.js +13 -0
- package/dist/shared/hidden-tools.js.map +1 -0
- package/dist/shared/utf8-text.d.ts +15 -0
- package/dist/shared/utf8-text.d.ts.map +1 -0
- package/dist/shared/utf8-text.js +20 -0
- package/dist/shared/utf8-text.js.map +1 -0
- package/dist/tenancy/static-tenant-source.d.ts +2 -1
- package/dist/tenancy/static-tenant-source.d.ts.map +1 -1
- package/dist/tenancy/static-tenant-source.js +9 -2
- package/dist/tenancy/static-tenant-source.js.map +1 -1
- package/kb-template/.bevelignore +0 -5
- package/mcp-app/page.html +349 -0
- package/migrations/0017_atlassian_account_links.sql +37 -0
- package/migrations/meta/0017_snapshot.json +2396 -0
- package/migrations/meta/_journal.json +8 -1
- package/package.json +6 -3
- package/src/__tests__/kb-layout-config.test.ts +10 -100
- package/src/__tests__/packaged-assets-ship.test.ts +66 -0
- package/src/assets.ts +38 -2
- package/src/core/__tests__/catalog-revision.test.ts +40 -0
- package/src/core/__tests__/gated-boot.test.ts +1 -0
- package/src/core/__tests__/lifecycle.test.ts +14 -0
- package/src/core/catalog-revision.ts +15 -2
- package/src/core/core-ports.ts +11 -0
- package/src/core/create-core-server.ts +78 -3
- package/src/core/create-core-services.ts +168 -10
- package/src/core/lifecycle.ts +28 -2
- package/src/core-config.ts +24 -1
- package/src/index.ts +2 -2
- package/src/modules/access/__tests__/access-control.atref-batch.test.ts +58 -0
- package/src/modules/access/__tests__/access-control.platform-restore.test.ts +8 -7
- package/src/modules/access/__tests__/access-control.service.test.ts +19 -0
- package/src/modules/access/__tests__/access-personal-plugin.test.ts +1 -18
- package/src/modules/access/access-control.interface.ts +28 -0
- package/src/modules/access/access-control.service.ts +37 -0
- package/src/modules/access/directory-sync-bot.ts +1 -1
- package/src/modules/agent-guide/__tests__/agent-guide.test.ts +386 -0
- package/src/modules/agent-guide/__tests__/agent-guide.tools.test.ts +207 -0
- package/src/modules/agent-guide/agent-guide.tools.ts +122 -0
- package/src/modules/agent-guide/agent-guide.ts +300 -0
- package/src/modules/agent-guide/index.ts +22 -0
- package/src/modules/agent-instructions/__tests__/shared-file-rules.test.ts +39 -118
- package/src/modules/agent-instructions/agent-instructions.routes.ts +3 -2
- package/src/modules/agent-instructions/compose.ts +26 -8
- package/src/modules/agent-instructions/index.ts +0 -3
- package/src/modules/agent-instructions/shared-file-rules.ts +50 -112
- package/src/modules/audit/__tests__/agent-audit.service.test.ts +64 -4
- package/src/modules/audit/__tests__/agent-connection.routes.test.ts +102 -0
- package/src/modules/audit/agent-audit.service.ts +58 -3
- package/src/modules/audit/agent-connection.routes.ts +56 -0
- package/src/modules/audit/audit.contract.ts +26 -0
- package/src/modules/audit/index.ts +1 -0
- package/src/modules/auth/__tests__/account.routes.test.ts +5 -3
- package/src/modules/auth/account.routes.ts +10 -3
- package/src/modules/auth/auth.middleware.ts +1 -1
- package/src/modules/code-mode/__tests__/code-mode.tool.test.ts +18 -2
- package/src/modules/code-mode/code-mode.tool.ts +13 -2
- package/src/modules/database/core-schema.ts +33 -3
- package/src/modules/embed/__tests__/account-links.migration.test.ts +98 -0
- package/src/modules/embed/__tests__/embed-link.test.ts +122 -0
- package/src/modules/embed/__tests__/embed.routes.test.ts +362 -0
- package/src/modules/embed/__tests__/embed.service.lock.test.ts +193 -0
- package/src/modules/embed/__tests__/embed.service.test.ts +805 -0
- package/src/modules/embed/__tests__/embed.tools.test.ts +304 -0
- package/src/modules/embed/__tests__/mcp-app.test.ts +207 -0
- package/src/modules/embed/account-link.service.ts +76 -0
- package/src/modules/embed/embed-link.ts +180 -0
- package/src/modules/embed/embed.errors.ts +43 -0
- package/src/modules/embed/embed.interface.ts +203 -0
- package/src/modules/embed/embed.routes.ts +363 -0
- package/src/modules/embed/embed.service.ts +705 -0
- package/src/modules/embed/embed.tools.ts +206 -0
- package/src/modules/embed/mcp-app.routes.ts +41 -0
- package/src/modules/embed/mcp-app.ts +173 -0
- package/src/modules/github-app/__tests__/github-app.test.ts +76 -0
- package/src/modules/github-app/github-app.connection.ts +38 -4
- package/src/modules/kb-fs/__tests__/move-with-edits.test.ts +304 -0
- package/src/modules/kb-fs/locking-filesystem.ts +287 -0
- package/src/modules/kb-sync/__tests__/kb-sync.service.test.ts +35 -0
- package/src/modules/kb-sync/kb-sync.interface.ts +2 -0
- package/src/modules/kb-sync/kb-sync.service.ts +6 -0
- package/src/modules/mcp/__tests__/bevel-oauth-provider.test.ts +68 -11
- package/src/modules/mcp/__tests__/fake-downstream-mcp-server.ts +14 -3
- package/src/modules/mcp/__tests__/mcp-proxy-helpers.test.ts +37 -2
- package/src/modules/mcp/__tests__/mcp.app.e2e.test.ts +200 -0
- package/src/modules/mcp/__tests__/mcp.e2e.test.ts +252 -0
- package/src/modules/mcp/__tests__/mcp.service.test.ts +42 -24
- package/src/modules/mcp/__tests__/tool-call-interface.e2e.test.ts +499 -0
- package/src/modules/mcp/__tests__/tool-schema-guard.test.ts +266 -0
- package/src/modules/mcp/mcp-auth.middleware.ts +2 -1
- package/src/modules/mcp/mcp.service.ts +218 -31
- package/src/modules/mcp/oauth/bevel-oauth-provider.ts +42 -4
- package/src/modules/mcp/tool-schema-guard.ts +196 -0
- package/src/modules/onboarding/__tests__/onboarding.routes.test.ts +163 -0
- package/src/modules/onboarding/__tests__/starter-pack.service.test.ts +635 -0
- package/src/modules/onboarding/__tests__/starter-packs.test.ts +169 -0
- package/src/modules/onboarding/onboarding.contract.ts +85 -0
- package/src/modules/onboarding/onboarding.routes.ts +85 -0
- package/src/modules/onboarding/starter-pack.service.ts +580 -0
- package/src/modules/onboarding/starter-packs.ts +182 -0
- package/src/modules/plugins/__tests__/plugins.tools.test.ts +177 -4
- package/src/modules/plugins/index.ts +1 -0
- package/src/modules/plugins/plugin-provision.service.ts +22 -1
- package/src/modules/plugins/plugins.routes.ts +29 -0
- package/src/modules/plugins/plugins.tools.ts +75 -15
- package/src/modules/settings/__tests__/deployment-settings.service.test.ts +176 -56
- package/src/modules/settings/__tests__/repository-source.test.ts +9 -0
- package/src/modules/settings/deployment-settings.service.ts +144 -55
- package/src/modules/settings/repository-source.ts +4 -4
- package/src/modules/settings/setup.routes.ts +3 -6
- package/src/modules/skills/__tests__/skills.tools.description.test.ts +95 -0
- package/src/modules/skills/skills.tools.ts +112 -46
- package/src/modules/tool-auth/__tests__/connection-key-rejection.test.ts +32 -0
- package/src/modules/tool-auth/__tests__/external-api-key.service.test.ts +65 -0
- package/src/modules/tool-auth/connection-key-rejection.ts +13 -3
- package/src/modules/tool-auth/external-api-key.interface.ts +9 -0
- package/src/modules/tool-auth/external-api-key.service.ts +61 -4
- package/src/modules/tool-auth/tool-auth.middleware.ts +13 -4
- package/src/modules/tool-helpers/__tests__/branch-declaration.test.ts +502 -0
- package/src/modules/tool-helpers/__tests__/route-argument-check.test.ts +337 -0
- package/src/modules/tool-helpers/index.ts +34 -1
- package/src/modules/tool-helpers/route-argument-check.ts +149 -0
- package/src/modules/tool-helpers/route-tool-schemas.ts +171 -0
- package/src/modules/tool-helpers/tool-def.ts +132 -7
- package/src/modules/tool-helpers/tool-handler.ts +196 -8
- package/src/modules/tool-helpers/tool.contract.ts +9 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.detail.route.test.ts +57 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +73 -4
- package/src/modules/tool-manuals/tool-manuals.contract.ts +24 -4
- package/src/modules/tool-manuals/tool-manuals.service.ts +17 -0
- package/src/modules/tool-manuals/tool-manuals.tools.ts +86 -38
- package/src/modules/tool-registry/__tests__/own-tool-schemas.test.ts +173 -0
- package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +81 -66
- package/src/modules/tool-registry/description-length.ts +57 -32
- package/src/modules/tool-registry/guide-first.ts +41 -0
- package/src/modules/tool-registry/tool-registry.ts +9 -2
- package/src/modules/workflow/__tests__/apply-failure.test.ts +6 -1
- package/src/modules/workflow/__tests__/file-lock.service.deletion-gate.test.ts +104 -0
- package/src/modules/workflow/__tests__/pending-commits.repository-replaced.test.ts +66 -0
- package/src/modules/workflow/__tests__/pending-commits.worker.test.ts +31 -2
- package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +110 -6
- package/src/modules/workflow/__tests__/sanitize-error.test.ts +89 -1
- package/src/modules/workflow/__tests__/workflow.routes.push-refused.test.ts +202 -0
- package/src/modules/workflow/__tests__/workflow.service.branch-in-use.test.ts +6 -6
- package/src/modules/workflow/__tests__/workflow.service.delete-branch.test.ts +1026 -0
- package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +468 -10
- package/src/modules/workflow/__tests__/workflow.service.folder-proposals.test.ts +4 -0
- package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +5 -4
- package/src/modules/workflow/__tests__/workflow.service.update-from-target.test.ts +54 -2
- package/src/modules/workflow/agent-tools/__tests__/change-request-read-shape.test.ts +705 -0
- package/src/modules/workflow/agent-tools/__tests__/change-request-read.tools.test.ts +1554 -0
- package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +150 -4
- package/src/modules/workflow/agent-tools/change-request-read-shape.ts +712 -0
- package/src/modules/workflow/agent-tools/change-request-read.tools.ts +759 -0
- package/src/modules/workflow/agent-tools/change-request-summary.ts +5 -1
- package/src/modules/workflow/agent-tools/workflow.tools.ts +137 -35
- package/src/modules/workflow/file-lock.service.ts +67 -5
- package/src/modules/workflow/git/__tests__/git.service.appliedChange.test.ts +285 -0
- package/src/modules/workflow/git/__tests__/git.service.changedFilesForPr.test.ts +124 -0
- package/src/modules/workflow/git/__tests__/git.service.deleteBranch.test.ts +164 -0
- package/src/modules/workflow/git/__tests__/git.service.mergeChangeRequest.test.ts +334 -0
- package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +48 -1
- package/src/modules/workflow/git/__tests__/node-git-runner.test.ts +231 -2
- package/src/modules/workflow/git/__tests__/pull-request.service.list-fetch.test.ts +72 -2
- package/src/modules/workflow/git/__tests__/pull-request.service.placeholder.test.ts +24 -2
- package/src/modules/workflow/git/__tests__/pull-request.service.test.ts +620 -1
- package/src/modules/workflow/git/git.service.ts +714 -133
- package/src/modules/workflow/git/merge-commit.ts +88 -0
- package/src/modules/workflow/git/node-git-runner.ts +82 -7
- package/src/modules/workflow/git/pull-request.service.ts +380 -54
- package/src/modules/workflow/pending-commits.service.ts +29 -1
- package/src/modules/workflow/pending-commits.worker.ts +24 -6
- package/src/modules/workflow/recovery-bot.ts +1 -1
- package/src/modules/workflow/review-workflow/__tests__/approval-states.test.ts +7 -1
- package/src/modules/workflow/review-workflow/__tests__/merge-records-own-commit.test.ts +407 -0
- package/src/modules/workflow/review-workflow/review-workflow.service.ts +191 -12
- package/src/modules/workflow/sanitize-error.ts +50 -0
- package/src/modules/workflow/workflow.routes.ts +7 -2
- package/src/modules/workflow/workflow.service.ts +886 -112
- package/src/modules/workspace/__tests__/agent-downloads.test.ts +823 -0
- package/src/modules/workspace/__tests__/escape-sequences.routes.test.ts +4 -3
- package/src/modules/workspace/__tests__/first-run.test.ts +234 -0
- package/src/modules/workspace/__tests__/git-internals.security.test.ts +5 -2
- package/src/modules/workspace/__tests__/md-links.test.ts +353 -0
- package/src/modules/workspace/__tests__/move-links.test.ts +261 -0
- package/src/modules/workspace/__tests__/rest-tool-arguments.test.ts +219 -0
- package/src/modules/workspace/__tests__/workspace-paths-inside-the-repo.test.ts +3 -0
- package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +102 -0
- package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +52 -5
- package/src/modules/workspace/__tests__/workspace.routes.folder-zip-access.test.ts +190 -0
- package/src/modules/workspace/__tests__/workspace.routes.move-platform-files.test.ts +21 -10
- package/src/modules/workspace/__tests__/workspace.service.forget-clone-races.test.ts +8 -4
- package/src/modules/workspace/__tests__/workspace.service.test.ts +50 -8
- package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +33 -55
- package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +44 -1
- package/src/modules/workspace/__tests__/workspace.tools.test.ts +664 -34
- package/src/modules/workspace/agent-download.builder.ts +308 -0
- package/src/modules/workspace/agent-download.routes.ts +183 -0
- package/src/modules/workspace/agent-download.store.ts +533 -0
- package/src/modules/workspace/agent-upload.store.ts +23 -7
- package/src/modules/workspace/file-readers/raw-mime.ts +40 -0
- package/src/modules/workspace/file-readers/text-reader.ts +5 -8
- package/src/modules/workspace/first-run.ts +222 -0
- package/src/modules/workspace/move-links.ts +237 -0
- package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +191 -489
- package/src/modules/workspace/startup/steps/seed-tree.ts +23 -38
- package/src/modules/workspace/startup/steps/template-files.step.ts +217 -249
- package/src/modules/workspace/startup/steps/template-source.ts +5 -3
- package/src/modules/workspace/workspace.routes.ts +64 -22
- package/src/modules/workspace/workspace.service.ts +202 -25
- package/src/modules/workspace/workspace.tools.ts +707 -68
- package/src/modules/write-access/__tests__/write-access.test.ts +13 -0
- package/src/modules/write-access/write-access.ts +9 -0
- package/src/shared/domain-errors.ts +123 -11
- package/src/shared/git.contract.ts +8 -1
- package/src/shared/hidden-tools.ts +45 -0
- package/src/shared/utf8-text.ts +21 -0
- package/src/tenancy/__tests__/static-tenant-source.test.ts +1 -0
- package/src/tenancy/static-tenant-source.ts +10 -2
- package/starter-packs/README.md +103 -0
- package/starter-packs/engineering/KnowledgeBase/About us.md +9 -0
- package/starter-packs/engineering/KnowledgeBase/Architecture.md +9 -0
- package/starter-packs/engineering/KnowledgeBase/Glossary.md +9 -0
- package/starter-packs/engineering/KnowledgeBase/How we ship.md +9 -0
- package/starter-packs/engineering/Plugins/engineering-starter/LICENSE +21 -0
- package/starter-packs/engineering/Plugins/engineering-starter/SOURCE.md +20 -0
- package/starter-packs/engineering/Plugins/engineering-starter/access.md +19 -0
- package/starter-packs/engineering/Plugins/engineering-starter/plugin.json +10 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/office-hours/SKILL.md +171 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/office-hours/sections/phase-2a-startup-diagnostic.md +157 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/office-hours/sections/phase-2b-builder-brainstorm.md +39 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/plan-ceo-review/SKILL.md +144 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/plan-eng-review/SKILL.md +144 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/review/SKILL.md +80 -0
- package/starter-packs/engineering/Plugins/engineering-starter/skills/review/checklist.md +184 -0
- package/starter-packs/engineering/pack.yaml +11 -0
- package/starter-packs/general/KnowledgeBase/About us.md +9 -0
- package/starter-packs/general/KnowledgeBase/Glossary.md +9 -0
- package/starter-packs/general/KnowledgeBase/How we work.md +9 -0
- package/starter-packs/general/Plugins/general-starter/LICENSE +202 -0
- package/starter-packs/general/Plugins/general-starter/SOURCE.md +10 -0
- package/starter-packs/general/Plugins/general-starter/access.md +19 -0
- package/starter-packs/general/Plugins/general-starter/plugin.json +10 -0
- package/starter-packs/general/Plugins/general-starter/skills/memory-management/SKILL.md +332 -0
- package/starter-packs/general/Plugins/general-starter/skills/productivity-start/SKILL.md +163 -0
- package/starter-packs/general/Plugins/general-starter/skills/productivity-update/SKILL.md +167 -0
- package/starter-packs/general/Plugins/general-starter/skills/task-management/SKILL.md +83 -0
- package/starter-packs/general/pack.yaml +10 -0
- package/starter-packs/operations/KnowledgeBase/Glossary.md +9 -0
- package/starter-packs/operations/KnowledgeBase/How we work.md +9 -0
- package/starter-packs/operations/KnowledgeBase/Processes.md +9 -0
- package/starter-packs/operations/KnowledgeBase/Tools we use.md +9 -0
- package/starter-packs/operations/KnowledgeBase/Vendors.md +9 -0
- package/starter-packs/operations/Plugins/operations-starter/LICENSE +202 -0
- package/starter-packs/operations/Plugins/operations-starter/SOURCE.md +10 -0
- package/starter-packs/operations/Plugins/operations-starter/access.md +19 -0
- package/starter-packs/operations/Plugins/operations-starter/plugin.json +10 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/capacity-plan/SKILL.md +111 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/change-request/SKILL.md +119 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/compliance-tracking/SKILL.md +44 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/process-doc/SKILL.md +80 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/process-optimization/SKILL.md +39 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/risk-assessment/SKILL.md +40 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/runbook/SKILL.md +85 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/status-report/SKILL.md +75 -0
- package/starter-packs/operations/Plugins/operations-starter/skills/vendor-review/SKILL.md +102 -0
- package/starter-packs/operations/pack.yaml +12 -0
- package/starter-packs/sales/KnowledgeBase/About us.md +9 -0
- package/starter-packs/sales/KnowledgeBase/Customers.md +9 -0
- package/starter-packs/sales/KnowledgeBase/Glossary.md +9 -0
- package/starter-packs/sales/KnowledgeBase/How we sell.md +9 -0
- package/starter-packs/sales/KnowledgeBase/Objections.md +9 -0
- package/starter-packs/sales/KnowledgeBase/Pricing.md +9 -0
- package/starter-packs/sales/Plugins/sales-starter/LICENSE +202 -0
- package/starter-packs/sales/Plugins/sales-starter/SOURCE.md +10 -0
- package/starter-packs/sales/Plugins/sales-starter/access.md +19 -0
- package/starter-packs/sales/Plugins/sales-starter/plugin.json +10 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/account-context/SKILL.md +94 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/account-plan/SKILL.md +95 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/account-research/SKILL.md +99 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/account-tiering/SKILL.md +101 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/call-prep/SKILL.md +111 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/call-summary/SKILL.md +111 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/close-plan/SKILL.md +106 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/competitive-intelligence/SKILL.md +102 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/create-an-asset/SKILL.md +105 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/crm-hygiene-check/SKILL.md +103 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/customer-health/SKILL.md +108 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/customer-voice/SKILL.md +94 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/daily-briefing/SKILL.md +61 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/deal-advance-gap/SKILL.md +102 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/deal-review/SKILL.md +105 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/deal-signals/SKILL.md +96 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/deal-slip-scenario/SKILL.md +104 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/draft-outreach/SKILL.md +116 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/end-of-day/SKILL.md +114 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/expansion-whitespace/SKILL.md +97 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/forecast/SKILL.md +100 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/handle-objection/SKILL.md +93 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/inbox-sweep/SKILL.md +114 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/lead-triage/SKILL.md +106 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/log-activity/SKILL.md +111 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/pipeline-review/SKILL.md +97 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/renewal-radar/SKILL.md +95 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/rep-context/SKILL.md +90 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/route-lead/SKILL.md +111 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/sales-setup/SKILL.md +200 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/schedule-meeting/SKILL.md +99 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/stakeholder-map/SKILL.md +107 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/team-pipeline/SKILL.md +105 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/update-opportunity/SKILL.md +112 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/weekly-wrap/SKILL.md +90 -0
- package/starter-packs/sales/Plugins/sales-starter/skills/win-loss-review/SKILL.md +88 -0
- package/starter-packs/sales/pack.yaml +13 -0
- package/starter-packs/support/KnowledgeBase/Escalations.md +9 -0
- package/starter-packs/support/KnowledgeBase/Glossary.md +9 -0
- package/starter-packs/support/KnowledgeBase/Known issues.md +9 -0
- package/starter-packs/support/KnowledgeBase/Products.md +9 -0
- package/starter-packs/support/KnowledgeBase/Tone of voice.md +9 -0
- package/starter-packs/support/Plugins/support-starter/LICENSE +202 -0
- package/starter-packs/support/Plugins/support-starter/SOURCE.md +10 -0
- package/starter-packs/support/Plugins/support-starter/access.md +19 -0
- package/starter-packs/support/Plugins/support-starter/plugin.json +10 -0
- package/starter-packs/support/Plugins/support-starter/skills/customer-escalation/SKILL.md +246 -0
- package/starter-packs/support/Plugins/support-starter/skills/customer-research/SKILL.md +250 -0
- package/starter-packs/support/Plugins/support-starter/skills/draft-response/SKILL.md +416 -0
- package/starter-packs/support/Plugins/support-starter/skills/kb-article/SKILL.md +350 -0
- package/starter-packs/support/Plugins/support-starter/skills/ticket-triage/SKILL.md +273 -0
- package/starter-packs/support/pack.yaml +12 -0
- package/kb-template/AGENTS.md +0 -730
|
@@ -517,3 +517,127 @@ describe('GitService.changedPathsForPr: who pays for the fetch', () => {
|
|
|
517
517
|
).toEqual(['brief.pdf']);
|
|
518
518
|
});
|
|
519
519
|
});
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* `changedPathsAndPairsForPr` is what a change-request SUMMARY is built from,
|
|
523
|
+
* and `changedFilesForPr` is what its DETAIL is built from. A read gate decides
|
|
524
|
+
* visibility over each — so if they ever describe different files, or pair a
|
|
525
|
+
* rename differently, a list advertises requests its own detail answers 404 for.
|
|
526
|
+
* That happened (ticket log, attempt 2: the list judged the flat path list,
|
|
527
|
+
* which names a rename by its new path alone), so the agreement is asserted
|
|
528
|
+
* here against a real repository rather than assumed.
|
|
529
|
+
*/
|
|
530
|
+
describe('GitService.changedPathsAndPairsForPr agrees with changedFilesForPr', () => {
|
|
531
|
+
let root: string;
|
|
532
|
+
const workspaceId = 'current-company-state';
|
|
533
|
+
|
|
534
|
+
beforeEach(async () => {
|
|
535
|
+
root = await fs.mkdtemp(path.join(os.tmpdir(), 'bevel-pr-pairs-'));
|
|
536
|
+
});
|
|
537
|
+
afterEach(async () => {
|
|
538
|
+
await fs.rm(root, { recursive: true, force: true }).catch(() => {});
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
/** The two file sets, each reduced to `path` + `previousPath`, sorted. */
|
|
542
|
+
async function bothViews(git: GitService, branch: string) {
|
|
543
|
+
const files = await git.changedFilesForPr(workspaceId, 'current-company-state', branch);
|
|
544
|
+
const { paths, pairs } = await git.changedPathsAndPairsForPr(
|
|
545
|
+
workspaceId,
|
|
546
|
+
'current-company-state',
|
|
547
|
+
branch,
|
|
548
|
+
);
|
|
549
|
+
const key = (f: { path: string; previousPath?: string }) => `${f.path}<=${f.previousPath ?? ''}`;
|
|
550
|
+
return {
|
|
551
|
+
detail: files.map(key).sort(),
|
|
552
|
+
summary: pairs.map(key).sort(),
|
|
553
|
+
paths: paths.sort(),
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
it('pairs a rename the same way, and the flat paths still name only the new side', async () => {
|
|
558
|
+
const { repo } = await seedWorkspace(root, workspaceId);
|
|
559
|
+
await fs.mkdir(path.join(repo, 'Engineering'), { recursive: true });
|
|
560
|
+
await fs.writeFile(path.join(repo, 'Engineering/note.md'), 'one\ntwo\nthree\n');
|
|
561
|
+
await runGit(repo, ['add', '-A']);
|
|
562
|
+
await runGit(repo, ['commit', '-m', 'note']);
|
|
563
|
+
await runGit(repo, ['push', 'origin', 'current-company-state']);
|
|
564
|
+
await runGit(repo, ['checkout', '-b', 'alice/moved']);
|
|
565
|
+
await fs.mkdir(path.join(repo, 'GTM'), { recursive: true });
|
|
566
|
+
await runGit(repo, ['mv', 'Engineering/note.md', 'GTM/note.md']);
|
|
567
|
+
await runGit(repo, ['commit', '-m', 'move it']);
|
|
568
|
+
await runGit(repo, ['push', '-u', 'origin', 'alice/moved']);
|
|
569
|
+
|
|
570
|
+
const git = new GitService(stubWorkspaceService(workspaceId, repo), new WorkflowHooks(), testKbContext());
|
|
571
|
+
const { detail, summary, paths } = await bothViews(git, 'alice/moved');
|
|
572
|
+
|
|
573
|
+
expect(summary).toEqual(['GTM/note.md<=Engineering/note.md']);
|
|
574
|
+
expect(summary).toEqual(detail);
|
|
575
|
+
// And this is exactly why the pairs are needed: the flat list cannot say
|
|
576
|
+
// the file came out of `Engineering/`.
|
|
577
|
+
expect(paths).toEqual(['GTM/note.md']);
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
it('agrees on a mixed change: add, modify, delete and rename together', async () => {
|
|
581
|
+
const { repo } = await seedWorkspace(root, workspaceId);
|
|
582
|
+
await fs.writeFile(path.join(repo, 'old-home.md'), 'a\nb\nc\nd\ne\n');
|
|
583
|
+
await fs.writeFile(path.join(repo, 'doomed.md'), 'goes away\n');
|
|
584
|
+
await runGit(repo, ['add', '-A']);
|
|
585
|
+
await runGit(repo, ['commit', '-m', 'seed more']);
|
|
586
|
+
await runGit(repo, ['push', 'origin', 'current-company-state']);
|
|
587
|
+
await runGit(repo, ['checkout', '-b', 'alice/mixed']);
|
|
588
|
+
await runGit(repo, ['mv', 'old-home.md', 'new-home.md']);
|
|
589
|
+
await fs.rm(path.join(repo, 'doomed.md'));
|
|
590
|
+
await fs.writeFile(path.join(repo, 'base.md'), 'base changed\n');
|
|
591
|
+
await fs.writeFile(path.join(repo, 'fresh.md'), 'new\n');
|
|
592
|
+
await runGit(repo, ['add', '-A']);
|
|
593
|
+
await runGit(repo, ['commit', '-m', 'mixed']);
|
|
594
|
+
await runGit(repo, ['push', '-u', 'origin', 'alice/mixed']);
|
|
595
|
+
|
|
596
|
+
const git = new GitService(stubWorkspaceService(workspaceId, repo), new WorkflowHooks(), testKbContext());
|
|
597
|
+
const { detail, summary } = await bothViews(git, 'alice/mixed');
|
|
598
|
+
expect(summary).toEqual(detail);
|
|
599
|
+
expect(summary).toContain('new-home.md<=old-home.md');
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
it('agrees that roles.yaml and the folder placeholder are not files of the request', async () => {
|
|
603
|
+
const { repo } = await seedWorkspace(root, workspaceId);
|
|
604
|
+
await runGit(repo, ['checkout', '-b', 'alice/config']);
|
|
605
|
+
await fs.writeFile(path.join(repo, 'roles.yaml'), 'roles:\n Admin:\n - a@x.io\n');
|
|
606
|
+
await fs.mkdir(path.join(repo, 'Reports'), { recursive: true });
|
|
607
|
+
await fs.writeFile(path.join(repo, 'Reports/.gitkeep'), '');
|
|
608
|
+
await fs.writeFile(path.join(repo, 'Reports/q3.md'), 'one\n');
|
|
609
|
+
await runGit(repo, ['add', '-A']);
|
|
610
|
+
await runGit(repo, ['commit', '-m', 'config + folder']);
|
|
611
|
+
await runGit(repo, ['push', '-u', 'origin', 'alice/config']);
|
|
612
|
+
|
|
613
|
+
const git = new GitService(stubWorkspaceService(workspaceId, repo), new WorkflowHooks(), testKbContext());
|
|
614
|
+
const { detail, summary, paths } = await bothViews(git, 'alice/config');
|
|
615
|
+
expect(summary).toEqual(['Reports/q3.md<=']);
|
|
616
|
+
expect(summary).toEqual(detail);
|
|
617
|
+
// The flat list keeps the placeholder — it is the authoritative
|
|
618
|
+
// empty-request check — which is a third reason not to gate reads on it.
|
|
619
|
+
expect(paths).toContain('Reports/.gitkeep');
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
it('agrees an empty file replaced by the placeholder is the file\'s removal', async () => {
|
|
623
|
+
const { repo } = await seedWorkspace(root, workspaceId);
|
|
624
|
+
await fs.mkdir(path.join(repo, 'Reports'), { recursive: true });
|
|
625
|
+
await fs.writeFile(path.join(repo, 'Reports/empty.md'), '');
|
|
626
|
+
await runGit(repo, ['add', '-A']);
|
|
627
|
+
await runGit(repo, ['commit', '-m', 'empty file']);
|
|
628
|
+
await runGit(repo, ['push', 'origin', 'current-company-state']);
|
|
629
|
+
await runGit(repo, ['checkout', '-b', 'alice/emptied']);
|
|
630
|
+
await fs.rm(path.join(repo, 'Reports/empty.md'));
|
|
631
|
+
await fs.writeFile(path.join(repo, 'Reports/.gitkeep'), '');
|
|
632
|
+
await runGit(repo, ['add', '-A']);
|
|
633
|
+
await runGit(repo, ['commit', '-m', 'emptied']);
|
|
634
|
+
await runGit(repo, ['push', '-u', 'origin', 'alice/emptied']);
|
|
635
|
+
|
|
636
|
+
const git = new GitService(stubWorkspaceService(workspaceId, repo), new WorkflowHooks(), testKbContext());
|
|
637
|
+
const { detail, summary } = await bothViews(git, 'alice/emptied');
|
|
638
|
+
// `withoutPlaceholderRename` runs on both sides, so neither reports a
|
|
639
|
+
// rename onto `.gitkeep` and neither loses the removed file.
|
|
640
|
+
expect(summary).toEqual(['Reports/empty.md<=']);
|
|
641
|
+
expect(summary).toEqual(detail);
|
|
642
|
+
});
|
|
643
|
+
});
|
|
@@ -12,6 +12,7 @@ import type { IAccessControl } from '../../../access/access-control.interface.js
|
|
|
12
12
|
import { GitService } from '../git.service.js';
|
|
13
13
|
import {
|
|
14
14
|
BranchAuthorshipError,
|
|
15
|
+
BranchDeleteRefusedError,
|
|
15
16
|
ProtectedBranchError,
|
|
16
17
|
WorkflowValidationError,
|
|
17
18
|
} from '../../../../shared/domain-errors.js';
|
|
@@ -182,6 +183,57 @@ describe('GitService.deleteBranch — authorship + remote delete', () => {
|
|
|
182
183
|
expect(await remoteHasRef(upstream, 'alice/my-draft')).toBe(false);
|
|
183
184
|
});
|
|
184
185
|
|
|
186
|
+
it('when the host refuses the remote deletion, deletes nothing and says so (409, no git output)', async () => {
|
|
187
|
+
const { upstream, repo } = await seedWorkspace(root, workspaceId);
|
|
188
|
+
// The host turns every push away, as GitHub did during its incident.
|
|
189
|
+
const hook = path.join(upstream, 'hooks', 'pre-receive');
|
|
190
|
+
await fs.writeFile(hook, '#!/bin/sh\necho "Internal Server Error" >&2\nexit 1\n');
|
|
191
|
+
await fs.chmod(hook, 0o755);
|
|
192
|
+
const svc = makeService();
|
|
193
|
+
|
|
194
|
+
const err = await svc.deleteBranch(workspaceId, 'alice/my-draft', ALICE).catch((e: unknown) => e);
|
|
195
|
+
expect(err).toBeInstanceOf(BranchDeleteRefusedError);
|
|
196
|
+
const refusal = err as BranchDeleteRefusedError;
|
|
197
|
+
expect(refusal.status).toBe(409);
|
|
198
|
+
expect(refusal.message).toBe(
|
|
199
|
+
'The repository host refused to delete "alice/my-draft"; it is still there. Try again later.',
|
|
200
|
+
);
|
|
201
|
+
expect(JSON.stringify({ ...refusal.payload, error: refusal.message })).not.toMatch(
|
|
202
|
+
/Internal Server Error|remote rejected|pre-receive|upstream\.git/,
|
|
203
|
+
);
|
|
204
|
+
// Still there — remotely AND locally.
|
|
205
|
+
expect(await remoteHasRef(upstream, 'alice/my-draft')).toBe(true);
|
|
206
|
+
expect(
|
|
207
|
+
await gitOut(repo, ['for-each-ref', 'refs/heads/alice/my-draft', '--format', '%(refname)']),
|
|
208
|
+
).toBe('refs/heads/alice/my-draft');
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('when the host cannot be reached, the local branch stays too', async () => {
|
|
212
|
+
const { repo } = await seedWorkspace(root, workspaceId);
|
|
213
|
+
await runGit(repo, ['remote', 'set-url', 'origin', path.join(root, 'gone.git')]);
|
|
214
|
+
const svc = makeService();
|
|
215
|
+
|
|
216
|
+
await expect(svc.deleteBranch(workspaceId, 'alice/my-draft', ALICE)).rejects.toBeInstanceOf(
|
|
217
|
+
BranchDeleteRefusedError,
|
|
218
|
+
);
|
|
219
|
+
expect(
|
|
220
|
+
await gitOut(repo, ['for-each-ref', 'refs/heads/alice/my-draft', '--format', '%(refname)']),
|
|
221
|
+
).toBe('refs/heads/alice/my-draft');
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('deletes the remote branch even when this clone has no tracking ref for it (pushed from elsewhere since the last fetch)', async () => {
|
|
225
|
+
const { upstream, repo } = await seedWorkspace(root, workspaceId);
|
|
226
|
+
await runGit(repo, ['update-ref', '-d', 'refs/remotes/origin/alice/my-draft']);
|
|
227
|
+
const svc = makeService();
|
|
228
|
+
|
|
229
|
+
await svc.deleteBranch(workspaceId, 'alice/my-draft', ALICE);
|
|
230
|
+
|
|
231
|
+
expect(await remoteHasRef(upstream, 'alice/my-draft')).toBe(false);
|
|
232
|
+
expect(
|
|
233
|
+
await gitOut(repo, ['for-each-ref', 'refs/heads/alice/my-draft', '--format', '%(refname)']),
|
|
234
|
+
).toBe('');
|
|
235
|
+
});
|
|
236
|
+
|
|
185
237
|
it("refuses when the user isn't the author — leaves the remote ref intact", async () => {
|
|
186
238
|
const { upstream } = await seedWorkspace(root, workspaceId);
|
|
187
239
|
const svc = makeService();
|
|
@@ -318,3 +370,115 @@ describe('GitService.deleteBranch — authorship + remote delete', () => {
|
|
|
318
370
|
).rejects.toBeInstanceOf(ProtectedBranchError);
|
|
319
371
|
});
|
|
320
372
|
});
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* The pieces an agent's `delete_branch` and the leftover cleanup decide with:
|
|
376
|
+
* where a branch stands against the default branch, whether a commit is
|
|
377
|
+
* contained in another, and a delete leased on the tip the checks saw.
|
|
378
|
+
*/
|
|
379
|
+
describe('GitService — branch state for a checked delete', () => {
|
|
380
|
+
let root: string;
|
|
381
|
+
const workspaceId = 'target-company-state';
|
|
382
|
+
|
|
383
|
+
beforeEach(async () => {
|
|
384
|
+
root = await mkTmpRoot();
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
afterEach(async () => {
|
|
388
|
+
await fs.rm(root, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
function makeService(): GitService {
|
|
392
|
+
return new GitService(
|
|
393
|
+
stubWorkspaceService(workspaceId, path.join(root, workspaceId)),
|
|
394
|
+
new WorkflowHooks(),
|
|
395
|
+
testKbContext(),
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/** Push `n` commits onto `branch` from a separate clone, as a colleague would. */
|
|
400
|
+
async function pushCommits(upstream: string, branch: string, n: number): Promise<string> {
|
|
401
|
+
const other = path.join(root, `.other-${Math.random().toString(36).slice(2)}`);
|
|
402
|
+
await runGit(root, ['clone', '-b', branch, upstream, other]);
|
|
403
|
+
for (let i = 0; i < n; i++) await runGit(other, ['commit', '--allow-empty', '-m', `work ${i}`]);
|
|
404
|
+
await runGit(other, ['push', 'origin', branch]);
|
|
405
|
+
return gitOut(other, ['rev-parse', 'HEAD']);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
it('counts the commits not on the default branch, and reads the tip', async () => {
|
|
409
|
+
const { upstream, repo } = await seedWorkspace(root, workspaceId);
|
|
410
|
+
const tip = await pushCommits(upstream, 'bob/other-draft', 3);
|
|
411
|
+
await runGit(repo, ['fetch', 'origin']);
|
|
412
|
+
const svc = makeService();
|
|
413
|
+
|
|
414
|
+
const state = await svc.branchState(workspaceId, 'bob/other-draft', 'target-company-state');
|
|
415
|
+
expect(state).toEqual({ exists: true, lastCommit: tip, unmergedCommits: 3 });
|
|
416
|
+
|
|
417
|
+
// A branch with nothing of its own counts none.
|
|
418
|
+
const merged = await svc.branchState(workspaceId, 'alice/my-draft', 'target-company-state');
|
|
419
|
+
expect(merged.exists).toBe(true);
|
|
420
|
+
expect(merged.unmergedCommits).toBe(0);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
it('a name that is not a branch does not exist', async () => {
|
|
424
|
+
await seedWorkspace(root, workspaceId);
|
|
425
|
+
const state = await makeService().branchState(workspaceId, 'alice/no-such-branch', 'target-company-state');
|
|
426
|
+
expect(state).toEqual({ exists: false, lastCommit: null, unmergedCommits: 0 });
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
it('isAncestor: yes for a contained commit, no for one that is not, and throws for an unknown commit', async () => {
|
|
430
|
+
const { upstream, repo } = await seedWorkspace(root, workspaceId);
|
|
431
|
+
const base = await gitOut(repo, ['rev-parse', 'HEAD']);
|
|
432
|
+
const tip = await pushCommits(upstream, 'bob/other-draft', 1);
|
|
433
|
+
await runGit(repo, ['fetch', 'origin']);
|
|
434
|
+
const svc = makeService();
|
|
435
|
+
|
|
436
|
+
expect(await svc.isAncestor(workspaceId, base, tip)).toBe(true);
|
|
437
|
+
expect(await svc.isAncestor(workspaceId, tip, base)).toBe(false);
|
|
438
|
+
await expect(svc.isAncestor(workspaceId, 'f'.repeat(40), base)).rejects.toThrow();
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
it('removes the branch from the shared repository even when this clone never fetched it, and answers its tip', async () => {
|
|
442
|
+
const { upstream, repo } = await seedWorkspace(root, workspaceId);
|
|
443
|
+
// Created from elsewhere after this clone was made: no tracking ref here.
|
|
444
|
+
const other = path.join(root, '.creator');
|
|
445
|
+
await runGit(root, ['clone', upstream, other]);
|
|
446
|
+
await runGit(other, ['checkout', '-b', 'alice/new-elsewhere']);
|
|
447
|
+
await runGit(other, ['commit', '--allow-empty', '-m', 'elsewhere']);
|
|
448
|
+
await runGit(other, ['push', 'origin', 'alice/new-elsewhere']);
|
|
449
|
+
const tip = await gitOut(other, ['rev-parse', 'HEAD']);
|
|
450
|
+
expect(await gitOut(repo, ['for-each-ref', 'refs/remotes/origin/alice/new-elsewhere'])).toBe('');
|
|
451
|
+
|
|
452
|
+
const result = await makeService().deleteBranch(workspaceId, 'alice/new-elsewhere', ALICE);
|
|
453
|
+
expect(result).toEqual({ lastCommit: tip });
|
|
454
|
+
expect(await remoteHasRef(upstream, 'alice/new-elsewhere')).toBe(false);
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
it('a delete leased on a tip refuses a branch that moved since, and leaves it in place', async () => {
|
|
458
|
+
const { upstream } = await seedWorkspace(root, workspaceId);
|
|
459
|
+
const seen = await gitOut(upstream, ['rev-parse', 'refs/heads/alice/my-draft']);
|
|
460
|
+
// A colleague pushes after the checks were made.
|
|
461
|
+
await pushCommits(upstream, 'alice/my-draft', 1);
|
|
462
|
+
|
|
463
|
+
const err = await makeService()
|
|
464
|
+
.deleteBranch(workspaceId, 'alice/my-draft', ALICE, { expectTip: seen })
|
|
465
|
+
.catch((e: unknown) => e);
|
|
466
|
+
expect(err).toBeInstanceOf(BranchDeleteRefusedError);
|
|
467
|
+
expect(await remoteHasRef(upstream, 'alice/my-draft')).toBe(true);
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
it('a delete leased on the current tip goes through', async () => {
|
|
471
|
+
const { upstream } = await seedWorkspace(root, workspaceId);
|
|
472
|
+
const seen = await gitOut(upstream, ['rev-parse', 'refs/heads/alice/my-draft']);
|
|
473
|
+
const result = await makeService().deleteBranch(workspaceId, 'alice/my-draft', ALICE, { expectTip: seen });
|
|
474
|
+
expect(result.lastCommit).toBe(seen);
|
|
475
|
+
expect(await remoteHasRef(upstream, 'alice/my-draft')).toBe(false);
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
it('mayDeleteBranch: the author yes, anyone else without the Admin role no', async () => {
|
|
479
|
+
await seedWorkspace(root, workspaceId);
|
|
480
|
+
const svc = makeService();
|
|
481
|
+
expect(await svc.mayDeleteBranch(workspaceId, 'alice/my-draft', ALICE)).toBe(true);
|
|
482
|
+
expect(await svc.mayDeleteBranch(workspaceId, 'bob/other-draft', ALICE)).toBe(false);
|
|
483
|
+
});
|
|
484
|
+
});
|
|
@@ -9,6 +9,7 @@ import type { AuthUser } from '@bevel-software/platform-shared';
|
|
|
9
9
|
import type { WorkspaceService } from '../../../workspace/workspace.service.js';
|
|
10
10
|
import { WorkflowHooks } from '../../workflow-hooks.js';
|
|
11
11
|
import { GitService } from '../git.service.js';
|
|
12
|
+
import { mergeCommitSubject } from '../merge-commit.js';
|
|
12
13
|
|
|
13
14
|
const execFileAsync = promisify(execFile);
|
|
14
15
|
const BASE = 'current-company-state';
|
|
@@ -103,6 +104,9 @@ describe('GitService.mergeChangeRequest', () => {
|
|
|
103
104
|
expect(result.kind).toBe('merged');
|
|
104
105
|
if (result.kind !== 'merged') return;
|
|
105
106
|
expect(result.sha).toMatch(/^[0-9a-f]{40}$/);
|
|
107
|
+
// A commit of this request's own was written: it IS the state the target is
|
|
108
|
+
// left at here, and it is what the row may record as the merge commit.
|
|
109
|
+
expect(result.mergeCommit).toBe(result.sha);
|
|
106
110
|
|
|
107
111
|
// The merge landed on origin/BASE: a fresh clone sees the feature file and a
|
|
108
112
|
// merge commit authored by the human triggerer.
|
|
@@ -115,6 +119,336 @@ describe('GitService.mergeChangeRequest', () => {
|
|
|
115
119
|
expect(log).toContain('Add feature (#1)');
|
|
116
120
|
});
|
|
117
121
|
|
|
122
|
+
// cubic P1 on #347. When the target already contains the source there is
|
|
123
|
+
// nothing to merge, so no commit is written and `sha` is the TARGET TIP —
|
|
124
|
+
// whatever landed on the target last, which in a deployment that lands
|
|
125
|
+
// everything through change requests is usually ANOTHER request's merge commit.
|
|
126
|
+
// Answering `mergeCommit: null` is what stops the row recording it as this
|
|
127
|
+
// request's own: a reader that took it would answer with that other request's
|
|
128
|
+
// files under this request's number. The fixture puts request #1's merge commit
|
|
129
|
+
// on the tip for exactly that reason.
|
|
130
|
+
it('owns NO merge commit when there was nothing to merge, not even the one on the tip', async () => {
|
|
131
|
+
const { upstream, baseWsId, baseRepo } = await seed(root, { 'base.md': 'base\n' });
|
|
132
|
+
// Another request lands first, so the target tip is a real merge commit whose
|
|
133
|
+
// subject names #1.
|
|
134
|
+
await pushFeatureBranch(root, upstream, 'bob/first', async (dir) => {
|
|
135
|
+
await fs.writeFile(path.join(dir, 'first.md'), 'first\n');
|
|
136
|
+
});
|
|
137
|
+
const git = new GitService(stubWorkspaceService(baseWsId, baseRepo), new WorkflowHooks(), testKbContext());
|
|
138
|
+
const first = await git.mergeChangeRequest(
|
|
139
|
+
baseWsId, 'bob/first', BASE, { subject: mergeCommitSubject('First', 1), body: 'x' }, USER,
|
|
140
|
+
{ appliedChangeNumber: 1 },
|
|
141
|
+
);
|
|
142
|
+
expect(first.kind).toBe('merged');
|
|
143
|
+
|
|
144
|
+
// A branch the target already contains: branched and pushed with no commit of
|
|
145
|
+
// its own, so the merge has nothing to do. (`pushFeatureBranch` always
|
|
146
|
+
// commits, which is the one thing this case must not do.)
|
|
147
|
+
const emptyDir = path.join(root, 'feat-empty');
|
|
148
|
+
await runGit(root, ['clone', '-b', BASE, upstream, emptyDir]);
|
|
149
|
+
await runGit(emptyDir, ['checkout', '-b', 'alice/empty']);
|
|
150
|
+
await runGit(emptyDir, ['push', '-u', 'origin', 'alice/empty']);
|
|
151
|
+
|
|
152
|
+
const result = await git.mergeChangeRequest(
|
|
153
|
+
baseWsId, 'alice/empty', BASE,
|
|
154
|
+
{ subject: mergeCommitSubject('Nothing to do', 2), body: 'Merged via Bevel' }, USER,
|
|
155
|
+
{ appliedChangeNumber: 2 },
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
expect(result.kind).toBe('merged');
|
|
159
|
+
if (result.kind !== 'merged') return;
|
|
160
|
+
// Asked for #2's own merge commit and there is none — the one on the tip is
|
|
161
|
+
// #1's, and it is not offered in its place.
|
|
162
|
+
expect(result.mergeCommit).toBeNull();
|
|
163
|
+
// The sha it reports is the target tip, which is #1's merge commit.
|
|
164
|
+
const verify = path.join(root, 'verify-empty');
|
|
165
|
+
await runGit(root, ['clone', '-b', BASE, upstream, verify]);
|
|
166
|
+
expect((await gitOut(verify, ['rev-parse', 'HEAD'])).trim()).toBe(result.sha);
|
|
167
|
+
expect(await gitOut(verify, ['log', '-1', '--format=%s'])).toContain('(#1)');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* cubic P2 on #347. The push and the row update are two steps, and a transient
|
|
172
|
+
* database fault between them leaves the merge commit ON the target with the
|
|
173
|
+
* row still open. The retry then finds nothing to merge — and if that answered
|
|
174
|
+
* "no merge commit", the row would record none, and the request would be
|
|
175
|
+
* permanently fileless (author-only) with the commit holding its files sitting
|
|
176
|
+
* on the target unreferenced.
|
|
177
|
+
*/
|
|
178
|
+
it('recovers the merge commit a previous attempt pushed but never recorded', async () => {
|
|
179
|
+
const { upstream, baseWsId, baseRepo } = await seed(root, { 'base.md': 'base\n' });
|
|
180
|
+
await pushFeatureBranch(root, upstream, 'alice/add', async (dir) => {
|
|
181
|
+
await fs.writeFile(path.join(dir, 'feature.md'), 'new content\n');
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const git = new GitService(stubWorkspaceService(baseWsId, baseRepo), new WorkflowHooks(), testKbContext());
|
|
185
|
+
const merge = () =>
|
|
186
|
+
git.mergeChangeRequest(
|
|
187
|
+
baseWsId, 'alice/add', BASE,
|
|
188
|
+
{ subject: mergeCommitSubject('Add feature', 7), body: 'Merged via Bevel' }, USER,
|
|
189
|
+
{ appliedChangeNumber: 7 },
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
// Attempt one succeeds in git; imagine the row update failing right here.
|
|
193
|
+
const first = await merge();
|
|
194
|
+
expect(first.kind).toBe('merged');
|
|
195
|
+
if (first.kind !== 'merged') return;
|
|
196
|
+
const pushed = first.mergeCommit;
|
|
197
|
+
expect(pushed).toBe(first.sha);
|
|
198
|
+
|
|
199
|
+
// Attempt two: the target already contains the source, so nothing is staged
|
|
200
|
+
// and no new commit is written — but #7 does own a merge commit, and it is
|
|
201
|
+
// the one the first attempt pushed.
|
|
202
|
+
const second = await merge();
|
|
203
|
+
expect(second.kind).toBe('merged');
|
|
204
|
+
if (second.kind !== 'merged') return;
|
|
205
|
+
expect(second.mergeCommit).toBe(pushed);
|
|
206
|
+
|
|
207
|
+
// Nothing new was pushed: the target is still at that same commit.
|
|
208
|
+
const verify = path.join(root, 'verify-retry');
|
|
209
|
+
await runGit(root, ['clone', '-b', BASE, upstream, verify]);
|
|
210
|
+
expect((await gitOut(verify, ['rev-parse', 'HEAD'])).trim()).toBe(pushed);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// The same ownership question, asked WITHOUT a merge — the form the approval
|
|
214
|
+
// gate needs, because the gate refuses the retry before any merge runs. It
|
|
215
|
+
// must hold in the two conditions that are true by then: the source branch is
|
|
216
|
+
// retired, and the clone asking is not the clone that pushed.
|
|
217
|
+
it('finds the merge commit a request owns on the target from another clone, with no source branch left', async () => {
|
|
218
|
+
const { upstream, baseWsId, baseRepo } = await seed(root, { 'base.md': 'base\n' });
|
|
219
|
+
await pushFeatureBranch(root, upstream, 'alice/add', async (dir) => {
|
|
220
|
+
await fs.writeFile(path.join(dir, 'feature.md'), 'new content\n');
|
|
221
|
+
});
|
|
222
|
+
// Cloned BEFORE the merge, so it can only know the commit by fetching.
|
|
223
|
+
const staleWsId = 'stale';
|
|
224
|
+
const staleRepo = path.join(root, staleWsId, 'knowledge-base');
|
|
225
|
+
await fs.mkdir(path.join(root, staleWsId), { recursive: true });
|
|
226
|
+
await runGit(root, ['clone', '-b', BASE, upstream, staleRepo]);
|
|
227
|
+
|
|
228
|
+
const git = new GitService(stubWorkspaceService(baseWsId, baseRepo), new WorkflowHooks(), testKbContext());
|
|
229
|
+
const merged = await git.mergeChangeRequest(
|
|
230
|
+
baseWsId, 'alice/add', BASE,
|
|
231
|
+
{ subject: mergeCommitSubject('Add feature', 7), body: 'Merged via Bevel' }, USER,
|
|
232
|
+
{ appliedChangeNumber: 7 },
|
|
233
|
+
);
|
|
234
|
+
expect(merged.kind).toBe('merged');
|
|
235
|
+
if (merged.kind !== 'merged') return;
|
|
236
|
+
|
|
237
|
+
// Retired, as applying a change request retires it.
|
|
238
|
+
await runGit(root, ['-C', upstream, 'branch', '-D', 'alice/add']);
|
|
239
|
+
|
|
240
|
+
const stale = new GitService(stubWorkspaceService(staleWsId, staleRepo), new WorkflowHooks(), testKbContext());
|
|
241
|
+
expect(await stale.appliedMergeCommitOnTarget(staleWsId, BASE, 7)).toBe(merged.mergeCommit);
|
|
242
|
+
// A number nothing on the target was merged under owns nothing.
|
|
243
|
+
expect(await stale.appliedMergeCommitOnTarget(staleWsId, BASE, 8)).toBeNull();
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('still finds the commit behind any number of newer merges on a busy target', async () => {
|
|
247
|
+
const { upstream, baseWsId, baseRepo } = await seed(root, { 'base.md': 'base\n' });
|
|
248
|
+
await pushFeatureBranch(root, upstream, 'alice/add', async (dir) => {
|
|
249
|
+
await fs.writeFile(path.join(dir, 'feature.md'), 'new content\n');
|
|
250
|
+
});
|
|
251
|
+
const git = new GitService(stubWorkspaceService(baseWsId, baseRepo), new WorkflowHooks(), testKbContext());
|
|
252
|
+
const merged = await git.mergeChangeRequest(
|
|
253
|
+
baseWsId, 'alice/add', BASE,
|
|
254
|
+
{ subject: mergeCommitSubject('Add feature', 7), body: 'Merged via Bevel' }, USER,
|
|
255
|
+
{ appliedChangeNumber: 7 },
|
|
256
|
+
);
|
|
257
|
+
expect(merged.kind).toBe('merged');
|
|
258
|
+
if (merged.kind !== 'merged') return;
|
|
259
|
+
|
|
260
|
+
// Twenty-five merges land on the target after it — more than the fixed
|
|
261
|
+
// count the probe once stopped at, which left a stuck request on a busy
|
|
262
|
+
// branch un-finalizable for good.
|
|
263
|
+
const busy = path.join(root, 'busy');
|
|
264
|
+
await runGit(root, ['clone', '-b', BASE, upstream, busy]);
|
|
265
|
+
for (let i = 0; i < 25; i += 1) {
|
|
266
|
+
await runGit(busy, ['checkout', '-b', `other-${i}`]);
|
|
267
|
+
await fs.writeFile(path.join(busy, `other-${i}.md`), `${i}\n`);
|
|
268
|
+
await runGit(busy, ['add', '-A']);
|
|
269
|
+
await runGit(busy, ['commit', '-m', `work ${i}`]);
|
|
270
|
+
await runGit(busy, ['checkout', BASE]);
|
|
271
|
+
await runGit(busy, ['merge', '--no-ff', '-m', `Other change ${i} (#${100 + i})`, `other-${i}`]);
|
|
272
|
+
}
|
|
273
|
+
await runGit(busy, ['push', 'origin', BASE]);
|
|
274
|
+
|
|
275
|
+
expect(await git.appliedMergeCommitOnTarget(baseWsId, BASE, 7)).toBe(merged.mergeCommit);
|
|
276
|
+
// Twenty-five merges are a hundred and fifty git processes; slow on a
|
|
277
|
+
// loaded Windows runner, so this one gets its own budget.
|
|
278
|
+
}, 120_000);
|
|
279
|
+
|
|
280
|
+
it('recognises a merge commit an earlier release wrote with the title unflattened, given the stored title', async () => {
|
|
281
|
+
// Before titles were flattened, a title with a blank line put `(#N)` after
|
|
282
|
+
// git's one-paragraph subject, and such rows read as author-only. The row
|
|
283
|
+
// still holds the title, which is what names the commit now.
|
|
284
|
+
const { upstream, baseWsId, baseRepo } = await seed(root, { 'base.md': 'base\n' });
|
|
285
|
+
await pushFeatureBranch(root, upstream, 'alice/add', async (dir) => {
|
|
286
|
+
await fs.writeFile(path.join(dir, 'feature.md'), 'new content\n');
|
|
287
|
+
});
|
|
288
|
+
const title = 'Add feature\n\nand a note';
|
|
289
|
+
const old = path.join(root, 'old-format');
|
|
290
|
+
await runGit(root, ['clone', '-b', BASE, upstream, old]);
|
|
291
|
+
await runGit(old, ['merge', '--no-ff', '-m', `${title} (#5)`, '-m', 'Merged via Bevel', 'origin/alice/add']);
|
|
292
|
+
await runGit(old, ['push', 'origin', BASE]);
|
|
293
|
+
const sha = (await gitOut(old, ['rev-parse', 'HEAD'])).trim();
|
|
294
|
+
expect((await gitOut(old, ['log', '-1', '--format=%s'])).trim()).toBe('Add feature');
|
|
295
|
+
|
|
296
|
+
const git = new GitService(stubWorkspaceService(baseWsId, baseRepo), new WorkflowHooks(), testKbContext());
|
|
297
|
+
// With the title: the commit is the request's own, on the target and by sha.
|
|
298
|
+
expect(await git.appliedMergeCommitOnTarget(baseWsId, BASE, 5, title)).toBe(sha);
|
|
299
|
+
expect(await git.appliedChangeShas(baseWsId, { number: 5, mergeSha: sha, title }))
|
|
300
|
+
.toMatchObject({ baseSha: expect.any(String), headSha: expect.any(String) });
|
|
301
|
+
// Without it, nothing vouches for a number that is not where the subject
|
|
302
|
+
// rule looks — and another title does not either.
|
|
303
|
+
expect(await git.appliedMergeCommitOnTarget(baseWsId, BASE, 5)).toBeNull();
|
|
304
|
+
await expect(git.appliedChangeShas(baseWsId, { number: 5, mergeSha: sha })).rejects.toThrow(/not the merge commit/);
|
|
305
|
+
expect(await git.appliedMergeCommitOnTarget(baseWsId, BASE, 5, 'Add feature')).toBeNull();
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('does not take a current-format commit of another request for one whose title merely contains its number', async () => {
|
|
309
|
+
// Request 43, titled `Fix crash (#42)`, merges as `Fix crash (#42) (#43)`.
|
|
310
|
+
// Asked for request 42 with the stored title `Fix crash`, the old-format
|
|
311
|
+
// rule must refuse it: 42's number is not at the end of the line there.
|
|
312
|
+
const { upstream, baseWsId, baseRepo } = await seed(root, { 'base.md': 'base\n' });
|
|
313
|
+
await pushFeatureBranch(root, upstream, 'alice/add', async (dir) => {
|
|
314
|
+
await fs.writeFile(path.join(dir, 'feature.md'), 'new content\n');
|
|
315
|
+
});
|
|
316
|
+
const git = new GitService(stubWorkspaceService(baseWsId, baseRepo), new WorkflowHooks(), testKbContext());
|
|
317
|
+
const merged = await git.mergeChangeRequest(
|
|
318
|
+
baseWsId, 'alice/add', BASE,
|
|
319
|
+
{ subject: mergeCommitSubject('Fix crash (#42)', 43), body: 'x' }, USER,
|
|
320
|
+
{ appliedChangeNumber: 43 },
|
|
321
|
+
);
|
|
322
|
+
expect(merged.kind).toBe('merged');
|
|
323
|
+
if (merged.kind !== 'merged') return;
|
|
324
|
+
expect(await git.appliedMergeCommitOnTarget(baseWsId, BASE, 43)).toBe(merged.mergeCommit);
|
|
325
|
+
expect(await git.appliedMergeCommitOnTarget(baseWsId, BASE, 42, 'Fix crash')).toBeNull();
|
|
326
|
+
await expect(git.appliedChangeShas(baseWsId, { number: 42, mergeSha: merged.sha, title: 'Fix crash' })).rejects.toThrow(
|
|
327
|
+
/not the merge commit/,
|
|
328
|
+
);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
// One variable at a time, because the two halves of the P1 are not the same
|
|
332
|
+
// claim. With the remote REACHABLE, a missing tracking ref is not a hazard at
|
|
333
|
+
// all: the refresh names its destination explicitly, so it recreates the ref
|
|
334
|
+
// and the walk has what it needs. With the remote UNREACHABLE, there is no
|
|
335
|
+
// authority for an answer, and the stale ref the clone still holds must not be
|
|
336
|
+
// scanned — what the caller does with a commit is record it as the request's
|
|
337
|
+
// published state, and a ref it could not refresh may name a commit the
|
|
338
|
+
// published branch no longer carries (cubic P1 on #347).
|
|
339
|
+
it('recreates a missing tracking ref, and refuses to read a stale one', async () => {
|
|
340
|
+
const { upstream, baseWsId, baseRepo } = await seed(root, { 'base.md': 'base\n' });
|
|
341
|
+
await pushFeatureBranch(root, upstream, 'alice/add', async (dir) => {
|
|
342
|
+
await fs.writeFile(path.join(dir, 'feature.md'), 'new content\n');
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
const git = new GitService(stubWorkspaceService(baseWsId, baseRepo), new WorkflowHooks(), testKbContext());
|
|
346
|
+
const merged = await git.mergeChangeRequest(
|
|
347
|
+
baseWsId, 'alice/add', BASE,
|
|
348
|
+
{ subject: mergeCommitSubject('Add feature', 7), body: 'Merged via Bevel' }, USER,
|
|
349
|
+
{ appliedChangeNumber: 7 },
|
|
350
|
+
);
|
|
351
|
+
expect(merged.kind).toBe('merged');
|
|
352
|
+
if (merged.kind !== 'merged') return;
|
|
353
|
+
// The commit IS in this clone, and origin/BASE names it.
|
|
354
|
+
expect((await gitOut(baseRepo, ['rev-parse', `origin/${BASE}`])).trim()).toBe(merged.mergeCommit);
|
|
355
|
+
|
|
356
|
+
// Variable 1: no tracking ref, remote reachable. The refresh brings it back.
|
|
357
|
+
await runGit(baseRepo, ['update-ref', '-d', `refs/remotes/origin/${BASE}`]);
|
|
358
|
+
expect(await git.appliedMergeCommitOnTarget(baseWsId, BASE, 7)).toBe(merged.mergeCommit);
|
|
359
|
+
expect((await gitOut(baseRepo, ['rev-parse', `origin/${BASE}`])).trim()).toBe(merged.mergeCommit);
|
|
360
|
+
|
|
361
|
+
// Variable 2: tracking ref present and naming the commit, remote gone. No
|
|
362
|
+
// authority, no answer — the ref is not scanned.
|
|
363
|
+
await runGit(baseRepo, ['remote', 'set-url', 'origin', path.join(root, 'gone.git')]);
|
|
364
|
+
expect(await git.appliedMergeCommitOnTarget(baseWsId, BASE, 7)).toBeNull();
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
// The merge commit a later attempt recovers is matched on the number alone, so
|
|
368
|
+
// a request that landed a DIFFERENT change under its own number is not offered
|
|
369
|
+
// another request's commit — and `git log --grep` finding the number in a
|
|
370
|
+
// BODY is not enough either, since the subject check still has to pass.
|
|
371
|
+
it('does not mistake a mention of the number in a merge body for the request\'s own commit', async () => {
|
|
372
|
+
const { upstream, baseWsId, baseRepo } = await seed(root, { 'base.md': 'base\n' });
|
|
373
|
+
await pushFeatureBranch(root, upstream, 'bob/first', async (dir) => {
|
|
374
|
+
await fs.writeFile(path.join(dir, 'first.md'), 'first\n');
|
|
375
|
+
});
|
|
376
|
+
const git = new GitService(stubWorkspaceService(baseWsId, baseRepo), new WorkflowHooks(), testKbContext());
|
|
377
|
+
// #1's merge commit, whose BODY mentions #9 — the shape `--grep` matches and
|
|
378
|
+
// the subject check must reject.
|
|
379
|
+
await git.mergeChangeRequest(
|
|
380
|
+
baseWsId, 'bob/first', BASE,
|
|
381
|
+
{ subject: mergeCommitSubject('First', 1), body: 'Supersedes the work in (#9)' }, USER,
|
|
382
|
+
{ appliedChangeNumber: 1 },
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
const emptyDir = path.join(root, 'feat-empty-9');
|
|
386
|
+
await runGit(root, ['clone', '-b', BASE, upstream, emptyDir]);
|
|
387
|
+
await runGit(emptyDir, ['checkout', '-b', 'alice/empty']);
|
|
388
|
+
await runGit(emptyDir, ['push', '-u', 'origin', 'alice/empty']);
|
|
389
|
+
|
|
390
|
+
const result = await git.mergeChangeRequest(
|
|
391
|
+
baseWsId, 'alice/empty', BASE, { subject: mergeCommitSubject('Nothing', 9), body: 'x' }, USER,
|
|
392
|
+
{ appliedChangeNumber: 9 },
|
|
393
|
+
);
|
|
394
|
+
expect(result.kind).toBe('merged');
|
|
395
|
+
if (result.kind !== 'merged') return;
|
|
396
|
+
expect(result.mergeCommit).toBeNull();
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* `git commit -m` takes the FIRST PARAGRAPH of the message as the subject, so a
|
|
401
|
+
* blank line inside it pushes `(#N)` into the body — where `%s` never reports
|
|
402
|
+
* it and the applied request's reader cannot find it (cubic P2 on #347).
|
|
403
|
+
* `mergeCommitSubject` flattens the title it builds from; this is the guard
|
|
404
|
+
* against any other caller reintroducing the break.
|
|
405
|
+
*/
|
|
406
|
+
it('refuses a subject that is more than one paragraph, before touching the clone', async () => {
|
|
407
|
+
const { upstream, baseWsId, baseRepo } = await seed(root, { 'base.md': 'base\n' });
|
|
408
|
+
await pushFeatureBranch(root, upstream, 'alice/add', async (dir) => {
|
|
409
|
+
await fs.writeFile(path.join(dir, 'feature.md'), 'new content\n');
|
|
410
|
+
});
|
|
411
|
+
const git = new GitService(stubWorkspaceService(baseWsId, baseRepo), new WorkflowHooks(), testKbContext());
|
|
412
|
+
await expect(
|
|
413
|
+
git.mergeChangeRequest(
|
|
414
|
+
baseWsId, 'alice/add', BASE, { subject: 'Add feature\n\nand a note (#3)', body: 'x' }, USER,
|
|
415
|
+
),
|
|
416
|
+
).rejects.toThrow(/single paragraph/);
|
|
417
|
+
|
|
418
|
+
// Nothing was merged or pushed.
|
|
419
|
+
const verify = path.join(root, 'verify-subject');
|
|
420
|
+
await runGit(root, ['clone', '-b', BASE, upstream, verify]);
|
|
421
|
+
await expect(fs.readFile(path.join(verify, 'feature.md'), 'utf8')).rejects.toThrow();
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
// A title with a blank line in it reaches here (titles are stored `.trim()`-ed
|
|
425
|
+
// and the tool schema bounds only their length), and the whole read-back of an
|
|
426
|
+
// applied request hangs on the number being ON the subject git reports.
|
|
427
|
+
it('keeps the number on the subject when the title itself spans paragraphs', async () => {
|
|
428
|
+
const { upstream, baseWsId, baseRepo } = await seed(root, { 'base.md': 'base\n' });
|
|
429
|
+
await pushFeatureBranch(root, upstream, 'alice/add', async (dir) => {
|
|
430
|
+
await fs.writeFile(path.join(dir, 'feature.md'), 'new content\n');
|
|
431
|
+
});
|
|
432
|
+
const git = new GitService(stubWorkspaceService(baseWsId, baseRepo), new WorkflowHooks(), testKbContext());
|
|
433
|
+
const result = await git.mergeChangeRequest(
|
|
434
|
+
baseWsId, 'alice/add', BASE,
|
|
435
|
+
{ subject: mergeCommitSubject('Add feature\n\nand a note', 4), body: 'x' }, USER,
|
|
436
|
+
{ appliedChangeNumber: 4 },
|
|
437
|
+
);
|
|
438
|
+
expect(result.kind).toBe('merged');
|
|
439
|
+
if (result.kind !== 'merged') return;
|
|
440
|
+
|
|
441
|
+
const verify = path.join(root, 'verify-flat');
|
|
442
|
+
await runGit(root, ['clone', '-b', BASE, upstream, verify]);
|
|
443
|
+
expect((await gitOut(verify, ['log', '-1', '--format=%s'])).trim())
|
|
444
|
+
.toBe('Add feature and a note (#4)');
|
|
445
|
+
// Which is what lets the request be recognised as the owner of this commit:
|
|
446
|
+
// the same read that would otherwise refuse it as "not the merge commit of
|
|
447
|
+
// change request #4" and leave the applied request fileless.
|
|
448
|
+
expect(await git.appliedChangeShas(baseWsId, { number: 4, mergeSha: result.sha }))
|
|
449
|
+
.toMatchObject({ baseSha: expect.any(String), headSha: expect.any(String) });
|
|
450
|
+
});
|
|
451
|
+
|
|
118
452
|
it('returns the conflicting paths when base and source both changed a file', async () => {
|
|
119
453
|
const { upstream, baseWsId, baseRepo } = await seed(root, { 'shared.md': 'original\n' });
|
|
120
454
|
// Source edits shared.md one way…
|