MCPcopy Index your code
hub / github.com/garrytan/gstack / commitSkill

Function commitSkill

browse/src/browser-skill-write.ts:119–172  ·  view source on GitHub ↗
(opts: CommitSkillOptions)

Source from the content-addressed store, hash-verified

117 * - resolved destination escapes the tier root (defense in depth)
118 */
119export function commitSkill(opts: CommitSkillOptions): string {
120 validateSkillName(opts.name);
121
122 const tiers = opts.tiers ?? defaultTierPaths();
123 const tierRoot = opts.tier === 'project' ? tiers.project : tiers.global;
124 if (!tierRoot) {
125 throw new Error(`commitSkill: tier "${opts.tier}" has no resolved path.`);
126 }
127
128 // Refuse to follow a symlinked staging dir — caller should hand us the path
129 // returned by stageSkill, which is always a real directory.
130 let stagedStat: fs.Stats;
131 try {
132 stagedStat = fs.lstatSync(opts.stagedDir);
133 } catch (err: any) {
134 throw new Error(`commitSkill: staged dir "${opts.stagedDir}" not accessible: ${err.code ?? err.message}`);
135 }
136 if (stagedStat.isSymbolicLink()) {
137 throw new Error(`commitSkill: staged dir "${opts.stagedDir}" is a symlink — refusing to commit.`);
138 }
139 if (!stagedStat.isDirectory()) {
140 throw new Error(`commitSkill: staged path "${opts.stagedDir}" is not a directory.`);
141 }
142
143 // Ensure the tier root exists, then resolve its real path so the final
144 // destination check defends against tierRoot itself being a symlink.
145 fs.mkdirSync(tierRoot, { recursive: true, mode: 0o755 });
146 const realTierRoot = fs.realpathSync(tierRoot);
147
148 const dest = path.join(realTierRoot, opts.name);
149 if (!isPathWithin(dest, realTierRoot)) {
150 // Should be impossible after validateSkillName, but defense in depth.
151 throw new Error(`commitSkill: destination "${dest}" escapes tier root.`);
152 }
153
154 // Refuse to clobber. Both regular dirs and symlinks count.
155 let destExists = false;
156 try {
157 fs.lstatSync(dest);
158 destExists = true;
159 } catch (err: any) {
160 if (err.code !== 'ENOENT') throw err;
161 }
162 if (destExists) {
163 throw new Error(
164 `commitSkill: a skill named "${opts.name}" already exists at ${dest}. ` +
165 `Pick a different name or remove the existing skill first ` +
166 `($B skill rm ${opts.name}${opts.tier === 'global' ? ' --global' : ''}).`,
167 );
168 }
169
170 fs.renameSync(opts.stagedDir, dest);
171 return dest;
172}
173
174// ─── Discard (cleanup on failure or reject) ─────────────────────
175

Callers 1

Calls 3

defaultTierPathsFunction · 0.90
isPathWithinFunction · 0.90
validateSkillNameFunction · 0.85

Tested by

no test coverage detected