A Spring Boot AI agent that bridges Slack with Java services. When a user @-mentions the bot in Slack, the agent:
- Outer LLM (business analyst) uses
DocumentToolsto read service-map, business-map, and skill docs to locate the relevant repository and entry points. - Outer LLM calls
find_call_graph, which triggers static call graph analysis from the selected entry point. - Inner LLM (code translator, sub-agent) translates the call graph into business-language description, with the ability to expand truncated nodes via
CallGraphExpandTools. - The result flows back to the outer LLM, which streams a consolidated answer to Slack.
cp .env.example .env # Fill in required values
mvn package -DskipTests
mvn spring-boot:rundocker compose up -d --build # First time: build image and start
docker compose restart app # Code changed: restart without rebuild
docker compose logs -f app # View real-time console logs
docker compose exec app bash # Enter running container
docker compose down # Stop and remove containerApplication logs are written to logs/ and automatically mapped to the host via volume mount.
# View current log
tail -f logs/app.log
# Archived logs are in logs/archived/
ls logs/archived/mvn test # Run all tests
mvn test -Dtest=CallGraphTest # Run single test class
mvn test -Dtest=ApplicationModularityTests # Verify module boundaries
mvn spring-boot:run -Dspring-boot.run.profiles=uat # Run with profilePlace these in a .env file at project root (used by docker-compose.yml). See .env.example for a template.
| Variable | Required | Description |
|---|---|---|
SLACK_APP_TOKEN |
Yes | Socket Mode token (xapp-...) |
SLACK_BOT_TOKEN |
Yes | Bot token (xoxb-...) |
SLACK_SIGNING_SECRET |
Yes | Request signing secret |
| Variable | Profile | Required | Description |
|---|---|---|---|
OPENAI_API_KEY |
dev | Yes | OpenAI API key |
OPENAI_BASE_URL |
dev | No | Custom OpenAI-compatible endpoint |
OPENAI_MODEL |
dev | No | Model name (default: gpt-3.5-turbo) |
GOOGLE_GENAI_API_KEY |
uat/pro | Yes | Google Gemini API key |
GOOGLE_GENAI_MODEL |
uat/pro | No | Model name (default: gemini-3.1-flash-lite-preview for uat, gemini-2.5-flash for pro) |
| Variable | Required | Description |
|---|---|---|
GIT_USERNAME |
Yes | Git username (email) |
GIT_TOKEN |
Yes | Personal Access Token (PAT) |
Each managed repo needs three variables. The naming convention is REPO_{REPO_NAME_UPPER}_*:
| Variable Pattern | Required | Description |
|---|---|---|
REPO_{NAME}_URL |
Yes | Git clone URL |
REPO_{NAME}_API_HOST |
No | Service API host (for API lookup) |
REPO_{NAME}_DEFAULT_BRANCH |
No | Default branch (default: main) |
Repos are registered in application.yml under git.repos. Example for test-repo:
REPO_TEST_URL=https://github.com/org/test-repo
REPO_TEST_API_HOST=http://localhost:8080
REPO_TEST_DEFAULT_BRANCH=main| Variable | Required | Description |
|---|---|---|
SPRING_PROFILES_ACTIVE |
No | Active profile: dev (default), uat, pro |
| Profile | AI Model | Key Env Vars |
|---|---|---|
dev (default) |
OpenAI-compatible | OPENAI_API_KEY, OPENAI_BASE_URL, OPENAI_MODEL |
uat |
Google Gemini | GOOGLE_GENAI_API_KEY, GOOGLE_GENAI_MODEL |
pro |
Google Gemini | GOOGLE_GENAI_API_KEY, GOOGLE_GENAI_MODEL |
The agent uses a two-layer LLM architecture (outer + inner) with four tools:
| Tool | Parameters | Reads | Purpose |
|---|---|---|---|
read_service_map |
(none) | repos/service-map.md |
System-wide repo overview, used to identify target repo |
read_business_map |
repoId |
repos/{repoId}/docs/business-map.md |
Business group list for a repo |
read_skill_doc |
repoId, groupName |
repos/{repoId}/docs/skills/{groupName}.md |
Detailed entry points and business logic for a group |
find_call_graph |
repoId, packageName, className, methodSignature |
Java source code in repos/{repoId}/ |
Analyzes method call chain, delegates to inner LLM for translation |
| Tool | Parameters | Purpose |
|---|---|---|
find_call_graph |
repoId, packageName, className, methodSignature |
Expands truncated (TRAVERSAL_CUTOFF) or data access (DATA_ACCESS) nodes for deeper analysis |
User @mention in Slack
-> Outer LLM (business analyst role)
|-- read_service_map -> repos/service-map.md
|-- read_business_map(repo) -> repos/{repo}/docs/business-map.md
|-- read_skill_doc(repo, grp) -> repos/{repo}/docs/skills/{grp}.md
|-- find_call_graph(repo, pkg, cls, method)
| -> AnalysisService builds call graph from source code
| -> Inner LLM (code translator role)
| |-- find_call_graph (expand truncated nodes)
| -> Returns business-language description
-> Streams consolidated answer to Slack thread
Repos are cloned to repos/ at runtime. Each repo must have the following docs/ structure for the tools to work:
repos/
service-map.md # Top-level: all repos overview
{repoId}/
docs/
business-map.md # Business group index
summary.md # Business summary (optional, used by service-map)
skills/
{groupName}.md # One file per business group
These files are generated by the business-scope, business-group, and service-map skills (see CLAUDE.md).
# application.yml
entry-point:
call-graph-depth: 3 # Max traversal depth for call graph analysisWhen depth is exceeded, nodes are marked as TRAVERSAL_CUTOFF and the inner LLM can expand them on demand.
Multi-turn conversation memory is enabled per Slack thread using Spring AI's PromptChatMemoryAdvisor.
- Storage: In-memory (
InMemoryChatMemoryRepository) -- data is lost on application restart - Window size: 20 messages per thread (oldest evicted when exceeded)
- Conversation ID: Slack
threadTs(each thread has isolated memory) - Future consideration: For production workloads, consider switching to
JdbcChatMemoryRepositoryor another persistent implementation to avoid memory pressure with high thread volume - Debug endpoints (dev/uat profile only):
GET /debug/chat-memory-- list all active conversation IDsGET /debug/chat-memory/{threadTs}-- view conversation historyDELETE /debug/chat-memory/{threadTs}-- clear conversation history
Swagger UI: /swagger-ui/index.html
docs/business-map.md-- Business group indexjava-coding-standard.md-- Full coding standard