@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.
- package/README.md +12 -0
- package/package.json +629 -0
- package/src/assets/icons/brands/social/bluesky-icon.tsx +9 -0
- package/src/assets/icons/brands/social/facebook-icon.tsx +9 -0
- package/src/assets/icons/brands/social/index.ts +8 -0
- package/src/assets/icons/brands/social/instagram-icon.tsx +9 -0
- package/src/assets/icons/brands/social/linkedin-icon.tsx +9 -0
- package/src/assets/icons/brands/social/threads-icon.tsx +9 -0
- package/src/assets/icons/brands/social/tiktok-icon.tsx +10 -0
- package/src/assets/icons/brands/social/x-icon.tsx +9 -0
- package/src/assets/icons/brands/social/youtube-icon.tsx +9 -0
- package/src/assets/icons/social-profile/facebook-icon.tsx +12 -0
- package/src/assets/icons/social-profile/index.ts +29 -0
- package/src/assets/icons/social-profile/instagram-icon.tsx +18 -0
- package/src/assets/icons/social-profile/linkedin-icon.tsx +12 -0
- package/src/assets/icons/social-profile/tiktok-icon.tsx +12 -0
- package/src/assets/icons/social-profile/x-icon.tsx +13 -0
- package/src/assets/icons/social-profile/youtube-icon.tsx +12 -0
- package/src/components/tools/engine/form-builder.tsx +219 -0
- package/src/components/tools/engine/package-widget-loader.tsx +38 -0
- package/src/components/tools/engine/package-widget-runtime.tsx +409 -0
- package/src/components/tools/engine/package-widget.tsx +77 -0
- package/src/components/tools/engine/result-view.tsx +596 -0
- package/src/components/tools/engine/use-legacy-llm-task.ts +64 -0
- package/src/components/tools/engine/use-tool-task.ts +195 -0
- package/src/components/tools/market-forecast-report.tsx +1150 -0
- package/src/components/tools/registry.ts +13 -0
- package/src/components/tools/workspace/index.ts +39 -0
- package/src/components/tools/workspace/tool-action-bar.tsx +99 -0
- package/src/components/tools/workspace/tool-empty-panel.tsx +32 -0
- package/src/components/tools/workspace/tool-error-panel.tsx +40 -0
- package/src/components/tools/workspace/tool-field.tsx +124 -0
- package/src/components/tools/workspace/tool-file-upload.tsx +112 -0
- package/src/components/tools/workspace/tool-form-section.tsx +21 -0
- package/src/components/tools/workspace/tool-form-shell.tsx +36 -0
- package/src/components/tools/workspace/tool-loading-panel.tsx +269 -0
- package/src/components/tools/workspace/tool-option-row.tsx +97 -0
- package/src/components/tools/workspace/tool-preview-panel.tsx +31 -0
- package/src/components/tools/workspace/tool-result-actions.tsx +67 -0
- package/src/components/tools/workspace/tool-result-file-actions.ts +31 -0
- package/src/components/tools/workspace/tool-result-header.tsx +39 -0
- package/src/components/tools/workspace/tool-status.tsx +13 -0
- package/src/components/tools/workspace/tool-workspace-layout.tsx +226 -0
- package/src/constants/LKS.ts +29 -0
- package/src/constants/related-tools.ts +18 -0
- package/src/constants/social-profile-kit.ts +284 -0
- package/src/constants/tool-assets.ts +7 -0
- package/src/env.d.ts +3 -0
- package/src/features/tools/_templates/standard-tool.container.tsx +90 -0
- package/src/features/tools/app-icon-resizer/app-icon-resizer.container.tsx +11 -0
- package/src/features/tools/app-icon-resizer/app-icon-resizer.spec.ts +15 -0
- package/src/features/tools/app-icon-resizer/widget/app-icon-resizer.tsx +444 -0
- package/src/features/tools/app-icon-resizer/widget/constants.ts +19 -0
- package/src/features/tools/app-icon-resizer/widget/helpers.ts +314 -0
- package/src/features/tools/app-icon-resizer/widget/index.ts +7 -0
- package/src/features/tools/app-icon-resizer/widget/platform-icons.tsx +59 -0
- package/src/features/tools/app-icon-resizer/widget/preview-dialogs.tsx +139 -0
- package/src/features/tools/app-icon-resizer/widget/result-shell.tsx +165 -0
- package/src/features/tools/app-icon-resizer/widget/types.ts +36 -0
- package/src/features/tools/app-store-screenshot-generator/app-store-screenshot-generator.container.tsx +108 -0
- package/src/features/tools/app-store-screenshot-generator/app-store-screenshot-generator.spec.ts +14 -0
- package/src/features/tools/app-store-screenshot-generator/config.ts +3 -0
- package/src/features/tools/app-store-screenshot-generator/widget/app-store-screenshot-generator.tsx +1250 -0
- package/src/features/tools/app-store-screenshot-generator/widget/asset-slots.ts +76 -0
- package/src/features/tools/app-store-screenshot-generator/widget/constants.ts +43 -0
- package/src/features/tools/app-store-screenshot-generator/widget/form-controls.tsx +214 -0
- package/src/features/tools/app-store-screenshot-generator/widget/generation.ts +528 -0
- package/src/features/tools/app-store-screenshot-generator/widget/index.ts +7 -0
- package/src/features/tools/app-store-screenshot-generator/widget/input-media.ts +175 -0
- package/src/features/tools/app-store-screenshot-generator/widget/types.ts +60 -0
- package/src/features/tools/app-store-screenshot-generator/widget/zip.ts +119 -0
- package/src/features/tools/build-in-public-story-generator/build-in-public-story-generator.container.tsx +135 -0
- package/src/features/tools/build-in-public-story-generator/build-in-public-story-generator.spec.ts +15 -0
- package/src/features/tools/build-in-public-story-generator/widget/build-in-public-story-generator.tsx +314 -0
- package/src/features/tools/build-in-public-story-generator/widget/constants.ts +3 -0
- package/src/features/tools/build-in-public-story-generator/widget/helpers.ts +25 -0
- package/src/features/tools/build-in-public-story-generator/widget/index.ts +7 -0
- package/src/features/tools/build-in-public-story-generator/widget/preview-panels.tsx +58 -0
- package/src/features/tools/build-in-public-story-generator/widget/result-shell.tsx +164 -0
- package/src/features/tools/build-in-public-story-generator/widget/types.ts +6 -0
- package/src/features/tools/cold-start-channel-selector/cold-start-channel-selector.container.tsx +30 -0
- package/src/features/tools/cold-start-channel-selector/cold-start-channel-selector.spec.ts +15 -0
- package/src/features/tools/cold-start-channel-selector/widget/cold-start-channel-selector.tsx +417 -0
- package/src/features/tools/cold-start-channel-selector/widget/constants.ts +46 -0
- package/src/features/tools/cold-start-channel-selector/widget/form-controls.tsx +41 -0
- package/src/features/tools/cold-start-channel-selector/widget/helpers.ts +75 -0
- package/src/features/tools/cold-start-channel-selector/widget/index.ts +7 -0
- package/src/features/tools/cold-start-channel-selector/widget/preview-panels.tsx +250 -0
- package/src/features/tools/cold-start-channel-selector/widget/result-shell.tsx +199 -0
- package/src/features/tools/cold-start-channel-selector/widget/types.ts +21 -0
- package/src/features/tools/commercial-bp-generator/commercial-bp-generator.container.tsx +102 -0
- package/src/features/tools/commercial-bp-generator/commercial-bp-generator.spec.ts +14 -0
- package/src/features/tools/commercial-bp-generator/widget/commercial-bp-generator.tsx +676 -0
- package/src/features/tools/commercial-bp-generator/widget/constants.ts +32 -0
- package/src/features/tools/commercial-bp-generator/widget/helpers.ts +147 -0
- package/src/features/tools/commercial-bp-generator/widget/index.ts +7 -0
- package/src/features/tools/commercial-bp-generator/widget/result-shell.tsx +271 -0
- package/src/features/tools/commercial-bp-generator/widget/style-option.tsx +54 -0
- package/src/features/tools/commercial-bp-generator/widget/types.ts +33 -0
- package/src/features/tools/community-feedback-inbox/community-feedback-inbox.container.tsx +126 -0
- package/src/features/tools/community-feedback-inbox/community-feedback-inbox.spec.ts +15 -0
- package/src/features/tools/community-feedback-inbox/widget/community-feedback-inbox.tsx +271 -0
- package/src/features/tools/community-feedback-inbox/widget/constants.ts +3 -0
- package/src/features/tools/community-feedback-inbox/widget/helpers.ts +28 -0
- package/src/features/tools/community-feedback-inbox/widget/index.ts +7 -0
- package/src/features/tools/community-feedback-inbox/widget/preview-panels.tsx +92 -0
- package/src/features/tools/community-feedback-inbox/widget/result-shell.tsx +266 -0
- package/src/features/tools/community-feedback-inbox/widget/types.ts +6 -0
- package/src/features/tools/community-pain-point-extractor/community-pain-point-extractor.container.tsx +124 -0
- package/src/features/tools/community-pain-point-extractor/community-pain-point-extractor.spec.ts +15 -0
- package/src/features/tools/community-pain-point-extractor/widget/community-pain-point-extractor.tsx +404 -0
- package/src/features/tools/community-pain-point-extractor/widget/constants.ts +41 -0
- package/src/features/tools/community-pain-point-extractor/widget/helpers.ts +76 -0
- package/src/features/tools/community-pain-point-extractor/widget/index.ts +8 -0
- package/src/features/tools/community-pain-point-extractor/widget/progress-panels.tsx +251 -0
- package/src/features/tools/community-pain-point-extractor/widget/result-shell.tsx +478 -0
- package/src/features/tools/community-pain-point-extractor/widget/types.ts +33 -0
- package/src/features/tools/community-post-risk-checker/community-post-risk-checker.package.ts +792 -0
- package/src/features/tools/competitor-analysis/competitor-analysis.container.tsx +136 -0
- package/src/features/tools/competitor-analysis/competitor-analysis.spec.ts +15 -0
- package/src/features/tools/competitor-analysis/widget/competitor-analysis.tsx +241 -0
- package/src/features/tools/competitor-analysis/widget/constants.ts +9 -0
- package/src/features/tools/competitor-analysis/widget/index.ts +7 -0
- package/src/features/tools/competitor-analysis/widget/preview-panels.tsx +60 -0
- package/src/features/tools/competitor-analysis/widget/result-shell.tsx +418 -0
- package/src/features/tools/competitor-analysis/widget/types.ts +13 -0
- package/src/features/tools/disclaimer-generator/disclaimer-generator.package.ts +339 -0
- package/src/features/tools/email-template-generator/email-template-generator.container.tsx +224 -0
- package/src/features/tools/email-template-generator/email-template-generator.spec.ts +14 -0
- package/src/features/tools/email-template-generator/widget/email-template-generator.tsx +358 -0
- package/src/features/tools/email-template-generator/widget/helpers.ts +39 -0
- package/src/features/tools/email-template-generator/widget/index.ts +7 -0
- package/src/features/tools/email-template-generator/widget/preview-panel.tsx +311 -0
- package/src/features/tools/email-template-generator/widget/result-text-block.tsx +7 -0
- package/src/features/tools/email-template-generator/widget/types.ts +24 -0
- package/src/features/tools/gtm-readiness-checklist/gtm-readiness-checklist.container.tsx +75 -0
- package/src/features/tools/gtm-readiness-checklist/gtm-readiness-checklist.spec.ts +15 -0
- package/src/features/tools/gtm-readiness-checklist/widget/gtm-readiness-checklist.tsx +122 -0
- package/src/features/tools/gtm-readiness-checklist/widget/index.ts +7 -0
- package/src/features/tools/investor-matching-planner/investor-matching-planner.package.ts +869 -0
- package/src/features/tools/koko-codex-assistant/koko-codex-assistant.container.tsx +151 -0
- package/src/features/tools/koko-codex-assistant/koko-codex-assistant.spec.ts +14 -0
- package/src/features/tools/koko-codex-assistant/widget/index.ts +7 -0
- package/src/features/tools/koko-codex-assistant/widget/koko-codex-assistant-download.tsx +192 -0
- package/src/features/tools/logo-generator/logo-generator.container.tsx +112 -0
- package/src/features/tools/logo-generator/logo-generator.spec.ts +15 -0
- package/src/features/tools/logo-generator/widget/index.ts +7 -0
- package/src/features/tools/logo-generator/widget/logo-generator.tsx +1244 -0
- package/src/features/tools/market-forecast-report/market-forecast-report.container.tsx +73 -0
- package/src/features/tools/market-forecast-report/market-forecast-report.spec.ts +15 -0
- package/src/features/tools/million-user-growth-test/million-user-growth-test.container.tsx +123 -0
- package/src/features/tools/million-user-growth-test/million-user-growth-test.spec.ts +14 -0
- package/src/features/tools/million-user-growth-test/widget/index.ts +7 -0
- package/src/features/tools/million-user-growth-test/widget/million-user-growth-test.tsx +433 -0
- package/src/features/tools/module-loader.ts +107 -0
- package/src/features/tools/og-image-generator/og-image-generator.container.tsx +86 -0
- package/src/features/tools/og-image-generator/og-image-generator.spec.ts +14 -0
- package/src/features/tools/og-image-generator/widget/constants.ts +38 -0
- package/src/features/tools/og-image-generator/widget/form-fields.tsx +240 -0
- package/src/features/tools/og-image-generator/widget/index.ts +7 -0
- package/src/features/tools/og-image-generator/widget/og-image-generator.tsx +742 -0
- package/src/features/tools/og-image-generator/widget/preview-panel.tsx +305 -0
- package/src/features/tools/og-image-generator/widget/types.ts +1 -0
- package/src/features/tools/package/direct-models.ts +20 -0
- package/src/features/tools/package/form.ts +38 -0
- package/src/features/tools/package/l10n.ts +22 -0
- package/src/features/tools/package/package-container.tsx +285 -0
- package/src/features/tools/package/package-loaders.ts +22 -0
- package/src/features/tools/package/package-registry.ts +36 -0
- package/src/features/tools/package/result-markdown.ts +142 -0
- package/src/features/tools/package/schema.ts +379 -0
- package/src/features/tools/package/to-tool-module.ts +20 -0
- package/src/features/tools/package/to-tool-spec.ts +52 -0
- package/src/features/tools/package/types.ts +381 -0
- package/src/features/tools/pitch-deck-generator/pitch-deck-generator.container.tsx +175 -0
- package/src/features/tools/pitch-deck-generator/pitch-deck-generator.spec.ts +14 -0
- package/src/features/tools/pitch-deck-generator/pitch-deck-library-data.ts +476 -0
- package/src/features/tools/pitch-deck-generator/pitch-deck-library.tsx +159 -0
- package/src/features/tools/pitch-deck-generator/widget/ceremony-line.tsx +27 -0
- package/src/features/tools/pitch-deck-generator/widget/constants.ts +27 -0
- package/src/features/tools/pitch-deck-generator/widget/index.ts +7 -0
- package/src/features/tools/pitch-deck-generator/widget/pitch-deck-generator.tsx +763 -0
- package/src/features/tools/pitch-deck-generator/widget/result-helpers.ts +102 -0
- package/src/features/tools/pitch-deck-generator/widget/result-shell.tsx +263 -0
- package/src/features/tools/pitch-deck-generator/widget/slide-frame.tsx +106 -0
- package/src/features/tools/pitch-deck-generator/widget/slide-lightbox.tsx +149 -0
- package/src/features/tools/pitch-deck-generator/widget/style-option.tsx +54 -0
- package/src/features/tools/pitch-deck-generator/widget/types.ts +44 -0
- package/src/features/tools/product-agreement-generator/product-agreement-generator.container.tsx +137 -0
- package/src/features/tools/product-agreement-generator/product-agreement-generator.spec.ts +14 -0
- package/src/features/tools/product-agreement-generator/widget/constants.ts +27 -0
- package/src/features/tools/product-agreement-generator/widget/form-controls.tsx +218 -0
- package/src/features/tools/product-agreement-generator/widget/helpers.ts +68 -0
- package/src/features/tools/product-agreement-generator/widget/index.ts +7 -0
- package/src/features/tools/product-agreement-generator/widget/product-agreement-generator.tsx +671 -0
- package/src/features/tools/product-agreement-generator/widget/result-shell.tsx +473 -0
- package/src/features/tools/product-agreement-generator/widget/types.ts +89 -0
- package/src/features/tools/product-hunt-launch-planner/product-hunt-launch-planner.container.tsx +122 -0
- package/src/features/tools/product-hunt-launch-planner/product-hunt-launch-planner.spec.ts +15 -0
- package/src/features/tools/product-hunt-launch-planner/widget/constants.ts +5 -0
- package/src/features/tools/product-hunt-launch-planner/widget/helpers.ts +44 -0
- package/src/features/tools/product-hunt-launch-planner/widget/index.ts +7 -0
- package/src/features/tools/product-hunt-launch-planner/widget/preview-panel.tsx +143 -0
- package/src/features/tools/product-hunt-launch-planner/widget/product-hunt-launch-planner.tsx +504 -0
- package/src/features/tools/product-hunt-launch-planner/widget/result-shell.tsx +176 -0
- package/src/features/tools/product-hunt-launch-planner/widget/types.ts +20 -0
- package/src/features/tools/product-name-generator/product-name-generator.container.tsx +14 -0
- package/src/features/tools/product-name-generator/product-name-generator.spec.ts +15 -0
- package/src/features/tools/product-name-generator/widget/candidate-card.tsx +273 -0
- package/src/features/tools/product-name-generator/widget/constants.ts +21 -0
- package/src/features/tools/product-name-generator/widget/form-controls.tsx +136 -0
- package/src/features/tools/product-name-generator/widget/helpers.ts +41 -0
- package/src/features/tools/product-name-generator/widget/index.ts +7 -0
- package/src/features/tools/product-name-generator/widget/product-name-generator.tsx +776 -0
- package/src/features/tools/product-name-generator/widget/types.ts +36 -0
- package/src/features/tools/product-positioning-analyzer/product-positioning-analyzer.package.ts +801 -0
- package/src/features/tools/product-poster-generator/product-poster-generator.container.tsx +14 -0
- package/src/features/tools/product-poster-generator/product-poster-generator.spec.ts +15 -0
- package/src/features/tools/product-poster-generator/widget/constants.ts +125 -0
- package/src/features/tools/product-poster-generator/widget/form-controls.tsx +204 -0
- package/src/features/tools/product-poster-generator/widget/helpers.ts +155 -0
- package/src/features/tools/product-poster-generator/widget/index.ts +7 -0
- package/src/features/tools/product-poster-generator/widget/product-poster-generator.tsx +663 -0
- package/src/features/tools/product-poster-generator/widget/result-shell.tsx +12 -0
- package/src/features/tools/product-poster-generator/widget/types.ts +83 -0
- package/src/features/tools/product-ui-generator/product-ui-generator.container.tsx +116 -0
- package/src/features/tools/product-ui-generator/product-ui-generator.spec.ts +14 -0
- package/src/features/tools/product-ui-generator/widget/constants.ts +18 -0
- package/src/features/tools/product-ui-generator/widget/helpers.ts +11 -0
- package/src/features/tools/product-ui-generator/widget/index.ts +7 -0
- package/src/features/tools/product-ui-generator/widget/preview-panels.tsx +73 -0
- package/src/features/tools/product-ui-generator/widget/product-ui-generator.tsx +304 -0
- package/src/features/tools/product-ui-generator/widget/result-shell.tsx +118 -0
- package/src/features/tools/product-ui-generator/widget/types.ts +9 -0
- package/src/features/tools/reddit-pain-point-finder/reddit-pain-point-finder.container.tsx +113 -0
- package/src/features/tools/reddit-pain-point-finder/reddit-pain-point-finder.spec.ts +15 -0
- package/src/features/tools/reddit-pain-point-finder/widget/constants.ts +5 -0
- package/src/features/tools/reddit-pain-point-finder/widget/index.ts +7 -0
- package/src/features/tools/reddit-pain-point-finder/widget/preview-panels.tsx +73 -0
- package/src/features/tools/reddit-pain-point-finder/widget/reddit-pain-point-finder.tsx +281 -0
- package/src/features/tools/reddit-pain-point-finder/widget/result-shell.tsx +203 -0
- package/src/features/tools/reddit-pain-point-finder/widget/types.ts +15 -0
- package/src/features/tools/registry.ts +288 -0
- package/src/features/tools/runtime-registry.ts +186 -0
- package/src/features/tools/shared/create-guide-stack-container.tsx +176 -0
- package/src/features/tools/shared/create-standard-landing-container.tsx +163 -0
- package/src/features/tools/shared/tool-content-sections.tsx +659 -0
- package/src/features/tools/shared/tool-cta.tsx +66 -0
- package/src/features/tools/shared/tool-draft-view.tsx +53 -0
- package/src/features/tools/shared/tool-guide-section.tsx +191 -0
- package/src/features/tools/shared/tool-handoff.tsx +38 -0
- package/src/features/tools/shared/tool-mcp-badge.tsx +23 -0
- package/src/features/tools/shared/tool-message-translator.ts +37 -0
- package/src/features/tools/shared/tool-page-shell.tsx +348 -0
- package/src/features/tools/shared/tool-standard-generation-steps.tsx +52 -0
- package/src/features/tools/shared/tool-standard-landing-sections.tsx +447 -0
- package/src/features/tools/shared/tool-ui-registry.ts +237 -0
- package/src/features/tools/shared/tool-widget-slot.tsx +29 -0
- package/src/features/tools/slogan-generator/slogan-generator.package.ts +641 -0
- package/src/features/tools/social-listening-research-report/social-listening-research-report.container.tsx +124 -0
- package/src/features/tools/social-listening-research-report/social-listening-research-report.spec.ts +15 -0
- package/src/features/tools/social-listening-research-report/widget/constants.ts +4 -0
- package/src/features/tools/social-listening-research-report/widget/index.ts +7 -0
- package/src/features/tools/social-listening-research-report/widget/preview-panels.tsx +73 -0
- package/src/features/tools/social-listening-research-report/widget/result-shell.tsx +216 -0
- package/src/features/tools/social-listening-research-report/widget/social-listening-research-report.tsx +303 -0
- package/src/features/tools/social-listening-research-report/widget/types.ts +15 -0
- package/src/features/tools/social-platform-asset-adapter/social-platform-asset-adapter.container.tsx +123 -0
- package/src/features/tools/social-platform-asset-adapter/social-platform-asset-adapter.spec.ts +15 -0
- package/src/features/tools/social-platform-asset-adapter/widget/constants.ts +43 -0
- package/src/features/tools/social-platform-asset-adapter/widget/helpers.ts +249 -0
- package/src/features/tools/social-platform-asset-adapter/widget/index.ts +7 -0
- package/src/features/tools/social-platform-asset-adapter/widget/result-shell.tsx +350 -0
- package/src/features/tools/social-platform-asset-adapter/widget/section-heading.tsx +18 -0
- package/src/features/tools/social-platform-asset-adapter/widget/social-platform-asset-adapter.tsx +641 -0
- package/src/features/tools/social-platform-asset-adapter/widget/types.ts +40 -0
- package/src/features/tools/social-profile-kit/social-profile-kit.container.tsx +177 -0
- package/src/features/tools/social-profile-kit/social-profile-kit.spec.ts +15 -0
- package/src/features/tools/social-profile-kit/widget/context-editor.tsx +171 -0
- package/src/features/tools/social-profile-kit/widget/generation-runtime.ts +281 -0
- package/src/features/tools/social-profile-kit/widget/index.ts +7 -0
- package/src/features/tools/social-profile-kit/widget/input-panel.tsx +294 -0
- package/src/features/tools/social-profile-kit/widget/platform-dock.tsx +115 -0
- package/src/features/tools/social-profile-kit/widget/profile-canvas.tsx +318 -0
- package/src/features/tools/social-profile-kit/widget/profile-preview/bluesky-preview.tsx +188 -0
- package/src/features/tools/social-profile-kit/widget/profile-preview/facebook-preview.tsx +168 -0
- package/src/features/tools/social-profile-kit/widget/profile-preview/index.ts +1 -0
- package/src/features/tools/social-profile-kit/widget/profile-preview/instagram-preview.tsx +278 -0
- package/src/features/tools/social-profile-kit/widget/profile-preview/linkedin-preview.tsx +358 -0
- package/src/features/tools/social-profile-kit/widget/profile-preview/profile-preview.tsx +348 -0
- package/src/features/tools/social-profile-kit/widget/profile-preview/shared.tsx +391 -0
- package/src/features/tools/social-profile-kit/widget/profile-preview/threads-preview.tsx +148 -0
- package/src/features/tools/social-profile-kit/widget/profile-preview/tiktok-preview.tsx +319 -0
- package/src/features/tools/social-profile-kit/widget/profile-preview/x-preview.tsx +341 -0
- package/src/features/tools/social-profile-kit/widget/profile-preview/youtube-preview.tsx +297 -0
- package/src/features/tools/social-profile-kit/widget/social-profile-kit-generator.tsx +1059 -0
- package/src/features/tools/social-profile-kit/widget/types.ts +104 -0
- package/src/features/tools/spec-registry.ts +158 -0
- package/src/features/tools/startup-idea-research/startup-idea-research.container.tsx +111 -0
- package/src/features/tools/startup-idea-research/startup-idea-research.spec.ts +15 -0
- package/src/features/tools/startup-idea-research/widget/constants.ts +11 -0
- package/src/features/tools/startup-idea-research/widget/index.ts +7 -0
- package/src/features/tools/startup-idea-research/widget/preview-panels.tsx +67 -0
- package/src/features/tools/startup-idea-research/widget/result-shell.tsx +323 -0
- package/src/features/tools/startup-idea-research/widget/startup-idea-research.tsx +272 -0
- package/src/features/tools/startup-idea-research/widget/types.ts +21 -0
- package/src/features/tools/tarot-reading/tarot-reading.container.tsx +412 -0
- package/src/features/tools/tarot-reading/tarot-reading.spec.ts +15 -0
- package/src/features/tools/tarot-reading/widget/cards.tsx +152 -0
- package/src/features/tools/tarot-reading/widget/constants.ts +69 -0
- package/src/features/tools/tarot-reading/widget/deck-view.tsx +86 -0
- package/src/features/tools/tarot-reading/widget/deck.ts +86 -0
- package/src/features/tools/tarot-reading/widget/index.ts +7 -0
- package/src/features/tools/tarot-reading/widget/landing-view.tsx +209 -0
- package/src/features/tools/tarot-reading/widget/reading-copy.tsx +138 -0
- package/src/features/tools/tarot-reading/widget/reading-view.tsx +189 -0
- package/src/features/tools/tarot-reading/widget/reveal-view.tsx +50 -0
- package/src/features/tools/tarot-reading/widget/ritual-view.tsx +276 -0
- package/src/features/tools/tarot-reading/widget/tarot-reading.tsx +262 -0
- package/src/features/tools/tarot-reading/widget/types.ts +22 -0
- package/src/features/tools/twitter-gif-downloader/twitter-gif-downloader.container.tsx +114 -0
- package/src/features/tools/twitter-gif-downloader/twitter-gif-downloader.spec.ts +14 -0
- package/src/features/tools/twitter-gif-downloader/widget/index.ts +7 -0
- package/src/features/tools/twitter-gif-downloader/widget/twitter-gif-downloader.tsx +351 -0
- package/src/features/tools/types.ts +68 -0
- package/src/features/tools/user-interview-guide-generator/user-interview-guide-generator.package.ts +774 -0
- package/src/features/tools/user-interview-insight-report-generator/user-interview-insight-report-generator.container.tsx +23 -0
- package/src/features/tools/user-interview-insight-report-generator/user-interview-insight-report-generator.spec.ts +14 -0
- package/src/features/tools/user-interview-insight-report-generator/widget/constants.ts +8 -0
- package/src/features/tools/user-interview-insight-report-generator/widget/helpers.ts +4 -0
- package/src/features/tools/user-interview-insight-report-generator/widget/index.ts +7 -0
- package/src/features/tools/user-interview-insight-report-generator/widget/preview-panels.tsx +55 -0
- package/src/features/tools/user-interview-insight-report-generator/widget/result-shell.tsx +122 -0
- package/src/features/tools/user-interview-insight-report-generator/widget/types.ts +16 -0
- package/src/features/tools/user-interview-insight-report-generator/widget/user-interview-insight-report-generator.tsx +293 -0
- package/src/features/tools/user-interview-record-generator/user-interview-record-generator.container.tsx +18 -0
- package/src/features/tools/user-interview-record-generator/user-interview-record-generator.spec.ts +14 -0
- package/src/features/tools/user-interview-record-generator/widget/constants.ts +9 -0
- package/src/features/tools/user-interview-record-generator/widget/helpers.ts +4 -0
- package/src/features/tools/user-interview-record-generator/widget/index.ts +7 -0
- package/src/features/tools/user-interview-record-generator/widget/preview-panels.tsx +48 -0
- package/src/features/tools/user-interview-record-generator/widget/result-shell.tsx +128 -0
- package/src/features/tools/user-interview-record-generator/widget/types.ts +16 -0
- package/src/features/tools/user-interview-record-generator/widget/user-interview-record-generator.tsx +305 -0
- package/src/features/tools/utm-builder/utm-builder.package.ts +846 -0
- package/src/features/tools/widget-registry.ts +73 -0
- package/src/generated/mcp-tools-registry.json +17 -0
- package/src/host/client-proxy.test.ts +33 -0
- package/src/host/client.ts +678 -0
- package/src/host/server.ts +119 -0
- package/src/i18n/client.ts +11 -0
- package/src/i18n/locale-fallback.ts +38 -0
- package/src/i18n/locale.ts +18 -0
- package/src/i18n/messages/en/page/tools.ts +124 -0
- package/src/i18n/messages/en/tools/app-icon-resizer.ts +244 -0
- package/src/i18n/messages/en/tools/app-store-screenshot-generator.ts +145 -0
- package/src/i18n/messages/en/tools/build-in-public-story-generator.ts +229 -0
- package/src/i18n/messages/en/tools/cold-start-channel-selector.ts +423 -0
- package/src/i18n/messages/en/tools/commercial-bp-generator.ts +304 -0
- package/src/i18n/messages/en/tools/community-feedback-inbox.ts +218 -0
- package/src/i18n/messages/en/tools/community-pain-point-extractor.ts +230 -0
- package/src/i18n/messages/en/tools/community-post-risk-checker.ts +209 -0
- package/src/i18n/messages/en/tools/competitor-analysis.ts +197 -0
- package/src/i18n/messages/en/tools/email-template-generator.ts +302 -0
- package/src/i18n/messages/en/tools/engine.ts +69 -0
- package/src/i18n/messages/en/tools/gtm-readiness-checklist.ts +166 -0
- package/src/i18n/messages/en/tools/index.ts +73 -0
- package/src/i18n/messages/en/tools/koko-codex-assistant.ts +171 -0
- package/src/i18n/messages/en/tools/logo-generator.ts +173 -0
- package/src/i18n/messages/en/tools/market-forecast-report.ts +228 -0
- package/src/i18n/messages/en/tools/million-user-growth-test.ts +386 -0
- package/src/i18n/messages/en/tools/og-image-generator.ts +172 -0
- package/src/i18n/messages/en/tools/pitch-deck-generator.ts +358 -0
- package/src/i18n/messages/en/tools/product-agreement-generator.ts +411 -0
- package/src/i18n/messages/en/tools/product-hunt-launch-planner.ts +272 -0
- package/src/i18n/messages/en/tools/product-name-generator.ts +383 -0
- package/src/i18n/messages/en/tools/product-poster-generator.ts +294 -0
- package/src/i18n/messages/en/tools/product-ui-generator.ts +234 -0
- package/src/i18n/messages/en/tools/reddit-pain-point-finder.ts +163 -0
- package/src/i18n/messages/en/tools/shared.ts +24 -0
- package/src/i18n/messages/en/tools/social-listening-research-report.ts +185 -0
- package/src/i18n/messages/en/tools/social-media-profile-generator.ts +22 -0
- package/src/i18n/messages/en/tools/social-platform-asset-adapter.ts +287 -0
- package/src/i18n/messages/en/tools/social-profile-kit.ts +150 -0
- package/src/i18n/messages/en/tools/startup-idea-research.ts +170 -0
- package/src/i18n/messages/en/tools/tarot-reading.ts +230 -0
- package/src/i18n/messages/en/tools/twitter-gif-downloader.ts +145 -0
- package/src/i18n/messages/en/tools/user-interview-insight-report-generator.ts +242 -0
- package/src/i18n/messages/en/tools/user-interview-record-generator.ts +244 -0
- package/src/i18n/messages/en/tools-ui.ts +704 -0
- package/src/i18n/messages/zh/page/tools.ts +124 -0
- package/src/i18n/messages/zh/tools/app-icon-resizer.ts +242 -0
- package/src/i18n/messages/zh/tools/app-store-screenshot-generator.ts +145 -0
- package/src/i18n/messages/zh/tools/build-in-public-story-generator.ts +230 -0
- package/src/i18n/messages/zh/tools/cold-start-channel-selector.ts +422 -0
- package/src/i18n/messages/zh/tools/commercial-bp-generator.ts +302 -0
- package/src/i18n/messages/zh/tools/community-feedback-inbox.ts +217 -0
- package/src/i18n/messages/zh/tools/community-pain-point-extractor.ts +230 -0
- package/src/i18n/messages/zh/tools/community-post-risk-checker.ts +200 -0
- package/src/i18n/messages/zh/tools/competitor-analysis.ts +192 -0
- package/src/i18n/messages/zh/tools/email-template-generator.ts +302 -0
- package/src/i18n/messages/zh/tools/engine.ts +68 -0
- package/src/i18n/messages/zh/tools/gtm-readiness-checklist.ts +166 -0
- package/src/i18n/messages/zh/tools/index.ts +73 -0
- package/src/i18n/messages/zh/tools/koko-codex-assistant.ts +170 -0
- package/src/i18n/messages/zh/tools/logo-generator.ts +172 -0
- package/src/i18n/messages/zh/tools/market-forecast-report.ts +228 -0
- package/src/i18n/messages/zh/tools/million-user-growth-test.ts +385 -0
- package/src/i18n/messages/zh/tools/og-image-generator.ts +173 -0
- package/src/i18n/messages/zh/tools/pitch-deck-generator.ts +358 -0
- package/src/i18n/messages/zh/tools/product-agreement-generator.ts +401 -0
- package/src/i18n/messages/zh/tools/product-hunt-launch-planner.ts +267 -0
- package/src/i18n/messages/zh/tools/product-name-generator.ts +383 -0
- package/src/i18n/messages/zh/tools/product-poster-generator.ts +282 -0
- package/src/i18n/messages/zh/tools/product-ui-generator.ts +234 -0
- package/src/i18n/messages/zh/tools/reddit-pain-point-finder.ts +162 -0
- package/src/i18n/messages/zh/tools/shared.ts +24 -0
- package/src/i18n/messages/zh/tools/social-listening-research-report.ts +181 -0
- package/src/i18n/messages/zh/tools/social-media-profile-generator.ts +22 -0
- package/src/i18n/messages/zh/tools/social-platform-asset-adapter.ts +284 -0
- package/src/i18n/messages/zh/tools/social-profile-kit.ts +150 -0
- package/src/i18n/messages/zh/tools/startup-idea-research.ts +165 -0
- package/src/i18n/messages/zh/tools/tarot-reading.ts +228 -0
- package/src/i18n/messages/zh/tools/twitter-gif-downloader.ts +138 -0
- package/src/i18n/messages/zh/tools/user-interview-insight-report-generator.ts +224 -0
- package/src/i18n/messages/zh/tools/user-interview-record-generator.ts +226 -0
- package/src/i18n/messages/zh/tools-ui.ts +684 -0
- package/src/i18n/messages/zh-tw/page/tools.ts +115 -0
- package/src/i18n/messages/zh-tw/tools/app-icon-resizer.ts +242 -0
- package/src/i18n/messages/zh-tw/tools/app-store-screenshot-generator.ts +145 -0
- package/src/i18n/messages/zh-tw/tools/build-in-public-story-generator.ts +230 -0
- package/src/i18n/messages/zh-tw/tools/cold-start-channel-selector.ts +440 -0
- package/src/i18n/messages/zh-tw/tools/commercial-bp-generator.ts +302 -0
- package/src/i18n/messages/zh-tw/tools/community-feedback-inbox.ts +217 -0
- package/src/i18n/messages/zh-tw/tools/community-pain-point-extractor.ts +230 -0
- package/src/i18n/messages/zh-tw/tools/community-post-risk-checker.ts +200 -0
- package/src/i18n/messages/zh-tw/tools/competitor-analysis.ts +192 -0
- package/src/i18n/messages/zh-tw/tools/email-template-generator.ts +302 -0
- package/src/i18n/messages/zh-tw/tools/engine.ts +68 -0
- package/src/i18n/messages/zh-tw/tools/gtm-readiness-checklist.ts +166 -0
- package/src/i18n/messages/zh-tw/tools/index.ts +73 -0
- package/src/i18n/messages/zh-tw/tools/koko-codex-assistant.ts +170 -0
- package/src/i18n/messages/zh-tw/tools/logo-generator.ts +71 -0
- package/src/i18n/messages/zh-tw/tools/market-forecast-report.ts +228 -0
- package/src/i18n/messages/zh-tw/tools/million-user-growth-test.ts +385 -0
- package/src/i18n/messages/zh-tw/tools/og-image-generator.ts +173 -0
- package/src/i18n/messages/zh-tw/tools/pitch-deck-generator.ts +358 -0
- package/src/i18n/messages/zh-tw/tools/product-agreement-generator.ts +401 -0
- package/src/i18n/messages/zh-tw/tools/product-hunt-launch-planner.ts +267 -0
- package/src/i18n/messages/zh-tw/tools/product-name-generator.ts +383 -0
- package/src/i18n/messages/zh-tw/tools/product-poster-generator.ts +282 -0
- package/src/i18n/messages/zh-tw/tools/product-ui-generator.ts +234 -0
- package/src/i18n/messages/zh-tw/tools/reddit-pain-point-finder.ts +162 -0
- package/src/i18n/messages/zh-tw/tools/shared.ts +24 -0
- package/src/i18n/messages/zh-tw/tools/social-listening-research-report.ts +181 -0
- package/src/i18n/messages/zh-tw/tools/social-media-profile-generator.ts +22 -0
- package/src/i18n/messages/zh-tw/tools/social-platform-asset-adapter.ts +284 -0
- package/src/i18n/messages/zh-tw/tools/social-profile-kit.ts +150 -0
- package/src/i18n/messages/zh-tw/tools/startup-idea-research.ts +165 -0
- package/src/i18n/messages/zh-tw/tools/tarot-reading.ts +228 -0
- package/src/i18n/messages/zh-tw/tools/twitter-gif-downloader.ts +138 -0
- package/src/i18n/messages/zh-tw/tools/user-interview-insight-report-generator.ts +224 -0
- package/src/i18n/messages/zh-tw/tools/user-interview-record-generator.ts +226 -0
- package/src/i18n/messages/zh-tw/tools-ui.ts +525 -0
- package/src/index.ts +15 -0
- package/src/lib/activity-task-handoff.ts +404 -0
- package/src/lib/api/errors.ts +39 -0
- package/src/lib/app-origin.ts +30 -0
- package/src/lib/fact-claim-contract.ts +68 -0
- package/src/lib/fake-delay.ts +3 -0
- package/src/lib/file/download.ts +10 -0
- package/src/lib/routing/locale-paths.ts +27 -0
- package/src/lib/seo-meta.ts +3 -0
- package/src/lib/tools/app-icon-resizer-schema.ts +191 -0
- package/src/lib/tools/app-store-screenshot-generator-schema.ts +185 -0
- package/src/lib/tools/build-in-public-story-generator-schema.ts +67 -0
- package/src/lib/tools/build-in-public-story-generator.format.ts +507 -0
- package/src/lib/tools/cold-start-channel-selector-schema.ts +147 -0
- package/src/lib/tools/commercial-bp-generator-schema.ts +222 -0
- package/src/lib/tools/community-feedback-inbox-schema.ts +125 -0
- package/src/lib/tools/community-feedback-inbox.format.ts +764 -0
- package/src/lib/tools/community-pain-point-extractor-schema.ts +166 -0
- package/src/lib/tools/community-post-risk-checker-schema.ts +99 -0
- package/src/lib/tools/community-post-risk-checker.format.ts +557 -0
- package/src/lib/tools/competitor-analysis-schema.ts +144 -0
- package/src/lib/tools/draft-preview-access.ts +3 -0
- package/src/lib/tools/draft-store.ts +69 -0
- package/src/lib/tools/email-template-generator-schema.ts +124 -0
- package/src/lib/tools/gtm-checklist-copy.ts +57 -0
- package/src/lib/tools/gtm-checklist-structure.ts +37 -0
- package/src/lib/tools/image-download.ts +68 -0
- package/src/lib/tools/input-quality.ts +97 -0
- package/src/lib/tools/logo-generator-schema.ts +224 -0
- package/src/lib/tools/market-forecast-report-schema.ts +299 -0
- package/src/lib/tools/mcp-registry.ts +16 -0
- package/src/lib/tools/mcp-types.ts +18 -0
- package/src/lib/tools/million-user-growth-test-schema.ts +49 -0
- package/src/lib/tools/million-user-growth-test.ts +181 -0
- package/src/lib/tools/og-image-generator-render.ts +448 -0
- package/src/lib/tools/og-image-generator-schema.ts +140 -0
- package/src/lib/tools/ops-catalog.ts +102 -0
- package/src/lib/tools/pitch-deck-generator-schema.ts +182 -0
- package/src/lib/tools/pitch-deck-package.ts +0 -0
- package/src/lib/tools/product-agreement-generator-schema.ts +168 -0
- package/src/lib/tools/product-hunt-launch-planner-schema.ts +136 -0
- package/src/lib/tools/product-hunt-launch-planner.format.ts +72 -0
- package/src/lib/tools/product-name-generator-schema.ts +563 -0
- package/src/lib/tools/product-poster-generator-schema.ts +215 -0
- package/src/lib/tools/product-ui-generator-schema.ts +157 -0
- package/src/lib/tools/reddit-pain-point-finder-schema.ts +88 -0
- package/src/lib/tools/resolve-tool-locale-copy.ts +75 -0
- package/src/lib/tools/slug.ts +60 -0
- package/src/lib/tools/social-listening-research-report-schema.ts +105 -0
- package/src/lib/tools/social-platform-asset-adapter-schema.ts +152 -0
- package/src/lib/tools/social-profile-kit.constants.ts +12 -0
- package/src/lib/tools/social-profile-kit.generation.ts +257 -0
- package/src/lib/tools/social-profile-kit.input.ts +290 -0
- package/src/lib/tools/social-profile-kit.manual.ts +481 -0
- package/src/lib/tools/social-profile-kit.package.ts +220 -0
- package/src/lib/tools/social-profile-kit.schema.ts +328 -0
- package/src/lib/tools/startup-idea-research-schema.ts +107 -0
- package/src/lib/tools/tarot-imagery.ts +89 -0
- package/src/lib/tools/tarot-reading.ts +1044 -0
- package/src/lib/tools/tool-message-keys.ts +18 -0
- package/src/lib/tools/types.ts +32 -0
- package/src/lib/tools/user-interview.format.ts +147 -0
- package/src/lib/tools/user-interview.generation.ts +670 -0
- package/src/lib/tools/user-interview.schema.ts +192 -0
- package/src/lib/zip.ts +106 -0
- package/src/services/tools/handlers/gtm-readiness-checklist.ts +71 -0
- package/src/services/tools/handlers/types.ts +7 -0
|
@@ -0,0 +1,557 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
CommunityPostCheckId,
|
|
3
|
+
CommunityPostCommunity,
|
|
4
|
+
CommunityPostCtaAdvice,
|
|
5
|
+
CommunityPostCtaPlan,
|
|
6
|
+
CommunityPostGoal,
|
|
7
|
+
CommunityPostLanguage,
|
|
8
|
+
CommunityPostRiskBrief,
|
|
9
|
+
CommunityPostRiskCheck,
|
|
10
|
+
CommunityPostRiskLevel,
|
|
11
|
+
CommunityPostRiskReport,
|
|
12
|
+
CommunityPostRiskStatus,
|
|
13
|
+
} from "./community-post-risk-checker-schema";
|
|
14
|
+
|
|
15
|
+
type CopyPack = {
|
|
16
|
+
checks: Record<CommunityPostCheckId, string>;
|
|
17
|
+
verdict: Record<CommunityPostRiskLevel, string>;
|
|
18
|
+
primaryFix: Record<CommunityPostRiskLevel, string>;
|
|
19
|
+
cta: Record<
|
|
20
|
+
"none" | "comment" | "bodyAfterContext" | "onlyIfAsked",
|
|
21
|
+
CommunityPostCtaAdvice
|
|
22
|
+
>;
|
|
23
|
+
fixes: Record<CommunityPostCheckId, string>;
|
|
24
|
+
reasons: Record<CommunityPostCheckId, (score: number) => string>;
|
|
25
|
+
flagReasons: Record<
|
|
26
|
+
"link" | "cta" | "promo" | "missingProcess" | "transparency",
|
|
27
|
+
string
|
|
28
|
+
>;
|
|
29
|
+
flagSuggestion: string;
|
|
30
|
+
missing: Record<"process" | "numbers" | "audience" | "tradeoff", string>;
|
|
31
|
+
revisionPlan: Record<
|
|
32
|
+
"title" | "opening" | "process" | "cta" | "disclosure",
|
|
33
|
+
string
|
|
34
|
+
>;
|
|
35
|
+
checklist: string[];
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const copy: Record<CommunityPostLanguage, CopyPack> = {
|
|
39
|
+
en: {
|
|
40
|
+
checks: {
|
|
41
|
+
titlePromotion: "Title promotion risk",
|
|
42
|
+
ctaTiming: "CTA timing",
|
|
43
|
+
processEvidence: "Real process evidence",
|
|
44
|
+
communityTone: "Community tone fit",
|
|
45
|
+
selfPromotion: "Self-promotion surface",
|
|
46
|
+
transparency: "Builder disclosure",
|
|
47
|
+
},
|
|
48
|
+
verdict: {
|
|
49
|
+
low: "Low self-promotion risk. The draft reads mostly like a community post, but still needs a final human pass against the subreddit rules.",
|
|
50
|
+
medium:
|
|
51
|
+
"Medium risk. The draft has useful context, yet a few sections still feel closer to product marketing than peer discussion.",
|
|
52
|
+
high: "High risk. This draft is likely to be read as promotional unless the CTA, title, and process evidence are rewritten.",
|
|
53
|
+
},
|
|
54
|
+
primaryFix: {
|
|
55
|
+
low: "Keep the CTA soft and preserve the process details.",
|
|
56
|
+
medium:
|
|
57
|
+
"Move the product ask later and add more concrete process, mistakes, or decision tradeoffs.",
|
|
58
|
+
high: "Remove early conversion language, rewrite the title as a learning question, and add the real story before mentioning the product.",
|
|
59
|
+
},
|
|
60
|
+
cta: {
|
|
61
|
+
none: {
|
|
62
|
+
placement: "Do not add a CTA in the post",
|
|
63
|
+
rationale:
|
|
64
|
+
"For advice or learning posts, the safest path is to ask for discussion rather than clicks.",
|
|
65
|
+
saferAlternative:
|
|
66
|
+
"End with one specific question and share the link only if someone asks.",
|
|
67
|
+
},
|
|
68
|
+
comment: {
|
|
69
|
+
placement: "Put the CTA in a first comment",
|
|
70
|
+
rationale:
|
|
71
|
+
"Reddit-style communities often punish body links before readers see the process or lesson.",
|
|
72
|
+
saferAlternative:
|
|
73
|
+
"Say the post is about the lesson first, then add a transparent comment with the link.",
|
|
74
|
+
},
|
|
75
|
+
bodyAfterContext: {
|
|
76
|
+
placement: "Place a soft CTA near the end of the body",
|
|
77
|
+
rationale:
|
|
78
|
+
"The draft has enough process context to support a careful, low-pressure ask.",
|
|
79
|
+
saferAlternative:
|
|
80
|
+
"Use wording like: 'Happy to share more context if useful.'",
|
|
81
|
+
},
|
|
82
|
+
onlyIfAsked: {
|
|
83
|
+
placement: "Share the CTA only if asked",
|
|
84
|
+
rationale:
|
|
85
|
+
"The current draft has enough promotional surface that a visible CTA would raise moderation risk.",
|
|
86
|
+
saferAlternative:
|
|
87
|
+
"Close with a question, then reply with the link only in relevant comment threads.",
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
fixes: {
|
|
91
|
+
titlePromotion:
|
|
92
|
+
"Rewrite the title as a specific question, lesson, or tradeoff instead of a product claim.",
|
|
93
|
+
ctaTiming:
|
|
94
|
+
"Delay the CTA until readers have seen the context, failure, evidence, and ask.",
|
|
95
|
+
processEvidence:
|
|
96
|
+
"Add what you tried, what failed, what changed, and one concrete detail from the process.",
|
|
97
|
+
communityTone:
|
|
98
|
+
"Use peer-to-peer language, fewer imperatives, and one specific question for the community.",
|
|
99
|
+
selfPromotion:
|
|
100
|
+
"Reduce product naming, links, sign-up language, and benefit claims in the body.",
|
|
101
|
+
transparency:
|
|
102
|
+
"Disclose your relationship to the product before asking for feedback or clicks.",
|
|
103
|
+
},
|
|
104
|
+
reasons: {
|
|
105
|
+
titlePromotion: (score) =>
|
|
106
|
+
score > 60
|
|
107
|
+
? "The title contains launch, superlative, or benefit-claim language."
|
|
108
|
+
: "The title is mostly neutral, with limited promotional wording.",
|
|
109
|
+
ctaTiming: (score) =>
|
|
110
|
+
score > 60
|
|
111
|
+
? "A link or conversion ask appears before the reader gets enough context."
|
|
112
|
+
: "The CTA is either absent, late, or low-pressure.",
|
|
113
|
+
processEvidence: (score) =>
|
|
114
|
+
score > 60
|
|
115
|
+
? "The post does not show enough of the real process behind the claim."
|
|
116
|
+
: "The draft includes some useful process or learning signals.",
|
|
117
|
+
communityTone: (score) =>
|
|
118
|
+
score > 60
|
|
119
|
+
? "The tone leans toward announcement copy rather than a community discussion."
|
|
120
|
+
: "The tone is reasonably close to peer discussion.",
|
|
121
|
+
selfPromotion: (score) =>
|
|
122
|
+
score > 60
|
|
123
|
+
? "Product mentions, links, and benefit language create a self-promotion surface."
|
|
124
|
+
: "The product surface is controlled enough for a community draft.",
|
|
125
|
+
transparency: (score) =>
|
|
126
|
+
score > 60
|
|
127
|
+
? "The draft asks readers to engage without clearly stating your relationship to the product."
|
|
128
|
+
: "The builder relationship is clear enough for a first pass.",
|
|
129
|
+
},
|
|
130
|
+
flagReasons: {
|
|
131
|
+
link: "contains a visible link or domain",
|
|
132
|
+
cta: "contains a conversion-style CTA",
|
|
133
|
+
promo: "uses promotional or benefit-claim language",
|
|
134
|
+
missingProcess: "mentions the product without enough process evidence",
|
|
135
|
+
transparency: "needs clearer founder/product disclosure",
|
|
136
|
+
},
|
|
137
|
+
flagSuggestion:
|
|
138
|
+
"Rewrite this paragraph around what happened, what you learned, and what you want feedback on.",
|
|
139
|
+
missing: {
|
|
140
|
+
process: "Add one concrete step you tried before writing this post.",
|
|
141
|
+
numbers: "Add a small number, sample size, or time window if it is honest.",
|
|
142
|
+
audience: "Name who the post is for without turning it into a sales segment.",
|
|
143
|
+
tradeoff: "Include one tradeoff, failed attempt, or open question.",
|
|
144
|
+
},
|
|
145
|
+
revisionPlan: {
|
|
146
|
+
title: "Make the title less like an ad and more like a specific community question.",
|
|
147
|
+
opening: "Use the first paragraph to establish context, not the product pitch.",
|
|
148
|
+
process: "Add the messy middle: attempts, constraints, evidence, and what changed.",
|
|
149
|
+
cta: "Move the CTA to a comment or remove it until someone asks.",
|
|
150
|
+
disclosure:
|
|
151
|
+
"State clearly whether you built, work on, or are affiliated with the product.",
|
|
152
|
+
},
|
|
153
|
+
checklist: [
|
|
154
|
+
"Could a moderator understand the post without clicking anything?",
|
|
155
|
+
"Does the body contain more learning than product claims?",
|
|
156
|
+
"Is the CTA optional, late, or comment-only?",
|
|
157
|
+
"Would the post still be useful if the product name were removed?",
|
|
158
|
+
],
|
|
159
|
+
},
|
|
160
|
+
zh: {
|
|
161
|
+
checks: {
|
|
162
|
+
titlePromotion: "标题广告感",
|
|
163
|
+
ctaTiming: "CTA 出现时机",
|
|
164
|
+
processEvidence: "真实过程证据",
|
|
165
|
+
communityTone: "社区语气匹配",
|
|
166
|
+
selfPromotion: "自我推广风险",
|
|
167
|
+
transparency: "身份透明度",
|
|
168
|
+
},
|
|
169
|
+
verdict: {
|
|
170
|
+
low: "低风险。草稿整体像社区讨论,但发布前仍应人工核对对应社区规则。",
|
|
171
|
+
medium:
|
|
172
|
+
"中等风险。内容有真实上下文,但部分段落仍接近产品营销,需要降噪。",
|
|
173
|
+
high: "高风险。标题、CTA 或产品描述会让帖子很容易被读成广告,建议重写后再发。",
|
|
174
|
+
},
|
|
175
|
+
primaryFix: {
|
|
176
|
+
low: "保留过程细节,让 CTA 保持克制。",
|
|
177
|
+
medium: "把产品请求后移,并补充更具体的过程、失败点或取舍。",
|
|
178
|
+
high: "先移除早期转化语言,把标题改成学习型问题,再补真实过程后再提产品。",
|
|
179
|
+
},
|
|
180
|
+
cta: {
|
|
181
|
+
none: {
|
|
182
|
+
placement: "正文里不要放 CTA",
|
|
183
|
+
rationale: "如果目的是请教或分享经验,最安全的做法是引发讨论,而不是引导点击。",
|
|
184
|
+
saferAlternative: "结尾只问一个具体问题,只有别人追问时再给链接。",
|
|
185
|
+
},
|
|
186
|
+
comment: {
|
|
187
|
+
placement: "把 CTA 放到首条评论",
|
|
188
|
+
rationale: "Reddit 类社区通常更敏感正文早期链接,尤其在读者还没看到过程之前。",
|
|
189
|
+
saferAlternative: "正文先讲经验和问题,再用透明评论补充链接。",
|
|
190
|
+
},
|
|
191
|
+
bodyAfterContext: {
|
|
192
|
+
placement: "可以在正文末尾放一个弱 CTA",
|
|
193
|
+
rationale: "草稿已有足够过程上下文,可以承载低压力请求。",
|
|
194
|
+
saferAlternative: "用类似“如果有帮助,我可以补更多上下文”的弱表达。",
|
|
195
|
+
},
|
|
196
|
+
onlyIfAsked: {
|
|
197
|
+
placement: "只有被问到时再发 CTA",
|
|
198
|
+
rationale: "当前草稿的推广面较强,公开 CTA 会明显增加社区风险。",
|
|
199
|
+
saferAlternative: "正文以问题收尾,在相关评论线程里再回复链接。",
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
fixes: {
|
|
203
|
+
titlePromotion: "把标题改成具体问题、经验或取舍,不要像产品卖点。",
|
|
204
|
+
ctaTiming: "等读者看到背景、失败、证据和请求后,再考虑是否出现 CTA。",
|
|
205
|
+
processEvidence: "补充你试过什么、哪里失败、后来怎么改,以及一个真实细节。",
|
|
206
|
+
communityTone: "使用同侪讨论语气,减少命令式表达,并提出一个具体问题。",
|
|
207
|
+
selfPromotion: "减少产品名、链接、注册、试用和收益承诺。",
|
|
208
|
+
transparency: "在请求反馈或点击前,说明你和产品的关系。",
|
|
209
|
+
},
|
|
210
|
+
reasons: {
|
|
211
|
+
titlePromotion: (score) =>
|
|
212
|
+
score > 60 ? "标题含有发布、夸张或收益承诺表达。" : "标题整体较克制,广告词不多。",
|
|
213
|
+
ctaTiming: (score) =>
|
|
214
|
+
score > 60 ? "链接或转化请求出现得太早。" : "CTA 缺失、靠后或压力较低。",
|
|
215
|
+
processEvidence: (score) =>
|
|
216
|
+
score > 60 ? "帖子没有展示足够真实过程。" : "草稿已经包含一些过程或学习信号。",
|
|
217
|
+
communityTone: (score) =>
|
|
218
|
+
score > 60 ? "语气更像公告或营销文案,而不是社区讨论。" : "语气基本接近同侪讨论。",
|
|
219
|
+
selfPromotion: (score) =>
|
|
220
|
+
score > 60 ? "产品名、链接和卖点形成了明显自我推广面。" : "产品露出目前比较可控。",
|
|
221
|
+
transparency: (score) =>
|
|
222
|
+
score > 60 ? "草稿没有清楚说明你与产品的关系。" : "身份关系基本清楚。",
|
|
223
|
+
},
|
|
224
|
+
flagReasons: {
|
|
225
|
+
link: "包含明显链接或域名",
|
|
226
|
+
cta: "包含转化型 CTA",
|
|
227
|
+
promo: "使用了推广或卖点表达",
|
|
228
|
+
missingProcess: "提到产品但缺少过程证据",
|
|
229
|
+
transparency: "需要更清楚说明身份关系",
|
|
230
|
+
},
|
|
231
|
+
flagSuggestion: "把这段改成发生了什么、学到了什么、希望社区反馈什么。",
|
|
232
|
+
missing: {
|
|
233
|
+
process: "补一个你实际尝试过的步骤。",
|
|
234
|
+
numbers: "如果真实,补一个小数字、样本量或时间窗口。",
|
|
235
|
+
audience: "说明这篇帖子写给谁,但不要写成销售人群标签。",
|
|
236
|
+
tradeoff: "补一个取舍、失败尝试或开放问题。",
|
|
237
|
+
},
|
|
238
|
+
revisionPlan: {
|
|
239
|
+
title: "把标题从广告式卖点改成具体社区问题。",
|
|
240
|
+
opening: "第一段先交代背景,不要先推产品。",
|
|
241
|
+
process: "补充真实过程:尝试、限制、证据和变化。",
|
|
242
|
+
cta: "把 CTA 移到评论区,或先完全移除。",
|
|
243
|
+
disclosure: "清楚说明你是否是产品创建者、团队成员或利益相关方。",
|
|
244
|
+
},
|
|
245
|
+
checklist: [
|
|
246
|
+
"不点链接,版主也能理解这篇帖子的价值吗?",
|
|
247
|
+
"正文里学习过程是否多于产品主张?",
|
|
248
|
+
"CTA 是否可选、靠后,或只在评论区?",
|
|
249
|
+
"去掉产品名后,这篇帖子是否仍然有用?",
|
|
250
|
+
],
|
|
251
|
+
},
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const promoPatterns = [
|
|
255
|
+
/\b(best|ultimate|revolutionary|game[-\s]?changing|all[-\s]?in[-\s]?one|#1|10x|viral|growth hack|save hours|scale faster|ai-powered)\b/i,
|
|
256
|
+
/\b(launch|launched|introducing|free trial|early access|waitlist|book a demo|sign up|subscribe|download|try it)\b/i,
|
|
257
|
+
/(最强|最好|革命性|颠覆|全能|一站式|爆款|增长神器|节省.*时间|免费试用|立即注册|预约演示|加入等待名单)/i,
|
|
258
|
+
];
|
|
259
|
+
|
|
260
|
+
const ctaPatterns = [
|
|
261
|
+
/\b(sign up|join|try|download|subscribe|book a demo|get early access|waitlist|dm me|check it out|give it a try)\b/i,
|
|
262
|
+
/(注册|试用|下载|预约|加入|私信我|点链接|看看这个|欢迎体验|加入等待名单)/i,
|
|
263
|
+
];
|
|
264
|
+
|
|
265
|
+
const processPatterns = [
|
|
266
|
+
/\b(i tried|we tried|what worked|what failed|learned|mistake|tradeoff|constraint|before|after|tested|interviewed|users told us|we found|not sure)\b/i,
|
|
267
|
+
/(我试了|我们试了|失败|学到|复盘|取舍|限制|之前|之后|测试|访谈|用户说|不确定|踩坑|数据)/i,
|
|
268
|
+
];
|
|
269
|
+
|
|
270
|
+
const disclosurePatterns = [
|
|
271
|
+
/\b(i built|we built|my product|our product|i'm building|we're building|i work on|founder|maker|affiliated)\b/i,
|
|
272
|
+
/(我做的|我们做的|我的产品|我们的产品|我在做|我们在做|创始人|开发者|利益相关|自家产品)/i,
|
|
273
|
+
];
|
|
274
|
+
|
|
275
|
+
const productPatterns = [
|
|
276
|
+
/\b(product|app|tool|platform|saas|startup|users|customers|mrr|beta|launch)\b/i,
|
|
277
|
+
/(产品|工具|平台|应用|SaaS|用户|客户|内测|发布|上线)/i,
|
|
278
|
+
];
|
|
279
|
+
|
|
280
|
+
const linkPattern =
|
|
281
|
+
/(https?:\/\/|www\.|[\w-]+\.(?:com|io|ai|app|dev|co|net|org)\b)/i;
|
|
282
|
+
|
|
283
|
+
function clampScore(score: number) {
|
|
284
|
+
return Math.max(0, Math.min(100, Math.round(score)));
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function scoreToStatus(score: number): CommunityPostRiskStatus {
|
|
288
|
+
if (score >= 62) return "risk";
|
|
289
|
+
if (score >= 34) return "watch";
|
|
290
|
+
return "pass";
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function scoreToLevel(score: number): CommunityPostRiskLevel {
|
|
294
|
+
if (score >= 68) return "high";
|
|
295
|
+
if (score >= 36) return "medium";
|
|
296
|
+
return "low";
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function countSignals(text: string, patterns: RegExp[]) {
|
|
300
|
+
return patterns.reduce((total, pattern) => total + (pattern.test(text) ? 1 : 0), 0);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function splitParagraphs(value: string) {
|
|
304
|
+
return value
|
|
305
|
+
.split(/\n{2,}/)
|
|
306
|
+
.map((item) => item.trim())
|
|
307
|
+
.filter(Boolean);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function hasEarlyCta(paragraphs: string[]) {
|
|
311
|
+
const earlyLimit = Math.max(0, Math.floor(paragraphs.length * 0.35));
|
|
312
|
+
return paragraphs.some((paragraph, index) => {
|
|
313
|
+
if (index > earlyLimit) return false;
|
|
314
|
+
return ctaPatterns.some((pattern) => pattern.test(paragraph)) || linkPattern.test(paragraph);
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function getCtaPlacement({
|
|
319
|
+
community,
|
|
320
|
+
ctaPlan,
|
|
321
|
+
riskScore,
|
|
322
|
+
linkIncluded,
|
|
323
|
+
goal,
|
|
324
|
+
}: {
|
|
325
|
+
community: CommunityPostCommunity;
|
|
326
|
+
ctaPlan: CommunityPostCtaPlan;
|
|
327
|
+
riskScore: number;
|
|
328
|
+
linkIncluded: boolean;
|
|
329
|
+
goal: CommunityPostGoal;
|
|
330
|
+
}): keyof CopyPack["cta"] {
|
|
331
|
+
const isReddit = community === "redditSaas" || community === "redditIndieHackers";
|
|
332
|
+
if (riskScore >= 68) return "onlyIfAsked";
|
|
333
|
+
if (ctaPlan === "none") return "none";
|
|
334
|
+
if (isReddit && (linkIncluded || ctaPlan === "body")) return "comment";
|
|
335
|
+
if (goal === "askAdvice" || goal === "shareLearning") return "none";
|
|
336
|
+
if (community === "indieHackers" && riskScore < 52) return "bodyAfterContext";
|
|
337
|
+
return "comment";
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function makeCheck(
|
|
341
|
+
language: CommunityPostLanguage,
|
|
342
|
+
id: CommunityPostCheckId,
|
|
343
|
+
score: number,
|
|
344
|
+
): CommunityPostRiskCheck {
|
|
345
|
+
const pack = copy[language];
|
|
346
|
+
const cleanScore = clampScore(score);
|
|
347
|
+
return {
|
|
348
|
+
id,
|
|
349
|
+
label: pack.checks[id],
|
|
350
|
+
status: scoreToStatus(cleanScore),
|
|
351
|
+
score: cleanScore,
|
|
352
|
+
reason: pack.reasons[id](cleanScore),
|
|
353
|
+
fix: pack.fixes[id],
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function buildFlaggedParagraphs(
|
|
358
|
+
brief: CommunityPostRiskBrief,
|
|
359
|
+
paragraphs: string[],
|
|
360
|
+
processSignalCount: number,
|
|
361
|
+
) {
|
|
362
|
+
const pack = copy[brief.language];
|
|
363
|
+
|
|
364
|
+
return paragraphs
|
|
365
|
+
.map((paragraph, index) => {
|
|
366
|
+
const reasons: string[] = [];
|
|
367
|
+
let score = 0;
|
|
368
|
+
|
|
369
|
+
if (linkPattern.test(paragraph)) {
|
|
370
|
+
score += 34;
|
|
371
|
+
reasons.push(pack.flagReasons.link);
|
|
372
|
+
}
|
|
373
|
+
if (countSignals(paragraph, ctaPatterns) > 0) {
|
|
374
|
+
score += 30;
|
|
375
|
+
reasons.push(pack.flagReasons.cta);
|
|
376
|
+
}
|
|
377
|
+
if (countSignals(paragraph, promoPatterns) > 0) {
|
|
378
|
+
score += 26;
|
|
379
|
+
reasons.push(pack.flagReasons.promo);
|
|
380
|
+
}
|
|
381
|
+
if (
|
|
382
|
+
processSignalCount < 2 &&
|
|
383
|
+
countSignals(paragraph, productPatterns) > 0 &&
|
|
384
|
+
countSignals(paragraph, processPatterns) === 0
|
|
385
|
+
) {
|
|
386
|
+
score += 22;
|
|
387
|
+
reasons.push(pack.flagReasons.missingProcess);
|
|
388
|
+
}
|
|
389
|
+
if (
|
|
390
|
+
(brief.ctaPlan === "body" || brief.linkIncluded) &&
|
|
391
|
+
countSignals(paragraph, disclosurePatterns) === 0 &&
|
|
392
|
+
index === 0
|
|
393
|
+
) {
|
|
394
|
+
score += 14;
|
|
395
|
+
reasons.push(pack.flagReasons.transparency);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (score < 30) return null;
|
|
399
|
+
|
|
400
|
+
return {
|
|
401
|
+
index: index + 1,
|
|
402
|
+
riskLevel: scoreToLevel(score),
|
|
403
|
+
text: paragraph.length > 260 ? `${paragraph.slice(0, 257)}...` : paragraph,
|
|
404
|
+
reason: reasons.join("; "),
|
|
405
|
+
suggestion: pack.flagSuggestion,
|
|
406
|
+
};
|
|
407
|
+
})
|
|
408
|
+
.filter((item): item is NonNullable<typeof item> => item !== null)
|
|
409
|
+
.slice(0, 5);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
export function createCommunityPostRiskReport(
|
|
413
|
+
brief: CommunityPostRiskBrief,
|
|
414
|
+
): CommunityPostRiskReport {
|
|
415
|
+
const paragraphs = splitParagraphs(brief.postBody);
|
|
416
|
+
const combined = `${brief.title}\n${brief.postBody}`;
|
|
417
|
+
const titlePromoSignals = countSignals(brief.title, promoPatterns);
|
|
418
|
+
const bodyPromoSignals = countSignals(brief.postBody, promoPatterns);
|
|
419
|
+
const ctaSignals = countSignals(brief.postBody, ctaPatterns);
|
|
420
|
+
const processSignalCount = countSignals(combined, processPatterns);
|
|
421
|
+
const disclosureSignalCount = countSignals(combined, disclosurePatterns);
|
|
422
|
+
const productSignalCount = countSignals(combined, productPatterns);
|
|
423
|
+
const strictCommunityBonus =
|
|
424
|
+
brief.community === "redditSaas" || brief.community === "redditIndieHackers"
|
|
425
|
+
? 10
|
|
426
|
+
: 0;
|
|
427
|
+
const earlyCta = hasEarlyCta(paragraphs);
|
|
428
|
+
const linkIncluded = brief.linkIncluded || linkPattern.test(combined);
|
|
429
|
+
|
|
430
|
+
const titleScore = clampScore(
|
|
431
|
+
titlePromoSignals * 28 +
|
|
432
|
+
(brief.title.length > 95 ? 16 : 0) +
|
|
433
|
+
(/[!!]{1,}/.test(brief.title) ? 10 : 0),
|
|
434
|
+
);
|
|
435
|
+
const ctaScore = clampScore(
|
|
436
|
+
(brief.ctaPlan === "body" ? 34 : 0) +
|
|
437
|
+
(brief.ctaPlan === "unsure" ? 12 : 0) +
|
|
438
|
+
(brief.ctaPlan === "comment" ? 8 : 0) +
|
|
439
|
+
(linkIncluded ? 22 : 0) +
|
|
440
|
+
(earlyCta ? 34 : 0) +
|
|
441
|
+
strictCommunityBonus,
|
|
442
|
+
);
|
|
443
|
+
const processScore = clampScore(
|
|
444
|
+
76 - processSignalCount * 18 + (paragraphs.length < 3 ? 10 : 0),
|
|
445
|
+
);
|
|
446
|
+
const toneScore = clampScore(
|
|
447
|
+
bodyPromoSignals * 18 +
|
|
448
|
+
ctaSignals * 12 +
|
|
449
|
+
strictCommunityBonus +
|
|
450
|
+
(processSignalCount === 0 ? 18 : 0) +
|
|
451
|
+
((combined.match(/[!!]/g)?.length ?? 0) > 2 ? 10 : 0),
|
|
452
|
+
);
|
|
453
|
+
const selfPromotionScore = clampScore(
|
|
454
|
+
productSignalCount * 10 +
|
|
455
|
+
bodyPromoSignals * 16 +
|
|
456
|
+
ctaSignals * 16 +
|
|
457
|
+
(linkIncluded ? 22 : 0) +
|
|
458
|
+
strictCommunityBonus,
|
|
459
|
+
);
|
|
460
|
+
const transparencyScore = clampScore(
|
|
461
|
+
productSignalCount > 0 || linkIncluded || brief.ctaPlan !== "none"
|
|
462
|
+
? 66 - disclosureSignalCount * 30 + strictCommunityBonus
|
|
463
|
+
: 12,
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
const checks = [
|
|
467
|
+
makeCheck(brief.language, "titlePromotion", titleScore),
|
|
468
|
+
makeCheck(brief.language, "ctaTiming", ctaScore),
|
|
469
|
+
makeCheck(brief.language, "processEvidence", processScore),
|
|
470
|
+
makeCheck(brief.language, "communityTone", toneScore),
|
|
471
|
+
makeCheck(brief.language, "selfPromotion", selfPromotionScore),
|
|
472
|
+
makeCheck(brief.language, "transparency", transparencyScore),
|
|
473
|
+
];
|
|
474
|
+
|
|
475
|
+
const riskScore = clampScore(
|
|
476
|
+
checks.reduce((total, check) => total + check.score, 0) / checks.length,
|
|
477
|
+
);
|
|
478
|
+
const riskLevel = scoreToLevel(riskScore);
|
|
479
|
+
const ctaKey = getCtaPlacement({
|
|
480
|
+
community: brief.community,
|
|
481
|
+
ctaPlan: brief.ctaPlan,
|
|
482
|
+
riskScore,
|
|
483
|
+
linkIncluded,
|
|
484
|
+
goal: brief.goal,
|
|
485
|
+
});
|
|
486
|
+
const pack = copy[brief.language];
|
|
487
|
+
|
|
488
|
+
return {
|
|
489
|
+
summary: {
|
|
490
|
+
riskScore,
|
|
491
|
+
riskLevel,
|
|
492
|
+
verdict: pack.verdict[riskLevel],
|
|
493
|
+
primaryFix: pack.primaryFix[riskLevel],
|
|
494
|
+
recommendedCtaPlacement: pack.cta[ctaKey].placement,
|
|
495
|
+
},
|
|
496
|
+
checks,
|
|
497
|
+
flaggedParagraphs: buildFlaggedParagraphs(brief, paragraphs, processSignalCount),
|
|
498
|
+
ctaAdvice: pack.cta[ctaKey],
|
|
499
|
+
revisionPlan: [
|
|
500
|
+
pack.revisionPlan.title,
|
|
501
|
+
pack.revisionPlan.opening,
|
|
502
|
+
pack.revisionPlan.process,
|
|
503
|
+
pack.revisionPlan.cta,
|
|
504
|
+
pack.revisionPlan.disclosure,
|
|
505
|
+
],
|
|
506
|
+
missingInfo: [
|
|
507
|
+
...(processSignalCount < 2 ? [pack.missing.process] : []),
|
|
508
|
+
...(processSignalCount < 3 ? [pack.missing.tradeoff] : []),
|
|
509
|
+
...(productSignalCount > 0 ? [pack.missing.audience] : []),
|
|
510
|
+
...(bodyPromoSignals > 0 ? [pack.missing.numbers] : []),
|
|
511
|
+
].slice(0, 4),
|
|
512
|
+
reviewChecklist: pack.checklist,
|
|
513
|
+
metadata: {
|
|
514
|
+
community: brief.community,
|
|
515
|
+
goal: brief.goal,
|
|
516
|
+
paragraphCount: paragraphs.length,
|
|
517
|
+
linkIncluded,
|
|
518
|
+
language: brief.language,
|
|
519
|
+
},
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
export function stringifyCommunityPostRiskReport(report: CommunityPostRiskReport) {
|
|
524
|
+
const lines = [
|
|
525
|
+
"# Community Post Risk Checker",
|
|
526
|
+
"",
|
|
527
|
+
`Risk score: ${report.summary.riskScore}/100`,
|
|
528
|
+
`Risk level: ${report.summary.riskLevel}`,
|
|
529
|
+
`Recommended CTA: ${report.summary.recommendedCtaPlacement}`,
|
|
530
|
+
"",
|
|
531
|
+
report.summary.verdict,
|
|
532
|
+
"",
|
|
533
|
+
"## Checks",
|
|
534
|
+
...report.checks.map(
|
|
535
|
+
(check) =>
|
|
536
|
+
`- ${check.label}: ${check.score}/100 (${check.status}) - ${check.fix}`,
|
|
537
|
+
),
|
|
538
|
+
"",
|
|
539
|
+
"## Flagged paragraphs",
|
|
540
|
+
...(report.flaggedParagraphs.length
|
|
541
|
+
? report.flaggedParagraphs.map(
|
|
542
|
+
(item) =>
|
|
543
|
+
`- Paragraph ${item.index}: ${item.reason}. ${item.suggestion}`,
|
|
544
|
+
)
|
|
545
|
+
: ["- None flagged in this pass."]),
|
|
546
|
+
"",
|
|
547
|
+
"## CTA advice",
|
|
548
|
+
`- ${report.ctaAdvice.placement}`,
|
|
549
|
+
`- ${report.ctaAdvice.rationale}`,
|
|
550
|
+
`- ${report.ctaAdvice.saferAlternative}`,
|
|
551
|
+
"",
|
|
552
|
+
"## Revision plan",
|
|
553
|
+
...report.revisionPlan.map((item) => `- ${item}`),
|
|
554
|
+
];
|
|
555
|
+
|
|
556
|
+
return lines.join("\n");
|
|
557
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/** resultType written back by the Skills-OL competitor-analysis skill. */
|
|
4
|
+
export const COMPETITOR_ANALYSIS_RESULT_TYPE = "competitor_analysis";
|
|
5
|
+
|
|
6
|
+
/** Scene passed to the LLM task (backend treats scene as metadata). */
|
|
7
|
+
export const COMPETITOR_ANALYSIS_SCENE = "competitor_analysis";
|
|
8
|
+
|
|
9
|
+
export const COMPETITOR_ANALYSIS_PROMPT_VERSION = "competitor-analysis.v1";
|
|
10
|
+
|
|
11
|
+
// ── Input ────────────────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
export const competitorAnalysisInputSchema = z.object({
|
|
14
|
+
competitorUrl: z
|
|
15
|
+
.string()
|
|
16
|
+
.trim()
|
|
17
|
+
.min(1)
|
|
18
|
+
.url()
|
|
19
|
+
.transform((value) => value.replace(/\/$/, "")),
|
|
20
|
+
competitorName: z.string().trim().max(120).optional(),
|
|
21
|
+
notes: z.string().trim().max(600).optional(),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export type CompetitorAnalysisInput = z.infer<typeof competitorAnalysisInputSchema>;
|
|
25
|
+
|
|
26
|
+
// ── Result (tolerant: estimation output may be partial) ────────────────────
|
|
27
|
+
|
|
28
|
+
const confidence = z.enum(["low", "medium", "high"]).catch("low");
|
|
29
|
+
const trend = z.enum(["growing", "flat", "declining", "unknown"]).catch("unknown");
|
|
30
|
+
|
|
31
|
+
const trendPoint = z.object({
|
|
32
|
+
period: z.string(),
|
|
33
|
+
value: z.number().catch(0),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const sharePoint = z.object({
|
|
37
|
+
name: z.string(),
|
|
38
|
+
/** 0-100 percentage when known. */
|
|
39
|
+
share: z.number().optional(),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const person = z.object({
|
|
43
|
+
name: z.string(),
|
|
44
|
+
role: z.string().optional(),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const overviewSchema = z
|
|
48
|
+
.object({
|
|
49
|
+
name: z.string().optional(),
|
|
50
|
+
url: z.string().optional(),
|
|
51
|
+
tagline: z.string().optional(),
|
|
52
|
+
description: z.string().optional(),
|
|
53
|
+
category: z.string().optional(),
|
|
54
|
+
foundedYear: z.string().optional(),
|
|
55
|
+
headquarters: z.string().optional(),
|
|
56
|
+
businessModel: z.string().optional(),
|
|
57
|
+
})
|
|
58
|
+
.default({});
|
|
59
|
+
|
|
60
|
+
const trafficSchema = z
|
|
61
|
+
.object({
|
|
62
|
+
summary: z.string().optional(),
|
|
63
|
+
monthlyVisitsEstimate: z.string().optional(),
|
|
64
|
+
trend,
|
|
65
|
+
trendSeries: z.array(trendPoint).default([]),
|
|
66
|
+
channels: z.array(sharePoint).default([]),
|
|
67
|
+
geographies: z.array(sharePoint).default([]),
|
|
68
|
+
confidence,
|
|
69
|
+
})
|
|
70
|
+
.default({ trend: "unknown", trendSeries: [], channels: [], geographies: [], confidence: "low" });
|
|
71
|
+
|
|
72
|
+
const businessScaleSchema = z
|
|
73
|
+
.object({
|
|
74
|
+
summary: z.string().optional(),
|
|
75
|
+
pricingModel: z.string().optional(),
|
|
76
|
+
priceRange: z.string().optional(),
|
|
77
|
+
estimatedRevenue: z.string().optional(),
|
|
78
|
+
employeeRange: z.string().optional(),
|
|
79
|
+
customerSegments: z.array(z.string()).default([]),
|
|
80
|
+
notableCustomers: z.array(z.string()).default([]),
|
|
81
|
+
confidence,
|
|
82
|
+
})
|
|
83
|
+
.default({ customerSegments: [], notableCustomers: [], confidence: "low" });
|
|
84
|
+
|
|
85
|
+
const teamSchema = z
|
|
86
|
+
.object({
|
|
87
|
+
summary: z.string().optional(),
|
|
88
|
+
teamSize: z.string().optional(),
|
|
89
|
+
founders: z.array(person).default([]),
|
|
90
|
+
keyPeople: z.array(person).default([]),
|
|
91
|
+
confidence,
|
|
92
|
+
})
|
|
93
|
+
.default({ founders: [], keyPeople: [], confidence: "low" });
|
|
94
|
+
|
|
95
|
+
const fundingSchema = z
|
|
96
|
+
.object({
|
|
97
|
+
summary: z.string().optional(),
|
|
98
|
+
totalRaised: z.string().optional(),
|
|
99
|
+
lastRound: z
|
|
100
|
+
.object({
|
|
101
|
+
stage: z.string().optional(),
|
|
102
|
+
amount: z.string().optional(),
|
|
103
|
+
date: z.string().optional(),
|
|
104
|
+
leadInvestors: z.array(z.string()).default([]),
|
|
105
|
+
})
|
|
106
|
+
.optional(),
|
|
107
|
+
investors: z.array(z.string()).default([]),
|
|
108
|
+
confidence,
|
|
109
|
+
})
|
|
110
|
+
.default({ investors: [], confidence: "low" });
|
|
111
|
+
|
|
112
|
+
const positioningSchema = z
|
|
113
|
+
.object({
|
|
114
|
+
strengths: z.array(z.string()).default([]),
|
|
115
|
+
weaknesses: z.array(z.string()).default([]),
|
|
116
|
+
opportunities: z.array(z.string()).default([]),
|
|
117
|
+
})
|
|
118
|
+
.default({ strengths: [], weaknesses: [], opportunities: [] });
|
|
119
|
+
|
|
120
|
+
const sourceSchema = z.object({
|
|
121
|
+
label: z.string(),
|
|
122
|
+
url: z.string().optional(),
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
export const competitorAnalysisResultSchema = z.object({
|
|
126
|
+
overview: overviewSchema,
|
|
127
|
+
traffic: trafficSchema,
|
|
128
|
+
businessScale: businessScaleSchema,
|
|
129
|
+
team: teamSchema,
|
|
130
|
+
funding: fundingSchema,
|
|
131
|
+
positioning: positioningSchema,
|
|
132
|
+
highlights: z.array(z.string()).default([]),
|
|
133
|
+
sources: z.array(sourceSchema).default([]),
|
|
134
|
+
confidence,
|
|
135
|
+
summary: z.string(),
|
|
136
|
+
disclaimer: z.string().default(""),
|
|
137
|
+
promptVersion: z.string().default(COMPETITOR_ANALYSIS_PROMPT_VERSION),
|
|
138
|
+
generatedAt: z.string().optional(),
|
|
139
|
+
sourceUrl: z.string().optional(),
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
export type CompetitorAnalysisResult = z.infer<typeof competitorAnalysisResultSchema>;
|
|
143
|
+
export type CompetitorConfidence = z.infer<typeof confidence>;
|
|
144
|
+
export type CompetitorTrafficTrend = z.infer<typeof trend>;
|