konpeki 0.2.0 → 0.3.0

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.
Files changed (39) hide show
  1. package/AGENTS.md +1 -1
  2. package/CONTRIBUTING.md +36 -0
  3. package/README.md +54 -18
  4. package/SECURITY.md +22 -0
  5. package/SETUP.md +112 -51
  6. package/composition/README.md +16 -3
  7. package/composition/compile.ts +1 -1
  8. package/composition/schema.json +9 -0
  9. package/composition/schema.ts +3 -1
  10. package/composition/types.ts +3 -1
  11. package/design/themes/README.md +25 -7
  12. package/design/themes/index.ts +15 -4
  13. package/docs/development.md +71 -11
  14. package/docs/workflow.md +11 -4
  15. package/index.html +17 -1
  16. package/package.json +8 -2
  17. package/plugin.json +22 -0
  18. package/public/og.png +0 -0
  19. package/runtime/konpeki.mjs +41 -13
  20. package/skills/konpeki/SKILL.md +186 -0
  21. package/skills/konpeki/assets/blank.json +23 -0
  22. package/skills/konpeki/scripts/ensure-runtime.mjs +69 -0
  23. package/skills/konpeki/scripts/prepare-document.mjs +40 -0
  24. package/slides/introducing-konpeki/PROMPT.md +21 -0
  25. package/slides/introducing-konpeki/README.md +52 -30
  26. package/slides/introducing-konpeki/SOURCE.md +16 -10
  27. package/slides/introducing-konpeki/author.ts +44 -42
  28. package/slides/introducing-konpeki/composition.json +135 -135
  29. package/src/app/App.tsx +87 -19
  30. package/src/components/Canvas.tsx +1 -1
  31. package/src/components/InspectorPanel.tsx +93 -40
  32. package/src/components/WorkspaceChrome.tsx +92 -5
  33. package/src/lib/storage.ts +38 -10
  34. package/src/main.tsx +3 -0
  35. package/src/styles/chrome.css +97 -25
  36. package/src/styles/feedback.css +20 -3
  37. package/src/styles/right-panel.css +28 -27
  38. package/src/styles/shell.css +60 -29
  39. package/.agents/skills/authoring-visuals/SKILL.md +0 -93
@@ -29,13 +29,71 @@ pnpm dev
29
29
  For a production preview, run `pnpm build` then `pnpm preview`. The build produces
30
30
  a static site in `dist` for a root or subdirectory. Use
31
31
  `?example=introducing-konpeki` or `?example=custom-visual` to open a bundled editable
