@kb-labs/scaffold 2.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/commands/doctor.d.ts +15 -0
  2. package/dist/commands/doctor.js +63 -0
  3. package/dist/commands/doctor.js.map +1 -0
  4. package/dist/commands/scaffold.d.ts +18 -0
  5. package/dist/commands/scaffold.js +196 -0
  6. package/dist/commands/scaffold.js.map +1 -0
  7. package/dist/index.d.ts +2 -0
  8. package/dist/index.js +114 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/manifest.d.ts +75 -0
  11. package/dist/manifest.js +114 -0
  12. package/dist/manifest.js.map +1 -0
  13. package/package.json +55 -0
  14. package/templates/adapter/blocks/base/block.yaml +4 -0
  15. package/templates/adapter/blocks/base/files/README.md.eta +28 -0
  16. package/templates/adapter/blocks/base/files/package.json.eta +37 -0
  17. package/templates/adapter/blocks/base/files/src/manifest.ts.eta +24 -0
  18. package/templates/adapter/blocks/base/files/tsconfig.json +8 -0
  19. package/templates/adapter/blocks/base/files/tsup.config.ts +10 -0
  20. package/templates/adapter/blocks/base/files/vitest.config.ts +12 -0
  21. package/templates/adapter/blocks/provider-example/block.yaml +6 -0
  22. package/templates/adapter/blocks/provider-example/files/src/index.ts.eta +6 -0
  23. package/templates/adapter/blocks/provider-example/files/src/llm.ts.eta +46 -0
  24. package/templates/adapter/entity.yaml +37 -0
  25. package/templates/plugin/blocks/base/block.yaml +5 -0
  26. package/templates/plugin/blocks/base/files/README.md.eta +54 -0
  27. package/templates/plugin/blocks/base/files/package.json.eta +14 -0
  28. package/templates/plugin/blocks/base/files/packages/__name__-contracts/package.json.eta +29 -0
  29. package/templates/plugin/blocks/base/files/packages/__name__-contracts/src/index.ts.eta +14 -0
  30. package/templates/plugin/blocks/base/files/packages/__name__-contracts/tsconfig.json +8 -0
  31. package/templates/plugin/blocks/base/files/packages/__name__-contracts/tsup.config.ts +10 -0
  32. package/templates/plugin/blocks/base/files/packages/__name__-contracts/vitest.config.ts +12 -0
  33. package/templates/plugin/blocks/base/files/packages/__name__-core/package.json.eta +33 -0
  34. package/templates/plugin/blocks/base/files/packages/__name__-core/src/hello.ts.eta +59 -0
  35. package/templates/plugin/blocks/base/files/packages/__name__-core/src/index.ts.eta +1 -0
  36. package/templates/plugin/blocks/base/files/packages/__name__-core/tests/hello.test.ts.eta +16 -0
  37. package/templates/plugin/blocks/base/files/packages/__name__-core/tsconfig.json +8 -0
  38. package/templates/plugin/blocks/base/files/packages/__name__-core/tsup.config.ts +10 -0
  39. package/templates/plugin/blocks/base/files/packages/__name__-core/vitest.config.ts +12 -0
  40. package/templates/plugin/blocks/base/files/packages/__name__-entry/eslint.config.js +3 -0
  41. package/templates/plugin/blocks/base/files/packages/__name__-entry/package.json.eta +41 -0
  42. package/templates/plugin/blocks/base/files/packages/__name__-entry/src/commands/hello.ts.eta +39 -0
  43. package/templates/plugin/blocks/base/files/packages/__name__-entry/src/index.ts.eta +4 -0
  44. package/templates/plugin/blocks/base/files/packages/__name__-entry/tsconfig.json +8 -0
  45. package/templates/plugin/blocks/base/files/packages/__name__-entry/tsup.config.ts +10 -0
  46. package/templates/plugin/blocks/base/files/packages/__name__-entry/vitest.config.ts +12 -0
  47. package/templates/plugin/blocks/base/files/pnpm-workspace.yaml.eta +3 -0
  48. package/templates/plugin/blocks/base/files/tsconfig.base.json +3 -0
  49. package/templates/plugin/blocks/base/manifest.patch.yaml +28 -0
  50. package/templates/plugin/blocks/base/manifest.snippets.yaml +4 -0
  51. package/templates/plugin/blocks/cli/block.yaml +5 -0
  52. package/templates/plugin/blocks/cli/files/packages/__name__-entry/src/commands/ping.ts.eta +25 -0
  53. package/templates/plugin/blocks/cli/manifest.patch.yaml +14 -0
  54. package/templates/plugin/blocks/contracts/block.yaml +5 -0
  55. package/templates/plugin/blocks/contracts/files/packages/__name__-contracts/src/events.ts.eta +13 -0
  56. package/templates/plugin/blocks/rest/block.yaml +6 -0
  57. package/templates/plugin/blocks/rest/files/packages/__name__-entry/src/rest/hello.ts.eta +22 -0
  58. package/templates/plugin/blocks/rest/manifest.patch.yaml +9 -0
  59. package/templates/plugin/entity.yaml +39 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/manifest.ts"],"names":[],"mappings":"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","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"]}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@kb-labs/scaffold",
