Skip to content

Commit c6401d3

Browse files
feat: add dotagents view to launch HarnessKit read-only inspector
1 parent e4a244c commit c6401d3

6 files changed

Lines changed: 113 additions & 12 deletions

File tree

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,13 @@ dotagents setup [--memory off|basic|memsearch] [--yes] [--dry-run] [--json]
8181
dotagents status [--agents ...]
8282
dotagents sync [--pull] [--agents ...]
8383
dotagents doctor [--e2e] [--agents ...]
84+
dotagents view [--port N] [--host ADDR] # launch HarnessKit (read-only inspector)
8485
dotagents skill new|update|promote
8586
dotagents mcp list|add|import|remove
8687
```
8788

89+
`dotagents view` shells out to [HarnessKit](https://github.com/RealZST/HarnessKit) (`hk serve`) for a read-only web UI over every detected harness — skills, MCP servers, hooks, and configs in one place. dotagents stays the only writer; use it to inspect and audit, not to deploy. Install HarnessKit separately.
90+
8891
## Configuration
8992

9093
`~/.agents/dotagents.yaml` is the single source of truth; `setup` fills in detected harnesses. Resolution order: `--config <path>``$DOTAGENTS_HOME/dotagents.yaml``~/.agents/dotagents.yaml`; never walks the current project. Machine-local entries overlay via `dotagents.local.yaml`. Managed entries are marked in native configs; anything else is left untouched.

cmd/dotagents/main.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ func run(args []string) error {
139139
return runSyncCommand(args[1:])
140140
case "doctor":
141141
return runDoctorCommand(args[1:])
142+
case "view":
143+
return runView(args[1:])
142144
case "skill":
143145
return runSkillCommand(args[1:])
144146
case "mcp":
@@ -490,6 +492,7 @@ func printAllUsage() {
490492
fmt.Println(" dotagents status [--agents ...]")
491493
fmt.Println(" dotagents sync [--pull] [--agents ...]")
492494
fmt.Println(" dotagents doctor [--e2e] [--agents ...]")
495+
fmt.Println(" dotagents view [hk serve flags: --port N, --host ADDR, --no-token]")
493496
fmt.Println(" dotagents skill new <name> [--description ...]")
494497
fmt.Println(" dotagents skill update [name ...]")
495498
fmt.Println(" dotagents skill promote <name-or-path> [--dry-run]")

cmd/dotagents/view.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package main
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"os"
7+
"os/exec"
8+
)
9+
10+
// hkBinary is the HarnessKit CLI that `dotagents view` launches as a read-only
11+
// cross-harness inspector. dotagents stays the only writer of the five managed
12+
// surfaces; HarnessKit is consumed purely as a viewer over the materialized
13+
// native harness directories, so `view` never mutates managed config.
14+
const hkBinary = "hk"
15+
16+
// hkLookPath is indirected so tests can exercise the missing-binary path
17+
// without depending on the host PATH.
18+
var hkLookPath = exec.LookPath
19+
20+
const hkInstallHint = `HarnessKit (hk) not found on PATH.
21+
22+
dotagents view launches HarnessKit as a read-only cross-harness inspector for
23+
skills, MCP servers, hooks, and configs across every detected agent.
24+
25+
Install it from https://github.com/RealZST/HarnessKit, then re-run "dotagents view".`
26+
27+
// hkServeArgs builds the argv for the underlying `hk serve` invocation. Extra
28+
// args are forwarded verbatim to hk serve (e.g. --port, --host, --no-token).
29+
func hkServeArgs(passthrough []string) []string {
30+
return append([]string{"serve"}, passthrough...)
31+
}
32+
33+
// runView launches the HarnessKit web UI over the materialized native harness
34+
// dirs. Read-only by intent: dotagents remains the source of truth, so this
35+
// command inspects but never writes managed surfaces.
36+
func runView(args []string) error {
37+
path, err := hkLookPath(hkBinary)
38+
if err != nil {
39+
return errors.New(hkInstallHint)
40+
}
41+
fmt.Fprintln(os.Stdout, "Launching HarnessKit (read-only). dotagents stays the source of truth — avoid HarnessKit's enable/disable/deploy actions on dotagents-managed skills, MCP, and hooks.")
42+
cmd := exec.Command(path, hkServeArgs(args)...) // nosemgrep: go.lang.security.audit.dangerous-exec-command
43+
cmd.Stdin = os.Stdin
44+
cmd.Stdout = os.Stdout
45+
cmd.Stderr = os.Stderr
46+
return cmd.Run()
47+
}

