@shiftleftpt/sbd-toe-mcp 0.1.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/.env.example +35 -0
- package/LICENSE +201 -0
- package/README.md +323 -0
- package/data/publish/algolia_docs_records.json +148847 -0
- package/data/publish/algolia_docs_records_enriched.json +194004 -0
- package/data/publish/algolia_entities_records.json +74715 -0
- package/data/publish/algolia_entities_records_enriched.json +177587 -0
- package/data/publish/algolia_index_settings.json +102 -0
- package/data/publish/sbd-toe-index-compact.json +111 -0
- package/data/reports/run_manifest.json +10 -0
- package/dist/backend/semantic-index-gateway.d.ts +25 -0
- package/dist/backend/semantic-index-gateway.js +555 -0
- package/dist/backend/semantic-index-gateway.js.map +1 -0
- package/dist/backend/semantic-index-gateway.test.d.ts +1 -0
- package/dist/backend/semantic-index-gateway.test.js +384 -0
- package/dist/backend/semantic-index-gateway.test.js.map +1 -0
- package/dist/bootstrap/checkout-backend.d.ts +31 -0
- package/dist/bootstrap/checkout-backend.js +136 -0
- package/dist/bootstrap/checkout-backend.js.map +1 -0
- package/dist/bootstrap/checkout-backend.test.d.ts +1 -0
- package/dist/bootstrap/checkout-backend.test.js +158 -0
- package/dist/bootstrap/checkout-backend.test.js.map +1 -0
- package/dist/bootstrap/release-checkout.d.ts +8 -0
- package/dist/bootstrap/release-checkout.js +168 -0
- package/dist/bootstrap/release-checkout.js.map +1 -0
- package/dist/bootstrap/release-checkout.test.d.ts +1 -0
- package/dist/bootstrap/release-checkout.test.js +137 -0
- package/dist/bootstrap/release-checkout.test.js.map +1 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.js +81 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1063 -0
- package/dist/index.js.map +1 -0
- package/dist/orchestrator/ask-manual.d.ts +13 -0
- package/dist/orchestrator/ask-manual.js +202 -0
- package/dist/orchestrator/ask-manual.js.map +1 -0
- package/dist/prompt/build-answer-prompt.d.ts +2 -0
- package/dist/prompt/build-answer-prompt.js +51 -0
- package/dist/prompt/build-answer-prompt.js.map +1 -0
- package/dist/prompt/system-prompt.d.ts +1 -0
- package/dist/prompt/system-prompt.js +94 -0
- package/dist/prompt/system-prompt.js.map +1 -0
- package/dist/resources/sbd-toe-resources.d.ts +18 -0
- package/dist/resources/sbd-toe-resources.js +164 -0
- package/dist/resources/sbd-toe-resources.js.map +1 -0
- package/dist/resources/sbd-toe-resources.test.d.ts +1 -0
- package/dist/resources/sbd-toe-resources.test.js +134 -0
- package/dist/resources/sbd-toe-resources.test.js.map +1 -0
- package/dist/test-utils.d.ts +153 -0
- package/dist/test-utils.js +176 -0
- package/dist/test-utils.js.map +1 -0
- package/dist/tools/generate-document.d.ts +22 -0
- package/dist/tools/generate-document.js +392 -0
- package/dist/tools/generate-document.js.map +1 -0
- package/dist/tools/generate-document.test.d.ts +1 -0
- package/dist/tools/generate-document.test.js +189 -0
- package/dist/tools/generate-document.test.js.map +1 -0
- package/dist/tools/map-review-scope.d.ts +20 -0
- package/dist/tools/map-review-scope.js +299 -0
- package/dist/tools/map-review-scope.js.map +1 -0
- package/dist/tools/map-review-scope.test.d.ts +1 -0
- package/dist/tools/map-review-scope.test.js +204 -0
- package/dist/tools/map-review-scope.test.js.map +1 -0
- package/dist/tools/plan-repo-governance.d.ts +41 -0
- package/dist/tools/plan-repo-governance.js +509 -0
- package/dist/tools/plan-repo-governance.js.map +1 -0
- package/dist/tools/plan-repo-governance.test.d.ts +1 -0
- package/dist/tools/plan-repo-governance.test.js +237 -0
- package/dist/tools/plan-repo-governance.test.js.map +1 -0
- package/dist/tools/structured-tools.d.ts +5 -0
- package/dist/tools/structured-tools.js +310 -0
- package/dist/tools/structured-tools.js.map +1 -0
- package/dist/tools/structured-tools.test.d.ts +1 -0
- package/dist/tools/structured-tools.test.js +459 -0
- package/dist/tools/structured-tools.test.js.map +1 -0
- package/dist/types.d.ts +160 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/upstream/backend-contract.d.ts +3 -0
- package/dist/upstream/backend-contract.js +37 -0
- package/dist/upstream/backend-contract.js.map +1 -0
- package/dist/validators/ai-disclosure.d.ts +39 -0
- package/dist/validators/ai-disclosure.js +183 -0
- package/dist/validators/ai-disclosure.js.map +1 -0
- package/dist/validators/ai-disclosure.test.d.ts +1 -0
- package/dist/validators/ai-disclosure.test.js +244 -0
- package/dist/validators/ai-disclosure.test.js.map +1 -0
- package/examples/claude-desktop.json +8 -0
- package/examples/vscode.mcp.json +9 -0
- package/package.json +50 -0
- package/prompts/sbd-toe-chat-system.md +71 -0
- package/smithery.yaml +44 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { getConfig } from "../config.js";
|
|
3
|
+
let cachedCheckout;
|
|
4
|
+
function validateCheckout(payload) {
|
|
5
|
+
if (!payload || typeof payload !== "object") {
|
|
6
|
+
throw new Error("O ficheiro de checkout do backend está vazio ou é inválido.");
|
|
7
|
+
}
|
|
8
|
+
const checkout = payload;
|
|
9
|
+
if (!checkout.indices?.docs?.indexName || !checkout.indices?.entities?.indexName) {
|
|
10
|
+
throw new Error("O checkout do backend não contém os índices documentais e estruturados esperados.");
|
|
11
|
+
}
|
|
12
|
+
return checkout;
|
|
13
|
+
}
|
|
14
|
+
export async function tryLoadBackendCheckout() {
|
|
15
|
+
if (cachedCheckout) {
|
|
16
|
+
return cachedCheckout;
|
|
17
|
+
}
|
|
18
|
+
const config = getConfig();
|
|
19
|
+
let rawFile;
|
|
20
|
+
try {
|
|
21
|
+
rawFile = await readFile(config.backend.checkoutFile, "utf8");
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
cachedCheckout = validateCheckout(JSON.parse(rawFile));
|
|
27
|
+
return cachedCheckout;
|
|
28
|
+
}
|
|
29
|
+
export async function loadBackendCheckout() {
|
|
30
|
+
const checkout = await tryLoadBackendCheckout();
|
|
31
|
+
if (!checkout) {
|
|
32
|
+
const config = getConfig();
|
|
33
|
+
throw new Error(`Falta o ficheiro de checkout do backend em "${config.backend.checkoutFile}". Corre "npm run checkout:backend" primeiro.`);
|
|
34
|
+
}
|
|
35
|
+
return checkout;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=backend-contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend-contract.js","sourceRoot":"","sources":["../../src/upstream/backend-contract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGzC,IAAI,cAA2C,CAAC;AAEhD,SAAS,gBAAgB,CAAC,OAAgB;IACxC,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;IACjF,CAAC;IAED,MAAM,QAAQ,GAAG,OAA0B,CAAC;IAC5C,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,SAAS,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;QACjF,MAAM,IAAI,KAAK,CACb,mFAAmF,CACpF,CAAC;IACJ,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB;IAC1C,IAAI,cAAc,EAAE,CAAC;QACnB,OAAO,cAAc,CAAC;IACxB,CAAC;IAED,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAE3B,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IAChE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,cAAc,GAAG,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAY,CAAC,CAAC;IAClE,OAAO,cAAc,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB;IACvC,MAAM,QAAQ,GAAG,MAAM,sBAAsB,EAAE,CAAC;IAChD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CACb,+CAA+C,MAAM,CAAC,OAAO,CAAC,YAAY,+CAA+C,CAC1H,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Disclosure Validation — S8 Governança de Documentos AI-Assisted
|
|
3
|
+
*
|
|
4
|
+
* SbD-ToE Cap. 14 (Governança, Revisões, Conformidade):
|
|
5
|
+
* Artefactos IA-gerados sujeitos a rastreabilidade explícita, revisão humana e validação automática.
|
|
6
|
+
*
|
|
7
|
+
* Formato suportado:
|
|
8
|
+
* - YAML frontmatter (--- AI_ASSISTED: true ... ---)
|
|
9
|
+
* - HTML comment section (<!-- AI-ASSISTED: true, ... -->)
|
|
10
|
+
*
|
|
11
|
+
* Campos obrigatórios: AI_ASSISTED, MODEL, DATE, PURPOSE
|
|
12
|
+
*/
|
|
13
|
+
export declare const ALLOWED_PURPOSES: readonly ["generated-code", "governance-doc", "test-plan", "integration-test", "documentation", "other"];
|
|
14
|
+
export type AllowedPurpose = (typeof ALLOWED_PURPOSES)[number];
|
|
15
|
+
export interface AIDisclosure {
|
|
16
|
+
ai_assisted: boolean;
|
|
17
|
+
model: string;
|
|
18
|
+
date: string;
|
|
19
|
+
purpose: AllowedPurpose;
|
|
20
|
+
reasoning?: string | undefined;
|
|
21
|
+
review_status?: string | undefined;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Extract disclosure from file content
|
|
25
|
+
* Tries YAML frontmatter first, then HTML comment section
|
|
26
|
+
*/
|
|
27
|
+
export declare function extractDisclosure(content: string): Record<string, unknown> | null;
|
|
28
|
+
/**
|
|
29
|
+
* Validate disclosure fields and return structured object
|
|
30
|
+
* Throws on validation failure
|
|
31
|
+
*/
|
|
32
|
+
export declare function validateDisclosure(disclosure: Record<string, unknown>): AIDisclosure;
|
|
33
|
+
/**
|
|
34
|
+
* Main validation function:
|
|
35
|
+
* 1. Extract disclosure from content
|
|
36
|
+
* 2. Validate all fields
|
|
37
|
+
* 3. Return structured AIDisclosure or throw error with field information
|
|
38
|
+
*/
|
|
39
|
+
export declare function validateAIDisclosure(content: string): AIDisclosure;
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Disclosure Validation — S8 Governança de Documentos AI-Assisted
|
|
3
|
+
*
|
|
4
|
+
* SbD-ToE Cap. 14 (Governança, Revisões, Conformidade):
|
|
5
|
+
* Artefactos IA-gerados sujeitos a rastreabilidade explícita, revisão humana e validação automática.
|
|
6
|
+
*
|
|
7
|
+
* Formato suportado:
|
|
8
|
+
* - YAML frontmatter (--- AI_ASSISTED: true ... ---)
|
|
9
|
+
* - HTML comment section (<!-- AI-ASSISTED: true, ... -->)
|
|
10
|
+
*
|
|
11
|
+
* Campos obrigatórios: AI_ASSISTED, MODEL, DATE, PURPOSE
|
|
12
|
+
*/
|
|
13
|
+
export const ALLOWED_PURPOSES = [
|
|
14
|
+
'generated-code',
|
|
15
|
+
'governance-doc',
|
|
16
|
+
'test-plan',
|
|
17
|
+
'integration-test',
|
|
18
|
+
'documentation',
|
|
19
|
+
'other',
|
|
20
|
+
];
|
|
21
|
+
/**
|
|
22
|
+
* Valida formato YYYY-MM-DD
|
|
23
|
+
*/
|
|
24
|
+
function isValidDate(dateStr) {
|
|
25
|
+
const re = /^\d{4}-\d{2}-\d{2}$/;
|
|
26
|
+
if (!re.test(dateStr))
|
|
27
|
+
return false;
|
|
28
|
+
const dt = new Date(dateStr);
|
|
29
|
+
return !Number.isNaN(dt.getTime()) && dt instanceof Date;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Parse YAML frontmatter (--- ... ---)
|
|
33
|
+
*/
|
|
34
|
+
function parseYAMLFrontmatter(content) {
|
|
35
|
+
const trimmed = content.trim();
|
|
36
|
+
if (!trimmed.startsWith('---'))
|
|
37
|
+
return null;
|
|
38
|
+
const endIdx = trimmed.indexOf('---', 3); // busca segundo ---
|
|
39
|
+
if (endIdx === -1)
|
|
40
|
+
return null;
|
|
41
|
+
const yamlBlock = trimmed.slice(3, endIdx).trim();
|
|
42
|
+
if (!yamlBlock)
|
|
43
|
+
return null;
|
|
44
|
+
const result = {};
|
|
45
|
+
for (const line of yamlBlock.split('\n')) {
|
|
46
|
+
const trimLine = line.trim();
|
|
47
|
+
if (!trimLine || trimLine.startsWith('#'))
|
|
48
|
+
continue;
|
|
49
|
+
const colonIdx = trimLine.indexOf(':');
|
|
50
|
+
if (colonIdx === -1)
|
|
51
|
+
continue;
|
|
52
|
+
const key = trimLine.slice(0, colonIdx).trim().toLowerCase().replace(/-/g, '_');
|
|
53
|
+
let rawValue = trimLine.slice(colonIdx + 1).trim();
|
|
54
|
+
let value = rawValue;
|
|
55
|
+
// Parse value
|
|
56
|
+
if (rawValue === 'true') {
|
|
57
|
+
value = true;
|
|
58
|
+
}
|
|
59
|
+
else if (rawValue === 'false') {
|
|
60
|
+
value = false;
|
|
61
|
+
}
|
|
62
|
+
else if (rawValue.startsWith('"') && rawValue.endsWith('"')) {
|
|
63
|
+
value = rawValue.slice(1, -1);
|
|
64
|
+
}
|
|
65
|
+
else if (rawValue.startsWith("'") && rawValue.endsWith("'")) {
|
|
66
|
+
value = rawValue.slice(1, -1);
|
|
67
|
+
}
|
|
68
|
+
result[key] = value;
|
|
69
|
+
}
|
|
70
|
+
return Object.keys(result).length > 0 ? result : null;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Parse HTML comment section (<!-- AI-ASSISTED: true, MODEL: ..., ... -->)
|
|
74
|
+
*/
|
|
75
|
+
function parseHTMLCommentSection(content) {
|
|
76
|
+
const re = /<!--\s*AI-ASSISTED:\s*([^,]+)(.*?)\s*-->/is;
|
|
77
|
+
const match = content.match(re);
|
|
78
|
+
if (!match)
|
|
79
|
+
return null;
|
|
80
|
+
const fullComment = match[0];
|
|
81
|
+
const result = {};
|
|
82
|
+
// Extract AI-ASSISTED: true/false
|
|
83
|
+
const aiValue = (match[1] ?? '').trim().toLowerCase();
|
|
84
|
+
result.ai_assisted = aiValue === 'true' ? true : aiValue === 'false' ? false : null;
|
|
85
|
+
// Parse remaining key:value pairs (MODEL, DATE, PURPOSE, etc.)
|
|
86
|
+
// Pattern: KEY: VALUE (comma-separated)
|
|
87
|
+
const pairs = fullComment
|
|
88
|
+
.replace(/<!--\s*/, '')
|
|
89
|
+
.replace(/\s*-->/g, '')
|
|
90
|
+
.split(',');
|
|
91
|
+
for (let i = 1; i < pairs.length; i++) {
|
|
92
|
+
const pairRaw = pairs[i];
|
|
93
|
+
const pair = pairRaw !== undefined ? pairRaw.trim() : '';
|
|
94
|
+
const colonIdx = pair.indexOf(':');
|
|
95
|
+
if (colonIdx === -1)
|
|
96
|
+
continue;
|
|
97
|
+
const key = pair.slice(0, colonIdx).trim().toLowerCase().replace(/-/g, '_');
|
|
98
|
+
const rawVal = pair.slice(colonIdx + 1).trim();
|
|
99
|
+
let value = rawVal;
|
|
100
|
+
if (rawVal.startsWith('"') && rawVal.endsWith('"')) {
|
|
101
|
+
value = rawVal.slice(1, -1);
|
|
102
|
+
}
|
|
103
|
+
else if (rawVal.startsWith("'") && rawVal.endsWith("'")) {
|
|
104
|
+
value = rawVal.slice(1, -1);
|
|
105
|
+
}
|
|
106
|
+
result[key] = value;
|
|
107
|
+
}
|
|
108
|
+
return Object.keys(result).length > 0 ? result : null;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Extract disclosure from file content
|
|
112
|
+
* Tries YAML frontmatter first, then HTML comment section
|
|
113
|
+
*/
|
|
114
|
+
export function extractDisclosure(content) {
|
|
115
|
+
let disclosure = parseYAMLFrontmatter(content);
|
|
116
|
+
if (disclosure)
|
|
117
|
+
return disclosure;
|
|
118
|
+
disclosure = parseHTMLCommentSection(content);
|
|
119
|
+
if (disclosure)
|
|
120
|
+
return disclosure;
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Validate disclosure fields and return structured object
|
|
125
|
+
* Throws on validation failure
|
|
126
|
+
*/
|
|
127
|
+
export function validateDisclosure(disclosure) {
|
|
128
|
+
// Check AI_ASSISTED
|
|
129
|
+
if (disclosure.ai_assisted !== true) {
|
|
130
|
+
throw new Error('AI_ASSISTED must be true');
|
|
131
|
+
}
|
|
132
|
+
// Check MODEL
|
|
133
|
+
const model = disclosure.model;
|
|
134
|
+
if (!model || typeof model !== 'string') {
|
|
135
|
+
throw new Error('MODEL is required and must be a non-empty string');
|
|
136
|
+
}
|
|
137
|
+
if (model.trim().length === 0) {
|
|
138
|
+
throw new Error('MODEL cannot be empty');
|
|
139
|
+
}
|
|
140
|
+
// Check DATE
|
|
141
|
+
const date = disclosure.date;
|
|
142
|
+
if (!date || typeof date !== 'string') {
|
|
143
|
+
throw new Error('DATE is required and must be a string (format: YYYY-MM-DD)');
|
|
144
|
+
}
|
|
145
|
+
if (!isValidDate(date)) {
|
|
146
|
+
throw new Error(`DATE must be in YYYY-MM-DD format, got: ${date}`);
|
|
147
|
+
}
|
|
148
|
+
// Check PURPOSE
|
|
149
|
+
const purpose = disclosure.purpose;
|
|
150
|
+
if (!purpose || typeof purpose !== 'string') {
|
|
151
|
+
throw new Error('PURPOSE is required and must be a string');
|
|
152
|
+
}
|
|
153
|
+
if (!ALLOWED_PURPOSES.includes(purpose)) {
|
|
154
|
+
throw new Error(`PURPOSE must be one of: ${ALLOWED_PURPOSES.join(', ')}, got: ${purpose}`);
|
|
155
|
+
}
|
|
156
|
+
const reasoningVal = typeof disclosure.reasoning === 'string' ? disclosure.reasoning : undefined;
|
|
157
|
+
const reviewVal = typeof disclosure.review_status === 'string' ? disclosure.review_status : undefined;
|
|
158
|
+
const result = {
|
|
159
|
+
ai_assisted: true,
|
|
160
|
+
model: model.trim(),
|
|
161
|
+
date,
|
|
162
|
+
purpose: purpose,
|
|
163
|
+
};
|
|
164
|
+
if (reasoningVal !== undefined)
|
|
165
|
+
result.reasoning = reasoningVal;
|
|
166
|
+
if (reviewVal !== undefined)
|
|
167
|
+
result.review_status = reviewVal;
|
|
168
|
+
return result;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Main validation function:
|
|
172
|
+
* 1. Extract disclosure from content
|
|
173
|
+
* 2. Validate all fields
|
|
174
|
+
* 3. Return structured AIDisclosure or throw error with field information
|
|
175
|
+
*/
|
|
176
|
+
export function validateAIDisclosure(content) {
|
|
177
|
+
const disclosure = extractDisclosure(content);
|
|
178
|
+
if (!disclosure) {
|
|
179
|
+
throw new Error('No AI disclosure found in document header');
|
|
180
|
+
}
|
|
181
|
+
return validateDisclosure(disclosure);
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=ai-disclosure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-disclosure.js","sourceRoot":"","sources":["../../src/validators/ai-disclosure.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { extractDisclosure, validateDisclosure, validateAIDisclosure, ALLOWED_PURPOSES, } from './ai-disclosure.js';
|
|
3
|
+
describe('AI Disclosure Validator', () => {
|
|
4
|
+
describe('YAML Frontmatter Parsing', () => {
|
|
5
|
+
it('should parse valid YAML frontmatter', () => {
|
|
6
|
+
const content = `---
|
|
7
|
+
ai_assisted: true
|
|
8
|
+
model: Claude Haiku 4.5
|
|
9
|
+
date: 2026-03-24
|
|
10
|
+
purpose: governance-doc
|
|
11
|
+
---
|
|
12
|
+
# Content here`;
|
|
13
|
+
const disclosure = extractDisclosure(content);
|
|
14
|
+
expect(disclosure).not.toBeNull();
|
|
15
|
+
expect(disclosure?.ai_assisted).toBe(true);
|
|
16
|
+
expect(disclosure?.model).toBe('Claude Haiku 4.5');
|
|
17
|
+
expect(disclosure?.date).toBe('2026-03-24');
|
|
18
|
+
expect(disclosure?.purpose).toBe('governance-doc');
|
|
19
|
+
});
|
|
20
|
+
it('should parse YAML with optional fields', () => {
|
|
21
|
+
const content = `---
|
|
22
|
+
ai_assisted: true
|
|
23
|
+
model: Claude 3.5 Sonnet
|
|
24
|
+
date: 2026-01-10
|
|
25
|
+
purpose: generated-code
|
|
26
|
+
reasoning: Auto-generated from template
|
|
27
|
+
review_status: pending-human-review
|
|
28
|
+
---
|
|
29
|
+
Code...`;
|
|
30
|
+
const disclosure = extractDisclosure(content);
|
|
31
|
+
expect(disclosure?.reasoning).toBe('Auto-generated from template');
|
|
32
|
+
expect(disclosure?.review_status).toBe('pending-human-review');
|
|
33
|
+
});
|
|
34
|
+
it('should handle YAML with quoted strings', () => {
|
|
35
|
+
const content = `---
|
|
36
|
+
ai_assisted: true
|
|
37
|
+
model: "Claude Haiku 4.5 (pt-BR)"
|
|
38
|
+
date: "2026-03-24"
|
|
39
|
+
purpose: "governance-doc"
|
|
40
|
+
---
|
|
41
|
+
Content`;
|
|
42
|
+
const disclosure = extractDisclosure(content);
|
|
43
|
+
expect(disclosure?.model).toBe('Claude Haiku 4.5 (pt-BR)');
|
|
44
|
+
});
|
|
45
|
+
it('should ignore YAML comments', () => {
|
|
46
|
+
const content = `---
|
|
47
|
+
# This is a comment
|
|
48
|
+
ai_assisted: true
|
|
49
|
+
model: Claude Haiku 4.5
|
|
50
|
+
date: 2026-03-24
|
|
51
|
+
purpose: test-plan
|
|
52
|
+
# Another comment
|
|
53
|
+
---
|
|
54
|
+
Content`;
|
|
55
|
+
const disclosure = extractDisclosure(content);
|
|
56
|
+
expect(disclosure?.purpose).toBe('test-plan');
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
describe('HTML Comment Section Parsing', () => {
|
|
60
|
+
it('should parse HTML comment disclosure', () => {
|
|
61
|
+
const content = `<!-- AI-ASSISTED: true, MODEL: Claude Haiku 4.5, DATE: 2026-03-24, PURPOSE: governance-doc -->
|
|
62
|
+
# Content here`;
|
|
63
|
+
const disclosure = extractDisclosure(content);
|
|
64
|
+
expect(disclosure?.ai_assisted).toBe(true);
|
|
65
|
+
expect(disclosure?.model).toBe('Claude Haiku 4.5');
|
|
66
|
+
expect(disclosure?.date).toBe('2026-03-24');
|
|
67
|
+
expect(disclosure?.purpose).toBe('governance-doc');
|
|
68
|
+
});
|
|
69
|
+
it('should handle HTML comment with special characters', () => {
|
|
70
|
+
const content = `<!-- AI-ASSISTED: true, MODEL: Claude Haiku 4.5 (pt-BR), DATE: 2026-03-24, PURPOSE: governance-doc + compliance -->
|
|
71
|
+
Content`;
|
|
72
|
+
const disclosure = extractDisclosure(content);
|
|
73
|
+
expect(disclosure?.model).toContain('Claude Haiku');
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
describe('Field Validation', () => {
|
|
77
|
+
it('should reject disclosure without AI_ASSISTED true', () => {
|
|
78
|
+
const raw = {
|
|
79
|
+
ai_assisted: false,
|
|
80
|
+
model: 'Claude',
|
|
81
|
+
date: '2026-03-24',
|
|
82
|
+
purpose: 'test-plan',
|
|
83
|
+
};
|
|
84
|
+
expect(() => validateDisclosure(raw)).toThrow('AI_ASSISTED must be true');
|
|
85
|
+
});
|
|
86
|
+
it('should reject disclosure without MODEL', () => {
|
|
87
|
+
const raw = {
|
|
88
|
+
ai_assisted: true,
|
|
89
|
+
date: '2026-03-24',
|
|
90
|
+
purpose: 'test-plan',
|
|
91
|
+
};
|
|
92
|
+
expect(() => validateDisclosure(raw)).toThrow('MODEL is required');
|
|
93
|
+
});
|
|
94
|
+
it('should reject disclosure with empty MODEL', () => {
|
|
95
|
+
const raw = {
|
|
96
|
+
ai_assisted: true,
|
|
97
|
+
model: ' ',
|
|
98
|
+
date: '2026-03-24',
|
|
99
|
+
purpose: 'test-plan',
|
|
100
|
+
};
|
|
101
|
+
expect(() => validateDisclosure(raw)).toThrow('MODEL cannot be empty');
|
|
102
|
+
});
|
|
103
|
+
it('should reject disclosure without DATE', () => {
|
|
104
|
+
const raw = {
|
|
105
|
+
ai_assisted: true,
|
|
106
|
+
model: 'Claude',
|
|
107
|
+
purpose: 'test-plan',
|
|
108
|
+
};
|
|
109
|
+
expect(() => validateDisclosure(raw)).toThrow('DATE is required');
|
|
110
|
+
});
|
|
111
|
+
it('should reject invalid DATE format', () => {
|
|
112
|
+
const raw = {
|
|
113
|
+
ai_assisted: true,
|
|
114
|
+
model: 'Claude',
|
|
115
|
+
date: '2026/03/24',
|
|
116
|
+
purpose: 'test-plan',
|
|
117
|
+
};
|
|
118
|
+
expect(() => validateDisclosure(raw)).toThrow('DATE must be in YYYY-MM-DD format');
|
|
119
|
+
});
|
|
120
|
+
it('should reject disclosure without PURPOSE', () => {
|
|
121
|
+
const raw = {
|
|
122
|
+
ai_assisted: true,
|
|
123
|
+
model: 'Claude',
|
|
124
|
+
date: '2026-03-24',
|
|
125
|
+
};
|
|
126
|
+
expect(() => validateDisclosure(raw)).toThrow('PURPOSE is required');
|
|
127
|
+
});
|
|
128
|
+
it('should reject PURPOSE not in allowlist', () => {
|
|
129
|
+
const raw = {
|
|
130
|
+
ai_assisted: true,
|
|
131
|
+
model: 'Claude',
|
|
132
|
+
date: '2026-03-24',
|
|
133
|
+
purpose: 'invalid-purpose',
|
|
134
|
+
};
|
|
135
|
+
expect(() => validateDisclosure(raw)).toThrow('PURPOSE must be one of');
|
|
136
|
+
});
|
|
137
|
+
it('should accept all allowed PURPOSE values', () => {
|
|
138
|
+
for (const purpose of ALLOWED_PURPOSES) {
|
|
139
|
+
const raw = {
|
|
140
|
+
ai_assisted: true,
|
|
141
|
+
model: 'Claude',
|
|
142
|
+
date: '2026-03-24',
|
|
143
|
+
purpose,
|
|
144
|
+
};
|
|
145
|
+
const result = validateDisclosure(raw);
|
|
146
|
+
expect(result.purpose).toBe(purpose);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
describe('Full Document Validation', () => {
|
|
151
|
+
it('should validate complete document with YAML disclosure', () => {
|
|
152
|
+
const content = `---
|
|
153
|
+
ai_assisted: true
|
|
154
|
+
model: Claude Haiku 4.5
|
|
155
|
+
date: 2026-03-24
|
|
156
|
+
purpose: governance-doc
|
|
157
|
+
---
|
|
158
|
+
# Document Title
|
|
159
|
+
This is the content.`;
|
|
160
|
+
const disclosure = validateAIDisclosure(content);
|
|
161
|
+
expect(disclosure.ai_assisted).toBe(true);
|
|
162
|
+
expect(disclosure.model).toBe('Claude Haiku 4.5');
|
|
163
|
+
expect(disclosure.date).toBe('2026-03-24');
|
|
164
|
+
expect(disclosure.purpose).toBe('governance-doc');
|
|
165
|
+
});
|
|
166
|
+
it('should validate complete document with HTML comment disclosure', () => {
|
|
167
|
+
const content = `<!-- AI-ASSISTED: true, MODEL: Claude Haiku 4.5, DATE: 2026-03-24, PURPOSE: test-plan -->
|
|
168
|
+
# Test Plan
|
|
169
|
+
This is a test plan.`;
|
|
170
|
+
const disclosure = validateAIDisclosure(content);
|
|
171
|
+
expect(disclosure.purpose).toBe('test-plan');
|
|
172
|
+
});
|
|
173
|
+
it('should throw on missing disclosure', () => {
|
|
174
|
+
const content = `# Content without disclosure
|
|
175
|
+
This document has no AI disclosure.`;
|
|
176
|
+
expect(() => validateAIDisclosure(content)).toThrow('No AI disclosure found in document header');
|
|
177
|
+
});
|
|
178
|
+
it('should throw on malformed disclosure', () => {
|
|
179
|
+
const content = `---
|
|
180
|
+
ai_assisted: true
|
|
181
|
+
model:
|
|
182
|
+
date: 2026-03-24
|
|
183
|
+
purpose: test-plan
|
|
184
|
+
---
|
|
185
|
+
Content`;
|
|
186
|
+
expect(() => validateAIDisclosure(content)).toThrow('MODEL is required');
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
describe('Edge Cases', () => {
|
|
190
|
+
it('should handle YAML with extra whitespace', () => {
|
|
191
|
+
const content = `---
|
|
192
|
+
ai_assisted: true
|
|
193
|
+
model: Claude Haiku 4.5
|
|
194
|
+
date: 2026-03-24
|
|
195
|
+
purpose: test-plan
|
|
196
|
+
---
|
|
197
|
+
Content`;
|
|
198
|
+
const disclosure = extractDisclosure(content);
|
|
199
|
+
expect(disclosure?.model).toBe('Claude Haiku 4.5');
|
|
200
|
+
});
|
|
201
|
+
it('should handle non-existent disclosure gracefully', () => {
|
|
202
|
+
const content = 'Just plain text without any disclosure markers.';
|
|
203
|
+
const disclosure = extractDisclosure(content);
|
|
204
|
+
expect(disclosure).toBeNull();
|
|
205
|
+
});
|
|
206
|
+
it('should reject invalid dates', () => {
|
|
207
|
+
const raw = {
|
|
208
|
+
ai_assisted: true,
|
|
209
|
+
model: 'Claude',
|
|
210
|
+
date: '2026-13-45', // Invalid month and day
|
|
211
|
+
purpose: 'test-plan',
|
|
212
|
+
};
|
|
213
|
+
expect(() => validateDisclosure(raw)).toThrow('DATE must be in YYYY-MM-DD format');
|
|
214
|
+
});
|
|
215
|
+
it('should handle unicode and special characters in MODEL', () => {
|
|
216
|
+
const raw = {
|
|
217
|
+
ai_assisted: true,
|
|
218
|
+
model: 'Claude Haiku 4.5 (pt-BR) — v2.0',
|
|
219
|
+
date: '2026-03-24',
|
|
220
|
+
purpose: 'documentation',
|
|
221
|
+
};
|
|
222
|
+
const result = validateDisclosure(raw);
|
|
223
|
+
expect(result.model).toContain('pt-BR');
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
describe('Return Type Validation', () => {
|
|
227
|
+
it('should return properly typed AIDisclosure object', () => {
|
|
228
|
+
const raw = {
|
|
229
|
+
ai_assisted: true,
|
|
230
|
+
model: 'Claude',
|
|
231
|
+
date: '2026-03-24',
|
|
232
|
+
purpose: 'test-plan',
|
|
233
|
+
};
|
|
234
|
+
const result = validateDisclosure(raw);
|
|
235
|
+
expect(result).toHaveProperty('ai_assisted');
|
|
236
|
+
expect(result).toHaveProperty('model');
|
|
237
|
+
expect(result).toHaveProperty('date');
|
|
238
|
+
expect(result).toHaveProperty('purpose');
|
|
239
|
+
expect(typeof result.ai_assisted).toBe('boolean');
|
|
240
|
+
expect(typeof result.model).toBe('string');
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
//# sourceMappingURL=ai-disclosure.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-disclosure.test.js","sourceRoot":"","sources":["../../src/validators/ai-disclosure.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@shiftleftpt/sbd-toe-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "Apache-2.0",
|
|
5
|
+
"description": "MCP server for SbD-ToE security manual — structured tools for Claude, GitHub Copilot and other MCP clients",
|
|
6
|
+
"keywords": ["mcp", "security", "sbd", "toe", "devsecops", "claude", "copilot", "llm"],
|
|
7
|
+
"homepage": "https://github.com/Shiftleftpt/sbd-toe-mcp-poc#readme",
|
|
8
|
+
"bugs": { "url": "https://github.com/Shiftleftpt/sbd-toe-mcp-poc/issues" },
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/Shiftleftpt/sbd-toe-mcp-poc.git"
|
|
12
|
+
},
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"type": "module",
|
|
17
|
+
"bin": {
|
|
18
|
+
"sbd-toe-mcp": "./dist/index.js"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist/",
|
|
22
|
+
"data/publish/",
|
|
23
|
+
"data/reports/run_manifest.json",
|
|
24
|
+
"prompts/",
|
|
25
|
+
"examples/",
|
|
26
|
+
".env.example",
|
|
27
|
+
"smithery.yaml"
|
|
28
|
+
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsc -p tsconfig.json",
|
|
31
|
+
"dev": "tsc -w -p tsconfig.json",
|
|
32
|
+
"start": "node dist/index.js",
|
|
33
|
+
"check": "tsc --noEmit -p tsconfig.json && node scripts/validate-ai-disclosure.mjs --check",
|
|
34
|
+
"checkout:backend": "tsc -p tsconfig.json && node dist/bootstrap/checkout-backend.js",
|
|
35
|
+
"package:release": "node scripts/package-release.mjs",
|
|
36
|
+
"test": "vitest run",
|
|
37
|
+
"test:watch": "vitest",
|
|
38
|
+
"test:coverage": "vitest run --coverage"
|
|
39
|
+
},
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=20.9.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/node": "^25.5.0",
|
|
45
|
+
"@vitest/coverage-v8": "^1.6.0",
|
|
46
|
+
"@vitest/ui": "^1.6.0",
|
|
47
|
+
"typescript": "^5.9.3",
|
|
48
|
+
"vitest": "^1.6.0"
|
|
49
|
+
}
|
|
50
|
+
}
|