taketomarket 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/.claude-plugin/marketplace.json +4 -4
  2. package/.claude-plugin/plugin.json +2 -2
  3. package/README.md +34 -11
  4. package/bin/lib/campaign.cjs +12 -8
  5. package/bin/lib/codebase-scan.cjs +86 -0
  6. package/bin/lib/config.cjs +129 -0
  7. package/bin/lib/deploy.cjs +36 -0
  8. package/bin/lib/deviation.cjs +1 -1
  9. package/bin/lib/drift-log.cjs +4 -4
  10. package/bin/lib/health.cjs +32 -31
  11. package/bin/lib/install-detect.cjs +62 -0
  12. package/bin/lib/legacy-folder.cjs +100 -0
  13. package/bin/lib/playwright-check.cjs +26 -0
  14. package/bin/lib/site-location.cjs +22 -0
  15. package/bin/lib/state.cjs +3 -3
  16. package/bin/lib/svg-render.cjs +42 -0
  17. package/bin/ttm-tools.cjs +136 -4
  18. package/gates/base-gates.md +8 -8
  19. package/gates/gate-evaluation.md +8 -8
  20. package/install.js +37 -3
  21. package/package.json +10 -6
  22. package/playbooks/aeo.md +218 -114
  23. package/playbooks/affiliate.md +225 -160
  24. package/playbooks/email.md +236 -174
  25. package/playbooks/events.md +303 -213
  26. package/playbooks/landing-pages.md +305 -0
  27. package/playbooks/linkedin.md +264 -142
  28. package/playbooks/manifesto.md +322 -0
  29. package/playbooks/paid-ads.md +240 -189
  30. package/playbooks/positioning.md +340 -0
  31. package/playbooks/pr-media.md +308 -168
  32. package/playbooks/pseo.md +426 -0
  33. package/playbooks/seo.md +251 -158
  34. package/playbooks/social.md +253 -182
  35. package/playbooks/youtube.md +286 -181
  36. package/references/brand-color-theory.md +48 -0
  37. package/references/codex-image-gen-research.md +58 -0
  38. package/references/context-loading.md +6 -6
  39. package/references/humanizer-patterns.md +433 -0
  40. package/references/inline-education-blurbs.md +461 -0
  41. package/references/landing-page-anatomy.md +64 -0
  42. package/references/linkedin-post-patterns.md +174 -0
  43. package/references/logo-design-principles.md +55 -0
  44. package/references/meta-gate-evaluation.md +3 -3
  45. package/references/obra-superpowers-conventions.md +170 -0
  46. package/references/playbook-leaders.md +472 -0
  47. package/references/playwright-mcp-setup.md +164 -0
  48. package/references/positioning-check-report.md +2 -2
  49. package/references/pseo-page-anatomy.md +56 -0
  50. package/references/pseo-templates/alternative-anatomy.md +31 -0
  51. package/references/pseo-templates/alternative-content-playbook.md +32 -0
  52. package/references/pseo-templates/blog-anatomy.md +28 -0
  53. package/references/pseo-templates/blog-content-playbook.md +36 -0
  54. package/references/pseo-templates/comparison-anatomy.md +29 -0
  55. package/references/pseo-templates/comparison-content-playbook.md +35 -0
  56. package/references/pseo-templates/use-case-anatomy.md +28 -0
  57. package/references/pseo-templates/use-case-content-playbook.md +30 -0
  58. package/skills/ttm-101/SKILL.md +25 -0
  59. package/skills/ttm-aeo-check/SKILL.md +17 -12
  60. package/skills/ttm-affiliate-kit/SKILL.md +5 -0
  61. package/skills/ttm-archive/SKILL.md +5 -0
  62. package/skills/ttm-brand-refresh/SKILL.md +5 -0
  63. package/skills/ttm-brief/SKILL.md +5 -0
  64. package/skills/ttm-competitor-scan/SKILL.md +5 -0
  65. package/skills/ttm-deploy/SKILL.md +22 -0
  66. package/skills/ttm-discover/SKILL.md +17 -0
  67. package/skills/ttm-email-check/SKILL.md +17 -0
  68. package/skills/ttm-email-preflight/SKILL.md +17 -11
  69. package/skills/ttm-fix/SKILL.md +5 -0
  70. package/skills/ttm-health/SKILL.md +6 -1
  71. package/skills/ttm-humanize/SKILL.md +33 -0
  72. package/skills/ttm-icp-refresh/SKILL.md +5 -0
  73. package/skills/ttm-improve-skill/SKILL.md +18 -0
  74. package/skills/ttm-init/SKILL.md +10 -3
  75. package/skills/ttm-keyword-map/SKILL.md +17 -11
  76. package/skills/ttm-landing/SKILL.md +19 -0
  77. package/skills/ttm-learn/SKILL.md +5 -0
  78. package/skills/ttm-linkedin-post/SKILL.md +26 -0
  79. package/skills/ttm-measure/SKILL.md +5 -0
  80. package/skills/ttm-new-campaign/SKILL.md +5 -0
  81. package/skills/ttm-next/SKILL.md +5 -0
  82. package/skills/ttm-playwright-setup/SKILL.md +18 -0
  83. package/skills/ttm-positioning-check/SKILL.md +5 -0
  84. package/skills/ttm-positioning-shift/SKILL.md +5 -0
  85. package/skills/ttm-produce/SKILL.md +5 -0
  86. package/skills/ttm-pseo/SKILL.md +26 -0
  87. package/skills/ttm-repurpose/SKILL.md +5 -0
  88. package/skills/ttm-request-skill/SKILL.md +18 -0
  89. package/skills/ttm-research/SKILL.md +18 -6
  90. package/skills/ttm-resume/SKILL.md +5 -0
  91. package/skills/ttm-review/SKILL.md +5 -0
  92. package/skills/ttm-seo/SKILL.md +64 -0
  93. package/skills/ttm-seo-audit/SKILL.md +17 -12
  94. package/skills/ttm-ship/SKILL.md +5 -0
  95. package/skills/ttm-state/SKILL.md +5 -0
  96. package/skills/ttm-update/SKILL.md +152 -4
  97. package/skills/ttm-verify/SKILL.md +5 -0
  98. package/templates/agents-md.md +14 -4
  99. package/templates/campaign-research.md +6 -6
  100. package/templates/campaign-state.md +1 -1
  101. package/templates/claude-md.md +14 -4
  102. package/templates/linkedin-base-template.md +48 -0
  103. package/templates/next-step-footer.md +13 -0
  104. package/templates/production-manifest.json +4 -4
  105. package/templates/pseo/alternative-cms-schema.json +65 -0
  106. package/templates/pseo/blog-cms-schema.json +55 -0
  107. package/templates/pseo/comparison-cms-schema.json +56 -0
  108. package/templates/pseo/use-case-cms-schema.json +62 -0
  109. package/templates/reference-files/brand.md +51 -0
  110. package/templates/reference-files/product-dna.md +73 -0
  111. package/templates/site-scaffold/app/globals.css +2 -0
  112. package/templates/site-scaffold/app/layout.tsx +17 -0
  113. package/templates/site-scaffold/app/page.tsx +33 -0
  114. package/templates/site-scaffold/app/robots.ts +8 -0
  115. package/templates/site-scaffold/app/sitemap.ts +10 -0
  116. package/templates/site-scaffold/app/tokens.css +21 -0
  117. package/templates/site-scaffold/components/Comparison.tsx +14 -0
  118. package/templates/site-scaffold/components/Faq.tsx +14 -0
  119. package/templates/site-scaffold/components/Features.tsx +14 -0
  120. package/templates/site-scaffold/components/FinalCta.tsx +17 -0
  121. package/templates/site-scaffold/components/Footer.tsx +12 -0
  122. package/templates/site-scaffold/components/Hero.tsx +22 -0
  123. package/templates/site-scaffold/components/HowItWorks.tsx +14 -0
  124. package/templates/site-scaffold/components/PricingTeaser.tsx +14 -0
  125. package/templates/site-scaffold/components/Problem.tsx +14 -0
  126. package/templates/site-scaffold/components/SocialProof.tsx +14 -0
  127. package/templates/site-scaffold/components/Solution.tsx +14 -0
  128. package/templates/site-scaffold/components/Testimonials.tsx +14 -0
  129. package/templates/site-scaffold/components/UseCases.tsx +14 -0
  130. package/templates/site-scaffold/content/.gitkeep +0 -0
  131. package/templates/site-scaffold/lib/.gitkeep +0 -0
  132. package/templates/site-scaffold/next.config.mjs +10 -0
  133. package/templates/site-scaffold/package.json +25 -0
  134. package/templates/site-scaffold/postcss.config.mjs +3 -0
  135. package/templates/site-scaffold/public/llms.txt +9 -0
  136. package/templates/site-scaffold/tsconfig.json +21 -0
  137. package/templates/verification-report.md +1 -1
  138. package/workflows/channel/linkedin-post.md +178 -0
  139. package/workflows/discipline/affiliate-kit.md +65 -6
  140. package/workflows/discipline/{email-preflight.md → email-check.md} +39 -4
  141. package/workflows/discipline/repurpose.md +82 -31
  142. package/workflows/discipline/{aeo-check.md → seo/aeo.md} +13 -6
  143. package/workflows/discipline/{seo-audit.md → seo/audit.md} +13 -6
  144. package/workflows/discipline/{keyword-map.md → seo/keyword-map.md} +13 -6
  145. package/workflows/education/ttm-101.md +114 -0
  146. package/workflows/lifecycle/brief-positioning-check.md +1 -1
  147. package/workflows/lifecycle/brief.md +64 -28
  148. package/workflows/lifecycle/{research.md → discover.md} +61 -19
  149. package/workflows/lifecycle/fix.md +72 -37
  150. package/workflows/lifecycle/humanize.md +280 -0
  151. package/workflows/lifecycle/learn.md +72 -35
  152. package/workflows/lifecycle/measure.md +54 -18
  153. package/workflows/lifecycle/produce.md +88 -37
  154. package/workflows/lifecycle/review.md +71 -25
  155. package/workflows/lifecycle/ship.md +62 -18
  156. package/workflows/lifecycle/verify.md +72 -26
  157. package/workflows/reference-mgmt/brand-refresh.md +50 -13
  158. package/workflows/reference-mgmt/competitor-scan.md +51 -15
  159. package/workflows/reference-mgmt/icp-refresh.md +48 -12
  160. package/workflows/reference-mgmt/positioning-check.md +55 -20
  161. package/workflows/reference-mgmt/positioning-shift.md +53 -17
  162. package/workflows/setup/init-brand-colors.md +75 -0
  163. package/workflows/setup/init-logo.md +113 -0
  164. package/workflows/setup/init-product-dna.md +83 -0
  165. package/workflows/setup/init-questions.md +166 -30
  166. package/workflows/setup/init-validation.md +22 -0
  167. package/workflows/setup/init.md +144 -39
  168. package/workflows/setup/new-campaign.md +48 -12
  169. package/workflows/site/deploy.md +98 -0
  170. package/workflows/site/landing.md +156 -0
  171. package/workflows/site/pseo.md +96 -0
  172. package/workflows/site/quality-gates.md +88 -0
  173. package/workflows/utility/archive.md +45 -9
  174. package/workflows/utility/health.md +77 -3
  175. package/workflows/utility/improve-skill.md +233 -0
  176. package/workflows/utility/next.md +38 -2
  177. package/workflows/utility/playwright-setup.md +128 -0
  178. package/workflows/utility/request-skill.md +218 -0
  179. package/workflows/utility/resume.md +40 -3
  180. package/workflows/utility/state.md +42 -7
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "taketomarket",
3
3
  "displayName": "takeToMarket",
