spirewise 1.0.1 → 1.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.
package/README.md CHANGED
@@ -10,66 +10,77 @@ project** or **globally** for every project.
10
10
 
11
11
  ## Install
12
12
 
13
- No clone needed:
13
+ No clone needed — just run:
14
14
 
15
15
  ```bash
16
- npx spirewise install
16
+ npx spirewise
17
17
  ```
18
18
 
19
- You'll be asked where to install:
19
+ You get a **full-screen interactive picker** with three steps:
20
20
 
21
21
  ```
22
- ==> Where should the skills be installed?
23
- 1) Workspace (this folder only the current project directory)
24
- 2) Global (your home folders, all projects)
25
- 3) Both
22
+ ══════════════════ Step 1/3 · Select skills ══════════════════
23
+ ◉ f6s-copywriting Generate F6S startup/company profile copy…
24
+ linkedin-copywriting Generate LinkedIn Company Page copy…
25
+
26
+ ↑/↓ move · space toggle · a all/none · enter confirm · esc cancel
26
27
  ```
27
28
 
28
- Skip the prompt with a scope flag:
29
+ Step 1 picks **skills**, step 2 picks **agents** (checkbox lists — move with
30
+ ↑/↓, toggle with **space**, **a** for all/none), step 3 picks **scope**
31
+ (Workspace / Global / Both). Press **enter** to confirm each step.
32
+
33
+ ### Or drive it with flags (skips the matching step)
34
+
35
+ ```bash
36
+ -s, --skills <a,b> skills to install (default: all / pick)
37
+ -a, --agents <a,b> agents to target (default: all / pick) alias: --agent
38
+ -sc, --scope <s> workspace | global | both
39
+ --workspace | --global | --both scope shortcuts
40
+ ```
29
41
 
30
42
  ```bash
31
- npx spirewise install --workspace # this folder only (current project)
32
- npx spirewise install --global # all projects (home folders)
33
- npx spirewise install --both # both
43
+ npx spirewise -sc both # pick skills+agents, scope both
44
+ npx spirewise -s f6s-copywriting -a claude,cursor -sc workspace
45
+ npx spirewise --global # pick skills+agents, scope global
34
46
  ```
35
47
 
36
48
  Or install the CLI globally:
37
49
 
38
50
  ```bash
39
51
  npm install -g spirewise
40
- spirewise install --both
52
+ spirewise -sc both
41
53
  ```
42
54
 
43
55
  ## What gets installed where
44
56
 
45
- | Agent | Project folder | Global folder | Format |
46
- |-------|----------------|---------------|--------|
57
+ | Agent | Workspace folder | Global folder | Format |
58
+ |-------|------------------|---------------|--------|
47
59
  | Claude Code | `.claude/skills/` | `~/.claude/skills/` | SKILL.md |
48
60
  | GitHub Copilot | `.github/skills/` | `~/.copilot/skills/` | SKILL.md |
49
61
  | Cursor | `.cursor/rules/` | `~/.cursor/rules/` | `.mdc` rule |
50
62
  | Windsurf | `.windsurf/rules/` | `~/.codeium/windsurf/global_rules/` | `.md` rule |
51
63
  | Codex CLI | `.codex/skills/` | `~/.codex/skills/` | SKILL.md |
52
64
  | Gemini CLI | `.gemini/skills/` | `~/.gemini/skills/` | SKILL.md |
65
+ | OpenCode | `.opencode/skills/` | `~/.config/opencode/skills/` | SKILL.md |
66
+ | Cline | `.clinerules/` | `~/.clinerules/` | `.md` rule |
67
+ | Roo Code | `.roo/rules/` | `~/.roo/rules/` | `.md` rule |
68
+ | Kilo Code | `.kilocode/rules/` | `~/.kilocode/rules/` | `.md` rule |
69
+ | Continue | `.continue/rules/` | `~/.continue/rules/` | `.md` rule |
70
+ | Amp | `.amp/rules/` | `~/.config/amp/rules/` | `.md` rule |
53
71
 
54
- Rule-based agents (Cursor, Windsurf) get a single rule file generated from the
55
- skill; the rest get the full `SKILL.md` folder.
72
+ Rule-based agents get a single rule file generated from the skill; the rest get
73
+ the full `SKILL.md` folder. (Newer tools' folders are best-effort conventions —
74
+ edit the `AGENTS` registry at the top of `bin/cli.js` if any differ.)
56
75
 
57
- ## Narrow it down
76
+ ## Inspect
58
77
 
59
78
  ```bash
60
- # only some agents
61
- npx spirewise install --both --agent claude,cursor
62
-
63
- # only some skills
64
- npx spirewise install f6s-copywriting --global
65
-
66
- # inspect
67
79
  npx spirewise list # available skills
68
80
  npx spirewise agents # supported agents + their folders
81
+ npx spirewise --help # all options
69
82
  ```
70
83
 
71
- Run `npx spirewise --help` for all options.
72
-
73
84
  ## Skills included
74
85
 
75
86
  | Skill | Output file | Purpose |
package/bin/cli.js CHANGED
@@ -2,71 +2,84 @@
2
2
  'use strict';
3
3
 
4
4
  /**
5
- * spirewise — install copywriting Agent Skills into ALL your AI agents at once.
5
+ * spirewise — install copywriting Agent Skills into your AI agents.
6
6
  *
7
- * Installs into each agent's correct folder, in the format that agent expects,
8
- * and asks whether to install for THIS project (cwd) or GLOBALLY (home dirs).
7
+ * Run with no flags for a full-screen interactive picker:
8
+ * npx spirewise (or: npx spirewise install)
9
9
  *
10
- * Usage:
11
- * npx spirewise install # interactive: pick scope, install all
12
- * npx spirewise install --project # this project only
13
- * npx spirewise install --global # global (home) only
14
- * npx spirewise install --both # project + global
15
- * npx spirewise install --agent claude,cursor # restrict agents
16
- * npx spirewise install f6s-copywriting # restrict skills
17
- * npx spirewise list # list skills
18
- * npx spirewise agents # list supported agents + folders
10
+ * Or drive everything from flags (skips the matching picker step):
11
+ * -s, --skills <a,b> skills to install (default: all)
12
+ * -a, --agents <a,b> agents to target (default: all) alias: --agent
13
+ * -sc, --scope <s> workspace | global | both
14
+ * --workspace|--global|--both scope shortcuts
15
+ *
16
+ * Other commands:
17
+ * npx spirewise list list skills
18
+ * npx spirewise agents list supported agents + folders
19
19
  */
20
20
 
21
21
  const fs = require('fs');
22
22
  const path = require('path');
23
23
  const os = require('os');
24
- const readline = require('readline');
25
24
 
26
25
  const PKG_ROOT = path.resolve(__dirname, '..');
27
26
  const SKILLS_DIR = path.join(PKG_ROOT, 'skills');
