@opendirectory.dev/skills 0.1.44 → 0.1.45

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.
@@ -0,0 +1,152 @@
1
+ # Launch Kit
2
+
3
+ ## Repo Summary
4
+ pydantic/pydantic
5
+
6
+ ## Audience
7
+ end users
8
+
9
+ ## Description
10
+ Data validation using Python type hints
11
+
12
+ # Show HN Draft
13
+
14
+ ## Title
15
+ Show HN: pydantic/pydantic - Data validation using Python type hints
16
+
17
+ ## Short Intro
18
+ Low-confidence repo context. Title-only draft recommended.
19
+
20
+ ## Core Explanation
21
+ Not enough README signal to write a reliable body.
22
+
23
+ ## Feedback Ask
24
+ Please review the repo framing and README quality before posting.
25
+
26
+ ## Notes
27
+ - Confidence: high
28
+
29
+ # Product Hunt Draft
30
+
31
+ ## Tagline
32
+ pydantic/pydantic: open-source project for end users
33
+
34
+ ## Description
35
+ Built for end users. Keeps the pitch grounded in repo facts.
36
+
37
+ ## Maker Comment
38
+ Draft only. Edit before posting.
39
+
40
+ ## Notes
41
+ - Confidence: high
42
+
43
+ # Reddit Drafts
44
+
45
+ ## Variant 1
46
+ Subreddit: r/SideProject (general maker context)
47
+
48
+ I built pydantic/pydantic for end users because data validation using python type hints.
49
+
50
+ This is a fit for general maker context; I’m sharing it here to get feedback, not to spam the subreddit.
51
+
52
+ It focuses on the repo metadata and README.
53
+
54
+ I built this myself and will only post where the subreddit rules allow self-promo.
55
+
56
+ If this fits the community, I’d appreciate feedback on the launch angle and any missing context.
57
+
58
+ ## Variant 2
59
+ Subreddit: r/opensource (open-source launch context)
60
+
61
+ I built pydantic/pydantic for end users because data validation using python type hints and I think the interesting part is the launch workflow, not the code itself.
62
+
63
+ I’m posting in open-source launch context because it’s an OSS launch question first and a product post second.
64
+
65
+ The repo signals I leaned on were README text and metadata.
66
+
67
+ I built this myself and will only post where the subreddit rules allow self-promo.
68
+
69
+ Curious whether this framing would land with open-source launch context.
70
+
71
+ ## Variant 3
72
+ Subreddit: r/programming (developer audience)
73
+
74
+ I built pydantic/pydantic for end users because data validation using python type hints, and the challenge was keeping the launch copy specific without turning it into jargon.
75
+
76
+ For developer audience, I’d mainly want feedback on whether the problem/solution framing is actually useful to builders.
77
+
78
+ The repo cues I used were Data validation using Python type hints and README details.
79
+
80
+ I built this myself and will only post where the subreddit rules allow self-promo.
81
+
82
+ Would this problem/solution framing feel useful to builders in developer audience?
83
+
84
+ ## Variant 4
85
+ Subreddit: r/devtools (developer tools audience)
86
+
87
+ I built pydantic/pydantic for end users because data validation using python type hints and I wanted a cleaner way to turn repo facts into launch copy.
88
+
89
+ This angle fits developer tools audience; I’m sharing it for feedback on the workflow, not as a promo blast.
90
+
91
+ The main angle here is Data validation using Python type hints, with README details as supporting context.
92
+
93
+ I built this myself and will only post where the subreddit rules allow self-promo.
94
+
95
+ Would this problem/solution framing feel useful to builders in developer tools audience?
96
+
97
+ ## Variant 5
98
+ Subreddit: r/commandline (command-line users)
99
+
100
+ I built pydantic/pydantic for end users because data validation using python type hints.
101
+
102
+ This is a fit for command-line users; I’m sharing it here to get feedback, not to spam the subreddit.
103
+
104
+ It focuses on the repo metadata and README.
105
+
106
+ I built this myself and will only post where the subreddit rules allow self-promo.
107
+
108
+ If this fits the community, I’d appreciate feedback on the launch angle and any missing context.
109
+
110
+ ## Notes
111
+ - Confidence: high
112
+
113
+ # Twitter/X Thread
114
+
115
+ ## Tweet 1
116
+ The launch story for pydantic/pydantic should start with data validation using python type hints, not the implementation details.
117
+
118
+ ## Tweet 2
119
+ The hard part isn’t the code; it’s explaining why data validation using python type hints matters to the right audience.
120
+
121
+ ## Tweet 3
122
+ This kit turns the repo’s own signals into a tighter launch story without inventing traction.
123
+
124
+ ## Tweet 4
125
+ It reads the README + GitHub metadata, then drafts Show HN, Product Hunt, Reddit, and X posts that stay close to the repo. The supporting detail I’d foreground is readme and metadata.
126
+
127
+ ## Tweet 5
128
+ Feedback welcome on the repo angle: add the repo link before posting
129
+
130
+ ## Notes
131
+ - Confidence: high
132
+
133
+ # First-Week Launch Plan
134
+
135
+ ## Day 0
136
+ Prep the launch copy, verify links, and trim anything that sounds generic for pydantic/pydantic before posting.
137
+
138
+ ## Launch day
139
+ Post the Show HN draft, Product Hunt draft, 5 Reddit variants, and the X thread. Keep replies factual and point people to the repo.
140
+
141
+ ## Day 1-2
142
+ Respond to feedback, update the README if commenters point out missing context, and tighten the launch framing around data validation using python type hints for end users.
143
+
144
+ ## Day 3-7
145
+ Publish a short follow-up post or changelog note, answer questions in communities where you posted, and reuse useful feedback to improve docs, examples, or onboarding.
146
+
147
+ ## Notes
148
+ - Confidence: high
149
+
150
+ # Assumptions / Low-Confidence Notes
151
+
152
+ - Confidence: high
@@ -0,0 +1,152 @@
1
+ # Launch Kit
2
+
3
+ ## Repo Summary
4
+ vercel/next.js
5
+
6
+ ## Audience
7
+ authors and maintainers
8
+
9
+ ## Description
10
+ The React Framework
11
+
12
+ # Show HN Draft
13
+
14
+ ## Title
15
+ Show HN: vercel/next.js - The React Framework
16
+
17
+ ## Short Intro
18
+ Low-confidence repo context. Title-only draft recommended.
19
+
20
+ ## Core Explanation
21
+ Not enough README signal to write a reliable body.
22
+
23
+ ## Feedback Ask
24
+ Please review the repo framing and README quality before posting.
25
+
26
+ ## Notes
27
+ - Confidence: high
28
+
29
+ # Product Hunt Draft
30
+
31
+ ## Tagline
32
+ vercel/next.js: framework for authors and maintainers
33
+
34
+ ## Description
35
+ Built for authors and maintainers. Keeps the pitch grounded in repo facts.
36
+
37
+ ## Maker Comment
38
+ Draft only. Edit before posting.
39
+
40
+ ## Notes
41
+ - Confidence: high
42
+
43
+ # Reddit Drafts
44
+
45
+ ## Variant 1
46
+ Subreddit: r/SideProject (general maker context)
47
+
48
+ I built vercel/next.js for authors and maintainers because the react framework.
49
+
50
+ This is a fit for general maker context; I’m sharing it here to get feedback, not to spam the subreddit.
51
+
52
+ It focuses on the repo metadata and README.
53
+
54
+ I built this myself and will only post where the subreddit rules allow self-promo.
55
+
56
+ If this fits the community, I’d appreciate feedback on the launch angle and any missing context.
57
+
58
+ ## Variant 2
59
+ Subreddit: r/opensource (open-source launch context)
60
+
61
+ I built vercel/next.js for authors and maintainers because the react framework and I think the interesting part is the launch workflow, not the code itself.
62
+
63
+ I’m posting in open-source launch context because it’s an OSS launch question first and a product post second.
64
+
65
+ The repo signals I leaned on were README text and metadata.
66
+
67
+ I built this myself and will only post where the subreddit rules allow self-promo.
68
+
69
+ Curious whether this framing would land with open-source launch context.
70
+
71
+ ## Variant 3
72
+ Subreddit: r/programming (developer audience)
73
+
74
+ I built vercel/next.js for authors and maintainers because the react framework, and the challenge was keeping the launch copy specific without turning it into jargon.
75
+
76
+ For developer audience, I’d mainly want feedback on whether the problem/solution framing is actually useful to builders.
77
+
78
+ The repo cues I used were The React Framework and README details.
79
+
80
+ I built this myself and will only post where the subreddit rules allow self-promo.
81
+
82
+ Would this problem/solution framing feel useful to builders in developer audience?
83
+
84
+ ## Variant 4
85
+ Subreddit: r/devtools (developer tools audience)
86
+
87
+ I built vercel/next.js for authors and maintainers because the react framework and I wanted a cleaner way to turn repo facts into launch copy.
88
+
89
+ This angle fits developer tools audience; I’m sharing it for feedback on the workflow, not as a promo blast.
90
+
91
+ The main angle here is The React Framework, with README details as supporting context.
92
+
93
+ I built this myself and will only post where the subreddit rules allow self-promo.
94
+
95
+ Would this problem/solution framing feel useful to builders in developer tools audience?
96
+
97
+ ## Variant 5
98
+ Subreddit: r/commandline (command-line users)
99
+
100
+ I built vercel/next.js for authors and maintainers because the react framework.
101
+
102
+ This is a fit for command-line users; I’m sharing it here to get feedback, not to spam the subreddit.
103
+
104
+ It focuses on the repo metadata and README.
105
+
106
+ I built this myself and will only post where the subreddit rules allow self-promo.
107
+
108
+ If this fits the community, I’d appreciate feedback on the launch angle and any missing context.
109
+
110
+ ## Notes
111
+ - Confidence: high
112
+
113
+ # Twitter/X Thread
114
+
115
+ ## Tweet 1
116
+ The launch story for vercel/next.js should start with the react framework, not the implementation details.
117
+
118
+ ## Tweet 2
119
+ The hard part isn’t the code; it’s explaining why the react framework matters to the right audience.
120
+
121
+ ## Tweet 3
122
+ This kit turns the repo’s own signals into a tighter launch story without inventing traction.
123
+
124
+ ## Tweet 4
125
+ It reads the README + GitHub metadata, then drafts Show HN, Product Hunt, Reddit, and X posts that stay close to the repo. The supporting detail I’d foreground is readme and metadata.
126
+
127
+ ## Tweet 5
128
+ Feedback welcome on the repo angle: add the repo link before posting
129
+
130
+ ## Notes
131
+ - Confidence: high
132
+
133
+ # First-Week Launch Plan
134
+
135
+ ## Day 0
136
+ Prep the launch copy, verify links, and trim anything that sounds generic for vercel/next.js before posting.
137
+
138
+ ## Launch day
139
+ Post the Show HN draft, Product Hunt draft, 5 Reddit variants, and the X thread. Keep replies factual and point people to the repo.
140
+
141
+ ## Day 1-2
142
+ Respond to feedback, update the README if commenters point out missing context, and tighten the launch framing around the react framework for authors and maintainers.
143
+
144
+ ## Day 3-7
145
+ Publish a short follow-up post or changelog note, answer questions in communities where you posted, and reuse useful feedback to improve docs, examples, or onboarding.
146
+
147
+ ## Notes
148
+ - Confidence: high
149
+
150
+ # Assumptions / Low-Confidence Notes
151
+
152
+ - Confidence: high
Binary file
@@ -0,0 +1,9 @@
1
+ # Channel Rules
2
+
3
+ This file will hold the operational constraints for each launch channel.
4
+
5
+ Planned channels:
6
+ - Show HN
7
+ - Product Hunt
8
+ - Reddit
9
+ - Twitter/X
@@ -0,0 +1,10 @@
1
+ # Launch Framework
2
+
3
+ This file will hold the canonical launch brief structure for `oss-launch-kit`.
4
+
5
+ Planned sections:
6
+ - repo summary
7
+ - audience and problem framing
8
+ - evidence inventory
9
+ - launch angle
10
+ - channel notes
@@ -0,0 +1,3 @@
1
+ # Output Template
2
+
3
+ This file will define the final Markdown layout for the generated launch kit.
@@ -0,0 +1,335 @@
1
+ """Build a grounded product brief from GitHub repo context.
2
+
3
+ This is a deterministic Stage A transformer: it extracts factual signals from
4
+ repo metadata and README text, then assembles a concise launch brief that the
5
+ asset generator can safely use.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ import re
12
+ import sys
13
+ from typing import Any
14
+
15
+
16
+ def _clean(text: str | None) -> str:
17
+ return re.sub(r"\s+", " ", (text or "").strip())
18
+
19
+
20
+ def _strip_markdown(text: str | None) -> str:
21
+ cleaned = text or ""
22
+ cleaned = re.sub(r"\[([^\]]+)\]\([^\)]+\)", r"\1", cleaned)
23
+ cleaned = re.sub(r"\[([^\]]+)\]\[\]", r"\1", cleaned)
24
+ cleaned = re.sub(r"[`*_>#]", "", cleaned)
25
+ return _clean(cleaned)
26
+
27
+
28
+ def _first_sentence(text: str) -> str:
29
+ cleaned = _strip_markdown(text)
30
+ if not cleaned:
31
+ return ""
32
+ match = re.search(r"^(.+?[.!?])(\s|$)", cleaned)
33
+ return match.group(1).strip() if match else cleaned
34
+
35
+
36
+ def _readme_paragraphs(readme_text: str) -> list[str]:
37
+ blocks = [block.strip() for block in re.split(r"\n\s*\n", readme_text or "")]
38
+ return [block for block in blocks if block]
39
+
40
+
41
+ def _readme_bullets(readme_text: str) -> list[str]:
42
+ bullets = []
43
+ for line in (readme_text or "").splitlines():
44
+ if re.match(r"^\s*[-*+]\s+", line):
45
+ bullets.append(_strip_markdown(re.sub(r"^\s*[-*+]\s+", "", line)))
46
+ return bullets
47
+
48
+
49
+ def _infer_audience(repo_context: dict[str, Any], readme_text: str) -> tuple[str, str]:
50
+ text = f"{repo_context.get('description') or ''}\n{readme_text}".lower()
51
+ candidates = [
52
+ ("developers", ["developer", "developers", "devs", "engineers", "coding"]),
53
+ ("teams", ["team", "teams", "workspace", "collaboration"]),
54
+ ("authors and maintainers", ["maintainer", "maintainers", "author", "open source"]),
55
+ ("data/infra users", ["api", "cli", "server", "deploy", "infra", "pipeline"]),
56
+ ("end users", ["app", "users", "personal", "workflow"]),
57
+ ]
58
+ for label, keywords in candidates:
59
+ if any(keyword in text for keyword in keywords):
60
+ return label, "medium"
61
+ return "unknown", "low"
62
+
63
+
64
+ def _infer_project_type(repo_context: dict[str, Any], readme_text: str) -> str:
65
+ text = f"{repo_context.get('description') or ''}\n{readme_text}".lower()
66
+ if any(term in text for term in ["framework", "platform"]):
67
+ return "framework"
68
+ if any(term in text for term in ["library", "package", "module", "sdk"]):
69
+ return "library"
70
+ if any(term in text for term in ["cli", "tool", "automation", "workflow"]):
71
+ return "tool"
72
+ if any(term in text for term in ["app", "saas", "dashboard", "interface"]):
73
+ return "app"
74
+ if any(term in text for term in ["template", "boilerplate", "starter"]):
75
+ return "template"
76
+ return "project"
77
+
78
+
79
+ def _evaluate_channel_fitness(project_type: str, readiness: str) -> dict[str, str]:
80
+ """Evaluate fitness for launch channels based on project type and readiness."""
81
+ fitness = {
82
+ "show_hn": "medium",
83
+ "product_hunt": "medium",
84
+ "reddit": "medium",
85
+ "twitter_x": "high",
86
+ }
87
+
88
+ # Weak repo / Unready project logic
89
+ if readiness == "low":
90
+ return {
91
+ "show_hn": "low",
92
+ "product_hunt": "low",
93
+ "reddit": "low",
94
+ "twitter_x": "medium"
95
+ }
96
+
97
+ # Project type nuances for ready projects
98
+ if project_type in ["library", "tool", "framework"]:
99
+ fitness["product_hunt"] = "low"
100
+ fitness["show_hn"] = "high"
101
+ elif project_type == "app":
102
+ fitness["product_hunt"] = "high"
103
+ fitness["show_hn"] = "medium"
104
+ elif project_type == "template":
105
+ fitness["show_hn"] = "low"
106
+ fitness["product_hunt"] = "low"
107
+ fitness["reddit"] = "high"
108
+
109
+ return fitness
110
+
111
+
112
+ def _infer_problem(repo_context: dict[str, Any], readme_text: str) -> tuple[str, str]:
113
+ description = _strip_markdown(repo_context.get("description"))
114
+ if description:
115
+ return description, "medium"
116
+
117
+ paras = _readme_paragraphs(readme_text)
118
+ if paras:
119
+ return _first_sentence(paras[0]), "low"
120
+
121
+ return "Unknown from available repo metadata.", "low"
122
+
123
+
124
+ def _infer_value_prop(problem: str, key_features: list[str]) -> str:
125
+ if key_features:
126
+ first = key_features[0]
127
+ return f"Solves {problem.lower()} with {first.lower()}."
128
+ return f"Grounds launch copy in the repository's own README and metadata around {problem.lower()}."
129
+
130
+
131
+ def _extract_key_features(readme_text: str, repo_context: dict[str, Any]) -> list[str]:
132
+ bullets = _readme_bullets(readme_text)
133
+ features: list[str] = []
134
+ for bullet in bullets:
135
+ if len(features) >= 5:
136
+ break
137
+ if len(bullet) < 3:
138
+ continue
139
+ features.append(bullet)
140
+
141
+ if not features:
142
+ description = _strip_markdown(repo_context.get("description"))
143
+ if description:
144
+ features.append(description)
145
+
146
+ return features[:5]
147
+
148
+
149
+ def _build_links(repo_context: dict[str, Any]) -> dict[str, str | None]:
150
+ return {
151
+ "repo": repo_context.get("repo_url"),
152
+ "homepage": repo_context.get("homepage") or None,
153
+ "readme_source": repo_context.get("fetched_from", {}).get("readme_api"),
154
+ }
155
+
156
+
157
+ def _check_readiness_signals(readme_text: str, repo_context: dict[str, Any]) -> dict[str, bool]:
158
+ text = readme_text.lower()
159
+ desc = _clean(repo_context.get("description")).lower()
160
+ return {
161
+ "has_license": bool(repo_context.get("license")),
162
+ "has_install": any(term in text for term in ["# install", "npm install", "pip install", "pip3 install", "go get", "setup", "quickstart"]),
163
+ "has_usage_example": any(term in text for term in ["example", "usage", "how to use", "code snippet"]),
164
+ "has_contributing": any(term in text for term in ["contributing", "pull request", "license path", "issue tracker"]),
165
+ "has_quickstart": any(term in text for term in ["# quickstart", "tl;dr", "getting started", "one-command", "fast start"]),
166
+ "has_clear_positioning": len(desc) > 30 and any(term in desc for term in ["built for", "helps", "allows", "solves", "for devs", "for users"]),
167
+ "has_demo_or_proof": any(term in text for term in ["demo", "screenshot", "gif", "preview", "live link", "http", "view it"]),
168
+ "has_description": len(desc) > 20,
169
+ }
170
+
171
+
172
+ def _generate_fix_plan(signals: dict[str, bool]) -> list[dict[str, Any]]:
173
+ """Generate a prioritized list of fixes for the repository."""
174
+ plan = []
175
+
176
+ if not signals["has_quickstart"]:
177
+ plan.append({
178
+ "id": "add_quickstart_example",
179
+ "severity": "high",
180
+ "reason": "New users need a copy-paste example to reach 'first success' quickly.",
181
+ "suggested_fix": "Add a 'Quickstart' section at the top of README with a single command or minimal code snippet.",
182
+ "likely_files": ["README.md"]
183
+ })
184
+
185
+ if not signals["has_usage_example"]:
186
+ plan.append({
187
+ "id": "add_usage_example",
188
+ "severity": "high",
189
+ "reason": "Without a concrete usage example, users cannot see how to apply the project.",
190
+ "suggested_fix": "Add a minimal 'Usage' section with one realistic example and expected output.",
191
+ "likely_files": ["README.md", "docs/usage.md"]
192
+ })
193
+
194
+ if not signals["has_clear_positioning"]:
195
+ plan.append({
196
+ "id": "improve_positioning",
197
+ "severity": "high",
198
+ "reason": "The project description is too short or lacks clear target audience context.",
199
+ "suggested_fix": "Update the repository description to clearly state 'Who it is for' and 'What problem it solves'.",
200
+ "likely_files": ["GitHub Metadata", "README.md"]
201
+ })
202
+
203
+ if not signals["has_demo_or_proof"]:
204
+ plan.append({
205
+ "id": "add_demo_or_proof",
206
+ "severity": "medium",
207
+ "reason": "Visual proof or live demos significantly increase conversion and trust.",
208
+ "suggested_fix": "Add a screenshot, GIF, or a link to a live demo/sample output in the README.",
209
+ "likely_files": ["README.md"]
210
+ })
211
+
212
+ if not signals["has_license"]:
213
+ plan.append({
214
+ "id": "add_license",
215
+ "severity": "medium",
216
+ "reason": "OSS users need a clear license to feel safe adopting the code.",
217
+ "suggested_fix": "Add an MIT or Apache-2.0 LICENSE file to the repository root.",
218
+ "likely_files": ["LICENSE"]
219
+ })
220
+
221
+ if not signals["has_contributing"]:
222
+ plan.append({
223
+ "id": "add_contribution_guide",
224
+ "severity": "low",
225
+ "reason": "Contributors need clear expectations for how to propose changes.",
226
+ "suggested_fix": "Create a CONTRIBUTING.md with basic rules (how to open PRs, coding style, etc.).",
227
+ "likely_files": ["CONTRIBUTING.md"]
228
+ })
229
+
230
+ return plan
231
+
232
+
233
+ def build_product_brief(repo_context: dict[str, Any]) -> dict[str, Any]:
234
+ """Convert fetched repo context into a grounded launch brief."""
235
+
236
+ readme_text = repo_context.get("readme_text") or ""
237
+ repo_name = repo_context.get("full_name") or repo_context.get("name") or "unknown-repo"
238
+
239
+ problem, problem_confidence = _infer_problem(repo_context, readme_text)
240
+ audience, audience_confidence = _infer_audience(repo_context, readme_text)
241
+ key_features = _extract_key_features(readme_text, repo_context)
242
+ value_prop = _infer_value_prop(problem, key_features)
243
+
244
+ summary = _clean(repo_context.get("description"))
245
+ if not summary:
246
+ summary = _first_sentence(readme_text) or f"Open-source project in {repo_context.get('language') or 'unknown language'}."
247
+
248
+ readiness_signals = _check_readiness_signals(readme_text, repo_context)
249
+ signal_count = sum(1 for v in readiness_signals.values() if v)
250
+
251
+ assumptions: list[str] = []
252
+ if repo_context.get("readme_error"):
253
+ assumptions.append(f"README issue: {repo_context['readme_error']}")
254
+ if audience == "unknown":
255
+ assumptions.append("Audience inferred from repo metadata only.")
256
+ if not key_features:
257
+ assumptions.append("No strong feature bullets extracted from README.")
258
+ if not readiness_signals["has_install"]:
259
+ assumptions.append("No clear installation instructions found in README.")
260
+
261
+ confidence = "high"
262
+ if repo_context.get("confidence") == "low" or audience_confidence == "low" or problem_confidence == "low":
263
+ confidence = "low"
264
+ elif assumptions:
265
+ confidence = "medium"
266
+
267
+ proof_points = []
268
+ if repo_context.get("stars") is not None:
269
+ proof_points.append(f"{repo_context['stars']} GitHub stars")
270
+ if repo_context.get("forks") is not None:
271
+ proof_points.append(f"{repo_context['forks']} forks")
272
+ if repo_context.get("topics"):
273
+ proof_points.append("topics: " + ", ".join(repo_context.get("topics", [])[:5]))
274
+ if repo_context.get("language"):
275
+ proof_points.append(f"primary language: {repo_context['language']}")
276
+ if repo_context.get("license"):
277
+ proof_points.append(f"license: {repo_context['license']}")
278
+
279
+ project_type = _infer_project_type(repo_context, readme_text)
280
+
281
+ # Launch Readiness assessment
282
+ stars = repo_context.get("stars", 0)
283
+ readme_len = len(readme_text)
284
+
285
+ # Heuristic scoring
286
+ if stars >= 50 and readme_len > 1500 and signal_count >= 5:
287
+ score = "high"
288
+ elif stars < 10 or readme_len < 500 or signal_count <= 2:
289
+ score = "low"
290
+ else:
291
+ score = "medium"
292
+
293
+ readiness = {
294
+ "score": score,
295
+ "signals": readiness_signals,
296
+ "fix_plan": _generate_fix_plan(readiness_signals)
297
+ }
298
+
299
+ channel_fitness = _evaluate_channel_fitness(project_type, score)
300
+
301
+ return {
302
+ "repo_name": repo_name,
303
+ "one_line_summary": summary,
304
+ "project_type": project_type,
305
+ "launch_readiness": readiness,
306
+ "audience": audience,
307
+ "problem_solved": problem,
308
+ "value_proposition": value_prop,
309
+ "key_proof_points": proof_points,
310
+ "key_features": key_features,
311
+ "links": _build_links(repo_context),
312
+ "confidence": confidence,
313
+ "assumptions": assumptions,
314
+ "channel_fitness": channel_fitness,
315
+ "source_confidence": {
316
+ "problem": problem_confidence,
317
+ "audience": audience_confidence,
318
+ "repo_context": repo_context.get("confidence", "low"),
319
+ },
320
+ }
321
+
322
+
323
+ def main() -> None:
324
+ if len(sys.argv) != 2:
325
+ raise SystemExit("Usage: python build_product_brief.py <repo-context-json>")
326
+
327
+ with open(sys.argv[1], "r", encoding="utf-8") as handle:
328
+ repo_context = json.load(handle)
329
+
330
+ brief = build_product_brief(repo_context)
331
+ print(json.dumps(brief, indent=2, ensure_ascii=True))
332
+
333
+
334
+ if __name__ == "__main__":
335
+ main()