tavant-docs-mcp 1.0.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/LICENSE +21 -0
- package/assets/bg-agenda-data.jpeg +0 -0
- package/assets/bg-breaker-brain.jpeg +0 -0
- package/assets/bg-breaker-cloud.jpeg +0 -0
- package/assets/bg-breaker-lines.jpeg +0 -0
- package/assets/bg-thankyou.jpeg +0 -0
- package/assets/bg-title-tech.jpeg +0 -0
- package/assets/cr-image1.png +0 -0
- package/assets/decor-cubes.png +0 -0
- package/assets/footer-bar.png +0 -0
- package/assets/tavant-logo-orange.png +0 -0
- package/assets/tavant-logo-small.png +0 -0
- package/assets/tavant-logo-white-sm.png +0 -0
- package/assets/tavant-logo-white.png +0 -0
- package/assets/tavant-template.potx +0 -0
- package/brand.js +21 -0
- package/index.js +172 -0
- package/knowledge/tavant-company.md +181 -0
- package/knowledge/tavant-template.md +61 -0
- package/package.json +32 -0
- package/templates/contract/builders.js +317 -0
- package/templates/contract/register.js +213 -0
- package/templates/contract/sections.js +73 -0
- package/templates/cr/builders.js +286 -0
- package/templates/cr/register.js +189 -0
- package/templates/cr/sections.js +55 -0
- package/templates/msa/builders.js +480 -0
- package/templates/msa/register.js +185 -0
- package/templates/msa/sections.js +86 -0
- package/templates/nda/builders.js +277 -0
- package/templates/nda/register.js +185 -0
- package/templates/nda/sections.js +73 -0
- package/templates/pptx/builders.js +712 -0
- package/templates/pptx/layouts.js +168 -0
- package/templates/pptx/register.js +363 -0
- package/templates/sow/builders.js +294 -0
- package/templates/sow/register.js +183 -0
- package/templates/sow/sections.js +76 -0
- package/test-custom-slide.js +79 -0
- package/test-e2e.js +190 -0
- package/test-msa.js +48 -0
- package/test-nda-cr.js +88 -0
- package/test-pptx.js +93 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
const {
|
|
2
|
+
Document, Packer, Paragraph, TextRun, HeadingLevel,
|
|
3
|
+
AlignmentType, Table, TableRow, TableCell,
|
|
4
|
+
WidthType, BorderStyle, Header, Footer,
|
|
5
|
+
} = require("docx");
|
|
6
|
+
const BRAND = require("../../brand");
|
|
7
|
+
|
|
8
|
+
const FONT = BRAND.font;
|
|
9
|
+
const ORANGE = BRAND.colors.orange;
|
|
10
|
+
|
|
11
|
+
// ─── Reusable helpers ──────────────────────────────────────────────────
|
|
12
|
+
function heading(text, level = HeadingLevel.HEADING_1) {
|
|
13
|
+
return new Paragraph({
|
|
14
|
+
heading: level,
|
|
15
|
+
spacing: { before: 300, after: 150 },
|
|
16
|
+
children: [
|
|
17
|
+
new TextRun({ text, bold: true, font: FONT, size: level === HeadingLevel.HEADING_1 ? 28 : 24, color: ORANGE }),
|
|
18
|
+
],
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function subheading(text) {
|
|
23
|
+
return new Paragraph({
|
|
24
|
+
spacing: { before: 200, after: 100 },
|
|
25
|
+
children: [
|
|
26
|
+
new TextRun({ text, bold: true, font: FONT, size: 22, color: "333333" }),
|
|
27
|
+
],
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function bodyText(text, options = {}) {
|
|
32
|
+
return new Paragraph({
|
|
33
|
+
spacing: { after: 120 },
|
|
34
|
+
children: [
|
|
35
|
+
new TextRun({ text, font: FONT, size: 22, color: "333333", ...options }),
|
|
36
|
+
],
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function bulletItem(text) {
|
|
41
|
+
return new Paragraph({
|
|
42
|
+
bullet: { level: 0 },
|
|
43
|
+
spacing: { after: 80 },
|
|
44
|
+
children: [
|
|
45
|
+
new TextRun({ text, font: FONT, size: 22, color: "333333" }),
|
|
46
|
+
],
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function emptyLine() {
|
|
51
|
+
return new Paragraph({ spacing: { after: 100 }, children: [] });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Simple 2-column table for key-value pairs
|
|
55
|
+
function kvTable(rows) {
|
|
56
|
+
const border = { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" };
|
|
57
|
+
const borders = { top: border, bottom: border, left: border, right: border };
|
|
58
|
+
return new Table({
|
|
59
|
+
width: { size: 100, type: WidthType.PERCENTAGE },
|
|
60
|
+
rows: rows.map(([label, value]) =>
|
|
61
|
+
new TableRow({
|
|
62
|
+
children: [
|
|
63
|
+
new TableCell({
|
|
64
|
+
width: { size: 40, type: WidthType.PERCENTAGE },
|
|
65
|
+
borders,
|
|
66
|
+
children: [new Paragraph({
|
|
67
|
+
spacing: { before: 40, after: 40 },
|
|
68
|
+
children: [new TextRun({ text: label, font: FONT, size: 22, bold: true, color: "333333" })],
|
|
69
|
+
})],
|
|
70
|
+
}),
|
|
71
|
+
new TableCell({
|
|
72
|
+
width: { size: 60, type: WidthType.PERCENTAGE },
|
|
73
|
+
borders,
|
|
74
|
+
children: [new Paragraph({
|
|
75
|
+
spacing: { before: 40, after: 40 },
|
|
76
|
+
children: [new TextRun({ text: value || "", font: FONT, size: 22, color: "333333" })],
|
|
77
|
+
})],
|
|
78
|
+
}),
|
|
79
|
+
],
|
|
80
|
+
})
|
|
81
|
+
),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ─── Section builders ──────────────────────────────────────────────────
|
|
86
|
+
const sectionBuilders = {
|
|
87
|
+
cover_page(data) {
|
|
88
|
+
return [
|
|
89
|
+
emptyLine(), emptyLine(), emptyLine(),
|
|
90
|
+
new Paragraph({
|
|
91
|
+
alignment: AlignmentType.CENTER,
|
|
92
|
+
spacing: { after: 200 },
|
|
93
|
+
children: [
|
|
94
|
+
new TextRun({ text: BRAND.company.toUpperCase(), bold: true, font: FONT, size: 48, color: ORANGE }),
|
|
95
|
+
],
|
|
96
|
+
}),
|
|
97
|
+
emptyLine(),
|
|
98
|
+
new Paragraph({
|
|
99
|
+
alignment: AlignmentType.CENTER,
|
|
100
|
+
spacing: { after: 100 },
|
|
101
|
+
children: [
|
|
102
|
+
new TextRun({ text: "CHANGE REQUEST FORM", bold: true, font: FONT, size: 36, color: "333333" }),
|
|
103
|
+
],
|
|
104
|
+
}),
|
|
105
|
+
emptyLine(),
|
|
106
|
+
new Paragraph({
|
|
107
|
+
alignment: AlignmentType.CENTER,
|
|
108
|
+
spacing: { after: 80 },
|
|
109
|
+
children: [
|
|
110
|
+
new TextRun({ text: data.customer_name || "[Customer Name]", font: FONT, size: 28, color: "666666" }),
|
|
111
|
+
],
|
|
112
|
+
}),
|
|
113
|
+
new Paragraph({
|
|
114
|
+
alignment: AlignmentType.CENTER,
|
|
115
|
+
spacing: { after: 80 },
|
|
116
|
+
children: [
|
|
117
|
+
new TextRun({ text: data.project_name || "[Project Name]", font: FONT, size: 24, color: "666666" }),
|
|
118
|
+
],
|
|
119
|
+
}),
|
|
120
|
+
new Paragraph({
|
|
121
|
+
alignment: AlignmentType.CENTER,
|
|
122
|
+
spacing: { after: 80 },
|
|
123
|
+
children: [
|
|
124
|
+
new TextRun({ text: data.date || "[Date]", font: FONT, size: 22, color: "666666", italics: true }),
|
|
125
|
+
],
|
|
126
|
+
}),
|
|
127
|
+
emptyLine(),
|
|
128
|
+
new Paragraph({
|
|
129
|
+
alignment: AlignmentType.CENTER,
|
|
130
|
+
children: [
|
|
131
|
+
new TextRun({ text: "3945 Freedom Circle, Suite 600, Santa Clara, CA 95054", font: FONT, size: 18, color: "999999" }),
|
|
132
|
+
],
|
|
133
|
+
}),
|
|
134
|
+
emptyLine(),
|
|
135
|
+
new Paragraph({
|
|
136
|
+
alignment: AlignmentType.CENTER,
|
|
137
|
+
children: [
|
|
138
|
+
new TextRun({ text: BRAND.footer, font: FONT, size: 18, color: "999999", italics: true }),
|
|
139
|
+
],
|
|
140
|
+
}),
|
|
141
|
+
];
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
background(data) {
|
|
145
|
+
const coNum = data.co_number || "001";
|
|
146
|
+
return [
|
|
147
|
+
heading(`CHANGE CONTROL CO# ${coNum}`, HeadingLevel.HEADING_2),
|
|
148
|
+
heading("1. Background"),
|
|
149
|
+
bodyText(
|
|
150
|
+
`This Change Order CO# ${coNum}, effective as of ${data.co_effective_date || "[Date]"}, is issued pursuant to that certain Statement of Work titled "${data.project_name || "[Project Name]"}" (the "SOW") entered into between ${data.customer_name || "[Customer Name]"} (hereinafter referred to as "Client" or "Customer") and Tavant Technologies, Inc. ("Tavant").`
|
|
151
|
+
),
|
|
152
|
+
emptyLine(),
|
|
153
|
+
bodyText(
|
|
154
|
+
`The SOW was entered into on ${data.sow_date || "[SOW Date]"} under the Master Services Agreement ("MSA") dated ${data.msa_date || "[MSA Date]"} between the parties.`
|
|
155
|
+
),
|
|
156
|
+
emptyLine(),
|
|
157
|
+
bodyText(
|
|
158
|
+
`This Change Order extends/modifies the SOW with a new end date of ${data.extended_end_date || "[Date]"}.`
|
|
159
|
+
),
|
|
160
|
+
];
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
project_details(data) {
|
|
164
|
+
const coNum = data.co_number || "001";
|
|
165
|
+
const paras = [
|
|
166
|
+
heading("2. Project Details"),
|
|
167
|
+
kvTable([
|
|
168
|
+
["Original SOW Name", data.project_name || "[Project Name]"],
|
|
169
|
+
["Change Order Number", `CO# ${coNum}`],
|
|
170
|
+
["Change Order Name", data.co_name || ""],
|
|
171
|
+
["Change Order Effective Date", data.co_effective_date || ""],
|
|
172
|
+
["Source", data.source || "Tavant"],
|
|
173
|
+
]),
|
|
174
|
+
emptyLine(),
|
|
175
|
+
];
|
|
176
|
+
|
|
177
|
+
// Timeline
|
|
178
|
+
if (data.timeline_description) {
|
|
179
|
+
paras.push(subheading("Timeline"));
|
|
180
|
+
paras.push(bodyText(data.timeline_description));
|
|
181
|
+
paras.push(emptyLine());
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// In Scope
|
|
185
|
+
if (data.in_scope) {
|
|
186
|
+
paras.push(subheading("Additional In Scope Services"));
|
|
187
|
+
const items = Array.isArray(data.in_scope) ? data.in_scope : [data.in_scope];
|
|
188
|
+
items.forEach(item => {
|
|
189
|
+
if (typeof item === "object" && item.category) {
|
|
190
|
+
paras.push(bodyText(item.category, { bold: true }));
|
|
191
|
+
if (item.items && Array.isArray(item.items)) {
|
|
192
|
+
item.items.forEach(sub => paras.push(bulletItem(sub)));
|
|
193
|
+
}
|
|
194
|
+
} else {
|
|
195
|
+
paras.push(bulletItem(String(item)));
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
paras.push(emptyLine());
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Out of Scope
|
|
202
|
+
if (data.out_of_scope) {
|
|
203
|
+
paras.push(subheading("Out of Scope"));
|
|
204
|
+
const items = Array.isArray(data.out_of_scope) ? data.out_of_scope : [data.out_of_scope];
|
|
205
|
+
items.forEach(item => paras.push(bulletItem(String(item))));
|
|
206
|
+
paras.push(emptyLine());
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Assumptions
|
|
210
|
+
if (data.assumptions) {
|
|
211
|
+
paras.push(subheading("Assumptions"));
|
|
212
|
+
const items = Array.isArray(data.assumptions) ? data.assumptions : [data.assumptions];
|
|
213
|
+
items.forEach(item => paras.push(bulletItem(String(item))));
|
|
214
|
+
paras.push(emptyLine());
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return paras;
|
|
218
|
+
},
|
|
219
|
+
|
|
220
|
+
charges(data) {
|
|
221
|
+
return [
|
|
222
|
+
heading("3. Charges"),
|
|
223
|
+
kvTable([
|
|
224
|
+
["Additional costs for the Change Order", data.additional_cost || "$[Amount]"],
|
|
225
|
+
["Completion Date adjustments", data.completion_date || "[Date]"],
|
|
226
|
+
]),
|
|
227
|
+
];
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
invoicing(data) {
|
|
231
|
+
const paras = [
|
|
232
|
+
heading("4. Invoicing Details and Billing Address"),
|
|
233
|
+
];
|
|
234
|
+
if (data.invoice_terms) {
|
|
235
|
+
const terms = Array.isArray(data.invoice_terms) ? data.invoice_terms : [data.invoice_terms];
|
|
236
|
+
terms.forEach(t => paras.push(bulletItem(String(t))));
|
|
237
|
+
} else {
|
|
238
|
+
paras.push(bulletItem("Invoices shall be submitted upon completion of deliverables."));
|
|
239
|
+
paras.push(bulletItem("All amounts are exclusive of applicable taxes."));
|
|
240
|
+
}
|
|
241
|
+
paras.push(emptyLine());
|
|
242
|
+
paras.push(bodyText(`Bill to Address: ${data.bill_to_address || "[Address]"}`));
|
|
243
|
+
return paras;
|
|
244
|
+
},
|
|
245
|
+
|
|
246
|
+
sow_reference(data) {
|
|
247
|
+
const sections = data.sow_sections || "Sections 8, 10 and 11";
|
|
248
|
+
return [
|
|
249
|
+
heading("5. SOW Cross-Reference"),
|
|
250
|
+
bodyText(`${sections} of the SOW are herein adopted in this Change Request.`),
|
|
251
|
+
];
|
|
252
|
+
},
|
|
253
|
+
|
|
254
|
+
counterparts() {
|
|
255
|
+
return [
|
|
256
|
+
heading("6. Counterparts"),
|
|
257
|
+
bodyText(
|
|
258
|
+
`This Change Request may be executed in any number of counterparts, each of which when so executed and delivered shall be deemed an original, and such counterparts together shall constitute one and the same instrument.`
|
|
259
|
+
),
|
|
260
|
+
emptyLine(),
|
|
261
|
+
bodyText(
|
|
262
|
+
`IN WITNESS WHEREOF, the parties hereto have caused this Change Request to be executed by their respective duly authorized representatives as of the date first written above.`
|
|
263
|
+
),
|
|
264
|
+
];
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
signatures(data) {
|
|
268
|
+
return [
|
|
269
|
+
heading("SIGNATURES"),
|
|
270
|
+
emptyLine(),
|
|
271
|
+
bodyText("TAVANT TECHNOLOGIES, INC.", { bold: true }),
|
|
272
|
+
emptyLine(),
|
|
273
|
+
bodyText("By: _________________________________"),
|
|
274
|
+
bodyText(`Title: ${data.tavant_title || "________________________"}`),
|
|
275
|
+
bodyText("Date: ___/___/______"),
|
|
276
|
+
emptyLine(), emptyLine(),
|
|
277
|
+
bodyText(data.customer_name || "[CUSTOMER NAME]", { bold: true }),
|
|
278
|
+
emptyLine(),
|
|
279
|
+
bodyText("By: _________________________________"),
|
|
280
|
+
bodyText(`Title: ${data.customer_title || "________________________"}`),
|
|
281
|
+
bodyText("Date: ___/___/______"),
|
|
282
|
+
];
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
module.exports = sectionBuilders;
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
const { Document, Packer, Header, Footer, Paragraph, TextRun, AlignmentType } = require("docx");
|
|
2
|
+
const { z } = require("zod");
|
|
3
|
+
const { v4: uuidv4 } = require("uuid");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const SECTIONS = require("./sections");
|
|
7
|
+
const sectionBuilders = require("./builders");
|
|
8
|
+
const BRAND = require("../../brand");
|
|
9
|
+
|
|
10
|
+
const changeRequests = new Map();
|
|
11
|
+
|
|
12
|
+
function register(server) {
|
|
13
|
+
server.tool(
|
|
14
|
+
"cr_list_sections",
|
|
15
|
+
"List all available sections for Tavant Change Request (CR) documents with their descriptions and fields",
|
|
16
|
+
{},
|
|
17
|
+
async () => ({
|
|
18
|
+
content: [{
|
|
19
|
+
type: "text",
|
|
20
|
+
text: JSON.stringify(Object.values(SECTIONS).map((s) => ({
|
|
21
|
+
id: s.id, name: s.name, description: s.description, fields: s.fields,
|
|
22
|
+
})), null, 2),
|
|
23
|
+
}],
|
|
24
|
+
})
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
server.tool(
|
|
28
|
+
"cr_create",
|
|
29
|
+
"Create a new Tavant Change Request document. Returns a cr_id. Use cr_add_section to build it, then cr_export to save as .docx",
|
|
30
|
+
{
|
|
31
|
+
customer_name: z.string().optional().describe("Customer/client company name"),
|
|
32
|
+
project_name: z.string().optional().describe("Project or SOW name"),
|
|
33
|
+
co_number: z.string().optional().describe("Change Order number, e.g. '001'"),
|
|
34
|
+
},
|
|
35
|
+
async ({ customer_name, project_name, co_number }) => {
|
|
36
|
+
const id = uuidv4();
|
|
37
|
+
changeRequests.set(id, {
|
|
38
|
+
customer_name: customer_name || "[Customer Name]",
|
|
39
|
+
project_name: project_name || "[Project Name]",
|
|
40
|
+
co_number: co_number || "001",
|
|
41
|
+
sections: [],
|
|
42
|
+
});
|
|
43
|
+
return {
|
|
44
|
+
content: [{
|
|
45
|
+
type: "text",
|
|
46
|
+
text: JSON.stringify({
|
|
47
|
+
cr_id: id,
|
|
48
|
+
customer_name: changeRequests.get(id).customer_name,
|
|
49
|
+
project_name: changeRequests.get(id).project_name,
|
|
50
|
+
message: "Change Request created. Use cr_add_section to add sections, then cr_export to save.",
|
|
51
|
+
}),
|
|
52
|
+
}],
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
server.tool(
|
|
58
|
+
"cr_add_section",
|
|
59
|
+
"Add a section to a Change Request document",
|
|
60
|
+
{
|
|
61
|
+
cr_id: z.string().describe("The CR ID from cr_create"),
|
|
62
|
+
section: z.string().describe(
|
|
63
|
+
"Section ID: cover_page, background, project_details, charges, invoicing, sow_reference, counterparts, signatures"
|
|
64
|
+
),
|
|
65
|
+
data: z.record(z.any()).optional().describe(
|
|
66
|
+
"Section content data. Use cr_list_sections to see fields per section."
|
|
67
|
+
),
|
|
68
|
+
},
|
|
69
|
+
async ({ cr_id, section, data }) => {
|
|
70
|
+
const cr = changeRequests.get(cr_id);
|
|
71
|
+
if (!cr) return { content: [{ type: "text", text: "Error: Change Request not found." }], isError: true };
|
|
72
|
+
const builder = sectionBuilders[section];
|
|
73
|
+
if (!builder) {
|
|
74
|
+
return {
|
|
75
|
+
content: [{ type: "text", text: `Error: Unknown section "${section}". Available: ${Object.keys(SECTIONS).join(", ")}` }],
|
|
76
|
+
isError: true,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
cr.sections.push({ section, data: data || {} });
|
|
80
|
+
return {
|
|
81
|
+
content: [{
|
|
82
|
+
type: "text",
|
|
83
|
+
text: JSON.stringify({ message: `Section added: ${section}`, total_sections: cr.sections.length }),
|
|
84
|
+
}],
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
server.tool(
|
|
90
|
+
"cr_export",
|
|
91
|
+
"Export Change Request as a .docx Word document",
|
|
92
|
+
{
|
|
93
|
+
cr_id: z.string().describe("The CR ID"),
|
|
94
|
+
output_path: z.string().optional().describe("Output file path. Defaults to ./output/CR_<project>.docx"),
|
|
95
|
+
},
|
|
96
|
+
async ({ cr_id, output_path }) => {
|
|
97
|
+
const cr = changeRequests.get(cr_id);
|
|
98
|
+
if (!cr) return { content: [{ type: "text", text: "Error: Change Request not found." }], isError: true };
|
|
99
|
+
|
|
100
|
+
const children = [];
|
|
101
|
+
for (const { section, data } of cr.sections) {
|
|
102
|
+
const builder = sectionBuilders[section];
|
|
103
|
+
if (builder) {
|
|
104
|
+
const paragraphs = builder({
|
|
105
|
+
...data,
|
|
106
|
+
customer_name: data.customer_name || cr.customer_name,
|
|
107
|
+
project_name: data.project_name || cr.project_name,
|
|
108
|
+
co_number: data.co_number || cr.co_number,
|
|
109
|
+
});
|
|
110
|
+
children.push(...paragraphs);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const doc = new Document({
|
|
115
|
+
creator: "Tavant",
|
|
116
|
+
title: `Change Request CO# ${cr.co_number} - ${cr.project_name}`,
|
|
117
|
+
description: `Change Request for ${cr.project_name}`,
|
|
118
|
+
styles: {
|
|
119
|
+
default: {
|
|
120
|
+
document: {
|
|
121
|
+
run: { font: BRAND.font, size: 22, color: "333333" },
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
sections: [{
|
|
126
|
+
properties: {
|
|
127
|
+
page: { margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 } },
|
|
128
|
+
},
|
|
129
|
+
headers: {
|
|
130
|
+
default: new Header({
|
|
131
|
+
children: [
|
|
132
|
+
new Paragraph({
|
|
133
|
+
alignment: AlignmentType.RIGHT,
|
|
134
|
+
children: [
|
|
135
|
+
new TextRun({ text: BRAND.footer, font: BRAND.font, size: 16, color: "999999", italics: true }),
|
|
136
|
+
],
|
|
137
|
+
}),
|
|
138
|
+
],
|
|
139
|
+
}),
|
|
140
|
+
},
|
|
141
|
+
footers: {
|
|
142
|
+
default: new Footer({
|
|
143
|
+
children: [
|
|
144
|
+
new Paragraph({
|
|
145
|
+
alignment: AlignmentType.CENTER,
|
|
146
|
+
children: [
|
|
147
|
+
new TextRun({
|
|
148
|
+
text: `CR CO# ${cr.co_number} | ${cr.project_name} | ${BRAND.company} & ${cr.customer_name} Confidential`,
|
|
149
|
+
font: BRAND.font, size: 16, color: "999999",
|
|
150
|
+
}),
|
|
151
|
+
],
|
|
152
|
+
}),
|
|
153
|
+
],
|
|
154
|
+
}),
|
|
155
|
+
},
|
|
156
|
+
children,
|
|
157
|
+
}],
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const buffer = await Packer.toBuffer(doc);
|
|
161
|
+
const sanitized = (cr.project_name || "CR").replace(/[^a-zA-Z0-9_-]/g, "_").substring(0, 50);
|
|
162
|
+
const defaultDir = path.join(process.cwd(), "output");
|
|
163
|
+
if (!fs.existsSync(defaultDir)) fs.mkdirSync(defaultDir, { recursive: true });
|
|
164
|
+
const filePath = output_path || path.join(defaultDir, `CR_${sanitized}.docx`);
|
|
165
|
+
const dir = path.dirname(filePath);
|
|
166
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
167
|
+
fs.writeFileSync(filePath, buffer);
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
content: [{
|
|
171
|
+
type: "text",
|
|
172
|
+
text: JSON.stringify({ message: "Change Request exported", file_path: filePath, total_sections: cr.sections.length }),
|
|
173
|
+
}],
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
server.tool(
|
|
179
|
+
"cr_delete",
|
|
180
|
+
"Delete a Change Request from memory",
|
|
181
|
+
{ cr_id: z.string().describe("The CR ID") },
|
|
182
|
+
async ({ cr_id }) => {
|
|
183
|
+
if (changeRequests.delete(cr_id)) return { content: [{ type: "text", text: "Change Request deleted." }] };
|
|
184
|
+
return { content: [{ type: "text", text: "Not found." }], isError: true };
|
|
185
|
+
}
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
module.exports = { register };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Change Request (CR) template section definitions — extracted from Tavant CR Template
|
|
2
|
+
// 8 sections matching the real corporate Change Request form structure
|
|
3
|
+
|
|
4
|
+
const SECTIONS = {
|
|
5
|
+
cover_page: {
|
|
6
|
+
id: "cover_page",
|
|
7
|
+
name: "Cover Page",
|
|
8
|
+
description: "Dark branded cover with CHANGE REQUEST FORM title, customer name, project name, date, and Tavant address",
|
|
9
|
+
fields: ["customer_name", "project_name", "date"],
|
|
10
|
+
},
|
|
11
|
+
background: {
|
|
12
|
+
id: "background",
|
|
13
|
+
name: "1. Background",
|
|
14
|
+
description: "Legal context: references the original SOW, MSA, Change Order number, parties, and dates",
|
|
15
|
+
fields: ["co_number", "project_name", "customer_name", "sow_date", "msa_date", "extended_end_date"],
|
|
16
|
+
},
|
|
17
|
+
project_details: {
|
|
18
|
+
id: "project_details",
|
|
19
|
+
name: "2. Project Details",
|
|
20
|
+
description: "Table with Original SOW Name, CO number, CO Name, CO Effective Date, Source. Plus sub-sections: Timeline, In Scope, Out of Scope, Assumptions",
|
|
21
|
+
fields: ["co_number", "co_name", "co_effective_date", "project_name", "source", "timeline_description", "in_scope", "out_of_scope", "assumptions"],
|
|
22
|
+
},
|
|
23
|
+
charges: {
|
|
24
|
+
id: "charges",
|
|
25
|
+
name: "3. Charges",
|
|
26
|
+
description: "Table with additional costs for the Change Order and completion date adjustments",
|
|
27
|
+
fields: ["additional_cost", "completion_date"],
|
|
28
|
+
},
|
|
29
|
+
invoicing: {
|
|
30
|
+
id: "invoicing",
|
|
31
|
+
name: "4. Invoicing Details & Billing",
|
|
32
|
+
description: "Invoice timing, tax exclusion note, and bill-to address",
|
|
33
|
+
fields: ["invoice_terms", "bill_to_address"],
|
|
34
|
+
},
|
|
35
|
+
sow_reference: {
|
|
36
|
+
id: "sow_reference",
|
|
37
|
+
name: "5. SOW Cross-Reference",
|
|
38
|
+
description: "Clause adopting specific SOW sections (8, 10, 11) into this Change Request",
|
|
39
|
+
fields: ["sow_sections"],
|
|
40
|
+
},
|
|
41
|
+
counterparts: {
|
|
42
|
+
id: "counterparts",
|
|
43
|
+
name: "6. Counterparts & Execution",
|
|
44
|
+
description: "Legal boilerplate: counterpart execution clause and 'In witness whereof'",
|
|
45
|
+
fields: [],
|
|
46
|
+
},
|
|
47
|
+
signatures: {
|
|
48
|
+
id: "signatures",
|
|
49
|
+
name: "Signatures",
|
|
50
|
+
description: "Side-by-side signature blocks for Tavant Technologies Inc. and Customer with By, Title, Date fields",
|
|
51
|
+
fields: ["customer_name", "customer_signatory", "customer_title", "tavant_signatory", "tavant_title"],
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
module.exports = SECTIONS;
|