specfact-cli 0.4.2__tar.gz → 0.5.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.
Files changed (64) hide show
  1. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/.gitignore +3 -0
  2. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/PKG-INFO +5 -96
  3. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/README.md +3 -95
  4. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/pyproject.toml +5 -1
  5. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/__init__.py +1 -1
  6. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/__init__.py +1 -1
  7. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/commands/plan.py +170 -4
  8. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/commands/sync.py +245 -110
  9. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/sync/__init__.py +10 -1
  10. specfact_cli-0.5.0/src/specfact_cli/sync/watcher.py +268 -0
  11. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/validators/repro_checker.py +22 -1
  12. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/LICENSE.md +0 -0
  13. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/agents/__init__.py +0 -0
  14. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/agents/analyze_agent.py +0 -0
  15. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/agents/base.py +0 -0
  16. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/agents/plan_agent.py +0 -0
  17. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/agents/registry.py +0 -0
  18. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/agents/sync_agent.py +0 -0
  19. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/analyzers/__init__.py +0 -0
  20. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/analyzers/code_analyzer.py +0 -0
  21. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/cli.py +0 -0
  22. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/commands/__init__.py +0 -0
  23. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/commands/enforce.py +0 -0
  24. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/commands/import_cmd.py +0 -0
  25. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/commands/init.py +0 -0
  26. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/commands/repro.py +0 -0
  27. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/common/__init__.py +0 -0
  28. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/common/logger_setup.py +0 -0
  29. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/common/logging_utils.py +0 -0
  30. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/common/text_utils.py +0 -0
  31. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/common/utils.py +0 -0
  32. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/comparators/__init__.py +0 -0
  33. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/comparators/plan_comparator.py +0 -0
  34. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/generators/__init__.py +0 -0
  35. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/generators/plan_generator.py +0 -0
  36. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/generators/protocol_generator.py +0 -0
  37. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/generators/report_generator.py +0 -0
  38. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/generators/workflow_generator.py +0 -0
  39. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/importers/__init__.py +0 -0
  40. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/importers/speckit_converter.py +0 -0
  41. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/importers/speckit_scanner.py +0 -0
  42. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/models/__init__.py +0 -0
  43. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/models/deviation.py +0 -0
  44. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/models/enforcement.py +0 -0
  45. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/models/plan.py +0 -0
  46. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/models/protocol.py +0 -0
  47. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/modes/__init__.py +0 -0
  48. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/modes/detector.py +0 -0
  49. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/modes/router.py +0 -0
  50. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/resources/semgrep/async.yml +0 -0
  51. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/sync/repository_sync.py +0 -0
  52. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/sync/speckit_sync.py +0 -0
  53. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/utils/__init__.py +0 -0
  54. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/utils/console.py +0 -0
  55. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/utils/feature_keys.py +0 -0
  56. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/utils/git.py +0 -0
  57. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/utils/github_annotations.py +0 -0
  58. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/utils/ide_setup.py +0 -0
  59. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/utils/prompts.py +0 -0
  60. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/utils/structure.py +0 -0
  61. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/utils/yaml_utils.py +0 -0
  62. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/validators/__init__.py +0 -0
  63. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/validators/fsm.py +0 -0
  64. {specfact_cli-0.4.2 → specfact_cli-0.5.0}/src/specfact_cli/validators/schema.py +0 -0
@@ -108,6 +108,9 @@ specs/
108
108
  # Ignore .specfact artifacts
109
109
  .specfact/
110
110
 
111
+ # Ignore reports directory (ephemeral artifacts)
112
+ reports/
113
+
111
114
  # Ignore mcp.json
112
115
  .github/mcp.json
113
116
  .cursor/mcp.json
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specfact-cli
3
- Version: 0.4.2
3
+ Version: 0.5.0
4
4
  Summary: SpecFact CLI - Spec→Contract→Sentinel tool for contract-driven development with automated quality gates
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
@@ -98,6 +98,7 @@ Requires-Dist: ruamel-yaml>=0.18.16
98
98
  Requires-Dist: ruff>=0.14.2
99
99
  Requires-Dist: typer>=0.20.0
100
100
  Requires-Dist: typing-extensions>=4.15.0
101
+ Requires-Dist: watchdog>=6.0.0
101
102
  Provides-Extra: dev
102
103
  Requires-Dist: basedpyright>=1.32.1; extra == 'dev'
103
104
  Requires-Dist: beartype>=0.22.4; extra == 'dev'
@@ -189,105 +190,13 @@ We ran SpecFact CLI **on itself** to prove it works:
189
190
 
190
191
  ---
191
192
 
