# Project export: GitScribe

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 12.0
- Tagline: Our Github repo analytics tool provide employers a way to evaluate the quality of a users contributions. Our AI commit CLI tool aims to automates the process of meaningful commits and documentation.
- Devpost: https://devpost.com/software/commital
- GitHub: https://github.com/Aaryan-Bidaye/GitScribe
- Video: https://www.youtube.com/embed/rkDxGoNJrZU?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 3 GitHub contributor(s) — Aaryan-Bidaye (71 commits), tyler-ham05 (22 commits), GitHensi (15 commits)

## Devpost submission (written by the team)

### Inspiration

Maintaining quality documentation is a time consuming and difficult task and is also prone to falsification and misrepresentation which is why we wanted to automate these processes by leveraging Anthropic's AI capabilities.

### What it does

Our tool uses Claude to analyze changes in a repo and create commit messages and documentation that accurately reflect the content and impact of the changes. By inputting the repo url our tool also analyzes all contributions to highlight top contributors based on the number of quality commits.

### How we built it

We used the MERN stack to develop the website, and utilized the Agentic SDK to handle our LLM API calls. The commit cli was developed in Python which also interfaced with MongoDB, our database, and anthropic's claude to produce commit messages, code evals, and comprehensive documentation.

### Challenges we ran into

We were having trouble managing the database in a way that didn't interfere with the various API calls that we had through the project. We also had trouble getting structured output from Claude API calls. However, we were eventually able to achieve consistent structured output through a workaround. Same issues plagued the cli until a way to manage api_keys properly was found.

### Accomplishments we're proud of

This is a tool we would actually use in our workflow and we learned new skills that will contribute to our professional development

### What we learned

For some of our team we learned how to create command line interfaces and manage APIs, for others we learned how to use React and front-end development principles

### What's next

for GitGenie Making it scalable for larger repos, and optimization of our algorithms and data structures . Additionally, it would be cool to create custom classification network or a custom AI agent to better classify commits.

## README (from the GitHub repository)

# GitScribe
testing


## Detected evidence (automated analysis)

