byterover-cli 2.6.0 → 3.0.1
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/.env.production +1 -0
- package/README.md +240 -14
- package/dist/agent/core/domain/knowledge/conflict-detector.d.ts +38 -0
- package/dist/agent/core/domain/knowledge/conflict-detector.js +71 -0
- package/dist/agent/core/domain/knowledge/conflict-resolver.d.ts +17 -0
- package/dist/agent/core/domain/knowledge/conflict-resolver.js +118 -0
- package/dist/agent/core/domain/knowledge/utils.d.ts +4 -0
- package/dist/agent/core/domain/knowledge/utils.js +6 -0
- package/dist/agent/core/interfaces/i-curate-service.d.ts +6 -0
- package/dist/agent/infra/tools/implementations/curate-tool.d.ts +67 -34
- package/dist/agent/infra/tools/implementations/curate-tool.js +294 -47
- package/dist/agent/resources/prompts/system-prompt.yml +15 -8
- package/dist/agent/resources/tools/code_exec.txt +3 -0
- package/dist/agent/resources/tools/curate.txt +12 -3
- package/dist/oclif/commands/connectors/install.d.ts +2 -1
- package/dist/oclif/commands/connectors/install.js +38 -3
- package/dist/oclif/commands/curate/index.d.ts +18 -0
- package/dist/oclif/commands/curate/index.js +78 -1
- package/dist/oclif/commands/init.d.ts +12 -0
- package/dist/oclif/commands/init.js +75 -0
- package/dist/oclif/commands/locations.js +1 -1
- package/dist/oclif/commands/providers/connect.d.ts +31 -1
- package/dist/oclif/commands/providers/connect.js +307 -27
- package/dist/oclif/commands/pull.d.ts +1 -0
- package/dist/oclif/commands/pull.js +7 -0
- package/dist/oclif/commands/push.d.ts +1 -0
- package/dist/oclif/commands/push.js +8 -0
- package/dist/oclif/commands/review/approve.d.ts +17 -0
- package/dist/oclif/commands/review/approve.js +37 -0
- package/dist/oclif/commands/review/base-review-decision.d.ts +18 -0
- package/dist/oclif/commands/review/base-review-decision.js +71 -0
- package/dist/oclif/commands/review/pending.d.ts +13 -0
- package/dist/oclif/commands/review/pending.js +94 -0
- package/dist/oclif/commands/review/reject.d.ts +17 -0
- package/dist/oclif/commands/review/reject.js +38 -0
- package/dist/oclif/commands/space/list.d.ts +2 -2
- package/dist/oclif/commands/space/list.js +13 -35
- package/dist/oclif/commands/space/switch.d.ts +2 -7
- package/dist/oclif/commands/space/switch.js +13 -56
- package/dist/oclif/commands/status.d.ts +1 -0
- package/dist/oclif/commands/status.js +11 -1
- package/dist/oclif/commands/vc/add.d.ts +7 -0
- package/dist/oclif/commands/vc/add.js +29 -0
- package/dist/oclif/commands/vc/branch.d.ts +15 -0
- package/dist/oclif/commands/vc/branch.js +70 -0
- package/dist/oclif/commands/vc/checkout.d.ts +14 -0
- package/dist/oclif/commands/vc/checkout.js +47 -0
- package/dist/oclif/commands/vc/clone.d.ts +9 -0
- package/dist/oclif/commands/vc/clone.js +61 -0
- package/dist/oclif/commands/vc/commit.d.ts +10 -0
- package/dist/oclif/commands/vc/commit.js +32 -0
- package/dist/oclif/commands/vc/config.d.ts +10 -0
- package/dist/oclif/commands/vc/config.js +30 -0
- package/dist/oclif/commands/vc/fetch.d.ts +10 -0
- package/dist/oclif/commands/vc/fetch.js +42 -0
- package/dist/oclif/commands/vc/index.d.ts +6 -0
- package/dist/oclif/commands/vc/index.js +8 -0
- package/dist/oclif/commands/vc/init.d.ts +6 -0
- package/dist/oclif/commands/vc/init.js +25 -0
- package/dist/oclif/commands/vc/log.d.ts +13 -0
- package/dist/oclif/commands/vc/log.js +48 -0
- package/dist/oclif/commands/vc/merge.d.ts +19 -0
- package/dist/oclif/commands/vc/merge.js +130 -0
- package/dist/oclif/commands/vc/pull.d.ts +13 -0
- package/dist/oclif/commands/vc/pull.js +60 -0
- package/dist/oclif/commands/vc/push.d.ts +13 -0
- package/dist/oclif/commands/vc/push.js +60 -0
- package/dist/oclif/commands/vc/remote/add.d.ts +10 -0
- package/dist/oclif/commands/vc/remote/add.js +30 -0
- package/dist/oclif/commands/vc/remote/index.d.ts +6 -0
- package/dist/oclif/commands/vc/remote/index.js +16 -0
- package/dist/oclif/commands/vc/remote/set-url.d.ts +10 -0
- package/dist/oclif/commands/vc/remote/set-url.js +30 -0
- package/dist/oclif/commands/vc/reset.d.ts +13 -0
- package/dist/oclif/commands/vc/reset.js +62 -0
- package/dist/oclif/commands/vc/status.d.ts +8 -0
- package/dist/oclif/commands/vc/status.js +106 -0
- package/dist/oclif/hooks/init/validate-brv-config.d.ts +26 -0
- package/dist/oclif/hooks/init/validate-brv-config.js +62 -0
- package/dist/oclif/lib/daemon-client.d.ts +2 -0
- package/dist/oclif/lib/daemon-client.js +36 -10
- package/dist/oclif/lib/prompt-utils.d.ts +43 -0
- package/dist/oclif/lib/prompt-utils.js +84 -0
- package/dist/oclif/lib/spinner.d.ts +8 -0
- package/dist/oclif/lib/spinner.js +23 -0
- package/dist/oclif/lib/task-client.d.ts +5 -0
- package/dist/oclif/lib/task-client.js +15 -2
- package/dist/server/config/environment.d.ts +2 -0
- package/dist/server/config/environment.js +2 -0
- package/dist/server/constants.d.ts +3 -0
- package/dist/server/constants.js +9 -0
- package/dist/server/core/domain/entities/auth-token.d.ts +2 -0
- package/dist/server/core/domain/entities/auth-token.js +7 -1
- package/dist/server/core/domain/entities/curate-log-entry.d.ts +11 -0
- package/dist/server/core/domain/entities/space.d.ts +4 -0
- package/dist/server/core/domain/entities/space.js +8 -0
- package/dist/server/core/domain/entities/team.d.ts +2 -0
- package/dist/server/core/domain/entities/team.js +4 -0
- package/dist/server/core/domain/errors/git-error.d.ts +6 -0
- package/dist/server/core/domain/errors/git-error.js +12 -0
- package/dist/server/core/domain/errors/task-error.d.ts +4 -0
- package/dist/server/core/domain/errors/task-error.js +8 -0
- package/dist/server/core/domain/errors/vc-error.d.ts +5 -0
- package/dist/server/core/domain/errors/vc-error.js +8 -0
- package/dist/server/core/domain/knowledge/markdown-writer.d.ts +4 -1
- package/dist/server/core/domain/knowledge/markdown-writer.js +37 -7
- package/dist/server/core/domain/transport/schemas.d.ts +6 -6
- package/dist/server/core/interfaces/context-tree/i-context-tree-service.d.ts +11 -0
- package/dist/server/core/interfaces/process/i-task-lifecycle-hook.d.ts +6 -0
- package/dist/server/core/interfaces/services/i-git-service.d.ts +234 -0
- package/dist/server/core/interfaces/services/i-git-service.js +1 -0
- package/dist/server/core/interfaces/storage/i-curate-log-store.d.ts +5 -0
- package/dist/server/core/interfaces/storage/i-review-backup-store.d.ts +19 -0
- package/dist/server/core/interfaces/storage/i-review-backup-store.js +1 -0
- package/dist/server/core/interfaces/vc/i-vc-git-config-store.d.ts +8 -0
- package/dist/server/core/interfaces/vc/i-vc-git-config-store.js +1 -0
- package/dist/server/infra/config/auto-init.d.ts +0 -2
- package/dist/server/infra/config/auto-init.js +0 -1
- package/dist/server/infra/context-tree/file-context-tree-service.d.ts +2 -0
- package/dist/server/infra/context-tree/file-context-tree-service.js +13 -0
- package/dist/server/infra/daemon/brv-server.js +23 -3
- package/dist/server/infra/git/cogit-url.d.ts +17 -0
- package/dist/server/infra/git/cogit-url.js +39 -0
- package/dist/server/infra/git/git-http-wrapper.d.ts +20 -0
- package/dist/server/infra/git/git-http-wrapper.js +334 -0
- package/dist/server/infra/git/isomorphic-git-service.d.ts +78 -0
- package/dist/server/infra/git/isomorphic-git-service.js +983 -0
- package/dist/server/infra/http/review-api-handler.d.ts +13 -0
- package/dist/server/infra/http/review-api-handler.js +286 -0
- package/dist/server/infra/http/review-ui.d.ts +7 -0
- package/dist/server/infra/http/review-ui.js +606 -0
- package/dist/server/infra/mcp/tools/brv-curate-tool.d.ts +2 -2
- package/dist/server/infra/process/curate-log-handler.d.ts +18 -2
- package/dist/server/infra/process/curate-log-handler.js +50 -13
- package/dist/server/infra/process/feature-handlers.js +41 -1
- package/dist/server/infra/process/task-router.js +16 -0
- package/dist/server/infra/space/http-space-service.js +2 -0
- package/dist/server/infra/storage/file-curate-log-store.d.ts +10 -0
- package/dist/server/infra/storage/file-curate-log-store.js +35 -0
- package/dist/server/infra/storage/file-review-backup-store.d.ts +29 -0
- package/dist/server/infra/storage/file-review-backup-store.js +121 -0
- package/dist/server/infra/transport/handlers/auth-handler.js +9 -5
- package/dist/server/infra/transport/handlers/handler-types.d.ts +9 -0
- package/dist/server/infra/transport/handlers/handler-types.js +11 -0
- package/dist/server/infra/transport/handlers/index.d.ts +4 -0
- package/dist/server/infra/transport/handlers/index.js +2 -0
- package/dist/server/infra/transport/handlers/init-handler.d.ts +1 -0
- package/dist/server/infra/transport/handlers/init-handler.js +13 -1
- package/dist/server/infra/transport/handlers/pull-handler.d.ts +3 -0
- package/dist/server/infra/transport/handlers/pull-handler.js +5 -1
- package/dist/server/infra/transport/handlers/push-handler.d.ts +20 -0
- package/dist/server/infra/transport/handlers/push-handler.js +116 -14
- package/dist/server/infra/transport/handlers/reset-handler.d.ts +11 -0
- package/dist/server/infra/transport/handlers/reset-handler.js +37 -1
- package/dist/server/infra/transport/handlers/review-handler.d.ts +35 -0
- package/dist/server/infra/transport/handlers/review-handler.js +162 -0
- package/dist/server/infra/transport/handlers/space-handler.d.ts +3 -0
- package/dist/server/infra/transport/handlers/space-handler.js +4 -1
- package/dist/server/infra/transport/handlers/status-handler.d.ts +5 -0
- package/dist/server/infra/transport/handlers/status-handler.js +51 -16
- package/dist/server/infra/transport/handlers/vc-handler.d.ts +100 -0
- package/dist/server/infra/transport/handlers/vc-handler.js +1050 -0
- package/dist/server/infra/transport/socket-io-transport-server.d.ts +7 -0
- package/dist/server/infra/transport/socket-io-transport-server.js +12 -1
- package/dist/server/infra/transport/transport-connector.d.ts +1 -1
- package/dist/server/infra/transport/transport-connector.js +2 -1
- package/dist/server/infra/vc/file-vc-git-config-store.d.ts +11 -0
- package/dist/server/infra/vc/file-vc-git-config-store.js +43 -0
- package/dist/server/templates/skill/SKILL.md +167 -33
- package/dist/server/utils/curate-result-parser.d.ts +64 -0
- package/dist/server/utils/curate-result-parser.js +8 -0
- package/dist/server/utils/gitignore.d.ts +9 -0
- package/dist/server/utils/gitignore.js +47 -0
- package/dist/shared/transport/events/index.d.ts +6 -0
- package/dist/shared/transport/events/index.js +3 -0
- package/dist/shared/transport/events/init-events.d.ts +8 -0
- package/dist/shared/transport/events/init-events.js +1 -0
- package/dist/shared/transport/events/push-events.d.ts +6 -0
- package/dist/shared/transport/events/review-events.d.ts +41 -0
- package/dist/shared/transport/events/review-events.js +5 -0
- package/dist/shared/transport/events/vc-events.d.ts +257 -0
- package/dist/shared/transport/events/vc-events.js +67 -0
- package/dist/shared/transport/types/dto.d.ts +6 -1
- package/dist/tui/app/pages/init-project-page.d.ts +9 -0
- package/dist/tui/app/pages/init-project-page.js +54 -0
- package/dist/tui/app/pages/protected-routes.js +14 -6
- package/dist/tui/components/index.d.ts +0 -2
- package/dist/tui/components/index.js +0 -1
- package/dist/tui/features/activity/hooks/use-activity-logs.js +7 -1
- package/dist/tui/features/commands/definitions/index.js +3 -0
- package/dist/tui/features/commands/definitions/space-list.js +9 -18
- package/dist/tui/features/commands/definitions/space-switch.js +10 -6
- package/dist/tui/features/commands/definitions/vc-add.d.ts +2 -0
- package/dist/tui/features/commands/definitions/vc-add.js +15 -0
- package/dist/tui/features/commands/definitions/vc-branch.d.ts +2 -0
- package/dist/tui/features/commands/definitions/vc-branch.js +33 -0
- package/dist/tui/features/commands/definitions/vc-checkout.d.ts +2 -0
- package/dist/tui/features/commands/definitions/vc-checkout.js +32 -0
- package/dist/tui/features/commands/definitions/vc-clone.d.ts +2 -0
- package/dist/tui/features/commands/definitions/vc-clone.js +18 -0
- package/dist/tui/features/commands/definitions/vc-commit.d.ts +2 -0
- package/dist/tui/features/commands/definitions/vc-commit.js +32 -0
- package/dist/tui/features/commands/definitions/vc-config.d.ts +2 -0
- package/dist/tui/features/commands/definitions/vc-config.js +40 -0
- package/dist/tui/features/commands/definitions/vc-fetch.d.ts +2 -0
- package/dist/tui/features/commands/definitions/vc-fetch.js +37 -0
- package/dist/tui/features/commands/definitions/vc-init.d.ts +2 -0
- package/dist/tui/features/commands/definitions/vc-init.js +11 -0
- package/dist/tui/features/commands/definitions/vc-log.d.ts +2 -0
- package/dist/tui/features/commands/definitions/vc-log.js +25 -0
- package/dist/tui/features/commands/definitions/vc-merge.d.ts +2 -0
- package/dist/tui/features/commands/definitions/vc-merge.js +48 -0
- package/dist/tui/features/commands/definitions/vc-pull.d.ts +2 -0
- package/dist/tui/features/commands/definitions/vc-pull.js +42 -0
- package/dist/tui/features/commands/definitions/vc-push.d.ts +2 -0
- package/dist/tui/features/commands/definitions/vc-push.js +38 -0
- package/dist/tui/features/commands/definitions/vc-remote.d.ts +2 -0
- package/dist/tui/features/commands/definitions/vc-remote.js +57 -0
- package/dist/tui/features/commands/definitions/vc-reset.d.ts +2 -0
- package/dist/tui/features/commands/definitions/vc-reset.js +35 -0
- package/dist/tui/features/commands/definitions/vc-status.d.ts +2 -0
- package/dist/tui/features/commands/definitions/vc-status.js +11 -0
- package/dist/tui/features/commands/definitions/vc.d.ts +2 -0
- package/dist/tui/features/commands/definitions/vc.js +36 -0
- package/dist/tui/features/commands/hooks/use-slash-command-processor.js +5 -5
- package/dist/tui/features/log/api/execute-log.d.ts +8 -0
- package/dist/tui/features/log/api/execute-log.js +13 -0
- package/dist/tui/features/log/components/log-flow.d.ts +14 -0
- package/dist/tui/features/log/components/log-flow.js +29 -0
- package/dist/tui/features/log/utils/format-log.d.ts +3 -0
- package/dist/tui/features/log/utils/format-log.js +42 -0
- package/dist/tui/features/onboarding/hooks/use-app-view-mode.d.ts +9 -5
- package/dist/tui/features/onboarding/hooks/use-app-view-mode.js +12 -5
- package/dist/tui/features/push/components/push-flow.js +9 -2
- package/dist/tui/features/reset/components/reset-flow.js +2 -1
- package/dist/tui/features/status/components/status-view.js +2 -1
- package/dist/tui/features/status/utils/format-status.js +9 -0
- package/dist/tui/features/tasks/hooks/use-task-subscriptions.js +11 -0
- package/dist/tui/features/tasks/stores/tasks-store.d.ts +10 -0
- package/dist/tui/features/tasks/stores/tasks-store.js +16 -0
- package/dist/tui/features/vc/add/api/execute-vc-add.d.ts +8 -0
- package/dist/tui/features/vc/add/api/execute-vc-add.js +13 -0
- package/dist/tui/features/vc/add/components/vc-add-flow.d.ts +7 -0
- package/dist/tui/features/vc/add/components/vc-add-flow.js +35 -0
- package/dist/tui/features/vc/branch/api/execute-vc-branch.d.ts +8 -0
- package/dist/tui/features/vc/branch/api/execute-vc-branch.js +13 -0
- package/dist/tui/features/vc/branch/components/vc-branch-flow.d.ts +8 -0
- package/dist/tui/features/vc/branch/components/vc-branch-flow.js +53 -0
- package/dist/tui/features/vc/branch/utils/format-branch.d.ts +4 -0
- package/dist/tui/features/vc/branch/utils/format-branch.js +12 -0
- package/dist/tui/features/vc/checkout/api/execute-vc-checkout.d.ts +8 -0
- package/dist/tui/features/vc/checkout/api/execute-vc-checkout.js +13 -0
- package/dist/tui/features/vc/checkout/components/vc-checkout-flow.d.ts +8 -0
- package/dist/tui/features/vc/checkout/components/vc-checkout-flow.js +33 -0
- package/dist/tui/features/vc/clone/api/execute-vc-clone.d.ts +8 -0
- package/dist/tui/features/vc/clone/api/execute-vc-clone.js +13 -0
- package/dist/tui/features/vc/clone/components/vc-clone-flow.d.ts +7 -0
- package/dist/tui/features/vc/clone/components/vc-clone-flow.js +79 -0
- package/dist/tui/features/vc/commit/api/execute-vc-commit.d.ts +8 -0
- package/dist/tui/features/vc/commit/api/execute-vc-commit.js +13 -0
- package/dist/tui/features/vc/commit/components/vc-commit-flow.d.ts +7 -0
- package/dist/tui/features/vc/commit/components/vc-commit-flow.js +29 -0
- package/dist/tui/features/vc/config/api/execute-vc-config.d.ts +8 -0
- package/dist/tui/features/vc/config/api/execute-vc-config.js +13 -0
- package/dist/tui/features/vc/config/components/vc-config-flow.d.ts +9 -0
- package/dist/tui/features/vc/config/components/vc-config-flow.js +30 -0
- package/dist/tui/features/vc/fetch/api/execute-vc-fetch.d.ts +8 -0
- package/dist/tui/features/vc/fetch/api/execute-vc-fetch.js +13 -0
- package/dist/tui/features/vc/fetch/components/vc-fetch-flow.d.ts +8 -0
- package/dist/tui/features/vc/fetch/components/vc-fetch-flow.js +75 -0
- package/dist/tui/features/vc/init/api/execute-vc-init.d.ts +8 -0
- package/dist/tui/features/vc/init/api/execute-vc-init.js +13 -0
- package/dist/tui/features/vc/init/components/vc-init-flow.d.ts +10 -0
- package/dist/tui/features/vc/init/components/vc-init-flow.js +37 -0
- package/dist/tui/features/vc/merge/api/execute-vc-merge.d.ts +8 -0
- package/dist/tui/features/vc/merge/api/execute-vc-merge.js +13 -0
- package/dist/tui/features/vc/merge/components/vc-merge-flow.d.ts +11 -0
- package/dist/tui/features/vc/merge/components/vc-merge-flow.js +72 -0
- package/dist/tui/features/vc/pull/api/execute-vc-pull.d.ts +8 -0
- package/dist/tui/features/vc/pull/api/execute-vc-pull.js +13 -0
- package/dist/tui/features/vc/pull/components/vc-pull-flow.d.ts +9 -0
- package/dist/tui/features/vc/pull/components/vc-pull-flow.js +83 -0
- package/dist/tui/features/vc/push/api/execute-vc-push.d.ts +8 -0
- package/dist/tui/features/vc/push/api/execute-vc-push.js +13 -0
- package/dist/tui/features/vc/push/components/vc-push-flow.d.ts +8 -0
- package/dist/tui/features/vc/push/components/vc-push-flow.js +83 -0
- package/dist/tui/features/vc/remote/api/execute-vc-remote.d.ts +8 -0
- package/dist/tui/features/vc/remote/api/execute-vc-remote.js +13 -0
- package/dist/tui/features/vc/remote/components/vc-remote-flow.d.ts +9 -0
- package/dist/tui/features/vc/remote/components/vc-remote-flow.js +42 -0
- package/dist/tui/features/vc/reset/api/execute-vc-reset.d.ts +8 -0
- package/dist/tui/features/vc/reset/api/execute-vc-reset.js +13 -0
- package/dist/tui/features/vc/reset/components/vc-reset-flow.d.ts +10 -0
- package/dist/tui/features/vc/reset/components/vc-reset-flow.js +63 -0
- package/dist/tui/features/vc/status/api/execute-vc-status.d.ts +8 -0
- package/dist/tui/features/vc/status/api/execute-vc-status.js +13 -0
- package/dist/tui/features/vc/status/components/vc-status-flow.d.ts +10 -0
- package/dist/tui/features/vc/status/components/vc-status-flow.js +133 -0
- package/dist/tui/lib/environment.d.ts +8 -0
- package/dist/tui/lib/environment.js +8 -0
- package/dist/tui/utils/error-messages.d.ts +5 -1
- package/dist/tui/utils/error-messages.js +32 -3
- package/oclif.manifest.json +1018 -98
- package/package.json +9 -3
- package/dist/oclif/hooks/prerun/validate-brv-config-version.d.ts +0 -33
- package/dist/oclif/hooks/prerun/validate-brv-config-version.js +0 -86
- package/dist/tui/components/init.d.ts +0 -33
- package/dist/tui/components/init.js +0 -234
- package/dist/tui/features/space/api/get-spaces.d.ts +0 -16
- package/dist/tui/features/space/api/get-spaces.js +0 -17
- package/dist/tui/features/space/api/switch-space.d.ts +0 -11
- package/dist/tui/features/space/api/switch-space.js +0 -24
- package/dist/tui/features/space/components/space-list-view.d.ts +0 -12
- package/dist/tui/features/space/components/space-list-view.js +0 -56
- package/dist/tui/features/space/components/space-switch-flow.d.ts +0 -13
- package/dist/tui/features/space/components/space-switch-flow.js +0 -97
|
@@ -5,8 +5,9 @@ import { BrvConfig } from '../../../core/domain/entities/brv-config.js';
|
|
|
5
5
|
import { NotAuthenticatedError, SpaceNotFoundError } from '../../../core/domain/errors/task-error.js';
|
|
6
6
|
import { syncConfigToXdg } from '../../../utils/config-xdg-sync.js';
|
|
7
7
|
import { getErrorMessage } from '../../../utils/error-helpers.js';
|
|
8
|
+
import { ensureProjectInitialized } from '../../config/auto-init.js';
|
|
8
9
|
import { mapAgentsToDTOs } from './agent-dto-mapper.js';
|
|
9
|
-
import { resolveRequiredProjectPath } from './handler-types.js';
|
|
10
|
+
import { guardAgainstGitVc, resolveRequiredProjectPath, } from './handler-types.js';
|
|
10
11
|
/**
|
|
11
12
|
* Handles init:* events.
|
|
12
13
|
* Business logic for project initialization — no terminal/UI calls.
|
|
@@ -44,9 +45,11 @@ export class InitHandler {
|
|
|
44
45
|
this.transport.onRequest(InitEvents.GET_SPACES, (data) => this.handleGetSpaces(data));
|
|
45
46
|
this.transport.onRequest(InitEvents.GET_AGENTS, () => this.handleGetAgents());
|
|
46
47
|
this.transport.onRequest(InitEvents.EXECUTE, (data, clientId) => this.handleExecute(data, clientId));
|
|
48
|
+
this.transport.onRequest(InitEvents.LOCAL, (data, clientId) => this.handleLocalInit(data, clientId));
|
|
47
49
|
}
|
|
48
50
|
async handleExecute(data, clientId) {
|
|
49
51
|
const projectPath = resolveRequiredProjectPath(this.resolveProjectPath, clientId);
|
|
52
|
+
await guardAgainstGitVc({ contextTreeService: this.contextTreeService, projectPath });
|
|
50
53
|
const token = await this.tokenStore.load();
|
|
51
54
|
if (!token || !token.isValid()) {
|
|
52
55
|
throw new NotAuthenticatedError();
|
|
@@ -154,4 +157,13 @@ export class InitHandler {
|
|
|
154
157
|
})),
|
|
155
158
|
};
|
|
156
159
|
}
|
|
160
|
+
async handleLocalInit(data, clientId) {
|
|
161
|
+
const projectPath = resolveRequiredProjectPath(this.resolveProjectPath, clientId);
|
|
162
|
+
const exists = await this.projectConfigStore.exists(projectPath);
|
|
163
|
+
if (exists && !data.force) {
|
|
164
|
+
return { alreadyInitialized: true, success: true };
|
|
165
|
+
}
|
|
166
|
+
await ensureProjectInitialized({ contextTreeService: this.contextTreeService, projectConfigStore: this.projectConfigStore }, projectPath);
|
|
167
|
+
return { alreadyInitialized: false, success: true };
|
|
168
|
+
}
|
|
157
169
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ITokenStore } from '../../../core/interfaces/auth/i-token-store.js';
|
|
2
|
+
import type { IContextTreeService } from '../../../core/interfaces/context-tree/i-context-tree-service.js';
|
|
2
3
|
import type { IContextTreeSnapshotService } from '../../../core/interfaces/context-tree/i-context-tree-snapshot-service.js';
|
|
3
4
|
import type { IContextTreeWriterService } from '../../../core/interfaces/context-tree/i-context-tree-writer-service.js';
|
|
4
5
|
import type { ICogitPullService } from '../../../core/interfaces/services/i-cogit-pull-service.js';
|
|
@@ -8,6 +9,7 @@ import { type ProjectBroadcaster, type ProjectPathResolver } from './handler-typ
|
|
|
8
9
|
export interface PullHandlerDeps {
|
|
9
10
|
broadcastToProject: ProjectBroadcaster;
|
|
10
11
|
cogitPullService: ICogitPullService;
|
|
12
|
+
contextTreeService: IContextTreeService;
|
|
11
13
|
contextTreeSnapshotService: IContextTreeSnapshotService;
|
|
12
14
|
contextTreeWriterService: IContextTreeWriterService;
|
|
13
15
|
projectConfigStore: IProjectConfigStore;
|
|
@@ -22,6 +24,7 @@ export interface PullHandlerDeps {
|
|
|
22
24
|
export declare class PullHandler {
|
|
23
25
|
private readonly broadcastToProject;
|
|
24
26
|
private readonly cogitPullService;
|
|
27
|
+
private readonly contextTreeService;
|
|
25
28
|
private readonly contextTreeSnapshotService;
|
|
26
29
|
private readonly contextTreeWriterService;
|
|
27
30
|
private readonly projectConfigStore;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PullEvents, } from '../../../../shared/transport/events/pull-events.js';
|
|
2
2
|
import { LocalChangesExistError, NotAuthenticatedError, ProjectNotInitError, SpaceNotConfiguredError, } from '../../../core/domain/errors/task-error.js';
|
|
3
|
-
import { hasAnyChanges, resolveRequiredProjectPath } from './handler-types.js';
|
|
3
|
+
import { guardAgainstGitVc, hasAnyChanges, resolveRequiredProjectPath, } from './handler-types.js';
|
|
4
4
|
/**
|
|
5
5
|
* Handles pull:* events.
|
|
6
6
|
* Business logic for pulling context tree from cloud — no terminal/UI calls.
|
|
@@ -8,6 +8,7 @@ import { hasAnyChanges, resolveRequiredProjectPath } from './handler-types.js';
|
|
|
8
8
|
export class PullHandler {
|
|
9
9
|
broadcastToProject;
|
|
10
10
|
cogitPullService;
|
|
11
|
+
contextTreeService;
|
|
11
12
|
contextTreeSnapshotService;
|
|
12
13
|
contextTreeWriterService;
|
|
13
14
|
projectConfigStore;
|
|
@@ -17,6 +18,7 @@ export class PullHandler {
|
|
|
17
18
|
constructor(deps) {
|
|
18
19
|
this.broadcastToProject = deps.broadcastToProject;
|
|
19
20
|
this.cogitPullService = deps.cogitPullService;
|
|
21
|
+
this.contextTreeService = deps.contextTreeService;
|
|
20
22
|
this.contextTreeSnapshotService = deps.contextTreeSnapshotService;
|
|
21
23
|
this.contextTreeWriterService = deps.contextTreeWriterService;
|
|
22
24
|
this.projectConfigStore = deps.projectConfigStore;
|
|
@@ -30,6 +32,7 @@ export class PullHandler {
|
|
|
30
32
|
}
|
|
31
33
|
async handleExecute(data, clientId) {
|
|
32
34
|
const projectPath = resolveRequiredProjectPath(this.resolveProjectPath, clientId);
|
|
35
|
+
await guardAgainstGitVc({ contextTreeService: this.contextTreeService, projectPath });
|
|
33
36
|
const token = await this.tokenStore.load();
|
|
34
37
|
if (!token || !token.isValid()) {
|
|
35
38
|
throw new NotAuthenticatedError();
|
|
@@ -66,6 +69,7 @@ export class PullHandler {
|
|
|
66
69
|
}
|
|
67
70
|
async handlePrepare(_data, clientId) {
|
|
68
71
|
const projectPath = resolveRequiredProjectPath(this.resolveProjectPath, clientId);
|
|
72
|
+
await guardAgainstGitVc({ contextTreeService: this.contextTreeService, projectPath });
|
|
69
73
|
const token = await this.tokenStore.load();
|
|
70
74
|
if (!token || !token.isValid()) {
|
|
71
75
|
throw new NotAuthenticatedError();
|
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
import type { ITokenStore } from '../../../core/interfaces/auth/i-token-store.js';
|
|
2
2
|
import type { IContextFileReader } from '../../../core/interfaces/context-tree/i-context-file-reader.js';
|
|
3
|
+
import type { IContextTreeService } from '../../../core/interfaces/context-tree/i-context-tree-service.js';
|
|
3
4
|
import type { IContextTreeSnapshotService } from '../../../core/interfaces/context-tree/i-context-tree-snapshot-service.js';
|
|
4
5
|
import type { ICogitPushService } from '../../../core/interfaces/services/i-cogit-push-service.js';
|
|
6
|
+
import type { ICurateLogStore } from '../../../core/interfaces/storage/i-curate-log-store.js';
|
|
5
7
|
import type { IProjectConfigStore } from '../../../core/interfaces/storage/i-project-config-store.js';
|
|
8
|
+
import type { IReviewBackupStore } from '../../../core/interfaces/storage/i-review-backup-store.js';
|
|
6
9
|
import type { ITransportServer } from '../../../core/interfaces/transport/i-transport-server.js';
|
|
7
10
|
import { type ProjectBroadcaster, type ProjectPathResolver } from './handler-types.js';
|
|
11
|
+
/** Factory that creates a curate log store scoped to a project directory. */
|
|
12
|
+
export type CurateLogStoreFactory = (projectPath: string) => ICurateLogStore;
|
|
8
13
|
export interface PushHandlerDeps {
|
|
9
14
|
broadcastToProject: ProjectBroadcaster;
|
|
10
15
|
cogitPushService: ICogitPushService;
|
|
11
16
|
contextFileReader: IContextFileReader;
|
|
17
|
+
contextTreeService: IContextTreeService;
|
|
12
18
|
contextTreeSnapshotService: IContextTreeSnapshotService;
|
|
19
|
+
curateLogStoreFactory: CurateLogStoreFactory;
|
|
13
20
|
projectConfigStore: IProjectConfigStore;
|
|
14
21
|
resolveProjectPath: ProjectPathResolver;
|
|
22
|
+
reviewBackupStoreFactory: (projectPath: string) => IReviewBackupStore;
|
|
15
23
|
tokenStore: ITokenStore;
|
|
16
24
|
transport: ITransportServer;
|
|
17
25
|
webAppUrl: string;
|
|
@@ -24,14 +32,26 @@ export declare class PushHandler {
|
|
|
24
32
|
private readonly broadcastToProject;
|
|
25
33
|
private readonly cogitPushService;
|
|
26
34
|
private readonly contextFileReader;
|
|
35
|
+
private readonly contextTreeService;
|
|
27
36
|
private readonly contextTreeSnapshotService;
|
|
37
|
+
private readonly curateLogStoreFactory;
|
|
28
38
|
private readonly projectConfigStore;
|
|
29
39
|
private readonly resolveProjectPath;
|
|
40
|
+
private readonly reviewBackupStoreFactory;
|
|
30
41
|
private readonly tokenStore;
|
|
31
42
|
private readonly transport;
|
|
32
43
|
private readonly webAppUrl;
|
|
33
44
|
constructor(deps: PushHandlerDeps);
|
|
34
45
|
setup(): void;
|
|
46
|
+
/**
|
|
47
|
+
* Filter a list of file paths, removing any with pending or rejected review status.
|
|
48
|
+
*/
|
|
49
|
+
private filterPushablePaths;
|
|
50
|
+
/**
|
|
51
|
+
* Build a map of context-tree-relative file path → review status from the curate log.
|
|
52
|
+
* Uses newest-wins strategy for the same file across multiple entries.
|
|
53
|
+
*/
|
|
54
|
+
private getFileReviewStatuses;
|
|
35
55
|
private handleExecute;
|
|
36
56
|
private handlePrepare;
|
|
37
57
|
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { join, relative } from 'node:path';
|
|
1
2
|
import { PushEvents, } from '../../../../shared/transport/events/push-events.js';
|
|
2
3
|
import { NotAuthenticatedError, ProjectNotInitError, SpaceNotConfiguredError, } from '../../../core/domain/errors/task-error.js';
|
|
3
4
|
import { mapToPushContexts } from '../../cogit/context-tree-to-push-context-mapper.js';
|
|
4
|
-
import { resolveRequiredProjectPath } from './handler-types.js';
|
|
5
|
+
import { guardAgainstGitVc, resolveRequiredProjectPath, } from './handler-types.js';
|
|
6
|
+
/** Path prefix of the context tree relative to the project root. */
|
|
7
|
+
const CONTEXT_TREE_RELATIVE = join('.brv', 'context-tree');
|
|
5
8
|
/**
|
|
6
9
|
* Handles push:* events.
|
|
7
10
|
* Business logic for pushing context tree to cloud — no terminal/UI calls.
|
|
@@ -10,9 +13,12 @@ export class PushHandler {
|
|
|
10
13
|
broadcastToProject;
|
|
11
14
|
cogitPushService;
|
|
12
15
|
contextFileReader;
|
|
16
|
+
contextTreeService;
|
|
13
17
|
contextTreeSnapshotService;
|
|
18
|
+
curateLogStoreFactory;
|
|
14
19
|
projectConfigStore;
|
|
15
20
|
resolveProjectPath;
|
|
21
|
+
reviewBackupStoreFactory;
|
|
16
22
|
tokenStore;
|
|
17
23
|
transport;
|
|
18
24
|
webAppUrl;
|
|
@@ -20,9 +26,12 @@ export class PushHandler {
|
|
|
20
26
|
this.broadcastToProject = deps.broadcastToProject;
|
|
21
27
|
this.cogitPushService = deps.cogitPushService;
|
|
22
28
|
this.contextFileReader = deps.contextFileReader;
|
|
29
|
+
this.contextTreeService = deps.contextTreeService;
|
|
23
30
|
this.contextTreeSnapshotService = deps.contextTreeSnapshotService;
|
|
31
|
+
this.curateLogStoreFactory = deps.curateLogStoreFactory;
|
|
24
32
|
this.projectConfigStore = deps.projectConfigStore;
|
|
25
33
|
this.resolveProjectPath = deps.resolveProjectPath;
|
|
34
|
+
this.reviewBackupStoreFactory = deps.reviewBackupStoreFactory;
|
|
26
35
|
this.tokenStore = deps.tokenStore;
|
|
27
36
|
this.transport = deps.transport;
|
|
28
37
|
this.webAppUrl = deps.webAppUrl;
|
|
@@ -31,8 +40,45 @@ export class PushHandler {
|
|
|
31
40
|
this.transport.onRequest(PushEvents.PREPARE, (data, clientId) => this.handlePrepare(data, clientId));
|
|
32
41
|
this.transport.onRequest(PushEvents.EXECUTE, (data, clientId) => this.handleExecute(data, clientId));
|
|
33
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Filter a list of file paths, removing any with pending or rejected review status.
|
|
45
|
+
*/
|
|
46
|
+
filterPushablePaths(paths, reviewStatuses) {
|
|
47
|
+
return paths.filter((path) => {
|
|
48
|
+
const status = reviewStatuses.get(path);
|
|
49
|
+
return status !== 'pending' && status !== 'rejected';
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Build a map of context-tree-relative file path → review status from the curate log.
|
|
54
|
+
* Uses newest-wins strategy for the same file across multiple entries.
|
|
55
|
+
*/
|
|
56
|
+
async getFileReviewStatuses(projectPath) {
|
|
57
|
+
const map = new Map();
|
|
58
|
+
try {
|
|
59
|
+
const store = this.curateLogStoreFactory(projectPath);
|
|
60
|
+
const entries = await store.list({ limit: 500, status: ['completed'] });
|
|
61
|
+
const contextTreeRoot = join(projectPath, CONTEXT_TREE_RELATIVE);
|
|
62
|
+
// Process oldest-first so the newest entry wins for each path
|
|
63
|
+
for (const entry of [...entries].reverse()) {
|
|
64
|
+
for (const op of entry.operations) {
|
|
65
|
+
if (!op.filePath || !op.reviewStatus)
|
|
66
|
+
continue;
|
|
67
|
+
const relativePath = relative(contextTreeRoot, op.filePath);
|
|
68
|
+
if (relativePath.startsWith('..'))
|
|
69
|
+
continue;
|
|
70
|
+
map.set(relativePath, op.reviewStatus);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// Best-effort
|
|
76
|
+
}
|
|
77
|
+
return map;
|
|
78
|
+
}
|
|
34
79
|
async handleExecute(data, clientId) {
|
|
35
80
|
const projectPath = resolveRequiredProjectPath(this.resolveProjectPath, clientId);
|
|
81
|
+
await guardAgainstGitVc({ contextTreeService: this.contextTreeService, projectPath });
|
|
36
82
|
const token = await this.tokenStore.load();
|
|
37
83
|
if (!token || !token.isValid()) {
|
|
38
84
|
throw new NotAuthenticatedError();
|
|
@@ -46,13 +92,18 @@ export class PushHandler {
|
|
|
46
92
|
}
|
|
47
93
|
this.broadcastToProject(projectPath, PushEvents.PROGRESS, { message: 'Reading context files...', step: 'reading' });
|
|
48
94
|
const changes = await this.contextTreeSnapshotService.getChanges(projectPath);
|
|
95
|
+
// Filter out files with pending/rejected review status
|
|
96
|
+
const reviewStatuses = await this.getFileReviewStatuses(projectPath);
|
|
97
|
+
const pushableAdded = this.filterPushablePaths(changes.added, reviewStatuses);
|
|
98
|
+
const pushableModified = this.filterPushablePaths(changes.modified, reviewStatuses);
|
|
99
|
+
const pushableDeleted = this.filterPushablePaths(changes.deleted, reviewStatuses);
|
|
49
100
|
const [addedFiles, modifiedFiles] = await Promise.all([
|
|
50
|
-
this.contextFileReader.readMany(
|
|
51
|
-
this.contextFileReader.readMany(
|
|
101
|
+
this.contextFileReader.readMany(pushableAdded, projectPath),
|
|
102
|
+
this.contextFileReader.readMany(pushableModified, projectPath),
|
|
52
103
|
]);
|
|
53
104
|
const pushContexts = mapToPushContexts({
|
|
54
105
|
addedFiles,
|
|
55
|
-
deletedPaths:
|
|
106
|
+
deletedPaths: pushableDeleted,
|
|
56
107
|
modifiedFiles,
|
|
57
108
|
});
|
|
58
109
|
this.broadcastToProject(projectPath, PushEvents.PROGRESS, { message: 'Pushing to cloud...', step: 'pushing' });
|
|
@@ -64,11 +115,37 @@ export class PushHandler {
|
|
|
64
115
|
spaceId: config.spaceId,
|
|
65
116
|
teamId: config.teamId,
|
|
66
117
|
});
|
|
67
|
-
|
|
118
|
+
// Save snapshot selectively: only record pushed files so excluded files
|
|
119
|
+
// still appear as changes in the next push.
|
|
120
|
+
const oldSnapshot = await this.contextTreeSnapshotService.getSnapshotState(projectPath);
|
|
121
|
+
const currentState = await this.contextTreeSnapshotService.getCurrentState(projectPath);
|
|
122
|
+
const newSnapshot = new Map(oldSnapshot);
|
|
123
|
+
for (const path of pushableAdded) {
|
|
124
|
+
const state = currentState.get(path);
|
|
125
|
+
if (state)
|
|
126
|
+
newSnapshot.set(path, state);
|
|
127
|
+
}
|
|
128
|
+
for (const path of pushableModified) {
|
|
129
|
+
const state = currentState.get(path);
|
|
130
|
+
if (state)
|
|
131
|
+
newSnapshot.set(path, state);
|
|
132
|
+
}
|
|
133
|
+
for (const path of pushableDeleted) {
|
|
134
|
+
newSnapshot.delete(path);
|
|
135
|
+
}
|
|
136
|
+
await this.contextTreeSnapshotService.saveSnapshotFromState(newSnapshot, projectPath);
|
|
137
|
+
// Best-effort: clear backups for pushed files so the pushed state becomes the new diff baseline
|
|
138
|
+
try {
|
|
139
|
+
const backupStore = this.reviewBackupStoreFactory(projectPath);
|
|
140
|
+
await Promise.all([...pushableAdded, ...pushableModified, ...pushableDeleted].map((p) => backupStore.delete(p)));
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// Backup cleanup must never block the push response
|
|
144
|
+
}
|
|
68
145
|
const url = `${this.webAppUrl}/${config.teamName}/${config.spaceName}`;
|
|
69
146
|
return {
|
|
70
147
|
added: addedFiles.length,
|
|
71
|
-
deleted:
|
|
148
|
+
deleted: pushableDeleted.length,
|
|
72
149
|
edited: modifiedFiles.length,
|
|
73
150
|
success: true,
|
|
74
151
|
url,
|
|
@@ -76,6 +153,7 @@ export class PushHandler {
|
|
|
76
153
|
}
|
|
77
154
|
async handlePrepare(_data, clientId) {
|
|
78
155
|
const projectPath = resolveRequiredProjectPath(this.resolveProjectPath, clientId);
|
|
156
|
+
await guardAgainstGitVc({ contextTreeService: this.contextTreeService, projectPath });
|
|
79
157
|
const token = await this.tokenStore.load();
|
|
80
158
|
if (!token || !token.isValid()) {
|
|
81
159
|
throw new NotAuthenticatedError();
|
|
@@ -92,17 +170,41 @@ export class PushHandler {
|
|
|
92
170
|
await this.contextTreeSnapshotService.initEmptySnapshot(projectPath);
|
|
93
171
|
}
|
|
94
172
|
const changes = await this.contextTreeSnapshotService.getChanges(projectPath);
|
|
173
|
+
// Get review statuses and filter pushable files
|
|
174
|
+
const reviewStatuses = await this.getFileReviewStatuses(projectPath);
|
|
175
|
+
const pushableAdded = this.filterPushablePaths(changes.added, reviewStatuses);
|
|
176
|
+
const pushableModified = this.filterPushablePaths(changes.modified, reviewStatuses);
|
|
177
|
+
const pushableDeleted = this.filterPushablePaths(changes.deleted, reviewStatuses);
|
|
95
178
|
const totalChanges = changes.added.length + changes.modified.length + changes.deleted.length;
|
|
179
|
+
const pushableTotal = pushableAdded.length + pushableModified.length + pushableDeleted.length;
|
|
180
|
+
const excludedReviewCount = totalChanges - pushableTotal;
|
|
181
|
+
// Count pending reviews from review statuses map
|
|
182
|
+
let pendingReviewCount = 0;
|
|
183
|
+
for (const status of reviewStatuses.values()) {
|
|
184
|
+
if (status === 'pending')
|
|
185
|
+
pendingReviewCount++;
|
|
186
|
+
}
|
|
96
187
|
const parts = [];
|
|
97
|
-
if (
|
|
98
|
-
parts.push(`${
|
|
99
|
-
if (
|
|
100
|
-
parts.push(`${
|
|
101
|
-
if (
|
|
102
|
-
parts.push(`${
|
|
188
|
+
if (pushableAdded.length > 0)
|
|
189
|
+
parts.push(`${pushableAdded.length} added`);
|
|
190
|
+
if (pushableModified.length > 0)
|
|
191
|
+
parts.push(`${pushableModified.length} modified`);
|
|
192
|
+
if (pushableDeleted.length > 0)
|
|
193
|
+
parts.push(`${pushableDeleted.length} deleted`);
|
|
194
|
+
let reviewUrl;
|
|
195
|
+
if (pendingReviewCount > 0) {
|
|
196
|
+
const port = this.transport.getPort();
|
|
197
|
+
if (port) {
|
|
198
|
+
const encoded = Buffer.from(projectPath).toString('base64url');
|
|
199
|
+
reviewUrl = `http://127.0.0.1:${port}/review?project=${encoded}`;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
103
202
|
return {
|
|
104
|
-
|
|
105
|
-
|
|
203
|
+
excludedReviewCount,
|
|
204
|
+
fileCount: pushableTotal,
|
|
205
|
+
hasChanges: pushableTotal > 0,
|
|
206
|
+
pendingReviewCount,
|
|
207
|
+
...(reviewUrl ? { reviewUrl } : {}),
|
|
106
208
|
summary: parts.length > 0 ? parts.join(', ') : 'No changes',
|
|
107
209
|
};
|
|
108
210
|
}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import type { IContextTreeService } from '../../../core/interfaces/context-tree/i-context-tree-service.js';
|
|
2
2
|
import type { IContextTreeSnapshotService } from '../../../core/interfaces/context-tree/i-context-tree-snapshot-service.js';
|
|
3
|
+
import type { ICurateLogStore } from '../../../core/interfaces/storage/i-curate-log-store.js';
|
|
4
|
+
import type { IReviewBackupStore } from '../../../core/interfaces/storage/i-review-backup-store.js';
|
|
3
5
|
import type { ITransportServer } from '../../../core/interfaces/transport/i-transport-server.js';
|
|
4
6
|
import { type ProjectPathResolver } from './handler-types.js';
|
|
5
7
|
export interface ResetHandlerDeps {
|
|
6
8
|
contextTreeService: IContextTreeService;
|
|
7
9
|
contextTreeSnapshotService: IContextTreeSnapshotService;
|
|
10
|
+
curateLogStoreFactory: (projectPath: string) => ICurateLogStore;
|
|
8
11
|
resolveProjectPath: ProjectPathResolver;
|
|
12
|
+
reviewBackupStoreFactory: (projectPath: string) => IReviewBackupStore;
|
|
9
13
|
transport: ITransportServer;
|
|
10
14
|
}
|
|
11
15
|
/**
|
|
@@ -15,9 +19,16 @@ export interface ResetHandlerDeps {
|
|
|
15
19
|
export declare class ResetHandler {
|
|
16
20
|
private readonly contextTreeService;
|
|
17
21
|
private readonly contextTreeSnapshotService;
|
|
22
|
+
private readonly curateLogStoreFactory;
|
|
18
23
|
private readonly resolveProjectPath;
|
|
24
|
+
private readonly reviewBackupStoreFactory;
|
|
19
25
|
private readonly transport;
|
|
20
26
|
constructor(deps: ResetHandlerDeps);
|
|
21
27
|
setup(): void;
|
|
28
|
+
/**
|
|
29
|
+
* Mark all pending review operations as 'rejected' so they no longer appear in /status.
|
|
30
|
+
* The context tree has been wiped, so these reviews are no longer actionable.
|
|
31
|
+
*/
|
|
32
|
+
private clearPendingReviews;
|
|
22
33
|
private handleExecute;
|
|
23
34
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ResetEvents } from '../../../../shared/transport/events/reset-events.js';
|
|
2
2
|
import { ContextTreeNotInitializedError } from '../../../core/domain/errors/task-error.js';
|
|
3
|
-
import { resolveRequiredProjectPath } from './handler-types.js';
|
|
3
|
+
import { guardAgainstGitVc, resolveRequiredProjectPath } from './handler-types.js';
|
|
4
4
|
/**
|
|
5
5
|
* Handles reset:execute event.
|
|
6
6
|
* Deletes and re-initializes the context tree — no terminal/UI calls.
|
|
@@ -8,19 +8,42 @@ import { resolveRequiredProjectPath } from './handler-types.js';
|
|
|
8
8
|
export class ResetHandler {
|
|
9
9
|
contextTreeService;
|
|
10
10
|
contextTreeSnapshotService;
|
|
11
|
+
curateLogStoreFactory;
|
|
11
12
|
resolveProjectPath;
|
|
13
|
+
reviewBackupStoreFactory;
|
|
12
14
|
transport;
|
|
13
15
|
constructor(deps) {
|
|
14
16
|
this.contextTreeService = deps.contextTreeService;
|
|
15
17
|
this.contextTreeSnapshotService = deps.contextTreeSnapshotService;
|
|
18
|
+
this.curateLogStoreFactory = deps.curateLogStoreFactory;
|
|
16
19
|
this.resolveProjectPath = deps.resolveProjectPath;
|
|
20
|
+
this.reviewBackupStoreFactory = deps.reviewBackupStoreFactory;
|
|
17
21
|
this.transport = deps.transport;
|
|
18
22
|
}
|
|
19
23
|
setup() {
|
|
20
24
|
this.transport.onRequest(ResetEvents.EXECUTE, (_data, clientId) => this.handleExecute(clientId));
|
|
21
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Mark all pending review operations as 'rejected' so they no longer appear in /status.
|
|
28
|
+
* The context tree has been wiped, so these reviews are no longer actionable.
|
|
29
|
+
*/
|
|
30
|
+
async clearPendingReviews(projectPath) {
|
|
31
|
+
const store = this.curateLogStoreFactory(projectPath);
|
|
32
|
+
// Only completed entries can carry reviewable operations; pending reviews are assigned at completion time.
|
|
33
|
+
const entries = await store.list({ status: ['completed'] });
|
|
34
|
+
const updates = entries
|
|
35
|
+
.map((entry) => {
|
|
36
|
+
const pendingIndices = entry.operations
|
|
37
|
+
.map((op, i) => (op.reviewStatus === 'pending' ? { operationIndex: i, reviewStatus: 'rejected' } : null))
|
|
38
|
+
.filter((u) => u !== null);
|
|
39
|
+
return pendingIndices.length > 0 ? { id: entry.id, pendingIndices } : null;
|
|
40
|
+
})
|
|
41
|
+
.filter((u) => u !== null);
|
|
42
|
+
await Promise.all(updates.map((u) => store.batchUpdateOperationReviewStatus(u.id, u.pendingIndices)));
|
|
43
|
+
}
|
|
22
44
|
async handleExecute(clientId) {
|
|
23
45
|
const projectPath = resolveRequiredProjectPath(this.resolveProjectPath, clientId);
|
|
46
|
+
await guardAgainstGitVc({ contextTreeService: this.contextTreeService, projectPath });
|
|
24
47
|
const exists = await this.contextTreeService.exists(projectPath);
|
|
25
48
|
if (!exists) {
|
|
26
49
|
throw new ContextTreeNotInitializedError();
|
|
@@ -28,6 +51,19 @@ export class ResetHandler {
|
|
|
28
51
|
await this.contextTreeService.delete(projectPath);
|
|
29
52
|
await this.contextTreeService.initialize(projectPath);
|
|
30
53
|
await this.contextTreeSnapshotService.initEmptySnapshot(projectPath);
|
|
54
|
+
// Best-effort: clear review backups and pending review statuses so /status starts fresh
|
|
55
|
+
try {
|
|
56
|
+
await this.reviewBackupStoreFactory(projectPath).clear();
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Backup cleanup must never block the reset response
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
await this.clearPendingReviews(projectPath);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Review status cleanup must never block the reset response
|
|
66
|
+
}
|
|
31
67
|
return { success: true };
|
|
32
68
|
}
|
|
33
69
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ICurateLogStore } from '../../../core/interfaces/storage/i-curate-log-store.js';
|
|
2
|
+
import type { IReviewBackupStore } from '../../../core/interfaces/storage/i-review-backup-store.js';
|
|
3
|
+
import type { ITransportServer } from '../../../core/interfaces/transport/i-transport-server.js';
|
|
4
|
+
import { type ProjectPathResolver } from './handler-types.js';
|
|
5
|
+
type CurateLogStoreFactory = (projectPath: string) => ICurateLogStore;
|
|
6
|
+
type ReviewBackupStoreFactory = (projectPath: string) => IReviewBackupStore;
|
|
7
|
+
export interface ReviewHandlerDeps {
|
|
8
|
+
curateLogStoreFactory: CurateLogStoreFactory;
|
|
9
|
+
/** Called after all pending ops for a task are decided. Used to notify TUI clients. */
|
|
10
|
+
onResolved?: (info: {
|
|
11
|
+
projectPath: string;
|
|
12
|
+
taskId: string;
|
|
13
|
+
}) => void;
|
|
14
|
+
resolveProjectPath: ProjectPathResolver;
|
|
15
|
+
reviewBackupStoreFactory: ReviewBackupStoreFactory;
|
|
16
|
+
transport: ITransportServer;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Handles review:decideTask — approves or rejects all pending review operations
|
|
20
|
+
* for a given task ID in a single transport request.
|
|
21
|
+
*
|
|
22
|
+
* Mirrors the per-file logic in review-api-handler.ts but operates at task scope.
|
|
23
|
+
*/
|
|
24
|
+
export declare class ReviewHandler {
|
|
25
|
+
private readonly curateLogStoreFactory;
|
|
26
|
+
private readonly onResolved;
|
|
27
|
+
private readonly resolveProjectPath;
|
|
28
|
+
private readonly reviewBackupStoreFactory;
|
|
29
|
+
private readonly transport;
|
|
30
|
+
constructor(deps: ReviewHandlerDeps);
|
|
31
|
+
setup(): void;
|
|
32
|
+
private handleDecideTask;
|
|
33
|
+
private handlePending;
|
|
34
|
+
}
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { mkdir, unlink, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname, join, relative } from 'node:path';
|
|
3
|
+
import { ReviewEvents, } from '../../../../shared/transport/events/review-events.js';
|
|
4
|
+
import { BRV_DIR, CONTEXT_TREE_DIR } from '../../../constants.js';
|
|
5
|
+
import { resolveRequiredProjectPath } from './handler-types.js';
|
|
6
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
7
|
+
async function writeFileWithDirs(absolutePath, content) {
|
|
8
|
+
await mkdir(dirname(absolutePath), { recursive: true });
|
|
9
|
+
await writeFile(absolutePath, content, 'utf8');
|
|
10
|
+
}
|
|
11
|
+
// ── Handler ──────────────────────────────────────────────────────────────────
|
|
12
|
+
/**
|
|
13
|
+
* Handles review:decideTask — approves or rejects all pending review operations
|
|
14
|
+
* for a given task ID in a single transport request.
|
|
15
|
+
*
|
|
16
|
+
* Mirrors the per-file logic in review-api-handler.ts but operates at task scope.
|
|
17
|
+
*/
|
|
18
|
+
export class ReviewHandler {
|
|
19
|
+
curateLogStoreFactory;
|
|
20
|
+
onResolved;
|
|
21
|
+
resolveProjectPath;
|
|
22
|
+
reviewBackupStoreFactory;
|
|
23
|
+
transport;
|
|
24
|
+
constructor(deps) {
|
|
25
|
+
this.curateLogStoreFactory = deps.curateLogStoreFactory;
|
|
26
|
+
this.onResolved = deps.onResolved;
|
|
27
|
+
this.resolveProjectPath = deps.resolveProjectPath;
|
|
28
|
+
this.reviewBackupStoreFactory = deps.reviewBackupStoreFactory;
|
|
29
|
+
this.transport = deps.transport;
|
|
30
|
+
}
|
|
31
|
+
setup() {
|
|
32
|
+
this.transport.onRequest(ReviewEvents.DECIDE_TASK, (data, clientId) => this.handleDecideTask(data, clientId));
|
|
33
|
+
this.transport.onRequest(ReviewEvents.PENDING, (_data, clientId) => this.handlePending(clientId));
|
|
34
|
+
}
|
|
35
|
+
async handleDecideTask({ decision, filePaths: filterPaths, taskId }, clientId) {
|
|
36
|
+
const projectPath = resolveRequiredProjectPath(this.resolveProjectPath, clientId);
|
|
37
|
+
const contextTreeDir = join(projectPath, BRV_DIR, CONTEXT_TREE_DIR);
|
|
38
|
+
const store = this.curateLogStoreFactory(projectPath);
|
|
39
|
+
const backupStore = this.reviewBackupStoreFactory(projectPath);
|
|
40
|
+
const entries = await store.list();
|
|
41
|
+
// Collect pending ops grouped by relative file path for this taskId
|
|
42
|
+
const pendingByPath = new Map();
|
|
43
|
+
for (const entry of entries) {
|
|
44
|
+
if (entry.taskId !== taskId)
|
|
45
|
+
continue;
|
|
46
|
+
for (let i = 0; i < entry.operations.length; i++) {
|
|
47
|
+
const op = entry.operations[i];
|
|
48
|
+
if (op.reviewStatus !== 'pending' || !op.filePath)
|
|
49
|
+
continue;
|
|
50
|
+
const rel = relative(contextTreeDir, op.filePath);
|
|
51
|
+
if (rel.startsWith('..'))
|
|
52
|
+
continue;
|
|
53
|
+
let ops = pendingByPath.get(rel);
|
|
54
|
+
if (!ops) {
|
|
55
|
+
ops = [];
|
|
56
|
+
pendingByPath.set(rel, ops);
|
|
57
|
+
}
|
|
58
|
+
ops.push({ additionalFilePaths: op.additionalFilePaths, logId: entry.id, operationIndex: i });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// If filePaths filter is provided, only process those files
|
|
62
|
+
if (filterPaths?.length) {
|
|
63
|
+
const filterSet = new Set(filterPaths);
|
|
64
|
+
const keysToDelete = [...pendingByPath.keys()].filter((key) => !filterSet.has(key));
|
|
65
|
+
for (const key of keysToDelete)
|
|
66
|
+
pendingByPath.delete(key);
|
|
67
|
+
}
|
|
68
|
+
// Apply decision for each affected file in parallel.
|
|
69
|
+
// allSettled so a single file failure does not block log updates for files that succeeded.
|
|
70
|
+
const settled = await Promise.allSettled([...pendingByPath.entries()].map(async ([relPath, ops]) => {
|
|
71
|
+
let reverted = false;
|
|
72
|
+
const allAdditionalPaths = [...new Set(ops.flatMap((o) => o.additionalFilePaths ?? []))];
|
|
73
|
+
if (decision === 'rejected') {
|
|
74
|
+
const absolutePath = join(contextTreeDir, relPath);
|
|
75
|
+
const backupContent = await backupStore.read(relPath);
|
|
76
|
+
// null backup = ADD operation (new file) → remove it; existing backup → restore
|
|
77
|
+
await (backupContent === null
|
|
78
|
+
? unlink(absolutePath).catch(() => { })
|
|
79
|
+
: writeFileWithDirs(absolutePath, backupContent));
|
|
80
|
+
// Restore additional paths (MERGE source, folder DELETE contents).
|
|
81
|
+
// Best-effort: partial failures must not block the log update below.
|
|
82
|
+
await Promise.allSettled(allAdditionalPaths.map(async (absPath) => {
|
|
83
|
+
const rel = relative(contextTreeDir, absPath);
|
|
84
|
+
const content = await backupStore.read(rel);
|
|
85
|
+
if (content !== null)
|
|
86
|
+
await writeFileWithDirs(absPath, content);
|
|
87
|
+
}));
|
|
88
|
+
reverted = true;
|
|
89
|
+
}
|
|
90
|
+
// Clear backups for both approve and reject (current state becomes new baseline)
|
|
91
|
+
await backupStore.delete(relPath);
|
|
92
|
+
await Promise.allSettled(allAdditionalPaths.map((absPath) => backupStore.delete(relative(contextTreeDir, absPath))));
|
|
93
|
+
return { ops, path: relPath, reverted };
|
|
94
|
+
}));
|
|
95
|
+
// Only update log entries for files that were successfully processed.
|
|
96
|
+
// Files that failed remain pending and can be retried.
|
|
97
|
+
const fileResults = settled
|
|
98
|
+
.filter((r) => r.status === 'fulfilled')
|
|
99
|
+
.map((r) => r.value);
|
|
100
|
+
// Batch-update review status grouped by logId (one read+write per entry file)
|
|
101
|
+
const byLogId = new Map();
|
|
102
|
+
for (const { ops } of fileResults) {
|
|
103
|
+
for (const { logId, operationIndex } of ops) {
|
|
104
|
+
let batch = byLogId.get(logId);
|
|
105
|
+
if (!batch) {
|
|
106
|
+
batch = [];
|
|
107
|
+
byLogId.set(logId, batch);
|
|
108
|
+
}
|
|
109
|
+
batch.push({ operationIndex, reviewStatus: decision });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
await Promise.all([...byLogId.entries()].map(([logId, updates]) => store.batchUpdateOperationReviewStatus(logId, updates)));
|
|
113
|
+
try {
|
|
114
|
+
this.onResolved?.({ projectPath, taskId });
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// Best-effort notification — never block the response
|
|
118
|
+
}
|
|
119
|
+
const totalCount = fileResults.reduce((sum, { ops }) => sum + ops.length, 0);
|
|
120
|
+
return { files: fileResults.map(({ path, reverted }) => ({ path, reverted })), totalCount };
|
|
121
|
+
}
|
|
122
|
+
async handlePending(clientId) {
|
|
123
|
+
const projectPath = resolveRequiredProjectPath(this.resolveProjectPath, clientId);
|
|
124
|
+
const contextTreeDir = join(projectPath, BRV_DIR, CONTEXT_TREE_DIR);
|
|
125
|
+
const store = this.curateLogStoreFactory(projectPath);
|
|
126
|
+
const entries = await store.list({ status: ['completed'] });
|
|
127
|
+
const taskMap = new Map();
|
|
128
|
+
for (const entry of entries) {
|
|
129
|
+
for (const op of entry.operations) {
|
|
130
|
+
// Skip failed ops (e.g. validation errors) — they were never applied to disk
|
|
131
|
+
if (op.reviewStatus !== 'pending' || op.status === 'failed')
|
|
132
|
+
continue;
|
|
133
|
+
let ops = taskMap.get(entry.taskId);
|
|
134
|
+
if (!ops) {
|
|
135
|
+
ops = [];
|
|
136
|
+
taskMap.set(entry.taskId, ops);
|
|
137
|
+
}
|
|
138
|
+
const pendingOp = { path: op.path, type: op.type };
|
|
139
|
+
if (op.filePath) {
|
|
140
|
+
const rel = relative(contextTreeDir, op.filePath);
|
|
141
|
+
if (!rel.startsWith('..'))
|
|
142
|
+
pendingOp.filePath = rel;
|
|
143
|
+
}
|
|
144
|
+
if (op.impact)
|
|
145
|
+
pendingOp.impact = op.impact;
|
|
146
|
+
if (op.reason)
|
|
147
|
+
pendingOp.reason = op.reason;
|
|
148
|
+
if (op.previousSummary)
|
|
149
|
+
pendingOp.previousSummary = op.previousSummary;
|
|
150
|
+
if (op.summary)
|
|
151
|
+
pendingOp.summary = op.summary;
|
|
152
|
+
ops.push(pendingOp);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const tasks = [...taskMap.entries()].map(([taskId, operations]) => ({
|
|
156
|
+
operations,
|
|
157
|
+
taskId,
|
|
158
|
+
}));
|
|
159
|
+
const pendingCount = tasks.reduce((sum, t) => sum + t.operations.length, 0);
|
|
160
|
+
return { pendingCount, tasks };
|
|
161
|
+
}
|
|
162
|
+
}
|