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.
- package/AGENTS.md +1 -1
- package/CONTRIBUTING.md +36 -0
- package/README.md +54 -18
- package/SECURITY.md +22 -0
- package/SETUP.md +112 -51
- package/composition/README.md +16 -3
- package/composition/compile.ts +1 -1
- package/composition/schema.json +9 -0
- package/composition/schema.ts +3 -1
- package/composition/types.ts +3 -1
- package/design/themes/README.md +25 -7
- package/design/themes/index.ts +15 -4
- package/docs/development.md +71 -11
- package/docs/workflow.md +11 -4
- package/index.html +17 -1
- package/package.json +8 -2
- package/plugin.json +22 -0
- package/public/og.png +0 -0
- package/runtime/konpeki.mjs +41 -13
- package/skills/konpeki/SKILL.md +186 -0
- package/skills/konpeki/assets/blank.json +23 -0
- package/skills/konpeki/scripts/ensure-runtime.mjs +69 -0
- package/skills/konpeki/scripts/prepare-document.mjs +40 -0
- package/slides/introducing-konpeki/PROMPT.md +21 -0
- package/slides/introducing-konpeki/README.md +52 -30
- package/slides/introducing-konpeki/SOURCE.md +16 -10
- package/slides/introducing-konpeki/author.ts +44 -42
- package/slides/introducing-konpeki/composition.json +135 -135
- package/src/app/App.tsx +87 -19
- package/src/components/Canvas.tsx +1 -1
- package/src/components/InspectorPanel.tsx +93 -40
- package/src/components/WorkspaceChrome.tsx +92 -5
- package/src/lib/storage.ts +38 -10
- package/src/main.tsx +3 -0
- package/src/styles/chrome.css +97 -25
- package/src/styles/feedback.css +20 -3
- package/src/styles/right-panel.css +28 -27
- package/src/styles/shell.css +60 -29
- package/.agents/skills/authoring-visuals/SKILL.md +0 -93
package/docs/development.md
CHANGED
|
@@ -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.
|
|
33
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
134
|
-
git
|
|
192
|
+
VERSION=0.2.1
|
|
193
|
+
git tag "$VERSION"
|
|
194
|
+
git push origin "$VERSION"
|
|
135
195
|
```
|
|
136
196
|
|
|
137
|
-
Replace `0.
|
|
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
|
|
4
|
-
coding agent a brief in its prompt field.
|
|
5
|
-
|
|
6
|
-
agent
|
|
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="
|
|
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.
|
|
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
|
-
".
|
|
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
|
package/runtime/konpeki.mjs
CHANGED
|
@@ -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
|
-
|
|
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:
|
|
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.
|
|
1355
|
-
console.
|
|
1356
|
-
console.
|
|
1357
|
-
console.
|
|
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
|
-
|
|
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
|
-
|
|
1366
|
-
console.log(
|
|
1367
|
-
|
|
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
|
+
}
|