28
27
  const HOME = process.env.HOME || process.env.USERPROFILE || os.homedir();
29
28
 
30
- const c = {
31
- reset: '\x1b[0m', blue: '\x1b[1;34m', green: '\x1b[1;32m',
32
- yellow: '\x1b[1;33m', red: '\x1b[1;31m', dim: '\x1b[2m',
29
+ const USE_COLOR = !process.env.NO_COLOR;
30
+ const RAW = {
31
+ reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m',
32
+ blue: '\x1b[1;34m', green: '\x1b[1;32m', yellow: '\x1b[1;33m',
33
+ red: '\x1b[1;31m', cyan: '\x1b[1;36m', magenta: '\x1b[1;35m',
33
34
  };
35
+ const paint = (code, s) => (USE_COLOR ? `${code}${s}${RAW.reset}` : s);
36
+ const c = USE_COLOR ? RAW
37
+ : { reset: '', bold: '', dim: '', blue: '', green: '', yellow: '', red: '', cyan: '', magenta: '' };
38
+
34
39
  const info = (m) => console.log(`${c.blue}==>${c.reset} ${m}`);
35
40
  const ok = (m) => console.log(`${c.green} ok${c.reset} ${m}`);
36
41
  const warn = (m) => console.error(`${c.yellow} !${c.reset} ${m}`);
37
42
  const die = (m) => { console.error(`${c.red}err${c.reset} ${m}`); process.exit(1); };
38
43
 
44
+ let PKG_VERSION = '';
45
+ try { PKG_VERSION = require(path.join(PKG_ROOT, 'package.json')).version || ''; } catch (_) {}
46
+
47
+ const BANNER = [
48
+ ' ███████╗██████╗ ██╗██████╗ ███████╗██╗ ██╗██╗███████╗███████╗',
49
+ ' ██╔════╝██╔══██╗██║██╔══██╗██╔════╝██║ ██║██║██╔════╝██╔════╝',
50
+ ' ███████╗██████╔╝██║██████╔╝█████╗ ██║ █╗ ██║██║███████╗█████╗ ',
51
+ ' ╚════██║██╔═══╝ ██║██╔══██╗██╔══╝ ██║███╗██║██║╚════██║██╔══╝ ',
52
+ ' ███████║██║ ██║██║ ██║███████╗╚███╔███╔╝██║███████║███████╗',
53
+ ' ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ ╚══╝╚══╝ ╚═╝╚══════╝╚══════╝',
54
+ ];
55
+
56
+ function banner() {
57
+ console.log('');
58
+ for (const line of BANNER) console.log(paint(RAW.cyan, line));
59
+ console.log(paint(RAW.magenta, ` Agent Skills · F6S & LinkedIn copywriting${PKG_VERSION ? ' · v' + PKG_VERSION : ''}`));
60
+ console.log('');
61
+ }
62
+
39
63
  /**
40
64
  * Agent registry. `format`:
41
65
  * - 'skill': copy the skill folder verbatim (SKILL.md + any files)
42
66
  * - 'rule' : write one rule file derived from SKILL.md (ext required)
43
- * `project` is resolved from cwd; `global` uses ~ for the home directory.
67
+ * `project` resolves from cwd; `global` uses ~ for the home directory.
68
+ * Newer tools' folders are best-effort conventions — edit freely.
44
69
  */
45
70
  const AGENTS = {
46
- claude: {
47
- label: 'Claude Code', format: 'skill',
48
- project: '.claude/skills', global: '~/.claude/skills',
49
- },
50
- copilot: {
51
- label: 'GitHub Copilot', format: 'skill',
52
- project: '.github/skills', global: '~/.copilot/skills',
53
- },
54
- cursor: {
55
- label: 'Cursor', format: 'rule', ext: '.mdc',
56
- project: '.cursor/rules', global: '~/.cursor/rules',
57
- },
58
- windsurf: {
59
- label: 'Windsurf', format: 'rule', ext: '.md',
60
- project: '.windsurf/rules', global: '~/.codeium/windsurf/global_rules',
61
- },
62
- codex: {
63
- label: 'Codex CLI', format: 'skill',
64
- project: '.codex/skills', global: '~/.codex/skills',
65
- },
66
- gemini: {
67
- label: 'Gemini CLI', format: 'skill',
68
- project: '.gemini/skills', global: '~/.gemini/skills',
69
- },
71
+ claude: { label: 'Claude Code', format: 'skill', project: '.claude/skills', global: '~/.claude/skills' },
72
+ copilot: { label: 'GitHub Copilot', format: 'skill', project: '.github/skills', global: '~/.copilot/skills' },
73
+ cursor: { label: 'Cursor', format: 'rule', ext: '.mdc', project: '.cursor/rules', global: '~/.cursor/rules' },
74
+ windsurf: { label: 'Windsurf', format: 'rule', ext: '.md', project: '.windsurf/rules', global: '~/.codeium/windsurf/global_rules' },
75
+ codex: { label: 'Codex CLI', format: 'skill', project: '.codex/skills', global: '~/.codex/skills' },
76
+ gemini: { label: 'Gemini CLI', format: 'skill', project: '.gemini/skills', global: '~/.gemini/skills' },
77
+ opencode: { label: 'OpenCode', format: 'skill', project: '.opencode/skills', global: '~/.config/opencode/skills' },
78
+ cline: { label: 'Cline', format: 'rule', ext: '.md', project: '.clinerules', global: '~/.clinerules' },
79
+ roo: { label: 'Roo Code', format: 'rule', ext: '.md', project: '.roo/rules', global: '~/.roo/rules' },
80
+ kilocode: { label: 'Kilo Code', format: 'rule', ext: '.md', project: '.kilocode/rules', global: '~/.kilocode/rules' },
81
+ continue: { label: 'Continue', format: 'rule', ext: '.md', project: '.continue/rules', global: '~/.continue/rules' },
82
+ amp: { label: 'Amp', format: 'rule', ext: '.md', project: '.amp/rules', global: '~/.config/amp/rules' },
70
83
  };
71
84
 
72
85
  function expandHome(p) {
@@ -74,26 +87,21 @@ function expandHome(p) {
74
87
  if (p.startsWith('~/')) return path.join(HOME, p.slice(2));
75
88
  return p;
76
89
  }
77
-
78
90
  function resolveTarget(agent, scope) {
79
91
  const rel = scope === 'project' ? agent.project : agent.global;
80
- return scope === 'project'
81
- ? path.resolve(process.cwd(), rel)
82
- : path.resolve(expandHome(rel));
92
+ return scope === 'project' ? path.resolve(process.cwd(), rel) : path.resolve(expandHome(rel));
83
93
  }
84
94
 
85
95
  function availableSkills() {
86
96
  if (!fs.existsSync(SKILLS_DIR)) return [];
87
97
  return fs.readdirSync(SKILLS_DIR, { withFileTypes: true })
88
98
  .filter((d) => d.isDirectory() && fs.existsSync(path.join(SKILLS_DIR, d.name, 'SKILL.md')))
89
- .map((d) => d.name)
90
- .sort();
99
+ .map((d) => d.name).sort();
91
100
  }
92
101
 
93
102
  function readSkill(name) {
94
103
  const raw = fs.readFileSync(path.join(SKILLS_DIR, name, 'SKILL.md'), 'utf8');
95
- let description = name;
96
- let body = raw;
104
+ let description = name, body = raw;
97
105
  const m = raw.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
98
106
  if (m) {
99
107
  body = m[2];
@@ -102,14 +110,19 @@ function readSkill(name) {
102
110
  }
103
111
  return { description, body: body.replace(/^\s+/, '') };
104
112
  }
113
+ function skillHint(name) {
114
+ try {
115
+ const d = readSkill(name).description;
116
+ const first = d.split('. ')[0];
117
+ return first.length > 52 ? first.slice(0, 49) + '…' : first;
118
+ } catch (_) { return ''; }
119
+ }
105
120
 
106
121
  function copyDir(src, dest) {
107
122
  fs.mkdirSync(dest, { recursive: true });
108
123
  for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
109
- const s = path.join(src, entry.name);
110
- const d = path.join(dest, entry.name);
111
- if (entry.isDirectory()) copyDir(s, d);
112
- else fs.copyFileSync(s, d);
124
+ const s = path.join(src, entry.name), d = path.join(dest, entry.name);
125
+ if (entry.isDirectory()) copyDir(s, d); else fs.copyFileSync(s, d);
113
126
  }
114
127
  }
115
128
 
@@ -120,87 +133,147 @@ function installToAgent(agentKey, agent, scope, skills) {
120
133
  if (agent.format === 'skill') {
121
134
  const dest = path.join(targetDir, skill);
122
135
  copyDir(path.join(SKILLS_DIR, skill), dest);
123
- ok(`${agent.label} ${c.dim}(${scope})${c.reset} ${skill} ${c.dim}-> ${dest}${c.reset}`);
136
+ console.log(` ${paint(RAW.green, '✓')} ${paint(RAW.bold, agent.label)} ${c.dim}(${scope === 'project' ? 'workspace' : scope})${c.reset} ${skill} ${c.dim} ${dest}${c.reset}`);
124
137
  } else {
125
138
  const { description, body } = readSkill(skill);
126
139
  const front = `---\ndescription: ${description}\nalwaysApply: false\n---\n\n`;
127
140
  const file = path.join(targetDir, skill + (agent.ext || '.md'));
128
141
  fs.writeFileSync(file, front + body);
129
- ok(`${agent.label} ${c.dim}(${scope})${c.reset} ${skill} ${c.dim}-> ${file}${c.reset}`);
142
+ console.log(` ${paint(RAW.green, '✓')} ${paint(RAW.bold, agent.label)} ${c.dim}(${scope === 'project' ? 'workspace' : scope})${c.reset} ${skill} ${c.dim} ${file}${c.reset}`);
130
143
  }
131
144
  }
132
145
  }
133
146
 
134
- function usage() {
135
- console.log(`spirewise install copywriting Agent Skills into all your AI agents
136
-
137
- Usage:
138
- spirewise install [skill ...] [options]
139
- spirewise list
140
- spirewise agents
141
-
142
- Scope (where to install):
143
- --workspace Install only into this folder / current project (alias: --project)
144
- --global Install only into global/home folders
145
- --both Install into both workspace and global
146
- (no scope flag -> you are asked interactively)
147
-
148
- Selection:
149
- [skill ...] Limit to named skills (default: all)
150
- --agent <a,b> Limit to named agents (default: all)
151
-
152
- Other:
153
- -h, --help Show this help
147
+ function box(lines) {
148
+ const vis = (s) => s.replace(/\x1b\[[0-9;]*m/g, '');
149
+ const width = Math.max(...lines.map((l) => vis(l).length));
150
+ console.log(paint(RAW.green, ' ┌' + '─'.repeat(width + 2) + '┐'));
151
+ for (const l of lines) console.log(paint(RAW.green, ' │ ') + l + ' '.repeat(width - vis(l).length) + paint(RAW.green, ' │'));
152
+ console.log(paint(RAW.green, ' └' + '─'.repeat(width + 2) + '┘'));
153
+ }
154
154
 
155
- Supported agents:
156
- ${Object.entries(AGENTS).map(([k, a]) => ` - ${k} (${a.label})`).join('\n')}
155
+ // --- Interactive full-width selector ---------------------------------------
156
+ function interactiveSelect({ title, subtitle, items, multi = true, preselected = [] }) {
157
+ return new Promise((resolve) => {
158
+ const stdin = process.stdin, stdout = process.stdout;
159
+ if (!stdin.isTTY) { resolve(null); return; }
160
+
161
+ let index = 0;
162
+ const selected = new Set();
163
+ if (multi) items.forEach((it, i) => { if (preselected.includes(it.value)) selected.add(i); });
164
+ else { const pi = items.findIndex((it) => preselected.includes(it.value)); if (pi >= 0) index = pi; }
165
+
166
+ const cols = () => stdout.columns || 80;
167
+ let lastLines = 0;
168
+
169
+ const bar = (text) => {
170
+ const w = cols(), label = ` ${text} `;
171
+ const side = Math.max(0, w - label.length), left = Math.floor(side / 2);
172
+ return paint(RAW.cyan, '═'.repeat(left) + label + '═'.repeat(side - left));
173
+ };
174
+
175
+ function render() {
176
+ const lines = ['', bar(title)];
177
+ if (subtitle) lines.push(paint(RAW.dim, ' ' + subtitle));
178
+ lines.push('');
179
+ items.forEach((it, i) => {
180
+ const cur = i === index ? paint(RAW.cyan, '❯') : ' ';
181
+ const mark = multi ? (selected.has(i) ? paint(RAW.green, '◉') : paint(RAW.dim, '◯'))
182
+ : (i === index ? paint(RAW.green, '◉') : paint(RAW.dim, '◯'));
183
+ const label = i === index ? paint(RAW.bold, it.label) : it.label;
184
+ const hint = it.hint ? paint(RAW.dim, ' ' + it.hint) : '';
185
+ lines.push(` ${cur} ${mark} ${label}${hint}`);
186
+ });
187
+ lines.push('');
188
+ lines.push(paint(RAW.dim, ' ' + (multi
189
+ ? '↑/↓ move · space toggle · a all/none · enter confirm · esc cancel'
190
+ : '↑/↓ move · enter confirm · esc cancel')));
191
+ if (lastLines > 0) stdout.write(`\x1b[${lastLines}A`);
192
+ stdout.write('\x1b[0J' + lines.join('\n') + '\n');
193
+ lastLines = lines.length;
194
+ }
157
195
 
158
- Skills:
159
- ${availableSkills().map((s) => ' - ' + s).join('\n') || ' (none found)'}
196
+ function cleanup() {
197
+ try { stdin.setRawMode(false); } catch (_) {}
198
+ stdin.pause();
199
+ stdin.removeListener('data', onData);
200
+ }
201
+ const finish = (r) => { cleanup(); resolve(r); };
202
+
203
+ function onData(s) {
204
+ if (s === '\x03' || s === '\x1b' || s === 'q') return finish(null); // ctrl-c / esc / q
205
+ if (s === '\r' || s === '\n') {
206
+ return finish(multi ? items.filter((_, i) => selected.has(i)).map((it) => it.value) : items[index].value);
207
+ }
208
+ if (s === '\x1b[A' || s === 'k') { index = (index - 1 + items.length) % items.length; return render(); }
209
+ if (s === '\x1b[B' || s === 'j') { index = (index + 1) % items.length; return render(); }
210
+ if (multi && s === ' ') { selected.has(index) ? selected.delete(index) : selected.add(index); return render(); }
211
+ if (multi && (s === 'a' || s === 'A')) {
212
+ if (selected.size === items.length) selected.clear(); else items.forEach((_, i) => selected.add(i));
213
+ return render();
214
+ }
215
+ if (!multi && s === ' ') return finish(items[index].value);
216
+ }
160
217
 
161
- Examples:
162
- npx spirewise install --both
163
- npx spirewise install --project --agent claude,cursor
164
- npx spirewise install f6s-copywriting --global
165
- `);
218
+ stdin.setRawMode(true); stdin.resume(); stdin.setEncoding('utf8');
219
+ stdin.on('data', onData);
220
+ render();
221
+ });
166
222
  }
