oacp-cli 0.1.2__tar.gz → 0.2.0__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 (115) hide show
  1. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/CHANGELOG.md +22 -0
  2. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/PKG-INFO +9 -5
  3. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/QUICKSTART.md +4 -2
  4. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/README.md +8 -4
  5. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/SPEC.md +40 -25
  6. oacp_cli-0.2.0/docs/protocol/agent_profiles.md +160 -0
  7. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/docs/protocol/cross_runtime_sync.md +8 -3
  8. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/docs/protocol/runtime_capabilities.md +1 -0
  9. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/docs/protocol/session_init.md +4 -1
  10. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/oacp/cli.py +9 -1
  11. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/pyproject.toml +9 -1
  12. oacp_cli-0.2.0/scripts/_oacp_constants.py +58 -0
  13. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/scripts/add_agent.py +70 -37
  14. oacp_cli-0.2.0/scripts/agent_profile.py +351 -0
  15. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/scripts/codex_session_init.py +3 -1
  16. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/scripts/handoff_schema.py +8 -4
  17. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/scripts/init_project_workspace.py +15 -8
  18. oacp_cli-0.2.0/scripts/memory_archive_common.py +67 -0
  19. oacp_cli-0.2.0/scripts/memory_cli.py +109 -0
  20. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/scripts/oacp_doctor.py +5 -22
  21. oacp_cli-0.2.0/scripts/oacp_inbox.py +245 -0
  22. oacp_cli-0.2.0/scripts/promote_to_archive.py +106 -0
  23. oacp_cli-0.2.0/scripts/restore_from_archive.py +96 -0
  24. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/scripts/send_inbox_message.py +129 -18
  25. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/scripts/session_lifecycle_hooks.py +3 -3
  26. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/scripts/setup_runtime.py +4 -31
  27. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/scripts/update_workspace.sh +23 -0
  28. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/scripts/validate_agent_card.py +54 -20
  29. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/scripts/validate_message.py +11 -4
  30. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/scripts/write_event.py +3 -1
  31. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/templates/agent_card.template.yaml +15 -0
  32. oacp_cli-0.2.0/templates/agent_profile.template.yaml +40 -0
  33. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/tests/test_add_agent.py +46 -0
  34. oacp_cli-0.2.0/tests/test_agent_profile.py +610 -0
  35. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/tests/test_codex_session_init.py +30 -0
  36. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/tests/test_handoff_schema.py +17 -0
  37. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/tests/test_init_project_workspace.py +17 -1
  38. oacp_cli-0.2.0/tests/test_memory_archive.py +222 -0
  39. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/tests/test_oacp_cli.py +22 -0
  40. oacp_cli-0.2.0/tests/test_oacp_constants.py +56 -0
  41. oacp_cli-0.2.0/tests/test_oacp_inbox.py +166 -0
  42. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/tests/test_send_inbox_message.py +169 -2
  43. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/tests/test_update_workspace.py +31 -2
  44. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/tests/test_validate_agent_card.py +79 -0
  45. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/tests/test_validate_message.py +6 -0
  46. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/.github/workflows/ci.yml +0 -0
  47. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/.github/workflows/release.yml +0 -0
  48. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/.gitignore +0 -0
  49. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/CONTRIBUTING.md +0 -0
  50. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/LICENSE +0 -0
  51. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/Makefile +0 -0
  52. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/SECURITY.md +0 -0
  53. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/docs/guides/adoption.md +0 -0
  54. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/docs/guides/doctor.md +0 -0
  55. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/docs/guides/prompt_caching.md +0 -0
  56. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/docs/guides/runtime_capability_matrix.md +0 -0
  57. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/docs/guides/setup.md +0 -0
  58. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/docs/guides/unified_skill_spec.md +0 -0
  59. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/docs/guides/versioning.md +0 -0
  60. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/docs/protocol/agent_safety_defaults.md +0 -0
  61. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/docs/protocol/credential_scoping.md +0 -0
  62. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/docs/protocol/dispatch_states.yaml +0 -0
  63. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/docs/protocol/inbox_outbox.md +0 -0
  64. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/docs/protocol/mcp_integration.md +0 -0
  65. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/docs/protocol/multi_agent_shared_workspace.md +0 -0
  66. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/docs/protocol/org_memory.md +0 -0
  67. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/docs/protocol/packet_states.yaml +0 -0
  68. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/docs/protocol/review_loop.md +0 -0
  69. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/docs/protocol/skills_manifest.yaml +0 -0
  70. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/docs/protocol/task_negotiation.md +0 -0
  71. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/examples/quickstart/README.md +0 -0
  72. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/mcp_servers/oacp_coordinator.py +0 -0
  73. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/oacp/__init__.py +0 -0
  74. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/scripts/_oacp_env.py +0 -0
  75. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/scripts/check_quality_gate.py +0 -0
  76. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/scripts/create_handoff_packet.py +0 -0
  77. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/scripts/init_org_memory.py +0 -0
  78. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/scripts/init_packet.sh +0 -0
  79. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/scripts/init_project_workspace.sh +0 -0
  80. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/scripts/normalize_findings.py +0 -0
  81. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/scripts/preflight.py +0 -0
  82. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/templates/agent_status.template.yaml +0 -0
  83. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/templates/checkpoint.template.md +0 -0
  84. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/templates/claude/agents/role_agent.template.md +0 -0
  85. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/templates/claude/rules/guardrail.template.md +0 -0
  86. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/templates/findings_packet.template.yaml +0 -0
  87. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/templates/github_actions_quality_gate.yaml +0 -0
  88. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/templates/guardrails/coding_standards.template.md +0 -0
  89. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/templates/guardrails/safe_commands.template.md +0 -0
  90. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/templates/guardrails/secrets_rules.template.md +0 -0
  91. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/templates/handoff_packet.template.yaml +0 -0
  92. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/templates/inbox_message.template.yaml +0 -0
  93. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/templates/manual_validation.template.md +0 -0
  94. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/templates/merge_decision.template.md +0 -0
  95. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/templates/org-memory/decisions.md +0 -0
  96. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/templates/org-memory/events/.gitkeep +0 -0
  97. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/templates/org-memory/events/20260317-170120-example-api-convention.md +0 -0
  98. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/templates/org-memory/recent.md +0 -0
  99. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/templates/org-memory/rules.md +0 -0
  100. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/templates/review_packet.template.md +0 -0
  101. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/templates/roles/role_baseline.template.md +0 -0
  102. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/templates/roles/role_definition.template.yaml +0 -0
  103. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/templates/runtime_capabilities.yaml +0 -0
  104. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/templates/skills_manifest.template.yaml +0 -0
  105. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/templates/test_packet.template.md +0 -0
  106. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/tests/test_create_handoff_packet.py +0 -0
  107. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/tests/test_github_workflows.py +0 -0
  108. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/tests/test_oacp_coordinator.py +0 -0
  109. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/tests/test_oacp_doctor.py +0 -0
  110. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/tests/test_oacp_env.py +0 -0
  111. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/tests/test_preflight.py +0 -0
  112. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/tests/test_review_loop.py +0 -0
  113. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/tests/test_session_lifecycle_hooks.py +0 -0
  114. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/tests/test_setup_runtime.py +0 -0
  115. {oacp_cli-0.1.2 → oacp_cli-0.2.0}/tests/test_workspace_discovery.py +0 -0
