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,291 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
- import { mkdtemp, rm } from "node:fs/promises";
3
- import { tmpdir } from "node:os";
4
- import { join } from "node:path";
5
- import { exportKanbanBoardToFile, generateKanbanBoard } from "../board.ts";
6
- import type { Task } from "../types/index.ts";
7
-
8
- describe("generateKanbanBoard", () => {
9
- it("creates board layout with statuses", () => {
10
- const tasks: Task[] = [
11
- {
12
- id: "task-1",
13
- title: "First",
14
- status: "To Do",
15
- assignee: [],
16
- createdDate: "",
17
- labels: [],
18
- dependencies: [],
19
- description: "",
20
- },
21
- {
22
- id: "task-2",
23
- title: "Second",
24
- status: "In Progress",
25
- assignee: [],
26
- createdDate: "",
27
- labels: [],
28
- dependencies: [],
29
- description: "",
30
- },
31
- {
32
- id: "task-3",
33
- title: "Third",
34
- status: "Done",
35
- assignee: [],
36
- createdDate: "",
37
- labels: [],
38
- dependencies: [],
39
- description: "",
40
- },
41
- ];
42
-
43
- const board = generateKanbanBoard(tasks, ["To Do", "In Progress", "Done"]);
44
- const lines = board.split("\n");
45
- expect(lines[0]).toContain("To Do");
46
- expect(lines[0]).toContain("In Progress");
47
- expect(lines[0]).toContain("Done");
48
- // Tasks are now on separate lines: ID first, then title
49
- expect(board).toContain("task-1");
50
- expect(board).toContain("First");
51
- expect(board).toContain("task-2");
52
- expect(board).toContain("Second");
53
- expect(board).toContain("task-3");
54
- expect(board).toContain("Third");
55
- });
56
-
57
- it("handles tasks with no status", () => {
58
- const tasks: Task[] = [
59
- {
60
- id: "task-1",
61
- title: "No Status Task",
62
- status: "",
63
- assignee: [],
64
- createdDate: "",
65
- labels: [],
66
- dependencies: [],
67
- description: "",
68
- },
69
- ];
70
-
71
- const board = generateKanbanBoard(tasks, ["To Do", "In Progress", "Done"]);
72
- expect(board).toContain("No Status");
73
- expect(board).toContain("task-1");
74
- expect(board).toContain("No Status Task");
75
- });
76
-
77
- it("omits 'No Status' column when all tasks have status", () => {
78
- const tasks: Task[] = [
79
- {
80
- id: "task-1",
81
- title: "Status Task",
82
- status: "To Do",
83
- assignee: [],
84
- createdDate: "",
85
- labels: [],
86
- dependencies: [],
87
- description: "",
88
- },
89
- ];
90
-
91
- const board = generateKanbanBoard(tasks, ["To Do", "In Progress", "Done"]);
92
- expect(board).not.toContain("No Status");
93
- });
94
-
95
- it("handles empty task list", () => {
96
- const board = generateKanbanBoard([], ["To Do", "In Progress", "Done"]);
97
- expect(board).toBe(""); // No columns when no tasks
98
- });
99
-
100
- it("respects status order from config", () => {
101
- const tasks: Task[] = [
102
- {
103
- id: "task-1",
104
- title: "First",
105
- status: "Done",
106
- assignee: [],
107
- createdDate: "",
108
- labels: [],
109
- dependencies: [],
110
- description: "",
111
- },
112
- {
113
- id: "task-2",
114
- title: "Second",
115
- status: "To Do",
116
- assignee: [],
117
- createdDate: "",
118
- labels: [],
119
- dependencies: [],
120
- description: "",
121
- },
122
- ];
123
-
124
- const board = generateKanbanBoard(tasks, ["To Do", "In Progress", "Done"]);
125
- const lines = board.split("\n");
126
- // Status order should be preserved even if tasks exist in different order
127
- const header = lines[0];
128
- const todoIndex = header.indexOf("To Do");
129
- const doneIndex = header.indexOf("Done");
130
- expect(todoIndex).toBeLessThan(doneIndex);
131
- });
132
-
133
- it("handles long task titles by wrapping text within max column width", () => {
134
- const tasks: Task[] = [
135
- {
136
- id: "task-1",
137
- title: "This is a very long task title that should expand the column width significantly",
138
- status: "To Do",
139
- assignee: [],
140
- createdDate: "",
141
- labels: [],
142
- dependencies: [],
143
- description: "",
144
- },
145
- ];
146
-
147
- const board = generateKanbanBoard(tasks, ["To Do"]);
148
- const lines = board.split("\n");
149
- const header = lines[0];
150
- const taskIdLine = lines[2]; // First task line (ID)
151
- const firstTitleLine = lines[3]; // First wrapped title line
152
- // Column width should be constrained by default maxColumnWidth (20)
153
- expect(header.length).toBe(20);
154
- expect(taskIdLine).toContain("task-1");
155
- expect(firstTitleLine).toContain("This is a very long");
156
- // Should have multiple lines for the wrapped title
157
- expect(lines.length).toBeGreaterThan(4);
158
- });
159
-
160
- it("nests subtasks under their parent when statuses match", () => {
161
- const tasks: Task[] = [
162
- {
163
- id: "task-1",
164
- title: "Parent",
165
- status: "To Do",
166
- assignee: [],
167
- createdDate: "",
168
- labels: [],
169
- dependencies: [],
170
- description: "",
171
- },
172
- {
173
- id: "task-1.1",
174
- title: "Child",
175
- status: "To Do",
176
- assignee: [],
177
- createdDate: "",
178
- labels: [],
179
- dependencies: [],
180
- description: "",
181
- parentTaskId: "task-1",
182
- },
183
- ];
184
-
185
- const board = generateKanbanBoard(tasks, ["To Do"]);
186
- expect(board).toContain(" └─ task-1.1");
187
- expect(board).toContain(" Child");
188
-
189
- const lines = board.split("\n");
190
- const parentIdx = lines.findIndex((l) => l.includes("task-1") && !l.includes("task-1.1"));
191
- const childIdx = lines.findIndex((l) => l.includes(" └─ task-1.1"));
192
- expect(parentIdx).toBeGreaterThan(-1);
193
- expect(childIdx).toBeGreaterThan(parentIdx);
194
- });
195
-
196
- it("sorts tasks by numeric id within each status", () => {
197
- const tasks: Task[] = [
198
- {
199
- id: "task-10",
200
- title: "T10",
201
- status: "To Do",
202
- assignee: [],
203
- createdDate: "",
204
- labels: [],
205
- dependencies: [],
206
- description: "",
207
- },
208
- {
209
- id: "task-2",
210
- title: "T2",
211
- status: "To Do",
212
- assignee: [],
213
- createdDate: "",
214
- labels: [],
215
- dependencies: [],
216
- description: "",
217
- },
218
- ];
219
-
220
- const board = generateKanbanBoard(tasks, ["To Do"]);
221
- const lines = board.split("\n");
222
- const firstIdLine = lines[2];
223
- const secondIdLine = lines[5];
224
- expect(firstIdLine.trim().startsWith("task-2")).toBe(true);
225
- expect(secondIdLine.trim().startsWith("task-10")).toBe(true);
226
- });
227
-
228
- it("creates vertical board layout", () => {
229
- const tasks: Task[] = [
230
- {
231
- id: "task-1",
232
- title: "First",
233
- status: "To Do",
234
- assignee: [],
235
- createdDate: "",
236
- labels: [],
237
- dependencies: [],
238
- description: "",
239
- },
240
- {
241
- id: "task-2",
242
- title: "Second",
243
- status: "In Progress",
244
- assignee: [],
245
- createdDate: "",
246
- labels: [],
247
- dependencies: [],
248
- description: "",
249
- },
250
- ];
251
-
252
- const board = generateKanbanBoard(tasks, ["To Do", "In Progress", "Done"], "vertical");
253
- const lines = board.split("\n");
254
- expect(lines[0]).toBe("To Do");
255
- expect(lines).toContain("In Progress");
256
- expect(board).toContain("task-1");
257
- expect(board).toContain("First");
258
- expect(board).toContain("task-2");
259
- expect(board).toContain("Second");
260
- });
261
- });
262
-
263
- describe("exportKanbanBoardToFile", () => {
264
- it("creates file and appends board content", async () => {
265
- const dir = await mkdtemp(join(tmpdir(), "board-export-"));
266
- const file = join(dir, "readme.md");
267
- const tasks: Task[] = [
268
- {
269
- id: "task-1",
270
- title: "First",
271
- status: "To Do",
272
- assignee: [],
273
- createdDate: "",
274
- labels: [],
275
- dependencies: [],
276
- description: "",
277
- },
278
- ];
279
-
280
- await exportKanbanBoardToFile(tasks, ["To Do"], file);
281
- const initial = await Bun.file(file).text();
282
- expect(initial).toContain("task-1");
283
-
284
- await exportKanbanBoardToFile(tasks, ["To Do"], file);
285
- const second = await Bun.file(file).text();
286
- const occurrences = second.split("task-1").length - 1;
287
- expect(occurrences).toBe(2);
288
-
289
- await rm(dir, { recursive: true, force: true });
290
- });
291
- });
@@ -1,28 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it } from "bun:test";
2
- import { mkdir, rm } from "node:fs/promises";
3
- import { platform } from "node:os";
4
- import { join } from "node:path";
5
-
6
- const TEST_DIR = join(process.cwd(), "test-build");
7
- const isWindows = platform() === "win32";
8
- const executableName = isWindows ? "backlog.exe" : "backlog";
9
- const OUTFILE = join(TEST_DIR, executableName);
10
-
11
- describe("CLI packaging", () => {
12
- beforeEach(async () => {
13
- await rm(TEST_DIR, { recursive: true, force: true }).catch(() => {});
14
- await mkdir(TEST_DIR, { recursive: true });
15
- });
16
-
17
- afterEach(async () => {
18
- await rm(TEST_DIR, { recursive: true, force: true }).catch(() => {});
19
- });
20
-
21
- it("should build and run compiled executable", async () => {
22
- await Bun.spawn(["bun", "build", "src/cli.ts", "--compile", "--outfile", OUTFILE, "--loader:.md=text"]).exited;
23
-
24
- const result = Bun.spawnSync({ cmd: [OUTFILE, "--help"] });
25
- const output = result.stdout.toString();
26
- expect(output).toContain("Backlog project management CLI");
27
- });
28
- });
@@ -1,273 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import {
3
- CHECKBOX_PATTERNS,
4
- type ChecklistItem,
5
- alignAcceptanceCriteria,
6
- extractAndFormatAcceptanceCriteria,
7
- formatChecklist,
8
- formatChecklistItem,
9
- parseCheckboxLine,
10
- parseCheckboxLines,
11
- } from "../ui/checklist.ts";
12
-
13
- describe("Checklist utilities", () => {
14
- describe("CHECKBOX_PATTERNS", () => {
15
- test("should match checked checkbox lines", () => {
16
- const testCases = [
17
- "- [x] Checked item",
18
- " - [x] Indented checked item",
19
- "- [x] Item with extra spaces",
20
- "-[x] No space after dash",
21
- ];
22
-
23
- for (const testCase of testCases) {
24
- expect(CHECKBOX_PATTERNS.CHECKBOX_LINE.test(testCase)).toBe(true);
25
- }
26
- });
27
-
28
- test("should match unchecked checkbox lines", () => {
29
- const testCases = [
30
- "- [ ] Unchecked item",
31
- " - [ ] Indented unchecked item",
32
- "- [ ] Item with extra spaces",
33
- "-[ ] No space after dash",
34
- ];
35
-
36
- for (const testCase of testCases) {
37
- expect(CHECKBOX_PATTERNS.CHECKBOX_LINE.test(testCase)).toBe(true);
38
- }
39
- });
40
-
41
- test("should not match non-checkbox lines", () => {
42
- const testCases = [
43
- "Regular text",
44
- "- Regular bullet point",
45
- "- [ Missing closing bracket",
46
- "- [y] Invalid checkbox state",
47
- "[x] Missing dash prefix",
48
- "## Header",
49
- ];
50
-
51
- for (const testCase of testCases) {
52
- expect(CHECKBOX_PATTERNS.CHECKBOX_LINE.test(testCase)).toBe(false);
53
- }
54
- });
55
-
56
- test("should match checkbox prefix pattern", () => {
57
- expect(CHECKBOX_PATTERNS.CHECKBOX_PREFIX.test("- [x] ")).toBe(true);
58
- expect(CHECKBOX_PATTERNS.CHECKBOX_PREFIX.test("- [ ] ")).toBe(true);
59
- expect(CHECKBOX_PATTERNS.CHECKBOX_PREFIX.test("-[x] ")).toBe(true);
60
- expect(CHECKBOX_PATTERNS.CHECKBOX_PREFIX.test("- [x]")).toBe(true);
61
- });
62
- });
63
-
64
- describe("parseCheckboxLine", () => {
65
- test("should parse checked checkbox lines", () => {
66
- const result = parseCheckboxLine("- [x] This is checked");
67
- expect(result).toEqual({
68
- text: "This is checked",
69
- checked: true,
70
- });
71
- });
72
-
73
- test("should parse unchecked checkbox lines", () => {
74
- const result = parseCheckboxLine("- [ ] This is unchecked");
75
- expect(result).toEqual({
76
- text: "This is unchecked",
77
- checked: false,
78
- });
79
- });
80
-
81
- test("should handle indented checkboxes", () => {
82
- const result = parseCheckboxLine(" - [x] Indented checkbox");
83
- expect(result).toEqual({
84
- text: "Indented checkbox",
85
- checked: true,
86
- });
87
- });
88
-
89
- test("should handle extra whitespace", () => {
90
- const result = parseCheckboxLine("- [x] Extra spaces around text ");
91
- expect(result).toEqual({
92
- text: "Extra spaces around text",
93
- checked: true,
94
- });
95
- });
96
-
97
- test("should return null for non-checkbox lines", () => {
98
- expect(parseCheckboxLine("Regular text")).toBe(null);
99
- expect(parseCheckboxLine("- Regular bullet")).toBe(null);
100
- expect(parseCheckboxLine("## Header")).toBe(null);
101
- });
102
- });
103
-
104
- describe("formatChecklistItem", () => {
105
- test("should format checked item with default options", () => {
106
- const item: ChecklistItem = { text: "Test item", checked: true };
107
- const result = formatChecklistItem(item);
108
- expect(result).toBe(" [x] Test item");
109
- });
110
-
111
- test("should format unchecked item with default options", () => {
112
- const item: ChecklistItem = { text: "Test item", checked: false };
113
- const result = formatChecklistItem(item);
114
- expect(result).toBe(" [ ] Test item");
115
- });
116
-
117
- test("should use custom symbols", () => {
118
- const item: ChecklistItem = { text: "Test item", checked: true };
119
- const result = formatChecklistItem(item, {
120
- checkedSymbol: "☑",
121
- uncheckedSymbol: "☐",
122
- });
123
- expect(result).toBe(" ☑ Test item");
124
- });
125
-
126
- test("should use custom padding", () => {
127
- const item: ChecklistItem = { text: "Test item", checked: false };
128
- const result = formatChecklistItem(item, {
129
- padding: " ",
130
- });
131
- expect(result).toBe(" [ ] Test item");
132
- });
133
- });
134
-
135
- describe("parseCheckboxLines", () => {
136
- test("should parse multiple checkbox lines", () => {
137
- const text = `- [x] First item
138
- - [ ] Second item
139
- - [x] Third item`;
140
-
141
- const result = parseCheckboxLines(text);
142
- expect(result).toEqual([
143
- { text: "First item", checked: true },
144
- { text: "Second item", checked: false },
145
- { text: "Third item", checked: true },
146
- ]);
147
- });
148
-
149
- test("should ignore non-checkbox lines", () => {
150
- const text = `- [x] Checkbox item
151
- Regular text
152
- - [ ] Another checkbox
153
- ## Header`;
154
-
155
- const result = parseCheckboxLines(text);
156
- expect(result).toEqual([
157
- { text: "Checkbox item", checked: true },
158
- { text: "Another checkbox", checked: false },
159
- ]);
160
- });
161
- });
162
-
163
- describe("formatChecklist", () => {
164
- test("should format multiple items consistently", () => {
165
- const items: ChecklistItem[] = [
166
- { text: "First item", checked: true },
167
- { text: "Second item", checked: false },
168
- { text: "Third item", checked: true },
169
- ];
170
-
171
- const result = formatChecklist(items);
172
- expect(result).toEqual([" [x] First item", " [ ] Second item", " [x] Third item"]);
173
- });
174
- });
175
-
176
- describe("alignAcceptanceCriteria", () => {
177
- test("should align checkbox items consistently", () => {
178
- const criteriaSection = `- [x] First criterion
179
- - [ ] Second criterion
180
- - [x] Third criterion`;
181
-
182
- const result = alignAcceptanceCriteria(criteriaSection);
183
- expect(result).toEqual([" [x] First criterion", " [ ] Second criterion", " [x] Third criterion"]);
184
- });
185
-
186
- test("should handle mixed content with consistent padding", () => {
187
- const criteriaSection = `- [x] Checkbox item
188
- Regular note
189
- - [ ] Another checkbox`;
190
-
191
- const result = alignAcceptanceCriteria(criteriaSection);
192
- expect(result).toEqual([" [x] Checkbox item", " Regular note", " [ ] Another checkbox"]);
193
- });
194
-
195
- test("should handle empty or whitespace-only lines", () => {
196
- const criteriaSection = `- [x] First item
197
-
198
- - [ ] Second item
199
-
200
- - [x] Third item`;
201
-
202
- const result = alignAcceptanceCriteria(criteriaSection);
203
- expect(result).toEqual([" [x] First item", " [ ] Second item", " [x] Third item"]);
204
- });
205
- });
206
-
207
- describe("extractAndFormatAcceptanceCriteria", () => {
208
- test("should extract and format acceptance criteria from markdown", () => {
209
- const content = `## Description
210
- Some description here.
211
-
212
- ## Acceptance Criteria
213
- - [x] First criterion is done
214
- - [ ] Second criterion pending
215
- - [x] Third criterion complete
216
-
217
- ## Implementation Notes
218
- Some notes here.`;
219
-
220
- const result = extractAndFormatAcceptanceCriteria(content);
221
- expect(result).toEqual([
222
- " [x] First criterion is done",
223
- " [ ] Second criterion pending",
224
- " [x] Third criterion complete",
225
- ]);
226
- });
227
-
228
- test("should return empty array when no acceptance criteria section exists", () => {
229
- const content = `## Description
230
- Some description here.
231
-
232
- ## Implementation Notes
233
- Some notes here.`;
234
-
235
- const result = extractAndFormatAcceptanceCriteria(content);
236
- expect(result).toEqual([]);
237
- });
238
-
239
- test("should handle case-insensitive section headers", () => {
240
- const content = `## acceptance criteria
241
- - [x] Test item
242
- - [ ] Another test`;
243
-
244
- const result = extractAndFormatAcceptanceCriteria(content);
245
- expect(result).toEqual([" [x] Test item", " [ ] Another test"]);
246
- });
247
- });
248
-
249
- describe("alignment consistency", () => {
250
- test("all formatted items should start at the same column", () => {
251
- const items: ChecklistItem[] = [
252
- { text: "Short", checked: true },
253
- { text: "Much longer item text here", checked: false },
254
- { text: "Medium length item", checked: true },
255
- ];
256
-
257
- const formatted = formatChecklist(items);
258
-
259
- // All items should start with the same padding
260
- for (const line of formatted) {
261
- expect(line.startsWith(" ")).toBe(true);
262
- expect(line.charAt(0)).toBe(" ");
263
- }
264
-
265
- // All checkbox positions should be the same
266
- const checkboxPositions = formatted.map((line) => line.indexOf("["));
267
- const firstPosition = checkboxPositions[0];
268
- for (const position of checkboxPositions) {
269
- expect(position).toBe(firstPosition);
270
- }
271
- });
272
- });
273
- });