167
223
 
224
+ // --- args -------------------------------------------------------------------
225
+ function splitList(v) { return String(v).split(',').map((s) => s.trim()).filter(Boolean); }
226
+
168
227
  function parseArgs(argv) {
169
- const o = { scope: null, agents: null, skills: [] };
228
+ const o = { scope: null, agents: null, skills: null, positional: [] };
229
+ const norm = (s) => (s === 'project' || s === 'workspace' || s === 'local') ? 'project' : s;
170
230
  for (let i = 0; i < argv.length; i++) {
171
- const a = argv[i];
172
- if (a === '--project' || a === '--workspace' || a === '--local') o.scope = 'project';
173
- else if (a === '--global') o.scope = 'global';
174
- else if (a === '--both') o.scope = 'both';
175
- else if (a === '--scope') o.scope = (argv[++i] || die('--scope needs a value'));
176
- else if (a === '--agent' || a === '--agents') o.agents = (argv[++i] || die('--agent needs a value')).split(',').map((s) => s.trim()).filter(Boolean);
177
- else if (a === '-h' || a === '--help') { usage(); process.exit(0); }
178
- else if (a.startsWith('-')) die(`Unknown option: ${a}`);
179
- else o.skills.push(a);
231
+ let a = argv[i], val = null;
232
+ if (a.startsWith('--') && a.includes('=')) { const idx = a.indexOf('='); val = a.slice(idx + 1); a = a.slice(0, idx); }
233
+ const next = () => (val !== null ? val : argv[++i]);
234
+ switch (a) {
235
+ case '-s': case '--skills': o.skills = splitList(next() || die('--skills needs a value')); break;
236
+ case '-a': case '--agents': case '--agent': o.agents = splitList(next() || die('--agents needs a value')); break;
237
+ case '-sc': case '--scope': o.scope = norm((next() || die('--scope needs a value')).toLowerCase()); break;
238
+ case '--workspace': case '--project': case '--local': o.scope = 'project'; break;
239
+ case '--global': o.scope = 'global'; break;
240
+ case '--both': o.scope = 'both'; break;
241
+ case '-h': case '--help': usage(); process.exit(0); break;
242
+ default:
243
+ if (a.startsWith('-')) die(`Unknown option: ${a}`);
244
+ o.positional.push(a);
245
+ }
180
246
  }
247
+ if (!o.skills && o.positional.length) o.skills = o.positional;
181
248
  return o;
182
249
  }
