@kb-labs/scaffold 2.94.0 → 2.96.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/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # @kb-labs/scaffold
2
+
3
+ > Part of [KB Labs](https://github.com/KirillBaranov/kb-labs) ecosystem.
4
+
5
+ V3 plugin manifest and CLI commands for the KB Labs scaffold engine.
@@ -1,5 +1,5 @@
1
1
  import { resolve } from 'path';
2
- import { defineCommand } from '@kb-labs/sdk';
2
+ import { defineCommand, handleError } from '@kb-labs/sdk';
3
3
  import { scanRoot } from '@kb-labs/scaffold-core';
4
4
 
5
5
  // src/commands/doctor.ts
@@ -10,11 +10,20 @@ var doctor_default = defineCommand({
10
10
  async execute(ctx, input) {
11
11
  const root = resolve(input.path ?? ".kb/plugins");
12
12
  const workspaceRoot = ctx.cwd ?? process.cwd();
13
- const scan = await scanRoot(root, { workspaceRoot });
13
+ let scan;
14
+ try {
15
+ scan = await scanRoot(root, { workspaceRoot });
16
+ } catch (err) {
17
+ handleError(ctx, err, input.json);
18
+ return { exitCode: 1 };
19
+ }
14
20
  const { errorCount, warnCount } = scan.findings.reduce(
15
21
  (acc, f) => {
16
- if (f.severity === "error") acc.errorCount++;
17
- else if (f.severity === "warn") acc.warnCount++;
22
+ if (f.severity === "error") {
23
+ acc.errorCount++;
24
+ } else if (f.severity === "warn") {
25
+ acc.warnCount++;
26
+ }
18
27
  return acc;
19
28
  },
20
29
  { errorCount: 0, warnCount: 0 }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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","file":"doctor.js","sourcesContent":["import { resolve } from 'node:path';\nimport { defineCommand, type PluginContextV3 } from '@kb-labs/sdk';\nimport { scanRoot, type Finding } from '@kb-labs/scaffold-core';\n\ninterface DoctorFlags {\n path?: string;\n json?: boolean;\n}\n\ntype DoctorResult = {\n exitCode: number;\n result?: {\n packagesScanned: number;\n findings: Finding[];\n };\n};\n\nexport default defineCommand({\n id: 'scaffold:doctor',\n description: 'Scan user-authored plugins for common issues',\n\n handler: {\n async execute(\n ctx: PluginContextV3,\n input: DoctorFlags,\n ): Promise<DoctorResult> {\n const root = resolve(input.path ?? '.kb/plugins');\n const workspaceRoot = ctx.cwd ?? process.cwd();\n const scan = await scanRoot(root, { workspaceRoot });\n\n const { errorCount, warnCount } = scan.findings.reduce(\n (acc, f) => {\n if (f.severity === 'error') acc.errorCount++;\n else if (f.severity === 'warn') acc.warnCount++;\n return acc;\n },\n { errorCount: 0, warnCount: 0 },\n );\n const hasErrors = errorCount > 0;\n\n if (input.json) {\n ctx.ui?.json?.({\n root,\n packagesScanned: scan.packagesScanned,\n findings: scan.findings,\n });\n } else if (scan.findings.length === 0) {\n ctx.ui?.success?.(`Scanned ${scan.packagesScanned} package(s) — no issues found.`, {\n title: 'scaffold doctor',\n sections: [\n { items: [`Path: ${root}`] },\n ],\n });\n } else {\n const byPkg = new Map<string, Finding[]>();\n for (const f of scan.findings) {\n const list = byPkg.get(f.package) ?? [];\n list.push(f);\n byPkg.set(f.package, list);\n }\n\n const sections = Array.from(byPkg.entries()).map(([pkg, findings]) => ({\n header: pkg,\n items: findings.map((f) => `${f.severity.toUpperCase()} ${f.message}`),\n }));\n\n if (hasErrors) {\n ctx.ui?.error?.(`Found ${errorCount} error(s), ${warnCount} warning(s) in ${scan.packagesScanned} package(s).`, {\n title: 'scaffold doctor',\n sections,\n });\n } else {\n ctx.ui?.warn?.(`Found ${warnCount} warning(s) in ${scan.packagesScanned} package(s).`, {\n title: 'scaffold doctor',\n sections,\n });\n }\n }\n\n return {\n exitCode: hasErrors ? 1 : 0,\n result: {\n packagesScanned: scan.packagesScanned,\n findings: scan.findings,\n },\n };\n },\n },\n});\n"]}
1
+ {"version":3,"sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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","file":"doctor.js","sourcesContent":["import { resolve } from 'node:path';\nimport { defineCommand, handleError, type PluginContextV3 } from '@kb-labs/sdk';\nimport { scanRoot, type Finding } from '@kb-labs/scaffold-core';\n\ninterface DoctorFlags {\n path?: string;\n json?: boolean;\n}\n\ntype DoctorResult = {\n exitCode: number;\n result?: {\n packagesScanned: number;\n findings: Finding[];\n };\n};\n\nexport default defineCommand({\n id: 'scaffold:doctor',\n description: 'Scan user-authored plugins for common issues',\n\n handler: {\n async execute(\n ctx: PluginContextV3,\n input: DoctorFlags,\n ): Promise<DoctorResult> {\n const root = resolve(input.path ?? '.kb/plugins');\n const workspaceRoot = ctx.cwd ?? process.cwd();\n let scan: Awaited<ReturnType<typeof scanRoot>>;\n try {\n scan = await scanRoot(root, { workspaceRoot });\n } catch (err) {\n handleError(ctx, err, input.json);\n return { exitCode: 1 };\n }\n\n const { errorCount, warnCount } = scan.findings.reduce(\n (acc, f) => {\n if (f.severity === 'error') { acc.errorCount++; }\n else if (f.severity === 'warn') { acc.warnCount++; }\n return acc;\n },\n { errorCount: 0, warnCount: 0 },\n );\n const hasErrors = errorCount > 0;\n\n if (input.json) {\n ctx.ui?.json?.({\n root,\n packagesScanned: scan.packagesScanned,\n findings: scan.findings,\n });\n } else if (scan.findings.length === 0) {\n ctx.ui?.success?.(`Scanned ${scan.packagesScanned} package(s) — no issues found.`, {\n title: 'scaffold doctor',\n sections: [\n { items: [`Path: ${root}`] },\n ],\n });\n } else {\n const byPkg = new Map<string, Finding[]>();\n for (const f of scan.findings) {\n const list = byPkg.get(f.package) ?? [];\n list.push(f);\n byPkg.set(f.package, list);\n }\n\n const sections = Array.from(byPkg.entries()).map(([pkg, findings]) => ({\n header: pkg,\n items: findings.map((f) => `${f.severity.toUpperCase()} ${f.message}`),\n }));\n\n if (hasErrors) {\n ctx.ui?.error?.(`Found ${errorCount} error(s), ${warnCount} warning(s) in ${scan.packagesScanned} package(s).`, {\n title: 'scaffold doctor',\n sections,\n });\n } else {\n ctx.ui?.warn?.(`Found ${warnCount} warning(s) in ${scan.packagesScanned} package(s).`, {\n title: 'scaffold doctor',\n sections,\n });\n }\n }\n\n return {\n exitCode: hasErrors ? 1 : 0,\n result: {\n packagesScanned: scan.packagesScanned,\n findings: scan.findings,\n },\n };\n },\n },\n});\n"]}
@@ -3,7 +3,7 @@ import { fileURLToPath } from 'url';
3
3
  import { createRequire } from 'module';
4
4
  import { readFile, mkdir, writeFile } from 'fs/promises';
5
5
  import { createHash } from 'crypto';
6
- import { defineCommand } from '@kb-labs/sdk';
6
+ import { defineCommand, validationError, handleError } from '@kb-labs/sdk';
7
7
  import { listEntities, runValidator, loadEntity, resolveBlocks, build, inspectTarget, formatTree, writeFiles } from '@kb-labs/scaffold-core';
8
8
  import { formatCommandHelp } from '@kb-labs/shared-cli-ui';
9
9
 
@@ -50,7 +50,9 @@ async function detectVersions() {
50
50
  return { sdk, devkit, commandKit };
51
51
  }
52
52
  function parsePositional(argv) {
53
- if (!argv || argv.length === 0) return {};
53
+ if (!argv || argv.length === 0) {
54
+ return {};
55
+ }
54
56
  const positional = argv.filter((a) => !a.startsWith("-"));
55
57
  return { entity: positional[0], name: positional[1] };
56
58
  }
@@ -60,7 +62,7 @@ async function runDefault(ctx, rawInput) {
60
62
  const { entity: entityArg, name: nameArg } = parsePositional(input.argv);
61
63
  const available = await listEntities(TEMPLATES_ROOT);
62
64
  if (available.length === 0) {
63
- ctx.ui?.error?.("No scaffold templates found. Reinstall @kb-labs/scaffold.");
65
+ validationError(ctx, "No scaffold templates found. Reinstall @kb-labs/scaffold.");
64
66
  return { exitCode: 1 };
65
67
  }
66
68
  if (!entityArg) {
@@ -74,7 +76,7 @@ async function runDefault(ctx, rawInput) {
74
76
  return { exitCode: 1 };
75
77
  }
76
78
  if (!available.includes(entityArg)) {
77
- ctx.ui?.error?.(`Unknown entity "${entityArg}". Available: ${available.join(", ")}`);
79
+ validationError(ctx, `Unknown entity "${entityArg}". Available: ${available.join(", ")}`);
78
80
  return { exitCode: 1 };
79
81
  }
80
82
  if (!nameArg) {
@@ -88,8 +90,7 @@ async function runDefault(ctx, rawInput) {
88
90
  }
89
91
  const problem = runValidator("npmName", nameArg);
90
92
  if (problem) {
91
- ctx.ui?.error?.(`Invalid name "${nameArg}": ${problem}`);
92
- ctx.ui?.info?.("Name must be lowercase, no spaces, valid npm package name.");
93
+ validationError(ctx, `Invalid name "${nameArg}": ${problem}`, "Name must be lowercase, no spaces, valid npm package name.");
93
94
  return { exitCode: 1 };
94
95
  }
95
96
  const entity = await loadEntity(TEMPLATES_ROOT, entityArg);
@@ -112,7 +113,7 @@ async function runDefault(ctx, rawInput) {
112
113
  try {
113
114
  resolveBlocks(entity.blocks, selectedBlocks);
114
115
  } catch (e) {
115
- ctx.ui?.error?.(e.message);
116
+ handleError(ctx, e);
116
117
  return { exitCode: 1 };
117
118
  }
118
119
  for (const blockId of selectedBlocks) {
@@ -153,8 +154,7 @@ async function runDefault(ctx, rawInput) {
153
154
  if (input.force) {
154
155
  ctx.ui?.warn?.(`--force: overwriting ${outRoot}`);
155
156
  } else {
156
- ctx.ui?.error?.(`Output directory "${outRoot}" already exists and is not empty.`);
157
- ctx.ui?.info?.("Use --force to overwrite.");
157
+ validationError(ctx, `Output directory "${outRoot}" already exists and is not empty.`, "Use --force to overwrite.");
158
158
  return { exitCode: 1 };
159
159
  }
160
160
  }
@@ -204,7 +204,9 @@ async function linkWithMarketplace(entryPkgDir, projectRoot) {
204
204
  const pkgJsonPath = resolve(entryPkgDir, "package.json");
205
205
  const pkgRaw = await readFile(pkgJsonPath, "utf8");
206
206
  const pkg = JSON.parse(pkgRaw);
207
- if (!pkg.name) return "failed";
207
+ if (!pkg.name) {
208
+ return "failed";
209
+ }
208
210
  const hash = createHash("sha256").update(pkgRaw).digest("base64");
209
211
  const provides = ["plugin", "cli-command"];
210
212
  const resolvedPath = relative(projectRoot, resolve(entryPkgDir));
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/commands/scaffold.ts"],"names":[],"mappings":";;;;;;;;;;AAyBA,IAAM,IAAA,GAAO,OAAA,CAAQ,aAAA,CAAc,MAAA,CAAA,IAAA,CAAY,GAAG,CAAC,CAAA;AACnD,IAAM,cAAA,GAAiB,OAAA,CAAQ,IAAA,EAAM,IAAA,EAAM,MAAM,WAAW,CAAA;AAE5D,IAAM,cAAA,GAAiB;AAAA,EACrB,EAAE,IAAA,EAAM,OAAA,EAAS,WAAA,EAAa,yBAAA,EAA0B;AAAA,EACxD,EAAE,IAAA,EAAM,QAAA,EAAU,WAAA,EAAa,sCAAA,EAAuC;AAAA,EACtE,EAAE,IAAA,EAAM,KAAA,EAAO,WAAA,EAAa,yBAAA,EAA0B;AAAA,EACtD,EAAE,IAAA,EAAM,OAAA,EAAS,WAAA,EAAa,qCAAA,EAAsC;AAAA,EACpE,EAAE,IAAA,EAAM,SAAA,EAAW,WAAA,EAAa,+BAAA;AAClC,CAAA;AAgBA,eAAe,YAAY,OAAA,EAAkC;AAC3D,EAAA,IAAI;AACF,IAAA,MAAM,GAAA,GAAM,MAAM,QAAA,CAAS,OAAA,EAAS,MAAM,CAAA;AAC1C,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AAC7B,IAAA,OAAO,OAAO,OAAA,IAAW,OAAA;AAAA,EAC3B,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,OAAA;AAAA,EACT;AACF;AAEA,IAAM,iBAAA,GAAoB;AAAA,EACxB,GAAA,EAAK,QAAA;AAAA,EACL,MAAA,EAAQ,QAAA;AAAA,EACR,UAAA,EAAY;AACd,CAAA;AAEA,eAAe,cAAA,GAAkD;AAC/D,EAAA,MAAM,GAAA,GAAM,aAAA,CAAc,MAAA,CAAA,IAAA,CAAY,GAAG,CAAA;AACzC,EAAA,MAAM,OAAA,GAAU,OAAO,IAAA,EAAc,QAAA,KAAsC;AACzE,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAW,GAAA,CAAI,OAAA,CAAQ,IAAI,CAAA;AACjC,MAAA,MAAM,CAAA,GAAI,MAAM,WAAA,CAAY,QAAQ,CAAA;AACpC,MAAA,OAAO,CAAA,KAAM,UAAU,QAAA,GAAW,CAAA;AAAA,IACpC,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,QAAA;AAAA,IACT;AAAA,EACF,CAAA;AACA,EAAA,MAAM,CAAC,GAAA,EAAK,MAAA,EAAQ,UAAU,CAAA,GAAI,MAAM,QAAQ,GAAA,CAAI;AAAA,IAClD,OAAA,CAAQ,2BAAA,EAA6B,iBAAA,CAAkB,GAAG,CAAA;AAAA,IAC1D,OAAA,CAAQ,8BAAA,EAAgC,iBAAA,CAAkB,MAAM,CAAA;AAAA,IAChE,OAAA,CAAQ,0CAAA,EAA4C,iBAAA,CAAkB,UAAU;AAAA,GACjF,CAAA;AACD,EAAA,OAAO,EAAE,GAAA,EAAK,MAAA,EAAQ,UAAA,EAAW;AACnC;AAEA,SAAS,gBAAgB,IAAA,EAGvB;AACA,EAAA,IAAI,CAAC,IAAA,IAAQ,IAAA,CAAK,MAAA,KAAW,CAAA,SAAU,EAAC;AACxC,EAAA,MAAM,UAAA,GAAa,KAAK,MAAA,CAAO,CAAC,MAAM,CAAC,CAAA,CAAE,UAAA,CAAW,GAAG,CAAC,CAAA;AACxD,EAAA,OAAO,EAAE,QAAQ,UAAA,CAAW,CAAC,GAAG,IAAA,EAAM,UAAA,CAAW,CAAC,CAAA,EAAE;AACtD;AAEA,eAAe,UAAA,CACb,KACA,QAAA,EACyB;AACzB,EAAA,MAAM,KAAA,GAAQ,KAAK,GAAA,EAAI;AACvB,EAAA,MAAM,QAAQ,EAAE,GAAG,SAAS,KAAA,EAAO,IAAA,EAAM,SAAS,IAAA,EAAK;AAEvD,EAAA,MAAM,EAAE,QAAQ,SAAA,EAAW,IAAA,EAAM,SAAQ,GAAI,eAAA,CAAgB,MAAM,IAAI,CAAA;AAEvE,EAAA,MAAM,SAAA,GAAY,MAAM,YAAA,CAAa,cAAc,CAAA;AACnD,EAAA,IAAI,SAAA,CAAU,WAAW,CAAA,EAAG;AAC1B,IAAA,GAAA,CAAI,EAAA,EAAI,QAAQ,2DAA2D,CAAA;AAC3E,IAAA,OAAO,EAAE,UAAU,CAAA,EAAE;AAAA,EACvB;AAEA,EAAA,IAAI,CAAC,SAAA,EAAW;AACd,IAAA,GAAA,CAAI,EAAA,EAAI,QAAQ,iBAAA,CAAkB;AAAA,MAChC,KAAA,EAAO,iBAAA;AAAA,MACP,WAAA,EAAa,kEAAA;AAAA,MACb,QAAA,EAAU,UAAU,GAAA,CAAI,CAAC,MAAM,CAAA,gBAAA,EAAmB,CAAC,CAAA,IAAA,EAAO,CAAC,CAAA,CAAE,CAAA;AAAA,MAC7D,KAAA,EAAO;AAAA,KACR,IAAI,IAAI,CAAA;AACT,IAAA,GAAA,CAAI,IAAI,IAAA,GAAO,CAAA,oBAAA,EAAuB,UAAU,IAAA,CAAK,IAAI,CAAC,CAAA,CAAE,CAAA;AAC5D,IAAA,OAAO,EAAE,UAAU,CAAA,EAAE;AAAA,EACvB;AAEA,EAAA,IAAI,CAAC,SAAA,CAAU,QAAA,CAAS,SAAS,CAAA,EAAG;AAClC,IAAA,GAAA,CAAI,EAAA,EAAI,QAAQ,CAAA,gBAAA,EAAmB,SAAS,iBAAiB,SAAA,CAAU,IAAA,CAAK,IAAI,CAAC,CAAA,CAAE,CAAA;AACnF,IAAA,OAAO,EAAE,UAAU,CAAA,EAAE;AAAA,EACvB;AAEA,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,GAAA,CAAI,EAAA,EAAI,QAAQ,iBAAA,CAAkB;AAAA,MAChC,KAAA,EAAO,mBAAmB,SAAS,CAAA,OAAA,CAAA;AAAA,MACnC,WAAA,EAAa,kBAAkB,SAAS,CAAA,6BAAA,CAAA;AAAA,MACxC,UAAU,CAAC,CAAA,gBAAA,EAAmB,SAAS,CAAA,IAAA,EAAO,SAAS,CAAA,CAAE,CAAA;AAAA,MACzD,KAAA,EAAO;AAAA,KACR,IAAI,IAAI,CAAA;AACT,IAAA,OAAO,EAAE,UAAU,CAAA,EAAE;AAAA,EACvB;AAEA,EAAA,MAAM,OAAA,GAAU,YAAA,CAAa,SAAA,EAAW,OAAO,CAAA;AAC/C,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,GAAA,CAAI,IAAI,KAAA,GAAQ,CAAA,cAAA,EAAiB,OAAO,CAAA,GAAA,EAAM,OAAO,CAAA,CAAE,CAAA;AACvD,IAAA,GAAA,CAAI,EAAA,EAAI,OAAO,4DAA4D,CAAA;AAC3E,IAAA,OAAO,EAAE,UAAU,CAAA,EAAE;AAAA,EACvB;AAEA,EAAA,MAAM,MAAA,GAAS,MAAM,UAAA,CAAW,cAAA,EAAgB,SAAS,CAAA;AAEzD,EAAA,MAAM,OAAgC,EAAC;AACvC,EAAA,KAAA,MAAW,CAAA,IAAK,OAAO,SAAA,EAAW;AAChC,IAAA,IAAI,CAAA,CAAE,IAAA,KAAS,OAAA,IAAW,KAAA,CAAM,UAAU,MAAA,EAAW;AACnD,MAAA,IAAA,CAAK,CAAA,CAAE,IAAI,CAAA,GAAI,KAAA,CAAM,KAAA;AAAA,IACvB,WAAW,CAAA,CAAE,IAAA,KAAS,MAAA,IAAU,KAAA,CAAM,SAAS,MAAA,EAAW;AACxD,MAAA,IAAA,CAAK,CAAA,CAAE,IAAI,CAAA,GAAI,KAAA,CAAM,IAAA;AAAA,IACvB,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,CAAA,CAAE,IAAI,CAAA,GAAI,CAAA,CAAE,OAAA,IAAW,EAAA;AAAA,IAC9B;AAAA,EACF;AAEA,EAAA,IAAI,cAAA;AACJ,EAAA,IAAI,MAAM,MAAA,EAAQ;AAChB,IAAA,cAAA,GAAiB,KAAA,CAAM,MAAA,CACpB,KAAA,CAAM,GAAG,CAAA,CACT,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,EAAM,CAAA,CACnB,OAAO,OAAO,CAAA;AAAA,EACnB,CAAA,MAAO;AACL,IAAA,cAAA,GAAiB,MAAA,CAAO,UAAU,MAAA,IAAU,MAAA,CAAO,OAAO,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,EAAE,CAAA;AAAA,EAC3E;AAEA,EAAA,IAAI;AACF,IAAA,aAAA,CAAc,MAAA,CAAO,QAAQ,cAAc,CAAA;AAAA,EAC7C,SAAS,CAAA,EAAG;AACV,IAAA,GAAA,CAAI,EAAA,EAAI,KAAA,GAAS,CAAA,CAAY,OAAO,CAAA;AACpC,IAAA,OAAO,EAAE,UAAU,CAAA,EAAE;AAAA,EACvB;AAEA,EAAA,KAAA,MAAW,WAAW,cAAA,EAAgB;AACpC,IAAA,MAAM,KAAA,GAAQ,OAAO,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,OAAO,CAAA;AACxD,IAAA,KAAA,MAAW,CAAA,IAAK,KAAA,EAAO,SAAA,IAAa,EAAC,EAAG;AACtC,MAAA,IAAA,CAAK,CAAA,CAAE,IAAI,CAAA,GAAI,CAAA,CAAE,OAAA,IAAW,EAAA;AAAA,IAC9B;AAAA,EACF;AAEA,EAAA,MAAM,KAAA,GAAS,IAAA,CAAK,KAAA,IAAoB,KAAA,CAAM,KAAA,IAAS,EAAA;AACvD,EAAA,MAAM,IAAA,GAAQ,KAAA,CAAM,IAAA,IAAS,IAAA,CAAK,IAAA,IAAmB,cAAA;AAIrD,EAAA,MAAM,QAAA,GAAW,MAAM,cAAA,EAAe;AAEtC,EAAA,MAAM,OAAA,GAAyB;AAAA,IAC7B,IAAA,EAAM,OAAA;AAAA,IACN,KAAA;AAAA,IACA,IAAA;AAAA,IACA,MAAA,EAAQ,cAAA;AAAA,IACR,IAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,MAAM,MAAA,GAAS,MAAM,KAAA,CAAM;AAAA,IACzB,MAAA;AAAA,IACA,gBAAA,EAAkB,cAAA;AAAA,IAClB;AAAA,GACD,CAAA;AAED,EAAA,MAAM,OAAA,GAAU,KAAA,CAAM,GAAA,IAAO,MAAA,CAAO,OAAA;AACpC,EAAA,MAAM,KAAA,GAAQ,MAAM,aAAA,CAAc,OAAO,CAAA;AAEzC,EAAA,IAAI,KAAA,CAAM,SAAS,CAAA,EAAG;AACpB,IAAA,GAAA,CAAI,EAAA,EAAI,UAAU,kBAAA,EAAoB;AAAA,MACpC,KAAA,EAAO,6BAAA;AAAA,MACP,QAAA,EAAU;AAAA,QACR,EAAE,MAAA,EAAQ,gBAAA,EAAkB,KAAA,EAAO,CAAC,OAAO,CAAA,EAAE;AAAA,QAC7C,EAAE,MAAA,EAAQ,OAAA,EAAS,KAAA,EAAO,UAAA,CAAW,MAAA,CAAO,KAAK,CAAA,CAAE,KAAA,CAAM,IAAI,CAAA,CAAE,MAAA,CAAO,OAAO,CAAA;AAAE;AACjF,KACD,CAAA;AACD,IAAA,OAAO,EAAE,QAAA,EAAU,CAAA,EAAG,MAAA,EAAQ,EAAE,SAAS,KAAA,EAAO,MAAA,CAAO,KAAA,CAAM,MAAA,EAAO,EAAE;AAAA,EACxE;AAEA,EAAA,IAAI,KAAA,CAAM,MAAA,IAAU,CAAC,KAAA,CAAM,KAAA,EAAO;AAChC,IAAA,IAAI,MAAM,KAAA,EAAO;AACf,MAAA,GAAA,CAAI,EAAA,EAAI,IAAA,GAAO,CAAA,qBAAA,EAAwB,OAAO,CAAA,CAAE,CAAA;AAAA,IAClD,CAAA,MAAO;AACL,MAAA,GAAA,CAAI,EAAA,EAAI,KAAA,GAAQ,CAAA,kBAAA,EAAqB,OAAO,CAAA,kCAAA,CAAoC,CAAA;AAChF,MAAA,GAAA,CAAI,EAAA,EAAI,OAAO,2BAA2B,CAAA;AAC1C,MAAA,OAAO,EAAE,UAAU,CAAA,EAAE;AAAA,IACvB;AAAA,EACF;AAEA,EAAA,MAAM,UAAA,CAAW,EAAE,OAAA,EAAS,KAAA,EAAO,OAAO,KAAA,EAAO,KAAA,EAAO,KAAA,CAAM,KAAA,EAAO,CAAA;AAErE,EAAA,MAAM,cACJ,SAAA,KAAc,QAAA,GACV,GAAG,OAAO,CAAA,UAAA,EAAa,OAAO,CAAA,MAAA,CAAA,GAC9B,OAAA;AAEN,EAAA,MAAM,MAAA,GAAS,MAAM,mBAAA,CAAoB,WAAA,EAAa,IAAI,GAAG,CAAA;AAE7D,EAAA,MAAM,gBAAA,GACJ,MAAA,KAAW,IAAA,GACP,oCAAA,GACA,+DAA0D,WAAW,CAAA,CAAA;AAE3E,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,GAAA,EAAI,GAAI,KAAA;AAE5B,EAAA,GAAA,CAAI,IAAI,OAAA,GAAU,CAAA,WAAA,EAAc,SAAS,CAAA,EAAA,EAAK,OAAO,CAAA,CAAA,CAAA,EAAK;AAAA,IACxD,KAAA,EAAO,cAAA;AAAA,IACP,QAAA,EAAU;AAAA,MACR;AAAA,QACE,MAAA,EAAQ,QAAA;AAAA,QACR,KAAA,EAAO,CAAC,OAAO;AAAA,OACjB;AAAA,MACA;AAAA,QACE,MAAA,EAAQ,aAAA;AAAA,QACR,KAAA,EAAO,CAAC,gBAAgB;AAAA,OAC1B;AAAA,MACA;AAAA,QACE,MAAA,EAAQ,YAAA;AAAA,QACR,KAAA,EAAO;AAAA,UACL,MAAM,OAAO,CAAA,CAAA;AAAA,UACb,cAAA;AAAA,UACA,eAAA;AAAA,UACA,WAAW,OAAO,CAAA,iBAAA,CAAA;AAAA,UAClB,CAAA,+BAAA,EAAkC,OAAA,CAAQ,OAAO,CAAC,CAAA;AAAA;AACpD;AACF,KACF;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAA,OAAO;AAAA,IACL,QAAA,EAAU,CAAA;AAAA,IACV,QAAQ,EAAE,OAAA,EAAS,KAAA,EAAO,MAAA,CAAO,MAAM,MAAA;AAAO,GAChD;AACF;AAQA,eAAe,mBAAA,CAAoB,aAAqB,WAAA,EAA2C;AACjG,EAAA,IAAI;AACF,IAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,WAAA,EAAa,KAAA,EAAO,kBAAkB,CAAA;AAE/D,IAAA,IAAI,IAAA;AACJ,IAAA,IAAI;AACF,MAAA,IAAA,GAAO,KAAK,KAAA,CAAM,MAAM,QAAA,CAAS,QAAA,EAAU,MAAM,CAAC,CAAA;AAAA,IACpD,CAAA,CAAA,MAAQ;AACN,MAAA,IAAA,GAAO,EAAE,MAAA,EAAQ,kBAAA,EAAoB,SAAA,EAAW,EAAC,EAAE;AAAA,IACrD;AAEA,IAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,WAAA,EAAa,cAAc,CAAA;AACvD,IAAA,MAAM,MAAA,GAAS,MAAM,QAAA,CAAS,WAAA,EAAa,MAAM,CAAA;AACjD,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,MAAM,CAAA;AAC7B,IAAA,IAAI,CAAC,GAAA,CAAI,IAAA,EAAM,OAAO,QAAA;AAEtB,IAAA,MAAM,IAAA,GAAO,WAAW,QAAQ,CAAA,CAAE,OAAO,MAAM,CAAA,CAAE,OAAO,QAAQ,CAAA;AAChE,IAAA,MAAM,QAAA,GAAqB,CAAC,QAAA,EAAU,aAAa,CAAA;AACnD,IAAA,MAAM,YAAA,GAAe,QAAA,CAAS,WAAA,EAAa,OAAA,CAAQ,WAAW,CAAC,CAAA;AAC/D,IAAA,MAAM,QAAA,GAAW,GAAA,CAAI,IAAA,CAAK,OAAA,CAAQ,WAAW,EAAE,CAAA;AAE/C,IAAA,IAAA,CAAK,SAAA,CAAU,QAAQ,CAAA,GAAI;AAAA,MACzB,OAAA,EAAS,IAAI,OAAA,IAAW,OAAA;AAAA,MACxB,SAAA,EAAW,UAAU,IAAI,CAAA,CAAA;AAAA,MACzB,YAAA,EAAc,KAAK,YAAY,CAAA,CAAA;AAAA,MAC/B,WAAA,EAAA,iBAAa,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,MACpC,MAAA,EAAQ,OAAA;AAAA,MACR,WAAA,EAAa,QAAA;AAAA,MACb,QAAA;AAAA,MACA,OAAA,EAAS;AAAA,KACX;AAEA,IAAA,MAAM,MAAM,OAAA,CAAQ,QAAQ,GAAG,EAAE,SAAA,EAAW,MAAM,CAAA;AAClD,IAAA,MAAM,UAAU,QAAA,EAAU,IAAA,CAAK,UAAU,IAAA,EAAM,IAAA,EAAM,CAAC,CAAC,CAAA;AACvD,IAAA,OAAO,IAAA;AAAA,EACT,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,QAAA;AAAA,EACT;AACF;AAEA,IAAO,mBAAQ,aAAA,CAAc;AAAA,EAC3B,EAAA,EAAI,cAAA;AAAA,EACJ,WAAA,EAAa,sCAAA;AAAA,EAEb,OAAA,EAAS;AAAA,IACP,OAAA,EAAS;AAAA;AAEb,CAAC","file":"scaffold.js","sourcesContent":["import { dirname, resolve, relative } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { createRequire } from 'node:module';\nimport { readFile, writeFile, mkdir } from 'node:fs/promises';\nimport { createHash } from 'node:crypto';\nimport {\n defineCommand,\n type PluginContextV3,\n type CLIInput,\n} from '@kb-labs/sdk';\nimport {\n build,\n inspectTarget,\n formatTree,\n listEntities,\n loadEntity,\n resolveBlocks,\n runValidator,\n writeFiles,\n} from '@kb-labs/scaffold-core';\nimport type {\n RenderContext,\n} from '@kb-labs/scaffold-contracts';\nimport { formatCommandHelp } from '@kb-labs/shared-cli-ui';\n\nconst here = dirname(fileURLToPath(import.meta.url));\nconst TEMPLATES_ROOT = resolve(here, '..', '..', 'templates');\n\nconst SCAFFOLD_FLAGS = [\n { name: 'scope', description: 'npm scope, e.g. @my-org' },\n { name: 'blocks', description: 'comma-separated block ids to include' },\n { name: 'out', description: 'custom output directory' },\n { name: 'force', description: 'overwrite existing output directory' },\n { name: 'dry-run', description: 'preview files without writing' },\n];\n\ninterface ScaffoldFlags {\n blocks?: string;\n force?: boolean;\n 'dry-run'?: boolean;\n out?: string;\n scope?: string;\n mode?: string;\n}\n\ntype ScaffoldResult = {\n exitCode: number;\n result?: { outRoot: string; files: number };\n};\n\nasync function readVersion(pkgPath: string): Promise<string> {\n try {\n const raw = await readFile(pkgPath, 'utf8');\n const parsed = JSON.parse(raw) as { version?: string };\n return parsed.version ?? '0.0.0';\n } catch {\n return '0.0.0';\n }\n}\n\nconst FALLBACK_VERSIONS = {\n sdk: '2.18.0',\n devkit: '2.28.0',\n commandKit: '2.30.0',\n} as const;\n\nasync function detectVersions(): Promise<Record<string, string>> {\n const req = createRequire(import.meta.url);\n const readPkg = async (spec: string, fallback: string): Promise<string> => {\n try {\n const resolved = req.resolve(spec);\n const v = await readVersion(resolved);\n return v === '0.0.0' ? fallback : v;\n } catch {\n return fallback;\n }\n };\n const [sdk, devkit, commandKit] = await Promise.all([\n readPkg('@kb-labs/sdk/package.json', FALLBACK_VERSIONS.sdk),\n readPkg('@kb-labs/devkit/package.json', FALLBACK_VERSIONS.devkit),\n readPkg('@kb-labs/shared-command-kit/package.json', FALLBACK_VERSIONS.commandKit),\n ]);\n return { sdk, devkit, commandKit };\n}\n\nfunction parsePositional(argv: string[] | undefined): {\n entity?: string;\n name?: string;\n} {\n if (!argv || argv.length === 0) return {};\n const positional = argv.filter((a) => !a.startsWith('-'));\n return { entity: positional[0], name: positional[1] };\n}\n\nasync function runDefault(\n ctx: PluginContextV3,\n rawInput: CLIInput<ScaffoldFlags>,\n): Promise<ScaffoldResult> {\n const start = Date.now();\n const input = { ...rawInput.flags, argv: rawInput.argv };\n\n const { entity: entityArg, name: nameArg } = parsePositional(input.argv);\n\n const available = await listEntities(TEMPLATES_ROOT);\n if (available.length === 0) {\n ctx.ui?.error?.('No scaffold templates found. Reinstall @kb-labs/scaffold.');\n return { exitCode: 1 };\n }\n\n if (!entityArg) {\n ctx.ui?.write?.(formatCommandHelp({\n title: 'kb scaffold run',\n description: 'Scaffold a new plugin, adapter, or other entity from a template.',\n examples: available.map((e) => `kb scaffold run ${e} my-${e}`),\n flags: SCAFFOLD_FLAGS,\n }) + '\\n');\n ctx.ui?.info?.(`Available entities: ${available.join(', ')}`);\n return { exitCode: 1 };\n }\n\n if (!available.includes(entityArg)) {\n ctx.ui?.error?.(`Unknown entity \"${entityArg}\". Available: ${available.join(', ')}`);\n return { exitCode: 1 };\n }\n\n if (!nameArg) {\n ctx.ui?.write?.(formatCommandHelp({\n title: `kb scaffold run ${entityArg} <name>`,\n description: `Scaffold a new ${entityArg}. Provide a name to continue.`,\n examples: [`kb scaffold run ${entityArg} my-${entityArg}`],\n flags: SCAFFOLD_FLAGS,\n }) + '\\n');\n return { exitCode: 1 };\n }\n\n const problem = runValidator('npmName', nameArg);\n if (problem) {\n ctx.ui?.error?.(`Invalid name \"${nameArg}\": ${problem}`);\n ctx.ui?.info?.('Name must be lowercase, no spaces, valid npm package name.');\n return { exitCode: 1 };\n }\n\n const entity = await loadEntity(TEMPLATES_ROOT, entityArg);\n\n const vars: Record<string, unknown> = {};\n for (const v of entity.variables) {\n if (v.name === 'scope' && input.scope !== undefined) {\n vars[v.name] = input.scope;\n } else if (v.name === 'mode' && input.mode !== undefined) {\n vars[v.name] = input.mode;\n } else {\n vars[v.name] = v.default ?? '';\n }\n }\n\n let selectedBlocks: string[];\n if (input.blocks) {\n selectedBlocks = input.blocks\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n } else {\n selectedBlocks = entity.defaults?.blocks ?? entity.blocks.map((b) => b.id);\n }\n\n try {\n resolveBlocks(entity.blocks, selectedBlocks);\n } catch (e) {\n ctx.ui?.error?.((e as Error).message);\n return { exitCode: 1 };\n }\n\n for (const blockId of selectedBlocks) {\n const block = entity.blocks.find((b) => b.id === blockId);\n for (const v of block?.variables ?? []) {\n vars[v.name] = v.default ?? '';\n }\n }\n\n const scope = (vars.scope as string) ?? input.scope ?? '';\n const mode = (input.mode ?? (vars.mode as string) ?? 'in-workspace') as\n | 'in-workspace'\n | 'standalone';\n\n const versions = await detectVersions();\n\n const context: RenderContext = {\n name: nameArg,\n scope,\n vars,\n blocks: selectedBlocks,\n mode,\n versions,\n };\n\n const result = await build({\n entity,\n selectedBlockIds: selectedBlocks,\n context,\n });\n\n const outRoot = input.out ?? result.outRoot;\n const state = await inspectTarget(outRoot);\n\n if (input['dry-run']) {\n ctx.ui?.success?.('Dry run complete', {\n title: 'scaffold run — dry run',\n sections: [\n { header: 'Would write to', items: [outRoot] },\n { header: 'Files', items: formatTree(result.files).split('\\n').filter(Boolean) },\n ],\n });\n return { exitCode: 0, result: { outRoot, files: result.files.length } };\n }\n\n if (state.exists && !state.empty) {\n if (input.force) {\n ctx.ui?.warn?.(`--force: overwriting ${outRoot}`);\n } else {\n ctx.ui?.error?.(`Output directory \"${outRoot}\" already exists and is not empty.`);\n ctx.ui?.info?.('Use --force to overwrite.');\n return { exitCode: 1 };\n }\n }\n\n await writeFiles({ outRoot, files: result.files, force: input.force });\n\n const entryPkgDir =\n entityArg === 'plugin'\n ? `${outRoot}/packages/${nameArg}-entry`\n : outRoot;\n\n const linked = await linkWithMarketplace(entryPkgDir, ctx.cwd);\n\n const registrationNote =\n linked === 'ok'\n ? 'Registered in .kb/marketplace.lock'\n : `Not auto-registered — run: kb marketplace plugins link ${entryPkgDir}`;\n\n const timing = Date.now() - start;\n\n ctx.ui?.success?.(`Scaffolded ${entityArg} \"${nameArg}\"`, {\n title: 'scaffold run',\n sections: [\n {\n header: 'Output',\n items: [outRoot],\n },\n {\n header: 'Marketplace',\n items: [registrationNote],\n },\n {\n header: 'Next steps',\n items: [\n `cd ${outRoot}`,\n 'pnpm install',\n 'pnpm -w build',\n `pnpm kb ${nameArg} hello # try it`,\n `pnpm kb scaffold doctor --path ${dirname(outRoot)}`,\n ],\n },\n ],\n timing,\n });\n\n return {\n exitCode: 0,\n result: { outRoot, files: result.files.length },\n };\n}\n\ntype LinkOutcome = 'ok' | 'no-shell' | 'failed';\n\n/**\n * Register a scaffolded plugin in the project-scope marketplace.lock.\n * Uses projectRoot (ctx.cwd) so the lock lives alongside the project config.\n */\nasync function linkWithMarketplace(entryPkgDir: string, projectRoot: string): Promise<LinkOutcome> {\n try {\n const lockPath = resolve(projectRoot, '.kb', 'marketplace.lock');\n\n let lock: { schema: string; installed: Record<string, unknown> };\n try {\n lock = JSON.parse(await readFile(lockPath, 'utf8'));\n } catch {\n lock = { schema: 'kb.marketplace/2', installed: {} };\n }\n\n const pkgJsonPath = resolve(entryPkgDir, 'package.json');\n const pkgRaw = await readFile(pkgJsonPath, 'utf8');\n const pkg = JSON.parse(pkgRaw) as { name?: string; version?: string; kb?: { manifest?: string } };\n if (!pkg.name) return 'failed';\n\n const hash = createHash('sha256').update(pkgRaw).digest('base64');\n const provides: string[] = ['plugin', 'cli-command'];\n const resolvedPath = relative(projectRoot, resolve(entryPkgDir));\n const pluginId = pkg.name.replace(/-entry$/, '');\n\n lock.installed[pluginId] = {\n version: pkg.version ?? '0.1.0',\n integrity: `sha256-${hash}`,\n resolvedPath: `./${resolvedPath}`,\n installedAt: new Date().toISOString(),\n source: 'local',\n primaryKind: 'plugin',\n provides,\n enabled: true,\n };\n\n await mkdir(dirname(lockPath), { recursive: true });\n await writeFile(lockPath, JSON.stringify(lock, null, 2));\n return 'ok';\n } catch {\n return 'failed';\n }\n}\n\nexport default defineCommand({\n id: 'scaffold:run',\n description: 'Scaffold <entity> <name> from blocks',\n\n handler: {\n execute: runDefault,\n },\n});\n"]}
1
+ {"version":3,"sources":["../../src/commands/scaffold.ts"],"names":[],"mappings":"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","file":"scaffold.js","sourcesContent":["import { dirname, resolve, relative } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { createRequire } from 'node:module';\nimport { readFile, writeFile, mkdir } from 'node:fs/promises';\nimport { createHash } from 'node:crypto';\nimport {\n defineCommand,\n validationError,\n handleError,\n type PluginContextV3,\n type CLIInput,\n} from '@kb-labs/sdk';\nimport {\n build,\n inspectTarget,\n formatTree,\n listEntities,\n loadEntity,\n resolveBlocks,\n runValidator,\n writeFiles,\n} from '@kb-labs/scaffold-core';\nimport type {\n RenderContext,\n} from '@kb-labs/scaffold-contracts';\nimport { formatCommandHelp } from '@kb-labs/shared-cli-ui';\n\nconst here = dirname(fileURLToPath(import.meta.url));\nconst TEMPLATES_ROOT = resolve(here, '..', '..', 'templates');\n\nconst SCAFFOLD_FLAGS = [\n { name: 'scope', description: 'npm scope, e.g. @my-org' },\n { name: 'blocks', description: 'comma-separated block ids to include' },\n { name: 'out', description: 'custom output directory' },\n { name: 'force', description: 'overwrite existing output directory' },\n { name: 'dry-run', description: 'preview files without writing' },\n];\n\ninterface ScaffoldFlags {\n blocks?: string;\n force?: boolean;\n 'dry-run'?: boolean;\n out?: string;\n scope?: string;\n mode?: string;\n}\n\ntype ScaffoldResult = {\n exitCode: number;\n result?: { outRoot: string; files: number };\n};\n\nasync function readVersion(pkgPath: string): Promise<string> {\n try {\n const raw = await readFile(pkgPath, 'utf8');\n const parsed = JSON.parse(raw) as { version?: string };\n return parsed.version ?? '0.0.0';\n } catch {\n return '0.0.0';\n }\n}\n\nconst FALLBACK_VERSIONS = {\n sdk: '2.18.0',\n devkit: '2.28.0',\n commandKit: '2.30.0',\n} as const;\n\nasync function detectVersions(): Promise<Record<string, string>> {\n const req = createRequire(import.meta.url);\n const readPkg = async (spec: string, fallback: string): Promise<string> => {\n try {\n const resolved = req.resolve(spec);\n const v = await readVersion(resolved);\n return v === '0.0.0' ? fallback : v;\n } catch {\n return fallback;\n }\n };\n const [sdk, devkit, commandKit] = await Promise.all([\n readPkg('@kb-labs/sdk/package.json', FALLBACK_VERSIONS.sdk),\n readPkg('@kb-labs/devkit/package.json', FALLBACK_VERSIONS.devkit),\n readPkg('@kb-labs/shared-command-kit/package.json', FALLBACK_VERSIONS.commandKit),\n ]);\n return { sdk, devkit, commandKit };\n}\n\nfunction parsePositional(argv: string[] | undefined): {\n entity?: string;\n name?: string;\n} {\n if (!argv || argv.length === 0) { return {}; }\n const positional = argv.filter((a) => !a.startsWith('-'));\n return { entity: positional[0], name: positional[1] };\n}\n\nasync function runDefault(\n ctx: PluginContextV3,\n rawInput: CLIInput<ScaffoldFlags>,\n): Promise<ScaffoldResult> {\n const start = Date.now();\n const input = { ...rawInput.flags, argv: rawInput.argv };\n\n const { entity: entityArg, name: nameArg } = parsePositional(input.argv);\n\n const available = await listEntities(TEMPLATES_ROOT);\n if (available.length === 0) {\n validationError(ctx, 'No scaffold templates found. Reinstall @kb-labs/scaffold.');\n return { exitCode: 1 };\n }\n\n if (!entityArg) {\n ctx.ui?.write?.(formatCommandHelp({\n title: 'kb scaffold run',\n description: 'Scaffold a new plugin, adapter, or other entity from a template.',\n examples: available.map((e) => `kb scaffold run ${e} my-${e}`),\n flags: SCAFFOLD_FLAGS,\n }) + '\\n');\n ctx.ui?.info?.(`Available entities: ${available.join(', ')}`);\n return { exitCode: 1 };\n }\n\n if (!available.includes(entityArg)) {\n validationError(ctx, `Unknown entity \"${entityArg}\". Available: ${available.join(', ')}`);\n return { exitCode: 1 };\n }\n\n if (!nameArg) {\n ctx.ui?.write?.(formatCommandHelp({\n title: `kb scaffold run ${entityArg} <name>`,\n description: `Scaffold a new ${entityArg}. Provide a name to continue.`,\n examples: [`kb scaffold run ${entityArg} my-${entityArg}`],\n flags: SCAFFOLD_FLAGS,\n }) + '\\n');\n return { exitCode: 1 };\n }\n\n const problem = runValidator('npmName', nameArg);\n if (problem) {\n validationError(ctx, `Invalid name \"${nameArg}\": ${problem}`, 'Name must be lowercase, no spaces, valid npm package name.');\n return { exitCode: 1 };\n }\n\n const entity = await loadEntity(TEMPLATES_ROOT, entityArg);\n\n const vars: Record<string, unknown> = {};\n for (const v of entity.variables) {\n if (v.name === 'scope' && input.scope !== undefined) {\n vars[v.name] = input.scope;\n } else if (v.name === 'mode' && input.mode !== undefined) {\n vars[v.name] = input.mode;\n } else {\n vars[v.name] = v.default ?? '';\n }\n }\n\n let selectedBlocks: string[];\n if (input.blocks) {\n selectedBlocks = input.blocks\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n } else {\n selectedBlocks = entity.defaults?.blocks ?? entity.blocks.map((b) => b.id);\n }\n\n try {\n resolveBlocks(entity.blocks, selectedBlocks);\n } catch (e) {\n handleError(ctx, e);\n return { exitCode: 1 };\n }\n\n for (const blockId of selectedBlocks) {\n const block = entity.blocks.find((b) => b.id === blockId);\n for (const v of block?.variables ?? []) {\n vars[v.name] = v.default ?? '';\n }\n }\n\n const scope = (vars.scope as string) ?? input.scope ?? '';\n const mode = (input.mode ?? (vars.mode as string) ?? 'in-workspace') as\n | 'in-workspace'\n | 'standalone';\n\n const versions = await detectVersions();\n\n const context: RenderContext = {\n name: nameArg,\n scope,\n vars,\n blocks: selectedBlocks,\n mode,\n versions,\n };\n\n const result = await build({\n entity,\n selectedBlockIds: selectedBlocks,\n context,\n });\n\n const outRoot = input.out ?? result.outRoot;\n const state = await inspectTarget(outRoot);\n\n if (input['dry-run']) {\n ctx.ui?.success?.('Dry run complete', {\n title: 'scaffold run — dry run',\n sections: [\n { header: 'Would write to', items: [outRoot] },\n { header: 'Files', items: formatTree(result.files).split('\\n').filter(Boolean) },\n ],\n });\n return { exitCode: 0, result: { outRoot, files: result.files.length } };\n }\n\n if (state.exists && !state.empty) {\n if (input.force) {\n ctx.ui?.warn?.(`--force: overwriting ${outRoot}`);\n } else {\n validationError(ctx, `Output directory \"${outRoot}\" already exists and is not empty.`, 'Use --force to overwrite.');\n return { exitCode: 1 };\n }\n }\n\n await writeFiles({ outRoot, files: result.files, force: input.force });\n\n const entryPkgDir =\n entityArg === 'plugin'\n ? `${outRoot}/packages/${nameArg}-entry`\n : outRoot;\n\n const linked = await linkWithMarketplace(entryPkgDir, ctx.cwd);\n\n const registrationNote =\n linked === 'ok'\n ? 'Registered in .kb/marketplace.lock'\n : `Not auto-registered — run: kb marketplace plugins link ${entryPkgDir}`;\n\n const timing = Date.now() - start;\n\n ctx.ui?.success?.(`Scaffolded ${entityArg} \"${nameArg}\"`, {\n title: 'scaffold run',\n sections: [\n {\n header: 'Output',\n items: [outRoot],\n },\n {\n header: 'Marketplace',\n items: [registrationNote],\n },\n {\n header: 'Next steps',\n items: [\n `cd ${outRoot}`,\n 'pnpm install',\n 'pnpm -w build',\n `pnpm kb ${nameArg} hello # try it`,\n `pnpm kb scaffold doctor --path ${dirname(outRoot)}`,\n ],\n },\n ],\n timing,\n });\n\n return {\n exitCode: 0,\n result: { outRoot, files: result.files.length },\n };\n}\n\ntype LinkOutcome = 'ok' | 'no-shell' | 'failed';\n\n/**\n * Register a scaffolded plugin in the project-scope marketplace.lock.\n * Uses projectRoot (ctx.cwd) so the lock lives alongside the project config.\n */\nasync function linkWithMarketplace(entryPkgDir: string, projectRoot: string): Promise<LinkOutcome> {\n try {\n const lockPath = resolve(projectRoot, '.kb', 'marketplace.lock');\n\n let lock: { schema: string; installed: Record<string, unknown> };\n try {\n lock = JSON.parse(await readFile(lockPath, 'utf8'));\n } catch {\n lock = { schema: 'kb.marketplace/2', installed: {} };\n }\n\n const pkgJsonPath = resolve(entryPkgDir, 'package.json');\n const pkgRaw = await readFile(pkgJsonPath, 'utf8');\n const pkg = JSON.parse(pkgRaw) as { name?: string; version?: string; kb?: { manifest?: string } };\n if (!pkg.name) { return 'failed'; }\n\n const hash = createHash('sha256').update(pkgRaw).digest('base64');\n const provides: string[] = ['plugin', 'cli-command'];\n const resolvedPath = relative(projectRoot, resolve(entryPkgDir));\n const pluginId = pkg.name.replace(/-entry$/, '');\n\n lock.installed[pluginId] = {\n version: pkg.version ?? '0.1.0',\n integrity: `sha256-${hash}`,\n resolvedPath: `./${resolvedPath}`,\n installedAt: new Date().toISOString(),\n source: 'local',\n primaryKind: 'plugin',\n provides,\n enabled: true,\n };\n\n await mkdir(dirname(lockPath), { recursive: true });\n await writeFile(lockPath, JSON.stringify(lock, null, 2));\n return 'ok';\n } catch {\n return 'failed';\n }\n}\n\nexport default defineCommand({\n id: 'scaffold:run',\n description: 'Scaffold <entity> <name> from blocks',\n\n handler: {\n execute: runDefault,\n },\n});\n"]}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  export { default as manifest } from './manifest.js';
2
- import '@kb-labs/perm-presets';
2
+ import '@kb-labs/plugin-contracts';
package/dist/index.js CHANGED
@@ -20,20 +20,18 @@ var manifest = {
20
20
  version: "0.1.0",
21
21
  display: {
22
22
  name: "Scaffold",
23
- description: "Generic entity scaffolder for KB Labs plugins, adapters, and friends.",
23
+ description: "Entity scaffolder for plugins and adapters",
24
24
  tags: ["scaffold", "generator", "dx"]
25
25
  },
26
26
  cli: {
27
27
  groupMeta: [
28
- { name: "scaffold", describe: "Scaffold KB Labs entities" }
28
+ { path: "scaffold", describe: "Scaffold KB Labs entities" }
29
29
  ],
30
30
  commands: [
31
31
  {
32
- id: "run",
33
- group: "scaffold",
32
+ path: "scaffold run",
34
33
  describe: "Scaffold <entity> <name> from blocks",
35
34
  handler: "./commands/scaffold.js#default",
36
- handlerPath: "./commands/scaffold.js",
37
35
  flags: [
38
36
  {
39
37
  name: "blocks",
@@ -80,11 +78,9 @@ var manifest = {
80
78
  ]
81
79
  },
82
80
  {
83
- id: "doctor",
84
- group: "scaffold",
81
+ path: "scaffold doctor",
85
82
  describe: "Scan user plugins for common issues",
86
83
  handler: "./commands/doctor.js#default",
87
- handlerPath: "./commands/doctor.js",
88
84
  flags: [
89
85
  {
90
86
  name: "path",
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/manifest.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { combinePermissions, kbPlatformPreset } from '@kb-labs/sdk';\n\nconst permissions = combinePermissions()\n .with(kbPlatformPreset)\n .withFs({\n mode: 'readWrite',\n allow: [\n '.kb/plugins/**',\n '.kb/marketplace.lock',\n 'plugins/**',\n 'adapters/**',\n ],\n })\n .withShell({\n // Needed to call `kb marketplace plugins link` after scaffolding,\n // so the generated plugin lands in .kb/marketplace.lock automatically.\n allow: ['kb'],\n })\n .withQuotas({ timeoutMs: 120000, memoryMb: 256 })\n .build();\n\nconst manifest = {\n schema: 'kb.plugin/3',\n id: '@kb-labs/scaffold',\n version: '0.1.0',\n display: {\n name: 'Scaffold',\n description:\n 'Generic entity scaffolder for KB Labs plugins, adapters, and friends.',\n tags: ['scaffold', 'generator', 'dx'],\n },\n cli: {\n groupMeta: [\n { name: 'scaffold', describe: 'Scaffold KB Labs entities' },\n ],\n commands: [\n {\n id: 'run',\n group: 'scaffold',\n describe: 'Scaffold <entity> <name> from blocks',\n handler: './commands/scaffold.js#default',\n handlerPath: './commands/scaffold.js',\n flags: [\n {\n name: 'blocks',\n type: 'string',\n description: 'Comma-separated block ids',\n },\n {\n name: 'yes',\n alias: 'y',\n type: 'boolean',\n description: 'Accept defaults and skip prompts',\n },\n {\n name: 'force',\n type: 'boolean',\n description: 'Overwrite non-empty target directory',\n },\n {\n name: 'dry-run',\n type: 'boolean',\n description: 'Print the tree that would be written and exit',\n },\n {\n name: 'out',\n type: 'string',\n description: 'Override the output directory',\n },\n {\n name: 'scope',\n type: 'string',\n description: 'npm scope for the generated package',\n },\n {\n name: 'mode',\n type: 'string',\n description:\n 'Layout mode: \"in-workspace\" (workspace:* deps) or \"standalone\" (semver deps + pnpm-workspace.yaml)',\n },\n ],\n examples: [\n 'kb scaffold run plugin my-plugin',\n 'kb scaffold run adapter my-llm --blocks base',\n 'kb scaffold run plugin ui --scope @acme --yes',\n 'kb scaffold run plugin demo --dry-run',\n ],\n },\n {\n id: 'doctor',\n group: 'scaffold',\n describe: 'Scan user plugins for common issues',\n handler: './commands/doctor.js#default',\n handlerPath: './commands/doctor.js',\n flags: [\n {\n name: 'path',\n type: 'string',\n description: 'Path to scan (default: .kb/plugins)',\n },\n {\n name: 'json',\n type: 'boolean',\n description: 'Emit JSON instead of text',\n },\n ],\n examples: [\n 'kb scaffold doctor',\n 'kb scaffold doctor --path ./plugins',\n 'kb scaffold doctor --json',\n ],\n },\n ],\n },\n permissions,\n} as const;\n\nexport default manifest;\n"]}
1
+ {"version":3,"sources":["../src/manifest.ts"],"names":[],"mappings":";;;AAEA,IAAM,cAAc,kBAAA,EAAmB,CACpC,IAAA,CAAK,gBAAgB,EACrB,MAAA,CAAO;AAAA,EACN,IAAA,EAAM,WAAA;AAAA,EACN,KAAA,EAAO;AAAA,IACL,gBAAA;AAAA,IACA,sBAAA;AAAA,IACA,YAAA;AAAA,IACA;AAAA;AAEJ,CAAC,EACA,SAAA,CAAU;AAAA;AAAA;AAAA,EAGT,KAAA,EAAO,CAAC,IAAI;AACd,CAAC,CAAA,CACA,WAAW,EAAE,SAAA,EAAW,MAAQ,QAAA,EAAU,GAAA,EAAK,CAAA,CAC/C,KAAA,EAAM;AAET,IAAM,QAAA,GAAW;AAAA,EACf,MAAA,EAAQ,aAAA;AAAA,EACR,EAAA,EAAI,mBAAA;AAAA,EACJ,OAAA,EAAS,OAAA;AAAA,EACT,OAAA,EAAS;AAAA,IACP,IAAA,EAAM,UAAA;AAAA,IACN,WAAA,EAAa,4CAAA;AAAA,IACb,IAAA,EAAM,CAAC,UAAA,EAAY,WAAA,EAAa,IAAI;AAAA,GACtC;AAAA,EACA,GAAA,EAAK;AAAA,IACH,SAAA,EAAW;AAAA,MACT,EAAE,IAAA,EAAM,UAAA,EAAY,QAAA,EAAU,2BAAA;AAA4B,KAC5D;AAAA,IACA,QAAA,EAAU;AAAA,MACR;AAAA,QACE,IAAA,EAAM,cAAA;AAAA,QACN,QAAA,EAAU,sCAAA;AAAA,QACV,OAAA,EAAS,gCAAA;AAAA,QACT,KAAA,EAAO;AAAA,UACL;AAAA,YACE,IAAA,EAAM,QAAA;AAAA,YACN,IAAA,EAAM,QAAA;AAAA,YACN,WAAA,EAAa;AAAA,WACf;AAAA,UACA;AAAA,YACE,IAAA,EAAM,KAAA;AAAA,YACN,KAAA,EAAO,GAAA;AAAA,YACP,IAAA,EAAM,SAAA;AAAA,YACN,WAAA,EAAa;AAAA,WACf;AAAA,UACA;AAAA,YACE,IAAA,EAAM,OAAA;AAAA,YACN,IAAA,EAAM,SAAA;AAAA,YACN,WAAA,EAAa;AAAA,WACf;AAAA,UACA;AAAA,YACE,IAAA,EAAM,SAAA;AAAA,YACN,IAAA,EAAM,SAAA;AAAA,YACN,WAAA,EAAa;AAAA,WACf;AAAA,UACA;AAAA,YACE,IAAA,EAAM,KAAA;AAAA,YACN,IAAA,EAAM,QAAA;AAAA,YACN,WAAA,EAAa;AAAA,WACf;AAAA,UACA;AAAA,YACE,IAAA,EAAM,OAAA;AAAA,YACN,IAAA,EAAM,QAAA;AAAA,YACN,WAAA,EAAa;AAAA,WACf;AAAA,UACA;AAAA,YACE,IAAA,EAAM,MAAA;AAAA,YACN,IAAA,EAAM,QAAA;AAAA,YACN,WAAA,EACE;AAAA;AACJ,SACF;AAAA,QACA,QAAA,EAAU;AAAA,UACR,kCAAA;AAAA,UACA,8CAAA;AAAA,UACA,+CAAA;AAAA,UACA;AAAA;AACF,OACF;AAAA,MACA;AAAA,QACE,IAAA,EAAM,iBAAA;AAAA,QACN,QAAA,EAAU,qCAAA;AAAA,QACV,OAAA,EAAS,8BAAA;AAAA,QACT,KAAA,EAAO;AAAA,UACL;AAAA,YACE,IAAA,EAAM,MAAA;AAAA,YACN,IAAA,EAAM,QAAA;AAAA,YACN,WAAA,EAAa;AAAA,WACf;AAAA,UACA;AAAA,YACE,IAAA,EAAM,MAAA;AAAA,YACN,IAAA,EAAM,SAAA;AAAA,YACN,WAAA,EAAa;AAAA;AACf,SACF;AAAA,QACA,QAAA,EAAU;AAAA,UACR,oBAAA;AAAA,UACA,qCAAA;AAAA,UACA;AAAA;AACF;AACF;AACF,GACF;AAAA,EACA;AACF,CAAA;AAEA,IAAO,gBAAA,GAAQ","file":"index.js","sourcesContent":["import { combinePermissions, kbPlatformPreset } from '@kb-labs/sdk';\n\nconst permissions = combinePermissions()\n .with(kbPlatformPreset)\n .withFs({\n mode: 'readWrite',\n allow: [\n '.kb/plugins/**',\n '.kb/marketplace.lock',\n 'plugins/**',\n 'adapters/**',\n ],\n })\n .withShell({\n // Needed to call `kb marketplace plugins link` after scaffolding,\n // so the generated plugin lands in .kb/marketplace.lock automatically.\n allow: ['kb'],\n })\n .withQuotas({ timeoutMs: 120000, memoryMb: 256 })\n .build();\n\nconst manifest = {\n schema: 'kb.plugin/3',\n id: '@kb-labs/scaffold',\n version: '0.1.0',\n display: {\n name: 'Scaffold',\n description: 'Entity scaffolder for plugins and adapters',\n tags: ['scaffold', 'generator', 'dx'],\n },\n cli: {\n groupMeta: [\n { path: 'scaffold', describe: 'Scaffold KB Labs entities' },\n ],\n commands: [\n {\n path: 'scaffold run',\n describe: 'Scaffold <entity> <name> from blocks',\n handler: './commands/scaffold.js#default',\n flags: [\n {\n name: 'blocks',\n type: 'string',\n description: 'Comma-separated block ids',\n },\n {\n name: 'yes',\n alias: 'y',\n type: 'boolean',\n description: 'Accept defaults and skip prompts',\n },\n {\n name: 'force',\n type: 'boolean',\n description: 'Overwrite non-empty target directory',\n },\n {\n name: 'dry-run',\n type: 'boolean',\n description: 'Print the tree that would be written and exit',\n },\n {\n name: 'out',\n type: 'string',\n description: 'Override the output directory',\n },\n {\n name: 'scope',\n type: 'string',\n description: 'npm scope for the generated package',\n },\n {\n name: 'mode',\n type: 'string',\n description:\n 'Layout mode: \"in-workspace\" (workspace:* deps) or \"standalone\" (semver deps + pnpm-workspace.yaml)',\n },\n ],\n examples: [\n 'kb scaffold run plugin my-plugin',\n 'kb scaffold run adapter my-llm --blocks base',\n 'kb scaffold run plugin ui --scope @acme --yes',\n 'kb scaffold run plugin demo --dry-run',\n ],\n },\n {\n path: 'scaffold doctor',\n describe: 'Scan user plugins for common issues',\n handler: './commands/doctor.js#default',\n flags: [\n {\n name: 'path',\n type: 'string',\n description: 'Path to scan (default: .kb/plugins)',\n },\n {\n name: 'json',\n type: 'boolean',\n description: 'Emit JSON instead of text',\n },\n ],\n examples: [\n 'kb scaffold doctor',\n 'kb scaffold doctor --path ./plugins',\n 'kb scaffold doctor --json',\n ],\n },\n ],\n },\n permissions,\n} as const;\n\nexport default manifest;\n"]}
@@ -1,4 +1,4 @@
1
- import * as _kb_labs_perm_presets from '@kb-labs/perm-presets';
1
+ import * as _kb_labs_plugin_contracts from '@kb-labs/plugin-contracts';
2
2
 
3
3
  declare const manifest: {
4
4
  readonly schema: "kb.plugin/3";
@@ -6,20 +6,18 @@ declare const manifest: {
6
6
  readonly version: "0.1.0";
7
7
  readonly display: {
8
8
  readonly name: "Scaffold";
9
- readonly description: "Generic entity scaffolder for KB Labs plugins, adapters, and friends.";
9
+ readonly description: "Entity scaffolder for plugins and adapters";
10
10
  readonly tags: readonly ["scaffold", "generator", "dx"];
11
11
  };
12
12
  readonly cli: {
13
13
  readonly groupMeta: readonly [{
14
- readonly name: "scaffold";
14
+ readonly path: "scaffold";
15
15
  readonly describe: "Scaffold KB Labs entities";
16
16
  }];
17
17
  readonly commands: readonly [{
18
- readonly id: "run";
19
- readonly group: "scaffold";
18
+ readonly path: "scaffold run";
20
19
  readonly describe: "Scaffold <entity> <name> from blocks";
21
20
  readonly handler: "./commands/scaffold.js#default";
22
- readonly handlerPath: "./commands/scaffold.js";
23
21
  readonly flags: readonly [{
24
22
  readonly name: "blocks";
25
23
  readonly type: "string";
@@ -52,11 +50,9 @@ declare const manifest: {
52
50
  }];
53
51
  readonly examples: readonly ["kb scaffold run plugin my-plugin", "kb scaffold run adapter my-llm --blocks base", "kb scaffold run plugin ui --scope @acme --yes", "kb scaffold run plugin demo --dry-run"];
54
52
  }, {
55
- readonly id: "doctor";
56
- readonly group: "scaffold";
53
+ readonly path: "scaffold doctor";
57
54
  readonly describe: "Scan user plugins for common issues";
58
55
  readonly handler: "./commands/doctor.js#default";
59
- readonly handlerPath: "./commands/doctor.js";
60
56
  readonly flags: readonly [{
61
57
  readonly name: "path";
62
58
  readonly type: "string";
@@ -69,7 +65,7 @@ declare const manifest: {
69
65
  readonly examples: readonly ["kb scaffold doctor", "kb scaffold doctor --path ./plugins", "kb scaffold doctor --json"];
70
66
  }];
71
67
  };
72
- readonly permissions: _kb_labs_perm_presets.RuntimePermissionSpec;
68
+ readonly permissions: _kb_labs_plugin_contracts.PermissionSpec;
73
69
  };
74
70
 
75
71
  export { manifest as default };
package/dist/manifest.js CHANGED
@@ -20,20 +20,18 @@ var manifest = {
20
20
  version: "0.1.0",
21
21
  display: {
22
22
  name: "Scaffold",
23
- description: "Generic entity scaffolder for KB Labs plugins, adapters, and friends.",
23
+ description: "Entity scaffolder for plugins and adapters",
24
24
  tags: ["scaffold", "generator", "dx"]
25
25
  },
26
26
  cli: {
27
27
  groupMeta: [
28
- { name: "scaffold", describe: "Scaffold KB Labs entities" }
28
+ { path: "scaffold", describe: "Scaffold KB Labs entities" }
29
29
  ],
30
30
  commands: [
31
31
  {
32
- id: "run",
33
- group: "scaffold",
32
+ path: "scaffold run",
34
33
  describe: "Scaffold <entity> <name> from blocks",
35
34
  handler: "./commands/scaffold.js#default",
36
- handlerPath: "./commands/scaffold.js",
37
35
  flags: [
38
36
  {
39
37
  name: "blocks",
@@ -80,11 +78,9 @@ var manifest = {
80
78
  ]
81
79
  },
82
80
  {
83
- id: "doctor",
84
- group: "scaffold",
81
+ path: "scaffold doctor",
85
82
  describe: "Scan user plugins for common issues",
86
83
  handler: "./commands/doctor.js#default",
87
- handlerPath: "./commands/doctor.js",
88
84
  flags: [
89
85
  {
90
86
  name: "path",
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/manifest.ts"],"names":[],"mappings":";;;AAEA,IAAM,cAAc,kBAAA,EAAmB,CACpC,IAAA,CAAK,gBAAgB,EACrB,MAAA,CAAO;AAAA,EACN,IAAA,EAAM,WAAA;AAAA,EACN,KAAA,EAAO;AAAA,IACL,gBAAA;AAAA,IACA,sBAAA;AAAA,IACA,YAAA;AAAA,IACA;AAAA;AAEJ,CAAC,EACA,SAAA,CAAU;AAAA;AAAA;AAAA,EAGT,KAAA,EAAO,CAAC,IAAI;AACd,CAAC,CAAA,CACA,WAAW,EAAE,SAAA,EAAW,MAAQ,QAAA,EAAU,GAAA,EAAK,CAAA,CAC/C,KAAA,EAAM;AAET,IAAM,QAAA,GAAW;AAAA,EACf,MAAA,EAAQ,aAAA;AAAA,EACR,EAAA,EAAI,mBAAA;AAAA,EACJ,OAAA,EAAS,OAAA;AAAA,EACT,OAAA,EAAS;AAAA,IACP,IAAA,EAAM,UAAA;AAAA,IACN,WAAA,EACE,uEAAA;AAAA,IACF,IAAA,EAAM,CAAC,UAAA,EAAY,WAAA,EAAa,IAAI;AAAA,GACtC;AAAA,EACA,GAAA,EAAK;AAAA,IACH,SAAA,EAAW;AAAA,MACT,EAAE,IAAA,EAAM,UAAA,EAAY,QAAA,EAAU,2BAAA;AAA4B,KAC5D;AAAA,IACA,QAAA,EAAU;AAAA,MACR;AAAA,QACE,EAAA,EAAI,KAAA;AAAA,QACJ,KAAA,EAAO,UAAA;AAAA,QACP,QAAA,EAAU,sCAAA;AAAA,QACV,OAAA,EAAS,gCAAA;AAAA,QACT,WAAA,EAAa,wBAAA;AAAA,QACb,KAAA,EAAO;AAAA,UACL;AAAA,YACE,IAAA,EAAM,QAAA;AAAA,YACN,IAAA,EAAM,QAAA;AAAA,YACN,WAAA,EAAa;AAAA,WACf;AAAA,UACA;AAAA,YACE,IAAA,EAAM,KAAA;AAAA,YACN,KAAA,EAAO,GAAA;AAAA,YACP,IAAA,EAAM,SAAA;AAAA,YACN,WAAA,EAAa;AAAA,WACf;AAAA,UACA;AAAA,YACE,IAAA,EAAM,OAAA;AAAA,YACN,IAAA,EAAM,SAAA;AAAA,YACN,WAAA,EAAa;AAAA,WACf;AAAA,UACA;AAAA,YACE,IAAA,EAAM,SAAA;AAAA,YACN,IAAA,EAAM,SAAA;AAAA,YACN,WAAA,EAAa;AAAA,WACf;AAAA,UACA;AAAA,YACE,IAAA,EAAM,KAAA;AAAA,YACN,IAAA,EAAM,QAAA;AAAA,YACN,WAAA,EAAa;AAAA,WACf;AAAA,UACA;AAAA,YACE,IAAA,EAAM,OAAA;AAAA,YACN,IAAA,EAAM,QAAA;AAAA,YACN,WAAA,EAAa;AAAA,WACf;AAAA,UACA;AAAA,YACE,IAAA,EAAM,MAAA;AAAA,YACN,IAAA,EAAM,QAAA;AAAA,YACN,WAAA,EACE;AAAA;AACJ,SACF;AAAA,QACA,QAAA,EAAU;AAAA,UACR,kCAAA;AAAA,UACA,8CAAA;AAAA,UACA,+CAAA;AAAA,UACA;AAAA;AACF,OACF;AAAA,MACA;AAAA,QACE,EAAA,EAAI,QAAA;AAAA,QACJ,KAAA,EAAO,UAAA;AAAA,QACP,QAAA,EAAU,qCAAA;AAAA,QACV,OAAA,EAAS,8BAAA;AAAA,QACT,WAAA,EAAa,sBAAA;AAAA,QACb,KAAA,EAAO;AAAA,UACL;AAAA,YACE,IAAA,EAAM,MAAA;AAAA,YACN,IAAA,EAAM,QAAA;AAAA,YACN,WAAA,EAAa;AAAA,WACf;AAAA,UACA;AAAA,YACE,IAAA,EAAM,MAAA;AAAA,YACN,IAAA,EAAM,SAAA;AAAA,YACN,WAAA,EAAa;AAAA;AACf,SACF;AAAA,QACA,QAAA,EAAU;AAAA,UACR,oBAAA;AAAA,UACA,qCAAA;AAAA,UACA;AAAA;AACF;AACF;AACF,GACF;AAAA,EACA;AACF,CAAA;AAEA,IAAO,gBAAA,GAAQ","file":"manifest.js","sourcesContent":["import { combinePermissions, kbPlatformPreset } from '@kb-labs/sdk';\n\nconst permissions = combinePermissions()\n .with(kbPlatformPreset)\n .withFs({\n mode: 'readWrite',\n allow: [\n '.kb/plugins/**',\n '.kb/marketplace.lock',\n 'plugins/**',\n 'adapters/**',\n ],\n })\n .withShell({\n // Needed to call `kb marketplace plugins link` after scaffolding,\n // so the generated plugin lands in .kb/marketplace.lock automatically.\n allow: ['kb'],\n })\n .withQuotas({ timeoutMs: 120000, memoryMb: 256 })\n .build();\n\nconst manifest = {\n schema: 'kb.plugin/3',\n id: '@kb-labs/scaffold',\n version: '0.1.0',\n display: {\n name: 'Scaffold',\n description:\n 'Generic entity scaffolder for KB Labs plugins, adapters, and friends.',\n tags: ['scaffold', 'generator', 'dx'],\n },\n cli: {\n groupMeta: [\n { name: 'scaffold', describe: 'Scaffold KB Labs entities' },\n ],\n commands: [\n {\n id: 'run',\n group: 'scaffold',\n describe: 'Scaffold <entity> <name> from blocks',\n handler: './commands/scaffold.js#default',\n handlerPath: './commands/scaffold.js',\n flags: [\n {\n name: 'blocks',\n type: 'string',\n description: 'Comma-separated block ids',\n },\n {\n name: 'yes',\n alias: 'y',\n type: 'boolean',\n description: 'Accept defaults and skip prompts',\n },\n {\n name: 'force',\n type: 'boolean',\n description: 'Overwrite non-empty target directory',\n },\n {\n name: 'dry-run',\n type: 'boolean',\n description: 'Print the tree that would be written and exit',\n },\n {\n name: 'out',\n type: 'string',\n description: 'Override the output directory',\n },\n {\n name: 'scope',\n type: 'string',\n description: 'npm scope for the generated package',\n },\n {\n name: 'mode',\n type: 'string',\n description:\n 'Layout mode: \"in-workspace\" (workspace:* deps) or \"standalone\" (semver deps + pnpm-workspace.yaml)',\n },\n ],\n examples: [\n 'kb scaffold run plugin my-plugin',\n 'kb scaffold run adapter my-llm --blocks base',\n 'kb scaffold run plugin ui --scope @acme --yes',\n 'kb scaffold run plugin demo --dry-run',\n ],\n },\n {\n id: 'doctor',\n group: 'scaffold',\n describe: 'Scan user plugins for common issues',\n handler: './commands/doctor.js#default',\n handlerPath: './commands/doctor.js',\n flags: [\n {\n name: 'path',\n type: 'string',\n description: 'Path to scan (default: .kb/plugins)',\n },\n {\n name: 'json',\n type: 'boolean',\n description: 'Emit JSON instead of text',\n },\n ],\n examples: [\n 'kb scaffold doctor',\n 'kb scaffold doctor --path ./plugins',\n 'kb scaffold doctor --json',\n ],\n },\n ],\n },\n permissions,\n} as const;\n\nexport default manifest;\n"]}
1
+ {"version":3,"sources":["../src/manifest.ts"],"names":[],"mappings":";;;AAEA,IAAM,cAAc,kBAAA,EAAmB,CACpC,IAAA,CAAK,gBAAgB,EACrB,MAAA,CAAO;AAAA,EACN,IAAA,EAAM,WAAA;AAAA,EACN,KAAA,EAAO;AAAA,IACL,gBAAA;AAAA,IACA,sBAAA;AAAA,IACA,YAAA;AAAA,IACA;AAAA;AAEJ,CAAC,EACA,SAAA,CAAU;AAAA;AAAA;AAAA,EAGT,KAAA,EAAO,CAAC,IAAI;AACd,CAAC,CAAA,CACA,WAAW,EAAE,SAAA,EAAW,MAAQ,QAAA,EAAU,GAAA,EAAK,CAAA,CAC/C,KAAA,EAAM;AAET,IAAM,QAAA,GAAW;AAAA,EACf,MAAA,EAAQ,aAAA;AAAA,EACR,EAAA,EAAI,mBAAA;AAAA,EACJ,OAAA,EAAS,OAAA;AAAA,EACT,OAAA,EAAS;AAAA,IACP,IAAA,EAAM,UAAA;AAAA,IACN,WAAA,EAAa,4CAAA;AAAA,IACb,IAAA,EAAM,CAAC,UAAA,EAAY,WAAA,EAAa,IAAI;AAAA,GACtC;AAAA,EACA,GAAA,EAAK;AAAA,IACH,SAAA,EAAW;AAAA,MACT,EAAE,IAAA,EAAM,UAAA,EAAY,QAAA,EAAU,2BAAA;AAA4B,KAC5D;AAAA,IACA,QAAA,EAAU;AAAA,MACR;AAAA,QACE,IAAA,EAAM,cAAA;AAAA,QACN,QAAA,EAAU,sCAAA;AAAA,QACV,OAAA,EAAS,gCAAA;AAAA,QACT,KAAA,EAAO;AAAA,UACL;AAAA,YACE,IAAA,EAAM,QAAA;AAAA,YACN,IAAA,EAAM,QAAA;AAAA,YACN,WAAA,EAAa;AAAA,WACf;AAAA,UACA;AAAA,YACE,IAAA,EAAM,KAAA;AAAA,YACN,KAAA,EAAO,GAAA;AAAA,YACP,IAAA,EAAM,SAAA;AAAA,YACN,WAAA,EAAa;AAAA,WACf;AAAA,UACA;AAAA,YACE,IAAA,EAAM,OAAA;AAAA,YACN,IAAA,EAAM,SAAA;AAAA,YACN,WAAA,EAAa;AAAA,WACf;AAAA,UACA;AAAA,YACE,IAAA,EAAM,SAAA;AAAA,YACN,IAAA,EAAM,SAAA;AAAA,YACN,WAAA,EAAa;AAAA,WACf;AAAA,UACA;AAAA,YACE,IAAA,EAAM,KAAA;AAAA,YACN,IAAA,EAAM,QAAA;AAAA,YACN,WAAA,EAAa;AAAA,WACf;AAAA,UACA;AAAA,YACE,IAAA,EAAM,OAAA;AAAA,YACN,IAAA,EAAM,QAAA;AAAA,YACN,WAAA,EAAa;AAAA,WACf;AAAA,UACA;AAAA,YACE,IAAA,EAAM,MAAA;AAAA,YACN,IAAA,EAAM,QAAA;AAAA,YACN,WAAA,EACE;AAAA;AACJ,SACF;AAAA,QACA,QAAA,EAAU;AAAA,UACR,kCAAA;AAAA,UACA,8CAAA;AAAA,UACA,+CAAA;AAAA,UACA;AAAA;AACF,OACF;AAAA,MACA;AAAA,QACE,IAAA,EAAM,iBAAA;AAAA,QACN,QAAA,EAAU,qCAAA;AAAA,QACV,OAAA,EAAS,8BAAA;AAAA,QACT,KAAA,EAAO;AAAA,UACL;AAAA,YACE,IAAA,EAAM,MAAA;AAAA,YACN,IAAA,EAAM,QAAA;AAAA,YACN,WAAA,EAAa;AAAA,WACf;AAAA,UACA;AAAA,YACE,IAAA,EAAM,MAAA;AAAA,YACN,IAAA,EAAM,SAAA;AAAA,YACN,WAAA,EAAa;AAAA;AACf,SACF;AAAA,QACA,QAAA,EAAU;AAAA,UACR,oBAAA;AAAA,UACA,qCAAA;AAAA,UACA;AAAA;AACF;AACF;AACF,GACF;AAAA,EACA;AACF,CAAA;AAEA,IAAO,gBAAA,GAAQ","file":"manifest.js","sourcesContent":["import { combinePermissions, kbPlatformPreset } from '@kb-labs/sdk';\n\nconst permissions = combinePermissions()\n .with(kbPlatformPreset)\n .withFs({\n mode: 'readWrite',\n allow: [\n '.kb/plugins/**',\n '.kb/marketplace.lock',\n 'plugins/**',\n 'adapters/**',\n ],\n })\n .withShell({\n // Needed to call `kb marketplace plugins link` after scaffolding,\n // so the generated plugin lands in .kb/marketplace.lock automatically.\n allow: ['kb'],\n })\n .withQuotas({ timeoutMs: 120000, memoryMb: 256 })\n .build();\n\nconst manifest = {\n schema: 'kb.plugin/3',\n id: '@kb-labs/scaffold',\n version: '0.1.0',\n display: {\n name: 'Scaffold',\n description: 'Entity scaffolder for plugins and adapters',\n tags: ['scaffold', 'generator', 'dx'],\n },\n cli: {\n groupMeta: [\n { path: 'scaffold', describe: 'Scaffold KB Labs entities' },\n ],\n commands: [\n {\n path: 'scaffold run',\n describe: 'Scaffold <entity> <name> from blocks',\n handler: './commands/scaffold.js#default',\n flags: [\n {\n name: 'blocks',\n type: 'string',\n description: 'Comma-separated block ids',\n },\n {\n name: 'yes',\n alias: 'y',\n type: 'boolean',\n description: 'Accept defaults and skip prompts',\n },\n {\n name: 'force',\n type: 'boolean',\n description: 'Overwrite non-empty target directory',\n },\n {\n name: 'dry-run',\n type: 'boolean',\n description: 'Print the tree that would be written and exit',\n },\n {\n name: 'out',\n type: 'string',\n description: 'Override the output directory',\n },\n {\n name: 'scope',\n type: 'string',\n description: 'npm scope for the generated package',\n },\n {\n name: 'mode',\n type: 'string',\n description:\n 'Layout mode: \"in-workspace\" (workspace:* deps) or \"standalone\" (semver deps + pnpm-workspace.yaml)',\n },\n ],\n examples: [\n 'kb scaffold run plugin my-plugin',\n 'kb scaffold run adapter my-llm --blocks base',\n 'kb scaffold run plugin ui --scope @acme --yes',\n 'kb scaffold run plugin demo --dry-run',\n ],\n },\n {\n path: 'scaffold doctor',\n describe: 'Scan user plugins for common issues',\n handler: './commands/doctor.js#default',\n flags: [\n {\n name: 'path',\n type: 'string',\n description: 'Path to scan (default: .kb/plugins)',\n },\n {\n name: 'json',\n type: 'boolean',\n description: 'Emit JSON instead of text',\n },\n ],\n examples: [\n 'kb scaffold doctor',\n 'kb scaffold doctor --path ./plugins',\n 'kb scaffold doctor --json',\n ],\n },\n ],\n },\n permissions,\n} as const;\n\nexport default manifest;\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kb-labs/scaffold",
3
3
  "description": "Generic entity scaffolder for KB Labs plugins, adapters, and friends.",
4
- "version": "2.94.0",
4
+ "version": "2.96.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -21,9 +21,9 @@
21
21
  ],
22
22
  "dependencies": {
23
23
  "@kb-labs/sdk": "2.21.2",
24
- "@kb-labs/scaffold-contracts": "2.94.0",
25
- "@kb-labs/scaffold-core": "2.94.0",
26
- "@kb-labs/shared-cli-ui": "2.94.0"
24
+ "@kb-labs/shared-cli-ui": "2.96.0",
25
+ "@kb-labs/scaffold-core": "2.96.0",
26
+ "@kb-labs/scaffold-contracts": "2.96.0"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/node": "^24.3.3",
@@ -31,9 +31,10 @@
31
31
  "rimraf": "^6.0.1",
32
32
  "tsup": "^8.5.0",
33
33
  "typescript": "^5.6.3",
34
- "vitest": "^3.2.4",
35
- "@kb-labs/devkit": "2.94.0",
36
- "@kb-labs/plugin-contracts": "2.94.0"
34
+ "vitest": "^3.2.6",
35
+ "@kb-labs/devkit": "2.96.0",
36
+ "@kb-labs/plugin-contracts": "2.96.0",
37
+ "@kb-labs/shared-testing-e2e": "2.96.0"
37
38
  },
38
39
  "engines": {
39
40
  "node": ">=20.0.0",
@@ -49,6 +50,7 @@
49
50
  "dev": "tsup --watch",
50
51
  "lint": "eslint src --ext .ts",
51
52
  "test": "vitest run --passWithNoTests",
53
+ "test:cli": "vitest run --config vitest.cli.config.ts --passWithNoTests",
52
54
  "test:watch": "vitest",
53
55
  "type-check": "tsc --noEmit"
54
56
  }
@@ -1,8 +1,131 @@
1
1
  # <%= it.scope ? it.scope + '/' : '' %><%= it.name %>
2
2
 
3
- <%= it.vars.description %>
3
+ > <%= it.vars.description %>
4
4
 
5
- ## Layout
5
+ ![license](https://img.shields.io/badge/license-<%= it.vars.license %>-green)
6
+ ![platform](https://img.shields.io/badge/kb--labs-%3E%3D0.1.0-orange)
7
+
8
+ ---
9
+
10
+ ## Overview
11
+
12
+ <!-- TODO: 2-3 sentences describing what this plugin solves from the user's perspective. -->
13
+
14
+ ---
15
+
16
+ ## Features
17
+
18
+ <!-- TODO: bullet list of key user-facing capabilities -->
19
+ - Feature one
20
+ - Feature two
21
+ - Feature three
22
+
23
+ ---
24
+
25
+ ## Requirements
26
+
27
+ **KB Labs platform** `>= 0.1.0`
28
+
29
+ **Platform services**
30
+
31
+ | Service | Required | Purpose |
32
+ |---------|----------|---------|
33
+ | `llm` | Optional | AI-powered generation |
34
+ | `cache` | Required | Result caching |
35
+ | `storage` | Required | Persistent state |
36
+
37
+ <!-- TODO: add / remove rows based on what the manifest declares -->
38
+
39
+ ---
40
+
41
+ ## Installation
42
+
43
+ ```bash
44
+ pnpm kb marketplace install <%= it.scope ? it.scope + '/' : '' %><%= it.name %>-entry
45
+ ```
46
+
47
+ ---
48
+
49
+ ## Commands
50
+
51
+ ```bash
52
+ kb <%= it.name %> hello # say hi
53
+ kb <%= it.name %> hello --who=World # same, explicit
54
+ ```
55
+
56
+ | Command | Description |
57
+ |---------|-------------|
58
+ | `kb <%= it.name %> hello` | Greet the platform |
59
+
60
+ <!-- TODO: replace with real commands once implemented -->
61
+
62
+ ---
63
+
64
+ ## Configuration
65
+
66
+ ```jsonc
67
+ {
68
+ "<%= it.name %>": {
69
+ // TODO: document config options here
70
+ }
71
+ }
72
+ ```
73
+
74
+ ---
75
+
76
+ <!--
77
+ ## REST API
78
+
79
+ Base path: `/<%= it.name %>`
80
+
81
+ | Method | Path | Description |
82
+ |--------|------|-------------|
83
+ | `GET` | `/<%= it.name %>/status` | Plugin status |
84
+
85
+ Remove the comment markers above/below if this plugin registers REST routes.
86
+ -->
87
+
88
+ <!--
89
+ ## Studio
90
+
91
+ Adds a **<%= it.name %>** section to KB Labs Studio.
92
+
93
+ | Page | Route | Description |
94
+ |------|-------|-------------|
95
+ | Overview | `/p/<%= it.name %>` | Status and quick actions |
96
+
97
+ Remove the comment markers above/below if this plugin adds Studio pages.
98
+ -->
99
+
100
+ ## Permissions
101
+
102
+ | Category | Scope | Reason |
103
+ |----------|-------|--------|
104
+ | Filesystem | `.kb/<%= it.name %>/**` | Plugin state |
105
+ | Platform | `llm`, `cache` | Core functionality |
106
+
107
+ <!-- TODO: update to match manifest permissions -->
108
+
109
+ ---
110
+
111
+ ## Changelog
112
+
113
+ ### 0.1.0
114
+
115
+ - Initial release
116
+
117
+ ---
118
+
119
+ ## License
120
+
121
+ <%= it.vars.license %>
122
+
123
+ ---
124
+
125
+ <details>
126
+ <summary>Development</summary>
127
+
128
+ **Layout**
6
129
 
7
130
  ```
8
131
  packages/
@@ -11,44 +134,31 @@ packages/
11
134
  <%= it.name %>-entry/ # V3 manifest + CLI command handlers
12
135
  ```
13
136
 
14
- All three packages are published together. `entry` depends on `core`, `core`
15
- depends on `contracts`. The runtime discovers the plugin through the `"kb"`
16
- field in `<%= it.name %>-entry/package.json`.
17
-
18
- ## Getting started
137
+ **Getting started**
19
138
 
20
139
  ```bash
21
140
  pnpm install
22
141
  pnpm -w build
23
- pnpm kb <%= it.name %> hello # say hi
24
- pnpm kb <%= it.name %> hello --who=World # same, explicit
142
+ pnpm kb <%= it.name %> hello
25
143
  ```
26
144
 
27
- ## What to edit
145
+ **What to edit**
28
146
 
29
- - **Business logic:** [packages/<%= it.name %>-core/src/hello.ts](./packages/<%= it.name %>-core/src/hello.ts)
30
- Inline comments show how to call the LLM, use the cache, and log through
31
- the platform everything goes through `@kb-labs/sdk`, never through
32
- `@kb-labs/core-*` or `@kb-labs/platform-*`.
33
- - **CLI surface:** [packages/<%= it.name %>-entry/src/manifest.ts](./packages/<%= it.name %>-entry/src/manifest.ts)
34
- Add more commands by appending to `cli.commands[]` and creating a handler
35
- in `src/commands/`.
36
- - **Shared types:** [packages/<%= it.name %>-contracts/src/index.ts](./packages/<%= it.name %>-contracts/src/index.ts)
37
- Input/output shapes go here so `core` and `entry` agree on them.
147
+ - Business logic: `packages/<%= it.name %>-core/src/hello.ts`
148
+ - CLI commands: `packages/<%= it.name %>-entry/src/manifest.ts`
149
+ - Shared types: `packages/<%= it.name %>-contracts/src/index.ts`
38
150
 
39
- ## Linking into a workspace (development)
151
+ **Link into a workspace**
40
152
 
41
153
  ```bash
42
154
  pnpm kb marketplace plugins link .
43
155
  pnpm kb plugins clear-cache
44
156
  ```
45
157
 
46
- ## Health check
158
+ **Health check**
47
159
 
48
160
  ```bash
49
161
  pnpm kb scaffold doctor --path .
50
162
  ```
51
163
 
52
- ## License
53
-
54
- <%= it.vars.license %>
164
+ </details>
@@ -1,5 +1,6 @@
1
1
  import { defineCommand, type CLIInput, type PluginContextV3 } from '@kb-labs/sdk';
2
2
  import { hello } from '<%= it.scope ? it.scope + '/' : '' %><%= it.name %>-core';
3
+ import { handleError, validationError } from '../utils/error.js';
3
4
 
4
5
  type HelloFlags = {
5
6
  who?: string;
@@ -9,10 +10,12 @@ type HelloFlags = {
9
10
  /**
10
11
  * `kb <%= it.name %> hello` — prints a friendly greeting.
11
12
  *
12
- * This handler demonstrates the bare minimum: receive flags, call into
13
- * `-core`, write output via `ctx.ui`. As you grow, reach for the patterns
14
- * sketched inside `<%= it.name %>-core/src/hello.ts` LLM calls, caching,
15
- * structured logging ALL through `@kb-labs/sdk`.
13
+ * This handler demonstrates the standard error-handling pattern:
14
+ * 1. Validate args/flags with validationError() before the try/catch
15
+ * 2. Wrap the core call in try/catch, forward errors via handleError()
16
+ * 3. In JSON mode: success = raw data, errors = { ok: false, error: { ... } }
17
+ *
18
+ * See src/utils/error.ts to extend handleError() for your API error class.
16
19
  */
17
20
  export default defineCommand({
18
21
  id: '<%= it.name %>:hello',
@@ -20,15 +23,27 @@ export default defineCommand({
20
23
 
21
24
  handler: {
22
25
  async execute(ctx: PluginContextV3, input: CLIInput<HelloFlags>) {
23
- const { greeting } = hello({ who: input.flags.who ?? 'World' });
24
-
25
- if (input.flags.json) {
26
- ctx.ui?.json?.({ greeting });
27
- } else {
28
- ctx.ui?.info?.(greeting);
26
+ // 1. Validate synchronous, before try/catch
27
+ if (input.flags.who !== undefined && input.flags.who.trim() === '') {
28
+ validationError(ctx, '--who cannot be empty', input.flags.json);
29
+ return { exitCode: 1, result: null };
29
30
  }
30
31
 
31
- return { exitCode: 0, result: { greeting } };
32
+ // 2. Call core, wrap in try/catch
33
+ try {
34
+ const { greeting } = hello({ who: input.flags.who ?? 'World' });
35
+
36
+ if (input.flags.json) {
37
+ ctx.ui?.json?.({ greeting });
38
+ } else {
39
+ ctx.ui?.info?.(greeting);
40
+ }
41
+
42
+ return { exitCode: 0, result: { greeting } };
43
+ } catch (err) {
44
+ handleError(ctx, err, input.flags.json);
45
+ return { exitCode: 1, result: null };
46
+ }
32
47
  },
33
48
  },
34
49
  });
@@ -0,0 +1,63 @@
1
+ import type { PluginContextV3 } from '@kb-labs/sdk';
2
+
3
+ /**
4
+ * Output a structured error from a command handler.
5
+ *
6
+ * In JSON mode → { ok: false, error: { code, message, hint? } }
7
+ * In CLI mode → ctx.ui.error() with formatted hint/cause box
8
+ *
9
+ * Usage — wrap every API call in try/catch:
10
+ *
11
+ * try {
12
+ * const result = await someApiCall();
13
+ * ...
14
+ * } catch (err) {
15
+ * handleError(ctx, err, input.flags.json);
16
+ * return { exitCode: 1, result: null };
17
+ * }
18
+ *
19
+ * Extend for your plugin's API error class:
20
+ *
21
+ * import { YourApiError } from '@<%= it.scope ? it.scope + '/' : '' %><%= it.name %>-core';
22
+ * if (err instanceof YourApiError) {
23
+ * const hint = err.status === 401 ? 'Check your API key'
24
+ * : err.status === 404 ? 'Resource not found — verify the ID'
25
+ * : 'API error';
26
+ * if (isJson) {
27
+ * ctx.ui?.json?.({ ok: false, error: { code: err.code, message: err.message, status: err.status, hint } });
28
+ * } else {
29
+ * ctx.ui?.error?.(err.message, { hint, cause: `API ${err.status} (${err.code})` });
30
+ * }
31
+ * return;
32
+ * }
33
+ */
34
+ export function handleError(ctx: PluginContextV3, err: unknown, isJson?: boolean): void {
35
+ const message = err instanceof Error ? err.message : String(err);
36
+
37
+ if (isJson) {
38
+ ctx.ui?.json?.({ ok: false, error: { code: 'INTERNAL_ERROR', message } });
39
+ } else {
40
+ ctx.ui?.error?.(message);
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Output a validation error (missing positional arg or required flag).
46
+ * Call BEFORE the try/catch — this is synchronous and never throws.
47
+ *
48
+ * if (!id) {
49
+ * validationError(ctx, 'id is required', 'Usage: kb <%= it.name %> cmd <id>', input.flags.json);
50
+ * return { exitCode: 1, result: null };
51
+ * }
52
+ */
53
+ export function validationError(
54
+ ctx: PluginContextV3,
55
+ message: string,
56
+ isJson?: boolean,
57
+ ): void {
58
+ if (isJson) {
59
+ ctx.ui?.json?.({ ok: false, error: { code: 'INVALID_ARGS', message } });
60
+ } else {
61
+ ctx.ui?.error?.(message);
62
+ }
63
+ }
@@ -9,11 +9,9 @@ cli:
9
9
  - name: "<%= it.name %>"
10
10
  description: "<%= it.vars.description %>"
11
11
  commands:
12
- - id: hello
13
- group: "<%= it.name %>"
12
+ - path: "<%= it.name %> hello"
14
13
  describe: "Say hello from <%= it.name %>"
15
14
  handler: "./commands/hello.js#default"
16
- handlerPath: "./commands/hello.js"
17
15
  flags:
18
16
  - name: who
19
17
  type: string
@@ -1,4 +1,5 @@
1
1
  import { defineCommand, type CLIInput, type PluginContextV3 } from '@kb-labs/sdk';
2
+ import { handleError } from '../utils/error.js';
2
3
 
3
4
  type PingFlags = { json?: boolean };
4
5
 
@@ -13,13 +14,20 @@ export default defineCommand({
13
14
 
14
15
  handler: {
15
16
  async execute(ctx: PluginContextV3, input: CLIInput<PingFlags>) {
16
- const payload = { ok: true, at: new Date().toISOString() };
17
- if (input.flags.json) {
18
- ctx.ui?.json?.(payload);
19
- } else {
20
- ctx.ui?.info?.(`pong @ ${payload.at}`);
17
+ try {
18
+ const payload = { ok: true, at: new Date().toISOString() };
19
+
20
+ if (input.flags.json) {
21
+ ctx.ui?.json?.(payload);
22
+ } else {
23
+ ctx.ui?.info?.(`pong @ ${payload.at}`);
24
+ }
25
+
26
+ return { exitCode: 0, result: payload };
27
+ } catch (err) {
28
+ handleError(ctx, err, input.flags.json);
29
+ return { exitCode: 1, result: null };
21
30
  }
22
- return { exitCode: 0, result: payload };
23
31
  },
24
32
  },
25
33
  });
@@ -1,10 +1,8 @@
1
1
  cli:
2
2
  commands:
3
- - id: ping
4
- group: "<%= it.name %>"
3
+ - path: "<%= it.name %> ping"
5
4
  describe: "Health ping for <%= it.name %>"
6
5
  handler: "./commands/ping.js#default"
7
- handlerPath: "./commands/ping.js"
8
6
  flags:
9
7
  - name: json
10
8
  type: boolean