Letter Writer について
Letter Writerは、ビジネスや個人向けの手紙を作成するスキルです。応募書類、業務連絡、推薦状、礼状などに対応し、テンプレートの調整や一括生成、PDF・DOCX・HTML出力ができます。
主な機能
- 01複数種類の手紙を作成
- 02テンプレートと文調の調整
- 03宛先別の一括生成
- 04PDF・DOCX・HTMLへの出力
活用シーン
- 01応募用カバーレターの作成
- 02正式なビジネス文書の準備
- 03複数の宛先向け手紙の生成
- 04礼状や推薦状の作成
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ eddiebe147/ claude-settings --skill letter-writerインストール後、Claude Code で/letter-writerを入力して読み込まれたか確認します。
詳細
レター作成
レター作成スキルは、適切な書式、語調、構成で、ビジネス用および個人用のレターを自動作成します。ビジネス文書、カバーレター、推薦状、お礼状、正式な連絡文書など、さまざまな種類のレターに対応します。対象者や目的に合わせた適切な書式、プロフェッショナルな表現、カスタマイズを実現します。
さまざまな用途やコミュニケーションのニーズに合わせてカスタマイズできるテンプレートを使用し、PDF、DOCX、HTML 形式のレターを生成します。
主なワークフロー
ワークフロー 1:カバーレターの作成
目的: 特定の求人応募に合わせた、説得力のあるカバーレターを作成します。
手順:
- 求人の詳細(職種、会社、応募要件)を収集します
- 候補者の関連する資格や経験を抽出します
- 適切なビジネス書式でレターを構成します
- 読み手を引き付ける書き出しの段落を作成します
- 関連する経験と実績を詳しく述べます
- スキルと求人要件を結び付けます
- 次の行動を促す、力強い結びの言葉を作成します
- 適切な行間と余白で書式を整えます
- PDF と DOCX 形式で出力します
実装:
const { Document, Packer, Paragraph, AlignmentType, TextRun } = require('docx');
const fs = require('fs');
async function generateCoverLetter(letterData, outputPath) {
const doc = new Document({
sections: [{
properties: {
page: {
margin: {
top: 1440, // 1 inch
right: 1440,
bottom: 1440,
left: 1440
}
}
},
children: [
// Your contact information
new Paragraph({
text: letterData.sender.name,
spacing: { after: 0 }
}),
new Paragraph({
text: letterData.sender.address,
spacing: { after: 0 }
}),
new Paragraph({
text: `${letterData.sender.city}, ${letterData.sender.state} ${letterData.sender.zip}`,
spacing: { after: 0 }
}),
new Paragraph({
text: letterData.sender.email,
spacing: { after: 0 }
}),
new Paragraph({
text: letterData.sender.phone,
spacing: { after: 200 }
}),
// Date
new Paragraph({
text: new Date().toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
}),
spacing: { after: 200 }
}),
// Recipient information
new Paragraph({
text: letterData.recipient.name || 'Hiring Manager',
spacing: { after: 0 }
}),
new Paragraph({
text: letterData.recipient.company,
spacing: { after: 0 }
}),
new Paragraph({
text: letterData.recipient.address,
spacing: { after: 0 }
}),
new Paragraph({
text: `${letterData.recipient.city}, ${letterData.recipient.state} ${letterData.recipient.zip}`,
spacing: { after: 200 }
}),
// Salutation
new Paragraph({
text: `Dear ${letterData.recipient.name || 'Hiring Manager'}:`,
spacing: { after: 200 }
}),
// Opening paragraph
new Paragraph({
text: `I am writing to express my strong interest in the ${letterData.position} position at ${letterData.recipient.company}. ${letterData.opening}`,
alignment: AlignmentType.JUSTIFIED,
spacing: { after: 200 }
}),
// Body paragraphs
...letterData.bodyParagraphs.map(para =>
new Paragraph({
text: para,
alignment: AlignmentType.JUSTIFIED,
spacing: { after: 200 }
})
),
// Closing paragraph
new Paragraph({
text: letterData.closing || `Thank you for considering my application. I am excited about the opportunity to contribute to ${letterData.recipient.company} and would welcome the chance to discuss how my skills and experience align with your needs. I look forward to hearing from you.`,
alignment: AlignmentType.JUSTIFIED,
spacing: { after: 200 }
}),
// Signature
new Paragraph({
text: 'Sincerely,',
spacing: { after: 400 }
}),
new Paragraph({
text: letterData.sender.name
})
]
}]
});
const buffer = await Packer.toBuffer(doc);
fs.writeFileSync(outputPath, buffer);
return outputPath;
}
// Helper function to generate body paragraphs based on job requirements
function generateCoverLetterBody(candidateData, jobData) {
const paragraphs = [];
// Experience paragraph
const relevantExperience = candidateData.experience
.filter(exp => exp.relevanceScore > 0.7)
.slice(0, 2);
if (relevantExperience.length > 0) {
const expText = `With ${calculateYearsExperience(candidateData.experience)} years of experience in ${jobData.industry}, I have developed strong expertise in ${jobData.keySkills.slice(0, 3).join(', ')}. In my current role at ${relevantExperience[0].company}, ${relevantExperience[0].achievements[0].toLowerCase()}`;
paragraphs.push(expText);
}
// Skills and qualifications paragraph
const matchingSkills = candidateData.skills
.filter(skill => jobData.requiredSkills.includes(skill.name))
.slice(0, 4);
if (matchingSkills.length > 0) {
const skillsText = `I am particularly well-suited for this role given my proficiency in ${matchingSkills.map(s => s.name).join(', ')}. ${candidateData.achievements.find(a => a.includes(matchingSkills[0].name)) || 'I have successfully applied these skills to deliver measurable results.'}`;
paragraphs.push(skillsText);
}
// Company-specific paragraph
if (jobData.companyInfo) {
const companyText = `I am particularly drawn to ${jobData.company} because of ${jobData.companyInfo.appeal || 'your reputation for innovation and excellence'}. I believe my background in ${jobData.relevantBackground} would enable me to make immediate contributions to your team.`;
paragraphs.push(companyText);
}
return paragraphs;
}
ワークフロー 2:ビジネスレターの作成
目的: 適切な書式で正式なビジネス文書を作成します。
手順:
- レターの種類(問い合わせ、苦情、提案など)を選択します
- ビジネスレターの構成に沿って書式を整えます
- プロフェッショナルな語調と表現を使用します
- 必要に応じて件名を記載します
- 明確で簡潔な内容を作成します
- 適切な結びの言葉と署名欄を追加します
- 利用可能な場合は、レターヘッドに合わせて書式を整えます
実装:
async function generateBusinessLetter(letterData, outputPath) {
const letterTypes = {
inquiry: {
subject: 'Inquiry Regarding',
opening: 'I am writing to inquire about',
tone: 'polite and professional'
},
complaint: {
subject: 'Concern Regarding',
opening: 'I am writing to bring to your attention',
tone: 'firm but professional'
},
proposal: {
subject: 'Proposal for',
opening: 'I am pleased to present',
tone: 'professional and enthusiastic'
},
thankyou: {
subject: 'Thank You',
opening: 'I wanted to express my sincere gratitude for',
tone: 'warm and appreciative'
}
};
const template = letterTypes[letterData.type] || letterTypes.inquiry;
const doc = new Document({
sections: [{
properties: {
page: {
margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 }
}
},
children: [
// Company letterhead (if provided)
...(letterData.letterhead ? [
new Paragraph({
text: letterData.letterhead.companyName,
alignment: AlignmentType.CENTER,
spacing: { after: 0 }
}),
new Paragraph({
text: letterData.letterhead.address,
alignment: AlignmentType.CENTER,
spacing: { after: 0 }
}),
new Paragraph({
text: `${letterData.letterhead.phone} | ${letterData.letterhead.email}`,
alignment: AlignmentType.CENTER,
spacing: { after: 400 }
})
] : []),
// Date
new Paragraph({
text: new Date().toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
}),
spacing: { after: 200 }
}),
// Recipient
new Paragraph({
text: letterData.recipient.name,
spacing: { after: 0 }
}),
new Paragraph({
text: letterData.recipient.title,
spacing: { after: 0 }
}),
new Paragraph({
text: letterData.recipient.company,
spacing: { after: 0 }
}),
new Paragraph({
text: letterData.recipient.address,
spacing: { after: 200 }
}),
// Subject line
...(letterData.subject ? [
new Paragraph({
children: [
new TextRun({ text: 'RE: ', bold: true }),
new TextRun(letterData.subject)
],
spacing: { after: 200 }
})
] : []),
// Salutation
new Paragraph({
text: `Dear ${letterData.recipient.name}:`,
spacing: { after: 200 }
}),
// Body
...letterData.paragraphs.map(para =>
new Paragraph({
text: para,
alignment: AlignmentType.JUSTIFIED,
spacing: { after: 200 }
})
),
// Closing
new Paragraph({
text: letterData.closing || 'Sincerely,',
spacing: { after: 400 }
}),
// Signature
new Paragraph({
text: letterData.sender.name,
spacing: { after: 0 }
}),
new Paragraph({
text: letterData.sender.title
})
]
}]
});
const buffer = await Packer.toBuffer(doc);
fs.writeFileSync(outputPath, buffer);
return outputPath;
}
ワークフロー 3:推薦状の作成
目的: 従業員、学生、同僚の推薦状を作成します。
手順:
- 推薦する人物についての情報を収集します
- レターの目的と宛先を確認します
- 推薦者としての信頼性と関係性を明確にします
- 具体的な資格や実績を詳しく述べます
- 具体例を提示します
- 適切な場合は、同僚などと比較します
- 熱意を込めて推薦します
- 追加情報を提供できることを伝えます
実装:
function generateRecommendationLetter(recommendationData) {
const letterData = {
sender: recommendationData.recommender,
recipient: recommendationData.recipient || {
name: 'To Whom It May Concern'
},
type: 'recommendation',
paragraphs: [
// Opening - establish relationship
`I am writing to enthusiastically recommend ${recommendationData.candidate.name} for ${recommendationData.purpose}. I have had the pleasure of working with ${recommendationData.candidate.firstName} for ${recommendationData.duration} in my capacity as ${recommendationData.recommender.title} at ${recommendationData.recommender.company}.`,
// Qualifications and strengths
`During our time working together, ${recommendationData.candidate.firstName} has consistently demonstrated exceptional ${recommendationData.strengths.join(', ')}. ${recommendationData.standoutQualities}`,
// Specific examples
...recommendationData.examples.map(example =>
`For instance, ${example.description} This ${example.impact}`
),
// Comparison to peers (if applicable)
...(recommendationData.comparison ? [
`${recommendationData.candidate.firstName} stands out among ${recommendationData.comparison.peerGroup} for ${recommendationData.comparison.distinguishingFactors}.`
] : []),
// Strong endorsement
`I am confident that ${recommendationData.candidate.firstName} will be an outstanding ${recommendationData.targetRole} and will bring the same level of dedication, skill, and enthusiasm that I have witnessed firsthand. ${recommendationData.candidate.pronoun} has my highest recommendation without reservation.`,
// Offer additional information
`Please feel free to contact me at ${recommendationData.recommender.phone} or ${recommendationData.recommender.email} if you would like to discuss ${recommendationData.candidate.firstName}'s qualifications further.`
]
};
return generateBusinessLetter(letterData, recommendationData.outputPath);
}
ワークフロー 4:お礼状の作成
目的: さまざまな場面に応じた、プロフェッショナルなお礼状を作成します。
手順:
- 場面(面接、贈り物、会議など)を特定します
- 具体的に感謝の気持ちを伝えます
- やり取りの中での重要な詳細に触れます
- 関心や関係性をあらためて伝えます
- 簡潔ながらも誠意のある内容にします
- 温かみがありながらもプロフェッショナルな語調を用います
- 媒体に適した書式に整えます
実装:
function generateThankYouLetter(thankYouData) {
const templates = {
interview: {
opening: `Thank you for taking the time to meet with me ${thankYouData.when} to discuss the ${thankYouData.position} position at ${thankYouData.company}.`,
body: `I enjoyed learning more about ${thankYouData.discussionTopics} and was particularly excited to hear about ${thankYouData.highlight}. Our conversation reinforced my strong interest in joining your team and contributing to ${thankYouData.contribution}.`,
closing: `I am very enthusiastic about this opportunity and believe my ${thankYouData.relevantSkills} would be a strong fit for your needs. Thank you again for your time and consideration. I look forward to hearing from you.`
},
meeting: {
opening: `Thank you for meeting with me on ${thankYouData.date} to discuss ${thankYouData.topic}.`,
body: `I found our conversation about ${thankYouData.keyPoints} very valuable. Your insights regarding ${thankYouData.insight} gave me a new perspective on ${thankYouData.subject}.`,
closing: `I appreciate your time and expertise. I look forward to our continued collaboration.`
},
gift: {
opening: `Thank you so much for the thoughtful ${thankYouData.gift}.`,
body: `Your generosity and thoughtfulness are greatly appreciated. ${thankYouData.personalNote}`,
closing: `Thank you again for thinking of me. Your kindness means a lot.`
},
networking: {
opening: `It was a pleasure meeting you at ${thankYouData.event} on ${thankYouData.date}.`,
body: `I enjoyed our conversation about ${thankYouData.topic} and found your perspective on ${thankYouData.insight} particularly interesting. I would love to stay connected and explore opportunities for ${thankYouData.futureCollaboration}.`,
closing: `Thank you for taking the time to speak with me. I've connected with you on LinkedIn and look forward to staying in touch.`
}
};
const template = templates[thankYouData.type] || templates.meeting;
return {
sender: thankYouData.sender,
recipient: thankYouData.recipient,
subject: `Thank you - ${thankYouData.regarding}`,
paragraphs: [
template.opening,
template.body,
template.closing
],
closing: 'Best regards,'
};
}
ワークフロー 5:レターの一括生成
目的: テンプレートとデータソースを使って、複数の宛先に合わせたレターを作成します。
手順:
- プレースホルダーを含むレターテンプレートを読み込みます
- 宛先データ(CSV、JSON、データベース)を読み込みます
- 宛先ごとに、以下を実行します:
- データをテンプレートに反映します
- 内容を宛先に合わせて調整します
- レターを生成します
- 固有のファイル名で保存します
- 生成の進捗を記録します
- 概要レポートを作成します
実装:
const Handlebars = require('handlebars');
async function batchGenerateLetters(templatePath, recipientsData, outputDir) {
const template = fs.readFileSync(templatePath, 'utf8');
const compiledTemplate = Handlebars.compile(template);
const results = [];
for (const recipient of recipientsData) {
try {
// Merge recipient data with template
const letterContent = compiledTemplate(recipient);
// Generate filename
const filename = `${outputDir}/letter-${recipient.id}-${recipient.name.replace(/\s+/g, '-')}.docx`;
// Create letter data
const letterData = {
sender: recipient.sender || recipientsData.defaultSender,
recipient: {
name: recipient.name,
title: recipient.title,
company: recipient.company,
address: recipient.address
},
paragraphs: letterContent.split('\n\n').filter(p => p.trim()),
subject: recipient.subject
};
// Generate letter
await generateBusinessLetter(letterData, filename);
results.push({
success: true,
recipientId: recipient.id,
name: recipient.name,
filename: filename
});
} catch (error) {
results.push({
success: false,
recipientId: recipient.id,
name: recipient.name,
error: error.message
});
}
}
// Generate summary report
const summary = {
total: recipientsData.length,
successful: results.filter(r => r.success).length,
failed: results.filter(r => !r.success).length,
results: results
};
fs.writeFileSync(
`${outputDir}/batch-summary.json`,
JSON.stringify(summary, null, 2)
);
return summary;
}
クイックリファレンス
| アクション | コマンド/トリガー |
|---|---|
| カバーレター | 「[position] のカバーレターを書いて」 |
| ビジネスレター | 「[recipient] 宛てのビジネスレターを作成して」 |
| 推薦状 | 「推薦状を作成して」 |
| お礼状 | 「お礼状を書いて」 |
| 正式なレター | 「正式な連絡文書の草案を作成して」 |
| レターの一括作成 | 「[recipients] 宛てのレターを作成して」 |
ベストプラクティス
- 適切な形式: 正しい余白を設定し、標準的なビジネスレターの形式を使用します
- プロフェッショナルなトーン: 目的や対象者に合わせたトーンにします
- 簡潔な文章: 明確かつ直接的に書き、不要な語句を避けます
- 校正: 誤字脱字や文法上の誤りがないようにします
- パーソナライズ: レターごとに内容を調整し、ありきたりな表現を避けます
- 具体的な詳細: 具体例や詳細な情報を盛り込みます
- 能動態: より力強く明確な文章にするため、能動態を使用します
- 適切な長さ: カバーレターは3~4段落、ビジネスレターは必要な内容を簡潔にまとめます
- 行動喚起: 明確な次のステップや望ましい結果を示して締めくくります
- 署名: 連絡先情報を記載した適切な署名ブロックを含めます
- 書式: フォント、余白、行間を統一します
- タイミング: お礼状は24~48時間以内に送ります
よくあるパターン
求人応募一式:
{
coverLetter: generateCoverLetter(candidateData, jobData),
resume: generateResume(candidateData),
references: generateReferencesList(candidateData.references)
}
カスタマーサービスの返信:
{
type: 'response',
tone: 'apologetic and solution-oriented',
structure: ['acknowledge issue', 'explain what happened', 'describe resolution', 'offer compensation', 'prevent future']
}
依存関係
必要なパッケージをインストールします:
npm install docx
npm install handlebars # For templating
npm install pdf-lib # For PDF generation
npm install nodemailer # For email sending (optional)
エラー処理
- データ不足: 任意のフィールドにはデフォルト値を設定します
- 無効な住所: 住所の形式を検証します
- テンプレートのエラー: レンダリングエラーを適切に処理します
- ファイルの権限: 書き込みエラーに対処します
高度な機能
メール連携:
async function sendLetter(letterPath, recipientEmail, subject) {
const transporter = nodemailer.createTransporter(config);
await transporter.sendMail({
from: senderEmail,
to: recipientEmail,
subject: subject,
html: '<p>Please find attached letter.</p>',
attachments: [{ path: letterPath }]
});
}
署名連携:
async function addDigitalSignature(letterPath, signatureImagePath) {
// Add scanned signature image to letter
// Position at signature block
}
テンプレートライブラリ:
const letterTemplates = {
coverLetter: './templates/cover-letter.hbs',
businessInquiry: './templates/business-inquiry.hbs',
recommendation: './templates/recommendation.hbs',
thankYou: './templates/thank-you.hbs'
};
コメント
Markdown 対応