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>
636 lines
22 KiB
JavaScript
636 lines
22 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* create-decision-structure.js — טיוטת מבנה החלטת ועדת ערר
|
||
*
|
||
* מייצר קובץ DOCX מעוצב עם כל חלקי ההחלטה (בלוקים א-יב).
|
||
* בלוקים א-ט ממולאים בתוכן, בלוק י (דיון) ויא (סיכום) = placeholders.
|
||
*
|
||
* שימוש:
|
||
* node create-decision-structure.js <input.json> [output.docx]
|
||
*
|
||
* מבוסס על create-legal-doc.js מתוך legal-docx skill.
|
||
* כללי RTL: START/END (לא LEFT/RIGHT), bidi+bidirectional+rightToLeft בכל רמה.
|
||
*
|
||
* v1.0
|
||
*/
|
||
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
const {
|
||
Document, Packer, Paragraph, TextRun, Header, Footer,
|
||
AlignmentType, HeadingLevel, PageNumber, LevelFormat,
|
||
Table, TableRow, TableCell, WidthType, BorderStyle,
|
||
ShadingType, UnderlineType
|
||
} = require(path.join(process.cwd(), 'node_modules', 'docx'));
|
||
|
||
// ═══════════════════════════════════════════════
|
||
// CONFIGURATION
|
||
// ═══════════════════════════════════════════════
|
||
const FONT = "David";
|
||
const FONT_SIZE = 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); // 1417
|
||
const PAGE_WIDTH = 11906; // A4
|
||
const PAGE_HEIGHT = 16838;
|
||
const CONTENT_WIDTH = PAGE_WIDTH - MARGIN_DXA * 2; // 9072
|
||
|
||
const noBorders = {
|
||
top: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||
bottom: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||
left: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||
right: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" }
|
||
};
|
||
|
||
// ═══════════════════════════════════════════════
|
||
// RTL HELPERS — מבוסס על create-legal-doc.js
|
||
// ═══════════════════════════════════════════════
|
||
|
||
const rtlRun = (text, opts = {}) => new TextRun({
|
||
text,
|
||
font: opts.font || FONT,
|
||
size: opts.size || FONT_SIZE,
|
||
bold: opts.bold || false,
|
||
underline: opts.underline ? { type: UnderlineType.SINGLE } : undefined,
|
||
rightToLeft: true,
|
||
});
|
||
|
||
const rtlPara = (children, opts = {}) => new Paragraph({
|
||
bidirectional: true,
|
||
alignment: opts.alignment || AlignmentType.BOTH,
|
||
spacing: opts.spacing || { after: 120, line: 276 }, // 1.15 line spacing = 276 twips
|
||
indent: opts.indent,
|
||
children: Array.isArray(children) ? children : [children],
|
||
...(opts.heading ? { heading: opts.heading } : {}),
|
||
});
|
||
|
||
// כותרת ראשית — "החלטה"
|
||
const mainTitle = (text) => rtlPara(
|
||
rtlRun(text, { bold: true, size: HEADING1_SIZE }),
|
||
{ heading: HeadingLevel.HEADING_1, alignment: AlignmentType.CENTER, spacing: { before: 240, after: 240 } }
|
||
);
|
||
|
||
// כותרת פרק — "תמצית טענות הצדדים", "דיון והכרעה"
|
||
const sectionTitle = (text) => rtlPara(
|
||
rtlRun(text, { bold: true, size: HEADING2_SIZE, underline: true }),
|
||
{ heading: HeadingLevel.HEADING_2, alignment: AlignmentType.CENTER, spacing: { before: 360, after: 240 } }
|
||
);
|
||
|
||
// כותרת משנה — "טענות העוררים"
|
||
const subTitle = (text) => rtlPara(
|
||
rtlRun(text, { bold: true, size: FONT_SIZE }),
|
||
{ alignment: AlignmentType.CENTER, spacing: { before: 240, after: 160 } }
|
||
);
|
||
|
||
// סעיף ממוספר — מספר bold + טקסט רגיל
|
||
const numberedPara = (num, text) => rtlPara([
|
||
rtlRun(`${num}. `, { bold: true }),
|
||
rtlRun(text),
|
||
], { spacing: { after: 120, line: 276 } });
|
||
|
||
// ציטוט (blockquote) — הזחה משני הצדדים
|
||
const blockquote = (text) => rtlPara(
|
||
rtlRun(text),
|
||
{ indent: { left: 567, right: 567 }, spacing: { before: 120, after: 120, line: 276 } }
|
||
);
|
||
|
||
// תיבת תמונה — מסגרת אפורה עם הנחיה
|
||
const imageBox = (description) => new Paragraph({
|
||
bidirectional: true,
|
||
alignment: AlignmentType.CENTER,
|
||
spacing: { before: 200, after: 200 },
|
||
shading: { type: ShadingType.CLEAR, fill: "F0F0F0" },
|
||
children: [
|
||
new TextRun({
|
||
text: `📷 תמונה: ${description}`,
|
||
font: FONT,
|
||
size: FONT_SIZE,
|
||
rightToLeft: true,
|
||
bold: true,
|
||
}),
|
||
],
|
||
});
|
||
|
||
// Placeholder — טקסט אפור שמסמן מקום
|
||
const placeholder = (text) => rtlPara(
|
||
new TextRun({
|
||
text: `[${text}]`,
|
||
font: FONT,
|
||
size: FONT_SIZE,
|
||
rightToLeft: true,
|
||
italics: true,
|
||
color: "808080",
|
||
}),
|
||
{ alignment: AlignmentType.CENTER, spacing: { before: 200, after: 200 } }
|
||
);
|
||
|
||
// רווח
|
||
const spacer = (after = 200) => rtlPara(rtlRun(""), { spacing: { after, before: 0 } });
|
||
|
||
// תא בטבלה
|
||
const rtlCell = (children, width, opts = {}) => new TableCell({
|
||
borders: noBorders,
|
||
width: { size: width, type: WidthType.DXA },
|
||
children: Array.isArray(children) ? children : [children],
|
||
...(opts.verticalAlign ? { verticalAlign: opts.verticalAlign } : {}),
|
||
});
|
||
|
||
// ═══════════════════════════════════════════════
|
||
// BLOCK BUILDERS — בוני הבלוקים
|
||
// ═══════════════════════════════════════════════
|
||
|
||
// בלוק א — כותרת מוסדית (טבלה 2 טורים)
|
||
function buildInstitutionalHeader(data) {
|
||
const leftCol = CONTENT_WIDTH * 0.5;
|
||
const rightCol = CONTENT_WIDTH * 0.5;
|
||
|
||
// צד ימין — מוסד
|
||
const rightCellContent = [
|
||
rtlPara(rtlRun("מדינת ישראל", { bold: true }), { alignment: AlignmentType.START, spacing: { after: 0 } }),
|
||
rtlPara(rtlRun("ועדת ערר לתכנון ובניה"), { alignment: AlignmentType.START, spacing: { after: 0 } }),
|
||
rtlPara(rtlRun("מחוז ירושלים"), { alignment: AlignmentType.START, spacing: { after: 80 } }),
|
||
];
|
||
|
||
// צד שמאל — מספרי תיק
|
||
const leftLines = [];
|
||
if (data.case_numbers) {
|
||
data.case_numbers.forEach(cn => {
|
||
leftLines.push(rtlPara([
|
||
rtlRun("מס' תיק: "),
|
||
rtlRun(cn, { bold: true }),
|
||
], { alignment: AlignmentType.START, spacing: { after: 0 } }));
|
||
});
|
||
}
|
||
if (data.plan_number) {
|
||
leftLines.push(rtlPara([
|
||
rtlRun("מס' תכנית: "),
|
||
rtlRun(data.plan_number, { bold: true }),
|
||
], { alignment: AlignmentType.START, spacing: { after: 0 } }));
|
||
}
|
||
if (data.request_number) {
|
||
leftLines.push(rtlPara([
|
||
rtlRun("מס' בקשה: "),
|
||
rtlRun(data.request_number, { bold: true }),
|
||
], { alignment: AlignmentType.START, spacing: { after: 0 } }));
|
||
}
|
||
|
||
return new Table({
|
||
visuallyRightToLeft: true,
|
||
width: { size: CONTENT_WIDTH, type: WidthType.DXA },
|
||
columnWidths: [rightCol, leftCol],
|
||
rows: [
|
||
new TableRow({
|
||
children: [
|
||
rtlCell(rightCellContent, rightCol),
|
||
rtlCell(leftLines.length ? leftLines : [rtlPara(rtlRun(""))], leftCol),
|
||
]
|
||
})
|
||
]
|
||
});
|
||
}
|
||
|
||
// בלוק ב — הרכב הוועדה
|
||
function buildPanel(data) {
|
||
const lines = [];
|
||
lines.push(spacer(120));
|
||
lines.push(rtlPara([
|
||
rtlRun("בפני:", { bold: true }),
|
||
], { spacing: { after: 40 } }));
|
||
lines.push(rtlPara([
|
||
rtlRun("יו\"ר הוועדה: ", { bold: true }),
|
||
rtlRun(data.panel?.chair || "עו\"ד דפנה תמיר"),
|
||
], { spacing: { after: 40 } }));
|
||
|
||
if (data.panel?.members) {
|
||
lines.push(rtlPara([
|
||
rtlRun("חברי הוועדה: ", { bold: true }),
|
||
rtlRun(data.panel.members[0] || ""),
|
||
], { spacing: { after: 40 } }));
|
||
for (let i = 1; i < data.panel.members.length; i++) {
|
||
lines.push(rtlPara(rtlRun(data.panel.members[i]), { spacing: { after: 40 } }));
|
||
}
|
||
}
|
||
return lines;
|
||
}
|
||
|
||
// בלוק ג — צדדים
|
||
function buildParties(data) {
|
||
const lines = [];
|
||
lines.push(spacer(120));
|
||
|
||
// עוררים
|
||
if (data.appellants) {
|
||
const label = data.appellants.length > 1 ? "העוררים:" : "העורר:";
|
||
lines.push(rtlPara(rtlRun(label, { bold: true }), { spacing: { after: 40 } }));
|
||
data.appellants.forEach(a => {
|
||
lines.push(rtlPara(rtlRun(a.name), { spacing: { after: 20 } }));
|
||
if (a.representative) {
|
||
lines.push(rtlPara(rtlRun(`ע"י ב"כ ${a.representative}`), { spacing: { after: 20 } }));
|
||
}
|
||
});
|
||
}
|
||
|
||
// נגד
|
||
lines.push(spacer(80));
|
||
lines.push(rtlPara(rtlRun("נגד", { bold: true }), {
|
||
alignment: AlignmentType.CENTER,
|
||
spacing: { before: 80, after: 80 }
|
||
}));
|
||
|
||
// משיבים
|
||
if (data.respondents) {
|
||
lines.push(rtlPara(rtlRun("המשיבים:", { bold: true }), { spacing: { after: 40 } }));
|
||
data.respondents.forEach((r, i) => {
|
||
lines.push(rtlPara(rtlRun(`${i + 1}. ${r.name}`), { spacing: { after: 20 } }));
|
||
if (r.representative) {
|
||
lines.push(rtlPara(rtlRun(`ע"י ב"כ ${r.representative}`), { spacing: { after: 20 } }));
|
||
}
|
||
});
|
||
}
|
||
|
||
return lines;
|
||
}
|
||
|
||
// בלוק ה — פתיחה
|
||
function buildOpening(data) {
|
||
const paras = [];
|
||
if (data.opening_paragraphs) {
|
||
data.opening_paragraphs.forEach((text, i) => {
|
||
paras.push(numberedPara(i + 1, text));
|
||
});
|
||
} else {
|
||
paras.push(numberedPara(1, `לפנינו ${data.appeal_description || "[תיאור הערר]"}.`));
|
||
}
|
||
return paras;
|
||
}
|
||
|
||
// בלוק ו — רקע עובדתי
|
||
function buildBackground(data) {
|
||
const paras = [];
|
||
let num = (data.opening_paragraphs?.length || 1) + 1;
|
||
|
||
if (data.use_petach_davar !== false) {
|
||
paras.push(sectionTitle("פתח דבר"));
|
||
}
|
||
|
||
// מקרקעין
|
||
if (data.property_description) {
|
||
paras.push(numberedPara(num++, data.property_description));
|
||
} else {
|
||
paras.push(numberedPara(num++, "[תיאור המקרקעין — מיקום, שטח, שכונה, ייעוד, מאפיינים ייחודיים]"));
|
||
}
|
||
|
||
// היסטוריה תכנונית
|
||
if (data.planning_history) {
|
||
data.planning_history.forEach(text => {
|
||
paras.push(numberedPara(num++, text));
|
||
});
|
||
} else {
|
||
paras.push(numberedPara(num++, "[היסטוריה תכנונית — תכניות קודמות, החלטות קודמות, היתרים]"));
|
||
}
|
||
|
||
// תמונה — מיקום
|
||
paras.push(imageBox("תשריט מיקום המגרש מתוך מערכת GIS — לסמן את המגרש"));
|
||
|
||
// מהות הבקשה
|
||
if (data.request_essence) {
|
||
if (Array.isArray(data.request_essence)) {
|
||
data.request_essence.forEach(text => {
|
||
paras.push(numberedPara(num++, text));
|
||
});
|
||
} else {
|
||
paras.push(numberedPara(num++, data.request_essence));
|
||
}
|
||
} else {
|
||
paras.push(numberedPara(num++, "[מהות הבקשה — פירוט מלא של מה שהתבקש]"));
|
||
}
|
||
|
||
// תמונה — תשריט
|
||
paras.push(imageBox("תשריט הבקשה / נספח בינוי / תכנית מוצעת"));
|
||
|
||
// ציטוט מפרוטוקול
|
||
if (data.committee_protocol_quote) {
|
||
paras.push(numberedPara(num++, "להלן מתוך פרוטוקול הדיון בוועדה המקומית:"));
|
||
paras.push(blockquote(data.committee_protocol_quote));
|
||
} else {
|
||
paras.push(numberedPara(num++, "[ציטוט מלא מפרוטוקול הוועדה המקומית]"));
|
||
}
|
||
|
||
// החלטת הוועדה + תנאים
|
||
if (data.committee_decision) {
|
||
paras.push(numberedPara(num++, data.committee_decision));
|
||
} else {
|
||
paras.push(numberedPara(num++, "[החלטת הוועדה המקומית — מה הוחלט, אילו תנאים נקבעו]"));
|
||
}
|
||
|
||
// תמונה אופציונלית — סביבה
|
||
if (data.include_aerial_photo !== false) {
|
||
paras.push(imageBox("צילום אוויר / מבט על הסביבה עם סימון המגרש"));
|
||
}
|
||
|
||
// סביבת המקרקעין
|
||
if (data.surroundings) {
|
||
paras.push(numberedPara(num++, data.surroundings));
|
||
}
|
||
|
||
// הגשת הערר
|
||
if (data.appeal_filing) {
|
||
paras.push(numberedPara(num++, data.appeal_filing));
|
||
} else {
|
||
paras.push(numberedPara(num++, "[הגשת הערר — תאריך, מי הגיש, הצטרפויות]"));
|
||
}
|
||
|
||
return { paras, nextNum: num };
|
||
}
|
||
|
||
// בלוק ז — טענות הצדדים
|
||
function buildClaims(data, startNum) {
|
||
const paras = [];
|
||
let num = startNum;
|
||
|
||
paras.push(sectionTitle("תמצית טענות הצדדים"));
|
||
|
||
// טענות העוררים
|
||
if (data.appellant_claims_sections) {
|
||
// מספר עוררים עם כותרות נפרדות
|
||
data.appellant_claims_sections.forEach(section => {
|
||
paras.push(subTitle(section.title));
|
||
section.claims.forEach(text => {
|
||
paras.push(numberedPara(num++, text));
|
||
});
|
||
});
|
||
} else {
|
||
paras.push(subTitle("טענות העוררים"));
|
||
if (data.appellant_claims) {
|
||
data.appellant_claims.forEach(text => {
|
||
paras.push(numberedPara(num++, text));
|
||
});
|
||
} else {
|
||
paras.push(numberedPara(num++, "[טענות העוררים]"));
|
||
}
|
||
}
|
||
|
||
// עמדת הוועדה המקומית
|
||
paras.push(subTitle("עמדת הוועדה המקומית"));
|
||
if (data.committee_position) {
|
||
data.committee_position.forEach(text => {
|
||
paras.push(numberedPara(num++, text));
|
||
});
|
||
} else {
|
||
paras.push(numberedPara(num++, "[עמדת הוועדה המקומית]"));
|
||
}
|
||
|
||
// עמדת מבקשי ההיתר / מגישי התכנית
|
||
const applicantTitle = data.type === "plan"
|
||
? "עמדת מגישי התכנית"
|
||
: "עמדת מבקשי ההיתר";
|
||
paras.push(subTitle(applicantTitle));
|
||
if (data.applicant_position) {
|
||
data.applicant_position.forEach(text => {
|
||
paras.push(numberedPara(num++, text));
|
||
});
|
||
} else {
|
||
paras.push(numberedPara(num++, `[${applicantTitle}]`));
|
||
}
|
||
|
||
return { paras, nextNum: num };
|
||
}
|
||
|
||
// בלוק ח — ההליכים בפני ועדת הערר
|
||
function buildProceedings(data, startNum) {
|
||
const paras = [];
|
||
let num = startNum;
|
||
|
||
paras.push(sectionTitle("ההליכים בפני ועדת הערר"));
|
||
|
||
if (data.proceedings) {
|
||
data.proceedings.forEach(item => {
|
||
if (item.type === "image") {
|
||
paras.push(imageBox(item.description));
|
||
} else if (item.type === "quote") {
|
||
paras.push(numberedPara(num++, item.intro || ""));
|
||
paras.push(blockquote(item.text));
|
||
} else {
|
||
paras.push(numberedPara(num++, item.text));
|
||
}
|
||
});
|
||
} else {
|
||
paras.push(numberedPara(num++, "[דיון — תאריך, נוכחים, עיקרי הדברים]"));
|
||
paras.push(numberedPara(num++, "[סיור (אם היה) — תאריך, תיאור]"));
|
||
paras.push(imageBox("צילומים מהסיור"));
|
||
paras.push(numberedPara(num++, "[החלטות ביניים]"));
|
||
paras.push(numberedPara(num++, "[השלמות טיעון — כרונולוגי]"));
|
||
paras.push(numberedPara(num++, "[חוו\"ד מקצועיות שהתקבלו]"));
|
||
paras.push(imageBox("הדמיות / חתכי בינוי מהשלמות טיעון (אם יש)"));
|
||
}
|
||
|
||
return { paras, nextNum: num };
|
||
}
|
||
|
||
// בלוק ט — תכניות חלות (אופציונלי)
|
||
function buildPlans(data, startNum) {
|
||
if (data.skip_plans_section) return { paras: [], nextNum: startNum };
|
||
|
||
const paras = [];
|
||
let num = startNum;
|
||
|
||
paras.push(sectionTitle("התכניות החלות על המקרקעין"));
|
||
|
||
if (data.applicable_plans) {
|
||
data.applicable_plans.forEach(item => {
|
||
if (item.type === "quote") {
|
||
paras.push(numberedPara(num++, item.intro || ""));
|
||
paras.push(blockquote(item.text));
|
||
} else {
|
||
paras.push(numberedPara(num++, item.text));
|
||
}
|
||
});
|
||
} else {
|
||
paras.push(numberedPara(num++, "[פירוט התכניות הרלוונטיות עם ציטוט מהוראותיהן]"));
|
||
}
|
||
|
||
return { paras, nextNum: num };
|
||
}
|
||
|
||
// בלוק יב — חתימות
|
||
function buildSignatures(data) {
|
||
const chairName = data.panel?.chair || "עו\"ד דפנה תמיר";
|
||
const secretaryName = data.secretary || "";
|
||
|
||
const halfWidth = Math.floor(CONTENT_WIDTH / 2);
|
||
|
||
return [
|
||
spacer(400),
|
||
rtlPara(rtlRun("ניתנה פה אחד, היום ______________, ______________."), {
|
||
spacing: { after: 400 }
|
||
}),
|
||
new Table({
|
||
visuallyRightToLeft: true,
|
||
width: { size: CONTENT_WIDTH, type: WidthType.DXA },
|
||
columnWidths: [halfWidth, halfWidth],
|
||
rows: [
|
||
new TableRow({
|
||
children: [
|
||
rtlCell([
|
||
rtlPara(rtlRun("________________________"), { alignment: AlignmentType.CENTER, spacing: { after: 40 } }),
|
||
rtlPara(rtlRun(chairName, { bold: true }), { alignment: AlignmentType.CENTER, spacing: { after: 20 } }),
|
||
rtlPara(rtlRun("יו\"ר ועדת הערר"), { alignment: AlignmentType.CENTER, spacing: { after: 20 } }),
|
||
], halfWidth),
|
||
rtlCell([
|
||
rtlPara(rtlRun("________________________"), { alignment: AlignmentType.CENTER, spacing: { after: 40 } }),
|
||
rtlPara(rtlRun(secretaryName || ""), { alignment: AlignmentType.CENTER, spacing: { after: 20 } }),
|
||
rtlPara(rtlRun("מזכירת ועדת הערר"), { alignment: AlignmentType.CENTER, spacing: { after: 20 } }),
|
||
], halfWidth),
|
||
]
|
||
})
|
||
]
|
||
}),
|
||
];
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════
|
||
// MAIN — הרכבת המסמך
|
||
// ═══════════════════════════════════════════════
|
||
|
||
function buildDocument(data) {
|
||
const content = [];
|
||
|
||
// בלוק א — כותרת מוסדית
|
||
content.push(buildInstitutionalHeader(data));
|
||
content.push(spacer(160));
|
||
|
||
// בלוק ב — הרכב
|
||
content.push(...buildPanel(data));
|
||
content.push(spacer(80));
|
||
|
||
// בלוק ג — צדדים
|
||
content.push(...buildParties(data));
|
||
content.push(spacer(160));
|
||
|
||
// בלוק ד — כותרת "החלטה"
|
||
content.push(mainTitle("החלטה"));
|
||
|
||
// בלוק ה — פתיחה
|
||
content.push(...buildOpening(data));
|
||
|
||
// בלוק ו — רקע
|
||
const bg = buildBackground(data);
|
||
content.push(...bg.paras);
|
||
|
||
// בלוק ז — טענות
|
||
const claims = buildClaims(data, bg.nextNum);
|
||
content.push(...claims.paras);
|
||
|
||
// בלוק ח — הליכים
|
||
const proc = buildProceedings(data, claims.nextNum);
|
||
content.push(...proc.paras);
|
||
|
||
// בלוק ט — תכניות (אופציונלי)
|
||
const plans = buildPlans(data, proc.nextNum);
|
||
content.push(...plans.paras);
|
||
|
||
// בלוק י — דיון והכרעה (placeholder)
|
||
content.push(sectionTitle("דיון והכרעה"));
|
||
content.push(placeholder("כאן מתחיל פרק הדיון וההכרעה — ייכתב בשלב הבא"));
|
||
|
||
// בלוק יא — סיכום (placeholder)
|
||
content.push(sectionTitle("סיכום"));
|
||
content.push(placeholder("ייכתב לאחר השלמת פרק הדיון"));
|
||
|
||
// בלוק יב — חתימות
|
||
content.push(...buildSignatures(data));
|
||
|
||
return new Document({
|
||
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.CENTER }
|
||
},
|
||
]
|
||
},
|
||
sections: [{
|
||
properties: {
|
||
page: {
|
||
size: { width: PAGE_WIDTH, height: PAGE_HEIGHT },
|
||
margin: { top: MARGIN_DXA, right: MARGIN_DXA, bottom: MARGIN_DXA, left: MARGIN_DXA }
|
||
},
|
||
bidi: true,
|
||
},
|
||
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 }),
|
||
rtlRun(" מתוך ", { size: 18 }),
|
||
new TextRun({ children: [PageNumber.TOTAL_PAGES], font: FONT, size: 18 }),
|
||
]
|
||
})]
|
||
})
|
||
},
|
||
children: content
|
||
}]
|
||
});
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════
|
||
// CLI
|
||
// ═══════════════════════════════════════════════
|
||
|
||
async function main() {
|
||
const inputFile = process.argv[2];
|
||
|
||
if (!inputFile) {
|
||
console.error('שימוש: node create-decision-structure.js <input.json> [output.docx]');
|
||
console.error('');
|
||
console.error('קובץ ה-JSON צריך לכלול:');
|
||
console.error(' case_numbers, panel, appellants, respondents,');
|
||
console.error(' opening_paragraphs, property_description, planning_history,');
|
||
console.error(' request_essence, committee_protocol_quote, committee_decision,');
|
||
console.error(' appellant_claims, committee_position, applicant_position,');
|
||
console.error(' proceedings, applicable_plans');
|
||
console.error('');
|
||
console.error('ראה: .claude/skills/legal-decision/references/decision-template.json');
|
||
process.exit(1);
|
||
}
|
||
|
||
const data = JSON.parse(fs.readFileSync(inputFile, 'utf-8'));
|
||
const outputFile = process.argv[3] || `החלטה-ערר-${(data.case_numbers?.[0] || 'draft').replace(/\//g, '-')}-מבנה.docx`;
|
||
|
||
const doc = buildDocument(data);
|
||
const buffer = await Packer.toBuffer(doc);
|
||
fs.writeFileSync(outputFile, buffer);
|
||
|
||
console.log(`✅ ${outputFile}`);
|
||
console.log(` פונט: ${FONT} ${FONT_SIZE / 2}pt`);
|
||
console.log(` שוליים: ${MARGIN_CM} ס"מ`);
|
||
console.log(` RTL: bidi + bidirectional + rightToLeft ✓`);
|
||
console.log(` Alignment: START/END ✓`);
|
||
console.log(` גודל: ${(buffer.length / 1024).toFixed(1)} KB`);
|
||
}
|
||
|
||
main().catch(err => {
|
||
console.error('❌ שגיאה:', err.message);
|
||
process.exit(1);
|
||
});
|