twagent 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.
- twagent-0.1.0/LICENSE +28 -0
- twagent-0.1.0/PKG-INFO +259 -0
- twagent-0.1.0/README.md +242 -0
- twagent-0.1.0/pyproject.toml +109 -0
- twagent-0.1.0/setup.cfg +4 -0
- twagent-0.1.0/src/twagent/__init__.py +1 -0
- twagent-0.1.0/src/twagent/cli.py +849 -0
- twagent-0.1.0/src/twagent/config.py +439 -0
- twagent-0.1.0/src/twagent/deploy.py +620 -0
- twagent-0.1.0/src/twagent/diff.py +198 -0
- twagent-0.1.0/src/twagent/doctor.py +109 -0
- twagent-0.1.0/src/twagent/extractor.py +135 -0
- twagent-0.1.0/src/twagent/interpolate.py +62 -0
- twagent-0.1.0/src/twagent/mcp.py +147 -0
- twagent-0.1.0/src/twagent/selector.py +191 -0
- twagent-0.1.0/src/twagent.egg-info/PKG-INFO +259 -0
- twagent-0.1.0/src/twagent.egg-info/SOURCES.txt +34 -0
- twagent-0.1.0/src/twagent.egg-info/dependency_links.txt +1 -0
- twagent-0.1.0/src/twagent.egg-info/entry_points.txt +2 -0
- twagent-0.1.0/src/twagent.egg-info/requires.txt +4 -0
- twagent-0.1.0/src/twagent.egg-info/top_level.txt +1 -0
- twagent-0.1.0/tests/test_cli.py +12 -0
- twagent-0.1.0/tests/test_cli_apply.py +400 -0
- twagent-0.1.0/tests/test_cli_diff.py +68 -0
- twagent-0.1.0/tests/test_cli_doctor.py +57 -0
- twagent-0.1.0/tests/test_cli_extract.py +41 -0
- twagent-0.1.0/tests/test_cli_listings.py +97 -0
- twagent-0.1.0/tests/test_config.py +454 -0
- twagent-0.1.0/tests/test_deploy_render.py +67 -0
- twagent-0.1.0/tests/test_deploy_symlinks.py +80 -0
- twagent-0.1.0/tests/test_doctor.py +108 -0
- twagent-0.1.0/tests/test_extractor.py +118 -0
- twagent-0.1.0/tests/test_interpolate.py +106 -0
- twagent-0.1.0/tests/test_mcp.py +160 -0
- twagent-0.1.0/tests/test_performance.py +88 -0
- twagent-0.1.0/tests/test_selector.py +157 -0
twagent-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, sysid
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
twagent-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: twagent
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: twagent CLI
|
|
5
|
+
Author-email: sysid <sysid@gmx.de>
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Requires-Python: >=3.13
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: typer>=0.15
|
|
13
|
+
Requires-Dist: jinja2>=3.1
|
|
14
|
+
Requires-Dist: simple-term-menu>=1.6
|
|
15
|
+
Requires-Dist: rich>=13.0
|
|
16
|
+
Dynamic: license-file
|
|
17
|
+
|
|
18
|
+
<p align="left">
|
|
19
|
+
<img src="doc/twagent-logo.png" alt="twagent logo" width="300">
|
|
20
|
+
</p>
|
|
21
|
+
|
|
22
|
+
Unified configuration framework for AI coding agents — Claude Code, Copilot CLI,
|
|
23
|
+
and Pi today; extensible to others. **One canonical TOML, one CLI, two deploy
|
|
24
|
+
modes.**
|
|
25
|
+
|
|
26
|
+
Replaces and supersedes two earlier tools:
|
|
27
|
+
- `twmcp` (MCP servers only, multiple agents)
|
|
28
|
+
- `devops-binx/agent/render.py` (instructions + skills, multiple agents)
|
|
29
|
+
|
|
30
|
+
## What it does
|
|
31
|
+
|
|
32
|
+
You edit a single canonical TOML at `~/.config/twagent/config.toml` describing
|
|
33
|
+
every artifact you care about — instruction templates, skills, subagents,
|
|
34
|
+
prompts, MCP servers — plus the agents that consume them and the profiles
|
|
35
|
+
that bundle them. Then:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
twagent apply # global sync
|
|
39
|
+
twagent apply --here --select tw-claude # ad-hoc local deploy
|
|
40
|
+
twagent apply -s e2e-emea -a copilot-cli # swap MCP env for one agent
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
twagent renders per-agent instruction files (Jinja2), symlinks file artifacts
|
|
44
|
+
into the right per-agent directories, and compiles MCP server configuration
|
|
45
|
+
into each agent's expected JSON shape.
|
|
46
|
+
|
|
47
|
+
## Mental model
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
┌──────────────┐ ┌─────────────┐
|
|
51
|
+
│ Registries │ → │ Profiles │ → apply (--global | --here)
|
|
52
|
+
│ │ │ (composable│ │
|
|
53
|
+
│ instructions │ │ bundles) │ ▼
|
|
54
|
+
│ skills │ │ │ per-agent paths
|
|
55
|
+
│ subagents │ │ extends... │ (global or cwd-relative)
|
|
56
|
+
│ prompts │ │ │
|
|
57
|
+
│ servers │ │ │
|
|
58
|
+
└──────────────┘ └─────────────┘
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- **Artifacts** live in registries: each one has a `name` + `source` path.
|
|
62
|
+
- **Profiles** bundle artifacts (skills, subagents, prompts, instructions,
|
|
63
|
+
servers) and compose via `extends`.
|
|
64
|
+
- **Agents** declare which capabilities they support and where their files go.
|
|
65
|
+
Each agent has an optional `global_profile` — what bare `apply` deploys.
|
|
66
|
+
- **`--select`** is polymorphic: it accepts profile names AND artifact names,
|
|
67
|
+
mixed. It's exhaustive — only the kinds derivable from the selection deploy.
|
|
68
|
+
|
|
69
|
+
## Quickstart
|
|
70
|
+
|
|
71
|
+
```sh
|
|
72
|
+
twagent edit --init # creates a stub at ~/.config/twagent/config.toml
|
|
73
|
+
|
|
74
|
+
# Migrate existing per-agent MCP into the canonical TOML (stdout-only)
|
|
75
|
+
twagent extract ~/.claude.json >> ~/.config/twagent/config.toml
|
|
76
|
+
|
|
77
|
+
# Inspect
|
|
78
|
+
twagent agents # capabilities + global_profile + paths
|
|
79
|
+
twagent profiles # extends-expanded contents per profile
|
|
80
|
+
twagent status # per-agent global deployment view
|
|
81
|
+
|
|
82
|
+
# Preview, then deploy
|
|
83
|
+
twagent apply -n # dry-run, secrets masked
|
|
84
|
+
twagent apply # do it
|
|
85
|
+
|
|
86
|
+
# Maintenance
|
|
87
|
+
twagent diff # what diverges from config?
|
|
88
|
+
twagent doctor # dangling links / missing sources
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## `apply` — non-trivial examples
|
|
92
|
+
|
|
93
|
+
The deploy command is the surface area. Two modes:
|
|
94
|
+
|
|
95
|
+
| Mode | Default | What it writes |
|
|
96
|
+
|---|---|---|
|
|
97
|
+
| `--global` (default, `-G`) | yes | Each agent's `global_profile`, to canonical paths (`~/.claude/...`, `~/.copilot/...`, `~/.pi/...`) |
|
|
98
|
+
| `--here` (`-H`) | no | A CLI-supplied selection, into the **current directory** via each agent's `paths.project.*` |
|
|
99
|
+
|
|
100
|
+
### Inspect first, deploy second
|
|
101
|
+
|
|
102
|
+
```sh
|
|
103
|
+
twagent apply -n # full preview, masked secrets
|
|
104
|
+
twagent apply -n -S # same, secrets revealed
|
|
105
|
+
twagent apply -n -a claude-code # preview ONE agent
|
|
106
|
+
twagent apply -n -a claude-code -a copilot-cli # preview a subset (repeatable)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Override the default profile, globally
|
|
110
|
+
|
|
111
|
+
When you want to swap your MCP environment "for the day" without editing
|
|
112
|
+
config:
|
|
113
|
+
|
|
114
|
+
```sh
|
|
115
|
+
twagent apply -s e2e-emea # swap globally (all 3 agents
|
|
116
|
+
# with mcp capability)
|
|
117
|
+
twagent apply -s e2e-emea -a copilot-cli # only copilot's MCP file
|
|
118
|
+
# NOTHING ELSE rewritten —
|
|
119
|
+
# no instruction render,
|
|
120
|
+
# no skill symlinks
|
|
121
|
+
twagent apply -s e2e-emea,bkmr-memory # mix: e2e MCP set + one skill
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`--select` is **exhaustive**: kinds that aren't in the selection are not
|
|
125
|
+
touched. This is why `-s e2e-emea` (servers-only) doesn't render CLAUDE.md.
|
|
126
|
+
|
|
127
|
+
### Mixed profile + artifact selection
|
|
128
|
+
|
|
129
|
+
```sh
|
|
130
|
+
twagent apply -s tw-claude # the tw-claude profile in full
|
|
131
|
+
twagent apply -s tw-claude,tw-cucumber-to-http # profile + an extra one-off
|
|
132
|
+
# skill — dedup'd, profile
|
|
133
|
+
# contents win on collision
|
|
134
|
+
twagent apply -s core,pr_review,e2e-us # three profiles composed
|
|
135
|
+
# at the CLI level (no need
|
|
136
|
+
# to define a wrapper profile)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Project-local deploys with `--here`
|
|
140
|
+
|
|
141
|
+
```sh
|
|
142
|
+
cd ~/dev/los/los-cha
|
|
143
|
+
|
|
144
|
+
twagent apply -H -s tw-claude,tw-cucumber-to-http -a claude-code
|
|
145
|
+
twagent apply -H -s tw-copilot,tw-cucumber-to-http -a copilot-cli
|
|
146
|
+
|
|
147
|
+
# Faster: just the project-specific delta
|
|
148
|
+
twagent apply -H -s tw-cucumber-to-http -a claude-code
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
`--here` writes under cwd via each agent's `paths.project.*`. Subdirs are
|
|
152
|
+
created if missing — this is an explicit user act.
|
|
153
|
+
|
|
154
|
+
### Just the instruction templates
|
|
155
|
+
|
|
156
|
+
In v3 instructions are first-class artifacts. Render only the templates:
|
|
157
|
+
|
|
158
|
+
```sh
|
|
159
|
+
twagent apply -s AGENT-md # render the AGENT-md template
|
|
160
|
+
# to every agent's instruction
|
|
161
|
+
# path — nothing else
|
|
162
|
+
twagent apply -s AGENT-md -a claude-code # one agent's instructions only
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Interactive picker
|
|
166
|
+
|
|
167
|
+
```sh
|
|
168
|
+
twagent apply -i # pick from a TUI menu
|
|
169
|
+
# of all profiles + artifacts
|
|
170
|
+
twagent apply -s tw-claude -i # picker pre-checked with
|
|
171
|
+
# tw-claude's contents — add
|
|
172
|
+
# or remove from there
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
`-i` cancellation (Esc) exits without deploying.
|
|
176
|
+
|
|
177
|
+
### Day-to-day flow
|
|
178
|
+
|
|
179
|
+
```sh
|
|
180
|
+
$EDITOR ~/.config/twagent/config.toml # tweak something
|
|
181
|
+
twagent diff # what would change?
|
|
182
|
+
twagent apply # do it
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## `apply` flags
|
|
186
|
+
|
|
187
|
+
| Long | Short | Effect |
|
|
188
|
+
|---|---|---|
|
|
189
|
+
| `--global` | `-G` | Default mode. Deploy each agent's `global_profile` to canonical paths. |
|
|
190
|
+
| `--here` | `-H` | Deploy `--select` set into current directory via `paths.project.*`. |
|
|
191
|
+
| `--agent <id>` | `-a` | Repeatable. Restrict to specific agents. |
|
|
192
|
+
| `--select <names>` | `-s` | csv of profile names AND/OR artifact names. `none` deploys empty. |
|
|
193
|
+
| `--interactive` | `-i` | Open TUI picker. `--select`, if also given, pre-checks items. |
|
|
194
|
+
| `--dry-run` | `-n` | Show plan, write nothing. Secrets masked unless `--show-secrets`. |
|
|
195
|
+
| `--show-secrets` | `-S` | Reveal `${VAR}`-resolved values in dry-run / diff output. |
|
|
196
|
+
|
|
197
|
+
## All commands
|
|
198
|
+
|
|
199
|
+
| Command | Purpose |
|
|
200
|
+
|---|---|
|
|
201
|
+
| `apply` | Deploy resolved configuration to disk. |
|
|
202
|
+
| `diff` | Show pending changes between config and on-disk state. Read-only. |
|
|
203
|
+
| `status` | Per-agent global deployment view (capabilities + `global_profile`). |
|
|
204
|
+
| `agents` | List agents with resolved paths and capabilities (`-j` for JSON). |
|
|
205
|
+
| `profiles` | List profiles with their `extends`-expanded contents. |
|
|
206
|
+
| `doctor` | Health check: dangling symlinks, missing sources, capability mismatches. |
|
|
207
|
+
| `extract <mcp.json>` | Convert an existing per-agent MCP file to canonical TOML on stdout. |
|
|
208
|
+
| `edit` | Open the canonical config in `$EDITOR`. `--init` creates a stub. `-t <name>` opens an instruction template. |
|
|
209
|
+
| `version` | Print the installed version. |
|
|
210
|
+
|
|
211
|
+
### Global flags
|
|
212
|
+
|
|
213
|
+
`--config <path>` (`-c`) — alternate config location.
|
|
214
|
+
`--verbose` (`-v`) — debug logging via `RichHandler` to stderr.
|
|
215
|
+
|
|
216
|
+
## Design
|
|
217
|
+
|
|
218
|
+
Headlines (v3 schema):
|
|
219
|
+
|
|
220
|
+
- **One canonical TOML.** Per-agent paths, vars, MCP-format selection,
|
|
221
|
+
and `global_profile` all live in the same file.
|
|
222
|
+
- **Five artifact registries**: `instructions`, `skills`, `subagents`,
|
|
223
|
+
`prompts`, `servers`. All names are globally unique (shadow rule).
|
|
224
|
+
- **Profiles bundle artifact references** by kind, and compose via `extends`
|
|
225
|
+
(depth-first, parent-first, dedup'd per kind, first-occurrence wins).
|
|
226
|
+
- **No `[[scopes]]` blocks.** Global deployment is per-agent
|
|
227
|
+
(`global_profile`); local deployment is ad-hoc CLI (`apply --here --select`).
|
|
228
|
+
- **Symlinks for file artifacts** (skills/subagents/prompts).
|
|
229
|
+
- **Render for instructions** (Jinja2, `StrictUndefined`).
|
|
230
|
+
- **Compile for MCP** (per-format translator handles agent-specific quirks).
|
|
231
|
+
- **Two-layer Jinja vars:** `[common.vars]` overlaid by `[agents.<id>.vars]`.
|
|
232
|
+
- **`${VAR:-default}` interpolation** inside MCP `env` and `headers` only.
|
|
233
|
+
- **Secrets masked in dry-run / diff output by default.** Opt in with
|
|
234
|
+
`--show-secrets`.
|
|
235
|
+
- **`--select` is exhaustive.** Deploys ONLY the capability kinds the
|
|
236
|
+
selection touches. Bare `apply` (no `--select`) deploys everything in
|
|
237
|
+
the agent's `global_profile`.
|
|
238
|
+
|
|
239
|
+
## Install
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
make install
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Develop
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
uv sync
|
|
249
|
+
make test
|
|
250
|
+
make format
|
|
251
|
+
make lint
|
|
252
|
+
make build
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Python 3.13+. `uv` for everything else.
|
|
256
|
+
|
|
257
|
+
## License
|
|
258
|
+
|
|
259
|
+
BSD-3-Clause.
|
twagent-0.1.0/README.md
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
<p align="left">
|
|
2
|
+
<img src="doc/twagent-logo.png" alt="twagent logo" width="300">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
Unified configuration framework for AI coding agents — Claude Code, Copilot CLI,
|
|
6
|
+
and Pi today; extensible to others. **One canonical TOML, one CLI, two deploy
|
|
7
|
+
modes.**
|
|
8
|
+
|
|
9
|
+
Replaces and supersedes two earlier tools:
|
|
10
|
+
- `twmcp` (MCP servers only, multiple agents)
|
|
11
|
+
- `devops-binx/agent/render.py` (instructions + skills, multiple agents)
|
|
12
|
+
|
|
13
|
+
## What it does
|
|
14
|
+
|
|
15
|
+
You edit a single canonical TOML at `~/.config/twagent/config.toml` describing
|
|
16
|
+
every artifact you care about — instruction templates, skills, subagents,
|
|
17
|
+
prompts, MCP servers — plus the agents that consume them and the profiles
|
|
18
|
+
that bundle them. Then:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
twagent apply # global sync
|
|
22
|
+
twagent apply --here --select tw-claude # ad-hoc local deploy
|
|
23
|
+
twagent apply -s e2e-emea -a copilot-cli # swap MCP env for one agent
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
twagent renders per-agent instruction files (Jinja2), symlinks file artifacts
|
|
27
|
+
into the right per-agent directories, and compiles MCP server configuration
|
|
28
|
+
into each agent's expected JSON shape.
|
|
29
|
+
|
|
30
|
+
## Mental model
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
┌──────────────┐ ┌─────────────┐
|
|
34
|
+
│ Registries │ → │ Profiles │ → apply (--global | --here)
|
|
35
|
+
│ │ │ (composable│ │
|
|
36
|
+
│ instructions │ │ bundles) │ ▼
|
|
37
|
+
│ skills │ │ │ per-agent paths
|
|
38
|
+
│ subagents │ │ extends... │ (global or cwd-relative)
|
|
39
|
+
│ prompts │ │ │
|
|
40
|
+
│ servers │ │ │
|
|
41
|
+
└──────────────┘ └─────────────┘
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- **Artifacts** live in registries: each one has a `name` + `source` path.
|
|
45
|
+
- **Profiles** bundle artifacts (skills, subagents, prompts, instructions,
|
|
46
|
+
servers) and compose via `extends`.
|
|
47
|
+
- **Agents** declare which capabilities they support and where their files go.
|
|
48
|
+
Each agent has an optional `global_profile` — what bare `apply` deploys.
|
|
49
|
+
- **`--select`** is polymorphic: it accepts profile names AND artifact names,
|
|
50
|
+
mixed. It's exhaustive — only the kinds derivable from the selection deploy.
|
|
51
|
+
|
|
52
|
+
## Quickstart
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
twagent edit --init # creates a stub at ~/.config/twagent/config.toml
|
|
56
|
+
|
|
57
|
+
# Migrate existing per-agent MCP into the canonical TOML (stdout-only)
|
|
58
|
+
twagent extract ~/.claude.json >> ~/.config/twagent/config.toml
|
|
59
|
+
|
|
60
|
+
# Inspect
|
|
61
|
+
twagent agents # capabilities + global_profile + paths
|
|
62
|
+
twagent profiles # extends-expanded contents per profile
|
|
63
|
+
twagent status # per-agent global deployment view
|
|
64
|
+
|
|
65
|
+
# Preview, then deploy
|
|
66
|
+
twagent apply -n # dry-run, secrets masked
|
|
67
|
+
twagent apply # do it
|
|
68
|
+
|
|
69
|
+
# Maintenance
|
|
70
|
+
twagent diff # what diverges from config?
|
|
71
|
+
twagent doctor # dangling links / missing sources
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## `apply` — non-trivial examples
|
|
75
|
+
|
|
76
|
+
The deploy command is the surface area. Two modes:
|
|
77
|
+
|
|
78
|
+
| Mode | Default | What it writes |
|
|
79
|
+
|---|---|---|
|
|
80
|
+
| `--global` (default, `-G`) | yes | Each agent's `global_profile`, to canonical paths (`~/.claude/...`, `~/.copilot/...`, `~/.pi/...`) |
|
|
81
|
+
| `--here` (`-H`) | no | A CLI-supplied selection, into the **current directory** via each agent's `paths.project.*` |
|
|
82
|
+
|
|
83
|
+
### Inspect first, deploy second
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
twagent apply -n # full preview, masked secrets
|
|
87
|
+
twagent apply -n -S # same, secrets revealed
|
|
88
|
+
twagent apply -n -a claude-code # preview ONE agent
|
|
89
|
+
twagent apply -n -a claude-code -a copilot-cli # preview a subset (repeatable)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Override the default profile, globally
|
|
93
|
+
|
|
94
|
+
When you want to swap your MCP environment "for the day" without editing
|
|
95
|
+
config:
|
|
96
|
+
|
|
97
|
+
```sh
|
|
98
|
+
twagent apply -s e2e-emea # swap globally (all 3 agents
|
|
99
|
+
# with mcp capability)
|
|
100
|
+
twagent apply -s e2e-emea -a copilot-cli # only copilot's MCP file
|
|
101
|
+
# NOTHING ELSE rewritten —
|
|
102
|
+
# no instruction render,
|
|
103
|
+
# no skill symlinks
|
|
104
|
+
twagent apply -s e2e-emea,bkmr-memory # mix: e2e MCP set + one skill
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`--select` is **exhaustive**: kinds that aren't in the selection are not
|
|
108
|
+
touched. This is why `-s e2e-emea` (servers-only) doesn't render CLAUDE.md.
|
|
109
|
+
|
|
110
|
+
### Mixed profile + artifact selection
|
|
111
|
+
|
|
112
|
+
```sh
|
|
113
|
+
twagent apply -s tw-claude # the tw-claude profile in full
|
|
114
|
+
twagent apply -s tw-claude,tw-cucumber-to-http # profile + an extra one-off
|
|
115
|
+
# skill — dedup'd, profile
|
|
116
|
+
# contents win on collision
|
|
117
|
+
twagent apply -s core,pr_review,e2e-us # three profiles composed
|
|
118
|
+
# at the CLI level (no need
|
|
119
|
+
# to define a wrapper profile)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Project-local deploys with `--here`
|
|
123
|
+
|
|
124
|
+
```sh
|
|
125
|
+
cd ~/dev/los/los-cha
|
|
126
|
+
|
|
127
|
+
twagent apply -H -s tw-claude,tw-cucumber-to-http -a claude-code
|
|
128
|
+
twagent apply -H -s tw-copilot,tw-cucumber-to-http -a copilot-cli
|
|
129
|
+
|
|
130
|
+
# Faster: just the project-specific delta
|
|
131
|
+
twagent apply -H -s tw-cucumber-to-http -a claude-code
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`--here` writes under cwd via each agent's `paths.project.*`. Subdirs are
|
|
135
|
+
created if missing — this is an explicit user act.
|
|
136
|
+
|
|
137
|
+
### Just the instruction templates
|
|
138
|
+
|
|
139
|
+
In v3 instructions are first-class artifacts. Render only the templates:
|
|
140
|
+
|
|
141
|
+
```sh
|
|
142
|
+
twagent apply -s AGENT-md # render the AGENT-md template
|
|
143
|
+
# to every agent's instruction
|
|
144
|
+
# path — nothing else
|
|
145
|
+
twagent apply -s AGENT-md -a claude-code # one agent's instructions only
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Interactive picker
|
|
149
|
+
|
|
150
|
+
```sh
|
|
151
|
+
twagent apply -i # pick from a TUI menu
|
|
152
|
+
# of all profiles + artifacts
|
|
153
|
+
twagent apply -s tw-claude -i # picker pre-checked with
|
|
154
|
+
# tw-claude's contents — add
|
|
155
|
+
# or remove from there
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
`-i` cancellation (Esc) exits without deploying.
|
|
159
|
+
|
|
160
|
+
### Day-to-day flow
|
|
161
|
+
|
|
162
|
+
```sh
|
|
163
|
+
$EDITOR ~/.config/twagent/config.toml # tweak something
|
|
164
|
+
twagent diff # what would change?
|
|
165
|
+
twagent apply # do it
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## `apply` flags
|
|
169
|
+
|
|
170
|
+
| Long | Short | Effect |
|
|
171
|
+
|---|---|---|
|
|
172
|
+
| `--global` | `-G` | Default mode. Deploy each agent's `global_profile` to canonical paths. |
|
|
173
|
+
| `--here` | `-H` | Deploy `--select` set into current directory via `paths.project.*`. |
|
|
174
|
+
| `--agent <id>` | `-a` | Repeatable. Restrict to specific agents. |
|
|
175
|
+
| `--select <names>` | `-s` | csv of profile names AND/OR artifact names. `none` deploys empty. |
|
|
176
|
+
| `--interactive` | `-i` | Open TUI picker. `--select`, if also given, pre-checks items. |
|
|
177
|
+
| `--dry-run` | `-n` | Show plan, write nothing. Secrets masked unless `--show-secrets`. |
|
|
178
|
+
| `--show-secrets` | `-S` | Reveal `${VAR}`-resolved values in dry-run / diff output. |
|
|
179
|
+
|
|
180
|
+
## All commands
|
|
181
|
+
|
|
182
|
+
| Command | Purpose |
|
|
183
|
+
|---|---|
|
|
184
|
+
| `apply` | Deploy resolved configuration to disk. |
|
|
185
|
+
| `diff` | Show pending changes between config and on-disk state. Read-only. |
|
|
186
|
+
| `status` | Per-agent global deployment view (capabilities + `global_profile`). |
|
|
187
|
+
| `agents` | List agents with resolved paths and capabilities (`-j` for JSON). |
|
|
188
|
+
| `profiles` | List profiles with their `extends`-expanded contents. |
|
|
189
|
+
| `doctor` | Health check: dangling symlinks, missing sources, capability mismatches. |
|
|
190
|
+
| `extract <mcp.json>` | Convert an existing per-agent MCP file to canonical TOML on stdout. |
|
|
191
|
+
| `edit` | Open the canonical config in `$EDITOR`. `--init` creates a stub. `-t <name>` opens an instruction template. |
|
|
192
|
+
| `version` | Print the installed version. |
|
|
193
|
+
|
|
194
|
+
### Global flags
|
|
195
|
+
|
|
196
|
+
`--config <path>` (`-c`) — alternate config location.
|
|
197
|
+
`--verbose` (`-v`) — debug logging via `RichHandler` to stderr.
|
|
198
|
+
|
|
199
|
+
## Design
|
|
200
|
+
|
|
201
|
+
Headlines (v3 schema):
|
|
202
|
+
|
|
203
|
+
- **One canonical TOML.** Per-agent paths, vars, MCP-format selection,
|
|
204
|
+
and `global_profile` all live in the same file.
|
|
205
|
+
- **Five artifact registries**: `instructions`, `skills`, `subagents`,
|
|
206
|
+
`prompts`, `servers`. All names are globally unique (shadow rule).
|
|
207
|
+
- **Profiles bundle artifact references** by kind, and compose via `extends`
|
|
208
|
+
(depth-first, parent-first, dedup'd per kind, first-occurrence wins).
|
|
209
|
+
- **No `[[scopes]]` blocks.** Global deployment is per-agent
|
|
210
|
+
(`global_profile`); local deployment is ad-hoc CLI (`apply --here --select`).
|
|
211
|
+
- **Symlinks for file artifacts** (skills/subagents/prompts).
|
|
212
|
+
- **Render for instructions** (Jinja2, `StrictUndefined`).
|
|
213
|
+
- **Compile for MCP** (per-format translator handles agent-specific quirks).
|
|
214
|
+
- **Two-layer Jinja vars:** `[common.vars]` overlaid by `[agents.<id>.vars]`.
|
|
215
|
+
- **`${VAR:-default}` interpolation** inside MCP `env` and `headers` only.
|
|
216
|
+
- **Secrets masked in dry-run / diff output by default.** Opt in with
|
|
217
|
+
`--show-secrets`.
|
|
218
|
+
- **`--select` is exhaustive.** Deploys ONLY the capability kinds the
|
|
219
|
+
selection touches. Bare `apply` (no `--select`) deploys everything in
|
|
220
|
+
the agent's `global_profile`.
|
|
221
|
+
|
|
222
|
+
## Install
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
make install
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Develop
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
uv sync
|
|
232
|
+
make test
|
|
233
|
+
make format
|
|
234
|
+
make lint
|
|
235
|
+
make build
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Python 3.13+. `uv` for everything else.
|
|
239
|
+
|
|
240
|
+
## License
|
|
241
|
+
|
|
242
|
+
BSD-3-Clause.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "twagent"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "twagent CLI"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "BSD-3-Clause"
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "sysid", email = "sysid@gmx.de" },
|
|
9
|
+
]
|
|
10
|
+
requires-python = ">=3.13"
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 3 - Alpha",
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
]
|
|
15
|
+
dependencies = [
|
|
16
|
+
"typer>=0.15",
|
|
17
|
+
"jinja2>=3.1",
|
|
18
|
+
"simple-term-menu>=1.6",
|
|
19
|
+
"rich>=13.0",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[project.scripts]
|
|
23
|
+
twagent = "twagent.cli:app"
|
|
24
|
+
|
|
25
|
+
[dependency-groups]
|
|
26
|
+
dev = [
|
|
27
|
+
"pytest>=8.3.4",
|
|
28
|
+
"pytest-cov>=6.0.0",
|
|
29
|
+
"ruff>=0.8.4",
|
|
30
|
+
"pre-commit>=4.0.0",
|
|
31
|
+
"bump-my-version>=0.28.0",
|
|
32
|
+
"build>=1.2.2",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[build-system]
|
|
36
|
+
requires = ["setuptools>=61"]
|
|
37
|
+
build-backend = "setuptools.build_meta"
|
|
38
|
+
|
|
39
|
+
[tool.setuptools.packages.find]
|
|
40
|
+
where = ["src"]
|
|
41
|
+
include = ["twagent*"]
|
|
42
|
+
|
|
43
|
+
[tool.pytest.ini_options]
|
|
44
|
+
markers = [
|
|
45
|
+
"integration: marks tests as integration tests",
|
|
46
|
+
"experimentation: marks tests as experimental tests, not to be run in CICD",
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
[tool.coverage.run]
|
|
50
|
+
source = ["twagent"]
|
|
51
|
+
omit = [
|
|
52
|
+
"tests/*",
|
|
53
|
+
"**/__main__.py",
|
|
54
|
+
"**/.venv/*",
|
|
55
|
+
"**/site-packages/*",
|
|
56
|
+
]
|
|
57
|
+
branch = true
|
|
58
|
+
|
|
59
|
+
[tool.coverage.report]
|
|
60
|
+
show_missing = true
|
|
61
|
+
skip_covered = true
|
|
62
|
+
fail_under = 85
|
|
63
|
+
|
|
64
|
+
[tool.ruff]
|
|
65
|
+
line-length = 88
|
|
66
|
+
indent-width = 4
|
|
67
|
+
target-version = "py313"
|
|
68
|
+
exclude = [
|
|
69
|
+
".direnv", ".eggs", ".git", ".mypy_cache", ".pytest_cache",
|
|
70
|
+
".ruff_cache", ".venv", "__pypackages__", "build", "dist",
|
|
71
|
+
"node_modules", "site-packages", "venv",
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
[tool.ruff.lint]
|
|
75
|
+
select = ["E4", "E7", "E9", "F"]
|
|
76
|
+
fixable = ["ALL"]
|
|
77
|
+
|
|
78
|
+
[tool.ruff.format]
|
|
79
|
+
quote-style = "double"
|
|
80
|
+
indent-style = "space"
|
|
81
|
+
skip-magic-trailing-comma = false
|
|
82
|
+
line-ending = "auto"
|
|
83
|
+
|
|
84
|
+
[tool.bumpversion]
|
|
85
|
+
current_version = "0.1.0"
|
|
86
|
+
commit = true
|
|
87
|
+
tag = false
|
|
88
|
+
tag_name = "v{new_version}"
|
|
89
|
+
message = "Bump version to {new_version}"
|
|
90
|
+
|
|
91
|
+
[tool.bumpversion.file_patterns]
|
|
92
|
+
"src/twagent/__init__.py" = [
|
|
93
|
+
{search = "__version__ = '{current_version}'", replace = "__version__ = '{new_version}'"},
|
|
94
|
+
]
|
|
95
|
+
"VERSION" = [
|
|
96
|
+
{search = "{current_version}", replace = "{new_version}"},
|
|
97
|
+
]
|
|
98
|
+
"pyproject.toml" = [
|
|
99
|
+
{search = "version = {current_version}", replace = "version = {new_version}"},
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
[[tool.bumpversion.files]]
|
|
103
|
+
filename = "VERSION"
|
|
104
|
+
|
|
105
|
+
[[tool.bumpversion.files]]
|
|
106
|
+
filename = "pyproject.toml"
|
|
107
|
+
|
|
108
|
+
[[tool.bumpversion.files]]
|
|
109
|
+
filename = "src/twagent/__init__.py"
|
twagent-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|