From 9469f9657abee6efe01cde62217ecd154a1eb3a4 Mon Sep 17 00:00:00 2001 From: Juergen Klaassen Date: Fri, 12 Jun 2026 11:52:43 -0600 Subject: [PATCH 1/2] feat: auto-generated showcase site (commands.json + llms.txt + GitHub Pages) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Borrows the pattern from jamf-cli's generator/site/. The site is generated from internal/schema (the same source the `jc schema` command uses) so there's a single source of truth — no parallel Cobra walker, no hand-kept catalog file. Adding a new command means: edit schema.go, run `make site`, commit. The verify-site CI gate catches the "forgot to regenerate" case on PRs; the deploy-site workflow regenerates at deploy time as defense in depth so Pages always reflects what's actually shipping. Components: - cmd/sitegen/main.go — renders schema.BuildCommandManifest() into: - docs/site/commands.json (manifest + sidebar categories) - docs/site/llms.txt (short index per https://llmstxt.org/) - docs/site/llms-full.txt (full reference for AI agents) Hardcoded category map groups 38 commands under 9 sidebar headings. Version is deliberately omitted from output so git-describe churn doesn't churn commands.json on every commit. - docs/site/index.html — vanilla HTML+JS, zero build deps: - ⌘K-focusable filter searches across command name, description, category, subcommands, and every flag - Category sidebar with scroll-spy active state - Per-command card: subcommand chips + flags table - Dark/light mode follows prefers-color-scheme - Long-form elaboration paragraphs (with `code` / *italic*) rendered for hand-curated commands - internal/schema/schema.go — adds optional Long field to CommandEntry, populated for the 7 commands in Setup + AI & Automation where the one-line Description undersells the feature. Other commands can be enriched incrementally; the field is omitempty. - Makefile — `make site` regenerates, `make verify-site` diffs against the committed artifacts and fails if stale. - .github/workflows/verify-site.yml — PR-time stale-catalog detector. Runs only when schema, sitegen, docs/site/, or the Makefile changed. - .github/workflows/deploy-site.yml — main-merge deploy to Pages via actions/upload-pages-artifact + actions/deploy-pages. Regenerates at deploy time as defense in depth. - README.md — site link in header, contributor notes in Development. Pages URL will be https://thejumpcloud.github.io/jc-cli/ once Pages is enabled for the repo (Settings → Pages → Source: GitHub Actions). Refs KLA-442 Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/deploy-site.yml | 56 ++ .github/workflows/verify-site.yml | 33 + Makefile | 25 +- README.md | 8 + cmd/sitegen/main.go | 299 +++++++ docs/site/commands.json | 1381 +++++++++++++++++++++++++++++ docs/site/index.html | 457 ++++++++++ docs/site/llms-full.txt | 557 ++++++++++++ docs/site/llms.txt | 78 ++ internal/schema/schema.go | 14 + 10 files changed, 2907 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/deploy-site.yml create mode 100644 .github/workflows/verify-site.yml create mode 100644 cmd/sitegen/main.go create mode 100644 docs/site/commands.json create mode 100644 docs/site/index.html create mode 100644 docs/site/llms-full.txt create mode 100644 docs/site/llms.txt diff --git a/.github/workflows/deploy-site.yml b/.github/workflows/deploy-site.yml new file mode 100644 index 0000000..02f1fb4 --- /dev/null +++ b/.github/workflows/deploy-site.yml @@ -0,0 +1,56 @@ +name: Deploy site + +# Publishes the auto-generated jc CLI showcase site to GitHub Pages on +# every merge to main. The site is regenerated from internal/schema in +# the workflow itself (defense in depth — the verify-site CI gate on PRs +# already keeps the committed docs/site/ in sync). This way Pages always +# reflects what's actually shipping. + +on: + push: + branches: [main] + paths: + - 'docs/site/**' + - 'cmd/sitegen/**' + - 'internal/schema/**' + - 'Makefile' + - '.github/workflows/deploy-site.yml' + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +# Only one Pages deploy at a time. If a second merge lands while one is +# deploying, the second waits — we want the latest deploy to win, not the +# earliest. cancel-in-progress: false because actions/deploy-pages doesn't +# tolerate mid-flight cancellation cleanly. +concurrency: + group: pages + cancel-in-progress: false + +jobs: + deploy: + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - uses: actions/checkout@v5 + + - uses: actions/setup-go@v6 + with: + go-version-file: go.mod + + - name: Regenerate site artifacts + run: make site + + - uses: actions/configure-pages@v5 + + - uses: actions/upload-pages-artifact@v3 + with: + path: docs/site + + - id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/verify-site.yml b/.github/workflows/verify-site.yml new file mode 100644 index 0000000..afd4fef --- /dev/null +++ b/.github/workflows/verify-site.yml @@ -0,0 +1,33 @@ +name: Verify site + +# Catches stale docs/site/ artifacts on incoming PRs. If a contributor +# touched internal/schema, cmd/sitegen, the Makefile site target, or +# docs/site/ directly without running `make site`, this job fails and +# tells them how to fix it. Complements the deploy-site workflow, which +# regenerates at deploy time but only after merge. + +on: + pull_request: + branches: [main] + paths: + - 'internal/schema/**' + - 'cmd/sitegen/**' + - 'docs/site/**' + - 'Makefile' + - '.github/workflows/verify-site.yml' + +permissions: + contents: read + +jobs: + verify: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + + - uses: actions/setup-go@v6 + with: + go-version-file: go.mod + + - name: Verify docs/site/ is up to date + run: make verify-site diff --git a/Makefile b/Makefile index 723067d..6a1fbcc 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ BIN := jc DIST := dist PLATFORMS := darwin/amd64 darwin/arm64 linux/amd64 linux/arm64 windows/amd64 -.PHONY: build test lint install clean vet integration-test integration-test-readonly clean-dist dist release +.PHONY: build test lint install clean vet integration-test integration-test-readonly clean-dist dist release site verify-site build: go build -ldflags "$(LDFLAGS)" -o $(BIN) ./cmd/jc @@ -64,3 +64,26 @@ integration-test: build integration-test-readonly: build @JC=./$(BIN) ./scripts/integration-test.sh --skip-mutable + +# Regenerate docs/site/ artifacts (commands.json, llms.txt, llms-full.txt) +# from the schema manifest. Run after touching internal/schema or any +# command that should appear in the public catalog. +site: + go run ./cmd/sitegen -out docs/site + +# CI gate: fail if docs/site/ is stale relative to the current schema. +# Regenerates into a temp dir and diffs the three generated artifacts +# against the committed copies. Catches "added a new command but forgot +# to run make site." +verify-site: + @tmp=$$(mktemp -d) && \ + go run ./cmd/sitegen -out $$tmp >/dev/null && \ + for f in commands.json llms.txt llms-full.txt; do \ + diff -u docs/site/$$f $$tmp/$$f || { \ + echo "verify-site: docs/site/$$f is stale — run 'make site' and commit"; \ + rm -rf $$tmp; \ + exit 1; \ + }; \ + done && \ + rm -rf $$tmp && \ + echo "verify-site: docs/site/ is up to date" diff --git a/README.md b/README.md index 484026b..c7cff16 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ Single Go binary. Full API coverage (V1, V2, Directory Insights, Graph). Six output formats. Built-in MCP server for AI assistants. Interactive TUI browser with full CRUD. Recipe engine for multi-step workflows. Plan mode for safe mutation previews. Natural language interface via `jc ask`. +**Browse the full command catalog:** [thejumpcloud.github.io/jc-cli](https://thejumpcloud.github.io/jc-cli/) — searchable reference for every command and flag, plus [`llms.txt`](https://thejumpcloud.github.io/jc-cli/llms.txt) / [`llms-full.txt`](https://thejumpcloud.github.io/jc-cli/llms-full.txt) for AI agents. + --- ## Installation @@ -912,8 +914,14 @@ make lint # Run go vet make install # Install to $GOPATH/bin make integration-test # Full integration test (requires auth) make integration-test-readonly # Read-only probes only (no create/delete) +make site # Regenerate docs/site/ from the schema manifest +make verify-site # Fail if docs/site/ drifted from the schema ``` +### Showcase site + +The public command catalog at [thejumpcloud.github.io/jc-cli](https://thejumpcloud.github.io/jc-cli/) is generated from `internal/schema/schema.go` (the same source the `jc schema` command uses). After adding or renaming a command, run `make site` and commit the regenerated files under `docs/site/`. The `verify-site` CI gate fails the PR if you forget. + ### Shell Completion ```bash diff --git a/cmd/sitegen/main.go b/cmd/sitegen/main.go new file mode 100644 index 0000000..97b1d66 --- /dev/null +++ b/cmd/sitegen/main.go @@ -0,0 +1,299 @@ +// Command sitegen renders the jc CLI command manifest into the static +// showcase site artifacts under docs/site/: +// +// - commands.json — the manifest, augmented with sidebar categories +// - llms.txt — short agent-facing index (https://llmstxt.org/) +// - llms-full.txt — full agent-facing reference +// +// The single source of truth is schema.BuildCommandManifest() — this tool +// is just a renderer. To regenerate, run `make site`. The CI gate +// `make verify-site` diffs the committed artifacts against a fresh +// regeneration and fails if they drifted. +package main + +import ( + "encoding/json" + "flag" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/klaassen-consulting/jc/internal/schema" +) + +// category groups command paths under a sidebar heading on the site. +// Order matters — it controls sidebar order. A command path that doesn't +// match any category falls under "Other" at the bottom. +type category struct { + Name string + Commands []string +} + +var categories = []category{ + { + Name: "Identity & Access", + Commands: []string{ + "jc users", "jc groups", "jc admins", + "jc auth-policies", "jc iplists", "jc identity-providers", + }, + }, + { + Name: "Devices & MDM", + Commands: []string{"jc devices", "jc apple-mdm"}, + }, + { + Name: "Apps & SSO", + Commands: []string{"jc apps", "jc app-templates", "jc saas-management"}, + }, + { + Name: "Policies & Commands", + Commands: []string{ + "jc policies", "jc policy-templates", "jc policy-groups", "jc commands", + }, + }, + { + Name: "Insights", + Commands: []string{"jc insights", "jc system-insights"}, + }, + { + Name: "Integrations", + Commands: []string{ + "jc ad", "jc gsuite", "jc office365", "jc duo", + "jc ldap", "jc radius", "jc software", "jc assets", + }, + }, + { + Name: "Org & Lifecycle", + Commands: []string{ + "jc org", "jc user-states", "jc access-requests", + "jc custom-emails", "jc bulk", "jc graph", + }, + }, + { + Name: "AI & Automation", + Commands: []string{ + "jc recipe", "jc mcp", "jc ask", "jc explain", "jc schema", + }, + }, + { + Name: "Setup", + Commands: []string{"jc auth", "jc config"}, + }, +} + +type enrichedCommand struct { + Path string `json:"path"` + Description string `json:"description"` + Long string `json:"long,omitempty"` + Category string `json:"category"` + Subcommands []string `json:"subcommands,omitempty"` + Flags []schema.FlagEntry `json:"flags,omitempty"` +} + +type output struct { + Name string `json:"name"` + Description string `json:"description"` + Categories []string `json:"categories"` + Commands []enrichedCommand `json:"commands"` + GlobalFlags []schema.FlagEntry `json:"global_flags"` + Resources []string `json:"resources"` +} + +func main() { + outDir := flag.String("out", "docs/site", "output directory") + flag.Parse() + + if err := run(*outDir); err != nil { + fmt.Fprintln(os.Stderr, "sitegen:", err) + os.Exit(1) + } +} + +func run(outDir string) error { + manifest := schema.BuildCommandManifest() + o := enrich(manifest) + + if err := os.MkdirAll(outDir, 0o755); err != nil { + return err + } + + if err := writeJSON(filepath.Join(outDir, "commands.json"), o); err != nil { + return fmt.Errorf("commands.json: %w", err) + } + if err := writeFile(filepath.Join(outDir, "llms.txt"), renderLLMs(o)); err != nil { + return fmt.Errorf("llms.txt: %w", err) + } + if err := writeFile(filepath.Join(outDir, "llms-full.txt"), renderLLMsFull(o)); err != nil { + return fmt.Errorf("llms-full.txt: %w", err) + } + + fmt.Fprintf(os.Stderr, "sitegen: wrote %d commands under %d categories to %s\n", + len(o.Commands), len(o.Categories), outDir) + return nil +} + +// enrich joins the schema manifest with the hardcoded category map. +// The version field is deliberately dropped: it tracks git-describe and +// would churn commands.json on every commit, defeating `make verify-site`. +func enrich(m schema.CommandManifest) output { + catByPath := make(map[string]string, len(categories)*4) + var catNames []string + for _, c := range categories { + catNames = append(catNames, c.Name) + for _, p := range c.Commands { + catByPath[p] = c.Name + } + } + const fallback = "Other" + hasFallback := false + + enriched := make([]enrichedCommand, 0, len(m.Commands)) + for _, c := range m.Commands { + cat, ok := catByPath[c.Path] + if !ok { + cat = fallback + hasFallback = true + } + enriched = append(enriched, enrichedCommand{ + Path: c.Path, + Description: c.Description, + Long: c.Long, + Category: cat, + Subcommands: c.Subcommands, + Flags: c.Flags, + }) + } + if hasFallback { + catNames = append(catNames, fallback) + } + + return output{ + Name: m.Name, + Description: m.Description, + Categories: catNames, + Commands: enriched, + GlobalFlags: m.GlobalFlags, + Resources: m.Resources, + } +} + +func writeJSON(path string, v any) error { + b, err := json.MarshalIndent(v, "", " ") + if err != nil { + return err + } + b = append(b, '\n') + return os.WriteFile(path, b, 0o644) +} + +func writeFile(path, content string) error { + return os.WriteFile(path, []byte(content), 0o644) +} + +// renderLLMs emits the short llms.txt index per https://llmstxt.org/. +func renderLLMs(o output) string { + var b strings.Builder + fmt.Fprintf(&b, "# %s\n\n", o.Name) + fmt.Fprintf(&b, "> %s\n\n", o.Description) + fmt.Fprintln(&b, "The jc CLI is a Go-based command-line tool for JumpCloud — managing users, devices, groups, policies, applications, commands, insights, and more. It exposes every JumpCloud resource through a consistent verb-noun grammar with structured JSON output, an MCP server for AI agents, and a recipe system for declarative automation.") + fmt.Fprintln(&b) + fmt.Fprintln(&b, "## Command catalog") + fmt.Fprintln(&b) + + byCategory := groupByCategory(o) + for _, cat := range o.Categories { + cmds := byCategory[cat] + if len(cmds) == 0 { + continue + } + fmt.Fprintf(&b, "### %s\n\n", cat) + for _, c := range cmds { + fmt.Fprintf(&b, "- `%s` — %s\n", c.Path, c.Description) + } + fmt.Fprintln(&b) + } + + fmt.Fprintln(&b, "## See also") + fmt.Fprintln(&b) + fmt.Fprintln(&b, "- [Full reference](llms-full.txt)") + fmt.Fprintln(&b, "- [Machine-readable manifest](commands.json)") + fmt.Fprintln(&b, "- [Source repository](https://github.com/TheJumpCloud/jc-cli)") + return b.String() +} + +// renderLLMsFull emits the long-form reference: every command, every flag, +// global flags, and the full resource list. +func renderLLMsFull(o output) string { + var b strings.Builder + fmt.Fprintf(&b, "# %s — full reference\n\n", o.Name) + fmt.Fprintf(&b, "> %s\n\n", o.Description) + + fmt.Fprintln(&b, "## Global flags") + fmt.Fprintln(&b) + fmt.Fprintln(&b, "These flags work on every `jc` subcommand:") + fmt.Fprintln(&b) + for _, f := range o.GlobalFlags { + renderFlag(&b, f) + } + fmt.Fprintln(&b) + + fmt.Fprintln(&b, "## Commands") + fmt.Fprintln(&b) + byCategory := groupByCategory(o) + for _, cat := range o.Categories { + cmds := byCategory[cat] + if len(cmds) == 0 { + continue + } + fmt.Fprintf(&b, "### %s\n\n", cat) + for _, c := range cmds { + fmt.Fprintf(&b, "#### `%s`\n\n", c.Path) + fmt.Fprintf(&b, "%s\n\n", c.Description) + if c.Long != "" { + fmt.Fprintf(&b, "%s\n\n", c.Long) + } + if len(c.Subcommands) > 0 { + fmt.Fprintln(&b, "**Subcommands:**", "`"+strings.Join(c.Subcommands, "`, `")+"`") + fmt.Fprintln(&b) + } + if len(c.Flags) > 0 { + fmt.Fprintln(&b, "**Flags:**") + fmt.Fprintln(&b) + for _, f := range c.Flags { + renderFlag(&b, f) + } + fmt.Fprintln(&b) + } + } + } + + fmt.Fprintln(&b, "## Resource types") + fmt.Fprintln(&b) + fmt.Fprintln(&b, "Every resource has a JSON schema available via `jc schema `:") + fmt.Fprintln(&b) + for _, r := range o.Resources { + fmt.Fprintf(&b, "- `%s`\n", r) + } + return b.String() +} + +func renderFlag(b *strings.Builder, f schema.FlagEntry) { + short := "" + if f.Shorthand != "" { + short = fmt.Sprintf(", `-%s`", f.Shorthand) + } + def := "" + if f.Default != "" { + def = fmt.Sprintf(" (default `%s`)", f.Default) + } + fmt.Fprintf(b, "- `--%s`%s `<%s>`%s — %s\n", f.Name, short, f.Type, def, f.Description) +} + +func groupByCategory(o output) map[string][]enrichedCommand { + m := make(map[string][]enrichedCommand, len(o.Categories)) + for _, c := range o.Commands { + m[c.Category] = append(m[c.Category], c) + } + return m +} diff --git a/docs/site/commands.json b/docs/site/commands.json new file mode 100644 index 0000000..8716be5 --- /dev/null +++ b/docs/site/commands.json @@ -0,0 +1,1381 @@ +{ + "name": "jc", + "description": "JumpCloud CLI — manage users, devices, groups, policies, commands, insights, and more", + "categories": [ + "Identity \u0026 Access", + "Devices \u0026 MDM", + "Apps \u0026 SSO", + "Policies \u0026 Commands", + "Insights", + "Integrations", + "Org \u0026 Lifecycle", + "AI \u0026 Automation", + "Setup" + ], + "commands": [ + { + "path": "jc auth", + "description": "Authentication commands", + "long": "Manage JumpCloud credentials and switch between organizations. The CLI supports two credential types: a static API key (read from `JC_API_KEY`, the config, or the system keychain), or an OAuth service account (client ID + secret), with automatic token refresh. Credentials can be stored as plaintext, in macOS Keychain / GNOME libsecret / Windows Credential Manager via a `keychain://` reference, or supplied per-invocation with `--api-key`. Multiple named profiles let MSPs and admins flip between orgs without re-authenticating (`jc auth switch \u003cprofile\u003e`), and `jc auth status` reveals the active profile, credential type, fingerprint, and source.", + "category": "Setup", + "subcommands": [ + "login", + "logout", + "status", + "switch" + ] + }, + { + "path": "jc config", + "description": "Configuration management", + "long": "View and update jc CLI configuration — default output format, color/pager behavior, TUI refresh interval, plan-mode safety toggles, cache TTLs, and dozens of other preferences. Settings live in `~/.config/jc/config.yaml` and can be overridden per-command via `JC_*` environment variables or flags (env \u003e flag \u003e config \u003e built-in default). Use `jc config view` for the full effective configuration including override sources, or `jc config set \u003ckey\u003e \u003cvalue\u003e` to persist a change.", + "category": "Setup", + "subcommands": [ + "view", + "set" + ] + }, + { + "path": "jc users", + "description": "Manage JumpCloud system users", + "category": "Identity \u0026 Access", + "subcommands": [ + "list", + "get", + "create", + "update", + "delete", + "search", + "lock", + "unlock", + "reset-mfa", + "reset-password", + "ssh-keys", + "ssh-key-add", + "ssh-key-delete" + ], + "flags": [ + { + "name": "limit", + "type": "int", + "description": "Maximum number of results (list/search)" + }, + { + "name": "sort", + "type": "string", + "description": "Sort field, prefix - for descending (list/search)" + }, + { + "name": "filter", + "type": "string[]", + "description": "Filter expressions (list)" + }, + { + "name": "search", + "type": "string", + "description": "Full-text search term (list)" + } + ] + }, + { + "path": "jc devices", + "description": "Manage JumpCloud devices (systems)", + "category": "Devices \u0026 MDM", + "subcommands": [ + "list", + "get", + "update", + "delete", + "search", + "lock", + "restart", + "erase", + "fde-key" + ], + "flags": [ + { + "name": "limit", + "type": "int", + "description": "Maximum number of results (list)" + }, + { + "name": "sort", + "type": "string", + "description": "Sort field, prefix - for descending (list)" + }, + { + "name": "filter", + "type": "string[]", + "description": "Filter expressions (list)" + }, + { + "name": "confirm-erase", + "type": "bool", + "description": "Required safety flag for erase command" + } + ] + }, + { + "path": "jc groups", + "description": "Manage user and device groups", + "category": "Identity \u0026 Access", + "subcommands": [ + "user list", + "user get", + "user create", + "user update", + "user delete", + "device list", + "device get", + "device create", + "device update", + "device delete", + "add-member", + "remove-member" + ], + "flags": [ + { + "name": "limit", + "type": "int", + "description": "Maximum number of results (list)" + }, + { + "name": "sort", + "type": "string", + "description": "Sort field (list)" + }, + { + "name": "filter", + "type": "string[]", + "description": "Filter expressions (list)" + } + ] + }, + { + "path": "jc commands", + "description": "Manage JumpCloud commands", + "category": "Policies \u0026 Commands", + "subcommands": [ + "list", + "get", + "create", + "update", + "delete", + "run", + "results", + "trigger" + ], + "flags": [ + { + "name": "limit", + "type": "int", + "description": "Maximum number of results (list)" + }, + { + "name": "sort", + "type": "string", + "description": "Sort field (list)" + }, + { + "name": "filter", + "type": "string[]", + "description": "Filter expressions (list)" + }, + { + "name": "type", + "type": "string", + "description": "Command type filter: linux, mac, windows (list)" + }, + { + "name": "data", + "type": "string", + "description": "JSON payload for trigger" + } + ] + }, + { + "path": "jc policies", + "description": "Manage JumpCloud policies", + "category": "Policies \u0026 Commands", + "subcommands": [ + "list", + "get", + "create", + "update", + "delete", + "results" + ], + "flags": [ + { + "name": "limit", + "type": "int", + "description": "Maximum number of results (list)" + }, + { + "name": "sort", + "type": "string", + "description": "Sort field (list)" + }, + { + "name": "filter", + "type": "string[]", + "description": "Filter expressions (list)" + } + ] + }, + { + "path": "jc apps", + "description": "Manage SSO applications", + "category": "Apps \u0026 SSO", + "subcommands": [ + "list", + "get", + "create", + "update", + "delete" + ], + "flags": [ + { + "name": "limit", + "type": "int", + "description": "Maximum number of results (list)" + }, + { + "name": "sort", + "type": "string", + "description": "Sort field (list)" + }, + { + "name": "filter", + "type": "string[]", + "description": "Filter expressions (list)" + }, + { + "name": "sso-type", + "type": "string", + "description": "SSO type (create, required)" + }, + { + "name": "config", + "type": "string", + "description": "SSO-specific config as JSON (create/update)" + } + ] + }, + { + "path": "jc admins", + "description": "Manage JumpCloud administrators", + "category": "Identity \u0026 Access", + "subcommands": [ + "list", + "get", + "create", + "update", + "delete" + ], + "flags": [ + { + "name": "limit", + "type": "int", + "description": "Maximum number of results (list)" + }, + { + "name": "sort", + "type": "string", + "description": "Sort field (list)" + }, + { + "name": "filter", + "type": "string[]", + "description": "Filter expressions (list)" + }, + { + "name": "email", + "type": "string", + "description": "Admin email address (create, required)" + }, + { + "name": "role", + "type": "string", + "description": "Admin role name (create/update)" + }, + { + "name": "enable-mfa", + "type": "bool", + "description": "Enable multi-factor authentication (create/update)" + }, + { + "name": "disable-mfa", + "type": "bool", + "description": "Disable multi-factor authentication (update)" + } + ] + }, + { + "path": "jc insights", + "description": "Query Directory Insights audit events", + "category": "Insights", + "subcommands": [ + "query", + "count", + "distinct", + "save", + "run", + "saved" + ], + "flags": [ + { + "name": "service", + "type": "string", + "description": "Event service: sso, radius, ldap, user_portal, admin, mdm, directory, software, systems, password_manager, all" + }, + { + "name": "last", + "type": "string", + "description": "Time range: 24h, 7d, 30d, 1m" + }, + { + "name": "start", + "type": "string", + "description": "Start time (RFC 3339 or YYYY-MM-DD)" + }, + { + "name": "end", + "type": "string", + "description": "End time (RFC 3339 or YYYY-MM-DD)" + }, + { + "name": "event-type", + "type": "string", + "description": "Filter by event type" + }, + { + "name": "limit", + "type": "int", + "description": "Maximum events to return" + }, + { + "name": "sort", + "type": "string", + "description": "Sort field" + } + ] + }, + { + "path": "jc auth-policies", + "description": "Manage authentication policies for conditional access", + "category": "Identity \u0026 Access", + "subcommands": [ + "list", + "get", + "create", + "update", + "delete", + "enable", + "disable", + "simulate", + "blast-radius" + ], + "flags": [ + { + "name": "limit", + "type": "int", + "description": "Maximum number of results (list)" + }, + { + "name": "sort", + "type": "string", + "description": "Sort field (list)" + }, + { + "name": "filter", + "type": "string[]", + "description": "Filter expressions (list)" + }, + { + "name": "conditions", + "type": "string", + "description": "Conditions tree as raw JSON (create/update)" + }, + { + "name": "user", + "type": "string", + "description": "User name or ID (simulate)" + }, + { + "name": "ip", + "type": "string", + "description": "Source IP address (simulate)" + }, + { + "name": "device", + "type": "string", + "description": "Device name or ID (simulate)" + }, + { + "name": "location", + "type": "string", + "description": "Country code (simulate)" + } + ] + }, + { + "path": "jc iplists", + "description": "Manage IP lists for authentication policies", + "category": "Identity \u0026 Access", + "subcommands": [ + "list", + "get", + "create", + "update", + "delete" + ], + "flags": [ + { + "name": "limit", + "type": "int", + "description": "Maximum number of results (list)" + }, + { + "name": "sort", + "type": "string", + "description": "Sort field (list)" + }, + { + "name": "filter", + "type": "string[]", + "description": "Filter expressions (list)" + }, + { + "name": "ips", + "type": "string", + "description": "Comma-separated IP entries (create/update)" + } + ] + }, + { + "path": "jc identity-providers", + "description": "Manage JumpCloud identity providers for SSO/OIDC federation", + "category": "Identity \u0026 Access", + "subcommands": [ + "list", + "get", + "create", + "update", + "delete" + ], + "flags": [ + { + "name": "limit", + "type": "int", + "description": "Maximum results (list)" + }, + { + "name": "name", + "type": "string", + "description": "Provider name (create/update)" + }, + { + "name": "type", + "type": "string", + "description": "Provider type: OIDC, GOOGLE, OKTA, AZURE (create)" + }, + { + "name": "client-id", + "type": "string", + "description": "OIDC client ID (create/update)" + }, + { + "name": "client-secret", + "type": "string", + "description": "OIDC client secret (create/update)" + }, + { + "name": "url", + "type": "string", + "description": "OIDC issuer URL (create/update)" + } + ] + }, + { + "path": "jc saas-management", + "description": "Manage JumpCloud SaaS Management applications", + "category": "Apps \u0026 SSO", + "subcommands": [ + "list", + "get", + "create", + "update", + "delete", + "accounts", + "account-get", + "account-delete", + "usage", + "licenses", + "catalog-get" + ], + "flags": [ + { + "name": "limit", + "type": "int", + "description": "Maximum number of results (list)" + }, + { + "name": "filter", + "type": "string[]", + "description": "Filter expressions (list)" + }, + { + "name": "catalog-app-id", + "type": "string", + "description": "Catalog application ID (create)" + }, + { + "name": "status", + "type": "string", + "description": "App status: APPROVED, UNAPPROVED, IGNORED (create/update)" + }, + { + "name": "access-restriction", + "type": "string", + "description": "Access restriction: DEFAULT_ACTION, NO_ACTION, BLOCK, DISMISSIBLE_WARNING (create/update)" + }, + { + "name": "account-id", + "type": "string", + "description": "Account ID (account-get/account-delete)" + }, + { + "name": "day-count", + "type": "int", + "description": "Number of days of usage data (usage, default 30)" + } + ] + }, + { + "path": "jc software", + "description": "Manage JumpCloud software apps", + "category": "Integrations", + "subcommands": [ + "list", + "get", + "create", + "update", + "delete", + "statuses", + "associations", + "reclaim-license" + ], + "flags": [ + { + "name": "limit", + "type": "int", + "description": "Maximum number of results (list)" + }, + { + "name": "sort", + "type": "string", + "description": "Sort field (list)" + }, + { + "name": "filter", + "type": "string[]", + "description": "Filter expressions (list)" + }, + { + "name": "name", + "type": "string", + "description": "App display name (create/update)" + }, + { + "name": "settings", + "type": "string", + "description": "Package settings as raw JSON (create/update)" + } + ] + }, + { + "path": "jc assets", + "description": "Manage JumpCloud assets (devices, accessories, locations)", + "category": "Integrations", + "subcommands": [ + "devices", + "accessories", + "locations" + ], + "flags": [ + { + "name": "limit", + "type": "int", + "description": "Maximum number of results (list)" + }, + { + "name": "field", + "type": "string[]", + "description": "Set field value as 'Label=Value' (create/update)" + } + ] + }, + { + "path": "jc ldap", + "description": "Manage JumpCloud LDAP servers", + "category": "Integrations", + "subcommands": [ + "list", + "get", + "create", + "update", + "delete", + "samba-domains", + "samba-domain-get", + "samba-domain-create", + "samba-domain-update", + "samba-domain-delete" + ], + "flags": [ + { + "name": "limit", + "type": "int", + "description": "Maximum number of results (list)" + }, + { + "name": "sort", + "type": "string", + "description": "Sort field (list)" + }, + { + "name": "filter", + "type": "string[]", + "description": "Filter expressions (list)" + }, + { + "name": "name", + "type": "string", + "description": "LDAP server name (create/update) or samba domain workgroup name" + }, + { + "name": "user-lockout-action", + "type": "string", + "description": "Action on user lockout (create/update)" + }, + { + "name": "user-password-expiration-action", + "type": "string", + "description": "Action on password expiration (create/update)" + }, + { + "name": "domain-id", + "type": "string", + "description": "Samba domain ID (samba-domain-get/update/delete)" + }, + { + "name": "sid", + "type": "string", + "description": "Samba domain security identifier (samba-domain-create/update)" + } + ] + }, + { + "path": "jc access-requests", + "description": "Manage JumpCloud temporary elevated device privilege requests", + "category": "Org \u0026 Lifecycle", + "subcommands": [ + "list", + "get", + "create", + "update", + "revoke" + ], + "flags": [ + { + "name": "limit", + "type": "int", + "description": "Maximum number of results (list)" + }, + { + "name": "filter", + "type": "string[]", + "description": "Filter expressions (list)" + }, + { + "name": "user", + "type": "string", + "description": "User name or ID (create)" + }, + { + "name": "device", + "type": "string", + "description": "Device name or ID (create)" + }, + { + "name": "expiry", + "type": "string", + "description": "Expiry time RFC 3339 (create/update)" + }, + { + "name": "sudo", + "type": "bool", + "description": "Enable sudo access (create)" + }, + { + "name": "sudo-nopasswd", + "type": "bool", + "description": "Enable passwordless sudo (create)" + }, + { + "name": "remarks", + "type": "string", + "description": "Optional remarks (create/update)" + } + ] + }, + { + "path": "jc ad", + "description": "Manage JumpCloud Active Directory integrations", + "category": "Integrations", + "subcommands": [ + "list", + "get", + "create", + "update", + "delete" + ], + "flags": [ + { + "name": "limit", + "type": "int", + "description": "Maximum number of results (list)" + }, + { + "name": "sort", + "type": "string", + "description": "Sort field (list)" + }, + { + "name": "filter", + "type": "string[]", + "description": "Filter expressions (list)" + }, + { + "name": "domain", + "type": "string", + "description": "AD domain name (create, required)" + }, + { + "name": "use-case", + "type": "string", + "description": "Integration use case (create/update)" + }, + { + "name": "groups-enabled", + "type": "bool", + "description": "Enable group sync (update)" + } + ] + }, + { + "path": "jc org", + "description": "View and update JumpCloud organization information", + "category": "Org \u0026 Lifecycle", + "subcommands": [ + "list", + "get", + "settings", + "update" + ], + "flags": [ + { + "name": "name", + "type": "string", + "description": "Organization display name (update)" + }, + { + "name": "settings-json", + "type": "string", + "description": "Raw JSON for complex settings fields (update)" + } + ] + }, + { + "path": "jc system-insights", + "description": "Query osquery system insight tables", + "category": "Insights", + "subcommands": [ + "list", + "tables" + ], + "flags": [ + { + "name": "system-id", + "type": "string", + "description": "Filter by device hostname or ID" + }, + { + "name": "filter", + "type": "string[]", + "description": "Filter expressions" + }, + { + "name": "limit", + "type": "int", + "description": "Maximum number of results" + }, + { + "name": "sort", + "type": "string", + "description": "Sort field" + } + ] + }, + { + "path": "jc radius", + "description": "Manage RADIUS servers", + "category": "Integrations", + "subcommands": [ + "list", + "get", + "create", + "update", + "delete" + ], + "flags": [ + { + "name": "limit", + "type": "int", + "description": "Maximum number of results (list)" + }, + { + "name": "sort", + "type": "string", + "description": "Sort field (list)" + }, + { + "name": "filter", + "type": "string[]", + "description": "Filter expressions (list)" + }, + { + "name": "name", + "type": "string", + "description": "RADIUS server name (create/update)" + }, + { + "name": "shared-secret", + "type": "string", + "description": "RADIUS shared secret (create, required)" + }, + { + "name": "auth-port", + "type": "int", + "default": "1812", + "description": "Authentication port (create/update)" + }, + { + "name": "accounting-port", + "type": "int", + "default": "1813", + "description": "Accounting port (create/update)" + } + ] + }, + { + "path": "jc policy-templates", + "description": "View policy templates", + "category": "Policies \u0026 Commands", + "subcommands": [ + "list", + "get" + ], + "flags": [ + { + "name": "limit", + "type": "int", + "description": "Maximum number of results (list)" + }, + { + "name": "filter", + "type": "string[]", + "description": "Filter expressions (list)" + } + ] + }, + { + "path": "jc apple-mdm", + "description": "Manage Apple MDM configurations", + "category": "Devices \u0026 MDM", + "subcommands": [ + "list", + "get", + "create", + "update", + "delete", + "enrollment-profiles", + "devices" + ], + "flags": [ + { + "name": "name", + "type": "string", + "description": "MDM configuration name (create/update)" + }, + { + "name": "org-name", + "type": "string", + "description": "Organization name (create/update)" + } + ] + }, + { + "path": "jc policy-groups", + "description": "Manage policy groups", + "category": "Policies \u0026 Commands", + "subcommands": [ + "list", + "get", + "create", + "update", + "delete" + ], + "flags": [ + { + "name": "limit", + "type": "int", + "description": "Maximum number of results (list)" + }, + { + "name": "sort", + "type": "string", + "description": "Sort field (list)" + }, + { + "name": "filter", + "type": "string[]", + "description": "Filter expressions (list)" + }, + { + "name": "name", + "type": "string", + "description": "Policy group name (create/update)" + }, + { + "name": "description", + "type": "string", + "description": "Policy group description (create/update)" + } + ] + }, + { + "path": "jc user-states", + "description": "Manage scheduled user state transitions", + "category": "Org \u0026 Lifecycle", + "subcommands": [ + "list", + "create", + "get", + "delete" + ], + "flags": [ + { + "name": "user", + "type": "string", + "description": "User name or ID (create, required)" + }, + { + "name": "state", + "type": "string", + "description": "Target state: suspended or activated (create, required)" + }, + { + "name": "start-date", + "type": "string", + "description": "Date for state change (create, required)" + }, + { + "name": "end-date", + "type": "string", + "description": "Optional end date to revert (create)" + } + ] + }, + { + "path": "jc gsuite", + "description": "Manage JumpCloud Google Workspace (G Suite) integrations", + "category": "Integrations", + "subcommands": [ + "list", + "get", + "translation-rules", + "import-users" + ], + "flags": [ + { + "name": "limit", + "type": "int", + "description": "Maximum number of results (list)" + }, + { + "name": "sort", + "type": "string", + "description": "Sort field (list)" + }, + { + "name": "filter", + "type": "string[]", + "description": "Filter expressions (list)" + } + ] + }, + { + "path": "jc office365", + "description": "Manage JumpCloud Office 365 integrations", + "category": "Integrations", + "subcommands": [ + "list", + "get", + "translation-rules", + "import-users" + ], + "flags": [ + { + "name": "limit", + "type": "int", + "description": "Maximum number of results (list)" + }, + { + "name": "sort", + "type": "string", + "description": "Sort field (list)" + }, + { + "name": "filter", + "type": "string[]", + "description": "Filter expressions (list)" + } + ] + }, + { + "path": "jc duo", + "description": "Manage JumpCloud Duo accounts and applications", + "category": "Integrations", + "subcommands": [ + "list", + "get", + "create", + "delete", + "apps", + "app-get", + "app-create", + "app-delete" + ], + "flags": [ + { + "name": "name", + "type": "string", + "description": "Duo account name (create)" + }, + { + "name": "app-id", + "type": "string", + "description": "Duo application ID (app-get, app-delete)" + }, + { + "name": "api-host", + "type": "string", + "description": "Duo API host (app-create)" + } + ] + }, + { + "path": "jc custom-emails", + "description": "Manage custom email templates", + "category": "Org \u0026 Lifecycle", + "subcommands": [ + "templates", + "get", + "create", + "update", + "delete" + ], + "flags": [ + { + "name": "type", + "type": "string", + "description": "Custom email type (create, required)" + }, + { + "name": "subject", + "type": "string", + "description": "Email subject line (create/update)" + }, + { + "name": "title", + "type": "string", + "description": "Email title (create/update)" + }, + { + "name": "body", + "type": "string", + "description": "Email body text (create/update)" + }, + { + "name": "header", + "type": "string", + "description": "Email header text (create/update)" + }, + { + "name": "button", + "type": "string", + "description": "Email button text (create/update)" + } + ] + }, + { + "path": "jc app-templates", + "description": "View application templates", + "category": "Apps \u0026 SSO", + "subcommands": [ + "list", + "get" + ], + "flags": [ + { + "name": "limit", + "type": "int", + "description": "Maximum number of results (list)" + }, + { + "name": "sort", + "type": "string", + "description": "Sort field (list)" + } + ] + }, + { + "path": "jc graph", + "description": "Manage JumpCloud resource associations", + "category": "Org \u0026 Lifecycle", + "subcommands": [ + "traverse", + "bind", + "unbind" + ], + "flags": [ + { + "name": "from", + "type": "string", + "description": "Source: type:identifier (e.g. user:jdoe)" + }, + { + "name": "to", + "type": "string", + "description": "Target type: user, system, user_group, system_group, application, policy, command" + } + ] + }, + { + "path": "jc bulk", + "description": "Bulk operations from CSV files", + "category": "Org \u0026 Lifecycle", + "subcommands": [ + "users" + ], + "flags": [ + { + "name": "file", + "type": "string", + "description": "Path to CSV file" + } + ] + }, + { + "path": "jc recipe", + "description": "Manage and run automation recipes", + "long": "Recipes turn JumpCloud workflows into declarative, version-controlled YAML files instead of one-off shell scripts. Each step is a `jc` invocation with template variables (`{{ .param.user }}`, `{{ .step.created_user.id }}`), conditional execution based on prior step outputs, and structured output capture that downstream steps can reference — so a single recipe can create a user, add them to groups, assign devices, send a welcome email, and bail out cleanly if any step fails or a precondition isn't met. The `--plan` flag walks every step without mutation, surfacing exactly what would change before you commit; `--param k=v` makes the same recipe reusable across users, orgs, or MSP customers. Built-ins ship for the workflows admins write over and over (onboarding, offboarding, MFA-reset campaigns, quarterly compliance audits); custom recipes live in `~/.config/jc/recipes/` and can be authored in `$EDITOR` via the TUI (`jc tui` → recipes → `n`). Recipes are also a first-class MCP primitive — agents can invoke `recipe.run` as a single tool call instead of orchestrating five separate ones, dramatically reducing the surface area for LLM missteps on multi-step changes.", + "category": "AI \u0026 Automation", + "subcommands": [ + "list", + "show", + "run", + "validate", + "create", + "import", + "export" + ], + "flags": [ + { + "name": "param", + "type": "string[]", + "description": "Recipe parameters as key=value (run)" + }, + { + "name": "file", + "type": "string", + "description": "Output file path (export)" + } + ] + }, + { + "path": "jc mcp", + "description": "MCP server for AI agent integration", + "long": "Run the jc CLI as a Model Context Protocol (MCP) server, exposing JumpCloud resources as typed tools to MCP-aware clients (Claude Code, Claude Desktop, Cursor, and any other host that speaks MCP). Includes per-minute rate limiting, an optional `--read-only` mode that disables every mutation tool, and a step-up authentication flow (TTY prompt, Touch ID, or webhook) for high-impact operations. Transport is stdio by default — point your MCP client at `jc mcp serve` and the CLI's full surface area becomes available to the agent.", + "category": "AI \u0026 Automation", + "subcommands": [ + "serve" + ], + "flags": [ + { + "name": "rate-limit", + "type": "int", + "default": "60", + "description": "Maximum tool calls per minute" + }, + { + "name": "read-only", + "type": "bool", + "description": "Disable all mutation tools" + } + ] + }, + { + "path": "jc schema", + "description": "Machine-readable schema and command manifest", + "long": "Return machine-readable JSON for every JumpCloud resource type and the full CLI command tree. Designed for LLMs, IDE plugins, code generators, and the public showcase site itself — `jc schema resources` lists every resource with its API version and supported verbs, `jc schema \u003cresource\u003e` returns the typed field list, and `jc schema commands` returns the full command manifest (paths, subcommands, flags, descriptions). The schema is the single source of truth — this site, the MCP server, and `jc ask` all read from it.", + "category": "AI \u0026 Automation", + "subcommands": [ + "resources", + "commands" + ] + }, + { + "path": "jc ask", + "description": "Translate natural language queries into jc CLI commands", + "long": "Translate plain-English questions into runnable `jc` commands using the bundled schema manifest plus an LLM prompt template. Useful for one-off queries like *\"show me admins who haven't logged in for 90 days\"* without remembering exact `jc insights query` flag syntax, or for *\"suspend everyone in the contractors group\"* without hand-piping `jc groups user list` into a `for` loop. The translated command is always printed for review before execution, and pairs naturally with `--plan` to preview any mutation before committing.", + "category": "AI \u0026 Automation" + }, + { + "path": "jc explain", + "description": "Explain what a command would do without executing", + "long": "Describe what a `jc` invocation would do — in plain English — without running it. Useful for sanity-checking LLM-generated commands before execution, understanding an unfamiliar invocation copied from a runbook, or onboarding new admins. The explanation covers the action type, target resource, affected scope (single object vs. batch), and a reversibility warning for destructive operations (`delete`, `lock`, `erase`).", + "category": "AI \u0026 Automation" + } + ], + "global_flags": [ + { + "name": "output", + "shorthand": "o", + "type": "string", + "default": "json", + "description": "Output format: json, table, csv, human, yaml, ndjson" + }, + { + "name": "table", + "shorthand": "t", + "type": "bool", + "description": "Shorthand for --output table" + }, + { + "name": "verbose", + "shorthand": "v", + "type": "bool", + "description": "Enable verbose HTTP logging" + }, + { + "name": "debug", + "type": "bool", + "description": "Enable debug logging" + }, + { + "name": "quiet", + "shorthand": "q", + "type": "bool", + "description": "Suppress output, exit code only" + }, + { + "name": "force", + "shorthand": "f", + "type": "bool", + "description": "Skip confirmation prompts" + }, + { + "name": "plan", + "type": "bool", + "description": "Preview changes without executing" + }, + { + "name": "ids", + "type": "bool", + "description": "Output one ID per line (for piping)" + }, + { + "name": "fields", + "type": "string", + "description": "Comma-separated list of fields to include" + }, + { + "name": "exclude", + "type": "string", + "description": "Comma-separated list of fields to exclude" + }, + { + "name": "all", + "type": "bool", + "description": "Include all available fields in output" + }, + { + "name": "org", + "type": "string", + "description": "Override active profile for this command" + }, + { + "name": "api-key", + "type": "string", + "description": "Override API key for this command" + }, + { + "name": "no-cache", + "type": "bool", + "description": "Bypass name-to-ID cache" + }, + { + "name": "no-color", + "type": "bool", + "description": "Disable color output" + }, + { + "name": "non-interactive", + "type": "bool", + "description": "Disable all interactive prompts" + } + ], + "resources": [ + "access-requests", + "ad", + "admins", + "app-templates", + "apple-mdm", + "apps", + "assets", + "auth-policies", + "commands", + "custom-emails", + "devices", + "duo", + "groups", + "gsuite", + "identity-providers", + "insights", + "iplists", + "ldap", + "office365", + "org", + "policies", + "policy-groups", + "policy-templates", + "radius", + "saas-management", + "software", + "system-insights", + "user-states", + "users" + ] +} diff --git a/docs/site/index.html b/docs/site/index.html new file mode 100644 index 0000000..dcc492b --- /dev/null +++ b/docs/site/index.html @@ -0,0 +1,457 @@ + + + + + +jc — JumpCloud CLI + + + + + + + +
+ +
+ +
+

