{"id":51197254,"url":"https://github.com/pbv7/wsectl","last_synced_at":"2026-06-27T21:31:38.173Z","repository":{"id":362991377,"uuid":"1261554950","full_name":"pbv7/wsectl","owner":"pbv7","description":"Unofficial command-line client for Worksection","archived":false,"fork":false,"pushed_at":"2026-06-16T11:31:50.000Z","size":404,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-16T12:22:49.430Z","etag":null,"topics":["cli","cli-tool","worksection"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pbv7.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"docs/security.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-06T21:09:02.000Z","updated_at":"2026-06-16T11:31:52.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/pbv7/wsectl","commit_stats":null,"previous_names":["pbv7/wsectl"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/pbv7/wsectl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pbv7%2Fwsectl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pbv7%2Fwsectl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pbv7%2Fwsectl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pbv7%2Fwsectl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pbv7","download_url":"https://codeload.github.com/pbv7/wsectl/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pbv7%2Fwsectl/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34869004,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-27T02:00:06.362Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","cli-tool","worksection"],"created_at":"2026-06-27T21:31:37.737Z","updated_at":"2026-06-27T21:31:38.166Z","avatar_url":"https://github.com/pbv7.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# wsectl\n\n[![CI](https://github.com/pbv7/wsectl/actions/workflows/ci.yml/badge.svg)](https://github.com/pbv7/wsectl/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/pbv7/wsectl/branch/main/graph/badge.svg)](https://codecov.io/gh/pbv7/wsectl)\n[![Go Reference](https://pkg.go.dev/badge/github.com/pbv7/wsectl.svg)](https://pkg.go.dev/github.com/pbv7/wsectl)\n[![Go Report Card](https://goreportcard.com/badge/github.com/pbv7/wsectl)](https://goreportcard.com/report/github.com/pbv7/wsectl)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n\nUnofficial command-line client for Worksection.\n\n`wsectl` is a Go CLI for read-only Worksection access. It is built for humans at a terminal and for scripts or coding agents that need stable JSON,\npredictable exit codes, explicit account profiles, and safe credential handling.\n\nThis project is not affiliated with, endorsed by, or supported by Worksection.\n\n## Status\n\nThe MVP is read-only by design. Commands that would change Worksection data are recognized and blocked with a clear error. The code is structured so\nwrite commands can be added later behind explicit safety decisions.\n\n## Start Here For Agents\n\nRun the guide compiled into the installed binary before constructing commands:\n\n```bash\nwsectl help agent --full\n```\n\nFor machine-readable discovery:\n\n```bash\nwsectl help agent --full --json\nwsectl commands --json\nwsectl api actions --json\nwsectl api schema get_projects --json\nwsectl doctor --json\n```\n\nThe help JSON includes `guide_format_version`. `commands --json` reports explicit category, Worksection actions, output modes, authentication\nrequirements, read-only status, examples, and agent notes. `api schema ACTION --json` reports the static action contract, including parameters,\nresponse shape, known fields, conditional `extra` fields, count path, OAuth scopes, and compatibility notes.\n\n## Install\n\nHomebrew (macOS/Linux):\n\n```bash\nbrew install pbv7/tap/wsectl\n```\n\nFrom source:\n\n```bash\ngo install github.com/pbv7/wsectl/cmd/wsectl@latest\n```\n\nFor local development:\n\n```bash\ngit clone https://github.com/pbv7/wsectl\ncd wsectl\ngo run ./cmd/wsectl\n```\n\n`go run` is useful for help and development checks. For keyring-backed OAuth login, prefer a stable binary built with `make build` or an installed\n`wsectl`; temporary `go run` binaries can confuse OS keychain access control on macOS.\n\n## Quick Start\n\nCreate a profile, log in with OAuth, then use JSON output for automation:\n\n```bash\nwsectl profiles add default --account-url https://company.worksection.com --auth-type oauth2\n# Set WSECTL_CLIENT_ID and WSECTL_CLIENT_SECRET in your shell first.\nwsectl auth login --client-id \"$WSECTL_CLIENT_ID\"\nwsectl doctor --api\nwsectl projects list --json\nwsectl tasks search --query \"invoice\" --json\n```\n\n`wsectl auth login` starts a temporary local HTTPS callback server, opens the browser, validates OAuth state, exchanges the returned code, and stores\ntokens in the selected secret store.\n\nFor a remote shell or headless session, keep the callback server active and open the URL manually:\n\n```bash\n# Set WSECTL_CLIENT_ID and WSECTL_CLIENT_SECRET in your shell first.\nwsectl auth login --no-browser --client-id \"$WSECTL_CLIENT_ID\"\n```\n\nFor CI, use environment credentials:\n\n```bash\nWSECTL_ACCOUNT_URL=https://company.worksection.com \\\nWSECTL_ACCESS_TOKEN=... \\\nwsectl me --json\n```\n\n## Persistent Desktop Setup\n\nFor regular use on a personal workstation, prefer a config profile plus OS keychain secrets instead of exporting tokens every time. See\n[Configuration](docs/configuration.md) for the full desktop, admin-token, encrypted-file, and CI workflows.\n\n```bash\nwsectl profiles add default \\\n  --account-url https://company.worksection.com \\\n  --auth-type oauth2\n\nwsectl profiles use default\n\n# Set WSECTL_CLIENT_ID and WSECTL_CLIENT_SECRET in your shell first.\nwsectl auth login --client-id \"$WSECTL_CLIENT_ID\"\n\nwsectl doctor --api\nwsectl me --json\n```\n\n`profiles add` writes non-secret account settings to `config.toml`. When `--secret-ref` is omitted, the profile uses `keyring:wsectl/PROFILE`, so\n`auth login` stores OAuth tokens and client credentials in the OS keychain. After this setup, normal commands do not need `WSECTL_ACCESS_TOKEN`,\n`WSECTL_ACCOUNT_URL`, or other credential environment variables.\n\nDefault config locations:\n\n- macOS/Linux: `~/.config/wsectl/config.toml` unless `$XDG_CONFIG_HOME` is set.\n- Windows: `%AppData%\\wsectl\\config.toml`.\n\nOptional command history is available as a local JSONL file. It is disabled by default and never writes logs to stdout:\n\n```bash\nWSECTL_HISTORY=1 wsectl projects list --json\nwsectl history path --json\nwsectl history list --json --limit 20\nwsectl history clear --keep 1000\n```\n\nFor containers, mount state explicitly:\n\n```bash\nWSECTL_HISTORY=1 WSECTL_HISTORY_FILE=/state/history.jsonl wsectl doctor --api --json\n```\n\nUse separate profiles for separate accounts or auth modes:\n\n```bash\nwsectl profiles add client-a --account-url https://client-a.worksection.com --auth-type oauth2\nwsectl profiles add client-b --account-url https://client-b.worksection.com --auth-type oauth2\nwsectl --profile client-a projects list --json\nwsectl profiles use client-b\n```\n\nFor an admin API key, keep a separate profile:\n\n```bash\nwsectl profiles add admin \\\n  --account-url https://company.worksection.com \\\n  --auth-type admin_token \\\n  --secret-ref keyring:wsectl/admin\n\n# Set WSECTL_ADMIN_TOKEN in your shell first.\nwsectl --profile admin auth login\nwsectl --profile admin doctor --api\n```\n\nFor a portable encrypted secret file instead of the OS keychain:\n\n```bash\nexport WSECTL_SECRET_PASSPHRASE=\"use-a-password-manager-value\"\n\nwsectl profiles add portable \\\n  --account-url https://company.worksection.com \\\n  --auth-type oauth2 \\\n  --secret-ref encrypted-file:$HOME/.config/wsectl/secrets/portable.json\n\n# Set WSECTL_CLIENT_ID and WSECTL_CLIENT_SECRET in your shell first.\nwsectl --profile portable auth login --client-id \"$WSECTL_CLIENT_ID\"\n```\n\nThe encrypted file is useless without `WSECTL_SECRET_PASSPHRASE`; do not store that passphrase next to the encrypted file.\n\n## Common Reads\n\n```bash\nwsectl me --json\nwsectl users list --json\nwsectl projects list --status active --extra text,options,users --json\nwsectl projects get 123 --extra text,users --json\nwsectl tasks all --extra text,files --json --out /tmp/tasks.json\nwsectl tasks list --project 123 --status active --extra text,comments --json\nwsectl tasks search --query \"invoice\" --json\nwsectl comments list 456 --extra files --json\nwsectl costs total --project 123 --start 01.05.2026 --end 31.05.2026 --json\nwsectl files download 789 --out ./attachment.bin\n```\n\nUse the lower-level API escape hatch when a read-only action is not wrapped by a first-class command:\n\n```bash\nwsectl api call get_users_schedule \\\n  --param datestart=01.05.2026 \\\n  --param dateend=31.05.2026 \\\n  --json\n```\n\n## Profiles\n\nProfiles keep account and credential references separate:\n\n```bash\nwsectl profiles add default --account-url https://company.worksection.com --auth-type oauth2\nwsectl profiles add admin --account-url https://company.worksection.com --auth-type admin_token\nwsectl profiles use default\nwsectl profiles list --json\nwsectl profiles show default --json\n```\n\nUse `--profile NAME` when a command must target a specific account:\n\n```bash\nwsectl --profile default projects list --json\n```\n\nConfig management commands:\n\n```bash\nwsectl profiles list --json\nwsectl profiles show default --json\nwsectl profiles use default\nwsectl auth status --json\nwsectl auth refresh\nwsectl auth logout\nwsectl profiles remove old-profile\n```\n\nUse `--config PATH` or `WSECTL_CONFIG` for a non-default config file. Use `WSECTL_PROFILE` or `--profile NAME` to override `current_profile` without\nediting config.\n\n## Output For Scripts\n\nPrefer JSON or NDJSON:\n\n```bash\nwsectl users list --json\nwsectl tasks all --extra text,files --ndjson\nwsectl projects list --json --jq '.data[] | {id, name, status}'\nwsectl tasks all --json --fields id,name,status --out /tmp/tasks.json\nwsectl tasks search --schema --json\n```\n\nDo not parse table output in scripts. Table output is for humans and may change to improve readability. JSON envelope fields are intended to stay\nstable:\n\n```json\n{\n  \"status\": \"ok\",\n  \"data\": [],\n  \"meta\": {\n    \"action\": \"get_tasks\",\n    \"profile\": \"default\",\n    \"account_url\": \"https://company.worksection.com\",\n    \"contract_version\": \"2026-06-07.1\",\n    \"response_shape\": \"array\",\n    \"count\": 0,\n    \"truncated\": false,\n    \"warnings\": []\n  }\n}\n```\n\nAlways check `meta.truncated` and `meta.warnings` for large responses.\n\n`--schema --json` on first-class read commands returns the same static action contract without loading config, opening the keychain, or calling\nWorksection. These contracts are advisory and agent-readable; they are not full JSON Schema and do not guarantee that every field appears in every\naccount.\n\n## Security Model\n\nSecrets are accessed through a `SecretStore` abstraction:\n\n- `keyring`: default OS keychain backend. `wsectl` enables OS-backed keychains and Pass, and intentionally disables the 99designs File and KeyCtl\n  backends.\n- `env`: read-only backend for CI and containers.\n- `encrypted-file`: explicit portable fallback protected by `WSECTL_SECRET_PASSPHRASE` with versioned Argon2id/AES-GCM payloads.\n- `plaintext`: explicit opt-in only.\n\nCommands do not print tokens by default. Use environment credentials for ephemeral automation and OS keychain storage for interactive use.\n\nFile downloads only forward bearer credentials to HTTPS URLs on the configured Worksection account host. If Worksection returns a cross-host file URL,\n`wsectl` fails closed with a structured `download_host_mismatch` error instead of leaking credentials.\n\n## Worksection API Limits\n\nWorksection documents these API constraints:\n\n- 1 request per second.\n- GET URL length limit of 8 kB.\n- Some endpoints can return at most 10,000 records.\n- Some long text fields can be shortened by the server.\n\n`wsectl` uses Worksection's documented/examples-compatible `POST` requests with API parameters in the query string and enforces client-side rate\nlimiting by default, but it cannot remove server-side limits. Very long filters can still hit request URL limits.\n\n## API Compatibility Notes\n\nOfficial Worksection web docs, official Postman collections, and live API behavior can disagree. `wsectl` treats Postman/live behavior as\nauthoritative for wire behavior and documents known normalizations.\n\n- First-class commands may expose human-readable values while sending Worksection's raw API value. For example,\n  `wsectl projects list --status archived` sends `filter=archive`.\n- Low-level `api call` uses raw API parameter names and values. For archived projects, use\n  `wsectl api call get_projects --param filter=archive --json`.\n- `wsectl tasks search --query TEXT` sends an escaped Worksection `filter`, not an undocumented `search` parameter.\n- `wsectl costs ... --timer true` sends `is_timer=true`.\n- `wsectl files images` filters client-side after `get_files`.\n- `wsectl costs list` returns the cost entries as a JSON array at `data`, with the server-side summary in `meta.aggregate`. `wsectl costs total`\n  returns its aggregate bundle (`total`, plus optional `projects`/`tasks` with `--extra`) as a plain object at `data`.\n- `wsectl projects events --period` accepts `\u003cN\u003em|h|d` — minutes, hours, or days (e.g. `30m`, `24h`, `7d`). Weeks, bare numbers, and words are\n  rejected client-side; the server additionally caps the value per unit.\n- `wsectl tasks search` needs at least one search dimension: `--query`, `--filter`, `--project`, `--task`, `--assignee`, or `--author`.\n  `--status` and `--extra` are modifiers, not search criteria, and fail on their own.\n- `wsectl tasks search --filter` supports `name` and the date fields `dateadd`, `datestart`, `dateend`, `dateclose` (the response-field forms\n  `date_added`/`date_closed` are also accepted) with operators `has`, `\u003c`, `\u003e`, and `and` — enabling server-side date-range filtering, e.g.\n  `--filter \"dateadd \u003e '01.06.2026' and dateadd \u003c '12.06.2026'\"`. Unsupported fields such as `status`, `tag`, or `priority` make the server reject the\n  filter with a misleading `Field is required: filter`.\n- Subtasks: `wsectl tasks list` / `wsectl tasks all` (`get_tasks`/`get_all_tasks`) return **top-level rows only**; `--extra subtasks` attaches a\n  `child` array of **stubs** (`{id, name, page, priority, status}`) to rows that have subtasks. `wsectl tasks search` returns the **flat** set of\n  matching tasks **including subtasks by default** — each subtask is a full first-class task row with its own `status`/assignee/`date_end` and a\n  `parent` object (`extra=subtasks` is a no-op on search). Use `wsectl tasks search --top-level-only` to drop subtasks (client-side; not combinable\n  with `--raw`). For a subtask's full data, read the flat `search` rows or `wsectl tasks get \u003csubtask_id\u003e`.\n- Worksection value shapes to expect: `priority` is a string (e.g. `\"5\"`); `tags` is a `{id: name}` map and appears only on `tasks get`; an unassigned\n  task has `user_to = {\"id\": \"1\", \"email\": \"NOONE\", \"name\": \"...\"}` (sometimes labeled \"Anyone\") — treat id `1` as unassigned. A task's\n  `date_start`/`date_end`/`date_closed` are present only when that date is set.\n- Live probes showed query-parameter POST works, JSON request bodies work for selected OAuth API reads, and `application/x-www-form-urlencoded` API\n  bodies return `invalid JSON`. This build does not use form-encoded API bodies.\n\n## Runtime Help\n\n`wsectl`, `wsectl --help`, and `wsectl help` print the same deterministic start screen. Subcommand help remains command-specific.\n\n```bash\nwsectl\nwsectl help agent --full\nwsectl doctor\nwsectl doctor --api --json\nwsectl help completion\nwsectl help output\nwsectl commands --json\nwsectl api actions --json\n```\n\n## Shell Completion\n\nCompletion scripts are generated by Cobra from the current command tree, so they stay aligned with new commands, flags, and enum completions when the\nbinary is updated.\n\n```bash\nwsectl completion bash\nwsectl completion zsh\nwsectl completion fish\nwsectl completion powershell\n```\n\nExamples:\n\n```bash\nwsectl completion zsh \u003e \"${fpath[1]}/_wsectl\"\nwsectl completion fish \u003e ~/.config/fish/completions/wsectl.fish\nwsectl completion powershell \u003e wsectl.ps1\n```\n\n## Repository Docs\n\n- [Manual](docs/manual.md)\n- [Agent usage](docs/agent-usage.md)\n- [Auth](docs/auth.md)\n- [Configuration](docs/configuration.md)\n- [Doctor](docs/doctor.md)\n- [Shell completion](docs/completion.md)\n- [Output contracts](docs/output-contracts.md)\n- [Recipes](docs/recipes.md)\n- [API coverage](docs/api-coverage.md)\n- [Security](docs/security.md)\n- [Release](docs/release.md)\n- [Generated command reference](docs/command-reference.md)\n- [Contributing](CONTRIBUTING.md)\n\n## Development\n\n```bash\nmake version\nmake check\nmake ci\nmake lint-md\nmake lint-workflows\nmake coverage\nmake coverage-check\nmake coverage-html\nmake build\nmake run ARGS=\"help agent\"\nmake build-all\nmake docs\nmake clean\n```\n\n`make build` and `make install` inject version, commit, and build date into `wsectl version`. `make build-all` creates local Linux, macOS, and\nWindows development binaries under `dist/`; release archives are still produced by GoReleaser.\n\nDevelopment-only lint tools are tracked in package-manager manifests: `actionlint` is declared as a Go tool in `go.mod`, and `markdownlint-cli2` is\ndeclared in `package-lock.json` for reproducible Markdown linting.\n\n`make coverage-check` uses POSIX shell tools and is intended for Unix-like developer environments. Windows CI runs direct Go commands for\ncross-platform coverage of the source.\n\nBefore tagging a release, run the end-to-end live probe against a real read-only Worksection account:\n\n```bash\nmake live-probe\n```\n\nThe live probe builds `dist/wsectl` and exercises the real CLI flow: doctor, identity, projects, tasks, comments, files, downloads, output modes,\nschema discovery, `api call`, and negative exit-code cases. See [Release](docs/release.md) for pinned project/task/file overrides and release\nacceptance notes.\n\nOptional live smoke tests are disabled by default:\n\n```bash\nWSECTL_LIVE_TESTS=1 \\\nWSECTL_TEST_ACCOUNT_URL=https://company.worksection.com \\\nWSECTL_TEST_ACCESS_TOKEN=... \\\ngo test ./internal/worksection -run LiveSmoke\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpbv7%2Fwsectl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpbv7%2Fwsectl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpbv7%2Fwsectl/lists"}