@tomako/tools-runtime 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.
Files changed (531) hide show
  1. package/README.md +12 -0
  2. package/package.json +629 -0
  3. package/src/assets/icons/brands/social/bluesky-icon.tsx +9 -0
  4. package/src/assets/icons/brands/social/facebook-icon.tsx +9 -0
  5. package/src/assets/icons/brands/social/index.ts +8 -0
  6. package/src/assets/icons/brands/social/instagram-icon.tsx +9 -0
  7. package/src/assets/icons/brands/social/linkedin-icon.tsx +9 -0
  8. package/src/assets/icons/brands/social/threads-icon.tsx +9 -0
  9. package/src/assets/icons/brands/social/tiktok-icon.tsx +10 -0
  10. package/src/assets/icons/brands/social/x-icon.tsx +9 -0
  11. package/src/assets/icons/brands/social/youtube-icon.tsx +9 -0
  12. package/src/assets/icons/social-profile/facebook-icon.tsx +12 -0
  13. package/src/assets/icons/social-profile/index.ts +29 -0
  14. package/src/assets/icons/social-profile/instagram-icon.tsx +18 -0
  15. package/src/assets/icons/social-profile/linkedin-icon.tsx +12 -0
  16. package/src/assets/icons/social-profile/tiktok-icon.tsx +12 -0
  17. package/src/assets/icons/social-profile/x-icon.tsx +13 -0
  18. package/src/assets/icons/social-profile/youtube-icon.tsx +12 -0
  19. package/src/components/tools/engine/form-builder.tsx +219 -0
  20. package/src/components/tools/engine/package-widget-loader.tsx +38 -0
  21. package/src/components/tools/engine/package-widget-runtime.tsx +409 -0
  22. package/src/components/tools/engine/package-widget.tsx +77 -0
  23. package/src/components/tools/engine/result-view.tsx +596 -0
  24. package/src/components/tools/engine/use-legacy-llm-task.ts +64 -0
  25. package/src/components/tools/engine/use-tool-task.ts +195 -0
  26. package/src/components/tools/market-forecast-report.tsx +1150 -0
  27. package/src/components/tools/registry.ts +13 -0
  28. package/src/components/tools/workspace/index.ts +39 -0
  29. package/src/components/tools/workspace/tool-action-bar.tsx +99 -0
  30. package/src/components/tools/workspace/tool-empty-panel.tsx +32 -0
  31. package/src/components/tools/workspace/tool-error-panel.tsx +40 -0
  32. package/src/components/tools/workspace/tool-field.tsx +124 -0
  33. package/src/components/tools/workspace/tool-file-upload.tsx +112 -0
  34. package/src/components/tools/workspace/tool-form-section.tsx +21 -0
  35. package/src/components/tools/workspace/tool-form-shell.tsx +36 -0
  36. package/src/components/tools/workspace/tool-loading-panel.tsx +269 -0
  37. package/src/components/tools/workspace/tool-option-row.tsx +97 -0
  38. package/src/components/tools/workspace/tool-preview-panel.tsx +31 -0
  39. package/src/components/tools/workspace/tool-result-actions.tsx +67 -0
  40. package/src/components/tools/workspace/tool-result-file-actions.ts +31 -0
  41. package/src/components/tools/workspace/tool-result-header.tsx +39 -0
  42. package/src/components/tools/workspace/tool-status.tsx +13 -0
  43. package/src/components/tools/workspace/tool-workspace-layout.tsx +226 -0
  44. package/src/constants/LKS.ts +29 -0
  45. package/src/constants/related-tools.ts +18 -0
  46. package/src/constants/social-profile-kit.ts +284 -0
  47. package/src/constants/tool-assets.ts +7 -0
  48. package/src/env.d.ts +3 -0
  49. package/src/features/tools/_templates/standard-tool.container.tsx +90 -0
  50. package/src/features/tools/app-icon-resizer/app-icon-resizer.container.tsx +11 -0
  51. package/src/features/tools/app-icon-resizer/app-icon-resizer.spec.ts +15 -0
  52. package/src/features/tools/app-icon-resizer/widget/app-icon-resizer.tsx +444 -0
  53. package/src/features/tools/app-icon-resizer/widget/constants.ts +19 -0
  54. package/src/features/tools/app-icon-resizer/widget/helpers.ts +314 -0
  55. package/src/features/tools/app-icon-resizer/widget/index.ts +7 -0
  56. package/src/features/tools/app-icon-resizer/widget/platform-icons.tsx +59 -0
  57. package/src/features/tools/app-icon-resizer/widget/preview-dialogs.tsx +139 -0
  58. package/src/features/tools/app-icon-resizer/widget/result-shell.tsx +165 -0
  59. package/src/features/tools/app-icon-resizer/widget/types.ts +36 -0
  60. package/src/features/tools/app-store-screenshot-generator/app-store-screenshot-generator.container.tsx +108 -0
  61. package/src/features/tools/app-store-screenshot-generator/app-store-screenshot-generator.spec.ts +14 -0
  62. package/src/features/tools/app-store-screenshot-generator/config.ts +3 -0
  63. package/src/features/tools/app-store-screenshot-generator/widget/app-store-screenshot-generator.tsx +1250 -0
  64. package/src/features/tools/app-store-screenshot-generator/widget/asset-slots.ts +76 -0
  65. package/src/features/tools/app-store-screenshot-generator/widget/constants.ts +43 -0
  66. package/src/features/tools/app-store-screenshot-generator/widget/form-controls.tsx +214 -0
  67. package/src/features/tools/app-store-screenshot-generator/widget/generation.ts +528 -0
  68. package/src/features/tools/app-store-screenshot-generator/widget/index.ts +7 -0
  69. package/src/features/tools/app-store-screenshot-generator/widget/input-media.ts +175 -0
  70. package/src/features/tools/app-store-screenshot-generator/widget/types.ts +60 -0
  71. package/src/features/tools/app-store-screenshot-generator/widget/zip.ts +119 -0
  72. package/src/features/tools/build-in-public-story-generator/build-in-public-story-generator.container.tsx +135 -0
  73. package/src/features/tools/build-in-public-story-generator/build-in-public-story-generator.spec.ts +15 -0
  74. package/src/features/tools/build-in-public-story-generator/widget/build-in-public-story-generator.tsx +314 -0
  75. package/src/features/tools/build-in-public-story-generator/widget/constants.ts +3 -0
  76. package/src/features/tools/build-in-public-story-generator/widget/helpers.ts +25 -0
  77. package/src/features/tools/build-in-public-story-generator/widget/index.ts +7 -0
  78. package/src/features/tools/build-in-public-story-generator/widget/preview-panels.tsx +58 -0
  79. package/src/features/tools/build-in-public-story-generator/widget/result-shell.tsx +164 -0
  80. package/src/features/tools/build-in-public-story-generator/widget/types.ts +6 -0
  81. package/src/features/tools/cold-start-channel-selector/cold-start-channel-selector.container.tsx +30 -0
  82. package/src/features/tools/cold-start-channel-selector/cold-start-channel-selector.spec.ts +15 -0
  83. package/src/features/tools/cold-start-channel-selector/widget/cold-start-channel-selector.tsx +417 -0
  84. package/src/features/tools/cold-start-channel-selector/widget/constants.ts +46 -0
  85. package/src/features/tools/cold-start-channel-selector/widget/form-controls.tsx +41 -0
  86. package/src/features/tools/cold-start-channel-selector/widget/helpers.ts +75 -0
  87. package/src/features/tools/cold-start-channel-selector/widget/index.ts +7 -0
  88. package/src/features/tools/cold-start-channel-selector/widget/preview-panels.tsx +250 -0
  89. package/src/features/tools/cold-start-channel-selector/widget/result-shell.tsx +199 -0
  90. package/src/features/tools/cold-start-channel-selector/widget/types.ts +21 -0
  91. package/src/features/tools/commercial-bp-generator/commercial-bp-generator.container.tsx +102 -0
  92. package/src/features/tools/commercial-bp-generator/commercial-bp-generator.spec.ts +14 -0
  93. package/src/features/tools/commercial-bp-generator/widget/commercial-bp-generator.tsx +676 -0
  94. package/src/features/tools/commercial-bp-generator/widget/constants.ts +32 -0
  95. package/src/features/tools/commercial-bp-generator/widget/helpers.ts +147 -0
  96. package/src/features/tools/commercial-bp-generator/widget/index.ts +7 -0
  97. package/src/features/tools/commercial-bp-generator/widget/result-shell.tsx +271 -0
  98. package/src/features/tools/commercial-bp-generator/widget/style-option.tsx +54 -0
  99. package/src/features/tools/commercial-bp-generator/widget/types.ts +33 -0
  100. package/src/features/tools/community-feedback-inbox/community-feedback-inbox.container.tsx +126 -0
  101. package/src/features/tools/community-feedback-inbox/community-feedback-inbox.spec.ts +15 -0
  102. package/src/features/tools/community-feedback-inbox/widget/community-feedback-inbox.tsx +271 -0
  103. package/src/features/tools/community-feedback-inbox/widget/constants.ts +3 -0
  104. package/src/features/tools/community-feedback-inbox/widget/helpers.ts +28 -0
  105. package/src/features/tools/community-feedback-inbox/widget/index.ts +7 -0
  106. package/src/features/tools/community-feedback-inbox/widget/preview-panels.tsx +92 -0
  107. package/src/features/tools/community-feedback-inbox/widget/result-shell.tsx +266 -0
  108. package/src/features/tools/community-feedback-inbox/widget/types.ts +6 -0
  109. package/src/features/tools/community-pain-point-extractor/community-pain-point-extractor.container.tsx +124 -0
  110. package/src/features/tools/community-pain-point-extractor/community-pain-point-extractor.spec.ts +15 -0
  111. package/src/features/tools/community-pain-point-extractor/widget/community-pain-point-extractor.tsx +404 -0
  112. package/src/features/tools/community-pain-point-extractor/widget/constants.ts +41 -0
  113. package/src/features/tools/community-pain-point-extractor/widget/helpers.ts +76 -0
  114. package/src/features/tools/community-pain-point-extractor/widget/index.ts +8 -0
  115. package/src/features/tools/community-pain-point-extractor/widget/progress-panels.tsx +251 -0
  116. package/src/features/tools/community-pain-point-extractor/widget/result-shell.tsx +478 -0
  117. package/src/features/tools/community-pain-point-extractor/widget/types.ts +33 -0
  118. package/src/features/tools/community-post-risk-checker/community-post-risk-checker.package.ts +792 -0
  119. package/src/features/tools/competitor-analysis/competitor-analysis.container.tsx +136 -0
  120. package/src/features/tools/competitor-analysis/competitor-analysis.spec.ts +15 -0
  121. package/src/features/tools/competitor-analysis/widget/competitor-analysis.tsx +241 -0
  122. package/src/features/tools/competitor-analysis/widget/constants.ts +9 -0
  123. package/src/features/tools/competitor-analysis/widget/index.ts +7 -0
  124. package/src/features/tools/competitor-analysis/widget/preview-panels.tsx +60 -0
  125. package/src/features/tools/competitor-analysis/widget/result-shell.tsx +418 -0
  126. package/src/features/tools/competitor-analysis/widget/types.ts +13 -0
  127. package/src/features/tools/disclaimer-generator/disclaimer-generator.package.ts +339 -0
  128. package/src/features/tools/email-template-generator/email-template-generator.container.tsx +224 -0
  129. package/src/features/tools/email-template-generator/email-template-generator.spec.ts +14 -0
  130. package/src/features/tools/email-template-generator/widget/email-template-generator.tsx +358 -0
  131. package/src/features/tools/email-template-generator/widget/helpers.ts +39 -0
  132. package/src/features/tools/email-template-generator/widget/index.ts +7 -0
  133. package/src/features/tools/email-template-generator/widget/preview-panel.tsx +311 -0
  134. package/src/features/tools/email-template-generator/widget/result-text-block.tsx +7 -0
  135. package/src/features/tools/email-template-generator/widget/types.ts +24 -0
  136. package/src/features/tools/gtm-readiness-checklist/gtm-readiness-checklist.container.tsx +75 -0
  137. package/src/features/tools/gtm-readiness-checklist/gtm-readiness-checklist.spec.ts +15 -0
  138. package/src/features/tools/gtm-readiness-checklist/widget/gtm-readiness-checklist.tsx +122 -0
  139. package/src/features/tools/gtm-readiness-checklist/widget/index.ts +7 -0
  140. package/src/features/tools/investor-matching-planner/investor-matching-planner.package.ts +869 -0
  141. package/src/features/tools/koko-codex-assistant/koko-codex-assistant.container.tsx +151 -0
  142. package/src/features/tools/koko-codex-assistant/koko-codex-assistant.spec.ts +14 -0
  143. package/src/features/tools/koko-codex-assistant/widget/index.ts +7 -0
  144. package/src/features/tools/koko-codex-assistant/widget/koko-codex-assistant-download.tsx +192 -0
  145. package/src/features/tools/logo-generator/logo-generator.container.tsx +112 -0
  146. package/src/features/tools/logo-generator/logo-generator.spec.ts +15 -0
  147. package/src/features/tools/logo-generator/widget/index.ts +7 -0
  148. package/src/features/tools/logo-generator/widget/logo-generator.tsx +1244 -0
  149. package/src/features/tools/market-forecast-report/market-forecast-report.container.tsx +73 -0
  150. package/src/features/tools/market-forecast-report/market-forecast-report.spec.ts +15 -0
  151. package/src/features/tools/million-user-growth-test/million-user-growth-test.container.tsx +123 -0
  152. package/src/features/tools/million-user-growth-test/million-user-growth-test.spec.ts +14 -0
  153. package/src/features/tools/million-user-growth-test/widget/index.ts +7 -0
  154. package/src/features/tools/million-user-growth-test/widget/million-user-growth-test.tsx +433 -0
  155. package/src/features/tools/module-loader.ts +107 -0
  156. package/src/features/tools/og-image-generator/og-image-generator.container.tsx +86 -0
  157. package/src/features/tools/og-image-generator/og-image-generator.spec.ts +14 -0
  158. package/src/features/tools/og-image-generator/widget/constants.ts +38 -0
  159. package/src/features/tools/og-image-generator/widget/form-fields.tsx +240 -0
  160. package/src/features/tools/og-image-generator/widget/index.ts +7 -0
  161. package/src/features/tools/og-image-generator/widget/og-image-generator.tsx +742 -0
  162. package/src/features/tools/og-image-generator/widget/preview-panel.tsx +305 -0
  163. package/src/features/tools/og-image-generator/widget/types.ts +1 -0
  164. package/src/features/tools/package/direct-models.ts +20 -0
  165. package/src/features/tools/package/form.ts +38 -0
  166. package/src/features/tools/package/l10n.ts +22 -0
  167. package/src/features/tools/package/package-container.tsx +285 -0
  168. package/src/features/tools/package/package-loaders.ts +22 -0
  169. package/src/features/tools/package/package-registry.ts +36 -0
  170. package/src/features/tools/package/result-markdown.ts +142 -0
  171. package/src/features/tools/package/schema.ts +379 -0
  172. package/src/features/tools/package/to-tool-module.ts +20 -0
  173. package/src/features/tools/package/to-tool-spec.ts +52 -0
  174. package/src/features/tools/package/types.ts +381 -0
  175. package/src/features/tools/pitch-deck-generator/pitch-deck-generator.container.tsx +175 -0
  176. package/src/features/tools/pitch-deck-generator/pitch-deck-generator.spec.ts +14 -0
  177. package/src/features/tools/pitch-deck-generator/pitch-deck-library-data.ts +476 -0
  178. package/src/features/tools/pitch-deck-generator/pitch-deck-library.tsx +159 -0
  179. package/src/features/tools/pitch-deck-generator/widget/ceremony-line.tsx +27 -0
  180. package/src/features/tools/pitch-deck-generator/widget/constants.ts +27 -0
  181. package/src/features/tools/pitch-deck-generator/widget/index.ts +7 -0
  182. package/src/features/tools/pitch-deck-generator/widget/pitch-deck-generator.tsx +763 -0
  183. package/src/features/tools/pitch-deck-generator/widget/result-helpers.ts +102 -0
  184. package/src/features/tools/pitch-deck-generator/widget/result-shell.tsx +263 -0
  185. package/src/features/tools/pitch-deck-generator/widget/slide-frame.tsx +106 -0
  186. package/src/features/tools/pitch-deck-generator/widget/slide-lightbox.tsx +149 -0
  187. package/src/features/tools/pitch-deck-generator/widget/style-option.tsx +54 -0
  188. package/src/features/tools/pitch-deck-generator/widget/types.ts +44 -0
  189. package/src/features/tools/product-agreement-generator/product-agreement-generator.container.tsx +137 -0
  190. package/src/features/tools/product-agreement-generator/product-agreement-generator.spec.ts +14 -0
  191. package/src/features/tools/product-agreement-generator/widget/constants.ts +27 -0
  192. package/src/features/tools/product-agreement-generator/widget/form-controls.tsx +218 -0
  193. package/src/features/tools/product-agreement-generator/widget/helpers.ts +68 -0
  194. package/src/features/tools/product-agreement-generator/widget/index.ts +7 -0
  195. package/src/features/tools/product-agreement-generator/widget/product-agreement-generator.tsx +671 -0
  196. package/src/features/tools/product-agreement-generator/widget/result-shell.tsx +473 -0
  197. package/src/features/tools/product-agreement-generator/widget/types.ts +89 -0
  198. package/src/features/tools/product-hunt-launch-planner/product-hunt-launch-planner.container.tsx +122 -0
  199. package/src/features/tools/product-hunt-launch-planner/product-hunt-launch-planner.spec.ts +15 -0
  200. package/src/features/tools/product-hunt-launch-planner/widget/constants.ts +5 -0
  201. package/src/features/tools/product-hunt-launch-planner/widget/helpers.ts +44 -0
  202. package/src/features/tools/product-hunt-launch-planner/widget/index.ts +7 -0
  203. package/src/features/tools/product-hunt-launch-planner/widget/preview-panel.tsx +143 -0
  204. package/src/features/tools/product-hunt-launch-planner/widget/product-hunt-launch-planner.tsx +504 -0
  205. package/src/features/tools/product-hunt-launch-planner/widget/result-shell.tsx +176 -0
  206. package/src/features/tools/product-hunt-launch-planner/widget/types.ts +20 -0
  207. package/src/features/tools/product-name-generator/product-name-generator.container.tsx +14 -0
  208. package/src/features/tools/product-name-generator/product-name-generator.spec.ts +15 -0
  209. package/src/features/tools/product-name-generator/widget/candidate-card.tsx +273 -0
  210. package/src/features/tools/product-name-generator/widget/constants.ts +21 -0
  211. package/src/features/tools/product-name-generator/widget/form-controls.tsx +136 -0
  212. package/src/features/tools/product-name-generator/widget/helpers.ts +41 -0
  213. package/src/features/tools/product-name-generator/widget/index.ts +7 -0
  214. package/src/features/tools/product-name-generator/widget/product-name-generator.tsx +776 -0
  215. package/src/features/tools/product-name-generator/widget/types.ts +36 -0
  216. package/src/features/tools/product-positioning-analyzer/product-positioning-analyzer.package.ts +801 -0
  217. package/src/features/tools/product-poster-generator/product-poster-generator.container.tsx +14 -0
  218. package/src/features/tools/product-poster-generator/product-poster-generator.spec.ts +15 -0
  219. package/src/features/tools/product-poster-generator/widget/constants.ts +125 -0
  220. package/src/features/tools/product-poster-generator/widget/form-controls.tsx +204 -0
  221. package/src/features/tools/product-poster-generator/widget/helpers.ts +155 -0
  222. package/src/features/tools/product-poster-generator/widget/index.ts +7 -0
  223. package/src/features/tools/product-poster-generator/widget/product-poster-generator.tsx +663 -0
  224. package/src/features/tools/product-poster-generator/widget/result-shell.tsx +12 -0
  225. package/src/features/tools/product-poster-generator/widget/types.ts +83 -0
  226. package/src/features/tools/product-ui-generator/product-ui-generator.container.tsx +116 -0
  227. package/src/features/tools/product-ui-generator/product-ui-generator.spec.ts +14 -0
  228. package/src/features/tools/product-ui-generator/widget/constants.ts +18 -0
  229. package/src/features/tools/product-ui-generator/widget/helpers.ts +11 -0
  230. package/src/features/tools/product-ui-generator/widget/index.ts +7 -0
  231. package/src/features/tools/product-ui-generator/widget/preview-panels.tsx +73 -0
  232. package/src/features/tools/product-ui-generator/widget/product-ui-generator.tsx +304 -0
  233. package/src/features/tools/product-ui-generator/widget/result-shell.tsx +118 -0
  234. package/src/features/tools/product-ui-generator/widget/types.ts +9 -0
  235. package/src/features/tools/reddit-pain-point-finder/reddit-pain-point-finder.container.tsx +113 -0
  236. package/src/features/tools/reddit-pain-point-finder/reddit-pain-point-finder.spec.ts +15 -0
  237. package/src/features/tools/reddit-pain-point-finder/widget/constants.ts +5 -0
  238. package/src/features/tools/reddit-pain-point-finder/widget/index.ts +7 -0
  239. package/src/features/tools/reddit-pain-point-finder/widget/preview-panels.tsx +73 -0
  240. package/src/features/tools/reddit-pain-point-finder/widget/reddit-pain-point-finder.tsx +281 -0
  241. package/src/features/tools/reddit-pain-point-finder/widget/result-shell.tsx +203 -0
  242. package/src/features/tools/reddit-pain-point-finder/widget/types.ts +15 -0
  243. package/src/features/tools/registry.ts +288 -0
  244. package/src/features/tools/runtime-registry.ts +186 -0
  245. package/src/features/tools/shared/create-guide-stack-container.tsx +176 -0
  246. package/src/features/tools/shared/create-standard-landing-container.tsx +163 -0
  247. package/src/features/tools/shared/tool-content-sections.tsx +659 -0
  248. package/src/features/tools/shared/tool-cta.tsx +66 -0
  249. package/src/features/tools/shared/tool-draft-view.tsx +53 -0
  250. package/src/features/tools/shared/tool-guide-section.tsx +191 -0
  251. package/src/features/tools/shared/tool-handoff.tsx +38 -0
  252. package/src/features/tools/shared/tool-mcp-badge.tsx +23 -0
  253. package/src/features/tools/shared/tool-message-translator.ts +37 -0
  254. package/src/features/tools/shared/tool-page-shell.tsx +348 -0
  255. package/src/features/tools/shared/tool-standard-generation-steps.tsx +52 -0
  256. package/src/features/tools/shared/tool-standard-landing-sections.tsx +447 -0
  257. package/src/features/tools/shared/tool-ui-registry.ts +237 -0
  258. package/src/features/tools/shared/tool-widget-slot.tsx +29 -0
  259. package/src/features/tools/slogan-generator/slogan-generator.package.ts +641 -0
  260. package/src/features/tools/social-listening-research-report/social-listening-research-report.container.tsx +124 -0
  261. package/src/features/tools/social-listening-research-report/social-listening-research-report.spec.ts +15 -0
  262. package/src/features/tools/social-listening-research-report/widget/constants.ts +4 -0
  263. package/src/features/tools/social-listening-research-report/widget/index.ts +7 -0
  264. package/src/features/tools/social-listening-research-report/widget/preview-panels.tsx +73 -0
  265. package/src/features/tools/social-listening-research-report/widget/result-shell.tsx +216 -0
  266. package/src/features/tools/social-listening-research-report/widget/social-listening-research-report.tsx +303 -0
  267. package/src/features/tools/social-listening-research-report/widget/types.ts +15 -0
  268. package/src/features/tools/social-platform-asset-adapter/social-platform-asset-adapter.container.tsx +123 -0
  269. package/src/features/tools/social-platform-asset-adapter/social-platform-asset-adapter.spec.ts +15 -0
  270. package/src/features/tools/social-platform-asset-adapter/widget/constants.ts +43 -0
  271. package/src/features/tools/social-platform-asset-adapter/widget/helpers.ts +249 -0
  272. package/src/features/tools/social-platform-asset-adapter/widget/index.ts +7 -0
  273. package/src/features/tools/social-platform-asset-adapter/widget/result-shell.tsx +350 -0
  274. package/src/features/tools/social-platform-asset-adapter/widget/section-heading.tsx +18 -0
  275. package/src/features/tools/social-platform-asset-adapter/widget/social-platform-asset-adapter.tsx +641 -0
  276. package/src/features/tools/social-platform-asset-adapter/widget/types.ts +40 -0
  277. package/src/features/tools/social-profile-kit/social-profile-kit.container.tsx +177 -0
  278. package/src/features/tools/social-profile-kit/social-profile-kit.spec.ts +15 -0
  279. package/src/features/tools/social-profile-kit/widget/context-editor.tsx +171 -0
  280. package/src/features/tools/social-profile-kit/widget/generation-runtime.ts +281 -0
  281. package/src/features/tools/social-profile-kit/widget/index.ts +7 -0
  282. package/src/features/tools/social-profile-kit/widget/input-panel.tsx +294 -0
  283. package/src/features/tools/social-profile-kit/widget/platform-dock.tsx +115 -0
  284. package/src/features/tools/social-profile-kit/widget/profile-canvas.tsx +318 -0
  285. package/src/features/tools/social-profile-kit/widget/profile-preview/bluesky-preview.tsx +188 -0
  286. package/src/features/tools/social-profile-kit/widget/profile-preview/facebook-preview.tsx +168 -0
  287. package/src/features/tools/social-profile-kit/widget/profile-preview/index.ts +1 -0
  288. package/src/features/tools/social-profile-kit/widget/profile-preview/instagram-preview.tsx +278 -0
  289. package/src/features/tools/social-profile-kit/widget/profile-preview/linkedin-preview.tsx +358 -0
  290. package/src/features/tools/social-profile-kit/widget/profile-preview/profile-preview.tsx +348 -0
  291. package/src/features/tools/social-profile-kit/widget/profile-preview/shared.tsx +391 -0
  292. package/src/features/tools/social-profile-kit/widget/profile-preview/threads-preview.tsx +148 -0
  293. package/src/features/tools/social-profile-kit/widget/profile-preview/tiktok-preview.tsx +319 -0
  294. package/src/features/tools/social-profile-kit/widget/profile-preview/x-preview.tsx +341 -0
  295. package/src/features/tools/social-profile-kit/widget/profile-preview/youtube-preview.tsx +297 -0
  296. package/src/features/tools/social-profile-kit/widget/social-profile-kit-generator.tsx +1059 -0
  297. package/src/features/tools/social-profile-kit/widget/types.ts +104 -0
  298. package/src/features/tools/spec-registry.ts +158 -0
  299. package/src/features/tools/startup-idea-research/startup-idea-research.container.tsx +111 -0
  300. package/src/features/tools/startup-idea-research/startup-idea-research.spec.ts +15 -0
  301. package/src/features/tools/startup-idea-research/widget/constants.ts +11 -0
  302. package/src/features/tools/startup-idea-research/widget/index.ts +7 -0
  303. package/src/features/tools/startup-idea-research/widget/preview-panels.tsx +67 -0
  304. package/src/features/tools/startup-idea-research/widget/result-shell.tsx +323 -0
  305. package/src/features/tools/startup-idea-research/widget/startup-idea-research.tsx +272 -0
  306. package/src/features/tools/startup-idea-research/widget/types.ts +21 -0
  307. package/src/features/tools/tarot-reading/tarot-reading.container.tsx +412 -0
  308. package/src/features/tools/tarot-reading/tarot-reading.spec.ts +15 -0
  309. package/src/features/tools/tarot-reading/widget/cards.tsx +152 -0
  310. package/src/features/tools/tarot-reading/widget/constants.ts +69 -0
  311. package/src/features/tools/tarot-reading/widget/deck-view.tsx +86 -0
  312. package/src/features/tools/tarot-reading/widget/deck.ts +86 -0
  313. package/src/features/tools/tarot-reading/widget/index.ts +7 -0
  314. package/src/features/tools/tarot-reading/widget/landing-view.tsx +209 -0
  315. package/src/features/tools/tarot-reading/widget/reading-copy.tsx +138 -0
  316. package/src/features/tools/tarot-reading/widget/reading-view.tsx +189 -0
  317. package/src/features/tools/tarot-reading/widget/reveal-view.tsx +50 -0
  318. package/src/features/tools/tarot-reading/widget/ritual-view.tsx +276 -0
  319. package/src/features/tools/tarot-reading/widget/tarot-reading.tsx +262 -0
  320. package/src/features/tools/tarot-reading/widget/types.ts +22 -0
  321. package/src/features/tools/twitter-gif-downloader/twitter-gif-downloader.container.tsx +114 -0
  322. package/src/features/tools/twitter-gif-downloader/twitter-gif-downloader.spec.ts +14 -0
  323. package/src/features/tools/twitter-gif-downloader/widget/index.ts +7 -0
  324. package/src/features/tools/twitter-gif-downloader/widget/twitter-gif-downloader.tsx +351 -0
  325. package/src/features/tools/types.ts +68 -0
  326. package/src/features/tools/user-interview-guide-generator/user-interview-guide-generator.package.ts +774 -0
  327. package/src/features/tools/user-interview-insight-report-generator/user-interview-insight-report-generator.container.tsx +23 -0
  328. package/src/features/tools/user-interview-insight-report-generator/user-interview-insight-report-generator.spec.ts +14 -0
  329. package/src/features/tools/user-interview-insight-report-generator/widget/constants.ts +8 -0
  330. package/src/features/tools/user-interview-insight-report-generator/widget/helpers.ts +4 -0
  331. package/src/features/tools/user-interview-insight-report-generator/widget/index.ts +7 -0
  332. package/src/features/tools/user-interview-insight-report-generator/widget/preview-panels.tsx +55 -0
  333. package/src/features/tools/user-interview-insight-report-generator/widget/result-shell.tsx +122 -0
  334. package/src/features/tools/user-interview-insight-report-generator/widget/types.ts +16 -0
  335. package/src/features/tools/user-interview-insight-report-generator/widget/user-interview-insight-report-generator.tsx +293 -0
  336. package/src/features/tools/user-interview-record-generator/user-interview-record-generator.container.tsx +18 -0
  337. package/src/features/tools/user-interview-record-generator/user-interview-record-generator.spec.ts +14 -0
  338. package/src/features/tools/user-interview-record-generator/widget/constants.ts +9 -0
  339. package/src/features/tools/user-interview-record-generator/widget/helpers.ts +4 -0
  340. package/src/features/tools/user-interview-record-generator/widget/index.ts +7 -0
  341. package/src/features/tools/user-interview-record-generator/widget/preview-panels.tsx +48 -0
  342. package/src/features/tools/user-interview-record-generator/widget/result-shell.tsx +128 -0
  343. package/src/features/tools/user-interview-record-generator/widget/types.ts +16 -0
  344. package/src/features/tools/user-interview-record-generator/widget/user-interview-record-generator.tsx +305 -0
  345. package/src/features/tools/utm-builder/utm-builder.package.ts +846 -0
  346. package/src/features/tools/widget-registry.ts +73 -0
  347. package/src/generated/mcp-tools-registry.json +17 -0
  348. package/src/host/client-proxy.test.ts +33 -0
  349. package/src/host/client.ts +678 -0
  350. package/src/host/server.ts +119 -0
  351. package/src/i18n/client.ts +11 -0
  352. package/src/i18n/locale-fallback.ts +38 -0
  353. package/src/i18n/locale.ts +18 -0
  354. package/src/i18n/messages/en/page/tools.ts +124 -0
  355. package/src/i18n/messages/en/tools/app-icon-resizer.ts +244 -0
  356. package/src/i18n/messages/en/tools/app-store-screenshot-generator.ts +145 -0
  357. package/src/i18n/messages/en/tools/build-in-public-story-generator.ts +229 -0
  358. package/src/i18n/messages/en/tools/cold-start-channel-selector.ts +423 -0
  359. package/src/i18n/messages/en/tools/commercial-bp-generator.ts +304 -0
  360. package/src/i18n/messages/en/tools/community-feedback-inbox.ts +218 -0
  361. package/src/i18n/messages/en/tools/community-pain-point-extractor.ts +230 -0
  362. package/src/i18n/messages/en/tools/community-post-risk-checker.ts +209 -0
  363. package/src/i18n/messages/en/tools/competitor-analysis.ts +197 -0
  364. package/src/i18n/messages/en/tools/email-template-generator.ts +302 -0
  365. package/src/i18n/messages/en/tools/engine.ts +69 -0
  366. package/src/i18n/messages/en/tools/gtm-readiness-checklist.ts +166 -0
  367. package/src/i18n/messages/en/tools/index.ts +73 -0
  368. package/src/i18n/messages/en/tools/koko-codex-assistant.ts +171 -0
  369. package/src/i18n/messages/en/tools/logo-generator.ts +173 -0
  370. package/src/i18n/messages/en/tools/market-forecast-report.ts +228 -0
  371. package/src/i18n/messages/en/tools/million-user-growth-test.ts +386 -0
  372. package/src/i18n/messages/en/tools/og-image-generator.ts +172 -0
  373. package/src/i18n/messages/en/tools/pitch-deck-generator.ts +358 -0
  374. package/src/i18n/messages/en/tools/product-agreement-generator.ts +411 -0
  375. package/src/i18n/messages/en/tools/product-hunt-launch-planner.ts +272 -0
  376. package/src/i18n/messages/en/tools/product-name-generator.ts +383 -0
  377. package/src/i18n/messages/en/tools/product-poster-generator.ts +294 -0
  378. package/src/i18n/messages/en/tools/product-ui-generator.ts +234 -0
  379. package/src/i18n/messages/en/tools/reddit-pain-point-finder.ts +163 -0
  380. package/src/i18n/messages/en/tools/shared.ts +24 -0
  381. package/src/i18n/messages/en/tools/social-listening-research-report.ts +185 -0
  382. package/src/i18n/messages/en/tools/social-media-profile-generator.ts +22 -0
  383. package/src/i18n/messages/en/tools/social-platform-asset-adapter.ts +287 -0
  384. package/src/i18n/messages/en/tools/social-profile-kit.ts +150 -0
  385. package/src/i18n/messages/en/tools/startup-idea-research.ts +170 -0
  386. package/src/i18n/messages/en/tools/tarot-reading.ts +230 -0
  387. package/src/i18n/messages/en/tools/twitter-gif-downloader.ts +145 -0
  388. package/src/i18n/messages/en/tools/user-interview-insight-report-generator.ts +242 -0
  389. package/src/i18n/messages/en/tools/user-interview-record-generator.ts +244 -0
  390. package/src/i18n/messages/en/tools-ui.ts +704 -0
  391. package/src/i18n/messages/zh/page/tools.ts +124 -0
  392. package/src/i18n/messages/zh/tools/app-icon-resizer.ts +242 -0
  393. package/src/i18n/messages/zh/tools/app-store-screenshot-generator.ts +145 -0
  394. package/src/i18n/messages/zh/tools/build-in-public-story-generator.ts +230 -0
  395. package/src/i18n/messages/zh/tools/cold-start-channel-selector.ts +422 -0
  396. package/src/i18n/messages/zh/tools/commercial-bp-generator.ts +302 -0
  397. package/src/i18n/messages/zh/tools/community-feedback-inbox.ts +217 -0
  398. package/src/i18n/messages/zh/tools/community-pain-point-extractor.ts +230 -0
  399. package/src/i18n/messages/zh/tools/community-post-risk-checker.ts +200 -0
  400. package/src/i18n/messages/zh/tools/competitor-analysis.ts +192 -0
  401. package/src/i18n/messages/zh/tools/email-template-generator.ts +302 -0
  402. package/src/i18n/messages/zh/tools/engine.ts +68 -0
  403. package/src/i18n/messages/zh/tools/gtm-readiness-checklist.ts +166 -0
  404. package/src/i18n/messages/zh/tools/index.ts +73 -0
  405. package/src/i18n/messages/zh/tools/koko-codex-assistant.ts +170 -0
  406. package/src/i18n/messages/zh/tools/logo-generator.ts +172 -0
  407. package/src/i18n/messages/zh/tools/market-forecast-report.ts +228 -0
  408. package/src/i18n/messages/zh/tools/million-user-growth-test.ts +385 -0
  409. package/src/i18n/messages/zh/tools/og-image-generator.ts +173 -0
  410. package/src/i18n/messages/zh/tools/pitch-deck-generator.ts +358 -0
  411. package/src/i18n/messages/zh/tools/product-agreement-generator.ts +401 -0
  412. package/src/i18n/messages/zh/tools/product-hunt-launch-planner.ts +267 -0
  413. package/src/i18n/messages/zh/tools/product-name-generator.ts +383 -0
  414. package/src/i18n/messages/zh/tools/product-poster-generator.ts +282 -0
  415. package/src/i18n/messages/zh/tools/product-ui-generator.ts +234 -0
  416. package/src/i18n/messages/zh/tools/reddit-pain-point-finder.ts +162 -0
  417. package/src/i18n/messages/zh/tools/shared.ts +24 -0
  418. package/src/i18n/messages/zh/tools/social-listening-research-report.ts +181 -0
  419. package/src/i18n/messages/zh/tools/social-media-profile-generator.ts +22 -0
  420. package/src/i18n/messages/zh/tools/social-platform-asset-adapter.ts +284 -0
  421. package/src/i18n/messages/zh/tools/social-profile-kit.ts +150 -0
  422. package/src/i18n/messages/zh/tools/startup-idea-research.ts +165 -0
  423. package/src/i18n/messages/zh/tools/tarot-reading.ts +228 -0
  424. package/src/i18n/messages/zh/tools/twitter-gif-downloader.ts +138 -0
  425. package/src/i18n/messages/zh/tools/user-interview-insight-report-generator.ts +224 -0
  426. package/src/i18n/messages/zh/tools/user-interview-record-generator.ts +226 -0
  427. package/src/i18n/messages/zh/tools-ui.ts +684 -0
  428. package/src/i18n/messages/zh-tw/page/tools.ts +115 -0
  429. package/src/i18n/messages/zh-tw/tools/app-icon-resizer.ts +242 -0
  430. package/src/i18n/messages/zh-tw/tools/app-store-screenshot-generator.ts +145 -0
  431. package/src/i18n/messages/zh-tw/tools/build-in-public-story-generator.ts +230 -0
  432. package/src/i18n/messages/zh-tw/tools/cold-start-channel-selector.ts +440 -0
  433. package/src/i18n/messages/zh-tw/tools/commercial-bp-generator.ts +302 -0
  434. package/src/i18n/messages/zh-tw/tools/community-feedback-inbox.ts +217 -0
  435. package/src/i18n/messages/zh-tw/tools/community-pain-point-extractor.ts +230 -0
  436. package/src/i18n/messages/zh-tw/tools/community-post-risk-checker.ts +200 -0
  437. package/src/i18n/messages/zh-tw/tools/competitor-analysis.ts +192 -0
  438. package/src/i18n/messages/zh-tw/tools/email-template-generator.ts +302 -0
  439. package/src/i18n/messages/zh-tw/tools/engine.ts +68 -0
  440. package/src/i18n/messages/zh-tw/tools/gtm-readiness-checklist.ts +166 -0
  441. package/src/i18n/messages/zh-tw/tools/index.ts +73 -0
  442. package/src/i18n/messages/zh-tw/tools/koko-codex-assistant.ts +170 -0
  443. package/src/i18n/messages/zh-tw/tools/logo-generator.ts +71 -0
  444. package/src/i18n/messages/zh-tw/tools/market-forecast-report.ts +228 -0
  445. package/src/i18n/messages/zh-tw/tools/million-user-growth-test.ts +385 -0
  446. package/src/i18n/messages/zh-tw/tools/og-image-generator.ts +173 -0
  447. package/src/i18n/messages/zh-tw/tools/pitch-deck-generator.ts +358 -0
  448. package/src/i18n/messages/zh-tw/tools/product-agreement-generator.ts +401 -0
  449. package/src/i18n/messages/zh-tw/tools/product-hunt-launch-planner.ts +267 -0
  450. package/src/i18n/messages/zh-tw/tools/product-name-generator.ts +383 -0
  451. package/src/i18n/messages/zh-tw/tools/product-poster-generator.ts +282 -0
  452. package/src/i18n/messages/zh-tw/tools/product-ui-generator.ts +234 -0
  453. package/src/i18n/messages/zh-tw/tools/reddit-pain-point-finder.ts +162 -0
  454. package/src/i18n/messages/zh-tw/tools/shared.ts +24 -0
  455. package/src/i18n/messages/zh-tw/tools/social-listening-research-report.ts +181 -0
  456. package/src/i18n/messages/zh-tw/tools/social-media-profile-generator.ts +22 -0
  457. package/src/i18n/messages/zh-tw/tools/social-platform-asset-adapter.ts +284 -0
  458. package/src/i18n/messages/zh-tw/tools/social-profile-kit.ts +150 -0
  459. package/src/i18n/messages/zh-tw/tools/startup-idea-research.ts +165 -0
  460. package/src/i18n/messages/zh-tw/tools/tarot-reading.ts +228 -0
  461. package/src/i18n/messages/zh-tw/tools/twitter-gif-downloader.ts +138 -0
  462. package/src/i18n/messages/zh-tw/tools/user-interview-insight-report-generator.ts +224 -0
  463. package/src/i18n/messages/zh-tw/tools/user-interview-record-generator.ts +226 -0
  464. package/src/i18n/messages/zh-tw/tools-ui.ts +525 -0
  465. package/src/index.ts +15 -0
  466. package/src/lib/activity-task-handoff.ts +404 -0
  467. package/src/lib/api/errors.ts +39 -0
  468. package/src/lib/app-origin.ts +30 -0
  469. package/src/lib/fact-claim-contract.ts +68 -0
  470. package/src/lib/fake-delay.ts +3 -0
  471. package/src/lib/file/download.ts +10 -0
  472. package/src/lib/routing/locale-paths.ts +27 -0
  473. package/src/lib/seo-meta.ts +3 -0
  474. package/src/lib/tools/app-icon-resizer-schema.ts +191 -0
  475. package/src/lib/tools/app-store-screenshot-generator-schema.ts +185 -0
  476. package/src/lib/tools/build-in-public-story-generator-schema.ts +67 -0
  477. package/src/lib/tools/build-in-public-story-generator.format.ts +507 -0
  478. package/src/lib/tools/cold-start-channel-selector-schema.ts +147 -0
  479. package/src/lib/tools/commercial-bp-generator-schema.ts +222 -0
  480. package/src/lib/tools/community-feedback-inbox-schema.ts +125 -0
  481. package/src/lib/tools/community-feedback-inbox.format.ts +764 -0
  482. package/src/lib/tools/community-pain-point-extractor-schema.ts +166 -0
  483. package/src/lib/tools/community-post-risk-checker-schema.ts +99 -0
  484. package/src/lib/tools/community-post-risk-checker.format.ts +557 -0
  485. package/src/lib/tools/competitor-analysis-schema.ts +144 -0
  486. package/src/lib/tools/draft-preview-access.ts +3 -0
  487. package/src/lib/tools/draft-store.ts +69 -0
  488. package/src/lib/tools/email-template-generator-schema.ts +124 -0
  489. package/src/lib/tools/gtm-checklist-copy.ts +57 -0
  490. package/src/lib/tools/gtm-checklist-structure.ts +37 -0
  491. package/src/lib/tools/image-download.ts +68 -0
  492. package/src/lib/tools/input-quality.ts +97 -0
  493. package/src/lib/tools/logo-generator-schema.ts +224 -0
  494. package/src/lib/tools/market-forecast-report-schema.ts +299 -0
  495. package/src/lib/tools/mcp-registry.ts +16 -0
  496. package/src/lib/tools/mcp-types.ts +18 -0
  497. package/src/lib/tools/million-user-growth-test-schema.ts +49 -0
  498. package/src/lib/tools/million-user-growth-test.ts +181 -0
  499. package/src/lib/tools/og-image-generator-render.ts +448 -0
  500. package/src/lib/tools/og-image-generator-schema.ts +140 -0
  501. package/src/lib/tools/ops-catalog.ts +102 -0
  502. package/src/lib/tools/pitch-deck-generator-schema.ts +182 -0
  503. package/src/lib/tools/pitch-deck-package.ts +0 -0
  504. package/src/lib/tools/product-agreement-generator-schema.ts +168 -0
  505. package/src/lib/tools/product-hunt-launch-planner-schema.ts +136 -0
  506. package/src/lib/tools/product-hunt-launch-planner.format.ts +72 -0
  507. package/src/lib/tools/product-name-generator-schema.ts +563 -0
  508. package/src/lib/tools/product-poster-generator-schema.ts +215 -0
  509. package/src/lib/tools/product-ui-generator-schema.ts +157 -0
  510. package/src/lib/tools/reddit-pain-point-finder-schema.ts +88 -0
  511. package/src/lib/tools/resolve-tool-locale-copy.ts +75 -0
  512. package/src/lib/tools/slug.ts +60 -0
  513. package/src/lib/tools/social-listening-research-report-schema.ts +105 -0
  514. package/src/lib/tools/social-platform-asset-adapter-schema.ts +152 -0
  515. package/src/lib/tools/social-profile-kit.constants.ts +12 -0
  516. package/src/lib/tools/social-profile-kit.generation.ts +257 -0
  517. package/src/lib/tools/social-profile-kit.input.ts +290 -0
  518. package/src/lib/tools/social-profile-kit.manual.ts +481 -0
  519. package/src/lib/tools/social-profile-kit.package.ts +220 -0
  520. package/src/lib/tools/social-profile-kit.schema.ts +328 -0
  521. package/src/lib/tools/startup-idea-research-schema.ts +107 -0
  522. package/src/lib/tools/tarot-imagery.ts +89 -0
  523. package/src/lib/tools/tarot-reading.ts +1044 -0
  524. package/src/lib/tools/tool-message-keys.ts +18 -0
  525. package/src/lib/tools/types.ts +32 -0
  526. package/src/lib/tools/user-interview.format.ts +147 -0
  527. package/src/lib/tools/user-interview.generation.ts +670 -0
  528. package/src/lib/tools/user-interview.schema.ts +192 -0
  529. package/src/lib/zip.ts +106 -0
  530. package/src/services/tools/handlers/gtm-readiness-checklist.ts +71 -0
  531. package/src/services/tools/handlers/types.ts +7 -0
