specfact-cli 0.14.0__tar.gz → 0.15.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/PKG-INFO +1 -1
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/pyproject.toml +1 -1
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/resources/prompts/specfact.01-import.md +8 -5
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/resources/prompts/specfact.03-review.md +9 -4
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/resources/prompts/specfact.04-sdd.md +4 -4
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/resources/prompts/specfact.05-enforce.md +3 -3
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/resources/prompts/specfact.compare.md +2 -2
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/resources/prompts/specfact.validate.md +2 -2
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/__init__.py +1 -1
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/__init__.py +1 -1
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/analyzers/code_analyzer.py +34 -5
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/analyzers/control_flow_analyzer.py +9 -23
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/cli.py +8 -1
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/commands/enforce.py +10 -9
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/commands/generate.py +16 -12
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/commands/implement.py +3 -3
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/commands/import_cmd.py +52 -65
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/commands/init.py +1 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/commands/migrate.py +456 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/commands/plan.py +34 -5
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/commands/repro.py +12 -5
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/commands/sdd.py +5 -2
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/commands/spec.py +1 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/commands/sync.py +4 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/enrichers/plan_enricher.py +47 -18
- specfact_cli-0.15.0/src/specfact_cli/sync/watcher_enhanced.py +493 -0
- specfact_cli-0.15.0/src/specfact_cli/templates/__init__.py +8 -0
- specfact_cli-0.15.0/src/specfact_cli/templates/specification_templates.py +146 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/utils/bundle_loader.py +3 -2
- specfact_cli-0.15.0/src/specfact_cli/utils/context_detection.py +269 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/utils/github_annotations.py +35 -10
- specfact_cli-0.15.0/src/specfact_cli/utils/progressive_disclosure.py +326 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/utils/sdd_discovery.py +27 -15
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/utils/structure.py +334 -50
- specfact_cli-0.15.0/src/specfact_cli/utils/suggestions.py +155 -0
- specfact_cli-0.14.0/src/specfact_cli/templates/__init__.py +0 -13
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/.gitignore +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/LICENSE.md +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/README.md +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/resources/mappings/node-async.yaml +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/resources/mappings/python-async.yaml +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/resources/mappings/speckit-default.yaml +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/resources/prompts/shared/cli-enforcement.md +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/resources/prompts/specfact.02-plan.md +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/resources/prompts/specfact.06-sync.md +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/resources/prompts/specfact.07-contracts.md +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/resources/schemas/deviation.schema.json +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/resources/schemas/plan.schema.json +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/resources/schemas/protocol.schema.json +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/resources/templates/github-action.yml.j2 +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/resources/templates/plan.bundle.yaml.j2 +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/resources/templates/pr-template.md.j2 +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/resources/templates/protocol.yaml.j2 +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/resources/templates/telemetry.yaml.example +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/agents/__init__.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/agents/analyze_agent.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/agents/base.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/agents/plan_agent.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/agents/registry.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/agents/sync_agent.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/analyzers/__init__.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/analyzers/ambiguity_scanner.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/analyzers/constitution_evidence_extractor.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/analyzers/contract_extractor.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/analyzers/graph_analyzer.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/analyzers/relationship_mapper.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/analyzers/requirement_extractor.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/analyzers/test_pattern_extractor.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/commands/__init__.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/commands/analyze.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/commands/bridge.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/commands/drift.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/commands/run.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/common/__init__.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/common/logger_setup.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/common/logging_utils.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/common/text_utils.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/common/utils.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/comparators/__init__.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/comparators/plan_comparator.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/enrichers/constitution_enricher.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/generators/__init__.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/generators/contract_generator.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/generators/openapi_extractor.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/generators/plan_generator.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/generators/protocol_generator.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/generators/report_generator.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/generators/task_generator.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/generators/test_to_openapi.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/generators/workflow_generator.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/importers/__init__.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/importers/speckit_converter.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/importers/speckit_scanner.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/integrations/__init__.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/integrations/specmatic.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/migrations/__init__.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/migrations/plan_migrator.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/models/__init__.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/models/bridge.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/models/deviation.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/models/enforcement.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/models/plan.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/models/project.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/models/protocol.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/models/quality.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/models/sdd.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/models/source_tracking.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/models/task.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/modes/__init__.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/modes/detector.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/modes/router.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/resources/semgrep/async.yml +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/resources/semgrep/code-quality.yml +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/resources/semgrep/feature-detection.yml +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/runtime.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/sync/__init__.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/sync/bridge_probe.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/sync/bridge_sync.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/sync/bridge_watch.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/sync/change_detector.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/sync/code_to_spec.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/sync/drift_detector.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/sync/repository_sync.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/sync/spec_to_code.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/sync/spec_to_tests.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/sync/speckit_sync.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/sync/watcher.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/telemetry.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/templates/bridge_templates.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/utils/__init__.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/utils/acceptance_criteria.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/utils/console.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/utils/enrichment_context.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/utils/enrichment_parser.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/utils/feature_keys.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/utils/git.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/utils/ide_setup.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/utils/incremental_check.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/utils/optional_deps.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/utils/performance.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/utils/progress.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/utils/prompts.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/utils/source_scanner.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/utils/structured_io.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/utils/yaml_utils.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/validators/__init__.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/validators/cli_first_validator.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/validators/contract_validator.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/validators/fsm.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/validators/repro_checker.py +0 -0
- {specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/validators/schema.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: specfact-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.15.0
|
|
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.
|
|
7
|
+
version = "0.15.0"
|
|
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"
|
|
@@ -20,7 +20,7 @@ Import codebase → plan bundle. CLI extracts routes/schemas/relationships/contr
|
|
|
20
20
|
|
|
21
21
|
**Target/Input**: `--bundle NAME` (optional, defaults to active plan), `--repo PATH`, `--entry-point PATH`, `--enrichment PATH`
|
|
22
22
|
**Output/Results**: `--report PATH`
|
|
23
|
-
**Behavior/Options**: `--shadow-only`, `--enrich-for-speckit`
|
|
23
|
+
**Behavior/Options**: `--shadow-only`, `--enrich-for-speckit/--no-enrich-for-speckit` (default: enabled, uses PlanEnricher for consistent enrichment)
|
|
24
24
|
**Advanced/Configuration**: `--confidence FLOAT` (0.0-1.0), `--key-format FORMAT` (classname|sequential)
|
|
25
25
|
|
|
26
26
|
## Workflow
|
|
@@ -28,12 +28,14 @@ Import codebase → plan bundle. CLI extracts routes/schemas/relationships/contr
|
|
|
28
28
|
1. **Execute CLI**: `specfact import from-code [<bundle>] --repo <path> [options]`
|
|
29
29
|
- CLI extracts: routes (FastAPI/Flask/Django), schemas (Pydantic), relationships, contracts (OpenAPI scaffolds), source tracking
|
|
30
30
|
- Uses active plan if bundle not specified
|
|
31
|
+
- **Auto-enrichment enabled by default**: Automatically enhances vague acceptance criteria, incomplete requirements, and generic tasks using PlanEnricher (same logic as `plan review --auto-enrich`)
|
|
32
|
+
- Use `--no-enrich-for-speckit` to disable auto-enrichment
|
|
31
33
|
|
|
32
34
|
2. **LLM Enrichment** (if `--enrichment` provided):
|
|
33
35
|
- Read `.specfact/projects/<bundle>/enrichment_context.md`
|
|
34
36
|
- Enrich: business context, "why" reasoning, missing acceptance criteria
|
|
35
37
|
- Validate: contracts vs code, feature/story alignment
|
|
36
|
-
- Save enrichment report to `.specfact/reports/enrichment/` (if created)
|
|
38
|
+
- Save enrichment report to `.specfact/projects/<bundle-name>/reports/enrichment/` (bundle-specific, Phase 8.5, if created)
|
|
37
39
|
|
|
38
40
|
3. **Present**: Bundle location, report path, summary (features/stories/contracts/relationships)
|
|
39
41
|
|
|
@@ -86,7 +88,7 @@ specfact import from-code [<bundle>] --repo <path> --no-interactive
|
|
|
86
88
|
- ❌ Write to `.specfact/` folder directly (always use CLI)
|
|
87
89
|
- ❌ Use direct file manipulation tools for writing (use CLI commands)
|
|
88
90
|
|
|
89
|
-
**Output**: Generate enrichment report (Markdown) saved to `.specfact/reports/enrichment/`
|
|
91
|
+
**Output**: Generate enrichment report (Markdown) saved to `.specfact/projects/<bundle-name>/reports/enrichment/` (bundle-specific, Phase 8.5)
|
|
90
92
|
|
|
91
93
|
### Phase 3: CLI Artifact Creation (REQUIRED)
|
|
92
94
|
|
|
@@ -107,8 +109,9 @@ specfact import from-code [<bundle>] --repo <path> --enrichment <enrichment-repo
|
|
|
107
109
|
## Common Patterns
|
|
108
110
|
|
|
109
111
|
```bash
|
|
110
|
-
/specfact.01-import --repo . # Uses active plan
|
|
111
|
-
/specfact.01-import --bundle legacy-api --repo .
|
|
112
|
+
/specfact.01-import --repo . # Uses active plan, auto-enrichment enabled by default
|
|
113
|
+
/specfact.01-import --bundle legacy-api --repo . # Auto-enrichment enabled
|
|
114
|
+
/specfact.01-import --repo . --no-enrich-for-speckit # Disable auto-enrichment
|
|
112
115
|
/specfact.01-import --repo . --entry-point src/auth/
|
|
113
116
|
/specfact.01-import --repo . --enrichment report.md
|
|
114
117
|
```
|
|
@@ -37,7 +37,7 @@ Review project bundle to identify/resolve ambiguities and missing information. A
|
|
|
37
37
|
|
|
38
38
|
- `--no-interactive` - Non-interactive mode (for CI/CD). Default: False (interactive mode)
|
|
39
39
|
- `--answers JSON` - JSON object with question_id -> answer mappings. Default: None
|
|
40
|
-
- `--auto-enrich` - Automatically enrich vague acceptance criteria. Default: False
|
|
40
|
+
- `--auto-enrich` - Automatically enrich vague acceptance criteria using PlanEnricher (same enrichment logic as `import from-code`). Default: False (opt-in for review, but import has auto-enrichment enabled by default)
|
|
41
41
|
|
|
42
42
|
### Advanced/Configuration
|
|
43
43
|
|
|
@@ -260,16 +260,21 @@ Create one with: specfact plan init legacy-api
|
|
|
260
260
|
|
|
261
261
|
## Enrichment Workflow
|
|
262
262
|
|
|
263
|
+
**Note**: Import command (`specfact import from-code`) has **auto-enrichment enabled by default** using PlanEnricher. Review command requires explicit `--auto-enrich` flag.
|
|
264
|
+
|
|
263
265
|
**Typical workflow when enrichment is needed:**
|
|
264
266
|
|
|
265
267
|
1. **Get findings**: `specfact plan review --list-findings --findings-format json`
|
|
266
268
|
2. **Analyze findings**: Review missing information (target_users, value_hypothesis, etc.)
|
|
267
|
-
3. **
|
|
268
|
-
|
|
269
|
+
3. **Apply automatic enrichment** (if needed):
|
|
270
|
+
- **During import**: Auto-enrichment happens automatically (enabled by default)
|
|
271
|
+
- **After import**: Use `specfact plan review --auto-enrich` to enhance vague criteria
|
|
272
|
+
4. **Create enrichment report** (for business context, confidence adjustments, missing features): Write Markdown file addressing findings
|
|
273
|
+
5. **Apply manual enrichment**:
|
|
269
274
|
- **Preferred**: Use enrichment to create `--answers` JSON and run `plan review --answers`
|
|
270
275
|
- **Alternative**: Use `plan update-idea` to update idea fields directly
|
|
271
276
|
- **Last resort**: If bundle needs regeneration, use `import from-code --enrichment`
|
|
272
|
-
|
|
277
|
+
6. **Verify**: Run `plan review` again to confirm improvements
|
|
273
278
|
|
|
274
279
|
## Context
|
|
275
280
|
|
|
@@ -25,7 +25,7 @@ Create/update SDD manifest from project bundle. Captures WHY (intent/constraints
|
|
|
25
25
|
### Target/Input
|
|
26
26
|
|
|
27
27
|
- `bundle NAME` (optional argument) - Project bundle name (e.g., legacy-api, auth-module). Default: active plan (set via `plan select`)
|
|
28
|
-
- `--sdd PATH` - Output SDD manifest path. Default: .specfact/
|
|
28
|
+
- `--sdd PATH` - Output SDD manifest path. Default: bundle-specific .specfact/projects/<bundle-name>/sdd.<format> (Phase 8.5)
|
|
29
29
|
|
|
30
30
|
### Output/Results
|
|
31
31
|
|
|
@@ -119,12 +119,12 @@ specfact plan harden [<bundle-name>] --no-interactive
|
|
|
119
119
|
### Success
|
|
120
120
|
|
|
121
121
|
```text
|
|
122
|
-
✓ SDD manifest created: .specfact/
|
|
122
|
+
✓ SDD manifest created: .specfact/projects/legacy-api/sdd.yaml
|
|
123
123
|
|
|
124
124
|
SDD Manifest Summary:
|
|
125
125
|
Project Bundle: .specfact/projects/legacy-api/
|
|
126
126
|
Bundle Hash: abc123def456...
|
|
127
|
-
SDD Path: .specfact/
|
|
127
|
+
SDD Path: .specfact/projects/legacy-api/sdd.yaml
|
|
128
128
|
|
|
129
129
|
WHY (Intent):
|
|
130
130
|
Build secure authentication system
|
|
@@ -151,7 +151,7 @@ Create one with: specfact plan init legacy-api
|
|
|
151
151
|
/specfact.04-sdd # Uses active plan
|
|
152
152
|
/specfact.04-sdd legacy-api # Specific bundle
|
|
153
153
|
/specfact.04-sdd --output-format json # JSON format
|
|
154
|
-
/specfact.04-sdd --sdd .specfact/
|
|
154
|
+
/specfact.04-sdd --sdd .specfact/projects/custom-bundle/sdd.yaml
|
|
155
155
|
```
|
|
156
156
|
|
|
157
157
|
## Context
|
|
@@ -25,12 +25,12 @@ Validate SDD manifest against project bundle and contracts. Checks hash matching
|
|
|
25
25
|
### Target/Input
|
|
26
26
|
|
|
27
27
|
- `bundle NAME` (optional argument) - Project bundle name (e.g., legacy-api, auth-module). Default: active plan (set via `plan select`)
|
|
28
|
-
- `--sdd PATH` - Path to SDD manifest. Default: .specfact/sdd/<bundle-name>.<format>
|
|
28
|
+
- `--sdd PATH` - Path to SDD manifest. Default: bundle-specific .specfact/projects/<bundle-name>/sdd.<format> (Phase 8.5), with fallback to legacy .specfact/sdd/<bundle-name>.<format>
|
|
29
29
|
|
|
30
30
|
### Output/Results
|
|
31
31
|
|
|
32
32
|
- `--output-format FORMAT` - Output format (yaml, json, markdown). Default: yaml
|
|
33
|
-
- `--out PATH` - Output file path. Default: .specfact/reports/
|
|
33
|
+
- `--out PATH` - Output file path. Default: bundle-specific .specfact/projects/<bundle-name>/reports/enforcement/report-<timestamp>.<format> (Phase 8.5)
|
|
34
34
|
|
|
35
35
|
### Behavior/Options
|
|
36
36
|
|
|
@@ -131,7 +131,7 @@ Total deviations: 0
|
|
|
131
131
|
Medium: 0
|
|
132
132
|
Low: 0
|
|
133
133
|
|
|
134
|
-
Report saved to: .specfact/reports/
|
|
134
|
+
Report saved to: .specfact/projects/<bundle-name>/reports/enforcement/report-2025-11-26T10-30-00.yaml
|
|
135
135
|
```
|
|
136
136
|
|
|
137
137
|
### Failure (Hash Mismatch)
|
|
@@ -31,7 +31,7 @@ Compare two project bundles (or legacy plan bundles) to detect deviations, misma
|
|
|
31
31
|
### Output/Results
|
|
32
32
|
|
|
33
33
|
- `--output-format FORMAT` - Output format (markdown, json, yaml). Default: markdown
|
|
34
|
-
- `--out PATH` - Output file path. Default: .specfact/reports/comparison/
|
|
34
|
+
- `--out PATH` - Output file path. Default: bundle-specific .specfact/projects/<bundle-name>/reports/comparison/report-<timestamp>.md (Phase 8.5), or global .specfact/reports/comparison/ if no bundle context
|
|
35
35
|
|
|
36
36
|
### Behavior/Options
|
|
37
37
|
|
|
@@ -125,7 +125,7 @@ specfact plan compare [--bundle <name>] --no-interactive
|
|
|
125
125
|
```text
|
|
126
126
|
✓ Comparison complete
|
|
127
127
|
|
|
128
|
-
Comparison Report: .specfact/reports/comparison/
|
|
128
|
+
Comparison Report: .specfact/projects/<bundle-name>/reports/comparison/report-2025-11-26T10-30-00.md
|
|
129
129
|
|
|
130
130
|
Deviations Summary:
|
|
131
131
|
Total: 5
|
|
@@ -28,7 +28,7 @@ Run full validation suite for reproducibility and contract compliance. Executes
|
|
|
28
28
|
|
|
29
29
|
### Output/Results
|
|
30
30
|
|
|
31
|
-
- `--out PATH` - Output report path. Default: .specfact/reports/enforcement/report-<timestamp>.yaml
|
|
31
|
+
- `--out PATH` - Output report path. Default: bundle-specific .specfact/projects/<bundle-name>/reports/enforcement/report-<timestamp>.yaml (Phase 8.5), or global .specfact/reports/enforcement/ if no bundle context
|
|
32
32
|
|
|
33
33
|
### Behavior/Options
|
|
34
34
|
|
|
@@ -135,7 +135,7 @@ Check Summary:
|
|
|
135
135
|
Property Tests ✓ Passed
|
|
136
136
|
Smoke Tests ✓ Passed
|
|
137
137
|
|
|
138
|
-
Report saved to: .specfact/reports/enforcement/report-2025-11-26T10-30-00.yaml
|
|
138
|
+
Report saved to: .specfact/projects/<bundle-name>/reports/enforcement/report-2025-11-26T10-30-00.yaml
|
|
139
139
|
```
|
|
140
140
|
|
|
141
141
|
### Failure
|
|
@@ -144,6 +144,8 @@ class CodeAnalyzer:
|
|
|
144
144
|
SpinnerColumn(),
|
|
145
145
|
TextColumn("[progress.description]{task.description}"),
|
|
146
146
|
BarColumn(),
|
|
147
|
+
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
|
148
|
+
TextColumn("({task.completed}/{task.total})"),
|
|
147
149
|
TimeElapsedColumn(),
|
|
148
150
|
console=console,
|
|
149
151
|
) as progress:
|
|
@@ -202,7 +204,13 @@ class CodeAnalyzer:
|
|
|
202
204
|
prev_features_count = len(self.features)
|
|
203
205
|
self._merge_analysis_results(results)
|
|
204
206
|
completed_count += 1
|
|
205
|
-
progress
|
|
207
|
+
# Update progress with feature count in description
|
|
208
|
+
features_count = len(self.features)
|
|
209
|
+
progress.update(
|
|
210
|
+
task3,
|
|
211
|
+
completed=completed_count,
|
|
212
|
+
description=f"[cyan]Phase 3: Analyzing files and extracting features... ({features_count} features discovered)",
|
|
213
|
+
)
|
|
206
214
|
|
|
207
215
|
# Phase 4.9: Report incremental results for quick first value
|
|
208
216
|
if self.incremental_callback and len(self.features) > prev_features_count:
|
|
@@ -211,7 +219,12 @@ class CodeAnalyzer:
|
|
|
211
219
|
except Exception as e:
|
|
212
220
|
console.print(f"[dim]⚠ Warning: Failed to analyze {file_path}: {e}[/dim]")
|
|
213
221
|
completed_count += 1
|
|
214
|
-
|
|
222
|
+
features_count = len(self.features)
|
|
223
|
+
progress.update(
|
|
224
|
+
task3,
|
|
225
|
+
completed=completed_count,
|
|
226
|
+
description=f"[cyan]Phase 3: Analyzing files and extracting features... ({features_count} features discovered)",
|
|
227
|
+
)
|
|
215
228
|
else:
|
|
216
229
|
executor = ThreadPoolExecutor(max_workers=max_workers)
|
|
217
230
|
interrupted = False
|
|
@@ -230,7 +243,13 @@ class CodeAnalyzer:
|
|
|
230
243
|
prev_features_count = len(self.features)
|
|
231
244
|
self._merge_analysis_results(results)
|
|
232
245
|
completed_count += 1
|
|
233
|
-
progress
|
|
246
|
+
# Update progress with feature count in description
|
|
247
|
+
features_count = len(self.features)
|
|
248
|
+
progress.update(
|
|
249
|
+
task3,
|
|
250
|
+
completed=completed_count,
|
|
251
|
+
description=f"[cyan]Phase 3: Analyzing files and extracting features... ({features_count} features discovered)",
|
|
252
|
+
)
|
|
234
253
|
|
|
235
254
|
# Phase 4.9: Report incremental results for quick first value
|
|
236
255
|
if self.incremental_callback and len(self.features) > prev_features_count:
|
|
@@ -248,7 +267,12 @@ class CodeAnalyzer:
|
|
|
248
267
|
file_path = future_to_file[future]
|
|
249
268
|
console.print(f"[dim]⚠ Warning: Failed to analyze {file_path}: {e}[/dim]")
|
|
250
269
|
completed_count += 1
|
|
251
|
-
|
|
270
|
+
features_count = len(self.features)
|
|
271
|
+
progress.update(
|
|
272
|
+
task3,
|
|
273
|
+
completed=completed_count,
|
|
274
|
+
description=f"[cyan]Phase 3: Analyzing files and extracting features... ({features_count} features discovered)",
|
|
275
|
+
)
|
|
252
276
|
except KeyboardInterrupt:
|
|
253
277
|
# Also catch KeyboardInterrupt from as_completed() itself
|
|
254
278
|
interrupted = True
|
|
@@ -278,7 +302,12 @@ class CodeAnalyzer:
|
|
|
278
302
|
# Update progress for skipped files
|
|
279
303
|
skipped_count = len(python_files) - len(files_to_analyze)
|
|
280
304
|
if skipped_count > 0:
|
|
281
|
-
|
|
305
|
+
features_count = len(self.features)
|
|
306
|
+
progress.update(
|
|
307
|
+
task3,
|
|
308
|
+
completed=len(python_files),
|
|
309
|
+
description=f"[cyan]Phase 3: Analyzing files and extracting features... ({features_count} features discovered)",
|
|
310
|
+
)
|
|
282
311
|
|
|
283
312
|
progress.update(
|
|
284
313
|
task3,
|
{specfact_cli-0.14.0 → specfact_cli-0.15.0}/src/specfact_cli/analyzers/control_flow_analyzer.py
RENAMED
|
@@ -53,7 +53,7 @@ class ControlFlowAnalyzer:
|
|
|
53
53
|
method_name: Name of the method
|
|
54
54
|
|
|
55
55
|
Returns:
|
|
56
|
-
Dictionary with scenario types as keys and lists of
|
|
56
|
+
Dictionary with scenario types as keys and lists of scenario descriptions as values
|
|
57
57
|
"""
|
|
58
58
|
scenarios: dict[str, list[str]] = {
|
|
59
59
|
"primary": [],
|
|
@@ -67,9 +67,7 @@ class ControlFlowAnalyzer:
|
|
|
67
67
|
|
|
68
68
|
# If no scenarios found, generate default primary scenario
|
|
69
69
|
if not any(scenarios.values()):
|
|
70
|
-
scenarios["primary"].append(
|
|
71
|
-
f"Given {class_name} instance, When {method_name} is called, Then method executes successfully"
|
|
72
|
-
)
|
|
70
|
+
scenarios["primary"].append(f"{method_name} executes successfully")
|
|
73
71
|
|
|
74
72
|
return scenarios
|
|
75
73
|
|
|
@@ -103,15 +101,13 @@ class ControlFlowAnalyzer:
|
|
|
103
101
|
# Primary scenario: if branch (happy path)
|
|
104
102
|
if if_node.body:
|
|
105
103
|
primary_action = self._extract_action_from_body(if_node.body)
|
|
106
|
-
scenarios["primary"].append(
|
|
107
|
-
f"Given {class_name} instance, When {method_name} is called with {condition}, Then {primary_action}"
|
|
108
|
-
)
|
|
104
|
+
scenarios["primary"].append(f"{method_name} called with {condition}: {primary_action}")
|
|
109
105
|
|
|
110
106
|
# Alternate scenario: else branch
|
|
111
107
|
if if_node.orelse:
|
|
112
108
|
alternate_action = self._extract_action_from_body(if_node.orelse)
|
|
113
109
|
scenarios["alternate"].append(
|
|
114
|
-
f"
|
|
110
|
+
f"{method_name} called with {self._negate_condition(condition)}: {alternate_action}"
|
|
115
111
|
)
|
|
116
112
|
|
|
117
113
|
@beartype
|
|
@@ -122,9 +118,7 @@ class ControlFlowAnalyzer:
|
|
|
122
118
|
# Primary scenario: try block (happy path)
|
|
123
119
|
if try_node.body:
|
|
124
120
|
primary_action = self._extract_action_from_body(try_node.body)
|
|
125
|
-
scenarios["primary"].append(
|
|
126
|
-
f"Given {class_name} instance, When {method_name} is called, Then {primary_action}"
|
|
127
|
-
)
|
|
121
|
+
scenarios["primary"].append(f"{method_name} executes: {primary_action}")
|
|
128
122
|
|
|
129
123
|
# Exception scenarios: except blocks
|
|
130
124
|
for handler in try_node.handlers:
|
|
@@ -133,22 +127,16 @@ class ControlFlowAnalyzer:
|
|
|
133
127
|
exception_type = self._extract_exception_type(handler.type)
|
|
134
128
|
|
|
135
129
|
exception_action = self._extract_action_from_body(handler.body) if handler.body else "error is handled"
|
|
136
|
-
scenarios["exception"].append(
|
|
137
|
-
f"Given {class_name} instance, When {method_name} is called and {exception_type} occurs, Then {exception_action}"
|
|
138
|
-
)
|
|
130
|
+
scenarios["exception"].append(f"{method_name} raises {exception_type}: {exception_action}")
|
|
139
131
|
|
|
140
132
|
# Check for retry/recovery logic in exception handler
|
|
141
133
|
if self._has_retry_logic(handler.body):
|
|
142
|
-
scenarios["recovery"].append(
|
|
143
|
-
f"Given {class_name} instance, When {method_name} fails with {exception_type}, Then system retries and recovers"
|
|
144
|
-
)
|
|
134
|
+
scenarios["recovery"].append(f"{method_name} retries and recovers after {exception_type}")
|
|
145
135
|
|
|
146
136
|
# Recovery scenario: finally block or retry logic
|
|
147
137
|
if try_node.finalbody:
|
|
148
138
|
recovery_action = self._extract_action_from_body(try_node.finalbody)
|
|
149
|
-
scenarios["recovery"].append(
|
|
150
|
-
f"Given {class_name} instance, When {method_name} completes or fails, Then {recovery_action}"
|
|
151
|
-
)
|
|
139
|
+
scenarios["recovery"].append(f"{method_name} cleanup: {recovery_action}")
|
|
152
140
|
|
|
153
141
|
@beartype
|
|
154
142
|
def _extract_loop_scenario(
|
|
@@ -157,9 +145,7 @@ class ControlFlowAnalyzer:
|
|
|
157
145
|
"""Extract scenario from loop (might indicate retry logic)."""
|
|
158
146
|
# Check if loop contains retry/retry logic
|
|
159
147
|
if self._has_retry_logic(loop_node.body):
|
|
160
|
-
scenarios["recovery"].append(
|
|
161
|
-
f"Given {class_name} instance, When {method_name} is called, Then system retries on failure until success"
|
|
162
|
-
)
|
|
148
|
+
scenarios["recovery"].append(f"{method_name} retries on failure until success")
|
|
163
149
|
|
|
164
150
|
@beartype
|
|
165
151
|
def _extract_condition(self, test_node: ast.AST) -> str:
|
|
@@ -72,6 +72,7 @@ from specfact_cli.commands import (
|
|
|
72
72
|
sync,
|
|
73
73
|
)
|
|
74
74
|
from specfact_cli.modes import OperationalMode, detect_mode
|
|
75
|
+
from specfact_cli.utils.progressive_disclosure import ProgressiveDisclosureGroup
|
|
75
76
|
from specfact_cli.utils.structured_io import StructuredFormat
|
|
76
77
|
|
|
77
78
|
|
|
@@ -120,7 +121,8 @@ app = typer.Typer(
|
|
|
120
121
|
help="SpecFact CLI - Spec → Contract → Sentinel for Contract-Driven Development",
|
|
121
122
|
add_completion=True, # Enable Typer's built-in completion (works natively for bash/zsh/fish without extensions)
|
|
122
123
|
rich_markup_mode="rich",
|
|
123
|
-
context_settings={"help_option_names": ["-h", "--help"]}, # Add
|
|
124
|
+
context_settings={"help_option_names": ["-h", "--help", "--help-advanced", "-ha"]}, # Add aliases for help
|
|
125
|
+
cls=ProgressiveDisclosureGroup, # Use custom group for progressive disclosure
|
|
124
126
|
)
|
|
125
127
|
|
|
126
128
|
console = Console()
|
|
@@ -352,6 +354,11 @@ app.add_typer(
|
|
|
352
354
|
|
|
353
355
|
def cli_main() -> None:
|
|
354
356
|
"""Entry point for the CLI application."""
|
|
357
|
+
# Intercept --help-advanced before Typer processes it
|
|
358
|
+
from specfact_cli.utils.progressive_disclosure import intercept_help_advanced
|
|
359
|
+
|
|
360
|
+
intercept_help_advanced()
|
|
361
|
+
|
|
355
362
|
# Normalize shell names in argv for Typer's built-in completion commands
|
|
356
363
|
normalize_shell_in_argv()
|
|
357
364
|
|
|
@@ -129,7 +129,7 @@ def enforce_sdd(
|
|
|
129
129
|
sdd: Path | None = typer.Option(
|
|
130
130
|
None,
|
|
131
131
|
"--sdd",
|
|
132
|
-
help="Path to SDD manifest. Default: .specfact/sdd/<bundle-name>.<format>",
|
|
132
|
+
help="Path to SDD manifest. Default: bundle-specific .specfact/projects/<bundle-name>/sdd.<format> (Phase 8.5), with fallback to legacy .specfact/sdd/<bundle-name>.<format>",
|
|
133
133
|
),
|
|
134
134
|
# Output/Results
|
|
135
135
|
output_format: str = typer.Option(
|
|
@@ -140,7 +140,7 @@ def enforce_sdd(
|
|
|
140
140
|
out: Path | None = typer.Option(
|
|
141
141
|
None,
|
|
142
142
|
"--out",
|
|
143
|
-
help="Output file path. Default: .specfact/reports/
|
|
143
|
+
help="Output file path. Default: bundle-specific .specfact/projects/<bundle-name>/reports/enforcement/report-<timestamp>.<format> (Phase 8.5)",
|
|
144
144
|
),
|
|
145
145
|
# Behavior/Options
|
|
146
146
|
no_interactive: bool = typer.Option(
|
|
@@ -213,7 +213,8 @@ def enforce_sdd(
|
|
|
213
213
|
discovered_sdd = find_sdd_for_bundle(bundle, base_path, sdd)
|
|
214
214
|
if discovered_sdd is None:
|
|
215
215
|
console.print("[bold red]✗[/bold red] SDD manifest not found")
|
|
216
|
-
console.print(f"[dim]Searched for: .specfact/
|
|
216
|
+
console.print(f"[dim]Searched for: .specfact/projects/{bundle}/sdd.yaml (bundle-specific, Phase 8.5)[/dim]")
|
|
217
|
+
console.print(f"[dim]Fallback: .specfact/sdd/{bundle}.yaml or .specfact/sdd/{bundle}.json[/dim]")
|
|
217
218
|
console.print("[dim]Legacy fallback: .specfact/sdd.yaml or .specfact/sdd.json[/dim]")
|
|
218
219
|
console.print(f"[dim]Create one with: specfact plan harden {bundle}[/dim]")
|
|
219
220
|
raise typer.Exit(1)
|
|
@@ -382,15 +383,15 @@ def enforce_sdd(
|
|
|
382
383
|
else:
|
|
383
384
|
console.print("[dim]No API contracts found in bundle[/dim]")
|
|
384
385
|
|
|
385
|
-
# Generate output report
|
|
386
|
+
# Generate output report (Phase 8.5: bundle-specific location)
|
|
386
387
|
output_format_str = output_format.lower()
|
|
387
388
|
if out is None:
|
|
388
|
-
|
|
389
|
-
reports_dir = Path(".") / SpecFactStructure.ROOT / "reports" / "sdd"
|
|
390
|
-
reports_dir.mkdir(parents=True, exist_ok=True)
|
|
391
|
-
timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
|
|
389
|
+
# Use bundle-specific enforcement report path
|
|
392
390
|
extension = "md" if output_format_str == "markdown" else output_format_str
|
|
393
|
-
out =
|
|
391
|
+
out = SpecFactStructure.get_bundle_enforcement_report_path(bundle_name=bundle, base_path=base_path)
|
|
392
|
+
# Update extension if needed
|
|
393
|
+
if extension != "yaml" and out.suffix != f".{extension}":
|
|
394
|
+
out = out.with_suffix(f".{extension}")
|
|
394
395
|
|
|
395
396
|
# Save report
|
|
396
397
|
if output_format_str == "markdown":
|
|
@@ -59,7 +59,7 @@ def generate_contracts(
|
|
|
59
59
|
sdd: Path | None = typer.Option(
|
|
60
60
|
None,
|
|
61
61
|
"--sdd",
|
|
62
|
-
help="Path to SDD manifest. Default: .specfact/
|
|
62
|
+
help="Path to SDD manifest. Default: bundle-specific .specfact/projects/<bundle-name>/sdd.yaml if --bundle specified (Phase 8.5), else .specfact/sdd.yaml. Ignored if --bundle is specified.",
|
|
63
63
|
),
|
|
64
64
|
plan: Path | None = typer.Option(
|
|
65
65
|
None,
|
|
@@ -149,7 +149,7 @@ def generate_contracts(
|
|
|
149
149
|
# Detect bundle format to determine SDD path
|
|
150
150
|
format_type, _ = detect_bundle_format(plan_path)
|
|
151
151
|
if format_type == BundleFormat.MODULAR:
|
|
152
|
-
# Modular bundle: SDD is at .specfact/
|
|
152
|
+
# Modular bundle: SDD is at .specfact/projects/<bundle-name>/sdd.yaml (Phase 8.5)
|
|
153
153
|
if plan_path.is_dir():
|
|
154
154
|
bundle_name = plan_path.name
|
|
155
155
|
else:
|
|
@@ -321,6 +321,7 @@ def generate_contracts_prompt(
|
|
|
321
321
|
None,
|
|
322
322
|
"--output",
|
|
323
323
|
help=("Output file path (currently unused, prompt saved to .specfact/prompts/)"),
|
|
324
|
+
hidden=True, # Hidden by default, shown with --help-advanced
|
|
324
325
|
),
|
|
325
326
|
) -> None:
|
|
326
327
|
"""
|
|
@@ -1360,7 +1361,7 @@ def generate_tasks(
|
|
|
1360
1361
|
out: Path | None = typer.Option(
|
|
1361
1362
|
None,
|
|
1362
1363
|
"--out",
|
|
1363
|
-
help="Output file path. Default: .specfact/
|
|
1364
|
+
help="Output file path. Default: bundle-specific .specfact/projects/<bundle-name>/tasks.yaml (Phase 8.5)",
|
|
1364
1365
|
),
|
|
1365
1366
|
# Behavior/Options
|
|
1366
1367
|
no_interactive: bool = typer.Option(
|
|
@@ -1452,17 +1453,20 @@ def generate_tasks(
|
|
|
1452
1453
|
print_info("Generating task breakdown...")
|
|
1453
1454
|
task_list = generate_tasks_func(project_bundle, sdd_manifest, bundle)
|
|
1454
1455
|
|
|
1455
|
-
# Determine output path
|
|
1456
|
+
# Determine output path (Phase 8.5: bundle-specific location)
|
|
1456
1457
|
if out is None:
|
|
1457
|
-
|
|
1458
|
-
|
|
1458
|
+
# Use bundle-specific tasks path (Phase 8.5)
|
|
1459
|
+
out = SpecFactStructure.get_bundle_tasks_path(bundle_name=bundle, base_path=base_path)
|
|
1460
|
+
# Ensure parent directory exists
|
|
1461
|
+
out.parent.mkdir(parents=True, exist_ok=True)
|
|
1462
|
+
# Update extension if needed
|
|
1459
1463
|
format_ext = output_format.lower()
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1464
|
+
if format_ext == "yaml":
|
|
1465
|
+
out = out.with_suffix(".yaml")
|
|
1466
|
+
elif format_ext == "json":
|
|
1467
|
+
out = out.with_suffix(".json")
|
|
1468
|
+
else:
|
|
1469
|
+
out = out.with_suffix(".md")
|
|
1466
1470
|
else:
|
|
1467
1471
|
# Ensure correct extension
|
|
1468
1472
|
if output_format.lower() == "yaml":
|
|
@@ -70,9 +70,9 @@ def implement_tasks(
|
|
|
70
70
|
- **Behavior/Options**: --dry-run, --skip-validation, --no-interactive
|
|
71
71
|
|
|
72
72
|
**Examples:**
|
|
73
|
-
specfact implement tasks .specfact/
|
|
74
|
-
specfact implement tasks .specfact/
|
|
75
|
-
specfact implement tasks .specfact/
|
|
73
|
+
specfact implement tasks .specfact/projects/bundle-name/tasks.yaml
|
|
74
|
+
specfact implement tasks .specfact/projects/bundle-name/tasks.yaml --phase setup
|
|
75
|
+
specfact implement tasks .specfact/projects/bundle-name/tasks.yaml --task TASK-001 --dry-run
|
|
76
76
|
"""
|
|
77
77
|
from specfact_cli.telemetry import telemetry
|
|
78
78
|
|