@taskforcehq/taskforce 0.3.168
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/README.md +241 -0
- package/dist/Taskforce.d.ts +3 -0
- package/dist/Taskforce.js +3 -0
- package/dist/Taskforce.module.css +7587 -0
- package/dist/TaskforceCore.CategoryFallback.test.d.ts +1 -0
- package/dist/TaskforceCore.CategoryFallback.test.js +98 -0
- package/dist/TaskforceCore.d.ts +12 -0
- package/dist/TaskforceCore.js +729 -0
- package/dist/TaskforceCore.test.d.ts +1 -0
- package/dist/TaskforceCore.test.js +2132 -0
- package/dist/TaskforceScreenshot.d.ts +11 -0
- package/dist/TaskforceScreenshot.js +113 -0
- package/dist/TaskforceWidget.d.ts +6 -0
- package/dist/TaskforceWidget.js +101 -0
- package/dist/TaskforceWidget.test.d.ts +1 -0
- package/dist/TaskforceWidget.test.js +92 -0
- package/dist/UnsavedChangesRelaxed.test.d.ts +1 -0
- package/dist/UnsavedChangesRelaxed.test.js +16 -0
- package/dist/__tests__/localizationCatalogs.test.d.ts +1 -0
- package/dist/__tests__/localizationCatalogs.test.js +37 -0
- package/dist/__tests__/planParser.test.d.ts +1 -0
- package/dist/__tests__/planParser.test.js +183 -0
- package/dist/__tests__/releaseVersion.test.d.ts +1 -0
- package/dist/__tests__/releaseVersion.test.js +32 -0
- package/dist/__tests__/structuredCommentValidation.test.d.ts +1 -0
- package/dist/__tests__/structuredCommentValidation.test.js +31 -0
- package/dist/__tests__/structuredDocValidation.test.d.ts +1 -0
- package/dist/__tests__/structuredDocValidation.test.js +30 -0
- package/dist/__tests__/templateEngine.test.d.ts +1 -0
- package/dist/__tests__/templateEngine.test.js +150 -0
- package/dist/__tests__/templates.test.d.ts +1 -0
- package/dist/__tests__/templates.test.js +45 -0
- package/dist/__tests__/workflowDocumentFixtures.test.d.ts +1 -0
- package/dist/__tests__/workflowDocumentFixtures.test.js +78 -0
- package/dist/__tests__/workflowExportMatrix.test.d.ts +1 -0
- package/dist/__tests__/workflowExportMatrix.test.js +113 -0
- package/dist/__tests__/workflowExportSnapshots.test.d.ts +1 -0
- package/dist/__tests__/workflowExportSnapshots.test.js +28 -0
- package/dist/__tests__/workflowGeneration.test.d.ts +1 -0
- package/dist/__tests__/workflowGeneration.test.js +12 -0
- package/dist/__tests__/workflowSimulation.test.d.ts +1 -0
- package/dist/__tests__/workflowSimulation.test.js +113 -0
- package/dist/__tests__/workflowV2Policy.test.d.ts +1 -0
- package/dist/__tests__/workflowV2Policy.test.js +80 -0
- package/dist/api.d.ts +30 -0
- package/dist/api.js +85 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +217 -0
- package/dist/compat/priorityCompat.d.ts +2 -0
- package/dist/compat/priorityCompat.js +24 -0
- package/dist/components/features/DocumentGenerateModal.d.ts +9 -0
- package/dist/components/features/DocumentGenerateModal.js +114 -0
- package/dist/components/features/DocumentIndex.d.ts +19 -0
- package/dist/components/features/DocumentIndex.js +51 -0
- package/dist/components/features/DocumentViewer.d.ts +9 -0
- package/dist/components/features/DocumentViewer.js +253 -0
- package/dist/components/features/DocumentWorkspace.d.ts +21 -0
- package/dist/components/features/DocumentWorkspace.js +88 -0
- package/dist/components/features/TaskSettings.d.ts +209 -0
- package/dist/components/features/TaskSettings.js +500 -0
- package/dist/components/features/TaskSettings.test.d.ts +1 -0
- package/dist/components/features/TaskSettings.test.js +308 -0
- package/dist/components/features/settings/CategoryManager.d.ts +19 -0
- package/dist/components/features/settings/CategoryManager.js +113 -0
- package/dist/components/features/settings/TaxonomyEditor.d.ts +8 -0
- package/dist/components/features/settings/TaxonomyEditor.js +98 -0
- package/dist/components/features/settings/TaxonomyManager 2.d.ts +52 -0
- package/dist/components/features/settings/TaxonomyManager 2.js +75 -0
- package/dist/components/features/settings/TaxonomyManager.d.ts +54 -0
- package/dist/components/features/settings/TaxonomyManager.js +77 -0
- package/dist/components/features/settings/TypeManager.d.ts +10 -0
- package/dist/components/features/settings/TypeManager.js +50 -0
- package/dist/components/features/settings/WorkflowEditorPrototype.d.ts +15 -0
- package/dist/components/features/settings/WorkflowEditorPrototype.js +245 -0
- package/dist/components/task/TaskActionHeader.d.ts +20 -0
- package/dist/components/task/TaskActionHeader.js +13 -0
- package/dist/components/task/TaskCard.d.ts +47 -0
- package/dist/components/task/TaskCard.js +326 -0
- package/dist/components/task/TaskCard.test.d.ts +1 -0
- package/dist/components/task/TaskCard.test.js +95 -0
- package/dist/components/task/TaskContextUpload.d.ts +12 -0
- package/dist/components/task/TaskContextUpload.js +393 -0
- package/dist/components/task/TaskContextUpload.test.d.ts +1 -0
- package/dist/components/task/TaskContextUpload.test.js +189 -0
- package/dist/components/task/TaskForm.d.ts +105 -0
- package/dist/components/task/TaskForm.js +241 -0
- package/dist/components/task/TaskForm.test.d.ts +1 -0
- package/dist/components/task/TaskForm.test.js +288 -0
- package/dist/components/task/TaskKanban 2.d.ts +76 -0
- package/dist/components/task/TaskKanban 2.js +1004 -0
- package/dist/components/task/TaskKanban.d.ts +76 -0
- package/dist/components/task/TaskKanban.empty-column-drop.test.d.ts +1 -0
- package/dist/components/task/TaskKanban.empty-column-drop.test.js +66 -0
- package/dist/components/task/TaskKanban.js +1026 -0
- package/dist/components/task/TaskKanban.test.d.ts +1 -0
- package/dist/components/task/TaskKanban.test.js +46 -0
- package/dist/components/task/TaskList.d.ts +74 -0
- package/dist/components/task/TaskList.js +38 -0
- package/dist/components/task/TaskList.test.d.ts +1 -0
- package/dist/components/task/TaskList.test.js +270 -0
- package/dist/components/task/TaskScreenshotSection.d.ts +15 -0
- package/dist/components/task/TaskScreenshotSection.js +22 -0
- package/dist/components/ui/LevelSelector.d.ts +13 -0
- package/dist/components/ui/LevelSelector.js +25 -0
- package/dist/components/ui/LevelSelector.test.d.ts +1 -0
- package/dist/components/ui/LevelSelector.test.js +55 -0
- package/dist/components/ui/Markdown.d.ts +6 -0
- package/dist/components/ui/Markdown.js +94 -0
- package/dist/components/ui/Markdown.test.d.ts +1 -0
- package/dist/components/ui/Markdown.test.js +38 -0
- package/dist/components/ui/Modal.d.ts +16 -0
- package/dist/components/ui/Modal.js +49 -0
- package/dist/components/ui/MultiSelectFilter.d.ts +10 -0
- package/dist/components/ui/MultiSelectFilter.js +71 -0
- package/dist/components/ui/MultiSelectFilter.test.d.ts +1 -0
- package/dist/components/ui/MultiSelectFilter.test.js +74 -0
- package/dist/components/ui/TaxonomyDropdown.d.ts +17 -0
- package/dist/components/ui/TaxonomyDropdown.js +145 -0
- package/dist/components/ui/TaxonomyDropdown.test.d.ts +1 -0
- package/dist/components/ui/TaxonomyDropdown.test.js +130 -0
- package/dist/components/views/StandaloneLayout 2.d.ts +8 -0
- package/dist/components/views/StandaloneLayout 2.js +1660 -0
- package/dist/components/views/StandaloneLayout.d.ts +8 -0
- package/dist/components/views/StandaloneLayout.js +2117 -0
- package/dist/components/views/WidgetView.d.ts +8 -0
- package/dist/components/views/WidgetView.js +221 -0
- package/dist/config/envSchema.d.ts +28 -0
- package/dist/config/envSchema.js +374 -0
- package/dist/config/envSchema.test.d.ts +1 -0
- package/dist/config/envSchema.test.js +97 -0
- package/dist/config/surfaceBase.d.ts +2 -0
- package/dist/config/surfaceBase.js +8 -0
- package/dist/core/BulkUpdateFieldsPreview.test.d.ts +1 -0
- package/dist/core/BulkUpdateFieldsPreview.test.js +62 -0
- package/dist/core/CompletedAt.test.d.ts +1 -0
- package/dist/core/CompletedAt.test.js +44 -0
- package/dist/core/CreatedBy.test.d.ts +1 -0
- package/dist/core/CreatedBy.test.js +60 -0
- package/dist/core/EntitlementsPolicy.test.d.ts +1 -0
- package/dist/core/EntitlementsPolicy.test.js +145 -0
- package/dist/core/InitiativeTemplates.test.d.ts +1 -0
- package/dist/core/InitiativeTemplates.test.js +50 -0
- package/dist/core/ParentLifecycle.test.d.ts +1 -0
- package/dist/core/ParentLifecycle.test.js +98 -0
- package/dist/core/ScheduleFields.test.d.ts +1 -0
- package/dist/core/ScheduleFields.test.js +197 -0
- package/dist/core/SetupState.test.d.ts +1 -0
- package/dist/core/SetupState.test.js +113 -0
- package/dist/core/SystemAdmin.test.d.ts +1 -0
- package/dist/core/SystemAdmin.test.js +295 -0
- package/dist/core/TaskTaxonomyValidation.test.d.ts +1 -0
- package/dist/core/TaskTaxonomyValidation.test.js +64 -0
- package/dist/core/Taskforce.d.ts +1063 -0
- package/dist/core/Taskforce.ids.test.d.ts +1 -0
- package/dist/core/Taskforce.ids.test.js +168 -0
- package/dist/core/Taskforce.js +5981 -0
- package/dist/core/Taskforce.realtimeMutation.test.d.ts +1 -0
- package/dist/core/Taskforce.realtimeMutation.test.js +76 -0
- package/dist/core/WorkspaceDelete.test.d.ts +1 -0
- package/dist/core/WorkspaceDelete.test.js +140 -0
- package/dist/core/WorkspacePermissions.test.d.ts +1 -0
- package/dist/core/WorkspacePermissions.test.js +151 -0
- package/dist/core/WorkspacePlanMode.test.d.ts +1 -0
- package/dist/core/WorkspacePlanMode.test.js +104 -0
- package/dist/hooks/authGuardPolicy.d.ts +26 -0
- package/dist/hooks/authGuardPolicy.js +32 -0
- package/dist/hooks/authGuardPolicy.test.d.ts +1 -0
- package/dist/hooks/authGuardPolicy.test.js +76 -0
- package/dist/hooks/useDraggable.d.ts +12 -0
- package/dist/hooks/useDraggable.js +65 -0
- package/dist/hooks/useRealtimeSync.d.ts +24 -0
- package/dist/hooks/useRealtimeSync.js +275 -0
- package/dist/hooks/useRealtimeSync.test.d.ts +1 -0
- package/dist/hooks/useRealtimeSync.test.js +284 -0
- package/dist/hooks/useSyncOrchestrator.d.ts +164 -0
- package/dist/hooks/useSyncOrchestrator.js +1427 -0
- package/dist/hooks/useSyncOrchestrator.retry-closure.test.d.ts +1 -0
- package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +367 -0
- package/dist/hooks/useTaskforce.d.ts +727 -0
- package/dist/hooks/useTaskforce.js +4396 -0
- package/dist/hooks/useTaskforce.priority-filters.test.d.ts +1 -0
- package/dist/hooks/useTaskforce.priority-filters.test.js +33 -0
- package/dist/hooks/useWorkspaceSyncController.d.ts +80 -0
- package/dist/hooks/useWorkspaceSyncController.js +598 -0
- package/dist/hooks/useWorkspaceSyncController.test.d.ts +1 -0
- package/dist/hooks/useWorkspaceSyncController.test.js +236 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +11 -0
- package/dist/localization/index.d.ts +8 -0
- package/dist/localization/index.js +81 -0
- package/dist/localization/locales/en-US.d.ts +151 -0
- package/dist/localization/locales/en-US.js +150 -0
- package/dist/localization/locales/es-419.d.ts +2 -0
- package/dist/localization/locales/es-419.js +150 -0
- package/dist/localization/locales/pt-BR.d.ts +2 -0
- package/dist/localization/locales/pt-BR.js +150 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +33 -0
- package/dist/mcp/optionResolution.d.ts +14 -0
- package/dist/mcp/optionResolution.js +126 -0
- package/dist/mcp/optionResolution.test.d.ts +1 -0
- package/dist/mcp/optionResolution.test.js +39 -0
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +1793 -0
- package/dist/mcp/structuredCommentValidation.d.ts +6 -0
- package/dist/mcp/structuredCommentValidation.js +56 -0
- package/dist/mcp/structuredDocValidation.d.ts +6 -0
- package/dist/mcp/structuredDocValidation.js +75 -0
- package/dist/migrations/billingSchemaParity.test.d.ts +1 -0
- package/dist/migrations/billingSchemaParity.test.js +46 -0
- package/dist/migrations/clientMigrations.d.ts +9 -0
- package/dist/migrations/clientMigrations.js +74 -0
- package/dist/migrations/flags.d.ts +6 -0
- package/dist/migrations/flags.js +8 -0
- package/dist/migrations/taskMigrations.d.ts +71 -0
- package/dist/migrations/taskMigrations.js +236 -0
- package/dist/migrations/taskMigrations.test.d.ts +1 -0
- package/dist/migrations/taskMigrations.test.js +34 -0
- package/dist/migrations/taskSchemaMigrations.d.ts +11 -0
- package/dist/migrations/taskSchemaMigrations.js +453 -0
- package/dist/plugins/index.d.ts +10 -0
- package/dist/plugins/index.js +10 -0
- package/dist/plugins/vite.d.ts +19 -0
- package/dist/plugins/vite.js +271 -0
- package/dist/public/android-chrome-192x192.png +0 -0
- package/dist/public/android-chrome-512x512.png +0 -0
- package/dist/public/apple-touch-icon.png +0 -0
- package/dist/public/favicon-16x16.png +0 -0
- package/dist/public/favicon-32x32.png +0 -0
- package/dist/public/favicon.ico +0 -0
- package/dist/public/favicon.png +0 -0
- package/dist/public/site.webmanifest +1 -0
- package/dist/public/taskforce.png +0 -0
- package/dist/resources/templates/WORKFLOW_V2_POLICY.md +55 -0
- package/dist/resources/templates/environments/aider.yaml +14 -0
- package/dist/resources/templates/environments/antigravity.yaml +10 -0
- package/dist/resources/templates/environments/claude.yaml +10 -0
- package/dist/resources/templates/environments/cline.yaml +10 -0
- package/dist/resources/templates/environments/codex.yaml +13 -0
- package/dist/resources/templates/environments/copilot.yaml +10 -0
- package/dist/resources/templates/environments/cursor.yaml +9 -0
- package/dist/resources/templates/environments/gemini.yaml +9 -0
- package/dist/resources/templates/environments/windsurf.yaml +10 -0
- package/dist/resources/templates/workflow-sources/README.md +9 -0
- package/dist/resources/templates/workflow-sources/workflowDocs.mjs +520 -0
- package/dist/resources/templates/workflow-sources/workflowEditorOverrides.json +1 -0
- package/dist/resources/templates/workflows/collaborate.yaml +104 -0
- package/dist/resources/templates/workflows/do.yaml +82 -0
- package/dist/resources/templates/workflows/evaluate.yaml +111 -0
- package/dist/resources/templates/workflows/execute.yaml +142 -0
- package/dist/resources/templates/workflows/plan.yaml +112 -0
- package/dist/resources/templates/workflows/review.yaml +109 -0
- package/dist/runtime/capabilities.d.ts +8 -0
- package/dist/runtime/capabilities.js +16 -0
- package/dist/security/virusScan.d.ts +16 -0
- package/dist/security/virusScan.js +83 -0
- package/dist/server/auth.d.ts +51 -0
- package/dist/server/auth.js +266 -0
- package/dist/server/auth.test.d.ts +1 -0
- package/dist/server/auth.test.js +111 -0
- package/dist/server/buildInfo.d.ts +7 -0
- package/dist/server/buildInfo.js +100 -0
- package/dist/server/buildInfo.test.d.ts +1 -0
- package/dist/server/buildInfo.test.js +29 -0
- package/dist/server/cors.d.ts +7 -0
- package/dist/server/cors.js +76 -0
- package/dist/server/cors.test.d.ts +1 -0
- package/dist/server/cors.test.js +71 -0
- package/dist/server/email.d.ts +28 -0
- package/dist/server/email.js +138 -0
- package/dist/server/email.test.d.ts +1 -0
- package/dist/server/email.test.js +113 -0
- package/dist/server/index.cookieProxy.test.d.ts +1 -0
- package/dist/server/index.cookieProxy.test.js +19 -0
- package/dist/server/index.d.ts +17 -0
- package/dist/server/index.js +757 -0
- package/dist/server/index.rateLimit.test.d.ts +1 -0
- package/dist/server/index.rateLimit.test.js +13 -0
- package/dist/server/index.test.d.ts +1 -0
- package/dist/server/index.test.js +19 -0
- package/dist/server/rateLimit.d.ts +29 -0
- package/dist/server/rateLimit.js +99 -0
- package/dist/server/rateLimit.test.d.ts +1 -0
- package/dist/server/rateLimit.test.js +107 -0
- package/dist/server/realtimeSync.auth.test.d.ts +1 -0
- package/dist/server/realtimeSync.auth.test.js +62 -0
- package/dist/server/realtimeSync.replay.test.d.ts +1 -0
- package/dist/server/realtimeSync.replay.test.js +192 -0
- package/dist/server/realtimeSync.workspaceIsolation.test.d.ts +1 -0
- package/dist/server/realtimeSync.workspaceIsolation.test.js +101 -0
- package/dist/server/realtimeSyncWs.d.ts +30 -0
- package/dist/server/realtimeSyncWs.js +257 -0
- package/dist/server/routes.billing.d.ts +15 -0
- package/dist/server/routes.billing.js +991 -0
- package/dist/server/routes.billing.test.d.ts +1 -0
- package/dist/server/routes.billing.test.js +387 -0
- package/dist/server/routes.d.ts +72 -0
- package/dist/server/routes.js +4820 -0
- package/dist/server/routes.sync.d.ts +17 -0
- package/dist/server/routes.sync.integration.test.d.ts +1 -0
- package/dist/server/routes.sync.integration.test.js +470 -0
- package/dist/server/routes.sync.js +1141 -0
- package/dist/server/routes.test.d.ts +1 -0
- package/dist/server/routes.test.js +4945 -0
- package/dist/server/runtimeMode.d.ts +4 -0
- package/dist/server/runtimeMode.js +25 -0
- package/dist/server/runtimeMode.test.d.ts +1 -0
- package/dist/server/runtimeMode.test.js +26 -0
- package/dist/server/securityHeaders.d.ts +3 -0
- package/dist/server/securityHeaders.js +25 -0
- package/dist/server/securityHeaders.test.d.ts +1 -0
- package/dist/server/securityHeaders.test.js +32 -0
- package/dist/server/smtpTransport.d.ts +21 -0
- package/dist/server/smtpTransport.js +11 -0
- package/dist/server/start.d.ts +1 -0
- package/dist/server/start.js +17 -0
- package/dist/services/formatTransformers.d.ts +29 -0
- package/dist/services/formatTransformers.js +56 -0
- package/dist/services/templateEngine.d.ts +46 -0
- package/dist/services/templateEngine.js +148 -0
- package/dist/shared/runtimeContract.d.ts +12 -0
- package/dist/shared/runtimeContract.js +23 -0
- package/dist/shared/runtimeContract.test.d.ts +1 -0
- package/dist/shared/runtimeContract.test.js +23 -0
- package/dist/soak/preflight.cli.d.ts +1 -0
- package/dist/soak/preflight.cli.js +28 -0
- package/dist/soak/preflight.d.ts +26 -0
- package/dist/soak/preflight.js +113 -0
- package/dist/soak/preflight.test.d.ts +1 -0
- package/dist/soak/preflight.test.js +39 -0
- package/dist/soak/syncSoakHarness.d.ts +6 -0
- package/dist/soak/syncSoakHarness.js +10 -0
- package/dist/storage/createDatabaseAdapter.d.ts +2 -0
- package/dist/storage/createDatabaseAdapter.js +9 -0
- package/dist/storage/databaseAdapter.d.ts +22 -0
- package/dist/storage/databaseAdapter.js +1 -0
- package/dist/storage/documentIntegrity.d.ts +28 -0
- package/dist/storage/documentIntegrity.js +103 -0
- package/dist/storage/documentPurge.d.ts +14 -0
- package/dist/storage/documentPurge.js +46 -0
- package/dist/storage/documentRegistry.d.ts +39 -0
- package/dist/storage/documentRegistry.js +115 -0
- package/dist/storage/integritySchedule.d.ts +17 -0
- package/dist/storage/integritySchedule.js +62 -0
- package/dist/storage/objectStorage.d.ts +20 -0
- package/dist/storage/objectStorage.js +15 -0
- package/dist/storage/objectStorageClient.d.ts +18 -0
- package/dist/storage/objectStorageClient.js +301 -0
- package/dist/storage/objectStorageConfig.d.ts +2 -0
- package/dist/storage/objectStorageConfig.js +45 -0
- package/dist/storage/postgresAdapter.d.ts +20 -0
- package/dist/storage/postgresAdapter.js +204 -0
- package/dist/storage/postgresWorker.d.ts +1 -0
- package/dist/storage/postgresWorker.js +110 -0
- package/dist/storage/providerConfig.d.ts +6 -0
- package/dist/storage/providerConfig.js +11 -0
- package/dist/storage/sqliteAdapter.d.ts +14 -0
- package/dist/storage/sqliteAdapter.js +28 -0
- package/dist/storage/taskAssetStore.d.ts +56 -0
- package/dist/storage/taskAssetStore.js +297 -0
- package/dist/sync/cloudSyncApi.d.ts +47 -0
- package/dist/sync/cloudSyncApi.js +138 -0
- package/dist/sync/cloudSyncApi.test.d.ts +1 -0
- package/dist/sync/cloudSyncApi.test.js +54 -0
- package/dist/sync/encryptionService.d.ts +11 -0
- package/dist/sync/encryptionService.js +61 -0
- package/dist/sync/encryptionService.test.d.ts +1 -0
- package/dist/sync/encryptionService.test.js +52 -0
- package/dist/sync/realtimeFeature.d.ts +6 -0
- package/dist/sync/realtimeFeature.js +20 -0
- package/dist/sync/realtimeFeature.test.d.ts +1 -0
- package/dist/sync/realtimeFeature.test.js +30 -0
- package/dist/sync/realtimeReconcile.d.ts +30 -0
- package/dist/sync/realtimeReconcile.js +90 -0
- package/dist/sync/realtimeReconcile.test.d.ts +1 -0
- package/dist/sync/realtimeReconcile.test.js +43 -0
- package/dist/sync/syncService.d.ts +96 -0
- package/dist/sync/syncService.js +423 -0
- package/dist/sync/syncService.test.d.ts +1 -0
- package/dist/sync/syncService.test.js +484 -0
- package/dist/sync/workspaceSyncModel.d.ts +61 -0
- package/dist/sync/workspaceSyncModel.js +136 -0
- package/dist/sync/workspaceSyncModel.test.d.ts +1 -0
- package/dist/sync/workspaceSyncModel.test.js +73 -0
- package/dist/test/setup.d.ts +1 -0
- package/dist/test/setup.js +15 -0
- package/dist/types.d.ts +157 -0
- package/dist/types.js +41 -0
- package/dist/ui/assets/RussoOne-Regular-C3BxZIj7.ttf +0 -0
- package/dist/ui/assets/index-0W5vyCpn.css +1 -0
- package/dist/ui/assets/index-MCZuNNfn.js +23 -0
- package/dist/ui/assets/logo_white_01-0Vx-iIIv.png +0 -0
- package/dist/ui/assets/taskforce-BxLPokNB.png +0 -0
- package/dist/ui/assets/vendor-dnd-BI1K77C1.js +5 -0
- package/dist/ui/assets/vendor-icons-ZFG3SGwo.js +1 -0
- package/dist/ui/assets/vendor-markdown-CKYkQHfj.js +29 -0
- package/dist/ui/assets/vendor-react-DdIDsh7f.js +9 -0
- package/dist/ui/assets/vendor-router-BtiNU7Dl.js +3 -0
- package/dist/ui/branding/logos/logo_black_01.png +0 -0
- package/dist/ui/branding/logos/logo_black_02.png +0 -0
- package/dist/ui/branding/logos/logo_white_01.png +0 -0
- package/dist/ui/branding/logos/logo_white_02.png +0 -0
- package/dist/ui/favicon/android-chrome-192x192.png +0 -0
- package/dist/ui/favicon/android-chrome-512x512.png +0 -0
- package/dist/ui/favicon/apple-touch-icon.png +0 -0
- package/dist/ui/favicon/favicon-16x16.png +0 -0
- package/dist/ui/favicon/favicon-32x32.png +0 -0
- package/dist/ui/favicon/favicon.ico +0 -0
- package/dist/ui/favicon/favicon.png +0 -0
- package/dist/ui/favicon/site.webmanifest +1 -0
- package/dist/ui/fonts/RussoOne-Regular.ttf +0 -0
- package/dist/ui/images/taskforce.png +0 -0
- package/dist/ui/index.html +19 -0
- package/dist/utils/checklistMarkdown.d.ts +9 -0
- package/dist/utils/checklistMarkdown.js +46 -0
- package/dist/utils/constants.d.ts +21 -0
- package/dist/utils/constants.icons.test.d.ts +1 -0
- package/dist/utils/constants.icons.test.js +40 -0
- package/dist/utils/constants.js +100 -0
- package/dist/utils/contextFiles.d.ts +8 -0
- package/dist/utils/contextFiles.js +54 -0
- package/dist/utils/formatting.d.ts +5 -0
- package/dist/utils/formatting.js +30 -0
- package/dist/utils/planParser.d.ts +22 -0
- package/dist/utils/planParser.js +151 -0
- package/dist/utils/taskDragZones.d.ts +8 -0
- package/dist/utils/taskDragZones.js +14 -0
- package/dist/utils/taskDragZones.test.d.ts +1 -0
- package/dist/utils/taskDragZones.test.js +14 -0
- package/dist/utils/taskHierarchy.d.ts +4 -0
- package/dist/utils/taskHierarchy.js +69 -0
- package/dist/utils/taskHierarchy.test.d.ts +1 -0
- package/dist/utils/taskHierarchy.test.js +38 -0
- package/dist/utils/taskParenting.d.ts +3 -0
- package/dist/utils/taskParenting.js +24 -0
- package/dist/utils/taskParenting.test.d.ts +1 -0
- package/dist/utils/taskParenting.test.js +21 -0
- package/dist/utils/taskRelations.d.ts +6 -0
- package/dist/utils/taskRelations.js +24 -0
- package/dist/utils/taskRelations.test.d.ts +1 -0
- package/dist/utils/taskRelations.test.js +34 -0
- package/dist/utils/taskUtils.d.ts +2 -0
- package/dist/utils/taskUtils.js +20 -0
- package/dist/utils/taskforceApiClient.d.ts +7 -0
- package/dist/utils/taskforceApiClient.js +42 -0
- package/dist/utils/workspaceIdentity.d.ts +8 -0
- package/dist/utils/workspaceIdentity.js +47 -0
- package/dist/utils/workspaceIdentity.test.d.ts +1 -0
- package/dist/utils/workspaceIdentity.test.js +32 -0
- package/dist/utils/workspaceLifecycle.d.ts +26 -0
- package/dist/utils/workspaceLifecycle.js +69 -0
- package/dist/utils/workspaceLifecycle.test.d.ts +1 -0
- package/dist/utils/workspaceLifecycle.test.js +64 -0
- package/package.json +133 -0
- package/scripts/check-compat-boundaries.js +48 -0
- package/scripts/check-env.ts +24 -0
- package/scripts/check-localization-strings.mjs +49 -0
- package/scripts/check-runtime-boundaries.mjs +44 -0
- package/scripts/debug-global.mjs +34 -0
- package/scripts/debug-server.mjs +36 -0
- package/scripts/export-sqlite-to-postgres.mjs +125 -0
- package/scripts/generate-env-example.ts +8 -0
- package/scripts/generate-workflow-templates.mjs +134 -0
- package/scripts/mcp-stdio-adapter.mjs +160 -0
- package/scripts/postinstall.js +60 -0
- package/scripts/release-version-lib.mjs +88 -0
- package/scripts/release-version.mjs +72 -0
- package/scripts/run-dev-proxy.sh +49 -0
- package/scripts/run-e2e-realtime.sh +16 -0
- package/scripts/run-smoke.sh +23 -0
- package/scripts/run-soak.sh +29 -0
- package/scripts/test-global.js +143 -0
- package/scripts/update-homebrew-formula.sh +38 -0
- package/src/resources/templates/WORKFLOW_V2_POLICY.md +55 -0
- package/src/resources/templates/environments/aider.yaml +14 -0
- package/src/resources/templates/environments/antigravity.yaml +10 -0
- package/src/resources/templates/environments/claude.yaml +10 -0
- package/src/resources/templates/environments/cline.yaml +10 -0
- package/src/resources/templates/environments/codex.yaml +13 -0
- package/src/resources/templates/environments/copilot.yaml +10 -0
- package/src/resources/templates/environments/cursor.yaml +9 -0
- package/src/resources/templates/environments/gemini.yaml +9 -0
- package/src/resources/templates/environments/windsurf.yaml +10 -0
- package/src/resources/templates/workflow-sources/README.md +9 -0
- package/src/resources/templates/workflow-sources/workflowDocs.mjs +520 -0
- package/src/resources/templates/workflow-sources/workflowEditorOverrides.json +1 -0
- package/src/resources/templates/workflows/collaborate.yaml +104 -0
- package/src/resources/templates/workflows/do.yaml +82 -0
- package/src/resources/templates/workflows/evaluate.yaml +111 -0
- package/src/resources/templates/workflows/execute.yaml +142 -0
- package/src/resources/templates/workflows/plan.yaml +112 -0
- package/src/resources/templates/workflows/review.yaml +109 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* planParser.ts
|
|
3
|
+
* Parses a markdown implementation plan into a structured task tree.
|
|
4
|
+
* Pure function — no I/O, no side effects.
|
|
5
|
+
*/
|
|
6
|
+
// ─── Inference helpers ──────────────────────────────────────────────────────
|
|
7
|
+
function inferType(title) {
|
|
8
|
+
const l = title.toLowerCase();
|
|
9
|
+
if (/\b(bug|fix|fixes|fixed|broken|broken|regression|hotfix)\b/.test(l))
|
|
10
|
+
return 'bug';
|
|
11
|
+
if (/\b(refactor|refactoring|rewrite|cleanup|clean.?up|reorgani[sz]e)\b/.test(l))
|
|
12
|
+
return 'refactor';
|
|
13
|
+
if (/\b(doc|docs|documentation|readme|changelog|comment|comments)\b/.test(l))
|
|
14
|
+
return 'documentation';
|
|
15
|
+
if (/\b(chore|setup|set.?up|config|configuration|infra|infrastructure|ci|cd|deploy|migration|upgrade)\b/.test(l))
|
|
16
|
+
return 'chore';
|
|
17
|
+
return 'feature';
|
|
18
|
+
}
|
|
19
|
+
function inferPriority(title) {
|
|
20
|
+
const l = title.toLowerCase();
|
|
21
|
+
if (/\b(critical|blocker|p0|urgent)\b/.test(l))
|
|
22
|
+
return 4;
|
|
23
|
+
if (/\b(high|important|p1)\b/.test(l))
|
|
24
|
+
return 3;
|
|
25
|
+
if (/\b(low|minor|nice.?to.?have|p3)\b/.test(l))
|
|
26
|
+
return 1;
|
|
27
|
+
return 2;
|
|
28
|
+
}
|
|
29
|
+
function slugify(text) {
|
|
30
|
+
return text.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '').slice(0, 60);
|
|
31
|
+
}
|
|
32
|
+
function makeKey(title, depth, parentKey = '') {
|
|
33
|
+
const s = slugify(title);
|
|
34
|
+
return parentKey ? `${parentKey}:${depth}:${s}` : `${depth}:${s}`;
|
|
35
|
+
}
|
|
36
|
+
function classifyLine(raw) {
|
|
37
|
+
const h2 = raw.match(/^##\s+(.+)/);
|
|
38
|
+
if (h2)
|
|
39
|
+
return { kind: 'h2', text: h2[1].trim() };
|
|
40
|
+
const h3 = raw.match(/^###\s+(.+)/);
|
|
41
|
+
if (h3)
|
|
42
|
+
return { kind: 'h3', text: h3[1].trim() };
|
|
43
|
+
const h4 = raw.match(/^####\s+(.+)/);
|
|
44
|
+
if (h4)
|
|
45
|
+
return { kind: 'h4', text: h4[1].trim() };
|
|
46
|
+
// List items: -, *, + with optional leading whitespace
|
|
47
|
+
const li = raw.match(/^(\s*)[-*+]\s+(.+)/);
|
|
48
|
+
if (li)
|
|
49
|
+
return { kind: 'listItem', text: li[2].trim(), indent: li[1].length };
|
|
50
|
+
// Numbered list
|
|
51
|
+
const oli = raw.match(/^(\s*)\d+[.)]\s+(.+)/);
|
|
52
|
+
if (oli)
|
|
53
|
+
return { kind: 'listItem', text: oli[2].trim(), indent: oli[1].length };
|
|
54
|
+
return { kind: 'other', text: raw.trim() };
|
|
55
|
+
}
|
|
56
|
+
// ─── Main parser ─────────────────────────────────────────────────────────────
|
|
57
|
+
export function parsePlan(markdown) {
|
|
58
|
+
const lines = markdown.split('\n');
|
|
59
|
+
const roots = [];
|
|
60
|
+
let currentH2 = null;
|
|
61
|
+
let currentH3 = null;
|
|
62
|
+
// Track context description lines (plain text before first bullet/heading belongs to current node)
|
|
63
|
+
let descBuffer = [];
|
|
64
|
+
function flushDesc(node) {
|
|
65
|
+
if (node && descBuffer.length > 0) {
|
|
66
|
+
const candidate = descBuffer.join(' ').trim();
|
|
67
|
+
if (candidate && !node.description)
|
|
68
|
+
node.description = candidate;
|
|
69
|
+
}
|
|
70
|
+
descBuffer = [];
|
|
71
|
+
}
|
|
72
|
+
function makeNode(title, depth, parentKey = '') {
|
|
73
|
+
return {
|
|
74
|
+
idempotencyKey: makeKey(title, depth, parentKey),
|
|
75
|
+
title,
|
|
76
|
+
description: '',
|
|
77
|
+
type: inferType(title),
|
|
78
|
+
priority: inferPriority(title),
|
|
79
|
+
depth,
|
|
80
|
+
children: [],
|
|
81
|
+
selected: true,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
for (const rawLine of lines) {
|
|
85
|
+
const line = classifyLine(rawLine);
|
|
86
|
+
if (line.kind === 'h2') {
|
|
87
|
+
flushDesc(currentH3 ?? currentH2);
|
|
88
|
+
currentH3 = null;
|
|
89
|
+
currentH2 = makeNode(line.text, 0);
|
|
90
|
+
roots.push(currentH2);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (line.kind === 'h3') {
|
|
94
|
+
flushDesc(currentH3 ?? currentH2);
|
|
95
|
+
currentH3 = makeNode(line.text, 1, currentH2?.idempotencyKey);
|
|
96
|
+
if (currentH2) {
|
|
97
|
+
currentH2.children.push(currentH3);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
// H3 without parent H2 — treat as root
|
|
101
|
+
roots.push(currentH3);
|
|
102
|
+
}
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (line.kind === 'h4') {
|
|
106
|
+
// H4s treated as list-item equivalents under H3
|
|
107
|
+
flushDesc(currentH3 ?? currentH2);
|
|
108
|
+
const parent = currentH3 ?? currentH2;
|
|
109
|
+
if (parent) {
|
|
110
|
+
const node = makeNode(line.text, parent.depth + 1, parent.idempotencyKey);
|
|
111
|
+
parent.children.push(node);
|
|
112
|
+
}
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (line.kind === 'listItem') {
|
|
116
|
+
flushDesc(currentH3 ?? currentH2);
|
|
117
|
+
const parent = currentH3 ?? currentH2;
|
|
118
|
+
if (parent) {
|
|
119
|
+
const depth = parent.depth + 1;
|
|
120
|
+
const node = makeNode(line.text, depth, parent.idempotencyKey);
|
|
121
|
+
parent.children.push(node);
|
|
122
|
+
}
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
// Plain text — could be description
|
|
126
|
+
if (line.kind === 'other' && line.text && !line.text.startsWith('#')) {
|
|
127
|
+
// Strip markdown formatting and horizontal rules
|
|
128
|
+
const clean = line.text.replace(/^[-*_]{3,}$/, '').trim();
|
|
129
|
+
if (clean)
|
|
130
|
+
descBuffer.push(clean);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
flushDesc(currentH3 ?? currentH2);
|
|
134
|
+
// Filter out nodes that are purely structural (e.g. document title H2 with no list children)
|
|
135
|
+
// Keep only nodes that have a actionable title (not just "Goal", "Overview", etc.)
|
|
136
|
+
return roots.filter(r => r.title.length > 0);
|
|
137
|
+
}
|
|
138
|
+
// Flatten a parsed tree to a list (breadth-first), only selected nodes
|
|
139
|
+
export function flattenSelected(tasks) {
|
|
140
|
+
const result = [];
|
|
141
|
+
function walk(nodes) {
|
|
142
|
+
for (const node of nodes) {
|
|
143
|
+
if (node.selected) {
|
|
144
|
+
result.push(node);
|
|
145
|
+
walk(node.children);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
walk(tasks);
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const isDropInCenterZone = (overRect, activeRect, centerZoneRatio = 0.56) => {
|
|
2
|
+
const zoneWidth = overRect.width * centerZoneRatio;
|
|
3
|
+
const zoneHeight = overRect.height * centerZoneRatio;
|
|
4
|
+
const zoneLeft = overRect.left + (overRect.width - zoneWidth) / 2;
|
|
5
|
+
const zoneTop = overRect.top + (overRect.height - zoneHeight) / 2;
|
|
6
|
+
const zoneRight = zoneLeft + zoneWidth;
|
|
7
|
+
const zoneBottom = zoneTop + zoneHeight;
|
|
8
|
+
const activeCenterX = activeRect.left + activeRect.width / 2;
|
|
9
|
+
const activeCenterY = activeRect.top + activeRect.height / 2;
|
|
10
|
+
return (activeCenterX >= zoneLeft &&
|
|
11
|
+
activeCenterX <= zoneRight &&
|
|
12
|
+
activeCenterY >= zoneTop &&
|
|
13
|
+
activeCenterY <= zoneBottom);
|
|
14
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { isDropInCenterZone } from './taskDragZones';
|
|
3
|
+
describe('taskDragZones', () => {
|
|
4
|
+
it('returns true when dragged card center is inside the target center zone', () => {
|
|
5
|
+
const overRect = { left: 0, top: 0, width: 200, height: 120 };
|
|
6
|
+
const activeRect = { left: 80, top: 45, width: 40, height: 30 };
|
|
7
|
+
expect(isDropInCenterZone(overRect, activeRect)).toBe(true);
|
|
8
|
+
});
|
|
9
|
+
it('returns false when dragged card center is outside the target center zone', () => {
|
|
10
|
+
const overRect = { left: 0, top: 0, width: 200, height: 120 };
|
|
11
|
+
const activeRect = { left: 5, top: 5, width: 30, height: 30 };
|
|
12
|
+
expect(isDropInCenterZone(overRect, activeRect)).toBe(false);
|
|
13
|
+
});
|
|
14
|
+
});
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { TaskItem } from '../types';
|
|
2
|
+
export declare function buildHierarchyDepthMap(tasks: TaskItem[]): Map<string, number>;
|
|
3
|
+
export declare function getMaxHierarchyDepth(tasks: TaskItem[]): number;
|
|
4
|
+
export declare function collectSubtreeTaskIds(tasks: TaskItem[], rootId: string): Set<string>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export function buildHierarchyDepthMap(tasks) {
|
|
2
|
+
const byId = new Map();
|
|
3
|
+
tasks.forEach((task) => byId.set(task.id, task));
|
|
4
|
+
const depthMemo = new Map();
|
|
5
|
+
const resolveDepth = (taskId, visiting) => {
|
|
6
|
+
const cached = depthMemo.get(taskId);
|
|
7
|
+
if (typeof cached === 'number')
|
|
8
|
+
return cached;
|
|
9
|
+
const task = byId.get(taskId);
|
|
10
|
+
if (!task)
|
|
11
|
+
return 0;
|
|
12
|
+
const parentId = task.parentTaskId || null;
|
|
13
|
+
if (!parentId || !byId.has(parentId)) {
|
|
14
|
+
depthMemo.set(taskId, 0);
|
|
15
|
+
return 0;
|
|
16
|
+
}
|
|
17
|
+
if (visiting.has(taskId) || visiting.has(parentId)) {
|
|
18
|
+
// Cycle fallback: treat node as root-level for safe rendering.
|
|
19
|
+
depthMemo.set(taskId, 0);
|
|
20
|
+
return 0;
|
|
21
|
+
}
|
|
22
|
+
visiting.add(taskId);
|
|
23
|
+
const depth = resolveDepth(parentId, visiting) + 1;
|
|
24
|
+
visiting.delete(taskId);
|
|
25
|
+
depthMemo.set(taskId, depth);
|
|
26
|
+
return depth;
|
|
27
|
+
};
|
|
28
|
+
tasks.forEach((task) => {
|
|
29
|
+
resolveDepth(task.id, new Set());
|
|
30
|
+
});
|
|
31
|
+
return depthMemo;
|
|
32
|
+
}
|
|
33
|
+
export function getMaxHierarchyDepth(tasks) {
|
|
34
|
+
const depthMap = buildHierarchyDepthMap(tasks);
|
|
35
|
+
let maxDepth = 0;
|
|
36
|
+
depthMap.forEach((depth) => {
|
|
37
|
+
if (depth > maxDepth)
|
|
38
|
+
maxDepth = depth;
|
|
39
|
+
});
|
|
40
|
+
return maxDepth;
|
|
41
|
+
}
|
|
42
|
+
export function collectSubtreeTaskIds(tasks, rootId) {
|
|
43
|
+
if (!rootId)
|
|
44
|
+
return new Set();
|
|
45
|
+
const childrenByParent = new Map();
|
|
46
|
+
tasks.forEach((task) => {
|
|
47
|
+
if (!task.parentTaskId)
|
|
48
|
+
return;
|
|
49
|
+
const list = childrenByParent.get(task.parentTaskId) || [];
|
|
50
|
+
list.push(task.id);
|
|
51
|
+
childrenByParent.set(task.parentTaskId, list);
|
|
52
|
+
});
|
|
53
|
+
const result = new Set();
|
|
54
|
+
const visited = new Set();
|
|
55
|
+
const stack = [rootId];
|
|
56
|
+
while (stack.length > 0) {
|
|
57
|
+
const currentId = stack.pop();
|
|
58
|
+
if (visited.has(currentId))
|
|
59
|
+
continue;
|
|
60
|
+
visited.add(currentId);
|
|
61
|
+
result.add(currentId);
|
|
62
|
+
const children = childrenByParent.get(currentId) || [];
|
|
63
|
+
children.forEach((childId) => {
|
|
64
|
+
if (!visited.has(childId))
|
|
65
|
+
stack.push(childId);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { collectSubtreeTaskIds } from './taskHierarchy';
|
|
3
|
+
const task = (overrides) => ({
|
|
4
|
+
id: 'task-default',
|
|
5
|
+
title: 'Task',
|
|
6
|
+
description: null,
|
|
7
|
+
category: 'general',
|
|
8
|
+
type: 'feature',
|
|
9
|
+
priority: 2,
|
|
10
|
+
status: 'task',
|
|
11
|
+
createdAt: '2026-02-16T00:00:00.000Z',
|
|
12
|
+
...overrides
|
|
13
|
+
});
|
|
14
|
+
describe('collectSubtreeTaskIds', () => {
|
|
15
|
+
it('collects selected parent and all descendants recursively', () => {
|
|
16
|
+
const root = task({ id: 'task-root' });
|
|
17
|
+
const childA = task({ id: 'task-a', parentTaskId: root.id });
|
|
18
|
+
const childB = task({ id: 'task-b', parentTaskId: root.id });
|
|
19
|
+
const grandchild = task({ id: 'task-c', parentTaskId: childA.id });
|
|
20
|
+
const unrelated = task({ id: 'task-z' });
|
|
21
|
+
const result = collectSubtreeTaskIds([root, childA, childB, grandchild, unrelated], root.id);
|
|
22
|
+
expect(result.has(root.id)).toBe(true);
|
|
23
|
+
expect(result.has(childA.id)).toBe(true);
|
|
24
|
+
expect(result.has(childB.id)).toBe(true);
|
|
25
|
+
expect(result.has(grandchild.id)).toBe(true);
|
|
26
|
+
expect(result.has(unrelated.id)).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
it('is cycle-safe when descendant links create a loop', () => {
|
|
29
|
+
const a = task({ id: 'task-a', parentTaskId: 'task-c' });
|
|
30
|
+
const b = task({ id: 'task-b', parentTaskId: a.id });
|
|
31
|
+
const c = task({ id: 'task-c', parentTaskId: b.id });
|
|
32
|
+
const result = collectSubtreeTaskIds([a, b, c], a.id);
|
|
33
|
+
expect(result.has(a.id)).toBe(true);
|
|
34
|
+
expect(result.has(b.id)).toBe(true);
|
|
35
|
+
expect(result.has(c.id)).toBe(true);
|
|
36
|
+
expect(result.size).toBe(3);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { TaskItem } from '../types';
|
|
2
|
+
export declare const shouldPromptReparent: (currentParentId?: string | null, nextParentId?: string | null) => boolean;
|
|
3
|
+
export declare const wouldCreateParentCycle: (childId: string, nextParentId: string | null | undefined, tasks: TaskItem[]) => boolean;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export const shouldPromptReparent = (currentParentId, nextParentId) => {
|
|
2
|
+
return Boolean(currentParentId && nextParentId && currentParentId !== nextParentId);
|
|
3
|
+
};
|
|
4
|
+
export const wouldCreateParentCycle = (childId, nextParentId, tasks) => {
|
|
5
|
+
if (!nextParentId)
|
|
6
|
+
return false;
|
|
7
|
+
if (childId === nextParentId)
|
|
8
|
+
return true;
|
|
9
|
+
const parentById = new Map();
|
|
10
|
+
for (const task of tasks) {
|
|
11
|
+
parentById.set(task.id, task.parentTaskId);
|
|
12
|
+
}
|
|
13
|
+
let cursor = nextParentId;
|
|
14
|
+
const visited = new Set();
|
|
15
|
+
while (cursor) {
|
|
16
|
+
if (cursor === childId)
|
|
17
|
+
return true;
|
|
18
|
+
if (visited.has(cursor))
|
|
19
|
+
return true;
|
|
20
|
+
visited.add(cursor);
|
|
21
|
+
cursor = parentById.get(cursor) || null;
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { shouldPromptReparent, wouldCreateParentCycle } from './taskParenting';
|
|
3
|
+
describe('taskParenting', () => {
|
|
4
|
+
it('prompts when moving from one parent to a different parent', () => {
|
|
5
|
+
expect(shouldPromptReparent('task-parent-a', 'task-parent-b')).toBe(true);
|
|
6
|
+
});
|
|
7
|
+
it('does not prompt when parent is unchanged or absent', () => {
|
|
8
|
+
expect(shouldPromptReparent('task-parent-a', 'task-parent-a')).toBe(false);
|
|
9
|
+
expect(shouldPromptReparent(null, 'task-parent-a')).toBe(false);
|
|
10
|
+
expect(shouldPromptReparent('task-parent-a', null)).toBe(false);
|
|
11
|
+
});
|
|
12
|
+
it('detects parent cycles through ancestor chains', () => {
|
|
13
|
+
const tasks = [
|
|
14
|
+
{ id: 'task-a', parentTaskId: null },
|
|
15
|
+
{ id: 'task-b', parentTaskId: 'task-a' },
|
|
16
|
+
{ id: 'task-c', parentTaskId: 'task-b' }
|
|
17
|
+
];
|
|
18
|
+
expect(wouldCreateParentCycle('task-a', 'task-c', tasks)).toBe(true);
|
|
19
|
+
expect(wouldCreateParentCycle('task-c', 'task-a', tasks)).toBe(false);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { TaskItem } from '../types';
|
|
2
|
+
export declare const isTerminalStatus: (status?: TaskItem["status"]) => status is "done" | "cancelled";
|
|
3
|
+
export declare const isTerminalTaskState: (task: TaskItem) => boolean;
|
|
4
|
+
export declare const getActiveDirectChildCount: (parentTaskId: string, tasks: TaskItem[]) => number;
|
|
5
|
+
export declare const hasActiveDirectChildren: (parentTaskId: string, tasks: TaskItem[]) => boolean;
|
|
6
|
+
export declare const sortChildTasksForDisplay: (tasks: TaskItem[]) => TaskItem[];
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export const isTerminalStatus = (status) => {
|
|
2
|
+
return status === 'done' || status === 'cancelled';
|
|
3
|
+
};
|
|
4
|
+
export const isTerminalTaskState = (task) => {
|
|
5
|
+
return Boolean(task.isArchived) || isTerminalStatus(task.status);
|
|
6
|
+
};
|
|
7
|
+
export const getActiveDirectChildCount = (parentTaskId, tasks) => {
|
|
8
|
+
return tasks.filter((task) => task.parentTaskId === parentTaskId && !isTerminalTaskState(task)).length;
|
|
9
|
+
};
|
|
10
|
+
export const hasActiveDirectChildren = (parentTaskId, tasks) => {
|
|
11
|
+
return getActiveDirectChildCount(parentTaskId, tasks) > 0;
|
|
12
|
+
};
|
|
13
|
+
export const sortChildTasksForDisplay = (tasks) => {
|
|
14
|
+
return [...tasks].sort((a, b) => {
|
|
15
|
+
const archivedDelta = Number(Boolean(a.isArchived)) - Number(Boolean(b.isArchived));
|
|
16
|
+
if (archivedDelta !== 0)
|
|
17
|
+
return archivedDelta;
|
|
18
|
+
const aOrder = typeof a.childDisplayOrder === 'number' ? a.childDisplayOrder : Number.POSITIVE_INFINITY;
|
|
19
|
+
const bOrder = typeof b.childDisplayOrder === 'number' ? b.childDisplayOrder : Number.POSITIVE_INFINITY;
|
|
20
|
+
if (aOrder !== bOrder)
|
|
21
|
+
return aOrder - bOrder;
|
|
22
|
+
return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
|
|
23
|
+
});
|
|
24
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { getActiveDirectChildCount, hasActiveDirectChildren, isTerminalTaskState } from './taskRelations';
|
|
3
|
+
const makeTask = (overrides) => ({
|
|
4
|
+
id: 'task-default',
|
|
5
|
+
title: 'Task',
|
|
6
|
+
description: null,
|
|
7
|
+
category: 'general',
|
|
8
|
+
type: 'feature',
|
|
9
|
+
priority: 2,
|
|
10
|
+
status: 'task',
|
|
11
|
+
createdAt: new Date().toISOString(),
|
|
12
|
+
...overrides
|
|
13
|
+
});
|
|
14
|
+
describe('taskRelations', () => {
|
|
15
|
+
it('treats archived tasks as terminal state', () => {
|
|
16
|
+
const archived = makeTask({
|
|
17
|
+
id: 'task-archived',
|
|
18
|
+
status: 'task',
|
|
19
|
+
isArchived: true
|
|
20
|
+
});
|
|
21
|
+
expect(isTerminalTaskState(archived)).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
it('does not count archived child tasks as active direct children', () => {
|
|
24
|
+
const tasks = [
|
|
25
|
+
makeTask({ id: 'parent', title: 'Parent' }),
|
|
26
|
+
makeTask({ id: 'active-child', parentTaskId: 'parent', status: 'in-progress' }),
|
|
27
|
+
makeTask({ id: 'archived-child', parentTaskId: 'parent', status: 'task', isArchived: true }),
|
|
28
|
+
makeTask({ id: 'done-child', parentTaskId: 'parent', status: 'done' }),
|
|
29
|
+
makeTask({ id: 'cancelled-child', parentTaskId: 'parent', status: 'cancelled' })
|
|
30
|
+
];
|
|
31
|
+
expect(getActiveDirectChildCount('parent', tasks)).toBe(1);
|
|
32
|
+
expect(hasActiveDirectChildren('parent', tasks)).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import * as Icons from 'lucide-react';
|
|
3
|
+
import { TypeMeta } from './constants';
|
|
4
|
+
import styles from '../Taskforce.module.css';
|
|
5
|
+
export const getIconForType = (type, size = 10) => {
|
|
6
|
+
const meta = TypeMeta[type?.toLowerCase()] || { icon: 'FileCode', color: 'indigo-500' };
|
|
7
|
+
const IconComp = Icons[meta.icon] || Icons.FileCode;
|
|
8
|
+
return _jsx(IconComp, { size: size });
|
|
9
|
+
};
|
|
10
|
+
export const renderHighlighted = (text, query) => {
|
|
11
|
+
if (!text)
|
|
12
|
+
return null;
|
|
13
|
+
if (!query.trim())
|
|
14
|
+
return text;
|
|
15
|
+
const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
16
|
+
const parts = text.split(new RegExp(`(${escapedQuery})`, 'gi'));
|
|
17
|
+
return (_jsx(_Fragment, { children: parts.map((part, i) => part.toLowerCase() === query.toLowerCase()
|
|
18
|
+
? _jsx("mark", { className: styles.highlight, children: part }, i)
|
|
19
|
+
: part) }));
|
|
20
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function resolveTaskforceApiUrl(path: string, apiBaseUrl?: string): string;
|
|
2
|
+
/**
|
|
3
|
+
* Fetches Taskforce API endpoints with a resilient fallback:
|
|
4
|
+
* if a configured apiBaseUrl request fails at the network layer,
|
|
5
|
+
* retry once against same-origin path.
|
|
6
|
+
*/
|
|
7
|
+
export declare function fetchTaskforceApi(path: string, init?: RequestInit, apiBaseUrl?: string): Promise<Response>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
function normalizeApiBaseUrl(apiBaseUrl) {
|
|
2
|
+
return typeof apiBaseUrl === 'string' ? apiBaseUrl.trim().replace(/\/+$/, '') : '';
|
|
3
|
+
}
|
|
4
|
+
export function resolveTaskforceApiUrl(path, apiBaseUrl) {
|
|
5
|
+
if (!path.startsWith('/'))
|
|
6
|
+
return path;
|
|
7
|
+
const base = normalizeApiBaseUrl(apiBaseUrl);
|
|
8
|
+
if (!base)
|
|
9
|
+
return path;
|
|
10
|
+
return `${base}${path}`;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Fetches Taskforce API endpoints with a resilient fallback:
|
|
14
|
+
* if a configured apiBaseUrl request fails at the network layer,
|
|
15
|
+
* retry once against same-origin path.
|
|
16
|
+
*/
|
|
17
|
+
export async function fetchTaskforceApi(path, init, apiBaseUrl) {
|
|
18
|
+
const primaryUrl = resolveTaskforceApiUrl(path, apiBaseUrl);
|
|
19
|
+
const hasConfiguredBase = normalizeApiBaseUrl(apiBaseUrl).length > 0;
|
|
20
|
+
try {
|
|
21
|
+
const primaryResponse = await fetch(primaryUrl, init);
|
|
22
|
+
const shouldRetryAuthFallback = hasConfiguredBase
|
|
23
|
+
&& primaryUrl !== path
|
|
24
|
+
&& path.startsWith('/api/taskforce/')
|
|
25
|
+
&& (primaryResponse.status === 401 || primaryResponse.status === 403);
|
|
26
|
+
if (!shouldRetryAuthFallback) {
|
|
27
|
+
return primaryResponse;
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
return await fetch(path, init);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return primaryResponse;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (primaryError) {
|
|
37
|
+
if (!hasConfiguredBase || !path.startsWith('/')) {
|
|
38
|
+
throw primaryError;
|
|
39
|
+
}
|
|
40
|
+
return fetch(path, init);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const RESERVED_WORKSPACE_ID = "default";
|
|
2
|
+
export declare const WORKSPACE_BOOTSTRAP_SCOPE = "bootstrap";
|
|
3
|
+
export declare function generateWorkspaceId(): string;
|
|
4
|
+
export declare function normalizeWorkspaceStorageScope(scopeRaw?: string): string;
|
|
5
|
+
export declare function normalizeWorkspaceStorageScopeLegacy(scopeRaw?: string): string;
|
|
6
|
+
export declare function resolveWorkspaceStorageScopeFromProjectRoot(projectRoot?: string): string;
|
|
7
|
+
export declare function isReservedWorkspaceId(workspaceId: string): boolean;
|
|
8
|
+
export declare function isUsableWorkspaceId(workspaceId: string): boolean;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export const RESERVED_WORKSPACE_ID = 'default';
|
|
2
|
+
export const WORKSPACE_BOOTSTRAP_SCOPE = 'bootstrap';
|
|
3
|
+
function hashScopeSeed(input) {
|
|
4
|
+
let hash = 2166136261;
|
|
5
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
6
|
+
hash ^= input.charCodeAt(i);
|
|
7
|
+
hash = Math.imul(hash, 16777619);
|
|
8
|
+
}
|
|
9
|
+
return (hash >>> 0).toString(16).padStart(8, '0');
|
|
10
|
+
}
|
|
11
|
+
export function generateWorkspaceId() {
|
|
12
|
+
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
|
|
13
|
+
return `workspace-${crypto.randomUUID()}`;
|
|
14
|
+
}
|
|
15
|
+
return `workspace-${Date.now().toString(16)}-${Math.random().toString(16).slice(2, 14)}`;
|
|
16
|
+
}
|
|
17
|
+
export function normalizeWorkspaceStorageScope(scopeRaw) {
|
|
18
|
+
const raw = String(scopeRaw || '').trim().toLowerCase();
|
|
19
|
+
if (!raw)
|
|
20
|
+
return WORKSPACE_BOOTSTRAP_SCOPE;
|
|
21
|
+
const compact = raw.replace(/[^a-z0-9._-]/g, '-').replace(/-+/g, '-').replace(/^-+|-+$/g, '');
|
|
22
|
+
const normalized = compact || 'project';
|
|
23
|
+
const prefix = normalized.slice(0, 56);
|
|
24
|
+
return `${prefix}-${hashScopeSeed(raw)}`;
|
|
25
|
+
}
|
|
26
|
+
export function normalizeWorkspaceStorageScopeLegacy(scopeRaw) {
|
|
27
|
+
const raw = String(scopeRaw || '').trim().toLowerCase();
|
|
28
|
+
if (!raw)
|
|
29
|
+
return WORKSPACE_BOOTSTRAP_SCOPE;
|
|
30
|
+
const compact = raw.replace(/[^a-z0-9._-]/g, '-').replace(/-+/g, '-').replace(/^-+|-+$/g, '');
|
|
31
|
+
if (!compact)
|
|
32
|
+
return WORKSPACE_BOOTSTRAP_SCOPE;
|
|
33
|
+
return compact.slice(0, 96);
|
|
34
|
+
}
|
|
35
|
+
export function resolveWorkspaceStorageScopeFromProjectRoot(projectRoot) {
|
|
36
|
+
const raw = String(projectRoot || '').trim();
|
|
37
|
+
if (!raw)
|
|
38
|
+
return WORKSPACE_BOOTSTRAP_SCOPE;
|
|
39
|
+
return normalizeWorkspaceStorageScope(raw);
|
|
40
|
+
}
|
|
41
|
+
export function isReservedWorkspaceId(workspaceId) {
|
|
42
|
+
return String(workspaceId || '').trim().toLowerCase() === RESERVED_WORKSPACE_ID;
|
|
43
|
+
}
|
|
44
|
+
export function isUsableWorkspaceId(workspaceId) {
|
|
45
|
+
const normalized = String(workspaceId || '').trim();
|
|
46
|
+
return normalized.length > 0 && !isReservedWorkspaceId(normalized);
|
|
47
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { RESERVED_WORKSPACE_ID, WORKSPACE_BOOTSTRAP_SCOPE, generateWorkspaceId, isReservedWorkspaceId, isUsableWorkspaceId, normalizeWorkspaceStorageScopeLegacy, normalizeWorkspaceStorageScope, resolveWorkspaceStorageScopeFromProjectRoot } from './workspaceIdentity';
|
|
3
|
+
describe('workspace identity invariants', () => {
|
|
4
|
+
it('uses bootstrap scope when project root is unavailable', () => {
|
|
5
|
+
expect(resolveWorkspaceStorageScopeFromProjectRoot('')).toBe(WORKSPACE_BOOTSTRAP_SCOPE);
|
|
6
|
+
expect(normalizeWorkspaceStorageScope('')).toBe(WORKSPACE_BOOTSTRAP_SCOPE);
|
|
7
|
+
});
|
|
8
|
+
it('normalizes project-root-derived scope with stable hash suffix', () => {
|
|
9
|
+
const scope = resolveWorkspaceStorageScopeFromProjectRoot('/Users/ross/My Project');
|
|
10
|
+
expect(scope.startsWith('users-ross-my-project-')).toBe(true);
|
|
11
|
+
expect(scope.length).toBeGreaterThan('users-ross-my-project-'.length);
|
|
12
|
+
});
|
|
13
|
+
it('keeps legacy scope normalization for migration fallback', () => {
|
|
14
|
+
expect(normalizeWorkspaceStorageScopeLegacy('/Users/ross/My Project')).toBe('users-ross-my-project');
|
|
15
|
+
});
|
|
16
|
+
it('avoids collisions for similarly normalized path variants', () => {
|
|
17
|
+
const a = normalizeWorkspaceStorageScope('/Users/ross/Maple Valley Group');
|
|
18
|
+
const b = normalizeWorkspaceStorageScope('/Users/ross/Maple-Valley-Group');
|
|
19
|
+
expect(a).not.toBe(b);
|
|
20
|
+
});
|
|
21
|
+
it('never treats default as usable workspace id', () => {
|
|
22
|
+
expect(isReservedWorkspaceId(RESERVED_WORKSPACE_ID)).toBe(true);
|
|
23
|
+
expect(isReservedWorkspaceId(' Default ')).toBe(true);
|
|
24
|
+
expect(isUsableWorkspaceId('default')).toBe(false);
|
|
25
|
+
expect(isUsableWorkspaceId('workspace-123')).toBe(true);
|
|
26
|
+
});
|
|
27
|
+
it('generates non-default workspace ids with workspace- prefix', () => {
|
|
28
|
+
const id = generateWorkspaceId();
|
|
29
|
+
expect(id.startsWith('workspace-')).toBe(true);
|
|
30
|
+
expect(id.toLowerCase()).not.toBe(RESERVED_WORKSPACE_ID);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type WorkspaceRuntimeMode = 'local' | 'cloud';
|
|
2
|
+
export type SetupStateStatus = 'present' | 'missing' | 'unreadable';
|
|
3
|
+
export type WorkspaceMode = 'core' | 'operations';
|
|
4
|
+
export interface WorkspaceSetupStateSnapshot {
|
|
5
|
+
globalSetupState: SetupStateStatus;
|
|
6
|
+
workspaceSetupState: SetupStateStatus;
|
|
7
|
+
runtimeMode: WorkspaceRuntimeMode;
|
|
8
|
+
workspaceId: string;
|
|
9
|
+
mode: WorkspaceMode;
|
|
10
|
+
forceSetup: boolean;
|
|
11
|
+
forceGlobalSetup: boolean;
|
|
12
|
+
forceWorkspaceSetup: boolean;
|
|
13
|
+
}
|
|
14
|
+
export type WorkspaceLifecyclePhase = 'ready' | 'needs-global-setup' | 'needs-workspace-setup' | 'setup-read-error';
|
|
15
|
+
export interface WorkspaceLifecycleResolution {
|
|
16
|
+
setupGateActive: boolean;
|
|
17
|
+
needsGlobalSetup: boolean;
|
|
18
|
+
needsWorkspaceSetup: boolean;
|
|
19
|
+
hasSetupReadError: boolean;
|
|
20
|
+
phase: WorkspaceLifecyclePhase;
|
|
21
|
+
}
|
|
22
|
+
export declare function resolveWorkspaceLifecycleState(input: {
|
|
23
|
+
setupState: WorkspaceSetupStateSnapshot | null;
|
|
24
|
+
runtimeMode: WorkspaceRuntimeMode;
|
|
25
|
+
isAuthenticated: boolean;
|
|
26
|
+
}): WorkspaceLifecycleResolution;
|