Indexed codebase: 20 recognized source files, 52 KB.
- Anthropic (technology) — detected in the code
- CSS (language) — detected in the code
- Express (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- MongoDB (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Node.js (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (25 of 25)

```
.gitignore
backend/index.js
backend/modules/commit.js
backend/package.json
cli/README.md
cli/src/.gitignore
cli/src/CHANGELOG.md
cli/src/docs/changes/2025-10-25_05-45-40.md
cli/src/docs/changes/2025-10-28_18-19-00.md
cli/src/gitgenie.py
README.md
website/.gitignore
website/eslint.config.js
website/index.html
website/package.json
website/README.md
website/src/App.css
website/src/App.jsx
website/src/index.css
website/src/main.jsx
website/src/pages/Dashboard.jsx
website/src/pages/Tween.jsx
website/src/pages/URL.jsx
website/src/pages/User.jsx
website/vite.config.js
```

### Dependencies

- backend/package.json: @anthropic-ai/sdk@^0.67.0, cors@^2.8.5, dotenv@^16.3.1, express@^4.21.2, mongoose@^7.5.0, morgan@^1.10.0, nodemon@^3.0.1
- website/package.json: @anthropic-ai/sdk@^0.67.0, @eslint/js@^9.36.0, @types/react@^19.1.16, @types/react-dom@^19.1.9, @vitejs/plugin-react@^5.0.4, axios@^1.12.2, eslint@^9.36.0, eslint-plugin-react-hooks@^5.2.0, eslint-plugin-react-refresh@^0.4.22, globals@^16.4.0, react@^19.1.1, react-dom@^19.1.1, react-router-dom@^7.9.4, vite@^7.1.12

### Recent commits (newest first)

- Rename project from GitGenie to GitScribe
- Remove empty test.txt file
- Remove MongoDB integration and update default model naming
- Remove database upload feature and fix doc generation
- Remove test file and add git documentation image
- done
- removed node_modules from root dir
- done
- Merge branch 'main' of https://github.com/Aaryan-Bidaye/GitGenie
- Semi-Final User.jsx
- omg
- hanges
- added return routing for all pages
- cleaning up
- Merge branch 'main' of https://github.com/Aaryan-Bidaye/GitGenie into tweener
- Merge branch 'main' of https://github.com/Aaryan-Bidaye/GitGenie into tweener
- finished tweener
- removed double btn class in App.css
- merge again for some reason
- merged main into css_branch and fixed errors caused by merge.

## Key source files (fetched from GitHub, selected and truncated for size)

### cli/src/CHANGELOG.md

```markdown
# Changelog

- 2025-10-28 18:19:00 — Remove MongoDB integration and update default model ([details](docs/changes/2025-10-28_18-19-00.md))


- 2025-10-25 05:45:40 — Release Documentation ([details](docs/changes/2025-10-25_05-45-40.md))
```

### cli/src/docs/changes/2025-10-28_18-19-00.md

```markdown
# Remove MongoDB integration and update default model

Remove MongoDB integration and update default model

cli/src/gitgenie.py:
- Remove MongoDB connection logic (get_mongo, send_commit_to_mongo)
- Remove hardcoded MongoDB URI and OpenRouter constants
- Change default model from 'anthropic/claude-4.5-sonnet' to 'claude-sonnet-4-5'

WHY: Simplify codebase by removing unused database persistence layer and standardize model naming convention.
```

### website/package.json

```
{
  "name": "website",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "lint": "eslint .",
    "preview": "vite preview"
  },
  "dependencies": {
    "@anthropic-ai/sdk": "^0.67.0",
    "axios": "^1.12.2",
    "react": "^19.1.1",
    "react-dom": "^19.1.1",
    "react-router-dom": "^7.9.4"
  },
  "devDependencies": {
    "@eslint/js": "^9.36.0",
    "@types/react": "^19.1.16",
    "@types/react-dom": "^19.1.9",
    "@vitejs/plugin-react": "^5.0.4",
    "eslint": "^9.36.0",
    "eslint-plugin-react-hooks": "^5.2.0",
    "eslint-plugin-react-refresh": "^0.4.22",
    "globals": "^16.4.0",
    "vite": "^7.1.12"
  }
}

```

### backend/package.json

```
{
  "name": "backend",
  "version": "0.0.1",
  "description": "testing",
  "homepage": "https://github.com/Aaryan-Bidaye/GitGenie#readme",
  "bugs": {
    "url": "https://github.com/Aaryan-Bidaye/GitGenie/issues"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/Aaryan-Bidaye/GitGenie.git"
  },
  "license": "MIT",
  "author": "Tyler Ham",
  "type": "commonjs",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node index.js",
    "dev": "nodemon index.js"
  },
  "dependencies": {
    "@anthropic-ai/sdk": "^0.67.0",
    "cors": "^2.8.5",
    "dotenv": "^16.3.1",
    "express": "^4.21.2",
    "mongoose": "^7.5.0",
    "morgan": "^1.10.0"
  },
  "devDependencies": {
    "nodemon": "^3.0.1"
  }
}

```

### backend/index.js

```javascript
const express = require('express')
const axios = require('axios');
const morgan = require('morgan')
const cors = require('cors')
const app = express()
const {getCommitModel } = require('./modules/commit')
const Anthropic = require('@anthropic-ai/sdk')
require('dotenv').config()
console.log('API Key exists:', !!process.env.ANTHROPIC_API_KEY)
console.log('API Key length:', process.env.ANTHROPIC_API_KEY?.length)
console.log('Anthropic SDK:', typeof Anthropic)

const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY })

console.log('Anthropic instance:', anthropic)
console.log('Has messages:', !!anthropic.messages)
console.log('Type of anthropic:', typeof anthropic)



morgan.token('body', (req) => {
  return req.method === 'POST' ? JSON.stringify(req.body) : ''
})

const errorHandler = (error, request, response, next) => {
  console.error(error.message)

  if (error.name === 'CastError') {
    return response.status(400).send({ error: 'malformatted id' })
  }
  else if (error.name == 'ValidationError'){
    return response.status(400).json({error: error.message})
  } 

  next(error)
}

app.use(express.json())
app.use(morgan(':method :url :status :res[content-length] :response-time ms :body'))
app.use(express.static('dist'))
app.use(cors())

console.log('hello world')
//api calls go here
app.post('/api/github', async (req, res) => {
  const repo = req.body.repo
  const sha = req.body.sha
  try {
    const response = await axios.get(`https://api.github.com/repos/${repo}/commits/${sha}`, {
       headers: { Authorization: `token ${process.env.GITHUB_TOKEN}` } 
    });
    res.json(response.data);
  }
  catch(error){
    console.log("couldn't fetch github")
    console.log(error)
  }
})
app.post('/api/analyzeCommit', async (req, res) => {
  const { commitChanges } = req.body
  
  try {
    const response = await anthropic.messages.create({
      model: "claude-sonnet-4-5-20250929",
      max_tokens: 1024,
      messages: [{
        role: "user",
        content: `You are a commit ranking assistant. 
        Read the following commit message and rank its impact from 1–10. 
        return your answer in json format with fields for impact, and integer
        summary, a shorter string summary of the commits, and body, a more comprehensive 
        descriptions of changes under 45 words. Do not include any extraneous 
        characters that are NOT json
        commit changes here: ${JSON.stringify(commitChanges)}`
      }],
      tools: [{
        name: "CommitAnalysis",
        description: "Analyze a commit's impact",
        input_schema: {
          type: "object",
          properties: {
            impact: { 
              type: "integer",
              description: "Impact rating from 1-10"
            },
            summary: { 
              type: "string",
              description: "Brief summary of the commit"
            },
            body: { 
              type: "string",
              description: "Detailed analysis of the commit"
            },
          },
          required: ["impact", "summary", "body"]
        }
      }],
      tool_choice: { type: "tool", name: "CommitAnalysis" }
    })

    const toolUse = response.content.find(block => block.type === 'tool_use')
    res.json(toolUse.input)
  } catch (err) {
    console.error('Full error:', err)
    res.status(500).json({ error: "Anthropic API call failed", details: err.message })
  }
})

