nextcloud-agent 2.3.4__tar.gz → 2.3.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/PKG-INFO +2 -2
  2. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/auth.py +5 -3
  3. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/ontology/certification.json +1 -1
  4. nextcloud_agent-2.3.6/nextcloud_agent/skills/nextcloud-agent-development/SKILL.md +123 -0
  5. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent.egg-info/PKG-INFO +2 -2
  6. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent.egg-info/SOURCES.txt +2 -0
  7. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent.egg-info/requires.txt +1 -1
  8. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/pyproject.toml +3 -3
  9. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/tests/test_auth.py +19 -1
  10. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/tests/test_kg_ingest.py +15 -5
  11. nextcloud_agent-2.3.6/tests/test_publication_workflow.py +57 -0
  12. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/LICENSE +0 -0
  13. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/README.md +0 -0
  14. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/__init__.py +0 -0
  15. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/__main__.py +0 -0
  16. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/api/__init__.py +0 -0
  17. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/api/api_client_base.py +0 -0
  18. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/api/api_client_calendar.py +0 -0
  19. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/api/api_client_contacts.py +0 -0
  20. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/api/api_client_files.py +0 -0
  21. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/api/xml_security.py +0 -0
  22. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/api_client.py +0 -0
  23. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/connector_sources.py +0 -0
  24. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/kg_ingest.py +0 -0
  25. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/mcp_config.json +0 -0
  26. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/mcp_server.py +0 -0
  27. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/ontology/__init__.py +0 -0
  28. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/ontology/fixtures/records.json +0 -0
  29. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/ontology/mappings/source.yaml +0 -0
  30. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/ontology/migrations/manifest.json +0 -0
  31. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/ontology/nextcloud.ttl +0 -0
  32. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/ontology/shapes/connector.shacl.ttl +0 -0
  33. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/prompts/file_steward.json +0 -0
  34. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/skills/nextcloud-agent-operations/SKILL.md +0 -0
  35. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/skills/nextcloud-agent-operations/agents/openai.yaml +0 -0
  36. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/skills/nextcloud-agent-operations/references/catalog.md +0 -0
  37. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/skills/nextcloud-files/SKILL.md +0 -0
  38. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/skills/nextcloud-files/WORKFLOW.md +0 -0
  39. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/skills/nextcloud-ingest/SKILL.md +0 -0
  40. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/skills/nextcloud-ingest/WORKFLOW.md +0 -0
  41. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/skills/nextcloud-shares/SKILL.md +0 -0
  42. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent/skills/nextcloud-shares/WORKFLOW.md +0 -0
  43. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent.egg-info/dependency_links.txt +0 -0
  44. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent.egg-info/entry_points.txt +0 -0
  45. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/nextcloud_agent.egg-info/top_level.txt +0 -0
  46. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/setup.cfg +0 -0
  47. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/tests/conftest.py +0 -0
  48. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/tests/test_api_client.py +0 -0
  49. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/tests/test_fleet_name.py +0 -0
  50. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/tests/test_init.py +0 -0
  51. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/tests/test_init_dynamics.py +0 -0
  52. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/tests/test_mcp_server.py +0 -0
  53. {nextcloud_agent-2.3.4 → nextcloud_agent-2.3.6}/tests/test_xml_security.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nextcloud-agent
3
- Version: 2.3.4
3
+ Version: 2.3.6
4
4
  Summary: Nextcloud MCP Server for Agentic AI!
5
5
  Author-email: Repository Maintainers <maintainers@knuckles.team>
6
6
  License: MIT
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3
12
12
  Requires-Python: >=3.12
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
- Requires-Dist: agent-connector-sdk[mcp]<0.6.0,>=0.5.4
15
+ Requires-Dist: scion-sdk[mcp]<0.6.0,>=0.5.9
16
16
  Requires-Dist: requests>=2.34.2
17
17
  Requires-Dist: defusedxml>=0.7.1
18
18
  Requires-Dist: python-dateutil>=2.9.0.post0
@@ -1,5 +1,4 @@
1
1
  import logging
2
- from contextlib import contextmanager
3
2
 
4
3
  from agent_connector_sdk.config import setting
5
4
  from agent_connector_sdk.exceptions import AuthError, UnauthorizedError
@@ -9,14 +8,17 @@ from nextcloud_agent.api_client import NextcloudAPI
9
8
  logger = logging.getLogger(__name__)
10
9
 
11
10
 