32
- example. Example links do not autosave; open a composition in a file-backed
33
- session to preserve edits.
32
+ example. Each example has an isolated browser-local working copy that survives
33
+ reload. The Browser menu supports JSON import/download, starting blank and
34
+ resetting the example. These actions never replace another example or the normal
35
+ local draft. Invalid stored data remains untouched until an explicit reset.
36
+
37
+ Deploy only the static `dist` output for the public playground, not a file-session
38
+ server. Imported documents stay in that browser; there is no account, cloud sync,
39
+ AI generation or Build/notes handoff in standalone mode. Downloaded JSON can be
40
+ opened in a file-backed session with a coding agent. Browser storage is not a
41
+ backup. Hosting shares bundled examples, not private drafts or an AI service.
42
+ No deployment is automatic. In a remote environment, expose a review server
43
+ through its authenticated preview mechanism, not a loopback address.
44
+
45
+ The source CLI's `preview` chooses the next available port if its default is
46
+ occupied. An explicit `--port <number>` fails rather than silently changing the
47
+ requested port; `--port 0` asks the OS for a free port. `--json` prints one readiness
48
+ record with `type`, `compositionPath` and the exact session-bearing `url` after
49
+ listening. Treat that URL as a capability, not public logging data. This is a
50
+ startup signal, not proof that the browser loaded the right composition.
51
+
52
+ ### GitHub Pages
53
+
54
+ The public playground is hosted at
55
+ [vcfgdev.github.io/konpeki](https://vcfgdev.github.io/konpeki/?example=introducing-konpeki).
56
+ The example query opens the introduction; the root URL opens the ordinary local
57
+ draft. Each visitor's edits stay in their own browser, not in the deployed site.
58
+
59
+ `.github/workflows/pages.yml` deploys only when explicitly dispatched on `main`:
34
60
 
35
- Hosting shares bundled examples, not private drafts, arbitrary documents or an
36
- AI service. No deployment is automatic. In a remote environment, expose the
37
- server through its authenticated preview mechanism; a local address is not a
38
- shareable URL.
61
+ ```sh
62
+ gh workflow run pages.yml --repo vcfgdev/konpeki --ref main
63
+ ```
64
+
65
+ The workflow uses the pinned mise/pnpm toolchain, runs typecheck and tests, builds
66
+ with the Pages base path, and uploads only `dist`. The deployment job publishes
67
+ that artifact to the `github-pages` environment. Repository **Settings → Pages →
68
+ Source** must be **GitHub Actions**. Ordinary pushes run CI but do not redeploy;
69
+ package releases remain separate. Inspect the public example after deployment,
70
+ including reload, fonts, editing, JSON download and Present.
71
+
72
+ Existing example working copies survive deployments. Download any edits before
73
+ choosing **Browser → Reset example** to load a newly published example.
74
+
75
+ ## Skill and plugin packaging
76
+
77
+ `skills/konpeki/` is the canonical portable skill. The repo's
78
+ `.agents/skills/konpeki` symlink enables local discovery without a second
79
+ copy. The root Agent Plugins `plugin.json` and repo marketplace expose the same
80
+ skill to compatible Codex clients; no MCP, hook or hosted AI is involved. Review
81
+ native-client installation separately from the npm smoke test.
82
+
83
+ The skill dispatches `init` (open only) and `generate` (create/revise, including
84
+ implicit setup). These are agent modes, not CLI subcommands. Its portable
85
+ `scripts/prepare-document.mjs` validates through the resolved CLI and exclusively
86
+ creates a blank file, or validates an existing file without rewriting it. The
87
+ bundled `assets/blank.json` matches `initialDraft(true)` in
88
+ `composition/document.ts`; onboarding tests enforce that contract. Keep scripts
89
+ and assets when copying the skill. No TypeScript import from `node_modules` is
90
+ needed, so a copied skill also supports the existing published runtime.
91
+
92
+ Its `scripts/ensure-runtime.mjs` pins the release runtime. It performs
93
+ no installation without `--install`, and never updates project dependencies.
94
+ When preparing a new release, deliberately update its pin and the plugin version
95
+ together with the package version after testing the target runtime. The current
96
+ pin is 0.3.0; local CLI/playground changes do not republish that npm version.
39
97
 
40
98
  ## Implementation reference
41
99
 
@@ -78,7 +136,8 @@ trusted local React source; never use it on untrusted JSX. It rejects unsupporte
78
136
  SVG elements rather than silently flattening them. Review converted typography
79
137
  and geometry in the browser; conversion is not a fidelity guarantee.
80
138
  The bundled `?example=react-page-migration` preview uses the same format.
81
- Example previews do not autosave; use a file-backed session to save edits.
139
+ Its working copy autosaves in that browser. Download JSON or use a file-backed
140
+ session to retain edits outside browser storage.
82
141
 
83
142
  ## Package contents
84
143
 
@@ -103,7 +162,7 @@ rebuilding during publication. Packing locally does not publish anything.
103
162
 
104
163
  ## Tag releases
105
164
 
106
- `.github/workflows/publish.yml` stages releases on bare version tags such as `0.1.1`
165
+ `.github/workflows/publish.yml` stages releases on bare version tags such as `0.2.1`
107
166
  (no `v` prefix). The tag must equal `package.json`'s version.
108
167
  The workflow installs the mise toolchain and frozen dependencies, runs typecheck,
109
168
  tests, build and package checks, then installs a tarball in an isolated directory
@@ -130,11 +189,12 @@ After updating the package version, completing release checks and pushing the
130
189
  release commit, explicitly create and push its matching tag:
131
190
 
132
191
  ```sh
133
- git tag 0.1.1
134
- git push origin 0.1.1
192
+ VERSION=0.2.1
193
+ git tag "$VERSION"
194
+ git push origin "$VERSION"
135
195
  ```
136
196
 
137
- Replace `0.1.1` with the new version. `0.1.0` is already published and cannot be
197
+ Replace `0.2.1` with the version in `package.json`. Published versions cannot be
138
198
  republished. Pushing a matching tag submits the tested package to npm's staging
139
199
  area. After the workflow succeeds, review the release in npmjs.com's **Staged
140
200
  Packages** tab and click **Approve**, completing 2FA to publish it. Alternatively,
package/docs/workflow.md CHANGED
@@ -1,9 +1,16 @@
1
1
  # Canvas workflow
2
2
 
3
- Start with the [quickstart](../README.md#start-in-your-coding-agent) and give your
4
- coding agent a brief in its prompt field. Keep the skill with the project:
5
- copying `SKILL.md` alone does not install Konpeki. Skill discovery varies by
6
- agent; there is no universal `/konpeki` command.
3
+ Start with [Use with your agent](../README.md#use-with-your-agent) and give your
4
+ coding agent a brief in its prompt field. Install the complete skill directory;
5
+ its bootstrap script finds or installs the runtime separately. Skill discovery
6
+ varies by agent: Codex CLI/IDE uses `$konpeki`, while the standalone Claude Code
7
+ skill uses `/konpeki`. Other clients may use skill selection or natural language.
8
+ `init` opens a blank or existing file-backed editor without generating; `generate`
9
+ creates or revises a visual, opens its preview and inspects it, with setup implicit.
10
+ A creation brief without a mode selects generate. Follow-up reviews continue the
11
+ same document without repeating a command. These are skill modes, not terminal
12
+ subcommands. **Build it** is an optional revision handoff, not a required first-run
13
+ step; opening the editor does not start a review listener.
7
14
 
8
15
  ## Documents and exports
9
16
 
package/index.html CHANGED
@@ -4,10 +4,26 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <meta name="theme-color" content="#ffffff" />
7
+ <link rel="icon" type="image/png" sizes="128x128" href="/src/assets/konpeki-mark.png" />
7
8
  <meta
8
9
  name="description"
9
- content="An editable canvas for agent-made visuals. Create a single explanation or a whole presentation, then revise it together."
10
+ content="Try Konpeki's editable canvas. Edit an example, create a page, export PNG or download editable JSON. No account needed; changes stay in your browser."
10
11
  />
12
+ <meta property="og:type" content="website" />
13
+ <meta property="og:site_name" content="Konpeki" />
14
+ <meta property="og:title" content="Konpeki — Browser playground" />
15
+ <meta property="og:description" content="Try Konpeki's editable canvas. Edit an example, create a page, export PNG or download editable JSON. No account needed; changes stay in your browser." />
16
+ <meta property="og:url" content="https://vcfgdev.github.io/konpeki/?example=introducing-konpeki" />
17
+ <meta property="og:image" content="https://vcfgdev.github.io/konpeki/og.png" />
18
+ <meta property="og:image:type" content="image/png" />
19
+ <meta property="og:image:width" content="1280" />
20
+ <meta property="og:image:height" content="640" />
21
+ <meta property="og:image:alt" content="Konpeki — Create clear visuals with your coding agent. Blue brush mark and editable canvas illustrations." />
22
+ <meta name="twitter:card" content="summary_large_image" />
23
+ <meta name="twitter:title" content="Konpeki — Browser playground" />
24
+ <meta name="twitter:description" content="Try Konpeki's editable canvas. Edit an example, create a page, export PNG or download editable JSON. No account needed; changes stay in your browser." />
25
+ <meta name="twitter:image" content="https://vcfgdev.github.io/konpeki/og.png" />
26
+ <meta name="twitter:image:alt" content="Konpeki — Create clear visuals with your coding agent. Blue brush mark and editable canvas illustrations." />
11
27
  <title>Konpeki</title>
12
28
  </head>
13
29
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "konpeki",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "A shared editable canvas for agent-made visuals",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -20,13 +20,19 @@
20
20
  "files": [
21
21
  "AGENTS.md",
22
22
  "AUTHORING.md",
23
+ "CONTRIBUTING.md",
24
+ "SECURITY.md",
23
25
  "SETUP.md",
24
26
  "docs/workflow.md",
25
27
  "docs/development.md",
26
- ".agents/skills/authoring-visuals/SKILL.md",
28
+ "plugin.json",
29
+ "skills/konpeki/SKILL.md",
30
+ "skills/konpeki/scripts/*.mjs",
31
+ "skills/konpeki/assets/blank.json",
27
32
  "runtime/konpeki.mjs",
28
33
  "index.html",
29
34
  "vite.config.ts",
35
+ "public/og.png",
30
36
  "src/main.tsx",
31
37
  "src/app/*.tsx",
32
38
  "src/components/*.tsx",
package/plugin.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
+ "name": "konpeki",
4
+ "version": "0.3.0",
5
+ "description": "Create editable visuals with your coding agent.",
6
+ "repository": "https://github.com/vcfgdev/konpeki",
7
+ "license": "Apache-2.0",
8
+ "extensions": {
9
+ "com.openai": {
10
+ "interface": {
11
+ "displayName": "Konpeki",
12
+ "shortDescription": "Create editable visuals with your coding agent",
13
+ "category": "Productivity",
14
+ "defaultPrompt": [
15
+ "Use Konpeki to turn these launch notes into a product announcement.",
16
+ "Use Konpeki to create a cover for this article.",
17
+ "Use Konpeki to turn this data into a chart for a presentation."
18
+ ]
19
+ }
20
+ }
21
+ }
22
+ }
package/public/og.png ADDED
Binary file
@@ -31,6 +31,12 @@ var themeIds = [
31
31
  "graphite"
32
32
  ];
33
33
  var themeModes = ["paper", "night"];
34
+ var typographyIds = [
35
+ "plex-sans",
36
+ "noto-sans",
37
+ "plex-serif",
38
+ "hanken-grotesk"
39
+ ];
34
40
  var vectorElementKinds = [
35
41
  "g",
36
42
  "rect",
@@ -715,8 +721,9 @@ var schema = {
715
721
  authoringMode: enumeration(authoringModes),
716
722
  theme: object({
717
723
  id: enumeration(themeIds),
718
- mode: enumeration(themeModes)
719
- }),
724
+ mode: enumeration(themeModes),
725
+ typography: enumeration(typographyIds)
726
+ }, ["id", "mode"]),
720
727
  slides: array(object({
721
728
  id: text,
722
729
  name: text,
@@ -1321,7 +1328,7 @@ function fileSessionPlugin({ compositionPath, token, onBuildRequest }) {
1321
1328
  var root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
1322
1329
  function usage() {
1323
1330
  console.error(`Usage:
1324
- konpeki preview <composition.json> [--host <host>] [--port <port>]
1331
+ konpeki preview <composition.json> [--host <host>] [--port <port>] [--json]
1325
1332
  konpeki validate <composition.json>
1326
1333
  konpeki wait <composition.json>
1327
1334
  konpeki request <composition.json>
@@ -1337,34 +1344,55 @@ async function preview(input) {
1337
1344
  const token = randomBytes(24).toString("base64url");
1338
1345
  const host = option("--host", "127.0.0.1");
1339
1346
  const port = Number(option("--port", "4318"));
1340
- if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error("Port must be an integer from 1 to 65535.");
1347
+ const json = process.argv.includes("--json");
1348
+ if (!Number.isInteger(port) || port < 0 || port > 65535) throw new Error("Port must be an integer from 0 to 65535 (0 chooses a free port).");
1341
1349
  const server = await createServer({
1342
1350
  root,
1343
1351
  configFile: resolve(root, "vite.config.ts"),
1352
+ logLevel: json ? "silent" : "info",
1344
1353
  server: {
1345
1354
  host,
1346
1355
  port,
1347
- strictPort: true
1356
+ strictPort: process.argv.includes("--port"),
1357
+ fs: { allow: [root, ...[
1358
+ "ibm-plex-sans",
1359
+ "ibm-plex-serif",
1360
+ "noto-sans",
1361
+ "hanken-grotesk"
1362
+ ].map((font) => dirname(fileURLToPath(import.meta.resolve(`@fontsource/${font}/package.json`))))] }
1348
1363
  },
1349
1364
  plugins: [fileSessionPlugin({
1350
1365
  compositionPath,
1351
1366
  token,
1352
1367
  onBuildRequest: ({ request }) => {
1353
1368
  if (!request) return;
1354
- console.log(`\nBuild requested for ${request.compositionPath}`);
1355
- console.log(`Revision: ${request.revision}`);
1356
- console.log(`Instruction: ${request.instruction}`);
1357
- console.log(`Run: konpeki wait ${JSON.stringify(request.compositionPath)}\n`);
1369
+ console.error(`\nBuild requested for ${request.compositionPath}`);
1370
+ console.error(`Revision: ${request.revision}`);
1371
+ console.error(`Instruction: ${request.instruction}`);
1372
+ console.error(`Run: konpeki wait ${JSON.stringify(request.compositionPath)}\n`);
1358
1373
  }
1359
1374
  })]
1360
1375
  });
1361
- await server.listen();
1376
+ try {
1377
+ await server.listen();
1378
+ } catch (error) {
1379
+ await server.close();
1380
+ throw error;
1381
+ }
1362
1382
  const address = server.httpServer?.address();
1363
1383
  const actualPort = address && typeof address === "object" ? address.port : port;
1364
1384
  const displayHost = host === "0.0.0.0" || host === "::" ? "localhost" : host;
1365
- console.log(`Konpeki is editing ${compositionPath}`);
1366
- console.log(`http://${displayHost}:${actualPort}/?session=${encodeURIComponent(token)}`);
1367
- console.log(`Waiting for edits and Build it requests. Press Ctrl+C to stop.`);
1385
+ const url = `http://${displayHost.includes(":") ? `[${displayHost}]` : displayHost}:${actualPort}/?session=${encodeURIComponent(token)}`;
1386
+ if (json) console.log(JSON.stringify({
1387
+ type: "ready",
1388
+ compositionPath,
1389
+ url
1390
+ }));
1391
+ else {
1392
+ console.log(`Konpeki is editing ${compositionPath}`);
1393
+ console.log(url);
1394
+ console.log(`Waiting for edits and Build it requests. Press Ctrl+C to stop.`);
1395
+ }
1368
1396
  }
1369
1397
  async function validate(input) {
1370
1398
  const result = await readCompositionFile(resolve(input));
@@ -0,0 +1,186 @@
1
+ ---
2
+ name: konpeki
3
+ description: Opens the Konpeki editor with init, or generates and revises editable visuals with generate. Use when asked to use Konpeki or create covers, social graphics, product announcements, charts, diagrams, article headers or presentations.
4
+ compatibility: Requires Node.js 24+, npm, a coding agent with file and command access, and a browser. Runtime installation needs network access and the host's approval.
5
+ ---
6
+
7
+ # Konpeki
8
+
9
+ Use one of two modes from the invocation or conversation:
10
+
11
+ - **init [composition.json]**: prepare the runtime and open a blank or existing
12
+ file-backed editor. Stop when it is ready; do not generate artwork or claim to
13
+ be listening for reviews.
14
+ - **generate [brief / materials]**: create or revise the visual, open its preview,
15
+ inspect and repair it, then handle feedback in the same conversation. Prepare
16
+ the runtime automatically; a separate init command is never required.
17
+
18
+ A creation brief without a mode, including “Use Konpeki to…”, means **generate**.
19
+ Bare “Konpeki” with no brief opens the editor as **init**. Follow-up feedback stays
20
+ with the current document; users need not repeat the skill or their materials.
21
+ Use existing chat, attachments, referenced files and canvas intent as inputs.
22
+ Ask only for missing information needed for faithful work, not a repeated brief.
23
+
24
+ Invocation belongs to the host: Codex CLI/IDE uses `$konpeki init` or
25
+ `$konpeki generate …`; a standalone Claude Code skill uses `/konpeki init` or
26
+ `/konpeki generate …`. Other clients may use skill selection or natural language;
27
+ plugin installations may namespace the skill. These are agent workflows, not
28
+ `konpeki init` / `konpeki generate` terminal commands.
29
+
30
+ ## 0. Find the runtime once
31
+
32
+ From the user's chosen workspace, run the script bundled beside this skill:
33
+
34
+ ```sh
35
+ node "<absolute-path-to-this-skill>/scripts/ensure-runtime.mjs"
36
+ ```
37
+
38
+ It reuses a compatible workspace installation, the surrounding Konpeki checkout,
39
+ or a previously installed cache. If none exists, follow the host's permission
40
+ flow, then rerun with `--install`. This installs the pinned npm release in a
41
+ user cache, not in the user's project, and does not change their agent guidance.
42
+ If Node.js 24+ or npm is missing, report that prerequisite and follow the host's
43
+ toolchain setup rules; never claim the runtime is ready when setup failed.
44
+
45
+ The script returns JSON with `root` (runtime resources) and `cli` (the executable
46
+ path). In the instructions below, `<root>` and `<cli>` mean those returned absolute
47
+ paths. Quote paths in shell commands. They may be outside the skill directory;
48
+ do not assume a copied skill contains the runtime. Read resources from `<root>`
49
+ and keep authored documents in the user's workspace, outside the runtime/cache.
50
+
51
+ ## init — open the editor without generating
52
+
53
+ Use the explicit document path, otherwise the document already active in this
54
+ conversation. With neither, use `slides/untitled/composition.json`. If several
55
+ documents are plausible, ask which to open instead of guessing.
56
+
57
+ Run the bundled helper with the resolved CLI and chosen destination:
58
+
59
+ ```sh
60
+ node "<absolute-path-to-this-skill>/scripts/prepare-document.mjs" "<cli>" "<composition.json>"
61
+ ```
62
+
63
+ It creates a validated blank page only when the file is missing. Existing files
64
+ are validated without rewriting them, and unreadable files are left untouched.
65
+ It returns `{ compositionPath, created }`; this means the document is prepared,
66
+ not that its browser is open. Do not replace a failed document with an example.
67
+
68
+ Follow **Open the file-backed preview** below. Verify the intended document loads,
69
+ then return its path and usable editor link. For a new document, verify an empty
70
+ page with editing controls. Stop here until the person asks to generate or revise.
71
+ Do not start an agent listener just because an editor is open.
72
+
73
+ ## generate — interpret the brief
74
+
75
+ Read `<root>/AGENTS.md` and `<root>/AUTHORING.md`. Use the user's existing prompt as the
76
+ brief; do not ask them to repeat it in the canvas. Establish the source facts,
77
+ audience, takeaway and destination. Ask only when missing facts or conflicting
78
+ requirements prevent a faithful result.
79
+
80
+ Continue the document opened by init or the current conversation unless the
81
+ person requests a new one. Before changing an existing document, inspect
82
+ `node "<cli>" request "<composition.json>"`. Claim a `submitted` request with
83
+ `wait` and follow **Review handoff** below. A `working` request may belong to
84
+ another agent; coordinate ownership before resuming, rather than claiming it again.
85
+ With no active request, work from the brief and current composition normally.
86
+
87
+ Use `default` authoring mode unless requested otherwise. Explicit visual
88
+ preferences override taste defaults, not factual fidelity or readability. Use
89
+ the comparison workflow in `<root>/AUTHORING.md`
90
+ only when requested or accepted. A single page is a complete creation; choose
91
+ dimensions for its destination rather than assuming a slide deck.
92
+
93
+ ### Author or revise the composition
94
+
95
+ Read `<root>/composition/README.md`. For a new visual,
96
+ create `slides/<name>/composition.json` unless the user supplies another path.
97
+ Use an unused destination; do not overwrite a different document at that path.
98
+ Save the creative brief in `PROMPT.md` and substantial facts, citations and asset
99
+ provenance in `SOURCE.md`. Separate assumptions from supplied facts. Preserve
100
+ creative requests accurately, but omit private coordination and environment or
101
+ agent metadata; label excerpts and redactions rather than calling them verbatim.
102
+
103
+ For revisions, reread the current file first. Preserve unrelated content,
104
+ component/vector IDs and human-edited geometry. Honor explicit chart or diagram
105
+ choices. Recompose for a new aspect ratio instead of stretching or cropping.
106
+
107
+ Prefer native text and semantic components. When standard drafts cannot express
108
+ the visual, use editable vectors inside their owning component. Reuse
109
+ `<root>/design/README.md` and reference examples as needed.
110
+ Trusted React may generate SVG for conversion, but imported JSX must not execute
111
+ in the canvas or become a second document source.
112
+
113
+ ### Validate, render and repair
114
+
115
+ ```sh
116
+ node "<cli>" validate "<composition.json>"
117
+ ```
118
+
119
+ Follow **Open the file-backed preview** below, then inspect
120
+ every affected page and requested theme at presentation and smaller review sizes
121
+ after fonts load. Check factual fidelity, text bounds, contrast, reading order
122
+ and relationships. Repair consequential issues and inspect fresh renders.
123
+
124
+ Follow `<root>/docs/development.md` verification guidance for
125
+ code changes. Validation alone is not visual review. If a required check cannot
126
+ run, state the limitation; do not claim it passed. Honor requested checkpoints;
127
+ otherwise continue to finished output.
128
+
129
+ ### Deliver and continue revisions
130
+
131
+ Return the composition path, usable preview, reviewed images or requested
132
+ exports, source attribution and verification limitations. Verify exports
133
+ separately; browser images do not prove editable PDF/PPTX or font fidelity.
134
+ Include any generator source while keeping composition JSON authoritative.
135
+
136
+ Accept follow-up chat feedback without requiring another generate invocation.
137
+ Reread the current composition each time, preserve human edits, and repeat the
138
+ render/repair checks. For canvas feedback, the person submits notes with **Build
139
+ it**. A submitted request can be handled by invoking generate again or by an
140
+ agent already waiting for it. Do not treat unsent notes as a submitted request.
141
+
142
+ ## Open the file-backed preview
143
+
144
+ Reuse a known live preview for the same absolute document path; verify it still
145
+ loads that document. Otherwise start:
146
+
147
+ ```sh
148
+ node "<cli>" preview "<composition.json>"
149
+ ```
150
+
151
+ Keep the process alive using the host's supported service mechanism. Open the
152
+ exact printed session URL in the host's in-app browser when available, otherwise
153
+ the person's regular browser. For a remote workspace, use its authenticated
154
+ preview or port forwarding and preserve the session query; a remote loopback URL
155
+ is not a usable handoff. Session URLs grant editing access: do not publish them.
156
+ Verify the intended document actually loads before calling setup complete. If
157
+ browser access is unavailable, return the usable link and state it was not checked.
158
+ Do not substitute the standalone playground: it cannot save to the agent's file
159
+ or submit canvas review requests.
160
+
161
+ ## Review handoff
162
+
163
+ When explicitly waiting for ongoing canvas reviews, run
164
+ `node "<cli>" wait "<composition.json>"` alongside the preview using the host's
165
+ supported long-running tool. Report whether a listener is actually running.
166
+ **Build it** cannot wake an idle agent. Without a listener, use its **Copy prompt**
167
+ handoff or ask the person to resume generate in agent chat; do not claim a permanent
168
+ connection. On receiving a request,
169
+ reread the named file and compare its revision with the request. If it changed,
170
+ reconcile against the latest document instead of applying a stale rewrite.
171
+ Apply every attached note to its named page, component or vector-element ID;
172
+ without notes, respect the selected scope. Preserve unrelated edits and stable
173
+ IDs. A request is already marked working when `wait` returns; it is not deleted.
174
+ Validate, render, inspect and repair the result, then run
175
+ `node "<cli>" finish "<composition.json>" <request-id> --message "Updated and checked"`.
176
+ File changes alone do not resolve notes. If blocked, finish with
177
+ `--status needs-clarification --message "..."` or `--status failed --message "..."`;
178
+ unresolved notes remain for retry. Never mark a partially handled batch done.
179
+ Use `node "<cli>" request "<composition.json>"` to recover an interrupted request and
180
+ verify it is still active before further writes. Cancellation does not stop your
181
+ process: stop work if the request is no longer active. Do not edit the feedback
182
+ sidecar directly. Return to `wait` only when the person requested a continuing loop.
183
+
184
+ See `<root>/docs/workflow.md` for the full handoff contract.
185
+ Never bypass revision checks or mutate hidden browser storage to replace the
186
+ document. Do not publish without permission.
@@ -0,0 +1,23 @@
1
+ {
2
+ "schema": "konpeki-composition/v1",
3
+ "title": "Untitled composition",
4
+ "theme": { "id": "plex", "mode": "paper" },
5
+ "slides": [
6
+ {
7
+ "id": "slide-1",
8
+ "name": "Page 01",
9
+ "canvas": { "width": 1920, "height": 1080 },
10
+ "innerPadding": { "top": 72, "right": 112, "bottom": 0, "left": 112 },
11
+ "pageNumber": { "style": "none", "color": "muted" },
12
+ "audience": "Decision makers",
13
+ "question": "What should the audience understand or decide?",
14
+ "intendedViewingSize": "presentation",
15
+ "contentSlots": [],
16
+ "components": [],
17
+ "groups": [],
18
+ "readingOrder": [],
19
+ "paintOrder": [],
20
+ "relationships": []
21
+ }
22
+ ]
23
+ }
@@ -0,0 +1,69 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { existsSync, mkdirSync, readFileSync } from "node:fs";
3
+ import { createRequire } from "node:module";
4
+ import { homedir } from "node:os";
5
+ import { dirname, join, resolve } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ // A copied skill must resolve a known runtime, not follow repo-relative links.
9
+ const version = "0.3.0";
10
+ const probeDocument = fileURLToPath(new URL("../assets/blank.json", import.meta.url));
11
+ function runtime(root) {
12
+ try {
13
+ const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
14
+ if (pkg.name !== "konpeki" || pkg.version !== version) return;
15
+ const cli = join(root, pkg.bin.konpeki);
16
+ const sourceCLI = join(root, "bin/konpeki.mjs");
17
+ if (!["AGENTS.md", "AUTHORING.md", "composition/README.md", "design/README.md", "docs/workflow.md"]
18
+ .every(path => existsSync(join(root, path)))) return;
19
+ // The development checkout runs TypeScript directly on the pinned Node.
20
+ for (const candidate of new Set([cli, sourceCLI])) {
21
+ if (!existsSync(candidate)) continue;
22
+ const probe = spawnSync(process.execPath, [candidate, "validate", probeDocument], {
23
+ stdio: "ignore",
24
+ timeout: 10_000,
25
+ });
26
+ if (probe.status === 0) return { root, cli: candidate, version };
27
+ }
28
+ } catch {
29
+ // Missing or incompatible installations are not modified.
30
+ }
31
+ }
32
+
33
+ try {
34
+ if (Number(process.versions.node.split(".")[0]) < 24)
35
+ throw new Error("Konpeki needs Node.js 24+. Use your host's approved toolchain setup, then retry.");
36
+ if (process.argv.slice(2).some(arg => arg !== "--install"))
37
+ throw new Error("Usage: node ensure-runtime.mjs [--install]");
38
+ let workspace;
39
+ try {
40
+ workspace = dirname(createRequire(join(process.cwd(), "package.json")).resolve("konpeki/package.json"));
41
+ } catch {}
42
+ const bundled = fileURLToPath(new URL("../../../", import.meta.url));
43
+ const cacheBase = process.platform === "win32"
44
+ ? process.env.LOCALAPPDATA || join(homedir(), "AppData", "Local")
45
+ : process.env.XDG_CACHE_HOME || join(homedir(), ".cache");
46
+ const cache = resolve(cacheBase, "konpeki", version);
47
+ const cachedRoot = join(cache, "node_modules", "konpeki");
48
+ let found = (workspace && runtime(workspace)) || runtime(bundled) || runtime(cachedRoot);
49
+ if (!found) {
50
+ if (!process.argv.includes("--install"))
51
+ throw new Error(`Konpeki ${version} is not installed. After the host approves installation, rerun with --install. This uses a user cache and leaves project dependencies unchanged.`);
52
+ mkdirSync(cache, { recursive: true });
53
+ // Explicit local prefix prevents npm from walking up into an ancestor project.
54
+ // Resolve "." from the absolute cwd; no user path enters Windows shell text.
55
+ const result = spawnSync("npm", ["install", "--prefix=.", "--global=false", "--save-exact", "--no-audit", "--no-fund", `konpeki@${version}`], {
56
+ cwd: cache,
57
+ stdio: ["inherit", 2, 2],
58
+ shell: process.platform === "win32",
59
+ });
60
+ if (result.error || result.status !== 0)
61
+ throw new Error("Konpeki installation failed. Check npm/network access and retry; the runtime is not ready.");
62
+ found = runtime(cachedRoot);
63
+ if (!found) throw new Error("The installed Konpeki runtime is incomplete or incompatible.");
64
+ }
65
+ console.log(JSON.stringify(found));
66
+ } catch (error) {
67
+ console.error(error.message);
68
+ process.exitCode = 1;
69
+ }
@@ -0,0 +1,40 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { dirname, resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ try {
7
+ const [cli, input, ...extra] = process.argv.slice(2);
8
+ if (!cli || !input || extra.length)
9
+ throw new Error("Usage: node prepare-document.mjs <cli> <composition.json>");
10
+ const compositionPath = resolve(input);
11
+ function validate(path) {
12
+ // Use the published JS CLI, not TypeScript imports from node_modules.
13
+ const result = spawnSync(process.execPath, [cli, "validate", path], {
14
+ stdio: ["ignore", 2, 2],
15
+ });
16
+ if (result.error || result.status !== 0)
17
+ throw new Error("Document validation failed; existing files were not changed.");
18
+ }
19
+ let created = false;
20
+ if (!existsSync(compositionPath)) {
21
+ if (existsSync(`${compositionPath}.review.json`))
22
+ throw new Error("Review data exists without its composition. Restore the document or choose a new path.");
23
+ const template = fileURLToPath(new URL("../assets/blank.json", import.meta.url));
24
+ validate(template);
25
+ const blank = readFileSync(template);
26
+ mkdirSync(dirname(compositionPath), { recursive: true });
27
+ try {
28
+ writeFileSync(compositionPath, blank, { flag: "wx" });
29
+ created = true;
30
+ } catch (error) {
31
+ // Another initializer may have created it; reopen, never replace it.
32
+ if (error.code !== "EEXIST") throw error;
33
+ }
34
+ }
35
+ validate(compositionPath);
36
+ console.log(JSON.stringify({ compositionPath, created }));
37
+ } catch (error) {
38
+ console.error(error.message);
39
+ process.exitCode = 1;
40
+ }