specfact-cli 0.11.4__tar.gz → 0.11.5__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.
Files changed (144) hide show
  1. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/PKG-INFO +1 -1
  2. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/pyproject.toml +1 -1
  3. specfact_cli-0.11.5/resources/prompts/specfact.03-review.md +220 -0
  4. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/__init__.py +1 -1
  5. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/__init__.py +1 -1
  6. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/commands/plan.py +8 -2
  7. specfact_cli-0.11.5/src/specfact_cli/utils/progress.py +182 -0
  8. specfact_cli-0.11.4/resources/prompts/specfact.03-review.md +0 -112
  9. specfact_cli-0.11.4/src/specfact_cli/utils/progress.py +0 -126
  10. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/.gitignore +0 -0
  11. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/LICENSE.md +0 -0
  12. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/README.md +0 -0
  13. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/resources/mappings/node-async.yaml +0 -0
  14. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/resources/mappings/python-async.yaml +0 -0
  15. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/resources/mappings/speckit-default.yaml +0 -0
  16. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/resources/prompts/shared/cli-enforcement.md +0 -0
  17. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/resources/prompts/specfact.01-import.md +0 -0
  18. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/resources/prompts/specfact.02-plan.md +0 -0
  19. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/resources/prompts/specfact.04-sdd.md +0 -0
  20. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/resources/prompts/specfact.05-enforce.md +0 -0
  21. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/resources/prompts/specfact.06-sync.md +0 -0
  22. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/resources/prompts/specfact.compare.md +0 -0
  23. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/resources/prompts/specfact.validate.md +0 -0
  24. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/resources/schemas/deviation.schema.json +0 -0
  25. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/resources/schemas/plan.schema.json +0 -0
  26. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/resources/schemas/protocol.schema.json +0 -0
  27. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/resources/templates/github-action.yml.j2 +0 -0
  28. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/resources/templates/plan.bundle.yaml.j2 +0 -0
  29. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/resources/templates/pr-template.md.j2 +0 -0
  30. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/resources/templates/protocol.yaml.j2 +0 -0
  31. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/resources/templates/telemetry.yaml.example +0 -0
  32. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/agents/__init__.py +0 -0
  33. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/agents/analyze_agent.py +0 -0
  34. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/agents/base.py +0 -0
  35. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/agents/plan_agent.py +0 -0
  36. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/agents/registry.py +0 -0
  37. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/agents/sync_agent.py +0 -0
  38. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/analyzers/__init__.py +0 -0
  39. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/analyzers/ambiguity_scanner.py +0 -0
  40. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/analyzers/code_analyzer.py +0 -0
  41. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/analyzers/constitution_evidence_extractor.py +0 -0
  42. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/analyzers/contract_extractor.py +0 -0
  43. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/analyzers/control_flow_analyzer.py +0 -0
  44. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/analyzers/graph_analyzer.py +0 -0
  45. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/analyzers/relationship_mapper.py +0 -0
  46. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/analyzers/requirement_extractor.py +0 -0
  47. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/analyzers/test_pattern_extractor.py +0 -0
  48. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/cli.py +0 -0
  49. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/commands/__init__.py +0 -0
  50. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/commands/analyze.py +0 -0
  51. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/commands/bridge.py +0 -0
  52. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/commands/drift.py +0 -0
  53. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/commands/enforce.py +0 -0
  54. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/commands/generate.py +0 -0
  55. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/commands/implement.py +0 -0
  56. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/commands/import_cmd.py +0 -0
  57. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/commands/init.py +0 -0
  58. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/commands/migrate.py +0 -0
  59. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/commands/repro.py +0 -0
  60. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/commands/run.py +0 -0
  61. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/commands/sdd.py +0 -0
  62. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/commands/spec.py +0 -0
  63. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/commands/sync.py +0 -0
  64. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/common/__init__.py +0 -0
  65. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/common/logger_setup.py +0 -0
  66. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/common/logging_utils.py +0 -0
  67. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/common/text_utils.py +0 -0
  68. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/common/utils.py +0 -0
  69. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/comparators/__init__.py +0 -0
  70. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/comparators/plan_comparator.py +0 -0
  71. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/enrichers/constitution_enricher.py +0 -0
  72. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/enrichers/plan_enricher.py +0 -0
  73. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/generators/__init__.py +0 -0
  74. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/generators/contract_generator.py +0 -0
  75. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/generators/openapi_extractor.py +0 -0
  76. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/generators/plan_generator.py +0 -0
  77. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/generators/protocol_generator.py +0 -0
  78. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/generators/report_generator.py +0 -0
  79. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/generators/task_generator.py +0 -0
  80. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/generators/test_to_openapi.py +0 -0
  81. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/generators/workflow_generator.py +0 -0
  82. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/importers/__init__.py +0 -0
  83. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/importers/speckit_converter.py +0 -0
  84. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/importers/speckit_scanner.py +0 -0
  85. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/integrations/__init__.py +0 -0
  86. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/integrations/specmatic.py +0 -0
  87. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/migrations/__init__.py +0 -0
  88. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/migrations/plan_migrator.py +0 -0
  89. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/models/__init__.py +0 -0
  90. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/models/bridge.py +0 -0
  91. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/models/deviation.py +0 -0
  92. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/models/enforcement.py +0 -0
  93. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/models/plan.py +0 -0
  94. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/models/project.py +0 -0
  95. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/models/protocol.py +0 -0
  96. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/models/quality.py +0 -0
  97. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/models/sdd.py +0 -0
  98. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/models/source_tracking.py +0 -0
  99. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/models/task.py +0 -0
  100. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/modes/__init__.py +0 -0
  101. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/modes/detector.py +0 -0
  102. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/modes/router.py +0 -0
  103. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/resources/semgrep/async.yml +0 -0
  104. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/resources/semgrep/code-quality.yml +0 -0
  105. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/resources/semgrep/feature-detection.yml +0 -0
  106. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/runtime.py +0 -0
  107. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/sync/__init__.py +0 -0
  108. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/sync/bridge_probe.py +0 -0
  109. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/sync/bridge_sync.py +0 -0
  110. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/sync/bridge_watch.py +0 -0
  111. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/sync/change_detector.py +0 -0
  112. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/sync/code_to_spec.py +0 -0
  113. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/sync/drift_detector.py +0 -0
  114. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/sync/repository_sync.py +0 -0
  115. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/sync/spec_to_code.py +0 -0
  116. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/sync/spec_to_tests.py +0 -0
  117. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/sync/speckit_sync.py +0 -0
  118. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/sync/watcher.py +0 -0
  119. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/telemetry.py +0 -0
  120. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/templates/__init__.py +0 -0
  121. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/templates/bridge_templates.py +0 -0
  122. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/utils/__init__.py +0 -0
  123. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/utils/acceptance_criteria.py +0 -0
  124. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/utils/bundle_loader.py +0 -0
  125. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/utils/console.py +0 -0
  126. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/utils/enrichment_context.py +0 -0
  127. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/utils/enrichment_parser.py +0 -0
  128. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/utils/feature_keys.py +0 -0
  129. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/utils/git.py +0 -0
  130. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/utils/github_annotations.py +0 -0
  131. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/utils/ide_setup.py +0 -0
  132. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/utils/incremental_check.py +0 -0
  133. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/utils/optional_deps.py +0 -0
  134. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/utils/prompts.py +0 -0
  135. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/utils/sdd_discovery.py +0 -0
  136. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/utils/source_scanner.py +0 -0
  137. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/utils/structure.py +0 -0
  138. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/utils/structured_io.py +0 -0
  139. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/utils/yaml_utils.py +0 -0
  140. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/validators/__init__.py +0 -0
  141. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/validators/contract_validator.py +0 -0
  142. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/validators/fsm.py +0 -0
  143. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/src/specfact_cli/validators/repro_checker.py +0 -0
  144. {specfact_cli-0.11.4 → specfact_cli-0.11.5}/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.11.4
