dotmd-cli 0.29.2 → 0.29.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/lifecycle.mjs +26 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dotmd-cli",
3
- "version": "0.29.2",
3
+ "version": "0.29.3",
4
4
  "description": "CLI for managing markdown documents with YAML frontmatter — index, query, validate, graph, export, Notion sync, AI summaries.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/lifecycle.mjs CHANGED
@@ -25,6 +25,29 @@ function findFileRoot(filePath, config) {
25
25
  return roots.find(r => filePath.startsWith(r + '/')) ?? config.docsRoot;
26
26
  }
27
27
 
28
+ // Pick an archive destination that won't clobber an existing record. If
29
+ // `<dir>/<basename>` is free, returns it unchanged; otherwise appends a UTC
30
+ // timestamp (and a counter on the vanishingly rare same-second collision) so
31
+ // both the prior archive and the current one survive.
32
+ function uniqueArchiveTarget(targetDir, basename) {
33
+ const base = path.join(targetDir, basename);
34
+ if (!existsSync(base)) return base;
35
+
36
+ const ext = path.extname(basename);
37
+ const stem = basename.slice(0, -ext.length);
38
+ const d = new Date();
39
+ const pad = (n) => String(n).padStart(2, '0');
40
+ const stamp = `${d.getUTCFullYear()}${pad(d.getUTCMonth() + 1)}${pad(d.getUTCDate())}T${pad(d.getUTCHours())}${pad(d.getUTCMinutes())}${pad(d.getUTCSeconds())}Z`;
41
+
42
+ let target = path.join(targetDir, `${stem}-${stamp}${ext}`);
43
+ let n = 2;
44
+ while (existsSync(target)) {
45
+ target = path.join(targetDir, `${stem}-${stamp}-${n}${ext}`);
46
+ n++;
47
+ }
48
+ return target;
49
+ }
50
+
28
51
  export async function runStatus(argv, config, opts = {}) {
29
52
  const { dryRun } = opts;
30
53
  const input = argv[0];
@@ -85,7 +108,7 @@ export async function runStatus(argv, config, opts = {}) {
85
108
  const prefix = dim('[dry-run]');
86
109
  process.stdout.write(`${prefix} Would update frontmatter: status: ${oldStatus ?? 'unknown'} → ${newStatus}, updated: ${today}\n`);
87
110
  if (isArchiving) {
88
- const targetPath = path.join(archiveDir, path.basename(filePath));
111
+ const targetPath = uniqueArchiveTarget(archiveDir, path.basename(filePath));
89
112
  process.stdout.write(`${prefix} Would move: ${toRepoPath(filePath, config.repoRoot)} → ${toRepoPath(targetPath, config.repoRoot)}\n`);
90
113
  finalPath = targetPath;
91
114
  }
@@ -106,8 +129,7 @@ export async function runStatus(argv, config, opts = {}) {
106
129
 
107
130
  if (isArchiving) {
108
131
  mkdirSync(archiveDir, { recursive: true });
109
- const targetPath = path.join(archiveDir, path.basename(filePath));
110
- if (existsSync(targetPath)) { die(`Target already exists: ${toRepoPath(targetPath, config.repoRoot)}`); }
132
+ const targetPath = uniqueArchiveTarget(archiveDir, path.basename(filePath));
111
133
  const result = gitMv(filePath, targetPath, config.repoRoot);
112
134
  if (result.status !== 0) { die(result.stderr || 'git mv failed.'); }
113
135
  finalPath = targetPath;
@@ -479,14 +501,13 @@ export function runArchive(argv, config, opts = {}) {
479
501
 
480
502
  const today = nowIso();
481
503
  const targetDir = path.join(archiveFileRoot, config.archiveDir);
482
- const targetPath = path.join(targetDir, path.basename(filePath));
504
+ const targetPath = uniqueArchiveTarget(targetDir, path.basename(filePath));
483
505
  const oldRepoPath = toRepoPath(filePath, config.repoRoot);
484
506
  const newRepoPath = toRepoPath(targetPath, config.repoRoot);
485
507
 
486
508
  if (dryRun) {
487
509
  const prefix = dim('[dry-run]');
488
510
  out.write(`${prefix} Would update frontmatter: status: ${oldStatus} → archived, updated: ${today}\n`);
489
- if (existsSync(targetPath)) { die(`Target already exists: ${toRepoPath(targetPath, config.repoRoot)}`); }
490
511
  out.write(`${prefix} Would move: ${oldRepoPath} → ${newRepoPath}\n`);
491
512
  if (config.indexPath) out.write(`${prefix} Would regenerate index\n`);
492
513
 
@@ -502,7 +523,6 @@ export function runArchive(argv, config, opts = {}) {
502
523
  appendVersionHistory(filePath, 'Archived.');
503
524
 
504
525
  mkdirSync(targetDir, { recursive: true });
505
- if (existsSync(targetPath)) { die(`Target already exists: ${toRepoPath(targetPath, config.repoRoot)}`); }
506
526
 
507
527
  const result = gitMv(filePath, targetPath, config.repoRoot);
508
528
  if (result.status !== 0) { die(result.stderr || 'git mv failed.'); }