-
Notifications
You must be signed in to change notification settings - Fork 101
Expand file tree
/
Copy pathpyproject.toml
More file actions
445 lines (426 loc) · 19.5 KB
/
pyproject.toml
File metadata and controls
445 lines (426 loc) · 19.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
[project]
name = "spec-kitty-cli"
version = "3.2.0rc27"
description = "Spec Kitty, a tool for Specification Driven Development (SDD) agentic projects, with kanban and git worktree isolation."
readme = "README.md"
license = { file = "LICENSE" }
requires-python = ">=3.11"
authors = [
{ name = "Spec Kitty Contributors" },
]
maintainers = [
{ name = "Spec Kitty Contributors" },
]
keywords = [
"specification",
"spec-driven-development",
"sdd",
"agentic-development",
"ai-coding",
"claude-code",
"ai-agents",
"llm-tools",
"code-generation",
"feature-specs",
"requirements",
"planning",
"kanban",
"git-worktree",
"workflow-automation",
"cli",
]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Software Development",
"Topic :: Software Development :: Code Generators",
"Topic :: Software Development :: Documentation",
"Topic :: Software Development :: Quality Assurance",
"Topic :: Software Development :: Version Control",
"Topic :: Software Development :: Version Control :: Git",
"Topic :: Utilities",
"Environment :: Console",
"Typing :: Typed",
]
dependencies = [
"typer>=0.24.1",
"rich>=14.3.3",
"pygments>=2.20.0", # CVE-2026-4539 fix; transitive dep of rich, pinned explicitly
"charset-normalizer>=3.4,<4", # encoding detection for charter chokepoint (FR-016; was transitive via requests)
"httpx[socks]>=0.28.1",
"platformdirs>=4.9.2",
"readchar>=4.2.1",
"truststore>=0.10.4",
"pyyaml>=6.0",
"ruamel.yaml>=0.18.0",
"pydantic>=2.0",
"packaging>=23.0",
"psutil>=5.9.0", # Cross-platform process management for dashboard
# spec-kitty-events / spec-kitty-tracker: external PyPI dependencies.
# Compatibility ranges follow the upstream public-surface contracts pinned
# in tests/contract/spec_kitty_events_consumer/ and
# tests/contract/spec_kitty_tracker_consumer/. Exact pinned versions live
# in uv.lock, not here. spec-kitty-runtime is intentionally absent: the
# CLI's runtime surface is internalized under
# src/specify_cli/next/_internal_runtime/ (see WP01 of mission
# shared-package-boundary-cutover-01KQ22DS), and absence is enforced by
# tests/architectural/test_pyproject_shape.py.
# TeamSpace dry-run and legacy-envelope normalization require the 5.2.0 event contract.
"spec-kitty-events>=5.2.0,<6.0.0",
"spec-kitty-tracker>=0.4,<0.5",
"websockets>=12.0", # WebSocket client for sync protocol
"toml>=0.10.2", # Config file parsing
"filelock>=3.13.0", # Credential storage locking
"requests>=2.33.0", # HTTP client for batch sync (CVE-2026-25645 fix)
"transitions>=0.9.2", # State machine library for mission DSL v1
"jsonschema>=4.0", # JSON Schema validation for mission configs
"python-ulid>=3.0", # ULID generation for event IDs
"google-re2>=1.1", # RE2 engine for linear-time regex (ReDoS prevention)
"cryptography>=42.0", # AES-256-GCM + scrypt KDF for encrypted auth session storage (feature 080)
]
[project.urls]
Repository = "https://github.com/Priivacy-ai/spec-kitty"
Issues = "https://github.com/Priivacy-ai/spec-kitty/issues"
Documentation = "https://docs.spec-kitty.ai/"
Changelog = "https://github.com/Priivacy-ai/spec-kitty/blob/main/CHANGELOG.md"
[project.optional-dependencies]
test = [
"pytest>=9.0.3", # CVE-2025-71176 fix
"pytest-asyncio>=0.21.0", # Required for async tests
"pytest-cov>=4.1.0", # Required by CI coverage flags
"pytest-timeout>=2.2.0", # Required by mutmut pytest_add_cli_args --timeout flag
"respx>=0.21.1", # HTTPX mocking for auth transport tests (feature 080)
"diff-cover>=10.0.0", # Enforce changed-line coverage in CI
"chardet>=3.0.4,<6", # Pin chardet below 6.x to avoid requests compatibility warning
"build>=1.0.0", # Required for distribution tests (wheel building)
"mutmut>=3.5.0", # Mutation testing
"pytestarch>=4.0.0", # Architectural dependency testing (ADR 2026-03-27-1)
]
lint = [
"ruff>=0.4.0",
"mypy>=1.10.0",
"types-jsonschema>=4.0.0",
"types-psutil>=5.9.0",
"types-PyYAML>=6.0",
"types-requests>=2.33.0",
"types-toml>=0.10.0",
"bandit>=1.7.0",
"pip-audit>=2.7.0",
"cyclonedx-bom>=4.0", # SBOM generation for release pipeline
]
[project.scripts]
spec-kitty = "specify_cli:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.metadata]
allow-direct-references = true
[tool.hatch.build.targets.wheel]
packages = ["src/kernel", "src/specify_cli", "src/doctrine", "src/charter"]
exclude = [
"src/specify_cli/**/tests/**",
]
# Include all non-Python files (templates, configs, etc.)
# Runtime command templates are package assets under specify_cli/missions;
# doctrine/missions holds the governance/action-bundle side of the surface.
artifacts = [
"src/specify_cli/**/*.md",
"src/specify_cli/**/*.yaml",
"src/specify_cli/**/*.yml",
"src/specify_cli/**/*.json",
"src/specify_cli/**/*.jsonl",
"src/charter/**/*.yaml",
"src/charter/**/*.yml",
"src/doctrine/**/*.md",
"src/doctrine/**/*.yaml",
"src/doctrine/**/*.yml",
"src/doctrine/**/*.json",
"src/doctrine/**/*.csv",
"src/doctrine/**/*.template",
]
[tool.hatch.build.targets.sdist]
include = [
"src/**/*",
"src/doctrine/curation/imports/**/*",
"README.md",
"LICENSE",
"CHANGELOG.md",
"pyproject.toml",
]
exclude = [
".kittify/**",
".kittify/active-mission",
"src/specify_cli/**/tests/**",
]
[tool.pytest.ini_options]
markers = [
"fast: Pure unit tests with no I/O or subprocess calls (CI fast gate)",
"integration: Integration tests using real filesystem or in-process I/O, no subprocess/git",
"slow: Tests taking >10 seconds or requiring heavy setup",
"git_repo: Tests that need a real git repository (integration gate)",
"adversarial: Adversarial/security tests for 0.13.0",
"doctrine: Doctrine package smoke and integration checks",
"distribution: Tests requiring wheel install (slow, no SPEC_KITTY_TEMPLATE_ROOT)",
"e2e: End-to-end tests exercising the full CLI workflow (may be slow)",
"platform_darwin: macOS-specific tests (case-insensitive FS)",
"platform_linux: Linux-specific tests",
"requires_symlinks: Tests that need symlink support",
"no_readiness_stub: Opt out of any autouse readiness-stub fixture so the test exercises its own readiness wiring (mission 082 tracker CLI tests)",
"architectural: Architectural enforcement tests (layer rules, import-graph invariants)",
"contract: Consumer-surface contract tests pinning external public APIs (e.g. spec-kitty-events / spec-kitty-tracker) the CLI depends on",
"windows_ci: Tests that require a native win32 environment — auto-skipped on non-Windows",
"live_adapter: tests that call the real Anthropic API (deselect with -m 'not live_adapter')",
"non_sandbox: test is structurally incompatible with mutmut's forked sandbox (subprocess CLI calls, whole-codebase AST walks, wheel builds, or repo-state fixtures outside also_copy). See ADR 2026-04-20-1",
"flaky: test passes in the main suite but is non-deterministic under mutmut or forking pipelines. Each entry is debt — root-cause and remove. See ADR 2026-04-20-1",
]
[tool.ruff]
target-version = "py311"
line-length = 164
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"F", # pyflakes
"W", # pycodestyle warnings
"C90", # mccabe complexity (≈ Sonar S3776)
"C4", # comprehension simplifications (≈ Sonar S7494, S7500)
"ARG", # unused arguments (≈ Sonar S1172)
"B", # bugbear (common bugs, raise-without-from, etc.)
"SIM", # simplify (≈ Sonar S1066, S3358)
"UP", # pyupgrade (deprecated patterns, Pydantic v1 compat)
"ASYNC", # async issues (≈ Sonar S7497, S7502)
"S", # bandit security checks (≈ Sonar S2083)
]
ignore = [
"S101", # allow assert (used extensively in tests and guards)
"S603", # subprocess without shell check (CLI tool by design)
"S607", # partial executable path (CLI tool by design)
"B008", # function calls in default arguments (intentional in CLI/Typer)
"S110", # try-except-pass: redundant with SIM105 (contextlib.suppress), which is already selected
"S310", # urllib-urlopen: spec-kitty intentionally makes HTTP requests to the SaaS backend
]
[tool.ruff.lint.per-file-ignores]
"tests/**" = ["ARG", "S", "E402"] # Relax unused-arg, security, and import-order rules in tests
# B904 (raise-without-from) in these CLI files requires human review: raise typer.Exit() calls
# appear inside nested if/else branches within except blocks, making automated patching unsafe.
"src/specify_cli/cli/commands/agent/config.py" = ["B904"]
"src/specify_cli/cli/commands/agent/status.py" = ["B904"]
"src/specify_cli/cli/commands/agent/workflow.py" = ["B904"]
# F822 false positives: these acceptance-support shims populate their namespace
# at runtime via ``globals().update(_load_acceptance_api())``; the names in
# ``__all__`` are real but resolved dynamically and not visible to static
# analysis. The runtime guarantee is enforced by _load_acceptance_api itself.
"scripts/tasks/acceptance_support.py" = ["F822"]
".kittify/overrides/scripts/tasks/acceptance_support.py" = ["F822"]
[tool.ruff.lint.mccabe]
max-complexity = 15 # Match SonarCloud S3776 threshold
[tool.mypy]
# Keep strict mode as the default quality bar for actively maintained code.
strict = true
# Point mypy at the source tree so it resolves specify_cli from src/ rather than
# an installed wheel that may lack a py.typed marker.
mypy_path = "src"
[[tool.mypy.overrides]]
# When checking a narrow file path under ``specify_cli``, mypy otherwise walks
# the package's CLI bootstrap import graph via ``specify_cli.__init__`` and
# reports unrelated module errors instead of the target file's own issues.
module = ["specify_cli.*"]
follow_imports = "skip"
[[tool.mypy.overrides]]
# When checking charter.neutrality in isolation, mypy walks into other charter
# submodules (interview, resolver, context, …) that have pre-existing strict
# issues being addressed in a separate mission. Skip them so that
# ``mypy --strict src/charter/neutrality/`` reports only neutrality's own issues.
module = ["charter.*"]
follow_imports = "skip"
[[tool.mypy.overrides]]
# Transitional quarantine for legacy modules that are still being migrated to strict typing.
module = [
"specify_cli.agent_utils.status",
"specify_cli.cli.commands.glossary",
"specify_cli.cli.commands",
"specify_cli.cli.commands.agent.config",
"specify_cli.cli.commands.agent.mission_run",
"specify_cli.cli.commands.agent.status",
"specify_cli.cli.commands.agent.tasks",
"specify_cli.cli.commands.agent.workflow",
"specify_cli.cli.commands.auth",
"specify_cli.cli.commands.implement",
"specify_cli.cli.commands.init",
"specify_cli.cli.commands.merge",
"specify_cli.cli.commands.next_cmd",
"specify_cli.cli.commands.sync",
"specify_cli.cli.commands.tracker",
"specify_cli.cli.commands.upgrade",
"specify_cli.cli.ui",
"specify_cli.core.context_validation",
"specify_cli.core.mission_detection",
"specify_cli.dashboard.diagnostics",
"specify_cli.dashboard.handlers.api",
"specify_cli.dashboard.handlers.base",
"specify_cli.dashboard.handlers.mission_runs",
"specify_cli.dashboard.lifecycle",
"specify_cli.dashboard.scanner",
"specify_cli.doc_state",
"specify_cli.dossier.api",
"specify_cli.dossier.drift_detector",
"specify_cli.dossier.indexer",
"specify_cli.dossier.manifest",
"specify_cli.dossier.models",
"specify_cli.events.adapter",
"specify_cli.frontmatter",
"specify_cli.gap_analysis",
"specify_cli.glossary.events",
"specify_cli.glossary.middleware",
"specify_cli.manifest",
"specify_cli.merge.ordering",
"specify_cli.mission",
"specify_cli.mission_v1.compat",
"specify_cli.mission_v1.events",
"specify_cli.mission_v1.runner",
"specify_cli.mission_v1",
"specify_cli.next.decision",
"specify_cli.next.runtime_bridge",
"specify_cli.orchestrator_api.commands",
"specify_cli.scripts.tasks.acceptance_support",
"specify_cli.scripts.tasks.task_helpers",
"specify_cli.scripts.tasks.tasks_cli",
"specify_cli.scripts.validate_encoding",
"specify_cli.status.validate",
"specify_cli.sync.auth",
"specify_cli.sync.background",
"specify_cli.sync.batch",
"specify_cli.sync.client",
"specify_cli.sync.emitter",
"specify_cli.sync.runtime",
"specify_cli.tasks_support",
"specify_cli.template.github_client",
"specify_cli.tracker.factory",
"specify_cli.tracker.service",
"specify_cli.tracker.store",
"specify_cli.upgrade.migrations.m_0_10_14_update_implement_slash_command",
"specify_cli.upgrade.migrations.m_0_10_9_repair_templates",
"specify_cli.upgrade.migrations.m_0_11_1_update_implement_slash_command",
"specify_cli.upgrade.migrations.m_0_13_0_update_research_implement_templates",
"specify_cli.upgrade.migrations.m_0_13_5_add_commit_workflow_to_templates",
"specify_cli.upgrade.migrations.m_0_9_1_complete_lane_migration",
"specify_cli.verify_enhanced",
]
ignore_errors = true
[dependency-groups]
dev = [
# Type stubs for dev tooling (mypy --strict gate).
# These mirror the entries in [project.optional-dependencies] lint so that
# `uv run --with mypy mypy --strict ...` resolves them from the default
# environment without requiring `--extra lint`.
"types-jsonschema>=4.0.0",
"types-psutil>=5.9.0",
"types-PyYAML>=6.0",
"types-requests>=2.33.0",
"types-toml>=0.10.0",
]
[tool.mutmut]
paths_to_mutate = [
"src/",
]
tests_dir = ["tests/"]
max_stack_depth = 8
max_children = 8
# Configure how long mutmut waits before killing a slow mutation
# Currently calculated as (duration_of_original_tests + timeout_constant) * timeout_multiplier seconds
timeout_constant = 1.0
timeout_multiplier = 15.0
# mutate_only_covered_lines=true # Optional flag to only mutate lines covered by tests; can be re-enabled later for more comprehensive mutation testing once coverage is improved.
# type_check_command disabled — mypy produces multi-line JSON (hint fields span lines)
# which breaks mutmut's line-based JSON parser. Without it, type-error mutants simply
# fail their tests instead of being filtered out. See mutmut issue for tracking.
# type_check_command = ['mypy', 'src/specify_cli/', '--output', 'json', '--disable-error-code', 'unused-ignore']
pytest_add_cli_args = [
"--timeout=30",
# Marker-based deselection. See ADR 2026-04-20-1 for the taxonomy.
# - Category markers (pre-existing): slow, e2e, distribution, adversarial, windows_ci,
# platform_darwin, live_adapter, architectural — none of these belong in the mutmut sandbox.
# - non_sandbox (new): tests structurally incompatible with the sandbox (subprocess CLI calls,
# whole-codebase AST walks, wheel builds, repo-state fixtures outside also_copy).
# - flaky (new): tests that pass in the main suite but are non-deterministic under mutmut —
# tech debt; each entry should shrink over time.
"-m", "not slow and not e2e and not distribution and not adversarial and not windows_ci and not platform_darwin and not live_adapter and not architectural and not non_sandbox and not flaky",
# Directories we skip at collection time because many files fail to *import* in the sandbox
# (bare-name script-module imports via sys.path tricks that don't survive the fork, repo-state
# consistency checks that walk the live repo). Marker-based deselection can't help here —
# the marker is evaluated *after* import. When we address the collection-time failure mode
# (e.g., by adjusting also_copy or the script module layout) we can lift these too.
"--ignore=tests/unit/agent/",
"--ignore=tests/unit/mission_v1/",
"--ignore=tests/unit/next/",
"--ignore=tests/unit/orchestrator_api/",
"--ignore=tests/unit/runtime/",
"--ignore=tests/specify_cli/cli/commands/agent/",
"--ignore=tests/specify_cli/core/",
"--ignore=tests/specify_cli/test_cli/",
"--ignore=tests/specify_cli/upgrade/",
"--ignore=tests/upgrade/",
"--ignore=tests/specify_cli/scripts/",
"--ignore=tests/cross_cutting/",
"--ignore=tests/release/",
"--ignore=tests/architecture/",
"--ignore=tests/docs/",
"--ignore=tests/test_dashboard/",
# The three entries below are mutmut-sandbox-only workarounds. This entire list lives in
# [tool.mutmut] pytest_add_cli_args, which is passed exclusively when mutmut forks pytest —
# plain `pytest` invocations are unaffected and will still collect these directories.
# sparse_checkout integration tests depend on a live `main` branch locally;
# mutmut runs from a feature-branch worktree where `main` may not exist.
"--ignore=tests/integration/sparse_checkout/",
# tests/auth/ requires `respx` which is not installed in the mutmut sandbox.
"--ignore=tests/auth/",
# tests/agent/cli/commands/ uses subprocess git commit which fails without git config in sandbox.
"--ignore=tests/agent/cli/commands/",
]
also_copy = [
"LICENSE",
"src/specify_cli/compat/", # compat package under test
"README.md",
"contracts/", # JSON fixture files for contract tests
"docs/", # Markdown files referenced by contract/terminology tests
"architecture/", # Architecture docs referenced by tests/docs/ consistency checks
".pytest_cache/spec-kitty-test-venv/", # Pre-seeded venv so test_venv fixture skips rebuild
"src/specify_cli/*.py", # Copy all top-level modules (frontmatter, etc.)
"src/kernel/", # Copy kernel package for shared utilities
"src/doctrine/", # Copy doctrine package for imports
"src/charter/", # Copy charter package
"src/specify_cli/status/", # Copy status module for status transitions
"src/specify_cli/glossary/", # Copy glossary module
"src/specify_cli/cli/", # Copy CLI commands
"src/specify_cli/agent_utils/", # Copy agent utilities
"src/specify_cli/git/", # Copy git utilities
"src/specify_cli/sync/", # Copy sync module
"src/specify_cli/next/", # Copy next module
"src/specify_cli/validators/", # Copy validators
"src/specify_cli/template/", # Copy template utilities
"src/specify_cli/charter/", # Copy charter
"src/specify_cli/missions/", # Copy missions
"src/specify_cli/upgrade/", # Copy upgrade system
"src/specify_cli/runtime/", # Copy runtime
"src/specify_cli/tracker/", # Copy tracker
"src/specify_cli/events/", # Copy events
"src/specify_cli/dossier/", # Copy dossier
"src/specify_cli/dashboard/", # Copy dashboard
"src/specify_cli/orchestrator_api/", # Copy orchestrator API
"src/specify_cli/templates/", # Copy templates
"src/specify_cli/mission_v1/", # Copy mission v1
"src/specify_cli/scripts/", # Copy scripts
"src/specify_cli/skills/", # Copy skills distribution runtime
"mutmut_pytest.ini", # Custom pytest config for mutmut runs
]
# Exclude migrations (idempotent, hard to unit-test meaningfully via mutation)
# version_utils.py contains the mutmut trampoline itself — mutating it breaks all sandbox imports
do_not_mutate = [
"src/specify_cli/upgrade/migrations/",
"src/specify_cli/version_utils.py",
]