spirewise 1.0.0 → 1.0.3

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
@@ -2,46 +2,73 @@
2
2
 
3
3
  Installable **Agent Skills** for copywriting — generate ready-to-paste copy for
4
4
  your **F6S** and **LinkedIn** company pages, with strict character-limit safety.
5
- Works with **GitHub Copilot**, **Claude Code**, and **Cursor**.
5
+
6
+ One command installs the skills into **all your AI agents at once** —
7
+ **GitHub Copilot, Claude Code, Cursor, Windsurf, Codex CLI, Gemini CLI** — each in
8
+ its correct folder and format. It asks whether to install for the **current
9
+ project** or **globally** for every project.
6
10
 
7
11
  ## Install
8
12
 
9
- No clone needed — run it with `npx`:
13
+ No clone needed:
10
14
 
11
15
  ```bash
12
- # install every skill
13
- npx spirewise install --all
16
+ npx spirewise install
17
+ ```
14
18
 
15
- # install a single skill
16
- npx spirewise install f6s-copywriting
19
+ You'll be asked where to install:
17
20
 
18
- # install several
19
- npx spirewise install f6s-copywriting linkedin-copywriting
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
26
+ ```
20
27
 
21
- # interactive picker
22
- npx spirewise install
28
+ Skip the prompt with a scope flag:
23
29
 
24
- # list what's available
25
- npx spirewise list
30
+ ```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
26
34
  ```
27
35
 
28
36
  Or install the CLI globally:
29
37
 
30
38
  ```bash
31
39
  npm install -g spirewise
32
- spirewise install --all
40
+ spirewise install --both
33
41
  ```
34
42
 
35
- ## Choose the target agent / folder
43
+ ## What gets installed where
44
+
45
+ | Agent | Project folder | Global folder | Format |
46
+ |-------|----------------|---------------|--------|
47
+ | Claude Code | `.claude/skills/` | `~/.claude/skills/` | SKILL.md |
48
+ | GitHub Copilot | `.github/skills/` | `~/.copilot/skills/` | SKILL.md |
49
+ | Cursor | `.cursor/rules/` | `~/.cursor/rules/` | `.mdc` rule |
50
+ | Windsurf | `.windsurf/rules/` | `~/.codeium/windsurf/global_rules/` | `.md` rule |
51
+ | Codex CLI | `.codex/skills/` | `~/.codex/skills/` | SKILL.md |
52
+ | Gemini CLI | `.gemini/skills/` | `~/.gemini/skills/` | SKILL.md |
53
+
54
+ Rule-based agents (Cursor, Windsurf) get a single rule file generated from the
55
+ skill; the rest get the full `SKILL.md` folder.
56
+
57
+ ## Narrow it down
36
58
 
37
59
  ```bash
