backlog.md 0.1.0 → 0.1.3

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.
Files changed (165) hide show
  1. package/bin/backlog-darwin-arm64/backlog +0 -0
  2. package/bin/backlog-darwin-x64/backlog +0 -0
  3. package/bin/backlog-linux-arm64/backlog +0 -0
  4. package/{cli → bin/backlog-linux-x64}/backlog +0 -0
  5. package/bin/backlog-win32-x64/backlog.exe +0 -0
  6. package/cli.js +62 -0
  7. package/package.json +57 -46
  8. package/.backlog/archive/drafts/readme.md +0 -3
  9. package/.backlog/archive/drafts/task-41 - temporary-test-task.md +0 -13
  10. package/.backlog/archive/readme.md +0 -6
  11. package/.backlog/archive/tasks/readme.md +0 -3
  12. package/.backlog/archive/tasks/task-41 - cli-migrate-terminal-ui-to-bblessed.md +0 -14
  13. package/.backlog/config.yml +0 -7
  14. package/.backlog/decisions/readme.md +0 -7
  15. package/.backlog/docs/readme.md +0 -20
  16. package/.backlog/drafts/readme.md +0 -3
  17. package/.backlog/drafts/task-26 - docs-add-board-export-step-to-agent-dod.md +0 -21
  18. package/.backlog/drafts/task-28 - add-code-of-conduct.md +0 -20
  19. package/.backlog/drafts/task-30 - create-changelog.md +0 -19
  20. package/.backlog/milestones/m-0 - project-setup.md +0 -8
  21. package/.backlog/milestones/m-1 - cli.md +0 -8
  22. package/.backlog/milestones/m-2 - cli-kanban.md +0 -8
  23. package/.backlog/milestones/m-3 - gui.md +0 -8
  24. package/.backlog/milestones/m-4 - gui-kanban.md +0 -8
  25. package/.backlog/milestones/m-5 - gui-advanced.md +0 -12
  26. package/.backlog/milestones/readme.md +0 -3
  27. package/.backlog/readme.md +0 -5
  28. package/.backlog/tasks/readme.md +0 -37
  29. package/.backlog/tasks/task-1 - cli-setup-core-project.md +0 -23
  30. package/.backlog/tasks/task-10 - gui-init-packaging.md +0 -23
  31. package/.backlog/tasks/task-11 - gui-kanban-board.md +0 -26
  32. package/.backlog/tasks/task-12 - gui-advanced.md +0 -25
  33. package/.backlog/tasks/task-13 - cli-add-agent-instruction-prompt.md +0 -53
  34. package/.backlog/tasks/task-13.1 - cli-agent-instruction-file-selection.md +0 -40
  35. package/.backlog/tasks/task-14 - gui-introduction-screens.md +0 -21
  36. package/.backlog/tasks/task-15 - improve-tasks-readme-with-generic-example-and-cli-reference.md +0 -20
  37. package/.backlog/tasks/task-16 - improve-docs-readme-with-generic-example-and-cli-reference.md +0 -20
  38. package/.backlog/tasks/task-17 - improve-drafts-readme-with-generic-example-and-cli-reference.md +0 -20
  39. package/.backlog/tasks/task-18 - improve-decisions-readme-with-generic-example-and-cli-reference.md +0 -20
  40. package/.backlog/tasks/task-19 - cli-fix-default-task-status-and-remove-draft-from-statuses.md +0 -55
  41. package/.backlog/tasks/task-2 - cli-core-logic-library.md +0 -28
  42. package/.backlog/tasks/task-20 - add-agent-guideline-to-mark-tasks-in-progress-on-start.md +0 -32
  43. package/.backlog/tasks/task-21 - kanban-board-vertical-layout.md +0 -31
  44. package/.backlog/tasks/task-22 - cli-prevent-double-dash-in-task-filenames.md +0 -24
  45. package/.backlog/tasks/task-23 - cli-kanban-board-order-tasks-by-id-asc.md +0 -30
  46. package/.backlog/tasks/task-24 - handle-subtasks-in-the-kanban-view.md +0 -38
  47. package/.backlog/tasks/task-24.1 - cli-kanban-board-milestone-view.md +0 -19
  48. package/.backlog/tasks/task-25 - cli-export-kanban-board-to-readme.md +0 -28
  49. package/.backlog/tasks/task-27 - add-contributing-guidelines.md +0 -27
  50. package/.backlog/tasks/task-29 - add-github-templates.md +0 -28
  51. package/.backlog/tasks/task-3 - cli-implement-backlog-init.md +0 -63
  52. package/.backlog/tasks/task-31 - update-readme-for-open-source.md +0 -26
  53. package/.backlog/tasks/task-32 - cli-hide-empty-'no-status'-column.md +0 -31
  54. package/.backlog/tasks/task-33 - cli-export-milestones-board-as-roadmap.md +0 -20
  55. package/.backlog/tasks/task-34 - split-readme.md-for-users-and-contributors.md +0 -26
  56. package/.backlog/tasks/task-35 - finalize-package.json-metadata-for-publishing.md +0 -24
  57. package/.backlog/tasks/task-36 - cli-prompt-for-project-name-in-init.md +0 -24
  58. package/.backlog/tasks/task-37 - cli-board-view-open-tasks-in-ide.md +0 -19
  59. package/.backlog/tasks/task-38 - cli-improved-agent-selection-for-init.md +0 -25
  60. package/.backlog/tasks/task-39 - cli-fix-empty-agent-instruction-files-on-init.md +0 -31
  61. package/.backlog/tasks/task-4 - cli-task-management-commands.md +0 -28
  62. package/.backlog/tasks/task-4.1 - cli-task-create.md +0 -27
  63. package/.backlog/tasks/task-4.10 - use-cli-to-mark-tasks-done.md +0 -51
  64. package/.backlog/tasks/task-4.11 - docs-add-definition-of-done-to-agent-guidelines.md +0 -23
  65. package/.backlog/tasks/task-4.12 - cli-handle-task-id-conflicts-across-branches.md +0 -53
  66. package/.backlog/tasks/task-4.13 - cli-fix-config-command-local-global-logic.md +0 -58
  67. package/.backlog/tasks/task-4.2 - cli-task-list-view.md +0 -25
  68. package/.backlog/tasks/task-4.3 - cli-task-edit.md +0 -24
  69. package/.backlog/tasks/task-4.4 - cli-task-archive-transition.md +0 -27
  70. package/.backlog/tasks/task-4.5 - cli-init-prompts-for-reporter-name-and-global-local-config.md +0 -28
  71. package/.backlog/tasks/task-4.6 - cli-add-empty-assignee-array-field-for-new-tasks.md +0 -35
  72. package/.backlog/tasks/task-4.7 - cli-parse-unquoted-created_date.md +0 -40
  73. package/.backlog/tasks/task-4.8 - cli-enforce-description-header.md +0 -48
  74. package/.backlog/tasks/task-4.9 - cli-normalize-task-id-inputs.md +0 -66
  75. package/.backlog/tasks/task-40 - cli-board-command-defaults-to-view.md +0 -38
  76. package/.backlog/tasks/task-41 - cli-migrate-terminal-ui-to-bblessed.md +0 -93
  77. package/.backlog/tasks/task-41.1 - cli-bblessed-init-wizard.md +0 -42
  78. package/.backlog/tasks/task-41.2 - cli-bblessed-task-view.md +0 -44
  79. package/.backlog/tasks/task-41.3 - cli-bblessed-doc-view.md +0 -45
  80. package/.backlog/tasks/task-41.4 - cli-bblessed-board-view.md +0 -49
  81. package/.backlog/tasks/task-41.5 - cli-audit-remaining-ui-for-bblessed.md +0 -55
  82. package/.backlog/tasks/task-42 - visual-hierarchy.md +0 -54
  83. package/.backlog/tasks/task-43 - remove-duplicate-acceptance-criteria-and-style-metadata.md +0 -56
  84. package/.backlog/tasks/task-44 - checklist-alignment.md +0 -24
  85. package/.backlog/tasks/task-45 - safe-line-wrapping.md +0 -23
  86. package/.backlog/tasks/task-46 - split-pane-layout.md +0 -24
  87. package/.backlog/tasks/task-47 - sticky-header-in-detail-view.md +0 -43
  88. package/.backlog/tasks/task-48 - footer-hint-line.md +0 -21
  89. package/.backlog/tasks/task-49 - status-styling.md +0 -53
  90. package/.backlog/tasks/task-5 - cli-docs-decisions.md +0 -57
  91. package/.backlog/tasks/task-50 - borders-&-padding.md +0 -22
  92. package/.backlog/tasks/task-51 - code-path-styling.md +0 -23
  93. package/.backlog/tasks/task-52 - cli-filter-tasks-list-by-status-or-assignee.md +0 -29
  94. package/.backlog/tasks/task-6 - cli-packaging.md +0 -65
  95. package/.backlog/tasks/task-6.1 - cli-local-installation-support-for-bunx-npx.md +0 -49
  96. package/.backlog/tasks/task-6.2 - cli-github-actions-for-build-&-publish.md +0 -64
  97. package/.backlog/tasks/task-7 - cli-kanban-view.md +0 -60
  98. package/.backlog/tasks/task-7.1 - cli-kanban-board-detect-remote-task-status.md +0 -62
  99. package/.backlog/tasks/task-8 - gui-project-setup.md +0 -21
  100. package/.backlog/tasks/task-9 - gui-task-crud.md +0 -24
  101. package/.cursorrules +0 -223
  102. package/.gitattributes +0 -2
  103. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -25
  104. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -15
  105. package/.github/PULL_REQUEST_TEMPLATE.md +0 -8
  106. package/.github/workflows/ci.yml +0 -36
  107. package/.husky/pre-commit +0 -1
  108. package/AGENTS.md +0 -65
  109. package/CLAUDE.md +0 -87
  110. package/CONTRIBUTING.md +0 -19
  111. package/DEVELOPMENT.md +0 -37
  112. package/biome.json +0 -31
  113. package/bun.lock +0 -152
  114. package/cli/.cursorrules-xh86jabm.md +0 -82
  115. package/cli/AGENTS-xh86jabm.md +0 -82
  116. package/cli/CLAUDE-xh86jabm.md +0 -82
  117. package/cli/cli.js +0 -19622
  118. package/cli/index.js +0 -2
  119. package/docs/npm-publishing.md +0 -69
  120. package/scripts/build.js +0 -73
  121. package/src/agent-instructions.ts +0 -54
  122. package/src/board.ts +0 -263
  123. package/src/cli.ts +0 -806
  124. package/src/constants/index.ts +0 -48
  125. package/src/core/backlog.ts +0 -183
  126. package/src/core/remote-tasks.ts +0 -168
  127. package/src/file-system/operations.ts +0 -515
  128. package/src/git/operations.ts +0 -189
  129. package/src/guidelines/.cursorrules.md +0 -82
  130. package/src/guidelines/AGENTS.md +0 -82
  131. package/src/guidelines/CLAUDE.md +0 -82
  132. package/src/guidelines/index.ts +0 -7
  133. package/src/index.ts +0 -30
  134. package/src/markdown/parser.ts +0 -145
  135. package/src/markdown/serializer.ts +0 -71
  136. package/src/test/agent-instructions.test.ts +0 -62
  137. package/src/test/board.test.ts +0 -291
  138. package/src/test/build.test.ts +0 -28
  139. package/src/test/checklist.test.ts +0 -273
  140. package/src/test/cli.test.ts +0 -1300
  141. package/src/test/code-path.test.ts +0 -204
  142. package/src/test/core.test.ts +0 -330
  143. package/src/test/filesystem.test.ts +0 -435
  144. package/src/test/git.test.ts +0 -26
  145. package/src/test/heading.test.ts +0 -102
  146. package/src/test/line-wrapping.test.ts +0 -252
  147. package/src/test/local-install.test.ts +0 -34
  148. package/src/test/markdown.test.ts +0 -526
  149. package/src/test/parallel-loading.test.ts +0 -160
  150. package/src/test/parent-id-normalization.test.ts +0 -48
  151. package/src/test/remote-id-conflict.test.ts +0 -60
  152. package/src/test/status-icon.test.ts +0 -93
  153. package/src/types/blessed.d.ts +0 -14
  154. package/src/types/index.ts +0 -55
  155. package/src/types/raw.d.ts +0 -4
  156. package/src/ui/board.ts +0 -322
  157. package/src/ui/checklist.ts +0 -103
  158. package/src/ui/code-path.ts +0 -113
  159. package/src/ui/heading.ts +0 -121
  160. package/src/ui/loading.ts +0 -216
  161. package/src/ui/status-icon.ts +0 -53
  162. package/src/ui/task-list.ts +0 -168
  163. package/src/ui/task-viewer.ts +0 -640
  164. package/src/ui/tui.ts +0 -301
  165. package/tsconfig.json +0 -26
