@bevel-software/platform-core-backend 0.15.1 → 0.20.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/THIRD-PARTY-NOTICES.md +4 -4
- package/dist/core/catalog-cache-invalidation.d.ts +16 -0
- package/dist/core/catalog-cache-invalidation.d.ts.map +1 -1
- package/dist/core/catalog-cache-invalidation.js +6 -1
- package/dist/core/catalog-cache-invalidation.js.map +1 -1
- package/dist/core/catalog-revision.d.ts +39 -0
- package/dist/core/catalog-revision.d.ts.map +1 -0
- package/dist/core/catalog-revision.js +94 -0
- package/dist/core/catalog-revision.js.map +1 -0
- package/dist/core/create-core-server.d.ts +7 -0
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +221 -75
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +52 -2
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +222 -87
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/core/lifecycle.d.ts +138 -0
- package/dist/core/lifecycle.d.ts.map +1 -0
- package/dist/core/lifecycle.js +218 -0
- package/dist/core/lifecycle.js.map +1 -0
- package/dist/core/public-config.d.ts +82 -0
- package/dist/core/public-config.d.ts.map +1 -0
- package/dist/core/public-config.js +78 -0
- package/dist/core/public-config.js.map +1 -0
- package/dist/core/readiness.d.ts +86 -0
- package/dist/core/readiness.d.ts.map +1 -0
- package/dist/core/readiness.js +56 -0
- package/dist/core/readiness.js.map +1 -0
- package/dist/core-config.d.ts +30 -0
- package/dist/core-config.d.ts.map +1 -1
- package/dist/core-config.js +64 -2
- package/dist/core-config.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/modules/access/access-control.interface.d.ts +215 -11
- package/dist/modules/access/access-control.interface.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +57 -11
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +616 -155
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/access/access-mutation.service.d.ts +71 -5
- package/dist/modules/access/access-mutation.service.d.ts.map +1 -1
- package/dist/modules/access/access-mutation.service.js +161 -13
- package/dist/modules/access/access-mutation.service.js.map +1 -1
- package/dist/modules/access/access-view.d.ts +147 -0
- package/dist/modules/access/access-view.d.ts.map +1 -0
- package/dist/modules/access/access-view.js +211 -0
- package/dist/modules/access/access-view.js.map +1 -0
- package/dist/modules/access/access.routes.d.ts.map +1 -1
- package/dist/modules/access/access.routes.js +255 -79
- package/dist/modules/access/access.routes.js.map +1 -1
- package/dist/modules/access/admin-locked-commit.d.ts +2 -3
- package/dist/modules/access/admin-locked-commit.d.ts.map +1 -1
- package/dist/modules/access/admin-locked-commit.js +9 -8
- package/dist/modules/access/admin-locked-commit.js.map +1 -1
- package/dist/modules/access/admin-route-helpers.d.ts.map +1 -1
- package/dist/modules/access/admin-route-helpers.js +4 -4
- package/dist/modules/access/admin-route-helpers.js.map +1 -1
- package/dist/modules/access/change-read-gate.d.ts +53 -0
- package/dist/modules/access/change-read-gate.d.ts.map +1 -0
- package/dist/modules/access/change-read-gate.js +134 -0
- package/dist/modules/access/change-read-gate.js.map +1 -0
- package/dist/modules/access/creator-access.d.ts +33 -39
- package/dist/modules/access/creator-access.d.ts.map +1 -1
- package/dist/modules/access/creator-access.js +92 -119
- package/dist/modules/access/creator-access.js.map +1 -1
- package/dist/modules/access/directory-sync-bot.d.ts.map +1 -1
- package/dist/modules/access/directory-sync-bot.js +3 -1
- package/dist/modules/access/directory-sync-bot.js.map +1 -1
- package/dist/modules/access/groups-admin.service.d.ts +3 -3
- package/dist/modules/access/groups-admin.service.js +2 -2
- package/dist/modules/access/reference-scan.d.ts +1 -1
- package/dist/modules/access/reference-scan.d.ts.map +1 -1
- package/dist/modules/access/reference-scan.js +5 -4
- package/dist/modules/access/reference-scan.js.map +1 -1
- package/dist/modules/access/roles-admin.service.d.ts +72 -13
- package/dist/modules/access/roles-admin.service.d.ts.map +1 -1
- package/dist/modules/access/roles-admin.service.js +71 -13
- package/dist/modules/access/roles-admin.service.js.map +1 -1
- package/dist/modules/access/synced-groups-committer.d.ts.map +1 -1
- package/dist/modules/access/synced-groups-committer.js +8 -6
- package/dist/modules/access/synced-groups-committer.js.map +1 -1
- package/dist/modules/access/synced-groups-writer.d.ts.map +1 -1
- package/dist/modules/access/synced-groups-writer.js +3 -8
- package/dist/modules/access/synced-groups-writer.js.map +1 -1
- package/dist/modules/access/user-access-removal.service.d.ts +209 -0
- package/dist/modules/access/user-access-removal.service.d.ts.map +1 -0
- package/dist/modules/access/user-access-removal.service.js +662 -0
- package/dist/modules/access/user-access-removal.service.js.map +1 -0
- package/dist/modules/access-model/access-errors.d.ts +32 -1
- package/dist/modules/access-model/access-errors.d.ts.map +1 -1
- package/dist/modules/access-model/access-errors.js +38 -11
- package/dist/modules/access-model/access-errors.js.map +1 -1
- package/dist/modules/access-model/access-grammar.d.ts +49 -24
- package/dist/modules/access-model/access-grammar.d.ts.map +1 -1
- package/dist/modules/access-model/access-grammar.js +68 -48
- package/dist/modules/access-model/access-grammar.js.map +1 -1
- package/dist/modules/access-model/access-splice.d.ts.map +1 -1
- package/dist/modules/access-model/access-splice.js +18 -18
- package/dist/modules/access-model/access-splice.js.map +1 -1
- package/dist/modules/access-model/access-template.d.ts +22 -0
- package/dist/modules/access-model/access-template.d.ts.map +1 -0
- package/dist/modules/access-model/access-template.js +42 -0
- package/dist/modules/access-model/access-template.js.map +1 -0
- package/dist/modules/access-model/change-gate.d.ts +65 -0
- package/dist/modules/access-model/change-gate.d.ts.map +1 -0
- package/dist/modules/access-model/change-gate.js +25 -0
- package/dist/modules/access-model/change-gate.js.map +1 -0
- package/dist/modules/access-model/creator.d.ts +13 -16
- package/dist/modules/access-model/creator.d.ts.map +1 -1
- package/dist/modules/access-model/creator.js.map +1 -1
- package/dist/modules/access-model/frontmatter-lines.d.ts +27 -0
- package/dist/modules/access-model/frontmatter-lines.d.ts.map +1 -0
- package/dist/modules/access-model/frontmatter-lines.js +74 -0
- package/dist/modules/access-model/frontmatter-lines.js.map +1 -0
- package/dist/modules/access-model/plugin-principals.d.ts +5 -3
- package/dist/modules/access-model/plugin-principals.d.ts.map +1 -1
- package/dist/modules/access-model/plugin-principals.js +11 -17
- package/dist/modules/access-model/plugin-principals.js.map +1 -1
- package/dist/modules/access-model/roles-yaml-guard.d.ts +73 -2
- package/dist/modules/access-model/roles-yaml-guard.d.ts.map +1 -1
- package/dist/modules/access-model/roles-yaml-guard.js +153 -3
- package/dist/modules/access-model/roles-yaml-guard.js.map +1 -1
- package/dist/modules/admin/admin-access.service.d.ts.map +1 -1
- package/dist/modules/admin/admin-access.service.js +5 -2
- package/dist/modules/admin/admin-access.service.js.map +1 -1
- package/dist/modules/agent-instructions/agent-instructions.routes.d.ts +21 -0
- package/dist/modules/agent-instructions/agent-instructions.routes.d.ts.map +1 -0
- package/dist/modules/agent-instructions/agent-instructions.routes.js +38 -0
- package/dist/modules/agent-instructions/agent-instructions.routes.js.map +1 -0
- package/dist/modules/agent-instructions/compose.d.ts +71 -0
- package/dist/modules/agent-instructions/compose.d.ts.map +1 -0
- package/dist/modules/agent-instructions/compose.js +234 -0
- package/dist/modules/agent-instructions/compose.js.map +1 -0
- package/dist/modules/agent-instructions/index.d.ts +4 -0
- package/dist/modules/agent-instructions/index.d.ts.map +1 -0
- package/dist/modules/agent-instructions/index.js +4 -0
- package/dist/modules/agent-instructions/index.js.map +1 -0
- package/dist/modules/agent-instructions/read-preamble.d.ts +37 -0
- package/dist/modules/agent-instructions/read-preamble.d.ts.map +1 -0
- package/dist/modules/agent-instructions/read-preamble.js +81 -0
- package/dist/modules/agent-instructions/read-preamble.js.map +1 -0
- package/dist/modules/audit/agent-audit.service.d.ts +54 -0
- package/dist/modules/audit/agent-audit.service.d.ts.map +1 -0
- package/dist/modules/audit/agent-audit.service.js +290 -0
- package/dist/modules/audit/agent-audit.service.js.map +1 -0
- package/dist/modules/audit/agent-rest-audit.middleware.d.ts +40 -0
- package/dist/modules/audit/agent-rest-audit.middleware.d.ts.map +1 -0
- package/dist/modules/audit/agent-rest-audit.middleware.js +75 -0
- package/dist/modules/audit/agent-rest-audit.middleware.js.map +1 -0
- package/dist/modules/audit/audit.contract.d.ts +145 -0
- package/dist/modules/audit/audit.contract.d.ts.map +1 -0
- package/dist/modules/audit/audit.contract.js +35 -0
- package/dist/modules/audit/audit.contract.js.map +1 -0
- package/dist/modules/audit/audit.routes.d.ts +21 -0
- package/dist/modules/audit/audit.routes.d.ts.map +1 -0
- package/dist/modules/audit/audit.routes.js +157 -0
- package/dist/modules/audit/audit.routes.js.map +1 -0
- package/dist/modules/audit/event-classifier.d.ts +52 -0
- package/dist/modules/audit/event-classifier.d.ts.map +1 -0
- package/dist/modules/audit/event-classifier.js +123 -0
- package/dist/modules/audit/event-classifier.js.map +1 -0
- package/dist/modules/audit/index.d.ts +7 -0
- package/dist/modules/audit/index.d.ts.map +1 -0
- package/dist/modules/audit/index.js +7 -0
- package/dist/modules/audit/index.js.map +1 -0
- package/dist/modules/audit/request-audit.d.ts +57 -0
- package/dist/modules/audit/request-audit.d.ts.map +1 -0
- package/dist/modules/audit/request-audit.js +117 -0
- package/dist/modules/audit/request-audit.js.map +1 -0
- package/dist/modules/auth/account-erasure.service.d.ts +30 -4
- package/dist/modules/auth/account-erasure.service.d.ts.map +1 -1
- package/dist/modules/auth/account-erasure.service.js +96 -10
- package/dist/modules/auth/account-erasure.service.js.map +1 -1
- package/dist/modules/auth/account.routes.d.ts +6 -3
- package/dist/modules/auth/account.routes.d.ts.map +1 -1
- package/dist/modules/auth/account.routes.js +109 -10
- package/dist/modules/auth/account.routes.js.map +1 -1
- package/dist/modules/auth/auth.routes.d.ts +10 -3
- package/dist/modules/auth/auth.routes.d.ts.map +1 -1
- package/dist/modules/auth/auth.routes.js +9 -4
- package/dist/modules/auth/auth.routes.js.map +1 -1
- package/dist/modules/auth/auth.service.d.ts +89 -5
- package/dist/modules/auth/auth.service.d.ts.map +1 -1
- package/dist/modules/auth/auth.service.js +160 -25
- package/dist/modules/auth/auth.service.js.map +1 -1
- package/dist/modules/auth/oidc-auth-provider.d.ts +46 -3
- package/dist/modules/auth/oidc-auth-provider.d.ts.map +1 -1
- package/dist/modules/auth/oidc-auth-provider.js +99 -21
- package/dist/modules/auth/oidc-auth-provider.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 +14 -2
- package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
- package/dist/modules/connection-probe/connection-probe.service.d.ts.map +1 -1
- package/dist/modules/connection-probe/connection-probe.service.js +56 -7
- package/dist/modules/connection-probe/connection-probe.service.js.map +1 -1
- package/dist/modules/database/advisory-lock.d.ts +103 -0
- package/dist/modules/database/advisory-lock.d.ts.map +1 -0
- package/dist/modules/database/advisory-lock.js +320 -0
- package/dist/modules/database/advisory-lock.js.map +1 -0
- package/dist/modules/database/connection.d.ts.map +1 -1
- package/dist/modules/database/connection.js +9 -1
- package/dist/modules/database/connection.js.map +1 -1
- package/dist/modules/database/core-schema.d.ts +859 -99
- package/dist/modules/database/core-schema.d.ts.map +1 -1
- package/dist/modules/database/core-schema.js +218 -0
- package/dist/modules/database/core-schema.js.map +1 -1
- package/dist/modules/database/migrate.d.ts.map +1 -1
- package/dist/modules/database/migrate.js +26 -6
- package/dist/modules/database/migrate.js.map +1 -1
- package/dist/modules/declared-variables/declared-variables.routes.d.ts.map +1 -1
- package/dist/modules/declared-variables/declared-variables.routes.js +4 -2
- package/dist/modules/declared-variables/declared-variables.routes.js.map +1 -1
- package/dist/modules/diff/diff.routes.d.ts.map +1 -1
- package/dist/modules/diff/diff.routes.js +2 -4
- package/dist/modules/diff/diff.routes.js.map +1 -1
- package/dist/modules/diff/diff.service.d.ts +4 -1
- package/dist/modules/diff/diff.service.d.ts.map +1 -1
- package/dist/modules/diff/diff.service.js +84 -81
- package/dist/modules/diff/diff.service.js.map +1 -1
- package/dist/modules/{workspace → kb-fs}/bevel-ignore.d.ts +14 -9
- package/dist/modules/kb-fs/bevel-ignore.d.ts.map +1 -0
- package/dist/modules/{workspace → kb-fs}/bevel-ignore.js +24 -14
- package/dist/modules/kb-fs/bevel-ignore.js.map +1 -0
- package/dist/modules/kb-fs/file-change-notifier.d.ts.map +1 -1
- package/dist/modules/kb-fs/file-change-notifier.js +3 -1
- package/dist/modules/kb-fs/file-change-notifier.js.map +1 -1
- package/dist/modules/kb-fs/git-guarded-filesystem.d.ts +45 -0
- package/dist/modules/kb-fs/git-guarded-filesystem.d.ts.map +1 -0
- package/dist/modules/kb-fs/git-guarded-filesystem.js +117 -0
- package/dist/modules/kb-fs/git-guarded-filesystem.js.map +1 -0
- package/dist/modules/kb-fs/locking-filesystem.d.ts +113 -18
- package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.js +353 -76
- package/dist/modules/kb-fs/locking-filesystem.js.map +1 -1
- package/dist/modules/kb-fs/node-fs.d.ts +23 -0
- package/dist/modules/kb-fs/node-fs.d.ts.map +1 -0
- package/dist/modules/kb-fs/node-fs.js +158 -0
- package/dist/modules/kb-fs/node-fs.js.map +1 -0
- package/dist/modules/kb-fs/read-only-filesystem.d.ts +10 -9
- package/dist/modules/kb-fs/read-only-filesystem.d.ts.map +1 -1
- package/dist/modules/kb-fs/read-only-filesystem.js +18 -9
- package/dist/modules/kb-fs/read-only-filesystem.js.map +1 -1
- package/dist/modules/kb-fs/repo-path.d.ts +100 -3
- package/dist/modules/kb-fs/repo-path.d.ts.map +1 -1
- package/dist/modules/kb-fs/repo-path.js +195 -3
- package/dist/modules/kb-fs/repo-path.js.map +1 -1
- package/dist/modules/kb-sync/kb-sync.routes.d.ts.map +1 -1
- package/dist/modules/kb-sync/kb-sync.routes.js +4 -2
- package/dist/modules/kb-sync/kb-sync.routes.js.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 +7 -5
- package/dist/modules/kb-sync/kb-sync.service.js.map +1 -1
- package/dist/modules/marketplace/git-http.routes.d.ts.map +1 -1
- package/dist/modules/marketplace/git-http.routes.js +6 -4
- package/dist/modules/marketplace/git-http.routes.js.map +1 -1
- package/dist/modules/marketplace/github-facade/github-facade-admin.routes.d.ts +12 -2
- package/dist/modules/marketplace/github-facade/github-facade-admin.routes.d.ts.map +1 -1
- package/dist/modules/marketplace/github-facade/github-facade-admin.routes.js +32 -3
- package/dist/modules/marketplace/github-facade/github-facade-admin.routes.js.map +1 -1
- package/dist/modules/marketplace/github-facade/github-facade-credentials.service.d.ts +27 -0
- package/dist/modules/marketplace/github-facade/github-facade-credentials.service.d.ts.map +1 -1
- package/dist/modules/marketplace/github-facade/github-facade-credentials.service.js +45 -0
- package/dist/modules/marketplace/github-facade/github-facade-credentials.service.js.map +1 -1
- package/dist/modules/marketplace/github-facade/github-facade.routes.d.ts.map +1 -1
- package/dist/modules/marketplace/github-facade/github-facade.routes.js +30 -6
- package/dist/modules/marketplace/github-facade/github-facade.routes.js.map +1 -1
- package/dist/modules/marketplace/github-facade/github-facade.service.d.ts +21 -9
- package/dist/modules/marketplace/github-facade/github-facade.service.d.ts.map +1 -1
- package/dist/modules/marketplace/github-facade/github-facade.service.js +60 -19
- package/dist/modules/marketplace/github-facade/github-facade.service.js.map +1 -1
- package/dist/modules/marketplace/github-facade/index.d.ts +1 -1
- package/dist/modules/marketplace/github-facade/index.d.ts.map +1 -1
- package/dist/modules/marketplace/github-facade/index.js +1 -1
- package/dist/modules/marketplace/github-facade/index.js.map +1 -1
- package/dist/modules/marketplace/marketplace-repo.service.d.ts +14 -1
- package/dist/modules/marketplace/marketplace-repo.service.d.ts.map +1 -1
- package/dist/modules/marketplace/marketplace-repo.service.js +26 -8
- package/dist/modules/marketplace/marketplace-repo.service.js.map +1 -1
- package/dist/modules/mcp/downstream-pool.d.ts +107 -0
- package/dist/modules/mcp/downstream-pool.d.ts.map +1 -0
- package/dist/modules/mcp/downstream-pool.js +221 -0
- package/dist/modules/mcp/downstream-pool.js.map +1 -0
- package/dist/modules/mcp/downstream-token-refresh.d.ts +77 -0
- package/dist/modules/mcp/downstream-token-refresh.d.ts.map +1 -0
- package/dist/modules/mcp/downstream-token-refresh.js +166 -0
- package/dist/modules/mcp/downstream-token-refresh.js.map +1 -0
- package/dist/modules/mcp/first-call-probe.cli-args.d.ts +78 -0
- package/dist/modules/mcp/first-call-probe.cli-args.d.ts.map +1 -0
- package/dist/modules/mcp/first-call-probe.cli-args.js +151 -0
- package/dist/modules/mcp/first-call-probe.cli-args.js.map +1 -0
- package/dist/modules/mcp/first-call-probe.cli.d.ts +2 -0
- package/dist/modules/mcp/first-call-probe.cli.d.ts.map +1 -0
- package/dist/modules/mcp/first-call-probe.cli.js +62 -0
- package/dist/modules/mcp/first-call-probe.cli.js.map +1 -0
- package/dist/modules/mcp/first-call-probe.d.ts +165 -0
- package/dist/modules/mcp/first-call-probe.d.ts.map +1 -0
- package/dist/modules/mcp/first-call-probe.js +298 -0
- package/dist/modules/mcp/first-call-probe.js.map +1 -0
- package/dist/modules/mcp/manual-failure-memo.d.ts +42 -17
- package/dist/modules/mcp/manual-failure-memo.d.ts.map +1 -1
- package/dist/modules/mcp/manual-failure-memo.js +79 -23
- package/dist/modules/mcp/manual-failure-memo.js.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.d.ts +10 -5
- package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.js +56 -16
- package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
- package/dist/modules/mcp/mcp.routes.d.ts +4 -4
- package/dist/modules/mcp/mcp.routes.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.routes.js +91 -180
- package/dist/modules/mcp/mcp.routes.js.map +1 -1
- package/dist/modules/mcp/mcp.service.d.ts +226 -61
- package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.service.js +758 -161
- package/dist/modules/mcp/mcp.service.js.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts +59 -0
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js +184 -10
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
- package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/oauth-consent.routes.js +3 -1
- package/dist/modules/mcp/oauth/oauth-consent.routes.js.map +1 -1
- package/dist/modules/mcp/surface-log-throttle.d.ts +59 -0
- package/dist/modules/mcp/surface-log-throttle.d.ts.map +1 -0
- package/dist/modules/mcp/surface-log-throttle.js +94 -0
- package/dist/modules/mcp/surface-log-throttle.js.map +1 -0
- package/dist/modules/plugins/compile/compile-marketplace.d.ts +13 -6
- package/dist/modules/plugins/compile/compile-marketplace.d.ts.map +1 -1
- package/dist/modules/plugins/compile/compile-marketplace.js +119 -36
- package/dist/modules/plugins/compile/compile-marketplace.js.map +1 -1
- package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts +8 -1
- package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts.map +1 -1
- package/dist/modules/plugins/compile/marketplace-compiler.service.js +9 -6
- package/dist/modules/plugins/compile/marketplace-compiler.service.js.map +1 -1
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts.map +1 -1
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js +32 -9
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js.map +1 -1
- package/dist/modules/plugins/discovery/bundle-dialect/registry.d.ts +26 -6
- package/dist/modules/plugins/discovery/bundle-dialect/registry.d.ts.map +1 -1
- package/dist/modules/plugins/discovery/bundle-dialect/registry.js +71 -14
- package/dist/modules/plugins/discovery/bundle-dialect/registry.js.map +1 -1
- package/dist/modules/plugins/discovery/kb-plugin-source.d.ts +4 -2
- package/dist/modules/plugins/discovery/kb-plugin-source.d.ts.map +1 -1
- package/dist/modules/plugins/discovery/kb-plugin-source.js +9 -6
- package/dist/modules/plugins/discovery/kb-plugin-source.js.map +1 -1
- package/dist/modules/plugins/discovery/native.source.d.ts +2 -1
- package/dist/modules/plugins/discovery/native.source.d.ts.map +1 -1
- package/dist/modules/plugins/discovery/native.source.js +22 -4
- package/dist/modules/plugins/discovery/native.source.js.map +1 -1
- package/dist/modules/plugins/discovery/plugin-source.d.ts +17 -3
- package/dist/modules/plugins/discovery/plugin-source.d.ts.map +1 -1
- package/dist/modules/plugins/index.d.ts +6 -3
- package/dist/modules/plugins/index.d.ts.map +1 -1
- package/dist/modules/plugins/index.js +6 -3
- package/dist/modules/plugins/index.js.map +1 -1
- package/dist/modules/plugins/join-request-jobs.service.d.ts +250 -0
- package/dist/modules/plugins/join-request-jobs.service.d.ts.map +1 -0
- package/dist/modules/plugins/join-request-jobs.service.js +602 -0
- package/dist/modules/plugins/join-request-jobs.service.js.map +1 -0
- package/dist/modules/plugins/join-request-records.store.d.ts +161 -0
- package/dist/modules/plugins/join-request-records.store.d.ts.map +1 -0
- package/dist/modules/plugins/join-request-records.store.js +179 -0
- package/dist/modules/plugins/join-request-records.store.js.map +1 -0
- package/dist/modules/plugins/join-requests.service.d.ts.map +1 -1
- package/dist/modules/plugins/join-requests.service.js +4 -2
- package/dist/modules/plugins/join-requests.service.js.map +1 -1
- package/dist/modules/plugins/plugin-links.d.ts +4 -1
- package/dist/modules/plugins/plugin-links.d.ts.map +1 -1
- package/dist/modules/plugins/plugin-links.js +6 -3
- package/dist/modules/plugins/plugin-links.js.map +1 -1
- package/dist/modules/plugins/plugin-links.service.d.ts +2 -5
- package/dist/modules/plugins/plugin-links.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugin-links.service.js +4 -13
- package/dist/modules/plugins/plugin-links.service.js.map +1 -1
- package/dist/modules/plugins/plugin-provision.service.d.ts +91 -19
- package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugin-provision.service.js +183 -38
- package/dist/modules/plugins/plugin-provision.service.js.map +1 -1
- package/dist/modules/plugins/plugin-rename.service.d.ts +3 -1
- package/dist/modules/plugins/plugin-rename.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugin-rename.service.js +20 -11
- package/dist/modules/plugins/plugin-rename.service.js.map +1 -1
- package/dist/modules/plugins/plugins.contract.d.ts +52 -4
- package/dist/modules/plugins/plugins.contract.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.routes.d.ts +38 -5
- package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.routes.js +190 -116
- package/dist/modules/plugins/plugins.routes.js.map +1 -1
- package/dist/modules/plugins/plugins.service.d.ts +38 -6
- package/dist/modules/plugins/plugins.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.service.js +86 -17
- package/dist/modules/plugins/plugins.service.js.map +1 -1
- package/dist/modules/plugins/plugins.tools.d.ts +25 -0
- package/dist/modules/plugins/plugins.tools.d.ts.map +1 -0
- package/dist/modules/plugins/plugins.tools.js +82 -0
- package/dist/modules/plugins/plugins.tools.js.map +1 -0
- package/dist/modules/plugins/teams.routes.d.ts +42 -0
- package/dist/modules/plugins/teams.routes.d.ts.map +1 -0
- package/dist/modules/plugins/teams.routes.js +79 -0
- package/dist/modules/plugins/teams.routes.js.map +1 -0
- package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts +40 -1
- package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts.map +1 -1
- package/dist/modules/secrets-vault/db-secrets-vault.service.js +202 -37
- package/dist/modules/secrets-vault/db-secrets-vault.service.js.map +1 -1
- package/dist/modules/secrets-vault/index.d.ts +1 -1
- package/dist/modules/secrets-vault/index.d.ts.map +1 -1
- package/dist/modules/secrets-vault/index.js.map +1 -1
- package/dist/modules/secrets-vault/mcp-oauth-discovery.service.d.ts.map +1 -1
- package/dist/modules/secrets-vault/mcp-oauth-discovery.service.js +5 -3
- package/dist/modules/secrets-vault/mcp-oauth-discovery.service.js.map +1 -1
- package/dist/modules/secrets-vault/secrets-variable-loader.d.ts.map +1 -1
- package/dist/modules/secrets-vault/secrets-variable-loader.js +3 -1
- package/dist/modules/secrets-vault/secrets-variable-loader.js.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.contract.d.ts +78 -0
- package/dist/modules/secrets-vault/secrets-vault.contract.d.ts.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.contract.js +40 -0
- package/dist/modules/secrets-vault/secrets-vault.contract.js.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.routes.js +106 -52
- package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
- package/dist/modules/settings/connection-check.d.ts +118 -0
- package/dist/modules/settings/connection-check.d.ts.map +1 -0
- package/dist/modules/settings/connection-check.js +287 -0
- package/dist/modules/settings/connection-check.js.map +1 -0
- package/dist/modules/settings/deployment-settings.service.d.ts +162 -8
- package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
- package/dist/modules/settings/deployment-settings.service.js +375 -52
- package/dist/modules/settings/deployment-settings.service.js.map +1 -1
- package/dist/modules/settings/git-root-folders.d.ts +54 -0
- package/dist/modules/settings/git-root-folders.d.ts.map +1 -0
- package/dist/modules/settings/git-root-folders.js +130 -0
- package/dist/modules/settings/git-root-folders.js.map +1 -0
- package/dist/modules/settings/oidc-check.d.ts +85 -0
- package/dist/modules/settings/oidc-check.d.ts.map +1 -0
- package/dist/modules/settings/oidc-check.js +172 -0
- package/dist/modules/settings/oidc-check.js.map +1 -0
- package/dist/modules/settings/setup.routes.d.ts +31 -1
- package/dist/modules/settings/setup.routes.d.ts.map +1 -1
- package/dist/modules/settings/setup.routes.js +400 -108
- package/dist/modules/settings/setup.routes.js.map +1 -1
- package/dist/modules/skills/allowed-tools-check.d.ts +132 -0
- package/dist/modules/skills/allowed-tools-check.d.ts.map +1 -0
- package/dist/modules/skills/allowed-tools-check.js +260 -0
- package/dist/modules/skills/allowed-tools-check.js.map +1 -0
- package/dist/modules/skills/index.d.ts +3 -1
- package/dist/modules/skills/index.d.ts.map +1 -1
- package/dist/modules/skills/index.js +1 -0
- package/dist/modules/skills/index.js.map +1 -1
- package/dist/modules/skills/pending-skills.service.d.ts +6 -11
- package/dist/modules/skills/pending-skills.service.d.ts.map +1 -1
- package/dist/modules/skills/pending-skills.service.js +18 -70
- package/dist/modules/skills/pending-skills.service.js.map +1 -1
- package/dist/modules/skills/skill-access-requests.routes.d.ts.map +1 -1
- package/dist/modules/skills/skill-access-requests.routes.js +9 -6
- package/dist/modules/skills/skill-access-requests.routes.js.map +1 -1
- package/dist/modules/skills/skills.contract.d.ts +44 -10
- package/dist/modules/skills/skills.contract.d.ts.map +1 -1
- package/dist/modules/skills/skills.routes.d.ts +8 -1
- package/dist/modules/skills/skills.routes.d.ts.map +1 -1
- package/dist/modules/skills/skills.routes.js +16 -2
- package/dist/modules/skills/skills.routes.js.map +1 -1
- package/dist/modules/skills/skills.service.d.ts +31 -7
- package/dist/modules/skills/skills.service.d.ts.map +1 -1
- package/dist/modules/skills/skills.service.js +191 -81
- package/dist/modules/skills/skills.service.js.map +1 -1
- package/dist/modules/skills/skills.tools.d.ts +4 -1
- package/dist/modules/skills/skills.tools.d.ts.map +1 -1
- package/dist/modules/skills/skills.tools.js +41 -6
- package/dist/modules/skills/skills.tools.js.map +1 -1
- package/dist/modules/tool-auth/connection-key-rejection.d.ts +18 -0
- package/dist/modules/tool-auth/connection-key-rejection.d.ts.map +1 -0
- package/dist/modules/tool-auth/connection-key-rejection.js +20 -0
- package/dist/modules/tool-auth/connection-key-rejection.js.map +1 -0
- package/dist/modules/tool-auth/connection-keys-admin.routes.d.ts +14 -0
- package/dist/modules/tool-auth/connection-keys-admin.routes.d.ts.map +1 -0
- package/dist/modules/tool-auth/connection-keys-admin.routes.js +67 -0
- package/dist/modules/tool-auth/connection-keys-admin.routes.js.map +1 -0
- package/dist/modules/tool-auth/external-api-key.interface.d.ts +72 -8
- package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -1
- package/dist/modules/tool-auth/external-api-key.interface.js.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.d.ts +17 -6
- package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.js +83 -27
- package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
- package/dist/modules/tool-auth/internal-token.service.d.ts +15 -2
- package/dist/modules/tool-auth/internal-token.service.d.ts.map +1 -1
- package/dist/modules/tool-auth/internal-token.service.js +6 -2
- package/dist/modules/tool-auth/internal-token.service.js.map +1 -1
- package/dist/modules/tool-auth/key-or-session.middleware.d.ts +23 -0
- package/dist/modules/tool-auth/key-or-session.middleware.d.ts.map +1 -0
- package/dist/modules/tool-auth/key-or-session.middleware.js +39 -0
- package/dist/modules/tool-auth/key-or-session.middleware.js.map +1 -0
- package/dist/modules/tool-auth/tool-auth.middleware.d.ts +15 -1
- package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
- package/dist/modules/tool-auth/tool-auth.middleware.js +17 -6
- package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
- package/dist/modules/tool-helpers/tool-context.d.ts +14 -0
- package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-context.js +28 -6
- package/dist/modules/tool-helpers/tool-context.js.map +1 -1
- package/dist/modules/tool-helpers/tool-handler.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-handler.js +22 -5
- package/dist/modules/tool-helpers/tool-handler.js.map +1 -1
- package/dist/modules/tool-helpers/tool.contract.d.ts +6 -1
- package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool.contract.js +7 -1
- package/dist/modules/tool-helpers/tool.contract.js.map +1 -1
- package/dist/modules/tool-manuals/index.d.ts +2 -1
- package/dist/modules/tool-manuals/index.d.ts.map +1 -1
- package/dist/modules/tool-manuals/index.js +1 -0
- package/dist/modules/tool-manuals/index.js.map +1 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.js +8 -6
- package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
- package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts +10 -2
- package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/mcp-server-edit.service.js +12 -15
- package/dist/modules/tool-manuals/mcp-server-edit.service.js.map +1 -1
- package/dist/modules/tool-manuals/pending-tools.service.d.ts +50 -0
- package/dist/modules/tool-manuals/pending-tools.service.d.ts.map +1 -0
- package/dist/modules/tool-manuals/pending-tools.service.js +149 -0
- package/dist/modules/tool-manuals/pending-tools.service.js.map +1 -0
- package/dist/modules/tool-manuals/tool-delete.service.d.ts +135 -0
- package/dist/modules/tool-manuals/tool-delete.service.d.ts.map +1 -0
- package/dist/modules/tool-manuals/tool-delete.service.js +303 -0
- package/dist/modules/tool-manuals/tool-delete.service.js.map +1 -0
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +161 -0
- package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.contract.js +9 -0
- package/dist/modules/tool-manuals/tool-manuals.contract.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +19 -2
- package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.js +127 -57
- package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts +119 -5
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.js +404 -74
- package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.js +78 -6
- package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.js +88 -54
- package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
- package/dist/modules/workflow/event-bus.d.ts +10 -4
- package/dist/modules/workflow/event-bus.d.ts.map +1 -1
- package/dist/modules/workflow/event-bus.js +14 -11
- package/dist/modules/workflow/event-bus.js.map +1 -1
- package/dist/modules/workflow/events.routes.d.ts +17 -2
- package/dist/modules/workflow/events.routes.d.ts.map +1 -1
- package/dist/modules/workflow/events.routes.js +31 -12
- package/dist/modules/workflow/events.routes.js.map +1 -1
- package/dist/modules/workflow/file-lock.service.d.ts +22 -4
- package/dist/modules/workflow/file-lock.service.d.ts.map +1 -1
- package/dist/modules/workflow/file-lock.service.js +27 -4
- package/dist/modules/workflow/file-lock.service.js.map +1 -1
- package/dist/modules/workflow/git/change-request-link.d.ts +20 -0
- package/dist/modules/workflow/git/change-request-link.d.ts.map +1 -0
- package/dist/modules/workflow/git/change-request-link.js +34 -0
- package/dist/modules/workflow/git/change-request-link.js.map +1 -0
- package/dist/modules/workflow/git/git-version.d.ts.map +1 -1
- package/dist/modules/workflow/git/git-version.js +3 -1
- package/dist/modules/workflow/git/git-version.js.map +1 -1
- package/dist/modules/workflow/git/git.service.d.ts +263 -4
- package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/git.service.js +528 -118
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workflow/git/node-git-runner.d.ts +36 -0
- package/dist/modules/workflow/git/node-git-runner.d.ts.map +1 -0
- package/dist/modules/workflow/git/node-git-runner.js +273 -0
- package/dist/modules/workflow/git/node-git-runner.js.map +1 -0
- package/dist/modules/workflow/git/pull-request.service.d.ts +51 -2
- package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/pull-request.service.js +157 -25
- package/dist/modules/workflow/git/pull-request.service.js.map +1 -1
- package/dist/modules/workflow/pending-commits.service.d.ts +11 -0
- package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -1
- package/dist/modules/workflow/pending-commits.service.js +24 -4
- package/dist/modules/workflow/pending-commits.service.js.map +1 -1
- package/dist/modules/workflow/pending-commits.worker.d.ts +15 -8
- package/dist/modules/workflow/pending-commits.worker.d.ts.map +1 -1
- package/dist/modules/workflow/pending-commits.worker.js +107 -58
- package/dist/modules/workflow/pending-commits.worker.js.map +1 -1
- package/dist/modules/workflow/recovery-bot.d.ts.map +1 -1
- package/dist/modules/workflow/recovery-bot.js +3 -1
- package/dist/modules/workflow/recovery-bot.js.map +1 -1
- package/dist/modules/workflow/review-workflow/approval-lock.d.ts +60 -0
- package/dist/modules/workflow/review-workflow/approval-lock.d.ts.map +1 -0
- package/dist/modules/workflow/review-workflow/approval-lock.js +116 -0
- package/dist/modules/workflow/review-workflow/approval-lock.js.map +1 -0
- package/dist/modules/workflow/review-workflow/review-workflow.interface.d.ts +54 -7
- package/dist/modules/workflow/review-workflow/review-workflow.interface.d.ts.map +1 -1
- package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts +64 -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 +269 -90
- package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
- package/dist/modules/workflow/sanitize-error.d.ts +5 -2
- package/dist/modules/workflow/sanitize-error.d.ts.map +1 -1
- package/dist/modules/workflow/sanitize-error.js +8 -4
- package/dist/modules/workflow/sanitize-error.js.map +1 -1
- package/dist/modules/workflow/workflow.routes.d.ts +2 -0
- package/dist/modules/workflow/workflow.routes.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.routes.js +259 -29
- package/dist/modules/workflow/workflow.routes.js.map +1 -1
- package/dist/modules/workflow/workflow.service.d.ts +166 -14
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.service.js +906 -106
- package/dist/modules/workflow/workflow.service.js.map +1 -1
- package/dist/modules/workspace/empty-dirs.d.ts +15 -0
- package/dist/modules/workspace/empty-dirs.d.ts.map +1 -0
- package/dist/modules/workspace/empty-dirs.js +90 -0
- package/dist/modules/workspace/empty-dirs.js.map +1 -0
- package/dist/modules/workspace/file-readers/content-mode.d.ts +50 -0
- package/dist/modules/workspace/file-readers/content-mode.d.ts.map +1 -0
- package/dist/modules/workspace/file-readers/content-mode.js +55 -0
- package/dist/modules/workspace/file-readers/content-mode.js.map +1 -0
- package/dist/modules/workspace/file-readers/doc-extract.service.d.ts +4 -1
- package/dist/modules/workspace/file-readers/doc-extract.service.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/doc-extract.service.js +4 -1
- package/dist/modules/workspace/file-readers/doc-extract.service.js.map +1 -1
- package/dist/modules/workspace/file-readers/doc-extract.types.d.ts +5 -2
- package/dist/modules/workspace/file-readers/doc-extract.types.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/doc-extract.types.js +7 -4
- package/dist/modules/workspace/file-readers/doc-extract.types.js.map +1 -1
- package/dist/modules/workspace/file-readers/document-reader.d.ts +2 -0
- package/dist/modules/workspace/file-readers/document-reader.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/document-reader.js +5 -0
- package/dist/modules/workspace/file-readers/document-reader.js.map +1 -1
- package/dist/modules/workspace/file-readers/extract-docx.d.ts +6 -0
- package/dist/modules/workspace/file-readers/extract-docx.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/extract-docx.js +6 -0
- package/dist/modules/workspace/file-readers/extract-docx.js.map +1 -1
- package/dist/modules/workspace/file-readers/file-reader.d.ts +24 -5
- package/dist/modules/workspace/file-readers/file-reader.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/file-reader.js +11 -6
- package/dist/modules/workspace/file-readers/file-reader.js.map +1 -1
- package/dist/modules/workspace/file-readers/file-reader.registry.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/file-reader.registry.js +6 -1
- package/dist/modules/workspace/file-readers/file-reader.registry.js.map +1 -1
- package/dist/modules/workspace/file-readers/image-reader.d.ts +7 -5
- package/dist/modules/workspace/file-readers/image-reader.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/image-reader.js +9 -5
- package/dist/modules/workspace/file-readers/image-reader.js.map +1 -1
- package/dist/modules/workspace/file-readers/ooxml-text.d.ts +3 -0
- package/dist/modules/workspace/file-readers/ooxml-text.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/ooxml-text.js +39 -2
- package/dist/modules/workspace/file-readers/ooxml-text.js.map +1 -1
- package/dist/modules/workspace/file-readers/text-reader.d.ts +29 -1
- package/dist/modules/workspace/file-readers/text-reader.d.ts.map +1 -1
- package/dist/modules/workspace/file-readers/text-reader.js +57 -3
- package/dist/modules/workspace/file-readers/text-reader.js.map +1 -1
- package/dist/modules/workspace/git-internals.middleware.d.ts +28 -0
- package/dist/modules/workspace/git-internals.middleware.d.ts.map +1 -0
- package/dist/modules/workspace/git-internals.middleware.js +93 -0
- package/dist/modules/workspace/git-internals.middleware.js.map +1 -0
- package/dist/modules/workspace/not-found.d.ts +80 -0
- package/dist/modules/workspace/not-found.d.ts.map +1 -0
- package/dist/modules/workspace/not-found.js +112 -0
- package/dist/modules/workspace/not-found.js.map +1 -0
- package/dist/modules/workspace/session-ontology.gate.d.ts +1 -1
- package/dist/modules/workspace/session-ontology.gate.d.ts.map +1 -1
- package/dist/modules/workspace/session-ontology.gate.js +1 -1
- package/dist/modules/workspace/session-ontology.gate.js.map +1 -1
- package/dist/modules/workspace/startup/beside-checkout.d.ts +42 -0
- package/dist/modules/workspace/startup/beside-checkout.d.ts.map +1 -0
- package/dist/modules/workspace/startup/beside-checkout.js +110 -0
- package/dist/modules/workspace/startup/beside-checkout.js.map +1 -0
- package/dist/modules/workspace/startup/kb-git.d.ts +7 -12
- package/dist/modules/workspace/startup/kb-git.d.ts.map +1 -1
- package/dist/modules/workspace/startup/kb-git.js +34 -35
- package/dist/modules/workspace/startup/kb-git.js.map +1 -1
- package/dist/modules/workspace/startup/kb-startup-runner.d.ts +95 -0
- package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -1
- package/dist/modules/workspace/startup/kb-startup-runner.js +257 -55
- package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -1
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts +45 -0
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js +417 -385
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js.map +1 -1
- package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts +48 -0
- package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts.map +1 -0
- package/dist/modules/workspace/startup/steps/personal-spaces.step.js +183 -0
- package/dist/modules/workspace/startup/steps/personal-spaces.step.js.map +1 -0
- package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts +51 -0
- package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts.map +1 -0
- package/dist/modules/workspace/startup/steps/plugin-display-names.step.js +158 -0
- package/dist/modules/workspace/startup/steps/plugin-display-names.step.js.map +1 -0
- package/dist/modules/workspace/startup/steps/plugin-layout.d.ts +64 -0
- package/dist/modules/workspace/startup/steps/plugin-layout.d.ts.map +1 -0
- package/dist/modules/workspace/startup/steps/plugin-layout.js +97 -0
- package/dist/modules/workspace/startup/steps/plugin-layout.js.map +1 -0
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts +7 -1
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.js +45 -81
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.js.map +1 -1
- package/dist/modules/workspace/startup/steps/roles-yaml.step.d.ts +3 -1
- package/dist/modules/workspace/startup/steps/roles-yaml.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/roles-yaml.step.js +4 -12
- package/dist/modules/workspace/startup/steps/roles-yaml.step.js.map +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts +6 -1
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.js +154 -78
- package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
- package/dist/modules/workspace/startup/steps/template-files.step.d.ts +96 -24
- package/dist/modules/workspace/startup/steps/template-files.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/template-files.step.js +446 -138
- package/dist/modules/workspace/startup/steps/template-files.step.js.map +1 -1
- package/dist/modules/workspace/startup/steps/template-source.d.ts +94 -0
- package/dist/modules/workspace/startup/steps/template-source.d.ts.map +1 -0
- package/dist/modules/workspace/startup/steps/template-source.js +160 -0
- package/dist/modules/workspace/startup/steps/template-source.js.map +1 -0
- package/dist/modules/workspace/workspace.routes.d.ts +14 -2
- package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.routes.js +649 -238
- package/dist/modules/workspace/workspace.routes.js.map +1 -1
- package/dist/modules/workspace/workspace.service.d.ts +298 -12
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.service.js +933 -269
- package/dist/modules/workspace/workspace.service.js.map +1 -1
- package/dist/modules/workspace/workspace.tools.d.ts +42 -1
- package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.tools.js +1626 -146
- package/dist/modules/workspace/workspace.tools.js.map +1 -1
- package/dist/modules/workspace/write-denial.d.ts +76 -0
- package/dist/modules/workspace/write-denial.d.ts.map +1 -0
- package/dist/modules/workspace/write-denial.js +138 -0
- package/dist/modules/workspace/write-denial.js.map +1 -0
- package/dist/shared/canonical-file-identity.d.ts +64 -0
- package/dist/shared/canonical-file-identity.d.ts.map +1 -0
- package/dist/shared/canonical-file-identity.js +92 -0
- package/dist/shared/canonical-file-identity.js.map +1 -0
- package/dist/shared/domain-errors.d.ts +108 -0
- package/dist/shared/domain-errors.d.ts.map +1 -1
- package/dist/shared/domain-errors.js +137 -0
- package/dist/shared/domain-errors.js.map +1 -1
- package/dist/shared/email-identity.d.ts +32 -0
- package/dist/shared/email-identity.d.ts.map +1 -0
- package/dist/shared/email-identity.js +37 -0
- package/dist/shared/email-identity.js.map +1 -0
- package/dist/shared/fs.contract.d.ts +206 -0
- package/dist/shared/fs.contract.d.ts.map +1 -0
- package/dist/shared/fs.contract.js +79 -0
- package/dist/shared/fs.contract.js.map +1 -0
- package/dist/shared/git-failure.d.ts +80 -0
- package/dist/shared/git-failure.d.ts.map +1 -0
- package/dist/shared/git-failure.js +139 -0
- package/dist/shared/git-failure.js.map +1 -0
- package/dist/shared/git-internals.d.ts +23 -0
- package/dist/shared/git-internals.d.ts.map +1 -0
- package/dist/shared/git-internals.js +181 -0
- package/dist/shared/git-internals.js.map +1 -0
- package/dist/shared/git.contract.d.ts +157 -0
- package/dist/shared/git.contract.d.ts.map +1 -0
- package/dist/shared/git.contract.js +103 -0
- package/dist/shared/git.contract.js.map +1 -0
- package/dist/shared/http-errors.d.ts +33 -0
- package/dist/shared/http-errors.d.ts.map +1 -0
- package/dist/shared/http-errors.js +35 -0
- package/dist/shared/http-errors.js.map +1 -0
- package/dist/shared/logger.contract.d.ts +63 -0
- package/dist/shared/logger.contract.d.ts.map +1 -0
- package/dist/shared/logger.contract.js +126 -0
- package/dist/shared/logger.contract.js.map +1 -0
- package/dist/shared/logging.d.ts +31 -0
- package/dist/shared/logging.d.ts.map +1 -0
- package/dist/shared/logging.js +92 -0
- package/dist/shared/logging.js.map +1 -0
- package/dist/shared/path-containment.d.ts +21 -0
- package/dist/shared/path-containment.d.ts.map +1 -0
- package/dist/shared/path-containment.js +34 -0
- package/dist/shared/path-containment.js.map +1 -0
- package/dist/shared/pending-proposals.d.ts +71 -0
- package/dist/shared/pending-proposals.d.ts.map +1 -0
- package/dist/shared/pending-proposals.js +98 -0
- package/dist/shared/pending-proposals.js.map +1 -0
- package/dist/shared/printable.d.ts +22 -0
- package/dist/shared/printable.d.ts.map +1 -0
- package/dist/shared/printable.js +65 -0
- package/dist/shared/printable.js.map +1 -0
- package/dist/shared/redact-secret.d.ts +30 -0
- package/dist/shared/redact-secret.d.ts.map +1 -0
- package/dist/shared/redact-secret.js +114 -0
- package/dist/shared/redact-secret.js.map +1 -0
- package/dist/shared/rename-no-replace.d.ts +119 -0
- package/dist/shared/rename-no-replace.d.ts.map +1 -0
- package/dist/shared/rename-no-replace.js +323 -0
- package/dist/shared/rename-no-replace.js.map +1 -0
- package/dist/shared/ssrf.d.ts.map +1 -1
- package/dist/shared/ssrf.js +49 -18
- package/dist/shared/ssrf.js.map +1 -1
- package/dist/version.d.ts +10 -6
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +10 -6
- package/dist/version.js.map +1 -1
- package/kb-template/.bevelignore +4 -3
- package/kb-template/AGENTS.md +281 -36
- package/kb-template/KnowledgeBase/How to get started.md +1 -1
- package/kb-template/access.md +5 -3
- package/kb-template/mcp-description.md +27 -0
- package/migrations/0006_api_tokens_revoked_by.sql +1 -0
- package/migrations/0007_github_facade_registered.sql +1 -0
- package/migrations/0008_change_request_apply_failure.sql +4 -0
- package/migrations/0009_change_request_apply_failure_kind.sql +13 -0
- package/migrations/0010_plugin_join_requests.sql +15 -0
- package/migrations/0011_plugin_join_request_claim.sql +11 -0
- package/migrations/0012_agent_audit.sql +41 -0
- package/migrations/0013_agent_identity_and_key_soft_delete.sql +29 -0
- package/migrations/meta/0006_snapshot.json +1723 -0
- package/migrations/meta/0007_snapshot.json +1729 -0
- package/migrations/meta/0008_snapshot.json +1753 -0
- package/migrations/meta/0009_snapshot.json +1759 -0
- package/migrations/meta/0010_snapshot.json +1872 -0
- package/migrations/meta/0011_snapshot.json +1884 -0
- package/migrations/meta/0012_snapshot.json +2247 -0
- package/migrations/meta/0013_snapshot.json +2259 -0
- package/migrations/meta/_journal.json +56 -0
- package/package.json +7 -6
- package/src/__tests__/core-config.domain.test.ts +67 -2
- package/src/__tests__/kb-layout-config.test.ts +186 -1
- package/src/__tests__/open-change-gate.ts +15 -0
- package/src/core/__tests__/catalog-cache-invalidation.test.ts +83 -3
- package/src/core/__tests__/catalog-revision.test.ts +190 -0
- package/src/core/__tests__/lifecycle.test.ts +369 -0
- package/src/core/__tests__/public-config.test.ts +30 -0
- package/src/core/__tests__/readiness.test.ts +109 -0
- package/src/core/catalog-cache-invalidation.ts +20 -1
- package/src/core/catalog-revision.ts +110 -0
- package/src/core/create-core-server.ts +286 -79
- package/src/core/create-core-services.ts +315 -81
- package/src/core/lifecycle.ts +312 -0
- package/src/core/public-config.ts +86 -0
- package/src/core/readiness.ts +123 -0
- package/src/core-config.ts +70 -4
- package/src/index.ts +15 -0
- package/src/modules/access/__tests__/access-control.admin-root-floor.test.ts +362 -0
- package/src/modules/access/__tests__/access-control.atref-batch.test.ts +2 -1
- package/src/modules/access/__tests__/access-control.atref-cache.test.ts +46 -22
- package/src/modules/access/__tests__/access-control.deny-dependents.test.ts +215 -0
- package/src/modules/access/__tests__/access-control.download-read.test.ts +268 -0
- package/src/modules/access/__tests__/access-control.everyone-read.test.ts +145 -0
- package/src/modules/access/__tests__/access-control.group-read.test.ts +163 -0
- package/src/modules/access/__tests__/access-control.invalidate-race.test.ts +2 -0
- package/src/modules/access/__tests__/access-control.platform-restore.test.ts +152 -0
- package/src/modules/access/__tests__/access-control.prospective.test.ts +189 -0
- package/src/modules/access/__tests__/access-control.read-batch.test.ts +2 -1
- package/src/modules/access/__tests__/access-control.service.test.ts +123 -78
- package/src/modules/access/__tests__/access-groups.test.ts +47 -14
- package/src/modules/access/__tests__/access-md-format.test.ts +2 -1
- package/src/modules/access/__tests__/access-mutation.service.test.ts +52 -4
- package/src/modules/access/__tests__/access-own-read-grant.test.ts +2 -0
- package/src/modules/access/__tests__/access-personal-plugin.test.ts +153 -0
- package/src/modules/access/__tests__/access-plugin-principals.test.ts +18 -1
- package/src/modules/access/__tests__/access-restrict-here.test.ts +450 -0
- package/src/modules/access/__tests__/access.routes.batch.test.ts +139 -0
- package/src/modules/access/__tests__/access.routes.folder-governs.test.ts +394 -0
- package/src/modules/access/__tests__/access.routes.group-grant.test.ts +6 -2
- package/src/modules/access/__tests__/access.routes.overrides.test.ts +3 -2
- package/src/modules/access/__tests__/access.routes.proposal-branch.test.ts +234 -0
- package/src/modules/access/__tests__/access.routes.prospective.test.ts +216 -0
- package/src/modules/access/__tests__/access.routes.revoke.test.ts +7 -2
- package/src/modules/access/__tests__/access.routes.unknown-email.test.ts +360 -0
- package/src/modules/access/__tests__/change-read-gate.test.ts +272 -0
- package/src/modules/access/__tests__/creator-access.test.ts +81 -83
- package/src/modules/access/__tests__/grant-sources.test.ts +3 -2
- package/src/modules/access/__tests__/groups-admin.service.test.ts +6 -5
- package/src/modules/access/__tests__/roles-admin.security.test.ts +196 -0
- package/src/modules/access/__tests__/roles-admin.service.test.ts +127 -2
- package/src/modules/access/__tests__/roles-capabilities.test.ts +5 -4
- package/src/modules/access/__tests__/roles.routes.test.ts +5 -2
- package/src/modules/access/__tests__/user-access-removal.service.test.ts +402 -0
- package/src/modules/access/__tests__/users-db-double.ts +63 -0
- package/src/modules/access/access-control.interface.ts +251 -11
- package/src/modules/access/access-control.service.ts +719 -164
- package/src/modules/access/access-mutation.service.ts +182 -13
- package/src/modules/access/access-view.ts +316 -0
- package/src/modules/access/access.routes.ts +293 -95
- package/src/modules/access/admin-locked-commit.ts +12 -14
- package/src/modules/access/admin-route-helpers.ts +4 -4
- package/src/modules/access/change-read-gate.ts +148 -0
- package/src/modules/access/creator-access.ts +89 -124
- package/src/modules/access/directory-sync-bot.ts +4 -1
- package/src/modules/access/groups-admin.service.ts +3 -3
- package/src/modules/access/reference-scan.ts +5 -5
- package/src/modules/access/roles-admin.service.ts +94 -15
- package/src/modules/access/synced-groups-committer.ts +10 -15
- package/src/modules/access/synced-groups-writer.ts +3 -12
- package/src/modules/access/user-access-removal.service.ts +711 -0
- package/src/modules/access-model/__tests__/access-splice.test.ts +63 -1
- package/src/modules/access-model/__tests__/frontmatter-fences.test.ts +71 -0
- package/src/modules/access-model/__tests__/roles-yaml-guard.test.ts +131 -0
- package/src/modules/access-model/access-errors.ts +56 -15
- package/src/modules/access-model/access-grammar.ts +85 -48
- package/src/modules/access-model/access-splice.ts +18 -18
- package/src/modules/access-model/access-template.ts +44 -0
- package/src/modules/access-model/change-gate.ts +80 -0
- package/src/modules/access-model/creator.ts +18 -21
- package/src/modules/access-model/frontmatter-lines.ts +98 -0
- package/src/modules/access-model/plugin-principals.ts +12 -19
- package/src/modules/access-model/roles-yaml-guard.ts +182 -4
- package/src/modules/admin/admin-access.service.ts +6 -2
- package/src/modules/agent-instructions/__tests__/agent-instructions.route.test.ts +100 -0
- package/src/modules/agent-instructions/__tests__/compose.test.ts +256 -0
- package/src/modules/agent-instructions/__tests__/read-preamble.test.ts +135 -0
- package/src/modules/agent-instructions/agent-instructions.routes.ts +41 -0
- package/src/modules/agent-instructions/compose.ts +271 -0
- package/src/modules/agent-instructions/index.ts +14 -0
- package/src/modules/agent-instructions/read-preamble.ts +88 -0
- package/src/modules/audit/__tests__/agent-audit.service.test.ts +370 -0
- package/src/modules/audit/__tests__/agent-rest-audit.middleware.test.ts +113 -0
- package/src/modules/audit/__tests__/audit.routes.test.ts +201 -0
- package/src/modules/audit/__tests__/event-classifier.test.ts +121 -0
- package/src/modules/audit/agent-audit.service.ts +316 -0
- package/src/modules/audit/agent-rest-audit.middleware.ts +83 -0
- package/src/modules/audit/audit.contract.ts +164 -0
- package/src/modules/audit/audit.routes.ts +168 -0
- package/src/modules/audit/event-classifier.ts +155 -0
- package/src/modules/audit/index.ts +14 -0
- package/src/modules/audit/request-audit.ts +141 -0
- package/src/modules/auth/__tests__/account-erasure.approval-gate.test.ts +209 -0
- package/src/modules/auth/__tests__/account.routes.test.ts +178 -2
- package/src/modules/auth/__tests__/auth.middleware.test.ts +78 -0
- package/src/modules/auth/__tests__/auth.service.test.ts +338 -5
- package/src/modules/auth/__tests__/oidc-auth-provider.test.ts +352 -12
- package/src/modules/auth/__tests__/onboarding-done.route.test.ts +3 -1
- package/src/modules/auth/account-erasure.service.ts +117 -10
- package/src/modules/auth/account.routes.ts +115 -14
- package/src/modules/auth/auth.routes.ts +20 -7
- package/src/modules/auth/auth.service.ts +187 -28
- package/src/modules/auth/oidc-auth-provider.ts +120 -22
- package/src/modules/code-mode/__tests__/code-mode.tool.test.ts +109 -1
- package/src/modules/code-mode/code-mode.tool.ts +13 -2
- package/src/modules/connection-probe/__tests__/connection-probe.service.test.ts +78 -0
- package/src/modules/connection-probe/connection-probe.service.ts +59 -7
- package/src/modules/database/__tests__/advisory-lease.test.ts +152 -0
- package/src/modules/database/__tests__/advisory-lock.test.ts +147 -0
- package/src/modules/database/advisory-lock.ts +346 -0
- package/src/modules/database/connection.ts +9 -1
- package/src/modules/database/core-schema.ts +227 -0
- package/src/modules/database/migrate.ts +27 -6
- package/src/modules/declared-variables/declared-variables.routes.ts +6 -3
- package/src/modules/diff/__tests__/diff.service.seed-atomicity.test.ts +3 -0
- package/src/modules/diff/__tests__/diff.service.test.ts +3 -0
- package/src/modules/diff/diff.routes.ts +2 -4
- package/src/modules/diff/diff.service.ts +80 -79
- package/src/modules/kb-fs/__tests__/locking-filesystem.test.ts +472 -9
- package/src/modules/kb-fs/__tests__/node-fs.test.ts +409 -0
- package/src/modules/kb-fs/__tests__/repo-path.test.ts +210 -1
- package/src/modules/{workspace → kb-fs}/bevel-ignore.ts +22 -15
- package/src/modules/kb-fs/file-change-notifier.ts +4 -1
- package/src/modules/kb-fs/git-guarded-filesystem.ts +136 -0
- package/src/modules/kb-fs/locking-filesystem.ts +399 -109
- package/src/modules/kb-fs/node-fs.ts +165 -0
- package/src/modules/kb-fs/read-only-filesystem.ts +18 -9
- package/src/modules/kb-fs/repo-path.ts +213 -3
- package/src/modules/kb-sync/kb-sync.routes.ts +5 -2
- package/src/modules/kb-sync/kb-sync.service.ts +8 -5
- package/src/modules/marketplace/__tests__/github-facade.test.ts +134 -2
- package/src/modules/marketplace/__tests__/marketplace-git.test.ts +2 -1
- package/src/modules/marketplace/__tests__/marketplace-repo.head.test.ts +47 -0
- package/src/modules/marketplace/git-http.routes.ts +7 -4
- package/src/modules/marketplace/github-facade/github-facade-admin.routes.ts +35 -3
- package/src/modules/marketplace/github-facade/github-facade-credentials.service.ts +55 -0
- package/src/modules/marketplace/github-facade/github-facade.routes.ts +32 -6
- package/src/modules/marketplace/github-facade/github-facade.service.ts +80 -19
- package/src/modules/marketplace/github-facade/index.ts +1 -0
- package/src/modules/marketplace/marketplace-repo.service.ts +23 -8
- package/src/modules/mcp/__tests__/bevel-oauth-provider.test.ts +183 -13
- package/src/modules/mcp/__tests__/downstream-pool.test.ts +278 -0
- package/src/modules/mcp/__tests__/downstream-token-refresh.test.ts +178 -0
- package/src/modules/mcp/__tests__/fake-downstream-mcp-server.ts +156 -0
- package/src/modules/mcp/__tests__/first-call-probe.cli-args.test.ts +178 -0
- package/src/modules/mcp/__tests__/first-call-probe.test.ts +414 -0
- package/src/modules/mcp/__tests__/manual-failure-memo.test.ts +98 -6
- package/src/modules/mcp/__tests__/mcp-auth.middleware.test.ts +68 -7
- package/src/modules/mcp/__tests__/mcp-routes-harness.ts +1 -3
- package/src/modules/mcp/__tests__/mcp.e2e.test.ts +1055 -59
- package/src/modules/mcp/__tests__/mcp.routes.local-token.test.ts +24 -4
- package/src/modules/mcp/__tests__/mcp.routes.stateless.test.ts +182 -0
- package/src/modules/mcp/__tests__/mcp.service.test.ts +391 -6
- package/src/modules/mcp/__tests__/surface-log-throttle.test.ts +169 -0
- package/src/modules/mcp/downstream-pool.ts +256 -0
- package/src/modules/mcp/downstream-token-refresh.ts +170 -0
- package/src/modules/mcp/first-call-probe.cli-args.ts +159 -0
- package/src/modules/mcp/first-call-probe.cli.ts +60 -0
- package/src/modules/mcp/first-call-probe.ts +418 -0
- package/src/modules/mcp/manual-failure-memo.ts +81 -23
- package/src/modules/mcp/mcp-auth.middleware.ts +56 -16
- package/src/modules/mcp/mcp.routes.ts +386 -482
- package/src/modules/mcp/mcp.service.ts +905 -177
- package/src/modules/mcp/oauth/bevel-oauth-provider.ts +189 -8
- package/src/modules/mcp/oauth/oauth-consent.routes.ts +4 -4
- package/src/modules/mcp/surface-log-throttle.ts +120 -0
- package/src/modules/plugins/__tests__/bundle-dialect.e2e.test.ts +10 -7
- package/src/modules/plugins/__tests__/bundle-dialect.test.ts +165 -0
- package/src/modules/plugins/__tests__/compile-marketplace.test.ts +139 -10
- package/src/modules/plugins/__tests__/fake-join-request-store.ts +189 -0
- package/src/modules/plugins/__tests__/join-request-jobs.service.test.ts +608 -0
- package/src/modules/plugins/__tests__/plugin-creation-display-name.test.ts +241 -0
- package/src/modules/plugins/__tests__/plugin-index.service.test.ts +148 -7
- package/src/modules/plugins/__tests__/plugin-links.service.test.ts +10 -13
- package/src/modules/plugins/__tests__/plugin-names.test.ts +77 -0
- package/src/modules/plugins/__tests__/plugin-provision.service.test.ts +129 -19
- package/src/modules/plugins/__tests__/plugin-rename.service.test.ts +91 -29
- package/src/modules/plugins/__tests__/plugins.routes.test.ts +370 -19
- package/src/modules/plugins/__tests__/plugins.tools.test.ts +131 -0
- package/src/modules/plugins/__tests__/teams.routes.test.ts +248 -0
- package/src/modules/plugins/compile/compile-marketplace.ts +143 -46
- package/src/modules/plugins/compile/marketplace-compiler.service.ts +8 -7
- package/src/modules/plugins/discovery/__tests__/kb-plugin-source.test.ts +56 -20
- package/src/modules/plugins/discovery/bundle-dialect/bundle.source.ts +30 -9
- package/src/modules/plugins/discovery/bundle-dialect/registry.ts +93 -18
- package/src/modules/plugins/discovery/kb-plugin-source.ts +10 -7
- package/src/modules/plugins/discovery/native.source.ts +21 -3
- package/src/modules/plugins/discovery/plugin-source.ts +17 -3
- package/src/modules/plugins/index.ts +16 -3
- package/src/modules/plugins/join-request-jobs.service.ts +675 -0
- package/src/modules/plugins/join-request-records.store.ts +353 -0
- package/src/modules/plugins/join-requests.service.ts +7 -4
- package/src/modules/plugins/plugin-links.service.ts +4 -17
- package/src/modules/plugins/plugin-links.ts +6 -3
- package/src/modules/plugins/plugin-provision.service.ts +209 -37
- package/src/modules/plugins/plugin-rename.service.ts +21 -9
- package/src/modules/plugins/plugins.contract.ts +52 -4
- package/src/modules/plugins/plugins.routes.ts +204 -124
- package/src/modules/plugins/plugins.service.ts +92 -18
- package/src/modules/plugins/plugins.tools.ts +88 -0
- package/src/modules/plugins/teams.routes.ts +129 -0
- package/src/modules/secrets-vault/__tests__/connect-pending.route.test.ts +359 -15
- package/src/modules/secrets-vault/__tests__/db-secrets-vault.oauth.test.ts +248 -0
- package/src/modules/secrets-vault/__tests__/namespace-secrets.test.ts +202 -0
- package/src/modules/secrets-vault/db-secrets-vault.service.ts +217 -35
- package/src/modules/secrets-vault/index.ts +1 -0
- package/src/modules/secrets-vault/mcp-oauth-discovery.service.ts +6 -3
- package/src/modules/secrets-vault/secrets-variable-loader.ts +4 -4
- package/src/modules/secrets-vault/secrets-vault.contract.ts +93 -0
- package/src/modules/secrets-vault/secrets-vault.routes.ts +99 -38
- package/src/modules/settings/__tests__/connection-check.test.ts +233 -0
- package/src/modules/settings/__tests__/deployment-settings.service.test.ts +223 -6
- package/src/modules/settings/__tests__/git-root-folders.test.ts +247 -0
- package/src/modules/settings/__tests__/oidc-check.test.ts +185 -0
- package/src/modules/settings/__tests__/setup.routes.boot-failure.test.ts +119 -0
- package/src/modules/settings/__tests__/setup.routes.layout-phase.test.ts +170 -0
- package/src/modules/settings/__tests__/setup.routes.oidc.test.ts +555 -0
- package/src/modules/settings/__tests__/setup.routes.test.ts +667 -16
- package/src/modules/settings/connection-check.ts +351 -0
- package/src/modules/settings/deployment-settings.service.ts +444 -59
- package/src/modules/settings/git-root-folders.ts +171 -0
- package/src/modules/settings/oidc-check.ts +229 -0
- package/src/modules/settings/setup.routes.ts +456 -110
- package/src/modules/skills/__tests__/allowed-tools-check.test.ts +224 -0
- package/src/modules/skills/__tests__/allowed-tools-warn.tools.test.ts +210 -0
- package/src/modules/skills/__tests__/pending-skills.service.test.ts +3 -3
- package/src/modules/skills/__tests__/skills.routes.warnings.test.ts +91 -0
- package/src/modules/skills/__tests__/skills.service.test.ts +223 -2
- package/src/modules/skills/allowed-tools-check.ts +352 -0
- package/src/modules/skills/index.ts +4 -0
- package/src/modules/skills/pending-skills.service.ts +21 -82
- package/src/modules/skills/skill-access-requests.routes.ts +10 -6
- package/src/modules/skills/skills.contract.ts +43 -11
- package/src/modules/skills/skills.routes.ts +16 -1
- package/src/modules/skills/skills.service.ts +214 -84
- package/src/modules/skills/skills.tools.ts +43 -5
- package/src/modules/tool-auth/__tests__/connection-key-rejection.test.ts +192 -0
- package/src/modules/tool-auth/__tests__/connection-keys-admin.routes.test.ts +116 -0
- package/src/modules/tool-auth/__tests__/external-api-key.service.test.ts +153 -8
- package/src/modules/tool-auth/__tests__/internal-token.service.test.ts +6 -0
- package/src/modules/tool-auth/connection-key-rejection.ts +25 -0
- package/src/modules/tool-auth/connection-keys-admin.routes.ts +76 -0
- package/src/modules/tool-auth/external-api-key.interface.ts +78 -8
- package/src/modules/tool-auth/external-api-key.service.ts +98 -33
- package/src/modules/tool-auth/internal-token.service.ts +16 -2
- package/src/modules/tool-auth/key-or-session.middleware.ts +45 -0
- package/src/modules/tool-auth/tool-auth.middleware.ts +33 -8
- package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +228 -0
- package/src/modules/tool-helpers/__tests__/phase4-tools.test.ts +33 -4
- package/src/modules/tool-helpers/tool-context.ts +46 -7
- package/src/modules/tool-helpers/tool-handler.ts +23 -5
- package/src/modules/tool-helpers/tool.contract.ts +5 -0
- package/src/modules/tool-manuals/__tests__/mcp-server-edit.service.test.ts +2 -1
- package/src/modules/tool-manuals/__tests__/pending-tools.service.test.ts +411 -0
- package/src/modules/tool-manuals/__tests__/tool-delete.route.test.ts +133 -0
- package/src/modules/tool-manuals/__tests__/tool-delete.service.test.ts +469 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.archive.route.test.ts +21 -2
- package/src/modules/tool-manuals/__tests__/tool-manuals.cli.test.ts +9 -4
- package/src/modules/tool-manuals/__tests__/tool-manuals.detail.route.test.ts +6 -1
- package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.routes.test.ts +254 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.test.ts +577 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +6 -1
- package/src/modules/tool-manuals/__tests__/tool-manuals.pending.route.test.ts +102 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.restart.test.ts +285 -0
- package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +178 -2
- package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +114 -2
- package/src/modules/tool-manuals/index.ts +3 -0
- package/src/modules/tool-manuals/mcp-json-discovery.ts +13 -10
- package/src/modules/tool-manuals/mcp-server-edit.service.ts +11 -14
- package/src/modules/tool-manuals/pending-tools.service.ts +179 -0
- package/src/modules/tool-manuals/tool-delete.service.ts +401 -0
- package/src/modules/tool-manuals/tool-manuals.contract.ts +165 -0
- package/src/modules/tool-manuals/tool-manuals.routes.ts +138 -54
- package/src/modules/tool-manuals/tool-manuals.service.ts +432 -87
- package/src/modules/tool-manuals/tool-manuals.tools.ts +81 -6
- package/src/modules/workflow/__tests__/apply-failure.test.ts +1038 -0
- package/src/modules/workflow/__tests__/event-bus.test.ts +61 -9
- package/src/modules/workflow/__tests__/fake-file-lock-db.ts +199 -0
- package/src/modules/workflow/__tests__/file-lock.service.path-identity.test.ts +237 -0
- package/src/modules/workflow/__tests__/format-affected-owners.test.ts +18 -0
- package/src/modules/workflow/__tests__/merge-announces-tree.test.ts +220 -0
- package/src/modules/workflow/__tests__/pending-commits.worker.concurrency.test.ts +187 -0
- package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +2 -0
- package/src/modules/workflow/__tests__/sanitize-error.test.ts +10 -0
- package/src/modules/workflow/__tests__/workflow.routes.folder-proposals.test.ts +128 -0
- package/src/modules/workflow/__tests__/workflow.routes.fork-point-file.test.ts +149 -0
- package/src/modules/workflow/__tests__/workflow.routes.lock-path.test.ts +305 -0
- package/src/modules/workflow/__tests__/workflow.service.branch-in-use.test.ts +2 -0
- package/src/modules/workflow/__tests__/workflow.service.commitFileWhileLocked.test.ts +2 -0
- package/src/modules/workflow/__tests__/workflow.service.deleted-branch-sweep.test.ts +2 -0
- package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +294 -25
- package/src/modules/workflow/__tests__/workflow.service.folder-proposals.test.ts +564 -0
- package/src/modules/workflow/__tests__/workflow.service.platform-restore.test.ts +135 -0
- package/src/modules/workflow/__tests__/workflow.service.read-gate.test.ts +172 -0
- package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +14 -0
- package/src/modules/workflow/__tests__/workflow.service.sync.test.ts +5 -0
- package/src/modules/workflow/__tests__/workflow.service.update-from-target.test.ts +281 -0
- package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +158 -7
- package/src/modules/workflow/agent-tools/workflow.tools.ts +93 -64
- package/src/modules/workflow/event-bus.ts +26 -28
- package/src/modules/workflow/events.routes.ts +56 -21
- package/src/modules/workflow/file-lock.service.ts +27 -4
- package/src/modules/workflow/git/__tests__/change-request-link.test.ts +43 -0
- package/src/modules/workflow/git/__tests__/git.service.accessGating.test.ts +1 -0
- package/src/modules/workflow/git/__tests__/git.service.changedFilesForPr.test.ts +238 -1
- package/src/modules/workflow/git/__tests__/git.service.forkPoint.test.ts +245 -0
- package/src/modules/workflow/git/__tests__/git.service.list-branches.test.ts +35 -0
- package/src/modules/workflow/git/__tests__/git.service.mergeChangeRequest.test.ts +149 -0
- package/src/modules/workflow/git/__tests__/git.service.revertPathsAndPush.test.ts +177 -0
- package/src/modules/workflow/git/__tests__/node-git-runner.test.ts +114 -0
- package/src/modules/workflow/git/__tests__/pull-request.service.getPrDetail.test.ts +69 -2
- package/src/modules/workflow/git/__tests__/pull-request.service.list-fetch.test.ts +121 -0
- package/src/modules/workflow/git/__tests__/pull-request.service.placeholder.test.ts +79 -0
- package/src/modules/workflow/git/__tests__/pull-request.service.test.ts +1 -1
- package/src/modules/workflow/git/__tests__/pull-request.service.viewer-can-cancel.test.ts +1 -1
- package/src/modules/workflow/git/__tests__/pull-request.service.viewer-can-update.test.ts +111 -0
- package/src/modules/workflow/git/change-request-link.ts +35 -0
- package/src/modules/workflow/git/git-version.ts +5 -2
- package/src/modules/workflow/git/git.service.ts +684 -144
- package/src/modules/workflow/git/node-git-runner.ts +315 -0
- package/src/modules/workflow/git/pull-request.service.ts +179 -27
- package/src/modules/workflow/pending-commits.service.ts +26 -7
- package/src/modules/workflow/pending-commits.worker.ts +114 -69
- package/src/modules/workflow/recovery-bot.ts +4 -1
- package/src/modules/workflow/review-workflow/__tests__/approval-states.test.ts +180 -1
- package/src/modules/workflow/review-workflow/__tests__/cancel-pr.test.ts +1 -1
- package/src/modules/workflow/review-workflow/__tests__/carry-approvals-forward.test.ts +520 -0
- package/src/modules/workflow/review-workflow/__tests__/erase-approver.test.ts +84 -0
- package/src/modules/workflow/review-workflow/__tests__/merge-gate.test.ts +89 -77
- package/src/modules/workflow/review-workflow/approval-lock.ts +122 -0
- package/src/modules/workflow/review-workflow/review-workflow.interface.ts +59 -7
- package/src/modules/workflow/review-workflow/review-workflow.service.ts +309 -105
- package/src/modules/workflow/sanitize-error.ts +9 -4
- package/src/modules/workflow/workflow.routes.ts +273 -29
- package/src/modules/workflow/workflow.service.ts +1087 -171
- package/src/modules/workspace/__tests__/empty-dirs.test.ts +44 -0
- package/src/modules/workspace/__tests__/file-stat-access.test.ts +301 -0
- package/src/modules/workspace/__tests__/git-internals.security.test.ts +883 -0
- package/src/modules/workspace/__tests__/workspace-paths-inside-the-repo.test.ts +464 -0
- package/src/modules/workspace/__tests__/workspace.routes.branch-errors.test.ts +139 -0
- package/src/modules/workspace/__tests__/workspace.routes.create-grant.test.ts +189 -37
- package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +51 -11
- package/src/modules/workspace/__tests__/workspace.routes.download-implies-read.test.ts +161 -0
- package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +109 -10
- package/src/modules/workspace/__tests__/workspace.routes.file-writes.test.ts +624 -0
- package/src/modules/workspace/__tests__/workspace.routes.folders.test.ts +409 -0
- package/src/modules/workspace/__tests__/workspace.routes.move-platform-files.test.ts +327 -0
- package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +25 -6
- package/src/modules/workspace/__tests__/workspace.service.ensure-remotes-fetched.test.ts +109 -0
- package/src/modules/workspace/__tests__/workspace.service.list-cloned.test.ts +6 -5
- package/src/modules/workspace/__tests__/workspace.service.move-never-overwrites.test.ts +218 -0
- package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +56 -1
- package/src/modules/workspace/__tests__/workspace.service.test.ts +811 -42
- package/src/modules/workspace/__tests__/workspace.service.unknown-branch.test.ts +197 -0
- package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +128 -0
- package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +212 -0
- package/src/modules/workspace/__tests__/workspace.tools.test.ts +2632 -110
- package/src/modules/workspace/__tests__/write-denial.test.ts +75 -0
- package/src/modules/workspace/empty-dirs.ts +79 -0
- package/src/modules/workspace/file-readers/README.md +32 -0
- package/src/modules/workspace/file-readers/__tests__/doc-extract.test.ts +59 -2
- package/src/modules/workspace/file-readers/__tests__/file-reader.registry.test.ts +133 -4
- package/src/modules/workspace/file-readers/__tests__/fixtures/soft-break.docx +0 -0
- package/src/modules/workspace/file-readers/__tests__/fixtures/tab.docx +0 -0
- package/src/modules/workspace/file-readers/content-mode.ts +87 -0
- package/src/modules/workspace/file-readers/doc-extract.service.ts +4 -1
- package/src/modules/workspace/file-readers/doc-extract.types.ts +8 -4
- package/src/modules/workspace/file-readers/document-reader.ts +6 -0
- package/src/modules/workspace/file-readers/extract-docx.ts +6 -0
- package/src/modules/workspace/file-readers/file-reader.registry.ts +9 -1
- package/src/modules/workspace/file-readers/file-reader.ts +27 -6
- package/src/modules/workspace/file-readers/image-reader.ts +10 -5
- package/src/modules/workspace/file-readers/ooxml-text.ts +38 -2
- package/src/modules/workspace/file-readers/text-reader.ts +61 -4
- package/src/modules/workspace/git-internals.middleware.ts +86 -0
- package/src/modules/workspace/not-found.ts +119 -0
- package/src/modules/workspace/session-ontology.gate.ts +1 -1
- package/src/modules/workspace/startup/__tests__/beside-checkout.test.ts +181 -0
- package/src/modules/workspace/startup/__tests__/kb-startup-runner.degraded.test.ts +361 -0
- package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +53 -16
- package/src/modules/workspace/startup/beside-checkout.ts +112 -0
- package/src/modules/workspace/startup/kb-git.ts +43 -36
- package/src/modules/workspace/startup/kb-startup-runner.ts +303 -63
- package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +1215 -73
- package/src/modules/workspace/startup/steps/groups-to-plugins.step.ts +460 -417
- package/src/modules/workspace/startup/steps/personal-spaces.step.ts +191 -0
- package/src/modules/workspace/startup/steps/plugin-display-names.step.ts +169 -0
- package/src/modules/workspace/startup/steps/plugin-layout.ts +110 -0
- package/src/modules/workspace/startup/steps/plugin-manifests.step.ts +46 -85
- package/src/modules/workspace/startup/steps/roles-yaml.step.ts +6 -12
- package/src/modules/workspace/startup/steps/seed-tree.ts +159 -78
- package/src/modules/workspace/startup/steps/template-files.step.ts +492 -141
- package/src/modules/workspace/startup/steps/template-source.ts +168 -0
- package/src/modules/workspace/workspace.routes.ts +687 -257
- package/src/modules/workspace/workspace.service.ts +1000 -303
- package/src/modules/workspace/workspace.tools.ts +1823 -180
- package/src/modules/workspace/write-denial.ts +189 -0
- package/src/shared/__tests__/canonical-file-identity.test.ts +154 -0
- package/src/shared/__tests__/email-identity.test.ts +35 -0
- package/src/shared/__tests__/filename.test.ts +0 -0
- package/src/shared/__tests__/frontmatter-id.test.ts +5 -0
- package/src/shared/__tests__/git-failure.test.ts +198 -0
- package/src/shared/__tests__/git-internals.test.ts +156 -0
- package/src/shared/__tests__/http-errors.test.ts +42 -0
- package/src/shared/__tests__/logging.test.ts +185 -0
- package/src/shared/__tests__/path-containment.test.ts +60 -0
- package/src/shared/__tests__/pending-proposals.readAt.test.ts +102 -0
- package/src/shared/__tests__/printable.test.ts +64 -0
- package/src/shared/__tests__/redact-secret.test.ts +122 -0
- package/src/shared/__tests__/rename-no-replace.test.ts +466 -0
- package/src/shared/__tests__/ssrf.test.ts +20 -0
- package/src/shared/canonical-file-identity.ts +92 -0
- package/src/shared/domain-errors.ts +149 -0
- package/src/shared/email-identity.ts +38 -0
- package/src/shared/fs.contract.ts +225 -0
- package/src/shared/git-failure.ts +196 -0
- package/src/shared/git-internals.ts +179 -0
- package/src/shared/git.contract.ts +170 -0
- package/src/shared/http-errors.ts +36 -0
- package/src/shared/logger.contract.ts +141 -0
- package/src/shared/logging.ts +96 -0
- package/src/shared/path-containment.ts +34 -0
- package/src/shared/pending-proposals.ts +161 -0
- package/src/shared/printable.ts +68 -0
- package/src/shared/redact-secret.ts +118 -0
- package/src/shared/rename-no-replace.ts +386 -0
- package/src/shared/ssrf.ts +44 -16
- package/src/version.ts +10 -6
- package/dist/modules/access/access-errors.d.ts +0 -34
- package/dist/modules/access/access-errors.d.ts.map +0 -1
- package/dist/modules/access/access-errors.js +0 -40
- package/dist/modules/access/access-errors.js.map +0 -1
- package/dist/modules/access/access-splice.d.ts +0 -140
- package/dist/modules/access/access-splice.d.ts.map +0 -1
- package/dist/modules/access/access-splice.js +0 -389
- package/dist/modules/access/access-splice.js.map +0 -1
- package/dist/modules/access/group-files.d.ts +0 -83
- package/dist/modules/access/group-files.d.ts.map +0 -1
- package/dist/modules/access/group-files.js +0 -167
- package/dist/modules/access/group-files.js.map +0 -1
- package/dist/modules/access/kb-read-filter.d.ts +0 -41
- package/dist/modules/access/kb-read-filter.d.ts.map +0 -1
- package/dist/modules/access/kb-read-filter.js +0 -60
- package/dist/modules/access/kb-read-filter.js.map +0 -1
- package/dist/modules/access/render-roles-yaml.d.ts +0 -22
- package/dist/modules/access/render-roles-yaml.d.ts.map +0 -1
- package/dist/modules/access/render-roles-yaml.js +0 -56
- package/dist/modules/access/render-roles-yaml.js.map +0 -1
- package/dist/modules/access/roles-yaml-guard.d.ts +0 -56
- package/dist/modules/access/roles-yaml-guard.d.ts.map +0 -1
- package/dist/modules/access/roles-yaml-guard.js +0 -79
- package/dist/modules/access/roles-yaml-guard.js.map +0 -1
- package/dist/modules/diff/diff-paths.d.ts +0 -2
- package/dist/modules/diff/diff-paths.d.ts.map +0 -1
- package/dist/modules/diff/diff-paths.js +0 -9
- package/dist/modules/diff/diff-paths.js.map +0 -1
- package/dist/modules/groups/group-provision.service.d.ts +0 -130
- package/dist/modules/groups/group-provision.service.d.ts.map +0 -1
- package/dist/modules/groups/group-provision.service.js +0 -288
- package/dist/modules/groups/group-provision.service.js.map +0 -1
- package/dist/modules/groups/groups.contract.d.ts +0 -106
- package/dist/modules/groups/groups.contract.d.ts.map +0 -1
- package/dist/modules/groups/groups.contract.js +0 -36
- package/dist/modules/groups/groups.contract.js.map +0 -1
- package/dist/modules/groups/groups.routes.d.ts +0 -42
- package/dist/modules/groups/groups.routes.d.ts.map +0 -1
- package/dist/modules/groups/groups.routes.js +0 -379
- package/dist/modules/groups/groups.routes.js.map +0 -1
- package/dist/modules/groups/groups.service.d.ts +0 -60
- package/dist/modules/groups/groups.service.d.ts.map +0 -1
- package/dist/modules/groups/groups.service.js +0 -172
- package/dist/modules/groups/groups.service.js.map +0 -1
- package/dist/modules/groups/index.d.ts +0 -7
- package/dist/modules/groups/index.d.ts.map +0 -1
- package/dist/modules/groups/index.js +0 -6
- package/dist/modules/groups/index.js.map +0 -1
- package/dist/modules/groups/join-proposals.d.ts +0 -53
- package/dist/modules/groups/join-proposals.d.ts.map +0 -1
- package/dist/modules/groups/join-proposals.js +0 -67
- package/dist/modules/groups/join-proposals.js.map +0 -1
- package/dist/modules/groups/join-requests.service.d.ts +0 -81
- package/dist/modules/groups/join-requests.service.d.ts.map +0 -1
- package/dist/modules/groups/join-requests.service.js +0 -135
- package/dist/modules/groups/join-requests.service.js.map +0 -1
- package/dist/modules/mcp/mcp-session-store.d.ts +0 -74
- package/dist/modules/mcp/mcp-session-store.d.ts.map +0 -1
- package/dist/modules/mcp/mcp-session-store.js +0 -131
- package/dist/modules/mcp/mcp-session-store.js.map +0 -1
- package/dist/modules/workflow/file-change-notifier.d.ts +0 -38
- package/dist/modules/workflow/file-change-notifier.d.ts.map +0 -1
- package/dist/modules/workflow/file-change-notifier.js +0 -22
- package/dist/modules/workflow/file-change-notifier.js.map +0 -1
- package/dist/modules/workflow/git/branch-name.d.ts +0 -10
- package/dist/modules/workflow/git/branch-name.d.ts.map +0 -1
- package/dist/modules/workflow/git/branch-name.js +0 -76
- package/dist/modules/workflow/git/branch-name.js.map +0 -1
- package/dist/modules/workflow/git/clone-config.d.ts +0 -61
- package/dist/modules/workflow/git/clone-config.d.ts.map +0 -1
- package/dist/modules/workflow/git/clone-config.js +0 -69
- package/dist/modules/workflow/git/clone-config.js.map +0 -1
- package/dist/modules/workflow/git/mutex.d.ts +0 -11
- package/dist/modules/workflow/git/mutex.d.ts.map +0 -1
- package/dist/modules/workflow/git/mutex.js +0 -23
- package/dist/modules/workflow/git/mutex.js.map +0 -1
- package/dist/modules/workflow/locking-filesystem.d.ts +0 -137
- package/dist/modules/workflow/locking-filesystem.d.ts.map +0 -1
- package/dist/modules/workflow/locking-filesystem.js +0 -553
- package/dist/modules/workflow/locking-filesystem.js.map +0 -1
- package/dist/modules/workflow/read-only-filesystem.d.ts +0 -24
- package/dist/modules/workflow/read-only-filesystem.d.ts.map +0 -1
- package/dist/modules/workflow/read-only-filesystem.js +0 -39
- package/dist/modules/workflow/read-only-filesystem.js.map +0 -1
- package/dist/modules/workflow/workflow.errors.d.ts +0 -197
- package/dist/modules/workflow/workflow.errors.d.ts.map +0 -1
- package/dist/modules/workflow/workflow.errors.js +0 -298
- package/dist/modules/workflow/workflow.errors.js.map +0 -1
- package/dist/modules/workspace/bevel-ignore.d.ts.map +0 -1
- package/dist/modules/workspace/bevel-ignore.js.map +0 -1
- package/dist/modules/workspace/kb-seed.interface.d.ts +0 -36
- package/dist/modules/workspace/kb-seed.interface.d.ts.map +0 -1
- package/dist/modules/workspace/kb-seed.interface.js +0 -2
- package/dist/modules/workspace/kb-seed.interface.js.map +0 -1
- package/dist/modules/workspace/kb-seed.service.d.ts +0 -125
- package/dist/modules/workspace/kb-seed.service.d.ts.map +0 -1
- package/dist/modules/workspace/kb-seed.service.js +0 -535
- package/dist/modules/workspace/kb-seed.service.js.map +0 -1
- package/dist/modules/workspace/plugins-migration.d.ts +0 -50
- package/dist/modules/workspace/plugins-migration.d.ts.map +0 -1
- package/dist/modules/workspace/plugins-migration.js +0 -379
- package/dist/modules/workspace/plugins-migration.js.map +0 -1
- package/dist/shared/fs-errors.d.ts +0 -8
- package/dist/shared/fs-errors.d.ts.map +0 -1
- package/dist/shared/fs-errors.js +0 -11
- package/dist/shared/fs-errors.js.map +0 -1
- package/dist/shared/fs-walk.d.ts +0 -23
- package/dist/shared/fs-walk.d.ts.map +0 -1
- package/dist/shared/fs-walk.js +0 -42
- package/dist/shared/fs-walk.js.map +0 -1
- package/dist/shared/hash-email.d.ts +0 -11
- package/dist/shared/hash-email.d.ts.map +0 -1
- package/dist/shared/hash-email.js +0 -14
- package/dist/shared/hash-email.js.map +0 -1
- package/dist/shared/kb-walk.d.ts +0 -52
- package/dist/shared/kb-walk.d.ts.map +0 -1
- package/dist/shared/kb-walk.js +0 -76
- package/dist/shared/kb-walk.js.map +0 -1
- package/src/modules/diff/diff-paths.ts +0 -9
- package/src/modules/mcp/__tests__/mcp-session-store.test.ts +0 -151
- package/src/modules/mcp/__tests__/mcp.routes.session.test.ts +0 -226
- package/src/modules/mcp/mcp-session-store.ts +0 -162
- package/src/shared/__tests__/fs-walk.test.ts +0 -63
- package/src/shared/__tests__/kb-walk.test.ts +0 -107
- package/src/shared/fs-errors.ts +0 -10
- package/src/shared/fs-walk.ts +0 -45
- package/src/shared/hash-email.ts +0 -14
- package/src/shared/kb-walk.ts +0 -95
|
@@ -1,19 +1,30 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
import { NodeFs } from '../../../../kb-fs/node-fs.js';
|
|
2
3
|
import { execFile } from 'node:child_process';
|
|
3
4
|
import { promisify } from 'node:util';
|
|
4
5
|
import fs from 'node:fs/promises';
|
|
5
6
|
import os from 'node:os';
|
|
6
7
|
import path from 'node:path';
|
|
7
8
|
import { KbStartupRunner } from '../../kb-startup-runner.js';
|
|
9
|
+
import { NodeGitRunner } from '../../../../workflow/git/node-git-runner.js';
|
|
8
10
|
import type { OnServerStart, ServerStartContext, StepResult } from '../../on-server-start.js';
|
|
9
11
|
import { GroupsToPluginsStep } from '../groups-to-plugins.step.js';
|
|
12
|
+
import { PluginDisplayNamesStep } from '../plugin-display-names.step.js';
|
|
10
13
|
import { PluginManifestsStep } from '../plugin-manifests.step.js';
|
|
14
|
+
import { PersonalSpacesStep } from '../personal-spaces.step.js';
|
|
11
15
|
import { RolesYamlStep } from '../roles-yaml.step.js';
|
|
12
16
|
import { renderRolesYaml } from '../../../../access-model/render-roles-yaml.js';
|
|
17
|
+
import { mergeGroupsIntoRoles, parseRolesYaml } from '../../../../access-model/access-grammar.js';
|
|
13
18
|
import { TemplateFilesStep } from '../template-files.step.js';
|
|
14
19
|
import { buildSeedTree } from '../seed-tree.js';
|
|
15
20
|
import { defaultKbTemplateDir } from '../../../../../assets.js';
|
|
16
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
DEFAULT_KB_LAYOUT,
|
|
23
|
+
agentsFilePointerSentence,
|
|
24
|
+
configureKbLayout,
|
|
25
|
+
renderKbLayoutPlaceholders,
|
|
26
|
+
} from '@bevel-software/platform-shared';
|
|
27
|
+
import { PLATFORM_HEADER, TOOL_PREFIX_LINE, composeAgentInstructions } from '../../../../agent-instructions/index.js';
|
|
17
28
|
|
|
18
29
|
const execFileAsync = promisify(execFile);
|
|
19
30
|
|
|
@@ -61,7 +72,11 @@ afterEach(async () => {
|
|
|
61
72
|
});
|
|
62
73
|
|
|
63
74
|
/** A populated upstream carrying `files`: one commit, both protected refs. Returns the seed clone. */
|
|
64
|
-
async function seedUpstream(
|
|
75
|
+
async function seedUpstream(
|
|
76
|
+
files: Record<string, string>,
|
|
77
|
+
/** Symlinks to seed beside them: path in the repo → the target it points at. */
|
|
78
|
+
symlinks: Record<string, string> = {},
|
|
79
|
+
): Promise<string> {
|
|
65
80
|
const seed = path.join(root, '.seed');
|
|
66
81
|
await fs.mkdir(seed, { recursive: true });
|
|
67
82
|
await git(seed, ['init', '-b', DEFAULT_BRANCH]);
|
|
@@ -70,6 +85,11 @@ async function seedUpstream(files: Record<string, string>): Promise<string> {
|
|
|
70
85
|
await fs.mkdir(path.dirname(abs), { recursive: true });
|
|
71
86
|
await fs.writeFile(abs, content, 'utf8');
|
|
72
87
|
}
|
|
88
|
+
for (const [rel, target] of Object.entries(symlinks)) {
|
|
89
|
+
const abs = path.join(seed, rel);
|
|
90
|
+
await fs.mkdir(path.dirname(abs), { recursive: true });
|
|
91
|
+
await fs.symlink(target, abs);
|
|
92
|
+
}
|
|
73
93
|
await git(seed, ['add', '-A']);
|
|
74
94
|
await git(seed, ['commit', '-m', 'init']);
|
|
75
95
|
await git(seed, ['branch', PROTECTED[1]!]);
|
|
@@ -80,6 +100,7 @@ async function seedUpstream(files: Record<string, string>): Promise<string> {
|
|
|
80
100
|
|
|
81
101
|
function makeRunner(steps: OnServerStart[], templateDir: string = TEMPLATE_DIR) {
|
|
82
102
|
return new KbStartupRunner({
|
|
103
|
+
gitRunner: new NodeGitRunner(),
|
|
83
104
|
kbRepoUrl: () => upstream,
|
|
84
105
|
gitUsername: () => 'x-access-token',
|
|
85
106
|
workspacesRoot,
|
|
@@ -110,7 +131,14 @@ async function exists(dir: string, rel: string): Promise<boolean> {
|
|
|
110
131
|
return fs.access(path.join(dir, rel)).then(() => true, () => false);
|
|
111
132
|
}
|
|
112
133
|
|
|
134
|
+
async function readJson(dir: string, rel: string): Promise<Record<string, any>> {
|
|
135
|
+
return JSON.parse(await fs.readFile(path.join(dir, rel), 'utf8'));
|
|
136
|
+
}
|
|
137
|
+
|
|
113
138
|
const norm = (text: string) => text.replace(/\r\n?/g, '\n');
|
|
139
|
+
const PREAMBLE_IGNORE_BLOCK =
|
|
140
|
+
'\n# Added by the platform: agent instructions are edited from External agent access.\n' +
|
|
141
|
+
'/mcp-description.md\n';
|
|
114
142
|
|
|
115
143
|
/** The template as the step writes it under the default layout — placeholders rendered. */
|
|
116
144
|
async function template(name: string): Promise<string> {
|
|
@@ -124,6 +152,7 @@ async function fullScaffold(): Promise<Record<string, string>> {
|
|
|
124
152
|
'AGENTS.md': await template('AGENTS.md'),
|
|
125
153
|
'.bevelignore': await template('.bevelignore'),
|
|
126
154
|
'.gitignore': await template('gitignore.template'),
|
|
155
|
+
'mcp-description.md': await template('mcp-description.md'),
|
|
127
156
|
'KnowledgeBase/.gitkeep': '',
|
|
128
157
|
'Plugins/.gitkeep': '',
|
|
129
158
|
'Skills/.gitkeep': '',
|
|
@@ -133,11 +162,11 @@ async function fullScaffold(): Promise<Record<string, string>> {
|
|
|
133
162
|
describe('TemplateFilesStep', () => {
|
|
134
163
|
it('adds the missing scaffolding — .gitignore arriving from its packable template spelling', async () => {
|
|
135
164
|
await seedUpstream({ 'marker.txt': 'seeded' });
|
|
136
|
-
await makeRunner([new TemplateFilesStep()]).runAll();
|
|
165
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
137
166
|
|
|
138
167
|
for (const b of PROTECTED) {
|
|
139
168
|
const dir = await checkout(b);
|
|
140
|
-
for (const rel of ['access.md', 'AGENTS.md', '.bevelignore', '.gitignore']) {
|
|
169
|
+
for (const rel of ['access.md', 'AGENTS.md', '.bevelignore', '.gitignore', 'mcp-description.md']) {
|
|
141
170
|
expect(await exists(dir, rel), `${b}: ${rel}`).toBe(true);
|
|
142
171
|
}
|
|
143
172
|
// The packaged template cannot ship a literal .gitignore (npm strips
|
|
@@ -161,7 +190,7 @@ describe('TemplateFilesStep', () => {
|
|
|
161
190
|
// the folders it will find, and an ignore file hiding the real ones.
|
|
162
191
|
configureKbLayout({ knowledgeBaseDir: 'docs', skillsDir: 'skills', pluginsDir: 'plugins' });
|
|
163
192
|
await seedUpstream({ 'marker.txt': 'seeded' });
|
|
164
|
-
await makeRunner([new TemplateFilesStep()]).runAll();
|
|
193
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
165
194
|
|
|
166
195
|
const dir = await checkout(DEFAULT_BRANCH);
|
|
167
196
|
const agents = norm(await fs.readFile(path.join(dir, 'AGENTS.md'), 'utf8'));
|
|
@@ -170,21 +199,91 @@ describe('TemplateFilesStep', () => {
|
|
|
170
199
|
expect(agents).not.toContain('{{');
|
|
171
200
|
expect(agents).not.toContain('KnowledgeBase/');
|
|
172
201
|
const ignore = norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8')).split('\n').map((l) => l.trim());
|
|
173
|
-
|
|
174
|
-
//
|
|
202
|
+
// Both roots stay VISIBLE, whatever they are called: the Skills & Tools
|
|
203
|
+
// sidebar reads each from the workspace tree.
|
|
204
|
+
expect(ignore).not.toContain('plugins/');
|
|
175
205
|
expect(ignore).not.toContain('skills/');
|
|
176
206
|
expect(ignore).not.toContain('Plugins/');
|
|
207
|
+
expect(ignore).toContain('roles.yaml');
|
|
177
208
|
expect(await exists(dir, 'docs/.gitkeep')).toBe(true);
|
|
178
209
|
|
|
179
210
|
// And a second boot sees the rendered guide as current: no churn commit.
|
|
180
|
-
await makeRunner([new TemplateFilesStep()]).runAll();
|
|
211
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
181
212
|
const again = await checkout(DEFAULT_BRANCH);
|
|
182
213
|
expect((await git(again, ['rev-list', '--count', 'HEAD'])).trim()).toBe('2'); // init + scaffolding
|
|
183
214
|
});
|
|
184
215
|
|
|
216
|
+
/**
|
|
217
|
+
* The placement rule. A tester's agent filed a ticket into a plugin folder
|
|
218
|
+
* and explained itself: it had found no convention saying where tickets go,
|
|
219
|
+
* and Plugins was where it already held write rights. The guide named the
|
|
220
|
+
* three roots but never said which KIND of file belongs in which — so the
|
|
221
|
+
* section has to survive into the guide agents actually read, which is this
|
|
222
|
+
* written file (an MCP session reads the branch's copy of it), under the
|
|
223
|
+
* deployment's own root names rather than the placeholders.
|
|
224
|
+
*/
|
|
225
|
+
it('writes the "Where a new file goes" placement rule, named for the deployment\'s roots', async () => {
|
|
226
|
+
configureKbLayout({ knowledgeBaseDir: 'Docs', skillsDir: 'Abilities', pluginsDir: 'Extensions' });
|
|
227
|
+
await seedUpstream({ 'marker.txt': 'seeded' });
|
|
228
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
229
|
+
|
|
230
|
+
const dir = await checkout(DEFAULT_BRANCH);
|
|
231
|
+
const agents = norm(await fs.readFile(path.join(dir, 'AGENTS.md'), 'utf8'));
|
|
232
|
+
const start = agents.indexOf('## Where a new file goes\n');
|
|
233
|
+
expect(start, 'the written guide has no placement section').toBeGreaterThan(-1);
|
|
234
|
+
// The section alone, so a phrase matched elsewhere in the guide cannot
|
|
235
|
+
// stand in for a rule that is missing here.
|
|
236
|
+
const section = agents.slice(start).split('\n## ')[0]!;
|
|
237
|
+
|
|
238
|
+
// A document goes to the knowledge root, a shared skill to the skills
|
|
239
|
+
// root or the plugin's own skills folder, machinery inside a plugin.
|
|
240
|
+
expect(section).toContain('`Docs/`');
|
|
241
|
+
expect(section).toContain('`Abilities/`');
|
|
242
|
+
expect(section).toContain('Extensions/<Plugin>/skills/<skill>/SKILL.md');
|
|
243
|
+
expect(section).toContain('Extensions/<Plugin>/software.bevel.hexis/tools/');
|
|
244
|
+
for (const kind of ['notes', 'reports', 'tickets', 'mcp.json', 'plugin.json']) {
|
|
245
|
+
expect(section, kind).toContain(kind);
|
|
246
|
+
}
|
|
247
|
+
// And the two rules the tester's agent broke.
|
|
248
|
+
expect(section).toMatch(/never holds a document/);
|
|
249
|
+
expect(section).toMatch(/\bask\b/);
|
|
250
|
+
|
|
251
|
+
// Rendered, not raw: a leftover placeholder would name a folder that is
|
|
252
|
+
// not there in a section whose whole job is naming the right folder.
|
|
253
|
+
expect(section).not.toContain('{{');
|
|
254
|
+
expect(section).not.toContain('KnowledgeBase/');
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* The step is built at boot, while the process still runs the defaults; the
|
|
259
|
+
* save that completes first-run setup applies the admin's names afterwards.
|
|
260
|
+
* A root list snapshotted at construction scaffolded `Skills/` beside the
|
|
261
|
+
* `skills/` they had just chosen — the very bug the names exist to avoid.
|
|
262
|
+
*/
|
|
263
|
+
it('scaffolds the layout in effect when it RUNS, not the one it was built under', async () => {
|
|
264
|
+
const builtAtBoot = new TemplateFilesStep(new NodeFs());
|
|
265
|
+
await seedUpstream({ 'skills/deploy/SKILL.md': '# deploy\n' });
|
|
266
|
+
configureKbLayout({ knowledgeBaseDir: 'Docs', skillsDir: 'skills', pluginsDir: 'Plugins' });
|
|
267
|
+
await makeRunner([builtAtBoot]).runAll();
|
|
268
|
+
|
|
269
|
+
const dir = await checkout(DEFAULT_BRANCH);
|
|
270
|
+
// Listed rather than probed: a probe for `Skills` answers yes on a
|
|
271
|
+
// case-insensitive filesystem that holds `skills`.
|
|
272
|
+
const roots = (await fs.readdir(dir, { withFileTypes: true }))
|
|
273
|
+
.filter((e) => e.isDirectory() && e.name !== '.git')
|
|
274
|
+
.map((e) => e.name)
|
|
275
|
+
.sort();
|
|
276
|
+
expect(roots).toEqual(['Docs', 'Plugins', 'skills']);
|
|
277
|
+
expect(await exists(dir, 'Docs/.gitkeep')).toBe(true);
|
|
278
|
+
const subject = (await git(dir, ['log', '--format=%s', '-1'])).trim();
|
|
279
|
+
expect(subject).toContain('Docs/.gitkeep');
|
|
280
|
+
expect(subject).not.toContain('KnowledgeBase/');
|
|
281
|
+
expect(subject).not.toContain('Skills/');
|
|
282
|
+
});
|
|
283
|
+
|
|
185
284
|
it('replaces a drifted AGENTS.md, and says so when that is the only change', async () => {
|
|
186
285
|
await seedUpstream({ ...(await fullScaffold()), 'AGENTS.md': 'stale conventions\n' });
|
|
187
|
-
await makeRunner([new TemplateFilesStep()]).runAll();
|
|
286
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
188
287
|
|
|
189
288
|
const dir = await checkout(DEFAULT_BRANCH);
|
|
190
289
|
expect(norm(await fs.readFile(path.join(dir, 'AGENTS.md'), 'utf8'))).toBe(norm(await template('AGENTS.md')));
|
|
@@ -192,13 +291,79 @@ describe('TemplateFilesStep', () => {
|
|
|
192
291
|
expect(subject).toBe('Update AGENTS.md to the current platform template');
|
|
193
292
|
});
|
|
194
293
|
|
|
195
|
-
it('
|
|
196
|
-
|
|
197
|
-
|
|
294
|
+
it('ships the roles-vs-groups guide, and a deployment on an older template receives it at boot', async () => {
|
|
295
|
+
const guide = await template('AGENTS.md');
|
|
296
|
+
// The section runs from its own heading to the next heading of any level (or the end).
|
|
297
|
+
const section = guide.split(/\n(?=#{1,6} )/).find((s) => s.startsWith('### Roles are pre-set')) ?? '';
|
|
298
|
+
const prose = section.replace(/\s+/g, ' ');
|
|
299
|
+
// What roles are, that agents never create them, the group test, what to do instead.
|
|
300
|
+
expect(prose).toContain('A role in `roles.yaml` is an app role');
|
|
301
|
+
expect(prose).toContain('**Agents never create roles.**');
|
|
302
|
+
expect(prose).toContain('**Is it really a group?**');
|
|
303
|
+
expect(prose).toContain('**What to do instead.**');
|
|
304
|
+
expect(prose).toContain('add people to existing roles, and use a GROUP for a task- or team-scoped set of people');
|
|
305
|
+
|
|
306
|
+
// An existing deployment's AGENTS.md from before the guide existed.
|
|
307
|
+
await seedUpstream({ ...(await fullScaffold()), 'AGENTS.md': guide.replace(section, '') });
|
|
308
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
309
|
+
|
|
310
|
+
const dir = await checkout(DEFAULT_BRANCH);
|
|
311
|
+
expect(norm(await fs.readFile(path.join(dir, 'AGENTS.md'), 'utf8'))).toContain('**Agents never create roles.**');
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('documents giving a role to a group, with a valid example, in the guide the step writes', async () => {
|
|
315
|
+
const guide = await template('AGENTS.md');
|
|
316
|
+
const section = guide.split(/\n(?=#{2,3} )/).find((s) => s.startsWith('### Giving a role to a group')) ?? '';
|
|
317
|
+
const prose = section.replace(/\s+/g, ' ');
|
|
318
|
+
expect(prose).toContain('`- group:<Name>`');
|
|
319
|
+
expect(prose).toContain('case- and whitespace-insensitively against the active group source');
|
|
320
|
+
expect(prose).toContain('validation error');
|
|
321
|
+
expect(prose).toContain('names the entry');
|
|
322
|
+
expect(prose).toContain("removes the role's contribution for everyone in the group");
|
|
323
|
+
// A bare name resolves to a same-named group first, so role denials use `role/`.
|
|
324
|
+
expect(prose).toContain('`deny role/Reviewer`');
|
|
325
|
+
// Closeness first: a person's own entry wins only inside the same access.md.
|
|
326
|
+
expect(prose).toContain('The nearest `access.md` that says anything about the person decides');
|
|
327
|
+
expect(prose).toContain('in the SAME `access.md` as the denial keeps that access');
|
|
328
|
+
expect(prose).toContain('A group under `Admin` makes every member a full admin');
|
|
329
|
+
// A change request cannot carry a roles.yaml edit — the merge restores the
|
|
330
|
+
// base file — so the guide says who changes roles and where, and never
|
|
331
|
+
// walks an agent through drafting one.
|
|
332
|
+
expect(prose).toContain('Only an Admin changes `roles.yaml`');
|
|
333
|
+
expect(prose).toContain('A change request cannot carry the edit');
|
|
334
|
+
expect(prose).toContain('Do not propose one');
|
|
335
|
+
expect(prose).toContain('`edit_file`');
|
|
336
|
+
for (const tool of ['create_branch', 'commit_change', 'open_change_request']) {
|
|
337
|
+
expect(prose).not.toContain(`\`${tool}\``);
|
|
198
338
|
}
|
|
339
|
+
|
|
340
|
+
// The example parses as a roles.yaml whose group entry names a real group.
|
|
341
|
+
const example = /```yaml\n([\s\S]*?)```/.exec(section)?.[1] ?? '';
|
|
342
|
+
expect(example).toContain('- group:Platform Team');
|
|
343
|
+
const parsed = parseRolesYaml(example);
|
|
344
|
+
expect(parsed.ok).toBe(true);
|
|
345
|
+
if (!parsed.ok) return;
|
|
346
|
+
const groups = new Map([['platform team', { displayName: 'Platform Team', emails: new Set(['pat@example.com']) }]]);
|
|
347
|
+
expect(mergeGroupsIntoRoles(parsed.index, groups, 'groups.yaml')).toEqual([]);
|
|
348
|
+
// The example hands out an ordinary role, never Admin.
|
|
349
|
+
expect(parsed.index.byEmail.get('pat@example.com')?.has('reviewer')).toBe(true);
|
|
350
|
+
expect(parsed.index.byEmail.get('pat@example.com')?.has('role/admin')).toBe(false);
|
|
351
|
+
|
|
352
|
+
// What the step writes — the file agents read through the MCP server — carries it.
|
|
353
|
+
await seedUpstream({ ...(await fullScaffold()), 'AGENTS.md': guide.replace(section, '') });
|
|
354
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
355
|
+
const written = norm(await fs.readFile(path.join(await checkout(DEFAULT_BRANCH), 'AGENTS.md'), 'utf8'));
|
|
356
|
+
expect(written).toContain('### Giving a role to a group');
|
|
357
|
+
expect(written).toContain(example);
|
|
199
358
|
});
|
|
200
359
|
|
|
201
|
-
it('
|
|
360
|
+
it('rejects the git folder — in any spelling — as a reserved root name', async () => {
|
|
361
|
+
for (const bad of ['.git', '.GIT', '.Git', '.git.', '%2egit']) {
|
|
362
|
+
expect(() => new TemplateFilesStep(new NodeFs(), [bad]), bad).toThrow(/must not name the git folder/);
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
it('appends the managed root-file rules to a custom template that lacks them', async () => {
|
|
202
367
|
// A distribution's own template whose ignore file does not carry the rule
|
|
203
368
|
// the on-disk merge assumes: the merge only runs against an EXISTING file,
|
|
204
369
|
// so the declared content itself must arrive with the rule in it.
|
|
@@ -210,12 +375,13 @@ describe('TemplateFilesStep', () => {
|
|
|
210
375
|
delete scaffold['.bevelignore']; // the one file the step will declare from the template
|
|
211
376
|
await seedUpstream(scaffold);
|
|
212
377
|
|
|
213
|
-
await makeRunner([new TemplateFilesStep()], customTemplate).runAll();
|
|
378
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())], customTemplate).runAll();
|
|
214
379
|
|
|
215
380
|
const dir = await checkout(DEFAULT_BRANCH);
|
|
216
381
|
const lines = norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8')).split('\n').map((l) => l.trim());
|
|
217
382
|
expect(lines).toContain('MyStuff/'); // the operator's rules survive
|
|
218
383
|
expect(lines).toContain('AGENTS.md'); // the platform's rule was appended
|
|
384
|
+
expect(lines).toContain('/mcp-description.md');
|
|
219
385
|
expect(lines).not.toContain('Skills/'); // never the skills root — the Library's tree needs it
|
|
220
386
|
});
|
|
221
387
|
|
|
@@ -229,13 +395,14 @@ describe('TemplateFilesStep', () => {
|
|
|
229
395
|
'# mine\n.git/\nAGENTS.md\nPlugins/\n\n# Added by the platform: the conventions doc is not node content.\nSkills/\n';
|
|
230
396
|
await seedUpstream(scaffold);
|
|
231
397
|
|
|
232
|
-
await makeRunner([new TemplateFilesStep()]).runAll();
|
|
398
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
233
399
|
|
|
234
400
|
const dir = await checkout(DEFAULT_BRANCH);
|
|
235
401
|
const text = norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8'));
|
|
236
|
-
|
|
402
|
+
// The plugins-root rule goes with it — the same sidebar draws that root now.
|
|
403
|
+
expect(text).toBe('# mine\n.git/\nAGENTS.md\n' + PREAMBLE_IGNORE_BLOCK);
|
|
237
404
|
// Idempotent: a second boot has nothing to change.
|
|
238
|
-
await makeRunner([new TemplateFilesStep()]).runAll();
|
|
405
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
239
406
|
const again = await checkout(DEFAULT_BRANCH);
|
|
240
407
|
expect(norm(await fs.readFile(path.join(again, '.bevelignore'), 'utf8'))).toBe(text);
|
|
241
408
|
});
|
|
@@ -249,10 +416,12 @@ describe('TemplateFilesStep', () => {
|
|
|
249
416
|
'AGENTS.md\nPlugins/\n# The shared-skills root is rendered by the Skills & Tools app, like Groups/.\nSkills/\n';
|
|
250
417
|
await seedUpstream(scaffold);
|
|
251
418
|
|
|
252
|
-
await makeRunner([new TemplateFilesStep()]).runAll();
|
|
419
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
253
420
|
|
|
254
421
|
const dir = await checkout(DEFAULT_BRANCH);
|
|
255
|
-
expect(norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8'))).toBe(
|
|
422
|
+
expect(norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8'))).toBe(
|
|
423
|
+
'AGENTS.md\n' + PREAMBLE_IGNORE_BLOCK,
|
|
424
|
+
);
|
|
256
425
|
});
|
|
257
426
|
|
|
258
427
|
it('recognises the legacy comment for a renamed plugins root with a space in its name', async () => {
|
|
@@ -264,10 +433,12 @@ describe('TemplateFilesStep', () => {
|
|
|
264
433
|
'AGENTS.md\nPlugins/\n# The shared-skills root is rendered by the Skills & Tools app, like My Plugins/.\nSkills/\n';
|
|
265
434
|
await seedUpstream(scaffold);
|
|
266
435
|
|
|
267
|
-
await makeRunner([new TemplateFilesStep()]).runAll();
|
|
436
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
268
437
|
|
|
269
438
|
const dir = await checkout(DEFAULT_BRANCH);
|
|
270
|
-
expect(norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8'))).toBe(
|
|
439
|
+
expect(norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8'))).toBe(
|
|
440
|
+
'AGENTS.md\n' + PREAMBLE_IGNORE_BLOCK,
|
|
441
|
+
);
|
|
271
442
|
});
|
|
272
443
|
|
|
273
444
|
it("keeps an operator's Skills/ rule whose own comment merely opens like the platform's", async () => {
|
|
@@ -275,29 +446,88 @@ describe('TemplateFilesStep', () => {
|
|
|
275
446
|
// same way and goes on differently was never written by the platform.
|
|
276
447
|
const scaffold = await fullScaffold();
|
|
277
448
|
const text =
|
|
278
|
-
'AGENTS.md\
|
|
449
|
+
'AGENTS.md\n# The shared-skills root is rendered by the Skills & Tools app, and I hide it anyway\nSkills/\n';
|
|
279
450
|
scaffold['.bevelignore'] = text;
|
|
280
451
|
await seedUpstream(scaffold);
|
|
281
452
|
|
|
282
|
-
await makeRunner([new TemplateFilesStep()]).runAll();
|
|
453
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
283
454
|
|
|
284
455
|
const dir = await checkout(DEFAULT_BRANCH);
|
|
285
|
-
expect(norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8'))).toBe(
|
|
456
|
+
expect(norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8'))).toBe(
|
|
457
|
+
text + PREAMBLE_IGNORE_BLOCK,
|
|
458
|
+
);
|
|
286
459
|
});
|
|
287
460
|
|
|
288
461
|
it("keeps a Skills/ rule the operator wrote themselves — provenance is the platform's comment", async () => {
|
|
289
462
|
const scaffold = await fullScaffold();
|
|
290
|
-
scaffold['.bevelignore'] = 'AGENTS.md\
|
|
463
|
+
scaffold['.bevelignore'] = 'AGENTS.md\n# I hide skills on purpose\nSkills/\n';
|
|
291
464
|
await seedUpstream(scaffold);
|
|
292
465
|
|
|
293
|
-
await makeRunner([new TemplateFilesStep()]).runAll();
|
|
466
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
294
467
|
|
|
295
468
|
const dir = await checkout(DEFAULT_BRANCH);
|
|
296
469
|
expect(norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8'))).toBe(
|
|
297
|
-
'AGENTS.md\
|
|
470
|
+
'AGENTS.md\n# I hide skills on purpose\nSkills/\n' + PREAMBLE_IGNORE_BLOCK,
|
|
298
471
|
);
|
|
299
472
|
});
|
|
300
473
|
|
|
474
|
+
it('drops EVERY Plugins/ rule, whoever wrote it — the sidebar draws that root now, and the seed left no comment to know it by', async () => {
|
|
475
|
+
// The first template ever seeded hid the plugins root as a bare line at
|
|
476
|
+
// the end of the file, so provenance cannot tell the platform's copy
|
|
477
|
+
// from an operator's; both would empty the Plugins tree, and both go.
|
|
478
|
+
// One under the platform's own comment loses the comment with it; a
|
|
479
|
+
// `!Plugins/` negation is not the rule and stays.
|
|
480
|
+
const scaffold = await fullScaffold();
|
|
481
|
+
scaffold['.bevelignore'] =
|
|
482
|
+
'# mine\nAGENTS.md\nPlugins/\nMy-Own-Rule/\n# Added by the platform: the conventions doc is not node content.\nPlugins/\n!Plugins/\n';
|
|
483
|
+
await seedUpstream(scaffold);
|
|
484
|
+
|
|
485
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
486
|
+
|
|
487
|
+
const dir = await checkout(DEFAULT_BRANCH);
|
|
488
|
+
const text = norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8'));
|
|
489
|
+
expect(text).toBe('# mine\nAGENTS.md\nMy-Own-Rule/\n!Plugins/\n' + PREAMBLE_IGNORE_BLOCK);
|
|
490
|
+
// Idempotent: a second boot has nothing to change.
|
|
491
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
492
|
+
const again = await checkout(DEFAULT_BRANCH);
|
|
493
|
+
expect(norm(await fs.readFile(path.join(again, '.bevelignore'), 'utf8'))).toBe(text);
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
it("declares a custom template's ignore file without the Plugins/ rule it still ships", async () => {
|
|
497
|
+
// A distribution's own template may still carry the rule the packaged
|
|
498
|
+
// one dropped; a KB seeded from it must not start out hiding the root.
|
|
499
|
+
const customTemplate = path.join(root, 'custom-template-plugins-rule');
|
|
500
|
+
await fs.cp(TEMPLATE_DIR, customTemplate, { recursive: true });
|
|
501
|
+
await fs.writeFile(path.join(customTemplate, '.bevelignore'), '.git/\nAGENTS.md\n\nPlugins/\n');
|
|
502
|
+
await seedUpstream({ 'marker.txt': 'seeded' });
|
|
503
|
+
|
|
504
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())], customTemplate).runAll();
|
|
505
|
+
|
|
506
|
+
const dir = await checkout(DEFAULT_BRANCH);
|
|
507
|
+
const lines = norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8')).split('\n').map((l) => l.trim());
|
|
508
|
+
expect(lines).not.toContain('Plugins/');
|
|
509
|
+
expect(lines).toContain('AGENTS.md');
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
it('never seeds a template entry that links into a git folder', async () => {
|
|
513
|
+
// A template that IS a working tree (this repo in a Docker build) can hold
|
|
514
|
+
// a link into its own .git. The name rule cannot see it; the target can.
|
|
515
|
+
const customTemplate = path.join(root, 'custom-template-gitlink');
|
|
516
|
+
await fs.cp(TEMPLATE_DIR, customTemplate, { recursive: true });
|
|
517
|
+
await fs.mkdir(path.join(customTemplate, '.git'), { recursive: true });
|
|
518
|
+
await fs.writeFile(path.join(customTemplate, '.git', 'config'), '[credential]\n\thelper = store\n');
|
|
519
|
+
await fs.symlink(path.join('.git', 'config'), path.join(customTemplate, 'notes.md'));
|
|
520
|
+
|
|
521
|
+
const dir = path.join(root, 'seeded-gitlink');
|
|
522
|
+
await fs.mkdir(dir, { recursive: true });
|
|
523
|
+
await buildSeedTree(new NodeFs(), customTemplate, [], ['admin@example.com'])(dir);
|
|
524
|
+
|
|
525
|
+
await expect(fs.readFile(path.join(dir, 'notes.md'), 'utf8')).rejects.toMatchObject({ code: 'ENOENT' });
|
|
526
|
+
await expect(fs.readdir(path.join(dir, '.git'))).rejects.toMatchObject({ code: 'ENOENT' });
|
|
527
|
+
// The rest of the template still seeded.
|
|
528
|
+
expect(await fs.readFile(path.join(dir, 'AGENTS.md'), 'utf8')).toContain('#');
|
|
529
|
+
});
|
|
530
|
+
|
|
301
531
|
it('seeds a binary template file byte for byte and keeps a script executable — text is what decodes', async () => {
|
|
302
532
|
const customTemplate = path.join(root, 'custom-template-bytes');
|
|
303
533
|
// The packaged template as a base, plus two files it does not ship.
|
|
@@ -312,7 +542,7 @@ describe('TemplateFilesStep', () => {
|
|
|
312
542
|
// The empty-remote seed is the one path that copies a whole template.
|
|
313
543
|
const dir = path.join(root, 'seeded-bytes');
|
|
314
544
|
await fs.mkdir(dir, { recursive: true });
|
|
315
|
-
await buildSeedTree(customTemplate, [], ['admin@example.com'])(dir);
|
|
545
|
+
await buildSeedTree(new NodeFs(),customTemplate, [], ['admin@example.com'])(dir);
|
|
316
546
|
|
|
317
547
|
expect(await fs.readFile(path.join(dir, 'assets', '.logo.bin'))).toEqual(binary);
|
|
318
548
|
expect(norm(await fs.readFile(path.join(dir, 'scripts', 'run.sh'), 'utf8'))).toBe('#!/bin/sh\necho Skills\n');
|
|
@@ -338,7 +568,7 @@ describe('TemplateFilesStep', () => {
|
|
|
338
568
|
delete scaffold['.bevelignore'];
|
|
339
569
|
await seedUpstream(scaffold);
|
|
340
570
|
|
|
341
|
-
await makeRunner([new TemplateFilesStep()], customTemplate).runAll();
|
|
571
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())], customTemplate).runAll();
|
|
342
572
|
|
|
343
573
|
const dir = await checkout(DEFAULT_BRANCH);
|
|
344
574
|
const text = norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8'));
|
|
@@ -349,45 +579,485 @@ describe('TemplateFilesStep', () => {
|
|
|
349
579
|
|
|
350
580
|
it("leaves an operator's !Skills/ negation alone — there is nothing of the platform's to remove", async () => {
|
|
351
581
|
const scaffold = await fullScaffold();
|
|
352
|
-
scaffold['.bevelignore'] = 'AGENTS.md\
|
|
582
|
+
scaffold['.bevelignore'] = 'AGENTS.md\n!Skills/\n';
|
|
353
583
|
await seedUpstream(scaffold);
|
|
354
584
|
|
|
355
|
-
await makeRunner([new TemplateFilesStep()]).runAll();
|
|
585
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
356
586
|
|
|
357
587
|
const dir = await checkout(DEFAULT_BRANCH);
|
|
358
|
-
expect(norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8'))).toBe(
|
|
588
|
+
expect(norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8'))).toBe(
|
|
589
|
+
'AGENTS.md\n!Skills/\n' + PREAMBLE_IGNORE_BLOCK,
|
|
590
|
+
);
|
|
359
591
|
});
|
|
360
592
|
|
|
361
593
|
it('respects an explicit !AGENTS.md negation — hiding the doc is a default, not a mandate', async () => {
|
|
362
594
|
// Appending the positive rule after the negation would WIN under ordered
|
|
363
595
|
// matching and silently defeat the operator's stated choice to show it.
|
|
364
596
|
const scaffold = await fullScaffold();
|
|
365
|
-
scaffold['.bevelignore'] = '# operator wants the doc visible\n!AGENTS.md\
|
|
597
|
+
scaffold['.bevelignore'] = '# operator wants the doc visible\n!AGENTS.md\nMyStuff/\n';
|
|
598
|
+
await seedUpstream(scaffold);
|
|
599
|
+
|
|
600
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
601
|
+
|
|
602
|
+
const dir = await checkout(DEFAULT_BRANCH);
|
|
603
|
+
expect(norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8'))).toBe(
|
|
604
|
+
'# operator wants the doc visible\n!AGENTS.md\nMyStuff/\n' + PREAMBLE_IGNORE_BLOCK,
|
|
605
|
+
);
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
it('respects an explicit !mcp-description.md negation', async () => {
|
|
609
|
+
const scaffold = await fullScaffold();
|
|
610
|
+
scaffold['.bevelignore'] = 'AGENTS.md\n!mcp-description.md\n';
|
|
611
|
+
await seedUpstream(scaffold);
|
|
612
|
+
|
|
613
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
614
|
+
|
|
615
|
+
const dir = await checkout(DEFAULT_BRANCH);
|
|
616
|
+
expect(norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8'))).toBe(
|
|
617
|
+
'AGENTS.md\n!mcp-description.md\n',
|
|
618
|
+
);
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
it("respells the platform's OWN unanchored preamble rule instead of leaving it standing", async () => {
|
|
622
|
+
// What the release that shipped the unanchored spelling wrote. The bare
|
|
623
|
+
// name hides a nested `mcp-description.md` page too, so the platform's own
|
|
624
|
+
// line is corrected rather than treated as somebody's choice.
|
|
625
|
+
const scaffold = await fullScaffold();
|
|
626
|
+
scaffold['.bevelignore'] =
|
|
627
|
+
'AGENTS.md\n# Added by the platform: agent instructions are edited from External agent access.\nmcp-description.md\n';
|
|
628
|
+
await seedUpstream(scaffold);
|
|
629
|
+
|
|
630
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
631
|
+
|
|
632
|
+
const dir = await checkout(DEFAULT_BRANCH);
|
|
633
|
+
expect(norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8'))).toBe(
|
|
634
|
+
'AGENTS.md\n# Added by the platform: agent instructions are edited from External agent access.\n/mcp-description.md\n',
|
|
635
|
+
);
|
|
636
|
+
// Idempotent: a second boot has nothing to change.
|
|
637
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
638
|
+
const again = await checkout(DEFAULT_BRANCH);
|
|
639
|
+
expect(norm(await fs.readFile(path.join(again, '.bevelignore'), 'utf8'))).toBe(
|
|
640
|
+
'AGENTS.md\n# Added by the platform: agent instructions are edited from External agent access.\n/mcp-description.md\n',
|
|
641
|
+
);
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
it("keeps an unanchored preamble rule the operator wrote themselves, and adds nothing beside it", async () => {
|
|
645
|
+
const scaffold = await fullScaffold();
|
|
646
|
+
scaffold['.bevelignore'] = 'AGENTS.md\n# I hide it everywhere on purpose\nmcp-description.md\n';
|
|
366
647
|
await seedUpstream(scaffold);
|
|
367
648
|
|
|
368
|
-
await makeRunner([new TemplateFilesStep()]).runAll();
|
|
649
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
369
650
|
|
|
370
651
|
const dir = await checkout(DEFAULT_BRANCH);
|
|
371
652
|
expect(norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8'))).toBe(
|
|
372
|
-
'#
|
|
653
|
+
'AGENTS.md\n# I hide it everywhere on purpose\nmcp-description.md\n',
|
|
373
654
|
);
|
|
374
655
|
});
|
|
375
656
|
|
|
657
|
+
it('seeds mcp-description.md on every protected branch, from the template, when it is missing', async () => {
|
|
658
|
+
const scaffold = await fullScaffold();
|
|
659
|
+
delete scaffold['mcp-description.md'];
|
|
660
|
+
await seedUpstream(scaffold);
|
|
661
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
662
|
+
|
|
663
|
+
for (const b of PROTECTED) {
|
|
664
|
+
const dir = await checkout(b);
|
|
665
|
+
expect(norm(await fs.readFile(path.join(dir, 'mcp-description.md'), 'utf8'))).toBe(
|
|
666
|
+
norm(await template('mcp-description.md')),
|
|
667
|
+
);
|
|
668
|
+
const subject = (await git(dir, ['log', '--format=%s', '-1'])).trim();
|
|
669
|
+
expect(subject).toBe('Add missing KB scaffolding: mcp-description.md');
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
it('leaves an existing mcp-description.md untouched: seeded once, never refreshed', async () => {
|
|
674
|
+
const mine = 'Acme builds solar farms.\n\n## What is where\n\n- Projects/\n';
|
|
675
|
+
await seedUpstream({ ...(await fullScaffold()), 'mcp-description.md': mine });
|
|
676
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
677
|
+
|
|
678
|
+
const dir = await checkout(DEFAULT_BRANCH);
|
|
679
|
+
expect(norm(await fs.readFile(path.join(dir, 'mcp-description.md'), 'utf8'))).toBe(mine);
|
|
680
|
+
expect((await git(dir, ['rev-list', '--count', 'HEAD'])).trim()).toBe('1'); // init only
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
it('keeps a deliberately emptied mcp-description.md empty: the top-up restores only a MISSING file', async () => {
|
|
684
|
+
await seedUpstream({ ...(await fullScaffold()), 'mcp-description.md': '' });
|
|
685
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
686
|
+
|
|
687
|
+
const dir = await checkout(DEFAULT_BRANCH);
|
|
688
|
+
expect(await fs.readFile(path.join(dir, 'mcp-description.md'), 'utf8')).toBe('');
|
|
689
|
+
expect((await git(dir, ['rev-list', '--count', 'HEAD'])).trim()).toBe('1');
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
it('ships a template that composes to the platform header alone: one comment, nothing broadcast', async () => {
|
|
693
|
+
const shipped = await template('mcp-description.md');
|
|
694
|
+
expect(shipped.trimStart().startsWith('<!--')).toBe(true);
|
|
695
|
+
expect(shipped.trimEnd().endsWith('-->')).toBe(true);
|
|
696
|
+
// The first line says how to turn the text on; the caps are stated inside.
|
|
697
|
+
expect(shipped.split('\n')[0]).toMatch(/Remove this comment wrapper/);
|
|
698
|
+
expect(shipped).toContain('6,000 characters');
|
|
699
|
+
expect(shipped).toContain('first paragraph under about 220');
|
|
700
|
+
expect(shipped).not.toContain('{{');
|
|
701
|
+
const composed = composeAgentInstructions(shipped);
|
|
702
|
+
expect(composed.instructions).toBe(PLATFORM_HEADER);
|
|
703
|
+
expect(composed.toolPrefix).toBe(TOOL_PREFIX_LINE);
|
|
704
|
+
expect(composed.unterminatedComment).toBe(false);
|
|
705
|
+
expect(composed.preambleChars).toBe(0);
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
it('seeds mcp-description.md from the packaged template when a custom template predates it, instead of failing the boot', async () => {
|
|
709
|
+
// A distribution's own KB_TEMPLATE_DIR forked before this file existed:
|
|
710
|
+
// the upgrade must not stop startup over a file whose content is one
|
|
711
|
+
// comment. The packaged copy stands in, and the log says so once.
|
|
712
|
+
const customTemplate = path.join(root, 'custom-template-old');
|
|
713
|
+
await fs.mkdir(customTemplate, { recursive: true });
|
|
714
|
+
await fs.writeFile(path.join(customTemplate, 'AGENTS.md'), await template('AGENTS.md'), 'utf8');
|
|
715
|
+
await fs.writeFile(path.join(customTemplate, 'access.md'), await template('access.md'), 'utf8');
|
|
716
|
+
const scaffold = await fullScaffold();
|
|
717
|
+
delete scaffold['mcp-description.md'];
|
|
718
|
+
await seedUpstream(scaffold);
|
|
719
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
720
|
+
|
|
721
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())], customTemplate).runAll();
|
|
722
|
+
|
|
723
|
+
const dir = await checkout(DEFAULT_BRANCH);
|
|
724
|
+
expect(norm(await fs.readFile(path.join(dir, 'mcp-description.md'), 'utf8'))).toBe(
|
|
725
|
+
norm(await template('mcp-description.md')),
|
|
726
|
+
);
|
|
727
|
+
expect(warn).toHaveBeenCalledWith(expect.stringContaining('no "mcp-description.md"'));
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
it('gives the stand-in to that file only: a custom template missing access.md still fails loudly', async () => {
|
|
731
|
+
const stricter = path.join(root, 'custom-template-broken');
|
|
732
|
+
await fs.mkdir(stricter, { recursive: true });
|
|
733
|
+
await fs.writeFile(path.join(stricter, 'AGENTS.md'), await template('AGENTS.md'), 'utf8');
|
|
734
|
+
const scaffold = await fullScaffold();
|
|
735
|
+
delete scaffold['access.md'];
|
|
736
|
+
await seedUpstream(scaffold);
|
|
737
|
+
await expect(makeRunner([new TemplateFilesStep(new NodeFs())], stricter).runAll()).rejects.toThrow(/ENOENT/);
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
it('points a clone at mcp-description.md from the managed AGENTS.md, before the platform mechanics', async () => {
|
|
741
|
+
const agents = await template('AGENTS.md');
|
|
742
|
+
const pointer = agents.indexOf('mcp-description.md');
|
|
743
|
+
expect(pointer).toBeGreaterThan(-1);
|
|
744
|
+
expect(pointer).toBeLessThan(agents.indexOf('## Directory Structure'));
|
|
745
|
+
expect(agents).toMatch(/default branch's copy inline/);
|
|
746
|
+
});
|
|
747
|
+
|
|
376
748
|
it('treats a CRLF checkout of identical AGENTS.md content as current — no churn commit', async () => {
|
|
377
749
|
const scaffold = await fullScaffold();
|
|
378
750
|
scaffold['AGENTS.md'] = norm(scaffold['AGENTS.md']!).replace(/\n/g, '\r\n');
|
|
379
751
|
await seedUpstream(scaffold);
|
|
380
|
-
await makeRunner([new TemplateFilesStep()]).runAll();
|
|
752
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
381
753
|
|
|
382
754
|
const dir = await checkout(DEFAULT_BRANCH);
|
|
383
755
|
expect((await git(dir, ['rev-list', '--count', 'HEAD'])).trim()).toBe('1'); // init only
|
|
384
756
|
});
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* The guide under a name of the deployment's own — and what becomes of the
|
|
760
|
+
* `AGENTS.md` beside it, which on such a deployment is the CUSTOMER'S file.
|
|
761
|
+
*/
|
|
762
|
+
describe('a renamed agent guide', () => {
|
|
763
|
+
/** The scaffold minus the guide, so each test says for itself what `AGENTS.md` holds. */
|
|
764
|
+
async function scaffoldWithoutGuide(): Promise<Record<string, string>> {
|
|
765
|
+
const scaffold = await fullScaffold();
|
|
766
|
+
delete scaffold['AGENTS.md'];
|
|
767
|
+
return scaffold;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
const CUSTOMER_GUIDE = '# Acme conventions\n\nWrite tickets in the present tense.\n';
|
|
771
|
+
|
|
772
|
+
it('writes and refreshes the guide under the configured name, on every protected branch', async () => {
|
|
773
|
+
configureKbLayout({ ...DEFAULT_KB_LAYOUT, agentsFile: 'HEXIS.md' });
|
|
774
|
+
await seedUpstream({ 'marker.txt': 'seeded' });
|
|
775
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
776
|
+
|
|
777
|
+
for (const branch of PROTECTED) {
|
|
778
|
+
const dir = await checkout(branch);
|
|
779
|
+
const guide = norm(await fs.readFile(path.join(dir, 'HEXIS.md'), 'utf8'));
|
|
780
|
+
// The header names the file it lives in, not the name it ships under.
|
|
781
|
+
expect(guide).toContain('`HEXIS.md`');
|
|
782
|
+
expect(guide).not.toContain('{{');
|
|
783
|
+
expect(await exists(dir, 'AGENTS.md')).toBe(false);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// A second boot sees the rendered guide as current: no churn commit…
|
|
787
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
788
|
+
expect((await git(await checkout(DEFAULT_BRANCH), ['rev-list', '--count', 'HEAD'])).trim()).toBe('2');
|
|
789
|
+
|
|
790
|
+
// …and a drifted copy is replaced, exactly as `AGENTS.md` is by default.
|
|
791
|
+
const dir = await checkout(DEFAULT_BRANCH);
|
|
792
|
+
await fs.writeFile(path.join(dir, 'HEXIS.md'), 'stale conventions\n', 'utf8');
|
|
793
|
+
await git(dir, ['commit', '-am', 'drift']);
|
|
794
|
+
await git(dir, ['push', 'origin', DEFAULT_BRANCH]);
|
|
795
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
796
|
+
const after = await checkout(DEFAULT_BRANCH);
|
|
797
|
+
expect(norm(await fs.readFile(path.join(after, 'HEXIS.md'), 'utf8'))).toContain('# Knowledge base');
|
|
798
|
+
expect((await git(after, ['log', '--format=%s', '-1'])).trim()).toBe(
|
|
799
|
+
'Update HEXIS.md to the current platform template',
|
|
800
|
+
);
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
it('leaves a customer AGENTS.md byte for byte alone, and shows it in the tree', async () => {
|
|
804
|
+
configureKbLayout({ ...DEFAULT_KB_LAYOUT, agentsFile: 'HEXIS.md' });
|
|
805
|
+
// Their file already names the guide, so the pointer has nothing to add
|
|
806
|
+
// — this test is about the bytes and the ignore rules alone.
|
|
807
|
+
const theirs = `${CUSTOMER_GUIDE}\nSee HEXIS.md for the platform.\n`;
|
|
808
|
+
await seedUpstream({ ...(await scaffoldWithoutGuide()), 'AGENTS.md': theirs });
|
|
809
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
810
|
+
|
|
811
|
+
const dir = await checkout(DEFAULT_BRANCH);
|
|
812
|
+
expect(norm(await fs.readFile(path.join(dir, 'AGENTS.md'), 'utf8'))).toBe(theirs);
|
|
813
|
+
expect(await exists(dir, 'HEXIS.md')).toBe(true);
|
|
814
|
+
const ignore = norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8'))
|
|
815
|
+
.split('\n')
|
|
816
|
+
.map((l) => l.trim());
|
|
817
|
+
// The configured file is hidden; the customer's shows.
|
|
818
|
+
expect(ignore).toContain('HEXIS.md');
|
|
819
|
+
expect(ignore).not.toContain('AGENTS.md');
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
it('removes the platform-written AGENTS.md — only its own header proves it ours', async () => {
|
|
823
|
+
// A knowledge base the platform seeded, whose admin then renames the guide.
|
|
824
|
+
await seedUpstream({ 'marker.txt': 'seeded' });
|
|
825
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
826
|
+
expect(await exists(await checkout(DEFAULT_BRANCH), 'AGENTS.md')).toBe(true);
|
|
827
|
+
|
|
828
|
+
configureKbLayout({ ...DEFAULT_KB_LAYOUT, agentsFile: 'HEXIS.md' });
|
|
829
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
830
|
+
|
|
831
|
+
// On EVERY protected branch: the step tops up each one, and a rename
|
|
832
|
+
// that only reached the default branch would leave the others carrying a
|
|
833
|
+
// stale platform guide under the customer's name.
|
|
834
|
+
for (const branch of PROTECTED) {
|
|
835
|
+
const dir = await checkout(branch);
|
|
836
|
+
expect(await exists(dir, 'AGENTS.md'), branch).toBe(false);
|
|
837
|
+
expect(await exists(dir, 'HEXIS.md'), branch).toBe(true);
|
|
838
|
+
const message = await git(dir, ['log', '--format=%B', '-1']);
|
|
839
|
+
expect(message, branch).toContain(
|
|
840
|
+
'Remove the platform-written AGENTS.md — the agent guide is now HEXIS.md',
|
|
841
|
+
);
|
|
842
|
+
}
|
|
843
|
+
});
|
|
844
|
+
|
|
845
|
+
it('keeps an AGENTS.md it cannot prove it wrote, and says so', async () => {
|
|
846
|
+
configureKbLayout({ ...DEFAULT_KB_LAYOUT, agentsFile: 'HEXIS.md' });
|
|
847
|
+
// Edited by the customer: the managed header is gone, so it is theirs.
|
|
848
|
+
const edited = `${CUSTOMER_GUIDE}\nMentions HEXIS.md already.\n`;
|
|
849
|
+
await seedUpstream({ ...(await scaffoldWithoutGuide()), 'AGENTS.md': edited });
|
|
850
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
851
|
+
|
|
852
|
+
for (const branch of PROTECTED) {
|
|
853
|
+
const dir = await checkout(branch);
|
|
854
|
+
expect(norm(await fs.readFile(path.join(dir, 'AGENTS.md'), 'utf8')), branch).toBe(edited);
|
|
855
|
+
expect(await git(dir, ['log', '--format=%B', '-1']), branch).toContain('Keep AGENTS.md');
|
|
856
|
+
}
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
it('leaves a root AGENTS.md that is a symbolic link exactly where it points', async () => {
|
|
860
|
+
// Following the link would read the managed header at the other end and
|
|
861
|
+
// delete the customer's entry — or, with the pointer on, write our
|
|
862
|
+
// sentence into a file nobody asked us to edit.
|
|
863
|
+
configureKbLayout({ ...DEFAULT_KB_LAYOUT, agentsFile: 'HEXIS.md' });
|
|
864
|
+
await seedUpstream({ ...(await scaffoldWithoutGuide()), 'conventions/ours.md': await template('AGENTS.md') });
|
|
865
|
+
// Committed as a link, so every checkout the step makes carries one.
|
|
866
|
+
const seeding = await checkout(DEFAULT_BRANCH);
|
|
867
|
+
await fs.symlink('conventions/ours.md', path.join(seeding, 'AGENTS.md'));
|
|
868
|
+
await git(seeding, ['add', '-A']);
|
|
869
|
+
await git(seeding, ['commit', '-m', 'link AGENTS.md at our own file']);
|
|
870
|
+
await git(seeding, ['push', 'origin', DEFAULT_BRANCH]);
|
|
871
|
+
|
|
872
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
873
|
+
|
|
874
|
+
const dir = await checkout(DEFAULT_BRANCH);
|
|
875
|
+
expect((await fs.lstat(path.join(dir, 'AGENTS.md'))).isSymbolicLink()).toBe(true);
|
|
876
|
+
expect(await fs.readlink(path.join(dir, 'AGENTS.md'))).toBe('conventions/ours.md');
|
|
877
|
+
// The file at the other end is untouched — neither removed nor appended to.
|
|
878
|
+
expect(norm(await fs.readFile(path.join(dir, 'conventions/ours.md'), 'utf8'))).toBe(
|
|
879
|
+
norm(await template('AGENTS.md')),
|
|
880
|
+
);
|
|
881
|
+
});
|
|
882
|
+
|
|
883
|
+
it("keeps an operator's own AGENTS.md ignore rule while dropping the platform's", async () => {
|
|
884
|
+
configureKbLayout({ ...DEFAULT_KB_LAYOUT, agentsFile: 'HEXIS.md' });
|
|
885
|
+
const scaffold = await scaffoldWithoutGuide();
|
|
886
|
+
// Their own rule, written by hand, nowhere near the template's slot.
|
|
887
|
+
scaffold['.bevelignore'] = '# mine\nAGENTS.md\nMyStuff/\n';
|
|
888
|
+
scaffold['AGENTS.md'] = `${CUSTOMER_GUIDE}\nSee HEXIS.md.\n`;
|
|
889
|
+
await seedUpstream(scaffold);
|
|
890
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
891
|
+
|
|
892
|
+
const text = norm(
|
|
893
|
+
await fs.readFile(path.join(await checkout(DEFAULT_BRANCH), '.bevelignore'), 'utf8'),
|
|
894
|
+
);
|
|
895
|
+
expect(text).toContain('# mine\nAGENTS.md\nMyStuff/');
|
|
896
|
+
expect(text.split('\n').map((l) => l.trim())).toContain('HEXIS.md');
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
it("keeps an operator's rule that merely happens to sit under .gitattributes", async () => {
|
|
900
|
+
configureKbLayout({ ...DEFAULT_KB_LAYOUT, agentsFile: 'HEXIS.md' });
|
|
901
|
+
const scaffold = await scaffoldWithoutGuide();
|
|
902
|
+
// Hand-written, and by coincidence in the position the template's own
|
|
903
|
+
// rule used to occupy. One matching line above is not provenance — the
|
|
904
|
+
// block the template wrote is not there, so the rule is theirs.
|
|
905
|
+
scaffold['.bevelignore'] = '# ours\n.gitattributes\nAGENTS.md\nMyStuff/\n';
|
|
906
|
+
scaffold['AGENTS.md'] = `${CUSTOMER_GUIDE}\nSee HEXIS.md.\n`;
|
|
907
|
+
await seedUpstream(scaffold);
|
|
908
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
909
|
+
|
|
910
|
+
const text = norm(
|
|
911
|
+
await fs.readFile(path.join(await checkout(DEFAULT_BRANCH), '.bevelignore'), 'utf8'),
|
|
912
|
+
);
|
|
913
|
+
expect(text).toContain('# ours\n.gitattributes\nAGENTS.md\nMyStuff/');
|
|
914
|
+
expect(text.split('\n').map((l) => l.trim())).toContain('HEXIS.md');
|
|
915
|
+
});
|
|
916
|
+
|
|
917
|
+
it("drops the bare rule an earlier release seeded in the template's hygiene block", async () => {
|
|
918
|
+
configureKbLayout({ ...DEFAULT_KB_LAYOUT, agentsFile: 'HEXIS.md' });
|
|
919
|
+
const scaffold = await scaffoldWithoutGuide();
|
|
920
|
+
// The `.bevelignore` of a knowledge base seeded before the rule had a
|
|
921
|
+
// comment above it: bare, at the end of the template's own block.
|
|
922
|
+
scaffold['.bevelignore'] =
|
|
923
|
+
'# Repo hygiene files that clutter the tree without being node content.\n' +
|
|
924
|
+
'.gitignore\n.gitattributes\nAGENTS.md\nroles.yaml\n';
|
|
925
|
+
scaffold['AGENTS.md'] = `${CUSTOMER_GUIDE}\nSee HEXIS.md.\n`;
|
|
926
|
+
await seedUpstream(scaffold);
|
|
927
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
928
|
+
|
|
929
|
+
const lines = norm(
|
|
930
|
+
await fs.readFile(path.join(await checkout(DEFAULT_BRANCH), '.bevelignore'), 'utf8'),
|
|
931
|
+
)
|
|
932
|
+
.split('\n')
|
|
933
|
+
.map((l) => l.trim());
|
|
934
|
+
// The platform's line goes; the block above it is content and stays.
|
|
935
|
+
expect(lines).not.toContain('AGENTS.md');
|
|
936
|
+
expect(lines).toContain('.gitattributes');
|
|
937
|
+
expect(lines).toContain('.gitignore');
|
|
938
|
+
expect(lines).toContain('HEXIS.md');
|
|
939
|
+
});
|
|
940
|
+
|
|
941
|
+
describe('the pointer in the customer\'s own AGENTS.md', () => {
|
|
942
|
+
/** Boot once with the guide renamed, against a root `AGENTS.md` holding `theirs`. */
|
|
943
|
+
async function bootWith(theirs: string | null, link = true): Promise<string> {
|
|
944
|
+
configureKbLayout({ ...DEFAULT_KB_LAYOUT, agentsFile: 'HEXIS.md' });
|
|
945
|
+
const scaffold = await scaffoldWithoutGuide();
|
|
946
|
+
if (theirs !== null) scaffold['AGENTS.md'] = theirs;
|
|
947
|
+
await seedUpstream(scaffold);
|
|
948
|
+
await makeRunner([new TemplateFilesStep(new NodeFs(), [], () => link)]).runAll();
|
|
949
|
+
return checkout(DEFAULT_BRANCH);
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
const SENTENCE = agentsFilePointerSentence('HEXIS.md');
|
|
953
|
+
|
|
954
|
+
it('appends the sentence when the guide is not mentioned, and says so', async () => {
|
|
955
|
+
const dir = await bootWith(CUSTOMER_GUIDE);
|
|
956
|
+
const text = norm(await fs.readFile(path.join(dir, 'AGENTS.md'), 'utf8'));
|
|
957
|
+
// Their bytes, then a blank line, then exactly the shared sentence.
|
|
958
|
+
expect(text).toBe(`${CUSTOMER_GUIDE.replace(/\n+$/, '')}\n\n${SENTENCE}\n`);
|
|
959
|
+
expect(await git(dir, ['log', '--format=%B', '-1'])).toContain(
|
|
960
|
+
'Add a pointer to HEXIS.md at the end of AGENTS.md',
|
|
961
|
+
);
|
|
962
|
+
});
|
|
963
|
+
|
|
964
|
+
/**
|
|
965
|
+
* The sentence spells a punctuated name twice and neither spelling is
|
|
966
|
+
* the raw one — the label is escaped, the destination percent-encoded.
|
|
967
|
+
* A second boot must still see the guide as mentioned, or it appends
|
|
968
|
+
* another copy, and the boot after that a third.
|
|
969
|
+
*/
|
|
970
|
+
it('appends once and only once, even when the name is nothing but punctuation', async () => {
|
|
971
|
+
const guide = 'Our [Agent] Guide (v2).md';
|
|
972
|
+
configureKbLayout({ ...DEFAULT_KB_LAYOUT, agentsFile: guide });
|
|
973
|
+
await seedUpstream({ ...(await scaffoldWithoutGuide()), 'AGENTS.md': CUSTOMER_GUIDE });
|
|
974
|
+
const boot = () => makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
975
|
+
|
|
976
|
+
await boot();
|
|
977
|
+
const once = norm(await fs.readFile(path.join(await checkout(DEFAULT_BRANCH), 'AGENTS.md'), 'utf8'));
|
|
978
|
+
expect(once).toBe(
|
|
979
|
+
`${CUSTOMER_GUIDE.replace(/\n+$/, '')}\n\n${agentsFilePointerSentence(guide)}\n`,
|
|
980
|
+
);
|
|
981
|
+
|
|
982
|
+
const head = (await git(await checkout(DEFAULT_BRANCH), ['rev-parse', 'HEAD'])).trim();
|
|
983
|
+
await boot();
|
|
984
|
+
const after = await checkout(DEFAULT_BRANCH);
|
|
985
|
+
expect(norm(await fs.readFile(path.join(after, 'AGENTS.md'), 'utf8'))).toBe(once);
|
|
986
|
+
// And the second boot committed nothing at all.
|
|
987
|
+
expect((await git(after, ['rev-parse', 'HEAD'])).trim()).toBe(head);
|
|
988
|
+
});
|
|
989
|
+
|
|
990
|
+
/**
|
|
991
|
+
* The guide's name is a setting, and a setting can be changed twice. The
|
|
992
|
+
* sentence the last rename wrote points at a file that is no longer
|
|
993
|
+
* there, and the new name is nowhere in the text — so "is it mentioned?"
|
|
994
|
+
* says no and an unaimed sentence would be joined by a second one.
|
|
995
|
+
*/
|
|
996
|
+
it('aims the sentence it already wrote at the new guide, rather than adding another', async () => {
|
|
997
|
+
configureKbLayout({ ...DEFAULT_KB_LAYOUT, agentsFile: 'HEXIS.md' });
|
|
998
|
+
await seedUpstream({ ...(await scaffoldWithoutGuide()), 'AGENTS.md': CUSTOMER_GUIDE });
|
|
999
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
1000
|
+
expect(norm(await fs.readFile(path.join(await checkout(DEFAULT_BRANCH), 'AGENTS.md'), 'utf8'))).toContain(
|
|
1001
|
+
agentsFilePointerSentence('HEXIS.md'),
|
|
1002
|
+
);
|
|
1003
|
+
|
|
1004
|
+
// The admin renames the guide a second time.
|
|
1005
|
+
configureKbLayout({ ...DEFAULT_KB_LAYOUT, agentsFile: 'GUIDE.md' });
|
|
1006
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
1007
|
+
|
|
1008
|
+
const dir = await checkout(DEFAULT_BRANCH);
|
|
1009
|
+
const text = norm(await fs.readFile(path.join(dir, 'AGENTS.md'), 'utf8'));
|
|
1010
|
+
// Their bytes, then ONE sentence, pointing at the guide that exists.
|
|
1011
|
+
expect(text).toBe(
|
|
1012
|
+
`${CUSTOMER_GUIDE.replace(/\n+$/, '')}\n\n${agentsFilePointerSentence('GUIDE.md')}\n`,
|
|
1013
|
+
);
|
|
1014
|
+
expect(text).not.toContain('HEXIS.md');
|
|
1015
|
+
expect(await git(dir, ['log', '--format=%B', '-1'])).toContain(
|
|
1016
|
+
'Point the sentence in AGENTS.md at GUIDE.md',
|
|
1017
|
+
);
|
|
1018
|
+
});
|
|
1019
|
+
|
|
1020
|
+
it('writes nothing when the name appears anywhere in the text, in any wording', async () => {
|
|
1021
|
+
const theirs = `${CUSTOMER_GUIDE}\nThe platform keeps its own notes in HEXIS.md; read those too.\n`;
|
|
1022
|
+
const dir = await bootWith(theirs);
|
|
1023
|
+
expect(norm(await fs.readFile(path.join(dir, 'AGENTS.md'), 'utf8'))).toBe(theirs);
|
|
1024
|
+
});
|
|
1025
|
+
|
|
1026
|
+
it('writes nothing while the setting is off', async () => {
|
|
1027
|
+
const dir = await bootWith(CUSTOMER_GUIDE, false);
|
|
1028
|
+
expect(norm(await fs.readFile(path.join(dir, 'AGENTS.md'), 'utf8'))).toBe(CUSTOMER_GUIDE);
|
|
1029
|
+
});
|
|
1030
|
+
|
|
1031
|
+
it('never creates an AGENTS.md for the pointer', async () => {
|
|
1032
|
+
const dir = await bootWith(null);
|
|
1033
|
+
expect(await exists(dir, 'AGENTS.md')).toBe(false);
|
|
1034
|
+
expect(await exists(dir, 'HEXIS.md')).toBe(true);
|
|
1035
|
+
});
|
|
1036
|
+
|
|
1037
|
+
it('never appends to a file the platform wrote — it removes that one instead', async () => {
|
|
1038
|
+
const dir = await bootWith(await template('AGENTS.md'));
|
|
1039
|
+
expect(await exists(dir, 'AGENTS.md')).toBe(false);
|
|
1040
|
+
});
|
|
1041
|
+
|
|
1042
|
+
it('does nothing at all under the default name', async () => {
|
|
1043
|
+
const theirs = `${CUSTOMER_GUIDE}`;
|
|
1044
|
+
// The default guide IS AGENTS.md, so the platform owns that file and
|
|
1045
|
+
// the pointer question never arises: the drifted copy is replaced.
|
|
1046
|
+
await seedUpstream({ ...(await fullScaffold()), 'AGENTS.md': theirs });
|
|
1047
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
1048
|
+
const dir = await checkout(DEFAULT_BRANCH);
|
|
1049
|
+
const text = norm(await fs.readFile(path.join(dir, 'AGENTS.md'), 'utf8'));
|
|
1050
|
+
expect(text).toBe(norm(await template('AGENTS.md')));
|
|
1051
|
+
expect(text).not.toContain(agentsFilePointerSentence('AGENTS.md'));
|
|
1052
|
+
});
|
|
1053
|
+
});
|
|
1054
|
+
});
|
|
385
1055
|
});
|
|
386
1056
|
|
|
387
1057
|
describe('RolesYamlStep', () => {
|
|
388
1058
|
it('generates roles.yaml with the configured admins on every protected branch', async () => {
|
|
389
1059
|
await seedUpstream({ 'marker.txt': 'seeded' });
|
|
390
|
-
await makeRunner([new RolesYamlStep(['admin@example.com'])]).runAll();
|
|
1060
|
+
await makeRunner([new RolesYamlStep(new NodeFs(), ['admin@example.com'])]).runAll();
|
|
391
1061
|
|
|
392
1062
|
for (const b of PROTECTED) {
|
|
393
1063
|
const dir = await checkout(b);
|
|
@@ -405,7 +1075,7 @@ describe('RolesYamlStep', () => {
|
|
|
405
1075
|
// skip-if-present check, reporting success over a KB whose access roster
|
|
406
1076
|
// cannot be read. Fail closed instead.
|
|
407
1077
|
await seedUpstream({ 'roles.yaml/placeholder.txt': 'squatter' });
|
|
408
|
-
await expect(makeRunner([new RolesYamlStep(['admin@example.com'])]).runAll()).rejects.toThrow(
|
|
1078
|
+
await expect(makeRunner([new RolesYamlStep(new NodeFs(), ['admin@example.com'])]).runAll()).rejects.toThrow(
|
|
409
1079
|
/"roles\.yaml" on branch "current-company-state" exists but is not a regular file \(directory\)/,
|
|
410
1080
|
);
|
|
411
1081
|
});
|
|
@@ -413,7 +1083,7 @@ describe('RolesYamlStep', () => {
|
|
|
413
1083
|
it('declares a skip when the file is missing and no admins are configured', async () => {
|
|
414
1084
|
await seedUpstream({ 'marker.txt': 'seeded' });
|
|
415
1085
|
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
416
|
-
await makeRunner([new RolesYamlStep([])]).runAll(); // resolves — a declared skip, not a failure
|
|
1086
|
+
await makeRunner([new RolesYamlStep(new NodeFs(), [])]).runAll(); // resolves — a declared skip, not a failure
|
|
417
1087
|
|
|
418
1088
|
expect(warn.mock.calls.some((c) => String(c[0]).includes('roles-yaml: skipped'))).toBe(true);
|
|
419
1089
|
const dir = await checkout(DEFAULT_BRANCH);
|
|
@@ -459,7 +1129,7 @@ describe('buildSeedTree', () => {
|
|
|
459
1129
|
|
|
460
1130
|
const dest = path.join(root, 'seed-dest');
|
|
461
1131
|
await fs.mkdir(dest, { recursive: true });
|
|
462
|
-
const generated = await buildSeedTree(templateDir, [], ['admin@example.com'])(dest);
|
|
1132
|
+
const generated = await buildSeedTree(new NodeFs(),templateDir, [], ['admin@example.com'])(dest);
|
|
463
1133
|
|
|
464
1134
|
expect(await exists(dest, '.git')).toBe(false);
|
|
465
1135
|
expect(await exists(dest, 'docs/.git')).toBe(false);
|
|
@@ -469,6 +1139,122 @@ describe('buildSeedTree', () => {
|
|
|
469
1139
|
expect(generated.sort()).toEqual(['KnowledgeBase/.gitkeep', 'Plugins/.gitkeep', 'Skills/.gitkeep', 'roles.yaml']);
|
|
470
1140
|
expect(await exists(dest, 'roles.yaml')).toBe(true);
|
|
471
1141
|
});
|
|
1142
|
+
|
|
1143
|
+
it('seeds the layout in effect when it seeds, not the one it was composed under', async () => {
|
|
1144
|
+
const templateDir = path.join(root, 'seed-template-layout');
|
|
1145
|
+
await fs.mkdir(templateDir, { recursive: true });
|
|
1146
|
+
await fs.writeFile(path.join(templateDir, 'access.md'), 'policy', 'utf8');
|
|
1147
|
+
// Composed at boot, under the defaults…
|
|
1148
|
+
const seed = buildSeedTree(new NodeFs(), templateDir, [], ['admin@example.com']);
|
|
1149
|
+
// …and run after the completing save applied the admin's names.
|
|
1150
|
+
configureKbLayout({ knowledgeBaseDir: 'Docs', skillsDir: 'skills', pluginsDir: 'Plugins' });
|
|
1151
|
+
const dest = path.join(root, 'seed-dest-layout');
|
|
1152
|
+
await fs.mkdir(dest, { recursive: true });
|
|
1153
|
+
const generated = await seed(dest);
|
|
1154
|
+
expect(generated.sort()).toEqual(['Docs/.gitkeep', 'Plugins/.gitkeep', 'roles.yaml', 'skills/.gitkeep']);
|
|
1155
|
+
});
|
|
1156
|
+
|
|
1157
|
+
/**
|
|
1158
|
+
* An empty remote seeded by a deployment that renamed its guide. Done in the
|
|
1159
|
+
* SEED rather than left to the top-up step, which would otherwise lay down
|
|
1160
|
+
* `AGENTS.md` and delete it one commit later — two commits saying opposite
|
|
1161
|
+
* things about a knowledge base nobody has used yet.
|
|
1162
|
+
*/
|
|
1163
|
+
it('seeds the packaged guide under the configured name, and no AGENTS.md beside it', async () => {
|
|
1164
|
+
configureKbLayout({ ...DEFAULT_KB_LAYOUT, agentsFile: 'HEXIS.md' });
|
|
1165
|
+
const dest = path.join(root, 'seed-dest-guide');
|
|
1166
|
+
await fs.mkdir(dest, { recursive: true });
|
|
1167
|
+
await buildSeedTree(new NodeFs(), TEMPLATE_DIR, [], ['admin@example.com'])(dest);
|
|
1168
|
+
expect(await exists(dest, 'AGENTS.md')).toBe(false);
|
|
1169
|
+
const guide = norm(await fs.readFile(path.join(dest, 'HEXIS.md'), 'utf8'));
|
|
1170
|
+
expect(guide).toContain('`HEXIS.md`');
|
|
1171
|
+
expect(guide).not.toContain('{{');
|
|
1172
|
+
// And the ignore file hides the name it actually seeded.
|
|
1173
|
+
const ignore = norm(await fs.readFile(path.join(dest, '.bevelignore'), 'utf8'))
|
|
1174
|
+
.split('\n')
|
|
1175
|
+
.map((l) => l.trim());
|
|
1176
|
+
expect(ignore).toContain('HEXIS.md');
|
|
1177
|
+
expect(ignore).not.toContain('AGENTS.md');
|
|
1178
|
+
});
|
|
1179
|
+
|
|
1180
|
+
it('lets the managed guide win a template that carries a root file of the same name', async () => {
|
|
1181
|
+
// A custom KB_TEMPLATE_DIR that happens to ship `HEXIS.md` at its root, on
|
|
1182
|
+
// a deployment that named its guide `HEXIS.md`: two walk entries, one
|
|
1183
|
+
// destination. Whichever the walk reached last used to decide what an
|
|
1184
|
+
// empty deployment was seeded with. The managed guide decides.
|
|
1185
|
+
const customTemplate = path.join(root, 'custom-template-guide-clash');
|
|
1186
|
+
await fs.cp(TEMPLATE_DIR, customTemplate, { recursive: true });
|
|
1187
|
+
await fs.writeFile(path.join(customTemplate, 'HEXIS.md'), '# Not the platform guide\n', 'utf8');
|
|
1188
|
+
|
|
1189
|
+
configureKbLayout({ ...DEFAULT_KB_LAYOUT, agentsFile: 'HEXIS.md' });
|
|
1190
|
+
const dest = path.join(root, 'seed-dest-guide-clash');
|
|
1191
|
+
await fs.mkdir(dest, { recursive: true });
|
|
1192
|
+
await buildSeedTree(new NodeFs(), customTemplate, [], ['admin@example.com'])(dest);
|
|
1193
|
+
|
|
1194
|
+
const guide = norm(await fs.readFile(path.join(dest, 'HEXIS.md'), 'utf8'));
|
|
1195
|
+
expect(guide).toContain('`HEXIS.md`');
|
|
1196
|
+
expect(guide).not.toContain('Not the platform guide');
|
|
1197
|
+
expect(await exists(dest, 'AGENTS.md')).toBe(false);
|
|
1198
|
+
});
|
|
1199
|
+
|
|
1200
|
+
it('names a template that is not there — a missing directory is a broken build, not an empty seed', async () => {
|
|
1201
|
+
const dest = path.join(root, 'seed-dest-missing');
|
|
1202
|
+
await fs.mkdir(dest, { recursive: true });
|
|
1203
|
+
await expect(buildSeedTree(new NodeFs(),path.join(root, 'no-such-template'), [], ['admin@example.com'])(dest)).rejects.toThrow(
|
|
1204
|
+
/KB template ".*no-such-template" is not a directory/,
|
|
1205
|
+
);
|
|
1206
|
+
});
|
|
1207
|
+
|
|
1208
|
+
it('seeds a file the template links to, and refuses a link to anything else', async () => {
|
|
1209
|
+
const templateDir = path.join(root, 'seed-template-links');
|
|
1210
|
+
const elsewhere = path.join(root, 'elsewhere-template');
|
|
1211
|
+
await fs.mkdir(path.join(templateDir, 'docs'), { recursive: true });
|
|
1212
|
+
await fs.mkdir(elsewhere, { recursive: true });
|
|
1213
|
+
await fs.writeFile(path.join(elsewhere, 'shared.md'), 'shared', 'utf8');
|
|
1214
|
+
await fs.writeFile(path.join(templateDir, 'access.md'), 'policy', 'utf8');
|
|
1215
|
+
await fs.symlink(path.join(elsewhere, 'shared.md'), path.join(templateDir, 'docs', 'linked.md'), 'file');
|
|
1216
|
+
|
|
1217
|
+
const dest = path.join(root, 'seed-dest-links');
|
|
1218
|
+
await fs.mkdir(dest, { recursive: true });
|
|
1219
|
+
await buildSeedTree(new NodeFs(),templateDir, [], ['admin@example.com'])(dest);
|
|
1220
|
+
// The link's CONTENT, under the link's own name, as a real file.
|
|
1221
|
+
expect(await fs.readFile(path.join(dest, 'docs/linked.md'), 'utf8')).toBe('shared');
|
|
1222
|
+
expect((await fs.lstat(path.join(dest, 'docs/linked.md'))).isSymbolicLink()).toBe(false);
|
|
1223
|
+
|
|
1224
|
+
// A link to nothing is a broken template, named as such…
|
|
1225
|
+
await fs.symlink(path.join(root, 'nowhere.md'), path.join(templateDir, 'dangling.md'), 'file');
|
|
1226
|
+
const dest2 = path.join(root, 'seed-dest-links-2');
|
|
1227
|
+
await fs.mkdir(dest2, { recursive: true });
|
|
1228
|
+
await expect(buildSeedTree(new NodeFs(),templateDir, [], ['admin@example.com'])(dest2)).rejects.toThrow(
|
|
1229
|
+
/KB template entry "dangling.md" links to nothing/,
|
|
1230
|
+
);
|
|
1231
|
+
// …and so is a link to a folder, named for what it is.
|
|
1232
|
+
await fs.rm(path.join(templateDir, 'dangling.md'));
|
|
1233
|
+
await fs.symlink(elsewhere, path.join(templateDir, 'dirlink'), process.platform === 'win32' ? 'junction' : 'dir');
|
|
1234
|
+
const dest3 = path.join(root, 'seed-dest-links-3');
|
|
1235
|
+
await fs.mkdir(dest3, { recursive: true });
|
|
1236
|
+
await expect(buildSeedTree(new NodeFs(),templateDir, [], ['admin@example.com'])(dest3)).rejects.toThrow(
|
|
1237
|
+
/KB template entry "dirlink" links to a directory/,
|
|
1238
|
+
);
|
|
1239
|
+
});
|
|
1240
|
+
|
|
1241
|
+
it('seeds a template file whose name collides with Object.prototype', async () => {
|
|
1242
|
+
// The packable-spelling lookup is keyed by FILENAME. Asked about a file
|
|
1243
|
+
// called `constructor` an object map answers from the prototype and hands
|
|
1244
|
+
// a function to path.join, failing the seed of a whole knowledge base
|
|
1245
|
+
// with a type error that names nothing.
|
|
1246
|
+
const templateDir = path.join(root, 'proto-template');
|
|
1247
|
+
await fs.mkdir(templateDir, { recursive: true });
|
|
1248
|
+
await fs.writeFile(path.join(templateDir, 'access.md'), 'policy', 'utf8');
|
|
1249
|
+
await fs.writeFile(path.join(templateDir, 'constructor'), 'not a function', 'utf8');
|
|
1250
|
+
await fs.writeFile(path.join(templateDir, 'toString'), 'nor this', 'utf8');
|
|
1251
|
+
|
|
1252
|
+
const dest = path.join(root, 'proto-dest');
|
|
1253
|
+
await fs.mkdir(dest, { recursive: true });
|
|
1254
|
+
await buildSeedTree(new NodeFs(), templateDir, [], ['admin@example.com'])(dest);
|
|
1255
|
+
expect(await fs.readFile(path.join(dest, 'constructor'), 'utf8')).toBe('not a function');
|
|
1256
|
+
expect(await fs.readFile(path.join(dest, 'toString'), 'utf8')).toBe('nor this');
|
|
1257
|
+
});
|
|
472
1258
|
});
|
|
473
1259
|
|
|
474
1260
|
describe('PluginManifestsStep', () => {
|
|
@@ -488,32 +1274,294 @@ describe('PluginManifestsStep', () => {
|
|
|
488
1274
|
'Plugins/functional/access.md': '---\n---\nread:\n - everyone\n',
|
|
489
1275
|
// Nothing plugin-shaped at all.
|
|
490
1276
|
'Plugins/notes/README.md': 'just a folder',
|
|
1277
|
+
// A legacy plugin whose vendored dependency ships a manifest: the walk
|
|
1278
|
+
// does not enter node_modules, so the folder is the plugin, not a
|
|
1279
|
+
// grouping folder over one.
|
|
1280
|
+
'Plugins/Vendored/access.md': '---\n---\nread:\n - everyone\n',
|
|
1281
|
+
'Plugins/Vendored/node_modules/some-pkg/plugin.json': '{"name":"some-pkg"}',
|
|
1282
|
+
// A plain folder whose only SKILL.md is vendored: not a plugin either.
|
|
1283
|
+
'Plugins/deps/node_modules/some-pkg/SKILL.md': '---\ndescription: z\n---\n',
|
|
491
1284
|
});
|
|
492
1285
|
|
|
493
|
-
await makeRunner([new PluginManifestsStep()]).runAll();
|
|
1286
|
+
await makeRunner([new PluginManifestsStep(new NodeFs())]).runAll();
|
|
494
1287
|
|
|
495
1288
|
for (const branch of PROTECTED) {
|
|
496
1289
|
const dir = await checkout(branch);
|
|
497
|
-
for (const legacy of ['GTM', 'Servers', 'Bare']) {
|
|
1290
|
+
for (const legacy of ['GTM', 'Servers', 'Bare', 'Vendored']) {
|
|
498
1291
|
const manifest = JSON.parse(await fs.readFile(path.join(dir, `Plugins/${legacy}/plugin.json`), 'utf8'));
|
|
499
1292
|
expect(manifest.name).toBe(legacy.toLowerCase());
|
|
500
1293
|
}
|
|
501
1294
|
expect(await exists(dir, 'Plugins/functional/plugin.json')).toBe(false);
|
|
502
1295
|
expect(await exists(dir, 'Plugins/notes/plugin.json')).toBe(false);
|
|
1296
|
+
expect(await exists(dir, 'Plugins/deps/plugin.json')).toBe(false);
|
|
503
1297
|
expect(await fs.readFile(path.join(dir, 'Plugins/Modern/plugin.json'), 'utf8')).toBe('{"name":"modern"}');
|
|
504
1298
|
}
|
|
505
1299
|
const dir = await checkout(DEFAULT_BRANCH);
|
|
506
1300
|
const log = (await git(dir, ['log', '-1', '--format=%B'])).trim();
|
|
507
|
-
expect(log).toContain('Add plugin manifests to
|
|
1301
|
+
expect(log).toContain('Add plugin manifests to 4 legacy plugin folders');
|
|
508
1302
|
expect(log).toContain('Plugins/GTM: plugin.json written');
|
|
509
1303
|
|
|
510
1304
|
// Idempotent: nothing left to write on the next boot.
|
|
511
|
-
await makeRunner([new PluginManifestsStep()]).runAll();
|
|
1305
|
+
await makeRunner([new PluginManifestsStep(new NodeFs())]).runAll();
|
|
512
1306
|
const again = await checkout(DEFAULT_BRANCH);
|
|
513
1307
|
expect((await git(again, ['rev-list', '--count', 'HEAD'])).trim()).toBe('2'); // init + one migration commit
|
|
514
1308
|
});
|
|
515
1309
|
});
|
|
516
1310
|
|
|
1311
|
+
describe('PluginDisplayNamesStep', () => {
|
|
1312
|
+
/**
|
|
1313
|
+
* The one-time repair behind the display-name rule: a manifest is now the
|
|
1314
|
+
* only thing any reader consults for what a plugin is called, so every
|
|
1315
|
+
* manifest written under the old rule — which stored the field only when
|
|
1316
|
+
* the folder was spelled differently from the identifier — has to be told
|
|
1317
|
+
* what its plugin was already called, or it would appear to rename itself
|
|
1318
|
+
* on the boot that introduces the rule.
|
|
1319
|
+
*/
|
|
1320
|
+
it('writes the folder\'s spelling into the manifests that lack the field, and touches nothing else', async () => {
|
|
1321
|
+
const scaffold = await fullScaffold();
|
|
1322
|
+
await seedUpstream({
|
|
1323
|
+
...scaffold,
|
|
1324
|
+
// Spelled differently from its identifier and saying nothing about it:
|
|
1325
|
+
// exactly the plugin that would start showing `sales-team`.
|
|
1326
|
+
'Plugins/Sales Team/plugin.json': '{\n "name": "sales-team",\n "version": "1.2.0"\n}\n',
|
|
1327
|
+
'Plugins/Sales Team/access.md': '---\n---\nread:\n - everyone\n',
|
|
1328
|
+
// Nested just as deep as discovery looks.
|
|
1329
|
+
'Plugins/Teams/EU Field/plugin.json': '{"name":"eu-field"}',
|
|
1330
|
+
// Already spelled as its identifier: called the same thing under both
|
|
1331
|
+
// rules, so there is nothing the folder knows that the manifest does not.
|
|
1332
|
+
'Plugins/imported-tools/plugin.json': '{"name":"imported-tools"}',
|
|
1333
|
+
// Already carries the field — the author's own answer, never overwritten,
|
|
1334
|
+
// even though the folder disagrees with both of its names.
|
|
1335
|
+
'Plugins/Ops Desk/plugin.json': '{"name":"ops","displayName":"Operations"}',
|
|
1336
|
+
// A bundle: a foreign repository's file this platform reads, never writes.
|
|
1337
|
+
'Plugins/functional/cluster/example/plugin.bundle.json': '{"name":"example"}',
|
|
1338
|
+
});
|
|
1339
|
+
|
|
1340
|
+
await makeRunner([new PluginDisplayNamesStep(new NodeFs())]).runAll();
|
|
1341
|
+
|
|
1342
|
+
for (const branch of PROTECTED) {
|
|
1343
|
+
const dir = await checkout(branch);
|
|
1344
|
+
// Backfilled from the folder — what the plugin was called the moment
|
|
1345
|
+
// before the boot — with everything else in the manifest kept, and the
|
|
1346
|
+
// field where the renderer puts it.
|
|
1347
|
+
const sales = await readJson(dir, 'Plugins/Sales Team/plugin.json');
|
|
1348
|
+
expect(sales).toEqual({ name: 'sales-team', displayName: 'Sales Team', version: '1.2.0' });
|
|
1349
|
+
expect(Object.keys(sales)).toEqual(['name', 'displayName', 'version']);
|
|
1350
|
+
expect(await readJson(dir, 'Plugins/Teams/EU Field/plugin.json')).toEqual({
|
|
1351
|
+
name: 'eu-field',
|
|
1352
|
+
displayName: 'EU Field',
|
|
1353
|
+
});
|
|
1354
|
+
// Untouched, byte for byte.
|
|
1355
|
+
expect(await fs.readFile(path.join(dir, 'Plugins/imported-tools/plugin.json'), 'utf8')).toBe(
|
|
1356
|
+
'{"name":"imported-tools"}',
|
|
1357
|
+
);
|
|
1358
|
+
expect(await fs.readFile(path.join(dir, 'Plugins/Ops Desk/plugin.json'), 'utf8')).toBe(
|
|
1359
|
+
'{"name":"ops","displayName":"Operations"}',
|
|
1360
|
+
);
|
|
1361
|
+
expect(await fs.readFile(path.join(dir, 'Plugins/functional/cluster/example/plugin.bundle.json'), 'utf8')).toBe(
|
|
1362
|
+
'{"name":"example"}',
|
|
1363
|
+
);
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
const dir = await checkout(DEFAULT_BRANCH);
|
|
1367
|
+
const log = (await git(dir, ['log', '-1', '--format=%B'])).trim();
|
|
1368
|
+
expect(log).toContain('Record the display names of 2 plugins in their manifests');
|
|
1369
|
+
expect(log).toContain('Plugins/Sales Team: displayName "Sales Team"');
|
|
1370
|
+
|
|
1371
|
+
// Idempotent: every manifest it would touch now carries the field.
|
|
1372
|
+
await makeRunner([new PluginDisplayNamesStep(new NodeFs())]).runAll();
|
|
1373
|
+
const again = await checkout(DEFAULT_BRANCH);
|
|
1374
|
+
expect((await git(again, ['rev-list', '--count', 'HEAD'])).trim()).toBe('2'); // init + one backfill commit
|
|
1375
|
+
});
|
|
1376
|
+
|
|
1377
|
+
/**
|
|
1378
|
+
* The three shapes the plain "lacks the field, folder differs" reading gets
|
|
1379
|
+
* wrong: a field that is present and says nothing, a manifest naming the
|
|
1380
|
+
* folder's own spelling without being an identifier, and a `plugin.json`
|
|
1381
|
+
* that is not this platform's file to write.
|
|
1382
|
+
*/
|
|
1383
|
+
it('repairs a blank field, leaves a manifest that already shows its folder, and never writes through a link', async () => {
|
|
1384
|
+
const scaffold = await fullScaffold();
|
|
1385
|
+
await seedUpstream(
|
|
1386
|
+
{
|
|
1387
|
+
...scaffold,
|
|
1388
|
+
// Present but BLANK. The new reader falls through it to `name` where
|
|
1389
|
+
// the old one fell through to the folder, so leaving it be is leaving
|
|
1390
|
+
// the rename this step exists to prevent: blank is absent, and repaired.
|
|
1391
|
+
'Plugins/Growth Team/plugin.json': '{"name":"growth-team","displayName":" "}',
|
|
1392
|
+
// A `name` that is no identifier but IS the folder's spelling: the
|
|
1393
|
+
// identity is the slug `my-plugin`, and what people see is already
|
|
1394
|
+
// "My Plugin". The folder knows nothing the manifest does not.
|
|
1395
|
+
'Plugins/My Plugin/plugin.json': '{"name":"My Plugin"}',
|
|
1396
|
+
// An own `__proto__` key — `JSON.parse` makes it one. Copied as DATA:
|
|
1397
|
+
// assignment would have set the object's prototype instead, dropping
|
|
1398
|
+
// the field and letting a prototype `displayName` stand in for the
|
|
1399
|
+
// one being written.
|
|
1400
|
+
'Plugins/Odd One/plugin.json': '{"name":"odd-one","__proto__":{"displayName":"from the prototype"}}',
|
|
1401
|
+
// A bundle makes this folder a plugin; the `plugin.json` beside it is
|
|
1402
|
+
// a LINK, which discovery does not count as a native manifest.
|
|
1403
|
+
'Plugins/Kit Pro/plugin.bundle.json': '{"name":"kit"}',
|
|
1404
|
+
'vendor-manifest.json': '{"name":"kit"}',
|
|
1405
|
+
},
|
|
1406
|
+
{ 'Plugins/Kit Pro/plugin.json': '../../vendor-manifest.json' },
|
|
1407
|
+
);
|
|
1408
|
+
|
|
1409
|
+
await makeRunner([new PluginDisplayNamesStep(new NodeFs())]).runAll();
|
|
1410
|
+
|
|
1411
|
+
const dir = await checkout(DEFAULT_BRANCH);
|
|
1412
|
+
expect(await readJson(dir, 'Plugins/Growth Team/plugin.json')).toEqual({
|
|
1413
|
+
name: 'growth-team',
|
|
1414
|
+
displayName: 'Growth Team',
|
|
1415
|
+
});
|
|
1416
|
+
// Untouched, byte for byte.
|
|
1417
|
+
expect(await fs.readFile(path.join(dir, 'Plugins/My Plugin/plugin.json'), 'utf8')).toBe('{"name":"My Plugin"}');
|
|
1418
|
+
expect(await fs.readFile(path.join(dir, 'Plugins/Odd One/plugin.json'), 'utf8')).toBe(
|
|
1419
|
+
'{\n "name": "odd-one",\n "displayName": "Odd One",\n "__proto__": {\n "displayName": "from the prototype"\n }\n}\n',
|
|
1420
|
+
);
|
|
1421
|
+
// Still a link, and the file it points at is still the vendor's own.
|
|
1422
|
+
expect((await fs.lstat(path.join(dir, 'Plugins/Kit Pro/plugin.json'))).isSymbolicLink()).toBe(true);
|
|
1423
|
+
expect(await fs.readFile(path.join(dir, 'vendor-manifest.json'), 'utf8')).toBe('{"name":"kit"}');
|
|
1424
|
+
|
|
1425
|
+
const log = (await git(dir, ['log', '-1', '--format=%B'])).trim();
|
|
1426
|
+
expect(log).toContain('Record the display names of 2 plugins in their manifests');
|
|
1427
|
+
expect(log).toContain('Plugins/Growth Team: displayName "Growth Team"');
|
|
1428
|
+
expect(log).not.toContain('Kit Pro');
|
|
1429
|
+
|
|
1430
|
+
// Idempotent over all of it: the next boot finds nothing to write.
|
|
1431
|
+
await makeRunner([new PluginDisplayNamesStep(new NodeFs())]).runAll();
|
|
1432
|
+
const again = await checkout(DEFAULT_BRANCH);
|
|
1433
|
+
expect((await git(again, ['rev-list', '--count', 'HEAD'])).trim()).toBe('2'); // init + one backfill commit
|
|
1434
|
+
});
|
|
1435
|
+
|
|
1436
|
+
it('leaves a plugin the manifests step just made alone — that manifest already says it', async () => {
|
|
1437
|
+
const scaffold = await fullScaffold();
|
|
1438
|
+
await seedUpstream({
|
|
1439
|
+
...scaffold,
|
|
1440
|
+
// Legacy: no manifest at all. The manifests step renders one, which the
|
|
1441
|
+
// renderer has already given both names; the backfill then finds nothing.
|
|
1442
|
+
'Plugins/Sales Team/access.md': '---\n---\nread:\n - everyone\n',
|
|
1443
|
+
});
|
|
1444
|
+
|
|
1445
|
+
await makeRunner([new PluginManifestsStep(new NodeFs()), new PluginDisplayNamesStep(new NodeFs())]).runAll();
|
|
1446
|
+
|
|
1447
|
+
const dir = await checkout(DEFAULT_BRANCH);
|
|
1448
|
+
expect(await readJson(dir, 'Plugins/Sales Team/plugin.json')).toEqual({
|
|
1449
|
+
$schema: 'https://agent-plugins.org/schemas/1.0.0/plugin.schema.json',
|
|
1450
|
+
name: 'sales-team',
|
|
1451
|
+
displayName: 'Sales Team',
|
|
1452
|
+
});
|
|
1453
|
+
// ONE commit — the backfill declared nothing of its own to caption.
|
|
1454
|
+
const log = (await git(dir, ['log', '-1', '--format=%B'])).trim();
|
|
1455
|
+
expect(log).toContain('Add plugin manifests to a legacy plugin folder');
|
|
1456
|
+
expect(log).not.toContain('Record the display name');
|
|
1457
|
+
});
|
|
1458
|
+
});
|
|
1459
|
+
|
|
1460
|
+
describe('PersonalSpacesStep', () => {
|
|
1461
|
+
// A personal folder as the previous template seeded it: the owner's grants
|
|
1462
|
+
// and nothing else — private only while nothing above grants read.
|
|
1463
|
+
const LEGACY_PERSONAL =
|
|
1464
|
+
'---\nread:\n - Ali Vega <ali@x.io>\nwrite:\n - Ali Vega <ali@x.io>\nowner:\n - Ali Vega <ali@x.io>\n---\nread: []\n';
|
|
1465
|
+
|
|
1466
|
+
it("adds `deny everyone` to every personal space's read rules on every branch, keeping the rest, once", async () => {
|
|
1467
|
+
const scaffold = await fullScaffold();
|
|
1468
|
+
const seed = await seedUpstream({
|
|
1469
|
+
...scaffold,
|
|
1470
|
+
'Plugins/personal-u1/plugin.json': '{"name":"personal-u1"}',
|
|
1471
|
+
'Plugins/personal-u1/access.md': LEGACY_PERSONAL,
|
|
1472
|
+
// A space someone already closed (its folder rules stay; only the
|
|
1473
|
+
// file's own block, which said nothing, gets the same statement), one
|
|
1474
|
+
// its owner opened on purpose (a denial beside that grant would be
|
|
1475
|
+
// overruled and read as a contradiction — and the file stays open
|
|
1476
|
+
// with the folder), and a shared plugin: untouched.
|
|
1477
|
+
'Plugins/personal-u2/access.md': '---\nread: []\n---\nread:\n - deny everyone\n - Bo <bo@x.io>\n',
|
|
1478
|
+
'Plugins/personal-u3/access.md': '---\nread: []\n---\nread:\n - everyone\nowner:\n - Cy <cy@x.io>\n',
|
|
1479
|
+
// Entries that say nothing about READ — a denial under write — leave the
|
|
1480
|
+
// space open to an inherited `read: everyone`, so it is closed like any
|
|
1481
|
+
// other.
|
|
1482
|
+
'Plugins/personal-u4/access.md': '---\nread: []\n---\nwrite:\n - deny everyone\nowner:\n - Di <di@x.io>\n',
|
|
1483
|
+
// The old seed's frontmatter grants are carried into the body ONLY where
|
|
1484
|
+
// the body has no word on that person: a denial someone wrote there
|
|
1485
|
+
// stands, and is not overridden by the older grant.
|
|
1486
|
+
'Plugins/personal-u5/access.md': '---\nread:\n - Ed <ed@x.io>\nowner:\n - Ed <ed@x.io>\n---\nread:\n - deny Ed <ed@x.io>\n',
|
|
1487
|
+
// A folder whose read denial is overruled by `write: everyone` (a
|
|
1488
|
+
// same-scope grant wins, and write folds into read) is OPEN: the file
|
|
1489
|
+
// stays open with it, and nothing is written.
|
|
1490
|
+
'Plugins/personal-u6/access.md': '---\nread: []\n---\nread:\n - deny everyone\nwrite:\n - everyone\nowner:\n - Fy <fy@x.io>\n',
|
|
1491
|
+
// `download: everyone` is a deliberate opening now that download folds
|
|
1492
|
+
// into read: the space is already settled OPEN, so nothing is written —
|
|
1493
|
+
// the same treatment `write: everyone` gets above.
|
|
1494
|
+
'Plugins/personal-u7/access.md': '---\nread: []\n---\ndownload:\n - everyone\nowner:\n - Gi <gi@x.io>\n',
|
|
1495
|
+
'Plugins/GTM/plugin.json': '{"name":"gtm"}',
|
|
1496
|
+
'Plugins/GTM/access.md': '---\nread:\n - everyone\n---\nread:\n - Ali Vega <ali@x.io>\n',
|
|
1497
|
+
});
|
|
1498
|
+
// A draft carrying a personal space too: the step visits every branch,
|
|
1499
|
+
// not only the protected ones.
|
|
1500
|
+
await git(seed, ['checkout', '-b', 'ali/draft']);
|
|
1501
|
+
await git(seed, ['push', 'origin', 'ali/draft']);
|
|
1502
|
+
|
|
1503
|
+
await makeRunner([new PersonalSpacesStep(new NodeFs())]).runAll();
|
|
1504
|
+
|
|
1505
|
+
for (const branch of [...PROTECTED, 'ali/draft']) {
|
|
1506
|
+
const dir = await checkout(branch);
|
|
1507
|
+
const closed = norm(await fs.readFile(path.join(dir, 'Plugins/personal-u1/access.md'), 'utf8'));
|
|
1508
|
+
// The body — which governs the folder — now denies everyone (Admin
|
|
1509
|
+
// included: nobody is named) and carries the owner's grants so they
|
|
1510
|
+
// can still read, write and own their space; the old seed's
|
|
1511
|
+
// frontmatter grants stay, joined by the same denial, so the file says
|
|
1512
|
+
// of itself what the folder says.
|
|
1513
|
+
expect(closed).toBe(
|
|
1514
|
+
'---\nread:\n - Ali Vega <ali@x.io>\n - deny everyone\nwrite:\n - Ali Vega <ali@x.io>\nowner:\n - Ali Vega <ali@x.io>\n---\n' +
|
|
1515
|
+
'read:\n - Ali Vega <ali@x.io>\n - deny everyone\n\nwrite:\n - Ali Vega <ali@x.io>\nowner:\n - Ali Vega <ali@x.io>',
|
|
1516
|
+
);
|
|
1517
|
+
// Already closed: the folder rules are untouched, the empty file block
|
|
1518
|
+
// now states the same — the denial, then the people the folder admits.
|
|
1519
|
+
expect(norm(await fs.readFile(path.join(dir, 'Plugins/personal-u2/access.md'), 'utf8'))).toBe(
|
|
1520
|
+
'---\nread:\n - deny everyone\n - Bo <bo@x.io>\n---\nread:\n - deny everyone\n - Bo <bo@x.io>\n',
|
|
1521
|
+
);
|
|
1522
|
+
expect(norm(await fs.readFile(path.join(dir, 'Plugins/personal-u3/access.md'), 'utf8'))).toBe(
|
|
1523
|
+
'---\nread: []\n---\nread:\n - everyone\nowner:\n - Cy <cy@x.io>\n',
|
|
1524
|
+
);
|
|
1525
|
+
const u4 = norm(await fs.readFile(path.join(dir, 'Plugins/personal-u4/access.md'), 'utf8'));
|
|
1526
|
+
expect(u4.slice(0, u4.indexOf('\n---\n', 4))).toBe('---\nread:\n - deny everyone\n - Di <di@x.io>');
|
|
1527
|
+
expect(u4.trimEnd().endsWith('\nread:\n - deny everyone')).toBe(true);
|
|
1528
|
+
expect(u4).toContain('write:\n - deny everyone');
|
|
1529
|
+
expect(u4).toContain('owner:\n - Di <di@x.io>');
|
|
1530
|
+
const u5 = norm(await fs.readFile(path.join(dir, 'Plugins/personal-u5/access.md'), 'utf8'));
|
|
1531
|
+
const u5Body = u5.slice(u5.indexOf('\n---\n', 4) + 5);
|
|
1532
|
+
// Ed's read denial stands — the frontmatter's read grant is NOT carried
|
|
1533
|
+
// beside it — while the owner grant, which the body said nothing about, is.
|
|
1534
|
+
expect(u5Body.match(/read:\n((?: - [^\n]*\n)*)/)![1]).toBe(' - deny Ed <ed@x.io>\n - deny everyone\n');
|
|
1535
|
+
expect(u5Body).toMatch(/owner:\n - Ed <ed@x.io>/);
|
|
1536
|
+
// The file block keeps Ed's grant as written and gains the denial.
|
|
1537
|
+
expect(u5.slice(0, u5.indexOf('\n---\n', 4))).toBe('---\nread:\n - Ed <ed@x.io>\n - deny everyone\nowner:\n - Ed <ed@x.io>');
|
|
1538
|
+
expect(norm(await fs.readFile(path.join(dir, 'Plugins/personal-u6/access.md'), 'utf8'))).toBe(
|
|
1539
|
+
'---\nread: []\n---\nread:\n - deny everyone\nwrite:\n - everyone\nowner:\n - Fy <fy@x.io>\n',
|
|
1540
|
+
);
|
|
1541
|
+
// Untouched: `download: everyone` settles read as GRANTED (download folds
|
|
1542
|
+
// into read), so the step reads the space as opened on purpose.
|
|
1543
|
+
expect(norm(await fs.readFile(path.join(dir, 'Plugins/personal-u7/access.md'), 'utf8'))).toBe(
|
|
1544
|
+
'---\nread: []\n---\ndownload:\n - everyone\nowner:\n - Gi <gi@x.io>\n',
|
|
1545
|
+
);
|
|
1546
|
+
expect(norm(await fs.readFile(path.join(dir, 'Plugins/GTM/access.md'), 'utf8'))).toBe(
|
|
1547
|
+
'---\nread:\n - everyone\n---\nread:\n - Ali Vega <ali@x.io>\n',
|
|
1548
|
+
);
|
|
1549
|
+
}
|
|
1550
|
+
const dir = await checkout(DEFAULT_BRANCH);
|
|
1551
|
+
const log = (await git(dir, ['log', '-1', '--format=%B'])).trim();
|
|
1552
|
+
expect(log).toContain('Keep 4 personal spaces private');
|
|
1553
|
+
expect(log).toContain('Plugins/personal-u1/access.md: read denies everyone');
|
|
1554
|
+
expect(log).toContain('Plugins/personal-u2/access.md: read denies everyone');
|
|
1555
|
+
expect(log).toContain('Plugins/personal-u4/access.md: read denies everyone');
|
|
1556
|
+
expect(log).toContain('Plugins/personal-u5/access.md: read denies everyone');
|
|
1557
|
+
|
|
1558
|
+
// Idempotent: the next boot finds every space already closed.
|
|
1559
|
+
await makeRunner([new PersonalSpacesStep(new NodeFs())]).runAll();
|
|
1560
|
+
const again = await checkout(DEFAULT_BRANCH);
|
|
1561
|
+
expect((await git(again, ['rev-list', '--count', 'HEAD'])).trim()).toBe('2');
|
|
1562
|
+
});
|
|
1563
|
+
});
|
|
1564
|
+
|
|
517
1565
|
describe('GroupsToPluginsStep', () => {
|
|
518
1566
|
it('migrates a Groups/ tree to the Plugins layout — on the protected branches AND a draft', async () => {
|
|
519
1567
|
const seed = await seedUpstream({
|
|
@@ -537,7 +1585,7 @@ describe('GroupsToPluginsStep', () => {
|
|
|
537
1585
|
await git(seed, ['checkout', '-b', 'alice/draft']);
|
|
538
1586
|
await git(seed, ['push', 'origin', 'alice/draft']);
|
|
539
1587
|
|
|
540
|
-
await makeRunner([new GroupsToPluginsStep()]).runAll();
|
|
1588
|
+
await makeRunner([new GroupsToPluginsStep(new NodeFs())]).runAll();
|
|
541
1589
|
|
|
542
1590
|
for (const b of [DEFAULT_BRANCH, 'alice/draft']) {
|
|
543
1591
|
const dir = await checkout(b);
|
|
@@ -552,10 +1600,11 @@ describe('GroupsToPluginsStep', () => {
|
|
|
552
1600
|
expect(mcp.mcpServers.notion).toEqual({ type: 'streamable-http', url: 'https://mcp.notion.com/mcp' });
|
|
553
1601
|
const manifest = JSON.parse(await fs.readFile(path.join(dir, 'Plugins/GTM/plugin.json'), 'utf8'));
|
|
554
1602
|
expect(manifest.name).toBe('gtm');
|
|
555
|
-
// The rename's companion edit: the stale ignore rule
|
|
1603
|
+
// The rename's companion edit: the stale ignore rule is retired — and
|
|
1604
|
+
// not replaced, since the plugins root is drawn by the sidebar now.
|
|
556
1605
|
const ignore = await fs.readFile(path.join(dir, '.bevelignore'), 'utf8');
|
|
557
|
-
expect(ignore).toContain('Plugins/');
|
|
558
1606
|
expect(ignore).not.toContain('Groups/');
|
|
1607
|
+
expect(ignore.split('\n').map((l) => l.trim())).not.toContain('Plugins/');
|
|
559
1608
|
}
|
|
560
1609
|
|
|
561
1610
|
const dir = await checkout(DEFAULT_BRANCH);
|
|
@@ -571,7 +1620,7 @@ describe('GroupsToPluginsStep', () => {
|
|
|
571
1620
|
// silently skipping a branch whose reserved root cannot be a plugin tree
|
|
572
1621
|
// (and on a draft nothing later would ever report it).
|
|
573
1622
|
await seedUpstream({ Plugins: 'i am a file, not a folder' });
|
|
574
|
-
await expect(makeRunner([new GroupsToPluginsStep()]).runAll()).rejects.toThrow(
|
|
1623
|
+
await expect(makeRunner([new GroupsToPluginsStep(new NodeFs())]).runAll()).rejects.toThrow(
|
|
575
1624
|
/"Plugins" exists but is not a directory/,
|
|
576
1625
|
);
|
|
577
1626
|
});
|
|
@@ -583,7 +1632,7 @@ describe('GroupsToPluginsStep', () => {
|
|
|
583
1632
|
'Plugins/B/y.md': 'new',
|
|
584
1633
|
});
|
|
585
1634
|
await makeRunner([
|
|
586
|
-
new GroupsToPluginsStep(),
|
|
1635
|
+
new GroupsToPluginsStep(new NodeFs()),
|
|
587
1636
|
// A later step dirties the branch so the refusal note surfaces in the commit.
|
|
588
1637
|
step('dirty', async (ctx) => {
|
|
589
1638
|
for (const b of await ctx.protectedBranches()) {
|
|
@@ -613,11 +1662,7 @@ describe('GroupsToPluginsStep', () => {
|
|
|
613
1662
|
*/
|
|
614
1663
|
describe('GroupsToPluginsStep — migration edge cases', () => {
|
|
615
1664
|
async function migrate(): Promise<void> {
|
|
616
|
-
await makeRunner([new GroupsToPluginsStep()]).runAll();
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
async function readJson(dir: string, rel: string): Promise<Record<string, any>> {
|
|
620
|
-
return JSON.parse(await fs.readFile(path.join(dir, rel), 'utf8'));
|
|
1665
|
+
await makeRunner([new GroupsToPluginsStep(new NodeFs())]).runAll();
|
|
621
1666
|
}
|
|
622
1667
|
|
|
623
1668
|
/** What the runner's `partial` warning carried — the named refusals. */
|
|
@@ -785,6 +1830,36 @@ describe('GroupsToPluginsStep — migration edge cases', () => {
|
|
|
785
1830
|
});
|
|
786
1831
|
});
|
|
787
1832
|
|
|
1833
|
+
it('leaves a plain folder under the root alone — a .gitkeep is not a plugin, and a grouping folder holding one is not either', async () => {
|
|
1834
|
+
await seedUpstream({
|
|
1835
|
+
'Plugins/GTM/access.md': 'write:\n - Admin\n',
|
|
1836
|
+
// Made with "New folder" in the tree: nothing plugin-shaped in it.
|
|
1837
|
+
'Plugins/TestFolder/.gitkeep': '',
|
|
1838
|
+
// A grouping folder with a plugin INSIDE: a manifest on the folder
|
|
1839
|
+
// would hide the plugin beneath it from every catalog.
|
|
1840
|
+
'Plugins/Teams/Agent Made/plugin.json': '{"name":"agent-made"}',
|
|
1841
|
+
// A legacy plugin whose vendored dependency happens to ship a
|
|
1842
|
+
// manifest: `node_modules` is nothing to the walk, so the folder is
|
|
1843
|
+
// still the plugin it was.
|
|
1844
|
+
'Plugins/Vendored/access.md': 'write:\n - Admin\n',
|
|
1845
|
+
'Plugins/Vendored/node_modules/some-pkg/plugin.json': '{"name":"some-pkg"}',
|
|
1846
|
+
// A folder whose ONLY plugin-shaped entry is one the walk skips. The
|
|
1847
|
+
// migration's own sweep never moves a dot-entry, so counting it as
|
|
1848
|
+
// legacy content wrote a manifest for a folder and then moved nothing
|
|
1849
|
+
// into it — and the manifests step, reading a walk that had already
|
|
1850
|
+
// dropped it, disagreed about the same folder.
|
|
1851
|
+
'Plugins/HiddenOnly/.hidden.tool': 'name: hidden\n',
|
|
1852
|
+
});
|
|
1853
|
+
await migrate();
|
|
1854
|
+
const dir = await checkout(DEFAULT_BRANCH);
|
|
1855
|
+
expect(await exists(dir, 'Plugins/GTM/plugin.json')).toBe(true);
|
|
1856
|
+
expect(await exists(dir, 'Plugins/TestFolder/plugin.json')).toBe(false);
|
|
1857
|
+
expect(await exists(dir, 'Plugins/Teams/plugin.json')).toBe(false);
|
|
1858
|
+
expect(await exists(dir, 'Plugins/Teams/Agent Made/plugin.json')).toBe(true);
|
|
1859
|
+
expect(await exists(dir, 'Plugins/Vendored/plugin.json')).toBe(true);
|
|
1860
|
+
expect(await exists(dir, 'Plugins/HiddenOnly/plugin.json')).toBe(false);
|
|
1861
|
+
});
|
|
1862
|
+
|
|
788
1863
|
it('leaves a personal folder a valid plugin', async () => {
|
|
789
1864
|
await seedUpstream({
|
|
790
1865
|
'Groups/personal-u-123/access.md': 'write:\n - Ali <ali@x.com>\n',
|
|
@@ -796,7 +1871,7 @@ describe('GroupsToPluginsStep — migration edge cases', () => {
|
|
|
796
1871
|
});
|
|
797
1872
|
|
|
798
1873
|
describe('the .bevelignore root rule', () => {
|
|
799
|
-
it('
|
|
1874
|
+
it('retires the rule with the rename — nothing takes its place — preserving every other line', async () => {
|
|
800
1875
|
await seedUpstream({
|
|
801
1876
|
'Groups/GTM/access.md': 'write:\n - Admin\n',
|
|
802
1877
|
'.bevelignore': '# mine\nGroups/\nMy-Own-Rule/\n',
|
|
@@ -804,48 +1879,115 @@ describe('GroupsToPluginsStep — migration edge cases', () => {
|
|
|
804
1879
|
await migrate();
|
|
805
1880
|
const dir = await checkout(DEFAULT_BRANCH);
|
|
806
1881
|
const ignore = norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8'));
|
|
807
|
-
expect(ignore.
|
|
808
|
-
expect(ignore).not.toContain('Groups/');
|
|
809
|
-
expect(ignore).toContain('# mine');
|
|
810
|
-
expect(ignore).toContain('My-Own-Rule/');
|
|
1882
|
+
expect(ignore).toBe('# mine\nMy-Own-Rule/\n');
|
|
811
1883
|
});
|
|
812
1884
|
|
|
813
|
-
it('
|
|
1885
|
+
it('drops EVERY exact Groups/ line, keeping the lines between them', async () => {
|
|
814
1886
|
await seedUpstream({
|
|
815
1887
|
'Groups/GTM/access.md': 'write:\n - Admin\n',
|
|
816
1888
|
'.bevelignore': 'Groups/\n# keep\nGroups/\n',
|
|
817
1889
|
});
|
|
818
1890
|
await migrate();
|
|
819
1891
|
const dir = await checkout(DEFAULT_BRANCH);
|
|
820
|
-
|
|
821
|
-
expect(lines.filter((l) => l.trim() === 'Plugins/')).toHaveLength(1);
|
|
822
|
-
expect(lines.filter((l) => l.trim() === 'Groups/')).toHaveLength(0);
|
|
823
|
-
expect(lines).toContain('# keep');
|
|
1892
|
+
expect(norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8'))).toBe('# keep\n');
|
|
824
1893
|
});
|
|
825
1894
|
|
|
826
|
-
it('
|
|
1895
|
+
it('retires a Plugins/ line beside it too — both root rules are stale for the same reason', async () => {
|
|
827
1896
|
await seedUpstream({
|
|
828
1897
|
'Groups/GTM/access.md': 'write:\n - Admin\n',
|
|
829
|
-
'.bevelignore': '
|
|
1898
|
+
'.bevelignore': '# mine\nGroups/\nPlugins/\n!Plugins/\n',
|
|
1899
|
+
});
|
|
1900
|
+
await migrate();
|
|
1901
|
+
const dir = await checkout(DEFAULT_BRANCH);
|
|
1902
|
+
expect(norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8'))).toBe('# mine\n!Plugins/\n');
|
|
1903
|
+
});
|
|
1904
|
+
|
|
1905
|
+
it('retires the rules on a branch with neither root — nothing to migrate there, and the rules are as stale', async () => {
|
|
1906
|
+
await seedUpstream({
|
|
1907
|
+
'marker.txt': 'seeded',
|
|
1908
|
+
'.bevelignore': 'Plugins/\nGroups/\nkeep/\n',
|
|
1909
|
+
});
|
|
1910
|
+
await migrate();
|
|
1911
|
+
const dir = await checkout(DEFAULT_BRANCH);
|
|
1912
|
+
expect(norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8'))).toBe('keep/\n');
|
|
1913
|
+
const log = (await git(dir, ['log', '-1', '--format=%B'])).trim();
|
|
1914
|
+
expect(log).toContain('.bevelignore: Groups/, Plugins/ dropped');
|
|
1915
|
+
});
|
|
1916
|
+
|
|
1917
|
+
it('names the commit for what happened: a migrated branch that only lost the rules was not reorganised', async () => {
|
|
1918
|
+
await seedUpstream({
|
|
1919
|
+
'Plugins/GTM/plugin.json': '{"name":"gtm"}',
|
|
1920
|
+
'Plugins/GTM/access.md': 'write:\n - Admin\n',
|
|
1921
|
+
'.bevelignore': 'Plugins/\n',
|
|
830
1922
|
});
|
|
831
1923
|
await migrate();
|
|
832
1924
|
const dir = await checkout(DEFAULT_BRANCH);
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
expect(
|
|
1925
|
+
const log = (await git(dir, ['log', '-1', '--format=%B'])).trim();
|
|
1926
|
+
expect(log.split('\n')[0]).toBe('Retire the stale Groups/ and Plugins/ ignore rules');
|
|
1927
|
+
expect(log).not.toContain('Reorganise');
|
|
836
1928
|
});
|
|
837
1929
|
|
|
838
|
-
it('
|
|
1930
|
+
it('retires the rules on a run that does not rename — a branch migrated by an earlier release, a draft included', async () => {
|
|
1931
|
+
// An earlier release renamed `Groups/` to `Plugins/` in the ignore file
|
|
1932
|
+
// of every branch it migrated; the template step retires that line on
|
|
1933
|
+
// the protected branches only, so this step does it wherever it goes.
|
|
839
1934
|
await seedUpstream({
|
|
840
1935
|
'Plugins/GTM/access.md': 'write:\n - Admin\n',
|
|
841
1936
|
'Plugins/GTM/outreach/SKILL.md': '# Outreach\n',
|
|
842
|
-
'.bevelignore': '
|
|
1937
|
+
'.bevelignore': 'AGENTS.md\nPlugins/\n',
|
|
843
1938
|
});
|
|
844
1939
|
await migrate();
|
|
845
1940
|
const dir = await checkout(DEFAULT_BRANCH);
|
|
846
|
-
expect(norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8'))).toBe('
|
|
847
|
-
// The run still reorganised the folder — the rule alone was off-limits.
|
|
1941
|
+
expect(norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8'))).toBe('AGENTS.md\n');
|
|
848
1942
|
expect(await exists(dir, 'Plugins/GTM/skills/outreach/SKILL.md')).toBe(true);
|
|
1943
|
+
// Idempotent: a second run declares nothing.
|
|
1944
|
+
await migrate();
|
|
1945
|
+
expect(norm(await fs.readFile(path.join(await checkout(DEFAULT_BRANCH), '.bevelignore'), 'utf8'))).toBe('AGENTS.md\n');
|
|
849
1946
|
});
|
|
1947
|
+
|
|
1948
|
+
it("takes a platform comment above a stale rule with it, and the blank line that opened the block — the template step's own tidy-up, on a branch it never visits", async () => {
|
|
1949
|
+
await seedUpstream({
|
|
1950
|
+
'Plugins/GTM/access.md': 'write:\n - Admin\n',
|
|
1951
|
+
'.bevelignore':
|
|
1952
|
+
'# mine\n.git/\n\n# Added by the platform: the conventions doc is not node content.\nPlugins/\n# The shared-skills root is rendered by the Skills & Tools app, like Groups/.\nGroups/\n',
|
|
1953
|
+
});
|
|
1954
|
+
await migrate();
|
|
1955
|
+
const dir = await checkout(DEFAULT_BRANCH);
|
|
1956
|
+
expect(norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8'))).toBe('# mine\n.git/\n');
|
|
1957
|
+
});
|
|
1958
|
+
|
|
1959
|
+
it('fails the run when the ignore file cannot be read — a hole is not "no file"', async () => {
|
|
1960
|
+
// A directory where the file should be: readable as neither. Treating
|
|
1961
|
+
// that as absence would leave a possible `Plugins/` rule in place and
|
|
1962
|
+
// report the migration done.
|
|
1963
|
+
await seedUpstream({
|
|
1964
|
+
'Plugins/GTM/access.md': 'write:\n - Admin\n',
|
|
1965
|
+
'.bevelignore/keep': '',
|
|
1966
|
+
});
|
|
1967
|
+
await expect(migrate()).rejects.toThrow(/EISDIR/);
|
|
1968
|
+
});
|
|
1969
|
+
});
|
|
1970
|
+
});
|
|
1971
|
+
|
|
1972
|
+
/**
|
|
1973
|
+
* The guide's rule in `.bevelignore` is a gitignore PATTERN, and the name rules
|
|
1974
|
+
* admit characters gitignore reads as syntax: written bare, `#Guide.md` is a
|
|
1975
|
+
* comment and hides nothing. Rendered escaped, the rule names exactly the file.
|
|
1976
|
+
*/
|
|
1977
|
+
describe('TemplateFilesStep: a guide named with gitignore syntax', () => {
|
|
1978
|
+
it('escapes the name in the ignore rule it writes, and keeps it escaped on the next boot', async () => {
|
|
1979
|
+
configureKbLayout({ ...DEFAULT_KB_LAYOUT, agentsFile: '#Guide[1].md' });
|
|
1980
|
+
await seedUpstream({ 'marker.txt': 'seeded' });
|
|
1981
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
1982
|
+
const dir = await checkout(DEFAULT_BRANCH);
|
|
1983
|
+
const ignore = norm(await fs.readFile(path.join(dir, '.bevelignore'), 'utf8')).split('\n').map((l) => l.trim());
|
|
1984
|
+
const escaped = String.raw`\#Guide\[1\].md`;
|
|
1985
|
+
expect(ignore).toContain(escaped);
|
|
1986
|
+
expect(ignore).not.toContain('#Guide[1].md');
|
|
1987
|
+
expect(await exists(dir, '#Guide[1].md')).toBe(true);
|
|
1988
|
+
// A second boot sees the escaped rule as present and adds no second line.
|
|
1989
|
+
await makeRunner([new TemplateFilesStep(new NodeFs())]).runAll();
|
|
1990
|
+
const again = norm(await fs.readFile(path.join(await checkout(DEFAULT_BRANCH), '.bevelignore'), 'utf8'));
|
|
1991
|
+
expect(again.split('\n').filter((l) => l.trim() === escaped)).toHaveLength(1);
|
|
850
1992
|
});
|
|
851
1993
|
});
|