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.
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,204 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import {
3
- CODE_PATH_PATTERNS,
4
- extractCodePaths,
5
- isCodePath,
6
- styleCodePath,
7
- transformCodePaths,
8
- transformCodePathsPlain,
9
- } from "../ui/code-path.ts";
10
-
11
- describe("Code path utilities", () => {
12
- describe("CODE_PATH_PATTERNS", () => {
13
- test("should match backticked file paths", () => {
14
- const testCases = [
15
- "`src/cli.ts`",
16
- "`package.json`",
17
- "`/Users/name/project/file.ts`",
18
- "`./relative/path.js`",
19
- "`../parent/file.md`",
20
- "`C:\\Windows\\file.exe`",
21
- ];
22
-
23
- for (const testCase of testCases) {
24
- // Reset regex for each test case
25
- CODE_PATH_PATTERNS.BACKTICKED_PATH.lastIndex = 0;
26
- expect(CODE_PATH_PATTERNS.BACKTICKED_PATH.test(testCase)).toBe(true);
27
- }
28
- });
29
-
30
- test("should not match non-path backticks", () => {
31
- const testCases = ["`just code`", "`function name`", "`variable`", "`123`"];
32
-
33
- for (const testCase of testCases) {
34
- // Reset regex lastIndex
35
- CODE_PATH_PATTERNS.BACKTICKED_PATH.lastIndex = 0;
36
- const match = testCase.match(CODE_PATH_PATTERNS.BACKTICKED_PATH);
37
- if (match) {
38
- const content = match[0].slice(1, -1);
39
- expect(isCodePath(content)).toBe(false);
40
- }
41
- }
42
- });
43
- });
44
-
45
- describe("isCodePath", () => {
46
- test("should detect file paths with extensions", () => {
47
- expect(isCodePath("src/cli.ts")).toBe(true);
48
- expect(isCodePath("package.json")).toBe(true);
49
- expect(isCodePath("file.md")).toBe(true);
50
- expect(isCodePath("/full/path/file.js")).toBe(true);
51
- });
52
-
53
- test("should detect paths with separators", () => {
54
- expect(isCodePath("src/utils")).toBe(true);
55
- expect(isCodePath("folder/subfolder")).toBe(true);
56
- expect(isCodePath("/absolute/path")).toBe(true);
57
- expect(isCodePath("C:\\Windows\\path")).toBe(true);
58
- });
59
-
60
- test("should not detect non-paths", () => {
61
- expect(isCodePath("just text")).toBe(false);
62
- expect(isCodePath("function")).toBe(false);
63
- expect(isCodePath("variable")).toBe(false);
64
- expect(isCodePath("123")).toBe(false);
65
- });
66
- });
67
-
68
- describe("extractCodePaths", () => {
69
- test("should extract file paths from text", () => {
70
- const text = "Check `src/cli.ts` and `package.json` for details.";
71
- const result = extractCodePaths(text);
72
- expect(result).toEqual(["src/cli.ts", "package.json"]);
73
- });
74
-
75
- test("should ignore non-path backticks", () => {
76
- const text = "Use `function` to call `src/cli.ts` method.";
77
- const result = extractCodePaths(text);
78
- expect(result).toEqual(["src/cli.ts"]);
79
- });
80
-
81
- test("should handle empty or no matches", () => {
82
- expect(extractCodePaths("No paths here")).toEqual([]);
83
- expect(extractCodePaths("Only `variables` here")).toEqual([]);
84
- expect(extractCodePaths("")).toEqual([]);
85
- });
86
-
87
- test("should handle complex paths", () => {
88
- const text = "Files: `/absolute/path/file.ts`, `./relative/file.js`, `../parent/file.md`";
89
- const result = extractCodePaths(text);
90
- expect(result).toEqual(["/absolute/path/file.ts", "./relative/file.js", "../parent/file.md"]);
91
- });
92
- });
93
-
94
- describe("styleCodePath", () => {
95
- test("should wrap path in gray styling tags", () => {
96
- const result = styleCodePath("src/cli.ts");
97
- expect(result).toBe("{gray-fg}`src/cli.ts`{/gray-fg}");
98
- });
99
-
100
- test("should handle paths with special characters", () => {
101
- const result = styleCodePath("/path/with-dashes_and.underscores.ts");
102
- expect(result).toBe("{gray-fg}`/path/with-dashes_and.underscores.ts`{/gray-fg}");
103
- });
104
- });
105
-
106
- describe("transformCodePaths", () => {
107
- test("should style isolated code paths", () => {
108
- const text = "Check this file: `src/cli.ts`";
109
- const result = transformCodePaths(text);
110
- expect(result).toBe("Check this file:\n{gray-fg}`src/cli.ts`{/gray-fg}");
111
- });
112
-
113
- test("should extract and separate multiple paths in prose", () => {
114
- const text = "Modify `src/cli.ts` and `src/ui/board.ts` to implement the feature.";
115
- const result = transformCodePaths(text);
116
- expect(result).toBe(
117
- "Modify and to implement the feature.\n{gray-fg}`src/cli.ts`{/gray-fg}\n{gray-fg}`src/ui/board.ts`{/gray-fg}",
118
- );
119
- });
120
-
121
- test("should preserve line breaks", () => {
122
- const text = "First line with `file1.ts`\nSecond line with `file2.js`";
123
- const result = transformCodePaths(text);
124
- expect(result).toContain("First line with\n{gray-fg}`file1.ts`{/gray-fg}");
125
- expect(result).toContain("Second line with\n{gray-fg}`file2.js`{/gray-fg}");
126
- });
127
-
128
- test("should handle text without code paths", () => {
129
- const text = "This is just regular text with `variables` and `functions`.";
130
- const result = transformCodePaths(text);
131
- expect(result).toBe(text);
132
- });
133
-
134
- test("should handle empty input", () => {
135
- expect(transformCodePaths("")).toBe("");
136
- // biome-ignore lint/suspicious/noExplicitAny: testing null/undefined inputs
137
- expect(transformCodePaths(null as any)).toBe(null);
138
- // biome-ignore lint/suspicious/noExplicitAny: testing null/undefined inputs
139
- expect(transformCodePaths(undefined as any)).toBe(undefined);
140
- });
141
-
142
- test("should handle only a path on a line", () => {
143
- const text = "`src/cli.ts`";
144
- const result = transformCodePaths(text);
145
- expect(result).toBe("{gray-fg}`src/cli.ts`{/gray-fg}");
146
- });
147
- });
148
-
149
- describe("transformCodePathsPlain", () => {
150
- test("should preserve code paths in plain text", () => {
151
- const text = "Check `src/cli.ts` and `package.json` files.";
152
- const result = transformCodePathsPlain(text);
153
- expect(result).toBe("Check `src/cli.ts` and `package.json` files.");
154
- });
155
-
156
- test("should ignore non-path backticks", () => {
157
- const text = "Use `function` to call `src/cli.ts` method.";
158
- const result = transformCodePathsPlain(text);
159
- expect(result).toBe("Use `function` to call `src/cli.ts` method.");
160
- });
161
-
162
- test("should handle empty input", () => {
163
- expect(transformCodePathsPlain("")).toBe("");
164
- // biome-ignore lint/suspicious/noExplicitAny: testing null/undefined inputs
165
- expect(transformCodePathsPlain(null as any)).toBe(null);
166
- // biome-ignore lint/suspicious/noExplicitAny: testing null/undefined inputs
167
- expect(transformCodePathsPlain(undefined as any)).toBe(undefined);
168
- });
169
- });
170
-
171
- describe("comprehensive path detection", () => {
172
- test("should capture 100% of code paths in test fixture", () => {
173
- const testFixture = `
174
- Implementation details:
175
- - Update \`src/cli.ts\` to add new command
176
- - Modify \`src/ui/task-viewer.ts\` for display
177
- - Check \`package.json\` for dependencies
178
- - Test with \`/absolute/path/test.js\`
179
- - Relative paths: \`./src/utils.ts\` and \`../config/settings.json\`
180
-
181
- Also review the \`README.md\` file and \`biome.json\` configuration.
182
- Windows paths like \`C:\\Users\\name\\file.txt\` should work too.
183
- `.trim();
184
-
185
- const extractedPaths = extractCodePaths(testFixture);
186
-
187
- // Verify we captured all expected paths
188
- const expectedPaths = [
189
- "src/cli.ts",
190
- "src/ui/task-viewer.ts",
191
- "package.json",
192
- "/absolute/path/test.js",
193
- "./src/utils.ts",
194
- "../config/settings.json",
195
- "README.md",
196
- "biome.json",
197
- "C:\\Users\\name\\file.txt",
198
- ];
199
-
200
- expect(extractedPaths).toEqual(expectedPaths);
201
- expect(extractedPaths.length).toBe(9);
202
- });
203
- });
204
- });
@@ -1,330 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it } from "bun:test";
2
- import { rm } from "node:fs/promises";
3
- import { join } from "node:path";
4
- import { Core } from "../core/backlog.ts";
5
- import type { Task } from "../types/index.ts";
6
-
7
- const TEST_DIR = join(process.cwd(), "test-core");
8
-
9
- describe("Core", () => {
10
- let core: Core;
11
-
12
- beforeEach(async () => {
13
- core = new Core(TEST_DIR);
14
- await core.filesystem.ensureBacklogStructure();
15
-
16
- // Initialize git repository for testing
17
- await Bun.spawn(["git", "init"], { cwd: TEST_DIR }).exited;
18
- await Bun.spawn(["git", "config", "user.name", "Test User"], { cwd: TEST_DIR }).exited;
19
- await Bun.spawn(["git", "config", "user.email", "test@example.com"], { cwd: TEST_DIR }).exited;
20
- });
21
-
22
- afterEach(async () => {
23
- try {
24
- await rm(TEST_DIR, { recursive: true, force: true });
25
- } catch {
26
- // Ignore cleanup errors
27
- }
28
- });
29
-
30
- describe("initialization", () => {
31
- it("should have filesystem and git operations available", () => {
32
- expect(core.filesystem).toBeDefined();
33
- expect(core.gitOps).toBeDefined();
34
- });
35
-
36
- it("should initialize project with default config", async () => {
37
- await core.initializeProject("Test Project");
38
-
39
- const config = await core.filesystem.loadConfig();
40
- expect(config?.projectName).toBe("Test Project");
41
- expect(config?.statuses).toEqual(["To Do", "In Progress", "Done"]);
42
- expect(config?.defaultStatus).toBe("To Do");
43
- });
44
- });
45
-
46
- describe("task operations", () => {
47
- const sampleTask: Task = {
48
- id: "task-1",
49
- title: "Test Task",
50
- status: "To Do",
51
- assignee: [],
52
- createdDate: "2025-06-07",
53
- labels: ["test"],
54
- dependencies: [],
55
- description: "This is a test task",
56
- };
57
-
58
- beforeEach(async () => {
59
- await core.initializeProject("Test Project");
60
- });
61
-
62
- it("should create task without auto-commit", async () => {
63
- await core.createTask(sampleTask, false);
64
-
65
- const loadedTask = await core.filesystem.loadTask("task-1");
66
- expect(loadedTask?.id).toBe("task-1");
67
- expect(loadedTask?.title).toBe("Test Task");
68
- });
69
-
70
- it("should create task with auto-commit", async () => {
71
- await core.createTask(sampleTask, true);
72
-
73
- // Check if task file was created
74
- const loadedTask = await core.filesystem.loadTask("task-1");
75
- expect(loadedTask?.id).toBe("task-1");
76
-
77
- // Check git status to see if there are uncommitted changes
78
- const hasChanges = await core.gitOps.hasUncommittedChanges();
79
-
80
- const lastCommit = await core.gitOps.getLastCommitMessage();
81
- // For now, just check that we have a commit (could be initialization or task)
82
- expect(lastCommit).toBeDefined();
83
- expect(lastCommit.length).toBeGreaterThan(0);
84
- });
85
-
86
- it("should update task with auto-commit", async () => {
87
- await core.createTask(sampleTask, true);
88
-
89
- // Check original task
90
- const originalTask = await core.filesystem.loadTask("task-1");
91
- expect(originalTask?.title).toBe("Test Task");
92
-
93
- const updatedTask = { ...sampleTask, title: "Updated Task" };
94
- await core.updateTask(updatedTask, true);
95
-
96
- // Check if task was updated
97
- const loadedTask = await core.filesystem.loadTask("task-1");
98
- expect(loadedTask?.title).toBe("Updated Task");
99
-
100
- const lastCommit = await core.gitOps.getLastCommitMessage();
101
- // For now, just check that we have a commit (could be initialization or task)
102
- expect(lastCommit).toBeDefined();
103
- expect(lastCommit.length).toBeGreaterThan(0);
104
- });
105
-
106
- it("should archive task with auto-commit", async () => {
107
- await core.createTask(sampleTask, true);
108
-
109
- const archived = await core.archiveTask("task-1", true);
110
- expect(archived).toBe(true);
111
-
112
- const lastCommit = await core.gitOps.getLastCommitMessage();
113
- expect(lastCommit).toContain("backlog: Archive task task-1");
114
- });
115
-
116
- it("should demote task with auto-commit", async () => {
117
- await core.createTask(sampleTask, true);
118
-
119
- const demoted = await core.demoteTask("task-1", true);
120
- expect(demoted).toBe(true);
121
-
122
- const lastCommit = await core.gitOps.getLastCommitMessage();
123
- expect(lastCommit).toContain("backlog: Demote task task-1");
124
- });
125
-
126
- it("should return false when archiving non-existent task", async () => {
127
- const archived = await core.archiveTask("non-existent", true);
128
- expect(archived).toBe(false);
129
- });
130
-
131
- it("should apply default status when task has empty status", async () => {
132
- const taskWithoutStatus: Task = {
133
- ...sampleTask,
134
- status: "",
135
- };
136
-
137
- await core.createTask(taskWithoutStatus, false);
138
-
139
- const loadedTask = await core.filesystem.loadTask("task-1");
140
- expect(loadedTask?.status).toBe("To Do"); // Should use default from config
141
- });
142
-
143
- it("should not override existing status", async () => {
144
- const taskWithStatus: Task = {
145
- ...sampleTask,
146
- status: "In Progress",
147
- };
148
-
149
- await core.createTask(taskWithStatus, false);
150
-
151
- const loadedTask = await core.filesystem.loadTask("task-1");
152
- expect(loadedTask?.status).toBe("In Progress");
153
- });
154
-
155
- it("should add description header when missing", async () => {
156
- const taskNoHeader: Task = {
157
- ...sampleTask,
158
- id: "task-2",
159
- description: "Just text",
160
- };
161
-
162
- await core.createTask(taskNoHeader, false);
163
- const loaded = await core.filesystem.loadTask("task-2");
164
- expect(loaded?.description.startsWith("## Description")).toBe(true);
165
- });
166
-
167
- it("should not duplicate description header", async () => {
168
- const taskWithHeader: Task = {
169
- ...sampleTask,
170
- id: "task-3",
171
- description: "## Description\n\nExisting",
172
- };
173
-
174
- await core.createTask(taskWithHeader, false);
175
- const loaded = await core.filesystem.loadTask("task-3");
176
- const matches = loaded?.description.match(/## Description/g) || [];
177
- expect(matches.length).toBe(1);
178
- });
179
-
180
- it("should handle task creation without auto-commit when git fails", async () => {
181
- // Create task in directory without git
182
- const nonGitCore = new Core(join(TEST_DIR, "no-git"));
183
- await nonGitCore.filesystem.ensureBacklogStructure();
184
-
185
- // This should succeed even without git
186
- await nonGitCore.createTask(sampleTask, false);
187
-
188
- const loadedTask = await nonGitCore.filesystem.loadTask("task-1");
189
- expect(loadedTask?.id).toBe("task-1");
190
- });
191
- });
192
-
193
- describe("draft operations", () => {
194
- const sampleDraft: Task = {
195
- id: "task-draft",
196
- title: "Draft Task",
197
- status: "Draft",
198
- assignee: [],
199
- createdDate: "2025-06-07",
200
- labels: [],
201
- dependencies: [],
202
- description: "Draft task",
203
- };
204
-
205
- beforeEach(async () => {
206
- await core.initializeProject("Draft Project");
207
- });
208
-
209
- it("should create draft without auto-commit", async () => {
210
- await core.createDraft(sampleDraft, false);
211
-
212
- const loaded = await core.filesystem.loadDraft("task-draft");
213
- expect(loaded?.id).toBe("task-draft");
214
- });
215
-
216
- it("should create draft with auto-commit", async () => {
217
- await core.createDraft(sampleDraft, true);
218
-
219
- const loaded = await core.filesystem.loadDraft("task-draft");
220
- expect(loaded?.id).toBe("task-draft");
221
-
222
- const lastCommit = await core.gitOps.getLastCommitMessage();
223
- expect(lastCommit).toBeDefined();
224
- expect(lastCommit.length).toBeGreaterThan(0);
225
- });
226
-
227
- it("should promote draft with auto-commit", async () => {
228
- await core.createDraft(sampleDraft, true);
229
-
230
- const promoted = await core.promoteDraft("task-draft", true);
231
- expect(promoted).toBe(true);
232
-
233
- const lastCommit = await core.gitOps.getLastCommitMessage();
234
- expect(lastCommit).toContain("backlog: Promote draft task-draft");
235
- });
236
-
237
- it("should archive draft with auto-commit", async () => {
238
- await core.createDraft(sampleDraft, true);
239
-
240
- const archived = await core.archiveDraft("task-draft", true);
241
- expect(archived).toBe(true);
242
-
243
- const lastCommit = await core.gitOps.getLastCommitMessage();
244
- expect(lastCommit).toContain("backlog: Archive draft task-draft");
245
- });
246
- });
247
-
248
- describe("integration with config", () => {
249
- it("should use custom default status from config", async () => {
250
- // Initialize with custom config
251
- await core.initializeProject("Custom Project");
252
-
253
- // Update config with custom default status
254
- const config = await core.filesystem.loadConfig();
255
- if (config) {
256
- config.defaultStatus = "Custom Status";
257
- await core.filesystem.saveConfig(config);
258
- }
259
-
260
- const taskWithoutStatus: Task = {
261
- id: "task-custom",
262
- title: "Custom Task",
263
- status: "",
264
- assignee: [],
265
- createdDate: "2025-06-07",
266
- labels: [],
267
- dependencies: [],
268
- description: "Task without status",
269
- };
270
-
271
- await core.createTask(taskWithoutStatus, false);
272
-
273
- const loadedTask = await core.filesystem.loadTask("task-custom");
274
- expect(loadedTask?.status).toBe("Custom Status");
275
- });
276
-
277
- it("should fall back to To Do when config has no default status", async () => {
278
- // Initialize project
279
- await core.initializeProject("Fallback Project");
280
-
281
- // Update config to remove default status
282
- const config = await core.filesystem.loadConfig();
283
- if (config) {
284
- config.defaultStatus = undefined;
285
- await core.filesystem.saveConfig(config);
286
- }
287
-
288
- const taskWithoutStatus: Task = {
289
- id: "task-fallback",
290
- title: "Fallback Task",
291
- status: "",
292
- assignee: [],
293
- createdDate: "2025-06-07",
294
- labels: [],
295
- dependencies: [],
296
- description: "Task without status",
297
- };
298
-
299
- await core.createTask(taskWithoutStatus, false);
300
-
301
- const loadedTask = await core.filesystem.loadTask("task-fallback");
302
- expect(loadedTask?.status).toBe("To Do");
303
- });
304
- });
305
-
306
- describe("directory accessor integration", () => {
307
- it("should use FileSystem directory accessors for git operations", async () => {
308
- await core.initializeProject("Accessor Test");
309
-
310
- const task: Task = {
311
- id: "task-accessor",
312
- title: "Accessor Test Task",
313
- status: "To Do",
314
- assignee: [],
315
- createdDate: "2025-06-07",
316
- labels: [],
317
- dependencies: [],
318
- description: "Testing directory accessors",
319
- };
320
-
321
- await core.createTask(task, true);
322
-
323
- // Verify the task file was created in the correct directory
324
- const tasksDir = core.filesystem.tasksDir;
325
- const files = await Array.fromAsync(new Bun.Glob("*.md").scan({ cwd: tasksDir }));
326
-
327
- expect(files.some((f) => f.startsWith("task-accessor"))).toBe(true);
328
- });
329
- });
330
- });