@veyralabs/skills 0.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.
@@ -0,0 +1,155 @@
1
+ # Guide Structure — NamingGuide Reference
2
+
3
+ Naming system patterns, tier structures, and how to match them to company type.
4
+
5
+ ---
6
+
7
+ ## Naming System Patterns
8
+
9
+ ### Pattern 1: Standalone Brand (Apple model)
10
+
11
+ Each product is a standalone brand. Features are descriptive. No prefix.
12
+
13
+ **Structure:**
14
+ - Company: Apple
15
+ - Products: iPhone, iPad, Mac, Apple Watch (note: Apple prefix added late, after brand confusion)
16
+ - Features: FaceTime, Siri, AirDrop — branded features get their own names
17
+ - Releases: codenames internally, version numbers publicly
18
+
19
+ **When to use:**
20
+ - Company with multiple distinct product lines
21
+ - Each product serves a different audience or category
22
+ - Company brand is strong enough to confer trust without prefix
23
+
24
+ **Naming rule:** Products need standalone strength. Features deserve names only when users will talk about them by name.
25
+
26
+ ---
27
+
28
+ ### Pattern 2: Brand-Prefix Constellation (Stripe model)
29
+
30
+ Products expand outward from core brand with compound names.
31
+
32
+ **Structure:**
33
+ - Core: Stripe
34
+ - Products: Stripe Atlas, Stripe Radar, Stripe Issuing, Stripe Treasury
35
+ - Features: unnamed or described within the product
36
+
37
+ **When to use:**
38
+ - Company with one core product and expanding platform features
39
+ - B2B / enterprise context where brand authority matters in sales
40
+ - Products that are genuinely distinct but serve the same audience
41
+
42
+ **Naming rule:** Second word should be a strong standalone word (Atlas, Radar) not a descriptor (StripeAnalytics = bad).
43
+
44
+ **Pitfall:** Prefix exhaustion. If every product is [Brand] + [word], the brand becomes noise. Limit to 5-7 products before reconsidering.
45
+
46
+ ---
47
+
48
+ ### Pattern 3: Ecosystem Umbrella (Notion model)
49
+
50
+ Everything lives under one brand. Features are described, not named. Consistency over personality.
51
+
52
+ **Structure:**
53
+ - Core: Notion
54
+ - Features: Notion AI, Notion Calendar, Notion Forms — functional compounds
55
+ - No sub-brands or standalone product names
56
+
57
+ **When to use:**
58
+ - Single-product company with expanding features
59
+ - Audience expects consistency and predictability
60
+ - Brand equity is strong enough that sub-branding would dilute it
61
+
62
+ **Naming rule:** Functional clarity over brand personality at the feature level. Reserve branded names for major platform pivots.
63
+
64
+ ---
65
+
66
+ ### Pattern 4: Codename Releases (macOS model)
67
+
68
+ Releases get creative codenames. Products stay clean.
69
+
70
+ **Structure:**
71
+ - Product: macOS
72
+ - Releases: Ventura, Monterey, Sonoma — geographic/thematic codenames
73
+ - Features: Stage Manager, Focus Mode — descriptive
74
+
75
+ **When to use:**
76
+ - Regular release cadence that needs narrative (annual releases, major versions)
77
+ - Team culture values the ritual of naming releases
78
+ - Audience will remember releases by name (enthusiast community)
79
+
80
+ **Naming rule:** Codenames should come from a consistent theme (Apple: California locations). Pick the theme and commit to it for 5+ releases minimum.
81
+
82
+ ---
83
+
84
+ ### Pattern 5: Open Tier (GitHub / Linear model)
85
+
86
+ Minimal naming system. Brand + product descriptions. Let the work speak.
87
+
88
+ **Structure:**
89
+ - GitHub: Issues, Pull Requests, Actions, Copilot (the one exception — AI gets a name)
90
+ - Linear: Issues, Projects, Cycles, Roadmaps — all descriptive
91
+
92
+ **When to use:**
93
+ - Developer tools where users value precision over brand personality
94
+ - Products where descriptive names aid discoverability and onboarding
95
+ - Teams that want to ship fast without naming debates
96
+
97
+ **Naming rule:** Only name something when it's genuinely new — when no existing word captures it. Default to the best descriptive name.
98
+
99
+ ---
100
+
101
+ ## Tier Definition Reference
102
+
103
+ ### Product Tier
104
+ The highest-level named thing. Carries full brand weight.
105
+ - Naming intensity: HIGH — invest time here
106
+ - Style: invented or modified real word preferred
107
+ - Length: 4-8 characters ideal
108
+ - Domain: should own the domain
109
+
110
+ ### Feature Tier
111
+ A capability within a product. Named only when users will refer to it by name.
112
+ **The question to ask:** "Will a user say 'I used [feature name] to do X' or will they say 'I used the [description] in [product]'?"
113
+ - Named features: Siri, AirDrop, FaceTime, Stripe Radar
114
+ - Unnamed features: file export, user permissions, notification settings
115
+ - Naming intensity: SELECTIVE — only when the feature has brand moment potential
116
+ - Style: can be more descriptive than product names; concept/metaphor works well
117
+
118
+ ### Release Tier
119
+ Time-based versions. Named only if release cadence is regular and team culture supports it.
120
+ - Naming intensity: LOW-MEDIUM — fun but not mission-critical
121
+ - Style: pick a theme and commit (geography, mythology, animals, etc.)
122
+ - Number: always also use a version number; codenames are supplementary
123
+
124
+ ### Sub-brand Tier
125
+ A distinct product that serves a meaningfully different audience or market.
126
+ - Naming intensity: HIGH — treat like a new brand
127
+ - Must pass the standalone test: would this name work without the parent brand?
128
+ - Visual identity: can deviate from parent, but should be related
129
+
130
+ ---
131
+
132
+ ## Audience-Specific Naming Conventions
133
+
134
+ **Developer audience:**
135
+ - Short, lowercase-friendly names work (bun, nx, vite)
136
+ - Technical precision valued over warmth
137
+ - GitHub org name as important as domain
138
+ - Feature names can be more technical
139
+
140
+ **Consumer audience:**
141
+ - Warmth and approachability required
142
+ - Avoid jargon, acronyms, or overly technical constructions
143
+ - Names should work as verbs ("I'll [name] it")
144
+ - Feature names should feel intuitive, not technical
145
+
146
+ **Enterprise audience:**
147
+ - Clarity and credibility over cleverness
148
+ - Avoid names that sound like consumer apps
149
+ - Sub-brands and product families need clear hierarchy
150
+ - Acronyms acceptable at enterprise scale (but risky pre-scale)
151
+
152
+ **SMB / prosumer audience:**
153
+ - Balance of approachability and professionalism
154
+ - Avoid names that are too consumer (feels unserious) or too enterprise (feels intimidating)
155
+ - Feature names should be self-explanatory on first encounter
package/validate.js ADDED
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env node
2
+
3
+ 'use strict';
4
+
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+
8
+ const REQUIRED_FIELDS = ['name', 'description'];
9
+ const NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
10
+ const SKILLS_DIR = path.join(__dirname, 'skills');
11
+
12
+ function findSkillFiles(dir) {
13
+ const results = [];
14
+
15
+ if (!fs.existsSync(dir)) return results;
16
+
17
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
18
+ if (!entry.isDirectory()) continue;
19
+ const candidate = path.join(dir, entry.name, 'SKILL.md');
20
+ if (fs.existsSync(candidate)) results.push(candidate);
21
+ }
22
+
23
+ return results;
24
+ }
25
+
26
+ function parseFrontmatter(content) {
27
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
28
+ if (!match) return { error: 'No YAML frontmatter found. File must start with a --- block.' };
29
+
30
+ const fields = {};
31
+ const lines = match[1].split('\n');
32
+ let i = 0;
33
+
34
+ while (i < lines.length) {
35
+ const line = lines[i];
36
+ const trimmed = line.trim();
37
+
38
+ if (!trimmed || trimmed.startsWith('#')) { i++; continue; }
39
+
40
+ const colon = trimmed.indexOf(':');
41
+ if (colon === -1) { i++; continue; }
42
+
43
+ const key = trimmed.slice(0, colon).trim();
44
+ const rawValue = trimmed.slice(colon + 1).trim();
45
+
46
+ if (!key || key.includes(' ')) { i++; continue; }
47
+
48
+ // Handle block scalars (> and |)
49
+ if (rawValue === '>' || rawValue === '|') {
50
+ const blockLines = [];
51
+ i++;
52
+ while (i < lines.length && (lines[i].startsWith(' ') || lines[i].trim() === '')) {
53
+ blockLines.push(lines[i].trim());
54
+ i++;
55
+ }
56
+ fields[key] = blockLines.join(' ').trim();
57
+ } else {
58
+ fields[key] = rawValue.replace(/^['"]|['"]$/g, '');
59
+ i++;
60
+ }
61
+ }
62
+
63
+ return { fields };
64
+ }
65
+
66
+ function validate(filePath) {
67
+ const content = fs.readFileSync(filePath, 'utf8');
68
+ const relative = path.relative(__dirname, filePath);
69
+ const errors = [];
70
+ const warnings = [];
71
+
72
+ const { error, fields } = parseFrontmatter(content);
73
+
74
+ if (error) {
75
+ return { file: relative, errors: [error], warnings, valid: false };
76
+ }
77
+
78
+ for (const field of REQUIRED_FIELDS) {
79
+ if (!fields[field] || !fields[field].trim()) {
80
+ errors.push(`Missing required field: "${field}"`);
81
+ }
82
+ }
83
+
84
+ if (fields.name && !NAME_PATTERN.test(fields.name)) {
85
+ errors.push(`"name" must be lowercase alphanumeric with hyphens only (got: "${fields.name}")`);
86
+ }
87
+
88
+ if (fields.description && fields.description.length < 20) {
89
+ warnings.push(`"description" is very short (${fields.description.length} chars) — be more specific`);
90
+ }
91
+
92
+ const wordCount = content.split(/\s+/).filter(Boolean).length;
93
+ if (wordCount < 100) {
94
+ warnings.push(`SKILL.md has only ${wordCount} words — skills typically need more instructions`);
95
+ }
96
+
97
+ return { file: relative, errors, warnings, valid: errors.length === 0 };
98
+ }
99
+
100
+ function main() {
101
+ const files = findSkillFiles(SKILLS_DIR);
102
+
103
+ if (files.length === 0) {
104
+ console.log('No SKILL.md files found in skills/');
105
+ process.exit(0);
106
+ }
107
+
108
+ console.log(`Validating ${files.length} skill(s)...\n`);
109
+
110
+ let allValid = true;
111
+
112
+ for (const filePath of files) {
113
+ const result = validate(filePath);
114
+ const icon = result.valid ? '✓' : '✗';
115
+
116
+ console.log(`${icon} ${result.file}`);
117
+
118
+ for (const err of result.errors) {
119
+ console.log(` ERROR ${err}`);
120
+ allValid = false;
121
+ }
122
+
123
+ for (const warn of result.warnings) {
124
+ console.log(` WARN ${warn}`);
125
+ }
126
+
127
+ if (result.errors.length === 0 && result.warnings.length === 0) {
128
+ console.log(' All checks passed');
129
+ }
130
+
131
+ console.log('');
132
+ }
133
+
134
+ if (!allValid) {
135
+ console.error('Validation failed. Fix the errors above before merging.');
136
+ process.exit(1);
137
+ }
138
+
139
+ console.log('All skills valid.');
140
+ }
141
+
142
+ main();