@pennyfarthing/shared 7.4.0 → 7.9.0
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/dist/generate-skill-docs.test.js +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/marker/constants.d.ts +37 -0
- package/dist/marker/constants.d.ts.map +1 -0
- package/dist/marker/constants.js +43 -0
- package/dist/marker/constants.js.map +1 -0
- package/dist/marker/detect.d.ts +25 -0
- package/dist/marker/detect.d.ts.map +1 -0
- package/dist/marker/detect.js +53 -0
- package/dist/marker/detect.js.map +1 -0
- package/dist/marker/detect.test.d.ts +10 -0
- package/dist/marker/detect.test.d.ts.map +1 -0
- package/dist/marker/detect.test.js +417 -0
- package/dist/marker/detect.test.js.map +1 -0
- package/dist/marker/index.d.ts +14 -0
- package/dist/marker/index.d.ts.map +1 -0
- package/dist/marker/index.js +15 -0
- package/dist/marker/index.js.map +1 -0
- package/dist/marker/strip.d.ts +26 -0
- package/dist/marker/strip.d.ts.map +1 -0
- package/dist/marker/strip.js +39 -0
- package/dist/marker/strip.js.map +1 -0
- package/dist/marker/types.d.ts +23 -0
- package/dist/marker/types.d.ts.map +1 -0
- package/dist/marker/types.js +7 -0
- package/dist/marker/types.js.map +1 -0
- package/dist/migrate-theme-schema.test.d.ts +8 -0
- package/dist/migrate-theme-schema.test.d.ts.map +1 -0
- package/dist/migrate-theme-schema.test.js +67 -0
- package/dist/migrate-theme-schema.test.js.map +1 -0
- package/dist/skill-search.test.js +1 -1
- package/dist/theme-loader.d.ts +1 -1
- package/dist/theme-loader.d.ts.map +1 -1
- package/dist/theme-loader.js +2 -88
- package/dist/theme-loader.js.map +1 -1
- package/dist/theme-loader.test.d.ts +6 -0
- package/dist/theme-loader.test.d.ts.map +1 -1
- package/dist/theme-loader.test.js +52 -62
- package/dist/theme-loader.test.js.map +1 -1
- package/package.json +4 -1
|
@@ -64,12 +64,12 @@ describe('Story 9-4: Skill Documentation Generator', () => {
|
|
|
64
64
|
assert.ok(result.content.startsWith('#'), 'Should start with a heading');
|
|
65
65
|
assert.ok(result.content.includes('##'), 'Should have section headings');
|
|
66
66
|
});
|
|
67
|
-
it('should include all
|
|
67
|
+
it('should include all 22 skills from registry', async () => {
|
|
68
68
|
// AC1: All skills from registry should appear in output
|
|
69
69
|
const result = await generateSkillDocs({
|
|
70
70
|
registryPath: REGISTRY_PATH,
|
|
71
71
|
});
|
|
72
|
-
assert.ok(result.skillCount ===
|
|
72
|
+
assert.ok(result.skillCount === 22, `Should include all 22 skills, got ${result.skillCount}`);
|
|
73
73
|
// Check for a sample of known skills
|
|
74
74
|
const expectedSkills = ['testing', 'jira', 'code-review', 'changelog', 'theme'];
|
|
75
75
|
for (const skill of expectedSkills) {
|
package/dist/index.d.ts
CHANGED
|
@@ -7,4 +7,5 @@ export { loadTheme, listThemes, getAgentPersona, type Theme, type ThemeAgent, }
|
|
|
7
7
|
export { searchSkills, type SearchOptions, type SkillResult, } from './skill-search.js';
|
|
8
8
|
export { suggestSkills, suggestFromSession, suggestFromKeywords, type SuggestOptions, type SkillSuggestion, type SessionContext, } from './skill-suggest.js';
|
|
9
9
|
export { generateSkillDocs, type GeneratorOptions, type GeneratorResult, } from './generate-skill-docs.js';
|
|
10
|
+
export { detectMarkers, stripMarkers, stripCodeBlocks, MARKER_PATTERN, MARKER_TYPES, VALID_MARKER_TYPES, type Marker, type MarkerType, } from './marker/index.js';
|
|
10
11
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EACnB,gBAAgB,EAChB,KAAK,aAAa,GACnB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,SAAS,EACT,UAAU,EACV,eAAe,EACf,KAAK,KAAK,EACV,KAAK,UAAU,GAChB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,YAAY,EACZ,KAAK,aAAa,EAClB,KAAK,WAAW,GACjB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,cAAc,GACpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,iBAAiB,EACjB,KAAK,gBAAgB,EACrB,KAAK,eAAe,GACrB,MAAM,0BAA0B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EACnB,gBAAgB,EAChB,KAAK,aAAa,GACnB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,SAAS,EACT,UAAU,EACV,eAAe,EACf,KAAK,KAAK,EACV,KAAK,UAAU,GAChB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,YAAY,EACZ,KAAK,aAAa,EAClB,KAAK,WAAW,GACjB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,cAAc,GACpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,iBAAiB,EACjB,KAAK,gBAAgB,EACrB,KAAK,eAAe,GACrB,MAAM,0BAA0B,CAAC;AAIlC,OAAO,EACL,aAAa,EACb,YAAY,EACZ,eAAe,EACf,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,KAAK,MAAM,EACX,KAAK,UAAU,GAChB,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -7,4 +7,7 @@ export { loadTheme, listThemes, getAgentPersona, } from './theme-loader.js';
|
|
|
7
7
|
export { searchSkills, } from './skill-search.js';
|
|
8
8
|
export { suggestSkills, suggestFromSession, suggestFromKeywords, } from './skill-suggest.js';
|
|
9
9
|
export { generateSkillDocs, } from './generate-skill-docs.js';
|
|
10
|
+
// Marker module - Reflector protocol marker detection
|
|
11
|
+
// @see docs/adr/0011-reflector-marker-consolidation.md
|
|
12
|
+
export { detectMarkers, stripMarkers, stripCodeBlocks, MARKER_PATTERN, MARKER_TYPES, VALID_MARKER_TYPES, } from './marker/index.js';
|
|
10
13
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EACnB,gBAAgB,GAEjB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,SAAS,EACT,UAAU,EACV,eAAe,GAGhB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,YAAY,GAGb,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,mBAAmB,GAIpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,iBAAiB,GAGlB,MAAM,0BAA0B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EACnB,gBAAgB,GAEjB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,SAAS,EACT,UAAU,EACV,eAAe,GAGhB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,YAAY,GAGb,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,mBAAmB,GAIpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,iBAAiB,GAGlB,MAAM,0BAA0B,CAAC;AAElC,sDAAsD;AACtD,uDAAuD;AACvD,OAAO,EACL,aAAa,EACb,YAAY,EACZ,eAAe,EACf,cAAc,EACd,YAAY,EACZ,kBAAkB,GAGnB,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constants for the Reflector marker system.
|
|
3
|
+
*
|
|
4
|
+
* @see docs/adr/0011-reflector-marker-consolidation.md
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Regex pattern for CYCLIST markers.
|
|
8
|
+
*
|
|
9
|
+
* Format: <!-- CYCLIST:TYPE:value -->
|
|
10
|
+
*
|
|
11
|
+
* - Case-insensitive for CYCLIST prefix and TYPE
|
|
12
|
+
* - Preserves value case
|
|
13
|
+
* - Handles whitespace variations
|
|
14
|
+
*
|
|
15
|
+
* Groups:
|
|
16
|
+
* - [1] = TYPE (e.g., HANDOFF, CONTEXT_CLEAR)
|
|
17
|
+
* - [2] = value (e.g., /dev, yesno, 1,2,3)
|
|
18
|
+
*
|
|
19
|
+
* IMPORTANT: Reset lastIndex before each use since this is a global regex.
|
|
20
|
+
*/
|
|
21
|
+
export declare const MARKER_PATTERN: RegExp;
|
|
22
|
+
/**
|
|
23
|
+
* Known marker type constants.
|
|
24
|
+
* Use these for type-safe comparisons instead of string literals.
|
|
25
|
+
*/
|
|
26
|
+
export declare const MARKER_TYPES: {
|
|
27
|
+
readonly HANDOFF: "handoff";
|
|
28
|
+
readonly CONTEXT_CLEAR: "context_clear";
|
|
29
|
+
readonly INVOKE: "invoke";
|
|
30
|
+
readonly QUESTION: "question";
|
|
31
|
+
readonly CHOICES: "choices";
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Valid marker types for validation.
|
|
35
|
+
*/
|
|
36
|
+
export declare const VALID_MARKER_TYPES: Set<string>;
|
|
37
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/marker/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,cAAc,QAA0C,CAAC;AAEtE;;;GAGG;AACH,eAAO,MAAM,YAAY;;;;;;CAMf,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,kBAAkB,aAM7B,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constants for the Reflector marker system.
|
|
3
|
+
*
|
|
4
|
+
* @see docs/adr/0011-reflector-marker-consolidation.md
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Regex pattern for CYCLIST markers.
|
|
8
|
+
*
|
|
9
|
+
* Format: <!-- CYCLIST:TYPE:value -->
|
|
10
|
+
*
|
|
11
|
+
* - Case-insensitive for CYCLIST prefix and TYPE
|
|
12
|
+
* - Preserves value case
|
|
13
|
+
* - Handles whitespace variations
|
|
14
|
+
*
|
|
15
|
+
* Groups:
|
|
16
|
+
* - [1] = TYPE (e.g., HANDOFF, CONTEXT_CLEAR)
|
|
17
|
+
* - [2] = value (e.g., /dev, yesno, 1,2,3)
|
|
18
|
+
*
|
|
19
|
+
* IMPORTANT: Reset lastIndex before each use since this is a global regex.
|
|
20
|
+
*/
|
|
21
|
+
export const MARKER_PATTERN = /<!--\s*CYCLIST:(\w+):([^>]+?)\s*-->/gi;
|
|
22
|
+
/**
|
|
23
|
+
* Known marker type constants.
|
|
24
|
+
* Use these for type-safe comparisons instead of string literals.
|
|
25
|
+
*/
|
|
26
|
+
export const MARKER_TYPES = {
|
|
27
|
+
HANDOFF: 'handoff',
|
|
28
|
+
CONTEXT_CLEAR: 'context_clear',
|
|
29
|
+
INVOKE: 'invoke',
|
|
30
|
+
QUESTION: 'question',
|
|
31
|
+
CHOICES: 'choices',
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Valid marker types for validation.
|
|
35
|
+
*/
|
|
36
|
+
export const VALID_MARKER_TYPES = new Set([
|
|
37
|
+
'handoff',
|
|
38
|
+
'context_clear',
|
|
39
|
+
'invoke',
|
|
40
|
+
'question',
|
|
41
|
+
'choices',
|
|
42
|
+
]);
|
|
43
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/marker/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,uCAAuC,CAAC;AAEtE;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,OAAO,EAAE,SAAS;IAClB,aAAa,EAAE,eAAe;IAC9B,MAAM,EAAE,QAAQ;IAChB,QAAQ,EAAE,UAAU;IACpB,OAAO,EAAE,SAAS;CACV,CAAC;AAEX;;GAEG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC;IACxC,SAAS;IACT,eAAe;IACf,QAAQ;IACR,UAAU;IACV,SAAS;CACV,CAAC,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marker detection for the Reflector protocol.
|
|
3
|
+
*
|
|
4
|
+
* @see docs/adr/0011-reflector-marker-consolidation.md
|
|
5
|
+
*/
|
|
6
|
+
import type { Marker } from './types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Detect CYCLIST markers in text.
|
|
9
|
+
*
|
|
10
|
+
* Scans text for CYCLIST markers and returns an array of parsed markers.
|
|
11
|
+
* Code blocks are stripped before detection to prevent false positives
|
|
12
|
+
* from markers appearing inside code examples.
|
|
13
|
+
*
|
|
14
|
+
* @param text - Text to scan for markers
|
|
15
|
+
* @returns Array of markers found, or null if none
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const text = '<!-- CYCLIST:HANDOFF:/dev -->';
|
|
20
|
+
* const markers = detectMarkers(text);
|
|
21
|
+
* // markers = [{ type: 'handoff', value: '/dev', source: 'structured_marker' }]
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare function detectMarkers(text: string): Marker[] | null;
|
|
25
|
+
//# sourceMappingURL=detect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../src/marker/detect.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,YAAY,CAAC;AAErD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAkC3D"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marker detection for the Reflector protocol.
|
|
3
|
+
*
|
|
4
|
+
* @see docs/adr/0011-reflector-marker-consolidation.md
|
|
5
|
+
*/
|
|
6
|
+
import { MARKER_PATTERN, VALID_MARKER_TYPES } from './constants.js';
|
|
7
|
+
import { stripCodeBlocks } from './strip.js';
|
|
8
|
+
/**
|
|
9
|
+
* Detect CYCLIST markers in text.
|
|
10
|
+
*
|
|
11
|
+
* Scans text for CYCLIST markers and returns an array of parsed markers.
|
|
12
|
+
* Code blocks are stripped before detection to prevent false positives
|
|
13
|
+
* from markers appearing inside code examples.
|
|
14
|
+
*
|
|
15
|
+
* @param text - Text to scan for markers
|
|
16
|
+
* @returns Array of markers found, or null if none
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const text = '<!-- CYCLIST:HANDOFF:/dev -->';
|
|
21
|
+
* const markers = detectMarkers(text);
|
|
22
|
+
* // markers = [{ type: 'handoff', value: '/dev', source: 'structured_marker' }]
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export function detectMarkers(text) {
|
|
26
|
+
// Handle null/undefined/empty input
|
|
27
|
+
if (!text) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
// Strip code blocks first - markers inside code should be ignored
|
|
31
|
+
const withoutCode = stripCodeBlocks(text);
|
|
32
|
+
if (!withoutCode.trim()) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
const markers = [];
|
|
36
|
+
// Reset lastIndex for global regex
|
|
37
|
+
MARKER_PATTERN.lastIndex = 0;
|
|
38
|
+
let match;
|
|
39
|
+
while ((match = MARKER_PATTERN.exec(withoutCode)) !== null) {
|
|
40
|
+
const rawType = match[1].toLowerCase();
|
|
41
|
+
// Skip unknown marker types
|
|
42
|
+
if (!VALID_MARKER_TYPES.has(rawType)) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
markers.push({
|
|
46
|
+
type: rawType,
|
|
47
|
+
value: match[2].trim(),
|
|
48
|
+
source: 'structured_marker',
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return markers.length > 0 ? markers : null;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=detect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.js","sourceRoot":"","sources":["../../src/marker/detect.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAG7C;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,oCAAoC;IACpC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,IAAI,CAAC;IACd,CAAC;IAED,kEAAkE;IAClE,MAAM,WAAW,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,mCAAmC;IACnC,cAAc,CAAC,SAAS,GAAG,CAAC,CAAC;IAE7B,IAAI,KAAK,CAAC;IACV,OAAO,CAAC,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAC3D,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QAEvC,4BAA4B;QAC5B,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YACrC,SAAS;QACX,CAAC;QAED,OAAO,CAAC,IAAI,CAAC;YACX,IAAI,EAAE,OAAqB;YAC3B,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;YACtB,MAAM,EAAE,mBAAmB;SAC5B,CAAC,CAAC;IACL,CAAC;IAED,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7C,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the marker detection module.
|
|
3
|
+
*
|
|
4
|
+
* These tests cover the shared marker parsing logic that will be used by
|
|
5
|
+
* both Cyclist terminal and VS Code extension.
|
|
6
|
+
*
|
|
7
|
+
* @see docs/adr/0011-reflector-marker-consolidation.md
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=detect.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.test.d.ts","sourceRoot":"","sources":["../../src/marker/detect.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the marker detection module.
|
|
3
|
+
*
|
|
4
|
+
* These tests cover the shared marker parsing logic that will be used by
|
|
5
|
+
* both Cyclist terminal and VS Code extension.
|
|
6
|
+
*
|
|
7
|
+
* @see docs/adr/0011-reflector-marker-consolidation.md
|
|
8
|
+
*/
|
|
9
|
+
import { describe, it } from 'node:test';
|
|
10
|
+
import assert from 'node:assert';
|
|
11
|
+
import { detectMarkers, stripMarkers, stripCodeBlocks, MARKER_PATTERN, MARKER_TYPES, VALID_MARKER_TYPES, } from './index.js';
|
|
12
|
+
// =============================================================================
|
|
13
|
+
// Type Tests
|
|
14
|
+
// =============================================================================
|
|
15
|
+
describe('marker types', () => {
|
|
16
|
+
describe('MarkerType', () => {
|
|
17
|
+
it('should include handoff type', () => {
|
|
18
|
+
const type = 'handoff';
|
|
19
|
+
assert.strictEqual(type, 'handoff');
|
|
20
|
+
});
|
|
21
|
+
it('should include context_clear type', () => {
|
|
22
|
+
const type = 'context_clear';
|
|
23
|
+
assert.strictEqual(type, 'context_clear');
|
|
24
|
+
});
|
|
25
|
+
it('should include invoke type', () => {
|
|
26
|
+
const type = 'invoke';
|
|
27
|
+
assert.strictEqual(type, 'invoke');
|
|
28
|
+
});
|
|
29
|
+
it('should include question type', () => {
|
|
30
|
+
const type = 'question';
|
|
31
|
+
assert.strictEqual(type, 'question');
|
|
32
|
+
});
|
|
33
|
+
it('should include choices type', () => {
|
|
34
|
+
const type = 'choices';
|
|
35
|
+
assert.strictEqual(type, 'choices');
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
describe('Marker interface', () => {
|
|
39
|
+
it('should have type, value, and optional source fields', () => {
|
|
40
|
+
const marker = {
|
|
41
|
+
type: 'handoff',
|
|
42
|
+
value: '/dev',
|
|
43
|
+
source: 'structured_marker',
|
|
44
|
+
};
|
|
45
|
+
assert.strictEqual(marker.type, 'handoff');
|
|
46
|
+
assert.strictEqual(marker.value, '/dev');
|
|
47
|
+
assert.strictEqual(marker.source, 'structured_marker');
|
|
48
|
+
});
|
|
49
|
+
it('should allow source to be omitted', () => {
|
|
50
|
+
const marker = {
|
|
51
|
+
type: 'invoke',
|
|
52
|
+
value: '/tea',
|
|
53
|
+
};
|
|
54
|
+
assert.strictEqual(marker.type, 'invoke');
|
|
55
|
+
assert.strictEqual(marker.value, '/tea');
|
|
56
|
+
assert.strictEqual(marker.source, undefined);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
// =============================================================================
|
|
61
|
+
// Constants Tests
|
|
62
|
+
// =============================================================================
|
|
63
|
+
describe('marker constants', () => {
|
|
64
|
+
describe('MARKER_PATTERN', () => {
|
|
65
|
+
it('should be a global case-insensitive regex', () => {
|
|
66
|
+
assert.ok(MARKER_PATTERN instanceof RegExp);
|
|
67
|
+
assert.ok(MARKER_PATTERN.flags.includes('g'), 'Should have global flag');
|
|
68
|
+
assert.ok(MARKER_PATTERN.flags.includes('i'), 'Should have case-insensitive flag');
|
|
69
|
+
});
|
|
70
|
+
it('should match basic CYCLIST marker format', () => {
|
|
71
|
+
const text = '<!-- CYCLIST:HANDOFF:/dev -->';
|
|
72
|
+
MARKER_PATTERN.lastIndex = 0;
|
|
73
|
+
const match = MARKER_PATTERN.exec(text);
|
|
74
|
+
assert.ok(match !== null, 'Should match basic marker');
|
|
75
|
+
assert.strictEqual(match[1], 'HANDOFF');
|
|
76
|
+
assert.strictEqual(match[2], '/dev');
|
|
77
|
+
});
|
|
78
|
+
it('should match with whitespace variations', () => {
|
|
79
|
+
const text = '<!-- CYCLIST:HANDOFF: /dev -->';
|
|
80
|
+
MARKER_PATTERN.lastIndex = 0;
|
|
81
|
+
const match = MARKER_PATTERN.exec(text);
|
|
82
|
+
assert.ok(match !== null, 'Should match with extra whitespace');
|
|
83
|
+
});
|
|
84
|
+
it('should match lowercase CYCLIST prefix', () => {
|
|
85
|
+
const text = '<!-- cyclist:handoff:/dev -->';
|
|
86
|
+
MARKER_PATTERN.lastIndex = 0;
|
|
87
|
+
const match = MARKER_PATTERN.exec(text);
|
|
88
|
+
assert.ok(match !== null, 'Should match lowercase prefix');
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
describe('MARKER_TYPES', () => {
|
|
92
|
+
it('should have HANDOFF constant', () => {
|
|
93
|
+
assert.strictEqual(MARKER_TYPES.HANDOFF, 'handoff');
|
|
94
|
+
});
|
|
95
|
+
it('should have CONTEXT_CLEAR constant', () => {
|
|
96
|
+
assert.strictEqual(MARKER_TYPES.CONTEXT_CLEAR, 'context_clear');
|
|
97
|
+
});
|
|
98
|
+
it('should have INVOKE constant', () => {
|
|
99
|
+
assert.strictEqual(MARKER_TYPES.INVOKE, 'invoke');
|
|
100
|
+
});
|
|
101
|
+
it('should have QUESTION constant', () => {
|
|
102
|
+
assert.strictEqual(MARKER_TYPES.QUESTION, 'question');
|
|
103
|
+
});
|
|
104
|
+
it('should have CHOICES constant', () => {
|
|
105
|
+
assert.strictEqual(MARKER_TYPES.CHOICES, 'choices');
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
describe('VALID_MARKER_TYPES', () => {
|
|
109
|
+
it('should be a Set', () => {
|
|
110
|
+
assert.ok(VALID_MARKER_TYPES instanceof Set);
|
|
111
|
+
});
|
|
112
|
+
it('should contain all 5 marker types', () => {
|
|
113
|
+
assert.strictEqual(VALID_MARKER_TYPES.size, 5);
|
|
114
|
+
assert.ok(VALID_MARKER_TYPES.has('handoff'));
|
|
115
|
+
assert.ok(VALID_MARKER_TYPES.has('context_clear'));
|
|
116
|
+
assert.ok(VALID_MARKER_TYPES.has('invoke'));
|
|
117
|
+
assert.ok(VALID_MARKER_TYPES.has('question'));
|
|
118
|
+
assert.ok(VALID_MARKER_TYPES.has('choices'));
|
|
119
|
+
});
|
|
120
|
+
it('should match MARKER_TYPES values', () => {
|
|
121
|
+
// VALID_MARKER_TYPES should contain exactly the values from MARKER_TYPES
|
|
122
|
+
const typeValues = Object.values(MARKER_TYPES);
|
|
123
|
+
assert.strictEqual(VALID_MARKER_TYPES.size, typeValues.length);
|
|
124
|
+
for (const value of typeValues) {
|
|
125
|
+
assert.ok(VALID_MARKER_TYPES.has(value), `Should contain ${value}`);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
it('should not contain invalid types', () => {
|
|
129
|
+
assert.ok(!VALID_MARKER_TYPES.has('invalid'));
|
|
130
|
+
assert.ok(!VALID_MARKER_TYPES.has('HANDOFF')); // Case sensitive - lowercase only
|
|
131
|
+
assert.ok(!VALID_MARKER_TYPES.has(''));
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
// =============================================================================
|
|
136
|
+
// Strip Functions Tests
|
|
137
|
+
// =============================================================================
|
|
138
|
+
describe('stripCodeBlocks', () => {
|
|
139
|
+
it('should remove single code block', () => {
|
|
140
|
+
const text = 'Before\n```javascript\nconst x = 1;\n```\nAfter';
|
|
141
|
+
const result = stripCodeBlocks(text);
|
|
142
|
+
assert.ok(!result.includes('```'), 'Should not contain code fence');
|
|
143
|
+
assert.ok(!result.includes('const x = 1'), 'Should not contain code content');
|
|
144
|
+
assert.ok(result.includes('Before'), 'Should preserve text before');
|
|
145
|
+
assert.ok(result.includes('After'), 'Should preserve text after');
|
|
146
|
+
});
|
|
147
|
+
it('should remove multiple code blocks', () => {
|
|
148
|
+
const text = '```js\ncode1\n```\ntext\n```ts\ncode2\n```';
|
|
149
|
+
const result = stripCodeBlocks(text);
|
|
150
|
+
assert.ok(!result.includes('code1'), 'Should remove first code block');
|
|
151
|
+
assert.ok(!result.includes('code2'), 'Should remove second code block');
|
|
152
|
+
assert.ok(result.includes('text'), 'Should preserve text between blocks');
|
|
153
|
+
});
|
|
154
|
+
it('should handle text without code blocks', () => {
|
|
155
|
+
const text = 'Just regular text with no code';
|
|
156
|
+
const result = stripCodeBlocks(text);
|
|
157
|
+
assert.strictEqual(result, text);
|
|
158
|
+
});
|
|
159
|
+
it('should handle empty string', () => {
|
|
160
|
+
const result = stripCodeBlocks('');
|
|
161
|
+
assert.strictEqual(result, '');
|
|
162
|
+
});
|
|
163
|
+
it('should handle multiline code blocks', () => {
|
|
164
|
+
const text = '```\nline1\nline2\nline3\n```';
|
|
165
|
+
const result = stripCodeBlocks(text);
|
|
166
|
+
assert.ok(!result.includes('line1'), 'Should remove multiline content');
|
|
167
|
+
assert.ok(!result.includes('line2'), 'Should remove multiline content');
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
describe('stripMarkers', () => {
|
|
171
|
+
it('should remove HANDOFF marker', () => {
|
|
172
|
+
const text = 'Some text <!-- CYCLIST:HANDOFF:/dev --> more text';
|
|
173
|
+
const result = stripMarkers(text);
|
|
174
|
+
assert.ok(!result.includes('CYCLIST'), 'Should remove marker');
|
|
175
|
+
assert.ok(!result.includes('<!--'), 'Should remove HTML comment');
|
|
176
|
+
assert.ok(result.includes('Some text'), 'Should preserve surrounding text');
|
|
177
|
+
assert.ok(result.includes('more text'), 'Should preserve surrounding text');
|
|
178
|
+
});
|
|
179
|
+
it('should remove CONTEXT_CLEAR marker', () => {
|
|
180
|
+
const text = '<!-- CYCLIST:CONTEXT_CLEAR:/sm -->';
|
|
181
|
+
const result = stripMarkers(text);
|
|
182
|
+
assert.strictEqual(result, '');
|
|
183
|
+
});
|
|
184
|
+
it('should remove multiple markers', () => {
|
|
185
|
+
const text = '<!-- CYCLIST:QUESTION:yesno --> text <!-- CYCLIST:CHOICES:1,2,3 -->';
|
|
186
|
+
const result = stripMarkers(text);
|
|
187
|
+
assert.ok(!result.includes('QUESTION'), 'Should remove first marker');
|
|
188
|
+
assert.ok(!result.includes('CHOICES'), 'Should remove second marker');
|
|
189
|
+
assert.ok(result.includes('text'), 'Should preserve text between markers');
|
|
190
|
+
});
|
|
191
|
+
it('should handle text without markers', () => {
|
|
192
|
+
const text = 'Regular text with no markers';
|
|
193
|
+
const result = stripMarkers(text);
|
|
194
|
+
assert.strictEqual(result, text);
|
|
195
|
+
});
|
|
196
|
+
it('should handle empty string', () => {
|
|
197
|
+
const result = stripMarkers('');
|
|
198
|
+
assert.strictEqual(result, '');
|
|
199
|
+
});
|
|
200
|
+
it('should handle null/undefined input', () => {
|
|
201
|
+
// @ts-expect-error - testing runtime behavior with invalid input
|
|
202
|
+
const result = stripMarkers(null);
|
|
203
|
+
assert.strictEqual(result, '');
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
// =============================================================================
|
|
207
|
+
// Detection Tests
|
|
208
|
+
// =============================================================================
|
|
209
|
+
describe('detectMarkers', () => {
|
|
210
|
+
describe('input handling', () => {
|
|
211
|
+
it('should return null for empty string', () => {
|
|
212
|
+
const result = detectMarkers('');
|
|
213
|
+
assert.strictEqual(result, null);
|
|
214
|
+
});
|
|
215
|
+
it('should return null for null input', () => {
|
|
216
|
+
// @ts-expect-error - testing runtime behavior with invalid input
|
|
217
|
+
const result = detectMarkers(null);
|
|
218
|
+
assert.strictEqual(result, null);
|
|
219
|
+
});
|
|
220
|
+
it('should return null for undefined input', () => {
|
|
221
|
+
// @ts-expect-error - testing runtime behavior with invalid input
|
|
222
|
+
const result = detectMarkers(undefined);
|
|
223
|
+
assert.strictEqual(result, null);
|
|
224
|
+
});
|
|
225
|
+
it('should return null for whitespace-only input', () => {
|
|
226
|
+
const result = detectMarkers(' \n\t ');
|
|
227
|
+
assert.strictEqual(result, null);
|
|
228
|
+
});
|
|
229
|
+
it('should return null for text without markers', () => {
|
|
230
|
+
const result = detectMarkers('Just regular text with no markers');
|
|
231
|
+
assert.strictEqual(result, null);
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
describe('marker type detection', () => {
|
|
235
|
+
it('should detect HANDOFF marker', () => {
|
|
236
|
+
const text = '<!-- CYCLIST:HANDOFF:/dev -->';
|
|
237
|
+
const result = detectMarkers(text);
|
|
238
|
+
assert.ok(result !== null, 'Should detect marker');
|
|
239
|
+
assert.strictEqual(result.length, 1);
|
|
240
|
+
assert.strictEqual(result[0].type, 'handoff');
|
|
241
|
+
assert.strictEqual(result[0].value, '/dev');
|
|
242
|
+
});
|
|
243
|
+
it('should detect CONTEXT_CLEAR marker', () => {
|
|
244
|
+
const text = '<!-- CYCLIST:CONTEXT_CLEAR:/reviewer -->';
|
|
245
|
+
const result = detectMarkers(text);
|
|
246
|
+
assert.ok(result !== null, 'Should detect marker');
|
|
247
|
+
assert.strictEqual(result.length, 1);
|
|
248
|
+
assert.strictEqual(result[0].type, 'context_clear');
|
|
249
|
+
assert.strictEqual(result[0].value, '/reviewer');
|
|
250
|
+
});
|
|
251
|
+
it('should detect INVOKE marker', () => {
|
|
252
|
+
const text = '<!-- CYCLIST:INVOKE:/tea -->';
|
|
253
|
+
const result = detectMarkers(text);
|
|
254
|
+
assert.ok(result !== null, 'Should detect marker');
|
|
255
|
+
assert.strictEqual(result.length, 1);
|
|
256
|
+
assert.strictEqual(result[0].type, 'invoke');
|
|
257
|
+
assert.strictEqual(result[0].value, '/tea');
|
|
258
|
+
});
|
|
259
|
+
it('should detect QUESTION marker', () => {
|
|
260
|
+
const text = '<!-- CYCLIST:QUESTION:yesno -->';
|
|
261
|
+
const result = detectMarkers(text);
|
|
262
|
+
assert.ok(result !== null, 'Should detect marker');
|
|
263
|
+
assert.strictEqual(result.length, 1);
|
|
264
|
+
assert.strictEqual(result[0].type, 'question');
|
|
265
|
+
assert.strictEqual(result[0].value, 'yesno');
|
|
266
|
+
});
|
|
267
|
+
it('should detect CHOICES marker with numbers', () => {
|
|
268
|
+
const text = '<!-- CYCLIST:CHOICES:1,2,3 -->';
|
|
269
|
+
const result = detectMarkers(text);
|
|
270
|
+
assert.ok(result !== null, 'Should detect marker');
|
|
271
|
+
assert.strictEqual(result.length, 1);
|
|
272
|
+
assert.strictEqual(result[0].type, 'choices');
|
|
273
|
+
assert.strictEqual(result[0].value, '1,2,3');
|
|
274
|
+
});
|
|
275
|
+
it('should detect CHOICES marker with text labels', () => {
|
|
276
|
+
const text = '<!-- CYCLIST:CHOICES:Option A,Option B,Option C -->';
|
|
277
|
+
const result = detectMarkers(text);
|
|
278
|
+
assert.ok(result !== null, 'Should detect marker');
|
|
279
|
+
assert.strictEqual(result.length, 1);
|
|
280
|
+
assert.strictEqual(result[0].type, 'choices');
|
|
281
|
+
assert.strictEqual(result[0].value, 'Option A,Option B,Option C');
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
describe('multiple markers', () => {
|
|
285
|
+
it('should detect multiple markers in order', () => {
|
|
286
|
+
const text = `
|
|
287
|
+
<!-- CYCLIST:QUESTION:yesno -->
|
|
288
|
+
Some text here
|
|
289
|
+
<!-- CYCLIST:CHOICES:1,2 -->
|
|
290
|
+
`;
|
|
291
|
+
const result = detectMarkers(text);
|
|
292
|
+
assert.ok(result !== null, 'Should detect markers');
|
|
293
|
+
assert.strictEqual(result.length, 2);
|
|
294
|
+
assert.strictEqual(result[0].type, 'question');
|
|
295
|
+
assert.strictEqual(result[1].type, 'choices');
|
|
296
|
+
});
|
|
297
|
+
it('should preserve order of markers', () => {
|
|
298
|
+
const text = '<!-- CYCLIST:HANDOFF:/a --><!-- CYCLIST:INVOKE:/b --><!-- CYCLIST:CONTEXT_CLEAR:/c -->';
|
|
299
|
+
const result = detectMarkers(text);
|
|
300
|
+
assert.ok(result !== null, 'Should detect markers');
|
|
301
|
+
assert.strictEqual(result.length, 3);
|
|
302
|
+
assert.strictEqual(result[0].value, '/a');
|
|
303
|
+
assert.strictEqual(result[1].value, '/b');
|
|
304
|
+
assert.strictEqual(result[2].value, '/c');
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
describe('code block handling', () => {
|
|
308
|
+
it('should ignore markers inside code blocks', () => {
|
|
309
|
+
const text = `
|
|
310
|
+
Regular text
|
|
311
|
+
\`\`\`
|
|
312
|
+
<!-- CYCLIST:HANDOFF:/dev -->
|
|
313
|
+
\`\`\`
|
|
314
|
+
More text
|
|
315
|
+
`;
|
|
316
|
+
const result = detectMarkers(text);
|
|
317
|
+
assert.strictEqual(result, null, 'Should not detect marker inside code block');
|
|
318
|
+
});
|
|
319
|
+
it('should detect markers outside code blocks', () => {
|
|
320
|
+
const text = `
|
|
321
|
+
\`\`\`
|
|
322
|
+
code here
|
|
323
|
+
\`\`\`
|
|
324
|
+
<!-- CYCLIST:HANDOFF:/dev -->
|
|
325
|
+
`;
|
|
326
|
+
const result = detectMarkers(text);
|
|
327
|
+
assert.ok(result !== null, 'Should detect marker outside code block');
|
|
328
|
+
assert.strictEqual(result.length, 1);
|
|
329
|
+
assert.strictEqual(result[0].type, 'handoff');
|
|
330
|
+
});
|
|
331
|
+
it('should handle mixed code blocks and markers', () => {
|
|
332
|
+
const text = `
|
|
333
|
+
<!-- CYCLIST:INVOKE:/tea -->
|
|
334
|
+
\`\`\`javascript
|
|
335
|
+
<!-- CYCLIST:HANDOFF:/ignored -->
|
|
336
|
+
\`\`\`
|
|
337
|
+
<!-- CYCLIST:CONTEXT_CLEAR:/sm -->
|
|
338
|
+
`;
|
|
339
|
+
const result = detectMarkers(text);
|
|
340
|
+
assert.ok(result !== null, 'Should detect markers');
|
|
341
|
+
assert.strictEqual(result.length, 2, 'Should only detect markers outside code');
|
|
342
|
+
assert.strictEqual(result[0].type, 'invoke');
|
|
343
|
+
assert.strictEqual(result[1].type, 'context_clear');
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
describe('case sensitivity', () => {
|
|
347
|
+
it('should be case-insensitive for CYCLIST prefix', () => {
|
|
348
|
+
const texts = [
|
|
349
|
+
'<!-- CYCLIST:HANDOFF:/dev -->',
|
|
350
|
+
'<!-- cyclist:HANDOFF:/dev -->',
|
|
351
|
+
'<!-- Cyclist:HANDOFF:/dev -->',
|
|
352
|
+
'<!-- CyCLiST:HANDOFF:/dev -->',
|
|
353
|
+
];
|
|
354
|
+
for (const text of texts) {
|
|
355
|
+
const result = detectMarkers(text);
|
|
356
|
+
assert.ok(result !== null, `Should detect marker in: ${text}`);
|
|
357
|
+
assert.strictEqual(result[0].type, 'handoff');
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
it('should be case-insensitive for marker type', () => {
|
|
361
|
+
const texts = [
|
|
362
|
+
'<!-- CYCLIST:HANDOFF:/dev -->',
|
|
363
|
+
'<!-- CYCLIST:handoff:/dev -->',
|
|
364
|
+
'<!-- CYCLIST:Handoff:/dev -->',
|
|
365
|
+
'<!-- CYCLIST:HaNdOfF:/dev -->',
|
|
366
|
+
];
|
|
367
|
+
for (const text of texts) {
|
|
368
|
+
const result = detectMarkers(text);
|
|
369
|
+
assert.ok(result !== null, `Should detect marker in: ${text}`);
|
|
370
|
+
assert.strictEqual(result[0].type, 'handoff', 'Type should be normalized to lowercase');
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
it('should preserve value case', () => {
|
|
374
|
+
const text = '<!-- CYCLIST:HANDOFF:/MyAgent -->';
|
|
375
|
+
const result = detectMarkers(text);
|
|
376
|
+
assert.ok(result !== null);
|
|
377
|
+
assert.strictEqual(result[0].value, '/MyAgent', 'Value case should be preserved');
|
|
378
|
+
});
|
|
379
|
+
});
|
|
380
|
+
describe('whitespace handling', () => {
|
|
381
|
+
it('should handle whitespace inside marker', () => {
|
|
382
|
+
const text = '<!-- CYCLIST:HANDOFF: /dev -->';
|
|
383
|
+
const result = detectMarkers(text);
|
|
384
|
+
assert.ok(result !== null, 'Should detect marker with whitespace');
|
|
385
|
+
assert.strictEqual(result[0].value, '/dev', 'Value should be trimmed');
|
|
386
|
+
});
|
|
387
|
+
it('should handle newlines around marker', () => {
|
|
388
|
+
const text = '\n\n<!-- CYCLIST:INVOKE:/tea -->\n\n';
|
|
389
|
+
const result = detectMarkers(text);
|
|
390
|
+
assert.ok(result !== null, 'Should detect marker with newlines');
|
|
391
|
+
assert.strictEqual(result[0].type, 'invoke');
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
describe('source field', () => {
|
|
395
|
+
it('should set source to structured_marker', () => {
|
|
396
|
+
const text = '<!-- CYCLIST:HANDOFF:/dev -->';
|
|
397
|
+
const result = detectMarkers(text);
|
|
398
|
+
assert.ok(result !== null);
|
|
399
|
+
assert.strictEqual(result[0].source, 'structured_marker');
|
|
400
|
+
});
|
|
401
|
+
});
|
|
402
|
+
describe('unknown marker types', () => {
|
|
403
|
+
it('should skip unknown marker types', () => {
|
|
404
|
+
const text = '<!-- CYCLIST:UNKNOWN_TYPE:value -->';
|
|
405
|
+
const result = detectMarkers(text);
|
|
406
|
+
assert.strictEqual(result, null, 'Should return null for unknown type');
|
|
407
|
+
});
|
|
408
|
+
it('should still detect valid markers alongside unknown ones', () => {
|
|
409
|
+
const text = '<!-- CYCLIST:INVALID:x --> <!-- CYCLIST:HANDOFF:/dev -->';
|
|
410
|
+
const result = detectMarkers(text);
|
|
411
|
+
assert.ok(result !== null, 'Should detect valid marker');
|
|
412
|
+
assert.strictEqual(result.length, 1);
|
|
413
|
+
assert.strictEqual(result[0].type, 'handoff');
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
//# sourceMappingURL=detect.test.js.map
|