cmd/dotagents/view_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package main
2+
3+
import (
4+
"errors"
5+
"reflect"
6+
"strings"
7+
"testing"
8+
)
9+
10+
func TestHKServeArgs(t *testing.T) {
11+
if got := hkServeArgs(nil); !reflect.DeepEqual(got, []string{"serve"}) {
12+
t.Fatalf("hkServeArgs(nil) = %v, want [serve]", got)
13+
}
14+
got := hkServeArgs([]string{"--port", "8080"})
15+
want := []string{"serve", "--port", "8080"}
16+
if !reflect.DeepEqual(got, want) {
17+
t.Fatalf("hkServeArgs passthrough = %v, want %v", got, want)
18+
}
19+
}
20+
21+
func TestRunViewMissingBinary(t *testing.T) {
22+
orig := hkLookPath
23+
t.Cleanup(func() { hkLookPath = orig })
24+
hkLookPath = func(string) (string, error) { return "", errors.New("not found") }
25+
26+
err := runView(nil)
27+
if err == nil {
28+
t.Fatal("expected error when hk binary is missing")
29+
}
30+
if !strings.Contains(err.Error(), "HarnessKit") || !strings.Contains(err.Error(), "github.com/RealZST/HarnessKit") {
31+
t.Fatalf("error should guide install, got: %v", err)
32+
}
33+
}

docs/harnesskit-integration.md

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Status: draft / thinking. Branch: `feat/harnesskit-integration`. Date: 2026-09-0
44

55
## Finding
66

