ctxgraph 0.2.3__tar.gz → 0.3.0__tar.gz

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 (53) hide show
  1. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/PKG-INFO +156 -75
  2. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/README.md +155 -74
  3. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/pyproject.toml +1 -1
  4. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph/capsule/renderer.py +9 -1
  5. ctxgraph-0.3.0/src/ctxgraph/capsule/savings.py +85 -0
  6. ctxgraph-0.3.0/src/ctxgraph/cli/main.py +537 -0
  7. ctxgraph-0.3.0/src/ctxgraph/config/init.py +26 -0
  8. ctxgraph-0.3.0/src/ctxgraph/history.py +69 -0
  9. ctxgraph-0.3.0/src/ctxgraph/skills/__init__.py +38 -0
  10. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph/view/visualizer.py +10 -2
  11. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph.egg-info/PKG-INFO +156 -75
  12. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph.egg-info/SOURCES.txt +5 -0
  13. ctxgraph-0.3.0/tests/test_e2e.py +219 -0
  14. ctxgraph-0.2.3/src/ctxgraph/cli/main.py +0 -262
  15. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/setup.cfg +0 -0
  16. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph/__init__.py +0 -0
  17. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph/analyzers/__init__.py +0 -0
  18. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph/analyzers/python/__init__.py +0 -0
  19. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph/analyzers/python/importer.py +0 -0
  20. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph/analyzers/python/semantic.py +0 -0
  21. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph/analyzers/python/symbols.py +0 -0
  22. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph/capsule/__init__.py +0 -0
  23. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph/cli/__init__.py +0 -0
  24. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph/clients/__init__.py +0 -0
  25. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph/clients/models.py +0 -0
  26. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph/config/__init__.py +0 -0
  27. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph/config/providers.py +0 -0
  28. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph/config/settings.py +0 -0
  29. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph/exclude/__init__.py +0 -0
  30. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph/exclude/patterns.py +0 -0
  31. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph/graph/__init__.py +0 -0
  32. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph/graph/builder.py +0 -0
  33. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph/graph/models.py +0 -0
  34. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph/graph/query.py +0 -0
  35. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph/graph/storage.py +0 -0
  36. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph/mcp/__init__.py +0 -0
  37. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph/mcp/server.py +0 -0
  38. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph/view/__init__.py +0 -0
  39. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph/wrapper/__init__.py +0 -0
  40. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph/wrapper/claude.py +0 -0
  41. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph.egg-info/dependency_links.txt +0 -0
  42. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph.egg-info/entry_points.txt +0 -0
  43. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph.egg-info/requires.txt +0 -0
  44. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/src/ctxgraph.egg-info/top_level.txt +0 -0
  45. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/tests/test_analyzers.py +0 -0
  46. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/tests/test_benchmark.py +0 -0
  47. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/tests/test_capsule.py +0 -0
  48. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/tests/test_config.py +0 -0
  49. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/tests/test_integration.py +0 -0
  50. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/tests/test_model_mode.py +0 -0
  51. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/tests/test_models.py +0 -0
  52. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/tests/test_query.py +0 -0
  53. {ctxgraph-0.2.3 → ctxgraph-0.3.0}/tests/test_storage.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ctxgraph
3
- Version: 0.2.3
3
+ Version: 0.3.0
4
4
  Summary: AI context engine for Python — cuts LLM tokens 97% via code knowledge graphs. Build, query, and generate compact context capsules for Claude, OpenAI, Ollama.
5
5
  Author: ctxgraph contributors
6
6
  License: MIT
@@ -86,10 +86,10 @@ Repository (.py files)
86
86
  ### Architecture
87
87
 
