diff options
Diffstat (limited to 'modules/ext/skill-pack-core.js')
| -rw-r--r-- | modules/ext/skill-pack-core.js | 37 |
1 files changed, 29 insertions, 8 deletions
diff --git a/modules/ext/skill-pack-core.js b/modules/ext/skill-pack-core.js index c2c95ba..3d3acf7 100644 --- a/modules/ext/skill-pack-core.js +++ b/modules/ext/skill-pack-core.js @@ -729,17 +729,28 @@ after the behaviour itself was fixed in v4.6.58.`), this.terminal.print('Usage: skill-create <name> <description> <path-or-text> [--tags=a,b] [--category=x]', 'warning'); return; } - const name = positional[0].replace(/[^a-z0-9-]/g, '').toLowerCase(); - if (!name) { this.terminal.print('Name must be kebab-case (letters, numbers, hyphens only)', 'error'); return; } + const rawName = positional[0]; + const name = rawName.toLowerCase(); + // MAINT-024: the old code stripped invalid characters instead of rejecting them, so + // "My-Skill" became "y-kill" and "DevOps_Thing" became "evpshing" — a skill created + // under a name nobody asked for, reported as success. + if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(name)) { + this.terminal.print(`Invalid skill name "${rawName}": use kebab-case (letters, numbers, single hyphens, e.g. my-skill)`, 'error'); + return; + } const desc = positional[1]; const bodyArg = positional.slice(2).join(' '); const fs0 = this.terminal.fs; let content = bodyArg; if (bodyArg.startsWith('/')) { - try { - const fromFile = fs0.readFile(bodyArg); - if (typeof fromFile === 'string' && fromFile.length) content = fromFile; - } catch (e) { /* not a file: treat the argument as literal text */ } + // A path that cannot be read is an error, not a body: silently using the path text + // as the skill body produced a skill whose content was "/skills/nope.md". + const fromFile = fs0.readFile(bodyArg); + if (typeof fromFile !== 'string' || !fromFile.length) { + this.terminal.print(`Cannot read skill body: ${bodyArg} (missing or empty)`, 'error'); + return; + } + content = fromFile; } const tags = flags.tags; const fs = this.terminal.fs; @@ -751,9 +762,19 @@ after the behaviour itself was fixed in v4.6.58.`), } const now = new Date().toISOString(); const skillContent = `---\nname: ${name}\ndescription: ${desc}\ntags: [${tags.join(', ')}]\ncategory: ${flags.category}\n---\n${content}`; - fs.writeFile(`/skills/${name}.md`, skillContent); + // MAINT-024: create /skills when this is the first skill, and do not print ✅ unless the + // file actually landed — the old code reported success on a failed write. + try { if (!fs.readDir('/skills')) fs.mkdirRecursive('/skills'); } catch (e) { /* write below reports it */ } + const wroteFile = fs.writeFile(`/skills/${name}.md`, skillContent); + if (!wroteFile) { + this.terminal.print(`Failed to write /skills/${name}.md — the skills directory is not writable`, 'error'); + return; + } index[name] = { name, description: desc, tags, category: flags.category, created: now, updated: now }; - fs.fsWriteJSON('/skills/index.json', index); + const wroteIndex = fs.fsWriteJSON('/skills/index.json', index); + if (wroteIndex === false) { + this.terminal.print(`⚠️ Skill "${name}" written, but /skills/index.json could not be updated — it may not appear in skill lists`, 'warning'); + } this.terminal.print(`✅ Skill "${name}" created (${(skillContent.length / 1024).toFixed(1)}KB)`, 'success'); } |