@dsdenes/devboard 0.1.560 → 0.1.563
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +59 -0
- package/README.md +2497 -26
- package/bin/devboard.mjs +352 -118
- package/client/App.svelte +523 -0
- package/client/FeedbackOverlay.svelte +1125 -0
- package/client/FlowMode.svelte +1015 -0
- package/client/app.css +3922 -0
- package/client/env.d.ts +23 -0
- package/client/lib/commands.ts +836 -0
- package/client/lib/components/ActivityBar.svelte +295 -0
- package/client/lib/components/AdrTable.svelte +241 -0
- package/client/lib/components/CommandPalette.svelte +198 -0
- package/client/lib/components/ConceptKind.svelte +66 -0
- package/client/lib/components/ConfirmDialog.svelte +62 -0
- package/client/lib/components/DataTable.svelte +243 -0
- package/client/lib/components/DetailSplit.svelte +68 -0
- package/client/lib/components/DiscoverOverview.svelte +898 -0
- package/client/lib/components/DiscoverWorkspace.svelte +437 -0
- package/client/lib/components/DraftsPage.svelte +186 -0
- package/client/lib/components/GoalWorkspace.svelte +1042 -0
- package/client/lib/components/IssuesAgentFocus.svelte +117 -0
- package/client/lib/components/IssuesAgentLog.svelte +50 -0
- package/client/lib/components/IssuesQuestion.svelte +95 -0
- package/client/lib/components/IssuesWorkspace.svelte +577 -0
- package/client/lib/components/MermaidDiagram.svelte +89 -0
- package/client/lib/components/MonacoEditor.svelte +190 -0
- package/client/lib/components/NotificationRegion.svelte +195 -0
- package/client/lib/components/PaneHandle.svelte +25 -0
- package/client/lib/components/ScoutIssues.svelte +215 -0
- package/client/lib/components/ScoutWorkspace.svelte +180 -0
- package/client/lib/components/Sidebar.svelte +622 -0
- package/client/lib/components/StatusBar.svelte +118 -0
- package/client/lib/components/Terminal.svelte +193 -0
- package/client/lib/components/WatchdogWorkspace.svelte +537 -0
- package/client/lib/components/ui/button.svelte +56 -0
- package/client/lib/components/ui/icon-button.svelte +56 -0
- package/client/lib/components/ui/input.svelte +19 -0
- package/client/lib/components/ui/page-header.svelte +120 -0
- package/client/lib/components/ui/stage-track.svelte +141 -0
- package/client/lib/components/watchdog-indicator.svelte +189 -0
- package/client/lib/connection.svelte.ts +210 -0
- package/client/lib/dependency-diagram.test.ts +90 -0
- package/client/lib/dependency-diagram.ts +75 -0
- package/client/lib/discover-format.test.ts +265 -0
- package/client/lib/discover-format.ts +394 -0
- package/client/lib/discover.svelte.ts +412 -0
- package/client/lib/draft-format.test.ts +48 -0
- package/client/lib/draft-format.ts +13 -0
- package/client/lib/flow-save.test.ts +148 -0
- package/client/lib/flow.svelte.ts +445 -0
- package/client/lib/goal-reconnect.test.ts +282 -0
- package/client/lib/goal.svelte.ts +652 -0
- package/client/lib/issues.svelte.ts +439 -0
- package/client/lib/layout.svelte.ts +234 -0
- package/client/lib/notification-issue.test.ts +53 -0
- package/client/lib/notification-issue.ts +27 -0
- package/client/lib/notifications.svelte.ts +62 -0
- package/client/lib/poll-status.test.ts +141 -0
- package/client/lib/poll-status.ts +88 -0
- package/client/lib/record-status.test.ts +19 -0
- package/client/lib/record-status.ts +25 -0
- package/client/lib/scout.svelte.ts +164 -0
- package/client/lib/skill-document.test.ts +77 -0
- package/client/lib/skill-document.ts +57 -0
- package/client/lib/status-tone.test.ts +40 -0
- package/client/lib/status-tone.ts +33 -0
- package/client/lib/types.ts +578 -0
- package/client/lib/utils.ts +6 -0
- package/client/lib/viewport.svelte.ts +20 -0
- package/client/lib/watchdog.svelte.ts +69 -0
- package/client/main.ts +7 -0
- package/dist/apple-touch-icon.png +0 -0
- package/dist/assets/abnfDiagram-VCTEODGH-CmBSMArZ.js +1 -0
- package/dist/assets/arc-BZgCSX2H.js +1 -0
- package/dist/assets/architecture-7GRP2DOG-CF9MHlfo.js +1 -0
- package/dist/assets/architectureDiagram-5GKGNRK7-DzWB6xiQ.js +36 -0
- package/dist/assets/array-BifhSqXX.js +1 -0
- package/dist/assets/blockDiagram-I7D4REHJ-BtmGJ0hn.js +129 -0
- package/dist/assets/c4Diagram-7LVT6UL2-D5CHrrRX.js +38 -0
- package/dist/assets/channel-CgM4lcUW.js +1 -0
- package/dist/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
- package/dist/assets/chunk-4HAMMTFA-DeIufjFl.js +62 -0
- package/dist/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
- package/dist/assets/chunk-75Z2AOVW-DbtzyKUg.js +2 -0
- package/dist/assets/chunk-DU6HZSFF-C0fdbx3K.js +127 -0
- package/dist/assets/chunk-F27PBJKO-HObL2IvF.js +1 -0
- package/dist/assets/chunk-FOHPRMQF-DD4dKubD.js +161 -0
- package/dist/assets/chunk-GMAD6QVW-fg8wvtPY.js +72 -0
- package/dist/assets/chunk-GVQU2GXP-C0TaHOrD.js +1 -0
- package/dist/assets/chunk-IMKFNOWR-BVoIdx20.js +231 -0
- package/dist/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
- package/dist/assets/chunk-L3NEJ4N5-B_kmqfk5.js +1 -0
- package/dist/assets/chunk-OSK3NFVY-DO0vhxHH.js +10 -0
- package/dist/assets/chunk-P2QGCYS3-CjljLvFj.js +1 -0
- package/dist/assets/chunk-POPQ4Y6H-BU0C7tw6.js +1 -0
- package/dist/assets/chunk-PWAF6VOD-Dn0OjBt0.js +1 -0
- package/dist/assets/chunk-SHT3W25Y-CMCZE2xx.js +168 -0
- package/dist/assets/chunk-SVP7TREG-BIRmRy_W.js +88 -0
- package/dist/assets/chunk-TICWLB2K-C1bc2CDE.js +206 -0
- package/dist/assets/chunk-XXDRQBXY-Cc38QUmR.js +1 -0
- package/dist/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
- package/dist/assets/classDiagram-ZZMXUADV-BDh5w6Nc.js +1 -0
- package/dist/assets/classDiagram-v2-VYDZK3BY-BDh5w6Nc.js +1 -0
- package/dist/assets/cose-bilkent-JH36ORCC-B7pU7qhM.js +1 -0
- package/dist/assets/cynefin-OW5HDTMX-76dswSqS.js +1 -0
- package/dist/assets/cynefinDiagram-5FMLGOSQ-2kS4q01d.js +62 -0
- package/dist/assets/cytoscape.esm-BegBNzOV.js +321 -0
- package/dist/assets/dagre-D7JM2Yhv.js +1 -0
- package/dist/assets/dagre-GXQ25YYZ-B5Y21TQM.js +4 -0
- package/dist/assets/defaultLocale-BFoDCU3G.js +1 -0
- package/dist/assets/diagram-S7CK7UJ4-CY6td7Et.js +30 -0
- package/dist/assets/diagram-UQ7AKVKN-DmDqnGZf.js +41 -0
- package/dist/assets/diagram-VSXAHHWV-KGLWOOcE.js +3 -0
- package/dist/assets/diagram-VX7I27RA-D5syUraJ.js +24 -0
- package/dist/assets/diagram-Z3DM3KII-Dtrbs1Om.js +24 -0
- package/dist/assets/dist-Dce-2YbA.js +1 -0
- package/dist/assets/ebnfDiagram-PWID7BFC-CQpJLtU3.js +1 -0
- package/dist/assets/editor.worker-BmdWdrHZ.js +33 -0
- package/dist/assets/erDiagram-RLTQ6QDP-cMTzKLu_.js +99 -0
- package/dist/assets/eventmodeling-NTZA5JFV-CMzLdNaL.js +1 -0
- package/dist/assets/flowDiagram-HODETNUW-BMUajB-G.js +1 -0
- package/dist/assets/ganttDiagram-EL5Y4UJY-DseBS5Fs.js +292 -0
- package/dist/assets/gitGraph-4MIJSDKK-CUQZRpxJ.js +1 -0
- package/dist/assets/gitGraphDiagram-WWUBYQGX-CfMfsJ3b.js +106 -0
- package/dist/assets/graphlib-DS17s2tU.js +1 -0
- package/dist/assets/index-ByQ-XX38.css +1 -0
- package/dist/assets/index-CIf5TpfY.js +214 -0
- package/dist/assets/info-A6RAGUB7-D3nAKZ8M.js +1 -0
- package/dist/assets/infoDiagram-27XIBGKW-zu1O0WOy.js +2 -0
- package/dist/assets/init-C-OQMol4.js +1 -0
- package/dist/assets/ishikawaDiagram-5VMMS53U-DKC7BO9p.js +70 -0
- package/dist/assets/journeyDiagram-3NMN7TZE-Ld5mXse7.js +139 -0
- package/dist/assets/kanban-definition-UXKFOSKX-CH7_QlHL.js +89 -0
- package/dist/assets/katex-DJhrJSbw.js +257 -0
- package/dist/assets/line-N9XiCREw.js +1 -0
- package/dist/assets/linear-C9hnqmJv.js +1 -0
- package/dist/assets/mermaid-parser.core-DgPwV5Bh.js +7 -0
- package/dist/assets/mermaid.core-BKqynXLR.js +44 -0
- package/dist/assets/mindmap-definition-YA3MSWOX-DkiL-wiW.js +96 -0
- package/dist/assets/monaco-DjpsE8to.css +1 -0
- package/dist/assets/monaco-DqzJaNIe.js +908 -0
- package/dist/assets/ordinal-BDEzSJ7C.js +1 -0
- package/dist/assets/packet-AYTQ26CC-DuMTvpEJ.js +1 -0
- package/dist/assets/path-fybaL0A-.js +1 -0
- package/dist/assets/pegDiagram-XKGWAZYB-DvMl_xSe.js +1 -0
- package/dist/assets/pie-WAS4IAKB-Da0xPaD8.js +1 -0
- package/dist/assets/pieDiagram-E7YTZNPT-amANNRJY.js +39 -0
- package/dist/assets/quadrantDiagram-AXDQQJYC-BrgvDMnk.js +7 -0
- package/dist/assets/radar-RG4KPBEZ-Dz3Ih5-N.js +1 -0
- package/dist/assets/railroad-74A4TZTK-CKMW-IV2.js +1 -0
- package/dist/assets/railroad-abnf-HS5TGJTU-ChqmSWSF.js +1 -0
- package/dist/assets/railroad-ebnf-LZEXJU2U-V3R4fFdb.js +1 -0
- package/dist/assets/railroad-peg-WCYAUIDC-B-RuhUQW.js +1 -0
- package/dist/assets/railroadDiagram-O6MQD6OU-FjG-6OLu.js +1 -0
- package/dist/assets/requirementDiagram-BXWQKSXE-Cnj8RfE4.js +84 -0
- package/dist/assets/rolldown-runtime-XKyaHtl9.js +1 -0
- package/dist/assets/rough.esm-Dy-Kn_BL.js +1 -0
- package/dist/assets/sankeyDiagram-P5KCCOFB-DMu1fnEh.js +40 -0
- package/dist/assets/sequenceDiagram-WJ2MYXX4-CtCtaRpW.js +162 -0
- package/dist/assets/sizeCapture-INFHLROL-3uHXrFqA.js +1 -0
- package/dist/assets/src-CGp3bfio.js +1 -0
- package/dist/assets/stateDiagram-D77RDMKH-xcJbLNhz.js +1 -0
- package/dist/assets/stateDiagram-v2-MP3YSRHH-DYHrQmJJ.js +1 -0
- package/dist/assets/swimlanes-42K2YHIH-BwLln_ga.js +1 -0
- package/dist/assets/swimlanesDiagram-VR7AAH4N-BcDfUQ0A.js +8 -0
- package/dist/assets/timeline-definition-24CTP7MA-ucNGsUUz.js +120 -0
- package/dist/assets/treeView-Q6P3EWNA-3uOomRue.js +1 -0
- package/dist/assets/treemap-WGGIJYW6-iGK7vwID.js +1 -0
- package/dist/assets/vennDiagram-4TSXK5OY-B9LgGe16.js +34 -0
- package/dist/assets/wardley-WFR3VGLG-FupMOf6k.js +1 -0
- package/dist/assets/wardleyDiagram-VM6X3IG4-CGZdEXM2.js +78 -0
- package/dist/assets/xterm-BrP-ENHg.css +1 -0
- package/dist/assets/xterm-C8zLTNGk.js +16 -0
- package/dist/assets/xychartDiagram-S5SC5T6Z-DafZrSBJ.js +7 -0
- package/dist/favicon.ico +0 -0
- package/dist/favicon.svg +6 -0
- package/dist/index.html +34 -0
- package/extensions/implement-goal-issue.ts +368 -0
- package/extensions/implement-next-epic.ts +835 -0
- package/index.html +28 -0
- package/install.sh +111 -0
- package/issues/control.ts +91 -0
- package/issues/guards.ts +12 -0
- package/issues/ledger.ts +122 -0
- package/issues/legacy.ts +68 -0
- package/issues/lock.ts +305 -0
- package/issues/paths.ts +76 -0
- package/issues/preflight.ts +189 -0
- package/issues/repository.ts +203 -0
- package/issues/scout/actions.ts +255 -0
- package/issues/scout/control.ts +80 -0
- package/issues/scout/github.ts +358 -0
- package/issues/scout/paths.ts +129 -0
- package/issues/scout/preflight.ts +277 -0
- package/issues/scout/recommendation.ts +400 -0
- package/issues/scout/scan.ts +143 -0
- package/issues/scout/state.ts +448 -0
- package/issues/scout/worker.ts +1282 -0
- package/issues/state.ts +584 -0
- package/issues/tracker.ts +271 -0
- package/issues/worker.ts +1065 -0
- package/lib/dependencies.ts +61 -0
- package/lib/forge-auth.ts +953 -0
- package/lib/forge.ts +141 -0
- package/lib/gitea.ts +729 -0
- package/lib/github-release.ts +318 -0
- package/lib/inventory.test.ts +79 -0
- package/lib/inventory.ts +53 -0
- package/lib/release.test.ts +269 -0
- package/lib/release.ts +801 -0
- package/omp/commands/adr-resolve.md +8 -0
- package/omp/commands/discover.md +6 -0
- package/omp/commands/epic-next.md +8 -0
- package/omp/commands/flow-change.md +8 -0
- package/omp/commands/flow-goal.md +8 -0
- package/omp/commands/flow-implement.md +8 -0
- package/omp/commands/flow-widget.md +8 -0
- package/omp/commands/goal-draft.md +8 -0
- package/omp/commands/goal-outcome.md +8 -0
- package/omp/commands/implement-issue.md +8 -0
- package/omp/commands/prepare-skills.md +6 -0
- package/omp/commands/scout-recommendations.md +8 -0
- package/omp/commands/setup-devboard.md +24 -0
- package/omp/config.yml +12 -0
- package/omp/extensions/implement-goal-issue.ts +2 -0
- package/omp/extensions/implement-next-epic.ts +2 -0
- package/omp/mcp.json +9 -0
- package/package.json +86 -7
- package/prompts/adr-resolve.md +130 -0
- package/prompts/discover.md +541 -0
- package/prompts/epic-next.md +533 -0
- package/prompts/flow-change.md +76 -0
- package/prompts/flow-goal.md +126 -0
- package/prompts/flow-implement.md +103 -0
- package/prompts/flow-widget.md +54 -0
- package/prompts/goal-draft.md +147 -0
- package/prompts/goal-outcome.md +87 -0
- package/prompts/implement-issue.md +66 -0
- package/prompts/prepare-skills.md +142 -0
- package/prompts/scout-recommendations.md +143 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/favicon.svg +6 -0
- package/server/acp.ts +540 -0
- package/server/activity.test.ts +96 -0
- package/server/activity.ts +48 -0
- package/server/app.test.ts +882 -0
- package/server/app.ts +6179 -0
- package/server/ask.test.ts +543 -0
- package/server/ask.ts +589 -0
- package/server/dependencies.test.ts +118 -0
- package/server/discover-remote.test.ts +1602 -0
- package/server/discover-sheet.test.ts +176 -0
- package/server/discover.test.ts +1510 -0
- package/server/discover.ts +3198 -0
- package/server/feedback.test.ts +458 -0
- package/server/feedback.ts +369 -0
- package/server/fixtures/adr-source.ts +71 -0
- package/server/fixtures/hang-download.ts +14 -0
- package/server/fixtures/stub-agent.ts +1870 -0
- package/server/fixtures/stub-forge.ts +141 -0
- package/server/fixtures/stub-gh.ts +57 -0
- package/server/flow-pr.test.ts +976 -0
- package/server/flow.test.ts +1632 -0
- package/server/flow.ts +2655 -0
- package/server/forge-pr.ts +387 -0
- package/server/goal-creation.test.ts +957 -0
- package/server/goal-draft.test.ts +348 -0
- package/server/goal-drafts.ts +449 -0
- package/server/goal-proposal.test.ts +479 -0
- package/server/goal-proposal.ts +232 -0
- package/server/goal-revision.test.ts +613 -0
- package/server/goal-slug.test.ts +56 -0
- package/server/goal-slug.ts +96 -0
- package/server/goal.ts +20 -0
- package/server/index.ts +147 -0
- package/server/issue-close.test.ts +100 -0
- package/server/issue-close.ts +178 -0
- package/server/issues.test.ts +167 -0
- package/server/issues.ts +479 -0
- package/server/journal.test.ts +350 -0
- package/server/journal.ts +329 -0
- package/server/lifecycle-pause.test.ts +1795 -0
- package/server/lifecycle.test.ts +1879 -0
- package/server/parallel-lifecycle.test.ts +1029 -0
- package/server/persistence-restart.test.ts +723 -0
- package/server/persistence.test.ts +1935 -0
- package/server/records.test.ts +180 -0
- package/server/records.ts +99 -0
- package/server/reset-implementation.test.ts +997 -0
- package/server/run.test.ts +611 -0
- package/server/scout.test.ts +877 -0
- package/server/scout.ts +459 -0
- package/server/skills-preparation.test.ts +1073 -0
- package/server/state.ts +249 -0
- package/server/tracker.ts +369 -0
- package/server/updates.test.ts +444 -0
- package/server/updates.ts +198 -0
- package/server/version.test.ts +140 -0
- package/server/version.ts +151 -0
- package/server/watchdog.ts +389 -0
- package/svelte.config.js +5 -0
- package/templates/host-AGENTS.md +235 -0
- package/tsconfig.json +21 -0
- package/vite.config.ts +21 -0
- package/watchdog/cleanup-main.ts +98 -0
- package/watchdog/cleanup.ts +301 -0
- package/watchdog/collect.ts +311 -0
- package/watchdog/coordinator.ts +397 -0
- package/watchdog/delivery.ts +107 -0
- package/watchdog/discovery.ts +209 -0
- package/watchdog/findings.ts +211 -0
- package/watchdog/github.ts +189 -0
- package/watchdog/history.ts +138 -0
- package/watchdog/install.ts +103 -0
- package/watchdog/ledger.ts +188 -0
- package/watchdog/lock.ts +63 -0
- package/watchdog/main.ts +106 -0
- package/watchdog/status.ts +71 -0
- package/watchdog/systemd.ts +96 -0
- package/watchdog/uninstall.ts +30 -0
- package/watchdog/units/devboard-watchdog.service +9 -0
- package/watchdog/units/devboard-watchdog.timer +10 -0
|
@@ -0,0 +1,1042 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { connection } from "../connection.svelte";
|
|
3
|
+
import { goal } from "../goal.svelte";
|
|
4
|
+
import ChevronRight from "lucide-svelte/icons/chevron-right";
|
|
5
|
+
import Trash2 from "lucide-svelte/icons/trash-2";
|
|
6
|
+
import type { WorkspaceTab } from "../layout.svelte";
|
|
7
|
+
import { recordStatusTone } from "../record-status";
|
|
8
|
+
import { renderSkillDocument } from "../skill-document";
|
|
9
|
+
import { statusTone } from "../status-tone";
|
|
10
|
+
import type { AdrRecord, Lane } from "../types";
|
|
11
|
+
import AdrTable from "./AdrTable.svelte";
|
|
12
|
+
import ConfirmDialog from "./ConfirmDialog.svelte";
|
|
13
|
+
import MonacoEditor from "./MonacoEditor.svelte";
|
|
14
|
+
import Terminal from "./Terminal.svelte";
|
|
15
|
+
import Button from "./ui/button.svelte";
|
|
16
|
+
import Input from "./ui/input.svelte";
|
|
17
|
+
import PageHeader from "./ui/page-header.svelte";
|
|
18
|
+
import StageTrack from "./ui/stage-track.svelte";
|
|
19
|
+
|
|
20
|
+
interface Props {
|
|
21
|
+
tab: WorkspaceTab | null;
|
|
22
|
+
openTab: (tab: WorkspaceTab) => void;
|
|
23
|
+
closeTab: (tab: WorkspaceTab) => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let { tab, openTab, closeTab }: Props = $props();
|
|
27
|
+
|
|
28
|
+
type FilterMode = "all" | "pending" | "resolved";
|
|
29
|
+
let filterMode = $state<FilterMode>("all");
|
|
30
|
+
let filterQuery = $state("");
|
|
31
|
+
let skillsList = $state<HTMLUListElement | undefined>();
|
|
32
|
+
let deleteTarget = $state<string | null>(null);
|
|
33
|
+
|
|
34
|
+
function skillPanelId(name: string): string {
|
|
35
|
+
return `skill-content-${name.replace(/[^A-Za-z0-9_-]/g, "-")}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const createGoalTitle = $derived(
|
|
39
|
+
goal.creationActive
|
|
40
|
+
? "Restart goal creation."
|
|
41
|
+
: goal.goalReady
|
|
42
|
+
? "Start a new goal creation and replace the current goal."
|
|
43
|
+
: goal.running || goal.lifecycleActive || goal.revisionActive
|
|
44
|
+
? "Stop the active run and start goal creation."
|
|
45
|
+
: "Start goal creation.",
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const runStartTitle = $derived(
|
|
49
|
+
goal.lifecycleActive
|
|
50
|
+
? "Interrupt the lifecycle and resolve the ADRs manually."
|
|
51
|
+
: undefined,
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const resolvedCount = $derived(goal.records.filter((record) => !record.pending).length);
|
|
55
|
+
const pendingCount = $derived(goal.pendingCount);
|
|
56
|
+
const progressLine = $derived(
|
|
57
|
+
`${resolvedCount} of ${goal.records.length} resolved · ${pendingCount} ADR${
|
|
58
|
+
pendingCount === 1 ? "" : "s"
|
|
59
|
+
} pending`,
|
|
60
|
+
);
|
|
61
|
+
const nextRecord = $derived(
|
|
62
|
+
goal.next === null ? null : (goal.records.find((record) => record.file === goal.next) ?? null),
|
|
63
|
+
);
|
|
64
|
+
const query = $derived(filterQuery.trim().toLowerCase());
|
|
65
|
+
const filteredRecords = $derived(
|
|
66
|
+
goal.records.filter((record) => {
|
|
67
|
+
if (filterMode === "pending" && !record.pending) return false;
|
|
68
|
+
if (filterMode === "resolved" && record.pending) return false;
|
|
69
|
+
if (query.length === 0) return true;
|
|
70
|
+
return [record.title, record.file, record.summary, record.status]
|
|
71
|
+
.join(" ")
|
|
72
|
+
.toLowerCase()
|
|
73
|
+
.includes(query);
|
|
74
|
+
}),
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
const lane = $derived(tab?.kind === "lane" ? goal.lane(tab.resourceId) : null);
|
|
78
|
+
const record = $derived(
|
|
79
|
+
tab?.kind === "adr" ? (goal.records.find((entry) => entry.file === tab?.resourceId) ?? null) : null,
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const filters: Array<{ id: FilterMode; label: string }> = [
|
|
83
|
+
{ id: "all", label: "All" },
|
|
84
|
+
{ id: "pending", label: "Pending" },
|
|
85
|
+
{ id: "resolved", label: "Resolved" },
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
const operationText = $derived(
|
|
89
|
+
goal.creationActive
|
|
90
|
+
? "Creating the goal"
|
|
91
|
+
: goal.revisionActive
|
|
92
|
+
? "Revising the goal"
|
|
93
|
+
: goal.skillsPrepActive
|
|
94
|
+
? "Preparing skills"
|
|
95
|
+
: goal.running
|
|
96
|
+
? `Manual run: ${goal.runTitle ?? "ADR"}`
|
|
97
|
+
: null,
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const documentState = $derived(
|
|
101
|
+
goal.goalEditingDisabled
|
|
102
|
+
? "Read only while work runs"
|
|
103
|
+
: goal.goalDirty
|
|
104
|
+
? "Unsaved changes"
|
|
105
|
+
: goal.goalReady
|
|
106
|
+
? "Saved"
|
|
107
|
+
: "Not written yet",
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const runActionLabel = $derived(
|
|
111
|
+
goal.runAction === "implementing"
|
|
112
|
+
? "implementing"
|
|
113
|
+
: goal.runAction === "generating"
|
|
114
|
+
? "generating an epic"
|
|
115
|
+
: "resolving",
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
function openAdr(entry: AdrRecord): void {
|
|
119
|
+
openTab({ id: `adr:${entry.file}`, kind: "adr", resourceId: entry.file, title: entry.title });
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function openLane(entry: Lane): void {
|
|
123
|
+
openTab({
|
|
124
|
+
id: `lane:${entry.id}`,
|
|
125
|
+
kind: "lane",
|
|
126
|
+
resourceId: entry.id,
|
|
127
|
+
title: goal.laneLabel(entry),
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function openOverview(): void {
|
|
132
|
+
openTab({ id: "overview", kind: "overview", resourceId: "overview", title: "Implementation" });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function lanePanel(entry: Lane) {
|
|
136
|
+
const activity = entry.activity;
|
|
137
|
+
return {
|
|
138
|
+
current: activity.length > 0 ? [activity[activity.length - 1] ?? ""] : [],
|
|
139
|
+
history: activity.slice(0, -1),
|
|
140
|
+
identity: goal.laneIdentity(entry),
|
|
141
|
+
label: goal.laneLabel(entry),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function submitAnswer(value: string): void {
|
|
146
|
+
goal.submitAnswer(value);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function clearFilters(): void {
|
|
150
|
+
filterMode = "all";
|
|
151
|
+
filterQuery = "";
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
$effect(() => {
|
|
155
|
+
if (tab?.kind !== "skills" || goal.selectedSkill === null) return;
|
|
156
|
+
const selected = goal.selectedSkill;
|
|
157
|
+
const item = [...(skillsList?.querySelectorAll<HTMLElement>("[data-name]") ?? [])].find(
|
|
158
|
+
(entry) => entry.dataset.name === selected,
|
|
159
|
+
);
|
|
160
|
+
item?.scrollIntoView({ block: "nearest" });
|
|
161
|
+
});
|
|
162
|
+
</script>
|
|
163
|
+
|
|
164
|
+
<div class="mode-body">
|
|
165
|
+
{#snippet runQuestion()}
|
|
166
|
+
{#if goal.question !== null}
|
|
167
|
+
{@const pending = goal.question}
|
|
168
|
+
<section class="panel goal-question" data-testid="question">
|
|
169
|
+
<div class="panel__header">
|
|
170
|
+
<h2 class="panel__title">Waiting on you</h2>
|
|
171
|
+
<span class="badge" data-tone="accent">run blocked</span>
|
|
172
|
+
</div>
|
|
173
|
+
<div class="panel__body stack-sm">
|
|
174
|
+
<p class="muted" style="margin: 0;">{pending.message}</p>
|
|
175
|
+
{#if pending.options.length > 0}
|
|
176
|
+
<div class="choice-list">
|
|
177
|
+
{#each pending.options as option (option.label)}
|
|
178
|
+
<button
|
|
179
|
+
type="button"
|
|
180
|
+
class="choice"
|
|
181
|
+
data-testid="choice"
|
|
182
|
+
data-command="goal.answer.submit"
|
|
183
|
+
onclick={() => submitAnswer(option.label)}
|
|
184
|
+
>
|
|
185
|
+
<span class="choice__label">
|
|
186
|
+
{option.label}
|
|
187
|
+
{#if option.recommended}
|
|
188
|
+
<span class="choice__recommended" data-testid="recommended">Recommended</span>
|
|
189
|
+
{/if}
|
|
190
|
+
</span>
|
|
191
|
+
{#if option.description}
|
|
192
|
+
<span class="choice__description">{option.description}</span>
|
|
193
|
+
{/if}
|
|
194
|
+
</button>
|
|
195
|
+
{/each}
|
|
196
|
+
</div>
|
|
197
|
+
{/if}
|
|
198
|
+
<form
|
|
199
|
+
class="answer-form"
|
|
200
|
+
onsubmit={(event) => {
|
|
201
|
+
event.preventDefault();
|
|
202
|
+
goal.submitAnswer(goal.draft);
|
|
203
|
+
}}
|
|
204
|
+
>
|
|
205
|
+
<input
|
|
206
|
+
id="goal-answer-input"
|
|
207
|
+
class="goal-answer-input"
|
|
208
|
+
style="min-height: 28px; padding: 4px 8px;"
|
|
209
|
+
type="text"
|
|
210
|
+
bind:value={goal.draft}
|
|
211
|
+
placeholder="Your answer"
|
|
212
|
+
aria-label="Your answer"
|
|
213
|
+
data-testid="answer-input"
|
|
214
|
+
/>
|
|
215
|
+
<Button
|
|
216
|
+
variant="default"
|
|
217
|
+
type="submit"
|
|
218
|
+
data-command="goal.answer.submit"
|
|
219
|
+
data-testid="submit-answer"
|
|
220
|
+
disabled={goal.draft.trim().length === 0}
|
|
221
|
+
>
|
|
222
|
+
Submit answer
|
|
223
|
+
</Button>
|
|
224
|
+
</form>
|
|
225
|
+
</div>
|
|
226
|
+
</section>
|
|
227
|
+
{/if}
|
|
228
|
+
{/snippet}
|
|
229
|
+
|
|
230
|
+
{#if tab !== null && tab.kind !== "overview" && goal.question !== null}
|
|
231
|
+
<div class="goal-global-question">{@render runQuestion()}</div>
|
|
232
|
+
{/if}
|
|
233
|
+
|
|
234
|
+
{#if tab === null}
|
|
235
|
+
<div class="goal-page">
|
|
236
|
+
<section class="panel goal-empty-state" data-testid="no-open-page">
|
|
237
|
+
<div class="panel__body stack-sm">
|
|
238
|
+
<h1 class="goal-empty-state__title">No page is open</h1>
|
|
239
|
+
<p class="empty" style="padding: 0;">
|
|
240
|
+
Choose a surface from the sidebar, or return to the lifecycle console.
|
|
241
|
+
</p>
|
|
242
|
+
<div class="row-sm">
|
|
243
|
+
<Button
|
|
244
|
+
variant="default"
|
|
245
|
+
data-command="open.goal.overview"
|
|
246
|
+
onclick={openOverview}
|
|
247
|
+
>
|
|
248
|
+
Open implementation
|
|
249
|
+
</Button>
|
|
250
|
+
</div>
|
|
251
|
+
</div>
|
|
252
|
+
</section>
|
|
253
|
+
</div>
|
|
254
|
+
{:else if tab.kind === "overview"}
|
|
255
|
+
<div class="goal-page goal-page--console" data-testid="goal-overview">
|
|
256
|
+
{#if connection.lastError !== null || connection.upgradePending || operationText !== null || goal.runState === "stopped"}
|
|
257
|
+
<div class="goal-notices" data-testid="goal-notices">
|
|
258
|
+
{#if connection.lastError !== null}
|
|
259
|
+
<p class="message" data-severity="error" role="alert">{connection.lastError}</p>
|
|
260
|
+
{/if}
|
|
261
|
+
{#if goal.creationActive}
|
|
262
|
+
<p class="message" data-testid="goal-creation-active" data-severity="notice" role="status">
|
|
263
|
+
Creating the goal. The document is being written by the agent.
|
|
264
|
+
</p>
|
|
265
|
+
{/if}
|
|
266
|
+
{#if goal.revisionActive}
|
|
267
|
+
<p class="message" data-testid="goal-revision-active" data-severity="notice" role="status">
|
|
268
|
+
Revising the goal with your guidance.
|
|
269
|
+
</p>
|
|
270
|
+
{/if}
|
|
271
|
+
{#if goal.skillsPrepActive}
|
|
272
|
+
<p class="message" data-testid="skills-preparation-active" data-severity="notice" role="status">
|
|
273
|
+
Preparing skills from the goal.
|
|
274
|
+
</p>
|
|
275
|
+
{/if}
|
|
276
|
+
{#if goal.runState === "stopped" && !goal.lifecycleActive}
|
|
277
|
+
<p class="message" data-testid="run-stopped" data-severity="notice" role="status">
|
|
278
|
+
Run stopped. The last action is kept below.
|
|
279
|
+
</p>
|
|
280
|
+
{/if}
|
|
281
|
+
{#if connection.upgradePending}
|
|
282
|
+
<p class="message" data-testid="upgrade-pending" data-severity="notice" role="status">
|
|
283
|
+
An update is waiting for the current work to finish. This dashboard restarts when it
|
|
284
|
+
ends.
|
|
285
|
+
</p>
|
|
286
|
+
{/if}
|
|
287
|
+
</div>
|
|
288
|
+
{/if}
|
|
289
|
+
|
|
290
|
+
<PageHeader
|
|
291
|
+
title="Implementation"
|
|
292
|
+
subtitle="Drive the lifecycle, or resolve a single decision record by hand."
|
|
293
|
+
testid="implementation-header"
|
|
294
|
+
>
|
|
295
|
+
{#snippet meta()}
|
|
296
|
+
<span class="muted" data-testid="pending-summary">
|
|
297
|
+
{pendingCount} ADR{pendingCount === 1 ? "" : "s"} pending
|
|
298
|
+
</span>
|
|
299
|
+
{#if nextRecord !== null && pendingCount > 0}
|
|
300
|
+
<span class="muted">Next: <span class="mono">{nextRecord.title}</span></span>
|
|
301
|
+
{/if}
|
|
302
|
+
{/snippet}
|
|
303
|
+
{#snippet actions()}
|
|
304
|
+
{#if goal.lifecycleActive}
|
|
305
|
+
<Button
|
|
306
|
+
variant="secondary"
|
|
307
|
+
data-command={goal.lifecycle.paused ? "goal.lifecycle.resume" : "goal.lifecycle.pause"}
|
|
308
|
+
onclick={() => (goal.lifecycle.paused ? goal.resumeLifecycle() : goal.pauseLifecycle())}
|
|
309
|
+
>
|
|
310
|
+
{goal.lifecycle.paused ? "Resume" : "Pause"}
|
|
311
|
+
</Button>
|
|
312
|
+
{/if}
|
|
313
|
+
<Button
|
|
314
|
+
variant="outline"
|
|
315
|
+
data-command="goal.adr.start-next"
|
|
316
|
+
data-testid="start-next"
|
|
317
|
+
title={runStartTitle}
|
|
318
|
+
disabled={goal.runStartDisabled || goal.next === null}
|
|
319
|
+
onclick={() => goal.startNext()}
|
|
320
|
+
>
|
|
321
|
+
Resolve next ADR
|
|
322
|
+
</Button>
|
|
323
|
+
<Button
|
|
324
|
+
variant="outline"
|
|
325
|
+
data-command="goal.reset"
|
|
326
|
+
data-testid="reset-implementation"
|
|
327
|
+
disabled={goal.resetDisabled}
|
|
328
|
+
onclick={() => goal.armReset()}
|
|
329
|
+
>
|
|
330
|
+
Reset implementation
|
|
331
|
+
</Button>
|
|
332
|
+
<Button
|
|
333
|
+
variant={goal.lifecycleActive ? "destructive" : "default"}
|
|
334
|
+
data-command="goal.lifecycle.toggle"
|
|
335
|
+
data-testid="lifecycle-control"
|
|
336
|
+
title={goal.lifecycleButtonTitle}
|
|
337
|
+
disabled={goal.lifecycleButtonDisabled}
|
|
338
|
+
onclick={() => goal.toggleLifecycle()}
|
|
339
|
+
>
|
|
340
|
+
{goal.lifecycleActive ? "Stop implementation" : "Start implementation"}
|
|
341
|
+
</Button>
|
|
342
|
+
{/snippet}
|
|
343
|
+
</PageHeader>
|
|
344
|
+
|
|
345
|
+
{#if goal.resetting || goal.resetStatus !== null}
|
|
346
|
+
<p
|
|
347
|
+
class="message"
|
|
348
|
+
data-severity="warning"
|
|
349
|
+
data-testid={goal.resetting ? "reset-running" : "reset-status"}
|
|
350
|
+
role="status"
|
|
351
|
+
>
|
|
352
|
+
{goal.resetting ? "Resetting implementation…" : goal.resetStatus}
|
|
353
|
+
</p>
|
|
354
|
+
{/if}
|
|
355
|
+
|
|
356
|
+
{@render runQuestion()}
|
|
357
|
+
|
|
358
|
+
<section class="panel" data-testid="lifecycle">
|
|
359
|
+
<div class="panel__header">
|
|
360
|
+
<h2 class="panel__title">Lifecycle</h2>
|
|
361
|
+
</div>
|
|
362
|
+
<div class="panel__body">
|
|
363
|
+
<StageTrack
|
|
364
|
+
stage={goal.lifecycle.stage}
|
|
365
|
+
pass={goal.lifecycle.pass}
|
|
366
|
+
paused={goal.lifecycle.paused}
|
|
367
|
+
seen={goal.lifecycleSeen || goal.lifecycleActive}
|
|
368
|
+
workLabel={goal.running && !goal.lifecycleActive ? goal.runTitle : goal.lifecycleWorkTitle}
|
|
369
|
+
workUrl={goal.running && !goal.lifecycleActive ? null : goal.lifecycleWorkUrl}
|
|
370
|
+
manual={goal.running && !goal.lifecycleActive}
|
|
371
|
+
testid="lifecycle-stages"
|
|
372
|
+
/>
|
|
373
|
+
</div>
|
|
374
|
+
</section>
|
|
375
|
+
|
|
376
|
+
{#if goal.running && !goal.lifecycleActive}
|
|
377
|
+
<section class="panel" data-testid="now">
|
|
378
|
+
<div class="panel__body row-sm">
|
|
379
|
+
<span class="muted">Manual run</span>
|
|
380
|
+
<strong>{goal.runTitle ?? "ADR"}</strong>
|
|
381
|
+
<span class="muted">{runActionLabel}</span>
|
|
382
|
+
</div>
|
|
383
|
+
</section>
|
|
384
|
+
{/if}
|
|
385
|
+
|
|
386
|
+
<section
|
|
387
|
+
class="panel goal-activity"
|
|
388
|
+
class:goal-activity--idle={goal.activity.length === 0 && !goal.activityActive}
|
|
389
|
+
data-testid="activity"
|
|
390
|
+
>
|
|
391
|
+
<div class="panel__header">
|
|
392
|
+
<h2 class="panel__title">Activity</h2>
|
|
393
|
+
<span class="badge" data-tone={statusTone(goal.activityActive ? "running" : "idle")}>
|
|
394
|
+
{goal.activityActive ? "live" : "idle"}
|
|
395
|
+
</span>
|
|
396
|
+
</div>
|
|
397
|
+
{#if goal.activity.length === 0 && !goal.activityActive}
|
|
398
|
+
<div class="panel__body" data-testid="activity-current-empty">
|
|
399
|
+
<p class="empty" style="padding: 0;">
|
|
400
|
+
The next action appears here. Start the lifecycle or resolve a record.
|
|
401
|
+
</p>
|
|
402
|
+
</div>
|
|
403
|
+
{:else}
|
|
404
|
+
<div class="panel__body term-split">
|
|
405
|
+
<Terminal
|
|
406
|
+
lines={[goal.currentAction]}
|
|
407
|
+
data-testid="activity-current"
|
|
408
|
+
label="Current activity"
|
|
409
|
+
/>
|
|
410
|
+
{#if goal.previousActions.length > 0}
|
|
411
|
+
<details class="activity-history">
|
|
412
|
+
<summary data-testid="activity-history">{goal.previousActionsLabel}</summary>
|
|
413
|
+
<div style="height: 140px;">
|
|
414
|
+
<Terminal
|
|
415
|
+
lines={goal.previousActions}
|
|
416
|
+
data-testid="activity-log"
|
|
417
|
+
label="Activity history"
|
|
418
|
+
/>
|
|
419
|
+
</div>
|
|
420
|
+
</details>
|
|
421
|
+
{/if}
|
|
422
|
+
</div>
|
|
423
|
+
{/if}
|
|
424
|
+
</section>
|
|
425
|
+
|
|
426
|
+
</div>
|
|
427
|
+
{:else if tab.kind === "adrs"}
|
|
428
|
+
<div class="goal-page">
|
|
429
|
+
<PageHeader
|
|
430
|
+
title="Decision records"
|
|
431
|
+
testid="adrs-header"
|
|
432
|
+
>
|
|
433
|
+
{#snippet meta()}
|
|
434
|
+
<span class="muted" data-testid="pending-summary">{progressLine}</span>
|
|
435
|
+
{/snippet}
|
|
436
|
+
</PageHeader>
|
|
437
|
+
|
|
438
|
+
{#if goal.records.length === 0}
|
|
439
|
+
<section class="panel" data-testid="adrs-overlay">
|
|
440
|
+
<div class="panel__body stack-sm goal-empty-state">
|
|
441
|
+
<h2 class="goal-empty-state__title">No decision records yet</h2>
|
|
442
|
+
<p class="empty" style="padding: 0;">
|
|
443
|
+
Records appear under <code>adrs/</code> when goal creation or the lifecycle writes them.
|
|
444
|
+
</p>
|
|
445
|
+
<div class="row-sm">
|
|
446
|
+
<Button variant="default" data-command="open.goal.overview" onclick={openOverview}>
|
|
447
|
+
Open implementation
|
|
448
|
+
</Button>
|
|
449
|
+
</div>
|
|
450
|
+
</div>
|
|
451
|
+
</section>
|
|
452
|
+
{:else}
|
|
453
|
+
<section class="panel goal-filters" aria-label="Record filters">
|
|
454
|
+
<div class="panel__body goal-filters__body">
|
|
455
|
+
<Input
|
|
456
|
+
type="search"
|
|
457
|
+
placeholder="Filter by title, file, or summary"
|
|
458
|
+
aria-label="Filter decision records"
|
|
459
|
+
data-testid="adr-search"
|
|
460
|
+
value={filterQuery}
|
|
461
|
+
oninput={(event) => (filterQuery = event.currentTarget.value)}
|
|
462
|
+
/>
|
|
463
|
+
<div class="filter-chips" role="group" aria-label="Implementation state">
|
|
464
|
+
{#each filters as entry (entry.id)}
|
|
465
|
+
<button
|
|
466
|
+
type="button"
|
|
467
|
+
class="filter-chip"
|
|
468
|
+
aria-pressed={filterMode === entry.id}
|
|
469
|
+
data-testid={`adr-filter-${entry.id}`}
|
|
470
|
+
onclick={() => (filterMode = entry.id)}
|
|
471
|
+
>
|
|
472
|
+
{entry.label}
|
|
473
|
+
</button>
|
|
474
|
+
{/each}
|
|
475
|
+
{#if filterMode !== "all" || filterQuery.trim().length > 0}
|
|
476
|
+
<Button variant="ghost" size="sm" data-testid="clear-adr-filter" onclick={clearFilters}>
|
|
477
|
+
Clear
|
|
478
|
+
</Button>
|
|
479
|
+
{/if}
|
|
480
|
+
</div>
|
|
481
|
+
</div>
|
|
482
|
+
</section>
|
|
483
|
+
|
|
484
|
+
<section class="panel goal-adrs-panel" data-testid="adrs-overlay">
|
|
485
|
+
<AdrTable
|
|
486
|
+
records={filteredRecords}
|
|
487
|
+
onopen={openAdr}
|
|
488
|
+
onstart={(entry) => goal.startRecord(entry)}
|
|
489
|
+
/>
|
|
490
|
+
</section>
|
|
491
|
+
{/if}
|
|
492
|
+
</div>
|
|
493
|
+
{:else if tab.kind === "goal-document"}
|
|
494
|
+
<div class="goal-page goal-page--fill">
|
|
495
|
+
<PageHeader title="Goal document" subtitle={documentState} testid="document-header">
|
|
496
|
+
{#snippet meta()}
|
|
497
|
+
{#if operationText !== null}
|
|
498
|
+
<span class="badge" data-tone="accent">{operationText}</span>
|
|
499
|
+
{/if}
|
|
500
|
+
{/snippet}
|
|
501
|
+
{#snippet actions()}
|
|
502
|
+
{#if goal.skills.length > 0}
|
|
503
|
+
<Button
|
|
504
|
+
variant="outline"
|
|
505
|
+
data-command="goal.skills.open"
|
|
506
|
+
data-testid="show-skills"
|
|
507
|
+
onclick={() =>
|
|
508
|
+
openTab({ id: "skills", kind: "skills", resourceId: "skills", title: "Skills" })}
|
|
509
|
+
>
|
|
510
|
+
Show skills ({goal.skills.length})
|
|
511
|
+
</Button>
|
|
512
|
+
{:else}
|
|
513
|
+
<Button
|
|
514
|
+
variant="outline"
|
|
515
|
+
data-command="goal.skills.prepare"
|
|
516
|
+
data-testid="prepare-skills"
|
|
517
|
+
disabled={goal.prepareDisabled}
|
|
518
|
+
onclick={() => goal.prepareSkills()}
|
|
519
|
+
>
|
|
520
|
+
Prepare skills
|
|
521
|
+
</Button>
|
|
522
|
+
{/if}
|
|
523
|
+
<Button
|
|
524
|
+
variant="secondary"
|
|
525
|
+
data-command="goal.create"
|
|
526
|
+
data-testid="create-goal"
|
|
527
|
+
title={createGoalTitle}
|
|
528
|
+
disabled={!connection.open}
|
|
529
|
+
onclick={() => goal.createGoal()}
|
|
530
|
+
>
|
|
531
|
+
{goal.goalReady ? "Recreate goal" : "Create goal"}
|
|
532
|
+
</Button>
|
|
533
|
+
<Button
|
|
534
|
+
variant="default"
|
|
535
|
+
data-command="goal.save"
|
|
536
|
+
data-testid="save-goal"
|
|
537
|
+
title={goal.goalEditingDisabled ? "The goal is read only during work." : undefined}
|
|
538
|
+
disabled={goal.goalEditingDisabled}
|
|
539
|
+
onclick={() => goal.saveGoal()}
|
|
540
|
+
>
|
|
541
|
+
Save goal
|
|
542
|
+
</Button>
|
|
543
|
+
{/snippet}
|
|
544
|
+
</PageHeader>
|
|
545
|
+
|
|
546
|
+
<section class="panel goal-document" data-testid="goal-editor">
|
|
547
|
+
<div class="panel__body goal-details">
|
|
548
|
+
<MonacoEditor
|
|
549
|
+
value={goal.goalDraft}
|
|
550
|
+
label="Current goal"
|
|
551
|
+
data-testid="goal-text"
|
|
552
|
+
readOnly={goal.goalEditingDisabled}
|
|
553
|
+
dirty={goal.goalDirty}
|
|
554
|
+
onchange={(text) => goal.setGoalDraft(text)}
|
|
555
|
+
onsave={() => goal.saveGoal()}
|
|
556
|
+
/>
|
|
557
|
+
</div>
|
|
558
|
+
<div class="panel__footer goal-revision">
|
|
559
|
+
<div class="goal-revision__heading">
|
|
560
|
+
<label class="field__label" for="revision-request">Revision guidance</label>
|
|
561
|
+
<Button
|
|
562
|
+
variant="secondary"
|
|
563
|
+
data-command="goal.revise"
|
|
564
|
+
data-testid="revise-goal"
|
|
565
|
+
disabled={goal.revisionDisabled}
|
|
566
|
+
onclick={() => goal.reviseGoal()}
|
|
567
|
+
>
|
|
568
|
+
Revise goal
|
|
569
|
+
</Button>
|
|
570
|
+
</div>
|
|
571
|
+
<p class="goal-revision__hint">
|
|
572
|
+
Revision writes a draft; save only when you accept it. Guidance is optional.
|
|
573
|
+
</p>
|
|
574
|
+
<textarea
|
|
575
|
+
id="revision-request"
|
|
576
|
+
class="textarea goal-revision__input"
|
|
577
|
+
rows="3"
|
|
578
|
+
bind:value={goal.revisionRequest}
|
|
579
|
+
placeholder="Optional guidance for the agent"
|
|
580
|
+
data-testid="revision-request"
|
|
581
|
+
disabled={goal.revisionDisabled}
|
|
582
|
+
></textarea>
|
|
583
|
+
</div>
|
|
584
|
+
</section>
|
|
585
|
+
</div>
|
|
586
|
+
{:else if tab.kind === "skills"}
|
|
587
|
+
<div class="goal-page goal-page--fill">
|
|
588
|
+
<PageHeader title="Skills" testid="skills-header">
|
|
589
|
+
{#snippet meta()}
|
|
590
|
+
{#if goal.contradictions.length > 0}
|
|
591
|
+
<span class="badge" data-tone="error">
|
|
592
|
+
{goal.contradictions.length} contradiction{goal.contradictions.length === 1 ? "" : "s"}
|
|
593
|
+
</span>
|
|
594
|
+
{/if}
|
|
595
|
+
{/snippet}
|
|
596
|
+
{#snippet actions()}
|
|
597
|
+
<Button
|
|
598
|
+
variant="secondary"
|
|
599
|
+
data-command="goal.skills.prepare"
|
|
600
|
+
data-testid="prepare-skills-overlay"
|
|
601
|
+
disabled={goal.prepareDisabled}
|
|
602
|
+
onclick={() => goal.prepareSkills()}
|
|
603
|
+
>
|
|
604
|
+
Prepare skills again
|
|
605
|
+
</Button>
|
|
606
|
+
<Button
|
|
607
|
+
variant="outline"
|
|
608
|
+
data-command={`tab.close.goal.${tab.id}`}
|
|
609
|
+
data-testid="close-skills"
|
|
610
|
+
onclick={() => closeTab(tab)}
|
|
611
|
+
>
|
|
612
|
+
Close
|
|
613
|
+
</Button>
|
|
614
|
+
{/snippet}
|
|
615
|
+
</PageHeader>
|
|
616
|
+
|
|
617
|
+
<section class="panel goal-skills" data-testid="skills-overlay">
|
|
618
|
+
<div class="panel__header">
|
|
619
|
+
<h2 class="panel__title">
|
|
620
|
+
Prepared skills <span class="mono" data-testid="skills-count">{goal.skills.length}</span>
|
|
621
|
+
</h2>
|
|
622
|
+
<span class="muted">Read by implementing agents</span>
|
|
623
|
+
</div>
|
|
624
|
+
<div class="panel__body stack-sm">
|
|
625
|
+
{#if goal.skills.length === 0}
|
|
626
|
+
<p class="empty" style="padding: 0;">
|
|
627
|
+
No skills prepared yet. Prepare them from the saved goal before starting implementation.
|
|
628
|
+
</p>
|
|
629
|
+
{:else}
|
|
630
|
+
<ul class="history-list" bind:this={skillsList}>
|
|
631
|
+
{#each goal.skills as skill (skill.name)}
|
|
632
|
+
{@const expanded = goal.selectedSkill === skill.name}
|
|
633
|
+
{@const panelId = skillPanelId(skill.name)}
|
|
634
|
+
{@const skillHtml =
|
|
635
|
+
skill.content === undefined ? "" : renderSkillDocument(skill.content)}
|
|
636
|
+
<li class="skill">
|
|
637
|
+
<div class="skill__row">
|
|
638
|
+
<button
|
|
639
|
+
type="button"
|
|
640
|
+
class="skill__button"
|
|
641
|
+
data-testid="skill"
|
|
642
|
+
data-name={skill.name}
|
|
643
|
+
data-selected={expanded}
|
|
644
|
+
aria-current={expanded ? "true" : undefined}
|
|
645
|
+
aria-expanded={expanded}
|
|
646
|
+
aria-controls={expanded ? panelId : undefined}
|
|
647
|
+
onclick={() => goal.selectSkill(expanded ? null : skill.name)}
|
|
648
|
+
>
|
|
649
|
+
<span class="skill__text">
|
|
650
|
+
<span class="mono skill__name">{skill.name}</span>
|
|
651
|
+
<span class="muted skill__description">{skill.description}</span>
|
|
652
|
+
</span>
|
|
653
|
+
<ChevronRight
|
|
654
|
+
size={14}
|
|
655
|
+
aria-hidden="true"
|
|
656
|
+
class={expanded ? "skill__chevron skill__chevron--open" : "skill__chevron"}
|
|
657
|
+
/>
|
|
658
|
+
</button>
|
|
659
|
+
{#if (skill.family ?? "prepare") === "prepare"}
|
|
660
|
+
<button
|
|
661
|
+
type="button"
|
|
662
|
+
class="skill__delete"
|
|
663
|
+
data-command={`goal.skills.delete.${skill.name}`}
|
|
664
|
+
data-testid="delete-skill"
|
|
665
|
+
data-name={skill.name}
|
|
666
|
+
aria-label={`Delete ${skill.name}`}
|
|
667
|
+
title={`Delete ${skill.name} from .omp/skills/`}
|
|
668
|
+
disabled={goal.skillDeleteDisabled}
|
|
669
|
+
onclick={() => (deleteTarget = skill.name)}
|
|
670
|
+
>
|
|
671
|
+
<Trash2 size={14} aria-hidden="true" />
|
|
672
|
+
</button>
|
|
673
|
+
{/if}
|
|
674
|
+
</div>
|
|
675
|
+
{#if expanded}
|
|
676
|
+
<div
|
|
677
|
+
class="skill__detail"
|
|
678
|
+
id={panelId}
|
|
679
|
+
data-testid="skill-content"
|
|
680
|
+
data-name={skill.name}
|
|
681
|
+
role="region"
|
|
682
|
+
aria-label={`${skill.name} content`}
|
|
683
|
+
>
|
|
684
|
+
<p class="skill__path" title={`.omp/skills/${skill.name}/SKILL.md`}>
|
|
685
|
+
.omp/skills/{skill.name}/SKILL.md
|
|
686
|
+
</p>
|
|
687
|
+
{#if skill.content !== undefined && skill.content.length > 0}
|
|
688
|
+
{#if skillHtml.length > 0}
|
|
689
|
+
<div class="skill__document" data-testid="skill-content-text">
|
|
690
|
+
{@html skillHtml}
|
|
691
|
+
</div>
|
|
692
|
+
{:else}
|
|
693
|
+
<p class="empty" style="padding: 0;">
|
|
694
|
+
This skill's file carries only preparation metadata; there are no
|
|
695
|
+
instructions to read.
|
|
696
|
+
</p>
|
|
697
|
+
{/if}
|
|
698
|
+
{:else}
|
|
699
|
+
<p class="empty" style="padding: 0;">
|
|
700
|
+
This skill has no file on disk; prepare the skills again to write it.
|
|
701
|
+
</p>
|
|
702
|
+
{/if}
|
|
703
|
+
</div>
|
|
704
|
+
{/if}
|
|
705
|
+
</li>
|
|
706
|
+
{/each}
|
|
707
|
+
</ul>
|
|
708
|
+
{/if}
|
|
709
|
+
|
|
710
|
+
{#if goal.contradictions.length > 0}
|
|
711
|
+
<ul class="history-list goal-contradictions" data-testid="skills-contradictions">
|
|
712
|
+
{#each goal.contradictions as contradiction (`${contradiction.skills[0]}:${contradiction.skills[1]}`)}
|
|
713
|
+
<li class="message" data-severity="warning">
|
|
714
|
+
<strong>{contradiction.skills[0]}</strong> and
|
|
715
|
+
<strong>{contradiction.skills[1]}</strong> state opposite policies:
|
|
716
|
+
“{contradiction.directives[0]}” vs “{contradiction.directives[1]}”.
|
|
717
|
+
</li>
|
|
718
|
+
{/each}
|
|
719
|
+
</ul>
|
|
720
|
+
{/if}
|
|
721
|
+
</div>
|
|
722
|
+
</section>
|
|
723
|
+
|
|
724
|
+
|
|
725
|
+
</div>
|
|
726
|
+
{:else if tab.kind === "adr"}
|
|
727
|
+
{#if record === null}
|
|
728
|
+
<div class="goal-page">
|
|
729
|
+
<section class="panel">
|
|
730
|
+
<div class="panel__body stack-sm">
|
|
731
|
+
<p class="empty" style="padding: 0;">This record is no longer listed.</p>
|
|
732
|
+
<div class="row-sm">
|
|
733
|
+
<Button
|
|
734
|
+
variant="default"
|
|
735
|
+
data-command="goal.adrs.open"
|
|
736
|
+
onclick={() =>
|
|
737
|
+
openTab({ id: "adrs", kind: "adrs", resourceId: "adrs", title: "ADRs" })}
|
|
738
|
+
>
|
|
739
|
+
Back to records
|
|
740
|
+
</Button>
|
|
741
|
+
</div>
|
|
742
|
+
</div>
|
|
743
|
+
</section>
|
|
744
|
+
</div>
|
|
745
|
+
{:else}
|
|
746
|
+
<div class="goal-page">
|
|
747
|
+
<PageHeader eyebrow="Decision record" title={record.title} testid="record-header">
|
|
748
|
+
{#snippet meta()}
|
|
749
|
+
{#if goal.next === record.file}
|
|
750
|
+
<span class="badge" data-tone="accent" data-testid="next-marker">Next</span>
|
|
751
|
+
{/if}
|
|
752
|
+
<span
|
|
753
|
+
class="badge"
|
|
754
|
+
data-tone={recordStatusTone(record.status)}
|
|
755
|
+
data-testid="record-status"
|
|
756
|
+
>
|
|
757
|
+
{record.status || "Unknown"}
|
|
758
|
+
</span>
|
|
759
|
+
{/snippet}
|
|
760
|
+
{#snippet actions()}
|
|
761
|
+
<Button
|
|
762
|
+
variant="outline"
|
|
763
|
+
data-command="goal.adrs.open"
|
|
764
|
+
data-testid="back-to-records"
|
|
765
|
+
onclick={() =>
|
|
766
|
+
openTab({ id: "adrs", kind: "adrs", resourceId: "adrs", title: "ADRs" })}
|
|
767
|
+
>
|
|
768
|
+
Back to records
|
|
769
|
+
</Button>
|
|
770
|
+
<Button
|
|
771
|
+
variant="outline"
|
|
772
|
+
data-command={`tab.close.goal.${tab.id}`}
|
|
773
|
+
data-testid="close-record"
|
|
774
|
+
onclick={() => closeTab(tab)}
|
|
775
|
+
>
|
|
776
|
+
Close
|
|
777
|
+
</Button>
|
|
778
|
+
{/snippet}
|
|
779
|
+
</PageHeader>
|
|
780
|
+
|
|
781
|
+
<section class="panel" data-testid="record-meta">
|
|
782
|
+
<div class="panel__body">
|
|
783
|
+
<dl class="kv-grid">
|
|
784
|
+
<dt>File</dt>
|
|
785
|
+
<dd><code data-testid="record-file">{record.file}</code></dd>
|
|
786
|
+
<dt>Document status</dt>
|
|
787
|
+
<dd>{record.status || "Unknown"}</dd>
|
|
788
|
+
<dt>Implementation</dt>
|
|
789
|
+
<dd>{record.pending ? "Pending" : "Resolved"}</dd>
|
|
790
|
+
</dl>
|
|
791
|
+
</div>
|
|
792
|
+
</section>
|
|
793
|
+
|
|
794
|
+
<section class="panel">
|
|
795
|
+
<div class="panel__header">
|
|
796
|
+
<h2 class="panel__title">Decision summary</h2>
|
|
797
|
+
</div>
|
|
798
|
+
<div class="panel__body">
|
|
799
|
+
<p data-testid="record-summary" style="margin: 0;">
|
|
800
|
+
{record.summary || "No decision summary recorded."}
|
|
801
|
+
</p>
|
|
802
|
+
</div>
|
|
803
|
+
</section>
|
|
804
|
+
|
|
805
|
+
{#if record.pending}
|
|
806
|
+
<section class="panel" data-testid="record-action">
|
|
807
|
+
<div class="panel__body row-sm">
|
|
808
|
+
<Button
|
|
809
|
+
variant="default"
|
|
810
|
+
data-command={`goal.adr.start.${record.file}`}
|
|
811
|
+
data-testid="start"
|
|
812
|
+
data-file={record.file}
|
|
813
|
+
title={runStartTitle}
|
|
814
|
+
disabled={goal.runStartDisabled}
|
|
815
|
+
onclick={() => goal.startRecord(record)}
|
|
816
|
+
>
|
|
817
|
+
Start this record
|
|
818
|
+
</Button>
|
|
819
|
+
<span class="muted">
|
|
820
|
+
Runs one record outside the lifecycle. Stop the lifecycle first when it is active.
|
|
821
|
+
</span>
|
|
822
|
+
</div>
|
|
823
|
+
</section>
|
|
824
|
+
{/if}
|
|
825
|
+
</div>
|
|
826
|
+
{/if}
|
|
827
|
+
{:else if tab.kind === "lane"}
|
|
828
|
+
{#if lane === null}
|
|
829
|
+
<div class="goal-page">
|
|
830
|
+
<section class="panel">
|
|
831
|
+
<div class="panel__body stack-sm">
|
|
832
|
+
<p class="empty" style="padding: 0;">This lane is no longer active.</p>
|
|
833
|
+
<div class="row-sm">
|
|
834
|
+
<Button variant="default" data-command="open.goal.overview" onclick={openOverview}>
|
|
835
|
+
Back to implementation
|
|
836
|
+
</Button>
|
|
837
|
+
</div>
|
|
838
|
+
</div>
|
|
839
|
+
</section>
|
|
840
|
+
</div>
|
|
841
|
+
{:else}
|
|
842
|
+
{@const panel = lanePanel(lane)}
|
|
843
|
+
<div class="goal-page">
|
|
844
|
+
<PageHeader eyebrow="Lane" title={panel.label} testid="lane-header">
|
|
845
|
+
{#snippet meta()}
|
|
846
|
+
<span
|
|
847
|
+
class="badge"
|
|
848
|
+
data-testid="lane-status"
|
|
849
|
+
data-kind={lane.kind}
|
|
850
|
+
data-status={lane.status}
|
|
851
|
+
data-tone={statusTone(lane.status)}
|
|
852
|
+
>
|
|
853
|
+
{lane.status}
|
|
854
|
+
</span>
|
|
855
|
+
<span class="muted">Attempt <span class="mono">{lane.attempt}</span></span>
|
|
856
|
+
{/snippet}
|
|
857
|
+
{#snippet actions()}
|
|
858
|
+
<Button
|
|
859
|
+
variant="outline"
|
|
860
|
+
data-command={`tab.close.goal.${tab.id}`}
|
|
861
|
+
data-testid="close-lane"
|
|
862
|
+
onclick={() => closeTab(tab)}
|
|
863
|
+
>
|
|
864
|
+
Close
|
|
865
|
+
</Button>
|
|
866
|
+
{/snippet}
|
|
867
|
+
</PageHeader>
|
|
868
|
+
|
|
869
|
+
<section class="panel" data-testid="lane-panel" data-lane={lane.id}>
|
|
870
|
+
<div class="panel__header">
|
|
871
|
+
<p class="panel__title" data-testid="lane-identity">
|
|
872
|
+
{#if panel.identity.text.length > 0}
|
|
873
|
+
{panel.identity.text}{#if panel.identity.url !== null}{" — "}{/if}
|
|
874
|
+
{/if}
|
|
875
|
+
{#if panel.identity.url !== null}
|
|
876
|
+
<a
|
|
877
|
+
href={panel.identity.url}
|
|
878
|
+
target="_blank"
|
|
879
|
+
rel="noopener noreferrer"
|
|
880
|
+
data-testid="lane-identity-link">{panel.identity.url}</a>
|
|
881
|
+
{/if}
|
|
882
|
+
</p>
|
|
883
|
+
</div>
|
|
884
|
+
<div class="panel__body">
|
|
885
|
+
<dl class="kv-grid">
|
|
886
|
+
<dt>Kind</dt>
|
|
887
|
+
<dd>
|
|
888
|
+
{lane.kind === "generation"
|
|
889
|
+
? "Epic generation"
|
|
890
|
+
: lane.kind === "adr"
|
|
891
|
+
? "ADR resolution"
|
|
892
|
+
: "Epic implementation"}
|
|
893
|
+
</dd>
|
|
894
|
+
<dt>Record</dt>
|
|
895
|
+
<dd>
|
|
896
|
+
{#if lane.record === null}
|
|
897
|
+
<span class="muted">—</span>
|
|
898
|
+
{:else}
|
|
899
|
+
<code>{lane.record.file}</code>
|
|
900
|
+
{/if}
|
|
901
|
+
</dd>
|
|
902
|
+
<dt>Epic</dt>
|
|
903
|
+
<dd>
|
|
904
|
+
{#if lane.epic === null}
|
|
905
|
+
<span class="muted">—</span>
|
|
906
|
+
{:else}
|
|
907
|
+
#{lane.epic.number} {lane.epic.title}
|
|
908
|
+
{/if}
|
|
909
|
+
</dd>
|
|
910
|
+
</dl>
|
|
911
|
+
</div>
|
|
912
|
+
</section>
|
|
913
|
+
|
|
914
|
+
{#if lane.question !== null}
|
|
915
|
+
{@const pending = lane.question}
|
|
916
|
+
<section class="panel goal-question" data-testid="lane-question" data-lane={lane.id}>
|
|
917
|
+
<div class="panel__header">
|
|
918
|
+
<h2 class="panel__title">Waiting on you</h2>
|
|
919
|
+
<span class="badge" data-tone="accent">lane blocked</span>
|
|
920
|
+
</div>
|
|
921
|
+
<div class="panel__body stack-sm">
|
|
922
|
+
<p class="muted" style="margin: 0;">{pending.message}</p>
|
|
923
|
+
{#if pending.options.length > 0}
|
|
924
|
+
<div class="choice-list">
|
|
925
|
+
{#each pending.options as option (option.label)}
|
|
926
|
+
<button
|
|
927
|
+
type="button"
|
|
928
|
+
class="choice"
|
|
929
|
+
data-testid="lane-choice"
|
|
930
|
+
data-command={`goal.lane.answer.submit.${lane.id}`}
|
|
931
|
+
onclick={() => goal.submitLaneAnswer(lane.id, option.label)}
|
|
932
|
+
>
|
|
933
|
+
<span class="choice__label">
|
|
934
|
+
{option.label}
|
|
935
|
+
{#if option.recommended}
|
|
936
|
+
<span class="choice__recommended">Recommended</span>
|
|
937
|
+
{/if}
|
|
938
|
+
</span>
|
|
939
|
+
{#if option.description}
|
|
940
|
+
<span class="choice__description">{option.description}</span>
|
|
941
|
+
{/if}
|
|
942
|
+
</button>
|
|
943
|
+
{/each}
|
|
944
|
+
</div>
|
|
945
|
+
{/if}
|
|
946
|
+
<form
|
|
947
|
+
class="answer-form"
|
|
948
|
+
onsubmit={(event) => {
|
|
949
|
+
event.preventDefault();
|
|
950
|
+
goal.submitLaneAnswer(lane.id, goal.laneDraft);
|
|
951
|
+
}}
|
|
952
|
+
>
|
|
953
|
+
<input
|
|
954
|
+
id={`lane-answer-${lane.id}`}
|
|
955
|
+
class="goal-answer-input"
|
|
956
|
+
style="min-height: 28px; padding: 4px 8px;"
|
|
957
|
+
type="text"
|
|
958
|
+
bind:value={goal.laneDraft}
|
|
959
|
+
placeholder="Your answer"
|
|
960
|
+
aria-label="Your answer"
|
|
961
|
+
data-testid="lane-answer-input"
|
|
962
|
+
/>
|
|
963
|
+
<Button
|
|
964
|
+
variant="default"
|
|
965
|
+
type="submit"
|
|
966
|
+
data-command={`goal.lane.answer.submit.${lane.id}`}
|
|
967
|
+
data-testid="lane-submit-answer"
|
|
968
|
+
disabled={goal.laneDraft.trim().length === 0}
|
|
969
|
+
>
|
|
970
|
+
Submit answer
|
|
971
|
+
</Button>
|
|
972
|
+
</form>
|
|
973
|
+
</div>
|
|
974
|
+
</section>
|
|
975
|
+
{/if}
|
|
976
|
+
|
|
977
|
+
<section class="panel goal-activity" data-testid="lane-activity">
|
|
978
|
+
<div class="panel__header">
|
|
979
|
+
<h2 class="panel__title">Activity</h2>
|
|
980
|
+
<span class="badge" data-tone={statusTone(lane.status)}>
|
|
981
|
+
{lane.status === "running" || lane.status === "retrying" ? "live" : lane.status}
|
|
982
|
+
</span>
|
|
983
|
+
</div>
|
|
984
|
+
<div class="panel__body term-split">
|
|
985
|
+
<Terminal
|
|
986
|
+
lines={panel.current.length === 0 ? ["No activity yet."] : panel.current}
|
|
987
|
+
data-testid="lane-current"
|
|
988
|
+
label="Lane current activity"
|
|
989
|
+
/>
|
|
990
|
+
{#if panel.history.length > 0}
|
|
991
|
+
<details class="activity-history">
|
|
992
|
+
<summary data-testid="lane-history">
|
|
993
|
+
{panel.history.length === 1
|
|
994
|
+
? "1 previous action"
|
|
995
|
+
: `${panel.history.length} previous actions`}
|
|
996
|
+
</summary>
|
|
997
|
+
<div style="height: 140px;">
|
|
998
|
+
<Terminal
|
|
999
|
+
lines={panel.history}
|
|
1000
|
+
data-testid="lane-history-log"
|
|
1001
|
+
label="Lane activity history"
|
|
1002
|
+
/>
|
|
1003
|
+
</div>
|
|
1004
|
+
</details>
|
|
1005
|
+
{/if}
|
|
1006
|
+
</div>
|
|
1007
|
+
</section>
|
|
1008
|
+
</div>
|
|
1009
|
+
{/if}
|
|
1010
|
+
{/if}
|
|
1011
|
+
</div>
|
|
1012
|
+
|
|
1013
|
+
<ConfirmDialog
|
|
1014
|
+
open={goal.resetArmed}
|
|
1015
|
+
title="Reset implementation"
|
|
1016
|
+
message="Reset deletes every top-level entry except .devboard, .omp, AGENTS.md, .gitignore, and GOAL.md. The internal .git and .worktree directories are preserved. Any active run, lifecycle, goal creation, revision, or skills preparation stops first."
|
|
1017
|
+
confirmLabel="Confirm reset"
|
|
1018
|
+
destructive
|
|
1019
|
+
testid="reset-confirmation"
|
|
1020
|
+
confirmCommand="goal.reset.confirm"
|
|
1021
|
+
cancelCommand="goal.reset.cancel"
|
|
1022
|
+
onconfirm={() => goal.confirmReset()}
|
|
1023
|
+
oncancel={() => goal.cancelReset()}
|
|
1024
|
+
/>
|
|
1025
|
+
|
|
1026
|
+
<ConfirmDialog
|
|
1027
|
+
open={deleteTarget !== null}
|
|
1028
|
+
title="Delete skill"
|
|
1029
|
+
message={deleteTarget === null
|
|
1030
|
+
? ""
|
|
1031
|
+
: `Delete "${deleteTarget}" from .omp/skills/? Implementing agents will no longer read it.`}
|
|
1032
|
+
confirmLabel="Delete skill"
|
|
1033
|
+
destructive
|
|
1034
|
+
testid="delete-skill-confirmation"
|
|
1035
|
+
confirmCommand="goal.skills.delete.confirm"
|
|
1036
|
+
cancelCommand="goal.skills.delete.cancel"
|
|
1037
|
+
onconfirm={() => {
|
|
1038
|
+
if (deleteTarget !== null) goal.deleteSkill(deleteTarget);
|
|
1039
|
+
deleteTarget = null;
|
|
1040
|
+
}}
|
|
1041
|
+
oncancel={() => (deleteTarget = null)}
|
|
1042
|
+
/>
|