taketomarket 1.0.1 → 2.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taketomarket",
3
- "version": "0.1.0",
3
+ "version": "2.1.0",
4
4
  "description": "Marketing operating system for Claude Code. Spec-driven campaigns with positioning-as-invariant enforcement, quality gate walls, and compound learnings.",
5
5
  "author": {
6
6
  "name": "takeToMarket"
package/README.md CHANGED
@@ -2,418 +2,118 @@
2
2
 
3
3
  A marketing operating system for Claude Code and Codex. Spec-driven campaigns with positioning-as-invariant enforcement, quality gate walls, and compound learnings.
4
4
 
5
- takeToMarket is not a content generator -- it is a marketing operating system that treats every campaign, asset, and channel as a spec-driven unit with a verifiable outcome, a positioning invariant, and a quality gate wall. Install it into your Claude Code or Codex environment and run marketing campaigns through a 9-phase lifecycle with persistent state, compound learnings, and automated verification.
5
+ **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.
6
6
 
7
- **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.
7
+ ## What it is / What it isn't
8
+
9
+ **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.
10
+
11
+ **IS NOT:** A content generator, one-click blog writer, or social media scheduler. Not a replacement for marketers — it is the operating system a marketer uses to ship more, drift less, and compound learnings. Not a reporting dashboard. Not a scheduler. takeToMarket enforces discipline; it does not generate random content.
12
+
13
+ ## Requirements
14
+
15
+ - Node.js 18+
16
+ - Claude Code v1.x+ (or Codex)
17
+ - git
8
18
 
9
19
  ## Installation
10
20
 
11
- ### Via npm (recommended)
21
+ ### Option 1 — npx (recommended)
12
22
 
13
23
  ```bash
14
24
  npx taketomarket
15
25
  ```
16
26
 
17
- The installer detects your runtime automatically (Claude Code or Codex) by checking for `~/.claude/` or `~/.codex/` directories.
18
-
19
- Override detection with an explicit flag:
27
+ Interactive: asks which tool(s) you use, installs to all selected runtimes in one pass.
20
28
 
21
- ```bash
22
- npx taketomarket --runtime codex
23
- ```
29
+ Flags:
30
+ - `--yes` or `-y` — skip confirmation (for CI/scripts)
31
+ - `--check` — show install status without installing
32
+ - `--runtime <claude|codex>` — skip interactive prompt, install to one runtime
24
33
 
25
- Validate the package without writing files:
34
+ ### Option 2 Claude Code plugin marketplace
26
35
 
27
- ```bash
28
- npx taketomarket --dry-run
36
+ ```
37
+ /plugin install taketomarket@claude-plugins-official
29
38
  ```
30
39
 
31
- After installation, the installer runs structural validation and prints a pass/fail report for each component (directories, plugin.json, SKILL.md files). If all checks pass, you are ready to go.
40
+ > Status: pending marketplace approval. Check https://github.com/ranjanrishikesh/takeToMarket for current status.
32
41
 
33
- ### Via git clone
42
+ ### Option 3 — Direct from GitHub
34
43
 
35
- ```bash
36
- git clone https://github.com/taketomarket/taketomarket.git
37
- cd taketomarket
38
- ```
44
+ > Status: pending verification. Use Option 1 or Option 4 in the meantime.
39
45
 
40
- Then copy each component into the Claude Code plugin directory:
46
+ ### Option 4 Manual (advanced)
41
47
 
42
48
  ```bash
43
- mkdir -p ~/.claude/plugins/taketomarket
44
- cp -r .claude-plugin ~/.claude/plugins/taketomarket/.claude-plugin
45
- cp -r skills ~/.claude/plugins/taketomarket/skills
46
- cp -r workflows ~/.claude/plugins/taketomarket/workflows
47
- cp -r templates ~/.claude/plugins/taketomarket/templates
48
- cp -r references ~/.claude/plugins/taketomarket/references
49
- cp -r playbooks ~/.claude/plugins/taketomarket/playbooks
50
- cp -r gates ~/.claude/plugins/taketomarket/gates
51
- cp -r bin ~/.claude/plugins/taketomarket/bin
52
- cp settings.json ~/.claude/plugins/taketomarket/
49
+ git clone https://github.com/ranjanrishikesh/takeToMarket
50
+ cd takeToMarket
51
+ node install.js
53
52
  ```
54
53
 
55
- For Codex, replace `~/.claude/` with `~/.codex/` in the paths above.
56
-
57
- ### Verify Installation
58
-
59
- Run `/ttm-health` inside Claude Code to confirm the setup. It validates directory integrity, reference file presence, and state consistency.
60
-
61
54
  ## Quick Start
62
55
 
63
- Once installed, run these commands inside Claude Code (or Codex) to execute your first campaign:
64
-
65
- 1. **Initialize your workspace:** `/ttm-init`
66
- Guided interview about your product, brand, audience, channels, competitors, and metrics. Generates all reference files in `.marketing/`.
67
-
68
- 2. **Create a campaign:** `/ttm-new-campaign spring-launch`
69
- Creates `.marketing/CAMPAIGNS/spring-launch/` with initialized state and reference file links.
70
-
71
- 3. **Research the market:** `/ttm-research spring-launch`
72
- Market/audience research with SERP analysis, competitor content mapping, and ambient narrative capture. Supports web search MCP tools or manual paste.
73
-
74
- 4. **Write the brief:** `/ttm-brief spring-launch`
75
- Generates a campaign brief with mandatory outcome metrics, positioning anchor, hook, proof points, channel mix, and asset list. Refuses to proceed without both output and outcome metrics.
76
-
77
- 5. **Produce content:** `/ttm-produce spring-launch`
78
- Generates each asset in a fresh isolated context loaded with brief + positioning + brand + ICP + playbook. Hero asset first, then derivatives in wave-parallel.
79
-
80
- 6. **Verify quality:** `/ttm-verify spring-launch`
81
- Runs every asset through the 10-gate quality wall in a separate context from production (preventing self-evaluation bias). Outputs pass/fail per gate with line-level feedback.
82
-
83
- 7. **Human review:** `/ttm-review spring-launch`
84
- Presents assets with a structured checklist covering positioning reinforcement, outcome realism, claim substantiation, and competitor differentiation.
85
-
86
- 8. **Fix failures:** `/ttm-fix spring-launch`
87
- Root cause analysis, fix brief with preservation constraints, re-production in isolated context, re-verification. Capped at 3 attempts per asset before escalating to human review.
88
-
89
- 9. **Ship it:** `/ttm-ship spring-launch`
90
- Launch checklist with tracking confirmed, UTMs verified, funnel tested, assets finalized. Items tagged [AI] auto-check; items tagged [HUMAN] require confirmation.
91
-
92
- 10. **Measure outcomes:** `/ttm-measure spring-launch`
93
- Paste analytics data (or use MCP tools). Analysis against outcome metrics using last-touch, linear, and time-decay attribution models. Outcome reported first.
94
-
95
- 11. **Extract learnings:** `/ttm-learn spring-launch`
96
- Lessons extracted with proposed reference file edits (each requiring human approval). Root-cause taxonomy entries and winning patterns logged to LEARNINGS.md for future campaigns.
97
-
98
- ## How It Works
99
-
100
- takeToMarket implements a 9-phase campaign lifecycle. Each phase has a dedicated command, persistent state tracking, and explicit entry/exit criteria.
101
-
102
56
  ```
103
- /ttm-init (one-time workspace setup)
104
- |
105
- v
106
- /ttm-new-campaign <slug>
107
- |
108
- v
109
- [1. DISCOVER] -----> /ttm-research <slug>
110
- | Market research, SERP analysis,
111
- | competitor content, ambient narrative
112
- v
113
- [2. BRIEF] --------> /ttm-brief <slug>
114
- | Outcome metrics, positioning anchor,
115
- | hook, proof points, channel mix
116
- v
117
- [3. PRODUCE] ------> /ttm-produce <slug>
118
- | Fresh context per asset, hero-first,
119
- | wave-parallel execution, playbook-loaded
120
- v
121
- [4. VERIFY] -------> /ttm-verify <slug>
122
- | 10-gate quality wall, separate context,
123
- | line-level feedback, deviation options
124
- v
125
- [5. REVIEW] -------> /ttm-review <slug>
126
- | Structured human checklist, per-asset
127
- | approve/reject/revise decisions
128
- v
129
- [6. FIX] ----------> /ttm-fix <slug> (if needed)
130
- | Root-cause analysis, 3-attempt cap,
131
- | preservation constraints, auto-escalate
132
- v
133
- [7. SHIP] ---------> /ttm-ship <slug>
134
- | Launch checklist, tracking confirmed,
135
- | UTMs verified, funnel tested
136
- v
137
- [8. MEASURE] ------> /ttm-measure <slug>
138
- | Paste analytics, 3 attribution models,
139
- | outcome-first reporting
140
- v
141
- [9. LEARN] --------> /ttm-learn <slug>
142
- Lessons extracted, reference files
143
- updated, patterns logged to LEARNINGS.md
57
+ /taketomarket:ttm-init # set up workspace (one time)
58
+ /taketomarket:ttm-new-campaign # create first campaign
59
+ /taketomarket:ttm-produce # run production wave
144
60
  ```
145
61
 
146
- ### Five Principles
147
-
148
- 1. **Positioning is the invariant.** POSITIONING.md loads into every phase context. It is read-only during campaigns. Drift is detected and flagged. Changing positioning requires an explicit shift workflow with migration planning and human approval.
149
-
150
- 2. **Outcome over output.** Every campaign brief requires both an output metric (what gets published) and an outcome metric (what business result is expected). Measure phase reports outcome first.
62
+ ## Campaign Lifecycle
151
63
 
152
- 3. **Quality gate wall.** No asset ships without passing all applicable gates. Tier 1 gates (positioning drift, claim accuracy, outcome alignment) are blocking. Tier 2 gates are advisory.
153
-
154
- 4. **Fix is not retry.** When assets fail review, the system performs root cause analysis and generates a specific fix brief with preservation constraints. It does not simply re-run production.
155
-
156
- 5. **Compound learnings.** Every campaign feeds back into reference files. LEARNINGS.md accumulates root-cause taxonomy entries and winning patterns that load into the Brief phase of future campaigns.
64
+ 1. **Init** set up workspace and reference files
65
+ 2. **New Campaign** — create campaign directory with initialized state
66
+ 3. **Research** discover market, audience, and ambient narrative
67
+ 4. **Brief** — generate brief with mandatory outcome metrics
68
+ 5. **Produce** generate assets in isolated contexts with full reference loading
69
+ 6. **Review** — human quality evaluation with structured checklist
70
+ 7. **Fix** — root cause analysis, re-produce, re-verify (capped 3×)
71
+ 8. **Verify** — quality gate wall check across all assets
72
+ 9. **Ship** — launch checklist confirming tracking, UTMs, funnel testing
73
+ 10. **Measure** — analytics vs outcome metrics with attribution models
74
+ 11. **Learn** — extract lessons, propose reference file edits, log to LEARNINGS.md
157
75
 
158
76
  ## Command Reference
159
77
 
160
- ### Setup
161
-
162
- | Command | Description |
163
- |---------|-------------|
164
- | `/ttm-init` | Interview-driven onboarding that generates all `.marketing/` reference files from structured questioning with specificity validation |
165
- | `/ttm-new-campaign <slug>` | Create a campaign directory with initialized state and reference file links |
166
-
167
- ### Campaign Lifecycle
168
-
169
- | Command | Description |
170
- |---------|-------------|
171
- | `/ttm-research <slug>` | Discover phase: market/audience research, SERP analysis, competitor content mapping, ambient narrative capture |
172
- | `/ttm-brief <slug>` | Generate campaign brief with outcome metric enforcement, positioning anchor, channel mix, and asset list |
173
- | `/ttm-produce <slug>` | Produce content assets in fresh isolated contexts loaded with brief, positioning, brand, ICP, and playbook |
174
- | `/ttm-verify <slug>` | Run all applicable quality gates on every asset with pass/fail report and line-level feedback |
175
- | `/ttm-review <slug>` | Present assets with structured review checklist for human evaluation and per-asset decisions |
176
- | `/ttm-fix <slug>` | Root cause analysis, fix brief, re-produce in isolated context, re-verify (3-attempt cap per asset) |
177
- | `/ttm-ship <slug>` | Launch checklist with tracking, UTM, and funnel verification; items tagged [AI]/[HUMAN] |
178
- | `/ttm-measure <slug>` | Analyze pasted analytics against outcome metrics using last-touch, linear, and time-decay attribution |
179
- | `/ttm-learn <slug>` | Extract lessons, propose reference file edits with human approval, log root-cause taxonomy entries |
180
-
181
- ### State Management
182
-
183
- | Command | Description |
184
- |---------|-------------|
185
- | `/ttm-state` | Dashboard showing campaign states, decisions in flight, blockers, and experiments |
186
- | `/ttm-resume <slug>` | Resume a paused campaign at its last completed phase |
187
- | `/ttm-archive <slug>` | Finalize a completed campaign, move to archive, update LEARNINGS.md |
188
- | `/ttm-health` | Validate `.marketing/` directory integrity, reference file completeness, and state consistency |
189
- | `/ttm-next` | Guidance on the right next command based on current campaign state |
190
-
191
- ### Positioning
192
-
193
78
  | Command | Description |
194
79
  |---------|-------------|
195
- | `/ttm-positioning-check` | Drift audit across recent assets with drift percentage, type categorization, and bleeding analysis |
196
- | `/ttm-positioning-shift` | Controlled repositioning with migration plan, deprecation schedule, and human approval gate |
197
-
198
- ### Reference Management
199
-
200
- | Command | Description |
201
- |---------|-------------|
202
- | `/ttm-brand-refresh` | Update BRAND.md proof points and voice guidelines; validates against positioning invariant |
203
- | `/ttm-icp-refresh` | Update ICP.md from new customer data with positioning invariant validation |
204
- | `/ttm-competitor-scan` | On-demand competitor analysis with web search or manual paste |
205
-
206
- ### Discipline Utilities
207
-
208
- | Command | Description |
209
- |---------|-------------|
210
- | `/ttm-seo-audit [url]` | Technical and content SEO audit with actionable report and priority ranking |
211
- | `/ttm-aeo-check [query]` | AI engine citation status check across major AI answer platforms |
212
- | `/ttm-keyword-map` | Keyword cluster map with intent tags (informational, commercial, transactional, navigational) |
213
- | `/ttm-email-preflight` | Deliverability, spam-trigger, and dark-mode rendering scan |
214
- | `/ttm-affiliate-kit` | Generate creative kit for affiliate partners with approved messaging and assets |
215
- | `/ttm-repurpose <asset>` | Fan out a long-form asset into derivatives across channels with full brief-produce-verify per derivative |
216
-
217
- **Total: 27 commands** across 6 categories.
218
-
219
- ## Quality Gate Wall
220
-
221
- Every asset passes through 10 base quality gates before shipping. Discipline playbooks add channel-specific gates on top.
222
-
223
- ### Base Gates
224
-
225
- | # | Gate | Tier | Behavior |
226
- |---|------|------|----------|
227
- | 1 | **Positioning Drift** | Tier 1 | Blocking -- asset must align with POSITIONING.md invariant |
228
- | 2 | **Claim Accuracy** | Tier 1 | Blocking -- every claim must have a source or proof point |
229
- | 3 | **Voice Drift** | Tier 2 | Advisory -- flags voice/tone inconsistency with BRAND.md |
230
- | 4 | **Outcome Alignment** | Tier 1 | Blocking -- asset must serve the campaign outcome metric |
231
- | 5 | **Funnel Integrity** | Tier 2 | Advisory -- CTA and funnel step match the campaign stage |
232
- | 6 | **UTM Hygiene** | Tier 2 | Advisory -- all links have valid, consistent UTM parameters |
233
- | 7 | **Compliance** | Tier 2 | Advisory -- legal disclaimers, required disclosures present |
234
- | 8 | **Competitor Collision** | Tier 2 | Advisory -- avoids using competitor claims or positioning |
235
- | 9 | **ICP Fit** | Tier 2 | Advisory -- content addresses the right audience segment |
236
- | 10 | **Format Correctness** | Tier 2 | Advisory -- meets channel-specific format requirements |
237
-
238
- ### Tier Behavior
239
-
240
- - **Tier 1 (blocking):** Asset cannot ship until the gate passes. Failing a Tier 1 gate triggers the fix loop.
241
- - **Tier 2 (advisory):** Flagged for review but does not block shipping. Each failure offers three options: Correct, Accept+log, or Escalate to positioning shift.
242
-
243
- ### Meta-Gates
244
-
245
- Multi-campaign portfolio checks run after per-asset gates:
246
-
247
- - **Portfolio Balance** -- channel mix across active campaigns
248
- - **Calendar Collision** -- overlapping launch dates or audience saturation
249
- - **Theme Consistency** -- messaging coherence across concurrent campaigns
250
- - **Learning Plan** -- measurement infrastructure coverage
251
-
252
- ## Playbooks
253
-
254
- 10 discipline playbooks extend the base quality gate wall with channel-specific knowledge, format rules, and additional gates:
255
-
256
- | Playbook | Focus Areas |
257
- |----------|-------------|
258
- | **SEO** | Title/H1 alignment, search-intent match, schema markup, internal-link density, thin-content detection |
259
- | **AEO** | Quote-worthy sentences, FAQ/HowTo schema, cross-domain fact consistency, AI citation optimization |
260
- | **Email** | Subject/preview spam-trigger scan, dark-mode rendering, unsubscribe presence, deliverability checks |
261
- | **LinkedIn** | Professional tone, opener hooks, native vs link format, engagement optimization |
262
- | **Social** | Platform-specific rules, visual ratios, hashtag strategy, engagement hooks |
263
- | **YouTube** | Thumbnail optimization, retention hooks, chapter markers, description SEO |
264
- | **Paid Ads** | Headline character limits, ad relevance scoring, landing page alignment |
265
- | **Affiliate** | Partner-safe messaging, commission disclosure, creative kit completeness |
266
- | **PR/Media** | News angle clarity, quote-readiness, fact-checking rigor, embargo handling |
267
- | **Events** | Session flow, attendee journey mapping, follow-up sequence planning |
268
-
269
- All playbooks inherit from `playbooks/base.md` and extend it with additive gates. The base contract ensures consistent evaluation across disciplines.
270
-
271
- ## Architecture
272
-
80
+ | `/taketomarket:ttm-aeo-check` | Check citation status across AI engines for a query |
81
+ | `/taketomarket:ttm-affiliate-kit` | Generate creative kit for affiliate partners |
82
+ | `/taketomarket:ttm-archive` | Archive a completed campaign, finalize state, and update LEARNINGS.md |
83
+ | `/taketomarket:ttm-brand-refresh` | Update BRAND.md with new proof points and deprecate expired ones |
84
+ | `/taketomarket:ttm-brief` | Generate a campaign brief with mandatory outcome metrics, positioning anchor, and channel mix |
85
+ | `/taketomarket:ttm-competitor-scan` | On-demand competitor analysis that updates COMPETITORS.md |
86
+ | `/taketomarket:ttm-email-preflight` | Deliverability, dark-mode, and spam-trigger scan for email assets |
87
+ | `/taketomarket:ttm-fix` | Fix phase: root cause analysis, fix brief, re-produce, re-verify (capped 3×) |
88
+ | `/taketomarket:ttm-health` | Validate .marketing/ directory integrity, reference file completeness, and state consistency |
89
+ | `/taketomarket:ttm-icp-refresh` | Update ICP.md from new customer data including calls, reviews, and feedback |
90
+ | `/taketomarket:ttm-init` | Interview-driven onboarding that generates all .marketing/ reference files |
91
+ | `/taketomarket:ttm-keyword-map` | Generate keyword cluster map with intent tags |
92
+ | `/taketomarket:ttm-learn` | Extract lessons from campaign data, propose reference file edits, log to LEARNINGS.md |
93
+ | `/taketomarket:ttm-measure` | Analyze campaign analytics against outcome metrics using attribution models |
94
+ | `/taketomarket:ttm-new-campaign` | Create a new campaign directory with initialized state and reference file links |
95
+ | `/taketomarket:ttm-next` | Guide user to the right next command based on current campaign state |
96
+ | `/taketomarket:ttm-positioning-check` | Sample recent assets and report positioning drift percentage and analysis |
97
+ | `/taketomarket:ttm-positioning-shift` | Controlled positioning change with reasoning, migration plan, and approval gate |
98
+ | `/taketomarket:ttm-produce` | Generate content assets in fresh contexts loaded with brief, positioning, brand, ICP, and playbook |
99
+ | `/taketomarket:ttm-repurpose` | Fan out a long-form asset into derivatives across channels with full brief-produce-verify per derivative |
100
+ | `/taketomarket:ttm-research` | Market and audience research including SERP, competitor content, and narrative mapping |
101
+ | `/taketomarket:ttm-resume` | Resume a paused campaign at its last completed phase |
102
+ | `/taketomarket:ttm-review` | Present assets with structured review checklist for human evaluation |
103
+ | `/taketomarket:ttm-seo-audit` | Technical and content SEO audit of a URL or sitemap |
104
+ | `/taketomarket:ttm-ship` | Generate launch checklist confirming tracking, UTMs, funnel testing, and asset finalization |
105
+ | `/taketomarket:ttm-state` | Display current campaign states, decisions in flight, blockers, and experiments |
106
+ | `/taketomarket:ttm-verify` | Run all applicable quality gates on every asset with pass/fail report and line-level feedback |
107
+
108
+ ## Verify Installation
109
+
110
+ Inside Claude Code, run:
273
111
  ```
274
- takeToMarket/
275
- .claude-plugin/
276
- plugin.json Plugin manifest (name, version, description, keywords)
277
-
278
- skills/ 27 SKILL.md command stubs (routing only, <30 lines each)
279
- ttm-init/SKILL.md
280
- ttm-new-campaign/SKILL.md
281
- ttm-research/SKILL.md
282
- ttm-brief/SKILL.md
283
- ttm-produce/SKILL.md
284
- ttm-verify/SKILL.md
285
- ttm-review/SKILL.md
286
- ttm-fix/SKILL.md
287
- ttm-ship/SKILL.md
288
- ttm-measure/SKILL.md
289
- ttm-learn/SKILL.md
290
- ttm-state/SKILL.md
291
- ttm-resume/SKILL.md
292
- ttm-archive/SKILL.md
293
- ttm-health/SKILL.md
294
- ttm-next/SKILL.md
295
- ttm-positioning-check/SKILL.md
296
- ttm-positioning-shift/SKILL.md
297
- ttm-brand-refresh/SKILL.md
298
- ttm-icp-refresh/SKILL.md
299
- ttm-competitor-scan/SKILL.md
300
- ttm-seo-audit/SKILL.md
301
- ttm-aeo-check/SKILL.md
302
- ttm-keyword-map/SKILL.md
303
- ttm-email-preflight/SKILL.md
304
- ttm-affiliate-kit/SKILL.md
305
- ttm-repurpose/SKILL.md
306
-
307
- workflows/
308
- setup/ /ttm-init interview workflow
309
- lifecycle/ Campaign phase workflows (research, brief, produce,
310
- verify, review, fix, ship, measure, learn)
311
- reference-mgmt/ Reference file management (positioning-check,
312
- positioning-shift, brand-refresh, icp-refresh,
313
- competitor-scan)
314
- discipline/ Discipline utility workflows (seo-audit, aeo-check,
315
- keyword-map, email-preflight, affiliate-kit, repurpose)
316
- utility/ State management workflows (state, resume, archive,
317
- health, next)
318
-
319
- templates/ Markdown templates for generated files
320
- reference-files/ Reference file templates (brand, icp, competitors, etc.)
321
- campaign-brief.md Brief template with outcome/output metrics
322
- production-manifest.json Asset manifest schema
323
-
324
- references/ Domain knowledge and context loading rules
325
- context-loading.md Two-tier context loading strategy
326
-
327
- playbooks/ Discipline playbook files
328
- base.md Base inheritance contract
329
- seo.md SEO discipline playbook
330
- aeo.md AEO discipline playbook
331
- email.md Email discipline playbook
332
- linkedin.md LinkedIn discipline playbook
333
- social.md Social discipline playbook
334
- youtube.md YouTube discipline playbook
335
- paid-ads.md Paid Ads discipline playbook
336
- affiliate.md Affiliate discipline playbook
337
- pr-media.md PR/Media discipline playbook
338
- events.md Events discipline playbook
339
-
340
- gates/ Gate evaluation logic and base gate definitions
341
-
342
- agents/ Subagent prompt templates (producer, verifier)
343
-
344
- bin/
345
- ttm-tools.cjs CLI entry point (single file, subcommand pattern)
346
- lib/
347
- campaign.cjs Campaign state operations, MANIFEST.json management
348
- health.cjs Health validation checks
349
- core.cjs Core utility functions
350
- slug.cjs Slug generation and validation
351
- state.cjs State file operations
352
- drift-log.cjs Positioning drift logging
353
- deviation.cjs Deviation tracking
354
- commit.cjs Commit message helpers
355
-
356
- settings.json Default plugin settings
357
- install.js npm installer entry point
358
- package.json npm package config (bin: taketomarket -> install.js)
112
+ /taketomarket:ttm-health
359
113
  ```
360
114
 
361
- ### Key Patterns
362
-
363
- **Thin SKILL.md routing.** Each skill file is under 30 lines. It contains YAML frontmatter (name, description, allowed-tools, context mode) and a single instruction to read the corresponding workflow file. This keeps skills lightweight and maintainable.
364
-
365
- **500-line file limit.** No single file exceeds 500 lines. Large content is extracted to `@`-referenced files. For example, the review workflow references `review-checklist.md` instead of embedding 400 lines of checklist questions inline.
366
-
367
- **Two-tier context loading.** Compact summaries of reference files load into every phase context (positioning summary, brand overview, ICP highlights). Full documents load only during produce and verify phases where deep knowledge matters. This preserves context window budget.
368
-
369
- **Positioning-as-invariant.** POSITIONING.md is architecturally read-only during campaigns. The system detects and blocks direct edits. Changing positioning requires the explicit `/ttm-positioning-shift` workflow with migration planning, human approval, and a deprecation schedule.
370
-
371
- **Fresh context isolation.** Produce and verify run in forked contexts (`context: fork` in SKILL.md frontmatter). This prevents the producer from evaluating its own output and keeps each asset's context window loaded with only the relevant brief, positioning, brand, ICP, and playbook.
372
-
373
- **Zero runtime dependencies.** The skill itself has no npm dependencies. All code in `bin/` uses only Node.js built-in modules (`fs`, `path`, `crypto`, `os`). Node.js 18+ is the only requirement, and it is already required by Claude Code.
374
-
375
- ### How to Add a Playbook
376
-
377
- 1. Create a new file in `playbooks/` (e.g., `playbooks/webinar.md`)
378
- 2. Follow the structure in `playbooks/base.md` -- extend with additive gates
379
- 3. Add a SKILL.md stub in `skills/ttm-webinar-audit/` pointing to a new workflow
380
- 4. Add the workflow in `workflows/discipline/webinar-audit.md`
381
- 5. Register the channel in the channels template at `templates/reference-files/`
382
-
383
- ### User's Project Structure (created by /ttm-init)
384
-
385
- When a user runs `/ttm-init` in their project, it creates:
386
-
387
- ```
388
- .marketing/
389
- POSITIONING.md Positioning invariant (read-only during campaigns)
390
- BRAND.md Voice, tone, proof points
391
- ICP.md Ideal customer profiles
392
- CHANNELS.md Channel strategy and constraints
393
- COMPETITORS.md Competitor landscape
394
- METRICS.md KPI definitions and baselines
395
- CALENDAR.md Marketing calendar
396
- STATE.md Global state tracking
397
- LEARNINGS.md Accumulated campaign learnings
398
- DRIFT-LOG.md Positioning drift event log
399
- CAMPAIGNS/
400
- <slug>/ Per-campaign directory
401
- STATE.md Campaign state
402
- RESEARCH.md Market research findings
403
- BRIEF.md Campaign brief
404
- MANIFEST.json Asset manifest and tracking
405
- assets/ Produced content assets
406
- ```
407
-
408
- ## Dual-Runtime Support
409
-
410
- takeToMarket works with both Claude Code and Codex:
411
-
412
- - **Claude Code:** Skills install as a plugin at `~/.claude/plugins/taketomarket/`. Commands appear as `/ttm-*` in the Claude Code interface. Uses `AskUserQuestion` for interactive prompts with text-mode fallback.
413
- - **Codex:** Skills install at `~/.codex/plugins/taketomarket/`. An AGENTS.md file provides Codex-compatible instructions alongside CLAUDE.md. Interactive prompts fall back to numbered list format when `AskUserQuestion` is unavailable.
414
-
415
- The installer auto-detects the runtime. Both runtimes read the same SKILL.md files and execute the same workflows.
115
+ This validates directory integrity, reference file presence, and state consistency.
416
116
 
417
117
  ## License
418
118
 
419
- MIT
119
+ MIT — see [LICENSE](LICENSE).
package/install.js CHANGED
@@ -27,6 +27,160 @@ const FILES_TO_COPY = [
27
27
  'settings.json',
28
28
  ];
29
29
 
30
+ // ── Runtime Selection ─────────────────────────────────────────────────────────
31
+
32
+ const RUNTIME_MENU = ['claude', 'codex', 'cursor', 'windsurf', 'gemini'];
33
+
34
+ /**
35
+ * Parse user input from the runtime selection prompt.
36
+ * @param {string} input - Raw user input (e.g., '1,3' or '6')
37
+ * @returns {string[]|null} Array of runtime names, or null if invalid
38
+ */
39
+ function parseRuntimeChoices(input) {
40
+ const trimmed = input.trim();
41
+ if (!trimmed) return null;
42
+ if (trimmed === '6') return [...RUNTIME_MENU];
43
+ if (trimmed === '7') return ['custom'];
44
+
45
+ const parts = trimmed.split(',').map(s => s.trim());
46
+ const names = new Set();
47
+ for (const part of parts) {
48
+ const n = parseInt(part, 10);
49
+ if (isNaN(n) || n < 1 || n > 7) return null;
50
+ if (n === 7) return ['custom'];
51
+ names.add(RUNTIME_MENU[n - 1]);
52
+ }
53
+ return [...names];
54
+ }
55
+
56
+ /**
57
+ * Build the install target map for all known runtimes.
58
+ * @param {string} [homeDir] - Home directory (injectable for tests)
59
+ * @returns {Object.<string, {label, skillsDir, parentDir}>}
60
+ */
61
+ function buildRuntimeTargets(homeDir = os.homedir()) {
62
+ return {
63
+ claude: {
64
+ label: 'Claude Code',
65
+ skillsDir: path.join(homeDir, '.claude', 'skills'),
66
+ parentDir: path.join(homeDir, '.claude'),
67
+ },
68
+ codex: {
69
+ label: 'Codex (OpenAI)',
70
+ skillsDir: path.join(homeDir, '.codex', 'skills'),
71
+ parentDir: path.join(homeDir, '.codex'),
72
+ },
73
+ cursor: {
74
+ label: 'Cursor',
75
+ skillsDir: path.join(homeDir, '.cursor', 'skills'),
76
+ parentDir: path.join(homeDir, '.cursor'),
77
+ },
78
+ windsurf: {
79
+ label: 'Windsurf',
80
+ skillsDir: path.join(homeDir, '.codeium', 'windsurf', 'skills'),
81
+ parentDir: path.join(homeDir, '.codeium'),
82
+ },
83
+ gemini: {
84
+ label: 'Gemini CLI',
85
+ skillsDir: path.join(homeDir, '.gemini', 'skills'),
86
+ parentDir: path.join(homeDir, '.gemini'),
87
+ },
88
+ };
89
+ }
90
+
91
+ /**
92
+ * Interactively ask user which runtimes to install to.
93
+ * Falls back to auto-detect when stdin is not a TTY or --runtime flag is set.
94
+ * @param {string[]} args - process.argv slice
95
+ * @param {string} [homeDir]
96
+ * @returns {Promise<Array<{label, dir, parentDir, register, partial}>>}
97
+ */
98
+ async function promptRuntimeSelection(args, homeDir = os.homedir()) {
99
+ // Legacy --runtime flag: bypass interactive prompt
100
+ const runtimeIdx = args.indexOf('--runtime');
101
+ if (runtimeIdx !== -1 && runtimeIdx + 1 < args.length) {
102
+ const name = args[runtimeIdx + 1].toLowerCase();
103
+ const allTargets = buildRuntimeTargets(homeDir);
104
+ if (!allTargets[name] && name !== 'custom') {
105
+ console.warn(`Warning: Unknown runtime "${name}". Defaulting to claude.`);
106
+ return [allTargets.claude];
107
+ }
108
+ return name === 'custom' ? [] : [allTargets[name]];
109
+ }
110
+
111
+ // Non-TTY fallback: auto-detect installed runtimes
112
+ if (!process.stdin.isTTY) {
113
+ const detected = getInstalledRuntimes(homeDir);
114
+ const allTargets = buildRuntimeTargets(homeDir);
115
+ if (detected.length === 0) {
116
+ console.log('Note: No known runtimes detected. Defaulting to Claude Code.');
117
+ return [allTargets.claude];
118
+ }
119
+ console.log(`Note: Non-interactive mode. Auto-detected: ${detected.join(', ')}`);
120
+ return detected.map(name => allTargets[name]);
121
+ }
122
+
123
+ // Interactive prompt
124
+ const { createInterface } = require('node:readline');
125
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
126
+
127
+ const ask = (question) => new Promise(resolve => rl.question(question, resolve));
128
+
129
+ console.log('');
130
+ console.log('Which AI coding tool(s) are you using? (select all that apply)');
131
+ console.log('');
132
+ console.log(' 1. Claude Code');
133
+ console.log(' 2. Codex (OpenAI)');
134
+ console.log(' 3. Cursor');
135
+ console.log(' 4. Windsurf');
136
+ console.log(' 5. Gemini CLI');
137
+ console.log(' 6. All of the above');
138
+ console.log(' 7. Let me type a custom path');
139
+ console.log('');
140
+
141
+ let choices = null;
142
+ let attempts = 0;
143
+ while (choices === null && attempts < 2) {
144
+ const input = await ask('Your choice (comma-separated, e.g. 1,3): ');
145
+ choices = parseRuntimeChoices(input);
146
+ if (choices === null) {
147
+ console.log('Invalid input. Please enter numbers 1-7 separated by commas.');
148
+ attempts++;
149
+ }
150
+ }
151
+
152
+ if (choices === null) {
153
+ rl.close();
154
+ console.error('Invalid input after 2 attempts. Exiting.');
155
+ console.log('Something went wrong? File an issue: https://github.com/ranjanrishikesh/takeToMarket/issues');
156
+ process.exit(1);
157
+ }
158
+
159
+ let customPath = null;
160
+ if (choices.includes('custom')) {
161
+ customPath = await ask('Enter install path: ');
162
+ customPath = customPath.trim();
163
+ if (!customPath) {
164
+ rl.close();
165
+ console.error('Custom path cannot be empty.');
166
+ process.exit(1);
167
+ }
168
+ }
169
+
170
+ rl.close();
171
+
172
+ const allTargets = buildRuntimeTargets(homeDir);
173
+ const result = [];
174
+ for (const name of choices) {
175
+ if (name === 'custom') {
176
+ result.push({ label: 'Custom', skillsDir: customPath, parentDir: null });
177
+ } else {
178
+ result.push(allTargets[name]);
179
+ }
180
+ }
181
+ return result;
182
+ }
183
+
30
184
  // ── Runtime detection ────────────────────────────────────────────────────────
31
185
 
32
186
  /**
@@ -116,6 +270,285 @@ function copyDirSync(src, dest) {
116
270
  }
117
271
  }
118
272
 
273
+ // ── Package Base & Per-Runtime Skill Install ──────────────────────────────────
274
+
275
+ const PACKAGE_BASE_DIRS = ['workflows', 'templates', 'references', 'playbooks', 'gates', 'bin', 'agents'];
276
+ const PACKAGE_BASE_FILES = ['settings.json', 'package.json'];
277
+
278
+ /**
279
+ * Copy non-skill package files to ~/.taketomarket/ (shared across all runtimes).
280
+ * @param {string} packageRoot - Source npm package root
281
+ * @param {string} [homeDir]
282
+ */
283
+ function copyPackageBase(packageRoot, homeDir = os.homedir()) {
284
+ const dest = path.join(homeDir, '.taketomarket');
285
+ fs.mkdirSync(dest, { recursive: true });
286
+
287
+ for (const dir of PACKAGE_BASE_DIRS) {
288
+ const src = path.join(packageRoot, dir);
289
+ if (dirExists(src)) {
290
+ copyDirSync(src, path.join(dest, dir));
291
+ }
292
+ }
293
+
294
+ for (const file of PACKAGE_BASE_FILES) {
295
+ const src = path.join(packageRoot, file);
296
+ if (fileExists(src)) {
297
+ fs.copyFileSync(src, path.join(dest, file));
298
+ }
299
+ }
300
+ }
301
+
302
+ /**
303
+ * Install individual skills into a runtime's skills directory.
304
+ * Replaces ${CLAUDE_PLUGIN_ROOT} in SKILL.md with absolute path to ~/.taketomarket.
305
+ * @param {string} skillsDir - Runtime's skills base dir (e.g. ~/.claude/skills/)
306
+ * @param {string} packageRoot - Source npm package root
307
+ * @param {string} [homeDir]
308
+ * @returns {number} Number of skills installed
309
+ */
310
+ function installSkillsForRuntime(skillsDir, packageRoot, homeDir = os.homedir()) {
311
+ const packageBase = path.join(homeDir, '.taketomarket');
312
+ const srcSkillsDir = path.join(packageRoot, 'skills');
313
+ if (!dirExists(srcSkillsDir)) return 0;
314
+
315
+ fs.mkdirSync(skillsDir, { recursive: true });
316
+
317
+ let count = 0;
318
+ const entries = fs.readdirSync(srcSkillsDir, { withFileTypes: true });
319
+ for (const entry of entries) {
320
+ if (!entry.isDirectory()) continue;
321
+ const skillMdSrc = path.join(srcSkillsDir, entry.name, 'SKILL.md');
322
+ if (!fileExists(skillMdSrc)) continue;
323
+
324
+ let content = fs.readFileSync(skillMdSrc, 'utf8');
325
+ content = content.replace(/\$\{CLAUDE_PLUGIN_ROOT\}/g, packageBase);
326
+
327
+ const destSkillDir = path.join(skillsDir, entry.name);
328
+ fs.mkdirSync(destSkillDir, { recursive: true });
329
+ fs.writeFileSync(path.join(destSkillDir, 'SKILL.md'), content, 'utf8');
330
+ count++;
331
+ }
332
+ return count;
333
+ }
334
+
335
+ // ── Plugin Registration ───────────────────────────────────────────────────────
336
+
337
+ /**
338
+ * Register taketomarket in Claude Code's installed_plugins.json.
339
+ * Preserves existing plugins. Atomic write (tmp → rename).
340
+ * @param {string} installPath - Absolute path to the installed plugin directory
341
+ * @param {string} version - Plugin version string (e.g., '2.0.0')
342
+ * @param {string} [homeDir] - Home directory (defaults to os.homedir(); injectable for tests)
343
+ */
344
+ function registerPlugin(installPath, version, homeDir = os.homedir()) {
345
+ const registryPath = path.join(homeDir, '.claude', 'plugins', 'installed_plugins.json');
346
+ const pluginsDir = path.dirname(registryPath);
347
+
348
+ let registry = { version: 2, plugins: {} };
349
+ if (fileExists(registryPath)) {
350
+ try {
351
+ const parsed = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
352
+ registry = parsed;
353
+ if (!registry.plugins) registry.plugins = {};
354
+ if (!registry.version) registry.version = 2;
355
+ } catch {
356
+ fs.renameSync(registryPath, registryPath + '.bak');
357
+ console.warn(' Warning: installed_plugins.json was corrupted. Backed up to .bak and recreated.');
358
+ registry = { version: 2, plugins: {} };
359
+ }
360
+ }
361
+
362
+ const now = new Date().toISOString();
363
+ const existing = (registry.plugins['taketomarket@npm'] || [])[0];
364
+
365
+ registry.plugins['taketomarket@npm'] = [{
366
+ scope: 'user',
367
+ installPath,
368
+ version,
369
+ installedAt: existing?.installedAt ?? now,
370
+ lastUpdated: now,
371
+ gitCommitSha: null,
372
+ }];
373
+
374
+ const tmpPath = registryPath + '.tmp';
375
+ fs.mkdirSync(pluginsDir, { recursive: true });
376
+ fs.writeFileSync(tmpPath, JSON.stringify(registry, null, 2) + '\n', 'utf8');
377
+ fs.renameSync(tmpPath, registryPath);
378
+
379
+ console.log(' Registered in installed_plugins.json');
380
+ }
381
+
382
+ // ── Skill Introspection ───────────────────────────────────────────────────────
383
+
384
+ /**
385
+ * Read skill names and descriptions from skills/ subdirectory of packageRoot.
386
+ * Parses the first content line of the 'description:' YAML frontmatter field.
387
+ * @param {string} packageRoot - Root of the npm package (use PACKAGE_ROOT in production)
388
+ * @returns {Array<{name: string, description: string}>} Sorted by name
389
+ */
390
+ function readSkillDescriptions(packageRoot) {
391
+ const skillsDir = path.join(packageRoot, 'skills');
392
+ if (!dirExists(skillsDir)) return [];
393
+
394
+ const results = [];
395
+ try {
396
+ const entries = fs.readdirSync(skillsDir, { withFileTypes: true });
397
+ for (const entry of entries) {
398
+ if (!entry.isDirectory()) continue;
399
+ const skillFile = path.join(skillsDir, entry.name, 'SKILL.md');
400
+ if (!fileExists(skillFile)) continue;
401
+
402
+ const content = fs.readFileSync(skillFile, 'utf8');
403
+ const descMatch = content.match(/^description:\s*>\s*\n((?:[ \t]+.+\n?)+)/m);
404
+ let description = '';
405
+ if (descMatch) {
406
+ description = descMatch[1].split('\n')[0].trim().replace(/\.$/, '') + '.';
407
+ } else {
408
+ const inlineMatch = content.match(/^description:\s*(.+)$/m);
409
+ if (inlineMatch) description = inlineMatch[1].trim();
410
+ }
411
+
412
+ results.push({ name: entry.name, description });
413
+ }
414
+ } catch {
415
+ // ignore — skills dir may be empty or unreadable
416
+ }
417
+
418
+ return results.sort((a, b) => a.name.localeCompare(b.name));
419
+ }
420
+
421
+ /**
422
+ * Print slash commands available after install. Reads from npm package source.
423
+ * @param {string} [packageRoot] - Root of npm package (defaults to PACKAGE_ROOT = __dirname)
424
+ */
425
+ function printInstallSummary(packageRoot = PACKAGE_ROOT) {
426
+ const skills = readSkillDescriptions(packageRoot);
427
+ console.log('');
428
+ console.log(`Installation complete! ${skills.length} skills installed.`);
429
+ console.log('');
430
+ console.log('Available commands:');
431
+ for (const { name, description } of skills) {
432
+ const cmd = `/taketomarket:${name}`.padEnd(42);
433
+ console.log(` ${cmd} ${description}`);
434
+ }
435
+ console.log('');
436
+ console.log('Quick start: open any project in Claude Code and run /taketomarket:ttm-init');
437
+ }
438
+
439
+ /**
440
+ * Detect which known runtimes are installed by checking their parent directories.
441
+ * @param {string} [homeDir]
442
+ * @returns {string[]} Names of detected runtimes (subset of RUNTIME_MENU)
443
+ */
444
+ function getInstalledRuntimes(homeDir = os.homedir()) {
445
+ const targets = buildRuntimeTargets(homeDir);
446
+ return RUNTIME_MENU.filter(name => {
447
+ const t = targets[name];
448
+ return t.parentDir && dirExists(t.parentDir);
449
+ });
450
+ }
451
+
452
+ // ── Status & Confirmation ─────────────────────────────────────────────────────
453
+
454
+ /**
455
+ * Used by confirmInstall — extracted for testability.
456
+ * @param {boolean} yesFlag
457
+ * @returns {boolean} true if yesFlag is set (no prompt needed)
458
+ */
459
+ function shouldProceed(yesFlag) {
460
+ return yesFlag === true || !process.stdin.isTTY;
461
+ }
462
+
463
+ /**
464
+ * Read Claude Code install status for the checkStatus output.
465
+ * @param {string} [homeDir]
466
+ * @returns {{ installed: boolean, skillCount: number, dir: string }}
467
+ */
468
+ function getClaudeStatus(homeDir = os.homedir()) {
469
+ const skillsDir = path.join(homeDir, '.claude', 'skills');
470
+ const probeSkill = path.join(skillsDir, 'ttm-init', 'SKILL.md');
471
+ const installed = fileExists(probeSkill);
472
+ if (!installed) return { installed: false, skillCount: 0, dir: skillsDir };
473
+
474
+ let skillCount = 0;
475
+ try {
476
+ skillCount = fs.readdirSync(skillsDir, { withFileTypes: true })
477
+ .filter(e => e.isDirectory() && e.name.startsWith('ttm-') && fileExists(path.join(skillsDir, e.name, 'SKILL.md')))
478
+ .length;
479
+ } catch { /* ignore */ }
480
+
481
+ return { installed: true, skillCount, dir: skillsDir };
482
+ }
483
+
484
+ /**
485
+ * Print install status for all known runtimes and exit.
486
+ * @param {string} version
487
+ * @param {string} [homeDir]
488
+ */
489
+ function checkStatus(version, homeDir = os.homedir()) {
490
+ const targets = buildRuntimeTargets(homeDir);
491
+ console.log('');
492
+ console.log(`takeToMarket v${version}`);
493
+ console.log('');
494
+
495
+ const claude = getClaudeStatus(homeDir);
496
+ if (claude.installed) {
497
+ console.log(`Claude Code: INSTALLED (${claude.skillCount} skills at ${claude.dir.replace(homeDir, '~')})`);
498
+ } else {
499
+ console.log('Claude Code: NOT INSTALLED');
500
+ }
501
+
502
+ for (const name of ['codex', 'cursor', 'windsurf', 'gemini']) {
503
+ const t = targets[name];
504
+ const label = t.label.padEnd(12);
505
+ if (t.skillsDir && dirExists(t.skillsDir)) {
506
+ console.log(`${label} INSTALLED (${t.skillsDir.replace(homeDir, '~')})`);
507
+ } else {
508
+ console.log(`${label} NOT INSTALLED`);
509
+ }
510
+ }
511
+
512
+ console.log('');
513
+ console.log('Run `npx taketomarket` to install or reinstall.');
514
+ process.exit(0);
515
+ }
516
+
517
+ /**
518
+ * Interactively confirm install. Skipped when yesFlag is true.
519
+ * @param {Array<{label, dir}>} targets
520
+ * @param {string} version
521
+ * @param {boolean} yesFlag
522
+ * @returns {Promise<boolean>}
523
+ */
524
+ async function confirmInstall(targets, version, yesFlag) {
525
+ if (shouldProceed(yesFlag)) return true;
526
+
527
+ const { createInterface } = require('node:readline');
528
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
529
+ const ask = (q) => new Promise(resolve => rl.question(q, resolve));
530
+
531
+ console.log('');
532
+ console.log(`takeToMarket v${version} — Marketing OS for AI coding tools`);
533
+ console.log('');
534
+ console.log('This will install to:');
535
+ for (const t of targets) {
536
+ const shortDir = t.skillsDir.replace(os.homedir(), '~');
537
+ console.log(` ${shortDir.padEnd(45)} (${t.label})`);
538
+ }
539
+ console.log('');
540
+
541
+ const answer = await ask('Proceed? [Y/n]: ');
542
+ rl.close();
543
+
544
+ const trimmed = answer.trim().toLowerCase();
545
+ if (trimmed === 'n') {
546
+ console.log('Installation cancelled.');
547
+ process.exit(0);
548
+ }
549
+ return true;
550
+ }
551
+
119
552
  // ── Validation ───────────────────────────────────────────────────────────────
120
553
 
121
554
  /**
@@ -168,16 +601,14 @@ function validateInstall(targetDir) {
168
601
 
169
602
  // ── Main ─────────────────────────────────────────────────────────────────────
170
603
 
171
- function main() {
604
+ async function main() {
172
605
  const args = process.argv.slice(2);
173
606
 
174
- // Check for --version / -v (D-10, D-11) — short-circuit BEFORE detectRuntime/validation.
175
607
  if (args.includes('--version') || args.includes('-v')) {
176
608
  process.stdout.write(`${VERSION}\n`);
177
609
  process.exit(0);
178
610
  }
179
611
 
180
- // Check for --help
181
612
  if (args.includes('--help') || args.includes('-h')) {
182
613
  console.log(`
183
614
  takeToMarket installer
@@ -185,35 +616,24 @@ takeToMarket installer
185
616
  Usage: npx taketomarket [options]
186
617
 
187
618
  Options:
188
- --runtime <claude|codex> Target runtime (default: auto-detect)
189
- --dry-run Validate source without writing files
619
+ --runtime <claude|codex> Target a specific runtime, skip interactive prompt
620
+ --check Show install status without installing
621
+ --yes, -y Skip confirmation prompt (for CI/scripted use)
622
+ --dry-run Validate source package without writing files
623
+ --version, -v Print version
190
624
  --help, -h Show this help message
191
625
  `);
192
626
  process.exit(0);
193
627
  }
194
628
 
195
- const DRY_RUN = args.includes('--dry-run');
196
- const runtime = detectRuntime(args);
197
-
198
- // Compute target directory using path.resolve for safety (T-10-01)
199
- const runtimeDir = runtime === 'codex' ? '.codex' : '.claude';
200
- const targetDir = path.resolve(os.homedir(), runtimeDir, 'plugins', 'taketomarket');
201
-
202
- // Verify targetDir is within home directory (T-10-01, T-10-03)
203
- const homeDir = os.homedir();
204
- if (!targetDir.startsWith(homeDir + path.sep)) {
205
- console.error('Error: Target directory resolves outside home directory. Aborting.');
206
- process.exit(1);
629
+ if (args.includes('--check')) {
630
+ checkStatus(VERSION);
631
+ // checkStatus calls process.exit(0)
207
632
  }
208
633
 
209
- console.log('');
210
- console.log(`takeToMarket installer v${VERSION}`);
211
- console.log(`Runtime: ${runtime}`);
212
- console.log(`Target: ${targetDir}`);
213
- console.log('');
214
-
215
- if (DRY_RUN) {
216
- // Validate source completeness without writing
634
+ if (args.includes('--dry-run')) {
635
+ console.log('');
636
+ console.log(`takeToMarket installer v${VERSION}`);
217
637
  console.log('[DRY RUN] Validating source package...');
218
638
  console.log('');
219
639
  const results = validateInstall(PACKAGE_ROOT);
@@ -223,59 +643,66 @@ Options:
223
643
  process.exit(0);
224
644
  }
225
645
 
226
- // Check for existing installation — remove stale files before copying (CR-02)
227
- if (dirExists(targetDir)) {
228
- console.log('Existing installation found. Removing before reinstall...');
229
- fs.rmSync(targetDir, { recursive: true, force: true });
230
- console.log('');
646
+ console.log('');
647
+ console.log(`takeToMarket installer v${VERSION}`);
648
+
649
+ const yesFlag = args.includes('--yes') || args.includes('-y');
650
+
651
+ // Runtime selection
652
+ const targets = await promptRuntimeSelection(args);
653
+
654
+ if (targets.length === 0) {
655
+ console.log('No runtimes selected. Exiting.');
656
+ process.exit(0);
231
657
  }
232
658
 
233
- // Copy directories
234
- for (const dir of DIRS_TO_COPY) {
235
- const srcDir = path.join(PACKAGE_ROOT, dir);
236
- if (dirExists(srcDir)) {
237
- console.log(` Copying ${dir}/`);
238
- copyDirSync(srcDir, path.join(targetDir, dir));
239
- } else {
240
- console.log(` Skipping ${dir}/ (not found in package)`);
659
+ // Confirmation
660
+ await confirmInstall(targets, VERSION, yesFlag);
661
+
662
+ // Copy shared package files once
663
+ console.log('');
664
+ console.log('Copying package files to ~/.taketomarket/...');
665
+ copyPackageBase(PACKAGE_ROOT);
666
+
667
+ // Install loop
668
+ const results = [];
669
+ for (const target of targets) {
670
+ console.log('');
671
+ console.log(`Installing skills to ${target.label}...`);
672
+
673
+ if (target.parentDir && !dirExists(target.parentDir)) {
674
+ console.warn(` Warning: ${target.label} not detected (${target.parentDir} not found). Installing anyway.`);
241
675
  }
242
- }
243
676
 
244
- // Copy individual files
245
- for (const file of FILES_TO_COPY) {
246
- const srcFile = path.join(PACKAGE_ROOT, file);
247
- if (fileExists(srcFile)) {
248
- console.log(` Copying ${file}`);
249
- const destFile = path.join(targetDir, file);
250
- fs.mkdirSync(path.dirname(destFile), { recursive: true });
251
- fs.copyFileSync(srcFile, destFile);
252
- } else {
253
- console.log(` Skipping ${file} (not found in package)`);
677
+ try {
678
+ const count = installSkillsForRuntime(target.skillsDir, PACKAGE_ROOT);
679
+ console.log(` Installed ${count} skills → ${target.skillsDir.replace(os.homedir(), '~')}`);
680
+ results.push({ target, success: true });
681
+ } catch (err) {
682
+ console.error(` Error: ${err.message}`);
683
+ results.push({ target, success: false, reason: err.message });
254
684
  }
255
685
  }
256
686
 
257
- console.log('');
687
+ // Summary
688
+ const successes = results.filter(r => r.success);
689
+ const failures = results.filter(r => !r.success);
258
690
 
259
- // Validate
260
- const results = validateInstall(targetDir);
261
- printResults(results);
262
-
263
- const failures = results.filter(r => r.status === 'fail');
264
- console.log('');
691
+ if (successes.length > 0) {
692
+ printInstallSummary();
693
+ }
265
694
 
266
695
  if (failures.length > 0) {
267
- console.log('Installation incomplete. Some components missing.');
268
- process.exit(1);
696
+ console.log('');
697
+ console.log('Failed runtimes:');
698
+ for (const f of failures) {
699
+ console.log(` ${f.target.label}: ${f.reason}`);
700
+ }
701
+ console.log('');
702
+ console.log('Something went wrong? File an issue: https://github.com/ranjanrishikesh/takeToMarket/issues');
269
703
  }
270
704
 
271
- console.log('Installation complete!');
272
- console.log('');
273
- console.log('Quick start:');
274
- console.log(' 1. Open a project directory');
275
- console.log(' 2. Run /ttm-init to set up your marketing workspace');
276
- console.log(' 3. Run /ttm-new-campaign <name> to start your first campaign');
277
- console.log('');
278
- console.log('Documentation: https://github.com/taketomarket/taketomarket/blob/main/README.md');
705
+ process.exit(failures.length === results.length ? 1 : 0);
279
706
  }
280
707
 
281
708
  /**
@@ -291,12 +718,15 @@ function printResults(results) {
291
718
  }
292
719
 
293
720
  if (require.main === module) {
294
- main();
721
+ main().catch(err => {
722
+ console.error(`Fatal: ${err.message}`);
723
+ console.log('Something went wrong? File an issue: https://github.com/ranjanrishikesh/takeToMarket/issues');
724
+ process.exit(1);
725
+ });
295
726
  }
296
727
 
297
728
  module.exports = {
298
729
  main,
299
- detectRuntime,
300
730
  validateInstall,
301
731
  copyDirSync,
302
732
  dirExists,
@@ -304,4 +734,19 @@ module.exports = {
304
734
  printResults,
305
735
  DIRS_TO_COPY,
306
736
  FILES_TO_COPY,
737
+ PACKAGE_BASE_DIRS,
738
+ PACKAGE_BASE_FILES,
739
+ registerPlugin,
740
+ parseRuntimeChoices,
741
+ buildRuntimeTargets,
742
+ getInstalledRuntimes,
743
+ readSkillDescriptions,
744
+ shouldProceed,
745
+ getClaudeStatus,
746
+ checkStatus,
747
+ confirmInstall,
748
+ printInstallSummary,
749
+ copyPackageBase,
750
+ installSkillsForRuntime,
751
+ PACKAGE_ROOT,
307
752
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taketomarket",
3
- "version": "1.0.1",
3
+ "version": "2.1.0",
4
4
  "description": "Marketing operating system for Claude Code. Spec-driven campaigns with positioning-as-invariant enforcement, quality gate walls, and compound learnings.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -0,0 +1,29 @@
1
+ ---
2
+ name: ttm-update
3
+ description: >
4
+ Check for takeToMarket updates and upgrade to the latest version. Compares
5
+ installed version against npm registry and runs installer if newer version found.
6
+ disable-model-invocation: false
7
+ allowed-tools: Bash Read
8
+ ---
9
+
10
+ # /ttm-update
11
+
12
+ Check if takeToMarket needs updating and upgrade if available.
13
+
14
+ 1. Get current installed version:
15
+ ```bash
16
+ cat $HOME/.taketomarket/package.json 2>/dev/null | python3 -c "import sys,json; print(json.load(sys.stdin).get('version','unknown'))" 2>/dev/null || echo "unknown"
17
+ ```
18
+
19
+ 2. Get latest version from npm:
20
+ ```bash
21
+ npm show taketomarket version 2>/dev/null || echo "unknown"
22
+ ```
23
+
24
+ 3. Compare versions. If a newer version is available (latest > installed), run:
25
+ ```bash
26
+ npx taketomarket@latest --yes
27
+ ```
28
+
29
+ 4. If already up to date, confirm: "takeToMarket vX.X.X is up to date."