jc CLI

+

+ +
+
+ + +
+
+ + + + diff --git a/docs/site/llms-full.txt b/docs/site/llms-full.txt new file mode 100644 index 0000000..8722039 --- /dev/null +++ b/docs/site/llms-full.txt @@ -0,0 +1,557 @@ +# jc — full reference + +> JumpCloud CLI — manage users, devices, groups, policies, commands, insights, and more + +## Global flags + +These flags work on every `jc` subcommand: + +- `--output`, `-o` `` (default `json`) — Output format: json, table, csv, human, yaml, ndjson +- `--table`, `-t` `` — Shorthand for --output table +- `--verbose`, `-v` `` — Enable verbose HTTP logging +- `--debug` `` — Enable debug logging +- `--quiet`, `-q` `` — Suppress output, exit code only +- `--force`, `-f` `` — Skip confirmation prompts +- `--plan` `` — Preview changes without executing +- `--ids` `` — Output one ID per line (for piping) +- `--fields` `` — Comma-separated list of fields to include +- `--exclude` `` — Comma-separated list of fields to exclude +- `--all` `` — Include all available fields in output +- `--org` `` — Override active profile for this command +- `--api-key` `` — Override API key for this command +- `--no-cache` `` — Bypass name-to-ID cache +- `--no-color` `` — Disable color output +- `--non-interactive` `` — Disable all interactive prompts + +## Commands + +### Identity & Access + +#### `jc users` + +Manage JumpCloud system users + +**Subcommands:** `list`, `get`, `create`, `update`, `delete`, `search`, `lock`, `unlock`, `reset-mfa`, `reset-password`, `ssh-keys`, `ssh-key-add`, `ssh-key-delete` + +**Flags:** + +- `--limit` `` — Maximum number of results (list/search) +- `--sort` `` — Sort field, prefix - for descending (list/search) +- `--filter` `` — Filter expressions (list) +- `--search` `` — Full-text search term (list) + +#### `jc groups` + +Manage user and device groups + +**Subcommands:** `user list`, `user get`, `user create`, `user update`, `user delete`, `device list`, `device get`, `device create`, `device update`, `device delete`, `add-member`, `remove-member` + +**Flags:** + +- `--limit` `` — Maximum number of results (list) +- `--sort` `` — Sort field (list) +- `--filter` `` — Filter expressions (list) + +#### `jc admins` + +Manage JumpCloud administrators + +**Subcommands:** `list`, `get`, `create`, `update`, `delete` + +**Flags:** + +- `--limit` `` — Maximum number of results (list) +- `--sort` `` — Sort field (list) +- `--filter` `` — Filter expressions (list) +- `--email` `` — Admin email address (create, required) +- `--role` `` — Admin role name (create/update) +- `--enable-mfa` `` — Enable multi-factor authentication (create/update) +- `--disable-mfa` `` — Disable multi-factor authentication (update) + +#### `jc auth-policies` + +Manage authentication policies for conditional access + +**Subcommands:** `list`, `get`, `create`, `update`, `delete`, `enable`, `disable`, `simulate`, `blast-radius` + +**Flags:** + +- `--limit` `` — Maximum number of results (list) +- `--sort` `` — Sort field (list) +- `--filter` `` — Filter expressions (list) +- `--conditions` `` — Conditions tree as raw JSON (create/update) +- `--user` `` — User name or ID (simulate) +- `--ip` `` — Source IP address (simulate) +- `--device` `` — Device name or ID (simulate) +- `--location` `` — Country code (simulate) + +#### `jc iplists` + +Manage IP lists for authentication policies + +**Subcommands:** `list`, `get`, `create`, `update`, `delete` + +**Flags:** + +- `--limit` `` — Maximum number of results (list) +- `--sort` `` — Sort field (list) +- `--filter` `` — Filter expressions (list) +- `--ips` `` — Comma-separated IP entries (create/update) + +#### `jc identity-providers` + +Manage JumpCloud identity providers for SSO/OIDC federation + +**Subcommands:** `list`, `get`, `create`, `update`, `delete` + +**Flags:** + +- `--limit` `` — Maximum results (list) +- `--name` `` — Provider name (create/update) +- `--type` `` — Provider type: OIDC, GOOGLE, OKTA, AZURE (create) +- `--client-id` `` — OIDC client ID (create/update) +- `--client-secret` `` — OIDC client secret (create/update) +- `--url` `` — OIDC issuer URL (create/update) + +### Devices & MDM + +#### `jc devices` + +Manage JumpCloud devices (systems) + +**Subcommands:** `list`, `get`, `update`, `delete`, `search`, `lock`, `restart`, `erase`, `fde-key` + +**Flags:** + +- `--limit` `` — Maximum number of results (list) +- `--sort` `` — Sort field, prefix - for descending (list) +- `--filter` `` — Filter expressions (list) +- `--confirm-erase` `` — Required safety flag for erase command + +#### `jc apple-mdm` + +Manage Apple MDM configurations + +**Subcommands:** `list`, `get`, `create`, `update`, `delete`, `enrollment-profiles`, `devices` + +**Flags:** + +- `--name` `` — MDM configuration name (create/update) +- `--org-name` `` — Organization name (create/update) + +### Apps & SSO + +#### `jc apps` + +Manage SSO applications + +**Subcommands:** `list`, `get`, `create`, `update`, `delete` + +**Flags:** + +- `--limit` `` — Maximum number of results (list) +- `--sort` `` — Sort field (list) +- `--filter` `` — Filter expressions (list) +- `--sso-type` `` — SSO type (create, required) +- `--config` `` — SSO-specific config as JSON (create/update) + +#### `jc saas-management` + +Manage JumpCloud SaaS Management applications + +**Subcommands:** `list`, `get`, `create`, `update`, `delete`, `accounts`, `account-get`, `account-delete`, `usage`, `licenses`, `catalog-get` + +**Flags:** + +- `--limit` `` — Maximum number of results (list) +- `--filter` `` — Filter expressions (list) +- `--catalog-app-id` `` — Catalog application ID (create) +- `--status` `` — App status: APPROVED, UNAPPROVED, IGNORED (create/update) +- `--access-restriction` `` — Access restriction: DEFAULT_ACTION, NO_ACTION, BLOCK, DISMISSIBLE_WARNING (create/update) +- `--account-id` `` — Account ID (account-get/account-delete) +- `--day-count` `` — Number of days of usage data (usage, default 30) + +#### `jc app-templates` + +View application templates + +**Subcommands:** `list`, `get` + +**Flags:** + +- `--limit` `` — Maximum number of results (list) +- `--sort` `` — Sort field (list) + +### Policies & Commands + +#### `jc commands` + +Manage JumpCloud commands + +**Subcommands:** `list`, `get`, `create`, `update`, `delete`, `run`, `results`, `trigger` + +**Flags:** + +- `--limit` `` — Maximum number of results (list) +- `--sort` `` — Sort field (list) +- `--filter` `` — Filter expressions (list) +- `--type` `` — Command type filter: linux, mac, windows (list) +- `--data` `` — JSON payload for trigger + +#### `jc policies` + +Manage JumpCloud policies + +**Subcommands:** `list`, `get`, `create`, `update`, `delete`, `results` + +**Flags:** + +- `--limit` `` — Maximum number of results (list) +- `--sort` `` — Sort field (list) +- `--filter` `` — Filter expressions (list) + +#### `jc policy-templates` + +View policy templates + +**Subcommands:** `list`, `get` + +**Flags:** + +- `--limit` `` — Maximum number of results (list) +- `--filter` `` — Filter expressions (list) + +#### `jc policy-groups` + +Manage policy groups + +**Subcommands:** `list`, `get`, `create`, `update`, `delete` + +**Flags:** + +- `--limit` `` — Maximum number of results (list) +- `--sort` `` — Sort field (list) +- `--filter` `` — Filter expressions (list) +- `--name` `` — Policy group name (create/update) +- `--description` `` — Policy group description (create/update) + +### Insights + +#### `jc insights` + +Query Directory Insights audit events + +**Subcommands:** `query`, `count`, `distinct`, `save`, `run`, `saved` + +**Flags:** + +- `--service` `` — Event service: sso, radius, ldap, user_portal, admin, mdm, directory, software, systems, password_manager, all +- `--last` `` — Time range: 24h, 7d, 30d, 1m +- `--start` `` — Start time (RFC 3339 or YYYY-MM-DD) +- `--end` `` — End time (RFC 3339 or YYYY-MM-DD) +- `--event-type` `` — Filter by event type +- `--limit` `` — Maximum events to return +- `--sort` `` — Sort field + +#### `jc system-insights` + +Query osquery system insight tables + +**Subcommands:** `list`, `tables` + +**Flags:** + +- `--system-id` `` — Filter by device hostname or ID +- `--filter` `` — Filter expressions +- `--limit` `` — Maximum number of results +- `--sort` `` — Sort field + +### Integrations + +#### `jc software` + +Manage JumpCloud software apps + +**Subcommands:** `list`, `get`, `create`, `update`, `delete`, `statuses`, `associations`, `reclaim-license` + +**Flags:** + +- `--limit` `` — Maximum number of results (list) +- `--sort` `` — Sort field (list) +- `--filter` `` — Filter expressions (list) +- `--name` `` — App display name (create/update) +- `--settings` `` — Package settings as raw JSON (create/update) + +#### `jc assets` + +Manage JumpCloud assets (devices, accessories, locations) + +**Subcommands:** `devices`, `accessories`, `locations` + +**Flags:** + +- `--limit` `` — Maximum number of results (list) +- `--field` `` — Set field value as 'Label=Value' (create/update) + +#### `jc ldap` + +Manage JumpCloud LDAP servers + +**Subcommands:** `list`, `get`, `create`, `update`, `delete`, `samba-domains`, `samba-domain-get`, `samba-domain-create`, `samba-domain-update`, `samba-domain-delete` + +**Flags:** + +- `--limit` `` — Maximum number of results (list) +- `--sort` `` — Sort field (list) +- `--filter` `` — Filter expressions (list) +- `--name` `` — LDAP server name (create/update) or samba domain workgroup name +- `--user-lockout-action` `` — Action on user lockout (create/update) +- `--user-password-expiration-action` `` — Action on password expiration (create/update) +- `--domain-id` `` — Samba domain ID (samba-domain-get/update/delete) +- `--sid` `` — Samba domain security identifier (samba-domain-create/update) + +#### `jc ad` + +Manage JumpCloud Active Directory integrations + +**Subcommands:** `list`, `get`, `create`, `update`, `delete` + +**Flags:** + +- `--limit` `` — Maximum number of results (list) +- `--sort` `` — Sort field (list) +- `--filter` `` — Filter expressions (list) +- `--domain` `` — AD domain name (create, required) +- `--use-case` `` — Integration use case (create/update) +- `--groups-enabled` `` — Enable group sync (update) + +#### `jc radius` + +Manage RADIUS servers + +**Subcommands:** `list`, `get`, `create`, `update`, `delete` + +**Flags:** + +- `--limit` `` — Maximum number of results (list) +- `--sort` `` — Sort field (list) +- `--filter` `` — Filter expressions (list) +- `--name` `` — RADIUS server name (create/update) +- `--shared-secret` `` — RADIUS shared secret (create, required) +- `--auth-port` `` (default `1812`) — Authentication port (create/update) +- `--accounting-port` `` (default `1813`) — Accounting port (create/update) + +#### `jc gsuite` + +Manage JumpCloud Google Workspace (G Suite) integrations + +**Subcommands:** `list`, `get`, `translation-rules`, `import-users` + +**Flags:** + +- `--limit` `` — Maximum number of results (list) +- `--sort` `` — Sort field (list) +- `--filter` `` — Filter expressions (list) + +#### `jc office365` + +Manage JumpCloud Office 365 integrations + +**Subcommands:** `list`, `get`, `translation-rules`, `import-users` + +**Flags:** + +- `--limit` `` — Maximum number of results (list) +- `--sort` `` — Sort field (list) +- `--filter` `` — Filter expressions (list) + +#### `jc duo` + +Manage JumpCloud Duo accounts and applications + +**Subcommands:** `list`, `get`, `create`, `delete`, `apps`, `app-get`, `app-create`, `app-delete` + +**Flags:** + +- `--name` `` — Duo account name (create) +- `--app-id` `` — Duo application ID (app-get, app-delete) +- `--api-host` `` — Duo API host (app-create) + +### Org & Lifecycle + +#### `jc access-requests` + +Manage JumpCloud temporary elevated device privilege requests + +**Subcommands:** `list`, `get`, `create`, `update`, `revoke` + +**Flags:** + +- `--limit` `` — Maximum number of results (list) +- `--filter` `` — Filter expressions (list) +- `--user` `` — User name or ID (create) +- `--device` `` — Device name or ID (create) +- `--expiry` `` — Expiry time RFC 3339 (create/update) +- `--sudo` `` — Enable sudo access (create) +- `--sudo-nopasswd` `` — Enable passwordless sudo (create) +- `--remarks` `` — Optional remarks (create/update) + +#### `jc org` + +View and update JumpCloud organization information + +**Subcommands:** `list`, `get`, `settings`, `update` + +**Flags:** + +- `--name` `` — Organization display name (update) +- `--settings-json` `` — Raw JSON for complex settings fields (update) + +#### `jc user-states` + +Manage scheduled user state transitions + +**Subcommands:** `list`, `create`, `get`, `delete` + +**Flags:** + +- `--user` `` — User name or ID (create, required) +- `--state` `` — Target state: suspended or activated (create, required) +- `--start-date` `` — Date for state change (create, required) +- `--end-date` `` — Optional end date to revert (create) + +#### `jc custom-emails` + +Manage custom email templates + +**Subcommands:** `templates`, `get`, `create`, `update`, `delete` + +**Flags:** + +- `--type` `` — Custom email type (create, required) +- `--subject` `` — Email subject line (create/update) +- `--title` `` — Email title (create/update) +- `--body` `` — Email body text (create/update) +- `--header` `` — Email header text (create/update) +- `--button` `` — Email button text (create/update) + +#### `jc graph` + +Manage JumpCloud resource associations + +**Subcommands:** `traverse`, `bind`, `unbind` + +**Flags:** + +- `--from` `` — Source: type:identifier (e.g. user:jdoe) +- `--to` `` — Target type: user, system, user_group, system_group, application, policy, command + +#### `jc bulk` + +Bulk operations from CSV files + +**Subcommands:** `users` + +**Flags:** + +- `--file` `` — Path to CSV file + +### AI & Automation + +#### `jc recipe` + +Manage and run automation recipes + +Recipes turn JumpCloud workflows into declarative, version-controlled YAML files instead of one-off shell scripts. Each step is a `jc` invocation with template variables (`{{ .param.user }}`, `{{ .step.created_user.id }}`), conditional execution based on prior step outputs, and structured output capture that downstream steps can reference — so a single recipe can create a user, add them to groups, assign devices, send a welcome email, and bail out cleanly if any step fails or a precondition isn't met. The `--plan` flag walks every step without mutation, surfacing exactly what would change before you commit; `--param k=v` makes the same recipe reusable across users, orgs, or MSP customers. Built-ins ship for the workflows admins write over and over (onboarding, offboarding, MFA-reset campaigns, quarterly compliance audits); custom recipes live in `~/.config/jc/recipes/` and can be authored in `$EDITOR` via the TUI (`jc tui` → recipes → `n`). Recipes are also a first-class MCP primitive — agents can invoke `recipe.run` as a single tool call instead of orchestrating five separate ones, dramatically reducing the surface area for LLM missteps on multi-step changes. + +**Subcommands:** `list`, `show`, `run`, `validate`, `create`, `import`, `export` + +**Flags:** + +- `--param` `` — Recipe parameters as key=value (run) +- `--file` `` — Output file path (export) + +#### `jc mcp` + +MCP server for AI agent integration + +Run the jc CLI as a Model Context Protocol (MCP) server, exposing JumpCloud resources as typed tools to MCP-aware clients (Claude Code, Claude Desktop, Cursor, and any other host that speaks MCP). Includes per-minute rate limiting, an optional `--read-only` mode that disables every mutation tool, and a step-up authentication flow (TTY prompt, Touch ID, or webhook) for high-impact operations. Transport is stdio by default — point your MCP client at `jc mcp serve` and the CLI's full surface area becomes available to the agent. + +**Subcommands:** `serve` + +**Flags:** + +- `--rate-limit` `` (default `60`) — Maximum tool calls per minute +- `--read-only` `` — Disable all mutation tools + +#### `jc schema` + +Machine-readable schema and command manifest + +Return machine-readable JSON for every JumpCloud resource type and the full CLI command tree. Designed for LLMs, IDE plugins, code generators, and the public showcase site itself — `jc schema resources` lists every resource with its API version and supported verbs, `jc schema ` returns the typed field list, and `jc schema commands` returns the full command manifest (paths, subcommands, flags, descriptions). The schema is the single source of truth — this site, the MCP server, and `jc ask` all read from it. + +**Subcommands:** `resources`, `commands` + +#### `jc ask` + +Translate natural language queries into jc CLI commands + +Translate plain-English questions into runnable `jc` commands using the bundled schema manifest plus an LLM prompt template. Useful for one-off queries like *"show me admins who haven't logged in for 90 days"* without remembering exact `jc insights query` flag syntax, or for *"suspend everyone in the contractors group"* without hand-piping `jc groups user list` into a `for` loop. The translated command is always printed for review before execution, and pairs naturally with `--plan` to preview any mutation before committing. + +#### `jc explain` + +Explain what a command would do without executing + +Describe what a `jc` invocation would do — in plain English — without running it. Useful for sanity-checking LLM-generated commands before execution, understanding an unfamiliar invocation copied from a runbook, or onboarding new admins. The explanation covers the action type, target resource, affected scope (single object vs. batch), and a reversibility warning for destructive operations (`delete`, `lock`, `erase`). + +### Setup + +#### `jc auth` + +Authentication commands + +Manage JumpCloud credentials and switch between organizations. The CLI supports two credential types: a static API key (read from `JC_API_KEY`, the config, or the system keychain), or an OAuth service account (client ID + secret), with automatic token refresh. Credentials can be stored as plaintext, in macOS Keychain / GNOME libsecret / Windows Credential Manager via a `keychain://` reference, or supplied per-invocation with `--api-key`. Multiple named profiles let MSPs and admins flip between orgs without re-authenticating (`jc auth switch `), and `jc auth status` reveals the active profile, credential type, fingerprint, and source. + +**Subcommands:** `login`, `logout`, `status`, `switch` + +#### `jc config` + +Configuration management + +View and update jc CLI configuration — default output format, color/pager behavior, TUI refresh interval, plan-mode safety toggles, cache TTLs, and dozens of other preferences. Settings live in `~/.config/jc/config.yaml` and can be overridden per-command via `JC_*` environment variables or flags (env > flag > config > built-in default). Use `jc config view` for the full effective configuration including override sources, or `jc config set ` to persist a change. + +**Subcommands:** `view`, `set` + +## Resource types + +Every resource has a JSON schema available via `jc schema `: + +- `access-requests` +- `ad` +- `admins` +- `app-templates` +- `apple-mdm` +- `apps` +- `assets` +- `auth-policies` +- `commands` +- `custom-emails` +- `devices` +- `duo` +- `groups` +- `gsuite` +- `identity-providers` +- `insights` +- `iplists` +- `ldap` +- `office365` +- `org` +- `policies` +- `policy-groups` +- `policy-templates` +- `radius` +- `saas-management` +- `software` +- `system-insights` +- `user-states` +- `users` diff --git a/docs/site/llms.txt b/docs/site/llms.txt new file mode 100644 index 0000000..2a2ae2a --- /dev/null +++ b/docs/site/llms.txt @@ -0,0 +1,78 @@ +# jc + +> JumpCloud CLI — manage users, devices, groups, policies, commands, insights, and more + +The jc CLI is a Go-based command-line tool for JumpCloud — managing users, devices, groups, policies, applications, commands, insights, and more. It exposes every JumpCloud resource through a consistent verb-noun grammar with structured JSON output, an MCP server for AI agents, and a recipe system for declarative automation. + +## Command catalog + +### Identity & Access + +- `jc users` — Manage JumpCloud system users +- `jc groups` — Manage user and device groups +- `jc admins` — Manage JumpCloud administrators +- `jc auth-policies` — Manage authentication policies for conditional access +- `jc iplists` — Manage IP lists for authentication policies +- `jc identity-providers` — Manage JumpCloud identity providers for SSO/OIDC federation + +### Devices & MDM + +- `jc devices` — Manage JumpCloud devices (systems) +- `jc apple-mdm` — Manage Apple MDM configurations + +### Apps & SSO + +- `jc apps` — Manage SSO applications +- `jc saas-management` — Manage JumpCloud SaaS Management applications +- `jc app-templates` — View application templates + +### Policies & Commands + +- `jc commands` — Manage JumpCloud commands +- `jc policies` — Manage JumpCloud policies +- `jc policy-templates` — View policy templates +- `jc policy-groups` — Manage policy groups + +### Insights + +- `jc insights` — Query Directory Insights audit events +- `jc system-insights` — Query osquery system insight tables + +### Integrations + +- `jc software` — Manage JumpCloud software apps +- `jc assets` — Manage JumpCloud assets (devices, accessories, locations) +- `jc ldap` — Manage JumpCloud LDAP servers +- `jc ad` — Manage JumpCloud Active Directory integrations +- `jc radius` — Manage RADIUS servers +- `jc gsuite` — Manage JumpCloud Google Workspace (G Suite) integrations +- `jc office365` — Manage JumpCloud Office 365 integrations +- `jc duo` — Manage JumpCloud Duo accounts and applications + +### Org & Lifecycle + +- `jc access-requests` — Manage JumpCloud temporary elevated device privilege requests +- `jc org` — View and update JumpCloud organization information +- `jc user-states` — Manage scheduled user state transitions +- `jc custom-emails` — Manage custom email templates +- `jc graph` — Manage JumpCloud resource associations +- `jc bulk` — Bulk operations from CSV files + +### AI & Automation + +- `jc recipe` — Manage and run automation recipes +- `jc mcp` — MCP server for AI agent integration +- `jc schema` — Machine-readable schema and command manifest +- `jc ask` — Translate natural language queries into jc CLI commands +- `jc explain` — Explain what a command would do without executing + +### Setup + +- `jc auth` — Authentication commands +- `jc config` — Configuration management + +## See also + +- [Full reference](llms-full.txt) +- [Machine-readable manifest](commands.json) +- [Source repository](https://github.com/TheJumpCloud/jc-cli) diff --git a/internal/schema/schema.go b/internal/schema/schema.go index ac30da0..ba3f756 100644 --- a/internal/schema/schema.go +++ b/internal/schema/schema.go @@ -43,9 +43,16 @@ type CommandManifest struct { } // CommandEntry describes a CLI command group with its subcommands and flags. +// +// Long is an optional multi-sentence elaboration shown on the showcase +// site and in llms-full.txt. It's deliberately hand-curated rather than +// pulled from Cobra's Long string — the audience here is "someone +// browsing the public catalog," which wants more context than `jc +// --help` typically gives. Leave empty if Description is sufficient. type CommandEntry struct { Path string `json:"path"` Description string `json:"description"` + Long string `json:"long,omitempty"` Subcommands []string `json:"subcommands,omitempty"` Flags []FlagEntry `json:"flags,omitempty"` } @@ -711,11 +718,13 @@ func BuildCommandManifest() CommandManifest { { Path: "jc auth", Description: "Authentication commands", + Long: "Manage JumpCloud credentials and switch between organizations. The CLI supports two credential types: a static API key (read from `JC_API_KEY`, the config, or the system keychain), or an OAuth service account (client ID + secret), with automatic token refresh. Credentials can be stored as plaintext, in macOS Keychain / GNOME libsecret / Windows Credential Manager via a `keychain://` reference, or supplied per-invocation with `--api-key`. Multiple named profiles let MSPs and admins flip between orgs without re-authenticating (`jc auth switch `), and `jc auth status` reveals the active profile, credential type, fingerprint, and source.", Subcommands: []string{"login", "logout", "status", "switch"}, }, { Path: "jc config", Description: "Configuration management", + Long: "View and update jc CLI configuration — default output format, color/pager behavior, TUI refresh interval, plan-mode safety toggles, cache TTLs, and dozens of other preferences. Settings live in `~/.config/jc/config.yaml` and can be overridden per-command via `JC_*` environment variables or flags (env > flag > config > built-in default). Use `jc config view` for the full effective configuration including override sources, or `jc config set ` to persist a change.", Subcommands: []string{"view", "set"}, }, { @@ -1076,6 +1085,7 @@ func BuildCommandManifest() CommandManifest { { Path: "jc recipe", Description: "Manage and run automation recipes", + Long: "Recipes turn JumpCloud workflows into declarative, version-controlled YAML files instead of one-off shell scripts. Each step is a `jc` invocation with template variables (`{{ .param.user }}`, `{{ .step.created_user.id }}`), conditional execution based on prior step outputs, and structured output capture that downstream steps can reference — so a single recipe can create a user, add them to groups, assign devices, send a welcome email, and bail out cleanly if any step fails or a precondition isn't met. The `--plan` flag walks every step without mutation, surfacing exactly what would change before you commit; `--param k=v` makes the same recipe reusable across users, orgs, or MSP customers. Built-ins ship for the workflows admins write over and over (onboarding, offboarding, MFA-reset campaigns, quarterly compliance audits); custom recipes live in `~/.config/jc/recipes/` and can be authored in `$EDITOR` via the TUI (`jc tui` → recipes → `n`). Recipes are also a first-class MCP primitive — agents can invoke `recipe.run` as a single tool call instead of orchestrating five separate ones, dramatically reducing the surface area for LLM missteps on multi-step changes.", Subcommands: []string{"list", "show", "run", "validate", "create", "import", "export"}, Flags: []FlagEntry{ {Name: "param", Type: "string[]", Description: "Recipe parameters as key=value (run)"}, @@ -1085,6 +1095,7 @@ func BuildCommandManifest() CommandManifest { { Path: "jc mcp", Description: "MCP server for AI agent integration", + Long: "Run the jc CLI as a Model Context Protocol (MCP) server, exposing JumpCloud resources as typed tools to MCP-aware clients (Claude Code, Claude Desktop, Cursor, and any other host that speaks MCP). Includes per-minute rate limiting, an optional `--read-only` mode that disables every mutation tool, and a step-up authentication flow (TTY prompt, Touch ID, or webhook) for high-impact operations. Transport is stdio by default — point your MCP client at `jc mcp serve` and the CLI's full surface area becomes available to the agent.", Subcommands: []string{"serve"}, Flags: []FlagEntry{ {Name: "rate-limit", Type: "int", Default: "60", Description: "Maximum tool calls per minute"}, @@ -1094,15 +1105,18 @@ func BuildCommandManifest() CommandManifest { { Path: "jc schema", Description: "Machine-readable schema and command manifest", + Long: "Return machine-readable JSON for every JumpCloud resource type and the full CLI command tree. Designed for LLMs, IDE plugins, code generators, and the public showcase site itself — `jc schema resources` lists every resource with its API version and supported verbs, `jc schema ` returns the typed field list, and `jc schema commands` returns the full command manifest (paths, subcommands, flags, descriptions). The schema is the single source of truth — this site, the MCP server, and `jc ask` all read from it.", Subcommands: []string{"resources", "commands"}, }, { Path: "jc ask", Description: "Translate natural language queries into jc CLI commands", + Long: "Translate plain-English questions into runnable `jc` commands using the bundled schema manifest plus an LLM prompt template. Useful for one-off queries like *\"show me admins who haven't logged in for 90 days\"* without remembering exact `jc insights query` flag syntax, or for *\"suspend everyone in the contractors group\"* without hand-piping `jc groups user list` into a `for` loop. The translated command is always printed for review before execution, and pairs naturally with `--plan` to preview any mutation before committing.", }, { Path: "jc explain", Description: "Explain what a command would do without executing", + Long: "Describe what a `jc` invocation would do — in plain English — without running it. Useful for sanity-checking LLM-generated commands before execution, understanding an unfamiliar invocation copied from a runbook, or onboarding new admins. The explanation covers the action type, target resource, affected scope (single object vs. batch), and a reversibility warning for destructive operations (`delete`, `lock`, `erase`).", }, }, } From e479d433f71ae2e596b2da12dd523319c3c98b26 Mon Sep 17 00:00:00 2001 From: Juergen Klaassen Date: Fri, 12 Jun 2026 12:29:04 -0600 Subject: [PATCH 2/2] fix(site): unify search predicate across cards/sidebar + index long + global flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cursor Bugbot review on PR #46 flagged three filter gaps: 1. Sidebar links tested `dataset.path` while cards tested a full haystack — searching `mfa` (a subcommand on jc users) showed the card but hid its sidebar link. Both now resolve the card by id and use the same haystack, so the two panes can never disagree. 2. The new `cmd.long` paragraphs weren't indexed — queries like `keychain`, `Touch ID`, `webhook` didn't surface the enriched cards even though those tokens lived in the elaborations. 3. `manifest.global_flags` (--plan, --output, --quiet, etc.) wasn't in any haystack. Added a synthetic "Global flags" card at the top of the catalog with its own Reference sidebar entry, so global flags are both findable via search and discoverable while browsing. Also extended the per-card haystack to include each flag's shorthand and default value while I was in there — searches for `-o` or `60` (the MCP rate-limit default) now hit too. Refs KLA-442, addresses Bugbot review on PR #46 Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/site/index.html | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/docs/site/index.html b/docs/site/index.html index dcc492b..79c971f 100644 --- a/docs/site/index.html +++ b/docs/site/index.html @@ -275,7 +275,33 @@