183
250
 
184
- function ask(question) {
185
- return new Promise((resolve) => {
186
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
187
- rl.question(question, (ans) => { rl.close(); resolve(ans.trim()); });
188
- });
189
- }
251
+ function usage() {
252
+ console.log(`spirewise install copywriting Agent Skills into your AI agents
190
253
 
191
- async function promptScope() {
192
- if (!process.stdin.isTTY) {
193
- warn('No terminal detected; defaulting scope to "project". Use --global or --both to change.');
194
- return 'project';
195
- }
196
- info('Where should the skills be installed?');
197
- console.log(' 1) Workspace (this folder only the current project directory)');
198
- console.log(' 2) Global (your home folders applies to all projects)');
199
- console.log(' 3) Both');
200
- const ans = (await ask(' Choose 1/2/3 [1]: ')).toLowerCase();
201
- if (ans === '2' || ans === 'global') return 'global';
202
- if (ans === '3' || ans === 'both') return 'both';
203
- return 'project';
254
+ Usage:
255
+ spirewise [install] [options] run interactive picker for anything not set
256
+ spirewise list list available skills
257
+ spirewise agents list supported agents + folders
258
+
259
+ Options:
260
+ -s, --skills <a,b> skills to install (default: all / pick)
261
+ -a, --agents <a,b> agents to target (default: all / pick) alias: --agent
262
+ -sc, --scope <s> workspace | global | both (default: pick)
263
+ --workspace | --global | --both scope shortcuts
264
+ -h, --help
265
+
266
+ Agents:
267
+ ${Object.entries(AGENTS).map(([k, a]) => ` - ${k} (${a.label})`).join('\n')}
268
+
269
+ Skills:
270
+ ${availableSkills().map((s) => ' - ' + s).join('\n') || ' (none found)'}
271
+
272
+ Examples:
273
+ npx spirewise # full interactive picker
274
+ npx spirewise -sc both # pick skills+agents, scope=both
275
+ npx spirewise -s f6s-copywriting -a claude,cursor -sc workspace
276
+ `);
204
277
  }
205
278
 
