Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
105 commits
Select commit Hold shift + click to select a range
bec0a45
Chat interface for AI assistant
lshala Sep 11, 2025
2d42e1b
Format
lshala Sep 11, 2025
715219c
Update dependency io.ktor.plugin to v3.3.0 (#2412)
renovate[bot] Sep 12, 2025
7722707
Update dependency com.charleskorn.kaml:kaml to v0.96.0 (#2430)
renovate[bot] Sep 15, 2025
308495f
Update dependency @sveltejs/kit to v2.39.1 (#2429)
renovate[bot] Sep 15, 2025
b8c202c
Update CODEOWNERS (#2425)
konradweiss Sep 17, 2025
d9ac76f
Dokka v2 (#2413)
lshala Sep 17, 2025
a5a1799
Update dependency eslint-plugin-svelte to v3.12.4 (#2433)
renovate[bot] Sep 22, 2025
c779ba6
Update dependency @sveltejs/kit to v2.42.2 (#2432)
renovate[bot] Sep 22, 2025
69092df
Update dependency com.vanniktech:gradle-maven-publish-plugin to v0.34…
renovate[bot] Sep 24, 2025
13b4c00
Moving `group` back to `allprojects` (#2435)
oxisto Sep 25, 2025
bfd3eb3
Update dependency @eslint/compat to v1.4.0 (#2436)
renovate[bot] Sep 29, 2025
90a329b
Update dependency @sveltejs/kit to v2.43.5 (#2437)
renovate[bot] Sep 29, 2025
090520d
Abstract Value Analysis (#1759)
co-de-pot Oct 2, 2025
b8ff709
Update dependency @sveltejs/kit to v2.44.0 (#2441)
renovate[bot] Oct 6, 2025
0d39b4c
Update dependency com.charleskorn.kaml:kaml to v0.97.0 (#2442)
renovate[bot] Oct 6, 2025
2417769
Timeout for CDG and CFSensitiveDFG pass (#2417)
KuechA Oct 6, 2025
ddda6cd
Only put AST nodes in `fields`/`methods`/... in `RecordDeclaration` t…
oxisto Oct 8, 2025
1630de2
Make MCP configurable (#2444)
maximiliankaul Oct 9, 2025
3e4d65c
Changing function signature of `parse` to accept the file content ins…
oxisto Oct 9, 2025
e5fb3ee
Transition to integration tests for MCP (#2445)
fwendland Oct 9, 2025
dd0c077
Update dependency com.charleskorn.kaml:kaml to v0.98.0 (#2448)
renovate[bot] Oct 13, 2025
00a0f18
Update dependency @sveltejs/kit to v2.46.4 (#2447)
renovate[bot] Oct 13, 2025
71b7dd6
Fixing comment matching on multiple nested namespaces with no locatio…
konradweiss Oct 14, 2025
4fcf453
Update dependency globals to v16.4.0 (#2451)
renovate[bot] Oct 20, 2025
e798ad5
Update dependency @sveltejs/kit to v2.47.2 (#2450)
renovate[bot] Oct 21, 2025
bdef6b6
Update dependency vite to v6.4.1 [SECURITY] (#2453)
renovate[bot] Oct 24, 2025
2ba6f1c
Update dependency com.charleskorn.kaml:kaml to v0.102.0 (#2457)
renovate[bot] Oct 27, 2025
bee0018
Update dependency @sveltejs/kit to v2.48.1 (#2456)
renovate[bot] Oct 28, 2025
a4b6bd9
Expose host and port config in codyze API (#2458)
KuechA Oct 30, 2025
3e0601e
Metric and evidence ids in query trees (#2440)
KuechA Oct 30, 2025
cf68b6c
Remove `evidenceID` from `QueryTree` (#2460)
KuechA Oct 30, 2025
c2d3bb1
Update dependency eslint-plugin-svelte to v3.13.0 (#2462)
renovate[bot] Nov 3, 2025
8f07ef8
Update dependency black.ninia:jep to v4.3.1 (#2461)
renovate[bot] Nov 5, 2025
f2b79a2
Propagate information from underlying node to overlay node (#2466)
fwendland Nov 12, 2025
cf5fc5d
Python: remove unsupported versions/add current & future versions (#2…
maximiliankaul Nov 13, 2025
c2abe82
Update dependency com.vanniktech:gradle-maven-publish-plugin to v0.35…
renovate[bot] Nov 17, 2025
cc84696
Update dependency globals to v16.5.0 (#2464)
renovate[bot] Nov 17, 2025
3532d49
Merge branch 'main' into codyze-goes-ai
lshala Nov 17, 2025
4b608b9
Use koog for LLM stuff; Fix Chat UI bugs
lshala Nov 20, 2025
2653e86
feat(console): rename to CodAIze Agent, update sidebar icon to robot,…
lshala Dec 16, 2025
50b9744
Add a custom MCP Client + UI Widgets
lshala Dec 19, 2025
488536e
Update mcp sdk types in cpg-mcp modules due to version upgrade
lshala Dec 19, 2025
3dde569
Update imports of kotlin mcp sdk in cpg-mcp module
lshala Jan 5, 2026
947863f
Update imports of kotlin mcp sdk in cpg-mcp module
lshala Jan 5, 2026
36fbbaf
Merge branch 'main' into codyze-goes-ai
lshala Jan 5, 2026
0a09fdb
Replace netty with cio
lshala Jan 15, 2026
ccca4a3
Add thinking
lshala Jan 21, 2026
0323c7c
spotless
lshala Jan 21, 2026
27748f0
Fix mcp util
lshala Jan 21, 2026
cee0280
Add gemini client; Update widgets
lshala Jan 25, 2026
5522b1b
Refactor clients
lshala Jan 26, 2026
741b34b
Add logs for token usage
lshala Jan 27, 2026
9de424b
Merge branch 'main' into codyze-goes-ai
lshala Jan 28, 2026
5302526
Change model
lshala Jan 29, 2026
664ad42
Refactor sending conversation history to LLM; Extend config for mlx llms
lshala Jan 29, 2026
1d58ede
Some refactoring
lshala Feb 11, 2026
a43cd1a
Change listing tools returning summaries instead of complete node
lshala Feb 17, 2026
7e0db4a
New tool and refactoring
lshala Feb 18, 2026
46e0259
Minor changes
lshala Feb 18, 2026
e6191ce
Minor changes 2
lshala Feb 18, 2026
f8221bb
Set noEdges to false for query tree
lshala Feb 18, 2026
302b01f
Remove debug logs
lshala Feb 18, 2026
76c626d
Merge main
lshala Feb 19, 2026
1f05e0f
Add missing payload for concept assignment
lshala Feb 19, 2026
912e91e
Revert CLI run command back to clikt and add wait parameter
lshala Feb 20, 2026
d353e6a
Downgrade ktor to 3.2.3 due to kotlin-mcp-sdk
lshala Feb 20, 2026
914e2f8
Merge branch 'main' into codyze-goes-ai
lshala Feb 20, 2026
858eb9a
Merge branch 'main' into codyze-goes-ai
lshala Feb 20, 2026
b132d81
Change cpg_llm_analyze tool in prompt
lshala Feb 20, 2026
b6d41c1
Merge branch 'main' into codyze-goes-ai
lshala Feb 20, 2026
9f46b96
Remove sampling since llm_analyze mcp tool is changed to a prompt
lshala Feb 23, 2026
cd036a0
Merge branch 'main' into codyze-goes-ai
lshala Feb 24, 2026
ced2e96
Merge branch 'main' into codyze-goes-ai
lshala Feb 24, 2026
352d98a
Minor changes
lshala Feb 25, 2026
a20f9fe
Change urls of mlx and vllm models
lshala Feb 26, 2026
5b5f194
Add prompts and resources support
lshala Mar 3, 2026
ef9e9d4
Merge branch 'main' into codyze-goes-ai
lshala Mar 3, 2026
4f35742
Revert ChatClient back using SSE instead of http
lshala Mar 4, 2026
0d8107e
Configure streamable http connection in MCP client
lshala Mar 5, 2026
78d10db
Add application.conf.example file
lshala Mar 5, 2026
625b766
Remove application.conf from tracking
lshala Mar 5, 2026
319949d
Fix svelte deprececated issues in layout.svelte
lshala Mar 5, 2026
6add33f
Fix svelte modal issue
lshala Mar 5, 2026
79faaa1
Fix svelte warnings: add $derived
lshala Mar 5, 2026
0b0b64e
Change integration tests due to latest kotlin-sdk release of 0.9.0
lshala Mar 6, 2026
80fbad8
Fix warnings in ListCommandsTest
lshala Mar 6, 2026
86b5803
Fix failing tests
lshala Mar 6, 2026
5cb28c9
Change JsonSChemaGeneratorTest using a test payload making the test m…
lshala Mar 6, 2026
bb25b7b
Fix mcp integration tests
lshala Mar 6, 2026
5342eb2
Uncomment prompt test
lshala Mar 6, 2026
e629ecd
Uncomment suggestConceptsPromptTest
lshala Mar 6, 2026
eea2a4e
Refactor mcp integration tests
lshala Mar 6, 2026
a89702e
Add mcp sdk ServerSession in tests
lshala Mar 6, 2026
9a9a614
Try fixing tests again
lshala Mar 6, 2026
ebe0e52
Add example python files back in codyze-core
lshala Mar 6, 2026
ad9d43b
Fix svelte issues
lshala Mar 6, 2026
7808409
Cleanup
lshala Mar 6, 2026
aa196a4
Revert mcp sdk version back to 0.8.4 due to flaky tests
lshala Mar 6, 2026
bc50f93
Make ChatService creation dependend on config file
lshala Mar 6, 2026
2ad3b03
Minor fixes
lshala Mar 9, 2026
3ed268a
Make CodeViewer, Filetree and NodeTable components reusable for ChatI…
lshala Mar 9, 2026
7b9c256
Fix issues when starting codyze webconsole and mcp module disabled
lshala Mar 9, 2026
66bc92e
Merge branch 'main' into codyze-goes-ai
lshala Mar 9, 2026
dc7b343
Basic koog client with agent loop
lshala Mar 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# AGENTS.md

This file provides guidance to AI coding agents (Claude Code, Cursor, Codex, Gemini CLI, etc.) working in this repository.

## Project Overview

**CPG** is a code property graph library and analysis platform.

Key modules:
- **cpg-core** – Core library: AST nodes, graph structures, passes, type system
- **cpg-analysis** – Higher-level analyses built on top of cpg-core (dataflow, control flow, call graphs, etc.)
- **cpg-language-\*** – Language frontends, one module per language (e.g., `cpg-language-go`, `cpg-language-python`)
- **cpg-concepts** – Concept and operation definitions
- **cpg-mcp** – MCP (Model Context Protocol) server exposing CPG analysis tools to LLMs
- **codyze-console** – Web-based analysis UI: Kotlin backend + Svelte 5 frontend

## Technology Stack

### Backend
- **Language**: Kotlin (requires Java 21)
- **Build**: Gradle with Kotlin DSL
- **Testing**: JUnit 5, kotlin.test
- **Formatting**: Google Java Style via spotless plugin

### Frontend (`codyze-console/src/main/webapp`)
- **Framework**: Svelte 5 with SvelteKit
- **Styling**: Tailwind CSS
- **Package Manager**: pnpm (not npm)

## Development Commands

### Build

```bash
# Full build (format + test + publish locally)
./gradlew clean spotlessApply build publishToMavenLocal

# Quick build
./gradlew build

# Build a single module
./gradlew :cpg-core:build
./gradlew :codyze-console:build
```

### Test & Quality

```bash
./gradlew test # Unit tests
./gradlew integrationTest # Integration tests
./gradlew performanceTest # Performance tests
./gradlew spotlessApply # Auto-format
./gradlew spotlessCheck # Check formatting only
```

### Frontend (`codyze-console`)

```bash
cd codyze-console/src/main/webapp
pnpm install # Install dependencies
pnpm run dev # Dev server
pnpm run build # Production build
pnpm run check # Type check
pnpm run lint # Lint
pnpm run format # Format
```

### Backend (`codyze-console`)

```bash
./gradlew :codyze-console:compileKotlin --console=plain
```

### MCP Server (`cpg-mcp`)

```bash
./gradlew :cpg-mcp:installDist # Build & install
./gradlew :cpg-mcp:run # Run (stdio)
./gradlew :cpg-mcp:run --args="--http 8080" # Run with streamable HTTP on port 8080
```

## Code Conventions

- Follow **Google Java Style** (enforced by spotless – run `./gradlew spotlessApply` before committing)
- Kotlin idioms preferred over Java-style patterns
- Use `kotlin.test` assertions in tests, not JUnit assertions directly
- Frontend: use **Svelte 5 runes** exclusively (no legacy `$:` reactive syntax)
23 changes: 23 additions & 0 deletions codyze-console/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,36 @@ mavenPublishing {
}
}

val mcpEnabled = findProject(":cpg-mcp") != null

dependencies {
// CPG modules
implementation(projects.cpgConcepts)
implementation(projects.cpgSerialization)

// MCP dependencies
if (mcpEnabled) {
implementation(project(":cpg-mcp"))
// MCP SDK
implementation(libs.mcp)
// MCP Client SDK - for custom MCP client implementation
implementation(libs.mcp.client)
} else {
// MCP SDK only available at compile time so the files in `/ai` compile,
compileOnly(libs.mcp)
compileOnly(libs.mcp.client)
}

// Ktor server dependencies
implementation(libs.bundles.ktor)

// Ktor client dependencies
implementation(libs.bundles.ktor.client)

// Koog AI agent framework
implementation(libs.koog.agents)
implementation(libs.koog.agents.mcp)

// Serialization
implementation(libs.kotlinx.serialization.json)
implementation(libs.jacksonyml)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
import de.fraunhofer.aisec.codyze.AnalysisProject
import de.fraunhofer.aisec.codyze.AnalysisResult
import de.fraunhofer.aisec.codyze.console.ai.McpServerHelper
import de.fraunhofer.aisec.cpg.TranslationConfiguration
import de.fraunhofer.aisec.cpg.graph.concepts.Concept
import de.fraunhofer.aisec.cpg.graph.concepts.conceptBuildHelper
Expand All @@ -40,6 +41,8 @@ import de.fraunhofer.aisec.cpg.passes.concepts.LoadPersistedConcepts.PersistedCo
import de.fraunhofer.aisec.cpg.passes.concepts.LoadPersistedConcepts.PersistedConcepts
import de.fraunhofer.aisec.cpg.passes.concepts.config.python.PythonStdLibConfigurationPass
import de.fraunhofer.aisec.cpg.query.QueryTree
import de.fraunhofer.aisec.cpg.serialization.NodeJSON
import de.fraunhofer.aisec.cpg.serialization.toJSON
import java.io.File
import java.nio.file.Path
import kotlin.uuid.Uuid
Expand Down Expand Up @@ -125,6 +128,9 @@ class ConsoleService {

val result = project.analyze()

// Update the global analysis result in the MCP server
McpServerHelper.setGlobalAnalysisResult(result.translationResult)

// Populate QueryTree cache for lazy loading
populateQueryTreeCache(result.requirementsResults)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,17 @@
*/
package de.fraunhofer.aisec.codyze.console

import io.ktor.http.HttpHeaders
import de.fraunhofer.aisec.codyze.console.ai.ChatService
import de.fraunhofer.aisec.codyze.console.ai.McpServerHelper
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.plugins.cors.routing.*
import io.ktor.server.routing.*
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.json.Json

/**
Expand All @@ -41,18 +44,48 @@ import kotlinx.serialization.json.Json
* the [configureWebconsole] function.
*/
fun ConsoleService.startConsole(host: String = "localhost", port: Int = 8080) {
embeddedServer(Netty, host = host, port = port) { configureWebconsole(this@startConsole) }
val chatService: ChatService? =
if (McpServerHelper.isEnabled) {
runBlocking { initChatService() }
} else {
println("MCP module not enabled, AI chat features will be disabled")
null
}

println("Starting main server on port $port...")
embeddedServer(Netty, host = host, port = port) {
configureWebconsole(this@startConsole, chatService)
}
.start(wait = true)
}

private suspend fun ConsoleService.initChatService(): ChatService? {
val chatService = ChatService.createIfConfigExist() ?: return null

McpServerHelper.startMcpServer(8081)

val translationResult = getTranslationResult()?.analysisResult?.translationResult
if (translationResult != null) {
McpServerHelper.setGlobalAnalysisResult(translationResult)
}

println("Initializing ChatService...")
chatService.connect()
println("MCP client connected!")
return chatService
}

/**
* This function takes care of configuring the web console based on the [service]. It sets up the
* CORS policy, content negotiation, and routing.
*
* Note: Currently, the CORS policy allows any host. This should be restricted to specific hosts in
* a production environment and will be made available as an option later.
*/
fun Application.configureWebconsole(service: ConsoleService = ConsoleService()) {
fun Application.configureWebconsole(
service: ConsoleService = ConsoleService(),
chatService: ChatService? = null,
) {
install(CORS) {
anyHost()
allowHeader(HttpHeaders.ContentType)
Expand All @@ -67,17 +100,21 @@ fun Application.configureWebconsole(service: ConsoleService = ConsoleService())
)
}

configureRouting(service)
configureRouting(service, chatService)
}

/**
* This function sets up the routing for the web console. It defines the API routes and static
* resources (for serving the single-page application frontend).
*/
fun Application.configureRouting(service: ConsoleService) {
fun Application.configureRouting(service: ConsoleService, chatService: ChatService? = null) {
routing {
// We'll add routes here
apiRoutes(service)
// If the cpg-mcp module is enabled, chatService won't be null, so the endpoints will be
// reachable
if (chatService != null) {
chatRoutes(chatService)
}
frontendRoutes()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,18 @@ import de.fraunhofer.aisec.cpg.assumptions.Assumption
import de.fraunhofer.aisec.cpg.graph.*
import de.fraunhofer.aisec.cpg.graph.concepts.Concept
import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnit
import de.fraunhofer.aisec.cpg.graph.edges.Edge
import de.fraunhofer.aisec.cpg.passes.concepts.LoadPersistedConcepts.*
import de.fraunhofer.aisec.cpg.query.*
import de.fraunhofer.aisec.cpg.serialization.*
import io.github.detekt.sarif4k.ArtifactLocation
import io.github.detekt.sarif4k.Result
import java.net.URI
import java.nio.file.Path
import kotlin.io.path.Path
import kotlin.io.path.toPath
import kotlin.uuid.Uuid
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder

/**
* JSON data class for an analysis request. It contains the source directory, an optional include
Expand All @@ -67,14 +61,6 @@ data class AnalyzeRequestJSON(
val conceptsFile: String? = null,
)

/** JSON data class for an [Edge]. */
@Serializable
data class EdgeJSON(
var label: String,
@Serializable(with = UuidSerializer::class) var start: Uuid,
@Serializable(with = UuidSerializer::class) var end: Uuid,
)

/** JSON data class for a SARIF [Result]. */
@Serializable
data class FindingsJSON(
Expand Down Expand Up @@ -142,25 +128,6 @@ data class ConstructorArguments(
val argumentType: String? = null, // currently not used
)

/** JSON data class for a [Node]. */
@Serializable
data class NodeJSON(
@Serializable(with = UuidSerializer::class) val id: Uuid,
val type: String,
val startLine: Int,
val startColumn: Int,
val endLine: Int,
val endColumn: Int,
val code: String,
val name: String,
val astChildren: List<NodeJSON>,
val prevDFG: List<EdgeJSON> = emptyList(),
val nextDFG: List<EdgeJSON> = emptyList(),
@Serializable(with = UuidSerializer::class) val translationUnitId: Uuid? = null,
val componentName: String? = null,
val fileName: String? = null,
)

/** JSON data class for a requirement category. */
@Serializable
data class RequirementsCategoryJSON(
Expand Down Expand Up @@ -309,7 +276,7 @@ fun AnalysisResult.toJSON(): AnalysisResultJSON =
components = components.map { it.toJSON() },
totalNodes = nodes.size,
analysisResult = this@toJSON,
sourceDir = config.sourceLocations.first().absolutePath,
sourceDir = config.sourceLocations.firstOrNull()?.absolutePath ?: "",
findings = sarif.runs.flatMap { it.results?.map { it.toJSON() } ?: emptyList() },
requirementCategories =
project.requirementCategoriesToJSON(this@toJSON.requirementsResults),
Expand Down Expand Up @@ -357,42 +324,6 @@ fun Component.toJSON(): ComponentJSON {
)
}

/** Converts a [Node] into its JSON representation. */
fun Node.toJSON(noEdges: Boolean = false): NodeJSON {
return NodeJSON(
id = this.id,
type = this.javaClass.simpleName,
startLine = location?.region?.startLine ?: -1,
startColumn = location?.region?.startColumn ?: -1,
endLine = location?.region?.endLine ?: -1,
endColumn = location?.region?.endColumn ?: -1,
code = this.code ?: "",
name = this.name.toString(),
fileName =
this.location?.artifactLocation?.uri?.let { uri ->
// Extract filename from URI
val path = uri.toString()
path.substringAfterLast('/').substringAfterLast('\\')
},
astChildren =
if (noEdges) emptyList()
else (this as? AstNode)?.astChildren?.map { it.toJSON() } ?: emptyList(),
prevDFG = if (noEdges) emptyList() else this.prevDFGEdges.map { it.toJSON() },
nextDFG = if (noEdges) emptyList() else this.nextDFGEdges.map { it.toJSON() },
translationUnitId = this.translationUnit?.id,
componentName = this.component?.name?.toString(),
)
}

/** Converts an [Edge] into its JSON representation. */
fun Edge<*>.toJSON(): EdgeJSON {
return EdgeJSON(
label = this.labels.firstOrNull() ?: "",
start = this.start.id,
end = this.end.id,
)
}

/** Converts an [Assumption] into its JSON representation. */
fun Assumption.toJSON(): AssumptionJSON {
return AssumptionJSON(
Expand Down Expand Up @@ -461,23 +392,6 @@ val ArtifactLocation.absolutePath: Path?
}
}

/**
* Custom serializer for [Uuid] to convert it to and from a string representation. This is used for
* serialization and deserialization of [Uuid] in the JSON data classes.
*/
object UuidSerializer : KSerializer<Uuid> {
override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor("Uuid", PrimitiveKind.STRING)

override fun serialize(encoder: Encoder, value: Uuid) {
encoder.encodeString(value.toString())
}

override fun deserialize(decoder: Decoder): Uuid {
return Uuid.parse(decoder.decodeString())
}
}

/** Converts the requirement categories of an [AnalysisProject] into their JSON representation. */
fun AnalysisProject.requirementCategoriesToJSON(
requirementsResults: Map<String, QueryTree<Boolean>>? = null
Expand Down
Loading
Loading