@pennyfarthing/shared 7.5.0 → 9.3.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/browser.d.ts +6 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +8 -0
- package/dist/browser.js.map +1 -0
- package/dist/generate-skill-docs.d.ts +35 -0
- package/dist/generate-skill-docs.d.ts.map +1 -0
- package/dist/generate-skill-docs.js +442 -0
- package/dist/generate-skill-docs.js.map +1 -0
- package/dist/generate-skill-docs.test.d.ts +13 -0
- package/dist/generate-skill-docs.test.d.ts.map +1 -0
- package/dist/generate-skill-docs.test.js +519 -0
- package/dist/generate-skill-docs.test.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/marker/constants.d.ts +39 -0
- package/dist/marker/constants.d.ts.map +1 -0
- package/dist/marker/constants.js +46 -0
- package/dist/marker/constants.js.map +1 -0
- package/dist/marker/continue.test.d.ts +12 -0
- package/dist/marker/continue.test.d.ts.map +1 -0
- package/dist/marker/continue.test.js +76 -0
- package/dist/marker/continue.test.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 +418 -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 +100 -0
- package/dist/migrate-theme-schema.test.js.map +1 -0
- package/dist/portrait-resolver.d.ts +32 -0
- package/dist/portrait-resolver.d.ts.map +1 -0
- package/dist/portrait-resolver.js +225 -0
- package/dist/portrait-resolver.js.map +1 -0
- package/dist/portrait-resolver.test.d.ts +2 -0
- package/dist/portrait-resolver.test.d.ts.map +1 -0
- package/dist/portrait-resolver.test.js +156 -0
- package/dist/portrait-resolver.test.js.map +1 -0
- package/dist/skill-search.d.ts +36 -0
- package/dist/skill-search.d.ts.map +1 -0
- package/dist/skill-search.js +300 -0
- package/dist/skill-search.js.map +1 -0
- package/dist/skill-search.sh +41 -0
- package/dist/skill-search.test.d.ts +16 -0
- package/dist/skill-search.test.d.ts.map +1 -0
- package/dist/skill-search.test.js +177 -0
- package/dist/skill-search.test.js.map +1 -0
- package/dist/skill-suggest.d.ts +76 -0
- package/dist/skill-suggest.d.ts.map +1 -0
- package/dist/skill-suggest.js +256 -0
- package/dist/skill-suggest.js.map +1 -0
- package/dist/skill-suggest.test.d.ts +12 -0
- package/dist/skill-suggest.test.d.ts.map +1 -0
- package/dist/skill-suggest.test.js +257 -0
- package/dist/skill-suggest.test.js.map +1 -0
- package/dist/theme-loader.d.ts +81 -0
- package/dist/theme-loader.d.ts.map +1 -0
- package/dist/theme-loader.js +491 -0
- package/dist/theme-loader.js.map +1 -0
- package/dist/theme-loader.test.d.ts +8 -0
- package/dist/theme-loader.test.d.ts.map +1 -0
- package/dist/theme-loader.test.js +62 -0
- package/dist/theme-loader.test.js.map +1 -0
- package/package.json +14 -1
|
@@ -0,0 +1,46 @@
|
|
|
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 --> or <!-- CYCLIST:TYPE --> (for CONTINUE)
|
|
10
|
+
*
|
|
11
|
+
* - Case-insensitive for CYCLIST prefix and TYPE
|
|
12
|
+
* - Preserves value case
|
|
13
|
+
* - Handles whitespace variations
|
|
14
|
+
* - Value is optional (CONTINUE marker has no value)
|
|
15
|
+
*
|
|
16
|
+
* Groups:
|
|
17
|
+
* - [1] = TYPE (e.g., HANDOFF, CONTEXT_CLEAR, CONTINUE)
|
|
18
|
+
* - [2] = value (e.g., /dev, yesno, 1,2,3) - undefined for CONTINUE
|
|
19
|
+
*
|
|
20
|
+
* IMPORTANT: Reset lastIndex before each use since this is a global regex.
|
|
21
|
+
*/
|
|
22
|
+
export const MARKER_PATTERN = /<!--\s*CYCLIST:(\w+)(?::([^>]+?))?\s*-->/gi;
|
|
23
|
+
/**
|
|
24
|
+
* Known marker type constants.
|
|
25
|
+
* Use these for type-safe comparisons instead of string literals.
|
|
26
|
+
*/
|
|
27
|
+
export const MARKER_TYPES = {
|
|
28
|
+
HANDOFF: 'handoff',
|
|
29
|
+
CONTEXT_CLEAR: 'context_clear',
|
|
30
|
+
INVOKE: 'invoke',
|
|
31
|
+
QUESTION: 'question',
|
|
32
|
+
CHOICES: 'choices',
|
|
33
|
+
CONTINUE: 'continue',
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Valid marker types for validation.
|
|
37
|
+
*/
|
|
38
|
+
export const VALID_MARKER_TYPES = new Set([
|
|
39
|
+
'handoff',
|
|
40
|
+
'context_clear',
|
|
41
|
+
'invoke',
|
|
42
|
+
'question',
|
|
43
|
+
'choices',
|
|
44
|
+
'continue',
|
|
45
|
+
]);
|
|
46
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/marker/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,4CAA4C,CAAC;AAE3E;;;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;IAClB,QAAQ,EAAE,UAAU;CACZ,CAAC;AAEX;;GAEG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC;IACxC,SAAS;IACT,eAAe;IACf,QAAQ;IACR,UAAU;IACV,SAAS;IACT,UAAU;CACX,CAAC,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the CONTINUE marker type
|
|
3
|
+
*
|
|
4
|
+
* Story: MSSCI-12787 - Implement CYCLIST Marker Parsing and Action Buttons
|
|
5
|
+
*
|
|
6
|
+
* The CONTINUE marker signals a status update - the user can continue
|
|
7
|
+
* or redirect. It has no value parameter.
|
|
8
|
+
*
|
|
9
|
+
* Format: <!-- CYCLIST:CONTINUE -->
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=continue.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"continue.test.d.ts","sourceRoot":"","sources":["../../src/marker/continue.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the CONTINUE marker type
|
|
3
|
+
*
|
|
4
|
+
* Story: MSSCI-12787 - Implement CYCLIST Marker Parsing and Action Buttons
|
|
5
|
+
*
|
|
6
|
+
* The CONTINUE marker signals a status update - the user can continue
|
|
7
|
+
* or redirect. It has no value parameter.
|
|
8
|
+
*
|
|
9
|
+
* Format: <!-- CYCLIST:CONTINUE -->
|
|
10
|
+
*/
|
|
11
|
+
import { describe, it } from 'node:test';
|
|
12
|
+
import assert from 'node:assert';
|
|
13
|
+
import { detectMarkers, MARKER_TYPES, VALID_MARKER_TYPES, } from './index.js';
|
|
14
|
+
describe('CONTINUE marker type', () => {
|
|
15
|
+
describe('type definition', () => {
|
|
16
|
+
it('should include continue in MarkerType', () => {
|
|
17
|
+
// This test verifies the type includes 'continue'
|
|
18
|
+
const type = 'continue';
|
|
19
|
+
assert.strictEqual(type, 'continue');
|
|
20
|
+
});
|
|
21
|
+
it('should have CONTINUE constant in MARKER_TYPES', () => {
|
|
22
|
+
assert.strictEqual(MARKER_TYPES.CONTINUE, 'continue');
|
|
23
|
+
});
|
|
24
|
+
it('should include continue in VALID_MARKER_TYPES', () => {
|
|
25
|
+
assert.ok(VALID_MARKER_TYPES.has('continue'), 'VALID_MARKER_TYPES should include continue');
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
describe('detection', () => {
|
|
29
|
+
it('should detect CONTINUE marker without value', () => {
|
|
30
|
+
const text = '<!-- CYCLIST:CONTINUE -->';
|
|
31
|
+
const result = detectMarkers(text);
|
|
32
|
+
assert.ok(result !== null, 'Should detect CONTINUE marker');
|
|
33
|
+
assert.strictEqual(result.length, 1);
|
|
34
|
+
assert.strictEqual(result[0].type, 'continue');
|
|
35
|
+
// CONTINUE has no value, should be empty string or null
|
|
36
|
+
assert.ok(result[0].value === '' || result[0].value === null || result[0].value === undefined, 'CONTINUE marker should have no value');
|
|
37
|
+
});
|
|
38
|
+
it('should be case-insensitive for CONTINUE type', () => {
|
|
39
|
+
const texts = [
|
|
40
|
+
'<!-- CYCLIST:CONTINUE -->',
|
|
41
|
+
'<!-- CYCLIST:continue -->',
|
|
42
|
+
'<!-- CYCLIST:Continue -->',
|
|
43
|
+
];
|
|
44
|
+
for (const text of texts) {
|
|
45
|
+
const result = detectMarkers(text);
|
|
46
|
+
assert.ok(result !== null, `Should detect marker in: ${text}`);
|
|
47
|
+
assert.strictEqual(result[0].type, 'continue', 'Type should be normalized to lowercase');
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
it('should detect CONTINUE alongside other markers', () => {
|
|
51
|
+
const text = `
|
|
52
|
+
Some status update here.
|
|
53
|
+
|
|
54
|
+
<!-- CYCLIST:CONTINUE -->
|
|
55
|
+
|
|
56
|
+
Run \`/dev\` to continue
|
|
57
|
+
`;
|
|
58
|
+
const result = detectMarkers(text);
|
|
59
|
+
assert.ok(result !== null, 'Should detect CONTINUE marker');
|
|
60
|
+
assert.strictEqual(result.length, 1);
|
|
61
|
+
assert.strictEqual(result[0].type, 'continue');
|
|
62
|
+
});
|
|
63
|
+
it('should ignore CONTINUE inside code blocks', () => {
|
|
64
|
+
const text = `
|
|
65
|
+
Regular text
|
|
66
|
+
\`\`\`
|
|
67
|
+
<!-- CYCLIST:CONTINUE -->
|
|
68
|
+
\`\`\`
|
|
69
|
+
More text
|
|
70
|
+
`;
|
|
71
|
+
const result = detectMarkers(text);
|
|
72
|
+
assert.strictEqual(result, null, 'Should not detect marker inside code block');
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
//# sourceMappingURL=continue.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"continue.test.js","sourceRoot":"","sources":["../../src/marker/continue.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -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,EAAE,IAAI,EAAE,IAAI,EAAE;YAC7B,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,418 @@
|
|
|
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 6 marker types', () => {
|
|
113
|
+
assert.strictEqual(VALID_MARKER_TYPES.size, 6);
|
|
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
|
+
assert.ok(VALID_MARKER_TYPES.has('continue'));
|
|
120
|
+
});
|
|
121
|
+
it('should match MARKER_TYPES values', () => {
|
|
122
|
+
// VALID_MARKER_TYPES should contain exactly the values from MARKER_TYPES
|
|
123
|
+
const typeValues = Object.values(MARKER_TYPES);
|
|
124
|
+
assert.strictEqual(VALID_MARKER_TYPES.size, typeValues.length);
|
|
125
|
+
for (const value of typeValues) {
|
|
126
|
+
assert.ok(VALID_MARKER_TYPES.has(value), `Should contain ${value}`);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
it('should not contain invalid types', () => {
|
|
130
|
+
assert.ok(!VALID_MARKER_TYPES.has('invalid'));
|
|
131
|
+
assert.ok(!VALID_MARKER_TYPES.has('HANDOFF')); // Case sensitive - lowercase only
|
|
132
|
+
assert.ok(!VALID_MARKER_TYPES.has(''));
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
// =============================================================================
|
|
137
|
+
// Strip Functions Tests
|
|
138
|
+
// =============================================================================
|
|
139
|
+
describe('stripCodeBlocks', () => {
|
|
140
|
+
it('should remove single code block', () => {
|
|
141
|
+
const text = 'Before\n```javascript\nconst x = 1;\n```\nAfter';
|
|
142
|
+
const result = stripCodeBlocks(text);
|
|
143
|
+
assert.ok(!result.includes('```'), 'Should not contain code fence');
|
|
144
|
+
assert.ok(!result.includes('const x = 1'), 'Should not contain code content');
|
|
145
|
+
assert.ok(result.includes('Before'), 'Should preserve text before');
|
|
146
|
+
assert.ok(result.includes('After'), 'Should preserve text after');
|
|
147
|
+
});
|
|
148
|
+
it('should remove multiple code blocks', () => {
|
|
149
|
+
const text = '```js\ncode1\n```\ntext\n```ts\ncode2\n```';
|
|
150
|
+
const result = stripCodeBlocks(text);
|
|
151
|
+
assert.ok(!result.includes('code1'), 'Should remove first code block');
|
|
152
|
+
assert.ok(!result.includes('code2'), 'Should remove second code block');
|
|
153
|
+
assert.ok(result.includes('text'), 'Should preserve text between blocks');
|
|
154
|
+
});
|
|
155
|
+
it('should handle text without code blocks', () => {
|
|
156
|
+
const text = 'Just regular text with no code';
|
|
157
|
+
const result = stripCodeBlocks(text);
|
|
158
|
+
assert.strictEqual(result, text);
|
|
159
|
+
});
|
|
160
|
+
it('should handle empty string', () => {
|
|
161
|
+
const result = stripCodeBlocks('');
|
|
162
|
+
assert.strictEqual(result, '');
|
|
163
|
+
});
|
|
164
|
+
it('should handle multiline code blocks', () => {
|
|
165
|
+
const text = '```\nline1\nline2\nline3\n```';
|
|
166
|
+
const result = stripCodeBlocks(text);
|
|
167
|
+
assert.ok(!result.includes('line1'), 'Should remove multiline content');
|
|
168
|
+
assert.ok(!result.includes('line2'), 'Should remove multiline content');
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
describe('stripMarkers', () => {
|
|
172
|
+
it('should remove HANDOFF marker', () => {
|
|
173
|
+
const text = 'Some text <!-- CYCLIST:HANDOFF:/dev --> more text';
|
|
174
|
+
const result = stripMarkers(text);
|
|
175
|
+
assert.ok(!result.includes('CYCLIST'), 'Should remove marker');
|
|
176
|
+
assert.ok(!result.includes('<!--'), 'Should remove HTML comment');
|
|
177
|
+
assert.ok(result.includes('Some text'), 'Should preserve surrounding text');
|
|
178
|
+
assert.ok(result.includes('more text'), 'Should preserve surrounding text');
|
|
179
|
+
});
|
|
180
|
+
it('should remove CONTEXT_CLEAR marker', () => {
|
|
181
|
+
const text = '<!-- CYCLIST:CONTEXT_CLEAR:/sm -->';
|
|
182
|
+
const result = stripMarkers(text);
|
|
183
|
+
assert.strictEqual(result, '');
|
|
184
|
+
});
|
|
185
|
+
it('should remove multiple markers', () => {
|
|
186
|
+
const text = '<!-- CYCLIST:QUESTION:yesno --> text <!-- CYCLIST:CHOICES:1,2,3 -->';
|
|
187
|
+
const result = stripMarkers(text);
|
|
188
|
+
assert.ok(!result.includes('QUESTION'), 'Should remove first marker');
|
|
189
|
+
assert.ok(!result.includes('CHOICES'), 'Should remove second marker');
|
|
190
|
+
assert.ok(result.includes('text'), 'Should preserve text between markers');
|
|
191
|
+
});
|
|
192
|
+
it('should handle text without markers', () => {
|
|
193
|
+
const text = 'Regular text with no markers';
|
|
194
|
+
const result = stripMarkers(text);
|
|
195
|
+
assert.strictEqual(result, text);
|
|
196
|
+
});
|
|
197
|
+
it('should handle empty string', () => {
|
|
198
|
+
const result = stripMarkers('');
|
|
199
|
+
assert.strictEqual(result, '');
|
|
200
|
+
});
|
|
201
|
+
it('should handle null/undefined input', () => {
|
|
202
|
+
// @ts-expect-error - testing runtime behavior with invalid input
|
|
203
|
+
const result = stripMarkers(null);
|
|
204
|
+
assert.strictEqual(result, '');
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
// =============================================================================
|
|
208
|
+
// Detection Tests
|
|
209
|
+
// =============================================================================
|
|
210
|
+
describe('detectMarkers', () => {
|
|
211
|
+
describe('input handling', () => {
|
|
212
|
+
it('should return null for empty string', () => {
|
|
213
|
+
const result = detectMarkers('');
|
|
214
|
+
assert.strictEqual(result, null);
|
|
215
|
+
});
|
|
216
|
+
it('should return null for null input', () => {
|
|
217
|
+
// @ts-expect-error - testing runtime behavior with invalid input
|
|
218
|
+
const result = detectMarkers(null);
|
|
219
|
+
assert.strictEqual(result, null);
|
|
220
|
+
});
|
|
221
|
+
it('should return null for undefined input', () => {
|
|
222
|
+
// @ts-expect-error - testing runtime behavior with invalid input
|
|
223
|
+
const result = detectMarkers(undefined);
|
|
224
|
+
assert.strictEqual(result, null);
|
|
225
|
+
});
|
|
226
|
+
it('should return null for whitespace-only input', () => {
|
|
227
|
+
const result = detectMarkers(' \n\t ');
|
|
228
|
+
assert.strictEqual(result, null);
|
|
229
|
+
});
|
|
230
|
+
it('should return null for text without markers', () => {
|
|
231
|
+
const result = detectMarkers('Just regular text with no markers');
|
|
232
|
+
assert.strictEqual(result, null);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
describe('marker type detection', () => {
|
|
236
|
+
it('should detect HANDOFF marker', () => {
|
|
237
|
+
const text = '<!-- CYCLIST:HANDOFF:/dev -->';
|
|
238
|
+
const result = detectMarkers(text);
|
|
239
|
+
assert.ok(result !== null, 'Should detect marker');
|
|
240
|
+
assert.strictEqual(result.length, 1);
|
|
241
|
+
assert.strictEqual(result[0].type, 'handoff');
|
|
242
|
+
assert.strictEqual(result[0].value, '/dev');
|
|
243
|
+
});
|
|
244
|
+
it('should detect CONTEXT_CLEAR marker', () => {
|
|
245
|
+
const text = '<!-- CYCLIST:CONTEXT_CLEAR:/reviewer -->';
|
|
246
|
+
const result = detectMarkers(text);
|
|
247
|
+
assert.ok(result !== null, 'Should detect marker');
|
|
248
|
+
assert.strictEqual(result.length, 1);
|
|
249
|
+
assert.strictEqual(result[0].type, 'context_clear');
|
|
250
|
+
assert.strictEqual(result[0].value, '/reviewer');
|
|
251
|
+
});
|
|
252
|
+
it('should detect INVOKE marker', () => {
|
|
253
|
+
const text = '<!-- CYCLIST:INVOKE:/tea -->';
|
|
254
|
+
const result = detectMarkers(text);
|
|
255
|
+
assert.ok(result !== null, 'Should detect marker');
|
|
256
|
+
assert.strictEqual(result.length, 1);
|
|
257
|
+
assert.strictEqual(result[0].type, 'invoke');
|
|
258
|
+
assert.strictEqual(result[0].value, '/tea');
|
|
259
|
+
});
|
|
260
|
+
it('should detect QUESTION marker', () => {
|
|
261
|
+
const text = '<!-- CYCLIST:QUESTION:yesno -->';
|
|
262
|
+
const result = detectMarkers(text);
|
|
263
|
+
assert.ok(result !== null, 'Should detect marker');
|
|
264
|
+
assert.strictEqual(result.length, 1);
|
|
265
|
+
assert.strictEqual(result[0].type, 'question');
|
|
266
|
+
assert.strictEqual(result[0].value, 'yesno');
|
|
267
|
+
});
|
|
268
|
+
it('should detect CHOICES marker with numbers', () => {
|
|
269
|
+
const text = '<!-- CYCLIST:CHOICES:1,2,3 -->';
|
|
270
|
+
const result = detectMarkers(text);
|
|
271
|
+
assert.ok(result !== null, 'Should detect marker');
|
|
272
|
+
assert.strictEqual(result.length, 1);
|
|
273
|
+
assert.strictEqual(result[0].type, 'choices');
|
|
274
|
+
assert.strictEqual(result[0].value, '1,2,3');
|
|
275
|
+
});
|
|
276
|
+
it('should detect CHOICES marker with text labels', () => {
|
|
277
|
+
const text = '<!-- CYCLIST:CHOICES:Option A,Option B,Option C -->';
|
|
278
|
+
const result = detectMarkers(text);
|
|
279
|
+
assert.ok(result !== null, 'Should detect marker');
|
|
280
|
+
assert.strictEqual(result.length, 1);
|
|
281
|
+
assert.strictEqual(result[0].type, 'choices');
|
|
282
|
+
assert.strictEqual(result[0].value, 'Option A,Option B,Option C');
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
describe('multiple markers', () => {
|
|
286
|
+
it('should detect multiple markers in order', () => {
|
|
287
|
+
const text = `
|
|
288
|
+
<!-- CYCLIST:QUESTION:yesno -->
|
|
289
|
+
Some text here
|
|
290
|
+
<!-- CYCLIST:CHOICES:1,2 -->
|
|
291
|
+
`;
|
|
292
|
+
const result = detectMarkers(text);
|
|
293
|
+
assert.ok(result !== null, 'Should detect markers');
|
|
294
|
+
assert.strictEqual(result.length, 2);
|
|
295
|
+
assert.strictEqual(result[0].type, 'question');
|
|
296
|
+
assert.strictEqual(result[1].type, 'choices');
|
|
297
|
+
});
|
|
298
|
+
it('should preserve order of markers', () => {
|
|
299
|
+
const text = '<!-- CYCLIST:HANDOFF:/a --><!-- CYCLIST:INVOKE:/b --><!-- CYCLIST:CONTEXT_CLEAR:/c -->';
|
|
300
|
+
const result = detectMarkers(text);
|
|
301
|
+
assert.ok(result !== null, 'Should detect markers');
|
|
302
|
+
assert.strictEqual(result.length, 3);
|
|
303
|
+
assert.strictEqual(result[0].value, '/a');
|
|
304
|
+
assert.strictEqual(result[1].value, '/b');
|
|
305
|
+
assert.strictEqual(result[2].value, '/c');
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
describe('code block handling', () => {
|
|
309
|
+
it('should ignore markers inside code blocks', () => {
|
|
310
|
+
const text = `
|
|
311
|
+
Regular text
|
|
312
|
+
\`\`\`
|
|
313
|
+
<!-- CYCLIST:HANDOFF:/dev -->
|
|
314
|
+
\`\`\`
|
|
315
|
+
More text
|
|
316
|
+
`;
|
|
317
|
+
const result = detectMarkers(text);
|
|
318
|
+
assert.strictEqual(result, null, 'Should not detect marker inside code block');
|
|
319
|
+
});
|
|
320
|
+
it('should detect markers outside code blocks', () => {
|
|
321
|
+
const text = `
|
|
322
|
+
\`\`\`
|
|
323
|
+
code here
|
|
324
|
+
\`\`\`
|
|
325
|
+
<!-- CYCLIST:HANDOFF:/dev -->
|
|
326
|
+
`;
|
|
327
|
+
const result = detectMarkers(text);
|
|
328
|
+
assert.ok(result !== null, 'Should detect marker outside code block');
|
|
329
|
+
assert.strictEqual(result.length, 1);
|
|
330
|
+
assert.strictEqual(result[0].type, 'handoff');
|
|
331
|
+
});
|
|
332
|
+
it('should handle mixed code blocks and markers', () => {
|
|
333
|
+
const text = `
|
|
334
|
+
<!-- CYCLIST:INVOKE:/tea -->
|
|
335
|
+
\`\`\`javascript
|
|
336
|
+
<!-- CYCLIST:HANDOFF:/ignored -->
|
|
337
|
+
\`\`\`
|
|
338
|
+
<!-- CYCLIST:CONTEXT_CLEAR:/sm -->
|
|
339
|
+
`;
|
|
340
|
+
const result = detectMarkers(text);
|
|
341
|
+
assert.ok(result !== null, 'Should detect markers');
|
|
342
|
+
assert.strictEqual(result.length, 2, 'Should only detect markers outside code');
|
|
343
|
+
assert.strictEqual(result[0].type, 'invoke');
|
|
344
|
+
assert.strictEqual(result[1].type, 'context_clear');
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
describe('case sensitivity', () => {
|
|
348
|
+
it('should be case-insensitive for CYCLIST prefix', () => {
|
|
349
|
+
const texts = [
|
|
350
|
+
'<!-- CYCLIST:HANDOFF:/dev -->',
|
|
351
|
+
'<!-- cyclist:HANDOFF:/dev -->',
|
|
352
|
+
'<!-- Cyclist:HANDOFF:/dev -->',
|
|
353
|
+
'<!-- CyCLiST:HANDOFF:/dev -->',
|
|
354
|
+
];
|
|
355
|
+
for (const text of texts) {
|
|
356
|
+
const result = detectMarkers(text);
|
|
357
|
+
assert.ok(result !== null, `Should detect marker in: ${text}`);
|
|
358
|
+
assert.strictEqual(result[0].type, 'handoff');
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
it('should be case-insensitive for marker type', () => {
|
|
362
|
+
const texts = [
|
|
363
|
+
'<!-- CYCLIST:HANDOFF:/dev -->',
|
|
364
|
+
'<!-- CYCLIST:handoff:/dev -->',
|
|
365
|
+
'<!-- CYCLIST:Handoff:/dev -->',
|
|
366
|
+
'<!-- CYCLIST:HaNdOfF:/dev -->',
|
|
367
|
+
];
|
|
368
|
+
for (const text of texts) {
|
|
369
|
+
const result = detectMarkers(text);
|
|
370
|
+
assert.ok(result !== null, `Should detect marker in: ${text}`);
|
|
371
|
+
assert.strictEqual(result[0].type, 'handoff', 'Type should be normalized to lowercase');
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
it('should preserve value case', () => {
|
|
375
|
+
const text = '<!-- CYCLIST:HANDOFF:/MyAgent -->';
|
|
376
|
+
const result = detectMarkers(text);
|
|
377
|
+
assert.ok(result !== null);
|
|
378
|
+
assert.strictEqual(result[0].value, '/MyAgent', 'Value case should be preserved');
|
|
379
|
+
});
|
|
380
|
+
});
|
|
381
|
+
describe('whitespace handling', () => {
|
|
382
|
+
it('should handle whitespace inside marker', () => {
|
|
383
|
+
const text = '<!-- CYCLIST:HANDOFF: /dev -->';
|
|
384
|
+
const result = detectMarkers(text);
|
|
385
|
+
assert.ok(result !== null, 'Should detect marker with whitespace');
|
|
386
|
+
assert.strictEqual(result[0].value, '/dev', 'Value should be trimmed');
|
|
387
|
+
});
|
|
388
|
+
it('should handle newlines around marker', () => {
|
|
389
|
+
const text = '\n\n<!-- CYCLIST:INVOKE:/tea -->\n\n';
|
|
390
|
+
const result = detectMarkers(text);
|
|
391
|
+
assert.ok(result !== null, 'Should detect marker with newlines');
|
|
392
|
+
assert.strictEqual(result[0].type, 'invoke');
|
|
393
|
+
});
|
|
394
|
+
});
|
|
395
|
+
describe('source field', () => {
|
|
396
|
+
it('should set source to structured_marker', () => {
|
|
397
|
+
const text = '<!-- CYCLIST:HANDOFF:/dev -->';
|
|
398
|
+
const result = detectMarkers(text);
|
|
399
|
+
assert.ok(result !== null);
|
|
400
|
+
assert.strictEqual(result[0].source, 'structured_marker');
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
describe('unknown marker types', () => {
|
|
404
|
+
it('should skip unknown marker types', () => {
|
|
405
|
+
const text = '<!-- CYCLIST:UNKNOWN_TYPE:value -->';
|
|
406
|
+
const result = detectMarkers(text);
|
|
407
|
+
assert.strictEqual(result, null, 'Should return null for unknown type');
|
|
408
|
+
});
|
|
409
|
+
it('should still detect valid markers alongside unknown ones', () => {
|
|
410
|
+
const text = '<!-- CYCLIST:INVALID:x --> <!-- CYCLIST:HANDOFF:/dev -->';
|
|
411
|
+
const result = detectMarkers(text);
|
|
412
|
+
assert.ok(result !== null, 'Should detect valid marker');
|
|
413
|
+
assert.strictEqual(result.length, 1);
|
|
414
|
+
assert.strictEqual(result[0].type, 'handoff');
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
//# sourceMappingURL=detect.test.js.map
|