@opendirectory.dev/skills 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/.claude/skills/claude-md-generator/.env.example +7 -0
- package/.claude/skills/claude-md-generator/README.md +78 -0
- package/.claude/skills/claude-md-generator/SKILL.md +248 -0
- package/.claude/skills/claude-md-generator/evals/evals.json +35 -0
- package/.claude/skills/claude-md-generator/references/section-guide.md +175 -0
- package/dist/e2e.test.d.ts +1 -0
- package/dist/e2e.test.js +62 -0
- package/dist/fs-adapters.d.ts +4 -0
- package/dist/fs-adapters.js +101 -0
- package/dist/fs-adapters.test.d.ts +1 -0
- package/dist/fs-adapters.test.js +108 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +211 -0
- package/dist/transformers.d.ts +6 -0
- package/dist/transformers.js +2 -0
- package/package.json +25 -0
- package/registry.json +226 -0
- package/skills/blog-cover-image-cli/.github/workflows/publish.yml +19 -0
- package/skills/blog-cover-image-cli/LICENSE +15 -0
- package/skills/blog-cover-image-cli/README.md +126 -0
- package/skills/blog-cover-image-cli/SKILL.md +7 -0
- package/skills/blog-cover-image-cli/agent-skill/blog-cover-generator/README.md +30 -0
- package/skills/blog-cover-image-cli/agent-skill/blog-cover-generator/SKILL.md +72 -0
- package/skills/blog-cover-image-cli/bin/cli.js +226 -0
- package/skills/blog-cover-image-cli/examples/100x_UX_Research_AI_Agent.png +0 -0
- package/skills/blog-cover-image-cli/examples/Firecrawl-supabase-bolt.png +0 -0
- package/skills/blog-cover-image-cli/examples/Git-City_Case_study_Cover_Image.jpg +0 -0
- package/skills/blog-cover-image-cli/examples/THE DISTRIBUTION LAYER (2).png +0 -0
- package/skills/blog-cover-image-cli/examples/canva-perplexity-duolingo-cover-image.png +0 -0
- package/skills/blog-cover-image-cli/examples/gamma-mistral-veed.png +0 -0
- package/skills/blog-cover-image-cli/examples/server-survival-case-study-cover-image(1).png +0 -0
- package/skills/blog-cover-image-cli/examples/viral-meme-automation.png +0 -0
- package/skills/blog-cover-image-cli/index.js +2 -0
- package/skills/blog-cover-image-cli/package-lock.json +2238 -0
- package/skills/blog-cover-image-cli/package.json +37 -0
- package/skills/blog-cover-image-cli/src/geminiGenerator.js +126 -0
- package/skills/blog-cover-image-cli/src/imageValidator.js +54 -0
- package/skills/blog-cover-image-cli/src/logoFetcher.js +86 -0
- package/skills/claude-md-generator/.env.example +7 -0
- package/skills/claude-md-generator/README.md +78 -0
- package/skills/claude-md-generator/SKILL.md +254 -0
- package/skills/claude-md-generator/evals/evals.json +35 -0
- package/skills/claude-md-generator/references/section-guide.md +175 -0
- package/skills/cook-the-blog/README.md +86 -0
- package/skills/cook-the-blog/SKILL.md +130 -0
- package/skills/dependency-update-bot/.env.example +13 -0
- package/skills/dependency-update-bot/README.md +101 -0
- package/skills/dependency-update-bot/SKILL.md +376 -0
- package/skills/dependency-update-bot/evals/evals.json +45 -0
- package/skills/dependency-update-bot/references/changelog-patterns.md +201 -0
- package/skills/docs-from-code/.env.example +13 -0
- package/skills/docs-from-code/README.md +97 -0
- package/skills/docs-from-code/SKILL.md +160 -0
- package/skills/docs-from-code/evals/evals.json +29 -0
- package/skills/docs-from-code/references/extraction-guide.md +174 -0
- package/skills/docs-from-code/references/output-template.md +135 -0
- package/skills/docs-from-code/scripts/extract_py.py +238 -0
- package/skills/docs-from-code/scripts/extract_ts.ts +284 -0
- package/skills/docs-from-code/scripts/package.json +18 -0
- package/skills/explain-this-pr/README.md +74 -0
- package/skills/explain-this-pr/SKILL.md +130 -0
- package/skills/explain-this-pr/evals/evals.json +35 -0
- package/skills/google-trends-api-skills/README.md +78 -0
- package/skills/google-trends-api-skills/SKILL.md +7 -0
- package/skills/google-trends-api-skills/google-trends-api/SKILL.md +163 -0
- package/skills/google-trends-api-skills/google-trends-api/references/api-responses.md +188 -0
- package/skills/google-trends-api-skills/google-trends-api/scripts/discover_keywords.py +344 -0
- package/skills/google-trends-api-skills/seo-keyword-research/SKILL.md +205 -0
- package/skills/google-trends-api-skills/seo-keyword-research/references/keyword-placement-guide.md +89 -0
- package/skills/google-trends-api-skills/seo-keyword-research/references/tech-blog-examples.md +207 -0
- package/skills/google-trends-api-skills/seo-keyword-research/scripts/blog_seo_research.py +373 -0
- package/skills/hackernews-intel/.env.example +33 -0
- package/skills/hackernews-intel/README.md +161 -0
- package/skills/hackernews-intel/SKILL.md +156 -0
- package/skills/hackernews-intel/evals/evals.json +35 -0
- package/skills/hackernews-intel/package.json +15 -0
- package/skills/hackernews-intel/scripts/monitor-hn.js +258 -0
- package/skills/kill-the-standup/.env.example +22 -0
- package/skills/kill-the-standup/README.md +84 -0
- package/skills/kill-the-standup/SKILL.md +169 -0
- package/skills/kill-the-standup/evals/evals.json +35 -0
- package/skills/kill-the-standup/references/standup-format.md +102 -0
- package/skills/linkedin-post-generator/.env.example +14 -0
- package/skills/linkedin-post-generator/README.md +107 -0
- package/skills/linkedin-post-generator/SKILL.md +228 -0
- package/skills/linkedin-post-generator/evals/evals.json +35 -0
- package/skills/linkedin-post-generator/references/linkedin-format.md +216 -0
- package/skills/linkedin-post-generator/references/output-template.md +154 -0
- package/skills/llms-txt-generator/.env.example +18 -0
- package/skills/llms-txt-generator/README.md +142 -0
- package/skills/llms-txt-generator/SKILL.md +176 -0
- package/skills/llms-txt-generator/evals/evals.json +35 -0
- package/skills/llms-txt-generator/references/llms-txt-spec.md +88 -0
- package/skills/llms-txt-generator/references/output-template.md +76 -0
- package/skills/llms-txt-generator/test-output/genzcareer.in/llms.txt +31 -0
- package/skills/luma-attendees-scraper/README.md +170 -0
- package/skills/luma-attendees-scraper/SKILL.md +7 -0
- package/skills/luma-attendees-scraper/luma_attendees_export.js +223 -0
- package/skills/meeting-brief-generator/.env.example +21 -0
- package/skills/meeting-brief-generator/README.md +90 -0
- package/skills/meeting-brief-generator/SKILL.md +275 -0
- package/skills/meeting-brief-generator/evals/evals.json +35 -0
- package/skills/meeting-brief-generator/references/brief-format.md +114 -0
- package/skills/meeting-brief-generator/references/output-template.md +150 -0
- package/skills/meta-ads-skill/README.md +100 -0
- package/skills/meta-ads-skill/SKILL.md +7 -0
- package/skills/meta-ads-skill/meta-ads-skill/SKILL.md +41 -0
- package/skills/meta-ads-skill/meta-ads-skill/references/report_templates.md +47 -0
- package/skills/meta-ads-skill/meta-ads-skill/references/workflows.md +51 -0
- package/skills/meta-ads-skill/meta-ads-skill/scripts/auth_check.py +22 -0
- package/skills/meta-ads-skill/meta-ads-skill/scripts/formatters.py +46 -0
- package/skills/newsletter-digest/.env.example +20 -0
- package/skills/newsletter-digest/README.md +147 -0
- package/skills/newsletter-digest/SKILL.md +221 -0
- package/skills/newsletter-digest/evals/evals.json +35 -0
- package/skills/newsletter-digest/feeds.json +7 -0
- package/skills/newsletter-digest/package.json +15 -0
- package/skills/newsletter-digest/references/digest-format.md +123 -0
- package/skills/newsletter-digest/references/output-template.md +136 -0
- package/skills/newsletter-digest/scripts/fetch-feeds.js +141 -0
- package/skills/newsletter-digest/scripts/ghost-publish.js +147 -0
- package/skills/noise2blog/.env.example +16 -0
- package/skills/noise2blog/README.md +107 -0
- package/skills/noise2blog/SKILL.md +229 -0
- package/skills/noise2blog/evals/evals.json +35 -0
- package/skills/noise2blog/references/blog-format.md +188 -0
- package/skills/noise2blog/references/output-template.md +184 -0
- package/skills/outreach-sequence-builder/.env.example +12 -0
- package/skills/outreach-sequence-builder/README.md +108 -0
- package/skills/outreach-sequence-builder/SKILL.md +248 -0
- package/skills/outreach-sequence-builder/evals/evals.json +36 -0
- package/skills/outreach-sequence-builder/references/output-template.md +171 -0
- package/skills/outreach-sequence-builder/references/sequence-format.md +167 -0
- package/skills/outreach-sequence-builder/references/signal-playbook.md +117 -0
- package/skills/position-me/README.md +71 -0
- package/skills/position-me/SKILL.md +7 -0
- package/skills/position-me/position-me/SKILL.md +50 -0
- package/skills/position-me/position-me/references/EVALUATION_SOP.md +40 -0
- package/skills/position-me/position-me/references/REPORT_TEMPLATE.md +58 -0
- package/skills/position-me/position-me/scripts/extract_links.py +49 -0
- package/skills/pr-description-writer/README.md +81 -0
- package/skills/pr-description-writer/SKILL.md +141 -0
- package/skills/pr-description-writer/evals/evals.json +35 -0
- package/skills/pr-description-writer/references/pr-format-guide.md +145 -0
- package/skills/producthunt-launch-kit/.env.example +7 -0
- package/skills/producthunt-launch-kit/README.md +95 -0
- package/skills/producthunt-launch-kit/SKILL.md +380 -0
- package/skills/producthunt-launch-kit/evals/evals.json +35 -0
- package/skills/producthunt-launch-kit/references/copy-rules.md +124 -0
- package/skills/reddit-icp-monitor/.env.example +16 -0
- package/skills/reddit-icp-monitor/README.md +117 -0
- package/skills/reddit-icp-monitor/SKILL.md +271 -0
- package/skills/reddit-icp-monitor/evals/evals.json +40 -0
- package/skills/reddit-icp-monitor/references/icp-format.md +131 -0
- package/skills/reddit-icp-monitor/references/reply-rules.md +110 -0
- package/skills/reddit-post-engine/.env.example +13 -0
- package/skills/reddit-post-engine/README.md +103 -0
- package/skills/reddit-post-engine/SKILL.md +303 -0
- package/skills/reddit-post-engine/evals/evals.json +35 -0
- package/skills/reddit-post-engine/references/subreddit-playbook.md +156 -0
- package/skills/schema-markup-generator/.env.example +19 -0
- package/skills/schema-markup-generator/README.md +114 -0
- package/skills/schema-markup-generator/SKILL.md +192 -0
- package/skills/schema-markup-generator/evals/evals.json +35 -0
- package/skills/schema-markup-generator/references/json-ld-spec.md +263 -0
- package/skills/schema-markup-generator/references/output-template.md +556 -0
- package/skills/show-hn-writer/.env.example +14 -0
- package/skills/show-hn-writer/README.md +88 -0
- package/skills/show-hn-writer/SKILL.md +303 -0
- package/skills/show-hn-writer/evals/evals.json +35 -0
- package/skills/show-hn-writer/references/hn-rules.md +74 -0
- package/skills/show-hn-writer/references/title-formulas.md +93 -0
- package/skills/stargazer/README.md +79 -0
- package/skills/stargazer/SKILL.md +7 -0
- package/skills/stargazer/stargazer-skill/SKILL.md +58 -0
- package/skills/stargazer/stargazer-skill/assets/.env.example +18 -0
- package/skills/stargazer/stargazer-skill/scripts/convert_to_csv.py +63 -0
- package/skills/stargazer/stargazer-skill/scripts/count_emails.py +52 -0
- package/skills/stargazer/stargazer-skill/scripts/stargazer_deep_extractor.py +450 -0
- package/skills/tweet-thread-from-blog/.env.example +14 -0
- package/skills/tweet-thread-from-blog/README.md +109 -0
- package/skills/tweet-thread-from-blog/SKILL.md +177 -0
- package/skills/tweet-thread-from-blog/evals/evals.json +35 -0
- package/skills/tweet-thread-from-blog/references/output-template.md +193 -0
- package/skills/tweet-thread-from-blog/references/thread-format.md +107 -0
- package/skills/twitter-GTM-find-skill/README.md +43 -0
- package/skills/twitter-GTM-find-skill/SKILL.md +7 -0
- package/skills/twitter-GTM-find-skill/twitter-GTM-find/SKILL.md +37 -0
- package/skills/twitter-GTM-find-skill/twitter-GTM-find/references/icp-checklist.md +35 -0
- package/skills/twitter-GTM-find-skill/twitter-GTM-find/scripts/package.json +23 -0
- package/skills/twitter-GTM-find-skill/twitter-GTM-find/scripts/run_pipeline.sh +8 -0
- package/skills/twitter-GTM-find-skill/twitter-GTM-find/scripts/src/debug.ts +23 -0
- package/skills/twitter-GTM-find-skill/twitter-GTM-find/scripts/src/extractor.ts +79 -0
- package/skills/twitter-GTM-find-skill/twitter-GTM-find/scripts/src/icp-filter.ts +87 -0
- package/skills/twitter-GTM-find-skill/twitter-GTM-find/scripts/src/index.ts +94 -0
- package/skills/twitter-GTM-find-skill/twitter-GTM-find/scripts/src/scraper.ts +41 -0
- package/skills/twitter-GTM-find-skill/twitter-GTM-find/scripts/tsconfig.json +13 -0
- package/skills/yc-intent-radar-skill/README.md +39 -0
- package/skills/yc-intent-radar-skill/SKILL.md +7 -0
- package/skills/yc-intent-radar-skill/yc-jobs-scraper/SKILL.md +59 -0
- package/skills/yc-intent-radar-skill/yc-jobs-scraper/scripts/auth.js +29 -0
- package/skills/yc-intent-radar-skill/yc-jobs-scraper/scripts/db.js +62 -0
- package/skills/yc-intent-radar-skill/yc-jobs-scraper/scripts/export_radar_candidates.js +40 -0
- package/skills/yc-intent-radar-skill/yc-jobs-scraper/scripts/package-lock.json +1525 -0
- package/skills/yc-intent-radar-skill/yc-jobs-scraper/scripts/package.json +12 -0
- package/skills/yc-intent-radar-skill/yc-jobs-scraper/scripts/scraper.js +217 -0
- package/src/e2e.test.ts +35 -0
- package/src/fs-adapters.test.ts +91 -0
- package/src/fs-adapters.ts +65 -0
- package/src/index.ts +182 -0
- package/src/transformers.ts +6 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "blog-cover-image-cli",
|
|
3
|
+
"version": "1.0.17",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"blog-cover-cli": "bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"directories": {
|
|
10
|
+
"example": "examples"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [],
|
|
16
|
+
"author": "",
|
|
17
|
+
"license": "ISC",
|
|
18
|
+
"type": "module",
|
|
19
|
+
"files": [
|
|
20
|
+
"bin",
|
|
21
|
+
"src",
|
|
22
|
+
"examples",
|
|
23
|
+
"agent-skill",
|
|
24
|
+
"index.js"
|
|
25
|
+
],
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@google/genai": "^1.44.0",
|
|
28
|
+
"@inquirer/prompts": "^8.3.0",
|
|
29
|
+
"chalk": "^5.6.2",
|
|
30
|
+
"commander": "^14.0.3",
|
|
31
|
+
"conf": "^15.1.0",
|
|
32
|
+
"dotenv": "^17.3.1",
|
|
33
|
+
"figlet": "^1.11.0",
|
|
34
|
+
"gradient-string": "^3.0.0",
|
|
35
|
+
"sharp": "^0.34.5"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { GoogleGenAI } from '@google/genai';
|
|
2
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
|
|
9
|
+
async function getExampleImages() {
|
|
10
|
+
const examplesDir = path.join(__dirname, '..', 'examples');
|
|
11
|
+
const imageParts = [];
|
|
12
|
+
try {
|
|
13
|
+
const files = await readdir(examplesDir);
|
|
14
|
+
for (const file of files) {
|
|
15
|
+
if (file.match(/\.(png|jpe?g)$/i)) {
|
|
16
|
+
const filePath = path.join(examplesDir, file);
|
|
17
|
+
const buffer = await readFile(filePath);
|
|
18
|
+
const ext = path.extname(file).toLowerCase();
|
|
19
|
+
const mimeType = ext === '.png' ? 'image/png' : 'image/jpeg';
|
|
20
|
+
imageParts.push({
|
|
21
|
+
inlineData: {
|
|
22
|
+
data: buffer.toString('base64'),
|
|
23
|
+
mimeType
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
} catch (err) {
|
|
29
|
+
console.warn('Warning: Could not load example images from ./examples directory.', err.message);
|
|
30
|
+
}
|
|
31
|
+
return imageParts;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Generates a blog cover image using Gemini 3.1 Flash Image Preview.
|
|
36
|
+
*
|
|
37
|
+
* @param {string} title - The title text to include in the image.
|
|
38
|
+
* @param {Object|null} logoData - Optional logo data { data: base64String, mimeType: 'image/png' }.
|
|
39
|
+
* @param {string} [apiKey] - Optional API key.
|
|
40
|
+
* @param {string} [criticalFeedback] - Optional feedback from a previous failed attempt.
|
|
41
|
+
* @returns {Promise<{ base64Image: string, textOutput: string }>} - The base64 encoded image data and any text output.
|
|
42
|
+
*/
|
|
43
|
+
export async function generateCoverImage(title, logoData, apiKey, criticalFeedback) {
|
|
44
|
+
const ai = new GoogleGenAI({ apiKey: apiKey || process.env.GEMINI_API_KEY });
|
|
45
|
+
const exampleImages = await getExampleImages();
|
|
46
|
+
|
|
47
|
+
let textPrompt = `
|
|
48
|
+
You are an expert technical marketing designer creating a high-converting, minimalist 16:9 blog cover image. Strictly match the visual aesthetic of the provided example images.
|
|
49
|
+
|
|
50
|
+
CANVAS & TYPOGRAPHY:
|
|
51
|
+
- Use a pure solid white background.
|
|
52
|
+
- Render the text in a heavy, bold, black sans-serif font.
|
|
53
|
+
- Perfectly center-align the text block in the middle of the canvas, leaving generous negative space (padding) around the edges.
|
|
54
|
+
- Break the text naturally into 1 to 3 balanced lines (ABSOLUTE MAXIMUM 3 LINES).
|
|
55
|
+
|
|
56
|
+
THE EXACT TEXT TO RENDER:
|
|
57
|
+
"${title}"
|
|
58
|
+
|
|
59
|
+
LOGO INTEGRATION RULES:
|
|
60
|
+
Carefully analyze the provided reference logo(s) and integrate them into the text flow using these exact rules:
|
|
61
|
+
1. IF the logo is a 'Wordmark' (it contains the brand's name text): Use it inline to completely replace that specific word in the text string.
|
|
62
|
+
2. IF the logo is a 'Logomark' (a standalone icon or character): Place it immediately adjacent to the typed brand name in the text.
|
|
63
|
+
3. TYPOGRAPHIC REPLACEMENT: If an icon is geometrically similar to a letter in the brand name (like the letter 'O'), creatively substitute the letter with the icon.
|
|
64
|
+
|
|
65
|
+
AESTHETIC TOUCHES:
|
|
66
|
+
Keep the composition radically uncluttered. DO NOT add random, unrelated third-party logos or generic floating UI icons unless explicitly requested in the title. The composition MUST be incredibly clean. If you add a UI element, it must make perfect contextual sense. The primary focus must remain on the bold text and the specific requested logo.
|
|
67
|
+
`;
|
|
68
|
+
|
|
69
|
+
if (criticalFeedback) {
|
|
70
|
+
textPrompt += `\nCRITICAL FEEDBACK FROM PREVIOUS ATTEMPT: ${criticalFeedback}. You MUST fix these errors in this new generation or you will be penalized.\n`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const parts = [];
|
|
74
|
+
|
|
75
|
+
// 1. Examples
|
|
76
|
+
if (exampleImages.length > 0) {
|
|
77
|
+
parts.push({ text: 'Here are some reference examples for the desired style and layout:' });
|
|
78
|
+
parts.push(...exampleImages);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// 2. Instructions + Title
|
|
82
|
+
parts.push({ text: textPrompt });
|
|
83
|
+
|
|
84
|
+
// 3. Logo
|
|
85
|
+
if (logoData) {
|
|
86
|
+
parts.push({ text: 'Here is the reference logo to include in the image:' });
|
|
87
|
+
parts.push({
|
|
88
|
+
inlineData: {
|
|
89
|
+
data: logoData.data,
|
|
90
|
+
mimeType: logoData.mimeType
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const contents = [{ role: 'user', parts }];
|
|
96
|
+
|
|
97
|
+
const response = await ai.models.generateContent({
|
|
98
|
+
model: 'gemini-3.1-flash-image-preview',
|
|
99
|
+
contents,
|
|
100
|
+
config: {
|
|
101
|
+
responseModalities: ['TEXT', 'IMAGE'],
|
|
102
|
+
imageConfig: {
|
|
103
|
+
aspectRatio: '16:9',
|
|
104
|
+
numberOfImages: 1
|
|
105
|
+
},
|
|
106
|
+
tools: [{ googleSearch: {} }]
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
let base64Image = null;
|
|
111
|
+
let textOutput = '';
|
|
112
|
+
|
|
113
|
+
for (const part of response.candidates[0].content.parts) {
|
|
114
|
+
if (part.text) {
|
|
115
|
+
textOutput += part.text + '\n';
|
|
116
|
+
} else if (part.inlineData) {
|
|
117
|
+
base64Image = part.inlineData.data;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!base64Image) {
|
|
122
|
+
throw new Error('No image was generated in the response. Text returned: ' + textOutput);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return { base64Image, textOutput };
|
|
126
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { GoogleGenAI } from '@google/genai';
|
|
2
|
+
|
|
3
|
+
export async function validateImage(base64Image, title, hasLogo, apiKey) {
|
|
4
|
+
const ai = new GoogleGenAI({ apiKey });
|
|
5
|
+
|
|
6
|
+
const prompt = `
|
|
7
|
+
Analyze this blog cover image and verify if it meets the following CRITICAL requirements:
|
|
8
|
+
1. TEXT ACCURACY: Does the image contain the exact text: "${title}"? It must be legible and spelled correctly.
|
|
9
|
+
2. LOGO PRESENCE: ${hasLogo ? 'The image MUST contain the requested company logo.' : 'No specific logo was required.'}
|
|
10
|
+
3. IMAGE INTEGRITY: Is the image clear and not obviously corrupted or garbled?
|
|
11
|
+
4. UNWANTED ELEMENTS: Are there any redundant, misplaced, or entirely irrelevant third-party icons or UI elements that don't fit the strict title context? (If yes, the image is INVALID).
|
|
12
|
+
|
|
13
|
+
RELAXED RULES (DO NOT FAIL FOR THESE):
|
|
14
|
+
- Colored text is ALLOWED and ENCOURAGED if it matches the brand.
|
|
15
|
+
- Brand-colored logos are ALLOWED.
|
|
16
|
+
- Minimalist, subtle UI elements are allowed ONLY if they perfectly match the specific requested context.
|
|
17
|
+
- The background does NOT have to be pure white; subtle gradients or brand colors are acceptable.
|
|
18
|
+
|
|
19
|
+
Return a JSON object with:
|
|
20
|
+
- "isValid": boolean (true only if CRITICAL requirements are met)
|
|
21
|
+
- "issues": string (empty if isValid is true, otherwise describe what is wrong)
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
const contents = [
|
|
25
|
+
{
|
|
26
|
+
role: 'user',
|
|
27
|
+
parts: [
|
|
28
|
+
{ text: prompt },
|
|
29
|
+
{
|
|
30
|
+
inlineData: {
|
|
31
|
+
data: base64Image,
|
|
32
|
+
mimeType: 'image/png',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
const response = await ai.models.generateContent({
|
|
40
|
+
model: 'gemini-3.1-pro-preview',
|
|
41
|
+
contents,
|
|
42
|
+
config: {
|
|
43
|
+
responseMimeType: 'application/json',
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const text = response.candidates[0].content.parts[0].text;
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
return JSON.parse(text);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
throw new Error(`Failed to parse validation response: ${text}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import sharp from 'sharp';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Fetches a logo from a domain or URL and converts it to PNG.
|
|
5
|
+
* @param {string} logoInput - Domain (e.g., 'google.com') or full URL.
|
|
6
|
+
* @param {string} [clientId] - Brandfetch Client ID.
|
|
7
|
+
* @returns {Promise<{ data: string, mimeType: string } | null>}
|
|
8
|
+
*/
|
|
9
|
+
export async function fetchLogo(logoInput, clientId) {
|
|
10
|
+
let url;
|
|
11
|
+
const headers = {
|
|
12
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',
|
|
13
|
+
'Accept': 'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8'
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
if (logoInput.startsWith('http://') || logoInput.startsWith('https://')) {
|
|
17
|
+
url = logoInput;
|
|
18
|
+
} else {
|
|
19
|
+
// Assume it's a domain, use Brandfetch API
|
|
20
|
+
url = `https://cdn.brandfetch.io/domain/${logoInput}/w/820/h/110/theme/dark/logo?c=${clientId}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
let response;
|
|
25
|
+
if (!logoInput.startsWith('http')) {
|
|
26
|
+
try {
|
|
27
|
+
response = await fetch(url, { headers });
|
|
28
|
+
} catch (e) {
|
|
29
|
+
response = { ok: false, headers: new Headers() };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let contentType = response.headers.get('content-type');
|
|
33
|
+
if (!response.ok || !contentType?.includes('image')) {
|
|
34
|
+
const hunterUrl = `https://logos.hunter.io/${logoInput}`;
|
|
35
|
+
try {
|
|
36
|
+
response = await fetch(hunterUrl, { headers });
|
|
37
|
+
} catch (e) {
|
|
38
|
+
response = { ok: false, headers: new Headers() };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
contentType = response.headers.get('content-type');
|
|
42
|
+
if (!response.ok || !contentType?.includes('image')) {
|
|
43
|
+
const fallbackUrl = `https://icon.horse/icon/${logoInput}`;
|
|
44
|
+
try {
|
|
45
|
+
response = await fetch(fallbackUrl, { headers });
|
|
46
|
+
} catch (e) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
contentType = response.headers.get('content-type');
|
|
51
|
+
if (!response.ok || !contentType?.includes('image')) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (contentType === 'image/x-icon' || contentType === 'image/vnd.microsoft.icon') {
|
|
56
|
+
throw new Error('Unsupported ICO format');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
response = await fetch(url, { headers });
|
|
62
|
+
const contentType = response.headers.get('content-type');
|
|
63
|
+
if (!response.ok || !contentType?.includes('image')) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
69
|
+
const buffer = Buffer.from(arrayBuffer);
|
|
70
|
+
|
|
71
|
+
// Convert to PNG using sharp
|
|
72
|
+
const pngBuffer = await sharp(buffer)
|
|
73
|
+
.png()
|
|
74
|
+
.toBuffer();
|
|
75
|
+
|
|
76
|
+
const base64String = pngBuffer.toString('base64');
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
data: base64String,
|
|
80
|
+
mimeType: 'image/png'
|
|
81
|
+
};
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error(`Error fetching logo:`, error.message);
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# claude-md-generator — Environment Variables
|
|
2
|
+
# =============================================
|
|
3
|
+
# Gemini is required for generating the CLAUDE.md content from analysis.
|
|
4
|
+
|
|
5
|
+
# Required: Google Gemini API key for CLAUDE.md generation
|
|
6
|
+
# Get it: aistudio.google.com, Get API key
|
|
7
|
+
GEMINI_API_KEY=your_gemini_api_key_here
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# claude-md-generator
|
|
2
|
+
|
|
3
|
+
<img width="1280" height="640" alt="claude-md-generator" src="https://github.com/user-attachments/assets/0e295271-2216-47f7-828f-845c98ef0298" />
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
Reads your codebase and writes a CLAUDE.md that gives Claude Code the context it needs: build commands, code conventions, architecture notes, and gotchas. Stays under 200 lines.
|
|
7
|
+
|
|
8
|
+
## What It Does
|
|
9
|
+
|
|
10
|
+
- Scans project files: package.json, tsconfig.json, linter configs, Makefile, directory structure
|
|
11
|
+
- Extracts all build, test, lint, and dev commands
|
|
12
|
+
- Identifies code style conventions that differ from defaults (path aliases, export patterns, naming)
|
|
13
|
+
- Maps non-obvious architecture decisions
|
|
14
|
+
- Finds gotchas: auto-generated files, required env var setup, test dependencies
|
|
15
|
+
- Generates CLAUDE.md using Gemini, then verifies it stays under 200 lines
|
|
16
|
+
- If CLAUDE.md already exists, improves it without discarding custom content
|
|
17
|
+
|
|
18
|
+
## Requirements
|
|
19
|
+
|
|
20
|
+
| Requirement | Purpose | How to Set Up |
|
|
21
|
+
|------------|---------|--------------|
|
|
22
|
+
| Gemini API key | CLAUDE.md generation from codebase analysis | aistudio.google.com, Get API key |
|
|
23
|
+
|
|
24
|
+
## Setup
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
cp .env.example .env
|
|
28
|
+
# Add GEMINI_API_KEY
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## How to Use
|
|
32
|
+
|
|
33
|
+
From the project root you want to document:
|
|
34
|
+
```
|
|
35
|
+
"Generate a CLAUDE.md for this project"
|
|
36
|
+
"Create a CLAUDE.md"
|
|
37
|
+
"Write Claude configuration for this repo"
|
|
38
|
+
"Help Claude understand this codebase"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
To update an existing CLAUDE.md:
|
|
42
|
+
```
|
|
43
|
+
"Update my CLAUDE.md: we added Vitest and changed the build system"
|
|
44
|
+
"Improve my existing CLAUDE.md"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## What Goes in CLAUDE.md
|
|
48
|
+
|
|
49
|
+
| Section | Include | Skip |
|
|
50
|
+
|---------|---------|------|
|
|
51
|
+
| Commands | Exact runnable commands, flags needed, env vars required | `npm install` and other obvious ones |
|
|
52
|
+
| Architecture | Non-obvious structure, auto-generated directories | "src contains source files" |
|
|
53
|
+
| Code Style | Path aliases, export conventions, non-default settings | Indent size (formatter handles it) |
|
|
54
|
+
| Testing | Required setup, how to run one test | "we use Jest" (visible from package.json) |
|
|
55
|
+
| Gotchas | Auto-generated files, env var order, known intentional issues | Things derivable from the code |
|
|
56
|
+
|
|
57
|
+
## Why Under 200 Lines
|
|
58
|
+
|
|
59
|
+
Long CLAUDE.md files get ignored. Claude loads the full file into context every session: a bloated CLAUDE.md with obvious content trains Claude to skim it. A tight 100-150 line CLAUDE.md with only non-obvious facts gets read and used.
|
|
60
|
+
|
|
61
|
+
The skill cuts aggressively: if a section says only things Claude can infer from the code, it removes it.
|
|
62
|
+
|
|
63
|
+
## Project Structure
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
claude-md-generator/
|
|
67
|
+
├── SKILL.md
|
|
68
|
+
├── README.md
|
|
69
|
+
├── .env.example
|
|
70
|
+
├── evals/
|
|
71
|
+
│ └── evals.json
|
|
72
|
+
└── references/
|
|
73
|
+
└── section-guide.md
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## License
|
|
77
|
+
|
|
78
|
+
MIT
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: claude-md-generator
|
|
3
|
+
description: ''
|
|
4
|
+
compatibility: [claude-code, gemini-cli, github-copilot]
|
|
5
|
+
author: OpenDirectory
|
|
6
|
+
version: 1.0.0
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# CLAUDE.md Generator
|
|
10
|
+
|
|
11
|
+
Read the codebase. Write a CLAUDE.md that tells Claude exactly what it needs: no more, no less.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
**Critical rule:** A good CLAUDE.md is under 100 lines. It contains only information Claude cannot derive from reading the code itself. Do not auto-write the file: always show the draft and wait for user approval first.
|
|
16
|
+
|
|
17
|
+
**Code snippet rule:** Never include inline code examples in CLAUDE.md. Instead use `file.ts:42` references. Code in CLAUDE.md wastes tokens and goes stale.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Step 1: Detect Mode
|
|
22
|
+
|
|
23
|
+
Determine which of three modes to run:
|
|
24
|
+
|
|
25
|
+
**create**: No CLAUDE.md exists. Write one from scratch.
|
|
26
|
+
**update**: A CLAUDE.md exists. Improve it without discarding custom content.
|
|
27
|
+
**audit**: Score all CLAUDE.md files in the project A-F and output a quality report. If the user says "audit", "check", "review", or "grade" my CLAUDE.md, run audit mode.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Discover ALL CLAUDE.md locations
|
|
31
|
+
find . -name "CLAUDE.md" -not -path "*/node_modules/*" -not -path "*/.git/*" 2>/dev/null
|
|
32
|
+
ls ~/.claude/CLAUDE.md 2>/dev/null && echo "Global CLAUDE.md found"
|
|
33
|
+
ls .claude.local.md 2>/dev/null && echo ".claude.local.md found"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
If multiple CLAUDE.md files are found: list them. Ask: "Found CLAUDE.md in [locations]. Should I update all of them or just [root]?"
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Step 2: Audit Mode (skip to Step 3 if create/update)
|
|
41
|
+
|
|
42
|
+
For each CLAUDE.md found, score it A-F using this rubric:
|
|
43
|
+
|
|
44
|
+
| Criterion | What to check |
|
|
45
|
+
|-----------|--------------|
|
|
46
|
+
| Commands | Build/test/lint commands present and runnable? |
|
|
47
|
+
| Architecture | Non-obvious structure explained? |
|
|
48
|
+
| Non-obvious patterns | Gotchas, generated files, env var order documented? |
|
|
49
|
+
| Conciseness | Under 100 lines? No obvious filler? |
|
|
50
|
+
| Currency | Commands still match current package.json/Makefile? |
|
|
51
|
+
| Actionability | Can a new contributor follow this without asking questions? |
|
|
52
|
+
|
|
53
|
+
Score: 90-100 = A, 70-89 = B, 50-69 = C, 30-49 = D, 0-29 = F
|
|
54
|
+
|
|
55
|
+
Present as a table:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
## CLAUDE.md Audit Report
|
|
59
|
+
|
|
60
|
+
| File | Score | Grade | Top Issues |
|
|
61
|
+
|------|-------|-------|-----------|
|
|
62
|
+
| ./CLAUDE.md | 72 | B | Missing gotchas section, test command outdated |
|
|
63
|
+
| ./packages/api/CLAUDE.md | 45 | D | No commands, 340 lines (too long), stale arch notes |
|
|
64
|
+
|
|
65
|
+
**Overall: B (72/100)**
|
|
66
|
+
|
|
67
|
+
Issues found:
|
|
68
|
+
- ./packages/api/CLAUDE.md: 340 lines: well over the 100-line target
|
|
69
|
+
- ./packages/api/CLAUDE.md: Test command references `jest` but package.json uses `vitest`
|
|
70
|
+
- ./CLAUDE.md: No Gotchas section: most valuable section is missing
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
After the report, ask: "Want me to fix any of these? (all / just root / specify)"
|
|
74
|
+
|
|
75
|
+
If user says yes, continue to Step 3 for each file they want fixed.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Step 3: Scan Project Structure
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Project type and package manager
|
|
83
|
+
ls package.json yarn.lock pnpm-lock.yaml bun.lockb requirements.txt pyproject.toml Cargo.toml go.mod 2>/dev/null
|
|
84
|
+
|
|
85
|
+
# Top-level directory structure
|
|
86
|
+
find . -maxdepth 2 -type d \
|
|
87
|
+
| grep -v node_modules | grep -v .git | grep -v __pycache__ \
|
|
88
|
+
| grep -v ".next" | grep -v dist | grep -v build | sort
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Step 4: Extract Build and Test Commands
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
# npm/yarn/pnpm/bun scripts
|
|
97
|
+
cat package.json 2>/dev/null \
|
|
98
|
+
| python3 -c "
|
|
99
|
+
import sys, json
|
|
100
|
+
d = json.load(sys.stdin)
|
|
101
|
+
for name, cmd in d.get('scripts', {}).items():
|
|
102
|
+
print(f'{name}: {cmd}')
|
|
103
|
+
"
|
|
104
|
+
|
|
105
|
+
# Python, Go, Rust Makefiles
|
|
106
|
+
cat Makefile 2>/dev/null | grep -E "^[a-z].*:" | head -20
|
|
107
|
+
|
|
108
|
+
# Go
|
|
109
|
+
cat go.mod 2>/dev/null | head -5
|
|
110
|
+
|
|
111
|
+
# Rust
|
|
112
|
+
cat Cargo.toml 2>/dev/null | grep -E "^\[" | head -10
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Identify the exact commands for: build, test (all), test (single file/name), dev server, lint/typecheck. Note any env vars required to run them.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Step 5: Find Code Style and Gotchas
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
# Import aliases (most commonly missed)
|
|
123
|
+
python3 -c "
|
|
124
|
+
import json, sys
|
|
125
|
+
try:
|
|
126
|
+
d = json.load(open('tsconfig.json'))
|
|
127
|
+
paths = d.get('compilerOptions', {}).get('paths', {})
|
|
128
|
+
if paths: print('Import aliases:', json.dumps(paths, indent=2))
|
|
129
|
+
except: pass
|
|
130
|
+
" 2>/dev/null
|
|
131
|
+
|
|
132
|
+
# Environment variables required
|
|
133
|
+
cat .env.example 2>/dev/null | grep -v "^#" | grep -v "^$" | head -20
|
|
134
|
+
|
|
135
|
+
# Auto-generated files (must not be edited)
|
|
136
|
+
find . -path "*/node_modules" -prune -o -name "*.ts" -print \
|
|
137
|
+
| xargs grep -l "DO NOT EDIT\|@generated\|Generated by" 2>/dev/null | head -5
|
|
138
|
+
|
|
139
|
+
# Test setup requirements
|
|
140
|
+
cat jest.config.js jest.config.ts vitest.config.ts 2>/dev/null | head -30
|
|
141
|
+
|
|
142
|
+
# Database/migration setup
|
|
143
|
+
ls migrations/ prisma/ drizzle/ db/ 2>/dev/null
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**What counts as a Gotcha** (include these, skip everything else):
|
|
147
|
+
- Files that are auto-generated (must not edit)
|
|
148
|
+
- Env vars required BEFORE tests run
|
|
149
|
+
- Non-default import alias mappings
|
|
150
|
+
- Test commands that require a running service
|
|
151
|
+
- Known intentional quirks (workarounds, not bugs)
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Step 6: Generate CLAUDE.md Draft with Gemini
|
|
156
|
+
|
|
157
|
+
Compile all findings and generate the draft:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
cat > /tmp/claude-md-request.json << 'ENDJSON'
|
|
161
|
+
{
|
|
162
|
+
"system_instruction": {
|
|
163
|
+
"parts": [{
|
|
164
|
+
"text": "Write a CLAUDE.md file for a software project. Rules: (1) Under 100 lines total. (2) Only include what Claude cannot derive from reading the code. (3) No inline code examples: use file.ts:42 references instead. (4) Sections: Commands, Code Style (only non-defaults), Testing (only if setup needed), Gotchas (required: what trips people up). Skip any section that has nothing non-obvious to say. (5) All commands in code blocks. (6) Preferred order: short Project Overview (1-2 sentences, only if non-obvious), Commands, Architecture (only non-obvious structure), Code Style, Testing, Gotchas. (7) Do not use em dashes. (8) Output only the CLAUDE.md content, no commentary."
|
|
165
|
+
}]
|
|
166
|
+
},
|
|
167
|
+
"contents": [{
|
|
168
|
+
"parts": [{
|
|
169
|
+
"text": "PROJECT_ANALYSIS_HERE"
|
|
170
|
+
}]
|
|
171
|
+
}],
|
|
172
|
+
"generationConfig": {
|
|
173
|
+
"temperature": 0.3,
|
|
174
|
+
"maxOutputTokens": 2048
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
ENDJSON
|
|
178
|
+
|
|
179
|
+
curl -s -X POST \
|
|
180
|
+
"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=$GEMINI_API_KEY" \
|
|
181
|
+
-H "Content-Type: application/json" \
|
|
182
|
+
-d @/tmp/claude-md-request.json \
|
|
183
|
+
| python3 -c "import sys,json; d=json.load(sys.stdin); print(d['candidates'][0]['content']['parts'][0]['text'])"
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Replace `PROJECT_ANALYSIS_HERE` with findings from Steps 3-5.
|
|
187
|
+
|
|
188
|
+
**For large projects (50+ files):** Add a `@path` pointer at the bottom of CLAUDE.md instead of inline detail:
|
|
189
|
+
|
|
190
|
+
```markdown
|
|
191
|
+
## Extended Reference
|
|
192
|
+
See @docs/ai-context/architecture.md for full module map.
|
|
193
|
+
See @docs/ai-context/testing.md for integration test setup details.
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Write the referenced files to `docs/ai-context/` with the detail that would not fit in 100 lines.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Step 7: Self-QA
|
|
201
|
+
|
|
202
|
+
Before presenting the draft, check:
|
|
203
|
+
|
|
204
|
+
- [ ] Under 100 lines (count: `echo "$CONTENT" | wc -l`)
|
|
205
|
+
- [ ] No inline code examples (only `file.ts:42` references or shell commands)
|
|
206
|
+
- [ ] All commands in code blocks and runnable as-is
|
|
207
|
+
- [ ] Gotchas section present with at least one real entry
|
|
208
|
+
- [ ] No section that says only things obvious from the files
|
|
209
|
+
- [ ] No em dashes
|
|
210
|
+
- [ ] No marketing words or filler phrases ("This project uses React to...")
|
|
211
|
+
- [ ] Import aliases documented if they exist
|
|
212
|
+
- [ ] Auto-generated files marked "do not edit" if they exist
|
|
213
|
+
|
|
214
|
+
If any check fails, revise before presenting.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Step 8: Present Draft and Wait for Approval
|
|
219
|
+
|
|
220
|
+
**Never write the file without user approval.**
|
|
221
|
+
|
|
222
|
+
Present the draft in a code block:
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
## Draft CLAUDE.md ([N] lines)
|
|
226
|
+
|
|
227
|
+
[full draft content here]
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
Write this to CLAUDE.md? (yes / edit first / cancel)
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
If user says **yes**: write the file, then confirm:
|
|
234
|
+
"CLAUDE.md written ([N] lines). Sections: [list of ## headers]."
|
|
235
|
+
|
|
236
|
+
If user says **edit first**: apply their edits, re-show the draft.
|
|
237
|
+
|
|
238
|
+
If user says **cancel**: stop.
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## What NOT to Include
|
|
243
|
+
|
|
244
|
+
- Language/framework version ("This is a TypeScript project")
|
|
245
|
+
- How the framework works (Claude already knows React, FastAPI, etc.)
|
|
246
|
+
- List of all dependencies
|
|
247
|
+
- Style rules the linter already enforces (indent size, quote style)
|
|
248
|
+
- Content that duplicates README.md
|
|
249
|
+
- Inline code examples or multi-line snippets
|
|
250
|
+
- Anything that would be identical for any project using the same stack
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "claude-md-generator",
|
|
3
|
+
"evals": [
|
|
4
|
+
{
|
|
5
|
+
"id": 1,
|
|
6
|
+
"prompt": "Generate a CLAUDE.md for this project.",
|
|
7
|
+
"expected_output": "Agent runs all scan commands from Steps 1-5: directory structure, package.json scripts, tsconfig.json, ESLint config, test setup, .env.example. Calls Gemini with analysis to generate CLAUDE.md. Output is under 200 lines. Contains: Commands section with exact runnable commands, Code Style section noting any non-default aliases or export conventions, Testing section with any required setup, Gotchas section with at least one entry (auto-generated files, required env vars, etc.). Does not include obvious facts derivable from the files (e.g., 'This project uses TypeScript').",
|
|
8
|
+
"files": ["package.json", "tsconfig.json", ".eslintrc.json", ".env.example"]
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"id": 2,
|
|
12
|
+
"prompt": "Create a CLAUDE.md. The project already has one.",
|
|
13
|
+
"expected_output": "Agent detects existing CLAUDE.md at project root. Reads it before generating. Preserves any sections the user has manually added. Improves sections that are outdated or missing. Shows a diff summary: 'Kept: [sections]. Updated: [sections]. Added: [sections].' Final file is still under 200 lines. Does not wholesale replace the existing file without showing what changed.",
|
|
14
|
+
"files": ["CLAUDE.md", "package.json"]
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"id": 3,
|
|
18
|
+
"prompt": "Write a CLAUDE.md for this Python project.",
|
|
19
|
+
"expected_output": "Agent detects Python project from requirements.txt or pyproject.toml. Reads Makefile or pyproject.toml for commands. Identifies test runner (pytest, unittest). Notes any virtual environment setup required. Checks for generated files (migrations, protobuf stubs). Commands section shows exact Python commands (pytest, python -m, etc.). Gotchas section notes any required env vars and any generated files that should not be edited.",
|
|
20
|
+
"files": ["requirements.txt", "pyproject.toml", "Makefile"]
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": 4,
|
|
24
|
+
"prompt": "Generate CLAUDE.md for this monorepo.",
|
|
25
|
+
"expected_output": "Agent detects monorepo from presence of packages/ or apps/ directory with multiple package.json files. Notes workspace structure in Architecture section. Documents root-level commands vs package-level commands. Notes any shared packages and how they are linked. Keeps the output under 200 lines by focusing on the most important commands and conventions. Does not try to document every package: focuses on the top-level patterns and the most commonly developed packages.",
|
|
26
|
+
"files": ["package.json", "packages/"]
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": 5,
|
|
30
|
+
"prompt": "Create a CLAUDE.md. There are no config files: it's a small Go project with just main.go and a Makefile.",
|
|
31
|
+
"expected_output": "Agent finds main.go and Makefile. Reads Makefile for build/test/run targets. Notes that there is no package manager. Commands section shows make targets. Architecture section notes entry point (main.go). Gotchas section notes any env vars referenced in main.go. Output is short (under 50 lines) because there is not much to document: the agent does not pad with filler content. Does not invent sections for things that do not exist in this project.",
|
|
32
|
+
"files": ["main.go", "Makefile"]
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|