@backlog-md/core 0.1.0

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 (50) hide show
  1. package/README.md +114 -0
  2. package/dist/abstractions/GitAdapter.d.ts +83 -0
  3. package/dist/abstractions/GitAdapter.d.ts.map +1 -0
  4. package/dist/abstractions/GitAdapter.js +12 -0
  5. package/dist/abstractions/GitAdapter.js.map +1 -0
  6. package/dist/abstractions/index.d.ts +9 -0
  7. package/dist/abstractions/index.d.ts.map +1 -0
  8. package/dist/abstractions/index.js +8 -0
  9. package/dist/abstractions/index.js.map +1 -0
  10. package/dist/core/Core.d.ts +91 -0
  11. package/dist/core/Core.d.ts.map +1 -0
  12. package/dist/core/Core.js +170 -0
  13. package/dist/core/Core.js.map +1 -0
  14. package/dist/core/config-parser.d.ts +16 -0
  15. package/dist/core/config-parser.d.ts.map +1 -0
  16. package/dist/core/config-parser.js +166 -0
  17. package/dist/core/config-parser.js.map +1 -0
  18. package/dist/core/index.d.ts +6 -0
  19. package/dist/core/index.d.ts.map +1 -0
  20. package/dist/core/index.js +6 -0
  21. package/dist/core/index.js.map +1 -0
  22. package/dist/index.d.ts +16 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +27 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/markdown/index.d.ts +40 -0
  27. package/dist/markdown/index.d.ts.map +1 -0
  28. package/dist/markdown/index.js +230 -0
  29. package/dist/markdown/index.js.map +1 -0
  30. package/dist/test-adapters/MockGitAdapter.d.ts +73 -0
  31. package/dist/test-adapters/MockGitAdapter.d.ts.map +1 -0
  32. package/dist/test-adapters/MockGitAdapter.js +197 -0
  33. package/dist/test-adapters/MockGitAdapter.js.map +1 -0
  34. package/dist/test-adapters/index.d.ts +17 -0
  35. package/dist/test-adapters/index.d.ts.map +1 -0
  36. package/dist/test-adapters/index.js +21 -0
  37. package/dist/test-adapters/index.js.map +1 -0
  38. package/dist/types/index.d.ts +209 -0
  39. package/dist/types/index.d.ts.map +1 -0
  40. package/dist/types/index.js +11 -0
  41. package/dist/types/index.js.map +1 -0
  42. package/dist/utils/index.d.ts +5 -0
  43. package/dist/utils/index.d.ts.map +1 -0
  44. package/dist/utils/index.js +5 -0
  45. package/dist/utils/index.js.map +1 -0
  46. package/dist/utils/sorting.d.ts +21 -0
  47. package/dist/utils/sorting.d.ts.map +1 -0
  48. package/dist/utils/sorting.js +66 -0
  49. package/dist/utils/sorting.js.map +1 -0
  50. package/package.json +99 -0
