harnery 0.0.1 → 0.2.1
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/LICENSE +21 -0
- package/README.md +84 -2
- package/bin/agent-coord +42 -0
- package/bin/agent-hook +44 -0
- package/bin/harn +40 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +18 -0
- package/dist/commander.d.ts +128 -0
- package/dist/commander.d.ts.map +1 -0
- package/dist/commander.js +126 -0
- package/dist/commands/agents.d.ts +18 -0
- package/dist/commands/agents.d.ts.map +1 -0
- package/dist/commands/agents.js +3946 -0
- package/dist/commands/backup.d.ts +22 -0
- package/dist/commands/backup.d.ts.map +1 -0
- package/dist/commands/backup.js +262 -0
- package/dist/commands/browse-ai.d.ts +4 -0
- package/dist/commands/browse-ai.d.ts.map +1 -0
- package/dist/commands/browse-ai.js +156 -0
- package/dist/commands/browse.d.ts +4 -0
- package/dist/commands/browse.d.ts.map +1 -0
- package/dist/commands/browse.js +590 -0
- package/dist/commands/callers.d.ts +4 -0
- package/dist/commands/callers.d.ts.map +1 -0
- package/dist/commands/callers.js +276 -0
- package/dist/commands/completion.d.ts +17 -0
- package/dist/commands/completion.d.ts.map +1 -0
- package/dist/commands/completion.js +158 -0
- package/dist/commands/config-get.d.ts +4 -0
- package/dist/commands/config-get.d.ts.map +1 -0
- package/dist/commands/config-get.js +131 -0
- package/dist/commands/context.d.ts +11 -0
- package/dist/commands/context.d.ts.map +1 -0
- package/dist/commands/context.js +185 -0
- package/dist/commands/cookies.d.ts +4 -0
- package/dist/commands/cookies.d.ts.map +1 -0
- package/dist/commands/cookies.js +140 -0
- package/dist/commands/docs.d.ts +4 -0
- package/dist/commands/docs.d.ts.map +1 -0
- package/dist/commands/docs.js +137 -0
- package/dist/commands/doctor.d.ts +25 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +200 -0
- package/dist/commands/edit-batch.d.ts +18 -0
- package/dist/commands/edit-batch.d.ts.map +1 -0
- package/dist/commands/edit-batch.js +172 -0
- package/dist/commands/eml.d.ts +4 -0
- package/dist/commands/eml.d.ts.map +1 -0
- package/dist/commands/eml.js +428 -0
- package/dist/commands/env.d.ts +4 -0
- package/dist/commands/env.d.ts.map +1 -0
- package/dist/commands/env.js +201 -0
- package/dist/commands/fetch.d.ts +4 -0
- package/dist/commands/fetch.d.ts.map +1 -0
- package/dist/commands/fetch.js +99 -0
- package/dist/commands/file-history.d.ts +4 -0
- package/dist/commands/file-history.d.ts.map +1 -0
- package/dist/commands/file-history.js +152 -0
- package/dist/commands/grep.d.ts +4 -0
- package/dist/commands/grep.d.ts.map +1 -0
- package/dist/commands/grep.js +317 -0
- package/dist/commands/init.d.ts +82 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +288 -0
- package/dist/commands/outline.d.ts +4 -0
- package/dist/commands/outline.d.ts.map +1 -0
- package/dist/commands/outline.js +509 -0
- package/dist/commands/presence.d.ts +12 -0
- package/dist/commands/presence.d.ts.map +1 -0
- package/dist/commands/presence.js +123 -0
- package/dist/commands/read.d.ts +7 -0
- package/dist/commands/read.d.ts.map +1 -0
- package/dist/commands/read.js +46 -0
- package/dist/commands/scratch.d.ts +4 -0
- package/dist/commands/scratch.d.ts.map +1 -0
- package/dist/commands/scratch.js +426 -0
- package/dist/commands/session.d.ts +4 -0
- package/dist/commands/session.d.ts.map +1 -0
- package/dist/commands/session.js +162 -0
- package/dist/commands/sync.d.ts +24 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +275 -0
- package/dist/commands/toc.d.ts +5 -0
- package/dist/commands/toc.d.ts.map +1 -0
- package/dist/commands/toc.js +153 -0
- package/dist/commands/tokens.d.ts +4 -0
- package/dist/commands/tokens.d.ts.map +1 -0
- package/dist/commands/tokens.js +48 -0
- package/dist/commands/tunnel.d.ts +4 -0
- package/dist/commands/tunnel.d.ts.map +1 -0
- package/dist/commands/tunnel.js +513 -0
- package/dist/commands/uninstall.d.ts +22 -0
- package/dist/commands/uninstall.d.ts.map +1 -0
- package/dist/commands/uninstall.js +126 -0
- package/dist/commands/web.d.ts +4 -0
- package/dist/commands/web.d.ts.map +1 -0
- package/dist/commands/web.js +165 -0
- package/dist/core/agents/canonical-emit.d.ts +27 -0
- package/dist/core/agents/canonical-emit.d.ts.map +1 -0
- package/dist/core/agents/canonical-emit.js +72 -0
- package/dist/core/agents/cli-emit.d.ts +27 -0
- package/dist/core/agents/cli-emit.d.ts.map +1 -0
- package/dist/core/agents/cli-emit.js +57 -0
- package/dist/core/agents/cli.d.ts +10 -0
- package/dist/core/agents/cli.d.ts.map +1 -0
- package/dist/core/agents/cli.js +757 -0
- package/dist/core/agents/codex-replay.d.ts +29 -0
- package/dist/core/agents/codex-replay.d.ts.map +1 -0
- package/dist/core/agents/codex-replay.js +138 -0
- package/dist/core/agents/coord-client.d.ts +98 -0
- package/dist/core/agents/coord-client.d.ts.map +1 -0
- package/dist/core/agents/coord-client.js +212 -0
- package/dist/core/agents/events/consume.d.ts +59 -0
- package/dist/core/agents/events/consume.d.ts.map +1 -0
- package/dist/core/agents/events/consume.js +147 -0
- package/dist/core/agents/events/emit.d.ts +42 -0
- package/dist/core/agents/events/emit.d.ts.map +1 -0
- package/dist/core/agents/events/emit.js +70 -0
- package/dist/core/agents/events/ulid.d.ts +11 -0
- package/dist/core/agents/events/ulid.d.ts.map +1 -0
- package/dist/core/agents/events/ulid.js +47 -0
- package/dist/core/agents/index.d.ts +14 -0
- package/dist/core/agents/index.d.ts.map +1 -0
- package/dist/core/agents/index.js +13 -0
- package/dist/core/agents/paths.d.ts +6 -0
- package/dist/core/agents/paths.d.ts.map +1 -0
- package/dist/core/agents/paths.js +17 -0
- package/dist/core/agents/render/prompt-context.d.ts +43 -0
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -0
- package/dist/core/agents/render/prompt-context.js +335 -0
- package/dist/core/agents/render/session-context.d.ts +39 -0
- package/dist/core/agents/render/session-context.d.ts.map +1 -0
- package/dist/core/agents/render/session-context.js +283 -0
- package/dist/core/agents/rules/claim-conflict.d.ts +35 -0
- package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -0
- package/dist/core/agents/rules/claim-conflict.js +244 -0
- package/dist/core/agents/rules/commit-conflict.d.ts +59 -0
- package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -0
- package/dist/core/agents/rules/commit-conflict.js +244 -0
- package/dist/core/agents/rules/stop-hook.d.ts +44 -0
- package/dist/core/agents/rules/stop-hook.d.ts.map +1 -0
- package/dist/core/agents/rules/stop-hook.js +161 -0
- package/dist/core/agents/session-events.d.ts +41 -0
- package/dist/core/agents/session-events.d.ts.map +1 -0
- package/dist/core/agents/session-events.js +205 -0
- package/dist/core/agents/state/activity-log.d.ts +18 -0
- package/dist/core/agents/state/activity-log.d.ts.map +1 -0
- package/dist/core/agents/state/activity-log.js +34 -0
- package/dist/core/agents/state/council.d.ts +39 -0
- package/dist/core/agents/state/council.d.ts.map +1 -0
- package/dist/core/agents/state/council.js +216 -0
- package/dist/core/agents/state/heartbeat-projector.d.ts +59 -0
- package/dist/core/agents/state/heartbeat-projector.d.ts.map +1 -0
- package/dist/core/agents/state/heartbeat-projector.js +436 -0
- package/dist/core/agents/state/heartbeat-writer.d.ts +64 -0
- package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -0
- package/dist/core/agents/state/heartbeat-writer.js +271 -0
- package/dist/core/agents/state/names.d.ts +35 -0
- package/dist/core/agents/state/names.d.ts.map +1 -0
- package/dist/core/agents/state/names.js +376 -0
- package/dist/core/agents/state/pidmap.d.ts +11 -0
- package/dist/core/agents/state/pidmap.d.ts.map +1 -0
- package/dist/core/agents/state/pidmap.js +32 -0
- package/dist/core/agents/state/scratch.d.ts +27 -0
- package/dist/core/agents/state/scratch.d.ts.map +1 -0
- package/dist/core/agents/state/scratch.js +90 -0
- package/dist/core/agents/state/shell-mutation.d.ts +17 -0
- package/dist/core/agents/state/shell-mutation.d.ts.map +1 -0
- package/dist/core/agents/state/shell-mutation.js +41 -0
- package/dist/core/agents/state/stale-sweep.d.ts +16 -0
- package/dist/core/agents/state/stale-sweep.d.ts.map +1 -0
- package/dist/core/agents/state/stale-sweep.js +166 -0
- package/dist/core/config.d.ts +29 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +108 -0
- package/dist/core/hooks/cli.d.ts +21 -0
- package/dist/core/hooks/cli.d.ts.map +1 -0
- package/dist/core/hooks/cli.js +1123 -0
- package/dist/core/hooks/effects/image-capture.d.ts +43 -0
- package/dist/core/hooks/effects/image-capture.d.ts.map +1 -0
- package/dist/core/hooks/effects/image-capture.js +288 -0
- package/dist/core/hooks/effects/index.d.ts +64 -0
- package/dist/core/hooks/effects/index.d.ts.map +1 -0
- package/dist/core/hooks/effects/index.js +197 -0
- package/dist/core/hooks/events/emit.d.ts +31 -0
- package/dist/core/hooks/events/emit.d.ts.map +1 -0
- package/dist/core/hooks/events/emit.js +89 -0
- package/dist/core/hooks/events/schema.d.ts +235 -0
- package/dist/core/hooks/events/schema.d.ts.map +1 -0
- package/dist/core/hooks/events/schema.js +12 -0
- package/dist/core/hooks/events/ulid.d.ts +10 -0
- package/dist/core/hooks/events/ulid.d.ts.map +1 -0
- package/dist/core/hooks/events/ulid.js +47 -0
- package/dist/core/hooks/harness/detect.d.ts +9 -0
- package/dist/core/hooks/harness/detect.d.ts.map +1 -0
- package/dist/core/hooks/harness/detect.js +29 -0
- package/dist/core/hooks/harness/events.d.ts +45 -0
- package/dist/core/hooks/harness/events.d.ts.map +1 -0
- package/dist/core/hooks/harness/events.js +71 -0
- package/dist/core/hooks/harness/output.d.ts +46 -0
- package/dist/core/hooks/harness/output.d.ts.map +1 -0
- package/dist/core/hooks/harness/output.js +87 -0
- package/dist/core/hooks/harness/parse.d.ts +67 -0
- package/dist/core/hooks/harness/parse.d.ts.map +1 -0
- package/dist/core/hooks/harness/parse.js +132 -0
- package/dist/core/hooks/index.d.ts +8 -0
- package/dist/core/hooks/index.d.ts.map +1 -0
- package/dist/core/hooks/index.js +7 -0
- package/dist/core/hooks/resolve/anchor.d.ts +37 -0
- package/dist/core/hooks/resolve/anchor.d.ts.map +1 -0
- package/dist/core/hooks/resolve/anchor.js +48 -0
- package/dist/core/hooks/resolve/coord-root.d.ts +6 -0
- package/dist/core/hooks/resolve/coord-root.d.ts.map +1 -0
- package/dist/core/hooks/resolve/coord-root.js +27 -0
- package/dist/core/hooks/resolve/intent.d.ts +33 -0
- package/dist/core/hooks/resolve/intent.d.ts.map +1 -0
- package/dist/core/hooks/resolve/intent.js +79 -0
- package/dist/core/hooks/resolve/owner.d.ts +42 -0
- package/dist/core/hooks/resolve/owner.d.ts.map +1 -0
- package/dist/core/hooks/resolve/owner.js +140 -0
- package/dist/core/hooks/resolve/transcript.d.ts +26 -0
- package/dist/core/hooks/resolve/transcript.d.ts.map +1 -0
- package/dist/core/hooks/resolve/transcript.js +73 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/lib/agent-browser/client.d.ts +99 -0
- package/dist/lib/agent-browser/client.d.ts.map +1 -0
- package/dist/lib/agent-browser/client.js +177 -0
- package/dist/lib/agent-browser/index.d.ts +2 -0
- package/dist/lib/agent-browser/index.d.ts.map +1 -0
- package/dist/lib/agent-browser/index.js +1 -0
- package/dist/lib/browser/client.d.ts +193 -0
- package/dist/lib/browser/client.d.ts.map +1 -0
- package/dist/lib/browser/client.js +325 -0
- package/dist/lib/browser/dev-overlay.d.ts +23 -0
- package/dist/lib/browser/dev-overlay.d.ts.map +1 -0
- package/dist/lib/browser/dev-overlay.js +153 -0
- package/dist/lib/browser/index.d.ts +5 -0
- package/dist/lib/browser/index.d.ts.map +1 -0
- package/dist/lib/browser/index.js +2 -0
- package/dist/lib/browser/layout.d.ts +79 -0
- package/dist/lib/browser/layout.d.ts.map +1 -0
- package/dist/lib/browser/layout.js +220 -0
- package/dist/lib/browser/visibility.d.ts +86 -0
- package/dist/lib/browser/visibility.d.ts.map +1 -0
- package/dist/lib/browser/visibility.js +333 -0
- package/dist/lib/browser/visual-diff.d.ts +38 -0
- package/dist/lib/browser/visual-diff.d.ts.map +1 -0
- package/dist/lib/browser/visual-diff.js +107 -0
- package/dist/lib/completion/bash.d.ts +25 -0
- package/dist/lib/completion/bash.d.ts.map +1 -0
- package/dist/lib/completion/bash.js +284 -0
- package/dist/lib/completion/fish.d.ts +16 -0
- package/dist/lib/completion/fish.d.ts.map +1 -0
- package/dist/lib/completion/fish.js +118 -0
- package/dist/lib/completion/index.d.ts +5 -0
- package/dist/lib/completion/index.d.ts.map +1 -0
- package/dist/lib/completion/index.js +4 -0
- package/dist/lib/completion/walk.d.ts +68 -0
- package/dist/lib/completion/walk.d.ts.map +1 -0
- package/dist/lib/completion/walk.js +102 -0
- package/dist/lib/completion/zsh.d.ts +13 -0
- package/dist/lib/completion/zsh.d.ts.map +1 -0
- package/dist/lib/completion/zsh.js +249 -0
- package/dist/lib/context/index.d.ts +107 -0
- package/dist/lib/context/index.d.ts.map +1 -0
- package/dist/lib/context/index.js +275 -0
- package/dist/lib/cookies/client.d.ts +131 -0
- package/dist/lib/cookies/client.d.ts.map +1 -0
- package/dist/lib/cookies/client.js +239 -0
- package/dist/lib/cookies/index.d.ts +2 -0
- package/dist/lib/cookies/index.d.ts.map +1 -0
- package/dist/lib/cookies/index.js +1 -0
- package/dist/lib/council/index.d.ts +266 -0
- package/dist/lib/council/index.d.ts.map +1 -0
- package/dist/lib/council/index.js +674 -0
- package/dist/lib/docs-index.d.ts +28 -0
- package/dist/lib/docs-index.d.ts.map +1 -0
- package/dist/lib/docs-index.js +169 -0
- package/dist/lib/docs-lint.d.ts +26 -0
- package/dist/lib/docs-lint.d.ts.map +1 -0
- package/dist/lib/docs-lint.js +378 -0
- package/dist/lib/docs-sweep.d.ts +34 -0
- package/dist/lib/docs-sweep.d.ts.map +1 -0
- package/dist/lib/docs-sweep.js +304 -0
- package/dist/lib/docs.d.ts +27 -0
- package/dist/lib/docs.d.ts.map +1 -0
- package/dist/lib/docs.js +142 -0
- package/dist/lib/env.d.ts +11 -0
- package/dist/lib/env.d.ts.map +1 -0
- package/dist/lib/env.js +12 -0
- package/dist/lib/exec.d.ts +32 -0
- package/dist/lib/exec.d.ts.map +1 -0
- package/dist/lib/exec.js +54 -0
- package/dist/lib/format.d.ts +29 -0
- package/dist/lib/format.d.ts.map +1 -0
- package/dist/lib/format.js +139 -0
- package/dist/lib/http/client.d.ts +56 -0
- package/dist/lib/http/client.d.ts.map +1 -0
- package/dist/lib/http/client.js +160 -0
- package/dist/lib/http/index.d.ts +2 -0
- package/dist/lib/http/index.d.ts.map +1 -0
- package/dist/lib/http/index.js +1 -0
- package/dist/lib/identities/index.d.ts +77 -0
- package/dist/lib/identities/index.d.ts.map +1 -0
- package/dist/lib/identities/index.js +190 -0
- package/dist/lib/machine.d.ts +19 -0
- package/dist/lib/machine.d.ts.map +1 -0
- package/dist/lib/machine.js +61 -0
- package/dist/lib/presence.d.ts +48 -0
- package/dist/lib/presence.d.ts.map +1 -0
- package/dist/lib/presence.js +123 -0
- package/dist/lib/readability/client.d.ts +39 -0
- package/dist/lib/readability/client.d.ts.map +1 -0
- package/dist/lib/readability/client.js +121 -0
- package/dist/lib/readability/index.d.ts +2 -0
- package/dist/lib/readability/index.d.ts.map +1 -0
- package/dist/lib/readability/index.js +1 -0
- package/dist/lib/scratch/index.d.ts +74 -0
- package/dist/lib/scratch/index.d.ts.map +1 -0
- package/dist/lib/scratch/index.js +393 -0
- package/dist/lib/tunnel/gate.d.ts +12 -0
- package/dist/lib/tunnel/gate.d.ts.map +1 -0
- package/dist/lib/tunnel/gate.js +101 -0
- package/dist/lib/tunnel/state.d.ts +34 -0
- package/dist/lib/tunnel/state.d.ts.map +1 -0
- package/dist/lib/tunnel/state.js +132 -0
- package/package.json +160 -8
- package/schemas/.gitkeep +0 -0
- package/schemas/config.schema.json +109 -0
- package/src/cli.ts +22 -0
- package/src/commander.ts +242 -0
- package/src/commands/.gitkeep +0 -0
- package/src/commands/agents.ts +4567 -0
- package/src/commands/backup.ts +305 -0
- package/src/commands/browse-ai.ts +198 -0
- package/src/commands/browse.ts +849 -0
- package/src/commands/callers.ts +363 -0
- package/src/commands/completion.ts +193 -0
- package/src/commands/config-get.ts +161 -0
- package/src/commands/context.ts +209 -0
- package/src/commands/cookies.ts +198 -0
- package/src/commands/docs.ts +174 -0
- package/src/commands/doctor.ts +231 -0
- package/src/commands/edit-batch.ts +233 -0
- package/src/commands/eml.ts +519 -0
- package/src/commands/env.ts +254 -0
- package/src/commands/fetch.ts +136 -0
- package/src/commands/file-history.ts +202 -0
- package/src/commands/grep.ts +371 -0
- package/src/commands/init.ts +335 -0
- package/src/commands/outline.ts +583 -0
- package/src/commands/presence.ts +152 -0
- package/src/commands/read.ts +64 -0
- package/src/commands/scratch.ts +445 -0
- package/src/commands/session.ts +187 -0
- package/src/commands/sync.ts +306 -0
- package/src/commands/toc.ts +218 -0
- package/src/commands/tokens.ts +79 -0
- package/src/commands/tunnel.ts +633 -0
- package/src/commands/uninstall.ts +144 -0
- package/src/commands/web.ts +193 -0
- package/src/core/agents/canonical-emit.ts +77 -0
- package/src/core/agents/cli-emit.ts +64 -0
- package/src/core/agents/cli.ts +838 -0
- package/src/core/agents/codex-replay.ts +163 -0
- package/src/core/agents/coord-client.ts +249 -0
- package/src/core/agents/events/consume.ts +196 -0
- package/src/core/agents/events/emit.ts +108 -0
- package/src/core/agents/events/ulid.ts +51 -0
- package/src/core/agents/index.ts +14 -0
- package/src/core/agents/paths.ts +16 -0
- package/src/core/agents/render/prompt-context.ts +401 -0
- package/src/core/agents/render/session-context.ts +341 -0
- package/src/core/agents/rules/claim-conflict.ts +282 -0
- package/src/core/agents/rules/commit-conflict.ts +303 -0
- package/src/core/agents/rules/stop-hook.ts +229 -0
- package/src/core/agents/session-events.ts +228 -0
- package/src/core/agents/state/activity-log.ts +33 -0
- package/src/core/agents/state/council.ts +265 -0
- package/src/core/agents/state/heartbeat-projector.ts +488 -0
- package/src/core/agents/state/heartbeat-writer.ts +333 -0
- package/src/core/agents/state/names.ts +399 -0
- package/src/core/agents/state/pidmap.ts +38 -0
- package/src/core/agents/state/scratch.ts +121 -0
- package/src/core/agents/state/shell-mutation.ts +44 -0
- package/src/core/agents/state/stale-sweep.ts +190 -0
- package/src/core/config.ts +111 -0
- package/src/core/hooks/cli.ts +1247 -0
- package/src/core/hooks/effects/image-capture.ts +330 -0
- package/src/core/hooks/effects/index.ts +210 -0
- package/src/core/hooks/events/emit.ts +120 -0
- package/src/core/hooks/events/schema.ts +430 -0
- package/src/core/hooks/events/ulid.ts +51 -0
- package/src/core/hooks/harness/detect.ts +30 -0
- package/src/core/hooks/harness/events.ts +102 -0
- package/src/core/hooks/harness/output.ts +100 -0
- package/src/core/hooks/harness/parse.ts +180 -0
- package/src/core/hooks/index.ts +16 -0
- package/src/core/hooks/resolve/anchor.ts +51 -0
- package/src/core/hooks/resolve/coord-root.ts +25 -0
- package/src/core/hooks/resolve/intent.ts +89 -0
- package/src/core/hooks/resolve/owner.ts +140 -0
- package/src/core/hooks/resolve/transcript.ts +72 -0
- package/src/hooks/.gitkeep +0 -0
- package/src/index.ts +15 -0
- package/src/lib/agent-browser/client.ts +239 -0
- package/src/lib/agent-browser/index.ts +1 -0
- package/src/lib/browser/client.ts +449 -0
- package/src/lib/browser/dev-overlay.ts +207 -0
- package/src/lib/browser/index.ts +24 -0
- package/src/lib/browser/layout.ts +288 -0
- package/src/lib/browser/visibility.ts +419 -0
- package/src/lib/browser/visual-diff.ts +150 -0
- package/src/lib/completion/bash.ts +291 -0
- package/src/lib/completion/fish.ts +134 -0
- package/src/lib/completion/index.ts +10 -0
- package/src/lib/completion/walk.ts +184 -0
- package/src/lib/completion/zsh.ts +262 -0
- package/src/lib/context/index.ts +386 -0
- package/src/lib/cookies/client.ts +301 -0
- package/src/lib/cookies/index.ts +13 -0
- package/src/lib/council/index.ts +803 -0
- package/src/lib/docs-index.ts +216 -0
- package/src/lib/docs-lint.ts +413 -0
- package/src/lib/docs-sweep.ts +348 -0
- package/src/lib/docs.ts +199 -0
- package/src/lib/env.ts +12 -0
- package/src/lib/exec.ts +74 -0
- package/src/lib/format.ts +147 -0
- package/src/lib/http/client.ts +211 -0
- package/src/lib/http/index.ts +1 -0
- package/src/lib/identities/index.ts +210 -0
- package/src/lib/machine.ts +61 -0
- package/src/lib/presence.ts +154 -0
- package/src/lib/readability/client.ts +169 -0
- package/src/lib/readability/index.ts +5 -0
- package/src/lib/readability/turndown-plugin-gfm.d.ts +10 -0
- package/src/lib/scratch/index.ts +470 -0
- package/src/lib/tunnel/gate.ts +113 -0
- package/src/lib/tunnel/state.ts +167 -0
- package/src/web/.gitkeep +0 -0
- package/index.js +0 -1
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { resolveBinName } from "../core/config.js";
|
|
3
|
+
import { htmlToMarkdown } from "../lib/readability/index.js";
|
|
4
|
+
// Module-scoped emit assigned by registerReadCommand. Same pattern as cookies.ts:
|
|
5
|
+
// the runRead helper closes over this so the .action callback stays concise.
|
|
6
|
+
let emit;
|
|
7
|
+
/**
|
|
8
|
+
* `harn read`: extract clean readable markdown from HTML.
|
|
9
|
+
*/
|
|
10
|
+
export function registerReadCommand(program, emitParam) {
|
|
11
|
+
emit = emitParam;
|
|
12
|
+
program
|
|
13
|
+
.command("read [html-file]")
|
|
14
|
+
.description(`Extract clean readable markdown from HTML. Reads from file or stdin (use '-'). Pair with \`${resolveBinName()} fetch\` or \`${resolveBinName()} browse\` for scrape-to-markdown.`)
|
|
15
|
+
.option("-o, --output <file>", "Write markdown to file instead of stdout")
|
|
16
|
+
.option("--url <url>", "Base URL: used to resolve relative links")
|
|
17
|
+
.option("--selector <css>", "Use this CSS selector instead of Readability (fallback when extraction misses content)")
|
|
18
|
+
.option("--raw", "Output cleaned HTML instead of markdown (debugging)")
|
|
19
|
+
.option("--max-chars <n>", "Truncate output to N characters (0 = disable)", "100000")
|
|
20
|
+
.action(async (htmlFile, opts) => {
|
|
21
|
+
try {
|
|
22
|
+
await runRead(htmlFile, opts);
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
26
|
+
emit.error({ code: "read_error", message: msg });
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
async function runRead(htmlFile, opts) {
|
|
32
|
+
const input = htmlFile && htmlFile !== "-" ? readFileSync(htmlFile, "utf-8") : readFileSync(0, "utf-8");
|
|
33
|
+
const result = await htmlToMarkdown(input, {
|
|
34
|
+
url: opts.url,
|
|
35
|
+
selector: opts.selector,
|
|
36
|
+
raw: opts.raw,
|
|
37
|
+
maxChars: Number.parseInt(opts.maxChars, 10),
|
|
38
|
+
});
|
|
39
|
+
if (opts.output) {
|
|
40
|
+
writeFileSync(opts.output, result.output);
|
|
41
|
+
emit.file(opts.output, { chars: result.output.length });
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
emit.text(result.output);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scratch.d.ts","sourceRoot":"","sources":["../../src/commands/scratch.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AA6BnD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,GAAG,IAAI,CA2RrF"}
|
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync, unlinkSync, watch } from "node:fs";
|
|
2
|
+
import { resolve as resolvePath } from "node:path";
|
|
3
|
+
import { emitCanonical, normalizeHarness, readHeartbeat } from "../core/agents/index.js";
|
|
4
|
+
import { resolveBinName } from "../core/config.js";
|
|
5
|
+
import { appendEntry, archiveScratch, currentOwnerOrThrow, lintScratch, listArchives, loadScratch, parseScratch, pruneArchives, resolveOwnerByName, SCRATCH_CATEGORIES, scratchDir, scratchPath, sweepOrphanScratchpads, } from "../lib/scratch/index.js";
|
|
6
|
+
/**
|
|
7
|
+
* `harn scratch`: per-agent markdown journal at `.harnery/scratch/<instance_id>.md`.
|
|
8
|
+
* Used both for self-notes (surviving compaction) and peer coordination (other
|
|
9
|
+
* agents pull-read it on demand). SessionEnd hook archives; SessionStart
|
|
10
|
+
* janitor surfaces the most-recent archive as a recovery cue.
|
|
11
|
+
*/
|
|
12
|
+
let emit;
|
|
13
|
+
export function registerScratchCommand(program, emitParam) {
|
|
14
|
+
emit = emitParam;
|
|
15
|
+
const root = program
|
|
16
|
+
.command("scratch")
|
|
17
|
+
.description("Per-agent markdown journal: append-only timestamped entries. " +
|
|
18
|
+
"Survives in-session compaction, archived at session end, pruned after 7 days.");
|
|
19
|
+
// ── add ───────────────────────────────────────────────────────────────
|
|
20
|
+
root
|
|
21
|
+
.command("add <category> <text...>")
|
|
22
|
+
.description(`Append an entry to my scratchpad. Category: ${SCRATCH_CATEGORIES.join(" | ")}`)
|
|
23
|
+
.action((category, text) => {
|
|
24
|
+
const cat = validateCategory(category);
|
|
25
|
+
const body = text.join(" ");
|
|
26
|
+
if (!body.trim()) {
|
|
27
|
+
emit.error({ code: "empty_body", message: "scratch add: body is empty" });
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const owner = currentOwnerOrThrow();
|
|
32
|
+
const doc = appendEntry(owner, cat, body);
|
|
33
|
+
const hb = readHeartbeat(owner);
|
|
34
|
+
emitCanonical({
|
|
35
|
+
type: "state.scratch_append",
|
|
36
|
+
owner,
|
|
37
|
+
session: hb?.session_id ?? owner,
|
|
38
|
+
harness: normalizeHarness(hb?.platform),
|
|
39
|
+
data: {
|
|
40
|
+
category: cat,
|
|
41
|
+
body_summary: body.length > 1000 ? `${body.slice(0, 997)}...` : body,
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
emit.data({
|
|
45
|
+
instance_id: owner,
|
|
46
|
+
name: doc.header.name,
|
|
47
|
+
category: cat,
|
|
48
|
+
entries: doc.entries.length,
|
|
49
|
+
bytes: doc.bytes,
|
|
50
|
+
path: doc.path,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
emit.error({ code: "add_failed", message: err.message });
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
// ── read ──────────────────────────────────────────────────────────────
|
|
59
|
+
root
|
|
60
|
+
.command("read")
|
|
61
|
+
.description("Render a scratchpad to stdout. No --name: my own. With --name: a peer's.")
|
|
62
|
+
.option("--name <name>", "Read the named peer's scratchpad (case-insensitive)")
|
|
63
|
+
.option("--owner <id>", "Read by instance_id directly")
|
|
64
|
+
.option("--archive <basename>", "Read an archive file (e.g. <owner>-<ts>.md)")
|
|
65
|
+
.option("--limit <n>", "Cap entries rendered (newest first)", "50")
|
|
66
|
+
.action((opts) => {
|
|
67
|
+
try {
|
|
68
|
+
runRead(opts);
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
emit.error({ code: "read_failed", message: err.message });
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
// ── list ──────────────────────────────────────────────────────────────
|
|
76
|
+
root
|
|
77
|
+
.command("list")
|
|
78
|
+
.description("Summarize all active scratchpads + archive count")
|
|
79
|
+
.option("--archives", "List archive files instead of active scratchpads")
|
|
80
|
+
.action((opts) => {
|
|
81
|
+
try {
|
|
82
|
+
if (opts.archives) {
|
|
83
|
+
const items = listArchives().map((a) => {
|
|
84
|
+
const doc = parseSafe(a.path);
|
|
85
|
+
const lastEntry = doc?.entries[0];
|
|
86
|
+
const ageMin = Math.floor((Date.now() - a.mtimeMs) / 60000);
|
|
87
|
+
return {
|
|
88
|
+
basename: a.basename,
|
|
89
|
+
name: doc?.header.name ?? "unknown",
|
|
90
|
+
entries: doc?.entries.length ?? 0,
|
|
91
|
+
bytes: a.bytes,
|
|
92
|
+
archived_min_ago: ageMin,
|
|
93
|
+
last_category: lastEntry?.category ?? null,
|
|
94
|
+
last_ts: lastEntry?.ts_display ?? null,
|
|
95
|
+
};
|
|
96
|
+
});
|
|
97
|
+
emit.data({ rows: items });
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const dir = scratchDir();
|
|
101
|
+
const rows = [];
|
|
102
|
+
for (const f of readdirSync(dir)) {
|
|
103
|
+
if (!f.endsWith(".md"))
|
|
104
|
+
continue;
|
|
105
|
+
const instanceId = f.replace(/\.md$/, "");
|
|
106
|
+
const doc = loadScratch(instanceId);
|
|
107
|
+
if (!doc)
|
|
108
|
+
continue;
|
|
109
|
+
rows.push({
|
|
110
|
+
instance_id: instanceId,
|
|
111
|
+
name: doc.header.name,
|
|
112
|
+
entries: doc.entries.length,
|
|
113
|
+
bytes: doc.bytes,
|
|
114
|
+
last_category: doc.entries[0]?.category ?? null,
|
|
115
|
+
last_ts: doc.entries[0]?.ts_display ?? null,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
rows.sort((a, b) => (b.last_ts ?? "").localeCompare(a.last_ts ?? ""));
|
|
119
|
+
emit.data({ rows });
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
emit.error({ code: "list_failed", message: err.message });
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
// ── tail ──────────────────────────────────────────────────────────────
|
|
127
|
+
root
|
|
128
|
+
.command("tail")
|
|
129
|
+
.description("Follow my scratchpad (or a peer's) for new entries.")
|
|
130
|
+
.option("--name <name>", "Tail the named peer's scratchpad")
|
|
131
|
+
.option("--owner <id>", "Tail by instance_id directly")
|
|
132
|
+
.action(async (opts) => {
|
|
133
|
+
try {
|
|
134
|
+
const owner = resolveTargetOwner(opts);
|
|
135
|
+
const path = scratchPath(owner);
|
|
136
|
+
if (!existsSync(path)) {
|
|
137
|
+
emit.error({ code: "no_scratchpad", message: `no scratchpad at ${path} yet` });
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
process.stderr.write(`tailing ${path}\n`); // lint-ok-emission: tail banner, immediate stderr flush before streaming
|
|
141
|
+
let lastSize = statSync(path).size;
|
|
142
|
+
// Print initial render
|
|
143
|
+
process.stdout.write(`${renderScratch(loadScratch(owner), 10)}\n`); // lint-ok-emission: streaming tail body
|
|
144
|
+
const w = watch(path, { persistent: true }, () => {
|
|
145
|
+
try {
|
|
146
|
+
const curr = statSync(path).size;
|
|
147
|
+
if (curr === lastSize)
|
|
148
|
+
return;
|
|
149
|
+
lastSize = curr;
|
|
150
|
+
const doc = loadScratch(owner);
|
|
151
|
+
if (!doc)
|
|
152
|
+
return;
|
|
153
|
+
const newest = doc.entries[0];
|
|
154
|
+
if (!newest)
|
|
155
|
+
return;
|
|
156
|
+
const line = `\n## ${newest.ts_display} · ${newest.category}\n${newest.body}\n`;
|
|
157
|
+
process.stdout.write(line); // lint-ok-emission: streaming tail, per-line stdout flush, no envelope wrap
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
// ignore transient
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
await new Promise((resolveP) => {
|
|
164
|
+
const stop = () => {
|
|
165
|
+
w.close();
|
|
166
|
+
resolveP();
|
|
167
|
+
};
|
|
168
|
+
process.on("SIGINT", stop);
|
|
169
|
+
process.on("SIGTERM", stop);
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
emit.error({ code: "tail_failed", message: err.message });
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
// ── clear ─────────────────────────────────────────────────────────────
|
|
178
|
+
root
|
|
179
|
+
.command("clear")
|
|
180
|
+
.description("Delete my scratchpad (rare; mainly for testing)")
|
|
181
|
+
.option("--yes", "Confirm deletion")
|
|
182
|
+
.action((opts) => {
|
|
183
|
+
if (!opts.yes) {
|
|
184
|
+
emit.error({ code: "needs_yes", message: "pass --yes to confirm" });
|
|
185
|
+
process.exit(1);
|
|
186
|
+
}
|
|
187
|
+
try {
|
|
188
|
+
const owner = currentOwnerOrThrow();
|
|
189
|
+
const path = scratchPath(owner);
|
|
190
|
+
if (existsSync(path)) {
|
|
191
|
+
unlinkSync(path);
|
|
192
|
+
emit.data({ cleared: true, path });
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
emit.data({ cleared: false, path, reason: "did not exist" });
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
catch (err) {
|
|
199
|
+
emit.error({ code: "clear_failed", message: err.message });
|
|
200
|
+
process.exit(1);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
// ── lint ──────────────────────────────────────────────────────────────
|
|
204
|
+
root
|
|
205
|
+
.command("lint")
|
|
206
|
+
.description("Validate scratchpad format + size")
|
|
207
|
+
.option("--all", "Lint every scratchpad in .harnery/scratch/")
|
|
208
|
+
.option("--owner <id>", "Lint a specific owner's scratchpad")
|
|
209
|
+
.action((opts) => {
|
|
210
|
+
try {
|
|
211
|
+
runLint(opts);
|
|
212
|
+
}
|
|
213
|
+
catch (err) {
|
|
214
|
+
emit.error({ code: "lint_failed", message: err.message });
|
|
215
|
+
process.exit(1);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
// ── archive (manual / hook helper) ────────────────────────────────────
|
|
219
|
+
root
|
|
220
|
+
.command("archive")
|
|
221
|
+
.description("Archive my scratchpad now (idempotent; fired by SessionEnd hook)")
|
|
222
|
+
.option("--owner <id>", "Archive a specific owner's scratchpad")
|
|
223
|
+
.action((opts) => {
|
|
224
|
+
try {
|
|
225
|
+
const owner = opts.owner ?? currentOwnerOrThrow();
|
|
226
|
+
const dest = archiveScratch(owner);
|
|
227
|
+
emit.data({ instance_id: owner, archived: !!dest, path: dest });
|
|
228
|
+
}
|
|
229
|
+
catch (err) {
|
|
230
|
+
emit.error({ code: "archive_failed", message: err.message });
|
|
231
|
+
process.exit(1);
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
// ── recovery-cue (SessionStart hook helper) ───────────────────────────
|
|
235
|
+
root
|
|
236
|
+
.command("recovery-cue")
|
|
237
|
+
.description("Emit a one-line recovery hint to stdout when a recent archive exists. " +
|
|
238
|
+
"Used by SessionStart hook to surface 'previous session was doing X'. " +
|
|
239
|
+
"Stays silent when no relevant archive (no noise on fresh sessions).")
|
|
240
|
+
.option("--max-age-hours <n>", "Only surface archives newer than this", "24")
|
|
241
|
+
.action((opts) => {
|
|
242
|
+
try {
|
|
243
|
+
const maxHours = Number.parseInt(opts.maxAgeHours, 10);
|
|
244
|
+
const archives = listArchives();
|
|
245
|
+
if (archives.length === 0)
|
|
246
|
+
return;
|
|
247
|
+
const newest = archives[0];
|
|
248
|
+
const ageHours = (Date.now() - newest.mtimeMs) / 3600_000;
|
|
249
|
+
if (ageHours > maxHours)
|
|
250
|
+
return;
|
|
251
|
+
const doc = parseSafe(newest.path);
|
|
252
|
+
if (!doc || doc.entries.length === 0)
|
|
253
|
+
return;
|
|
254
|
+
const last = doc.entries[0];
|
|
255
|
+
const ageMin = Math.floor((Date.now() - newest.mtimeMs) / 60_000);
|
|
256
|
+
const ageStr = ageMin < 60 ? `${ageMin}m` : `${Math.floor(ageMin / 60)}h ${ageMin % 60}m`;
|
|
257
|
+
const bodyOneLine = last.body.replace(/\s+/g, " ").trim();
|
|
258
|
+
const bodyTrunc = bodyOneLine.length > 100 ? `${bodyOneLine.slice(0, 99)}…` : bodyOneLine;
|
|
259
|
+
const cue = `Recent scratchpad archive (${ageStr} ago): agent-${doc.header.name}: ` +
|
|
260
|
+
`last entry [${last.category}] "${bodyTrunc}". ` +
|
|
261
|
+
`Read full: \`${resolveBinName()} scratch read --archive ${newest.basename}\`.`;
|
|
262
|
+
process.stdout.write(`${cue}\n`); // lint-ok-emission: bash hook reads stdout to compose SessionStart additionalContext
|
|
263
|
+
}
|
|
264
|
+
catch (_err) {
|
|
265
|
+
// Recovery cue is best-effort; never fail the SessionStart hook.
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
// ── janitor (SessionStart hook) ───────────────────────────────────────
|
|
269
|
+
root
|
|
270
|
+
.command("janitor")
|
|
271
|
+
.description("Prune old archives + sweep orphan scratchpads (heartbeat gone). Fired by SessionStart hook.")
|
|
272
|
+
.option("--days <n>", "Archive retention in days", "7")
|
|
273
|
+
.option("--quiet", "Suppress stdout output")
|
|
274
|
+
.action((opts) => {
|
|
275
|
+
try {
|
|
276
|
+
const days = Number.parseInt(opts.days, 10);
|
|
277
|
+
const swept = sweepOrphanScratchpads();
|
|
278
|
+
const pruned = pruneArchives(days);
|
|
279
|
+
if (!opts.quiet) {
|
|
280
|
+
emit.data({
|
|
281
|
+
archives_pruned: pruned,
|
|
282
|
+
orphans_swept: swept.length,
|
|
283
|
+
orphans: swept,
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
catch (err) {
|
|
288
|
+
emit.error({ code: "janitor_failed", message: err.message });
|
|
289
|
+
process.exit(1);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
// ─── runRead ──────────────────────────────────────────────────────────────
|
|
294
|
+
function runRead(opts) {
|
|
295
|
+
const limit = Number.parseInt(opts.limit, 10);
|
|
296
|
+
if (opts.archive) {
|
|
297
|
+
const dir = scratchDir();
|
|
298
|
+
const path = resolvePath(dir, "archived", opts.archive);
|
|
299
|
+
if (!existsSync(path)) {
|
|
300
|
+
emit.error({ code: "no_archive", message: `archive not found: ${opts.archive}` });
|
|
301
|
+
process.exit(1);
|
|
302
|
+
}
|
|
303
|
+
const content = readFileSync(path, "utf8");
|
|
304
|
+
process.stdout.write(content); // lint-ok-emission: archive render, file content is already the rendered form
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
const owner = resolveTargetOwner(opts);
|
|
308
|
+
const doc = loadScratch(owner);
|
|
309
|
+
if (!doc) {
|
|
310
|
+
emit.error({
|
|
311
|
+
code: "no_scratchpad",
|
|
312
|
+
message: `no scratchpad for owner=${owner.slice(0, 8)}… (file not created yet)`,
|
|
313
|
+
});
|
|
314
|
+
process.exit(1);
|
|
315
|
+
}
|
|
316
|
+
const hb = readHeartbeat(owner);
|
|
317
|
+
const staleBanner = renderStaleBannerIfNeeded(hb?.last_heartbeat);
|
|
318
|
+
process.stdout.write(`${staleBanner + renderScratch(doc, limit)}\n`); // lint-ok-emission: pretty render, multi-line markdown for direct TTY/pipe consumption
|
|
319
|
+
}
|
|
320
|
+
function runLint(opts) {
|
|
321
|
+
const dir = scratchDir();
|
|
322
|
+
const files = [];
|
|
323
|
+
if (opts.owner) {
|
|
324
|
+
files.push(scratchPath(opts.owner));
|
|
325
|
+
}
|
|
326
|
+
else if (opts.all) {
|
|
327
|
+
for (const f of readdirSync(dir)) {
|
|
328
|
+
if (f.endsWith(".md"))
|
|
329
|
+
files.push(resolvePath(dir, f));
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
const owner = currentOwnerOrThrow();
|
|
334
|
+
files.push(scratchPath(owner));
|
|
335
|
+
}
|
|
336
|
+
let totalIssues = 0;
|
|
337
|
+
const reports = [];
|
|
338
|
+
for (const path of files) {
|
|
339
|
+
if (!existsSync(path)) {
|
|
340
|
+
reports.push({ path, issues: [{ line: 0, message: "(file does not exist)" }] });
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
const content = readFileSync(path, "utf8");
|
|
344
|
+
const issues = lintScratch(content);
|
|
345
|
+
if (issues.length > 0)
|
|
346
|
+
totalIssues += issues.length;
|
|
347
|
+
reports.push({ path, issues });
|
|
348
|
+
}
|
|
349
|
+
emit.data({ files: reports.length, total_issues: totalIssues, reports });
|
|
350
|
+
if (totalIssues > 0)
|
|
351
|
+
process.exit(1);
|
|
352
|
+
}
|
|
353
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────
|
|
354
|
+
function validateCategory(c) {
|
|
355
|
+
if (!SCRATCH_CATEGORIES.includes(c)) {
|
|
356
|
+
emit.error({
|
|
357
|
+
code: "bad_category",
|
|
358
|
+
message: `category must be one of: ${SCRATCH_CATEGORIES.join(", ")}`,
|
|
359
|
+
});
|
|
360
|
+
process.exit(1);
|
|
361
|
+
}
|
|
362
|
+
return c;
|
|
363
|
+
}
|
|
364
|
+
function resolveTargetOwner(opts) {
|
|
365
|
+
if (opts.owner)
|
|
366
|
+
return opts.owner;
|
|
367
|
+
if (opts.name) {
|
|
368
|
+
const id = resolveOwnerByName(opts.name);
|
|
369
|
+
if (!id) {
|
|
370
|
+
emit.error({
|
|
371
|
+
code: "no_match",
|
|
372
|
+
message: `no live agent named "${opts.name}". Try \`${resolveBinName()} agents list\`.`,
|
|
373
|
+
});
|
|
374
|
+
process.exit(1);
|
|
375
|
+
}
|
|
376
|
+
return id;
|
|
377
|
+
}
|
|
378
|
+
return currentOwnerOrThrow();
|
|
379
|
+
}
|
|
380
|
+
function renderScratch(doc, limit) {
|
|
381
|
+
const lines = [];
|
|
382
|
+
lines.push(`# Scratchpad: agent-${doc.header.name}`);
|
|
383
|
+
if (doc.header.session_id)
|
|
384
|
+
lines.push(`session_id: ${doc.header.session_id}`);
|
|
385
|
+
if (doc.header.machine)
|
|
386
|
+
lines.push(`machine: ${doc.header.machine}`);
|
|
387
|
+
if (doc.header.started)
|
|
388
|
+
lines.push(`started: ${doc.header.started}`);
|
|
389
|
+
if (doc.header.last_updated)
|
|
390
|
+
lines.push(`last_updated: ${doc.header.last_updated}`);
|
|
391
|
+
lines.push("\n---\n");
|
|
392
|
+
for (const entry of doc.entries.slice(0, limit)) {
|
|
393
|
+
lines.push(`## ${entry.ts_display} · ${entry.category}`);
|
|
394
|
+
if (entry.body)
|
|
395
|
+
lines.push(entry.body);
|
|
396
|
+
lines.push("");
|
|
397
|
+
}
|
|
398
|
+
if (doc.entries.length > limit) {
|
|
399
|
+
lines.push(`(+${doc.entries.length - limit} older entries; raise --limit to see them)`);
|
|
400
|
+
}
|
|
401
|
+
return lines.join("\n").trimEnd();
|
|
402
|
+
}
|
|
403
|
+
const FRESHNESS_SECS = 600;
|
|
404
|
+
function renderStaleBannerIfNeeded(lastHeartbeat) {
|
|
405
|
+
if (!lastHeartbeat)
|
|
406
|
+
return "";
|
|
407
|
+
const ts = Date.parse(lastHeartbeat);
|
|
408
|
+
if (!Number.isFinite(ts))
|
|
409
|
+
return "";
|
|
410
|
+
const ageSec = Math.floor((Date.now() - ts) / 1000);
|
|
411
|
+
if (ageSec < FRESHNESS_SECS)
|
|
412
|
+
return "";
|
|
413
|
+
const m = Math.floor(ageSec / 60);
|
|
414
|
+
return `[STALE: heartbeat ${m}m old, agent may be dead]\n\n`;
|
|
415
|
+
}
|
|
416
|
+
function parseSafe(path) {
|
|
417
|
+
try {
|
|
418
|
+
if (!existsSync(path))
|
|
419
|
+
return null;
|
|
420
|
+
const content = readFileSync(path, "utf8");
|
|
421
|
+
return parseScratch(path, content);
|
|
422
|
+
}
|
|
423
|
+
catch {
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/commands/session.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAsCnD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,GAAG,IAAI,CAsDrF"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { resolveOwner, selfDisplayName } from "../core/agents/index.js";
|
|
3
|
+
import { clampField, newCmdId, readLastIntent, writeSessionEvent, } from "../core/agents/session-events.js";
|
|
4
|
+
import { resolveBinName } from "../core/config.js";
|
|
5
|
+
/** Strip the `agent-` prefix so structured event `agent` field matches heartbeat.name. */
|
|
6
|
+
function bareAgentName() {
|
|
7
|
+
const full = selfDisplayName();
|
|
8
|
+
return full.startsWith("agent-") ? full.slice(6) : full;
|
|
9
|
+
}
|
|
10
|
+
function selfOwnerId() {
|
|
11
|
+
return resolveOwner() ?? undefined;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* `harn session`: run a command (or narrate) and emit canonical coordination
|
|
15
|
+
* events for follow-along.
|
|
16
|
+
*
|
|
17
|
+
* Follow-along happens through the canonical `.harnery/events.ndjson` stream +
|
|
18
|
+
* the `/live` web viewer (or `harn agents watch`): `harn session -- <cmd>` runs
|
|
19
|
+
* the command, forwards its output to the terminal verbatim, and emits canonical
|
|
20
|
+
* `command.start/output/end`; `harn session log` emits a canonical `narration`
|
|
21
|
+
* event. The file-management subcommands (`tail`/`clear`/`trim`/`path`) are
|
|
22
|
+
* retired no-op stubs; `trim` is a silent no-op so a SessionStart hook that
|
|
23
|
+
* calls it keeps working.
|
|
24
|
+
*
|
|
25
|
+
* Usage:
|
|
26
|
+
* harn session "<intent>" -- <cmd> [args...] run command, emit command.* events
|
|
27
|
+
* harn session log "<message>" emit a narration event
|
|
28
|
+
*/
|
|
29
|
+
let emit;
|
|
30
|
+
export function registerSessionCommand(program, emitParam) {
|
|
31
|
+
emit = emitParam;
|
|
32
|
+
const session = program
|
|
33
|
+
.command("session")
|
|
34
|
+
.description("Run a command (or narrate) and emit canonical coordination events");
|
|
35
|
+
// Narration-only entry → canonical narration event.
|
|
36
|
+
session
|
|
37
|
+
.command("log <message...>")
|
|
38
|
+
.description("Emit a narration event (no command run)")
|
|
39
|
+
.action((messageParts) => {
|
|
40
|
+
const message = messageParts.join(" ");
|
|
41
|
+
writeSessionEvent("narration", bareAgentName(), {
|
|
42
|
+
instance_id: selfOwnerId(),
|
|
43
|
+
message: clampField(message),
|
|
44
|
+
});
|
|
45
|
+
emit.text(`⋯ ${message}\n`);
|
|
46
|
+
});
|
|
47
|
+
// Retired file-management subcommands, kept as graceful no-op stubs so
|
|
48
|
+
// existing callers (hooks, muscle memory) don't error. `trim` is the one a
|
|
49
|
+
// hook invokes, so it must exit 0 silently.
|
|
50
|
+
session
|
|
51
|
+
.command("trim")
|
|
52
|
+
.description("(retired): no-op kept for hook compatibility")
|
|
53
|
+
.option("-y, --yes", "(ignored)")
|
|
54
|
+
.option("--max-entries <n>", "(ignored)")
|
|
55
|
+
.option("--max-bytes <size>", "(ignored)")
|
|
56
|
+
.allowUnknownOption()
|
|
57
|
+
.allowExcessArguments()
|
|
58
|
+
.action(() => {
|
|
59
|
+
/* silent no-op, exit 0 */
|
|
60
|
+
});
|
|
61
|
+
for (const name of ["tail", "clear", "path"]) {
|
|
62
|
+
session
|
|
63
|
+
.command(name)
|
|
64
|
+
.description(`(retired): watch /live or '${resolveBinName()} agents watch'`)
|
|
65
|
+
.allowUnknownOption()
|
|
66
|
+
.action(() => {
|
|
67
|
+
emit.text(`Retired. Live activity flows to .harnery/events.ndjson; watch the /live web viewer or run '${resolveBinName()} agents watch'.\n`);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
// Default form: harn session "<intent>" -- <cmd> [args]
|
|
71
|
+
session
|
|
72
|
+
.argument("<intent>", "One-line description of what this action is for")
|
|
73
|
+
.argument("<cmd...>", "The command to run (prefix with -- if it has its own flags)")
|
|
74
|
+
.allowUnknownOption()
|
|
75
|
+
.action((intent, cmd) => {
|
|
76
|
+
runLogged(intent, cmd);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
// Core
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
/**
|
|
83
|
+
* Strip ONLY carriage-return redraw noise (progress bars like `docker pull`'s
|
|
84
|
+
* sticky line) from a chunk before splitting into event lines. Terminal
|
|
85
|
+
* forwarding keeps the raw bytes; this only cleans the canonical event copy.
|
|
86
|
+
*/
|
|
87
|
+
function stripCRNoise(text) {
|
|
88
|
+
return text.replace(/[^\n]*\r(?!\n)/g, "");
|
|
89
|
+
}
|
|
90
|
+
function runLogged(intent, cmdArg) {
|
|
91
|
+
// Strip a leading `--` separator if commander left one in.
|
|
92
|
+
const cmd = cmdArg[0] === "--" ? cmdArg.slice(1) : cmdArg;
|
|
93
|
+
if (cmd.length === 0) {
|
|
94
|
+
emit.error({ code: "usage", message: "Usage: harn session <intent> -- <cmd> [args...]" });
|
|
95
|
+
process.exit(2);
|
|
96
|
+
}
|
|
97
|
+
const cmdLine = cmd.map(quoteArg).join(" ");
|
|
98
|
+
const agent = bareAgentName();
|
|
99
|
+
const instanceId = selfOwnerId();
|
|
100
|
+
const cmdId = newCmdId();
|
|
101
|
+
// Intent precedence: explicit arg wins; else the PreToolUse-stamped intent
|
|
102
|
+
// file; else the command line itself.
|
|
103
|
+
const resolvedIntent = intent && intent.trim().length > 0 ? intent : (readLastIntent(instanceId) ?? cmdLine);
|
|
104
|
+
writeSessionEvent("command_start", agent, {
|
|
105
|
+
instance_id: instanceId,
|
|
106
|
+
cmd_id: cmdId,
|
|
107
|
+
intent: clampField(resolvedIntent),
|
|
108
|
+
cmd: clampField(cmdLine),
|
|
109
|
+
});
|
|
110
|
+
const start = Date.now();
|
|
111
|
+
const child = spawn(cmd[0], cmd.slice(1), {
|
|
112
|
+
stdio: ["inherit", "pipe", "pipe"],
|
|
113
|
+
env: process.env,
|
|
114
|
+
});
|
|
115
|
+
child.stdout?.on("data", (chunk) => {
|
|
116
|
+
process.stdout.write(chunk); // lint-ok-emission: harn session forwards child stdout verbatim; this IS the command's primary purpose
|
|
117
|
+
for (const line of stripCRNoise(chunk.toString("utf8")).split("\n")) {
|
|
118
|
+
if (line.length === 0)
|
|
119
|
+
continue;
|
|
120
|
+
writeSessionEvent("output", agent, {
|
|
121
|
+
instance_id: instanceId,
|
|
122
|
+
cmd_id: cmdId,
|
|
123
|
+
stream: "stdout",
|
|
124
|
+
line: clampField(line),
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
child.stderr?.on("data", (chunk) => {
|
|
129
|
+
process.stderr.write(chunk); // lint-ok-emission: harn session forwards child stderr verbatim; this IS the command's primary purpose
|
|
130
|
+
for (const line of stripCRNoise(chunk.toString("utf8")).split("\n")) {
|
|
131
|
+
if (line.length === 0)
|
|
132
|
+
continue;
|
|
133
|
+
writeSessionEvent("output", agent, {
|
|
134
|
+
instance_id: instanceId,
|
|
135
|
+
cmd_id: cmdId,
|
|
136
|
+
stream: "stderr",
|
|
137
|
+
line: clampField(line),
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
child.on("error", (err) => {
|
|
142
|
+
process.stderr.write(`✗ spawn failed: ${err.message}\n`);
|
|
143
|
+
process.exit(127);
|
|
144
|
+
});
|
|
145
|
+
child.on("close", (code, signal) => {
|
|
146
|
+
const durationMs = Date.now() - start;
|
|
147
|
+
writeSessionEvent("command_end", agent, {
|
|
148
|
+
instance_id: instanceId,
|
|
149
|
+
cmd_id: cmdId,
|
|
150
|
+
exit: code ?? null,
|
|
151
|
+
signal: signal ?? null,
|
|
152
|
+
duration_ms: durationMs,
|
|
153
|
+
});
|
|
154
|
+
process.exit(code ?? 1);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
/** Shell-safe quote an argument for human-readable logging (not for eval). */
|
|
158
|
+
function quoteArg(arg) {
|
|
159
|
+
if (/^[A-Za-z0-9_@%+=:,./-]+$/.test(arg))
|
|
160
|
+
return arg;
|
|
161
|
+
return `'${arg.replace(/'/g, `'\\''`)}'`;
|
|
162
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `harn sync`: keep a curated subset of `.harnery/` live across machines
|
|
3
|
+
* via rclone (typically a Google Drive remote, but any rclone remote works).
|
|
4
|
+
*
|
|
5
|
+
* Scope: a deliberately small set of file types: durable identities,
|
|
6
|
+
* archived scratchpads, council manifests. The high-churn machine-local
|
|
7
|
+
* stuff (events.ndjson, .pid-map/, active/, .last-intent.*) stays put.
|
|
8
|
+
*
|
|
9
|
+
* Subcommands:
|
|
10
|
+
* init: interactive: `rclone config` walks through Google OAuth, then
|
|
11
|
+
* we record the chosen remote name in ~/.config/harnery/sync.json
|
|
12
|
+
* status: diff local vs remote (rclone check --one-way)
|
|
13
|
+
* push: local to remote (rclone copy)
|
|
14
|
+
* pull: remote to local (rclone copy)
|
|
15
|
+
* list: show what's in the remote
|
|
16
|
+
*
|
|
17
|
+
* Power users override via env (HARNERY_SYNC_REMOTE, HARNERY_SYNC_PREFIX) or by
|
|
18
|
+
* editing the config file. Like `harn backup`, this surfaces rclone rather
|
|
19
|
+
* than abstracting it.
|
|
20
|
+
*/
|
|
21
|
+
import type { Command } from "commander";
|
|
22
|
+
import type { EmitContext } from "../commander.js";
|
|
23
|
+
export declare function registerSyncCommand(program: Command, emit: EmitContext): void;
|
|
24
|
+
//# sourceMappingURL=sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAgHnD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAuK7E"}
|