@waymakeros/cli 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +158 -0
- package/dist/cli/cli.d.ts +7 -0
- package/dist/cli/cli.d.ts.map +1 -0
- package/dist/cli/cli.js +703 -0
- package/dist/cli/cli.js.map +1 -0
- package/dist/cli/commands/auth.d.ts +14 -0
- package/dist/cli/commands/auth.d.ts.map +1 -0
- package/dist/cli/commands/auth.js +507 -0
- package/dist/cli/commands/auth.js.map +1 -0
- package/dist/cli/commands/init.d.ts +13 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +297 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/kanban.d.ts +12 -0
- package/dist/cli/commands/kanban.d.ts.map +1 -0
- package/dist/cli/commands/kanban.js +71 -0
- package/dist/cli/commands/kanban.js.map +1 -0
- package/dist/cli/commands/serve.d.ts +12 -0
- package/dist/cli/commands/serve.d.ts.map +1 -0
- package/dist/cli/commands/serve.js +170 -0
- package/dist/cli/commands/serve.js.map +1 -0
- package/dist/cli/commands/sync.d.ts +12 -0
- package/dist/cli/commands/sync.d.ts.map +1 -0
- package/dist/cli/commands/sync.js +222 -0
- package/dist/cli/commands/sync.js.map +1 -0
- package/dist/cli/daemon.d.ts +106 -0
- package/dist/cli/daemon.d.ts.map +1 -0
- package/dist/cli/daemon.js +393 -0
- package/dist/cli/daemon.js.map +1 -0
- package/dist/cli/index.d.ts +13 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +90 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +238 -0
- package/dist/index.js.map +1 -0
- package/dist/simple-server.d.ts +7 -0
- package/dist/simple-server.d.ts.map +1 -0
- package/dist/simple-server.js +157 -0
- package/dist/simple-server.js.map +1 -0
- package/dist/sync/commander-client.d.ts +161 -0
- package/dist/sync/commander-client.d.ts.map +1 -0
- package/dist/sync/commander-client.js +405 -0
- package/dist/sync/commander-client.js.map +1 -0
- package/dist/sync/config-manager.d.ts +48 -0
- package/dist/sync/config-manager.d.ts.map +1 -0
- package/dist/sync/config-manager.js +169 -0
- package/dist/sync/config-manager.js.map +1 -0
- package/dist/sync/file-watcher.d.ts +53 -0
- package/dist/sync/file-watcher.d.ts.map +1 -0
- package/dist/sync/file-watcher.js +228 -0
- package/dist/sync/file-watcher.js.map +1 -0
- package/dist/sync/folder-service.d.ts +110 -0
- package/dist/sync/folder-service.d.ts.map +1 -0
- package/dist/sync/folder-service.js +298 -0
- package/dist/sync/folder-service.js.map +1 -0
- package/dist/sync/folder-status-mapper.d.ts +106 -0
- package/dist/sync/folder-status-mapper.d.ts.map +1 -0
- package/dist/sync/folder-status-mapper.js +235 -0
- package/dist/sync/folder-status-mapper.js.map +1 -0
- package/dist/sync/layer-resolver.d.ts +54 -0
- package/dist/sync/layer-resolver.d.ts.map +1 -0
- package/dist/sync/layer-resolver.js +206 -0
- package/dist/sync/layer-resolver.js.map +1 -0
- package/dist/sync/markdown-parser.d.ts +49 -0
- package/dist/sync/markdown-parser.d.ts.map +1 -0
- package/dist/sync/markdown-parser.js +202 -0
- package/dist/sync/markdown-parser.js.map +1 -0
- package/dist/sync/reverse-sync.d.ts +139 -0
- package/dist/sync/reverse-sync.d.ts.map +1 -0
- package/dist/sync/reverse-sync.js +773 -0
- package/dist/sync/reverse-sync.js.map +1 -0
- package/dist/sync/sync-engine.d.ts +157 -0
- package/dist/sync/sync-engine.d.ts.map +1 -0
- package/dist/sync/sync-engine.js +875 -0
- package/dist/sync/sync-engine.js.map +1 -0
- package/dist/sync/sync-index.d.ts +150 -0
- package/dist/sync/sync-index.d.ts.map +1 -0
- package/dist/sync/sync-index.js +287 -0
- package/dist/sync/sync-index.js.map +1 -0
- package/dist/sync/trinity-mapper.d.ts +62 -0
- package/dist/sync/trinity-mapper.d.ts.map +1 -0
- package/dist/sync/trinity-mapper.js +548 -0
- package/dist/sync/trinity-mapper.js.map +1 -0
- package/dist/sync/types.d.ts +176 -0
- package/dist/sync/types.d.ts.map +1 -0
- package/dist/sync/types.js +6 -0
- package/dist/sync/types.js.map +1 -0
- package/dist/tools/apply-framework.d.ts +7 -0
- package/dist/tools/apply-framework.d.ts.map +1 -0
- package/dist/tools/apply-framework.js +30 -0
- package/dist/tools/apply-framework.js.map +1 -0
- package/dist/tools/commander-comment-create.d.ts +7 -0
- package/dist/tools/commander-comment-create.d.ts.map +1 -0
- package/dist/tools/commander-comment-create.js +100 -0
- package/dist/tools/commander-comment-create.js.map +1 -0
- package/dist/tools/commander-comment-list.d.ts +7 -0
- package/dist/tools/commander-comment-list.d.ts.map +1 -0
- package/dist/tools/commander-comment-list.js +110 -0
- package/dist/tools/commander-comment-list.js.map +1 -0
- package/dist/tools/commander-document-create.d.ts +7 -0
- package/dist/tools/commander-document-create.d.ts.map +1 -0
- package/dist/tools/commander-document-create.js +168 -0
- package/dist/tools/commander-document-create.js.map +1 -0
- package/dist/tools/commander-document-get.d.ts +7 -0
- package/dist/tools/commander-document-get.d.ts.map +1 -0
- package/dist/tools/commander-document-get.js +104 -0
- package/dist/tools/commander-document-get.js.map +1 -0
- package/dist/tools/commander-document-list.d.ts +7 -0
- package/dist/tools/commander-document-list.d.ts.map +1 -0
- package/dist/tools/commander-document-list.js +117 -0
- package/dist/tools/commander-document-list.js.map +1 -0
- package/dist/tools/commander-document-update.d.ts +7 -0
- package/dist/tools/commander-document-update.d.ts.map +1 -0
- package/dist/tools/commander-document-update.js +133 -0
- package/dist/tools/commander-document-update.js.map +1 -0
- package/dist/tools/commander-folder-create.d.ts +7 -0
- package/dist/tools/commander-folder-create.d.ts.map +1 -0
- package/dist/tools/commander-folder-create.js +157 -0
- package/dist/tools/commander-folder-create.js.map +1 -0
- package/dist/tools/commander-folder-delete.d.ts +7 -0
- package/dist/tools/commander-folder-delete.d.ts.map +1 -0
- package/dist/tools/commander-folder-delete.js +85 -0
- package/dist/tools/commander-folder-delete.js.map +1 -0
- package/dist/tools/commander-folder-get.d.ts +7 -0
- package/dist/tools/commander-folder-get.d.ts.map +1 -0
- package/dist/tools/commander-folder-get.js +94 -0
- package/dist/tools/commander-folder-get.js.map +1 -0
- package/dist/tools/commander-folder-list.d.ts +7 -0
- package/dist/tools/commander-folder-list.d.ts.map +1 -0
- package/dist/tools/commander-folder-list.js +96 -0
- package/dist/tools/commander-folder-list.js.map +1 -0
- package/dist/tools/commander-folder-update.d.ts +7 -0
- package/dist/tools/commander-folder-update.d.ts.map +1 -0
- package/dist/tools/commander-folder-update.js +120 -0
- package/dist/tools/commander-folder-update.js.map +1 -0
- package/dist/tools/commander-framework-apply.d.ts +7 -0
- package/dist/tools/commander-framework-apply.d.ts.map +1 -0
- package/dist/tools/commander-framework-apply.js +110 -0
- package/dist/tools/commander-framework-apply.js.map +1 -0
- package/dist/tools/commander-framework-categories.d.ts +7 -0
- package/dist/tools/commander-framework-categories.d.ts.map +1 -0
- package/dist/tools/commander-framework-categories.js +90 -0
- package/dist/tools/commander-framework-categories.js.map +1 -0
- package/dist/tools/commander-framework-get.d.ts +7 -0
- package/dist/tools/commander-framework-get.d.ts.map +1 -0
- package/dist/tools/commander-framework-get.js +124 -0
- package/dist/tools/commander-framework-get.js.map +1 -0
- package/dist/tools/commander-framework-list.d.ts +7 -0
- package/dist/tools/commander-framework-list.d.ts.map +1 -0
- package/dist/tools/commander-framework-list.js +103 -0
- package/dist/tools/commander-framework-list.js.map +1 -0
- package/dist/tools/commander-goal-create.d.ts +7 -0
- package/dist/tools/commander-goal-create.d.ts.map +1 -0
- package/dist/tools/commander-goal-create.js +130 -0
- package/dist/tools/commander-goal-create.js.map +1 -0
- package/dist/tools/commander-goal-get.d.ts +7 -0
- package/dist/tools/commander-goal-get.d.ts.map +1 -0
- package/dist/tools/commander-goal-get.js +83 -0
- package/dist/tools/commander-goal-get.js.map +1 -0
- package/dist/tools/commander-goal-list.d.ts +7 -0
- package/dist/tools/commander-goal-list.d.ts.map +1 -0
- package/dist/tools/commander-goal-list.js +111 -0
- package/dist/tools/commander-goal-list.js.map +1 -0
- package/dist/tools/commander-goal-update.d.ts +7 -0
- package/dist/tools/commander-goal-update.d.ts.map +1 -0
- package/dist/tools/commander-goal-update.js +110 -0
- package/dist/tools/commander-goal-update.js.map +1 -0
- package/dist/tools/commander-key-result-create.d.ts +7 -0
- package/dist/tools/commander-key-result-create.d.ts.map +1 -0
- package/dist/tools/commander-key-result-create.js +134 -0
- package/dist/tools/commander-key-result-create.js.map +1 -0
- package/dist/tools/commander-key-result-update.d.ts +7 -0
- package/dist/tools/commander-key-result-update.d.ts.map +1 -0
- package/dist/tools/commander-key-result-update.js +119 -0
- package/dist/tools/commander-key-result-update.js.map +1 -0
- package/dist/tools/commander-layer-create.d.ts +7 -0
- package/dist/tools/commander-layer-create.d.ts.map +1 -0
- package/dist/tools/commander-layer-create.js +167 -0
- package/dist/tools/commander-layer-create.js.map +1 -0
- package/dist/tools/commander-layer-delete.d.ts +7 -0
- package/dist/tools/commander-layer-delete.d.ts.map +1 -0
- package/dist/tools/commander-layer-delete.js +141 -0
- package/dist/tools/commander-layer-delete.js.map +1 -0
- package/dist/tools/commander-layer-list.d.ts +7 -0
- package/dist/tools/commander-layer-list.d.ts.map +1 -0
- package/dist/tools/commander-layer-list.js +102 -0
- package/dist/tools/commander-layer-list.js.map +1 -0
- package/dist/tools/commander-presentation.d.ts +7 -0
- package/dist/tools/commander-presentation.d.ts.map +1 -0
- package/dist/tools/commander-presentation.js +185 -0
- package/dist/tools/commander-presentation.js.map +1 -0
- package/dist/tools/commander-project-create.d.ts +7 -0
- package/dist/tools/commander-project-create.d.ts.map +1 -0
- package/dist/tools/commander-project-create.js +130 -0
- package/dist/tools/commander-project-create.js.map +1 -0
- package/dist/tools/commander-project-get.d.ts +7 -0
- package/dist/tools/commander-project-get.d.ts.map +1 -0
- package/dist/tools/commander-project-get.js +107 -0
- package/dist/tools/commander-project-get.js.map +1 -0
- package/dist/tools/commander-project-list.d.ts +7 -0
- package/dist/tools/commander-project-list.d.ts.map +1 -0
- package/dist/tools/commander-project-list.js +104 -0
- package/dist/tools/commander-project-list.js.map +1 -0
- package/dist/tools/commander-project-update.d.ts +7 -0
- package/dist/tools/commander-project-update.d.ts.map +1 -0
- package/dist/tools/commander-project-update.js +126 -0
- package/dist/tools/commander-project-update.js.map +1 -0
- package/dist/tools/commander-project.d.ts +7 -0
- package/dist/tools/commander-project.d.ts.map +1 -0
- package/dist/tools/commander-project.js +144 -0
- package/dist/tools/commander-project.js.map +1 -0
- package/dist/tools/commander-role-assign.d.ts +7 -0
- package/dist/tools/commander-role-assign.d.ts.map +1 -0
- package/dist/tools/commander-role-assign.js +115 -0
- package/dist/tools/commander-role-assign.js.map +1 -0
- package/dist/tools/commander-role-create.d.ts +7 -0
- package/dist/tools/commander-role-create.d.ts.map +1 -0
- package/dist/tools/commander-role-create.js +130 -0
- package/dist/tools/commander-role-create.js.map +1 -0
- package/dist/tools/commander-role-get.d.ts +7 -0
- package/dist/tools/commander-role-get.d.ts.map +1 -0
- package/dist/tools/commander-role-get.js +108 -0
- package/dist/tools/commander-role-get.js.map +1 -0
- package/dist/tools/commander-role-list.d.ts +7 -0
- package/dist/tools/commander-role-list.d.ts.map +1 -0
- package/dist/tools/commander-role-list.js +106 -0
- package/dist/tools/commander-role-list.js.map +1 -0
- package/dist/tools/commander-role-update.d.ts +7 -0
- package/dist/tools/commander-role-update.d.ts.map +1 -0
- package/dist/tools/commander-role-update.js +126 -0
- package/dist/tools/commander-role-update.js.map +1 -0
- package/dist/tools/commander-search.d.ts +7 -0
- package/dist/tools/commander-search.d.ts.map +1 -0
- package/dist/tools/commander-search.js +146 -0
- package/dist/tools/commander-search.js.map +1 -0
- package/dist/tools/commander-sheet-create.d.ts +7 -0
- package/dist/tools/commander-sheet-create.d.ts.map +1 -0
- package/dist/tools/commander-sheet-create.js +160 -0
- package/dist/tools/commander-sheet-create.js.map +1 -0
- package/dist/tools/commander-sheet-get.d.ts +7 -0
- package/dist/tools/commander-sheet-get.d.ts.map +1 -0
- package/dist/tools/commander-sheet-get.js +128 -0
- package/dist/tools/commander-sheet-get.js.map +1 -0
- package/dist/tools/commander-sheet-list.d.ts +7 -0
- package/dist/tools/commander-sheet-list.d.ts.map +1 -0
- package/dist/tools/commander-sheet-list.js +108 -0
- package/dist/tools/commander-sheet-list.js.map +1 -0
- package/dist/tools/commander-sheet-update.d.ts +7 -0
- package/dist/tools/commander-sheet-update.d.ts.map +1 -0
- package/dist/tools/commander-sheet-update.js +163 -0
- package/dist/tools/commander-sheet-update.js.map +1 -0
- package/dist/tools/commander-status-list.d.ts +7 -0
- package/dist/tools/commander-status-list.d.ts.map +1 -0
- package/dist/tools/commander-status-list.js +103 -0
- package/dist/tools/commander-status-list.js.map +1 -0
- package/dist/tools/commander-task-assign.d.ts +7 -0
- package/dist/tools/commander-task-assign.d.ts.map +1 -0
- package/dist/tools/commander-task-assign.js +91 -0
- package/dist/tools/commander-task-assign.js.map +1 -0
- package/dist/tools/commander-task-create.d.ts +7 -0
- package/dist/tools/commander-task-create.d.ts.map +1 -0
- package/dist/tools/commander-task-create.js +142 -0
- package/dist/tools/commander-task-create.js.map +1 -0
- package/dist/tools/commander-task-delete.d.ts +7 -0
- package/dist/tools/commander-task-delete.d.ts.map +1 -0
- package/dist/tools/commander-task-delete.js +88 -0
- package/dist/tools/commander-task-delete.js.map +1 -0
- package/dist/tools/commander-task-list-mine.d.ts +7 -0
- package/dist/tools/commander-task-list-mine.d.ts.map +1 -0
- package/dist/tools/commander-task-list-mine.js +109 -0
- package/dist/tools/commander-task-list-mine.js.map +1 -0
- package/dist/tools/commander-task-read.d.ts +10 -0
- package/dist/tools/commander-task-read.d.ts.map +1 -0
- package/dist/tools/commander-task-read.js +96 -0
- package/dist/tools/commander-task-read.js.map +1 -0
- package/dist/tools/commander-task-update.d.ts +7 -0
- package/dist/tools/commander-task-update.d.ts.map +1 -0
- package/dist/tools/commander-task-update.js +159 -0
- package/dist/tools/commander-task-update.js.map +1 -0
- package/dist/tools/commander-taskboard-tasks.d.ts +7 -0
- package/dist/tools/commander-taskboard-tasks.d.ts.map +1 -0
- package/dist/tools/commander-taskboard-tasks.js +97 -0
- package/dist/tools/commander-taskboard-tasks.js.map +1 -0
- package/dist/tools/commander-team-add-member.d.ts +7 -0
- package/dist/tools/commander-team-add-member.d.ts.map +1 -0
- package/dist/tools/commander-team-add-member.js +104 -0
- package/dist/tools/commander-team-add-member.js.map +1 -0
- package/dist/tools/commander-team-create.d.ts +7 -0
- package/dist/tools/commander-team-create.d.ts.map +1 -0
- package/dist/tools/commander-team-create.js +117 -0
- package/dist/tools/commander-team-create.js.map +1 -0
- package/dist/tools/commander-team-list.d.ts +7 -0
- package/dist/tools/commander-team-list.d.ts.map +1 -0
- package/dist/tools/commander-team-list.js +94 -0
- package/dist/tools/commander-team-list.js.map +1 -0
- package/dist/tools/commander-team-remove-member.d.ts +7 -0
- package/dist/tools/commander-team-remove-member.d.ts.map +1 -0
- package/dist/tools/commander-team-remove-member.js +98 -0
- package/dist/tools/commander-team-remove-member.js.map +1 -0
- package/dist/tools/commander-user-get.d.ts +7 -0
- package/dist/tools/commander-user-get.d.ts.map +1 -0
- package/dist/tools/commander-user-get.js +101 -0
- package/dist/tools/commander-user-get.js.map +1 -0
- package/dist/tools/commander-user-invite.d.ts +7 -0
- package/dist/tools/commander-user-invite.d.ts.map +1 -0
- package/dist/tools/commander-user-invite.js +119 -0
- package/dist/tools/commander-user-invite.js.map +1 -0
- package/dist/tools/commander-user-list.d.ts +7 -0
- package/dist/tools/commander-user-list.d.ts.map +1 -0
- package/dist/tools/commander-user-list.js +108 -0
- package/dist/tools/commander-user-list.js.map +1 -0
- package/dist/tools/commander-workspace-create.d.ts +7 -0
- package/dist/tools/commander-workspace-create.d.ts.map +1 -0
- package/dist/tools/commander-workspace-create.js +124 -0
- package/dist/tools/commander-workspace-create.js.map +1 -0
- package/dist/tools/commander-workspace-list.d.ts +7 -0
- package/dist/tools/commander-workspace-list.d.ts.map +1 -0
- package/dist/tools/commander-workspace-list.js +95 -0
- package/dist/tools/commander-workspace-list.js.map +1 -0
- package/dist/tools/commander-workspace-update.d.ts +7 -0
- package/dist/tools/commander-workspace-update.d.ts.map +1 -0
- package/dist/tools/commander-workspace-update.js +118 -0
- package/dist/tools/commander-workspace-update.js.map +1 -0
- package/dist/tools/commander-workspace.d.ts +7 -0
- package/dist/tools/commander-workspace.d.ts.map +1 -0
- package/dist/tools/commander-workspace.js +131 -0
- package/dist/tools/commander-workspace.js.map +1 -0
- package/dist/tools/create-kanban.d.ts +7 -0
- package/dist/tools/create-kanban.d.ts.map +1 -0
- package/dist/tools/create-kanban.js +32 -0
- package/dist/tools/create-kanban.js.map +1 -0
- package/dist/tools/create-table.d.ts +7 -0
- package/dist/tools/create-table.d.ts.map +1 -0
- package/dist/tools/create-table.js +188 -0
- package/dist/tools/create-table.js.map +1 -0
- package/dist/tools/search-knowledge.d.ts +7 -0
- package/dist/tools/search-knowledge.d.ts.map +1 -0
- package/dist/tools/search-knowledge.js +33 -0
- package/dist/tools/search-knowledge.js.map +1 -0
- package/dist/tools/waymaker-kanban-view.d.ts +7 -0
- package/dist/tools/waymaker-kanban-view.d.ts.map +1 -0
- package/dist/tools/waymaker-kanban-view.js +255 -0
- package/dist/tools/waymaker-kanban-view.js.map +1 -0
- package/dist/tools/waymaker-sync-configure.d.ts +13 -0
- package/dist/tools/waymaker-sync-configure.d.ts.map +1 -0
- package/dist/tools/waymaker-sync-configure.js +291 -0
- package/dist/tools/waymaker-sync-configure.js.map +1 -0
- package/dist/tools/waymaker-sync-file.d.ts +7 -0
- package/dist/tools/waymaker-sync-file.d.ts.map +1 -0
- package/dist/tools/waymaker-sync-file.js +79 -0
- package/dist/tools/waymaker-sync-file.js.map +1 -0
- package/dist/tools/waymaker-sync-poll.d.ts +8 -0
- package/dist/tools/waymaker-sync-poll.d.ts.map +1 -0
- package/dist/tools/waymaker-sync-poll.js +398 -0
- package/dist/tools/waymaker-sync-poll.js.map +1 -0
- package/dist/tools/waymaker-sync-start.d.ts +7 -0
- package/dist/tools/waymaker-sync-start.d.ts.map +1 -0
- package/dist/tools/waymaker-sync-start.js +56 -0
- package/dist/tools/waymaker-sync-start.js.map +1 -0
- package/dist/tools/waymaker-sync-status.d.ts +7 -0
- package/dist/tools/waymaker-sync-status.d.ts.map +1 -0
- package/dist/tools/waymaker-sync-status.js +45 -0
- package/dist/tools/waymaker-sync-status.js.map +1 -0
- package/dist/tools/waymaker-sync-stop.d.ts +7 -0
- package/dist/tools/waymaker-sync-stop.d.ts.map +1 -0
- package/dist/tools/waymaker-sync-stop.js +56 -0
- package/dist/tools/waymaker-sync-stop.js.map +1 -0
- package/dist/tools/waymaker-sync-workspace.d.ts +26 -0
- package/dist/tools/waymaker-sync-workspace.d.ts.map +1 -0
- package/dist/tools/waymaker-sync-workspace.js +148 -0
- package/dist/tools/waymaker-sync-workspace.js.map +1 -0
- package/dist/tools/waymaker-sync-workspace.tool.d.ts +9 -0
- package/dist/tools/waymaker-sync-workspace.tool.d.ts.map +1 -0
- package/dist/tools/waymaker-sync-workspace.tool.js +68 -0
- package/dist/tools/waymaker-sync-workspace.tool.js.map +1 -0
- package/dist/types/tool.d.ts +13 -0
- package/dist/types/tool.d.ts.map +1 -0
- package/dist/types/tool.js +2 -0
- package/dist/types/tool.js.map +1 -0
- package/dist/utils/daemon-health.d.ts +21 -0
- package/dist/utils/daemon-health.d.ts.map +1 -0
- package/dist/utils/daemon-health.js +40 -0
- package/dist/utils/daemon-health.js.map +1 -0
- package/dist/utils/fetch-ipv4.d.ts +36 -0
- package/dist/utils/fetch-ipv4.d.ts.map +1 -0
- package/dist/utils/fetch-ipv4.js +91 -0
- package/dist/utils/fetch-ipv4.js.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,875 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Waymaker Sync - Sync Engine
|
|
3
|
+
* Main orchestration engine for bidirectional sync between IDE and Commander
|
|
4
|
+
*
|
|
5
|
+
* Best Practices Implemented:
|
|
6
|
+
* 1. Persistent Index - tracks synced files across daemon restarts
|
|
7
|
+
* 2. Checksum-based change detection - skips unchanged files
|
|
8
|
+
* 3. Retry with exponential backoff - handles transient failures
|
|
9
|
+
* 4. Atomic index writes - prevents corruption
|
|
10
|
+
* 5. Conflict detection - warns when IDE and Commander diverge
|
|
11
|
+
*/
|
|
12
|
+
import * as path from 'path';
|
|
13
|
+
import * as fs from 'fs/promises';
|
|
14
|
+
import { glob } from 'glob';
|
|
15
|
+
import { EventEmitter } from 'events';
|
|
16
|
+
import { ConfigManager } from './config-manager.js';
|
|
17
|
+
import { MarkdownParser } from './markdown-parser.js';
|
|
18
|
+
import { TrinityMapper } from './trinity-mapper.js';
|
|
19
|
+
import { FileWatcher } from './file-watcher.js';
|
|
20
|
+
import { FolderStatusMapper } from './folder-status-mapper.js';
|
|
21
|
+
import { SyncIndexManager } from './sync-index.js';
|
|
22
|
+
/** Retry configuration */
|
|
23
|
+
const RETRY_CONFIG = {
|
|
24
|
+
maxRetries: 3,
|
|
25
|
+
baseDelayMs: 1000,
|
|
26
|
+
maxDelayMs: 10000,
|
|
27
|
+
};
|
|
28
|
+
export class SyncEngine extends EventEmitter {
|
|
29
|
+
configManager;
|
|
30
|
+
parser;
|
|
31
|
+
mapper = null; // Initialized in initialize()
|
|
32
|
+
watcher = null;
|
|
33
|
+
folderStatusMapper;
|
|
34
|
+
indexManager;
|
|
35
|
+
projectPath;
|
|
36
|
+
running = false;
|
|
37
|
+
startedAt = null;
|
|
38
|
+
syncCount = 0;
|
|
39
|
+
errorCount = 0;
|
|
40
|
+
lastSyncAt = null;
|
|
41
|
+
recentErrors = [];
|
|
42
|
+
syncStates = new Map();
|
|
43
|
+
initialScanComplete = false;
|
|
44
|
+
constructor(projectPath) {
|
|
45
|
+
super();
|
|
46
|
+
this.projectPath = projectPath;
|
|
47
|
+
this.configManager = new ConfigManager(projectPath);
|
|
48
|
+
this.parser = new MarkdownParser();
|
|
49
|
+
this.folderStatusMapper = new FolderStatusMapper();
|
|
50
|
+
this.indexManager = new SyncIndexManager(projectPath);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Initialize sync engine
|
|
54
|
+
*/
|
|
55
|
+
async initialize() {
|
|
56
|
+
console.error('[SyncEngine] Initializing...');
|
|
57
|
+
// Load configuration
|
|
58
|
+
await this.configManager.load();
|
|
59
|
+
const config = this.configManager.getConfig();
|
|
60
|
+
console.error('[SyncEngine] Configuration loaded');
|
|
61
|
+
console.error(`[SyncEngine] Workspace: ${config.workspace_id}`);
|
|
62
|
+
console.error(`[SyncEngine] Project: ${config.project_id}`);
|
|
63
|
+
console.error(`[SyncEngine] Taskboard: ${config.sync.taskboard_id || 'not set'}`);
|
|
64
|
+
console.error(`[SyncEngine] Mode: ${config.sync.mode}`);
|
|
65
|
+
// Load persistent sync index
|
|
66
|
+
await this.indexManager.load();
|
|
67
|
+
if (config.sync.taskboard_id) {
|
|
68
|
+
this.indexManager.setTaskboardId(config.sync.taskboard_id);
|
|
69
|
+
}
|
|
70
|
+
const indexStats = this.indexManager.getStats();
|
|
71
|
+
console.error(`[SyncEngine] Index loaded: ${indexStats.filesCount} files tracked, ${indexStats.totalSyncs} total syncs`);
|
|
72
|
+
// Initialize TrinityMapper with FolderService configuration
|
|
73
|
+
const folderServiceConfig = {
|
|
74
|
+
projectId: config.project_id,
|
|
75
|
+
workspaceId: config.workspace_id,
|
|
76
|
+
organizationId: config.organization_id,
|
|
77
|
+
userId: 'sync-engine', // TBD: Get from auth context
|
|
78
|
+
projectRoot: this.projectPath,
|
|
79
|
+
};
|
|
80
|
+
this.mapper = new TrinityMapper(undefined, folderServiceConfig);
|
|
81
|
+
console.error('[SyncEngine] TrinityMapper initialized with FolderService');
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Start sync daemon
|
|
85
|
+
*/
|
|
86
|
+
async start() {
|
|
87
|
+
if (this.running) {
|
|
88
|
+
throw new Error('Sync engine is already running');
|
|
89
|
+
}
|
|
90
|
+
await this.initialize();
|
|
91
|
+
const config = this.configManager.getConfig();
|
|
92
|
+
if (!config.sync.enabled) {
|
|
93
|
+
throw new Error('Sync is disabled in configuration');
|
|
94
|
+
}
|
|
95
|
+
if (!config.sync.taskboard_id) {
|
|
96
|
+
console.error('[SyncEngine] WARNING: No taskboard_id configured. Some sync operations may fail.');
|
|
97
|
+
}
|
|
98
|
+
// Create file watcher
|
|
99
|
+
this.watcher = new FileWatcher({
|
|
100
|
+
watchPatterns: config.sync.watch_patterns,
|
|
101
|
+
ignorePatterns: config.sync.ignore_patterns,
|
|
102
|
+
projectPath: this.projectPath,
|
|
103
|
+
debounceMs: 1000,
|
|
104
|
+
});
|
|
105
|
+
// Set up event handlers
|
|
106
|
+
this.watcher.on('change', (event) => {
|
|
107
|
+
this.handleFileChange(event);
|
|
108
|
+
});
|
|
109
|
+
this.watcher.on('ready', () => {
|
|
110
|
+
console.error('[SyncEngine] File watcher ready');
|
|
111
|
+
this.emit('ready');
|
|
112
|
+
});
|
|
113
|
+
this.watcher.on('error', (error) => {
|
|
114
|
+
console.error('[SyncEngine] Watcher error:', error);
|
|
115
|
+
this.handleError(error.message, 'watcher');
|
|
116
|
+
});
|
|
117
|
+
// Start watching
|
|
118
|
+
this.watcher.start();
|
|
119
|
+
this.running = true;
|
|
120
|
+
this.startedAt = new Date();
|
|
121
|
+
console.error('[SyncEngine] ✅ Sync engine started');
|
|
122
|
+
console.error(`[SyncEngine] Watching ${config.sync.watch_patterns.length} patterns`);
|
|
123
|
+
this.emit('started');
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Stop sync daemon
|
|
127
|
+
*/
|
|
128
|
+
async stop() {
|
|
129
|
+
if (!this.running) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
console.error('[SyncEngine] Stopping sync engine...');
|
|
133
|
+
// Flush pending index writes
|
|
134
|
+
await this.indexManager.flush();
|
|
135
|
+
console.error('[SyncEngine] Index saved');
|
|
136
|
+
// Stop file watcher
|
|
137
|
+
if (this.watcher) {
|
|
138
|
+
await this.watcher.stop();
|
|
139
|
+
this.watcher = null;
|
|
140
|
+
}
|
|
141
|
+
this.running = false;
|
|
142
|
+
this.startedAt = null;
|
|
143
|
+
console.error('[SyncEngine] ✅ Sync engine stopped');
|
|
144
|
+
this.emit('stopped');
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Handle file change events
|
|
148
|
+
*/
|
|
149
|
+
async handleFileChange(event) {
|
|
150
|
+
const config = this.configManager.getConfig();
|
|
151
|
+
const mode = config.sync.mode;
|
|
152
|
+
// Skip if mode is commander-to-ide only
|
|
153
|
+
if (mode === 'commander-to-ide') {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
console.error(`[SyncEngine] Processing ${event.type}: ${event.path}`);
|
|
157
|
+
try {
|
|
158
|
+
// Handle file moves specially - detect status change
|
|
159
|
+
if (event.type === 'move' && event.oldPath) {
|
|
160
|
+
await this.handleFileMove(event);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
// Use retry logic for file changes too
|
|
164
|
+
const result = await this.syncWithRetry(event.path);
|
|
165
|
+
if (result.success) {
|
|
166
|
+
this.syncCount++;
|
|
167
|
+
this.lastSyncAt = new Date();
|
|
168
|
+
// Update sync state
|
|
169
|
+
const checksum = this.indexManager.calculateChecksum(await this.parser.parseFile(path.join(this.projectPath, event.path)).then(p => p.content));
|
|
170
|
+
// Update in-memory state
|
|
171
|
+
this.syncStates.set(event.path, {
|
|
172
|
+
filePath: event.path,
|
|
173
|
+
lastSyncedAt: new Date(),
|
|
174
|
+
lastModifiedAt: event.timestamp,
|
|
175
|
+
commanderId: result.commanderId || null,
|
|
176
|
+
documentType: result.documentType,
|
|
177
|
+
checksum,
|
|
178
|
+
});
|
|
179
|
+
// Update persistent index
|
|
180
|
+
if (result.operation !== 'skip') {
|
|
181
|
+
const parsed = await this.parser.parseFile(path.join(this.projectPath, event.path));
|
|
182
|
+
this.indexManager.updateFileState(event.path, {
|
|
183
|
+
checksum,
|
|
184
|
+
documentType: result.documentType,
|
|
185
|
+
taskId: parsed.frontmatter.sync?.task_id || result.commanderId || undefined,
|
|
186
|
+
documentId: parsed.frontmatter.sync?.document_id || undefined,
|
|
187
|
+
layerId: parsed.frontmatter.sync?.layer_id || undefined,
|
|
188
|
+
fileModifiedAt: event.timestamp,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
this.emit('sync-success', result);
|
|
192
|
+
console.error(`[SyncEngine] ✅ ${result.message}`);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
if (result.operation !== 'skip') {
|
|
196
|
+
this.handleError(result.error || result.message, event.path);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
this.handleError(error instanceof Error ? error.message : String(error), event.path);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Handle file move events - update task status based on folder change
|
|
206
|
+
*/
|
|
207
|
+
async handleFileMove(event) {
|
|
208
|
+
if (!event.oldPath) {
|
|
209
|
+
// Can't process without old path, fall back to normal sync
|
|
210
|
+
await this.syncFileToCommander(event.path);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const absolutePath = path.join(this.projectPath, event.path);
|
|
214
|
+
// Detect status change from folder paths
|
|
215
|
+
const statusChange = this.folderStatusMapper.detectStatusChange(event.oldPath, event.path);
|
|
216
|
+
if (statusChange) {
|
|
217
|
+
console.error(`[SyncEngine] Status change detected: ${statusChange.oldStatus.statusSection} → ${statusChange.newStatus.statusSection}`);
|
|
218
|
+
// Parse the file to get task_id
|
|
219
|
+
const parsed = await this.parser.parseFile(absolutePath);
|
|
220
|
+
if (parsed.frontmatter.sync?.task_id) {
|
|
221
|
+
// Update task status in Commander
|
|
222
|
+
const taskId = parsed.frontmatter.sync.task_id;
|
|
223
|
+
const newStatus = statusChange.newStatus.statusSection;
|
|
224
|
+
console.error(`[SyncEngine] Updating task ${taskId} status to: ${newStatus}`);
|
|
225
|
+
try {
|
|
226
|
+
await this.updateTaskStatus(taskId, newStatus);
|
|
227
|
+
// Update sync state for new path
|
|
228
|
+
const checksum = this.indexManager.calculateChecksum(parsed.content);
|
|
229
|
+
const oldState = this.syncStates.get(event.oldPath);
|
|
230
|
+
const docType = oldState?.documentType || 'task';
|
|
231
|
+
// Update in-memory state
|
|
232
|
+
this.syncStates.delete(event.oldPath);
|
|
233
|
+
this.syncStates.set(event.path, {
|
|
234
|
+
filePath: event.path,
|
|
235
|
+
lastSyncedAt: new Date(),
|
|
236
|
+
lastModifiedAt: event.timestamp,
|
|
237
|
+
commanderId: taskId,
|
|
238
|
+
documentType: docType,
|
|
239
|
+
checksum,
|
|
240
|
+
});
|
|
241
|
+
// Update persistent index (remove old path, add new)
|
|
242
|
+
this.indexManager.removeFile(event.oldPath);
|
|
243
|
+
this.indexManager.updateFileState(event.path, {
|
|
244
|
+
checksum,
|
|
245
|
+
documentType: docType,
|
|
246
|
+
taskId,
|
|
247
|
+
documentId: parsed.frontmatter.sync?.document_id || undefined,
|
|
248
|
+
layerId: parsed.frontmatter.sync?.layer_id || undefined,
|
|
249
|
+
fileModifiedAt: event.timestamp,
|
|
250
|
+
});
|
|
251
|
+
this.syncCount++;
|
|
252
|
+
this.lastSyncAt = new Date();
|
|
253
|
+
const result = {
|
|
254
|
+
success: true,
|
|
255
|
+
operation: 'update',
|
|
256
|
+
documentType: 'task',
|
|
257
|
+
filePath: event.path,
|
|
258
|
+
commanderId: taskId,
|
|
259
|
+
message: `Task status updated: ${statusChange.oldStatus.statusSection} → ${newStatus}`,
|
|
260
|
+
};
|
|
261
|
+
this.emit('sync-success', result);
|
|
262
|
+
console.error(`[SyncEngine] ✅ ${result.message}`);
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
this.handleError(error instanceof Error ? error.message : String(error), event.path);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
// No task_id, do a full sync to create/update
|
|
270
|
+
console.error(`[SyncEngine] No task_id in file, performing full sync`);
|
|
271
|
+
await this.syncFileToCommander(event.path);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
// No status change (move within same status folder), just update path metadata
|
|
276
|
+
console.error(`[SyncEngine] File moved but status unchanged, syncing content`);
|
|
277
|
+
await this.syncFileToCommander(event.path);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Update task status in Commander
|
|
282
|
+
*/
|
|
283
|
+
async updateTaskStatus(taskId, newStatus) {
|
|
284
|
+
// Get API key from env or auth.json
|
|
285
|
+
let apiKey = process.env.WAYMAKER_API_KEY;
|
|
286
|
+
if (!apiKey) {
|
|
287
|
+
const authPath = path.join(process.env.HOME || '', '.waymaker', 'auth.json');
|
|
288
|
+
try {
|
|
289
|
+
const authData = JSON.parse(await fs.readFile(authPath, 'utf-8'));
|
|
290
|
+
apiKey = authData.accessToken;
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
// Ignore auth file read errors
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (!apiKey) {
|
|
297
|
+
throw new Error('Authentication required. Run: waymaker auth login');
|
|
298
|
+
}
|
|
299
|
+
const baseUrl = process.env.WAYMAKER_APPS_URL || 'https://apps.waymakerone.com';
|
|
300
|
+
const response = await fetch(`${baseUrl}/functions/v1/commander-task-operations`, {
|
|
301
|
+
method: 'POST',
|
|
302
|
+
headers: {
|
|
303
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
304
|
+
'Content-Type': 'application/json',
|
|
305
|
+
},
|
|
306
|
+
body: JSON.stringify({
|
|
307
|
+
action: 'update-task',
|
|
308
|
+
id: taskId,
|
|
309
|
+
status: newStatus,
|
|
310
|
+
}),
|
|
311
|
+
});
|
|
312
|
+
if (!response.ok) {
|
|
313
|
+
const errorText = await response.text();
|
|
314
|
+
throw new Error(`Failed to update task status: ${response.status} ${errorText}`);
|
|
315
|
+
}
|
|
316
|
+
const result = await response.json();
|
|
317
|
+
if (!result.success) {
|
|
318
|
+
throw new Error(result.message || 'Failed to update task status');
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Sync a single file to Commander
|
|
323
|
+
*/
|
|
324
|
+
async syncFileToCommander(relativePath) {
|
|
325
|
+
const config = this.configManager.getConfig();
|
|
326
|
+
const absolutePath = path.join(this.projectPath, relativePath);
|
|
327
|
+
if (!config.sync.taskboard_id) {
|
|
328
|
+
return {
|
|
329
|
+
success: false,
|
|
330
|
+
operation: 'skip',
|
|
331
|
+
documentType: 'document',
|
|
332
|
+
filePath: relativePath,
|
|
333
|
+
message: 'No taskboard_id configured',
|
|
334
|
+
error: 'Configuration error: sync.taskboard_id is required',
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
if (!this.mapper) {
|
|
338
|
+
throw new Error('Sync engine not initialized - call initialize() first');
|
|
339
|
+
}
|
|
340
|
+
// Check if file was just written by ReverseSync (prevent sync loop)
|
|
341
|
+
try {
|
|
342
|
+
const parsed = await this.parser.parseFile(absolutePath);
|
|
343
|
+
const commanderModifiedAt = parsed.frontmatter.commander_modified_at;
|
|
344
|
+
if (commanderModifiedAt) {
|
|
345
|
+
const modifiedTime = new Date(commanderModifiedAt).getTime();
|
|
346
|
+
const now = Date.now();
|
|
347
|
+
const timeDiff = now - modifiedTime;
|
|
348
|
+
// If Commander modified this file within the last 10 seconds, skip syncing back
|
|
349
|
+
// This prevents the sync loop between ReverseSync and SyncEngine
|
|
350
|
+
if (timeDiff < 10000) {
|
|
351
|
+
console.log(`[SyncEngine] ✅ Skipping recently synced file (${Math.round(timeDiff / 1000)}s ago from Commander)`);
|
|
352
|
+
return {
|
|
353
|
+
success: true,
|
|
354
|
+
operation: 'skip',
|
|
355
|
+
documentType: 'document',
|
|
356
|
+
filePath: relativePath,
|
|
357
|
+
message: 'Skipped - recently synced from Commander',
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
catch (error) {
|
|
363
|
+
// If parsing fails, continue with sync attempt
|
|
364
|
+
console.error(`[SyncEngine] Warning: Could not check commander_modified_at: ${error}`);
|
|
365
|
+
}
|
|
366
|
+
return await this.mapper.syncToCommander({
|
|
367
|
+
filePath: absolutePath,
|
|
368
|
+
taskboardId: config.sync.taskboard_id,
|
|
369
|
+
projectId: config.project_id,
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Sync with retry logic and exponential backoff
|
|
374
|
+
* Handles transient failures like network timeouts
|
|
375
|
+
*/
|
|
376
|
+
async syncWithRetry(relativePath) {
|
|
377
|
+
let lastError = null;
|
|
378
|
+
for (let attempt = 0; attempt <= RETRY_CONFIG.maxRetries; attempt++) {
|
|
379
|
+
try {
|
|
380
|
+
const result = await this.syncFileToCommander(relativePath);
|
|
381
|
+
// If sync succeeded or was skipped (not an error), return
|
|
382
|
+
if (result.success || result.operation === 'skip') {
|
|
383
|
+
return result;
|
|
384
|
+
}
|
|
385
|
+
// Check if error is retryable
|
|
386
|
+
const errorMsg = result.error || result.message;
|
|
387
|
+
if (!this.isRetryableError(errorMsg)) {
|
|
388
|
+
return result; // Non-retryable error, return immediately
|
|
389
|
+
}
|
|
390
|
+
lastError = new Error(errorMsg);
|
|
391
|
+
}
|
|
392
|
+
catch (error) {
|
|
393
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
394
|
+
// Check if error is retryable
|
|
395
|
+
if (!this.isRetryableError(lastError.message)) {
|
|
396
|
+
throw lastError;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
// If we have more retries, wait with exponential backoff
|
|
400
|
+
if (attempt < RETRY_CONFIG.maxRetries) {
|
|
401
|
+
const delay = Math.min(RETRY_CONFIG.baseDelayMs * Math.pow(2, attempt), RETRY_CONFIG.maxDelayMs);
|
|
402
|
+
console.error(`[SyncEngine] Retry ${attempt + 1}/${RETRY_CONFIG.maxRetries} for ${relativePath} in ${delay}ms`);
|
|
403
|
+
await this.sleep(delay);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
// All retries exhausted
|
|
407
|
+
return {
|
|
408
|
+
success: false,
|
|
409
|
+
operation: 'skip',
|
|
410
|
+
documentType: 'document',
|
|
411
|
+
filePath: relativePath,
|
|
412
|
+
message: `Failed after ${RETRY_CONFIG.maxRetries} retries`,
|
|
413
|
+
error: lastError?.message || 'Unknown error',
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Check if an error is retryable (transient failures)
|
|
418
|
+
*/
|
|
419
|
+
isRetryableError(errorMsg) {
|
|
420
|
+
const retryablePatterns = [
|
|
421
|
+
/timeout/i,
|
|
422
|
+
/ETIMEDOUT/i,
|
|
423
|
+
/ECONNRESET/i,
|
|
424
|
+
/ECONNREFUSED/i,
|
|
425
|
+
/ENOTFOUND/i,
|
|
426
|
+
/network/i,
|
|
427
|
+
/socket hang up/i,
|
|
428
|
+
/503/i, // Service Unavailable
|
|
429
|
+
/502/i, // Bad Gateway
|
|
430
|
+
/504/i, // Gateway Timeout
|
|
431
|
+
/429/i, // Too Many Requests
|
|
432
|
+
];
|
|
433
|
+
return retryablePatterns.some(pattern => pattern.test(errorMsg));
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Sleep helper for retry delays
|
|
437
|
+
*/
|
|
438
|
+
sleep(ms) {
|
|
439
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Handle sync errors
|
|
443
|
+
*/
|
|
444
|
+
handleError(error, filePath) {
|
|
445
|
+
this.errorCount++;
|
|
446
|
+
const errorEntry = {
|
|
447
|
+
timestamp: new Date(),
|
|
448
|
+
error,
|
|
449
|
+
filePath,
|
|
450
|
+
};
|
|
451
|
+
this.recentErrors.push(errorEntry);
|
|
452
|
+
// Keep only last 10 errors
|
|
453
|
+
if (this.recentErrors.length > 10) {
|
|
454
|
+
this.recentErrors.shift();
|
|
455
|
+
}
|
|
456
|
+
console.error(`[SyncEngine] ❌ Error in ${filePath}: ${error}`);
|
|
457
|
+
this.emit('error', errorEntry);
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Get sync daemon status
|
|
461
|
+
*/
|
|
462
|
+
getStatus() {
|
|
463
|
+
const stats = this.watcher?.getStats();
|
|
464
|
+
const indexStats = this.indexManager.getStats();
|
|
465
|
+
return {
|
|
466
|
+
running: this.running,
|
|
467
|
+
startedAt: this.startedAt,
|
|
468
|
+
filesWatched: stats?.filesWatched ?? 0,
|
|
469
|
+
lastSyncAt: this.lastSyncAt,
|
|
470
|
+
syncCount: this.syncCount,
|
|
471
|
+
errorCount: this.errorCount,
|
|
472
|
+
recentErrors: this.recentErrors,
|
|
473
|
+
indexStats,
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Get sync state for a file
|
|
478
|
+
*/
|
|
479
|
+
getSyncState(filePath) {
|
|
480
|
+
return this.syncStates.get(filePath) || null;
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Get all sync states
|
|
484
|
+
*/
|
|
485
|
+
getAllSyncStates() {
|
|
486
|
+
return this.syncStates;
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Manually trigger sync for a specific file
|
|
490
|
+
*/
|
|
491
|
+
async syncFile(relativePath) {
|
|
492
|
+
return await this.syncFileToCommander(relativePath);
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Sync all existing files matching watch patterns
|
|
496
|
+
* This is the "initial scan" that runs on daemon start
|
|
497
|
+
*/
|
|
498
|
+
async syncAll(options = {}) {
|
|
499
|
+
const startTime = Date.now();
|
|
500
|
+
const { batchSize = 50, concurrency = 5, dryRun = false, force = false, } = options;
|
|
501
|
+
const config = this.configManager.getConfig();
|
|
502
|
+
if (!config.sync.taskboard_id) {
|
|
503
|
+
return {
|
|
504
|
+
totalFiles: 0,
|
|
505
|
+
synced: 0,
|
|
506
|
+
skipped: 0,
|
|
507
|
+
failed: 1,
|
|
508
|
+
results: [],
|
|
509
|
+
errors: [{ filePath: '', error: 'No taskboard_id configured' }],
|
|
510
|
+
duration: Date.now() - startTime,
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
console.error('[SyncEngine] Starting initial scan...');
|
|
514
|
+
console.error(`[SyncEngine] Options: batchSize=${batchSize}, concurrency=${concurrency}, dryRun=${dryRun}`);
|
|
515
|
+
console.error(`[SyncEngine] Taskboard ID: ${config.sync.taskboard_id}`);
|
|
516
|
+
console.error(`[SyncEngine] Workspace ID: ${config.workspace_id}`);
|
|
517
|
+
console.error(`[SyncEngine] Organization ID: ${config.organization_id}`);
|
|
518
|
+
console.error(`[SyncEngine] Project ID: ${config.project_id}`);
|
|
519
|
+
// Step 1: Discover and create all folders first (including empty ones)
|
|
520
|
+
// This ensures folder structure parity between IDE and Commander
|
|
521
|
+
console.error('[SyncEngine] Step 1: Discovering directories...');
|
|
522
|
+
const directories = await this.discoverDirectories(config.sync.watch_patterns);
|
|
523
|
+
console.error(`[SyncEngine] Found ${directories.length} directories to sync`);
|
|
524
|
+
if (!dryRun && this.mapper) {
|
|
525
|
+
console.error('[SyncEngine] Creating folder structure in Commander...');
|
|
526
|
+
const folderService = this.mapper.getFolderService();
|
|
527
|
+
if (folderService) {
|
|
528
|
+
let foldersCreated = 0;
|
|
529
|
+
for (const dir of directories) {
|
|
530
|
+
try {
|
|
531
|
+
await folderService.ensureFolderPath(dir);
|
|
532
|
+
foldersCreated++;
|
|
533
|
+
}
|
|
534
|
+
catch (error) {
|
|
535
|
+
console.error(`[SyncEngine] ⚠️ Failed to create folder ${dir}:`, error);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
console.error(`[SyncEngine] ✅ Folder structure synced (${foldersCreated} directories)`);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
// Step 2: Discover all markdown files matching watch patterns
|
|
542
|
+
console.error('[SyncEngine] Step 2: Discovering files...');
|
|
543
|
+
const files = await this.discoverFiles(config.sync.watch_patterns);
|
|
544
|
+
console.error(`[SyncEngine] Found ${files.length} files to scan`);
|
|
545
|
+
if (dryRun) {
|
|
546
|
+
console.error('[SyncEngine] DRY RUN - no files will be synced');
|
|
547
|
+
return {
|
|
548
|
+
totalFiles: files.length,
|
|
549
|
+
synced: 0,
|
|
550
|
+
skipped: files.length,
|
|
551
|
+
failed: 0,
|
|
552
|
+
results: files.map(f => ({
|
|
553
|
+
success: true,
|
|
554
|
+
operation: 'skip',
|
|
555
|
+
documentType: 'document',
|
|
556
|
+
filePath: f,
|
|
557
|
+
message: 'Dry run - skipped',
|
|
558
|
+
})),
|
|
559
|
+
errors: [],
|
|
560
|
+
duration: Date.now() - startTime,
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
const results = [];
|
|
564
|
+
const errors = [];
|
|
565
|
+
let synced = 0;
|
|
566
|
+
let skipped = 0;
|
|
567
|
+
let failed = 0;
|
|
568
|
+
// Process files in batches
|
|
569
|
+
for (let i = 0; i < files.length; i += batchSize) {
|
|
570
|
+
const batch = files.slice(i, i + batchSize);
|
|
571
|
+
console.error(`[SyncEngine] Processing batch ${Math.floor(i / batchSize) + 1}/${Math.ceil(files.length / batchSize)} (${batch.length} files)`);
|
|
572
|
+
// Process batch with concurrency limit
|
|
573
|
+
const batchResults = await this.processBatch(batch, concurrency, force);
|
|
574
|
+
for (const result of batchResults) {
|
|
575
|
+
results.push(result);
|
|
576
|
+
if (result.success) {
|
|
577
|
+
if (result.operation === 'skip') {
|
|
578
|
+
skipped++;
|
|
579
|
+
}
|
|
580
|
+
else {
|
|
581
|
+
synced++;
|
|
582
|
+
this.syncCount++;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
else {
|
|
586
|
+
failed++;
|
|
587
|
+
// Use handleError to populate recentErrors (was missing!)
|
|
588
|
+
this.handleError(result.error || result.message, result.filePath);
|
|
589
|
+
errors.push({
|
|
590
|
+
filePath: result.filePath,
|
|
591
|
+
error: result.error || result.message,
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
// Save index after each batch (incremental saves - don't lose progress on crash)
|
|
596
|
+
await this.indexManager.flush();
|
|
597
|
+
console.error(`[SyncEngine] Index saved after batch (${Math.min(i + batchSize, files.length)}/${files.length} files processed)`);
|
|
598
|
+
// Emit progress event
|
|
599
|
+
this.emit('sync-progress', {
|
|
600
|
+
total: files.length,
|
|
601
|
+
processed: Math.min(i + batchSize, files.length),
|
|
602
|
+
synced,
|
|
603
|
+
skipped,
|
|
604
|
+
failed,
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
this.lastSyncAt = new Date();
|
|
608
|
+
this.initialScanComplete = true;
|
|
609
|
+
// Record full sync in persistent index
|
|
610
|
+
this.indexManager.recordFullSync();
|
|
611
|
+
await this.indexManager.flush(); // Ensure index is saved
|
|
612
|
+
const duration = Date.now() - startTime;
|
|
613
|
+
console.error(`[SyncEngine] Initial scan complete in ${duration}ms`);
|
|
614
|
+
console.error(`[SyncEngine] Results: ${synced} synced, ${skipped} skipped, ${failed} failed`);
|
|
615
|
+
this.emit('sync-all-complete', { totalFiles: files.length, synced, skipped, failed, duration });
|
|
616
|
+
return {
|
|
617
|
+
totalFiles: files.length,
|
|
618
|
+
synced,
|
|
619
|
+
skipped,
|
|
620
|
+
failed,
|
|
621
|
+
results,
|
|
622
|
+
errors,
|
|
623
|
+
duration,
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Discover all directories matching watch patterns (including empty ones)
|
|
628
|
+
* This ensures folder structure parity between IDE and Commander
|
|
629
|
+
*/
|
|
630
|
+
async discoverDirectories(patterns) {
|
|
631
|
+
const allDirs = new Set();
|
|
632
|
+
const config = this.configManager.getConfig();
|
|
633
|
+
const ignorePatterns = config.sync.ignore_patterns || [];
|
|
634
|
+
for (const pattern of patterns) {
|
|
635
|
+
try {
|
|
636
|
+
// Get base directory from pattern (e.g., "docs/**/*.md" → "docs")
|
|
637
|
+
const baseParts = pattern.split('*')[0].split('/').filter(p => p);
|
|
638
|
+
const baseDir = baseParts.join('/') || '.';
|
|
639
|
+
const absoluteBaseDir = path.join(this.projectPath, baseDir);
|
|
640
|
+
// Check if base directory exists
|
|
641
|
+
try {
|
|
642
|
+
await fs.access(absoluteBaseDir);
|
|
643
|
+
}
|
|
644
|
+
catch {
|
|
645
|
+
continue; // Base directory doesn't exist, skip
|
|
646
|
+
}
|
|
647
|
+
// Walk directory tree to find all subdirectories
|
|
648
|
+
const walkDir = async (dir) => {
|
|
649
|
+
try {
|
|
650
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
651
|
+
for (const entry of entries) {
|
|
652
|
+
if (entry.isDirectory()) {
|
|
653
|
+
const fullPath = path.join(dir, entry.name);
|
|
654
|
+
const relativePath = path.relative(this.projectPath, fullPath);
|
|
655
|
+
// Check ignore patterns
|
|
656
|
+
const shouldIgnore = ignorePatterns.some(ignorePattern => {
|
|
657
|
+
// Simple glob matching for common patterns
|
|
658
|
+
if (ignorePattern.includes('**')) {
|
|
659
|
+
const prefix = ignorePattern.split('**')[0];
|
|
660
|
+
return relativePath.startsWith(prefix.replace(/\/$/, ''));
|
|
661
|
+
}
|
|
662
|
+
return relativePath.includes(ignorePattern.replace(/\*\*/g, '').replace(/\//g, ''));
|
|
663
|
+
});
|
|
664
|
+
if (!shouldIgnore) {
|
|
665
|
+
allDirs.add(relativePath);
|
|
666
|
+
await walkDir(fullPath); // Recurse
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
catch (error) {
|
|
672
|
+
console.error(`[SyncEngine] Error reading directory ${dir}:`, error);
|
|
673
|
+
}
|
|
674
|
+
};
|
|
675
|
+
await walkDir(absoluteBaseDir);
|
|
676
|
+
}
|
|
677
|
+
catch (error) {
|
|
678
|
+
console.error(`[SyncEngine] Error discovering directories for pattern ${pattern}:`, error);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
// Sort for consistent processing order (parents before children)
|
|
682
|
+
return Array.from(allDirs).sort();
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Discover all markdown files matching patterns
|
|
686
|
+
*/
|
|
687
|
+
async discoverFiles(patterns) {
|
|
688
|
+
const allFiles = new Set();
|
|
689
|
+
for (const pattern of patterns) {
|
|
690
|
+
try {
|
|
691
|
+
const absolutePattern = path.join(this.projectPath, pattern);
|
|
692
|
+
const matches = await glob(absolutePattern, {
|
|
693
|
+
nodir: true,
|
|
694
|
+
ignore: ['**/node_modules/**', '**/.git/**', '**/dist/**'],
|
|
695
|
+
});
|
|
696
|
+
for (const match of matches) {
|
|
697
|
+
if (match.endsWith('.md')) {
|
|
698
|
+
// Store as relative path
|
|
699
|
+
const relativePath = path.relative(this.projectPath, match);
|
|
700
|
+
allFiles.add(relativePath);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
catch (error) {
|
|
705
|
+
console.error(`[SyncEngine] Error globbing pattern ${pattern}:`, error);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
// Sort files for consistent processing order
|
|
709
|
+
return Array.from(allFiles).sort();
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Process a batch of files with concurrency limit
|
|
713
|
+
* Uses Promise.allSettled to prevent one bad file from crashing entire batch
|
|
714
|
+
*/
|
|
715
|
+
async processBatch(files, concurrency, force) {
|
|
716
|
+
const results = [];
|
|
717
|
+
// Process in chunks of 'concurrency' size
|
|
718
|
+
for (let i = 0; i < files.length; i += concurrency) {
|
|
719
|
+
const chunk = files.slice(i, i + concurrency);
|
|
720
|
+
const chunkPromises = chunk.map(file => this.syncFileWithCheck(file, force));
|
|
721
|
+
// Use allSettled to handle individual failures gracefully
|
|
722
|
+
const settledResults = await Promise.allSettled(chunkPromises);
|
|
723
|
+
for (let j = 0; j < settledResults.length; j++) {
|
|
724
|
+
const settled = settledResults[j];
|
|
725
|
+
const filePath = chunk[j];
|
|
726
|
+
if (settled.status === 'fulfilled') {
|
|
727
|
+
results.push(settled.value);
|
|
728
|
+
}
|
|
729
|
+
else {
|
|
730
|
+
// Promise rejected - create error result instead of crashing
|
|
731
|
+
console.error(`[SyncEngine] ❌ Unhandled error in ${filePath}:`, settled.reason);
|
|
732
|
+
results.push({
|
|
733
|
+
success: false,
|
|
734
|
+
operation: 'skip',
|
|
735
|
+
documentType: 'document',
|
|
736
|
+
filePath,
|
|
737
|
+
message: `Unhandled error: ${settled.reason?.message || String(settled.reason)}`,
|
|
738
|
+
error: settled.reason?.message || String(settled.reason),
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
return results;
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Sync a file with checksum verification to avoid unnecessary syncs
|
|
747
|
+
* Uses persistent index and retry logic with exponential backoff
|
|
748
|
+
*/
|
|
749
|
+
async syncFileWithCheck(relativePath, force) {
|
|
750
|
+
try {
|
|
751
|
+
const absolutePath = path.join(this.projectPath, relativePath);
|
|
752
|
+
// Read and parse file
|
|
753
|
+
let content;
|
|
754
|
+
let fileStat = null;
|
|
755
|
+
try {
|
|
756
|
+
content = await fs.readFile(absolutePath, 'utf-8');
|
|
757
|
+
fileStat = await fs.stat(absolutePath);
|
|
758
|
+
}
|
|
759
|
+
catch {
|
|
760
|
+
// File was deleted - remove from index
|
|
761
|
+
this.indexManager.removeFile(relativePath);
|
|
762
|
+
return {
|
|
763
|
+
success: false,
|
|
764
|
+
operation: 'skip',
|
|
765
|
+
documentType: 'document',
|
|
766
|
+
filePath: relativePath,
|
|
767
|
+
message: 'File not found or unreadable',
|
|
768
|
+
error: 'File not found',
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
// Calculate checksum
|
|
772
|
+
const checksum = this.indexManager.calculateChecksum(content);
|
|
773
|
+
// Check persistent index - skip if unchanged (unless forced)
|
|
774
|
+
if (!force && !this.indexManager.needsSync(relativePath, checksum)) {
|
|
775
|
+
const indexState = this.indexManager.getFileState(relativePath);
|
|
776
|
+
return {
|
|
777
|
+
success: true,
|
|
778
|
+
operation: 'skip',
|
|
779
|
+
documentType: (indexState?.documentType || 'document'),
|
|
780
|
+
filePath: relativePath,
|
|
781
|
+
message: 'Already synced (checksum match in index)',
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
// Also check in-memory state for recent syncs (within same session)
|
|
785
|
+
const existingState = this.syncStates.get(relativePath);
|
|
786
|
+
if (!force && existingState && existingState.checksum === checksum) {
|
|
787
|
+
return {
|
|
788
|
+
success: true,
|
|
789
|
+
operation: 'skip',
|
|
790
|
+
documentType: existingState.documentType,
|
|
791
|
+
filePath: relativePath,
|
|
792
|
+
message: 'Already synced (checksum match)',
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
// Parse file - auto-init will happen in trinity-mapper if no sync block
|
|
796
|
+
const parsed = await this.parser.parseFile(absolutePath);
|
|
797
|
+
// Only skip if explicitly disabled (sync.enabled: false)
|
|
798
|
+
if (parsed.frontmatter.sync?.enabled === false) {
|
|
799
|
+
console.error(`[SyncEngine] Skipped (sync.enabled: false): ${relativePath}`);
|
|
800
|
+
return {
|
|
801
|
+
success: true,
|
|
802
|
+
operation: 'skip',
|
|
803
|
+
documentType: 'document',
|
|
804
|
+
filePath: relativePath,
|
|
805
|
+
message: 'Sync disabled via frontmatter (sync.enabled: false)',
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
// Files without sync block will be auto-initialized by trinity-mapper
|
|
809
|
+
// Don't skip them here - let them flow through to get frontmatter added
|
|
810
|
+
// Check for potential conflicts
|
|
811
|
+
const fileModifiedAt = fileStat ? new Date(fileStat.mtimeMs) : new Date();
|
|
812
|
+
const conflictCheck = this.indexManager.detectPotentialConflicts(relativePath, checksum, fileModifiedAt);
|
|
813
|
+
if (conflictCheck.hasConflict) {
|
|
814
|
+
console.error(`[SyncEngine] ⚠️ Potential conflict detected for ${relativePath}: ${conflictCheck.reason}`);
|
|
815
|
+
this.emit('conflict', { filePath: relativePath, reason: conflictCheck.reason });
|
|
816
|
+
}
|
|
817
|
+
// Extract status from folder path
|
|
818
|
+
const statusFromPath = this.folderStatusMapper.getStatusFromPath(relativePath);
|
|
819
|
+
console.error(`[SyncEngine] ${relativePath} -> status: ${statusFromPath.statusSection} (${statusFromPath.confidence})`);
|
|
820
|
+
// Sync to Commander with retry logic
|
|
821
|
+
const result = await this.syncWithRetry(relativePath);
|
|
822
|
+
// Update both in-memory state and persistent index on success
|
|
823
|
+
if (result.success && result.operation !== 'skip') {
|
|
824
|
+
// Update in-memory state
|
|
825
|
+
this.syncStates.set(relativePath, {
|
|
826
|
+
filePath: relativePath,
|
|
827
|
+
lastSyncedAt: new Date(),
|
|
828
|
+
lastModifiedAt: fileModifiedAt,
|
|
829
|
+
commanderId: result.commanderId || null,
|
|
830
|
+
documentType: result.documentType,
|
|
831
|
+
checksum,
|
|
832
|
+
});
|
|
833
|
+
// Update persistent index
|
|
834
|
+
this.indexManager.updateFileState(relativePath, {
|
|
835
|
+
checksum,
|
|
836
|
+
documentType: result.documentType,
|
|
837
|
+
taskId: parsed.frontmatter.sync?.task_id || result.commanderId || undefined,
|
|
838
|
+
documentId: parsed.frontmatter.sync?.document_id || undefined,
|
|
839
|
+
layerId: parsed.frontmatter.sync?.layer_id || undefined,
|
|
840
|
+
fileModifiedAt,
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
return result;
|
|
844
|
+
}
|
|
845
|
+
catch (error) {
|
|
846
|
+
return {
|
|
847
|
+
success: false,
|
|
848
|
+
operation: 'skip',
|
|
849
|
+
documentType: 'document',
|
|
850
|
+
filePath: relativePath,
|
|
851
|
+
message: error instanceof Error ? error.message : String(error),
|
|
852
|
+
error: error instanceof Error ? error.message : String(error),
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
857
|
+
* Get folder status mapper instance
|
|
858
|
+
*/
|
|
859
|
+
getFolderStatusMapper() {
|
|
860
|
+
return this.folderStatusMapper;
|
|
861
|
+
}
|
|
862
|
+
/**
|
|
863
|
+
* Check if initial scan is complete
|
|
864
|
+
*/
|
|
865
|
+
isInitialScanComplete() {
|
|
866
|
+
return this.initialScanComplete;
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Check if engine is running
|
|
870
|
+
*/
|
|
871
|
+
isRunning() {
|
|
872
|
+
return this.running;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
//# sourceMappingURL=sync-engine.js.map
|