@walwal-harness/cli 7.1.10 → 7.1.13
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
CHANGED
|
@@ -180,9 +180,10 @@ npx walwal-harness init --force --project-root /path/to/project
|
|
|
180
180
|
|
|
181
181
|
Features:
|
|
182
182
|
|
|
183
|
-
- **
|
|
183
|
+
- **Split Workspace** — full-viewport left/right panes keep Org Tree + Mission Timeline beside the selected mission detail; both panes scroll independently with hidden scrollbars, and the detail pane resets to top on selection changes
|
|
184
|
+
- **Org Tree** — live status of Owner → CEO → CXX → Workers hierarchy, with worker cards grouped under their owning CXX
|
|
184
185
|
- **Mission Timeline** — clickable history of goal/submission/hot-fix missions showing the full dispatch chain
|
|
185
|
-
- **Mission Flow tab** — per-mission flow: Owner prompt → CEO routing → CXX → worker
|
|
186
|
+
- **Mission Flow tab** — per-mission flow: Owner prompt → CEO routing → CXX → owner-specific worker evidence → CQO verdict
|
|
186
187
|
- **History tab** — mission-specific Owner request (from CEO summary + closest progress.log match)
|
|
187
188
|
- **Gotchas tab** — searchable `.harness/gotchas/*.md` knowledge base, click to read full markdown
|
|
188
189
|
- **Document tab** — per-CXX markdown doc viewer
|
|
@@ -221,6 +222,10 @@ harness-hiring → register and onboard worker
|
|
|
221
222
|
|
|
222
223
|
| Version | Summary |
|
|
223
224
|
|---|---|
|
|
225
|
+
| 7.1.13 | Dashboard: full-viewport split workspace, hidden pane scrollbars, and detail scroll reset on selection |
|
|
226
|
+
| 7.1.12 | Dashboard: split workspace panes now scroll independently |
|
|
227
|
+
| 7.1.11 | Dashboard: persistent 50/50 split workspace and CXX-owned worker grouping in the org tree |
|
|
228
|
+
| 7.1.10 | Adds `/submission` and nested goal/submission/hot-fix mission history |
|
|
224
229
|
| 7.1.9 | Dashboard is packaged with npm and auto-syncs isolated cache by package version; README documents dashboard port/reinstall and update/migrate commands |
|
|
225
230
|
| 7.1.8 | HR-Resource: replace China-platform-specific workers with global equivalents; refine cross-border/video/livestream skills |
|
|
226
231
|
| 7.1.7 | Implementation Notes mandatory in all CXX docs and worker reports; harness-worker-evidence-validate.sh |
|
|
@@ -15,3 +15,12 @@ body {
|
|
|
15
15
|
font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
|
|
16
16
|
sans-serif;
|
|
17
17
|
}
|
|
18
|
+
|
|
19
|
+
.scrollbar-hidden {
|
|
20
|
+
scrollbar-width: none;
|
|
21
|
+
-ms-overflow-style: none;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.scrollbar-hidden::-webkit-scrollbar {
|
|
25
|
+
display: none;
|
|
26
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { useEffect } from "react";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
3
|
|
|
4
4
|
export type DrawerTab =
|
|
5
5
|
| "mission-flow" // Mission document hierarchy (PRIMARY)
|
|
@@ -15,6 +15,8 @@ interface DrawerProps {
|
|
|
15
15
|
onClose: () => void;
|
|
16
16
|
onTabChange: (tab: DrawerTab) => void;
|
|
17
17
|
children: React.ReactNode;
|
|
18
|
+
mode?: "overlay" | "inline";
|
|
19
|
+
scrollResetKey?: string;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
const TABS: Array<{ id: DrawerTab; label: string }> = [
|
|
@@ -25,15 +27,60 @@ const TABS: Array<{ id: DrawerTab; label: string }> = [
|
|
|
25
27
|
{ id: "logs", label: "Agent Log" },
|
|
26
28
|
];
|
|
27
29
|
|
|
28
|
-
export function Drawer({ open, tab, title, onClose, onTabChange, children }: DrawerProps) {
|
|
30
|
+
export function Drawer({ open, tab, title, onClose, onTabChange, children, mode = "overlay", scrollResetKey }: DrawerProps) {
|
|
31
|
+
const contentRef = useRef<HTMLDivElement | null>(null);
|
|
32
|
+
|
|
29
33
|
useEffect(() => {
|
|
30
|
-
if (!open) return;
|
|
34
|
+
if (!open || mode === "inline") return;
|
|
31
35
|
const onKey = (e: KeyboardEvent) => {
|
|
32
36
|
if (e.key === "Escape") onClose();
|
|
33
37
|
};
|
|
34
38
|
window.addEventListener("keydown", onKey);
|
|
35
39
|
return () => window.removeEventListener("keydown", onKey);
|
|
36
|
-
}, [open, onClose]);
|
|
40
|
+
}, [open, onClose, mode]);
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
contentRef.current?.scrollTo({ top: 0, left: 0 });
|
|
44
|
+
}, [scrollResetKey]);
|
|
45
|
+
|
|
46
|
+
if (mode === "inline") {
|
|
47
|
+
return (
|
|
48
|
+
<aside
|
|
49
|
+
role="complementary"
|
|
50
|
+
aria-label="Detail panel"
|
|
51
|
+
data-testid="drawer"
|
|
52
|
+
data-open="true"
|
|
53
|
+
className="h-full min-h-0 rounded-md border border-brick-wall bg-brick-bg shadow-2xl flex flex-col overflow-hidden"
|
|
54
|
+
>
|
|
55
|
+
<header className="flex items-center justify-between border-b border-brick-wall px-4 py-3">
|
|
56
|
+
<h2 className="text-sm font-mono uppercase tracking-widest text-gray-200 truncate">
|
|
57
|
+
{title}
|
|
58
|
+
</h2>
|
|
59
|
+
</header>
|
|
60
|
+
<nav className="flex flex-wrap border-b border-brick-wall text-[11px] font-mono">
|
|
61
|
+
{TABS.map((t) => (
|
|
62
|
+
<button
|
|
63
|
+
key={t.id}
|
|
64
|
+
type="button"
|
|
65
|
+
data-testid={`drawer-tab-${t.id}`}
|
|
66
|
+
data-active={t.id === tab}
|
|
67
|
+
onClick={() => onTabChange(t.id)}
|
|
68
|
+
className={`min-w-[33%] flex-1 px-2 py-2 transition-colors ${
|
|
69
|
+
t.id === tab
|
|
70
|
+
? "bg-brick-wall/50 text-gray-100 border-b-2 border-aura-typing"
|
|
71
|
+
: "text-gray-500 hover:text-gray-200"
|
|
72
|
+
}`}
|
|
73
|
+
>
|
|
74
|
+
{t.label}
|
|
75
|
+
</button>
|
|
76
|
+
))}
|
|
77
|
+
</nav>
|
|
78
|
+
<div ref={contentRef} className="scrollbar-hidden flex-1 overflow-y-auto px-4 py-3 text-xs text-gray-200 font-mono">
|
|
79
|
+
{children}
|
|
80
|
+
</div>
|
|
81
|
+
</aside>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
37
84
|
|
|
38
85
|
return (
|
|
39
86
|
<>
|
|
@@ -88,7 +135,7 @@ export function Drawer({ open, tab, title, onClose, onTabChange, children }: Dra
|
|
|
88
135
|
</button>
|
|
89
136
|
))}
|
|
90
137
|
</nav>
|
|
91
|
-
<div className="flex-1 overflow-y-auto px-4 py-3 text-xs text-gray-200 font-mono">
|
|
138
|
+
<div ref={contentRef} className="scrollbar-hidden flex-1 overflow-y-auto px-4 py-3 text-xs text-gray-200 font-mono">
|
|
92
139
|
{children}
|
|
93
140
|
</div>
|
|
94
141
|
</aside>
|
|
@@ -225,7 +225,8 @@ export function OrgTree({ snapshot, activeNodeId, onNodeClick }: OrgTreeProps) {
|
|
|
225
225
|
},
|
|
226
226
|
];
|
|
227
227
|
|
|
228
|
-
// Workers
|
|
228
|
+
// Workers are grouped by their owning CXX so evaluator reports sit under CQO,
|
|
229
|
+
// implementation reports under CTO, and legacy flat reports stay visible.
|
|
229
230
|
const workers = currentMission?.workers ?? [];
|
|
230
231
|
|
|
231
232
|
return (
|
|
@@ -271,7 +272,9 @@ export function OrgTree({ snapshot, activeNodeId, onNodeClick }: OrgTreeProps) {
|
|
|
271
272
|
{/* Row 3: CXX nodes with workers below */}
|
|
272
273
|
<div className="flex gap-3 justify-center">
|
|
273
274
|
{cxxNodes.map((node) => {
|
|
274
|
-
const nodeWorkers =
|
|
275
|
+
const nodeWorkers = workers.filter(
|
|
276
|
+
(w) => w.owner === node.role || (w.owner === "unknown" && node.role === "cto")
|
|
277
|
+
);
|
|
275
278
|
|
|
276
279
|
return (
|
|
277
280
|
<div key={node.id} className="flex flex-col items-center gap-2">
|
|
@@ -293,7 +296,7 @@ export function OrgTree({ snapshot, activeNodeId, onNodeClick }: OrgTreeProps) {
|
|
|
293
296
|
onClick={() =>
|
|
294
297
|
onNodeClick({
|
|
295
298
|
id: `worker-${w.name}`,
|
|
296
|
-
role:
|
|
299
|
+
role: node.role,
|
|
297
300
|
label: w.name,
|
|
298
301
|
sublabel: w.status,
|
|
299
302
|
status: w.status === "COMPLETE" ? "idle" : "typing",
|
|
@@ -27,7 +27,6 @@ interface SceneProps {
|
|
|
27
27
|
|
|
28
28
|
export function Scene({ snapshot: initial, lang = "ko" }: SceneProps) {
|
|
29
29
|
const { snapshot, connectionState } = useHarnessStream(initial);
|
|
30
|
-
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
31
30
|
const [drawerTab, setDrawerTab] = useState<DrawerTab>("mission-flow");
|
|
32
31
|
const [selectedAgent, setSelectedAgent] = useState<AgentState | null>(null);
|
|
33
32
|
const [selectedNode, setSelectedNode] = useState<OrgNodeDef | null>(null);
|
|
@@ -38,7 +37,6 @@ export function Scene({ snapshot: initial, lang = "ko" }: SceneProps) {
|
|
|
38
37
|
setSelectedAgent(a);
|
|
39
38
|
setSelectedNode(null);
|
|
40
39
|
setDrawerTab("logs");
|
|
41
|
-
setDrawerOpen(true);
|
|
42
40
|
};
|
|
43
41
|
|
|
44
42
|
const handleNodeClick = (node: OrgNodeDef) => {
|
|
@@ -55,7 +53,6 @@ export function Scene({ snapshot: initial, lang = "ko" }: SceneProps) {
|
|
|
55
53
|
} else {
|
|
56
54
|
setDrawerTab("mission-flow");
|
|
57
55
|
}
|
|
58
|
-
setDrawerOpen(true);
|
|
59
56
|
};
|
|
60
57
|
|
|
61
58
|
const handleMissionClick = (mission: MissionDoc) => {
|
|
@@ -63,7 +60,6 @@ export function Scene({ snapshot: initial, lang = "ko" }: SceneProps) {
|
|
|
63
60
|
setSelectedNode(null);
|
|
64
61
|
setSelectedAgent(null);
|
|
65
62
|
setDrawerTab("mission-flow");
|
|
66
|
-
setDrawerOpen(true);
|
|
67
63
|
};
|
|
68
64
|
|
|
69
65
|
const drawerTitle =
|
|
@@ -74,60 +70,126 @@ export function Scene({ snapshot: initial, lang = "ko" }: SceneProps) {
|
|
|
74
70
|
: selectedNode
|
|
75
71
|
? selectedNode.label
|
|
76
72
|
: "Mission Flow";
|
|
73
|
+
const drawerScrollResetKey = [
|
|
74
|
+
drawerTab,
|
|
75
|
+
selectedMission?.missionId ?? "",
|
|
76
|
+
selectedNode?.id ?? "",
|
|
77
|
+
selectedAgent?.id ?? "",
|
|
78
|
+
].join(":");
|
|
77
79
|
|
|
78
80
|
// Suppress unused lang warning — kept for future i18n
|
|
79
81
|
void lang;
|
|
80
82
|
|
|
81
83
|
return (
|
|
82
|
-
<div className="
|
|
84
|
+
<div className="flex h-[100dvh] w-full flex-col overflow-hidden px-4 py-5">
|
|
83
85
|
{snapshot.escalations.length > 0 && (
|
|
84
86
|
<EscalationStrip escalations={snapshot.escalations} />
|
|
85
87
|
)}
|
|
86
|
-
<div className="min-w-0">
|
|
87
|
-
<
|
|
88
|
-
<
|
|
89
|
-
<div>
|
|
90
|
-
<
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
88
|
+
<div className="grid min-h-0 flex-1 min-w-0 gap-4 lg:grid-cols-2">
|
|
89
|
+
<div className="scrollbar-hidden min-h-0 min-w-0 overflow-y-auto pr-1">
|
|
90
|
+
<section className="min-w-0">
|
|
91
|
+
<div className="mb-3 flex flex-wrap items-end justify-between gap-3">
|
|
92
|
+
<div>
|
|
93
|
+
<p className="text-[11px] font-mono uppercase tracking-[0.24em] text-cyan-300/80">
|
|
94
|
+
Company Structure
|
|
95
|
+
</p>
|
|
96
|
+
<h1 className="mt-1 text-2xl font-semibold text-gray-100">
|
|
97
|
+
{snapshot.projectName || "walwal-harness"} · Organization
|
|
98
|
+
</h1>
|
|
99
|
+
</div>
|
|
100
|
+
<ActivityIndicator snapshot={snapshot} connectionState={connectionState} />
|
|
96
101
|
</div>
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
102
|
+
<div
|
|
103
|
+
data-testid="brick-office-stage"
|
|
104
|
+
className="w-full overflow-x-auto rounded-md border border-gray-700/80 bg-[#12151b] shadow-2xl"
|
|
105
|
+
>
|
|
106
|
+
<div className="pointer-events-none flex items-center justify-between gap-3 border-b border-white/10 bg-black/30 px-4 py-2 font-mono text-[10px] text-gray-300 backdrop-blur">
|
|
107
|
+
<span>Owner → CEO → CXX → Workers</span>
|
|
108
|
+
<span className="truncate">
|
|
109
|
+
{snapshot.missions?.[0]?.missionId ?? "no active mission"}
|
|
110
|
+
{snapshot.missions?.[0]?.cxxPresent.length
|
|
111
|
+
? ` · ${snapshot.missions[0].cxxPresent.join(" / ")}`
|
|
112
|
+
: ""}
|
|
113
|
+
</span>
|
|
114
|
+
</div>
|
|
115
|
+
<OrgTree
|
|
116
|
+
snapshot={snapshot}
|
|
117
|
+
activeNodeId={selectedNode?.id ?? null}
|
|
118
|
+
onNodeClick={handleNodeClick}
|
|
119
|
+
/>
|
|
120
|
+
</div>
|
|
121
|
+
</section>
|
|
122
|
+
|
|
123
|
+
<section className="mt-4">
|
|
124
|
+
<div className="mb-2">
|
|
125
|
+
<p className="font-mono text-[10px] uppercase tracking-[0.2em] text-cyan-300/60">Mission History</p>
|
|
126
|
+
<p className="text-[11px] text-gray-500 mt-0.5">goal · submission · hot-fix 명령별 파생 작업 흐름</p>
|
|
111
127
|
</div>
|
|
112
|
-
<
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
128
|
+
<MissionTimeline
|
|
129
|
+
missions={snapshot.missions ?? []}
|
|
130
|
+
activeMissionId={selectedMission?.missionId ?? null}
|
|
131
|
+
onMissionClick={handleMissionClick}
|
|
116
132
|
/>
|
|
117
|
-
</
|
|
118
|
-
</
|
|
133
|
+
</section>
|
|
134
|
+
</div>
|
|
119
135
|
|
|
120
|
-
<
|
|
121
|
-
<
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
136
|
+
<div className="min-h-0 min-w-0">
|
|
137
|
+
<Drawer
|
|
138
|
+
mode="inline"
|
|
139
|
+
open={true}
|
|
140
|
+
tab={drawerTab}
|
|
141
|
+
title={drawerTitle}
|
|
142
|
+
onClose={() => undefined}
|
|
143
|
+
onTabChange={setDrawerTab}
|
|
144
|
+
scrollResetKey={drawerScrollResetKey}
|
|
145
|
+
>
|
|
146
|
+
{drawerTab === "mission-flow" && (
|
|
147
|
+
<MissionFlowTab
|
|
148
|
+
mission={selectedMission ?? snapshot.missions?.[0] ?? null}
|
|
149
|
+
ownerHistory={snapshot.ownerHistory ?? []}
|
|
150
|
+
/>
|
|
151
|
+
)}
|
|
152
|
+
{drawerTab === "history" && (
|
|
153
|
+
<OwnerHistoryTab
|
|
154
|
+
ownerHistory={snapshot.ownerHistory ?? []}
|
|
155
|
+
mission={selectedMission ?? snapshot.missions?.[0] ?? null}
|
|
156
|
+
/>
|
|
157
|
+
)}
|
|
158
|
+
{drawerTab === "gotchas" && (
|
|
159
|
+
<GotchasTab gotchas={snapshot.gotchas ?? []} />
|
|
160
|
+
)}
|
|
161
|
+
{drawerTab === "mission-doc" && selectedNode && (
|
|
162
|
+
<MissionDocTab
|
|
163
|
+
missions={snapshot.missions ?? []}
|
|
164
|
+
role={
|
|
165
|
+
selectedNode.role === "owner"
|
|
166
|
+
? "ceo"
|
|
167
|
+
: (selectedNode.role as "ceo" | "cto" | "cqo" | "coo" | "cdo" | "ops")
|
|
168
|
+
}
|
|
169
|
+
fromLabel={selectedNode.role === "ceo" ? "Owner" : "CEO"}
|
|
170
|
+
toLabel={
|
|
171
|
+
selectedNode.role === "cto"
|
|
172
|
+
? "Workers"
|
|
173
|
+
: selectedNode.role === "cqo"
|
|
174
|
+
? "Owner Report"
|
|
175
|
+
: "—"
|
|
176
|
+
}
|
|
177
|
+
/>
|
|
178
|
+
)}
|
|
179
|
+
{drawerTab === "logs" && selectedNode && (
|
|
180
|
+
<AgentLogTab agentId={selectedNode.agentIds[0] ?? selectedNode.id} />
|
|
181
|
+
)}
|
|
182
|
+
{drawerTab === "logs" && selectedAgent && !selectedNode && (
|
|
183
|
+
<AgentLogTab agentId={selectedAgent.id} />
|
|
184
|
+
)}
|
|
185
|
+
{drawerTab === "logs" && !selectedNode && !selectedAgent && (
|
|
186
|
+
<div className="text-gray-500 text-xs">Select a node to view logs.</div>
|
|
187
|
+
)}
|
|
188
|
+
{drawerTab === "mission-flow" && !selectedMission && !snapshot.missions?.length && (
|
|
189
|
+
<div className="text-gray-500 text-xs">No missions found in .harness/documents/</div>
|
|
190
|
+
)}
|
|
191
|
+
</Drawer>
|
|
192
|
+
</div>
|
|
131
193
|
</div>
|
|
132
194
|
|
|
133
195
|
{/* Hidden DOM index for E2E + a11y */}
|
|
@@ -153,60 +215,6 @@ export function Scene({ snapshot: initial, lang = "ko" }: SceneProps) {
|
|
|
153
215
|
</li>
|
|
154
216
|
))}
|
|
155
217
|
</ul>
|
|
156
|
-
|
|
157
|
-
<Drawer
|
|
158
|
-
open={drawerOpen}
|
|
159
|
-
tab={drawerTab}
|
|
160
|
-
title={drawerTitle}
|
|
161
|
-
onClose={() => setDrawerOpen(false)}
|
|
162
|
-
onTabChange={setDrawerTab}
|
|
163
|
-
>
|
|
164
|
-
{drawerTab === "mission-flow" && (
|
|
165
|
-
<MissionFlowTab
|
|
166
|
-
mission={selectedMission ?? snapshot.missions?.[0] ?? null}
|
|
167
|
-
ownerHistory={snapshot.ownerHistory ?? []}
|
|
168
|
-
/>
|
|
169
|
-
)}
|
|
170
|
-
{drawerTab === "history" && (
|
|
171
|
-
<OwnerHistoryTab
|
|
172
|
-
ownerHistory={snapshot.ownerHistory ?? []}
|
|
173
|
-
mission={selectedMission ?? snapshot.missions?.[0] ?? null}
|
|
174
|
-
/>
|
|
175
|
-
)}
|
|
176
|
-
{drawerTab === "gotchas" && (
|
|
177
|
-
<GotchasTab gotchas={snapshot.gotchas ?? []} />
|
|
178
|
-
)}
|
|
179
|
-
{drawerTab === "mission-doc" && selectedNode && (
|
|
180
|
-
<MissionDocTab
|
|
181
|
-
missions={snapshot.missions ?? []}
|
|
182
|
-
role={
|
|
183
|
-
selectedNode.role === "owner"
|
|
184
|
-
? "ceo"
|
|
185
|
-
: (selectedNode.role as "ceo" | "cto" | "cqo" | "coo" | "cdo" | "ops")
|
|
186
|
-
}
|
|
187
|
-
fromLabel={selectedNode.role === "ceo" ? "Owner" : "CEO"}
|
|
188
|
-
toLabel={
|
|
189
|
-
selectedNode.role === "cto"
|
|
190
|
-
? "Workers"
|
|
191
|
-
: selectedNode.role === "cqo"
|
|
192
|
-
? "Owner Report"
|
|
193
|
-
: "—"
|
|
194
|
-
}
|
|
195
|
-
/>
|
|
196
|
-
)}
|
|
197
|
-
{drawerTab === "logs" && selectedNode && (
|
|
198
|
-
<AgentLogTab agentId={selectedNode.agentIds[0] ?? selectedNode.id} />
|
|
199
|
-
)}
|
|
200
|
-
{drawerTab === "logs" && selectedAgent && !selectedNode && (
|
|
201
|
-
<AgentLogTab agentId={selectedAgent.id} />
|
|
202
|
-
)}
|
|
203
|
-
{drawerTab === "logs" && !selectedNode && !selectedAgent && (
|
|
204
|
-
<div className="text-gray-500 text-xs">Select a node to view logs.</div>
|
|
205
|
-
)}
|
|
206
|
-
{drawerTab === "mission-flow" && !selectedMission && !snapshot.missions?.length && (
|
|
207
|
-
<div className="text-gray-500 text-xs">No missions found in .harness/documents/</div>
|
|
208
|
-
)}
|
|
209
|
-
</Drawer>
|
|
210
218
|
</div>
|
|
211
219
|
);
|
|
212
220
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@walwal-harness/cli",
|
|
3
|
-
"version": "7.1.
|
|
3
|
+
"version": "7.1.13",
|
|
4
4
|
"description": "Company-style AI agent harness for Claude and Codex. Installs commands, CXX agents, skills, HR-Resource hiring pool, and project-local .harness runtime state.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"walwal-harness": "bin/init.js"
|