@diagrammo/dgmo 0.2.26 → 0.2.27

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": "@diagrammo/dgmo",
3
- "version": "0.2.26",
3
+ "version": "0.2.27",
4
4
  "description": "DGMO diagram markup language — parser, renderer, and color system",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/cli.ts CHANGED
@@ -249,6 +249,11 @@ async function main(): Promise<void> {
249
249
  noInput();
250
250
  }
251
251
 
252
+ // Strip any ANSI escape codes that may have leaked into input
253
+ // (e.g. from shell aliases like cat=bat with --color always)
254
+ // eslint-disable-next-line no-control-regex
255
+ content = content.replace(/\x1b\[[0-9;]*m/g, '');
256
+
252
257
  // Resolve org chart imports (tags: and import: directives)
253
258
  if (opts.input && parseDgmoChartType(content) === 'org') {
254
259
  const inputPath = resolve(opts.input);
@@ -26,6 +26,7 @@ export function looksLikeInitiativeStatus(content: string): boolean {
26
26
  const lines = content.split('\n');
27
27
  let hasArrow = false;
28
28
  let hasStatus = false;
29
+ let hasIndentedArrow = false;
29
30
  for (const line of lines) {
30
31
  const trimmed = line.trim();
31
32
  if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith('//')) continue;
@@ -33,9 +34,12 @@ export function looksLikeInitiativeStatus(content: string): boolean {
33
34
  if (trimmed.match(/^title\s*:/i)) continue;
34
35
  if (trimmed.includes('->')) hasArrow = true;
35
36
  if (/\|\s*(done|wip|todo|na)\s*$/i.test(trimmed)) hasStatus = true;
37
+ // Indented arrow is a strong signal — only initiative-status uses this
38
+ const isIndented = line.length > 0 && line !== trimmed && /^\s/.test(line);
39
+ if (isIndented && trimmed.startsWith('->')) hasIndentedArrow = true;
36
40
  if (hasArrow && hasStatus) return true;
37
41
  }
38
- return false;
42
+ return hasIndentedArrow;
39
43
  }
40
44
 
41
45
  // ============================================================
@@ -44,7 +48,7 @@ export function looksLikeInitiativeStatus(content: string): boolean {
44
48
 
45
49
  function parseStatus(raw: string, line: number, diagnostics: DgmoError[]): InitiativeStatus {
46
50
  const trimmed = raw.trim().toLowerCase();
47
- if (!trimmed) return null;
51
+ if (!trimmed) return 'na';
48
52
  if (VALID_STATUSES.includes(trimmed)) return trimmed as InitiativeStatus;
49
53
 
50
54
  // Unknown status — emit warning with suggestion
@@ -70,6 +74,7 @@ export function parseInitiativeStatus(content: string): ParsedInitiativeStatus {
70
74
  const lines = content.split('\n');
71
75
  const nodeLabels = new Set<string>();
72
76
  let currentGroup: ISGroup | null = null;
77
+ let lastNodeLabel: string | null = null;
73
78
 
74
79
  for (let i = 0; i < lines.length; i++) {
75
80
  const lineNum = i + 1; // 1-based
@@ -120,7 +125,18 @@ export function parseInitiativeStatus(content: string): ParsedInitiativeStatus {
120
125
 
121
126
  // Edge: contains `->`
122
127
  if (trimmed.includes('->')) {
123
- const edge = parseEdgeLine(trimmed, lineNum, result.diagnostics);
128
+ let edgeText = trimmed;
129
+ // Indented `-> Target` shorthand — prepend the last node label as source
130
+ if (trimmed.startsWith('->')) {
131
+ if (!lastNodeLabel) {
132
+ result.diagnostics.push(
133
+ makeDgmoError(lineNum, 'Indented edge has no preceding node to use as source', 'warning')
134
+ );
135
+ continue;
136
+ }
137
+ edgeText = `${lastNodeLabel} ${trimmed}`;
138
+ }
139
+ const edge = parseEdgeLine(edgeText, lineNum, result.diagnostics);
124
140
  if (edge) result.edges.push(edge);
125
141
  continue;
126
142
  }
@@ -128,6 +144,7 @@ export function parseInitiativeStatus(content: string): ParsedInitiativeStatus {
128
144
  // Node: everything else
129
145
  const node = parseNodeLine(trimmed, lineNum, result.diagnostics);
130
146
  if (node) {
147
+ lastNodeLabel = node.label;
131
148
  if (nodeLabels.has(node.label)) {
132
149
  result.diagnostics.push(
133
150
  makeDgmoError(lineNum, `Duplicate node "${node.label}"`, 'warning')
@@ -156,7 +173,7 @@ export function parseInitiativeStatus(content: string): ParsedInitiativeStatus {
156
173
  );
157
174
  // Auto-create an implicit node
158
175
  if (!result.nodes.some((n) => n.label === edge.source)) {
159
- result.nodes.push({ label: edge.source, status: null, shape: inferParticipantType(edge.source), lineNumber: edge.lineNumber });
176
+ result.nodes.push({ label: edge.source, status: 'na', shape: inferParticipantType(edge.source), lineNumber: edge.lineNumber });
160
177
  nodeLabels.add(edge.source);
161
178
  }
162
179
  }
@@ -165,7 +182,7 @@ export function parseInitiativeStatus(content: string): ParsedInitiativeStatus {
165
182
  makeDgmoError(edge.lineNumber, `Edge target "${edge.target}" is not a declared node`, 'warning')
166
183
  );
167
184
  if (!result.nodes.some((n) => n.label === edge.target)) {
168
- result.nodes.push({ label: edge.target, status: null, shape: inferParticipantType(edge.target), lineNumber: edge.lineNumber });
185
+ result.nodes.push({ label: edge.target, status: 'na', shape: inferParticipantType(edge.target), lineNumber: edge.lineNumber });
169
186
  nodeLabels.add(edge.target);
170
187
  }
171
188
  }
@@ -193,7 +210,7 @@ function parseNodeLine(
193
210
  const status = parseStatus(statusRaw, lineNum, diagnostics);
194
211
  return { label, status, shape: inferParticipantType(label), lineNumber: lineNum };
195
212
  }
196
- return { label: trimmed, status: null, shape: inferParticipantType(trimmed), lineNumber: lineNum };
213
+ return { label: trimmed, status: 'na', shape: inferParticipantType(trimmed), lineNumber: lineNum };
197
214
  }
198
215
 
199
216
  function parseEdgeLine(
@@ -218,7 +235,7 @@ function parseEdgeLine(
218
235
  }
219
236
 
220
237
  // Extract status from end (after last |)
221
- let status: InitiativeStatus = null;
238
+ let status: InitiativeStatus = 'na';
222
239
  const lastPipe = rest.lastIndexOf('|');
223
240
  if (lastPipe >= 0) {
224
241
  const statusRaw = rest.slice(lastPipe + 1).trim();