@livestore/cli 0.0.0-snapshot-2ac5fd340c97c9e07fe4c5dc6d31d5132aa6557c.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 (80) hide show
  1. package/.claude/settings.local.json +12 -0
  2. package/LICENSE +201 -0
  3. package/dist/cli +0 -0
  4. package/dist/cli.d.ts +15 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +22 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/commands/mcp-coach.d.ts +15 -0
  9. package/dist/commands/mcp-coach.d.ts.map +1 -0
  10. package/dist/commands/mcp-coach.js +87 -0
  11. package/dist/commands/mcp-coach.js.map +1 -0
  12. package/dist/commands/mcp-tools.d.ts +41 -0
  13. package/dist/commands/mcp-tools.d.ts.map +1 -0
  14. package/dist/commands/mcp-tools.js +148 -0
  15. package/dist/commands/mcp-tools.js.map +1 -0
  16. package/dist/commands/mcp.d.ts +5 -0
  17. package/dist/commands/mcp.d.ts.map +1 -0
  18. package/dist/commands/mcp.js +67 -0
  19. package/dist/commands/mcp.js.map +1 -0
  20. package/dist/commands/new-project.d.ts +34 -0
  21. package/dist/commands/new-project.d.ts.map +1 -0
  22. package/dist/commands/new-project.js +163 -0
  23. package/dist/commands/new-project.js.map +1 -0
  24. package/dist/mcp-content/architecture.d.ts +2 -0
  25. package/dist/mcp-content/architecture.d.ts.map +1 -0
  26. package/dist/mcp-content/architecture.js +171 -0
  27. package/dist/mcp-content/architecture.js.map +1 -0
  28. package/dist/mcp-content/features.d.ts +2 -0
  29. package/dist/mcp-content/features.d.ts.map +1 -0
  30. package/dist/mcp-content/features.js +177 -0
  31. package/dist/mcp-content/features.js.map +1 -0
  32. package/dist/mcp-content/getting-started.d.ts +2 -0
  33. package/dist/mcp-content/getting-started.d.ts.map +1 -0
  34. package/dist/mcp-content/getting-started.js +405 -0
  35. package/dist/mcp-content/getting-started.js.map +1 -0
  36. package/dist/mcp-content/overview.d.ts +2 -0
  37. package/dist/mcp-content/overview.d.ts.map +1 -0
  38. package/dist/mcp-content/overview.js +120 -0
  39. package/dist/mcp-content/overview.js.map +1 -0
  40. package/dist/mcp-content/schemas/blog.d.ts +2 -0
  41. package/dist/mcp-content/schemas/blog.d.ts.map +1 -0
  42. package/dist/mcp-content/schemas/blog.js +223 -0
  43. package/dist/mcp-content/schemas/blog.js.map +1 -0
  44. package/dist/mcp-content/schemas/ecommerce.d.ts +2 -0
  45. package/dist/mcp-content/schemas/ecommerce.d.ts.map +1 -0
  46. package/dist/mcp-content/schemas/ecommerce.js +436 -0
  47. package/dist/mcp-content/schemas/ecommerce.js.map +1 -0
  48. package/dist/mcp-content/schemas/social.d.ts +2 -0
  49. package/dist/mcp-content/schemas/social.d.ts.map +1 -0
  50. package/dist/mcp-content/schemas/social.js +339 -0
  51. package/dist/mcp-content/schemas/social.js.map +1 -0
  52. package/dist/mcp-content/schemas/todo.d.ts +2 -0
  53. package/dist/mcp-content/schemas/todo.d.ts.map +1 -0
  54. package/dist/mcp-content/schemas/todo.js +172 -0
  55. package/dist/mcp-content/schemas/todo.js.map +1 -0
  56. package/dist/mod.d.ts +2 -0
  57. package/dist/mod.d.ts.map +1 -0
  58. package/dist/mod.js +2 -0
  59. package/dist/mod.js.map +1 -0
  60. package/dist/test-tool.d.ts +2 -0
  61. package/dist/test-tool.d.ts.map +1 -0
  62. package/dist/test-tool.js +57 -0
  63. package/dist/test-tool.js.map +1 -0
  64. package/dist/tsconfig.tsbuildinfo +1 -0
  65. package/package.json +27 -0
  66. package/src/cli.ts +35 -0
  67. package/src/commands/mcp-coach.ts +121 -0
  68. package/src/commands/mcp-tools.ts +169 -0
  69. package/src/commands/mcp.ts +97 -0
  70. package/src/commands/new-project.ts +263 -0
  71. package/src/mcp-content/architecture.ts +170 -0
  72. package/src/mcp-content/features.ts +176 -0
  73. package/src/mcp-content/getting-started.ts +404 -0
  74. package/src/mcp-content/overview.ts +119 -0
  75. package/src/mcp-content/schemas/blog.ts +222 -0
  76. package/src/mcp-content/schemas/ecommerce.ts +435 -0
  77. package/src/mcp-content/schemas/social.ts +338 -0
  78. package/src/mcp-content/schemas/todo.ts +171 -0
  79. package/src/mod.ts +1 -0
  80. package/tsconfig.json +9 -0
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@livestore/cli",
3
+ "version": "0.0.0-snapshot-2ac5fd340c97c9e07fe4c5dc6d31d5132aa6557c.0",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": "./dist/mod.js"
7
+ },
8
+ "bin": {
9
+ "livestore": "./dist/cli.js"
10
+ },
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "dependencies": {
15
+ "@effect/ai-openai": "^0.29.0",
16
+ "@livestore/utils-dev": "0.0.0-snapshot-2ac5fd340c97c9e07fe4c5dc6d31d5132aa6557c.0",
17
+ "@livestore/utils": "0.0.0-snapshot-2ac5fd340c97c9e07fe4c5dc6d31d5132aa6557c.0"
18
+ },
19
+ "devDependencies": {
20
+ "@types/node": "24.2.0",
21
+ "typescript": "^5.9.2"
22
+ },
23
+ "scripts": {
24
+ "build": "tsc",
25
+ "dev": "bun src/cli.ts"
26
+ }
27
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Effect, FetchHttpClient, Layer, Logger, LogLevel } from '@livestore/utils/effect'
4
+ import { Cli, PlatformNode } from '@livestore/utils/node'
5
+ import { mcpCommand } from './commands/mcp.ts'
6
+ import { newProjectCommand } from './commands/new-project.ts'
7
+
8
+ const helloCommand = Cli.Command.make(
9
+ 'hello',
10
+ {
11
+ name: Cli.Options.text('name').pipe(Cli.Options.withDefault('World')),
12
+ },
13
+ Effect.fn(function* ({ name }: { name: string }) {
14
+ yield* Effect.log(`Hello, ${name}! 🎉`)
15
+ }),
16
+ )
17
+
18
+ export const command = Cli.Command.make('livestore', {
19
+ verbose: Cli.Options.boolean('verbose').pipe(Cli.Options.withDefault(false)),
20
+ }).pipe(Cli.Command.withSubcommands([helloCommand, mcpCommand, newProjectCommand]))
21
+
22
+ if (import.meta.main) {
23
+ const cli = Cli.Command.run(command, {
24
+ name: 'livestore',
25
+ version: '0.1.0',
26
+ })
27
+
28
+ const layer = Layer.mergeAll(
29
+ PlatformNode.NodeContext.layer,
30
+ FetchHttpClient.layer,
31
+ Logger.minimumLogLevel(LogLevel.Info),
32
+ )
33
+
34
+ cli(process.argv).pipe(Effect.provide(layer), PlatformNode.NodeRuntime.runMain)
35
+ }
@@ -0,0 +1,121 @@
1
+ import { OpenAiClient, OpenAiLanguageModel } from '@effect/ai-openai'
2
+ import {
3
+ AiError,
4
+ AiLanguageModel,
5
+ AiTool,
6
+ Config,
7
+ Effect,
8
+ FetchHttpClient,
9
+ Layer,
10
+ Schema,
11
+ } from '@livestore/utils/effect'
12
+
13
+ // Define the coach tool that analyzes LiveStore usage
14
+ export const coachTool = AiTool.make('livestore_coach', {
15
+ description:
16
+ 'Analyze LiveStore code (schemas, queries, mutations, etc.) and provide AI-powered feedback on best practices, performance, and improvements.',
17
+ parameters: {
18
+ code: Schema.String.annotations({
19
+ description: 'The LiveStore code to analyze (TypeScript/JavaScript)',
20
+ }),
21
+ codeType: Schema.optional(
22
+ Schema.String.annotations({
23
+ description: "Type of code being analyzed: 'schema', 'query', 'mutation', 'component', or 'general'",
24
+ }),
25
+ ),
26
+ },
27
+ success: Schema.Struct({
28
+ feedback: Schema.String.annotations({
29
+ description: 'AI-generated feedback and recommendations for the code',
30
+ }),
31
+ score: Schema.optional(
32
+ Schema.Number.annotations({
33
+ description: 'Code quality score from 1-10 (optional)',
34
+ }),
35
+ ),
36
+ suggestions: Schema.Array(
37
+ Schema.String.annotations({
38
+ description: 'Specific actionable suggestions for improvement',
39
+ }),
40
+ ).annotations({
41
+ description: 'List of specific improvement suggestions',
42
+ }),
43
+ }),
44
+ failure: AiError.AiError,
45
+ })
46
+
47
+ const OpenAiClientLayer = OpenAiClient.layerConfig({
48
+ apiKey: Config.redacted('OPENAI_API_KEY'),
49
+ })
50
+
51
+ const OpenAiModel = OpenAiLanguageModel.layer({
52
+ model: 'gpt-5-nano',
53
+ })
54
+
55
+ // Coach tool handler that uses OpenAI for analysis
56
+ export const coachToolHandler = Effect.fnUntraced(
57
+ function* ({ code, codeType }) {
58
+ // Build the analysis prompt
59
+ const codeTypeContext = codeType ? `This is ${codeType} code using LiveStore. ` : 'This is LiveStore code. '
60
+
61
+ const prompt = `${codeTypeContext}Please review the following code and provide helpful feedback focusing on:
62
+
63
+ 1. LiveStore best practices and conventions
64
+ 2. Schema design and relationships (if applicable)
65
+ 3. Query optimization and performance
66
+ 4. Code structure and maintainability
67
+ 5. Security considerations
68
+ 6. Naming conventions
69
+ 7. TypeScript usage and type safety
70
+
71
+ Code to review:
72
+ \`\`\`typescript
73
+ ${code}
74
+ \`\`\`
75
+
76
+ Please provide:
77
+ 1. Overall assessment and quality score (1-10)
78
+ 2. Specific areas for improvement
79
+ 3. Best practice recommendations
80
+ 4. Any potential issues or concerns
81
+
82
+ Format your response as constructive feedback that helps developers improve their LiveStore usage.`
83
+
84
+ const systemPrompt = `You are an expert LiveStore developer and code reviewer. Provide constructive, specific, and actionable feedback on LiveStore code. Focus on best practices, performance, and maintainability.`
85
+
86
+ // Get OpenAI client and call the API
87
+ const llm = yield* AiLanguageModel.AiLanguageModel
88
+ const completion = yield* llm.generateText({ prompt, system: systemPrompt })
89
+
90
+ const feedback = completion.text ?? 'Unable to generate feedback'
91
+
92
+ // Extract suggestions from the feedback (simple approach)
93
+ const suggestions: string[] = feedback
94
+ .split('\n')
95
+ .filter(
96
+ (line: string) =>
97
+ line.trim().startsWith('-') ||
98
+ line.trim().startsWith('*') ||
99
+ line.trim().startsWith('•') ||
100
+ line.toLowerCase().includes('recommend') ||
101
+ line.toLowerCase().includes('consider') ||
102
+ line.toLowerCase().includes('improve'),
103
+ )
104
+ .map((line: string) => line.replace(/^[-*•]\s*/, '').trim())
105
+ .filter((suggestion: string) => suggestion.length > 10)
106
+ .slice(0, 5) // Limit to 5 suggestions
107
+
108
+ // Try to extract a score (simple regex approach)
109
+ const scoreMatch = feedback.match(/(?:score|rating|quality).*?(\d+(?:\.\d+)?)/i)
110
+ const score = scoreMatch ? Number.parseFloat(scoreMatch[1] ?? '0') : undefined
111
+
112
+ return {
113
+ feedback,
114
+ score: score && score >= 1 && score <= 10 ? score : undefined,
115
+ suggestions,
116
+ }
117
+ },
118
+ Effect.provide(Layer.provideMerge(OpenAiModel, OpenAiClientLayer)),
119
+ Effect.provide(FetchHttpClient.layer),
120
+ Effect.catchTag('ConfigError', (e) => Effect.die(e)),
121
+ )
@@ -0,0 +1,169 @@
1
+ import { AiTool, AiToolkit, Effect, Schema } from '@livestore/utils/effect'
2
+ import { blogSchemaContent } from '../mcp-content/schemas/blog.ts'
3
+ import { ecommerceSchemaContent } from '../mcp-content/schemas/ecommerce.ts'
4
+ import { socialSchemaContent } from '../mcp-content/schemas/social.ts'
5
+ import { todoSchemaContent } from '../mcp-content/schemas/todo.ts'
6
+ import { coachTool, coachToolHandler } from './mcp-coach.ts'
7
+
8
+ // Create toolkit with tools and handlers following Tim Smart's pattern
9
+ export const livestoreToolkit = AiToolkit.make(
10
+ coachTool,
11
+
12
+ AiTool.make('livestore_generate_schema', {
13
+ description:
14
+ 'Generate a LiveStore schema for a specific use case. Choose from predefined types (todo, blog, social, ecommerce) or request a custom schema by providing a description.',
15
+ parameters: {
16
+ schemaType: Schema.String.annotations({
17
+ description: "Schema type: 'todo', 'blog', 'social', 'ecommerce', or 'custom'",
18
+ }),
19
+ customDescription: Schema.optional(
20
+ Schema.String.annotations({
21
+ description:
22
+ "For custom schemas: describe your data model needs (e.g., 'user management system with roles and permissions')",
23
+ }),
24
+ ),
25
+ },
26
+ success: Schema.Struct({
27
+ schemaCode: Schema.String.annotations({
28
+ description: 'The generated LiveStore schema TypeScript code',
29
+ }),
30
+ explanation: Schema.String.annotations({
31
+ description: 'Brief explanation of the schema structure',
32
+ }),
33
+ }),
34
+ }),
35
+
36
+ AiTool.make('livestore_get_example_schema', {
37
+ description:
38
+ 'Get a complete example LiveStore schema with TypeScript code. Returns ready-to-use schema definitions for common application types.',
39
+ parameters: {
40
+ type: Schema.String.annotations({
41
+ description: "Example type: 'todo', 'blog', 'social', or 'ecommerce'",
42
+ }),
43
+ },
44
+ success: Schema.Struct({
45
+ schemaCode: Schema.String.annotations({
46
+ description: 'The complete LiveStore schema code',
47
+ }),
48
+ description: Schema.String.annotations({
49
+ description: 'Description of what this schema models',
50
+ }),
51
+ }),
52
+ })
53
+ .annotate(AiTool.Readonly, true)
54
+ .annotate(AiTool.Destructive, false),
55
+ )
56
+
57
+ // Tool handlers using Tim Smart's pattern
58
+ export const toolHandlers = livestoreToolkit.of({
59
+ livestore_coach: coachToolHandler,
60
+
61
+ livestore_generate_schema: Effect.fnUntraced(function* ({ schemaType, customDescription }) {
62
+ let schemaCode: string
63
+ let explanation: string
64
+
65
+ switch (schemaType.toLowerCase()) {
66
+ case 'todo':
67
+ schemaCode = todoSchemaContent
68
+ explanation = 'Todo application schema with tasks, tags, and many-to-many relationships'
69
+ break
70
+ case 'blog':
71
+ schemaCode = blogSchemaContent
72
+ explanation = 'Blog platform schema with posts, comments, and publishing workflow'
73
+ break
74
+ case 'social':
75
+ schemaCode = socialSchemaContent
76
+ explanation = 'Social media schema with users, posts, follows, and likes'
77
+ break
78
+ case 'ecommerce':
79
+ schemaCode = ecommerceSchemaContent
80
+ explanation = 'E-commerce schema with products, categories, orders, and inventory'
81
+ break
82
+ case 'custom': {
83
+ if (!customDescription) {
84
+ schemaCode = `// Custom schema requested but no description provided
85
+ import { Schema } from '@livestore/livestore'
86
+
87
+ // Please provide a description of your data model needs
88
+ export const CustomSchema = Schema.table('items', {
89
+ id: Schema.id,
90
+ name: Schema.text,
91
+ createdAt: Schema.datetime.default('now')
92
+ })
93
+
94
+ export const schema = Schema.create({
95
+ items: CustomSchema
96
+ })`
97
+ explanation = 'Basic custom schema template - please provide more details about your requirements'
98
+ break
99
+ }
100
+
101
+ // Generate a basic custom schema based on description
102
+ const tableName = customDescription.toLowerCase().includes('user')
103
+ ? 'users'
104
+ : customDescription.toLowerCase().includes('product')
105
+ ? 'products'
106
+ : customDescription.toLowerCase().includes('post')
107
+ ? 'posts'
108
+ : 'items'
109
+
110
+ schemaCode = `// Custom schema based on: ${customDescription}
111
+ import { Schema } from '@livestore/livestore'
112
+
113
+ export const ${tableName.charAt(0).toUpperCase() + tableName.slice(0, -1)}Schema = Schema.table('${tableName}', {
114
+ id: Schema.id,
115
+ name: Schema.text,
116
+ description: Schema.text.optional(),
117
+ createdAt: Schema.datetime.default('now'),
118
+ updatedAt: Schema.datetime.default('now')
119
+ })
120
+
121
+ export const schema = Schema.create({
122
+ ${tableName}: ${tableName.charAt(0).toUpperCase() + tableName.slice(0, -1)}Schema
123
+ })`
124
+
125
+ explanation = `Custom schema generated for: ${customDescription}. This is a basic template - you may need to customize the fields based on your specific requirements.`
126
+ break
127
+ }
128
+ default:
129
+ schemaCode = todoSchemaContent
130
+ explanation =
131
+ 'Unknown schema type, returning todo example. Available types: todo, blog, social, ecommerce, custom'
132
+ }
133
+
134
+ return { schemaCode, explanation }
135
+ }),
136
+
137
+ livestore_get_example_schema: Effect.fnUntraced(function* ({ type }) {
138
+ let schemaCode: string
139
+ let description: string
140
+
141
+ switch (type.toLowerCase()) {
142
+ case 'todo':
143
+ schemaCode = todoSchemaContent
144
+ description =
145
+ 'Complete todo application with tasks, tags, and many-to-many relationships. Includes boolean completion status and timestamps.'
146
+ break
147
+ case 'blog':
148
+ schemaCode = blogSchemaContent
149
+ description =
150
+ 'Blog platform with posts and comments. Features published status, unique slugs, and author attribution.'
151
+ break
152
+ case 'social':
153
+ schemaCode = socialSchemaContent
154
+ description =
155
+ 'Social media platform with users, posts, follows, and likes. Includes user profiles and social interactions.'
156
+ break
157
+ case 'ecommerce':
158
+ schemaCode = ecommerceSchemaContent
159
+ description =
160
+ 'E-commerce platform with products, categories, orders, and inventory management. Features order items and status tracking.'
161
+ break
162
+ default:
163
+ schemaCode = todoSchemaContent
164
+ description = 'Default todo example (unknown type requested). Available types: todo, blog, social, ecommerce'
165
+ }
166
+
167
+ return { schemaCode, description }
168
+ }),
169
+ })
@@ -0,0 +1,97 @@
1
+ import { Effect, Layer, Logger, McpServer } from '@livestore/utils/effect'
2
+ import { Cli, PlatformNode } from '@livestore/utils/node'
3
+ import { architectureContent } from '../mcp-content/architecture.ts'
4
+ import { featuresContent } from '../mcp-content/features.ts'
5
+ import { gettingStartedContent } from '../mcp-content/getting-started.ts'
6
+ // Content imports
7
+ import { overviewContent } from '../mcp-content/overview.ts'
8
+ import { blogSchemaContent } from '../mcp-content/schemas/blog.ts'
9
+ import { ecommerceSchemaContent } from '../mcp-content/schemas/ecommerce.ts'
10
+ import { socialSchemaContent } from '../mcp-content/schemas/social.ts'
11
+ import { todoSchemaContent } from '../mcp-content/schemas/todo.ts'
12
+
13
+ // Tools imports
14
+ import { livestoreToolkit, toolHandlers } from './mcp-tools.ts'
15
+
16
+ const LivestoreResources = Layer.mergeAll(
17
+ McpServer.resource({
18
+ uri: 'livestore://overview',
19
+ name: 'LiveStore Overview',
20
+ description: 'Overview of LiveStore - the local-first data platform',
21
+ content: Effect.succeed(overviewContent),
22
+ }),
23
+
24
+ McpServer.resource({
25
+ uri: 'livestore://features',
26
+ name: 'LiveStore Features',
27
+ description: 'Core features and capabilities of LiveStore',
28
+ content: Effect.succeed(featuresContent),
29
+ }),
30
+
31
+ McpServer.resource({
32
+ uri: 'livestore://getting-started',
33
+ name: 'Getting Started with LiveStore',
34
+ description: 'Quick start guide for LiveStore development',
35
+ content: Effect.succeed(gettingStartedContent),
36
+ }),
37
+
38
+ McpServer.resource({
39
+ uri: 'livestore://architecture',
40
+ name: 'LiveStore Architecture',
41
+ description: 'Technical architecture and design principles of LiveStore',
42
+ content: Effect.succeed(architectureContent),
43
+ }),
44
+
45
+ McpServer.resource({
46
+ uri: 'livestore://schemas/todo',
47
+ name: 'Todo App Schema',
48
+ description: 'Complete LiveStore schema for a todo application with tags',
49
+ content: Effect.succeed(todoSchemaContent),
50
+ }),
51
+
52
+ McpServer.resource({
53
+ uri: 'livestore://schemas/blog',
54
+ name: 'Blog Platform Schema',
55
+ description: 'LiveStore schema for a blog platform with posts and comments',
56
+ content: Effect.succeed(blogSchemaContent),
57
+ }),
58
+
59
+ McpServer.resource({
60
+ uri: 'livestore://schemas/social',
61
+ name: 'Social Media Schema',
62
+ description: 'LiveStore schema for a social media platform with users, posts, and interactions',
63
+ content: Effect.succeed(socialSchemaContent),
64
+ }),
65
+
66
+ McpServer.resource({
67
+ uri: 'livestore://schemas/ecommerce',
68
+ name: 'E-commerce Schema',
69
+ description: 'LiveStore schema for an e-commerce platform with products, orders, and categories',
70
+ content: Effect.succeed(ecommerceSchemaContent),
71
+ }),
72
+ )
73
+
74
+ const LivestoreTools = McpServer.toolkit(livestoreToolkit).pipe(Layer.provide(livestoreToolkit.toLayer(toolHandlers)))
75
+
76
+ const mcpServerCommand = Cli.Command.make(
77
+ 'server',
78
+ {},
79
+ Effect.fn(function* () {
80
+ yield* Effect.log('🚀 Starting LiveStore MCP Server...')
81
+
82
+ // Following Tim Smart's exact pattern from main.ts
83
+ return yield* McpServer.layerStdio({
84
+ name: 'livestore-mcp',
85
+ version: '0.1.0',
86
+ stdin: PlatformNode.NodeStream.stdin,
87
+ stdout: PlatformNode.NodeSink.stdout,
88
+ }).pipe(
89
+ Layer.provide(LivestoreResources),
90
+ Layer.provide(LivestoreTools),
91
+ Layer.provide(Logger.add(Logger.prettyLogger({ stderr: true }))),
92
+ Layer.launch,
93
+ )
94
+ }),
95
+ )
96
+
97
+ export const mcpCommand = Cli.Command.make('mcp').pipe(Cli.Command.withSubcommands([mcpServerCommand]))