jc CLI

byCategory.set(cmd.category, arr); } + // Synthetic "Global flags" entry so flags like --plan / --output are + // searchable and discoverable from the sidebar. Treat it as its own + // top-of-sidebar reference section, separate from command categories. + const globalEntry = { + path: 'Global flags', + description: 'Flags accepted by every jc subcommand.', + long: 'These flags are accepted by every `jc` subcommand (e.g. `--plan` for safe previews, `-o json` for structured output, `--quiet` for exit-code-only). Per-command flags shown on each card stack on top of these.', + category: 'Reference', + flags: manifest.global_flags || [], + }; + const nav = document.getElementById('nav'); + const refHeading = document.createElement('p'); + refHeading.className = 'cat'; + refHeading.textContent = 'Reference'; + nav.appendChild(refHeading); + const refList = document.createElement('ul'); + const refLi = document.createElement('li'); + const refLink = document.createElement('a'); + refLink.className = 'cmd-link'; + refLink.href = '#' + slug(globalEntry.path); + refLink.textContent = globalEntry.path; + refLink.dataset.path = globalEntry.path; + refLi.appendChild(refLink); + refList.appendChild(refLi); + nav.appendChild(refList); + for (const [cat, cmds] of byCategory) { if (cmds.length === 0) continue; const h = document.createElement('p'); @@ -297,6 +323,7 @@

