55 "context"
66 "encoding/json"
77 "fmt"
8- "io"
98 "io/fs"
10- "net/http"
119 "os"
1210 "os/exec"
1311 "path/filepath"
@@ -23,52 +21,37 @@ import (
2321
2422 "encr.dev/cli/cmd/encore/auth"
2523 "encr.dev/cli/cmd/encore/cmdutil"
24+ "encr.dev/cli/cmd/encore/llm_rules"
2625 "encr.dev/cli/internal/platform"
2726 "encr.dev/cli/internal/telemetry"
2827 "encr.dev/internal/conf"
2928 "encr.dev/internal/env"
29+ "encr.dev/internal/userconfig"
3030 "encr.dev/internal/version"
3131 "encr.dev/pkg/github"
3232 "encr.dev/pkg/xos"
3333 daemonpb "encr.dev/proto/encore/daemon"
3434)
3535
36- const mcpJSON string = `{
37- "mcpServers": {
38- "encore-mcp": {
39- "command": "encore",
40- "args": ["mcp", "run", "--app={{ENCORE_APP_ID}}"]
41- }
42- }
43- }
44- `
45-
46- const mdcTemplate string = `---
47- description: Encore %s rules
48- globs:
49- alwaysApply: true
50- ---
51- %s
52- `
53-
5436var (
5537 createAppTemplate string
5638 createAppOnPlatform bool
57- createAppLang string
58- createAppEditor = cmdutil.Oneof {
39+ createAppLang = cmdutil.Oneof {
5940 Value : "" ,
60- Allowed : []string {"cursor" },
61- Flag : "editor" ,
62- FlagShort : "" , // no short flag
63- Desc : "Initialize the app for a cursor-based editor" ,
41+ Allowed : cmdutil .LanguageFlagValues (),
42+ Flag : "lang" ,
43+ FlagShort : "l" ,
44+ Desc : "Programming language to use for the app." ,
45+ TypeDesc : "string" ,
46+ }
47+ createAppLLMRules = cmdutil.Oneof {
48+ Value : "" ,
49+ Allowed : llm_rules .LLMRulesFlagValues (),
50+ Flag : "llm-rules" ,
51+ FlagShort : "r" ,
52+ Desc : "Initialize the app with llm rules for a specific tool" ,
6453 TypeDesc : "string" ,
6554 }
66- )
67-
68- type editor string
69-
70- const (
71- EditorCursor editor = "cursor"
7255)
7356
7457var createAppCmd = & cobra.Command {
@@ -82,7 +65,19 @@ var createAppCmd = &cobra.Command{
8265 if len (args ) > 0 {
8366 name = args [0 ]
8467 }
85- if err := createApp (context .Background (), name , createAppTemplate , language (createAppLang ), editor (createAppEditor .Value )); err != nil {
68+
69+ var tool llm_rules.Tool
70+ if createAppLLMRules .Value == "" {
71+ cfg , err := userconfig .Global ().Get ()
72+ if err != nil {
73+ cmdutil .Fatalf ("Couldn't read user config: %s" , err )
74+ }
75+ tool = llm_rules .Tool (cfg .LLMRules )
76+ } else {
77+ tool = llm_rules .Tool (createAppLLMRules .Value )
78+ }
79+
80+ if err := createApp (context .Background (), name , createAppTemplate , cmdutil .Language (createAppLang .Value ), tool ); err != nil {
8681 cmdutil .Fatal (err )
8782 }
8883 },
@@ -92,8 +87,8 @@ func init() {
9287 appCmd .AddCommand (createAppCmd )
9388 createAppCmd .Flags ().BoolVar (& createAppOnPlatform , "platform" , true , "whether to create the app with the Encore Platform" )
9489 createAppCmd .Flags ().StringVar (& createAppTemplate , "example" , "" , "URL to example code to use." )
95- createAppCmd . Flags (). StringVar ( & createAppLang , "lang" , "" , "Programming language to use for the app. (ts, go)" )
96- createAppEditor .AddFlag (createAppCmd )
90+ createAppLang . AddFlag ( createAppCmd )
91+ createAppLLMRules .AddFlag (createAppCmd )
9792}
9893
9994func promptAccountCreation () {
@@ -163,7 +158,7 @@ func promptRunApp() bool {
163158}
164159
165160// createApp is the implementation of the "encore app create" command.
166- func createApp (ctx context.Context , name , template string , lang language , editor editor ) (err error ) {
161+ func createApp (ctx context.Context , name , template string , lang cmdutil. Language , llmRules llm_rules. Tool ) (err error ) {
167162 defer func () {
168163 // We need to send the telemetry synchronously to ensure it's sent before the command exits.
169164 telemetry .SendSync ("app.create" , map [string ]any {
@@ -177,15 +172,15 @@ func createApp(ctx context.Context, name, template string, lang language, editor
177172
178173 promptAccountCreation ()
179174
180- if name == "" || template == "" {
181- name , template , lang = selectTemplate (name , template , lang , false )
175+ if name == "" || template == "" || llmRules == "" {
176+ name , template , lang , llmRules = createAppForm (name , template , lang , llmRules , false )
182177 }
183178 // Treat the special name "empty" as the empty app template
184179 // (the rest of the code assumes that's the empty string).
185180 if template == "empty" {
186181 template = ""
187182 }
188- if template == "" && lang == languageTS {
183+ if template == "" && lang == cmdutil . LanguageTS {
189184 template = "ts/empty"
190185 }
191186
@@ -286,7 +281,7 @@ func createApp(ctx context.Context, name, template string, lang language, editor
286281
287282 // Update to latest encore.dev release
288283 if _ , err := os .Stat (filepath .Join (name , appRootRelpath , "go.mod" )); err == nil {
289- lang = languageGo
284+ lang = cmdutil . LanguageGo
290285 s := spinner .New (spinner .CharSets [14 ], 100 * time .Millisecond )
291286 s .Prefix = "Running go get encore.dev@latest"
292287 s .Start ()
@@ -295,7 +290,7 @@ func createApp(ctx context.Context, name, template string, lang language, editor
295290 }
296291 s .Stop ()
297292 } else if _ , err := os .Stat (filepath .Join (name , appRootRelpath , "package.json" )); err == nil {
298- lang = languageTS
293+ lang = cmdutil . LanguageTS
299294 s := spinner .New (spinner .CharSets [14 ], 100 * time .Millisecond )
300295 s .Prefix = "Running npm install encore.dev@latest"
301296 s .Start ()
@@ -336,23 +331,8 @@ func createApp(ctx context.Context, name, template string, lang language, editor
336331 color .Red ("Failed to create app on daemon: %s\n " , err )
337332 }
338333
339- switch editor {
340- case EditorCursor :
341- cursorDir := filepath .Join (name , appRootRelpath , ".cursor" )
342- rulesDir := filepath .Join (cursorDir , "rules" )
343- err := os .MkdirAll (rulesDir , 0755 )
344- if err != nil {
345- return err
346- }
347- err = os .WriteFile (filepath .Join (cursorDir , "mcp.json" ), []byte (strings .ReplaceAll (mcpJSON , "{{ENCORE_APP_ID}}" , appResp .AppId )), 0644 )
348- if err != nil {
349- return err
350- }
351- llmInstructions , err := downloadLLMInstructions (lang )
352- err = os .WriteFile (filepath .Join (rulesDir , "encore.mdc" ), fmt .Appendf (nil , mdcTemplate , lang , string (llmInstructions )), 0644 )
353- if err != nil {
354- return err
355- }
334+ if err := llm_rules .SetupLLMRules (llmRules , lang , filepath .Join (name , appRootRelpath ), appResp .AppId ); err != nil {
335+ color .Red ("Failed to setup LLM rules: %s\n " , err )
356336 }
357337
358338 cmdutil .ClearTerminalExceptFirstNLines (0 )
@@ -364,16 +344,7 @@ func createApp(ctx context.Context, name, template string, lang language, editor
364344 fmt .Printf ("Web URL: %s%s" , cyanf ("https://app.encore.cloud/" + app .Slug ), cmdutil .Newline )
365345 }
366346 fmt .Printf ("App Root: %s\n " , cyanf (appRoot ))
367- switch editor {
368- case EditorCursor :
369- fmt .Printf ("MCP: %s\n " , cyanf ("Configured in Cursor" ))
370- fmt .Println ()
371- fmt .Println ("Try these prompts in Cursor:" )
372- fmt .Println ("→ \" add image uploads to my hello world app\" " )
373- fmt .Println ("→ \" add a SQL database for storing user profiles\" " )
374- fmt .Println ("→ \" add a pub/sub topic for sending notifications\" " )
375- }
376- fmt .Println ()
347+ llm_rules .PrintLLMRulesInfo (llmRules )
377348 greenBoldF := green .Add (color .Bold ).SprintfFunc ()
378349 fmt .Printf ("Run your app with: %s\n " , greenBoldF ("cd %s && encore run" , filepath .Join (name , appRootRelpath )))
379350 fmt .Println ()
@@ -400,7 +371,7 @@ func createApp(ctx context.Context, name, template string, lang language, editor
400371 _ , _ = cyan .Printf (" encore run\n " )
401372 fmt .Print (" Run your app locally\n \n " )
402373
403- if lang == languageGo {
374+ if lang == cmdutil . LanguageGo {
404375 _ , _ = cyan .Printf (" encore test ./...\n " )
405376 } else {
406377 _ , _ = cyan .Printf (" encore test\n " )
@@ -416,44 +387,15 @@ func createApp(ctx context.Context, name, template string, lang language, editor
416387 return nil
417388}
418389
419- func downloadLLMInstructions (lang language ) (string , error ) {
420- fmt .Println ("Downloading LLM Instructions..." )
421- var url string
422- switch lang {
423- case languageGo :
424- url = "https://raw.githubusercontent.com/encoredev/encore/refs/heads/main/go_llm_instructions.txt"
425- case languageTS :
426- url = "https://raw.githubusercontent.com/encoredev/encore/refs/heads/main/ts_llm_instructions.txt"
427- default :
428- return "" , fmt .Errorf ("unsupported language" )
429- }
430- s := spinner .New (spinner .CharSets [14 ], 100 * time .Millisecond )
431- s .Prefix = "Downloading LLM instructions..."
432- s .Start ()
433- defer s .Stop ()
434- resp , err := http .Get (url )
435- if err != nil {
436- s .FinalMSG = fmt .Sprintf ("failed, skipping: %v" , err .Error ())
437- return "" , err
438- }
439- defer resp .Body .Close ()
440- body , err := io .ReadAll (resp .Body )
441- if err != nil {
442- s .FinalMSG = fmt .Sprintf ("failed, skipping: %v" , err .Error ())
443- return "" , err
444- }
445- return string (body ), nil
446- }
447-
448390// detectLang attempts to detect the application language for an Encore application
449391// situated at appRoot.
450- func detectLang (appRoot string ) language {
392+ func detectLang (appRoot string ) cmdutil. Language {
451393 if _ , err := os .Stat (filepath .Join (appRoot , "go.mod" )); err == nil {
452- return languageGo
394+ return cmdutil . LanguageGo
453395 } else if _ , err := os .Stat (filepath .Join (appRoot , "package.json" )); err == nil {
454- return languageTS
396+ return cmdutil . LanguageTS
455397 }
456- return languageGo
398+ return cmdutil . LanguageGo
457399}
458400
459401func validateName (name string ) error {
0 commit comments