dotmd-cli 0.39.5 → 0.39.6

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dotmd-cli",
3
- "version": "0.39.5",
3
+ "version": "0.39.6",
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/index.mjs CHANGED
@@ -74,6 +74,18 @@ export function buildIndex(config, opts = {}) {
74
74
  }
75
75
  }
76
76
 
77
+ // Per-type counts (F6): same input docs, keyed by `type` first so callers
78
+ // can distinguish `plan/partial` (work shipped + tail deferred) from
79
+ // `doc/partial` (incomplete reference material). Untyped docs (pre-0.30
80
+ // corpora) land under `unknown` rather than getting dropped silently.
81
+ const countsByType = {};
82
+ for (const doc of transformedDocs) {
83
+ if (!doc.status) continue;
84
+ const type = doc.type || 'unknown';
85
+ if (!countsByType[type]) countsByType[type] = {};
86
+ countsByType[type][doc.status] = (countsByType[type][doc.status] ?? 0) + 1;
87
+ }
88
+
77
89
  if (!fast) {
78
90
  if (config.indexPath) {
79
91
  const indexCheck = checkIndex(transformedDocs, config);
@@ -95,6 +107,7 @@ export function buildIndex(config, opts = {}) {
95
107
  generatedAt: new Date().toISOString(),
96
108
  docs: transformedDocs,
97
109
  countsByStatus,
110
+ countsByType,
98
111
  warnings,
99
112
  errors,
100
113
  };
package/src/stats.mjs CHANGED
@@ -46,6 +46,7 @@ export function buildStats(index, config) {
46
46
  generatedAt: new Date().toISOString(),
47
47
  totalDocs: docs.length,
48
48
  countsByStatus: index.countsByStatus,
49
+ countsByType: index.countsByType ?? {},
49
50
  health: {
50
51
  staleCount,
51
52
  stalePct: pct(staleCount, nonArchived.length),
@@ -97,16 +98,45 @@ function _renderStats(stats, config) {
97
98
  lines.push(bold(`Stats`) + dim(` — ${stats.totalDocs} docs`));
98
99
  lines.push('');
99
100
 
100
- // Status
101
+ // Status. With F6, render one line per type when 2+ types have docs so
102
+ // `plan/partial` and `doc/partial` (semantically distinct under per-type
103
+ // taxonomies) don't collapse into one number. Single-type corpora keep the
104
+ // existing flat line — no needless `Plans:` header on a plans-only repo.
101
105
  lines.push(bold('Status'));
102
- const allStatuses = [
103
- ...config.statusOrder.filter(s => stats.countsByStatus[s]),
104
- ...Object.keys(stats.countsByStatus).filter(s => !config.statusOrder.includes(s)).sort(),
105
- ];
106
- const statusParts = allStatuses
107
- .filter(s => stats.countsByStatus[s])
108
- .map(s => `${s}: ${stats.countsByStatus[s]}`);
109
- lines.push(' ' + statusParts.join(' '));
106
+ const typesWithDocs = Object.entries(stats.countsByType ?? {})
107
+ .filter(([, statusMap]) => Object.values(statusMap).some(n => n > 0));
108
+ if (typesWithDocs.length > 1) {
109
+ // Type order: respect config.validTypes declaration order; unknown last.
110
+ const typeOrder = [...(config.validTypes ?? [])];
111
+ const sortedTypes = typesWithDocs.sort(([a], [b]) => {
112
+ const ai = typeOrder.indexOf(a);
113
+ const bi = typeOrder.indexOf(b);
114
+ if (ai === -1 && bi === -1) return a.localeCompare(b);
115
+ if (ai === -1) return 1;
116
+ if (bi === -1) return -1;
117
+ return ai - bi;
118
+ });
119
+ const labelFor = (t) => t === 'unknown' ? 'Untyped' : (t.charAt(0).toUpperCase() + t.slice(1) + 's');
120
+ for (const [type, statusMap] of sortedTypes) {
121
+ const statusesForType = [
122
+ ...config.statusOrder.filter(s => statusMap[s]),
123
+ ...Object.keys(statusMap).filter(s => !config.statusOrder.includes(s)).sort(),
124
+ ];
125
+ const parts = statusesForType
126
+ .filter(s => statusMap[s])
127
+ .map(s => `${s}: ${statusMap[s]}`);
128
+ lines.push(` ${dim(labelFor(type) + ':')} ${parts.join(' ')}`);
129
+ }
130
+ } else {
131
+ const allStatuses = [
132
+ ...config.statusOrder.filter(s => stats.countsByStatus[s]),
133
+ ...Object.keys(stats.countsByStatus).filter(s => !config.statusOrder.includes(s)).sort(),
134
+ ];
135
+ const statusParts = allStatuses
136
+ .filter(s => stats.countsByStatus[s])
137
+ .map(s => `${s}: ${stats.countsByStatus[s]}`);
138
+ lines.push(' ' + statusParts.join(' '));
139
+ }
110
140
  lines.push('');
111
141
 
112
142
  // Health