specfact-cli 0.42.3__tar.gz → 0.42.4__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.42.3 → specfact_cli-0.42.4}/PKG-INFO +3 -1
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/README.md +2 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/pyproject.toml +1 -1
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/__init__.py +1 -1
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/__init__.py +1 -1
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/adapters/ado.py +16 -3
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/analyzers/code_analyzer.py +37 -24
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/modules/init/module-package.yaml +3 -3
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/modules/init/src/commands.py +96 -59
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/registry/module_packages.py +8 -1
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/runtime.py +2 -1
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/ide_setup.py +210 -51
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/terminal.py +40 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/.gitignore +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/LICENSE +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/keys/README.md +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/keys/module-signing-public.pem +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/mappings/node-async.yaml +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/mappings/python-async.yaml +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/mappings/speckit-default.yaml +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/prompts/shared/cli-enforcement.md +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/prompts/specfact.01-import.md +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/prompts/specfact.02-plan.md +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/prompts/specfact.03-review.md +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/prompts/specfact.04-sdd.md +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/prompts/specfact.05-enforce.md +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/prompts/specfact.06-sync.md +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/prompts/specfact.07-contracts.md +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/prompts/specfact.compare.md +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/prompts/specfact.validate.md +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/schemas/deviation.schema.json +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/schemas/plan.schema.json +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/schemas/protocol.schema.json +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/templates/github-action.yml.j2 +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/templates/persona/architect.md.j2 +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/templates/persona/developer.md.j2 +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/templates/persona/product-owner.md.j2 +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/templates/plan.bundle.yaml.j2 +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/templates/policies/kanban.yaml +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/templates/policies/mixed.yaml +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/templates/policies/safe.yaml +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/templates/policies/scrum.yaml +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/templates/pr-template.md.j2 +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/templates/protocol.yaml.j2 +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/resources/templates/telemetry.yaml.example +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/__main__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/adapters/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/adapters/backlog_base.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/adapters/base.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/adapters/github.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/adapters/openspec.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/adapters/openspec_parser.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/adapters/registry.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/adapters/speckit.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/agents/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/agents/analyze_agent.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/agents/base.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/agents/plan_agent.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/agents/registry.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/agents/sync_agent.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/analyzers/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/analyzers/ambiguity_scanner.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/analyzers/constitution_evidence_extractor.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/analyzers/contract_extractor.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/analyzers/control_flow_analyzer.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/analyzers/graph_analyzer.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/analyzers/relationship_mapper.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/analyzers/requirement_extractor.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/analyzers/test_pattern_extractor.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/backlog/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/backlog/adapters/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/backlog/adapters/base.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/backlog/converter.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/backlog/filters.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/backlog/mappers/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/backlog/mappers/ado_mapper.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/backlog/mappers/base.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/backlog/mappers/github_mapper.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/backlog/mappers/template_config.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/cli.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/commands/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/commands/_bundle_shim.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/commands/analyze.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/commands/contract_cmd.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/commands/drift.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/commands/enforce.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/commands/generate.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/commands/import_cmd.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/commands/init.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/commands/migrate.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/commands/plan.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/commands/project_cmd.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/commands/repro.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/commands/sdd.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/commands/spec.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/commands/sync.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/commands/update.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/commands/validate.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/common/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/common/bundle_factory.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/common/logger_setup.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/common/logging_utils.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/common/text_utils.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/common/utils.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/comparators/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/comparators/plan_comparator.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/contracts/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/contracts/crosshair_props.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/contracts/module_interface.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/enrichers/constitution_enricher.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/enrichers/plan_enricher.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/generators/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/generators/contract_generator.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/generators/openapi_extractor.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/generators/persona_exporter.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/generators/plan_generator.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/generators/protocol_generator.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/generators/report_generator.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/generators/task_generator.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/generators/test_to_openapi.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/generators/workflow_generator.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/groups/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/groups/codebase_group.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/groups/govern_group.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/groups/member_group.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/groups/project_group.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/groups/spec_group.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/importers/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/importers/speckit_converter.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/importers/speckit_scanner.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/integrations/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/integrations/specmatic.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/merge/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/merge/resolver.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/migrations/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/migrations/plan_migrator.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/models/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/models/backlog_item.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/models/bridge.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/models/capabilities.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/models/change.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/models/contract.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/models/deviation.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/models/dor_config.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/models/enforcement.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/models/module_package.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/models/persona_template.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/models/plan.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/models/project.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/models/protocol.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/models/quality.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/models/sdd.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/models/source_tracking.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/models/task.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/models/validation.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/modes/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/modes/detector.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/modes/router.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/modules/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/modules/_bundle_import.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/modules/init/src/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/modules/init/src/app.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/modules/init/src/first_run_selection.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/modules/module_io_shim.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/modules/module_registry/module-package.yaml +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/modules/module_registry/src/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/modules/module_registry/src/app.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/modules/module_registry/src/commands.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/modules/upgrade/module-package.yaml +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/modules/upgrade/src/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/modules/upgrade/src/app.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/modules/upgrade/src/commands.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/parsers/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/parsers/persona_importer.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/registry/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/registry/alias_manager.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/registry/bootstrap.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/registry/bridge_registry.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/registry/crypto_validator.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/registry/custom_registries.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/registry/dependency_resolver.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/registry/extension_registry.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/registry/help_cache.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/registry/marketplace_client.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/registry/metadata.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/registry/module_discovery.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/registry/module_grouping.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/registry/module_installer.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/registry/module_lifecycle.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/registry/module_security.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/registry/module_state.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/registry/registry.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/resources/semgrep/async.yml +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/resources/semgrep/code-quality.yml +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/resources/semgrep/feature-detection.yml +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/sync/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/sync/bridge_probe.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/sync/bridge_sync.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/sync/bridge_sync_openspec_md_parse.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/sync/bridge_sync_requirement_from_proposal.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/sync/bridge_sync_requirement_helpers.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/sync/bridge_sync_tasks_from_proposal.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/sync/bridge_sync_what_changes_format.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/sync/bridge_sync_write_openspec_from_proposal.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/sync/bridge_watch.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/sync/change_detector.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/sync/code_to_spec.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/sync/drift_detector.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/sync/repository_sync.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/sync/spec_to_code.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/sync/spec_to_tests.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/sync/watcher.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/sync/watcher_enhanced.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/telemetry.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/templates/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/templates/defaults/defect_v1.yaml +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/templates/defaults/enabler_v1.yaml +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/templates/defaults/spike_v1.yaml +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/templates/defaults/user_story_v1.yaml +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/templates/frameworks/scrum/user_story_v1.yaml +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/templates/personas/product-owner/user_story_v1.yaml +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/templates/providers/ado/work_item_v1.yaml +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/templates/registry.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/templates/specification_templates.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/acceptance_criteria.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/auth_tokens.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/bundle_converters.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/bundle_loader.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/code_change_detector.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/console.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/content_sanitizer.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/context_detection.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/contract_predicates.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/enrichment_context.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/enrichment_parser.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/env_manager.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/feature_keys.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/git.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/github_annotations.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/icontract_helpers.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/incremental_check.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/metadata.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/optional_deps.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/performance.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/persona_ownership.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/progress.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/progressive_disclosure.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/prompts.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/sdd_discovery.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/source_scanner.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/startup_checks.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/structure.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/structured_io.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/suggestions.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/utils/yaml_utils.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/validation/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/validation/command_audit.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/validators/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/validators/agile_validation.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/validators/change_proposal_integration.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/validators/cli_first_validator.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/validators/contract_validator.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/validators/fsm.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/validators/repro_checker.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/validators/schema.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/validators/sidecar/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/validators/sidecar/contract_populator.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/validators/sidecar/crosshair_runner.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/validators/sidecar/crosshair_summary.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/validators/sidecar/dependency_installer.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/validators/sidecar/framework_detector.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/validators/sidecar/frameworks/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/validators/sidecar/frameworks/base.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/validators/sidecar/frameworks/django.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/validators/sidecar/frameworks/drf.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/validators/sidecar/frameworks/fastapi.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/validators/sidecar/frameworks/flask.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/validators/sidecar/harness_generator.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/validators/sidecar/models.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/validators/sidecar/orchestrator.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/validators/sidecar/specmatic_runner.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/validators/sidecar/unannotated_detector.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/versioning/__init__.py +0 -0
- {specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/versioning/analyzer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: specfact-cli
|
|
3
|
-
Version: 0.42.
|
|
3
|
+
Version: 0.42.4
|
|
4
4
|
Summary: The swiss knife CLI for agile DevOps teams. Keep backlog, specs, tests, and code in sync with validation and contract enforcement for new projects and long-lived codebases.
|
|
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
|
|
@@ -342,6 +342,8 @@ specfact init ide --ide cursor
|
|
|
342
342
|
specfact init ide --ide vscode
|
|
343
343
|
```
|
|
344
344
|
|
|
345
|
+
`specfact init ide` discovers prompt resources from installed workflow modules and exports them to your IDE. If module prompt payloads are not installed yet, the CLI uses packaged fallback resources.
|
|
346
|
+
|
|
345
347
|
### Run Your First Flow
|
|
346
348
|
|
|
347
349
|
```bash
|
|
@@ -61,6 +61,8 @@ specfact init ide --ide cursor
|
|
|
61
61
|
specfact init ide --ide vscode
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
+
`specfact init ide` discovers prompt resources from installed workflow modules and exports them to your IDE. If module prompt payloads are not installed yet, the CLI uses packaged fallback resources.
|
|
65
|
+
|
|
64
66
|
### Run Your First Flow
|
|
65
67
|
|
|
66
68
|
```bash
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "specfact-cli"
|
|
7
|
-
version = "0.42.
|
|
7
|
+
version = "0.42.4"
|
|
8
8
|
description = "The swiss knife CLI for agile DevOps teams. Keep backlog, specs, tests, and code in sync with validation and contract enforcement for new projects and long-lived codebases."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -12,10 +12,10 @@ from __future__ import annotations
|
|
|
12
12
|
|
|
13
13
|
import os
|
|
14
14
|
import re
|
|
15
|
-
from collections.abc import Mapping
|
|
15
|
+
from collections.abc import Callable, Mapping
|
|
16
16
|
from datetime import UTC, datetime
|
|
17
17
|
from pathlib import Path
|
|
18
|
-
from typing import Any, NoReturn, cast
|
|
18
|
+
from typing import Any, NoReturn, Protocol, cast
|
|
19
19
|
from urllib.parse import urlparse
|
|
20
20
|
|
|
21
21
|
import requests
|
|
@@ -51,6 +51,19 @@ _ADO_COMMENTS_API_VERSION = "7.1-preview.4"
|
|
|
51
51
|
console = Console()
|
|
52
52
|
|
|
53
53
|
|
|
54
|
+
class _AccessTokenLike(Protocol):
|
|
55
|
+
"""Typed subset of Azure access token fields used by refresh logic."""
|
|
56
|
+
|
|
57
|
+
token: str
|
|
58
|
+
expires_on: int
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _get_access_token(credential: Any, scopes: list[str]) -> _AccessTokenLike:
|
|
62
|
+
"""Return a typed Azure access token from a credential object."""
|
|
63
|
+
get_token_fn = cast(Callable[..., _AccessTokenLike], credential.get_token)
|
|
64
|
+
return get_token_fn(*scopes)
|
|
65
|
+
|
|
66
|
+
|
|
54
67
|
def _as_str_dict(obj: dict[Any, Any]) -> dict[str, Any]:
|
|
55
68
|
"""Narrow a runtime ``dict`` to ``dict[str, Any]`` for static analysis."""
|
|
56
69
|
return cast(dict[str, Any], obj)
|
|
@@ -1850,7 +1863,7 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
|
|
|
1850
1863
|
# offline_access is a reserved scope and cannot be explicitly requested
|
|
1851
1864
|
azure_devops_resource = "499b84ac-1321-427f-aa17-267ca6975798/.default"
|
|
1852
1865
|
azure_devops_scopes = [azure_devops_resource]
|
|
1853
|
-
token = credential
|
|
1866
|
+
token = _get_access_token(credential, azure_devops_scopes)
|
|
1854
1867
|
|
|
1855
1868
|
# Return refreshed token data
|
|
1856
1869
|
from datetime import UTC, datetime
|
|
@@ -644,26 +644,8 @@ class CodeAnalyzer:
|
|
|
644
644
|
# Extract classes as features
|
|
645
645
|
for node in ast.walk(tree):
|
|
646
646
|
if isinstance(node, ast.ClassDef):
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
current_count = 0 if self.key_format == "sequential" else len(self.features)
|
|
650
|
-
|
|
651
|
-
# Extract Semgrep evidence for confidence scoring
|
|
652
|
-
class_start_line = node.lineno if hasattr(node, "lineno") else None
|
|
653
|
-
class_end_line = node.end_lineno if hasattr(node, "end_lineno") else None
|
|
654
|
-
semgrep_evidence = self._extract_semgrep_evidence(
|
|
655
|
-
semgrep_findings, node.name, class_start_line, class_end_line
|
|
656
|
-
)
|
|
657
|
-
|
|
658
|
-
# Create feature with Semgrep evidence included in confidence calculation
|
|
659
|
-
feature = self._extract_feature_from_class_parallel(
|
|
660
|
-
node, file_path, current_count, semgrep_evidence
|
|
661
|
-
)
|
|
662
|
-
if feature:
|
|
663
|
-
# Enhance feature with detailed Semgrep findings (outcomes, constraints, themes)
|
|
664
|
-
self._enhance_feature_with_semgrep(
|
|
665
|
-
feature, semgrep_findings, file_path, node.name, class_start_line, class_end_line
|
|
666
|
-
)
|
|
647
|
+
feature = self._extract_feature_for_results(node, file_path, semgrep_findings)
|
|
648
|
+
if feature is not None:
|
|
667
649
|
results["features"].append(feature)
|
|
668
650
|
|
|
669
651
|
except (SyntaxError, UnicodeDecodeError):
|
|
@@ -672,6 +654,31 @@ class CodeAnalyzer:
|
|
|
672
654
|
|
|
673
655
|
return results
|
|
674
656
|
|
|
657
|
+
def _extract_feature_for_results(
|
|
658
|
+
self,
|
|
659
|
+
node: ast.ClassDef,
|
|
660
|
+
file_path: Path,
|
|
661
|
+
semgrep_findings: list[dict[str, Any]],
|
|
662
|
+
) -> Feature | None:
|
|
663
|
+
"""Extract one feature while isolating per-class enhancement failures."""
|
|
664
|
+
current_count = 0 if self.key_format == "sequential" else len(self.features)
|
|
665
|
+
class_start_line = node.lineno if hasattr(node, "lineno") else None
|
|
666
|
+
class_end_line = node.end_lineno if hasattr(node, "end_lineno") else None
|
|
667
|
+
semgrep_evidence = self._extract_semgrep_evidence(semgrep_findings, node.name, class_start_line, class_end_line)
|
|
668
|
+
|
|
669
|
+
feature = self._extract_feature_from_class_parallel(node, file_path, current_count, semgrep_evidence)
|
|
670
|
+
if feature is None:
|
|
671
|
+
return None
|
|
672
|
+
|
|
673
|
+
try:
|
|
674
|
+
self._enhance_feature_with_semgrep(
|
|
675
|
+
feature, semgrep_findings, file_path, node.name, class_start_line, class_end_line
|
|
676
|
+
)
|
|
677
|
+
except Exception as exc:
|
|
678
|
+
console.print(f"[dim]⚠ Warning: Skipped Semgrep enhancement for {file_path}:{node.name}: {exc}[/dim]")
|
|
679
|
+
|
|
680
|
+
return feature
|
|
681
|
+
|
|
675
682
|
def _merge_analysis_results(self, results: dict[str, Any]) -> None:
|
|
676
683
|
"""Merge parallel analysis results into instance variables."""
|
|
677
684
|
# Merge themes
|
|
@@ -1327,10 +1334,16 @@ class CodeAnalyzer:
|
|
|
1327
1334
|
if not methods:
|
|
1328
1335
|
return None, None
|
|
1329
1336
|
primary_method = methods[0]
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1337
|
+
try:
|
|
1338
|
+
scenarios = self.control_flow_analyzer.extract_scenarios_from_method(
|
|
1339
|
+
primary_method, class_name, primary_method.name
|
|
1340
|
+
)
|
|
1341
|
+
except Exception:
|
|
1342
|
+
scenarios = None
|
|
1343
|
+
try:
|
|
1344
|
+
contracts = self.contract_extractor.extract_function_contracts(primary_method)
|
|
1345
|
+
except Exception:
|
|
1346
|
+
contracts = None
|
|
1334
1347
|
return scenarios, contracts
|
|
1335
1348
|
|
|
1336
1349
|
def _generate_story_title(self, group_name: str, class_name: str) -> str:
|
{specfact_cli-0.42.3 → specfact_cli-0.42.4}/src/specfact_cli/modules/init/module-package.yaml
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
name: init
|
|
2
|
-
version: 0.1.
|
|
2
|
+
version: 0.1.13
|
|
3
3
|
commands:
|
|
4
4
|
- init
|
|
5
5
|
category: core
|
|
@@ -17,5 +17,5 @@ publisher:
|
|
|
17
17
|
description: Initialize SpecFact workspace and bootstrap local configuration.
|
|
18
18
|
license: Apache-2.0
|
|
19
19
|
integrity:
|
|
20
|
-
checksum: sha256:
|
|
21
|
-
signature:
|
|
20
|
+
checksum: sha256:4f929d90edc481d21cf1aa9c2782f569a34baf560e25d3317067f1c5951509e9
|
|
21
|
+
signature: Sa60QWb6UaZRp40885CzkWMCGwT809/qQj1dnWIJcocwfLwT7Je+BR2STbGQv2RbTbTBBXmQ0Qwj/+AM3U0qAA==
|
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import subprocess
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import Any
|
|
7
|
+
from typing import Any, cast
|
|
8
8
|
|
|
9
9
|
import click
|
|
10
10
|
import typer
|
|
@@ -23,14 +23,16 @@ from specfact_cli.registry.help_cache import run_discovery_and_write_cache
|
|
|
23
23
|
from specfact_cli.registry.module_installer import USER_MODULES_ROOT as INIT_USER_MODULES_ROOT
|
|
24
24
|
from specfact_cli.registry.module_packages import get_discovered_modules_for_state
|
|
25
25
|
from specfact_cli.registry.module_state import write_modules_state
|
|
26
|
-
from specfact_cli.runtime import
|
|
26
|
+
from specfact_cli.runtime import debug_print, is_non_interactive
|
|
27
27
|
from specfact_cli.telemetry import telemetry
|
|
28
|
+
from specfact_cli.utils.contract_predicates import repo_path_exists, repo_path_is_dir
|
|
28
29
|
from specfact_cli.utils.env_manager import EnvManager, EnvManagerInfo, build_tool_command, detect_env_manager
|
|
29
30
|
from specfact_cli.utils.ide_setup import (
|
|
30
31
|
IDE_CONFIG,
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
_copy_template_files_to_ide,
|
|
33
|
+
_discover_module_resource_dirs,
|
|
33
34
|
detect_ide,
|
|
35
|
+
discover_prompt_template_files,
|
|
34
36
|
find_package_resources_path,
|
|
35
37
|
)
|
|
36
38
|
|
|
@@ -49,8 +51,36 @@ install_bundles_for_init = first_run_selection.install_bundles_for_init
|
|
|
49
51
|
is_first_run = first_run_selection.is_first_run
|
|
50
52
|
|
|
51
53
|
|
|
54
|
+
@beartype
|
|
55
|
+
@require(repo_path_exists, "Repo path must exist")
|
|
56
|
+
@require(repo_path_is_dir, "Repo path must be a directory")
|
|
57
|
+
@require(lambda ide: ide in IDE_CONFIG, "IDE must be valid")
|
|
58
|
+
@ensure(
|
|
59
|
+
lambda result: (
|
|
60
|
+
isinstance(result, tuple)
|
|
61
|
+
and len(result) == 2
|
|
62
|
+
and isinstance(result[0], list)
|
|
63
|
+
and all(isinstance(path, Path) for path in result[0])
|
|
64
|
+
and (result[1] is None or isinstance(result[1], Path))
|
|
65
|
+
),
|
|
66
|
+
"Must return copied files and optional settings path",
|
|
67
|
+
)
|
|
68
|
+
def copy_templates_to_ide(repo_path: Path, ide: str, force: bool = False) -> tuple[list[Path], Path | None]:
|
|
69
|
+
"""Compatibility wrapper that discovers prompt templates before copying them."""
|
|
70
|
+
return _copy_template_files_to_ide(repo_path, ide, discover_prompt_template_files(repo_path), force)
|
|
71
|
+
|
|
72
|
+
|
|
52
73
|
def _resolve_field_mapping_templates_dir(repo_path: Path) -> Path | None:
|
|
53
74
|
"""Locate backlog field mapping templates (dev checkout or installed package)."""
|
|
75
|
+
for resource_root in _discover_module_resource_dirs(
|
|
76
|
+
"resources/templates/backlog/field_mappings",
|
|
77
|
+
repo_path=repo_path,
|
|
78
|
+
categories={"backlog"},
|
|
79
|
+
):
|
|
80
|
+
installed_templates_dir = (resource_root / "templates" / "backlog" / "field_mappings").resolve()
|
|
81
|
+
if installed_templates_dir.exists():
|
|
82
|
+
return installed_templates_dir
|
|
83
|
+
|
|
54
84
|
dev_templates_dir = (repo_path / "resources" / "templates" / "backlog" / "field_mappings").resolve()
|
|
55
85
|
if dev_templates_dir.exists():
|
|
56
86
|
return dev_templates_dir
|
|
@@ -161,7 +191,8 @@ def _questionary_style() -> Any:
|
|
|
161
191
|
import questionary # type: ignore[reportMissingImports]
|
|
162
192
|
except ImportError:
|
|
163
193
|
return None
|
|
164
|
-
|
|
194
|
+
questionary_module = cast(Any, questionary)
|
|
195
|
+
return questionary_module.Style(
|
|
165
196
|
[
|
|
166
197
|
("qmark", "fg:#00af87 bold"),
|
|
167
198
|
("question", "bold"),
|
|
@@ -214,12 +245,16 @@ def _run_module_checkbox_prompt(
|
|
|
214
245
|
) -> list[str]:
|
|
215
246
|
action_title = "Enable" if action == "enable" else "Disable"
|
|
216
247
|
current_state = "disabled" if action == "enable" else "enabled"
|
|
217
|
-
selected: list[str] | None =
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
248
|
+
selected: list[str] | None = (
|
|
249
|
+
cast(Any, questionary)
|
|
250
|
+
.checkbox(
|
|
251
|
+
f"{action_title} module(s) from currently {current_state}:",
|
|
252
|
+
choices=choices,
|
|
253
|
+
instruction="(multi-select)",
|
|
254
|
+
style=_questionary_style(),
|
|
255
|
+
)
|
|
256
|
+
.ask()
|
|
257
|
+
)
|
|
223
258
|
if not selected:
|
|
224
259
|
return []
|
|
225
260
|
return [display_to_id[s] for s in selected if s in display_to_id]
|
|
@@ -261,46 +296,35 @@ def _select_module_ids_interactive(action: str, modules_list: list[dict[str, Any
|
|
|
261
296
|
|
|
262
297
|
def _resolve_templates_dir(repo_path: Path) -> Path | None:
|
|
263
298
|
"""Resolve templates directory from repo checkout or installed package."""
|
|
299
|
+
prompt_files = discover_prompt_template_files(repo_path, include_package_fallback=False)
|
|
300
|
+
if prompt_files:
|
|
301
|
+
return prompt_files[0].parent
|
|
302
|
+
|
|
264
303
|
dev_templates_dir = (repo_path / "resources" / "prompts").resolve()
|
|
265
304
|
if dev_templates_dir.exists():
|
|
266
305
|
return dev_templates_dir
|
|
267
|
-
try:
|
|
268
|
-
import importlib.resources
|
|
269
306
|
|
|
270
|
-
resources_ref = importlib.resources.files("specfact_cli")
|
|
271
|
-
templates_ref = resources_ref / "resources" / "prompts"
|
|
272
|
-
package_templates_dir = Path(str(templates_ref)).resolve()
|
|
273
|
-
if package_templates_dir.exists():
|
|
274
|
-
return package_templates_dir
|
|
275
|
-
except Exception as exc:
|
|
276
|
-
if is_debug_mode():
|
|
277
|
-
debug_log_operation(
|
|
278
|
-
"template_resolution",
|
|
279
|
-
"importlib.resources(specfact_cli/resources/prompts)",
|
|
280
|
-
"error",
|
|
281
|
-
error=repr(exc),
|
|
282
|
-
)
|
|
283
307
|
return find_package_resources_path("specfact_cli", "resources/prompts")
|
|
284
308
|
|
|
285
309
|
|
|
286
|
-
def _expected_ide_prompt_basenames(format_type: str) -> list[str]:
|
|
310
|
+
def _expected_ide_prompt_basenames(repo_path: Path, format_type: str) -> list[str]:
|
|
311
|
+
prompt_files = discover_prompt_template_files(repo_path)
|
|
287
312
|
if format_type == "prompt.md":
|
|
288
|
-
return [f"{
|
|
313
|
+
return [f"{path.stem}.prompt.md" for path in prompt_files]
|
|
289
314
|
if format_type == "toml":
|
|
290
|
-
return [f"{
|
|
291
|
-
return [
|
|
315
|
+
return [f"{path.stem}.toml" for path in prompt_files]
|
|
316
|
+
return [path.name for path in prompt_files]
|
|
292
317
|
|
|
293
318
|
|
|
294
|
-
def _count_outdated_ide_prompts(ide_dir: Path,
|
|
319
|
+
def _count_outdated_ide_prompts(ide_dir: Path, prompt_files: list[Path], format_type: str) -> int:
|
|
295
320
|
outdated = 0
|
|
296
|
-
for
|
|
297
|
-
src = templates_dir / f"{cmd}.md"
|
|
321
|
+
for src in prompt_files:
|
|
298
322
|
if format_type == "prompt.md":
|
|
299
|
-
dest = ide_dir / f"{
|
|
323
|
+
dest = ide_dir / f"{src.stem}.prompt.md"
|
|
300
324
|
elif format_type == "toml":
|
|
301
|
-
dest = ide_dir / f"{
|
|
325
|
+
dest = ide_dir / f"{src.stem}.toml"
|
|
302
326
|
else:
|
|
303
|
-
dest = ide_dir /
|
|
327
|
+
dest = ide_dir / src.name
|
|
304
328
|
if src.exists() and dest.exists() and dest.stat().st_mtime < src.stat().st_mtime:
|
|
305
329
|
outdated += 1
|
|
306
330
|
return outdated
|
|
@@ -312,7 +336,7 @@ def _audit_prompt_installation(repo_path: Path) -> None:
|
|
|
312
336
|
config = IDE_CONFIG[detected_ide]
|
|
313
337
|
ide_dir = repo_path / str(config["folder"])
|
|
314
338
|
format_type = str(config["format"])
|
|
315
|
-
expected_files = _expected_ide_prompt_basenames(format_type)
|
|
339
|
+
expected_files = _expected_ide_prompt_basenames(repo_path, format_type)
|
|
316
340
|
|
|
317
341
|
if not ide_dir.exists():
|
|
318
342
|
console.print(
|
|
@@ -322,8 +346,8 @@ def _audit_prompt_installation(repo_path: Path) -> None:
|
|
|
322
346
|
return
|
|
323
347
|
|
|
324
348
|
missing = [name for name in expected_files if not (ide_dir / name).exists()]
|
|
325
|
-
|
|
326
|
-
outdated = _count_outdated_ide_prompts(ide_dir,
|
|
349
|
+
prompt_files = discover_prompt_template_files(repo_path)
|
|
350
|
+
outdated = _count_outdated_ide_prompts(ide_dir, prompt_files, format_type) if prompt_files else 0
|
|
327
351
|
|
|
328
352
|
if not missing and outdated == 0:
|
|
329
353
|
console.print(f"[green]Prompt status:[/green] {detected_ide} prompts are present and up to date.")
|
|
@@ -362,12 +386,16 @@ def _select_ide_interactive(default_ide: str) -> str:
|
|
|
362
386
|
choices.append(label)
|
|
363
387
|
|
|
364
388
|
default_label = next((lbl for lbl, iid in label_to_ide.items() if iid == default_ide), choices[0])
|
|
365
|
-
selected =
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
389
|
+
selected = (
|
|
390
|
+
cast(Any, questionary)
|
|
391
|
+
.select(
|
|
392
|
+
"Select IDE for prompt setup",
|
|
393
|
+
choices=choices,
|
|
394
|
+
default=default_label,
|
|
395
|
+
style=_questionary_style(),
|
|
396
|
+
)
|
|
397
|
+
.ask()
|
|
398
|
+
)
|
|
371
399
|
if not selected:
|
|
372
400
|
raise typer.Exit(1)
|
|
373
401
|
console.print(Rule(style="dim"))
|
|
@@ -441,11 +469,15 @@ def _manual_bundle_ids_from_questionary(questionary: Any) -> list[str]:
|
|
|
441
469
|
f"{first_run_selection.BUNDLE_DISPLAY.get(bid, bid)} [dim]({bid})[/dim]"
|
|
442
470
|
for bid in first_run_selection.CANONICAL_BUNDLES
|
|
443
471
|
]
|
|
444
|
-
selected =
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
472
|
+
selected = (
|
|
473
|
+
cast(Any, questionary)
|
|
474
|
+
.checkbox(
|
|
475
|
+
"Select bundles to install:",
|
|
476
|
+
choices=bundle_choices,
|
|
477
|
+
style=_questionary_style(),
|
|
478
|
+
)
|
|
479
|
+
.ask()
|
|
480
|
+
)
|
|
449
481
|
if not selected:
|
|
450
482
|
return []
|
|
451
483
|
return [bid for bid in first_run_selection.CANONICAL_BUNDLES if any(bid in s for s in selected)]
|
|
@@ -489,11 +521,15 @@ def _interactive_first_run_bundle_selection() -> list[str]:
|
|
|
489
521
|
profile_to_key = {f"{label} [dim]({key})[/dim]": key for key, label in first_run_selection.PROFILE_DISPLAY_ORDER}
|
|
490
522
|
profile_to_key["Choose bundles manually"] = "_manual_"
|
|
491
523
|
|
|
492
|
-
choice =
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
524
|
+
choice = (
|
|
525
|
+
cast(Any, questionary)
|
|
526
|
+
.select(
|
|
527
|
+
"Select a profile or choose bundles manually:",
|
|
528
|
+
choices=[*profile_choices, "Choose bundles manually"],
|
|
529
|
+
style=_questionary_style(),
|
|
530
|
+
)
|
|
531
|
+
.ask()
|
|
532
|
+
)
|
|
497
533
|
|
|
498
534
|
if not choice:
|
|
499
535
|
return []
|
|
@@ -559,13 +595,14 @@ def init_ide(
|
|
|
559
595
|
if install_deps:
|
|
560
596
|
_install_contract_enhancement_dependencies(repo_path, env_info)
|
|
561
597
|
|
|
562
|
-
|
|
563
|
-
if not
|
|
598
|
+
prompt_files = discover_prompt_template_files(repo_path)
|
|
599
|
+
if not prompt_files:
|
|
564
600
|
console.print("[red]Error:[/red] Templates directory not found.")
|
|
565
601
|
raise typer.Exit(1)
|
|
566
602
|
|
|
567
|
-
|
|
568
|
-
|
|
603
|
+
template_sources = ", ".join(sorted({str(path.parent) for path in prompt_files}))
|
|
604
|
+
console.print(f"[cyan]Templates:[/cyan] {template_sources}")
|
|
605
|
+
copied_files, settings_path = copy_templates_to_ide(repo_path, selected_ide, force)
|
|
569
606
|
_copy_backlog_field_mapping_templates(repo_path, force, console)
|
|
570
607
|
|
|
571
608
|
console.print()
|
|
@@ -578,7 +578,14 @@ def _make_package_loader(package_dir: Path, package_name: str, command_name: str
|
|
|
578
578
|
raise ValueError(f"Cannot load from {package_dir.name}")
|
|
579
579
|
mod = importlib.util.module_from_spec(spec)
|
|
580
580
|
sys.modules[spec.name] = mod
|
|
581
|
-
|
|
581
|
+
try:
|
|
582
|
+
spec.loader.exec_module(mod)
|
|
583
|
+
except (ImportError, ModuleNotFoundError, OSError) as exc:
|
|
584
|
+
raise ValueError(
|
|
585
|
+
"Runtime compatibility error while loading "
|
|
586
|
+
f"module '{package_name}' command '{command_name}' from {package_dir}: {exc}. "
|
|
587
|
+
f"Reinstall the module and run SpecFact with the same Python interpreter ({sys.executable})."
|
|
588
|
+
) from exc
|
|
582
589
|
command_attr = f"{_normalized_module_name(command_name)}_app"
|
|
583
590
|
app = getattr(mod, command_attr, None)
|
|
584
591
|
if app is None:
|
|
@@ -28,7 +28,7 @@ from specfact_cli.common.logger_setup import (
|
|
|
28
28
|
)
|
|
29
29
|
from specfact_cli.modes import OperationalMode
|
|
30
30
|
from specfact_cli.utils.structured_io import StructuredFormat
|
|
31
|
-
from specfact_cli.utils.terminal import detect_terminal_capabilities, get_console_config
|
|
31
|
+
from specfact_cli.utils.terminal import detect_terminal_capabilities, ensure_output_stream_safety, get_console_config
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
DEBUG_LOG_DATEFMT = "%Y-%m-%d %H:%M:%S"
|
|
@@ -201,6 +201,7 @@ def get_configured_console() -> Console:
|
|
|
201
201
|
(e.g. CliRunner's captured stdout after invoke() ends).
|
|
202
202
|
"""
|
|
203
203
|
mode = get_terminal_mode()
|
|
204
|
+
ensure_output_stream_safety()
|
|
204
205
|
|
|
205
206
|
if _is_test_env():
|
|
206
207
|
config = get_console_config()
|