doit-toolkit-cli 0.1.9__py3-none-any.whl

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 (134) hide show
  1. doit_cli/__init__.py +1356 -0
  2. doit_cli/cli/__init__.py +26 -0
  3. doit_cli/cli/analytics_command.py +616 -0
  4. doit_cli/cli/context_command.py +213 -0
  5. doit_cli/cli/diagram_command.py +304 -0
  6. doit_cli/cli/fixit_command.py +641 -0
  7. doit_cli/cli/hooks_command.py +211 -0
  8. doit_cli/cli/init_command.py +613 -0
  9. doit_cli/cli/memory_command.py +293 -0
  10. doit_cli/cli/status_command.py +117 -0
  11. doit_cli/cli/sync_prompts_command.py +248 -0
  12. doit_cli/cli/validate_command.py +196 -0
  13. doit_cli/cli/verify_command.py +204 -0
  14. doit_cli/cli/workflow_mixin.py +224 -0
  15. doit_cli/cli/xref_command.py +555 -0
  16. doit_cli/formatters/__init__.py +8 -0
  17. doit_cli/formatters/base.py +38 -0
  18. doit_cli/formatters/json_formatter.py +126 -0
  19. doit_cli/formatters/markdown_formatter.py +97 -0
  20. doit_cli/formatters/rich_formatter.py +257 -0
  21. doit_cli/main.py +49 -0
  22. doit_cli/models/__init__.py +139 -0
  23. doit_cli/models/agent.py +74 -0
  24. doit_cli/models/analytics_models.py +384 -0
  25. doit_cli/models/context_config.py +464 -0
  26. doit_cli/models/crossref_models.py +182 -0
  27. doit_cli/models/diagram_models.py +363 -0
  28. doit_cli/models/fixit_models.py +355 -0
  29. doit_cli/models/hook_config.py +125 -0
  30. doit_cli/models/project.py +91 -0
  31. doit_cli/models/results.py +121 -0
  32. doit_cli/models/search_models.py +228 -0
  33. doit_cli/models/status_models.py +195 -0
  34. doit_cli/models/sync_models.py +146 -0
  35. doit_cli/models/template.py +77 -0
  36. doit_cli/models/validation_models.py +175 -0
  37. doit_cli/models/workflow_models.py +319 -0
  38. doit_cli/prompts/__init__.py +5 -0
  39. doit_cli/prompts/fixit_prompts.py +344 -0
  40. doit_cli/prompts/interactive.py +390 -0
  41. doit_cli/rules/__init__.py +5 -0
  42. doit_cli/rules/builtin_rules.py +160 -0
  43. doit_cli/services/__init__.py +79 -0
  44. doit_cli/services/agent_detector.py +168 -0
  45. doit_cli/services/analytics_service.py +218 -0
  46. doit_cli/services/architecture_generator.py +290 -0
  47. doit_cli/services/backup_service.py +204 -0
  48. doit_cli/services/config_loader.py +113 -0
  49. doit_cli/services/context_loader.py +1121 -0
  50. doit_cli/services/coverage_calculator.py +142 -0
  51. doit_cli/services/crossref_service.py +237 -0
  52. doit_cli/services/cycle_time_calculator.py +134 -0
  53. doit_cli/services/date_inferrer.py +349 -0
  54. doit_cli/services/diagram_service.py +337 -0
  55. doit_cli/services/drift_detector.py +109 -0
  56. doit_cli/services/entity_parser.py +301 -0
  57. doit_cli/services/er_diagram_generator.py +197 -0
  58. doit_cli/services/fixit_service.py +699 -0
  59. doit_cli/services/github_service.py +192 -0
  60. doit_cli/services/hook_manager.py +258 -0
  61. doit_cli/services/hook_validator.py +528 -0
  62. doit_cli/services/input_validator.py +322 -0
  63. doit_cli/services/memory_search.py +527 -0
  64. doit_cli/services/mermaid_validator.py +334 -0
  65. doit_cli/services/prompt_transformer.py +91 -0
  66. doit_cli/services/prompt_writer.py +133 -0
  67. doit_cli/services/query_interpreter.py +428 -0
  68. doit_cli/services/report_exporter.py +219 -0
  69. doit_cli/services/report_generator.py +256 -0
  70. doit_cli/services/requirement_parser.py +112 -0
  71. doit_cli/services/roadmap_summarizer.py +209 -0
  72. doit_cli/services/rule_engine.py +443 -0
  73. doit_cli/services/scaffolder.py +215 -0
  74. doit_cli/services/score_calculator.py +172 -0
  75. doit_cli/services/section_parser.py +204 -0
  76. doit_cli/services/spec_scanner.py +327 -0
  77. doit_cli/services/state_manager.py +355 -0
  78. doit_cli/services/status_reporter.py +143 -0
  79. doit_cli/services/task_parser.py +347 -0
  80. doit_cli/services/template_manager.py +710 -0
  81. doit_cli/services/template_reader.py +158 -0
  82. doit_cli/services/user_journey_generator.py +214 -0
  83. doit_cli/services/user_story_parser.py +232 -0
  84. doit_cli/services/validation_service.py +188 -0
  85. doit_cli/services/validator.py +232 -0
  86. doit_cli/services/velocity_tracker.py +173 -0
  87. doit_cli/services/workflow_engine.py +405 -0
  88. doit_cli/templates/agent-file-template.md +28 -0
  89. doit_cli/templates/checklist-template.md +39 -0
  90. doit_cli/templates/commands/doit.checkin.md +363 -0
  91. doit_cli/templates/commands/doit.constitution.md +187 -0
  92. doit_cli/templates/commands/doit.documentit.md +485 -0
  93. doit_cli/templates/commands/doit.fixit.md +181 -0
  94. doit_cli/templates/commands/doit.implementit.md +265 -0
  95. doit_cli/templates/commands/doit.planit.md +262 -0
  96. doit_cli/templates/commands/doit.reviewit.md +355 -0
  97. doit_cli/templates/commands/doit.roadmapit.md +368 -0
  98. doit_cli/templates/commands/doit.scaffoldit.md +458 -0
  99. doit_cli/templates/commands/doit.specit.md +521 -0
  100. doit_cli/templates/commands/doit.taskit.md +304 -0
  101. doit_cli/templates/commands/doit.testit.md +277 -0
  102. doit_cli/templates/config/context.yaml +134 -0
  103. doit_cli/templates/config/hooks.yaml +93 -0
  104. doit_cli/templates/config/validation-rules.yaml +64 -0
  105. doit_cli/templates/github-issue-templates/epic.yml +78 -0
  106. doit_cli/templates/github-issue-templates/feature.yml +116 -0
  107. doit_cli/templates/github-issue-templates/task.yml +129 -0
  108. doit_cli/templates/hooks/.gitkeep +0 -0
  109. doit_cli/templates/hooks/post-commit.sh +25 -0
  110. doit_cli/templates/hooks/post-merge.sh +75 -0
  111. doit_cli/templates/hooks/pre-commit.sh +17 -0
  112. doit_cli/templates/hooks/pre-push.sh +18 -0
  113. doit_cli/templates/memory/completed_roadmap.md +50 -0
  114. doit_cli/templates/memory/constitution.md +125 -0
  115. doit_cli/templates/memory/roadmap.md +61 -0
  116. doit_cli/templates/plan-template.md +146 -0
  117. doit_cli/templates/scripts/bash/check-prerequisites.sh +166 -0
  118. doit_cli/templates/scripts/bash/common.sh +156 -0
  119. doit_cli/templates/scripts/bash/create-new-feature.sh +297 -0
  120. doit_cli/templates/scripts/bash/setup-plan.sh +61 -0
  121. doit_cli/templates/scripts/bash/update-agent-context.sh +675 -0
  122. doit_cli/templates/scripts/powershell/check-prerequisites.ps1 +148 -0
  123. doit_cli/templates/scripts/powershell/common.ps1 +137 -0
  124. doit_cli/templates/scripts/powershell/create-new-feature.ps1 +283 -0
  125. doit_cli/templates/scripts/powershell/setup-plan.ps1 +61 -0
  126. doit_cli/templates/scripts/powershell/update-agent-context.ps1 +406 -0
  127. doit_cli/templates/spec-template.md +159 -0
  128. doit_cli/templates/tasks-template.md +313 -0
  129. doit_cli/templates/vscode-settings.json +14 -0
  130. doit_toolkit_cli-0.1.9.dist-info/METADATA +324 -0
  131. doit_toolkit_cli-0.1.9.dist-info/RECORD +134 -0
  132. doit_toolkit_cli-0.1.9.dist-info/WHEEL +4 -0
  133. doit_toolkit_cli-0.1.9.dist-info/entry_points.txt +2 -0
  134. doit_toolkit_cli-0.1.9.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,126 @@
