@md2do/cli 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/README.md +29 -3
- package/dist/cli.js +466 -6
- package/dist/index.d.ts +6 -1
- package/dist/index.js +461 -0
- package/package.json +7 -4
- package/src/cli.ts +2 -0
- package/src/commands/config.ts +731 -0
- package/src/commands/index.ts +1 -0
- package/coverage/base.css +0 -224
- package/coverage/block-navigation.js +0 -87
- package/coverage/coverage-final.json +0 -10
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +0 -146
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +0 -146
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -210
- package/coverage/lcov-report/src/cli.ts.html +0 -211
- package/coverage/lcov-report/src/commands/index.html +0 -161
- package/coverage/lcov-report/src/commands/index.ts.html +0 -97
- package/coverage/lcov-report/src/commands/list.ts.html +0 -898
- package/coverage/lcov-report/src/commands/stats.ts.html +0 -1168
- package/coverage/lcov-report/src/commands/todoist.ts.html +0 -2383
- package/coverage/lcov-report/src/formatters/index.html +0 -131
- package/coverage/lcov-report/src/formatters/json.ts.html +0 -280
- package/coverage/lcov-report/src/formatters/pretty.ts.html +0 -955
- package/coverage/lcov-report/src/index.html +0 -146
- package/coverage/lcov-report/src/index.ts.html +0 -91
- package/coverage/lcov-report/src/scanner.ts.html +0 -481
- package/coverage/lcov.info +0 -2041
- package/coverage/prettify.css +0 -1
- package/coverage/prettify.js +0 -2
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +0 -210
- package/coverage/src/cli.ts.html +0 -211
- package/coverage/src/commands/index.html +0 -161
- package/coverage/src/commands/index.ts.html +0 -97
- package/coverage/src/commands/list.ts.html +0 -898
- package/coverage/src/commands/stats.ts.html +0 -1168
- package/coverage/src/commands/todoist.ts.html +0 -2383
- package/coverage/src/formatters/index.html +0 -131
- package/coverage/src/formatters/json.ts.html +0 -280
- package/coverage/src/formatters/pretty.ts.html +0 -955
- package/coverage/src/index.html +0 -146
- package/coverage/src/index.ts.html +0 -91
- package/coverage/src/scanner.ts.html +0 -481
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# @md2do/cli
|
|
2
2
|
|
|
3
|
+
## 0.5.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#47](https://github.com/TeamNickHart/md2do/pull/47) [`ce65eaf`](https://github.com/TeamNickHart/md2do/commit/ce65eaf588c702254fb564f748a5841241cc5c97) Thanks [@nickhart](https://github.com/nickhart)! - Documentation improvements and standardization
|
|
8
|
+
- Standardized bracket syntax across all docs (use space after colon: `[due: ...]`)
|
|
9
|
+
- Marked semantic/relative dates as experimental with clear warnings
|
|
10
|
+
- Clarified Todoist sync is one-way (pull only), not bidirectional
|
|
11
|
+
- Documented context extraction limitation (must run from repo root)
|
|
12
|
+
- Updated code examples in READMEs to match best practices
|
|
13
|
+
- Removed unhelpful parentheses syntax warning
|
|
14
|
+
|
|
15
|
+
- Updated dependencies [[`ce65eaf`](https://github.com/TeamNickHart/md2do/commit/ce65eaf588c702254fb564f748a5841241cc5c97)]:
|
|
16
|
+
- @md2do/core@0.5.1
|
|
17
|
+
- @md2do/config@0.5.1
|
|
18
|
+
- @md2do/todoist@0.5.1
|
|
19
|
+
|
|
20
|
+
## 0.5.0
|
|
21
|
+
|
|
22
|
+
### Minor Changes
|
|
23
|
+
|
|
24
|
+
- Add interactive config command for easy setup and configuration management
|
|
25
|
+
|
|
26
|
+
New `config` command provides interactive and programmatic configuration:
|
|
27
|
+
- `config init` - Interactive setup wizard with friendly prompts
|
|
28
|
+
- `config set/get` - Programmatically manage individual config values
|
|
29
|
+
- `config list` - View merged configuration with optional source display
|
|
30
|
+
- `config edit` - Open config file in $EDITOR
|
|
31
|
+
- `config validate` - Validate configuration against schema
|
|
32
|
+
|
|
33
|
+
Features:
|
|
34
|
+
- Interactive wizard for first-time setup (workday hours, assignee, output preferences, warnings)
|
|
35
|
+
- Non-interactive mode with CLI flags for automation
|
|
36
|
+
- Global flag support for all subcommands
|
|
37
|
+
- Multi-format support (JSON, YAML, JS)
|
|
38
|
+
- Automatic config validation
|
|
39
|
+
|
|
40
|
+
This makes initial setup much easier - just run `md2do config init`!
|
|
41
|
+
|
|
3
42
|
## 0.4.0
|
|
4
43
|
|
|
5
44
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
**Turn your markdown files into a powerful task management system.**
|
|
8
8
|
|
|
9
|
-
md2do scans your markdown notes for TODO items and gives you a CLI to filter, sort, and sync them with Todoist.
|
|
9
|
+
md2do scans your markdown notes for TODO items and gives you a CLI to filter, sort, and sync them with [Todoist](https://www.todoist.com).
|
|
10
|
+
Perfect for developers who live in plain text but want the power of a real task manager.
|
|
10
11
|
|
|
11
12
|
## ⚡ Quick Start
|
|
12
13
|
|
|
@@ -37,8 +38,8 @@ That's it! md2do will scan all `.md` files in your current directory and show yo
|
|
|
37
38
|
md2do recognizes standard markdown tasks with optional metadata:
|
|
38
39
|
|
|
39
40
|
```markdown
|
|
40
|
-
- [ ] Implement user authentication @
|
|
41
|
-
- [x] Write documentation @
|
|
41
|
+
- [ ] Implement user authentication @jane !!! #backend #auth (2026-01-20)
|
|
42
|
+
- [x] Write documentation @nick !! #docs (2026-01-15)
|
|
42
43
|
- [ ] Fix bug in parser @alex ! #bug (2026-01-18)
|
|
43
44
|
```
|
|
44
45
|
|
|
@@ -120,6 +121,31 @@ md2do list --project acme-app
|
|
|
120
121
|
md2do list --person jane
|
|
121
122
|
```
|
|
122
123
|
|
|
124
|
+
## ⚙️ Configuration
|
|
125
|
+
|
|
126
|
+
Set up md2do with an interactive wizard:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
# Interactive setup
|
|
130
|
+
md2do config init
|
|
131
|
+
|
|
132
|
+
# Or configure specific values
|
|
133
|
+
md2do config set workday.startTime "09:00"
|
|
134
|
+
md2do config set defaultAssignee "alice"
|
|
135
|
+
|
|
136
|
+
# View current config
|
|
137
|
+
md2do config list
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Quick config for common settings:**
|
|
141
|
+
|
|
142
|
+
- Work hours (for time-based due dates)
|
|
143
|
+
- Default assignee (for filtering)
|
|
144
|
+
- Output preferences (format, colors)
|
|
145
|
+
- Warning levels (strict/recommended/off)
|
|
146
|
+
|
|
147
|
+
[Full configuration guide →](https://github.com/TeamNickHart/md2do/blob/main/docs/guide/configuration.md)
|
|
148
|
+
|
|
123
149
|
## 🔄 Todoist Integration
|
|
124
150
|
|
|
125
151
|
Sync your markdown tasks with Todoist:
|
package/dist/cli.js
CHANGED
|
@@ -24,9 +24,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// src/cli.ts
|
|
27
|
-
var
|
|
28
|
-
var
|
|
29
|
-
var
|
|
27
|
+
var import_commander5 = require("commander");
|
|
28
|
+
var import_fs2 = require("fs");
|
|
29
|
+
var import_path2 = require("path");
|
|
30
30
|
|
|
31
31
|
// src/commands/list.ts
|
|
32
32
|
var import_commander = require("commander");
|
|
@@ -1202,21 +1202,481 @@ async function todoistSyncAction(options) {
|
|
|
1202
1202
|
console.log("");
|
|
1203
1203
|
}
|
|
1204
1204
|
|
|
1205
|
+
// src/commands/config.ts
|
|
1206
|
+
var import_commander4 = require("commander");
|
|
1207
|
+
var import_config3 = require("@md2do/config");
|
|
1208
|
+
var import_fs = require("fs");
|
|
1209
|
+
var import_path = require("path");
|
|
1210
|
+
var import_os = require("os");
|
|
1211
|
+
var import_child_process = require("child_process");
|
|
1212
|
+
var import_js_yaml = __toESM(require("js-yaml"));
|
|
1213
|
+
function createConfigCommand() {
|
|
1214
|
+
const command = new import_commander4.Command("config");
|
|
1215
|
+
command.description("Manage md2do configuration");
|
|
1216
|
+
command.addCommand(createConfigInitCommand());
|
|
1217
|
+
command.addCommand(createConfigSetCommand());
|
|
1218
|
+
command.addCommand(createConfigGetCommand());
|
|
1219
|
+
command.addCommand(createConfigListCommand());
|
|
1220
|
+
command.addCommand(createConfigEditCommand());
|
|
1221
|
+
command.addCommand(createConfigValidateCommand());
|
|
1222
|
+
return command;
|
|
1223
|
+
}
|
|
1224
|
+
function createConfigInitCommand() {
|
|
1225
|
+
const command = new import_commander4.Command("init");
|
|
1226
|
+
command.description("Initialize md2do configuration with interactive prompts").option("-g, --global", "Create global configuration in home directory").option(
|
|
1227
|
+
"--format <type>",
|
|
1228
|
+
"Config file format (json|yaml|js)",
|
|
1229
|
+
"json"
|
|
1230
|
+
).option("--default-assignee <username>", "Default assignee username").option("--workday-start <time>", "Work day start time (HH:MM)").option("--workday-end <time>", "Work day end time (HH:MM)").option(
|
|
1231
|
+
"--default-due-time <when>",
|
|
1232
|
+
"Default due time (start|end)",
|
|
1233
|
+
"end"
|
|
1234
|
+
).option(
|
|
1235
|
+
"--output-format <type>",
|
|
1236
|
+
"Output format (pretty|table|json)",
|
|
1237
|
+
"pretty"
|
|
1238
|
+
).option("--no-colors", "Disable colored output").option(
|
|
1239
|
+
"--warnings <level>",
|
|
1240
|
+
"Warning level (recommended|strict|off)",
|
|
1241
|
+
"recommended"
|
|
1242
|
+
).action(async (options) => {
|
|
1243
|
+
try {
|
|
1244
|
+
await configInitAction(options);
|
|
1245
|
+
} catch (error) {
|
|
1246
|
+
if (error instanceof Error && error.message === "canceled") {
|
|
1247
|
+
const p = await import("@clack/prompts");
|
|
1248
|
+
p.cancel("Configuration canceled");
|
|
1249
|
+
process.exit(0);
|
|
1250
|
+
}
|
|
1251
|
+
console.error(
|
|
1252
|
+
"Error:",
|
|
1253
|
+
error instanceof Error ? error.message : String(error)
|
|
1254
|
+
);
|
|
1255
|
+
process.exit(1);
|
|
1256
|
+
}
|
|
1257
|
+
});
|
|
1258
|
+
return command;
|
|
1259
|
+
}
|
|
1260
|
+
async function configInitAction(options) {
|
|
1261
|
+
const p = await import("@clack/prompts");
|
|
1262
|
+
p.intro("Welcome to md2do configuration!");
|
|
1263
|
+
const hasAnyOption = options.defaultAssignee !== void 0 || options.workdayStart !== void 0 || options.workdayEnd !== void 0 || options.defaultDueTime !== void 0 || options.outputFormat !== void 0 || options.colors !== void 0 || options.warnings !== void 0;
|
|
1264
|
+
const interactive = !hasAnyOption;
|
|
1265
|
+
const config = {};
|
|
1266
|
+
if (interactive) {
|
|
1267
|
+
const defaultAssignee = await p.text({
|
|
1268
|
+
message: "Your username (for filtering tasks):",
|
|
1269
|
+
placeholder: "Leave empty to skip",
|
|
1270
|
+
validate: (value) => {
|
|
1271
|
+
if (value && !/^[a-zA-Z0-9_-]+$/.test(value)) {
|
|
1272
|
+
return "Username should only contain letters, numbers, dashes, and underscores";
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
});
|
|
1276
|
+
const workdayStart = await p.text({
|
|
1277
|
+
message: "Work day start time (HH:MM):",
|
|
1278
|
+
initialValue: "08:00",
|
|
1279
|
+
validate: (value) => {
|
|
1280
|
+
if (typeof value === "string" && !/^\d{2}:\d{2}$/.test(value)) {
|
|
1281
|
+
return "Time must be in HH:MM format";
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
});
|
|
1285
|
+
const workdayEnd = await p.text({
|
|
1286
|
+
message: "Work day end time (HH:MM):",
|
|
1287
|
+
initialValue: "17:00",
|
|
1288
|
+
validate: (value) => {
|
|
1289
|
+
if (typeof value === "string" && !/^\d{2}:\d{2}$/.test(value)) {
|
|
1290
|
+
return "Time must be in HH:MM format";
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
});
|
|
1294
|
+
const defaultDueTime = await p.select({
|
|
1295
|
+
message: "Default due time:",
|
|
1296
|
+
options: [
|
|
1297
|
+
{ value: "end", label: "End of day" },
|
|
1298
|
+
{ value: "start", label: "Start of day" }
|
|
1299
|
+
],
|
|
1300
|
+
initialValue: "end"
|
|
1301
|
+
});
|
|
1302
|
+
const outputFormat = await p.select({
|
|
1303
|
+
message: "Output format:",
|
|
1304
|
+
options: [
|
|
1305
|
+
{ value: "pretty", label: "Pretty (human-readable)" },
|
|
1306
|
+
{ value: "table", label: "Table (structured)" },
|
|
1307
|
+
{ value: "json", label: "JSON (machine-readable)" }
|
|
1308
|
+
],
|
|
1309
|
+
initialValue: "pretty"
|
|
1310
|
+
});
|
|
1311
|
+
const colors = await p.confirm({
|
|
1312
|
+
message: "Enable colored output?",
|
|
1313
|
+
initialValue: true
|
|
1314
|
+
});
|
|
1315
|
+
const warnings = await p.select({
|
|
1316
|
+
message: "Warning level:",
|
|
1317
|
+
options: [
|
|
1318
|
+
{
|
|
1319
|
+
value: "recommended",
|
|
1320
|
+
label: "Recommended (validates format, metadata optional)"
|
|
1321
|
+
},
|
|
1322
|
+
{
|
|
1323
|
+
value: "strict",
|
|
1324
|
+
label: "Strict (enforces complete metadata)"
|
|
1325
|
+
},
|
|
1326
|
+
{ value: "off", label: "Off (no warnings)" }
|
|
1327
|
+
],
|
|
1328
|
+
initialValue: "recommended"
|
|
1329
|
+
});
|
|
1330
|
+
if (defaultAssignee && typeof defaultAssignee === "string") {
|
|
1331
|
+
config.defaultAssignee = defaultAssignee;
|
|
1332
|
+
}
|
|
1333
|
+
config.workday = {
|
|
1334
|
+
startTime: typeof workdayStart === "string" ? workdayStart : "08:00",
|
|
1335
|
+
endTime: typeof workdayEnd === "string" ? workdayEnd : "17:00",
|
|
1336
|
+
defaultDueTime: typeof defaultDueTime === "string" ? defaultDueTime : "end"
|
|
1337
|
+
};
|
|
1338
|
+
config.output = {
|
|
1339
|
+
format: typeof outputFormat === "string" ? outputFormat : "pretty",
|
|
1340
|
+
colors: typeof colors === "boolean" ? colors : true,
|
|
1341
|
+
paths: true
|
|
1342
|
+
};
|
|
1343
|
+
if (typeof warnings === "string" && warnings === "off") {
|
|
1344
|
+
config.warnings = { enabled: false };
|
|
1345
|
+
} else if (typeof warnings === "string" && warnings === "strict") {
|
|
1346
|
+
config.warnings = {
|
|
1347
|
+
enabled: true,
|
|
1348
|
+
rules: {
|
|
1349
|
+
"unsupported-bullet": "warn",
|
|
1350
|
+
"malformed-checkbox": "warn",
|
|
1351
|
+
"missing-space-after": "warn",
|
|
1352
|
+
"missing-space-before": "warn",
|
|
1353
|
+
"relative-date-no-context": "warn",
|
|
1354
|
+
"missing-due-date": "warn",
|
|
1355
|
+
"missing-completed-date": "warn",
|
|
1356
|
+
"duplicate-todoist-id": "error",
|
|
1357
|
+
"file-read-error": "error"
|
|
1358
|
+
}
|
|
1359
|
+
};
|
|
1360
|
+
}
|
|
1361
|
+
} else {
|
|
1362
|
+
if (options.defaultAssignee) {
|
|
1363
|
+
config.defaultAssignee = options.defaultAssignee;
|
|
1364
|
+
}
|
|
1365
|
+
config.workday = {
|
|
1366
|
+
startTime: options.workdayStart || "08:00",
|
|
1367
|
+
endTime: options.workdayEnd || "17:00",
|
|
1368
|
+
defaultDueTime: options.defaultDueTime || "end"
|
|
1369
|
+
};
|
|
1370
|
+
config.output = {
|
|
1371
|
+
format: options.outputFormat || "pretty",
|
|
1372
|
+
colors: options.colors ?? true,
|
|
1373
|
+
paths: true
|
|
1374
|
+
};
|
|
1375
|
+
if (options.warnings === "off") {
|
|
1376
|
+
config.warnings = { enabled: false };
|
|
1377
|
+
} else if (options.warnings === "strict") {
|
|
1378
|
+
config.warnings = {
|
|
1379
|
+
enabled: true,
|
|
1380
|
+
rules: {
|
|
1381
|
+
"unsupported-bullet": "warn",
|
|
1382
|
+
"malformed-checkbox": "warn",
|
|
1383
|
+
"missing-space-after": "warn",
|
|
1384
|
+
"missing-space-before": "warn",
|
|
1385
|
+
"relative-date-no-context": "warn",
|
|
1386
|
+
"missing-due-date": "warn",
|
|
1387
|
+
"missing-completed-date": "warn",
|
|
1388
|
+
"duplicate-todoist-id": "error",
|
|
1389
|
+
"file-read-error": "error"
|
|
1390
|
+
}
|
|
1391
|
+
};
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
try {
|
|
1395
|
+
(0, import_config3.validateConfig)(config);
|
|
1396
|
+
} catch (error) {
|
|
1397
|
+
p.cancel(
|
|
1398
|
+
`Invalid configuration: ${error instanceof Error ? error.message : String(error)}`
|
|
1399
|
+
);
|
|
1400
|
+
process.exit(1);
|
|
1401
|
+
}
|
|
1402
|
+
const format = options.format || "json";
|
|
1403
|
+
const configPath = getConfigPath(options.global || false, format);
|
|
1404
|
+
if ((0, import_fs.existsSync)(configPath)) {
|
|
1405
|
+
if (interactive) {
|
|
1406
|
+
const overwrite = await p.confirm({
|
|
1407
|
+
message: `Configuration file already exists at ${configPath}. Overwrite?`,
|
|
1408
|
+
initialValue: false
|
|
1409
|
+
});
|
|
1410
|
+
if (!overwrite) {
|
|
1411
|
+
p.cancel("Configuration canceled");
|
|
1412
|
+
process.exit(0);
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
writeConfigFile(configPath, config, format);
|
|
1417
|
+
p.outro(`\u2713 Configuration saved to ${configPath}`);
|
|
1418
|
+
}
|
|
1419
|
+
function createConfigSetCommand() {
|
|
1420
|
+
const command = new import_commander4.Command("set");
|
|
1421
|
+
command.description("Set a configuration value").argument("<key>", "Configuration key (e.g., workday.startTime)").argument("<value>", "Configuration value").option("-g, --global", "Set in global configuration").action((key, value, options) => {
|
|
1422
|
+
try {
|
|
1423
|
+
configSetAction(key, value, options);
|
|
1424
|
+
} catch (error) {
|
|
1425
|
+
console.error(
|
|
1426
|
+
"Error:",
|
|
1427
|
+
error instanceof Error ? error.message : String(error)
|
|
1428
|
+
);
|
|
1429
|
+
process.exit(1);
|
|
1430
|
+
}
|
|
1431
|
+
});
|
|
1432
|
+
return command;
|
|
1433
|
+
}
|
|
1434
|
+
function configSetAction(key, value, options) {
|
|
1435
|
+
const isGlobal = options.global || false;
|
|
1436
|
+
const configPath = findExistingConfigPath(isGlobal);
|
|
1437
|
+
let config = {};
|
|
1438
|
+
let format = "json";
|
|
1439
|
+
if (configPath) {
|
|
1440
|
+
format = getConfigFormat(configPath);
|
|
1441
|
+
const content = (0, import_fs.readFileSync)(configPath, "utf-8");
|
|
1442
|
+
config = parseConfigFile(content, format);
|
|
1443
|
+
} else {
|
|
1444
|
+
format = "json";
|
|
1445
|
+
}
|
|
1446
|
+
const parsedValue = parseValue(value);
|
|
1447
|
+
setNestedValue(config, key, parsedValue);
|
|
1448
|
+
try {
|
|
1449
|
+
(0, import_config3.validateConfig)(config);
|
|
1450
|
+
} catch (error) {
|
|
1451
|
+
console.error(
|
|
1452
|
+
`Invalid configuration: ${error instanceof Error ? error.message : String(error)}`
|
|
1453
|
+
);
|
|
1454
|
+
process.exit(1);
|
|
1455
|
+
}
|
|
1456
|
+
const finalPath = configPath || getConfigPath(isGlobal, format);
|
|
1457
|
+
writeConfigFile(finalPath, config, format);
|
|
1458
|
+
console.log(`\u2713 Set ${key} = ${value} in ${finalPath}`);
|
|
1459
|
+
}
|
|
1460
|
+
function createConfigGetCommand() {
|
|
1461
|
+
const command = new import_commander4.Command("get");
|
|
1462
|
+
command.description("Get a configuration value").argument("<key>", "Configuration key (e.g., workday.startTime)").option("-g, --global", "Get from global configuration only").action(async (key, options) => {
|
|
1463
|
+
try {
|
|
1464
|
+
await configGetAction(key, options);
|
|
1465
|
+
} catch (error) {
|
|
1466
|
+
console.error(
|
|
1467
|
+
"Error:",
|
|
1468
|
+
error instanceof Error ? error.message : String(error)
|
|
1469
|
+
);
|
|
1470
|
+
process.exit(1);
|
|
1471
|
+
}
|
|
1472
|
+
});
|
|
1473
|
+
return command;
|
|
1474
|
+
}
|
|
1475
|
+
async function configGetAction(key, options) {
|
|
1476
|
+
const config = options.global ? await (0, import_config3.loadConfig)({ loadGlobal: true, loadEnv: false, cwd: (0, import_os.homedir)() }) : await (0, import_config3.loadConfig)();
|
|
1477
|
+
const value = getNestedValue(config, key);
|
|
1478
|
+
if (value === void 0) {
|
|
1479
|
+
console.log("(not set)");
|
|
1480
|
+
} else if (typeof value === "object") {
|
|
1481
|
+
console.log(JSON.stringify(value, null, 2));
|
|
1482
|
+
} else {
|
|
1483
|
+
console.log(value);
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
function createConfigListCommand() {
|
|
1487
|
+
const command = new import_commander4.Command("list");
|
|
1488
|
+
command.description("Show all configuration values").option("--show-origin", "Show where each value comes from").action(async (options) => {
|
|
1489
|
+
try {
|
|
1490
|
+
await configListAction(options);
|
|
1491
|
+
} catch (error) {
|
|
1492
|
+
console.error(
|
|
1493
|
+
"Error:",
|
|
1494
|
+
error instanceof Error ? error.message : String(error)
|
|
1495
|
+
);
|
|
1496
|
+
process.exit(1);
|
|
1497
|
+
}
|
|
1498
|
+
});
|
|
1499
|
+
return command;
|
|
1500
|
+
}
|
|
1501
|
+
async function configListAction(options) {
|
|
1502
|
+
const config = await (0, import_config3.loadConfig)();
|
|
1503
|
+
console.log("");
|
|
1504
|
+
console.log("Current configuration:");
|
|
1505
|
+
console.log("");
|
|
1506
|
+
console.log(JSON.stringify(config, null, 2));
|
|
1507
|
+
console.log("");
|
|
1508
|
+
if (options.showOrigin) {
|
|
1509
|
+
console.log("Configuration sources:");
|
|
1510
|
+
console.log(" 1. Default values (built-in)");
|
|
1511
|
+
console.log(` 2. Global config (${getConfigPath(true, "json")})`);
|
|
1512
|
+
console.log(` 3. Project config (${getConfigPath(false, "json")})`);
|
|
1513
|
+
console.log(" 4. Environment variables");
|
|
1514
|
+
console.log("");
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
function createConfigEditCommand() {
|
|
1518
|
+
const command = new import_commander4.Command("edit");
|
|
1519
|
+
command.description("Open configuration file in your default editor").option("-g, --global", "Edit global configuration").action((options) => {
|
|
1520
|
+
try {
|
|
1521
|
+
configEditAction(options);
|
|
1522
|
+
} catch (error) {
|
|
1523
|
+
console.error(
|
|
1524
|
+
"Error:",
|
|
1525
|
+
error instanceof Error ? error.message : String(error)
|
|
1526
|
+
);
|
|
1527
|
+
process.exit(1);
|
|
1528
|
+
}
|
|
1529
|
+
});
|
|
1530
|
+
return command;
|
|
1531
|
+
}
|
|
1532
|
+
function configEditAction(options) {
|
|
1533
|
+
const isGlobal = options.global || false;
|
|
1534
|
+
let configPath = findExistingConfigPath(isGlobal);
|
|
1535
|
+
if (!configPath) {
|
|
1536
|
+
configPath = getConfigPath(isGlobal, "json");
|
|
1537
|
+
writeConfigFile(configPath, {}, "json");
|
|
1538
|
+
console.log(`Created new configuration file: ${configPath}`);
|
|
1539
|
+
}
|
|
1540
|
+
const editor = process.env.VISUAL || process.env.EDITOR || (process.platform === "win32" ? "notepad" : "vi");
|
|
1541
|
+
console.log(`Opening ${configPath} in ${editor}...`);
|
|
1542
|
+
try {
|
|
1543
|
+
(0, import_child_process.execSync)(`${editor} '${configPath}'`, { stdio: "inherit" });
|
|
1544
|
+
} catch (error) {
|
|
1545
|
+
console.error("Failed to open editor");
|
|
1546
|
+
console.error(`You can manually edit the file at: ${configPath}`);
|
|
1547
|
+
process.exit(1);
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
function createConfigValidateCommand() {
|
|
1551
|
+
const command = new import_commander4.Command("validate");
|
|
1552
|
+
command.description("Validate current configuration").action(async () => {
|
|
1553
|
+
try {
|
|
1554
|
+
await configValidateAction();
|
|
1555
|
+
} catch (error) {
|
|
1556
|
+
console.error(
|
|
1557
|
+
"Error:",
|
|
1558
|
+
error instanceof Error ? error.message : String(error)
|
|
1559
|
+
);
|
|
1560
|
+
process.exit(1);
|
|
1561
|
+
}
|
|
1562
|
+
});
|
|
1563
|
+
return command;
|
|
1564
|
+
}
|
|
1565
|
+
async function configValidateAction() {
|
|
1566
|
+
try {
|
|
1567
|
+
const config = await (0, import_config3.loadConfig)();
|
|
1568
|
+
(0, import_config3.validateConfig)(config);
|
|
1569
|
+
console.log("\u2713 Configuration is valid");
|
|
1570
|
+
} catch (error) {
|
|
1571
|
+
console.error(
|
|
1572
|
+
`Invalid configuration: ${error instanceof Error ? error.message : String(error)}`
|
|
1573
|
+
);
|
|
1574
|
+
process.exit(1);
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
function getConfigPath(isGlobal, format) {
|
|
1578
|
+
const base = isGlobal ? (0, import_os.homedir)() : process.cwd();
|
|
1579
|
+
const fileName = format === "json" ? ".md2do.json" : format === "yaml" ? ".md2do.yaml" : ".md2do.js";
|
|
1580
|
+
return (0, import_path.join)(base, fileName);
|
|
1581
|
+
}
|
|
1582
|
+
function findExistingConfigPath(isGlobal) {
|
|
1583
|
+
const base = isGlobal ? (0, import_os.homedir)() : process.cwd();
|
|
1584
|
+
const possibleFiles = [
|
|
1585
|
+
".md2do.json",
|
|
1586
|
+
".md2do.yaml",
|
|
1587
|
+
".md2do.yml",
|
|
1588
|
+
".md2do.js",
|
|
1589
|
+
".md2do.cjs",
|
|
1590
|
+
"md2do.config.js",
|
|
1591
|
+
"md2do.config.cjs"
|
|
1592
|
+
];
|
|
1593
|
+
for (const file of possibleFiles) {
|
|
1594
|
+
const path = (0, import_path.join)(base, file);
|
|
1595
|
+
if ((0, import_fs.existsSync)(path)) {
|
|
1596
|
+
return path;
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
return null;
|
|
1600
|
+
}
|
|
1601
|
+
function getConfigFormat(path) {
|
|
1602
|
+
if (path.endsWith(".json")) return "json";
|
|
1603
|
+
if (path.endsWith(".yaml") || path.endsWith(".yml")) return "yaml";
|
|
1604
|
+
return "js";
|
|
1605
|
+
}
|
|
1606
|
+
function parseConfigFile(content, format) {
|
|
1607
|
+
if (format === "json") {
|
|
1608
|
+
return JSON.parse(content);
|
|
1609
|
+
} else if (format === "yaml") {
|
|
1610
|
+
const loaded = import_js_yaml.default.load(content);
|
|
1611
|
+
return loaded || {};
|
|
1612
|
+
} else {
|
|
1613
|
+
return JSON.parse(content);
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
function writeConfigFile(path, config, format) {
|
|
1617
|
+
let content;
|
|
1618
|
+
if (format === "json") {
|
|
1619
|
+
content = JSON.stringify(config, null, 2) + "\n";
|
|
1620
|
+
} else if (format === "yaml") {
|
|
1621
|
+
content = import_js_yaml.default.dump(config);
|
|
1622
|
+
} else {
|
|
1623
|
+
content = `module.exports = ${JSON.stringify(config, null, 2)};
|
|
1624
|
+
`;
|
|
1625
|
+
}
|
|
1626
|
+
(0, import_fs.writeFileSync)(path, content, "utf-8");
|
|
1627
|
+
}
|
|
1628
|
+
function parseValue(value) {
|
|
1629
|
+
if (value === "true") return true;
|
|
1630
|
+
if (value === "false") return false;
|
|
1631
|
+
const num = Number(value);
|
|
1632
|
+
if (!isNaN(num)) return num;
|
|
1633
|
+
try {
|
|
1634
|
+
return JSON.parse(value);
|
|
1635
|
+
} catch {
|
|
1636
|
+
return value;
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
function getNestedValue(obj, path) {
|
|
1640
|
+
const keys = path.split(".");
|
|
1641
|
+
let current = obj;
|
|
1642
|
+
for (const key of keys) {
|
|
1643
|
+
if (current === null || current === void 0 || typeof current !== "object") {
|
|
1644
|
+
return void 0;
|
|
1645
|
+
}
|
|
1646
|
+
current = current[key];
|
|
1647
|
+
}
|
|
1648
|
+
return current;
|
|
1649
|
+
}
|
|
1650
|
+
function setNestedValue(obj, path, value) {
|
|
1651
|
+
const keys = path.split(".");
|
|
1652
|
+
const lastKey = keys.pop();
|
|
1653
|
+
if (!lastKey) return;
|
|
1654
|
+
let current = obj;
|
|
1655
|
+
for (const key of keys) {
|
|
1656
|
+
if (!(key in current) || typeof current[key] !== "object") {
|
|
1657
|
+
current[key] = {};
|
|
1658
|
+
}
|
|
1659
|
+
current = current[key];
|
|
1660
|
+
}
|
|
1661
|
+
current[lastKey] = value;
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1205
1664
|
// src/cli.ts
|
|
1206
1665
|
var getVersion = () => {
|
|
1207
1666
|
try {
|
|
1208
|
-
const packagePath = (0,
|
|
1209
|
-
const packageJson = JSON.parse((0,
|
|
1667
|
+
const packagePath = (0, import_path2.join)(__dirname, "../package.json");
|
|
1668
|
+
const packageJson = JSON.parse((0, import_fs2.readFileSync)(packagePath, "utf-8"));
|
|
1210
1669
|
return packageJson.version;
|
|
1211
1670
|
} catch {
|
|
1212
1671
|
return "0.0.0";
|
|
1213
1672
|
}
|
|
1214
1673
|
};
|
|
1215
|
-
var program = new
|
|
1674
|
+
var program = new import_commander5.Command();
|
|
1216
1675
|
program.name("md2do").description("Scan and manage TODOs in markdown files").version(getVersion());
|
|
1217
1676
|
program.addCommand(createListCommand());
|
|
1218
1677
|
program.addCommand(createStatsCommand());
|
|
1219
1678
|
program.addCommand(createTodoistCommand());
|
|
1679
|
+
program.addCommand(createConfigCommand());
|
|
1220
1680
|
if (process.argv.length === 2) {
|
|
1221
1681
|
program.help();
|
|
1222
1682
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -9,4 +9,9 @@ declare function createStatsCommand(): Command;
|
|
|
9
9
|
*/
|
|
10
10
|
declare function createTodoistCommand(): Command;
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Create the config command group
|
|
14
|
+
*/
|
|
15
|
+
declare function createConfigCommand(): Command;
|
|
16
|
+
|
|
17
|
+
export { createConfigCommand, createListCommand, createStatsCommand, createTodoistCommand };
|