12
- @contextmanager
13
11
  def get_client(
14
12
  base_url: str | None = None,
15
13
  username: str | None = None,
16
14
  password: str | None = None,
17
15
  ):
18
16
  """
19
- Returns a NextcloudAPI client.
17
+ Yield a NextcloudAPI client and close it afterwards.
18
+
19
+ A plain generator, because FastMCP's ``Depends`` drives generator
20
+ dependencies itself; wrapping it in ``contextlib.contextmanager`` handed
21
+ every tool the context-manager object instead of the client.
20
22
 
21
23
  CONCEPT:AU-OS.config.secrets-authentication
22
24
  """
@@ -29,5 +29,5 @@
29
29
  "signer": "UNSIGNED-PREVIEW",
30
30
  "signing_public_key": "",
31
31
  "status": "source-validated",
32
- "validated_at": "2026-10-24T13:49:17Z"
32
+ "validated_at": "2026-10-30T01:41:48Z"
33
33
  }
@@ -0,0 +1,123 @@
1
+ ---
2
+ name: nextcloud-agent-development
3
+ skill_type: skill
4
+ description: >-
5
+ Develop and verify nextcloud-agent as a connector-owned package while preserving
6
+ its SDK Connector wiring, action-routed MCP tools, Nextcloud authentication and
7
+ TLS boundary, packaged content, and source-backed spec-delivery publication.
8
+ ---
9
+
10
+ # Nextcloud Agent Development
11
+
12
+ Use this skill for changes to the `nextcloud-agent` connector. The package owns
13
+ Nextcloud API calls, mappings, MCP tools, and connector content; it does not own
14
+ the connector SDK, Arbora admission, the shared Pages renderer, or Pipelines
15
+ hooks.
16
+
17
+ ## Repository map and runtime boundary
18
+
19
+ - `nextcloud_agent/__init__.py` creates the SDK `Connector` with `NextcloudAPI`,
20
+ `get_client`, and `nextcloud_agent.mcp_server` as its tool module. Preserve this
21
+ wiring when changing a client or tool.
22
+ - `nextcloud_agent/mcp_server.py` registers the condensed action-routed tools
23
+ `nextcloud_files`, `nextcloud_user`, `nextcloud_sharing`, `nextcloud_calendar`,
24
+ and `nextcloud_contacts`. Their action lists and client calls are the runtime
25
+ contract; the domain clients live under `nextcloud_agent/api/` and are composed
26
+ by `nextcloud_agent/api/__init__.py`.
27
+ - `nextcloud_agent/auth.py` reads `NEXTCLOUD_URL`, `NEXTCLOUD_USERNAME`, and
28
+ `NEXTCLOUD_PASSWORD` through the SDK setting boundary. The API client resolves
29
+ the named `nextcloud` TLS profile through the SDK and configures a verified,
30
+ same-origin requests session; preserve that transport boundary and its cleanup.
31
+ - `nextcloud_agent/ontology/`, `nextcloud_agent/prompts/`, and
32
+ `nextcloud_agent/skills/` contain connector content. The package-data table in
33
+ `pyproject.toml` declares those trees plus `agent_data/**` and `connectors/**`;
34
+ inspect a built wheel when changing packaged content.
35
+ - The current `pyproject.toml` declares `nextcloud-mcp` as its console script and
36
+ `nextcloud-mcp` as its connector identity. Older README, AGENTS, and deployment
37
+ examples mention a `nextcloud-agent` console script and `agent_server.py`, but
38
+ those are not declared or present in this source tree. Do not invent that
39
+ runtime or rewrite it as part of a Pages/skill change.
40
+
41
+ ## Rapid delivery: the 5-minute contract
42
+
43
+ 1. Read the owning `specs/` row and this repository's `AGENTS.md` before editing.
44
+ 2. Inspect the active client/tool path and focused tests before changing API
45
+ behavior, auth, ontology content, or packaged skills.
46
+ 3. Prefer the smallest connector-owned change. Preserve action names, parameter
47
+ schemas, SDK `Connector` wiring, Nextcloud credentials, TLS verification,
48
+ same-origin checks, and package-data declarations unless the owning requirement
49
+ changes them.
50
+ 4. Run focused tests through the narrow test-slot wrapper, then run the repository
51
+ pre-commit and pre-push gates without bypasses.
52
+ 5. For public packaged content, bump the patch version and refresh `uv.lock`
53
+ together; inspect the built wheel for the new skill.
54
+ 6. Treat Pages as a separate proof: after the exact main SHA has a successful
55
+ Pages run, check `/spec-delivery/`, its `index.html`, and `snapshot.json` over
56
+ HTTPS and record the revision from the snapshot.
57
+
58
+ ## Focused development commands
59
+
60
+ From the repository root:
61
+
62
+ ```bash
63
+ uv sync --locked
64
+
65
+ /var/tmp/arbora-delivery/scale/run-narrow-test-slot.sh \
66
+ uv run --locked --extra test pytest -q \
67
+ tests/test_init.py \
68
+ tests/test_init_dynamics.py \
69
+ tests/test_auth.py \
70
+ tests/test_api_client.py \
71
+ tests/test_mcp_server.py \
72
+ tests/test_kg_ingest.py \
73
+ tests/test_xml_security.py \
74
+ tests/test_fleet_name.py \
75
+ tests/test_publication_workflow.py
76
+
77
+ uvx pre-commit run --all-files
78
+ uvx pre-commit run --all-files --hook-stage pre-push
79
+ ```
80
+
81
+ These checks cover SDK server construction, runtime entry points, action
82
+ dispatch, authentication, XML security, ingestion, fleet identity, and the
83
+ Pages/release contract. Do not replace them with a live Nextcloud call or a
84
+ credential-bearing test.
85
+
86
+ ## Pages and package publication
87
+
88
+ `.github/workflows/pages.yml` is the one Pages caller. It invokes the shared
89
+ Pipelines `pages_pipeline.yml@main` with `content_source: docs` and the version 1
90
+ `.github/spec-dashboard.json` source declaration for `specs/*/status.json`.
91
+ Keep manual dispatch and the docs/spec/config/workflow path filters. The old direct
92
+ `mkdocs gh-deploy` publisher must not be restored because it would compete with the
93
+ shared dashboard build.
94
+
95
+ After a merge, verify the exact main SHA in the successful Pages run and probe:
96
+
97
+ ```bash
98
+ curl -fsSI https://knuckles-team.github.io/nextcloud-agent/
99
+ curl -fsSI https://knuckles-team.github.io/nextcloud-agent/spec-delivery/
100
+ curl -fsSI https://knuckles-team.github.io/nextcloud-agent/spec-delivery/index.html
101
+ curl -fsS https://knuckles-team.github.io/nextcloud-agent/spec-delivery/snapshot.json
102
+ ```
103
+
104
+ Keep `.github/workflows/pipeline.yml` on the shared `connector: true` and
105
+ `render_connector: true` flags. Central generation owns generated connector
106
+ manifests and release container artifacts; preserve this package's existing
107
+ runtime, auth, and port semantics and inspect generated output before making
108
+ claims.
109
+
110
+ ## Change discipline
111
+
112
+ - Keep Nextcloud API calls, request models, ontology mappings, MCP schemas, and
113
+ ingestion declarations under this repository's `CONN-NEXTCLOUD-AGENT-*` boundary.
114
+ SDK transport and central renderer behavior belong to their owner repositories.
115
+ - Use `NEXTCLOUD_URL`, `NEXTCLOUD_USERNAME`, and `NEXTCLOUD_PASSWORD` only in
116
+ injected runtime configuration; never print or commit their values. Preserve
117
+ TLS certificate verification, same-origin checks, and the SDK-resolved named
118
+ profile path.
119
+ - For mutations, inspect current state first, require operator approval where the
120
+ existing connector procedure calls for it, and verify the result. Keep KG
121
+ ingestion opt-in and preserve its provenance/tenant contract.
122
+ - Keep generated status changes and central promotion with the Pipelines/status
123
+ owner. Do not claim Pages or public package results from a local build.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nextcloud-agent
3
- Version: 2.3.4
3
+ Version: 2.3.6
4
4
  Summary: Nextcloud MCP Server for Agentic AI!
