@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.
474 lines (463 loc) • 16.2 kB
HTML
<!-- ═══ Setup View ═══ -->
<div
x-show="activeView === 'setup'"
x-cloak
style="height: calc(100vh - 3.5rem); overflow-y: auto"
>
<div style="max-width: 1100px; margin: 0 auto; padding: 20px 24px 24px">
<div style="margin-bottom: 14px">
<h2
class="gf-text-primary"
style="font-size: 18px; font-weight: 600; margin-bottom: 4px"
>
Setup
</h2>
<p class="gf-text-muted" style="font-size: 14px; max-width: 760px">
Generate and run a setup prompt to install or refresh goat-flow for a
specific target agent. The header Runner executes the selected setup
prompt in Workspace.
</p>
</div>
<!-- Status banner (full width) -->
<div class="gf-card" style="padding: 14px 18px; margin-bottom: 12px">
<div
style="
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
flex-wrap: wrap;
"
>
<div
style="display: flex; align-items: center; gap: 12px; flex-wrap: wrap"
>
<span
class="gf-status-dot"
:class="report?.scopes?.setup?.status === 'pass' ? 'gf-status-pass' : 'gf-status-danger'"
:aria-label="report?.scopes?.setup?.status === 'pass' ? 'Passing' : 'Failing'"
x-text="report?.scopes?.setup?.status === 'pass' ? '✓' : '!'"
></span>
<span
class="gf-text-primary"
style="font-size: 15px; font-weight: 600"
x-text="projectName"
></span>
<span class="gf-text-muted">·</span>
<span
style="font-size: 14px; font-weight: 500"
:style="{ color: report?.scopes?.setup?.status === 'pass' ? 'var(--status-pass)' : 'var(--red-400)' }"
x-text="(() => { const checks = report?.scopes?.setup?.checks || []; const p = checks.filter(c => c.status === 'pass').length; return `${p} of ${checks.length} installed`; })()"
></span>
<span class="gf-text-muted">·</span>
<span class="gf-text-muted" style="font-size: 13px"
>audited just now</span
>
</div>
<button
@click="runAudit(true)"
:disabled="auditing"
class="gf-btn gf-btn-md gf-btn-secondary"
>
<span x-text="auditing ? 'Auditing...' : 'Re-audit'"></span>
</button>
</div>
</div>
<!-- Runner selector (full width) -->
<div style="margin-bottom: 12px">
<div class="section-label" style="margin-bottom: 8px">Setup target</div>
<div
style="
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 10px;
"
>
<template
x-for="agent in supportedAgents"
:key="'setup-runner-' + agent.id"
>
<button
@click="setupSelectedAgent = agent.id; generateSetupPrompt();"
class="gf-card"
:style="setupSelectedAgent === agent.id ? { borderColor: 'var(--accent-border)', background: 'var(--accent-bg)' } : {}"
style="padding: 11px 12px; text-align: left"
>
<div
class="gf-text-primary"
style="font-size: 14px; font-weight: 500"
x-text="agent.name"
></div>
<div
style="
display: flex;
align-items: baseline;
gap: 6px;
margin-top: 4px;
"
>
<span
style="color: var(--accent); font-size: 18px; font-weight: 600"
x-text="setupTargetGrade(agent.id)"
></span>
<span
class="gf-text-muted"
style="font-size: 13px"
x-text="setupTargetPercent(agent.id)"
></span>
</div>
</button>
</template>
</div>
</div>
<!-- Two-column layout -->
<div
style="
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
align-items: start;
"
>
<!-- LEFT COLUMN: config, installs, recommendation -->
<div style="display: flex; flex-direction: column; gap: 12px">
<!-- Detected config -->
<div class="gf-card" style="padding: 16px 18px">
<div class="section-label" style="margin-bottom: 10px">
Detected configuration
</div>
<div style="display: flex; flex-direction: column; gap: 6px">
<div
x-show="setupDetecting"
class="gf-text-muted"
style="font-size: 13px; padding: 4px 0"
>
Detecting project configuration...
</div>
</div>
<div
x-show="!setupDetecting"
style="display: flex; flex-direction: column; gap: 6px"
>
<div
style="
display: grid;
grid-template-columns: 90px 1fr;
gap: 10px;
align-items: center;
"
>
<span class="gf-text-muted" style="font-size: 13px"
>Languages</span
>
<div style="display: flex; gap: 6px; flex-wrap: wrap">
<template
x-for="lang in setupData.languages"
:key="'set-lang-' + lang"
>
<span class="gf-tag" x-text="lang"></span>
</template>
<span
x-show="setupData.languages.length === 0"
class="gf-text-muted"
style="font-size: 13px; font-style: italic"
>None detected</span
>
</div>
</div>
<div
style="
display: grid;
grid-template-columns: 90px 1fr;
gap: 10px;
align-items: center;
"
>
<span class="gf-text-muted" style="font-size: 13px"
>Frameworks</span
>
<div style="display: flex; gap: 6px; flex-wrap: wrap">
<template
x-for="fw in setupData.frameworks"
:key="'set-fw-' + fw"
>
<span class="gf-tag" x-text="fw"></span>
</template>
<span
x-show="setupData.frameworks.length === 0"
class="gf-text-muted"
style="font-size: 13px; font-style: italic"
>None detected</span
>
</div>
</div>
<div
style="
display: grid;
grid-template-columns: 90px 1fr;
gap: 10px;
align-items: center;
"
>
<span class="gf-text-muted" style="font-size: 13px">Build</span>
<span
class="gf-text-secondary mono"
style="font-size: 12.5px"
x-text="setupData.commands.build || '-'"
></span>
</div>
<div
style="
display: grid;
grid-template-columns: 90px 1fr;
gap: 10px;
align-items: center;
"
>
<span class="gf-text-muted" style="font-size: 13px">Test</span>
<span
class="gf-text-secondary mono"
style="font-size: 12.5px"
x-text="setupData.commands.test || '-'"
></span>
</div>
<div
style="
display: grid;
grid-template-columns: 90px 1fr;
gap: 10px;
align-items: center;
"
>
<span class="gf-text-muted" style="font-size: 13px">Lint</span>
<span
class="gf-text-secondary mono"
style="font-size: 12.5px"
x-text="setupData.commands.lint || '-'"
></span>
</div>
<div
style="
display: grid;
grid-template-columns: 90px 1fr;
gap: 10px;
align-items: center;
"
>
<span class="gf-text-muted" style="font-size: 13px"
>Formatter</span
>
<span
class="gf-text-secondary mono"
style="font-size: 12.5px"
x-text="setupData.commands.format || '-'"
></span>
</div>
</div>
</div>
<div class="gf-card" style="padding: 16px 18px">
<div class="section-label" style="margin-bottom: 10px">
Setup installs or refreshes
</div>
<div
style="
display: flex;
flex-direction: column;
gap: 8px;
font-size: 13px;
"
>
<div
style="display: grid; grid-template-columns: 92px 1fr; gap: 8px"
>
<span class="gf-text-muted">Skills</span>
<span class="gf-text-secondary mono" style="font-size: 12px"
>goat, goat-debug, goat-plan, goat-review, goat-critique,
goat-security, goat-qa</span
>
</div>
<div
style="display: grid; grid-template-columns: 92px 1fr; gap: 8px"
>
<span class="gf-text-muted">Hooks</span>
<span class="gf-text-secondary mono" style="font-size: 12px"
>guardrails</span
>
</div>
<div
style="display: grid; grid-template-columns: 92px 1fr; gap: 8px"
>
<span class="gf-text-muted">Instructions</span>
<span
class="gf-text-secondary mono"
style="font-size: 12px"
x-text="setupInstructionSurfaces()"
></span>
</div>
<div
style="display: grid; grid-template-columns: 92px 1fr; gap: 8px"
>
<span class="gf-text-muted">Templates</span>
<span class="gf-text-secondary mono" style="font-size: 12px"
>.goat-flow/learning-loop/footguns, /lessons, /decisions</span
>
</div>
</div>
</div>
<div
class="gf-card"
style="padding: 14px 16px; border-color: rgba(245, 158, 11, 0.35)"
>
<div class="section-label" style="color: #fbbf24; margin-bottom: 6px">
Recommendation
</div>
<p class="gf-text-muted" style="font-size: 13px; margin: 0 0 8px">
<span
x-text="report?.scopes?.setup?.status === 'pass' ? 'Setup checks are passing. Add project-specific architecture and pitfalls context when you want stronger harness scores.' : 'Run setup or fix missing setup files before relying on this project harness.'"
></span>
</p>
<button @click="activeView = 'home'" class="gf-btn gf-btn-sm">
View harness on Home
</button>
</div>
</div>
<!-- RIGHT COLUMN: prompt card -->
<div class="gf-prompt-card" style="position: sticky; top: 20px">
<div class="gf-prompt-hdr">
<div>
<div class="section-label">Setup prompt</div>
<div class="gf-text-muted" style="font-size: 13px; margin-top: 2px">
for
<span
class="gf-text-primary"
style="font-weight: 500"
x-text="agentName(setupSelectedAgent)"
></span>
</div>
</div>
<div style="display: flex; gap: 6px">
<button
x-data="{ copied: false }"
@click="if (setupOutputs[setupSelectedAgent]) { copyText(setupOutputs[setupSelectedAgent]); copied = true; setTimeout(() => copied = false, 1200); }"
class="gf-btn gf-btn-md gf-btn-secondary"
>
<span x-text="copied ? 'Copied' : 'Copy'"></span>
</button>
<button
@click="generateSetupPrompt(true)"
:disabled="setupGenerating"
class="gf-btn gf-btn-md gf-btn-secondary"
>
Regenerate
</button>
</div>
</div>
<div
x-show="!setupGenerating"
class="gf-terminal-cta"
style="padding: 14px 16px; border-radius: 0"
>
<button
@click="if (setupOutputs[setupSelectedAgent]) { launchPreset(setupOutputs[setupSelectedAgent], activeRunner, 'Setup ' + agentName(setupSelectedAgent) + ' via ' + agentName(activeRunner)); activeView = 'workspace'; }"
:disabled="launching || !setupOutputs[setupSelectedAgent]"
class="gf-btn-terminal-cta"
style="width: 100%; justify-content: center; padding: 11px"
>
<span
x-text="launching ? 'Starting setup...' : 'Run Setup in Terminal'"
></span>
<svg
x-show="!launching"
width="13"
height="13"
fill="none"
stroke="currentColor"
stroke-width="2.5"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M14 5l7 7m0 0l-7 7m7-7H3"
/>
</svg>
</button>
<p
class="gf-text-muted"
style="font-size: 13px; margin: 10px 0 0; line-height: 1.5"
>
Runs with
<span
class="mono gf-text-secondary"
x-text="agentName(activeRunner)"
></span>
against the
<span
class="mono gf-text-secondary"
x-text="agentName(setupSelectedAgent)"
></span>
setup prompt.
</p>
</div>
<div x-show="setupGenerating" class="gf-prompt-loading">
<svg
class="animate-spin"
style="width: 16px; height: 16px; color: var(--accent)"
fill="none"
viewBox="0 0 24 24"
>
<circle
class="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
stroke-width="4"
/>
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
/>
</svg>
<span
class="gf-text-muted"
style="font-size: 12px"
x-text="'Generating setup prompt for ' + agentName(setupSelectedAgent) + '...'"
></span>
</div>
<pre
x-show="!setupGenerating && setupOutputs[setupSelectedAgent]"
class="gf-prompt-body"
x-text="setupOutputs[setupSelectedAgent]"
></pre>
<p
x-show="!setupGenerating && !setupOutputs[setupSelectedAgent]"
class="gf-text-disabled"
style="
flex: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
"
>
Select an agent to auto-generate the setup prompt.
</p>
</div>
</div>
<div
class="gf-footer"
style="text-align: center; font-size: 11px; padding: 12px 0"
>
Built by
<a
href="https://www.blundergoat.com"
target="_blank"
class="gf-footer-link"
>BlunderGOAT</a
>
· <span x-text="dashboardVersion ? `v${dashboardVersion}` : ''"></span>
</div>
</div>
</div>