Add backlog audit and task update command
- agents/prompts/backlog_audit.md: QA analyst prompt for checking
which pending tasks are already implemented in the codebase
- agents/runner.py: run_audit() — project-level agent that reads
all pending tasks, inspects code, returns classification
- cli/main.py: kin audit <project_id> — runs audit, offers to mark
done tasks; kin task update <id> --status --priority
- web/api.py: POST /api/projects/{id}/audit (runs audit inline),
POST /api/projects/{id}/audit/apply (batch mark as done)
- Frontend: "Audit backlog" button on ProjectView with results
modal showing already_done/still_pending/unclear categories
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e755a19633
commit
96509dcafc
9 changed files with 548 additions and 2 deletions
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { api, type ProjectDetail } from '../api'
|
||||
import { api, type ProjectDetail, type AuditResult } from '../api'
|
||||
import Badge from '../components/Badge.vue'
|
||||
import Modal from '../components/Modal.vue'
|
||||
|
||||
|
|
@ -28,6 +28,42 @@ function toggleMode() {
|
|||
localStorage.setItem(`kin-mode-${props.id}`, autoMode.value ? 'auto' : 'review')
|
||||
}
|
||||
|
||||
// Audit
|
||||
const auditLoading = ref(false)
|
||||
const auditResult = ref<AuditResult | null>(null)
|
||||
const showAuditModal = ref(false)
|
||||
const auditApplying = ref(false)
|
||||
|
||||
async function runAudit() {
|
||||
auditLoading.value = true
|
||||
auditResult.value = null
|
||||
try {
|
||||
const res = await api.auditProject(props.id)
|
||||
auditResult.value = res
|
||||
showAuditModal.value = true
|
||||
} catch (e: any) {
|
||||
error.value = e.message
|
||||
} finally {
|
||||
auditLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function applyAudit() {
|
||||
if (!auditResult.value?.already_done?.length) return
|
||||
auditApplying.value = true
|
||||
try {
|
||||
const ids = auditResult.value.already_done.map(t => t.id)
|
||||
await api.auditApply(props.id, ids)
|
||||
showAuditModal.value = false
|
||||
auditResult.value = null
|
||||
await load()
|
||||
} catch (e: any) {
|
||||
error.value = e.message
|
||||
} finally {
|
||||
auditApplying.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Add task modal
|
||||
const showAddTask = ref(false)
|
||||
const taskForm = ref({ title: '', priority: 5, route_type: '' })
|
||||
|
|
@ -216,6 +252,12 @@ async function addDecision() {
|
|||
:title="autoMode ? 'Auto mode: agents can write files' : 'Review mode: agents read-only'">
|
||||
{{ autoMode ? '🔓 Auto' : '🔒 Review' }}
|
||||
</button>
|
||||
<button @click="runAudit" :disabled="auditLoading"
|
||||
class="px-2 py-1 text-xs bg-purple-900/30 text-purple-400 border border-purple-800 rounded hover:bg-purple-900/50 disabled:opacity-50"
|
||||
title="Check which pending tasks are already done">
|
||||
<span v-if="auditLoading" class="inline-block w-3 h-3 border-2 border-purple-400 border-t-transparent rounded-full animate-spin mr-1"></span>
|
||||
{{ auditLoading ? 'Auditing...' : 'Audit backlog' }}
|
||||
</button>
|
||||
<button @click="showAddTask = true"
|
||||
class="px-3 py-1 text-xs bg-gray-800 text-gray-300 border border-gray-700 rounded hover:bg-gray-700">
|
||||
+ Task
|
||||
|
|
@ -350,5 +392,46 @@ async function addDecision() {
|
|||
</button>
|
||||
</form>
|
||||
</Modal>
|
||||
|
||||
<!-- Audit Modal -->
|
||||
<Modal v-if="showAuditModal && auditResult" title="Backlog Audit Results" @close="showAuditModal = false">
|
||||
<div v-if="!auditResult.success" class="text-red-400 text-sm">
|
||||
Audit failed: {{ auditResult.error }}
|
||||
</div>
|
||||
<div v-else class="space-y-4">
|
||||
<div v-if="auditResult.already_done?.length">
|
||||
<h3 class="text-sm font-semibold text-green-400 mb-2">Already done ({{ auditResult.already_done.length }})</h3>
|
||||
<div v-for="item in auditResult.already_done" :key="item.id"
|
||||
class="px-3 py-2 border border-green-900/50 rounded text-xs mb-1">
|
||||
<span class="text-green-400 font-medium">{{ item.id }}</span>
|
||||
<span class="text-gray-400 ml-2">{{ item.reason }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="auditResult.still_pending?.length">
|
||||
<h3 class="text-sm font-semibold text-gray-400 mb-2">Still pending ({{ auditResult.still_pending.length }})</h3>
|
||||
<div v-for="item in auditResult.still_pending" :key="item.id"
|
||||
class="px-3 py-2 border border-gray-800 rounded text-xs mb-1">
|
||||
<span class="text-gray-300 font-medium">{{ item.id }}</span>
|
||||
<span class="text-gray-500 ml-2">{{ item.reason }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="auditResult.unclear?.length">
|
||||
<h3 class="text-sm font-semibold text-yellow-400 mb-2">Unclear ({{ auditResult.unclear.length }})</h3>
|
||||
<div v-for="item in auditResult.unclear" :key="item.id"
|
||||
class="px-3 py-2 border border-yellow-900/50 rounded text-xs mb-1">
|
||||
<span class="text-yellow-400 font-medium">{{ item.id }}</span>
|
||||
<span class="text-gray-400 ml-2">{{ item.reason }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="auditResult.cost_usd || auditResult.duration_seconds" class="text-xs text-gray-600">
|
||||
<span v-if="auditResult.duration_seconds">{{ auditResult.duration_seconds }}s</span>
|
||||
<span v-if="auditResult.cost_usd" class="ml-2">${{ auditResult.cost_usd?.toFixed(4) }}</span>
|
||||
</div>
|
||||
<button v-if="auditResult.already_done?.length" @click="applyAudit" :disabled="auditApplying"
|
||||
class="w-full py-2 bg-green-900/50 text-green-400 border border-green-800 rounded text-sm hover:bg-green-900 disabled:opacity-50">
|
||||
{{ auditApplying ? 'Applying...' : `Mark ${auditResult.already_done.length} tasks as done` }}
|
||||
</button>
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue