taketomarket 2.0.0 → 2.2.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.
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "taketomarket",
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",
6
+ "author": {
7
+ "name": "Rishikesh Ranjan",
8
+ "url": "https://github.com/ranjanrishikesh"
9
+ },
10
+ "homepage": "https://www.npmjs.com/package/taketomarket",
11
+ "repository": "https://github.com/ranjanrishikesh/takeToMarket",
12
+ "license": "MIT",
13
+ "keywords": ["marketing", "campaigns", "positioning", "quality-gates", "agent-skills"],
14
+ "category": "productivity",
15
+ "source": "git",
16
+ "sourceUrl": "https://github.com/ranjanrishikesh/takeToMarket.git",
17
+ "skills": [
18
+ "ttm-init", "ttm-new-campaign", "ttm-brief", "ttm-research",
19
+ "ttm-produce", "ttm-review", "ttm-fix", "ttm-verify", "ttm-ship",
20
+ "ttm-measure", "ttm-learn", "ttm-health", "ttm-state", "ttm-next",
21
+ "ttm-resume", "ttm-archive", "ttm-positioning-check",
22
+ "ttm-positioning-shift", "ttm-brand-refresh", "ttm-icp-refresh",
23
+ "ttm-competitor-scan", "ttm-aeo-check", "ttm-seo-audit",
24
+ "ttm-email-preflight", "ttm-keyword-map", "ttm-affiliate-kit",
25
+ "ttm-repurpose", "ttm-update"
26
+ ]
27
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taketomarket",
3
- "version": "2.0.0",
3
+ "version": "2.2.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
@@ -1,5 +1,7 @@
1
1
  # takeToMarket
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/taketomarket)](https://www.npmjs.com/package/taketomarket)
4
+
3
5
  A marketing operating system for Claude Code and Codex. Spec-driven campaigns with positioning-as-invariant enforcement, quality gate walls, and compound learnings.
4
6
 
5
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.
@@ -39,9 +41,14 @@ Flags:
39
41
 
40
42
  > Status: pending marketplace approval. Check https://github.com/ranjanrishikesh/takeToMarket for current status.
41
43
 
42
- ### Option 3 — Direct from GitHub
44
+ ### Option 3 — Direct from GitHub (Claude Code)
45
+
46
+ ```
47
+ /plugin marketplace add ranjanrishikesh/takeToMarket
48
+ /plugin install taketomarket
49
+ ```
43
50
 
44
- > Status: pending verification. Use Option 1 or Option 4 in the meantime.
51
+ This uses the Claude Code plugin system to install directly from the GitHub repo. Run both commands in Claude Code's chat.
45
52
 
46
53
  ### Option 4 — Manual (advanced)
47
54
 
@@ -54,11 +61,25 @@ node install.js
54
61
  ## Quick Start
55
62
 
56
63
  ```
57
- /taketomarket:ttm-init # set up workspace (one time)
58
- /taketomarket:ttm-new-campaign # create first campaign
59
- /taketomarket:ttm-produce # run production wave
64
+ /ttm-init # set up workspace (one time)
65
+ /ttm-new-campaign # create first campaign
66
+ /ttm-produce # run production wave
60
67
  ```
61
68
 
69
+ ## Runtime Notes
70
+
71
+ Commands vary by tool:
72
+
73
+ | Runtime | Install path | Invocation |
74
+ |---------|-------------|------------|
75
+ | Claude Code | `~/.claude/skills/` | `/ttm-init` |
76
+ | Codex | `~/.codex/skills/` or `~/.agents/skills/` | `$ttm-init` or mention by name |
77
+ | Cursor | `~/.cursor/skills/` | `/ttm-init` |
78
+ | Windsurf | `~/.codeium/windsurf/skills/` | `@ttm-init` |
79
+ | Gemini CLI | `~/.gemini/skills/` | automatic or `/skills enable` |
80
+
81
+ All non-Claude runtimes also support `~/.agents/skills/` as a universal path. Select **option 6** during install to use it.
82
+
62
83
  ## Campaign Lifecycle
63
84
 
64
85
  1. **Init** — set up workspace and reference files
@@ -77,39 +98,39 @@ node install.js
77
98
 
78
99
  | Command | Description |
79
100
  |---------|-------------|
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 |
101
+ | `/ttm-aeo-check` | Check citation status across AI engines for a query |
102
+ | `/ttm-affiliate-kit` | Generate creative kit for affiliate partners |
103
+ | `/ttm-archive` | Archive a completed campaign, finalize state, and update LEARNINGS.md |
104
+ | `/ttm-brand-refresh` | Update BRAND.md with new proof points and deprecate expired ones |
105
+ | `/ttm-brief` | Generate a campaign brief with mandatory outcome metrics, positioning anchor, and channel mix |
106
+ | `/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 |
108
+ | `/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 |
110
+ | `/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 |
113
+ | `/ttm-learn` | Extract lessons from campaign data, propose reference file edits, log to LEARNINGS.md |
114
+ | `/ttm-measure` | Analyze campaign analytics against outcome metrics using attribution models |
115
+ | `/ttm-new-campaign` | Create a new campaign directory with initialized state and reference file links |
116
+ | `/ttm-next` | Guide user to the right next command based on current campaign state |
117
+ | `/ttm-positioning-check` | Sample recent assets and report positioning drift percentage and analysis |
118
+ | `/ttm-positioning-shift` | Controlled positioning change with reasoning, migration plan, and approval gate |
119
+ | `/ttm-produce` | Generate content assets in fresh contexts loaded with brief, positioning, brand, ICP, and playbook |
120
+ | `/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
+ | `/ttm-resume` | Resume a paused campaign at its last completed phase |
123
+ | `/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 |
125
+ | `/ttm-ship` | Generate launch checklist confirming tracking, UTMs, funnel testing, and asset finalization |
126
+ | `/ttm-state` | Display current campaign states, decisions in flight, blockers, and experiments |
127
+ | `/ttm-verify` | Run all applicable quality gates on every asset with pass/fail report and line-level feedback |
107
128
 
108
129
  ## Verify Installation
109
130
 
110
131
  Inside Claude Code, run:
111
132
  ```
112
- /taketomarket:ttm-health
133
+ /ttm-health
113
134
  ```
114
135
 
115
136
  This validates directory integrity, reference file presence, and state consistency.
@@ -117,3 +138,11 @@ This validates directory integrity, reference file presence, and state consisten
117
138
  ## License
118
139
 
119
140
  MIT — see [LICENSE](LICENSE).
141
+
142
+ ## Privacy & Security
143
+
144
+ takeToMarket runs entirely on your local filesystem. No data collection, no telemetry, no servers. See [SECURITY.md](SECURITY.md) for the combined privacy and security policy.
145
+
146
+ ## Contributing
147
+
148
+ See [CONTRIBUTING.md](CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).
package/install.js CHANGED
@@ -29,7 +29,7 @@ const FILES_TO_COPY = [
29
29
 
30
30
  // ── Runtime Selection ─────────────────────────────────────────────────────────
31
31
 
32
- const RUNTIME_MENU = ['claude', 'codex', 'cursor', 'windsurf', 'gemini'];
32
+ const RUNTIME_MENU = ['claude', 'codex', 'cursor', 'windsurf', 'gemini', 'agents'];
33
33
 
34
34
  /**
35
35
  * Parse user input from the runtime selection prompt.
@@ -39,15 +39,16 @@ const RUNTIME_MENU = ['claude', 'codex', 'cursor', 'windsurf', 'gemini'];
39
39
  function parseRuntimeChoices(input) {
40
40
  const trimmed = input.trim();
41
41
  if (!trimmed) return null;
42
- if (trimmed === '6') return [...RUNTIME_MENU];
43
- if (trimmed === '7') return ['custom'];
42
+ if (trimmed === '7') return [...RUNTIME_MENU];
43
+ if (trimmed === '8') return ['custom'];
44
44
 
45
45
  const parts = trimmed.split(',').map(s => s.trim());
46
46
  const names = new Set();
47
47
  for (const part of parts) {
48
48
  const n = parseInt(part, 10);
49
- if (isNaN(n) || n < 1 || n > 7) return null;
50
- if (n === 7) return ['custom'];
49
+ if (isNaN(n) || n < 1 || n > 8) return null;
50
+ if (n === 7) return [...RUNTIME_MENU];
51
+ if (n === 8) return ['custom'];
51
52
  names.add(RUNTIME_MENU[n - 1]);
52
53
  }
53
54
  return [...names];
@@ -56,44 +57,39 @@ function parseRuntimeChoices(input) {
56
57
  /**
57
58
  * Build the install target map for all known runtimes.
58
59
  * @param {string} [homeDir] - Home directory (injectable for tests)
59
- * @returns {Object.<string, {label, dir, parentDir, register, partial}>}
60
+ * @returns {Object.<string, {label, skillsDir, parentDir}>}
60
61
  */
61
62
  function buildRuntimeTargets(homeDir = os.homedir()) {
62
63
  return {
63
64
  claude: {
64
65
  label: 'Claude Code',
65
- dir: path.join(homeDir, '.claude', 'plugins', 'taketomarket'),
66
+ skillsDir: path.join(homeDir, '.claude', 'skills'),
66
67
  parentDir: path.join(homeDir, '.claude'),
67
- register: true,
68
- partial: false,
69
68
  },
70
69
  codex: {
71
70
  label: 'Codex (OpenAI)',
72
- dir: path.join(homeDir, '.codex', 'plugins', 'taketomarket'),
71
+ skillsDir: path.join(homeDir, '.codex', 'skills'),
73
72
  parentDir: path.join(homeDir, '.codex'),
74
- register: false,
75
- partial: true,
76
73
  },
77
74
  cursor: {
78
75
  label: 'Cursor',
79
- dir: path.join(homeDir, '.cursor', 'rules'),
76
+ skillsDir: path.join(homeDir, '.cursor', 'skills'),
80
77
  parentDir: path.join(homeDir, '.cursor'),
81
- register: false,
82
- partial: true,
83
78
  },
84
79
  windsurf: {
85
80
  label: 'Windsurf',
86
- dir: path.join(homeDir, '.codeium', 'windsurf'),
81
+ skillsDir: path.join(homeDir, '.codeium', 'windsurf', 'skills'),
87
82
  parentDir: path.join(homeDir, '.codeium'),
88
- register: false,
89
- partial: true,
90
83
  },
91
84
  gemini: {
92
85
  label: 'Gemini CLI',
93
- dir: path.join(homeDir, '.gemini'),
86
+ skillsDir: path.join(homeDir, '.gemini', 'skills'),
94
87
  parentDir: path.join(homeDir, '.gemini'),
95
- register: false,
96
- partial: true,
88
+ },
89
+ agents: {
90
+ label: 'All runtimes (~/.agents/skills/ — universal)',
91
+ skillsDir: path.join(homeDir, '.agents', 'skills'),
92
+ parentDir: path.join(homeDir, '.agents'),
97
93
  },
98
94
  };
99
95
  }
@@ -144,8 +140,9 @@ async function promptRuntimeSelection(args, homeDir = os.homedir()) {
144
140
  console.log(' 3. Cursor');
145
141
  console.log(' 4. Windsurf');
146
142
  console.log(' 5. Gemini CLI');
147
- console.log(' 6. All of the above');
148
- console.log(' 7. Let me type a custom path');
143
+ console.log(' 6. All runtimes via ~/.agents/skills/ (universal — works for Codex, Cursor, Windsurf, Gemini)');
144
+ console.log(' 7. All of the above');
145
+ console.log(' 8. Let me type a custom path');
149
146
  console.log('');
150
147
 
151
148
  let choices = null;
@@ -154,7 +151,7 @@ async function promptRuntimeSelection(args, homeDir = os.homedir()) {
154
151
  const input = await ask('Your choice (comma-separated, e.g. 1,3): ');
155
152
  choices = parseRuntimeChoices(input);
156
153
  if (choices === null) {
157
- console.log('Invalid input. Please enter numbers 1-7 separated by commas.');
154
+ console.log('Invalid input. Please enter numbers 1-8 separated by commas.');
158
155
  attempts++;
159
156
  }
160
157
  }
@@ -183,7 +180,7 @@ async function promptRuntimeSelection(args, homeDir = os.homedir()) {
183
180
  const result = [];
184
181
  for (const name of choices) {
185
182
  if (name === 'custom') {
186
- result.push({ label: 'Custom', dir: customPath, parentDir: null, register: false, partial: false });
183
+ result.push({ label: 'Custom', skillsDir: customPath, parentDir: null });
187
184
  } else {
188
185
  result.push(allTargets[name]);
189
186
  }
@@ -280,6 +277,68 @@ function copyDirSync(src, dest) {
280
277
  }
281
278
  }
282
279
 
280
+ // ── Package Base & Per-Runtime Skill Install ──────────────────────────────────
281
+
282
+ const PACKAGE_BASE_DIRS = ['workflows', 'templates', 'references', 'playbooks', 'gates', 'bin', 'agents'];
283
+ const PACKAGE_BASE_FILES = ['settings.json', 'package.json'];
284
+
285
+ /**
286
+ * Copy non-skill package files to ~/.taketomarket/ (shared across all runtimes).
287
+ * @param {string} packageRoot - Source npm package root
288
+ * @param {string} [homeDir]
289
+ */
290
+ function copyPackageBase(packageRoot, homeDir = os.homedir()) {
291
+ const dest = path.join(homeDir, '.taketomarket');
292
+ fs.mkdirSync(dest, { recursive: true });
293
+
294
+ for (const dir of PACKAGE_BASE_DIRS) {
295
+ const src = path.join(packageRoot, dir);
296
+ if (dirExists(src)) {
297
+ copyDirSync(src, path.join(dest, dir));
298
+ }
299
+ }
300
+
301
+ for (const file of PACKAGE_BASE_FILES) {
302
+ const src = path.join(packageRoot, file);
303
+ if (fileExists(src)) {
304
+ fs.copyFileSync(src, path.join(dest, file));
305
+ }
306
+ }
307
+ }
308
+
309
+ /**
310
+ * Install individual skills into a runtime's skills directory.
311
+ * Replaces ${CLAUDE_PLUGIN_ROOT} in SKILL.md with absolute path to ~/.taketomarket.
312
+ * @param {string} skillsDir - Runtime's skills base dir (e.g. ~/.claude/skills/)
313
+ * @param {string} packageRoot - Source npm package root
314
+ * @param {string} [homeDir]
315
+ * @returns {number} Number of skills installed
316
+ */
317
+ function installSkillsForRuntime(skillsDir, packageRoot, homeDir = os.homedir()) {
318
+ const packageBase = path.join(homeDir, '.taketomarket');
319
+ const srcSkillsDir = path.join(packageRoot, 'skills');
320
+ if (!dirExists(srcSkillsDir)) return 0;
321
+
322
+ fs.mkdirSync(skillsDir, { recursive: true });
323
+
324
+ let count = 0;
325
+ const entries = fs.readdirSync(srcSkillsDir, { withFileTypes: true });
326
+ for (const entry of entries) {
327
+ if (!entry.isDirectory()) continue;
328
+ const skillMdSrc = path.join(srcSkillsDir, entry.name, 'SKILL.md');
329
+ if (!fileExists(skillMdSrc)) continue;
330
+
331
+ let content = fs.readFileSync(skillMdSrc, 'utf8');
332
+ content = content.replace(/\$\{CLAUDE_PLUGIN_ROOT\}/g, packageBase);
333
+
334
+ const destSkillDir = path.join(skillsDir, entry.name);
335
+ fs.mkdirSync(destSkillDir, { recursive: true });
336
+ fs.writeFileSync(path.join(destSkillDir, 'SKILL.md'), content, 'utf8');
337
+ count++;
338
+ }
339
+ return count;
340
+ }
341
+
283
342
  // ── Plugin Registration ───────────────────────────────────────────────────────
284
343
 
285
344
  /**
@@ -377,11 +436,11 @@ function printInstallSummary(packageRoot = PACKAGE_ROOT) {
377
436
  console.log('');
378
437
  console.log('Available commands:');
379
438
  for (const { name, description } of skills) {
380
- const cmd = `/taketomarket:${name}`.padEnd(42);
439
+ const cmd = `/${name}`.padEnd(30);
381
440
  console.log(` ${cmd} ${description}`);
382
441
  }
383
442
  console.log('');
384
- console.log('Quick start: open any project in Claude Code and run /taketomarket:ttm-init');
443
+ console.log('Quick start: open any project in Claude Code and run /ttm-init');
385
444
  }
386
445
 
387
446
  /**
@@ -411,33 +470,22 @@ function shouldProceed(yesFlag) {
411
470
  /**
412
471
  * Read Claude Code install status for the checkStatus output.
413
472
  * @param {string} [homeDir]
414
- * @returns {{ installed: boolean, registered: boolean, skillCount: number, dir: string }}
473
+ * @returns {{ installed: boolean, skillCount: number, dir: string }}
415
474
  */
416
475
  function getClaudeStatus(homeDir = os.homedir()) {
417
- const pluginDir = path.join(homeDir, '.claude', 'plugins', 'taketomarket');
418
- const installed = dirExists(pluginDir);
419
- if (!installed) return { installed: false, registered: false, skillCount: 0, dir: pluginDir };
476
+ const skillsDir = path.join(homeDir, '.claude', 'skills');
477
+ const probeSkill = path.join(skillsDir, 'ttm-init', 'SKILL.md');
478
+ const installed = fileExists(probeSkill);
479
+ if (!installed) return { installed: false, skillCount: 0, dir: skillsDir };
420
480
 
421
- const skillsDir = path.join(pluginDir, 'skills');
422
481
  let skillCount = 0;
423
- if (dirExists(skillsDir)) {
424
- try {
425
- skillCount = fs.readdirSync(skillsDir, { withFileTypes: true })
426
- .filter(e => e.isDirectory() && fileExists(path.join(skillsDir, e.name, 'SKILL.md')))
427
- .length;
428
- } catch { /* ignore */ }
429
- }
430
-
431
- const registryPath = path.join(homeDir, '.claude', 'plugins', 'installed_plugins.json');
432
- let registered = false;
433
- if (fileExists(registryPath)) {
434
- try {
435
- const reg = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
436
- registered = !!(reg.plugins && reg.plugins['taketomarket@npm']);
437
- } catch { /* ignore */ }
438
- }
482
+ try {
483
+ skillCount = fs.readdirSync(skillsDir, { withFileTypes: true })
484
+ .filter(e => e.isDirectory() && e.name.startsWith('ttm-') && fileExists(path.join(skillsDir, e.name, 'SKILL.md')))
485
+ .length;
486
+ } catch { /* ignore */ }
439
487
 
440
- return { installed: true, registered, skillCount, dir: pluginDir };
488
+ return { installed: true, skillCount, dir: skillsDir };
441
489
  }
442
490
 
443
491
  /**
@@ -453,8 +501,7 @@ function checkStatus(version, homeDir = os.homedir()) {
453
501
 
454
502
  const claude = getClaudeStatus(homeDir);
455
503
  if (claude.installed) {
456
- console.log(`Claude Code: INSTALLED (${claude.skillCount} skills, ${claude.dir.replace(homeDir, '~')})`);
457
- console.log(` registered: ${claude.registered ? 'yes (installed_plugins.json)' : 'NO — slash commands will not appear'}`);
504
+ console.log(`Claude Code: INSTALLED (${claude.skillCount} skills at ${claude.dir.replace(homeDir, '~')})`);
458
505
  } else {
459
506
  console.log('Claude Code: NOT INSTALLED');
460
507
  }
@@ -462,8 +509,8 @@ function checkStatus(version, homeDir = os.homedir()) {
462
509
  for (const name of ['codex', 'cursor', 'windsurf', 'gemini']) {
463
510
  const t = targets[name];
464
511
  const label = t.label.padEnd(12);
465
- if (t.dir && dirExists(t.dir)) {
466
- console.log(`${label} INSTALLED (${t.dir.replace(homeDir, '~')})`);
512
+ if (t.skillsDir && dirExists(t.skillsDir)) {
513
+ console.log(`${label} INSTALLED (${t.skillsDir.replace(homeDir, '~')})`);
467
514
  } else {
468
515
  console.log(`${label} NOT INSTALLED`);
469
516
  }
@@ -493,7 +540,7 @@ async function confirmInstall(targets, version, yesFlag) {
493
540
  console.log('');
494
541
  console.log('This will install to:');
495
542
  for (const t of targets) {
496
- const shortDir = t.dir.replace(os.homedir(), '~');
543
+ const shortDir = t.skillsDir.replace(os.homedir(), '~');
497
544
  console.log(` ${shortDir.padEnd(45)} (${t.label})`);
498
545
  }
499
546
  console.log('');
@@ -619,57 +666,25 @@ Options:
619
666
  // Confirmation
620
667
  await confirmInstall(targets, VERSION, yesFlag);
621
668
 
669
+ // Copy shared package files once
670
+ console.log('');
671
+ console.log('Copying package files to ~/.taketomarket/...');
672
+ copyPackageBase(PACKAGE_ROOT);
673
+
622
674
  // Install loop
623
675
  const results = [];
624
676
  for (const target of targets) {
625
677
  console.log('');
626
- console.log(`Installing to ${target.label}...`);
678
+ console.log(`Installing skills to ${target.label}...`);
627
679
 
628
680
  if (target.parentDir && !dirExists(target.parentDir)) {
629
- console.warn(` Warning: ${target.label} doesn't appear to be installed (${target.parentDir} not found).`);
630
- console.warn(' Installing anyway — files will be ready when you install the runtime.');
681
+ console.warn(` Warning: ${target.label} not detected (${target.parentDir} not found). Installing anyway.`);
631
682
  }
