Commit Graph

13968 Commits

Author SHA1 Message Date
Tink bot f04930137e test(veans): pin runUpdate's call-order invariants
The two ordering rules in commands/update.go::runUpdate aren't enforced
by anything beyond the lines being written in that sequence:

  1. MoveTaskToBucket runs AFTER UpdateTask, so a status transition
     doesn't clobber freshly attached labels.
  2. The scrapped-reason comment posts BEFORE the bucket move, so the
     audit trail reads chronologically.

Both are documented in CLAUDE.md but neither is exercised by the e2e
suite: TestUpdate_DescriptionReplaceUniqueness is the only update-side
e2e and it only covers --description-replace-old/new.

Add two unit tests that drive runUpdate against an httptest.Server and
assert the exact (method, path) sequence. Sanity-checked locally by
swapping the field-update and bucket-move blocks — both tests fail with
a clear order diff, confirming they catch the regression that's most
likely to slip through review.
2026-05-27 08:21:57 +00:00
Tink bot ba6615f378 feat(veans): warn when Chain.Set falls back past a failed backend
A keyring transient failure on Set silently falls through to the file
backend today, which leaves a stale keyring entry from any prior
successful write shadowing the new file-backend token. Fixing the
shadow itself is deferred (would need a Set-and-Delete coordination,
or a stricter contract).

What we can do cheaply: surface the fallback so an operator hitting
the shadow has a breadcrumb. On Chain.Set fallthrough past a writable
backend that errored, print:

  veans: credential store: keyring rejected write (X); falling back to file

The warning goes to stderr (not the structured envelope — Set still
returns nil because the write landed somewhere). Env-backend's
read-only skip is unchanged and silent.

ChainStderr is exposed as a package var so tests can capture/assert
the warning when we backfill credential-store coverage.
2026-05-27 08:21:57 +00:00
Tink bot 75e546f0c1 feat(veans): make the HTTP client timeout configurable via .veans.yml
The 30s timeout on the client.New HTTPClient was hard-coded and
opaque. Long-running paginated reads against slow networks were
tripping it with no escape hatch.

Lift the value into a named constant and let .veans.yml override it
via a new optional http_timeout field (Go duration syntax, e.g.
"60s", "5m"). The field has omitempty so a freshly-written
.veans.yml from `veans init` doesn't surface the knob — operators
who need to tune it can hand-edit, but it stays out of the way for
the common case.

Runtime loader applies the override after client.New if set;
bootstrap- and login-time clients (built before .veans.yml exists)
keep the default.
2026-05-27 08:21:57 +00:00
Tink bot c4a0575305 feat(veans): offer "create a new project" from init's picker
The project picker used to require at least one pre-existing project
and would otherwise hard-error: "no projects visible to this user —
create one in the Vikunja UI first". Now it always offers an extra
numbered entry "Create a new project" and, when the user picks it,
prompts for a title (required) + identifier (optional). Empty-list
case routes straight to creation.

Backed by a new client.CreateProject(ctx, *Project) method (`PUT
/projects`); the e2e harness now uses that instead of the raw c.Do
call it did before.

Also fixed a latent bufio bug in StdPrompter.ReadLine that this work
surfaced: every call created a fresh bufio.Reader, which read-ahead a
buffer and threw it away on return. Second+ prompts read empty. Reuse
one buffered reader on the StdPrompter instance.
2026-05-27 08:21:57 +00:00
Tink bot 9b8ad4d027 feat(veans): URL discovery on init, port of the frontend's heuristic
The previous init flow took whatever the user typed for --server and
called GET <url>/api/v1/info on it. If the user typed
"vikunja.example.com" (no scheme), or pasted the URL with /api/v1 in
it (double-suffix), or pointed at a localhost install on the default
:3456 port without typing the port, we'd hand back a raw HTTP error.

New `client.DiscoverServer` ports the frontend's
helpers/checkAndSetApiUrl.ts discovery: probe a small ordered set of
plausible bases for /api/v1/info, return the first one that returns
parseable Info. Candidate order:

  1. scheme://host[:port]/path           (as the user typed it)
  2. scheme://host:3456/path             (default API port)
  3. opposite scheme of (1)
  4. opposite scheme of (2)