88
88
  ```
89
- ┌─────────┐ ┌──────────────┐ ┌──────────────┐
90
- │ CLI │───▶│ Analyzers │───▶│ SQLite DB │
91
- │ typer │ │ AST-based │ │ .ctxgraph/ │
92
- └────┬────┘ └──────────────┘ └──────┬───────┘
89
+ ┌─────────┐ ┌──────────────┐ ┌──────────────┐ ┌─────────────┐
90
+ │ CLI │───▶│ Analyzers │───▶│ SQLite DB │───▶│ Skills/Hist
91
+ │ typer │ │ AST-based │ │ .ctxgraph/ │ │ .ctxgraph/ │
92
+ └────┬────┘ └──────────────┘ └──────┬───────┘ └─────────────┘
93
93
  │ │
94
94
  ├── ctx build ──────────────────────▶│ Graph build
95
95
  │ │
@@ -97,9 +97,17 @@ Repository (.py files)
97
97
  │ │
98
98
  ├── ctx query ◀─────────────────────│ Keyword search
99
99
  │ │
100
- ├── ctx view ◀──────────────────────│ D3.js viz
101
- │ │
102
- ├── ctx serve ◀─────────────────────│ MCP server
100
+ ├── ctx ask ◀──────────────────────│ LLM query + savings
101
+ │ │
102
+ ├── ctx view ◀──────────────────────│ D3.js viz
103
+ │ │
104
+ ├── ctx serve ◀─────────────────────│ MCP server
105
+ │ │
106
+ ├── ctx init ───────────────────────│ Scaffold project
107
+ │ │
108
+ ├── ctx history ◀───────────────────│ Query log
109
+ │ │
110
+ └── ctx skill ◀─────────────────────│ Skill management
103
111
  │ │
104
112
  └── ccg wrapper ───▶ Claude Code ───┘ AI tool
105
113
  ```
@@ -159,10 +167,36 @@ JSON: 426 tokens DSL: 143 tokens
159
167
 
160
168
  **+16.7pp average coverage improvement** — better answers, concrete file names, real code structure.
161
169
 
170
+ ### Token Savings Display
171
+
172
+ Use `--savings` to see how many tokens each capsule saves:
173
+
174
+ ```bash
175
+ ctx capsule "user authentication" --savings
176
+ # ┌──────────────────────────┬──────────────┐
177
+ # │ Metric │ Value │
178
+ # ├──────────────────────────┼──────────────┤
179
+ # │ Raw Project .py Files │ 10,587 tokens│
180
+ # │ Capsule DSL │ 132 tokens │
181
+ # │ JSON Equivalent │ 490 tokens │
182
+ # │ Savings vs Raw │ 98.8% │
183
+ # │ DSL vs JSON │ 73.1% │
184
+ # └──────────────────────────┴──────────────┘
185
+ ```
186
+
187
+ `ctx ask` shows this automatically on every query.
188
+
162
189
  ---
163
190
 
164
191
  ## Commands
165
192
 
193
+ ### `ctx init` — Scaffold project
194
+ ```bash
195
+ ctx init
196
+ # Creates: .ctxgraph/config.toml, .ctxgraph/skills/, .ctxgraph/history.jsonl
197
+ ```
198
+ Sets up a fresh `.ctxgraph/` directory with default config and built-in skills. Idempotent — safe to run on existing projects.
199
+
166
200
  ### `ctx build` — Build knowledge graph
167
201
  ```bash
168
202
  ctx build # Current directory
@@ -170,12 +204,24 @@ ctx build /path/to/project # Specific repo
170
204
  ctx build --exclude "vendor/*" # Custom exclude patterns
171
205
  ```
172
206
 
207
+ ### `ctx ask <query>` — Ask questions via LLM
208
+ ```bash
209
+ ctx ask "how does JWT auth work" # Uses configured provider
210
+ ctx ask "fix login bug" --provider claude --model claude-sonnet-4-20250514
211
+ ctx ask "refactor payment flow" --skill project-style # Activate a skill
212
+ ctx ask "find auth code" --graph # Show graph search results
213
+ ctx ask "deep dive" --mode deep # Deep graph context
214
+ ```
215
+ Shows token savings automatically. Requires a running Ollama instance (or other configured provider).
216
+
173
217
  ### `ctx capsule <query>` — Generate context