4
- "description": "Marketing OS for Claude Code and Codex. Spec-driven campaigns with positioning invariants and quality gates.",
5
- "version": "2.2.0",
4
+ "description": "Marketing OS for developerneurs and solopreneurs. Built for engineers shipping products with zero marketing experience required. Spec-driven campaigns with positioning-as-invariant enforcement and quality gate walls.",
5
+ "version": "2.3.0-rc.1",
6
6
  "author": {
7
7
  "name": "Rishikesh Ranjan",
8
8
  "url": "https://github.com/ranjanrishikesh"
9
9
  },
10
10
  "homepage": "https://www.npmjs.com/package/taketomarket",
11
- "repository": "https://github.com/ranjanrishikesh/takeToMarket",
11
+ "repository": "https://github.com/ranjanrishikesh/taketomarket",
12
12
  "license": "MIT",
13
13
  "keywords": ["marketing", "campaigns", "positioning", "quality-gates", "agent-skills"],
14
14
  "category": "productivity",
15
15
  "source": "git",
16
- "sourceUrl": "https://github.com/ranjanrishikesh/takeToMarket.git",
16
+ "sourceUrl": "https://github.com/ranjanrishikesh/taketomarket.git",
17
17
  "skills": [
18
18
  "ttm-init", "ttm-new-campaign", "ttm-brief", "ttm-research",
19
19
  "ttm-produce", "ttm-review", "ttm-fix", "ttm-verify", "ttm-ship",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "taketomarket",
3
- "version": "2.2.0",
4
- "description": "Marketing operating system for Claude Code. Spec-driven campaigns with positioning-as-invariant enforcement, quality gate walls, and compound learnings.",
3
+ "version": "2.3.0",
4
+ "description": "Marketing OS for developerneurs and solopreneurs. Built for engineers shipping products with zero marketing experience required. Spec-driven campaigns with positioning-as-invariant enforcement and quality gate walls.",
5
5
  "author": {
6
6
  "name": "takeToMarket"
7
7
  },
package/README.md CHANGED
@@ -1,11 +1,28 @@
1
1
  # takeToMarket
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/taketomarket)](https://www.npmjs.com/package/taketomarket)
4
+ [![GitHub stars](https://img.shields.io/github/stars/ranjanrishikesh/taketomarket?style=social)](https://github.com/ranjanrishikesh/taketomarket)
4
5
 
5
- A marketing operating system for Claude Code and Codex. Spec-driven campaigns with positioning-as-invariant enforcement, quality gate walls, and compound learnings.
6
+ **Marketing OS for developerneurs and solopreneurs.** Built for engineers shipping products with zero marketing experience required.
7
+
8
+ takeToMarket is a Claude Code / Codex skill set that brings spec-driven development to marketing. Every campaign, asset, and channel is a spec-driven unit with a verifiable outcome metric and a positioning-invariant quality gate wall.
6
9
 
7
10
  **Core invariant:** Every marketing asset ships with a verifiable outcome metric and passes through a positioning-invariant quality gate wall — no asset ships without both, ever.
8
11
 
12
+ ## Who this is for
13
+
14
+ - **Developerneurs** — engineers building and shipping their own products.
15
+ - **Solopreneurs with engineering backgrounds** — founders who code their MVP themselves.
16
+ - **Indie hackers** — anyone shipping a product who has zero or near-zero marketing/growth experience.
17
+
18
+ If you can write code but have never built a landing page, written a positioning statement, or thought about ICPs — takeToMarket gives you the operating system. The AI does the marketing work; you stay in control of the decisions.
19
+
20
+ ## Who this is NOT for
21
+
22
+ - Full-time marketers who already have a stack — takeToMarket overlaps with what you already do.
23
+ - Agencies serving multiple clients — built for one product per workspace.
24
+ - Anyone wanting a one-click blog generator — takeToMarket is opinionated, slower, and quality-gated by design.
25
+
9
26
  ## What it is / What it isn't
10
27
 
11
28
  **IS:** A marketing OS that treats every campaign, asset, and channel as a spec-driven unit with a verifiable outcome, a positioning invariant, and a quality gate wall. Persistent state. Compound learnings. Nine-phase lifecycle enforcement.
@@ -39,12 +56,12 @@ Flags:
39
56
  /plugin install taketomarket@claude-plugins-official
40
57
  ```
41
58
 
42
- > Status: pending marketplace approval. Check https://github.com/ranjanrishikesh/takeToMarket for current status.
59
+ > Status: pending marketplace approval. Check https://github.com/ranjanrishikesh/taketomarket for current status.
43
60
 
44
61
  ### Option 3 — Direct from GitHub (Claude Code)
45
62
 
46
63
  ```
47
- /plugin marketplace add ranjanrishikesh/takeToMarket
64
+ /plugin marketplace add ranjanrishikesh/taketomarket
48
65
  /plugin install taketomarket
49
66
  ```
50
67
 
@@ -53,7 +70,7 @@ This uses the Claude Code plugin system to install directly from the GitHub repo
53
70
  ### Option 4 — Manual (advanced)
54
71
 
55
72
  ```bash
56
- git clone https://github.com/ranjanrishikesh/takeToMarket
73
+ git clone https://github.com/ranjanrishikesh/taketomarket
57
74
  cd takeToMarket
58
75
  node install.js
59
76
  ```
@@ -98,34 +115,40 @@ All non-Claude runtimes also support `~/.agents/skills/` as a universal path. Se
98
115
 
99
116
  | Command | Description |
100
117
  |---------|-------------|
101
- | `/ttm-aeo-check` | Check citation status across AI engines for a query |
102
118
  | `/ttm-affiliate-kit` | Generate creative kit for affiliate partners |
103
119
  | `/ttm-archive` | Archive a completed campaign, finalize state, and update LEARNINGS.md |
104
120
  | `/ttm-brand-refresh` | Update BRAND.md with new proof points and deprecate expired ones |
105
121
  | `/ttm-brief` | Generate a campaign brief with mandatory outcome metrics, positioning anchor, and channel mix |
106
122
  | `/ttm-competitor-scan` | On-demand competitor analysis that updates COMPETITORS.md |
107
- | `/ttm-email-preflight` | Deliverability, dark-mode, and spam-trigger scan for email assets |
123
+ | `/ttm-deploy` | Vercel deploy with auto-detected best path (git-push, Vercel CLI, API token) |
124
+ | `/ttm-discover` | Discover phase: SERP analysis, competitor content audit, ambient narrative mapping (renamed from `/ttm-research`) |
125
+ | `/ttm-email-check` | Deliverability, dark-mode, and spam-trigger check for email assets (renamed from `/ttm-email-preflight`) |
108
126
  | `/ttm-fix` | Fix phase: root cause analysis, fix brief, re-produce, re-verify (capped 3×) |
109
- | `/ttm-health` | Validate .marketing/ directory integrity, reference file completeness, and state consistency |
127
+ | `/ttm-health` | Validate .taketomarket/ directory integrity, reference file completeness, and state consistency |
128
+ | `/ttm-humanize` [MANDATORY] | Mandatory final-step humanizer for every audience-facing asset. Runnable ad-hoc. |
110
129
  | `/ttm-icp-refresh` | Update ICP.md from new customer data including calls, reviews, and feedback |
111
- | `/ttm-init` | Interview-driven onboarding that generates all .marketing/ reference files |
112
- | `/ttm-keyword-map` | Generate keyword cluster map with intent tags |
130
+ | `/ttm-init` | Interview-driven onboarding that generates all .taketomarket/ reference files |
131
+ | `/ttm-landing` | Next.js 15 + Tailwind v4 + React 19 marketing site scaffold with brand-token integration |
113
132
  | `/ttm-learn` | Extract lessons from campaign data, propose reference file edits, log to LEARNINGS.md |
133
+ | `/ttm-linkedin-post` | Manual LinkedIn post generator. Author-mimic style + post-history tracking + last-7-day news WebSearch. Final draft passes through `/ttm-humanize` |
114
134
  | `/ttm-measure` | Analyze campaign analytics against outcome metrics using attribution models |
115
135
  | `/ttm-new-campaign` | Create a new campaign directory with initialized state and reference file links |
116
136
  | `/ttm-next` | Guide user to the right next command based on current campaign state |
137
+ | `/ttm-playwright-setup` | Install walkthrough for Playwright MCP server + Chrome extension bridge. Run once per machine to unlock author scraping, competitor render, and Lighthouse/visual gates |
117
138
  | `/ttm-positioning-check` | Sample recent assets and report positioning drift percentage and analysis |
118
139
  | `/ttm-positioning-shift` | Controlled positioning change with reasoning, migration plan, and approval gate |
119
140
  | `/ttm-produce` | Generate content assets in fresh contexts loaded with brief, positioning, brand, ICP, and playbook |
141
+ | `/ttm-pseo` | Programmatic SEO route generator for blog, use-case, comparison, alternative templates. JSON CMS input. AEO + SEO optimized |
120
142
  | `/ttm-repurpose` | Fan out a long-form asset into derivatives across channels with full brief-produce-verify per derivative |
121
- | `/ttm-research` | Market and audience research including SERP, competitor content, and narrative mapping |
122
143
  | `/ttm-resume` | Resume a paused campaign at its last completed phase |
123
144
  | `/ttm-review` | Present assets with structured review checklist for human evaluation |
124
- | `/ttm-seo-audit` | Technical and content SEO audit of a URL or sitemap |
145
+ | `/ttm-seo audit\|keyword-map\|aeo` | Unified SEO + AEO toolkit with three subcommands (merged from `/ttm-aeo-check`, `/ttm-keyword-map`, `/ttm-seo-audit`) |
125
146
  | `/ttm-ship` | Generate launch checklist confirming tracking, UTMs, funnel testing, and asset finalization |
126
147
  | `/ttm-state` | Display current campaign states, decisions in flight, blockers, and experiments |
127
148
  | `/ttm-verify` | Run all applicable quality gates on every asset with pass/fail report and line-level feedback |
128
149
 
150
+ **[MANDATORY]** `/ttm-humanize` runs automatically as the final step of every producing skill (`/ttm-produce`, `/ttm-repurpose`, `/ttm-affiliate-kit`). It can also be invoked ad-hoc to humanize existing copy.
151
+
129
152
  ## Verify Installation
130
153
 
131
154
  Inside Claude Code, run:
@@ -8,6 +8,10 @@
8
8
  * Exports: cmdCampaignInit, cmdCampaignState, cmdCampaignUpdate, cmdCampaignList, cmdCampaignArchive
9
9
  */
10
10
 
11
+ // NOTE: `phase: researched` is a state-machine identifier kept for backward
12
+ // compatibility with existing STATE.md files. User-facing command names use
13
+ // /ttm-discover (the v2.3.0 rename). Full literal rename is scheduled for v2.4.0.
14
+
11
15
  'use strict';
12
16
 
13
17
  const fs = require('fs');
@@ -32,7 +36,7 @@ function resolveCampaignStatePath(slug) {
32
36
  // Re-sanitize slug (defense in depth -- caller may pass unsanitized input)
33
37
  const safe = slug.toLowerCase().replace(/[^a-z0-9-]/g, '');
34
38
  if (!safe) error('campaign slug must contain at least one alphanumeric character after sanitization');
35
- const statePath = path.resolve(process.cwd(), '.marketing', 'CAMPAIGNS', safe, 'STATE.md');
39
+ const statePath = path.resolve(process.cwd(), '.taketomarket', 'CAMPAIGNS', safe, 'STATE.md');
36
40
  const projectRoot = path.resolve(process.cwd());
37
41
  if (!statePath.startsWith(projectRoot + path.sep)) {
38
42
  error('campaign STATE.md path escapes project directory');
@@ -55,7 +59,7 @@ function cmdCampaignInit(slug, name, raw) {
55
59
  const safe = slug.toLowerCase().replace(/[^a-z0-9-]/g, '');
56
60
  if (!safe) error('campaign slug must contain at least one alphanumeric character after sanitization');
57
61
 
58
- const campaignDir = path.resolve(process.cwd(), '.marketing', 'CAMPAIGNS', safe);
62
+ const campaignDir = path.resolve(process.cwd(), '.taketomarket', 'CAMPAIGNS', safe);
59
63
  const assetsDir = path.resolve(campaignDir, 'ASSETS');
60
64
  const statePath = path.resolve(campaignDir, 'STATE.md');
61
65
 
@@ -112,7 +116,7 @@ function cmdCampaignInit(slug, name, raw) {
112
116
  'ship.shipped_at': 'null',
113
117
  'ship.checklist_result': 'null',
114
118
  };
115
- const bodyContent = `\n# Campaign: ${name}\n\nPhase: created\nNext step: Run \`/ttm-research ${safe}\` to gather market intelligence.\n`;
119
+ const bodyContent = `\n# Campaign: ${name}\n\nPhase: created\nNext step: Run \`/ttm-discover ${safe}\` to gather market intelligence.\n`;
116
120
  const stateContent = serializeFrontmatter(frontmatterObj, bodyContent);
117
121
 
118
122
  // TOCTOU-safe creation: wx flag fails atomically if file already exists
@@ -268,7 +272,7 @@ function cmdCampaignList(filter, sinceArg, raw) {
268
272
  error('--active/--shipped-since-last-audit and --since are mutually exclusive');
269
273
  }
270
274
 
271
- const campaignsDir = path.resolve(process.cwd(), '.marketing', 'CAMPAIGNS');
275
+ const campaignsDir = path.resolve(process.cwd(), '.taketomarket', 'CAMPAIGNS');
272
276
 
273
277
  // If no campaigns directory, return empty
274
278
  let entries;
@@ -303,7 +307,7 @@ function cmdCampaignList(filter, sinceArg, raw) {
303
307
  const shippedCampaigns = campaigns.filter(c => c['phase.shipped'] && c['phase.shipped'] !== 'null');
304
308
  let lastAuditTimestamp = null;
305
309
 
306
- const driftLogPath = path.resolve(process.cwd(), '.marketing', 'DRIFT-LOG.md');
310
+ const driftLogPath = path.resolve(process.cwd(), '.taketomarket', 'DRIFT-LOG.md');
307
311
  const driftLogContent = safeReadFile(driftLogPath);
308
312
  if (driftLogContent) {
309
313
  // Find last audit event timestamp in the Audit Trail table
@@ -366,8 +370,8 @@ function cmdCampaignArchive(slug, raw) {
366
370
  const safe = slug.toLowerCase().replace(/[^a-z0-9-]/g, '');
367
371
  if (!safe) error('campaign slug must contain at least one alphanumeric character after sanitization');
368
372
  const projectRoot = path.resolve(process.cwd());
369
- const srcDir = path.resolve(projectRoot, '.marketing', 'CAMPAIGNS', safe);
370
- const destDir = path.resolve(projectRoot, '.marketing', 'CAMPAIGNS', 'ARCHIVE', safe);
373
+ const srcDir = path.resolve(projectRoot, '.taketomarket', 'CAMPAIGNS', safe);
374
+ const destDir = path.resolve(projectRoot, '.taketomarket', 'CAMPAIGNS', 'ARCHIVE', safe);
371
375
 
372
376
  // Security check: both paths must be inside project root
373
377
  if (!srcDir.startsWith(projectRoot + path.sep)) {
@@ -468,7 +472,7 @@ function cmdRepurposeManifest(slug, sourceAssetId, derivatives, raw) {
468
472
  const safeSlug = slug.toLowerCase().replace(/[^a-z0-9-]/g, '');
469
473
  if (!safeSlug) error('campaign slug must contain at least one alphanumeric character after sanitization');
470
474
  const projectRoot = path.resolve(process.cwd());
471
- const manifestPath = path.resolve(projectRoot, '.marketing', 'CAMPAIGNS', safeSlug, 'MANIFEST.json');
475
+ const manifestPath = path.resolve(projectRoot, '.taketomarket', 'CAMPAIGNS', safeSlug, 'MANIFEST.json');
472
476
 
473
477
  // Security: path must stay within project root (T-10-12)
474
478
  if (!manifestPath.startsWith(projectRoot + path.sep)) {
@@ -0,0 +1,86 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ function fileExists(p) {
7
+ try { fs.accessSync(p); return true; } catch { return false; }
8
+ }
9
+
10
+ function detectStack(cwd) {
11
+ const stack = new Set();
12
+ const pkgPath = path.join(cwd, 'package.json');
13
+ if (fileExists(pkgPath)) {
14
+ stack.add('node');
15
+ try {
16
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
17
+ const deps = Object.assign({}, pkg.dependencies, pkg.devDependencies);
18
+ for (const name of Object.keys(deps)) {
19
+ if (['react', 'next', 'astro', 'svelte', 'vue', 'angular', 'remix', 'nuxt'].includes(name)) {
20
+ stack.add(name);
21
+ }
22
+ if (name === 'typescript') stack.add('typescript');
23
+ if (name === 'tailwindcss' || name === '@tailwindcss/postcss') stack.add('tailwind');
24
+ if (name === 'express' || name === 'fastify' || name === 'koa') stack.add(name);
25
+ }
26
+ } catch {}
27
+ }
28
+ if (fileExists(path.join(cwd, 'pyproject.toml')) || fileExists(path.join(cwd, 'requirements.txt'))) {
29
+ stack.add('python');
30
+ }
31
+ if (fileExists(path.join(cwd, 'go.mod'))) stack.add('go');
32
+ if (fileExists(path.join(cwd, 'Cargo.toml'))) stack.add('rust');
33
+ if (fileExists(path.join(cwd, 'Gemfile'))) stack.add('ruby');
34
+ if (fileExists(path.join(cwd, 'composer.json'))) stack.add('php');
35
+ return Array.from(stack);
36
+ }
37
+
38
+ function detectMonorepo(cwd) {
39
+ if (fileExists(path.join(cwd, 'pnpm-workspace.yaml'))) return true;
40
+ if (fileExists(path.join(cwd, 'turbo.json'))) return true;
41
+ if (fileExists(path.join(cwd, 'nx.json'))) return true;
42
+ if (fileExists(path.join(cwd, 'lerna.json'))) return true;
43
+ const pkgPath = path.join(cwd, 'package.json');
44
+ if (fileExists(pkgPath)) {
45
+ try {
46
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
47
+ if (pkg.workspaces) return true;
48
+ } catch {}
49
+ }
50
+ const cargoPath = path.join(cwd, 'Cargo.toml');
51
+ if (fileExists(cargoPath)) {
52
+ try {
53
+ const cargo = fs.readFileSync(cargoPath, 'utf8');
54
+ if (/^\s*\[workspace\]/m.test(cargo)) return true;
55
+ } catch {}
56
+ }
57
+ return false;
58
+ }
59
+
60
+ function detectFeatureCandidates(cwd) {
61
+ const candidates = [];
62
+ for (const base of ['src', 'app', 'lib', 'packages', 'apps', 'modules']) {
63
+ const p = path.join(cwd, base);
64
+ if (!fileExists(p)) continue;
65
+ try {
66
+ const entries = fs.readdirSync(p, { withFileTypes: true });
67
+ for (const e of entries) {
68
+ if (e.isDirectory() && !e.name.startsWith('.') && !e.name.startsWith('_')) {
69
+ candidates.push(e.name);
70
+ }
71
+ }
72
+ } catch {}
73
+ }
74
+ return Array.from(new Set(candidates));
75
+ }
76
+
77
+ function scanCodebase(cwd) {
78
+ return {
79
+ cwd,
80
+ stack: detectStack(cwd),
81
+ monorepo: detectMonorepo(cwd),
82
+ featureCandidates: detectFeatureCandidates(cwd),
83
+ };
84
+ }
85
+
86
+ module.exports = { scanCodebase, detectStack, detectMonorepo, detectFeatureCandidates };
@@ -0,0 +1,129 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ // Relative path fragment. Always prepended with `cwd` in readConfig/writeConfig.
7
+ const CONFIG_RELATIVE_PATH = path.join('.taketomarket', 'CONFIG.md');
8
+
9
+ const DEFAULTS = {
10
+ yolo: false,
11
+ inline_education: true,
12
+ landing_path: null,
13
+ brand_path: '.taketomarket/brand',
14
+ };
15
+
16
+ function parseYamlFrontmatter(text) {
17
+ if (!text.startsWith('---\n')) return {};
18
+ const end = text.indexOf('\n---', 4);
19
+ if (end < 0) return {};
20
+ const yaml = text.slice(4, end);
21
+ const out = {};
22
+ const lines = yaml.split('\n');
23
+ let i = 0;
24
+ while (i < lines.length) {
25
+ const line = lines[i];
26
+ // Match scalar key: `key: value`
27
+ const scalar = line.match(/^([a-z_][a-z0-9_]*):\s*(.*)$/);
28
+ if (!scalar) { i++; continue; }
29
+ const key = scalar[1];
30
+ let val = scalar[2].trim();
31
+ if (val === '') {
32
+ // Possible nested object: gather indented `key.subkey: value` form on following lines
33
+ // We support a single-level nested object whose keys are listed as ` subkey: value`.
34
+ const obj = {};
35
+ let j = i + 1;
36
+ let foundChild = false;
37
+ while (j < lines.length) {
38
+ const child = lines[j].match(/^[ \t]+([a-zA-Z0-9_\-]+):\s*(.*)$/);
39
+ if (!child) break;
40
+ foundChild = true;
41
+ let cval = child[2].trim();
42
+ if (cval === 'true') cval = true;
43
+ else if (cval === 'false') cval = false;
44
+ else if (cval === 'null' || cval === '~' || cval === '') cval = null;
45
+ else if (/^-?\d+$/.test(cval)) cval = parseInt(cval, 10);
46
+ obj[child[1]] = cval;
47
+ j++;
48
+ }
49
+ if (foundChild) {
50
+ out[key] = obj;
51
+ i = j;
52
+ continue;
53
+ }
54
+ out[key] = null;
55
+ i++;
56
+ continue;
57
+ }
58
+ if (val === 'true') val = true;
59
+ else if (val === 'false') val = false;
60
+ else if (val === 'null' || val === '~') val = null;
61
+ else if (/^-?\d+$/.test(val)) val = parseInt(val, 10);
62
+ out[key] = val;
63
+ i++;
64
+ }
65
+ return out;
66
+ }
67
+
68
+ function serializeYamlFrontmatter(obj) {
69
+ const lines = ['---'];
70
+ for (const [k, v] of Object.entries(obj)) {
71
+ if (v === null) lines.push(`${k}: null`);
72
+ else if (typeof v === 'boolean') lines.push(`${k}: ${v}`);
73
+ else if (v && typeof v === 'object' && !Array.isArray(v)) {
74
+ const keys = Object.keys(v);
75
+ if (keys.length === 0) {
76
+ lines.push(`${k}: {}`);
77
+ } else {
78
+ lines.push(`${k}:`);
79
+ for (const sk of keys) {
80
+ const sv = v[sk];
81
+ if (sv === null) lines.push(` ${sk}: null`);
82
+ else if (typeof sv === 'boolean') lines.push(` ${sk}: ${sv}`);
83
+ else lines.push(` ${sk}: ${sv}`);
84
+ }
85
+ }
86
+ }
87
+ else lines.push(`${k}: ${v}`);
88
+ }
89
+ lines.push('---');
90
+ return lines.join('\n');
91
+ }
92
+
93
+ function readConfig(cwd) {
94
+ const p = path.join(cwd, CONFIG_RELATIVE_PATH);
95
+ if (!fs.existsSync(p)) return { ...DEFAULTS };
96
+ const body = fs.readFileSync(p, 'utf8');
97
+ return { ...DEFAULTS, ...parseYamlFrontmatter(body) };
98
+ }
99
+
100
+ function writeConfig(cwd, cfg) {
101
+ const dir = path.join(cwd, '.taketomarket');
102
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
103
+ const merged = { ...DEFAULTS, ...cfg };
104
+ const text = serializeYamlFrontmatter(merged) + '\n\n# takeToMarket Config\n\nManaged by `/ttm-config`. Do not edit the frontmatter manually unless you know what you are doing.\n';
105
+ fs.writeFileSync(path.join(cwd, CONFIG_RELATIVE_PATH), text);
106
+ }
107
+
108
+ function setConfig(cwd, key, value) {
109
+ const cfg = readConfig(cwd);
110
+ cfg[key] = value;
111
+ writeConfig(cwd, cfg);
112
+ }
113
+
114
+ function markFirstRunSeen(cwd, skillName) {
115
+ const cfg = readConfig(cwd);
116
+ const seen = (cfg.first_run_seen && typeof cfg.first_run_seen === 'object')
117
+ ? { ...cfg.first_run_seen }
118
+ : {};
119
+ seen[skillName] = true;
120
+ cfg.first_run_seen = seen;
121
+ writeConfig(cwd, cfg);
122
+ }
123
+
124
+ function isFirstRunSeen(cwd, skillName) {
125
+ const cfg = readConfig(cwd);
126
+ return !!(cfg.first_run_seen && typeof cfg.first_run_seen === 'object' && cfg.first_run_seen[skillName] === true);
127
+ }
128
+
129
+ module.exports = { readConfig, writeConfig, setConfig, markFirstRunSeen, isFirstRunSeen, DEFAULTS };
@@ -0,0 +1,36 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { execSync } = require('child_process');
6
+
7
+ function commandExists(cmd) {
8
+ try {
9
+ execSync(`command -v ${cmd}`, { stdio: 'ignore' });
10
+ return true;
11
+ } catch {
12
+ return false;
13
+ }
14
+ }
15
+
16
+ function detectDeployPath(cwd, opts = {}) {
17
+ const env = opts.env !== undefined ? opts.env : process.env;
18
+ const hasCli = opts.hasCli !== undefined ? opts.hasCli : commandExists('vercel');
19
+ const available = [];
20
+
21
+ const vercelProjectJson = path.join(cwd, '.vercel', 'project.json');
22
+ const gitConnected = fs.existsSync(vercelProjectJson);
23
+
24
+ if (gitConnected) available.push('git-push');
25
+ if (hasCli) available.push('cli');
26
+ if (env.VERCEL_TOKEN) available.push('api-token');
27
+
28
+ let preferred = null;
29
+ if (available.includes('git-push')) preferred = 'git-push';
30
+ else if (available.includes('cli')) preferred = 'cli';
31
+ else if (available.includes('api-token')) preferred = 'api-token';
32
+
33
+ return { available, preferred, gitConnected, hasCli, hasToken: !!env.VERCEL_TOKEN };
34
+ }
35
+
36
+ module.exports = { detectDeployPath };
@@ -67,7 +67,7 @@ function cmdDeviationAppend(slug, gate, result, justification, asset, raw, extra
67
67
  // Validate slug via path.resolve to prevent traversal
68
68
  const safe = slug.toLowerCase().replace(/[^a-z0-9-]/g, '');
69
69
  const projectRoot = path.resolve(process.cwd());
70
- const campaignDir = path.resolve(projectRoot, '.marketing', 'CAMPAIGNS', safe);
70
+ const campaignDir = path.resolve(projectRoot, '.taketomarket', 'CAMPAIGNS', safe);
71
71
  if (!campaignDir.startsWith(projectRoot)) {
72
72
  error('campaign path escapes project directory');
73
73
  }
@@ -46,7 +46,7 @@ function sanitizeDetails(text) {
46
46
  */
47
47
  function resolveDriftLogPath() {
48
48
  const projectRoot = path.resolve(process.cwd());
49
- const driftLogPath = path.resolve(process.cwd(), '.marketing', 'DRIFT-LOG.md');
49
+ const driftLogPath = path.resolve(process.cwd(), '.taketomarket', 'DRIFT-LOG.md');
50
50
  if (!driftLogPath.startsWith(projectRoot)) {
51
51
  error('DRIFT-LOG.md path escapes project directory');
52
52
  }
@@ -83,7 +83,7 @@ function ensureDriftLog(driftLogPath) {
83
83
  '<!-- DEPRECATION ENTRIES BELOW THIS LINE -->',
84
84
  ].join('\n');
85
85
 
86
- // Ensure .marketing directory exists
86
+ // Ensure .taketomarket directory exists
87
87
  const dir = path.dirname(driftLogPath);
88
88
  fs.mkdirSync(dir, { recursive: true });
89
89
 
@@ -96,7 +96,7 @@ function ensureDriftLog(driftLogPath) {
96
96
  }
97
97
 
98
98
  /**
99
- * Append a drift event entry to .marketing/DRIFT-LOG.md.
99
+ * Append a drift event entry to .taketomarket/DRIFT-LOG.md.
100
100
  *
101
101
  * @param {string} eventType - Event type (shift, audit, deviation)
102
102
  * @param {string} source - Source command or campaign that triggered the event
@@ -157,7 +157,7 @@ function cmdDriftLogAppend(eventType, source, details, affectedCount, raw) {
157
157
  }
158
158
 
159
159
  /**
160
- * Append a deprecation entry to .marketing/DRIFT-LOG.md Deprecation Backlog.
160
+ * Append a deprecation entry to .taketomarket/DRIFT-LOG.md Deprecation Backlog.
161
161
  *
162
162
  * @param {string} asset - Asset identifier
163
163
  * @param {string} campaign - Campaign slug