192
- ## What Can You Do?
193
-
194
- ### 1. 🔄 Import from GitHub Spec-Kit
195
-
196
- Already using Spec-Kit? **Level up to automated enforcement** in one command:
197
-
198
- ```bash
199
- specfact import from-spec-kit --repo ./spec-kit-project --write
200
- ```
201
-
202
- **Result**: Your Spec-Kit artifacts become production-ready contracts with automated quality gates.
203
-
204
- ### 2. 🔍 Analyze Your Existing Code
205
-
206
- Turn brownfield code into a clean spec:
207
-
208
- ```bash
209
- specfact import from-code --repo . --name my-project
210
- ```
211
-
212
- **Result**: Auto-generated plan showing what your code actually does
213
-
214
- ### 3. 📋 Plan New Features
215
-
216
- Start with a spec, not with code:
217
-
218
- ```bash
219
- specfact plan init --interactive
220
- specfact plan add-feature --key FEATURE-001 --title "User Login"
221
- ```
222
-
223
- **Result**: Clear acceptance criteria before writing any code
224
-
225
- ### 4. 🛡️ Enforce Quality
226
-
227
- Set rules that actually block bad code:
228
-
229
- ```bash
230
- specfact enforce stage --preset balanced
231
- ```
232
-
233
- **Modes:**
234
-
235
- - `minimal` - Just observe, never block
236
- - `balanced` - Block critical bugs, warn on others
237
- - `strict` - Block everything suspicious
238
-
239
- ### 5. ✅ Validate Everything
240
-
241
- One command to check it all:
242
-
243
- ```bash
244
- specfact repro
245
- ```
246
-
247
- **Checks:** Contracts, types, async patterns, state machines
248
-
249
- ---
250
-
251
193
  ## Documentation
252
194
 
253
- For complete documentation, see **[docs/README.md](docs/README.md)**.
254
-
255
- **Quick Links:**
256
-
257
- - 📖 **[Getting Started](docs/getting-started/README.md)** - Installation and first steps
258
- - 🎯 **[The Journey: From Spec-Kit to SpecFact](docs/guides/speckit-journey.md)** - Level up from interactive authoring to automated enforcement
259
- - 📋 **[Command Reference](docs/reference/commands.md)** - All commands with examples
260
- - 🤖 **[IDE Integration](docs/guides/ide-integration.md)** - Set up slash commands in your IDE
261
- - 💡 **[Use Cases](docs/guides/use-cases.md)** - Real-world scenarios
262
-
263
- ---
264
-
265
- ## Installation Options
266
-
267
- ### 1. uvx (Easiest)
195
+ **New to SpecFact?** Start with the [Getting Started Guide](docs/getting-started/README.md)
268
196
 
269
- No installation needed:
197
+ **Using Spec-Kit?** See [The Journey: From Spec-Kit to SpecFact](docs/guides/speckit-journey.md)
270
198
 
271
- ```bash
272
- uvx --from specfact-cli specfact plan init
273
- ```
274
-
275
- ### 2. pip
276
-
277
- Install globally:
278
-
279
- ```bash
280
- pip install specfact-cli
281
- specfact --help
282
- ```
283
-
284
- ### 3. Docker
285
-
286
- Run in a container:
287
-
288
- ```bash
289
- docker run ghcr.io/nold-ai/specfact-cli:latest --help
290
- ```
199
+ **Need help?** Browse the [Documentation Hub](docs/README.md)
291
200
 
292
201
  ---
293
202
 
@@ -66,105 +66,13 @@ We ran SpecFact CLI **on itself** to prove it works:
66
66
 
67
67
  ---
68
68
 
69
- ## What Can You Do?
70
-
71
- ### 1. 🔄 Import from GitHub Spec-Kit
72
-
73
- Already using Spec-Kit? **Level up to automated enforcement** in one command:
74
-
75
- ```bash
76
- specfact import from-spec-kit --repo ./spec-kit-project --write
77
- ```
78
-
79
- **Result**: Your Spec-Kit artifacts become production-ready contracts with automated quality gates.
80
-
81
- ### 2. 🔍 Analyze Your Existing Code
82
-
83
- Turn brownfield code into a clean spec:
84
-
85
- ```bash
86
- specfact import from-code --repo . --name my-project
87
- ```
88
-
89
- **Result**: Auto-generated plan showing what your code actually does
90
-
91
- ### 3. 📋 Plan New Features
92
-
93
- Start with a spec, not with code:
94
-
95
- ```bash
96
- specfact plan init --interactive
97
- specfact plan add-feature --key FEATURE-001 --title "User Login"
98
- ```
99
-
100
- **Result**: Clear acceptance criteria before writing any code
101
-
102
- ### 4. 🛡️ Enforce Quality
103
-
104
- Set rules that actually block bad code:
105
-
106
- ```bash
107
- specfact enforce stage --preset balanced
108
- ```
109
-
110
- **Modes:**
111
-
112
- - `minimal` - Just observe, never block
113
- - `balanced` - Block critical bugs, warn on others
114
- - `strict` - Block everything suspicious
115
-
116
- ### 5. ✅ Validate Everything
117
-
118
- One command to check it all:
119
-
120
- ```bash
121
- specfact repro
122
- ```
123
-
124
- **Checks:** Contracts, types, async patterns, state machines
125
-
126
- ---
127
-
128
69
  ## Documentation