174
218
  ```bash
175
219
  ctx capsule "fix JWT token validation" # Balanced (default: 20 nodes, depth 2)
176
220
  ctx capsule "fix JWT token validation" --mode fast # Fast (10 nodes, depth 1)
177
221
  ctx capsule "fix JWT token validation" --mode deep # Deep (40 nodes, depth 3)
178
222
  ctx capsule --overview # Project architecture overview
223
+ ctx capsule "fix auth" --savings # Show token savings table
224
+ ctx capsule "fix auth" --skill project-style # Prepends skill context
179
225
  ```
180
226
 
181
227
  | Mode | Max Nodes | BFS Depth | When to Use |
@@ -217,6 +263,22 @@ Claude Desktop config:
217
263
  ```
218
264
  Tools: `search_graph`, `get_context_capsule`, `get_file_dependencies`, `get_project_overview`.
219
265
 
266
+ ### `ctx history` — Query history
267
+ ```bash
268
+ ctx history # Last 10 queries
269
+ ctx history -n 20 # Last 20
270
+ ctx history --filter "auth" # Filter by keyword
271
+ ctx history --stats # Aggregate statistics
272
+ ```
273
+ History stored in JSONL format at `.ctxgraph/history.jsonl`. Auto-prunes to 1000 entries.
274
+
275
+ ### `ctx skill` — Manage skills
276
+ ```bash
277
+ ctx skill list # Show all available skills
278
+ ctx skill show project-style # Display skill contents
279
+ ```
280
+ Skills are TOML files in `.ctxgraph/skills/`. Activate with `ctx ask --skill <name>` or `ctx capsule --skill <name>`.
281
+
220
282
  ### `ctx info` — Graph statistics
221
283
  ```bash
222
284
  ctx info
@@ -330,9 +392,15 @@ ctx capsule "extract payment processing into separate module" --mode deep
330
392
 
331
393
  ## Framework Integrations
332
394
 
333
- ctxgraph can be used as a Python library — not just a CLI. This makes it easy to plug into LangChain, LangGraph, OpenAI Agents, or any custom AI pipeline.
395
+ ctxgraph is a Python library first the CLI is just a wrapper. This makes it easy to feed code context into LangChain, LangGraph, OpenAI Agents, or any LLM pipeline.
396
+
397
+ ### How the Python API works
398
+
399
+ The flow is always the same:
334
400
 
335
- ### Python API
401
+ 1. **`build_graph(path)`** → scans your code, stores a knowledge graph in `path/.ctxgraph/graph.db`
402
+ 2. **`get_storage(path)`** → opens that SQLite database for queries (fast, no re-scanning)
403
+ 3. **`render_capsule(storage, query)`** → searches the graph, returns a compact text capsule
336
404
 
337
405
  ```python
338
406
  from pathlib import Path
@@ -340,154 +408,158 @@ from ctxgraph.graph.builder import build_graph, get_storage
340
408
  from ctxgraph.capsule.renderer import render_capsule
341
409
  from ctxgraph.graph.query import search_relevant_nodes
342
410
 
343
- # 1. Build the graph (one-time setup)
344
- stats = build_graph(Path("/path/to/project"))
411
+ # --- Step 1: Build (one-time, ~0.1-1s per project) ---
412
+ stats = build_graph(Path("/path/to/my_project"))
345
413
  print(f"Built: {stats['total_nodes']} nodes, {stats['total_edges']} edges")
346
414
 
347
- # 2. Get storage for an existing graph
348
- storage = get_storage(Path("/path/to/project"))
415
+ # --- Step 2: Use (instant reads the .db file) ---
416
+ storage = get_storage(Path("/path/to/my_project"))
349
417
 
350
- # 3. Generate a context capsule (token-efficient text)
418
+ # Generate a capsule — a token-efficient DSL string
351
419
  capsule = render_capsule(storage, "fix JWT token validation", max_nodes=20)
352
420
  print(capsule)
353
-
354
- # 4. Search for relevant nodes programmatically
421
+ # → [CTX]fix JWT token validation
422
+ # [F]src/auth/jwt.py
423
+ # D:JWT token creation and validation
424
+ # [F]src/auth/middleware.py
425
+ # D:Auth middleware for request validation
426
+ # ...
427
+
428
+ # Or search for nodes programmatically
355
429
  results = search_relevant_nodes(storage, "auth login", max_nodes=10, max_depth=2)
356
430
  for node, score in results:
357
431
  print(f" {node.type}:{node.name} (score={score})")
358
432
  ```
