taketomarket 2.0.0 → 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": "2.0.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/install.js CHANGED
@@ -56,44 +56,34 @@ function parseRuntimeChoices(input) {
56
56
  /**
57
57
  * Build the install target map for all known runtimes.
58
58
  * @param {string} [homeDir] - Home directory (injectable for tests)
59
- * @returns {Object.<string, {label, dir, parentDir, register, partial}>}
59
+ * @returns {Object.<string, {label, skillsDir, parentDir}>}
60
60
  */
61
61
  function buildRuntimeTargets(homeDir = os.homedir()) {
62
62
  return {
63
63
  claude: {
64
64
  label: 'Claude Code',
65
- dir: path.join(homeDir, '.claude', 'plugins', 'taketomarket'),
65
+ skillsDir: path.join(homeDir, '.claude', 'skills'),
66
66
  parentDir: path.join(homeDir, '.claude'),
67
- register: true,
68
- partial: false,
69
67
  },
70
68
  codex: {
71
69
  label: 'Codex (OpenAI)',
72
- dir: path.join(homeDir, '.codex', 'plugins', 'taketomarket'),
70
+ skillsDir: path.join(homeDir, '.codex', 'skills'),
73
71
  parentDir: path.join(homeDir, '.codex'),
74
- register: false,
75
- partial: true,
76
72
  },
77
73
  cursor: {
78
74
  label: 'Cursor',
79
- dir: path.join(homeDir, '.cursor', 'rules'),
75
+ skillsDir: path.join(homeDir, '.cursor', 'skills'),
80
76
  parentDir: path.join(homeDir, '.cursor'),
81
- register: false,
82
- partial: true,
83
77
  },
84
78
  windsurf: {
85
79
  label: 'Windsurf',
86
- dir: path.join(homeDir, '.codeium', 'windsurf'),
80
+ skillsDir: path.join(homeDir, '.codeium', 'windsurf', 'skills'),
87
81
  parentDir: path.join(homeDir, '.codeium'),
88
- register: false,
89
- partial: true,
90
82
  },
91
83
  gemini: {
92
84
  label: 'Gemini CLI',
93
- dir: path.join(homeDir, '.gemini'),
85
+ skillsDir: path.join(homeDir, '.gemini', 'skills'),
94
86
  parentDir: path.join(homeDir, '.gemini'),
95
- register: false,
96
- partial: true,
97
87
  },
98
88
  };
99
89
  }
@@ -183,7 +173,7 @@ async function promptRuntimeSelection(args, homeDir = os.homedir()) {
183
173
  const result = [];
184
174
  for (const name of choices) {
185
175
  if (name === 'custom') {
186
- result.push({ label: 'Custom', dir: customPath, parentDir: null, register: false, partial: false });
176
+ result.push({ label: 'Custom', skillsDir: customPath, parentDir: null });
187
177
  } else {
188
178
  result.push(allTargets[name]);
189
179
  }
@@ -280,6 +270,68 @@ function copyDirSync(src, dest) {
280
270
  }
281
271
  }
282
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
+
283
335
  // ── Plugin Registration ───────────────────────────────────────────────────────
284
336
 
285
337
  /**
@@ -411,33 +463,22 @@ function shouldProceed(yesFlag) {
411
463
  /**
412
464
  * Read Claude Code install status for the checkStatus output.
413
465
  * @param {string} [homeDir]
414
- * @returns {{ installed: boolean, registered: boolean, skillCount: number, dir: string }}
466
+ * @returns {{ installed: boolean, skillCount: number, dir: string }}
415
467
  */
416
468
  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 };
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 };
420
473
 
421
- const skillsDir = path.join(pluginDir, 'skills');
422
474
  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
- }
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 */ }
439
480
 
440
- return { installed: true, registered, skillCount, dir: pluginDir };
481
+ return { installed: true, skillCount, dir: skillsDir };
441
482
  }
442
483
 
443
484
  /**
@@ -453,8 +494,7 @@ function checkStatus(version, homeDir = os.homedir()) {
453
494
 
454
495
  const claude = getClaudeStatus(homeDir);
455
496
  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'}`);
497
+ console.log(`Claude Code: INSTALLED (${claude.skillCount} skills at ${claude.dir.replace(homeDir, '~')})`);
458
498
  } else {
459
499
  console.log('Claude Code: NOT INSTALLED');
460
500
  }
@@ -462,8 +502,8 @@ function checkStatus(version, homeDir = os.homedir()) {
462
502
  for (const name of ['codex', 'cursor', 'windsurf', 'gemini']) {
463
503
  const t = targets[name];
464
504
  const label = t.label.padEnd(12);
465
- if (t.dir && dirExists(t.dir)) {
466
- console.log(`${label} INSTALLED (${t.dir.replace(homeDir, '~')})`);
505
+ if (t.skillsDir && dirExists(t.skillsDir)) {
506
+ console.log(`${label} INSTALLED (${t.skillsDir.replace(homeDir, '~')})`);
467
507
  } else {
468
508
  console.log(`${label} NOT INSTALLED`);
469
509
  }
@@ -493,7 +533,7 @@ async function confirmInstall(targets, version, yesFlag) {
493
533
  console.log('');
494
534
  console.log('This will install to:');
495
535
  for (const t of targets) {
496
- const shortDir = t.dir.replace(os.homedir(), '~');
536
+ const shortDir = t.skillsDir.replace(os.homedir(), '~');
497
537
  console.log(` ${shortDir.padEnd(45)} (${t.label})`);
498
538
  }
499
539
  console.log('');
@@ -619,57 +659,25 @@ Options:
619
659
  // Confirmation
620
660
  await confirmInstall(targets, VERSION, yesFlag);
621
661
 
662
+ // Copy shared package files once
663
+ console.log('');
664
+ console.log('Copying package files to ~/.taketomarket/...');
665
+ copyPackageBase(PACKAGE_ROOT);
666
+
622
667
  // Install loop
623
668
  const results = [];
624
669
  for (const target of targets) {
625
670
  console.log('');
626
- console.log(`Installing to ${target.label}...`);
671
+ console.log(`Installing skills to ${target.label}...`);
627
672
 
628
673
  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.');
674
+ console.warn(` Warning: ${target.label} not detected (${target.parentDir} not found). Installing anyway.`);
631
675
  }
632
676
 
633
677
  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
- }
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 });
673
681
  } catch (err) {
674
682
  console.error(` Error: ${err.message}`);
675
683
  results.push({ target, success: false, reason: err.message });
@@ -726,6 +734,8 @@ module.exports = {
726
734
  printResults,
727
735
  DIRS_TO_COPY,
728
736
  FILES_TO_COPY,
737
+ PACKAGE_BASE_DIRS,
738
+ PACKAGE_BASE_FILES,
729
739
  registerPlugin,
730
740
  parseRuntimeChoices,
731
741
  buildRuntimeTargets,
@@ -736,5 +746,7 @@ module.exports = {
736
746
  checkStatus,
737
747
  confirmInstall,
738
748
  printInstallSummary,
749
+ copyPackageBase,
750
+ installSkillsForRuntime,
739
751
  PACKAGE_ROOT,
740
752
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taketomarket",
3
- "version": "2.0.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
  "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."