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.
- package/bin/backlog-darwin-arm64/backlog +0 -0
- package/bin/backlog-darwin-x64/backlog +0 -0
- package/bin/backlog-linux-arm64/backlog +0 -0
- package/{cli → bin/backlog-linux-x64}/backlog +0 -0
- package/bin/backlog-win32-x64/backlog.exe +0 -0
- package/cli.js +62 -0
- package/package.json +57 -46
- package/.backlog/archive/drafts/readme.md +0 -3
- package/.backlog/archive/drafts/task-41 - temporary-test-task.md +0 -13
- package/.backlog/archive/readme.md +0 -6
- package/.backlog/archive/tasks/readme.md +0 -3
- package/.backlog/archive/tasks/task-41 - cli-migrate-terminal-ui-to-bblessed.md +0 -14
- package/.backlog/config.yml +0 -7
- package/.backlog/decisions/readme.md +0 -7
- package/.backlog/docs/readme.md +0 -20
- package/.backlog/drafts/readme.md +0 -3
- package/.backlog/drafts/task-26 - docs-add-board-export-step-to-agent-dod.md +0 -21
- package/.backlog/drafts/task-28 - add-code-of-conduct.md +0 -20
- package/.backlog/drafts/task-30 - create-changelog.md +0 -19
- package/.backlog/milestones/m-0 - project-setup.md +0 -8
- package/.backlog/milestones/m-1 - cli.md +0 -8
- package/.backlog/milestones/m-2 - cli-kanban.md +0 -8
- package/.backlog/milestones/m-3 - gui.md +0 -8
- package/.backlog/milestones/m-4 - gui-kanban.md +0 -8
- package/.backlog/milestones/m-5 - gui-advanced.md +0 -12
- package/.backlog/milestones/readme.md +0 -3
- package/.backlog/readme.md +0 -5
- package/.backlog/tasks/readme.md +0 -37
- package/.backlog/tasks/task-1 - cli-setup-core-project.md +0 -23
- package/.backlog/tasks/task-10 - gui-init-packaging.md +0 -23
- package/.backlog/tasks/task-11 - gui-kanban-board.md +0 -26
- package/.backlog/tasks/task-12 - gui-advanced.md +0 -25
- package/.backlog/tasks/task-13 - cli-add-agent-instruction-prompt.md +0 -53
- package/.backlog/tasks/task-13.1 - cli-agent-instruction-file-selection.md +0 -40
- package/.backlog/tasks/task-14 - gui-introduction-screens.md +0 -21
- package/.backlog/tasks/task-15 - improve-tasks-readme-with-generic-example-and-cli-reference.md +0 -20
- package/.backlog/tasks/task-16 - improve-docs-readme-with-generic-example-and-cli-reference.md +0 -20
- package/.backlog/tasks/task-17 - improve-drafts-readme-with-generic-example-and-cli-reference.md +0 -20
- package/.backlog/tasks/task-18 - improve-decisions-readme-with-generic-example-and-cli-reference.md +0 -20
- package/.backlog/tasks/task-19 - cli-fix-default-task-status-and-remove-draft-from-statuses.md +0 -55
- package/.backlog/tasks/task-2 - cli-core-logic-library.md +0 -28
- package/.backlog/tasks/task-20 - add-agent-guideline-to-mark-tasks-in-progress-on-start.md +0 -32
- package/.backlog/tasks/task-21 - kanban-board-vertical-layout.md +0 -31
- package/.backlog/tasks/task-22 - cli-prevent-double-dash-in-task-filenames.md +0 -24
- package/.backlog/tasks/task-23 - cli-kanban-board-order-tasks-by-id-asc.md +0 -30
- package/.backlog/tasks/task-24 - handle-subtasks-in-the-kanban-view.md +0 -38
- package/.backlog/tasks/task-24.1 - cli-kanban-board-milestone-view.md +0 -19
- package/.backlog/tasks/task-25 - cli-export-kanban-board-to-readme.md +0 -28
- package/.backlog/tasks/task-27 - add-contributing-guidelines.md +0 -27
- package/.backlog/tasks/task-29 - add-github-templates.md +0 -28
- package/.backlog/tasks/task-3 - cli-implement-backlog-init.md +0 -63
- package/.backlog/tasks/task-31 - update-readme-for-open-source.md +0 -26
- package/.backlog/tasks/task-32 - cli-hide-empty-'no-status'-column.md +0 -31
- package/.backlog/tasks/task-33 - cli-export-milestones-board-as-roadmap.md +0 -20
- package/.backlog/tasks/task-34 - split-readme.md-for-users-and-contributors.md +0 -26
- package/.backlog/tasks/task-35 - finalize-package.json-metadata-for-publishing.md +0 -24
- package/.backlog/tasks/task-36 - cli-prompt-for-project-name-in-init.md +0 -24
- package/.backlog/tasks/task-37 - cli-board-view-open-tasks-in-ide.md +0 -19
- package/.backlog/tasks/task-38 - cli-improved-agent-selection-for-init.md +0 -25
- package/.backlog/tasks/task-39 - cli-fix-empty-agent-instruction-files-on-init.md +0 -31
- package/.backlog/tasks/task-4 - cli-task-management-commands.md +0 -28
- package/.backlog/tasks/task-4.1 - cli-task-create.md +0 -27
- package/.backlog/tasks/task-4.10 - use-cli-to-mark-tasks-done.md +0 -51
- package/.backlog/tasks/task-4.11 - docs-add-definition-of-done-to-agent-guidelines.md +0 -23
- package/.backlog/tasks/task-4.12 - cli-handle-task-id-conflicts-across-branches.md +0 -53
- package/.backlog/tasks/task-4.13 - cli-fix-config-command-local-global-logic.md +0 -58
- package/.backlog/tasks/task-4.2 - cli-task-list-view.md +0 -25
- package/.backlog/tasks/task-4.3 - cli-task-edit.md +0 -24
- package/.backlog/tasks/task-4.4 - cli-task-archive-transition.md +0 -27
- package/.backlog/tasks/task-4.5 - cli-init-prompts-for-reporter-name-and-global-local-config.md +0 -28
- package/.backlog/tasks/task-4.6 - cli-add-empty-assignee-array-field-for-new-tasks.md +0 -35
- package/.backlog/tasks/task-4.7 - cli-parse-unquoted-created_date.md +0 -40
- package/.backlog/tasks/task-4.8 - cli-enforce-description-header.md +0 -48
- package/.backlog/tasks/task-4.9 - cli-normalize-task-id-inputs.md +0 -66
- package/.backlog/tasks/task-40 - cli-board-command-defaults-to-view.md +0 -38
- package/.backlog/tasks/task-41 - cli-migrate-terminal-ui-to-bblessed.md +0 -93
- package/.backlog/tasks/task-41.1 - cli-bblessed-init-wizard.md +0 -42
- package/.backlog/tasks/task-41.2 - cli-bblessed-task-view.md +0 -44
- package/.backlog/tasks/task-41.3 - cli-bblessed-doc-view.md +0 -45
- package/.backlog/tasks/task-41.4 - cli-bblessed-board-view.md +0 -49
- package/.backlog/tasks/task-41.5 - cli-audit-remaining-ui-for-bblessed.md +0 -55
- package/.backlog/tasks/task-42 - visual-hierarchy.md +0 -54
- package/.backlog/tasks/task-43 - remove-duplicate-acceptance-criteria-and-style-metadata.md +0 -56
- package/.backlog/tasks/task-44 - checklist-alignment.md +0 -24
- package/.backlog/tasks/task-45 - safe-line-wrapping.md +0 -23
- package/.backlog/tasks/task-46 - split-pane-layout.md +0 -24
- package/.backlog/tasks/task-47 - sticky-header-in-detail-view.md +0 -43
- package/.backlog/tasks/task-48 - footer-hint-line.md +0 -21
- package/.backlog/tasks/task-49 - status-styling.md +0 -53
- package/.backlog/tasks/task-5 - cli-docs-decisions.md +0 -57
- package/.backlog/tasks/task-50 - borders-&-padding.md +0 -22
- package/.backlog/tasks/task-51 - code-path-styling.md +0 -23
- package/.backlog/tasks/task-52 - cli-filter-tasks-list-by-status-or-assignee.md +0 -29
- package/.backlog/tasks/task-6 - cli-packaging.md +0 -65
- package/.backlog/tasks/task-6.1 - cli-local-installation-support-for-bunx-npx.md +0 -49
- package/.backlog/tasks/task-6.2 - cli-github-actions-for-build-&-publish.md +0 -64
- package/.backlog/tasks/task-7 - cli-kanban-view.md +0 -60
- package/.backlog/tasks/task-7.1 - cli-kanban-board-detect-remote-task-status.md +0 -62
- package/.backlog/tasks/task-8 - gui-project-setup.md +0 -21
- package/.backlog/tasks/task-9 - gui-task-crud.md +0 -24
- package/.cursorrules +0 -223
- package/.gitattributes +0 -2
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -25
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -15
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -8
- package/.github/workflows/ci.yml +0 -36
- package/.husky/pre-commit +0 -1
- package/AGENTS.md +0 -65
- package/CLAUDE.md +0 -87
- package/CONTRIBUTING.md +0 -19
- package/DEVELOPMENT.md +0 -37
- package/biome.json +0 -31
- package/bun.lock +0 -152
- package/cli/.cursorrules-xh86jabm.md +0 -82
- package/cli/AGENTS-xh86jabm.md +0 -82
- package/cli/CLAUDE-xh86jabm.md +0 -82
- package/cli/cli.js +0 -19622
- package/cli/index.js +0 -2
- package/docs/npm-publishing.md +0 -69
- package/scripts/build.js +0 -73
- package/src/agent-instructions.ts +0 -54
- package/src/board.ts +0 -263
- package/src/cli.ts +0 -806
- package/src/constants/index.ts +0 -48
- package/src/core/backlog.ts +0 -183
- package/src/core/remote-tasks.ts +0 -168
- package/src/file-system/operations.ts +0 -515
- package/src/git/operations.ts +0 -189
- package/src/guidelines/.cursorrules.md +0 -82
- package/src/guidelines/AGENTS.md +0 -82
- package/src/guidelines/CLAUDE.md +0 -82
- package/src/guidelines/index.ts +0 -7
- package/src/index.ts +0 -30
- package/src/markdown/parser.ts +0 -145
- package/src/markdown/serializer.ts +0 -71
- package/src/test/agent-instructions.test.ts +0 -62
- package/src/test/board.test.ts +0 -291
- package/src/test/build.test.ts +0 -28
- package/src/test/checklist.test.ts +0 -273
- package/src/test/cli.test.ts +0 -1300
- package/src/test/code-path.test.ts +0 -204
- package/src/test/core.test.ts +0 -330
- package/src/test/filesystem.test.ts +0 -435
- package/src/test/git.test.ts +0 -26
- package/src/test/heading.test.ts +0 -102
- package/src/test/line-wrapping.test.ts +0 -252
- package/src/test/local-install.test.ts +0 -34
- package/src/test/markdown.test.ts +0 -526
- package/src/test/parallel-loading.test.ts +0 -160
- package/src/test/parent-id-normalization.test.ts +0 -48
- package/src/test/remote-id-conflict.test.ts +0 -60
- package/src/test/status-icon.test.ts +0 -93
- package/src/types/blessed.d.ts +0 -14
- package/src/types/index.ts +0 -55
- package/src/types/raw.d.ts +0 -4
- package/src/ui/board.ts +0 -322
- package/src/ui/checklist.ts +0 -103
- package/src/ui/code-path.ts +0 -113
- package/src/ui/heading.ts +0 -121
- package/src/ui/loading.ts +0 -216
- package/src/ui/status-icon.ts +0 -53
- package/src/ui/task-list.ts +0 -168
- package/src/ui/task-viewer.ts +0 -640
- package/src/ui/tui.ts +0 -301
- package/tsconfig.json +0 -26
package/src/constants/index.ts
DELETED
|
@@ -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";
|
package/src/core/backlog.ts
DELETED
|
@@ -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
|
-
}
|
package/src/core/remote-tasks.ts
DELETED
|
@@ -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
|
-
}
|