fips-agents-cli 0.7.0__tar.gz → 0.8.2__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.
- fips_agents_cli-0.8.2/.github/CODEOWNERS +2 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/.gitignore +4 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/CLAUDE.md +16 -5
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/PKG-INFO +78 -6
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/README.md +77 -5
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/pyproject.toml +1 -1
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/scripts/release.sh +27 -6
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/src/fips_agents_cli/cli.py +2 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/src/fips_agents_cli/commands/create.py +25 -1
- fips_agents_cli-0.8.2/src/fips_agents_cli/commands/vendor.py +154 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/src/fips_agents_cli/tools/git.py +10 -1
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/src/fips_agents_cli/tools/project.py +130 -1
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/src/fips_agents_cli/version.py +1 -1
- fips_agents_cli-0.8.2/tests/fixtures/middleware_template/component.py.j2 +39 -0
- fips_agents_cli-0.8.2/tests/fixtures/middleware_template/test.py.j2 +40 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/tests/test_create.py +4 -4
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/tests/test_generate.py +117 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/.claude/commands/create-release.md +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/.claude/docs-state.json +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/.github/agents/README.md +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/.github/agents/create-release.agent.md +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/.github/workflows/test.yml +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/.github/workflows/workflow.yaml +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/LICENSE +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/RELEASE_CHECKLIST.md +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/docs/PUBLISHING.md +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/docs/QUICK_START_PUBLISHING.md +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/docs/README.md +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/llms.txt +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/planning/AGENT_FRAMEWORK_PLAN.md +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/planning/GENERATOR_IMPLEMENTATION_PLAN.md +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/planning/IMPLEMENTATION_SUMMARY.md +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/planning/MVP-PLAN.md +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/planning/PLAN.md +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/planning/PROMPT_ISSUE.md +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/planning/agent-registry-roadmap.md +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/planning/agent-template-gaps.md +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/planning/composable-agent-capabilities.md +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/research/BAML_RESEARCH_REPORT.md +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/research/Ignite-CLI-Architecture-Analysis.md +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/retrospectives/2026-04-06_issue-triage-v0.3.0/RETRO.md +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/retrospectives/2026-04-10_full-stack-integration/RETRO.md +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/scripts/README.md +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/src/fips_agents_cli/__init__.py +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/src/fips_agents_cli/__main__.py +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/src/fips_agents_cli/commands/__init__.py +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/src/fips_agents_cli/commands/add.py +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/src/fips_agents_cli/commands/generate.py +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/src/fips_agents_cli/commands/model_car.py +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/src/fips_agents_cli/commands/patch.py +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/src/fips_agents_cli/tools/__init__.py +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/src/fips_agents_cli/tools/filesystem.py +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/src/fips_agents_cli/tools/generators.py +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/src/fips_agents_cli/tools/github.py +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/src/fips_agents_cli/tools/patching.py +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/src/fips_agents_cli/tools/validation.py +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/tests/__init__.py +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/tests/conftest.py +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/tests/test_filesystem.py +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/tests/test_generators.py +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/tests/test_github.py +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/tests/test_model_car.py +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/tests/test_project.py +0 -0
- {fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/tests/test_validation.py +0 -0
|
@@ -25,6 +25,7 @@ MANIFEST
|
|
|
25
25
|
|
|
26
26
|
# Virtual environments
|
|
27
27
|
venv/
|
|
28
|
+
.venv/
|
|
28
29
|
env/
|
|
29
30
|
ENV/
|
|
30
31
|
env.bak/
|
|
@@ -88,3 +89,6 @@ tokens.txt
|
|
|
88
89
|
|
|
89
90
|
# Project specific
|
|
90
91
|
setup_structure.sh
|
|
92
|
+
|
|
93
|
+
# Session handoff notes (local-only — content lives in MEMORY.md)
|
|
94
|
+
NEXT_SESSION.md
|
|
@@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|
|
6
6
|
|
|
7
7
|
**fips-agents-cli** is a Python-based CLI tool for scaffolding FIPS-compliant AI agent projects. It scaffolds MCP (Model Context Protocol) servers and AI agent projects from production-ready templates, customizes them for new projects, and prepares them for immediate development use.
|
|
8
8
|
|
|
9
|
-
**Current Status:** `create mcp-server`
|
|
9
|
+
**Current Status:** Scaffolding commands implemented: `create mcp-server`, `create agent`, `create gateway`, `create ui`, `create sandbox`, `create model-car`. Post-scaffolding commands: `generate` (tool, resource, prompt, middleware), `patch` (check, generators, core, docs, build, all), `add` (code-executor), `vendor`. Note: `create workflow` exists in code but is not yet working.
|
|
10
10
|
|
|
11
11
|
## Development Commands
|
|
12
12
|
|
|
@@ -91,18 +91,25 @@ This command will:
|
|
|
91
91
|
|
|
92
92
|
```bash
|
|
93
93
|
# 1. Update version in both files manually or use the script:
|
|
94
|
-
./scripts/release.sh <version>
|
|
94
|
+
./scripts/release.sh <version> [<summary>]
|
|
95
95
|
|
|
96
|
-
#
|
|
96
|
+
# Examples:
|
|
97
|
+
./scripts/release.sh 0.1.2
|
|
97
98
|
./scripts/release.sh 0.1.2 "Add new generator features"
|
|
98
99
|
|
|
99
100
|
# The script handles:
|
|
100
101
|
# - Updating version.py and pyproject.toml
|
|
102
|
+
# - Constructing the release commit message from the project convention
|
|
101
103
|
# - Committing changes (including README.md changelog)
|
|
102
104
|
# - Creating and pushing tag
|
|
103
105
|
# - Triggering GitHub Actions
|
|
104
106
|
```
|
|
105
107
|
|
|
108
|
+
**Release commit message convention**: `chore: Release fips-agents-cli vX.Y.Z`,
|
|
109
|
+
optionally followed by ` — <summary>`. The script always constructs the message
|
|
110
|
+
from the version argument; callers only supply the summary, never the full
|
|
111
|
+
message. This keeps `git log --grep "Release fips-agents-cli"` reliable.
|
|
112
|
+
|
|
106
113
|
**Note**: Always update the changelog in README.md before running the script.
|
|
107
114
|
|
|
108
115
|
See `RELEASE_CHECKLIST.md` for detailed release procedures and troubleshooting.
|
|
@@ -141,7 +148,7 @@ Two cloning strategies exist:
|
|
|
141
148
|
1. Clone monorepo, extract `templates/agent-loop/` subdirectory
|
|
142
149
|
2. Update `pyproject.toml` name field via tomlkit
|
|
143
150
|
3. String-replace `agent-template` → new name in Chart.yaml, values.yaml, AGENTS.md, Containerfile, Makefile
|
|
144
|
-
4. Source directories are NOT renamed — `
|
|
151
|
+
4. Source directories are NOT renamed — the agent-loop template ships with `src/agent.py` directly, not a project-named module
|
|
145
152
|
5. Initialize fresh git repository with initial commit
|
|
146
153
|
|
|
147
154
|
**Rich Console Output**: All user-facing output uses Rich library for:
|
|
@@ -272,7 +279,7 @@ def test_something(temp_dir):
|
|
|
272
279
|
|
|
273
280
|
**Agent Template** (`fips-agents/agent-template` — monorepo, `templates/agent-loop/`):
|
|
274
281
|
- `pyproject.toml` with project name (no entry point scripts)
|
|
275
|
-
- `src/
|
|
282
|
+
- `src/agent.py` user agent subclass (lives at this path directly — no project-named module wrapper)
|
|
276
283
|
- `chart/` Helm chart for OpenShift deployment
|
|
277
284
|
- `agent.yaml` configuration (customized via `/plan-agent`, not during scaffolding)
|
|
278
285
|
- `.claude/commands/` with agent development slash commands
|
|
@@ -325,6 +332,10 @@ Common causes:
|
|
|
325
332
|
2. Dependencies not installed: `pip install -e .[dev]`
|
|
326
333
|
3. Git not configured globally: `git config --global user.email "test@example.com"`
|
|
327
334
|
|
|
335
|
+
## Release Process Notes
|
|
336
|
+
|
|
337
|
+
When creating a new release, always update the changelog in README.md *before* running the release script. The changelog lives in the README under the `## Changelog` heading. Each version should have its own section with the version number and date, listing new features, fixes, and breaking changes. Forgetting the changelog entry leads to patch releases (like v0.8.1) that exist solely to backfill documentation — avoid that by treating the changelog update as a required step in every release.
|
|
338
|
+
|
|
328
339
|
## Repository-Specific Notes
|
|
329
340
|
|
|
330
341
|
- The main branch is `main` (not `master`)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fips-agents-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.2
|
|
4
4
|
Summary: CLI tool for creating and managing FIPS-compliant AI agent projects
|
|
5
5
|
Project-URL: Homepage, https://github.com/fips-agents/fips-agents-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/fips-agents/fips-agents-cli
|
|
@@ -145,6 +145,7 @@ fips-agents add --help
|
|
|
145
145
|
fips-agents create --help
|
|
146
146
|
fips-agents generate --help
|
|
147
147
|
fips-agents patch --help
|
|
148
|
+
fips-agents vendor --help
|
|
148
149
|
```
|
|
149
150
|
|
|
150
151
|
---
|
|
@@ -216,6 +217,12 @@ Creates an AI agent project from the [agent-template](https://github.com/fips-ag
|
|
|
216
217
|
|
|
217
218
|
**Options:** Same as `create mcp-server` (see shared options table above).
|
|
218
219
|
|
|
220
|
+
**Additional option:**
|
|
221
|
+
|
|
222
|
+
| Option | Description |
|
|
223
|
+
|--------|-------------|
|
|
224
|
+
| `--vendored` | Copy fipsagents source into the project instead of using PyPI dependency |
|
|
225
|
+
|
|
219
226
|
**Examples:**
|
|
220
227
|
|
|
221
228
|
```bash
|
|
@@ -227,6 +234,9 @@ fips-agents create agent my-agent --github --org fips-agents
|
|
|
227
234
|
|
|
228
235
|
# Non-interactive mode
|
|
229
236
|
fips-agents create agent my-agent --yes --local
|
|
237
|
+
|
|
238
|
+
# Create with vendored fipsagents source
|
|
239
|
+
fips-agents create agent my-agent --vendored --local
|
|
230
240
|
```
|
|
231
241
|
|
|
232
242
|
#### `create gateway`
|
|
@@ -635,6 +645,45 @@ cd my-research-agent
|
|
|
635
645
|
fips-agents add code-executor
|
|
636
646
|
```
|
|
637
647
|
|
|
648
|
+
---
|
|
649
|
+
|
|
650
|
+
### Vendor Commands
|
|
651
|
+
|
|
652
|
+
The `vendor` command copies the fipsagents source into your agent project, replacing the PyPI dependency. This gives you full control over the fipsagents code.
|
|
653
|
+
|
|
654
|
+
#### `vendor`
|
|
655
|
+
|
|
656
|
+
```bash
|
|
657
|
+
fips-agents vendor [OPTIONS]
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
Copies fipsagents source into `src/fipsagents/` and rewrites `pyproject.toml` to use individual dependencies instead of the fipsagents package.
|
|
661
|
+
|
|
662
|
+
**Options:**
|
|
663
|
+
|
|
664
|
+
| Option | Description |
|
|
665
|
+
|--------|-------------|
|
|
666
|
+
| `--update` | Update an already-vendored project with the latest upstream source |
|
|
667
|
+
| `--version TEXT` | Vendor a specific version tag (e.g., `fipsagents-v0.7.0`). Default: latest main |
|
|
668
|
+
|
|
669
|
+
**Examples:**
|
|
670
|
+
|
|
671
|
+
```bash
|
|
672
|
+
# Vendor into current project
|
|
673
|
+
fips-agents vendor
|
|
674
|
+
|
|
675
|
+
# Vendor a specific version
|
|
676
|
+
fips-agents vendor --version fipsagents-v0.7.0
|
|
677
|
+
|
|
678
|
+
# Update vendored source from upstream
|
|
679
|
+
fips-agents vendor --update
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
**When to use vendored vs. PyPI:**
|
|
683
|
+
|
|
684
|
+
- **PyPI dependency** (default): Best for teams running multiple agents that share the same fipsagents version. Centralized updates.
|
|
685
|
+
- **Vendored source**: Best for agents that need custom BaseAgent modifications, environments with no PyPI access, or when you want to read and debug the fipsagents source locally.
|
|
686
|
+
|
|
638
687
|
## Project Name Requirements
|
|
639
688
|
|
|
640
689
|
Project names must follow these rules:
|
|
@@ -663,10 +712,9 @@ pytest
|
|
|
663
712
|
|
|
664
713
|
```bash
|
|
665
714
|
cd my-research-agent
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
pytest
|
|
715
|
+
make install # Create venv, install dependencies
|
|
716
|
+
make run-local # Start HTTP server on port 8080
|
|
717
|
+
make test # Run tests
|
|
670
718
|
# See AGENTS.md for the /plan-agent slash command workflow
|
|
671
719
|
```
|
|
672
720
|
|
|
@@ -778,7 +826,8 @@ fips-agents-cli/
|
|
|
778
826
|
│ │ ├── create.py # create mcp-server, agent, gateway, ui
|
|
779
827
|
│ │ ├── generate.py # generate tool/resource/prompt/middleware
|
|
780
828
|
│ │ ├── model_car.py # create model-car
|
|
781
|
-
│ │
|
|
829
|
+
│ │ ├── patch.py # patch command
|
|
830
|
+
│ │ └── vendor.py # vendor fipsagents source
|
|
782
831
|
│ └── tools/ # Utility modules
|
|
783
832
|
│ ├── filesystem.py
|
|
784
833
|
│ ├── git.py
|
|
@@ -897,6 +946,29 @@ MIT License - see LICENSE file for details
|
|
|
897
946
|
|
|
898
947
|
## Changelog
|
|
899
948
|
|
|
949
|
+
### Version 0.8.2
|
|
950
|
+
|
|
951
|
+
- Test: New `TestGenerateMiddlewareRealTemplate` integration test renders the real v3.x middleware Jinja2 template against each `--hook-type` and the no-flag case, with templates committed under `tests/fixtures/middleware_template/` so the suite runs offline (#3)
|
|
952
|
+
- Chore: `scripts/release.sh` now constructs the release commit message from the version argument using the project convention (`chore: Release fips-agents-cli vX.Y.Z` with optional ` — <summary>`); convention documented in CLAUDE.md (#4)
|
|
953
|
+
- Docs: Drop broken `See Also` links from `.fips-agents-cli/README.md` in scaffolded MCP projects (#5, fixed in `fips-agents/mcp-server-template`)
|
|
954
|
+
- Docs: Updated CLAUDE.md release section to reflect the new release-script signature
|
|
955
|
+
|
|
956
|
+
### Version 0.8.1
|
|
957
|
+
|
|
958
|
+
- Docs: Updated CLAUDE.md current-status line and added release-process notes
|
|
959
|
+
- Chore: Repository hardening — added CODEOWNERS and enabled main branch protection
|
|
960
|
+
- Chore: Black formatting drift cleanup; removed remaining "framework" language from docs in favor of "toolkit"
|
|
961
|
+
- Chore: `.venv/` and `NEXT_SESSION.md` gitignored
|
|
962
|
+
|
|
963
|
+
### Version 0.8.0
|
|
964
|
+
|
|
965
|
+
- Feature: New `--vendored` flag on `create agent` copies fipsagents source instead of PyPI dependency
|
|
966
|
+
- Feature: New `fips-agents vendor` command for post-scaffold vendoring of existing projects
|
|
967
|
+
- Feature: `fips-agents vendor --update` refreshes vendored source from upstream
|
|
968
|
+
- Feature: `fips-agents vendor --version` pins to a specific fipsagents release tag
|
|
969
|
+
- Fix: `customize_agent_project` now removes monorepo Makefile install line (matching workflow template behavior)
|
|
970
|
+
- Fix: Added `redeploy.sh` to agent project customization file list
|
|
971
|
+
|
|
900
972
|
### Version 0.7.0
|
|
901
973
|
|
|
902
974
|
- Feature: New `add` command group for composable agent capabilities
|
|
@@ -112,6 +112,7 @@ fips-agents add --help
|
|
|
112
112
|
fips-agents create --help
|
|
113
113
|
fips-agents generate --help
|
|
114
114
|
fips-agents patch --help
|
|
115
|
+
fips-agents vendor --help
|
|
115
116
|
```
|
|
116
117
|
|
|
117
118
|
---
|
|
@@ -183,6 +184,12 @@ Creates an AI agent project from the [agent-template](https://github.com/fips-ag
|
|
|
183
184
|
|
|
184
185
|
**Options:** Same as `create mcp-server` (see shared options table above).
|
|
185
186
|
|
|
187
|
+
**Additional option:**
|
|
188
|
+
|
|
189
|
+
| Option | Description |
|
|
190
|
+
|--------|-------------|
|
|
191
|
+
| `--vendored` | Copy fipsagents source into the project instead of using PyPI dependency |
|
|
192
|
+
|
|
186
193
|
**Examples:**
|
|
187
194
|
|
|
188
195
|
```bash
|
|
@@ -194,6 +201,9 @@ fips-agents create agent my-agent --github --org fips-agents
|
|
|
194
201
|
|
|
195
202
|
# Non-interactive mode
|
|
196
203
|
fips-agents create agent my-agent --yes --local
|
|
204
|
+
|
|
205
|
+
# Create with vendored fipsagents source
|
|
206
|
+
fips-agents create agent my-agent --vendored --local
|
|
197
207
|
```
|
|
198
208
|
|
|
199
209
|
#### `create gateway`
|
|
@@ -602,6 +612,45 @@ cd my-research-agent
|
|
|
602
612
|
fips-agents add code-executor
|
|
603
613
|
```
|
|
604
614
|
|
|
615
|
+
---
|
|
616
|
+
|
|
617
|
+
### Vendor Commands
|
|
618
|
+
|
|
619
|
+
The `vendor` command copies the fipsagents source into your agent project, replacing the PyPI dependency. This gives you full control over the fipsagents code.
|
|
620
|
+
|
|
621
|
+
#### `vendor`
|
|
622
|
+
|
|
623
|
+
```bash
|
|
624
|
+
fips-agents vendor [OPTIONS]
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
Copies fipsagents source into `src/fipsagents/` and rewrites `pyproject.toml` to use individual dependencies instead of the fipsagents package.
|
|
628
|
+
|
|
629
|
+
**Options:**
|
|
630
|
+
|
|
631
|
+
| Option | Description |
|
|
632
|
+
|--------|-------------|
|
|
633
|
+
| `--update` | Update an already-vendored project with the latest upstream source |
|
|
634
|
+
| `--version TEXT` | Vendor a specific version tag (e.g., `fipsagents-v0.7.0`). Default: latest main |
|
|
635
|
+
|
|
636
|
+
**Examples:**
|
|
637
|
+
|
|
638
|
+
```bash
|
|
639
|
+
# Vendor into current project
|
|
640
|
+
fips-agents vendor
|
|
641
|
+
|
|
642
|
+
# Vendor a specific version
|
|
643
|
+
fips-agents vendor --version fipsagents-v0.7.0
|
|
644
|
+
|
|
645
|
+
# Update vendored source from upstream
|
|
646
|
+
fips-agents vendor --update
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
**When to use vendored vs. PyPI:**
|
|
650
|
+
|
|
651
|
+
- **PyPI dependency** (default): Best for teams running multiple agents that share the same fipsagents version. Centralized updates.
|
|
652
|
+
- **Vendored source**: Best for agents that need custom BaseAgent modifications, environments with no PyPI access, or when you want to read and debug the fipsagents source locally.
|
|
653
|
+
|
|
605
654
|
## Project Name Requirements
|
|
606
655
|
|
|
607
656
|
Project names must follow these rules:
|
|
@@ -630,10 +679,9 @@ pytest
|
|
|
630
679
|
|
|
631
680
|
```bash
|
|
632
681
|
cd my-research-agent
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
pytest
|
|
682
|
+
make install # Create venv, install dependencies
|
|
683
|
+
make run-local # Start HTTP server on port 8080
|
|
684
|
+
make test # Run tests
|
|
637
685
|
# See AGENTS.md for the /plan-agent slash command workflow
|
|
638
686
|
```
|
|
639
687
|
|
|
@@ -745,7 +793,8 @@ fips-agents-cli/
|
|
|
745
793
|
│ │ ├── create.py # create mcp-server, agent, gateway, ui
|
|
746
794
|
│ │ ├── generate.py # generate tool/resource/prompt/middleware
|
|
747
795
|
│ │ ├── model_car.py # create model-car
|
|
748
|
-
│ │
|
|
796
|
+
│ │ ├── patch.py # patch command
|
|
797
|
+
│ │ └── vendor.py # vendor fipsagents source
|
|
749
798
|
│ └── tools/ # Utility modules
|
|
750
799
|
│ ├── filesystem.py
|
|
751
800
|
│ ├── git.py
|
|
@@ -864,6 +913,29 @@ MIT License - see LICENSE file for details
|
|
|
864
913
|
|
|
865
914
|
## Changelog
|
|
866
915
|
|
|
916
|
+
### Version 0.8.2
|
|
917
|
+
|
|
918
|
+
- Test: New `TestGenerateMiddlewareRealTemplate` integration test renders the real v3.x middleware Jinja2 template against each `--hook-type` and the no-flag case, with templates committed under `tests/fixtures/middleware_template/` so the suite runs offline (#3)
|
|
919
|
+
- Chore: `scripts/release.sh` now constructs the release commit message from the version argument using the project convention (`chore: Release fips-agents-cli vX.Y.Z` with optional ` — <summary>`); convention documented in CLAUDE.md (#4)
|
|
920
|
+
- Docs: Drop broken `See Also` links from `.fips-agents-cli/README.md` in scaffolded MCP projects (#5, fixed in `fips-agents/mcp-server-template`)
|
|
921
|
+
- Docs: Updated CLAUDE.md release section to reflect the new release-script signature
|
|
922
|
+
|
|
923
|
+
### Version 0.8.1
|
|
924
|
+
|
|
925
|
+
- Docs: Updated CLAUDE.md current-status line and added release-process notes
|
|
926
|
+
- Chore: Repository hardening — added CODEOWNERS and enabled main branch protection
|
|
927
|
+
- Chore: Black formatting drift cleanup; removed remaining "framework" language from docs in favor of "toolkit"
|
|
928
|
+
- Chore: `.venv/` and `NEXT_SESSION.md` gitignored
|
|
929
|
+
|
|
930
|
+
### Version 0.8.0
|
|
931
|
+
|
|
932
|
+
- Feature: New `--vendored` flag on `create agent` copies fipsagents source instead of PyPI dependency
|
|
933
|
+
- Feature: New `fips-agents vendor` command for post-scaffold vendoring of existing projects
|
|
934
|
+
- Feature: `fips-agents vendor --update` refreshes vendored source from upstream
|
|
935
|
+
- Feature: `fips-agents vendor --version` pins to a specific fipsagents release tag
|
|
936
|
+
- Fix: `customize_agent_project` now removes monorepo Makefile install line (matching workflow template behavior)
|
|
937
|
+
- Fix: Added `redeploy.sh` to agent project customization file list
|
|
938
|
+
|
|
867
939
|
### Version 0.7.0
|
|
868
940
|
|
|
869
941
|
- Feature: New `add` command group for composable agent capabilities
|
|
@@ -2,9 +2,20 @@
|
|
|
2
2
|
#
|
|
3
3
|
# release.sh - Automated release script for fips-agents-cli
|
|
4
4
|
#
|
|
5
|
-
# Usage: ./scripts/release.sh <version> <
|
|
5
|
+
# Usage: ./scripts/release.sh <version> [<summary>]
|
|
6
6
|
#
|
|
7
|
-
#
|
|
7
|
+
# The release commit message is always constructed from the version using
|
|
8
|
+
# the project convention:
|
|
9
|
+
#
|
|
10
|
+
# chore: Release fips-agents-cli vX.Y.Z
|
|
11
|
+
#
|
|
12
|
+
# If a summary is provided, it is appended after an em-dash:
|
|
13
|
+
#
|
|
14
|
+
# chore: Release fips-agents-cli vX.Y.Z — <summary>
|
|
15
|
+
#
|
|
16
|
+
# Examples:
|
|
17
|
+
# ./scripts/release.sh 0.1.2
|
|
18
|
+
# ./scripts/release.sh 0.1.2 "Add new generator features"
|
|
8
19
|
#
|
|
9
20
|
|
|
10
21
|
set -e # Exit on error
|
|
@@ -29,14 +40,16 @@ print_info() {
|
|
|
29
40
|
}
|
|
30
41
|
|
|
31
42
|
# Check arguments
|
|
32
|
-
if [ $# -
|
|
33
|
-
print_error "Usage: $0 <version> <
|
|
34
|
-
echo "
|
|
43
|
+
if [ $# -lt 1 ] || [ $# -gt 2 ]; then
|
|
44
|
+
print_error "Usage: $0 <version> [<summary>]"
|
|
45
|
+
echo "Examples:"
|
|
46
|
+
echo " $0 0.1.2"
|
|
47
|
+
echo " $0 0.1.2 \"Add new generator features\""
|
|
35
48
|
exit 1
|
|
36
49
|
fi
|
|
37
50
|
|
|
38
51
|
VERSION=$1
|
|
39
|
-
|
|
52
|
+
SUMMARY=${2:-}
|
|
40
53
|
|
|
41
54
|
# Validate version format (x.y.z)
|
|
42
55
|
if ! [[ $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
@@ -45,6 +58,14 @@ if ! [[ $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
|
45
58
|
exit 1
|
|
46
59
|
fi
|
|
47
60
|
|
|
61
|
+
# Construct the conventional release commit message.
|
|
62
|
+
# Convention: "chore: Release fips-agents-cli vX.Y.Z" with optional " — <summary>".
|
|
63
|
+
if [ -n "$SUMMARY" ]; then
|
|
64
|
+
COMMIT_MSG="chore: Release fips-agents-cli v${VERSION} — ${SUMMARY}"
|
|
65
|
+
else
|
|
66
|
+
COMMIT_MSG="chore: Release fips-agents-cli v${VERSION}"
|
|
67
|
+
fi
|
|
68
|
+
|
|
48
69
|
print_info "Preparing release v$VERSION"
|
|
49
70
|
echo
|
|
50
71
|
|
|
@@ -7,6 +7,7 @@ from fips_agents_cli.commands.add import add
|
|
|
7
7
|
from fips_agents_cli.commands.create import create
|
|
8
8
|
from fips_agents_cli.commands.generate import generate
|
|
9
9
|
from fips_agents_cli.commands.patch import patch
|
|
10
|
+
from fips_agents_cli.commands.vendor import vendor
|
|
10
11
|
from fips_agents_cli.version import __version__
|
|
11
12
|
|
|
12
13
|
console = Console()
|
|
@@ -29,6 +30,7 @@ cli.add_command(add)
|
|
|
29
30
|
cli.add_command(create)
|
|
30
31
|
cli.add_command(generate)
|
|
31
32
|
cli.add_command(patch)
|
|
33
|
+
cli.add_command(vendor)
|
|
32
34
|
|
|
33
35
|
|
|
34
36
|
def main():
|
|
@@ -379,6 +379,12 @@ def mcp_server(
|
|
|
379
379
|
default=False,
|
|
380
380
|
help="Create GitHub repo only, don't clone locally",
|
|
381
381
|
)
|
|
382
|
+
@click.option(
|
|
383
|
+
"--vendored",
|
|
384
|
+
is_flag=True,
|
|
385
|
+
default=False,
|
|
386
|
+
help="Copy fipsagents source into the project instead of using PyPI dependency",
|
|
387
|
+
)
|
|
382
388
|
def agent(
|
|
383
389
|
project_name: str,
|
|
384
390
|
target_dir: str | None,
|
|
@@ -390,6 +396,7 @@ def agent(
|
|
|
390
396
|
org: str | None,
|
|
391
397
|
repo_description: str | None,
|
|
392
398
|
remote_only: bool,
|
|
399
|
+
vendored: bool,
|
|
393
400
|
):
|
|
394
401
|
"""
|
|
395
402
|
Create a new AI agent project from template.
|
|
@@ -410,6 +417,8 @@ def agent(
|
|
|
410
417
|
fips-agents create agent my-research-agent --local
|
|
411
418
|
|
|
412
419
|
fips-agents create agent my-research-agent --yes
|
|
420
|
+
|
|
421
|
+
fips-agents create agent my-research-agent --vendored --local
|
|
413
422
|
"""
|
|
414
423
|
try:
|
|
415
424
|
# Step 1: Validate options
|
|
@@ -495,8 +504,17 @@ def agent(
|
|
|
495
504
|
) as progress:
|
|
496
505
|
progress.add_task(description="Cloning agent template...", total=None)
|
|
497
506
|
try:
|
|
507
|
+
post_clone = None
|
|
508
|
+
if vendored:
|
|
509
|
+
from fips_agents_cli.tools.project import vendor_fipsagents_from_clone
|
|
510
|
+
|
|
511
|
+
post_clone = vendor_fipsagents_from_clone
|
|
512
|
+
|
|
498
513
|
template_commit = clone_template_subdir(
|
|
499
|
-
AGENT_TEMPLATE_URL,
|
|
514
|
+
AGENT_TEMPLATE_URL,
|
|
515
|
+
target_path,
|
|
516
|
+
AGENT_TEMPLATE_SUBDIR,
|
|
517
|
+
post_clone_fn=post_clone,
|
|
500
518
|
)
|
|
501
519
|
except Exception as e:
|
|
502
520
|
console.print("\n[red]✗[/red] Failed to clone agent template")
|
|
@@ -517,6 +535,10 @@ def agent(
|
|
|
517
535
|
try:
|
|
518
536
|
customize_agent_project(target_path, project_name, github_repo=github_repo)
|
|
519
537
|
cleanup_template_files(target_path)
|
|
538
|
+
if vendored:
|
|
539
|
+
from fips_agents_cli.tools.project import rewrite_pyproject_for_vendored
|
|
540
|
+
|
|
541
|
+
rewrite_pyproject_for_vendored(target_path)
|
|
520
542
|
if template_commit:
|
|
521
543
|
write_template_info(
|
|
522
544
|
target_path,
|
|
@@ -571,6 +593,8 @@ def agent(
|
|
|
571
593
|
github_url=github_url,
|
|
572
594
|
github_repo=github_repo,
|
|
573
595
|
)
|
|
596
|
+
if vendored:
|
|
597
|
+
console.print("[cyan] Framework source vendored in src/fipsagents/[/cyan]")
|
|
574
598
|
|
|
575
599
|
except KeyboardInterrupt:
|
|
576
600
|
console.print("\n[yellow]⚠[/yellow] Operation cancelled by user")
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"""Vendor command for copying fipsagents source into an agent project."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
import tempfile
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
10
|
+
|
|
11
|
+
console = Console()
|
|
12
|
+
|
|
13
|
+
AGENT_TEMPLATE_URL = "https://github.com/fips-agents/agent-template"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@click.command("vendor")
|
|
17
|
+
@click.option(
|
|
18
|
+
"--update",
|
|
19
|
+
is_flag=True,
|
|
20
|
+
default=False,
|
|
21
|
+
help="Update an already-vendored project with the latest fipsagents source",
|
|
22
|
+
)
|
|
23
|
+
@click.option(
|
|
24
|
+
"--version",
|
|
25
|
+
"tag",
|
|
26
|
+
default=None,
|
|
27
|
+
help="Vendor a specific version (e.g., fipsagents-v0.7.0). Default: latest main.",
|
|
28
|
+
)
|
|
29
|
+
def vendor(update: bool, tag: str | None):
|
|
30
|
+
"""
|
|
31
|
+
Vendor fipsagents source into the current project.
|
|
32
|
+
|
|
33
|
+
Copies the fipsagents framework source code into src/fipsagents/ so you
|
|
34
|
+
have full control over the framework. Replaces the PyPI dependency with
|
|
35
|
+
the individual packages that fipsagents depends on.
|
|
36
|
+
|
|
37
|
+
Use --update to refresh an already-vendored project with the latest
|
|
38
|
+
upstream source. Commit your changes first — update overwrites the
|
|
39
|
+
vendored source.
|
|
40
|
+
|
|
41
|
+
Examples:
|
|
42
|
+
|
|
43
|
+
fips-agents vendor
|
|
44
|
+
|
|
45
|
+
fips-agents vendor --version fipsagents-v0.7.0
|
|
46
|
+
|
|
47
|
+
fips-agents vendor --update
|
|
48
|
+
"""
|
|
49
|
+
try:
|
|
50
|
+
project_path = Path.cwd()
|
|
51
|
+
|
|
52
|
+
# Verify we're in a project directory
|
|
53
|
+
pyproject_path = project_path / "pyproject.toml"
|
|
54
|
+
if not pyproject_path.exists():
|
|
55
|
+
console.print("[red]✗[/red] No pyproject.toml found. Run this from a project root.")
|
|
56
|
+
sys.exit(1)
|
|
57
|
+
|
|
58
|
+
vendored_marker = project_path / "src" / "fipsagents" / "VENDORED"
|
|
59
|
+
is_already_vendored = vendored_marker.exists()
|
|
60
|
+
|
|
61
|
+
if update and not is_already_vendored:
|
|
62
|
+
console.print(
|
|
63
|
+
"[red]✗[/red] Project is not vendored yet. "
|
|
64
|
+
"Run 'fips-agents vendor' (without --update) first."
|
|
65
|
+
)
|
|
66
|
+
sys.exit(1)
|
|
67
|
+
|
|
68
|
+
if is_already_vendored and not update:
|
|
69
|
+
# Show current version and ask if they want to update
|
|
70
|
+
console.print("[yellow]⚠[/yellow] fipsagents is already vendored in this project.")
|
|
71
|
+
console.print(f" Marker: {vendored_marker}")
|
|
72
|
+
console.print(" Use --update to refresh from upstream.")
|
|
73
|
+
sys.exit(0)
|
|
74
|
+
|
|
75
|
+
# Warn about overwriting local changes
|
|
76
|
+
if update:
|
|
77
|
+
console.print(
|
|
78
|
+
"\n[yellow]⚠[/yellow] This will overwrite src/fipsagents/ with " "upstream source."
|
|
79
|
+
)
|
|
80
|
+
console.print(
|
|
81
|
+
" Commit any local modifications first to preserve them in git history.\n"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
console.print("[bold cyan]Vendoring fipsagents[/bold cyan]\n")
|
|
85
|
+
|
|
86
|
+
# Clone the agent-template repo
|
|
87
|
+
import git as gitmodule
|
|
88
|
+
|
|
89
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
90
|
+
tmp_path = Path(tmp) / "repo"
|
|
91
|
+
|
|
92
|
+
with Progress(
|
|
93
|
+
SpinnerColumn(),
|
|
94
|
+
TextColumn("[progress.description]{task.description}"),
|
|
95
|
+
console=console,
|
|
96
|
+
) as progress:
|
|
97
|
+
progress.add_task(description="Cloning agent-template repo...", total=None)
|
|
98
|
+
try:
|
|
99
|
+
repo = gitmodule.Repo.clone_from(
|
|
100
|
+
AGENT_TEMPLATE_URL,
|
|
101
|
+
str(tmp_path),
|
|
102
|
+
branch="main",
|
|
103
|
+
depth=1,
|
|
104
|
+
single_branch=True,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# Checkout specific tag if requested
|
|
108
|
+
if tag:
|
|
109
|
+
try:
|
|
110
|
+
repo.git.fetch("origin", f"refs/tags/{tag}:refs/tags/{tag}")
|
|
111
|
+
repo.git.checkout(tag)
|
|
112
|
+
console.print(f"[green]✓[/green] Checked out tag: {tag}")
|
|
113
|
+
except gitmodule.GitCommandError:
|
|
114
|
+
console.print(
|
|
115
|
+
f"[red]✗[/red] Tag '{tag}' not found. "
|
|
116
|
+
f"Available tags: fipsagents-v0.7.0, etc."
|
|
117
|
+
)
|
|
118
|
+
sys.exit(1)
|
|
119
|
+
|
|
120
|
+
except gitmodule.GitCommandError as e:
|
|
121
|
+
console.print(f"[red]✗[/red] Failed to clone: {e}")
|
|
122
|
+
sys.exit(1)
|
|
123
|
+
|
|
124
|
+
# Copy the fipsagents source
|
|
125
|
+
from fips_agents_cli.tools.project import (
|
|
126
|
+
rewrite_pyproject_for_vendored,
|
|
127
|
+
vendor_fipsagents_from_clone,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
vendor_fipsagents_from_clone(tmp_path, project_path)
|
|
131
|
+
|
|
132
|
+
# Only rewrite pyproject.toml if not already vendored
|
|
133
|
+
# (on --update, deps are already rewritten)
|
|
134
|
+
if not is_already_vendored:
|
|
135
|
+
rewrite_pyproject_for_vendored(project_path)
|
|
136
|
+
|
|
137
|
+
# Success
|
|
138
|
+
console.print("\n[bold green]Vendoring complete![/bold green]\n")
|
|
139
|
+
console.print("Next steps:")
|
|
140
|
+
console.print(" 1. pip install -e . # Reinstall with vendored source")
|
|
141
|
+
console.print(" 2. make test # Verify everything works")
|
|
142
|
+
console.print(" 3. git add src/fipsagents/ # Commit the vendored source")
|
|
143
|
+
console.print("")
|
|
144
|
+
if update:
|
|
145
|
+
console.print(
|
|
146
|
+
"[dim]Tip: Use 'git diff src/fipsagents/' to review upstream changes.[/dim]"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
except KeyboardInterrupt:
|
|
150
|
+
console.print("\n[yellow]Cancelled[/yellow]")
|
|
151
|
+
sys.exit(130)
|
|
152
|
+
except Exception as e:
|
|
153
|
+
console.print(f"\n[red]✗[/red] Unexpected error: {e}")
|
|
154
|
+
sys.exit(1)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Git operations for cloning and initializing repositories."""
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
3
5
|
import shutil
|
|
4
6
|
import tempfile
|
|
5
7
|
from pathlib import Path
|
|
@@ -60,7 +62,12 @@ def clone_template(repo_url: str, target_path: Path, branch: str = "main") -> st
|
|
|
60
62
|
|
|
61
63
|
|
|
62
64
|
def clone_template_subdir(
|
|
63
|
-
repo_url: str,
|
|
65
|
+
repo_url: str,
|
|
66
|
+
target_path: Path,
|
|
67
|
+
subdir: str,
|
|
68
|
+
branch: str = "main",
|
|
69
|
+
*,
|
|
70
|
+
post_clone_fn: callable | None = None,
|
|
64
71
|
) -> str:
|
|
65
72
|
"""
|
|
66
73
|
Clone a monorepo and extract a subdirectory as the project root.
|
|
@@ -89,6 +96,8 @@ def clone_template_subdir(
|
|
|
89
96
|
if not src.is_dir():
|
|
90
97
|
raise FileNotFoundError(f"Subdirectory '{subdir}' not found in template repo")
|
|
91
98
|
shutil.copytree(src, target_path, dirs_exist_ok=True)
|
|
99
|
+
if post_clone_fn is not None:
|
|
100
|
+
post_clone_fn(tmp_path, target_path)
|
|
92
101
|
return commit_hash
|
|
93
102
|
|
|
94
103
|
|
|
@@ -206,6 +206,7 @@ def customize_agent_project(
|
|
|
206
206
|
project_path / "Containerfile",
|
|
207
207
|
project_path / "Makefile",
|
|
208
208
|
project_path / "deploy.sh",
|
|
209
|
+
project_path / "redeploy.sh",
|
|
209
210
|
]
|
|
210
211
|
|
|
211
212
|
for file_path in files_to_update:
|
|
@@ -213,7 +214,31 @@ def customize_agent_project(
|
|
|
213
214
|
|
|
214
215
|
console.print("[green]✓[/green] Updated configuration files")
|
|
215
216
|
|
|
216
|
-
# 3.
|
|
217
|
+
# 3. Remove monorepo-specific base-agent install line from Makefile
|
|
218
|
+
# Cloned projects get fipsagents from PyPI, not a local path
|
|
219
|
+
makefile_path = project_path / "Makefile"
|
|
220
|
+
if makefile_path.exists():
|
|
221
|
+
text = makefile_path.read_text()
|
|
222
|
+
# Remove the monorepo install line and its comments
|
|
223
|
+
lines = text.splitlines(keepends=True)
|
|
224
|
+
filtered = []
|
|
225
|
+
skip_block = False
|
|
226
|
+
for line in lines:
|
|
227
|
+
if "In the monorepo" in line:
|
|
228
|
+
skip_block = True
|
|
229
|
+
continue
|
|
230
|
+
if skip_block and ("fips-agents" in line or "scaffolding step" in line):
|
|
231
|
+
continue
|
|
232
|
+
if skip_block and ("$(PIP) install -e" in line and "packages/" in line):
|
|
233
|
+
skip_block = False
|
|
234
|
+
continue
|
|
235
|
+
skip_block = False
|
|
236
|
+
filtered.append(line)
|
|
237
|
+
makefile_path.write_text("".join(filtered))
|
|
238
|
+
|
|
239
|
+
console.print("[green]✓[/green] Cleaned up Makefile for standalone use")
|
|
240
|
+
|
|
241
|
+
# 4. Replace OWNER/REPO placeholder in Containerfile image source label
|
|
217
242
|
repo_value = github_repo if github_repo else f"OWNER/{new_name}"
|
|
218
243
|
_replace_in_file(project_path / "Containerfile", "OWNER/REPO", repo_value)
|
|
219
244
|
|
|
@@ -541,3 +566,107 @@ def write_template_info(
|
|
|
541
566
|
except Exception as e:
|
|
542
567
|
# Don't fail the entire operation if this fails
|
|
543
568
|
console.print(f"[yellow]⚠[/yellow] Could not write template info: {e}")
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
def vendor_fipsagents_from_clone(
|
|
572
|
+
clone_path: Path,
|
|
573
|
+
project_path: Path,
|
|
574
|
+
) -> None:
|
|
575
|
+
"""
|
|
576
|
+
Copy fipsagents package source from a monorepo clone into the project.
|
|
577
|
+
|
|
578
|
+
Called as a post_clone_fn callback during clone_template_subdir() when
|
|
579
|
+
--vendored is used, or directly by 'fips-agents vendor' for post-scaffold
|
|
580
|
+
vendoring.
|
|
581
|
+
|
|
582
|
+
Args:
|
|
583
|
+
clone_path: Root of the cloned agent-template monorepo
|
|
584
|
+
project_path: Root of the target project
|
|
585
|
+
"""
|
|
586
|
+
pkg_src = clone_path / "packages" / "fipsagents" / "src" / "fipsagents"
|
|
587
|
+
if not pkg_src.is_dir():
|
|
588
|
+
console.print("[red]✗[/red] fipsagents package not found in template repo")
|
|
589
|
+
raise FileNotFoundError(f"Expected fipsagents source at {pkg_src}")
|
|
590
|
+
|
|
591
|
+
dest = project_path / "src" / "fipsagents"
|
|
592
|
+
|
|
593
|
+
# Remove existing vendored source if present (for --update)
|
|
594
|
+
if dest.exists():
|
|
595
|
+
shutil.rmtree(dest)
|
|
596
|
+
|
|
597
|
+
shutil.copytree(pkg_src, dest)
|
|
598
|
+
console.print("[green]✓[/green] Copied fipsagents source to src/fipsagents/")
|
|
599
|
+
|
|
600
|
+
# Copy upstream pyproject.toml as provenance record
|
|
601
|
+
upstream_toml = clone_path / "packages" / "fipsagents" / "pyproject.toml"
|
|
602
|
+
if upstream_toml.exists():
|
|
603
|
+
shutil.copy2(upstream_toml, dest / "UPSTREAM.toml")
|
|
604
|
+
|
|
605
|
+
# Read version from the vendored package
|
|
606
|
+
version = "unknown"
|
|
607
|
+
init_file = dest / "baseagent" / "__init__.py"
|
|
608
|
+
if init_file.exists():
|
|
609
|
+
for line in init_file.read_text().splitlines():
|
|
610
|
+
if line.startswith("__version__"):
|
|
611
|
+
version = line.split("=")[1].strip().strip('"').strip("'")
|
|
612
|
+
break
|
|
613
|
+
|
|
614
|
+
# Write VENDORED marker
|
|
615
|
+
marker = dest / "VENDORED"
|
|
616
|
+
marker.write_text(
|
|
617
|
+
f"version: {version}\n"
|
|
618
|
+
f"vendored: {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M UTC')}\n"
|
|
619
|
+
f"source: https://github.com/fips-agents/agent-template\n"
|
|
620
|
+
f"\n"
|
|
621
|
+
f"This fipsagents source is vendored into the project.\n"
|
|
622
|
+
f"To update: fips-agents vendor --update\n"
|
|
623
|
+
)
|
|
624
|
+
console.print(f"[green]✓[/green] Wrote VENDORED marker (version {version})")
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
def rewrite_pyproject_for_vendored(project_path: Path) -> None:
|
|
628
|
+
"""
|
|
629
|
+
Rewrite pyproject.toml to replace fipsagents pip dependency with
|
|
630
|
+
the individual dependencies that fipsagents itself declares.
|
|
631
|
+
|
|
632
|
+
Args:
|
|
633
|
+
project_path: Root of the target project
|
|
634
|
+
"""
|
|
635
|
+
pyproject_path = project_path / "pyproject.toml"
|
|
636
|
+
if not pyproject_path.exists():
|
|
637
|
+
return
|
|
638
|
+
|
|
639
|
+
with open(pyproject_path) as f:
|
|
640
|
+
pyproject = tomlkit.parse(f.read())
|
|
641
|
+
|
|
642
|
+
# Replace fipsagents[server] with its individual dependencies
|
|
643
|
+
vendored_deps = [
|
|
644
|
+
"litellm>=1.83.0",
|
|
645
|
+
"fastmcp>=3.0.0",
|
|
646
|
+
"pydantic>=2.0",
|
|
647
|
+
"pyyaml",
|
|
648
|
+
"httpx",
|
|
649
|
+
"python-frontmatter",
|
|
650
|
+
"fastapi>=0.110",
|
|
651
|
+
"uvicorn[standard]>=0.27",
|
|
652
|
+
]
|
|
653
|
+
|
|
654
|
+
if "project" in pyproject and "dependencies" in pyproject["project"]:
|
|
655
|
+
old_deps = list(pyproject["project"]["dependencies"])
|
|
656
|
+
new_deps = [d for d in old_deps if "fipsagents" not in d.lower()]
|
|
657
|
+
new_deps.extend(vendored_deps)
|
|
658
|
+
pyproject["project"]["dependencies"] = new_deps
|
|
659
|
+
|
|
660
|
+
# Fix optional memory dependency too
|
|
661
|
+
if "project" in pyproject:
|
|
662
|
+
opt_deps = pyproject["project"].get("optional-dependencies", {})
|
|
663
|
+
if "memory" in opt_deps:
|
|
664
|
+
old_memory = list(opt_deps["memory"])
|
|
665
|
+
new_memory = [d for d in old_memory if "fipsagents" not in d.lower()]
|
|
666
|
+
new_memory.append("memoryhub")
|
|
667
|
+
opt_deps["memory"] = new_memory
|
|
668
|
+
|
|
669
|
+
with open(pyproject_path, "w") as f:
|
|
670
|
+
f.write(tomlkit.dumps(pyproject))
|
|
671
|
+
|
|
672
|
+
console.print("[green]✓[/green] Updated pyproject.toml (vendored dependencies)")
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""{{ description }}"""
|
|
2
|
+
|
|
3
|
+
import mcp.types as mt
|
|
4
|
+
from fastmcp.server.middleware import CallNext, Middleware, MiddlewareContext
|
|
5
|
+
from fastmcp.tools.tool import ToolResult
|
|
6
|
+
|
|
7
|
+
from src.core.logging import get_logger
|
|
8
|
+
|
|
9
|
+
log = get_logger("middleware.{{ component_name }}")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class {{ component_name | replace('_', ' ') | title | replace(' ', '') }}Middleware(Middleware):
|
|
13
|
+
"""{{ description }}
|
|
14
|
+
|
|
15
|
+
To activate, add an instance to the middleware=[] list in create_server()
|
|
16
|
+
(see src/core/server.py).
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
async def on_call_tool(
|
|
20
|
+
self,
|
|
21
|
+
context: MiddlewareContext[mt.CallToolRequestParams],
|
|
22
|
+
call_next: CallNext[mt.CallToolRequestParams, ToolResult],
|
|
23
|
+
) -> ToolResult:
|
|
24
|
+
"""Wrap tool execution with {{ component_name }} logic."""
|
|
25
|
+
tool_name = context.message.name
|
|
26
|
+
|
|
27
|
+
log.debug(f"{{ component_name }} middleware: before {tool_name}")
|
|
28
|
+
|
|
29
|
+
# TODO: Add pre-execution logic here
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
result = await call_next(context)
|
|
33
|
+
except Exception as e:
|
|
34
|
+
log.error(f"{{ component_name }} middleware: error in {tool_name}: {e}")
|
|
35
|
+
raise
|
|
36
|
+
|
|
37
|
+
# TODO: Add post-execution logic here
|
|
38
|
+
|
|
39
|
+
return result
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Tests for {{ component_name }} middleware."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from unittest.mock import AsyncMock, MagicMock
|
|
5
|
+
import mcp.types as mt
|
|
6
|
+
|
|
7
|
+
from src.middleware.{{ module_path }} import {{ component_name | replace('_', ' ') | title | replace(' ', '') }}Middleware
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.mark.asyncio
|
|
11
|
+
async def test_{{ component_name }}_success():
|
|
12
|
+
"""Test middleware handles successful execution."""
|
|
13
|
+
middleware = {{ component_name | replace('_', ' ') | title | replace(' ', '') }}Middleware()
|
|
14
|
+
|
|
15
|
+
# Create mock context
|
|
16
|
+
context = MagicMock(spec=mt.CallToolRequestParams)
|
|
17
|
+
context.message.name = "test_tool"
|
|
18
|
+
|
|
19
|
+
# Create mock call_next
|
|
20
|
+
call_next = AsyncMock(return_value="success_result")
|
|
21
|
+
|
|
22
|
+
# Execute middleware
|
|
23
|
+
result = await middleware.on_call_tool(context, call_next)
|
|
24
|
+
|
|
25
|
+
assert result == "success_result"
|
|
26
|
+
call_next.assert_called_once_with(context)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@pytest.mark.asyncio
|
|
30
|
+
async def test_{{ component_name }}_error():
|
|
31
|
+
"""Test middleware handles errors."""
|
|
32
|
+
middleware = {{ component_name | replace('_', ' ') | title | replace(' ', '') }}Middleware()
|
|
33
|
+
|
|
34
|
+
context = MagicMock(spec=mt.CallToolRequestParams)
|
|
35
|
+
context.message.name = "failing_tool"
|
|
36
|
+
|
|
37
|
+
call_next = AsyncMock(side_effect=ValueError("Test error"))
|
|
38
|
+
|
|
39
|
+
with pytest.raises(ValueError, match="Test error"):
|
|
40
|
+
await middleware.on_call_tool(context, call_next)
|
|
@@ -661,7 +661,7 @@ class TestCreateAgent:
|
|
|
661
661
|
mock_is_git_installed.return_value = True
|
|
662
662
|
mock_gh_installed.return_value = False
|
|
663
663
|
|
|
664
|
-
def create_minimal_structure(url, target_path, subdir, branch=None):
|
|
664
|
+
def create_minimal_structure(url, target_path, subdir, branch=None, **kwargs):
|
|
665
665
|
target_path.mkdir(parents=True, exist_ok=True)
|
|
666
666
|
(target_path / "pyproject.toml").write_text('[project]\nname = "agent-template"')
|
|
667
667
|
|
|
@@ -698,7 +698,7 @@ class TestCreateAgent:
|
|
|
698
698
|
mock_is_git_installed.return_value = True
|
|
699
699
|
mock_gh_installed.return_value = False
|
|
700
700
|
|
|
701
|
-
def create_minimal_structure(url, target_path, subdir, branch=None):
|
|
701
|
+
def create_minimal_structure(url, target_path, subdir, branch=None, **kwargs):
|
|
702
702
|
target_path.mkdir(parents=True, exist_ok=True)
|
|
703
703
|
(target_path / "pyproject.toml").write_text('[project]\nname = "agent-template"')
|
|
704
704
|
|
|
@@ -763,7 +763,7 @@ class TestCreateAgentGitHub:
|
|
|
763
763
|
mock_get_username.return_value = "testuser"
|
|
764
764
|
mock_push.return_value = True
|
|
765
765
|
|
|
766
|
-
def create_minimal_structure(url, target_path, subdir, branch=None):
|
|
766
|
+
def create_minimal_structure(url, target_path, subdir, branch=None, **kwargs):
|
|
767
767
|
target_path.mkdir(parents=True, exist_ok=True)
|
|
768
768
|
(target_path / "pyproject.toml").write_text('[project]\nname = "agent-template"')
|
|
769
769
|
|
|
@@ -839,7 +839,7 @@ class TestCreateAgentGitHub:
|
|
|
839
839
|
mock_get_username.return_value = "testuser"
|
|
840
840
|
mock_push.return_value = True
|
|
841
841
|
|
|
842
|
-
def create_minimal_structure(url, target_path, subdir, branch=None):
|
|
842
|
+
def create_minimal_structure(url, target_path, subdir, branch=None, **kwargs):
|
|
843
843
|
target_path.mkdir(parents=True, exist_ok=True)
|
|
844
844
|
(target_path / "pyproject.toml").write_text('[project]\nname = "agent-template"')
|
|
845
845
|
|
|
@@ -398,6 +398,123 @@ class TestGenerateMiddlewareCommand:
|
|
|
398
398
|
os.chdir(original_cwd)
|
|
399
399
|
|
|
400
400
|
|
|
401
|
+
@pytest.fixture
|
|
402
|
+
def mock_mcp_project_with_real_middleware_template(tmp_path):
|
|
403
|
+
"""Mock MCP project that uses the real middleware Jinja2 templates as fixtures.
|
|
404
|
+
|
|
405
|
+
Templates are committed under tests/fixtures/middleware_template/ so the
|
|
406
|
+
test runs offline and is not coupled to mcp-server-template's git state.
|
|
407
|
+
Refresh the fixtures when the upstream template changes meaningfully.
|
|
408
|
+
"""
|
|
409
|
+
import shutil
|
|
410
|
+
from pathlib import Path
|
|
411
|
+
|
|
412
|
+
pyproject_content = """
|
|
413
|
+
[project]
|
|
414
|
+
name = "test-mcp-server"
|
|
415
|
+
version = "0.1.0"
|
|
416
|
+
dependencies = [
|
|
417
|
+
"fastmcp>=3.0.0",
|
|
418
|
+
]
|
|
419
|
+
"""
|
|
420
|
+
(tmp_path / "pyproject.toml").write_text(pyproject_content)
|
|
421
|
+
|
|
422
|
+
for component_dir in ["middleware"]:
|
|
423
|
+
(tmp_path / "src" / component_dir).mkdir(parents=True)
|
|
424
|
+
(tmp_path / "tests" / component_dir).mkdir(parents=True)
|
|
425
|
+
|
|
426
|
+
fixture_dir = Path(__file__).parent / "fixtures" / "middleware_template"
|
|
427
|
+
generators_dir = tmp_path / ".fips-agents-cli" / "generators" / "middleware"
|
|
428
|
+
generators_dir.mkdir(parents=True)
|
|
429
|
+
shutil.copy(fixture_dir / "component.py.j2", generators_dir / "component.py.j2")
|
|
430
|
+
shutil.copy(fixture_dir / "test.py.j2", generators_dir / "test.py.j2")
|
|
431
|
+
|
|
432
|
+
return tmp_path
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
class TestGenerateMiddlewareRealTemplate:
|
|
436
|
+
"""Integration tests that render the real v3.x middleware template.
|
|
437
|
+
|
|
438
|
+
These guard against regressions that would slip past the synthetic
|
|
439
|
+
`mock_mcp_project` fixture, which uses a stripped-down template that
|
|
440
|
+
doesn't exercise fastmcp imports, class structure, or the real
|
|
441
|
+
Jinja2 conditionals.
|
|
442
|
+
"""
|
|
443
|
+
|
|
444
|
+
@pytest.mark.parametrize("hook_type", ["before_tool", "after_tool", "on_error"])
|
|
445
|
+
def test_real_template_renders_for_each_hook_type(
|
|
446
|
+
self, runner, mock_mcp_project_with_real_middleware_template, hook_type
|
|
447
|
+
):
|
|
448
|
+
"""Each --hook-type renders valid Python against the real v3.x template."""
|
|
449
|
+
import ast
|
|
450
|
+
import os
|
|
451
|
+
|
|
452
|
+
project = mock_mcp_project_with_real_middleware_template
|
|
453
|
+
original_cwd = os.getcwd()
|
|
454
|
+
try:
|
|
455
|
+
os.chdir(project)
|
|
456
|
+
result = runner.invoke(
|
|
457
|
+
generate,
|
|
458
|
+
[
|
|
459
|
+
"middleware",
|
|
460
|
+
f"{hook_type}_mw",
|
|
461
|
+
"--description",
|
|
462
|
+
f"{hook_type} hook middleware",
|
|
463
|
+
"--hook-type",
|
|
464
|
+
hook_type,
|
|
465
|
+
],
|
|
466
|
+
catch_exceptions=False,
|
|
467
|
+
)
|
|
468
|
+
assert result.exit_code == 0, result.output
|
|
469
|
+
|
|
470
|
+
rendered = project / "src" / "middleware" / f"{hook_type}_mw.py"
|
|
471
|
+
assert rendered.exists()
|
|
472
|
+
|
|
473
|
+
source = rendered.read_text()
|
|
474
|
+
ast.parse(source) # raises if invalid Python
|
|
475
|
+
assert "from fastmcp.server.middleware import" in source
|
|
476
|
+
assert "class " in source and "Middleware(Middleware):" in source
|
|
477
|
+
assert "async def on_call_tool" in source
|
|
478
|
+
|
|
479
|
+
test_file = project / "tests" / "middleware" / f"test_{hook_type}_mw.py"
|
|
480
|
+
assert test_file.exists()
|
|
481
|
+
ast.parse(test_file.read_text())
|
|
482
|
+
finally:
|
|
483
|
+
os.chdir(original_cwd)
|
|
484
|
+
|
|
485
|
+
def test_real_template_renders_without_hook_type(
|
|
486
|
+
self, runner, mock_mcp_project_with_real_middleware_template
|
|
487
|
+
):
|
|
488
|
+
"""Omitting --hook-type still produces a valid generic wrapper (backward compat)."""
|
|
489
|
+
import ast
|
|
490
|
+
import os
|
|
491
|
+
|
|
492
|
+
project = mock_mcp_project_with_real_middleware_template
|
|
493
|
+
original_cwd = os.getcwd()
|
|
494
|
+
try:
|
|
495
|
+
os.chdir(project)
|
|
496
|
+
result = runner.invoke(
|
|
497
|
+
generate,
|
|
498
|
+
[
|
|
499
|
+
"middleware",
|
|
500
|
+
"generic_mw",
|
|
501
|
+
"--description",
|
|
502
|
+
"generic middleware",
|
|
503
|
+
],
|
|
504
|
+
catch_exceptions=False,
|
|
505
|
+
)
|
|
506
|
+
assert result.exit_code == 0, result.output
|
|
507
|
+
|
|
508
|
+
rendered = project / "src" / "middleware" / "generic_mw.py"
|
|
509
|
+
assert rendered.exists()
|
|
510
|
+
source = rendered.read_text()
|
|
511
|
+
ast.parse(source)
|
|
512
|
+
assert "class GenericMwMiddleware(Middleware):" in source
|
|
513
|
+
assert "async def on_call_tool" in source
|
|
514
|
+
finally:
|
|
515
|
+
os.chdir(original_cwd)
|
|
516
|
+
|
|
517
|
+
|
|
401
518
|
class TestGenerateErrorCases:
|
|
402
519
|
"""Tests for error handling."""
|
|
403
520
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fips_agents_cli-0.7.0 → fips_agents_cli-0.8.2}/research/Ignite-CLI-Architecture-Analysis.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|