Heuristics:
- Missing scheme → https for public hosts, http for localhost /
  127.0.0.1 / [::1] (matches most CLIs' behaviour)
- Trailing /api/v1 from a pasted URL is stripped before probing, so
  we don't double up to /api/v1/api/v1/info
- Trailing slashes normalized

Errors now list everything we tried + the last underlying network
error, so the user can see why a URL failed instead of just
"GET /info: connection refused":

  veans: VALIDATION_ERROR: couldn't find a Vikunja instance reachable
    from "vikunja.example.com" — tried:
    - https://vikunja.example.com/api/v1/info
    - https://vikunja.example.com:3456/api/v1/info
    - http://vikunja.example.com/api/v1/info
    - http://vikunja.example.com:3456/api/v1/info
    last error: dial tcp: lookup vikunja.example.com: no such host

bootstrap.Init now defers URL canonicalisation to DiscoverServer and
caches the matched info from the probe (no second /info round-trip).

Unit tests cover the candidate-builder across the common shapes:
bare hostname, localhost, /api/v1-suffixed paste, explicit port,
subpath install, 127.0.0.1:3456, trailing slash. e2e green.
2026-05-27 08:21:57 +00:00
Tink bot 814b2a635f feat(veans): install agent hooks during init instead of just printing
Adds a final step to bootstrap.Init that offers to wire `veans prime`
into Claude Code and OpenCode automatically. Per-agent yes/no prompts
default to "yes" for Claude Code and "no" for OpenCode; --install-claude
/ --install-opencode flags skip the prompt for scripted contexts;
--no-hooks falls back to the previous behaviour of just printing the
snippets.

Claude Code:
  - Writes/merges .claude/settings.json
  - JSON merge preserves existing keys (model, permissions, other hooks)
    and only appends a `veans prime` command entry under SessionStart
    and PreCompact if one isn't already there
  - Idempotent: re-running reports "Already configured" without
    duplicating entries

OpenCode:
  - Writes .opencode/plugin/veans-prime.ts with the standard handler
    skeleton
  - Existing files are left alone (no TS-merge story for v0)

Failures during hook install are non-fatal: the repo is already
configured, so the user gets a warning + the printed snippets as a
fallback path.

Unit tests cover the merge logic (fresh file, idempotent rerun,
preserving user's other hooks/keys), the install actions
("Wrote"/"Updated"/"Already configured"), and the offer flow
(flags-bypass-prompt vs prompt-when-unset vs no-hooks).
2026-05-27 08:21:57 +00:00
Tink bot 1bc3afa430 feat(veans): match existing bucket titles via case-insensitive alias table 2026-05-27 08:21:57 +00:00
Tink bot 4ac89741e3 feat(veans): reuse owned bot or prompt for fresh name on collision 2026-05-27 08:21:57 +00:00
Tink bot cd7cc113a1 docs(veans): AGENTS.md cheat sheet for coding agents
Captures the non-obvious things an agent will hit working on this
submodule:

- Wire-format quirks (view_kind/bucket_configuration_mode are JSON
  strings; Task.BucketID is always 0 in GET — use ?expand=buckets and
  CurrentBucketID; POST /tasks doesn't move buckets, use the dedicated
  bucket-tasks endpoint; bot creation is at /user/bots; APIToken
  expires_at is required, use FarFuture for "no expiry").
- Permission discovery via /routes (group names are path-derived; use
  PermissionsForBot at runtime instead of hard-coding).
- OAuth shape (PKCE/S256 mandatory, no client registration, JSON-only
  token exchange, loopback redirect via 127.0.0.1:0, Shutdown uses
  context.WithoutCancel to drain on outer cancel).
- Credential chain order + per-test HOME/XDG override.
- Identifier validation (runelength only) + base-36 timestamp suffix
  trick for unique e2e identifiers.
- mage Aliases map (without it, `mage test` rejects the namespace).
- License-header enforcement via local .golangci.yml + code-header-
  template.txt copy.
- Things to actively avoid: bare exec.Command, committing the built
  binary, stdout from `prime` outside a configured workspace.

CLAUDE.md is a symlink to AGENTS.md so Claude Code picks it up via
either name.
2026-05-27 08:21:57 +00:00
Tink bot 632579b304 ci(veans): add fast veans-test job for unit tests 2026-05-27 08:21:57 +00:00
Tink bot c1d5272afe feat(veans): switch OAuth handshake to a loopback HTTP server 2026-05-27 08:21:57 +00:00
Tink bot b18762171d feat(veans): add browser launcher helpers for OAuth flow 2026-05-27 08:21:57 +00:00
Tink bot 952ad89a8b chore(veans): apply veans golangci pass across sources 2026-05-27 08:21:57 +00:00
Tink bot 202a5f60b0 ci(veans): add veans-lint job to Test workflow 2026-05-27 08:21:57 +00:00
Tink bot 8ef796f016 chore(veans): add veans-local golangci config 2026-05-27 08:21:57 +00:00
Tink bot 35aa486eb5 feat(veans): use OAuth 2.0 Authorization Code + PKCE as default auth
Vikunja's built-in OAuth server (Vikunja 2.3+) does not require client
registration and accepts arbitrary client_ids — it just enforces PKCE
(S256) and constrains redirect URIs to the vikunja- scheme. Earlier I
deferred OAuth on the assumption it needed a registered client; that
was wrong, and the docs make the path much smoother than POST /login.

The custom-scheme constraint (no http:// loopback) is side-stepped by
manual paste-back: veans prints the authorize URL, the user signs in,
their browser fails to open vikunja-veans-cli://callback?code=... and
shows an error, the user copies the URL from the address bar and
pastes it back. CLI extracts code + state, verifies state for CSRF,
exchanges via POST /api/v1/oauth/token (JSON body — Vikunja rejects
form-encoded), and returns the access token.

Resolution order in AcquireHumanToken:
  1. --token (paste-in JWT or personal API token; SSO/OIDC users)
  2. --use-password / --username + --password (POST /login)
  3. OAuth flow (interactive default)

login command supports the same --use-password / --token escape hatches
for token rotation on instances with OAuth disabled.

Includes unit tests for the PKCE generator (verifier shape per RFC 7636,
challenge = SHA256(verifier) base64url-no-pad), authorize-URL
construction, and the lenient callback parser (full URL / query-only /
bare code).
2026-05-27 08:21:57 +00:00
Tink bot d0c77ad6fe docs(veans): add README with quick-start guide 2026-05-27 08:21:57 +00:00
Tink bot 950d41df91 ci(veans): add veans-e2e workflow 2026-05-27 08:21:57 +00:00
Tink bot 4c3d449a35 test(veans): add e2e suite covering init, tasks, claim, prime flows 2026-05-27 08:21:57 +00:00
Tink bot 3a7bcb2a50 chore(veans): gitignore built binary 2026-05-27 08:21:57 +00:00
Tink bot df7a60d137 feat(veans): add login command for token rotation 2026-05-27 08:21:57 +00:00
Tink bot 2e2393121b feat(veans): add api passthrough command 2026-05-27 08:21:57 +00:00
Tink bot e8cdfcf023 feat(veans): add prime command for agent prompt injection 2026-05-27 08:21:57 +00:00
Tink bot b9551d55ba feat(veans): add claim command for assigning and bucket transition 2026-05-27 08:21:57 +00:00
Tink bot 6ebe25bfbc feat(veans): add update command with description and status transitions 2026-05-27 08:21:57 +00:00
Tink bot 6b756d92c3 feat(veans): add create command with labels and relations 2026-05-27 08:21:57 +00:00
Tink bot 2425d9923e feat(veans): add label get-or-create helper 2026-05-27 08:21:57 +00:00
Tink bot e88427ca3c feat(veans): add show command with PROJ-NN/#NN ID resolver 2026-05-27 08:21:57 +00:00
Tink bot 5e80c17281 feat(veans): add list command with filters and JSON output 2026-05-27 08:21:57 +00:00
Tink bot 081373bb48 feat(veans): add shared command runtime and git branch helper 2026-05-27 08:21:57 +00:00
Tink bot 81f4845a6b feat(veans): wire init cobra command 2026-05-27 08:21:57 +00:00
Tink bot 37b6ff538b feat(veans): orchestrate init bootstrap from probe to config write 2026-05-27 08:21:57 +00:00
Tink bot d2c3f3244d feat(veans): discover /routes for permission-group negotiation 2026-05-27 08:21:57 +00:00
Tink bot 1f5abaa6fb feat(veans): require APIToken.ExpiresAt with FarFuture sentinel 2026-05-27 08:21:57 +00:00
Tink bot 6b48a37710 feat(veans): add canonical status to bucket-title mapping 2026-05-27 08:21:57 +00:00
Tink bot 36fb0f0ace feat(veans): add .veans.yml schema and config helpers 2026-05-27 08:21:57 +00:00
Tink bot 878233f758 feat(veans): add transient human auth flow 2026-05-27 08:21:57 +00:00
Tink bot f05fc60777 feat(veans): add credential store with keychain, env, and file backends 2026-05-27 08:21:57 +00:00
Tink bot 4b6b8fca78 chore(veans): add magefile build and lint targets 2026-05-27 08:21:57 +00:00
Tink bot 3eec756863 feat(veans): add cobra root and version subcommand 2026-05-27 08:21:57 +00:00
Tink bot 87c312fb2b feat(veans): add JSON HTTP client and wire types 2026-05-27 08:21:57 +00:00
Tink bot e4c4837805 feat(veans): add stable error envelope and code constants 2026-05-27 08:21:57 +00:00
Tink bot 3d0039df2d feat(veans): scaffold Go module 2026-05-27 08:21:57 +00:00
Frederick [Bot] 6abf6c6012 chore(i18n): update translations via Crowdin 2026-05-27 02:31:52 +00:00
Tink bot b8cabcd825 fix(assignees): use db.ILIKE helper for assignee search count query 2026-05-26 19:43:16 +00:00
nithinvarma411 b6a02cb6a5 fix(assignees): resolve 500 error when reading task assignees 2026-05-26 18:59:33 +00:00
dependabot[bot] e0fb3ed732 chore(deps): bump js-cookie from 3.0.5 to 3.0.7 in /frontend
Bumps [js-cookie](https://github.com/js-cookie/js-cookie) from 3.0.5 to 3.0.7.
- [Release notes](https://github.com/js-cookie/js-cookie/releases)
- [Commits](https://github.com/js-cookie/js-cookie/compare/v3.0.5...v3.0.7)

---
updated-dependencies:
- dependency-name: js-cookie
  dependency-version: 3.0.7
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-26 18:36:29 +00:00
renovate[bot] dc85d2e3cb chore(deps): update dev-dependencies 2026-05-26 18:36:03 +00:00
Frederick [Bot] 56b82b23d8 chore(i18n): update translations via Crowdin 2026-05-24 02:13:34 +00:00
Frederick [Bot] 8a1b2252e2 chore(i18n): update translations via Crowdin 2026-05-23 02:05:00 +00:00