@@ -0,0 +1,764 @@
1
+ import {
2
+ communityFeedbackCategories,
3
+ type CommunityFeedbackBacklogItem,
4
+ type CommunityFeedbackBetaCandidate,
5
+ type CommunityFeedbackBrief,
6
+ type CommunityFeedbackCategory,
7
+ type CommunityFeedbackCategorySummary,
8
+ type CommunityFeedbackComment,
9
+ type CommunityFeedbackContentAngle,
10
+ type CommunityFeedbackFaqItem,
11
+ type CommunityFeedbackInboxReport,
12
+ type CommunityFeedbackObjection,
13
+ type CommunityFeedbackQuote,
14
+ type CommunityFeedbackSource,
15
+ } from "./community-feedback-inbox-schema";
16
+
17
+ type Language = CommunityFeedbackBrief["language"];
18
+
19
+ type CategoryCopy = {
20
+ title: string;
21
+ pattern: string;
22
+ action: string;
23
+ backlogTitle: string;
24
+ faqQuestion: string;
25
+ responseAngle: string;
26
+ };
27
+
28
+ type CopyPack = {
29
+ categories: Record<CommunityFeedbackCategory, CategoryCopy>;
30
+ sourceLabels: Record<CommunityFeedbackSource, string>;
31
+ authors: {
32
+ unknown: string;
33
+ };
34
+ summary: {
35
+ noDominant: string;
36
+ strongestFallback: string;
37
+ nextLaunch: string;
38
+ nextBeta: string;
39
+ nextRoadmap: string;
40
+ nextLanding: string;
41
+ };
42
+ beta: {
43
+ reason: string;
44
+ invite: string;
45
+ };
46
+ content: {
47
+ painTitle: string;
48
+ painHook: string;
49
+ objectionTitle: string;
50
+ objectionHook: string;
51
+ featureTitle: string;
52
+ featureHook: string;
53
+ format: string;
54
+ };
55
+ backlog: {
56
+ why: string;
57
+ nextAction: string;
58
+ };
59
+ missing: {
60
+ author: string;
61
+ source: string;
62
+ product: string;
63
+ volume: string;
64
+ };
65
+ review: string[];
66
+ markdown: {
67
+ title: string;
68
+ summary: string;
69
+ categories: string;
70
+ beta: string;
71
+ content: string;
72
+ backlog: string;
73
+ faq: string;
74
+ objections: string;
75
+ quotes: string;
76
+ missing: string;
77
+ review: string;
78
+ };
79
+ };
80
+
81
+ const copy: Record<Language, CopyPack> = {
82
+ zh: {
83
+ categories: {
84
+ pain: {
85
+ title: "痛点",
86
+ pattern: "用户在描述卡点、麻烦、失败、慢、难理解或当前替代方案的成本。",
87
+ action: "优先转成问题假设,确认这个痛点是否重复出现并影响激活或留存。",
88
+ backlogTitle: "降低用户当前工作流摩擦",
89
+ faqQuestion: "这个产品能解决我现在遇到的具体卡点吗?",
90
+ responseAngle: "用具体场景回答,而不是只复述功能列表。",
91
+ },
92
+ objection: {
93
+ title: "反对意见",
94
+ pattern: "用户表达不确定、担心隐私/可信度/复杂度,或说明为什么暂时不会试。",
95
+ action: "沉淀成 landing page objection、评论回复和下一轮发帖验证问题。",
96
+ backlogTitle: "补强信任与使用边界",
97
+ faqQuestion: "我为什么应该相信这个结果?",
98
+ responseAngle: "承认边界,说明人工复核、数据处理和适用范围。",
99
+ },
100
+ competitor: {
101
+ title: "竞品/替代方案",
102
+ pattern: "评论提到正在用的工具、手工流程、插件、表格、竞品或替代路线。",
103
+ action: "更新竞品地图,找出 Tomako 应该强调的差异点和不该竞争的场景。",
104
+ backlogTitle: "明确替代方案对比",
105
+ faqQuestion: "它和我现在用的工具有什么区别?",
106
+ responseAngle: "比较工作流差异,而不是宣称全面更好。",
107
+ },
108
+ feature: {
109
+ title: "功能请求",
110
+ pattern: "用户要求支持某个平台、集成、导出、自动化、权限或新的结果格式。",
111
+ action: "先合并重复请求,再按证据强度进入 backlog,而不是逐条照单全收。",
112
+ backlogTitle: "评估高频功能请求",
113
+ faqQuestion: "现在支持哪些输入和输出?",
114
+ responseAngle: "说清已支持、暂不支持和下一步考虑的范围。",
115
+ },
116
+ pricing: {
117
+ title: "定价反馈",
118
+ pattern: "用户提到免费、付费、贵、订阅、预算、价格锚点或购买意愿。",
119
+ action: "拆成支付意愿、价格阻力和包装方式,避免只按单条评论改价。",
120
+ backlogTitle: "验证定价包装与试用门槛",
121
+ faqQuestion: "免费版和付费版应该怎么分?",
122
+ responseAngle: "解释适合试用的免费范围,以及付费价值来自哪里。",
123
+ },
124
+ copy: {
125
+ title: "文案/定位反馈",
126
+ pattern: "用户看不懂一句话、标题、落地页承诺、目标用户或使用场景。",
127
+ action: "直接喂给 landing page 首屏、FAQ、评论回复和下一篇 build-in-public。",
128
+ backlogTitle: "重写首屏定位与示例",
129
+ faqQuestion: "我是不是这个产品的目标用户?",
130
+ responseAngle: "用人群、场景和结果来解释定位。",
131
+ },
132
+ },
133
+ sourceLabels: {
134
+ mixed: "混合来源",
135
+ reddit: "Reddit",
136
+ indieHackers: "Indie Hackers",
137
+ hackerNews: "Hacker News",
138
+ productHunt: "Product Hunt",
139
+ x: "X",
140
+ discord: "Discord",
141
+ },
142
+ authors: {
143
+ unknown: "未标记用户",
144
+ },
145
+ summary: {
146
+ noDominant: "暂未形成明显主主题",
147
+ strongestFallback: "还需要更多具体评论来判断最强信号",
148
+ nextLaunch: "先把高质量评论转成下一篇 build-in-public 帖子,再回到同一社区验证。",
149
+ nextBeta: "优先私信高信号用户,邀请他们试用 beta 并追问具体场景。",
150
+ nextRoadmap: "先合并重复反馈,再把有证据的请求转成 backlog 候选项。",
151
+ nextLanding: "把反对意见和看不懂的表达转成 landing page FAQ 与首屏文案改写。",
152
+ },
153
+ beta: {
154
+ reason: "这条评论包含明确使用场景、尝试意愿或可追问的问题,适合进入 beta 邀请名单。",
155
+ invite:
156
+ "谢谢你的反馈。我正在整理一小批 beta 用户,想更具体地了解你刚才提到的场景。方便试用一下并给我 10 分钟反馈吗?",
157
+ },
158
+ content: {
159
+ painTitle: "我从社区评论里发现的真实卡点",
160
+ painHook: "这不是一次曝光,而是一组可复用的用户语言。",
161
+ objectionTitle: "用户为什么还不愿意试用",
162
+ objectionHook: "反对意见比夸奖更适合改 landing page。",
163
+ featureTitle: "哪些功能请求值得先进 backlog",
164
+ featureHook: "重复请求不等于优先级,但它说明用户在用什么语言表达需求。",
165
+ format: "build-in-public post",
166
+ },
167
+ backlog: {
168
+ why: "这类反馈出现了重复证据,且能影响用户理解、试用或转化。",
169
+ nextAction: "先写成假设和验收标准,再用下一轮发帖或 beta 访谈验证。",
170
+ },
171
+ missing: {
172
+ author: "评论里缺少清晰用户名;beta 邀请名单只能给出弱候选。",
173
+ source: "来源混在一起;建议下次按 Reddit、IH、PH 等渠道分段粘贴。",
174
+ product: "产品上下文较短;分类可以运行,但内容素材会偏泛。",
175
+ volume: "评论数量偏少;适合做定性线索,不适合判断优先级。",
176
+ },
177
+ review: [
178
+ "对照原评论复核分类,尤其不要把一句吐槽直接当成功能需求。",
179
+ "只邀请明确表达场景、意愿或问题的人进入 beta,不要批量骚扰。",
180
+ "把重复反馈写成假设和证据数,再进入 backlog。",
181
+ "发布 build-in-public 素材前去掉用户名和可识别隐私信息。",
182
+ "把反对意见优先转成 landing page FAQ,而不是立刻加新功能。",
183
+ ],
184
+ markdown: {
185
+ title: "Community Feedback Inbox Report",
186
+ summary: "Summary",
187
+ categories: "Categories",
188
+ beta: "Beta invite shortlist",
189
+ content: "Build-in-public angles",
190
+ backlog: "Backlog candidates",
191
+ faq: "Landing page FAQ candidates",
192
+ objections: "Objection handling",
193
+ quotes: "Quote bank",
194
+ missing: "Missing info",
195
+ review: "Review checklist",
196
+ },
197
+ },
198
+ en: {
199
+ categories: {
200
+ pain: {
201
+ title: "Pain points",
202
+ pattern:
203
+ "People describe friction, failure, slowness, confusion, or the cost of the current workaround.",
204
+ action:
205
+ "Turn this into a problem hypothesis and verify whether it repeats across activation or retention moments.",
206
+ backlogTitle: "Reduce workflow friction",
207
+ faqQuestion: "Does this solve the specific friction I have now?",
208
+ responseAngle:
209
+ "Answer with a concrete use case instead of repeating the feature list.",
210
+ },
211
+ objection: {
212
+ title: "Objections",
213
+ pattern:
214
+ "People express doubt, privacy concerns, trust concerns, complexity, or why they would not try it yet.",
215
+ action:
216
+ "Turn these into landing page objections, comment replies, and the next validation question.",
217
+ backlogTitle: "Strengthen trust and boundaries",
218
+ faqQuestion: "Why should I trust the result?",
219
+ responseAngle:
220
+ "Name the boundary, human review path, data handling, and fit clearly.",
221
+ },
222
+ competitor: {
223
+ title: "Competitors",
224
+ pattern:
225
+ "Comments mention tools, manual workflows, plugins, spreadsheets, or alternative routes.",
226
+ action:
227
+ "Update the competitor map and decide which difference Tomako should emphasize.",
228
+ backlogTitle: "Clarify alternative comparison",
229
+ faqQuestion: "How is this different from what I use now?",
230
+ responseAngle:
231
+ "Compare workflow differences instead of claiming to be better at everything.",
232
+ },
233
+ feature: {
234
+ title: "Feature requests",
235
+ pattern:
236
+ "People ask for a platform, integration, export, automation, permission model, or new result format.",
237
+ action:
238
+ "Merge repeated requests before putting them into backlog; do not ship every suggestion.",
239
+ backlogTitle: "Evaluate repeated feature requests",
240
+ faqQuestion: "Which inputs and outputs are supported now?",
241
+ responseAngle:
242
+ "State what is supported, what is not, and what is being considered.",
243
+ },
244
+ pricing: {
245
+ title: "Pricing feedback",
246
+ pattern:
247
+ "People mention free, paid, expensive, subscription, budget, pricing anchors, or purchase intent.",
248
+ action:
249
+ "Separate willingness to pay, price resistance, and packaging before changing the price.",
250
+ backlogTitle: "Validate packaging and trial limits",
251
+ faqQuestion: "What should be free versus paid?",
252
+ responseAngle:
253
+ "Explain what is safe to try for free and where paid value starts.",
254
+ },
255
+ copy: {
256
+ title: "Copy feedback",
257
+ pattern:
258
+ "People do not understand the headline, positioning, target user, promise, or use case.",
259
+ action:
260
+ "Feed this directly into the hero copy, FAQ, comment replies, and the next build-in-public post.",
261
+ backlogTitle: "Rewrite positioning and examples",
262
+ faqQuestion: "Am I the target user for this?",
263
+ responseAngle:
264
+ "Explain the audience, situation, and result in plain user language.",
265
+ },
266
+ },
267
+ sourceLabels: {
268
+ mixed: "Mixed sources",
269
+ reddit: "Reddit",
270
+ indieHackers: "Indie Hackers",
271
+ hackerNews: "Hacker News",
272
+ productHunt: "Product Hunt",
273
+ x: "X",
274
+ discord: "Discord",
275
+ },
276
+ authors: {
277
+ unknown: "Unlabeled user",
278
+ },
279
+ summary: {
280
+ noDominant: "No dominant theme yet",
281
+ strongestFallback: "More specific comments are needed to identify the strongest signal",
282
+ nextLaunch:
283
+ "Turn high-quality comments into the next build-in-public post, then validate it in the same community.",
284
+ nextBeta:
285
+ "Invite high-signal commenters into beta and ask for their specific workflow.",
286
+ nextRoadmap:
287
+ "Merge repeated feedback first, then turn evidence-backed requests into backlog candidates.",
288
+ nextLanding:
289
+ "Turn objections and unclear language into landing page FAQ and hero copy edits.",
290
+ },
291
+ beta: {
292
+ reason:
293
+ "This comment contains a clear use case, trial intent, or a useful follow-up question.",
294
+ invite:
295
+ "Thanks for the thoughtful feedback. I am inviting a small beta group and would love to understand your workflow better. Would you be open to trying it and sharing 10 minutes of feedback?",
296
+ },
297
+ content: {
298
+ painTitle: "What community comments revealed about real friction",
299
+ painHook:
300
+ "This is not just exposure; it is reusable user language for the next iteration.",
301
+ objectionTitle: "Why people are still hesitant to try it",
302
+ objectionHook:
303
+ "Objections often improve a landing page faster than compliments do.",
304
+ featureTitle: "Which feature requests deserve backlog attention",
305
+ featureHook:
306
+ "Repeated requests are not priority by themselves, but they reveal how users describe demand.",
307
+ format: "build-in-public post",
308
+ },
309
+ backlog: {
310
+ why:
311
+ "This feedback has repeated evidence and can affect understanding, trial, or conversion.",
312
+ nextAction:
313
+ "Write a hypothesis and acceptance criteria, then validate with the next post or beta interview.",
314
+ },
315
+ missing: {
316
+ author:
317
+ "Comments do not include clear usernames; beta invite candidates are weaker.",
318
+ source:
319
+ "Sources are mixed together; next time paste Reddit, IH, PH, and other channels in separate batches.",
320
+ product:
321
+ "The product context is short; classification works, but generated content angles may stay generic.",
322
+ volume:
323
+ "Comment volume is low; treat the result as qualitative signal, not prioritization proof.",
324
+ },
325
+ review: [
326
+ "Check categories against the original comments, especially before treating a complaint as a feature request.",
327
+ "Only invite people who gave a clear use case, intent, or question; do not mass-DM.",
328
+ "Write repeated feedback as hypotheses with evidence counts before adding it to backlog.",
329
+ "Remove usernames and identifiable private information before publishing build-in-public content.",
330
+ "Turn objections into landing page FAQ before jumping to new features.",
331
+ ],
332
+ markdown: {
333
+ title: "Community Feedback Inbox Report",
334
+ summary: "Summary",
335
+ categories: "Categories",
336
+ beta: "Beta invite shortlist",
337
+ content: "Build-in-public angles",
338
+ backlog: "Backlog candidates",
339
+ faq: "Landing page FAQ candidates",
340
+ objections: "Objection handling",
341
+ quotes: "Quote bank",
342
+ missing: "Missing info",
343
+ review: "Review checklist",
344
+ },
345
+ },
346
+ };
347
+
348
+ const keywordPatterns: Record<CommunityFeedbackCategory, RegExp[]> = {
349
+ pain: [
350
+ /pain|problem|annoy|hard|difficult|confusing|slow|bug|broken|stuck|friction/i,
351
+ /can't|cannot|waste|manual|spreadsheet|messy|scattered|overwhelming/i,
352
+ /痛|卡|难|慢|麻烦|混乱|不会|失败|崩|分散|手动|表格/,
353
+ ],
354
+ objection: [
355
+ /but|however|not sure|worry|concern|trust|privacy|secure|too much|skeptic/i,
356
+ /won't|wouldn't|why would|do i need|is it safe/i,
357
+ /但是|担心|不确定|隐私|安全|可信|复杂|为什么|不会用/,
358
+ ],
359
+ competitor: [
360
+ /competitor|alternative|instead|vs\.?|versus|compare|already use|currently use/i,
361
+ /notion|airtable|canny|productboard|featurebase|userjot|linear|slack|discord/i,
362
+ /竞品|替代|对比|已经在用|现在用|飞书|钉钉|表格/,
363
+ ],
364
+ feature: [
365
+ /feature|request|please add|would love|support|integration|export|import|api|webhook/i,
366
+ /automation|sync|browser extension|csv|markdown|notion|slack/i,
367
+ /功能|希望|能不能|支持|导出|导入|集成|自动|同步|插件|接口/,
368
+ ],
369
+ pricing: [
370
+ /price|pricing|paid|free|cheap|expensive|cost|subscription|plan|budget/i,
371
+ /\$\d+|per month|per seat/i,
372
+ /价格|定价|免费|付费|贵|便宜|订阅|预算|套餐/,
373
+ ],
374
+ copy: [
375
+ /copy|headline|landing|positioning|unclear|confusing|what does|who is this for/i,
376
+ /i don't get|sounds like|explain|tagline|homepage/i,
377
+ /文案|标题|官网|落地页|定位|看不懂|不清楚|给谁用|一句话/,
378
+ ],
379
+ };
380
+
381
+ const betaPatterns = [
382
+ /i would use|we would use|i need|we need|happy to try|beta|waitlist|dm me|message me/i,
383
+ /can i try|sign me up|early access|send me|interested/i,
384
+ /我想试|可以试|内测|发我|私信|愿意|报名|需要这个|我们会用/,
385
+ ];
386
+
387
+ function cleanText(value: string) {
388
+ return value.replace(/\s+/g, " ").trim();
389
+ }
390
+
391
+ function truncate(value: string, max = 220) {
392
+ const text = cleanText(value);
393
+ if (text.length <= max) return text;
394
+ return `${text.slice(0, max - 1).trim()}...`;
395
+ }
396
+
397
+ function parseAuthor(raw: string, fallback: string) {
398
+ const text = raw.trim();
399
+ const match = text.match(/^@?([\p{L}\p{N}_.-]{2,32})\s*(?:[::-]|said:)\s+(.+)$/iu);
400
+ if (!match) return { author: fallback, text };
401
+ return { author: match[1] ?? fallback, text: match[2] ?? text };
402
+ }
403
+
404
+ function splitComments(comments: string) {
405
+ const normalized = comments
406
+ .replace(/\r\n/g, "\n")
407
+ .split(/\n{2,}|(?:\n(?=(?:[-*]\s+|@?[\p{L}\p{N}_.-]{2,32}\s*[::])))/u)
408
+ .map((item) => item.replace(/^[-*]\s+/, "").trim())
409
+ .filter((item) => cleanText(item).length >= 12);
410
+
411
+ if (normalized.length > 1) return normalized.slice(0, 50);
412
+
413
+ return comments
414
+ .split(/\n/)
415
+ .map((item) => item.replace(/^[-*]\s+/, "").trim())
416
+ .filter((item) => cleanText(item).length >= 12)
417
+ .slice(0, 50);
418
+ }
419
+
420
+ function scoreCategory(text: string, category: CommunityFeedbackCategory) {
421
+ return keywordPatterns[category].reduce(
422
+ (score, pattern) => score + (pattern.test(text) ? 1 : 0),
423
+ 0,
424
+ );
425
+ }
426
+
427
+ function categorize(text: string): CommunityFeedbackCategory[] {
428
+ const scored = communityFeedbackCategories
429
+ .map((category) => ({ category, score: scoreCategory(text, category) }))
430
+ .filter((item) => item.score > 0)
431
+ .sort((a, b) => b.score - a.score);
432
+
433
+ if (scored.length === 0) return ["pain"];
434
+ return scored.slice(0, 3).map((item) => item.category);
435
+ }
436
+
437
+ function getSignalScore(text: string, categories: CommunityFeedbackCategory[]) {
438
+ let score = Math.min(30, Math.floor(cleanText(text).length / 12));
439
+ score += categories.length * 12;
440
+ if (betaPatterns.some((pattern) => pattern.test(text))) score += 24;
441
+ if (/[?!?]/.test(text)) score += 6;
442
+ if (/because|when|while|so that|for my|for our|因为|当|为了|所以/.test(text)) {
443
+ score += 10;
444
+ }
445
+ return Math.min(score, 100);
446
+ }
447
+
448
+ function parseFeedback(brief: CommunityFeedbackBrief): CommunityFeedbackComment[] {
449
+ const fallback = copy[brief.language].authors.unknown;
450
+ return splitComments(brief.comments).map((raw, index) => {
451
+ const parsed = parseAuthor(raw, fallback);
452
+ const categories = categorize(parsed.text);
453
+ return {
454
+ id: `comment-${index + 1}`,
455
+ author: parsed.author,
456
+ source: brief.source,
457
+ text: cleanText(parsed.text),
458
+ categories,
459
+ signalScore: getSignalScore(parsed.text, categories),
460
+ };
461
+ });
462
+ }
463
+
464
+ function getDominantCategory(
465
+ comments: CommunityFeedbackComment[],
466
+ language: Language,
467
+ ) {
468
+ const counts = new Map<CommunityFeedbackCategory, number>();
469
+ comments.forEach((comment) => {
470
+ comment.categories.forEach((category) => {
471
+ counts.set(category, (counts.get(category) ?? 0) + 1);
472
+ });
473
+ });
474
+ const top = [...counts.entries()].sort((a, b) => b[1] - a[1])[0];
475
+ if (!top) return copy[language].summary.noDominant;
476
+ return copy[language].categories[top[0]].title;
477
+ }
478
+
479
+ function buildCategorySummaries(
480
+ comments: CommunityFeedbackComment[],
481
+ language: Language,
482
+ ): CommunityFeedbackCategorySummary[] {
483
+ const c = copy[language];
484
+
485
+ return communityFeedbackCategories
486
+ .map((category) => {
487
+ const matching = comments.filter((comment) =>
488
+ comment.categories.includes(category),
489
+ );
490
+ return {
491
+ category,
492
+ title: c.categories[category].title,
493
+ count: matching.length,
494
+ pattern: c.categories[category].pattern,
495
+ action: c.categories[category].action,
496
+ evidence: matching
497
+ .sort((a, b) => b.signalScore - a.signalScore)
498
+ .slice(0, 3)
499
+ .map((comment) => truncate(comment.text)),
500
+ };
501
+ })
502
+ .filter((summary) => summary.count > 0)
503
+ .sort((a, b) => b.count - a.count);
504
+ }
505
+
506
+ function buildBetaCandidates(
507
+ comments: CommunityFeedbackComment[],
508
+ language: Language,
509
+ ): CommunityFeedbackBetaCandidate[] {
510
+ const c = copy[language];
511
+ return comments
512
+ .filter(
513
+ (comment) =>
514
+ comment.signalScore >= 48 ||
515
+ betaPatterns.some((pattern) => pattern.test(comment.text)),
516
+ )
517
+ .sort((a, b) => b.signalScore - a.signalScore)
518
+ .slice(0, 5)
519
+ .map((comment) => ({
520
+ author: comment.author,
521
+ source: comment.source,
522
+ score: comment.signalScore,
523
+ reason: c.beta.reason,
524
+ inviteDraft: c.beta.invite,
525
+ evidence: truncate(comment.text),
526
+ }));
527
+ }
528
+
529
+ function firstCommentFor(
530
+ comments: CommunityFeedbackComment[],
531
+ category: CommunityFeedbackCategory,
532
+ ) {
533
+ return (
534
+ comments
535
+ .filter((comment) => comment.categories.includes(category))
536
+ .sort((a, b) => b.signalScore - a.signalScore)[0]?.text ??
537
+ comments.sort((a, b) => b.signalScore - a.signalScore)[0]?.text ??
538
+ ""
539
+ );
540
+ }
541
+
542
+ function buildContentAngles(
543
+ comments: CommunityFeedbackComment[],
544
+ language: Language,
545
+ ): CommunityFeedbackContentAngle[] {
546
+ const c = copy[language].content;
547
+ return [
548
+ {
549
+ title: c.painTitle,
550
+ hook: c.painHook,
551
+ format: c.format,
552
+ supportingComment: truncate(firstCommentFor(comments, "pain")),
553
+ },
554
+ {
555
+ title: c.objectionTitle,
556
+ hook: c.objectionHook,
557
+ format: c.format,
558
+ supportingComment: truncate(firstCommentFor(comments, "objection")),
559
+ },
560
+ {
561
+ title: c.featureTitle,
562
+ hook: c.featureHook,
563
+ format: c.format,
564
+ supportingComment: truncate(firstCommentFor(comments, "feature")),
565
+ },
566
+ ].filter((item) => item.supportingComment);
567
+ }
568
+
569
+ function confidenceFromCount(count: number): CommunityFeedbackBacklogItem["confidence"] {
570
+ if (count >= 4) return "high";
571
+ if (count >= 2) return "medium";
572
+ return "low";
573
+ }
574
+
575
+ function buildBacklogItems(
576
+ summaries: CommunityFeedbackCategorySummary[],
577
+ language: Language,
578
+ ): CommunityFeedbackBacklogItem[] {
579
+ const c = copy[language];
580
+ return summaries
581
+ .filter((summary) =>
582
+ ["pain", "feature", "pricing", "copy", "objection"].includes(
583
+ summary.category,
584
+ ),
585
+ )
586
+ .slice(0, 5)
587
+ .map((summary) => ({
588
+ title: c.categories[summary.category].backlogTitle,
589
+ category: summary.category,
590
+ evidenceCount: summary.count,
591
+ whyItMatters: c.backlog.why,
592
+ nextAction: c.backlog.nextAction,
593
+ confidence: confidenceFromCount(summary.count),
594
+ }));
595
+ }
596
+
597
+ function buildFaqItems(
598
+ summaries: CommunityFeedbackCategorySummary[],
599
+ language: Language,
600
+ ): CommunityFeedbackFaqItem[] {
601
+ const c = copy[language];
602
+ return summaries.slice(0, 5).map((summary) => ({
603
+ question: c.categories[summary.category].faqQuestion,
604
+ answerAngle: c.categories[summary.category].responseAngle,
605
+ evidence: summary.evidence[0] ?? "",
606
+ }));
607
+ }
608
+
609
+ function buildObjections(
610
+ comments: CommunityFeedbackComment[],
611
+ language: Language,
612
+ ): CommunityFeedbackObjection[] {
613
+ const c = copy[language];
614
+ return comments
615
+ .filter((comment) => comment.categories.includes("objection"))
616
+ .sort((a, b) => b.signalScore - a.signalScore)
617
+ .slice(0, 4)
618
+ .map((comment) => ({
619
+ title: c.categories.objection.title,
620
+ responseAngle: c.categories.objection.responseAngle,
621
+ evidence: truncate(comment.text),
622
+ }));
623
+ }
624
+
625
+ function buildQuoteBank(
626
+ comments: CommunityFeedbackComment[],
627
+ ): CommunityFeedbackQuote[] {
628
+ return comments
629
+ .filter((comment) => comment.signalScore >= 36)
630
+ .sort((a, b) => b.signalScore - a.signalScore)
631
+ .slice(0, 8)
632
+ .map((comment) => ({
633
+ text: truncate(comment.text, 180),
634
+ category: comment.categories[0] ?? "pain",
635
+ source: comment.source,
636
+ }));
637
+ }
638
+
639
+ function buildMissingInfo(
640
+ brief: CommunityFeedbackBrief,
641
+ comments: CommunityFeedbackComment[],
642
+ ) {
643
+ const c = copy[brief.language].missing;
644
+ const missing = [];
645
+ if (comments.every((comment) => comment.author === copy[brief.language].authors.unknown)) {
646
+ missing.push(c.author);
647
+ }
648
+ if (brief.source === "mixed") missing.push(c.source);
649
+ if (brief.productContext.trim().length < 80) missing.push(c.product);
650
+ if (comments.length < 6) missing.push(c.volume);
651
+ return missing;
652
+ }
653
+
654
+ function getRecommendedNextStep(brief: CommunityFeedbackBrief) {
655
+ const c = copy[brief.language].summary;
656
+ if (brief.goal === "betaRecruiting") return c.nextBeta;
657
+ if (brief.goal === "roadmap") return c.nextRoadmap;
658
+ if (brief.goal === "landingPage") return c.nextLanding;
659
+ return c.nextLaunch;
660
+ }
661
+
662
+ export function createCommunityFeedbackInboxReport(
663
+ brief: CommunityFeedbackBrief,
664
+ ): CommunityFeedbackInboxReport {
665
+ const comments = parseFeedback(brief);
666
+ const summaries = buildCategorySummaries(comments, brief.language);
667
+ const strongest =
668
+ comments.sort((a, b) => b.signalScore - a.signalScore)[0]?.text ??
669
+ copy[brief.language].summary.strongestFallback;
670
+ const betaCandidates = buildBetaCandidates(comments, brief.language);
671
+
672
+ return {
673
+ summary: {
674
+ totalComments: comments.length,
675
+ highSignalCount: comments.filter((comment) => comment.signalScore >= 48).length,
676
+ betaCandidateCount: betaCandidates.length,
677
+ dominantCategory: getDominantCategory(comments, brief.language),
678
+ strongestSignal: truncate(strongest),
679
+ recommendedNextStep: getRecommendedNextStep(brief),
680
+ },
681
+ categories: summaries,
682
+ betaCandidates,
683
+ contentAngles: buildContentAngles(comments, brief.language),
684
+ backlogItems: buildBacklogItems(summaries, brief.language),
685
+ faqItems: buildFaqItems(summaries, brief.language),
686
+ objections: buildObjections(comments, brief.language),
687
+ quoteBank: buildQuoteBank(comments),
688
+ missingInfo: buildMissingInfo(brief, comments),
689
+ reviewChecklist: copy[brief.language].review,
690
+ };
691
+ }
692
+
693
+ export function stringifyCommunityFeedbackInboxReport(
694
+ report: CommunityFeedbackInboxReport,
695
+ ) {
696
+ const m = copy.en.markdown;
697
+ const lines = [
698
+ `# ${m.title}`,
699
+ "",
700
+ `## ${m.summary}`,
701
+ `- Total comments: ${report.summary.totalComments}`,
702
+ `- High-signal comments: ${report.summary.highSignalCount}`,
703
+ `- Beta candidates: ${report.summary.betaCandidateCount}`,
704
+ `- Dominant category: ${report.summary.dominantCategory}`,
705
+ `- Strongest signal: ${report.summary.strongestSignal}`,
706
+ `- Recommended next step: ${report.summary.recommendedNextStep}`,
707
+ "",
708
+ `## ${m.categories}`,
709
+ ...report.categories.flatMap((item) => [
710
+ `### ${item.title} (${item.count})`,
711
+ `- Pattern: ${item.pattern}`,
712
+ `- Action: ${item.action}`,
713
+ ...item.evidence.map((evidence) => `- Evidence: ${evidence}`),
714
+ "",
715
+ ]),
716
+ `## ${m.beta}`,
717
+ ...report.betaCandidates.map(
718
+ (item) =>
719
+ `- ${item.author} (${item.score}/100): ${item.reason}\n Evidence: ${item.evidence}\n Draft: ${item.inviteDraft}`,
720
+ ),
721
+ "",
722
+ `## ${m.content}`,
723
+ ...report.contentAngles.map(
724
+ (item) =>
725
+ `- ${item.title}: ${item.hook}\n Format: ${item.format}\n Evidence: ${item.supportingComment}`,
726
+ ),
727
+ "",
728
+ `## ${m.backlog}`,
729
+ ...report.backlogItems.map(
730
+ (item) =>
731
+ `- ${item.title} (${item.confidence}, ${item.evidenceCount} signals): ${item.nextAction}`,
732
+ ),
733
+ "",
734
+ `## ${m.faq}`,
735
+ ...report.faqItems.map(
736
+ (item) =>
737
+ `- Q: ${item.question}\n Angle: ${item.answerAngle}\n Evidence: ${item.evidence}`,
738
+ ),
739
+ "",
740
+ `## ${m.objections}`,
741
+ ...report.objections.map(
742
+ (item) =>
743
+ `- ${item.title}: ${item.responseAngle}\n Evidence: ${item.evidence}`,
744
+ ),
745
+ "",
746
+ `## ${m.quotes}`,
747
+ ...report.quoteBank.map((item) => `- [${item.category}] ${item.text}`),
748
+ "",
749
+ `## ${m.missing}`,
750
+ ...report.missingInfo.map((item) => `- ${item}`),
751
+ "",
752
+ `## ${m.review}`,
753
+ ...report.reviewChecklist.map((item) => `- [ ] ${item}`),
754
+ ];
755
+
756
+ return lines.join("\n");
757
+ }
758
+
759
+ export function getCommunityFeedbackSourceLabel(
760
+ source: CommunityFeedbackSource,
761
+ language: Language,
762
+ ) {
763
+ return copy[language].sourceLabels[source];
764
+ }