@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
|
@@ -2,13 +2,14 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { logger } from '../../../shared/logging.js';
|
|
4
4
|
import { isFolderPlaceholder } from '@bevel-software/platform-shared';
|
|
5
|
+
import { mergeCommitMessageNames } from './merge-commit.js';
|
|
5
6
|
import { AccessDeniedError } from '../../access-model/access-errors.js';
|
|
6
7
|
import { WorkspaceMutex } from '../../kb-fs/mutex.js';
|
|
7
8
|
import { isAbsence } from '../../../shared/fs.contract.js';
|
|
8
9
|
import { assertInsideRepo } from '../../kb-fs/repo-path.js';
|
|
9
10
|
import { cloneTrackingConfigArgs, SAFE_IMPLICIT_FETCH_ARGS } from '../../kb-fs/clone-config.js';
|
|
10
11
|
import { assertValidBranchName, assertValidRelativePath, isBranchAuthoredBy, isOwnSuggestionsBranch, } from '../../kb-fs/branch-name.js';
|
|
11
|
-
import { BranchAuthorshipError, WorkflowDomainError, WorkflowValidationError, ProtectedBranchError, PullRebaseConflictError, RemoteBranchGoneError, VersionNotOnBranchError, isMissingRemoteBranchFailure, } from '../../../shared/domain-errors.js';
|
|
12
|
+
import { AppliedChangeMismatchError, BranchAuthorshipError, BranchDeleteRefusedError, WorkflowDomainError, WorkflowValidationError, ProtectedBranchError, PullRebaseConflictError, RemoteBranchGoneError, VersionNotOnBranchError, isMissingRemoteBranchFailure, } from '../../../shared/domain-errors.js';
|
|
12
13
|
import { GitRunError, assertOneSpecPerLine, isGitTimeout, redactGitToken, } from '../../../shared/git.contract.js';
|
|
13
14
|
import { NodeGitRunner } from './node-git-runner.js';
|
|
14
15
|
import { printable } from '../../../shared/printable.js';
|
|
@@ -648,6 +649,16 @@ export class GitService {
|
|
|
648
649
|
// - The diff-backup reseed that used to fire here is unnecessary: a
|
|
649
650
|
// fresh per-branch workspace gets its backup seeded by the workspace
|
|
650
651
|
// bootstrap path, not by a branch switch on an existing one.
|
|
652
|
+
/**
|
|
653
|
+
* Delete a branch, here and on origin. Answers the tip it had — origin's
|
|
654
|
+
* when origin had it, else the local head's; null when neither existed —
|
|
655
|
+
* so the caller can say which commit a restore would start from.
|
|
656
|
+
*
|
|
657
|
+
* `expectTip`, when given, is the tip the caller's checks were made
|
|
658
|
+
* against: the remote delete is then leased on it, and a branch that moved
|
|
659
|
+
* since (a colleague pushed) is refused by the host rather than deleted
|
|
660
|
+
* with work nobody looked at.
|
|
661
|
+
*/
|
|
651
662
|
async deleteBranch(workspaceId, name, user, opts = {}) {
|
|
652
663
|
assertValidBranchName(name);
|
|
653
664
|
if (this.kb.isProtectedBranch(name)) {
|
|
@@ -674,27 +685,33 @@ export class GitService {
|
|
|
674
685
|
throw new WorkflowValidationError(`Branch "${name}" still exists on origin — refusing to auto-prune.`);
|
|
675
686
|
}
|
|
676
687
|
}
|
|
677
|
-
else {
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
688
|
+
else if (!(await this.mayDeleteBranch(workspaceId, name, user))) {
|
|
689
|
+
throw new BranchAuthorshipError(name);
|
|
690
|
+
}
|
|
691
|
+
// Remote delete FIRST. Skip in `onlyIfNoRemote` mode (the contract is
|
|
692
|
+
// local-only cleanup) and skip when origin has no such branch. Origin
|
|
693
|
+
// is asked, not this clone's remote-tracking ref: a branch pushed from
|
|
694
|
+
// another clone since the last fetch has no tracking ref here, and
|
|
695
|
+
// trusting the cache would delete it locally while it lives on.
|
|
696
|
+
// A refusal by the host — or a host that cannot be asked — fails the
|
|
697
|
+
// whole delete: the local branch is untouched, so nothing is
|
|
698
|
+
// half-deleted and the person can try again. Deleting locally first
|
|
699
|
+
// would leave a branch that vanished here but lives on the remote —
|
|
700
|
+
// and, unlike a refused write, a deleted branch has no later push to
|
|
701
|
+
// reconcile it.
|
|
702
|
+
let remoteTip = null;
|
|
703
|
+
if (!opts.onlyIfNoRemote) {
|
|
704
|
+
try {
|
|
705
|
+
remoteTip = await this.originBranchTip(cwd, name);
|
|
706
|
+
if (remoteTip !== null) {
|
|
707
|
+
const lease = opts.expectTip ? [`--force-with-lease=refs/heads/${name}:${opts.expectTip}`] : [];
|
|
708
|
+
await this.git(cwd, ['push', ...lease, 'origin', '--delete', name]);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
catch (err) {
|
|
712
|
+
const detail = sanitizeError(err);
|
|
713
|
+
log.warn(`the repository host refused to delete branch "${name}"; leaving it in place:`, { detail });
|
|
714
|
+
throw new BranchDeleteRefusedError(name, detail);
|
|
698
715
|
}
|
|
699
716
|
}
|
|
700
717
|
// Local delete. `-D` force-deletes even if the branch isn't "fully
|
|
@@ -705,18 +722,87 @@ export class GitService {
|
|
|
705
722
|
// Skip when no local ref exists — the picker can surface branches that
|
|
706
723
|
// exist only as `refs/remotes/origin/<name>` (the user hasn't worked on
|
|
707
724
|
// them locally), and "discard from origin" must still work for those.
|
|
725
|
+
let localTip = null;
|
|
708
726
|
if (await this.refExists(cwd, `refs/heads/${name}`)) {
|
|
727
|
+
localTip = await this.revParseOrNull(cwd, `refs/heads/${name}`);
|
|
709
728
|
await this.git(cwd, ['branch', '-D', name]);
|
|
710
729
|
}
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
730
|
+
return { lastCommit: remoteTip ?? localTip };
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* The author-or-Admin rule for deleting `name`. The branch's author (per
|
|
735
|
+
* the `<email-localpart>/...` naming convention — see `isBranchAuthoredBy`)
|
|
736
|
+
* can always delete their own draft. Admins (per the workspace's
|
|
737
|
+
* `roles.yaml`) can additionally clean up anyone's branch, including
|
|
738
|
+
* unprefixed CLI branches like `fix/...` that have no recognisable author.
|
|
739
|
+
* Admin is detected via `canWrite(_, _, 'roles.yaml')` because that
|
|
740
|
+
* predicate is the existing source of truth for `Admin`-role membership
|
|
741
|
+
* inside the access model — no separate `isAdmin()` plumbing needed.
|
|
742
|
+
*
|
|
743
|
+
* Two authorship conventions: `<localpart>/…` UI drafts, and the
|
|
744
|
+
* `suggestions/<who>-<id8>/…` namespace the propose flow files its branches
|
|
745
|
+
* under — a user resetting THEIR OWN suggestion bundle is deleting their
|
|
746
|
+
* own branch.
|
|
747
|
+
*/
|
|
748
|
+
async mayDeleteBranch(workspaceId, name, user) {
|
|
749
|
+
const isAuthor = isBranchAuthoredBy(name, user.email) ||
|
|
750
|
+
isOwnSuggestionsBranch(name, { email: user.email, id: user.id });
|
|
751
|
+
if (isAuthor)
|
|
752
|
+
return true;
|
|
753
|
+
return this.accessControl
|
|
754
|
+
? await this.accessControl.canWrite(workspaceId, user.email, 'roles.yaml')
|
|
755
|
+
: false;
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* Where `name` stands in this clone, read from its refs as the last fetch
|
|
759
|
+
* left them (the caller fetches first): whether it exists on origin or as a
|
|
760
|
+
* local head, its tip (origin's when it has one), and how many commits on
|
|
761
|
+
* it — on either ref — are not on `defaultBranch`.
|
|
762
|
+
*
|
|
763
|
+
* The default branch is read from origin's ref, falling back to the local
|
|
764
|
+
* head on a clone whose default was never pushed. A default that resolves
|
|
765
|
+
* on neither throws: a count against nothing would call every commit
|
|
766
|
+
* unmerged, or none.
|
|
767
|
+
*/
|
|
768
|
+
async branchState(workspaceId, name, defaultBranch) {
|
|
769
|
+
assertValidBranchName(name);
|
|
770
|
+
assertValidBranchName(defaultBranch);
|
|
771
|
+
return this.mutex.run(workspaceId, async () => {
|
|
772
|
+
const cwd = await this.repoDir(workspaceId);
|
|
773
|
+
const remote = await this.revParseOrNull(cwd, `refs/remotes/origin/${name}`);
|
|
774
|
+
const local = await this.revParseOrNull(cwd, `refs/heads/${name}`);
|
|
775
|
+
if (remote === null && local === null)
|
|
776
|
+
return { exists: false, lastCommit: null, unmergedCommits: 0 };
|
|
777
|
+
const base = (await this.revParseOrNull(cwd, `refs/remotes/origin/${defaultBranch}`)) ??
|
|
778
|
+
(await this.revParseOrNull(cwd, `refs/heads/${defaultBranch}`));
|
|
779
|
+
if (base === null) {
|
|
780
|
+
throw new Error(`the default branch "${defaultBranch}" does not resolve in this clone`);
|
|
781
|
+
}
|
|
782
|
+
const tips = [remote, local].filter((sha) => sha !== null);
|
|
783
|
+
const { stdout } = await this.git(cwd, ['rev-list', '--count', ...tips, `^${base}`]);
|
|
784
|
+
return { exists: true, lastCommit: remote ?? local, unmergedCommits: Number(stdout.trim()) || 0 };
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
/**
|
|
788
|
+
* Whether commit `ancestor` is contained in commit `descendant`. Exit 1 is
|
|
789
|
+
* git's "no"; anything else — a commit this clone does not have — throws,
|
|
790
|
+
* so a caller deciding to delete on a yes never gets one by accident.
|
|
791
|
+
*/
|
|
792
|
+
async isAncestor(workspaceId, ancestor, descendant) {
|
|
793
|
+
const sha = /^[0-9a-f]{4,64}$/i;
|
|
794
|
+
if (!sha.test(ancestor) || !sha.test(descendant))
|
|
795
|
+
throw new Error('isAncestor takes commit ids');
|
|
796
|
+
return this.mutex.run(workspaceId, async () => {
|
|
797
|
+
const cwd = await this.repoDir(workspaceId);
|
|
798
|
+
try {
|
|
799
|
+
await this.git(cwd, ['merge-base', '--is-ancestor', ancestor, descendant]);
|
|
800
|
+
return true;
|
|
801
|
+
}
|
|
802
|
+
catch (err) {
|
|
803
|
+
if (err instanceof GitRunError && !err.timedOut && err.exitCode === 1)
|
|
804
|
+
return false;
|
|
805
|
+
throw err;
|
|
720
806
|
}
|
|
721
807
|
});
|
|
722
808
|
}
|
|
@@ -1250,12 +1336,23 @@ export class GitService {
|
|
|
1250
1336
|
* the new base and re-merge on top of it. Conflicts are deterministic — they
|
|
1251
1337
|
* return immediately (no retry) so the caller can route into resolution.
|
|
1252
1338
|
*
|
|
1253
|
-
* Returns the
|
|
1339
|
+
* Returns the state the target is left at (`sha`) together with the merge
|
|
1340
|
+
* commit this change request owns (`mergeCommit`, null when there is none), or
|
|
1341
|
+
* the conflicting paths.
|
|
1254
1342
|
*/
|
|
1255
1343
|
async mergeChangeRequest(baseWorkspaceId, sourceBranch, targetBranch, commit, user, opts = {}) {
|
|
1256
1344
|
assertValidBranchName(sourceBranch);
|
|
1257
1345
|
assertValidBranchName(targetBranch);
|
|
1258
1346
|
assertValidAuthor(user);
|
|
1347
|
+
// The same single-line rule `commit` applies, and for a sharper reason here:
|
|
1348
|
+
// `git commit -m` takes the first PARAGRAPH of this string as the subject, so
|
|
1349
|
+
// a blank line inside it would silently push everything after the break into
|
|
1350
|
+
// the body — including the `(#N)` an applied request is read back by.
|
|
1351
|
+
// `mergeCommitSubject` already flattens the title it builds from; this is the
|
|
1352
|
+
// guard that keeps any other caller from reintroducing the break.
|
|
1353
|
+
if (/\n\s*\n/.test(commit.subject)) {
|
|
1354
|
+
throw new WorkflowValidationError('merge commit subject must be a single paragraph');
|
|
1355
|
+
}
|
|
1259
1356
|
const MAX_ATTEMPTS = 3;
|
|
1260
1357
|
return this.mutex.run(baseWorkspaceId, async () => {
|
|
1261
1358
|
const cwd = await this.repoDir(baseWorkspaceId);
|
|
@@ -1320,13 +1417,28 @@ export class GitService {
|
|
|
1320
1417
|
// (e.g. missing identity, unrelated histories) is diagnosable.
|
|
1321
1418
|
throw new Error(`git merge failed without detectable conflicts: ${redactGitToken(err instanceof Error ? err.message : String(err), this.credentials.token())}`);
|
|
1322
1419
|
}
|
|
1323
|
-
// Nothing staged ⇒
|
|
1324
|
-
// is the "merged" state; report it without an empty commit
|
|
1420
|
+
// Nothing staged ⇒ the target already contains the source. The target tip
|
|
1421
|
+
// is the "merged" state; report it without an empty commit — but NOT as
|
|
1422
|
+
// this request's own merge commit. It is whatever landed on the target
|
|
1423
|
+
// last, usually another request's merge commit, and a reader that took it
|
|
1424
|
+
// for this request's would answer with that other request's files under
|
|
1425
|
+
// this number (cubic P1 on #347).
|
|
1426
|
+
//
|
|
1427
|
+
// There are two ways to arrive here, and they differ in exactly one
|
|
1428
|
+
// thing: whether this request has a merge commit on the target already.
|
|
1429
|
+
// An EMPTY request never had anything to land and has none. A request
|
|
1430
|
+
// whose previous attempt pushed its merge commit and then failed to
|
|
1431
|
+
// finalize the row (a transient database fault between the push and the
|
|
1432
|
+
// update) has one, and it is the only place its file list survives — so
|
|
1433
|
+
// it is looked for rather than assumed absent (cubic P2 on #347).
|
|
1325
1434
|
const { stdout: staged } = await this.git(cwd, ['diff', '--cached', '--name-only']);
|
|
1326
1435
|
if (staged.trim() === '') {
|
|
1327
1436
|
const { stdout: sha } = await this.git(cwd, ['rev-parse', 'HEAD']);
|
|
1328
1437
|
await this.git(cwd, ['merge', '--abort']).catch(() => undefined);
|
|
1329
|
-
|
|
1438
|
+
const own = opts.appliedChangeNumber === undefined
|
|
1439
|
+
? null
|
|
1440
|
+
: await this.ownMergeCommitOn(cwd, `origin/${targetBranch}`, opts.appliedChangeNumber, opts.appliedChangeTitle);
|
|
1441
|
+
return { kind: 'merged', sha: sha.trim(), mergeCommit: own };
|
|
1330
1442
|
}
|
|
1331
1443
|
await this.git(cwd, [
|
|
1332
1444
|
'commit',
|
|
@@ -1338,7 +1450,8 @@ export class GitService {
|
|
|
1338
1450
|
try {
|
|
1339
1451
|
await this.git(cwd, ['push', 'origin', `HEAD:refs/heads/${targetBranch}`]);
|
|
1340
1452
|
this.accessControl?.invalidate(baseWorkspaceId);
|
|
1341
|
-
|
|
1453
|
+
// A commit of this request's own, with the subject the reader verifies.
|
|
1454
|
+
return { kind: 'merged', sha: sha.trim(), mergeCommit: sha.trim() };
|
|
1342
1455
|
}
|
|
1343
1456
|
catch (err) {
|
|
1344
1457
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -1352,6 +1465,100 @@ export class GitService {
|
|
|
1352
1465
|
throw new Error(`merge push to "${targetBranch}" kept being rejected after ${MAX_ATTEMPTS} attempts (base moving concurrently)`);
|
|
1353
1466
|
});
|
|
1354
1467
|
}
|
|
1468
|
+
/**
|
|
1469
|
+
* The merge commit change request `number` OWNS on `targetBranch`, or null when
|
|
1470
|
+
* the target carries none. Asks the published target only — no source branch,
|
|
1471
|
+
* no working tree, nothing merged, nothing written.
|
|
1472
|
+
*
|
|
1473
|
+
* This is the same question {@link mergeChangeRequest} answers for itself when
|
|
1474
|
+
* it finds nothing to merge, asked WITHOUT attempting a merge at all. The
|
|
1475
|
+
* merge path cannot be the only way to ask it: a request whose merge commit
|
|
1476
|
+
* was pushed by an attempt that then failed to record the row has an empty
|
|
1477
|
+
* diff against the target, so the approval gate refuses the retry ("no file
|
|
1478
|
+
* changes to approve") BEFORE any merge runs, and the recovery inside the
|
|
1479
|
+
* merge never gets its turn (cubic P2 on #347). The gate asks here instead.
|
|
1480
|
+
*
|
|
1481
|
+
* The target ref is refreshed first, because the attempt that pushed the
|
|
1482
|
+
* commit may have run in another process or another clone — and a fetch that
|
|
1483
|
+
* FAILS answers null without looking, which is the only safe answer: what the
|
|
1484
|
+
* caller does with a commit is record it as this request's published state, so
|
|
1485
|
+
* a decision taken on a stale tracking ref could finalize a request with a
|
|
1486
|
+
* commit the published branch no longer carries, and a clone that has no
|
|
1487
|
+
* tracking ref at all would turn the gate's clean refusal into a git error
|
|
1488
|
+
* (cubic P1 on #347). No authority, no answer; the caller then refuses exactly
|
|
1489
|
+
* as it did before this method existed.
|
|
1490
|
+
*/
|
|
1491
|
+
async appliedMergeCommitOnTarget(baseWorkspaceId, targetBranch, number, title) {
|
|
1492
|
+
assertValidBranchName(targetBranch);
|
|
1493
|
+
const cwd = await this.repoDir(baseWorkspaceId);
|
|
1494
|
+
// The fetch runs outside the workspace mutex, the way the implicit and
|
|
1495
|
+
// per-request fetches do (see `fetchLocks`, `fetchPrRefs`): it writes only
|
|
1496
|
+
// `refs/remotes/origin/*` and new objects, never HEAD, the index or the
|
|
1497
|
+
// working tree, so nothing it does needs serializing against a checkout or
|
|
1498
|
+
// a commit, and holding the mutex through a network round trip would park
|
|
1499
|
+
// every other git op behind the origin. (`mergeChangeRequest` fetches under
|
|
1500
|
+
// the mutex for a reason this method lacks: its fetch is one step of a
|
|
1501
|
+
// merge whose working tree must not move between the fetch and the push.)
|
|
1502
|
+
// The read that follows is a local one, and takes the mutex like
|
|
1503
|
+
// `appliedChangeShas` does.
|
|
1504
|
+
const refreshed = await this.git(cwd, [
|
|
1505
|
+
'fetch', '--no-write-fetch-head', 'origin',
|
|
1506
|
+
`+refs/heads/${targetBranch}:refs/remotes/origin/${targetBranch}`,
|
|
1507
|
+
]).then(() => true, () => false);
|
|
1508
|
+
if (!refreshed) {
|
|
1509
|
+
log.warn(`could not refresh "${targetBranch}" to look for change request #${number}'s merge commit, ` +
|
|
1510
|
+
'so it is reported as owning none.');
|
|
1511
|
+
return null;
|
|
1512
|
+
}
|
|
1513
|
+
return this.mutex.run(baseWorkspaceId, () => this.ownMergeCommitOn(cwd, `origin/${targetBranch}`, number, title));
|
|
1514
|
+
}
|
|
1515
|
+
/**
|
|
1516
|
+
* The newest merge commit reachable from `targetRef` that is change request
|
|
1517
|
+
* `number`'s OWN, or null if the target carries none.
|
|
1518
|
+
*
|
|
1519
|
+
* Asked when a merge finds nothing to merge, to tell "this request never had
|
|
1520
|
+
* anything to land" apart from "a previous attempt already landed it". The
|
|
1521
|
+
* second happens when the push succeeded and the row update did not — the
|
|
1522
|
+
* merge commit is on the target, and if this attempt then records no sha the
|
|
1523
|
+
* request becomes permanently fileless, readable by its author alone, with the
|
|
1524
|
+
* commit that holds its files still sitting there unreferenced (cubic P2 on
|
|
1525
|
+
* #347).
|
|
1526
|
+
*
|
|
1527
|
+
* The two conditions are the reader's own (`appliedChangeShas`): a merge
|
|
1528
|
+
* commit, whose subject names this number. Deciding it here with the same
|
|
1529
|
+
* predicate means the writer records only what the reader will accept. The
|
|
1530
|
+
* walk is bounded by `--grep` (the number, as a fixed string), which is the
|
|
1531
|
+
* whole of the target's history for commits mentioning the number — and not
|
|
1532
|
+
* by a count: a fixed count of newer merges once hid a stuck request's commit
|
|
1533
|
+
* from the probe on a busy target, and a request that could no longer be
|
|
1534
|
+
* finalized is worse than a log walk. The message check then confirms a
|
|
1535
|
+
* body-only mention is not the request's own. Never on an ordinary merge:
|
|
1536
|
+
* the two callers are the empty-merge case here and
|
|
1537
|
+
* {@link appliedMergeCommitOnTarget}, which the approval gate asks before it
|
|
1538
|
+
* refuses a retry that has nothing left to merge.
|
|
1539
|
+
*
|
|
1540
|
+
* `title` lets the old message format be recognised — see
|
|
1541
|
+
* {@link mergeCommitMessageNames}.
|
|
1542
|
+
*/
|
|
1543
|
+
async ownMergeCommitOn(cwd, targetRef, number, title) {
|
|
1544
|
+
// The whole message per commit. Records are NUL-separated (`-z`), the one
|
|
1545
|
+
// byte a commit message cannot hold, and the sha is the record's first
|
|
1546
|
+
// line: nothing a title may contain can split a record or a line early.
|
|
1547
|
+
const { stdout } = await this.git(cwd, [
|
|
1548
|
+
'log', '-z', '--merges', '--fixed-strings', `--grep=(#${number})`,
|
|
1549
|
+
'--format=%H%n%B', targetRef,
|
|
1550
|
+
]);
|
|
1551
|
+
for (const record of stdout.split('\0')) {
|
|
1552
|
+
const newline = record.indexOf('\n');
|
|
1553
|
+
const sha = (newline === -1 ? record : record.slice(0, newline)).trim();
|
|
1554
|
+
if (!/^[0-9a-f]{40,64}$/.test(sha))
|
|
1555
|
+
continue;
|
|
1556
|
+
const message = newline === -1 ? '' : record.slice(newline + 1);
|
|
1557
|
+
if (mergeCommitMessageNames(message, number, title))
|
|
1558
|
+
return sha;
|
|
1559
|
+
}
|
|
1560
|
+
return null;
|
|
1561
|
+
}
|
|
1355
1562
|
/** Conflicted paths from a half-done merge, parsed from porcelain status. */
|
|
1356
1563
|
async conflictedPaths(cwd) {
|
|
1357
1564
|
const { stdout } = await this.git(cwd, ['status', '--porcelain=v1']);
|
|
@@ -1466,7 +1673,21 @@ export class GitService {
|
|
|
1466
1673
|
*/
|
|
1467
1674
|
async remoteBranchExists(workspaceId, branch) {
|
|
1468
1675
|
assertValidBranchName(branch);
|
|
1469
|
-
|
|
1676
|
+
return this.originHasBranch(await this.repoDir(workspaceId), branch);
|
|
1677
|
+
}
|
|
1678
|
+
/** Origin's tip of `branch`, asked of origin itself; null when it has no such branch. */
|
|
1679
|
+
async originBranchTip(cwd, branch) {
|
|
1680
|
+
try {
|
|
1681
|
+
const { stdout } = await this.git(cwd, ['ls-remote', '--exit-code', '--heads', 'origin', `refs/heads/${branch}`]);
|
|
1682
|
+
return /^([0-9a-f]+)\s/.exec(stdout)?.[1] ?? null;
|
|
1683
|
+
}
|
|
1684
|
+
catch (err) {
|
|
1685
|
+
if (err.exitCode === 2)
|
|
1686
|
+
return null;
|
|
1687
|
+
throw err;
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
async originHasBranch(cwd, branch) {
|
|
1470
1691
|
try {
|
|
1471
1692
|
await this.git(cwd, ['ls-remote', '--exit-code', '--heads', 'origin', `refs/heads/${branch}`]);
|
|
1472
1693
|
return true;
|
|
@@ -1554,9 +1775,10 @@ export class GitService {
|
|
|
1554
1775
|
// commit the clone holds but origin has not seen into cherry-picks of
|
|
1555
1776
|
// the commits it merged — the merged branch stops being a parent, so
|
|
1556
1777
|
// "does the published head contain the target's head" answers no
|
|
1557
|
-
// however many times the update runs.
|
|
1558
|
-
//
|
|
1559
|
-
//
|
|
1778
|
+
// however many times the update runs. The change-request update and
|
|
1779
|
+
// every push's cooperative recovery ask for it (a merge a refused push
|
|
1780
|
+
// stranded rides along with the branch's next push); with no merge
|
|
1781
|
+
// among the local commits the two spellings produce the same history.
|
|
1560
1782
|
await this.git(cwd, [
|
|
1561
1783
|
'rebase',
|
|
1562
1784
|
'--autostash',
|
|
@@ -1671,7 +1893,12 @@ export class GitService {
|
|
|
1671
1893
|
}
|
|
1672
1894
|
throw err;
|
|
1673
1895
|
}
|
|
1674
|
-
|
|
1896
|
+
// `preserveMerges` here too: this sync runs on a timer, so it is the
|
|
1897
|
+
// pull most likely to reach a branch while a merge a refused open or
|
|
1898
|
+
// update left local is still waiting for its push. A plain rebase would
|
|
1899
|
+
// flatten that merge before the push ever ran, and the branch would be
|
|
1900
|
+
// "behind" its target again however many times it was updated.
|
|
1901
|
+
await this.rebaseOntoRemote(cwd, branch, remoteRef, { preserveMerges: true });
|
|
1675
1902
|
this.accessControl?.invalidate(workspaceId);
|
|
1676
1903
|
const after = await this.revParseOrNull(cwd, 'HEAD');
|
|
1677
1904
|
const treeAfter = (await this.revParseOrNull(cwd, 'HEAD^{tree}')) ?? '';
|
|
@@ -1723,6 +1950,37 @@ export class GitService {
|
|
|
1723
1950
|
async hasUnpushedCommits(workspaceId) {
|
|
1724
1951
|
return this.mutex.run(workspaceId, async () => this.hasUnpushedCommitsAt(await this.repoDir(workspaceId)));
|
|
1725
1952
|
}
|
|
1953
|
+
/**
|
|
1954
|
+
* Whether this clone's HEAD already holds everything origin has on its
|
|
1955
|
+
* branch: a push would then fast-forward origin to HEAD, and no cooperative
|
|
1956
|
+
* pull-rebase would replay origin's commits INTO HEAD first. Asked after a
|
|
1957
|
+
* fetch, about the fetched ref.
|
|
1958
|
+
*
|
|
1959
|
+
* The roles.yaml preservation asks this before pushing a restore it finds
|
|
1960
|
+
* already committed. "HEAD's roles.yaml is the base version" is only worth
|
|
1961
|
+
* publishing when origin ends up AT HEAD — a clone behind origin would have
|
|
1962
|
+
* origin's divergent copy rebased into it on the way, and the push would
|
|
1963
|
+
* land exactly what the restore exists to keep off the base.
|
|
1964
|
+
*
|
|
1965
|
+
* Exit 1 is git's "not an ancestor". Every other failure propagates — a
|
|
1966
|
+
* tracking ref that does not exist, a broken repository — so a probe that
|
|
1967
|
+
* cannot answer fails the guard closed, never open.
|
|
1968
|
+
*/
|
|
1969
|
+
async headContainsOrigin(workspaceId) {
|
|
1970
|
+
return this.mutex.run(workspaceId, async () => {
|
|
1971
|
+
const cwd = await this.repoDir(workspaceId);
|
|
1972
|
+
const branch = await this.currentBranch(cwd);
|
|
1973
|
+
try {
|
|
1974
|
+
await this.git(cwd, ['merge-base', '--is-ancestor', `refs/remotes/origin/${branch}`, 'HEAD']);
|
|
1975
|
+
return true;
|
|
1976
|
+
}
|
|
1977
|
+
catch (err) {
|
|
1978
|
+
if (err instanceof GitRunError && !err.timedOut && err.exitCode === 1)
|
|
1979
|
+
return false;
|
|
1980
|
+
throw err;
|
|
1981
|
+
}
|
|
1982
|
+
});
|
|
1983
|
+
}
|
|
1726
1984
|
/**
|
|
1727
1985
|
* `hasUnpushedCommits` without the workspace reservation — for a caller that
|
|
1728
1986
|
* already holds one (`mergeChangeRequest`, which has to ask inside the same
|
|
@@ -2216,70 +2474,234 @@ export class GitService {
|
|
|
2216
2474
|
return this.mutex.run(workspaceId, async () => {
|
|
2217
2475
|
const baseRef = opts.at ? opts.at.baseSha : await this.resolvePublishedBranchRef(cwd, baseBranch);
|
|
2218
2476
|
const headRef = opts.at ? opts.at.headSha : await this.resolvePublishedBranchRef(cwd, headBranch);
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2477
|
+
// three-dot = changes on head since merge-base
|
|
2478
|
+
return this.prFilesForRange(cwd, `${baseRef}...${headRef}`, patchCap);
|
|
2479
|
+
});
|
|
2480
|
+
}
|
|
2481
|
+
/**
|
|
2482
|
+
* The changed-file list over one already-decided diff `range`, with the
|
|
2483
|
+
* statuses, the +/- counts and up to `patchCap` patches — everything
|
|
2484
|
+
* `changedFilesForPr` does once it knows which two commits it is comparing.
|
|
2485
|
+
*
|
|
2486
|
+
* Separated from the ref resolution because a change request has a second
|
|
2487
|
+
* diff that means exactly the same thing and resolves nothing: the one a
|
|
2488
|
+
* MERGE COMMIT holds, read against its first parent. Both must filter
|
|
2489
|
+
* `roles.yaml` and the folder placeholder the same way and zip the two
|
|
2490
|
+
* `--name-status` / `--numstat` lists by the same index, or a merged request
|
|
2491
|
+
* would answer a different file list from the one it answered while open.
|
|
2492
|
+
*
|
|
2493
|
+
* The caller holds the workspace reservation; this takes none.
|
|
2494
|
+
*/
|
|
2495
|
+
async prFilesForRange(cwd, range, patchCap) {
|
|
2496
|
+
const [{ stdout: nameStatusOut }, { stdout: numstatOut }] = await Promise.all([
|
|
2497
|
+
this.git(cwd, ['diff', '-M', '-z', '--name-status', range]),
|
|
2498
|
+
this.git(cwd, ['diff', '-M', '-z', '--numstat', range]),
|
|
2499
|
+
]);
|
|
2500
|
+
let statuses = parseNameStatusZ(nameStatusOut);
|
|
2501
|
+
let counts = parseNumstatZ(numstatOut);
|
|
2502
|
+
// roles.yaml can NEVER change through a merge — `preserveBaseRolesYaml`
|
|
2503
|
+
// restores the base copy onto the source branch before every merge — so
|
|
2504
|
+
// listing it as "changed" claims something the merge will not do, and
|
|
2505
|
+
// (worse) makes its approval a requirement for a change that cannot
|
|
2506
|
+
// land. Filter it from the review surface entirely; the neutralisation
|
|
2507
|
+
// reads the raw refs itself and is unaffected. Both lists are filtered
|
|
2508
|
+
// IN STEP so the index-zip below stays aligned.
|
|
2509
|
+
// The empty-folder placeholder is filtered the same way: it is never
|
|
2510
|
+
// content, so it is not a file to review or approve. It still merges
|
|
2511
|
+
// with the rest, and `changedPathsForPr` keeps it, so a request that
|
|
2512
|
+
// only creates a folder is not mistaken for an empty one and closed.
|
|
2513
|
+
// An empty file deleted as its folder gets the (equally empty)
|
|
2514
|
+
// placeholder reads to `-M` as a RENAME onto it; the real side of such
|
|
2515
|
+
// a pair is first made the plain removal (or addition) it is, so
|
|
2516
|
+
// dropping the placeholder never drops the file with it.
|
|
2517
|
+
statuses = statuses.map(withoutPlaceholderRename);
|
|
2518
|
+
if (statuses.some((s) => s.path === 'roles.yaml' || isFolderPlaceholder(s.path))) {
|
|
2519
|
+
const keep = statuses.map((s) => s.path !== 'roles.yaml' && !isFolderPlaceholder(s.path));
|
|
2520
|
+
statuses = statuses.filter((_, i) => keep[i]);
|
|
2521
|
+
if (counts.length === keep.length)
|
|
2522
|
+
counts = counts.filter((_, i) => keep[i]);
|
|
2523
|
+
}
|
|
2524
|
+
// `--name-status` and `--numstat` enumerate the same files in the same
|
|
2525
|
+
// order (same `-M` over the same range), so we zip by index. If the two
|
|
2526
|
+
// ever disagree in length, the index alignment is unsafe — fall back to
|
|
2527
|
+
// zeroed counts (statuses/paths stay correct) rather than pin the wrong
|
|
2528
|
+
// +/- to a file.
|
|
2529
|
+
const aligned = counts.length === statuses.length;
|
|
2530
|
+
if (!aligned) {
|
|
2531
|
+
crLog.warn(`diff name-status/numstat length mismatch (${statuses.length} vs ${counts.length}) ` +
|
|
2532
|
+
`for ${range} — reporting file list without +/- counts`);
|
|
2533
|
+
}
|
|
2534
|
+
const files = statuses.map((s, i) => {
|
|
2535
|
+
const c = (aligned ? counts[i] : undefined) ?? { additions: 0, deletions: 0, isBinary: false };
|
|
2536
|
+
return {
|
|
2537
|
+
path: s.path,
|
|
2538
|
+
previousPath: s.previousPath,
|
|
2539
|
+
status: s.status,
|
|
2540
|
+
additions: c.additions,
|
|
2541
|
+
deletions: c.deletions,
|
|
2542
|
+
patch: undefined,
|
|
2543
|
+
isBinary: c.isBinary,
|
|
2544
|
+
sha: '',
|
|
2545
|
+
rawUrl: '',
|
|
2546
|
+
};
|
|
2547
|
+
});
|
|
2548
|
+
// Generate per-file patches for the first `patchCap` non-binary files.
|
|
2549
|
+
let generated = 0;
|
|
2550
|
+
for (const f of files) {
|
|
2551
|
+
if (generated >= patchCap)
|
|
2552
|
+
break;
|
|
2553
|
+
if (f.isBinary)
|
|
2554
|
+
continue;
|
|
2555
|
+
f.patch = await this.filePatchForPr(cwd, range, f);
|
|
2556
|
+
generated += 1;
|
|
2557
|
+
}
|
|
2558
|
+
return files;
|
|
2559
|
+
}
|
|
2560
|
+
/**
|
|
2561
|
+
* The FIRST PARENT of a commit, resolved — the other end of the diff one
|
|
2562
|
+
* commit introduced, and the check that this clone holds the commit at all.
|
|
2563
|
+
*
|
|
2564
|
+
* This is how an applied change request is read back. Its source branch is
|
|
2565
|
+
* retired, so there are no two branch refs left to diff; what is left is the
|
|
2566
|
+
* merge commit the row records, whose first parent is the target as it stood
|
|
2567
|
+
* before the merge and whose tree is the target with the change in it. The
|
|
2568
|
+
* diff between the two is precisely what the request applied — and it is
|
|
2569
|
+
* immutable, which the branch pair never was.
|
|
2570
|
+
*
|
|
2571
|
+
* First-parent and TWO dots, both deliberately. A three-dot diff would be
|
|
2572
|
+
* read from the merge base of the two parents, i.e. it would re-report the
|
|
2573
|
+
* source branch's whole history rather than what landed. `^1` is the target
|
|
2574
|
+
* side for a merge commit, and for a squashed or fast-forwarded commit it is
|
|
2575
|
+
* simply the commit before — the same answer either way.
|
|
2576
|
+
*
|
|
2577
|
+
* Throws `WorkflowValidationError` whenever the recorded commit cannot be
|
|
2578
|
+
* PROVEN to be this request's own merge commit — see
|
|
2579
|
+
* {@link appliedChangeShas}. Every caller reads that as "the file set could
|
|
2580
|
+
* not be resolved" and falls back to its own fail-closed answer, which is what
|
|
2581
|
+
* a clone that has not fetched the merge yet must get: no files, never
|
|
2582
|
+
* somebody else's.
|
|
2583
|
+
*/
|
|
2584
|
+
async appliedEnds(cwd, applied) {
|
|
2585
|
+
const { mergeSha, number } = applied;
|
|
2586
|
+
if (!/^[0-9a-f]{40,64}$/.test(mergeSha)) {
|
|
2587
|
+
throw new WorkflowValidationError(`invalid commit sha: ${mergeSha}`);
|
|
2588
|
+
}
|
|
2589
|
+
const revParse = async (rev) => {
|
|
2590
|
+
try {
|
|
2591
|
+
// One question, both answers: `--verify --quiet` prints what it resolved
|
|
2592
|
+
// and exits 1 if there is nothing to resolve.
|
|
2593
|
+
const { stdout } = await this.git(cwd, ['rev-parse', '--verify', '--quiet', rev]);
|
|
2594
|
+
return stdout.trim();
|
|
2281
2595
|
}
|
|
2282
|
-
|
|
2596
|
+
catch (err) {
|
|
2597
|
+
// Exit 1 under --quiet is git's own "no such object": this clone does
|
|
2598
|
+
// not have the commit, or the commit has no such parent. Anything else
|
|
2599
|
+
// (a deadline, a broken repository) is the caller's to see.
|
|
2600
|
+
if (err instanceof GitRunError && !err.timedOut && err.exitCode === 1)
|
|
2601
|
+
return null;
|
|
2602
|
+
throw err;
|
|
2603
|
+
}
|
|
2604
|
+
};
|
|
2605
|
+
// Two refusals, told apart for the caller's sake. A commit this clone does
|
|
2606
|
+
// NOT HOLD is a passing state — the next fetch may bring it — and is
|
|
2607
|
+
// reported as plain validation failure, to be asked again. A commit the
|
|
2608
|
+
// clone holds that is NOT THIS REQUEST'S merge commit (no second parent,
|
|
2609
|
+
// or a message that does not name the request) never becomes it, and is
|
|
2610
|
+
// reported as a mismatch the caller may remember.
|
|
2611
|
+
if (!(await revParse(`${mergeSha}^{commit}`))) {
|
|
2612
|
+
throw new WorkflowValidationError(`commit ${mergeSha} is not in this clone, so change request #${number} cannot be read from it yet`);
|
|
2613
|
+
}
|
|
2614
|
+
// The SECOND parent first, because its absence is the whole P1: a merge
|
|
2615
|
+
// `--no-ff` always has one, and a commit that does not is not a merge this
|
|
2616
|
+
// request made.
|
|
2617
|
+
const headSha = await revParse(`${mergeSha}^2^{commit}`);
|
|
2618
|
+
if (!headSha) {
|
|
2619
|
+
throw new AppliedChangeMismatchError(`commit ${mergeSha} is not a merge commit, so it cannot be change request #${number}'s`);
|
|
2620
|
+
}
|
|
2621
|
+
const baseSha = await revParse(`${mergeSha}^1^{commit}`);
|
|
2622
|
+
if (!baseSha) {
|
|
2623
|
+
throw new AppliedChangeMismatchError(`no first parent for commit ${mergeSha}`);
|
|
2624
|
+
}
|
|
2625
|
+
const { stdout: message } = await this.git(cwd, ['log', '-1', '--format=%B', mergeSha]);
|
|
2626
|
+
if (!mergeCommitMessageNames(message, number, applied.title)) {
|
|
2627
|
+
throw new AppliedChangeMismatchError(`commit ${mergeSha} is not the merge commit of change request #${number}`);
|
|
2628
|
+
}
|
|
2629
|
+
return { baseSha, headSha };
|
|
2630
|
+
}
|
|
2631
|
+
/**
|
|
2632
|
+
* The two commits an APPLIED change request spanned, recovered from the merge
|
|
2633
|
+
* commit its row records: the target as it stood before the merge (`^1`) and
|
|
2634
|
+
* the source tip that was merged (`^2`).
|
|
2635
|
+
*
|
|
2636
|
+
* These are what the detail of a merged request reports as its `baseSha` and
|
|
2637
|
+
* `headSha`, and the `headSha` matters beyond being informative: an approval
|
|
2638
|
+
* row stores the source head it was given against, and the detail calls an
|
|
2639
|
+
* approval stale when that sha is not the detail's `headSha`. Answering the
|
|
2640
|
+
* merge commit itself there would report every approval a merged request ever
|
|
2641
|
+
* collected as stale, and every file of it as unapproved — the opposite of
|
|
2642
|
+
* what happened.
|
|
2643
|
+
*
|
|
2644
|
+
* ## Why the commit is VERIFIED rather than taken
|
|
2645
|
+
*
|
|
2646
|
+
* `merged_sha` is not reliably a commit this request created. When the target
|
|
2647
|
+
* already contains the source there is nothing to merge, so
|
|
2648
|
+
* `mergeChangeRequest` writes no commit and reports the TARGET TIP as the
|
|
2649
|
+
* merged state — and in a deployment that lands everything through change
|
|
2650
|
+
* requests, that tip is usually ANOTHER request's merge commit. Reading "the
|
|
2651
|
+
* recorded commit's own change" then answers with somebody else's files under
|
|
2652
|
+
* this request's number: the request looks like it changed files it never
|
|
2653
|
+
* touched, and becomes visible to whoever may read THOSE (cubic P1 on #347).
|
|
2654
|
+
*
|
|
2655
|
+
* So three things must hold, and all three are cheap:
|
|
2656
|
+
*
|
|
2657
|
+
* 1. the commit is in this clone,
|
|
2658
|
+
* 2. it has a SECOND parent — `mergeChangeRequest` merges `--no-ff`, so
|
|
2659
|
+
* every merge commit it writes has one, and a commit that has none was
|
|
2660
|
+
* not written by a merge,
|
|
2661
|
+
* 3. its subject names THIS request — see `merge-commit.ts`.
|
|
2662
|
+
*
|
|
2663
|
+
* Anything else fails closed: no files, so author-only, rather than a file
|
|
2664
|
+
* list that belongs to another change. The write side no longer records a
|
|
2665
|
+
* no-op merge's sha at all, so for rows written from now on all three hold by
|
|
2666
|
+
* construction; the verification is what protects the rows written before it.
|
|
2667
|
+
*/
|
|
2668
|
+
async appliedChangeShas(workspaceId, applied) {
|
|
2669
|
+
const cwd = await this.repoDir(workspaceId);
|
|
2670
|
+
return this.mutex.run(workspaceId, async () => this.appliedEnds(cwd, applied));
|
|
2671
|
+
}
|
|
2672
|
+
/**
|
|
2673
|
+
* The changed-file list an APPLIED change request landed, read from its merge
|
|
2674
|
+
* commit against that commit's first parent, and shaped the way
|
|
2675
|
+
* `changedFilesForPr` shapes a branch pair — how an applied request is read
|
|
2676
|
+
* back, since its source branch is retired and the merge commit is what is
|
|
2677
|
+
* left of it.
|
|
2678
|
+
*
|
|
2679
|
+
* TWO dots, from the first parent. A three-dot diff would be read from the
|
|
2680
|
+
* merge base of the merge commit's two parents, i.e. it would re-report the
|
|
2681
|
+
* source branch's whole history instead of what landed on the target.
|
|
2682
|
+
*
|
|
2683
|
+
* Verified, not assumed — see {@link appliedChangeShas}. No fetch: the commit
|
|
2684
|
+
* is in this clone or this rejects.
|
|
2685
|
+
*/
|
|
2686
|
+
async changedFilesOfAppliedChange(workspaceId, applied, opts = {}) {
|
|
2687
|
+
const cwd = await this.repoDir(workspaceId);
|
|
2688
|
+
return this.mutex.run(workspaceId, async () => {
|
|
2689
|
+
const { baseSha } = await this.appliedEnds(cwd, applied);
|
|
2690
|
+
return this.prFilesForRange(cwd, `${baseSha}..${applied.mergeSha}`, opts.patchCap ?? 400);
|
|
2691
|
+
});
|
|
2692
|
+
}
|
|
2693
|
+
/**
|
|
2694
|
+
* The same applied change as the two path views a change-request SUMMARY needs
|
|
2695
|
+
* — the flat touched-path list and the rename-aware pairs — out of one
|
|
2696
|
+
* `git diff`, exactly as {@link changedPathsAndPairsForPr} does for a branch
|
|
2697
|
+
* pair. Same verification and same no-fetch contract.
|
|
2698
|
+
*/
|
|
2699
|
+
async changedPathsAndPairsOfAppliedChange(workspaceId, applied) {
|
|
2700
|
+
const cwd = await this.repoDir(workspaceId);
|
|
2701
|
+
return this.mutex.run(workspaceId, async () => {
|
|
2702
|
+
const { baseSha } = await this.appliedEnds(cwd, applied);
|
|
2703
|
+
const entries = await this.prChangedEntriesForRange(cwd, `${baseSha}..${applied.mergeSha}`);
|
|
2704
|
+
return { paths: flattenChangedPaths(entries, {}), pairs: changedPathPairs(entries) };
|
|
2283
2705
|
});
|
|
2284
2706
|
}
|
|
2285
2707
|
/**
|
|
@@ -2335,28 +2757,54 @@ export class GitService {
|
|
|
2335
2757
|
* deadlock — and where the refs are the ones the merge is actually built on.
|
|
2336
2758
|
*/
|
|
2337
2759
|
async prChangedPathsAt(cwd, baseRef, headRef, opts = {}) {
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2760
|
+
return flattenChangedPaths(await this.prChangedEntriesAt(cwd, baseRef, headRef), opts);
|
|
2761
|
+
}
|
|
2762
|
+
/**
|
|
2763
|
+
* The same diff, left as parsed ENTRIES: each changed file with the path it
|
|
2764
|
+
* came from when git detected a rename.
|
|
2765
|
+
*
|
|
2766
|
+
* The flat path list cannot express that pairing, and a caller deciding what
|
|
2767
|
+
* someone may READ needs it. A file renamed out of a folder the caller cannot
|
|
2768
|
+
* open is readable under neither of its names — the diff of a rename shows
|
|
2769
|
+
* the old side's content — and a flat list that offers both paths unlabelled
|
|
2770
|
+
* cannot say which new file the closed old path belongs to. `forAccessCheck`
|
|
2771
|
+
* solves the write side of this by taking the union; a read gate needs the
|
|
2772
|
+
* pairing itself.
|
|
2773
|
+
*/
|
|
2774
|
+
async prChangedEntriesAt(cwd, baseRef, headRef) {
|
|
2775
|
+
return this.prChangedEntriesForRange(cwd, `${baseRef}...${headRef}`);
|
|
2776
|
+
}
|
|
2777
|
+
/**
|
|
2778
|
+
* The same parsed entries over an already-decided `range`, so a merge
|
|
2779
|
+
* commit's own diff ({@link firstParentRange}) is read by the same parser as a
|
|
2780
|
+
* change request's branch pair.
|
|
2781
|
+
*/
|
|
2782
|
+
async prChangedEntriesForRange(cwd, range) {
|
|
2783
|
+
const { stdout } = await this.git(cwd, ['diff', '-M', '-z', '--name-status', range]);
|
|
2784
|
+
return parseNameStatusZ(stdout);
|
|
2785
|
+
}
|
|
2786
|
+
/**
|
|
2787
|
+
* One diff, both views of it: the flat touched-path list every existing
|
|
2788
|
+
* caller reads, and the rename-aware pairs a read gate needs. The change-
|
|
2789
|
+
* request summary builder wants both, and must not pay for two `git diff`s
|
|
2790
|
+
* to get them — it is on the list path that is polled every 60s.
|
|
2791
|
+
*/
|
|
2792
|
+
async changedPathsAndPairsForPr(workspaceId, baseBranch, headBranch, opts = {}) {
|
|
2793
|
+
assertValidBranchName(baseBranch);
|
|
2794
|
+
assertValidBranchName(headBranch);
|
|
2795
|
+
const cwd = await this.repoDir(workspaceId);
|
|
2796
|
+
// Same ref pinning and the same fetch skip as `changedPathsForPr`, for the
|
|
2797
|
+
// same reasons spelled out there.
|
|
2798
|
+
const pinned = opts.fetch === false ? await this.publishedPrCommits(cwd, baseBranch, headBranch) : null;
|
|
2799
|
+
if (!pinned) {
|
|
2800
|
+
await this.fetchPrRefs(cwd, baseBranch, headBranch);
|
|
2801
|
+
}
|
|
2802
|
+
return this.mutex.run(workspaceId, async () => {
|
|
2803
|
+
const baseRef = pinned ? pinned.base : await this.resolvePublishedBranchRef(cwd, baseBranch);
|
|
2804
|
+
const headRef = pinned ? pinned.head : await this.resolvePublishedBranchRef(cwd, headBranch);
|
|
2805
|
+
const entries = await this.prChangedEntriesAt(cwd, baseRef, headRef);
|
|
2806
|
+
return { paths: flattenChangedPaths(entries, {}), pairs: changedPathPairs(entries) };
|
|
2807
|
+
});
|
|
2360
2808
|
}
|
|
2361
2809
|
/**
|
|
2362
2810
|
* The merge-base commit of a change request's two branches (their origin
|
|
@@ -3135,6 +3583,59 @@ function mapGitStatus(letter) {
|
|
|
3135
3583
|
* Parse `git diff -M -z --name-status` output. NUL-separated tokens: each record
|
|
3136
3584
|
* is `<statusLetter>\0<path>`, or for a rename/copy `<Rxxx|Cxxx>\0<old>\0<new>`.
|
|
3137
3585
|
*/
|
|
3586
|
+
/**
|
|
3587
|
+
* The flat touched-path list of a parsed `--name-status` diff. Lifted out of
|
|
3588
|
+
* `prChangedPathsAt` unchanged, so the pair-returning reader can share one
|
|
3589
|
+
* diff with it rather than running a second.
|
|
3590
|
+
*/
|
|
3591
|
+
function flattenChangedPaths(entries, opts = {}) {
|
|
3592
|
+
// Deduped: both sides of a rename can collide with another entry's path.
|
|
3593
|
+
return [
|
|
3594
|
+
...new Set(entries
|
|
3595
|
+
// A rename onto or off the placeholder is a real file removed or
|
|
3596
|
+
// added (see `withoutPlaceholderRename`): both of its paths are
|
|
3597
|
+
// touched, or filtering the placeholder would lose the file.
|
|
3598
|
+
//
|
|
3599
|
+
// Authorizing the change needs both sides of EVERY rename, not just
|
|
3600
|
+
// the placeholder's: git reports a rename under its new name alone,
|
|
3601
|
+
// and the old name is a path the change deletes.
|
|
3602
|
+
.flatMap((s) => s.previousPath &&
|
|
3603
|
+
(opts.forAccessCheck || isFolderPlaceholder(s.path) || isFolderPlaceholder(s.previousPath))
|
|
3604
|
+
? [s.previousPath, s.path]
|
|
3605
|
+
: [s.path])
|
|
3606
|
+
// Same rule as `changedFilesForPr`: a roles.yaml change never
|
|
3607
|
+
// survives a merge, so it is not a touched path for routing or
|
|
3608
|
+
// summaries either.
|
|
3609
|
+
.filter((p) => opts.forAccessCheck || p !== 'roles.yaml')),
|
|
3610
|
+
];
|
|
3611
|
+
}
|
|
3612
|
+
/**
|
|
3613
|
+
* The changed files of a diff as read-gate pairs: every file the request
|
|
3614
|
+
* proposes, carrying the path it was renamed from when it has one.
|
|
3615
|
+
*
|
|
3616
|
+
* Normalised and filtered EXACTLY as `changedFilesForPr` does it, through the
|
|
3617
|
+
* same `withoutPlaceholderRename` and the same two drops — because this is the
|
|
3618
|
+
* file set a change-request LIST decides visibility over and that one is the
|
|
3619
|
+
* file set its DETAIL decides over, and the two answering differently is how a
|
|
3620
|
+
* list comes to advertise a request its own detail refuses (or to hide one the
|
|
3621
|
+
* detail serves). Any future change to one filter belongs in both.
|
|
3622
|
+
*/
|
|
3623
|
+
function changedPathPairs(entries) {
|
|
3624
|
+
const byPath = new Map();
|
|
3625
|
+
for (const raw of entries) {
|
|
3626
|
+
// A rename between a real file and the placeholder is a removal or an
|
|
3627
|
+
// addition of the real side; after this, the placeholder filter below
|
|
3628
|
+
// cannot take the file with it.
|
|
3629
|
+
const entry = withoutPlaceholderRename(raw);
|
|
3630
|
+
if (isFolderPlaceholder(entry.path) || entry.path === 'roles.yaml')
|
|
3631
|
+
continue;
|
|
3632
|
+
byPath.set(entry.path, {
|
|
3633
|
+
path: entry.path,
|
|
3634
|
+
...(entry.previousPath ? { previousPath: entry.previousPath } : {}),
|
|
3635
|
+
});
|
|
3636
|
+
}
|
|
3637
|
+
return [...byPath.values()];
|
|
3638
|
+
}
|
|
3138
3639
|
export function parseNameStatusZ(out) {
|
|
3139
3640
|
const tokens = out.split('\0');
|
|
3140
3641
|
const entries = [];
|