@hegemonart/get-design-done 1.54.0 → 1.55.0
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +47 -0
- package/README.md +2 -0
- package/bin/gdd-dashboard +91 -0
- package/package.json +2 -1
- package/scripts/lib/dashboard/graph-html.cjs +0 -0
- package/scripts/lib/health-mirror/index.cjs +146 -1
- package/sdk/cli/commands/dashboard.ts +419 -0
- package/sdk/cli/index.js +253 -2
- package/sdk/cli/index.ts +7 -0
- package/sdk/dashboard/data/_pkg-root.cjs +92 -0
- package/sdk/dashboard/data/cost-aggregator.cjs +187 -0
- package/sdk/dashboard/data/discovery.cjs +297 -0
- package/sdk/dashboard/data/risk-surface.cjs +136 -0
- package/sdk/dashboard/data/source.cjs +576 -0
- package/sdk/dashboard/tui/ansi.cjs +355 -0
- package/sdk/dashboard/tui/index.cjs +778 -0
- package/sdk/mcp/gdd-mcp/server.js +70 -0
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
},
|
|
6
6
|
"metadata": {
|
|
7
7
|
"description": "Get Design Done — 5-stage agent-orchestrated design pipeline with 9 connections, handoff-first workflow, bidirectional Figma write-back, 22+ specialized agents, queryable knowledge layer (intel store, dependency analysis, learnings extraction), and a self-improvement loop (reflector, frontmatter + budget feedback, global-skills layer). v1.20.0 ships the SDK foundation: gdd-state MCP server (11 typed tools), lockfile-safe STATE.md mutations, event stream, and resilience primitives (jittered-backoff, rate-guard, error-classifier, iteration-budget) for rate-limit + 429 + context-overflow recovery. Full CI/CD pipeline (Node 22/24 × Linux/macOS/Windows) and release automation (auto-tag + GitHub Release + release-time smoke test).",
|
|
8
|
-
"version": "1.
|
|
8
|
+
"version": "1.55.0"
|
|
9
9
|
},
|
|
10
10
|
"plugins": [
|
|
11
11
|
{
|
|
12
12
|
"name": "get-design-done",
|
|
13
13
|
"source": "./",
|
|
14
14
|
"description": "Agent-orchestrated 5-stage design pipeline: Brief → Explore → Plan → Design → Verify. 22+ specialized agents, 9 connections (Figma, Refero, Preview, Storybook, Chromatic, Figma Writer, Graphify, Pinterest, Claude Design), Claude Design handoff, bidirectional Figma write-back, and a queryable intel store (.design/intel/) for dependency and learnings queries. Standalone commands: style, darkmode, compare, figma-write, graphify, handoff, analyze-dependencies, skill-manifest, extract-learnings. Embeds NNG heuristics, WCAG thresholds, typographic systems, motion framework, and anti-pattern catalog. Ships with a full CI/CD pipeline (Node 22/24 × Linux/macOS/Windows) and release automation. Optimization layer (v1.0.4.1, retroactive): gdd-router + gdd-cache-manager skills, PreToolUse budget-enforcer hook, tier-aware agent frontmatter, lazy checker gates, streaming synthesizer, /gdd:warm-cache + /gdd:optimize commands, and cost telemetry at .design/telemetry/costs.jsonl — targeting 50-70% per-task token-cost reduction with no quality-floor regression. v1.20.0 SDK foundation: gdd-state MCP server (11 typed tools), lockfile-safe STATE.md mutations, event stream at .design/telemetry/events.jsonl, resilience primitives (jittered-backoff, rate-guard, error-classifier, iteration-budget) with rate-limit + 429 + context-overflow recovery, and TypeScript toolchain.",
|
|
15
|
-
"version": "1.
|
|
15
|
+
"version": "1.55.0",
|
|
16
16
|
"author": {
|
|
17
17
|
"name": "hegemonart"
|
|
18
18
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "get-design-done",
|
|
3
3
|
"short_name": "gdd",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.55.0",
|
|
5
5
|
"description": "Agent-orchestrated 5-stage design pipeline: Brief → Explore → Plan → Design → Verify. 59 specialized agents, 88 skills, 41 connection integrations (Figma, Refero, Preview, Storybook, Chromatic, Graphify, Slack, Linear, Jira, Notion, and more), handoff-first workflow via Claude Design bundles, bidirectional Figma write-back (annotations, Code Connect), queryable intel store (`.design/intel/`) for O(1) design surface lookups, and self-improvement loop (reflector agent, frontmatter + budget feedback, global-skills layer at `~/.claude/gdd/global-skills/`). Standalone commands: style, darkmode, compare, figma-write, graphify, handoff, analyze-dependencies, skill-manifest, extract-learnings, reflect, apply-reflections. Embeds NNG heuristics, WCAG thresholds, typographic systems, motion framework, and anti-pattern catalog. Ships with a full CI/CD pipeline (Node 22/24 × Linux/macOS/Windows, lint + schema + frontmatter + stale-ref + shellcheck + gitleaks + injection-scan + blocking size-budget) and release automation (auto-tag + GitHub Release + release-time smoke test). Optimization layer (v1.0.4.1, retroactive): gdd-router + gdd-cache-manager skills, PreToolUse budget-enforcer hook, tier-aware agent frontmatter, lazy checker gates, streaming synthesizer, /gdd:warm-cache + /gdd:optimize commands, and cost telemetry at .design/telemetry/costs.jsonl — targeting 50-70% per-task token-cost reduction with no quality-floor regression. v1.20.0 SDK foundation: gdd-state MCP server (11 typed tools), lockfile-safe STATE.md mutations, event stream at .design/telemetry/events.jsonl, resilience primitives (jittered-backoff, rate-guard, error-classifier, iteration-budget) with rate-limit + 429 + context-overflow recovery, and TypeScript toolchain. v1.27.7 ships gdd-mcp (Phase 27.7): 12 read-only MCP tools for sub-3s priming. v1.28.0 (Phase 28): Foundational References Tier 2 — 5 new reference files (color-theory, composition, proportion-systems, i18n, contrast-advanced), 2 verifier i18n probes + 1 explore i18n-readiness probe, 12 additive cross-link insertions across 10 existing references, 2 orthogonal audit-scoring lens-tags (composition_alignment + i18n_readiness).",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "hegemonart",
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,53 @@ All notable changes to get-design-done are documented here. Versions follow [sem
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## [1.55.0] - 2026-06-03
|
|
8
|
+
|
|
9
|
+
### Phase 55 - GDD Dashboard (Multi-Harness Control Plane + Graph Visualization + Session Surface)
|
|
10
|
+
|
|
11
|
+
GDD ships to 14 runtimes and users run multi-harness sessions in parallel; a control plane is the natural way to surface
|
|
12
|
+
that. Phase 55 ships a READ-ONLY dashboard: a terminal TUI (`bin/gdd-dashboard`) and an opt-in browser graph view
|
|
13
|
+
(`gdd dashboard --web`) that visualizes the Phase 52 DesignContext graph. **Built fully dep-free** (maintainer Rule-4
|
|
14
|
+
decision): no Ink, no React / Vite / React Flow. The ROADMAP had named those stacks (~100 packages / ~84 MB + a CI build
|
|
15
|
+
gate); a footprint study plus the read-only / single-machine / opt-in scale made a hand-rolled ANSI TUI + a
|
|
16
|
+
self-contained-HTML graph (extending the Phase 35.5 `build-html.cjs` precedent) the better fit, and it keeps the
|
|
17
|
+
zero-new-dependency streak intact. The web layer is kept swappable, so a React Flow migration later needs no data rewrite.
|
|
18
|
+
Planned and executed via the GSD pipeline (3 + 3 parallel executors).
|
|
19
|
+
|
|
20
|
+
### Breaking changes
|
|
21
|
+
|
|
22
|
+
- **New `bin/gdd-dashboard` + `gdd dashboard [--web]`.** A new bin (the TUI) and a new SDK CLI subcommand. Read-only by
|
|
23
|
+
design: the dashboard never mutates state (the action surface is "open this file / copy this command / run the
|
|
24
|
+
slash-skill"). The `--web` server is an ephemeral LOCAL loopback (127.0.0.1, OS-assigned port) serving a single
|
|
25
|
+
self-contained HTML file to your own browser, then exits.
|
|
26
|
+
- **`gsd-health` gains a 10th check** (`dashboard_reachable`); the health check count moves 9 -> 10.
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- **Data plane** `sdk/dashboard/data/` - `source.cjs` (`loadDashboardModel` reads state / events / graph / health via the
|
|
31
|
+
shared libs in-process, with a `.design/*` file-scrape fallback; never throws), `cost-aggregator.cjs` (per-runtime +
|
|
32
|
+
cumulative + per-cycle), `discovery.cjs` (14-runtime detection, worktree enumeration via `git worktree list`,
|
|
33
|
+
best-effort session manifests).
|
|
34
|
+
- **TUI** `bin/gdd-dashboard` + `sdk/dashboard/tui/` - a hand-rolled ANSI render core (`ansi.cjs`: box/column layout,
|
|
35
|
+
width-aware truncation incl. CJK, line-diff repaint) + 5 panes (Sessions / Cycle / Cost / Findings /
|
|
36
|
+
DesignContext-tree), keyboard navigation, alt-screen, event-tail live refresh. Plain `.cjs` (no bundle, no flags).
|
|
37
|
+
- **Web graph** `scripts/lib/dashboard/graph-html.cjs` (`buildGraphHtml`) - one self-contained HTML doc (inline SVG +
|
|
38
|
+
vanilla JS): layered Atomic / Molecular / Organism / Template layout with barycenter crossing-reduction, viewBox
|
|
39
|
+
pan/zoom, click-to-inspect, type/tag filters, find-consumers highlight, unreachable outline, PNG export, minimap.
|
|
40
|
+
Deterministic (hermetic byte-equal test). Launched by `gdd dashboard --web` (`node:http` loopback + browser-open;
|
|
41
|
+
headless prints the URL; `--once` writes `.design/dashboard.html`).
|
|
42
|
+
- **Risk surfacing** `sdk/dashboard/data/risk-surface.cjs` - reads `risk_score`/`confidence` when present (Phase 56),
|
|
43
|
+
color-routes Allow/Review/RequireConfirmation/Block; a blank placeholder pre-56.
|
|
44
|
+
|
|
45
|
+
### Notes
|
|
46
|
+
|
|
47
|
+
- 6-manifest lockstep at **v1.55.0** + `OFF_CADENCE_VERSIONS.add('1.55.0')` + 37 `manifests-version.txt` baselines.
|
|
48
|
+
No new skill (the dashboard is a bin + CLI subcommand) -> skill-list / build:skills / skills.json unchanged. No new
|
|
49
|
+
agent. The dashboard's local `node:http` server is allowlisted in `scripts/security/outbound-allowlist.json` (an
|
|
50
|
+
inbound loopback read-only server, not outbound egress). Tarball golden 969 -> 979. **No new runtime dependency.**
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
7
54
|
## [1.54.0] - 2026-06-03
|
|
8
55
|
|
|
9
56
|
### Phase 54 - Composable Reference Addendums (Per-DS + Per-Framework + Per-Motion-Lib)
|
package/README.md
CHANGED
|
@@ -269,6 +269,8 @@ All 14 runtimes receive their native artifact layout (`skills/`, `command/`, `ag
|
|
|
269
269
|
|
|
270
270
|
**Composable reference addendums (v1.54.0).** One general mapper prompt cannot know Tailwind's `@theme` tokens from shadcn's `cn()` from vanilla-extract's `style({})`. Phase 54 adds 18 stack-specific addendums (8 design systems, 6 frameworks, 4 motion libraries) under `reference/{systems,frameworks,motion}/`, each at most 50 lines with Conventions / File patterns / Gotchas / Example-output sections. `scripts/lib/detect/stack.cjs` detects the project stack from dependencies plus config files and import signatures; `scripts/lib/mapper-spawn.cjs` composes the matching addendums (capped at one DS + one framework + one motion) into each explore mapper's prompt at spawn time, additively, so a project on an unknown stack just keeps the base prompt and `gsd-health` reports the coverage gap. A `/gdd:new-addendum` scaffolder adds more. Maintainer-authored, vendor-cross-checked, no LLM-generated content. **No new runtime dependency.**
|
|
271
271
|
|
|
272
|
+
**GDD dashboard (v1.55.0).** A read-only multi-harness control plane. `gdd-dashboard` opens a terminal TUI with five panes (sessions per runtime, current cycle, cost telemetry, findings, a DesignContext tree); `gdd dashboard --web` opens an interactive browser view of the Phase 52 graph (layered Atomic/Molecular/Organism/Template layout, pan/zoom, click-to-inspect, type/tag filters, find-consumers highlight, PNG export, minimap). It is **built fully dep-free** (a maintainer decision over the roadmap's Ink + React Flow + Vite stack, which would have added ~100 packages): the TUI is a hand-rolled ANSI renderer, the graph view is a self-contained HTML file (inline SVG + vanilla JS, extending the export builder), and the data plane reads GDD state through the existing shared libraries in-process. Read-only by design (the action surface is open-file / copy-command / run-skill); the `--web` server is a local loopback that serves to your own browser and exits. **No new runtime dependency.**
|
|
273
|
+
|
|
272
274
|
Verify with:
|
|
273
275
|
|
|
274
276
|
```
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// bin/gdd-dashboard — Phase 55 (GDD Dashboard, DEP-FREE), TUI-01 (executor D).
|
|
3
|
+
//
|
|
4
|
+
// CJS trampoline that launches the terminal dashboard
|
|
5
|
+
// (sdk/dashboard/tui/index.cjs) under `node`, forwarding argv + the child's
|
|
6
|
+
// exit code / signal. Windows-safe.
|
|
7
|
+
//
|
|
8
|
+
// SIMPLER than bin/gdd-sdk (R8): the TUI is plain `.cjs` (Node builtins only),
|
|
9
|
+
// so there is NO esbuild bundle and NO `--experimental-strip-types` flag — we
|
|
10
|
+
// just `node <entry>`. The only reason this is a spawning trampoline (rather
|
|
11
|
+
// than a direct require) is the same one gdd-sdk documents: npm's
|
|
12
|
+
// auto-generated Windows `.cmd` shim invokes `node bin/gdd-dashboard` and we
|
|
13
|
+
// want a single, platform-uniform launch path that cleanly forwards the exit
|
|
14
|
+
// code and re-raises a terminating signal (so Ctrl-C tears down correctly).
|
|
15
|
+
//
|
|
16
|
+
// Sibling resolution uses a PACKAGE-ROOT WALK-UP (the Phase 53/54 lesson, D7),
|
|
17
|
+
// never a fixed `__dirname`-relative cross-tree jump: we climb from this file's
|
|
18
|
+
// directory to the GDD package root (package.json with name
|
|
19
|
+
// "get-design-done") and resolve `sdk/dashboard/tui/index.cjs` from there. This
|
|
20
|
+
// keeps the bin correct even if it is copied/relocated within the tree.
|
|
21
|
+
|
|
22
|
+
'use strict';
|
|
23
|
+
|
|
24
|
+
const { spawn } = require('node:child_process');
|
|
25
|
+
const fs = require('node:fs');
|
|
26
|
+
const path = require('node:path');
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Walk up from `startDir` to the GDD package root: the first ancestor whose
|
|
30
|
+
* package.json declares `name === "get-design-done"`. Falls back to the first
|
|
31
|
+
* ancestor that has any package.json, then to `startDir`. Bounded climb.
|
|
32
|
+
* @param {string} startDir
|
|
33
|
+
* @returns {string} absolute package-root directory
|
|
34
|
+
*/
|
|
35
|
+
function findPackageRoot(startDir) {
|
|
36
|
+
let dir = path.resolve(startDir);
|
|
37
|
+
let firstWithPkg = null;
|
|
38
|
+
for (let i = 0; i < 12; i++) {
|
|
39
|
+
const pkgPath = path.join(dir, 'package.json');
|
|
40
|
+
let pkg = null;
|
|
41
|
+
try {
|
|
42
|
+
pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
43
|
+
} catch {
|
|
44
|
+
pkg = null;
|
|
45
|
+
}
|
|
46
|
+
if (pkg) {
|
|
47
|
+
if (firstWithPkg === null) firstWithPkg = dir;
|
|
48
|
+
if (pkg.name === 'get-design-done') return dir;
|
|
49
|
+
}
|
|
50
|
+
const parent = path.dirname(dir);
|
|
51
|
+
if (parent === dir) break;
|
|
52
|
+
dir = parent;
|
|
53
|
+
}
|
|
54
|
+
return firstWithPkg || path.resolve(startDir);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const root = findPackageRoot(__dirname);
|
|
58
|
+
const entry = path.join(root, 'sdk', 'dashboard', 'tui', 'index.cjs');
|
|
59
|
+
|
|
60
|
+
if (!fs.existsSync(entry)) {
|
|
61
|
+
// eslint-disable-next-line no-console
|
|
62
|
+
console.error(`gdd-dashboard: TUI entry not found at ${entry}`);
|
|
63
|
+
process.exit(3);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const child = spawn(process.execPath, [entry, ...process.argv.slice(2)], {
|
|
67
|
+
stdio: 'inherit',
|
|
68
|
+
shell: false,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
child.on('exit', (code, signal) => {
|
|
72
|
+
if (signal) {
|
|
73
|
+
// Re-raise the signal so the parent shell sees the same termination mode
|
|
74
|
+
// (e.g. Ctrl+C propagates as SIGINT). Falls back to a non-zero exit if the
|
|
75
|
+
// self-signal cannot be delivered.
|
|
76
|
+
try {
|
|
77
|
+
process.kill(process.pid, signal);
|
|
78
|
+
} catch {
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
process.exit(typeof code === 'number' ? code : 0);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
child.on('error', (err) => {
|
|
87
|
+
// Failure to spawn node itself — extremely rare; surface to stderr.
|
|
88
|
+
// eslint-disable-next-line no-console
|
|
89
|
+
console.error('gdd-dashboard: failed to launch the TUI:', err.message);
|
|
90
|
+
process.exit(3);
|
|
91
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hegemonart/get-design-done",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.55.0",
|
|
4
4
|
"description": "A design-quality pipeline for AI coding agents: brief, plan, implement, and verify UI work against your design system.",
|
|
5
5
|
"author": "Hegemon",
|
|
6
6
|
"homepage": "https://github.com/hegemonart/get-design-done",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"NOTICE"
|
|
35
35
|
],
|
|
36
36
|
"bin": {
|
|
37
|
+
"gdd-dashboard": "./bin/gdd-dashboard",
|
|
37
38
|
"gdd-detect": "./bin/gdd-detect",
|
|
38
39
|
"gdd-events": "./scripts/cli/gdd-events.mjs",
|
|
39
40
|
"gdd-graph": "./bin/gdd-graph",
|
|
Binary file
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// Surface:
|
|
9
9
|
// async getHealthChecks(rootDir) → { checks: HealthCheck[] }
|
|
10
10
|
//
|
|
11
|
-
// The
|
|
11
|
+
// The 10 checks (in stable order) are:
|
|
12
12
|
// 1. claude_md — CLAUDE.md presence
|
|
13
13
|
// 2. planning_dir — .planning/ presence
|
|
14
14
|
// 3. design_dir — .design/ presence
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
// 7. skill_discipline — using-gdd bootstrap + SessionStart inject (Plan 32-07)
|
|
19
19
|
// 8. harness_freshness — per-harness last_verified age (Phase 44)
|
|
20
20
|
// 9. stack_addendums — Phase 54 coverage: N/M detected stacks have addendums
|
|
21
|
+
// 10. dashboard_reachable — Phase 55: bin/gdd-dashboard on disk + data plane loads
|
|
21
22
|
//
|
|
22
23
|
// Check 5 was added in Plan 30-06 — surfaces the report-issue kill-switch
|
|
23
24
|
// (env or config disable) so users can verify why the command is
|
|
@@ -48,6 +49,22 @@
|
|
|
48
49
|
// inject-using-gdd entry)
|
|
49
50
|
// status: 'ok' when ready, 'warn' otherwise. PURE read-only (rootDir-relative
|
|
50
51
|
// file + JSON inspection only) — NEVER throws, NEVER networks.
|
|
52
|
+
//
|
|
53
|
+
// Check 10 was added in Phase 55 — surfaces whether the GDD dashboard is
|
|
54
|
+
// reachable so a user running /gdd:health knows the `gdd dashboard` entrypoint
|
|
55
|
+
// is wired. GRACEFUL-ABSENT by design (D-8 risk surfacing precedent): the
|
|
56
|
+
// dashboard is an opt-in, read-only surface that also works via file-scrape, so
|
|
57
|
+
// a missing bin or absent data plane is a 'warn' (actionable note), NEVER a
|
|
58
|
+
// hard 'fail'. The status is 'ok' when BOTH the bin/gdd-dashboard trampoline
|
|
59
|
+
// resolves on disk (located via a package-root walk-up — the Phase 53/54 lesson,
|
|
60
|
+
// NEVER a fixed __dirname jump) AND the dashboard data plane module
|
|
61
|
+
// (sdk/dashboard/data/source.cjs) loads + exposes loadDashboardModel. The detail
|
|
62
|
+
// line is one of:
|
|
63
|
+
// - "dashboard: bin/gdd-dashboard present; data plane ok"
|
|
64
|
+
// - "dashboard: bin missing" (trampoline not on disk)
|
|
65
|
+
// - "dashboard: data plane unavailable" (bin present, source.cjs absent)
|
|
66
|
+
// - "dashboard: bin missing; data plane unavailable"
|
|
67
|
+
// PURE read-only (fs.statSync + a wrapped require) — NEVER throws, NEVER networks.
|
|
51
68
|
|
|
52
69
|
const fs = require('node:fs');
|
|
53
70
|
const path = require('node:path');
|
|
@@ -310,6 +327,39 @@ async function getHealthChecks(rootDir) {
|
|
|
310
327
|
checks.push({ name: 'stack_addendums', status, detail });
|
|
311
328
|
}
|
|
312
329
|
|
|
330
|
+
// 10. dashboard_reachable — Phase 55. GRACEFUL-ABSENT: reports whether the
|
|
331
|
+
// GDD dashboard entrypoint is wired (bin/gdd-dashboard on disk) AND its data
|
|
332
|
+
// plane module loads. NEVER 'fail' — a missing bin is a 'warn' note because
|
|
333
|
+
// the dashboard is opt-in and also works via file-scrape. PURE read-only
|
|
334
|
+
// (fs.statSync + a wrapped require); NEVER throws, NEVER networks.
|
|
335
|
+
{
|
|
336
|
+
let status;
|
|
337
|
+
let detail;
|
|
338
|
+
try {
|
|
339
|
+
const gddRoot = resolveDashboardRoot(rootDir);
|
|
340
|
+
const binPresent = dashboardBinResolves(gddRoot);
|
|
341
|
+
const dataPlaneOk = dashboardDataPlaneLoads(gddRoot);
|
|
342
|
+
if (binPresent && dataPlaneOk) {
|
|
343
|
+
status = 'ok';
|
|
344
|
+
detail = 'dashboard: bin/gdd-dashboard present; data plane ok';
|
|
345
|
+
} else {
|
|
346
|
+
status = 'warn';
|
|
347
|
+
if (!binPresent && !dataPlaneOk) {
|
|
348
|
+
detail = 'dashboard: bin missing; data plane unavailable';
|
|
349
|
+
} else if (!binPresent) {
|
|
350
|
+
detail = 'dashboard: bin missing';
|
|
351
|
+
} else {
|
|
352
|
+
detail = 'dashboard: data plane unavailable';
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
} catch {
|
|
356
|
+
// Absolute safety net — the health probe must never crash on this check.
|
|
357
|
+
status = 'warn';
|
|
358
|
+
detail = 'dashboard: unavailable';
|
|
359
|
+
}
|
|
360
|
+
checks.push({ name: 'dashboard_reachable', status, detail });
|
|
361
|
+
}
|
|
362
|
+
|
|
313
363
|
return { checks };
|
|
314
364
|
}
|
|
315
365
|
|
|
@@ -402,4 +452,99 @@ function figmaVariablesBlockedLocally(rootDir) {
|
|
|
402
452
|
}
|
|
403
453
|
}
|
|
404
454
|
|
|
455
|
+
/**
|
|
456
|
+
* Walk UP from `startDir` to the GDD package root (the first ancestor whose
|
|
457
|
+
* package.json `name` is the GDD package). This mirrors the Phase 53/54 lesson
|
|
458
|
+
* (sdk/dashboard/data/_pkg-root.cjs): NEVER resolve a cross-tree sibling via a
|
|
459
|
+
* fixed __dirname-relative jump. The shipped package name is scoped
|
|
460
|
+
* ("@hegemonart/get-design-done"); dev/self-host/fixture roots may use the bare
|
|
461
|
+
* "get-design-done" — both match. Bounded climb; defensive. Returns null if no
|
|
462
|
+
* GDD root marker is found.
|
|
463
|
+
*
|
|
464
|
+
* @param {string} startDir
|
|
465
|
+
* @returns {string|null} absolute package-root dir, or null
|
|
466
|
+
*/
|
|
467
|
+
function findGddPackageRoot(startDir) {
|
|
468
|
+
try {
|
|
469
|
+
let dir = path.resolve(startDir);
|
|
470
|
+
for (let i = 0; i < 12; i++) {
|
|
471
|
+
try {
|
|
472
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf8'));
|
|
473
|
+
if (pkg && typeof pkg.name === 'string') {
|
|
474
|
+
if (pkg.name === 'get-design-done' || /\/get-design-done$/.test(pkg.name)) {
|
|
475
|
+
return dir;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
} catch {
|
|
479
|
+
// no/garbage package.json at this level — keep climbing
|
|
480
|
+
}
|
|
481
|
+
const parent = path.dirname(dir);
|
|
482
|
+
if (parent === dir) break;
|
|
483
|
+
dir = parent;
|
|
484
|
+
}
|
|
485
|
+
return null;
|
|
486
|
+
} catch {
|
|
487
|
+
return null;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Resolve the AUTHORITATIVE GDD package root for the dashboard probe, given the
|
|
493
|
+
* project root being health-checked. Resolution (D-7 walk-up, never a fixed
|
|
494
|
+
* __dirname jump):
|
|
495
|
+
* - If `rootDir` itself sits inside a GDD checkout (dev / self-host / a
|
|
496
|
+
* hermetic fixture that declares the GDD name), THAT root is authoritative —
|
|
497
|
+
* its own bin/ + sdk/dashboard/ are the truth. No cross-root fallback (so a
|
|
498
|
+
* fixture is hermetic + deterministic regardless of the shipped tree).
|
|
499
|
+
* - Otherwise `rootDir` is an unrelated CONSUMER project (no GDD marker); the
|
|
500
|
+
* dashboard ships alongside THIS module, so walk up from __dirname.
|
|
501
|
+
* Returns null only if neither resolves (degrades the check to 'warn').
|
|
502
|
+
*
|
|
503
|
+
* @param {string} rootDir project root passed to getHealthChecks
|
|
504
|
+
* @returns {string|null}
|
|
505
|
+
*/
|
|
506
|
+
function resolveDashboardRoot(rootDir) {
|
|
507
|
+
const fromRoot = findGddPackageRoot(rootDir);
|
|
508
|
+
if (fromRoot) return fromRoot;
|
|
509
|
+
return findGddPackageRoot(__dirname);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Does the bin/gdd-dashboard trampoline resolve on disk under the authoritative
|
|
514
|
+
* GDD root? (Phase 55, check 10.) `fs.statSync` follows symlinks, so an npm
|
|
515
|
+
* bin-linked trampoline (a symlink resolving to a file) counts as present. PURE
|
|
516
|
+
* read-only; NEVER throws.
|
|
517
|
+
*
|
|
518
|
+
* @param {string} gddRoot authoritative GDD root (or null)
|
|
519
|
+
* @returns {boolean} true iff bin/gdd-dashboard is present on disk
|
|
520
|
+
*/
|
|
521
|
+
function dashboardBinResolves(gddRoot) {
|
|
522
|
+
if (!gddRoot) return false;
|
|
523
|
+
try {
|
|
524
|
+
return fs.statSync(path.join(gddRoot, 'bin', 'gdd-dashboard')).isFile();
|
|
525
|
+
} catch {
|
|
526
|
+
return false;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Does the dashboard data plane module load + expose loadDashboardModel under
|
|
532
|
+
* the authoritative GDD root? (Phase 55, check 10.) The data plane is
|
|
533
|
+
* sdk/dashboard/data/source.cjs (R1 — the in-process shared-lib read surface the
|
|
534
|
+
* dashboard renders). A missing module or a require error degrades to false
|
|
535
|
+
* (→ 'warn'), NEVER throws.
|
|
536
|
+
*
|
|
537
|
+
* @param {string} gddRoot authoritative GDD root (or null)
|
|
538
|
+
* @returns {boolean} true iff source.cjs loads and exports loadDashboardModel
|
|
539
|
+
*/
|
|
540
|
+
function dashboardDataPlaneLoads(gddRoot) {
|
|
541
|
+
if (!gddRoot) return false;
|
|
542
|
+
try {
|
|
543
|
+
const mod = require(path.join(gddRoot, 'sdk', 'dashboard', 'data', 'source.cjs'));
|
|
544
|
+
return !!(mod && typeof mod.loadDashboardModel === 'function');
|
|
545
|
+
} catch {
|
|
546
|
+
return false;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
405
550
|
module.exports = { getHealthChecks };
|