@walwal-harness/cli 7.1.40 → 7.1.42
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/HR-Resource/cdo/SKILL.md +9 -1
- package/HR-Resource/ceo/SKILL.md +33 -2
- package/HR-Resource/coo/SKILL.md +9 -1
- package/HR-Resource/cqo/SKILL.md +9 -1
- package/HR-Resource/cto/SKILL.md +10 -2
- package/HR-Resource/ops/SKILL.md +12 -4
- package/HR-Resource/ops-prompt-inspector/SKILL.md +11 -6
- package/HR-Resource/ops-prompt-inspector/assets/prompt-inspector-static.js +180 -0
- package/HR-Resource/ops-prompt-inspector/scripts/setup.py +66 -3
- package/apps/harness-dashboard/app/api/metrics/route.ts +17 -0
- package/apps/harness-dashboard/app/api/wake/route.ts +41 -0
- package/apps/harness-dashboard/app/brick.css +482 -0
- package/apps/harness-dashboard/app/layout.tsx +13 -0
- package/apps/harness-dashboard/app/page.tsx +2 -2
- package/apps/harness-dashboard/components/Scene.tsx +690 -36
- package/apps/harness-dashboard/components/brick/BrickDashboard.tsx +149 -0
- package/apps/harness-dashboard/components/brick/reports.tsx +291 -0
- package/apps/harness-dashboard/components/brick/tweaks.tsx +738 -0
- package/apps/harness-dashboard/components/brick/ui.tsx +463 -0
- package/apps/harness-dashboard/components/brick/views-graph.tsx +305 -0
- package/apps/harness-dashboard/components/brick/views-grid.tsx +543 -0
- package/apps/harness-dashboard/components/brick/views-timeline.tsx +238 -0
- package/apps/harness-dashboard/lib/__tests__/harness-state.test.ts +1 -202
- package/apps/harness-dashboard/lib/__tests__/sandbox-e2e.test.ts +490 -0
- package/apps/harness-dashboard/lib/brick/adapter.ts +423 -0
- package/apps/harness-dashboard/lib/brick/contract.ts +247 -0
- package/apps/harness-dashboard/lib/harness-state.ts +107 -796
- package/apps/harness-dashboard/lib/metrics/sampler.ts +545 -0
- package/apps/harness-dashboard/lib/types.ts +22 -222
- package/apps/harness-dashboard/playwright.config.ts +26 -7
- package/assets/templates/AGENTS-ko.md.template +1 -1
- package/assets/templates/AGENTS.md.template +12 -2
- package/assets/templates/HARNESS.md +2 -0
- package/assets/templates/company-pipeline-manifest.json +5 -14
- package/assets/templates/worker-report.md.template +38 -0
- package/bin/init.js +2 -1
- package/commands/goal.md +4 -2
- package/commands/hot-fix.md +1 -1
- package/commands/submission.md +1 -1
- package/package.json +1 -1
- package/scripts/conductor-tick.sh +12 -0
- package/scripts/harness-agenda.sh +105 -0
- package/scripts/harness-company-block.sh +89 -0
- package/scripts/harness-company-complete.sh +17 -8
- package/scripts/harness-company-cycle.sh +54 -0
- package/scripts/harness-progress-set.sh +8 -2
- package/scripts/harness-stop.sh +76 -0
- package/scripts/harness-wake-control.sh +151 -0
- package/scripts/harness-wake.sh +5 -0
- package/scripts/harness-worker-dispatch.sh +31 -7
- package/apps/harness-dashboard/components/Drawer.tsx +0 -146
- package/apps/harness-dashboard/components/Header.tsx +0 -40
- package/apps/harness-dashboard/components/MissionTimeline.tsx +0 -214
- package/apps/harness-dashboard/components/OrgTree.tsx +0 -459
- package/apps/harness-dashboard/components/drawer/AgentLogTab.tsx +0 -56
- package/apps/harness-dashboard/components/drawer/DesignPreviewTab.tsx +0 -160
- package/apps/harness-dashboard/components/drawer/GotchasTab.tsx +0 -89
- package/apps/harness-dashboard/components/drawer/HypothesisTab.tsx +0 -42
- package/apps/harness-dashboard/components/drawer/IncidentsTab.tsx +0 -50
- package/apps/harness-dashboard/components/drawer/MissionDocTab.tsx +0 -96
- package/apps/harness-dashboard/components/drawer/MissionFlowTab.tsx +0 -576
- package/apps/harness-dashboard/components/drawer/OwnerHistoryTab.tsx +0 -160
- package/apps/harness-dashboard/components/drawer/RoomMetricsTab.tsx +0 -90
- package/apps/harness-dashboard/components/drawer/TracksTab.tsx +0 -47
- package/apps/harness-dashboard/components/three/ArchiveBoxes.tsx +0 -95
- package/apps/harness-dashboard/components/three/DeptBoard.tsx +0 -175
- package/apps/harness-dashboard/components/three/Floor3D.tsx +0 -404
- package/apps/harness-dashboard/components/three/GoalCard3D.tsx +0 -123
- package/apps/harness-dashboard/components/three/HypothesisCards.tsx +0 -71
- package/apps/harness-dashboard/components/three/MeetingWhiteboard.tsx +0 -98
- package/apps/harness-dashboard/components/three/Minifig3D.tsx +0 -391
- package/apps/harness-dashboard/components/three/OpsSiren.tsx +0 -99
- package/apps/harness-dashboard/components/three/Stage3D.tsx +0 -104
- package/apps/harness-dashboard/components/three/TrackRibbons.tsx +0 -81
- package/apps/harness-dashboard/lib/__tests__/i18n.test.ts +0 -16
- package/apps/harness-dashboard/lib/__tests__/iso.test.ts +0 -42
- package/apps/harness-dashboard/lib/__tests__/state-mapping.test.ts +0 -183
- package/apps/harness-dashboard/lib/agent-roster.ts +0 -42
- package/apps/harness-dashboard/lib/furniture.ts +0 -165
- package/apps/harness-dashboard/lib/i18n.ts +0 -26
- package/apps/harness-dashboard/lib/iso.ts +0 -150
- package/apps/harness-dashboard/lib/markdown.tsx +0 -306
- package/apps/harness-dashboard/lib/path-planning.ts +0 -72
- package/apps/harness-dashboard/lib/state-mapping.ts +0 -146
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/* =============================================================
|
|
4
|
+
walwal-harness · VIEW 2 — ORCHESTRATION (node-edge graph)
|
|
5
|
+
Ported from design_handoff_harness_dashboard/harness/views-graph.jsx.
|
|
6
|
+
Exports: GraphView, useSize
|
|
7
|
+
|
|
8
|
+
Renders the CEO -> exec -> worker node/edge graph (animated active
|
|
9
|
+
edges, ResizeObserver sizing) plus a detail panel for the selected
|
|
10
|
+
node. Every value comes from the real-data Contract (ContractState);
|
|
11
|
+
where the contract is honest about missing data (null heartbeat /
|
|
12
|
+
progress / event time) we render an em-dash, never a fake number.
|
|
13
|
+
============================================================= */
|
|
14
|
+
|
|
15
|
+
import React, { useState, useRef, useLayoutEffect } from "react";
|
|
16
|
+
import {
|
|
17
|
+
fmt,
|
|
18
|
+
statusColor,
|
|
19
|
+
type AgentRole,
|
|
20
|
+
type AgentStatus,
|
|
21
|
+
type ContractState,
|
|
22
|
+
type ContractAgent,
|
|
23
|
+
type ContractWorker,
|
|
24
|
+
type DocTarget,
|
|
25
|
+
} from "../../lib/brick/contract";
|
|
26
|
+
import { StatusDot, Label } from "./ui";
|
|
27
|
+
|
|
28
|
+
/* ---- sizing hook ------------------------------------------------------ */
|
|
29
|
+
|
|
30
|
+
export interface Size {
|
|
31
|
+
w: number;
|
|
32
|
+
h: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function useSize(ref: React.RefObject<HTMLElement | null>): Size {
|
|
36
|
+
const [size, setSize] = useState<Size>({ w: 1000, h: 600 });
|
|
37
|
+
useLayoutEffect(() => {
|
|
38
|
+
if (!ref.current) return;
|
|
39
|
+
const ro = new ResizeObserver((ents) => {
|
|
40
|
+
const r = ents[0].contentRect;
|
|
41
|
+
setSize({ w: r.width, h: r.height });
|
|
42
|
+
});
|
|
43
|
+
ro.observe(ref.current);
|
|
44
|
+
return () => ro.disconnect();
|
|
45
|
+
}, [ref]);
|
|
46
|
+
return size;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* ---- helpers ---------------------------------------------------------- */
|
|
50
|
+
|
|
51
|
+
interface Point {
|
|
52
|
+
x: number;
|
|
53
|
+
y: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Inline `--hue` custom property (kept as the mockup's per-node convention). */
|
|
57
|
+
type HueStyle = React.CSSProperties & { "--hue"?: string };
|
|
58
|
+
|
|
59
|
+
const isActive = (st: AgentStatus): boolean =>
|
|
60
|
+
st === "typing" || st === "talking" || st === "active" || st === "live";
|
|
61
|
+
|
|
62
|
+
const edge = (a: Point, b: Point): string =>
|
|
63
|
+
`M${a.x} ${a.y} C ${(a.x + b.x) / 2} ${a.y}, ${(a.x + b.x) / 2} ${b.y}, ${b.x} ${b.y}`;
|
|
64
|
+
|
|
65
|
+
/** Honest progress width: null -> 0 (no fabricated bar). */
|
|
66
|
+
const progWidth = (p: number | null): number => (p == null ? 0 : Math.round(p));
|
|
67
|
+
/** Honest progress label: null -> em-dash. */
|
|
68
|
+
const progLabel = (p: number | null): string => (p == null ? "—" : Math.round(p) + "%");
|
|
69
|
+
|
|
70
|
+
/* ---- props ------------------------------------------------------------ */
|
|
71
|
+
|
|
72
|
+
export interface GraphViewProps {
|
|
73
|
+
s: ContractState;
|
|
74
|
+
/** Selected node id (controlled). Falls back to an internal default. */
|
|
75
|
+
sel?: AgentRole;
|
|
76
|
+
setSel?: (id: AgentRole) => void;
|
|
77
|
+
/** Open a report document for an agent or worker. */
|
|
78
|
+
openDoc?: (target: DocTarget) => void;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* ---- GraphView -------------------------------------------------------- */
|
|
82
|
+
|
|
83
|
+
export function GraphView({ s, sel: selProp, setSel: setSelProp, openDoc }: GraphViewProps) {
|
|
84
|
+
const wrapRef = useRef<HTMLDivElement | null>(null);
|
|
85
|
+
const { w: W, h: H } = useSize(wrapRef);
|
|
86
|
+
const [selLocal, setSelLocal] = useState<AgentRole>("cto");
|
|
87
|
+
const sel = selProp ?? selLocal;
|
|
88
|
+
const setSel = setSelProp ?? setSelLocal;
|
|
89
|
+
|
|
90
|
+
const pad = 56;
|
|
91
|
+
const order: AgentRole[] = ["ceo", "coo", "cto", "cqo", "cdo", "ops"];
|
|
92
|
+
const execs: ContractAgent[] = order
|
|
93
|
+
.map((id) => s.agents.find((a) => a.id === id))
|
|
94
|
+
.filter((a): a is ContractAgent => Boolean(a));
|
|
95
|
+
|
|
96
|
+
// workers grouped by exec order
|
|
97
|
+
const workers: ContractWorker[] = [];
|
|
98
|
+
execs.forEach((e) =>
|
|
99
|
+
s.workers.filter((w) => w.agent === e.id).forEach((w) => workers.push(w))
|
|
100
|
+
);
|
|
101
|
+
const N = Math.max(workers.length, 1);
|
|
102
|
+
|
|
103
|
+
const xCEO = pad + 24;
|
|
104
|
+
const xExec = Math.max(260, W * 0.4);
|
|
105
|
+
const xWork = Math.max(520, W * 0.8);
|
|
106
|
+
|
|
107
|
+
const wpos: Record<string, Point> = {};
|
|
108
|
+
workers.forEach((w, i) => {
|
|
109
|
+
wpos[w.id] = { x: xWork, y: pad + ((i + 0.5) * (H - 2 * pad)) / N };
|
|
110
|
+
});
|
|
111
|
+
const epos: Record<string, Point> = {};
|
|
112
|
+
execs.forEach((e, i) => {
|
|
113
|
+
const mine = workers.filter((w) => w.agent === e.id);
|
|
114
|
+
const y = mine.length
|
|
115
|
+
? mine.reduce((acc, w) => acc + wpos[w.id].y, 0) / mine.length
|
|
116
|
+
: pad + ((i + 0.5) * (H - 2 * pad)) / execs.length;
|
|
117
|
+
epos[e.id] = { x: xExec, y };
|
|
118
|
+
});
|
|
119
|
+
const ceoPos: Point = { x: xCEO, y: H / 2 };
|
|
120
|
+
|
|
121
|
+
const selAgent: ContractAgent = s.agents.find((a) => a.id === sel) ?? execs[0];
|
|
122
|
+
const selWorkers = s.workers.filter((w) => w.agent === sel);
|
|
123
|
+
const selEvents = s.events.filter((e) => e.agent === sel).slice(-10).reverse();
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<div className="graphview">
|
|
127
|
+
<div className="graph-wrap" ref={wrapRef}>
|
|
128
|
+
<svg className="graph-edges" width={W} height={H}>
|
|
129
|
+
{execs.map((e) => {
|
|
130
|
+
const active = isActive(e.status);
|
|
131
|
+
return (
|
|
132
|
+
<path
|
|
133
|
+
key={"ce" + e.id}
|
|
134
|
+
d={edge(ceoPos, epos[e.id])}
|
|
135
|
+
className={"gedge" + (active ? " is-active" : "")}
|
|
136
|
+
style={{ stroke: e.hue, "--hue": e.hue } as HueStyle}
|
|
137
|
+
/>
|
|
138
|
+
);
|
|
139
|
+
})}
|
|
140
|
+
{workers.map((w) => {
|
|
141
|
+
const e = s.agents.find((a) => a.id === w.agent);
|
|
142
|
+
const active = isActive(w.status);
|
|
143
|
+
return (
|
|
144
|
+
<path
|
|
145
|
+
key={"ew" + w.id}
|
|
146
|
+
d={edge(epos[w.agent], wpos[w.id])}
|
|
147
|
+
className={"gedge gedge-w" + (active ? " is-active" : "")}
|
|
148
|
+
style={{ stroke: e?.hue, "--hue": e?.hue } as HueStyle}
|
|
149
|
+
/>
|
|
150
|
+
);
|
|
151
|
+
})}
|
|
152
|
+
</svg>
|
|
153
|
+
|
|
154
|
+
{/* CEO root */}
|
|
155
|
+
<div
|
|
156
|
+
className="gnode gnode-root"
|
|
157
|
+
style={{ left: ceoPos.x, top: ceoPos.y, "--hue": "#4cc2ff" } as HueStyle}
|
|
158
|
+
onClick={() => setSel("ceo")}
|
|
159
|
+
>
|
|
160
|
+
<span className="gnode-ring" />
|
|
161
|
+
<span className="glyph" style={{ color: "#4cc2ff", borderColor: "#4cc2ff66" }}>
|
|
162
|
+
CEO
|
|
163
|
+
</span>
|
|
164
|
+
<span className="gnode-cap">root loop</span>
|
|
165
|
+
</div>
|
|
166
|
+
|
|
167
|
+
{/* exec nodes */}
|
|
168
|
+
{execs
|
|
169
|
+
.filter((e) => e.id !== "ceo")
|
|
170
|
+
.map((e) => {
|
|
171
|
+
const active = isActive(e.status);
|
|
172
|
+
return (
|
|
173
|
+
<div
|
|
174
|
+
key={e.id}
|
|
175
|
+
className={
|
|
176
|
+
"gnode gnode-exec" +
|
|
177
|
+
(sel === e.id ? " is-sel" : "") +
|
|
178
|
+
(active ? " is-alive" : "")
|
|
179
|
+
}
|
|
180
|
+
style={{ left: epos[e.id].x, top: epos[e.id].y, "--hue": e.hue } as HueStyle}
|
|
181
|
+
onClick={() => setSel(e.id)}
|
|
182
|
+
>
|
|
183
|
+
{active && <span className="gnode-ring" />}
|
|
184
|
+
<span className="glyph" style={{ color: e.hue, borderColor: e.hue + "66" }}>
|
|
185
|
+
{e.name}
|
|
186
|
+
</span>
|
|
187
|
+
<span className="gnode-meta">
|
|
188
|
+
<StatusDot status={e.status} size={6} />
|
|
189
|
+
{e.workers}w
|
|
190
|
+
</span>
|
|
191
|
+
</div>
|
|
192
|
+
);
|
|
193
|
+
})}
|
|
194
|
+
|
|
195
|
+
{/* worker nodes */}
|
|
196
|
+
{workers.map((w) => {
|
|
197
|
+
const e = s.agents.find((a) => a.id === w.agent);
|
|
198
|
+
const hue = e?.hue;
|
|
199
|
+
const fresh = w.started != null && s.now - w.started < 4000;
|
|
200
|
+
return (
|
|
201
|
+
<div
|
|
202
|
+
key={w.id}
|
|
203
|
+
className={
|
|
204
|
+
"gnode gnode-worker" +
|
|
205
|
+
(sel === w.agent ? " is-dim-on" : "") +
|
|
206
|
+
(fresh ? " is-fresh" : "")
|
|
207
|
+
}
|
|
208
|
+
style={{ left: wpos[w.id].x, top: wpos[w.id].y, "--hue": hue } as HueStyle}
|
|
209
|
+
onClick={() => setSel(w.agent)}
|
|
210
|
+
title={w.name}
|
|
211
|
+
>
|
|
212
|
+
<span className="gw-dot" style={{ background: hue }} />
|
|
213
|
+
<span className="gw-name">{w.name}</span>
|
|
214
|
+
<span className="gw-prog">
|
|
215
|
+
<span style={{ width: progWidth(w.progress) + "%", background: hue }} />
|
|
216
|
+
</span>
|
|
217
|
+
</div>
|
|
218
|
+
);
|
|
219
|
+
})}
|
|
220
|
+
|
|
221
|
+
<div className="graph-legend">
|
|
222
|
+
<span>CEO → exec → worker · animated edge = active dispatch</span>
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
<aside className="graph-detail">
|
|
227
|
+
<Label right={selAgent.loop}>NODE · {selAgent.name}</Label>
|
|
228
|
+
<div className="gd-head" style={{ "--hue": selAgent.hue } as HueStyle}>
|
|
229
|
+
<span
|
|
230
|
+
className="glyph glyph-lg"
|
|
231
|
+
style={{ color: selAgent.hue, borderColor: selAgent.hue + "66" }}
|
|
232
|
+
>
|
|
233
|
+
{selAgent.name}
|
|
234
|
+
</span>
|
|
235
|
+
<div>
|
|
236
|
+
<div className="gd-role">{selAgent.role}</div>
|
|
237
|
+
<div className="gd-status" style={{ color: statusColor(selAgent.status) }}>
|
|
238
|
+
<StatusDot status={selAgent.status} size={7} />
|
|
239
|
+
{selAgent.status} · loop {selAgent.loop}
|
|
240
|
+
</div>
|
|
241
|
+
</div>
|
|
242
|
+
<button
|
|
243
|
+
className="gd-read"
|
|
244
|
+
onClick={() => openDoc && openDoc({ type: "agent", agent: selAgent.id })}
|
|
245
|
+
>
|
|
246
|
+
read report ↗
|
|
247
|
+
</button>
|
|
248
|
+
</div>
|
|
249
|
+
<div className="gd-work">{selAgent.work}</div>
|
|
250
|
+
<div className="gd-stats">
|
|
251
|
+
<div>
|
|
252
|
+
<b>{selWorkers.length}</b>
|
|
253
|
+
<span>workers</span>
|
|
254
|
+
</div>
|
|
255
|
+
<div>
|
|
256
|
+
<b>{selAgent.todos}</b>
|
|
257
|
+
<span>open todos</span>
|
|
258
|
+
</div>
|
|
259
|
+
<div>
|
|
260
|
+
<b>{selAgent.heartbeat == null ? "—" : fmt.ago(s.now - selAgent.heartbeat)}</b>
|
|
261
|
+
<span>heartbeat</span>
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
264
|
+
<Label>SPAWNED WORKERS</Label>
|
|
265
|
+
<div className="gd-workers">
|
|
266
|
+
{selWorkers.length === 0 && <div className="gd-empty">no active workers</div>}
|
|
267
|
+
{selWorkers.map((w) => (
|
|
268
|
+
<button
|
|
269
|
+
key={w.id}
|
|
270
|
+
className="gd-worker"
|
|
271
|
+
onClick={() => openDoc && openDoc({ type: "worker", agent: selAgent.id, worker: w })}
|
|
272
|
+
>
|
|
273
|
+
<StatusDot status={w.status} size={6} />
|
|
274
|
+
<span className="gd-wname">{w.name}</span>
|
|
275
|
+
<span className="gd-wprog">
|
|
276
|
+
<span className="prog-bar">
|
|
277
|
+
<span
|
|
278
|
+
className="prog-fill"
|
|
279
|
+
style={{ width: progWidth(w.progress) + "%", background: selAgent.hue }}
|
|
280
|
+
/>
|
|
281
|
+
</span>
|
|
282
|
+
{progLabel(w.progress)}
|
|
283
|
+
</span>
|
|
284
|
+
</button>
|
|
285
|
+
))}
|
|
286
|
+
</div>
|
|
287
|
+
<Label>RECENT · {selAgent.name}</Label>
|
|
288
|
+
<div className="gd-events">
|
|
289
|
+
{selEvents.map((e) => (
|
|
290
|
+
<button
|
|
291
|
+
key={e.id}
|
|
292
|
+
className="gd-ev"
|
|
293
|
+
onClick={() => openDoc && openDoc({ type: "agent", agent: selAgent.id })}
|
|
294
|
+
>
|
|
295
|
+
<span className="sl-time">{e.at == null ? "—" : fmt.time(new Date(e.at))}</span>
|
|
296
|
+
<span className="sl-msg">
|
|
297
|
+
{e.verb} <span className="sl-obj">`{e.obj}`</span>
|
|
298
|
+
</span>
|
|
299
|
+
</button>
|
|
300
|
+
))}
|
|
301
|
+
</div>
|
|
302
|
+
</aside>
|
|
303
|
+
</div>
|
|
304
|
+
);
|
|
305
|
+
}
|