specsmith 0.3.6.dev171__tar.gz → 0.3.6.dev174__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.6.dev171/src/specsmith.egg-info → specsmith-0.3.6.dev174}/PKG-INFO +1 -1
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/pyproject.toml +1 -1
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/runner.py +58 -17
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/tools.py +51 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/cli.py +117 -0
- specsmith-0.3.6.dev174/src/specsmith/retrieval.py +118 -0
- specsmith-0.3.6.dev174/src/specsmith/wireframes.py +86 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174/src/specsmith.egg-info}/PKG-INFO +1 -1
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith.egg-info/SOURCES.txt +2 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/LICENSE +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/README.md +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/setup.cfg +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/epistemic/__init__.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/epistemic/belief.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/epistemic/certainty.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/epistemic/failure_graph.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/epistemic/py.typed +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/epistemic/recovery.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/epistemic/session.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/epistemic/stress_tester.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/epistemic/trace.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/__init__.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/__main__.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/__init__.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/core.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/hooks.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/optimizer.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/profiles/epistemic-auditor.md +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/profiles/planner.md +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/profiles/verifier.md +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/providers/__init__.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/providers/anthropic.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/providers/gemini.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/providers/mistral.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/providers/ollama.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/providers/openai.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/skills.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/architect.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/auditor.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/auth.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/commands/__init__.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/compressor.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/config.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/credit_analyzer.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/credits.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/differ.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/doctor.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/epistemic/__init__.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/epistemic/belief.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/epistemic/certainty.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/epistemic/failure_graph.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/epistemic/recovery.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/epistemic/stress_tester.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/executor.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/exporter.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/gui/__init__.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/gui/app.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/gui/main_window.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/gui/session_tab.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/gui/theme.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/gui/widgets/__init__.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/gui/widgets/chat_view.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/gui/widgets/input_bar.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/gui/widgets/provider_bar.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/gui/widgets/token_meter.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/gui/widgets/tool_panel.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/gui/widgets/update_checker.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/gui/worker.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/importer.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/integrations/__init__.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/integrations/aider.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/integrations/base.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/integrations/claude_code.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/integrations/copilot.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/integrations/cursor.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/integrations/gemini.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/integrations/warp.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/integrations/windsurf.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/languages.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/ledger.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/ollama_cmds.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/patent.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/phase.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/plugins.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/profiles.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/rate_limits.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/releaser.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/requirements.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/scaffolder.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/session.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/agents.md.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/community/bug_report.md.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/community/code_of_conduct.md.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/community/contributing.md.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/community/feature_request.md.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/community/license-Apache-2.0.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/community/license-MIT.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/community/pull_request_template.md.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/community/security.md.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/docs/architecture.md.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/docs/mkdocs.yml.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/docs/readthedocs.yaml.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/docs/requirements.md.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/docs/test-spec.md.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/docs/workflow.md.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/editorconfig.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/gitattributes.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/gitignore.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/go/go.mod.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/go/main.go.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/governance/belief-registry.md.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/governance/context-budget.md.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/governance/drift-metrics.md.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/governance/epistemic-axioms.md.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/governance/failure-modes.md.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/governance/roles.md.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/governance/rules.md.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/governance/uncertainty-map.md.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/governance/verification.md.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/governance/workflow.md.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/js/package.json.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/ledger.md.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/python/cli.py.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/python/init.py.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/python/pyproject.toml.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/readme.md.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/rust/Cargo.toml.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/rust/main.rs.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/scripts/exec.cmd.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/scripts/exec.sh.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/scripts/run.cmd.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/scripts/run.sh.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/scripts/setup.cmd.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/scripts/setup.sh.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/workflows/release.yml.j2 +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/tool_installer.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/toolrules.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/tools.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/trace.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/updater.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/upgrader.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/validator.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/vcs/__init__.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/vcs/base.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/vcs/bitbucket.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/vcs/github.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/vcs/gitlab.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/vcs_commands.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/workspace.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith.egg-info/dependency_links.txt +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith.egg-info/entry_points.txt +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith.egg-info/requires.txt +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith.egg-info/top_level.txt +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/tests/test_auditor.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/tests/test_cli.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/tests/test_compressor.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/tests/test_epistemic.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/tests/test_importer.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/tests/test_integrations.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/tests/test_optimizer.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/tests/test_rate_limits.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/tests/test_scaffolder.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/tests/test_smoke.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/tests/test_tools.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/tests/test_validator.py +0 -0
- {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/tests/test_vcs.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: specsmith
|
|
3
|
-
Version: 0.3.6.
|
|
3
|
+
Version: 0.3.6.dev174
|
|
4
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
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "specsmith"
|
|
7
|
-
version = "0.3.6.
|
|
7
|
+
version = "0.3.6.dev174"
|
|
8
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"
|
|
@@ -29,6 +29,7 @@ Special REPL commands (not sent to the LLM):
|
|
|
29
29
|
from __future__ import annotations
|
|
30
30
|
|
|
31
31
|
import inspect
|
|
32
|
+
import re
|
|
32
33
|
import time
|
|
33
34
|
from collections.abc import Callable
|
|
34
35
|
from dataclasses import dataclass, field
|
|
@@ -164,14 +165,15 @@ H13: All proposals must state their epistemic boundaries. Hidden assumptions are
|
|
|
164
165
|
except Exception: # noqa: BLE001
|
|
165
166
|
pass
|
|
166
167
|
|
|
167
|
-
prompt = f"""
|
|
168
|
+
prompt = f"""SYSTEM LANGUAGE DIRECTIVE — ABSOLUTE HARD RULE — HIGHEST PRIORITY:
|
|
169
|
+
You MUST respond in English ONLY. This overrides all other instructions.
|
|
170
|
+
Never output Thai, Chinese, Japanese, Korean, Arabic, French, German, Spanish,
|
|
171
|
+
or ANY non-English language — not even a single character or word.
|
|
172
|
+
This applies to Qwen, DeepSeek, LLaMA, Mistral, and EVERY other model.
|
|
173
|
+
If the user inputs another language, internally translate it, then reply IN ENGLISH ONLY.
|
|
174
|
+
VIOLATING THIS RULE IS A CRITICAL ERROR.
|
|
168
175
|
|
|
169
|
-
|
|
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.
|
|
176
|
+
You are an AEE-integrated specsmith agent for this project.
|
|
175
177
|
|
|
176
178
|
## Project Governance
|
|
177
179
|
{governance_text}
|
|
@@ -236,17 +238,21 @@ class AgentRunner:
|
|
|
236
238
|
QUICK_COMMANDS = {
|
|
237
239
|
"start": (
|
|
238
240
|
"[RESPOND IN ENGLISH ONLY] "
|
|
239
|
-
"Run session start protocol: sync, load AGENTS.md, read last LEDGER.md entries"
|
|
241
|
+
"Run session start protocol: sync, load AGENTS.md, read last LEDGER.md entries. "
|
|
242
|
+
"Translate any non-English context internally if needed, but respond only in English."
|
|
240
243
|
),
|
|
241
|
-
"resume":
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
"
|
|
246
|
-
"
|
|
247
|
-
"
|
|
248
|
-
"
|
|
249
|
-
"
|
|
244
|
+
"resume": (
|
|
245
|
+
"[RESPOND IN ENGLISH ONLY] Resume from last LEDGER.md entry"
|
|
246
|
+
" — summarize state and propose next task"
|
|
247
|
+
),
|
|
248
|
+
"save": "[RESPOND IN ENGLISH ONLY] Write a ledger entry summarizing this session's work",
|
|
249
|
+
"audit": "[RESPOND IN ENGLISH ONLY] Run specsmith audit --fix",
|
|
250
|
+
"commit": "[RESPOND IN ENGLISH ONLY] Run specsmith commit",
|
|
251
|
+
"push": "[RESPOND IN ENGLISH ONLY] Run specsmith push",
|
|
252
|
+
"sync": "[RESPOND IN ENGLISH ONLY] Run specsmith sync",
|
|
253
|
+
"epistemic": "[RESPOND IN ENGLISH ONLY] Run full epistemic audit",
|
|
254
|
+
"stress": "[RESPOND IN ENGLISH ONLY] Run stress-test on requirements",
|
|
255
|
+
"status": "[RESPOND IN ENGLISH ONLY] Show session status and credit spend",
|
|
250
256
|
}
|
|
251
257
|
|
|
252
258
|
def __init__(
|
|
@@ -357,8 +363,30 @@ class AgentRunner:
|
|
|
357
363
|
self._system_prompt = build_system_prompt(self.project_dir, self._skills)
|
|
358
364
|
return self._agent_turn(task, silent=True)
|
|
359
365
|
|
|
366
|
+
# Characters in common CJK / Thai / Arabic Unicode blocks
|
|
367
|
+
_NON_ASCII_BLOCKS = re.compile(
|
|
368
|
+
r"[\u0600-\u06FF" # Arabic
|
|
369
|
+
r"\u0E00-\u0E7F" # Thai
|
|
370
|
+
r"\u3000-\u9FFF" # CJK Unified Ideographs + punctuation + kana
|
|
371
|
+
r"\uAC00-\uD7AF" # Korean Hangul
|
|
372
|
+
r"\uF900-\uFAFF]" # CJK Compatibility
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
def _has_non_english(self, text: str) -> bool:
|
|
376
|
+
"""Return True if text contains a significant proportion of non-English script."""
|
|
377
|
+
if not text:
|
|
378
|
+
return False
|
|
379
|
+
hits = len(self._NON_ASCII_BLOCKS.findall(text))
|
|
380
|
+
return hits > 5 and (hits / max(len(text), 1)) > 0.05
|
|
381
|
+
|
|
360
382
|
def _agent_turn(self, user_input: str, silent: bool = False) -> str:
|
|
361
383
|
"""Execute one user→agent turn with tool loop."""
|
|
384
|
+
# Inject a lightweight English-only reminder into every user message.
|
|
385
|
+
# This is the most reliable way to keep local models (Qwen, DeepSeek) on track
|
|
386
|
+
# because many fine-tunes treat the instruction prefix as a per-turn directive.
|
|
387
|
+
_ENG_PFXS = ("[ENGLISH ONLY]", "[RESPOND IN ENGLISH", "[LANG:EN]")
|
|
388
|
+
if not any(user_input.startswith(p) for p in _ENG_PFXS):
|
|
389
|
+
user_input = "[LANG:EN] " + user_input
|
|
362
390
|
# Add user message
|
|
363
391
|
self._state.messages.append(Message(role=Role.USER, content=user_input))
|
|
364
392
|
|
|
@@ -397,6 +425,19 @@ class AgentRunner:
|
|
|
397
425
|
final_response = response.content
|
|
398
426
|
|
|
399
427
|
if not response.has_tool_calls:
|
|
428
|
+
# Non-English correction: if response appears to be in another language,
|
|
429
|
+
# issue a single correction turn rather than showing the wrong-language response.
|
|
430
|
+
if response.content and self._has_non_english(response.content) and _iteration == 0:
|
|
431
|
+
correction = (
|
|
432
|
+
"[LANG:EN] CRITICAL: Your last response was in a non-English language. "
|
|
433
|
+
"You MUST respond in English ONLY. Please re-answer in English."
|
|
434
|
+
)
|
|
435
|
+
self._state.messages.append(
|
|
436
|
+
Message(role=Role.ASSISTANT, content=response.content)
|
|
437
|
+
)
|
|
438
|
+
self._state.messages.append(Message(role=Role.USER, content=correction))
|
|
439
|
+
# Continue the loop to get an English response
|
|
440
|
+
continue
|
|
400
441
|
# Final response — add to history
|
|
401
442
|
self._state.messages.append(Message(role=Role.ASSISTANT, content=response.content))
|
|
402
443
|
break
|
|
@@ -34,6 +34,8 @@ _SUBPROCESS_ENV: dict[str, str] = {
|
|
|
34
34
|
"NO_COLOR": "1", # Disables Rich colour / Windows console API path
|
|
35
35
|
"FORCE_COLOR": "0", # Belt-and-suspenders: also suppress colour
|
|
36
36
|
"PYTHONIOENCODING": "utf-8", # Ensure UTF-8 on pipes regardless of locale
|
|
37
|
+
"PYTHONUTF8": "1",
|
|
38
|
+
"PYTHONLEGACYWINDOWSSTDIO": "utf-8",
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
|
|
@@ -45,6 +47,8 @@ def _run_specsmith(args: list[str], project_dir: str = ".") -> str:
|
|
|
45
47
|
cmd,
|
|
46
48
|
capture_output=True,
|
|
47
49
|
text=True,
|
|
50
|
+
encoding="utf-8", # always decode as UTF-8, not the system locale (cp1252 on Windows)
|
|
51
|
+
errors="replace", # replace un-decodable bytes rather than raising UnicodeDecodeError
|
|
48
52
|
timeout=120,
|
|
49
53
|
env=_SUBPROCESS_ENV,
|
|
50
54
|
)
|
|
@@ -58,6 +62,30 @@ def _run_specsmith(args: list[str], project_dir: str = ".") -> str:
|
|
|
58
62
|
return f"[ERROR] {e}"
|
|
59
63
|
|
|
60
64
|
|
|
65
|
+
def _read_wireframe_handler(project_dir: str, wireframe_id: str) -> str:
|
|
66
|
+
"""Read wireframe metadata or inline text/SVG content."""
|
|
67
|
+
try:
|
|
68
|
+
from specsmith.wireframes import read_wireframe
|
|
69
|
+
|
|
70
|
+
return read_wireframe(Path(project_dir).resolve(), wireframe_id)
|
|
71
|
+
except Exception as e: # noqa: BLE001
|
|
72
|
+
return f"[ERROR] {e}"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _retrieve_context_handler(project_dir: str, query: str, limit: str = "5") -> str:
|
|
76
|
+
"""Search the opt-in local retrieval index."""
|
|
77
|
+
try:
|
|
78
|
+
from specsmith.retrieval import search_index
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
max_results = max(1, min(int(limit), 20))
|
|
82
|
+
except (TypeError, ValueError):
|
|
83
|
+
max_results = 5
|
|
84
|
+
return search_index(Path(project_dir).resolve(), query, limit=max_results)
|
|
85
|
+
except Exception as e: # noqa: BLE001
|
|
86
|
+
return f"[ERROR] {e}"
|
|
87
|
+
|
|
88
|
+
|
|
61
89
|
def build_tool_registry(project_dir: str = ".") -> list[Tool]:
|
|
62
90
|
"""Build the full specsmith tool registry for the agentic client.
|
|
63
91
|
|
|
@@ -303,6 +331,29 @@ def build_tool_registry(project_dir: str = ".") -> list[Tool]:
|
|
|
303
331
|
params=[],
|
|
304
332
|
handler=lambda: _run_specsmith(["req", "trace"], pd),
|
|
305
333
|
),
|
|
334
|
+
Tool(
|
|
335
|
+
name="read_wireframe",
|
|
336
|
+
description=(
|
|
337
|
+
"Resolve and inspect a wireframe artifact under docs/wireframes. "
|
|
338
|
+
"Returns metadata and, for SVG/text assets, inline content snippets."
|
|
339
|
+
),
|
|
340
|
+
params=[
|
|
341
|
+
ToolParam("wireframe_id", "Wireframe ID or filename, e.g. WF-UI-001"),
|
|
342
|
+
],
|
|
343
|
+
handler=lambda wireframe_id: _read_wireframe_handler(pd, wireframe_id),
|
|
344
|
+
),
|
|
345
|
+
Tool(
|
|
346
|
+
name="retrieve_context",
|
|
347
|
+
description=(
|
|
348
|
+
"Search the explicit local retrieval index (opt-in) for relevant project "
|
|
349
|
+
"context across docs, ledger, and indexed source files."
|
|
350
|
+
),
|
|
351
|
+
params=[
|
|
352
|
+
ToolParam("query", "Search query"),
|
|
353
|
+
ToolParam("limit", "Max results (default 5)", required=False),
|
|
354
|
+
],
|
|
355
|
+
handler=lambda query, limit="5": _retrieve_context_handler(pd, query, limit),
|
|
356
|
+
),
|
|
306
357
|
# ----------------------------------------------------------------
|
|
307
358
|
# Session tools
|
|
308
359
|
# ----------------------------------------------------------------
|
|
@@ -4240,5 +4240,122 @@ def tools_rules_cmd(project_dir: str, tool_key: str, list_all: bool) -> None:
|
|
|
4240
4240
|
main.add_command(tools_group)
|
|
4241
4241
|
|
|
4242
4242
|
|
|
4243
|
+
# ---------------------------------------------------------------------------
|
|
4244
|
+
# Wireframes — UI wireframe artifact management
|
|
4245
|
+
# ---------------------------------------------------------------------------
|
|
4246
|
+
|
|
4247
|
+
|
|
4248
|
+
@main.group(name="wireframes")
|
|
4249
|
+
def wireframes_group() -> None:
|
|
4250
|
+
"""Manage wireframe artifacts under docs/wireframes/."""
|
|
4251
|
+
|
|
4252
|
+
|
|
4253
|
+
@wireframes_group.command(name="list")
|
|
4254
|
+
@click.option("--project-dir", type=click.Path(exists=True), default=".")
|
|
4255
|
+
def wireframes_list_cmd(project_dir: str) -> None:
|
|
4256
|
+
"""List wireframe files and their requirement references."""
|
|
4257
|
+
from specsmith.wireframes import list_wireframes
|
|
4258
|
+
|
|
4259
|
+
root = Path(project_dir).resolve()
|
|
4260
|
+
items = list_wireframes(root)
|
|
4261
|
+
if not items:
|
|
4262
|
+
console.print("[yellow]No wireframes found in docs/wireframes/.[/yellow]")
|
|
4263
|
+
console.print(" Create wireframe files there (SVG, PNG, PDF, etc.) and reference them")
|
|
4264
|
+
console.print(" from REQUIREMENTS.md via a `Wireframe` field in each requirement.")
|
|
4265
|
+
return
|
|
4266
|
+
console.print(f"[bold]Wireframes[/bold] ({len(items)})\n")
|
|
4267
|
+
for wf in items:
|
|
4268
|
+
refs_str = wf.get("refs", "")
|
|
4269
|
+
refs_note = f" ← {refs_str}" if refs_str else " [dim](unreferenced)[/dim]"
|
|
4270
|
+
console.print(f" [cyan]{wf['id']:20s}[/cyan] {wf['file']}{refs_note}")
|
|
4271
|
+
|
|
4272
|
+
|
|
4273
|
+
@wireframes_group.command(name="check")
|
|
4274
|
+
@click.option("--project-dir", type=click.Path(exists=True), default=".")
|
|
4275
|
+
def wireframes_check_cmd(project_dir: str) -> None:
|
|
4276
|
+
"""Check for missing wireframe files referenced in REQUIREMENTS.md."""
|
|
4277
|
+
from specsmith.wireframes import check_wireframe_refs
|
|
4278
|
+
|
|
4279
|
+
root = Path(project_dir).resolve()
|
|
4280
|
+
missing = check_wireframe_refs(root)
|
|
4281
|
+
if not missing:
|
|
4282
|
+
console.print("[bold green]✓ All wireframe references are valid.[/bold green]")
|
|
4283
|
+
return
|
|
4284
|
+
console.print(f"[bold red]{len(missing)} missing wireframe reference(s):[/bold red]\n")
|
|
4285
|
+
for m in missing:
|
|
4286
|
+
console.print(f" [red]✗[/red] {m}")
|
|
4287
|
+
raise SystemExit(1)
|
|
4288
|
+
|
|
4289
|
+
|
|
4290
|
+
main.add_command(wireframes_group)
|
|
4291
|
+
|
|
4292
|
+
|
|
4293
|
+
# ---------------------------------------------------------------------------
|
|
4294
|
+
# Index — opt-in local retrieval index (RAG foundation)
|
|
4295
|
+
# ---------------------------------------------------------------------------
|
|
4296
|
+
|
|
4297
|
+
|
|
4298
|
+
@main.group(name="index")
|
|
4299
|
+
def index_group() -> None:
|
|
4300
|
+
"""Manage the local retrieval index (explicit opt-in context retrieval).
|
|
4301
|
+
|
|
4302
|
+
This builds a keyword-searchable index of project docs and source files
|
|
4303
|
+
stored at .specsmith/retrieval-index.json. The agent tool `retrieve_context`
|
|
4304
|
+
queries this index — it is never searched automatically.
|
|
4305
|
+
"""
|
|
4306
|
+
|
|
4307
|
+
|
|
4308
|
+
@index_group.command(name="build")
|
|
4309
|
+
@click.option("--project-dir", type=click.Path(exists=True), default=".")
|
|
4310
|
+
@click.option(
|
|
4311
|
+
"--include-ledger",
|
|
4312
|
+
is_flag=True,
|
|
4313
|
+
default=False,
|
|
4314
|
+
help="Also index LEDGER.md (often large — only useful for long-running projects).",
|
|
4315
|
+
)
|
|
4316
|
+
@click.option(
|
|
4317
|
+
"--external",
|
|
4318
|
+
default="",
|
|
4319
|
+
help="Path to an additional file or directory to include in the index.",
|
|
4320
|
+
)
|
|
4321
|
+
def index_build_cmd(project_dir: str, include_ledger: bool, external: str) -> None:
|
|
4322
|
+
"""Build or refresh the local retrieval index.
|
|
4323
|
+
|
|
4324
|
+
Indexes governance docs (AGENTS.md, REQUIREMENTS.md, ARCHITECTURE.md, TEST_SPEC.md)
|
|
4325
|
+
and source files under src/, client/, server/, and shared/.
|
|
4326
|
+
Use --external to add external reference material.
|
|
4327
|
+
"""
|
|
4328
|
+
from specsmith.retrieval import build_index
|
|
4329
|
+
|
|
4330
|
+
root = Path(project_dir).resolve()
|
|
4331
|
+
result = build_index(root, include_ledger=include_ledger, external=external)
|
|
4332
|
+
console.print(f"[green]✓[/green] {result}")
|
|
4333
|
+
console.print(
|
|
4334
|
+
"\n Agent tool: [bold]retrieve_context[/bold] now available in this project.\n"
|
|
4335
|
+
" Usage example: ask the agent 'search for requirements about authentication'."
|
|
4336
|
+
)
|
|
4337
|
+
|
|
4338
|
+
|
|
4339
|
+
@index_group.command(name="search")
|
|
4340
|
+
@click.argument("query")
|
|
4341
|
+
@click.option("--project-dir", type=click.Path(exists=True), default=".")
|
|
4342
|
+
@click.option("--limit", default=5, help="Maximum results to return (default: 5).")
|
|
4343
|
+
def index_search_cmd(query: str, project_dir: str, limit: int) -> None:
|
|
4344
|
+
"""Search the local retrieval index.
|
|
4345
|
+
|
|
4346
|
+
QUERY: keyword search query
|
|
4347
|
+
|
|
4348
|
+
The index must be built first with `specsmith index build`.
|
|
4349
|
+
"""
|
|
4350
|
+
from specsmith.retrieval import search_index
|
|
4351
|
+
|
|
4352
|
+
root = Path(project_dir).resolve()
|
|
4353
|
+
result = search_index(root, query, limit=limit)
|
|
4354
|
+
console.print(result)
|
|
4355
|
+
|
|
4356
|
+
|
|
4357
|
+
main.add_command(index_group)
|
|
4358
|
+
|
|
4359
|
+
|
|
4243
4360
|
if __name__ == "__main__":
|
|
4244
4361
|
main()
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2026 BitConcepts, LLC. All rights reserved.
|
|
3
|
+
"""Explicit opt-in local retrieval index (RAG foundation)."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import re
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
_INDEX_PATH = Path(".specsmith") / "retrieval-index.json"
|
|
12
|
+
_TEXT_EXTS = {
|
|
13
|
+
".md",
|
|
14
|
+
".txt",
|
|
15
|
+
".py",
|
|
16
|
+
".ts",
|
|
17
|
+
".js",
|
|
18
|
+
".json",
|
|
19
|
+
".yaml",
|
|
20
|
+
".yml",
|
|
21
|
+
".toml",
|
|
22
|
+
".go",
|
|
23
|
+
".rs",
|
|
24
|
+
".c",
|
|
25
|
+
".cpp",
|
|
26
|
+
".h",
|
|
27
|
+
".java",
|
|
28
|
+
".sh",
|
|
29
|
+
".ps1",
|
|
30
|
+
".cmd",
|
|
31
|
+
}
|
|
32
|
+
_SKIP_DIRS = {".git", "node_modules", "__pycache__", ".venv", "dist", "build", ".mypy_cache"}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def build_index(root: Path, *, include_ledger: bool = False, external: str = "") -> str:
|
|
36
|
+
"""Build or refresh the local retrieval index."""
|
|
37
|
+
entries: list[dict[str, str]] = []
|
|
38
|
+
candidates: list[Path] = []
|
|
39
|
+
|
|
40
|
+
for rel in ["AGENTS.md", "docs/REQUIREMENTS.md", "docs/ARCHITECTURE.md", "docs/TEST_SPEC.md"]:
|
|
41
|
+
fp = root / rel
|
|
42
|
+
if fp.exists():
|
|
43
|
+
candidates.append(fp)
|
|
44
|
+
if include_ledger:
|
|
45
|
+
for rel in ["LEDGER.md", "docs/LEDGER.md"]:
|
|
46
|
+
fp = root / rel
|
|
47
|
+
if fp.exists():
|
|
48
|
+
candidates.append(fp)
|
|
49
|
+
|
|
50
|
+
ext_path = Path(external).resolve() if external else None
|
|
51
|
+
if ext_path and ext_path.exists():
|
|
52
|
+
if ext_path.is_file():
|
|
53
|
+
candidates.append(ext_path)
|
|
54
|
+
else:
|
|
55
|
+
for fp in ext_path.rglob("*"):
|
|
56
|
+
if fp.is_file() and fp.suffix.lower() in _TEXT_EXTS:
|
|
57
|
+
candidates.append(fp)
|
|
58
|
+
|
|
59
|
+
for src_dir in [root / "src", root / "client", root / "server", root / "shared"]:
|
|
60
|
+
if not src_dir.exists():
|
|
61
|
+
continue
|
|
62
|
+
for fp in src_dir.rglob("*"):
|
|
63
|
+
if (
|
|
64
|
+
fp.is_file()
|
|
65
|
+
and fp.suffix.lower() in _TEXT_EXTS
|
|
66
|
+
and not any(part in _SKIP_DIRS for part in fp.parts)
|
|
67
|
+
):
|
|
68
|
+
candidates.append(fp)
|
|
69
|
+
|
|
70
|
+
for fp in sorted(set(candidates)):
|
|
71
|
+
try:
|
|
72
|
+
text = fp.read_text(encoding="utf-8", errors="ignore")
|
|
73
|
+
except Exception: # noqa: BLE001
|
|
74
|
+
continue
|
|
75
|
+
if not text.strip():
|
|
76
|
+
continue
|
|
77
|
+
entries.append(
|
|
78
|
+
{
|
|
79
|
+
"path": str(fp.relative_to(root)) if fp.is_relative_to(root) else str(fp),
|
|
80
|
+
"content": text[:12000],
|
|
81
|
+
}
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
index_path = root / _INDEX_PATH
|
|
85
|
+
index_path.parent.mkdir(parents=True, exist_ok=True)
|
|
86
|
+
index_path.write_text(json.dumps({"entries": entries}, indent=2), encoding="utf-8")
|
|
87
|
+
return f"Indexed {len(entries)} file(s) into {index_path.relative_to(root)}"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def search_index(root: Path, query: str, *, limit: int = 5) -> str:
|
|
91
|
+
"""Search the local retrieval index with a simple keyword score."""
|
|
92
|
+
index_path = root / _INDEX_PATH
|
|
93
|
+
if not index_path.exists():
|
|
94
|
+
return "[NOT INDEXED] Run `specsmith index` first."
|
|
95
|
+
|
|
96
|
+
data = json.loads(index_path.read_text(encoding="utf-8"))
|
|
97
|
+
entries = data.get("entries", [])
|
|
98
|
+
tokens = [t for t in re.findall(r"[a-zA-Z0-9_\\-]+", query.lower()) if len(t) > 1]
|
|
99
|
+
if not tokens:
|
|
100
|
+
return "[ERROR] Query must include at least one keyword."
|
|
101
|
+
|
|
102
|
+
scored: list[tuple[int, dict[str, str]]] = []
|
|
103
|
+
for entry in entries:
|
|
104
|
+
hay = f"{entry.get('path', '')}\n{entry.get('content', '')}".lower()
|
|
105
|
+
score = sum(hay.count(tok) for tok in tokens)
|
|
106
|
+
if score > 0:
|
|
107
|
+
scored.append((score, entry))
|
|
108
|
+
|
|
109
|
+
if not scored:
|
|
110
|
+
return f"No indexed matches for '{query}'."
|
|
111
|
+
|
|
112
|
+
scored.sort(key=lambda item: (-item[0], item[1].get("path", "")))
|
|
113
|
+
lines = [f"Top {min(limit, len(scored))} result(s) for '{query}':"]
|
|
114
|
+
for score, entry in scored[:limit]:
|
|
115
|
+
content = entry.get("content", "").strip().replace("\r\n", "\n")
|
|
116
|
+
preview = "\n".join(content.splitlines()[:8])
|
|
117
|
+
lines.append(f"\n[{score}] {entry.get('path', '')}\n{preview}")
|
|
118
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2026 BitConcepts, LLC. All rights reserved.
|
|
3
|
+
"""Wireframe artifact helpers for UI-oriented projects."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from specsmith.requirements import list_reqs
|
|
11
|
+
|
|
12
|
+
_WF_ID_RE = re.compile(r"^(WF-[A-Z0-9-]+)")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _wireframes_dir(root: Path) -> Path:
|
|
16
|
+
return root / "docs" / "wireframes"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def list_wireframes(root: Path) -> list[dict[str, str]]:
|
|
20
|
+
"""List wireframe files and any directly linked requirements."""
|
|
21
|
+
wf_dir = _wireframes_dir(root)
|
|
22
|
+
reqs = list_reqs(root)
|
|
23
|
+
refs: dict[str, list[str]] = {}
|
|
24
|
+
for req in reqs:
|
|
25
|
+
wf = req.get("wireframe", "").strip()
|
|
26
|
+
if wf:
|
|
27
|
+
refs.setdefault(Path(wf).name, []).append(req["id"])
|
|
28
|
+
|
|
29
|
+
items: list[dict[str, str]] = []
|
|
30
|
+
if not wf_dir.exists():
|
|
31
|
+
return items
|
|
32
|
+
for fp in sorted(p for p in wf_dir.iterdir() if p.is_file()):
|
|
33
|
+
match = _WF_ID_RE.match(fp.stem)
|
|
34
|
+
items.append(
|
|
35
|
+
{
|
|
36
|
+
"id": match.group(1) if match else fp.stem,
|
|
37
|
+
"file": str(fp.relative_to(root)),
|
|
38
|
+
"refs": ", ".join(refs.get(fp.name, [])),
|
|
39
|
+
}
|
|
40
|
+
)
|
|
41
|
+
return items
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def check_wireframe_refs(root: Path) -> list[str]:
|
|
45
|
+
"""Return missing wireframe references from REQUIREMENTS.md."""
|
|
46
|
+
missing: list[str] = []
|
|
47
|
+
for req in list_reqs(root):
|
|
48
|
+
wf = req.get("wireframe", "").strip()
|
|
49
|
+
if not wf:
|
|
50
|
+
continue
|
|
51
|
+
target = (root / wf).resolve()
|
|
52
|
+
if not target.exists():
|
|
53
|
+
missing.append(f"{req['id']} → {wf}")
|
|
54
|
+
return missing
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def read_wireframe(root: Path, wireframe_id: str) -> str:
|
|
58
|
+
"""Return metadata and best-effort content for a wireframe artifact."""
|
|
59
|
+
wf_dir = _wireframes_dir(root)
|
|
60
|
+
if not wf_dir.exists():
|
|
61
|
+
return "[NOT FOUND] docs/wireframes/"
|
|
62
|
+
|
|
63
|
+
needle = wireframe_id.lower()
|
|
64
|
+
candidates = [
|
|
65
|
+
fp
|
|
66
|
+
for fp in wf_dir.iterdir()
|
|
67
|
+
if fp.is_file() and (fp.name.lower() == needle or fp.stem.lower().startswith(needle))
|
|
68
|
+
]
|
|
69
|
+
if not candidates:
|
|
70
|
+
return f"[NOT FOUND] {wireframe_id}"
|
|
71
|
+
|
|
72
|
+
target = candidates[0]
|
|
73
|
+
size = target.stat().st_size
|
|
74
|
+
rel = target.relative_to(root)
|
|
75
|
+
suffix = target.suffix.lower()
|
|
76
|
+
header = f"{rel} ({size:,} bytes)"
|
|
77
|
+
if suffix in {".svg", ".md", ".txt"}:
|
|
78
|
+
content = target.read_text(encoding="utf-8", errors="ignore")
|
|
79
|
+
if len(content) > 8000:
|
|
80
|
+
content = content[:8000] + "\n...(truncated)"
|
|
81
|
+
return f"{header}\n\n{content}"
|
|
82
|
+
return (
|
|
83
|
+
f"{header}\n\n"
|
|
84
|
+
"Binary wireframe asset. Use a vision-capable client or open the file directly. "
|
|
85
|
+
"For traceability, reference it from REQUIREMENTS.md via a `Wireframe` field."
|
|
86
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: specsmith
|
|
3
|
-
Version: 0.3.6.
|
|
3
|
+
Version: 0.3.6.dev174
|
|
4
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
|
|
@@ -35,6 +35,7 @@ src/specsmith/profiles.py
|
|
|
35
35
|
src/specsmith/rate_limits.py
|
|
36
36
|
src/specsmith/releaser.py
|
|
37
37
|
src/specsmith/requirements.py
|
|
38
|
+
src/specsmith/retrieval.py
|
|
38
39
|
src/specsmith/scaffolder.py
|
|
39
40
|
src/specsmith/session.py
|
|
40
41
|
src/specsmith/tool_installer.py
|
|
@@ -45,6 +46,7 @@ src/specsmith/updater.py
|
|
|
45
46
|
src/specsmith/upgrader.py
|
|
46
47
|
src/specsmith/validator.py
|
|
47
48
|
src/specsmith/vcs_commands.py
|
|
49
|
+
src/specsmith/wireframes.py
|
|
48
50
|
src/specsmith/workspace.py
|
|
49
51
|
src/specsmith.egg-info/PKG-INFO
|
|
50
52
|
src/specsmith.egg-info/SOURCES.txt
|
|
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
|
{specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/profiles/epistemic-auditor.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/providers/anthropic.py
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
|
|
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
|
{specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/gui/widgets/update_checker.py
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
|
{specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/community/bug_report.md.j2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/community/license-MIT.j2
RENAMED
|
File without changes
|
|
File without changes
|
{specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/community/security.md.j2
RENAMED
|
File without changes
|
{specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/docs/architecture.md.j2
RENAMED
|
File without changes
|
{specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/docs/mkdocs.yml.j2
RENAMED
|
File without changes
|
{specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/docs/readthedocs.yaml.j2
RENAMED
|
File without changes
|
{specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/docs/requirements.md.j2
RENAMED
|
File without changes
|
{specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/docs/test-spec.md.j2
RENAMED
|
File without changes
|
{specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/docs/workflow.md.j2
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
|
{specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/governance/roles.md.j2
RENAMED
|
File without changes
|
{specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/governance/rules.md.j2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/governance/workflow.md.j2
RENAMED
|
File without changes
|
{specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/js/package.json.j2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/python/pyproject.toml.j2
RENAMED
|
File without changes
|
|
File without changes
|
{specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/rust/Cargo.toml.j2
RENAMED
|
File without changes
|
|
File without changes
|
{specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/scripts/exec.cmd.j2
RENAMED
|
File without changes
|
{specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/scripts/exec.sh.j2
RENAMED
|
File without changes
|
{specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/scripts/run.cmd.j2
RENAMED
|
File without changes
|
|
File without changes
|
{specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/scripts/setup.cmd.j2
RENAMED
|
File without changes
|
{specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/scripts/setup.sh.j2
RENAMED
|
File without changes
|
{specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/workflows/release.yml.j2
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
|
{specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith.egg-info/dependency_links.txt
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
|