backlog.md 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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
|
@@ -1,526 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "bun:test";
|
|
2
|
-
import { parseDecisionLog, parseDocument, parseMarkdown, parseTask } from "../markdown/parser.ts";
|
|
3
|
-
import {
|
|
4
|
-
serializeDecisionLog,
|
|
5
|
-
serializeDocument,
|
|
6
|
-
serializeTask,
|
|
7
|
-
updateTaskAcceptanceCriteria,
|
|
8
|
-
} from "../markdown/serializer.ts";
|
|
9
|
-
import type { DecisionLog, Document, Task } from "../types/index.ts";
|
|
10
|
-
|
|
11
|
-
describe("Markdown Parser", () => {
|
|
12
|
-
describe("parseMarkdown", () => {
|
|
13
|
-
it("should parse frontmatter and content", () => {
|
|
14
|
-
const content = `---
|
|
15
|
-
title: "Test Task"
|
|
16
|
-
status: "To Do"
|
|
17
|
-
labels: ["bug", "urgent"]
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
This is the task description.
|
|
21
|
-
|
|
22
|
-
## Acceptance Criteria
|
|
23
|
-
|
|
24
|
-
- [ ] First criterion
|
|
25
|
-
- [ ] Second criterion`;
|
|
26
|
-
|
|
27
|
-
const result = parseMarkdown(content);
|
|
28
|
-
|
|
29
|
-
expect(result.frontmatter.title).toBe("Test Task");
|
|
30
|
-
expect(result.frontmatter.status).toBe("To Do");
|
|
31
|
-
expect(result.frontmatter.labels).toEqual(["bug", "urgent"]);
|
|
32
|
-
expect(result.content).toContain("This is the task description");
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it("should handle content without frontmatter", () => {
|
|
36
|
-
const content = "Just some markdown content";
|
|
37
|
-
const result = parseMarkdown(content);
|
|
38
|
-
|
|
39
|
-
expect(result.frontmatter).toEqual({});
|
|
40
|
-
expect(result.content).toBe("Just some markdown content");
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("should handle empty content", () => {
|
|
44
|
-
const content = "";
|
|
45
|
-
const result = parseMarkdown(content);
|
|
46
|
-
|
|
47
|
-
expect(result.frontmatter).toEqual({});
|
|
48
|
-
expect(result.content).toBe("");
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
describe("parseTask", () => {
|
|
53
|
-
it("should parse a complete task", () => {
|
|
54
|
-
const content = `---
|
|
55
|
-
id: task-1
|
|
56
|
-
title: "Fix login bug"
|
|
57
|
-
status: "In Progress"
|
|
58
|
-
assignee: "@developer"
|
|
59
|
-
reporter: "@manager"
|
|
60
|
-
created_date: "2025-06-03"
|
|
61
|
-
labels: ["bug", "frontend"]
|
|
62
|
-
milestone: "v1.0"
|
|
63
|
-
dependencies: ["task-0"]
|
|
64
|
-
parent_task_id: "task-parent"
|
|
65
|
-
subtasks: ["task-1.1", "task-1.2"]
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
## Description
|
|
69
|
-
|
|
70
|
-
Fix the login bug that prevents users from signing in.
|
|
71
|
-
|
|
72
|
-
## Acceptance Criteria
|
|
73
|
-
|
|
74
|
-
- [ ] Login form validates correctly
|
|
75
|
-
- [ ] Error messages are displayed properly`;
|
|
76
|
-
|
|
77
|
-
const task = parseTask(content);
|
|
78
|
-
|
|
79
|
-
expect(task.id).toBe("task-1");
|
|
80
|
-
expect(task.title).toBe("Fix login bug");
|
|
81
|
-
expect(task.status).toBe("In Progress");
|
|
82
|
-
expect(task.assignee).toEqual(["@developer"]);
|
|
83
|
-
expect(task.reporter).toBe("@manager");
|
|
84
|
-
expect(task.createdDate).toBe("2025-06-03");
|
|
85
|
-
expect(task.labels).toEqual(["bug", "frontend"]);
|
|
86
|
-
expect(task.milestone).toBe("v1.0");
|
|
87
|
-
expect(task.dependencies).toEqual(["task-0"]);
|
|
88
|
-
expect(task.parentTaskId).toBe("task-parent");
|
|
89
|
-
expect(task.subtasks).toEqual(["task-1.1", "task-1.2"]);
|
|
90
|
-
expect(task.acceptanceCriteria).toEqual([
|
|
91
|
-
"Login form validates correctly",
|
|
92
|
-
"Error messages are displayed properly",
|
|
93
|
-
]);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it("should parse a task with minimal fields", () => {
|
|
97
|
-
const content = `---
|
|
98
|
-
id: task-2
|
|
99
|
-
title: "Simple task"
|
|
100
|
-
---
|
|
101
|
-
|
|
102
|
-
Just a basic task.`;
|
|
103
|
-
|
|
104
|
-
const task = parseTask(content);
|
|
105
|
-
|
|
106
|
-
expect(task.id).toBe("task-2");
|
|
107
|
-
expect(task.title).toBe("Simple task");
|
|
108
|
-
expect(task.status).toBe("");
|
|
109
|
-
expect(task.assignee).toEqual([]);
|
|
110
|
-
expect(task.reporter).toBeUndefined();
|
|
111
|
-
expect(task.labels).toEqual([]);
|
|
112
|
-
expect(task.dependencies).toEqual([]);
|
|
113
|
-
expect(task.acceptanceCriteria).toEqual([]);
|
|
114
|
-
expect(task.parentTaskId).toBeUndefined();
|
|
115
|
-
expect(task.subtasks).toBeUndefined();
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it("should handle task with empty status", () => {
|
|
119
|
-
const content = `---
|
|
120
|
-
id: task-3
|
|
121
|
-
title: "No status task"
|
|
122
|
-
created_date: "2025-06-07"
|
|
123
|
-
---
|
|
124
|
-
|
|
125
|
-
Task without status.`;
|
|
126
|
-
|
|
127
|
-
const task = parseTask(content);
|
|
128
|
-
|
|
129
|
-
expect(task.status).toBe("");
|
|
130
|
-
expect(task.createdDate).toBe("2025-06-07");
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it("should parse unquoted created_date", () => {
|
|
134
|
-
const content = `---
|
|
135
|
-
id: task-5
|
|
136
|
-
title: "Unquoted"
|
|
137
|
-
created_date: 2025-06-08
|
|
138
|
-
---`;
|
|
139
|
-
|
|
140
|
-
const task = parseTask(content);
|
|
141
|
-
|
|
142
|
-
expect(task.createdDate).toBe("2025-06-08");
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
it("should parse created_date in short format", () => {
|
|
146
|
-
const content = `---
|
|
147
|
-
id: task-6
|
|
148
|
-
title: "Short"
|
|
149
|
-
created_date: 08-06-25
|
|
150
|
-
---`;
|
|
151
|
-
|
|
152
|
-
const task = parseTask(content);
|
|
153
|
-
|
|
154
|
-
expect(task.createdDate).toBe("2025-06-08");
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it("should extract acceptance criteria with checked items", () => {
|
|
158
|
-
const content = `---
|
|
159
|
-
id: task-4
|
|
160
|
-
title: "Test with mixed criteria"
|
|
161
|
-
---
|
|
162
|
-
|
|
163
|
-
## Acceptance Criteria
|
|
164
|
-
|
|
165
|
-
- [ ] Todo item
|
|
166
|
-
- [x] Done item
|
|
167
|
-
- [ ] Another todo`;
|
|
168
|
-
|
|
169
|
-
const task = parseTask(content);
|
|
170
|
-
|
|
171
|
-
expect(task.acceptanceCriteria).toEqual(["Todo item", "Done item", "Another todo"]);
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
it("should parse unquoted assignee names starting with @", () => {
|
|
175
|
-
const content = `---
|
|
176
|
-
id: task-5
|
|
177
|
-
title: "Assignee Test"
|
|
178
|
-
assignee: @MrLesk
|
|
179
|
-
---
|
|
180
|
-
|
|
181
|
-
Test task.`;
|
|
182
|
-
|
|
183
|
-
const task = parseTask(content);
|
|
184
|
-
|
|
185
|
-
expect(task.assignee).toEqual(["@MrLesk"]);
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
it("should parse unquoted reporter names starting with @", () => {
|
|
189
|
-
const content = `---
|
|
190
|
-
id: task-6
|
|
191
|
-
title: "Reporter Test"
|
|
192
|
-
assignee: []
|
|
193
|
-
reporter: @MrLesk
|
|
194
|
-
created_date: 2025-06-08
|
|
195
|
-
---
|
|
196
|
-
|
|
197
|
-
Test task with reporter.`;
|
|
198
|
-
|
|
199
|
-
const task = parseTask(content);
|
|
200
|
-
|
|
201
|
-
expect(task.reporter).toBe("@MrLesk");
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
describe("parseDecisionLog", () => {
|
|
206
|
-
it("should parse a decision log", () => {
|
|
207
|
-
const content = `---
|
|
208
|
-
id: decision-1
|
|
209
|
-
title: "Use TypeScript for backend"
|
|
210
|
-
date: "2025-06-03"
|
|
211
|
-
status: "accepted"
|
|
212
|
-
---
|
|
213
|
-
|
|
214
|
-
## Context
|
|
215
|
-
|
|
216
|
-
We need to choose a language for the backend.
|
|
217
|
-
|
|
218
|
-
## Decision
|
|
219
|
-
|
|
220
|
-
We will use TypeScript for better type safety.
|
|
221
|
-
|
|
222
|
-
## Consequences
|
|
223
|
-
|
|
224
|
-
Better development experience but steeper learning curve.`;
|
|
225
|
-
|
|
226
|
-
const decision = parseDecisionLog(content);
|
|
227
|
-
|
|
228
|
-
expect(decision.id).toBe("decision-1");
|
|
229
|
-
expect(decision.title).toBe("Use TypeScript for backend");
|
|
230
|
-
expect(decision.status).toBe("accepted");
|
|
231
|
-
expect(decision.context).toBe("We need to choose a language for the backend.");
|
|
232
|
-
expect(decision.decision).toBe("We will use TypeScript for better type safety.");
|
|
233
|
-
expect(decision.consequences).toBe("Better development experience but steeper learning curve.");
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
it("should parse decision log with alternatives", () => {
|
|
237
|
-
const content = `---
|
|
238
|
-
id: decision-2
|
|
239
|
-
title: "Choose database"
|
|
240
|
-
date: "2025-06-03"
|
|
241
|
-
status: "proposed"
|
|
242
|
-
---
|
|
243
|
-
|
|
244
|
-
## Context
|
|
245
|
-
|
|
246
|
-
Need a database solution.
|
|
247
|
-
|
|
248
|
-
## Decision
|
|
249
|
-
|
|
250
|
-
Use PostgreSQL.
|
|
251
|
-
|
|
252
|
-
## Consequences
|
|
253
|
-
|
|
254
|
-
Good performance and reliability.
|
|
255
|
-
|
|
256
|
-
## Alternatives
|
|
257
|
-
|
|
258
|
-
Considered MongoDB and MySQL.`;
|
|
259
|
-
|
|
260
|
-
const decision = parseDecisionLog(content);
|
|
261
|
-
|
|
262
|
-
expect(decision.alternatives).toBe("Considered MongoDB and MySQL.");
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
it("should handle missing sections", () => {
|
|
266
|
-
const content = `---
|
|
267
|
-
id: decision-3
|
|
268
|
-
title: "Minimal decision"
|
|
269
|
-
date: "2025-06-03"
|
|
270
|
-
status: "proposed"
|
|
271
|
-
---
|
|
272
|
-
|
|
273
|
-
## Context
|
|
274
|
-
|
|
275
|
-
Some context.`;
|
|
276
|
-
|
|
277
|
-
const decision = parseDecisionLog(content);
|
|
278
|
-
|
|
279
|
-
expect(decision.context).toBe("Some context.");
|
|
280
|
-
expect(decision.decision).toBe("");
|
|
281
|
-
expect(decision.consequences).toBe("");
|
|
282
|
-
expect(decision.alternatives).toBeUndefined();
|
|
283
|
-
});
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
describe("parseDocument", () => {
|
|
287
|
-
it("should parse a document", () => {
|
|
288
|
-
const content = `---
|
|
289
|
-
id: doc-1
|
|
290
|
-
title: "API Guide"
|
|
291
|
-
type: "guide"
|
|
292
|
-
created_date: 2025-06-07
|
|
293
|
-
tags: [api]
|
|
294
|
-
---
|
|
295
|
-
|
|
296
|
-
Document body.`;
|
|
297
|
-
|
|
298
|
-
const doc = parseDocument(content);
|
|
299
|
-
|
|
300
|
-
expect(doc.id).toBe("doc-1");
|
|
301
|
-
expect(doc.title).toBe("API Guide");
|
|
302
|
-
expect(doc.type).toBe("guide");
|
|
303
|
-
expect(doc.createdDate).toBe("2025-06-07");
|
|
304
|
-
expect(doc.tags).toEqual(["api"]);
|
|
305
|
-
expect(doc.content).toBe("Document body.");
|
|
306
|
-
});
|
|
307
|
-
});
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
describe("Markdown Serializer", () => {
|
|
311
|
-
describe("serializeTask", () => {
|
|
312
|
-
it("should serialize a task correctly", () => {
|
|
313
|
-
const task: Task = {
|
|
314
|
-
id: "task-1",
|
|
315
|
-
title: "Test Task",
|
|
316
|
-
status: "To Do",
|
|
317
|
-
assignee: ["@developer"],
|
|
318
|
-
reporter: "@manager",
|
|
319
|
-
createdDate: "2025-06-03",
|
|
320
|
-
labels: ["bug", "frontend"],
|
|
321
|
-
milestone: "v1.0",
|
|
322
|
-
dependencies: ["task-0"],
|
|
323
|
-
description: "This is a test task description.",
|
|
324
|
-
};
|
|
325
|
-
|
|
326
|
-
const result = serializeTask(task);
|
|
327
|
-
|
|
328
|
-
expect(result).toContain("id: task-1");
|
|
329
|
-
expect(result).toContain("title: Test Task");
|
|
330
|
-
expect(result).toContain("status: To Do");
|
|
331
|
-
expect(result).toContain("created_date: '2025-06-03'");
|
|
332
|
-
expect(result).toContain("labels:");
|
|
333
|
-
expect(result).toContain("- bug");
|
|
334
|
-
expect(result).toContain("- frontend");
|
|
335
|
-
expect(result).toContain("This is a test task description.");
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
it("should serialize task with subtasks", () => {
|
|
339
|
-
const task: Task = {
|
|
340
|
-
id: "task-parent",
|
|
341
|
-
title: "Parent Task",
|
|
342
|
-
status: "In Progress",
|
|
343
|
-
assignee: [],
|
|
344
|
-
createdDate: "2025-06-03",
|
|
345
|
-
labels: [],
|
|
346
|
-
dependencies: [],
|
|
347
|
-
description: "A parent task with subtasks.",
|
|
348
|
-
subtasks: ["task-parent.1", "task-parent.2"],
|
|
349
|
-
};
|
|
350
|
-
|
|
351
|
-
const result = serializeTask(task);
|
|
352
|
-
|
|
353
|
-
expect(result).toContain("subtasks:");
|
|
354
|
-
expect(result).toContain("- task-parent.1");
|
|
355
|
-
expect(result).toContain("- task-parent.2");
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
it("should serialize task with parent", () => {
|
|
359
|
-
const task: Task = {
|
|
360
|
-
id: "task-1.1",
|
|
361
|
-
title: "Subtask",
|
|
362
|
-
status: "To Do",
|
|
363
|
-
assignee: [],
|
|
364
|
-
createdDate: "2025-06-03",
|
|
365
|
-
labels: [],
|
|
366
|
-
dependencies: [],
|
|
367
|
-
description: "A subtask.",
|
|
368
|
-
parentTaskId: "task-1",
|
|
369
|
-
};
|
|
370
|
-
|
|
371
|
-
const result = serializeTask(task);
|
|
372
|
-
|
|
373
|
-
expect(result).toContain("parent_task_id: task-1");
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
it("should serialize minimal task", () => {
|
|
377
|
-
const task: Task = {
|
|
378
|
-
id: "task-minimal",
|
|
379
|
-
title: "Minimal Task",
|
|
380
|
-
status: "Draft",
|
|
381
|
-
assignee: [],
|
|
382
|
-
createdDate: "2025-06-03",
|
|
383
|
-
labels: [],
|
|
384
|
-
dependencies: [],
|
|
385
|
-
description: "Minimal task.",
|
|
386
|
-
};
|
|
387
|
-
|
|
388
|
-
const result = serializeTask(task);
|
|
389
|
-
|
|
390
|
-
expect(result).toContain("id: task-minimal");
|
|
391
|
-
expect(result).toContain("title: Minimal Task");
|
|
392
|
-
expect(result).toContain("assignee: []");
|
|
393
|
-
expect(result).not.toContain("reporter:");
|
|
394
|
-
expect(result).not.toContain("updated_date:");
|
|
395
|
-
});
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
describe("serializeDecisionLog", () => {
|
|
399
|
-
it("should serialize a decision log correctly", () => {
|
|
400
|
-
const decision: DecisionLog = {
|
|
401
|
-
id: "decision-1",
|
|
402
|
-
title: "Use TypeScript",
|
|
403
|
-
date: "2025-06-03",
|
|
404
|
-
status: "accepted",
|
|
405
|
-
context: "We need type safety",
|
|
406
|
-
decision: "Use TypeScript",
|
|
407
|
-
consequences: "Better DX",
|
|
408
|
-
};
|
|
409
|
-
|
|
410
|
-
const result = serializeDecisionLog(decision);
|
|
411
|
-
|
|
412
|
-
expect(result).toContain("id: decision-1");
|
|
413
|
-
expect(result).toContain("## Context");
|
|
414
|
-
expect(result).toContain("We need type safety");
|
|
415
|
-
expect(result).toContain("## Decision");
|
|
416
|
-
expect(result).toContain("Use TypeScript");
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
it("should serialize decision log with alternatives", () => {
|
|
420
|
-
const decision: DecisionLog = {
|
|
421
|
-
id: "decision-2",
|
|
422
|
-
title: "Database Choice",
|
|
423
|
-
date: "2025-06-03",
|
|
424
|
-
status: "accepted",
|
|
425
|
-
context: "Need database",
|
|
426
|
-
decision: "PostgreSQL",
|
|
427
|
-
consequences: "Good performance",
|
|
428
|
-
alternatives: "Considered MongoDB",
|
|
429
|
-
};
|
|
430
|
-
|
|
431
|
-
const result = serializeDecisionLog(decision);
|
|
432
|
-
|
|
433
|
-
expect(result).toContain("## Alternatives");
|
|
434
|
-
expect(result).toContain("Considered MongoDB");
|
|
435
|
-
});
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
describe("serializeDocument", () => {
|
|
439
|
-
it("should serialize a document correctly", () => {
|
|
440
|
-
const document: Document = {
|
|
441
|
-
id: "doc-1",
|
|
442
|
-
title: "API Documentation",
|
|
443
|
-
type: "specification",
|
|
444
|
-
createdDate: "2025-06-07",
|
|
445
|
-
updatedDate: "2025-06-08",
|
|
446
|
-
content: "This document describes the API endpoints.",
|
|
447
|
-
tags: ["api", "docs"],
|
|
448
|
-
};
|
|
449
|
-
|
|
450
|
-
const result = serializeDocument(document);
|
|
451
|
-
|
|
452
|
-
expect(result).toContain("id: doc-1");
|
|
453
|
-
expect(result).toContain("title: API Documentation");
|
|
454
|
-
expect(result).toContain("type: specification");
|
|
455
|
-
expect(result).toContain("created_date: '2025-06-07'");
|
|
456
|
-
expect(result).toContain("updated_date: '2025-06-08'");
|
|
457
|
-
expect(result).toContain("tags:");
|
|
458
|
-
expect(result).toContain("- api");
|
|
459
|
-
expect(result).toContain("- docs");
|
|
460
|
-
expect(result).toContain("This document describes the API endpoints.");
|
|
461
|
-
});
|
|
462
|
-
|
|
463
|
-
it("should serialize document without optional fields", () => {
|
|
464
|
-
const document: Document = {
|
|
465
|
-
id: "doc-2",
|
|
466
|
-
title: "Simple Doc",
|
|
467
|
-
type: "guide",
|
|
468
|
-
createdDate: "2025-06-07",
|
|
469
|
-
content: "Simple content.",
|
|
470
|
-
};
|
|
471
|
-
|
|
472
|
-
const result = serializeDocument(document);
|
|
473
|
-
|
|
474
|
-
expect(result).toContain("id: doc-2");
|
|
475
|
-
expect(result).not.toContain("updated_date:");
|
|
476
|
-
expect(result).not.toContain("tags:");
|
|
477
|
-
});
|
|
478
|
-
});
|
|
479
|
-
|
|
480
|
-
describe("updateTaskAcceptanceCriteria", () => {
|
|
481
|
-
it("should add acceptance criteria to content without existing section", () => {
|
|
482
|
-
const content = "# Task Description\n\nThis is a simple task.";
|
|
483
|
-
const criteria = ["Login works correctly", "Error handling is proper"];
|
|
484
|
-
|
|
485
|
-
const result = updateTaskAcceptanceCriteria(content, criteria);
|
|
486
|
-
|
|
487
|
-
expect(result).toContain("## Acceptance Criteria");
|
|
488
|
-
expect(result).toContain("- [ ] Login works correctly");
|
|
489
|
-
expect(result).toContain("- [ ] Error handling is proper");
|
|
490
|
-
});
|
|
491
|
-
|
|
492
|
-
it("should replace existing acceptance criteria section", () => {
|
|
493
|
-
const content = `# Task Description
|
|
494
|
-
|
|
495
|
-
This is a task with existing criteria.
|
|
496
|
-
|
|
497
|
-
## Acceptance Criteria
|
|
498
|
-
|
|
499
|
-
- [ ] Old criterion 1
|
|
500
|
-
- [ ] Old criterion 2
|
|
501
|
-
|
|
502
|
-
## Notes
|
|
503
|
-
|
|
504
|
-
Some additional notes.`;
|
|
505
|
-
|
|
506
|
-
const criteria = ["New criterion 1", "New criterion 2"];
|
|
507
|
-
|
|
508
|
-
const result = updateTaskAcceptanceCriteria(content, criteria);
|
|
509
|
-
|
|
510
|
-
expect(result).toContain("- [ ] New criterion 1");
|
|
511
|
-
expect(result).toContain("- [ ] New criterion 2");
|
|
512
|
-
expect(result).not.toContain("Old criterion 1");
|
|
513
|
-
expect(result).toContain("## Notes");
|
|
514
|
-
});
|
|
515
|
-
|
|
516
|
-
it("should handle empty criteria array", () => {
|
|
517
|
-
const content = "# Task Description\n\nSimple task.";
|
|
518
|
-
const criteria: string[] = [];
|
|
519
|
-
|
|
520
|
-
const result = updateTaskAcceptanceCriteria(content, criteria);
|
|
521
|
-
|
|
522
|
-
expect(result).toContain("## Acceptance Criteria");
|
|
523
|
-
expect(result).not.toContain("- [ ]");
|
|
524
|
-
});
|
|
525
|
-
});
|
|
526
|
-
});
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "bun:test";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import type { GitOps } from "../core/git-ops.ts";
|
|
4
|
-
import { loadRemoteTasks, resolveTaskConflict } from "../core/remote-tasks.ts";
|
|
5
|
-
import type { TaskWithMetadata } from "../core/remote-tasks.ts";
|
|
6
|
-
|
|
7
|
-
// Mock GitOps for testing
|
|
8
|
-
class MockGitOps implements Partial<GitOps> {
|
|
9
|
-
private tasks: Record<string, { content: string; timestamp: Date }>[] = [];
|
|
10
|
-
|
|
11
|
-
constructor(tasks: Record<string, { content: string; timestamp: Date }>[]) {
|
|
12
|
-
this.tasks = tasks;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async fetch(): Promise<void> {
|
|
16
|
-
// Mock fetch
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
async listRemoteBranches(): Promise<string[]> {
|
|
20
|
-
return ["main", "feature", "feature2"];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async listFilesInTree(ref: string, path: string): Promise<string[]> {
|
|
24
|
-
if (ref === "origin/main") {
|
|
25
|
-
return [".backlog/tasks/task-1 - Main Task.md"];
|
|
26
|
-
}
|
|
27
|
-
if (ref === "origin/feature") {
|
|
28
|
-
return [".backlog/tasks/task-2 - Feature Task.md"];
|
|
29
|
-
}
|
|
30
|
-
if (ref === "origin/feature2") {
|
|
31
|
-
return [".backlog/tasks/task-3 - Feature2 Task.md"];
|
|
32
|
-
}
|
|
33
|
-
return [];
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async showFile(ref: string, file: string): Promise<string> {
|
|
37
|
-
if (file.includes("task-1")) {
|
|
38
|
-
return `---
|
|
39
|
-
id: task-1
|
|
40
|
-
title: Main Task
|
|
41
|
-
status: To Do
|
|
42
|
-
assignee: []
|
|
43
|
-
created_date: 2025-06-13
|
|
44
|
-
labels: []
|
|
45
|
-
dependencies: []
|
|
46
|
-
---\n\n## Description\n\nMain task`;
|
|
47
|
-
}
|
|
48
|
-
if (file.includes("task-2")) {
|
|
49
|
-
return `---
|
|
50
|
-
id: task-2
|
|
51
|
-
title: Feature Task
|
|
52
|
-
status: In Progress
|
|
53
|
-
assignee: []
|
|
54
|
-
created_date: 2025-06-13
|
|
55
|
-
labels: []
|
|
56
|
-
dependencies: []
|
|
57
|
-
---\n\n## Description\n\nFeature task`;
|
|
58
|
-
}
|
|
59
|
-
if (file.includes("task-3")) {
|
|
60
|
-
return `---
|
|
61
|
-
id: task-3
|
|
62
|
-
title: Feature2 Task
|
|
63
|
-
status: Done
|
|
64
|
-
assignee: []
|
|
65
|
-
created_date: 2025-06-13
|
|
66
|
-
labels: []
|
|
67
|
-
dependencies: []
|
|
68
|
-
---\n\n## Description\n\nFeature2 task`;
|
|
69
|
-
}
|
|
70
|
-
return "";
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async getFileLastModifiedTime(ref: string, file: string): Promise<Date | null> {
|
|
74
|
-
return new Date("2025-06-13");
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
describe("Parallel remote task loading", () => {
|
|
79
|
-
it("should load tasks from multiple branches in parallel", async () => {
|
|
80
|
-
const mockGitOps = new MockGitOps([]) as unknown as GitOps;
|
|
81
|
-
|
|
82
|
-
// Track progress messages
|
|
83
|
-
const progressMessages: string[] = [];
|
|
84
|
-
const remoteTasks = await loadRemoteTasks(mockGitOps, (msg) => {
|
|
85
|
-
progressMessages.push(msg);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
// Verify results - we should have tasks from all remote branches
|
|
89
|
-
expect(remoteTasks.length).toBe(3);
|
|
90
|
-
const taskIds = remoteTasks.map((t) => t.id);
|
|
91
|
-
expect(taskIds).toContain("task-1");
|
|
92
|
-
expect(taskIds).toContain("task-2");
|
|
93
|
-
expect(taskIds).toContain("task-3");
|
|
94
|
-
|
|
95
|
-
// Verify each task has correct metadata
|
|
96
|
-
const task1 = remoteTasks.find((t) => t.id === "task-1");
|
|
97
|
-
expect(task1?.source).toBe("remote");
|
|
98
|
-
expect(task1?.branch).toBe("main");
|
|
99
|
-
expect(task1?.status).toBe("To Do");
|
|
100
|
-
|
|
101
|
-
// Verify progress reporting
|
|
102
|
-
expect(progressMessages.some((msg) => msg.includes("Fetching remote branches"))).toBe(true);
|
|
103
|
-
expect(progressMessages.some((msg) => msg.includes("Found 3 remote branches"))).toBe(true);
|
|
104
|
-
expect(progressMessages.some((msg) => msg.includes("Loaded 3 total remote tasks"))).toBe(true);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it("should handle errors gracefully", async () => {
|
|
108
|
-
// Create a mock that throws an error
|
|
109
|
-
const errorGitOps = {
|
|
110
|
-
fetch: async () => {
|
|
111
|
-
throw new Error("Network error");
|
|
112
|
-
},
|
|
113
|
-
} as unknown as GitOps;
|
|
114
|
-
|
|
115
|
-
// Should return empty array on error
|
|
116
|
-
const remoteTasks = await loadRemoteTasks(errorGitOps);
|
|
117
|
-
expect(remoteTasks).toEqual([]);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it("should resolve task conflicts correctly", async () => {
|
|
121
|
-
const statuses = ["To Do", "In Progress", "Done"];
|
|
122
|
-
|
|
123
|
-
const localTask: TaskWithMetadata = {
|
|
124
|
-
id: "task-1",
|
|
125
|
-
title: "Local Task",
|
|
126
|
-
status: "To Do",
|
|
127
|
-
assignee: [],
|
|
128
|
-
createdDate: "2025-06-13",
|
|
129
|
-
labels: [],
|
|
130
|
-
dependencies: [],
|
|
131
|
-
description: "Local version",
|
|
132
|
-
source: "local",
|
|
133
|
-
lastModified: new Date("2025-06-13T10:00:00Z"),
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
const remoteTask: TaskWithMetadata = {
|
|
137
|
-
id: "task-1",
|
|
138
|
-
title: "Remote Task",
|
|
139
|
-
status: "Done",
|
|
140
|
-
assignee: [],
|
|
141
|
-
createdDate: "2025-06-13",
|
|
142
|
-
labels: [],
|
|
143
|
-
dependencies: [],
|
|
144
|
-
description: "Remote version",
|
|
145
|
-
source: "remote",
|
|
146
|
-
branch: "feature",
|
|
147
|
-
lastModified: new Date("2025-06-13T12:00:00Z"),
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
// Test most_progressed strategy - should pick Done over To Do
|
|
151
|
-
const resolved1 = resolveTaskConflict(localTask, remoteTask, statuses, "most_progressed");
|
|
152
|
-
expect(resolved1.status).toBe("Done");
|
|
153
|
-
expect(resolved1.title).toBe("Remote Task");
|
|
154
|
-
|
|
155
|
-
// Test most_recent strategy - should pick the more recent one
|
|
156
|
-
const resolved2 = resolveTaskConflict(localTask, remoteTask, statuses, "most_recent");
|
|
157
|
-
expect(resolved2.lastModified).toEqual(new Date("2025-06-13T12:00:00Z"));
|
|
158
|
-
expect(resolved2.title).toBe("Remote Task");
|
|
159
|
-
});
|
|
160
|
-
});
|