spectra-sdd 0.4.0__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.
@@ -0,0 +1,44 @@
1
+ Metadata-Version: 2.1
2
+ Name: spectra-sdd
3
+ Version: 0.4.0
4
+ Summary: Spec-Driven Development for the Agentic Era
5
+ Author: GuiMiran
6
+ License: CC-BY-NC-ND-4.0
7
+ Project-URL: Homepage, https://github.com/GuiMiran/spectra
8
+ Project-URL: Repository, https://github.com/GuiMiran/spectra
9
+ Project-URL: Issues, https://github.com/GuiMiran/spectra/issues
10
+ Keywords: spec-driven-development,sdd,ai-agents,specifications,agentic-ai
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Topic :: Software Development
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Requires-Python: >=3.8
22
+ Description-Content-Type: text/markdown
23
+
24
+ # Spectra — Python
25
+
26
+ Spec-Driven Development for the Agentic Era.
27
+
28
+ ```
29
+ pip install guimiran-spectra
30
+ ```
31
+
32
+ ## Commands
33
+
34
+ ```
35
+ spectra init Scaffold .spectra/ with 13 layers
36
+ spectra status Show which layers are filled vs pending
37
+ spectra trace Scan specs + code → generate 12-trace.md
38
+ spectra validate Validate spec quality and cross-refs
39
+ spectra --version Show installed version
40
+ ```
41
+
42
+ ## Docs
43
+
44
+ https://github.com/GuiMiran/spectra
@@ -0,0 +1,21 @@
1
+ # Spectra — Python
2
+
3
+ Spec-Driven Development for the Agentic Era.
4
+
5
+ ```
6
+ pip install guimiran-spectra
7
+ ```
8
+
9
+ ## Commands
10
+
11
+ ```
12
+ spectra init Scaffold .spectra/ with 13 layers
13
+ spectra status Show which layers are filled vs pending
14
+ spectra trace Scan specs + code → generate 12-trace.md
15
+ spectra validate Validate spec quality and cross-refs
16
+ spectra --version Show installed version
17
+ ```
18
+
19
+ ## Docs
20
+
21
+ https://github.com/GuiMiran/spectra
@@ -0,0 +1,40 @@
1
+ [build-system]
2
+ requires = ["setuptools>=42", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "spectra-sdd"
7
+ version = "0.4.0"
8
+ description = "Spec-Driven Development for the Agentic Era"
9
+ keywords = ["spec-driven-development", "sdd", "ai-agents", "specifications", "agentic-ai"]
10
+ license = { text = "CC-BY-NC-ND-4.0" }
11
+ authors = [{ name = "GuiMiran" }]
12
+ readme = "README.md"
13
+ requires-python = ">=3.8"
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Environment :: Console",
17
+ "Intended Audience :: Developers",
18
+ "Topic :: Software Development",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.8",
21
+ "Programming Language :: Python :: 3.9",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ ]
26
+
27
+ [project.urls]
28
+ Homepage = "https://github.com/GuiMiran/spectra"
29
+ Repository = "https://github.com/GuiMiran/spectra"
30
+ Issues = "https://github.com/GuiMiran/spectra/issues"
31
+
32
+ [project.scripts]
33
+ spectra = "spectra.cli:main"
34
+
35
+ [tool.setuptools.packages.find]
36
+ where = ["."]
37
+ include = ["spectra*"]
38
+
39
+ [tool.setuptools.package-data]
40
+ spectra = ["data/**/*", "data/*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ __version__ = "0.4.0"
@@ -0,0 +1,3 @@
1
+ from spectra.cli import main
2
+
3
+ main()
@@ -0,0 +1,35 @@
1
+ import sys
2
+ from spectra import __version__
3
+ from spectra.colors import p
4
+
5
+
6
+ def main() -> None:
7
+ command = sys.argv[1] if len(sys.argv) > 1 else None
8
+
9
+ if command == 'init':
10
+ from spectra.commands.init import cmd_init
11
+ cmd_init()
12
+
13
+ elif command == 'status':
14
+ from spectra.commands.status import cmd_status
15
+ cmd_status()
16
+
17
+ elif command == 'trace':
18
+ from spectra.commands.trace import cmd_trace
19
+ cmd_trace()
20
+
21
+ elif command == 'validate':
22
+ from spectra.commands.validate import cmd_validate
23
+ cmd_validate()
24
+
25
+ elif command in ('help', '--help', '-h'):
26
+ from spectra.commands.help import cmd_help
27
+ cmd_help()
28
+
29
+ elif command in ('--version', '-v'):
30
+ p(f' spectra v{__version__}')
31
+ p()
32
+
33
+ else:
34
+ from spectra.commands.welcome import welcome
35
+ welcome()
@@ -0,0 +1,25 @@
1
+ import sys
2
+
3
+ _USE_COLOR = sys.stdout.isatty() or True # always emit ANSI; terminals handle it
4
+
5
+ RESET = '\x1b[0m'
6
+ BOLD = '\x1b[1m'
7
+ CYAN = '\x1b[36m'
8
+ WHITE = '\x1b[97m'
9
+ YELLOW = '\x1b[33m'
10
+ GREEN = '\x1b[32m'
11
+ GRAY = '\x1b[90m'
12
+ MAGENTA = '\x1b[35m'
13
+ RED = '\x1b[31m'
14
+
15
+
16
+ def c(color: str, text: str) -> str:
17
+ return f"{color}{text}{RESET}"
18
+
19
+
20
+ def b(color: str, text: str) -> str:
21
+ return f"{BOLD}{color}{text}{RESET}"
22
+
23
+
24
+ def p(line: str = '') -> None:
25
+ print(line)
File without changes
@@ -0,0 +1,24 @@
1
+ from spectra.colors import p, b, c, CYAN, WHITE, GREEN, GRAY
2
+
3
+
4
+ def cmd_help() -> None:
5
+ p()
6
+ p(b(CYAN, ' SPECTRA') + c(GRAY, ' · Spec-Driven Development for Agentic AI'))
7
+ p()
8
+ p(c(GRAY, ' ┌─ Commands ────────────────────────────────────────────────┐'))
9
+ p(f' │ {b(GREEN, "spectra init")} {c(GRAY, "Scaffold .spectra/ with 13 layers")} │')
10
+ p(f' │ {b(GREEN, "spectra status")} {c(GRAY, "Show which layers are filled vs pending")} │')
11
+ p(f' │ {b(GREEN, "spectra trace")} {c(GRAY, "Scan specs + code → generate 12-trace.md")} │')
12
+ p(f' │ {b(GREEN, "spectra validate")} {c(GRAY, "Validate spec quality and cross-refs")} │')
13
+ p(f' │ {b(GREEN, "spectra --version")} {c(GRAY, "Show installed version")} │')
14
+ p(c(GRAY, ' └───────────────────────────────────────────────────────────┘'))
15
+ p()
16
+ p(f' {c(GRAY, "Workflow")}:')
17
+ p(f' {c(GRAY, "1.")} {b(WHITE, "spectra init")} {c(GRAY, "→")} scaffold layers')
18
+ p(f' {c(GRAY, "2.")} Fill {b(WHITE, "SPECTRA-PROMPT.md")} {c(GRAY, "→")} paste into Claude/GPT-4/Gemini')
19
+ p(f' {c(GRAY, "3.")} Copy generated specs {c(GRAY, "→")} paste into {b(WHITE, ".spectra/")}')
20
+ p(f' {c(GRAY, "4.")} {b(WHITE, "spectra status")} {c(GRAY, "→")} verify coverage')
21
+ p(f' {c(GRAY, "5.")} {b(WHITE, "spectra trace")} {c(GRAY, "→")} generate traceability matrix')
22
+ p()
23
+ p(f' {c(GRAY, "Docs")} {c(CYAN, "https://github.com/GuiMiran/spectra")}')
24
+ p()
@@ -0,0 +1,182 @@
1
+ import os
2
+ import shutil
3
+ from datetime import date
4
+ from pathlib import Path
5
+ from spectra.colors import p, b, c, CYAN, WHITE, YELLOW, GREEN, GRAY, MAGENTA
6
+
7
+ DATA_DIR = Path(__file__).parent.parent / 'data'
8
+
9
+ LAYERS = [
10
+ ('00', 'vision', 'Vision & Context'),
11
+ ('01', 'glossary', 'Domain Glossary'),
12
+ ('02', 'stories', 'User Stories'),
13
+ ('03', 'business-rules', 'Business Rules'),
14
+ ('04', 'invariants', 'Invariants'),
15
+ ('05', 'contracts', 'Operation Contracts'),
16
+ ('06', 'policies', 'Decision Policies'),
17
+ ('07', 'events', 'Domain Events'),
18
+ ('08', 'agents', 'Agents'),
19
+ ('09', 'skills', 'Skills'),
20
+ ('10', 'workflows', 'Workflows'),
21
+ ('11', 'acceptance-criteria', 'Acceptance Criteria'),
22
+ ('12', 'trace', 'SPECTRA-TRACE'),
23
+ ]
24
+
25
+
26
+ def _generate_layer(num: str, name: str) -> str:
27
+ return f"""# LAYER {num} — {name.upper()}
28
+
29
+ > **Status**: Pending — fill this layer with your domain knowledge.
30
+ > Send `SPECTRA-PROMPT.md` to an LLM to generate all 13 layers at once.
31
+
32
+ ---
33
+
34
+ <!-- Add your {name} here. Each element must have a unique ID. -->
35
+ <!-- Example IDs: US-001, BR-001, INV-001, POL-001, EVT-001, SK-001, WF-001 -->
36
+ """
37
+
38
+
39
+ def _generate_trace() -> str:
40
+ today = date.today().isoformat()
41
+ return f"""# LAYER 12 — SPECTRA-TRACE
42
+ ## Bidirectional Agentic Traceability Matrix
43
+
44
+ > **Live layer** — updated automatically by the agent at the end of every iteration.
45
+ > Forward: detects functional gaps (spec without code).
46
+ > Reverse: detects technical gaps (code without spec).
47
+
48
+ ---
49
+
50
+ ## Coverage Dashboard
51
+ > Last updated: iter-0 · {today} · Agent: —
52
+
53
+ | Metric | Value | Trend |
54
+ |-------------------------|-------|-------|
55
+ | Total specs | 0 | — |
56
+ | Implemented specs | 0 | — |
57
+ | Functional coverage | 0% | — |
58
+ | Artifacts without spec | 0 | — |
59
+ | CRITICAL gaps | 0 | — |
60
+ | MAJOR gaps | 0 | — |
61
+ | MINOR gaps | 0 | — |
62
+ | Current iteration | iter-0| — |
63
+
64
+ ---
65
+
66
+ ## Forward Matrix — Spec → Code
67
+ > Detects **functional gaps**: specs with no implementation.
68
+
69
+ | spec_id | type | description | prio | status | artifacts | tests | severity | iter | notes |
70
+ |---------|------|-------------|------|--------|-----------|-------|----------|------|-------|
71
+ | — | — | *No specs registered yet* | — | — | — | — | — | — | — |
72
+
73
+ **Status legend**: `✅ IMPL` · `⏳ PARTIAL` · `❌ PENDING` · `🚫 EXCLUDED`
74
+ **Severity**: `CRITICAL` (invariant/legal) · `MAJOR` (MUST BR/US) · `MINOR` (SHOULD/COULD)
75
+
76
+ ---
77
+
78
+ ## Reverse Matrix — Code → Spec
79
+ > Detects **technical gaps**: code artifacts with no spec justifying them.
80
+
81
+ | artifact | type | description | specs | status | action | iter_detected |
82
+ |----------|------|-------------|-------|--------|--------|---------------|
83
+ | — | — | *No artifacts registered yet* | — | — | — | — |
84
+
85
+ **Status legend**: `✅ TRACED` · `⚠️ ORPHAN` · `🔍 REVIEW`
86
+ **Action**: `KEEP` · `SPECIFY` · `DELETE` · `REFACTOR`
87
+
88
+ ---
89
+
90
+ ## Gap Report — iter-0
91
+ > No gaps detected yet. Start filling the spec layers and implementing features.
92
+
93
+ ---
94
+
95
+ ## Iteration Log
96
+
97
+ | iter | date | specs_impl | coverage | critical_gaps | major_gaps | orphans | note |
98
+ |------|------|------------|----------|---------------|-----------|---------|------|
99
+ | iter-0 | {today} | 0/0 | 0% | 0 | 0 | 0 | Initial scaffold |
100
+
101
+ ---
102
+
103
+ ## Agent Protocol
104
+ > Instructions for the agent on how to update this file.
105
+
106
+ At the end of every iteration:
107
+ 1. Count total specs across layers 02–11
108
+ 2. For each spec, find its implementing artifacts and link them in the Forward Matrix
109
+ 3. Scan all source files for `// @spectra` trace comments → populate Reverse Matrix
110
+ 4. Recalculate coverage = implemented / total × 100
111
+ 5. Classify unimplemented specs by severity (CRITICAL / MAJOR / MINOR)
112
+ 6. Update Coverage Dashboard and append a row to Iteration Log
113
+ 7. Write Gap Report with actionable next steps
114
+
115
+ ```
116
+ coverage = implemented_specs / total_specs × 100
117
+ CRITICAL ← INV not implemented | BR from legal regulation | SK blocking
118
+ MAJOR ← MUST BR not implemented | MUST US not implemented
119
+ MINOR ← SHOULD/COULD BR or US | POL edge case
120
+ ```
121
+ """
122
+
123
+
124
+ def cmd_init() -> None:
125
+ p()
126
+ p(b(CYAN, ' Initializing Spectra...'))
127
+ p()
128
+
129
+ spectra_dir = Path.cwd() / '.spectra'
130
+
131
+ if spectra_dir.exists():
132
+ p(c(YELLOW, ' ⚠ .spectra/ already exists in this directory.'))
133
+ p(c(GRAY, ' Remove it first if you want to re-initialize.'))
134
+ p()
135
+ raise SystemExit(1)
136
+
137
+ spectra_dir.mkdir(parents=True)
138
+
139
+ for num, slug, name in LAYERS:
140
+ file = spectra_dir / f'{num}-{slug}.md'
141
+ content = _generate_trace() if num == '12' else _generate_layer(num, name)
142
+ file.write_text(content, encoding='utf-8')
143
+ label = b(MAGENTA, name) if num == '12' else c(WHITE, name)
144
+ p(f' {c(GRAY, num + " ·")} {label}')
145
+
146
+ # Copy SPECTRA-PROMPT.md from package data
147
+ prompt_src = DATA_DIR / 'SPECTRA-PROMPT.md'
148
+ prompt_dest = Path.cwd() / 'SPECTRA-PROMPT.md'
149
+ if prompt_src.exists() and not prompt_dest.exists():
150
+ shutil.copy2(prompt_src, prompt_dest)
151
+
152
+ # Create .instructions.md for AI agents
153
+ instr_dest = Path.cwd() / '.instructions.md'
154
+ if not instr_dest.exists():
155
+ instr_dest.write_text(
156
+ '# Project Instructions\n\n'
157
+ '## Spectra Framework\n\n'
158
+ 'This project uses **Spectra** for domain specification.\n\n'
159
+ '### AI Agent Instructions\n\n'
160
+ '1. Read relevant layers from `.spectra/` before making any changes\n'
161
+ '2. Never violate invariants defined in `04-invariants.md`\n'
162
+ '3. Apply business rules from `03-business-rules.md`\n'
163
+ '4. Use only canonical terms from `01-glossary.md`\n'
164
+ '5. Update `12-trace.md` (SPECTRA-TRACE) at the end of every iteration\n\n'
165
+ '> Specs are the source of truth. Code is the derivative.\n',
166
+ encoding='utf-8',
167
+ )
168
+
169
+ p()
170
+ p(c(GREEN, ' ✔ .spectra/ created with 13 layers'))
171
+ p(c(GREEN, ' ✔ SPECTRA-PROMPT.md ready'))
172
+ p(c(GREEN, ' ✔ .instructions.md created for AI agents'))
173
+ p()
174
+ p(b(YELLOW, ' Next steps'))
175
+ p()
176
+ p(f' {c(GRAY, "1.")} Open {b(WHITE, "SPECTRA-PROMPT.md")} and fill in your domain')
177
+ p(f' {c(GRAY, "2.")} Send it to {b(WHITE, "Claude, GPT-4, or Gemini")} to generate the 13 layers')
178
+ p(f' {c(GRAY, "3.")} Drop the specs into {b(WHITE, ".spectra/")} — your agent reads them automatically')
179
+ p(f' {c(GRAY, "4.")} Build. The agent now knows your domain.')
180
+ p()
181
+ p(c(GRAY, ' Docs: https://github.com/GuiMiran/spectra'))
182
+ p()
@@ -0,0 +1,70 @@
1
+ import re
2
+ from pathlib import Path
3
+ from spectra.colors import p, b, c, CYAN, WHITE, YELLOW, GREEN, GRAY, MAGENTA, RED
4
+ from spectra.layers import LAYERS, ID_PATTERNS, STUB_MARKERS
5
+
6
+
7
+ def cmd_status() -> None:
8
+ spectra_dir = Path.cwd() / '.spectra'
9
+
10
+ p()
11
+ p(b(CYAN, ' SPECTRA STATUS'))
12
+ p()
13
+
14
+ if not spectra_dir.exists():
15
+ p(c(YELLOW, ' ⚠ No .spectra/ directory found.'))
16
+ p(c(GRAY, ' Run: spectra init'))
17
+ p()
18
+ raise SystemExit(1)
19
+
20
+ def is_stub(content: str) -> bool:
21
+ return any(m in content for m in STUB_MARKERS)
22
+
23
+ total_ids = 0
24
+ filled_layers = 0
25
+
26
+ p(c(GRAY, ' ─────────────────────────────────────────────────────────────'))
27
+ p(f' {"Layer":<28} {"IDs":<6} Status')
28
+ p(c(GRAY, ' ─────────────────────────────────────────────────────────────'))
29
+
30
+ for num, slug, name in LAYERS:
31
+ file = spectra_dir / f'{num}-{slug}.md'
32
+
33
+ if not file.exists():
34
+ p(f' {c(GRAY, num + " ·")} {name:<25} {c(RED, "—— ❌ missing")}')
35
+ continue
36
+
37
+ content = file.read_text(encoding='utf-8')
38
+ stub = is_stub(content)
39
+
40
+ clean = re.sub(r'```[\s\S]*?```', '', content)
41
+ ids = sum(len(rx.findall(clean)) for rx in ID_PATTERNS.values())
42
+ total_ids += ids
43
+ if not stub:
44
+ filled_layers += 1
45
+
46
+ is_trace = (num == '12')
47
+ name_col = b(MAGENTA, name) if is_trace else c(WHITE, name)
48
+ ids_col = c(GREEN if ids > 0 else GRAY, str(ids).ljust(6))
49
+ status = c(YELLOW, '⏳ pending') if stub else c(GREEN, '✅ filled')
50
+
51
+ extra = 20 if is_trace else 0
52
+ p(f' {c(GRAY, num + " ·")} {name_col:<{25 + extra}} {ids_col} {status}')
53
+
54
+ p(c(GRAY, ' ─────────────────────────────────────────────────────────────'))
55
+ p()
56
+
57
+ spec_layers = len(LAYERS) - 1 # exclude trace
58
+ pct = round((filled_layers / spec_layers) * 100) if spec_layers else 0
59
+ bar = '█' * (pct // 5) + '░' * (20 - pct // 5)
60
+ p(f' Layers filled {b(WHITE, str(filled_layers) + "/" + str(spec_layers))} {c(GREEN if filled_layers > 0 else GRAY, bar)} {b(CYAN, str(pct) + "%")}')
61
+ p(f' Total IDs {b(WHITE, str(total_ids))}')
62
+ p()
63
+
64
+ if filled_layers == 0:
65
+ p(c(YELLOW, ' Next: fill SPECTRA-PROMPT.md and send it to an LLM to generate your specs.'))
66
+ elif pct < 80:
67
+ p(c(YELLOW, ' Some layers still pending. Run: spectra trace to build the traceability matrix.'))
68
+ else:
69
+ p(c(GREEN, ' Specs look complete. Run: spectra trace to generate the traceability matrix.'))
70
+ p()