129
70
 
130
- For complete documentation, see **[docs/README.md](docs/README.md)**.
131
-
132
- **Quick Links:**
133
-
134
- - 📖 **[Getting Started](docs/getting-started/README.md)** - Installation and first steps
135
- - 🎯 **[The Journey: From Spec-Kit to SpecFact](docs/guides/speckit-journey.md)** - Level up from interactive authoring to automated enforcement
136
- - 📋 **[Command Reference](docs/reference/commands.md)** - All commands with examples
137
- - 🤖 **[IDE Integration](docs/guides/ide-integration.md)** - Set up slash commands in your IDE
138
- - 💡 **[Use Cases](docs/guides/use-cases.md)** - Real-world scenarios
139
-
140
- ---
141
-
142
- ## Installation Options
143
-
144
- ### 1. uvx (Easiest)
71
+ **New to SpecFact?** Start with the [Getting Started Guide](docs/getting-started/README.md)
145
72
 
146
- No installation needed:
73
+ **Using Spec-Kit?** See [The Journey: From Spec-Kit to SpecFact](docs/guides/speckit-journey.md)
147
74
 
148
- ```bash
149
- uvx --from specfact-cli specfact plan init
150
- ```
151
-
152
- ### 2. pip
153
-
154
- Install globally:
155
-
156
- ```bash
157
- pip install specfact-cli
158
- specfact --help
159
- ```
160
-
161
- ### 3. Docker
162
-
163
- Run in a container:
164
-
165
- ```bash
166
- docker run ghcr.io/nold-ai/specfact-cli:latest --help
167
- ```
75
+ **Need help?** Browse the [Documentation Hub](docs/README.md)
168
76
 
169
77
  ---
170
78
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "specfact-cli"
7
- version = "0.4.2"
7
+ version = "0.5.0"
8
8
  description = "SpecFact CLI - Spec→Contract→Sentinel tool for contract-driven development with automated quality gates"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -59,6 +59,9 @@ dependencies = [
59
59
 
60
60
  # Code analysis
61
61
  "ruff>=0.14.2",
62
+
63
+ # File system watching
64
+ "watchdog>=6.0.0",
62
65
  ]
63
66
 
64
67
  [project.optional-dependencies]