3
+ Version: 0.11.5
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.11.4"
7
+ version = "0.11.5"
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"
@@ -0,0 +1,220 @@
1
+ ---
2
+ description: Review project bundle to identify ambiguities, resolve gaps, and prepare for promotion.
3
+ ---
4
+
5
+ # SpecFact Review Command
6
+
7
+ ## User Input
8
+
9
+ ```text
10
+ $ARGUMENTS
11
+ ```
12
+
13
+ You **MUST** consider the user input before proceeding (if not empty).
14
+
15
+ ## Purpose
16
+
17
+ Review project bundle to identify/resolve ambiguities and missing information. Asks targeted questions for promotion readiness.
18
+
19
+ **When to use:** After import/creation, before promotion, when clarification needed.
20
+
21
+ **Quick:** `/specfact.03-review` (uses active plan) or `/specfact.03-review legacy-api`
22
+
23
+ ## Parameters
24
+
25
+ ### Target/Input
26
+
27
+ - `bundle NAME` (optional argument) - Project bundle name (e.g., legacy-api, auth-module). Default: active plan (set via `plan select`)
28
+ - `--category CATEGORY` - Focus on specific taxonomy category. Default: None (all categories)
29
+
30
+ ### Output/Results
31
+
32
+ - `--list-questions` - Output questions in JSON format. Default: False
33
+ - `--list-findings` - Output all findings in structured format. Default: False
34
+ - `--findings-format FORMAT` - Output format: json, yaml, or table. Default: json for non-interactive, table for interactive
35
+
36
+ ### Behavior/Options
37
+
38
+ - `--no-interactive` - Non-interactive mode (for CI/CD). Default: False (interactive mode)
39
+ - `--answers JSON` - JSON object with question_id -> answer mappings. Default: None
40
+ - `--auto-enrich` - Automatically enrich vague acceptance criteria. Default: False
41
+
42
+ ### Advanced/Configuration
43
+
44
+ - `--max-questions INT` - Maximum questions per session. Default: 5 (range: 1-10)
45
+
46
+ ## Workflow
47
+
48
+ ### Step 1: Parse Arguments
49
+
50
+ - Extract bundle name (defaults to active plan if not specified)
51
+ - Extract optional parameters (max-questions, category, etc.)
52
+
53
+ ### Step 2: Execute CLI to Get Findings
54
+
55
+ **First, get findings to understand what needs enrichment:**
56
+
57
+ ```bash
58
+ specfact plan review [<bundle-name>] --list-findings --findings-format json
59
+ # Uses active plan if bundle not specified
60
+ ```
61
+
62
+ This outputs all ambiguities and missing information in structured format.
63
+
64
+ ### Step 3: Create Enrichment Report (if needed)
65
+
66
+ Based on the findings, create a Markdown enrichment report that addresses:
67
+
68
+ - **Business Context**: Priorities, constraints, unknowns
69
+ - **Confidence Adjustments**: Feature confidence score updates (if needed)
70
+ - **Missing Features**: New features to add (if any)
71
+ - **Manual Updates**: Guidance for updating `idea.yaml` fields like `target_users`, `value_hypothesis`, `narrative`
72
+
73
+ **Enrichment Report Format:**
74
+
75
+ ```markdown
76
+ ## Business Context
77
+
78
+ ### Priorities
79
+ - Priority 1
80
+ - Priority 2
81
+
82
+ ### Constraints
83
+ - Constraint 1
84
+ - Constraint 2
85
+
86
+ ### Unknowns
87
+ - Unknown 1
88
+ - Unknown 2
89
+
90
+ ## Confidence Adjustments
91
+
92
+ FEATURE-KEY → 0.95
93
+ FEATURE-OTHER → 0.8
94
+
95
+ ## Missing Features
96
+
97
+ (If any features are missing)
98
+
99
+ ## Recommendations for Manual Updates
100
+
101
+ ### idea.yaml Updates Required
102
+
103
+ **target_users:**
104
+ - Primary: [description]
105
+ - Secondary: [description]
106
+
107
+ **value_hypothesis:**
108
+ [Value proposition]
109
+
110
+ **narrative:**
111
+ [Improved narrative]
112
+ ```
113
+
114
+ ### Step 4: Apply Enrichment
115
+
116
+ #### Option A: Use enrichment to answer review questions
117
+
118
+ Create answers JSON from enrichment report and use with review:
119
+
120
+ ```bash
121
+ specfact plan review [<bundle-name>] --answers '{"Q001": "answer1", "Q002": "answer2"}'
122
+ ```
123
+
124
+ #### Option B: Update idea fields directly via CLI
125
+
126
+ Use `plan update-idea` to update idea fields from enrichment recommendations:
127
+
128
+ ```bash
129
+ specfact plan update-idea --bundle [<bundle-name>] --value-hypothesis "..." --narrative "..." --target-users "..."
130
+ ```
131
+
132
+ #### Option C: Apply enrichment via import (only if bundle needs regeneration)
133
+
134
+ ```bash
135
+ specfact import from-code [<bundle-name>] --repo . --enrichment enrichment-report.md
136
+ ```
137
+
138
+ **Note:**
139
+
140
+ - **Preferred**: Use Option A (answers) or Option B (update-idea) for most cases
141
+ - Only use Option C if you need to regenerate the bundle
142
+ - Never manually edit `.specfact/` files directly - always use CLI commands
143
+
144
+ ### Step 5: Present Results
145
+
146
+ - Display Q&A, sections touched, coverage summary (initial/updated)
147
+ - Note: Clarifications don't affect hash (stable across review sessions)
148
+ - If enrichment report was created, summarize what was addressed
149
+
150
+ ## CLI Enforcement
151
+
152
+ **CRITICAL**: Always use SpecFact CLI commands. See [CLI Enforcement Rules](./shared/cli-enforcement.md) for details.
153
+
154
+ **Rules:** Execute CLI first, use `--no-interactive` in CI/CD, never modify `.specfact/` directly, use CLI output as grounding.
155
+
156
+ ## Expected Output
157
+
158
+ ### Success
159
+
160
+ ```text
161
+ ✓ Review complete: 5 question(s) answered
162
+
163
+ Project Bundle: legacy-api
164
+ Questions Asked: 5
165
+
166
+ Sections Touched:
167
+ • idea.narrative
168
+ • features[FEATURE-001].acceptance
169
+ • features[FEATURE-002].outcomes
170
+
171
+ Coverage Summary:
172
+ ✅ Functional Scope: clear
173
+ ✅ Technical Constraints: clear
174
+ ⚠️ Business Context: partial
175
+ ```
176
+
177
+ ### Error (Missing Bundle)
178
+
179
+ ```text
180
+ ✗ Project bundle 'legacy-api' not found
181
+ Create one with: specfact plan init legacy-api
182
+ ```
183
+
184
+ ## Common Patterns
185
+
186
+ ```bash
187
+ # Get findings first
188
+ /specfact.03-review --list-findings # List all findings
189
+ /specfact.03-review --list-findings --findings-format json # JSON format for enrichment
190
+
191
+ # Interactive review
192
+ /specfact.03-review # Uses active plan
193
+ /specfact.03-review legacy-api # Specific bundle
194
+ /specfact.03-review --max-questions 3 # Limit questions
195
+ /specfact.03-review --category "Functional Scope" # Focus category
196
+
197
+ # Non-interactive with answers
198
+ /specfact.03-review --answers '{"Q001": "answer"}' # Provide answers directly
199
+ /specfact.03-review --list-questions # Output questions as JSON
200
+
201
+ # Auto-enrichment
202
+ /specfact.03-review --auto-enrich # Auto-enrich vague criteria
203
+ ```
204
+
205
+ ## Enrichment Workflow
206
+
207
+ **Typical workflow when enrichment is needed:**
208
+
209
+ 1. **Get findings**: `specfact plan review --list-findings --findings-format json`
210
+ 2. **Analyze findings**: Review missing information (target_users, value_hypothesis, etc.)
211
+ 3. **Create enrichment report**: Write Markdown file addressing findings
212
+ 4. **Apply enrichment**:
213
+ - **Preferred**: Use enrichment to create `--answers` JSON and run `plan review --answers`
214
+ - **Alternative**: Use `plan update-idea` to update idea fields directly
215
+ - **Last resort**: If bundle needs regeneration, use `import from-code --enrichment`
216
+ 5. **Verify**: Run `plan review` again to confirm improvements
217
+
218
+ ## Context
219
+
220
+ {ARGS}
@@ -3,4 +3,4 @@ SpecFact CLI - Spec→Contract→Sentinel tool for contract-driven development.
3
3
  """
4
4
 
5
5
  # Define the package version (kept in sync with pyproject.toml and setup.py)
6
- __version__ = "0.11.4"
6
+ __version__ = "0.11.5"
@@ -9,6 +9,6 @@ This package provides command-line tools for:
9
9
  - Validating reproducibility
10
10
  """
