@smartmemory/compose 0.2.21-beta → 0.2.22-beta
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/lib/feature-validator.js
CHANGED
|
@@ -32,7 +32,7 @@ import fs from 'node:fs';
|
|
|
32
32
|
import path from 'node:path';
|
|
33
33
|
import { fileURLToPath } from 'node:url';
|
|
34
34
|
import { FEATURE_CODE_RE_STRICT, validateCode } from './feature-code.js';
|
|
35
|
-
import { parseRoadmap } from './roadmap-parser.js';
|
|
35
|
+
import { parseRoadmap, splitRoadmapCells } from './roadmap-parser.js';
|
|
36
36
|
import { listFeatures, readFeature } from './feature-json.js';
|
|
37
37
|
import { loadExternalPrefixes } from './project-paths.js';
|
|
38
38
|
import { checkRoundtrip, LOSSY_LABELS } from './roadmap-roundtrip.js';
|
|
@@ -161,7 +161,9 @@ export function loadValidationContext(cwd, options = {}) {
|
|
|
161
161
|
const rowMatch = rawLine.match(/^\|(.+)\|\s*$/);
|
|
162
162
|
if (!rowMatch) { inTable = false; sawSeparator = false; continue; }
|
|
163
163
|
|
|
164
|
-
|
|
164
|
+
// Escaped-pipe-aware split (COMP-MCP-VALIDATE-4): a `\|` in a description
|
|
165
|
+
// cell must not shift status-column detection and read prose as the status.
|
|
166
|
+
const cols = splitRoadmapCells(rawLine);
|
|
165
167
|
|
|
166
168
|
// Detect header row by column names. Recognize common column-name variants
|
|
167
169
|
// (feature/code/item/name) and (status/state) so non-canonical tables that
|
|
@@ -19,6 +19,7 @@ import { join, resolve, dirname } from 'path';
|
|
|
19
19
|
import { fileURLToPath } from 'url';
|
|
20
20
|
import { SchemaValidator } from '../server/schema-validator.js';
|
|
21
21
|
import { FEATURE_CODE_RE_STRICT } from './feature-code.js';
|
|
22
|
+
import { splitRoadmapCells } from './roadmap-parser.js';
|
|
22
23
|
|
|
23
24
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
24
25
|
const SCHEMA_PATH = resolve(__dirname, '../contracts/feature-json.schema.json');
|
|
@@ -108,7 +109,9 @@ export function scanRoadmapRows(roadmapPath) {
|
|
|
108
109
|
if (/^##\s+/.test(rawLine)) { inTable = false; sawSeparator = false; codeIdx = statusIdx = -1; continue; }
|
|
109
110
|
const rowMatch = rawLine.match(/^\|(.+)\|\s*$/);
|
|
110
111
|
if (!rowMatch) { inTable = false; sawSeparator = false; continue; }
|
|
111
|
-
|
|
112
|
+
// Escaped-pipe-aware split (COMP-MCP-VALIDATE-4): keep column detection stable
|
|
113
|
+
// when a description cell contains `\|`.
|
|
114
|
+
const cols = splitRoadmapCells(rawLine);
|
|
112
115
|
const lower = cols.map((c) => c.toLowerCase());
|
|
113
116
|
const featureColIdx = lower.findIndex((c) => ['feature', 'code', 'item', 'name'].includes(c));
|
|
114
117
|
const statusColIdx = lower.findIndex((c) => ['status', 'state'].includes(c));
|
package/lib/roadmap-parser.js
CHANGED
|
@@ -21,6 +21,24 @@ const MILESTONE_HEADING_RE = /^###\s+(.+?)(?:\s*:\s*(.+))?$/;
|
|
|
21
21
|
const TABLE_ROW_RE = /^\|(.+)\|$/;
|
|
22
22
|
const FENCE_RE = /^```/;
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Split a markdown table row into trimmed cells, honoring escaped pipes.
|
|
26
|
+
* Splits on UNESCAPED `|` only (`/(?<!\\)\|/`), drops the leading/trailing empty
|
|
27
|
+
* cells from the outer pipes, and unescapes `\|` → `|` in each cell. Symmetric
|
|
28
|
+
* with `escCell()` in roadmap-gen.js. For pipe-free rows this is identical to a
|
|
29
|
+
* naive `split('|')`.
|
|
30
|
+
*
|
|
31
|
+
* The canonical row splitter — every ROADMAP-row parse site (validator,
|
|
32
|
+
* write-guard, this parser) must use it so a `\|` in a description cell can never
|
|
33
|
+
* shift status-column detection (COMP-MCP-VALIDATE-4).
|
|
34
|
+
*
|
|
35
|
+
* @param {string} rawLine - A full table row line, e.g. "| a | b \\| c | PLANNED |"
|
|
36
|
+
* @returns {string[]} trimmed, unescaped cells
|
|
37
|
+
*/
|
|
38
|
+
export function splitRoadmapCells(rawLine) {
|
|
39
|
+
return rawLine.trim().split(/(?<!\\)\|/).slice(1, -1).map((c) => c.trim().replace(/\\\|/g, '|'));
|
|
40
|
+
}
|
|
41
|
+
|
|
24
42
|
/**
|
|
25
43
|
* @typedef {{ code: string, description: string, status: string, phaseId: string, position: number }} FeatureEntry
|
|
26
44
|
*/
|
|
@@ -112,10 +130,8 @@ export function parseRoadmap(text) {
|
|
|
112
130
|
continue;
|
|
113
131
|
}
|
|
114
132
|
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
// identical to split('|') (no backslashes, no lookbehind matches).
|
|
118
|
-
const cells = trimmed.split(/(?<!\\)\|/).slice(1, -1).map(c => c.trim().replace(/\\\|/g, '|'));
|
|
133
|
+
// Escaped-pipe-aware cell split (the canonical splitter — see splitRoadmapCells).
|
|
134
|
+
const cells = splitRoadmapCells(trimmed);
|
|
119
135
|
|
|
120
136
|
// Skip separator rows (|---|---|---|)
|
|
121
137
|
if (cells.every(c => /^[-:]+$/.test(c))) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smartmemory/compose",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.22-beta",
|
|
4
4
|
"description": "Structured AI dev pipeline — goal-to-product orchestration with gates, iteration loops, and feature lifecycle management.",
|
|
5
5
|
"author": "SmartMemory",
|
|
6
6
|
"license": "MIT",
|