@outfitter/presets 0.2.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/dist/index.d.ts +43 -0
- package/dist/index.js +118 -0
- package/package.json +63 -0
- package/presets/.gitkeep +0 -0
- package/presets/basic/.gitignore.template +30 -0
- package/presets/basic/.lefthook.yml.template +26 -0
- package/presets/basic/package.json.template +39 -0
- package/presets/basic/src/index.ts.template +26 -0
- package/presets/basic/tsconfig.json.template +34 -0
- package/presets/cli/.gitignore.template +4 -0
- package/presets/cli/.lefthook.yml.template +26 -0
- package/presets/cli/README.md.template +35 -0
- package/presets/cli/biome.json.template +4 -0
- package/presets/cli/package.json.template +46 -0
- package/presets/cli/src/cli.ts.template +8 -0
- package/presets/cli/src/index.ts.template +7 -0
- package/presets/cli/src/program.ts.template +31 -0
- package/presets/cli/tsconfig.json.template +34 -0
- package/presets/daemon/.gitignore.template +4 -0
- package/presets/daemon/.lefthook.yml.template +26 -0
- package/presets/daemon/README.md.template +67 -0
- package/presets/daemon/biome.json.template +4 -0
- package/presets/daemon/package.json.template +49 -0
- package/presets/daemon/src/cli.ts.template +96 -0
- package/presets/daemon/src/daemon-main.ts.template +79 -0
- package/presets/daemon/src/daemon.ts.template +11 -0
- package/presets/daemon/src/index.ts.template +7 -0
- package/presets/daemon/tsconfig.json.template +23 -0
- package/presets/full-stack/.gitignore.template +30 -0
- package/presets/full-stack/README.md.template +30 -0
- package/presets/full-stack/apps/cli/package.json.template +35 -0
- package/presets/full-stack/apps/cli/src/cli.ts.template +24 -0
- package/presets/full-stack/apps/cli/src/index.test.ts.template +18 -0
- package/presets/full-stack/apps/cli/src/index.ts.template +5 -0
- package/presets/full-stack/apps/cli/tsconfig.json.template +37 -0
- package/presets/full-stack/apps/mcp/package.json.template +36 -0
- package/presets/full-stack/apps/mcp/src/index.test.ts.template +18 -0
- package/presets/full-stack/apps/mcp/src/index.ts.template +6 -0
- package/presets/full-stack/apps/mcp/src/mcp.ts.template +22 -0
- package/presets/full-stack/apps/mcp/src/server.ts.template +10 -0
- package/presets/full-stack/apps/mcp/tsconfig.json.template +37 -0
- package/presets/full-stack/package.json.template +16 -0
- package/presets/full-stack/packages/core/package.json.template +32 -0
- package/presets/full-stack/packages/core/src/handlers.ts.template +31 -0
- package/presets/full-stack/packages/core/src/index.test.ts.template +30 -0
- package/presets/full-stack/packages/core/src/index.ts.template +8 -0
- package/presets/full-stack/packages/core/src/types.ts.template +13 -0
- package/presets/full-stack/packages/core/tsconfig.json.template +34 -0
- package/presets/library/.gitignore.template +30 -0
- package/presets/library/README.md.template +29 -0
- package/presets/library/bunup.config.ts.template +20 -0
- package/presets/library/package.json.template +52 -0
- package/presets/library/src/handlers.ts.template +31 -0
- package/presets/library/src/index.test.ts.template +35 -0
- package/presets/library/src/index.ts.template +8 -0
- package/presets/library/src/types.ts.template +13 -0
- package/presets/library/tsconfig.json.template +34 -0
- package/presets/mcp/.gitignore.template +4 -0
- package/presets/mcp/.lefthook.yml.template +26 -0
- package/presets/mcp/README.md.template +54 -0
- package/presets/mcp/biome.json.template +4 -0
- package/presets/mcp/package.json.template +46 -0
- package/presets/mcp/src/index.ts.template +7 -0
- package/presets/mcp/src/mcp.ts.template +33 -0
- package/presets/mcp/src/server.ts.template +8 -0
- package/presets/mcp/tsconfig.json.template +23 -0
- package/presets/minimal/.gitignore.template +30 -0
- package/presets/minimal/.lefthook.yml.template +26 -0
- package/presets/minimal/package.json.template +46 -0
- package/presets/minimal/src/index.ts.template +26 -0
- package/presets/minimal/tsconfig.json.template +34 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# {{projectName}}
|
|
2
|
+
|
|
3
|
+
{{description}}
|
|
4
|
+
|
|
5
|
+
A background daemon with CLI control interface.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
bun add {{packageName}}
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Start daemon in background
|
|
17
|
+
{{binName}} start
|
|
18
|
+
|
|
19
|
+
# Start daemon in foreground (for debugging)
|
|
20
|
+
{{binName}} start --foreground
|
|
21
|
+
|
|
22
|
+
# Check status
|
|
23
|
+
{{binName}} status
|
|
24
|
+
|
|
25
|
+
# Stop daemon
|
|
26
|
+
{{binName}} stop
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Endpoints
|
|
30
|
+
|
|
31
|
+
The daemon exposes a Unix socket with the following endpoints:
|
|
32
|
+
|
|
33
|
+
### GET /health
|
|
34
|
+
|
|
35
|
+
Returns daemon health information:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"status": "ok",
|
|
40
|
+
"uptime": 12345,
|
|
41
|
+
"version": "{{version}}"
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### POST /shutdown
|
|
46
|
+
|
|
47
|
+
Gracefully shuts down the daemon.
|
|
48
|
+
|
|
49
|
+
## Development
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# Install dependencies
|
|
53
|
+
bun install
|
|
54
|
+
|
|
55
|
+
# Run daemon in foreground
|
|
56
|
+
bun run dev
|
|
57
|
+
|
|
58
|
+
# Build
|
|
59
|
+
bun run build
|
|
60
|
+
|
|
61
|
+
# Run tests
|
|
62
|
+
bun run test
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## License
|
|
66
|
+
|
|
67
|
+
MIT
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{packageName}}",
|
|
3
|
+
"version": "{{version}}",
|
|
4
|
+
"description": "{{description}}",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"{{binName}}": "./dist/cli.js",
|
|
8
|
+
"{{binName}}d": "./dist/daemon.js"
|
|
9
|
+
},
|
|
10
|
+
"main": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "bun build src/cli.ts src/daemon.ts --outdir dist --target bun && bun build src/index.ts --outdir dist --target bun --sourcemap",
|
|
20
|
+
"dev": "bun --watch src/daemon.ts -- --foreground",
|
|
21
|
+
"dev:daemon": "bun --watch src/daemon.ts -- --foreground",
|
|
22
|
+
"typecheck": "tsc --noEmit",
|
|
23
|
+
"check": "ultracite check",
|
|
24
|
+
"verify:ci": "bun run typecheck && bun run check && bun run build && bun run test",
|
|
25
|
+
"test": "bun test",
|
|
26
|
+
"test:watch": "bun test --watch",
|
|
27
|
+
"lint": "biome check .",
|
|
28
|
+
"lint:fix": "biome check . --write",
|
|
29
|
+
"format": "biome format --write .",
|
|
30
|
+
"clean:artifacts": "rm -rf dist .turbo"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@outfitter/contracts": "workspace:*",
|
|
34
|
+
"@outfitter/types": "workspace:*",
|
|
35
|
+
"@outfitter/daemon": "workspace:*",
|
|
36
|
+
"@outfitter/cli": "workspace:*",
|
|
37
|
+
"@outfitter/logging": "workspace:*",
|
|
38
|
+
"commander": "^14.0.2"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {},
|
|
41
|
+
"outfitter": {
|
|
42
|
+
"template": {
|
|
43
|
+
"kind": "runnable",
|
|
44
|
+
"placement": "apps",
|
|
45
|
+
"surfaces": ["cli", "daemon"]
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"license": "MIT"
|
|
49
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* {{projectName}} CLI entry point
|
|
4
|
+
*
|
|
5
|
+
* Commands: start, stop, restart, status
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createCLI, command } from "@outfitter/cli";
|
|
9
|
+
import { createLogger } from "@outfitter/logging";
|
|
10
|
+
import { spawn } from "node:child_process";
|
|
11
|
+
import { getSocketPath, getLockPath, isDaemonAlive } from "@outfitter/daemon";
|
|
12
|
+
|
|
13
|
+
const logger = createLogger({ name: "{{binName}}" });
|
|
14
|
+
|
|
15
|
+
const TOOL_NAME = "{{binName}}";
|
|
16
|
+
const socketPath = getSocketPath(TOOL_NAME);
|
|
17
|
+
const lockPath = getLockPath(TOOL_NAME);
|
|
18
|
+
|
|
19
|
+
const program = createCLI({
|
|
20
|
+
name: "{{binName}}",
|
|
21
|
+
version: "{{version}}",
|
|
22
|
+
description: "{{description}}",
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
program.register(
|
|
26
|
+
command("start")
|
|
27
|
+
.description("Start the daemon")
|
|
28
|
+
.option("-f, --foreground", "Run in foreground")
|
|
29
|
+
.action(async ({ flags }) => {
|
|
30
|
+
const foreground = Boolean((flags as { foreground?: boolean }).foreground);
|
|
31
|
+
|
|
32
|
+
if (await isDaemonAlive(lockPath)) {
|
|
33
|
+
logger.warn`Daemon is already running`;
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (foreground) {
|
|
38
|
+
// Run in foreground - import and run daemon directly
|
|
39
|
+
const { runDaemon } = await import("./daemon-main.js");
|
|
40
|
+
await runDaemon();
|
|
41
|
+
} else {
|
|
42
|
+
// Spawn daemon in background
|
|
43
|
+
const daemon = spawn(process.execPath, [import.meta.dir + "/daemon.js"], {
|
|
44
|
+
detached: true,
|
|
45
|
+
stdio: "ignore",
|
|
46
|
+
});
|
|
47
|
+
daemon.unref();
|
|
48
|
+
logger.info`Daemon started with PID ${daemon.pid}`;
|
|
49
|
+
}
|
|
50
|
+
}),
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
program.register(
|
|
54
|
+
command("stop")
|
|
55
|
+
.description("Stop the daemon")
|
|
56
|
+
.action(async () => {
|
|
57
|
+
if (!(await isDaemonAlive(lockPath))) {
|
|
58
|
+
logger.warn`Daemon is not running`;
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
// Signal daemon to stop via HTTP
|
|
62
|
+
try {
|
|
63
|
+
const response = await fetch(`http://unix:${socketPath}:/shutdown`, {
|
|
64
|
+
method: "POST",
|
|
65
|
+
});
|
|
66
|
+
if (response.ok) {
|
|
67
|
+
logger.info`Daemon stopped`;
|
|
68
|
+
}
|
|
69
|
+
} catch {
|
|
70
|
+
logger.error`Failed to stop daemon`;
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
}),
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
program.register(
|
|
77
|
+
command("status")
|
|
78
|
+
.description("Check daemon status")
|
|
79
|
+
.action(async () => {
|
|
80
|
+
if (await isDaemonAlive(lockPath)) {
|
|
81
|
+
logger.info`Daemon is running`;
|
|
82
|
+
// Fetch health info
|
|
83
|
+
try {
|
|
84
|
+
const response = await fetch(`http://unix:${socketPath}:/health`);
|
|
85
|
+
const health = await response.json();
|
|
86
|
+
console.log(JSON.stringify(health, null, 2));
|
|
87
|
+
} catch {
|
|
88
|
+
logger.warn`Could not fetch health info`;
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
logger.info`Daemon is not running`;
|
|
92
|
+
}
|
|
93
|
+
}),
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* {{projectName}} daemon main logic
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { createLogger } from "@outfitter/logging";
|
|
6
|
+
import {
|
|
7
|
+
getSocketPath,
|
|
8
|
+
getLockPath,
|
|
9
|
+
getDaemonDir,
|
|
10
|
+
acquireDaemonLock,
|
|
11
|
+
releaseDaemonLock,
|
|
12
|
+
} from "@outfitter/daemon";
|
|
13
|
+
import { mkdir } from "node:fs/promises";
|
|
14
|
+
|
|
15
|
+
const logger = createLogger({ name: "{{binName}}d" });
|
|
16
|
+
|
|
17
|
+
const TOOL_NAME = "{{binName}}";
|
|
18
|
+
const startTime = Date.now();
|
|
19
|
+
|
|
20
|
+
export async function runDaemon(): Promise<void> {
|
|
21
|
+
const socketPath = getSocketPath(TOOL_NAME);
|
|
22
|
+
const lockPath = getLockPath(TOOL_NAME);
|
|
23
|
+
const daemonDir = getDaemonDir(TOOL_NAME);
|
|
24
|
+
|
|
25
|
+
// Ensure daemon directory exists
|
|
26
|
+
await mkdir(daemonDir, { recursive: true });
|
|
27
|
+
|
|
28
|
+
// Acquire lock
|
|
29
|
+
const lockResult = await acquireDaemonLock(lockPath);
|
|
30
|
+
if (!lockResult.isOk()) {
|
|
31
|
+
logger.error`Failed to acquire lock: ${lockResult.error.message}`;
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
const lock = lockResult.value;
|
|
35
|
+
|
|
36
|
+
logger.info`Daemon starting on ${socketPath}`;
|
|
37
|
+
|
|
38
|
+
// Create HTTP server on Unix socket
|
|
39
|
+
const server = Bun.serve({
|
|
40
|
+
unix: socketPath,
|
|
41
|
+
fetch(request) {
|
|
42
|
+
const url = new URL(request.url);
|
|
43
|
+
|
|
44
|
+
if (url.pathname === "/health") {
|
|
45
|
+
return Response.json({
|
|
46
|
+
status: "ok",
|
|
47
|
+
uptime: Date.now() - startTime,
|
|
48
|
+
version: "{{version}}",
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (url.pathname === "/shutdown" && request.method === "POST") {
|
|
53
|
+
logger.info`Shutdown requested`;
|
|
54
|
+
// Schedule shutdown
|
|
55
|
+
setTimeout(async () => {
|
|
56
|
+
await releaseDaemonLock(lock);
|
|
57
|
+
server.stop();
|
|
58
|
+
process.exit(0);
|
|
59
|
+
}, 100);
|
|
60
|
+
return new Response("Shutting down");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return new Response("Not found", { status: 404 });
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Handle signals
|
|
68
|
+
const shutdown = async () => {
|
|
69
|
+
logger.info`Received shutdown signal`;
|
|
70
|
+
await releaseDaemonLock(lock);
|
|
71
|
+
server.stop();
|
|
72
|
+
process.exit(0);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
process.on("SIGTERM", shutdown);
|
|
76
|
+
process.on("SIGINT", shutdown);
|
|
77
|
+
|
|
78
|
+
logger.info`Daemon running`;
|
|
79
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"verbatimModuleSyntax": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"declaration": true,
|
|
11
|
+
"declarationDir": "dist",
|
|
12
|
+
"outDir": "dist",
|
|
13
|
+
"rootDir": "src",
|
|
14
|
+
"types": ["bun-types"],
|
|
15
|
+
"noImplicitAny": true,
|
|
16
|
+
"strictNullChecks": true,
|
|
17
|
+
"noUncheckedIndexedAccess": true,
|
|
18
|
+
"exactOptionalPropertyTypes": true,
|
|
19
|
+
"noPropertyAccessFromIndexSignature": true
|
|
20
|
+
},
|
|
21
|
+
"include": ["src"],
|
|
22
|
+
"exclude": ["node_modules", "dist"]
|
|
23
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules/
|
|
3
|
+
|
|
4
|
+
# Build output
|
|
5
|
+
**/dist/
|
|
6
|
+
|
|
7
|
+
# IDE
|
|
8
|
+
.idea/
|
|
9
|
+
.vscode/
|
|
10
|
+
*.swp
|
|
11
|
+
*.swo
|
|
12
|
+
|
|
13
|
+
# OS
|
|
14
|
+
.DS_Store
|
|
15
|
+
Thumbs.db
|
|
16
|
+
|
|
17
|
+
# Logs
|
|
18
|
+
*.log
|
|
19
|
+
npm-debug.log*
|
|
20
|
+
|
|
21
|
+
# Environment
|
|
22
|
+
.env
|
|
23
|
+
.env.local
|
|
24
|
+
.env.*.local
|
|
25
|
+
|
|
26
|
+
# Test coverage
|
|
27
|
+
coverage/
|
|
28
|
+
|
|
29
|
+
# Bun
|
|
30
|
+
bun.lockb
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# {{projectName}}
|
|
2
|
+
|
|
3
|
+
{{description}}
|
|
4
|
+
|
|
5
|
+
## Workspace Layout
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
{{projectName}}/
|
|
9
|
+
├── apps/
|
|
10
|
+
│ ├── cli/ # CLI surface
|
|
11
|
+
│ └── mcp/ # MCP server surface
|
|
12
|
+
└── packages/
|
|
13
|
+
└── core/ # Shared handler layer
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Development
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Install dependencies for all workspace members
|
|
20
|
+
bun install
|
|
21
|
+
|
|
22
|
+
# Build all packages
|
|
23
|
+
bun run build
|
|
24
|
+
|
|
25
|
+
# Typecheck all packages
|
|
26
|
+
bun run typecheck
|
|
27
|
+
|
|
28
|
+
# Run all tests
|
|
29
|
+
bun run test
|
|
30
|
+
```
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{packageName}}-cli",
|
|
3
|
+
"version": "{{version}}",
|
|
4
|
+
"description": "CLI surface for {{projectName}}",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"{{projectName}}": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "bun build src/cli.ts --outdir dist --target bun && bun build src/index.ts --outdir dist --target bun --sourcemap",
|
|
19
|
+
"dev": "bun --watch src/cli.ts",
|
|
20
|
+
"test": "bun test",
|
|
21
|
+
"test:watch": "bun test --watch",
|
|
22
|
+
"typecheck": "tsc --noEmit",
|
|
23
|
+
"lint": "biome check .",
|
|
24
|
+
"lint:fix": "biome check . --write",
|
|
25
|
+
"format": "biome format --write .",
|
|
26
|
+
"clean:artifacts": "rm -rf dist .turbo"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"{{packageName}}-core": "workspace:*",
|
|
30
|
+
"@outfitter/cli": "workspace:*",
|
|
31
|
+
"@outfitter/contracts": "workspace:*"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {},
|
|
34
|
+
"license": "MIT"
|
|
35
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createContext } from "@outfitter/contracts";
|
|
2
|
+
import { output } from "@outfitter/cli";
|
|
3
|
+
import { createGreeting } from "{{packageName}}-core";
|
|
4
|
+
|
|
5
|
+
export async function runCli(argv: readonly string[] = process.argv): Promise<void> {
|
|
6
|
+
const args = argv.slice(2);
|
|
7
|
+
const name =
|
|
8
|
+
args.find((arg) => !arg.startsWith("-")) ?? "World";
|
|
9
|
+
const excited = args.includes("--excited");
|
|
10
|
+
const result = await createGreeting(
|
|
11
|
+
{ name, excited },
|
|
12
|
+
createContext({ cwd: process.cwd(), env: process.env })
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
if (result.isErr()) {
|
|
16
|
+
throw result.error;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
await output(result.value.message, { mode: "human" });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (import.meta.main) {
|
|
23
|
+
await runCli();
|
|
24
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { createContext } from "@outfitter/contracts";
|
|
3
|
+
import { createGreeting } from "{{packageName}}-core";
|
|
4
|
+
|
|
5
|
+
describe("cli surface", () => {
|
|
6
|
+
test("can use shared core handler", async () => {
|
|
7
|
+
const result = await createGreeting(
|
|
8
|
+
{ name: "CLI", excited: true },
|
|
9
|
+
createContext({ cwd: process.cwd(), env: process.env })
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
expect(result.isOk()).toBe(true);
|
|
13
|
+
if (result.isErr()) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
expect(result.value.message).toBe("Hello, CLI!");
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"target": "ESNext",
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"moduleResolution": "bundler",
|
|
7
|
+
"lib": ["ESNext"],
|
|
8
|
+
"types": ["@types/bun"],
|
|
9
|
+
"baseUrl": ".",
|
|
10
|
+
"paths": {
|
|
11
|
+
"{{packageName}}-core": ["../../packages/core/src/index.ts"]
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
"strict": true,
|
|
15
|
+
"noImplicitAny": true,
|
|
16
|
+
"strictNullChecks": true,
|
|
17
|
+
"noUncheckedIndexedAccess": true,
|
|
18
|
+
"exactOptionalPropertyTypes": true,
|
|
19
|
+
"noPropertyAccessFromIndexSignature": true,
|
|
20
|
+
"noImplicitReturns": true,
|
|
21
|
+
"noFallthroughCasesInSwitch": true,
|
|
22
|
+
|
|
23
|
+
"declaration": true,
|
|
24
|
+
"declarationMap": true,
|
|
25
|
+
"sourceMap": true,
|
|
26
|
+
"outDir": "./dist",
|
|
27
|
+
|
|
28
|
+
"esModuleInterop": true,
|
|
29
|
+
"forceConsistentCasingInFileNames": true,
|
|
30
|
+
"isolatedModules": true,
|
|
31
|
+
"verbatimModuleSyntax": true,
|
|
32
|
+
"skipLibCheck": true,
|
|
33
|
+
"resolveJsonModule": true
|
|
34
|
+
},
|
|
35
|
+
"include": ["src/**/*"],
|
|
36
|
+
"exclude": ["node_modules", "dist"]
|
|
37
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{packageName}}-mcp",
|
|
3
|
+
"version": "{{version}}",
|
|
4
|
+
"description": "MCP surface for {{projectName}}",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"{{projectName}}-mcp": "./dist/server.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "bun build src/server.ts --outdir dist --target bun && bun build src/index.ts --outdir dist --target bun --sourcemap",
|
|
19
|
+
"dev": "bun --watch src/server.ts",
|
|
20
|
+
"test": "bun test",
|
|
21
|
+
"test:watch": "bun test --watch",
|
|
22
|
+
"typecheck": "tsc --noEmit",
|
|
23
|
+
"lint": "biome check .",
|
|
24
|
+
"lint:fix": "biome check . --write",
|
|
25
|
+
"format": "biome format --write .",
|
|
26
|
+
"clean:artifacts": "rm -rf dist .turbo"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"{{packageName}}-core": "workspace:*",
|
|
30
|
+
"@outfitter/contracts": "workspace:*",
|
|
31
|
+
"@outfitter/mcp": "workspace:*",
|
|
32
|
+
"zod": "^4.3.5"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {},
|
|
35
|
+
"license": "MIT"
|
|
36
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { createContext } from "@outfitter/contracts";
|
|
3
|
+
import { createGreeting } from "{{packageName}}-core";
|
|
4
|
+
|
|
5
|
+
describe("mcp surface", () => {
|
|
6
|
+
test("can use shared core handler", async () => {
|
|
7
|
+
const result = await createGreeting(
|
|
8
|
+
{ name: "MCP" },
|
|
9
|
+
createContext({ cwd: process.cwd(), env: process.env })
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
expect(result.isOk()).toBe(true);
|
|
13
|
+
if (result.isErr()) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
expect(result.value.message).toBe("Hello, MCP.");
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createGreeting } from "{{packageName}}-core";
|
|
2
|
+
import { createMcpServer, defineTool } from "@outfitter/mcp";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
const server = createMcpServer({
|
|
6
|
+
name: "{{projectName}}-mcp",
|
|
7
|
+
version: "{{version}}",
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const greetTool = defineTool({
|
|
11
|
+
name: "greet",
|
|
12
|
+
description: "Generate greeting via shared core handler",
|
|
13
|
+
inputSchema: z.object({
|
|
14
|
+
name: z.string().default("World").describe("Name to greet"),
|
|
15
|
+
excited: z.boolean().default(false),
|
|
16
|
+
}),
|
|
17
|
+
handler: async (input, ctx) => createGreeting(input, ctx),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
server.registerTool(greetTool);
|
|
21
|
+
|
|
22
|
+
export { server };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"target": "ESNext",
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"moduleResolution": "bundler",
|
|
7
|
+
"lib": ["ESNext"],
|
|
8
|
+
"types": ["@types/bun"],
|
|
9
|
+
"baseUrl": ".",
|
|
10
|
+
"paths": {
|
|
11
|
+
"{{packageName}}-core": ["../../packages/core/src/index.ts"]
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
"strict": true,
|
|
15
|
+
"noImplicitAny": true,
|
|
16
|
+
"strictNullChecks": true,
|
|
17
|
+
"noUncheckedIndexedAccess": true,
|
|
18
|
+
"exactOptionalPropertyTypes": true,
|
|
19
|
+
"noPropertyAccessFromIndexSignature": true,
|
|
20
|
+
"noImplicitReturns": true,
|
|
21
|
+
"noFallthroughCasesInSwitch": true,
|
|
22
|
+
|
|
23
|
+
"declaration": true,
|
|
24
|
+
"declarationMap": true,
|
|
25
|
+
"sourceMap": true,
|
|
26
|
+
"outDir": "./dist",
|
|
27
|
+
|
|
28
|
+
"esModuleInterop": true,
|
|
29
|
+
"forceConsistentCasingInFileNames": true,
|
|
30
|
+
"isolatedModules": true,
|
|
31
|
+
"verbatimModuleSyntax": true,
|
|
32
|
+
"skipLibCheck": true,
|
|
33
|
+
"resolveJsonModule": true
|
|
34
|
+
},
|
|
35
|
+
"include": ["src/**/*"],
|
|
36
|
+
"exclude": ["node_modules", "dist"]
|
|
37
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{packageName}}",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "{{version}}",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"workspaces": ["apps/*", "packages/*"],
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "bun run --filter '*' build",
|
|
9
|
+
"dev": "bun run --filter '*' dev",
|
|
10
|
+
"test": "bun run --filter '*' test",
|
|
11
|
+
"typecheck": "bun run --filter '*' typecheck",
|
|
12
|
+
"lint": "bun run --filter '*' lint",
|
|
13
|
+
"lint:fix": "bun run --filter '*' lint:fix",
|
|
14
|
+
"format": "bun run --filter '*' format"
|
|
15
|
+
}
|
|
16
|
+
}
|