359
433
 
434
+ > **Tip:** `build_graph` is a one-time setup. In production, run `ctx build` during CI/deployment and let your app code only call `get_storage` + `render_capsule`.
435
+
360
436
  ### LangChain
361
437
 
362
- Inject ctxgraph capsules directly into your LangChain prompts dramatically reducing token usage while providing precise code context.
438
+ Pass the capsule as context in your prompt template. The LLM gets exactly the files, classes, and dependencies it needs — no token waste.
363
439
 
364
440
  ```python
365
441
  from pathlib import Path
366
442
  from langchain_openai import ChatOpenAI
367
443
  from langchain_core.prompts import ChatPromptTemplate
368
- from ctxgraph.graph.builder import build_graph, get_storage
444
+ from ctxgraph.graph.builder import get_storage # graph already built
369
445
  from ctxgraph.capsule.renderer import render_capsule
370
446
 
371
- # Build graph once
372
- build_graph(Path("./my_project"))
447
+ # Load existing graph (zero build time)
373
448
  storage = get_storage(Path("./my_project"))
374
449
 
375
- # Generate context for a specific task
376
- context = render_capsule(storage, "user authentication flow", max_nodes=20)
450
+ # Generate capsule for the question
451
+ context = render_capsule(storage, "login rate limiter", max_nodes=15)
377
452
 
378
453
  prompt = ChatPromptTemplate.from_messages([
379
- ("system", "You are a senior Python developer. Use the code context below to answer the question.\n\n{context}"),
454
+ ("system", "You are a senior Python dev. Answer using the code context below.\n\n{context}"),
380
455
  ("user", "{question}"),
381
456
  ])
382
457
 
383
458
  llm = ChatOpenAI(model="gpt-4o")
384
- chain = prompt | llm
459
+ response = prompt | llm | (lambda msg: msg.content)
385
460
 
386
- response = chain.invoke({
461
+ print(response.invoke({
387
462
  "context": context,
388
- "question": "Where is the login rate limiter implemented?",
389
- })
463
+ "question": "Where is the rate limiter applied in the login flow?",
464
+ }))
465
+ # → "The rate limiter is in src/auth/middleware.py at line 42.
466
+ # It wraps the login endpoint with a 5req/min limit per IP."
390
467
  ```
391
468
 
392
469
  ### LangGraph
393
470
 
394
- Use ctxgraph as a tool within a LangGraph agent the agent requests context capsules when it needs to understand the codebase.
471
+ Expose ctxgraph as a tool the agent calls on-demand. The agent fetches context only when it hits a code-related question.
395
472
 
