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.
Files changed (166) hide show
  1. {specsmith-0.3.6.dev171/src/specsmith.egg-info → specsmith-0.3.6.dev174}/PKG-INFO +1 -1
  2. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/pyproject.toml +1 -1
  3. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/runner.py +58 -17
  4. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/tools.py +51 -0
  5. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/cli.py +117 -0
  6. specsmith-0.3.6.dev174/src/specsmith/retrieval.py +118 -0
  7. specsmith-0.3.6.dev174/src/specsmith/wireframes.py +86 -0
  8. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174/src/specsmith.egg-info}/PKG-INFO +1 -1
  9. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith.egg-info/SOURCES.txt +2 -0
  10. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/LICENSE +0 -0
  11. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/README.md +0 -0
  12. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/setup.cfg +0 -0
  13. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/epistemic/__init__.py +0 -0
  14. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/epistemic/belief.py +0 -0
  15. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/epistemic/certainty.py +0 -0
  16. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/epistemic/failure_graph.py +0 -0
  17. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/epistemic/py.typed +0 -0
  18. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/epistemic/recovery.py +0 -0
  19. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/epistemic/session.py +0 -0
  20. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/epistemic/stress_tester.py +0 -0
  21. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/epistemic/trace.py +0 -0
  22. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/__init__.py +0 -0
  23. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/__main__.py +0 -0
  24. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/__init__.py +0 -0
  25. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/core.py +0 -0
  26. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/hooks.py +0 -0
  27. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/optimizer.py +0 -0
  28. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/profiles/epistemic-auditor.md +0 -0
  29. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/profiles/planner.md +0 -0
  30. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/profiles/verifier.md +0 -0
  31. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/providers/__init__.py +0 -0
  32. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/providers/anthropic.py +0 -0
  33. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/providers/gemini.py +0 -0
  34. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/providers/mistral.py +0 -0
  35. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/providers/ollama.py +0 -0
  36. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/providers/openai.py +0 -0
  37. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/agent/skills.py +0 -0
  38. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/architect.py +0 -0
  39. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/auditor.py +0 -0
  40. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/auth.py +0 -0
  41. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/commands/__init__.py +0 -0
  42. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/compressor.py +0 -0
  43. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/config.py +0 -0
  44. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/credit_analyzer.py +0 -0
  45. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/credits.py +0 -0
  46. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/differ.py +0 -0
  47. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/doctor.py +0 -0
  48. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/epistemic/__init__.py +0 -0
  49. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/epistemic/belief.py +0 -0
  50. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/epistemic/certainty.py +0 -0
  51. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/epistemic/failure_graph.py +0 -0
  52. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/epistemic/recovery.py +0 -0
  53. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/epistemic/stress_tester.py +0 -0
  54. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/executor.py +0 -0
  55. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/exporter.py +0 -0
  56. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/gui/__init__.py +0 -0
  57. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/gui/app.py +0 -0
  58. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/gui/main_window.py +0 -0
  59. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/gui/session_tab.py +0 -0
  60. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/gui/theme.py +0 -0
  61. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/gui/widgets/__init__.py +0 -0
  62. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/gui/widgets/chat_view.py +0 -0
  63. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/gui/widgets/input_bar.py +0 -0
  64. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/gui/widgets/provider_bar.py +0 -0
  65. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/gui/widgets/token_meter.py +0 -0
  66. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/gui/widgets/tool_panel.py +0 -0
  67. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/gui/widgets/update_checker.py +0 -0
  68. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/gui/worker.py +0 -0
  69. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/importer.py +0 -0
  70. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/integrations/__init__.py +0 -0
  71. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/integrations/aider.py +0 -0
  72. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/integrations/base.py +0 -0
  73. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/integrations/claude_code.py +0 -0
  74. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/integrations/copilot.py +0 -0
  75. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/integrations/cursor.py +0 -0
  76. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/integrations/gemini.py +0 -0
  77. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/integrations/warp.py +0 -0
  78. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/integrations/windsurf.py +0 -0
  79. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/languages.py +0 -0
  80. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/ledger.py +0 -0
  81. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/ollama_cmds.py +0 -0
  82. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/patent.py +0 -0
  83. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/phase.py +0 -0
  84. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/plugins.py +0 -0
  85. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/profiles.py +0 -0
  86. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/rate_limits.py +0 -0
  87. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/releaser.py +0 -0
  88. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/requirements.py +0 -0
  89. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/scaffolder.py +0 -0
  90. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/session.py +0 -0
  91. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/agents.md.j2 +0 -0
  92. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/community/bug_report.md.j2 +0 -0
  93. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/community/code_of_conduct.md.j2 +0 -0
  94. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/community/contributing.md.j2 +0 -0
  95. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/community/feature_request.md.j2 +0 -0
  96. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/community/license-Apache-2.0.j2 +0 -0
  97. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/community/license-MIT.j2 +0 -0
  98. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/community/pull_request_template.md.j2 +0 -0
  99. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/community/security.md.j2 +0 -0
  100. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/docs/architecture.md.j2 +0 -0
  101. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/docs/mkdocs.yml.j2 +0 -0
  102. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/docs/readthedocs.yaml.j2 +0 -0
  103. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/docs/requirements.md.j2 +0 -0
  104. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/docs/test-spec.md.j2 +0 -0
  105. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/docs/workflow.md.j2 +0 -0
  106. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/editorconfig.j2 +0 -0
  107. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/gitattributes.j2 +0 -0
  108. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/gitignore.j2 +0 -0
  109. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/go/go.mod.j2 +0 -0
  110. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/go/main.go.j2 +0 -0
  111. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/governance/belief-registry.md.j2 +0 -0
  112. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/governance/context-budget.md.j2 +0 -0
  113. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/governance/drift-metrics.md.j2 +0 -0
  114. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/governance/epistemic-axioms.md.j2 +0 -0
  115. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/governance/failure-modes.md.j2 +0 -0
  116. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/governance/roles.md.j2 +0 -0
  117. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/governance/rules.md.j2 +0 -0
  118. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/governance/uncertainty-map.md.j2 +0 -0
  119. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/governance/verification.md.j2 +0 -0
  120. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/governance/workflow.md.j2 +0 -0
  121. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/js/package.json.j2 +0 -0
  122. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/ledger.md.j2 +0 -0
  123. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/python/cli.py.j2 +0 -0
  124. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/python/init.py.j2 +0 -0
  125. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/python/pyproject.toml.j2 +0 -0
  126. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/readme.md.j2 +0 -0
  127. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/rust/Cargo.toml.j2 +0 -0
  128. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/rust/main.rs.j2 +0 -0
  129. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/scripts/exec.cmd.j2 +0 -0
  130. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/scripts/exec.sh.j2 +0 -0
  131. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/scripts/run.cmd.j2 +0 -0
  132. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/scripts/run.sh.j2 +0 -0
  133. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/scripts/setup.cmd.j2 +0 -0
  134. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/scripts/setup.sh.j2 +0 -0
  135. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/templates/workflows/release.yml.j2 +0 -0
  136. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/tool_installer.py +0 -0
  137. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/toolrules.py +0 -0
  138. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/tools.py +0 -0
  139. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/trace.py +0 -0
  140. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/updater.py +0 -0
  141. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/upgrader.py +0 -0
  142. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/validator.py +0 -0
  143. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/vcs/__init__.py +0 -0
  144. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/vcs/base.py +0 -0
  145. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/vcs/bitbucket.py +0 -0
  146. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/vcs/github.py +0 -0
  147. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/vcs/gitlab.py +0 -0
  148. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/vcs_commands.py +0 -0
  149. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith/workspace.py +0 -0
  150. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith.egg-info/dependency_links.txt +0 -0
  151. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith.egg-info/entry_points.txt +0 -0
  152. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith.egg-info/requires.txt +0 -0
  153. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/src/specsmith.egg-info/top_level.txt +0 -0
  154. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/tests/test_auditor.py +0 -0
  155. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/tests/test_cli.py +0 -0
  156. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/tests/test_compressor.py +0 -0
  157. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/tests/test_epistemic.py +0 -0
  158. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/tests/test_importer.py +0 -0
  159. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/tests/test_integrations.py +0 -0
  160. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/tests/test_optimizer.py +0 -0
  161. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/tests/test_rate_limits.py +0 -0
  162. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/tests/test_scaffolder.py +0 -0
  163. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/tests/test_smoke.py +0 -0
  164. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/tests/test_tools.py +0 -0
  165. {specsmith-0.3.6.dev171 → specsmith-0.3.6.dev174}/tests/test_validator.py +0 -0
  166. {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.dev171
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.dev171"
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"""You are an AEE-integrated specsmith agent for this project.
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
- 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.
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": "Resume from last LEDGER.md entry — summarize state and propose next task",
242
- "save": "Write a ledger entry summarizing this session's work",
243
- "audit": "Run specsmith audit --fix",
244
- "commit": "Run specsmith commit",
245
- "push": "Run specsmith push",
246
- "sync": "Run specsmith sync",
247
- "epistemic": "Run full epistemic audit",
248
- "stress": "Run stress-test on requirements",
249
- "status": "Show session status and credit spend",
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.dev171
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