package/README.md ADDED
@@ -0,0 +1,114 @@
1
+ # @backlog-md/core
2
+
3
+ Runtime-agnostic core package for Backlog.md task management.
4
+
5
+ ## Status
6
+
7
+ **Planning Phase** - This repository contains design documentation and architecture diagrams for extracting the core functionality from [Backlog.md](https://github.com/yourusername/Backlog.md) into a standalone package.
8
+
9
+ ## Goals
10
+
11
+ 1. **Runtime Independence** - Core logic works in Bun, Node.js, or browser environments
12
+ 2. **Adapter Pattern** - All I/O operations abstracted via injectable adapters
13
+ 3. **Testability** - Full test coverage using in-memory adapters
14
+ 4. **Reusability** - Use Backlog.md's task management as a library
15
+
16
+ ## Documentation
17
+
18
+ | Document | Description |
19
+ |----------|-------------|
20
+ | [Core Package Extraction Design](docs/doc-003%20-%20Core-Package-Extraction-Design.md) | Architecture and migration plan |
21
+ | [Test Adapter Specification](docs/test-adapter-specification.md) | Functionality requirements for test adapters |
22
+
23
+ ## Architecture Diagrams
24
+
25
+ Located in `.principal-views/`:
26
+
27
+ - **architecture.canvas** - Core package internal structure
28
+ - **package-boundaries.canvas** - What goes in core vs CLI
29
+ - **extraction-checklist.canvas** - Migration review items
30
+
31
+ View with [Principal View CLI](https://github.com/principal-ai/principal-view-cli):
32
+
33
+ ```bash
34
+ npx @principal-ai/principal-view-cli validate .principal-views/*.canvas
35
+ ```
36
+
37
+ ## Adapter Interfaces
38
+
39
+ The core package will expose these adapter interfaces:
40
+
41
+ ```typescript
42
+ // FileSystemAdapter - file operations
43
+ interface FileSystemAdapter {
44
+ readFile(path: string): Promise<string>
45
+ writeFile(path: string, content: string): Promise<void>
46
+ exists(path: string): Promise<boolean>
47
+ createDir(path: string, options?: { recursive?: boolean }): Promise<void>
48
+ deleteFile(path: string): Promise<void>
49
+ rename(from: string, to: string): Promise<void>
50
+ // ...
51
+ }
52
+
53
+ // GlobAdapter - pattern matching
54
+ interface GlobAdapter {
55
+ scan(pattern: string, options?: { cwd?: string }): AsyncIterable<string>
56
+ }
57
+
58
+ // GitAdapter - git operations
59
+ interface GitAdapter {
60
+ exec(args: string[], options?: { cwd?: string }): Promise<ExecResult>
61
+ isGitRepository(root: string): Promise<boolean>
62
+ initRepository(root: string): Promise<void>
63
+ }
64
+ ```
65
+
66
+ ## Usage (Planned)
67
+
68
+ ```typescript
69
+ import { Core } from "@backlog-md/core";
70
+ import { bunAdapters } from "@backlog-md/bun-adapters";
71
+
72
+ const core = new Core("/path/to/project", {
73
+ adapters: bunAdapters,
74
+ });
75
+
76
+ // Create a task
77
+ const task = await core.createTaskFromData({
78
+ title: "Implement feature",
79
+ status: "todo",
80
+ priority: "high",
81
+ });
82
+
83
+ // Query tasks
84
+ const tasks = await core.queryTasks({
85
+ filters: { status: "in-progress" },
86
+ });
87
+
88
+ // Full-text search
89
+ const searchService = await core.getSearchService();
90
+ const results = await searchService.search({ query: "authentication" });
91
+ ```
92
+
93
+ ## Testing (Planned)
94
+
95
+ ```typescript
96
+ import { Core } from "@backlog-md/core";
97
+ import { createTestAdapters } from "@backlog-md/core/test-adapters";
98
+
99
+ const adapters = createTestAdapters();
100
+ adapters.fs.seedFiles({
101
+ "/project/backlog.json": '{"projectName": "Test"}',
102
+ });
103
+ adapters.git.mockBranch("main");
104
+
105
+ const core = new Core("/project", { adapters });
106
+
107
+ // Tests run in-memory, no filesystem access
108
+ const task = await core.createTaskFromData({ title: "Test", status: "todo" });
109
+ expect(task.id).toBe("1");
110
+ ```
111
+
112
+ ## License
113
+
114
+ MIT
@@ -0,0 +1,83 @@
1
+ /**
2
+ * GitAdapter - Abstract interface for Git operations
3
+ *
4
+ * This interface abstracts away the underlying git implementation,
5
+ * allowing the same business logic to run in different environments:
6
+ * - Bun (BunGitAdapter - uses Bun.spawn)
7
+ * - Node.js (NodeGitAdapter - uses child_process)
8
+ * - Browser (IsomorphicGitAdapter - uses isomorphic-git library)
9
+ * - Tests (MockGitAdapter - configurable mock responses)
10
+ */
11
+ export interface GitExecResult {
12
+ stdout: string;
13
+ stderr: string;
14
+ exitCode: number;
15
+ }
16
+ export interface GitExecOptions {
17
+ /** Whether this is a read-only operation (can use GIT_OPTIONAL_LOCKS=0) */
18
+ readOnly?: boolean;
19
+ /** Working directory for the command */
20
+ cwd?: string;
21
+ /** Environment variables to pass */
22
+ env?: Record<string, string>;
23
+ }
24
+ /**
25
+ * Low-level git command executor
26
+ * This is the core abstraction - higher-level operations are built on top
27
+ */
28
+ export interface GitAdapter {
29
+ /**
30
+ * Execute a git command with the given arguments
31
+ * @param args - Git command arguments (e.g., ["status", "--porcelain"])
32
+ * @param options - Execution options
33
+ * @returns Command output
34
+ * @throws Error if the command fails (non-zero exit code)
35
+ */
36
+ exec(args: string[], options?: GitExecOptions): Promise<GitExecResult>;
37
+ /**
38
+ * Check if we're in a git repository
39
+ */
40
+ isGitRepository(path: string): Promise<boolean>;
41
+ /**
42
+ * Initialize a new git repository
43
+ */
44
+ initRepository(path: string): Promise<void>;
45
+ /**
46
+ * The project root this adapter operates on
47
+ */
48
+ readonly projectRoot: string;
49
+ }
50
+ /**
51
+ * High-level git operations interface
52
+ * These are convenience methods built on top of GitAdapter.exec()
53
+ * The GitOperations class implements these using a GitAdapter
54
+ */
55
+ export interface GitOperationsInterface {
56
+ addFile(filePath: string): Promise<void>;
57
+ addFiles(filePaths: string[]): Promise<void>;
58
+ stageBacklogDirectory(backlogDir?: string): Promise<void>;
59
+ stageFileMove(fromPath: string, toPath: string): Promise<void>;
60
+ commitChanges(message: string): Promise<void>;
61
+ commitStagedChanges(message: string): Promise<void>;
62
+ commitTaskChange(taskId: string, message: string): Promise<void>;
63
+ addAndCommitTaskFile(taskId: string, filePath: string, action: "create" | "update" | "archive"): Promise<void>;
64
+ resetIndex(): Promise<void>;
65
+ getStatus(): Promise<string>;
66
+ isClean(): Promise<boolean>;
67
+ hasUncommittedChanges(): Promise<boolean>;
68
+ getLastCommitMessage(): Promise<string>;
69
+ getCurrentBranch(): Promise<string>;
70
+ listLocalBranches(): Promise<string[]>;
71
+ listAllBranches(remote?: string): Promise<string[]>;
72
+ listRemoteBranches(remote?: string): Promise<string[]>;
73
+ listRecentBranches(daysAgo: number): Promise<string[]>;
74
+ listRecentRemoteBranches(daysAgo: number, remote?: string): Promise<string[]>;
75
+ hasAnyRemote(): Promise<boolean>;
76
+ hasRemote(remote?: string): Promise<boolean>;
77
+ fetch(remote?: string): Promise<void>;
78
+ listFilesInTree(ref: string, path: string): Promise<string[]>;
79
+ showFile(ref: string, filePath: string): Promise<string>;
80
+ getBranchLastModifiedMap(ref: string, dir: string, sinceDays?: number): Promise<Map<string, Date>>;
81
+ getFileLastModifiedBranch(filePath: string): Promise<string | null>;
82
+ }
83
+ //# sourceMappingURL=GitAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GitAdapter.d.ts","sourceRoot":"","sources":["../../src/abstractions/GitAdapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * GitAdapter - Abstract interface for Git operations
3
+ *
4
+ * This interface abstracts away the underlying git implementation,
5
+ * allowing the same business logic to run in different environments:
6
+ * - Bun (BunGitAdapter - uses Bun.spawn)
7
+ * - Node.js (NodeGitAdapter - uses child_process)
8
+ * - Browser (IsomorphicGitAdapter - uses isomorphic-git library)
9
+ * - Tests (MockGitAdapter - configurable mock responses)
10
+ */
11
+ export {};
12
+ //# sourceMappingURL=GitAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GitAdapter.js","sourceRoot":"","sources":["../../src/abstractions/GitAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Adapter abstractions for @backlog-md/core
3
+ *
4
+ * Re-exports common adapters from @principal-ai/repository-abstraction
5
+ * and adds backlog-specific adapters (GitAdapter).
6
+ */
7
+ export type { FileSystemAdapter, FileStats, GlobAdapter, GlobOptions, } from "@principal-ai/repository-abstraction";
8
+ export type { GitAdapter, GitExecResult, GitExecOptions, GitOperationsInterface, } from "./GitAdapter";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/abstractions/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,YAAY,EACV,iBAAiB,EACjB,SAAS,EACT,WAAW,EACX,WAAW,GACZ,MAAM,sCAAsC,CAAC;AAG9C,YAAY,EACV,UAAU,EACV,aAAa,EACb,cAAc,EACd,sBAAsB,GACvB,MAAM,cAAc,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Adapter abstractions for @backlog-md/core
3
+ *
4
+ * Re-exports common adapters from @principal-ai/repository-abstraction
5
+ * and adds backlog-specific adapters (GitAdapter).
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/abstractions/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Core - Main entry point for @backlog-md/core
3
+ *
4
+ * Provides a runtime-agnostic API for managing Backlog.md projects
5
+ * by accepting adapter implementations for I/O operations.
6
+ */
7
+ import type { FileSystemAdapter } from "@principal-ai/repository-abstraction";
8
+ import type { Task, BacklogConfig, TaskListFilter } from "../types";
9
+ /**
10
+ * Options for creating a Core instance
11
+ */
12
+ export interface CoreOptions {
13
+ /** Root directory of the project */
14
+ projectRoot: string;
15
+ /** Adapter implementations for I/O operations */
16
+ adapters: {
17
+ /** FileSystem adapter (required) */
18
+ fs: FileSystemAdapter;
19
+ };
20
+ }
21
+ /**
22
+ * Core class for Backlog.md operations
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * const core = new Core({
27
+ * projectRoot: '/path/to/project',
28
+ * adapters: { fs: new NodeFileSystemAdapter() }
29
+ * });
30
+ *
31
+ * await core.initialize();
32
+ * const tasks = core.listTasks();
33
+ * const grouped = core.getTasksByStatus();
34
+ * ```
35
+ */
36
+ export declare class Core {
37
+ private readonly projectRoot;
38
+ private readonly fs;
39
+ private config;
40
+ private tasks;
41
+ private initialized;
42
+ constructor(options: CoreOptions);
43
+ /**
44
+ * Check if projectRoot contains a valid Backlog.md project
45
+ */
46
+ isBacklogProject(): Promise<boolean>;
47
+ /**
48
+ * Initialize the Core instance
49
+ *
50
+ * Loads configuration and discovers all tasks.
51
+ * Must be called before using other methods.
52
+ */
53
+ initialize(): Promise<void>;
54
+ /**
55
+ * Get the loaded configuration
56
+ *
57
+ * @throws Error if not initialized
58
+ */
59
+ getConfig(): BacklogConfig;
60
+ /**
61
+ * List all tasks, optionally filtered
62
+ *
63
+ * @param filter - Optional filter criteria
64
+ * @returns Sorted array of tasks
65
+ */
66
+ listTasks(filter?: TaskListFilter): Task[];
67
+ /**
68
+ * Get tasks grouped by status
69
+ *
70
+ * This is the primary method for kanban-style displays.
71
+ * Returns a Map with status as key and sorted tasks as value.
72
+ * The Map preserves the order of statuses from config.
73
+ */
74
+ getTasksByStatus(): Map<string, Task[]>;
75
+ /**
76
+ * Get a single task by ID
77
+ *
78
+ * @param id - Task ID
79
+ * @returns Task or undefined if not found
80
+ */
81
+ getTask(id: string): Task | undefined;
82
+ /**
83
+ * Reload all tasks from disk
84
+ *
85
+ * Useful after external changes to task files.
86
+ */
87
+ reload(): Promise<void>;
88
+ private ensureInitialized;
89
+ private loadTasksFromDirectory;
90
+ }
91
+ //# sourceMappingURL=Core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Core.d.ts","sourceRoot":"","sources":["../../src/core/Core.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAC9E,OAAO,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAKpE;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;IAEpB,iDAAiD;IACjD,QAAQ,EAAE;QACR,oCAAoC;QACpC,EAAE,EAAE,iBAAiB,CAAC;KACvB,CAAC;CACH;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,IAAI;IACf,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAoB;IAEvC,OAAO,CAAC,MAAM,CAA8B;IAC5C,OAAO,CAAC,KAAK,CAAgC;IAC7C,OAAO,CAAC,WAAW,CAAS;gBAEhB,OAAO,EAAE,WAAW;IAKhC;;OAEG;IACG,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC;IAK1C;;;;;OAKG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IA+BjC;;;;OAIG;IACH,SAAS,IAAI,aAAa;IAK1B;;;;;OAKG;IACH,SAAS,CAAC,MAAM,CAAC,EAAE,cAAc,GAAG,IAAI,EAAE;IAyB1C;;;;;;OAMG;IACH,gBAAgB,IAAI,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC;IAMvC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS;IAKrC;;;;OAIG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ7B,OAAO,CAAC,iBAAiB;YAMX,sBAAsB;CAiCrC"}
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Core - Main entry point for @backlog-md/core
3
+ *
4
+ * Provides a runtime-agnostic API for managing Backlog.md projects
5
+ * by accepting adapter implementations for I/O operations.
6
+ */
7
+ import { parseBacklogConfig } from "./config-parser";
8
+ import { parseTaskMarkdown } from "../markdown";
9
+ import { sortTasks, groupTasksByStatus } from "../utils";
10
+ /**
11
+ * Core class for Backlog.md operations
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * const core = new Core({
16
+ * projectRoot: '/path/to/project',
17
+ * adapters: { fs: new NodeFileSystemAdapter() }
18
+ * });
19
+ *
20
+ * await core.initialize();
21
+ * const tasks = core.listTasks();
22
+ * const grouped = core.getTasksByStatus();
23
+ * ```
24
+ */
25
+ export class Core {
26
+ projectRoot;
27
+ fs;
28
+ config = null;
29
+ tasks = new Map();
30
+ initialized = false;
31
+ constructor(options) {
32
+ this.projectRoot = options.projectRoot;
33
+ this.fs = options.adapters.fs;
34
+ }
35
+ /**
36
+ * Check if projectRoot contains a valid Backlog.md project
37
+ */
38
+ async isBacklogProject() {
39
+ const configPath = this.fs.join(this.projectRoot, "backlog", "config.yml");
40
+ return this.fs.exists(configPath);
41
+ }
42
+ /**
43
+ * Initialize the Core instance
44
+ *
45
+ * Loads configuration and discovers all tasks.
46
+ * Must be called before using other methods.
47
+ */
48
+ async initialize() {
49
+ if (this.initialized)
50
+ return;
51
+ // Load config
52
+ const configPath = this.fs.join(this.projectRoot, "backlog", "config.yml");
53
+ const configExists = await this.fs.exists(configPath);
54
+ if (!configExists) {
55
+ throw new Error(`Not a Backlog.md project: config.yml not found at ${configPath}`);
56
+ }
57
+ const configContent = await this.fs.readFile(configPath);
58
+ this.config = parseBacklogConfig(configContent);
59
+ // Load tasks from tasks/ directory
60
+ const tasksDir = this.fs.join(this.projectRoot, "backlog", "tasks");
61
+ if (await this.fs.exists(tasksDir)) {
62
+ await this.loadTasksFromDirectory(tasksDir, "local");
63
+ }
64
+ // Load tasks from completed/ directory
65
+ const completedDir = this.fs.join(this.projectRoot, "backlog", "completed");
66
+ if (await this.fs.exists(completedDir)) {
67
+ await this.loadTasksFromDirectory(completedDir, "completed");
68
+ }
69
+ this.initialized = true;
70
+ }
71
+ /**
72
+ * Get the loaded configuration
73
+ *
74
+ * @throws Error if not initialized
75
+ */
76
+ getConfig() {
77
+ this.ensureInitialized();
78
+ return this.config;
79
+ }
80
+ /**
81
+ * List all tasks, optionally filtered
82
+ *
83
+ * @param filter - Optional filter criteria
84
+ * @returns Sorted array of tasks
85
+ */
86
+ listTasks(filter) {
87
+ this.ensureInitialized();
88
+ let tasks = Array.from(this.tasks.values());
89
+ if (filter?.status) {
90
+ tasks = tasks.filter((t) => t.status === filter.status);
91
+ }
92
+ if (filter?.assignee) {
93
+ tasks = tasks.filter((t) => t.assignee.includes(filter.assignee));
94
+ }
95
+ if (filter?.priority) {
96
+ tasks = tasks.filter((t) => t.priority === filter.priority);
97
+ }
98
+ if (filter?.labels && filter.labels.length > 0) {
99
+ tasks = tasks.filter((t) => filter.labels.some((label) => t.labels.includes(label)));
100
+ }
101
+ if (filter?.parentTaskId) {
102
+ tasks = tasks.filter((t) => t.parentTaskId === filter.parentTaskId);
103
+ }
104
+ return sortTasks(tasks);
105
+ }
106
+ /**
107
+ * Get tasks grouped by status
108
+ *
109
+ * This is the primary method for kanban-style displays.
110
+ * Returns a Map with status as key and sorted tasks as value.
111
+ * The Map preserves the order of statuses from config.
112
+ */
113
+ getTasksByStatus() {
114
+ this.ensureInitialized();
115
+ const tasks = Array.from(this.tasks.values());
116
+ return groupTasksByStatus(tasks, this.config.statuses);
117
+ }
118
+ /**
119
+ * Get a single task by ID
120
+ *
121
+ * @param id - Task ID
122
+ * @returns Task or undefined if not found
123
+ */
124
+ getTask(id) {
125
+ this.ensureInitialized();
126
+ return this.tasks.get(id);
127
+ }
128
+ /**
129
+ * Reload all tasks from disk
130
+ *
131
+ * Useful after external changes to task files.
132
+ */
133
+ async reload() {
134
+ this.tasks.clear();
135
+ this.initialized = false;
136
+ await this.initialize();
137
+ }
138
+ // --- Private methods ---
139
+ ensureInitialized() {
140
+ if (!this.initialized) {
141
+ throw new Error("Core not initialized. Call initialize() first.");
142
+ }
143
+ }
144
+ async loadTasksFromDirectory(dir, source) {
145
+ const entries = await this.fs.readDir(dir);
146
+ for (const entry of entries) {
147
+ const fullPath = this.fs.join(dir, entry);
148
+ // Skip directories
149
+ if (await this.fs.isDirectory(fullPath)) {
150
+ continue;
151
+ }
152
+ // Only process markdown files
153
+ if (!entry.endsWith(".md")) {
154
+ continue;
155
+ }
156
+ try {
157
+ const content = await this.fs.readFile(fullPath);
158
+ const task = parseTaskMarkdown(content, fullPath);
159
+ // Set source based on directory
160
+ task.source = source;
161
+ this.tasks.set(task.id, task);
162
+ }
163
+ catch (error) {
164
+ // Log but don't fail on individual task parse errors
165
+ console.warn(`Failed to parse task file ${fullPath}:`, error);
166
+ }
167
+ }
168
+ }
169
+ }
170
+ //# sourceMappingURL=Core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Core.js","sourceRoot":"","sources":["../../src/core/Core.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Backlog.md Config Parser
3
+ *
4
+ * Parses config.yml using a line-by-line approach matching the official Backlog.md
5
+ * implementation. This is more reliable than a generic YAML parser for this format.
6
+ */
7
+ import type { BacklogConfig } from "../types";
8
+ /**
9
+ * Parse Backlog.md config.yml content
10
+ */
11
+ export declare function parseBacklogConfig(content: string): BacklogConfig;
12
+ /**
13
+ * Serialize BacklogConfig to config.yml format
14
+ */
15
+ export declare function serializeBacklogConfig(config: BacklogConfig): string;
16
+ //# sourceMappingURL=config-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-parser.d.ts","sourceRoot":"","sources":["../../src/core/config-parser.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAI9C;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CA6GjE;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CA8DpE"}