feat: integrate agent-manager module with KiloCode workflows

- Move agent-manager from packages/opencode/ to src/kilocode/
- Add Gitea client for pipeline logging
- Add pipeline-runner for workflow orchestration
- Create slash commands: /pipeline, /status, /evaluate
- Update AGENTS.md with workflow documentation
- Update README.md with KiloCode integration details
- Add package.json and tsconfig.json for TypeScript compilation
- Remove duplicate files from packages/opencode/

This enables:
- /pipeline <issue> - run full agent pipeline with Gitea logging
- Direct agent invocation via @mention
- Performance tracking and prompt optimization
This commit is contained in:
swp
2026-04-04 01:11:06 +01:00
parent 4a69c5323b
commit 35bbdcb08f
21 changed files with 1478 additions and 74 deletions

View File

@@ -0,0 +1,58 @@
---
description: Evaluate agent performance for completed issue
mode: subagent
model: ollama-cloud/gpt-oss:120b
color: "#F59E0B"
---
# Evaluate Command
Generate performance evaluation report for a completed pipeline run.
## Usage
```
/evaluate <issue-number>
```
## Process
1. Fetch issue comments
2. Parse agent execution logs
3. Calculate scores per agent
4. Generate recommendations
5. Post evaluation to Gitea
## Scoring Criteria
| Criterion | Weight |
|-----------|--------|
| Code Quality | 30% |
| Test Coverage | 20% |
| Review Iterations | 20% |
| Time to Complete | 15% |
| Security Issues | 15% |
## Output Format
```markdown
## 🟢 Pipeline Evaluation Report
**Issue**: #42
**Overall Score**: 8.2/10
**Duration**: 2.5h
**Iterations**: 2
### Agent Scores
| Agent | Score | Notes |
|-------|-------|-------|
| 🟢 requirement-refiner | 9/10 | Clear acceptance criteria |
| 🟢 lead-developer | 8/10 | Clean implementation |
| 🟡 code-skeptic | 7/10 | Found 2 minor issues |
| 🟢 the-fixer | 9/10 | Fixed issues quickly |
### Recommendations
- Consider optimizing code-skeptic prompt (score < 8)
```

139
.kilo/commands/pipeline.md Normal file
View File

@@ -0,0 +1,139 @@
---
description: Run full agent pipeline for an issue with Gitea logging
---
# Pipeline Workflow
You are orchestrating the full agent pipeline for issue #{issue_number}. Execute each step sequentially, logging progress to Gitea.
## Parameters
- `issue_number`: The issue number to process (ask if not provided)
## Step 1: Fetch Issue Context
1. Read the issue from Gitea using `bash`:
```bash
gh issue view {issue_number} --json title,body,labels,assignees
```
2. Parse the issue body for:
- Acceptance criteria (checkboxes)
- Referenced files
- Current status label
## Step 2: Check for Duplicates
1. Use `grep` to search git history for similar issues:
```bash
git log --all --oneline --grep="{keywords from title}"
```
2. Report findings as Gitea comment
## Step 3: Route Based on Status
Based on the issue status label, invoke the appropriate agent using Task tool:
| Status Label | Agent to Invoke |
|-------------|-----------------|
| `status: new` | `@requirement-refiner` |
| `status: planned` | `@history-miner` |
| `status: researching` | `@system-analyst` |
| `status: designed` | `@sdet-engineer` |
| `status: testing` | `@lead-developer` |
| `status: implementing` | `@code-skeptic` |
| `status: reviewing` | `@performance-engineer` |
| `status: fixing` | `@the-fixer` |
| `status: releasing` | `@release-manager` |
| `status: evaluated` | `@prompt-optimizer` |
## Step 4: Execute Agent Task
1. Use Task tool to invoke the agent:
```
Use the Task tool with subagent_type: "lead-developer" (or appropriate agent)
prompt: "Implement the following: {issue details}"
```
2. After agent completes, check result:
- If successful → proceed to next agent in workflow
- If issues found → route to `@the-fixer`
## Step 5: Log Progress to Gitea
After each agent completes, post comment:
```bash
gh issue comment {issue_number} --body "## ✅ {agent_name} completed
**Score**: {score}/10
**Duration**: {duration}
**Next**: {next_agent}
{agent_notes}"
```
## Step 6: Update Status Label
```bash
gh issue edit {issue_number} --remove-label "status: {old_status}" --add-label "status: {new_status}"
```
## Step 7: Continue Pipeline
Loop through steps 3-6 until status is `status: completed`.
## Step 8: Generate Evaluation Report
When pipeline completes, generate final report:
```markdown
## 🟢 Pipeline Evaluation Report
**Issue**: #{issue_number}
**Overall Score**: {average}/10
**Duration**: {total_hours}h
**Iterations**: {count}
### Agent Scores
| Agent | Score | Notes |
|-------|-------|-------|
| {agent} | {score}/10 | {notes} |
### Recommendations
- {recommendation_1}
- {recommendation_2}
```
## Workflow Graph
```
new → requirement-refiner → planned
planned → history-miner → researching
researching → system-analyst → designed
designed → sdet-engineer → testing
testing → lead-developer → implementing
implementing → code-skeptic → reviewing
reviewing → performance-engineer → perf-check
perf-check → security-auditor → security-check
security-check → release-manager → releasing
releasing → evaluator → evaluated
evaluated → prompt-optimizer → completed
```
On failure at any step → route to `the-fixer` → back to `code-skeptic`
## Environment
Required environment variables:
```
GITEA_API_URL=https://git.softuniq.eu/api/v1
GITEA_TOKEN=your-token
```
## Error Handling
If any step fails:
1. Post error comment to issue
2. Add label `status: blocked`
3. Ask user for guidance using `question` tool

41
.kilo/commands/status.md Normal file
View File

