@md2do/cli 0.2.2 → 0.3.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 (36) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/coverage/coverage-final.json +9 -9
  3. package/coverage/index.html +7 -7
  4. package/coverage/lcov-report/index.html +7 -7
  5. package/coverage/lcov-report/src/cli.ts.html +53 -5
  6. package/coverage/lcov-report/src/commands/index.html +5 -5
  7. package/coverage/lcov-report/src/commands/index.ts.html +1 -1
  8. package/coverage/lcov-report/src/commands/list.ts.html +49 -10
  9. package/coverage/lcov-report/src/commands/stats.ts.html +1 -1
  10. package/coverage/lcov-report/src/commands/todoist.ts.html +1 -1
  11. package/coverage/lcov-report/src/formatters/index.html +1 -1
  12. package/coverage/lcov-report/src/formatters/json.ts.html +1 -1
  13. package/coverage/lcov-report/src/formatters/pretty.ts.html +1 -1
  14. package/coverage/lcov-report/src/index.html +5 -5
  15. package/coverage/lcov-report/src/index.ts.html +1 -1
  16. package/coverage/lcov-report/src/scanner.ts.html +1 -1
  17. package/coverage/lcov.info +31 -2
  18. package/coverage/src/cli.ts.html +53 -5
  19. package/coverage/src/commands/index.html +5 -5
  20. package/coverage/src/commands/index.ts.html +1 -1
  21. package/coverage/src/commands/list.ts.html +49 -10
  22. package/coverage/src/commands/stats.ts.html +1 -1
  23. package/coverage/src/commands/todoist.ts.html +1 -1
  24. package/coverage/src/formatters/index.html +1 -1
  25. package/coverage/src/formatters/json.ts.html +1 -1
  26. package/coverage/src/formatters/pretty.ts.html +1 -1
  27. package/coverage/src/index.html +5 -5
  28. package/coverage/src/index.ts.html +1 -1
  29. package/coverage/src/scanner.ts.html +1 -1
  30. package/dist/cli.js +34 -18
  31. package/dist/index.js +34 -18
  32. package/package.json +5 -5
  33. package/src/commands/list.ts +41 -12
  34. package/src/scanner.ts +6 -1
  35. package/tests/e2e/__snapshots__/warnings.test.ts.snap +104 -0
  36. package/tests/e2e/warnings.test.ts +186 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@md2do/cli",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "CLI interface for md2do task manager",
5
5
  "keywords": [
6
6
  "markdown",
@@ -40,16 +40,16 @@
40
40
  },
41
41
  "dependencies": {
42
42
  "@doist/todoist-api-typescript": "^3.0.3",
43
- "chalk": "^5.6.2",
43
+ "chalk": "^4.1.2",
44
44
  "cli-table3": "^0.6.5",
45
45
  "commander": "^11.1.0",
46
46
  "cosmiconfig": "^9.0.0",
47
47
  "date-fns": "^3.0.6",
48
48
  "fast-glob": "^3.3.3",
49
49
  "zod": "^3.22.4",
50
- "@md2do/core": "0.2.2",
51
- "@md2do/config": "0.2.2",
52
- "@md2do/todoist": "0.2.2"
50
+ "@md2do/core": "0.3.0",
51
+ "@md2do/config": "0.3.0",
52
+ "@md2do/todoist": "0.3.0"
53
53
  },
