codymaster 7.0.1 โ 7.0.3
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/CHANGELOG.md +64 -0
- package/README.md +117 -2
- package/dist/agent/codex.js +73 -21
- package/dist/agent-dispatch.js +63 -48
- package/dist/cli/commands/brain.js +18 -0
- package/dist/cli/commands/design-studio.js +1 -1
- package/dist/cm-suggest.js +3 -3
- package/dist/dashboard-project-summary.js +9 -0
- package/dist/dashboard.js +11 -5
- package/dist/execution-analyzer.js +9 -1
- package/dist/judge.js +16 -15
- package/dist/mcp-context-server.js +45 -23
- package/dist/mcp-skills-tools.js +2 -2
- package/dist/skill-chain.js +26 -3
- package/dist/skill-token-report.js +105 -0
- package/dist/sprint-pipeline.js +3 -3
- package/dist/ui/onboarding.js +3 -4
- package/dist/utils/design-taste.js +1 -1
- package/dist/utils/output-compress.js +8 -0
- package/package.json +3 -2
- package/public/dashboard/app.js +40 -13
- package/public/dashboard/index.html +190 -5
- package/public/dashboard/style.css +1 -1
- package/scripts/build-skills.mjs +36 -2
- package/scripts/mcp-bridge.js +41 -24
- package/scripts/pack-plugin.mjs +206 -0
- package/skills/cm-ads-tracker/SKILL.md +401 -0
- package/skills/cm-ads-tracker/evals/evals.json +55 -0
- package/skills/cm-ads-tracker/references/gtm-architecture.md +321 -0
- package/skills/cm-ads-tracker/references/industry-events.md +294 -0
- package/skills/cm-ads-tracker/references/platforms-api.md +238 -0
- package/skills/cm-ads-tracker/templates/capi-payload.md +79 -0
- package/skills/cm-ads-tracker/templates/datalayer-push.js +104 -0
- package/skills/cm-ads-tracker/templates/gtm-variables.js +56 -0
- package/skills/cm-auto-publisher/SKILL.md +81 -0
- package/skills/cm-booking-calendar/SKILL.md +521 -0
- package/skills/cm-booking-calendar/references/industry-patterns.md +527 -0
- package/skills/cm-booking-calendar/templates/booking-form.css +626 -0
- package/skills/cm-booking-calendar/templates/booking-form.html +477 -0
- package/skills/cm-booking-calendar/templates/calendar-engine.js +419 -0
- package/skills/cm-booking-calendar/templates/calendar-export.js +395 -0
- package/skills/cm-booking-calendar/templates/reminder-config.js +629 -0
- package/skills/cm-brainstorm-idea/SKILL.md +5 -5
- package/skills/cm-code-review/SKILL.md +2 -2
- package/skills/cm-codeintell/SKILL.md +47 -580
- package/skills/cm-codeintell/references/integration-workflows.md +23 -0
- package/skills/cm-codeintell/references/layer-0-skeleton.md +54 -0
- package/skills/cm-codeintell/references/layer-1-codegraph.md +58 -0
- package/skills/cm-codeintell/references/layer-2-architecture.md +31 -0
- package/skills/cm-codeintell/references/layer-3-context-builder.md +32 -0
- package/skills/cm-content-factory/.content-factory-state.json +132 -0
- package/skills/cm-content-factory/.git 2/logs/refs/heads/main +1 -0
- package/skills/cm-content-factory/.git 2/logs/refs/remotes/origin/main +1 -0
- package/skills/cm-content-factory/.git 2/objects/02/fb0956734b5f8ba3f918b7defd04a89cfe0076 +0 -0
- package/skills/cm-content-factory/.git 2/objects/08/1e129d75dc6feac6c02037272e6bd1a04e3324 +0 -0
- package/skills/cm-content-factory/.git 2/objects/0c/5393416f3c5e01c9a655a802bff0dd52f76f0a +0 -0
- package/skills/cm-content-factory/.git 2/objects/10/0b9be46978a946a77188f68be725098a122001 +0 -0
- package/skills/cm-content-factory/.git 2/objects/10/cf041167fc9843610eb3d90259ef3396315fdc +0 -0
- package/skills/cm-content-factory/.git 2/objects/12/5e19538dd6e1338ffe74f6c4c165b00435bf48 +0 -0
- package/skills/cm-content-factory/.git 2/objects/16/a9b9d0088d5c1347628b45a2620b479d8ad57c +0 -0
- package/skills/cm-content-factory/.git 2/objects/17/8c2a9ef93c33ae4eec9d58e82321f9229843a1 +0 -0
- package/skills/cm-content-factory/.git 2/objects/25/397ae41d09104d763bdcac2695209d85cdea89 +0 -0
- package/skills/cm-content-factory/.git 2/objects/2f/a836b7947f2d458e1f639788bf4bb0983a3305 +0 -0
- package/skills/cm-content-factory/.git 2/objects/3a/baaaf0a1c0909c0828335791557125fba911e0 +0 -0
- package/skills/cm-content-factory/.git 2/objects/42/2924221b81f5ce3c4e4daac9a64a24f9b01f9a +0 -0
- package/skills/cm-content-factory/.git 2/objects/42/ec0ce707447dc11446a34c9995fb8533801731 +0 -0
- package/skills/cm-content-factory/.git 2/objects/46/e43ce92866d56ce74b1d750db307cfe6154a15 +0 -0
- package/skills/cm-content-factory/.git 2/objects/48/5e41b633c63f55b8277bcc59f44f67681f671a +0 -0
- package/skills/cm-content-factory/.git 2/objects/49/49c596a3a89fa240642acd95dd3258e261eb09 +0 -0
- package/skills/cm-content-factory/.git 2/objects/50/9d42d8412ef8eaf7f7e138476bac2e4d10ce60 +0 -0
- package/skills/cm-content-factory/.git 2/objects/55/0c8c389d981b463ef849aeb792d8be3ccb6ec8 +0 -0
- package/skills/cm-content-factory/.git 2/objects/5d/82d3b18410cdda3ace3677436f0cb599dbe2d2 +0 -0
- package/skills/cm-content-factory/.git 2/objects/60/0617c58e871a38b33bf29e282d132bb3c381ad +0 -0
- package/skills/cm-content-factory/.git 2/objects/6a/8369a99c687b7245c92ffaf0e0f0dab9014504 +0 -0
- package/skills/cm-content-factory/.git 2/objects/79/bea435d40ab531c1aaf6be0432c6a5b7aaed21 +0 -0
- package/skills/cm-content-factory/.git 2/objects/7e/5ebd79251c2f14e4aceb86c74b6b6daae6b500 +0 -0
- package/skills/cm-content-factory/.git 2/objects/81/98a822a60178d6d5023ddb3e222cddf048742e +0 -0
- package/skills/cm-content-factory/.git 2/objects/86/0a0e1943dfe53411d2e499a1f16f46a96ef758 +0 -0
- package/skills/cm-content-factory/.git 2/objects/86/971fb55fdc081fdbae52376f0f13e57a4e9b04 +0 -0
- package/skills/cm-content-factory/.git 2/objects/88/b89dd609a0a03f8d4fe8bfde20d5b8fc1d326d +0 -0
- package/skills/cm-content-factory/.git 2/objects/90/8737edb6b7809e32cc01590b4e08ba42a9d40d +0 -0
- package/skills/cm-content-factory/.git 2/objects/93/d5a8a9a7d4fb7f11491cb596a6880528725118 +0 -0
- package/skills/cm-content-factory/.git 2/objects/98/46a2ab81d0c3b3eb00ef88fc56989aa7e9f316 +0 -0
- package/skills/cm-content-factory/.git 2/objects/9b/d8dd1e49cf274eaf9c555f3ab39dce7af5715e +0 -0
- package/skills/cm-content-factory/.git 2/objects/a1/13329fb0cec96ae78b222d33a24c3b5bc7fa1f +0 -0
- package/skills/cm-content-factory/.git 2/objects/a9/e6effe626e8a3aea3a8fc3364b492191c6e7d0 +0 -0
- package/skills/cm-content-factory/.git 2/objects/ad/6de7e48d9782cca9353d1ff0aa1aab7fe1df85 +0 -0
- package/skills/cm-content-factory/.git 2/objects/af/54ae316f771ff692e299ffcd8bf2f06b413b59 +0 -0
- package/skills/cm-content-factory/.git 2/objects/b0/4cb8b0b00dad633e731c1472161419e738d674 +0 -0
- package/skills/cm-content-factory/.git 2/objects/b3/094abb0b9ed46419b269e4a4e36a459690e3b0 +0 -0
- package/skills/cm-content-factory/.git 2/objects/b9/435c5d4baac2cfc5c83009ddd27b46b60db5f1 +0 -0
- package/skills/cm-content-factory/.git 2/objects/ba/5da17dbaec5ec2dcfdfd126aead518d1171d5c +0 -0
- package/skills/cm-content-factory/.git 2/objects/c0/bf58703aa258ba5dd63083bebaec8f223d844c +0 -0
- package/skills/cm-content-factory/.git 2/objects/c4/701a34edf1fc1bad58ccc57bd03f9426acb59a +0 -0
- package/skills/cm-content-factory/.git 2/objects/c7/5ccce9a4e5cc74d9b3174550cf6d993ca43638 +0 -0
- package/skills/cm-content-factory/.git 2/objects/c7/710d59b5a35b0f1f0a0399386643a0bd94c929 +0 -0
- package/skills/cm-content-factory/.git 2/objects/d1/fe58237112e953e5fec52da22cf38e08be3df9 +5 -0
- package/skills/cm-content-factory/.git 2/objects/d2/2bbe9fd2f74c95bc5583e803f5e435f1e2cd86 +0 -0
- package/skills/cm-content-factory/.git 2/objects/d7/e72852ea2bff74581dbf247d400120086229f4 +0 -0
- package/skills/cm-content-factory/.git 2/objects/d8/d4c3b5553e4fd72807e1d4b49ef07d9ef3ac35 +0 -0
- package/skills/cm-content-factory/.git 2/objects/dc/75050c2876f6a02ae2a53a3c886f395b622977 +0 -0
- package/skills/cm-content-factory/.git 2/objects/ee/e8546f95acec500187c08a28a8b9ee02db0dec +0 -0
- package/skills/cm-content-factory/.git 2/objects/ef/263c059208b416c2146434f10cb2b9fabcba16 +0 -0
- package/skills/cm-content-factory/.git 2/objects/f3/ae597e84d9a59b88acd21c99bde2eaf686d785 +0 -0
- package/skills/cm-content-factory/.git 2/objects/f3/f6f5673c821d3d8e76fa267a9e882e7a5387ea +0 -0
- package/skills/cm-content-factory/.git 2/objects/f9/6e6d0ad02624dd11d5848594d056caef7a5e8b +0 -0
- package/skills/cm-content-factory/.git 2/objects/ff/278988fc1edf0db3abcf18de795f4cc0b4f3e1 +0 -0
- package/skills/cm-content-factory/.git 2/refs/heads/main +1 -0
- package/skills/cm-content-factory/.git 2/refs/remotes/origin/main +1 -0
- package/skills/cm-content-factory/.pytest_cache 2/v/cache/nodeids +76 -0
- package/skills/cm-content-factory/.pytest_cache 2/v/cache/stepwise +1 -0
- package/skills/cm-content-factory/AGENTS.md +61 -0
- package/skills/cm-content-factory/CLAUDE.md +63 -0
- package/skills/cm-content-factory/CURSOR.md +43 -0
- package/skills/cm-content-factory/Content Factory.zip +0 -0
- package/skills/cm-content-factory/SKILL.md +416 -0
- package/skills/cm-content-factory/cf +313 -0
- package/skills/cm-content-factory/config.schema.json +397 -0
- package/skills/cm-content-factory/dashboard/app.js +556 -0
- package/skills/cm-content-factory/dashboard/index.html +397 -0
- package/skills/cm-content-factory/dashboard/style.css +1211 -0
- package/skills/cm-content-factory/examples/01-real-estate.config.json +146 -0
- package/skills/cm-content-factory/examples/02-personal-finance.config.json +146 -0
- package/skills/cm-content-factory/examples/03-health-wellness.config.json +147 -0
- package/skills/cm-content-factory/examples/04-saas-software.config.json +147 -0
- package/skills/cm-content-factory/examples/05-legal-services.config.json +147 -0
- package/skills/cm-content-factory/examples/06-insurance.config.json +146 -0
- package/skills/cm-content-factory/examples/07-ecommerce-dropship.config.json +146 -0
- package/skills/cm-content-factory/examples/08-online-education.config.json +147 -0
- package/skills/cm-content-factory/examples/09-crypto-defi.config.json +147 -0
- package/skills/cm-content-factory/examples/10-beauty-skincare.config.json +147 -0
- package/skills/cm-content-factory/examples/11-home-services.config.json +146 -0
- package/skills/cm-content-factory/examples/12-dental-clinic.config.json +147 -0
- package/skills/cm-content-factory/examples/13-pet-care.config.json +147 -0
- package/skills/cm-content-factory/examples/14-travel-hospitality.config.json +147 -0
- package/skills/cm-content-factory/examples/15-ai-automation.config.json +147 -0
- package/skills/cm-content-factory/examples/16-wedding-events.config.json +147 -0
- package/skills/cm-content-factory/examples/17-fitness-coaching.config.json +148 -0
- package/skills/cm-content-factory/examples/18-cybersecurity.config.json +147 -0
- package/skills/cm-content-factory/examples/19-food-restaurant.config.json +148 -0
- package/skills/cm-content-factory/examples/20-solar-energy.config.json +147 -0
- package/skills/cm-content-factory/examples/fitness-blog.config.json +116 -0
- package/skills/cm-content-factory/examples/tech-blog.config.json +107 -0
- package/skills/cm-content-factory/extensions/EXTENSION_GUIDE.md +72 -0
- package/skills/cm-content-factory/extensions/hooks.py +126 -0
- package/skills/cm-content-factory/extensions/openclaw_adapter.py +132 -0
- package/skills/cm-content-factory/landing/docs/content/changelog.md +36 -0
- package/skills/cm-content-factory/landing/docs/content/deployment.md +46 -0
- package/skills/cm-content-factory/landing/docs/content/execution-flow.md +67 -0
- package/skills/cm-content-factory/landing/docs/content/openspace.md +27 -0
- package/skills/cm-content-factory/landing/docs/content/openviking.md +33 -0
- package/skills/cm-content-factory/landing/docs/content/use-cases.md +26 -0
- package/skills/cm-content-factory/landing/docs/content/v5-intro.md +28 -0
- package/skills/cm-content-factory/landing/docs/index.html +240 -0
- package/skills/cm-content-factory/landing/index.html +680 -0
- package/skills/cm-content-factory/landing/script.js +143 -0
- package/skills/cm-content-factory/landing/style.css +1216 -0
- package/skills/cm-content-factory/landing/translations.js +508 -0
- package/skills/cm-content-factory/logs/events.jsonl +11 -0
- package/skills/cm-content-factory/profiles/_template.profile.json +231 -0
- package/skills/cm-content-factory/profiles/finance.profile.json +278 -0
- package/skills/cm-content-factory/profiles/legal.profile.json +263 -0
- package/skills/cm-content-factory/profiles/medical-research.profile.json +321 -0
- package/skills/cm-content-factory/profiles/technology.profile.json +275 -0
- package/skills/cm-content-factory/scripts/agent_dispatcher.py +266 -0
- package/skills/cm-content-factory/scripts/audit.py +106 -0
- package/skills/cm-content-factory/scripts/dashboard_server.py +225 -0
- package/skills/cm-content-factory/scripts/deploy.py +146 -0
- package/skills/cm-content-factory/scripts/extract.py +132 -0
- package/skills/cm-content-factory/scripts/landing_generator.py +459 -0
- package/skills/cm-content-factory/scripts/memory.py +521 -0
- package/skills/cm-content-factory/scripts/monetize.py +239 -0
- package/skills/cm-content-factory/scripts/pipeline.py +357 -0
- package/skills/cm-content-factory/scripts/plan.py +163 -0
- package/skills/cm-content-factory/scripts/publish.py +145 -0
- package/skills/cm-content-factory/scripts/research.py +337 -0
- package/skills/cm-content-factory/scripts/scaffold.py +464 -0
- package/skills/cm-content-factory/scripts/scoreboard.py +336 -0
- package/skills/cm-content-factory/scripts/seo.py +90 -0
- package/skills/cm-content-factory/scripts/state_manager.py +320 -0
- package/skills/cm-content-factory/scripts/token_manager.py +268 -0
- package/skills/cm-content-factory/scripts/validate.py +221 -0
- package/skills/cm-content-factory/scripts/wizard.py +329 -0
- package/skills/cm-content-factory/scripts/write.py +93 -0
- package/skills/cm-content-factory/sites/docs-site/src/assets/houston.webp +0 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/architecture.md +90 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/data-flow.md +54 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/deployment.md +38 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/flows/index.md +65 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/flows/lc-content-lifecycle.md +48 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/flows/seq-write-mode.md +39 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/flows/uj-first-batch.md +42 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/flows/wf-content-pipeline.md +51 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/flows/wf-learning-cycle.md +52 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/getting-started/configuration.md +86 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/getting-started/installation.md +80 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/getting-started/intro.md +58 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/index.md +102 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/jtbd/index.md +45 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/jtbd/optimize-seo.md +29 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/jtbd/scale-content-production.md +55 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/jtbd/standardize-quality.md +29 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/personas/buyer-cmo-huong.md +41 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/personas/buyer-content-lead-khoa.md +40 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/personas/index.md +56 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/personas/user-content-manager-lan.md +46 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/personas/user-seo-minh.md +45 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/personas/user-writer-tu.md +45 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/sop/content-pipeline.md +108 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/sop/index.md +22 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/sop/memory-system.md +52 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/sop/seo-optimization.md +58 -0
- package/skills/cm-content-factory/sites/docs-site/src/content/docs/sop/troubleshooting-guide.md +92 -0
- package/skills/cm-content-factory/sites/docs-site/src/styles/custom.css +575 -0
- package/skills/cm-content-factory/tests/conftest.py +66 -0
- package/skills/cm-content-factory/tests/test_agent_dispatcher.py +125 -0
- package/skills/cm-content-factory/tests/test_memory.py +128 -0
- package/skills/cm-content-factory/tests/test_pipeline.py +107 -0
- package/skills/cm-content-factory/tests/test_research.py +56 -0
- package/skills/cm-content-factory/tests/test_state_manager.py +131 -0
- package/skills/cm-content-factory/tests/test_token_manager.py +110 -0
- package/skills/cm-content-factory/tests/test_wizard.py +121 -0
- package/skills/cm-continuity/SKILL.md +49 -480
- package/skills/cm-continuity/references/cm-uri-scheme.md +23 -0
- package/skills/cm-continuity/references/continuity-template.md +48 -0
- package/skills/cm-continuity/references/mcp-context-server.md +27 -0
- package/skills/cm-continuity/references/memory-architecture.md +26 -0
- package/skills/cm-continuity/references/memory-audit.md +18 -0
- package/skills/cm-continuity/references/session-protocol.md +31 -0
- package/skills/cm-continuity/references/storage-formats.md +20 -0
- package/skills/cm-cro-methodology/SKILL.md +290 -0
- package/skills/cm-cro-methodology/references/COPYWRITING.md +178 -0
- package/skills/cm-cro-methodology/references/OBJECTIONS.md +135 -0
- package/skills/cm-cro-methodology/references/PERSUASION.md +158 -0
- package/skills/cm-cro-methodology/references/RESEARCH.md +220 -0
- package/skills/cm-cro-methodology/references/funnel-analysis.md +365 -0
- package/skills/cm-cro-methodology/references/testing-methodology.md +330 -0
- package/skills/cm-design-system/SKILL.md +5 -6
- package/skills/cm-execution/SKILL.md +61 -379
- package/skills/cm-execution/references/mode-a-batch.md +28 -0
- package/skills/cm-execution/references/mode-b-subagent.md +46 -0
- package/skills/cm-execution/references/mode-c-parallel.md +39 -0
- package/skills/cm-execution/references/mode-d-rarv.md +62 -0
- package/skills/cm-execution/references/mode-e-triz-parallel.md +53 -0
- package/skills/cm-execution/references/mode-f-party.md +61 -0
- package/skills/cm-execution/references/persona-dispatch.md +22 -0
- package/skills/cm-execution/references/security-rules.md +47 -0
- package/skills/cm-google-form/SKILL.md +266 -0
- package/skills/cm-google-form/templates/apps-script.js +55 -0
- package/skills/cm-google-form/templates/form-markup.html +110 -0
- package/skills/cm-google-form/templates/form-submit.js +201 -0
- package/skills/cm-google-form/templates/toast.css +152 -0
- package/skills/cm-growth-hacking/SKILL.md +282 -0
- package/skills/cm-growth-hacking/bottom-sheet-engine.md +261 -0
- package/skills/cm-growth-hacking/calendar-integration.md +264 -0
- package/skills/cm-growth-hacking/references/engagement-patterns.md +346 -0
- package/skills/cm-growth-hacking/templates/bottom-sheet.css +528 -0
- package/skills/cm-growth-hacking/templates/bottom-sheet.js +269 -0
- package/skills/cm-growth-hacking/templates/calendar-cta.js +213 -0
- package/skills/cm-growth-hacking/templates/tracking-events.js +211 -0
- package/skills/cm-growth-hacking/templates/trigger-manager.js +254 -0
- package/skills/cm-growth-hacking/tracking-events.md +246 -0
- package/skills/cm-growth-hacking/trigger-system.md +342 -0
- package/skills/cm-how-it-work/SKILL.md +8 -9
- package/skills/cm-identity-guard/SKILL.md +4 -4
- package/skills/cm-jtbd/SKILL.md +98 -0
- package/skills/cm-notebooklm/SKILL.md +156 -0
- package/skills/cm-notebooklm/references/command_reference.md +94 -0
- package/skills/cm-notebooklm/references/workflows.md +60 -0
- package/skills/cm-notebooklm/resources/knowledge_sources.md +106 -0
- package/skills/cm-notebooklm/scripts/brain-sync.sh +453 -0
- package/skills/cm-notebooklm/scripts/graduate_wisdom.py +101 -0
- package/skills/cm-planning/SKILL.md +3 -3
- package/skills/cm-project-bootstrap/SKILL.md +2 -2
- package/skills/cm-quality-gate/SKILL.md +1 -1
- package/skills/cm-readit/SKILL.md +289 -0
- package/skills/cm-readit/audio-player.md +206 -0
- package/skills/cm-readit/examples/blog-reader.js +352 -0
- package/skills/cm-readit/examples/voice-cro.js +390 -0
- package/skills/cm-readit/tts-engine.md +262 -0
- package/skills/cm-readit/ui-patterns.md +362 -0
- package/skills/cm-readit/voice-cro.md +223 -0
- package/skills/cm-safe-deploy/SKILL.md +80 -510
- package/skills/cm-safe-deploy/references/gate-0-5-security-scan.md +31 -0
- package/skills/cm-safe-deploy/references/gate-0-secret-hygiene.md +68 -0
- package/skills/cm-safe-deploy/references/gate-1-syntax.md +23 -0
- package/skills/cm-safe-deploy/references/gate-2-test-suite.md +28 -0
- package/skills/cm-safe-deploy/references/gate-3-i18n.md +19 -0
- package/skills/cm-safe-deploy/references/gate-4-5-build-dist.md +16 -0
- package/skills/cm-safe-deploy/references/gate-6-deploy-smoke.md +18 -0
- package/skills/cm-safe-deploy/references/rollback.md +17 -0
- package/skills/cm-safe-deploy/references/setup-new-project.md +20 -0
- package/skills/cm-skill-index/SKILL.md +15 -15
- package/skills/cm-start/SKILL.md +1 -1
- package/skills/cm-tdd/SKILL.md +51 -356
- package/skills/cm-tdd/references/bugfix-example.md +15 -0
- package/skills/cm-tdd/references/rationalizations.md +20 -0
- package/skills/cm-tdd/references/red-green-refactor.md +33 -0
- package/skills/cm-tdd/references/stuck-debugging.md +18 -0
- package/skills/cm-tdd/references/test-quality.md +19 -0
- package/skills/cm-ux-master/SKILL.md +368 -115
- package/skills/profiles/core.txt +1 -4
- package/skills/profiles/design.txt +1 -2
- package/skills/profiles/full.txt +10 -16
- package/skills/profiles/growth.txt +9 -9
- package/skills/profiles/top35.json +13 -13
- package/skills/cm-conductor-worktrees/SKILL.archive.md +0 -28
- package/skills/cm-conductor-worktrees/SKILL.md +0 -26
- package/skills/cm-dashboard/SKILL.archive.md +0 -15
- package/skills/cm-dashboard/SKILL.md +0 -26
- package/skills/cm-dashboard/ui/app.js +0 -1278
- package/skills/cm-dashboard/ui/index.html +0 -206
- package/skills/cm-dashboard/ui/style.css +0 -440
- package/skills/cm-design-studio/SKILL.archive.md +0 -34
- package/skills/cm-design-studio/SKILL.md +0 -26
- package/skills/cm-engineering-meta/SKILL.archive.md +0 -73
- package/skills/cm-engineering-meta/SKILL.md +0 -26
- package/skills/cm-git-worktrees/SKILL.archive.md +0 -157
- package/skills/cm-git-worktrees/SKILL.md +0 -26
- package/skills/cm-post-deploy-canary/SKILL.archive.md +0 -22
- package/skills/cm-post-deploy-canary/SKILL.md +0 -26
- package/skills/cm-qa-visual-cli/SKILL.archive.md +0 -22
- package/skills/cm-qa-visual-cli/SKILL.md +0 -26
- package/skills/cm-second-opinion-cli/SKILL.archive.md +0 -23
- package/skills/cm-second-opinion-cli/SKILL.md +0 -26
- package/skills/cm-secret-shield/SKILL.archive.md +0 -580
- package/skills/cm-secret-shield/SKILL.md +0 -26
- package/skills/cm-security-gate/SKILL.archive.md +0 -239
- package/skills/cm-security-gate/SKILL.md +0 -26
- package/skills/cm-skill-health/SKILL.archive.md +0 -83
- package/skills/cm-skill-health/SKILL.md +0 -26
- package/skills/cm-skill-mastery/SKILL.archive.md +0 -156
- package/skills/cm-skill-mastery/SKILL.md +0 -26
- package/skills/cm-skill-search/SKILL.archive.md +0 -49
- package/skills/cm-skill-search/SKILL.md +0 -26
- package/skills/cm-skill-share/SKILL.archive.md +0 -58
- package/skills/cm-skill-share/SKILL.md +0 -26
- package/skills/cm-test-gate/SKILL.archive.md +0 -245
- package/skills/cm-test-gate/SKILL.md +0 -26
- package/skills/cm-ui-preview/SKILL.archive.md +0 -153
- package/skills/cm-ui-preview/SKILL.md +0 -26
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Monetization Scorer โ Score content by commercial potential.
|
|
4
|
+
|
|
5
|
+
Analyzes articles and topics for monetization potential:
|
|
6
|
+
- Commercial intent score
|
|
7
|
+
- Conversion potential
|
|
8
|
+
- CTA recommendations
|
|
9
|
+
- Revenue-priority topic ranking
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
python3 monetize.py --config content-factory.config.json --score-all
|
|
13
|
+
python3 monetize.py --config content-factory.config.json --score article.md
|
|
14
|
+
python3 monetize.py --config content-factory.config.json --priority-queue
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import json
|
|
18
|
+
import sys
|
|
19
|
+
import re
|
|
20
|
+
import argparse
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from datetime import datetime
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# Commercial intent signals
|
|
26
|
+
COMMERCIAL_KEYWORDS_VI = [
|
|
27
|
+
"giรก", "bao nhiรชu", "mua", "ฤแบทt lแปch", "dแปch vแปฅ", "chi phรญ",
|
|
28
|
+
"khuyแบฟn mรฃi", "ฦฐu ฤรฃi", "trแป liแปu", "phรกc ฤแป", "liแปu trรฌnh"
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
COMMERCIAL_KEYWORDS_EN = [
|
|
32
|
+
"price", "cost", "buy", "book", "service", "deal",
|
|
33
|
+
"discount", "treatment", "program", "plan", "subscription"
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
TRANSACTIONAL_CATEGORIES = ["phac-do", "chan-dung", "reviews", "programs"]
|
|
37
|
+
|
|
38
|
+
CTA_TEMPLATES = {
|
|
39
|
+
"vi": {
|
|
40
|
+
"soft": "๐ Tรฌm hiแปu thรชm vแป dแปch vแปฅ {service} tแบกi {brand}",
|
|
41
|
+
"medium": "๐ ฤแบทt lแปch tฦฐ vแบฅn miแป
n phรญ: {hotline}",
|
|
42
|
+
"hard": "๐ ฦฏu ฤรฃi ฤแบทc biแปt: Giแบฃm 20% cho lแบงn ฤแบงu trแบฃi nghiแปm. ฤแบทt lแปch ngay!",
|
|
43
|
+
"educational": "๐ Xem thรชm bร i viแบฟt vแป {topic} tแบกi {brand}"
|
|
44
|
+
},
|
|
45
|
+
"en": {
|
|
46
|
+
"soft": "๐ Learn more about {service} at {brand}",
|
|
47
|
+
"medium": "๐ Book a free consultation: {hotline}",
|
|
48
|
+
"hard": "๐ Special offer: 20% off your first session. Book now!",
|
|
49
|
+
"educational": "๐ Read more about {topic} at {brand}"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class MonetizationScorer:
|
|
55
|
+
"""Score content by commercial and conversion potential."""
|
|
56
|
+
|
|
57
|
+
def __init__(self, config_path: str):
|
|
58
|
+
with open(config_path, "r", encoding="utf-8") as f:
|
|
59
|
+
self.config = json.load(f)
|
|
60
|
+
|
|
61
|
+
self.project_root = Path(config_path).resolve().parent
|
|
62
|
+
self.language = self.config.get("brand", {}).get("language", "vi")
|
|
63
|
+
|
|
64
|
+
def score_article(self, filepath: Path) -> dict:
|
|
65
|
+
"""Score a single article for monetization potential."""
|
|
66
|
+
with open(filepath, "r", encoding="utf-8") as f:
|
|
67
|
+
content = f.read()
|
|
68
|
+
|
|
69
|
+
# Parse frontmatter
|
|
70
|
+
fm = self._parse_frontmatter(content)
|
|
71
|
+
body = self._get_body(content)
|
|
72
|
+
|
|
73
|
+
score = {
|
|
74
|
+
"file": filepath.name,
|
|
75
|
+
"slug": filepath.stem,
|
|
76
|
+
"category": fm.get("category", "unknown"),
|
|
77
|
+
"scores": {
|
|
78
|
+
"commercial_intent": 0,
|
|
79
|
+
"conversion_potential": 0,
|
|
80
|
+
"topic_demand": 0,
|
|
81
|
+
"content_quality": 0
|
|
82
|
+
},
|
|
83
|
+
"total": 0,
|
|
84
|
+
"cta_recommendation": "",
|
|
85
|
+
"monetization_notes": []
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
# 1. Commercial intent (0-30)
|
|
89
|
+
keywords = COMMERCIAL_KEYWORDS_VI if self.language == "vi" else COMMERCIAL_KEYWORDS_EN
|
|
90
|
+
keyword_hits = sum(1 for kw in keywords if kw.lower() in body.lower())
|
|
91
|
+
score["scores"]["commercial_intent"] = min(30, keyword_hits * 5)
|
|
92
|
+
|
|
93
|
+
# Transactional category bonus
|
|
94
|
+
if fm.get("category", "") in TRANSACTIONAL_CATEGORIES:
|
|
95
|
+
score["scores"]["commercial_intent"] = min(30, score["scores"]["commercial_intent"] + 10)
|
|
96
|
+
|
|
97
|
+
# 2. Conversion potential (0-30)
|
|
98
|
+
has_cta = bool(re.search(r'(ฤแบทt lแปch|tฦฐ vแบฅn|liรชn hแป|book|contact|call)', body, re.IGNORECASE))
|
|
99
|
+
has_pricing = bool(re.search(r'(giรก|chi phรญ|price|cost|\d+\.?\d*\s*(vnฤ|ฤ|\$|usd))', body, re.IGNORECASE))
|
|
100
|
+
has_faq = bool(re.search(r'(cรขu hแปi|faq|q&a)', body, re.IGNORECASE))
|
|
101
|
+
|
|
102
|
+
score["scores"]["conversion_potential"] = (
|
|
103
|
+
(10 if has_cta else 0) +
|
|
104
|
+
(10 if has_pricing else 0) +
|
|
105
|
+
(10 if has_faq else 0)
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# 3. Topic demand (0-20) โ based on title keyword
|
|
109
|
+
title = fm.get("title", filepath.stem)
|
|
110
|
+
high_demand = ["ฤau", "mแบฅt ngแปง", "stress", "giแบฃm cรขn", "ฤau lฦฐng", "cแป vai gรกy",
|
|
111
|
+
"pain", "sleep", "weight", "back", "neck"]
|
|
112
|
+
demand_hits = sum(1 for kw in high_demand if kw.lower() in title.lower())
|
|
113
|
+
score["scores"]["topic_demand"] = min(20, demand_hits * 10)
|
|
114
|
+
|
|
115
|
+
# 4. Content quality signals (0-20)
|
|
116
|
+
words = len(body.split())
|
|
117
|
+
h2_count = len(re.findall(r'^## ', body, re.MULTILINE))
|
|
118
|
+
score["scores"]["content_quality"] = (
|
|
119
|
+
(5 if words >= 500 else 0) +
|
|
120
|
+
(5 if words >= 800 else 0) +
|
|
121
|
+
(5 if h2_count >= 3 else 0) +
|
|
122
|
+
(5 if has_faq else 0)
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# Total
|
|
126
|
+
score["total"] = sum(score["scores"].values())
|
|
127
|
+
|
|
128
|
+
# CTA recommendation
|
|
129
|
+
templates = CTA_TEMPLATES.get(self.language, CTA_TEMPLATES["vi"])
|
|
130
|
+
if score["total"] >= 60:
|
|
131
|
+
score["cta_recommendation"] = "hard"
|
|
132
|
+
elif score["total"] >= 35:
|
|
133
|
+
score["cta_recommendation"] = "medium"
|
|
134
|
+
elif score["scores"]["commercial_intent"] > 0:
|
|
135
|
+
score["cta_recommendation"] = "soft"
|
|
136
|
+
else:
|
|
137
|
+
score["cta_recommendation"] = "educational"
|
|
138
|
+
|
|
139
|
+
score["suggested_cta"] = templates.get(score["cta_recommendation"], "")
|
|
140
|
+
|
|
141
|
+
# Notes
|
|
142
|
+
if not has_cta:
|
|
143
|
+
score["monetization_notes"].append("Missing CTA โ add a booking/contact link")
|
|
144
|
+
if score["scores"]["commercial_intent"] == 0:
|
|
145
|
+
score["monetization_notes"].append("Low commercial intent โ add service mentions")
|
|
146
|
+
if score["total"] >= 50:
|
|
147
|
+
score["monetization_notes"].append("๐ฅ High monetization potential!")
|
|
148
|
+
|
|
149
|
+
return score
|
|
150
|
+
|
|
151
|
+
def score_all(self) -> list:
|
|
152
|
+
"""Score all articles for monetization."""
|
|
153
|
+
content_dir = self.project_root / self.config["output"]["content_dir"]
|
|
154
|
+
if not content_dir.exists():
|
|
155
|
+
return []
|
|
156
|
+
|
|
157
|
+
articles = sorted(content_dir.glob("*.md"))
|
|
158
|
+
scores = [self.score_article(a) for a in articles]
|
|
159
|
+
return sorted(scores, key=lambda s: s["total"], reverse=True)
|
|
160
|
+
|
|
161
|
+
def priority_queue(self) -> list:
|
|
162
|
+
"""Generate monetization-priority writing queue."""
|
|
163
|
+
scores = self.score_all()
|
|
164
|
+
|
|
165
|
+
# Identify high-potential articles needing improvement
|
|
166
|
+
needs_work = [s for s in scores if s["total"] >= 30 and s["monetization_notes"]]
|
|
167
|
+
already_good = [s for s in scores if s["total"] >= 60]
|
|
168
|
+
low_potential = [s for s in scores if s["total"] < 30]
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
"hot": [s["slug"] for s in already_good[:10]],
|
|
172
|
+
"optimize": [{"slug": s["slug"], "notes": s["monetization_notes"]} for s in needs_work[:10]],
|
|
173
|
+
"low_priority": len(low_potential),
|
|
174
|
+
"avg_score": sum(s["total"] for s in scores) / max(len(scores), 1)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
def _parse_frontmatter(self, content: str) -> dict:
|
|
178
|
+
match = re.match(r'^---\s*\n(.+?)\n---', content, re.DOTALL)
|
|
179
|
+
if not match:
|
|
180
|
+
return {}
|
|
181
|
+
fm = {}
|
|
182
|
+
for line in match.group(1).split('\n'):
|
|
183
|
+
if ':' in line:
|
|
184
|
+
key, _, val = line.partition(':')
|
|
185
|
+
fm[key.strip()] = val.strip().strip('"').strip("'")
|
|
186
|
+
return fm
|
|
187
|
+
|
|
188
|
+
def _get_body(self, content: str) -> str:
|
|
189
|
+
match = re.match(r'^---\s*\n.+?\n---\s*\n(.*)', content, re.DOTALL)
|
|
190
|
+
return match.group(1) if match else content
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def main():
|
|
194
|
+
parser = argparse.ArgumentParser(description="Monetization Scorer")
|
|
195
|
+
parser.add_argument("--config", required=True)
|
|
196
|
+
parser.add_argument("--score", help="Score single article")
|
|
197
|
+
parser.add_argument("--score-all", action="store_true")
|
|
198
|
+
parser.add_argument("--priority-queue", action="store_true")
|
|
199
|
+
args = parser.parse_args()
|
|
200
|
+
|
|
201
|
+
scorer = MonetizationScorer(args.config)
|
|
202
|
+
|
|
203
|
+
if args.score:
|
|
204
|
+
fp = Path(args.score)
|
|
205
|
+
if not fp.exists():
|
|
206
|
+
fp = scorer.project_root / scorer.config["output"]["content_dir"] / args.score
|
|
207
|
+
result = scorer.score_article(fp)
|
|
208
|
+
print(f"\n๐ฐ {result['slug']}: {result['total']}/100")
|
|
209
|
+
for k, v in result["scores"].items():
|
|
210
|
+
print(f" {k}: {v}")
|
|
211
|
+
print(f" CTA: {result['cta_recommendation']}")
|
|
212
|
+
for note in result["monetization_notes"]:
|
|
213
|
+
print(f" ๐ {note}")
|
|
214
|
+
|
|
215
|
+
elif args.score_all:
|
|
216
|
+
scores = scorer.score_all()
|
|
217
|
+
print(f"\n{'โ' * 60}")
|
|
218
|
+
print(f" ๐ฐ MONETIZATION SCORES ({len(scores)} articles)")
|
|
219
|
+
print(f"{'โ' * 60}")
|
|
220
|
+
for s in scores[:20]:
|
|
221
|
+
bar = "โ" * (s["total"] // 5) + "โ" * (20 - s["total"] // 5)
|
|
222
|
+
print(f" {s['total']:3d} {bar} {s['slug'][:40]}")
|
|
223
|
+
avg = sum(s["total"] for s in scores) / max(len(scores), 1)
|
|
224
|
+
print(f"\n Average: {avg:.0f}/100")
|
|
225
|
+
|
|
226
|
+
elif args.priority_queue:
|
|
227
|
+
queue = scorer.priority_queue()
|
|
228
|
+
print(f"\n๐ฅ HOT (ready to monetize): {len(queue['hot'])}")
|
|
229
|
+
for slug in queue["hot"]:
|
|
230
|
+
print(f" โ {slug}")
|
|
231
|
+
print(f"\nโก OPTIMIZE (needs CTA/improvements): {len(queue['optimize'])}")
|
|
232
|
+
for item in queue["optimize"]:
|
|
233
|
+
print(f" โ {item['slug']}: {', '.join(item['notes'][:2])}")
|
|
234
|
+
print(f"\n๐ Low priority: {queue['low_priority']} articles")
|
|
235
|
+
print(f" Average score: {queue['avg_score']:.0f}/100")
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
if __name__ == "__main__":
|
|
239
|
+
main()
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Content Factory Pipeline โ Master orchestrator for the AI Content Factory.
|
|
4
|
+
|
|
5
|
+
Reads content-factory.config.json and executes phases:
|
|
6
|
+
extract โ plan โ write โ audit โ seo โ publish
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
python3 .agents/skills/content-factory/scripts/pipeline.py # Run full pipeline
|
|
10
|
+
python3 .agents/skills/content-factory/scripts/pipeline.py --phase extract # Run single phase
|
|
11
|
+
python3 .agents/skills/content-factory/scripts/pipeline.py --validate-config # Validate config
|
|
12
|
+
python3 .agents/skills/content-factory/scripts/pipeline.py --dry-run # Preview actions
|
|
13
|
+
python3 .agents/skills/content-factory/scripts/pipeline.py --test-hooks # Test hook system
|
|
14
|
+
python3 .agents/skills/content-factory/scripts/pipeline.py --status # Show pipeline status
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import json
|
|
18
|
+
import sys
|
|
19
|
+
import os
|
|
20
|
+
import subprocess
|
|
21
|
+
import argparse
|
|
22
|
+
import threading
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from datetime import datetime
|
|
25
|
+
|
|
26
|
+
# State integration
|
|
27
|
+
try:
|
|
28
|
+
from state_manager import StateManager
|
|
29
|
+
HAS_STATE = True
|
|
30
|
+
except ImportError:
|
|
31
|
+
HAS_STATE = False
|
|
32
|
+
|
|
33
|
+
# Resolve paths
|
|
34
|
+
SCRIPT_DIR = Path(__file__).resolve().parent
|
|
35
|
+
SKILL_DIR = SCRIPT_DIR.parent
|
|
36
|
+
PROJECT_ROOT = Path(os.getcwd()).resolve()
|
|
37
|
+
CONFIG_FILE = PROJECT_ROOT / "content-factory.config.json"
|
|
38
|
+
SCHEMA_FILE = SKILL_DIR / "config.schema.json"
|
|
39
|
+
|
|
40
|
+
PHASES = ["extract", "plan", "write", "audit", "seo", "publish"]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def load_config() -> dict:
|
|
44
|
+
"""Load and return the project config."""
|
|
45
|
+
if not CONFIG_FILE.exists():
|
|
46
|
+
print(f"โ Config not found: {CONFIG_FILE}")
|
|
47
|
+
print(" Create content-factory.config.json in project root.")
|
|
48
|
+
print(f" See examples in: {SKILL_DIR / 'examples/'}")
|
|
49
|
+
sys.exit(1)
|
|
50
|
+
|
|
51
|
+
with open(CONFIG_FILE, "r", encoding="utf-8") as f:
|
|
52
|
+
return json.load(f)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def validate_config(config: dict) -> bool:
|
|
56
|
+
"""Validate config structure (basic validation without jsonschema dep)."""
|
|
57
|
+
required_keys = ["niche", "brand", "content", "sources", "output"]
|
|
58
|
+
missing = [k for k in required_keys if k not in config]
|
|
59
|
+
if missing:
|
|
60
|
+
print(f"โ Missing required config keys: {', '.join(missing)}")
|
|
61
|
+
return False
|
|
62
|
+
|
|
63
|
+
if "name" not in config.get("brand", {}):
|
|
64
|
+
print("โ Missing brand.name in config")
|
|
65
|
+
return False
|
|
66
|
+
|
|
67
|
+
if "article_types" not in config.get("content", {}):
|
|
68
|
+
print("โ Missing content.article_types in config")
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
if "type" not in config.get("sources", {}) or "path" not in config.get("sources", {}):
|
|
72
|
+
print("โ Missing sources.type or sources.path in config")
|
|
73
|
+
return False
|
|
74
|
+
|
|
75
|
+
if "content_dir" not in config.get("output", {}):
|
|
76
|
+
print("โ Missing output.content_dir in config")
|
|
77
|
+
return False
|
|
78
|
+
|
|
79
|
+
print("โ
Config validation passed")
|
|
80
|
+
return True
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def fire_hooks(config: dict, hook_name: str):
|
|
84
|
+
"""Execute registered hooks for a phase."""
|
|
85
|
+
hooks = config.get("extensions", {}).get("hooks", {}).get(hook_name, [])
|
|
86
|
+
if not hooks:
|
|
87
|
+
return
|
|
88
|
+
|
|
89
|
+
print(f" ๐ Running {hook_name} hooks ({len(hooks)})...")
|
|
90
|
+
for hook_script in hooks:
|
|
91
|
+
hook_path = PROJECT_ROOT / hook_script
|
|
92
|
+
if hook_path.exists():
|
|
93
|
+
try:
|
|
94
|
+
result = subprocess.run(
|
|
95
|
+
["python3", str(hook_path), str(CONFIG_FILE)],
|
|
96
|
+
cwd=str(PROJECT_ROOT),
|
|
97
|
+
capture_output=True, text=True, timeout=300
|
|
98
|
+
)
|
|
99
|
+
if result.returncode == 0:
|
|
100
|
+
print(f" โ
{hook_script}")
|
|
101
|
+
else:
|
|
102
|
+
print(f" โ ๏ธ {hook_script}: {result.stderr[:200]}")
|
|
103
|
+
except Exception as e:
|
|
104
|
+
print(f" โ {hook_script}: {e}")
|
|
105
|
+
else:
|
|
106
|
+
print(f" โ ๏ธ Hook not found: {hook_script}")
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def run_phase(phase: str, config: dict, dry_run: bool = False, extra_args: list = None,
|
|
110
|
+
state_manager: 'StateManager' = None):
|
|
111
|
+
"""Run a single pipeline phase."""
|
|
112
|
+
script_path = SCRIPT_DIR / f"{phase}.py"
|
|
113
|
+
if not script_path.exists():
|
|
114
|
+
print(f"โ Phase script not found: {script_path}")
|
|
115
|
+
if state_manager:
|
|
116
|
+
state_manager.update_phase(phase, "failed", error=f"Script not found: {phase}.py")
|
|
117
|
+
return False
|
|
118
|
+
|
|
119
|
+
# Emit state: phase starting
|
|
120
|
+
if state_manager:
|
|
121
|
+
state_manager.update_phase(phase, "running", progress=0.0)
|
|
122
|
+
|
|
123
|
+
# Fire pre-hook
|
|
124
|
+
fire_hooks(config, f"pre_{phase}")
|
|
125
|
+
|
|
126
|
+
cmd = ["python3", str(script_path), "--config", str(CONFIG_FILE)]
|
|
127
|
+
if dry_run:
|
|
128
|
+
cmd.append("--dry-run")
|
|
129
|
+
if extra_args:
|
|
130
|
+
cmd.extend(extra_args)
|
|
131
|
+
|
|
132
|
+
print(f"\n{'โ' * 50}")
|
|
133
|
+
print(f" Phase: {phase.upper()}")
|
|
134
|
+
print(f"{'โ' * 50}")
|
|
135
|
+
|
|
136
|
+
if dry_run:
|
|
137
|
+
print(f" [DRY RUN] Would execute: {' '.join(cmd)}")
|
|
138
|
+
if state_manager:
|
|
139
|
+
state_manager.update_phase(phase, "done", progress=1.0)
|
|
140
|
+
fire_hooks(config, f"post_{phase}")
|
|
141
|
+
return True
|
|
142
|
+
|
|
143
|
+
try:
|
|
144
|
+
result = subprocess.run(
|
|
145
|
+
cmd, cwd=str(PROJECT_ROOT),
|
|
146
|
+
timeout=3600 # 1h max per phase
|
|
147
|
+
)
|
|
148
|
+
success = result.returncode == 0
|
|
149
|
+
except subprocess.TimeoutExpired:
|
|
150
|
+
print(f" โ Phase {phase} timed out (1h limit)")
|
|
151
|
+
success = False
|
|
152
|
+
except Exception as e:
|
|
153
|
+
print(f" โ Phase {phase} error: {e}")
|
|
154
|
+
success = False
|
|
155
|
+
|
|
156
|
+
# Emit state: phase result
|
|
157
|
+
if state_manager:
|
|
158
|
+
if success:
|
|
159
|
+
state_manager.update_phase(phase, "done", progress=1.0)
|
|
160
|
+
else:
|
|
161
|
+
state_manager.update_phase(phase, "failed", error=f"Phase {phase} failed")
|
|
162
|
+
|
|
163
|
+
# Fire post-hook
|
|
164
|
+
fire_hooks(config, f"post_{phase}")
|
|
165
|
+
|
|
166
|
+
return success
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def show_status(config: dict):
|
|
170
|
+
"""Show current pipeline status."""
|
|
171
|
+
print(f"\n{'โ' * 60}")
|
|
172
|
+
print(f" ๐ญ Content Factory โ Status Report")
|
|
173
|
+
print(f"{'โ' * 60}")
|
|
174
|
+
print(f" Niche: {config['niche']}")
|
|
175
|
+
print(f" Brand: {config['brand']['name']}")
|
|
176
|
+
print(f" Language: {config['brand'].get('language', 'vi')}")
|
|
177
|
+
print(f" AI: {config.get('pipeline', {}).get('ai_provider', 'gemini-cli')}")
|
|
178
|
+
print(f" Parallel: {config.get('pipeline', {}).get('concurrency', 1)}")
|
|
179
|
+
print()
|
|
180
|
+
|
|
181
|
+
# Knowledge-base status
|
|
182
|
+
kb_dir = PROJECT_ROOT / config["output"].get("knowledge_dir", "knowledge-base/")
|
|
183
|
+
if kb_dir.exists():
|
|
184
|
+
groups = [d for d in kb_dir.iterdir() if d.is_dir()]
|
|
185
|
+
index = kb_dir / "index.json"
|
|
186
|
+
disease_count = 0
|
|
187
|
+
if index.exists():
|
|
188
|
+
with open(index) as f:
|
|
189
|
+
idx = json.load(f)
|
|
190
|
+
disease_count = idx.get("total_diseases", len(idx.get("groups", [])))
|
|
191
|
+
print(f" ๐ Knowledge-base: {len(groups)} groups, {disease_count} entries")
|
|
192
|
+
else:
|
|
193
|
+
print(f" ๐ Knowledge-base: Not yet created")
|
|
194
|
+
|
|
195
|
+
# Topics queue status
|
|
196
|
+
queue_dir = PROJECT_ROOT / config["output"].get("queue_dir", "topics-queue/")
|
|
197
|
+
if queue_dir.exists():
|
|
198
|
+
batches = list(queue_dir.glob("*.json"))
|
|
199
|
+
total_topics = 0
|
|
200
|
+
for b in batches:
|
|
201
|
+
with open(b) as f:
|
|
202
|
+
data = json.load(f)
|
|
203
|
+
total_topics += len(data.get("topics", []))
|
|
204
|
+
print(f" ๐ Topics queue: {len(batches)} batches, {total_topics} topics")
|
|
205
|
+
else:
|
|
206
|
+
print(f" ๐ Topics queue: Not yet created")
|
|
207
|
+
|
|
208
|
+
# Content status
|
|
209
|
+
content_dir = PROJECT_ROOT / config["output"]["content_dir"]
|
|
210
|
+
if content_dir.exists():
|
|
211
|
+
articles = list(content_dir.glob("*.md"))
|
|
212
|
+
print(f" ๐ Content: {len(articles)} articles")
|
|
213
|
+
else:
|
|
214
|
+
print(f" ๐ Content: Not yet created")
|
|
215
|
+
|
|
216
|
+
# Extensions
|
|
217
|
+
ext = config.get("extensions", {})
|
|
218
|
+
hooks_count = sum(len(v) for v in ext.get("hooks", {}).values())
|
|
219
|
+
openclaw = "โ
enabled" if ext.get("openclaw", {}).get("enabled") else "โฌ disabled"
|
|
220
|
+
print(f" ๐ Hooks: {hooks_count} registered")
|
|
221
|
+
print(f" ๐ OpenClaw: {openclaw}")
|
|
222
|
+
|
|
223
|
+
print(f"\n{'โ' * 60}\n")
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def test_hooks(config: dict):
|
|
227
|
+
"""Test that all registered hooks exist and are executable."""
|
|
228
|
+
print("๐งช Testing hooks...")
|
|
229
|
+
hooks = config.get("extensions", {}).get("hooks", {})
|
|
230
|
+
all_ok = True
|
|
231
|
+
for hook_name, scripts in hooks.items():
|
|
232
|
+
for script in scripts:
|
|
233
|
+
path = PROJECT_ROOT / script
|
|
234
|
+
if path.exists():
|
|
235
|
+
print(f" โ
{hook_name}: {script}")
|
|
236
|
+
else:
|
|
237
|
+
print(f" โ {hook_name}: {script} โ NOT FOUND")
|
|
238
|
+
all_ok = False
|
|
239
|
+
if not hooks or all(not v for v in hooks.values()):
|
|
240
|
+
print(" โฌ No hooks registered")
|
|
241
|
+
return all_ok
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def start_dashboard(port: int = 5050):
|
|
245
|
+
"""Start dashboard server in background thread."""
|
|
246
|
+
dashboard_script = SCRIPT_DIR / "dashboard_server.py"
|
|
247
|
+
if not dashboard_script.exists():
|
|
248
|
+
print(f" โ ๏ธ Dashboard not found: {dashboard_script}")
|
|
249
|
+
return None
|
|
250
|
+
|
|
251
|
+
import subprocess as sp
|
|
252
|
+
proc = sp.Popen(
|
|
253
|
+
["python3", str(dashboard_script), "--port", str(port)],
|
|
254
|
+
cwd=str(PROJECT_ROOT),
|
|
255
|
+
)
|
|
256
|
+
return proc
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def main():
|
|
260
|
+
parser = argparse.ArgumentParser(
|
|
261
|
+
description="Content Factory Pipeline โ Master orchestrator"
|
|
262
|
+
)
|
|
263
|
+
parser.add_argument("--phase", choices=PHASES, help="Run single phase")
|
|
264
|
+
parser.add_argument("--validate-config", action="store_true", help="Validate config only")
|
|
265
|
+
parser.add_argument("--dry-run", action="store_true", help="Preview without executing")
|
|
266
|
+
parser.add_argument("--test-hooks", action="store_true", help="Test hook system")
|
|
267
|
+
parser.add_argument("--status", action="store_true", help="Show pipeline status")
|
|
268
|
+
parser.add_argument("--from-phase", choices=PHASES, help="Start pipeline from phase")
|
|
269
|
+
parser.add_argument("--batch", type=int, default=10, help="Batch size for write phase")
|
|
270
|
+
parser.add_argument("--group", help="Filter by group code")
|
|
271
|
+
parser.add_argument("--dashboard", action="store_true", help="Auto-start dashboard server")
|
|
272
|
+
parser.add_argument("--dashboard-port", type=int, default=5050, help="Dashboard port")
|
|
273
|
+
parser.add_argument("--budget", type=float, help="Max budget in USD")
|
|
274
|
+
args = parser.parse_args()
|
|
275
|
+
|
|
276
|
+
config = load_config()
|
|
277
|
+
|
|
278
|
+
if args.validate_config:
|
|
279
|
+
sys.exit(0 if validate_config(config) else 1)
|
|
280
|
+
|
|
281
|
+
if args.status:
|
|
282
|
+
show_status(config)
|
|
283
|
+
return
|
|
284
|
+
|
|
285
|
+
if args.test_hooks:
|
|
286
|
+
sys.exit(0 if test_hooks(config) else 1)
|
|
287
|
+
|
|
288
|
+
if not validate_config(config):
|
|
289
|
+
sys.exit(1)
|
|
290
|
+
|
|
291
|
+
# Initialize state manager
|
|
292
|
+
sm = None
|
|
293
|
+
if HAS_STATE:
|
|
294
|
+
sm = StateManager(str(PROJECT_ROOT))
|
|
295
|
+
sm.reset()
|
|
296
|
+
sm.log_event("info", f"Pipeline started for {config['brand']['name']}")
|
|
297
|
+
if args.budget:
|
|
298
|
+
sm.set_budget(args.budget)
|
|
299
|
+
|
|
300
|
+
# Start dashboard
|
|
301
|
+
dashboard_proc = None
|
|
302
|
+
if args.dashboard:
|
|
303
|
+
dashboard_proc = start_dashboard(args.dashboard_port)
|
|
304
|
+
if dashboard_proc:
|
|
305
|
+
print(f" ๐ Dashboard: http://localhost:{args.dashboard_port}")
|
|
306
|
+
|
|
307
|
+
print(f"\n๐ญ Content Factory Pipeline โ {config['brand']['name']}")
|
|
308
|
+
print(f" Niche: {config['niche']}")
|
|
309
|
+
print(f" Started: {datetime.now().strftime('%Y-%m-%d %H:%M')}")
|
|
310
|
+
|
|
311
|
+
# Determine phases to run
|
|
312
|
+
if args.phase:
|
|
313
|
+
phases = [args.phase]
|
|
314
|
+
elif args.from_phase:
|
|
315
|
+
start_idx = PHASES.index(args.from_phase)
|
|
316
|
+
phases = PHASES[start_idx:]
|
|
317
|
+
else:
|
|
318
|
+
phases = PHASES
|
|
319
|
+
|
|
320
|
+
# Build extra args per phase
|
|
321
|
+
extra = {}
|
|
322
|
+
if args.batch:
|
|
323
|
+
extra["write"] = ["--batch", str(args.batch)]
|
|
324
|
+
if args.group:
|
|
325
|
+
for p in phases:
|
|
326
|
+
extra.setdefault(p, []).extend(["--group", args.group])
|
|
327
|
+
|
|
328
|
+
# Execute
|
|
329
|
+
results = {}
|
|
330
|
+
for phase in phases:
|
|
331
|
+
phase_extra = extra.get(phase, [])
|
|
332
|
+
success = run_phase(phase, config, args.dry_run, phase_extra, state_manager=sm)
|
|
333
|
+
results[phase] = success
|
|
334
|
+
if not success and not args.dry_run:
|
|
335
|
+
print(f"\nโ Pipeline stopped at phase: {phase}")
|
|
336
|
+
break
|
|
337
|
+
|
|
338
|
+
# Summary
|
|
339
|
+
print(f"\n{'โ' * 50}")
|
|
340
|
+
print(f" ๐ PIPELINE SUMMARY")
|
|
341
|
+
print(f"{'โ' * 50}")
|
|
342
|
+
for phase, ok in results.items():
|
|
343
|
+
icon = "โ
" if ok else "โ"
|
|
344
|
+
print(f" {icon} {phase.upper()}")
|
|
345
|
+
print(f"{'โ' * 50}")
|
|
346
|
+
print(f" Finished: {datetime.now().strftime('%Y-%m-%d %H:%M')}\n")
|
|
347
|
+
|
|
348
|
+
if sm:
|
|
349
|
+
sm.log_event("info", "Pipeline finished")
|
|
350
|
+
|
|
351
|
+
# Stop dashboard if we started it
|
|
352
|
+
if dashboard_proc:
|
|
353
|
+
dashboard_proc.terminate()
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
if __name__ == "__main__":
|
|
357
|
+
main()
|