@@ -1,48 +0,0 @@
1
- /**
2
- * Default directory structure for backlog projects
3
- */
4
- export const DEFAULT_DIRECTORIES = {
5
- /** Main backlog directory */
6
- BACKLOG: ".backlog",
7
- /** Active tasks directory */
8
- TASKS: "tasks",
9
- /** Draft tasks directory */
10
- DRAFTS: "drafts",
11
- /** Archive root directory */
12
- ARCHIVE: "archive",
13
- /** Archived tasks directory */
14
- ARCHIVE_TASKS: "archive/tasks",
15
- /** Archived drafts directory */
16
- ARCHIVE_DRAFTS: "archive/drafts",
17
- /** Documentation directory */
18
- DOCS: "docs",
19
- /** Decision logs directory */
20
- DECISIONS: "decisions",
21
- } as const;
22
-
23
- /**
24
- * Default configuration file names
25
- */
26
- export const DEFAULT_FILES = {
27
- /** Main configuration file */
28
- CONFIG: "config.yml",
29
- /** Local user settings file */
30
- USER: ".user",
31
- } as const;
32
-
33
- /**
34
- * Default task statuses
35
- */
36
- export const DEFAULT_STATUSES = ["To Do", "In Progress", "Done"] as const;
37
-
38
- /**
39
- * Fallback status when no default is configured
40
- */
41
- export const FALLBACK_STATUS = "To Do";
42
-
43
- /**
44
- * Maximum width for wrapped text lines in UI components
45
- */
46
- export const WRAP_LIMIT = 72;
47
-
48
- export * from "../guidelines/index.ts";
@@ -1,183 +0,0 @@
1
- import { join } from "node:path";
2
- import { DEFAULT_STATUSES, FALLBACK_STATUS } from "../constants/index.ts";
3
- import { FileSystem } from "../file-system/operations.ts";
4
- import { GitOperations } from "../git/operations.ts";
5
- import type { BacklogConfig, DecisionLog, Document, Task } from "../types/index.ts";
6
-
7
- function ensureDescriptionHeader(description: string): string {
8
- const trimmed = description.trim();
9
- if (trimmed === "") {
10
- return "## Description";
11
- }
12
- return /^##\s+Description/i.test(trimmed) ? trimmed : `## Description\n\n${trimmed}`;
13
- }
14
-
15
- export class Core {
16
- private fs: FileSystem;
17
- private git: GitOperations;
18
-
19
- constructor(projectRoot: string) {
20
- this.fs = new FileSystem(projectRoot);
21
- this.git = new GitOperations(projectRoot);
22
- }
23
-
24
- // File system operations
25
- get filesystem() {
26
- return this.fs;
27
- }
28
-
29
- // Git operations
30
- get gitOps() {
31
- return this.git;
32
- }
33
-
34
- // High-level operations that combine filesystem and git
35
- async createTask(task: Task, autoCommit = true): Promise<void> {
36
- if (!task.status) {
37
- const config = await this.fs.loadConfig();
38
- task.status = config?.defaultStatus || FALLBACK_STATUS;
39
- }
40
-
41
- // Normalize assignee to array if it's a string (YAML allows both string and array)
42
- // biome-ignore lint/suspicious/noExplicitAny: Required for YAML flexibility
43
- if (typeof (task as any).assignee === "string") {
44
- // biome-ignore lint/suspicious/noExplicitAny: Required for YAML flexibility
45
- (task as any).assignee = [(task as any).assignee];
46
- }
47
-
48
- task.description = ensureDescriptionHeader(task.description);
49
- await this.fs.saveTask(task);
50
-
51
- if (autoCommit) {
52
- const tasksDir = this.fs.tasksDir;
53
- const files = await Array.fromAsync(new Bun.Glob("*.md").scan({ cwd: tasksDir }));
54
- const taskFile = files.find((file) => file.startsWith(`task-${task.id} -`));
55
- if (taskFile) {
56
- const filePath = join(tasksDir, taskFile);
57
- await this.git.addAndCommitTaskFile(task.id, filePath, "create");
58
- }
59
- }
60
- }
61
-
62
- async createDraft(task: Task, autoCommit = true): Promise<void> {
63
- // Drafts always have status "Draft", regardless of config default
64
- task.status = "Draft";
65
-
66
- // Normalize assignee to array if it's a string (YAML allows both string and array)
67
- // biome-ignore lint/suspicious/noExplicitAny: Required for YAML flexibility
68
- if (typeof (task as any).assignee === "string") {
69
- // biome-ignore lint/suspicious/noExplicitAny: Required for YAML flexibility
70
- (task as any).assignee = [(task as any).assignee];
71
- }
72
-
73
- task.description = ensureDescriptionHeader(task.description);
74
- await this.fs.saveDraft(task);
75
-
76
- if (autoCommit) {
77
- const draftsDir = this.fs.draftsDir;
78
- const files = await Array.fromAsync(new Bun.Glob("*.md").scan({ cwd: draftsDir }));
79
- const taskFile = files.find((file) => file.startsWith(`task-${task.id} -`));
80
- if (taskFile) {
81
- const filePath = join(draftsDir, taskFile);
82
- await this.git.addFile(filePath);
83
- await this.git.commitTaskChange(task.id, `Create draft ${task.id}`);
84
- }
85
- }
86
- }
87
-
88
- async updateTask(task: Task, autoCommit = true): Promise<void> {
89
- // Normalize assignee to array if it's a string (YAML allows both string and array)
90
- // biome-ignore lint/suspicious/noExplicitAny: Required for YAML flexibility
91
- if (typeof (task as any).assignee === "string") {
92
- // biome-ignore lint/suspicious/noExplicitAny: Required for YAML flexibility
93
- (task as any).assignee = [(task as any).assignee];
94
- }
95
-
96
- task.description = ensureDescriptionHeader(task.description);
97
- await this.fs.saveTask(task);
98
-
99
- if (autoCommit) {
100
- const tasksDir = this.fs.tasksDir;
101
- const files = await Array.fromAsync(new Bun.Glob("*.md").scan({ cwd: tasksDir }));
102
- const taskFile = files.find((file) => file.startsWith(`task-${task.id} -`));
103
-
104
- if (taskFile) {
105
- const filePath = join(tasksDir, taskFile);
106
- await this.git.addAndCommitTaskFile(task.id, filePath, "update");
107
- }
108
- }
109
- }
110
-
111
- async archiveTask(taskId: string, autoCommit = true): Promise<boolean> {
112
- const success = await this.fs.archiveTask(taskId);
113
-
114
- if (success && autoCommit) {
115
- await this.git.commitBacklogChanges(`Archive task ${taskId}`);
116
- }
117
-
118
- return success;
119
- }
120
-
121
- async archiveDraft(taskId: string, autoCommit = true): Promise<boolean> {
122
- const success = await this.fs.archiveDraft(taskId);
123
-
124
- if (success && autoCommit) {
125
- await this.git.commitBacklogChanges(`Archive draft ${taskId}`);
126
- }
127
-
128
- return success;
129
- }
130
-
131
- async promoteDraft(taskId: string, autoCommit = true): Promise<boolean> {
132
- const success = await this.fs.promoteDraft(taskId);
133
-
134
- if (success && autoCommit) {
135
- await this.git.commitBacklogChanges(`Promote draft ${taskId}`);
136
- }
137
-
138
- return success;
139
- }
140
-
141
- async demoteTask(taskId: string, autoCommit = true): Promise<boolean> {
142
- const success = await this.fs.demoteTask(taskId);
143
-
144
- if (success && autoCommit) {
145
- await this.git.commitBacklogChanges(`Demote task ${taskId}`);
146
- }
147
-
148
- return success;
149
- }
150
-
151
- async createDecisionLog(decision: DecisionLog, autoCommit = true): Promise<void> {
152
- await this.fs.saveDecisionLog(decision);
153
-
154
- if (autoCommit) {
155
- await this.git.commitBacklogChanges(`Add decision ${decision.id}`);
156
- }
157
- }
158
-
159
- async createDocument(doc: Document, autoCommit = true, subPath = ""): Promise<void> {
160
- await this.fs.saveDocument(doc, subPath);
161
-
162
- if (autoCommit) {
163
- await this.git.commitBacklogChanges(`Add document ${doc.id}`);
164
- }
165
- }
166
-
167
- async initializeProject(projectName: string): Promise<void> {
168
- await this.fs.ensureBacklogStructure();
169
-
170
- const config: BacklogConfig = {
171
- projectName: projectName,
172
- statuses: [...DEFAULT_STATUSES],
173
- labels: [],
174
- milestones: [],
175
- defaultStatus: DEFAULT_STATUSES[0], // Use first status as default
176
- dateFormat: "yyyy-mm-dd",
177
- maxColumnWidth: 20, // Default for terminal display
178
- };
179
-
180
- await this.fs.saveConfig(config);
181
- await this.git.commitBacklogChanges(`Initialize backlog project: ${projectName}`);
182
- }
183
- }
@@ -1,168 +0,0 @@
1
- /**
2
- * Helper functions for loading remote tasks in parallel
3
- */
4
-
5
- import { parseTask } from "../markdown/parser.ts";
6
- import type { Task } from "../types/index.ts";
7
- import type { GitOps } from "./git-ops.ts";
8
-
9
- export interface TaskWithMetadata extends Task {
10
- lastModified?: Date;
11
- source: "local" | "remote";
12
- branch?: string;
13
- }
14
-
15
- interface RemoteTaskLoadResult {
16
- task: TaskWithMetadata;
17
- error?: never;
18
- }
19
-
20
- interface RemoteTaskLoadError {
21
- task?: never;
22
- error: Error;
23
- file: string;
24
- branch: string;
25
- }
26
-
27
- type RemoteTaskResult = RemoteTaskLoadResult | RemoteTaskLoadError;
28
-
29
- /**
30
- * Load all remote tasks in parallel for better performance
31
- */
32
- export async function loadRemoteTasks(
33
- gitOps: GitOps,
34
- onProgress?: (message: string) => void,
35
- ): Promise<TaskWithMetadata[]> {
36
- const tasks: TaskWithMetadata[] = [];
37
-
38
- try {
39
- // Fetch remote branches
40
- onProgress?.("Fetching remote branches...");
41
- await gitOps.fetch();
42
- const branches = await gitOps.listRemoteBranches();
43
-
44
- if (branches.length === 0) {
45
- return tasks;
46
- }
47
-
48
- onProgress?.(`Found ${branches.length} remote branches`);
49
-
50
- // Process all branches in parallel
51
- const branchPromises = branches.map(async (branch) => {
52
- const ref = `origin/${branch}`;
53
-
54
- try {
55
- // List files in the branch
56
- const files = await gitOps.listFilesInTree(ref, ".backlog/tasks");
57
-
58
- if (files.length === 0) {
59
- return [];
60
- }
61
-
62
- // Load all files in this branch in parallel
63
- const filePromises = files.map(async (file): Promise<RemoteTaskResult> => {
64
- try {
65
- // Load file content and timestamp in parallel
66
- const [content, lastModified] = await Promise.all([
67
- gitOps.showFile(ref, file),
68
- gitOps.getFileLastModifiedTime(ref, file),
69
- ]);
70
-
71
- const task = parseTask(content);
72
-
73
- return {
74
- task: {
75
- ...task,
76
- lastModified: lastModified || undefined,
77
- source: "remote" as const,
78
- branch,
79
- },
80
- };
81
- } catch (error) {
82
- return {
83
- error: error as Error,
84
- file,
85
- branch,
86
- };
87
- }
88
- });
89
-
90
- const results = await Promise.all(filePromises);
91
-
92
- // Extract successful tasks and report errors
93
- const branchTasks: TaskWithMetadata[] = [];
94
- for (const result of results) {
95
- if (result.task) {
96
- branchTasks.push(result.task);
97
- } else if (result.error) {
98
- // Log error but continue processing
99
- console.error(`Failed to load task from ${result.branch}:${result.file}: ${result.error.message}`);
100
- }
101
- }
102
-
103
- onProgress?.(`Loaded ${branchTasks.length} tasks from ${branch}`);
104
- return branchTasks;
105
- } catch (error) {
106
- console.error(`Failed to process branch ${branch}:`, error);
107
- return [];
108
- }
109
- });
110
-
111
- // Wait for all branches to complete
112
- const branchResults = await Promise.all(branchPromises);
113
-
114
- // Flatten results
115
- for (const branchTasks of branchResults) {
116
- tasks.push(...branchTasks);
117
- }
118
-
119
- onProgress?.(`Loaded ${tasks.length} total remote tasks`);
120
- } catch (error) {
121
- // If fetch fails, we can still work with local tasks
122
- console.error("Failed to fetch remote tasks:", error);
123
- }
124
-
125
- return tasks;
126
- }
127
-
128
- /**
129
- * Resolve conflicts between local and remote tasks based on strategy
130
- */
131
- export function resolveTaskConflict(
132
- existing: TaskWithMetadata,
133
- incoming: TaskWithMetadata,
134
- statuses: string[],
135
- strategy: "most_recent" | "most_progressed" = "most_progressed",
136
- ): TaskWithMetadata {
137
- if (strategy === "most_recent") {
138
- // First try to use updated_date from the task metadata
139
- const existingDate = existing.updatedDate ? new Date(existing.updatedDate) : existing.lastModified;
140
- const incomingDate = incoming.updatedDate ? new Date(incoming.updatedDate) : incoming.lastModified;
141
-
142
- if (existingDate && incomingDate) {
143
- return existingDate >= incomingDate ? existing : incoming;
144
- }
145
- // If we can't compare dates, fall back to most_progressed
146
- }
147
-
148
- // Default to most_progressed strategy
149
- const currentIdx = statuses.indexOf(existing.status);
150
- const newIdx = statuses.indexOf(incoming.status);
151
-
152
- // If incoming task has a more progressed status, use it
153
- if (newIdx > currentIdx || currentIdx === -1) {
154
- return incoming;
155
- }
156
-
157
- // If statuses are equal and we have dates, use the most recent
158
- if (newIdx === currentIdx) {
159
- const existingDate = existing.updatedDate ? new Date(existing.updatedDate) : existing.lastModified;
160
- const incomingDate = incoming.updatedDate ? new Date(incoming.updatedDate) : incoming.lastModified;
161
-
162
- if (existingDate && incomingDate) {
163
- return existingDate >= incomingDate ? existing : incoming;
164
- }
165
- }
166
-
167
- return existing;
168
- }