396
473
  ```python
397
474
  from pathlib import Path
398
- from typing import Literal
399
475
  from langgraph.graph import StateGraph, MessagesState
400
476
  from langgraph.prebuilt import ToolNode
401
477
  from langchain_openai import ChatOpenAI
402
478
  from langchain_core.tools import tool
403
- from ctxgraph.graph.builder import build_graph, get_storage
479
+ from ctxgraph.graph.builder import get_storage
404
480
  from ctxgraph.capsule.renderer import render_capsule
405
481
 
406
- # Pre-build graph
407
- build_graph(Path("./my_project"))
408
- storage = get_storage(Path("./my_project"))
482
+ # Pre-built graph — loaded instantly
483
+ _storage = get_storage(Path("./my_project"))
409
484
 
410
485
  @tool
411
486
  def code_context(task: str) -> str:
412
- """Get code context relevant to a task. Use this before answering code questions."""
413
- return render_capsule(storage, task, max_nodes=20)
487
+ """Fetch relevant source code for a development task.
488
+ Use this whenever the user asks about implementation details,
489
+ bug fixes, or architecture in the codebase."""
490
+ return render_capsule(_storage, task, max_nodes=20)
414
491
 
492
+ # --- Build LangGraph ---
415
493
  tools = [code_context]
416
- tool_node = ToolNode(tools)
417
-
418
494
  model = ChatOpenAI(model="gpt-4o", temperature=0).bind_tools(tools)
419
495
 
420
- def should_continue(state: MessagesState) -> Literal["tools", "__end__"]:
421
- return "tools" if state["messages"][-1].tool_calls else "__end__"
422
-
423
- def call_model(state: MessagesState):
496
+ def agent_node(state: MessagesState):
424
497
  return {"messages": [model.invoke(state["messages"])]}
425
498
 
426
499
  graph = StateGraph(MessagesState)
427
- graph.add_node("agent", call_model)
428
- graph.add_node("tools", tool_node)
500
+ graph.add_node("agent", agent_node)
501
+ graph.add_node("tools", ToolNode(tools))
429
502
  graph.set_entry_point("agent")
430
- graph.add_conditional_edges("agent", should_continue)
503
+ graph.add_conditional_edges(
504
+ "agent",
505
+ lambda s: "tools" if s["messages"][-1].tool_calls else "__end__",
506
+ )
431
507
  graph.add_edge("tools", "agent")
432
508
 
433
509
  app = graph.compile()
434
510
 
435
- for chunk in app.stream({"messages": [("user", "Find the bug in the payment processor")]}):
436
- for node, msg in chunk.items():
437
- print(f"[{node}]: {msg['messages'][0].content[:200] if msg.get('messages') else ''}")
511
+ # --- Run ---
512
+ for chunk in app.stream({"messages": [("user", "How does payment retry work?")]}):
513
+ for node, vals in chunk.items():
514
+ msg = vals["messages"][0]
515
+ if hasattr(msg, "content") and msg.content:
516
+ print(f"[{node}]: {msg.content[:300]}")
438
517
  ```
439
518
 
519
+ When the user asks about code, the agent calls `code_context("payment retry")`, gets back a capsule with `[F]src/payment/retry.py`, `[F]src/payment/processor.py`, and their dependency edges, then answers with those files in context.
520
+
440
521
  ### OpenAI Agents SDK
441
522
 
442
- Use ctxgraph with the official OpenAI Agents SDK (also works with Azure OpenAI via `AzureOpenAIChatCompletionAgent`).
523
+ Same pattern ctxgraph is a function tool the agent invokes.
443
524
 
444
525
  ```python
445
526
  from pathlib import Path
446
- from openai import AzureOpenAI # or OpenAI for standard API
447
527
  from agents import Agent, Runner, function_tool
448
- from ctxgraph.graph.builder import build_graph, get_storage
528
+ from ctxgraph.graph.builder import get_storage
449
529
  from ctxgraph.capsule.renderer import render_capsule
450
530
 
451
- # Pre-build the graph
452
- build_graph(Path("./my_project"))
453
- storage = get_storage(Path("./my_project"))
531
+ _storage = get_storage(Path("./my_project"))
454
532
 
455
533
  @function_tool
456
534
  def fetch_code_context(task_description: str) -> str:
457
- """Retrieve relevant code context for a development task."""
458
- return render_capsule(storage, task_description, max_nodes=20)
535
+ """Retrieve code context from the project's knowledge graph.
536
+ Provide a task description like 'JWT auth middleware' or 'payment processor'."""
537
+ return render_capsule(_storage, task_description, max_nodes=20)
459
538
 
460
539
  agent = Agent(
461
540
  name="Code Assistant",
462
- instructions="You are a helpful coding assistant. Use the code context tool to understand the codebase before answering.",
463
- model="gpt-4o", # or AzureOpenAIChatCompletionAgent(deployment="gpt-4o", ...)
541
+ instructions="You help developers understand their codebase. Use fetch_code_context to get relevant files before answering.",
542
+ model="gpt-4o",
464
543
  tools=[fetch_code_context],
465
544
  )
466
545
 
467
- result = Runner.run_sync(
468
- agent,
469
- "How does the JWT authentication middleware work?",
470
- )
546
+ result = Runner.run_sync(agent, "How does the notification system handle email vs SMS?")
471
547
  print(result.final_output)
472
548
  ```
