dotmd-cli 0.49.2 → 0.49.4
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/package.json +1 -1
- package/src/lifecycle.mjs +19 -13
- package/src/new.mjs +4 -2
- package/src/prompts.mjs +4 -3
package/package.json
CHANGED
package/src/lifecycle.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { extractFrontmatter, parseSimpleFrontmatter } from './frontmatter.mjs';
|
|
4
|
-
import { asString, toRepoPath, die, warn, resolveDocPath, resolveRefPath, escapeRegex, nowIso, suggestCandidates, emitFilesFooter } from './util.mjs';
|
|
4
|
+
import { asString, toRepoPath, die, warn, resolveDocPath, resolveRefPath, escapeRegex, nowIso, suggestCandidates, emitFilesFooter, isArchivedPath } from './util.mjs';
|
|
5
5
|
import { gitMv, getGitLastModified, getGitLastModifiedBatch } from './git.mjs';
|
|
6
6
|
import { buildIndex, collectDocFiles } from './index.mjs';
|
|
7
7
|
import { renderIndexFile, writeIndex } from './index-file.mjs';
|
|
@@ -169,7 +169,7 @@ export async function runStatus(argv, config, opts = {}) {
|
|
|
169
169
|
const archiveDir = path.join(fileRoot, config.archiveDir);
|
|
170
170
|
const relFromRoot = path.relative(fileRoot, filePath);
|
|
171
171
|
const relSegments = relFromRoot.split(path.sep);
|
|
172
|
-
const inArchive =
|
|
172
|
+
const inArchive = isArchivedPath(toRepoPath(filePath, config.repoRoot), config);
|
|
173
173
|
const isArchiving = config.lifecycle.archiveStatuses.has(newStatus) && !inArchive;
|
|
174
174
|
const isUnarchiving = !config.lifecycle.archiveStatuses.has(newStatus) && inArchive;
|
|
175
175
|
|
|
@@ -620,8 +620,15 @@ export function runArchive(argv, config, opts = {}) {
|
|
|
620
620
|
appendVersionHistory(filePath, `Archived (frontmatter healed in place from \`${oldStatus}\`).`);
|
|
621
621
|
if (!noIndex) regenIndex(config);
|
|
622
622
|
out.write(`${green('✓ Healed')}: ${repoPathHeal} (${oldStatus} → archived; file already under \`${config.archiveDir}/\`)\n`);
|
|
623
|
-
|
|
624
|
-
|
|
623
|
+
const touched = [repoPathHeal];
|
|
624
|
+
if (config.indexPath && !noIndex) touched.push(config.indexPath);
|
|
625
|
+
if (showFiles) emitFilesFooter(touched, config);
|
|
626
|
+
return {
|
|
627
|
+
action: 'healed',
|
|
628
|
+
oldRepoPath: repoPathHeal,
|
|
629
|
+
newRepoPath: repoPathHeal,
|
|
630
|
+
touched,
|
|
631
|
+
};
|
|
625
632
|
}
|
|
626
633
|
|
|
627
634
|
const closeoutAction = closeoutTemplate ? planCloseoutInjection(body) : null;
|
|
@@ -701,7 +708,12 @@ export function runArchive(argv, config, opts = {}) {
|
|
|
701
708
|
|
|
702
709
|
try { config.hooks.onArchive?.({ path: newRepoPath, oldStatus }, { oldPath: oldRepoPath, newPath: newRepoPath }); } catch (err) { warn(`Hook 'onArchive' threw: ${err.message}`); }
|
|
703
710
|
|
|
704
|
-
return {
|
|
711
|
+
return {
|
|
712
|
+
action: 'archived',
|
|
713
|
+
oldRepoPath,
|
|
714
|
+
newRepoPath,
|
|
715
|
+
touched,
|
|
716
|
+
};
|
|
705
717
|
}
|
|
706
718
|
|
|
707
719
|
// Unified status-transition verb. Collapses status/archive/release into one
|
|
@@ -763,9 +775,7 @@ export async function runSet(argv, config, opts = {}) {
|
|
|
763
775
|
const parsedFm = parseSimpleFrontmatter(fmRaw);
|
|
764
776
|
const oldStatus = asString(parsedFm.status);
|
|
765
777
|
|
|
766
|
-
const
|
|
767
|
-
const relFromRoot = path.relative(fileRoot, filePath);
|
|
768
|
-
const inArchive = relFromRoot.startsWith(config.archiveDir + '/') || relFromRoot.startsWith(config.archiveDir + path.sep);
|
|
778
|
+
const inArchive = isArchivedPath(toRepoPath(filePath, config.repoRoot), config);
|
|
769
779
|
|
|
770
780
|
if (config.lifecycle.archiveStatuses.has(newStatus) && !inArchive) {
|
|
771
781
|
const archiveArgs = [filePath];
|
|
@@ -807,11 +817,7 @@ export function runBulkArchive(argv, config, opts = {}) {
|
|
|
807
817
|
}
|
|
808
818
|
}
|
|
809
819
|
|
|
810
|
-
const unique = [...new Set(matched)].filter(f =>
|
|
811
|
-
const root = findFileRoot(f, config);
|
|
812
|
-
const rel = path.relative(root, f);
|
|
813
|
-
return !rel.startsWith(config.archiveDir + '/') && !rel.startsWith(config.archiveDir + path.sep);
|
|
814
|
-
});
|
|
820
|
+
const unique = [...new Set(matched)].filter(f => !isArchivedPath(toRepoPath(f, config.repoRoot), config));
|
|
815
821
|
if (unique.length === 0) die('No matching files found (already-archived files are excluded).');
|
|
816
822
|
|
|
817
823
|
process.stdout.write(`${unique.length} file(s) to archive:\n`);
|
package/src/new.mjs
CHANGED
|
@@ -366,14 +366,16 @@ export async function runNew(argv, config, opts = {}) {
|
|
|
366
366
|
else if (bodyArg !== null) {
|
|
367
367
|
bodyInput = readBodyInput(bodyArg);
|
|
368
368
|
bodyInputSource = bodyArg === '-' ? 'stdin (`-`)' : (bodyArg.startsWith('@') ? `file (\`${bodyArg}\`)` : 'inline body argument');
|
|
369
|
-
} else
|
|
369
|
+
} else {
|
|
370
370
|
// Auto-consume piped or redirected stdin so agents don't need the `-`
|
|
371
371
|
// placeholder for the most common pattern (`cat draft.md | dotmd new …`,
|
|
372
372
|
// `dotmd new … < draft.md`, or a `<<'EOF'` heredoc). We probe stdin via
|
|
373
373
|
// fstatSync rather than `!isTTY` so a closed/inherited fd doesn't trigger
|
|
374
374
|
// a blocking read of an empty stream. We accept FIFO (shell pipes), regular
|
|
375
375
|
// file (shell redirection / heredoc), and socket (Node spawnSync `input:`
|
|
376
|
-
// delivers stdin as an AF_UNIX socket).
|
|
376
|
+
// delivers stdin as an AF_UNIX socket). Probe this even for templates that
|
|
377
|
+
// don't accept bodies so the fail-fast guard below can reject accidental
|
|
378
|
+
// heredoc/input instead of silently scaffolding without it.
|
|
377
379
|
try {
|
|
378
380
|
const stat = fstatSync(0);
|
|
379
381
|
if (stat.isFIFO() || stat.isFile() || stat.isSocket()) {
|
package/src/prompts.mjs
CHANGED
|
@@ -243,7 +243,7 @@ export function consumePrompt(filePath, config, opts) {
|
|
|
243
243
|
if (docType !== 'prompt') {
|
|
244
244
|
die(`Not a prompt (type: ${docType ?? 'unknown'}): ${repoPath}`);
|
|
245
245
|
}
|
|
246
|
-
if (status === 'archived') {
|
|
246
|
+
if (status === 'archived' || isArchivedPath(repoPath, config)) {
|
|
247
247
|
die(`Already consumed: ${repoPath}`);
|
|
248
248
|
}
|
|
249
249
|
|
|
@@ -267,12 +267,13 @@ export function consumePrompt(filePath, config, opts) {
|
|
|
267
267
|
// ever being archived, and the next session sees the same prompt as pending.
|
|
268
268
|
// Body is already in memory from extractFrontmatter, so the source file
|
|
269
269
|
// can move out from under us safely.
|
|
270
|
-
runArchive([filePath], config, { noIndex, showFiles, out: process.stderr });
|
|
270
|
+
const archiveResult = runArchive([filePath], config, { noIndex, showFiles, out: process.stderr });
|
|
271
271
|
|
|
272
272
|
process.stdout.write(body);
|
|
273
273
|
if (!body.endsWith('\n')) process.stdout.write('\n');
|
|
274
274
|
|
|
275
|
-
|
|
275
|
+
const consumedPath = archiveResult?.newRepoPath ?? repoPath;
|
|
276
|
+
process.stderr.write(`${green('✓ Consumed')}: ${consumedPath}\n`);
|
|
276
277
|
}
|
|
277
278
|
|
|
278
279
|
function runPromptsArchive(argv, config, opts = {}) {
|