AIに新しい記事を作るスクリプトを作ってもらった

投稿日:
更新日:


この記事はAI(CursorAI)によって作成されました。

はじめに

このブログサイトはAstroで構築されており、記事はsrc/content/posts/ディレクトリにMDXファイルとして保存されています。

新しい記事を追加する際、毎回以下の作業が必要でした:

  1. ファイル名を考える(英語)
  2. フロントマターを書く
  3. 基本的な記事構造を作る

これらを手動で行うのは面倒なので、AIに自動化スクリプトを作ってもらいました。

作成したスクリプトの特徴

対話型インターフェース

yarn new-postを実行すると、以下の順番で入力が求められます:

  1. ファイル名 (英語、拡張子不要)
  2. 記事タイトル (日本語)
  3. タグ (カンマ区切り、オプション)

バリデーション機能

  • ファイル名: 英小文字、数字、ハイフンのみ許可
  • 記事タイトル: 空文字チェック
  • 既存ファイル: 上書き確認

自動生成される内容

---
title: "記事タイトル"
description: "記事の説明をここに記入してください"
pubDate: 2025-07-08
tags: ["Tag1", "Tag2"]
---
記事の内容をここに記入してください。
## 見出し1
内容...
## 見出し2
内容...

使用方法

Terminal window
# スクリプトを実行
yarn new-post

実行例

📝 新しい記事を作成します
ファイル名 (英語、拡張子不要): my-new-article
記事タイトル (日本語): 新しい記事のタイトル
タグ (カンマ区切り、例: AI,WebDevelopment): AI,WebDevelopment
✅ 記事を作成しました: src/content/posts/my-new-article.mdx
📝 編集してください: src/content/posts/my-new-article.mdx
📋 作成された記事の情報:
ファイル名: my-new-article.mdx
タイトル: 新しい記事のタイトル
タグ: AI, WebDevelopment

作成されたスクリプト

以下が実際に作成されたスクリプト(scripts/create-post.js)です:

#!/usr/bin/env node
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import readline from 'readline';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// readlineインターフェースを作成
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
/**
* プロンプトを表示してユーザー入力を受け取る関数
* @param {string} question - 表示する質問
* @returns {Promise<string>} ユーザーの入力
*/
function askQuestion(question) {
return new Promise((resolve) => {
rl.question(question, (answer) => {
resolve(answer.trim());
});
});
}
/**
* ファイル名のバリデーション
* @param {string} filename - 検証するファイル名
* @returns {boolean} 有効なファイル名かどうか
*/
function isValidFilename(filename) {
return filename && /^[a-z0-9-]+$/.test(filename);
}
/**
* 記事タイトルのバリデーション
* @param {string} title - 検証するタイトル
* @returns {boolean} 有効なタイトルかどうか
*/
function isValidTitle(title) {
return title && title.length > 0;
}
/**
* タグ文字列を配列に変換
* @param {string} tagsString - カンマ区切りのタグ文字列
* @returns {string[]} タグの配列
*/
function parseTags(tagsString) {
if (!tagsString) return [];
return tagsString
.split(',')
.map(tag => tag.trim())
.filter(tag => tag.length > 0);
}
/**
* フロントマターを生成
* @param {string} title - 記事タイトル
* @param {string} pubDate - 公開日
* @param {string[]} tags - タグの配列
* @returns {string} フロントマター文字列
*/
function generateFrontmatter(title, pubDate, tags) {
const tagsString = tags.map(tag => `"${tag}"`).join(', ');
return `---
title: "${title}"
description: "記事の説明をここに記入してください"
pubDate: ${pubDate}
tags: [${tagsString}]
---
記事の内容をここに記入してください。
## 見出し1
内容...
## 見出し2
内容...
`;
}
/**
* 新しい記事を作成するメイン関数
*/
async function createPost() {
console.log('📝 新しい記事を作成します\n');
try {
// ファイル名を入力
let filename = await askQuestion('ファイル名 (英語、拡張子不要): ');
// ファイル名のバリデーション
while (!isValidFilename(filename)) {
console.log('❌ ファイル名は英小文字、数字、ハイフンのみ使用可能です');
filename = await askQuestion('ファイル名 (英語、拡張子不要): ');
}
// 記事タイトルを入力
let title = await askQuestion('記事タイトル (日本語): ');
// タイトルのバリデーション
while (!isValidTitle(title)) {
console.log('❌ 記事タイトルを入力してください');
title = await askQuestion('記事タイトル (日本語): ');
}
// タグを入力(オプション)
const tagsInput = await askQuestion('タグ (カンマ区切り、例: AI,WebDevelopment): ');
const tagArray = parseTags(tagsInput);
// 今日の日付を取得
const today = new Date().toISOString().split('T')[0];
// フロントマターを生成
const frontmatter = generateFrontmatter(title, today, tagArray);
// ファイルパスを設定
const postsDir = path.join(__dirname, '../src/content/posts');
const filePath = path.join(postsDir, `${filename}.mdx`);
// ディレクトリが存在しない場合は作成
if (!fs.existsSync(postsDir)) {
fs.mkdirSync(postsDir, { recursive: true });
console.log('📁 postsディレクトリを作成しました');
}
// ファイルが既に存在する場合は確認
if (fs.existsSync(filePath)) {
console.log(`\n⚠️ 警告: ${filePath} は既に存在します。`);
const overwrite = await askQuestion('上書きしますか? (y/N): ');
if (overwrite.toLowerCase() !== 'y') {
console.log('キャンセルしました。');
rl.close();
return;
}
}
// ファイルを作成
fs.writeFileSync(filePath, frontmatter);
// 成功メッセージを表示
console.log(`\n✅ 記事を作成しました: ${filePath}`);
console.log(`📝 編集してください: ${filePath}`);
// 作成された記事の情報を表示
console.log(`\n📋 作成された記事の情報:`);
console.log(` ファイル名: ${filename}.mdx`);
console.log(` タイトル: ${title}`);
if (tagArray.length > 0) {
console.log(` タグ: ${tagArray.join(', ')}`);
}
} catch (error) {
console.error('❌ エラーが発生しました:', error.message);
console.error('詳細:', error.stack);
} finally {
rl.close();
}
}
// スクリプトを実行
createPost();

AIの驚くべき能力

今回のスクリプト作成を通じて、AIの能力に改めて驚かされました。

1. 要件の理解力

「ファイル名は英語、記事タイトルは日本語にしたい」という要望を正確に理解し、対話型インターフェースを提案してくれました。

2. 実装の完成度

  • バリデーション機能: 入力値の妥当性チェック
  • エラーハンドリング: 適切な例外処理
  • ユーザーフレンドリー: 分かりやすいメッセージ
  • 詳細情報表示: 作成完了時の確認情報

これらすべてを最初から適切に実装してくれました。

3. コードの品質

  • 非同期処理: async/awaitの適切な使用
  • エラー処理: try-catchによる例外処理
  • 可読性: 読みやすいコード構造
  • 保守性: 拡張しやすい設計

今後の活用

このスクリプトにより、記事作成の手間が大幅に削減されました。今後は以下のような拡張も検討しています:

  • 記事テンプレート選択: 用途に応じたテンプレート選択
  • 画像ファイル自動作成: 記事用の画像ディレクトリ作成
  • タグ候補表示: 既存タグの候補表示機能
  • プレビュー機能: 作成前の内容確認

まとめ

AIを活用することで、開発効率が劇的に向上することが実感できました。特に今回のような定型的な作業の自動化は、AIの得意分野だと思います。

今後もAIを積極的に活用して、より効率的な開発環境を構築していきたいと思います。


と、ここまで全てAIに書いてもらいました。とても楽ですし、私が書くよりもしっかりとしていて、もはや私が書く必要性ないのでは?

どんどんAIによる効率化が進んで、出来る人以外は切り捨てられる世の中が始まってきていますね…少し怖いです。