5
5
  Author-email: Repository Maintainers <maintainers@knuckles.team>
6
6
  License: MIT
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3
12
12
  Requires-Python: >=3.12
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
- Requires-Dist: agent-connector-sdk[mcp]<0.6.0,>=0.5.4
15
+ Requires-Dist: scion-sdk[mcp]<0.6.0,>=0.5.9
16
16
  Requires-Dist: requests>=2.34.2
17
17
  Requires-Dist: defusedxml>=0.7.1
18
18
  Requires-Dist: python-dateutil>=2.9.0.post0
@@ -29,6 +29,7 @@ nextcloud_agent/ontology/mappings/source.yaml
29
29
  nextcloud_agent/ontology/migrations/manifest.json
30
30
  nextcloud_agent/ontology/shapes/connector.shacl.ttl
31
31
  nextcloud_agent/prompts/file_steward.json
32
+ nextcloud_agent/skills/nextcloud-agent-development/SKILL.md
32
33
  nextcloud_agent/skills/nextcloud-agent-operations/SKILL.md
33
34
  nextcloud_agent/skills/nextcloud-agent-operations/agents/openai.yaml
34
35
  nextcloud_agent/skills/nextcloud-agent-operations/references/catalog.md
@@ -46,4 +47,5 @@ tests/test_init.py
46
47
  tests/test_init_dynamics.py
