@bpmnkit/cli 0.0.36 → 0.0.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/commands/connector.js +82 -0
- package/dist/commands/deploy.js +70 -0
- package/dist/commands/index.js +8 -0
- package/dist/commands/lint.js +36 -6
- package/dist/commands/pattern.js +55 -0
- package/dist/commands/plan.js +70 -0
- package/dist/commands/skills.js +6 -2
- package/dist/commands/synth.js +103 -0
- package/package.json +10 -8
- package/skills/aikit.md +34 -255
- package/skills/deploy.md +23 -20
- package/skills/implement.md +25 -59
- package/skills/review.md +25 -19
- package/skills/test.md +20 -22
- package/skills/design.md +0 -87
package/README.md
CHANGED
|
@@ -103,10 +103,12 @@ casen instances list --state active
|
|
|
103
103
|
| [`@bpmnkit/plugins`](https://www.npmjs.com/package/@bpmnkit/plugins) | 22 composable canvas plugins |
|
|
104
104
|
| [`@bpmnkit/api`](https://www.npmjs.com/package/@bpmnkit/api) | Camunda 8 REST API TypeScript client |
|
|
105
105
|
| [`@bpmnkit/ascii`](https://www.npmjs.com/package/@bpmnkit/ascii) | Render BPMN diagrams as Unicode ASCII art |
|
|
106
|
+
| [`@bpmnkit/docspack`](https://www.npmjs.com/package/@bpmnkit/docspack) | BPMN Kit docs as an offline docspack package for AI agents |
|
|
106
107
|
| [`@bpmnkit/ui`](https://www.npmjs.com/package/@bpmnkit/ui) | Shared design tokens and UI components |
|
|
107
108
|
| [`@bpmnkit/profiles`](https://www.npmjs.com/package/@bpmnkit/profiles) | Shared auth, profile storage, and client factories for CLI & proxy |
|
|
108
109
|
| [`@bpmnkit/operate`](https://www.npmjs.com/package/@bpmnkit/operate) | Monitoring & operations frontend for Camunda clusters |
|
|
109
110
|
| [`@bpmnkit/connector-gen`](https://www.npmjs.com/package/@bpmnkit/connector-gen) | Generate connector templates from OpenAPI specs |
|
|
111
|
+
| [`@bpmnkit/connectors`](https://www.npmjs.com/package/@bpmnkit/connectors) | Camunda 8 OOTB connector catalog and deterministic template application |
|
|
110
112
|
| [`@bpmnkit/proxy`](https://www.npmjs.com/package/@bpmnkit/proxy) | Local AI bridge and Camunda API proxy server |
|
|
111
113
|
| [`@bpmnkit/patterns`](https://www.npmjs.com/package/@bpmnkit/patterns) | Domain process patterns for BPMNKit AIKit |
|
|
112
114
|
| [`@bpmnkit/reebe-wasm`](https://www.npmjs.com/package/@bpmnkit/reebe-wasm) | WebAssembly BPMN engine for browser simulation |
|
|
@@ -1,5 +1,84 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
|
+
import { getTemplate, listConnectors, searchConnectors } from "@bpmnkit/connectors";
|
|
4
|
+
const searchCmd = {
|
|
5
|
+
name: "search",
|
|
6
|
+
description: "Search the bundled Camunda 8 out-of-the-box connector catalog (Slack, HTTP, AWS, AI Agent, …)",
|
|
7
|
+
args: [
|
|
8
|
+
{ name: "query", description: 'Search terms, e.g. "slack" or "send email"', required: true },
|
|
9
|
+
],
|
|
10
|
+
examples: [{ description: "Find the Slack connector", command: "casen connector search slack" }],
|
|
11
|
+
async run(ctx) {
|
|
12
|
+
const query = ctx.positional.join(" ");
|
|
13
|
+
const results = searchConnectors(query);
|
|
14
|
+
if (results.length === 0) {
|
|
15
|
+
ctx.output.info(`No connectors matched "${query}". Try 'casen connector list' to browse all.`);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
ctx.output.printList({ items: results }, [
|
|
19
|
+
{ key: "id", header: "TEMPLATE ID", maxWidth: 48 },
|
|
20
|
+
{ key: "direction", header: "DIRECTION", maxWidth: 18 },
|
|
21
|
+
{ key: "taskType", header: "TASK TYPE", maxWidth: 32 },
|
|
22
|
+
{ key: "description", header: "DESCRIPTION", maxWidth: 50 },
|
|
23
|
+
]);
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
const listCatalogCmd = {
|
|
27
|
+
name: "list",
|
|
28
|
+
description: "List all bundled Camunda 8 out-of-the-box connector templates",
|
|
29
|
+
async run(ctx) {
|
|
30
|
+
ctx.output.printList({ items: listConnectors() }, [
|
|
31
|
+
{ key: "id", header: "TEMPLATE ID", maxWidth: 48 },
|
|
32
|
+
{ key: "direction", header: "DIRECTION", maxWidth: 18 },
|
|
33
|
+
{ key: "taskType", header: "TASK TYPE", maxWidth: 32 },
|
|
34
|
+
]);
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
const showCmd = {
|
|
38
|
+
name: "show",
|
|
39
|
+
description: "Show a connector template's required/optional input keys",
|
|
40
|
+
args: [
|
|
41
|
+
{
|
|
42
|
+
name: "templateId",
|
|
43
|
+
description: "Template id, e.g. io.camunda.connectors.Slack.v1",
|
|
44
|
+
required: true,
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
examples: [
|
|
48
|
+
{
|
|
49
|
+
description: "Show the Slack connector's inputs",
|
|
50
|
+
command: "casen connector show io.camunda.connectors.Slack.v1",
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
async run(ctx) {
|
|
54
|
+
const templateId = ctx.positional[0];
|
|
55
|
+
if (!templateId)
|
|
56
|
+
throw new Error("Missing required argument: <templateId>");
|
|
57
|
+
const summary = searchConnectors(templateId).find((s) => s.id === templateId) ??
|
|
58
|
+
listConnectors().find((s) => s.id === templateId);
|
|
59
|
+
if (!summary || !getTemplate(templateId)) {
|
|
60
|
+
throw new Error(`Unknown connector template "${templateId}". Use 'casen connector search <query>' to find one.`);
|
|
61
|
+
}
|
|
62
|
+
ctx.output.info(`${summary.name} (${summary.id})`);
|
|
63
|
+
if (summary.taskType)
|
|
64
|
+
ctx.output.info(`Task type: ${summary.taskType}`);
|
|
65
|
+
ctx.output.info(`Direction: ${summary.direction}`);
|
|
66
|
+
if (summary.description)
|
|
67
|
+
ctx.output.info(summary.description);
|
|
68
|
+
if (summary.requiredInputs.length > 0) {
|
|
69
|
+
ctx.output.info("\nRequired inputs:");
|
|
70
|
+
for (const i of summary.requiredInputs) {
|
|
71
|
+
ctx.output.info(` ${i.key}${i.isSecret ? " (secret)" : ""} — ${i.label}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (summary.optionalInputs.length > 0) {
|
|
75
|
+
ctx.output.info("\nOptional inputs:");
|
|
76
|
+
for (const i of summary.optionalInputs) {
|
|
77
|
+
ctx.output.info(` ${i.key}${i.isSecret ? " (secret)" : ""} — ${i.label}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
};
|
|
3
82
|
export const connectorGroup = {
|
|
4
83
|
name: "connector",
|
|
5
84
|
description: "Generate Camunda REST connector element templates from OpenAPI specs",
|
|
@@ -167,6 +246,9 @@ export const connectorGroup = {
|
|
|
167
246
|
]);
|
|
168
247
|
},
|
|
169
248
|
},
|
|
249
|
+
searchCmd,
|
|
250
|
+
listCatalogCmd,
|
|
251
|
+
showCmd,
|
|
170
252
|
],
|
|
171
253
|
};
|
|
172
254
|
//# sourceMappingURL=connector.js.map
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { basename, resolve } from "node:path";
|
|
3
|
+
import { getActiveProfile, getAuthHeader } from "@bpmnkit/profiles";
|
|
4
|
+
const ZEEBE_ADDRESS = (process.env.ZEEBE_ADDRESS ?? "http://localhost:26500").replace(/\/$/, "");
|
|
5
|
+
const deployCmd = {
|
|
6
|
+
name: "deploy",
|
|
7
|
+
description: "Deploy a BPMN (or DMN/form) file to local Reebe or the active Camunda 8 profile",
|
|
8
|
+
args: [{ name: "file", description: "Path to the resource file", required: true }],
|
|
9
|
+
flags: [
|
|
10
|
+
{
|
|
11
|
+
name: "target",
|
|
12
|
+
description: 'Deployment target: "local" (Reebe, via ZEEBE_ADDRESS) or "camunda8" (active profile)',
|
|
13
|
+
type: "string",
|
|
14
|
+
default: "local",
|
|
15
|
+
enum: ["local", "camunda8"],
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
examples: [
|
|
19
|
+
{ description: "Deploy to local Reebe", command: "casen deploy deploy order-process.bpmn" },
|
|
20
|
+
{
|
|
21
|
+
description: "Deploy to Camunda 8",
|
|
22
|
+
command: "casen deploy deploy order-process.bpmn --target camunda8",
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
async run(ctx) {
|
|
26
|
+
const filePath = ctx.positional[0];
|
|
27
|
+
if (!filePath)
|
|
28
|
+
throw new Error("Missing required argument: <file>");
|
|
29
|
+
const absPath = resolve(filePath);
|
|
30
|
+
const content = await readFile(absPath, "utf-8").catch(() => {
|
|
31
|
+
throw new Error(`Cannot read file: ${absPath}`);
|
|
32
|
+
});
|
|
33
|
+
const formData = new FormData();
|
|
34
|
+
formData.append("resources[]", new Blob([content], { type: "application/octet-stream" }), basename(absPath));
|
|
35
|
+
const target = typeof ctx.flags.target === "string" ? ctx.flags.target : "local";
|
|
36
|
+
if (target === "camunda8") {
|
|
37
|
+
const profile = getActiveProfile();
|
|
38
|
+
if (!profile?.config.baseUrl) {
|
|
39
|
+
throw new Error("No active Camunda 8 profile. Run: casen profile create");
|
|
40
|
+
}
|
|
41
|
+
const authHeader = await getAuthHeader(profile.config);
|
|
42
|
+
const baseUrl = profile.config.baseUrl.replace(/\/$/, "");
|
|
43
|
+
const res = await fetch(`${baseUrl}/v2/deployments`, {
|
|
44
|
+
method: "POST",
|
|
45
|
+
headers: { authorization: authHeader },
|
|
46
|
+
body: formData,
|
|
47
|
+
});
|
|
48
|
+
if (!res.ok)
|
|
49
|
+
throw new Error(`Camunda 8 deploy failed: ${res.status} ${await res.text()}`);
|
|
50
|
+
ctx.output.print({ success: true, target: "camunda8", result: await res.json() });
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
let res;
|
|
54
|
+
try {
|
|
55
|
+
res = await fetch(`${ZEEBE_ADDRESS}/v2/deployments`, { method: "POST", body: formData });
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
throw new Error(`Cannot reach Reebe at ${ZEEBE_ADDRESS}. Start it with: casen reebe start\n${String(err)}`);
|
|
59
|
+
}
|
|
60
|
+
if (!res.ok)
|
|
61
|
+
throw new Error(`Local deploy failed: ${res.status} ${await res.text()}`);
|
|
62
|
+
ctx.output.print({ success: true, target: "local", result: await res.json() });
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
export const deployGroup = {
|
|
66
|
+
name: "deploy",
|
|
67
|
+
description: "Deploy BPMN/DMN/form resources to local Reebe or Camunda 8",
|
|
68
|
+
commands: [deployCmd],
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=deploy.js.map
|
package/dist/commands/index.js
CHANGED
|
@@ -4,8 +4,11 @@ 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 { deployGroup } from "./deploy.js";
|
|
7
8
|
import { generateGroup } from "./generate.js";
|
|
8
9
|
import { lintGroup } from "./lint.js";
|
|
10
|
+
import { patternGroup } from "./pattern.js";
|
|
11
|
+
import { planGroup } from "./plan.js";
|
|
9
12
|
import { pluginGroup } from "./plugin.js";
|
|
10
13
|
import { profileGroup } from "./profile.js";
|
|
11
14
|
import { proxyGroup } from "./proxy.js";
|
|
@@ -14,6 +17,7 @@ import { computeRelations } from "./relations.js";
|
|
|
14
17
|
import { settingsGroup } from "./settings.js";
|
|
15
18
|
import { skillsGroup } from "./skills.js";
|
|
16
19
|
import { storyGroup } from "./story.js";
|
|
20
|
+
import { synthGroup } from "./synth.js";
|
|
17
21
|
import { testGroup } from "./test.js";
|
|
18
22
|
import { viewGroup } from "./view.js";
|
|
19
23
|
import { workerStartCmd } from "./worker-start.js";
|
|
@@ -57,13 +61,17 @@ const workerGroup = {
|
|
|
57
61
|
/** Pinned groups shown above the separator in the main TUI menu. */
|
|
58
62
|
export const pinnedGroups = [
|
|
59
63
|
askGroup,
|
|
64
|
+
deployGroup,
|
|
60
65
|
generateGroup,
|
|
61
66
|
lintGroup,
|
|
67
|
+
patternGroup,
|
|
68
|
+
planGroup,
|
|
62
69
|
proxyGroup,
|
|
63
70
|
reebeGroup,
|
|
64
71
|
skillsGroup,
|
|
65
72
|
storyGroup,
|
|
66
73
|
settingsGroup,
|
|
74
|
+
synthGroup,
|
|
67
75
|
testGroup,
|
|
68
76
|
viewGroup,
|
|
69
77
|
workerGroup,
|
package/dist/commands/lint.js
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { applyConnectorTemplate } from "@bpmnkit/connectors";
|
|
2
3
|
import { Bpmn, compactify, optimize } from "@bpmnkit/core";
|
|
4
|
+
/** Resolves a connector template's missing required keys via @bpmnkit/connectors, for the `connector/*` lint rule. */
|
|
5
|
+
export function resolveConnectorRequirements(templateId, boundKeys) {
|
|
6
|
+
const values = Object.fromEntries(boundKeys.map((k) => [k, "x"]));
|
|
7
|
+
const result = applyConnectorTemplate(templateId, values);
|
|
8
|
+
// Only "missing-required" problems mean something is actually unset — an "unknown-key"
|
|
9
|
+
// problem here just means a bound key doesn't match propertyKey()'s lookup key (e.g. a
|
|
10
|
+
// property whose `id` differs from its zeebe:input binding name), not a missing value.
|
|
11
|
+
return result.problems
|
|
12
|
+
.filter((p) => p.kind === "missing-required" && p.key !== undefined)
|
|
13
|
+
.map((p) => p.key);
|
|
14
|
+
}
|
|
3
15
|
const SEVERITY_SYMBOL = {
|
|
4
16
|
error: "✖",
|
|
5
17
|
warning: "⚠",
|
|
@@ -16,6 +28,11 @@ const lintCmd = {
|
|
|
16
28
|
description: "Comma-separated categories to run (default: all)",
|
|
17
29
|
type: "string",
|
|
18
30
|
},
|
|
31
|
+
{
|
|
32
|
+
name: "profile",
|
|
33
|
+
description: 'Lint profile. "deploy" runs every category and shows only error-severity findings — the deploy-readiness gate.',
|
|
34
|
+
type: "string",
|
|
35
|
+
},
|
|
19
36
|
{
|
|
20
37
|
name: "format",
|
|
21
38
|
description: "Output format: text (default) or json",
|
|
@@ -27,6 +44,13 @@ const lintCmd = {
|
|
|
27
44
|
type: "boolean",
|
|
28
45
|
},
|
|
29
46
|
],
|
|
47
|
+
examples: [
|
|
48
|
+
{ description: "Lint a file", command: "casen lint lint order-process.bpmn" },
|
|
49
|
+
{
|
|
50
|
+
description: "Deploy-readiness gate (errors only)",
|
|
51
|
+
command: "casen lint lint order-process.bpmn --profile deploy",
|
|
52
|
+
},
|
|
53
|
+
],
|
|
30
54
|
async run(ctx) {
|
|
31
55
|
const filePath = ctx.positional[0];
|
|
32
56
|
if (!filePath)
|
|
@@ -37,8 +61,14 @@ const lintCmd = {
|
|
|
37
61
|
const categories = typeof categoriesFlag === "string" && categoriesFlag.length > 0
|
|
38
62
|
? categoriesFlag.split(",").map((s) => s.trim())
|
|
39
63
|
: undefined;
|
|
40
|
-
const
|
|
41
|
-
const
|
|
64
|
+
const deployProfile = ctx.flags.profile === "deploy";
|
|
65
|
+
const report = optimize(defs, {
|
|
66
|
+
...(categories !== undefined ? { categories } : {}),
|
|
67
|
+
resolveConnectorRequirements,
|
|
68
|
+
});
|
|
69
|
+
const findings = deployProfile
|
|
70
|
+
? report.findings.filter((f) => f.severity === "error")
|
|
71
|
+
: report.findings;
|
|
42
72
|
// --fix: apply all auto-fixable findings and write back
|
|
43
73
|
if (ctx.flags.fix) {
|
|
44
74
|
const fixable = findings.filter((f) => f.applyFix);
|
|
@@ -67,10 +97,10 @@ const lintCmd = {
|
|
|
67
97
|
const elIds = f.elementIds.length > 0 ? ` [${f.elementIds.join(", ")}]` : "";
|
|
68
98
|
ctx.output.info(`${symbol} [${f.category}]${elIds} ${f.message}`);
|
|
69
99
|
}
|
|
70
|
-
const
|
|
71
|
-
const errorCount =
|
|
72
|
-
const warnCount =
|
|
73
|
-
const infoCount =
|
|
100
|
+
const total = findings.length;
|
|
101
|
+
const errorCount = findings.filter((f) => f.severity === "error").length;
|
|
102
|
+
const warnCount = findings.filter((f) => f.severity === "warning").length;
|
|
103
|
+
const infoCount = findings.filter((f) => f.severity === "info").length;
|
|
74
104
|
ctx.output.info(`\n${total} finding${total !== 1 ? "s" : ""}: ${errorCount} error${errorCount !== 1 ? "s" : ""}, ${warnCount} warning${warnCount !== 1 ? "s" : ""}, ${infoCount} info`);
|
|
75
105
|
if (errorCount > 0) {
|
|
76
106
|
throw new Error(`Lint failed with ${errorCount} error${errorCount !== 1 ? "s" : ""}`);
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ALL_PATTERNS, findPattern } from "@bpmnkit/patterns";
|
|
2
|
+
const listCmd = {
|
|
3
|
+
name: "list",
|
|
4
|
+
description: "List the built-in domain process patterns",
|
|
5
|
+
examples: [{ description: "List all patterns", command: "casen pattern list" }],
|
|
6
|
+
async run(ctx) {
|
|
7
|
+
ctx.output.print(ALL_PATTERNS.map((p) => ({
|
|
8
|
+
id: p.id,
|
|
9
|
+
name: p.name,
|
|
10
|
+
description: p.description,
|
|
11
|
+
keywords: p.keywords,
|
|
12
|
+
})));
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
const getCmd = {
|
|
16
|
+
name: "get",
|
|
17
|
+
description: "Show a domain pattern's full context: readme, worker specs, and variations",
|
|
18
|
+
args: [
|
|
19
|
+
{
|
|
20
|
+
name: "query",
|
|
21
|
+
description: "Pattern id (e.g. invoice-approval) or a free-text description to match",
|
|
22
|
+
required: true,
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
examples: [
|
|
26
|
+
{ description: "By id", command: "casen pattern get invoice-approval" },
|
|
27
|
+
{ description: "By description", command: 'casen pattern get "employee onboarding"' },
|
|
28
|
+
],
|
|
29
|
+
async run(ctx) {
|
|
30
|
+
const query = ctx.positional.join(" ");
|
|
31
|
+
if (!query)
|
|
32
|
+
throw new Error("Missing required argument: <query>");
|
|
33
|
+
const pattern = findPattern(query);
|
|
34
|
+
if (!pattern) {
|
|
35
|
+
ctx.output.info(`No pattern matched "${query}". Run \`casen pattern list\` to see all patterns.`);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
ctx.output.print({
|
|
39
|
+
id: pattern.id,
|
|
40
|
+
name: pattern.name,
|
|
41
|
+
description: pattern.description,
|
|
42
|
+
readme: pattern.readme,
|
|
43
|
+
workers: pattern.workers,
|
|
44
|
+
variations: pattern.variations,
|
|
45
|
+
note: "`template` is a rough structural reference in an older compact-diagram shape, not a ProcessPlan — use the readme/workers as context when writing your own plan, don't paste the template in as-is.",
|
|
46
|
+
template: pattern.template,
|
|
47
|
+
});
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
export const patternGroup = {
|
|
51
|
+
name: "pattern",
|
|
52
|
+
description: "Look up built-in domain process patterns for AI-driven process generation",
|
|
53
|
+
commands: [listCmd, getCmd],
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=pattern.js.map
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { Bpmn, extractPlan } from "@bpmnkit/core";
|
|
4
|
+
const extractCmd = {
|
|
5
|
+
name: "extract",
|
|
6
|
+
description: "Lift an existing .bpmn file back into ProcessPlan JSON form",
|
|
7
|
+
args: [{ name: "file", description: "Path to the .bpmn file", required: true }],
|
|
8
|
+
flags: [
|
|
9
|
+
{
|
|
10
|
+
name: "output",
|
|
11
|
+
short: "o",
|
|
12
|
+
description: "Output .json file path (default: <file>.plan.json)",
|
|
13
|
+
type: "string",
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
examples: [{ description: "Extract a plan", command: "casen plan extract order-process.bpmn" }],
|
|
17
|
+
async run(ctx) {
|
|
18
|
+
const filePath = ctx.positional[0];
|
|
19
|
+
if (!filePath)
|
|
20
|
+
throw new Error("Missing required argument: <file>");
|
|
21
|
+
const xml = await readFile(resolve(filePath), "utf-8");
|
|
22
|
+
const defs = Bpmn.parse(xml);
|
|
23
|
+
const { plan, unsupported } = extractPlan(defs);
|
|
24
|
+
const outputPath = resolve(typeof ctx.flags.output === "string"
|
|
25
|
+
? ctx.flags.output
|
|
26
|
+
: `${filePath.replace(/\.bpmn$/, "")}.plan.json`);
|
|
27
|
+
await writeFile(outputPath, `${JSON.stringify(plan, null, "\t")}\n`, "utf-8");
|
|
28
|
+
ctx.output.ok(`Wrote ${outputPath}`);
|
|
29
|
+
if (unsupported.length > 0) {
|
|
30
|
+
ctx.output.info(`\n${unsupported.length} element(s) could not be lifted — left out of the plan, not guessed at:`);
|
|
31
|
+
for (const u of unsupported)
|
|
32
|
+
ctx.output.info(` [${u.id}] (${u.type}) ${u.reason}`);
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
export const PLAN_SCHEMA_SUMMARY = `ProcessPlan (version 1)
|
|
37
|
+
{
|
|
38
|
+
"version": 1,
|
|
39
|
+
"process": { "id": string, "name"?: string, "versionTag"?: string },
|
|
40
|
+
"inputs"?: [{ "name": string, "type": string, "required"?: boolean, "description"?: string }],
|
|
41
|
+
"steps": PlanStep[],
|
|
42
|
+
"tests"?: PlanScenario[]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
PlanStep.kind: "start" | "connector" | "serviceTask" | "userTask" | "businessRuleTask" |
|
|
46
|
+
"scriptTask" | "sendTask" | "receiveTask" | "callActivity" | "aiAgent" | "gateway" |
|
|
47
|
+
"subProcess" | "wait" | "end" | "raw"
|
|
48
|
+
|
|
49
|
+
Every step: { id?, name?, documentation?, errorBoundary?: { errorCode, steps }, timerBoundary?: {...} }
|
|
50
|
+
FEEL convention: a leading "=" makes a string a FEEL expression; without it, the value is literal.
|
|
51
|
+
|
|
52
|
+
steps[0] must be kind "start". "gateway" steps carry { gatewayType, branches: [{ condition?, default?, steps }] }.
|
|
53
|
+
"connector"/"aiAgent" tool steps reference a bundled Camunda template via { template, values } —
|
|
54
|
+
see \`casen connector search <query>\` / \`casen connector show <templateId>\` for available templates
|
|
55
|
+
and their required/optional input keys.
|
|
56
|
+
|
|
57
|
+
Full type definitions: packages/core/src/plan/types.ts in the bpmnkit monorepo.`;
|
|
58
|
+
const schemaCmd = {
|
|
59
|
+
name: "schema",
|
|
60
|
+
description: "Print the ProcessPlan format reference",
|
|
61
|
+
async run(ctx) {
|
|
62
|
+
ctx.output.info(PLAN_SCHEMA_SUMMARY);
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
export const planGroup = {
|
|
66
|
+
name: "plan",
|
|
67
|
+
description: "ProcessPlan authoring — extract an existing process, or print the plan format reference",
|
|
68
|
+
commands: [extractCmd, schemaCmd],
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=plan.js.map
|
package/dist/commands/skills.js
CHANGED
|
@@ -4,7 +4,8 @@ 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
|
-
* /
|
|
7
|
+
* /implement, /review, /test, /deploy
|
|
8
|
+
* Lightweight, CLI-only versions of the full plugin's skill set — see aikit.md.
|
|
8
9
|
*
|
|
9
10
|
* Also installs aikit.md into .claude/ as a shared tool reference.
|
|
10
11
|
*/
|
|
@@ -84,7 +85,10 @@ const skillsInstallCmd = {
|
|
|
84
85
|
ctx.output.info(` /${name}`);
|
|
85
86
|
}
|
|
86
87
|
ctx.output.info("");
|
|
87
|
-
ctx.output.info("
|
|
88
|
+
ctx.output.info("These commands drive `casen` directly — no MCP server or proxy needed. " +
|
|
89
|
+
"For the full skill set (implement/extend/agent/connect/review/test/deploy + generated " +
|
|
90
|
+
"reference docs), install the Claude Code plugin instead: " +
|
|
91
|
+
"/plugin marketplace add github:bpmnkit/monorepo && /plugin install bpmnkit");
|
|
88
92
|
},
|
|
89
93
|
};
|
|
90
94
|
export const skillsGroup = {
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { applyConnectorTemplate } from "@bpmnkit/connectors";
|
|
4
|
+
import { Bpmn, compilePlan, mergePlan, slugify, uniqueId } from "@bpmnkit/core";
|
|
5
|
+
async function readPlan(path) {
|
|
6
|
+
const text = await readFile(resolve(path), "utf-8");
|
|
7
|
+
return JSON.parse(text);
|
|
8
|
+
}
|
|
9
|
+
/** Convert plan-embedded test scenarios to the `.bpmn.tests.json` shape consumed by `casen test`. */
|
|
10
|
+
export function toScenarioSidecar(tests) {
|
|
11
|
+
const taken = new Set();
|
|
12
|
+
return tests.map((t) => {
|
|
13
|
+
const id = uniqueId(slugify(t.name), taken);
|
|
14
|
+
const mocks = Object.fromEntries(Object.entries(t.mocks ?? {}).map(([jobType, mock]) => [
|
|
15
|
+
jobType,
|
|
16
|
+
"error" in mock
|
|
17
|
+
? {
|
|
18
|
+
error: mock.error.message
|
|
19
|
+
? `${mock.error.code}: ${mock.error.message}`
|
|
20
|
+
: mock.error.code,
|
|
21
|
+
}
|
|
22
|
+
: { outputs: mock.outputs },
|
|
23
|
+
]));
|
|
24
|
+
return { id, name: t.name, inputs: t.inputs, mocks, expect: t.expect };
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
const synthCmd = {
|
|
28
|
+
name: "synth",
|
|
29
|
+
description: "Compile a ProcessPlan JSON file into deployable, laid-out BPMN XML",
|
|
30
|
+
args: [{ name: "plan", description: "Path to the ProcessPlan JSON file", required: true }],
|
|
31
|
+
flags: [
|
|
32
|
+
{
|
|
33
|
+
name: "output",
|
|
34
|
+
short: "o",
|
|
35
|
+
description: "Output .bpmn file path (default: <plan>.bpmn)",
|
|
36
|
+
type: "string",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "merge",
|
|
40
|
+
description: "Merge into an existing .bpmn file instead of creating a new one",
|
|
41
|
+
type: "string",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "json",
|
|
45
|
+
description: "Print the result (problems + xml) as JSON instead of writing a file",
|
|
46
|
+
type: "boolean",
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
examples: [
|
|
50
|
+
{ description: "Compile a plan to BPMN", command: "casen synth order-process.plan.json" },
|
|
51
|
+
{
|
|
52
|
+
description: "Extend an existing process",
|
|
53
|
+
command: "casen synth delta.plan.json --merge order-process.bpmn",
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
async run(ctx) {
|
|
57
|
+
const planPath = ctx.positional[0];
|
|
58
|
+
if (!planPath)
|
|
59
|
+
throw new Error("Missing required argument: <plan>");
|
|
60
|
+
const plan = await readPlan(planPath);
|
|
61
|
+
const mergeTarget = typeof ctx.flags.merge === "string" ? ctx.flags.merge : undefined;
|
|
62
|
+
const result = mergeTarget
|
|
63
|
+
? mergePlan(Bpmn.parse(await readFile(resolve(mergeTarget), "utf-8")), plan, {
|
|
64
|
+
resolveConnector: applyConnectorTemplate,
|
|
65
|
+
})
|
|
66
|
+
: compilePlan(plan, { resolveConnector: applyConnectorTemplate });
|
|
67
|
+
if (ctx.flags.json) {
|
|
68
|
+
ctx.output.print(result);
|
|
69
|
+
if (!result.xml)
|
|
70
|
+
process.exitCode = 1;
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (result.problems.length > 0) {
|
|
74
|
+
for (const p of result.problems)
|
|
75
|
+
ctx.output.info(`✖ [${p.path}] ${p.message}`);
|
|
76
|
+
}
|
|
77
|
+
if (!result.xml) {
|
|
78
|
+
throw new Error(`Compilation failed with ${result.problems.length} problem(s) — see above`);
|
|
79
|
+
}
|
|
80
|
+
const outputPath = resolve(typeof ctx.flags.output === "string"
|
|
81
|
+
? ctx.flags.output
|
|
82
|
+
: (mergeTarget ?? planPath.replace(/\.json$/, ".bpmn")));
|
|
83
|
+
await writeFile(outputPath, result.xml, "utf-8");
|
|
84
|
+
if (plan.tests && plan.tests.length > 0) {
|
|
85
|
+
const sidecarPath = `${outputPath}.tests.json`;
|
|
86
|
+
await writeFile(sidecarPath, JSON.stringify(toScenarioSidecar(plan.tests), null, 2), "utf-8");
|
|
87
|
+
ctx.output.info(`Wrote ${sidecarPath} (${plan.tests.length} scenario(s) — run: casen test ${outputPath})`);
|
|
88
|
+
}
|
|
89
|
+
if (result.problems.length > 0) {
|
|
90
|
+
ctx.output.info(`\nWrote ${outputPath} with ${result.problems.length} problem(s) above.`);
|
|
91
|
+
process.exitCode = 1;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
ctx.output.ok(`Wrote ${outputPath}`);
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
export const synthGroup = {
|
|
99
|
+
name: "synth",
|
|
100
|
+
description: "Compile a ProcessPlan into deployable BPMN — the deterministic AI-generation pipeline",
|
|
101
|
+
commands: [synthCmd],
|
|
102
|
+
};
|
|
103
|
+
//# sourceMappingURL=synth.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.37",
|
|
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,15 @@
|
|
|
16
16
|
"node": ">=20"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@bpmnkit/api": "0.0.
|
|
20
|
-
"@bpmnkit/ascii": "0.0.
|
|
21
|
-
"@bpmnkit/connector-gen": "0.0.
|
|
22
|
-
"@bpmnkit/
|
|
23
|
-
"@bpmnkit/
|
|
24
|
-
"@bpmnkit/
|
|
25
|
-
"@bpmnkit/
|
|
19
|
+
"@bpmnkit/api": "0.0.20",
|
|
20
|
+
"@bpmnkit/ascii": "0.0.30",
|
|
21
|
+
"@bpmnkit/connector-gen": "0.0.15",
|
|
22
|
+
"@bpmnkit/connectors": "0.0.2",
|
|
23
|
+
"@bpmnkit/core": "0.1.2",
|
|
24
|
+
"@bpmnkit/engine": "0.1.30",
|
|
25
|
+
"@bpmnkit/patterns": "0.0.5",
|
|
26
|
+
"@bpmnkit/profiles": "0.0.18",
|
|
27
|
+
"@bpmnkit/proxy": "0.0.33"
|
|
26
28
|
},
|
|
27
29
|
"publishConfig": {
|
|
28
30
|
"access": "public"
|
package/skills/aikit.md
CHANGED
|
@@ -1,267 +1,46 @@
|
|
|
1
|
-
# BPMNKit
|
|
1
|
+
# BPMNKit — Reference
|
|
2
2
|
|
|
3
|
-
This file is installed to `.claude/aikit.md` by `casen skills install`.
|
|
4
|
-
The skill files (`/design`, `/implement`, etc.) reference it with `@.claude/aikit.md`.
|
|
3
|
+
This file is installed to `.claude/aikit.md` by `casen skills install`. The skill files (`/implement`, `/review`, `/test`, `/deploy`) reference it with `@.claude/aikit.md`.
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
## MCP server
|
|
9
|
-
|
|
10
|
-
All tools are exposed by the `bpmnkit-aikit` MCP server configured in `.claude/mcp.json`.
|
|
11
|
-
Tool names follow the pattern `mcp__bpmnkit-aikit__<tool_name>`.
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## BPMN tools
|
|
16
|
-
|
|
17
|
-
### `bpmn_create`
|
|
18
|
-
|
|
19
|
-
Generate a new BPMN process from a natural language description.
|
|
20
|
-
|
|
21
|
-
- Automatically loads a matching domain pattern for context before calling the AI.
|
|
22
|
-
- Writes the `.bpmn` file to disk.
|
|
23
|
-
|
|
24
|
-
**Parameters**
|
|
25
|
-
| Name | Required | Description |
|
|
26
|
-
|---|---|---|
|
|
27
|
-
| `description` | yes | Natural language description of the process. Include actors, decision points, and expected outcomes. The richer the description, the better the diagram. |
|
|
28
|
-
| `outputDir` | no | Directory to write the file (default: current working directory). |
|
|
29
|
-
|
|
30
|
-
**Returns** `{ path: string, patternMatched: string | null }`
|
|
31
|
-
|
|
32
|
-
**Good description example:**
|
|
33
|
-
> "Invoice approval process with a clerk review step, automatic approval under €500, manager approval for higher amounts, and email notification on rejection."
|
|
34
|
-
|
|
35
|
-
---
|
|
36
|
-
|
|
37
|
-
### `bpmn_read`
|
|
38
|
-
|
|
39
|
-
Read a BPMN file and return its compact JSON representation.
|
|
40
|
-
|
|
41
|
-
**Parameters**
|
|
42
|
-
| Name | Required | Description |
|
|
43
|
-
|---|---|---|
|
|
44
|
-
| `path` | yes | Path to the `.bpmn` file. |
|
|
45
|
-
|
|
46
|
-
**Returns** Compact JSON with shape:
|
|
47
|
-
```json
|
|
48
|
-
{
|
|
49
|
-
"id": "process-id",
|
|
50
|
-
"processes": [{
|
|
51
|
-
"id": "...", "name": "...",
|
|
52
|
-
"elements": [
|
|
53
|
-
{ "type": "startEvent", "id": "...", "name": "..." },
|
|
54
|
-
{ "type": "serviceTask", "id": "...", "name": "...", "jobType": "com.example:do-thing:1" },
|
|
55
|
-
{ "type": "userTask", "id": "...", "name": "...", "formId": "approve-form" },
|
|
56
|
-
{ "type": "businessRuleTask", "id": "...", "name": "...", "decisionId": "credit-check" },
|
|
57
|
-
{ "type": "exclusiveGateway", "id": "...", "name": "..." },
|
|
58
|
-
{ "type": "endEvent", "id": "...", "name": "..." }
|
|
59
|
-
]
|
|
60
|
-
}]
|
|
61
|
-
}
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
Use `jobType` to identify service tasks for worker scaffolding. Use `formId` / `decisionId` to know which tasks need forms/DMN tables.
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
### `bpmn_update`
|
|
69
|
-
|
|
70
|
-
Update an existing BPMN by describing the change in natural language.
|
|
71
|
-
|
|
72
|
-
**Parameters**
|
|
73
|
-
| Name | Required | Description |
|
|
74
|
-
|---|---|---|
|
|
75
|
-
| `path` | yes | Path to the `.bpmn` file. |
|
|
76
|
-
| `instruction` | yes | What to change, e.g. "Add an error boundary event on the payment task that routes to a manual review lane." |
|
|
77
|
-
|
|
78
|
-
**Returns** `{ path: string, updated: true }`
|
|
79
|
-
|
|
80
|
-
---
|
|
81
|
-
|
|
82
|
-
### `bpmn_validate`
|
|
83
|
-
|
|
84
|
-
Validate a BPMN file using the BPMNKit pattern advisor.
|
|
85
|
-
|
|
86
|
-
**Parameters**
|
|
87
|
-
| Name | Required | Description |
|
|
88
|
-
|---|---|---|
|
|
89
|
-
| `path` | yes | Path to the `.bpmn` file. |
|
|
90
|
-
|
|
91
|
-
**Returns**
|
|
92
|
-
```json
|
|
93
|
-
{
|
|
94
|
-
"summary": { "total": 3, "errors": 1, "warnings": 1, "info": 1, "autoFixable": 2 },
|
|
95
|
-
"findings": [
|
|
96
|
-
{
|
|
97
|
-
"severity": "error" | "warning" | "info",
|
|
98
|
-
"category": "string",
|
|
99
|
-
"message": "string",
|
|
100
|
-
"suggestion": "string",
|
|
101
|
-
"elementIds": ["..."],
|
|
102
|
-
"autoFixable": true
|
|
103
|
-
}
|
|
104
|
-
]
|
|
105
|
-
}
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
Errors block deployment. Warnings and info are advisory.
|
|
109
|
-
|
|
110
|
-
---
|
|
111
|
-
|
|
112
|
-
### `bpmn_deploy`
|
|
113
|
-
|
|
114
|
-
Deploy a BPMN process to a running engine.
|
|
115
|
-
|
|
116
|
-
**Parameters**
|
|
117
|
-
| Name | Required | Description |
|
|
118
|
-
|---|---|---|
|
|
119
|
-
| `path` | yes | Path to the `.bpmn` file. |
|
|
120
|
-
| `target` | yes | `"local"` — local reebe instance (uses `ZEEBE_ADDRESS`). `"camunda8"` — active Camunda 8 profile (set with `casen profile create`). |
|
|
121
|
-
|
|
122
|
-
**Returns** `{ success: true, target: string, result: object }`
|
|
123
|
-
|
|
124
|
-
---
|
|
125
|
-
|
|
126
|
-
### `bpmn_simulate`
|
|
127
|
-
|
|
128
|
-
Structural analysis: validation findings + worker coverage check.
|
|
129
|
-
|
|
130
|
-
> **Note:** Phase 1 only — structural analysis. Full process execution simulation is planned for a future phase.
|
|
5
|
+
These are lightweight, CLI-only slash commands. For the full skill set — `/bpmnkit:implement`, `/bpmnkit:extend`, `/bpmnkit:agent`, `/bpmnkit:connect`, plus generated reference docs (`plan-format.md`, `connectors.md`, `agentic.md`, `feel.md`) — install the Claude Code plugin instead:
|
|
131
6
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
| `path` | yes | Path to the `.bpmn` file. |
|
|
136
|
-
|
|
137
|
-
**Returns**
|
|
138
|
-
```json
|
|
139
|
-
{
|
|
140
|
-
"validation": { "errors": 0, "findings": [] },
|
|
141
|
-
"workerCoverage": {
|
|
142
|
-
"total": 3,
|
|
143
|
-
"covered": 2,
|
|
144
|
-
"missing": ["com.example:send-invoice:1"]
|
|
145
|
-
}
|
|
146
|
-
}
|
|
7
|
+
```sh
|
|
8
|
+
/plugin marketplace add github:bpmnkit/monorepo
|
|
9
|
+
/plugin install bpmnkit
|
|
147
10
|
```
|
|
148
11
|
|
|
149
12
|
---
|
|
150
13
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
Query recent process executions from the local proxy.
|
|
154
|
-
|
|
155
|
-
**Parameters**
|
|
156
|
-
| Name | Required | Description |
|
|
157
|
-
|---|---|---|
|
|
158
|
-
| `processId` | no | Filter by process definition ID. |
|
|
159
|
-
|
|
160
|
-
**Returns** `{ runs: [...] }` — up to 20 recent executions.
|
|
161
|
-
|
|
162
|
-
---
|
|
163
|
-
|
|
164
|
-
## Worker tools
|
|
165
|
-
|
|
166
|
-
### `worker_list`
|
|
167
|
-
|
|
168
|
-
List all available workers: built-in BPMNKit workers and any scaffolded workers found in `./workers/`.
|
|
169
|
-
|
|
170
|
-
**Parameters** none
|
|
171
|
-
|
|
172
|
-
**Returns** `{ workers: [{ jobType, name, description, ... }], total: number }`
|
|
173
|
-
|
|
174
|
-
---
|
|
175
|
-
|
|
176
|
-
### `worker_scaffold`
|
|
177
|
-
|
|
178
|
-
Scaffold a TypeScript worker for a Zeebe job type. Generates `index.ts`, `package.json`, `tsconfig.json`, `README.md` in `./workers/<slug>/`.
|
|
179
|
-
|
|
180
|
-
**Parameters**
|
|
181
|
-
| Name | Required | Description |
|
|
182
|
-
|---|---|---|
|
|
183
|
-
| `jobType` | yes | Zeebe job type string, e.g. `com.example:send-invoice:1`. |
|
|
184
|
-
| `description` | no | What this worker does. |
|
|
185
|
-
| `inputs` | no | Object mapping input variable names to type descriptions, e.g. `{ "invoiceId": "string", "amount": "number" }`. |
|
|
186
|
-
| `outputs` | no | Object mapping output variable names to type descriptions. |
|
|
187
|
-
|
|
188
|
-
**Returns** `{ path: string, files: [...], jobType: string, note: string }`
|
|
189
|
-
|
|
190
|
-
After scaffolding: `cd workers/<slug> && npm install && npm start`. Edit `index.ts` to implement `handle()`.
|
|
191
|
-
|
|
192
|
-
---
|
|
193
|
-
|
|
194
|
-
## Form & DMN tools
|
|
195
|
-
|
|
196
|
-
### `form_create`
|
|
197
|
-
|
|
198
|
-
Generate Camunda form JSON for all `userTask` elements in a BPMN that have a `formId`. Writes one `.form` file per task.
|
|
14
|
+
## The pipeline
|
|
199
15
|
|
|
200
|
-
|
|
201
|
-
| Name | Required | Description |
|
|
202
|
-
|---|---|---|
|
|
203
|
-
| `bpmnPath` | yes | Path to the `.bpmn` file. |
|
|
204
|
-
| `outputDir` | no | Where to write form files (default: same directory as the BPMN). |
|
|
16
|
+
Every process is authored as a `ProcessPlan` JSON file, never hand-written BPMN XML:
|
|
205
17
|
|
|
206
|
-
**Returns**
|
|
207
|
-
```json
|
|
208
|
-
{
|
|
209
|
-
"forms": [
|
|
210
|
-
{ "taskId": "...", "taskName": "...", "formId": "...", "path": "path/to/form-id.form" }
|
|
211
|
-
]
|
|
212
|
-
}
|
|
213
18
|
```
|
|
214
|
-
|
|
215
|
-
Returns `{ "forms": [] }` if no user tasks with `formId` are found.
|
|
216
|
-
|
|
217
|
-
---
|
|
218
|
-
|
|
219
|
-
### `dmn_create`
|
|
220
|
-
|
|
221
|
-
Generate DMN decision table XML for all `businessRuleTask` elements in a BPMN that have a `decisionId`. Writes one `.dmn` file per task.
|
|
222
|
-
|
|
223
|
-
**Parameters**
|
|
224
|
-
| Name | Required | Description |
|
|
225
|
-
|---|---|---|
|
|
226
|
-
| `bpmnPath` | yes | Path to the `.bpmn` file. |
|
|
227
|
-
| `outputDir` | no | Where to write DMN files (default: same directory as the BPMN). |
|
|
228
|
-
|
|
229
|
-
**Returns**
|
|
230
|
-
```json
|
|
231
|
-
{
|
|
232
|
-
"decisions": [
|
|
233
|
-
{ "taskId": "...", "taskName": "...", "decisionId": "...", "path": "path/to/decision-id.dmn" }
|
|
234
|
-
]
|
|
235
|
-
}
|
|
19
|
+
<name>.plan.json → casen synth → <name>.bpmn (+ <name>.bpmn.tests.json if plan.tests is set)
|
|
236
20
|
```
|
|
237
21
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
| `domain` | yes | Pattern id (e.g. `"invoice-approval"`) or free-text query (e.g. `"employee onboarding"`). |
|
|
264
|
-
|
|
265
|
-
**Returns** `{ id, name, description, keywords, readme, workers, variations, template }`
|
|
266
|
-
|
|
267
|
-
Pass `pattern.readme` and `pattern.workers` as additional context in the `description` parameter of `bpmn_create`.
|
|
22
|
+
`casen plan schema` prints the full `ProcessPlan` format reference. `casen plan extract <file>.bpmn` lifts an existing process back into plan form (for `/extend`-style changes).
|
|
23
|
+
|
|
24
|
+
## Key commands
|
|
25
|
+
|
|
26
|
+
| Command | Does |
|
|
27
|
+
|---|---|
|
|
28
|
+
| `casen plan schema` | Print the `ProcessPlan` JSON format reference |
|
|
29
|
+
| `casen plan extract <file>.bpmn` | Lift an existing process into `<file>.plan.json` |
|
|
30
|
+
| `casen synth <plan>.json --output <file>.bpmn` | Compile a plan to laid-out, deployable BPMN |
|
|
31
|
+
| `casen synth <plan>.json --merge <file>.bpmn --output <file>.bpmn` | Compile a delta plan and merge it into an existing process |
|
|
32
|
+
| `casen connector search "<query>"` | Find a Camunda connector template by name/keyword |
|
|
33
|
+
| `casen connector show <template-id>` | Required/optional input keys, task type, direction |
|
|
34
|
+
| `casen lint lint <file>.bpmn` | Full static analysis (all categories) |
|
|
35
|
+
| `casen lint lint <file>.bpmn --profile deploy` | Deploy-readiness gate — errors only |
|
|
36
|
+
| `casen lint lint <file>.bpmn --fix` | Apply auto-fixable findings, write back |
|
|
37
|
+
| `casen test <file>.bpmn` | Run scenarios from `<file>.bpmn.tests.json` |
|
|
38
|
+
| `casen deploy deploy <file>.bpmn [--target camunda8]` | Deploy to local Reebe (default) or Camunda 8 |
|
|
39
|
+
| `casen worker start` | Start every scaffolded worker in `./workers/` |
|
|
40
|
+
|
|
41
|
+
## Conventions
|
|
42
|
+
|
|
43
|
+
- A value starting with `=` is a FEEL expression; without it, it's a literal string.
|
|
44
|
+
- Secrets always use the `{{secrets.NAME}}` placeholder — never a literal credential.
|
|
45
|
+
- Every plan step's `id`/`name` should follow Camunda naming conventions ("Verb Object" tasks, "Object + past participle" start events, "?" gateway questions) — see the full plugin's `references/modeling-style.md` for the complete list.
|
|
46
|
+
- Worker stubs use `@bpmnkit/worker-client`'s `createWorkerClient({ workerName }).poll(jobType)` API, written to `workers/<slug>/index.ts`.
|
package/skills/deploy.md
CHANGED
|
@@ -1,33 +1,36 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
2
|
+
description: Gate a BPMN process on deploy-readiness, then deploy it to local Reebe or Camunda 8.
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
@.claude/aikit.md
|
|
6
6
|
|
|
7
|
-
Deploy the BPMN process
|
|
7
|
+
Deploy the BPMN process: $ARGUMENTS
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Extract the `.bpmn` filename (find the single `.bpmn` in cwd, or ask, if not given) and destination (`--local` default, or `--camunda`).
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
## Step 1 — Gate on deploy-readiness
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
```sh
|
|
14
|
+
casen lint lint <file>.bpmn --profile deploy
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
If this reports any errors, stop and fix them first (or run `/review`) — do not deploy with deploy-profile errors.
|
|
18
|
+
|
|
19
|
+
## Step 2 — Deploy
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
casen deploy deploy <file>.bpmn # local Reebe
|
|
23
|
+
casen deploy deploy <file>.bpmn --target camunda8 # active Camunda 8 profile
|
|
24
|
+
```
|
|
14
25
|
|
|
15
|
-
|
|
16
|
-
- If there are errors, show them and ask: **"Fix errors first or deploy anyway?"**
|
|
17
|
-
- If warnings only: show them but proceed.
|
|
26
|
+
Local deploy unreachable → tell the user to run `casen reebe start --port 26500` first, then retry. Camunda 8 deploy with no active profile → tell the user to run `casen profile create <name> --base-url <url> --auth-type bearer --token <token>` then `casen profile use <name>`, then retry.
|
|
18
27
|
|
|
19
|
-
|
|
28
|
+
## Step 3 — Verify and summarize
|
|
20
29
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
30
|
+
```sh
|
|
31
|
+
casen process-definition list --output json
|
|
32
|
+
```
|
|
24
33
|
|
|
25
|
-
|
|
26
|
-
- On success: "Deployed successfully. Process ID: <id>"
|
|
27
|
-
- On failure: show the error and suggest a fix (profile not set up, reebe not running, etc.)
|
|
34
|
+
Report: `Deployed: <process-id> version: <N> target: <local|camunda8>`.
|
|
28
35
|
|
|
29
|
-
|
|
30
|
-
```
|
|
31
|
-
Don't forget to start your workers:
|
|
32
|
-
casen worker start
|
|
33
|
-
```
|
|
36
|
+
If any scaffolded workers exist in `./workers/`, remind: `casen worker start`. Remind about any `{{secrets.NAME}}` the process references — they must be provisioned in the target engine's secret store.
|
package/skills/implement.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Implement a BPMN process end-to-end from a natural language description
|
|
2
|
+
description: Implement a BPMN process end-to-end from a natural language description — plan, compile, test, deploy.
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
@.claude/aikit.md
|
|
6
6
|
|
|
7
|
-
You are implementing a BPMN process
|
|
7
|
+
You are implementing a BPMN process using `casen`. You never write BPMN XML by hand — every process is authored as a `ProcessPlan` JSON file and compiled with `casen synth`.
|
|
8
8
|
|
|
9
9
|
## Request
|
|
10
10
|
|
|
@@ -12,82 +12,48 @@ $ARGUMENTS
|
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
15
|
-
## Step 1 —
|
|
15
|
+
## Step 1 — Resolve external interactions
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
If any pattern keywords match the request, call `mcp__bpmnkit-aikit__pattern_get` to load the full pattern as context for the next step.
|
|
17
|
+
For each external system the process touches (Slack, email, HTTP, etc.): `casen connector search "<system>"` then `casen connector show <template-id>` for its required inputs. No match → use a plain `jobType` step (a worker gets scaffolded in Step 5).
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
## Step 2 — Plan: Create the BPMN
|
|
23
|
-
|
|
24
|
-
Spawn a subagent with this task:
|
|
19
|
+
## Step 2 — Write the plan
|
|
25
20
|
|
|
26
|
-
|
|
27
|
-
>
|
|
28
|
-
> If a domain pattern was loaded in Step 1, pass its readme and worker specs as additional context in the description parameter.
|
|
29
|
-
>
|
|
30
|
-
> Return the file path of the generated BPMN.
|
|
31
|
-
|
|
32
|
-
---
|
|
21
|
+
Write `<slug>.plan.json` per `casen plan schema`. Name elements clearly (see `.claude/aikit.md`'s naming conventions).
|
|
33
22
|
|
|
34
|
-
## Step 3 —
|
|
35
|
-
|
|
36
|
-
Spawn a subagent with this task:
|
|
37
|
-
|
|
38
|
-
> You are implementing workers for a BPMN process.
|
|
39
|
-
>
|
|
40
|
-
> 1. Call `mcp__bpmnkit-aikit__worker_list` to get the catalog of available workers.
|
|
41
|
-
> 2. Call `mcp__bpmnkit-aikit__bpmn_read` on the BPMN file from Step 2 to find all service task job types.
|
|
42
|
-
> 3. For each service task job type:
|
|
43
|
-
> - If a built-in or previously scaffolded worker matches: note it as "reused"
|
|
44
|
-
> - If no match exists: call `mcp__bpmnkit-aikit__worker_scaffold` with the job type, a description, and expected inputs/outputs derived from the BPMN context
|
|
45
|
-
> 4. Return: a list of `{ jobType, status: "reused" | "scaffolded", workerPath? }` for each service task
|
|
46
|
-
|
|
47
|
-
---
|
|
23
|
+
## Step 3 — Compile
|
|
48
24
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
25
|
+
```sh
|
|
26
|
+
casen synth <slug>.plan.json --output <slug>.bpmn
|
|
27
|
+
```
|
|
52
28
|
|
|
53
|
-
|
|
54
|
-
> Identify any errors that block deployment and any warnings worth noting.
|
|
55
|
-
> Return: `{ errors: [...], warnings: [...] }`
|
|
29
|
+
Fix any reported problems in the plan (never the XML) and re-run — bounded to 2 retries before asking the user.
|
|
56
30
|
|
|
57
|
-
|
|
31
|
+
## Step 4 — Test
|
|
58
32
|
|
|
59
|
-
|
|
33
|
+
Add a `tests` array to the plan covering the happy path and every branch/boundary, re-synth (writes `<slug>.bpmn.tests.json`), then:
|
|
60
34
|
|
|
61
|
-
|
|
35
|
+
```sh
|
|
36
|
+
casen test <slug>.bpmn
|
|
37
|
+
```
|
|
62
38
|
|
|
63
|
-
|
|
64
|
-
> Return: worker coverage report (total service tasks, covered, missing)
|
|
39
|
+
## Step 5 — Scaffold workers
|
|
65
40
|
|
|
66
|
-
|
|
41
|
+
For every job-type step with no existing worker, write `workers/<slug>/index.ts` using `@bpmnkit/worker-client`'s `createWorkerClient({ workerName }).poll(jobType)` API.
|
|
67
42
|
|
|
68
43
|
## Step 6 — Present summary and ask to deploy
|
|
69
44
|
|
|
70
|
-
Collect all results and present a summary:
|
|
71
|
-
|
|
72
45
|
```
|
|
73
46
|
BPMN file: <path>
|
|
74
|
-
Pattern used: <id or "none">
|
|
75
|
-
|
|
76
|
-
Workers:
|
|
77
|
-
✓ reused: <list>
|
|
78
|
-
+ scaffolded: <list with paths>
|
|
79
|
-
|
|
80
|
-
Validation:
|
|
81
|
-
Errors: <count> — <list if any>
|
|
82
|
-
Warnings: <count> — <list if any>
|
|
83
47
|
|
|
84
|
-
|
|
48
|
+
Connectors used: <list, with required secrets — never their values>
|
|
49
|
+
Workers: reused <list> / scaffolded <list with paths>
|
|
50
|
+
Tests: X/Y passed
|
|
85
51
|
|
|
86
52
|
Scaffolded workers require: npm install && npm start (in each workers/<name>/ directory)
|
|
87
53
|
```
|
|
88
54
|
|
|
89
|
-
Then ask: **"Deploy to local
|
|
55
|
+
Then ask: **"Deploy to local Reebe, deploy to Camunda 8, or skip?"**
|
|
90
56
|
|
|
91
|
-
-
|
|
92
|
-
-
|
|
93
|
-
-
|
|
57
|
+
- local: `casen lint lint <slug>.bpmn --profile deploy` (must be zero errors) then `casen deploy deploy <slug>.bpmn`
|
|
58
|
+
- camunda8: same lint gate, then `casen deploy deploy <slug>.bpmn --target camunda8`
|
|
59
|
+
- skip: done
|
package/skills/review.md
CHANGED
|
@@ -1,35 +1,41 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Review a BPMN file and report findings with severity and
|
|
2
|
+
description: Review a BPMN file and report findings with severity and an explicit deploy-ready verdict.
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
@.claude/aikit.md
|
|
6
6
|
|
|
7
|
-
Review the BPMN file
|
|
7
|
+
Review the BPMN file: $ARGUMENTS
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
If no file is given, find the single `.bpmn` in cwd or ask.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
## Step 1 — Run both profiles
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
```sh
|
|
14
|
+
casen lint lint <file>.bpmn --profile deploy --format json
|
|
15
|
+
casen lint lint <file>.bpmn --format json
|
|
16
|
+
```
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
## Step 2 — Present findings grouped by severity
|
|
16
19
|
|
|
17
|
-
|
|
20
|
+
**Errors** (must fix before deploy)
|
|
21
|
+
- `[element-id]` `[category]` message. Fix: suggestion, if present.
|
|
18
22
|
|
|
19
|
-
**
|
|
20
|
-
-
|
|
23
|
+
**Warnings** (should fix)
|
|
24
|
+
- ...
|
|
21
25
|
|
|
22
|
-
**
|
|
23
|
-
-
|
|
26
|
+
**Info** (consider)
|
|
27
|
+
- ...
|
|
24
28
|
|
|
25
|
-
|
|
26
|
-
- For each info item: message
|
|
29
|
+
## Step 3 — Offer auto-fix
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
If there are any findings, offer:
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
casen lint lint <file>.bpmn --fix
|
|
32
35
|
```
|
|
33
36
|
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
Re-run Step 1 afterward to confirm.
|
|
38
|
+
|
|
39
|
+
## Step 4 — Explicit verdict
|
|
40
|
+
|
|
41
|
+
End with: **"Deploy-ready: yes"** (zero errors from the deploy profile) or **"Deploy-ready: no — N error(s)"**.
|
package/skills/test.md
CHANGED
|
@@ -1,38 +1,36 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
2
|
+
description: Run scenario tests on a BPMN process file and report path coverage.
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
@.claude/aikit.md
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Run scenario tests: $ARGUMENTS
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
If no file is given, find the single `.bpmn` in cwd or ask.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
## Step 1 — Ensure scenarios exist
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Look for `<file>.bpmn.tests.json` (written automatically by `casen synth` from a plan's `tests` array). If missing: `casen plan extract <file>.bpmn`, add a `tests` array covering the happy path and every branch/boundary, then `casen synth <plan>.json --merge <file>.bpmn` to regenerate it — or hand-write the sidecar directly (array of `{ id, name, inputs?, mocks?, expect? }`).
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
## Step 2 — Run
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
```sh
|
|
18
|
+
casen test <file>.bpmn
|
|
19
|
+
```
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
## Step 3 — Report
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
```
|
|
24
|
+
| Scenario | Result | Details |
|
|
25
|
+
|----------|--------|---------|
|
|
26
|
+
| happy-path | ✓ PASS | (Nms) |
|
|
27
|
+
| error-path | ✗ FAIL | field: expected X, got Y |
|
|
28
|
+
```
|
|
22
29
|
|
|
23
|
-
|
|
24
|
-
- Pools / participants
|
|
25
|
-
- Service tasks and their job types
|
|
26
|
-
- Decision gateways and branch conditions
|
|
27
|
-
- Event types (timer, message, error, escalation)
|
|
30
|
+
## Step 4 — Coverage
|
|
28
31
|
|
|
29
|
-
|
|
30
|
-
- ✓ Covered job types (matched to built-in or scaffolded workers)
|
|
31
|
-
- ✗ Missing job types (no worker found — scaffold with worker_scaffold)
|
|
32
|
+
Cross-reference gateway branches and error/timer boundaries against which scenarios exercise them; report anything uncovered.
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
- Errors and warnings from the pattern advisor
|
|
34
|
+
## Step 5 — Summary
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
- Happy path: <describe the main success path>
|
|
38
|
-
- Edge cases: <describe key branches, timeouts, error conditions>
|
|
36
|
+
"X/Y scenarios passed. N branch(es)/boundary(ies) uncovered."
|
package/skills/design.md
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Design a BPMN process — flow, forms, and decision tables. No workers, no deployment.
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
@.claude/aikit.md
|
|
6
|
-
|
|
7
|
-
You are designing a BPMN process using BPMNKit AIKit tools. Work through these steps in order.
|
|
8
|
-
|
|
9
|
-
## Request
|
|
10
|
-
|
|
11
|
-
$ARGUMENTS
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## Step 1 — Check for a domain pattern
|
|
16
|
-
|
|
17
|
-
Call `mcp__bpmnkit-aikit__pattern_list` to see available domain patterns.
|
|
18
|
-
If any pattern keywords match the request, call `mcp__bpmnkit-aikit__pattern_get` to load the full pattern as context.
|
|
19
|
-
|
|
20
|
-
---
|
|
21
|
-
|
|
22
|
-
## Step 2 — Create the BPMN
|
|
23
|
-
|
|
24
|
-
Spawn a subagent with this task:
|
|
25
|
-
|
|
26
|
-
> Using `mcp__bpmnkit-aikit__bpmn_create`, generate a BPMN process for: **$ARGUMENTS**
|
|
27
|
-
>
|
|
28
|
-
> If a pattern was loaded in Step 1, pass its readme and worker specs as context in the description parameter.
|
|
29
|
-
>
|
|
30
|
-
> Return the file path of the generated BPMN.
|
|
31
|
-
|
|
32
|
-
---
|
|
33
|
-
|
|
34
|
-
## Step 3 — Generate forms
|
|
35
|
-
|
|
36
|
-
Spawn a subagent with this task:
|
|
37
|
-
|
|
38
|
-
> Call `mcp__bpmnkit-aikit__form_create` with the BPMN path from Step 2.
|
|
39
|
-
>
|
|
40
|
-
> Return: list of `{ taskId, formId, path }` for each form created, or an empty list if no user tasks with formId were found.
|
|
41
|
-
|
|
42
|
-
---
|
|
43
|
-
|
|
44
|
-
## Step 4 — Generate DMN tables
|
|
45
|
-
|
|
46
|
-
Spawn a subagent with this task:
|
|
47
|
-
|
|
48
|
-
> Call `mcp__bpmnkit-aikit__dmn_create` with the BPMN path from Step 2.
|
|
49
|
-
>
|
|
50
|
-
> Return: list of `{ taskId, decisionId, path }` for each DMN file created, or an empty list if no business rule tasks with decisionId were found.
|
|
51
|
-
|
|
52
|
-
---
|
|
53
|
-
|
|
54
|
-
## Step 5 — Validate
|
|
55
|
-
|
|
56
|
-
Spawn a subagent with this task:
|
|
57
|
-
|
|
58
|
-
> Call `mcp__bpmnkit-aikit__bpmn_validate` on the BPMN file from Step 2.
|
|
59
|
-
>
|
|
60
|
-
> Return: `{ errors: [...], warnings: [...] }`
|
|
61
|
-
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
## Step 6 — Present design summary
|
|
65
|
-
|
|
66
|
-
Collect all results and present:
|
|
67
|
-
|
|
68
|
-
```
|
|
69
|
-
BPMN: <path>
|
|
70
|
-
Pattern: <id or "none">
|
|
71
|
-
|
|
72
|
-
Forms (<count>):
|
|
73
|
-
<formId> → <path>
|
|
74
|
-
(or "none" if empty)
|
|
75
|
-
|
|
76
|
-
DMN tables (<count>):
|
|
77
|
-
<decisionId> → <path>
|
|
78
|
-
(or "none" if empty)
|
|
79
|
-
|
|
80
|
-
Validation:
|
|
81
|
-
Errors: <count> — <list if any>
|
|
82
|
-
Warnings: <count> — <list if any>
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
Then say: **"Use `/implement` to add workers, or `/deploy` when ready."**
|
|
86
|
-
|
|
87
|
-
Do NOT offer to deploy.
|