@blundergoat/goat-flow
Version:
AI coding agent harness and local dashboard for Claude Code, OpenAI Codex, Google Antigravity, and GitHub Copilot - setup audits, guardrails, structured skills, deny hooks, and persistent learning loops.
204 lines (194 loc) • 7.47 kB
HTML
<!-- ═══ Plans View ═══ -->
<div
x-show="activeView === 'plans'"
x-cloak
class="gf-tasks-view"
style="height: calc(100vh - 3.5rem); overflow-y: auto"
>
<main class="gf-tasks-page">
<header class="gf-page-head">
<div>
<h1>Plans</h1>
<p>
Milestone state from <code>.goat-flow/plans/</code> in the selected
project, with active plan selection.
</p>
</div>
<button
@click="loadTasks()"
:disabled="tasksLoading"
class="gf-btn gf-btn-md gf-btn-secondary"
>
<span x-text="tasksLoading ? 'Refreshing...' : 'Refresh'"></span>
</button>
</header>
<div x-show="tasksLoading && !tasksState" class="gf-card gf-task-loading">
Loading plans...
</div>
<div x-show="tasksError" x-cloak class="gf-card gf-task-error" role="alert">
<div class="gf-text-primary">Plans could not be loaded</div>
<p x-text="tasksError"></p>
</div>
<template x-if="tasksState && !tasksState.exists">
<section class="gf-card gf-task-empty">
<span class="gf-empty-mark">M00</span>
<h2>No plans directory found</h2>
<p>
<code x-text="tasksState.planRoot"></code>
does not exist for the selected project.
</p>
</section>
</template>
<template x-if="tasksState && tasksState.exists">
<section
class="gf-tasks-layout"
@gf-select-task-plan="selectTaskPlan($event.detail.planName)"
@gf-set-active-task-plan="setActiveTaskPlan($event.detail.planName)"
>
<aside class="gf-card gf-task-plans">
<div class="gf-task-panel-head">
<div>
<div class="gf-section-label">Plans</div>
<h2 x-text="tasksState.plans.length"></h2>
</div>
<div x-show="tasksState.active" class="gf-task-active-wrap">
<span class="gf-tooltip-wrap">
<button
type="button"
class="gf-info-button gf-task-active-help"
@click.stop
aria-label="What active plan means"
aria-describedby="task-active-plan-tip"
>
?
</button>
<span
id="task-active-plan-tip"
class="gf-tooltip gf-task-active-tooltip"
role="tooltip"
>
Active is the default work plan. Goat skills scan this folder
for current milestones and review/spec context; other plans
stay visible but are not the default.
</span>
</span>
<span
class="gf-task-active-pill"
x-text="'active: ' + tasksState.active"
></span>
</div>
</div>
<div x-show="tasksState.plans.length === 0" class="gf-empty-state">
<span class="gf-empty-mark">--</span>
<p>No plans are present.</p>
</div>
<div class="gf-task-plan-list">
<template x-for="plan in tasksState.plans" :key="plan.path">
<div
class="gf-task-plan-row"
:class="{
'is-selected': plan.name === tasksState.selectedPlan,
'is-active-plan': plan.active,
}"
>
<button
@click="$dispatch('gf-select-task-plan', { planName: plan.name })"
class="gf-task-plan-main"
:aria-label="'View plan ' + plan.name"
>
<span>
<strong x-text="plan.name"></strong>
<small
x-text="plan.milestoneCount + ' milestone' + (plan.milestoneCount === 1 ? '' : 's')"
></small>
</span>
</button>
<button
@click.stop="$dispatch('gf-set-active-task-plan', { planName: plan.name })"
class="gf-task-plan-active-btn"
:class="{ 'is-active': plan.active }"
:disabled="plan.active || tasksActivePlanSaving === plan.name"
:title="plan.active ? 'Active plan' : 'Set active plan'"
:aria-label="plan.active ? plan.name + ' is the active plan' : 'Set ' + plan.name + ' as active plan'"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<path d="M5 5v14" />
<path d="M5 5h11l-2 4 2 4H5" />
</svg>
</button>
</div>
</template>
</div>
</aside>
<section class="gf-card gf-task-milestones">
<div class="gf-task-panel-head">
<div>
<div class="gf-section-label">Milestones</div>
<h2 x-text="tasksState.selectedPlan || 'No plan selected'"></h2>
</div>
<span
class="gf-task-path"
x-text="tasksState.planRoot"
:title="tasksState.planRoot"
></span>
</div>
<div
x-show="tasksState.milestones.length === 0"
class="gf-empty-state"
>
<span class="gf-empty-mark">M00</span>
<p>No milestone files were found for this plan.</p>
</div>
<div class="gf-task-milestone-list">
<template
x-for="milestone in tasksState.milestones"
:key="milestone.path"
>
<article class="gf-task-milestone">
<div class="gf-task-milestone-main">
<div class="gf-task-milestone-copy">
<div class="gf-task-meta-row">
<span class="gf-task-status" x-text="milestone.status">
</span>
<span x-text="taskModifiedLabel(milestone.modifiedAt)">
</span>
</div>
<h3 x-text="milestone.title"></h3>
<p
x-show="milestone.objective"
x-text="milestone.objective"
></p>
<code x-text="milestone.filename"></code>
</div>
<div class="gf-task-progress">
<span x-text="taskProgressLabel(milestone)"></span>
<div
class="gf-task-progress-track"
role="progressbar"
:aria-valuenow="milestone.completedTasks"
aria-valuemin="0"
:aria-valuemax="milestone.totalTasks"
>
<span
class="gf-task-progress-fill"
:style="{ width: taskProgressPct(milestone) + '%' }"
></span>
</div>
</div>
</div>
</article>
</template>
</div>
</section>
</section>
</template>
</main>
</div>