ai-editor-cli 0.1.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/dist/cli.js +47 -0
- package/dist/commands/diagnose.js +70 -0
- package/dist/commands/sns.js +78 -0
- package/dist/commands/title.js +75 -0
- package/dist/utils/env.js +46 -0
- package/dist/utils/fileReader.js +74 -0
- package/dist/utils/license.js +78 -0
- package/package.json +31 -0
- package/templates/diagnose.md +42 -0
- package/templates/sns_threads.md +25 -0
- package/templates/sns_x.md +25 -0
- package/templates/title.md +41 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const diagnose_1 = require("./commands/diagnose");
|
|
6
|
+
const title_1 = require("./commands/title");
|
|
7
|
+
const sns_1 = require("./commands/sns");
|
|
8
|
+
const license_1 = require("./utils/license");
|
|
9
|
+
const program = new commander_1.Command();
|
|
10
|
+
program
|
|
11
|
+
.name('ai-editor')
|
|
12
|
+
.description('AI編集長 CLI for Claude Code')
|
|
13
|
+
.version('0.1.0');
|
|
14
|
+
program
|
|
15
|
+
.command('diagnose <file>')
|
|
16
|
+
.description('記事の弱点診断')
|
|
17
|
+
.option('--model <model-id>', '使用するClaudeモデル', 'claude-haiku-4-5-20251001')
|
|
18
|
+
.option('--output <file>', '結果をファイルに保存')
|
|
19
|
+
.option('--lang <ja|en>', '出力言語', 'ja')
|
|
20
|
+
.action(async (file, options) => {
|
|
21
|
+
await (0, diagnose_1.runDiagnose)(file, options);
|
|
22
|
+
});
|
|
23
|
+
program
|
|
24
|
+
.command('title <file>')
|
|
25
|
+
.description('タイトル改善案を生成')
|
|
26
|
+
.option('--model <model-id>', '使用するClaudeモデル', 'claude-haiku-4-5-20251001')
|
|
27
|
+
.option('--output <file>', '結果をファイルに保存')
|
|
28
|
+
.option('--lang <ja|en>', '出力言語', 'ja')
|
|
29
|
+
.action(async (file, options) => {
|
|
30
|
+
await (0, title_1.runTitle)(file, options);
|
|
31
|
+
});
|
|
32
|
+
program
|
|
33
|
+
.command('sns <file>')
|
|
34
|
+
.description('SNS投稿文に変換')
|
|
35
|
+
.option('--platform <x|threads>', '投稿先プラットフォーム')
|
|
36
|
+
.option('--model <model-id>', '使用するClaudeモデル', 'claude-haiku-4-5-20251001')
|
|
37
|
+
.option('--output <file>', '結果をファイルに保存')
|
|
38
|
+
.option('--lang <ja|en>', '出力言語', 'ja')
|
|
39
|
+
.action(async (file, options) => {
|
|
40
|
+
await (0, sns_1.runSns)(file, options);
|
|
41
|
+
});
|
|
42
|
+
(0, license_1.checkLicense)().then(() => {
|
|
43
|
+
program.parseAsync(process.argv).catch((err) => {
|
|
44
|
+
console.error(err.message);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.runDiagnose = runDiagnose;
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
|
|
42
|
+
const fileReader_1 = require("../utils/fileReader");
|
|
43
|
+
const env_1 = require("../utils/env");
|
|
44
|
+
async function runDiagnose(filePath, options) {
|
|
45
|
+
const content = (0, fileReader_1.readArticleFile)(filePath);
|
|
46
|
+
const apiKey = (0, env_1.getApiKey)();
|
|
47
|
+
const systemPrompt = (0, fileReader_1.readTemplate)(path.join(__dirname, '../../templates/diagnose.md'));
|
|
48
|
+
const client = new sdk_1.default({ apiKey });
|
|
49
|
+
const langNote = options.lang === 'en' ? 'Please respond in English.' : '日本語で回答してください。';
|
|
50
|
+
const userMessage = `${langNote}\n\n以下の記事を診断してください:\n\n${content}`;
|
|
51
|
+
try {
|
|
52
|
+
const response = await client.messages.create({
|
|
53
|
+
model: options.model,
|
|
54
|
+
max_tokens: 2048,
|
|
55
|
+
system: systemPrompt,
|
|
56
|
+
messages: [{ role: 'user', content: userMessage }],
|
|
57
|
+
});
|
|
58
|
+
const textBlock = response.content.find((b) => b.type === 'text');
|
|
59
|
+
(0, fileReader_1.writeOutput)(textBlock ? textBlock.text : '', options.output);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
if (error instanceof sdk_1.default.AuthenticationError) {
|
|
63
|
+
console.error('Error: APIキーが無効です。Anthropicダッシュボードで確認してください');
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
67
|
+
console.error(`Error: API呼び出しに失敗しました。しばらく待ってから再実行してください (詳細: ${detail})`);
|
|
68
|
+
process.exit(2);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.runSns = runSns;
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
|
|
42
|
+
const fileReader_1 = require("../utils/fileReader");
|
|
43
|
+
const env_1 = require("../utils/env");
|
|
44
|
+
const VALID_PLATFORMS = ['x', 'threads'];
|
|
45
|
+
async function runSns(filePath, options) {
|
|
46
|
+
if (!options.platform || !VALID_PLATFORMS.includes(options.platform)) {
|
|
47
|
+
console.error('Error: --platform x または --platform threads を指定してください');
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
const content = (0, fileReader_1.readArticleFile)(filePath);
|
|
51
|
+
const apiKey = (0, env_1.getApiKey)();
|
|
52
|
+
const templateFile = options.platform === 'x' ? 'sns_x.md' : 'sns_threads.md';
|
|
53
|
+
const systemPrompt = (0, fileReader_1.readTemplate)(path.join(__dirname, `../../templates/${templateFile}`));
|
|
54
|
+
const client = new sdk_1.default({ apiKey });
|
|
55
|
+
const platformLabel = options.platform === 'x' ? 'X(旧Twitter)' : 'Threads';
|
|
56
|
+
const charLimit = options.platform === 'x' ? '140字以内' : '500字以内';
|
|
57
|
+
const langNote = options.lang === 'en' ? 'Please respond in English.' : '日本語で回答してください。';
|
|
58
|
+
const userMessage = `${langNote}\n\n以下の記事を${platformLabel}投稿文(${charLimit})に変換してください:\n\n${content}`;
|
|
59
|
+
try {
|
|
60
|
+
const response = await client.messages.create({
|
|
61
|
+
model: options.model,
|
|
62
|
+
max_tokens: 1024,
|
|
63
|
+
system: systemPrompt,
|
|
64
|
+
messages: [{ role: 'user', content: userMessage }],
|
|
65
|
+
});
|
|
66
|
+
const textBlock = response.content.find((b) => b.type === 'text');
|
|
67
|
+
(0, fileReader_1.writeOutput)(textBlock ? textBlock.text : '', options.output);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
if (error instanceof sdk_1.default.AuthenticationError) {
|
|
71
|
+
console.error('Error: APIキーが無効です。Anthropicダッシュボードで確認してください');
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
75
|
+
console.error(`Error: API呼び出しに失敗しました。しばらく待ってから再実行してください (詳細: ${detail})`);
|
|
76
|
+
process.exit(2);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.runTitle = runTitle;
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
|
|
42
|
+
const fileReader_1 = require("../utils/fileReader");
|
|
43
|
+
const env_1 = require("../utils/env");
|
|
44
|
+
function extractTitle(content) {
|
|
45
|
+
const match = content.match(/^#\s+(.+)$/m);
|
|
46
|
+
return match ? match[1].trim() : '(タイトルなし)';
|
|
47
|
+
}
|
|
48
|
+
async function runTitle(filePath, options) {
|
|
49
|
+
const content = (0, fileReader_1.readArticleFile)(filePath);
|
|
50
|
+
const apiKey = (0, env_1.getApiKey)();
|
|
51
|
+
const systemPrompt = (0, fileReader_1.readTemplate)(path.join(__dirname, '../../templates/title.md'));
|
|
52
|
+
const client = new sdk_1.default({ apiKey });
|
|
53
|
+
const currentTitle = extractTitle(content);
|
|
54
|
+
const langNote = options.lang === 'en' ? 'Please respond in English.' : '日本語で回答してください。';
|
|
55
|
+
const userMessage = `${langNote}\n\n現在のタイトル: ${currentTitle}\n\n以下の記事のタイトルを改善してください:\n\n${content}`;
|
|
56
|
+
try {
|
|
57
|
+
const response = await client.messages.create({
|
|
58
|
+
model: options.model,
|
|
59
|
+
max_tokens: 1024,
|
|
60
|
+
system: systemPrompt,
|
|
61
|
+
messages: [{ role: 'user', content: userMessage }],
|
|
62
|
+
});
|
|
63
|
+
const textBlock = response.content.find((b) => b.type === 'text');
|
|
64
|
+
(0, fileReader_1.writeOutput)(textBlock ? textBlock.text : '', options.output);
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
if (error instanceof sdk_1.default.AuthenticationError) {
|
|
68
|
+
console.error('Error: APIキーが無効です。Anthropicダッシュボードで確認してください');
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
72
|
+
console.error(`Error: API呼び出しに失敗しました。しばらく待ってから再実行してください (詳細: ${detail})`);
|
|
73
|
+
process.exit(2);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.getApiKey = getApiKey;
|
|
37
|
+
const dotenv = __importStar(require("dotenv"));
|
|
38
|
+
dotenv.config();
|
|
39
|
+
function getApiKey() {
|
|
40
|
+
const key = process.env.ANTHROPIC_API_KEY;
|
|
41
|
+
if (!key) {
|
|
42
|
+
console.error('Error: ANTHROPIC_API_KEY が設定されていません。環境変数または .env ファイルに設定してください');
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
return key;
|
|
46
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.readArticleFile = readArticleFile;
|
|
37
|
+
exports.readTemplate = readTemplate;
|
|
38
|
+
exports.writeOutput = writeOutput;
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const ALLOWED_EXTENSIONS = ['.md', '.txt'];
|
|
42
|
+
function readArticleFile(filePath) {
|
|
43
|
+
if (!fs.existsSync(filePath)) {
|
|
44
|
+
console.error(`Error: ファイルが見つかりません: ${filePath}`);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
48
|
+
if (!ALLOWED_EXTENSIONS.includes(ext)) {
|
|
49
|
+
console.error('Error: 対応形式は .md または .txt のみです');
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
53
|
+
if (content.trim().length === 0) {
|
|
54
|
+
console.error('Error: ファイルの内容が空です');
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
return content;
|
|
58
|
+
}
|
|
59
|
+
function readTemplate(templatePath) {
|
|
60
|
+
if (!fs.existsSync(templatePath)) {
|
|
61
|
+
console.error(`Error: テンプレートファイルが見つかりません: ${templatePath}`);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
return fs.readFileSync(templatePath, 'utf-8');
|
|
65
|
+
}
|
|
66
|
+
function writeOutput(content, outputPath) {
|
|
67
|
+
if (outputPath) {
|
|
68
|
+
fs.writeFileSync(outputPath, content, 'utf-8');
|
|
69
|
+
console.error(`出力ファイル: ${outputPath}`);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
process.stdout.write(content + '\n');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.checkLicense = checkLicense;
|
|
37
|
+
const dotenv = __importStar(require("dotenv"));
|
|
38
|
+
dotenv.config();
|
|
39
|
+
// サーバーURLが確定したらここに設定し、スタブを実際のHTTPコールに置き換える
|
|
40
|
+
const LICENSE_SERVER_URL = 'https://script.google.com/macros/s/AKfycbxyGfKklcjqQkSRNkQlBtWqRYw62-ox7C5Rq8dT19B2zhRroh4hHpx0DZgmksySo3i7/exec';
|
|
41
|
+
async function checkLicense() {
|
|
42
|
+
const licenseKey = process.env.AI_EDITOR_LICENSE;
|
|
43
|
+
if (!licenseKey) {
|
|
44
|
+
console.error('Error: AI_EDITOR_LICENSE が設定されていません。環境変数または .env ファイルに設定してください');
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
if (!LICENSE_SERVER_URL) {
|
|
48
|
+
// サーバーURL未定のためスタブ: 形式チェックのみ行う
|
|
49
|
+
if (licenseKey.length < 8) {
|
|
50
|
+
console.error('Error: ライセンスキーの形式が無効です');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
// スタブ: 常にOK(本番実装時にHTTPコールに置き換える)
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
// 本番実装用テンプレート(サーバーURL確定後に有効化)
|
|
57
|
+
// 送信データ: ライセンスキーのみ。APIキー・記事データは送らない。
|
|
58
|
+
try {
|
|
59
|
+
const res = await fetch(LICENSE_SERVER_URL, {
|
|
60
|
+
method: 'POST',
|
|
61
|
+
headers: { 'Content-Type': 'application/json' },
|
|
62
|
+
body: JSON.stringify({ license: licenseKey }),
|
|
63
|
+
});
|
|
64
|
+
if (!res.ok) {
|
|
65
|
+
console.error('Error: ライセンスサーバーへの接続に失敗しました');
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
const data = await res.json();
|
|
69
|
+
if (!data.valid) {
|
|
70
|
+
console.error('Error: ライセンスキーが無効です。正しいキーを確認してください');
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
console.error('Error: ライセンスサーバーに接続できません。ネットワーク接続を確認してください');
|
|
76
|
+
process.exit(2);
|
|
77
|
+
}
|
|
78
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ai-editor-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "AI編集長 CLI for Claude Code",
|
|
5
|
+
"main": "dist/cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ai-editor": "dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"templates"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"dev": "ts-node src/cli.ts",
|
|
16
|
+
"start": "node dist/cli.js"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@anthropic-ai/sdk": "^0.26.0",
|
|
20
|
+
"commander": "^12.0.0",
|
|
21
|
+
"dotenv": "^16.4.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^20.0.0",
|
|
25
|
+
"ts-node": "^10.9.0",
|
|
26
|
+
"typescript": "^5.4.0"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18.0.0"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# diagnose システムプロンプト
|
|
2
|
+
|
|
3
|
+
あなたはnote・Brain・ブログ記事の専門編集者です。日本語コンテンツ販売に特化した観点で記事を診断してください。
|
|
4
|
+
|
|
5
|
+
## 診断観点
|
|
6
|
+
|
|
7
|
+
1. タイトル:誰に向けた記事か5秒で伝わるか、数字・具体性・ターゲットが含まれているか
|
|
8
|
+
2. 導入文(冒頭3文):読者の「痛み」または「欲求」に触れているか、「自分のことだ」と感じさせる共感から入っているか
|
|
9
|
+
3. 本文構成:メッセージが1つに絞られているか、論理の飛びがないか、具体例・数字・体験談があるか
|
|
10
|
+
4. AI臭チェック:根拠のない断言・定型フレーズの連続・具体例のない抽象論を具体的に指摘する
|
|
11
|
+
5. CTA:有料記事への誘導が自然か(押し付けになっていないか)
|
|
12
|
+
6. 販売導線:「できるようになった自分」をイメージできるか、「解決する言葉」になっているか
|
|
13
|
+
|
|
14
|
+
## 出力フォーマット(厳守)
|
|
15
|
+
|
|
16
|
+
以下のフォーマットで出力してください。前置きや後書きは不要です。
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
## 記事診断レポート
|
|
20
|
+
|
|
21
|
+
弱点スコア: XX/100
|
|
22
|
+
|
|
23
|
+
### 優先修正ポイント(上位3つ)
|
|
24
|
+
1. [修正ポイント] — [理由]
|
|
25
|
+
2. [修正ポイント] — [理由]
|
|
26
|
+
3. [修正ポイント] — [理由]
|
|
27
|
+
|
|
28
|
+
### 各観点の評価
|
|
29
|
+
- タイトル: [評価コメント]
|
|
30
|
+
- 導入文: [評価コメント]
|
|
31
|
+
- 本文構成: [評価コメント]
|
|
32
|
+
- CTA: [評価コメント]
|
|
33
|
+
- 販売導線: [評価コメント]
|
|
34
|
+
|
|
35
|
+
### 修正後の方向性
|
|
36
|
+
[具体的な改善方向を1〜3文で]
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 出力ルール
|
|
40
|
+
|
|
41
|
+
- 改善コメントは具体的に(「改善が必要です」等の抽象的指摘は禁止)
|
|
42
|
+
- 良い点も1つは必ず含める
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
あなたはThreads投稿の専門コピーライターです。記事内容をThreads投稿文(500字以内)に変換してください。
|
|
2
|
+
|
|
3
|
+
【設計方針】
|
|
4
|
+
- 冒頭1〜2行で読者の認知を揺さぶる(「その認知は間違っているかもしれない」型)
|
|
5
|
+
- 記事の核心的な気づきを3点以内で構成する
|
|
6
|
+
- 最後は疑問または余白で終わらせ、記事への自然な誘導につなげる
|
|
7
|
+
- 「売り込み感」を出さない
|
|
8
|
+
|
|
9
|
+
【禁止】
|
|
10
|
+
- 500字超
|
|
11
|
+
- 「ぜひ購入してください」等の直接的な誘導
|
|
12
|
+
- 記事の目次の羅列
|
|
13
|
+
|
|
14
|
+
## 出力フォーマット(厳守)
|
|
15
|
+
|
|
16
|
+
以下のフォーマットで出力してください。前置きや後書きは不要です。
|
|
17
|
+
|
|
18
|
+
```text
|
|
19
|
+
## Threads投稿文
|
|
20
|
+
|
|
21
|
+
[投稿文 500字以内]
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
文字数: XX字
|
|
25
|
+
```
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
あなたはX(旧Twitter)投稿の専門コピーライターです。記事内容をX投稿文(140字以内)に変換してください。
|
|
2
|
+
|
|
3
|
+
【設計方針】
|
|
4
|
+
- 「売る言葉」より「解決する言葉」を使う
|
|
5
|
+
- 冒頭で読者の問題または希少な数字・事実を提示する
|
|
6
|
+
- 記事の「一番の気づき」を1つだけ伝える(情報を詰め込まない)
|
|
7
|
+
- 続きを読みたくなる疑問または余白で終わらせる
|
|
8
|
+
|
|
9
|
+
【禁止】
|
|
10
|
+
- 140字超
|
|
11
|
+
- 複数のメッセージを詰め込む
|
|
12
|
+
- 「ぜひ読んでください」等の直接的な誘導
|
|
13
|
+
|
|
14
|
+
## 出力フォーマット(厳守)
|
|
15
|
+
|
|
16
|
+
以下のフォーマットで出力してください。前置きや後書きは不要です。
|
|
17
|
+
|
|
18
|
+
```text
|
|
19
|
+
## X投稿文
|
|
20
|
+
|
|
21
|
+
[投稿文 140字以内]
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
文字数: XX字
|
|
25
|
+
```
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# title システムプロンプト
|
|
2
|
+
|
|
3
|
+
あなたはnote・Brain・ブログ記事のタイトル専門コピーライターです。記事の内容を読んで、タイトル改善案を5つ生成してください。
|
|
4
|
+
|
|
5
|
+
## タイトル基準
|
|
6
|
+
|
|
7
|
+
- 「誰が・何をすると・どうなるか」が一目でわかる
|
|
8
|
+
- 数字を含む(例:「7つの方法」「3ステップ」)
|
|
9
|
+
- ターゲット読者が「自分のことだ」と感じるキーワードを使う
|
|
10
|
+
- クリックしたくなるフック(疑問・希少性・対比)を使う
|
|
11
|
+
|
|
12
|
+
## 優先パターン(この順で検討)
|
|
13
|
+
|
|
14
|
+
1. 数字+テクニック型(例:「○○する3つの方法」)
|
|
15
|
+
2. 問題提起型(例:「なぜ○○しても稼げないのか」)
|
|
16
|
+
3. 希少性型(例:「上位5%だけが知っている○○」)
|
|
17
|
+
|
|
18
|
+
## 禁止
|
|
19
|
+
|
|
20
|
+
- 「今すぐ」「絶対」「誰でも」等の誇大表現
|
|
21
|
+
- 5秒で読んで意味が伝わらないタイトル
|
|
22
|
+
|
|
23
|
+
## 出力フォーマット(厳守)
|
|
24
|
+
|
|
25
|
+
以下のフォーマットで出力してください。前置きや後書きは不要です。
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
## タイトル改善案
|
|
29
|
+
|
|
30
|
+
現在のタイトル: [元タイトル]
|
|
31
|
+
|
|
32
|
+
### 改善案(5案)
|
|
33
|
+
1. [タイトル案] — 理由: [選択根拠]
|
|
34
|
+
2. [タイトル案] — 理由: [選択根拠]
|
|
35
|
+
3. [タイトル案] — 理由: [選択根拠]
|
|
36
|
+
4. [タイトル案] — 理由: [選択根拠]
|
|
37
|
+
5. [タイトル案] — 理由: [選択根拠]
|
|
38
|
+
|
|
39
|
+
### 推奨案
|
|
40
|
+
[最も推奨するタイトル] — 理由: [推奨根拠]
|
|
41
|
+
```
|