@@ -495,6 +498,7 @@ markers = [
495
498
  "asyncio: mark test as async",
496
499
  "integration: marks tests as integration tests",
497
500
  "timeout: mark tests with a timeout",
501
+ "slow: marks tests as slow (deselect with '-m \"not slow\"')",
498
502
  "workflow_coverage: mark test for workflow coverage tracking",
499
503
  "component_coverage: mark test for component coverage tracking",
500
504
  "contract_coverage: mark test for contract coverage tracking",
@@ -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.4.2"
6
+ __version__ = "0.5.0"
@@ -9,6 +9,6 @@ This package provides command-line tools for:
9
9
  - Validating reproducibility
10
10
  """
11
11
 
12
- __version__ = "0.4.2"
12
+ __version__ = "0.5.0"
13
13
 
14
14
  __all__ = ["__version__"]
@@ -543,6 +543,13 @@ def add_story(
543
543
 
544
544
  @app.command("compare")
545
545
  @beartype
546
+ @require(lambda manual: manual is None or isinstance(manual, Path), "Manual must be None or Path")
547
+ @require(lambda auto: auto is None or isinstance(auto, Path), "Auto must be None or Path")
548
+ @require(
549
+ lambda format: isinstance(format, str) and format.lower() in ("markdown", "json", "yaml"),
550
+ "Format must be markdown, json, or yaml",
551
+ )
552
+ @require(lambda out: out is None or isinstance(out, Path), "Out must be None or Path")
546
553
  def compare(
547
554
  manual: Path | None = typer.Option(
548
555
  None,
@@ -554,6 +561,11 @@ def compare(
554
561
  "--auto",
555
562
  help="Auto-derived plan bundle path (default: latest in .specfact/plans/)",
556
563
  ),
564
+ code_vs_plan: bool = typer.Option(
565
+ False,
566
+ "--code-vs-plan",
567
+ help="Alias for comparing code-derived plan vs manual plan (auto-detects latest auto plan)",
568
+ ),
557
569
  format: str = typer.Option(
558
570
  "markdown",
559
571
  "--format",
@@ -566,19 +578,53 @@ def compare(
566
578
  ),
567
579
  ) -> None:
568
580
  """
569
- Compare manual and auto-derived plans.
581
+ Compare manual and auto-derived plans to detect code vs plan drift.
570
582
 
571
- Detects deviations between manually created plans and
572
- reverse-engineered plans from code.
583
+ Detects deviations between manually created plans (intended design) and
584
+ reverse-engineered plans from code (actual implementation). This comparison
585
+ identifies code vs plan drift automatically.
586
+
587
+ Use --code-vs-plan for convenience: automatically compares the latest
588
+ code-derived plan against the manual plan.
573
589
 
574
590
  Example:
575
591
  specfact plan compare --manual .specfact/plans/main.bundle.yaml --auto .specfact/plans/auto-derived-<timestamp>.bundle.yaml
592
+ specfact plan compare --code-vs-plan # Convenience alias
576
593
  """
577
594
  from specfact_cli.utils.structure import SpecFactStructure
578
595
 
579
596
  # Ensure .specfact structure exists
580
597
  SpecFactStructure.ensure_structure()
581
598
 
599
+ # Handle --code-vs-plan convenience alias
600
+ if code_vs_plan:
601
+ # Auto-detect manual plan (default)
602
+ if manual is None:
603
+ manual = SpecFactStructure.get_default_plan_path()
604
+ if not manual.exists():
605
+ print_error(
606
+ f"Default manual plan not found: {manual}\nCreate one with: specfact plan init --interactive"
607
+ )
608
+ raise typer.Exit(1)
609
+ print_info(f"Using default manual plan: {manual}")
610
+
611
+ # Auto-detect latest code-derived plan
612
+ if auto is None:
613
+ auto = SpecFactStructure.get_latest_brownfield_report()
614
+ if auto is None:
615
+ plans_dir = Path(SpecFactStructure.PLANS)
616
+ print_error(
617
+ f"No code-derived plans found in {plans_dir}\nGenerate one with: specfact import from-code --repo ."
618
+ )
619
+ raise typer.Exit(1)
620
+ print_info(f"Using latest code-derived plan: {auto}")
621
+
622
+ # Override help text to emphasize code vs plan drift
623
+ print_section("Code vs Plan Drift Detection")
624
+ console.print(
625
+ "[dim]Comparing intended design (manual plan) vs actual implementation (code-derived plan)[/dim]\n"
626
+ )
627
+
582
628
  # Use default paths if not specified (smart defaults)
583
629
  if manual is None:
584
630
  manual = SpecFactStructure.get_default_plan_path()
@@ -705,7 +751,23 @@ def compare(
705
751
  # Apply enforcement rules if config exists
706
752
  from specfact_cli.utils.structure import SpecFactStructure
707
753
 
708
- config_path = SpecFactStructure.get_enforcement_config_path()
754
+ # Determine base path from plan paths (use manual plan's parent directory)
755
+ base_path = manual.parent if manual else None
756
+ # If base_path is not a repository root, find the repository root
757
+ if base_path:
758
+ # Walk up to find repository root (where .specfact would be)
759
+ current = base_path.resolve()
760
+ while current != current.parent:
761
+ if (current / SpecFactStructure.ROOT).exists():
762
+ base_path = current
763
+ break
764
+ current = current.parent
765
+ else:
766
+ # If we didn't find .specfact, use the plan's directory
767
+ # But resolve to absolute path first
768
+ base_path = manual.parent.resolve()
769
+
770
+ config_path = SpecFactStructure.get_enforcement_config_path(base_path)
709
771
  if config_path.exists():
710
772
  try:
711
773
  from specfact_cli.utils.yaml_utils import load_yaml
@@ -895,9 +957,113 @@ def select(
895
957
  print_info(" - specfact plan promote")
896
958
  print_info(" - specfact plan add-feature")
897
959
  print_info(" - specfact plan add-story")
960
+ print_info(" - specfact plan sync --shared")
898
961
  print_info(" - specfact sync spec-kit")
899
962
 
900
963
 
964
+ @app.command("sync")
965
+ @beartype
966
+ @require(lambda repo: repo is None or isinstance(repo, Path), "Repo must be None or Path")
967
+ @require(lambda plan: plan is None or isinstance(plan, Path), "Plan must be None or Path")
968
+ @require(lambda overwrite: isinstance(overwrite, bool), "Overwrite must be bool")
969
+ @require(lambda watch: isinstance(watch, bool), "Watch must be bool")
970
+ @require(lambda interval: isinstance(interval, int) and interval >= 1, "Interval must be int >= 1")
971
+ def sync(
972
+ shared: bool = typer.Option(
973
+ False,
974
+ "--shared",
975
+ help="Enable shared plans sync (bidirectional sync with Spec-Kit)",
976
+ ),
977
+ repo: Path | None = typer.Option(
978
+ None,
979
+ "--repo",
980
+ help="Path to repository (default: current directory)",
981
+ ),
982
+ plan: Path | None = typer.Option(
983
+ None,
984
+ "--plan",
985
+ help="Path to SpecFact plan bundle for SpecFact → Spec-Kit conversion (default: active plan)",
986
+ ),
987
+ overwrite: bool = typer.Option(
988
+ False,
989
+ "--overwrite",
990
+ help="Overwrite existing Spec-Kit artifacts (delete all existing before sync)",
991
+ ),
992
+ watch: bool = typer.Option(
993
+ False,
994
+ "--watch",
995
+ help="Watch mode for continuous sync",
996
+ ),
997
+ interval: int = typer.Option(
998
+ 5,
999
+ "--interval",
1000
+ help="Watch interval in seconds (default: 5)",
1001
+ min=1,
1002
+ ),
1003
+ ) -> None:
1004
+ """
1005
+ Sync shared plans between Spec-Kit and SpecFact (bidirectional sync).
1006
+
1007
+ This is a convenience wrapper around `specfact sync spec-kit --bidirectional`
1008
+ that enables team collaboration through shared structured plans. The bidirectional
1009
+ sync keeps Spec-Kit artifacts and SpecFact plans synchronized automatically.
1010
+
1011
+ Shared plans enable:
1012
+ - Team collaboration: Multiple developers can work on the same plan
1013
+ - Automated sync: Changes in Spec-Kit automatically sync to SpecFact
1014
+ - Deviation detection: Compare code vs plan drift automatically
1015
+ - Conflict resolution: Automatic conflict detection and resolution
1016
+
1017
+ Example:
1018
+ specfact plan sync --shared # One-time sync
1019
+ specfact plan sync --shared --watch # Continuous sync
1020
+ specfact plan sync --shared --repo ./project # Sync specific repo
1021
+ """
1022
+ from specfact_cli.commands.sync import sync_spec_kit
1023
+ from specfact_cli.utils.structure import SpecFactStructure
1024
+
1025
+ if not shared:
1026
+ print_error("This command requires --shared flag")
1027
+ print_info("Use 'specfact plan sync --shared' to enable shared plans sync")
1028
+ print_info("Or use 'specfact sync spec-kit --bidirectional' for direct sync")
1029
+ raise typer.Exit(1)
1030
+
1031
+ # Use default repo if not specified
1032
+ if repo is None:
1033
+ repo = Path(".").resolve()
1034
+ print_info(f"Using current directory: {repo}")
1035
+
1036
+ # Use default plan if not specified
1037
+ if plan is None:
1038
+ plan = SpecFactStructure.get_default_plan_path()
1039
+ if not plan.exists():
1040
+ print_warning(f"Default plan not found: {plan}")
1041
+ print_info("Using default plan path (will be created if needed)")
1042
+ else:
1043
+ print_info(f"Using active plan: {plan}")
1044
+
1045
+ print_section("Shared Plans Sync")
1046
+ console.print("[dim]Bidirectional sync between Spec-Kit and SpecFact for team collaboration[/dim]\n")
1047
+
1048
+ # Call the underlying sync command
1049
+ try:
1050
+ # Call sync_spec_kit with bidirectional=True
1051
+ sync_spec_kit(
1052
+ repo=repo,
1053
+ bidirectional=True, # Always bidirectional for shared plans
1054
+ plan=plan,
1055
+ overwrite=overwrite,
1056
+ watch=watch,
1057
+ interval=interval,
1058
+ )
1059
+ except typer.Exit:
1060
+ # Re-raise typer.Exit to preserve exit codes
1061
+ raise
1062
+ except Exception as e:
1063
+ print_error(f"Shared plans sync failed: {e}")
1064
+ raise typer.Exit(1) from e
1065
+
1066
+
901
1067
  @app.command("promote")
902
1068
  @beartype
903
1069
  @require(lambda plan: plan is None or isinstance(plan, Path), "Plan must be None or Path")