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.
- package/.claude-plugin/marketplace.json +27 -0
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +62 -33
- package/install.js +115 -96
- package/package.json +1 -1
- package/skills/ttm-update/SKILL.md +29 -0
|
@@ -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.
|
|
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
|
+
[](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
|
-
|
|
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
|
-
/
|
|
58
|
-
/
|
|
59
|
-
/
|
|
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
|
-
| `/
|
|
81
|
-
| `/
|
|
82
|
-
| `/
|
|
83
|
-
| `/
|
|
84
|
-
| `/
|
|
85
|
-
| `/
|
|
86
|
-
| `/
|
|
87
|
-
| `/
|
|
88
|
-
| `/
|
|
89
|
-
| `/
|
|
90
|
-
| `/
|
|
91
|
-
| `/
|
|
92
|
-
| `/
|
|
93
|
-
| `/
|
|
94
|
-
| `/
|
|
95
|
-
| `/
|
|
96
|
-
| `/
|
|
97
|
-
| `/
|
|
98
|
-
| `/
|
|
99
|
-
| `/
|
|
100
|
-
| `/
|
|
101
|
-
| `/
|
|
102
|
-
| `/
|
|
103
|
-
| `/
|
|
104
|
-
| `/
|
|
105
|
-
| `/
|
|
106
|
-
| `/
|
|
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
|
-
/
|
|
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 === '
|
|
43
|
-
if (trimmed === '
|
|
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 >
|
|
50
|
-
if (n === 7) return [
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
86
|
+
skillsDir: path.join(homeDir, '.gemini', 'skills'),
|
|
94
87
|
parentDir: path.join(homeDir, '.gemini'),
|
|
95
|
-
|
|
96
|
-
|
|
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
|
|
148
|
-
console.log(' 7.
|
|
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-
|
|
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',
|
|
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 =
|
|
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 /
|
|
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,
|
|
473
|
+
* @returns {{ installed: boolean, skillCount: number, dir: string }}
|
|
415
474
|
*/
|
|
416
475
|
function getClaudeStatus(homeDir = os.homedir()) {
|
|
417
|
-
const
|
|
418
|
-
const
|
|
419
|
-
|
|
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
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
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,
|
|
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
|
|
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.
|
|
466
|
-
console.log(`${label} INSTALLED (${t.
|
|
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.
|
|
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}
|
|
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
|
-
|
|
635
|
-
|
|
636
|
-
|
|
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.
|
|
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."
|