3
+ "version": "2.29.0",
4
+ "type": "module",
5
+ "description": "Generic entity scaffolder for KB Labs plugins, adapters, and friends.",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ },
13
+ "./plugin-manifest": {
14
+ "import": "./dist/manifest.js",
15
+ "types": "./dist/manifest.d.ts"
16
+ }
17
+ },
18
+ "kb": {
19
+ "manifest": "./dist/manifest.js"
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "templates"
24
+ ],
25
+ "sideEffects": false,
26
+ "dependencies": {
27
+ "@clack/prompts": "^0.11.0",
28
+ "@kb-labs/scaffold-contracts": "2.29.0",
29
+ "@kb-labs/scaffold-core": "2.29.0",
30
+ "@kb-labs/sdk": "2.18.1"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^24.3.3",
34
+ "eslint": "^9",
35
+ "rimraf": "^6.0.1",
36
+ "tsup": "^8.5.0",
37
+ "typescript": "^5.6.3",
38
+ "vitest": "^3.2.4",
39
+ "@kb-labs/devkit": "2.29.0",
40
+ "@kb-labs/plugin-contracts": "2.29.0"
41
+ },
42
+ "engines": {
43
+ "node": ">=20.0.0",
44
+ "pnpm": ">=9.0.0"
45
+ },
46
+ "scripts": {
47
+ "clean": "rimraf dist",
48
+ "build": "tsup",
49
+ "dev": "tsup --watch",
50
+ "type-check": "tsc --noEmit",
51
+ "test": "vitest run --passWithNoTests",
52
+ "test:watch": "vitest",
53
+ "lint": "eslint src --ext .ts"
54
+ }
55
+ }
@@ -0,0 +1,4 @@
1
+ id: base
2
+ describe: |
3
+ Required. Single-package layout with a stub implementation and working
4
+ AdapterManifest. All imports go through @kb-labs/sdk/adapters.
@@ -0,0 +1,28 @@
1
+ # <%= it.scope ? it.scope + '/' : '' %><%= it.name %>
2
+
3
+ <%= it.vars.description %>
4
+
5
+ ## Layout
6
+
7
+ ```
8
+ src/
9
+ index.ts # createAdapter factory — instantiated by the host
10
+ manifest.ts # AdapterManifest (id, type, what this adapter implements)
11
+ ```
12
+
13
+ All imports come from `@kb-labs/sdk/adapters`. **Never** reach into
14
+ `@kb-labs/core-platform` directly — that's internal and will break on
15
+ core majors. If you need something not re-exported by SDK, open an issue
16
+ on `@kb-labs/sdk` rather than working around it.
17
+
18
+ ## Using it
19
+
20
+ Register the adapter in your project's `.kb/kb.config.jsonc` (the host
21
+ plugin will resolve this package via npm or `workspace:*`). Then the
22
+ runtime wires it up to whichever capability it `implements`.
23
+
24
+ ## Health check
25
+
26
+ ```bash
27
+ pnpm kb scaffold doctor --path .
28
+ ```
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "<%= it.scope ? it.scope + '/' : '' %><%= it.name %>",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "<%= it.vars.description %>",
6
+ "license": "<%= it.vars.license %>",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ },
14
+ "./manifest": {
15
+ "import": "./dist/manifest.js",
16
+ "types": "./dist/manifest.d.ts"
17
+ }
18
+ },
19
+ "files": ["dist"],
20
+ "sideEffects": false,
21
+ "scripts": {
22
+ "clean": "rimraf dist",
23
+ "build": "tsup",
24
+ "type-check": "tsc --noEmit",
25
+ "test": "vitest run --passWithNoTests"
26
+ },
27
+ "dependencies": {
28
+ "@kb-labs/sdk": "<%= it.mode === 'in-workspace' ? 'workspace:*' : '^' + it.versions.sdk %>"
29
+ },
30
+ "devDependencies": {
31
+ "@kb-labs/devkit": "<%= it.mode === 'in-workspace' ? 'workspace:*' : '^' + it.versions.devkit %>",
32
+ "rimraf": "^6.0.1",
33
+ "tsup": "^8.5.0",
34
+ "typescript": "^5.6.3",
35
+ "vitest": "^3.2.4"
36
+ }
37
+ }
@@ -0,0 +1,24 @@
1
+ import { defineAdapterManifest } from '@kb-labs/sdk/adapters';
2
+
3
+ /**
4
+ * AdapterManifest for <%= it.name %>.
5
+ *
6
+ * `implements` names the capability interface this adapter provides
7
+ * (e.g. "ILLM", "ILogger", "ICache"). The host resolves adapters by this
8
+ * string at bind time.
9
+ *
10
+ * Types:
11
+ * - "core" — shipped with the platform, always available
12
+ * - "extension" — third-party or in-project, opt-in via config
13
+ * - "proxy" — forwards calls to a remote runtime
14
+ */
15
+ export const manifest = defineAdapterManifest({
16
+ manifestVersion: '1.0.0',
17
+ id: '<%= it.name %>',
18
+ name: '<%= it.name %>',
19
+ version: '0.1.0',
20
+ type: 'extension',
21
+ implements: 'ILLM',
22
+ });
23
+
24
+ export default manifest;
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "@kb-labs/devkit/tsconfig/node.json",
3
+ "compilerOptions": {
4
+ "rootDir": "src",
5
+ "outDir": "dist"
6
+ },
7
+ "include": ["src"]
8
+ }
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from 'tsup';
2
+ import nodePreset from '@kb-labs/devkit/tsup/node';
3
+
4
+ export default defineConfig({
5
+ ...nodePreset,
6
+ entry: ['src/index.ts', 'src/manifest.ts'],
7
+ dts: { resolve: true, skipLibCheck: true },
8
+ clean: true,
9
+ sourcemap: true,
10
+ });
@@ -0,0 +1,12 @@
1
+ import { defineConfig } from 'vitest/config';
2
+ import baseConfig from '@kb-labs/devkit/vitest/node';
3
+
4
+ export default defineConfig({
5
+ ...baseConfig,
6
+ test: {
7
+ ...baseConfig.test,
8
+ globals: true,
9
+ include: ['tests/**/*.test.ts', 'src/**/*.test.ts'],
10
+ environment: 'node',
11
+ },
12
+ });
@@ -0,0 +1,6 @@
1
+ id: provider-example
2
+ describe: |
3
+ Adds a stub ILLM implementation showing how to wire an adapter factory
4
+ (createAdapter), how the manifest's `implements` string maps to the
5
+ interface, and how to declare capabilities. Requires `base`.
6
+ requires: [base]
@@ -0,0 +1,6 @@
1
+ export { manifest } from './manifest.js';
2
+ export { createAdapter } from './llm.js';
3
+ export type {
4
+ <%= it.name.replace(/(^|-)(\w)/g, (_, _s, c) => c.toUpperCase()) %>Config,
5
+ } from './llm.js';
6
+ export { default } from './manifest.js';
@@ -0,0 +1,46 @@
1
+ import type {
2
+ ILLM,
3
+ LLMOptions,
4
+ LLMResponse,
5
+ IDisposable,
6
+ } from '@kb-labs/sdk/adapters';
7
+
8
+ /**
9
+ * Minimal ILLM implementation.
10
+ *
11
+ * Replace the `complete` body with a real API call (OpenAI, Anthropic,
12
+ * local llama.cpp, whatever). `dispose` is called by the host on shutdown
13
+ * — use it to close connection pools, flush buffers, etc.
14
+ *
15
+ * Cookbook:
16
+ * - Use `useLogger(ctx)` / `useCache(ctx)` from '@kb-labs/sdk' inside
17
+ * the host, not here. Adapters should stay capability-focused.
18
+ * - Declare streaming / batch support via AdapterCapabilities in the
19
+ * manifest so the router can pick the right adapter for a request.
20
+ */
21
+ export class <%= it.name.replace(/(^|-)(\w)/g, (_, _s, c) => c.toUpperCase()) %>LLM implements ILLM, IDisposable {
22
+ constructor(private readonly config: { apiKey?: string } = {}) {}
23
+
24
+ async complete(options: LLMOptions): Promise<LLMResponse> {
25
+ // TODO: call your provider here.
26
+ return {
27
+ text: `(stub) <%= it.name %> would answer: ${options.prompt}`,
28
+ model: 'stub',
29
+ usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
30
+ };
31
+ }
32
+
33
+ async dispose(): Promise<void> {
34
+ // TODO: tear down connections / flush buffers.
35
+ }
36
+ }
37
+
38
+ export interface <%= it.name.replace(/(^|-)(\w)/g, (_, _s, c) => c.toUpperCase()) %>Config {
39
+ apiKey?: string;
40
+ }
41
+
42
+ export function createAdapter(
43
+ config: <%= it.name.replace(/(^|-)(\w)/g, (_, _s, c) => c.toUpperCase()) %>Config = {},
44
+ ): ILLM {
45
+ return new <%= it.name.replace(/(^|-)(\w)/g, (_, _s, c) => c.toUpperCase()) %>LLM(config);
46
+ }
@@ -0,0 +1,37 @@
1
+ id: adapter
2
+ displayName: KB Labs Adapter
3
+ description: |
4
+ A KB Labs adapter: implements a replaceable platform capability (LLM,
5
+ logger, cache, storage, etc). Single package, everything imported
6
+ through @kb-labs/sdk/adapters — never from @kb-labs/core-platform.
7
+
8
+ output: adapters/<%= it.name %>
9
+
10
+ variables:
11
+ - name: scope
12
+ type: string
13
+ describe: npm scope (e.g. @kb-labs, @acme). Leave blank for unscoped.
14
+ default: "@kb-labs"
15
+ validate: npmScope
16
+ - name: description
17
+ type: string
18
+ describe: Short description of the adapter
19
+ default: A KB Labs adapter
20
+ - name: license
21
+ type: string
22
+ describe: License identifier
23
+ default: MIT
24
+ - name: mode
25
+ type: select
26
+ describe: Where will this adapter live?
27
+ default: in-workspace
28
+ choices:
29
+ - value: in-workspace
30
+ label: Inside a KB Labs workspace (workspace:* deps)
31
+ - value: standalone
32
+ label: Standalone repo (semver-pinned deps)
33
+
34
+ defaults:
35
+ blocks:
36
+ - base
37
+ - provider-example
@@ -0,0 +1,5 @@
1
+ id: base
2
+ describe: |
3
+ Required. Scaffolds the three-package layout (entry / core / contracts)
4
+ with a working `hello` CLI command and inline examples showing how to use
5
+ the LLM, cache, and logger from the platform.
@@ -0,0 +1,54 @@
1
+ # <%= it.scope ? it.scope + '/' : '' %><%= it.name %>
2
+
3
+ <%= it.vars.description %>
4
+
5
+ ## Layout
6
+
7
+ ```
8
+ packages/
9
+ <%= it.name %>-contracts/ # public types (request/response shapes)
10
+ <%= it.name %>-core/ # pure business logic (testable in isolation)
11
+ <%= it.name %>-entry/ # V3 manifest + CLI command handlers
12
+ ```
13
+
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
19
+
20
+ ```bash
21
+ pnpm install
22
+ pnpm -w build
23
+ pnpm kb <%= it.name %> hello # say hi
24
+ pnpm kb <%= it.name %> hello --who=World # same, explicit
25
+ ```
26
+
27
+ ## What to edit
28
+
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.
38
+
39
+ ## Linking into a workspace (development)
40
+
41
+ ```bash
42
+ pnpm kb marketplace plugins link .
43
+ pnpm kb plugins clear-cache
44
+ ```
45
+
46
+ ## Health check
47
+
48
+ ```bash
49
+ pnpm kb scaffold doctor --path .
50
+ ```
51
+
52
+ ## License
53
+
54
+ <%= it.vars.license %>
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "<%= it.scope ? it.scope + '/' : '' %><%= it.name %>",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "description": "<%= it.vars.description %>",
6
+ "license": "<%= it.vars.license %>",
7
+ "type": "module",
8
+ "scripts": {
9
+ "build": "pnpm -r build",
10
+ "clean": "pnpm -r clean",
11
+ "type-check": "pnpm -r type-check",
12
+ "test": "pnpm -r test"
13
+ }
14
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "<%= it.scope ? it.scope + '/' : '' %><%= it.name %>-contracts",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Public types for <%= it.name %>",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": ["dist"],
15
+ "sideEffects": false,
16
+ "scripts": {
17
+ "clean": "rimraf dist",
18
+ "build": "tsup",
19
+ "type-check": "tsc --noEmit",
20
+ "test": "vitest run --passWithNoTests"
21
+ },
22
+ "devDependencies": {
23
+ "@kb-labs/devkit": "<%= it.mode === 'in-workspace' ? 'workspace:*' : '^' + it.versions.devkit %>",
24
+ "rimraf": "^6.0.1",
25
+ "tsup": "^8.5.0",
26
+ "typescript": "^5.6.3",
27
+ "vitest": "^3.2.4"
28
+ }
29
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Public contracts for <%= it.name %>.
3
+ *
4
+ * Keep this package tiny — it is the stable boundary between `-core` and
5
+ * `-entry`. If a type is only used inside one package, it does not belong here.
6
+ */
7
+
8
+ export interface HelloInput {
9
+ who: string;
10
+ }
11
+
12
+ export interface HelloResult {
13
+ greeting: string;
14
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "@kb-labs/devkit/tsconfig/node.json",
3
+ "compilerOptions": {
4
+ "rootDir": "src",
5
+ "outDir": "dist"
6
+ },
7
+ "include": ["src"]
8
+ }
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from 'tsup';
2
+ import nodePreset from '@kb-labs/devkit/tsup/node';
3
+
4
+ export default defineConfig({
5
+ ...nodePreset,
6
+ entry: ['src/index.ts'],
7
+ dts: { resolve: true, skipLibCheck: true },
8
+ clean: true,
9
+ sourcemap: true,
10
+ });
@@ -0,0 +1,12 @@
1
+ import { defineConfig } from 'vitest/config';
2
+ import baseConfig from '@kb-labs/devkit/vitest/node';
3
+
4
+ export default defineConfig({
5
+ ...baseConfig,
6
+ test: {
7
+ ...baseConfig.test,
8
+ globals: true,
9
+ include: ['tests/**/*.test.ts', 'src/**/*.test.ts'],
10
+ environment: 'node',
11
+ },
12
+ });
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "<%= it.scope ? it.scope + '/' : '' %><%= it.name %>-core",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Business logic for <%= it.name %> (pure, testable in isolation).",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": ["dist"],
15
+ "sideEffects": false,
16
+ "scripts": {
17
+ "clean": "rimraf dist",
18
+ "build": "tsup",
19
+ "type-check": "tsc --noEmit",
20
+ "test": "vitest run --passWithNoTests"
21
+ },
22
+ "dependencies": {
23
+ "<%= it.scope ? it.scope + '/' : '' %><%= it.name %>-contracts": "<%= it.mode === 'in-workspace' ? 'workspace:*' : '^0.1.0' %>",
24
+ "@kb-labs/sdk": "<%= it.mode === 'in-workspace' ? 'workspace:*' : '^' + it.versions.sdk %>"
25
+ },
26
+ "devDependencies": {
27
+ "@kb-labs/devkit": "<%= it.mode === 'in-workspace' ? 'workspace:*' : '^' + it.versions.devkit %>",
28
+ "rimraf": "^6.0.1",
29
+ "tsup": "^8.5.0",
30
+ "typescript": "^5.6.3",
31
+ "vitest": "^3.2.4"
32
+ }
33
+ }
@@ -0,0 +1,59 @@
1
+ import type { HelloInput, HelloResult } from '<%= it.scope ? it.scope + '/' : '' %><%= it.name %>-contracts';
2
+
3
+ /**
4
+ * Business logic for the `hello` command.
5
+ *
6
+ * This is a pure function so it can be unit-tested without any platform
7
+ * scaffolding. The handler in `-entry` is responsible for wiring platform
8
+ * services (logger, cache, LLM) and passing the results in — keep this
9
+ * function free of side effects.
10
+ */
11
+ export function hello(input: HelloInput): HelloResult {
12
+ const who = input.who?.trim() || 'World';
13
+ return { greeting: `Hello, ${who} from <%= it.name %>!` };
14
+ }
15
+
16
+ /*
17
+ * ───────────────────────────────────────────────────────────────────────────
18
+ * Cookbook: reach for these patterns when you grow past "hello".
19
+ * Everything flows through `@kb-labs/sdk` — DO NOT import from
20
+ * `@kb-labs/core-*` or `@kb-labs/platform-*` directly.
21
+ * ───────────────────────────────────────────────────────────────────────────
22
+ */
23
+
24
+ // ── 1. Call an LLM ─────────────────────────────────────────────────────────
25
+ //
26
+ // import { useLLM, type PluginContextV3 } from '@kb-labs/sdk';
27
+ //
28
+ // export async function greetWithLLM(ctx: PluginContextV3, who: string) {
29
+ // const llm = await useLLM(ctx); // throws if no LLM adapter bound
30
+ // const res = await llm.complete({
31
+ // prompt: `Write a one-line greeting for ${who}.`,
32
+ // maxTokens: 40,
33
+ // });
34
+ // return res.text;
35
+ // }
36
+
37
+ // ── 2. Cache the result ────────────────────────────────────────────────────
38
+ //
39
+ // import { useCache, type PluginContextV3 } from '@kb-labs/sdk';
40
+ //
41
+ // export async function cachedGreeting(ctx: PluginContextV3, who: string) {
42
+ // const cache = await useCache(ctx); // namespaced per plugin
43
+ // const key = `greeting:${who}`;
44
+ // const hit = await cache.get<string>(key);
45
+ // if (hit) return hit;
46
+ //
47
+ // const greeting = `Hello, ${who}!`;
48
+ // await cache.set(key, greeting, { ttlMs: 60_000 });
49
+ // return greeting;
50
+ // }
51
+
52
+ // ── 3. Structured logging ──────────────────────────────────────────────────
53
+ //
54
+ // import { useLogger, type PluginContextV3 } from '@kb-labs/sdk';
55
+ //
56
+ // export function logGreeting(ctx: PluginContextV3, who: string) {
57
+ // const log = useLogger(ctx);
58
+ // log.info({ who }, 'computed greeting');
59
+ // }
@@ -0,0 +1 @@
1
+ export { hello } from './hello.js';
@@ -0,0 +1,16 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { hello } from '../src/hello.js';
3
+
4
+ describe('hello', () => {
5
+ it('greets a named person', () => {
6
+ expect(hello({ who: 'Kirill' })).toEqual({
7
+ greeting: 'Hello, Kirill from <%= it.name %>!',
8
+ });
9
+ });
10
+
11
+ it('falls back to World when empty', () => {
12
+ expect(hello({ who: '' })).toEqual({
13
+ greeting: 'Hello, World from <%= it.name %>!',
14
+ });
15
+ });
16
+ });
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "@kb-labs/devkit/tsconfig/node.json",
3
+ "compilerOptions": {
4
+ "rootDir": "src",
5
+ "outDir": "dist"
6
+ },
7
+ "include": ["src"]
8
+ }
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from 'tsup';
2
+ import nodePreset from '@kb-labs/devkit/tsup/node';
3
+
4
+ export default defineConfig({
5
+ ...nodePreset,
6
+ entry: ['src/index.ts'],
7
+ dts: { resolve: true, skipLibCheck: true },
8
+ clean: true,
9
+ sourcemap: true,
10
+ });
@@ -0,0 +1,12 @@
1
+ import { defineConfig } from 'vitest/config';
2
+ import baseConfig from '@kb-labs/devkit/vitest/node';
3
+
4
+ export default defineConfig({
5
+ ...baseConfig,
6
+ test: {
7
+ ...baseConfig.test,
8
+ globals: true,
9
+ include: ['tests/**/*.test.ts', 'src/**/*.test.ts'],
10
+ environment: 'node',
11
+ },
12
+ });
@@ -0,0 +1,3 @@
1
+ import nodePreset from '@kb-labs/devkit/eslint/node.js';
2
+
3
+ export default [...nodePreset];