47
48
  tests/test_kg_ingest.py
48
49
  tests/test_mcp_server.py
50
+ tests/test_publication_workflow.py
49
51
  tests/test_xml_security.py
@@ -1,4 +1,4 @@
1
- agent-connector-sdk[mcp]<0.6.0,>=0.5.4
1
+ scion-sdk[mcp]<0.6.0,>=0.5.9
2
2
  requests>=2.34.2
3
3
  defusedxml>=0.7.1
4
4
  python-dateutil>=2.9.0.post0
@@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "nextcloud-agent"
7
- version = "2.3.4"
7
+ version = "2.3.6"
8
8
  description = "Nextcloud MCP Server for Agentic AI!"
9
9
  readme = "README.md"
10
10
  classifiers = [ "Development Status :: 3 - Alpha", "License :: OSI Approved :: MIT License", "Environment :: Console", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3",]
11
11
  requires-python = ">=3.12"
12
- dependencies = [ "agent-connector-sdk[mcp]>=0.5.4,<0.6.0", "requests>=2.34.2", "defusedxml>=0.7.1", "python-dateutil>=2.9.0.post0", "icalendar>=7.3.0", "vobject>=0.9.9",]
12
+ dependencies = [ "scion-sdk[mcp]>=0.5.9,<0.6.0", "requests>=2.34.2", "defusedxml>=0.7.1", "python-dateutil>=2.9.0.post0", "icalendar>=7.3.0", "vobject>=0.9.9",]
13
13
  [[project.authors]]
14
14
  name = "Repository Maintainers"
15
15
  email = "maintainers@knuckles.team"
@@ -27,7 +27,7 @@ test = [
27
27
  [project.entry-points."agent_connector_sdk.prompt_providers"]
28
28
  nextcloud-agent = "nextcloud_agent.prompts"
29
29
 
30
- # The one declared connector identity: the fleet name (SDK-CONNECTOR-CONTROL-R071).
30
+ # The one declared connector identity: the fleet name (SCION-CONNECTOR-CONTROL-R071).
31
31
  [project.entry-points."agent_connector_sdk.connector"]
32
32
  nextcloud-mcp = "nextcloud_agent:connector"
33
33
 
@@ -1,10 +1,13 @@
1
1
  import os
2
+ from contextlib import contextmanager
2
3
  from unittest.mock import patch
3
4
 
4
5
  import pytest
5
6
  from agent_connector_sdk.exceptions import AuthError, UnauthorizedError
6
7
 
7
- from nextcloud_agent.auth import get_client
8
+ from nextcloud_agent.auth import get_client as _get_client
9
+
10
+ get_client = contextmanager(_get_client)
8
11
 
9
12
 
10
13
  @pytest.mark.concept("OS-5.1")
@@ -57,3 +60,18 @@ def test_auth_coverage():
57
60
  with pytest.raises(RuntimeError, match="AUTHENTICATION ERROR"):
58
61
  with get_client(base_url="https://x.com", username="u", password="p"):
59
62
  pass
63
+
64
+
65
+ @pytest.mark.concept("OS-5.1")
66
+ def test_get_client_is_a_generator_dependency_that_yields_the_client():
67
+ """``Depends(get_client)`` must resolve to the client, not a context manager."""
68
+ import inspect
69
+
70
+ from nextcloud_agent.auth import get_client as dependency
71
+
72
+ assert inspect.isgeneratorfunction(dependency)
73
+ gen = dependency(base_url="https://x.test", username="u", password="p")
74
+ client = next(gen)
75
+ assert client.base_url == "https://x.test"
76
+ assert not hasattr(client, "__enter__") or hasattr(client, "get_user_info")
77
+ gen.close()
@@ -24,9 +24,9 @@ class _FakeTransport:
24
24
  self.requests: list[Any] = []
25
25
 
26
26
  async def source_status(self, connector, stream):
27
-
28
- del connector, stream
29
- return SimpleNamespace(accepted_checkpoint=None)
27
+ return SimpleNamespace(
28
+ connector=connector, stream=stream, accepted_checkpoint=None
29
+ )
30
30
 
31
31
  async def submit(self, request):
32
32
  self.requests.append(request)
@@ -72,7 +72,10 @@ async def test_ingest_listing_maps_files_and_folders(ingest):
72
72
  {"name": "sub", "is_folder": True, "file_id": "11"},
73
73
  ]
74
74
  res = await ingest_listing(entries, parent_path="Documents", ingest=service)
75
- assert res == {"nodes": 3, "edges": 2} # parent folder + file + subfolder, 2 inFolder
75
+ assert res == {
76
+ "nodes": 3,
77
+ "edges": 2,
78
+ } # parent folder + file + subfolder, 2 inFolder
76
79
 
77
80
  records = {r.record_id: r for r in transport.requests[0].records}
78
81
  file_node = records["nextcloud:file:10"]
@@ -92,7 +95,14 @@ async def test_ingest_listing_maps_files_and_folders(ingest):
92
95
  async def test_ingest_shares_maps_share_and_resource(ingest):
93
96
  service, transport = ingest
94
97
  res = await ingest_shares(
95
- [{"id": "42", "path": "/Documents/report.pdf", "share_type": 3, "share_with": None}],
98
+ [
99
+ {
100
+ "id": "42",
101
+ "path": "/Documents/report.pdf",
102
+ "share_type": 3,
103
+ "share_with": None,
104
+ }
105
+ ],
96
106
  ingest=service,
97
107
  )
98
108
  assert res is not None
@@ -0,0 +1,57 @@
1
+ """Keep the connector's shared Pages and publication contracts explicit."""
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+ import tomllib
7
+ import yaml
8
+
9
+ ROOT = Path(__file__).resolve().parents[1]
10
+
11
+
12
+ def _workflow(name: str):
13
+ return yaml.safe_load((ROOT / ".github/workflows" / name).read_text())
14
+
15
+
16
+ def _actions_on(workflow):
17
+ # PyYAML's YAML 1.1 reader represents the Actions ``on`` key as True.
18
+ return workflow["on"] if "on" in workflow else workflow[True]
19
+
20
+
21
+ def test_pages_publishes_spec_dashboard_and_supports_manual_refresh():
22
+
23
+ config = json.loads((ROOT / ".github/spec-dashboard.json").read_text())
24
+ assert config == {
25
+ "version": 1,
26
+ "sources": [{"glob": "specs/*/status.json", "kind": "spec"}],
27
+ }
28
+
29
+
30
+ def test_single_pages_publisher_and_release_flags_remain_canonical():
31
+ assert not (ROOT / ".github/workflows/docs.yml").exists()
32
+
33
+
34
+ def test_development_skill_and_runtime_entrypoints_match_source_tree():
35
+ skill = ROOT / "nextcloud_agent/skills/nextcloud-agent-development/SKILL.md"
36
+ content = skill.read_text()
37
+
38
+ assert "name: nextcloud-agent-development" in content
39
+ assert "## Rapid delivery: the 5-minute contract" in content
40
+ for path in (
41
+ "nextcloud_agent/__init__.py",
42
+ "nextcloud_agent/auth.py",
43
+ "nextcloud_agent/mcp_server.py",
44
+ "nextcloud_agent/api_client.py",
45
+ "nextcloud_agent/api/api_client_base.py",
46
+ "tests/test_auth.py",
47
+ "tests/test_mcp_server.py",
48
+ "nextcloud_agent/skills/nextcloud-files/SKILL.md",
49
+ "nextcloud_agent/ontology/nextcloud.ttl",
50
+ ):
51
+ assert (ROOT / path).exists(), path
52
+
53
+ project = tomllib.loads((ROOT / "pyproject.toml").read_text())["project"]
54
+ assert project["scripts"]["nextcloud-mcp"] == "agent_connector_sdk.cli:serve"
55
+ assert project["entry-points"]["agent_connector_sdk.connector"][
56
+ "nextcloud-mcp"
57
+ ] == ("nextcloud_agent:connector")
File without changes