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,363 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import type { Command } from "commander";
|
|
3
|
+
import type { EmitContext, HarneryProgramContext } from "../commander.ts";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* `harn callers <symbol>`: find references to a symbol across the monorepo with
|
|
7
|
+
* kind classification (call / import / type / decl / ref). Thin wrapper over
|
|
8
|
+
* grep -rnw with post-filtering: declarations + single-line comments are
|
|
9
|
+
* filtered out by default, and lines starting with " * " (multi-line comment
|
|
10
|
+
* continuation) are dropped. Inherent text-based-search limitations: multi-line
|
|
11
|
+
* string literals and block-comment interiors aren't excluded.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
interface CallersOpts {
|
|
15
|
+
repo?: string;
|
|
16
|
+
allRepos?: boolean;
|
|
17
|
+
lang?: string;
|
|
18
|
+
includeDecl?: boolean;
|
|
19
|
+
includeComments?: boolean;
|
|
20
|
+
limit?: string;
|
|
21
|
+
json?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface Caller {
|
|
25
|
+
repo: string;
|
|
26
|
+
file: string;
|
|
27
|
+
line: number;
|
|
28
|
+
kind: "call" | "import" | "decl" | "type" | "ref";
|
|
29
|
+
text: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface CallersResult {
|
|
33
|
+
symbol: string;
|
|
34
|
+
repos: { name: string; cwd: string }[];
|
|
35
|
+
callers: Caller[];
|
|
36
|
+
by_kind: Record<string, number>;
|
|
37
|
+
total: number;
|
|
38
|
+
truncated: boolean;
|
|
39
|
+
elapsed_ms: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const DEFAULT_EXCLUDE_DIRS = [
|
|
43
|
+
".git",
|
|
44
|
+
".cache",
|
|
45
|
+
"node_modules",
|
|
46
|
+
"dist",
|
|
47
|
+
"build",
|
|
48
|
+
"out",
|
|
49
|
+
".next",
|
|
50
|
+
"vendor",
|
|
51
|
+
"__pycache__",
|
|
52
|
+
".venv",
|
|
53
|
+
"tmp",
|
|
54
|
+
"tmp-files",
|
|
55
|
+
"coverage",
|
|
56
|
+
".parcel-cache",
|
|
57
|
+
".turbo",
|
|
58
|
+
".harnery",
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
// (no file-name excludes by default).
|
|
62
|
+
const DEFAULT_EXCLUDE_FILES: string[] = [];
|
|
63
|
+
|
|
64
|
+
const LANG_GLOBS: Record<string, string[]> = {
|
|
65
|
+
ts: ["*.ts", "*.tsx"],
|
|
66
|
+
js: ["*.js", "*.jsx", "*.mjs", "*.cjs"],
|
|
67
|
+
py: ["*.py"],
|
|
68
|
+
php: ["*.php"],
|
|
69
|
+
rb: ["*.rb"],
|
|
70
|
+
go: ["*.go"],
|
|
71
|
+
rs: ["*.rs"],
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export function registerCallersCommand(
|
|
75
|
+
program: Command,
|
|
76
|
+
emit: EmitContext,
|
|
77
|
+
context?: HarneryProgramContext,
|
|
78
|
+
): void {
|
|
79
|
+
program
|
|
80
|
+
.command("callers <symbol>")
|
|
81
|
+
.description(
|
|
82
|
+
"Find references to a symbol across the monorepo with kind classification " +
|
|
83
|
+
"(call / import / type / decl / ref). Heuristic: filters out declarations + " +
|
|
84
|
+
"single-line comments by default; opt back in with --include-decl / --include-comments.",
|
|
85
|
+
)
|
|
86
|
+
.option("--repo <name>", "Scope to one submodule (`.` = parent repo root)")
|
|
87
|
+
.option("--all-repos", "Search parent + every submodule")
|
|
88
|
+
.option("--lang <lang>", `File type preset (${Object.keys(LANG_GLOBS).join(", ")})`)
|
|
89
|
+
.option("--include-decl", "Include the declaration line(s) too")
|
|
90
|
+
.option("--include-comments", "Include matches in single-line comments")
|
|
91
|
+
.option("--limit <n>", "Truncate output to N matches total", "500")
|
|
92
|
+
.option("--json", "Structured JSON envelope")
|
|
93
|
+
.action(async (symbol: string, opts: CallersOpts) => {
|
|
94
|
+
try {
|
|
95
|
+
const result = await runCallers(symbol, opts, context);
|
|
96
|
+
if (opts.json) {
|
|
97
|
+
emit.config({ format: "json" });
|
|
98
|
+
emit.data(result);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
emit.text(`${renderCallers(result)}\n`);
|
|
102
|
+
} catch (err) {
|
|
103
|
+
emit.error({ code: "callers_failed", message: (err as Error).message });
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function runCallers(
|
|
110
|
+
symbol: string,
|
|
111
|
+
opts: CallersOpts,
|
|
112
|
+
context: HarneryProgramContext | undefined,
|
|
113
|
+
): Promise<CallersResult> {
|
|
114
|
+
if (!/^[A-Za-z_$][\w$]*$/.test(symbol)) {
|
|
115
|
+
throw new Error(`symbol must be a valid identifier (got: ${symbol})`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const started = Date.now();
|
|
119
|
+
const repos = resolveRepos(opts, context);
|
|
120
|
+
const limit = opts.limit ? Number.parseInt(opts.limit, 10) : 500;
|
|
121
|
+
|
|
122
|
+
const allCallers: Caller[] = [];
|
|
123
|
+
let truncated = false;
|
|
124
|
+
|
|
125
|
+
for (const repo of repos) {
|
|
126
|
+
if (allCallers.length >= limit) {
|
|
127
|
+
truncated = true;
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
const remaining = limit - allCallers.length;
|
|
131
|
+
const found = await grepInRepo(symbol, opts, repo, remaining);
|
|
132
|
+
if (found.truncated) truncated = true;
|
|
133
|
+
allCallers.push(...found.callers);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const by_kind: Record<string, number> = {};
|
|
137
|
+
for (const c of allCallers) {
|
|
138
|
+
by_kind[c.kind] = (by_kind[c.kind] || 0) + 1;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
symbol,
|
|
143
|
+
repos: repos.map((r) => ({ name: r.name, cwd: r.cwd })),
|
|
144
|
+
callers: allCallers,
|
|
145
|
+
by_kind,
|
|
146
|
+
total: allCallers.length,
|
|
147
|
+
truncated,
|
|
148
|
+
elapsed_ms: Date.now() - started,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function resolveRepos(
|
|
153
|
+
opts: CallersOpts,
|
|
154
|
+
context: HarneryProgramContext | undefined,
|
|
155
|
+
): { name: string; cwd: string }[] {
|
|
156
|
+
const repoRoot = context?.repoRoot;
|
|
157
|
+
const submodules = context?.submodules;
|
|
158
|
+
if (opts.allRepos) {
|
|
159
|
+
if (!repoRoot || !submodules) {
|
|
160
|
+
throw new Error("--all-repos requires harnery to be configured with repoRoot + submodules");
|
|
161
|
+
}
|
|
162
|
+
const out: { name: string; cwd: string }[] = [{ name: "parent", cwd: repoRoot }];
|
|
163
|
+
for (const name of submodules) out.push({ name, cwd: `${repoRoot}/${name}` });
|
|
164
|
+
return out;
|
|
165
|
+
}
|
|
166
|
+
if (opts.repo) {
|
|
167
|
+
if (opts.repo === "." || opts.repo === "parent") {
|
|
168
|
+
if (!repoRoot) {
|
|
169
|
+
throw new Error('--repo "." requires harnery to be configured with repoRoot');
|
|
170
|
+
}
|
|
171
|
+
return [{ name: "parent", cwd: repoRoot }];
|
|
172
|
+
}
|
|
173
|
+
if (!submodules || !repoRoot) {
|
|
174
|
+
throw new Error(
|
|
175
|
+
`--repo "${opts.repo}" requires harnery to be configured with repoRoot + submodules`,
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
const found = submodules.find((n) => n === opts.repo);
|
|
179
|
+
if (!found) {
|
|
180
|
+
throw new Error(`unknown repo "${opts.repo}". Valid: parent (.), ${submodules.join(", ")}`);
|
|
181
|
+
}
|
|
182
|
+
return [{ name: found, cwd: `${repoRoot}/${found}` }];
|
|
183
|
+
}
|
|
184
|
+
return [{ name: "cwd", cwd: process.cwd() }];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function grepInRepo(
|
|
188
|
+
symbol: string,
|
|
189
|
+
opts: CallersOpts,
|
|
190
|
+
repo: { name: string; cwd: string },
|
|
191
|
+
limit: number,
|
|
192
|
+
): Promise<{ callers: Caller[]; truncated: boolean }> {
|
|
193
|
+
const args: string[] = ["-rnw", "--color=never", "-I", "-E"];
|
|
194
|
+
for (const d of DEFAULT_EXCLUDE_DIRS) args.push(`--exclude-dir=${d}`);
|
|
195
|
+
for (const f of DEFAULT_EXCLUDE_FILES) args.push(`--exclude=${f}`);
|
|
196
|
+
const langGlobs = opts.lang ? LANG_GLOBS[opts.lang] : undefined;
|
|
197
|
+
if (opts.lang && !langGlobs) {
|
|
198
|
+
throw new Error(`unknown --lang "${opts.lang}". Valid: ${Object.keys(LANG_GLOBS).join(", ")}`);
|
|
199
|
+
}
|
|
200
|
+
if (langGlobs) for (const g of langGlobs) args.push(`--include=${g}`);
|
|
201
|
+
args.push("--", symbol, ".");
|
|
202
|
+
|
|
203
|
+
return new Promise((resolveP, reject) => {
|
|
204
|
+
const proc = spawn("grep", args, { cwd: repo.cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
205
|
+
const callers: Caller[] = [];
|
|
206
|
+
let buffer = "";
|
|
207
|
+
let stderr = "";
|
|
208
|
+
let truncated = false;
|
|
209
|
+
|
|
210
|
+
proc.stdout.setEncoding("utf8");
|
|
211
|
+
proc.stdout.on("data", (chunk: string) => {
|
|
212
|
+
if (truncated) return;
|
|
213
|
+
buffer += chunk;
|
|
214
|
+
let nl = buffer.indexOf("\n");
|
|
215
|
+
while (nl >= 0) {
|
|
216
|
+
const line = buffer.slice(0, nl);
|
|
217
|
+
buffer = buffer.slice(nl + 1);
|
|
218
|
+
if (line.length !== 0) {
|
|
219
|
+
const parsed = parseCallerLine(line, symbol, opts, repo.name);
|
|
220
|
+
if (parsed) callers.push(parsed);
|
|
221
|
+
if (callers.length >= limit) {
|
|
222
|
+
truncated = true;
|
|
223
|
+
proc.kill("SIGTERM");
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
nl = buffer.indexOf("\n");
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
proc.stderr.setEncoding("utf8");
|
|
231
|
+
proc.stderr.on("data", (chunk: string) => {
|
|
232
|
+
stderr += chunk;
|
|
233
|
+
});
|
|
234
|
+
proc.on("error", reject);
|
|
235
|
+
proc.on("close", (code) => {
|
|
236
|
+
if (buffer.length > 0 && !truncated) {
|
|
237
|
+
const parsed = parseCallerLine(buffer, symbol, opts, repo.name);
|
|
238
|
+
if (parsed) callers.push(parsed);
|
|
239
|
+
}
|
|
240
|
+
if (code !== null && code !== 0 && code !== 1 && !truncated) {
|
|
241
|
+
reject(new Error(`grep exited ${code}: ${stderr.trim() || "(no stderr)"}`));
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
resolveP({ callers, truncated });
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function parseCallerLine(
|
|
250
|
+
line: string,
|
|
251
|
+
symbol: string,
|
|
252
|
+
opts: CallersOpts,
|
|
253
|
+
repoName: string,
|
|
254
|
+
): Caller | null {
|
|
255
|
+
const firstColon = line.indexOf(":");
|
|
256
|
+
if (firstColon < 0) return null;
|
|
257
|
+
const after = line.slice(firstColon + 1);
|
|
258
|
+
const secondColon = after.indexOf(":");
|
|
259
|
+
if (secondColon < 0) return null;
|
|
260
|
+
const lineNum = Number.parseInt(after.slice(0, secondColon), 10);
|
|
261
|
+
if (!Number.isFinite(lineNum)) return null;
|
|
262
|
+
|
|
263
|
+
const file = line.slice(0, firstColon).replace(/^\.\//, "");
|
|
264
|
+
const text = after.slice(secondColon + 1);
|
|
265
|
+
|
|
266
|
+
if (!opts.includeComments) {
|
|
267
|
+
if (/^\s*(\/\/|#|--)/.test(text)) return null;
|
|
268
|
+
}
|
|
269
|
+
if (/^\s*\*[\s/]/.test(text)) return null; // /* multi-line comment continuation
|
|
270
|
+
|
|
271
|
+
const kind = classifyMatch(text, symbol);
|
|
272
|
+
if (kind === "decl" && !opts.includeDecl) return null;
|
|
273
|
+
|
|
274
|
+
return { repo: repoName, file, line: lineNum, kind, text };
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function classifyMatch(text: string, symbol: string): Caller["kind"] {
|
|
278
|
+
const sym = escapeRegex(symbol);
|
|
279
|
+
|
|
280
|
+
if (
|
|
281
|
+
new RegExp(
|
|
282
|
+
`^\\s*(export\\s+)?(async\\s+)?(function|class|interface|type|enum|namespace)\\s+${sym}\\b`,
|
|
283
|
+
).test(text)
|
|
284
|
+
) {
|
|
285
|
+
return "decl";
|
|
286
|
+
}
|
|
287
|
+
if (new RegExp(`^\\s*(export\\s+)?(const|let|var)\\s+${sym}\\b`).test(text)) return "decl";
|
|
288
|
+
if (
|
|
289
|
+
new RegExp(`^\\s*(public|private|protected)\\s+(static\\s+)?function\\s+${sym}\\b`).test(text)
|
|
290
|
+
) {
|
|
291
|
+
return "decl";
|
|
292
|
+
}
|
|
293
|
+
if (new RegExp(`^\\s*(async\\s+)?def\\s+${sym}\\b`).test(text)) return "decl";
|
|
294
|
+
if (new RegExp(`^\\s*class\\s+${sym}\\b`).test(text)) return "decl";
|
|
295
|
+
|
|
296
|
+
if (/^\s*(import\b|from\b|use\s+|require\s*\(|include\s|require_once\s)/.test(text)) {
|
|
297
|
+
return "import";
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (new RegExp(`\\b${sym}\\s*\\(`).test(text)) return "call";
|
|
301
|
+
|
|
302
|
+
if (
|
|
303
|
+
new RegExp(
|
|
304
|
+
`(:\\s*${sym}\\b|extends\\s+${sym}\\b|implements\\s+${sym}\\b|as\\s+${sym}\\b|<${sym}[>\\s,])`,
|
|
305
|
+
).test(text)
|
|
306
|
+
) {
|
|
307
|
+
return "type";
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return "ref";
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function escapeRegex(s: string): string {
|
|
314
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function renderCallers(r: CallersResult): string {
|
|
318
|
+
const lines: string[] = [];
|
|
319
|
+
|
|
320
|
+
if (r.total === 0) {
|
|
321
|
+
lines.push(`callers · ${r.symbol}: no matches`);
|
|
322
|
+
return lines.join("\n");
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const summary = Object.entries(r.by_kind)
|
|
326
|
+
.sort((a, b) => b[1] - a[1])
|
|
327
|
+
.map(([k, n]) => `${k}=${n}`)
|
|
328
|
+
.join(", ");
|
|
329
|
+
lines.push(`callers · ${r.symbol} (${r.total} total: ${summary}, ${r.elapsed_ms}ms)`);
|
|
330
|
+
|
|
331
|
+
const byRepo = new Map<string, Caller[]>();
|
|
332
|
+
for (const c of r.callers) {
|
|
333
|
+
if (!byRepo.has(c.repo)) byRepo.set(c.repo, []);
|
|
334
|
+
byRepo.get(c.repo)!.push(c);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const showRepoHeader = byRepo.size > 1;
|
|
338
|
+
|
|
339
|
+
for (const [repo, callers] of byRepo) {
|
|
340
|
+
if (showRepoHeader) {
|
|
341
|
+
lines.push("");
|
|
342
|
+
lines.push(`── ${repo} (${callers.length}) ──`);
|
|
343
|
+
}
|
|
344
|
+
let lastFile = "";
|
|
345
|
+
for (const c of callers) {
|
|
346
|
+
if (c.file !== lastFile) {
|
|
347
|
+
lines.push("");
|
|
348
|
+
lines.push(` ${c.file}`);
|
|
349
|
+
lastFile = c.file;
|
|
350
|
+
}
|
|
351
|
+
const kindLabel = `[${c.kind}]`.padEnd(8);
|
|
352
|
+
const snippet = c.text.trim().slice(0, 100);
|
|
353
|
+
lines.push(` L${c.line.toString().padEnd(5)} ${kindLabel} ${snippet}`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (r.truncated) {
|
|
358
|
+
lines.push("");
|
|
359
|
+
lines.push("(truncated; pass --limit to widen)");
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return lines.join("\n");
|
|
363
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import type { Command } from "commander";
|
|
4
|
+
import type { EmitContext, HarneryProgramContext } from "../commander.ts";
|
|
5
|
+
import {
|
|
6
|
+
type CompletionContextLookup,
|
|
7
|
+
generateBash,
|
|
8
|
+
generateFish,
|
|
9
|
+
generateZsh,
|
|
10
|
+
walkProgram,
|
|
11
|
+
} from "../lib/completion/index.ts";
|
|
12
|
+
|
|
13
|
+
const noopProviderRunner = async (): Promise<string[]> => [];
|
|
14
|
+
const noopLookup: CompletionContextLookup = () => undefined;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* `harn completion`: emit / install shell completion scripts.
|
|
18
|
+
*
|
|
19
|
+
* harn completion bash | zsh | fish → write generated script to stdout
|
|
20
|
+
* harn completion install [--shell ...] → install to the conventional location
|
|
21
|
+
*
|
|
22
|
+
* And the hidden internal entry point:
|
|
23
|
+
*
|
|
24
|
+
* harn __complete <provider> -- <partial>
|
|
25
|
+
* Called by the generated shell scripts at tab-time for dynamic-value
|
|
26
|
+
* completion. Returns newline-separated candidate values. Empty exit
|
|
27
|
+
* code 0 even on errors (don't disrupt the user's tab).
|
|
28
|
+
*/
|
|
29
|
+
export function registerCompletionCommand(
|
|
30
|
+
program: Command,
|
|
31
|
+
_emit: EmitContext,
|
|
32
|
+
context?: HarneryProgramContext,
|
|
33
|
+
): void {
|
|
34
|
+
const lookup = context?.completionLookup ?? noopLookup;
|
|
35
|
+
const runProvider = context?.completionRunner ?? noopProviderRunner;
|
|
36
|
+
const root = program
|
|
37
|
+
.command("completion")
|
|
38
|
+
.description(
|
|
39
|
+
"Shell tab-completion. Emit a script per shell or install to the standard location.",
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
root
|
|
43
|
+
.command("bash")
|
|
44
|
+
.description("Emit bash completion script to stdout")
|
|
45
|
+
.action(() => {
|
|
46
|
+
const out = generateBash(walkProgram(program, lookup), program.name());
|
|
47
|
+
process.stdout.write(out); // raw bytes: shell completion scripts must be unframed (consumer evals stdout).
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
root
|
|
51
|
+
.command("zsh")
|
|
52
|
+
.description("Emit zsh completion script to stdout")
|
|
53
|
+
.action(() => {
|
|
54
|
+
const out = generateZsh(walkProgram(program, lookup), program.name());
|
|
55
|
+
process.stdout.write(out); // raw bytes: shell completion scripts must be unframed (consumer evals stdout).
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
root
|
|
59
|
+
.command("fish")
|
|
60
|
+
.description("Emit fish completion script to stdout")
|
|
61
|
+
.action(() => {
|
|
62
|
+
const out = generateFish(walkProgram(program, lookup), program.name());
|
|
63
|
+
process.stdout.write(out); // raw bytes: shell completion scripts must be unframed (consumer evals stdout).
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
root
|
|
67
|
+
.command("install")
|
|
68
|
+
.description("Write completion script to the conventional location for the chosen shell")
|
|
69
|
+
.option("--shell <name>", "bash | zsh | fish (default: auto-detect from $SHELL)")
|
|
70
|
+
.option("--path <file>", "Override destination path")
|
|
71
|
+
.option("--print-path", "Print the destination path and exit (no write)")
|
|
72
|
+
.action(async (opts: InstallOpts) => {
|
|
73
|
+
await installCompletion(program, opts, lookup);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Hidden internal entry: shells call this at tab-time. `hidden: true` so
|
|
77
|
+
// `harn --help` doesn't surface it.
|
|
78
|
+
const hidden = program
|
|
79
|
+
.command("__complete <provider> [partial]", { hidden: true })
|
|
80
|
+
.description("Internal: dynamic-value completion callback for the shell")
|
|
81
|
+
.allowExcessArguments(true)
|
|
82
|
+
.action(async (provider: string, partial: string | undefined) => {
|
|
83
|
+
try {
|
|
84
|
+
const values = await runProvider(provider, partial ?? "");
|
|
85
|
+
for (const v of values) {
|
|
86
|
+
process.stdout.write(`${v}\n`); // lint-ok-emission: shell callback; newline-delimited raw values is the contract with compgen/_describe/fish.
|
|
87
|
+
}
|
|
88
|
+
} catch {
|
|
89
|
+
// Swallow errors silently: failure during tab completion should not
|
|
90
|
+
// break the user's shell.
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
// Keep TS happy that the variable is used.
|
|
94
|
+
void hidden;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
interface InstallOpts {
|
|
98
|
+
shell?: string;
|
|
99
|
+
path?: string;
|
|
100
|
+
printPath?: boolean;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function installCompletion(
|
|
104
|
+
program: Command,
|
|
105
|
+
opts: InstallOpts,
|
|
106
|
+
lookup: CompletionContextLookup,
|
|
107
|
+
): Promise<void> {
|
|
108
|
+
const shell = opts.shell ?? detectShell();
|
|
109
|
+
if (!shell) {
|
|
110
|
+
process.stderr.write("Could not auto-detect shell. Pass --shell bash|zsh|fish explicitly.\n"); // lint-ok-emission: install error; stderr keeps stdout clean for --print-path piping.
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const destination = opts.path ?? defaultInstallPath(shell, program.name());
|
|
115
|
+
if (opts.printPath) {
|
|
116
|
+
process.stdout.write(`${destination}\n`); // lint-ok-emission: --print-path is meant to be piped (e.g., dest=$(harn completion install --print-path)).
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let content: string;
|
|
121
|
+
switch (shell) {
|
|
122
|
+
case "bash":
|
|
123
|
+
content = generateBash(walkProgram(program, lookup), program.name());
|
|
124
|
+
break;
|
|
125
|
+
case "zsh":
|
|
126
|
+
content = generateZsh(walkProgram(program, lookup), program.name());
|
|
127
|
+
break;
|
|
128
|
+
case "fish":
|
|
129
|
+
content = generateFish(walkProgram(program, lookup), program.name());
|
|
130
|
+
break;
|
|
131
|
+
default:
|
|
132
|
+
process.stderr.write(`Unknown shell: ${shell}\n`); // lint-ok-emission: install-time error, see above.
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
mkdirSync(dirname(destination), { recursive: true });
|
|
137
|
+
writeFileSync(destination, content);
|
|
138
|
+
process.stderr.write(`Installed ${shell} completion to ${destination}\n`); // lint-ok-emission: install progress; stderr keeps stdout clean.
|
|
139
|
+
|
|
140
|
+
// Hint how to activate.
|
|
141
|
+
printActivationHint(shell, destination);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function detectShell(): string | null {
|
|
145
|
+
const shellPath = process.env.SHELL ?? "";
|
|
146
|
+
if (shellPath.endsWith("/bash")) return "bash";
|
|
147
|
+
if (shellPath.endsWith("/zsh")) return "zsh";
|
|
148
|
+
if (shellPath.endsWith("/fish")) return "fish";
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function defaultInstallPath(shell: string, binName: string): string {
|
|
153
|
+
const home = process.env.HOME ?? "";
|
|
154
|
+
switch (shell) {
|
|
155
|
+
case "bash": {
|
|
156
|
+
// bash-completion v2 user-level location.
|
|
157
|
+
const xdg = process.env.XDG_DATA_HOME ?? resolve(home, ".local/share");
|
|
158
|
+
return resolve(xdg, `bash-completion/completions/${binName}`);
|
|
159
|
+
}
|
|
160
|
+
case "zsh": {
|
|
161
|
+
// Common $fpath user location. zsh auto-loads from ~/.zsh/completions if
|
|
162
|
+
// it's in $fpath.
|
|
163
|
+
return resolve(home, `.zsh/completions/_${binName}`);
|
|
164
|
+
}
|
|
165
|
+
case "fish": {
|
|
166
|
+
// Fish auto-loads everything in ~/.config/fish/completions/.
|
|
167
|
+
return resolve(home, `.config/fish/completions/${binName}.fish`);
|
|
168
|
+
}
|
|
169
|
+
default:
|
|
170
|
+
return resolve(home, `.${binName}-completion.${shell}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function printActivationHint(shell: string, path: string): void {
|
|
175
|
+
switch (shell) {
|
|
176
|
+
case "bash":
|
|
177
|
+
if (existsSync(resolve(process.env.HOME ?? "", ".bashrc"))) {
|
|
178
|
+
const bashHint = `\nTo activate now in this shell: source "${path}"\nNew shells will pick it up automatically if bash-completion is enabled.\nIf completions don't fire in new shells, add this to ~/.bashrc:\n [ -f "${path}" ] && source "${path}"\n`;
|
|
179
|
+
process.stderr.write(bashHint); // lint-ok-emission: post-install activation hint; stderr keeps stdout clean.
|
|
180
|
+
}
|
|
181
|
+
break;
|
|
182
|
+
case "zsh": {
|
|
183
|
+
const zshHint = `\nTo activate now: autoload -U compinit && compinit\nMake sure ${resolve(path, "..")} is in your $fpath. Add to ~/.zshrc:\n fpath=(${resolve(path, "..")} $fpath)\n autoload -U compinit && compinit\n`;
|
|
184
|
+
process.stderr.write(zshHint); // lint-ok-emission: post-install activation hint; stderr keeps stdout clean.
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
case "fish": {
|
|
188
|
+
const fishHint = `\nFish auto-loads completions from ${resolve(path, "..")}. New shells pick it up.\nTo activate now: source "${path}"\n`;
|
|
189
|
+
process.stderr.write(fishHint); // lint-ok-emission: post-install activation hint; stderr keeps stdout clean.
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|