206
279
  async function main() {
@@ -213,44 +286,88 @@ async function main() {
213
286
  if (available.length === 0) die('No skills found in package.');
214
287
 
215
288
  if (command === 'list') {
216
- info('Available skills:');
217
- available.forEach((s) => console.log(' - ' + s));
218
- return;
289
+ info('Available skills:'); available.forEach((s) => console.log(' - ' + s)); return;
219
290
  }
220
291
  if (command === 'agents') {
221
292
  info('Supported agents and their folders:');
222
293
  for (const [k, a] of Object.entries(AGENTS)) {
223
- console.log(` ${k} (${a.label}) [${a.format}]`);
224
- console.log(` project: ${a.project}`);
225
- console.log(` global: ${a.global}`);
294
+ console.log(` ${paint(RAW.bold, k)} (${a.label}) [${a.format}]`);
295
+ console.log(` workspace: ${a.project}`);
296
+ console.log(` global: ${a.global}`);
226
297
  }
227
298
  return;
228
299
  }
229
300
 
230
301
  const o = parseArgs(argv);
231
-
232
- let agentKeys = Object.keys(AGENTS);
233
- if (o.agents) {
234
- for (const k of o.agents) if (!AGENTS[k]) die(`Unknown agent '${k}'. Run "spirewise agents".`);
235
- agentKeys = o.agents;
236
- }
237
-
238
- let skills = o.skills.length ? o.skills : available.slice();
239
- for (const s of skills) if (!available.includes(s)) die(`Unknown skill '${s}'. Run "spirewise list".`);
240
-
241
- let scope = o.scope || await promptScope();
302
+ const tty = process.stdin.isTTY;
303
+ banner();
304
+
305
+ // 1) SKILLS
306
+ let skills = o.skills;
307
+ if (skills) {
308
+ for (const s of skills) if (!available.includes(s)) die(`Unknown skill '${s}'. Run "spirewise list".`);
309
+ } else if (tty) {
310
+ skills = await interactiveSelect({
311
+ title: 'Step 1/3 · Select skills',
312
+ subtitle: 'Copy templates to install into your agents',
313
+ items: available.map((s) => ({ value: s, label: s, hint: skillHint(s) })),
314
+ multi: true, preselected: available,
315
+ });
316
+ if (skills === null) die('Cancelled.');
317
+ if (!skills.length) die('No skills selected.');
318
+ } else { warn('No terminal; defaulting to all skills.'); skills = available.slice(); }
319
+
320
+ // 2) AGENTS
321
+ let agentKeys = o.agents;
322
+ if (agentKeys) {
323
+ for (const k of agentKeys) if (!AGENTS[k]) die(`Unknown agent '${k}'. Run "spirewise agents".`);
324
+ } else if (tty) {
325
+ agentKeys = await interactiveSelect({
326
+ title: 'Step 2/3 · Select agents',
327
+ subtitle: 'Each agent gets the skills in its own folder + format',
328
+ items: Object.entries(AGENTS).map(([k, a]) => ({ value: k, label: a.label, hint: `(${k}) · ${a.format}` })),
329
+ multi: true, preselected: Object.keys(AGENTS),
330
+ });
331
+ if (agentKeys === null) die('Cancelled.');
332
+ if (!agentKeys.length) die('No agents selected.');
333
+ } else { warn('No terminal; defaulting to all agents.'); agentKeys = Object.keys(AGENTS); }
334
+
335
+ // 3) SCOPE
336
+ let scope = o.scope;
337
+ if (!scope && tty) {
338
+ scope = await interactiveSelect({
339
+ title: 'Step 3/3 · Select scope',
340
+ subtitle: 'Where should the skills live?',
341
+ items: [
342
+ { value: 'project', label: 'Workspace', hint: 'this folder only — the current project' },
343
+ { value: 'global', label: 'Global', hint: 'your home folders — applies to all projects' },
344
+ { value: 'both', label: 'Both', hint: 'workspace + global' },
345
+ ],
346
+ multi: false, preselected: ['project'],
347
+ });
348
+ if (scope === null) die('Cancelled.');
349
+ } else if (!scope) { warn('No terminal; defaulting scope to "workspace".'); scope = 'project'; }
242
350
  if (!['project', 'global', 'both'].includes(scope)) die(`Invalid scope '${scope}'.`);
243
351
  const scopes = scope === 'both' ? ['project', 'global'] : [scope];
244
352
 
245
- info(`Installing ${skills.length} skill(s) into ${agentKeys.length} agent(s): ${agentKeys.join(', ')}`);
246
- info(`Scope: ${scope}`);
247
-
248
- for (const sc of scopes) {
249
- for (const k of agentKeys) installToAgent(k, AGENTS[k], sc, skills);
250
- }
353
+ // INSTALL
354
+ console.log('');
355
+ info(`Installing ${paint(RAW.bold, String(skills.length))} skill(s) into ${paint(RAW.bold, String(agentKeys.length))} agent(s) · scope ${paint(RAW.bold, scope === 'project' ? 'workspace' : scope)}`);
356
+ let count = 0;
357
+ for (const sc of scopes) for (const k of agentKeys) { installToAgent(k, AGENTS[k], sc, skills); count += skills.length; }
358
+
359
+ console.log('');
360
+ box([
361
+ `Installed ${paint(RAW.bold, String(skills.length))} skill(s) into ${paint(RAW.bold, String(agentKeys.length))} agent(s)`,
362
+ `scope: ${scopes.map((s) => (s === 'project' ? 'workspace' : s)).join(' + ')} · ${count} item(s) written`,
363
+ `next: open your agent and say "write our F6S profile copy"`,
364
+ ]);
365
+ console.log('');
366
+ }
251
367
 
252
- ok(`Done. ${skills.length} skill(s) -> ${agentKeys.length} agent(s) (${scopes.join(' + ')}).`);
253
- info('Restart your agent or reload skills/rules if needed.');
368
+ if (require.main === module) {
369
+ main().catch((e) => die(e && e.message ? e.message : String(e)));
370
+ } else {
371
+ module.exports = { interactiveSelect, AGENTS, availableSkills, parseArgs };
254
372
  }
255
373
 
256
- main().catch((e) => die(e && e.message ? e.message : String(e)));
package/install.sh CHANGED
@@ -30,6 +30,12 @@ AGENTS=(
30
30
  "windsurf|Windsurf|rule|.md|.windsurf/rules|HOME/.codeium/windsurf/global_rules"
31
31
  "codex|Codex CLI|skill||.codex/skills|HOME/.codex/skills"
32
32
  "gemini|Gemini CLI|skill||.gemini/skills|HOME/.gemini/skills"
33
+ "opencode|OpenCode|skill||.opencode/skills|HOME/.config/opencode/skills"
34
+ "cline|Cline|rule|.md|.clinerules|HOME/.clinerules"
35
+ "roo|Roo Code|rule|.md|.roo/rules|HOME/.roo/rules"
36
+ "kilocode|Kilo Code|rule|.md|.kilocode/rules|HOME/.kilocode/rules"
37
+ "continue|Continue|rule|.md|.continue/rules|HOME/.continue/rules"
38
+ "amp|Amp|rule|.md|.amp/rules|HOME/.config/amp/rules"
33
39
  )
34
40
 
35
41
  color() { printf '\033[%sm%s\033[0m' "$1" "$2"; }
@@ -37,27 +43,41 @@ info() { printf '%s %s\n' "$(color '1;34' '==>')" "$1"; }
37
43
  ok() { printf '%s %s\n' "$(color '1;32' ' ok')" "$1"; }
38
44
  warn() { printf '%s %s\n' "$(color '1;33' ' !')" "$1" >&2; }
39
45
  die() { printf '%s %s\n' "$(color '1;31' 'err')" "$1" >&2; exit 1; }
46
+ step() { printf '%s %s %s\n' "$(color '1;36' ' >')" "$(color '1' "Step $1/4")" "$2"; }
47
+ substep() { printf ' %s %s\n' "$(color '1;32' 'v')" "$1"; }
48
+
49
+ banner() {
50
+ printf '\n'
51
+ color '1;36' ' ███████╗██████╗ ██╗██████╗ ███████╗██╗ ██╗██╗███████╗███████╗'; printf '\n'
52
+ color '1;36' ' ██╔════╝██╔══██╗██║██╔══██╗██╔════╝██║ ██║██║██╔════╝██╔════╝'; printf '\n'
53
+ color '1;36' ' ███████╗██████╔╝██║██████╔╝█████╗ ██║ █╗ ██║██║███████╗█████╗ '; printf '\n'
54
+ color '1;36' ' ╚════██║██╔═══╝ ██║██╔══██╗██╔══╝ ██║███╗██║██║╚════██║██╔══╝ '; printf '\n'
55
+ color '1;36' ' ███████║██║ ██║██║ ██║███████╗╚███╔███╔╝██║███████║███████╗'; printf '\n'
56
+ color '1;36' ' ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ ╚══╝╚══╝ ╚═╝╚══════╝╚══════╝'; printf '\n'
57
+ color '1;35' ' Agent Skills · F6S & LinkedIn copywriting'; printf '\n\n'
58
+ }
40
59
 
41
60
  usage() {
42
61
  cat <<EOF
43
62
  spirewise — install copywriting Agent Skills into all your AI agents.
44
63
 
45
64
  Usage:
46
- install.sh [scope] [--agent a,b] [skill ...]
65
+ install.sh [scope] [-a agents] [-s skills]
47
66
 
48
67
  Scope:
49
- --workspace install only into this folder / current project (alias: --project)
50
- --global install only into global/home folders
51
- --both install into both
68
+ -sc, --scope <s> workspace | global | both
69
+ --workspace this folder / current project (alias: --project)
70
+ --global global/home folders
71
+ --both both
52
72
  (no scope -> you are asked interactively)
53
73
 
54
74
  Selection:
55
- [skill ...] limit to named skills (default: all)
56
- --agent <a,b> limit to named agents (default: all)
57
- --list list available skills
58
- -h, --help show this help
75
+ -s, --skills <a,b> limit to named skills (default: all)
76
+ -a, --agents <a,b> limit to named agents (default: all) alias: --agent
77
+ --list list available skills
78
+ -h, --help show this help
59
79
 
60
- Agents: claude copilot cursor windsurf codex gemini
80
+ Agents: claude copilot cursor windsurf codex gemini opencode cline roo kilocode continue amp
61
81
  EOF
62
82
  }
63
83
 
@@ -148,13 +168,13 @@ install_for_agent() {
148
168
  if [[ "$fmt" == "skill" ]]; then
149
169
  mkdir -p "$dir/$s"
150
170
  cp -R "$base/$s/." "$dir/$s/"
151
- ok "$label ($scope) $s -> $dir/$s"
171
+ substep "$label ($scope) $s -> $dir/$s"
152
172
  else
153
173
  local desc body file
154
174
  desc="$(skill_description "$base/$s/SKILL.md")"
155
175
  file="$dir/$s$ext"
156
176
  { printf -- '---\ndescription: %s\nalwaysApply: false\n---\n\n' "$desc"; skill_body "$base/$s/SKILL.md"; } >"$file"
157
- ok "$label ($scope) $s -> $file"
177
+ substep "$label ($scope) $s -> $file"
158
178
  fi
159
179
  done
160
180
  }
@@ -169,7 +189,12 @@ while [[ $# -gt 0 ]]; do
169
189
  --project|--workspace|--local) SCOPE="project"; shift ;;
170
190
  --global) SCOPE="global"; shift ;;
171
191
  --both) SCOPE="both"; shift ;;
