specsmith 0.3.5__tar.gz → 0.3.6.dev164__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.
- {specsmith-0.3.5/src/specsmith.egg-info → specsmith-0.3.6.dev164}/PKG-INFO +12 -7
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/README.md +9 -4
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/pyproject.toml +14 -2
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/agent/providers/ollama.py +21 -7
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/agent/runner.py +104 -5
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/cli.py +178 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/config.py +21 -0
- specsmith-0.3.6.dev164/src/specsmith/profiles.py +425 -0
- specsmith-0.3.6.dev164/src/specsmith/tool_installer.py +471 -0
- specsmith-0.3.6.dev164/src/specsmith/toolrules.py +404 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/vcs_commands.py +3 -2
- {specsmith-0.3.5 → specsmith-0.3.6.dev164/src/specsmith.egg-info}/PKG-INFO +12 -7
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith.egg-info/SOURCES.txt +3 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/LICENSE +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/setup.cfg +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/epistemic/__init__.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/epistemic/belief.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/epistemic/certainty.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/epistemic/failure_graph.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/epistemic/py.typed +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/epistemic/recovery.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/epistemic/session.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/epistemic/stress_tester.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/epistemic/trace.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/__init__.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/__main__.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/agent/__init__.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/agent/core.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/agent/hooks.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/agent/optimizer.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/agent/profiles/epistemic-auditor.md +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/agent/profiles/planner.md +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/agent/profiles/verifier.md +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/agent/providers/__init__.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/agent/providers/anthropic.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/agent/providers/gemini.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/agent/providers/mistral.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/agent/providers/openai.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/agent/skills.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/agent/tools.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/architect.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/auditor.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/auth.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/commands/__init__.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/compressor.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/credit_analyzer.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/credits.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/differ.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/doctor.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/epistemic/__init__.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/epistemic/belief.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/epistemic/certainty.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/epistemic/failure_graph.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/epistemic/recovery.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/epistemic/stress_tester.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/executor.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/exporter.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/gui/__init__.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/gui/app.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/gui/main_window.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/gui/session_tab.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/gui/theme.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/gui/widgets/__init__.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/gui/widgets/chat_view.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/gui/widgets/input_bar.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/gui/widgets/provider_bar.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/gui/widgets/token_meter.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/gui/widgets/tool_panel.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/gui/widgets/update_checker.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/gui/worker.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/importer.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/integrations/__init__.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/integrations/aider.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/integrations/base.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/integrations/claude_code.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/integrations/copilot.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/integrations/cursor.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/integrations/gemini.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/integrations/warp.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/integrations/windsurf.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/languages.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/ledger.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/ollama_cmds.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/patent.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/phase.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/plugins.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/rate_limits.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/releaser.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/requirements.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/scaffolder.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/session.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/agents.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/community/bug_report.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/community/code_of_conduct.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/community/contributing.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/community/feature_request.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/community/license-Apache-2.0.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/community/license-MIT.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/community/pull_request_template.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/community/security.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/docs/architecture.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/docs/mkdocs.yml.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/docs/readthedocs.yaml.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/docs/requirements.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/docs/test-spec.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/docs/workflow.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/editorconfig.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/gitattributes.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/gitignore.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/go/go.mod.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/go/main.go.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/governance/belief-registry.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/governance/context-budget.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/governance/drift-metrics.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/governance/epistemic-axioms.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/governance/failure-modes.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/governance/roles.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/governance/rules.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/governance/uncertainty-map.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/governance/verification.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/governance/workflow.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/js/package.json.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/ledger.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/python/cli.py.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/python/init.py.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/python/pyproject.toml.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/readme.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/rust/Cargo.toml.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/rust/main.rs.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/scripts/exec.cmd.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/scripts/exec.sh.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/scripts/run.cmd.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/scripts/run.sh.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/scripts/setup.cmd.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/scripts/setup.sh.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/templates/workflows/release.yml.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/tools.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/trace.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/updater.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/upgrader.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/validator.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/vcs/__init__.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/vcs/base.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/vcs/bitbucket.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/vcs/github.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/vcs/gitlab.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith/workspace.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith.egg-info/dependency_links.txt +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith.egg-info/entry_points.txt +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith.egg-info/requires.txt +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/src/specsmith.egg-info/top_level.txt +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/tests/test_auditor.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/tests/test_cli.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/tests/test_compressor.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/tests/test_epistemic.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/tests/test_importer.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/tests/test_integrations.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/tests/test_optimizer.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/tests/test_rate_limits.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/tests/test_scaffolder.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/tests/test_smoke.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/tests/test_tools.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/tests/test_validator.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev164}/tests/test_vcs.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: specsmith
|
|
3
|
-
Version: 0.3.
|
|
4
|
-
Summary: Applied Epistemic Engineering toolkit —
|
|
3
|
+
Version: 0.3.6.dev164
|
|
4
|
+
Summary: Applied Epistemic Engineering toolkit — AEE agent sessions, execution profiles, FPGA/HDL governance, tool installer, 50+ CLI commands.
|
|
5
5
|
Author: BitConcepts
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/BitConcepts/specsmith
|
|
@@ -9,7 +9,7 @@ Project-URL: Documentation, https://specsmith.readthedocs.io
|
|
|
9
9
|
Project-URL: Repository, https://github.com/BitConcepts/specsmith
|
|
10
10
|
Project-URL: Changelog, https://github.com/BitConcepts/specsmith/blob/main/CHANGELOG.md
|
|
11
11
|
Project-URL: Issues, https://github.com/BitConcepts/specsmith/issues
|
|
12
|
-
Keywords: agentic,scaffold,governance,agents-md,cli,epistemic-engineering,belief-artifacts,stress-testing,certainty,trace-vault,aee,knowledge-engineering
|
|
12
|
+
Keywords: agentic,scaffold,governance,agents-md,cli,epistemic-engineering,belief-artifacts,stress-testing,certainty,trace-vault,aee,knowledge-engineering,fpga,hdl,vhdl,execution-profiles,tool-installer,llm,ollama,requirements-management
|
|
13
13
|
Classifier: Development Status :: 4 - Beta
|
|
14
14
|
Classifier: Environment :: Console
|
|
15
15
|
Classifier: Intended Audience :: Developers
|
|
@@ -194,15 +194,18 @@ The **specsmith AEE Workbench** VS Code extension is the flagship client:
|
|
|
194
194
|
```
|
|
195
195
|
|
|
196
196
|
**Key features:**
|
|
197
|
-
- **
|
|
197
|
+
- **6-tab Settings panel** — Project / Tools / Files / Updates / Actions / Execution
|
|
198
|
+
- **Execution profiles** — safe (read-only) / standard / open / admin; custom allow/block command lists stored in `scaffold.yml`
|
|
198
199
|
- **AEE phase indicator** — shows current phase with readiness %, Next Phase button, and phase selector
|
|
199
200
|
- **AI agent sessions** — independent process per project, JSONL bridge, chat with file injection
|
|
200
201
|
- **Live model listing** — Anthropic, OpenAI, Gemini, Mistral, local Ollama (GPU-aware)
|
|
201
202
|
- **Ollama integration** — browse curated catalog, download models with progress, task-based suggestions
|
|
202
203
|
- **FPGA/HDL tool support** — vivado, gtkwave, vsg, ghdl, verilator, yosys, nextpnr, and 15 more
|
|
204
|
+
- **Tool installer** — scan installed tools; one-click install via winget/brew/apt for missing tools
|
|
205
|
+
- **Tool rules** — curated AI context rules for 20+ tools (VSG, GHDL, Verilator, ruff, mypy, etc.) auto-injected into agent system prompt
|
|
203
206
|
- **API key management** — stored in OS credential store (Windows Credential Manager / macOS Keychain)
|
|
204
|
-
- **Update checker** — PyPI version check
|
|
205
|
-
- **Auto-open** —
|
|
207
|
+
- **Update checker** — PyPI version check, Install Update button, release channel selector (stable / pre-release)
|
|
208
|
+
- **Auto-open** — Settings panel always opens alongside every new session; never a blank pane
|
|
206
209
|
|
|
207
210
|
**[→ specsmith-vscode on GitHub](https://github.com/BitConcepts/specsmith-vscode)**
|
|
208
211
|
|
|
@@ -232,7 +235,7 @@ specsmith supports FPGA-specific project types with full governance:
|
|
|
232
235
|
|
|
233
236
|
```yaml
|
|
234
237
|
# scaffold.yml
|
|
235
|
-
type: fpga-rtl-
|
|
238
|
+
type: fpga-rtl-amd # or fpga-rtl-intel / fpga-rtl-lattice / fpga-rtl
|
|
236
239
|
fpga_tools:
|
|
237
240
|
- vivado
|
|
238
241
|
- gtkwave
|
|
@@ -264,6 +267,8 @@ Supported tools: **Synthesis:** vivado, quartus, radiant, diamond, gowin.
|
|
|
264
267
|
|
|
265
268
|
**VCS:** `commit` `push` `sync` `branch` `pr` `status`
|
|
266
269
|
|
|
270
|
+
**Tools:** `tools scan [--fpga]` `tools install <tool>` `tools rules [--tool] [--list]`
|
|
271
|
+
|
|
267
272
|
**Tools:** `exec` `ps` `abort` `watch` `optimize` `credits` `self-update`
|
|
268
273
|
|
|
269
274
|
**Auth:** `auth set/list/remove/check`
|
|
@@ -129,15 +129,18 @@ The **specsmith AEE Workbench** VS Code extension is the flagship client:
|
|
|
129
129
|
```
|
|
130
130
|
|
|
131
131
|
**Key features:**
|
|
132
|
-
- **
|
|
132
|
+
- **6-tab Settings panel** — Project / Tools / Files / Updates / Actions / Execution
|
|
133
|
+
- **Execution profiles** — safe (read-only) / standard / open / admin; custom allow/block command lists stored in `scaffold.yml`
|
|
133
134
|
- **AEE phase indicator** — shows current phase with readiness %, Next Phase button, and phase selector
|
|
134
135
|
- **AI agent sessions** — independent process per project, JSONL bridge, chat with file injection
|
|
135
136
|
- **Live model listing** — Anthropic, OpenAI, Gemini, Mistral, local Ollama (GPU-aware)
|
|
136
137
|
- **Ollama integration** — browse curated catalog, download models with progress, task-based suggestions
|
|
137
138
|
- **FPGA/HDL tool support** — vivado, gtkwave, vsg, ghdl, verilator, yosys, nextpnr, and 15 more
|
|
139
|
+
- **Tool installer** — scan installed tools; one-click install via winget/brew/apt for missing tools
|
|
140
|
+
- **Tool rules** — curated AI context rules for 20+ tools (VSG, GHDL, Verilator, ruff, mypy, etc.) auto-injected into agent system prompt
|
|
138
141
|
- **API key management** — stored in OS credential store (Windows Credential Manager / macOS Keychain)
|
|
139
|
-
- **Update checker** — PyPI version check
|
|
140
|
-
- **Auto-open** —
|
|
142
|
+
- **Update checker** — PyPI version check, Install Update button, release channel selector (stable / pre-release)
|
|
143
|
+
- **Auto-open** — Settings panel always opens alongside every new session; never a blank pane
|
|
141
144
|
|
|
142
145
|
**[→ specsmith-vscode on GitHub](https://github.com/BitConcepts/specsmith-vscode)**
|
|
143
146
|
|
|
@@ -167,7 +170,7 @@ specsmith supports FPGA-specific project types with full governance:
|
|
|
167
170
|
|
|
168
171
|
```yaml
|
|
169
172
|
# scaffold.yml
|
|
170
|
-
type: fpga-rtl-
|
|
173
|
+
type: fpga-rtl-amd # or fpga-rtl-intel / fpga-rtl-lattice / fpga-rtl
|
|
171
174
|
fpga_tools:
|
|
172
175
|
- vivado
|
|
173
176
|
- gtkwave
|
|
@@ -199,6 +202,8 @@ Supported tools: **Synthesis:** vivado, quartus, radiant, diamond, gowin.
|
|
|
199
202
|
|
|
200
203
|
**VCS:** `commit` `push` `sync` `branch` `pr` `status`
|
|
201
204
|
|
|
205
|
+
**Tools:** `tools scan [--fpga]` `tools install <tool>` `tools rules [--tool] [--list]`
|
|
206
|
+
|
|
202
207
|
**Tools:** `exec` `ps` `abort` `watch` `optimize` `credits` `self-update`
|
|
203
208
|
|
|
204
209
|
**Auth:** `auth set/list/remove/check`
|
|
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "specsmith"
|
|
7
|
-
version = "0.3.
|
|
8
|
-
description = "Applied Epistemic Engineering toolkit —
|
|
7
|
+
version = "0.3.6.dev164"
|
|
8
|
+
description = "Applied Epistemic Engineering toolkit — AEE agent sessions, execution profiles, FPGA/HDL governance, tool installer, 50+ CLI commands."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
11
11
|
requires-python = ">=3.10"
|
|
@@ -16,6 +16,8 @@ keywords = [
|
|
|
16
16
|
"agentic", "scaffold", "governance", "agents-md", "cli",
|
|
17
17
|
"epistemic-engineering", "belief-artifacts", "stress-testing",
|
|
18
18
|
"certainty", "trace-vault", "aee", "knowledge-engineering",
|
|
19
|
+
"fpga", "hdl", "vhdl", "execution-profiles", "tool-installer",
|
|
20
|
+
"llm", "ollama", "requirements-management",
|
|
19
21
|
]
|
|
20
22
|
classifiers = [
|
|
21
23
|
"Development Status :: 4 - Beta",
|
|
@@ -99,6 +101,11 @@ line-length = 100
|
|
|
99
101
|
[tool.ruff.lint]
|
|
100
102
|
select = ["E", "F", "W", "I", "UP", "B", "SIM"]
|
|
101
103
|
|
|
104
|
+
[tool.ruff.lint.per-file-ignores]
|
|
105
|
+
# Long rule text and install command strings in documentation modules
|
|
106
|
+
"src/specsmith/toolrules.py" = ["E501"]
|
|
107
|
+
"src/specsmith/tool_installer.py" = ["E501"]
|
|
108
|
+
|
|
102
109
|
[tool.mypy]
|
|
103
110
|
python_version = "3.10"
|
|
104
111
|
strict = true
|
|
@@ -120,6 +127,8 @@ module = [
|
|
|
120
127
|
"google.*",
|
|
121
128
|
"yaml",
|
|
122
129
|
"yaml.*",
|
|
130
|
+
"keyring", # optional OS credential store; stubs not published
|
|
131
|
+
"keyring.*",
|
|
123
132
|
]
|
|
124
133
|
ignore_missing_imports = true
|
|
125
134
|
|
|
@@ -140,6 +149,9 @@ module = [
|
|
|
140
149
|
"specsmith.importer",
|
|
141
150
|
"specsmith.agent.providers.gemini",
|
|
142
151
|
"specsmith.agent.runner",
|
|
152
|
+
"specsmith.profiles",
|
|
153
|
+
"specsmith.toolrules",
|
|
154
|
+
"specsmith.tool_installer",
|
|
143
155
|
]
|
|
144
156
|
ignore_errors = true
|
|
145
157
|
|
|
@@ -96,18 +96,32 @@ class OllamaProvider:
|
|
|
96
96
|
) -> CompletionResponse:
|
|
97
97
|
"""Complete using the native /api/chat endpoint.
|
|
98
98
|
|
|
99
|
-
Tool calling is attempted first; if the model returns
|
|
100
|
-
|
|
99
|
+
Tool calling is attempted first; if the model returns HTTP 400
|
|
100
|
+
(tool calling not supported) we retry without tools. If the native
|
|
101
|
+
fallback *also* returns 400 (common when the ``think`` parameter is
|
|
102
|
+
unsupported by an older Ollama server) we strip ``think`` and retry.
|
|
101
103
|
"""
|
|
102
104
|
if tools:
|
|
103
105
|
try:
|
|
104
106
|
return self._complete_with_tools(messages, tools, max_tokens)
|
|
105
107
|
except Exception as exc: # noqa: BLE001
|
|
106
|
-
if _is_tool_fallback_error(exc):
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
if not _is_tool_fallback_error(exc):
|
|
109
|
+
raise
|
|
110
|
+
# Fall through to native (no-tools) completion below
|
|
111
|
+
return self._complete_native_with_fallback(messages, max_tokens)
|
|
112
|
+
|
|
113
|
+
def _complete_native_with_fallback(
|
|
114
|
+
self, messages: list[Message], max_tokens: int
|
|
115
|
+
) -> CompletionResponse:
|
|
116
|
+
"""Call _complete_native; if it fails with 400 and think is set, retry without it."""
|
|
117
|
+
try:
|
|
118
|
+
return self._complete_native(messages, max_tokens)
|
|
119
|
+
except Exception as exc: # noqa: BLE001
|
|
120
|
+
if self._think is not None and _is_tool_fallback_error(exc):
|
|
121
|
+
# Older Ollama doesn't support 'think' — disable permanently for this session
|
|
122
|
+
self._think = None
|
|
123
|
+
return self._complete_native(messages, max_tokens)
|
|
124
|
+
raise
|
|
111
125
|
|
|
112
126
|
def _complete_native(self, messages: list[Message], max_tokens: int) -> CompletionResponse:
|
|
113
127
|
"""Plain chat completion without tools."""
|
|
@@ -112,8 +112,10 @@ def build_system_prompt(
|
|
|
112
112
|
lines = content.splitlines()[:200]
|
|
113
113
|
agents_md = "\n".join(lines)
|
|
114
114
|
|
|
115
|
-
# Load
|
|
115
|
+
# Load scaffold.yml for spec_version, project type, and FPGA tools
|
|
116
116
|
spec_version = "unknown"
|
|
117
|
+
project_type = ""
|
|
118
|
+
fpga_tools: list[str] = []
|
|
117
119
|
scaffold_path = root / "scaffold.yml"
|
|
118
120
|
if scaffold_path.exists():
|
|
119
121
|
try:
|
|
@@ -122,6 +124,8 @@ def build_system_prompt(
|
|
|
122
124
|
with open(scaffold_path) as f:
|
|
123
125
|
raw = yaml.safe_load(f) or {}
|
|
124
126
|
spec_version = raw.get("spec_version", "unknown")
|
|
127
|
+
project_type = str(raw.get("type", ""))
|
|
128
|
+
fpga_tools = list(raw.get("fpga_tools", []) or [])
|
|
125
129
|
except Exception: # noqa: BLE001
|
|
126
130
|
pass
|
|
127
131
|
|
|
@@ -148,9 +152,26 @@ H13: All proposals must state their epistemic boundaries. Hidden assumptions are
|
|
|
148
152
|
agents_md or f"Spec version: {spec_version}. AGENTS.md not found — run specsmith audit."
|
|
149
153
|
)
|
|
150
154
|
|
|
155
|
+
# Load tool-specific rules for this project type
|
|
156
|
+
tool_rules_section = ""
|
|
157
|
+
if project_type:
|
|
158
|
+
try:
|
|
159
|
+
from specsmith.toolrules import get_rules_for_project
|
|
160
|
+
|
|
161
|
+
rules_text = get_rules_for_project(project_type, fpga_tools, max_chars=4000)
|
|
162
|
+
if rules_text:
|
|
163
|
+
tool_rules_section = f"\n## Tool Rules\n{rules_text}\n"
|
|
164
|
+
except Exception: # noqa: BLE001
|
|
165
|
+
pass
|
|
166
|
+
|
|
151
167
|
prompt = f"""You are an AEE-integrated specsmith agent for this project.
|
|
152
|
-
|
|
153
|
-
|
|
168
|
+
|
|
169
|
+
⚠ LANGUAGE RULE (HARD CONSTRAINT — NEVER VIOLATE):
|
|
170
|
+
Respond ONLY in English. Every single response must be in English.
|
|
171
|
+
Never use Chinese (中文), Japanese (日本語), Korean (한국어), French, German, Spanish,
|
|
172
|
+
Arabic, or ANY other non-English language — not even a single word.
|
|
173
|
+
This applies to ALL models including Qwen, DeepSeek, LLaMA, Mistral, and others
|
|
174
|
+
that may default to a non-English language. ENGLISH ONLY, ALWAYS.
|
|
154
175
|
|
|
155
176
|
## Project Governance
|
|
156
177
|
{governance_text}
|
|
@@ -164,7 +185,7 @@ Do not use Chinese, Japanese, Korean, or any other non-English language at any t
|
|
|
164
185
|
- The ledger + accepted repo state is authority
|
|
165
186
|
{aee_section}
|
|
166
187
|
{skills_section}
|
|
167
|
-
|
|
188
|
+
{tool_rules_section}
|
|
168
189
|
## Quick Commands
|
|
169
190
|
Users may type these shortcuts:
|
|
170
191
|
- `start` — new session protocol (sync + update check + load state)
|
|
@@ -213,7 +234,10 @@ class AgentRunner:
|
|
|
213
234
|
}
|
|
214
235
|
|
|
215
236
|
QUICK_COMMANDS = {
|
|
216
|
-
"start":
|
|
237
|
+
"start": (
|
|
238
|
+
"[RESPOND IN ENGLISH ONLY] "
|
|
239
|
+
"Run session start protocol: sync, load AGENTS.md, read last LEDGER.md entries"
|
|
240
|
+
),
|
|
217
241
|
"resume": "Resume from last LEDGER.md entry — summarize state and propose next task",
|
|
218
242
|
"save": "Write a ledger entry summarizing this session's work",
|
|
219
243
|
"audit": "Run specsmith audit --fix",
|
|
@@ -252,6 +276,11 @@ class AgentRunner:
|
|
|
252
276
|
self._hooks = HookRegistry()
|
|
253
277
|
self._system_prompt = ""
|
|
254
278
|
|
|
279
|
+
# Execution profile — loaded from scaffold.yml at session start
|
|
280
|
+
from specsmith import profiles
|
|
281
|
+
|
|
282
|
+
self._profile = profiles.load_from_scaffold(self.project_dir)
|
|
283
|
+
|
|
255
284
|
# Token / credit optimization engine (opt-in)
|
|
256
285
|
self._optimizer: OptimizationEngine | None = (
|
|
257
286
|
OptimizationEngine(
|
|
@@ -474,6 +503,8 @@ class AgentRunner:
|
|
|
474
503
|
self, tool_calls: list[dict[str, Any]], silent: bool = False
|
|
475
504
|
) -> list[ToolResult]:
|
|
476
505
|
"""Execute tool calls and return results."""
|
|
506
|
+
from specsmith import profiles as _profiles
|
|
507
|
+
|
|
477
508
|
results: list[ToolResult] = []
|
|
478
509
|
for tc in tool_calls:
|
|
479
510
|
name = tc.get("name", "")
|
|
@@ -486,6 +517,74 @@ class AgentRunner:
|
|
|
486
517
|
else:
|
|
487
518
|
self._print(f"\n[Tool: {name}]")
|
|
488
519
|
|
|
520
|
+
# ── Execution profile enforcement ──────────────────────────────────
|
|
521
|
+
tool_ok, tool_reason = _profiles.check_tool_allowed(self._profile, name)
|
|
522
|
+
if not tool_ok:
|
|
523
|
+
blocked_msg = f"[BLOCKED by profile '{self._profile.name}'] {tool_reason}"
|
|
524
|
+
if not silent:
|
|
525
|
+
if self._json_events:
|
|
526
|
+
self._emit_event(type="tool_blocked", name=name, reason=tool_reason)
|
|
527
|
+
else:
|
|
528
|
+
self._print(f" ✗ {blocked_msg}")
|
|
529
|
+
results.append(
|
|
530
|
+
ToolResult(
|
|
531
|
+
tool_name=name,
|
|
532
|
+
tool_call_id=call_id,
|
|
533
|
+
content=blocked_msg,
|
|
534
|
+
error=True,
|
|
535
|
+
)
|
|
536
|
+
)
|
|
537
|
+
continue
|
|
538
|
+
|
|
539
|
+
# For run_command: check the command string
|
|
540
|
+
if name == "run_command" and "command" in inputs:
|
|
541
|
+
cmd_ok, cmd_reason = _profiles.check_command_allowed(
|
|
542
|
+
self._profile, str(inputs["command"])
|
|
543
|
+
)
|
|
544
|
+
if not cmd_ok:
|
|
545
|
+
blocked_msg = f"[BLOCKED by profile '{self._profile.name}'] {cmd_reason}"
|
|
546
|
+
if not silent:
|
|
547
|
+
if self._json_events:
|
|
548
|
+
self._emit_event(
|
|
549
|
+
type="tool_blocked",
|
|
550
|
+
name=name,
|
|
551
|
+
command=inputs["command"],
|
|
552
|
+
reason=cmd_reason,
|
|
553
|
+
)
|
|
554
|
+
else:
|
|
555
|
+
self._print(f" ✗ {blocked_msg}")
|
|
556
|
+
results.append(
|
|
557
|
+
ToolResult(
|
|
558
|
+
tool_name=name,
|
|
559
|
+
tool_call_id=call_id,
|
|
560
|
+
content=blocked_msg,
|
|
561
|
+
error=True,
|
|
562
|
+
)
|
|
563
|
+
)
|
|
564
|
+
continue
|
|
565
|
+
|
|
566
|
+
# For write_file: check file-write permission and size
|
|
567
|
+
if name == "write_file" and "content" in inputs:
|
|
568
|
+
write_ok, write_reason = _profiles.check_write_allowed(
|
|
569
|
+
self._profile, str(inputs["content"])
|
|
570
|
+
)
|
|
571
|
+
if not write_ok:
|
|
572
|
+
blocked_msg = f"[BLOCKED by profile '{self._profile.name}'] {write_reason}"
|
|
573
|
+
if not silent:
|
|
574
|
+
if self._json_events:
|
|
575
|
+
self._emit_event(type="tool_blocked", name=name, reason=write_reason)
|
|
576
|
+
else:
|
|
577
|
+
self._print(f" ✗ {blocked_msg}")
|
|
578
|
+
results.append(
|
|
579
|
+
ToolResult(
|
|
580
|
+
tool_name=name,
|
|
581
|
+
tool_call_id=call_id,
|
|
582
|
+
content=blocked_msg,
|
|
583
|
+
error=True,
|
|
584
|
+
)
|
|
585
|
+
)
|
|
586
|
+
continue
|
|
587
|
+
|
|
489
588
|
# Fire pre_tool hooks
|
|
490
589
|
pre_ctx = HookContext(
|
|
491
590
|
trigger=HookTrigger.PRE_TOOL,
|
|
@@ -4059,6 +4059,184 @@ def tools_scan_cmd(project_dir: str, as_json: bool, fpga: bool) -> None:
|
|
|
4059
4059
|
)
|
|
4060
4060
|
|
|
4061
4061
|
|
|
4062
|
+
@tools_group.command(name="install")
|
|
4063
|
+
@click.argument("tool", required=False, default="")
|
|
4064
|
+
@click.option(
|
|
4065
|
+
"--list",
|
|
4066
|
+
"list_all",
|
|
4067
|
+
is_flag=True,
|
|
4068
|
+
default=False,
|
|
4069
|
+
help="List all known installable tools.",
|
|
4070
|
+
)
|
|
4071
|
+
@click.option(
|
|
4072
|
+
"--category",
|
|
4073
|
+
default="",
|
|
4074
|
+
help="Filter by category (fpga, python, c, rust, go, devops, linux, doc, js, other).",
|
|
4075
|
+
)
|
|
4076
|
+
@click.option(
|
|
4077
|
+
"--dry-run",
|
|
4078
|
+
is_flag=True,
|
|
4079
|
+
default=False,
|
|
4080
|
+
help="Print the install command without running it.",
|
|
4081
|
+
)
|
|
4082
|
+
@click.option(
|
|
4083
|
+
"--yes",
|
|
4084
|
+
"-y",
|
|
4085
|
+
is_flag=True,
|
|
4086
|
+
default=False,
|
|
4087
|
+
help="Run the install command without prompting.",
|
|
4088
|
+
)
|
|
4089
|
+
def tools_install_cmd(tool: str, list_all: bool, category: str, dry_run: bool, yes: bool) -> None:
|
|
4090
|
+
"""Show or run the install command for a development tool.
|
|
4091
|
+
|
|
4092
|
+
TOOL is the tool key (e.g. ghdl, ruff, verilator). Run with --list to see
|
|
4093
|
+
all available tools. The best install method for the current platform is
|
|
4094
|
+
selected automatically (winget on Windows, brew on macOS, apt/dnf on Linux).
|
|
4095
|
+
"""
|
|
4096
|
+
import subprocess
|
|
4097
|
+
|
|
4098
|
+
from specsmith.tool_installer import (
|
|
4099
|
+
KNOWN_TOOLS,
|
|
4100
|
+
ToolInstallInfo,
|
|
4101
|
+
get_install_command,
|
|
4102
|
+
list_tools,
|
|
4103
|
+
)
|
|
4104
|
+
|
|
4105
|
+
if list_all or not tool:
|
|
4106
|
+
items: list[ToolInstallInfo] = list_tools(category=category or None)
|
|
4107
|
+
console.print(f"[bold]Known installable tools[/bold] ({len(items)})\n")
|
|
4108
|
+
cats: dict[str, list[ToolInstallInfo]] = {}
|
|
4109
|
+
for t in items:
|
|
4110
|
+
cats.setdefault(t.category, []).append(t)
|
|
4111
|
+
for cat, ts in sorted(cats.items()):
|
|
4112
|
+
console.print(f" [teal]{cat}[/teal]")
|
|
4113
|
+
for t in ts:
|
|
4114
|
+
console.print(f" [dim]{t.key:<25s}[/dim] {t.display_name}")
|
|
4115
|
+
console.print()
|
|
4116
|
+
console.print(
|
|
4117
|
+
" Run [bold]specsmith tools install <key>[/bold] to get the install command."
|
|
4118
|
+
)
|
|
4119
|
+
return
|
|
4120
|
+
|
|
4121
|
+
info = KNOWN_TOOLS.get(tool)
|
|
4122
|
+
if info is None:
|
|
4123
|
+
# Fuzzy fallback: substring match
|
|
4124
|
+
matches = [k for k in KNOWN_TOOLS if tool.lower() in k.lower()]
|
|
4125
|
+
if matches:
|
|
4126
|
+
console.print(
|
|
4127
|
+
f"[yellow]Unknown tool '{tool}'. Did you mean: {', '.join(matches[:5])}?[/yellow]"
|
|
4128
|
+
)
|
|
4129
|
+
else:
|
|
4130
|
+
console.print(
|
|
4131
|
+
f"[red]Unknown tool '{tool}'.[/red] "
|
|
4132
|
+
"Run [bold]specsmith tools install --list[/bold] "
|
|
4133
|
+
"to see available tools."
|
|
4134
|
+
)
|
|
4135
|
+
raise SystemExit(1)
|
|
4136
|
+
|
|
4137
|
+
cmd = get_install_command(tool)
|
|
4138
|
+
if cmd is None:
|
|
4139
|
+
if info.manual:
|
|
4140
|
+
console.print(
|
|
4141
|
+
f"[yellow]No automatic install for '{info.display_name}' on this platform.[/yellow]"
|
|
4142
|
+
)
|
|
4143
|
+
console.print(f" Manual install: {info.manual}")
|
|
4144
|
+
else:
|
|
4145
|
+
console.print(f"[red]No install method known for '{tool}'.[/red]")
|
|
4146
|
+
return
|
|
4147
|
+
|
|
4148
|
+
if info.notes:
|
|
4149
|
+
console.print(f"[dim]Note:[/dim] {info.notes}")
|
|
4150
|
+
|
|
4151
|
+
if dry_run or not yes:
|
|
4152
|
+
console.print(f"\n[bold]Install command for {info.display_name}:[/bold]")
|
|
4153
|
+
console.print(f" [teal]{cmd}[/teal]")
|
|
4154
|
+
if not dry_run and not yes:
|
|
4155
|
+
confirmed = console.input("\nRun this command now? [[bold]y[/bold]/N] ").strip().lower()
|
|
4156
|
+
if confirmed not in ("y", "yes"):
|
|
4157
|
+
console.print("[dim]Aborted.[/dim]")
|
|
4158
|
+
return
|
|
4159
|
+
if not dry_run:
|
|
4160
|
+
console.print(f"[bold]Running:[/bold] {cmd}")
|
|
4161
|
+
result = subprocess.run(cmd, shell=True, check=False) # noqa: S602
|
|
4162
|
+
if result.returncode != 0:
|
|
4163
|
+
console.print(f"[red]Install failed (exit {result.returncode}).[/red]")
|
|
4164
|
+
raise SystemExit(result.returncode)
|
|
4165
|
+
console.print("[green]\u2713[/green] Done.")
|
|
4166
|
+
|
|
4167
|
+
|
|
4168
|
+
@tools_group.command(name="rules")
|
|
4169
|
+
@click.option("--project-dir", type=click.Path(exists=True), default=".")
|
|
4170
|
+
@click.option(
|
|
4171
|
+
"--tool",
|
|
4172
|
+
"tool_key",
|
|
4173
|
+
default="",
|
|
4174
|
+
help="Show rules for a specific tool key (e.g. ghdl, ruff).",
|
|
4175
|
+
)
|
|
4176
|
+
@click.option(
|
|
4177
|
+
"--list",
|
|
4178
|
+
"list_all",
|
|
4179
|
+
is_flag=True,
|
|
4180
|
+
default=False,
|
|
4181
|
+
help="List all tools that have AI context rules.",
|
|
4182
|
+
)
|
|
4183
|
+
def tools_rules_cmd(project_dir: str, tool_key: str, list_all: bool) -> None:
|
|
4184
|
+
"""Show the AI context rules injected into the agent system prompt.
|
|
4185
|
+
|
|
4186
|
+
Without options, shows rules for the current project type (from scaffold.yml).
|
|
4187
|
+
Use --tool to show rules for a specific tool, or --list to see all available.
|
|
4188
|
+
"""
|
|
4189
|
+
from specsmith.toolrules import TOOL_RULES, get_rules_for_project
|
|
4190
|
+
|
|
4191
|
+
if list_all:
|
|
4192
|
+
console.print(f"[bold]Tool rules available[/bold] ({len(TOOL_RULES)} tools):\n")
|
|
4193
|
+
for key in sorted(TOOL_RULES):
|
|
4194
|
+
first_line = TOOL_RULES[key].strip().splitlines()[0].lstrip("# ").strip()
|
|
4195
|
+
console.print(f" [teal]{key:<25s}[/teal] {first_line}")
|
|
4196
|
+
console.print("\n Use [bold]specsmith tools rules --tool <key>[/bold] to view full rules.")
|
|
4197
|
+
return
|
|
4198
|
+
|
|
4199
|
+
if tool_key:
|
|
4200
|
+
if tool_key not in TOOL_RULES:
|
|
4201
|
+
matches = [k for k in TOOL_RULES if tool_key.lower() in k.lower()]
|
|
4202
|
+
if matches:
|
|
4203
|
+
console.print(
|
|
4204
|
+
f"[yellow]No rules for '{tool_key}'. Similar: {', '.join(matches[:5])}[/yellow]"
|
|
4205
|
+
)
|
|
4206
|
+
else:
|
|
4207
|
+
console.print(f"[red]No rules for '{tool_key}'.[/red]")
|
|
4208
|
+
raise SystemExit(1)
|
|
4209
|
+
console.print(TOOL_RULES[tool_key])
|
|
4210
|
+
return
|
|
4211
|
+
|
|
4212
|
+
# Project-level rules from scaffold.yml
|
|
4213
|
+
import yaml
|
|
4214
|
+
|
|
4215
|
+
root = Path(project_dir).resolve()
|
|
4216
|
+
scaffold_path = root / "scaffold.yml"
|
|
4217
|
+
if not scaffold_path.exists():
|
|
4218
|
+
console.print(
|
|
4219
|
+
"[yellow]No scaffold.yml found. "
|
|
4220
|
+
"Run specsmith init or use --tool to view a specific tool.[/yellow]"
|
|
4221
|
+
)
|
|
4222
|
+
raise SystemExit(1)
|
|
4223
|
+
|
|
4224
|
+
with open(scaffold_path) as f:
|
|
4225
|
+
raw = yaml.safe_load(f) or {}
|
|
4226
|
+
project_type = str(raw.get("type", "cli-python"))
|
|
4227
|
+
fpga_tools: list[str] = raw.get("fpga_tools", []) or []
|
|
4228
|
+
|
|
4229
|
+
rules = get_rules_for_project(project_type, fpga_tools, max_chars=20000)
|
|
4230
|
+
if not rules:
|
|
4231
|
+
console.print(
|
|
4232
|
+
f"[yellow]No tool rules configured for project type '{project_type}'.[/yellow]"
|
|
4233
|
+
)
|
|
4234
|
+
return
|
|
4235
|
+
|
|
4236
|
+
console.print(f"[bold]Tool rules for project type:[/bold] [teal]{project_type}[/teal]\n")
|
|
4237
|
+
console.print(rules)
|
|
4238
|
+
|
|
4239
|
+
|
|
4062
4240
|
main.add_command(tools_group)
|
|
4063
4241
|
|
|
4064
4242
|
|
|
@@ -195,6 +195,27 @@ class ProjectConfig(BaseModel):
|
|
|
195
195
|
description="Agent integrations to generate (agents-md, warp, claude-code, cursor, etc.)",
|
|
196
196
|
)
|
|
197
197
|
|
|
198
|
+
# Agent execution profile
|
|
199
|
+
execution_profile: str = Field(
|
|
200
|
+
default="standard",
|
|
201
|
+
description=(
|
|
202
|
+
"Agent execution profile: safe (read-only), standard (default), "
|
|
203
|
+
"open (most commands), admin (no limits)."
|
|
204
|
+
),
|
|
205
|
+
)
|
|
206
|
+
custom_allowed_commands: list[str] = Field(
|
|
207
|
+
default_factory=list,
|
|
208
|
+
description="Extra allowed command prefixes merged with the active execution profile.",
|
|
209
|
+
)
|
|
210
|
+
custom_blocked_commands: list[str] = Field(
|
|
211
|
+
default_factory=list,
|
|
212
|
+
description="Extra blocked command prefixes merged with the active execution profile.",
|
|
213
|
+
)
|
|
214
|
+
custom_blocked_tools: list[str] = Field(
|
|
215
|
+
default_factory=list,
|
|
216
|
+
description="Agent tool names to always block, regardless of the execution profile.",
|
|
217
|
+
)
|
|
218
|
+
|
|
198
219
|
# Applied Epistemic Engineering configuration
|
|
199
220
|
enable_epistemic: bool = Field(
|
|
200
221
|
default=False,
|