specfact-cli 0.26.16__tar.gz → 0.26.17__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.
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/PKG-INFO +1 -1
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/pyproject.toml +1 -1
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/__init__.py +1 -1
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/__init__.py +1 -1
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/analyzers/ambiguity_scanner.py +5 -9
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/commands/backlog_commands.py +126 -53
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/generators/report_generator.py +2 -2
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/merge/resolver.py +8 -8
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/models/bridge.py +2 -2
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/models/change.py +2 -2
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/models/contract.py +2 -2
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/models/deviation.py +3 -3
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/models/enforcement.py +3 -3
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/models/persona_template.py +2 -2
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/models/project.py +2 -2
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/models/task.py +3 -3
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/modes/detector.py +2 -2
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/runtime.py +2 -2
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/env_manager.py +2 -2
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/structured_io.py +2 -2
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/validators/sidecar/crosshair_summary.py +2 -2
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/validators/sidecar/frameworks/django.py +3 -6
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/validators/sidecar/frameworks/fastapi.py +3 -6
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/validators/sidecar/frameworks/flask.py +3 -6
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/validators/sidecar/models.py +2 -2
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/versioning/analyzer.py +2 -2
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/.gitignore +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/LICENSE.md +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/README.md +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/mappings/node-async.yaml +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/mappings/python-async.yaml +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/mappings/speckit-default.yaml +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/prompts/shared/cli-enforcement.md +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/prompts/specfact.01-import.md +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/prompts/specfact.02-plan.md +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/prompts/specfact.03-review.md +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/prompts/specfact.04-sdd.md +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/prompts/specfact.05-enforce.md +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/prompts/specfact.06-sync.md +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/prompts/specfact.07-contracts.md +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/prompts/specfact.backlog-daily.md +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/prompts/specfact.backlog-refine.md +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/prompts/specfact.compare.md +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/prompts/specfact.sync-backlog.md +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/prompts/specfact.validate.md +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/schemas/deviation.schema.json +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/schemas/plan.schema.json +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/schemas/protocol.schema.json +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/templates/backlog/defaults/defect_v1.yaml +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/templates/backlog/defaults/enabler_v1.yaml +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/templates/backlog/defaults/spike_v1.yaml +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/templates/backlog/defaults/user_story_v1.yaml +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/templates/backlog/field_mappings/ado_agile.yaml +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/templates/backlog/field_mappings/ado_default.yaml +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/templates/backlog/field_mappings/ado_kanban.yaml +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/templates/backlog/field_mappings/ado_safe.yaml +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/templates/backlog/field_mappings/ado_scrum.yaml +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/templates/backlog/frameworks/safe/safe_feature_v1.yaml +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/templates/backlog/frameworks/scrum/user_story_v1.yaml +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/templates/backlog/personas/developer/developer_task_v1.yaml +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/templates/backlog/personas/product-owner/user_story_v1.yaml +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/templates/backlog/providers/ado/work_item_v1.yaml +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/templates/github-action.yml.j2 +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/templates/persona/architect.md.j2 +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/templates/persona/developer.md.j2 +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/templates/persona/product-owner.md.j2 +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/templates/plan.bundle.yaml.j2 +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/templates/pr-template.md.j2 +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/templates/protocol.yaml.j2 +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/resources/templates/telemetry.yaml.example +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/__main__.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/adapters/__init__.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/adapters/ado.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/adapters/backlog_base.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/adapters/base.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/adapters/github.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/adapters/openspec.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/adapters/openspec_parser.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/adapters/registry.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/adapters/speckit.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/agents/__init__.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/agents/analyze_agent.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/agents/base.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/agents/plan_agent.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/agents/registry.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/agents/sync_agent.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/analyzers/__init__.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/analyzers/code_analyzer.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/analyzers/constitution_evidence_extractor.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/analyzers/contract_extractor.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/analyzers/control_flow_analyzer.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/analyzers/graph_analyzer.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/analyzers/relationship_mapper.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/analyzers/requirement_extractor.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/analyzers/test_pattern_extractor.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/backlog/__init__.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/backlog/adapters/__init__.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/backlog/adapters/base.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/backlog/adapters/local_yaml_adapter.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/backlog/ai_refiner.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/backlog/converter.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/backlog/filters.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/backlog/format_detector.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/backlog/formats/__init__.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/backlog/formats/base.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/backlog/formats/markdown_format.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/backlog/formats/structured_format.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/backlog/mappers/__init__.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/backlog/mappers/ado_mapper.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/backlog/mappers/base.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/backlog/mappers/github_mapper.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/backlog/mappers/template_config.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/backlog/template_detector.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/cli.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/commands/__init__.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/commands/analyze.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/commands/auth.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/commands/contract_cmd.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/commands/drift.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/commands/enforce.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/commands/generate.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/commands/import_cmd.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/commands/init.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/commands/migrate.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/commands/plan.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/commands/project_cmd.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/commands/repro.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/commands/sdd.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/commands/spec.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/commands/sync.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/commands/update.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/commands/validate.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/common/__init__.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/common/logger_setup.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/common/logging_utils.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/common/text_utils.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/common/utils.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/comparators/__init__.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/comparators/plan_comparator.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/contracts/__init__.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/contracts/crosshair_props.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/enrichers/constitution_enricher.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/enrichers/plan_enricher.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/generators/__init__.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/generators/contract_generator.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/generators/openapi_extractor.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/generators/persona_exporter.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/generators/plan_generator.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/generators/protocol_generator.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/generators/task_generator.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/generators/test_to_openapi.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/generators/workflow_generator.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/importers/__init__.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/importers/speckit_converter.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/importers/speckit_scanner.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/integrations/__init__.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/integrations/specmatic.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/merge/__init__.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/migrations/__init__.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/migrations/plan_migrator.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/models/__init__.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/models/backlog_item.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/models/capabilities.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/models/dor_config.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/models/plan.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/models/protocol.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/models/quality.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/models/sdd.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/models/source_tracking.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/modes/__init__.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/modes/router.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/parsers/__init__.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/parsers/persona_importer.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/resources/semgrep/async.yml +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/resources/semgrep/code-quality.yml +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/resources/semgrep/feature-detection.yml +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/sync/__init__.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/sync/bridge_probe.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/sync/bridge_sync.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/sync/bridge_watch.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/sync/change_detector.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/sync/code_to_spec.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/sync/drift_detector.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/sync/repository_sync.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/sync/spec_to_code.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/sync/spec_to_tests.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/sync/watcher.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/sync/watcher_enhanced.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/telemetry.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/templates/__init__.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/templates/bridge_templates.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/templates/defaults/defect_v1.yaml +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/templates/defaults/enabler_v1.yaml +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/templates/defaults/spike_v1.yaml +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/templates/defaults/user_story_v1.yaml +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/templates/frameworks/scrum/user_story_v1.yaml +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/templates/personas/product-owner/user_story_v1.yaml +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/templates/providers/ado/work_item_v1.yaml +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/templates/registry.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/templates/specification_templates.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/__init__.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/acceptance_criteria.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/auth_tokens.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/bundle_loader.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/code_change_detector.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/console.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/content_sanitizer.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/context_detection.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/enrichment_context.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/enrichment_parser.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/feature_keys.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/git.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/github_annotations.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/ide_setup.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/incremental_check.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/metadata.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/optional_deps.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/performance.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/progress.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/progressive_disclosure.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/prompts.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/sdd_discovery.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/source_scanner.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/startup_checks.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/structure.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/suggestions.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/terminal.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/utils/yaml_utils.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/validators/__init__.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/validators/agile_validation.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/validators/change_proposal_integration.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/validators/cli_first_validator.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/validators/contract_validator.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/validators/fsm.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/validators/repro_checker.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/validators/schema.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/validators/sidecar/__init__.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/validators/sidecar/contract_populator.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/validators/sidecar/crosshair_runner.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/validators/sidecar/dependency_installer.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/validators/sidecar/framework_detector.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/validators/sidecar/frameworks/__init__.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/validators/sidecar/frameworks/base.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/validators/sidecar/frameworks/drf.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/validators/sidecar/harness_generator.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/validators/sidecar/orchestrator.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/validators/sidecar/specmatic_runner.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/validators/sidecar/unannotated_detector.py +0 -0
- {specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/versioning/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: specfact-cli
|
|
3
|
-
Version: 0.26.
|
|
3
|
+
Version: 0.26.17
|
|
4
4
|
Summary: Brownfield-first CLI: Reverse engineer legacy Python → specs → enforced contracts. Automate legacy code documentation and prevent modernization regressions.
|
|
5
5
|
Project-URL: Homepage, https://github.com/nold-ai/specfact-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/nold-ai/specfact-cli.git
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "specfact-cli"
|
|
7
|
-
version = "0.26.
|
|
7
|
+
version = "0.26.17"
|
|
8
8
|
description = "Brownfield-first CLI: Reverse engineer legacy Python → specs → enforced contracts. Automate legacy code documentation and prevent modernization regressions."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
{specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/analyzers/ambiguity_scanner.py
RENAMED
|
@@ -10,7 +10,7 @@ from __future__ import annotations
|
|
|
10
10
|
import ast
|
|
11
11
|
import re
|
|
12
12
|
from dataclasses import dataclass
|
|
13
|
-
from enum import
|
|
13
|
+
from enum import StrEnum
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
|
|
16
16
|
from beartype import beartype
|
|
@@ -19,7 +19,7 @@ from icontract import ensure, require
|
|
|
19
19
|
from specfact_cli.models.plan import PlanBundle
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
class AmbiguityStatus(
|
|
22
|
+
class AmbiguityStatus(StrEnum):
|
|
23
23
|
"""Ambiguity status levels."""
|
|
24
24
|
|
|
25
25
|
CLEAR = "Clear"
|
|
@@ -27,7 +27,7 @@ class AmbiguityStatus(str, Enum):
|
|
|
27
27
|
MISSING = "Missing"
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
class TaxonomyCategory(
|
|
30
|
+
class TaxonomyCategory(StrEnum):
|
|
31
31
|
"""Taxonomy categories for ambiguity detection."""
|
|
32
32
|
|
|
33
33
|
FUNCTIONAL_SCOPE = "Functional Scope & Behavior"
|
|
@@ -910,12 +910,8 @@ class AmbiguityScanner:
|
|
|
910
910
|
# Look for role/permission constants (e.g., ADMIN = "admin")
|
|
911
911
|
if (
|
|
912
912
|
"role" in attr_name or "permission" in attr_name
|
|
913
|
-
) and isinstance(item.value,
|
|
914
|
-
role_value =
|
|
915
|
-
item.value.s
|
|
916
|
-
if isinstance(item.value, ast.Str)
|
|
917
|
-
else item.value
|
|
918
|
-
)
|
|
913
|
+
) and isinstance(item.value, ast.Constant):
|
|
914
|
+
role_value = item.value.value
|
|
919
915
|
if isinstance(role_value, str) and len(role_value) > 2:
|
|
920
916
|
role_clean = role_value.strip().lower()
|
|
921
917
|
if (
|
|
@@ -168,8 +168,8 @@ def _load_standup_config() -> dict[str, Any]:
|
|
|
168
168
|
with open(path, encoding="utf-8") as f:
|
|
169
169
|
data = yaml.safe_load(f) or {}
|
|
170
170
|
config = dict(data.get("standup", data))
|
|
171
|
-
except Exception:
|
|
172
|
-
|
|
171
|
+
except Exception as exc:
|
|
172
|
+
debug_log_operation("config_load", str(path), "error", error=repr(exc))
|
|
173
173
|
break
|
|
174
174
|
if os.environ.get("SPECFACT_STANDUP_STATE"):
|
|
175
175
|
config["default_state"] = os.environ["SPECFACT_STANDUP_STATE"]
|
|
@@ -203,8 +203,8 @@ def _load_backlog_config() -> dict[str, Any]:
|
|
|
203
203
|
config = dict(nested) if isinstance(nested, dict) else {}
|
|
204
204
|
else:
|
|
205
205
|
config = dict(data) if isinstance(data, dict) else {}
|
|
206
|
-
except Exception:
|
|
207
|
-
|
|
206
|
+
except Exception as exc:
|
|
207
|
+
debug_log_operation("config_load", str(path), "error", error=repr(exc))
|
|
208
208
|
break
|
|
209
209
|
return config
|
|
210
210
|
|
|
@@ -378,20 +378,66 @@ def _format_daily_item_detail(item: BacklogItem, comments: list[str]) -> str:
|
|
|
378
378
|
return "\n".join(parts)
|
|
379
379
|
|
|
380
380
|
|
|
381
|
+
def _collect_comment_annotations(
|
|
382
|
+
adapter: str,
|
|
383
|
+
items: list[BacklogItem],
|
|
384
|
+
*,
|
|
385
|
+
repo_owner: str | None,
|
|
386
|
+
repo_name: str | None,
|
|
387
|
+
github_token: str | None,
|
|
388
|
+
ado_org: str | None,
|
|
389
|
+
ado_project: str | None,
|
|
390
|
+
ado_token: str | None,
|
|
391
|
+
) -> dict[str, list[str]]:
|
|
392
|
+
"""
|
|
393
|
+
Collect comment annotations for backlog items when the adapter supports get_comments().
|
|
394
|
+
|
|
395
|
+
Returns a mapping of item ID -> list of comment strings. Returns empty dict if not supported.
|
|
396
|
+
"""
|
|
397
|
+
comments_by_item_id: dict[str, list[str]] = {}
|
|
398
|
+
try:
|
|
399
|
+
adapter_kwargs = _build_adapter_kwargs(
|
|
400
|
+
adapter,
|
|
401
|
+
repo_owner=repo_owner,
|
|
402
|
+
repo_name=repo_name,
|
|
403
|
+
github_token=github_token,
|
|
404
|
+
ado_org=ado_org,
|
|
405
|
+
ado_project=ado_project,
|
|
406
|
+
ado_token=ado_token,
|
|
407
|
+
)
|
|
408
|
+
registry = AdapterRegistry()
|
|
409
|
+
adapter_instance = registry.get_adapter(adapter, **adapter_kwargs)
|
|
410
|
+
if not isinstance(adapter_instance, BacklogAdapter):
|
|
411
|
+
return comments_by_item_id
|
|
412
|
+
get_comments_fn = getattr(adapter_instance, "get_comments", None)
|
|
413
|
+
if not callable(get_comments_fn):
|
|
414
|
+
return comments_by_item_id
|
|
415
|
+
for item in items:
|
|
416
|
+
with contextlib.suppress(Exception):
|
|
417
|
+
raw = get_comments_fn(item)
|
|
418
|
+
comments_by_item_id[item.id] = list(raw) if isinstance(raw, list) else []
|
|
419
|
+
except Exception:
|
|
420
|
+
return comments_by_item_id
|
|
421
|
+
return comments_by_item_id
|
|
422
|
+
|
|
423
|
+
|
|
381
424
|
@beartype
|
|
382
425
|
def _build_copilot_export_content(
|
|
383
426
|
items: list[BacklogItem],
|
|
384
427
|
include_value_score: bool = False,
|
|
428
|
+
include_comments: bool = False,
|
|
429
|
+
comments_by_item_id: dict[str, list[str]] | None = None,
|
|
385
430
|
) -> str:
|
|
386
431
|
"""
|
|
387
432
|
Build Markdown content for Copilot export: one section per item.
|
|
388
433
|
|
|
389
434
|
Per item: ID, title, status, assignees, last updated, progress summary (standup fields),
|
|
390
|
-
blockers, and optionally
|
|
435
|
+
blockers, optional value score, and optionally description/comments when enabled.
|
|
391
436
|
"""
|
|
392
437
|
lines: list[str] = []
|
|
393
438
|
lines.append("# Daily standup – Copilot export")
|
|
394
439
|
lines.append("")
|
|
440
|
+
comments_map = comments_by_item_id or {}
|
|
395
441
|
for item in items:
|
|
396
442
|
lines.append(f"## {item.id} - {item.title}")
|
|
397
443
|
lines.append("")
|
|
@@ -402,11 +448,26 @@ def _build_copilot_export_content(
|
|
|
402
448
|
item.updated_at.strftime("%Y-%m-%d %H:%M") if hasattr(item.updated_at, "strftime") else str(item.updated_at)
|
|
403
449
|
)
|
|
404
450
|
lines.append(f"- **Last updated:** {updated}")
|
|
451
|
+
if include_comments:
|
|
452
|
+
body = (item.body_markdown or "").strip()
|
|
453
|
+
if body:
|
|
454
|
+
snippet = body[:_SUMMARIZE_BODY_TRUNCATE]
|
|
455
|
+
if len(body) > _SUMMARIZE_BODY_TRUNCATE:
|
|
456
|
+
snippet += "\n..."
|
|
457
|
+
lines.append("- **Description:**")
|
|
458
|
+
for line in snippet.splitlines():
|
|
459
|
+
lines.append(f" {line}" if line else " ")
|
|
405
460
|
yesterday, today, blockers = _parse_standup_from_body(item.body_markdown or "")
|
|
406
461
|
if yesterday or today:
|
|
407
462
|
lines.append(f"- **Progress:** Yesterday: {yesterday or '—'}; Today: {today or '—'}")
|
|
408
463
|
if blockers:
|
|
409
464
|
lines.append(f"- **Blockers:** {blockers}")
|
|
465
|
+
if include_comments:
|
|
466
|
+
item_comments = comments_map.get(item.id, [])
|
|
467
|
+
if item_comments:
|
|
468
|
+
lines.append("- **Comments (annotations):**")
|
|
469
|
+
for c in item_comments:
|
|
470
|
+
lines.append(f" - {c}")
|
|
410
471
|
if item.story_points is not None:
|
|
411
472
|
lines.append(f"- **Story points:** {item.story_points}")
|
|
412
473
|
if item.priority is not None:
|
|
@@ -428,19 +489,25 @@ def _build_summarize_prompt_content(
|
|
|
428
489
|
filter_context: dict[str, Any],
|
|
429
490
|
include_value_score: bool = False,
|
|
430
491
|
comments_by_item_id: dict[str, list[str]] | None = None,
|
|
492
|
+
include_comments: bool = False,
|
|
431
493
|
) -> str:
|
|
432
494
|
"""
|
|
433
495
|
Build prompt content for standup summary: instruction + filter context + per-item data.
|
|
434
496
|
|
|
435
|
-
|
|
436
|
-
a meaningful summary.
|
|
497
|
+
When include_comments is True, includes body (description) and annotations (comments) per item
|
|
498
|
+
so an LLM can produce a meaningful summary. When False, only metadata (id, title, status,
|
|
499
|
+
assignees, last updated) is included to avoid leaking sensitive or large context.
|
|
500
|
+
For use with slash command (e.g. specfact.daily) or copy-paste to Copilot.
|
|
437
501
|
"""
|
|
438
502
|
lines: list[str] = []
|
|
439
503
|
lines.append("--- BEGIN STANDUP PROMPT ---")
|
|
440
504
|
lines.append("Generate a concise daily standup summary from the following data.")
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
505
|
+
if include_comments:
|
|
506
|
+
lines.append(
|
|
507
|
+
"Include: current focus, blockers, and pending items. Use each item's description and comments for context. Keep it short and actionable."
|
|
508
|
+
)
|
|
509
|
+
else:
|
|
510
|
+
lines.append("Include: current focus and pending items from the metadata below. Keep it short and actionable.")
|
|
444
511
|
lines.append("")
|
|
445
512
|
lines.append("## Filter context")
|
|
446
513
|
lines.append(f"- Adapter: {filter_context.get('adapter', '—')}")
|
|
@@ -449,7 +516,8 @@ def _build_summarize_prompt_content(
|
|
|
449
516
|
lines.append(f"- Assignee: {filter_context.get('assignee', '—')}")
|
|
450
517
|
lines.append(f"- Limit: {filter_context.get('limit', '—')}")
|
|
451
518
|
lines.append("")
|
|
452
|
-
|
|
519
|
+
data_header = "Standup data (with description and comments)" if include_comments else "Standup data (metadata only)"
|
|
520
|
+
lines.append(f"## {data_header}")
|
|
453
521
|
lines.append("")
|
|
454
522
|
comments_map = comments_by_item_id or {}
|
|
455
523
|
for item in items:
|
|
@@ -462,24 +530,25 @@ def _build_summarize_prompt_content(
|
|
|
462
530
|
item.updated_at.strftime("%Y-%m-%d %H:%M") if hasattr(item.updated_at, "strftime") else str(item.updated_at)
|
|
463
531
|
)
|
|
464
532
|
lines.append(f"- **Last updated:** {updated}")
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
533
|
+
if include_comments:
|
|
534
|
+
body = (item.body_markdown or "").strip()
|
|
535
|
+
if body:
|
|
536
|
+
snippet = body[:_SUMMARIZE_BODY_TRUNCATE]
|
|
537
|
+
if len(body) > _SUMMARIZE_BODY_TRUNCATE:
|
|
538
|
+
snippet += "\n..."
|
|
539
|
+
lines.append("- **Description:**")
|
|
540
|
+
lines.append(snippet)
|
|
541
|
+
lines.append("")
|
|
542
|
+
yesterday, today, blockers = _parse_standup_from_body(item.body_markdown or "")
|
|
543
|
+
if yesterday or today:
|
|
544
|
+
lines.append(f"- **Progress:** Yesterday: {yesterday or '—'}; Today: {today or '—'}")
|
|
545
|
+
if blockers:
|
|
546
|
+
lines.append(f"- **Blockers:** {blockers}")
|
|
547
|
+
item_comments = comments_map.get(item.id, [])
|
|
548
|
+
if item_comments:
|
|
549
|
+
lines.append("- **Comments (annotations):**")
|
|
550
|
+
for c in item_comments:
|
|
551
|
+
lines.append(f" - {c}")
|
|
483
552
|
if item.story_points is not None:
|
|
484
553
|
lines.append(f"- **Story points:** {item.story_points}")
|
|
485
554
|
if item.priority is not None:
|
|
@@ -559,7 +628,7 @@ def _run_interactive_daily(
|
|
|
559
628
|
console.print(Panel(detail, title=f"Story: {item.id}", border_style="cyan"))
|
|
560
629
|
|
|
561
630
|
if suggest_next and n > 1:
|
|
562
|
-
pending = [i for i in items if not i.assignees or
|
|
631
|
+
pending = [i for i in items if not i.assignees or i.story_points is not None]
|
|
563
632
|
if pending:
|
|
564
633
|
best: BacklogItem | None = None
|
|
565
634
|
best_score: float = -1.0
|
|
@@ -1030,6 +1099,12 @@ def daily(
|
|
|
1030
1099
|
"--copilot-export",
|
|
1031
1100
|
help="Write summarized progress per story to a file for Copilot slash-command use during standup.",
|
|
1032
1101
|
),
|
|
1102
|
+
include_comments: bool = typer.Option(
|
|
1103
|
+
False,
|
|
1104
|
+
"--comments",
|
|
1105
|
+
"--annotations",
|
|
1106
|
+
help="Include item comments/annotations in summarize/copilot export (adapter must support get_comments).",
|
|
1107
|
+
),
|
|
1033
1108
|
summarize: bool = typer.Option(
|
|
1034
1109
|
False,
|
|
1035
1110
|
"--summarize",
|
|
@@ -1118,10 +1193,28 @@ def daily(
|
|
|
1118
1193
|
console.print("[yellow]No backlog items found.[/yellow]")
|
|
1119
1194
|
return
|
|
1120
1195
|
|
|
1196
|
+
comments_by_item_id: dict[str, list[str]] = {}
|
|
1197
|
+
if include_comments and (copilot_export is not None or summarize or summarize_to is not None):
|
|
1198
|
+
comments_by_item_id = _collect_comment_annotations(
|
|
1199
|
+
adapter,
|
|
1200
|
+
filtered,
|
|
1201
|
+
repo_owner=repo_owner,
|
|
1202
|
+
repo_name=repo_name,
|
|
1203
|
+
github_token=github_token,
|
|
1204
|
+
ado_org=ado_org,
|
|
1205
|
+
ado_project=ado_project,
|
|
1206
|
+
ado_token=ado_token,
|
|
1207
|
+
)
|
|
1208
|
+
|
|
1121
1209
|
if copilot_export is not None:
|
|
1122
1210
|
include_score = suggest_next or bool(standup_config.get("suggest_next"))
|
|
1123
1211
|
export_path = Path(copilot_export)
|
|
1124
|
-
content = _build_copilot_export_content(
|
|
1212
|
+
content = _build_copilot_export_content(
|
|
1213
|
+
filtered,
|
|
1214
|
+
include_value_score=include_score,
|
|
1215
|
+
include_comments=include_comments,
|
|
1216
|
+
comments_by_item_id=comments_by_item_id or None,
|
|
1217
|
+
)
|
|
1125
1218
|
export_path.write_text(content, encoding="utf-8")
|
|
1126
1219
|
console.print(f"[dim]Exported {len(filtered)} item(s) to {export_path}[/dim]")
|
|
1127
1220
|
|
|
@@ -1134,32 +1227,12 @@ def daily(
|
|
|
1134
1227
|
"assignee": effective_assignee or "—",
|
|
1135
1228
|
"limit": effective_limit,
|
|
1136
1229
|
}
|
|
1137
|
-
comments_by_item_id: dict[str, list[str]] = {}
|
|
1138
|
-
try:
|
|
1139
|
-
adapter_kwargs_sum = _build_adapter_kwargs(
|
|
1140
|
-
adapter,
|
|
1141
|
-
repo_owner=repo_owner,
|
|
1142
|
-
repo_name=repo_name,
|
|
1143
|
-
github_token=github_token,
|
|
1144
|
-
ado_org=ado_org,
|
|
1145
|
-
ado_project=ado_project,
|
|
1146
|
-
ado_token=ado_token,
|
|
1147
|
-
)
|
|
1148
|
-
registry_sum = AdapterRegistry()
|
|
1149
|
-
adapter_instance_sum = registry_sum.get_adapter(adapter, **adapter_kwargs_sum)
|
|
1150
|
-
get_comments_fn = getattr(adapter_instance_sum, "get_comments", None)
|
|
1151
|
-
if callable(get_comments_fn):
|
|
1152
|
-
for it in filtered:
|
|
1153
|
-
with contextlib.suppress(Exception):
|
|
1154
|
-
raw = get_comments_fn(it)
|
|
1155
|
-
comments_by_item_id[it.id] = list(raw) if isinstance(raw, list) else []
|
|
1156
|
-
except Exception:
|
|
1157
|
-
pass
|
|
1158
1230
|
content = _build_summarize_prompt_content(
|
|
1159
1231
|
filtered,
|
|
1160
1232
|
filter_context=filter_ctx,
|
|
1161
1233
|
include_value_score=include_score,
|
|
1162
1234
|
comments_by_item_id=comments_by_item_id or None,
|
|
1235
|
+
include_comments=include_comments,
|
|
1163
1236
|
)
|
|
1164
1237
|
if summarize_to:
|
|
1165
1238
|
Path(summarize_to).write_text(content, encoding="utf-8")
|
|
@@ -1245,7 +1318,7 @@ def daily(
|
|
|
1245
1318
|
end_date = dt.strptime(str(sprint_end)[:10], "%Y-%m-%d").date()
|
|
1246
1319
|
console.print(f"[dim]{_format_sprint_end_header(end_date)}[/dim]")
|
|
1247
1320
|
except (ValueError, TypeError):
|
|
1248
|
-
|
|
1321
|
+
console.print("[dim]Sprint end date could not be parsed; header skipped.[/dim]")
|
|
1249
1322
|
|
|
1250
1323
|
def _add_standup_rows_to_table(tbl: Table, row_list: list[dict[str, Any]], include_pri: bool) -> None:
|
|
1251
1324
|
for r in row_list:
|
{specfact_cli-0.26.16 → specfact_cli-0.26.17}/src/specfact_cli/generators/report_generator.py
RENAMED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
|
-
from enum import
|
|
6
|
+
from enum import StrEnum
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
|
|
9
9
|
from beartype import beartype
|
|
@@ -14,7 +14,7 @@ from specfact_cli.models.deviation import Deviation, DeviationReport, Validation
|
|
|
14
14
|
from specfact_cli.utils.structured_io import StructuredFormat, dump_structured_file
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
class ReportFormat(
|
|
17
|
+
class ReportFormat(StrEnum):
|
|
18
18
|
"""Report output format."""
|
|
19
19
|
|
|
20
20
|
MARKDOWN = "markdown"
|
|
@@ -8,7 +8,7 @@ enabling automatic conflict resolution based on section ownership.
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
10
|
from dataclasses import dataclass
|
|
11
|
-
from enum import
|
|
11
|
+
from enum import StrEnum
|
|
12
12
|
from typing import Any
|
|
13
13
|
|
|
14
14
|
from beartype import beartype
|
|
@@ -17,7 +17,7 @@ from icontract import ensure, require
|
|
|
17
17
|
from specfact_cli.models.project import BundleManifest, ProjectBundle
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
class MergeStrategy(
|
|
20
|
+
class MergeStrategy(StrEnum):
|
|
21
21
|
"""Merge resolution strategy."""
|
|
22
22
|
|
|
23
23
|
AUTO = "auto" # Automatic resolution based on persona ownership
|
|
@@ -102,7 +102,7 @@ class PersonaMergeResolver:
|
|
|
102
102
|
if self._sections_disjoint(ours, theirs):
|
|
103
103
|
# No conflicts - merge all changes
|
|
104
104
|
merged = self._merge_sections(base, ours, theirs)
|
|
105
|
-
return MergeResolution(
|
|
105
|
+
return MergeResolution(merged, [], 0, 0, 0)
|
|
106
106
|
|
|
107
107
|
# Rule 2: Find conflicts and resolve based on persona ownership
|
|
108
108
|
field_conflicts = self._find_conflicts(base, ours, theirs)
|
|
@@ -161,11 +161,11 @@ class PersonaMergeResolver:
|
|
|
161
161
|
conflicts.append(conflict)
|
|
162
162
|
|
|
163
163
|
return MergeResolution(
|
|
164
|
-
|
|
165
|
-
conflicts
|
|
166
|
-
auto_resolved
|
|
167
|
-
manual_resolved
|
|
168
|
-
unresolved
|
|
164
|
+
merged,
|
|
165
|
+
conflicts,
|
|
166
|
+
auto_resolved,
|
|
167
|
+
manual_resolved,
|
|
168
|
+
unresolved,
|
|
169
169
|
)
|
|
170
170
|
|
|
171
171
|
@beartype
|
|
@@ -9,7 +9,7 @@ future tool integrations using the same interface pattern.
|
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
|
-
from enum import
|
|
12
|
+
from enum import StrEnum
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
|
|
15
15
|
from beartype import beartype
|
|
@@ -19,7 +19,7 @@ from pydantic import BaseModel, Field
|
|
|
19
19
|
from specfact_cli.utils.structured_io import StructuredFormat, dump_structured_file, load_structured_file
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
class AdapterType(
|
|
22
|
+
class AdapterType(StrEnum):
|
|
23
23
|
"""Supported adapter types."""
|
|
24
24
|
|
|
25
25
|
SPECKIT = "speckit"
|
|
@@ -12,7 +12,7 @@ ensuring they remain adapter-agnostic.
|
|
|
12
12
|
|
|
13
13
|
from __future__ import annotations
|
|
14
14
|
|
|
15
|
-
from enum import
|
|
15
|
+
from enum import StrEnum
|
|
16
16
|
from typing import Any
|
|
17
17
|
|
|
18
18
|
from icontract import ensure, require
|
|
@@ -22,7 +22,7 @@ from specfact_cli.models.plan import Feature
|
|
|
22
22
|
from specfact_cli.models.source_tracking import SourceTracking
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
class ChangeType(
|
|
25
|
+
class ChangeType(StrEnum):
|
|
26
26
|
"""Change type for delta specs (tool-agnostic)."""
|
|
27
27
|
|
|
28
28
|
ADDED = "added"
|
|
@@ -17,7 +17,7 @@ stored in bundle-specific contracts/ directories and linked to features.
|
|
|
17
17
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
|
-
from enum import
|
|
20
|
+
from enum import StrEnum
|
|
21
21
|
from pathlib import Path
|
|
22
22
|
from typing import Any
|
|
23
23
|
|
|
@@ -26,7 +26,7 @@ from icontract import ensure, require
|
|
|
26
26
|
from pydantic import BaseModel, Field
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
class ContractStatus(
|
|
29
|
+
class ContractStatus(StrEnum):
|
|
30
30
|
"""Contract status levels."""
|
|
31
31
|
|
|
32
32
|
DRAFT = "draft" # Initial contract, not validated
|
|
@@ -7,14 +7,14 @@ protocols, and actual implementation following the CLI-First specification.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
-
from enum import
|
|
10
|
+
from enum import StrEnum
|
|
11
11
|
|
|
12
12
|
from beartype import beartype
|
|
13
13
|
from icontract import ensure, require
|
|
14
14
|
from pydantic import BaseModel, Field
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
class DeviationSeverity(
|
|
17
|
+
class DeviationSeverity(StrEnum):
|
|
18
18
|
"""Deviation severity level."""
|
|
19
19
|
|
|
20
20
|
HIGH = "HIGH"
|
|
@@ -22,7 +22,7 @@ class DeviationSeverity(str, Enum):
|
|
|
22
22
|
LOW = "LOW"
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
class DeviationType(
|
|
25
|
+
class DeviationType(StrEnum):
|
|
26
26
|
"""Type of deviation."""
|
|
27
27
|
|
|
28
28
|
MISSING_FEATURE = "missing_feature"
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"""Enforcement configuration models for quality gates."""
|
|
2
2
|
|
|
3
|
-
from enum import
|
|
3
|
+
from enum import StrEnum
|
|
4
4
|
|
|
5
5
|
from beartype import beartype
|
|
6
6
|
from icontract import ensure, require
|
|
7
7
|
from pydantic import BaseModel, Field
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
class EnforcementAction(
|
|
10
|
+
class EnforcementAction(StrEnum):
|
|
11
11
|
"""Actions that can be taken when a deviation is detected."""
|
|
12
12
|
|
|
13
13
|
BLOCK = "BLOCK" # Fail the validation (exit code 1)
|
|
@@ -15,7 +15,7 @@ class EnforcementAction(str, Enum):
|
|
|
15
15
|
LOG = "LOG" # Only log, no warning (exit code 0)
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
class EnforcementPreset(
|
|
18
|
+
class EnforcementPreset(StrEnum):
|
|
19
19
|
"""Predefined enforcement presets."""
|
|
20
20
|
|
|
21
21
|
MINIMAL = "minimal" # Log everything, never block
|
|
@@ -8,7 +8,7 @@ for persona-specific Markdown artifacts.
|
|
|
8
8
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
|
-
from enum import
|
|
11
|
+
from enum import StrEnum
|
|
12
12
|
from typing import Any
|
|
13
13
|
|
|
14
14
|
from beartype import beartype
|
|
@@ -16,7 +16,7 @@ from icontract import ensure, require
|
|
|
16
16
|
from pydantic import BaseModel, Field
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
class SectionType(
|
|
19
|
+
class SectionType(StrEnum):
|
|
20
20
|
"""Section type classification."""
|
|
21
21
|
|
|
22
22
|
REQUIRED = "required" # Must be present in export/import
|
|
@@ -13,7 +13,7 @@ import os
|
|
|
13
13
|
from collections.abc import Callable
|
|
14
14
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
15
15
|
from datetime import UTC, datetime
|
|
16
|
-
from enum import
|
|
16
|
+
from enum import StrEnum
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
from typing import Any
|
|
19
19
|
|
|
@@ -33,7 +33,7 @@ from specfact_cli.models.plan import (
|
|
|
33
33
|
)
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
class BundleFormat(
|
|
36
|
+
class BundleFormat(StrEnum):
|
|
37
37
|
"""Bundle format types."""
|
|
38
38
|
|
|
39
39
|
MONOLITHIC = "monolithic" # Single file with all aspects
|
|
@@ -7,14 +7,14 @@ plan bundles and SDD manifests.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
-
from enum import
|
|
10
|
+
from enum import StrEnum
|
|
11
11
|
|
|
12
12
|
from beartype import beartype
|
|
13
13
|
from icontract import ensure, require
|
|
14
14
|
from pydantic import BaseModel, Field
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
class TaskPhase(
|
|
17
|
+
class TaskPhase(StrEnum):
|
|
18
18
|
"""Task execution phases."""
|
|
19
19
|
|
|
20
20
|
SETUP = "setup" # Project structure, dependencies, config
|
|
@@ -23,7 +23,7 @@ class TaskPhase(str, Enum):
|
|
|
23
23
|
POLISH = "polish" # Tests, docs, optimization
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
class TaskStatus(
|
|
26
|
+
class TaskStatus(StrEnum):
|
|
27
27
|
"""Task completion status."""
|
|
28
28
|
|
|
29
29
|
PENDING = "pending"
|
|
@@ -8,13 +8,13 @@ based on environment and explicit flags.
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
10
|
import os
|
|
11
|
-
from enum import
|
|
11
|
+
from enum import StrEnum
|
|
12
12
|
|
|
13
13
|
from beartype import beartype
|
|
14
14
|
from icontract import ensure, require
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
class OperationalMode(
|
|
17
|
+
class OperationalMode(StrEnum):
|
|
18
18
|
"""Operational modes for SpecFact CLI."""
|
|
19
19
|
|
|
20
20
|
CICD = "cicd"
|
|
@@ -12,7 +12,7 @@ import json
|
|
|
12
12
|
import logging
|
|
13
13
|
import os
|
|
14
14
|
import sys
|
|
15
|
-
from enum import
|
|
15
|
+
from enum import StrEnum
|
|
16
16
|
from logging.handlers import RotatingFileHandler
|
|
17
17
|
from typing import Any
|
|
18
18
|
|
|
@@ -34,7 +34,7 @@ DEBUG_LOG_DATEFMT = "%Y-%m-%d %H:%M:%S"
|
|
|
34
34
|
DEBUG_LOG_FORMAT = "%(asctime)s | %(message)s"
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
class TerminalMode(
|
|
37
|
+
class TerminalMode(StrEnum):
|
|
38
38
|
"""Terminal output modes for Rich Console and Progress."""
|
|
39
39
|
|
|
40
40
|
GRAPHICAL = "graphical" # Full Rich features (colors, animations)
|
|
@@ -10,14 +10,14 @@ from __future__ import annotations
|
|
|
10
10
|
|
|
11
11
|
import shutil
|
|
12
12
|
from dataclasses import dataclass
|
|
13
|
-
from enum import
|
|
13
|
+
from enum import StrEnum
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
|
|
16
16
|
from beartype import beartype
|
|
17
17
|
from icontract import ensure, require
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
class EnvManager(
|
|
20
|
+
class EnvManager(StrEnum):
|
|
21
21
|
"""Python environment manager types."""
|
|
22
22
|
|
|
23
23
|
HATCH = "hatch"
|