kspec 1.0.19 → 1.0.20
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/package.json +1 -1
- package/src/index.js +62 -16
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -167,25 +167,71 @@ function formatDate(format) {
|
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
function slugify(text) {
|
|
170
|
-
//
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
170
|
+
// Filler words to remove (action verbs, articles, prepositions, generic terms)
|
|
171
|
+
const fillerWords = new Set([
|
|
172
|
+
'a', 'an', 'the', 'and', 'or', 'but', 'for', 'with', 'using', 'via',
|
|
173
|
+
'create', 'build', 'make', 'implement', 'add', 'develop', 'write',
|
|
174
|
+
'application', 'app', 'system', 'feature', 'functionality', 'module',
|
|
175
|
+
'that', 'this', 'which', 'will', 'should', 'can', 'could', 'would',
|
|
176
|
+
'new', 'simple', 'basic', 'full', 'complete'
|
|
177
|
+
]);
|
|
178
|
+
|
|
179
|
+
// Tech terms to prioritize (frameworks, libraries, tools)
|
|
180
|
+
const techTerms = new Set([
|
|
181
|
+
'react', 'nextjs', 'next', 'vue', 'angular', 'svelte', 'node', 'express',
|
|
182
|
+
'typescript', 'javascript', 'python', 'rust', 'go', 'java',
|
|
183
|
+
'tailwind', 'shadcn', 'prisma', 'drizzle', 'supabase', 'firebase',
|
|
184
|
+
'postgres', 'mysql', 'mongodb', 'redis', 'graphql', 'rest', 'trpc',
|
|
185
|
+
'docker', 'kubernetes', 'aws', 'vercel', 'netlify'
|
|
186
|
+
]);
|
|
187
|
+
|
|
188
|
+
// Normalize text: handle "to do" -> "todo", lowercase, clean
|
|
189
|
+
let normalized = text.toLowerCase()
|
|
190
|
+
.replace(/to\s*do/g, 'todo')
|
|
191
|
+
.replace(/e[\s-]?commerce/g, 'ecommerce')
|
|
192
|
+
.replace(/[^a-z0-9\s]/g, ' ')
|
|
193
|
+
.replace(/\s+/g, ' ')
|
|
194
|
+
.trim();
|
|
195
|
+
|
|
196
|
+
const words = normalized.split(' ').filter(w => w.length > 0);
|
|
197
|
+
|
|
198
|
+
// Separate into tech terms and other meaningful words
|
|
199
|
+
const tech = [];
|
|
200
|
+
const meaningful = [];
|
|
201
|
+
|
|
202
|
+
for (const word of words) {
|
|
203
|
+
if (fillerWords.has(word)) continue;
|
|
204
|
+
if (techTerms.has(word)) {
|
|
205
|
+
tech.push(word);
|
|
206
|
+
} else if (word.length > 2) {
|
|
207
|
+
meaningful.push(word);
|
|
208
|
+
}
|
|
181
209
|
}
|
|
182
|
-
|
|
183
|
-
|
|
210
|
+
|
|
211
|
+
// Prioritize: first meaningful word + tech terms (max 3-4 words total)
|
|
212
|
+
const selected = [];
|
|
213
|
+
if (meaningful.length > 0) selected.push(meaningful[0]);
|
|
214
|
+
selected.push(...tech.slice(0, 2));
|
|
215
|
+
if (selected.length < 3 && meaningful.length > 1) {
|
|
216
|
+
selected.push(meaningful[1]);
|
|
184
217
|
}
|
|
185
|
-
|
|
186
|
-
|
|
218
|
+
|
|
219
|
+
// Fallback if nothing meaningful
|
|
220
|
+
if (selected.length === 0) {
|
|
221
|
+
const fallback = words.filter(w => w.length > 2).slice(0, 2);
|
|
222
|
+
selected.push(...fallback);
|
|
187
223
|
}
|
|
188
|
-
|
|
224
|
+
|
|
225
|
+
let slug = selected.join('-');
|
|
226
|
+
|
|
227
|
+
// Truncate if still too long
|
|
228
|
+
if (slug.length > 30) {
|
|
229
|
+
slug = selected.slice(0, 2).join('-');
|
|
230
|
+
}
|
|
231
|
+
if (slug.length > 30) {
|
|
232
|
+
slug = slug.slice(0, 30);
|
|
233
|
+
}
|
|
234
|
+
|
|
189
235
|
return slug.replace(/^-+|-+$/g, '') || 'feature';
|
|
190
236
|
}
|
|
191
237
|
|