7-
[HarnessKit](https://github.com/RealZST/HarnessKit) (RealZST/HarnessKit, Rust, Apache-2.0, ~420★, active) is a web UI (also desktop/CLI) that inspects and manages agent extensions, configs, memory, and rules across 13 harnesses. Verified live against the maintainer's machine on 2026-09-06: it detects and reads the **full dotagents stack** — Claude Code, Codex, **Oh My Pi** (`~/.omp/agent/`), **Hermes** (`~/.hermes/`), plus Gemini CLI, Copilot, OpenCode, Grok Build. This is exactly the coverage (Pi/OMP + Hermes) that CCO and ai-config-sync-manager lack.
7+
[HarnessKit](https://github.com/RealZST/HarnessKit) (RealZST/HarnessKit, Rust, Apache-2.0, ~420★, active) is a web UI (also desktop/CLI) that inspects and manages agent extensions, configs, memory, and rules across 13 harnesses. Verified live against a full stack install on 2026-09-06: it detects and reads the **full dotagents stack** — Claude Code, Codex, **Oh My Pi** (`~/.omp/agent/`), **Hermes** (`~/.hermes/`), plus Gemini CLI, Copilot, OpenCode, Grok Build. This is exactly the coverage (Pi/OMP + Hermes) that CCO and ai-config-sync-manager lack.
88

99
Consequence: dotagents does **not** need to build its own config viewer. HarnessKit already does the read/inspect/audit surface better than we would from scratch, and on every harness we care about.
1010

@@ -60,19 +60,25 @@ Both risk exactly the drift the boundary invariant forbids. Treat L3 as a spike
6060
- `cmd/dotagents/detect.go`, `harness.go` — harness detection; useful if HK install is only offered when ≥1 supported harness is present.
6161
- `cmd/dotagents/report.go`, `inspect.go` — the `status`/`inspect` output; where a "view in HarnessKit" pointer could surface.
6262

63-
## Open questions (need verification, do not guess)
63+
## Open questions
6464

65-
1. **HK install method** — GitHub release binary vs `cargo install` vs `brew`. Check the repo's releases/install docs before wiring L1.
66-
2. **HK headless/CLI mode** — does HK expose a non-interactive "start server on port X, no onboarding" invocation suitable for `dotagents view`? The web UI ran a 3-step onboarding wizard on first open; confirm it can be skipped/scripted.
67-
3. **Config-root targeting** — can HK be pointed at an arbitrary config root (for `--config`/`$DOTAGENTS_HOME` setups), or does it only scan default `~/.<agent>` paths? Observed it reading default paths (`~/.omp`, `~/.hermes`); custom-root support unconfirmed.
68-
4. **Pluggable write backend (L3)** — does HK have any hook/API to delegate mutations? Assume no until shown.
69-
5. **Publish-age policy fit** — HK releases cadence vs the 3-day external-package rule; pick a window.
65+
Resolved 2026-09-06 by inspecting `hk` 1.10.0 (`hk --help`, `hk serve --help`, `hk list --help`):
66+
67+
1. **HK headless/CLI mode — RESOLVED (yes).** `hk serve` is fully scriptable: `--port`, `--host`, `--token`/`--no-token`, `--name`. The 3-step onboarding is client-side UI state, not a server gate. HK also ships a pure CLI — `hk status`, `hk list --json`, `hk audit`, `hk info`, `hk enable/disable` — so a future text/status integration can consume `hk list --json` without the web server.
68+
2. **Config-root targeting — RESOLVED (no flag, not needed).** `hk serve`/`hk list` have no `--config`/`--root`; only `HK_SCOPE_LAST_USED` (scope memory). HK reads the native harness homes (`~/.claude`, `~/.codex`, `~/.omp`, `~/.hermes`), which is exactly what dotagents materializes — so `view` targets the right thing for the default `~/.agents` root. Custom `$DOTAGENTS_HOME`/`--config` only relocates dotagents' YAML, not the harness homes HK reads, so no retargeting is required.
69+
3. **HK binary/version — RESOLVED.** `hk` 1.10.0, Mach-O arm64, installed at `~/.local/bin/hk`.
70+
71+
Still open (gate L1, not L2):
72+
73+
4. **HK install method (L1)** — release binary vs `cargo install` vs `brew` tap. Verify from HK's releases/install docs before wiring an opt-in installer.
74+
5. **Publish-age policy fit (L1)** — HK's release cadence vs the 3-day external-package rule (`package_age.go`); pick a window.
75+
6. **Pluggable write backend (L3)** — does HK expose any hook/API to delegate mutations? Assume no until shown.
7076

7177
## Recommended first slice
7278

73-
Ship **L0 + L2** first; hold L1's auto-install behind the package-age answer:
79+
**L0 + L2, now unblocked** (open questions #1#3 resolved in HK's favor):
7480

75-
1. L0 docs pointer (README + comparison + skill).
76-
2. `dotagents view` that launches HK against the resolved config root, read-only framing. Answer open questions #2 and #3 as part of this slice.
77-
3. Then L1 opt-in install once #1 and #5 are settled.
78-
4. L3: separate spike doc, no code, decision recorded here.
81+
1. L0 docs pointer README + `dotagents` SKILL + CLI help. Pointer only, no duplicated harness-compat table.
82+
2. `dotagents view` — thin launcher: `exec.LookPath("hk")`, forward args to `hk serve`, read-only framing, install hint when absent. Implemented on this branch (`cmd/dotagents/view.go`, `view_test.go`).
83+
3. L1 opt-in install — deferred until #4 and #5 are settled.
84+
4. L3separate spike, no code; decision recorded here.

skills/dotagents/SKILL.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ dotagents setup [--memory off|basic|memsearch] [--agents ...] [--yes] [--dry-run
2222
dotagents status [--agents ...]
2323
dotagents sync [--pull] [--agents ...]
2424
dotagents doctor [--e2e] [--agents ...]
25+
dotagents view [--port N] [--host ADDR]
2526
dotagents skill new <name> [--description ...]
2627
dotagents skill update [name ...]
2728
dotagents skill promote <name-or-path> [--dry-run]
@@ -119,6 +120,14 @@ Runs sync, status, and doctor as one health check. It fails on drift, conflicts,
119120
dotagents doctor --e2e
120121
```
121122

123+
## view
124+
125+
Launches [HarnessKit](https://github.com/RealZST/HarnessKit) (`hk serve`) as a read-only web UI over every detected harness — skills, MCP, hooks, and configs in one place, with a security audit. dotagents stays the only writer; `view` is for inspection and audit, not deployment. Requires `hk` on `PATH` (install HarnessKit separately); flags are forwarded to `hk serve`.
126+
127+
```bash
128+
dotagents view --port 7070
129+
```
130+
122131
## Capability matrix
123132

124133
| Harness | Skills | Roles | MCP | Hooks |

0 commit comments

Comments
 (0)