@bonnard/agentops
Advanced tools
+33
-29
@@ -14,3 +14,2 @@ #!/usr/bin/env node | ||
| import * as tar from "tar"; | ||
| import { Pack } from "tar"; | ||
| //#region src/lib/credentials.ts | ||
@@ -774,3 +773,20 @@ const AGENTOPS_DIR = path.join(os.homedir(), ".agentops"); | ||
| * Pack a skill directory into a .tgz Buffer. | ||
| * The tarball contains all files with paths relative to the skill directory root. | ||
| * | ||
| * Bundle format (from CLI 0.7.10 onwards): | ||
| * - Files live under a top-level directory named after the skill | ||
| * - e.g. "my-skill/SKILL.md", "my-skill/scripts/run.sh" | ||
| * | ||
| * This way, when a user downloads the bundle from the web and extracts it | ||
| * via macOS Archive Utility (or any double-click unpacker), they get a | ||
| * "my-skill/" folder containing the files — not a pile of files in their | ||
| * Downloads folder. | ||
| * | ||
| * The CLI install path strips this wrapper (see unpackSkill) so files end | ||
| * up directly under `.claude/skills/<name>/` as before. | ||
| * | ||
| * Old bundles submitted before 0.7.10 still work — they use the flat | ||
| * format (files at the root of the tarball) and unpackSkill handles both. | ||
| * | ||
| * Precondition: path.basename(skillDir) must equal the skill name. The | ||
| * skill-scaffolding and find helpers always satisfy this. | ||
| */ | ||
@@ -783,16 +799,11 @@ async function packSkill(skillDir) { | ||
| if (!fs.existsSync(skillMd)) throw new Error(`No SKILL.md found in ${resolved}`); | ||
| const files = walkDir(resolved, resolved); | ||
| const parentDir = path.dirname(resolved); | ||
| const dirName = path.basename(resolved); | ||
| const packStream = tar.create({ | ||
| cwd: parentDir, | ||
| gzip: true, | ||
| portable: true | ||
| }, [dirName]); | ||
| const chunks = []; | ||
| await new Promise((resolve, reject) => { | ||
| const pack = new Pack({ | ||
| cwd: resolved, | ||
| gzip: true, | ||
| portable: true | ||
| }); | ||
| pack.on("data", (chunk) => chunks.push(chunk)); | ||
| pack.on("end", () => resolve()); | ||
| pack.on("error", (err) => reject(err)); | ||
| for (const file of files) pack.write(file); | ||
| pack.end(); | ||
| }); | ||
| for await (const chunk of packStream) chunks.push(chunk); | ||
| return Buffer.concat(chunks); | ||
@@ -803,2 +814,6 @@ } | ||
| * Creates the target directory if it doesn't exist. | ||
| * | ||
| * Always strips one leading path component — bundles from CLI 0.7.10+ are | ||
| * wrapped in a top-level skill-name directory, and we drop it on install so | ||
| * files land directly under `.claude/skills/<name>/`. | ||
| */ | ||
@@ -811,4 +826,4 @@ async function unpackSkill(tgz, targetDir) { | ||
| cwd: resolved, | ||
| strip: 0, | ||
| strict: true | ||
| strip: 1, | ||
| strict: false | ||
| }); | ||
@@ -839,14 +854,2 @@ extract.on("end", () => resolve()); | ||
| } | ||
| /** | ||
| * Walk a directory recursively and return relative file paths. | ||
| */ | ||
| function walkDir(dir, root) { | ||
| const results = []; | ||
| for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { | ||
| const fullPath = path.join(dir, entry.name); | ||
| if (entry.isDirectory()) results.push(...walkDir(fullPath, root)); | ||
| else if (entry.isFile()) results.push(path.relative(root, fullPath)); | ||
| } | ||
| return results.sort(); | ||
| } | ||
| //#endregion | ||
@@ -965,2 +968,3 @@ //#region src/commands/install.ts | ||
| if (skill.tags.length > 0) console.log(pc.dim(`[${skill.tags.join(", ")}]`)); | ||
| if (typeof skill.fileCount === "number") console.log(pc.dim(`${skill.fileCount} ${skill.fileCount === 1 ? "file" : "files"}`)); | ||
| console.log(); | ||
@@ -967,0 +971,0 @@ const draftLabel = isPublishedClean ? "" : pc.yellow(" (draft — pending review)"); |
+1
-1
| { | ||
| "name": "@bonnard/agentops", | ||
| "version": "0.7.9", | ||
| "version": "0.7.10", | ||
| "type": "module", | ||
@@ -5,0 +5,0 @@ "bin": { |
63274
0.84%1653
0.24%