app.get('/api/:repository', async (req, res) => {
  try{
    const { repository } = req.params
    const CommitModel = getCommitModel(repository)
    const commits = await CommitModel.find({});
    res.json(commits);
  }
  catch (error) {
    console.error(`Error fetching commits for repo ${repository}`, error)
    res.status(500).json({ error: 'Internal server error', message: error.message })
  }
}) 

// GET endpoint for api/:repository/:sha
app.get('/api/:repository/:sha', async (req, res) => {
  try {
    const { repository, sha } = req.params
    const CommitModel = getCommitModel(repository)
    const commit = await CommitModel.findOne({ sha: sha })
    if (!commit) {
      return res.status(404).json({ error: 'Commit not found', repository, sha })
    }
    res.json({
      repository,
      sha,
      commit: {
        username: commit.username,
        sha: commit.sha,
        pfp: commit.pfp,
        date: commit.date,
        summary: commit.summary,
        body: commit.body,
        impact: commit.impact,
      },
    })
  } catch (error) {
    console.error('Error fetching commit:', error)
    res.status(500).json({ error: 'Internal server error', message: error.message })
  }
})

app.post('/api', async (req, res) => {
  //const { repository } = req.params
  const repository = req.body.repository
  const CommitModel = getCommitModel(repository)
  const body = req.body
  const commit = new CommitModel({
    username: body.username,
    pfp: body.pfp,
    sha: body.sha,
    date: body.date,
    body: body.body,
    summary: body.summary,
    impact: body.impact,
  })
  commit.save() 
  .then(savedCommit => {
    res.status(201).json(savedCommit)
  })
  .catch(error => next(error))
})


//api calls end here
app.use(errorHandler)

const PORT = process.env.PORT
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`)
})

```

### website/src/main.jsx

```javascript
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { BrowserRouter } from 'react-router-dom'

import './index.css'
import App from './App.jsx'

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </StrictMode>,
)

```

### website/src/App.jsx

```javascript
import { Routes, Route, Link } from 'react-router-dom'
import './App.css'
import Search from './pages/URL'
import Dashboard from './pages/Dashboard'
import UserData from './pages/User'
import Tween from './pages/Tween'


function App() {
  return (
    <>
        <Routes>
          <Route path="/" element={<Search />} />
          <Route path="/analyzing" element={<Tween/>} />
          <Route path="/dashboard" element={<Dashboard />} />
          <Route path="/user" element={<UserData />} />
        </Routes>
    </>
  )
}

export default App

```

### website/vite.config.js

```javascript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vite.dev/config/
export default defineConfig({
  plugins: [react()],
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:3001',
        changeOrigin: true,
      },
    }
  },
})

```

### website/index.html

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>website</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.jsx"></script>
  </body>
</html>

```

### website/eslint.config.js

```javascript
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import { defineConfig, globalIgnores } from 'eslint/config'

export default defineConfig([
  globalIgnores(['dist']),
  {
    files: ['**/*.{js,jsx}'],
    extends: [
      js.configs.recommended,
      reactHooks.configs['recommended-latest'],
      reactRefresh.configs.vite,
    ],
    languageOptions: {
      ecmaVersion: 2020,
      globals: globals.browser,
      parserOptions: {
        ecmaVersion: 'latest',
        ecmaFeatures: { jsx: true },
        sourceType: 'module',
      },
    },
    rules: {
      'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
    },
  },
])

```

[9 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]