@@ -0,0 +1,41 @@
---
description: Check pipeline status for an issue
mode: subagent
model: qwen/qwen3.6-plus:free
color: "#3B82F6"
---
# Status Command
Check current pipeline status for an issue.
## Usage
```
/status <issue-number>
```
## Output
```
📊 Issue #42 Pipeline Status
Current State: implementing
Current Agent: @LeadDeveloper
Progress:
✅ requirement-refiner (completed, score: 9)
✅ history-miner (completed, score: 8)
✅ system-analyst (completed, score: 8)
✅ sdet-engineer (completed, tests written)
🔄 lead-developer (in progress)
Next Steps:
1. Complete implementation
2. Code review by @CodeSkeptic
3. Performance check
4. Security audit
5. Release
Estimated Completion: 1.5h
```

View File

@@ -3,5 +3,17 @@
"instructions": [".kilo/rules/*.md"],
"skills": {
"paths": [".kilo/skills"]
},
"agent": {
"pipeline-runner": {
"description": "Runs agent pipeline with Gitea logging",
"mode": "subagent",
"permission": {
"read": "allow",
"write": "allow",
"bash": "allow",
"task": "allow"
}
}
}
}

206
AGENTS.md
View File

@@ -1,46 +1,186 @@
# Kilo Code Agents Reference
## Commands (Quick Actions)
This file configures AI agent behavior for the APAW project - a self-improving code pipeline with Gitea logging.
| Command | Description | Model |
## Pipeline Workflow
The main workflow is `/pipeline` - use it to process issues through all agents automatically.
```
User: /pipeline 42
Agent: Runs full pipeline for issue #42 with Gitea logging
```
## Commands (Slash Commands)
| Command | Description | Usage |
|---------|-------------|-------|
| `/plan` | Creates detailed task plans | ollama-cloud/qwen3-coder:480b |
| `/ask` | Answers codebase questions | openai/qwen3-32b |
| `/debug` | Analyzes and fixes bugs | ollama-cloud/gpt-oss:20b |
| `/code` | Quick code generation | ollama-cloud/qwen3-coder:480b |
| `/pipeline <issue>` | Run full agent pipeline for issue | `/pipeline 42` |
| `/status <issue>` | Check pipeline status for issue | `/status 42` |
| `/evaluate <issue>` | Generate performance report | `/evaluate 42` |
| `/plan` | Creates detailed task plans | `/plan feature X` |
| `/ask` | Answers codebase questions | `/ask how does auth work` |
| `/debug` | Analyzes and fixes bugs | `/debug error in login` |
| `/code` | Quick code generation | `/code add validation` |
## Pipeline Agents
## Pipeline Agents (Subagents)
| Agent | Role | Model |
|-------|------|-------|
| `@RequirementRefiner` | Converts vague ideas to strict User Stories | ollama-cloud/kimi-k2-thinking |
| `@HistoryMiner` | Finds duplicates and past solutions in git | ollama-cloud/gpt-oss:20b |
| `@SystemAnalyst` | Designs technical specifications | qwen/qwen3.6-plus:free |
| `@SDETEngineer` | Writes tests following TDD | ollama-cloud/qwen3-coder:480b |
| `@LeadDeveloper` | Primary code writer | ollama-cloud/qwen3-coder:480b |
| `@FrontendDeveloper` | UI implementation with multimodal | ollama-cloud/kimi-k2.5 |
| `@CodeSkeptic` | Adversarial code reviewer | ollama-cloud/minimax-m2.5 |
| `@TheFixer` | Iteratively fixes bugs | ollama-cloud/minimax-m2.5 |
| `@PerformanceEngineer` | Reviews for performance issues | ollama-cloud/nemotron-3-super |
| `@SecurityAuditor` | Scans for vulnerabilities | ollama-cloud/kimi-k2.5 |
| `@ReleaseManager` | Git operations and deployments | ollama-cloud/qwen3-coder:480b |
| `@Evaluator` | Scores agent effectiveness | ollama-cloud/gpt-oss:120b |
| `@PromptOptimizer` | Improves agent prompts | qwen/qwen3.6-plus:free |
| `@ProductOwner` | Manages issue checklists | qwen/qwen3.6-plus:free |
| `@Orchestrator` | Routes tasks between agents | ollama-cloud/glm-5 |
| `@AgentArchitect` | Manages agent network per Kilo.ai spec | qwen/qwen3.6-plus:free |
These agents are invoked automatically by `/pipeline` or manually via `@mention`:
**Note:** For AgentArchitect, use `subagent_type: "system-analyst"` with prompt "You are Agent Architect..." (workaround for unsupported agent-architect type).
| Agent | Role | When Invoked |
|-------|------|--------------|
| `@requirement-refiner` | Converts ideas to User Stories | Issue status: new |
| `@history-miner` | Finds duplicates in git | Status: planned |
| `@system-analyst` | Designs specifications | Status: researching |
| `@sdet-engineer` | Writes tests (TDD) | Status: designed |
| `@lead-developer` | Implements code | Status: testing (tests fail) |
| `@frontend-developer` | UI implementation | When UI work needed |
| `@code-skeptic` | Adversarial review | Status: implementing |
| `@the-fixer` | Fixes issues | When review fails |
| `@performance-engineer` | Performance review | After code-skeptic |
| `@security-auditor` | Security audit | After performance |
| `@release-manager` | Git operations | Status: releasing |
| `@evaluator` | Scores effectiveness | Status: evaluated |
| `@prompt-optimizer` | Improves prompts | When score < 7 |
## Workflow
## Workflow State Machine
```
[new] → HistoryMiner → [researching] → SystemAnalyst → [designing] → SDET
[testing] → LeadDev → CodeSkeptic → [fail? TheFixer] → [pass] → Performance → Security → Release → Evaluator
[new]
↓ requirement-refiner
[planned]
↓ history-miner
[researching]
↓ system-analyst
[designed]
↓ sdet-engineer (writes failing tests)
[testing]
↓ lead-developer (makes tests pass)
[implementing]
↓ code-skeptic (review)
[reviewing] ──[fail]──→ [fixing] ──→ [reviewing]
↓ [pass]
[perf-check]
↓ performance-engineer
[security-check]
↓ security-auditor
[releasing]
↓ release-manager
[evaluated]
↓ evaluator
├── [score ≥ 7] → [completed]
└── [score < 7] → prompt-optimizer → [completed]
```
## Architecture Documentation
## Gitea Integration
- **KILO_SPEC.md** - Complete Kilo.ai specification for agent architecture
- **agent-architect.md** - Agent management and network configuration
### Status Labels
Pipeline uses Gitea labels to track progress:
- `status: new``status: planned``status: researching` → ...
- Agents add/remove labels automatically
### Performance Logging
Each agent logs to Gitea issue comments:
```markdown
## ✅ lead-developer completed
**Score**: 8/10
**Duration**: 1.2h
**Files**: src/auth.ts, src/user.ts
### Notes
- Clean implementation
- Follows existing patterns
- Tests passing
```
### Efficiency Tracking
Scores saved to `.kilo/logs/efficiency_score.json`:
```json
{
"version": "1.0",
"history": [
{
"issue": 42,
"date": "2024-01-02T10:00:00Z",
"agents": {
"lead-developer": 8,
"code-skeptic": 7,
"the-fixer": 9
},
"iterations": 2,
"duration_hours": 1.5
}
]
}
```
## Manual Agent Invocation
```typescript
// Use Task tool to invoke subagent
Task tool with:
subagent_type: "lead-developer"
prompt: "Implement authentication for issue #42"
```
Or via `@mention`:
```
@lead-developer implement authentication flow
```
## Environment Variables
Required for Gitea integration:
```bash
GITEA_API_URL=https://git.softuniq.eu/api/v1
GITEA_TOKEN=your-token-here
```
## Self-Improvement Cycle
1. **Pipeline runs** for each issue
2. **Evaluator scores** each agent (1-10)
3. **Low scores (<7)** trigger prompt-optimizer
4. **Prompt optimizer** analyzes failures and improves prompts
5. **New prompts** saved to `.kilo/agents/`
6. **Next run** uses improved prompts
## Architecture Files
| File | Purpose |
|------|---------|
| `AGENTS.md` | This file - main config |
| `.kilo/agents/*.md` | Agent definitions with prompts |
| `.kilo/commands/*.md` | Workflow commands |
| `.kilo/rules/*.md` | Custom rules loaded globally |
| `.kilo/skills/` | Skill modules |
| `src/kilocode/` | TypeScript API for programmatic use |
## Using the TypeScript API
```typescript
import {
PipelineRunner,
GiteaClient,
decideRouting
} from './src/kilocode/index.js'
const runner = await createPipelineRunner({
giteaToken: process.env.GITEA_TOKEN
})
await runner.run({ issueNumber: 42 })
```
## Code Style
- Use TypeScript for new files
- Follow existing patterns
- Write tests before code (TDD)
- Keep functions under 50 lines
- Use early returns
- No comments unless explicitly requested

