@jakkrichm/create-nexus-devflow 2.0.25 → 2.0.27
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/README.md +1 -1
- package/dist/bin/create-nexus-devflow.d.ts +18 -1
- package/dist/bin/create-nexus-devflow.js +439 -11
- package/dist/bin/create-nexus-devflow.js.map +1 -1
- package/dist/lib/command-catalog.d.ts +11 -0
- package/dist/lib/command-catalog.js +63 -0
- package/dist/lib/command-catalog.js.map +1 -0
- package/dist/lib/current-work.js +94 -48
- package/dist/lib/current-work.js.map +1 -1
- package/dist/lib/dashboard-page.d.ts +2 -0
- package/dist/lib/dashboard-page.js +201 -0
- package/dist/lib/dashboard-page.js.map +1 -0
- package/dist/lib/dashboard-snapshot.d.ts +49 -0
- package/dist/lib/dashboard-snapshot.js +96 -0
- package/dist/lib/dashboard-snapshot.js.map +1 -0
- package/dist/lib/dashboard.d.ts +2 -0
- package/dist/lib/dashboard.js +360 -8
- package/dist/lib/dashboard.js.map +1 -1
- package/dist/lib/discoveries.d.ts +19 -0
- package/dist/lib/discoveries.js +81 -0
- package/dist/lib/discoveries.js.map +1 -0
- package/dist/lib/doctor.d.ts +26 -0
- package/dist/lib/doctor.js +474 -0
- package/dist/lib/doctor.js.map +1 -0
- package/dist/lib/findings.d.ts +26 -2
- package/dist/lib/findings.js +165 -1
- package/dist/lib/findings.js.map +1 -1
- package/dist/lib/gatekeeper.d.ts +19 -0
- package/dist/lib/gatekeeper.js +77 -0
- package/dist/lib/gatekeeper.js.map +1 -0
- package/dist/lib/git-hooks.d.ts +16 -0
- package/dist/lib/git-hooks.js +78 -0
- package/dist/lib/git-hooks.js.map +1 -0
- package/dist/lib/history.d.ts +6 -1
- package/dist/lib/history.js +98 -11
- package/dist/lib/history.js.map +1 -1
- package/dist/lib/ideas.d.ts +26 -0
- package/dist/lib/ideas.js +179 -0
- package/dist/lib/ideas.js.map +1 -0
- package/dist/lib/status.d.ts +2 -0
- package/dist/lib/status.js +60 -6
- package/dist/lib/status.js.map +1 -1
- package/dist/lib/update.js +2 -2
- package/dist/lib/update.js.map +1 -1
- package/dist/lib/version-check.d.ts +22 -0
- package/dist/lib/version-check.js +59 -0
- package/dist/lib/version-check.js.map +1 -0
- package/dist/lib/workflow-state.d.ts +23 -0
- package/dist/lib/workflow-state.js +112 -0
- package/dist/lib/workflow-state.js.map +1 -0
- package/dist/scripts/prepare-template.js +3 -3
- package/package.json +1 -1
- package/template/{.claude/skills/00-discover → .agents/skills/00-explore}/SKILL.md +10 -10
- package/template/.agents/skills/10-define/SKILL.md +4 -4
- package/template/.agents/skills/20-spec/SKILL.md +2 -3
- package/template/.agents/skills/30-plan/SKILL.md +2 -3
- package/template/.agents/skills/40-execute/SKILL.md +2 -2
- package/template/.agents/skills/50-verify/SKILL.md +2 -2
- package/template/.agents/skills/60-report/SKILL.md +3 -3
- package/template/{.claude/skills/70-release → .agents/skills/70-deliver}/SKILL.md +7 -13
- package/template/.agents/skills/adopt/SKILL.md +136 -73
- package/template/.agents/skills/audit/SKILL.md +275 -0
- package/template/.agents/skills/autopilot/SKILL.md +228 -147
- package/template/.agents/skills/brainstorm/SKILL.md +3 -3
- package/template/.agents/skills/brief/SKILL.md +74 -51
- package/template/.agents/skills/check/SKILL.md +96 -76
- package/template/.agents/skills/ci/SKILL.md +140 -61
- package/template/.agents/skills/complete/SKILL.md +156 -101
- package/template/.agents/skills/convert-any-to-md/SKILL.md +2 -2
- package/template/.agents/skills/debug/SKILL.md +124 -49
- package/template/.agents/skills/devflow/SKILL.md +16 -10
- package/template/.agents/skills/discovery/SKILL.md +166 -0
- package/template/.agents/skills/doctor/SKILL.md +188 -72
- package/template/.agents/skills/feature/SKILL.md +195 -102
- package/template/.agents/skills/fix/SKILL.md +41 -90
- package/template/.agents/skills/idea/SKILL.md +3 -3
- package/template/.agents/skills/implement/SKILL.md +189 -46
- package/template/.agents/skills/onboard/SKILL.md +215 -85
- package/template/.agents/skills/overview/SKILL.md +51 -85
- package/template/.agents/skills/prototype/SKILL.md +82 -27
- package/template/.agents/skills/release/SKILL.md +160 -0
- package/template/.agents/skills/report-html/SKILL.md +2 -2
- package/template/.agents/skills/rollback/SKILL.md +123 -77
- package/template/.agents/skills/status/SKILL.md +109 -0
- package/template/.agents/skills/test/SKILL.md +2 -2
- package/template/.agents/skills/tests/SKILL.md +126 -0
- package/template/.agents/skills/try/SKILL.md +77 -65
- package/template/{.agents/skills/00-discover → .claude/skills/00-explore}/SKILL.md +9 -9
- package/template/.claude/skills/10-define/SKILL.md +2 -2
- package/template/.claude/skills/20-spec/SKILL.md +1 -1
- package/template/.claude/skills/30-plan/SKILL.md +1 -1
- package/template/.claude/skills/60-report/SKILL.md +1 -1
- package/template/{.agents/skills/70-release → .claude/skills/70-deliver}/SKILL.md +6 -12
- package/template/.claude/skills/adopt/SKILL.md +2 -2
- package/template/.claude/skills/audit/SKILL.md +141 -0
- package/template/.claude/skills/autopilot/SKILL.md +74 -52
- package/template/.claude/skills/brief/SKILL.md +67 -45
- package/template/.claude/skills/ci/SKILL.md +1 -1
- package/template/.claude/skills/complete/SKILL.md +1 -1
- package/template/.claude/skills/debug/SKILL.md +1 -1
- package/template/.claude/skills/devflow/SKILL.md +7 -7
- package/template/.claude/skills/discovery/SKILL.md +145 -0
- package/template/.claude/skills/doctor/SKILL.md +1 -1
- package/template/.claude/skills/feature/SKILL.md +67 -18
- package/template/.claude/skills/fix/SKILL.md +1 -1
- package/template/.claude/skills/idea/SKILL.md +2 -2
- package/template/.claude/skills/onboard/SKILL.md +3 -3
- package/template/.claude/skills/overview/SKILL.md +39 -88
- package/template/.claude/skills/prototype/SKILL.md +1 -1
- package/template/.claude/skills/release/SKILL.md +171 -0
- package/template/.claude/skills/rollback/SKILL.md +2 -2
- package/template/AGENTS.md +5 -5
- package/template/devflow/build-plan.md +40 -0
- package/template/devflow/context/ai-interaction.md +4 -4
- package/template/devflow/context/coding-standards.md +3 -3
- package/template/devflow/context/current-stage.md +1 -1
- package/template/devflow/context/findings.md +1 -1
- package/template/devflow/history/HISTORY.md +2 -2
- package/template/devflow/project-plan.md +27 -0
- package/template/devflow/reference/mockup.html +476 -0
- package/template/devflow/reference/project-overview-template.md +39 -0
- package/template/devflow/reference/running-id-contract.md +24 -2
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
const DASHBOARD_PAGE_HTML = `<!doctype html>
|
|
2
|
+
<html lang="th">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Nexus-DevFlow Blueprint Dashboard</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600;700&family=Noto+Sans+Thai:wght@400;500;600;700&family=Space+Grotesk:wght@500;600;700&display=swap" rel="stylesheet">
|
|
10
|
+
<style>
|
|
11
|
+
:root{
|
|
12
|
+
--font-display:"Google Sans Thai","Google Sans","Noto Sans Thai","Space Grotesk",sans-serif;
|
|
13
|
+
--font-sans:"Google Sans Thai","Google Sans","Noto Sans Thai",sans-serif;
|
|
14
|
+
--font-mono:"IBM Plex Mono",monospace;
|
|
15
|
+
--bg:#0a2540;--bg-deep:#061a2e;--panel:rgba(255,255,255,.04);--panel-strong:#0c2b49;
|
|
16
|
+
--ink:#edf6ff;--soft:#b7d3ef;--muted:#7598ba;--line:rgba(191,224,255,.17);--line-strong:rgba(191,224,255,.36);
|
|
17
|
+
--cyan:#7dd8ff;--mint:#6fe3b4;--gold:#f2c14e;--red:#ff7061;--violet:#b8a4ff;
|
|
18
|
+
--mint-soft:rgba(111,227,180,.14);--gold-soft:rgba(242,193,78,.14);--red-soft:rgba(255,112,97,.14);--violet-soft:rgba(184,164,255,.14);
|
|
19
|
+
}
|
|
20
|
+
*{box-sizing:border-box}
|
|
21
|
+
html{color-scheme:dark}
|
|
22
|
+
body{margin:0;min-height:100vh;background-color:var(--bg);color:var(--ink);font-family:var(--font-sans);-webkit-font-smoothing:antialiased;background-image:linear-gradient(rgba(125,216,255,.065) 1px,transparent 1px),linear-gradient(90deg,rgba(125,216,255,.065) 1px,transparent 1px),radial-gradient(circle at 8% -5%,rgba(125,216,255,.16),transparent 42rem);background-size:34px 34px,34px 34px,auto}
|
|
23
|
+
button{font:inherit}
|
|
24
|
+
code{font-family:var(--font-mono);color:var(--cyan)}
|
|
25
|
+
.sheet{width:min(1180px,calc(100% - 40px));margin:0 auto;padding:34px 0 64px}
|
|
26
|
+
.titleblock{display:flex;align-items:flex-end;justify-content:space-between;gap:26px;padding-bottom:20px;border-bottom:1px solid var(--line-strong);flex-wrap:wrap}
|
|
27
|
+
.eyebrow,.label{font:600 10px/1 var(--font-mono);letter-spacing:.13em;text-transform:uppercase;color:var(--cyan)}
|
|
28
|
+
.eyebrow{display:flex;align-items:center;gap:8px;margin-bottom:12px}.eyebrow:before{content:"";width:20px;height:1px;background:var(--cyan)}
|
|
29
|
+
h1{margin:0 0 6px;font:700 clamp(32px,4vw,48px)/1.05 var(--font-display);letter-spacing:-.03em}
|
|
30
|
+
.path,.muted{color:var(--muted);font-size:12px;line-height:1.65}.path{font-family:var(--font-mono)}
|
|
31
|
+
.badges{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}.badge,.pill{padding:4px 9px;border:1px solid var(--line-strong);border-radius:3px;background:rgba(255,255,255,.025);color:var(--muted);font:600 10px/1.5 var(--font-mono);text-transform:uppercase}
|
|
32
|
+
.ok,.healthy,.current,.ready,.idle{border-color:rgba(111,227,180,.42)!important;color:var(--mint)!important;background:var(--mint-soft)!important}
|
|
33
|
+
.warning,.available,.offline,.blocked,.fail{border-color:rgba(255,112,97,.42)!important;color:var(--red)!important;background:var(--red-soft)!important}
|
|
34
|
+
.active,.deep,.fast,.configured{border-color:rgba(184,164,255,.42)!important;color:var(--violet)!important;background:var(--violet-soft)!important}
|
|
35
|
+
.meta{display:grid;grid-template-columns:auto auto;gap:4px 20px;margin:0;font:11px/1.55 var(--font-mono)}.meta dt{color:var(--muted);text-transform:uppercase}.meta dd{margin:0;color:var(--soft);text-align:right}
|
|
36
|
+
.live{display:flex;align-items:center;gap:8px;margin:14px 0 24px;font:11px var(--font-mono);color:var(--muted)}.live-dot{width:7px;height:7px;border-radius:50%;background:var(--gold)}.live-dot.on{background:var(--mint);box-shadow:0 0 0 4px rgba(111,227,180,.12)}
|
|
37
|
+
.panel,.card{position:relative;min-width:0;border:1px solid var(--line);border-radius:4px;background:var(--panel)}
|
|
38
|
+
#dual-track{overflow:hidden}
|
|
39
|
+
.stepper{justify-content:center}.step{display:flex;flex-direction:column;align-items:center;text-align:center}.step-icon{display:grid;place-items:center;width:30px;height:30px;margin:10px auto 8px;border:1px solid currentColor;border-radius:8px;background:rgba(255,255,255,.035)}.icon-glyph{display:block;font-size:17px;line-height:1}.step.flow-0,.step.flow-4{color:var(--cyan)}.step.flow-1,.step.flow-5{color:var(--violet)}.step.flow-2,.step.flow-6{color:var(--gold)}.step.flow-3,.step.flow-7{color:var(--mint)}.step .step-name{color:currentColor}.step .step-cmd{color:var(--muted)}
|
|
40
|
+
.panel{padding:24px 26px;margin-bottom:20px}.panel-head,.card-head{display:flex;align-items:center;justify-content:space-between;gap:14px;flex-wrap:wrap}.panel-head{margin-bottom:20px}.card-head{margin-bottom:17px}
|
|
41
|
+
.track-tabs{display:flex;gap:7px}.tab{display:inline-flex;align-items:center;gap:7px;padding:7px 13px;border:1px solid var(--line-strong);border-radius:99px;background:transparent;color:var(--muted);font:600 10px var(--font-mono);cursor:pointer}.tab:hover,.tab:focus-visible{border-color:var(--cyan);color:var(--cyan);outline:none}.tab.on{background:var(--cyan);border-color:var(--cyan);color:var(--bg-deep)}.tab-active-dot{display:none;padding:2px 6px;border-radius:99px;font:700 8px/1 var(--font-mono);letter-spacing:.05em;text-transform:uppercase;background:var(--mint);color:var(--bg-deep)}.tab.is-active-track .tab-active-dot{display:inline-flex;align-items:center;gap:3px;box-shadow:0 0 0 2px rgba(111,227,180,.25)}.tab.on.is-active-track .tab-active-dot{background:var(--bg-deep);color:var(--mint)}
|
|
42
|
+
.track-view{display:none;min-width:0}.track-view.on{display:block}.stepper{display:flex;width:100%;max-width:100%;overflow-x:auto;padding:4px 0 7px}.step{flex:1 1 0;min-width:104px;position:relative;padding-right:12px}.step-line{position:absolute;top:10px;left:21px;right:0;height:1px;background:var(--line-strong)}.step:last-child .step-line{display:none}.step.done .step-line{background:var(--mint)}
|
|
43
|
+
.step-dot{position:relative;z-index:1;width:21px;height:21px;border-radius:50%;display:grid;place-items:center;border:2px solid var(--line-strong);background:var(--bg-deep);color:var(--muted);font:700 8px var(--font-mono)}.step.done .step-dot{border-color:var(--mint);color:var(--mint)}.step.active .step-dot{border-color:var(--gold);color:var(--gold);box-shadow:0 0 0 4px rgba(242,193,78,.16)}
|
|
44
|
+
.step-name{margin-top:9px;color:var(--soft);font:600 10px/1.35 var(--font-mono)}.step.active .step-name{color:var(--gold)}.step.done .step-name{color:var(--mint)}.step-cmd{margin-top:3px;color:var(--muted);font:9px/1.4 var(--font-mono)}.track-note{margin-top:12px;padding-top:12px;border-top:1px dashed var(--line);color:var(--muted);font:11px/1.65 var(--font-mono)}
|
|
45
|
+
.next-action{background:var(--bg-deep);border-color:var(--line-strong)}.next-head{display:flex;justify-content:space-between;gap:12px;align-items:center}.command{margin:8px 0 6px;color:var(--gold);font:700 clamp(22px,3vw,30px)/1.3 var(--font-mono)}.copy{padding:7px 12px;border:1px solid var(--cyan);border-radius:3px;background:transparent;color:var(--cyan);font:600 10px var(--font-mono);cursor:pointer}.copy:hover,.copy:focus-visible{background:var(--cyan);color:var(--bg-deep);outline:none}
|
|
46
|
+
.stats{display:grid;grid-template-columns:repeat(5,1fr);gap:1px;margin:0 0 20px;border:1px solid var(--line);border-radius:4px;overflow:hidden;background:var(--line)}.stat{padding:16px 18px;background:var(--bg-deep)}.stat-value{font:700 25px/1 var(--font-display)}.stat-label{margin-top:6px;color:var(--muted);font:10px var(--font-mono);text-transform:uppercase;letter-spacing:.08em}
|
|
47
|
+
.grid{display:grid;grid-template-columns:repeat(12,1fr);gap:20px;margin-bottom:20px}.card{grid-column:span 4;min-width:0;padding:clamp(20px,2.4vw,28px)}.card.wide{grid-column:span 8}.card.full{grid-column:1/-1}.value{font:700 18px/1.4 var(--font-display)}
|
|
48
|
+
.facts{display:grid;gap:10px}.fact{display:flex;justify-content:space-between;gap:16px;padding-bottom:10px;border-bottom:1px dashed var(--line)}.fact:last-child{padding-bottom:0;border:0}.fact span:first-child{color:var(--muted);font-size:11px}.fact span:last-child{color:var(--soft);font:11px var(--font-mono);text-align:right;overflow-wrap:anywhere}
|
|
49
|
+
.progress{height:6px;margin:14px 0 9px;border-radius:99px;background:rgba(255,255,255,.07);overflow:hidden}.progress span{display:block;height:100%;width:0;background:var(--gold);transition:width .35s ease}
|
|
50
|
+
ul{list-style:none;margin:0;padding:0}li{padding:10px 0;border-bottom:1px dashed var(--line);color:var(--soft);font-size:12px;line-height:1.55}li:last-child{border:0}.empty{color:var(--muted)}
|
|
51
|
+
.severity{display:grid;grid-template-columns:repeat(4,1fr);gap:8px}.sev{padding:9px 6px;border:1px solid var(--line);border-radius:3px;text-align:center}.sev b{display:block;font:700 17px var(--font-display)}.sev small{color:var(--muted);font:9px var(--font-mono)}
|
|
52
|
+
.commands{display:flex;flex-wrap:wrap;gap:10px}.cmd{position:relative;padding:8px 13px;border:1px solid var(--line-strong);border-radius:3px;background:rgba(255,255,255,.025);color:var(--soft);font:11px var(--font-mono);cursor:pointer}.cmd:hover,.cmd:focus-visible,.cmd:focus{border-color:var(--cyan);color:var(--cyan);outline:2px solid rgba(125,216,255,.2);outline-offset:2px}.cmd:after{content:attr(data-tip);position:absolute;z-index:30;left:50%;bottom:calc(100% + 10px);width:max-content;max-width:min(300px,80vw);padding:9px 11px;border:1px solid var(--line-strong);border-radius:3px;background:#031421;color:var(--soft);font:10px/1.5 var(--font-mono);white-space:normal;text-align:left;opacity:0;visibility:hidden;transform:translate(-50%,5px);box-shadow:0 12px 30px rgba(0,0,0,.4);transition:.15s;pointer-events:none}.cmd:hover:after,.cmd:focus-visible:after,.cmd:focus:after{opacity:1;visibility:visible;transform:translate(-50%,0)}
|
|
53
|
+
.adapters{display:grid;grid-template-columns:repeat(4,1fr);gap:12px}.adapter{padding:15px;border:1px solid var(--line);border-radius:3px;background:rgba(255,255,255,.02)}.adapter strong{display:block;margin-bottom:6px;text-transform:capitalize}.adapter-path{color:var(--muted);font:10px var(--font-mono)}.adapter-state{margin-top:9px;font:10px var(--font-mono)}
|
|
54
|
+
.idea-meta,.history-meta{margin-top:4px;color:var(--muted);font:10px var(--font-mono)}
|
|
55
|
+
.section-note{margin:-2px 0 14px;color:var(--muted);font:10px/1.5 var(--font-mono)}.record-list{display:grid;gap:8px}.record-item{display:grid;grid-template-columns:38px minmax(0,1fr) auto;gap:13px;align-items:center;padding:13px 15px;border:1px solid var(--line);border-left:3px solid var(--record-color,var(--cyan));border-radius:3px;background:rgba(255,255,255,.02)}.record-icon{display:grid;place-items:center;width:31px;height:31px;border-radius:8px;background:color-mix(in srgb,var(--record-color,var(--cyan)) 14%,transparent)}.record-body{min-width:0}.record-meta{color:var(--record-color,var(--cyan));font:700 10px var(--font-mono);letter-spacing:.06em;text-transform:uppercase}.record-title{margin-top:3px;color:var(--ink);font-weight:650}.record-desc{margin-top:3px;color:var(--soft);font-size:12px}.record-desc:empty{display:none}.record-title strong,.record-desc strong{color:var(--record-color,var(--cyan));font-weight:750}.record-title em,.record-desc em{color:var(--gold)}.record-title code,.record-desc code{padding:1px 4px;border:1px solid var(--line);border-radius:3px;color:var(--cyan);font:10px var(--font-mono)}.record-title a,.record-desc a{color:var(--cyan);text-decoration:underline}.record-actions{display:flex;align-items:center;justify-content:flex-end;gap:7px;flex-wrap:wrap}.record-action{border:1px solid var(--record-color,var(--cyan));border-radius:3px;padding:8px 11px;background:transparent;color:var(--record-color,var(--cyan));font:700 10px var(--font-mono);cursor:pointer}.record-action:hover,.record-action:focus-visible{background:var(--record-color,var(--cyan));color:var(--bg)}.record-badge{align-self:start;border:1px solid rgba(111,227,180,.36);border-radius:99px;padding:4px 8px;background:var(--mint-soft);color:var(--mint);font:700 9px var(--font-mono);letter-spacing:.04em;text-transform:uppercase}.record-item.tone-cyan{--record-color:var(--cyan)}.record-item.tone-mint{--record-color:var(--mint)}.record-item.tone-gold{--record-color:var(--gold)}.record-item.tone-red{--record-color:var(--red)}.record-item.tone-violet{--record-color:var(--violet)}.stepper{gap:0;justify-content:center}.step{padding-right:0;overflow:visible}.step-line{left:calc(50% + 10px);right:calc(-50% + 10px);width:auto}#sev-p0{color:var(--red)}#sev-p1{color:var(--gold)}#sev-p2{color:var(--violet)}#sev-p3{color:var(--cyan)}#stat-runs.tone{color:var(--mint)}#stat-findings.tone-ok{color:var(--mint)}#stat-findings.tone-alert{color:var(--red)}#stat-ideas.tone-ok{color:var(--mint)}#stat-ideas.tone-pending{color:var(--gold)}#stat-adapters.tone-ok{color:var(--mint)}#stat-adapters.tone-partial{color:var(--gold)}#stat-state.tone-idle{color:var(--cyan)}#stat-state.tone-active{color:var(--violet)}#stat-state.tone-blocked{color:var(--red)}
|
|
56
|
+
footer{display:flex;justify-content:space-between;gap:10px;flex-wrap:wrap;margin-top:26px;padding-top:17px;border-top:1px solid var(--line);color:var(--muted);font:10px var(--font-mono)}
|
|
57
|
+
@media(max-width:920px){.grid .card,.grid .card.wide{grid-column:1/-1}.stats{grid-template-columns:repeat(2,1fr)}.adapters{grid-template-columns:repeat(2,1fr)}.titleblock{align-items:flex-start;flex-direction:column}.meta dd{text-align:left}.stepper{gap:0}.cmd:after{position:fixed;left:12px;right:12px;bottom:16px;width:auto;max-width:none;transform:translateY(5px)}.cmd:hover:after,.cmd:focus-visible:after,.cmd:focus:after{transform:none}}
|
|
58
|
+
@media(max-width:560px){.sheet{width:calc(100% - 24px);padding-top:22px}.panel,.card{padding:18px}.grid{gap:14px;margin-bottom:14px}.stats{margin-bottom:14px}.adapters{grid-template-columns:1fr}.cmd:after{position:fixed;left:12px;right:12px;bottom:16px;width:auto;max-width:none;transform:translateY(5px)}.cmd:hover:after,.cmd:focus-visible:after,.cmd:focus:after{transform:none}.meta{grid-template-columns:1fr}.track-tabs{width:100%}.tab{flex:1}.fact{align-items:flex-start}.record-item{grid-template-columns:34px minmax(0,1fr)}.record-action{grid-column:2;justify-self:start}}
|
|
59
|
+
@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.001ms!important;transition-duration:.001ms!important}}
|
|
60
|
+
</style>
|
|
61
|
+
</head>
|
|
62
|
+
<body>
|
|
63
|
+
<main class="sheet">
|
|
64
|
+
<header class="titleblock">
|
|
65
|
+
<div><div class="eyebrow">Nexus-DevFlow · Workspace Status</div><h1 id="project-name">Nexus-DevFlow</h1><div class="path" id="project-path">Connecting...</div><div class="badges"><span class="badge" id="version-badge">version</span><span class="badge" id="health-badge">health</span><span class="badge">MIT</span><span class="badge" id="track-badge">track</span></div></div>
|
|
66
|
+
<dl class="meta"><dt>Package</dt><dd id="package-name">@jakkrichm/create-nexus-devflow</dd><dt>Adapters</dt><dd id="adapter-count">-</dd><dt>Branch</dt><dd id="header-branch">-</dd><dt>Snapshot</dt><dd id="generated-at">-</dd></dl>
|
|
67
|
+
</header>
|
|
68
|
+
<div class="live"><span class="live-dot" id="live-dot"></span><span id="live-label">Connecting to local dashboard...</span></div>
|
|
69
|
+
|
|
70
|
+
<section class="panel" id="dual-track">
|
|
71
|
+
<div class="panel-head"><span class="label">Dual-Track Delivery Model</span><div class="track-tabs" role="tablist" aria-label="Delivery tracks"><button class="tab on" id="tab-fast" role="tab" aria-selected="true" aria-controls="view-fast" data-track="fast"><span>🚀 Fast-Track · 4 steps</span><span class="tab-active-dot">● ACTIVE</span></button><button class="tab" id="tab-deep" role="tab" aria-selected="false" aria-controls="view-deep" data-track="deep"><span>♟️ Deep-Track · 8 stages</span><span class="tab-active-dot">● ACTIVE</span></button></div></div>
|
|
72
|
+
<div class="track-view on" id="view-fast" role="tabpanel" aria-labelledby="tab-fast"><div class="stepper" id="pipeline-fast"></div><div class="track-note" id="note-fast">Fast-Track for daily features, fixes, UI work and iterative refactoring.</div></div>
|
|
73
|
+
<div class="track-view" id="view-deep" role="tabpanel" aria-labelledby="tab-deep"><div class="stepper" id="pipeline-deep"></div><div class="track-note" id="note-deep">Deep-Track for architecture, migrations, security audits and coordinated delivery.</div></div>
|
|
74
|
+
</section>
|
|
75
|
+
|
|
76
|
+
<section class="panel next-action" id="next-panel">
|
|
77
|
+
<div class="next-head"><span class="label">Next Action</span><button class="copy" id="next-copy" type="button">Copy command</button></div><div class="command" id="next-command" aria-live="polite">Loading...</div><div class="muted" id="next-reason"></div>
|
|
78
|
+
</section>
|
|
79
|
+
|
|
80
|
+
<section class="stats" aria-label="Workspace summary"><div class="stat"><div class="stat-value" id="stat-runs">-</div><div class="stat-label">Runs released</div></div><div class="stat"><div class="stat-value" id="stat-findings">-</div><div class="stat-label">Active findings</div></div><div class="stat"><div class="stat-value" id="stat-ideas">-</div><div class="stat-label">Pending ideas</div></div><div class="stat"><div class="stat-value" id="stat-adapters">-</div><div class="stat-label">Adapters configured</div></div><div class="stat"><div class="stat-value" id="stat-state">-</div><div class="stat-label">Current state</div></div></section>
|
|
81
|
+
|
|
82
|
+
<section class="grid">
|
|
83
|
+
<article class="card"><div class="card-head"><span class="label">Nexus-DevFlow</span><span class="pill" id="update-pill">checking</span></div><div class="facts"><div class="fact"><span>Installed / latest</span><span id="installed-latest">-</span></div><div class="fact"><span>Health</span><span id="system-health">-</span></div><div class="fact"><span>Architecture</span><span>3-Pillars + Dual-Track</span></div></div></article>
|
|
84
|
+
<article class="card wide"><div class="card-head"><span class="label">Current Work</span><span class="pill" id="work-pill">loading</span></div><div class="value" id="work-title">Loading...</div><div class="progress"><span id="work-progress"></span></div><div class="muted" id="work-meta"></div></article>
|
|
85
|
+
<article class="card"><div class="card-head"><span class="label">Git Status</span><span class="pill" id="git-pill">loading</span></div><div class="facts"><div class="fact"><span>Branch</span><span id="git-branch">-</span></div><div class="fact"><span>Working tree</span><span id="git-changed">-</span></div><div class="fact"><span>Upstream</span><span id="git-upstream">-</span></div><div class="fact"><span>Last commit</span><span id="git-commit">-</span></div></div></article>
|
|
86
|
+
<article class="card"><div class="card-head"><span class="label">Findings Ledger</span><span class="value" id="findings-count">-</span></div><div class="severity"><div class="sev"><b id="sev-p0">0</b><small>P0</small></div><div class="sev"><b id="sev-p1">0</b><small>P1</small></div><div class="sev"><b id="sev-p2">0</b><small>P2</small></div><div class="sev"><b id="sev-p3">0</b><small>P3</small></div></div><ul id="findings-list"></ul></article>
|
|
87
|
+
<article class="card"><div class="card-head"><span class="label">Completion</span><span class="pill" id="completion-pill">-</span></div><ul id="completion-list"></ul></article>
|
|
88
|
+
<article class="card"><div class="card-head"><span class="label">Warnings</span><span class="value" id="warnings-count">-</span></div><ul id="warnings-list"></ul></article>
|
|
89
|
+
</section>
|
|
90
|
+
|
|
91
|
+
<section class="grid"><article class="card full"><div class="card-head"><span class="label">Doctor · Health Check</span><span class="pill" id="doctor-pill">-</span></div><div class="record-list" id="doctor-list"></div></article></section>
|
|
92
|
+
<section class="card full panel"><div class="card-head"><span class="label">Discoveries</span><span class="value" id="discovery-count">-</span></div><div class="record-list" id="discovery-list"></div></section>
|
|
93
|
+
<section class="card full panel"><div class="card-head"><span class="label">Quick Commands</span><span class="muted">hover or focus for help · click to copy</span></div><div class="commands" id="command-list"></div></section>
|
|
94
|
+
<section class="card full panel"><div class="card-head"><span class="label">Adapter Health</span><span class="muted">configured · detected · healthy</span></div><div class="adapters" id="adapter-list"></div></section>
|
|
95
|
+
<section class="card full panel"><div class="card-head"><span class="label">Idea Inbox & Backlog</span><span class="value" id="ideas-count">-</span></div><div class="section-note">From devflow/ideas.md · start a selected item with /feature IDEA-xxx</div><div class="record-list" id="ideas-list"></div></section>
|
|
96
|
+
<section class="card full panel"><div class="card-head"><span class="label">Completed Work Archive</span><span class="value" id="history-count">-</span></div><div class="section-note">Categorized delivery history · features, fixes and rollbacks</div><div class="record-list" id="history-list"></div></section>
|
|
97
|
+
<footer><span>Read-only local DevFlow 2.0 dashboard · refreshes every 2 seconds</span><span id="footer-revision">snapshot pending</span></footer>
|
|
98
|
+
</main>
|
|
99
|
+
<script>
|
|
100
|
+
const byId = (id) => document.getElementById(id);
|
|
101
|
+
const text = (id, value) => { const node = byId(id); if (node) node.textContent = value == null ? '-' : String(value); };
|
|
102
|
+
const pill = (id, state, label) => { const node = byId(id); if (!node) return; node.className = 'pill ' + (state || 'configured'); node.textContent = label || state || '-'; };
|
|
103
|
+
const list = (id, values, empty) => { const node = byId(id); if (!node) return; node.replaceChildren(); const rows = values.length ? values : [empty]; rows.forEach((value) => { const li = document.createElement('li'); li.textContent = value; if (!values.length) li.className = 'empty'; node.append(li); }); };
|
|
104
|
+
const titleCase = (value) => String(value || '').replaceAll('_',' ').replace(/\b\w/g, (c) => c.toUpperCase());
|
|
105
|
+
const ICON_PATHS = { compass:'M12 2l3 7 7 3-7 3-3 7-3-7-7-3 7-3 3-7z', target:'M12 3a9 9 0 1 0 9 9M12 7a5 5 0 1 0 5 5M12 10a2 2 0 1 0 2 2', document:'M6 2h8l4 4v16H6zM14 2v5h5M9 12h6M9 16h6', list:'M8 6h12M8 12h12M8 18h12M3 6h1M3 12h1M3 18h1', code:'M8 8l-4 4 4 4M16 8l4 4-4 4M14 4l-4 16', shield:'M12 2l8 3v6c0 5-3 9-8 11-5-2-8-6-8-11V5zM8 12l3 3 5-6', report:'M5 3h14v18H5zM8 8h8M8 12h8M8 16h5', package:'M3 7l9-5 9 5v10l-9 5-9-5zM3 7l9 5 9-5M12 12v10', bulb:'M9 18h6M10 22h4M8 14c-2-1-3-3-3-5a7 7 0 0 1 14 0c0 2-1 4-3 5-1 1-1 2-1 3H9c0-1 0-2-1-3z', check:'M5 12l4 4L19 6', wrench:'M14 6a4 4 0 0 0-5 5L3 17l4 4 6-6a4 4 0 0 0 5-5l-3 1-2-2 1-3z', rollback:'M9 7H4v-5M4 7a9 9 0 1 1-1 7', search:'M11 4a7 7 0 1 0 0 14 7 7 0 0 0 0-14M16 16l5 5', alert:'M12 3l10 18H2zM12 9v5M12 18h.01' };
|
|
106
|
+
const FLOW_ICON_NAMES = ['compass','target','document','list','code','shield','report','package'];
|
|
107
|
+
const EMOJI_ICONS = { compass:'🔎',target:'🎯',document:'📄',list:'🧩',code:'⚙️',shield:'🛡️',report:'📊',package:'📦',bulb:'⚡',check:'✅',wrench:'🛠️',rollback:'↩️',search:'🔍',alert:'⚠️' };
|
|
108
|
+
function iconImage(name) { const icon = document.createElement('span'); icon.className = 'icon-glyph'; icon.setAttribute('aria-hidden','true'); icon.textContent = EMOJI_ICONS[name] || '📄'; return icon; }
|
|
109
|
+
function decoratePipeline(id) { byId(id)?.querySelectorAll('.step').forEach((step,index) => { step.classList.add('flow-' + index); const icon = document.createElement('span'); icon.className = 'step-icon'; icon.setAttribute('aria-hidden','true'); icon.append(iconImage(FLOW_ICON_NAMES[index] || 'document')); const name = step.querySelector('.step-name'); step.insertBefore(icon,name || null); }); }
|
|
110
|
+
function richText(root,value) { const source = String(value || ''); let cursor = 0; const plain = (end) => { if (end > cursor) root.append(document.createTextNode(source.slice(cursor,end))); cursor = end; }; while (cursor < source.length) { if (source.startsWith('**',cursor)) { const end = source.indexOf('**',cursor + 2); if (end > cursor + 2) { const node = document.createElement('strong'); node.textContent = source.slice(cursor + 2,end); root.append(node); cursor = end + 2; continue; } } if (source[cursor] === '*') { const end = source.indexOf('*',cursor + 1); if (end > cursor + 1) { const node = document.createElement('em'); node.textContent = source.slice(cursor + 1,end); root.append(node); cursor = end + 1; continue; } } if (source.charCodeAt(cursor) === 96) { const end = source.indexOf(String.fromCharCode(96),cursor + 1); if (end > cursor + 1) { const node = document.createElement('code'); node.textContent = source.slice(cursor + 1,end); root.append(node); cursor = end + 1; continue; } } if (source[cursor] === '[') { const close = source.indexOf('](',cursor + 1); const end = close >= 0 ? source.indexOf(')',close + 2) : -1; const href = close >= 0 && end > close ? source.slice(close + 2,end) : ''; if (close > cursor + 1 && end > close && (href.startsWith('https://') || href.startsWith('http://'))) { const node = document.createElement('a'); node.textContent = source.slice(cursor + 1,close); node.href = href; node.target = '_blank'; node.rel = 'noreferrer'; root.append(node); cursor = end + 1; continue; } } const next = [source.indexOf('**',cursor + 1),source.indexOf('*',cursor + 1),source.indexOf(String.fromCharCode(96),cursor + 1),source.indexOf('[',cursor + 1)].filter((index) => index >= 0).sort((a,b) => a-b)[0] ?? source.length; plain(next > cursor ? next : cursor + 1); } }
|
|
111
|
+
function recordCard(options) { const card = document.createElement('article'); card.className = 'record-item tone-' + (options.tone || 'cyan'); const icon = document.createElement('span'); icon.className = 'record-icon'; icon.append(iconImage(options.icon || 'document')); const body = document.createElement('div'); body.className = 'record-body'; const meta = document.createElement('div'); meta.className = 'record-meta'; richText(meta,options.meta); const title = document.createElement('div'); title.className = 'record-title'; richText(title,options.title); const desc = document.createElement('div'); desc.className = 'record-desc'; richText(desc,options.description); body.append(meta,title,desc); card.append(icon,body); if (options.badge) { const badge = document.createElement('span'); badge.className = 'record-badge'; badge.textContent = options.badge; card.append(badge); } else if (options.actions?.length) { const actions = document.createElement('div'); actions.className = 'record-actions'; options.actions.forEach((command) => { const action = document.createElement('button'); action.className = 'record-action'; action.type = 'button'; action.textContent = command; action.addEventListener('click',() => { navigator.clipboard?.writeText(command); action.textContent = 'Copied'; setTimeout(() => { action.textContent = command; },1200); }); actions.append(action); }); card.append(actions); } return card; }
|
|
112
|
+
function renderRecords(id,items,empty,mapper) { const root = byId(id); root.replaceChildren(); if (!items?.length) { const node = document.createElement('div'); node.className = 'empty'; node.textContent = empty; root.append(node); return; } items.forEach((item,index) => root.append(recordCard(mapper(item,index)))); }
|
|
113
|
+
function ideaTone(feasibility) { const value = String(feasibility || '').toLowerCase(); if (value.includes('high') || value.includes('easy') || value.includes('ง่าย')) return 'mint'; if (value.includes('low') || value.includes('hard') || value.includes('ยาก')) return 'red'; if (value.includes('medium') || value.includes('ปานกลาง')) return 'gold'; return 'violet'; }
|
|
114
|
+
function renderIdeas(items) { renderRecords('ideas-list',items,'No pending ideas',(item) => ({ icon:'bulb',tone:ideaTone(item.feasibility),meta:item.id,title:item.title,description:(item.description || '⚡') + ' · ' + (item.feasibility || '**Not scored**'),actions:['/feature ' + item.id,'/discovery ' + item.id] })); }
|
|
115
|
+
function renderHistory(items) { const map = { feature:{icon:'package',tone:'cyan'},fix:{icon:'wrench',tone:'gold'},rollback:{icon:'rollback',tone:'red'} }; renderRecords('history-list',(items || []).slice(0,10),'No completed work in history archive',(item) => { const type = String(item.type || 'other').toLowerCase(); const style = map[type] || {icon:'report',tone:'violet'}; return { ...style,meta:type + ' ' + (item.buildPlanItem || ''),title:item.title,description:'',badge:'Released' }; }); }
|
|
116
|
+
function renderDoctor(items) { renderRecords('doctor-list',items,'No doctor checks available',(item) => ({ icon:item.status === 'pass' ? 'check' : 'alert',tone:item.status === 'pass' ? 'mint' : item.status === 'warn' ? 'gold' : 'red',meta:item.status,title:item.name,description:item.message })); }
|
|
117
|
+
function renderDiscoveries(items) { renderRecords('discovery-list',items,'No discoveries recorded',(item) => { const decision = item.decision || 'Pending'; const normalized = String(decision).toLowerCase(); return { icon:'search',tone:normalized === 'proceed' || normalized === 'approved' ? 'mint' : 'violet',meta:item.id,title:item.title,description:'',badge:'Decision: ' + decision }; }); }
|
|
118
|
+
let nextCopyValue = '/feature';
|
|
119
|
+
let nextCopyLabel = '/feature';
|
|
120
|
+
byId('next-copy').addEventListener('click',(event) => { event.stopImmediatePropagation(); navigator.clipboard?.writeText(nextCopyValue); const button = byId('next-copy'); button.textContent = 'Copied ' + nextCopyLabel; setTimeout(() => { button.textContent = 'Copy command ' + nextCopyLabel; },1200); },true);
|
|
121
|
+
let preferredTrack = (() => { try { return localStorage.getItem('devflow-dashboard-track'); } catch { return null; } })();
|
|
122
|
+
document.querySelectorAll('[data-track]').forEach((button) => button.addEventListener('click',() => { preferredTrack = button.dataset.track; try { localStorage.setItem('devflow-dashboard-track',preferredTrack); } catch {} },{capture:true}));
|
|
123
|
+
|
|
124
|
+
function renderPipeline(id, nodes) {
|
|
125
|
+
const root = byId(id); root.replaceChildren();
|
|
126
|
+
(nodes || []).forEach((node, index) => {
|
|
127
|
+
const step = document.createElement('div'); step.className = 'step ' + node.state;
|
|
128
|
+
const line = document.createElement('span'); line.className = 'step-line';
|
|
129
|
+
const dot = document.createElement('span'); dot.className = 'step-dot'; dot.textContent = node.state === 'done' ? '✓' : String(index + 1).padStart(nodes.length > 4 ? 2 : 1, '0');
|
|
130
|
+
const name = document.createElement('div'); name.className = 'step-name'; name.textContent = node.label;
|
|
131
|
+
const command = document.createElement('div'); command.className = 'step-cmd'; command.textContent = node.command;
|
|
132
|
+
step.append(line, dot, name, command); root.append(step);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function selectTrack(track) {
|
|
137
|
+
document.querySelectorAll('.tab').forEach((node) => { const on = node.dataset.track === track; node.classList.toggle('on', on); node.setAttribute('aria-selected', String(on)); });
|
|
138
|
+
document.querySelectorAll('.track-view').forEach((node) => node.classList.toggle('on', node.id === 'view-' + track));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function wireCopy(button, getValue) {
|
|
142
|
+
button.addEventListener('click', async () => {
|
|
143
|
+
const original = button.textContent;
|
|
144
|
+
button.textContent = 'Copied!';
|
|
145
|
+
try { await navigator.clipboard.writeText(getValue()); }
|
|
146
|
+
catch { button.textContent = 'Copy unavailable'; }
|
|
147
|
+
setTimeout(() => { button.textContent = original; }, 1100);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
document.querySelectorAll('.tab').forEach((node) => node.addEventListener('click', () => selectTrack(node.dataset.track)));
|
|
152
|
+
wireCopy(byId('next-copy'), () => byId('next-command').textContent.trim());
|
|
153
|
+
|
|
154
|
+
function renderCommands(commands) {
|
|
155
|
+
const root = byId('command-list'); root.replaceChildren();
|
|
156
|
+
(commands || []).filter((item) => item.family === 'companion').forEach((item) => {
|
|
157
|
+
const button = document.createElement('button'); button.type = 'button'; button.className = 'cmd'; button.textContent = item.name; button.dataset.tip = item.description; button.setAttribute('aria-label', item.name + ': ' + item.description); wireCopy(button, () => item.command); root.append(button);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function renderAdapters(adapters) {
|
|
162
|
+
const root = byId('adapter-list'); root.replaceChildren();
|
|
163
|
+
(adapters || []).forEach((item) => { const card = document.createElement('div'); card.className = 'adapter'; const name = document.createElement('strong'); name.textContent = item.name; const path = document.createElement('div'); path.className = 'adapter-path'; path.textContent = item.path; const state = document.createElement('div'); state.className = 'adapter-state ' + item.state; state.textContent = item.state + ' · ' + item.message; card.append(name,path,state); root.append(card); });
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function renderSnapshot(data) {
|
|
167
|
+
const status = data.status || {}; const project = status.project || {}; const devflow = status.devflow || {}; const work = status.currentWork || {}; const git = status.git || {}; const findings = status.findings || { active: [], blockers: [] }; const workflow = data.workflow || {}; const update = data.update || {}; const doctor = data.doctor || { checks: [] }; const discoveries = data.discoveries || { recent: [] }; const history = data.history || { items: [], total: 0 }; const ideas = status.ideas || { pending: [], totalPending: 0 };
|
|
168
|
+
text('project-name', project.name || 'Nexus-DevFlow'); text('project-path', project.root || ''); text('version-badge', 'v' + (devflow.version || 'unknown')); pill('health-badge', status.health, status.health === 'ok' ? 'Health OK' : 'Health warning'); pill('track-badge', workflow.track, 'Track ' + titleCase(workflow.track || 'idle'));
|
|
169
|
+
text('adapter-count', (devflow.adapters || []).length + ' configured'); text('header-branch', git.branch || 'unknown'); text('generated-at', new Date(data.generatedAt).toLocaleTimeString()); text('footer-revision', 'snapshot ' + data.generatedAt);
|
|
170
|
+
const currentTrack = workflow.track || 'idle';
|
|
171
|
+
const isDeepActive = currentTrack === 'deep';
|
|
172
|
+
const isFastActive = currentTrack === 'fast';
|
|
173
|
+
const tabFast = byId('tab-fast');
|
|
174
|
+
const tabDeep = byId('tab-deep');
|
|
175
|
+
if (tabFast) tabFast.classList.toggle('is-active-track', isFastActive);
|
|
176
|
+
if (tabDeep) tabDeep.classList.toggle('is-active-track', isDeepActive);
|
|
177
|
+
renderPipeline('pipeline-fast', workflow.fast); renderPipeline('pipeline-deep', workflow.deep); decoratePipeline('pipeline-fast'); decoratePipeline('pipeline-deep');
|
|
178
|
+
const effectiveTrack = isDeepActive ? 'deep' : isFastActive ? 'fast' : (preferredTrack || 'fast');
|
|
179
|
+
selectTrack(effectiveTrack);
|
|
180
|
+
text('note-fast', workflow.track === 'fast' ? 'Active Fast-Track run: ' + (work.runId || work.title || 'current work') : 'Fast-Track is ready for daily features, fixes, UI work and iterative refactoring.');
|
|
181
|
+
text('note-deep', workflow.track === 'deep' ? 'Active Deep-Track run: ' + (workflow.activeRunId || workflow.activeDiscoveryId || 'current run') + ' · stage ' + (workflow.currentStage || 'unknown') : 'Deep-Track is ready for architecture, migrations, security audits and coordinated delivery.');
|
|
182
|
+
const nextCommand = data.nextAction?.command || status.nextAction?.command || '/feature'; const workTitle = String(work.title || ''); const bracketStart = workTitle.indexOf('['); const bracketEnd = bracketStart >= 0 ? workTitle.indexOf(']',bracketStart + 1) : -1; const titleRunId = bracketStart >= 0 && bracketEnd > bracketStart ? workTitle.slice(bracketStart + 1,bracketEnd).trim() : ''; const nextRunId = work.runId || work.id || workflow.activeRunId || workflow.activeDiscoveryId || titleRunId; nextCopyValue = nextRunId ? nextCommand + ' ' + nextRunId : nextCommand; nextCopyLabel = nextCommand; text('next-command', nextCommand); text('next-copy', 'Copy command ' + nextCopyLabel); byId('next-copy').title = 'Copy ' + nextCopyValue; text('next-reason', data.nextAction?.reason || status.nextAction?.reason || 'Ready for new work.');
|
|
183
|
+
text('stat-runs', history.total || 0); const activeFindings = (findings.active || []).length; text('stat-findings', activeFindings); text('stat-ideas', ideas.totalPending || 0); const healthyAdapters = (data.adapters || []).filter((item) => item.state === 'configured' || item.state === 'detected' || item.state === 'healthy').length; text('stat-adapters', healthyAdapters + '/' + (devflow.adapters || []).length); text('stat-state', titleCase(work.state || 'idle')); byId('stat-runs').className = 'stat-value tone'; byId('stat-findings').className = 'stat-value ' + (activeFindings ? 'tone-alert' : 'tone-ok'); byId('stat-ideas').className = 'stat-value ' + (ideas.totalPending ? 'tone-pending' : 'tone-ok'); byId('stat-adapters').className = 'stat-value ' + (healthyAdapters === (devflow.adapters || []).length ? 'tone-ok' : 'tone-partial'); byId('stat-state').className = 'stat-value tone-' + (work.state === 'blocked' ? 'blocked' : work.state === 'active' ? 'active' : 'idle');
|
|
184
|
+
pill('update-pill', update.state, update.state === 'current' ? 'up to date' : update.state); text('installed-latest', (update.installedVersion || 'unknown') + ' / ' + (update.latestVersion || update.state)); text('system-health', status.health || 'unknown');
|
|
185
|
+
pill('work-pill', work.state, work.state || 'idle'); text('work-title', work.title || 'No active delivery run'); const pct = work.total > 0 ? Math.round((work.completed / work.total) * 100) : 0; byId('work-progress').style.width = pct + '%'; text('work-meta', work.state === 'active' ? work.completed + ' of ' + work.total + ' steps completed' : 'Last completed: ' + (workflow.lastCompletedRun || 'none recorded'));
|
|
186
|
+
pill('git-pill', git.clean ? 'ok' : 'warning', git.clean ? 'clean' : (git.changedFiles || 0) + ' changed'); text('git-branch', git.branch || 'unknown'); text('git-changed', git.clean ? 'clean' : (git.changedFiles || 0) + ' changed files'); text('git-upstream', git.upstream ? git.upstream + ' · ' + (git.ahead || 0) + ' ahead / ' + (git.behind || 0) + ' behind' : 'none'); text('git-commit', git.lastCommit || '-');
|
|
187
|
+
text('findings-count', findings.total || 0); ['P0','P1','P2','P3'].forEach((severity) => text('sev-' + severity.toLowerCase(), (findings.active || []).filter((item) => item.severity === severity).length)); list('findings-list', (findings.active || []).map((item) => item.id + ' · ' + item.title), 'No active findings');
|
|
188
|
+
pill('completion-pill', status.completion?.state, status.completion?.state || 'unknown'); list('completion-list', status.completion?.blockers || [], work.state === 'idle' ? 'No active run to complete' : 'All readiness checks passed'); text('warnings-count', (status.warnings || []).length); list('warnings-list', (status.warnings || []).map((item) => item.message), 'No active warnings or drift');
|
|
189
|
+
pill('doctor-pill', doctor.failCount ? 'fail' : doctor.warnCount ? 'warning' : 'ok', doctor.passCount + '/' + doctor.totalChecks + ' pass'); renderDoctor(doctor.checks || []);
|
|
190
|
+
text('discovery-count', discoveries.total || 0); renderDiscoveries(discoveries.recent || []); renderCommands(data.commands); renderAdapters(data.adapters);
|
|
191
|
+
text('ideas-count', (ideas.totalPending || 0) + ' pending'); renderIdeas(ideas.pending || []); text('history-count', (history.total || 0) + ' released'); renderHistory(history.items || []);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
let refreshing = false;
|
|
195
|
+
async function refresh() { if (refreshing) return; refreshing = true; try { const response = await fetch('/api/dashboard'); if (!response.ok) throw new Error('HTTP ' + response.status); renderSnapshot(await response.json()); byId('live-dot').classList.add('on'); text('live-label','Connected · live snapshot every 2 seconds'); } catch (error) { byId('live-dot').classList.remove('on'); text('live-label','Disconnected · ' + error.message); } finally { refreshing = false; } }
|
|
196
|
+
refresh(); setInterval(refresh, 2000);
|
|
197
|
+
</script>
|
|
198
|
+
</body>
|
|
199
|
+
</html>`;
|
|
200
|
+
export { DASHBOARD_PAGE_HTML };
|
|
201
|
+
//# sourceMappingURL=dashboard-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard-page.js","sourceRoot":"","sources":["../../lib/dashboard-page.ts"],"names":[],"mappings":"AAAA,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAsMpB,CAAC;AAET,OAAO,EAAE,mBAAmB,EAAE,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { CommandCatalogItem } from "./command-catalog.js";
|
|
2
|
+
import type { DiscoverySummary } from "./discoveries.js";
|
|
3
|
+
import type { DoctorCheck } from "./doctor.js";
|
|
4
|
+
import type { HistorySummary } from "./history.js";
|
|
5
|
+
import type { ProjectStatus } from "./status.js";
|
|
6
|
+
import type { FetchLike, PackageVersionStatus } from "./version-check.js";
|
|
7
|
+
import type { WorkflowState } from "./workflow-state.js";
|
|
8
|
+
interface DashboardDoctorSummary {
|
|
9
|
+
checks: DoctorCheck[];
|
|
10
|
+
totalChecks: number;
|
|
11
|
+
passCount: number;
|
|
12
|
+
warnCount: number;
|
|
13
|
+
failCount: number;
|
|
14
|
+
}
|
|
15
|
+
interface AdapterDashboardItem {
|
|
16
|
+
name: string;
|
|
17
|
+
path: string;
|
|
18
|
+
state: "healthy" | "warning" | "configured";
|
|
19
|
+
message: string;
|
|
20
|
+
}
|
|
21
|
+
interface DashboardNextAction {
|
|
22
|
+
command: string | null;
|
|
23
|
+
reason: string;
|
|
24
|
+
}
|
|
25
|
+
interface DashboardSnapshot {
|
|
26
|
+
schemaVersion: 1;
|
|
27
|
+
generatedAt: string;
|
|
28
|
+
status: ProjectStatus;
|
|
29
|
+
workflow: WorkflowState;
|
|
30
|
+
history: HistorySummary;
|
|
31
|
+
doctor: DashboardDoctorSummary;
|
|
32
|
+
discoveries: DiscoverySummary;
|
|
33
|
+
update: PackageVersionStatus;
|
|
34
|
+
commands: CommandCatalogItem[];
|
|
35
|
+
adapters: AdapterDashboardItem[];
|
|
36
|
+
nextAction: DashboardNextAction;
|
|
37
|
+
}
|
|
38
|
+
interface DashboardSnapshotOptions {
|
|
39
|
+
fetchImpl?: FetchLike;
|
|
40
|
+
now?: () => number;
|
|
41
|
+
slowTtlMs?: number;
|
|
42
|
+
versionCacheTtlMs?: number;
|
|
43
|
+
versionTimeoutMs?: number;
|
|
44
|
+
}
|
|
45
|
+
declare function readDashboardSnapshot(startPath?: string, options?: DashboardSnapshotOptions): Promise<DashboardSnapshot>;
|
|
46
|
+
declare function clearDashboardSnapshotCache(): void;
|
|
47
|
+
declare function selectDashboardNextAction(status: ProjectStatus, workflow: WorkflowState): DashboardNextAction;
|
|
48
|
+
export { clearDashboardSnapshotCache, readDashboardSnapshot, selectDashboardNextAction };
|
|
49
|
+
export type { AdapterDashboardItem, DashboardDoctorSummary, DashboardNextAction, DashboardSnapshot, DashboardSnapshotOptions };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { readCommandCatalog } from "./command-catalog.js";
|
|
2
|
+
import { readDiscoveries } from "./discoveries.js";
|
|
3
|
+
import { runDoctor } from "./doctor.js";
|
|
4
|
+
import { readHistory } from "./history.js";
|
|
5
|
+
import { readProjectStatus } from "./status.js";
|
|
6
|
+
import { checkPackageVersion } from "./version-check.js";
|
|
7
|
+
import { readWorkflowState } from "./workflow-state.js";
|
|
8
|
+
const slowCache = new Map();
|
|
9
|
+
async function readDashboardSnapshot(startPath = process.cwd(), options = {}) {
|
|
10
|
+
const now = options.now || Date.now;
|
|
11
|
+
const status = await readProjectStatus(startPath);
|
|
12
|
+
const projectRoot = status.project.root;
|
|
13
|
+
const workflow = await readWorkflowState(projectRoot, status.currentWork);
|
|
14
|
+
const [history, slow, update] = await Promise.all([
|
|
15
|
+
readHistory(projectRoot),
|
|
16
|
+
readSlowData(projectRoot, workflow.activeDiscoveryId, now, options.slowTtlMs ?? 15_000),
|
|
17
|
+
checkPackageVersion({
|
|
18
|
+
installedVersion: status.devflow.version,
|
|
19
|
+
fetchImpl: options.fetchImpl,
|
|
20
|
+
timeoutMs: options.versionTimeoutMs,
|
|
21
|
+
cacheTtlMs: options.versionCacheTtlMs,
|
|
22
|
+
now
|
|
23
|
+
})
|
|
24
|
+
]);
|
|
25
|
+
return {
|
|
26
|
+
schemaVersion: 1,
|
|
27
|
+
generatedAt: new Date(now()).toISOString(),
|
|
28
|
+
status,
|
|
29
|
+
workflow,
|
|
30
|
+
history,
|
|
31
|
+
doctor: selectDoctorSummary(slow.doctor),
|
|
32
|
+
discoveries: slow.discoveries,
|
|
33
|
+
update,
|
|
34
|
+
commands: slow.commands,
|
|
35
|
+
adapters: buildAdapterItems(status.devflow.adapters, slow.doctor),
|
|
36
|
+
nextAction: selectDashboardNextAction(status, workflow)
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
async function readSlowData(projectRoot, activeDiscoveryId, now, ttlMs) {
|
|
40
|
+
const cached = slowCache.get(projectRoot);
|
|
41
|
+
if (cached && cached.expiresAt > now() && cached.activeDiscoveryId === activeDiscoveryId) {
|
|
42
|
+
return cached.value;
|
|
43
|
+
}
|
|
44
|
+
const [doctor, discoveries, commands] = await Promise.all([
|
|
45
|
+
runDoctor(projectRoot, { fix: false }),
|
|
46
|
+
readDiscoveries(projectRoot, activeDiscoveryId),
|
|
47
|
+
readCommandCatalog(projectRoot)
|
|
48
|
+
]);
|
|
49
|
+
const value = { doctor, discoveries, commands };
|
|
50
|
+
slowCache.set(projectRoot, { expiresAt: now() + ttlMs, activeDiscoveryId, value });
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
function selectDoctorSummary(report) {
|
|
54
|
+
return {
|
|
55
|
+
checks: report.checks,
|
|
56
|
+
totalChecks: report.totalChecks,
|
|
57
|
+
passCount: report.passCount,
|
|
58
|
+
warnCount: report.warnCount,
|
|
59
|
+
failCount: report.failCount
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function buildAdapterItems(adapters, doctor) {
|
|
63
|
+
const adapterCheck = doctor.checks.find((check) => check.id === "ai_adapters");
|
|
64
|
+
const state = adapterCheck?.status === "fail"
|
|
65
|
+
? "warning"
|
|
66
|
+
: adapterCheck?.status === "pass"
|
|
67
|
+
? "healthy"
|
|
68
|
+
: "configured";
|
|
69
|
+
const paths = {
|
|
70
|
+
codex: ".agents/skills/",
|
|
71
|
+
copilot: ".agents/skills/",
|
|
72
|
+
antigravity: ".agents/skills/",
|
|
73
|
+
claude: ".claude/skills/"
|
|
74
|
+
};
|
|
75
|
+
return adapters.map((name) => ({
|
|
76
|
+
name,
|
|
77
|
+
path: paths[name] || "AGENTS.md",
|
|
78
|
+
state,
|
|
79
|
+
message: adapterCheck?.message || "Configured in the Nexus-DevFlow manifest."
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
function clearDashboardSnapshotCache() {
|
|
83
|
+
slowCache.clear();
|
|
84
|
+
}
|
|
85
|
+
function selectDashboardNextAction(status, workflow) {
|
|
86
|
+
if (workflow.track !== "deep" || !workflow.currentStage) {
|
|
87
|
+
return status.nextAction;
|
|
88
|
+
}
|
|
89
|
+
const suffix = workflow.activeRunId ? ` ${workflow.activeRunId}` : "";
|
|
90
|
+
return {
|
|
91
|
+
command: `/${workflow.currentStage}${suffix}`,
|
|
92
|
+
reason: `Continue the active Deep-Track run at ${workflow.currentStage}.`
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
export { clearDashboardSnapshotCache, readDashboardSnapshot, selectDashboardNextAction };
|
|
96
|
+
//# sourceMappingURL=dashboard-snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard-snapshot.js","sourceRoot":"","sources":["../../lib/dashboard-snapshot.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/dashboard.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import type { DashboardSnapshotOptions } from "./dashboard-snapshot.js";
|
|
1
2
|
interface DashboardServer {
|
|
2
3
|
close: () => Promise<void>;
|
|
3
4
|
url: string;
|
|
4
5
|
}
|
|
5
6
|
interface DashboardServerOptions {
|
|
6
7
|
port?: number;
|
|
8
|
+
snapshotOptions?: DashboardSnapshotOptions;
|
|
7
9
|
}
|
|
8
10
|
declare function startDashboardServer(startPath?: string, options?: DashboardServerOptions): Promise<DashboardServer>;
|
|
9
11
|
declare function openDashboard(url: string): Promise<void>;
|