@tycoworks/bon 0.1.0 → 0.1.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/bin/bon.js CHANGED
@@ -1,2 +1,2 @@
1
- #!/usr/bin/env node --experimental-strip-types --no-warnings
2
- import("../src/cli.ts");
1
+ #!/usr/bin/env node
2
+ import("../dist/cli.js");
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,92 @@
1
+ import { copyFileSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { generate } from "@tycoworks/bon-core";
5
+ import { Command } from "commander";
6
+ import { generateManifest } from "./manifest.js";
7
+ import { smokeSteps } from "./smoke.js";
8
+ const DEFAULT_CONFIG = "theme.config.ts";
9
+ const DEFAULT_SMOKE_OUTPUT = "smoke-all.pptx";
10
+ const SKILL_DIR = "skills/slides";
11
+ const MANIFEST_FILE = "manifest.json";
12
+ const SKILL_FILE = "SKILL.md";
13
+ const BUILD_COMMAND = "npx bon build";
14
+ const sdkDir = dirname(fileURLToPath(import.meta.url));
15
+ const skillMdPath = resolve(sdkDir, "..", SKILL_FILE);
16
+ async function loadConfig(configPath) {
17
+ const abs = resolve(process.cwd(), configPath);
18
+ let mod;
19
+ try {
20
+ mod = await import(abs);
21
+ }
22
+ catch {
23
+ throw new Error(`Config file not found: ${configPath}`);
24
+ }
25
+ const raw = (mod.default ?? mod.config);
26
+ if (!raw) {
27
+ throw new Error(`${configPath} must export a default or named "config" of type ThemeConfig`);
28
+ }
29
+ return { ...raw, rootDir: dirname(abs) };
30
+ }
31
+ const program = new Command().name("bon").description("PPTX template engine CLI").version("0.1.0");
32
+ program
33
+ .command("build")
34
+ .description("Build a PPTX deck from a JSON spec")
35
+ .argument("<deck>", "path to deck JSON file")
36
+ .argument("[output]", "output PPTX filename")
37
+ .option(`-c, --config <path>`, "path to theme config file", DEFAULT_CONFIG)
38
+ .action(async (deckPath, output, opts) => {
39
+ const config = await loadConfig(opts.config);
40
+ let deck;
41
+ try {
42
+ const raw = readFileSync(resolve(process.cwd(), deckPath), "utf-8");
43
+ deck = JSON.parse(raw);
44
+ }
45
+ catch (err) {
46
+ throw new Error(`Failed to read deck file: ${deckPath} (${err.message})`);
47
+ }
48
+ if (output)
49
+ deck.output = output;
50
+ await generate(deck, config);
51
+ });
52
+ program
53
+ .command("manifest")
54
+ .description("Generate manifest.json from theme config")
55
+ .option(`-c, --config <path>`, "path to theme config file", DEFAULT_CONFIG)
56
+ .option(`-o, --out <file>`, "write to file instead of stdout")
57
+ .action(async (opts) => {
58
+ const config = await loadConfig(opts.config);
59
+ const json = generateManifest(config, { build: { command: BUILD_COMMAND } });
60
+ if (opts.out) {
61
+ writeFileSync(resolve(process.cwd(), opts.out), `${json}\n`);
62
+ console.log(`WROTE ${opts.out}`);
63
+ }
64
+ else {
65
+ process.stdout.write(`${json}\n`);
66
+ }
67
+ });
68
+ program
69
+ .command("smoke")
70
+ .description("Generate one smoke-test slide per layout")
71
+ .option(`-c, --config <path>`, "path to theme config file", DEFAULT_CONFIG)
72
+ .option(`-o, --out <file>`, "output PPTX filename", DEFAULT_SMOKE_OUTPUT)
73
+ .action(async (opts) => {
74
+ const config = await loadConfig(opts.config);
75
+ const steps = smokeSteps(config);
76
+ await generate({ output: opts.out, steps }, config);
77
+ });
78
+ program
79
+ .command("plugin")
80
+ .description("Generate plugin package (manifest.json + SKILL.md) for AI agents")
81
+ .option(`-c, --config <path>`, "path to theme config file", DEFAULT_CONFIG)
82
+ .action(async (opts) => {
83
+ const config = await loadConfig(opts.config);
84
+ const outDir = resolve(process.cwd(), SKILL_DIR);
85
+ mkdirSync(outDir, { recursive: true });
86
+ const json = generateManifest(config, { build: { command: BUILD_COMMAND } });
87
+ writeFileSync(resolve(outDir, MANIFEST_FILE), `${json}\n`);
88
+ console.log(`WROTE ${SKILL_DIR}/${MANIFEST_FILE}`);
89
+ copyFileSync(skillMdPath, resolve(outDir, SKILL_FILE));
90
+ console.log(`WROTE ${SKILL_DIR}/${SKILL_FILE}`);
91
+ });
92
+ await program.parseAsync(process.argv);
@@ -0,0 +1,2 @@
1
+ import type { ThemeConfig } from "@tycoworks/bon-core";
2
+ export declare function defineConfig(config: ThemeConfig): ThemeConfig;
package/dist/config.js ADDED
@@ -0,0 +1,3 @@
1
+ export function defineConfig(config) {
2
+ return config;
3
+ }
@@ -0,0 +1,6 @@
1
+ export type { AssetCatalog, AssetEntry, AssetSlot, Config, ContentSlot, Deck, DeckStep, Layout, ParagraphSelector, ThemeConfig, } from "@tycoworks/bon-core";
2
+ export { AssetType, Fit, generate, SlotType } from "@tycoworks/bon-core";
3
+ export { defineConfig } from "./config.js";
4
+ export type { ManifestOptions } from "./manifest.js";
5
+ export { generateManifest } from "./manifest.js";
6
+ export { smokeSteps } from "./smoke.js";
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { AssetType, Fit, generate, SlotType } from "@tycoworks/bon-core";
2
+ export { defineConfig } from "./config.js";
3
+ export { generateManifest } from "./manifest.js";
4
+ export { smokeSteps } from "./smoke.js";
@@ -0,0 +1,7 @@
1
+ import type { Config } from "@tycoworks/bon-core";
2
+ export type ManifestOptions = {
3
+ build?: {
4
+ command: string;
5
+ };
6
+ };
7
+ export declare function generateManifest(config: Config, options?: ManifestOptions): string;
@@ -0,0 +1,53 @@
1
+ function stripContentSlot(slot) {
2
+ const result = { key: slot.key, type: slot.type };
3
+ if (slot.limit) {
4
+ result.limit = slot.limit;
5
+ }
6
+ return result;
7
+ }
8
+ function stripAssetSlot(slot) {
9
+ const result = { key: slot.key, accepts: slot.accepts };
10
+ if (slot.required) {
11
+ result.required = true;
12
+ }
13
+ return result;
14
+ }
15
+ export function generateManifest(config, options) {
16
+ const layouts = config.layouts.map((layout) => ({
17
+ name: layout.name,
18
+ description: layout.description,
19
+ whenToUse: layout.whenToUse,
20
+ whenNotToUse: layout.whenNotToUse,
21
+ contentSlots: layout.contentSlots.map(stripContentSlot),
22
+ assetSlots: (layout.assetSlots ?? []).map(stripAssetSlot),
23
+ }));
24
+ const assets = {};
25
+ for (const [category, entries] of Object.entries(config.assets)) {
26
+ assets[category] = {};
27
+ for (const [name, entry] of Object.entries(entries)) {
28
+ const manifestEntry = {
29
+ path: entry.path,
30
+ type: entry.type,
31
+ description: entry.description,
32
+ };
33
+ if (entry.whenToUse) {
34
+ manifestEntry.whenToUse = entry.whenToUse;
35
+ }
36
+ assets[category][name] = manifestEntry;
37
+ }
38
+ }
39
+ const manifest = {
40
+ version: 1,
41
+ layouts,
42
+ assets,
43
+ build: {
44
+ command: options?.build?.command ?? "npx bon build",
45
+ deckFormat: "json",
46
+ deckSchema: {
47
+ output: "string",
48
+ steps: "array of { layout, content, assets }",
49
+ },
50
+ },
51
+ };
52
+ return JSON.stringify(manifest, null, 2);
53
+ }
@@ -0,0 +1,2 @@
1
+ import type { Config, DeckStep } from "@tycoworks/bon-core";
2
+ export declare function smokeSteps(config: Config): DeckStep[];
package/dist/smoke.js ADDED
@@ -0,0 +1,36 @@
1
+ import { SlotType } from "@tycoworks/bon-core";
2
+ function pickAsset(config, slot) {
3
+ for (const group of Object.values(config.assets)) {
4
+ for (const entry of Object.values(group)) {
5
+ if (entry.type === slot.accepts)
6
+ return entry.path;
7
+ }
8
+ }
9
+ return undefined;
10
+ }
11
+ export function smokeSteps(config) {
12
+ return config.layouts.map((layout) => {
13
+ const content = {};
14
+ for (const s of layout.contentSlots) {
15
+ if (s.type === SlotType.Prose) {
16
+ content[s.key] = ["Sample intro line for this block.", "- First point", "- Second point"];
17
+ }
18
+ else if (s.type === SlotType.Lines) {
19
+ content[s.key] = ["Sample Name", "Sample Role, Company"];
20
+ }
21
+ else if (s.key.includes("value")) {
22
+ content[s.key] = "42%";
23
+ }
24
+ else {
25
+ content[s.key] = `Sample ${s.key.replace(/_/g, " ")}`;
26
+ }
27
+ }
28
+ const assets = {};
29
+ for (const a of layout.assetSlots ?? []) {
30
+ const path = pickAsset(config, a);
31
+ if (path)
32
+ assets[a.key] = path;
33
+ }
34
+ return { layout: layout.name, content, assets };
35
+ });
36
+ }
package/package.json CHANGED
@@ -1,16 +1,19 @@
1
1
  {
2
2
  "name": "@tycoworks/bon",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
- "main": "src/index.ts",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
6
7
  "bin": {
7
8
  "bon": "bin/bon.js"
8
9
  },
10
+ "files": ["dist", "bin", "SKILL.md"],
9
11
  "scripts": {
12
+ "build": "tsc --build",
10
13
  "typecheck": "tsc --noEmit"
11
14
  },
12
15
  "dependencies": {
13
- "@tycoworks/bon-core": "^0.1.0",
16
+ "@tycoworks/bon-core": "^0.1.1",
14
17
  "commander": "^15.0.0"
15
18
  },
16
19
  "devDependencies": {
package/src/cli.ts DELETED
@@ -1,98 +0,0 @@
1
- import { copyFileSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
- import { dirname, resolve } from "node:path";
3
- import { fileURLToPath } from "node:url";
4
- import type { Config, ThemeConfig } from "@tycoworks/bon-core";
5
- import { generate } from "@tycoworks/bon-core";
6
- import { Command } from "commander";
7
- import { generateManifest } from "./manifest.ts";
8
- import { smokeSteps } from "./smoke.ts";
9
-
10
- const DEFAULT_CONFIG = "theme.config.ts";
11
- const DEFAULT_SMOKE_OUTPUT = "smoke-all.pptx";
12
- const SKILL_DIR = "skills/slides";
13
- const MANIFEST_FILE = "manifest.json";
14
- const SKILL_FILE = "SKILL.md";
15
- const BUILD_COMMAND = "npx bon build";
16
-
17
- const sdkDir = dirname(fileURLToPath(import.meta.url));
18
- const skillMdPath = resolve(sdkDir, "..", SKILL_FILE);
19
-
20
- async function loadConfig(configPath: string): Promise<Config> {
21
- const abs = resolve(process.cwd(), configPath);
22
- let mod: Record<string, unknown>;
23
- try {
24
- mod = await import(abs);
25
- } catch {
26
- throw new Error(`Config file not found: ${configPath}`);
27
- }
28
- const raw = (mod.default ?? mod.config) as ThemeConfig | undefined;
29
- if (!raw) {
30
- throw new Error(`${configPath} must export a default or named "config" of type ThemeConfig`);
31
- }
32
- return { ...raw, rootDir: dirname(abs) };
33
- }
34
-
35
- const program = new Command().name("bon").description("PPTX template engine CLI").version("0.1.0");
36
-
37
- program
38
- .command("build")
39
- .description("Build a PPTX deck from a JSON spec")
40
- .argument("<deck>", "path to deck JSON file")
41
- .argument("[output]", "output PPTX filename")
42
- .option(`-c, --config <path>`, "path to theme config file", DEFAULT_CONFIG)
43
- .action(async (deckPath: string, output: string | undefined, opts: { config: string }) => {
44
- const config = await loadConfig(opts.config);
45
- let deck: Record<string, unknown>;
46
- try {
47
- const raw = readFileSync(resolve(process.cwd(), deckPath), "utf-8");
48
- deck = JSON.parse(raw);
49
- } catch (err) {
50
- throw new Error(`Failed to read deck file: ${deckPath} (${(err as Error).message})`);
51
- }
52
- if (output) deck.output = output;
53
- await generate(deck as any, config);
54
- });
55
-
56
- program
57
- .command("manifest")
58
- .description("Generate manifest.json from theme config")
59
- .option(`-c, --config <path>`, "path to theme config file", DEFAULT_CONFIG)
60
- .option(`-o, --out <file>`, "write to file instead of stdout")
61
- .action(async (opts: { config: string; out?: string }) => {
62
- const config = await loadConfig(opts.config);
63
- const json = generateManifest(config, { build: { command: BUILD_COMMAND } });
64
- if (opts.out) {
65
- writeFileSync(resolve(process.cwd(), opts.out), `${json}\n`);
66
- console.log(`WROTE ${opts.out}`);
67
- } else {
68
- process.stdout.write(`${json}\n`);
69
- }
70
- });
71
-
72
- program
73
- .command("smoke")
74
- .description("Generate one smoke-test slide per layout")
75
- .option(`-c, --config <path>`, "path to theme config file", DEFAULT_CONFIG)
76
- .option(`-o, --out <file>`, "output PPTX filename", DEFAULT_SMOKE_OUTPUT)
77
- .action(async (opts: { config: string; out: string }) => {
78
- const config = await loadConfig(opts.config);
79
- const steps = smokeSteps(config);
80
- await generate({ output: opts.out, steps }, config);
81
- });
82
-
83
- program
84
- .command("plugin")
85
- .description("Generate plugin package (manifest.json + SKILL.md) for AI agents")
86
- .option(`-c, --config <path>`, "path to theme config file", DEFAULT_CONFIG)
87
- .action(async (opts: { config: string }) => {
88
- const config = await loadConfig(opts.config);
89
- const outDir = resolve(process.cwd(), SKILL_DIR);
90
- mkdirSync(outDir, { recursive: true });
91
- const json = generateManifest(config, { build: { command: BUILD_COMMAND } });
92
- writeFileSync(resolve(outDir, MANIFEST_FILE), `${json}\n`);
93
- console.log(`WROTE ${SKILL_DIR}/${MANIFEST_FILE}`);
94
- copyFileSync(skillMdPath, resolve(outDir, SKILL_FILE));
95
- console.log(`WROTE ${SKILL_DIR}/${SKILL_FILE}`);
96
- });
97
-
98
- await program.parseAsync(process.argv);
package/src/config.ts DELETED
@@ -1,5 +0,0 @@
1
- import type { ThemeConfig } from "@tycoworks/bon-core";
2
-
3
- export function defineConfig(config: ThemeConfig): ThemeConfig {
4
- return config;
5
- }
package/src/index.ts DELETED
@@ -1,17 +0,0 @@
1
- export type {
2
- AssetCatalog,
3
- AssetEntry,
4
- AssetSlot,
5
- Config,
6
- ContentSlot,
7
- Deck,
8
- DeckStep,
9
- Layout,
10
- ParagraphSelector,
11
- ThemeConfig,
12
- } from "@tycoworks/bon-core";
13
- export { AssetType, Fit, generate, SlotType } from "@tycoworks/bon-core";
14
- export { defineConfig } from "./config.ts";
15
- export type { ManifestOptions } from "./manifest.ts";
16
- export { generateManifest } from "./manifest.ts";
17
- export { smokeSteps } from "./smoke.ts";
package/src/manifest.ts DELETED
@@ -1,106 +0,0 @@
1
- import type { Config } from "@tycoworks/bon-core";
2
-
3
- export type ManifestOptions = {
4
- build?: { command: string };
5
- };
6
-
7
- type ManifestContentSlot = {
8
- key: string;
9
- type: string;
10
- limit?: { maxChars?: number; maxLines?: number; maxItems?: number };
11
- };
12
-
13
- type ManifestAssetSlot = {
14
- key: string;
15
- accepts: string;
16
- required?: true;
17
- };
18
-
19
- type ManifestLayout = {
20
- name: string;
21
- description: string;
22
- whenToUse: string;
23
- whenNotToUse: string;
24
- contentSlots: ManifestContentSlot[];
25
- assetSlots: ManifestAssetSlot[];
26
- };
27
-
28
- type ManifestAssetEntry = {
29
- path: string;
30
- type: string;
31
- description: string;
32
- whenToUse?: string;
33
- };
34
-
35
- type Manifest = {
36
- version: 1;
37
- layouts: ManifestLayout[];
38
- assets: Record<string, Record<string, ManifestAssetEntry>>;
39
- build: {
40
- command: string;
41
- deckFormat: "json";
42
- deckSchema: {
43
- output: "string";
44
- steps: "array of { layout, content, assets }";
45
- };
46
- };
47
- };
48
-
49
- function stripContentSlot(slot: { key: string; type: string; limit?: object }): ManifestContentSlot {
50
- const result: ManifestContentSlot = { key: slot.key, type: slot.type };
51
- if (slot.limit) {
52
- result.limit = slot.limit;
53
- }
54
- return result;
55
- }
56
-
57
- function stripAssetSlot(slot: { key: string; accepts: string; required?: boolean }): ManifestAssetSlot {
58
- const result: ManifestAssetSlot = { key: slot.key, accepts: slot.accepts };
59
- if (slot.required) {
60
- result.required = true;
61
- }
62
- return result;
63
- }
64
-
65
- export function generateManifest(config: Config, options?: ManifestOptions): string {
66
- const layouts: ManifestLayout[] = config.layouts.map((layout) => ({
67
- name: layout.name,
68
- description: layout.description,
69
- whenToUse: layout.whenToUse,
70
- whenNotToUse: layout.whenNotToUse,
71
- contentSlots: layout.contentSlots.map(stripContentSlot),
72
- assetSlots: (layout.assetSlots ?? []).map(stripAssetSlot),
73
- }));
74
-
75
- const assets: Record<string, Record<string, ManifestAssetEntry>> = {};
76
- for (const [category, entries] of Object.entries(config.assets)) {
77
- assets[category] = {};
78
- for (const [name, entry] of Object.entries(entries)) {
79
- const manifestEntry: ManifestAssetEntry = {
80
- path: entry.path,
81
- type: entry.type,
82
- description: entry.description,
83
- };
84
- if (entry.whenToUse) {
85
- manifestEntry.whenToUse = entry.whenToUse;
86
- }
87
- assets[category][name] = manifestEntry;
88
- }
89
- }
90
-
91
- const manifest: Manifest = {
92
- version: 1,
93
- layouts,
94
- assets,
95
- build: {
96
- command: options?.build?.command ?? "npx bon build",
97
- deckFormat: "json",
98
- deckSchema: {
99
- output: "string",
100
- steps: "array of { layout, content, assets }",
101
- },
102
- },
103
- };
104
-
105
- return JSON.stringify(manifest, null, 2);
106
- }
package/src/smoke.ts DELETED
@@ -1,34 +0,0 @@
1
- import type { Config, DeckStep } from "@tycoworks/bon-core";
2
- import { SlotType } from "@tycoworks/bon-core";
3
-
4
- function pickAsset(config: Config, slot: { key: string; accepts: string }): string | undefined {
5
- for (const group of Object.values(config.assets)) {
6
- for (const entry of Object.values(group)) {
7
- if (entry.type === slot.accepts) return entry.path;
8
- }
9
- }
10
- return undefined;
11
- }
12
-
13
- export function smokeSteps(config: Config): DeckStep[] {
14
- return config.layouts.map((layout) => {
15
- const content: Record<string, string | string[]> = {};
16
- for (const s of layout.contentSlots) {
17
- if (s.type === SlotType.Prose) {
18
- content[s.key] = ["Sample intro line for this block.", "- First point", "- Second point"];
19
- } else if (s.type === SlotType.Lines) {
20
- content[s.key] = ["Sample Name", "Sample Role, Company"];
21
- } else if (s.key.includes("value")) {
22
- content[s.key] = "42%";
23
- } else {
24
- content[s.key] = `Sample ${s.key.replace(/_/g, " ")}`;
25
- }
26
- }
27
- const assets: Record<string, string> = {};
28
- for (const a of layout.assetSlots ?? []) {
29
- const path = pickAsset(config, a);
30
- if (path) assets[a.key] = path;
31
- }
32
- return { layout: layout.name, content, assets };
33
- });
34
- }
package/tsconfig.json DELETED
@@ -1,19 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "es2022",
4
- "lib": ["es2023"],
5
- "module": "nodenext",
6
- "moduleResolution": "nodenext",
7
- "allowImportingTsExtensions": true,
8
- "verbatimModuleSyntax": true,
9
- "erasableSyntaxOnly": true,
10
- "declaration": true,
11
- "declarationDir": "dist",
12
- "emitDeclarationOnly": true,
13
- "strict": true,
14
- "skipLibCheck": true,
15
- "composite": true
16
- },
17
- "include": ["src/**/*.ts"],
18
- "references": [{ "path": "../core" }]
19
- }