@tycoworks/tycoslide 0.10.0 → 0.10.1
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/README.md +1 -1
- package/dist/cli.js +4 -7
- package/dist/skillZip.d.ts +8 -8
- package/dist/skillZip.js +39 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -50,7 +50,7 @@ npx tycoslide build deck.md # → deck.pptx
|
|
|
50
50
|
```bash
|
|
51
51
|
npx tycoslide build deck.md # markdown → PPTX (theme resolved from deck frontmatter)
|
|
52
52
|
npx tycoslide build deck.md --no-notes # omit speaker notes from the output
|
|
53
|
-
npx tycoslide package #
|
|
53
|
+
npx tycoslide package # regenerate skill.md/syntax.md/manifest.json + zip the whole theme into a self-contained <package-name>.zip
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
## Theme Structure
|
package/dist/cli.js
CHANGED
|
@@ -5,7 +5,7 @@ import { Command } from "commander";
|
|
|
5
5
|
import { buildDeck } from "./index.js";
|
|
6
6
|
import { generateManifest } from "./manifest.js";
|
|
7
7
|
import { compileDeck, loadThemeConfig, parseSlideDocument, RESERVED_KEY } from "./markdown/index.js";
|
|
8
|
-
import { renameSkill,
|
|
8
|
+
import { renameSkill, zipDir } from "./skillZip.js";
|
|
9
9
|
const DEFAULT_CONFIG = "theme.json";
|
|
10
10
|
const MANIFEST_FILE = "manifest.json";
|
|
11
11
|
// The theme skill is written as lowercase skill.md (copied from tycoslide's own
|
|
@@ -76,13 +76,10 @@ program
|
|
|
76
76
|
const syntaxMd = readFileSync(syntaxMdPath, "utf-8");
|
|
77
77
|
writeFileSync(resolve(process.cwd(), SYNTAX_FILE), syntaxMd);
|
|
78
78
|
console.log(`WROTE ${SYNTAX_FILE}`);
|
|
79
|
+
// Bundle the WHOLE theme so the skill is self-contained: unzip ->
|
|
80
|
+
// `npm install` (pulls the engine + its deps) -> `npx tycoslide build`.
|
|
79
81
|
const zipFile = `${skillName}.zip`;
|
|
80
|
-
|
|
81
|
-
{ name: SKILL_FILE, content: skillMd },
|
|
82
|
-
{ name: SYNTAX_FILE, content: syntaxMd },
|
|
83
|
-
{ name: MANIFEST_FILE, content: manifestJson },
|
|
84
|
-
]);
|
|
85
|
-
writeFileSync(resolve(process.cwd(), zipFile), zipBuf);
|
|
82
|
+
writeFileSync(resolve(process.cwd(), zipFile), await zipDir(process.cwd(), skillName));
|
|
86
83
|
console.log(`WROTE ${zipFile}`);
|
|
87
84
|
});
|
|
88
85
|
await program.parseAsync(process.argv);
|
package/dist/skillZip.d.ts
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export declare function renameSkill(md: string, name: string): string;
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
* folder (e.g. `mz-slides/
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* Zip an entire theme directory into an uploadable Agent Skill archive whose
|
|
10
|
+
* entries all live under a single root folder (e.g. `mz-slides/theme.json`),
|
|
11
|
+
* matching Anthropic's custom-skill format. Recursively includes every file
|
|
12
|
+
* except node_modules, hidden entries (any name starting with `.`), and
|
|
13
|
+
* top-level build artifacts (.pptx/.pdf/.zip at the repo root; the template
|
|
14
|
+
* .pptx under template/ is kept). Subdirectory structure is preserved with
|
|
15
|
+
* POSIX slashes. Fails fast if nothing is left to zip.
|
|
13
16
|
*/
|
|
14
|
-
export declare function
|
|
15
|
-
name: string;
|
|
16
|
-
content: string | Buffer;
|
|
17
|
-
}[]): Promise<Buffer>;
|
|
17
|
+
export declare function zipDir(rootDir: string, folderName: string): Promise<Buffer>;
|
package/dist/skillZip.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { extname, join, relative, sep } from "node:path";
|
|
1
3
|
import JSZip from "jszip";
|
|
2
4
|
const FRONTMATTER = /^---\n([\s\S]*?)\n---/;
|
|
3
5
|
const NAME_LINE = /^name:[ \t]*.*$/m;
|
|
@@ -15,21 +17,48 @@ export function renameSkill(md, name) {
|
|
|
15
17
|
throw new Error('skill.md frontmatter has no "name:" line');
|
|
16
18
|
return md.replace(block[0], block[0].replace(NAME_LINE, `name: ${name}`));
|
|
17
19
|
}
|
|
20
|
+
// Never packaged: dependencies (npm install rebuilds them) and hidden entries
|
|
21
|
+
// (name starting with "." — covers VCS, tooling, caches, secrets like .env/.npmrc).
|
|
22
|
+
const EXCLUDE_DIRS = new Set(["node_modules"]);
|
|
23
|
+
// Build artifacts, dropped ONLY at the repo root: decks build to cwd
|
|
24
|
+
// (showcase.pptx, deck.pptx, the output .zip), while the template .pptx lives
|
|
25
|
+
// under template/ and must be kept — so these extensions are pruned top-level only.
|
|
26
|
+
const ROOT_ARTIFACT_EXTS = new Set([".pptx", ".pdf", ".zip"]);
|
|
18
27
|
/**
|
|
19
|
-
*
|
|
20
|
-
* folder (e.g. `mz-slides/
|
|
21
|
-
*
|
|
22
|
-
*
|
|
28
|
+
* Zip an entire theme directory into an uploadable Agent Skill archive whose
|
|
29
|
+
* entries all live under a single root folder (e.g. `mz-slides/theme.json`),
|
|
30
|
+
* matching Anthropic's custom-skill format. Recursively includes every file
|
|
31
|
+
* except node_modules, hidden entries (any name starting with `.`), and
|
|
32
|
+
* top-level build artifacts (.pptx/.pdf/.zip at the repo root; the template
|
|
33
|
+
* .pptx under template/ is kept). Subdirectory structure is preserved with
|
|
34
|
+
* POSIX slashes. Fails fast if nothing is left to zip.
|
|
23
35
|
*/
|
|
24
|
-
export async function
|
|
25
|
-
if (files.length === 0)
|
|
26
|
-
throw new Error(`No files to zip for skill folder: ${folderName}`);
|
|
36
|
+
export async function zipDir(rootDir, folderName) {
|
|
27
37
|
const zip = new JSZip();
|
|
28
38
|
const folder = zip.folder(folderName);
|
|
29
39
|
if (!folder)
|
|
30
40
|
throw new Error(`Failed to create zip folder: ${folderName}`);
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
41
|
+
let count = 0;
|
|
42
|
+
const walk = (dir) => {
|
|
43
|
+
const atRoot = dir === rootDir;
|
|
44
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
45
|
+
if (entry.name.startsWith("."))
|
|
46
|
+
continue;
|
|
47
|
+
const abs = join(dir, entry.name);
|
|
48
|
+
if (entry.isDirectory()) {
|
|
49
|
+
if (!EXCLUDE_DIRS.has(entry.name))
|
|
50
|
+
walk(abs);
|
|
51
|
+
}
|
|
52
|
+
else if (entry.isFile()) {
|
|
53
|
+
if (atRoot && ROOT_ARTIFACT_EXTS.has(extname(entry.name)))
|
|
54
|
+
continue;
|
|
55
|
+
folder.file(relative(rootDir, abs).split(sep).join("/"), readFileSync(abs));
|
|
56
|
+
count++;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
walk(rootDir);
|
|
61
|
+
if (count === 0)
|
|
62
|
+
throw new Error(`No files to zip in directory: ${rootDir}`);
|
|
34
63
|
return zip.generateAsync({ type: "nodebuffer" });
|
|
35
64
|
}
|