apmw 0.1.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.
- apmw-0.1.0/.github/workflows/publish.yml +119 -0
- apmw-0.1.0/.gitignore +10 -0
- apmw-0.1.0/AGENTS.md +50 -0
- apmw-0.1.0/GitVersion.yml +2 -0
- apmw-0.1.0/PKG-INFO +5 -0
- apmw-0.1.0/README.md +308 -0
- apmw-0.1.0/install.ps1 +13 -0
- apmw-0.1.0/install.sh +14 -0
- apmw-0.1.0/pyproject.toml +20 -0
- apmw-0.1.0/scripts/apmw_entrypoint.py +4 -0
- apmw-0.1.0/src/apmw/__init__.py +443 -0
- apmw-0.1.0/src/apmw/data/agent-strip-fields.json +7 -0
- apmw-0.1.0/tests/test_cli.py +58 -0
- apmw-0.1.0/tests/test_sanitize.py +239 -0
- apmw-0.1.0/uv.lock +307 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
name: Publish apmw
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: write
|
|
11
|
+
packages: write
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
determine-version:
|
|
15
|
+
name: Determine Version
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
outputs:
|
|
18
|
+
semver: ${{ steps.version.outputs.semver }}
|
|
19
|
+
python-version: ${{ steps.version.outputs.python-version }}
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
with:
|
|
23
|
+
fetch-depth: 0
|
|
24
|
+
- name: Determine version
|
|
25
|
+
id: version
|
|
26
|
+
uses: GravionLabs/ci/versioning/determine-version@main
|
|
27
|
+
with:
|
|
28
|
+
gitversion-config-file: GitVersion.yml
|
|
29
|
+
|
|
30
|
+
build-test:
|
|
31
|
+
name: Build & Test
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
needs: determine-version
|
|
34
|
+
steps:
|
|
35
|
+
- name: Build
|
|
36
|
+
uses: GravionLabs/ci/python/build@main
|
|
37
|
+
with:
|
|
38
|
+
version: ${{ needs.determine-version.outputs.python-version }}
|
|
39
|
+
|
|
40
|
+
- name: Test
|
|
41
|
+
uses: GravionLabs/ci/python/test@main
|
|
42
|
+
|
|
43
|
+
build-executables:
|
|
44
|
+
name: Build executable (${{ matrix.suffix }})
|
|
45
|
+
runs-on: ${{ matrix.os }}
|
|
46
|
+
needs: determine-version
|
|
47
|
+
strategy:
|
|
48
|
+
matrix:
|
|
49
|
+
include:
|
|
50
|
+
- os: ubuntu-latest
|
|
51
|
+
suffix: linux
|
|
52
|
+
- os: macos-latest
|
|
53
|
+
suffix: macos-arm64
|
|
54
|
+
- os: windows-latest
|
|
55
|
+
suffix: windows.exe
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/checkout@v4
|
|
58
|
+
|
|
59
|
+
- name: Setup Python (uv)
|
|
60
|
+
uses: GravionLabs/ci/python/setup@main
|
|
61
|
+
|
|
62
|
+
- name: Install dependencies
|
|
63
|
+
run: uv sync
|
|
64
|
+
|
|
65
|
+
- name: Build executable
|
|
66
|
+
run: uv run --with pyinstaller pyinstaller --onefile --name apmw-${{ matrix.suffix }} scripts/apmw_entrypoint.py
|
|
67
|
+
|
|
68
|
+
- name: Upload executable artifact
|
|
69
|
+
uses: actions/upload-artifact@v4
|
|
70
|
+
with:
|
|
71
|
+
name: apmw-${{ matrix.suffix }}
|
|
72
|
+
path: dist/apmw-${{ matrix.suffix }}
|
|
73
|
+
if-no-files-found: error
|
|
74
|
+
|
|
75
|
+
publish:
|
|
76
|
+
name: Publish to PyPI
|
|
77
|
+
if: github.event_name != 'pull_request'
|
|
78
|
+
runs-on: ubuntu-latest
|
|
79
|
+
permissions:
|
|
80
|
+
id-token: write
|
|
81
|
+
needs:
|
|
82
|
+
- determine-version
|
|
83
|
+
- build-test
|
|
84
|
+
steps:
|
|
85
|
+
- name: Publish Python package
|
|
86
|
+
uses: GravionLabs/ci/python/pypi/publish@main
|
|
87
|
+
with:
|
|
88
|
+
download-artifact: 'true'
|
|
89
|
+
|
|
90
|
+
release:
|
|
91
|
+
name: Release
|
|
92
|
+
if: github.event_name != 'pull_request'
|
|
93
|
+
runs-on: ubuntu-latest
|
|
94
|
+
needs:
|
|
95
|
+
- determine-version
|
|
96
|
+
- publish
|
|
97
|
+
- build-executables
|
|
98
|
+
permissions:
|
|
99
|
+
contents: write
|
|
100
|
+
steps:
|
|
101
|
+
- name: Create GitHub Release
|
|
102
|
+
uses: GravionLabs/ci/versioning/gh-release@main
|
|
103
|
+
with:
|
|
104
|
+
version: ${{ needs.determine-version.outputs.semver }}
|
|
105
|
+
tag-prefix: v
|
|
106
|
+
|
|
107
|
+
- name: Download executable artifacts
|
|
108
|
+
uses: actions/download-artifact@v4
|
|
109
|
+
with:
|
|
110
|
+
pattern: apmw-*
|
|
111
|
+
path: executables
|
|
112
|
+
merge-multiple: true
|
|
113
|
+
|
|
114
|
+
- name: Attach executables to release
|
|
115
|
+
env:
|
|
116
|
+
GH_TOKEN: ${{ github.token }}
|
|
117
|
+
run: |
|
|
118
|
+
TAG="v${{ needs.determine-version.outputs.semver }}"
|
|
119
|
+
gh release upload "$TAG" executables/* --clobber
|
apmw-0.1.0/.gitignore
ADDED
apmw-0.1.0/AGENTS.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# apmw
|
|
2
|
+
|
|
3
|
+
Cross-platform wrapper CLI for [apm](https://github.com/GravionLabs). `apm install`/`apm update`
|
|
4
|
+
deploy platform-neutral skills cleanly, but agent files carry platform-specific frontmatter
|
|
5
|
+
(`permission`, `mode`, `tools`, `model`, etc.) that each target — Claude Code, OpenCode, GitHub
|
|
6
|
+
Copilot, Cursor, Codex — interprets differently. `apmw` runs the real `apm` command and then
|
|
7
|
+
strips/rewrites the installed agent frontmatter for the target platform. `apmw sanitize` exposes
|
|
8
|
+
the stripping step standalone, for re-sanitizing an already-installed agent directory without
|
|
9
|
+
invoking `apm` at all.
|
|
10
|
+
|
|
11
|
+
## Repo layout
|
|
12
|
+
|
|
13
|
+
- `src/apmw/__init__.py` — the entire package: argument parsing, sanitization logic, and `apm`
|
|
14
|
+
subprocess invocation all live in this single file.
|
|
15
|
+
- `src/apmw/data/agent-strip-fields.json` — per-platform list of frontmatter fields to strip.
|
|
16
|
+
**Must be kept in sync with `apm-hub`'s `config/agent-strip-fields.json`** (used by
|
|
17
|
+
`src/generators/agent-target-generator.ts` there) — there is no automated sync, update both by
|
|
18
|
+
hand when the set of fields per platform changes.
|
|
19
|
+
- `tests/test_sanitize.py` — unit tests for the frontmatter-stripping logic (`sanitize_installed`,
|
|
20
|
+
`strip_fields`, `add_permission_block`).
|
|
21
|
+
- `tests/test_cli.py` — CLI-level tests: argument parsing and `main()` dispatch.
|
|
22
|
+
|
|
23
|
+
## Key concepts
|
|
24
|
+
|
|
25
|
+
- `TARGET_DIRS` — local (CWD-relative) agent install dir per platform, e.g. `claude` → `.claude`.
|
|
26
|
+
- `GLOBAL_PATHS` — per-OS global agent install dirs per platform, used when `-g/--global` is passed.
|
|
27
|
+
- `TARGET_STRIP_FIELDS` — loaded from `agent-strip-fields.json`; which frontmatter keys to drop
|
|
28
|
+
per platform.
|
|
29
|
+
- `do_sanitize(platform, global_, source)` — the core operation: sanitizes either the local or
|
|
30
|
+
global agent dir for a platform, optionally also sanitizing source files under `packages/`.
|
|
31
|
+
- `sanitize_installed` / `sanitize_source` — the actual file-rewriting functions `do_sanitize`
|
|
32
|
+
calls.
|
|
33
|
+
- Subcommands (`build_parser()` in `src/apmw/__init__.py`): `install` and `update` run the real
|
|
34
|
+
`apm` binary via `run_apm()` and then call `do_sanitize()`; `sanitize` calls `do_sanitize()`
|
|
35
|
+
directly with no `apm` invocation.
|
|
36
|
+
|
|
37
|
+
## Dev workflow
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
uv sync
|
|
41
|
+
uv run pytest
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
No linter or formatter is configured in `pyproject.toml` — don't introduce one speculatively.
|
|
45
|
+
|
|
46
|
+
## Style
|
|
47
|
+
|
|
48
|
+
The codebase has no docstrings and minimal comments (only where a non-obvious rationale needs
|
|
49
|
+
capturing, e.g. the `claude` strip-fields comment). Keep additions flat and terse — match what's
|
|
50
|
+
already there rather than adding documentation blocks.
|
apmw-0.1.0/PKG-INFO
ADDED
apmw-0.1.0/README.md
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
# apmw
|
|
2
|
+
|
|
3
|
+
Cross-platform wrapper for the [apm](https://github.com/GravionLabs) CLI.
|
|
4
|
+
|
|
5
|
+
`apm install` and `apm update` deploy platform-neutral skills cleanly, but
|
|
6
|
+
agent files carry platform-specific frontmatter (`permission`, `mode`,
|
|
7
|
+
`tools`, etc.) that each target — Claude Code, OpenCode, GitHub Copilot,
|
|
8
|
+
Cursor, Codex — interprets differently. `apmw` runs the real `apm` command
|
|
9
|
+
and then sanitizes the installed agent frontmatter for the target platform
|
|
10
|
+
you specify.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pipx install apmw
|
|
18
|
+
# or
|
|
19
|
+
uvx apmw install -p claude
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Prebuilt single-file executables (linux, macOS-arm64, windows) that need no Python environment
|
|
23
|
+
are attached to each [GitHub Release](https://github.com/GravionLabs/apmw/releases).
|
|
24
|
+
|
|
25
|
+
### Local development install
|
|
26
|
+
|
|
27
|
+
To install the local checkout (editable — picks up source edits immediately):
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
./install.sh # Linux / macOS
|
|
31
|
+
.\install.ps1 # Windows
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Uses `uv tool install` if `uv` is on `PATH`, falling back to `pipx`.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
apmw install -p claude # apm install + sanitize for Claude Code
|
|
42
|
+
apmw install -p opencode # apm install + sanitize for OpenCode
|
|
43
|
+
apmw update -p copilot # apm update + sanitize for GitHub Copilot
|
|
44
|
+
apmw sanitize -p claude # sanitize an already-installed local agent dir (no apm call)
|
|
45
|
+
apmw sanitize -g -p claude # sanitize the global Claude Code agent dir
|
|
46
|
+
apmw sanitize # sanitize for every known platform
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Flags:**
|
|
50
|
+
|
|
51
|
+
| Flag | Description |
|
|
52
|
+
| :--- | :---------- |
|
|
53
|
+
| `-p, --platform` | Target: `opencode`, `copilot`, `claude`, `cursor`, `codex`; omit to run for all |
|
|
54
|
+
| `-g, --global` | Sanitize the platform's global agent install directory instead of the project dir |
|
|
55
|
+
| `-s, --source` | Also inject `permission:` blocks into source agent files under `packages/` |
|
|
56
|
+
| `-y, --yes` | (`update` only) Skip consent prompt |
|
|
57
|
+
| `--dry-run` | (`update` only) Preview changes without writing |
|
|
58
|
+
|
|
59
|
+
`apmw install`/`update` require `apm` to be installed and on `PATH`.
|
|
60
|
+
`apmw sanitize` operates only on local files and does not invoke `apm`.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Global install — all packages
|
|
65
|
+
|
|
66
|
+
Install the full apm-hub skill set into the global agent directories so every
|
|
67
|
+
repository on the machine benefits without a per-project `apm install`:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# 1. Register the marketplace (once per machine)
|
|
71
|
+
apm marketplace add GravionLabs/apm-hub
|
|
72
|
+
|
|
73
|
+
# 2. Install the meta-package globally
|
|
74
|
+
apmw install all@apm-hub -g -p opencode
|
|
75
|
+
apmw install all@apm-hub -g -p claude
|
|
76
|
+
apmw install all@apm-hub -g -p copilot
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`apmw install` calls `apm install -g` (which deploys to global paths) and then
|
|
80
|
+
sanitizes all agent files in those paths for the target platform.
|
|
81
|
+
|
|
82
|
+
**Global agent paths per platform:**
|
|
83
|
+
|
|
84
|
+
| Platform | Linux / macOS | Windows |
|
|
85
|
+
| :------------- | :----------------------------------------------- | :------------------------------------------ |
|
|
86
|
+
| OpenCode | `~/.config/opencode/agents/` | `%APPDATA%\opencode\agents\` |
|
|
87
|
+
| Claude Code | `~/.claude/agents/` · `~/.config/claude/agents/` | `%APPDATA%\Claude\agents\` |
|
|
88
|
+
| GitHub Copilot | `~/.config/github-copilot/agents/` | `%APPDATA%\GitHub Copilot\agents\` |
|
|
89
|
+
| Cursor | `~/.cursor/agents/` | `%APPDATA%\Cursor\agents\` |
|
|
90
|
+
| Codex | `~/.codex/agents/` | `%APPDATA%\Codex\agents\` |
|
|
91
|
+
|
|
92
|
+
### Transitive MCP server definitions
|
|
93
|
+
|
|
94
|
+
When a package declares MCP server entries in its `apm.yml`, `apm install`
|
|
95
|
+
propagates those definitions to consuming projects transitively. For example,
|
|
96
|
+
the `github` package may ship an MCP server configuration for the GitHub API:
|
|
97
|
+
|
|
98
|
+
```yaml
|
|
99
|
+
# packages/github/apm.yml
|
|
100
|
+
name: github
|
|
101
|
+
version: 1.0.0
|
|
102
|
+
mcp:
|
|
103
|
+
servers:
|
|
104
|
+
- name: github
|
|
105
|
+
command: npx
|
|
106
|
+
args: ["-y", "@modelcontextprotocol/server-github"]
|
|
107
|
+
env:
|
|
108
|
+
GITHUB_PERSONAL_ACCESS_TOKEN: "${GITHUB_TOKEN}"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
When you run `apmw install all@apm-hub -g -p opencode`, the `all` meta-package
|
|
112
|
+
pulls in `github` (and every other package) transitively. `apm` writes the MCP
|
|
113
|
+
server entries to the platform's agent config file (e.g.
|
|
114
|
+
`~/.config/opencode/mcp.json`) so every project on the machine can use GitHub
|
|
115
|
+
MCP tools without any per-project setup.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Multi-target agent authoring
|
|
120
|
+
|
|
121
|
+
Agent source files (`*.agent.md`) are written in **GitHub Copilot style** as
|
|
122
|
+
the primary format. Each platform understands only a subset of frontmatter
|
|
123
|
+
fields — `apmw` strips the incompatible ones and can also inject
|
|
124
|
+
platform-specific additions via a reserved `metadata:` key.
|
|
125
|
+
|
|
126
|
+
### Field compatibility
|
|
127
|
+
|
|
128
|
+
| Field | Copilot | OpenCode | Claude Code | Cursor / Codex |
|
|
129
|
+
| :--------------- | :-----: | :------: | :---------: | :------------: |
|
|
130
|
+
| `name` | ✅ | ✅ | ✅ | ✅ |
|
|
131
|
+
| `description` | ✅ | ✅ | ✅ | ✅ |
|
|
132
|
+
| `tools` | ✅ | ❌ | ✅ * | ✅ |
|
|
133
|
+
| `mode` | ✅ | ❌ | ❌ | ❌ |
|
|
134
|
+
| `steps` | ✅ | ❌ | ❌ | ❌ |
|
|
135
|
+
| `color` | ✅ | ❌ | ❌ | ❌ |
|
|
136
|
+
| `permission` | ❌ | ✅ | ❌ | ❌ |
|
|
137
|
+
| `user-invocable` | ✅ | ❌ | ❌ | ❌ |
|
|
138
|
+
| `model` | ✅ | ✅ | ❌ | ✅ |
|
|
139
|
+
| `agents` | ✅ | ❌ | ❌ | ✅ |
|
|
140
|
+
|
|
141
|
+
*Claude Code uses different tool identifiers than Copilot (`Bash` vs `run_in_terminal`, etc.).
|
|
142
|
+
|
|
143
|
+
### `metadata:` — platform-specific overrides
|
|
144
|
+
|
|
145
|
+
Add a `metadata:` block to supply fields that only apply to a specific
|
|
146
|
+
platform. `apmw` extracts the block for the target platform, injects the
|
|
147
|
+
lines into the sanitized frontmatter, and strips `metadata:` itself from all
|
|
148
|
+
outputs. **Metadata wins on conflict** — if both the top level and
|
|
149
|
+
`metadata.opencode` define `permission:`, the metadata version is used.
|
|
150
|
+
|
|
151
|
+
```yaml
|
|
152
|
+
---
|
|
153
|
+
name: dev-workflow
|
|
154
|
+
description: "Dev workflow orchestrator."
|
|
155
|
+
# Copilot-native fields — kept as-is in the Copilot output
|
|
156
|
+
mode: all
|
|
157
|
+
steps: 40
|
|
158
|
+
color: primary
|
|
159
|
+
tools: ['agent', 'read', 'search']
|
|
160
|
+
user-invocable: true
|
|
161
|
+
# Platform-specific overrides (never appear in any output file)
|
|
162
|
+
metadata:
|
|
163
|
+
opencode:
|
|
164
|
+
permission:
|
|
165
|
+
read: allow
|
|
166
|
+
edit: deny
|
|
167
|
+
glob: allow
|
|
168
|
+
grep: allow
|
|
169
|
+
bash:
|
|
170
|
+
"*": deny
|
|
171
|
+
task: allow
|
|
172
|
+
skill: allow
|
|
173
|
+
webfetch: allow
|
|
174
|
+
question: allow
|
|
175
|
+
claude:
|
|
176
|
+
tools:
|
|
177
|
+
- Bash
|
|
178
|
+
- Read
|
|
179
|
+
- Glob
|
|
180
|
+
- Grep
|
|
181
|
+
- WebFetch
|
|
182
|
+
- Task
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
Agent body — unchanged across all platforms.
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### What each platform receives
|
|
189
|
+
|
|
190
|
+
**`.github/agents/dev-workflow.agent.md`** — Copilot output (`metadata:` stripped, all else kept):
|
|
191
|
+
```yaml
|
|
192
|
+
---
|
|
193
|
+
name: dev-workflow
|
|
194
|
+
description: "Dev workflow orchestrator."
|
|
195
|
+
mode: all
|
|
196
|
+
steps: 40
|
|
197
|
+
color: primary
|
|
198
|
+
tools: ['agent', 'read', 'search']
|
|
199
|
+
user-invocable: true
|
|
200
|
+
---
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**`.opencode/agents/dev-workflow.md`** — `permission:` injected from `metadata.opencode`; Copilot-only fields stripped by default:
|
|
204
|
+
```yaml
|
|
205
|
+
---
|
|
206
|
+
name: dev-workflow
|
|
207
|
+
description: "Dev workflow orchestrator."
|
|
208
|
+
mode: all
|
|
209
|
+
steps: 40
|
|
210
|
+
color: primary
|
|
211
|
+
permission:
|
|
212
|
+
read: allow
|
|
213
|
+
edit: deny
|
|
214
|
+
glob: allow
|
|
215
|
+
grep: allow
|
|
216
|
+
bash:
|
|
217
|
+
"*": deny
|
|
218
|
+
task: allow
|
|
219
|
+
skill: allow
|
|
220
|
+
webfetch: allow
|
|
221
|
+
question: allow
|
|
222
|
+
---
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**`.claude/agents/dev-workflow.md`** — Claude-native `tools:` injected from `metadata.claude`; all incompatible fields stripped:
|
|
226
|
+
```yaml
|
|
227
|
+
---
|
|
228
|
+
name: dev-workflow
|
|
229
|
+
description: "Dev workflow orchestrator."
|
|
230
|
+
tools:
|
|
231
|
+
- Bash
|
|
232
|
+
- Read
|
|
233
|
+
- Glob
|
|
234
|
+
- Grep
|
|
235
|
+
- WebFetch
|
|
236
|
+
- Task
|
|
237
|
+
---
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Strip rules
|
|
241
|
+
|
|
242
|
+
The strip rules are loaded from `src/apmw/data/agent-strip-fields.json` and
|
|
243
|
+
must be kept in sync with `apm-hub`'s `config/agent-strip-fields.json`:
|
|
244
|
+
|
|
245
|
+
```json
|
|
246
|
+
{
|
|
247
|
+
"opencode": ["tools", "user-invocable", "agents", "metadata"],
|
|
248
|
+
"copilot": ["mode", "steps", "color", "permission", "task", "metadata"],
|
|
249
|
+
"claude": ["mode", "steps", "color", "tools", "permission", "user-invocable", "agents", "model", "metadata"],
|
|
250
|
+
"cursor": ["mode", "steps", "color", "permission", "metadata"],
|
|
251
|
+
"codex": ["mode", "steps", "color", "permission", "metadata"]
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
`metadata` appears in every platform's list — it is always stripped after extraction.
|
|
256
|
+
|
|
257
|
+
### Processing pipeline
|
|
258
|
+
|
|
259
|
+
For each `*.md` file in the target agent directory, `apmw` runs:
|
|
260
|
+
|
|
261
|
+
1. **Extract** — find `metadata.<platform>` and de-indent its content lines.
|
|
262
|
+
2. **Strip** — remove fields in the platform's strip list (including `metadata`).
|
|
263
|
+
3. **Inject** — if step 1 produced lines: strip any conflicting top-level fields, then append the extracted lines. Metadata wins.
|
|
264
|
+
4. **Rewrite** — write the file only if the frontmatter changed.
|
|
265
|
+
|
|
266
|
+
### OpenCode `permission:` reference
|
|
267
|
+
|
|
268
|
+
```yaml
|
|
269
|
+
permission:
|
|
270
|
+
read: allow # file read
|
|
271
|
+
edit: deny # file write/edit
|
|
272
|
+
glob: allow # file glob search
|
|
273
|
+
grep: allow # content search
|
|
274
|
+
bash:
|
|
275
|
+
"*": deny # deny all shell commands by default
|
|
276
|
+
"git *": allow # allow specific patterns
|
|
277
|
+
"gh *": allow
|
|
278
|
+
task: allow # spawn sub-agents
|
|
279
|
+
skill: allow # load skills
|
|
280
|
+
webfetch: allow # HTTP fetch
|
|
281
|
+
question: allow # interactive prompts
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Claude Code `tools:` reference
|
|
285
|
+
|
|
286
|
+
```yaml
|
|
287
|
+
tools:
|
|
288
|
+
- Bash # shell commands
|
|
289
|
+
- Read # read files
|
|
290
|
+
- Write # create/overwrite files
|
|
291
|
+
- Edit # patch existing files
|
|
292
|
+
- Glob # file pattern search
|
|
293
|
+
- Grep # content search
|
|
294
|
+
- WebFetch # HTTP fetch
|
|
295
|
+
- Task # spawn sub-agents
|
|
296
|
+
- TodoWrite # task list management
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## Development
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
uv sync
|
|
305
|
+
uv run pytest
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
No linter or formatter is configured — don't introduce one speculatively.
|
apmw-0.1.0/install.ps1
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
$ErrorActionPreference = "Stop"
|
|
2
|
+
$ScriptDir = $PSScriptRoot
|
|
3
|
+
|
|
4
|
+
if (Get-Command uv -ErrorAction SilentlyContinue) {
|
|
5
|
+
uv tool install --editable $ScriptDir --force
|
|
6
|
+
} elseif (Get-Command pipx -ErrorAction SilentlyContinue) {
|
|
7
|
+
pipx install --editable $ScriptDir --force
|
|
8
|
+
} else {
|
|
9
|
+
Write-Error "neither 'uv' nor 'pipx' found on PATH"
|
|
10
|
+
exit 1
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
Write-Host "apmw installed — run 'apmw --help' to verify."
|
apmw-0.1.0/install.sh
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
4
|
+
|
|
5
|
+
if command -v uv >/dev/null 2>&1; then
|
|
6
|
+
uv tool install --editable "$SCRIPT_DIR" --force
|
|
7
|
+
elif command -v pipx >/dev/null 2>&1; then
|
|
8
|
+
pipx install --editable "$SCRIPT_DIR" --force
|
|
9
|
+
else
|
|
10
|
+
echo "error: neither 'uv' nor 'pipx' found on PATH" >&2
|
|
11
|
+
exit 1
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
echo "apmw installed — run 'apmw --help' to verify."
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "apmw"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Cross-platform wrapper for the apm CLI — sanitizes agent frontmatter per target"
|
|
5
|
+
requires-python = ">=3.10"
|
|
6
|
+
dependencies = []
|
|
7
|
+
|
|
8
|
+
[project.scripts]
|
|
9
|
+
apmw = "apmw:main"
|
|
10
|
+
|
|
11
|
+
[build-system]
|
|
12
|
+
requires = ["hatchling"]
|
|
13
|
+
build-backend = "hatchling.build"
|
|
14
|
+
|
|
15
|
+
[dependency-groups]
|
|
16
|
+
dev = ["pytest>=8", "pytest-cov>=5"]
|
|
17
|
+
|
|
18
|
+
[tool.pytest.ini_options]
|
|
19
|
+
testpaths = ["tests"]
|
|
20
|
+
pythonpath = ["src"]
|