NextCloud AIO OpenClaw Skill
This skill connects OpenClaw to Nextcloud for:
- Notes API
- CalDAV Tasks and Events
- WebDAV Files
- CardDAV Contacts
Primary goal: Predictable, loss-resistant Nextcloud reads/writes.
This skill is validated against:
- Nextcloud Hub 25 Autumn (
32.0.6)
- OpenClaw (
2026.3.2)
Required Configuration
Set these environment variables:
NEXTCLOUD_URL (example: https://cloud.example.com)
NEXTCLOUD_USER
NEXTCLOUD_TOKEN (App Password strongly preferred)
NEXTCLOUD_TIMEZONE (recommended, IANA timezone like America/Los_Angeles; used for timezone-less date/time inputs)
Runtime and Metadata Contract
This section is the source of truth for registry metadata parity.
- Required binary:
node (Node.js 20+)
- Required environment variables:
NEXTCLOUD_URL, NEXTCLOUD_USER, NEXTCLOUD_TOKEN
- Optional environment variable:
NEXTCLOUD_TIMEZONE
- Required network target: the host defined by
NEXTCLOUD_URL (plus normal redirects from that host)
If a registry entry says "no required binaries" or "no required env vars", treat it as stale metadata and fix before publishing.
Preflight Audit Gate
Before first execution in any new environment:
- Complete the bundled script review checklist below.
- Confirm no unexpected endpoints or privileged system calls are present.
- If checks are incomplete, do not run the skill.
- Prefer sandbox/container validation and least-privilege app credentials first.
Security and Publish Safety
- Never hardcode credentials in
SKILL.md, scripts, examples, commit messages, or changelog text.
- Store secrets only in environment variables or local secret stores.
- Use dedicated app passwords for integrations; rotate if leaked or shared.
- Keep local
.env files out of git (see .gitignore).
- Before publishing, run a quick scan for obvious secrets and remove any accidental values.
Bundled Script Review Checklist (Before First Use)
The skill ships two scripts: scripts/nextcloud.js (Node.js, text operations) and scripts/files_binary.py (Python 3, binary file transfers). Review both before running in new environments:
- Confirm runtime requirements:
node --version (must be 20+)
- Check obvious outbound endpoints:
rg -n "https?://" scripts/nextcloud.js
- Check sensitive capability usage:
rg -n "child_process|spawn\\(|exec\\(|require\\(\"fs\"\\)|require\\('fs'\\)|fs\\." scripts/nextcloud.js
- Verify credentials are env-driven only:
rg -n "NEXTCLOUD_URL|NEXTCLOUD_USER|NEXTCLOUD_TOKEN|NEXTCLOUD_TIMEZONE" scripts/nextcloud.js
- Run in low-risk mode first:
- Use a dedicated low-privilege Nextcloud app password
- Test against non-production data when possible
If review confidence is low, do not run until code is reviewed by a trusted maintainer.
Static audit snapshot (current bundle)
Quick grep-style checks on scripts/nextcloud.js should currently show:
- Config is read from
process.env.NEXTCLOUD_URL|USER|TOKEN|TIMEZONE.
- Request path is built as
${CONFIG.url}${endpoint} and uses fetch(...).
- No obvious
child_process or fs usage in the bundle entry path.
Quick checks on scripts/files_binary.py:
- Config is read from
os.environ.get("NEXTCLOUD_URL|USER|TOKEN").
- DAV URL is built as
{url}/remote.php/dav/files/{user}/{path}.
- Uses only stdlib (
urllib, base64, xml.etree); no third-party deps.
This is not a full security audit. Re-run checks after every bundle update.
Run
node scripts/nextcloud.js <command> <subcommand> [options]
Commands
Notes
notes list
notes get --id <id>
notes create --title <title> --content <content> [--category <category>]
notes edit --id <id> [--title <title>] [--content <content>] [--category <category>]
notes delete --id <id>
Tasks
tasks list [--calendar <calendar-name>]
tasks create --title <title> [--calendar <calendar-name>] [--due <iso>] [--priority <0-9>] [--description <text>] [--timezone <IANA>]
tasks edit --uid <uid> [--calendar <calendar-name>] [--title <title>] [--due <iso>] [--priority <0-9>] [--description <text>] [--timezone <IANA>]
tasks delete --uid <uid> [--calendar <calendar-name>]
tasks complete --uid <uid> [--calendar <calendar-name>]
Calendar Events
calendar list [--from <iso>] [--to <iso>] (default: next 7 days)
calendar create --summary <summary> --start <iso-or-date> --end <iso-or-date> [--calendar <calendar-name>] [--description <text>] [--timezone <IANA>] [--all-day]
calendar edit --uid <uid> [--calendar <calendar-name>] [--summary <summary>] [--start <iso-or-date>] [--end <iso-or-date>] [--description <text>] [--timezone <IANA>] [--all-day]
calendar delete --uid <uid> [--calendar <calendar-name>]
Important:
- In many setups,
Contact birthdays appears as an events calendar but is read-only.
- Birthdays must be managed through
contacts create/edit --birthday ..., not through calendar create/edit.
calendar list returns event summary, start, end, and may include location and description when present.
Calendar Discovery
calendars list [--type <tasks|events>]
Files (text)
files list [--path <path>]
files search --query <query> (supports natural-language input; ranks by filename/path token relevance)
files get --path <path> (accepts relative user path or full DAV href)
files upload --path <path> --content <content>
files delete --path <path>
Files (binary) — ODT, DOCX, PDF, images, etc.
nextcloud.js passes file content as text strings, which corrupts binary formats.
Use the companion Python script for any binary file:
python3 scripts/files_binary.py download <nc_path> <local_path>
python3 scripts/files_binary.py upload <local_path> <nc_path>
python3 scripts/files_binary.py exists <nc_path>
python3 scripts/files_binary.py list [<nc_path>]
Reads the same NEXTCLOUD_URL, NEXTCLOUD_USER, NEXTCLOUD_TOKEN env vars.
Auto-detects MIME type from file extension (ODT, DOCX, XLSX, PDF, PNG, JPG, and more).
When to use which:
| Situation |
Command |
Plain text files (.md, .txt, .csv) |
node scripts/nextcloud.js files get/upload |
Binary files (.odt, .docx, .pdf, images) |
python3 scripts/files_binary.py download/upload |
Contacts
contacts list [--addressbook <name>]
contacts get --uid <uid> [--addressbook <name>]
contacts search --query <query> [--addressbook <name>]
contacts create [--name <full-name>] [--first-name <given>] [--last-name <family>] [--middle-name <middle>] [--prefix <prefix>] [--suffix <suffix>] [--addressbook <name>] [--email <single>] [--emails <csv>] [--phone <single>] [--phones <csv>] [--organization <org>] [--title <title>] [--note <note>] [--birthday <YYYY-MM-DD|YYYYMMDD|--MM-DD|--MMDD>]
contacts edit --uid <uid> [--addressbook <name>] [--name <full-name>] [--first-name <given>] [--last-name <family>] [--middle-name <middle>] [--prefix <prefix>] [--suffix <suffix>] [--email <single>] [--emails <csv>] [--phone <single>] [--phones <csv>] [--organization <org>] [--title <title>] [--note <note>] [--birthday <YYYY-MM-DD|YYYYMMDD|--MM-DD|--MMDD>]
contacts delete --uid <uid> [--addressbook <name>]
Address Book Discovery
Contact Data Contract (Important)
Use fullName and structuredName as canonical name fields.
Contact output includes:
uid
addressBook
fullName
structuredName with:
familyName
givenName
additionalNames
honorificPrefixes
honorificSuffixes
nameRaw (raw N value for diagnostics only; do not display to users by default)
emails array or null
phones array or null
organization, title, note
birthday normalized for readability (YYYY-MM-DD or --MM-DD when possible)
birthdayRaw (raw CardDAV value)
Reliability Rules (Quick Reference)
Apply these on every write to prevent corruption and confusion:
- Identity and naming
- Never infer
UID from URL; always use payload uid.
- Use
fullName and structuredName for user-facing names; keep nameRaw diagnostic-only.
- Contact and birthday integrity
- Birthday input formats:
YYYY-MM-DD, YYYYMMDD, --MM-DD, --MMDD.
- Never add trailing semicolons to
BDAY.
- Birthdays are contact fields: use
contacts create/edit --birthday ...; never edit birthday-derived calendar entries directly.
- Intent-preserving updates
- Modify only fields explicitly requested by the user.
- To clear a value, pass explicit empty values.
- Respect explicit
--addressbook; otherwise use remembered default or ask once.
- Calendar and task validation
- Validate date/datetime inputs before sending; require
end > start for timed events.
- Escape ICS text fields (
SUMMARY, DESCRIPTION).
- Validate task priority range (
0..9).
- For timezone-less timed input, use
--timezone or NEXTCLOUD_TIMEZONE.
- For all-day events, use
VALUE=DATE semantics with exclusive DTEND.
- Capability and safety checks
- Do not assume listed event calendars are writable.
- Treat
Contact birthdays as read-only/system calendar.
- If no writable event calendar exists, explain event mutation is unavailable until writable access exists.
- Some calendars are multi-component (
VEVENT + VTODO); keep them eligible for both event/task flows.
- Files and post-write verification
- Require non-empty file paths for
files upload/get/delete.
- Escape XML filter/search values for CardDAV/WebDAV operations.
- After mutations (
create, edit, complete, delete), verify with follow-up read/list when practical.
- For propagation delay, retry around
0.5s, 1s, 2s before declaring failure.
Known Nextcloud/CardDAV Behaviors
- Servers/clients may negotiate or normalize vCard versions (
3.0 vs 4.0) and content on write/read.
FN and N should each appear once in valid contacts.
BDAY failures are often malformed input (for example stray delimiters), not server instability.
- Folded vCard lines must be unfolded before parsing.
Error to Action Map (Fast)
HTTP 403 on calendar create/edit/delete -> likely read-only/system calendar or missing permission; switch to writable events calendar.
HTTP 501 on files search -> WebDAV SEARCH unsupported; fallback to recursive PROPFIND + client-side filter.
HTTP 415 on task/event update -> malformed ICS payload; normalize formatting and retry once.
- Contact write succeeded but UI still stale -> run verification retry cycle before reporting failure.
Agent Behavior: Default Calendar Selection
When user creates tasks/events without explicit calendar:
- Run
calendars list --type tasks or calendars list --type events.
- For
events, filter out likely read-only calendars first (Contact birthdays, Holidays, names containing read only or readonly).
- If at least one writable calendar remains, ask for selection.
- Ask whether to remember as default.
- Store in memory.
- If no writable events calendar remains, explain why event mutation is unavailable and suggest creating a writable calendar in Nextcloud Calendar.
Memory keys:
default_task_calendar
default_event_calendar
Agent Behavior: Default Address Book Selection
When user creates contacts without explicit address book:
- Run
addressbooks list.
- Ask for selection.
- Ask whether to remember default.
- Store in memory.
Memory key:
Agent Behavior Playbooks (Condensed)
These playbooks are execution shortcuts. Reliability and safety rules above still apply.
Contacts and Birthdays
- Resolve address book (
--addressbook or remembered default).
- For create/edit, pass only user-requested fields; use explicit empty values when user wants fields cleared.
- Use
--name for full-name input; use structured-name flags for split-name input.
- Pass multiple emails/phones as CSV values.
- Birthday changes always go through contacts (
contacts create/edit --birthday ...), never calendar mutation commands.
- Verify birthday/name-sensitive writes with
contacts get --uid ... when confidence is important.
Tasks
- Resolve task calendar when omitted.
- Validate due and priority (
0..9) before write.
- After create/edit/complete/delete, verify via focused list/get when practical.
Calendar Events
- Resolve writable events calendar; treat
Contact birthdays and system calendars as read-only.
- Validate
start/end and require end > start for timed events.
- For timezone-less date-times, pass
--timezone or use NEXTCLOUD_TIMEZONE.
- For all-day intent, use
--all-day with date values (not timed midnight ranges).
- Re-list in a focused window after mutation when confidence is important.
All-day end-date normalization behavior:
start=YYYY-MM-DD, end=YYYY-MM-DD -> inclusive single-day input, normalized.
start=YYYY-MM-DD, end=next-day -> already-exclusive single-day input.
- Multi-day inclusive ranges -> normalized to canonical exclusive
DTEND.
Appointment briefing workflow (high priority intent)
Use this when user asks for appointment details, address, or "what is my appointment today":
- Determine "today" in
NEXTCLOUD_TIMEZONE (or ask if timezone is unclear).
- Run
calendar list --from <start-of-day-iso> --to <end-of-day-iso>.
- Filter events by user terms (for example
dermatology, doctor, provider name).
- For the selected event, include
summary, time window, location, and key lines from description.
- If multiple events match, ask a quick disambiguation question.
- If none match, clearly say none found for today and offer to check tomorrow or full week.
- Provide a plain-text message suitable for SMS/chat (short lines, no markdown).
Files
- Require explicit file path for mutating operations.
files get/upload/delete accept either a relative user path (for example /Share-Family/file.docx) or a full DAV href returned by search.
- For binary files (ODT, DOCX, PDF, images), use
python3 scripts/files_binary.py — never pass binary content through nextcloud.js files upload.
- Use deterministic test paths for temporary validation files.
- Read back uploaded content and then delete test files.
- Use
files search with user phrasing directly; it auto-normalizes query text and ranks likely matches.
- On large instances, prefer narrower path-based listing before broad search to reduce load.
Notes
- Create with explicit title/content.
- Read back by id after create/edit.
- Delete temporary test notes after verification.
Internal Smoke Test Protocol
When validating this skill in a live environment:
- Contacts: create -> get -> edit -> get -> optional delete.
- Notes: create -> get -> edit -> get -> delete.
- Files: upload -> get -> search/list -> delete.
- Tasks: create -> list/locate -> edit -> complete -> delete.
- Calendar: create -> list/locate -> edit -> delete.
Use a unique timestamped suffix on all test data and clean up all temporary artifacts unless the user requests keeping them.
Use least-privilege credentials for tests and monitor outbound traffic; the process should only communicate with NEXTCLOUD_URL.
Calendar exception handling:
- If no writable event calendar exists, mark calendar mutation tests as intentionally skipped (environment limitation), and still run
calendar list.
- Do not treat this as a skill failure; treat it as a server capability/permission constraint.
Persistent Rules (Store These)
Persist these high-value rules in memory/rules:
- Birthdays are contact fields: use
contacts create/edit --birthday ...; never mutate Contact birthdays directly.
- For all-day events, always use
calendar create/edit --all-day with date values (avoid timed midnight ranges).
- For timezone-less timed values, use
--timezone or NEXTCLOUD_TIMEZONE; ask if timezone is unknown.
- Prefer writable user calendars for event mutations; if
403, report likely read-only/permission issue.
- Verify event/contact writes with a focused read/list before declaring final success.
- Keep canonical all-day encoding (
VALUE=DATE); avoid duplicate compatibility events unless explicitly requested.
Troubleshooting Playbook
Birthday was updated but not visible in Nextcloud UI yet:
- Run
contacts get --uid ... immediately.
- If value is correct in API, advise short wait + UI refresh.
- Re-check after retry intervals before escalating.
contacts get and contacts search disagree briefly:
- Prefer retry cycle and then trust latest consistent result.
- Do not create duplicate contact entries while waiting.
Event creation keeps failing:
- Run
calendars list --type events and verify at least one writable non-system calendar exists.
- If only birthday/system calendars exist, explain limitation and stop mutation attempts.
Output Envelope
All commands return JSON:
Success:
{
"status": "success",
"data": {}
}
Error:
{
"status": "error",
"message": "Error description"
}
1---2name: nextcloud-aio-oc-nextcloud-aio-openclaw3description: Reliable Nextcloud integration for Notes, Tasks, Calendar, Files, and Contacts (CardDAV-safe vCard handling with robust contact create/update).4license: MIT5---67# NextCloud AIO OpenClaw Skill89This skill connects OpenClaw to Nextcloud for:10- Notes API11- CalDAV Tasks and Events12- WebDAV Files13- CardDAV Contacts1415Primary goal: Predictable, loss-resistant Nextcloud reads/writes.1617This skill is validated against:18- Nextcloud Hub 25 Autumn (`32.0.6`)19- OpenClaw (`2026.3.2`)2021## Required Configuration2223Set these environment variables:24- `NEXTCLOUD_URL` (example: `https://cloud.example.com`)25- `NEXTCLOUD_USER`26- `NEXTCLOUD_TOKEN` (App Password strongly preferred)27- `NEXTCLOUD_TIMEZONE` (recommended, IANA timezone like `America/Los_Angeles`; used for timezone-less date/time inputs)2829## Runtime and Metadata Contract3031This section is the source of truth for registry metadata parity.3233- Required binary: `node` (Node.js `20+`)34- Required environment variables: `NEXTCLOUD_URL`, `NEXTCLOUD_USER`, `NEXTCLOUD_TOKEN`35- Optional environment variable: `NEXTCLOUD_TIMEZONE`36- Required network target: the host defined by `NEXTCLOUD_URL` (plus normal redirects from that host)3738If a registry entry says "no required binaries" or "no required env vars", treat it as stale metadata and fix before publishing.3940## Preflight Audit Gate4142Before first execution in any new environment:4344- Complete the bundled script review checklist below.45- Confirm no unexpected endpoints or privileged system calls are present.46- If checks are incomplete, do not run the skill.47- Prefer sandbox/container validation and least-privilege app credentials first.4849## Security and Publish Safety5051- Never hardcode credentials in `SKILL.md`, scripts, examples, commit messages, or changelog text.52- Store secrets only in environment variables or local secret stores.53- Use dedicated app passwords for integrations; rotate if leaked or shared.54- Keep local `.env` files out of git (see `.gitignore`).55- Before publishing, run a quick scan for obvious secrets and remove any accidental values.5657## Bundled Script Review Checklist (Before First Use)5859The skill ships two scripts: `scripts/nextcloud.js` (Node.js, text operations) and `scripts/files_binary.py` (Python 3, binary file transfers). Review both before running in new environments:60611. Confirm runtime requirements:62 - `node --version` (must be `20+`)632. Check obvious outbound endpoints:64 - `rg -n "https?://" scripts/nextcloud.js`653. Check sensitive capability usage:66 - `rg -n "child_process|spawn\\(|exec\\(|require\\(\"fs\"\\)|require\\('fs'\\)|fs\\." scripts/nextcloud.js`674. Verify credentials are env-driven only:68 - `rg -n "NEXTCLOUD_URL|NEXTCLOUD_USER|NEXTCLOUD_TOKEN|NEXTCLOUD_TIMEZONE" scripts/nextcloud.js`695. Run in low-risk mode first:70 - Use a dedicated low-privilege Nextcloud app password71 - Test against non-production data when possible7273If review confidence is low, do not run until code is reviewed by a trusted maintainer.7475### Static audit snapshot (current bundle)7677Quick grep-style checks on `scripts/nextcloud.js` should currently show:7879- Config is read from `process.env.NEXTCLOUD_URL|USER|TOKEN|TIMEZONE`.80- Request path is built as `${CONFIG.url}${endpoint}` and uses `fetch(...)`.81- No obvious `child_process` or `fs` usage in the bundle entry path.8283Quick checks on `scripts/files_binary.py`:8485- Config is read from `os.environ.get("NEXTCLOUD_URL|USER|TOKEN")`.86- DAV URL is built as `{url}/remote.php/dav/files/{user}/{path}`.87- Uses only stdlib (`urllib`, `base64`, `xml.etree`); no third-party deps.8889This is not a full security audit. Re-run checks after every bundle update.9091## Run9293```bash94node scripts/nextcloud.js <command> <subcommand> [options]95```9697## Commands9899### Notes100- `notes list`101- `notes get --id <id>`102- `notes create --title <title> --content <content> [--category <category>]`103- `notes edit --id <id> [--title <title>] [--content <content>] [--category <category>]`104- `notes delete --id <id>`105106### Tasks107- `tasks list [--calendar <calendar-name>]`108- `tasks create --title <title> [--calendar <calendar-name>] [--due <iso>] [--priority <0-9>] [--description <text>] [--timezone <IANA>]`109- `tasks edit --uid <uid> [--calendar <calendar-name>] [--title <title>] [--due <iso>] [--priority <0-9>] [--description <text>] [--timezone <IANA>]`110- `tasks delete --uid <uid> [--calendar <calendar-name>]`111- `tasks complete --uid <uid> [--calendar <calendar-name>]`112113### Calendar Events114- `calendar list [--from <iso>] [--to <iso>]` (default: next 7 days)115- `calendar create --summary <summary> --start <iso-or-date> --end <iso-or-date> [--calendar <calendar-name>] [--description <text>] [--timezone <IANA>] [--all-day]`116- `calendar edit --uid <uid> [--calendar <calendar-name>] [--summary <summary>] [--start <iso-or-date>] [--end <iso-or-date>] [--description <text>] [--timezone <IANA>] [--all-day]`117- `calendar delete --uid <uid> [--calendar <calendar-name>]`118119Important:120- In many setups, `Contact birthdays` appears as an events calendar but is read-only.121- Birthdays must be managed through `contacts create/edit --birthday ...`, not through `calendar create/edit`.122- `calendar list` returns event `summary`, `start`, `end`, and may include `location` and `description` when present.123124### Calendar Discovery125- `calendars list [--type <tasks|events>]`126127### Files (text)128- `files list [--path <path>]`129- `files search --query <query>` (supports natural-language input; ranks by filename/path token relevance)130- `files get --path <path>` (accepts relative user path or full DAV href)131- `files upload --path <path> --content <content>`132- `files delete --path <path>`133134### Files (binary) — ODT, DOCX, PDF, images, etc.135136`nextcloud.js` passes file content as text strings, which corrupts binary formats.137Use the companion Python script for any binary file:138139```bash140python3 scripts/files_binary.py download <nc_path> <local_path>141python3 scripts/files_binary.py upload <local_path> <nc_path>142python3 scripts/files_binary.py exists <nc_path>143python3 scripts/files_binary.py list [<nc_path>]144```145146Reads the same `NEXTCLOUD_URL`, `NEXTCLOUD_USER`, `NEXTCLOUD_TOKEN` env vars.147Auto-detects MIME type from file extension (ODT, DOCX, XLSX, PDF, PNG, JPG, and more).148149**When to use which:**150151| Situation | Command |152|-----------|---------|153| Plain text files (`.md`, `.txt`, `.csv`) | `node scripts/nextcloud.js files get/upload` |154| Binary files (`.odt`, `.docx`, `.pdf`, images) | `python3 scripts/files_binary.py download/upload` |155156### Contacts157- `contacts list [--addressbook <name>]`158- `contacts get --uid <uid> [--addressbook <name>]`159- `contacts search --query <query> [--addressbook <name>]`160- `contacts create [--name <full-name>] [--first-name <given>] [--last-name <family>] [--middle-name <middle>] [--prefix <prefix>] [--suffix <suffix>] [--addressbook <name>] [--email <single>] [--emails <csv>] [--phone <single>] [--phones <csv>] [--organization <org>] [--title <title>] [--note <note>] [--birthday <YYYY-MM-DD|YYYYMMDD|--MM-DD|--MMDD>]`161- `contacts edit --uid <uid> [--addressbook <name>] [--name <full-name>] [--first-name <given>] [--last-name <family>] [--middle-name <middle>] [--prefix <prefix>] [--suffix <suffix>] [--email <single>] [--emails <csv>] [--phone <single>] [--phones <csv>] [--organization <org>] [--title <title>] [--note <note>] [--birthday <YYYY-MM-DD|YYYYMMDD|--MM-DD|--MMDD>]`162- `contacts delete --uid <uid> [--addressbook <name>]`163164### Address Book Discovery165- `addressbooks list`166167## Contact Data Contract (Important)168169Use `fullName` and `structuredName` as canonical name fields.170171Contact output includes:172- `uid`173- `addressBook`174- `fullName`175- `structuredName` with:176 - `familyName`177 - `givenName`178 - `additionalNames`179 - `honorificPrefixes`180 - `honorificSuffixes`181- `nameRaw` (raw `N` value for diagnostics only; do not display to users by default)182- `emails` array or `null`183- `phones` array or `null`184- `organization`, `title`, `note`185- `birthday` normalized for readability (`YYYY-MM-DD` or `--MM-DD` when possible)186- `birthdayRaw` (raw CardDAV value)187188## Reliability Rules (Quick Reference)189190Apply these on every write to prevent corruption and confusion:1911921) Identity and naming193- Never infer `UID` from URL; always use payload `uid`.194- Use `fullName` and `structuredName` for user-facing names; keep `nameRaw` diagnostic-only.1951962) Contact and birthday integrity197- Birthday input formats: `YYYY-MM-DD`, `YYYYMMDD`, `--MM-DD`, `--MMDD`.198- Never add trailing semicolons to `BDAY`.199- Birthdays are contact fields: use `contacts create/edit --birthday ...`; never edit birthday-derived calendar entries directly.2002013) Intent-preserving updates202- Modify only fields explicitly requested by the user.203- To clear a value, pass explicit empty values.204- Respect explicit `--addressbook`; otherwise use remembered default or ask once.2052064) Calendar and task validation207- Validate date/datetime inputs before sending; require `end > start` for timed events.208- Escape ICS text fields (`SUMMARY`, `DESCRIPTION`).209- Validate task priority range (`0..9`).210- For timezone-less timed input, use `--timezone` or `NEXTCLOUD_TIMEZONE`.211- For all-day events, use `VALUE=DATE` semantics with exclusive `DTEND`.2122135) Capability and safety checks214- Do not assume listed event calendars are writable.215- Treat `Contact birthdays` as read-only/system calendar.216- If no writable event calendar exists, explain event mutation is unavailable until writable access exists.217- Some calendars are multi-component (`VEVENT` + `VTODO`); keep them eligible for both event/task flows.2182196) Files and post-write verification220- Require non-empty file paths for `files upload/get/delete`.221- Escape XML filter/search values for CardDAV/WebDAV operations.222- After mutations (`create`, `edit`, `complete`, `delete`), verify with follow-up read/list when practical.223- For propagation delay, retry around `0.5s`, `1s`, `2s` before declaring failure.224225## Known Nextcloud/CardDAV Behaviors226227- Servers/clients may negotiate or normalize vCard versions (`3.0` vs `4.0`) and content on write/read.228- `FN` and `N` should each appear once in valid contacts.229- `BDAY` failures are often malformed input (for example stray delimiters), not server instability.230- Folded vCard lines must be unfolded before parsing.231232## Error to Action Map (Fast)233234- `HTTP 403` on `calendar create/edit/delete` -> likely read-only/system calendar or missing permission; switch to writable events calendar.235- `HTTP 501` on `files search` -> WebDAV `SEARCH` unsupported; fallback to recursive `PROPFIND` + client-side filter.236- `HTTP 415` on task/event update -> malformed ICS payload; normalize formatting and retry once.237- Contact write succeeded but UI still stale -> run verification retry cycle before reporting failure.238239## Agent Behavior: Default Calendar Selection240241When user creates tasks/events without explicit calendar:2421. Run `calendars list --type tasks` or `calendars list --type events`.2432. For `events`, filter out likely read-only calendars first (`Contact birthdays`, `Holidays`, names containing `read only` or `readonly`).2443. If at least one writable calendar remains, ask for selection.2454. Ask whether to remember as default.2465. Store in memory.2476. If no writable events calendar remains, explain why event mutation is unavailable and suggest creating a writable calendar in Nextcloud Calendar.248249Memory keys:250- `default_task_calendar`251- `default_event_calendar`252253## Agent Behavior: Default Address Book Selection254255When user creates contacts without explicit address book:2561. Run `addressbooks list`.2572. Ask for selection.2583. Ask whether to remember default.2594. Store in memory.260261Memory key:262- `default_addressbook`263264## Agent Behavior Playbooks (Condensed)265266These playbooks are execution shortcuts. Reliability and safety rules above still apply.267268### Contacts and Birthdays2691. Resolve address book (`--addressbook` or remembered default).2702. For create/edit, pass only user-requested fields; use explicit empty values when user wants fields cleared.2713. Use `--name` for full-name input; use structured-name flags for split-name input.2724. Pass multiple emails/phones as CSV values.2735. Birthday changes always go through contacts (`contacts create/edit --birthday ...`), never calendar mutation commands.2746. Verify birthday/name-sensitive writes with `contacts get --uid ...` when confidence is important.275276### Tasks2771. Resolve task calendar when omitted.2782. Validate due and priority (`0..9`) before write.2793. After create/edit/complete/delete, verify via focused list/get when practical.280281### Calendar Events2821. Resolve writable events calendar; treat `Contact birthdays` and system calendars as read-only.2832. Validate `start`/`end` and require `end > start` for timed events.2843. For timezone-less date-times, pass `--timezone` or use `NEXTCLOUD_TIMEZONE`.2854. For all-day intent, use `--all-day` with date values (not timed midnight ranges).2865. Re-list in a focused window after mutation when confidence is important.287288All-day end-date normalization behavior:289- `start=YYYY-MM-DD`, `end=YYYY-MM-DD` -> inclusive single-day input, normalized.290- `start=YYYY-MM-DD`, `end=next-day` -> already-exclusive single-day input.291- Multi-day inclusive ranges -> normalized to canonical exclusive `DTEND`.292293### Appointment briefing workflow (high priority intent)294Use this when user asks for appointment details, address, or "what is my appointment today":2951. Determine "today" in `NEXTCLOUD_TIMEZONE` (or ask if timezone is unclear).2962. Run `calendar list --from <start-of-day-iso> --to <end-of-day-iso>`.2973. Filter events by user terms (for example `dermatology`, `doctor`, provider name).2984. For the selected event, include `summary`, time window, `location`, and key lines from `description`.2995. If multiple events match, ask a quick disambiguation question.3006. If none match, clearly say none found for today and offer to check tomorrow or full week.3017. Provide a plain-text message suitable for SMS/chat (short lines, no markdown).302303### Files3041. Require explicit file path for mutating operations.3052. `files get/upload/delete` accept either a relative user path (for example `/Share-Family/file.docx`) or a full DAV href returned by search.3063. For binary files (ODT, DOCX, PDF, images), use `python3 scripts/files_binary.py` — never pass binary content through `nextcloud.js files upload`.3074. Use deterministic test paths for temporary validation files.3085. Read back uploaded content and then delete test files.3096. Use `files search` with user phrasing directly; it auto-normalizes query text and ranks likely matches.3107. On large instances, prefer narrower path-based listing before broad search to reduce load.311312### Notes3131. Create with explicit title/content.3142. Read back by id after create/edit.3153. Delete temporary test notes after verification.316317## Internal Smoke Test Protocol318319When validating this skill in a live environment:3201. Contacts: create -> get -> edit -> get -> optional delete.3212. Notes: create -> get -> edit -> get -> delete.3223. Files: upload -> get -> search/list -> delete.3234. Tasks: create -> list/locate -> edit -> complete -> delete.3245. Calendar: create -> list/locate -> edit -> delete.325326Use a unique timestamped suffix on all test data and clean up all temporary artifacts unless the user requests keeping them.327Use least-privilege credentials for tests and monitor outbound traffic; the process should only communicate with `NEXTCLOUD_URL`.328329Calendar exception handling:330- If no writable event calendar exists, mark calendar mutation tests as intentionally skipped (environment limitation), and still run `calendar list`.331- Do not treat this as a skill failure; treat it as a server capability/permission constraint.332333## Persistent Rules (Store These)334335Persist these high-value rules in memory/rules:3363371. Birthdays are contact fields: use `contacts create/edit --birthday ...`; never mutate `Contact birthdays` directly.3382. For all-day events, always use `calendar create/edit --all-day` with date values (avoid timed midnight ranges).3393. For timezone-less timed values, use `--timezone` or `NEXTCLOUD_TIMEZONE`; ask if timezone is unknown.3404. Prefer writable user calendars for event mutations; if `403`, report likely read-only/permission issue.3415. Verify event/contact writes with a focused read/list before declaring final success.3426. Keep canonical all-day encoding (`VALUE=DATE`); avoid duplicate compatibility events unless explicitly requested.343344## Troubleshooting Playbook345346- Birthday was updated but not visible in Nextcloud UI yet:347 - Run `contacts get --uid ...` immediately.348 - If value is correct in API, advise short wait + UI refresh.349 - Re-check after retry intervals before escalating.350351- `contacts get` and `contacts search` disagree briefly:352 - Prefer retry cycle and then trust latest consistent result.353 - Do not create duplicate contact entries while waiting.354355- Event creation keeps failing:356 - Run `calendars list --type events` and verify at least one writable non-system calendar exists.357 - If only birthday/system calendars exist, explain limitation and stop mutation attempts.358359## Output Envelope360361All commands return JSON:362363Success:364```json365{366 "status": "success",367 "data": {}368}369```370371Error:372```json373{374 "status": "error",375 "message": "Error description"376}377```