38
- spirewise install --all --agent claude # ~/.claude/skills (default)
39
- spirewise install --all --agent copilot # ~/.copilot/skills
40
- spirewise install --all --agent cursor # ~/.cursor/skills
41
- spirewise install --all --target ./skills # any custom folder
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
+ npx spirewise list # available skills
68
+ npx spirewise agents # supported agents + their folders
42
69
  ```
43
70
 
44
- Run `spirewise --help` for all options.
71
+ Run `npx spirewise --help` for all options.
45
72
 
46
73
  ## Skills included
47
74
 
@@ -63,6 +90,18 @@ Once installed, just ask your agent:
63
90
  The agent follows the skill: gathers your company details, writes the `.txt`
64
91
  file in the right folder, and confirms all character counts stay under limit.
65
92
 
93
+ ## No Node? Use the shell installer
94
+
95
+ ```bash
96
+ # from a clone
97
+ ./install.sh --both
98
+
99
+ # remote
100
+ curl -fsSL https://raw.githubusercontent.com/spirerise/spirewise/main/install.sh | bash -s -- --both
101
+ ```
102
+
103
+ It mirrors the CLI: all agents, project/global scope prompt, `--agent` filter.
104
+
66
105
  ## License
67
106
 
68
107
  MIT
package/bin/cli.js CHANGED
@@ -2,42 +2,129 @@
2
2
  'use strict';
3
3
 
4
4
  /**
5
- * spirewise — install copywriting Agent Skills into your AI agent.
5
+ * spirewise — install copywriting Agent Skills into ALL your AI agents at once.
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).
6
9
  *
7
10
  * Usage:
8
- * npx spirewise list
9
- * npx spirewise install --all
10
- * npx spirewise install f6s-copywriting
11
- * npx spirewise install f6s-copywriting linkedin-copywriting
12
- * npx spirewise install (interactive picker)
13
- * npx spirewise install --agent cursor --all
14
- * npx spirewise install --target ./my/skills --all
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
15
19
  */
16
20
 
17
21
  const fs = require('fs');
18
22
  const path = require('path');
23
+ const os = require('os');
19
24
  const readline = require('readline');
20
25
 
21
26
  const PKG_ROOT = path.resolve(__dirname, '..');
22
27
  const SKILLS_DIR = path.join(PKG_ROOT, 'skills');
28
+ const HOME = process.env.HOME || process.env.USERPROFILE || os.homedir();
23
29
 
24
- const c = {
25
- reset: '\x1b[0m', blue: '\x1b[1;34m', green: '\x1b[1;32m',
26
- yellow: '\x1b[1;33m', red: '\x1b[1;31m', dim: '\x1b[2m',
30
+ const USE_COLOR = !process.env.NO_COLOR;
31
+ const RAW = {
32
+ reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m',
33
+ blue: '\x1b[1;34m', green: '\x1b[1;32m', yellow: '\x1b[1;33m',
34
+ red: '\x1b[1;31m', cyan: '\x1b[1;36m', magenta: '\x1b[1;35m',
27
35
  };
36
+ const paint = (code, s) => (USE_COLOR ? `${code}${s}${RAW.reset}` : s);
37
+ const c = USE_COLOR
38
+ ? RAW
39
+ : { reset: '', bold: '', dim: '', blue: '', green: '', yellow: '', red: '', cyan: '', magenta: '' };
40
+
28
41
  const info = (m) => console.log(`${c.blue}==>${c.reset} ${m}`);
29
42
  const ok = (m) => console.log(`${c.green} ok${c.reset} ${m}`);
30
43
  const warn = (m) => console.error(`${c.yellow} !${c.reset} ${m}`);
31
44
  const die = (m) => { console.error(`${c.red}err${c.reset} ${m}`); process.exit(1); };
32
45
 
33
- const AGENT_DIRS = {
34
- claude: path.join(os_home(), '.claude', 'skills'),
35
- copilot: path.join(os_home(), '.copilot', 'skills'),
36
- cursor: path.join(os_home(), '.cursor', 'skills'),
46
+ let PKG_VERSION = '';
47
+ try { PKG_VERSION = require(path.join(PKG_ROOT, 'package.json')).version || ''; } catch (_) {}
48
+
49
+ const BANNER = [
50
+ ' ███████╗██████╗ ██╗██████╗ ███████╗██╗ ██╗██╗███████╗███████╗',
51
+ ' ██╔════╝██╔══██╗██║██╔══██╗██╔════╝██║ ██║██║██╔════╝██╔════╝',
52
+ ' ███████╗██████╔╝██║██████╔╝█████╗ ██║ █╗ ██║██║███████╗█████╗ ',
53
+ ' ╚════██║██╔═══╝ ██║██╔══██╗██╔══╝ ██║███╗██║██║╚════██║██╔══╝ ',
54
+ ' ███████║██║ ██║██║ ██║███████╗╚███╔███╔╝██║███████║███████╗',
55
+ ' ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ ╚══╝╚══╝ ╚═╝╚══════╝╚══════╝',
56
+ ];
57
+
58
+ function banner() {
59
+ console.log('');
60
+ for (const line of BANNER) console.log(paint(RAW.cyan, line));
61
+ const tag = ` Agent Skills · F6S & LinkedIn copywriting${PKG_VERSION ? ' · v' + PKG_VERSION : ''}`;
62
+ console.log(paint(RAW.magenta, tag));
63
+ console.log('');
64
+ }
65
+
66
+ let STEP_TOTAL = 4;
67
+ function step(n, msg) {
68
+ console.log(`${paint(RAW.cyan, ' ▸')} ${paint(RAW.bold, `Step ${n}/${STEP_TOTAL}`)} ${msg}`);
69
+ }
70
+ function substep(msg) {
71
+ console.log(` ${paint(RAW.green, '✓')} ${msg}`);
72
+ }
73
+
74
+ function box(lines) {
75
+ const vis = (s) => s.replace(/\x1b\[[0-9;]*m/g, '');
76
+ const width = Math.max(...lines.map((l) => vis(l).length));
77
+ const top = ' ┌' + '─'.repeat(width + 2) + '┐';
78
+ const bot = ' └' + '─'.repeat(width + 2) + '┘';
79
+ console.log(paint(RAW.green, top));
80
+ for (const l of lines) console.log(paint(RAW.green, ' │ ') + l + ' '.repeat(width - vis(l).length) + paint(RAW.green, ' │'));
81
+ console.log(paint(RAW.green, bot));
82
+ }
83
+
84
+ /**
85
+ * Agent registry. `format`:
86
+ * - 'skill': copy the skill folder verbatim (SKILL.md + any files)
87
+ * - 'rule' : write one rule file derived from SKILL.md (ext required)
88
+ * `project` is resolved from cwd; `global` uses ~ for the home directory.
89
+ */
90
+ const AGENTS = {
91
+ claude: {
92
+ label: 'Claude Code', format: 'skill',
93
+ project: '.claude/skills', global: '~/.claude/skills',
94
+ },
95
+ copilot: {
96
+ label: 'GitHub Copilot', format: 'skill',
97
+ project: '.github/skills', global: '~/.copilot/skills',
98
+ },
99
+ cursor: {
100
+ label: 'Cursor', format: 'rule', ext: '.mdc',
101
+ project: '.cursor/rules', global: '~/.cursor/rules',
102
+ },
103
+ windsurf: {
104
+ label: 'Windsurf', format: 'rule', ext: '.md',
105
+ project: '.windsurf/rules', global: '~/.codeium/windsurf/global_rules',
106
+ },
107
+ codex: {
108
+ label: 'Codex CLI', format: 'skill',
109
+ project: '.codex/skills', global: '~/.codex/skills',
110
+ },
111
+ gemini: {
112
+ label: 'Gemini CLI', format: 'skill',
113
+ project: '.gemini/skills', global: '~/.gemini/skills',
114
+ },
37
115
  };
38
116
 
39
- function os_home() {
40
- return process.env.HOME || process.env.USERPROFILE || require('os').homedir();
117
+ function expandHome(p) {
118
+ if (p === '~') return HOME;
119
+ if (p.startsWith('~/')) return path.join(HOME, p.slice(2));
120
+ return p;
121
+ }
122
+
123
+ function resolveTarget(agent, scope) {
124
+ const rel = scope === 'project' ? agent.project : agent.global;
125
+ return scope === 'project'
126
+ ? path.resolve(process.cwd(), rel)
127
+ : path.resolve(expandHome(rel));
41
128
  }
42
129
 
43
130
  function availableSkills() {
@@ -48,6 +135,19 @@ function availableSkills() {
48
135
  .sort();
49
136
  }
50
137
 
138
+ function readSkill(name) {
139
+ const raw = fs.readFileSync(path.join(SKILLS_DIR, name, 'SKILL.md'), 'utf8');
140
+ let description = name;
141
+ let body = raw;
142
+ const m = raw.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
143
+ if (m) {
144
+ body = m[2];
145
+ const dm = m[1].match(/description:\s*(?:>-)?\s*([\s\S]*?)(?:\n\w|$)/);
146
+ if (dm) description = dm[1].replace(/\s+/g, ' ').trim();
147
+ }
148
+ return { description, body: body.replace(/^\s+/, '') };
149
+ }
150
+
51
151
  function copyDir(src, dest) {
52
152
  fs.mkdirSync(dest, { recursive: true });
53
153
  for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
@@ -58,81 +158,100 @@ function copyDir(src, dest) {
58
158
  }
59
159
  }
60
160
 
61
- function installSkill(skill, targetDir) {
62
- const src = path.join(SKILLS_DIR, skill);
63
- if (!fs.existsSync(src)) die(`Skill '${skill}' not found in package.`);
64
- const dest = path.join(targetDir, skill);
65
- copyDir(src, dest);
66
- ok(`installed ${skill} ${c.dim}-> ${dest}${c.reset}`);
161
+ function installToAgent(agentKey, agent, scope, skills) {
162
+ const targetDir = resolveTarget(agent, scope);
163
+ fs.mkdirSync(targetDir, { recursive: true });
164
+ for (const skill of skills) {
165
+ if (agent.format === 'skill') {
166
+ const dest = path.join(targetDir, skill);
167
+ copyDir(path.join(SKILLS_DIR, skill), dest);
168
+ console.log(` ${paint(RAW.green, '✓')} ${paint(RAW.bold, agent.label)} ${c.dim}(${scope})${c.reset} ${skill} ${c.dim}→ ${dest}${c.reset}`);
169
+ } else {
170
+ const { description, body } = readSkill(skill);
171
+ const front = `---\ndescription: ${description}\nalwaysApply: false\n---\n\n`;
172
+ const file = path.join(targetDir, skill + (agent.ext || '.md'));
173
+ fs.writeFileSync(file, front + body);
174
+ console.log(` ${paint(RAW.green, '✓')} ${paint(RAW.bold, agent.label)} ${c.dim}(${scope})${c.reset} ${skill} ${c.dim}→ ${file}${c.reset}`);
175
+ }
176
+ }
67
177
  }
68
178
 
69
179
  function usage() {
70
- console.log(`spirewise — install copywriting Agent Skills
180
+ console.log(`spirewise — install copywriting Agent Skills into all your AI agents
71
181
 
72
182
  Usage:
73
- spirewise list
74
183
  spirewise install [skill ...] [options]
184
+ spirewise list
185
+ spirewise agents
75
186
 
76
- Commands:
77
- list List available skills
78
- install Install skills (default command)
187
+ Scope (where to install):
188
+ --workspace Install only into this folder / current project (alias: --project)
189
+ --global Install only into global/home folders
190
+ --both Install into both workspace and global
191
+ (no scope flag -> you are asked interactively)
79
192
 
80
- Options:
81
- --all Install every available skill
82
- --agent <name> Target agent: claude | copilot | cursor (default: claude)
83
- --target <dir> Install into a specific folder (overrides --agent)
193
+ Selection:
194
+ [skill ...] Limit to named skills (default: all)
195
+ --agent <a,b> Limit to named agents (default: all)
196
+
197
+ Other:
84
198
  -h, --help Show this help
85
199
 
86
- Available skills:
200
+ Supported agents:
201
+ ${Object.entries(AGENTS).map(([k, a]) => ` - ${k} (${a.label})`).join('\n')}
202
+
203
+ Skills:
87
204
  ${availableSkills().map((s) => ' - ' + s).join('\n') || ' (none found)'}
88
205
 
89
206
  Examples:
90
- npx spirewise install --all
91
- npx spirewise install f6s-copywriting
92
- npx spirewise install --agent cursor --all
207
+ npx spirewise install --both
208
+ npx spirewise install --project --agent claude,cursor
209
+ npx spirewise install f6s-copywriting --global
93
210
  `);