@@ -5,6 +5,27 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.2.0] - 2026-03-20
9
+
10
+ ### Added
11
+
12
+ - `oacp inbox` command for listing agent inboxes with table and `--json` output
13
+ - Sender inference for `oacp send` — `--from` is now optional when `OACP_AGENT`, `AGENT_NAME`, or agent card runtime can identify the sender
14
+
15
+ ### Changed
16
+
17
+ - Consolidated shared script constants into `_oacp_constants.py` — canonical `AGENT_RE`, runtime tuples, timestamp/template helpers
18
+ - Agent name validation now requires an alphanumeric first character (names starting with `_`, `.`, `-` are rejected)
19
+ - Message ID and filename suffixes use `secrets.token_hex` instead of `random.choices`
20
+
21
+ ## [0.1.9] - 2026-03-20
22
+
23
+ ### Added
24
+
25
+ - Memory archive layer with `oacp archive` CLI command for active/archive split (#62, #11)
26
+ - Declarative agent profiles with YAML schema and `oacp agent init|show|list` CLI commands (#52, #48)
27
+ - `known_debt.md` as standard memory file for tracking technical debt (#53, #32)
28
+
8
29
  ## [0.1.2] - 2026-03-18
9
30
 
10
31
  ### Added
@@ -65,6 +86,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
65
86
  - Checkout step in github-release workflow job (#19)
66
87
  - Pre-release audit fixes: SHA-pinned actions, dangling doc refs (#15, #16)
67
88
 
89
+ [0.1.9]: https://github.com/kiloloop/oacp/compare/v0.1.2...v0.1.9
68
90
  [0.1.2]: https://github.com/kiloloop/oacp/compare/v0.1.1...v0.1.2
69
91
  [0.1.1]: https://github.com/kiloloop/oacp/compare/v0.1.0...v0.1.1
70
92
  [0.1.0]: https://github.com/kiloloop/oacp/releases/tag/v0.1.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oacp-cli
3
- Version: 0.1.2
3
+ Version: 0.2.0
4
4
  Summary: Open Agent Coordination Protocol CLI for file-based multi-agent workflows
5
5
  Project-URL: Homepage, https://github.com/kiloloop/oacp
6
6
  Project-URL: Repository, https://github.com/kiloloop/oacp
@@ -68,7 +68,7 @@ Doctor checks your CLI tools, and with `--project` it audits workspace structure
68
68
 
69
69
  - **Inbox/outbox messaging** — async YAML-based communication with threading, broadcast, and expiry
70
70
  - **Structured review loop** — severity-graded findings, quality gates, and multi-round review
71
- - **Durable shared memory** — project facts, decisions, and open threads that persist across sessions
71
+ - **Durable shared memory** — project facts, decisions, open threads, and known debt with an explicit active/archive split
72
72
  - **Dispatch state machine** — full task lifecycle tracking from assignment to merge
73
73
  - **Agent safety defaults** — baseline rules for git, credentials, staging, and scope discipline
74
74
  - **Runtime-agnostic** — works with any agent runtime that can read/write files
@@ -151,6 +151,7 @@ uv tool install .
151
151
  ## Commands
152
152
 
153
153
  - `oacp init` creates a project workspace under `$OACP_HOME/projects/`
154
+ - `oacp memory` archives or restores project memory files
154
155
  - `oacp send` sends a protocol-compliant inbox message
155
156
  - `oacp doctor` checks environment and workspace health
156
157
  - `oacp validate` validates an inbox/outbox YAML message
@@ -164,7 +165,7 @@ If `OACP_HOME` is unset, workspace commands default to `~/oacp` (underscore).
164
165
  | **Inbox/Outbox** | Async messaging between agents via YAML files in `agents/<name>/inbox/` |
165
166
  | **Review Loop** | Structured code review: `review_request` → `review_feedback` → `review_addressed` → `review_lgtm` |
166
167
  | **Quality Gate** | Merge-readiness criteria: no unresolved P0/P1 findings, deferred nits tracked |
167
- | **Durable Memory** | Shared `memory/` directory with project facts, decisions, and open threads |
168
+ | **Durable Memory** | Shared `memory/` directory with an active working set plus `memory/archive/` for historical memory |
168
169
  | **Dispatch States** | Task lifecycle: `received` → `accepted` → `working` → `pr_opened` → `in_review` → `done` |
169
170
  | **Safety Defaults** | Baseline rules all agents follow: no force push, no secrets in commits, stage hygiene |
170
171
 
@@ -175,7 +176,7 @@ oacp/
175
176
  ├── docs/
176
177
  │ ├── protocol/ # Canonical protocol specifications (13 specs)
177
178
  │ └── guides/ # Setup, adoption, versioning
178
- ├── scripts/ # 13 kernel scripts (Python + shell)
179
+ ├── scripts/ # 18 kernel scripts (Python + shell)
179
180
  ├── templates/ # Packet, role, and guardrail templates (19)
180
181
  ├── tests/ # Test suite
181
182
  ├── Makefile # Task runner (make help for all targets)
@@ -214,6 +215,7 @@ OACP ships kernel scripts — the key CLI commands you'll use most:
214
215
 
215
216
  - **`oacp init`** — create a new project workspace (the first command you run)
216
217
  - **`oacp add-agent`** — add an agent to an existing project workspace
218
+ - **`oacp memory`** — archive or restore project memory files
217
219
  - **`oacp setup`** — generate runtime-specific config files (Claude, Codex, Gemini)
218
220
  - **`oacp send`** — send protocol-compliant messages between agents
219
221
  - **`oacp doctor`** — environment and workspace health check
@@ -257,7 +259,9 @@ $OACP_HOME/projects/<project>/
257
259
  ├── memory/ # Shared durable memory
258
260
  │ ├── project_facts.md
259
261
  │ ├── decision_log.md
260
- │ └── open_threads.md
262
+ │ ├── open_threads.md
263
+ │ ├── known_debt.md
264
+ │ └── archive/
261
265
  ├── packets/ # Review/findings artifacts
262
266
  └── workspace.json # Project metadata
263
267
  ```
@@ -55,7 +55,9 @@ $OACP_HOME/projects/my-first-project/
55
55
  ├── memory/
56
56
  │ ├── project_facts.md
57
57
  │ ├── decision_log.md
58
- │ └── open_threads.md
58
+ │ ├── open_threads.md
59
+ │ ├── known_debt.md
60
+ │ └── archive/
59
61
  ├── packets/
60
62
  │ ├── review/
61
63
  │ └── findings/
@@ -82,7 +84,7 @@ Check inbox: ls $OACP_HOME/projects/my-first-project/agents/claude/inbox/
82
84
  OACP workspace: $OACP_HOME/projects/my-first-project/
83
85
  ```
84
86
 
85
- **Other runtimes** — point your agent's system prompt at the workspace path and instruct it to read `memory/project_facts.md` at session start.
87
+ **Other runtimes** — point your agent's system prompt at the workspace path and instruct it to read the standard `memory/` files at session start.
86
88
 
87
89
  For full runtime setup (role templates, guardrails, skills), see [docs/guides/setup.md](docs/guides/setup.md).
88
90
 
@@ -42,7 +42,7 @@ Doctor checks your CLI tools, and with `--project` it audits workspace structure
42
42
 
43
43
  - **Inbox/outbox messaging** — async YAML-based communication with threading, broadcast, and expiry
44
44
  - **Structured review loop** — severity-graded findings, quality gates, and multi-round review
45
- - **Durable shared memory** — project facts, decisions, and open threads that persist across sessions
45
+ - **Durable shared memory** — project facts, decisions, open threads, and known debt with an explicit active/archive split
46
46
  - **Dispatch state machine** — full task lifecycle tracking from assignment to merge
47
47
  - **Agent safety defaults** — baseline rules for git, credentials, staging, and scope discipline
48
48
  - **Runtime-agnostic** — works with any agent runtime that can read/write files
@@ -125,6 +125,7 @@ uv tool install .
125
125
  ## Commands
126
126
 
127
127
  - `oacp init` creates a project workspace under `$OACP_HOME/projects/`
128
+ - `oacp memory` archives or restores project memory files
128
129
  - `oacp send` sends a protocol-compliant inbox message
129
130
  - `oacp doctor` checks environment and workspace health
130
131
  - `oacp validate` validates an inbox/outbox YAML message
@@ -138,7 +139,7 @@ If `OACP_HOME` is unset, workspace commands default to `~/oacp` (underscore).
138
139
  | **Inbox/Outbox** | Async messaging between agents via YAML files in `agents/<name>/inbox/` |
139
140
  | **Review Loop** | Structured code review: `review_request` → `review_feedback` → `review_addressed` → `review_lgtm` |
140
141
  | **Quality Gate** | Merge-readiness criteria: no unresolved P0/P1 findings, deferred nits tracked |
141
- | **Durable Memory** | Shared `memory/` directory with project facts, decisions, and open threads |
142
+ | **Durable Memory** | Shared `memory/` directory with an active working set plus `memory/archive/` for historical memory |
142
143
  | **Dispatch States** | Task lifecycle: `received` → `accepted` → `working` → `pr_opened` → `in_review` → `done` |
143
144
  | **Safety Defaults** | Baseline rules all agents follow: no force push, no secrets in commits, stage hygiene |
144
145
 
@@ -149,7 +150,7 @@ oacp/
149
150
  ├── docs/
150
151
  │ ├── protocol/ # Canonical protocol specifications (13 specs)
151
152
  │ └── guides/ # Setup, adoption, versioning
152
- ├── scripts/ # 13 kernel scripts (Python + shell)
153
+ ├── scripts/ # 18 kernel scripts (Python + shell)
153
154
  ├── templates/ # Packet, role, and guardrail templates (19)
154
155
  ├── tests/ # Test suite
155
156
  ├── Makefile # Task runner (make help for all targets)
@@ -188,6 +189,7 @@ OACP ships kernel scripts — the key CLI commands you'll use most:
188
189
 
189
190
  - **`oacp init`** — create a new project workspace (the first command you run)
190
191
  - **`oacp add-agent`** — add an agent to an existing project workspace
192
+ - **`oacp memory`** — archive or restore project memory files
191
193
  - **`oacp setup`** — generate runtime-specific config files (Claude, Codex, Gemini)
192
194
  - **`oacp send`** — send protocol-compliant messages between agents
193
195
  - **`oacp doctor`** — environment and workspace health check
@@ -231,7 +233,9 @@ $OACP_HOME/projects/<project>/
231
233
  ├── memory/ # Shared durable memory
232
234
  │ ├── project_facts.md
233
235
  │ ├── decision_log.md
234
- │ └── open_threads.md
236
+ │ ├── open_threads.md
237
+ │ ├── known_debt.md
238
+ │ └── archive/
235
239
  ├── packets/ # Review/findings artifacts
236
240
  └── workspace.json # Project metadata
237
241
  ```
@@ -29,28 +29,36 @@ Full specification: [`docs/protocol/inbox_outbox.md`](docs/protocol/inbox_outbox
29
29
  Agents communicate through a shared filesystem using YAML messages. Each agent has an inbox and outbox directory within a project workspace:
30
30
 
31
31
  ```
32
- $OACP_HOME/projects/<project>/
33
- ├── agents/
32
+ $OACP_HOME/
33
+ ├── agents/ # Global agent profiles
34
34
  │ ├── claude/
35
- │ │ ├── inbox/ # Other agents write here
36
- │ │ ├── outbox/ # Claude's sent messages (copies)
37
- │ │ ├── status.yaml # Dynamic agent state
38
- │ │ └── agent_card.yaml # Static agent identity
39
- │ ├── codex/
40
- │ │ ├── inbox/
41
- │ │ ├── outbox/
42
- │ │ └── ...
43
- │ └── gemini/
44
- │ └── ...
45
- ├── memory/ # Shared durable memory
46
- │ ├── project_facts.md
47
- │ ├── decision_log.md
48
- │ └── open_threads.md
49
- ├── packets/ # Review/findings artifacts
50
- │ ├── review/
51
- │ └── findings/
52
- ├── merges/ # Merge decision records
53
- └── workspace.json # Project metadata
35
+ │ │ └── profile.yaml # Global identity defaults
36
+ │ └── codex/
37
+ │ └── profile.yaml
38
+ └── projects/<project>/
39
+ ├── agents/
40
+ │ ├── claude/
41
+ │ │ ├── inbox/ # Other agents write here
42
+ │ │ ├── outbox/ # Claude's sent messages (copies)
43
+ │ │ ├── status.yaml # Dynamic agent state
44
+ │ │ └── agent_card.yaml # Static agent identity (overrides global)
45
+ │ ├── codex/
46
+ │ │ ├── inbox/
47
+ │ │ ├── outbox/
48
+ │ │ └── ...
49
+ │ └── gemini/
50
+ │ └── ...
51
+ ├── memory/ # Shared durable memory
52
+ │ ├── project_facts.md
53
+ │ ├── decision_log.md
54
+ │ ├── open_threads.md
55
+ │ └── known_debt.md
56
+ │ └── archive/
57
+ ├── packets/ # Review/findings artifacts
58
+ │ ├── review/
59
+ │ └── findings/
60
+ ├── merges/ # Merge decision records
61
+ └── workspace.json # Project metadata
54
62
  ```
55
63
 
56
64
  ### Message Format
@@ -315,8 +323,11 @@ Location: `$OACP_HOME/projects/<project>/memory/`
315
323
  | `project_facts.md` | Agent roles, repo structure, architecture, conventions |
316
324
  | `decision_log.md` | Timestamped decisions with rationale |
317
325
  | `open_threads.md` | Unresolved issues, blocked epics, cross-agent coordination |
326
+ | `known_debt.md` | Verified unresolved debt and recurring cleanup items |
318
327
 
319
- All runtimes read these at session start. Only stable, verified outcomes are written here. Promotion flows through merge decisions via a project-defined durable-memory promotion mechanism.
328
+ The top-level `memory/` files are the active working set. Historical memory can be moved into `memory/archive/`, which is not loaded at session start by default.
329
+
330
+ All runtimes read the active memory files at session start. Only stable, verified outcomes are written here. Promotion flows through merge decisions via a project-defined durable-memory promotion mechanism.
320
331
 
321
332
  #### 2. Handoff Messages with Context Keys (ephemeral)
322
333
 
@@ -330,7 +341,7 @@ Review packets, findings packets, and merge decisions form a structured audit tr
330
341
 
331
342
  | Sync Point | Direction | Action |
332
343
  |------------|-----------|--------|
333
- | Session start | Memory → Agent | Read all 3 memory files |
344
+ | Session start | Memory → Agent | Read all 4 active memory files (not `memory/archive/`) |
334
345
  | Task completion | Agent → Memory | Write stable outcomes via merge decision |
335
346
  | Handoff | Agent → Agent | Include `conversation_id` + `context_keys` |
336
347
  | Review cycle start | Packets → Agent | Read relevant packet history |
@@ -342,7 +353,7 @@ Agents follow a 6-step init sequence at session start:
342
353
 
343
354
  1. **Load global rules** (required) — safety defaults, tool preferences
344
355
  2. **Load project rules** (required) — repo structure, conventions
345
- 3. **Load durable memory** (required) — project facts, decisions, open threads
356
+ 3. **Load durable memory** (required) — project facts, decisions, open threads, known debt
346
357
  4. **Check inbox** (optional) — summarize pending messages
347
358
  5. **Load skills/tools** (optional) — runtime-specific capabilities
348
359
  6. **Report status** (required) — update `status.yaml`
@@ -445,7 +456,7 @@ The kernel boundary was established through a classification audit of all projec
445
456
 
446
457
  OACP ships a **kernel** — the minimal set of scripts, templates, and docs needed to adopt the protocol. Everything else is internal tooling for advanced orchestration workflows.
447
458
 
448
- ### Kernel Scripts (14)
459
+ ### Kernel Scripts (18)
449
460
 
450
461
  These scripts ship with the OSS release. Most are stdlib-only Python or POSIX shell with no external dependencies beyond `python3`, `git`, and `gh`. Exception: `preflight.py` also requires `ruff`, `shellcheck`, and optionally `pyyaml` for YAML validation.
451
462
 
@@ -458,9 +469,13 @@ These scripts ship with the OSS release. Most are stdlib-only Python or POSIX sh
458
469
  | `init_packet.sh` | Bootstraps review/findings/merge packet directories |
459
470
  | `init_project_workspace.py` | Creates a new project workspace — CLI: `oacp init` |
460
471
  | `add_agent.py` | Add an agent to an existing project workspace — CLI: `oacp add-agent` |
472
+ | `agent_profile.py` | Two-tier agent profile management — CLI: `oacp agent` |
473
+ | `memory_cli.py` | Archive or restore project memory files — CLI: `oacp memory` |
461
474
  | `setup_runtime.py` | Generate runtime-specific config files — CLI: `oacp setup` |
462
475
  | `normalize_findings.py` | Converts raw reviewer output to canonical findings YAML |
463
476
  | `preflight.py` | Unified quality checks — CI runs this on every PR |
477
+ | `promote_to_archive.py` | Move a non-standard memory file into `memory/archive/` |
478
+ | `restore_from_archive.py` | Restore an archived memory file into the active `memory/` working set |
464
479
  | `send_inbox_message.py` | CLI for all inbox messaging — CLI: `oacp send` |
465
480
  | `update_workspace.sh` | Idempotent workspace sync across protocol versions |
466
481
  | `validate_agent_card.py` | Validates agent card YAML against the schema |
@@ -0,0 +1,160 @@
1
+ # Agent Profiles — Two-Tier Identity System
2
+
3
+ ## Purpose
4
+
5
+ Agent profiles provide a two-tier identity system: **global profiles** declare stable defaults for an agent across all projects, while **project-level agent cards** override those defaults for a specific project context. The merge produces a single resolved view used for routing, discovery, and authorization.
6
+
7
+ The model mirrors how Claude Code handles instructions: a global `~/.claude/CLAUDE.md` sets baseline behavior, and a project-level `CLAUDE.md` overrides or extends it per repository. Profiles work the same way — define an agent's identity once, specialize per project.
8
+
9
+ ## Directory Layout
10
+
11
+ ```
12
+ $OACP_HOME/
13
+ ├── agents/ # Global agent profiles
14
+ │ ├── claude/
15
+ │ │ └── profile.yaml
16
+ │ └── codex/
17
+ │ └── profile.yaml
18
+ └── projects/<project>/
19
+ └── agents/
20
+ └── claude/
21
+ ├── agent_card.yaml # Project-level overrides
22
+ ├── status.yaml
23
+ ├── inbox/
24
+ └── outbox/
25
+ ```
26
+
27
+ - **Global profiles** (`agents/<name>/profile.yaml`) are created once per agent and rarely change.
28
+ - **Project cards** (`projects/<project>/agents/<name>/agent_card.yaml`) carry project-specific overrides — permissions, skills, routing rules — and are the authoritative resolved identity for that project.
29
+ - **Status files** (`status.yaml`) remain separate; they track dynamic session state, not static identity.
30
+
31
+ ## Merge Rules
32
+
33
+ When both a global profile and a project card exist, they are merged with the following rules:
34
+
35
+ ### Scalars
36
+
37
+ Project value wins if non-empty. Empty or null project values fall through to the global default.
38
+
39
+ ```yaml
40
+ # Global profile # Project card # Merged result
41
+ model: "claude-opus-4-6" model: "claude-sonnet-4" model: "claude-sonnet-4"
42
+ description: "Implementer" description: "" description: "Implementer"
43
+ trust_level: standard trust_level: elevated trust_level: elevated
44
+ ```
45
+
46
+ ### Dict Sections
47
+
48
+ Key-level merge within the dict. Project keys override matching global keys; global-only keys are preserved.
49
+
50
+ ```yaml
51
+ # Global profile # Project card # Merged result
52
+ capabilities: capabilities: capabilities:
53
+ tools: [Bash, Read] tools: [Bash, Read, Edit] tools: [Bash, Read, Edit]
54
+ languages: [python] domains: [trading] languages: [python]
55
+ domains: [backend] domains: [trading]
56
+ ```
57
+
58
+ Note: `tools`, `languages`, and `domains` within `capabilities` are list sub-fields and follow list replacement (see below). The dict-level merge applies to the `capabilities` section itself — project can add or override keys without erasing global-only keys.
59
+
60
+ ### Lists
61
+
62
+ Full replacement. If the project card defines a list field, it replaces the global list entirely. This prevents ambiguous merge semantics (append? deduplicate? reorder?).
63
+
64
+ ```yaml
65
+ # Global profile # Project card # Merged result
66
+ skills: skills: skills:
67
+ - id: code_review - id: security_audit - id: security_audit
68
+ name: Code Review name: Security Audit name: Security Audit
69
+ ```
70
+
71
+ ### Missing Sections
72
+
73
+ If the project card omits a section entirely, the global profile's value is used verbatim.
74
+
75
+ ## Profile Schema Reference
76
+
77
+ Global profiles use `agent_profile.template.yaml`. Fields:
78
+
79
+ | Field | Type | Required | Description |
80
+ |-------|------|----------|-------------|
81
+ | `version` | string | yes | Schema version (semver, currently `0.2.0`) |
82
+ | `name` | string | yes | Agent identifier (matches directory name) |
83
+ | `runtime` | string | yes | One of: `claude`, `codex`, `gemini`, `human` |
84
+ | `model` | string | no | Model version identifier |
85
+ | `description` | string | no | One-line role summary |
86
+ | `routing_rules` | dict | no | `primary` (preferred targets) and `avoid` (agents to skip) |
87
+ | `trust_level` | string | no | `untrusted`, `standard`, `elevated`, or `admin` |
88
+ | `quota` | dict | no | `max_cost_usd_per_month`, `reset_day`, `warn_threshold` |
89
+ | `capabilities` | dict | no | `tools`, `languages`, `domains` lists |
90
+ | `skills` | list | no | Structured skill declarations (A2A-compatible) |
91
+
92
+ Project-level agent cards extend this schema with additional sections: `permissions`, `availability`, and `protocol`. See `templates/agent_card.template.yaml` for the full card schema.
93
+
94
+ ## CLI Reference
95
+
96
+ The CLI wraps `scripts/agent_profile.py`. All commands auto-discover `$OACP_HOME`.
97
+
98
+ ### `oacp agent init`
99
+
100
+ Scaffold a global agent profile from the template.
101
+
102
+ ```bash
103
+ oacp agent init claude --runtime claude
104
+ # Created global profile: $OACP_HOME/agents/claude/profile.yaml
105
+
106
+ oacp agent init codex --runtime codex
107
+ # Created global profile: $OACP_HOME/agents/codex/profile.yaml
108
+ ```
109
+
110
+ If the profile already exists, the command skips without overwriting.
111
+
112
+ ### `oacp agent show`
113
+
114
+ Print the resolved profile YAML. Without `--project`, prints the global profile. With `--project`, prints the merged result.
115
+
116
+ ```bash
117
+ oacp agent show claude
118
+ # Prints global profile for claude
119
+
120
+ oacp agent show claude --project my-app
121
+ # Prints merged profile (global + project card overrides)
122
+ ```
123
+
124
+ ### `oacp agent list`
125
+
126
+ List known agents with their tier tags.
127
+
128
+ ```bash
129
+ oacp agent list
130
+ # claude (global)
131
+ # codex (global)
132
+
133
+ oacp agent list --project my-app
134
+ # claude (global, project)
135
+ # codex (global)
136
+ # gemini (project)
137
+ ```
138
+
139
+ ## Relationship to Existing Agent Cards
140
+
141
+ Agent cards predate global profiles and remain the authoritative per-project identity. The profile system adds a global defaults layer underneath:
142
+
143
+ | Concern | Global Profile | Project Agent Card |
144
+ |---------|---------------|--------------------|
145
+ | **Location** | `$OACP_HOME/agents/<name>/profile.yaml` | `$OACP_HOME/projects/<project>/agents/<name>/agent_card.yaml` |
146
+ | **Scope** | All projects | Single project |
147
+ | **Updates** | Rarely (identity changes) | Per project as needed |
148
+ | **Authority** | Defaults only | Authoritative for the project |
149
+ | **Extra sections** | None | `permissions`, `availability`, `protocol` |
150
+
151
+ When both exist, the merged result is what other systems (routing, doctor checks, discovery) should consume. When only a project card exists (no global profile), the card is used as-is. When only a global profile exists (no project card), the profile is used as-is.
152
+
153
+ ## Cross-References
154
+
155
+ - **Runtime Capabilities**: `docs/protocol/runtime_capabilities.md` — capability keys, status schema, health checks
156
+ - **Inbox Protocol**: `docs/protocol/inbox_outbox.md` — agent messaging format
157
+ - **Profile Script**: `scripts/agent_profile.py` — implementation with merge logic
158
+ - **Profile Template**: `templates/agent_profile.template.yaml` — global profile scaffold
159
+ - **Card Template**: `templates/agent_card.template.yaml` — project card scaffold
160
+ - **Card Validator**: `scripts/validate_agent_card.py` — schema validation
@@ -23,11 +23,16 @@ The protocol defines three complementary sync mechanisms, ordered from most dura
23
23
  | `project_facts.md` | Agent roles, repo structure, architecture, conventions | Any agent via the project's durable-memory promotion flow |
24
24
  | `decision_log.md` | Timestamped decisions with rationale | Any agent via the project's durable-memory promotion flow |
25
25
  | `open_threads.md` | Unresolved issues, blocked epics, cross-agent coordination | Any agent via the project's durable-memory promotion flow |
26
+ | `known_debt.md` | Verified unresolved debt that should persist across sessions | Any agent via the project's durable-memory promotion flow |
26
27
 
27
- These files are the **source of truth** for stable project knowledge. All runtimes read them at session start. Only verified, stable outcomes should be written here.
28
+ The top-level files in `memory/` are the active working set. Historical memory can be moved into `memory/archive/`, which is not loaded during session init unless an agent explicitly opts in.
29
+
30
+ These files are the **source of truth** for stable project knowledge. All runtimes read the active working set at session start. Only verified, stable outcomes should be written here.
28
31
 
29
32
  **Promotion flow**: Merge decisions contain a "Durable Memory Updates" section. Each implementation should provide a promotion mechanism that extracts approved entries from merge artifacts and appends them to the appropriate memory file, deduplicating against existing content.
30
33
 
34
+ **Archive flow**: Users or coordinator agents may move non-standard memory files into `memory/archive/` for historical retention, then restore them back into `memory/` when they become active again.
35
+
31
36
  ### 2. Handoff Messages with Context Keys
32
37
 
33
38
  **Location**: `agents/<agent>/inbox/`
@@ -57,7 +62,7 @@ Agents synchronize knowledge at well-defined points in the workflow:
57
62
 
58
63
  | Sync Point | Action | Direction |
59
64
  |------------|--------|-----------|
60
- | **Session start** | Read `memory/project_facts.md`, `decision_log.md`, `open_threads.md` | Memory -> Agent |
65
+ | **Session start** | Read `memory/project_facts.md`, `decision_log.md`, `open_threads.md`, `known_debt.md` (not `memory/archive/`) | Memory -> Agent |
61
66
  | **Task completion** | Write stable outcomes to memory via merge decision + durable-memory promotion flow | Agent -> Memory |
62
67
  | **Handoff** | Include `conversation_id` + `context_keys` in handoff message | Agent -> Agent |
63
68
  | **Review cycle start** | Read relevant packet history | Packets -> Agent |
@@ -104,7 +109,7 @@ Each project should provide a durable-memory promotion mechanism that:
104
109
 
105
110
  1. Scans merge decisions or equivalent terminal artifacts
106
111
  2. Extracts entries from the "Durable Memory Updates" section
107
- 3. Appends new entries to `decision_log.md`, `open_threads.md`, or `project_facts.md`
112
+ 3. Appends new entries to `decision_log.md`, `open_threads.md`, `project_facts.md`, or `known_debt.md`
108
113
  4. Deduplicates against existing content
109
114
 
110
115
  This ensures that knowledge flows from ephemeral review artifacts into durable memory that persists across sessions and runtimes, without requiring a specific helper script name.
@@ -269,6 +269,7 @@ The card schema is inspired by Google's A2A Agent Card spec (v0.3.0). Key differ
269
269
 
270
270
  ## Cross-References
271
271
 
272
+ - **Agent Profiles**: `docs/protocol/agent_profiles.md` — two-tier global profile + project card system
272
273
  - **Parity Matrix**: `docs/guides/runtime_capability_matrix.md` — detailed per-runtime capability comparison
273
274
  - **Inbox Protocol**: `docs/protocol/inbox_outbox.md` — agent messaging format
274
275
  - **Session Lifecycle**: `scripts/session_lifecycle_hooks.py` (reference implementation) — session boundary hooks
@@ -60,11 +60,14 @@ The init sequence has 6 steps in a fixed order. Each step is classified as **req
60
60
 
61
61
  **Location:** `$OACP_HOME/projects/<project>/memory/`
62
62
 
63
+ The top-level files in `memory/` are the active working set. `memory/archive/` is historical storage and is not loaded by default at session start.
64
+
63
65
  **Loading order:**
64
66
 
65
67
  1. `project_facts.md` — agent roles, repo structure, architecture, conventions. Read first because it provides the mental model for everything else.
66
68
  2. `decision_log.md` — timestamped decisions with rationale. Read second to understand what has been decided and why.
67
- 3. `open_threads.md` — unresolved issues, blocked epics, cross-agent coordination. Read last because it builds on the context from facts and decisions.
69
+ 3. `open_threads.md` — unresolved issues, blocked epics, cross-agent coordination. Read third because it builds on the context from facts and decisions.
70
+ 4. `known_debt.md` — verified unresolved debt, recurring pain points, and cleanup items that future sessions should keep in view.
68
71
 
69
72
  **Conflict resolution:** If memory files contradict project rules (e.g., `project_facts.md` says "use pytest" but `CLAUDE.md` says "use make test"), project rules win. Memory files may be stale; project rules are maintained alongside the code.
70
73
 
@@ -21,6 +21,9 @@ Installable Open Agent Coordination Protocol (OACP) CLI.
21
21
  Commands:
22
22
  init Create a project workspace under $OACP_HOME/projects/
23
23
  add-agent Add an agent to an existing project workspace
24
+ agent Manage global agent profiles (init, show, list)
25
+ inbox List pending inbox messages
26
+ memory Archive or restore project memory files
24
27
  setup Generate runtime-specific config files in a repo
25
28
  send Send a protocol-compliant inbox message
26
29
  org-memory Initialize org-level memory at $OACP_HOME/org-memory/
@@ -32,8 +35,10 @@ Examples:
32
35
  oacp init my-project --repo /path/to/repo
33
36
  oacp init my-project --agents claude,codex
34
37
  oacp add-agent my-project alice --runtime claude
38
+ oacp inbox my-project --agent claude
39
+ oacp memory archive my-project research_notes.md
35
40
  oacp setup claude --project my-project
36
- oacp send my-project --from codex --to iris --type notification --subject "Done" --body "Completed"
41
+ oacp send my-project --to iris --type notification --subject "Done" --body "Completed"
37
42
  oacp org-memory init
38
43
  oacp write-event --agent claude --project my-project --type decision --slug api-convention --body "Use REST for public APIs"
39
44
  oacp doctor
@@ -43,6 +48,9 @@ Examples:
43
48
  SCRIPT_NAMES = {
44
49
  "init": "init_project_workspace.py",
45
50
  "add-agent": "add_agent.py",
51
+ "agent": "agent_profile.py",
52
+ "inbox": "oacp_inbox.py",
53
+ "memory": "memory_cli.py",
46
54
  "setup": "setup_runtime.py",
47
55
  "send": "send_inbox_message.py",
48
56
  "org-memory": "init_org_memory.py",
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "oacp-cli"
7
- version = "0.1.2"
7
+ version = "0.2.0"
8
8
  description = "Open Agent Coordination Protocol CLI for file-based multi-agent workflows"
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -50,6 +50,7 @@ packages = ["oacp"]
50
50
 
51
51
  [tool.hatch.build.targets.wheel.force-include]
52
52
  "scripts/codex_session_init.py" = "oacp/_scripts/codex_session_init.py"
53
+ "scripts/_oacp_constants.py" = "oacp/_scripts/_oacp_constants.py"
53
54
  "scripts/_oacp_env.py" = "oacp/_scripts/_oacp_env.py"
54
55
  "scripts/handoff_schema.py" = "oacp/_scripts/handoff_schema.py"
55
56
  "scripts/oacp_doctor.py" = "oacp/_scripts/oacp_doctor.py"
@@ -58,6 +59,12 @@ packages = ["oacp"]
58
59
  "scripts/session_lifecycle_hooks.py" = "oacp/_scripts/session_lifecycle_hooks.py"
59
60
  "scripts/validate_message.py" = "oacp/_scripts/validate_message.py"
60
61
  "scripts/add_agent.py" = "oacp/_scripts/add_agent.py"
62
+ "scripts/agent_profile.py" = "oacp/_scripts/agent_profile.py"
63
+ "scripts/oacp_inbox.py" = "oacp/_scripts/oacp_inbox.py"
64
+ "scripts/memory_cli.py" = "oacp/_scripts/memory_cli.py"
65
+ "scripts/memory_archive_common.py" = "oacp/_scripts/memory_archive_common.py"
66
+ "scripts/promote_to_archive.py" = "oacp/_scripts/promote_to_archive.py"
67
+ "scripts/restore_from_archive.py" = "oacp/_scripts/restore_from_archive.py"
61
68
  "scripts/setup_runtime.py" = "oacp/_scripts/setup_runtime.py"
62
69
  "scripts/init_org_memory.py" = "oacp/_scripts/init_org_memory.py"
63
70
  "scripts/write_event.py" = "oacp/_scripts/write_event.py"
@@ -66,6 +73,7 @@ packages = ["oacp"]
66
73
  "templates/org-memory/decisions.md" = "oacp/_templates/org-memory/decisions.md"
67
74
  "templates/org-memory/rules.md" = "oacp/_templates/org-memory/rules.md"
68
75
  "templates/agent_card.template.yaml" = "oacp/_templates/agent_card.template.yaml"
76
+ "templates/agent_profile.template.yaml" = "oacp/_templates/agent_profile.template.yaml"
69
77
  "templates/claude/agents/role_agent.template.md" = "oacp/_templates/claude/agents/role_agent.template.md"
70
78
 
71
79
  [tool.ruff]
@@ -0,0 +1,58 @@
1
+ # SPDX-FileCopyrightText: 2026 Kiloloop
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """Shared constants and helpers for OACP scripts."""
4
+
5
+ from __future__ import annotations
6
+
7
+ import datetime as dt
8
+ import re
9
+ from contextlib import nullcontext
10
+ from importlib import resources
11
+ from pathlib import Path
12
+
13
+ AGENT_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$")
14
+ CREATABLE_RUNTIMES = ("claude", "codex", "gemini")
15
+ ALL_RUNTIMES = ("claude", "codex", "gemini", "human", "unknown")
16
+ CANONICAL_CAPABILITIES = {
17
+ "headless",
18
+ "mcp_tools",
19
+ "shell_access",
20
+ "git_ops",
21
+ "github_cli",
22
+ "subagents",
23
+ "parallel_teams",
24
+ "web_search",
25
+ "browser",
26
+ "session_memory",
27
+ "notifications",
28
+ "async_tasks",
29
+ "image_generation",
30
+ }
31
+
32
+
33
+ def utc_now_iso(now: dt.datetime | None = None) -> str:
34
+ """Return a UTC RFC3339 timestamp with seconds precision."""
35
+ base = now or dt.datetime.now(dt.timezone.utc)
36
+ if base.tzinfo is None:
37
+ base = base.replace(tzinfo=dt.timezone.utc)
38
+ else:
39
+ base = base.astimezone(dt.timezone.utc)
40
+ return base.strftime("%Y-%m-%dT%H:%M:%SZ")
41
+
42
+
43
+ def _write_if_missing(path: Path, content: str) -> bool:
44
+ """Write content to *path* only if it does not already exist."""
45
+ if path.exists():
46
+ return False
47
+ path.parent.mkdir(parents=True, exist_ok=True)
48
+ path.write_text(content, encoding="utf-8")
49
+ return True
50
+
51
+
52
+ def _template_path(relative: str):
53
+ """Resolve a template file from the repo tree or installed package."""
54
+ repo_template = Path(__file__).resolve().parent.parent / "templates" / relative
55
+ if repo_template.is_file():
56
+ return nullcontext(repo_template)
57
+ resource = resources.files("oacp").joinpath("_templates", relative)
58
+ return resources.as_file(resource)