specsmith 0.3.5__tar.gz → 0.3.6.dev170__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.dev170}/PKG-INFO +26 -9
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/README.md +23 -6
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/pyproject.toml +14 -2
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/agent/providers/ollama.py +21 -7
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/agent/runner.py +104 -5
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/agent/tools.py +11 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/cli.py +178 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/config.py +38 -5
- specsmith-0.3.6.dev170/src/specsmith/profiles.py +425 -0
- specsmith-0.3.6.dev170/src/specsmith/tool_installer.py +471 -0
- specsmith-0.3.6.dev170/src/specsmith/toolrules.py +404 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/vcs_commands.py +3 -2
- {specsmith-0.3.5 → specsmith-0.3.6.dev170/src/specsmith.egg-info}/PKG-INFO +26 -9
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith.egg-info/SOURCES.txt +3 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/LICENSE +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/setup.cfg +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/epistemic/__init__.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/epistemic/belief.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/epistemic/certainty.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/epistemic/failure_graph.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/epistemic/py.typed +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/epistemic/recovery.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/epistemic/session.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/epistemic/stress_tester.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/epistemic/trace.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/__init__.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/__main__.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/agent/__init__.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/agent/core.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/agent/hooks.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/agent/optimizer.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/agent/profiles/epistemic-auditor.md +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/agent/profiles/planner.md +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/agent/profiles/verifier.md +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/agent/providers/__init__.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/agent/providers/anthropic.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/agent/providers/gemini.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/agent/providers/mistral.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/agent/providers/openai.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/agent/skills.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/architect.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/auditor.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/auth.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/commands/__init__.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/compressor.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/credit_analyzer.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/credits.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/differ.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/doctor.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/epistemic/__init__.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/epistemic/belief.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/epistemic/certainty.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/epistemic/failure_graph.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/epistemic/recovery.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/epistemic/stress_tester.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/executor.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/exporter.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/gui/__init__.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/gui/app.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/gui/main_window.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/gui/session_tab.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/gui/theme.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/gui/widgets/__init__.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/gui/widgets/chat_view.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/gui/widgets/input_bar.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/gui/widgets/provider_bar.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/gui/widgets/token_meter.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/gui/widgets/tool_panel.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/gui/widgets/update_checker.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/gui/worker.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/importer.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/integrations/__init__.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/integrations/aider.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/integrations/base.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/integrations/claude_code.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/integrations/copilot.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/integrations/cursor.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/integrations/gemini.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/integrations/warp.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/integrations/windsurf.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/languages.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/ledger.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/ollama_cmds.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/patent.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/phase.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/plugins.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/rate_limits.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/releaser.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/requirements.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/scaffolder.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/session.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/agents.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/community/bug_report.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/community/code_of_conduct.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/community/contributing.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/community/feature_request.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/community/license-Apache-2.0.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/community/license-MIT.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/community/pull_request_template.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/community/security.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/docs/architecture.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/docs/mkdocs.yml.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/docs/readthedocs.yaml.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/docs/requirements.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/docs/test-spec.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/docs/workflow.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/editorconfig.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/gitattributes.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/gitignore.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/go/go.mod.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/go/main.go.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/governance/belief-registry.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/governance/context-budget.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/governance/drift-metrics.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/governance/epistemic-axioms.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/governance/failure-modes.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/governance/roles.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/governance/rules.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/governance/uncertainty-map.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/governance/verification.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/governance/workflow.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/js/package.json.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/ledger.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/python/cli.py.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/python/init.py.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/python/pyproject.toml.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/readme.md.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/rust/Cargo.toml.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/rust/main.rs.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/scripts/exec.cmd.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/scripts/exec.sh.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/scripts/run.cmd.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/scripts/run.sh.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/scripts/setup.cmd.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/scripts/setup.sh.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/templates/workflows/release.yml.j2 +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/tools.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/trace.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/updater.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/upgrader.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/validator.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/vcs/__init__.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/vcs/base.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/vcs/bitbucket.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/vcs/github.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/vcs/gitlab.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith/workspace.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith.egg-info/dependency_links.txt +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith.egg-info/entry_points.txt +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith.egg-info/requires.txt +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/src/specsmith.egg-info/top_level.txt +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/tests/test_auditor.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/tests/test_cli.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/tests/test_compressor.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/tests/test_epistemic.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/tests/test_importer.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/tests/test_integrations.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/tests/test_optimizer.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/tests/test_rate_limits.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/tests/test_scaffolder.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/tests/test_smoke.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/tests/test_tools.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/tests/test_validator.py +0 -0
- {specsmith-0.3.5 → specsmith-0.3.6.dev170}/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.dev170
|
|
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
|
|
@@ -66,6 +66,7 @@ Dynamic: license-file
|
|
|
66
66
|
# specsmith
|
|
67
67
|
|
|
68
68
|
[](https://github.com/BitConcepts/specsmith/actions/workflows/ci.yml)
|
|
69
|
+
[](https://github.com/sponsors/BitConcepts)
|
|
69
70
|
[](https://specsmith.readthedocs.io/en/stable/)
|
|
70
71
|
[](https://pypi.org/project/specsmith/)
|
|
71
72
|
[](https://www.python.org/downloads/)
|
|
@@ -122,7 +123,7 @@ specsmith phase list # list all phases
|
|
|
122
123
|
```
|
|
123
124
|
|
|
124
125
|
The current phase is persisted in `scaffold.yml` as `aee_phase` and displayed in the VS Code
|
|
125
|
-
|
|
126
|
+
Settings Panel. Each phase has a checklist of file/command criteria, recommended commands,
|
|
126
127
|
and a readiness percentage.
|
|
127
128
|
|
|
128
129
|
---
|
|
@@ -190,24 +191,38 @@ The **specsmith AEE Workbench** VS Code extension is the flagship client:
|
|
|
190
191
|
```
|
|
191
192
|
# Install specsmith first, then:
|
|
192
193
|
# VS Code: Ctrl+Shift+P → specsmith: New Agent Session
|
|
193
|
-
#
|
|
194
|
+
# Settings Panel: Ctrl+Shift+G
|
|
194
195
|
```
|
|
195
196
|
|
|
196
197
|
**Key features:**
|
|
197
|
-
- **
|
|
198
|
+
- **6-tab Settings panel** — Project / Tools / Files / Updates / Actions / Execution
|
|
199
|
+
- **Execution profiles** — safe (read-only) / standard / open / admin; custom allow/block command lists stored in `scaffold.yml`
|
|
198
200
|
- **AEE phase indicator** — shows current phase with readiness %, Next Phase button, and phase selector
|
|
199
201
|
- **AI agent sessions** — independent process per project, JSONL bridge, chat with file injection
|
|
200
202
|
- **Live model listing** — Anthropic, OpenAI, Gemini, Mistral, local Ollama (GPU-aware)
|
|
201
203
|
- **Ollama integration** — browse curated catalog, download models with progress, task-based suggestions
|
|
202
204
|
- **FPGA/HDL tool support** — vivado, gtkwave, vsg, ghdl, verilator, yosys, nextpnr, and 15 more
|
|
205
|
+
- **Tool installer** — scan installed tools; one-click install via winget/brew/apt for missing tools
|
|
206
|
+
- **Tool rules** — curated AI context rules for 20+ tools (VSG, GHDL, Verilator, ruff, mypy, etc.) auto-injected into agent system prompt
|
|
203
207
|
- **API key management** — stored in OS credential store (Windows Credential Manager / macOS Keychain)
|
|
204
|
-
- **Update checker** — PyPI version check
|
|
205
|
-
- **Auto-open** —
|
|
208
|
+
- **Update checker** — PyPI version check, Install Update button, release channel selector (stable / pre-release)
|
|
209
|
+
- **Auto-open** — Settings panel always opens alongside every new session; never a blank pane
|
|
206
210
|
|
|
207
211
|
**[→ specsmith-vscode on GitHub](https://github.com/BitConcepts/specsmith-vscode)**
|
|
208
212
|
|
|
209
213
|
---
|
|
210
214
|
|
|
215
|
+
## Supporting specsmith
|
|
216
|
+
|
|
217
|
+
If specsmith is saving you time or helping your team ship better software, please consider:
|
|
218
|
+
|
|
219
|
+
- **[Sponsoring BitConcepts](https://github.com/sponsors/BitConcepts)** — directly funds development
|
|
220
|
+
- **Starring** [specsmith](https://github.com/BitConcepts/specsmith) and [specsmith-vscode](https://github.com/BitConcepts/specsmith-vscode) on GitHub
|
|
221
|
+
- **Reporting bugs** and **requesting features** via [GitHub Issues](https://github.com/BitConcepts/specsmith/issues)
|
|
222
|
+
- **Contributing** — see [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
211
226
|
## Ollama — Local LLMs (Zero API Cost)
|
|
212
227
|
|
|
213
228
|
specsmith has first-class Ollama support, including:
|
|
@@ -232,7 +247,7 @@ specsmith supports FPGA-specific project types with full governance:
|
|
|
232
247
|
|
|
233
248
|
```yaml
|
|
234
249
|
# scaffold.yml
|
|
235
|
-
type: fpga-rtl-
|
|
250
|
+
type: fpga-rtl-amd # or fpga-rtl-intel / fpga-rtl-lattice / fpga-rtl
|
|
236
251
|
fpga_tools:
|
|
237
252
|
- vivado
|
|
238
253
|
- gtkwave
|
|
@@ -264,6 +279,8 @@ Supported tools: **Synthesis:** vivado, quartus, radiant, diamond, gowin.
|
|
|
264
279
|
|
|
265
280
|
**VCS:** `commit` `push` `sync` `branch` `pr` `status`
|
|
266
281
|
|
|
282
|
+
**Tools:** `tools scan [--fpga]` `tools install <tool>` `tools rules [--tool] [--list]`
|
|
283
|
+
|
|
267
284
|
**Tools:** `exec` `ps` `abort` `watch` `optimize` `credits` `self-update`
|
|
268
285
|
|
|
269
286
|
**Auth:** `auth set/list/remove/check`
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# specsmith
|
|
2
2
|
|
|
3
3
|
[](https://github.com/BitConcepts/specsmith/actions/workflows/ci.yml)
|
|
4
|
+
[](https://github.com/sponsors/BitConcepts)
|
|
4
5
|
[](https://specsmith.readthedocs.io/en/stable/)
|
|
5
6
|
[](https://pypi.org/project/specsmith/)
|
|
6
7
|
[](https://www.python.org/downloads/)
|
|
@@ -57,7 +58,7 @@ specsmith phase list # list all phases
|
|
|
57
58
|
```
|
|
58
59
|
|
|
59
60
|
The current phase is persisted in `scaffold.yml` as `aee_phase` and displayed in the VS Code
|
|
60
|
-
|
|
61
|
+
Settings Panel. Each phase has a checklist of file/command criteria, recommended commands,
|
|
61
62
|
and a readiness percentage.
|
|
62
63
|
|
|
63
64
|
---
|
|
@@ -125,24 +126,38 @@ The **specsmith AEE Workbench** VS Code extension is the flagship client:
|
|
|
125
126
|
```
|
|
126
127
|
# Install specsmith first, then:
|
|
127
128
|
# VS Code: Ctrl+Shift+P → specsmith: New Agent Session
|
|
128
|
-
#
|
|
129
|
+
# Settings Panel: Ctrl+Shift+G
|
|
129
130
|
```
|
|
130
131
|
|
|
131
132
|
**Key features:**
|
|
132
|
-
- **
|
|
133
|
+
- **6-tab Settings panel** — Project / Tools / Files / Updates / Actions / Execution
|
|
134
|
+
- **Execution profiles** — safe (read-only) / standard / open / admin; custom allow/block command lists stored in `scaffold.yml`
|
|
133
135
|
- **AEE phase indicator** — shows current phase with readiness %, Next Phase button, and phase selector
|
|
134
136
|
- **AI agent sessions** — independent process per project, JSONL bridge, chat with file injection
|
|
135
137
|
- **Live model listing** — Anthropic, OpenAI, Gemini, Mistral, local Ollama (GPU-aware)
|
|
136
138
|
- **Ollama integration** — browse curated catalog, download models with progress, task-based suggestions
|
|
137
139
|
- **FPGA/HDL tool support** — vivado, gtkwave, vsg, ghdl, verilator, yosys, nextpnr, and 15 more
|
|
140
|
+
- **Tool installer** — scan installed tools; one-click install via winget/brew/apt for missing tools
|
|
141
|
+
- **Tool rules** — curated AI context rules for 20+ tools (VSG, GHDL, Verilator, ruff, mypy, etc.) auto-injected into agent system prompt
|
|
138
142
|
- **API key management** — stored in OS credential store (Windows Credential Manager / macOS Keychain)
|
|
139
|
-
- **Update checker** — PyPI version check
|
|
140
|
-
- **Auto-open** —
|
|
143
|
+
- **Update checker** — PyPI version check, Install Update button, release channel selector (stable / pre-release)
|
|
144
|
+
- **Auto-open** — Settings panel always opens alongside every new session; never a blank pane
|
|
141
145
|
|
|
142
146
|
**[→ specsmith-vscode on GitHub](https://github.com/BitConcepts/specsmith-vscode)**
|
|
143
147
|
|
|
144
148
|
---
|
|
145
149
|
|
|
150
|
+
## Supporting specsmith
|
|
151
|
+
|
|
152
|
+
If specsmith is saving you time or helping your team ship better software, please consider:
|
|
153
|
+
|
|
154
|
+
- **[Sponsoring BitConcepts](https://github.com/sponsors/BitConcepts)** — directly funds development
|
|
155
|
+
- **Starring** [specsmith](https://github.com/BitConcepts/specsmith) and [specsmith-vscode](https://github.com/BitConcepts/specsmith-vscode) on GitHub
|
|
156
|
+
- **Reporting bugs** and **requesting features** via [GitHub Issues](https://github.com/BitConcepts/specsmith/issues)
|
|
157
|
+
- **Contributing** — see [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
146
161
|
## Ollama — Local LLMs (Zero API Cost)
|
|
147
162
|
|
|
148
163
|
specsmith has first-class Ollama support, including:
|
|
@@ -167,7 +182,7 @@ specsmith supports FPGA-specific project types with full governance:
|
|
|
167
182
|
|
|
168
183
|
```yaml
|
|
169
184
|
# scaffold.yml
|
|
170
|
-
type: fpga-rtl-
|
|
185
|
+
type: fpga-rtl-amd # or fpga-rtl-intel / fpga-rtl-lattice / fpga-rtl
|
|
171
186
|
fpga_tools:
|
|
172
187
|
- vivado
|
|
173
188
|
- gtkwave
|
|
@@ -199,6 +214,8 @@ Supported tools: **Synthesis:** vivado, quartus, radiant, diamond, gowin.
|
|
|
199
214
|
|
|
200
215
|
**VCS:** `commit` `push` `sync` `branch` `pr` `status`
|
|
201
216
|
|
|
217
|
+
**Tools:** `tools scan [--fpga]` `tools install <tool>` `tools rules [--tool] [--list]`
|
|
218
|
+
|
|
202
219
|
**Tools:** `exec` `ps` `abort` `watch` `optimize` `credits` `self-update`
|
|
203
220
|
|
|
204
221
|
**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.dev170"
|
|
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,
|
|
@@ -26,6 +26,16 @@ from pathlib import Path
|
|
|
26
26
|
|
|
27
27
|
from specsmith.agent.core import Tool, ToolParam
|
|
28
28
|
|
|
29
|
+
# Env vars that prevent Rich from using the Windows Console API
|
|
30
|
+
# (LegacyWindowsTerm) when stdout is a captured pipe. Without these, Rich
|
|
31
|
+
# crashes with 'WriteFile failed' / handle errors on every command.
|
|
32
|
+
_SUBPROCESS_ENV: dict[str, str] = {
|
|
33
|
+
**os.environ,
|
|
34
|
+
"NO_COLOR": "1", # Disables Rich colour / Windows console API path
|
|
35
|
+
"FORCE_COLOR": "0", # Belt-and-suspenders: also suppress colour
|
|
36
|
+
"PYTHONIOENCODING": "utf-8", # Ensure UTF-8 on pipes regardless of locale
|
|
37
|
+
}
|
|
38
|
+
|
|
29
39
|
|
|
30
40
|
def _run_specsmith(args: list[str], project_dir: str = ".") -> str:
|
|
31
41
|
"""Execute a specsmith command and return combined stdout+stderr."""
|
|
@@ -36,6 +46,7 @@ def _run_specsmith(args: list[str], project_dir: str = ".") -> str:
|
|
|
36
46
|
capture_output=True,
|
|
37
47
|
text=True,
|
|
38
48
|
timeout=120,
|
|
49
|
+
env=_SUBPROCESS_ENV,
|
|
39
50
|
)
|
|
40
51
|
output = (result.stdout + result.stderr).strip()
|
|
41
52
|
if result.returncode != 0:
|
|
@@ -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
|
|