harnery 0.0.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +494 -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 +32 -0
- package/dist/lib/readability/client.d.ts.map +1 -0
- package/dist/lib/readability/client.js +119 -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 +564 -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 +156 -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,304 @@
|
|
|
1
|
+
import { existsSync as __existsSyncForDocs } from "node:fs";
|
|
2
|
+
import { resolve as __resolveForDocs } from "node:path";
|
|
3
|
+
import { sh } from "./exec.js";
|
|
4
|
+
// Module-level docs context, initialized by initDocsContext() before any
|
|
5
|
+
// other function in this file is called. The repo root + submodule list
|
|
6
|
+
// are passed via the context provided to registerDocsCommand.
|
|
7
|
+
let REPO_ROOT = "";
|
|
8
|
+
let SUBMODULES = [];
|
|
9
|
+
export function initDocsContext(opts) {
|
|
10
|
+
REPO_ROOT = opts.repoRoot;
|
|
11
|
+
SUBMODULES = opts.submodules;
|
|
12
|
+
}
|
|
13
|
+
function submodulePath(name) {
|
|
14
|
+
return __resolveForDocs(REPO_ROOT, name);
|
|
15
|
+
}
|
|
16
|
+
function isSubmoduleInitialized(name) {
|
|
17
|
+
return __existsSyncForDocs(__resolveForDocs(REPO_ROOT, name, ".git"));
|
|
18
|
+
}
|
|
19
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
20
|
+
import { join, relative } from "node:path";
|
|
21
|
+
const STALLED_PLAN_DAYS = 90;
|
|
22
|
+
const UNARCHIVED_SHIPPED_DAYS = 180;
|
|
23
|
+
const OPEN_ISSUE_DAYS = 30;
|
|
24
|
+
const COLD_HANDOFF_DAYS = 30;
|
|
25
|
+
const RUNBOOK_DAYS = 180;
|
|
26
|
+
const TOPIC_DOC_DAYS = 365;
|
|
27
|
+
const DECISIONS_DORMANT_DAYS = 180;
|
|
28
|
+
/** Days since last git commit touching a file */
|
|
29
|
+
async function lastCommitAgeDays(cwd, file) {
|
|
30
|
+
const result = await sh(`git log -1 --format=%aI -- "${file}"`, { cwd });
|
|
31
|
+
if (result.exitCode !== 0 || !result.stdout.trim())
|
|
32
|
+
return null;
|
|
33
|
+
const ms = Date.now() - new Date(result.stdout.trim()).getTime();
|
|
34
|
+
return Math.floor(ms / (1000 * 60 * 60 * 24));
|
|
35
|
+
}
|
|
36
|
+
/** Days since ANY commit in the repo (measures repo activity) */
|
|
37
|
+
async function lastRepoCommitAgeDays(cwd) {
|
|
38
|
+
const result = await sh("git log -1 --format=%aI", { cwd });
|
|
39
|
+
if (result.exitCode !== 0 || !result.stdout.trim())
|
|
40
|
+
return null;
|
|
41
|
+
const ms = Date.now() - new Date(result.stdout.trim()).getTime();
|
|
42
|
+
return Math.floor(ms / (1000 * 60 * 60 * 24));
|
|
43
|
+
}
|
|
44
|
+
function readStatus(filePath) {
|
|
45
|
+
try {
|
|
46
|
+
const content = readFileSync(filePath, "utf8");
|
|
47
|
+
const head = content.split("\n").slice(0, 20).join("\n");
|
|
48
|
+
const m = head.match(/\*\*Status:\*\*\s*([a-zA-Z][a-zA-Z-]*)/);
|
|
49
|
+
return m ? m[1].toLowerCase() : null;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async function sweepPlans(repoName, repoPath, items) {
|
|
56
|
+
const plansDir = join(repoPath, "docs", "plans");
|
|
57
|
+
if (!existsSync(plansDir))
|
|
58
|
+
return;
|
|
59
|
+
const walk = (dir) => {
|
|
60
|
+
const out = [];
|
|
61
|
+
for (const entry of readdirSync(dir)) {
|
|
62
|
+
const full = join(dir, entry);
|
|
63
|
+
let st;
|
|
64
|
+
try {
|
|
65
|
+
st = statSync(full);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (st.isDirectory()) {
|
|
71
|
+
out.push(...walk(full));
|
|
72
|
+
}
|
|
73
|
+
else if (entry.endsWith(".md") && entry !== "README.md") {
|
|
74
|
+
out.push(full);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
};
|
|
79
|
+
const files = walk(plansDir);
|
|
80
|
+
for (const full of files) {
|
|
81
|
+
const rel = relative(repoPath, full);
|
|
82
|
+
const displayPath = join(repoName === "(root)" ? "" : repoName, rel);
|
|
83
|
+
const isArchived = rel.includes("/archive/");
|
|
84
|
+
const status = readStatus(full);
|
|
85
|
+
const age = await lastCommitAgeDays(repoPath, rel);
|
|
86
|
+
if (age == null)
|
|
87
|
+
continue;
|
|
88
|
+
if (!isArchived && status === "in-progress" && age > STALLED_PLAN_DAYS) {
|
|
89
|
+
items.push({
|
|
90
|
+
kind: "stalled-plan",
|
|
91
|
+
repo: repoName,
|
|
92
|
+
path: displayPath,
|
|
93
|
+
ageDays: age,
|
|
94
|
+
message: `plan is in-progress, last touched ${age}d ago; confirm still active or bump status`,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
if (!isArchived && status === "shipped" && age > UNARCHIVED_SHIPPED_DAYS) {
|
|
98
|
+
items.push({
|
|
99
|
+
kind: "unarchived-shipped",
|
|
100
|
+
repo: repoName,
|
|
101
|
+
path: displayPath,
|
|
102
|
+
ageDays: age,
|
|
103
|
+
message: `plan is shipped but still in docs/plans/ after ${age}d; move to docs/plans/archive/`,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async function sweepIssues(repoName, repoPath, items) {
|
|
109
|
+
const issuesDir = join(repoPath, "docs", "issues");
|
|
110
|
+
if (!existsSync(issuesDir))
|
|
111
|
+
return;
|
|
112
|
+
for (const entry of readdirSync(issuesDir)) {
|
|
113
|
+
if (!entry.endsWith(".md") || entry === "README.md")
|
|
114
|
+
continue;
|
|
115
|
+
const full = join(issuesDir, entry);
|
|
116
|
+
const rel = join("docs", "issues", entry);
|
|
117
|
+
const displayPath = join(repoName === "(root)" ? "" : repoName, rel);
|
|
118
|
+
const status = readStatus(full);
|
|
119
|
+
if (status !== "open")
|
|
120
|
+
continue;
|
|
121
|
+
const age = await lastCommitAgeDays(repoPath, rel);
|
|
122
|
+
if (age == null || age <= OPEN_ISSUE_DAYS)
|
|
123
|
+
continue;
|
|
124
|
+
items.push({
|
|
125
|
+
kind: "open-issue-cold",
|
|
126
|
+
repo: repoName,
|
|
127
|
+
path: displayPath,
|
|
128
|
+
ageDays: age,
|
|
129
|
+
message: `issue still marked open after ${age}d; resolve, mark wontfix, or triage`,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
async function sweepHandoffs(repoName, repoPath, items) {
|
|
134
|
+
const handoffsDir = join(repoPath, "docs", "handoffs");
|
|
135
|
+
if (!existsSync(handoffsDir))
|
|
136
|
+
return;
|
|
137
|
+
const walk = (dir) => {
|
|
138
|
+
const out = [];
|
|
139
|
+
for (const entry of readdirSync(dir)) {
|
|
140
|
+
const full = join(dir, entry);
|
|
141
|
+
let st;
|
|
142
|
+
try {
|
|
143
|
+
st = statSync(full);
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (st.isDirectory()) {
|
|
149
|
+
out.push(...walk(full));
|
|
150
|
+
}
|
|
151
|
+
else if (entry.endsWith(".md")) {
|
|
152
|
+
out.push(full);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return out;
|
|
156
|
+
};
|
|
157
|
+
for (const full of walk(handoffsDir)) {
|
|
158
|
+
const rel = relative(repoPath, full);
|
|
159
|
+
const status = readStatus(full);
|
|
160
|
+
if (status !== "open")
|
|
161
|
+
continue;
|
|
162
|
+
const age = await lastCommitAgeDays(repoPath, rel);
|
|
163
|
+
if (age == null || age <= COLD_HANDOFF_DAYS)
|
|
164
|
+
continue;
|
|
165
|
+
const displayPath = join(repoName === "(root)" ? "" : repoName, rel);
|
|
166
|
+
items.push({
|
|
167
|
+
kind: "cold-handoff",
|
|
168
|
+
repo: repoName,
|
|
169
|
+
path: displayPath,
|
|
170
|
+
ageDays: age,
|
|
171
|
+
message: `handoff still marked open after ${age}d; resolve, abandon, or note current progress`,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
async function sweepRunbook(repoName, repoPath, items) {
|
|
176
|
+
const runbook = join(repoPath, "docs", "runbook.md");
|
|
177
|
+
if (!existsSync(runbook))
|
|
178
|
+
return;
|
|
179
|
+
const age = await lastCommitAgeDays(repoPath, "docs/runbook.md");
|
|
180
|
+
if (age == null || age <= RUNBOOK_DAYS)
|
|
181
|
+
return;
|
|
182
|
+
const displayPath = join(repoName === "(root)" ? "" : repoName, "docs/runbook.md");
|
|
183
|
+
items.push({
|
|
184
|
+
kind: "runbook-unverified",
|
|
185
|
+
repo: repoName,
|
|
186
|
+
path: displayPath,
|
|
187
|
+
ageDays: age,
|
|
188
|
+
message: `runbook hasn't been edited in ${age}d; re-verify procedures`,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
async function sweepTopicDocs(repoName, repoPath, items) {
|
|
192
|
+
const docsDir = join(repoPath, "docs");
|
|
193
|
+
if (!existsSync(docsDir))
|
|
194
|
+
return;
|
|
195
|
+
// Skip known date-stamped or lifecycle-managed dirs
|
|
196
|
+
const skipDirs = new Set([
|
|
197
|
+
"audits",
|
|
198
|
+
"issues",
|
|
199
|
+
"plans",
|
|
200
|
+
"changelogs",
|
|
201
|
+
"emails", // parent-specific
|
|
202
|
+
]);
|
|
203
|
+
for (const entry of readdirSync(docsDir)) {
|
|
204
|
+
if (skipDirs.has(entry))
|
|
205
|
+
continue;
|
|
206
|
+
const full = join(docsDir, entry);
|
|
207
|
+
let st;
|
|
208
|
+
try {
|
|
209
|
+
st = statSync(full);
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (!st.isDirectory())
|
|
215
|
+
continue;
|
|
216
|
+
// For each topic dir, find .md files and check age
|
|
217
|
+
const walk = (dir) => {
|
|
218
|
+
const out = [];
|
|
219
|
+
for (const f of readdirSync(dir)) {
|
|
220
|
+
const fp = join(dir, f);
|
|
221
|
+
let s;
|
|
222
|
+
try {
|
|
223
|
+
s = statSync(fp);
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
if (s.isDirectory())
|
|
229
|
+
out.push(...walk(fp));
|
|
230
|
+
else if (f.endsWith(".md"))
|
|
231
|
+
out.push(fp);
|
|
232
|
+
}
|
|
233
|
+
return out;
|
|
234
|
+
};
|
|
235
|
+
for (const full2 of walk(full)) {
|
|
236
|
+
const rel = relative(repoPath, full2);
|
|
237
|
+
const age = await lastCommitAgeDays(repoPath, rel);
|
|
238
|
+
if (age == null || age <= TOPIC_DOC_DAYS)
|
|
239
|
+
continue;
|
|
240
|
+
const displayPath = join(repoName === "(root)" ? "" : repoName, rel);
|
|
241
|
+
items.push({
|
|
242
|
+
kind: "topic-doc-stale",
|
|
243
|
+
repo: repoName,
|
|
244
|
+
path: displayPath,
|
|
245
|
+
ageDays: age,
|
|
246
|
+
message: `topic doc untouched for ${age}d; confirm still accurate or archive`,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
async function sweepDecisions(repoName, repoPath, items) {
|
|
252
|
+
const decisions = join(repoPath, "docs", "decisions.md");
|
|
253
|
+
if (!existsSync(decisions))
|
|
254
|
+
return;
|
|
255
|
+
const age = await lastCommitAgeDays(repoPath, "docs/decisions.md");
|
|
256
|
+
const repoAge = await lastRepoCommitAgeDays(repoPath);
|
|
257
|
+
if (age == null || repoAge == null)
|
|
258
|
+
return;
|
|
259
|
+
// Only flag if the repo is active (recent commits) but decisions haven't moved
|
|
260
|
+
if (age <= DECISIONS_DORMANT_DAYS)
|
|
261
|
+
return;
|
|
262
|
+
if (repoAge > 30)
|
|
263
|
+
return; // repo itself is dormant, nothing to decide
|
|
264
|
+
const displayPath = join(repoName === "(root)" ? "" : repoName, "docs/decisions.md");
|
|
265
|
+
items.push({
|
|
266
|
+
kind: "decisions-dormant",
|
|
267
|
+
repo: repoName,
|
|
268
|
+
path: displayPath,
|
|
269
|
+
ageDays: age,
|
|
270
|
+
message: `repo is active but decisions.md hasn't been appended to in ${age}d; any architectural choices undocumented?`,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
export async function runSweep(opts) {
|
|
274
|
+
const targets = [{ name: "(root)", path: REPO_ROOT }];
|
|
275
|
+
for (const name of SUBMODULES) {
|
|
276
|
+
if (!isSubmoduleInitialized(name))
|
|
277
|
+
continue;
|
|
278
|
+
targets.push({ name, path: submodulePath(name) });
|
|
279
|
+
}
|
|
280
|
+
const filter = opts.repo === "." ? "(root)" : opts.repo;
|
|
281
|
+
const filtered = filter ? targets.filter((t) => t.name === filter) : targets;
|
|
282
|
+
const items = [];
|
|
283
|
+
for (const { name, path } of filtered) {
|
|
284
|
+
await sweepPlans(name, path, items);
|
|
285
|
+
await sweepIssues(name, path, items);
|
|
286
|
+
await sweepHandoffs(name, path, items);
|
|
287
|
+
await sweepRunbook(name, path, items);
|
|
288
|
+
await sweepTopicDocs(name, path, items);
|
|
289
|
+
await sweepDecisions(name, path, items);
|
|
290
|
+
}
|
|
291
|
+
// Sort by severity proxy: oldest first within kind
|
|
292
|
+
items.sort((a, b) => b.ageDays - a.ageDays);
|
|
293
|
+
return items;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Cheap parent-repo-only count of `cold-handoff` items, used by `docs lint`
|
|
297
|
+
* to print a one-line nudge without running the full sweep across every
|
|
298
|
+
* submodule (handoffs live under the parent's docs/handoffs/ by convention).
|
|
299
|
+
*/
|
|
300
|
+
export async function countColdHandoffs() {
|
|
301
|
+
const items = [];
|
|
302
|
+
await sweepHandoffs("(root)", REPO_ROOT, items);
|
|
303
|
+
return items.length;
|
|
304
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export declare function initDocsContext(opts: {
|
|
2
|
+
repoRoot: string;
|
|
3
|
+
submodules: readonly string[];
|
|
4
|
+
}): void;
|
|
5
|
+
export interface DocCommit {
|
|
6
|
+
date: string;
|
|
7
|
+
message: string;
|
|
8
|
+
}
|
|
9
|
+
export interface DocFileInfo {
|
|
10
|
+
path: string;
|
|
11
|
+
dir: string;
|
|
12
|
+
commits: DocCommit[];
|
|
13
|
+
lastCommitDate: string | null;
|
|
14
|
+
lastCommitAgeDays: number | null;
|
|
15
|
+
untracked: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Scan for .md files across the parent repo and all submodules.
|
|
19
|
+
* Uses `git ls-files` to only find tracked files (skips .venv, node_modules, etc.).
|
|
20
|
+
*/
|
|
21
|
+
export declare function scanDocs(opts: {
|
|
22
|
+
commitCount?: number;
|
|
23
|
+
dir?: string;
|
|
24
|
+
noSubmodules?: boolean;
|
|
25
|
+
staleDays?: number;
|
|
26
|
+
}): Promise<DocFileInfo[]>;
|
|
27
|
+
//# sourceMappingURL=docs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs.d.ts","sourceRoot":"","sources":["../../src/lib/docs.ts"],"names":[],"mappings":"AAUA,wBAAgB,eAAe,CAAC,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,CAG/F;AAUD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,SAAS,EAAE,OAAO,CAAC;CACpB;AAaD;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CA4DzB"}
|
package/dist/lib/docs.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { existsSync as __existsSyncForDocs } from "node:fs";
|
|
2
|
+
import { resolve as __resolveForDocs } from "node:path";
|
|
3
|
+
import { sh } from "./exec.js";
|
|
4
|
+
// Module-level docs context, initialized by initDocsContext() before any
|
|
5
|
+
// other function in this file is called. The repo root + submodule list
|
|
6
|
+
// are passed via the context provided to registerDocsCommand.
|
|
7
|
+
let REPO_ROOT = "";
|
|
8
|
+
let SUBMODULES = [];
|
|
9
|
+
export function initDocsContext(opts) {
|
|
10
|
+
REPO_ROOT = opts.repoRoot;
|
|
11
|
+
SUBMODULES = opts.submodules;
|
|
12
|
+
}
|
|
13
|
+
function submodulePath(name) {
|
|
14
|
+
return __resolveForDocs(REPO_ROOT, name);
|
|
15
|
+
}
|
|
16
|
+
function isSubmoduleInitialized(name) {
|
|
17
|
+
return __existsSyncForDocs(__resolveForDocs(REPO_ROOT, name, ".git"));
|
|
18
|
+
}
|
|
19
|
+
/** Directories/patterns to skip even within git-tracked files */
|
|
20
|
+
const SKIP_PATTERNS = [
|
|
21
|
+
/\/\.cursor\//,
|
|
22
|
+
/\/\.claude\//,
|
|
23
|
+
/\/\.pytest_cache\//,
|
|
24
|
+
/\/AGENTS\.md$/, // Auto-generated
|
|
25
|
+
];
|
|
26
|
+
/** Top-level directories to ignore by default (path prefix match) */
|
|
27
|
+
const IGNORE_DIRS = ["docs/vendors"];
|
|
28
|
+
/**
|
|
29
|
+
* Scan for .md files across the parent repo and all submodules.
|
|
30
|
+
* Uses `git ls-files` to only find tracked files (skips .venv, node_modules, etc.).
|
|
31
|
+
*/
|
|
32
|
+
export async function scanDocs(opts) {
|
|
33
|
+
const commitCount = opts.commitCount ?? 1;
|
|
34
|
+
const now = Date.now();
|
|
35
|
+
// Collect tracked .md files from parent repo
|
|
36
|
+
const parentFiles = await getTrackedMdFiles(REPO_ROOT, "");
|
|
37
|
+
// Collect tracked .md files from each initialized submodule (unless --no-submodules)
|
|
38
|
+
let submoduleFiles = [];
|
|
39
|
+
if (!opts.noSubmodules) {
|
|
40
|
+
const submoduleResults = await Promise.all(SUBMODULES.filter(isSubmoduleInitialized).map(async (name) => {
|
|
41
|
+
const cwd = submodulePath(name);
|
|
42
|
+
return getTrackedMdFiles(cwd, name);
|
|
43
|
+
}));
|
|
44
|
+
submoduleFiles = submoduleResults.flat();
|
|
45
|
+
}
|
|
46
|
+
let allFiles = [...parentFiles, ...submoduleFiles];
|
|
47
|
+
// Filter out skipped patterns and ignored directories
|
|
48
|
+
allFiles = allFiles.filter((f) => !SKIP_PATTERNS.some((p) => p.test(`/${f}`)) &&
|
|
49
|
+
!IGNORE_DIRS.some((d) => f.startsWith(`${d}/`)));
|
|
50
|
+
// Filter by directory if specified
|
|
51
|
+
if (opts.dir) {
|
|
52
|
+
// Treat "." as an alias for "(root)", the natural way to say "root-level files"
|
|
53
|
+
const dirFilter = opts.dir === "." ? "(root)" : opts.dir.toLowerCase();
|
|
54
|
+
allFiles = allFiles.filter((f) => {
|
|
55
|
+
const topDir = getTopDir(f).toLowerCase();
|
|
56
|
+
// Exact match first, fall back to substring
|
|
57
|
+
return topDir === dirFilter || topDir.includes(dirFilter);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
// Fetch git history for all files, batched in parallel groups of 20
|
|
61
|
+
const results = [];
|
|
62
|
+
const batchSize = 20;
|
|
63
|
+
for (let i = 0; i < allFiles.length; i += batchSize) {
|
|
64
|
+
const batch = allFiles.slice(i, i + batchSize);
|
|
65
|
+
const batchResults = await Promise.all(batch.map((filePath) => getFileInfo(filePath, commitCount, now)));
|
|
66
|
+
results.push(...batchResults);
|
|
67
|
+
}
|
|
68
|
+
// Filter by stale days if specified
|
|
69
|
+
if (opts.staleDays) {
|
|
70
|
+
const minAge = opts.staleDays;
|
|
71
|
+
return results.filter((r) => r.untracked || (r.lastCommitAgeDays !== null && r.lastCommitAgeDays >= minAge));
|
|
72
|
+
}
|
|
73
|
+
return results;
|
|
74
|
+
}
|
|
75
|
+
/** Get top-level directory for grouping */
|
|
76
|
+
function getTopDir(filePath) {
|
|
77
|
+
const parts = filePath.split("/");
|
|
78
|
+
if (parts.length === 1)
|
|
79
|
+
return "(root)";
|
|
80
|
+
return parts[0];
|
|
81
|
+
}
|
|
82
|
+
/** Get tracked .md files from a git repo */
|
|
83
|
+
async function getTrackedMdFiles(cwd, prefix) {
|
|
84
|
+
// Use shell to avoid pathspec vs glob ambiguity; ** ensures recursive matching
|
|
85
|
+
const result = await sh('git ls-files --cached "**/*.md" "*.md"', { cwd });
|
|
86
|
+
if (result.exitCode !== 0 || !result.stdout)
|
|
87
|
+
return [];
|
|
88
|
+
return result.stdout
|
|
89
|
+
.split("\n")
|
|
90
|
+
.filter((f) => f.endsWith(".md"))
|
|
91
|
+
.map((f) => (prefix ? `${prefix}/${f}` : f));
|
|
92
|
+
}
|
|
93
|
+
/** Get file info with git history */
|
|
94
|
+
async function getFileInfo(filePath, commitCount, now) {
|
|
95
|
+
const dir = getTopDir(filePath);
|
|
96
|
+
// Determine which repo to query and the relative path within it
|
|
97
|
+
const { cwd, relativePath } = resolveFilePath(filePath);
|
|
98
|
+
const logResult = await sh(`git log -${commitCount} --format="%aI%x09%s" -- "${relativePath}"`, {
|
|
99
|
+
cwd,
|
|
100
|
+
});
|
|
101
|
+
if (logResult.exitCode !== 0 || !logResult.stdout.trim()) {
|
|
102
|
+
return {
|
|
103
|
+
path: filePath,
|
|
104
|
+
dir,
|
|
105
|
+
commits: [],
|
|
106
|
+
lastCommitDate: null,
|
|
107
|
+
lastCommitAgeDays: null,
|
|
108
|
+
untracked: true,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const commits = logResult.stdout
|
|
112
|
+
.trim()
|
|
113
|
+
.split("\n")
|
|
114
|
+
.map((line) => {
|
|
115
|
+
const [date, ...msgParts] = line.split("\t");
|
|
116
|
+
return { date: date, message: msgParts.join("\t") };
|
|
117
|
+
});
|
|
118
|
+
const lastDate = commits[0]?.date ?? null;
|
|
119
|
+
const ageDays = lastDate
|
|
120
|
+
? Math.floor((now - new Date(lastDate).getTime()) / (1000 * 60 * 60 * 24))
|
|
121
|
+
: null;
|
|
122
|
+
return {
|
|
123
|
+
path: filePath,
|
|
124
|
+
dir,
|
|
125
|
+
commits,
|
|
126
|
+
lastCommitDate: lastDate,
|
|
127
|
+
lastCommitAgeDays: ageDays,
|
|
128
|
+
untracked: false,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/** Resolve a monorepo-relative path to the correct git repo cwd + relative path */
|
|
132
|
+
function resolveFilePath(filePath) {
|
|
133
|
+
for (const name of SUBMODULES) {
|
|
134
|
+
if (filePath.startsWith(`${name}/`)) {
|
|
135
|
+
return {
|
|
136
|
+
cwd: submodulePath(name),
|
|
137
|
+
relativePath: filePath.slice(name.length + 1),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return { cwd: REPO_ROOT, relativePath: filePath };
|
|
142
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read a harnery environment variable by its suffix (the part after the
|
|
3
|
+
* `HARNERY_` prefix). Centralizes the env-var namespace so the prefix lives in
|
|
4
|
+
* exactly one place.
|
|
5
|
+
*
|
|
6
|
+
* Examples:
|
|
7
|
+
* coordEnv("AGENT_COORD_OWNER") → process.env.HARNERY_AGENT_COORD_OWNER
|
|
8
|
+
* coordEnv("COORD_ROOT_OVERRIDE") → process.env.HARNERY_COORD_ROOT_OVERRIDE
|
|
9
|
+
*/
|
|
10
|
+
export declare function coordEnv(suffix: string): string | undefined;
|
|
11
|
+
//# sourceMappingURL=env.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/lib/env.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE3D"}
|
package/dist/lib/env.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read a harnery environment variable by its suffix (the part after the
|
|
3
|
+
* `HARNERY_` prefix). Centralizes the env-var namespace so the prefix lives in
|
|
4
|
+
* exactly one place.
|
|
5
|
+
*
|
|
6
|
+
* Examples:
|
|
7
|
+
* coordEnv("AGENT_COORD_OWNER") → process.env.HARNERY_AGENT_COORD_OWNER
|
|
8
|
+
* coordEnv("COORD_ROOT_OVERRIDE") → process.env.HARNERY_COORD_ROOT_OVERRIDE
|
|
9
|
+
*/
|
|
10
|
+
export function coordEnv(suffix) {
|
|
11
|
+
return process.env[`HARNERY_${suffix}`];
|
|
12
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic process-spawn helpers shared by harnery commands. Callers pass
|
|
3
|
+
* `cwd` explicitly or fall back to `process.cwd()`.
|
|
4
|
+
*
|
|
5
|
+
* Built on `node:child_process` so it runs identically under Bun and Node.
|
|
6
|
+
* harnery's published package targets Node ≥ 20, and Bun implements the same
|
|
7
|
+
* module, so there's one implementation, not a runtime fork.
|
|
8
|
+
*/
|
|
9
|
+
export interface ExecResult {
|
|
10
|
+
stdout: string;
|
|
11
|
+
stderr: string;
|
|
12
|
+
exitCode: number;
|
|
13
|
+
}
|
|
14
|
+
export interface ExecOpts {
|
|
15
|
+
cwd?: string;
|
|
16
|
+
timeout?: number;
|
|
17
|
+
env?: Record<string, string>;
|
|
18
|
+
trim?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Run a shell command and return stdout/stderr/exitCode.
|
|
22
|
+
*
|
|
23
|
+
* `trim` defaults to true (back-compat with every existing caller). Pass
|
|
24
|
+
* `trim: false` when the consumer depends on leading whitespace. `git status
|
|
25
|
+
* --porcelain` is the canonical case: the first line of ` M PATH` output gets
|
|
26
|
+
* its leading space stripped by .trim(), shifting the X/Y status columns and
|
|
27
|
+
* breaking any positional parser. Trailing newline still gets dropped.
|
|
28
|
+
*/
|
|
29
|
+
export declare function exec(cmd: string[], opts?: ExecOpts): Promise<ExecResult>;
|
|
30
|
+
/** Run a shell command via /bin/sh -c (for pipes, redirects, etc.) */
|
|
31
|
+
export declare function sh(command: string, opts?: Omit<ExecOpts, "trim">): Promise<ExecResult>;
|
|
32
|
+
//# sourceMappingURL=exec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../src/lib/exec.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,QAAQ;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;;;GAQG;AACH,wBAAgB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,IAAI,GAAE,QAAa,GAAG,OAAO,CAAC,UAAU,CAAC,CAmC5E;AAED,sEAAsE;AACtE,wBAAsB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAEhG"}
|
package/dist/lib/exec.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic process-spawn helpers shared by harnery commands. Callers pass
|
|
3
|
+
* `cwd` explicitly or fall back to `process.cwd()`.
|
|
4
|
+
*
|
|
5
|
+
* Built on `node:child_process` so it runs identically under Bun and Node.
|
|
6
|
+
* harnery's published package targets Node ≥ 20, and Bun implements the same
|
|
7
|
+
* module, so there's one implementation, not a runtime fork.
|
|
8
|
+
*/
|
|
9
|
+
import { spawn } from "node:child_process";
|
|
10
|
+
/**
|
|
11
|
+
* Run a shell command and return stdout/stderr/exitCode.
|
|
12
|
+
*
|
|
13
|
+
* `trim` defaults to true (back-compat with every existing caller). Pass
|
|
14
|
+
* `trim: false` when the consumer depends on leading whitespace. `git status
|
|
15
|
+
* --porcelain` is the canonical case: the first line of ` M PATH` output gets
|
|
16
|
+
* its leading space stripped by .trim(), shifting the X/Y status columns and
|
|
17
|
+
* breaking any positional parser. Trailing newline still gets dropped.
|
|
18
|
+
*/
|
|
19
|
+
export function exec(cmd, opts = {}) {
|
|
20
|
+
const [file, ...args] = cmd;
|
|
21
|
+
return new Promise((resolveResult) => {
|
|
22
|
+
const proc = spawn(file, args, {
|
|
23
|
+
cwd: opts.cwd ?? process.cwd(),
|
|
24
|
+
env: opts.env ?? { ...process.env },
|
|
25
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
26
|
+
});
|
|
27
|
+
const out = [];
|
|
28
|
+
const err = [];
|
|
29
|
+
proc.stdout?.on("data", (d) => out.push(d));
|
|
30
|
+
proc.stderr?.on("data", (d) => err.push(d));
|
|
31
|
+
const timeout = opts.timeout ?? 30_000;
|
|
32
|
+
const timer = setTimeout(() => proc.kill(), timeout);
|
|
33
|
+
const finish = (exitCode, errOverride) => {
|
|
34
|
+
clearTimeout(timer);
|
|
35
|
+
const stdout = Buffer.concat(out).toString("utf-8");
|
|
36
|
+
const stderr = errOverride ?? Buffer.concat(err).toString("utf-8");
|
|
37
|
+
const shouldTrim = opts.trim !== false;
|
|
38
|
+
resolveResult({
|
|
39
|
+
stdout: shouldTrim ? stdout.trim() : stdout.replace(/\n$/, ""),
|
|
40
|
+
stderr: shouldTrim ? stderr.trim() : stderr.replace(/\n$/, ""),
|
|
41
|
+
exitCode,
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
// ENOENT (binary not found) and similar spawn failures surface here rather
|
|
45
|
+
// than throwing: resolve with 127 + the message so callers' exitCode
|
|
46
|
+
// branches handle it instead of crashing on an unhandled rejection.
|
|
47
|
+
proc.on("error", (e) => finish(127, e.message));
|
|
48
|
+
proc.on("close", (code) => finish(code ?? 1));
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/** Run a shell command via /bin/sh -c (for pipes, redirects, etc.) */
|
|
52
|
+
export async function sh(command, opts = {}) {
|
|
53
|
+
return exec(["sh", "-c", command], opts);
|
|
54
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export declare const c: {
|
|
2
|
+
bold: (s: string) => string;
|
|
3
|
+
dim: (s: string) => string;
|
|
4
|
+
green: (s: string) => string;
|
|
5
|
+
red: (s: string) => string;
|
|
6
|
+
yellow: (s: string) => string;
|
|
7
|
+
cyan: (s: string) => string;
|
|
8
|
+
gray: (s: string) => string;
|
|
9
|
+
magenta: (s: string) => string;
|
|
10
|
+
};
|
|
11
|
+
/** Placeholder shown when a value is missing (null/empty), e.g. an unknown branch or age. */
|
|
12
|
+
export declare const NO_DATA = "\u2014";
|
|
13
|
+
/**
|
|
14
|
+
* Pretty-print + ANSI-colorize a JSON value. 2-space indent. TTY-aware: when
|
|
15
|
+
* stdout is not a TTY, the underlying `c.*` helpers emit no ANSI codes, so
|
|
16
|
+
* output is plain text. Use this for `--pretty` flags on JSON-emitting commands.
|
|
17
|
+
*
|
|
18
|
+
* Colors: keys=cyan, strings=green, numbers=magenta, booleans=yellow, null=dim.
|
|
19
|
+
*/
|
|
20
|
+
export declare function colorJson(value: unknown, indent?: number): string;
|
|
21
|
+
/** Render a table from rows of objects */
|
|
22
|
+
export declare function table(rows: Record<string, unknown>[], opts?: {
|
|
23
|
+
maxColWidth?: number;
|
|
24
|
+
}): string;
|
|
25
|
+
/** Format rows as CSV */
|
|
26
|
+
export declare function csv(rows: Record<string, unknown>[]): string;
|
|
27
|
+
/** Print a labeled key-value section */
|
|
28
|
+
export declare function kvLine(label: string, value: string, color?: (s: string) => string): string;
|
|
29
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/lib/format.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,CAAC;cACF,MAAM;aACP,MAAM;eACJ,MAAM;aACR,MAAM;gBACH,MAAM;cACR,MAAM;cACN,MAAM;iBACH,MAAM;CACpB,CAAC;AAEF,6FAA6F;AAC7F,eAAO,MAAM,OAAO,WAAM,CAAC;AAE3B;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,SAAI,GAAG,MAAM,CA4C5D;AAED,0CAA0C;AAC1C,wBAAgB,KAAK,CACnB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC/B,IAAI,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,MAAM,CAiCR;AAgBD,yBAAyB;AACzB,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,MAAM,CAM3D;AASD,wCAAwC;AACxC,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,CAG1F"}
|