473
549
 
474
- ### Azure OpenAI with Custom Agent
550
+ ### Azure OpenAI (direct client)
475
551
 
476
- For Azure OpenAI, configure the client directly and inject ctxgraph context:
552
+ For Azure OpenAI or any OpenAI-compatible endpoint, inject the capsule directly into the system message.
477
553
 
478
554
  ```python
479
555
  import os
480
556
  from openai import AzureOpenAI
481
557
  from pathlib import Path
482
- from ctxgraph.graph.builder import build_graph, get_storage
558
+ from ctxgraph.graph.builder import get_storage
483
559
  from ctxgraph.capsule.renderer import render_capsule
484
560
 
485
- # Build graph
486
- build_graph(Path("./my_project"))
487
561
  storage = get_storage(Path("./my_project"))
488
-
489
- # Generate context capsule
490
- context = render_capsule(storage, "authentication and authorization", max_nodes=25)
562
+ context = render_capsule(storage, "event bus architecture", max_nodes=25)
491
563
 
492
564
  client = AzureOpenAI(
493
565
  api_version="2024-08-01-preview",
@@ -498,8 +570,8 @@ client = AzureOpenAI(
498
570
  response = client.chat.completions.create(
499
571
  model="gpt-4o", # deployment name
500
572
  messages=[
501
- {"role": "system", "content": f"You are a senior Python developer. Use the code context below.\n\n{context}"},
502
- {"role": "user", "content": "Explain the role-based access control (RBAC) implementation."},
573
+ {"role": "system", "content": f"You are a senior developer. Code context:\n\n{context}"},
574
+ {"role": "user", "content": "How do I add a new event handler?"},
503
575
  ],
504
576
  )
505
577
  print(response.choices[0].message.content)
@@ -521,25 +593,34 @@ python benchmarks/run_ollama_comparison.py # Requires local Ollama
521
593
  ### Project Structure
522
594
  ```
523
595
  src/ctxgraph/
524
- ├── cli/main.py — Typer CLI (6 commands)
596
+ ├── cli/main.py — Typer CLI (9 commands)
525
597
  ├── graph/
526
598
  │ ├── models.py — Node, Edge, Graph dataclasses
527
599
  │ ├── storage.py — SQLite persistence
528
600
  │ ├── builder.py — Graph build orchestrator
529
601
  │ └── query.py — Tokenizer + BFS + relevance scoring
530
- ├── capsule/renderer.py — DSL context generation
602
+ ├── capsule/
603
+ │ ├── renderer.py — DSL context generation
604
+ │ └── savings.py — Token savings computation
531
605
  ├── analyzers/python/
532
606
  │ ├── importer.py — AST import extraction
533
607
  │ ├── symbols.py — AST class/function/method analysis
534
608
  │ └── semantic.py — Docstring summarization
535
609
  ├── config/
610
+ │ ├── __init__.py
611
+ │ ├── init.py — Project scaffold (.ctxgraph dir)
536
612
  │ ├── settings.py — TOML/JSON/env config loading
537
613
  │ └── providers.py — Ollama, Claude, OpenAI clients
538
614
  ├── clients/models.py — Mode enum (fast/balanced/deep)
539
615
  ├── exclude/patterns.py — Exclusion pattern matching
540
616
  ├── view/visualizer.py — D3.js HTML graph generator
541
617
  ├── wrapper/claude.py — ccg Claude wrapper
542
- └── mcp/server.py — MCP protocol server
618
+ ├── mcp/server.py — MCP protocol server
619
+ ├── skills/
620
+ │ ├── __init__.py — Skill discovery + loading
621
+ │ ├── project-style.toml — Default skill: project conventions
622
+ │ └── field-guide.toml — Default skill: field guide
623
+ └── history.py — JSONL history append/query/stats
543
624
  ```
544
625
 
545
626
  ---