632
683
 
633
684
  try {
634
- if (dirExists(target.dir)) {
635
- console.log(' Existing installation found. Removing before reinstall...');
636
- fs.rmSync(target.dir, { recursive: true, force: true });
637
- }
638
-
639
- for (const dir of DIRS_TO_COPY) {
640
- const srcDir = path.join(PACKAGE_ROOT, dir);
641
- if (dirExists(srcDir)) {
642
- console.log(` Copying ${dir}/`);
643
- copyDirSync(srcDir, path.join(target.dir, dir));
644
- }
645
- }
646
-
647
- for (const file of FILES_TO_COPY) {
648
- const srcFile = path.join(PACKAGE_ROOT, file);
649
- if (fileExists(srcFile)) {
650
- console.log(` Copying ${file}`);
651
- const destFile = path.join(target.dir, file);
652
- fs.mkdirSync(path.dirname(destFile), { recursive: true });
653
- fs.copyFileSync(srcFile, destFile);
654
- }
655
- }
656
-
657
- if (target.register) {
658
- registerPlugin(target.dir, VERSION);
659
- }
660
-
661
- const validation = validateInstall(target.dir);
662
- printResults(validation);
663
- const failures = validation.filter(r => r.status === 'fail');
664
-
665
- if (failures.length > 0) {
666
- results.push({ target, success: false, reason: 'validation failed' });
667
- } else {
668
- if (target.partial) {
669
- console.log(` [PARTIAL] ${target.label}: files copied — slash command registration coming in a future update`);
670
- }
671
- results.push({ target, success: true });
672
- }
685
+ const count = installSkillsForRuntime(target.skillsDir, PACKAGE_ROOT);
686
+ console.log(` Installed ${count} skills ${target.skillsDir.replace(os.homedir(), '~')}`);
687
+ results.push({ target, success: true });
673
688
  } catch (err) {
674
689
  console.error(` Error: ${err.message}`);
675
690
  results.push({ target, success: false, reason: err.message });
@@ -726,6 +741,8 @@ module.exports = {
726
741
  printResults,
727
742
  DIRS_TO_COPY,
728
743
  FILES_TO_COPY,
744
+ PACKAGE_BASE_DIRS,
745
+ PACKAGE_BASE_FILES,
729
746
  registerPlugin,
730
747
  parseRuntimeChoices,
731
748
  buildRuntimeTargets,
@@ -736,5 +753,7 @@ module.exports = {
736
753
  checkStatus,
737
754
  confirmInstall,
738
755
  printInstallSummary,
756
+ copyPackageBase,
757
+ installSkillsForRuntime,
739
758
  PACKAGE_ROOT,
740
759
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taketomarket",
3
- "version": "2.0.0",
3
+ "version": "2.2.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."