1
+ """JSON formatter for status output."""
2
+
3
+ import json
4
+ from datetime import datetime
5
+ from typing import Any
6
+
7
+ from ..models.status_models import SpecState, StatusReport
8
+ from .base import StatusFormatter
9
+
10
+
11
+ class JsonFormatter(StatusFormatter):
12
+ """Formats status as JSON for machine consumption.
13
+
14
+ Produces structured JSON output suitable for parsing by other tools,
15
+ CI/CD systems, or programmatic analysis.
16
+ """
17
+
18
+ def format(self, report: StatusReport, verbose: bool = False) -> str:
19
+ """Format the status report as JSON.
20
+
21
+ Args:
22
+ report: The StatusReport to format.
23
+ verbose: Include detailed validation errors.
24
+
25
+ Returns:
26
+ JSON string representation.
27
+ """
28
+ data = self._build_json_data(report, verbose)
29
+ return json.dumps(data, indent=2, default=self._json_serializer)
30
+
31
+ def _build_json_data(self, report: StatusReport, verbose: bool) -> dict[str, Any]:
32
+ """Build the JSON data structure.
33
+
34
+ Args:
35
+ report: The StatusReport to convert.
36
+ verbose: Include detailed validation errors.
37
+
38
+ Returns:
39
+ Dictionary ready for JSON serialization.
40
+ """
41
+ # Build summary
42
+ summary = {
43
+ "total": report.total_count,
44
+ "by_status": {
45
+ "draft": report.by_status.get(SpecState.DRAFT, 0),
46
+ "in_progress": report.by_status.get(SpecState.IN_PROGRESS, 0),
47
+ "complete": report.by_status.get(SpecState.COMPLETE, 0),
48
+ "approved": report.by_status.get(SpecState.APPROVED, 0),
49
+ },
50
+ "blocking": report.blocking_count,
51
+ "validation_pass": sum(
52
+ 1 for s in report.specs if s.validation_passed
53
+ ),
54
+ "validation_fail": sum(
55
+ 1 for s in report.specs
56
+ if s.validation_result is not None and not s.validation_passed
57
+ ),
58
+ "completion_percentage": round(report.completion_percentage, 2),
59
+ "ready_to_commit": report.is_ready_to_commit,
60
+ }
61
+
62
+ # Build specs list
63
+ specs = []
64
+ for spec in report.specs:
65
+ spec_data = {
66
+ "name": spec.name,
67
+ "path": str(spec.path),
68
+ "status": spec.status.value,
69
+ "last_modified": spec.last_modified.isoformat(),
70
+ "is_blocking": spec.is_blocking,
71
+ "error": spec.error,
72
+ }
73
+
74
+ # Add validation info
75
+ if spec.validation_result is not None:
76
+ validation_data = {
77
+ "passed": spec.validation_passed,
78
+ "score": spec.validation_score,
79
+ "error_count": spec.validation_result.error_count,
80
+ "warning_count": spec.validation_result.warning_count,
81
+ }
82
+
83
+ if verbose and spec.validation_result.issues:
84
+ validation_data["issues"] = [
85
+ {
86
+ "rule_id": issue.rule_id,
87
+ "severity": issue.severity.value,
88
+ "line": issue.line_number,
89
+ "message": issue.message,
90
+ "suggestion": issue.suggestion,
91
+ }
92
+ for issue in spec.validation_result.issues
93
+ ]
94
+ else:
95
+ validation_data["issues"] = []
96
+
97
+ spec_data["validation"] = validation_data
98
+ else:
99
+ spec_data["validation"] = None
100
+
101
+ specs.append(spec_data)
102
+
103
+ return {
104
+ "generated_at": report.generated_at.isoformat(),
105
+ "project_root": str(report.project_root),
106
+ "summary": summary,
107
+ "specs": specs,
108
+ }
109
+
110
+ def _json_serializer(self, obj: Any) -> Any:
111
+ """Custom JSON serializer for non-standard types.
112
+
113
+ Args:
114
+ obj: Object to serialize.
115
+
116
+ Returns:
117
+ JSON-serializable representation.
118
+
119
+ Raises:
120
+ TypeError: If object cannot be serialized.
121
+ """
122
+ if isinstance(obj, datetime):
123
+ return obj.isoformat()
124
+ if hasattr(obj, "__dict__"):
125
+ return obj.__dict__
126
+ raise TypeError(f"Object of type {type(obj)} is not JSON serializable")
@@ -0,0 +1,97 @@
1
+ """Markdown formatter for status output."""
2
+
3
+ from ..models.status_models import SpecState, StatusReport
4
+ from .base import StatusFormatter
5
+
6
+
7
+ class MarkdownFormatter(StatusFormatter):
8
+ """Formats status as markdown table for documentation.
9
+
10
+ Produces GitHub-flavored markdown output suitable for embedding
11
+ in documentation, README files, or issue descriptions.
12
+ """
13
+
14
+ def format(self, report: StatusReport, verbose: bool = False) -> str:
15
+ """Format the status report as markdown.
16
+
17
+ Args:
18
+ report: The StatusReport to format.
19
+ verbose: Include detailed validation errors.
20
+
21
+ Returns:
22
+ Markdown string representation.
23
+ """
24
+ lines = []
25
+
26
+ # Header
27
+ lines.append("# Spec Status Report")
28
+ lines.append("")
29
+ lines.append(f"**Generated**: {report.generated_at.strftime('%Y-%m-%d %H:%M:%S')}")
30
+ lines.append(f"**Project**: {report.project_root.name}")
31
+ lines.append("")
32
+
33
+ # Specifications table
34
+ lines.append("## Specifications")
35
+ lines.append("")
36
+
37
+ if report.specs:
38
+ lines.append("| Spec | Status | Validation | Last Modified |")
39
+ lines.append("|------|--------|------------|---------------|")
40
+
41
+ for spec in report.specs:
42
+ status_display = f"{spec.status.emoji} {spec.status.display_name}"
43
+
44
+ # Validation indicator
45
+ if spec.error:
46
+ validation = "❌ Error"
47
+ elif spec.validation_result is None:
48
+ validation = "—"
49
+ elif spec.validation_passed:
50
+ validation = "✅ Pass"
51
+ else:
52
+ validation = f"❌ Fail ({spec.validation_result.error_count})"
53
+
54
+ if spec.is_blocking:
55
+ validation += " ⛔"
56
+
57
+ modified = spec.last_modified.strftime("%Y-%m-%d")
58
+
59
+ lines.append(f"| {spec.name} | {status_display} | {validation} | {modified} |")
60
+
61
+ # Add verbose details
62
+ if verbose:
63
+ lines.append("")
64
+ lines.append("### Validation Details")
65
+ lines.append("")
66
+ for spec in report.specs:
67
+ if spec.validation_result and spec.validation_result.issues:
68
+ lines.append(f"#### {spec.name}")
69
+ lines.append("")
70
+ for issue in spec.validation_result.issues:
71
+ severity = issue.severity.value.upper()
72
+ lines.append(f"- **{severity}** (line {issue.line_number}): {issue.message}")
73
+ if issue.suggestion:
74
+ lines.append(f" - Suggestion: {issue.suggestion}")
75
+ lines.append("")
76
+ else:
77
+ lines.append("*No specifications found.*")
78
+ lines.append("")
79
+
80
+ # Summary
81
+ lines.append("## Summary")
82
+ lines.append("")
83
+ lines.append(f"- **Total Specs**: {report.total_count}")
84
+ lines.append(f"- **Draft**: {report.by_status.get(SpecState.DRAFT, 0)}")
85
+ lines.append(f"- **In Progress**: {report.by_status.get(SpecState.IN_PROGRESS, 0)}")
86
+ lines.append(f"- **Complete**: {report.by_status.get(SpecState.COMPLETE, 0)}")
87
+ lines.append(f"- **Approved**: {report.by_status.get(SpecState.APPROVED, 0)}")
88
+ lines.append(f"- **Completion**: {report.completion_percentage:.0f}%")
89
+
90
+ if report.is_ready_to_commit:
91
+ lines.append("- **Status**: ✅ Ready to commit")
92
+ else:
93
+ lines.append(f"- **Status**: ⛔ {report.blocking_count} spec(s) blocking")
94
+
95
+ lines.append("")
96
+
97
+ return "\n".join(lines)
@@ -0,0 +1,257 @@
1
+ """Rich terminal formatter for status output."""
2
+
3
+ from datetime import datetime
4
+ from typing import Optional
5
+
6
+ from rich.console import Console
7
+ from rich.panel import Panel
8
+ from rich.table import Table
9
+
10
+ from ..models.status_models import SpecState, SpecStatus, StatusReport
11
+ from .base import StatusFormatter
12
+
13
+
14
+ class RichFormatter(StatusFormatter):
15
+ """Formats status as rich terminal output with colors and tables.
16
+
17
+ Uses the Rich library to produce attractive terminal output with:
18
+ - Colored status indicators
19
+ - Formatted tables
20
+ - Summary panels
21
+ """
22
+
23
+ def __init__(self, console: Optional[Console] = None) -> None:
24
+ """Initialize formatter with optional console.
25
+
26
+ Args:
27
+ console: Rich Console instance. Creates new one if not provided.
28
+ """
29
+ self.console = console or Console()
30
+
31
+ def format(self, report: StatusReport, verbose: bool = False) -> str:
32
+ """Format the status report as a string.
33
+
34
+ Note: For rich terminal output, use format_to_console() instead.
35
+ This method captures the output as a string.
36
+
37
+ Args:
38
+ report: The StatusReport to format.
39
+ verbose: Include detailed validation errors.
40
+
41
+ Returns:
42
+ Formatted string representation (with ANSI codes).
43
+ """
44
+ # Capture output to string
45
+ from io import StringIO
46
+
47
+ string_io = StringIO()
48
+ temp_console = Console(file=string_io, force_terminal=True)
49
+
50
+ self._render_to_console(report, verbose, temp_console)
51
+
52
+ return string_io.getvalue()
53
+
54
+ def format_to_console(self, report: StatusReport, verbose: bool = False) -> None:
55
+ """Format and print directly to console with rich formatting.
56
+
57
+ Args:
58
+ report: The StatusReport to format.
59
+ verbose: Include detailed validation errors.
60
+ """
61
+ self._render_to_console(report, verbose, self.console)
62
+
63
+ def _render_to_console(
64
+ self, report: StatusReport, verbose: bool, console: Console
65
+ ) -> None:
66
+ """Internal method to render report to a console.
67
+
68
+ Args:
69
+ report: The StatusReport to format.
70
+ verbose: Include detailed validation errors.
71
+ console: Console to render to.
72
+ """
73
+ # Header panel
74
+ project_name = report.project_root.name
75
+ generated = report.generated_at.strftime("%Y-%m-%d %H:%M:%S")
76
+
77
+ header = f"[bold]📊 Project:[/bold] {project_name}\n"
78
+ header += f"[bold]📅 Generated:[/bold] {generated}"
79
+
80
+ console.print(
81
+ Panel(header, title="Spec Status Dashboard", border_style="blue")
82
+ )
83
+ console.print()
84
+
85
+ # Specs table
86
+ if report.specs:
87
+ table = self._create_specs_table(report, verbose)
88
+ console.print(table)
89
+ console.print()
90
+
91
+ # Summary panel
92
+ summary = self._create_summary_panel(report)
93
+ console.print(summary)
94
+ else:
95
+ console.print(
96
+ "[yellow]No specifications found.[/yellow] "
97
+ "Run 'doit specit' to create one."
98
+ )
99
+
100
+ def _create_specs_table(
101
+ self, report: StatusReport, verbose: bool
102
+ ) -> Table:
103
+ """Create the main specs table.
104
+
105
+ Args:
106
+ report: The StatusReport containing specs.
107
+ verbose: Include validation error details.
108
+
109
+ Returns:
110
+ Rich Table with spec information.
111
+ """
112
+ table = Table(show_header=True, header_style="bold")
113
+
114
+ # Columns
115
+ table.add_column("Spec", style="cyan", no_wrap=True)
116
+ table.add_column("Status", justify="center")
117
+ table.add_column("Validation", justify="center")
118
+ table.add_column("Modified", justify="right")
119
+
120
+ if verbose:
121
+ table.add_column("Details", style="dim")
122
+
123
+ for spec in report.specs:
124
+ # Status with emoji
125
+ status_display = f"{spec.status.emoji} {spec.status.display_name}"
126
+ status_style = self._get_status_style(spec.status)
127
+
128
+ # Validation indicator
129
+ if spec.error:
130
+ validation_display = "[red]❌ Error[/red]"
131
+ elif spec.validation_result is None:
132
+ validation_display = "[dim]—[/dim]"
133
+ elif spec.validation_passed:
134
+ validation_display = "[green]✅ Pass[/green]"
135
+ else:
136
+ validation_display = f"[red]❌ Fail ({spec.validation_result.error_count})[/red]"
137
+
138
+ # Blocking indicator
139
+ if spec.is_blocking:
140
+ validation_display += " [bold red]⛔[/bold red]"
141
+
142
+ # Modified date (relative)
143
+ modified = self._format_relative_date(spec.last_modified)
144
+
145
+ # Build row
146
+ row = [
147
+ spec.name,
148
+ f"[{status_style}]{status_display}[/{status_style}]",
149
+ validation_display,
150
+ modified,
151
+ ]
152
+
153
+ if verbose:
154
+ details = self._get_spec_details(spec)
155
+ row.append(details)
156
+
157
+ table.add_row(*row)
158
+
159
+ return table
160
+
161
+ def _create_summary_panel(self, report: StatusReport) -> Panel:
162
+ """Create the summary panel with statistics.
163
+
164
+ Args:
165
+ report: The StatusReport to summarize.
166
+
167
+ Returns:
168
+ Rich Panel with summary statistics.
169
+ """
170
+ # Status counts
171
+ counts = report.by_status
172
+ status_line = (
173
+ f"[dim]Draft:[/dim] {counts.get(SpecState.DRAFT, 0)} │ "
174
+ f"[dim]In Progress:[/dim] {counts.get(SpecState.IN_PROGRESS, 0)} │ "
175
+ f"[dim]Complete:[/dim] {counts.get(SpecState.COMPLETE, 0)} │ "
176
+ f"[dim]Approved:[/dim] {counts.get(SpecState.APPROVED, 0)}"
177
+ )
178
+
179
+ # Completion percentage
180
+ completion = f"[bold]Completion:[/bold] {report.completion_percentage:.0f}%"
181
+
182
+ # Ready to commit indicator
183
+ if report.is_ready_to_commit:
184
+ ready = "[green]✅ Ready to commit[/green]"
185
+ else:
186
+ ready = f"[red]⛔ {report.blocking_count} spec(s) blocking[/red]"
187
+
188
+ summary = f"{status_line}\n{completion} │ {ready}"
189
+
190
+ return Panel(summary, title="Summary", border_style="green" if report.is_ready_to_commit else "red")
191
+
192
+ def _get_status_style(self, status: SpecState) -> str:
193
+ """Get Rich style for a status.
194
+
195
+ Args:
196
+ status: The SpecState to style.
197
+
198
+ Returns:
199
+ Rich style string.
200
+ """
201
+ styles = {
202
+ SpecState.DRAFT: "dim",
203
+ SpecState.IN_PROGRESS: "yellow",
204
+ SpecState.COMPLETE: "green",
205
+ SpecState.APPROVED: "bold green",
206
+ SpecState.ERROR: "red",
207
+ }
208
+ return styles.get(status, "white")
209
+
210
+ def _format_relative_date(self, dt: datetime) -> str:
211
+ """Format a datetime as relative time (Today, Yesterday, etc.).
212
+
213
+ Args:
214
+ dt: Datetime to format.
215
+
216
+ Returns:
217
+ Human-readable relative date string.
218
+ """
219
+ now = datetime.now()
220
+ diff = now - dt
221
+
222
+ if diff.days == 0:
223
+ return "Today"
224
+ elif diff.days == 1:
225
+ return "Yesterday"
226
+ elif diff.days < 7:
227
+ return f"{diff.days} days ago"
228
+ else:
229
+ return dt.strftime("%Y-%m-%d")
230
+
231
+ def _get_spec_details(self, spec: SpecStatus) -> str:
232
+ """Get detailed information about a spec for verbose mode.
233
+
234
+ Args:
235
+ spec: The SpecStatus to describe.
236
+
237
+ Returns:
238
+ Details string with validation errors if any.
239
+ """
240
+ if spec.error:
241
+ return spec.error
242
+
243
+ if spec.validation_result is None:
244
+ return "Not validated"
245
+
246
+ if spec.validation_passed:
247
+ return f"Score: {spec.validation_score}"
248
+
249
+ # Show validation errors
250
+ errors = []
251
+ for issue in spec.validation_result.issues[:3]: # Limit to 3
252
+ errors.append(f"• {issue.message}")
253
+
254
+ if len(spec.validation_result.issues) > 3:
255
+ errors.append(f"• ... and {len(spec.validation_result.issues) - 3} more")
256
+
257
+ return "\n".join(errors)
doit_cli/main.py ADDED
@@ -0,0 +1,49 @@
1
+ """Main CLI application for doit-cli new commands (init, verify)."""
2
+
3
+ import typer
4
+
5
+ from .cli.analytics_command import app as analytics_app
6
+ from .cli.context_command import context_app
7
+ from .cli.diagram_command import diagram_app
8
+ from .cli.fixit_command import app as fixit_app
9
+ from .cli.hooks_command import hooks_app
10
+ from .cli.init_command import init_command
11
+ from .cli.memory_command import memory_app
12
+ from .cli.status_command import status_command
13
+ from .cli.sync_prompts_command import sync_prompts_command
14
+ from .cli.validate_command import validate_command
15
+ from .cli.verify_command import verify_command
16
+ from .cli.xref_command import xref_app
17
+
18
+
19
+ # Create a new typer app for the refactored commands
20
+ app = typer.Typer(
21
+ name="doit",
22
+ help="Doit CLI - Setup tool for Doit spec-driven development projects",
23
+ add_completion=False,
24
+ )
25
+
26
+ # Register commands
27
+ app.command(name="init")(init_command)
28
+ app.command(name="status")(status_command)
29
+ app.command(name="sync-prompts")(sync_prompts_command)
30
+ app.command(name="validate")(validate_command)
31
+ app.command(name="verify")(verify_command)
32
+
33
+ # Register subcommand groups
34
+ app.add_typer(analytics_app, name="analytics")
35
+ app.add_typer(context_app, name="context")
36
+ app.add_typer(diagram_app, name="diagram")
37
+ app.add_typer(fixit_app, name="fixit")
38
+ app.add_typer(hooks_app, name="hooks")
39
+ app.add_typer(memory_app, name="memory")
40
+ app.add_typer(xref_app, name="xref")
41
+
42
+
43
+ def main():
44
+ """Main entry point for the CLI."""
45
+ app()
46
+
47
+
48
+ if __name__ == "__main__":
49
+ main()
@@ -0,0 +1,139 @@
1
+ """Data models for doit-cli."""
2
+
3
+ from .agent import Agent
4
+ from .project import Project
5
+ from .template import Template
6
+ from .results import InitResult, VerifyResult, VerifyCheck, VerifyStatus
7
+ from .context_config import (
8
+ ContextConfig,
9
+ SourceConfig,
10
+ CommandOverride,
11
+ ContextSource,
12
+ LoadedContext,
13
+ )
14
+ from .validation_models import (
15
+ Severity,
16
+ ValidationStatus,
17
+ ValidationRule,
18
+ ValidationIssue,
19
+ ValidationResult,
20
+ RuleOverride,
21
+ CustomRule,
22
+ ValidationConfig,
23
+ )
24
+ from .workflow_models import (
25
+ WorkflowStatus,
26
+ WorkflowStep,
27
+ Workflow,
28
+ StepResponse,
29
+ WorkflowState,
30
+ ValidationResult as WorkflowValidationResult,
31
+ WorkflowError,
32
+ ValidationError as WorkflowValidationError,
33
+ NavigationCommand,
34
+ StateCorruptionError,
35
+ )
36
+ from .status_models import (
37
+ SpecState,
38
+ SpecStatus,
39
+ StatusReport,
40
+ )
41
+ from .crossref_models import (
42
+ CoverageStatus,
43
+ Requirement,
44
+ TaskReference,
45
+ Task,
46
+ CrossReference,
47
+ RequirementCoverage,
48
+ CoverageReport,
49
+ )
50
+ from .diagram_models import (
51
+ DiagramType,
52
+ Cardinality,
53
+ AcceptanceScenario,
54
+ ParsedUserStory,
55
+ EntityAttribute,
56
+ EntityRelationship,
57
+ ParsedEntity,
58
+ DiagramSection,
59
+ ValidationResult as DiagramValidationResult,
60
+ GeneratedDiagram,
61
+ DiagramResult,
62
+ )
63
+ from .search_models import (
64
+ QueryType,
65
+ SourceType,
66
+ SourceFilter,
67
+ SearchQuery,
68
+ SearchResult,
69
+ MemorySource,
70
+ ContentSnippet,
71
+ SearchHistory,
72
+ )
73
+
74
+ __all__ = [
75
+ "Agent",
76
+ "Project",
77
+ "Template",
78
+ "InitResult",
79
+ "VerifyResult",
80
+ "VerifyCheck",
81
+ "VerifyStatus",
82
+ "ContextConfig",
83
+ "SourceConfig",
84
+ "CommandOverride",
85
+ "ContextSource",
86
+ "LoadedContext",
87
+ "Severity",
88
+ "ValidationStatus",
89
+ "ValidationRule",
90
+ "ValidationIssue",
91
+ "ValidationResult",
92
+ "RuleOverride",
93
+ "CustomRule",
94
+ "ValidationConfig",
95
+ # Workflow models
96
+ "WorkflowStatus",
97
+ "WorkflowStep",
98
+ "Workflow",
99
+ "StepResponse",
100
+ "WorkflowState",
101
+ "WorkflowValidationResult",
102
+ "WorkflowError",
103
+ "WorkflowValidationError",
104
+ "NavigationCommand",
105
+ "StateCorruptionError",
106
+ # Status models
107
+ "SpecState",
108
+ "SpecStatus",
109
+ "StatusReport",
110
+ # Cross-reference models
111
+ "CoverageStatus",
112
+ "Requirement",
113
+ "TaskReference",
114
+ "Task",
115
+ "CrossReference",
116
+ "RequirementCoverage",
117
+ "CoverageReport",
118
+ # Diagram models
119
+ "DiagramType",
120
+ "Cardinality",
121
+ "AcceptanceScenario",
122
+ "ParsedUserStory",
123
+ "EntityAttribute",
124
+ "EntityRelationship",
125
+ "ParsedEntity",
126
+ "DiagramSection",
127
+ "DiagramValidationResult",
128
+ "GeneratedDiagram",
129
+ "DiagramResult",
130
+ # Search models
131
+ "QueryType",
132
+ "SourceType",
133
+ "SourceFilter",
134
+ "SearchQuery",
135
+ "SearchResult",
136
+ "MemorySource",
137
+ "ContentSnippet",
138
+ "SearchHistory",
139
+ ]