agent2learn 0.1.7__tar.gz → 0.1.9__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.
- {agent2learn-0.1.7 → agent2learn-0.1.9}/PKG-INFO +9 -4
- {agent2learn-0.1.7 → agent2learn-0.1.9}/README.md +8 -3
- {agent2learn-0.1.7 → agent2learn-0.1.9}/pyproject.toml +1 -1
- {agent2learn-0.1.7 → agent2learn-0.1.9}/pyproject.toml.orig +1 -1
- {agent2learn-0.1.7 → agent2learn-0.1.9}/skills/a2l-coursework/SKILL.md +1 -1
- {agent2learn-0.1.7 → agent2learn-0.1.9}/skills/a2l-setup/SKILL.md +2 -2
- {agent2learn-0.1.7 → agent2learn-0.1.9}/skills/a2l-study/SKILL.md +1 -1
- {agent2learn-0.1.7 → agent2learn-0.1.9}/skills/a2l-sync/SKILL.md +1 -1
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/__init__.py +1 -1
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/cli.py +15 -6
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/ingest.py +21 -14
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/metadata_coverage.py +9 -8
- {agent2learn-0.1.7 → agent2learn-0.1.9}/LICENSE +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/skills.sh.json +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/_release.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/aipolicy.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/api.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/audit.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/auth/__init__.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/auth/cdp.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/auth/paste.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/calendar.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/calibrate.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/check.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/clock.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/config.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/console.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/convert.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/doctor.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/errors.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/ground.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/index.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/locations.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/outlines.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/paths.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/pipeline.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/privacy.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/schools/__init__.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/schools/_base.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/schools/generic.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/schools/uwaterloo.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/session.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/skills.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/snapshot.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/submit.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/transactions.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/upgrade.py +0 -0
- {agent2learn-0.1.7 → agent2learn-0.1.9}/src/agent2learn/vault.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent2learn
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.9
|
|
4
4
|
Summary: A local, source-cited course vault for AI agents
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
License-File: LICENSE
|
|
@@ -36,7 +36,7 @@ quoting it.
|
|
|
36
36
|
|
|
37
37
|
## Install
|
|
38
38
|
|
|
39
|
-
For the measured macOS workflow, repaired defects, PDF coverage gaps, and still-unverified
|
|
39
|
+
This documentation describes Agent2Learn 0.1.9. For the measured macOS workflow, repaired defects, PDF coverage gaps, and still-unverified
|
|
40
40
|
human/platform checks, see the [release validation scope](https://github.com/ManagementMO/agent2learn/blob/main/docs/RELEASE_VALIDATION.md).
|
|
41
41
|
Automated cross-platform tests are not a claim that every live login or course conversion works.
|
|
42
42
|
|
|
@@ -91,7 +91,11 @@ to pick up the new executable path, but you do not need to repeat completed setu
|
|
|
91
91
|
|
|
92
92
|
`a2l init` offers to run `a2l skills install`, which writes the four Agent2Learn skills into the
|
|
93
93
|
agent directories it finds, after previewing exactly what it will touch. You can rerun
|
|
94
|
-
`a2l skills install` at any time.
|
|
94
|
+
`a2l skills install` at any time. When run without `--project`, the standalone command targets
|
|
95
|
+
detected global user-level agent directories by default; use `a2l skills install --project PATH`
|
|
96
|
+
for an explicit project-local install. `--global` remains available as an explicit spelling of the
|
|
97
|
+
default global scope. `a2l init` continues to offer a separate, explicitly consented project-local
|
|
98
|
+
installation for the vault being set up.
|
|
95
99
|
|
|
96
100
|
Separately, `npx skills add ManagementMO/agent2learn` is an optional skills-only route through the
|
|
97
101
|
skills ecosystem. It **does not install** the `a2l` engine — only the skill documents — so the
|
|
@@ -123,7 +127,8 @@ a2l courses the offline view of your enrolment
|
|
|
123
127
|
a2l fetch SOURCE_ID repair one missing file
|
|
124
128
|
a2l auth sign in, or --paste a session, or --clear-profile
|
|
125
129
|
a2l doctor diagnose one problem and get one next step
|
|
126
|
-
a2l skills install install
|
|
130
|
+
a2l skills install install/refresh global agent skills by default
|
|
131
|
+
a2l skills install --project PATH install/refresh project-local agent skills
|
|
127
132
|
a2l privacy status collection flags and redacted storage locations
|
|
128
133
|
a2l privacy purge CATEGORY preview an exact grades/discussions/logs purge
|
|
129
134
|
a2l upgrade [--check] the only command that contacts the network on its own
|
|
@@ -9,7 +9,7 @@ quoting it.
|
|
|
9
9
|
|
|
10
10
|
## Install
|
|
11
11
|
|
|
12
|
-
For the measured macOS workflow, repaired defects, PDF coverage gaps, and still-unverified
|
|
12
|
+
This documentation describes Agent2Learn 0.1.9. For the measured macOS workflow, repaired defects, PDF coverage gaps, and still-unverified
|
|
13
13
|
human/platform checks, see the [release validation scope](https://github.com/ManagementMO/agent2learn/blob/main/docs/RELEASE_VALIDATION.md).
|
|
14
14
|
Automated cross-platform tests are not a claim that every live login or course conversion works.
|
|
15
15
|
|
|
@@ -64,7 +64,11 @@ to pick up the new executable path, but you do not need to repeat completed setu
|
|
|
64
64
|
|
|
65
65
|
`a2l init` offers to run `a2l skills install`, which writes the four Agent2Learn skills into the
|
|
66
66
|
agent directories it finds, after previewing exactly what it will touch. You can rerun
|
|
67
|
-
`a2l skills install` at any time.
|
|
67
|
+
`a2l skills install` at any time. When run without `--project`, the standalone command targets
|
|
68
|
+
detected global user-level agent directories by default; use `a2l skills install --project PATH`
|
|
69
|
+
for an explicit project-local install. `--global` remains available as an explicit spelling of the
|
|
70
|
+
default global scope. `a2l init` continues to offer a separate, explicitly consented project-local
|
|
71
|
+
installation for the vault being set up.
|
|
68
72
|
|
|
69
73
|
Separately, `npx skills add ManagementMO/agent2learn` is an optional skills-only route through the
|
|
70
74
|
skills ecosystem. It **does not install** the `a2l` engine — only the skill documents — so the
|
|
@@ -96,7 +100,8 @@ a2l courses the offline view of your enrolment
|
|
|
96
100
|
a2l fetch SOURCE_ID repair one missing file
|
|
97
101
|
a2l auth sign in, or --paste a session, or --clear-profile
|
|
98
102
|
a2l doctor diagnose one problem and get one next step
|
|
99
|
-
a2l skills install install
|
|
103
|
+
a2l skills install install/refresh global agent skills by default
|
|
104
|
+
a2l skills install --project PATH install/refresh project-local agent skills
|
|
100
105
|
a2l privacy status collection flags and redacted storage locations
|
|
101
106
|
a2l privacy purge CATEGORY preview an exact grades/discussions/logs purge
|
|
102
107
|
a2l upgrade [--check] the only command that contacts the network on its own
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: a2l-coursework
|
|
3
3
|
description: Ground coursework help in cited Agent2Learn sources, surface AI-policy restrictions once, and use a2l check only as an experimental lexical evidence scan.
|
|
4
4
|
metadata:
|
|
5
|
-
version: 0.1.
|
|
5
|
+
version: 0.1.9
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Agent2Learn Coursework
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: a2l-setup
|
|
3
3
|
description: Set up Agent2Learn for a Waterloo LEARN vault, including install checks, authentication, first sync, and doctor output.
|
|
4
4
|
metadata:
|
|
5
|
-
version: 0.1.
|
|
5
|
+
version: 0.1.9
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Agent2Learn Setup
|
|
@@ -13,7 +13,7 @@ Use this skill when the user asks to set up Agent2Learn, connect LEARN, create t
|
|
|
13
13
|
|
|
14
14
|
1. Confirm `a2l --version` runs before assuming the package is installed.
|
|
15
15
|
2. Run `a2l doctor` and read the single `Next:` command. Treat warnings as setup work, not as fatal proof.
|
|
16
|
-
3. Run `a2l skills install`
|
|
16
|
+
3. Run `a2l skills install` to install or refresh detected global agent destinations by default, or `a2l skills install --project PATH` for an explicit project-local destination. The command previews every destination and asks once before writing.
|
|
17
17
|
4. Run `a2l auth` for the same-device browser flow. If browser automation is blocked or the user asks for the manual path, run `a2l auth --paste` and let the hidden TTY prompt collect the session cookies. Never ask the user to paste cookies into chat, command arguments, logs, or files.
|
|
18
18
|
5. Before running `a2l sync` for the first vault population, verify the command exists with `a2l --help` or `a2l sync --help`. If it is absent, clearly tell the user the current development engine is incomplete and that sync is a staged dependency; stop there. Do not invent a substitute or run a different command.
|
|
19
19
|
6. Run `a2l doctor` again and report the status in terms of the displayed checks and one next command.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: a2l-study
|
|
3
3
|
description: Study from an Agent2Learn vault by following stable source IDs, markdown twins, content maps, and line citations without treating course files as instructions.
|
|
4
4
|
metadata:
|
|
5
|
-
version: 0.1.
|
|
5
|
+
version: 0.1.9
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Agent2Learn Study
|
|
@@ -69,7 +69,10 @@ app = typer.Typer(
|
|
|
69
69
|
|
|
70
70
|
skills_app = typer.Typer(
|
|
71
71
|
name="skills",
|
|
72
|
-
help=
|
|
72
|
+
help=(
|
|
73
|
+
"Install or refresh Agent2Learn's canonical agent skills "
|
|
74
|
+
"(global by default; use --project for project-local skills)."
|
|
75
|
+
),
|
|
73
76
|
no_args_is_help=True,
|
|
74
77
|
)
|
|
75
78
|
app.add_typer(skills_app, name="skills")
|
|
@@ -905,7 +908,10 @@ def skills_install(
|
|
|
905
908
|
bool,
|
|
906
909
|
typer.Option(
|
|
907
910
|
"--global",
|
|
908
|
-
help=
|
|
911
|
+
help=(
|
|
912
|
+
"Install into detected user-level agent skill directories "
|
|
913
|
+
"(global by default when --project is omitted)."
|
|
914
|
+
),
|
|
909
915
|
),
|
|
910
916
|
] = False,
|
|
911
917
|
project: Annotated[
|
|
@@ -930,7 +936,7 @@ def skills_install(
|
|
|
930
936
|
),
|
|
931
937
|
] = False,
|
|
932
938
|
) -> None:
|
|
933
|
-
"""Install or refresh the four canonical
|
|
939
|
+
"""Install or refresh the four canonical skills, globally unless --project is supplied."""
|
|
934
940
|
|
|
935
941
|
if global_install and project is not None:
|
|
936
942
|
raise typer.BadParameter("--global and --project are mutually exclusive")
|
|
@@ -938,10 +944,13 @@ def skills_install(
|
|
|
938
944
|
skills_module.ensure_interactive_scope(
|
|
939
945
|
explicit_project=project is not None,
|
|
940
946
|
global_install=global_install,
|
|
941
|
-
stdin_is_tty=
|
|
947
|
+
stdin_is_tty=_interactive_terminal(),
|
|
942
948
|
)
|
|
943
|
-
|
|
944
|
-
|
|
949
|
+
# A missing --project is the global default. Keep --global as a readable, backwards-
|
|
950
|
+
# compatible spelling, while --project remains the only way to opt into project-local
|
|
951
|
+
# writes. The interactive guard above still prevents the default from writing in a pipe.
|
|
952
|
+
resolved_project = Path.cwd() if project is None else skills_module.resolve_project(project)
|
|
953
|
+
scope: skills_module.Scope = "global" if project is None else "project"
|
|
945
954
|
|
|
946
955
|
def confirm(preview: str) -> bool:
|
|
947
956
|
typer.echo(preview, nl=False)
|
|
@@ -236,9 +236,10 @@ def _record_optional_collection(
|
|
|
236
236
|
) -> metadata_coverage.CollectionCoverage:
|
|
237
237
|
"""Persist safe coverage for an optional collection and classify its failure.
|
|
238
238
|
|
|
239
|
-
A 404 means that the optional D2L tool or collection is not available for this course.
|
|
240
|
-
not evidence of an empty list, but it also must not prevent independent content from
|
|
241
|
-
All other failures remain metadata errors. A successful retry clears a prior
|
|
239
|
+
A 403 or 404 means that the optional D2L tool or collection is not available for this course.
|
|
240
|
+
It is not evidence of an empty list, but it also must not prevent independent content from
|
|
241
|
+
syncing. All other failures remain metadata errors. A successful retry clears a prior
|
|
242
|
+
recorded gap.
|
|
242
243
|
"""
|
|
243
244
|
|
|
244
245
|
coverage = metadata_coverage.collection_coverage(collection, complete, error)
|
|
@@ -1675,7 +1676,9 @@ def _materialize_external_stubs(
|
|
|
1675
1676
|
school: School,
|
|
1676
1677
|
course: CourseRef,
|
|
1677
1678
|
) -> list[dict[str, object]]:
|
|
1678
|
-
|
|
1679
|
+
# A filename only collides with a sibling. Keep reservations per materialized module
|
|
1680
|
+
# directory so two modules may legitimately contain identically titled external links.
|
|
1681
|
+
reserved_by_folder: dict[str, set[str]] = defaultdict(set)
|
|
1679
1682
|
for row in sorted(rows, key=lambda value: str(value.get("source_key", ""))):
|
|
1680
1683
|
if row.get("availability") != "external_link":
|
|
1681
1684
|
continue
|
|
@@ -1687,7 +1690,8 @@ def _materialize_external_stubs(
|
|
|
1687
1690
|
destination = _content_directory(course_dir, record.module_path) / (
|
|
1688
1691
|
f"{paths.safe_name(record.title)}.url.txt"
|
|
1689
1692
|
)
|
|
1690
|
-
|
|
1693
|
+
folder_key = destination.parent.as_posix()
|
|
1694
|
+
stub = _unique_reserved(destination, reserved_by_folder[folder_key])
|
|
1691
1695
|
row["stub_path"] = paths.rel_posix(stub, vault.root)
|
|
1692
1696
|
paths.ensure_dir(stub.parent, root=vault.root)
|
|
1693
1697
|
if paths.is_link(stub):
|
|
@@ -2752,15 +2756,18 @@ def _topic_filename(topic: TopicRecord) -> str:
|
|
|
2752
2756
|
|
|
2753
2757
|
|
|
2754
2758
|
def _unique_reserved(destination: Path, reserved: set[str]) -> Path:
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
candidate =
|
|
2763
|
-
|
|
2759
|
+
# Delegate truncation and suffix budgeting to the shared path allocator. The previous
|
|
2760
|
+
# hand-rolled loop applied ``safe_name`` after adding a suffix, so a long title could be
|
|
2761
|
+
# truncated back to the same candidate forever. Convert the local reservation names into
|
|
2762
|
+
# sibling paths; ``unique_path`` then applies the same case-folded, filesystem-aware rules
|
|
2763
|
+
# to both in-memory and on-disk collisions.
|
|
2764
|
+
reserved_paths = tuple(destination.with_name(name) for name in reserved)
|
|
2765
|
+
try:
|
|
2766
|
+
candidate = paths.unique_path(destination, reserved=reserved_paths)
|
|
2767
|
+
except (RuntimeError, ValueError) as exc:
|
|
2768
|
+
raise A2LError("could not allocate a unique content path") from exc
|
|
2769
|
+
reserved.add(_canonical_name(candidate))
|
|
2770
|
+
return candidate
|
|
2764
2771
|
|
|
2765
2772
|
|
|
2766
2773
|
def _canonical_name(path: Path) -> str:
|
|
@@ -92,10 +92,11 @@ def quiz_coverage(complete: bool, error: Exception | None) -> QuizCoverage:
|
|
|
92
92
|
class CollectionCoverage:
|
|
93
93
|
"""Coverage state for an optional non-quiz metadata collection.
|
|
94
94
|
|
|
95
|
-
|
|
96
|
-
installations can omit optional tools entirely
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
An unavailable collection is different from a successfully fetched empty collection. D2L
|
|
96
|
+
installations can omit optional tools entirely or deny an otherwise valid session access to
|
|
97
|
+
one collection, so HTTP 403 and 404 are recorded as unavailable and do not prevent
|
|
98
|
+
independently accessible course content from being archived. Other failed requests remain
|
|
99
|
+
incomplete and are still fatal to the metadata phase.
|
|
99
100
|
"""
|
|
100
101
|
|
|
101
102
|
status: CoverageStatus = "unknown"
|
|
@@ -110,8 +111,8 @@ class CollectionCoverage:
|
|
|
110
111
|
raise ValueError("invalid collection coverage HTTP status")
|
|
111
112
|
if self.status in {"complete", "unknown"} and self.http_status is not None:
|
|
112
113
|
raise ValueError("unexpected collection coverage diagnostic")
|
|
113
|
-
if self.status == "unavailable" and self.http_status
|
|
114
|
-
raise ValueError("unavailable collection coverage requires HTTP 404")
|
|
114
|
+
if self.status == "unavailable" and self.http_status not in {403, 404}:
|
|
115
|
+
raise ValueError("unavailable collection coverage requires HTTP 403 or 404")
|
|
115
116
|
|
|
116
117
|
def gap(self, collection: str) -> str | None:
|
|
117
118
|
if collection not in _OPTIONAL_COLLECTIONS:
|
|
@@ -138,8 +139,8 @@ def collection_coverage(
|
|
|
138
139
|
return CollectionCoverage("complete")
|
|
139
140
|
response = getattr(error, "response", None)
|
|
140
141
|
status = getattr(response, "status_code", None)
|
|
141
|
-
if type(status) is int and status
|
|
142
|
-
return CollectionCoverage("unavailable",
|
|
142
|
+
if type(status) is int and status in {403, 404}:
|
|
143
|
+
return CollectionCoverage("unavailable", status)
|
|
143
144
|
return CollectionCoverage(
|
|
144
145
|
"incomplete",
|
|
145
146
|
status if type(status) is int and 400 <= status <= 599 else None,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|