33import json
44import sys
55from pathlib import Path
6- from typing import Optional
6+ from typing import Any , Optional
77
88import click
99
1010from knowcode import __version__
11- from knowcode .context_synthesizer import ContextSynthesizer
12- from knowcode .graph_builder import GraphBuilder
13- from knowcode .knowledge_store import KnowledgeStore
1411from knowcode .models import EntityKind
12+ from knowcode .service import KnowCodeService
13+ from knowcode .knowledge_store import KnowledgeStore
1514
1615
1716@click .group ()
@@ -54,28 +53,22 @@ def analyze(directory: str, output: str, ignore: tuple[str, ...], temporal: bool
5453 if coverage :
5554 click .echo (f"Coverage report: { coverage } " )
5655
57- # Build graph
58- builder = GraphBuilder ()
59- builder . build_from_directory (
60- root_dir = directory ,
61- additional_ignores = list (ignore ),
62- analyze_temporal = temporal ,
63- coverage_path = Path ( coverage ) if coverage else None ,
56+ service = KnowCodeService ()
57+ stats = service . analyze (
58+ directory = directory ,
59+ output = output ,
60+ ignore = list (ignore ),
61+ temporal = temporal ,
62+ coverage = coverage ,
6463 )
6564
66- # Create store and save
67- store = KnowledgeStore .from_graph_builder (builder )
68- output_path = Path (output )
69- store .save (output_path )
70-
71- # Print summary
72- stats = builder .stats ()
7365 click .echo ("\n ✓ Analysis complete!" )
7466 click .echo (f" Entities: { stats ['total_entities' ]} " )
7567 click .echo (f" Relationships: { stats ['total_relationships' ]} " )
76- if stats [ 'total_errors' ] > 0 :
68+ if stats . get ( 'total_errors' , 0 ) > 0 :
7769 click .echo (f" Errors: { stats ['total_errors' ]} " )
7870
71+ output_path = Path (output )
7972 save_path = output_path / KnowledgeStore .DEFAULT_FILENAME if output_path .is_dir () else output_path
8073 click .echo (f"\n Saved to: { save_path } " )
8174
@@ -101,68 +94,26 @@ def query(query_type: str, target: str, store: str, as_json: bool) -> None:
10194 TARGET: Entity ID or search pattern
10295 """
10396 try :
104- knowledge = KnowledgeStore . load ( store )
97+ service = KnowCodeService ( store_path = store )
10598 except FileNotFoundError :
10699 click .echo ("Error: Knowledge store not found. Run 'knowcode analyze' first." , err = True )
107100 sys .exit (1 )
108101
109- results : list [dict [str , str ]] = []
102+ results : list [dict [str , Any ]] = []
110103
111104 if query_type == "search" :
112- entities = knowledge .search (target )
113- for e in entities :
114- results .append ({
115- "id" : e .id ,
116- "kind" : e .kind .value ,
117- "name" : e .qualified_name ,
118- "file" : e .location .file_path ,
119- "line" : str (e .location .line_start ),
120- })
105+ results = service .search (target )
121106
122107 elif query_type == "callers" :
123- entity = knowledge .get_entity (target )
124- if not entity :
125- # Try searching
126- matches = knowledge .search (target )
127- if matches :
128- entity = matches [0 ]
129- click .echo (f"Using: { entity .id } " )
130-
131- if entity :
132- callers = knowledge .get_callers (entity .id )
133- for c in callers :
134- results .append ({
135- "id" : c .id ,
136- "name" : c .qualified_name ,
137- "file" : c .location .file_path ,
138- })
108+ results = service .get_callers (target )
139109
140110 elif query_type == "callees" :
141- entity = knowledge .get_entity (target )
142- if not entity :
143- matches = knowledge .search (target )
144- if matches :
145- entity = matches [0 ]
146- click .echo (f"Using: { entity .id } " )
147-
148- if entity :
149- callees = knowledge .get_callees (entity .id )
150- for c in callees :
151- results .append ({
152- "id" : c .id ,
153- "name" : c .qualified_name ,
154- })
111+ results = service .get_callees (target )
155112
156113 elif query_type == "deps" :
157- entity = knowledge .get_entity (target )
158- if not entity :
159- matches = knowledge .search (target )
160- if matches :
161- entity = matches [0 ]
162- click .echo (f"Using: { entity .id } " )
163-
114+ entity = service .store .get_entity (target ) or next (iter (service .store .search (target )), None )
164115 if entity :
165- deps = knowledge .get_dependencies (entity .id )
116+ deps = service . store .get_dependencies (entity .id )
166117 for d in deps :
167118 results .append ({
168119 "id" : d .id ,
@@ -179,6 +130,8 @@ def query(query_type: str, target: str, store: str, as_json: bool) -> None:
179130 else :
180131 for r in results :
181132 name = r .get ("name" , r .get ("id" , "unknown" ))
133+ if "qualified_name" in r :
134+ name = r ["qualified_name" ]
182135 extra = ""
183136 if "file" in r :
184137 extra = f" ({ r ['file' ]} :{ r .get ('line' , '' )} )"
@@ -207,32 +160,20 @@ def context(target: str, store: str, max_tokens: int) -> None:
207160 TARGET: Entity ID or search pattern
208161 """
209162 try :
210- knowledge = KnowledgeStore . load ( store )
163+ service = KnowCodeService ( store_path = store )
211164 except FileNotFoundError :
212165 click .echo ("Error: Knowledge store not found. Run 'knowcode analyze' first." , err = True )
213166 sys .exit (1 )
214167
215- synthesizer = ContextSynthesizer (knowledge , max_tokens = max_tokens )
216-
217- # Try exact match first
218- entity = knowledge .get_entity (target )
219- if not entity :
220- # Try search
221- matches = knowledge .search (target )
222- if matches :
223- entity = matches [0 ]
224- click .echo (f"Using: { entity .id } \n " , err = True )
225-
226- if not entity :
227- click .echo (f"Entity not found: { target } " , err = True )
228- sys .exit (1 )
229-
230- bundle = synthesizer .synthesize (entity .id )
231- if bundle :
232- click .echo (bundle .context_text )
233- click .echo (f"\n --- { bundle .total_chars } chars, { bundle .total_tokens } tokens, { len (bundle .included_entities )} entities ---" , err = True )
234- if bundle .truncated :
168+ try :
169+ bundle_dict = service .get_context (target , max_tokens = max_tokens )
170+ click .echo (bundle_dict ["context_text" ])
171+ click .echo (f"\n --- { len (bundle_dict ['context_text' ])} chars, { bundle_dict ['total_tokens' ]} tokens, { len (bundle_dict ['included_entities' ])} entities ---" , err = True )
172+ if bundle_dict ["truncated" ]:
235173 click .echo ("(truncated)" , err = True )
174+ except ValueError as e :
175+ click .echo (f"Error: { e } " , err = True )
176+ sys .exit (1 )
236177
237178
238179@cli .command ()
@@ -251,11 +192,12 @@ def context(target: str, store: str, max_tokens: int) -> None:
251192def export (store : str , output : str ) -> None :
252193 """Export knowledge store as Markdown documentation."""
253194 try :
254- knowledge = KnowledgeStore . load ( store )
195+ service = KnowCodeService ( store_path = store )
255196 except FileNotFoundError :
256197 click .echo ("Error: Knowledge store not found. Run 'knowcode analyze' first." , err = True )
257198 sys .exit (1 )
258199
200+ knowledge = service .store
259201 output_dir = Path (output )
260202 output_dir .mkdir (parents = True , exist_ok = True )
261203
@@ -298,36 +240,50 @@ def export(store: str, output: str) -> None:
298240def stats (store : str ) -> None :
299241 """Show statistics about the knowledge store."""
300242 try :
301- knowledge = KnowledgeStore . load ( store )
243+ service = KnowCodeService ( store_path = store )
302244 except FileNotFoundError :
303245 click .echo ("Error: Knowledge store not found. Run 'knowcode analyze' first." , err = True )
304246 sys .exit (1 )
305247
248+ s = service .get_stats ()
306249 click .echo ("Knowledge Store Statistics" )
307250 click .echo ("-" * 30 )
308251
309- # Count by kind
310- by_kind : dict [str , int ] = {}
311- for entity in knowledge .entities .values ():
312- kind = entity .kind .value
313- by_kind [kind ] = by_kind .get (kind , 0 ) + 1
314-
315- click .echo (f"\n Total Entities: { len (knowledge .entities )} " )
316- for kind , count in sorted (by_kind .items ()):
252+ click .echo (f"\n Total Entities: { s ['total_entities' ]} " )
253+ for kind , count in sorted (s ['entities_by_kind' ].items ()):
317254 click .echo (f" { kind } : { count } " )
318255
319- click .echo (f"\n Total Relationships: { len (knowledge .relationships )} " )
320-
321- # Relationship types
322- rel_types : dict [str , int ] = {}
323- for rel in knowledge .relationships :
324- kind = rel .kind .value
325- rel_types [kind ] = rel_types .get (kind , 0 ) + 1
326-
327- for kind , count in sorted (rel_types .items ()):
256+ click .echo (f"\n Total Relationships: { s ['total_relationships' ]} " )
257+ for kind , count in sorted (s ['relationships_by_type' ].items ()):
328258 click .echo (f" { kind } : { count } " )
329259
330260
261+ @cli .command ()
262+ @click .option (
263+ "--store" , "-s" ,
264+ type = click .Path (exists = True ),
265+ default = "." ,
266+ help = "Path to knowledge store file or directory" ,
267+ )
268+ @click .option (
269+ "--host" ,
270+ default = "127.0.0.1" ,
271+ help = "Host to bind the server to (default: 127.0.0.1)" ,
272+ )
273+ @click .option (
274+ "--port" ,
275+ default = 8000 ,
276+ help = "Port to bind the server to (default: 8000)" ,
277+ )
278+ def server (store : str , host : str , port : int ) -> None :
279+ """Start the KnowCode intelligence server."""
280+ from knowcode .server .main import start_server
281+
282+ click .echo (f"Starting KnowCode server on { host } :{ port } " )
283+ click .echo (f"Using knowledge store: { store } " )
284+
285+ start_server (host = host , port = port , store_path = store )
286+
331287
332288@cli .command ()
333289@click .argument ("target" , required = False )
@@ -349,11 +305,13 @@ def history(target: Optional[str], store: str, limit: int) -> None:
349305 TARGET: Optional entity ID or search pattern. If omitted, shows commit log.
350306 """
351307 try :
352- knowledge = KnowledgeStore . load ( store )
308+ service = KnowCodeService ( store_path = store )
353309 except FileNotFoundError :
354310 click .echo ("Error: Knowledge store not found. Run 'knowcode analyze' first." , err = True )
355311 sys .exit (1 )
356312
313+ knowledge = service .store
314+
357315 if not target :
358316 # Show recent commits
359317 commits = knowledge .get_entities_by_kind ("commit" )
0 commit comments