94
211
  }
95
212
 
96
213
  function parseArgs(argv) {
97
- const opts = { all: false, agent: 'claude', target: null, skills: [] };
214
+ const o = { scope: null, agents: null, skills: [] };
98
215
  for (let i = 0; i < argv.length; i++) {
99
216
  const a = argv[i];
100
- if (a === '--all') opts.all = true;
101
- else if (a === '--agent') opts.agent = argv[++i] || die('--agent needs a value');
102
- else if (a === '--target') opts.target = argv[++i] || die('--target needs a value');
217
+ if (a === '--project' || a === '--workspace' || a === '--local') o.scope = 'project';
218
+ else if (a === '--global') o.scope = 'global';
219
+ else if (a === '--both') o.scope = 'both';
220
+ else if (a === '--scope') o.scope = (argv[++i] || die('--scope needs a value'));
221
+ else if (a === '--agent' || a === '--agents') o.agents = (argv[++i] || die('--agent needs a value')).split(',').map((s) => s.trim()).filter(Boolean);
103
222
  else if (a === '-h' || a === '--help') { usage(); process.exit(0); }
104
223
  else if (a.startsWith('-')) die(`Unknown option: ${a}`);
105
- else opts.skills.push(a);
224
+ else o.skills.push(a);
106
225
  }
107
- return opts;
226
+ return o;
108
227
  }
