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,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bash completion generator. Walks a CommandSpec tree and emits a single
|
|
3
|
+
* self-contained bash function `_<bin>` plus the `complete -F _<bin> <bin>`
|
|
4
|
+
* directive that registers it. The bin name is injected by the caller so the
|
|
5
|
+
* same generator serves any host CLI.
|
|
6
|
+
*
|
|
7
|
+
* Generated script architecture:
|
|
8
|
+
* 1. `_<bin>_path()`: walks COMP_WORDS forward, skipping options and option
|
|
9
|
+
* values, returns the current command-path string (e.g., "anthropic cost").
|
|
10
|
+
* 2. `_<bin>_options()` / `_<bin>_subcommands()` / `_<bin>_option_takes_value()` /
|
|
11
|
+
* `_<bin>_option_choices()`: table-lookup functions, generated as big
|
|
12
|
+
* case statements from the spec tree.
|
|
13
|
+
* 3. `_<bin>()`: main entry. Sets COMPREPLY based on whether the cursor is
|
|
14
|
+
* on a subcommand name, option name (--<TAB>), or value.
|
|
15
|
+
* 4. Dynamic value paths shell out to `<bin> __complete <provider> -- "$cur"`.
|
|
16
|
+
*
|
|
17
|
+
* Important escaping rules:
|
|
18
|
+
* - Single-quoted strings in bash don't honor escapes. We replace `'` in
|
|
19
|
+
* descriptions/choices with `'\''` (the standard bash single-quote escape).
|
|
20
|
+
* - We avoid descriptions in bash output because bash COMPREPLY can't render
|
|
21
|
+
* them; descriptions are a zsh/fish feature.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import type { CommandSpec, OptionSpec, PositionalSpec } from "./walk.js";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Derive a valid shell function-name prefix from the bin name (e.g. `my-cli`
|
|
28
|
+
* → `_my_cli`). Non-identifier characters collapse to `_`.
|
|
29
|
+
*/
|
|
30
|
+
function fnPrefix(binName: string): string {
|
|
31
|
+
return `_${binName.replace(/[^a-zA-Z0-9_]/g, "_")}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Encode a positional/option value source as a single string the bash driver
|
|
36
|
+
* can parse: `DYN:<provider>` for dynamic, `ENUM:val1 val2 val3` for static
|
|
37
|
+
* choices, or empty when there's nothing to suggest.
|
|
38
|
+
*/
|
|
39
|
+
function valueSpec(opt: { dynamicProvider?: string; valueChoices?: string[] }): string {
|
|
40
|
+
if (opt.dynamicProvider) return `DYN:${opt.dynamicProvider}`;
|
|
41
|
+
if (opt.valueChoices && opt.valueChoices.length > 0) {
|
|
42
|
+
return `ENUM:${opt.valueChoices.join(" ")}`;
|
|
43
|
+
}
|
|
44
|
+
return "";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function generateBash(root: CommandSpec, binName: string): string {
|
|
48
|
+
const fn = fnPrefix(binName);
|
|
49
|
+
// Build lookup tables keyed by command path. Path "" represents the root.
|
|
50
|
+
const subcommandsByPath = new Map<string, string[]>();
|
|
51
|
+
const optionsByPath = new Map<string, OptionSpec[]>();
|
|
52
|
+
const positionalsByPath = new Map<string, PositionalSpec[]>();
|
|
53
|
+
|
|
54
|
+
const walk = (s: CommandSpec): void => {
|
|
55
|
+
subcommandsByPath.set(s.path, s.subcommands.map((c) => c.name).sort());
|
|
56
|
+
optionsByPath.set(s.path, s.options);
|
|
57
|
+
positionalsByPath.set(s.path, s.positionals);
|
|
58
|
+
for (const sub of s.subcommands) walk(sub);
|
|
59
|
+
};
|
|
60
|
+
walk(root);
|
|
61
|
+
|
|
62
|
+
const out: string[] = [];
|
|
63
|
+
out.push(
|
|
64
|
+
`# ${binName} bash completion, generated by \`${binName} completion bash\`. Do not edit.`,
|
|
65
|
+
);
|
|
66
|
+
out.push(`# Source via: eval "$(${binName} completion bash)"`);
|
|
67
|
+
out.push("");
|
|
68
|
+
out.push(`${fn}_subcommands() {`);
|
|
69
|
+
out.push(` case "$1" in`);
|
|
70
|
+
for (const [path, names] of [...subcommandsByPath.entries()].sort()) {
|
|
71
|
+
if (names.length === 0) continue;
|
|
72
|
+
out.push(` ${bashCase(path)}) echo '${names.join(" ")}' ;;`);
|
|
73
|
+
}
|
|
74
|
+
out.push(" esac");
|
|
75
|
+
out.push("}");
|
|
76
|
+
out.push("");
|
|
77
|
+
out.push(`${fn}_options() {`);
|
|
78
|
+
out.push(` case "$1" in`);
|
|
79
|
+
for (const [path, opts] of [...optionsByPath.entries()].sort()) {
|
|
80
|
+
if (opts.length === 0) continue;
|
|
81
|
+
const flags = opts.flatMap((o) => [o.long, o.short]).filter((f) => f.length > 0);
|
|
82
|
+
out.push(` ${bashCase(path)}) echo '${flags.join(" ")}' ;;`);
|
|
83
|
+
}
|
|
84
|
+
out.push(" esac");
|
|
85
|
+
out.push("}");
|
|
86
|
+
out.push("");
|
|
87
|
+
out.push(`${fn}_option_takes_value() {`);
|
|
88
|
+
out.push(
|
|
89
|
+
` # echo "1" if option takes a value, empty otherwise. $1 = command path, $2 = option flag.`,
|
|
90
|
+
);
|
|
91
|
+
out.push(` case "$1|$2" in`);
|
|
92
|
+
for (const [path, opts] of [...optionsByPath.entries()].sort()) {
|
|
93
|
+
for (const o of opts) {
|
|
94
|
+
if (!o.takesValue) continue;
|
|
95
|
+
for (const flag of [o.long, o.short].filter((f) => f.length > 0)) {
|
|
96
|
+
out.push(` ${bashCase(path)}\\|${bashEscape(flag)}) echo 1 ;;`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
out.push(" esac");
|
|
101
|
+
out.push("}");
|
|
102
|
+
out.push("");
|
|
103
|
+
out.push(`${fn}_option_completion() {`);
|
|
104
|
+
out.push(` # Echo "DYN:<provider>" or "ENUM:val1 val2 ...". Empty if no spec.`);
|
|
105
|
+
out.push(` case "$1|$2" in`);
|
|
106
|
+
for (const [path, opts] of [...optionsByPath.entries()].sort()) {
|
|
107
|
+
for (const o of opts) {
|
|
108
|
+
if (!o.takesValue) continue;
|
|
109
|
+
const spec = valueSpec(o);
|
|
110
|
+
if (!spec) continue;
|
|
111
|
+
for (const flag of [o.long, o.short].filter((f) => f.length > 0)) {
|
|
112
|
+
out.push(` ${bashCase(path)}\\|${bashEscape(flag)}) echo '${bashEscape(spec)}' ;;`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
out.push(" esac");
|
|
117
|
+
out.push("}");
|
|
118
|
+
out.push("");
|
|
119
|
+
out.push(`${fn}_positional_completion() {`);
|
|
120
|
+
out.push(
|
|
121
|
+
` # Echo "DYN:<provider>" or "ENUM:val1 val2 ...". $1 = command path, $2 = positional index.`,
|
|
122
|
+
);
|
|
123
|
+
out.push(` case "$1|$2" in`);
|
|
124
|
+
for (const [path, positionals] of [...positionalsByPath.entries()].sort()) {
|
|
125
|
+
positionals.forEach((p, i) => {
|
|
126
|
+
const spec = valueSpec(p);
|
|
127
|
+
if (!spec) return;
|
|
128
|
+
out.push(` ${bashCase(path)}\\|${i}) echo '${bashEscape(spec)}' ;;`);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
out.push(" esac");
|
|
132
|
+
out.push("}");
|
|
133
|
+
out.push("");
|
|
134
|
+
out.push(bashDriver(fn, binName));
|
|
135
|
+
out.push("");
|
|
136
|
+
out.push(`complete -F ${fn} ${binName}`);
|
|
137
|
+
return `${out.join("\n")}\n`;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Converts a command path to a bash `case` pattern. Empty path → "ROOT".
|
|
142
|
+
* Replace spaces with `__` since bash case patterns don't tolerate spaces well
|
|
143
|
+
* inside an unquoted pattern. The driver substitutes the same way.
|
|
144
|
+
*/
|
|
145
|
+
function bashCase(path: string): string {
|
|
146
|
+
if (path === "") return "ROOT";
|
|
147
|
+
return path.replace(/ /g, "__");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Escape a string for safe embedding in a single-quoted bash string. Inside
|
|
152
|
+
* single quotes the only character that needs escaping is the single quote
|
|
153
|
+
* itself, which we close, escape, and reopen: `'\''`.
|
|
154
|
+
*/
|
|
155
|
+
function bashEscape(s: string): string {
|
|
156
|
+
return s.replace(/'/g, `'\\''`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* The driver: the parts of the completion that don't depend on the command
|
|
161
|
+
* tree. Walks COMP_WORDS to determine the current command path, then dispatches
|
|
162
|
+
* to subcommand / option / value completion. `fn` is the function-name prefix
|
|
163
|
+
* and `binName` is the CLI name used for the `__complete` callback.
|
|
164
|
+
*/
|
|
165
|
+
function bashDriver(fn: string, binName: string): string {
|
|
166
|
+
return `${fn}() {
|
|
167
|
+
local cur prev words cword
|
|
168
|
+
COMPREPLY=()
|
|
169
|
+
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
170
|
+
prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
171
|
+
|
|
172
|
+
# Walk forward through words, skipping options and option-values, to
|
|
173
|
+
# determine which command path we're currently inside. Stop when we hit
|
|
174
|
+
# COMP_CWORD (the position the cursor is on).
|
|
175
|
+
local -a path_parts=()
|
|
176
|
+
local i=1
|
|
177
|
+
local path_str="ROOT"
|
|
178
|
+
while [ $i -lt $COMP_CWORD ]; do
|
|
179
|
+
local w="\${COMP_WORDS[$i]}"
|
|
180
|
+
case "$w" in
|
|
181
|
+
-*)
|
|
182
|
+
# Option. If it takes a value, skip the next word too.
|
|
183
|
+
if [ "$(${fn}_option_takes_value "$path_str" "$w")" = "1" ]; then
|
|
184
|
+
i=$((i+1))
|
|
185
|
+
fi
|
|
186
|
+
;;
|
|
187
|
+
*)
|
|
188
|
+
path_parts+=("$w")
|
|
189
|
+
local joined
|
|
190
|
+
joined=$(printf '%s__' "\${path_parts[@]}")
|
|
191
|
+
joined="\${joined%__}"
|
|
192
|
+
# If this word is a known subcommand at our current path, descend.
|
|
193
|
+
local known
|
|
194
|
+
known=$(${fn}_subcommands "$path_str")
|
|
195
|
+
if [[ " $known " == *" $w "* ]]; then
|
|
196
|
+
path_str="$joined"
|
|
197
|
+
fi
|
|
198
|
+
;;
|
|
199
|
+
esac
|
|
200
|
+
i=$((i+1))
|
|
201
|
+
done
|
|
202
|
+
|
|
203
|
+
# --- decide what we're completing ----------------------------------------
|
|
204
|
+
|
|
205
|
+
# Case 1: previous word is an option that takes a value → complete that value.
|
|
206
|
+
if [ -n "$prev" ] && [ "$(${fn}_option_takes_value "$path_str" "$prev")" = "1" ]; then
|
|
207
|
+
local spec
|
|
208
|
+
spec=$(${fn}_option_completion "$path_str" "$prev")
|
|
209
|
+
${fn}_emit_values "$spec" "$cur"
|
|
210
|
+
return
|
|
211
|
+
fi
|
|
212
|
+
|
|
213
|
+
# Case 2: current word starts with - → complete option name.
|
|
214
|
+
if [[ "$cur" == -* ]]; then
|
|
215
|
+
local opts
|
|
216
|
+
opts=$(${fn}_options "$path_str")
|
|
217
|
+
if [ -n "$opts" ]; then
|
|
218
|
+
COMPREPLY=( $(compgen -W "$opts" -- "$cur") )
|
|
219
|
+
fi
|
|
220
|
+
return
|
|
221
|
+
fi
|
|
222
|
+
|
|
223
|
+
# Case 3: complete subcommand name (and any positional values for this command).
|
|
224
|
+
local subs
|
|
225
|
+
subs=$(${fn}_subcommands "$path_str")
|
|
226
|
+
|
|
227
|
+
# How many positional args have we accumulated for the current path? Count
|
|
228
|
+
# path_parts entries beyond the part that matched a subcommand.
|
|
229
|
+
local pos_index=0
|
|
230
|
+
if [ -n "$subs" ]; then
|
|
231
|
+
# If subs are still available here, the cursor is on a subcommand slot.
|
|
232
|
+
COMPREPLY=( $(compgen -W "$subs" -- "$cur") )
|
|
233
|
+
return
|
|
234
|
+
fi
|
|
235
|
+
|
|
236
|
+
# No more subcommands, we're on a positional. Determine which positional
|
|
237
|
+
# index we're on by counting non-option words after the last matched subcommand.
|
|
238
|
+
local after_cmd=0
|
|
239
|
+
local j=1
|
|
240
|
+
local seen_path="ROOT"
|
|
241
|
+
while [ $j -lt $COMP_CWORD ]; do
|
|
242
|
+
local w="\${COMP_WORDS[$j]}"
|
|
243
|
+
case "$w" in
|
|
244
|
+
-*)
|
|
245
|
+
if [ "$(${fn}_option_takes_value "$seen_path" "$w")" = "1" ]; then
|
|
246
|
+
j=$((j+1))
|
|
247
|
+
fi
|
|
248
|
+
;;
|
|
249
|
+
*)
|
|
250
|
+
local joined
|
|
251
|
+
if [ "$seen_path" = "ROOT" ]; then joined="$w"; else joined="\${seen_path}__\${w}"; fi
|
|
252
|
+
local known
|
|
253
|
+
known=$(${fn}_subcommands "$seen_path")
|
|
254
|
+
if [[ " $known " == *" $w "* ]]; then
|
|
255
|
+
seen_path="$joined"
|
|
256
|
+
else
|
|
257
|
+
after_cmd=$((after_cmd+1))
|
|
258
|
+
fi
|
|
259
|
+
;;
|
|
260
|
+
esac
|
|
261
|
+
j=$((j+1))
|
|
262
|
+
done
|
|
263
|
+
pos_index=$after_cmd
|
|
264
|
+
|
|
265
|
+
local pos_spec
|
|
266
|
+
pos_spec=$(${fn}_positional_completion "$path_str" "$pos_index")
|
|
267
|
+
if [ -n "$pos_spec" ]; then
|
|
268
|
+
${fn}_emit_values "$pos_spec" "$cur"
|
|
269
|
+
return
|
|
270
|
+
fi
|
|
271
|
+
|
|
272
|
+
# Fallback: file completion.
|
|
273
|
+
COMPREPLY=( $(compgen -f -- "$cur") )
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
${fn}_emit_values() {
|
|
277
|
+
local spec="$1"
|
|
278
|
+
local cur="$2"
|
|
279
|
+
if [[ "$spec" == DYN:* ]]; then
|
|
280
|
+
local provider="\${spec#DYN:}"
|
|
281
|
+
local values
|
|
282
|
+
values=$(${binName} __complete "$provider" -- "$cur" 2>/dev/null)
|
|
283
|
+
if [ -n "$values" ]; then
|
|
284
|
+
COMPREPLY=( $(compgen -W "$values" -- "$cur") )
|
|
285
|
+
fi
|
|
286
|
+
elif [[ "$spec" == ENUM:* ]]; then
|
|
287
|
+
local choices="\${spec#ENUM:}"
|
|
288
|
+
COMPREPLY=( $(compgen -W "$choices" -- "$cur") )
|
|
289
|
+
fi
|
|
290
|
+
}`;
|
|
291
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fish completion generator. Fish has the cleanest completion grammar of the
|
|
3
|
+
* three shells: one `complete` line per option per command. No driver function
|
|
4
|
+
* needed; fish's built-in `__fish_seen_subcommand_from` and friends do the
|
|
5
|
+
* subcommand parsing for us. The bin name is injected by the caller.
|
|
6
|
+
*
|
|
7
|
+
* Layout per command path:
|
|
8
|
+
* complete -c <bin> -n '<condition>' -a <subcommand> -d '<description>'
|
|
9
|
+
* complete -c <bin> -n '<condition>' -l <long> [-s <short>] -d '<description>' [-r] [-x] [-a '<choices>']
|
|
10
|
+
*
|
|
11
|
+
* Dynamic values: fish runs the command in `-a '(...)'` at completion time,
|
|
12
|
+
* giving us per-tab callbacks for free.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { CommandSpec } from "./walk.js";
|
|
16
|
+
|
|
17
|
+
function fishEscape(s: string): string {
|
|
18
|
+
// Inside single-quoted fish strings, escape \\ and '.
|
|
19
|
+
return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function truncate(s: string, n: number): string {
|
|
23
|
+
if (!s) return "";
|
|
24
|
+
return s.length > n ? `${s.slice(0, n - 1)}…` : s;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Returns the fish condition expression for "we are inside the command path".
|
|
29
|
+
* Path "" (root) means "no subcommand seen yet".
|
|
30
|
+
*/
|
|
31
|
+
function pathCondition(path: string): string {
|
|
32
|
+
if (path === "") {
|
|
33
|
+
return "__fish_use_subcommand";
|
|
34
|
+
}
|
|
35
|
+
const parts = path.split(" ");
|
|
36
|
+
// Build: __fish_seen_subcommand_from a; and __fish_seen_subcommand_from b; ...
|
|
37
|
+
// Fish's __fish_seen_subcommand_from accepts a list of allowed words at a
|
|
38
|
+
// given depth. For a 2-deep path "anthropic cost", the condition is:
|
|
39
|
+
// __fish_seen_subcommand_from anthropic; and __fish_seen_subcommand_from cost
|
|
40
|
+
const clauses = parts.map((p) => `__fish_seen_subcommand_from ${p}`);
|
|
41
|
+
return clauses.join("; and ");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function valueAction(
|
|
45
|
+
opt: { dynamicProvider?: string; valueChoices?: string[] },
|
|
46
|
+
binName: string,
|
|
47
|
+
): {
|
|
48
|
+
flag: string;
|
|
49
|
+
arg: string;
|
|
50
|
+
} {
|
|
51
|
+
// Fish flag semantics:
|
|
52
|
+
// -r/--require-parameter : the flag requires an argument
|
|
53
|
+
// -x/--exclusive : flag requires an argument AND no further completion after
|
|
54
|
+
// -a '<arg>' : completions for the argument
|
|
55
|
+
if (opt.dynamicProvider) {
|
|
56
|
+
return {
|
|
57
|
+
flag: "-r",
|
|
58
|
+
arg: `-a '(${binName} __complete ${opt.dynamicProvider} -- (commandline -ct))'`,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (opt.valueChoices && opt.valueChoices.length > 0) {
|
|
62
|
+
return {
|
|
63
|
+
flag: "-r",
|
|
64
|
+
arg: `-a '${opt.valueChoices.map(fishEscape).join(" ")}'`,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return { flag: "-r", arg: "" };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function generateFish(root: CommandSpec, binName: string): string {
|
|
71
|
+
const out: string[] = [];
|
|
72
|
+
out.push(
|
|
73
|
+
`# ${binName} fish completion, generated by \`${binName} completion fish\`. Do not edit.`,
|
|
74
|
+
);
|
|
75
|
+
out.push(`# Source via: ${binName} completion fish | source`);
|
|
76
|
+
out.push(
|
|
77
|
+
`# Or install: ${binName} completion fish > ~/.config/fish/completions/${binName}.fish`,
|
|
78
|
+
);
|
|
79
|
+
out.push("");
|
|
80
|
+
|
|
81
|
+
const walk = (s: CommandSpec): void => {
|
|
82
|
+
const cond = pathCondition(s.path);
|
|
83
|
+
// Subcommands at this depth
|
|
84
|
+
for (const sub of s.subcommands) {
|
|
85
|
+
const desc = truncate(sub.description, 80);
|
|
86
|
+
out.push(
|
|
87
|
+
`complete -c ${binName} -f -n '${cond}' -a '${fishEscape(sub.name)}' -d '${fishEscape(desc)}'`,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
// Options for this command
|
|
91
|
+
for (const o of s.options) {
|
|
92
|
+
const long = o.long ? o.long.replace(/^--/, "") : "";
|
|
93
|
+
const short = o.short ? o.short.replace(/^-/, "") : "";
|
|
94
|
+
const desc = truncate(o.description, 80);
|
|
95
|
+
let line = `complete -c ${binName} -n '${cond}'`;
|
|
96
|
+
if (long) line += ` -l ${long}`;
|
|
97
|
+
if (short) line += ` -s ${short}`;
|
|
98
|
+
if (o.takesValue) {
|
|
99
|
+
const v = valueAction(o, binName);
|
|
100
|
+
line += ` ${v.flag}`;
|
|
101
|
+
if (v.arg) line += ` ${v.arg}`;
|
|
102
|
+
else line += " -f";
|
|
103
|
+
} else {
|
|
104
|
+
line += " -f";
|
|
105
|
+
}
|
|
106
|
+
line += ` -d '${fishEscape(desc)}'`;
|
|
107
|
+
out.push(line);
|
|
108
|
+
}
|
|
109
|
+
// Positional values: fish doesn't natively handle Nth positional, but we
|
|
110
|
+
// can suggest values when no subcommand follows. The condition becomes:
|
|
111
|
+
// "in this path AND not seen another subcommand from this path's subs".
|
|
112
|
+
if (s.positionals.length > 0) {
|
|
113
|
+
const subNames = s.subcommands.map((c) => c.name).join(" ");
|
|
114
|
+
const positionalCond =
|
|
115
|
+
s.subcommands.length > 0
|
|
116
|
+
? `${cond}; and not __fish_seen_subcommand_from ${subNames}`
|
|
117
|
+
: cond;
|
|
118
|
+
// We only render the first positional; fish has limited support for
|
|
119
|
+
// distinguishing positional indices in this completion grammar.
|
|
120
|
+
const p = s.positionals[0];
|
|
121
|
+
if (p) {
|
|
122
|
+
const v = valueAction(p, binName);
|
|
123
|
+
if (v.arg) {
|
|
124
|
+
out.push(`complete -c ${binName} -f -n '${positionalCond}' ${v.arg}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
for (const sub of s.subcommands) walk(sub);
|
|
130
|
+
};
|
|
131
|
+
walk(root);
|
|
132
|
+
|
|
133
|
+
return `${out.join("\n")}\n`;
|
|
134
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { generateBash } from "./bash.js";
|
|
2
|
+
export { generateFish } from "./fish.js";
|
|
3
|
+
export {
|
|
4
|
+
type CommandSpec,
|
|
5
|
+
type CompletionContextLookup,
|
|
6
|
+
type OptionSpec,
|
|
7
|
+
type PositionalSpec,
|
|
8
|
+
walkProgram,
|
|
9
|
+
} from "./walk.js";
|
|
10
|
+
export { generateZsh } from "./zsh.js";
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Walks the Commander program tree and produces a normalized CommandSpec tree
|
|
3
|
+
* that the bash/zsh/fish completion generators consume. Decoupled from
|
|
4
|
+
* Commander internals so the generators don't have to know about Option/Argument
|
|
5
|
+
* classes, just the spec types defined here.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Argument, Command, Option } from "commander";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Caller-injected lookup that maps a (commandPath, option-or-positional)
|
|
12
|
+
* pair to a dynamic-provider key. The shell completion script later
|
|
13
|
+
* invokes `<bin> __complete <key>` at tab-time and the consumer's
|
|
14
|
+
* provider registry produces the actual values. harn standalone uses
|
|
15
|
+
* the default no-op lookup (no dynamic completion).
|
|
16
|
+
*/
|
|
17
|
+
export type CompletionContextLookup = (key: {
|
|
18
|
+
commandPath: string;
|
|
19
|
+
option?: string;
|
|
20
|
+
positional?: number;
|
|
21
|
+
}) => string | undefined;
|
|
22
|
+
|
|
23
|
+
const noopLookup: CompletionContextLookup = () => undefined;
|
|
24
|
+
|
|
25
|
+
export interface CommandSpec {
|
|
26
|
+
/** Bare command name (e.g., "anthropic", "cost"). */
|
|
27
|
+
name: string;
|
|
28
|
+
/** Full path from root, space-separated (e.g., "anthropic cost"). Empty for the root program. */
|
|
29
|
+
path: string;
|
|
30
|
+
description: string;
|
|
31
|
+
subcommands: CommandSpec[];
|
|
32
|
+
options: OptionSpec[];
|
|
33
|
+
positionals: PositionalSpec[];
|
|
34
|
+
/** True if this command has the `--help` flag (default for all Commander commands). */
|
|
35
|
+
hasHelp: boolean;
|
|
36
|
+
/** True if this is a hidden command; completion can skip it. */
|
|
37
|
+
hidden: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface OptionSpec {
|
|
41
|
+
/** Long form including `--`, e.g., "--workspace". May be empty if only short form exists. */
|
|
42
|
+
long: string;
|
|
43
|
+
/** Short form including `-`, e.g., "-w". May be empty. */
|
|
44
|
+
short: string;
|
|
45
|
+
description: string;
|
|
46
|
+
/** True if the option requires a value (e.g., `--workspace <id>`). */
|
|
47
|
+
takesValue: boolean;
|
|
48
|
+
/** Optional static choice list for the value (e.g. `--format json|csv|table`). */
|
|
49
|
+
valueChoices?: string[];
|
|
50
|
+
/** Optional dynamic-value provider key; completion script calls back via `harn __complete <key>`. */
|
|
51
|
+
dynamicProvider?: string;
|
|
52
|
+
/** True if the option can be repeated. */
|
|
53
|
+
variadic: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface PositionalSpec {
|
|
57
|
+
name: string;
|
|
58
|
+
description: string;
|
|
59
|
+
required: boolean;
|
|
60
|
+
variadic: boolean;
|
|
61
|
+
valueChoices?: string[];
|
|
62
|
+
dynamicProvider?: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Walks the Commander program tree and returns the root CommandSpec.
|
|
67
|
+
* The root represents `harn` itself; its `subcommands` are the top-level
|
|
68
|
+
* subcommands.
|
|
69
|
+
*/
|
|
70
|
+
export function walkProgram(
|
|
71
|
+
program: Command,
|
|
72
|
+
lookup: CompletionContextLookup = noopLookup,
|
|
73
|
+
): CommandSpec {
|
|
74
|
+
return walkCommand(program, "", true, lookup);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function walkCommand(
|
|
78
|
+
cmd: Command,
|
|
79
|
+
parentPath: string,
|
|
80
|
+
isRoot: boolean,
|
|
81
|
+
lookup: CompletionContextLookup,
|
|
82
|
+
): CommandSpec {
|
|
83
|
+
const name = cmd.name();
|
|
84
|
+
// The root program represents `harn` itself; its path stays empty so that
|
|
85
|
+
// top-level subcommands have paths like "anthropic", not "harn anthropic".
|
|
86
|
+
// The shell driver walks COMP_WORDS[1..], so it's already operating in
|
|
87
|
+
// post-`harn` space and shouldn't see "bp" in its lookup keys.
|
|
88
|
+
const path = isRoot ? "" : parentPath ? `${parentPath} ${name}` : name;
|
|
89
|
+
|
|
90
|
+
const subcommands: CommandSpec[] = cmd.commands
|
|
91
|
+
.filter((c) => !isHidden(c))
|
|
92
|
+
// Skip the auto-injected `help` subcommand; noisy without being useful.
|
|
93
|
+
.filter((c) => c.name() !== "help")
|
|
94
|
+
.map((c) => walkCommand(c, path, false, lookup));
|
|
95
|
+
|
|
96
|
+
// Sort subcommands alphabetically for predictable output.
|
|
97
|
+
subcommands.sort((a, b) => a.name.localeCompare(b.name));
|
|
98
|
+
|
|
99
|
+
const options: OptionSpec[] = cmd.options
|
|
100
|
+
.filter((o) => !o.hidden)
|
|
101
|
+
.map((o) => optionSpec(o, path, lookup));
|
|
102
|
+
|
|
103
|
+
// Commander auto-injects `-h, --help` for every command but doesn't expose
|
|
104
|
+
// it in `cmd.options`. Add a synthetic entry so tab-completion suggests it.
|
|
105
|
+
options.push({
|
|
106
|
+
long: "--help",
|
|
107
|
+
short: "-h",
|
|
108
|
+
description: "Show help for this command",
|
|
109
|
+
takesValue: false,
|
|
110
|
+
variadic: false,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const positionals: PositionalSpec[] = cmd.registeredArguments.map((a, i) =>
|
|
114
|
+
positionalSpec(a, path, i, lookup),
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
name,
|
|
119
|
+
path,
|
|
120
|
+
description: (cmd.description() || "").replace(/\s+/g, " ").trim(),
|
|
121
|
+
subcommands,
|
|
122
|
+
options,
|
|
123
|
+
positionals,
|
|
124
|
+
hasHelp: true,
|
|
125
|
+
hidden: isHidden(cmd),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function optionSpec(opt: Option, commandPath: string, lookup: CompletionContextLookup): OptionSpec {
|
|
130
|
+
const long = opt.long ?? "";
|
|
131
|
+
const short = opt.short ?? "";
|
|
132
|
+
const takesValue = opt.required || opt.optional;
|
|
133
|
+
const provider = lookup({
|
|
134
|
+
commandPath,
|
|
135
|
+
option: long || short,
|
|
136
|
+
});
|
|
137
|
+
return {
|
|
138
|
+
long,
|
|
139
|
+
short,
|
|
140
|
+
description: (opt.description || "").replace(/\s+/g, " ").trim(),
|
|
141
|
+
takesValue,
|
|
142
|
+
valueChoices: opt.argChoices ? [...opt.argChoices] : undefined,
|
|
143
|
+
dynamicProvider: provider,
|
|
144
|
+
variadic: opt.variadic,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function positionalSpec(
|
|
149
|
+
arg: Argument,
|
|
150
|
+
commandPath: string,
|
|
151
|
+
index: number,
|
|
152
|
+
lookup: CompletionContextLookup,
|
|
153
|
+
): PositionalSpec {
|
|
154
|
+
const provider = lookup({
|
|
155
|
+
commandPath,
|
|
156
|
+
positional: index,
|
|
157
|
+
});
|
|
158
|
+
return {
|
|
159
|
+
name: arg.name(),
|
|
160
|
+
description: (arg.description || "").replace(/\s+/g, " ").trim(),
|
|
161
|
+
required: arg.required,
|
|
162
|
+
variadic: arg.variadic,
|
|
163
|
+
valueChoices: arg.argChoices ? [...arg.argChoices] : undefined,
|
|
164
|
+
dynamicProvider: provider,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function isHidden(cmd: Command): boolean {
|
|
169
|
+
// Commander marks hidden commands via `command(..., { hidden: true })`.
|
|
170
|
+
// The flag lives on a private field; we read it defensively via `any`.
|
|
171
|
+
const c = cmd as unknown as { _hidden?: boolean };
|
|
172
|
+
return c._hidden === true;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Flatten the spec tree into a list of (path, spec) entries for code generation. */
|
|
176
|
+
export function flatten(root: CommandSpec): Array<{ path: string; spec: CommandSpec }> {
|
|
177
|
+
const out: Array<{ path: string; spec: CommandSpec }> = [];
|
|
178
|
+
const walk = (s: CommandSpec): void => {
|
|
179
|
+
out.push({ path: s.path, spec: s });
|
|
180
|
+
for (const sub of s.subcommands) walk(sub);
|
|
181
|
+
};
|
|
182
|
+
walk(root);
|
|
183
|
+
return out;
|
|
184
|
+
}
|