203
README.md
View File

@@ -64,18 +64,19 @@
│ │ └── global.md # Глобальные правила
│ └── logs/
│ └── efficiency_score.json # История оценок
── packages/
└── opencode/
── src/
└── kilocode/
└── agent-manager/ # TypeScript-интеграция с KiloCode
├── index.ts # Загрузчик конфигурации агентов
├── workflow.ts # State Machine пайплайна
├── router.ts # Маршрутизатор между агентами
├── prompt-loader.ts # Динамическая загрузка промптов
├── git-ops.ts # Git-операции (история, коммиты)
├── evaluator.ts # Логика оценки эффективности
── types.ts # TypeScript-типы системы
── src/
└── kilocode/
── index.ts # Точка входа модуля
│ └── agent-manager/ # TypeScript-интеграция с KiloCode
├── index.ts # Загрузчик конфигурации агентов
├── workflow.ts # State Machine пайплайна
├── router.ts # Маршрутизатор между агентами
├── prompt-loader.ts # Динамическая загрузка промптов
├── git-ops.ts # Git-операции (история, коммиты)
├── evaluator.ts # Логика оценки эффективности
├── gitea-client.ts # Gitea API для логирования
── pipeline-runner.ts # Оркестратор пайплайна
│ └── types.ts # TypeScript-типы системы
```
---
@@ -356,14 +357,188 @@ chore(ai-brain): optimize Lead Dev prompt based on Issue #142 failures
---
## KiloCode Workflows Integration
Проект интегрирован с **KiloCode Workflows** (slash commands):
### Доступные команды
| Команда | Описание | Пример |
|---------|----------|--------|
| `/pipeline <issue>` | Запуск полного пайплайна | `/pipeline 42` |
| `/status <issue>` | Проверка статуса пайплайна | `/status 42` |
| `/evaluate <issue>` | Генерация отчёта эффективности | `/evaluate 42` |
| `/plan` | Создание детального плана | `/plan feature X` |
| `/ask` | Вопросы по кодовой базе | `/ask how does auth work` |
| `/debug` | Анализ и исправление багов | `/debug error in login` |
| `/code` | Быстрая генерация кода | `/code add validation` |
### Как использовать Pipeline
1. Откройте проект в VS Code с плагином KiloCode
2. Создайте Issue в Gitea
3. Введите `/pipeline <номер-issue>` в чате KiloCode
4. Пайплайн автоматически:
- Получит контекст Issue
- Проверит дубликаты в git
- Маршрутизирует через агентов по статусу
- Логирует прогресс в комментарии Gitea
- Сгенерирует итоговый отчёт
### Workflow файлы
| Файл | Назначение |
|------|------------|
| `.kilo/commands/pipeline.md` | Основной workflow пайплайна |
| `.kilo/commands/status.md` | Проверка статуса |
| `.kilo/commands/evaluate.md` | Оценка эффективности |
| `.kilo/agents/*.md` | Определения агентов (subagents) |
| `.kilo/rules/*.md` | Правила кодирования |
| `.kilo/skills/gitea/` | Gitea integration skill |
### Прямой вызов агентов
Агентов можно вызывать напрямую через `@mention`:
```
@lead-developer implement authentication flow
@code-skeptic review the auth module
@security-auditor check for vulnerabilities
```
---
## Технический стек
- **Оркестрация**: Node.js / TypeScript (Agent Manager)
- **Интеграция**: KiloCode VS Code Extension
- **Интеграция**: KiloCode VS Code Extension / Claude Code
- **Версионирование**: Gitea + Git Flow
- **Язык разработки**: Go / Node.js (основные проекты)
- **Язык разработки**: TypeScript / Node.js / Go
- **Тестирование**: TDD (Red-Green-Refactor)
---
## Agent Manager API
Новый модуль `src/kilocode/` предоставляет программный API для работы с пайплайном:
### Установка
```bash
bun install
bun run build
```
### Использование
```typescript
import {
PipelineRunner,
GiteaClient,
decideRouting,
WORKFLOW_GRAPH,
type AgentRole,
type IssueStatus
} from './src/kilocode/index.js'
// Инициализация пайплайна
const runner = await createPipelineRunner({
giteaToken: process.env.GITEA_TOKEN,
giteaApiUrl: 'https://git.softuniq.eu/api/v1',
efficiencyThreshold: 7,
autoLog: true
})
// Запуск пайплайна для Issue
const result = await runner.run({
issueNumber: 42,
files: ['src/auth.ts', 'src/user.ts'],
testResults: { passed: 5, failed: 0 }
})
// Определение следующего агента
const decision = decideRouting({
status: 'implementing',
labels: ['status: implemented'],
checklists: { completed: 3, total: 5 },
comments: [],
files: ['src/auth.ts']
})
console.log(decision.nextAgent) // 'code-skeptic'
```
### Gitea интеграция
```typescript
const client = new GiteaClient({
apiUrl: 'https://git.softuniq.eu/api/v1',
token: process.env.GITEA_TOKEN
})
client.setRepository('UniqueSoft', 'APAW')
// Получить Issue
const issue = await client.getIssue(42)
// Установить статус
await client.setStatus(42, 'implementing')
// Добавить комментарий
await client.createComment(42, {
body: '## ✅ Implementation Complete\n\nAll tests passed.'
})
// Закрыть Issue
await client.closeIssue(42)
```
### Логирование эффективности
```typescript
await runner.logEvaluation(42, [
{ agent: 'lead-developer', score: 8, notes: 'Clean implementation' },
{ agent: 'code-skeptic', score: 7, notes: 'Found 2 minor issues' },
{ agent: 'the-fixer', score: 9, notes: 'Fixed issues quickly', iterations: 1 }
], 2, 1.5)
```
---
## Self-Improving Pipeline
Система автоматически логирует эффективность каждого агента в Gitea Issues:
1. **Pipeline Runner** запускается для каждой задачи
2. Каждый агент логирует свой прогресс в комментарии Issue
3. **Evaluator** оценивает эффективность агентов (1-10)
4. **Prompt Optimizer** получает агентов с низкими оценками (<7)
5. Промпты автоматически улучшаются на основе анализа
Журнал эффективности сохраняется в `.kilo/logs/efficiency_score.json` для аналитики.
---
## Переменные окружения
```bash
# Gitea API
GITEA_API_URL=https://git.softuniq.eu/api/v1
GITEA_TOKEN=your-token-here
```
---
## Удаление дубликатов
Если в проекте есть старые файлы в `packages/opencode/`, выполните:
```bash
sudo rm -rf packages/opencode/src/kilocode/
```
Код интегрирован в `src/kilocode/`.
---
*Разработано в рамках проекта APAW (Automatic Programmers Agent Workflow) — 2026*

23
cleanup-packages.sh Executable file
View File

@@ -0,0 +1,23 @@
#!/bin/bash
# Cleanup script to remove duplicate files in packages/opencode
# The agent-manager code is now integrated into src/kilocode/
# Run with: sudo ./cleanup-packages.sh
echo "Removing duplicate files from packages/opencode..."
# Remove the old location (files are now in src/kilocode/agent-manager/)
rm -rf /opt/Projects/APAW/packages/opencode/src/kilocode/
# Remove empty directories
rmdir /opt/Projects/APAW/packages/opencode/src/ 2>/dev/null || true
rmdir /opt/Projects/APAW/packages/opencode/ 2>/dev/null || true
rmdir /opt/Projects/APAW/packages/ 2>/dev/null || true
echo "Cleanup complete!"
echo ""
echo "Agent manager is now located at:"
echo " src/kilocode/agent-manager/ - Core modules"
echo " src/kilocode/index.ts - Entry point"
echo ""
echo "Usage:"
echo " import { PipelineRunner, GiteaClient } from './src/kilocode/index.js'"

View File

@@ -2,5 +2,17 @@
# Fix permissions for root-owned files
# Run with: sudo ./fix-permissions.sh
chown -R swp:swp /opt/Projects/APAW/packages/opencode/src/kilocode/agent-manager/
echo "Permissions fixed"
echo "Fixing permissions..."
# Fix ownership for all project directories
chown -R swp:swp /opt/Projects/APAW/packages/ 2>/dev/null || true
chown -R swp:swp /opt/Projects/APAW/src/ 2>/dev/null || true
chown -R swp:swp /opt/Projects/APAW/.kilo/ 2>/dev/null || true
chown swp:swp /opt/Projects/APAW/*.sh 2>/dev/null || true
chown swp:swp /opt/Projects/APAW/package.json 2>/dev/null || true
chown swp:swp /opt/Projects/APAW/tsconfig.json 2>/dev/null || true
# Make scripts executable
chmod +x /opt/Projects/APAW/*.sh 2>/dev/null || true
echo "Permissions fixed!"

46
package.json Normal file
View File

@@ -0,0 +1,46 @@
{
"name": "apaw",
"version": "1.0.0",
"description": "Self-improving code pipeline with agent management and Gitea logging",
"type": "module",
"main": "./dist/kilocode/index.js",
"types": "./dist/kilocode/index.d.ts",
"exports": {
".": {
"import": "./dist/kilocode/index.js",
"types": "./dist/kilocode/index.d.ts"
},
"./agent-manager": {
"import": "./dist/kilocode/agent-manager/index.js",
"types": "./dist/kilocode/agent-manager/index.d.ts"
}
},
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"clean": "rm -rf dist",
"typecheck": "tsc --noEmit",
"test": "bun test"
},
"dependencies": {
"zod": "^3.24.1"
},
"devDependencies": {
"@types/bun": "^1.1.6",
"@types/node": "^20.10.0",
"typescript": "^5.4.5"
},
"keywords": [
"agent",
"pipeline",
"workflow",
"gitea",
"automation",
"self-improving",
"kilocode"
],
"license": "MIT",
"workspaces": [
".kilo"
]
}

View File

@@ -1,4 +1,5 @@
// kilocode_change - new file
// kilocode_change - integrated module
// Evaluator - scores agent performance
import type { AgentRole } from "./index"
import { saveEfficiencyScore } from "./prompt-loader"

View File

@@ -1,6 +1,7 @@
// kilocode_change - new file
// kilocode_change - integrated module
// Git operations using spawn API
import { $ } from "bun"
import { spawn } from "child_process"
export interface CommitInfo {
hash: string
@@ -15,15 +16,45 @@ export interface IssueReference {
action: "closed" | "fixed" | "references" | "related"
}
async function execGit(args: string[]): Promise<string> {
return new Promise((resolve, reject) => {
const process = spawn("git", args, { cwd: process.cwd() })
let stdout = ""
let stderr = ""
process.stdout.on("data", (data) => {
stdout += data.toString()
})
process.stderr.on("data", (data) => {
stderr += data.toString()
})
process.on("close", (code) => {
if (code === 0) {
resolve(stdout)
} else {
reject(new Error(`Git command failed: git ${args.join(" ")}\n${stderr}`))
}
})
process.on("error", (err) => {
reject(err)
})
})
}
export async function gitLog(since?: string, until?: string, limit = 50): Promise<CommitInfo[]> {
const sinceArg = since ? `--since="${since}"` : ""
const untilArg = until ? `--until="${until}"` : ""
const format = '%H%n%an%n%ad%n%s%n---'
const cmd = `git log ${sinceArg} ${untilArg} -n ${limit} --pretty=format:"${format}" --name-only`
const args = ["log"]
if (since) args.push(`--since=${since}`)
if (until) args.push(`--until=${until}`)
args.push("-n", String(limit))
args.push("--pretty=format:%H%n%an%n%ad%n%s%n---")
args.push("--name-only")
try {
const result = await $`git log ${sinceArg} ${untilArg} -n ${limit} --pretty=format:${format} --name-only`.text()
const result = await execGit(args)
return parseGitLog(result)
} catch (err) {
return []
@@ -32,7 +63,11 @@ export async function gitLog(since?: string, until?: string, limit = 50): Promis
export async function searchCommits(query: string): Promise<CommitInfo[]> {
try {
const result = await $`git log --all --grep="${query}" --pretty=format:"%H%n%an%n%ad%n%s%n---" --name-only`.text()
const result = await execGit([
"log", "--all", `--grep=${query}`,
"--pretty=format:%H%n%an%n%ad%n%s%n---",
"--name-only"
])
return parseGitLog(result)
} catch (err) {
return []
@@ -41,7 +76,11 @@ export async function searchCommits(query: string): Promise<CommitInfo[]> {
export async function getFileHistory(filepath: string, limit = 20): Promise<CommitInfo[]> {
try {
const result = await $`git log -n ${limit} --pretty=format:"%H%n%an%n%ad%n%s%n---" -- ${filepath}`.text()
const result = await execGit([
"log", "-n", String(limit),
"--pretty=format:%H%n%an%n%ad%n%s%n---",
"--", filepath
])
return parseGitLog(result)
} catch (err) {
return []
@@ -50,7 +89,9 @@ export async function getFileHistory(filepath: string, limit = 20): Promise<Comm
export async function blameFile(filepath: string): Promise<{ line: number; author: string; date: string }[]> {
try {
const result = await $`git blame --line-porcelain ${filepath}`.text()
const result = await execGit([
"blame", "--line-porcelain", filepath
])
return parseBlame(result)
} catch (err) {
return []
@@ -78,7 +119,7 @@ export async function findRelatedIssues(keyword: string): Promise<IssueReference
export async function hasCommitWithMessage(message: string): Promise<boolean> {
try {
await $`git log --all --grep="${message}" -n 1`.quiet()
await execGit(["log", "--all", `--grep=${message}`, "-n", "1"])
return true
} catch (err) {
return false

View File

@@ -0,0 +1,259 @@
// kilocode_change - integrated module
// Gitea API client for logging agent performance
const GITEA_API_URL = process.env.GITEA_API_URL || "https://git.softuniq.eu/api/v1"
const GITEA_TOKEN = process.env.GITEA_TOKEN || ""
export interface GiteaConfig {
apiUrl: string
token: string
owner: string
repo: string
}
export interface IssueComment {
body: string
created_at?: string
updated_at?: string
}
export interface Issue {
number: number
title: string
body: string
state: "open" | "closed"
labels: Array<{ name: string; color: string }>
assignees: Array<{ login: string }>
comments: number
created_at: string
updated_at: string
}
export interface CreateIssueOptions {
title: string
body: string
labels?: string[]
assignees?: string[]
}
export interface CreateCommentOptions {
body: string
}
export class GiteaClient {
private baseUrl: string
private token: string
private owner: string
private repo: string
constructor(config?: Partial<GiteaConfig>) {
this.baseUrl = config?.apiUrl || GITEA_API_URL
this.token = config?.token || GITEA_TOKEN
this.owner = config?.owner || ""
this.repo = config?.repo || ""
}
setRepository(owner: string, repo: string): void {
this.owner = owner
this.repo = repo
}
private async request<T>(
method: string,
path: string,
body?: unknown
): Promise<T> {
const url = `${this.baseUrl}${path}`
const headers: Record<string, string> = {
"Accept": "application/json",
"Content-Type": "application/json",
}
if (this.token) {
headers["Authorization"] = `token ${this.token}`
}
const response = await fetch(url, {
method,
headers,
body: body ? JSON.stringify(body) : undefined,
})
if (!response.ok) {
const error = await response.text()
throw new Error(`Gitea API error: ${response.status} - ${error}`)
}
return response.json()
}
async getIssue(issueNumber: number): Promise<Issue> {
return this.request(
"GET",
`/repos/${this.owner}/${this.repo}/issues/${issueNumber}`
)
}
async createIssue(options: CreateIssueOptions): Promise<Issue> {
return this.request(
"POST",
`/repos/${this.owner}/${this.repo}/issues`,
options
)
}
async addLabel(issueNumber: number, labelId: number): Promise<void> {
await this.request(
"POST",
`/repos/${this.owner}/${this.repo}/issues/${issueNumber}/labels`,
{ labels: [labelId] }
)
}
async removeLabel(issueNumber: number, labelId: number): Promise<void> {
await this.request(
"DELETE",
`/repos/${this.owner}/${this.repo}/issues/${issueNumber}/labels/${labelId}`
)
}
async createComment(issueNumber: number, options: CreateCommentOptions): Promise<{ id: number; body: string }> {
return this.request(
"POST",
`/repos/${this.owner}/${this.repo}/issues/${issueNumber}/comments`,
options
)
}
async getComments(issueNumber: number): Promise<IssueComment[]> {
return this.request(
"GET",
`/repos/${this.owner}/${this.repo}/issues/${issueNumber}/comments`
)
}
async updateComment(issueNumber: number, commentId: number, body: string): Promise<void> {
await this.request(
"PATCH",
`/repos/${this.owner}/${this.repo}/issues/comments/${commentId}`,
{ body }
)
}
async closeIssue(issueNumber: number): Promise<Issue> {
return this.request(
"PATCH",
`/repos/${this.owner}/${this.repo}/issues/${issueNumber}`,
{ state: "closed" }
)
}
async reopenIssue(issueNumber: number): Promise<Issue> {
return this.request(
"PATCH",
`/repos/${this.owner}/${this.repo}/issues/${issueNumber}`,
{ state: "open" }
)
}
async getStatusLabels(issueNumber: number): Promise<string[]> {
const issue = await this.getIssue(issueNumber)
return issue.labels
.filter(l => l.name.startsWith("status:"))
.map(l => l.name)
}
async setStatus(issueNumber: number, status: string): Promise<void> {
const statusLabel = `status: ${status}`
const allLabels = await this.request<Array<{ id: number; name: string }>>(
"GET",
`/repos/${this.owner}/${this.repo}/labels`
)
const statusLabels = allLabels.filter(l => l.name.startsWith("status:"))
for (const label of statusLabels) {
try {
await this.removeLabel(issueNumber, label.id)
} catch {
// Label might not be on issue
}
}
const targetLabel = allLabels.find(l => l.name === statusLabel)
if (targetLabel) {
await this.addLabel(issueNumber, targetLabel.id)
}
}
}
export async function logAgentPerformance(
client: GiteaClient,
issueNumber: number,
agentName: string,
score: number,
notes: string
): Promise<void> {
const comment = `## Agent Performance Log
| Metric | Value |
|--------|-------|
| Agent | ${agentName} |
| Score | ${score}/10 |
| Timestamp | ${new Date().toISOString()} |
### Notes
${notes}
`
await client.createComment(issueNumber, { body: comment })
}
export async function logPipelineStep(
client: GiteaClient,
issueNumber: number,
step: string,
status: "started" | "completed" | "failed",
details?: string
): Promise<void> {
const emoji = status === "completed" ? "✅" : status === "failed" ? "❌" : "🔄"
const comment = `${emoji} **${step}**: ${status}${details ? `\n\n\`\`\`\n${details}\n\`\`\`` : ""}`
await client.createComment(issueNumber, { body: comment })
}
export async function detectRepository(): Promise<{ owner: string; repo: string }> {
try {
const { spawn } = await import("child_process")
return new Promise((resolve) => {
const process = spawn("git", ["remote", "get-url", "origin"], { cwd: process.cwd() })
let stdout = ""
process.stdout.on("data", (data) => {
stdout += data.toString()
})
process.on("close", () => {
// Parse URLs like:
// - git@git.softuniq.eu:UniqueSoft/APAW.git
// - https://git.softuniq.eu/UniqueSoft/APAW.git
const match = stdout.match(/[:/]([^/]+)\/([^/.]+)/)
if (match) {
resolve({ owner: match[1], repo: match[2] })
} else {
resolve({ owner: "", repo: "" })
}
})
process.on("error", () => {
resolve({ owner: "", repo: "" })
})
})
} catch {
return { owner: "", repo: "" }
}
}

View File

@@ -1,4 +1,5 @@
// kilocode_change - new file
// kilocode_change - integrated module
// Agent manager - loads configs, manages workflow
import { readFile, readdir } from "fs/promises"
import { join } from "path"

View File

@@ -0,0 +1,257 @@
// kilocode_change - integrated module
// Pipeline runner - orchestrates agent workflow with Gitea logging
import type { AgentRole } from "./index"
import { decideRouting, formatAgentTag, type IssueContext, type RoutingDecision } from "./router"
import { type IssueStatus } from "./workflow"
import {
saveEfficiencyScore,
type EfficiencyScore,
hasLowScore,
findPromptOptimizationTargets
} from "./prompt-loader"
import {
calculateOverallScore,
generateRecommendations,
type AgentPerformance,
type EvaluationResult
} from "./evaluator"
import {
GiteaClient,
logPipelineStep,
logAgentPerformance,
detectRepository
} from "./gitea-client"
export interface PipelineConfig {
giteaToken?: string
giteaApiUrl?: string
efficiencyThreshold?: number
autoLog?: boolean
}
export interface PipelineRunOptions {
issueNumber: number
initialStatus?: IssueStatus
files?: string[]
testResults?: { passed: number; failed: number }
}
export interface PipelineResult {
success: boolean
finalAgent: AgentRole | null
finalStatus: string
agentsUsed: AgentRole[]
totalSteps: number
errors: string[]
}
export class PipelineRunner {
private client: GiteaClient
private efficiencyThreshold: number
private autoLog: boolean
private initialized: boolean = false
constructor(config: PipelineConfig = {}) {
this.client = new GiteaClient({
token: config.giteaToken,
apiUrl: config.giteaApiUrl,
})
this.efficiencyThreshold = config.efficiencyThreshold ?? 7
this.autoLog = config.autoLog ?? true
}
async initialize(): Promise<void> {
if (this.initialized) return
const { owner, repo } = await detectRepository()
this.client.setRepository(owner, repo)
this.initialized = true
}
async run(options: PipelineRunOptions): Promise<PipelineResult> {
await this.initialize()
const agentsUsed: AgentRole[] = []
const errors: string[] = []
let currentStatus: IssueStatus = options.initialStatus ?? "new"
let currentAgent: AgentRole | null = null
let steps = 0
const maxSteps = 20 // Prevent infinite loops
let ctx: IssueContext = await this.buildIssueContext(options)
while (steps < maxSteps) {
steps++
const decision = decideRouting(ctx)
if (!decision.nextAgent) {
break
}
currentAgent = decision.nextAgent
agentsUsed.push(currentAgent)
if (this.autoLog) {
await logPipelineStep(
this.client,
options.issueNumber,
`${formatAgentTag(currentAgent)}`,
"started",
decision.instructions
)
}
currentStatus = decision.status as IssueStatus
await this.client.setStatus(options.issueNumber, currentStatus)
ctx = await this.buildIssueContext(options)
}
return {
success: errors.length === 0,
finalAgent: currentAgent,
finalStatus: currentStatus,
agentsUsed,
totalSteps: steps,
errors,
}
}
private async buildIssueContext(options: PipelineRunOptions): Promise<IssueContext> {
const issue = await this.client.getIssue(options.issueNumber)
const comments = await this.client.getComments(options.issueNumber)
return {
status: issue.labels.find(l => l.name.startsWith("status:"))?.name.replace("status: ", "") ?? "new",
labels: issue.labels.map(l => l.name),
checklists: this.parseChecklists(issue.body),
comments: comments.map(c => c.body),
files: options.files ?? [],
testResults: options.testResults,
}
}
private parseChecklists(body: string): { completed: number; total: number } {
const lines = body.split("\n")
const checkItems = lines.filter(l => l.match(/- \[[ x]\]/i))
const completed = checkItems.filter(l => l.match(/- \[x\]/i)).length
return { completed, total: checkItems.length }
}
async logEvaluation(
issueNumber: number,
performances: AgentPerformance[],
iterations: number,
durationHours: number
): Promise<void> {
await this.initialize()
const agents: Record<string, number> = {}
for (const perf of performances) {
agents[perf.agent] = perf.score
}
const result: EvaluationResult = {
issue: issueNumber,
date: new Date().toISOString(),
agents,
iterations,
duration_hours: durationHours,
summary: calculateOverallScore(performances).toString(),
recommendations: generateRecommendations({
issue: issueNumber,
date: new Date().toISOString(),
agents,
iterations,
duration_hours: durationHours,
summary: "",
recommendations: [],
}),
}
await saveEfficiencyScore({
issue: result.issue,
date: result.date,
agents: result.agents,
iterations: result.iterations,
duration_hours: result.duration_hours,
})
if (this.autoLog) {
const overallScore = calculateOverallScore(performances)
const scoreEmoji = overallScore >= 8 ? "🟢" : overallScore >= 5 ? "🟡" : "🔴"
let comment = `## ${scoreEmoji} Pipeline Evaluation Report
**Issue**: #${issueNumber}
**Overall Score**: ${overallScore}/10
**Duration**: ${durationHours.toFixed(1)}h
**Iterations**: ${iterations}
### Agent Scores
| Agent | Score |
|-------|-------|
`
for (const perf of performances) {
const emoji = perf.score >= 8 ? "🟢" : perf.score >= 5 ? "🟡" : "🔴"
comment += `| ${emoji} ${perf.agent} | ${perf.score}/10 |\n`
}
if (result.recommendations.length > 0) {
comment += `\n### Recommendations\n\n`
for (const rec of result.recommendations) {
comment += `- ${rec}\n`
}
}
await this.client.createComment(issueNumber, { body: comment })
const lowScorers = performances.filter(p => p.score < this.efficiencyThreshold)
if (lowScorers.length > 0) {
const targets = lowScorers.map(p => `@${p.agent}`).join(", ")
await this.client.createComment(issueNumber, {
body: `⚠️ **Prompt Optimization Needed**\n\nThe following agents scored below ${this.efficiencyThreshold}/10: ${targets}\n\nConsider running prompt optimization after this issue is closed.`
})
}
}
}
async checkForDuplicates(issueNumber: number, keywords: string[]): Promise<{
hasDuplicates: boolean
relatedIssues: number[]
}> {
await this.initialize()
const recentComments = await this.client.getComments(issueNumber)
const minedIssues: number[] = []
for (const keyword of keywords) {
for (const comment of recentComments) {
const matches = comment.body.matchAll(/#(\d+)/g)
for (const match of matches) {
const num = parseInt(match[1], 10)
if (num !== issueNumber && !minedIssues.includes(num)) {
minedIssues.push(num)
}
}
}
}
return {
hasDuplicates: minedIssues.length > 0,
relatedIssues: minedIssues,
}
}
}
export async function createPipelineRunner(config?: PipelineConfig): Promise<PipelineRunner> {
const runner = new PipelineRunner(config)
await runner.initialize()
return runner
}
export { GiteaClient }

View File

@@ -1,4 +1,5 @@
// kilocode_change - new file
// kilocode_change - integrated module
// Prompt loader with efficiency logging
import { readFile, writeFile, readdir, mkdir } from "fs/promises"
import { join, dirname } from "path"
@@ -9,7 +10,7 @@ const AGENTS_DIR = ".kilo/agent"
const LOGS_DIR = ".kilo/logs"
const EFFICIENCY_FILE = "efficiency_score.json"
interface EfficiencyScore {
export interface EfficiencyScore {
issue: number
date: string
agents: Record<AgentRole, number>
@@ -17,7 +18,7 @@ interface EfficiencyScore {
duration_hours: number
}
interface EfficiencyLog {
export interface EfficiencyLog {
version: string
history: EfficiencyScore[]
}
@@ -64,7 +65,7 @@ export async function saveEfficiencyScore(score: EfficiencyScore): Promise<void>
await writeFile(logPath, JSON.stringify(log, null, 2), "utf-8")
}
export async function hasLowScore(score: EfficiencyScore, threshold = 7): boolean {
export async function hasLowScore(score: EfficiencyScore, threshold = 7): Promise<boolean> {
const lowScores = Object.entries(score.agents)
.filter(([_, s]) => s < threshold)
.map(([agent, _]) => agent)
@@ -92,7 +93,6 @@ export async function initializeAgentDirectory(): Promise<void> {
await ensureDir(agentsDir)
await ensureDir(logsDir)
// Initialize efficiency log if it doesn't exist
const logPath = join(logsDir, EFFICIENCY_FILE)
if (!existsSync(logPath)) {
const initial: EfficiencyLog = { version: "1.0", history: [] }

View File

@@ -1,4 +1,5 @@
// kilocode_change - new file
// kilocode_change - integrated module
// Router - decides next agent based on issue context
import type { AgentRole } from "./index"
import { getNextAgent, getStatusForAgent } from "./workflow"

View File

@@ -1,8 +1,11 @@
// kilocode_change - new file
// kilocode_change - integrated module
// Typings re-export
export type { AgentRole, AgentConfig, WorkflowTransition, WorkflowState } from "./index"
export type { IssueStatus } from "./workflow"
export type { IssueStatus, WorkflowNode } from "./workflow"
export type { RoutingDecision, IssueContext } from "./router"
export type { EfficiencyScore, EfficiencyLog } from "./prompt-loader"
export type { AgentPerformance, EvaluationResult } from "./evaluator"
export type { CommitInfo, IssueReference } from "./git-ops"
export type { AgentPerformance, EvaluationResult } from "./evaluator"
export type { GiteaConfig, Issue, IssueComment, CreateIssueOptions } from "./gitea-client"
export type { PipelineConfig, PipelineRunOptions, PipelineResult } from "./pipeline-runner"

View File

@@ -1,4 +1,5 @@
// kilocode_change - new file
// kilocode_change - integrated module
// Workflow graph and state transitions
import type { AgentRole } from "./index"

111
src/kilocode/index.ts Normal file
View File

@@ -0,0 +1,111 @@
// kilocode_change - integrated module
// Main entry point for agent-manager
// Types
export type {
AgentRole,
AgentConfig,
WorkflowTransition,
WorkflowState
} from "./agent-manager/index"
export type {
EfficiencyScore,
EfficiencyLog
} from "./agent-manager/prompt-loader"
export type {
IssueStatus,
WorkflowNode
} from "./agent-manager/workflow"
export type {
RoutingDecision,
IssueContext
} from "./agent-manager/router"
export type {
AgentPerformance,
EvaluationResult
} from "./agent-manager/evaluator"
export type {
CommitInfo,
IssueReference
} from "./agent-manager/git-ops"
export type {
GiteaConfig,
Issue,
IssueComment,
CreateIssueOptions
} from "./agent-manager/gitea-client"
export type {
PipelineConfig,
PipelineRunOptions,
PipelineResult
} from "./agent-manager/pipeline-runner"
// Agent Manager functions
export {
loadAgentConfig,
loadAllAgents,
loadRules,
loadFullSystemPrompt
} from "./agent-manager/index"
// Prompt Loader functions
export {
loadPrompt,
savePrompt,
loadEfficiencyLog,
saveEfficiencyScore,
hasLowScore,
findPromptOptimizationTargets,
initializeAgentDirectory,
listAvailableAgents,
} from "./agent-manager/prompt-loader"
// Workflow constants and functions
export {
WORKFLOW_GRAPH,
STATUS_LABELS,
AGENT_BY_STATUS,
NEXT_AGENT_AFTER,
getNextAgent,
getStatusForAgent,
} from "./agent-manager/workflow"
// Router functions
export {
decideRouting,
formatAgentTag,
parseAgentTag,
} from "./agent-manager/router"
// Evaluator functions
export {
calculateOverallScore,
detectPatterns,
generateRecommendations,
saveEvaluation,
formatEvaluationReport,
} from "./agent-manager/evaluator"
// Git operations
export {
gitLog,
searchCommits,
getFileHistory,
blameFile,
findRelatedIssues,
hasCommitWithMessage,
} from "./agent-manager/git-ops"
// Gitea Client
export {
GiteaClient,
logAgentPerformance,
logPipelineStep,
detectRepository,
} from "./agent-manager/gitea-client"
// Pipeline Runner
export {
PipelineRunner,
createPipelineRunner,
} from "./agent-manager/pipeline-runner"

View File

@@ -0,0 +1,58 @@
#!/usr/bin/env bun
// Pipeline runner script
// Usage: bun run src/kilocode/scripts/run-pipeline.ts --issue 42
import {
createPipelineRunner,
loadAllAgents,
type AgentRole
} from '../index.js'
const args = process.argv.slice(2)
const issueArg = args.find(a => a.startsWith('--issue='))
const issueNumber = issueArg ? parseInt(issueArg.split('=')[1], 10) : null
if (!issueNumber) {
console.error('Usage: bun run run-pipeline.ts --issue=<number>')
process.exit(1)
}
async function main() {
console.log(`🚀 Starting pipeline for issue #${issueNumber}`)
try {
// Load available agents
const agents = await loadAllAgents()
console.log(`📋 Loaded ${agents.size} agent configurations`)
// Create pipeline runner
const runner = await createPipelineRunner({
efficiencyThreshold: 7,
autoLog: true
})
// Run pipeline
const result = await runner.run({
issueNumber,
files: []
})
console.log('\n📊 Pipeline Result:')
console.log(` Success: ${result.success}`)
console.log(` Final Status: ${result.finalStatus}`)
console.log(` Agents Used: ${result.agentsUsed.join(' → ')}`)
console.log(` Total Steps: ${result.totalSteps}`)
if (result.errors.length > 0) {
console.log('\n❌ Errors:')
result.errors.forEach(e => console.log(` - ${e}`))
}
process.exit(result.success ? 0 : 1)
} catch (error) {
console.error('Pipeline error:', error)
process.exit(1)
}
}
main()

25
tsconfig.json Normal file
View File

@@ -0,0 +1,25 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src",
"resolveJsonModule": true,
"isolatedModules": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"allowImportingTsExtensions": false,
"rewriteRelativeImportExtensions": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}