172
- --agent|--agents) AGENT_FILTER="${2:?--agent needs a value}"; shift 2 ;;
192
+ -sc|--scope)
193
+ v="${2:?--scope needs a value}"; shift 2
194
+ case "$v" in workspace|project|local) SCOPE="project";; global) SCOPE="global";; both) SCOPE="both";; *) die "Invalid scope '$v'";; esac ;;
195
+ -a|--agent|--agents) AGENT_FILTER="${2:?--agents needs a value}"; shift 2 ;;
196
+ -s|--skills)
197
+ IFS=',' read -r -a _sk <<<"${2:?--skills needs a value}"; SELECTED+=("${_sk[@]}"); shift 2 ;;
173
198
  --list) DO_LIST=true; shift ;;
174
199
  -h|--help) usage; exit 0 ;;
175
200
  -*) die "Unknown option: $1" ;;
@@ -187,14 +212,23 @@ if $DO_LIST; then
187
212
  info "Available skills:"; printf ' - %s\n' "${AVAILABLE[@]}"; exit 0
188
213
  fi
189
214
 
190
- # Skills: default all.
215
+ banner
216
+
217
+ # Step 1: skills (default all).
218
+ step 1 "Scanning available skills"
191
219
  [[ ${#SELECTED[@]} -eq 0 ]] && SELECTED=("${AVAILABLE[@]}")
192
220
  for s in "${SELECTED[@]}"; do
193
221
  found=false; for a in "${AVAILABLE[@]}"; do [[ "$s" == "$a" ]] && found=true && break; done
194
222
  $found || die "Unknown skill '$s'. Use --list."
195
223
  done
224
+ substep "${#SELECTED[@]} skill(s): ${SELECTED[*]}"
196
225
 
197
- # Scope: prompt if not given.
226
+ # Step 2: agents
227
+ step 2 "Selecting target agents"
228
+ if [[ -n "$AGENT_FILTER" ]]; then substep "agents: $AGENT_FILTER"; else substep "agents: all supported"; fi
229
+
230
+ # Step 3: scope — prompt if not given.
231
+ step 3 "Choosing install scope"
198
232
  if [[ -z "$SCOPE" ]]; then
199
233
  if [[ -t 0 ]]; then
200
234
  info "Where should the skills be installed?"
@@ -209,11 +243,12 @@ if [[ -z "$SCOPE" ]]; then
209
243
  SCOPE="project"
210
244
  fi
211
245
  fi
246
+ [[ "$SCOPE" == "project" ]] && substep "scope: workspace" || substep "scope: $SCOPE"
212
247
 
213
248
  SCOPES=("$SCOPE"); [[ "$SCOPE" == "both" ]] && SCOPES=("project" "global")
214
249
 
215
- info "Installing ${#SELECTED[@]} skill(s) into all matching agents. Scope: $SCOPE"
216
-
250
+ # Step 4: install
251
+ step 4 "Installing"
217
252
  for sc in "${SCOPES[@]}"; do
218
253
  for entry in "${AGENTS[@]}"; do
219
254
  IFS='|' read -r key label fmt ext pdir gdir <<<"$entry"
@@ -225,5 +260,6 @@ for sc in "${SCOPES[@]}"; do
225
260
  done
226
261
  done
227
262
 
228
- ok "Done."
229
- info "Restart your agent or reload skills/rules if needed."
263
+ printf '\n'
264
+ ok "Installed ${#SELECTED[@]} skill(s). Next: open your agent and say \"write our F6S profile copy\"."
265
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spirewise",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Installable Agent Skills for copywriting (F6S & LinkedIn company pages) for GitHub Copilot, Claude Code, and Cursor.",
5
5
  "bin": {
6
6
  "spirewise": "bin/cli.js"
package/skills/README.md CHANGED
@@ -31,35 +31,45 @@ LinkedIn company page copy" and it will follow the skill: gather inputs, write t
31
31
 
32
32
  ## Install via command
33
33
 
34
- The easiest way is the npm CLI it installs into **all** supported agents at
35
- once and asks whether to install for the current **project** or **globally**:
34
+ The easiest way is the npm CLI. Run it with no flags for a full-screen
35
+ interactive picker (skills agents scope), or pass flags to skip steps:
36
36
 
37
37
  ```bash
38
- npx spirewise install # asks: project / global / both
39
- npx spirewise install --both # project + global, all agents
40
- npx spirewise install --workspace # this folder only (current project)
41
- npx spirewise install --global # all projects (home folders)
38
+ npx spirewise # interactive picker for everything
39
+ npx spirewise -sc both # pick skills+agents, scope = both
40
+ npx spirewise -s f6s-copywriting -a claude,cursor -sc workspace
41
+ npx spirewise list # list skills
42
+ npx spirewise agents # agents + folders
42
43
  ```
43
44
 
44
- Narrow it down:
45
+ Flags:
45
46
 
46
- ```bash
47
- npx spirewise install --both --agent claude,cursor # only some agents
48
- npx spirewise install f6s-copywriting --global # only some skills
49
- npx spirewise list # list skills
50
- npx spirewise agents # agents + folders
51
47
  ```
48
+ -s, --skills <a,b> skills to install (default: all / pick)
49
+ -a, --agents <a,b> agents to target (default: all / pick) alias: --agent
50
+ -sc, --scope <s> workspace | global | both
51
+ --workspace | --global | --both scope shortcuts
52
+ ```
53
+
54
+ In the picker: **↑/↓** move, **space** toggle, **a** all/none, **enter**
55
+ confirm, **esc** cancel.
52
56
 
53
- Supported agents and their folders (project / global):
57
+ Supported agents and their folders (workspace / global):
54
58
 
55
- | Agent | Project | Global | Format |
56
- |-------|---------|--------|--------|
59
+ | Agent | Workspace | Global | Format |
60
+ |-------|-----------|--------|--------|
57
61
  | Claude Code | `.claude/skills/` | `~/.claude/skills/` | SKILL.md |
58
62
  | GitHub Copilot | `.github/skills/` | `~/.copilot/skills/` | SKILL.md |
59
63
  | Cursor | `.cursor/rules/` | `~/.cursor/rules/` | `.mdc` |
60
64
  | Windsurf | `.windsurf/rules/` | `~/.codeium/windsurf/global_rules/` | `.md` |
61
65
  | Codex CLI | `.codex/skills/` | `~/.codex/skills/` | SKILL.md |
62
66
  | Gemini CLI | `.gemini/skills/` | `~/.gemini/skills/` | SKILL.md |
67
+ | OpenCode | `.opencode/skills/` | `~/.config/opencode/skills/` | SKILL.md |
68
+ | Cline | `.clinerules/` | `~/.clinerules/` | `.md` |
69
+ | Roo Code | `.roo/rules/` | `~/.roo/rules/` | `.md` |
70
+ | Kilo Code | `.kilocode/rules/` | `~/.kilocode/rules/` | `.md` |
71
+ | Continue | `.continue/rules/` | `~/.continue/rules/` | `.md` |
72
+ | Amp | `.amp/rules/` | `~/.config/amp/rules/` | `.md` |
63
73
 
64
74
  ### No-Node fallback: `install.sh`
65
75
 
@@ -2,10 +2,12 @@
2
2
  name: f6s-copywriting
3
3
  description: >-
4
4
  Generate complete, ready-to-paste copywriting for an F6S (f6s.com) startup/company
5
- profile page. Use when the user asks to "write our F6S profile", "create F6S
6
- copy", "fill out F6S page", or prepare content for an accelerator / investor
7
- application on F6S. Produces a single .txt file under an `f6s/` folder in the
8
- project root, with every field kept STRICTLY under its character limit.
5
+ profile page, written like a senior unicorn-startup marketer humanized,
6
+ attention-grabbing, and conversion-focused. Use when the user asks to "write our
7
+ F6S profile", "create F6S copy", "fill out F6S page", or prepare content for an
8
+ accelerator / investor application on F6S. Produces a single .txt file under an
9
+ `f6s/` folder in the project root, with every field kept STRICTLY under its
10
+ character limit.
9
11
  ---
10
12
 
11
13
  # F6S Profile Copywriting
@@ -69,6 +71,49 @@ keep the same "stay under" headroom rule.
69
71
  - Write in third person or first-person plural ("we") consistently.
70
72
  - No keyword stuffing; F6S profiles are read by humans (accelerators/investors).
71
73
 
74
+ ## Voice & quality standard — write like a unicorn-startup marketer
75
+
76
+ Write every word as if you are a **senior brand + growth marketer who has led
77
+ messaging for category-defining startups** (think the caliber of Stripe, Notion,
78
+ Linear, Ramp, Airbnb). The copy an accelerator partner or investor reads should
79
+ feel sharp, confident, and unmistakably human. Non-negotiable bar:
80
+
81
+ **1. Humanized — sounds like a real person, never "AI-generated".**
82
+ - Vary sentence length. Mix short, punchy lines with one longer, rhythmic one.
83
+ - Use plain, concrete language a smart 12-year-old could follow. Cut jargon.
84
+ - Write with a point of view and a pulse — confident, not corporate-bland.
85
+ - Read it aloud in your head; if a sentence sounds robotic, rewrite it.
86
+
87
+ **2. Banned "AI tells" — never use these.**
88
+ - Filler verbs/phrases: "leverage", "utilize", "seamlessly", "robust",
89
+ "cutting-edge", "state-of-the-art", "empower", "unlock", "elevate",
90
+ "revolutionize", "game-changer", "in today's fast-paced world",
91
+ "we are committed to", "at the intersection of".
92
+ - Hype with no proof: "best", "#1", "world-class", "industry-leading".
93
+ - Em-dash overuse, three-item lists on autopilot, and symmetrical "not only…
94
+ but also" scaffolding. Break the pattern; write like a human, not a template.
95
+
96
+ **3. Attracting & focused — earn the next 5 seconds of attention.**
97
+ - Hook first. The opening line must make the reader want the next one.
98
+ - One core idea per field. If it does two jobs, split or cut.
99
+ - Specific > vague every time: real numbers, named customers, concrete outcomes.
100
+ "Cut onboarding from 3 weeks to 2 days for 40+ teams" beats "improves
101
+ efficiency".
102
+ - Lead with the customer's problem and the outcome, not your feature list.
103
+ - Active voice, present tense, strong verbs. Subject does the thing.
104
+
105
+ **4. Investor/accelerator lens (this is F6S).**
106
+ - Make the *why now*, the *traction*, and the *wedge* obvious fast.
107
+ - Show momentum with evidence (growth %, revenue, retention, logos, waitlist).
108
+ - Sound fundable and grounded — ambitious vision backed by real proof.
109
+
110
+ **5. Final humanization pass (always run before saving).**
111
+ - Remove every banned word above; replace with plain, specific language.
112
+ - Delete adjectives that aren't earned by a fact.
113
+ - Tighten: if a word can be cut without losing meaning, cut it.
114
+ - Ensure no two consecutive sentences share the same shape or opener.
115
+ - Confirm it reads like one confident human voice end to end.
116
+
72
117
  ## Required .txt output template
73
118
 
74
119
  Write the file using exactly this structure. Put the live character count in
@@ -130,3 +175,5 @@ Other: <url>
130
175
  3. No field is empty unless intentionally a `[PLACEHOLDER]`.
131
176
  4. No banned hype words; tone is factual and specific.
132
177
  5. Confirm counts programmatically (e.g. `awk`/`wc -m` per field) — do not trust eyeballing.
178
+ 6. Humanization pass done: zero banned "AI tells", varied sentence shapes, reads
179
+ like one confident human marketer wrote it, and every claim is specific.
@@ -1,11 +1,12 @@
1
1
  ---
2
2
  name: linkedin-copywriting
3
3
  description: >-
4
- Generate complete, ready-to-paste copywriting for a LinkedIn Company Page.
5
- Use when the user asks to "write our LinkedIn page", "create LinkedIn company
6
- copy", "fill out the About section", or prepare LinkedIn page content. Produces
7
- a single .txt file under a `linkedin copywriting/` folder in the project root,
8
- with every field kept STRICTLY under its character limit.
4
+ Generate complete, ready-to-paste copywriting for a LinkedIn Company Page,
5
+ written like a senior unicorn-startup marketer humanized, attention-grabbing,
6
+ and conversion-focused. Use when the user asks to "write our LinkedIn page",
7
+ "create LinkedIn company copy", "fill out the About section", or prepare LinkedIn
8
+ page content. Produces a single .txt file under a `linkedin copywriting/` folder
9
+ in the project root, with every field kept STRICTLY under its character limit.
9
10
  ---
10
11
 
11
12
  # LinkedIn Company Page Copywriting
@@ -68,6 +69,50 @@ the same "stay under" headroom rule.
68
69
  - Tone: professional, confident, specific. Avoid empty hype.
69
70
  - End the About section with a clear next step (visit site, follow, contact).
70
71
 
72
+ ## Voice & quality standard — write like a unicorn-startup marketer
73
+
74
+ Write every word as if you are a **senior brand + growth marketer who has led
75
+ messaging for category-defining startups** (think the caliber of Stripe, Notion,
76
+ Linear, Ramp, Airbnb). The page should feel sharp, confident, and unmistakably
77
+ human — like a person customers and recruiters *want* to follow. Non-negotiable:
78
+
79
+ **1. Humanized — sounds like a real person, never "AI-generated".**
80
+ - Vary sentence length. Mix short, punchy lines with one longer, rhythmic one.
81
+ - Plain, concrete language a smart 12-year-old could follow. Cut jargon.
82
+ - Write with a point of view and a pulse — confident, not corporate-bland.
83
+ - Read it aloud in your head; if a sentence sounds robotic, rewrite it.
84
+
85
+ **2. Banned "AI tells" — never use these.**
86
+ - Filler: "leverage", "utilize", "seamlessly", "robust", "cutting-edge",
87
+ "state-of-the-art", "empower", "unlock", "elevate", "revolutionize",
88
+ "game-changer", "in today's fast-paced world", "we are committed to",
89
+ "passionate about", "at the intersection of", "solutions provider".
90
+ - Hype with no proof: "best", "#1", "world-class", "industry-leading".
91
+ - Em-dash overuse, robotic three-item lists, and "not only… but also"
92
+ scaffolding. Break the pattern; write like a human, not a template.
93
+
94
+ **3. Attracting & focused — earn the next 5 seconds of attention.**
95
+ - The first ~150 chars of About are the hook shown before "see more" — make them
96
+ a magnetic, standalone value statement, not a throat-clearing intro.
97
+ - One core idea per section. Specific > vague every time: real numbers, named
98
+ customers, concrete outcomes. "Helps 12,000 founders raise faster" beats
99
+ "provides solutions for startups".
100
+ - Lead with the reader's problem and the outcome, then how you deliver it.
101
+ - Active voice, present tense, strong verbs. Subject does the thing.
102
+
103
+ **4. LinkedIn lens (humans + search).**
104
+ - Weave the terms customers/recruiters/partners actually search — naturally,
105
+ never stuffed. Keywords serve the sentence, not the other way around.
106
+ - Tagline = benefit- or category-led with a real hook, not a hollow slogan.
107
+ - Close About with one clear next step (follow, visit site, get in touch).
108
+
109
+ **5. Final humanization pass (always run before saving).**
110
+ - Remove every banned word above; replace with plain, specific language.
111
+ - Delete adjectives that aren't earned by a fact.
112
+ - Tighten: if a word can be cut without losing meaning, cut it.
113
+ - Ensure no two consecutive sentences share the same shape or opener.
114
+ - Confirm it reads like one confident human voice end to end.
115
+
71
116
  ## Required .txt output template
72
117
 
73
118
  Write the file using exactly this structure. Put the live character count in
@@ -118,3 +163,5 @@ Generated: <YYYY-MM-DD>
118
163
  3. Specialties: ≤ 20 items, each ≤ 28 chars.
119
164
  4. About section's first ~150 chars work as a standalone preview hook.
120
165
  5. Confirm counts programmatically (e.g. `awk`/`wc -m` per field) — do not trust eyeballing.
166
+ 6. Humanization pass done: zero banned "AI tells", varied sentence shapes, reads
167
+ like one confident human marketer wrote it, and every claim is specific.