11
11
 
12
- __version__ = "0.11.4"
12
+ __version__ = "0.11.5"
13
13
 
14
14
  __all__ = ["__version__"]
@@ -3607,10 +3607,12 @@ def _handle_no_questions_case(
3607
3607
  )
3608
3608
  console.print(f" {status_icon} {cat.value}: {status.value}")
3609
3609
 
3610
+ return
3611
+
3610
3612
 
3611
3613
  @beartype
3612
3614
  @require(lambda questions_to_ask: isinstance(questions_to_ask, list), "Questions must be list")
3613
- @ensure(lambda result: None, "Must return None")
3615
+ @ensure(lambda result: result is None, "Must return None")
3614
3616
  def _handle_list_questions_mode(questions_to_ask: list[tuple[Any, str]]) -> None:
3615
3617
  """
3616
3618
  Handle --list-questions mode by outputting questions as JSON.
@@ -3638,6 +3640,8 @@ def _handle_list_questions_mode(questions_to_ask: list[tuple[Any, str]]) -> None
3638
3640
  sys.stdout.write("\n")
3639
3641
  sys.stdout.flush()
3640
3642
 
3643
+ return
3644
+
3641
3645
 
3642
3646
  @beartype
3643
3647
  @require(lambda answers: isinstance(answers, str), "Answers must be string")
@@ -3824,7 +3828,7 @@ def _ask_questions_interactive(
3824
3828
  @require(lambda report: report is not None, "Report must not be None")
3825
3829
  @require(lambda current_stage: isinstance(current_stage, str), "Current stage must be str")
3826
3830
  @require(lambda today_session: today_session is not None, "Today session must not be None")
3827
- @ensure(lambda result: None, "Must return None")
3831
+ @ensure(lambda result: result is None, "Must return None")
3828
3832
  def _display_review_summary(
3829
3833
  plan_bundle: PlanBundle,
3830
3834
  scanner: Any, # AmbiguityScanner
@@ -3898,6 +3902,8 @@ def _display_review_summary(
3898
3902
  console.print(" • Plan is ready for approval")
3899
3903
  console.print(" • Run: specfact plan promote --stage approved")
3900
3904
 
3905
+ return
3906
+
3901
3907
 
3902
3908
  @app.command("review")
3903
3909
  @beartype
@@ -0,0 +1,182 @@
1
+ """
2
+ Progress display utilities for consistent UI/UX across all commands.
3
+
4
+ This module provides unified progress display functions that ensure
5
+ consistent formatting and user experience across all CLI commands.
6
+ Includes timing information for visibility into operation duration.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import os
12
+ from collections.abc import Callable
13
+ from pathlib import Path
14
+ from time import time
15
+ from typing import Any
16
+
17
+ from rich.console import Console
18
+ from rich.progress import Progress, SpinnerColumn, TextColumn, TimeElapsedColumn
19
+
20
+ from specfact_cli.models.project import ProjectBundle
21
+ from specfact_cli.utils.bundle_loader import load_project_bundle, save_project_bundle
22
+
23
+
24
+ console = Console()
25
+
26
+
27
+ def _is_test_mode() -> bool:
28
+ """Check if running in test mode."""
29
+ return os.environ.get("TEST_MODE") == "true" or os.environ.get("PYTEST_CURRENT_TEST") is not None
30
+
31
+
32
+ def _safe_progress_display(display_console: Console) -> bool:
33
+ """
34
+ Check if it's safe to create a Progress display.
35
+
36
+ Returns True if Progress can be created, False if it should be skipped.
37
+ """
38
+ # Always skip in test mode
39
+ if _is_test_mode():
40
+ return False
41
+
42
+ # Try to detect if a Progress is already active by checking console state
43
+ # This is a best-effort check - we'll catch LiveError if it fails
44
+ try:
45
+ # Rich stores active Live displays in Console._live
46
+ if hasattr(display_console, "_live") and display_console._live is not None:
47
+ return False
48
+ except Exception:
49
+ pass
50
+
51
+ return True
52
+
53
+
54
+ def create_progress_callback(progress: Progress, task_id: Any, prefix: str = "") -> Callable[[int, int, str], None]:
55
+ """
56
+ Create a standardized progress callback function.
57
+
58
+ Args:
59
+ progress: Rich Progress instance
60
+ task_id: Task ID from progress.add_task()
61
+ prefix: Optional prefix for progress messages (e.g., "Loading", "Saving")
62
+
63
+ Returns:
64
+ Callback function that updates progress with n/m counter format
65
+ """
66
+
67
+ def callback(current: int, total: int, artifact: str) -> None:
68
+ """Update progress with n/m counter format."""
69
+ if prefix:
70
+ description = f"{prefix} artifact {current}/{total}: {artifact}"
71
+ else:
72
+ description = f"Processing artifact {current}/{total}: {artifact}"
73
+ progress.update(task_id, description=description)
74
+
75
+ return callback
76
+
77
+
78
+ def load_bundle_with_progress(
79
+ bundle_dir: Path,
80
+ validate_hashes: bool = False,
81
+ console_instance: Console | None = None,
82
+ ) -> ProjectBundle:
83
+ """
84
+ Load project bundle with unified progress display.
85
+
86
+ Uses consistent n/m counter format: "Loading artifact 3/12: FEATURE-001.yaml"
87
+ Includes timing information showing elapsed time.
88
+
89
+ Args:
90
+ bundle_dir: Path to bundle directory
91
+ validate_hashes: Whether to validate file checksums
92
+ console_instance: Optional Console instance (defaults to module console)
93
+
94
+ Returns:
95
+ Loaded ProjectBundle instance
96
+ """
97
+ display_console = console_instance or console
98
+ start_time = time()
99
+
100
+ # Try to use Progress display, but fall back to direct load if it fails
101
+ # (e.g., if another Progress is already active)
102
+ use_progress = _safe_progress_display(display_console)
103
+
104
+ if use_progress:
105
+ try:
106
+ with Progress(
107
+ SpinnerColumn(),
108
+ TextColumn("[progress.description]{task.description}"),
109
+ TimeElapsedColumn(),
110
+ console=display_console,
111
+ ) as progress:
112
+ task = progress.add_task("Loading project bundle...", total=None)
113
+
114
+ progress_callback = create_progress_callback(progress, task, prefix="Loading")
115
+
116
+ bundle = load_project_bundle(
117
+ bundle_dir,
118
+ validate_hashes=validate_hashes,
119
+ progress_callback=progress_callback,
120
+ )
121
+ elapsed = time() - start_time
122
+ progress.update(task, description=f"✓ Bundle loaded ({elapsed:.2f}s)")
123
+ return bundle
124
+ except Exception:
125
+ # If Progress creation fails (e.g., LiveError), fall back to direct load
126
+ pass
127
+
128
+ # No progress display - just load directly
129
+ return load_project_bundle(
130
+ bundle_dir,
131
+ validate_hashes=validate_hashes,
132
+ progress_callback=None,
133
+ )
134
+
135
+
136
+ def save_bundle_with_progress(
137
+ bundle: ProjectBundle,
138
+ bundle_dir: Path,
139
+ atomic: bool = True,
140
+ console_instance: Console | None = None,
141
+ ) -> None:
142
+ """
143
+ Save project bundle with unified progress display.
144
+
145
+ Uses consistent n/m counter format: "Saving artifact 3/12: FEATURE-001.yaml"
146
+ Includes timing information showing elapsed time.
147
+
148
+ Args:
149
+ bundle: ProjectBundle instance to save
150
+ bundle_dir: Path to bundle directory
151
+ atomic: Whether to use atomic writes
152
+ console_instance: Optional Console instance (defaults to module console)
153
+ """
154
+ display_console = console_instance or console
155
+ start_time = time()
156
+
157
+ # Try to use Progress display, but fall back to direct save if it fails
158
+ # (e.g., if another Progress is already active)
159
+ use_progress = _safe_progress_display(display_console)
160
+
161
+ if use_progress:
162
+ try:
163
+ with Progress(
164
+ SpinnerColumn(),
165
+ TextColumn("[progress.description]{task.description}"),
166
+ TimeElapsedColumn(),
167
+ console=display_console,
168
+ ) as progress:
169
+ task = progress.add_task("Saving project bundle...", total=None)
170
+
171
+ progress_callback = create_progress_callback(progress, task, prefix="Saving")
172
+
173
+ save_project_bundle(bundle, bundle_dir, atomic=atomic, progress_callback=progress_callback)
174
+ elapsed = time() - start_time
175
+ progress.update(task, description=f"✓ Bundle saved ({elapsed:.2f}s)")
176
+ return
177
+ except Exception:
178
+ # If Progress creation fails (e.g., LiveError), fall back to direct save
179
+ pass
180
+
181
+ # No progress display - just save directly
182
+ save_project_bundle(bundle, bundle_dir, atomic=atomic, progress_callback=None)
@@ -1,112 +0,0 @@
1
- ---
2
- description: Review project bundle to identify ambiguities, resolve gaps, and prepare for promotion.
3
- ---
4
-
5
- # SpecFact Review Command
6
-
7
- ## User Input
8
-
9
- ```text
10
- $ARGUMENTS
11
- ```
12
-
13
- You **MUST** consider the user input before proceeding (if not empty).
14
-
15
- ## Purpose
16
-
17
- Review project bundle to identify/resolve ambiguities and missing information. Asks targeted questions for promotion readiness.
18
-
19
- **When to use:** After import/creation, before promotion, when clarification needed.
20
-
21
- **Quick:** `/specfact.03-review` (uses active plan) or `/specfact.03-review legacy-api`
22
-
23
- ## Parameters
24
-
25
- ### Target/Input
26
-
27
- - `bundle NAME` (optional argument) - Project bundle name (e.g., legacy-api, auth-module). Default: active plan (set via `plan select`)
28
- - `--category CATEGORY` - Focus on specific taxonomy category. Default: None (all categories)
29
-
30
- ### Output/Results
31
-
32
- - `--list-questions` - Output questions in JSON format. Default: False
33
- - `--list-findings` - Output all findings in structured format. Default: False
34
- - `--findings-format FORMAT` - Output format: json, yaml, or table. Default: json for non-interactive, table for interactive
35
-
36
- ### Behavior/Options
37
-
38
- - `--no-interactive` - Non-interactive mode (for CI/CD). Default: False (interactive mode)
39
- - `--answers JSON` - JSON object with question_id -> answer mappings. Default: None
40
- - `--auto-enrich` - Automatically enrich vague acceptance criteria. Default: False
41
-
42
- ### Advanced/Configuration
43
-
44
- - `--max-questions INT` - Maximum questions per session. Default: 5 (range: 1-10)
45
-
46
- ## Workflow
47
-
48
- ### Step 1: Parse Arguments
49
-
50
- - Extract bundle name (defaults to active plan if not specified)
51
- - Extract optional parameters (max-questions, category, etc.)
52
-
53
- ### Step 2: Execute CLI
54
-
55
- ```bash
56
- specfact plan review [<bundle-name>] [--max-questions <n>] [--category <category>] [--list-questions] [--list-findings] [--answers JSON]
57
- # Uses active plan if bundle not specified
58
- ```
59
-
60
- ### Step 3: Present Results
61
-
62
- - Display Q&A, sections touched, coverage summary (initial/updated)
63
- - Note: Clarifications don't affect hash (stable across review sessions)
64
-
65
- ## CLI Enforcement
66
-
67
- **CRITICAL**: Always use SpecFact CLI commands. See [CLI Enforcement Rules](./shared/cli-enforcement.md) for details.
68
-
69
- **Rules:** Execute CLI first, use `--no-interactive` in CI/CD, never modify `.specfact/` directly, use CLI output as grounding.
70
-
71
- ## Expected Output
72
-
73
- ### Success
74
-
75
- ```text
76
- ✓ Review complete: 5 question(s) answered
77
-
78
- Project Bundle: legacy-api
79
- Questions Asked: 5
80
-
81
- Sections Touched:
82
- • idea.narrative
83
- • features[FEATURE-001].acceptance
84
- • features[FEATURE-002].outcomes
85
-
86
- Coverage Summary:
87
- ✅ Functional Scope: clear
88
- ✅ Technical Constraints: clear
89
- ⚠️ Business Context: partial
90
- ```
91
-
92
- ### Error (Missing Bundle)
93
-
94
- ```text
95
- ✗ Project bundle 'legacy-api' not found
96
- Create one with: specfact plan init legacy-api
97
- ```
98
-
99
- ## Common Patterns
100
-
101
- ```bash
102
- /specfact.03-review # Uses active plan
103
- /specfact.03-review legacy-api # Specific bundle
104
- /specfact.03-review --max-questions 3 # Limit questions
105
- /specfact.03-review --category "Functional Scope" # Focus category
106
- /specfact.03-review --list-questions # JSON output
107
- /specfact.03-review --auto-enrich # Auto-enrichment
108
- ```
109
-
110
- ## Context
111
-
112
- {ARGS}