@bpmnkit/cli 0.0.27 → 0.0.29
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/dist/commands/generate.js +679 -0
- package/dist/commands/index.js +4 -0
- package/dist/commands/skills.js +32 -11
- package/dist/commands/view.js +373 -0
- package/package.json +4 -4
- package/skills/aikit.md +267 -0
- package/skills/deploy.md +2 -0
- package/skills/design.md +2 -0
- package/skills/implement.md +2 -0
- package/skills/review.md +2 -0
- package/skills/test.md +2 -0
package/dist/commands/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { askGroup } from "./ask.js";
|
|
|
4
4
|
import { getDmnReqsXmlCmd, getDmnXmlCmd, getStartFormCmd, getUserTaskFormCmd, getXmlCmd, renderBpmnCmd, } from "./bpmn.js";
|
|
5
5
|
import { completionGroup } from "./completion.js";
|
|
6
6
|
import { connectorGroup } from "./connector.js";
|
|
7
|
+
import { generateGroup } from "./generate.js";
|
|
7
8
|
import { lintGroup } from "./lint.js";
|
|
8
9
|
import { pluginGroup } from "./plugin.js";
|
|
9
10
|
import { profileGroup } from "./profile.js";
|
|
@@ -14,6 +15,7 @@ import { settingsGroup } from "./settings.js";
|
|
|
14
15
|
import { skillsGroup } from "./skills.js";
|
|
15
16
|
import { storyGroup } from "./story.js";
|
|
16
17
|
import { testGroup } from "./test.js";
|
|
18
|
+
import { viewGroup } from "./view.js";
|
|
17
19
|
import { workerStartCmd } from "./worker-start.js";
|
|
18
20
|
import { workerCmd } from "./worker.js";
|
|
19
21
|
// Inject custom commands into generated groups without modifying generated files.
|
|
@@ -55,6 +57,7 @@ const workerGroup = {
|
|
|
55
57
|
/** Pinned groups shown above the separator in the main TUI menu. */
|
|
56
58
|
export const pinnedGroups = [
|
|
57
59
|
askGroup,
|
|
60
|
+
generateGroup,
|
|
58
61
|
lintGroup,
|
|
59
62
|
proxyGroup,
|
|
60
63
|
reebeGroup,
|
|
@@ -62,6 +65,7 @@ export const pinnedGroups = [
|
|
|
62
65
|
storyGroup,
|
|
63
66
|
settingsGroup,
|
|
64
67
|
testGroup,
|
|
68
|
+
viewGroup,
|
|
65
69
|
workerGroup,
|
|
66
70
|
];
|
|
67
71
|
/** API command groups — shown below the plugin section in the main TUI menu. */
|
package/dist/commands/skills.js
CHANGED
|
@@ -4,7 +4,9 @@ import { fileURLToPath } from "node:url";
|
|
|
4
4
|
/**
|
|
5
5
|
* Install BPMNKit AIKit skills into `.claude/commands/` in the current project.
|
|
6
6
|
* Skills are markdown prompt files that Claude Code executes as slash commands:
|
|
7
|
-
* /implement, /review, /test, /deploy
|
|
7
|
+
* /design, /implement, /review, /test, /deploy
|
|
8
|
+
*
|
|
9
|
+
* Also installs aikit.md into .claude/ as a shared tool reference.
|
|
8
10
|
*/
|
|
9
11
|
const skillsInstallCmd = {
|
|
10
12
|
name: "install",
|
|
@@ -27,23 +29,42 @@ const skillsInstallCmd = {
|
|
|
27
29
|
],
|
|
28
30
|
async run(ctx) {
|
|
29
31
|
const force = ctx.flags.force === true;
|
|
30
|
-
// Locate the bundled skills directory relative to this
|
|
31
|
-
// Installed layout: dist/
|
|
32
|
+
// Locate the bundled skills directory relative to this file.
|
|
33
|
+
// Installed layout: dist/commands/skills.js → ../../skills/<name>.md
|
|
32
34
|
const binDir = fileURLToPath(new URL(".", import.meta.url));
|
|
33
|
-
const skillsSrc = join(binDir, "..", "skills");
|
|
35
|
+
const skillsSrc = join(binDir, "..", "..", "skills");
|
|
34
36
|
if (!existsSync(skillsSrc)) {
|
|
35
37
|
throw new Error(`Bundled skills directory not found at: ${skillsSrc}`);
|
|
36
38
|
}
|
|
37
|
-
const
|
|
38
|
-
if (
|
|
39
|
+
const allFiles = readdirSync(skillsSrc).filter((f) => f.endsWith(".md"));
|
|
40
|
+
if (allFiles.length === 0) {
|
|
39
41
|
throw new Error("No skill files found in bundled skills directory");
|
|
40
42
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
// aikit.md goes to .claude/ as a shared reference; everything else is a slash command
|
|
44
|
+
const REFERENCE_FILE = "aikit.md";
|
|
45
|
+
const skillFiles = allFiles.filter((f) => f !== REFERENCE_FILE);
|
|
46
|
+
const claudeDir = join(process.cwd(), ".claude");
|
|
47
|
+
const commandsDir = join(claudeDir, "commands");
|
|
48
|
+
mkdirSync(commandsDir, { recursive: true });
|
|
43
49
|
let installed = 0;
|
|
44
50
|
let skipped = 0;
|
|
51
|
+
// Install the tool reference to .claude/aikit.md
|
|
52
|
+
const refSrc = join(skillsSrc, REFERENCE_FILE);
|
|
53
|
+
const refDest = join(claudeDir, REFERENCE_FILE);
|
|
54
|
+
if (existsSync(refSrc)) {
|
|
55
|
+
if (existsSync(refDest) && !force) {
|
|
56
|
+
ctx.output.info(` skip ${REFERENCE_FILE} (already exists — use --force to overwrite)`);
|
|
57
|
+
skipped++;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
writeFileSync(refDest, readFileSync(refSrc, "utf8"), "utf8");
|
|
61
|
+
ctx.output.ok(` wrote .claude/${REFERENCE_FILE}`);
|
|
62
|
+
installed++;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Install slash command skills to .claude/commands/
|
|
45
66
|
for (const file of skillFiles) {
|
|
46
|
-
const dest = join(
|
|
67
|
+
const dest = join(commandsDir, file);
|
|
47
68
|
if (existsSync(dest) && !force) {
|
|
48
69
|
ctx.output.info(` skip ${file} (already exists — use --force to overwrite)`);
|
|
49
70
|
skipped++;
|
|
@@ -51,11 +72,11 @@ const skillsInstallCmd = {
|
|
|
51
72
|
}
|
|
52
73
|
const content = readFileSync(join(skillsSrc, file), "utf8");
|
|
53
74
|
writeFileSync(dest, content, "utf8");
|
|
54
|
-
ctx.output.ok(` wrote
|
|
75
|
+
ctx.output.ok(` wrote .claude/commands/${file}`);
|
|
55
76
|
installed++;
|
|
56
77
|
}
|
|
57
78
|
ctx.output.info("");
|
|
58
|
-
ctx.output.ok(`Installed ${installed}
|
|
79
|
+
ctx.output.ok(`Installed ${installed} file(s)${skipped > 0 ? `, skipped ${skipped}` : ""} → .claude/`);
|
|
59
80
|
ctx.output.info("");
|
|
60
81
|
ctx.output.info("Available slash commands in Claude Code:");
|
|
61
82
|
for (const file of skillFiles) {
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { readFile, readdir, stat } from "node:fs/promises";
|
|
3
|
+
import { createServer } from "node:http";
|
|
4
|
+
import { basename, extname, join } from "node:path";
|
|
5
|
+
import { renderDmnAscii, renderFormAscii } from "@bpmnkit/ascii";
|
|
6
|
+
import { Bpmn, exportSvg } from "@bpmnkit/core";
|
|
7
|
+
// ── Utilities ─────────────────────────────────────────────────────────────────
|
|
8
|
+
function openBrowser(url) {
|
|
9
|
+
const [cmd, args] = process.platform === "darwin"
|
|
10
|
+
? ["open", [url]]
|
|
11
|
+
: process.platform === "win32"
|
|
12
|
+
? ["cmd", ["/c", "start", "", url]]
|
|
13
|
+
: ["xdg-open", [url]];
|
|
14
|
+
spawn(cmd, args, { detached: true, stdio: "ignore" }).unref();
|
|
15
|
+
}
|
|
16
|
+
function escapeHtml(s) {
|
|
17
|
+
return s
|
|
18
|
+
.replace(/&/g, "&")
|
|
19
|
+
.replace(/</g, "<")
|
|
20
|
+
.replace(/>/g, ">")
|
|
21
|
+
.replace(/"/g, """);
|
|
22
|
+
}
|
|
23
|
+
/** Expand paths: directories are scanned (top-level) for matching extensions. */
|
|
24
|
+
async function resolvePaths(paths, extensions) {
|
|
25
|
+
const result = [];
|
|
26
|
+
for (const p of paths) {
|
|
27
|
+
const s = await stat(p).catch(() => null);
|
|
28
|
+
if (!s)
|
|
29
|
+
throw new Error(`Path not found: ${p}`);
|
|
30
|
+
if (s.isDirectory()) {
|
|
31
|
+
const entries = await readdir(p, { withFileTypes: true });
|
|
32
|
+
const matched = entries
|
|
33
|
+
.filter((e) => e.isFile() && extensions.includes(extname(e.name).toLowerCase()))
|
|
34
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
35
|
+
.map((e) => join(p, e.name));
|
|
36
|
+
if (matched.length === 0) {
|
|
37
|
+
throw new Error(`No ${extensions.join("/")} files found in directory: ${p}`);
|
|
38
|
+
}
|
|
39
|
+
result.push(...matched);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
result.push(p);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
// ── Panel builders ────────────────────────────────────────────────────────────
|
|
48
|
+
async function buildBpmnPanel(file, theme) {
|
|
49
|
+
const xml = await readFile(file, "utf-8");
|
|
50
|
+
const defs = Bpmn.parse(xml);
|
|
51
|
+
return { label: basename(file), type: "bpmn", content: exportSvg(defs, { theme }) };
|
|
52
|
+
}
|
|
53
|
+
async function buildDmnPanel(file) {
|
|
54
|
+
const xml = await readFile(file, "utf-8");
|
|
55
|
+
return { label: basename(file), type: "dmn", content: renderDmnAscii(xml) };
|
|
56
|
+
}
|
|
57
|
+
async function buildFormPanel(file) {
|
|
58
|
+
const json = await readFile(file, "utf-8");
|
|
59
|
+
return { label: basename(file), type: "form", content: renderFormAscii(json) };
|
|
60
|
+
}
|
|
61
|
+
async function panelFromFile(file, theme) {
|
|
62
|
+
const ext = extname(file).toLowerCase();
|
|
63
|
+
if (ext === ".bpmn")
|
|
64
|
+
return buildBpmnPanel(file, theme);
|
|
65
|
+
if (ext === ".dmn")
|
|
66
|
+
return buildDmnPanel(file);
|
|
67
|
+
if (ext === ".form")
|
|
68
|
+
return buildFormPanel(file);
|
|
69
|
+
throw new Error(`Unsupported file extension: ${file}. Use .bpmn, .dmn, or .form`);
|
|
70
|
+
}
|
|
71
|
+
// ── HTML builder ──────────────────────────────────────────────────────────────
|
|
72
|
+
function buildHtml(panels, theme) {
|
|
73
|
+
const tabs = panels
|
|
74
|
+
.map((p, i) => `<button class="tab${i === 0 ? " active" : ""}" data-idx="${i}" data-type="${p.type}">${escapeHtml(p.label)}</button>`)
|
|
75
|
+
.join("\n ");
|
|
76
|
+
const panelHtml = panels
|
|
77
|
+
.map((p, i) => {
|
|
78
|
+
const cls = `panel${i === 0 ? " active" : ""}`;
|
|
79
|
+
if (p.type === "bpmn") {
|
|
80
|
+
return `<div class="${cls}" data-idx="${i}" data-type="bpmn">${p.content}</div>`;
|
|
81
|
+
}
|
|
82
|
+
return `<div class="${cls}" data-idx="${i}" data-type="${p.type}"><pre>${escapeHtml(p.content)}</pre></div>`;
|
|
83
|
+
})
|
|
84
|
+
.join("\n ");
|
|
85
|
+
return `<!DOCTYPE html>
|
|
86
|
+
<html lang="en" data-theme="${theme}">
|
|
87
|
+
<head>
|
|
88
|
+
<meta charset="UTF-8">
|
|
89
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
90
|
+
<title>BPMN Kit Viewer</title>
|
|
91
|
+
<style>
|
|
92
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0 }
|
|
93
|
+
:root {
|
|
94
|
+
--bg: #f4f4f8; --surface: #ffffff; --border: #d0d0e8;
|
|
95
|
+
--fg: #1a1a2e; --fg-muted: #6666a0; --accent: #1a56db;
|
|
96
|
+
--mono: ui-monospace, "Cascadia Code", "JetBrains Mono", monospace;
|
|
97
|
+
}
|
|
98
|
+
[data-theme="dark"] {
|
|
99
|
+
--bg: #0d0d16; --surface: #161626; --border: #2a2a42;
|
|
100
|
+
--fg: #cdd6f4; --fg-muted: #8888a8; --accent: #6b9df7;
|
|
101
|
+
}
|
|
102
|
+
body {
|
|
103
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
104
|
+
background: var(--bg);
|
|
105
|
+
color: var(--fg);
|
|
106
|
+
height: 100vh;
|
|
107
|
+
display: flex;
|
|
108
|
+
flex-direction: column;
|
|
109
|
+
}
|
|
110
|
+
header {
|
|
111
|
+
background: var(--surface);
|
|
112
|
+
border-bottom: 1px solid var(--border);
|
|
113
|
+
padding: 0 16px;
|
|
114
|
+
display: flex;
|
|
115
|
+
align-items: center;
|
|
116
|
+
gap: 2px;
|
|
117
|
+
overflow-x: auto;
|
|
118
|
+
flex-shrink: 0;
|
|
119
|
+
height: 44px;
|
|
120
|
+
}
|
|
121
|
+
.brand {
|
|
122
|
+
font-size: 11px;
|
|
123
|
+
font-weight: 700;
|
|
124
|
+
letter-spacing: 0.08em;
|
|
125
|
+
text-transform: uppercase;
|
|
126
|
+
color: var(--fg-muted);
|
|
127
|
+
margin-right: 12px;
|
|
128
|
+
white-space: nowrap;
|
|
129
|
+
}
|
|
130
|
+
.tab {
|
|
131
|
+
padding: 6px 14px;
|
|
132
|
+
border: none;
|
|
133
|
+
border-bottom: 2px solid transparent;
|
|
134
|
+
background: none;
|
|
135
|
+
color: var(--fg-muted);
|
|
136
|
+
font: inherit;
|
|
137
|
+
font-size: 13px;
|
|
138
|
+
cursor: pointer;
|
|
139
|
+
border-radius: 4px 4px 0 0;
|
|
140
|
+
white-space: nowrap;
|
|
141
|
+
transition: color 0.1s;
|
|
142
|
+
}
|
|
143
|
+
.tab.active { color: var(--accent); border-bottom-color: var(--accent); font-weight: 500; }
|
|
144
|
+
.tab:hover:not(.active) { color: var(--fg); }
|
|
145
|
+
.tab-type {
|
|
146
|
+
font-size: 9px;
|
|
147
|
+
font-weight: 700;
|
|
148
|
+
letter-spacing: 0.06em;
|
|
149
|
+
text-transform: uppercase;
|
|
150
|
+
opacity: 0.6;
|
|
151
|
+
margin-left: 5px;
|
|
152
|
+
vertical-align: middle;
|
|
153
|
+
}
|
|
154
|
+
main {
|
|
155
|
+
flex: 1;
|
|
156
|
+
overflow: auto;
|
|
157
|
+
padding: 24px;
|
|
158
|
+
display: flex;
|
|
159
|
+
justify-content: center;
|
|
160
|
+
align-items: flex-start;
|
|
161
|
+
}
|
|
162
|
+
.panel { display: none; width: 100%; }
|
|
163
|
+
.panel.active { display: block; }
|
|
164
|
+
.panel[data-type="bpmn"] svg {
|
|
165
|
+
max-width: 100%;
|
|
166
|
+
height: auto;
|
|
167
|
+
border-radius: 8px;
|
|
168
|
+
box-shadow: 0 2px 16px rgba(0,0,0,0.1);
|
|
169
|
+
}
|
|
170
|
+
.panel[data-type="dmn"] pre,
|
|
171
|
+
.panel[data-type="form"] pre {
|
|
172
|
+
font-family: var(--mono);
|
|
173
|
+
font-size: 13px;
|
|
174
|
+
line-height: 1.5;
|
|
175
|
+
background: var(--surface);
|
|
176
|
+
border: 1px solid var(--border);
|
|
177
|
+
border-radius: 8px;
|
|
178
|
+
padding: 20px 24px;
|
|
179
|
+
overflow-x: auto;
|
|
180
|
+
white-space: pre;
|
|
181
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
|
|
182
|
+
}
|
|
183
|
+
footer {
|
|
184
|
+
background: var(--surface);
|
|
185
|
+
border-top: 1px solid var(--border);
|
|
186
|
+
padding: 6px 16px;
|
|
187
|
+
font-size: 11px;
|
|
188
|
+
color: var(--fg-muted);
|
|
189
|
+
flex-shrink: 0;
|
|
190
|
+
}
|
|
191
|
+
kbd {
|
|
192
|
+
background: var(--border);
|
|
193
|
+
border-radius: 3px;
|
|
194
|
+
padding: 1px 4px;
|
|
195
|
+
font-family: inherit;
|
|
196
|
+
}
|
|
197
|
+
</style>
|
|
198
|
+
</head>
|
|
199
|
+
<body>
|
|
200
|
+
<header>
|
|
201
|
+
<span class="brand">BPMN Kit</span>
|
|
202
|
+
${tabs}
|
|
203
|
+
</header>
|
|
204
|
+
<main>
|
|
205
|
+
${panelHtml}
|
|
206
|
+
</main>
|
|
207
|
+
<footer>
|
|
208
|
+
Use <kbd>Ctrl+Scroll</kbd> or browser zoom to zoom —
|
|
209
|
+
Press <kbd>Ctrl+C</kbd> in terminal to stop the server
|
|
210
|
+
</footer>
|
|
211
|
+
<script>
|
|
212
|
+
const tabs = document.querySelectorAll('.tab')
|
|
213
|
+
const panels = document.querySelectorAll('.panel')
|
|
214
|
+
tabs.forEach(tab => {
|
|
215
|
+
tab.addEventListener('click', () => {
|
|
216
|
+
const idx = tab.dataset.idx
|
|
217
|
+
tabs.forEach(t => t.classList.toggle('active', t.dataset.idx === idx))
|
|
218
|
+
panels.forEach(p => p.classList.toggle('active', p.dataset.idx === idx))
|
|
219
|
+
})
|
|
220
|
+
})
|
|
221
|
+
</script>
|
|
222
|
+
</body>
|
|
223
|
+
</html>`;
|
|
224
|
+
}
|
|
225
|
+
// ── Server ────────────────────────────────────────────────────────────────────
|
|
226
|
+
async function serve(panels, port, theme, noOpen, ctx) {
|
|
227
|
+
const html = buildHtml(panels, theme);
|
|
228
|
+
const server = createServer((_req, res) => {
|
|
229
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
230
|
+
res.end(html);
|
|
231
|
+
});
|
|
232
|
+
await new Promise((resolve, reject) => {
|
|
233
|
+
server.listen(port, "127.0.0.1", () => resolve());
|
|
234
|
+
server.on("error", reject);
|
|
235
|
+
});
|
|
236
|
+
const url = `http://localhost:${port}`;
|
|
237
|
+
ctx.output.info(`Serving ${panels.length} file${panels.length === 1 ? "" : "s"} at ${url}`);
|
|
238
|
+
if (!noOpen)
|
|
239
|
+
openBrowser(url);
|
|
240
|
+
ctx.output.info("Press Ctrl+C to stop");
|
|
241
|
+
await new Promise((resolve) => {
|
|
242
|
+
process.once("SIGINT", resolve);
|
|
243
|
+
process.once("SIGTERM", resolve);
|
|
244
|
+
});
|
|
245
|
+
server.close();
|
|
246
|
+
}
|
|
247
|
+
// ── Shared flags ──────────────────────────────────────────────────────────────
|
|
248
|
+
const PORT_FLAG = {
|
|
249
|
+
name: "port",
|
|
250
|
+
description: "Port for the local server (default: 3044)",
|
|
251
|
+
type: "number",
|
|
252
|
+
default: 3044,
|
|
253
|
+
};
|
|
254
|
+
const THEME_FLAG = {
|
|
255
|
+
name: "theme",
|
|
256
|
+
description: "Color theme: light (default) or dark",
|
|
257
|
+
type: "string",
|
|
258
|
+
default: "light",
|
|
259
|
+
enum: ["light", "dark"],
|
|
260
|
+
};
|
|
261
|
+
const NO_OPEN_FLAG = {
|
|
262
|
+
name: "no-open",
|
|
263
|
+
description: "Do not open the browser automatically",
|
|
264
|
+
type: "boolean",
|
|
265
|
+
};
|
|
266
|
+
// ── Commands ──────────────────────────────────────────────────────────────────
|
|
267
|
+
const viewBpmnCmd = {
|
|
268
|
+
name: "bpmn",
|
|
269
|
+
description: "View BPMN files or a folder of .bpmn files in the browser",
|
|
270
|
+
args: [
|
|
271
|
+
{ name: "path", description: "File(s) or folder(s) containing .bpmn files", required: true },
|
|
272
|
+
],
|
|
273
|
+
flags: [PORT_FLAG, THEME_FLAG, NO_OPEN_FLAG],
|
|
274
|
+
examples: [
|
|
275
|
+
{ description: "View a single file", command: "casen view bpmn process.bpmn" },
|
|
276
|
+
{ description: "View all files in a folder", command: "casen view bpmn ./processes/" },
|
|
277
|
+
{ description: "View multiple files", command: "casen view bpmn order.bpmn payment.bpmn" },
|
|
278
|
+
{
|
|
279
|
+
description: "Dark theme on custom port",
|
|
280
|
+
command: "casen view bpmn process.bpmn --theme dark --port 8080",
|
|
281
|
+
},
|
|
282
|
+
],
|
|
283
|
+
async run(ctx) {
|
|
284
|
+
if (ctx.positional.length === 0)
|
|
285
|
+
throw new Error("Provide at least one file or folder path");
|
|
286
|
+
const port = ctx.flags.port ?? 3044;
|
|
287
|
+
const theme = ctx.flags.theme === "dark" ? "dark" : "light";
|
|
288
|
+
const files = await resolvePaths(ctx.positional, [".bpmn"]);
|
|
289
|
+
const panels = await Promise.all(files.map((f) => buildBpmnPanel(f, theme)));
|
|
290
|
+
await serve(panels, port, theme, ctx.flags["no-open"] === true, ctx);
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
const viewDmnCmd = {
|
|
294
|
+
name: "dmn",
|
|
295
|
+
description: "View DMN files or a folder of .dmn files in the browser",
|
|
296
|
+
args: [
|
|
297
|
+
{ name: "path", description: "File(s) or folder(s) containing .dmn files", required: true },
|
|
298
|
+
],
|
|
299
|
+
flags: [PORT_FLAG, THEME_FLAG, NO_OPEN_FLAG],
|
|
300
|
+
examples: [
|
|
301
|
+
{ description: "View a single DMN file", command: "casen view dmn decision.dmn" },
|
|
302
|
+
{ description: "View all DMN files in a folder", command: "casen view dmn ./decisions/" },
|
|
303
|
+
],
|
|
304
|
+
async run(ctx) {
|
|
305
|
+
if (ctx.positional.length === 0)
|
|
306
|
+
throw new Error("Provide at least one file or folder path");
|
|
307
|
+
const port = ctx.flags.port ?? 3044;
|
|
308
|
+
const theme = ctx.flags.theme === "dark" ? "dark" : "light";
|
|
309
|
+
const files = await resolvePaths(ctx.positional, [".dmn"]);
|
|
310
|
+
const panels = await Promise.all(files.map((f) => buildDmnPanel(f)));
|
|
311
|
+
await serve(panels, port, theme, ctx.flags["no-open"] === true, ctx);
|
|
312
|
+
},
|
|
313
|
+
};
|
|
314
|
+
const viewFormCmd = {
|
|
315
|
+
name: "form",
|
|
316
|
+
description: "View Camunda form files or a folder of .form files in the browser",
|
|
317
|
+
args: [
|
|
318
|
+
{ name: "path", description: "File(s) or folder(s) containing .form files", required: true },
|
|
319
|
+
],
|
|
320
|
+
flags: [PORT_FLAG, THEME_FLAG, NO_OPEN_FLAG],
|
|
321
|
+
examples: [
|
|
322
|
+
{ description: "View a single form file", command: "casen view form my-form.form" },
|
|
323
|
+
{ description: "View all forms in a folder", command: "casen view form ./forms/" },
|
|
324
|
+
],
|
|
325
|
+
async run(ctx) {
|
|
326
|
+
if (ctx.positional.length === 0)
|
|
327
|
+
throw new Error("Provide at least one file or folder path");
|
|
328
|
+
const port = ctx.flags.port ?? 3044;
|
|
329
|
+
const theme = ctx.flags.theme === "dark" ? "dark" : "light";
|
|
330
|
+
const files = await resolvePaths(ctx.positional, [".form"]);
|
|
331
|
+
const panels = await Promise.all(files.map((f) => buildFormPanel(f)));
|
|
332
|
+
await serve(panels, port, theme, ctx.flags["no-open"] === true, ctx);
|
|
333
|
+
},
|
|
334
|
+
};
|
|
335
|
+
const viewOpenCmd = {
|
|
336
|
+
name: "open",
|
|
337
|
+
description: "View any mix of .bpmn, .dmn, and .form files or folders in the browser",
|
|
338
|
+
args: [
|
|
339
|
+
{
|
|
340
|
+
name: "path",
|
|
341
|
+
description: "File(s) or folder(s) — .bpmn, .dmn, and .form auto-detected",
|
|
342
|
+
required: true,
|
|
343
|
+
},
|
|
344
|
+
],
|
|
345
|
+
flags: [PORT_FLAG, THEME_FLAG, NO_OPEN_FLAG],
|
|
346
|
+
examples: [
|
|
347
|
+
{
|
|
348
|
+
description: "Open any supported file",
|
|
349
|
+
command: "casen view open process.bpmn decision.dmn",
|
|
350
|
+
},
|
|
351
|
+
{ description: "Open a folder (all supported types)", command: "casen view open ./project/" },
|
|
352
|
+
{
|
|
353
|
+
description: "Mix files and folders",
|
|
354
|
+
command: "casen view open ./processes/ extra.dmn review.form",
|
|
355
|
+
},
|
|
356
|
+
],
|
|
357
|
+
async run(ctx) {
|
|
358
|
+
if (ctx.positional.length === 0)
|
|
359
|
+
throw new Error("Provide at least one file or folder path");
|
|
360
|
+
const port = ctx.flags.port ?? 3044;
|
|
361
|
+
const theme = ctx.flags.theme === "dark" ? "dark" : "light";
|
|
362
|
+
const files = await resolvePaths(ctx.positional, [".bpmn", ".dmn", ".form"]);
|
|
363
|
+
const panels = await Promise.all(files.map((f) => panelFromFile(f, theme)));
|
|
364
|
+
await serve(panels, port, theme, ctx.flags["no-open"] === true, ctx);
|
|
365
|
+
},
|
|
366
|
+
};
|
|
367
|
+
// ── Group ─────────────────────────────────────────────────────────────────────
|
|
368
|
+
export const viewGroup = {
|
|
369
|
+
name: "view",
|
|
370
|
+
description: "View BPMN, DMN, and form files in the browser via a local server",
|
|
371
|
+
commands: [viewOpenCmd, viewBpmnCmd, viewDmnCmd, viewFormCmd],
|
|
372
|
+
};
|
|
373
|
+
//# sourceMappingURL=view.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bpmnkit/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.29",
|
|
4
4
|
"description": "Command-line interface for Camunda 8 — deploy, manage, and monitor processes from the terminal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -16,13 +16,13 @@
|
|
|
16
16
|
"node": ">=20"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@bpmnkit/api": "0.0.18",
|
|
20
19
|
"@bpmnkit/ascii": "0.0.22",
|
|
21
20
|
"@bpmnkit/connector-gen": "0.0.13",
|
|
22
21
|
"@bpmnkit/core": "0.0.22",
|
|
22
|
+
"@bpmnkit/api": "0.0.18",
|
|
23
23
|
"@bpmnkit/engine": "0.1.22",
|
|
24
|
-
"@bpmnkit/
|
|
25
|
-
"@bpmnkit/
|
|
24
|
+
"@bpmnkit/proxy": "0.0.25",
|
|
25
|
+
"@bpmnkit/profiles": "0.0.16"
|
|
26
26
|
},
|
|
27
27
|
"publishConfig": {
|
|
28
28
|
"access": "public"
|