agent-memory-cli 0.1.0__tar.gz → 0.1.1__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.
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/CHANGELOG.md +15 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/PKG-INFO +4 -2
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/README.md +2 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/pyproject.toml +1 -1
- agent_memory_cli-0.1.1/scripts/release_notes.py +78 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/__init__.py +1 -1
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/cli.py +112 -1
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/doctor.py +35 -9
- agent_memory_cli-0.1.1/src/agent_memory/events.py +409 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/layout.py +17 -1
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/sync.py +108 -24
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/golden/canonical_memory_gitignore.txt +2 -2
- agent_memory_cli-0.1.1/tests/golden/event_record.md +14 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/test_cli.py +3 -2
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/test_doctor.py +68 -0
- agent_memory_cli-0.1.1/tests/test_events.py +706 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/test_layout.py +17 -1
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/test_sync.py +176 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/.gitignore +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/LICENSE +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/Makefile +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/scripts/check_installed_wheel.py +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/__main__.py +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/archive.py +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/debrief.py +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/git_runner.py +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/home.py +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/org.py +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/publication.py +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/setup/__init__.py +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/setup/claude.py +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/setup/codex.py +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/setup/common.py +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/setup/legacy.py +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/startup.py +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/status.py +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/templates/org-memory/decisions.md +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/templates/org-memory/recent.md +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/templates/org-memory/rules.md +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/templates/project-memory/decision_log.md +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/templates/project-memory/known_debt.md +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/templates/project-memory/open_threads.md +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/templates/project-memory/project_facts.md +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/templates/workflow/SKILL.md +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/src/agent_memory/workflow.py +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/conformance/org_memory/cases/bad_layout/expected.json +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/conformance/org_memory/cases/bad_layout/org-memory/debriefs/.hidden/2026/08/20260825-alice-abc12345.md +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/conformance/org_memory/cases/bad_layout/org-memory/debriefs/demo-project/2026/08/20260825-alice-1f3a9c2b.md +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/conformance/org_memory/cases/bad_layout/org-memory/debriefs/demo-project/2026/08/20260825-alice-ABCD.md +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/conformance/org_memory/cases/bad_layout/org-memory/debriefs/demo-project/2026/08/20260825_alice_abc12345.md +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/conformance/org_memory/cases/bad_layout/org-memory/debriefs/demo-project/2026/09/20260825-alice-def12345.md +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/conformance/org_memory/cases/bad_layout/org-memory/debriefs/demo-project/20260825-alice-abc12345.md +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/conformance/org_memory/cases/empty_store/expected.json +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/conformance/org_memory/cases/empty_store/org-memory/debriefs/.gitkeep +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/conformance/org_memory/cases/missing_debriefs_dir/expected.json +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/conformance/org_memory/cases/missing_debriefs_dir/org-memory/recent.md +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/conformance/org_memory/cases/staging_artifact/expected.json +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/conformance/org_memory/cases/staging_artifact/org-memory/debriefs/demo-project/2026/08/.stage.20260825-alice-77xx88yy.md.a1b2c3 +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/conformance/org_memory/cases/staging_artifact/org-memory/debriefs/demo-project/2026/08/20260825-alice-1f3a9c2b.md +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/conformance/org_memory/cases/valid_store/expected.json +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/conformance/org_memory/cases/valid_store/org-memory/debriefs/.gitkeep +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/conformance/org_memory/cases/valid_store/org-memory/debriefs/Demo_Project/2026/08/20260825-bob-ops-9f00aa11.md +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/conformance/org_memory/cases/valid_store/org-memory/debriefs/demo-project/2026/08/20260825-alice-1f3a9c2b.md +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/conformance/org_memory/cases/valid_store/org-memory/debriefs/demo.project/2026/07/20260701-Alice_2.dev-00aa11bb.md +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/conformance/org_memory/cases/valid_store/org-memory/debriefs/other-project/2026/12/20261203-bob-9e0d44aa.md +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/conftest.py +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/fixtures/setup/claude_settings_fleet.json +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/fixtures/setup/codex_hooks_kernel.json +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/golden/debrief_record.md +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/golden/doctor_memory_0.4.5.json +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/golden/doctor_memory_0.4.5.txt +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/test_archive.py +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/test_debrief.py +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/test_home.py +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/test_org.py +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/test_setup.py +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/test_startup.py +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/test_status.py +0 -0
- {agent_memory_cli-0.1.0 → agent_memory_cli-0.1.1}/tests/test_workflow.py +0 -0
|
@@ -4,6 +4,21 @@ All notable changes to agent-memory are documented in this file. The format
|
|
|
4
4
|
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the
|
|
5
5
|
project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## 0.1.1 - 2026-09-12
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `event write` publishes one org-memory event under `org-memory/events/`, never replacing a published record ([event](docs/commands.md#event-write)).
|
|
12
|
+
- A `v*` tag attaches the built sdist and wheel to the GitHub Release ([releases](https://github.com/kiloloop/agent-memory/releases)).
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- A nested `.gitignore` or `.oacp-memory-repo` no longer blocks `pull`; the allowlist anchors both names to the home root ([sync](docs/commands.md#sync)).
|
|
17
|
+
- `enable` retires superseded lines when it updates a home's `.gitignore`, and the receipt names them ([sync](docs/commands.md#sync)).
|
|
18
|
+
- `doctor` scopes its working-tree row to the allowlist: memory changes read DIRTY, and changes elsewhere are named as outside it ([doctor](docs/commands.md#doctor)).
|
|
19
|
+
- `push` and `doctor` keep both paths of a rename git detects in the working tree; the source path was previously misread ([sync](docs/commands.md#sync)).
|
|
20
|
+
- `push` names a credential helper a sandbox blocked as its own failure, with the remedy, instead of reporting it as a rejected push ([sync](docs/commands.md#sync)).
|
|
21
|
+
|
|
7
22
|
## 0.1.0 - 2026-09-06
|
|
8
23
|
|
|
9
24
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: agent-memory-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: Cross-session memory for coding agents: plain files, git-native, no server
|
|
5
5
|
Project-URL: Homepage, https://github.com/kiloloop/agent-memory
|
|
6
6
|
Project-URL: Source, https://github.com/kiloloop/agent-memory
|
|
@@ -24,7 +24,7 @@ Requires-Python: >=3.10
|
|
|
24
24
|
Provides-Extra: dev
|
|
25
25
|
Requires-Dist: build<2,>=1.2; extra == 'dev'
|
|
26
26
|
Requires-Dist: pytest<10,>=8; extra == 'dev'
|
|
27
|
-
Requires-Dist: ruff<
|
|
27
|
+
Requires-Dist: ruff<0.17,>=0.16; extra == 'dev'
|
|
28
28
|
Requires-Dist: twine<8,>=7; extra == 'dev'
|
|
29
29
|
Description-Content-Type: text/markdown
|
|
30
30
|
|
|
@@ -177,6 +177,7 @@ similarity queries, synthesis, or indexing. Recall reads a fixed file set.
|
|
|
177
177
|
| [`capture`][capture-and-recall] | Record a decision. |
|
|
178
178
|
| [`recall`][capture-and-recall] | Read bounded context. |
|
|
179
179
|
| [`debrief write`][debrief-write] | Publish a session summary. |
|
|
180
|
+
| [`event write`][event-write] | Publish an org-memory event. |
|
|
180
181
|
|
|
181
182
|
[status]: https://github.com/kiloloop/agent-memory/blob/main/docs/commands.md#status
|
|
182
183
|
[doctor]: https://github.com/kiloloop/agent-memory/blob/main/docs/commands.md#doctor
|
|
@@ -187,6 +188,7 @@ similarity queries, synthesis, or indexing. Recall reads a fixed file set.
|
|
|
187
188
|
[startup]: https://github.com/kiloloop/agent-memory/blob/main/docs/commands.md#startup
|
|
188
189
|
[capture-and-recall]: https://github.com/kiloloop/agent-memory/blob/main/docs/commands.md#capture-and-recall
|
|
189
190
|
[debrief-write]: https://github.com/kiloloop/agent-memory/blob/main/docs/commands.md#debrief-write
|
|
191
|
+
[event-write]: https://github.com/kiloloop/agent-memory/blob/main/docs/commands.md#event-write
|
|
190
192
|
|
|
191
193
|
## Examples
|
|
192
194
|
|
|
@@ -147,6 +147,7 @@ similarity queries, synthesis, or indexing. Recall reads a fixed file set.
|
|
|
147
147
|
| [`capture`][capture-and-recall] | Record a decision. |
|
|
148
148
|
| [`recall`][capture-and-recall] | Read bounded context. |
|
|
149
149
|
| [`debrief write`][debrief-write] | Publish a session summary. |
|
|
150
|
+
| [`event write`][event-write] | Publish an org-memory event. |
|
|
150
151
|
|
|
151
152
|
[status]: https://github.com/kiloloop/agent-memory/blob/main/docs/commands.md#status
|
|
152
153
|
[doctor]: https://github.com/kiloloop/agent-memory/blob/main/docs/commands.md#doctor
|
|
@@ -157,6 +158,7 @@ similarity queries, synthesis, or indexing. Recall reads a fixed file set.
|
|
|
157
158
|
[startup]: https://github.com/kiloloop/agent-memory/blob/main/docs/commands.md#startup
|
|
158
159
|
[capture-and-recall]: https://github.com/kiloloop/agent-memory/blob/main/docs/commands.md#capture-and-recall
|
|
159
160
|
[debrief-write]: https://github.com/kiloloop/agent-memory/blob/main/docs/commands.md#debrief-write
|
|
161
|
+
[event-write]: https://github.com/kiloloop/agent-memory/blob/main/docs/commands.md#event-write
|
|
160
162
|
|
|
161
163
|
## Examples
|
|
162
164
|
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# SPDX-FileCopyrightText: 2026 Kiloloop
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
"""Print the changelog section for one release, as GitHub Release notes.
|
|
5
|
+
|
|
6
|
+
The release workflow feeds this to ``gh release create --notes-file``. GitHub's own
|
|
7
|
+
``--generate-notes`` cannot serve here: the public repository receives each version as a
|
|
8
|
+
single squashed commit, so the generated summary is one line no matter how much the
|
|
9
|
+
release contains. The changelog section already says what shipped, one line per change.
|
|
10
|
+
|
|
11
|
+
Two transformations make a changelog section stand alone on a release page:
|
|
12
|
+
|
|
13
|
+
1. Relative links (``docs/commands.md#init``) are rewritten against the tag. A release
|
|
14
|
+
body is not rendered relative to the repository root, so a relative target 404s; and
|
|
15
|
+
pinning to the tag rather than to ``main`` keeps old release notes pointing at the
|
|
16
|
+
documentation as it stood for that version.
|
|
17
|
+
2. The section heading itself is dropped — the release already carries the version.
|
|
18
|
+
|
|
19
|
+
Exits 1 when the section is absent, so the caller can fall back rather than publish a
|
|
20
|
+
release with an empty body.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import argparse
|
|
26
|
+
import re
|
|
27
|
+
import sys
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
|
|
30
|
+
LINK = re.compile(r"\]\(([^)]+)\)")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def extract(changelog: str, version: str) -> str | None:
|
|
34
|
+
"""Return the body of ``## <version>``, or None when no such section exists."""
|
|
35
|
+
lines = changelog.split("\n")
|
|
36
|
+
start = None
|
|
37
|
+
for i, line in enumerate(lines):
|
|
38
|
+
# "## 0.1.0 - 2026-09-06" and "## [0.1.0] - ..." both name this version.
|
|
39
|
+
if line.startswith("## ") and re.match(rf"##\s+\[?{re.escape(version)}\]?\b", line):
|
|
40
|
+
start = i + 1
|
|
41
|
+
break
|
|
42
|
+
if start is None:
|
|
43
|
+
return None
|
|
44
|
+
body: list[str] = []
|
|
45
|
+
for line in lines[start:]:
|
|
46
|
+
if line.startswith("## "):
|
|
47
|
+
break
|
|
48
|
+
body.append(line)
|
|
49
|
+
return "\n".join(body).strip() or None
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def absolutise(notes: str, repository: str, tag: str) -> str:
|
|
53
|
+
"""Rewrite repo-relative link targets to absolute URLs pinned at ``tag``."""
|
|
54
|
+
base = f"https://github.com/{repository}/blob/{tag}/"
|
|
55
|
+
return LINK.sub(
|
|
56
|
+
lambda m: f"]({m.group(1)})" if m.group(1).startswith("http") else f"]({base}{m.group(1)})",
|
|
57
|
+
notes,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def main() -> int:
|
|
62
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
63
|
+
parser.add_argument("--tag", required=True, help="release tag, e.g. v0.1.0")
|
|
64
|
+
parser.add_argument("--repository", required=True, help="owner/repo, for absolute links")
|
|
65
|
+
parser.add_argument("--changelog", default="CHANGELOG.md", type=Path)
|
|
66
|
+
args = parser.parse_args()
|
|
67
|
+
|
|
68
|
+
version = args.tag[1:] if args.tag.startswith("v") else args.tag
|
|
69
|
+
notes = extract(args.changelog.read_text(encoding="utf-8"), version)
|
|
70
|
+
if notes is None:
|
|
71
|
+
print(f"no changelog section for {version} in {args.changelog}", file=sys.stderr)
|
|
72
|
+
return 1
|
|
73
|
+
print(absolutise(notes, args.repository, args.tag))
|
|
74
|
+
return 0
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
if __name__ == "__main__":
|
|
78
|
+
raise SystemExit(main())
|
|
@@ -10,7 +10,7 @@ import sys
|
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
from typing import Callable, List, Optional, Sequence, Tuple
|
|
12
12
|
|
|
13
|
-
from . import __version__, archive, debrief, doctor, org, setup, startup, status, sync, workflow
|
|
13
|
+
from . import __version__, archive, debrief, doctor, events, org, setup, startup, status, sync, workflow
|
|
14
14
|
from .home import BINDING_FILE, HomeError, HomeResolution, find_project, resolve_home
|
|
15
15
|
|
|
16
16
|
EXIT_OK = 0
|
|
@@ -189,6 +189,38 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
189
189
|
write.add_argument("--oacp-dir", dest="home", metavar="PATH", help=argparse.SUPPRESS)
|
|
190
190
|
write.add_argument("--dry-run", action="store_true", help="validate and compose the record, print it, and write nothing")
|
|
191
191
|
write.add_argument("--json", dest="json_output", action="store_true", help="emit a machine-readable result")
|
|
192
|
+
event_ = commands.add_parser("event", help="Org-memory event commands.", description="Org-memory event commands.")
|
|
193
|
+
event_commands = event_.add_subparsers(dest="event_command", metavar="<command>")
|
|
194
|
+
event_help = (
|
|
195
|
+
"Publish one org-memory event into the home's events store, failure-atomically: the canonical path only ever "
|
|
196
|
+
"holds a complete, verified record, byte-identical to the kernel's write-event script for the same inputs. "
|
|
197
|
+
"Exit 0 published (or an identical record was already there), 1 on a validation error, 2 on a publication "
|
|
198
|
+
"failure."
|
|
199
|
+
)
|
|
200
|
+
event_write = event_commands.add_parser("write", parents=[common], help=event_help, description=event_help)
|
|
201
|
+
event_write.set_defaults(handler=_event_write)
|
|
202
|
+
event_write.add_argument("--agent", required=True, help="agent creating the event")
|
|
203
|
+
event_write.add_argument("--project", required=True, help="originating project")
|
|
204
|
+
event_write.add_argument(
|
|
205
|
+
"--type", dest="event_type", required=True, choices=sorted(events.ALLOWED_TYPES), help="event type"
|
|
206
|
+
)
|
|
207
|
+
event_write.add_argument(
|
|
208
|
+
"--slug", required=True, help="short slug for the filename: lowercase alphanumerics and hyphens, no dots"
|
|
209
|
+
)
|
|
210
|
+
event_body = event_write.add_mutually_exclusive_group()
|
|
211
|
+
event_body.add_argument("--body", help="event body, inline")
|
|
212
|
+
event_body.add_argument("--body-file", help="path to the event body in Markdown, or '-' to read stdin")
|
|
213
|
+
event_write.add_argument("--source-ref", help="provenance id, e.g. the debrief stem the event was folded from")
|
|
214
|
+
event_write.add_argument(
|
|
215
|
+
"--related", help="cross-references: comma-separated or a JSON array (e.g. 'PR #43,issue #10')"
|
|
216
|
+
)
|
|
217
|
+
event_write.add_argument("--supersedes", help="path of the event this entry overrides")
|
|
218
|
+
# The kernel script's home flag; accepted, unadvertised, through v0.1.x.
|
|
219
|
+
event_write.add_argument("--oacp-dir", dest="home", metavar="PATH", help=argparse.SUPPRESS)
|
|
220
|
+
event_write.add_argument(
|
|
221
|
+
"--dry-run", action="store_true", help="validate and compose the record, print it, and write nothing"
|
|
222
|
+
)
|
|
223
|
+
event_write.add_argument("--json", dest="json_output", action="store_true", help="emit a machine-readable result")
|
|
192
224
|
return parser
|
|
193
225
|
|
|
194
226
|
|
|
@@ -414,6 +446,85 @@ def _debrief_write(args: argparse.Namespace) -> int:
|
|
|
414
446
|
return EXIT_OK
|
|
415
447
|
|
|
416
448
|
|
|
449
|
+
def _event_write(args: argparse.Namespace) -> int:
|
|
450
|
+
home = resolve_home(args.home).path
|
|
451
|
+
try:
|
|
452
|
+
body = _event_body(args)
|
|
453
|
+
except OSError as exc:
|
|
454
|
+
print(f"ERROR: cannot read body: {exc}", file=sys.stderr)
|
|
455
|
+
return EXIT_FAILED
|
|
456
|
+
except events.WriterError as exc:
|
|
457
|
+
print(f"ERROR: {exc}", file=sys.stderr)
|
|
458
|
+
return exc.code
|
|
459
|
+
|
|
460
|
+
try:
|
|
461
|
+
result = events.write_event(
|
|
462
|
+
home=home,
|
|
463
|
+
agent=args.agent,
|
|
464
|
+
project=args.project,
|
|
465
|
+
event_type=args.event_type,
|
|
466
|
+
slug=args.slug,
|
|
467
|
+
body=body,
|
|
468
|
+
source_ref=args.source_ref,
|
|
469
|
+
related=events.normalize_related(args.related) if args.related else None,
|
|
470
|
+
supersedes=args.supersedes,
|
|
471
|
+
dry_run=args.dry_run,
|
|
472
|
+
)
|
|
473
|
+
except events.WriterError as exc:
|
|
474
|
+
print(f"ERROR: {exc}", file=sys.stderr)
|
|
475
|
+
return exc.code
|
|
476
|
+
except OSError as exc:
|
|
477
|
+
print(f"ERROR: publication failed: {exc}", file=sys.stderr)
|
|
478
|
+
return 2
|
|
479
|
+
|
|
480
|
+
if args.json_output:
|
|
481
|
+
print(
|
|
482
|
+
json.dumps(
|
|
483
|
+
{
|
|
484
|
+
"path": str(result.path),
|
|
485
|
+
"status": result.status,
|
|
486
|
+
"created_at_utc": result.created_at_utc,
|
|
487
|
+
"date": result.created_at_utc[:10],
|
|
488
|
+
"agent": args.agent,
|
|
489
|
+
"project": args.project,
|
|
490
|
+
"type": args.event_type,
|
|
491
|
+
},
|
|
492
|
+
indent=2,
|
|
493
|
+
)
|
|
494
|
+
)
|
|
495
|
+
else:
|
|
496
|
+
print(f"{result.status}: {result.path}")
|
|
497
|
+
print(f"created_at_utc: {result.created_at_utc}")
|
|
498
|
+
|
|
499
|
+
if args.dry_run:
|
|
500
|
+
print("--- record preview (nothing was written) ---", file=sys.stderr)
|
|
501
|
+
sys.stderr.flush()
|
|
502
|
+
sys.stderr.buffer.write(result.record)
|
|
503
|
+
sys.stderr.buffer.flush()
|
|
504
|
+
return EXIT_OK
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
def _event_body(args: argparse.Namespace) -> bytes:
|
|
508
|
+
"""The body bytes: ``--body-file`` (a path, or ``-`` for stdin), ``--body``, else piped stdin.
|
|
509
|
+
|
|
510
|
+
Trailing newlines are dropped, as the kernel script drops them; the record
|
|
511
|
+
closes the body with exactly one. A body file is read as text, so its
|
|
512
|
+
CRLF and CR line endings become LF (:func:`events.body_from_file`); stdin
|
|
513
|
+
and the inline body are taken as given, both as the script does.
|
|
514
|
+
"""
|
|
515
|
+
if args.body_file is not None and args.body_file != "-":
|
|
516
|
+
return events.body_from_file(Path(args.body_file).expanduser().read_bytes())
|
|
517
|
+
if args.body_file is not None:
|
|
518
|
+
raw = sys.stdin.buffer.read()
|
|
519
|
+
elif args.body is not None:
|
|
520
|
+
raw = args.body.encode("utf-8")
|
|
521
|
+
elif not sys.stdin.isatty():
|
|
522
|
+
raw = sys.stdin.buffer.read()
|
|
523
|
+
else:
|
|
524
|
+
raise events.WriterError("no body provided: use --body, --body-file <path|->, or pipe the body to stdin", 1)
|
|
525
|
+
return raw.rstrip(b"\n")
|
|
526
|
+
|
|
527
|
+
|
|
417
528
|
def _report(outcome: sync.Outcome) -> int:
|
|
418
529
|
if outcome.lines:
|
|
419
530
|
print("\n".join(outcome.lines), file=sys.stdout if outcome.ok else sys.stderr)
|
|
@@ -444,15 +444,7 @@ def check_memory_sync(
|
|
|
444
444
|
state = None
|
|
445
445
|
|
|
446
446
|
if state is not None:
|
|
447
|
-
|
|
448
|
-
cat.add(
|
|
449
|
-
"working-tree",
|
|
450
|
-
Severity.warn,
|
|
451
|
-
"working tree — DIRTY memory changes present",
|
|
452
|
-
"Run `agent-memory push` or resolve changes manually",
|
|
453
|
-
)
|
|
454
|
-
else:
|
|
455
|
-
cat.add("working-tree", Severity.ok, "working tree — clean")
|
|
447
|
+
_add_working_tree(cat, home, runner, state)
|
|
456
448
|
|
|
457
449
|
text = f"sync state — {sync_state_text(state)}"
|
|
458
450
|
if not state.has_remote:
|
|
@@ -534,6 +526,40 @@ def check_memory_sync(
|
|
|
534
526
|
return cat
|
|
535
527
|
|
|
536
528
|
|
|
529
|
+
def _add_working_tree(cat: Category, home: Path, runner: Optional[GitRunner], state: GitState) -> None:
|
|
530
|
+
"""The tree row, scoped like the counts above it.
|
|
531
|
+
|
|
532
|
+
Memory changes inside the allowlist are what ``push`` would publish and
|
|
533
|
+
read DIRTY. Changes anywhere else are named as outside the allowlist: not
|
|
534
|
+
memory content, but ``pull`` still refuses a tree that carries them.
|
|
535
|
+
"""
|
|
536
|
+
if not state.dirty:
|
|
537
|
+
cat.add("working-tree", Severity.ok, "working tree — clean")
|
|
538
|
+
return
|
|
539
|
+
try:
|
|
540
|
+
changed = sync.changed_paths(home, runner)
|
|
541
|
+
except sync.SyncError as exc:
|
|
542
|
+
cat.add("working-tree", Severity.warn, f"working tree — DIRTY; the change readout failed: {exc}")
|
|
543
|
+
return
|
|
544
|
+
memory = [path for path in changed if layout.is_allowed_memory_path(path)]
|
|
545
|
+
elsewhere = [path for path in changed if not layout.is_allowed_memory_path(path)]
|
|
546
|
+
if memory:
|
|
547
|
+
text = f"working tree — DIRTY {len(memory)} memory change(s): {_summarize(memory)}"
|
|
548
|
+
if elsewhere:
|
|
549
|
+
text += f"; {len(elsewhere)} change(s) outside the memory allowlist"
|
|
550
|
+
cat.add("working-tree", Severity.warn, text, "Run `agent-memory push` or resolve changes manually")
|
|
551
|
+
elif elsewhere:
|
|
552
|
+
cat.add(
|
|
553
|
+
"working-tree",
|
|
554
|
+
Severity.warn,
|
|
555
|
+
f"working tree — {len(elsewhere)} change(s) outside the memory allowlist: {_summarize(elsewhere)}; "
|
|
556
|
+
"no memory change is pending, but memory pull refuses a dirty tree",
|
|
557
|
+
"Commit, stash or ignore them outside the memory tiers",
|
|
558
|
+
)
|
|
559
|
+
else:
|
|
560
|
+
cat.add("working-tree", Severity.ok, "working tree — clean")
|
|
561
|
+
|
|
562
|
+
|
|
537
563
|
def enclosing_repository(home: Path, runner: Optional[GitRunner] = None) -> Optional[Path]:
|
|
538
564
|
"""The worktree root when ``home`` sits inside a repository that is not its own; ``None`` when it is the root."""
|
|
539
565
|
root = sync.worktree_root(home, runner)
|