109
228
 
110
- function pick(available) {
229
+ function ask(question) {
111
230
  return new Promise((resolve) => {
112
- if (!process.stdin.isTTY) {
113
- die("No skills specified. Use --all or name skills (run in a terminal for the picker).");
114
- }
115
- info('Select skills to install (space-separated numbers, or "a" for all):');
116
- available.forEach((s, i) => console.log(` ${i + 1}) ${s}`));
117
231
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
118
- rl.question(' > ', (answer) => {
119
- rl.close();
120
- const t = answer.trim().toLowerCase();
121
- if (t === 'a') return resolve(available.slice());
122
- const chosen = [];
123
- for (const tok of t.split(/\s+/)) {
124
- const idx = parseInt(tok, 10) - 1;
125
- if (idx >= 0 && idx < available.length) chosen.push(available[idx]);
126
- }
127
- resolve(chosen);
128
- });
232
+ rl.question(question, (ans) => { rl.close(); resolve(ans.trim()); });
129
233
  });
130
234
  }
131
235
 
236
+ async function promptScope() {
237
+ if (!process.stdin.isTTY) {
238
+ warn('No terminal detected; defaulting scope to "project". Use --global or --both to change.');
239
+ return 'project';
240
+ }
241
+ info('Where should the skills be installed?');
242
+ console.log(' 1) Workspace (this folder only — the current project directory)');
243
+ console.log(' 2) Global (your home folders — applies to all projects)');
244
+ console.log(' 3) Both');
245
+ const ans = (await ask(' Choose 1/2/3 [1]: ')).toLowerCase();
246
+ if (ans === '2' || ans === 'global') return 'global';
247
+ if (ans === '3' || ans === 'both') return 'both';
248
+ return 'project';
249
+ }
250
+
132
251
  async function main() {
133
252
  let argv = process.argv.slice(2);
134
253
  let command = 'install';
135
- if (argv[0] === 'list' || argv[0] === 'install') { command = argv[0]; argv = argv.slice(1); }
254
+ if (['list', 'install', 'agents'].includes(argv[0])) { command = argv[0]; argv = argv.slice(1); }
136
255
  else if (argv[0] === '-h' || argv[0] === '--help') { usage(); return; }
137
256
 
138
257
  const available = availableSkills();
@@ -143,27 +262,52 @@ async function main() {
143
262
  available.forEach((s) => console.log(' - ' + s));
144
263
  return;
145
264
  }
265
+ if (command === 'agents') {
266
+ info('Supported agents and their folders:');
267
+ for (const [k, a] of Object.entries(AGENTS)) {
268
+ console.log(` ${k} (${a.label}) [${a.format}]`);
269
+ console.log(` project: ${a.project}`);
270
+ console.log(` global: ${a.global}`);
271
+ }
272
+ return;
273
+ }
274
+
275
+ const o = parseArgs(argv);
146
276
 
147
- const opts = parseArgs(argv);
277
+ banner();
148
278
 
149
- if (opts.agent && !AGENT_DIRS[opts.agent] && !opts.target) {
150
- die(`Unknown agent '${opts.agent}' (use claude | copilot | cursor)`);
279
+ step(1, 'Scanning available skills');
280
+ let skills = o.skills.length ? o.skills : available.slice();
281
+ for (const s of skills) if (!available.includes(s)) die(`Unknown skill '${s}'. Run "spirewise list".`);
282
+ substep(`${skills.length} skill(s): ${skills.join(', ')}`);
283
+
284
+ step(2, 'Selecting target agents');
285
+ let agentKeys = Object.keys(AGENTS);
286
+ if (o.agents) {
287
+ for (const k of o.agents) if (!AGENTS[k]) die(`Unknown agent '${k}'. Run "spirewise agents".`);
288
+ agentKeys = o.agents;
151
289
  }
152
- const targetDir = opts.target ? path.resolve(opts.target) : AGENT_DIRS[opts.agent];
290
+ substep(`${agentKeys.length} agent(s): ${agentKeys.join(', ')}`);
153
291
 
154
- let selected = opts.all ? available.slice() : opts.skills.slice();
155
- if (selected.length === 0) selected = await pick(available);
156
- if (selected.length === 0) die('Nothing selected.');
292
+ step(3, 'Choosing install scope');
293
+ let scope = o.scope || await promptScope();
294
+ if (!['project', 'global', 'both'].includes(scope)) die(`Invalid scope '${scope}'.`);
295
+ const scopes = scope === 'both' ? ['project', 'global'] : [scope];
296
+ substep(`scope: ${paint(RAW.bold, scope === 'project' ? 'workspace' : scope)}`);
157
297
 
158
- for (const s of selected) {
159
- if (!available.includes(s)) die(`Unknown skill '${s}'. Run "spirewise list" to see options.`);
298
+ step(4, 'Installing');
299
+ let count = 0;
300
+ for (const sc of scopes) {
301
+ for (const k of agentKeys) { installToAgent(k, AGENTS[k], sc, skills); count += skills.length; }
160
302
  }
161
303
 
162
- info(`Target: ${targetDir}`);
163
- fs.mkdirSync(targetDir, { recursive: true });
164
- for (const s of selected) installSkill(s, targetDir);
165
- ok(`Done. Installed ${selected.length} skill(s).`);
166
- info('Restart your agent or reload skills if needed.');
304
+ console.log('');
305
+ box([
306
+ `Installed ${paint(RAW.bold, String(skills.length))} skill(s) into ${paint(RAW.bold, String(agentKeys.length))} agent(s)`,
307
+ `scope: ${scopes.map((s) => (s === 'project' ? 'workspace' : s)).join(' + ')} · ${count} item(s) written`,
308
+ `next: open your agent and say "write our F6S profile copy"`,
309
+ ]);
310
+ console.log('');
167
311
  }
168
312
 
169
313
  main().catch((e) => die(e && e.message ? e.message : String(e)));
package/install.sh ADDED
@@ -0,0 +1,253 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # spirewise — no-Node installer for copywriting Agent Skills.
4
+ # Installs into ALL supported agents, in their correct folders, and asks
5
+ # whether to install for THIS project (cwd) or GLOBALLY (home dirs).
6
+ #
7
+ # Local: ./install.sh [--project|--global|--both] [--agent a,b] [skill ...]
8
+ # Remote: curl -fsSL <raw>/install.sh | bash -s -- --both
9
+ #
10
+ # Prefer the npm CLI when Node is available: npx spirewise install
11
+ #
12
+ set -euo pipefail
13
+
14
+ # ---- Repo config (used only for remote install) ---------------------------
15
+ REPO_OWNER="${SKILLS_REPO_OWNER:-spirerise}"
16
+ REPO_NAME="${SKILLS_REPO_NAME:-spirewise}"
17
+ REPO_BRANCH="${SKILLS_REPO_BRANCH:-main}"
18
+ SKILLS_SUBDIR="skills"
19
+ # ---------------------------------------------------------------------------
20
+
21
+ RAW_BASE="https://raw.githubusercontent.com/${REPO_OWNER}/${REPO_NAME}/${REPO_BRANCH}"
22
+ API_BASE="https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/contents/${SKILLS_SUBDIR}?ref=${REPO_BRANCH}"
23
+
24
+ # Agent registry: "key|Label|format|ext|projectDir|globalDir"
25
+ # format = skill (copy SKILL.md folder) | rule (single file from SKILL.md)
26
+ AGENTS=(
27
+ "claude|Claude Code|skill||.claude/skills|HOME/.claude/skills"
28
+ "copilot|GitHub Copilot|skill||.github/skills|HOME/.copilot/skills"
29
+ "cursor|Cursor|rule|.mdc|.cursor/rules|HOME/.cursor/rules"
30
+ "windsurf|Windsurf|rule|.md|.windsurf/rules|HOME/.codeium/windsurf/global_rules"
31
+ "codex|Codex CLI|skill||.codex/skills|HOME/.codex/skills"
32
+ "gemini|Gemini CLI|skill||.gemini/skills|HOME/.gemini/skills"
33
+ )
34
+
35
+ color() { printf '\033[%sm%s\033[0m' "$1" "$2"; }
36
+ info() { printf '%s %s\n' "$(color '1;34' '==>')" "$1"; }
37
+ ok() { printf '%s %s\n' "$(color '1;32' ' ok')" "$1"; }
38
+ warn() { printf '%s %s\n' "$(color '1;33' ' !')" "$1" >&2; }
39
+ die() { printf '%s %s\n' "$(color '1;31' 'err')" "$1" >&2; exit 1; }
40
+ step() { printf '%s %s %s\n' "$(color '1;36' ' >')" "$(color '1' "Step $1/4")" "$2"; }
41
+ substep() { printf ' %s %s\n' "$(color '1;32' 'v')" "$1"; }
42
+
43
+ banner() {
44
+ printf '\n'
45
+ color '1;36' ' ███████╗██████╗ ██╗██████╗ ███████╗██╗ ██╗██╗███████╗███████╗'; printf '\n'
46
+ color '1;36' ' ██╔════╝██╔══██╗██║██╔══██╗██╔════╝██║ ██║██║██╔════╝██╔════╝'; printf '\n'
47
+ color '1;36' ' ███████╗██████╔╝██║██████╔╝█████╗ ██║ █╗ ██║██║███████╗█████╗ '; printf '\n'
48
+ color '1;36' ' ╚════██║██╔═══╝ ██║██╔══██╗██╔══╝ ██║███╗██║██║╚════██║██╔══╝ '; printf '\n'
49
+ color '1;36' ' ███████║██║ ██║██║ ██║███████╗╚███╔███╔╝██║███████║███████╗'; printf '\n'
50
+ color '1;36' ' ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ ╚══╝╚══╝ ╚═╝╚══════╝╚══════╝'; printf '\n'
51
+ color '1;35' ' Agent Skills · F6S & LinkedIn copywriting'; printf '\n\n'
52
+ }
53
+
54
+ usage() {
55
+ cat <<EOF
56
+ spirewise — install copywriting Agent Skills into all your AI agents.
57
+
58
+ Usage:
59
+ install.sh [scope] [--agent a,b] [skill ...]
60
+
61
+ Scope:
62
+ --workspace install only into this folder / current project (alias: --project)
63
+ --global install only into global/home folders
64
+ --both install into both
65
+ (no scope -> you are asked interactively)
66
+
67
+ Selection:
68
+ [skill ...] limit to named skills (default: all)
69
+ --agent <a,b> limit to named agents (default: all)
70
+ --list list available skills
71
+ -h, --help show this help
72
+
73
+ Agents: claude copilot cursor windsurf codex gemini
74
+ EOF
75
+ }
76
+
77
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" 2>/dev/null && pwd || true)"
78
+ LOCAL_SKILLS_DIR=""
79
+ [[ -n "$SCRIPT_DIR" && -d "$SCRIPT_DIR/$SKILLS_SUBDIR" ]] && LOCAL_SKILLS_DIR="$SCRIPT_DIR/$SKILLS_SUBDIR"
80
+
81
+ WORK=""
82
+ cleanup() { [[ -n "$WORK" && -d "$WORK" ]] && rm -rf "$WORK"; return 0; }
83
+ trap cleanup EXIT
84
+
85
+ # Ensure a local copy of every requested skill folder; echo its base dir.
86
+ ensure_skills_dir() {
87
+ if [[ -n "$LOCAL_SKILLS_DIR" ]]; then
88
+ printf '%s' "$LOCAL_SKILLS_DIR"; return
89
+ fi
90
+ command -v curl >/dev/null || die "curl is required for remote install."
91
+ WORK="$(mktemp -d)"
92
+ printf '%s' "$WORK"
93
+ }
94
+
95
+ list_remote_skills() {
96
+ curl -fsSL "$API_BASE" \
97
+ | grep '"name"' | sed -E 's/.*"name": *"([^"]+)".*/\1/' \
98
+ | while read -r n; do
99
+ curl -fsI "${RAW_BASE}/${SKILLS_SUBDIR}/${n}/SKILL.md" >/dev/null 2>&1 && echo "$n"
100
+ done
101
+ }
102
+
103
+ available_skills() {
104
+ if [[ -n "$LOCAL_SKILLS_DIR" ]]; then
105
+ find "$LOCAL_SKILLS_DIR" -maxdepth 2 -name SKILL.md -print0 \
106
+ | xargs -0 -n1 dirname | xargs -n1 basename | sort
107
+ else
108
+ list_remote_skills | sort
109
+ fi
110
+ }
111
+
112
+ # Fetch a single SKILL.md into $BASE/<skill>/SKILL.md when remote.
113
+ fetch_skill() {
114
+ local skill="$1" base="$2"
115
+ [[ -n "$LOCAL_SKILLS_DIR" ]] && return 0
116
+ mkdir -p "$base/$skill"
117
+ curl -fsSL "${RAW_BASE}/${SKILLS_SUBDIR}/${skill}/SKILL.md" -o "$base/$skill/SKILL.md" \
118
+ || die "Failed to download $skill/SKILL.md"
119
+ }
120
+
121
+ # Extract the description from a SKILL.md frontmatter (handles folded ">-"/"|").
122
+ skill_description() {
123
+ awk '
124
+ /^---[[:space:]]*$/ { c++; next }
125
+ c==1 {
126
+ if ($0 ~ /^description:/) {
127
+ line=$0; sub(/^description:[[:space:]]*/, "", line)
128
+ if (line ~ /^[>|]-?[+]?[[:space:]]*$/) { folded=1; next }
129
+ gsub(/^[[:space:]]+|[[:space:]]+$/, "", line); print line; done=1; exit
130
+ }
131
+ if (folded) {
132
+ if ($0 ~ /^[[:space:]]/) {
133
+ s=$0; gsub(/^[[:space:]]+|[[:space:]]+$/, "", s)
134
+ out=(out==""?s:out" "s); next
135
+ }
136
+ print out; done=1; exit
137
+ }
138
+ }
139
+ END { if (!done && out!="") print out }
140
+ ' "$1"
141
+ }
142
+
143
+ # Print SKILL.md body (everything after the closing frontmatter ---).
144
+ skill_body() {
145
+ awk 'BEGIN{c=0} /^---[[:space:]]*$/{c++; next} c>=2{print}' "$1"
146
+ }
147
+
148
+ # Resolve a registry dir token (HOME/... -> $HOME/...).
149
+ resolve_dir() {
150
+ local d="$1"
151
+ if [[ "$d" == HOME/* ]]; then printf '%s/%s' "$HOME" "${d#HOME/}"; else printf '%s' "$d"; fi
152
+ }
153
+
154
+ install_for_agent() {
155
+ local key="$1" label="$2" fmt="$3" ext="$4" dir="$5" scope="$6" base="$7"; shift 7
156
+ local skills=("$@")
157
+ mkdir -p "$dir"
158
+ local s
159
+ for s in "${skills[@]}"; do
160
+ fetch_skill "$s" "$base"
161
+ if [[ "$fmt" == "skill" ]]; then
162
+ mkdir -p "$dir/$s"
163
+ cp -R "$base/$s/." "$dir/$s/"
164
+ substep "$label ($scope) $s -> $dir/$s"
165
+ else
166
+ local desc body file
167
+ desc="$(skill_description "$base/$s/SKILL.md")"
168
+ file="$dir/$s$ext"
169
+ { printf -- '---\ndescription: %s\nalwaysApply: false\n---\n\n' "$desc"; skill_body "$base/$s/SKILL.md"; } >"$file"
170
+ substep "$label ($scope) $s -> $file"
171
+ fi
172
+ done
173
+ }
174
+
175
+ # ---- Parse args ------------------------------------------------------------
176
+ SCOPE=""
177
+ AGENT_FILTER=""
178
+ DO_LIST=false
179
+ SELECTED=()
180
+ while [[ $# -gt 0 ]]; do
181
+ case "$1" in
182
+ --project|--workspace|--local) SCOPE="project"; shift ;;
183
+ --global) SCOPE="global"; shift ;;
184
+ --both) SCOPE="both"; shift ;;
185
+ --agent|--agents) AGENT_FILTER="${2:?--agent needs a value}"; shift 2 ;;
186
+ --list) DO_LIST=true; shift ;;
187
+ -h|--help) usage; exit 0 ;;
188
+ -*) die "Unknown option: $1" ;;
189
+ *) SELECTED+=("$1"); shift ;;
190
+ esac
191
+ done
192
+
193
+ BASE="$(ensure_skills_dir)"
194
+
195
+ AVAILABLE=()
196
+ while IFS= read -r line; do [[ -n "$line" ]] && AVAILABLE+=("$line"); done < <(available_skills)
197
+ [[ ${#AVAILABLE[@]} -gt 0 ]] || die "No skills found."
198
+
199
+ if $DO_LIST; then
200
+ info "Available skills:"; printf ' - %s\n' "${AVAILABLE[@]}"; exit 0
201
+ fi
202
+
203
+ banner
204
+
205
+ # Step 1: skills (default all).
206
+ step 1 "Scanning available skills"
207
+ [[ ${#SELECTED[@]} -eq 0 ]] && SELECTED=("${AVAILABLE[@]}")
208
+ for s in "${SELECTED[@]}"; do
209
+ found=false; for a in "${AVAILABLE[@]}"; do [[ "$s" == "$a" ]] && found=true && break; done
210
+ $found || die "Unknown skill '$s'. Use --list."
211
+ done
212
+ substep "${#SELECTED[@]} skill(s): ${SELECTED[*]}"
213
+
214
+ # Step 2: agents
215
+ step 2 "Selecting target agents"
216
+ if [[ -n "$AGENT_FILTER" ]]; then substep "agents: $AGENT_FILTER"; else substep "agents: all supported"; fi
217
+
218
+ # Step 3: scope — prompt if not given.
219
+ step 3 "Choosing install scope"
220
+ if [[ -z "$SCOPE" ]]; then
221
+ if [[ -t 0 ]]; then
222
+ info "Where should the skills be installed?"
223
+ echo " 1) Workspace (this folder only — current project)"
224
+ echo " 2) Global (your home folders — all projects)"
225
+ echo " 3) Both"
226
+ printf ' Choose 1/2/3 [1]: '
227
+ read -r ans
228
+ case "$ans" in 2|global) SCOPE="global";; 3|both) SCOPE="both";; *) SCOPE="project";; esac
229
+ else
230
+ warn 'No terminal; defaulting scope to "project". Use --global or --both.'
231
+ SCOPE="project"
232
+ fi
233
+ fi
234
+ [[ "$SCOPE" == "project" ]] && substep "scope: workspace" || substep "scope: $SCOPE"
235
+
236
+ SCOPES=("$SCOPE"); [[ "$SCOPE" == "both" ]] && SCOPES=("project" "global")
237
+
238
+ # Step 4: install
239
+ step 4 "Installing"
240
+ for sc in "${SCOPES[@]}"; do
241
+ for entry in "${AGENTS[@]}"; do
242
+ IFS='|' read -r key label fmt ext pdir gdir <<<"$entry"
243
+ if [[ -n "$AGENT_FILTER" ]]; then
244
+ case ",$AGENT_FILTER," in *",$key,"*) ;; *) continue ;; esac
245
+ fi
246
+ if [[ "$sc" == "project" ]]; then dir="$(resolve_dir "$pdir")"; else dir="$(resolve_dir "$gdir")"; fi
247
+ install_for_agent "$key" "$label" "$fmt" "$ext" "$dir" "$sc" "$BASE" "${SELECTED[@]}"
248
+ done
249
+ done
250
+
251
+ printf '\n'
252
+ ok "Installed ${#SELECTED[@]} skill(s). Next: open your agent and say \"write our F6S profile copy\"."
253
+
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "spirewise",
3
- "version": "1.0.0",
3
+ "version": "1.0.3",
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"
7
7
  },
8
8
  "files": [
9
9
  "bin/",
10
- "skills/"
10
+ "skills/",
11
+ "install.sh"
11
12
  ],
12
13
  "scripts": {
13
14
  "test": "node bin/cli.js list"
@@ -29,6 +30,6 @@
29
30
  },
30
31
  "repository": {
31
32
  "type": "git",
32
- "url": "https://github.com/spirerise/spirewise.git"
33
+ "url": "git+https://github.com/spirerise/spirewise.git"
33
34
  }
34
35
  }
package/skills/README.md CHANGED
@@ -31,34 +31,46 @@ 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 (skills are bundled in the package):
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**:
35
36
 
36
37
  ```bash
37
- npx spirewise install --all # install every skill
38
- npx spirewise install f6s-copywriting # install just one
39
- npx spirewise install # interactive picker
40
- npx spirewise list # list available skills
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)
41
42
  ```
42
43
 
43
- Target a specific agent or folder:
44
+ Narrow it down:
44
45
 
45
46
  ```bash
46
- npx spirewise install --all --agent claude # ~/.claude/skills (default)
47
- npx spirewise install --all --agent copilot # ~/.copilot/skills
48
- npx spirewise install --all --agent cursor # ~/.cursor/skills
49
- npx spirewise install --all --target ./skills
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
50
51
  ```
51
52
 
53
+ Supported agents and their folders (project / global):
54
+
55
+ | Agent | Project | Global | Format |
56
+ |-------|---------|--------|--------|
57
+ | Claude Code | `.claude/skills/` | `~/.claude/skills/` | SKILL.md |
58
+ | GitHub Copilot | `.github/skills/` | `~/.copilot/skills/` | SKILL.md |
59
+ | Cursor | `.cursor/rules/` | `~/.cursor/rules/` | `.mdc` |
60
+ | Windsurf | `.windsurf/rules/` | `~/.codeium/windsurf/global_rules/` | `.md` |
61
+ | Codex CLI | `.codex/skills/` | `~/.codex/skills/` | SKILL.md |
62
+ | Gemini CLI | `.gemini/skills/` | `~/.gemini/skills/` | SKILL.md |
63
+
52
64
  ### No-Node fallback: `install.sh`
53
65
 
54
- If you can't use npm, the bundled `install.sh` does the same over `curl`:
66
+ If you can't use npm, the bundled `install.sh` mirrors the CLI over `curl`:
55
67
 
56
68
  ```bash
57
69
  # from a clone
58
- ./install.sh --all
70
+ ./install.sh --both
59
71
 
60
72
  # remote
61
- curl -fsSL https://raw.githubusercontent.com/spirerise/spirewise/main/install.sh | bash -s -- --all
73
+ curl -fsSL https://raw.githubusercontent.com/spirerise/spirewise/main/install.sh | bash -s -- --both
62
74
  ```
63
75
 
64
76
  Run `./install.sh --help` or `npx spirewise --help` for all options.
@@ -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.