44
55namespace Butschster \ContextGenerator \Console ;
66
7- use Butschster \ContextGenerator \Application \JsonSchema ;
87use Butschster \ContextGenerator \Config \ConfigType ;
98use Butschster \ContextGenerator \Config \Registry \ConfigRegistry ;
109use Butschster \ContextGenerator \DirectoriesInterface ;
11- use Butschster \ContextGenerator \Document \Document ;
12- use Butschster \ContextGenerator \Document \DocumentRegistry ;
13- use Butschster \ContextGenerator \Lib \TreeBuilder \TreeViewConfig ;
14- use Butschster \ContextGenerator \Source \Tree \TreeSource ;
10+ use Butschster \ContextGenerator \Template \Analysis \ProjectAnalysisService ;
11+ use Butschster \ContextGenerator \Template \Registry \TemplateRegistry ;
12+ use Spiral \Console \Attribute \Argument ;
1513use Spiral \Console \Attribute \Option ;
1614use Spiral \Files \FilesInterface ;
1715use Symfony \Component \Console \Attribute \AsCommand ;
2018
2119#[AsCommand(
2220 name: 'init ' ,
23- description: 'Initialize a new context configuration file ' ,
21+ description: 'Initialize a new context configuration file with smart project analysis ' ,
2422)]
2523final class InitCommand extends BaseCommand
2624{
25+ #[Argument(
26+ name: 'template ' ,
27+ description: 'Specific template to use (optional) ' ,
28+ )]
29+ protected ?string $ template = null ;
30+
2731 #[Option(
2832 name: 'config-file ' ,
2933 shortcut: 'c ' ,
3034 description: 'The name of the file to create ' ,
3135 )]
3236 protected string $ configFilename = 'context.yaml ' ;
3337
34- public function __invoke (DirectoriesInterface $ dirs , FilesInterface $ files ): int
35- {
38+ public function __invoke (
39+ DirectoriesInterface $ dirs ,
40+ FilesInterface $ files ,
41+ TemplateRegistry $ templateRegistry ,
42+ ProjectAnalysisService $ analysisService ,
43+ ): int {
3644 $ filename = $ this ->configFilename ;
37- $ ext = \pathinfo ($ filename , PATHINFO_EXTENSION );
45+ $ ext = \pathinfo ($ filename , \ PATHINFO_EXTENSION );
3846
3947 try {
4048 $ type = ConfigType::fromExtension ($ ext );
4149 } catch (\ValueError ) {
4250 $ this ->output ->error (\sprintf ('Unsupported config type: %s ' , $ ext ));
43-
4451 return Command::FAILURE ;
4552 }
4653
@@ -49,27 +56,141 @@ public function __invoke(DirectoriesInterface $dirs, FilesInterface $files): int
4956
5057 if ($ files ->exists ($ filePath )) {
5158 $ this ->output ->error (\sprintf ('Config %s already exists ' , $ filePath ));
59+ return Command::FAILURE ;
60+ }
61+
62+ if ($ this ->template !== null ) {
63+ return $ this ->initWithTemplate ($ files , $ templateRegistry , $ this ->template , $ type , $ filePath );
64+ }
65+
66+ return $ this ->initWithAnalysis ($ dirs , $ files , $ analysisService , $ templateRegistry , $ type , $ filePath );
67+ }
68+
69+ private function initWithTemplate (
70+ FilesInterface $ files ,
71+ TemplateRegistry $ templateRegistry ,
72+ string $ templateName ,
73+ ConfigType $ type ,
74+ string $ filePath ,
75+ ): int {
76+ $ template = $ templateRegistry ->getTemplate ($ templateName );
77+
78+ if ($ template === null ) {
79+ $ this ->output ->error (\sprintf ('Template "%s" not found ' , $ templateName ));
80+
81+ $ this ->output ->note ('Available templates: ' );
82+ foreach ($ templateRegistry ->getAllTemplates () as $ availableTemplate ) {
83+ $ this ->output ->writeln (\sprintf (' - %s: %s ' , $ availableTemplate ->name , $ availableTemplate ->description ));
84+ }
85+
86+ $ this ->output ->writeln ('' );
87+ $ this ->output ->writeln ('Use <info>ctx template:list</info> to see all available templates with details. ' );
5288
5389 return Command::FAILURE ;
5490 }
5591
56- $ config = new ConfigRegistry (
57- schema: JsonSchema::SCHEMA_URL ,
58- );
59-
60- $ config ->register (new DocumentRegistry ([
61- new Document (
62- description: 'Project structure overview ' ,
63- outputPath: 'project-structure.md ' ,
64- firstSource: new TreeSource (
65- sourcePaths: ['src ' ],
66- treeView: new TreeViewConfig (
67- showCharCount: true ,
68- ),
69- ),
70- ),
71- ]));
92+ $ this ->output ->success (\sprintf ('Using template: %s ' , $ template ->description ));
93+
94+ return $ this ->writeConfig ($ files , $ template ->config , $ type , $ filePath );
95+ }
96+
97+ private function initWithAnalysis (
98+ DirectoriesInterface $ dirs ,
99+ FilesInterface $ files ,
100+ ProjectAnalysisService $ analysisService ,
101+ TemplateRegistry $ templateRegistry ,
102+ ConfigType $ type ,
103+ string $ filePath ,
104+ ): int {
105+ $ this ->output ->writeln ('Analyzing project... ' );
106+
107+ $ results = $ analysisService ->analyzeProject ($ dirs ->getRootPath ());
108+ $ bestMatch = $ results [0 ];
109+
110+ // Check if this is a fallback result
111+ $ isFallback = $ bestMatch ->metadata ['isFallback ' ] ?? false ;
112+
113+ if ($ isFallback ) {
114+ $ this ->output ->warning ('No specific project type detected. Using default configuration. ' );
115+ } else {
116+ $ this ->output ->success (\sprintf (
117+ 'Detected: %s (confidence: %.0f%%) ' ,
118+ $ bestMatch ->detectedType ,
119+ $ bestMatch ->confidence * 100 ,
120+ ));
121+ }
72122
123+ if ($ bestMatch ->hasHighConfidence ()) {
124+ $ primaryTemplate = $ bestMatch ->getPrimaryTemplate ();
125+ if ($ primaryTemplate !== null ) {
126+ $ template = $ templateRegistry ->getTemplate ($ primaryTemplate );
127+ if ($ template !== null ) {
128+ $ this ->output ->writeln (\sprintf ('Using template: %s ' , $ template ->description ));
129+ return $ this ->writeConfig ($ files , $ template ->config , $ type , $ filePath );
130+ }
131+ }
132+ }
133+
134+ // Show analysis results for lower confidence matches
135+ return $ this ->showAnalysisResults ($ results , $ templateRegistry , $ files , $ type , $ filePath );
136+ }
137+
138+ private function showAnalysisResults (
139+ array $ results ,
140+ TemplateRegistry $ templateRegistry ,
141+ FilesInterface $ files ,
142+ ConfigType $ type ,
143+ string $ filePath ,
144+ ): int {
145+ $ this ->output ->title ('Analysis Results ' );
146+
147+ foreach ($ results as $ result ) {
148+ // Skip showing fallback results in detailed analysis
149+ if ($ result ->metadata ['isFallback ' ] ?? false ) {
150+ continue ;
151+ }
152+
153+ $ this ->output ->section (\sprintf (
154+ '%s: %s (%.0f%% confidence) ' ,
155+ \ucfirst ((string ) $ result ->analyzerName ),
156+ $ result ->detectedType ,
157+ $ result ->confidence * 100 ,
158+ ));
159+
160+ if (!empty ($ result ->suggestedTemplates )) {
161+ $ this ->output ->writeln ('Suggested templates: ' );
162+ foreach ($ result ->suggestedTemplates as $ templateName ) {
163+ $ template = $ templateRegistry ->getTemplate ($ templateName );
164+ if ($ template !== null ) {
165+ $ this ->output ->writeln (\sprintf (' - %s: %s ' , $ templateName , $ template ->description ));
166+ }
167+ }
168+ }
169+ }
170+
171+ // Use the best match (could be fallback if no other matches)
172+ $ bestResult = $ results [0 ];
173+ $ primaryTemplate = $ bestResult ->getPrimaryTemplate ();
174+
175+ if ($ primaryTemplate !== null ) {
176+ $ template = $ templateRegistry ->getTemplate ($ primaryTemplate );
177+ if ($ template !== null ) {
178+ $ this ->output ->note (\sprintf ('Using template: %s ' , $ template ->description ));
179+ return $ this ->writeConfig ($ files , $ template ->config , $ type , $ filePath );
180+ }
181+ }
182+
183+ // This should never happen, but provide safety fallback
184+ $ this ->output ->error ('No suitable template found ' );
185+ return Command::FAILURE ;
186+ }
187+
188+ private function writeConfig (
189+ FilesInterface $ files ,
190+ ConfigRegistry $ config ,
191+ ConfigType $ type ,
192+ string $ filePath ,
193+ ): int {
73194 try {
74195 $ content = match ($ type ) {
75196 ConfigType::Json => \json_encode ($ config , JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES ),
@@ -85,13 +206,6 @@ public function __invoke(DirectoriesInterface $dirs, FilesInterface $files): int
85206 };
86207 } catch (\Throwable $ e ) {
87208 $ this ->output ->error (\sprintf ('Failed to create config: %s ' , $ e ->getMessage ()));
88-
89- return Command::FAILURE ;
90- }
91-
92- if ($ files ->exists ($ filePath )) {
93- $ this ->output ->error (\sprintf ('Config %s already exists ' , $ filePath ));
94-
95209 return Command::FAILURE ;
96210 }
97211
0 commit comments