@tycoworks/tycoslide 0.12.0 → 0.13.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 +6 -4
- package/SKILL.md +9 -9
- package/dist/cli.js +12 -10
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/manifest.d.ts +5 -0
- package/dist/manifest.js +10 -8
- package/dist/markdown/deckCompiler.js +2 -1
- package/dist/markdown/schema/themeConfigSchema.d.ts +1 -1
- package/dist/skillZip.d.ts +24 -5
- package/dist/skillZip.js +44 -13
- package/package.json +1 -1
- package/syntax.md +1 -1
package/README.md
CHANGED
|
@@ -54,7 +54,7 @@ npx tycoslide build deck.md # → deck.pptx
|
|
|
54
54
|
```bash
|
|
55
55
|
npx tycoslide build deck.md # markdown → PPTX (theme resolved from deck frontmatter)
|
|
56
56
|
npx tycoslide build deck.md --no-notes # omit speaker notes from the output
|
|
57
|
-
npx tycoslide package # regenerate skill
|
|
57
|
+
npx tycoslide package # regenerate the skill files + zip the theme into a self-contained <package-name>.zip
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
## Theme Structure
|
|
@@ -68,16 +68,18 @@ my-theme/
|
|
|
68
68
|
assets/icons/
|
|
69
69
|
theme.json
|
|
70
70
|
package.json
|
|
71
|
-
|
|
71
|
+
SKILL.md # generated by `tycoslide package`
|
|
72
72
|
syntax.md # generated by `tycoslide package`
|
|
73
|
-
manifest.json # generated by `tycoslide package`
|
|
73
|
+
manifest.json # generated by `tycoslide package` -- the theme's layouts
|
|
74
|
+
assets.json # generated by `tycoslide package` -- the theme's picture catalog
|
|
74
75
|
<package-name>.zip # uploadable Agent Skill bundle
|
|
75
76
|
```
|
|
76
77
|
|
|
77
78
|
**Template** — the PPTX file with named shapes that tycoslide fills.
|
|
78
79
|
**Layout** — a slide pattern in the template (Title, Body, Quote, etc.).
|
|
79
80
|
**Theme** — the directory that bundles a template, assets, and config.
|
|
80
|
-
**Manifest** —
|
|
81
|
+
**Manifest** — the machine-readable list of layouts, for an AI agent to read.
|
|
82
|
+
**Catalog** — the machine-readable list of assets, for an AI agent to search.
|
|
81
83
|
|
|
82
84
|
## Diagrams
|
|
83
85
|
|
package/SKILL.md
CHANGED
|
@@ -26,7 +26,8 @@ This skill builds on-brand decks from a markdown deck file. The theme provides s
|
|
|
26
26
|
|
|
27
27
|
| Task | Guide |
|
|
28
28
|
|------|-------|
|
|
29
|
-
| Discover layouts
|
|
29
|
+
| Discover layouts | Read `manifest.json` |
|
|
30
|
+
| Find a logo, illustration or icon | Search `assets.json` |
|
|
30
31
|
| Write a deck (structure, slots, assets) | See [Creating Slides](#creating-slides) below |
|
|
31
32
|
| Fix build errors | See [QA](#qa-required) below |
|
|
32
33
|
|
|
@@ -34,10 +35,9 @@ This skill builds on-brand decks from a markdown deck file. The theme provides s
|
|
|
34
35
|
|
|
35
36
|
## Layout Discovery
|
|
36
37
|
|
|
37
|
-
Before writing anything, read `manifest.json`. It
|
|
38
|
+
Before writing anything, read `manifest.json`. It lists the theme's **layouts** -- for each: `name`, `description`, `parameters` (frontmatter inputs) and `slots` (body regions). A layout is identified by its `name`; every parameter and slot by its `key`. Parameters carry a `type`, slots carry `accepts`, and either may be `required`.
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
- **assets** -- brand logos, client logos, illustrations, and icons (`description`)
|
|
40
|
+
Pictures live in `assets.json`: every logo, illustration and icon the theme offers, keyed by category and name. **Search it, do not read it whole** -- an icon set alone can run to thousands of entries. Grep for it and use the `$category.name` you find -- but **search for what the icon depicts, not what you mean by it**: a catalog is indexed by picture, so "freshness" finds nothing while `grep -i "clock" assets.json` and `grep -i "bolt"` find the icon you wanted.
|
|
41
41
|
|
|
42
42
|
A layout's inputs split two ways (see [syntax.md](syntax.md) for details):
|
|
43
43
|
- **parameters** -- one value on a frontmatter line. Fill by putting a value under the parameter's key in the slide frontmatter.
|
|
@@ -89,7 +89,7 @@ A deck file has three parts:
|
|
|
89
89
|
|
|
90
90
|
### Slide frontmatter
|
|
91
91
|
|
|
92
|
-
Every slide must have a `layout:` key. All other frontmatter keys map 1:1 to the layout's **parameters**.
|
|
92
|
+
Every slide must have a `layout:` key. All other frontmatter keys map 1:1 to the layout's **parameters**. A value containing a colon-then-space must be quoted -- `title: "The change: compute on the difference"` -- or YAML reads it as a second key and the build fails.
|
|
93
93
|
|
|
94
94
|
```yaml
|
|
95
95
|
---
|
|
@@ -141,8 +141,8 @@ Keep each slot's content to what its region comfortably holds. When content over
|
|
|
141
141
|
- **Don't repeat the same layout** -- vary layouts for visual rhythm
|
|
142
142
|
- **Don't overstuff a slot** -- keep content to what its region comfortably holds; split across slides when there's too much
|
|
143
143
|
- **Don't restyle the layout** -- the theme owns all design; you only fill slots
|
|
144
|
-
- **Don't use an image that's wrong for the slot** -- a small slot wants a simple icon, not a dense illustration. If you get a `shrunk to X%` warning, look at the rendered slide: if the image is now too small to make out, use a simpler one.
|
|
145
|
-
- **Don't invent layout or asset names** -- only use
|
|
144
|
+
- **Don't use an image that's wrong for the slot** -- a small slot wants a simple icon, not a dense illustration. If you get a `shrunk to X%` warning, look at the rendered slide: if the image is now too small to make out, use a simpler one. A `leaves X% of the frame empty` warning is the opposite: the image is a different shape from the slot. Neither fails the build -- judge both from the rendered slide.
|
|
145
|
+
- **Don't invent layout or asset names** -- only use layouts from `manifest.json` and assets from `assets.json`
|
|
146
146
|
- **Don't leave required parameters or slots empty** -- and don't leave a placeholder logo or dummy text in an image slot you care about
|
|
147
147
|
|
|
148
148
|
---
|
|
@@ -159,7 +159,7 @@ Build the deck again ([Build](#build)) and read the output carefully. Common err
|
|
|
159
159
|
|-------|-----|
|
|
160
160
|
| `unknown layout "xyz"` | Check layout names in `manifest.json` |
|
|
161
161
|
| A parameter or slot didn't fill | Use the key names the layout declares -- parameters in frontmatter, slots as body regions |
|
|
162
|
-
| An image didn't swap / placeholder remains | Write a `::key::` region using the image slot's key, containing `` from `
|
|
162
|
+
| An image didn't swap / placeholder remains | Write a `::key::` region using the image slot's key, containing `` from `assets.json` |
|
|
163
163
|
| YAML parse error | Fix the YAML syntax in the slide's frontmatter |
|
|
164
164
|
| `Skipped setting relation target` | The asset image couldn't be placed; check the path and file |
|
|
165
165
|
| `forbidden style directive` | Remove `style`, `classDef`, `linkStyle`, or `%%{init}` from your mermaid block -- use `class` for grouping instead |
|
|
@@ -211,7 +211,7 @@ Check for:
|
|
|
211
211
|
For each issue, suggest a specific fix.
|
|
212
212
|
|
|
213
213
|
Read: /path/to/deck.md and the rendered PNGs in the working directory
|
|
214
|
-
Also read: manifest.json (for layout documentation)
|
|
214
|
+
Also read: manifest.json (for layout documentation); search assets.json for pictures
|
|
215
215
|
```
|
|
216
216
|
|
|
217
217
|
If the subagent finds issues, fix them and rebuild.
|
package/dist/cli.js
CHANGED
|
@@ -3,14 +3,14 @@ import { basename, dirname, resolve } from "node:path";
|
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import { buildDeck } from "./index.js";
|
|
6
|
-
import { generateManifest } from "./manifest.js";
|
|
6
|
+
import { ASSETS_FILE, generateAssetCatalog, generateManifest } from "./manifest.js";
|
|
7
7
|
import { compileDeck, loadThemeConfig, parseSlideDocument, RESERVED_KEY } from "./markdown/index.js";
|
|
8
|
-
import { renameSkill, zipDir } from "./skillZip.js";
|
|
8
|
+
import { renameSkill, skillPackageJson, zipDir } from "./skillZip.js";
|
|
9
9
|
const DEFAULT_CONFIG = "theme.json";
|
|
10
10
|
const MANIFEST_FILE = "manifest.json";
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
const SKILL_FILE = "
|
|
11
|
+
// SKILL.md, uppercase: the Agent Skills format requires that exact filename at the
|
|
12
|
+
// root of a skill folder, and a case-sensitive filesystem will not find any other.
|
|
13
|
+
const SKILL_FILE = "SKILL.md";
|
|
14
14
|
const SYNTAX_FILE = "syntax.md";
|
|
15
15
|
const sdkDir = dirname(fileURLToPath(import.meta.url));
|
|
16
16
|
const skillMdPath = resolve(sdkDir, "..", "SKILL.md");
|
|
@@ -60,11 +60,12 @@ program
|
|
|
60
60
|
if (!themePkg.name) {
|
|
61
61
|
throw new Error('Cannot name the skill: the theme\'s package.json has no "name" field.');
|
|
62
62
|
}
|
|
63
|
-
// basename drops any npm scope, e.g. "@acme/
|
|
63
|
+
// basename drops any npm scope, e.g. "@acme/acme-slides" -> "acme-slides".
|
|
64
64
|
const skillName = basename(themePkg.name);
|
|
65
|
-
|
|
66
|
-
writeFileSync(resolve(process.cwd(), MANIFEST_FILE), manifestJson);
|
|
65
|
+
writeFileSync(resolve(process.cwd(), MANIFEST_FILE), `${generateManifest(config)}\n`);
|
|
67
66
|
console.log(`WROTE ${MANIFEST_FILE}`);
|
|
67
|
+
writeFileSync(resolve(process.cwd(), ASSETS_FILE), `${generateAssetCatalog(config)}\n`);
|
|
68
|
+
console.log(`WROTE ${ASSETS_FILE}`);
|
|
68
69
|
let skillMd;
|
|
69
70
|
try {
|
|
70
71
|
skillMd = renameSkill(readFileSync(skillMdPath, "utf-8"), skillName);
|
|
@@ -80,8 +81,9 @@ program
|
|
|
80
81
|
// Bundle the WHOLE theme so the skill is self-contained: unzip ->
|
|
81
82
|
// `npm install` (pulls the engine + its deps) -> `npx tycoslide build`.
|
|
82
83
|
const zipFile = `${skillName}.zip`;
|
|
83
|
-
const generated = [opts.config, MANIFEST_FILE, SKILL_FILE, SYNTAX_FILE];
|
|
84
|
-
|
|
84
|
+
const generated = [opts.config, MANIFEST_FILE, ASSETS_FILE, SKILL_FILE, SYNTAX_FILE];
|
|
85
|
+
const skillPkg = skillPackageJson(themePkg, { name: pkg.name, version: pkg.version });
|
|
86
|
+
writeFileSync(resolve(process.cwd(), zipFile), await zipDir(process.cwd(), skillName, config, generated, skillPkg));
|
|
85
87
|
console.log(`WROTE ${zipFile}`);
|
|
86
88
|
});
|
|
87
89
|
// Everything below the CLI throws plain Errors carrying a written-for-humans
|
package/dist/index.d.ts
CHANGED
|
@@ -34,6 +34,6 @@ export declare function toEngineConfig(config: CompilerConfig): Config;
|
|
|
34
34
|
export declare function buildDeck(deck: CompilerDeck, config: CompilerConfig, options?: GenerateOptions): Promise<void>;
|
|
35
35
|
export type { Config, Deck, DeckStep, GenerateOptions, ImageFill, Layout, Slot, StyledParagraph, TableFill, TextFill, TextRun, ThemeConfig, } from "./engine/index.js";
|
|
36
36
|
export { fillImage, fillTable, fillTemplate, fillText, generate, SlotType } from "./engine/index.js";
|
|
37
|
-
export { generateManifest } from "./manifest.js";
|
|
37
|
+
export { ASSETS_FILE, generateAssetCatalog, generateManifest } from "./manifest.js";
|
|
38
38
|
export type { AssetCatalog, AssetEntry, CompilerBlock, CompilerConfig, CompilerDeck, CompilerDeckStep, CompilerLayout, CompilerParameter, CompilerSlot, CompilerThemeConfig, EngineFill, MermaidConfig, MermaidVariant, ParsedDocument, RawSlide, } from "./markdown/index.js";
|
|
39
39
|
export { AcceptType, compileMarkdownDeck, loadThemeConfig, parseThemeConfig } from "./markdown/index.js";
|
package/dist/index.js
CHANGED
|
@@ -118,6 +118,6 @@ export async function buildDeck(deck, config, options = {}) {
|
|
|
118
118
|
// Engine — primitives-only public surface.
|
|
119
119
|
export { fillImage, fillTable, fillTemplate, fillText, generate, SlotType } from "./engine/index.js";
|
|
120
120
|
// Authoring
|
|
121
|
-
export { generateManifest } from "./manifest.js";
|
|
121
|
+
export { ASSETS_FILE, generateAssetCatalog, generateManifest } from "./manifest.js";
|
|
122
122
|
// Markdown / Compiler
|
|
123
123
|
export { AcceptType, compileMarkdownDeck, loadThemeConfig, parseThemeConfig } from "./markdown/index.js";
|
package/dist/manifest.d.ts
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
import type { CompilerConfig } from "./markdown/types.js";
|
|
2
|
+
/** Filename of the searchable asset catalog, named by the manifest that points at it. */
|
|
3
|
+
export declare const ASSETS_FILE = "assets.json";
|
|
4
|
+
/** The layouts document: read whole, so it carries no open-ended list. */
|
|
2
5
|
export declare function generateManifest(config: CompilerConfig): string;
|
|
6
|
+
/** The catalog document: searched by name, never read whole. */
|
|
7
|
+
export declare function generateAssetCatalog(config: CompilerConfig): string;
|
package/dist/manifest.js
CHANGED
|
@@ -18,6 +18,9 @@ function stripSlot(slot) {
|
|
|
18
18
|
result.required = true;
|
|
19
19
|
return result;
|
|
20
20
|
}
|
|
21
|
+
/** Filename of the searchable asset catalog, named by the manifest that points at it. */
|
|
22
|
+
export const ASSETS_FILE = "assets.json";
|
|
23
|
+
/** The layouts document: read whole, so it carries no open-ended list. */
|
|
21
24
|
export function generateManifest(config) {
|
|
22
25
|
const layouts = config.layouts.map((layout) => {
|
|
23
26
|
const ml = {
|
|
@@ -30,18 +33,17 @@ export function generateManifest(config) {
|
|
|
30
33
|
ml.description = layout.description;
|
|
31
34
|
return ml;
|
|
32
35
|
});
|
|
36
|
+
const manifest = { layouts, assets: ASSETS_FILE };
|
|
37
|
+
return JSON.stringify(manifest, null, 2);
|
|
38
|
+
}
|
|
39
|
+
/** The catalog document: searched by name, never read whole. */
|
|
40
|
+
export function generateAssetCatalog(config) {
|
|
33
41
|
const assets = {};
|
|
34
42
|
for (const [category, entries] of Object.entries(config.assets)) {
|
|
35
43
|
assets[category] = {};
|
|
36
44
|
for (const [name, entry] of Object.entries(entries)) {
|
|
37
|
-
|
|
38
|
-
path: entry.path,
|
|
39
|
-
type: entry.type,
|
|
40
|
-
description: entry.description,
|
|
41
|
-
};
|
|
42
|
-
assets[category][name] = manifestEntry;
|
|
45
|
+
assets[category][name] = { path: entry.path, type: entry.type, description: entry.description };
|
|
43
46
|
}
|
|
44
47
|
}
|
|
45
|
-
|
|
46
|
-
return JSON.stringify(manifest, null, 2);
|
|
48
|
+
return JSON.stringify(assets, null, 2);
|
|
47
49
|
}
|
|
@@ -257,7 +257,8 @@ export async function compileDeck(doc, config) {
|
|
|
257
257
|
const [, category, name] = match;
|
|
258
258
|
const entry = assets[category]?.[name];
|
|
259
259
|
if (!entry) {
|
|
260
|
-
// Every asset in the catalog is far too many to read
|
|
260
|
+
// Every asset in the catalog is far too many to read: a theme's icon set
|
|
261
|
+
// alone can run to thousands.
|
|
261
262
|
// A known category narrows it to that category's names, which is what the
|
|
262
263
|
// author is choosing between; an unknown one lists the categories instead.
|
|
263
264
|
const group = assets[category];
|
|
@@ -43,8 +43,8 @@ export declare const ThemeConfigSchema: z.ZodObject<{
|
|
|
43
43
|
assets: z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
44
44
|
path: z.ZodString;
|
|
45
45
|
type: z.ZodEnum<{
|
|
46
|
-
icon: "icon";
|
|
47
46
|
image: "image";
|
|
47
|
+
icon: "icon";
|
|
48
48
|
background: "background";
|
|
49
49
|
}>;
|
|
50
50
|
description: z.ZodString;
|
package/dist/skillZip.d.ts
CHANGED
|
@@ -1,16 +1,35 @@
|
|
|
1
1
|
import type { CompilerThemeConfig } from "./markdown/types.js";
|
|
2
2
|
/**
|
|
3
|
-
* Rewrite the `name:` value in a
|
|
3
|
+
* Rewrite the `name:` value in a SKILL.md's leading YAML frontmatter so the
|
|
4
4
|
* packaged skill is named after the consuming theme, not the source template.
|
|
5
5
|
* Only the leading `---`…`---` block is touched; the body is left byte-for-byte.
|
|
6
6
|
* Throws if there is no frontmatter or no `name:` line — the caller names the file.
|
|
7
7
|
*/
|
|
8
8
|
export declare function renameSkill(md: string, name: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* The `package.json` a packaged skill installs from — deliberately NOT the theme's
|
|
11
|
+
* own. A theme repo's manifest is a development document: it carries the script
|
|
12
|
+
* that regenerates the skill, and lists the engine as a devDependency because the
|
|
13
|
+
* repo builds with it rather than shipping it.
|
|
14
|
+
*
|
|
15
|
+
* Copying that verbatim breaks the consumer twice. The build script runs as a
|
|
16
|
+
* postinstall inside their container, so anything it touches that is read-only
|
|
17
|
+
* fails their whole `npm install`. And under `--omit=dev` the engine is never
|
|
18
|
+
* installed, so neither the postinstall nor `npx tycoslide build` can find it.
|
|
19
|
+
*
|
|
20
|
+
* What ships instead declares only what the skill needs to RUN: the theme's own
|
|
21
|
+
* dependencies plus the engine, as runtime dependencies, and no scripts at all.
|
|
22
|
+
*/
|
|
23
|
+
export declare function skillPackageJson(theme: Record<string, unknown>, engine: {
|
|
24
|
+
name: string;
|
|
25
|
+
version: string;
|
|
26
|
+
}): string;
|
|
9
27
|
/**
|
|
10
28
|
* Zip a theme into an uploadable Agent Skill archive whose entries all live
|
|
11
|
-
* under a single root folder (e.g. `
|
|
29
|
+
* under a single root folder (e.g. `acme-slides/theme.json`), matching Anthropic's
|
|
12
30
|
* custom-skill format. `generated` names the files the caller just wrote (the
|
|
13
|
-
* config, manifest,
|
|
14
|
-
*
|
|
31
|
+
* config, manifest, SKILL.md, syntax.md); `packageJson` is the authored manifest
|
|
32
|
+
* from `skillPackageJson`. Optional support files are skipped when absent;
|
|
33
|
+
* anything the config declares but that is missing is an error.
|
|
15
34
|
*/
|
|
16
|
-
export declare function zipDir(rootDir: string, folderName: string, config: CompilerThemeConfig, generated: string[]): Promise<Buffer>;
|
|
35
|
+
export declare function zipDir(rootDir: string, folderName: string, config: CompilerThemeConfig, generated: string[], packageJson: string): Promise<Buffer>;
|
package/dist/skillZip.js
CHANGED
|
@@ -5,7 +5,7 @@ import { TEMPLATE_DIR } from "./engine/index.js";
|
|
|
5
5
|
const FRONTMATTER = /^---\n([\s\S]*?)\n---/;
|
|
6
6
|
const NAME_LINE = /^name:[ \t]*.*$/m;
|
|
7
7
|
/**
|
|
8
|
-
* Rewrite the `name:` value in a
|
|
8
|
+
* Rewrite the `name:` value in a SKILL.md's leading YAML frontmatter so the
|
|
9
9
|
* packaged skill is named after the consuming theme, not the source template.
|
|
10
10
|
* Only the leading `---`…`---` block is touched; the body is left byte-for-byte.
|
|
11
11
|
* Throws if there is no frontmatter or no `name:` line — the caller names the file.
|
|
@@ -13,18 +13,47 @@ const NAME_LINE = /^name:[ \t]*.*$/m;
|
|
|
13
13
|
export function renameSkill(md, name) {
|
|
14
14
|
const block = md.match(FRONTMATTER);
|
|
15
15
|
if (!block)
|
|
16
|
-
throw new Error("
|
|
16
|
+
throw new Error("SKILL.md has no YAML frontmatter block");
|
|
17
17
|
if (!NAME_LINE.test(block[1]))
|
|
18
|
-
throw new Error('
|
|
18
|
+
throw new Error('SKILL.md frontmatter has no "name:" line');
|
|
19
19
|
return md.replace(block[0], block[0].replace(NAME_LINE, `name: ${name}`));
|
|
20
20
|
}
|
|
21
|
+
/** The manifest a packaged skill installs from, authored rather than copied. */
|
|
22
|
+
const PACKAGE_JSON = "package.json";
|
|
21
23
|
/**
|
|
22
|
-
* Files a packaged skill needs beyond the theme's own declarations.
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* when present so that install is reproducible.
|
|
24
|
+
* Files a packaged skill needs beyond the theme's own declarations. Only the
|
|
25
|
+
* lockfile: `package.json` is authored by `skillPackageJson` rather than taken
|
|
26
|
+
* from the theme directory.
|
|
26
27
|
*/
|
|
27
|
-
const SUPPORT_FILES = ["package
|
|
28
|
+
const SUPPORT_FILES = ["package-lock.json"];
|
|
29
|
+
/**
|
|
30
|
+
* The `package.json` a packaged skill installs from — deliberately NOT the theme's
|
|
31
|
+
* own. A theme repo's manifest is a development document: it carries the script
|
|
32
|
+
* that regenerates the skill, and lists the engine as a devDependency because the
|
|
33
|
+
* repo builds with it rather than shipping it.
|
|
34
|
+
*
|
|
35
|
+
* Copying that verbatim breaks the consumer twice. The build script runs as a
|
|
36
|
+
* postinstall inside their container, so anything it touches that is read-only
|
|
37
|
+
* fails their whole `npm install`. And under `--omit=dev` the engine is never
|
|
38
|
+
* installed, so neither the postinstall nor `npx tycoslide build` can find it.
|
|
39
|
+
*
|
|
40
|
+
* What ships instead declares only what the skill needs to RUN: the theme's own
|
|
41
|
+
* dependencies plus the engine, as runtime dependencies, and no scripts at all.
|
|
42
|
+
*/
|
|
43
|
+
export function skillPackageJson(theme, engine) {
|
|
44
|
+
const dependencies = {
|
|
45
|
+
...(theme.dependencies ?? {}),
|
|
46
|
+
[engine.name]: `^${engine.version}`,
|
|
47
|
+
};
|
|
48
|
+
const skill = {
|
|
49
|
+
name: theme.name,
|
|
50
|
+
version: theme.version,
|
|
51
|
+
description: theme.description,
|
|
52
|
+
private: true,
|
|
53
|
+
dependencies: Object.fromEntries(Object.entries(dependencies).sort(([a], [b]) => a.localeCompare(b))),
|
|
54
|
+
};
|
|
55
|
+
return `${JSON.stringify(skill, null, 2)}\n`;
|
|
56
|
+
}
|
|
28
57
|
/**
|
|
29
58
|
* Every path a packaged theme needs, relative to `rootDir` and POSIX-separated.
|
|
30
59
|
*
|
|
@@ -40,18 +69,20 @@ function skillPaths(config, generated) {
|
|
|
40
69
|
}
|
|
41
70
|
/**
|
|
42
71
|
* Zip a theme into an uploadable Agent Skill archive whose entries all live
|
|
43
|
-
* under a single root folder (e.g. `
|
|
72
|
+
* under a single root folder (e.g. `acme-slides/theme.json`), matching Anthropic's
|
|
44
73
|
* custom-skill format. `generated` names the files the caller just wrote (the
|
|
45
|
-
* config, manifest,
|
|
46
|
-
*
|
|
74
|
+
* config, manifest, SKILL.md, syntax.md); `packageJson` is the authored manifest
|
|
75
|
+
* from `skillPackageJson`. Optional support files are skipped when absent;
|
|
76
|
+
* anything the config declares but that is missing is an error.
|
|
47
77
|
*/
|
|
48
|
-
export async function zipDir(rootDir, folderName, config, generated) {
|
|
78
|
+
export async function zipDir(rootDir, folderName, config, generated, packageJson) {
|
|
49
79
|
const zip = new JSZip();
|
|
50
80
|
const folder = zip.folder(folderName);
|
|
51
81
|
if (!folder)
|
|
52
82
|
throw new Error(`Failed to create zip folder: ${folderName}`);
|
|
83
|
+
folder.file(PACKAGE_JSON, packageJson);
|
|
53
84
|
const optional = new Set(SUPPORT_FILES);
|
|
54
|
-
let count =
|
|
85
|
+
let count = 1;
|
|
55
86
|
for (const rel of skillPaths(config, generated)) {
|
|
56
87
|
const abs = join(rootDir, ...rel.split("/"));
|
|
57
88
|
if (!existsSync(abs)) {
|
package/package.json
CHANGED
package/syntax.md
CHANGED
|
@@ -140,7 +140,7 @@ Fill a slot by writing a `::key::` region in the body; the marker maps to the sl
|
|
|
140
140
|
|
|
141
141
|

|
|
142
142
|
```
|
|
143
|
-
The categories and names are
|
|
143
|
+
The categories and names are catalogued in `assets.json`. How the picture is scaled and cropped comes from the **asset's `type`** in the catalog: `icon` never enlarges past native and never crops, `image` fits the whole picture without cropping, `background` fills the frame and center-crops. A fenced `mermaid` block also fills an image slot, rendering to a themed PNG (see below).
|
|
144
144
|
|
|
145
145
|
---
|
|
146
146
|
|