Files
legal-ai/skills/docx/scripts/create-legal-doc.js
Chaim 911c797eb2 Reorganize: skills/ directory + move memory to docs/
skill-legal-decision/ → skills/decision/
skill-legal-assistant/ → skills/assistant/
skill-legal-docx/ → skills/docx/
memory/*.md → docs/

Also removed: TASKS.md (use TaskMaster), classifier.py (replaced by local_classifier.py)
Updated all references in CLAUDE.md, scripts, PRDs, docs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 14:27:07 +00:00

262 lines
9.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* create-legal-doc.js — תבנית בסיסית למסמך משפטי בעברית
*
* שימוש: העתק לתיקיית העבודה, ערוך את CONTENT, הרץ עם node.
*
* הסקריפט כולל את כל הגדרות ה-RTL הנדרשות ב-5 רמות:
* 1. Document defaults (styles)
* 2. Section properties (bidi: true)
* 3. Per-paragraph (bidirectional + alignment)
* 4. Per-run (rightToLeft + font)
* 5. Numbering (alignment: START, not RIGHT!)
*
* שינוי פונט: החלף "David" ב-"FrankRuehl" או "Miriam"
* שינוי שוליים: שנה MARGIN_CM
*
* v3.0 — תיקון באג: AlignmentType.START במקום RIGHT במספור
*/
const fs = require('fs');
const { Document, Packer, Paragraph, TextRun, Header, Footer,
AlignmentType, HeadingLevel, PageNumber, LevelFormat,
Table, TableRow, TableCell, WidthType, BorderStyle,
LineRuleType } = require('docx');
// ═══════════════════════════════════════════════
// CONFIGURATION — שנה כאן
// ═══════════════════════════════════════════════
const FONT = "David";
const FONT_SIZE = 24; // half-points (24 = 12pt)
const HEADING1_SIZE = 32; // 16pt
const HEADING2_SIZE = 28; // 14pt
const MARGIN_CM = 2.5;
const MARGIN_DXA = Math.round(MARGIN_CM / 2.54 * 1440);
const OUTPUT_FILE = "legal-document.docx";
const HEADER_TEXT = ""; // שם המשרד — השאר ריק אם לא צריך
const FOOTER_CONFIDENTIAL = "";
// ═══════════════════════════════════════════════
// HELPERS — פונקציות עזר
// ═══════════════════════════════════════════════
// TextRun עם RTL
const rtlRun = (text, opts = {}) => new TextRun({
text,
...opts,
font: opts.font || FONT,
size: opts.size || FONT_SIZE,
rightToLeft: true, // ← חובה! תמיד אחרון — לא ניתן לדריסה
});
// Paragraph עם RTL
const rtlPara = (children, opts = {}) => new Paragraph({
bidirectional: true, // ← חובה!
alignment: opts.alignment || AlignmentType.BOTH, // ✅ BOTH, לא JUSTIFIED
spacing: opts.spacing,
children: Array.isArray(children) ? children : [children],
...(opts.heading ? { heading: opts.heading } : {}),
...(opts.numbering ? { numbering: opts.numbering } : {}),
});
// כותרת ראשית
const heading1 = (text) => rtlPara(
rtlRun(text, { bold: true, size: HEADING1_SIZE }),
{ heading: HeadingLevel.HEADING_1, alignment: AlignmentType.CENTER }
);
// כותרת משנה
const heading2 = (text) => rtlPara(
rtlRun(text, { bold: true, size: HEADING2_SIZE }),
{ heading: HeadingLevel.HEADING_2, alignment: AlignmentType.START } // ✅ START
);
// רווח
const spacer = (after = 200) => rtlPara(rtlRun(""), { spacing: { after } });
// ═══════════════════════════════════════════════
// CONTENT — ערוך כאן את תוכן המסמך
// ═══════════════════════════════════════════════
const CONTENT = [
// כותרת
heading1("הסכם שירותים משפטיים"),
// תאריך
rtlPara(rtlRun("נערך ונחתם בתל אביב ביום ___________"), { spacing: { after: 200 } }),
// צדדים
heading2("בין הצדדים"),
rtlPara([
rtlRun("מצד אחד: ", { bold: true }),
rtlRun('[שם], ח.פ./ת.ז. [מספר], מ[כתובת] (להלן: "'),
rtlRun("המזמין", { bold: true }),
rtlRun('")'),
], { spacing: { after: 120 } }),
rtlPara([
rtlRun("מצד שני: ", { bold: true }),
rtlRun('[שם], ח.פ./ת.ז. [מספר], מ[כתובת] (להלן: "'),
rtlRun("הספק", { bold: true }),
rtlRun('")'),
], { spacing: { after: 200 } }),
// הואילים
heading2("הואיל:"),
rtlPara(rtlRun("1. והואיל ו[תנאי ראשון];"), { spacing: { after: 120 } }),
rtlPara(rtlRun("2. והואיל ו[תנאי שני];"), { spacing: { after: 120 } }),
rtlPara(rtlRun("3. והואיל והצדדים מעוניינים להסדיר את תנאי ההתקשרות ביניהם;"),
{ spacing: { after: 200 } }),
// מעבר
rtlPara(rtlRun("לפיכך הוסכם, הותנה והוצהר בין הצדדים כדלקמן:", { bold: true }),
{ alignment: AlignmentType.CENTER, spacing: { before: 200, after: 200 } }),
// סעיפים
heading2("1. היקף השירותים"),
rtlPara(rtlRun("1.1 [תוכן הסעיף]"), { spacing: { after: 120 } }),
rtlPara(rtlRun("1.2 [תוכן הסעיף]"), { spacing: { after: 120 } }),
heading2("2. התמורה"),
rtlPara(rtlRun("2.1 [תוכן הסעיף]"), { spacing: { after: 120 } }),
// חתימות
rtlPara(rtlRun("ולראיה באו הצדדים על החתום:", { bold: true }),
{ alignment: AlignmentType.CENTER, spacing: { before: 600, after: 400 } }),
// טבלת חתימות
new Table({
visuallyRightToLeft: true, // ✅ קריטי!
width: { size: 9072, type: WidthType.DXA },
columnWidths: [4536, 4536],
rows: [
new TableRow({
children: [
new TableCell({
borders: {
top: { style: BorderStyle.NONE },
bottom: { style: BorderStyle.NONE },
left: { style: BorderStyle.NONE },
right: { style: BorderStyle.NONE }
},
children: [
rtlPara(rtlRun("________________________"), { alignment: AlignmentType.CENTER }),
rtlPara(rtlRun("המזמין"), { alignment: AlignmentType.CENTER })
]
}),
new TableCell({
borders: {
top: { style: BorderStyle.NONE },
bottom: { style: BorderStyle.NONE },
left: { style: BorderStyle.NONE },
right: { style: BorderStyle.NONE }
},
children: [
rtlPara(rtlRun("________________________"), { alignment: AlignmentType.CENTER }),
rtlPara(rtlRun("הספק"), { alignment: AlignmentType.CENTER })
]
})
]
})
]
})
];
// ═══════════════════════════════════════════════
// DOCUMENT GENERATION — לא לשנות (אלא אם יודע מה עושים)
// ═══════════════════════════════════════════════
const doc = new Document({
// רמה 1: Document defaults
styles: {
default: {
document: {
run: { font: FONT, size: FONT_SIZE, rightToLeft: true },
paragraph: { bidirectional: true, alignment: AlignmentType.BOTH }
}
},
paragraphStyles: [
{
id: "Heading1", name: "Heading 1", basedOn: "Normal", next: "Normal",
quickFormat: true,
run: { size: HEADING1_SIZE, bold: true, font: FONT, rightToLeft: true },
paragraph: { spacing: { before: 240, after: 240 }, outlineLevel: 0,
bidirectional: true, alignment: AlignmentType.CENTER }
},
{
id: "Heading2", name: "Heading 2", basedOn: "Normal", next: "Normal",
quickFormat: true,
run: { size: HEADING2_SIZE, bold: true, font: FONT, rightToLeft: true },
paragraph: { spacing: { before: 200, after: 200 }, outlineLevel: 1,
bidirectional: true, alignment: AlignmentType.START } // ✅ START
},
]
},
// מספור סעיפים
numbering: {
config: [{
reference: "legal-clauses",
levels: [
{
level: 0,
format: LevelFormat.DECIMAL,
text: "%1.",
alignment: AlignmentType.START, // ✅ START — לא RIGHT!
suffix: "tab",
style: { paragraph: { indent: { left: 720, hanging: 360 } } }
},
{
level: 1,
format: LevelFormat.DECIMAL,
text: "%1.%2",
alignment: AlignmentType.START, // ✅ START — לא RIGHT!
suffix: "tab",
style: { paragraph: { indent: { left: 1440, hanging: 500 } } }
},
]
}]
},
sections: [{
// רמה 2: Section properties
properties: {
page: {
size: { width: 11906, height: 16838 }, // A4
margin: { top: MARGIN_DXA, right: MARGIN_DXA, bottom: MARGIN_DXA, left: MARGIN_DXA }
},
bidi: true, // ← חובה!
},
headers: HEADER_TEXT ? {
default: new Header({
children: [rtlPara(rtlRun(HEADER_TEXT, { bold: true, size: 20 }),
{ alignment: AlignmentType.CENTER })]
})
} : undefined,
footers: {
default: new Footer({
children: [new Paragraph({
bidirectional: true,
alignment: AlignmentType.CENTER,
children: [
rtlRun("עמוד ", { size: 18 }),
new TextRun({ children: [PageNumber.CURRENT], font: FONT, size: 18 }),
...(FOOTER_CONFIDENTIAL ? [rtlRun(` | ${FOOTER_CONFIDENTIAL}`, { size: 18 })] : []),
]
})]
})
},
children: CONTENT
}]
});
Packer.toBuffer(doc).then(buffer => {
fs.writeFileSync(OUTPUT_FILE, buffer);
console.log(`${OUTPUT_FILE} created successfully`);
console.log(` Font: ${FONT} ${FONT_SIZE/2}pt`);
console.log(` Margins: ${MARGIN_CM}cm (${MARGIN_DXA} DXA)`);
console.log(` Size: ${(buffer.length / 1024).toFixed(1)} KB`);
console.log(` RTL: bidi + bidirectional + rightToLeft ✓`);
console.log(` Alignment: START/END (not LEFT/RIGHT) ✓`);
});