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:
58
.kilo/commands/evaluate.md
Normal file
58
.kilo/commands/evaluate.md
Normal 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
139
.kilo/commands/pipeline.md
Normal 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
41
.kilo/commands/status.md
Normal 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
|
||||
```
|
||||
@@ -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
206
AGENTS.md
@@ -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
203
README.md
@@ -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
23
cleanup-packages.sh
Executable 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'"
|
||||
@@ -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
46
package.json
Normal 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"
|
||||
]
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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
|
||||
259
src/kilocode/agent-manager/gitea-client.ts
Normal file
259
src/kilocode/agent-manager/gitea-client.ts
Normal 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: "" }
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
257
src/kilocode/agent-manager/pipeline-runner.ts
Normal file
257
src/kilocode/agent-manager/pipeline-runner.ts
Normal 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 }
|
||||
@@ -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: [] }
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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
111
src/kilocode/index.ts
Normal 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"
|
||||
58
src/kilocode/scripts/run-pipeline.ts
Normal file
58
src/kilocode/scripts/run-pipeline.ts
Normal 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
25
tsconfig.json
Normal 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"]
|
||||
}
|
||||
Reference in New Issue
Block a user