54
54
  "devDependencies": {
55
55
  "tsup": "^8.0.1"
@@ -1,5 +1,6 @@
1
1
  import { Command } from 'commander';
2
- import { filters, sorting } from '@md2do/core';
2
+ import { filters, sorting, filterWarnings } from '@md2do/core';
3
+ import { loadConfig, DEFAULT_CONFIG } from '@md2do/config';
3
4
  import { scanMarkdownFiles } from '../scanner.js';
4
5
  import { formatAsPretty, formatAsTable } from '../formatters/pretty.js';
5
6
  import { formatAsJson } from '../formatters/json.js';
@@ -25,6 +26,8 @@ interface ListCommandOptions {
25
26
  colors?: boolean;
26
27
  paths?: boolean;
27
28
  context?: boolean;
29
+ warnings?: boolean;
30
+ allWarnings?: boolean;
28
31
  }
29
32
 
30
33
  export function createListCommand(): Command {
@@ -77,6 +80,10 @@ export function createListCommand(): Command {
77
80
  .option('--no-paths', 'Hide file paths')
78
81
  .option('--context', 'Show context information (project, person, heading)')
79
82
 
83
+ // Warning options
84
+ .option('--no-warnings', 'Hide all warnings')
85
+ .option('--all-warnings', 'Show all warnings (default shows first 5)')
86
+
80
87
  .action(async (options: ListCommandOptions) => {
81
88
  try {
82
89
  // Scan markdown files
@@ -205,20 +212,42 @@ export function createListCommand(): Command {
205
212
 
206
213
  console.log(output);
207
214
 
208
- // Show warnings if any
209
- if (scanResult.warnings.length > 0) {
210
- console.error(
211
- `\n⚠️ ${scanResult.warnings.length} warnings encountered during scanning`,
215
+ // Load config and apply warning filters (unless --no-warnings overrides)
216
+ if (options.warnings !== false) {
217
+ const config = await loadConfig({
218
+ cwd: options.path || process.cwd(),
219
+ });
220
+ const warningConfig = config.warnings ?? DEFAULT_CONFIG.warnings;
221
+
222
+ // Apply config-based filtering
223
+ const filteredWarnings = filterWarnings(
224
+ scanResult.warnings,
225
+ warningConfig ?? {},
212
226
  );
213
- for (const warning of scanResult.warnings.slice(0, 5)) {
214
- console.error(
215
- ` ${warning.file}:${warning.line} - ${warning.reason}`,
216
- );
217
- }
218
- if (scanResult.warnings.length > 5) {
227
+
228
+ // Show warnings if any remain after filtering
229
+ if (filteredWarnings.length > 0) {
219
230
  console.error(
220
- ` ... and ${scanResult.warnings.length - 5} more warnings`,
231
+ `\n⚠️ ${filteredWarnings.length} warning${filteredWarnings.length > 1 ? 's' : ''} encountered during scanning`,
221
232
  );
233
+
234
+ // Show all warnings if --all-warnings, otherwise show first 5
235
+ const warningsToShow = options.allWarnings
236
+ ? filteredWarnings
237
+ : filteredWarnings.slice(0, 5);
238
+
239
+ for (const warning of warningsToShow) {
240
+ // Use message field (new) or fallback to reason (legacy)
241
+ const message =
242
+ warning.message || warning.reason || 'Unknown warning';
243
+ console.error(` ${warning.file}:${warning.line} - ${message}`);
244
+ }
245
+
246
+ if (!options.allWarnings && filteredWarnings.length > 5) {
247
+ console.error(
248
+ ` ... and ${filteredWarnings.length - 5} more warning${filteredWarnings.length - 5 > 1 ? 's' : ''} (use --all-warnings to see all)`,
249
+ );
250
+ }
222
251
  }
223
252
  }
224
253
  } catch (error) {
package/src/scanner.ts CHANGED
@@ -82,11 +82,16 @@ export async function scanMarkdownFiles(
82
82
  allWarnings.push(...result.warnings);
83
83
  } catch (error) {
84
84
  // Add warning for files that couldn't be read
85
+ const message = `Failed to read file: ${error instanceof Error ? error.message : 'Unknown error'}`;
85
86
  allWarnings.push({
87
+ severity: 'error',
88
+ source: 'md2do',
89
+ ruleId: 'file-read-error',
86
90
  file,
87
91
  line: 0,
88
92
  text: '',
89
- reason: `Failed to read file: ${error instanceof Error ? error.message : 'Unknown error'}`,
93
+ message,
94
+ reason: message,
90
95
  });
91
96
  }
92
97
  }
@@ -0,0 +1,104 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`E2E: Warning Configuration Profiles > custom: all disabled > all-disabled 1`] = `
4
+ "
5
+ Found 3 tasks
6
+ ✓ 1 completed | ○ 2 incomplete
7
+
8
+ ○ !! Valid task (2026-02-01) @alice #bug
9
+ tasks.md:6
10
+
11
+ ✓ Completed task without completion date
12
+ tasks.md:7
13
+
14
+ ○ Task without a due date
15
+ tasks.md:8
16
+
17
+ "
18
+ `;
19
+
20
+ exports[`E2E: Warning Configuration Profiles > custom: only spacing errors > custom-space-only 1`] = `
21
+ "
22
+ Found 3 tasks
23
+ ✓ 1 completed | ○ 2 incomplete
24
+
25
+ ○ !! Valid task (2026-02-01) @alice #bug
26
+ tasks.md:6
27
+
28
+ ✓ Completed task without completion date
29
+ tasks.md:7
30
+
31
+ ○ Task without a due date
32
+ tasks.md:8
33
+
34
+
35
+ ⚠️ 2 warnings encountered during scanning
36
+ tasks.md:4 - Missing space after checkbox. Use "- [x] Task" format.
37
+ tasks.md:5 - Missing space before checkbox. Use "- [x] Task" format.
38
+ "
39
+ `;
40
+
41
+ exports[`E2E: Warning Configuration Profiles > recommended profile (default) > recommended-default 1`] = `
42
+ "
43
+ Found 3 tasks
44
+ ✓ 1 completed | ○ 2 incomplete
45
+
46
+ ○ !! Valid task (2026-02-01) @alice #bug
47
+ tasks.md:6
48
+
49
+ ✓ Completed task without completion date
50
+ tasks.md:7
51
+
52
+ ○ Task without a due date
53
+ tasks.md:8
54
+
55
+
56
+ ⚠️ 3 warnings encountered during scanning
57
+ tasks.md:3 - Unsupported bullet marker (* or +). Use dash (-) for task lists.
58
+ tasks.md:4 - Missing space after checkbox. Use "- [x] Task" format.
59
+ tasks.md:5 - Missing space before checkbox. Use "- [x] Task" format.
60
+ "
61
+ `;
62
+
63
+ exports[`E2E: Warning Configuration Profiles > strict profile > strict-all-warnings 1`] = `
64
+ "
65
+ Found 3 tasks
66
+ ✓ 1 completed | ○ 2 incomplete
67
+
68
+ ○ !! Valid task (2026-02-01) @alice #bug
69
+ tasks.md:6
70
+
71
+ ✓ Completed task without completion date
72
+ tasks.md:7
73
+
74
+ ○ Task without a due date
75
+ tasks.md:8
76
+
77
+
78
+ ⚠️ 6 warnings encountered during scanning
79
+ tasks.md:3 - Unsupported bullet marker (* or +). Use dash (-) for task lists.
80
+ tasks.md:4 - Missing space after checkbox. Use "- [x] Task" format.
81
+ tasks.md:5 - Missing space before checkbox. Use "- [x] Task" format.
82
+ tasks.md:6 - Task has no due date. Add [due: YYYY-MM-DD] or place under a heading with a date.
83
+ tasks.md:7 - Completed task missing completion date. Add [completed: YYYY-MM-DD].
84
+ ... and 1 more warning (use --all-warnings to see all)
85
+ "
86
+ `;
87
+
88
+ exports[`E2E: Warning Filtering Edge Cases > respects CLI --no-warnings override > cli-no-warnings-override 1`] = `
89
+ "No tasks found
90
+ "
91
+ `;
92
+
93
+ exports[`E2E: Warning Filtering Edge Cases > shows warnings correctly with truncation > truncated-warnings 1`] = `
94
+ "No tasks found
95
+
96
+ ⚠️ 20 warnings encountered during scanning
97
+ many-warnings.md:1 - Unsupported bullet marker (* or +). Use dash (-) for task lists.
98
+ many-warnings.md:2 - Unsupported bullet marker (* or +). Use dash (-) for task lists.
99
+ many-warnings.md:3 - Unsupported bullet marker (* or +). Use dash (-) for task lists.
100
+ many-warnings.md:4 - Unsupported bullet marker (* or +). Use dash (-) for task lists.
101
+ many-warnings.md:5 - Unsupported bullet marker (* or +). Use dash (-) for task lists.
102
+ ... and 15 more warnings (use --all-warnings to see all)
103
+ "
104
+ `;
@@ -0,0 +1,186 @@
1
+ /**
2
+ * E2E Tests: Warning Configuration Profiles
3
+ *
4
+ * Tests warning filtering with different configuration profiles using snapshots.
5
+ */
6
+
7
+ import { describe, it, expect } from 'vitest';
8
+ import { execSync } from 'child_process';
9
+ import { join } from 'path';
10
+ import { mkdtempSync, writeFileSync, rmSync } from 'fs';
11
+ import { tmpdir } from 'os';
12
+
13
+ function runCLIWithConfig(
14
+ config: object,
15
+ testFiles: Record<string, string>,
16
+ ): string {
17
+ const tmpDir = mkdtempSync(join(tmpdir(), 'md2do-test-'));
18
+
19
+ try {
20
+ // Write config
21
+ writeFileSync(join(tmpDir, '.md2do.json'), JSON.stringify(config, null, 2));
22
+
23
+ // Write test files
24
+ for (const [filename, content] of Object.entries(testFiles)) {
25
+ writeFileSync(join(tmpDir, filename), content);
26
+ }
27
+
28
+ // Run CLI - need to capture both stdout and stderr since warnings go to stderr
29
+ const cliPath = join(__dirname, '../../dist/cli.js');
30
+ const output = execSync(
31
+ `node ${cliPath} list --path ${tmpDir} --no-colors 2>&1`,
32
+ {
33
+ encoding: 'utf-8',
34
+ },
35
+ );
36
+
37
+ // Normalize file paths for consistent snapshots across environments
38
+ // Replace absolute paths with relative paths
39
+ return output.replace(new RegExp('file://[^\\s]+/packages/cli/', 'g'), '');
40
+ } finally {
41
+ rmSync(tmpDir, { recursive: true });
42
+ }
43
+ }
44
+
45
+ describe('E2E: Warning Configuration Profiles', () => {
46
+ const testFiles = {
47
+ 'tasks.md': `
48
+ # Tasks
49
+
50
+ * [x] Wrong bullet type task
51
+ - [ ]Missing space after checkbox task
52
+ -[x] Missing space before checkbox task
53
+ - [ ] Valid task @alice !! #bug (2026-02-01)
54
+ - [x] Completed task without completion date
55
+ - [ ] Task without a due date
56
+ `.trim(),
57
+ };
58
+
59
+ it('recommended profile (default)', () => {
60
+ const output = runCLIWithConfig({}, testFiles);
61
+
62
+ // Format warnings shown
63
+ expect(output).toContain('⚠️');
64
+ expect(output).toContain('Unsupported bullet');
65
+ expect(output).toContain('Missing space');
66
+
67
+ // Metadata warnings NOT shown (stylistic choice)
68
+ expect(output).not.toContain('No due date');
69
+ expect(output).not.toContain('Completed without date');
70
+
71
+ expect(output).toMatchSnapshot('recommended-default');
72
+ });
73
+
74
+ it('strict profile', () => {
75
+ const output = runCLIWithConfig(
76
+ {
77
+ warnings: {
78
+ enabled: true,
79
+ rules: {
80
+ 'unsupported-bullet': 'error',
81
+ 'malformed-checkbox': 'error',
82
+ 'missing-space-after': 'error',
83
+ 'missing-space-before': 'error',
84
+ 'relative-date-no-context': 'error',
85
+ 'missing-due-date': 'warn',
86
+ 'missing-completed-date': 'warn',
87
+ 'duplicate-todoist-id': 'error',
88
+ 'file-read-error': 'error',
89
+ },
90
+ },
91
+ },
92
+ testFiles,
93
+ );
94
+
95
+ // ALL warnings shown
96
+ expect(output).toContain('⚠️');
97
+ expect(output).toContain('Unsupported bullet');
98
+ expect(output).toContain('Missing space');
99
+ expect(output).toContain('Task has no due date');
100
+ expect(output).toContain('Completed task missing completion date');
101
+
102
+ expect(output).toMatchSnapshot('strict-all-warnings');
103
+ });
104
+
105
+ it('custom: all disabled', () => {
106
+ const output = runCLIWithConfig(
107
+ {
108
+ warnings: { enabled: false },
109
+ },
110
+ testFiles,
111
+ );
112
+
113
+ expect(output).not.toContain('⚠️');
114
+ expect(output).toMatchSnapshot('all-disabled');
115
+ });
116
+
117
+ it('custom: only spacing errors', () => {
118
+ const output = runCLIWithConfig(
119
+ {
120
+ warnings: {
121
+ enabled: true,
122
+ rules: {
123
+ 'unsupported-bullet': 'off',
124
+ 'malformed-checkbox': 'off',
125
+ 'missing-space-after': 'error',
126
+ 'missing-space-before': 'error',
127
+ 'relative-date-no-context': 'off',
128
+ 'missing-due-date': 'off',
129
+ 'missing-completed-date': 'off',
130
+ 'duplicate-todoist-id': 'error',
131
+ 'file-read-error': 'error',
132
+ },
133
+ },
134
+ },
135
+ testFiles,
136
+ );
137
+
138
+ // Only space warnings
139
+ expect(output).toContain('Missing space');
140
+ expect(output).not.toContain('Unsupported bullet');
141
+ expect(output).not.toContain('Task has no due date');
142
+
143
+ expect(output).toMatchSnapshot('custom-space-only');
144
+ });
145
+ });
146
+
147
+ describe('E2E: Warning Filtering Edge Cases', () => {
148
+ it('shows warnings correctly with truncation', () => {
149
+ // Create many warnings to test truncation
150
+ const testFiles = {
151
+ 'many-warnings.md': Array.from(
152
+ { length: 20 },
153
+ (_, i) => `* [ ] Task ${i}`,
154
+ ).join('\n'),
155
+ };
156
+
157
+ const output = runCLIWithConfig({}, testFiles);
158
+
159
+ // Should show first 5, then "... and N more"
160
+ expect(output).toMatch(/and \d+ more warning/);
161
+ expect(output).toMatchSnapshot('truncated-warnings');
162
+ });
163
+
164
+ it('respects CLI --no-warnings override', () => {
165
+ const tmpDir = mkdtempSync(join(tmpdir(), 'md2do-test-'));
166
+
167
+ try {
168
+ const testFile = '* [x] Wrong bullet';
169
+ writeFileSync(join(tmpDir, 'tasks.md'), testFile);
170
+
171
+ const cliPath = join(__dirname, '../../dist/cli.js');
172
+ const output = execSync(
173
+ `node ${cliPath} list --path ${tmpDir} --no-warnings --no-colors`,
174
+ {
175
+ encoding: 'utf-8',
176
+ },
177
+ );
178
+
179
+ // Should not show warnings even though config has them enabled
180
+ expect(output).not.toContain('⚠️');
181
+ expect(output).toMatchSnapshot('cli-no-warnings-override');
182
+ } finally {
183
+ rmSync(tmpDir, { recursive: true });
184
+ }
185
+ });
186
+ });