jc CLI

} const cards = document.getElementById('cards'); + cards.appendChild(renderCard(globalEntry)); for (const cmd of manifest.commands) { cards.appendChild(renderCard(cmd)); } @@ -311,9 +338,15 @@

jc CLI

card.hidden = !hit; if (hit) shown++; } + // Use the card's haystack — same predicate as above — so the sidebar + // can't disagree with the card area. Pre-fix the sidebar tested only + // `dataset.path`, so a query like `mfa` (a subcommand on `jc users`) + // would show the card and hide its sidebar link, or vice versa. for (const link of nav.querySelectorAll('a.cmd-link')) { - const path = link.dataset.path.toLowerCase(); - link.hidden = q && !path.includes(q); + const id = link.getAttribute('href').slice(1); + const card = document.getElementById(id); + const hit = !q || (card && card.dataset.haystack.includes(q)); + link.hidden = !hit; } empty.hidden = shown > 0; }); @@ -387,9 +420,9 @@

jc CLI

} const haystack = [ - cmd.path, cmd.description, cmd.category, + cmd.path, cmd.description, cmd.long || '', cmd.category, ...(cmd.subcommands || []), - ...(cmd.flags || []).flatMap(f => [f.name, f.description]), + ...(cmd.flags || []).flatMap(f => [f.name, f.description, f.shorthand || '', f.default || '']), ].join(' ').toLowerCase(); card.dataset.haystack = haystack;